From 4f55f9127d2f54a0c35a8f3217e0c3959013d04c Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 8 Dec 2022 01:46:44 +0100 Subject: [PATCH 01/87] Prepare for generated files --- .../adapter/baseAdapter.js => src/adapter/baseAdapter.ts | 0 .../adapter/genericJson.js => src/adapter/genericJson.ts | 0 .../adapter/localFolder.js => src/adapter/localFolder.ts | 0 .../adapter/reddit.js => src/adapter/reddit.ts | 0 .../adapter/unsplash.js => src/adapter/unsplash.ts | 0 .../adapter/urlSource.js => src/adapter/urlSource.ts | 0 .../adapter/wallhaven.js => src/adapter/wallhaven.ts | 0 randomwallpaper@iflow.space/extension.js => src/extension.ts | 0 randomwallpaper@iflow.space/history.js => src/history.ts | 0 .../elements.js => src/historyMenuElements.ts | 0 randomwallpaper@iflow.space/hydraPaper.js => src/hydraPaper.ts | 0 .../jsonpath/jsonpath.js => src/jsonPath.ts | 0 randomwallpaper@iflow.space/logger.js => src/logger.ts | 0 {randomwallpaper@iflow.space => src}/metadata.json | 0 randomwallpaper@iflow.space/prefs.js => src/prefs.ts | 0 .../randomWallpaperMenu.js => src/randomWallpaperMenu.ts | 0 {randomwallpaper@iflow.space => src}/schemas/.gitignore | 0 ...gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml | 0 randomwallpaper@iflow.space/settings.js => src/settings.ts | 0 randomwallpaper@iflow.space/soupBowl.js => src/soupBowl.ts | 0 {randomwallpaper@iflow.space => src}/stylesheet.css | 0 randomwallpaper@iflow.space/timer.js => src/timer.ts | 0 {randomwallpaper@iflow.space => src}/ui/.gitignore | 0 {randomwallpaper@iflow.space => src}/ui/genericJson.blp | 0 .../ui/genericJson.js => src/ui/genericJson.ts | 0 {randomwallpaper@iflow.space => src}/ui/localFolder.blp | 0 .../ui/localFolder.js => src/ui/localFolder.ts | 0 {randomwallpaper@iflow.space => src}/ui/pageGeneral.blp | 0 {randomwallpaper@iflow.space => src}/ui/pageSources.blp | 0 {randomwallpaper@iflow.space => src}/ui/reddit.blp | 0 randomwallpaper@iflow.space/ui/reddit.js => src/ui/reddit.ts | 0 {randomwallpaper@iflow.space => src}/ui/sourceRow.blp | 0 .../ui/sourceRow.js => src/ui/sourceRow.ts | 0 {randomwallpaper@iflow.space => src}/ui/unsplash.blp | 0 randomwallpaper@iflow.space/ui/unsplash.js => src/ui/unsplash.ts | 0 {randomwallpaper@iflow.space => src}/ui/urlSource.blp | 0 .../ui/urlSource.js => src/ui/urlSource.ts | 0 {randomwallpaper@iflow.space => src}/ui/wallhaven.blp | 0 .../ui/wallhaven.js => src/ui/wallhaven.ts | 0 randomwallpaper@iflow.space/utils.js => src/utils.ts | 0 .../wallpaperController.js => src/wallpaperController.ts | 0 41 files changed, 0 insertions(+), 0 deletions(-) rename randomwallpaper@iflow.space/adapter/baseAdapter.js => src/adapter/baseAdapter.ts (100%) rename randomwallpaper@iflow.space/adapter/genericJson.js => src/adapter/genericJson.ts (100%) rename randomwallpaper@iflow.space/adapter/localFolder.js => src/adapter/localFolder.ts (100%) rename randomwallpaper@iflow.space/adapter/reddit.js => src/adapter/reddit.ts (100%) rename randomwallpaper@iflow.space/adapter/unsplash.js => src/adapter/unsplash.ts (100%) rename randomwallpaper@iflow.space/adapter/urlSource.js => src/adapter/urlSource.ts (100%) rename randomwallpaper@iflow.space/adapter/wallhaven.js => src/adapter/wallhaven.ts (100%) rename randomwallpaper@iflow.space/extension.js => src/extension.ts (100%) rename randomwallpaper@iflow.space/history.js => src/history.ts (100%) rename randomwallpaper@iflow.space/elements.js => src/historyMenuElements.ts (100%) rename randomwallpaper@iflow.space/hydraPaper.js => src/hydraPaper.ts (100%) rename randomwallpaper@iflow.space/jsonpath/jsonpath.js => src/jsonPath.ts (100%) rename randomwallpaper@iflow.space/logger.js => src/logger.ts (100%) rename {randomwallpaper@iflow.space => src}/metadata.json (100%) rename randomwallpaper@iflow.space/prefs.js => src/prefs.ts (100%) rename randomwallpaper@iflow.space/randomWallpaperMenu.js => src/randomWallpaperMenu.ts (100%) rename {randomwallpaper@iflow.space => src}/schemas/.gitignore (100%) rename {randomwallpaper@iflow.space => src}/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml (100%) rename randomwallpaper@iflow.space/settings.js => src/settings.ts (100%) rename randomwallpaper@iflow.space/soupBowl.js => src/soupBowl.ts (100%) rename {randomwallpaper@iflow.space => src}/stylesheet.css (100%) rename randomwallpaper@iflow.space/timer.js => src/timer.ts (100%) rename {randomwallpaper@iflow.space => src}/ui/.gitignore (100%) rename {randomwallpaper@iflow.space => src}/ui/genericJson.blp (100%) rename randomwallpaper@iflow.space/ui/genericJson.js => src/ui/genericJson.ts (100%) rename {randomwallpaper@iflow.space => src}/ui/localFolder.blp (100%) rename randomwallpaper@iflow.space/ui/localFolder.js => src/ui/localFolder.ts (100%) rename {randomwallpaper@iflow.space => src}/ui/pageGeneral.blp (100%) rename {randomwallpaper@iflow.space => src}/ui/pageSources.blp (100%) rename {randomwallpaper@iflow.space => src}/ui/reddit.blp (100%) rename randomwallpaper@iflow.space/ui/reddit.js => src/ui/reddit.ts (100%) rename {randomwallpaper@iflow.space => src}/ui/sourceRow.blp (100%) rename randomwallpaper@iflow.space/ui/sourceRow.js => src/ui/sourceRow.ts (100%) rename {randomwallpaper@iflow.space => src}/ui/unsplash.blp (100%) rename randomwallpaper@iflow.space/ui/unsplash.js => src/ui/unsplash.ts (100%) rename {randomwallpaper@iflow.space => src}/ui/urlSource.blp (100%) rename randomwallpaper@iflow.space/ui/urlSource.js => src/ui/urlSource.ts (100%) rename {randomwallpaper@iflow.space => src}/ui/wallhaven.blp (100%) rename randomwallpaper@iflow.space/ui/wallhaven.js => src/ui/wallhaven.ts (100%) rename randomwallpaper@iflow.space/utils.js => src/utils.ts (100%) rename randomwallpaper@iflow.space/wallpaperController.js => src/wallpaperController.ts (100%) diff --git a/randomwallpaper@iflow.space/adapter/baseAdapter.js b/src/adapter/baseAdapter.ts similarity index 100% rename from randomwallpaper@iflow.space/adapter/baseAdapter.js rename to src/adapter/baseAdapter.ts diff --git a/randomwallpaper@iflow.space/adapter/genericJson.js b/src/adapter/genericJson.ts similarity index 100% rename from randomwallpaper@iflow.space/adapter/genericJson.js rename to src/adapter/genericJson.ts diff --git a/randomwallpaper@iflow.space/adapter/localFolder.js b/src/adapter/localFolder.ts similarity index 100% rename from randomwallpaper@iflow.space/adapter/localFolder.js rename to src/adapter/localFolder.ts diff --git a/randomwallpaper@iflow.space/adapter/reddit.js b/src/adapter/reddit.ts similarity index 100% rename from randomwallpaper@iflow.space/adapter/reddit.js rename to src/adapter/reddit.ts diff --git a/randomwallpaper@iflow.space/adapter/unsplash.js b/src/adapter/unsplash.ts similarity index 100% rename from randomwallpaper@iflow.space/adapter/unsplash.js rename to src/adapter/unsplash.ts diff --git a/randomwallpaper@iflow.space/adapter/urlSource.js b/src/adapter/urlSource.ts similarity index 100% rename from randomwallpaper@iflow.space/adapter/urlSource.js rename to src/adapter/urlSource.ts diff --git a/randomwallpaper@iflow.space/adapter/wallhaven.js b/src/adapter/wallhaven.ts similarity index 100% rename from randomwallpaper@iflow.space/adapter/wallhaven.js rename to src/adapter/wallhaven.ts diff --git a/randomwallpaper@iflow.space/extension.js b/src/extension.ts similarity index 100% rename from randomwallpaper@iflow.space/extension.js rename to src/extension.ts diff --git a/randomwallpaper@iflow.space/history.js b/src/history.ts similarity index 100% rename from randomwallpaper@iflow.space/history.js rename to src/history.ts diff --git a/randomwallpaper@iflow.space/elements.js b/src/historyMenuElements.ts similarity index 100% rename from randomwallpaper@iflow.space/elements.js rename to src/historyMenuElements.ts diff --git a/randomwallpaper@iflow.space/hydraPaper.js b/src/hydraPaper.ts similarity index 100% rename from randomwallpaper@iflow.space/hydraPaper.js rename to src/hydraPaper.ts diff --git a/randomwallpaper@iflow.space/jsonpath/jsonpath.js b/src/jsonPath.ts similarity index 100% rename from randomwallpaper@iflow.space/jsonpath/jsonpath.js rename to src/jsonPath.ts diff --git a/randomwallpaper@iflow.space/logger.js b/src/logger.ts similarity index 100% rename from randomwallpaper@iflow.space/logger.js rename to src/logger.ts diff --git a/randomwallpaper@iflow.space/metadata.json b/src/metadata.json similarity index 100% rename from randomwallpaper@iflow.space/metadata.json rename to src/metadata.json diff --git a/randomwallpaper@iflow.space/prefs.js b/src/prefs.ts similarity index 100% rename from randomwallpaper@iflow.space/prefs.js rename to src/prefs.ts diff --git a/randomwallpaper@iflow.space/randomWallpaperMenu.js b/src/randomWallpaperMenu.ts similarity index 100% rename from randomwallpaper@iflow.space/randomWallpaperMenu.js rename to src/randomWallpaperMenu.ts diff --git a/randomwallpaper@iflow.space/schemas/.gitignore b/src/schemas/.gitignore similarity index 100% rename from randomwallpaper@iflow.space/schemas/.gitignore rename to src/schemas/.gitignore diff --git a/randomwallpaper@iflow.space/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml similarity index 100% rename from randomwallpaper@iflow.space/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml rename to src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml diff --git a/randomwallpaper@iflow.space/settings.js b/src/settings.ts similarity index 100% rename from randomwallpaper@iflow.space/settings.js rename to src/settings.ts diff --git a/randomwallpaper@iflow.space/soupBowl.js b/src/soupBowl.ts similarity index 100% rename from randomwallpaper@iflow.space/soupBowl.js rename to src/soupBowl.ts diff --git a/randomwallpaper@iflow.space/stylesheet.css b/src/stylesheet.css similarity index 100% rename from randomwallpaper@iflow.space/stylesheet.css rename to src/stylesheet.css diff --git a/randomwallpaper@iflow.space/timer.js b/src/timer.ts similarity index 100% rename from randomwallpaper@iflow.space/timer.js rename to src/timer.ts diff --git a/randomwallpaper@iflow.space/ui/.gitignore b/src/ui/.gitignore similarity index 100% rename from randomwallpaper@iflow.space/ui/.gitignore rename to src/ui/.gitignore diff --git a/randomwallpaper@iflow.space/ui/genericJson.blp b/src/ui/genericJson.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/genericJson.blp rename to src/ui/genericJson.blp diff --git a/randomwallpaper@iflow.space/ui/genericJson.js b/src/ui/genericJson.ts similarity index 100% rename from randomwallpaper@iflow.space/ui/genericJson.js rename to src/ui/genericJson.ts diff --git a/randomwallpaper@iflow.space/ui/localFolder.blp b/src/ui/localFolder.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/localFolder.blp rename to src/ui/localFolder.blp diff --git a/randomwallpaper@iflow.space/ui/localFolder.js b/src/ui/localFolder.ts similarity index 100% rename from randomwallpaper@iflow.space/ui/localFolder.js rename to src/ui/localFolder.ts diff --git a/randomwallpaper@iflow.space/ui/pageGeneral.blp b/src/ui/pageGeneral.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/pageGeneral.blp rename to src/ui/pageGeneral.blp diff --git a/randomwallpaper@iflow.space/ui/pageSources.blp b/src/ui/pageSources.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/pageSources.blp rename to src/ui/pageSources.blp diff --git a/randomwallpaper@iflow.space/ui/reddit.blp b/src/ui/reddit.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/reddit.blp rename to src/ui/reddit.blp diff --git a/randomwallpaper@iflow.space/ui/reddit.js b/src/ui/reddit.ts similarity index 100% rename from randomwallpaper@iflow.space/ui/reddit.js rename to src/ui/reddit.ts diff --git a/randomwallpaper@iflow.space/ui/sourceRow.blp b/src/ui/sourceRow.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/sourceRow.blp rename to src/ui/sourceRow.blp diff --git a/randomwallpaper@iflow.space/ui/sourceRow.js b/src/ui/sourceRow.ts similarity index 100% rename from randomwallpaper@iflow.space/ui/sourceRow.js rename to src/ui/sourceRow.ts diff --git a/randomwallpaper@iflow.space/ui/unsplash.blp b/src/ui/unsplash.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/unsplash.blp rename to src/ui/unsplash.blp diff --git a/randomwallpaper@iflow.space/ui/unsplash.js b/src/ui/unsplash.ts similarity index 100% rename from randomwallpaper@iflow.space/ui/unsplash.js rename to src/ui/unsplash.ts diff --git a/randomwallpaper@iflow.space/ui/urlSource.blp b/src/ui/urlSource.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/urlSource.blp rename to src/ui/urlSource.blp diff --git a/randomwallpaper@iflow.space/ui/urlSource.js b/src/ui/urlSource.ts similarity index 100% rename from randomwallpaper@iflow.space/ui/urlSource.js rename to src/ui/urlSource.ts diff --git a/randomwallpaper@iflow.space/ui/wallhaven.blp b/src/ui/wallhaven.blp similarity index 100% rename from randomwallpaper@iflow.space/ui/wallhaven.blp rename to src/ui/wallhaven.blp diff --git a/randomwallpaper@iflow.space/ui/wallhaven.js b/src/ui/wallhaven.ts similarity index 100% rename from randomwallpaper@iflow.space/ui/wallhaven.js rename to src/ui/wallhaven.ts diff --git a/randomwallpaper@iflow.space/utils.js b/src/utils.ts similarity index 100% rename from randomwallpaper@iflow.space/utils.js rename to src/utils.ts diff --git a/randomwallpaper@iflow.space/wallpaperController.js b/src/wallpaperController.ts similarity index 100% rename from randomwallpaper@iflow.space/wallpaperController.js rename to src/wallpaperController.ts From 476aeffca45882acc9e00b29bf2227c2bebf8a40 Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 8 Dec 2022 20:53:41 +0100 Subject: [PATCH 02/87] Use TypeScript for better tooling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … since blueprint-compiler building had to be done anyway. --- .eslintrc-gjs.yml | 321 +++ .eslintrc.yml | 275 ++ .github/workflows/build.yml | 55 + .gitignore | 10 +- README.md | 50 +- build.sh | 169 +- package-lock.json | 4461 ++++++++++++++++++++++++++++++++ package.json | 13 + src/adapter/baseAdapter.ts | 225 +- src/adapter/genericJson.ts | 285 +- src/adapter/localFolder.ts | 204 +- src/adapter/reddit.ts | 206 +- src/adapter/unsplash.ts | 295 +-- src/adapter/urlSource.ts | 104 +- src/adapter/wallhaven.ts | 282 +- src/extension.ts | 109 +- src/history.ts | 365 +-- src/historyMenuElements.ts | 827 +++--- src/hydraPaper.ts | 159 +- src/jsonPath.ts | 223 +- src/logger.ts | 73 +- src/metadata.json | 6 +- src/prefs.ts | 529 ++-- src/randomWallpaperMenu.ts | 433 ++-- src/settings.ts | 222 +- src/soupBowl.ts | 86 +- src/stylesheet.css | 6 +- src/timer.ts | 280 +- src/ui/genericJson.blp | 150 +- src/ui/genericJson.ts | 161 +- src/ui/localFolder.blp | 18 +- src/ui/localFolder.ts | 124 +- src/ui/pageGeneral.blp | 310 +-- src/ui/pageSources.blp | 28 +- src/ui/reddit.blp | 138 +- src/ui/reddit.ts | 131 +- src/ui/sourceRow.blp | 100 +- src/ui/sourceRow.ts | 337 +-- src/ui/unsplash.blp | 92 +- src/ui/unsplash.ts | 206 +- src/ui/urlSource.blp | 102 +- src/ui/urlSource.ts | 33 +- src/ui/wallhaven.blp | 176 +- src/ui/wallhaven.ts | 296 ++- src/utils.ts | 227 +- src/wallpaperController.ts | 1098 ++++---- tsconfig.json | 39 + types/gtk4/adw/adw.d.ts | 3718 ++++++++++++++++++++++++++ types/misc/extensionUtils.d.ts | 102 + types/ui/main.d.ts | 8 + types/ui/panelMenu.d.ts | 12 + types/ui/popupMenu.d.ts | 74 + 52 files changed, 13751 insertions(+), 4202 deletions(-) create mode 100644 .eslintrc-gjs.yml create mode 100644 .eslintrc.yml create mode 100644 .github/workflows/build.yml create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json create mode 100644 types/gtk4/adw/adw.d.ts create mode 100644 types/misc/extensionUtils.d.ts create mode 100644 types/ui/main.d.ts create mode 100644 types/ui/panelMenu.d.ts create mode 100644 types/ui/popupMenu.d.ts diff --git a/.eslintrc-gjs.yml b/.eslintrc-gjs.yml new file mode 100644 index 00000000..c1677c93 --- /dev/null +++ b/.eslintrc-gjs.yml @@ -0,0 +1,321 @@ +--- +# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later +# SPDX-FileCopyrightText: 2018 Claudio André + +# Contains some additional styling rules to autofix generated javascript +# https://eslint.org/docs/latest/rules/padding-line-between-statements +env: + es2021: true +extends: 'eslint:recommended' +plugins: + - jsdoc +rules: + array-bracket-newline: + - error + - consistent + array-bracket-spacing: + - error + - never + array-callback-return: error + arrow-parens: + - error + - as-needed + arrow-spacing: error + block-scoped-var: error + block-spacing: error + brace-style: error + # Waiting for this to have matured a bit in eslint + # camelcase: + # - error + # - properties: never + # allow: [^vfunc_, ^on_, _instance_init] + comma-dangle: + - error + - arrays: always-multiline + objects: always-multiline + functions: never + comma-spacing: + - error + - before: false + after: true + comma-style: + - error + - last + computed-property-spacing: error + curly: + - error + - multi-or-nest + - consistent + dot-location: + - error + - property + eol-last: error + eqeqeq: error + func-call-spacing: error + func-name-matching: error + func-style: + - error + - declaration + - allowArrowFunctions: true + indent: + - error + - 4 + - ignoredNodes: + # Allow not indenting the body of GObject.registerClass, since in the + # future it's intended to be a decorator + - 'CallExpression[callee.object.name=GObject][callee.property.name=registerClass] > ClassExpression:first-child' + # Allow dedenting chained member expressions + MemberExpression: 'off' + jsdoc/check-alignment: error + jsdoc/check-param-names: error + jsdoc/check-tag-names: error + jsdoc/check-types: error + jsdoc/implements-on-classes: error + jsdoc/newline-after-description: error + jsdoc/require-jsdoc: error + jsdoc/require-param: error + jsdoc/require-param-description: error + jsdoc/require-param-name: error + jsdoc/require-param-type: error + key-spacing: + - error + - beforeColon: false + afterColon: true + keyword-spacing: + - error + - before: true + after: true + linebreak-style: + - error + - unix + lines-between-class-members: + - error + - always + - exceptAfterSingleLine: true + max-nested-callbacks: error + max-statements-per-line: error + new-parens: error + no-array-constructor: error + no-await-in-loop: error + no-caller: error + no-constant-condition: + - error + - checkLoops: false + no-div-regex: error + no-empty: + - error + - allowEmptyCatch: true + no-extra-bind: error + no-extra-parens: + - error + - all + - conditionalAssign: false + nestedBinaryExpressions: false + returnAssign: false + no-implicit-coercion: + - error + - allow: + - '!!' + no-invalid-this: error + no-iterator: error + no-label-var: error + no-lonely-if: error + no-loop-func: error + no-nested-ternary: error + no-new-object: error + no-new-wrappers: error + no-octal-escape: error + no-proto: error + no-prototype-builtins: 'off' + no-restricted-globals: [error, window] + no-restricted-properties: + - error + - object: imports + property: format + message: Use template strings + - object: pkg + property: initFormat + message: Use template strings + - object: Lang + property: copyProperties + message: Use Object.assign() + - object: Lang + property: bind + message: Use arrow notation or Function.prototype.bind() + - object: Lang + property: Class + message: Use ES6 classes + no-restricted-syntax: + - error + - selector: >- + MethodDefinition[key.name="_init"] > + FunctionExpression[params.length=1] > + BlockStatement[body.length=1] + CallExpression[arguments.length=1][callee.object.type="Super"][callee.property.name="_init"] > + Identifier:first-child + message: _init() that only calls super._init() is unnecessary + - selector: >- + MethodDefinition[key.name="_init"] > + FunctionExpression[params.length=0] > + BlockStatement[body.length=1] + CallExpression[arguments.length=0][callee.object.type="Super"][callee.property.name="_init"] + message: _init() that only calls super._init() is unnecessary + - selector: BinaryExpression[operator="instanceof"][right.name="Array"] + message: Use Array.isArray() + no-return-assign: error + no-return-await: error + no-self-compare: error + no-shadow: error + no-shadow-restricted-names: error + no-spaced-func: error + no-tabs: error + no-template-curly-in-string: error + no-throw-literal: error + no-trailing-spaces: error + no-undef-init: error + no-unneeded-ternary: error + no-unused-expressions: error + no-unused-vars: + - error + # Vars use a suffix _ instead of a prefix because of file-scope private vars + - varsIgnorePattern: (^unused|_$) + argsIgnorePattern: ^(unused|_) + no-useless-call: error + no-useless-computed-key: error + no-useless-concat: error + no-useless-constructor: error + no-useless-rename: error + no-useless-return: error + no-whitespace-before-property: error + no-with: error + nonblock-statement-body-position: + - error + - below + object-curly-newline: + - error + - consistent: true + multiline: true + object-curly-spacing: error + object-shorthand: error + operator-assignment: error + operator-linebreak: error + padded-blocks: + - error + - never + # These may be a bit controversial, we can try them out and enable them later + # prefer-const: error + # prefer-destructuring: error + prefer-numeric-literals: error + prefer-promise-reject-errors: error + prefer-rest-params: error + prefer-spread: error + prefer-template: error + quotes: + - error + - single + - avoidEscape: true + require-await: error + rest-spread-spacing: error + semi: + - error + - always + semi-spacing: + - error + - before: false + after: true + semi-style: error + space-before-blocks: error + space-before-function-paren: + - error + - named: never + # for `function ()` and `async () =>`, preserve space around keywords + anonymous: always + asyncArrow: always + space-in-parens: error + space-infix-ops: + - error + - int32Hint: false + space-unary-ops: error + spaced-comment: error + switch-colon-spacing: error + symbol-description: error + template-curly-spacing: error + template-tag-spacing: error + unicode-bom: error + wrap-iife: + - error + - inside + yield-star-spacing: error + yoda: error + padding-line-between-statements: + - error + - blankLine: always + prev: function + next: function + - blankLine: always + prev: class + next: function + - blankLine: always + prev: function + next: class + - blankLine: always + prev: class + next: class + - blankLine: always + prev: import + next: function + - blankLine: always + prev: const + next: function + - blankLine: always + prev: let + next: function + - blankLine: always + prev: import + next: class + - blankLine: always + prev: class + next: export + - blankLine: always + prev: function + next: export + - blankLine: always + prev: '*' + next: return + - blankLine: always + prev: '*' + next: block-like + - blankLine: always + prev: block-like + next: '*' + - blankLine: always + prev: '*' + next: if + - blankLine: always + prev: if + next: '*' +settings: + jsdoc: + mode: typescript +globals: + 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 +parserOptions: + ecmaVersion: 2022 + sourceType: "module" diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 00000000..2c100f96 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,275 @@ +--- +# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later +# SPDX-FileCopyrightText: 2018 Claudio André +env: + es2021: true +extends: + - 'eslint:recommended' +parser: "@typescript-eslint/parser" +plugins: + - jsdoc + - "@typescript-eslint" +rules: + array-bracket-newline: + - error + - consistent + array-bracket-spacing: + - error + - never + array-callback-return: error + arrow-parens: + - error + - as-needed + arrow-spacing: error + block-scoped-var: error + block-spacing: error + brace-style: error + # Waiting for this to have matured a bit in eslint + # camelcase: + # - error + # - properties: never + # allow: [^vfunc_, ^on_, _instance_init] + comma-dangle: + - error + - arrays: always-multiline + objects: always-multiline + functions: never + comma-spacing: + - error + - before: false + after: true + comma-style: + - error + - last + computed-property-spacing: error + curly: + - error + - multi-or-nest + - consistent + dot-location: + - error + - property + eol-last: error + eqeqeq: error + func-call-spacing: error + func-name-matching: error + func-style: + - error + - declaration + - allowArrowFunctions: true + indent: + - error + - 4 + - ignoredNodes: + # Allow not indenting the body of GObject.registerClass, since in the + # future it's intended to be a decorator + - 'CallExpression[callee.object.name=GObject][callee.property.name=registerClass] > ClassExpression:first-child' + # Allow de denting chained member expressions + MemberExpression: 'off' + jsdoc/check-alignment: error + jsdoc/check-param-names: error + jsdoc/check-tag-names: error + jsdoc/check-types: error + jsdoc/implements-on-classes: error + jsdoc/newline-after-description: error + jsdoc/require-jsdoc: error + jsdoc/require-param: error + jsdoc/require-param-description: error + jsdoc/require-param-name: error + jsdoc/require-param-type: error + key-spacing: + - error + - beforeColon: false + afterColon: true + keyword-spacing: + - error + - before: true + after: true + linebreak-style: + - error + - unix + lines-between-class-members: + - error + - always + - exceptAfterSingleLine: true + max-nested-callbacks: error + max-statements-per-line: error + new-parens: error + no-array-constructor: error + no-await-in-loop: error + no-caller: error + no-constant-condition: + - error + - checkLoops: false + no-div-regex: error + no-empty: + - error + - allowEmptyCatch: true + no-extra-bind: error + no-extra-parens: + - error + - all + - conditionalAssign: false + nestedBinaryExpressions: false + returnAssign: false + no-implicit-coercion: + - error + - allow: + - '!!' + no-invalid-this: error + no-iterator: error + no-label-var: error + no-lonely-if: error + no-loop-func: error + no-nested-ternary: error + no-new-object: error + no-new-wrappers: error + no-octal-escape: error + no-proto: error + no-prototype-builtins: 'off' + no-restricted-globals: [error, window] + no-restricted-properties: + - error + - object: imports + property: format + message: Use template strings + - object: pkg + property: initFormat + message: Use template strings + - object: Lang + property: copyProperties + message: Use Object.assign() + - object: Lang + property: bind + message: Use arrow notation or Function.prototype.bind() + - object: Lang + property: Class + message: Use ES6 classes + no-restricted-syntax: + - error + - selector: >- + MethodDefinition[key.name="_init"] > + FunctionExpression[params.length=1] > + BlockStatement[body.length=1] + CallExpression[arguments.length=1][callee.object.type="Super"][callee.property.name="_init"] > + Identifier:first-child + message: _init() that only calls super._init() is unnecessary + - selector: >- + MethodDefinition[key.name="_init"] > + FunctionExpression[params.length=0] > + BlockStatement[body.length=1] + CallExpression[arguments.length=0][callee.object.type="Super"][callee.property.name="_init"] + message: _init() that only calls super._init() is unnecessary + - selector: BinaryExpression[operator="instanceof"][right.name="Array"] + message: Use Array.isArray() + no-return-assign: error + no-return-await: error + no-self-compare: error + no-shadow: error + no-shadow-restricted-names: error + no-spaced-func: error + no-tabs: error + no-template-curly-in-string: error + no-throw-literal: error + no-trailing-spaces: error + no-undef-init: error + no-unneeded-ternary: error + no-unused-expressions: error + no-unused-vars: + - error + # Vars use a suffix _ instead of a prefix because of file-scope private vars + - varsIgnorePattern: (^unused|_$) + argsIgnorePattern: ^(unused|_) + no-useless-call: error + no-useless-computed-key: error + no-useless-concat: error + no-useless-constructor: error + no-useless-rename: error + no-useless-return: error + no-whitespace-before-property: error + no-with: error + nonblock-statement-body-position: + - error + - below + object-curly-newline: + - error + - consistent: true + multiline: true + object-curly-spacing: error + object-shorthand: error + operator-assignment: error + operator-linebreak: error + padded-blocks: + - error + - never + # These may be a bit controversial, we can try them out and enable them later + # prefer-const: error + # prefer-destructuring: error + prefer-numeric-literals: error + prefer-promise-reject-errors: error + prefer-rest-params: error + prefer-spread: error + prefer-template: error + quotes: + - error + - single + - avoidEscape: true + require-await: error + rest-spread-spacing: error + semi: + - error + - always + semi-spacing: + - error + - before: false + after: true + semi-style: error + space-before-blocks: error + space-before-function-paren: + - error + - named: never + # for `function ()` and `async () =>`, preserve space around keywords + anonymous: always + asyncArrow: always + space-in-parens: error + space-infix-ops: + - error + - int32Hint: false + space-unary-ops: error + spaced-comment: error + switch-colon-spacing: error + symbol-description: error + template-curly-spacing: error + template-tag-spacing: error + unicode-bom: error + wrap-iife: + - error + - inside + yield-star-spacing: error + yoda: error +settings: + jsdoc: + mode: typescript +globals: + 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 +parserOptions: + ecmaVersion: 2022 + sourceType: "module" + project: ['./tsconfig.json'] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..8af6e6e9 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,55 @@ +name: Build and create ZIP +run-name: Generate javascript, ui and schema to publish +on: [push, pull_request] +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Install dependencies + run: | + sudo apt -q update + sudo apt -q install --no-install-recommends npm + - name: Check out repository code + uses: actions/checkout@v3 + - name: Setup environment + run: | + ${{github.workspace}}/build.sh setup_env + - name: Check TypeScript + run: | + ${{github.workspace}}/build.sh check + build: + runs-on: ubuntu-latest + steps: + - name: Install dependencies + run: | + sudo apt -q update + sudo apt -q install --no-install-recommends npm blueprint-compiler libglib2.0-0 bash gnome-shell libgtk-4-bin libgtk-4-common libgtk-4-dev libadwaita-1-dev gir1.2-adw-1 gir1.2-gtk-4.0 zstd + - name: Check out repository code + uses: actions/checkout@v3 + - name: Ubuntu specific workarounds + run: | + cd ${{github.workspace}}/.github/workflows || exit 1 + mkdir libadwaita + cd libadwaita || exit 1 + wget https://archlinux.org/packages/extra/x86_64/libadwaita/download/ -O libadwaita.tar.zstd + tar -xf libadwaita.tar.zstd + sed -i -E 's#libdir=\$\{prefix\}/lib#libdir=${prefix}/lib/x86_64-linux-gnu#g' usr/lib/pkgconfig/libadwaita-1.pc + sed -i -E 's#Libs: -L\$\{prefix\}/lib -ladwaita-1#Libs: -L${prefix}/lib/x86_64-linux-gnu -ladwaita-1#g' usr/lib/pkgconfig/libadwaita-1.pc + sudo rm /usr/lib/x86_64-linux-gnu/girepository-1.0/Adw-1.typelib + sudo rm -r /usr/include/libadwaita-1 + sudo rm /usr/lib/x86_64-linux-gnu/libadwaita-1.so + sudo rm /usr/share/gir-1.0/Adw-1.gir + sudo rm /usr/lib/x86_64-linux-gnu/pkgconfig/libadwaita-1.pc + sudo mv usr/lib/girepository-1.0/Adw-1.typelib /usr/lib/x86_64-linux-gnu/girepository-1.0/ + sudo mv usr/include/libadwaita-1 /usr/include/ + sudo mv usr/lib/libadwaita-1.so.0 /usr/lib/x86_64-linux-gnu/ + sudo mv usr/lib/pkgconfig/libadwaita-1.pc /usr/lib/x86_64-linux-gnu/pkgconfig/ + sudo mv usr/share/gir-1.0/Adw-1.gir /usr/share/gir-1.0/ + - run: ${{github.workspace}}/build.sh setup_env + - run: ${{github.workspace}}/build.sh build + - run: ${{github.workspace}}/build.sh format + - run: ${{github.workspace}}/build.sh copy_static + - uses: actions/upload-artifact@v3 + with: + name: randomwallpaper@iflow.space.shell-extension.zip + path: ${{github.workspace}}/randomwallpaper@iflow.space diff --git a/.gitignore b/.gitignore index f5e78c9c..abdc8f0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ .idea -# ready to deploy to gnome extensions -random-wallpaper-gnome3.zip -randomwallpaper@iflow.space.zip # Temporary ui files **/*~ + +# Generated stuff +randomwallpaper@iflow.space/ +randomwallpaper@iflow.space.shell-extension.zip + +# Build stuff +node_modules/ diff --git a/README.md b/README.md index 9c97000e..a7011610 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Install and try the extension at [extensions.gnome.org](https://extensions.gnome * Automatic renewal (Auto-Fetching) ## Installation (symlink to the repository) -Requires [`blueprint-compiler`](https://repology.org/project/blueprint-compiler/versions) at install and update time. +Requires [`blueprint-compiler`](https://repology.org/project/blueprint-compiler/versions) and [`npm`](https://repology.org/project/npm/versions) at install and update time. Clone the repository and run `./build.sh && ./install.sh` in the repository folder to make a symbolic link from the extensions folder to the git repository. This installation will depend on the repository folder, so do not delete the cloned folder. @@ -38,9 +38,9 @@ __Installing this way has various advantages:__ * Updating the extension with `git pull && ./build.sh` ## Installation (manually) -Requires [`blueprint-compiler`](https://repology.org/project/blueprint-compiler/versions) at install and update time. +Requires [`blueprint-compiler`](https://repology.org/project/blueprint-compiler/versions) and [`npm`](https://repology.org/project/npm/versions) at install and update time. -Clone or download the repository and copy the folder `randomwallpaper@iflow.space` in the repository to `~/.local/share/gnome-shell/extensions/`. +Clone or download the repository and copy the folder `randomwallpaper@iflow.space` in the repository to `$XDG_DATA_HOME/gnome-shell/extensions/` (usually `$HOME/.local/share/gnome-shell/extensions/`). Run `./build.sh` inside the repository. Then open the command prompt (Alt+F2) end enter `r` to restart the gnome session. @@ -50,27 +50,39 @@ Now you should be able to activate the extension through the gnome-tweak-tool. ## Uninstall Run `./install uninstall` to delete the symbolic link. -If you installed the extension manually you have to delete the extension folder `randomwallpaper@iflow.space` in `~/.local/share/gnome-shell/extensions/`. +If you installed the extension manually you have to delete the extension folder `randomwallpaper@iflow.space` in `$XDG_DATA_HOME/gnome-shell/extensions/` (usually `$HOME/.local/share/gnome-shell/extensions/`). ## Debugging You can follow the output of the extension with `./debug.sh`. Information should be printed using the existing logger class but can also be printed with `global.log()` (not recommended). To debug the `prefs.js` use `./debug.sh prefs`. -## Compiling schemas -This can be done with the command: `glib-compile-schemas randomwallpaper@iflow.space/schemas/` - -## Compiling UI -Requires [`blueprint-compiler`](https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/). -Run `./build.sh` to compile ui files. - -## Adding predefined sources -1. Build UI for settings using the [blueprint-compiler](https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/) language in `…/ui/mySource.blp` - see [Workbench](https://apps.gnome.org/app/re.sonny.Workbench/) for a live preview editor. - * Add the file to `build.sh` -1. Create a settings layout to the `…/schemas/….gschema.xml` -1. Create your logic hooking the settings in a `…/ui/mySource.js` -1. Add the new source to `…/ui/sourceRow.js` -1. Create a adapter to read the settings and fetching the images and additional information in `…/adapter/mySource.js` by extending the `BaseAdapter`. - * Add your adapter to `…/wallpaperController.js` +## Compiling individual parts +### Schemas +This can be done with the command: +~~~ +glib-compile-schemas --targetdir="randomwallpaper@iflow.space/schemas/" "src/schemas" +~~~ + +### UI +Requires [`blueprint-compiler`](https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/): +~~~ +blueprint-compiler batch-compile "src/ui" "randomwallpaper@iflow.space/ui" "src"/ui/*.blp +~~~ + +### TypeScript +Requires [`npm`](https://repology.org/project/npm/versions): +~~~ +npm install +npx --silent tsc +~~~ + +## Adding new sources +1. Build UI for settings using the [blueprint-compiler](https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/) language in `src/ui/mySource.blp` - see [Workbench](https://apps.gnome.org/app/re.sonny.Workbench/) for a live preview editor. +1. Create and add a settings layout to the `src/schemas/….gschema.xml`. Also add your source to the `types` enum. +1. Create your logic hooking the settings in a `src/ui/mySource.ts` +1. Add the new source to `src/ui/sourceRow.ts:_getSettingsGroup()`, don't forget the import statement. +1. Create a adapter to read the settings and fetching the images and additional information in `src/adapter/mySource.ts` by extending the `BaseAdapter`. +1. Add your adapter to `src/wallpaperController.ts:_getRandomAdapter()`, don't forget the import statement. ## Support Me If you enjoy this extension and want to support the development, then feel free to buy me a coffee. :wink: :coffee: diff --git a/build.sh b/build.sh index fc84dcf6..ca9364ac 100755 --- a/build.sh +++ b/build.sh @@ -1,25 +1,144 @@ -#!/bin/bash - -BASEDIR="randomwallpaper@iflow.space" -ZIPNAME="$BASEDIR.zip" - -rm "$ZIPNAME" -rm "$BASEDIR/schemas/gschemas.compiled" -rm "$BASEDIR/wallpapers/"* -glib-compile-schemas "$BASEDIR/schemas/" - -# cd "$BASEDIR/ui" || exit 1 -blueprint-compiler batch-compile "$BASEDIR/ui" "$BASEDIR/ui" \ - "$BASEDIR/ui/genericJson.blp" \ - "$BASEDIR/ui/localFolder.blp" \ - "$BASEDIR/ui/pageGeneral.blp" \ - "$BASEDIR/ui/pageSources.blp" \ - "$BASEDIR/ui/reddit.blp" \ - "$BASEDIR/ui/sourceRow.blp" \ - "$BASEDIR/ui/unsplash.blp" \ - "$BASEDIR/ui/urlSource.blp" \ - "$BASEDIR/ui/wallhaven.blp" - -cd "$BASEDIR" || exit 1 -zip -r "$ZIPNAME" . -mv "$ZIPNAME" .. +#!/usr/bin/env bash + +UUID="randomwallpaper@iflow.space" + +# fail on error +set -e + +# https://unix.stackexchange.com/a/20325 +if [[ $EUID -eq 0 ]]; then + echo "This script must NOT be run as root" 1>&2 + exit 1 +fi + +# https://stackoverflow.com/a/246128 +SCRIPTDIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) + +SRCDIR="$SCRIPTDIR/src" +DESTDIR="$SCRIPTDIR/$UUID" + +cd "$SCRIPTDIR" || exit 1 + +check_command() { + if ! command -v "$1" &>/dev/null; then + echo "Please install \"$1\" and make sure it's available in your \$PATH" + exit 1 + fi +} + +setup_environment() { + check_command "npm" + + # install, config in package.json + npm --silent install + + # Delete output directory, everything will be rewritten + rm -r "$DESTDIR" &>/dev/null || true +} + +compile_ui() { + check_command "blueprint-compiler" + + # Compile UI files + blueprint-compiler batch-compile "$DESTDIR/ui" "$SRCDIR/ui" "$SRCDIR"/ui/*.blp +} + +compile_js() { + check_command "npm" + + # TypeScript to JavaScript, config in tsconfig.json + npx --silent tsc + + # rewrite imports to gjs own module system + shopt -s globstar nullglob + for file in "$DESTDIR"/**/*.js; do + # I don't know why these aren't available, they should? + sed -i -E "s#import \* as (.*) from 'gi://.*';#const \1 = imports.gi.\1;#g" "$file" + + # Libadwaita seems somehow missing from gi:// + sed -i -E "s#import \* as Adw from '@gi/gtk4/adw/adw';#const Adw = imports.gi.Adw;#g" "$file" + + # Special module naming + sed -i -E "s#import \* as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray';#const ByteArray = imports.byteArray;#g" "$file" + + # Special cases for extension internal imports, shell + sed -i -E "s#import \* as ExtensionUtils from '@gi/misc/extensionUtils';#const ExtensionUtils = imports.misc.extensionUtils;#g" "$file" + sed -i -E "s#import \* as PopupMenu from '@gi/ui/popupMenu';#const PopupMenu = imports.ui.popupMenu;#g" "$file" + sed -i -E "s#import \* as PanelMenu from '@gi/ui/panelMenu';#const PanelMenu = imports.ui.panelMenu;#g" "$file" + sed -i -E "s#import \* as Main from '@gi/ui/main';#const Main = imports.ui.main;#g" "$file" + + # all remaining + sed -i -E "s#import \* as (.*) from '@gi-types/.*';#const \1 = imports.gi.\1;#g" "$file" + done + + # extension.js and prefs.js can't be modules (yet) while dynamically loaded by GJS + # https://github.com/microsoft/TypeScript/issues/41567 + sed -i -E "s#export \{\};##g" "$DESTDIR/extension.js" +} + +compile_schemas() { + check_command "glib-compile-schemas" + + # the pack command also compiles the schemas but only into the zip file + glib-compile-schemas --targetdir="$DESTDIR/schemas" "$SRCDIR/schemas/" +} + +format_js() { + check_command "npm" + + # Format js using the official gjs stylesheet and a few manual quirks + npx --silent eslint --config "$SCRIPTDIR/.eslintrc-gjs.yml" --fix "$DESTDIR/**/*.js" +} + +check_ts() { + check_command "npm" + + npx --silent eslint "$SRCDIR/**/*.ts" +} + +copy_static_files() { + # Copy non generated files to destdir + mkdir -p "$DESTDIR/schemas/" + cp "$SRCDIR/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml" "$DESTDIR/schemas/" + cp "$SRCDIR/metadata.json" "$DESTDIR/" + cp "$SRCDIR/stylesheet.css" "$DESTDIR/" +} + +pack() { + check_command "gnome-extensions" + + # pack everything into a sharable zip file + extra_source=() + for file in "$DESTDIR"/*; do + extra_source+=("--extra-source=$file") + done + + gnome-extensions pack --force "${extra_source[@]}" "$DESTDIR" +} + +if [ $# -eq 0 ]; then + # No arguments, do everything + setup_environment + compile_ui + compile_js + compile_schemas + format_js + check_ts + copy_static_files + pack +elif [ "$1" == "check" ]; then + check_ts +elif [ "$1" == "build" ]; then + compile_ui + compile_js + compile_schemas +elif [ "$1" == "format" ]; then + format_js +elif [ "$1" == "pack" ]; then + copy_static_files + pack +elif [ "$1" == "setup_env" ]; then + setup_environment +elif [ "$1" == "copy_static" ]; then + copy_static_files +fi diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..4b3f51be --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4461 @@ +{ + "name": "RandomWallpaperGnome3", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@gi-types/base-types": "^1.0.0", + "@gi-types/gjs-environment": "^1.1.0", + "@gi-types/gtk4-types": "^1.0.0", + "@gi-types/shell": "^0.1.6", + "@typescript-eslint/eslint-plugin": "^5.46.0", + "@typescript-eslint/parser": "^5.46.0", + "eslint": "^8.29.0", + "eslint-plugin-jsdoc": "^39.6.4", + "typescript": "^4.9.4" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", + "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", + "dev": true, + "dependencies": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~3.1.0" + }, + "engines": { + "node": "^14 || ^16 || ^17 || ^18 || ^19" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.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/@gi-types/accountsservice1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/accountsservice1/-/accountsservice1-1.0.1.tgz", + "integrity": "sha512-u2X3p54sl3U5Vptx/DmGVjwf3YdkjqcTaiwPIGBmdhTOcdVBLXbIrVtH50dVkwt6nURASnknOFzVuPBg+jEPCg==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/atk": { + "version": "2.36.7", + "resolved": "https://registry.npmjs.org/@gi-types/atk/-/atk-2.36.7.tgz", + "integrity": "sha512-PWR/EqgBgqehMfDr48fqvy77yGEKLwtqN0qKnKOtWqDm65LbImoAFydlMx5CUeyE/ZNcGDhQGLimQ0Rk6kVivw==", + "dev": true, + "dependencies": { + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/atk1": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/@gi-types/atk1/-/atk1-2.36.0.tgz", + "integrity": "sha512-lCuoaJwqFDV+V+sf4mOcciLCJitiUsGNg/W2Q5Y+21FycPZqlsjgXb0pimo7gLdq11d1lEfHffh3AkY80+7SHw==", + "dev": true, + "dependencies": { + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0" + } + }, + "node_modules/@gi-types/atspi2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/atspi2/-/atspi2-2.0.1.tgz", + "integrity": "sha512-REenxB9zE4J++j92cgqTVKBPS/Pdmlj1JJ38c8t174OObS6nIUyi9B563VyehkfbZN75BZ7sGNihnnsLVsbIrQ==", + "dev": true, + "dependencies": { + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/base-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gi-types/base-types/-/base-types-1.0.0.tgz", + "integrity": "sha512-A+TMfI4f+RgBx5uM5la4tqRrraP8N9B3SDFFPvYyH3h9VMcy03/Fp4X0LVtHuYdAUHmdRR8Z4Got9lwVd975+g==", + "dev": true, + "dependencies": { + "@gi-types/accountsservice1": "^1.0.0", + "@gi-types/atk1": "*", + "@gi-types/atspi2": "*", + "@gi-types/cairo1": "*", + "@gi-types/dbus1": "*", + "@gi-types/gck1": "*", + "@gi-types/gcr3": "*", + "@gi-types/gdkpixbuf2": "*", + "@gi-types/gdm1": "*", + "@gi-types/geoclue2": "*", + "@gi-types/gio2": "*", + "@gi-types/girepository2": "^1.68.0", + "@gi-types/glib2": "*", + "@gi-types/gmodule2": "*", + "@gi-types/gobject2": "*", + "@gi-types/harfbuzz2": "*", + "@gi-types/ibus1": "*", + "@gi-types/json1": "*", + "@gi-types/malcontent0": "*", + "@gi-types/modemmanager1": "*", + "@gi-types/nm1": "*", + "@gi-types/notify0": "*", + "@gi-types/pango1": "*", + "@gi-types/polkit1": "*", + "@gi-types/polkitagent1": "*", + "@gi-types/rsvg2": "*", + "@gi-types/soup2": "*", + "@gi-types/telepathyglib0": "*", + "@gi-types/telepathylogger0": "*", + "@gi-types/upowerglib1": "*", + "@gi-types/xlib2": "*" + } + }, + "node_modules/@gi-types/cairo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/cairo/-/cairo-1.0.3.tgz", + "integrity": "sha512-IT4JbSYgMj2aJbx7Yy8nzHgga2vX5i5OHR2+vXKc9vg/wrnExHiSbacWbxO7CZdk6YBqpH/7cjOa4bkTmBN8tg==", + "dev": true, + "dependencies": { + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/cairo1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/cairo1/-/cairo1-1.0.1.tgz", + "integrity": "sha512-Q92XKfTWVmtF3LBX9Kx7TkdRjeaZeb8llC01p9WgbkVfFX1d2VO4LrqYgeNkThwmJpscHzDzGF0uv5njsfd4xg==", + "dev": true, + "dependencies": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/cally": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/cally/-/cally-7.0.3.tgz", + "integrity": "sha512-mezisdoGvnewxncjpejrAa2XO0WIYsv+jD+olQu49cSU3BIBI9vmVMd1cmkngTGqombkd/2C9KwFMq1ZvEOhiQ==", + "dev": true, + "dependencies": { + "@gi-types/atk": "^2.36.7", + "@gi-types/clutter": "^7.0.6", + "@gi-types/cogl": "^7.0.3", + "@gi-types/coglpango": "^7.0.2", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/clutter": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@gi-types/clutter/-/clutter-7.0.6.tgz", + "integrity": "sha512-hLQ5K+wnq40B1VWFVJPtENw82at2SHHE1CN+BCagjO6L9PpAvO9sCV9uUSbl9K5zrzcfgxYdwFrXW5NP6wSKTg==", + "dev": true, + "dependencies": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/cogl": "^7.0.3", + "@gi-types/coglpango": "^7.0.2", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/graphene": "^1.0.1", + "@gi-types/json": "^1.6.5", + "@gi-types/pango": "^1.0.5" + } + }, + "node_modules/@gi-types/clutterx11": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@gi-types/clutterx11/-/clutterx11-7.0.2.tgz", + "integrity": "sha512-xYJPu1tszpuEkfc6L28DBm2gsuC3pemfi8ixm41I/RI2iIHuhxGEV/PtyOIy2pSM1ZmdItbjRQWq7k0Jz5oggQ==", + "dev": true, + "dependencies": { + "@gi-types/clutter": "^7.0.6", + "@gi-types/cogl": "^7.0.3", + "@gi-types/coglpango": "^7.0.2", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/cogl": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/cogl/-/cogl-7.0.3.tgz", + "integrity": "sha512-yub8mzFnUvZfcBGCxeTS/mShT9pOWcx/FaVmBU+NCEdUlv6LwA+kaVeDJx9SjVFOIrybsJ06q7f/QedkQOfUQQ==", + "dev": true, + "dependencies": { + "@gi-types/cairo": "^1.0.3", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/graphene": "^1.0.1" + } + }, + "node_modules/@gi-types/coglpango": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@gi-types/coglpango/-/coglpango-7.0.2.tgz", + "integrity": "sha512-YH6lmMZeo5HU9mhekIKAtj1OCvK2D+l8hzr1NDz5UIxR1K3oAYqu3sMX1EI0dB24pxcn+mqq/J4xpMO50sCnJA==", + "dev": true, + "dependencies": { + "@gi-types/cogl": "^7.0.3", + "@gi-types/gobject": "^2.66.8", + "@gi-types/pango": "^1.0.5" + } + }, + "node_modules/@gi-types/dbus1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/dbus1/-/dbus1-1.0.1.tgz", + "integrity": "sha512-gFM9UrQCQXxpFB5hg2EW8CRCgY5OTmfidBEh085rux/dqFyGR8lSfDIyN0/RZXrD8rggoQ6JteNhbmNxtQkWmw==", + "dev": true, + "dependencies": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/freetype22": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/freetype22/-/freetype22-2.0.1.tgz", + "integrity": "sha512-3p4XvTSn6o1UCGpla/1KhvMIwZkgIyBtGErbnXuAvGzULtlseQjX2IljY4fi2FKtFd9ngRtIIF71inRPUIj+1w==", + "dev": true, + "dependencies": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/gck": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@gi-types/gck/-/gck-1.0.6.tgz", + "integrity": "sha512-yjbVHprwZ6KE3B/JqMFgHJvAgULuSs0obEWMJFeSpXHgke5Vab8a2Gi0pXdwU5AHVD1rVxhNy/8dkRdiIucwoA==", + "dev": true, + "dependencies": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/gck1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gck1/-/gck1-1.0.1.tgz", + "integrity": "sha512-EkLhQvsb/qHBffTJ5j9aK/KX2nE8LTHDbPSMrXtaTWz3yQYuRDqTyXJWVVUxfcXwKliE9GYyqEOAiXlXCamFHQ==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/gcr": { + "version": "3.38.8", + "resolved": "https://registry.npmjs.org/@gi-types/gcr/-/gcr-3.38.8.tgz", + "integrity": "sha512-UuIStqiXrcXA9RmQZJ/T0K5WpFt5GtwVgLooSOVMflnn28ltx7gA0OVD/keUOjGqVPtubr102Jvzu+KNSGbDxQ==", + "dev": true, + "dependencies": { + "@gi-types/gck": "^1.0.6", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.9" + } + }, + "node_modules/@gi-types/gcr3": { + "version": "3.41.1", + "resolved": "https://registry.npmjs.org/@gi-types/gcr3/-/gcr3-3.41.1.tgz", + "integrity": "sha512-sZWGbOQlbtwNoggDDBqFxD1lAyGpIEg7TR5RVVrcQfaMwo7yIkRahvXCy7XCoYnWsEmAw46fEdUI5408Ykn8/g==", + "dev": true, + "dependencies": { + "@gi-types/gck1": "^1.0.1", + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/gdesktopenums": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/gdesktopenums/-/gdesktopenums-3.0.3.tgz", + "integrity": "sha512-2Ng2uCCZcgASARPcIxapkTFrkM5std3ANUeW1pc4CJ4ZXLYgwSad8IQxJ4wB7hXkhZR9CYXmoA4KBmScjaHfyA==", + "dev": true, + "dependencies": { + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/gdk": { + "version": "3.24.7", + "resolved": "https://registry.npmjs.org/@gi-types/gdk/-/gdk-3.24.7.tgz", + "integrity": "sha512-t50PlPeiqeNNQvXcKuso7/aiy1zm2zNtDgXZgcmtCfjk2u9m4m5gC4/kePzfYtSBqo15FZdv747ESRLC0zNk6Q==", + "dev": true, + "dependencies": { + "@gi-types/cairo": "^1.0.3", + "@gi-types/gdkpixbuf": "^2.0.5", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/pango": "^1.0.5" + } + }, + "node_modules/@gi-types/gdk4": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gdk4/-/gdk4-4.0.1.tgz", + "integrity": "sha512-Msf4bWdw8A0qorTvbvWgCwMno8nUCGLiDcEhNI6iqh6o8IJKchID9a51j6KeqpHkwSPZAcaR2TKM+CVafa4pOw==", + "dev": true, + "dependencies": { + "@gi-types/cairo1": "^1.0.0", + "@gi-types/gdkpixbuf2": "^2.0.0", + "@gi-types/gio2": "^2.68.0", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0", + "@gi-types/pango1": "^1.0.0" + } + }, + "node_modules/@gi-types/gdkpixbuf": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@gi-types/gdkpixbuf/-/gdkpixbuf-2.0.5.tgz", + "integrity": "sha512-bLw6CFCbM0SNi30vkbupoVxE7T5hz+V6DQHLa1Sh0yYwm8OTQ8jYofZNTabfjBiTR3Z4dxtN65DkIpJoD+9fuQ==", + "dev": true, + "dependencies": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/gdkpixbuf2": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@gi-types/gdkpixbuf2/-/gdkpixbuf2-2.0.2.tgz", + "integrity": "sha512-vwopihyKOjJszDPvMj+T2XO6hMWYRmhW/uSrfCeCudMhzmJBrpIzmRZTYfeTgMMDU8cuWydCjacbiD7hSio0tw==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gmodule2": "^2.0.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/gdm1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gdm1/-/gdm1-1.0.1.tgz", + "integrity": "sha512-XfrIzC4fI8SxnjjJ16d3PA5FVMOnkyZrEdg4fYtLK4m/GdytrivAgxsousXlnlIEXuDUA4Nss7ycja6TIa70NA==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/geoclue2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/geoclue2/-/geoclue2-2.0.1.tgz", + "integrity": "sha512-7X7URSNs3fdrER2uUdWmU4FaQry7aBaCyCGTNitmAwKigijv1OihI6oDyIx9Y24rmwrz6Im2+tKrg0bugnxJjQ==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/gio": { + "version": "2.66.9", + "resolved": "https://registry.npmjs.org/@gi-types/gio/-/gio-2.66.9.tgz", + "integrity": "sha512-hRqJqQSQ4jLjUSiS7XZ7pXvIdu/+ZuFvIGPqrLFXyYa+aTenHiYg/EaLR3iPbAzkvpyBLRZk7Pmgs0quoK+Vtg==", + "dev": true, + "dependencies": { + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/gio2": { + "version": "2.72.1", + "resolved": "https://registry.npmjs.org/@gi-types/gio2/-/gio2-2.72.1.tgz", + "integrity": "sha512-V3ASxEbq4xBdw/rcROEbNrTqtFtWfwFOcOsXO+48JPIEqZRTJZcf9LX/ATxi9hxaZXogpmOBMsxuyNsd5ZQXyA==", + "dev": true, + "dependencies": { + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/girepository2": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@gi-types/girepository2/-/girepository2-1.68.0.tgz", + "integrity": "sha512-OYTsgMdQ1UWua6Bvx/rSL29qn9CCZ1rMpoBsfvDs5ol+9nNYsxUIl7AvRIojEk/IbWmYh/K9Kv3Mqi3ntB3aTA==", + "dev": true, + "dependencies": { + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0" + } + }, + "node_modules/@gi-types/gjs-environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@gi-types/gjs-environment/-/gjs-environment-1.1.0.tgz", + "integrity": "sha512-WXQ1xwuLou5qobQ76LyYxU5bgZYNbkypm3ZLphNWZLuj8C9MbWmb9BJihdq2IAAyH6+HIrrN6ss1syu7IJZUaA==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.68.0", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0" + } + }, + "node_modules/@gi-types/glib": { + "version": "2.66.10", + "resolved": "https://registry.npmjs.org/@gi-types/glib/-/glib-2.66.10.tgz", + "integrity": "sha512-x34Y/xngzSS/BjA38/XpZjAzQqdWdUFtEVKsAGIMi4g+2ZES5QvBLkg/vNHwzSeoY/vUf+E/aZ3+6xQZFUzRjg==", + "dev": true, + "dependencies": { + "@gi-types/gobject": "^2.66.9" + } + }, + "node_modules/@gi-types/glib2": { + "version": "2.72.1", + "resolved": "https://registry.npmjs.org/@gi-types/glib2/-/glib2-2.72.1.tgz", + "integrity": "sha512-Mfhs1UGV1a0GOLolqA1xC2oVV5cILbJjGh/M9Db3irqWNIWGM0LUFkoG2nRMBpJ0enhIYbqY9CCpGlMpAnyxOw==", + "dev": true, + "dependencies": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/gmodule2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gmodule2/-/gmodule2-2.0.1.tgz", + "integrity": "sha512-thIYqYL3ALKBLR/zpjrlXro1m9UTv6eAYyuIdpPtmQvlaggBLoUakYl6mzNmqcb22OIlq/8jJrVx+cjFxdPd/A==", + "dev": true, + "dependencies": { + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/gobject": { + "version": "2.66.9", + "resolved": "https://registry.npmjs.org/@gi-types/gobject/-/gobject-2.66.9.tgz", + "integrity": "sha512-fr6a0W0UfLQiYYuQg4ZY+u4oFgC0QacjffbCBupiVHyC4NVDH2FbWiEVQEb0V52QmSEZTAfRyc/ZgmDzwpUXuw==", + "dev": true, + "dependencies": { + "@gi-types/glib": "^2.66.9" + } + }, + "node_modules/@gi-types/gobject2": { + "version": "2.72.1", + "resolved": "https://registry.npmjs.org/@gi-types/gobject2/-/gobject2-2.72.1.tgz", + "integrity": "sha512-1UUD2zzwRo7vJLu0J62L1cgB39wjYNs1+6qS0UBhdLhzkOWK+CnVII9CNh21Z4YBs0Oa2d5REeGUiZO4d7a6Zw==", + "dev": true, + "dependencies": { + "@gi-types/glib2": "^2.72.1" + } + }, + "node_modules/@gi-types/graphene": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/graphene/-/graphene-1.0.1.tgz", + "integrity": "sha512-XWzEdAWdvsJH2ieraRhbXqyoFXXG2HP2P8amSpryBsXuWaiessnhsNKgB+pu93cgrW029nnYJMb6AUJodASMrw==", + "dev": true, + "dependencies": { + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/graphene1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/graphene1/-/graphene1-1.0.1.tgz", + "integrity": "sha512-O5cJG/eIi912eF1zcaXrSur25LgMJDU+VhFDnYuO4Latj32eypFJf5Sw3KOZvN2EoctIBxjutbXLOxYYKMBhHA==", + "dev": true, + "dependencies": { + "@gi-types/gobject2": "^2.68.0" + } + }, + "node_modules/@gi-types/gsk4": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gsk4/-/gsk4-4.0.1.tgz", + "integrity": "sha512-/OtGBQwwtyQw7vjkfC4/P2pQf9hrtB89sXhDyKQG5B2UJ/LTAQQJNK7vu4u2W0Y922WK/zqrzimBy38JUoPO5Q==", + "dev": true, + "dependencies": { + "@gi-types/cairo1": "^1.0.0", + "@gi-types/gdk4": "^4.0.1", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0", + "@gi-types/graphene1": "^1.0.1", + "@gi-types/pango1": "^1.0.0" + } + }, + "node_modules/@gi-types/gtk": { + "version": "3.24.9", + "resolved": "https://registry.npmjs.org/@gi-types/gtk/-/gtk-3.24.9.tgz", + "integrity": "sha512-U2LH7hKlIvPAvjVe0eQX8s6MDjYaFKCRpAGmwR3lIoBPIdvYOyy76odGY1suFKrepBa97iW6JMbEcv/0f2D98w==", + "dev": true, + "dependencies": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/gdk": "^3.24.7", + "@gi-types/gdkpixbuf": "^2.0.5", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.9", + "@gi-types/pango": "^1.0.5", + "@gi-types/xlib": "^2.0.0" + } + }, + "node_modules/@gi-types/gtk4": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@gi-types/gtk4/-/gtk4-4.6.1.tgz", + "integrity": "sha512-WtdHV2S/xParqwJX9BJ/OsGTzoEorEhVAP0W2eJpUk5rqfSlzIXHnNvFvs9T5sT43fRZmefVd3Id1EbxsHKeLg==", + "dev": true, + "dependencies": { + "@gi-types/cairo1": "^1.0.0", + "@gi-types/gdk4": "^4.0.1", + "@gi-types/gdkpixbuf2": "^2.0.0", + "@gi-types/gio2": "^2.68.0", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0", + "@gi-types/graphene1": "^1.0.1", + "@gi-types/gsk4": "^4.0.1", + "@gi-types/pango1": "^1.50.1" + } + }, + "node_modules/@gi-types/gtk4-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gi-types/gtk4-types/-/gtk4-types-1.0.0.tgz", + "integrity": "sha512-Y7SNMhAX9bUBK/HfJbidMePIjsa/7B6WroLR2nZF3afIzqjYgEugmbYWC85516z0+c1/3Z4eXC8V3j1CleXmGQ==", + "dev": true, + "dependencies": { + "@gi-types/gdk4": "*", + "@gi-types/graphene1": "*", + "@gi-types/gsk4": "*", + "@gi-types/gtk4": "*" + } + }, + "node_modules/@gi-types/gvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@gi-types/gvc/-/gvc-1.0.2.tgz", + "integrity": "sha512-u5QMX4nI6FJSvnc/Se0JObycRlMznTz6BlZ2CM84i9ePhL0K6vplM0UIeTfWDxJiwS+M9mofQnA10uWt8CZRhA==", + "dev": true, + "dependencies": { + "@gi-types/gio": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/harfbuzz": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@gi-types/harfbuzz/-/harfbuzz-0.0.0.tgz", + "integrity": "sha512-pqhpLBN5aYl2P6yOPo0HCZTLmcTDiynGkEgfEq1yPwY0qhJpuEJYmaVUzwb7RrQrt7absVZP7ClhhO8TYiporw==", + "dev": true, + "dependencies": { + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/harfbuzz2": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@gi-types/harfbuzz2/-/harfbuzz2-4.4.2.tgz", + "integrity": "sha512-jsntc6SthoOdw4VSetTOKFI0BM1ccDISP1JQhnL5NETrVuYGnNAwRZ2oDIujuG0M70bHDv/MWKiZnmJxXNPrKw==", + "dev": true, + "dependencies": { + "@gi-types/freetype22": "^2.0.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/ibus1": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@gi-types/ibus1/-/ibus1-1.5.1.tgz", + "integrity": "sha512-pwPk1H0zlf3u5DQ0g3ENfN4PY9U1mu5N6dRHYocwXsmENs0EEZvYrHa+SfUC2DEpsIvvMoAfm2lwKAVQ7KqNRg==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/json": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@gi-types/json/-/json-1.6.6.tgz", + "integrity": "sha512-YtnjubEVEmnGirOlcwNkwD9hftaOKuJv2t0HnNFXcHuBCZpD72Jt5iuUtyFFvF7cduVR/jAXh71fw5U3kMYB8g==", + "dev": true, + "dependencies": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.9" + } + }, + "node_modules/@gi-types/json1": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@gi-types/json1/-/json1-1.6.1.tgz", + "integrity": "sha512-6DpNf3PTYYKbL96U76R/mWZuAXv32cI0ZDalyKAciKeh9pczHjlnNdjq8h6lDKJc5vKxviHW9TsOdtB3NaXuqQ==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/malcontent0": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/malcontent0/-/malcontent0-0.0.1.tgz", + "integrity": "sha512-zB/M/ew8P1ZEqeXChK9b5PAoqq5TrmkTZ3NkZ0R/DPX/xt7aZRNPwdxrFt9enzrlKImjthcctp/tpk48MgGoKg==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/meta": { + "version": "3.38.5", + "resolved": "https://registry.npmjs.org/@gi-types/meta/-/meta-3.38.5.tgz", + "integrity": "sha512-EzYGvZu58j/hDZv/zQX4ooL0Pq4TEP2Okhn0JlVmGpFIB65ZWOkCHQqj3LODdpp327+cWMNYkG5WHZXJAmtuZQ==", + "dev": true, + "dependencies": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/clutter": "^7.0.6", + "@gi-types/cogl": "^7.0.3", + "@gi-types/coglpango": "^7.0.2", + "@gi-types/gdesktopenums": "^3.0.3", + "@gi-types/gdk": "^3.24.7", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/gtk": "^3.24.8", + "@gi-types/json": "^1.6.5", + "@gi-types/pango": "^1.0.5" + } + }, + "node_modules/@gi-types/modemmanager1": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@gi-types/modemmanager1/-/modemmanager1-1.18.1.tgz", + "integrity": "sha512-eQgs3B0S61qtrIIoa9APcmszDGsVEbxZJwlAOak/4ifF4HYrF42fsTDQslzGoAe0aDe9B/FJe2ycR8LgMgquhg==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/nm": { + "version": "1.28.9", + "resolved": "https://registry.npmjs.org/@gi-types/nm/-/nm-1.28.9.tgz", + "integrity": "sha512-sZd0HWWJ4aeXTrbnmTlN80jkjqdAohGhhAbtdfrlnIzrR76hROuXe+vxs1dn++FwV/67c1hQuxCERD3JyqEfnw==", + "dev": true, + "dependencies": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/nm1": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/@gi-types/nm1/-/nm1-1.38.1.tgz", + "integrity": "sha512-TZNDTSxPRFjv+kjI/RyqLBO9Rb1FVDuUTfUyB9znGuj4NnPvQ5lzLRlvSG5SIyRYbeu1hik7lGteK+mM/NslVQ==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/notify0": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@gi-types/notify0/-/notify0-0.7.2.tgz", + "integrity": "sha512-cEhvHAffDmZS/Jy5mB+ZrSRMr+2DOt0exfmsHFodbG1nS44kpMaTtpKMHf1uwLuoUTjzdUx/XoTSHuHZsYQ+sw==", + "dev": true, + "dependencies": { + "@gi-types/gdkpixbuf2": "^2.0.2", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/pango": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@gi-types/pango/-/pango-1.0.5.tgz", + "integrity": "sha512-FcDRuMaiYwczKcxE11ZErlOCcbBQX69d/u2hKFkhpZxQrZk0fBNTGLbwJ/7QwAoQw4RsrCzYSOZ4TX0phLPQQw==", + "dev": true, + "dependencies": { + "@gi-types/cairo": "^1.0.3", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/harfbuzz": "^0.0.0" + } + }, + "node_modules/@gi-types/pango1": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@gi-types/pango1/-/pango1-1.50.1.tgz", + "integrity": "sha512-SdpoZ5Fcz7Zo8Tal5Ap4Z/4SMqpo/PspORIadxmNZ3kafFOwK7TPvdWiUIBgua+i3uWiLnreKCGD/6rLJLbw3g==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1", + "@gi-types/harfbuzz2": "^4.4.2" + } + }, + "node_modules/@gi-types/polkit": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@gi-types/polkit/-/polkit-1.0.6.tgz", + "integrity": "sha512-l7FLcXXzN0ttWjRWIblHHhwZje+jzPdLbXKPNJtRmllBe2ISxVNU31Y+yxxdyXTO1JHr2R543isWEGu2yEmmAA==", + "dev": true, + "dependencies": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "node_modules/@gi-types/polkit1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/polkit1/-/polkit1-1.0.1.tgz", + "integrity": "sha512-M+9OPCx0jeOGe28uN6fegTsYOMuNnjZbgw1sJp6wXRRswihaceqHvuKSePo7dhyC+rx8knS04Cde1rYFQlZiBg==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/polkitagent": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/polkitagent/-/polkitagent-1.0.3.tgz", + "integrity": "sha512-x/u0LmV6EC8TACtxlKMIqyAYEur0FRF5P//1wMrBjWdbD0pfBm+KxMvXNCIjs3pHJ1lAZRc9t7Lo4HE03ICPew==", + "dev": true, + "dependencies": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/polkit": "^1.0.6" + } + }, + "node_modules/@gi-types/polkitagent1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/polkitagent1/-/polkitagent1-1.0.1.tgz", + "integrity": "sha512-klFuDVAoFomKpQBx6JtigNhAiuMnkeKEUj8pewR+n3wraVFJ1K/pyT/O14UYR6Qgl8smOBFybyQv15tgc35HnA==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1", + "@gi-types/polkit1": "^1.0.1" + } + }, + "node_modules/@gi-types/rsvg2": { + "version": "2.54.2", + "resolved": "https://registry.npmjs.org/@gi-types/rsvg2/-/rsvg2-2.54.2.tgz", + "integrity": "sha512-FVrNq2JdQZLaUmJeeIGfF79EHzbcd8TqUlaAH8y8f2Yy7Gc4FyBqC+BtnSJ2bN65tus2khPN3YYqVc+YDDIZLw==", + "dev": true, + "dependencies": { + "@gi-types/cairo1": "^1.0.1", + "@gi-types/gdkpixbuf2": "^2.0.2", + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/shell": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@gi-types/shell/-/shell-0.1.6.tgz", + "integrity": "sha512-CFM3e4D02ZqVtOwE9PtPgp+qrxN25vNYXUKFrWj5BZtlrOnie1uC03+riu7f4zSpLfOW8BWX4KpZxycoH9AUbw==", + "dev": true, + "dependencies": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/clutter": "^7.0.6", + "@gi-types/clutterx11": "^7.0.2", + "@gi-types/gcr": "^3.38.7", + "@gi-types/gdkpixbuf": "^2.0.5", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/gtk": "^3.24.8", + "@gi-types/gvc": "^1.0.2", + "@gi-types/json": "^1.6.5", + "@gi-types/meta": "^3.38.5", + "@gi-types/nm": "^1.28.9", + "@gi-types/polkitagent": "^1.0.3", + "@gi-types/st": "^1.0.6" + } + }, + "node_modules/@gi-types/soup2": { + "version": "2.74.1", + "resolved": "https://registry.npmjs.org/@gi-types/soup2/-/soup2-2.74.1.tgz", + "integrity": "sha512-K11KSN032DFAc3RTTBtPiZ9utWqw4CJ7wUAG1cMRdQtRBD44Xi2o2r9RuZkqUhT12V0vU7ZafF8Apb6PM6AOpg==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/st": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@gi-types/st/-/st-1.0.6.tgz", + "integrity": "sha512-xXFs7t19Jmy/GVC9CJFk8oByI546BZdGG/JXx+wN54YG8d+1nX3RiZ2nVNgp2y1gYhJDTcHBRZFnPs+7QUduoQ==", + "dev": true, + "dependencies": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/cally": "^7.0.3", + "@gi-types/clutter": "^7.0.6", + "@gi-types/cogl": "^7.0.3", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/gtk": "^3.24.8", + "@gi-types/json": "^1.6.5", + "@gi-types/meta": "^3.38.5", + "@gi-types/pango": "^1.0.5" + } + }, + "node_modules/@gi-types/telepathyglib0": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@gi-types/telepathyglib0/-/telepathyglib0-0.12.1.tgz", + "integrity": "sha512-c/etfKk0Fdts4GCu0M9cVn13Xev5jqgfwWiFiwi61Y9wUNFw9AlXEhth2FlcXhPuhI58EvhWgvuiizooAVnT0w==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/telepathylogger0": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@gi-types/telepathylogger0/-/telepathylogger0-0.2.1.tgz", + "integrity": "sha512-FSgUMaXT4W9AL8qrmiHxGw6/D/78yURmKyPhIQP8abn64aiTizktDrViMO/8xaV86NMvM31dotnOU0gWx2IgUg==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1", + "@gi-types/telepathyglib0": "^0.12.1" + } + }, + "node_modules/@gi-types/upowerglib1": { + "version": "0.99.1", + "resolved": "https://registry.npmjs.org/@gi-types/upowerglib1/-/upowerglib1-0.99.1.tgz", + "integrity": "sha512-51DF1nxsMbr3n1VAidfuDuyc7CrotflWfXfTj100+CooOvYsLcH6FbjkbkLGTP9yFGPQO3ahbsr12AeYaJkEQA==", + "dev": true, + "dependencies": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@gi-types/xlib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@gi-types/xlib/-/xlib-2.0.0.tgz", + "integrity": "sha512-YHNrcCw/i8QX3sh5bZ+SN7xEDrmhKIqAoUh/JgiDyPqqH2du+BpQUZy/e0oM0AIZWPNFKgD5M5Bq0+1MvVS3IA==", + "dev": true, + "dependencies": { + "@gi-types/gobject": "^2.66.9" + } + }, + "node_modules/@gi-types/xlib2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/xlib2/-/xlib2-2.0.1.tgz", + "integrity": "sha512-AouZupNa8roCOwKbyGuVuOmt7n8YYBprH60q5Ggu9vGVY+AhpTjEbeJBiLkHW74T8587Z3AilRo7cBWnXPM56A==", + "dev": true, + "dependencies": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", + "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.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": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "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.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz", + "integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/type-utils": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.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.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz", + "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.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.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz", + "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.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.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz", + "integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/utils": "5.46.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.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz", + "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==", + "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.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz", + "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.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.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.0.tgz", + "integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "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.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz", + "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.46.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/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "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/comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, + "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.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "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.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "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.15.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "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.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "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-plugin-jsdoc": { + "version": "39.6.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.4.tgz", + "integrity": "sha512-fskvdLCfwmPjHb6e+xNGDtGgbF8X7cDwMtVLAP2WwSf9Htrx68OAx31BESBM1FAwsN2HTQyYQq7m4aW4Q4Nlag==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.36.1", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "semver": "^7.3.8", + "spdx-expression-parse": "^3.0.1" + }, + "engines": { + "node": "^14 || ^16 || ^17 || ^18 || ^19" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "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-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "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.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "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.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "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.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "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.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "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.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", + "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "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/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "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.2.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", + "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "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-sdsl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "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/jsdoc-type-pratt-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", + "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, + "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/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.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "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.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "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/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "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.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "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/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "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": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "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/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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" + } + } + }, + "dependencies": { + "@es-joy/jsdoccomment": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", + "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", + "dev": true, + "requires": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~3.1.0" + } + }, + "@eslint/eslintrc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.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" + } + }, + "@gi-types/accountsservice1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/accountsservice1/-/accountsservice1-1.0.1.tgz", + "integrity": "sha512-u2X3p54sl3U5Vptx/DmGVjwf3YdkjqcTaiwPIGBmdhTOcdVBLXbIrVtH50dVkwt6nURASnknOFzVuPBg+jEPCg==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/atk": { + "version": "2.36.7", + "resolved": "https://registry.npmjs.org/@gi-types/atk/-/atk-2.36.7.tgz", + "integrity": "sha512-PWR/EqgBgqehMfDr48fqvy77yGEKLwtqN0qKnKOtWqDm65LbImoAFydlMx5CUeyE/ZNcGDhQGLimQ0Rk6kVivw==", + "dev": true, + "requires": { + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/atk1": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/@gi-types/atk1/-/atk1-2.36.0.tgz", + "integrity": "sha512-lCuoaJwqFDV+V+sf4mOcciLCJitiUsGNg/W2Q5Y+21FycPZqlsjgXb0pimo7gLdq11d1lEfHffh3AkY80+7SHw==", + "dev": true, + "requires": { + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0" + } + }, + "@gi-types/atspi2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/atspi2/-/atspi2-2.0.1.tgz", + "integrity": "sha512-REenxB9zE4J++j92cgqTVKBPS/Pdmlj1JJ38c8t174OObS6nIUyi9B563VyehkfbZN75BZ7sGNihnnsLVsbIrQ==", + "dev": true, + "requires": { + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/base-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gi-types/base-types/-/base-types-1.0.0.tgz", + "integrity": "sha512-A+TMfI4f+RgBx5uM5la4tqRrraP8N9B3SDFFPvYyH3h9VMcy03/Fp4X0LVtHuYdAUHmdRR8Z4Got9lwVd975+g==", + "dev": true, + "requires": { + "@gi-types/accountsservice1": "^1.0.0", + "@gi-types/atk1": "*", + "@gi-types/atspi2": "*", + "@gi-types/cairo1": "*", + "@gi-types/dbus1": "*", + "@gi-types/gck1": "*", + "@gi-types/gcr3": "*", + "@gi-types/gdkpixbuf2": "*", + "@gi-types/gdm1": "*", + "@gi-types/geoclue2": "*", + "@gi-types/gio2": "*", + "@gi-types/girepository2": "^1.68.0", + "@gi-types/glib2": "*", + "@gi-types/gmodule2": "*", + "@gi-types/gobject2": "*", + "@gi-types/harfbuzz2": "*", + "@gi-types/ibus1": "*", + "@gi-types/json1": "*", + "@gi-types/malcontent0": "*", + "@gi-types/modemmanager1": "*", + "@gi-types/nm1": "*", + "@gi-types/notify0": "*", + "@gi-types/pango1": "*", + "@gi-types/polkit1": "*", + "@gi-types/polkitagent1": "*", + "@gi-types/rsvg2": "*", + "@gi-types/soup2": "*", + "@gi-types/telepathyglib0": "*", + "@gi-types/telepathylogger0": "*", + "@gi-types/upowerglib1": "*", + "@gi-types/xlib2": "*" + } + }, + "@gi-types/cairo": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/cairo/-/cairo-1.0.3.tgz", + "integrity": "sha512-IT4JbSYgMj2aJbx7Yy8nzHgga2vX5i5OHR2+vXKc9vg/wrnExHiSbacWbxO7CZdk6YBqpH/7cjOa4bkTmBN8tg==", + "dev": true, + "requires": { + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/cairo1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/cairo1/-/cairo1-1.0.1.tgz", + "integrity": "sha512-Q92XKfTWVmtF3LBX9Kx7TkdRjeaZeb8llC01p9WgbkVfFX1d2VO4LrqYgeNkThwmJpscHzDzGF0uv5njsfd4xg==", + "dev": true, + "requires": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/cally": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/cally/-/cally-7.0.3.tgz", + "integrity": "sha512-mezisdoGvnewxncjpejrAa2XO0WIYsv+jD+olQu49cSU3BIBI9vmVMd1cmkngTGqombkd/2C9KwFMq1ZvEOhiQ==", + "dev": true, + "requires": { + "@gi-types/atk": "^2.36.7", + "@gi-types/clutter": "^7.0.6", + "@gi-types/cogl": "^7.0.3", + "@gi-types/coglpango": "^7.0.2", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/clutter": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@gi-types/clutter/-/clutter-7.0.6.tgz", + "integrity": "sha512-hLQ5K+wnq40B1VWFVJPtENw82at2SHHE1CN+BCagjO6L9PpAvO9sCV9uUSbl9K5zrzcfgxYdwFrXW5NP6wSKTg==", + "dev": true, + "requires": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/cogl": "^7.0.3", + "@gi-types/coglpango": "^7.0.2", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/graphene": "^1.0.1", + "@gi-types/json": "^1.6.5", + "@gi-types/pango": "^1.0.5" + } + }, + "@gi-types/clutterx11": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@gi-types/clutterx11/-/clutterx11-7.0.2.tgz", + "integrity": "sha512-xYJPu1tszpuEkfc6L28DBm2gsuC3pemfi8ixm41I/RI2iIHuhxGEV/PtyOIy2pSM1ZmdItbjRQWq7k0Jz5oggQ==", + "dev": true, + "requires": { + "@gi-types/clutter": "^7.0.6", + "@gi-types/cogl": "^7.0.3", + "@gi-types/coglpango": "^7.0.2", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/cogl": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/cogl/-/cogl-7.0.3.tgz", + "integrity": "sha512-yub8mzFnUvZfcBGCxeTS/mShT9pOWcx/FaVmBU+NCEdUlv6LwA+kaVeDJx9SjVFOIrybsJ06q7f/QedkQOfUQQ==", + "dev": true, + "requires": { + "@gi-types/cairo": "^1.0.3", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/graphene": "^1.0.1" + } + }, + "@gi-types/coglpango": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@gi-types/coglpango/-/coglpango-7.0.2.tgz", + "integrity": "sha512-YH6lmMZeo5HU9mhekIKAtj1OCvK2D+l8hzr1NDz5UIxR1K3oAYqu3sMX1EI0dB24pxcn+mqq/J4xpMO50sCnJA==", + "dev": true, + "requires": { + "@gi-types/cogl": "^7.0.3", + "@gi-types/gobject": "^2.66.8", + "@gi-types/pango": "^1.0.5" + } + }, + "@gi-types/dbus1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/dbus1/-/dbus1-1.0.1.tgz", + "integrity": "sha512-gFM9UrQCQXxpFB5hg2EW8CRCgY5OTmfidBEh085rux/dqFyGR8lSfDIyN0/RZXrD8rggoQ6JteNhbmNxtQkWmw==", + "dev": true, + "requires": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/freetype22": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/freetype22/-/freetype22-2.0.1.tgz", + "integrity": "sha512-3p4XvTSn6o1UCGpla/1KhvMIwZkgIyBtGErbnXuAvGzULtlseQjX2IljY4fi2FKtFd9ngRtIIF71inRPUIj+1w==", + "dev": true, + "requires": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/gck": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@gi-types/gck/-/gck-1.0.6.tgz", + "integrity": "sha512-yjbVHprwZ6KE3B/JqMFgHJvAgULuSs0obEWMJFeSpXHgke5Vab8a2Gi0pXdwU5AHVD1rVxhNy/8dkRdiIucwoA==", + "dev": true, + "requires": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/gck1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gck1/-/gck1-1.0.1.tgz", + "integrity": "sha512-EkLhQvsb/qHBffTJ5j9aK/KX2nE8LTHDbPSMrXtaTWz3yQYuRDqTyXJWVVUxfcXwKliE9GYyqEOAiXlXCamFHQ==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/gcr": { + "version": "3.38.8", + "resolved": "https://registry.npmjs.org/@gi-types/gcr/-/gcr-3.38.8.tgz", + "integrity": "sha512-UuIStqiXrcXA9RmQZJ/T0K5WpFt5GtwVgLooSOVMflnn28ltx7gA0OVD/keUOjGqVPtubr102Jvzu+KNSGbDxQ==", + "dev": true, + "requires": { + "@gi-types/gck": "^1.0.6", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.9" + } + }, + "@gi-types/gcr3": { + "version": "3.41.1", + "resolved": "https://registry.npmjs.org/@gi-types/gcr3/-/gcr3-3.41.1.tgz", + "integrity": "sha512-sZWGbOQlbtwNoggDDBqFxD1lAyGpIEg7TR5RVVrcQfaMwo7yIkRahvXCy7XCoYnWsEmAw46fEdUI5408Ykn8/g==", + "dev": true, + "requires": { + "@gi-types/gck1": "^1.0.1", + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/gdesktopenums": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/gdesktopenums/-/gdesktopenums-3.0.3.tgz", + "integrity": "sha512-2Ng2uCCZcgASARPcIxapkTFrkM5std3ANUeW1pc4CJ4ZXLYgwSad8IQxJ4wB7hXkhZR9CYXmoA4KBmScjaHfyA==", + "dev": true, + "requires": { + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/gdk": { + "version": "3.24.7", + "resolved": "https://registry.npmjs.org/@gi-types/gdk/-/gdk-3.24.7.tgz", + "integrity": "sha512-t50PlPeiqeNNQvXcKuso7/aiy1zm2zNtDgXZgcmtCfjk2u9m4m5gC4/kePzfYtSBqo15FZdv747ESRLC0zNk6Q==", + "dev": true, + "requires": { + "@gi-types/cairo": "^1.0.3", + "@gi-types/gdkpixbuf": "^2.0.5", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/pango": "^1.0.5" + } + }, + "@gi-types/gdk4": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gdk4/-/gdk4-4.0.1.tgz", + "integrity": "sha512-Msf4bWdw8A0qorTvbvWgCwMno8nUCGLiDcEhNI6iqh6o8IJKchID9a51j6KeqpHkwSPZAcaR2TKM+CVafa4pOw==", + "dev": true, + "requires": { + "@gi-types/cairo1": "^1.0.0", + "@gi-types/gdkpixbuf2": "^2.0.0", + "@gi-types/gio2": "^2.68.0", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0", + "@gi-types/pango1": "^1.0.0" + } + }, + "@gi-types/gdkpixbuf": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@gi-types/gdkpixbuf/-/gdkpixbuf-2.0.5.tgz", + "integrity": "sha512-bLw6CFCbM0SNi30vkbupoVxE7T5hz+V6DQHLa1Sh0yYwm8OTQ8jYofZNTabfjBiTR3Z4dxtN65DkIpJoD+9fuQ==", + "dev": true, + "requires": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/gdkpixbuf2": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@gi-types/gdkpixbuf2/-/gdkpixbuf2-2.0.2.tgz", + "integrity": "sha512-vwopihyKOjJszDPvMj+T2XO6hMWYRmhW/uSrfCeCudMhzmJBrpIzmRZTYfeTgMMDU8cuWydCjacbiD7hSio0tw==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gmodule2": "^2.0.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/gdm1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gdm1/-/gdm1-1.0.1.tgz", + "integrity": "sha512-XfrIzC4fI8SxnjjJ16d3PA5FVMOnkyZrEdg4fYtLK4m/GdytrivAgxsousXlnlIEXuDUA4Nss7ycja6TIa70NA==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/geoclue2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/geoclue2/-/geoclue2-2.0.1.tgz", + "integrity": "sha512-7X7URSNs3fdrER2uUdWmU4FaQry7aBaCyCGTNitmAwKigijv1OihI6oDyIx9Y24rmwrz6Im2+tKrg0bugnxJjQ==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/gio": { + "version": "2.66.9", + "resolved": "https://registry.npmjs.org/@gi-types/gio/-/gio-2.66.9.tgz", + "integrity": "sha512-hRqJqQSQ4jLjUSiS7XZ7pXvIdu/+ZuFvIGPqrLFXyYa+aTenHiYg/EaLR3iPbAzkvpyBLRZk7Pmgs0quoK+Vtg==", + "dev": true, + "requires": { + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/gio2": { + "version": "2.72.1", + "resolved": "https://registry.npmjs.org/@gi-types/gio2/-/gio2-2.72.1.tgz", + "integrity": "sha512-V3ASxEbq4xBdw/rcROEbNrTqtFtWfwFOcOsXO+48JPIEqZRTJZcf9LX/ATxi9hxaZXogpmOBMsxuyNsd5ZQXyA==", + "dev": true, + "requires": { + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/girepository2": { + "version": "1.68.0", + "resolved": "https://registry.npmjs.org/@gi-types/girepository2/-/girepository2-1.68.0.tgz", + "integrity": "sha512-OYTsgMdQ1UWua6Bvx/rSL29qn9CCZ1rMpoBsfvDs5ol+9nNYsxUIl7AvRIojEk/IbWmYh/K9Kv3Mqi3ntB3aTA==", + "dev": true, + "requires": { + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0" + } + }, + "@gi-types/gjs-environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@gi-types/gjs-environment/-/gjs-environment-1.1.0.tgz", + "integrity": "sha512-WXQ1xwuLou5qobQ76LyYxU5bgZYNbkypm3ZLphNWZLuj8C9MbWmb9BJihdq2IAAyH6+HIrrN6ss1syu7IJZUaA==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.68.0", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0" + } + }, + "@gi-types/glib": { + "version": "2.66.10", + "resolved": "https://registry.npmjs.org/@gi-types/glib/-/glib-2.66.10.tgz", + "integrity": "sha512-x34Y/xngzSS/BjA38/XpZjAzQqdWdUFtEVKsAGIMi4g+2ZES5QvBLkg/vNHwzSeoY/vUf+E/aZ3+6xQZFUzRjg==", + "dev": true, + "requires": { + "@gi-types/gobject": "^2.66.9" + } + }, + "@gi-types/glib2": { + "version": "2.72.1", + "resolved": "https://registry.npmjs.org/@gi-types/glib2/-/glib2-2.72.1.tgz", + "integrity": "sha512-Mfhs1UGV1a0GOLolqA1xC2oVV5cILbJjGh/M9Db3irqWNIWGM0LUFkoG2nRMBpJ0enhIYbqY9CCpGlMpAnyxOw==", + "dev": true, + "requires": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/gmodule2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gmodule2/-/gmodule2-2.0.1.tgz", + "integrity": "sha512-thIYqYL3ALKBLR/zpjrlXro1m9UTv6eAYyuIdpPtmQvlaggBLoUakYl6mzNmqcb22OIlq/8jJrVx+cjFxdPd/A==", + "dev": true, + "requires": { + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/gobject": { + "version": "2.66.9", + "resolved": "https://registry.npmjs.org/@gi-types/gobject/-/gobject-2.66.9.tgz", + "integrity": "sha512-fr6a0W0UfLQiYYuQg4ZY+u4oFgC0QacjffbCBupiVHyC4NVDH2FbWiEVQEb0V52QmSEZTAfRyc/ZgmDzwpUXuw==", + "dev": true, + "requires": { + "@gi-types/glib": "^2.66.9" + } + }, + "@gi-types/gobject2": { + "version": "2.72.1", + "resolved": "https://registry.npmjs.org/@gi-types/gobject2/-/gobject2-2.72.1.tgz", + "integrity": "sha512-1UUD2zzwRo7vJLu0J62L1cgB39wjYNs1+6qS0UBhdLhzkOWK+CnVII9CNh21Z4YBs0Oa2d5REeGUiZO4d7a6Zw==", + "dev": true, + "requires": { + "@gi-types/glib2": "^2.72.1" + } + }, + "@gi-types/graphene": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/graphene/-/graphene-1.0.1.tgz", + "integrity": "sha512-XWzEdAWdvsJH2ieraRhbXqyoFXXG2HP2P8amSpryBsXuWaiessnhsNKgB+pu93cgrW029nnYJMb6AUJodASMrw==", + "dev": true, + "requires": { + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/graphene1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/graphene1/-/graphene1-1.0.1.tgz", + "integrity": "sha512-O5cJG/eIi912eF1zcaXrSur25LgMJDU+VhFDnYuO4Latj32eypFJf5Sw3KOZvN2EoctIBxjutbXLOxYYKMBhHA==", + "dev": true, + "requires": { + "@gi-types/gobject2": "^2.68.0" + } + }, + "@gi-types/gsk4": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/gsk4/-/gsk4-4.0.1.tgz", + "integrity": "sha512-/OtGBQwwtyQw7vjkfC4/P2pQf9hrtB89sXhDyKQG5B2UJ/LTAQQJNK7vu4u2W0Y922WK/zqrzimBy38JUoPO5Q==", + "dev": true, + "requires": { + "@gi-types/cairo1": "^1.0.0", + "@gi-types/gdk4": "^4.0.1", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0", + "@gi-types/graphene1": "^1.0.1", + "@gi-types/pango1": "^1.0.0" + } + }, + "@gi-types/gtk": { + "version": "3.24.9", + "resolved": "https://registry.npmjs.org/@gi-types/gtk/-/gtk-3.24.9.tgz", + "integrity": "sha512-U2LH7hKlIvPAvjVe0eQX8s6MDjYaFKCRpAGmwR3lIoBPIdvYOyy76odGY1suFKrepBa97iW6JMbEcv/0f2D98w==", + "dev": true, + "requires": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/gdk": "^3.24.7", + "@gi-types/gdkpixbuf": "^2.0.5", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.9", + "@gi-types/pango": "^1.0.5", + "@gi-types/xlib": "^2.0.0" + } + }, + "@gi-types/gtk4": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@gi-types/gtk4/-/gtk4-4.6.1.tgz", + "integrity": "sha512-WtdHV2S/xParqwJX9BJ/OsGTzoEorEhVAP0W2eJpUk5rqfSlzIXHnNvFvs9T5sT43fRZmefVd3Id1EbxsHKeLg==", + "dev": true, + "requires": { + "@gi-types/cairo1": "^1.0.0", + "@gi-types/gdk4": "^4.0.1", + "@gi-types/gdkpixbuf2": "^2.0.0", + "@gi-types/gio2": "^2.68.0", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0", + "@gi-types/graphene1": "^1.0.1", + "@gi-types/gsk4": "^4.0.1", + "@gi-types/pango1": "^1.50.1" + } + }, + "@gi-types/gtk4-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gi-types/gtk4-types/-/gtk4-types-1.0.0.tgz", + "integrity": "sha512-Y7SNMhAX9bUBK/HfJbidMePIjsa/7B6WroLR2nZF3afIzqjYgEugmbYWC85516z0+c1/3Z4eXC8V3j1CleXmGQ==", + "dev": true, + "requires": { + "@gi-types/gdk4": "*", + "@gi-types/graphene1": "*", + "@gi-types/gsk4": "*", + "@gi-types/gtk4": "*" + } + }, + "@gi-types/gvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@gi-types/gvc/-/gvc-1.0.2.tgz", + "integrity": "sha512-u5QMX4nI6FJSvnc/Se0JObycRlMznTz6BlZ2CM84i9ePhL0K6vplM0UIeTfWDxJiwS+M9mofQnA10uWt8CZRhA==", + "dev": true, + "requires": { + "@gi-types/gio": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/harfbuzz": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@gi-types/harfbuzz/-/harfbuzz-0.0.0.tgz", + "integrity": "sha512-pqhpLBN5aYl2P6yOPo0HCZTLmcTDiynGkEgfEq1yPwY0qhJpuEJYmaVUzwb7RrQrt7absVZP7ClhhO8TYiporw==", + "dev": true, + "requires": { + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/harfbuzz2": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@gi-types/harfbuzz2/-/harfbuzz2-4.4.2.tgz", + "integrity": "sha512-jsntc6SthoOdw4VSetTOKFI0BM1ccDISP1JQhnL5NETrVuYGnNAwRZ2oDIujuG0M70bHDv/MWKiZnmJxXNPrKw==", + "dev": true, + "requires": { + "@gi-types/freetype22": "^2.0.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/ibus1": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@gi-types/ibus1/-/ibus1-1.5.1.tgz", + "integrity": "sha512-pwPk1H0zlf3u5DQ0g3ENfN4PY9U1mu5N6dRHYocwXsmENs0EEZvYrHa+SfUC2DEpsIvvMoAfm2lwKAVQ7KqNRg==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/json": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@gi-types/json/-/json-1.6.6.tgz", + "integrity": "sha512-YtnjubEVEmnGirOlcwNkwD9hftaOKuJv2t0HnNFXcHuBCZpD72Jt5iuUtyFFvF7cduVR/jAXh71fw5U3kMYB8g==", + "dev": true, + "requires": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.9" + } + }, + "@gi-types/json1": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@gi-types/json1/-/json1-1.6.1.tgz", + "integrity": "sha512-6DpNf3PTYYKbL96U76R/mWZuAXv32cI0ZDalyKAciKeh9pczHjlnNdjq8h6lDKJc5vKxviHW9TsOdtB3NaXuqQ==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/malcontent0": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/malcontent0/-/malcontent0-0.0.1.tgz", + "integrity": "sha512-zB/M/ew8P1ZEqeXChK9b5PAoqq5TrmkTZ3NkZ0R/DPX/xt7aZRNPwdxrFt9enzrlKImjthcctp/tpk48MgGoKg==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/meta": { + "version": "3.38.5", + "resolved": "https://registry.npmjs.org/@gi-types/meta/-/meta-3.38.5.tgz", + "integrity": "sha512-EzYGvZu58j/hDZv/zQX4ooL0Pq4TEP2Okhn0JlVmGpFIB65ZWOkCHQqj3LODdpp327+cWMNYkG5WHZXJAmtuZQ==", + "dev": true, + "requires": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/clutter": "^7.0.6", + "@gi-types/cogl": "^7.0.3", + "@gi-types/coglpango": "^7.0.2", + "@gi-types/gdesktopenums": "^3.0.3", + "@gi-types/gdk": "^3.24.7", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/gtk": "^3.24.8", + "@gi-types/json": "^1.6.5", + "@gi-types/pango": "^1.0.5" + } + }, + "@gi-types/modemmanager1": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@gi-types/modemmanager1/-/modemmanager1-1.18.1.tgz", + "integrity": "sha512-eQgs3B0S61qtrIIoa9APcmszDGsVEbxZJwlAOak/4ifF4HYrF42fsTDQslzGoAe0aDe9B/FJe2ycR8LgMgquhg==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/nm": { + "version": "1.28.9", + "resolved": "https://registry.npmjs.org/@gi-types/nm/-/nm-1.28.9.tgz", + "integrity": "sha512-sZd0HWWJ4aeXTrbnmTlN80jkjqdAohGhhAbtdfrlnIzrR76hROuXe+vxs1dn++FwV/67c1hQuxCERD3JyqEfnw==", + "dev": true, + "requires": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/nm1": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/@gi-types/nm1/-/nm1-1.38.1.tgz", + "integrity": "sha512-TZNDTSxPRFjv+kjI/RyqLBO9Rb1FVDuUTfUyB9znGuj4NnPvQ5lzLRlvSG5SIyRYbeu1hik7lGteK+mM/NslVQ==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/notify0": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@gi-types/notify0/-/notify0-0.7.2.tgz", + "integrity": "sha512-cEhvHAffDmZS/Jy5mB+ZrSRMr+2DOt0exfmsHFodbG1nS44kpMaTtpKMHf1uwLuoUTjzdUx/XoTSHuHZsYQ+sw==", + "dev": true, + "requires": { + "@gi-types/gdkpixbuf2": "^2.0.2", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/pango": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@gi-types/pango/-/pango-1.0.5.tgz", + "integrity": "sha512-FcDRuMaiYwczKcxE11ZErlOCcbBQX69d/u2hKFkhpZxQrZk0fBNTGLbwJ/7QwAoQw4RsrCzYSOZ4TX0phLPQQw==", + "dev": true, + "requires": { + "@gi-types/cairo": "^1.0.3", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/harfbuzz": "^0.0.0" + } + }, + "@gi-types/pango1": { + "version": "1.50.1", + "resolved": "https://registry.npmjs.org/@gi-types/pango1/-/pango1-1.50.1.tgz", + "integrity": "sha512-SdpoZ5Fcz7Zo8Tal5Ap4Z/4SMqpo/PspORIadxmNZ3kafFOwK7TPvdWiUIBgua+i3uWiLnreKCGD/6rLJLbw3g==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1", + "@gi-types/harfbuzz2": "^4.4.2" + } + }, + "@gi-types/polkit": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@gi-types/polkit/-/polkit-1.0.6.tgz", + "integrity": "sha512-l7FLcXXzN0ttWjRWIblHHhwZje+jzPdLbXKPNJtRmllBe2ISxVNU31Y+yxxdyXTO1JHr2R543isWEGu2yEmmAA==", + "dev": true, + "requires": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8" + } + }, + "@gi-types/polkit1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/polkit1/-/polkit1-1.0.1.tgz", + "integrity": "sha512-M+9OPCx0jeOGe28uN6fegTsYOMuNnjZbgw1sJp6wXRRswihaceqHvuKSePo7dhyC+rx8knS04Cde1rYFQlZiBg==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/polkitagent": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gi-types/polkitagent/-/polkitagent-1.0.3.tgz", + "integrity": "sha512-x/u0LmV6EC8TACtxlKMIqyAYEur0FRF5P//1wMrBjWdbD0pfBm+KxMvXNCIjs3pHJ1lAZRc9t7Lo4HE03ICPew==", + "dev": true, + "requires": { + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/polkit": "^1.0.6" + } + }, + "@gi-types/polkitagent1": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/polkitagent1/-/polkitagent1-1.0.1.tgz", + "integrity": "sha512-klFuDVAoFomKpQBx6JtigNhAiuMnkeKEUj8pewR+n3wraVFJ1K/pyT/O14UYR6Qgl8smOBFybyQv15tgc35HnA==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1", + "@gi-types/polkit1": "^1.0.1" + } + }, + "@gi-types/rsvg2": { + "version": "2.54.2", + "resolved": "https://registry.npmjs.org/@gi-types/rsvg2/-/rsvg2-2.54.2.tgz", + "integrity": "sha512-FVrNq2JdQZLaUmJeeIGfF79EHzbcd8TqUlaAH8y8f2Yy7Gc4FyBqC+BtnSJ2bN65tus2khPN3YYqVc+YDDIZLw==", + "dev": true, + "requires": { + "@gi-types/cairo1": "^1.0.1", + "@gi-types/gdkpixbuf2": "^2.0.2", + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/shell": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@gi-types/shell/-/shell-0.1.6.tgz", + "integrity": "sha512-CFM3e4D02ZqVtOwE9PtPgp+qrxN25vNYXUKFrWj5BZtlrOnie1uC03+riu7f4zSpLfOW8BWX4KpZxycoH9AUbw==", + "dev": true, + "requires": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/clutter": "^7.0.6", + "@gi-types/clutterx11": "^7.0.2", + "@gi-types/gcr": "^3.38.7", + "@gi-types/gdkpixbuf": "^2.0.5", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/gtk": "^3.24.8", + "@gi-types/gvc": "^1.0.2", + "@gi-types/json": "^1.6.5", + "@gi-types/meta": "^3.38.5", + "@gi-types/nm": "^1.28.9", + "@gi-types/polkitagent": "^1.0.3", + "@gi-types/st": "^1.0.6" + } + }, + "@gi-types/soup2": { + "version": "2.74.1", + "resolved": "https://registry.npmjs.org/@gi-types/soup2/-/soup2-2.74.1.tgz", + "integrity": "sha512-K11KSN032DFAc3RTTBtPiZ9utWqw4CJ7wUAG1cMRdQtRBD44Xi2o2r9RuZkqUhT12V0vU7ZafF8Apb6PM6AOpg==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/st": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@gi-types/st/-/st-1.0.6.tgz", + "integrity": "sha512-xXFs7t19Jmy/GVC9CJFk8oByI546BZdGG/JXx+wN54YG8d+1nX3RiZ2nVNgp2y1gYhJDTcHBRZFnPs+7QUduoQ==", + "dev": true, + "requires": { + "@gi-types/atk": "^2.36.7", + "@gi-types/cairo": "^1.0.3", + "@gi-types/cally": "^7.0.3", + "@gi-types/clutter": "^7.0.6", + "@gi-types/cogl": "^7.0.3", + "@gi-types/gio": "^2.66.9", + "@gi-types/glib": "^2.66.9", + "@gi-types/gobject": "^2.66.8", + "@gi-types/gtk": "^3.24.8", + "@gi-types/json": "^1.6.5", + "@gi-types/meta": "^3.38.5", + "@gi-types/pango": "^1.0.5" + } + }, + "@gi-types/telepathyglib0": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@gi-types/telepathyglib0/-/telepathyglib0-0.12.1.tgz", + "integrity": "sha512-c/etfKk0Fdts4GCu0M9cVn13Xev5jqgfwWiFiwi61Y9wUNFw9AlXEhth2FlcXhPuhI58EvhWgvuiizooAVnT0w==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/telepathylogger0": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@gi-types/telepathylogger0/-/telepathylogger0-0.2.1.tgz", + "integrity": "sha512-FSgUMaXT4W9AL8qrmiHxGw6/D/78yURmKyPhIQP8abn64aiTizktDrViMO/8xaV86NMvM31dotnOU0gWx2IgUg==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/glib2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1", + "@gi-types/telepathyglib0": "^0.12.1" + } + }, + "@gi-types/upowerglib1": { + "version": "0.99.1", + "resolved": "https://registry.npmjs.org/@gi-types/upowerglib1/-/upowerglib1-0.99.1.tgz", + "integrity": "sha512-51DF1nxsMbr3n1VAidfuDuyc7CrotflWfXfTj100+CooOvYsLcH6FbjkbkLGTP9yFGPQO3ahbsr12AeYaJkEQA==", + "dev": true, + "requires": { + "@gi-types/gio2": "^2.72.1", + "@gi-types/gobject2": "^2.72.1" + } + }, + "@gi-types/xlib": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@gi-types/xlib/-/xlib-2.0.0.tgz", + "integrity": "sha512-YHNrcCw/i8QX3sh5bZ+SN7xEDrmhKIqAoUh/JgiDyPqqH2du+BpQUZy/e0oM0AIZWPNFKgD5M5Bq0+1MvVS3IA==", + "dev": true, + "requires": { + "@gi-types/gobject": "^2.66.9" + } + }, + "@gi-types/xlib2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gi-types/xlib2/-/xlib2-2.0.1.tgz", + "integrity": "sha512-AouZupNa8roCOwKbyGuVuOmt7n8YYBprH60q5Ggu9vGVY+AhpTjEbeJBiLkHW74T8587Z3AilRo7cBWnXPM56A==", + "dev": true, + "requires": { + "@gi-types/gobject2": "^2.72.1" + } + }, + "@humanwhocodes/config-array": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", + "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@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 + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@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, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@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 + }, + "@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, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz", + "integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/type-utils": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz", + "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz", + "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz", + "integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz", + "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz", + "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.0.tgz", + "integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz", + "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true + }, + "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, + "requires": {} + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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 + }, + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "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, + "requires": { + "color-name": "~1.1.4" + } + }, + "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 + }, + "comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "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, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + }, + "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, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "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 + }, + "eslint": { + "version": "8.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", + "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "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.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "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.15.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "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.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "eslint-plugin-jsdoc": { + "version": "39.6.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.4.tgz", + "integrity": "sha512-fskvdLCfwmPjHb6e+xNGDtGgbF8X7cDwMtVLAP2WwSf9Htrx68OAx31BESBM1FAwsN2HTQyYQq7m4aW4Q4Nlag==", + "dev": true, + "requires": { + "@es-joy/jsdoccomment": "~0.36.1", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "semver": "^7.3.8", + "spdx-expression-parse": "^3.0.1" + } + }, + "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, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "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, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "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 + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@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" + }, + "dependencies": { + "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, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "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 + }, + "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 + }, + "fastq": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", + "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "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, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "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, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "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, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "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 + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "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" + } + }, + "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, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", + "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "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" + } + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "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 + }, + "ignore": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", + "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "dev": true + }, + "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, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "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, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "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, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "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 + }, + "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 + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-sdsl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "dev": true + }, + "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, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsdoc-type-pratt-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", + "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "dev": true + }, + "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 + }, + "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 + }, + "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, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "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, + "requires": { + "p-locate": "^5.0.0" + } + }, + "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 + }, + "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, + "requires": { + "yallist": "^4.0.0" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "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 + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "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 + }, + "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, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "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, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "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, + "requires": { + "p-limit": "^3.0.2" + } + }, + "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, + "requires": { + "callsites": "^3.0.0" + } + }, + "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 + }, + "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 + }, + "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 + }, + "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 + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "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 + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "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 + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "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 + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "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, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "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, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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 + }, + "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, + "requires": { + "has-flag": "^4.0.0" + } + }, + "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 + }, + "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, + "requires": { + "is-number": "^7.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "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, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "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 + }, + "typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "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 + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "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 + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..bed63905 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "devDependencies": { + "@gi-types/base-types": "^1.0.0", + "@gi-types/gjs-environment": "^1.1.0", + "@gi-types/gtk4-types": "^1.0.0", + "@gi-types/shell": "^0.1.6", + "@typescript-eslint/eslint-plugin": "^5.46.0", + "@typescript-eslint/parser": "^5.46.0", + "eslint": "^8.29.0", + "eslint-plugin-jsdoc": "^39.6.4", + "typescript": "^4.9.4" + } +} diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 3863c352..b399c9b5 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -1,126 +1,99 @@ -const Gio = imports.gi.Gio; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const LoggerModule = Self.imports.logger; -const SettingsModule = Self.imports.settings; - -/* - libSoup is accessed through the SoupBowl wrapper to support libSoup3 and libSoup2.4 simultaneously in the extension - runtime and in the preferences window. - */ -const SoupBowl = Self.imports.soupBowl; - -var BaseAdapter = class { - constructor(params = {}) { - this.logger = new LoggerModule.Logger('RWG3', 'BaseAdapter'); - this._settings = new SettingsModule.Settings(params.schemaID, params.schemaPath); - this._wallpaperLocation = params.wallpaperLocation; - - this._sourceName = params.name; - if (this._sourceName === null || this._sourceName === "") { - this._sourceName = params.defaultName; - } - - let path = `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${params.id}/`; - this._generalSettings = new SettingsModule.Settings(SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); - } - - /** - * Retrieves a new url for an image and calls the given callback with an HistoryEntry as parameter. - * The history element will be null and the error will be set if an error occurred. - * - * @param callback(historyElement, error) - */ - requestRandomImage(callback) { - this._error("requestRandomImage not implemented", callback); - } - - fileName(uri) { - while (this._isURIEncoded(uri)) { - uri = decodeURIComponent(uri); - } - - let base = uri.substring(uri.lastIndexOf('/') + 1); - if (base.indexOf('?') >= 0) { - base = base.substr(0, base.indexOf('?')); - } - return base; - } - - /** - * copy file from uri to local wallpaper directory and calls the given callback with the name and the full filepath - * of the written file as parameter. - * @param uri - * @param callback(name, path, error) - */ - fetchFile(uri, callback) { - //extract the name from the url and - let name = this.fileName(uri); - - let bowl = new SoupBowl.Bowl(); - - let file = Gio.file_new_for_path(this._wallpaperLocation + String(name)); - let fstream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); - - // start the download - let request = bowl.Soup.Message.new('GET', uri); - - bowl.send_and_receive(request, (response_data_bytes) => { - if (!response_data_bytes) { - fstream.close(null); - - if (callback) { - callback(null, null, 'Not a valid response'); - } - - return; - } - - try { - fstream.write(response_data_bytes, null); - - fstream.close(null); - - // call callback with the name and the full filepath of the written file as parameter - if (callback) { - callback(name, file.get_path()); - } - } catch (e) { - if (callback) { - callback(null, null, e); - } - } - }); - } - - _isImageBlocked(filename) { - let blockedFilenames = this._generalSettings.get('blocked-images', 'strv'); - - if (blockedFilenames.includes(filename)) { - this.logger.warn(`Image is blocked: ${filename}`); - return true; - } - - return false; - } - - _isURIEncoded(uri) { - uri = uri || ''; - - try { - return uri !== decodeURIComponent(uri); - } catch (err) { - this.logger.error(err); - return false; - } - } - - _error(err, callback) { - let error = { "error": err }; - this.logger.error(JSON.stringify(error)); - - if (callback) { - callback(null, error); - } - } -}; +import * as Gio from 'gi://Gio'; + +import * as SettingsModule from './../settings.js'; + +import {HistoryEntry} from './../history.js'; +import {Logger} from './../logger.js'; +import {SoupBowl} from './../soupBowl.js'; + +class BaseAdapter { + logger: Logger; + + protected _settings: SettingsModule.Settings; + protected _wallpaperLocation: string; + protected _sourceName: string; + protected _generalSettings: SettingsModule.Settings; + + constructor(params: { + defaultName: string; + id: string; + name: string | null; + schemaID: string; + schemaPath: string; + wallpaperLocation: string; + }) { + const path = `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${params.id}/`; + this.logger = new Logger('RWG3', `${params.defaultName} adapter`); + + this._wallpaperLocation = params.wallpaperLocation; + this._settings = new SettingsModule.Settings(params.schemaID, params.schemaPath); + this._sourceName = params.name ?? params.defaultName; + + this._generalSettings = new SettingsModule.Settings( + SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, + path + ); + } + + /** + * Retrieves a new url for an image and crafts a new HistoryEntry. + * + */ + requestRandomImage(): Promise { + throw new Error('requestRandomImage not implemented'); + } + + /** + * copy file from uri to local wallpaper directory and returns the full filepath + * of the written file. + * + * @param {HistoryEntry} historyEntry The historyEntry to fetch + */ + async fetchFile(historyEntry: HistoryEntry) { + const bowl = new SoupBowl(); + + const file = Gio.file_new_for_path(`${this._wallpaperLocation}/${String(historyEntry.name)}`); + const fstream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); + + // start the download + let request = bowl.newGetMessage(historyEntry.source.imageDownloadUrl); + + const response_data_bytes = await bowl.send_and_receive(request); + if (!response_data_bytes) { + fstream.close(null); + throw new Error('Not a valid image response'); + } + + fstream.write(response_data_bytes, null); + fstream.close(null); + + return file; + } + + /** + * Check if this image is in the list of blocked images. + * + * @param {string} filename Name of the image + */ + protected _isImageBlocked(filename: string) { + const blockedFilenames = this._generalSettings.getStrv('blocked-images'); + + if (blockedFilenames.includes(filename)) { + this.logger.warn(`Image is blocked: ${filename}`); + return true; + } + + return false; + } + + // eslint-disable-next-line no-unused-vars + protected _error(err: string, callback?: (element: null, error: { error: string }) => void) { + const error = {error: err}; + this.logger.error(JSON.stringify(error)); + + if (callback) + callback(null, error); + } +} + +export {BaseAdapter}; diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index b7007e45..cb9f9a39 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -1,149 +1,136 @@ -const ByteArray = imports.byteArray; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const HistoryModule = Self.imports.history; -const JSONPath = Self.imports.jsonpath.jsonpath; -const SettingsModule = Self.imports.settings; -const SoupBowl = Self.imports.soupBowl; - -const BaseAdapter = Self.imports.adapter.baseAdapter; - -var GenericJsonAdapter = class extends BaseAdapter.BaseAdapter { - constructor(id, name, wallpaperLocation) { - super({ - id: id, - schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERIC_JSON, - schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/genericJSON/${id}/`, - wallpaperLocation: wallpaperLocation, - name: name, - defaultName: 'Generic JSON Source' - }); - - this.bowl = new SoupBowl.Bowl(); - } - - _getHistoryEntry() { - return new Promise((resolve, reject) => { - let url = this._settings.get("request-url", "string"); - url = encodeURI(url); - - let message = this.bowl.Soup.Message.new('GET', url); - if (message === null) { - reject('Could not create request.'); - } - - this.bowl.send_and_receive(message, (response_body_bytes) => { - try { - const response_body = JSON.parse(ByteArray.toString(response_body_bytes)); - - let imageJSONPath = this._settings.get("image-path", "string"); - let postJSONPath = this._settings.get("post-path", "string"); - let domainUrl = this._settings.get("domain", "string"); - let authorNameJSONPath = this._settings.get("author-name-path", "string"); - let authorUrlJSONPath = this._settings.get("author-url-path", "string"); - - let rObject; - let imageDownloadUrl; - for (let i = 0; i < 5; i++) { - rObject = JSONPath.JSONPathParser.access(response_body, imageJSONPath); - imageDownloadUrl = this._settings.get("image-prefix", "string") + rObject.Object; - - let imageBlocked = this._isImageBlocked(this.fileName(imageDownloadUrl)); - - if (!imageBlocked) { - break; - } - - // Only retry with @random present in JSONPath - if (imageBlocked && !imageJSONPath.includes("@random")) { - // Abort and try again - resolve(null); - } - - imageDownloadUrl = null; - } - - if (imageDownloadUrl === null) { - reject("Only blocked images found."); - } - - // '@random' would yield different results so lets make sure the values stay - // the same as long as the path is identical - let samePath = imageJSONPath.substring(0, this.findFirstDifference(imageJSONPath, postJSONPath)); - - // count occurrences of '@random' to slice the array later - // https://stackoverflow.com/a/4009768 - let occurrences = (samePath.match(/@random/g) || []).length; - let slicedRandomElements = rObject.RandomElements.slice(0, occurrences); - - let postUrl = JSONPath.JSONPathParser.access(response_body, postJSONPath, slicedRandomElements, false).Object; - postUrl = this._settings.get("post-prefix", "string") + postUrl; - if (typeof postUrl !== 'string' || !postUrl instanceof String) { - postUrl = null; - } - - let authorName = JSONPath.JSONPathParser.access(response_body, authorNameJSONPath, slicedRandomElements, false).Object; - if (typeof authorName !== 'string' || !authorName instanceof String) { - authorName = null; - } - - let authorUrl = JSONPath.JSONPathParser.access(response_body, authorUrlJSONPath, slicedRandomElements, false).Object; - authorUrl = this._settings.get("author-url-prefix", "string") + authorUrl; - if (typeof authorUrl !== 'string' || !authorUrl instanceof String) { - authorUrl = null; - } - - let historyEntry = new HistoryModule.HistoryEntry(authorName, this._sourceName, imageDownloadUrl); - - if (authorUrl !== null && authorUrl !== "") { - historyEntry.source.authorUrl = authorUrl; - } - - if (postUrl !== null && postUrl !== "") { - historyEntry.source.imageLinkUrl = postUrl; - } - - if (domainUrl !== null && domainUrl !== "") { - historyEntry.source.sourceUrl = domainUrl; - } - - resolve(historyEntry); - } catch (e) { - reject("Unexpected response. (" + e + ")"); - } - }); - }); - } - - async requestRandomImage(callback) { - for (let i = 0; i < 5; i++) { - try { - let historyEntry = await this._getHistoryEntry(); - - if (historyEntry === null) { - // Image blocked, try again - continue; - } - - if (callback) { - callback(historyEntry); - } - - return; - } catch (error) { - this._error(error, callback); - return; - } - } - - this._error("Only blocked images found.", callback); - } - - // https://stackoverflow.com/a/32859917 - findFirstDifference(jsonPath1, jsonPath2) { - let i = 0; - if (jsonPath1 === jsonPath2) return -1; - while (jsonPath1[i] === jsonPath2[i]) i++; - return i; - } -}; +import * as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray'; + +import * as JSONPath from './../jsonPath.js'; +import * as SettingsModule from './../settings.js'; +import * as Utils from './../utils.js'; + +import {BaseAdapter} from './../adapter/baseAdapter.js'; +import {HistoryEntry} from './../history.js'; +import {SoupBowl} from './../soupBowl.js'; + +class GenericJsonAdapter extends BaseAdapter { + private _bowl = new SoupBowl(); + + constructor(id: string, name: string, wallpaperLocation: string) { + super({ + defaultName: 'Generic JSON Source', + id, + name, + schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERIC_JSON, + schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/genericJSON/${id}/`, + wallpaperLocation, + }); + } + + private async _getHistoryEntry() { + let url = this._settings.getString('request-url'); + url = encodeURI(url); + + const message = this._bowl.newGetMessage(url); + if (message === null) + throw new Error('Could not create request.'); + + const response_body_bytes = await this._bowl.send_and_receive(message); + if (!response_body_bytes) + throw new Error('Error fetching response.'); + + const response_body = JSON.parse(ByteArray.toString(response_body_bytes)); + const imageJSONPath = this._settings.getString('image-path'); + const postJSONPath = this._settings.getString('post-path'); + const domainUrl = this._settings.getString('domain'); + const authorNameJSONPath = this._settings.getString('author-name-path'); + const authorUrlJSONPath = this._settings.getString('author-url-path'); + + let returnObject; + let imageDownloadUrl; + for (let i = 0; i < 5; i++) { + returnObject = JSONPath.getTarget(response_body, imageJSONPath); + + if (returnObject && (typeof returnObject.Object === 'string' || typeof returnObject.Object === 'number') && returnObject.Object !== '') { + imageDownloadUrl = this._settings.getString('image-prefix') + String(returnObject.Object); + + const imageBlocked = this._isImageBlocked(Utils.fileName(imageDownloadUrl)); + + if (!imageBlocked) + break; + + // Only retry with @random present in JSONPath + if (imageBlocked && !imageJSONPath.includes('@random')) { + // Abort and try again + return null; + } + } + + imageDownloadUrl = null; + } + + if (!imageDownloadUrl) + throw new Error('Only blocked images found.'); + + // '@random' would yield different results so lets make sure the values stay + // the same as long as the path is identical + const samePath = imageJSONPath.substring(0, Utils.findFirstDifference(imageJSONPath, postJSONPath)); + + // count occurrences of '@random' to slice the array later + // https://stackoverflow.com/a/4009768 + const occurrences = (samePath.match(/@random/g) || []).length; + const slicedRandomNumbers = returnObject?.RandomNumbers?.slice(0, occurrences); + + // A bit cumbersome to handle "unknown" in the following parts: + // https://github.com/microsoft/TypeScript/issues/27706 + + let postUrl: string; + const postUrlObject = JSONPath.getTarget(response_body, postJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + if (typeof postUrlObject === 'string' || typeof postUrlObject === 'number') + postUrl = this._settings.getString('post-prefix') + String(postUrlObject); + else + postUrl = ''; + + let authorName: string | null = null; + const authorNameObject = JSONPath.getTarget(response_body, authorNameJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + if (typeof authorNameObject === 'string' && authorNameObject !== '') + authorName = authorNameObject; + + let authorUrl: string; + const authorUrlObject = JSONPath.getTarget(response_body, authorUrlJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + if (typeof authorUrlObject === 'string' || typeof authorUrlObject === 'number') + authorUrl = this._settings.getString('author-url-prefix') + String(authorUrlObject); + else + authorUrl = ''; + + const historyEntry = new HistoryEntry(authorName, this._sourceName, imageDownloadUrl); + + if (authorUrl !== '') + historyEntry.source.authorUrl = authorUrl; + + if (postUrl !== '') + historyEntry.source.imageLinkUrl = postUrl; + + if (domainUrl !== '') + historyEntry.source.sourceUrl = domainUrl; + + return historyEntry; + } + + async requestRandomImage() { + for (let i = 0; i < 5; i++) { + try { + // This should run sequentially + // eslint-disable-next-line no-await-in-loop + const historyEntry = await this._getHistoryEntry(); + + if (historyEntry) + return historyEntry; + } catch (error) { + this.logger.warn(`Failed getting image: ${error}`); + // Do not escalate yet, try again + } + + // Image blocked, try again + } + + throw new Error('Only blocked images found.'); + } +} + +export {GenericJsonAdapter}; diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 953baf50..de85f66c 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -1,99 +1,105 @@ -const Gio = imports.gi.Gio; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const HistoryModule = Self.imports.history; -const SettingsModule = Self.imports.settings; -const Utils = Self.imports.utils; - -const BaseAdapter = Self.imports.adapter.baseAdapter; - -var LocalFolderAdapter = class extends BaseAdapter.BaseAdapter { - constructor(id, name, wallpaperLocation) { - super({ - id: id, - schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_LOCAL_FOLDER, - schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/localFolder/${id}/`, - wallpaperLocation: wallpaperLocation, - name: name, - defaultName: 'Local Folder' - }); - } - - requestRandomImage(callback) { - const folder = Gio.File.new_for_path(this._settings.get('folder', 'string')); - let files = this._listDirectory(folder); - - if (files === null || files.length < 1) { - this._error("Empty array.", callback); - } - - let randomFilePath; - for (let i = 0; i < 5; i++) { - let randomFile = files[Utils.Utils.getRandomNumber(files.length)]; - randomFilePath = randomFile.get_uri(); - - if (!this._isImageBlocked(randomFile.get_basename())) { - break; - } - - randomFilePath = null; - } - - if (randomFilePath === null) { - this._error("Only blocked images found.", callback); - return; - } - - if (callback) { - let historyEntry = new HistoryModule.HistoryEntry(null, this._sourceName, randomFilePath); - historyEntry.source.sourceUrl = this._wallpaperLocation; - callback(historyEntry); - } - } - - fetchFile(path, callback) { - let sourceFile = Gio.File.new_for_uri(path); - let name = sourceFile.get_basename() - let targetFile = Gio.File.new_for_path(this._wallpaperLocation + String(name)); - - // https://gjs.guide/guides/gio/file-operations.html#copying-and-moving-files - sourceFile.copy(targetFile, Gio.FileCopyFlags.NONE, null, null); - - if (callback) { - callback(name, targetFile.get_path()); - } - } - - // https://gjs.guide/guides/gio/file-operations.html#recursively-deleting-a-directory - _listDirectory(directory) { - const iterator = directory.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); - - let files = []; - while (true) { - const info = iterator.next_file(null); - - if (info === null) { - break; - } - - const child = iterator.get_child(info); - const type = info.get_file_type(); - - switch (type) { - case Gio.FileType.DIRECTORY: - files = files.concat(this._listDirectory(child)); - break; - - default: - break; - } - - let contentType = info.get_content_type(); - if (contentType === 'image/png' || contentType === 'image/jpeg') { - files.push(child); - } - } - - return files; - } -}; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; + +import * as SettingsModule from './../settings.js'; +import * as Utils from './../utils.js'; + +import {BaseAdapter} from './../adapter/baseAdapter.js'; +import {HistoryEntry} from './../history.js'; + +// https://gjs.guide/guides/gjs/asynchronous-programming.html#promisify-helper +Gio._promisify(Gio.File.prototype, 'copy_async', 'copy_finish'); + +class LocalFolderAdapter extends BaseAdapter { + constructor(id: string, name: string, wallpaperLocation: string) { + super({ + defaultName: 'Local Folder', + id, + name, + schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_LOCAL_FOLDER, + schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/localFolder/${id}/`, + wallpaperLocation, + }); + } + + requestRandomImage(): Promise { + return new Promise((resolve, reject) => { + const folder = Gio.File.new_for_path(this._settings.getString('folder')); + const files = this._listDirectory(folder); + + if (files.length < 1) { + reject(new Error('No files found')); + return; + } + + let randomFilePath: string | null = null; + for (let i = 0; i < 5; i++) { + const randomFile = files[Utils.getRandomNumber(files.length)]; + randomFilePath = randomFile.get_uri(); + const randomFileName = randomFile.get_basename(); + + if (randomFileName && !this._isImageBlocked(randomFileName)) + break; + + randomFilePath = null; + } + + if (!randomFilePath) { + reject(new Error('Only blocked images found')); + return; + } + + const historyEntry = new HistoryEntry(null, this._sourceName, randomFilePath); + historyEntry.source.sourceUrl = this._wallpaperLocation; + resolve(historyEntry); + }); + } + + async fetchFile(historyEntry: HistoryEntry) { + const sourceFile = Gio.File.new_for_uri(historyEntry.source.imageDownloadUrl); + const name = sourceFile.get_basename(); + const targetFile = Gio.File.new_for_path(this._wallpaperLocation + String(name)); + + // https://gjs.guide/guides/gio/file-operations.html#copying-and-moving-files + // This function was rewritten by Gio._promisify + // @ts-expect-error + if (!await sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null)) + throw new Error('Failed copying image.'); + + return targetFile; + } + + // https://gjs.guide/guides/gio/file-operations.html#recursively-deleting-a-directory + private _listDirectory(directory: Gio.File): Gio.File[] { + const iterator = directory.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); + + let files: Gio.File[] = []; + while (true) { + const info = iterator.next_file(null); + + if (info === null) + break; + + + const child = iterator.get_child(info); + const type = info.get_file_type(); + + switch (type) { + case Gio.FileType.DIRECTORY: + files = files.concat(this._listDirectory(child)); + break; + + default: + break; + } + + const contentType = info.get_content_type(); + if (contentType === 'image/png' || contentType === 'image/jpeg') + files.push(child); + } + + return files; + } +} + +export {LocalFolderAdapter}; diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index 61773ca0..c7b7f0a4 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -1,93 +1,113 @@ -const ByteArray = imports.byteArray; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const HistoryModule = Self.imports.history; -const SettingsModule = Self.imports.settings; -const SoupBowl = Self.imports.soupBowl; -const Utils = Self.imports.utils; - -const BaseAdapter = Self.imports.adapter.baseAdapter; - -var RedditAdapter = class extends BaseAdapter.BaseAdapter { - constructor(id, name, wallpaperLocation) { - super({ - id: id, - schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_REDDIT, - schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/reddit/${id}/`, - wallpaperLocation: wallpaperLocation, - name: name, - defaultName: 'Reddit' - }); - - this.bowl = new SoupBowl.Bowl(); - } - - _ampDecode(string) { - return string.replace(/\&/g, '&'); - } - - requestRandomImage(callback) { - const subreddits = this._settings.get('subreddits', 'string').split(',').map(s => s.trim()).join('+'); - const require_sfw = this._settings.get('allow-sfw', 'boolean'); - - const url = encodeURI('https://www.reddit.com/r/' + subreddits + '.json'); - let message = this.bowl.Soup.Message.new('GET', url); - if (message === null) { - this._error("Could not create request.", callback); - return; - } - - this.bowl.send_and_receive(message, (response_body_bytes) => { - try { - const response_body = JSON.parse(ByteArray.toString(response_body_bytes)); - - const submissions = response_body.data.children.filter(child => { - if (child.data.post_hint !== 'image') return false; - if (require_sfw) return child.data.over_18 === false; - - let minWidth = this._settings.get('min-width', 'int'); - let minHeight = this._settings.get('min-height', 'int'); - if (child.data.preview.images[0].source.width < minWidth) return false; - if (child.data.preview.images[0].source.height < minHeight) return false; - - let imageRatio1 = this._settings.get('image-ratio1', 'int'); - let imageRatio2 = this._settings.get('image-ratio2', 'int'); - if (child.data.preview.images[0].source.width / imageRatio1 * imageRatio2 < child.data.preview.images[0].source.height) return false; - return true; - }); - if (submissions.length === 0) { - this._error("No suitable submissions found!", callback); - return; - } - - let submission; - let imageDownloadUrl; - for (let i = 0; i < 5; i++) { - const random = Utils.Utils.getRandomNumber(submissions.length); - submission = submissions[random].data; - imageDownloadUrl = this._ampDecode(submission.preview.images[0].source.url); - - if (!this._isImageBlocked(this.fileName(imageDownloadUrl))) { - break; - } - - imageDownloadUrl = null; - } - - if (imageDownloadUrl === null) { - this._error("Only blocked images found.", callback); - return; - } - - if (callback) { - let historyEntry = new HistoryModule.HistoryEntry(null, this._sourceName, imageDownloadUrl); - historyEntry.source.sourceUrl = 'https://www.reddit.com/' + submission.subreddit_name_prefixed; - historyEntry.source.imageLinkUrl = 'https://www.reddit.com/' + submission.permalink; - callback(historyEntry); - } - } catch (e) { - this._error("Could not create request. (" + e + ")", callback); - } - }); - } -}; +import * as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray'; + +import * as SettingsModule from './../settings.js'; +import * as Utils from './../utils.js'; + +import {BaseAdapter} from './../adapter/baseAdapter.js'; +import {HistoryEntry} from './../history.js'; +import {SoupBowl} from './../soupBowl.js'; + +interface RedditResponse { + data: { + children: RedditSubmission[], + } +} + +interface RedditSubmission { + data: { + post_hint: string, + over_18: boolean, + subreddit_name_prefixed: string, + permalink: string, + preview: { + images: { + source: { + width: number, + height: number, + url: string, + } + }[] + } + } +} + +class RedditAdapter extends BaseAdapter { + private _bowl = new SoupBowl(); + + constructor(id: string, name: string, wallpaperLocation: string) { + super({ + defaultName: 'Reddit', + id, + name, + schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_REDDIT, + schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/reddit/${id}/`, + wallpaperLocation, + }); + } + + private _ampDecode(string: string) { + return string.replace(/&/g, '&'); + } + + async requestRandomImage() { + const subreddits = this._settings.getString('subreddits').split(',').map(s => s.trim()).join('+'); + const require_sfw = this._settings.getBoolean('allow-sfw'); + + const url = encodeURI(`https://www.reddit.com/r/${subreddits}.json`); + const message = this._bowl.newGetMessage(url); + + const response_body_bytes = await this._bowl.send_and_receive(message); + + try { + const response_body: RedditResponse = JSON.parse(ByteArray.toString(response_body_bytes)); + + const submissions = response_body.data.children.filter(child => { + if (child.data.post_hint !== 'image') + return false; + if (require_sfw) + return child.data.over_18 === false; + + const minWidth = this._settings.getInt('min-width'); + const minHeight = this._settings.getInt('min-height'); + if (child.data.preview.images[0].source.width < minWidth) + return false; + if (child.data.preview.images[0].source.height < minHeight) + return false; + + const imageRatio1 = this._settings.getInt('image-ratio1'); + const imageRatio2 = this._settings.getInt('image-ratio2'); + if (child.data.preview.images[0].source.width / imageRatio1 * imageRatio2 < child.data.preview.images[0].source.height) + return false; + return true; + }); + + if (submissions.length === 0) + throw new Error('No suitable submissions found!'); + + let submission = null; + let imageDownloadUrl = null; + for (let i = 0; i < 5; i++) { + const random = Utils.getRandomNumber(submissions.length); + submission = submissions[random].data; + imageDownloadUrl = this._ampDecode(submission.preview.images[0].source.url); + + if (!this._isImageBlocked(Utils.fileName(imageDownloadUrl))) + break; + + imageDownloadUrl = null; + } + + if (!imageDownloadUrl || !submission) + throw new Error('Only blocked images found.'); + + const historyEntry = new HistoryEntry(null, this._sourceName, imageDownloadUrl); + historyEntry.source.sourceUrl = `https://www.reddit.com/${submission.subreddit_name_prefixed}`; + historyEntry.source.imageLinkUrl = `https://www.reddit.com/${submission.permalink}`; + return historyEntry; + } catch (e) { + throw new Error(`Could not create request. (${e})`); + } + } +} + +export {RedditAdapter}; diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 71f5ce98..279c1a99 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -1,155 +1,140 @@ -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const HistoryModule = Self.imports.history; -const SettingsModule = Self.imports.settings; -const SoupBowl = Self.imports.soupBowl; -const Utils = Self.imports.utils; - -const BaseAdapter = Self.imports.adapter.baseAdapter; - -var UnsplashAdapter = class extends BaseAdapter.BaseAdapter { - constructor(id, name, wallpaperLocation) { - // Make sure we're not picking up a valid config - if (id === null) { - id = -1; - } - - super({ - id: id, - schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH, - schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id}/`, - wallpaperLocation: wallpaperLocation, - name: name, - defaultName: 'Unsplash' - }); - - this._sourceUrl = 'https://source.unsplash.com'; - - // query options - this.options = { - 'query': '', - 'w': 1920, - 'h': 1080, - 'featured': false, - 'constraintType': 0, - 'constraintValue': '', - }; - - this.bowl = new SoupBowl.Bowl(); - } - - _getHistoryEntry() { - return new Promise((resolve, reject) => { - this._readOptionsFromSettings(); - let optionsString = this._generateOptionsString(); - - let url = `https://source.unsplash.com${optionsString}`; - url = encodeURI(url); - - this.logger.info(`Unsplash request to: ${url}`); - - let message = this.bowl.Soup.Message.new('GET', url); - if (message === null) { - reject("Could not create request."); - } - - // unsplash redirects to actual file; we only want the file location - message.set_flags(this.bowl.Soup.MessageFlags.NO_REDIRECT); - - this.bowl.send_and_receive(message, (_null_expected) => { - let imageLinkUrl; - - // expecting redirect - if (message.status_code !== 302) { - reject("Unexpected response status code (expected 302)"); - } - - imageLinkUrl = message.response_headers.get_one('Location'); - - if (this._isImageBlocked(this.fileName(imageLinkUrl))) { - // Abort and try again - resolve(null); - } - - let historyEntry = new HistoryModule.HistoryEntry(null, this._sourceName, imageLinkUrl); - historyEntry.source.sourceUrl = this._sourceUrl; - historyEntry.source.imageLinkUrl = imageLinkUrl; - - resolve(historyEntry); - }); - }); - } - - async requestRandomImage(callback) { - for (let i = 0; i < 5; i++) { - try { - let historyEntry = await this._getHistoryEntry(); - - if (historyEntry === null) { - // Image blocked, try again - continue; - } - - if (callback) { - callback(historyEntry); - } - - return; - } catch (error) { - this._error(error, callback); - return; - } - } - - this._error("Only blocked images found.", callback); - } - - _generateOptionsString() { - let options = this.options; - let optionsString = ""; - - switch (options.constraintType) { - case 1: - optionsString = `/user/${options.constraintValue}/`; - break; - case 2: - optionsString = `/user/${options.constraintValue}/likes/`; - break; - case 3: - optionsString = `/collection/${options.constraintValue}/`; - break; - default: - if (options.featured) { - optionsString = `/featured/`; - } else { - optionsString = `/random/`; - } - } - - if (options.w && options.h) { - optionsString += `${options.w}x${options.h}`; - } - - if (options.query) { - let q = options.query.replace(/\W/, ','); - optionsString += `?${q}`; - } - - return optionsString; - } - - _readOptionsFromSettings() { - this.options.w = this._settings.get('image-width', 'int'); - this.options.h = this._settings.get('image-height', 'int'); - - this.options.constraintType = this._settings.get('constraint-type', 'enum'); - this.options.constraintValue = this._settings.get('constraint-value', 'string'); - - const keywords = this._settings.get('keyword', 'string').split(","); - if (keywords.length > 0) { - const randomKeyword = keywords[Utils.Utils.getRandomNumber(keywords.length)]; - this.options.query = randomKeyword.trim(); - } - - this.options.featured = this._settings.get('featured-only', 'boolean'); - } -}; +import * as SettingsModule from './../settings.js'; +import * as Utils from './../utils.js'; + +import {BaseAdapter} from './../adapter/baseAdapter.js'; +import {HistoryEntry} from './../history.js'; +import {SoupBowl} from './../soupBowl.js'; + +class UnsplashAdapter extends BaseAdapter { + private _bowl = new SoupBowl(); + private _sourceUrl = 'https://source.unsplash.com'; + + // default query options + private _options = { + 'query': '', + 'w': 1920, + 'h': 1080, + 'featured': false, + 'constraintType': 0, + 'constraintValue': '', + }; + + constructor(id: string | null, name: string | null, wallpaperLocation: string) { + super({ + defaultName: 'Unsplash', + id: id ?? '-1', + name, + schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH, + schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id}/`, + wallpaperLocation, + }); + } + + private async _getHistoryEntry(): Promise { + this._readOptionsFromSettings(); + const optionsString = this._generateOptionsString(); + + let url = `https://source.unsplash.com${optionsString}`; + url = encodeURI(url); + + this.logger.info(`Unsplash request to: ${url}`); + + const message = this._bowl.newGetMessage(url); + + // unsplash redirects to actual file; we only want the file location + message.set_flags(this._bowl.MessageFlags.NO_REDIRECT); + + await this._bowl.send_and_receive(message); + + // expecting redirect + if (message.status_code !== 302) + throw new Error('Unexpected response status code (expected 302)'); + + const imageLinkUrl = message.response_headers.get_one('Location'); + if (!imageLinkUrl) + throw new Error('No image link in response.'); + + + if (this._isImageBlocked(Utils.fileName(imageLinkUrl))) { + // Abort and try again + return null; + } + + const historyEntry = new HistoryEntry(null, this._sourceName, imageLinkUrl); + historyEntry.source.sourceUrl = this._sourceUrl; + historyEntry.source.imageLinkUrl = imageLinkUrl; + + return historyEntry; + } + + async requestRandomImage() { + for (let i = 0; i < 5; i++) { + try { + // This should run sequentially + // eslint-disable-next-line no-await-in-loop + const historyEntry = await this._getHistoryEntry(); + + if (historyEntry) + return historyEntry; + } catch (error) { + this.logger.warn(`Failed getting image: ${error}`); + // Do not escalate yet, try again + } + + // Image blocked, try again + } + + throw new Error('Only blocked images found.'); + } + + private _generateOptionsString() { + const options = this._options; + let optionsString = ''; + + switch (options.constraintType) { + case 1: + optionsString = `/user/${options.constraintValue}/`; + break; + case 2: + optionsString = `/user/${options.constraintValue}/likes/`; + break; + case 3: + optionsString = `/collection/${options.constraintValue}/`; + break; + default: + if (options.featured) + optionsString = '/featured/'; + else + optionsString = '/random/'; + } + + if (options.w && options.h) + optionsString += `${options.w}x${options.h}`; + + + if (options.query) { + const q = options.query.replace(/\W/, ','); + optionsString += `?${q}`; + } + + return optionsString; + } + + private _readOptionsFromSettings() { + this._options.w = this._settings.getInt('image-width'); + this._options.h = this._settings.getInt('image-height'); + + this._options.constraintType = this._settings.getEnum('constraint-type'); + this._options.constraintValue = this._settings.getString('constraint-value'); + + const keywords = this._settings.getString('keyword').split(','); + if (keywords.length > 0) { + const randomKeyword = keywords[Utils.getRandomNumber(keywords.length)]; + this._options.query = randomKeyword.trim(); + } + + this._options.featured = this._settings.getBoolean('featured-only'); + } +} + +export {UnsplashAdapter}; diff --git a/src/adapter/urlSource.ts b/src/adapter/urlSource.ts index ba3df5f6..d0365b52 100644 --- a/src/adapter/urlSource.ts +++ b/src/adapter/urlSource.ts @@ -1,57 +1,47 @@ -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const HistoryModule = Self.imports.history; -const JSONPath = Self.imports.jsonpath.jsonpath; -const SettingsModule = Self.imports.settings; - -const BaseAdapter = Self.imports.adapter.baseAdapter; - -var UrlSourceAdapter = class extends BaseAdapter.BaseAdapter { - constructor(id, name, wallpaperLocation) { - super({ - id: id, - schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_URL_SOURCE, - schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/urlSource/${id}/`, - wallpaperLocation: wallpaperLocation, - name: name, - defaultName: 'Static URL' - }); - } - - requestRandomImage(callback) { - let imageDownloadUrl = this._settings.get("image-url", "string"); - let authorName = this._settings.get("author-name", "string"); - let authorUrl = this._settings.get("author-url", "string"); - let domainUrl = this._settings.get("domain", "string"); - let postUrl = this._settings.get("domain", "string"); - - if (typeof postUrl !== 'string' || !postUrl instanceof String) { - postUrl = null; - } - - if (typeof authorName !== 'string' || !authorName instanceof String) { - authorName = null; - } - - if (typeof authorUrl !== 'string' || !authorUrl instanceof String) { - authorUrl = null; - } - - if (callback) { - let historyEntry = new HistoryModule.HistoryEntry(authorName, this._sourceName, imageDownloadUrl); - - if (authorUrl !== null && authorUrl !== "") { - historyEntry.source.authorUrl = authorUrl; - } - - if (postUrl !== null && postUrl !== "") { - historyEntry.source.imageLinkUrl = postUrl; - } - - if (domainUrl !== null && domainUrl !== "") { - historyEntry.source.sourceUrl = domainUrl; - } - - callback(historyEntry); - } - } -}; +import * as SettingsModule from './../settings.js'; + +import {BaseAdapter} from './../adapter/baseAdapter.js'; +import {HistoryEntry} from './../history.js'; + +class UrlSourceAdapter extends BaseAdapter { + constructor(id: string, name: string, wallpaperLocation: string) { + super({ + defaultName: 'Static URL', + id, + name, + schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_URL_SOURCE, + schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/urlSource/${id}/`, + wallpaperLocation, + }); + } + + requestRandomImage() { + const imageDownloadUrl = this._settings.getString('image-url'); + let authorName: string | null = this._settings.getString('author-name'); + const authorUrl = this._settings.getString('author-url'); + const domainUrl = this._settings.getString('domain'); + const postUrl = this._settings.getString('domain'); + + if (imageDownloadUrl === '') + throw new Error('Missing download url'); + + if (authorName === '') + authorName = null; + + const historyEntry = new HistoryEntry(authorName, this._sourceName, imageDownloadUrl); + + if (authorUrl !== '') + historyEntry.source.authorUrl = authorUrl; + + if (postUrl !== '') + historyEntry.source.imageLinkUrl = postUrl; + + + if (domainUrl !== '') + historyEntry.source.sourceUrl = domainUrl; + + return Promise.resolve(historyEntry); + } +} + +export {UrlSourceAdapter}; diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index a7b884bd..a7eea5c1 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -1,138 +1,144 @@ -const ByteArray = imports.byteArray; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const HistoryModule = Self.imports.history; -const SettingsModule = Self.imports.settings; -const SoupBowl = Self.imports.soupBowl; -const Utils = Self.imports.utils; - -const BaseAdapter = Self.imports.adapter.baseAdapter; - -var WallhavenAdapter = class extends BaseAdapter.BaseAdapter { - constructor(id, name, wallpaperLocation) { - super({ - id: id, - schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN, - schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/wallhaven/${id}/`, - wallpaperLocation: wallpaperLocation, - name: name, - defaultName: 'Wallhaven' - }); - - this.options = { - 'q': '', - 'apikey': '', - 'purity': '110', // SFW, sketchy - 'sorting': 'random', - 'categories': '111', // General, Anime, People - 'resolutions': ['1920x1200', '2560x1440'] - }; - - this.bowl = new SoupBowl.Bowl(); - } - - requestRandomImage(callback) { - this._readOptionsFromSettings(); - let optionsString = this._generateOptionsString(); - - let url = 'https://wallhaven.cc/api/v1/search?' + encodeURI(optionsString); - let message = this.bowl.Soup.Message.new('GET', url); - if (message === null) { - this._error("Could not create request.", callback); - return; - } - - this.bowl.send_and_receive(message, (response_body_bytes) => { - const response_body = ByteArray.toString(response_body_bytes); - - let response = null; - try { - response = JSON.parse(response_body).data; - } finally { - if (!response || response.length === 0) { - this._error("Failed to request image.", callback); - return; - } - } - - let downloadURL; - let siteURL; - for (let i = 0; i < 5; i++) { - // get a random entry from the array - let entry = response[Utils.Utils.getRandomNumber(response.length)]; - downloadURL = entry.path; - siteURL = entry.url; - - if (!this._isImageBlocked(this.fileName(downloadURL))) { - break; - } - - downloadURL = null; - } - - if (downloadURL === null) { - this._error("Only blocked images found.", callback); - return; - } - - let apiKey = this.options["apikey"]; - if (apiKey) { - downloadURL += "?apikey=" + apiKey; - } - - if (callback) { - let historyEntry = new HistoryModule.HistoryEntry(null, this._sourceName, downloadURL); - historyEntry.source.sourceUrl = 'https://wallhaven.cc/'; - historyEntry.source.imageLinkUrl = siteURL; - callback(historyEntry); - } - }); - } - - _generateOptionsString() { - let options = this.options; - let optionsString = ""; - - for (let key in options) { - if (options.hasOwnProperty(key)) { - if (Array.isArray(options[key])) { - optionsString += key + "=" + options[key].join() + "&"; - } else { - if (options[key]) { - optionsString += key + "=" + options[key] + "&"; - } - } - } - } - - return optionsString; - } - - _readOptionsFromSettings() { - const keywords = this._settings.get('keyword', 'string').split(","); - if (keywords.length > 0) { - const randomKeyword = keywords[Utils.Utils.getRandomNumber(keywords.length)]; - this.options.q = randomKeyword.trim(); - } - this.options.apikey = this._settings.get('api-key', 'string'); - - this.options.resolutions = this._settings.get('resolutions', 'string').split(','); - this.options.resolutions = this.options.resolutions.map((elem) => { - return elem.trim(); - }); - - let categories = []; - categories.push(+this._settings.get('category-general', 'boolean')); // + is implicit conversion to int - categories.push(+this._settings.get('category-anime', 'boolean')); - categories.push(+this._settings.get('category-people', 'boolean')); - this.options.categories = categories.join(''); - - let purity = []; - purity.push(+this._settings.get('allow-sfw', 'boolean')); - purity.push(+this._settings.get('allow-sketchy', 'boolean')); - purity.push(+this._settings.get('allow-nsfw', 'boolean')); - this.options.purity = purity.join(''); - - this.options.colors = this._settings.get('color', 'string'); - } -}; +import * as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray'; + +import * as SettingsModule from './../settings.js'; +import * as Utils from './../utils.js'; + +import {BaseAdapter} from './../adapter/baseAdapter.js'; +import {HistoryEntry} from './../history.js'; +import {SoupBowl} from './../soupBowl.js'; + +interface QueryOptions { + q: string, + apikey: string, + purity: string, + sorting: string, + categories: string, + resolutions: string[], + colors: string, + // atleast: string, + // ratios: string[], + // order: string, + // topRange: string, +} + +interface WallhavenSearchResponse { + data: { + path: string, + url: string, + }[] +} + +class WallhavenAdapter extends BaseAdapter { + private _bowl = new SoupBowl(); + private _options: QueryOptions = { + q: '', + apikey: '', + purity: '110', // SFW, sketchy + sorting: 'random', + categories: '111', // General, Anime, People + resolutions: ['1920x1200', '2560x1440'], + colors: '', + }; + + constructor(id: string, name: string, wallpaperLocation: string) { + super({ + id, + schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN, + schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/wallhaven/${id}/`, + wallpaperLocation, + name, + defaultName: 'Wallhaven', + }); + } + + async requestRandomImage() { + this._readOptionsFromSettings(); + const optionsString = this._generateOptionsString(this._options); + + const url = `https://wallhaven.cc/api/v1/search?${encodeURI(optionsString)}`; + const message = this._bowl.newGetMessage(url); + + const response_body_bytes = await this._bowl.send_and_receive(message); + + let response: WallhavenSearchResponse['data']; + try { + response = JSON.parse(ByteArray.toString(response_body_bytes)).data; + } catch { + throw new Error('Error parsing API.'); + } + if (!response || response.length === 0) + throw new Error('Empty response'); + + let downloadURL: string | null = null; + let siteURL: string = ''; + for (let i = 0; i < 5; i++) { + // get a random entry from the array + const entry = response[Utils.getRandomNumber(response.length)]; + downloadURL = entry.path; + siteURL = entry.url; + + if (!this._isImageBlocked(Utils.fileName(downloadURL))) + break; + + downloadURL = null; + } + + if (!downloadURL) + throw new Error('Only blocked images found.'); + + const apiKey = this._options['apikey']; + if (apiKey !== '') + downloadURL += `?apikey=${apiKey}`; + + const historyEntry = new HistoryEntry(null, this._sourceName, downloadURL); + historyEntry.source.sourceUrl = 'https://wallhaven.cc/'; + historyEntry.source.imageLinkUrl = siteURL; + return historyEntry; + } + + private _generateOptionsString(options: T) { + let optionsString = ''; + + for (const key in options) { + if (options.hasOwnProperty(key)) { + if (Array.isArray(options[key])) + // eslint-disable-next-line no-extra-parens + optionsString += `${key}=${(options[key] as Array).join()}&`; + else if (options[key] !== '') + optionsString += `${key}=${options[key]}&`; + } + } + + return optionsString; + } + + private _readOptionsFromSettings() { + const keywords = this._settings.getString('keyword').split(','); + if (keywords.length > 0) { + const randomKeyword = keywords[Utils.getRandomNumber(keywords.length)]; + this._options.q = randomKeyword.trim(); + } + this._options.apikey = this._settings.getString('api-key'); + + this._options.resolutions = this._settings.getString('resolutions').split(','); + this._options.resolutions = this._options.resolutions.map(elem => { + return elem.trim(); + }); + + let categories = []; + categories.push(Number(this._settings.getBoolean('category-general'))); // + is implicit conversion to int + categories.push(Number(this._settings.getBoolean('category-anime'))); + categories.push(Number(this._settings.getBoolean('category-people'))); + this._options.categories = categories.join(''); + + let purity = []; + purity.push(Number(this._settings.getBoolean('allow-sfw'))); + purity.push(Number(this._settings.getBoolean('allow-sketchy'))); + purity.push(Number(this._settings.getBoolean('allow-nsfw'))); + this._options.purity = purity.join(''); + + this._options.colors = this._settings.getString('color'); + } +} + +export {WallhavenAdapter}; diff --git a/src/extension.ts b/src/extension.ts index 65736e4c..fcff5572 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,37 +1,90 @@ -//self -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const WallpaperController = Self.imports.wallpaperController; -const RandomWallpaperMenu = Self.imports.randomWallpaperMenu; -const LoggerModule = Self.imports.logger; +import type * as LoggerNamespace from './logger.js'; +import type * as AFTimer from './timer.js'; +import type * as WallpaperControllerNamespace from './wallpaperController.js'; +import type * as RandomWallpaperMenuNamespace from './randomWallpaperMenu.js'; -const Timer = Self.imports.timer; +let Logger: typeof LoggerNamespace | null = null; +let Timer: typeof AFTimer | null = null; +let WallpaperController: typeof WallpaperControllerNamespace | null = null; +let RandomWallpaperMenu: typeof RandomWallpaperMenuNamespace | null = null; -let wallpaperController; -let panelMenu; -let logger; +/** + * + */ +// eslint-disable-next-line no-unused-vars +function init() { + return new Extension(); +} -function init(metaData) { } +class Extension { + private _logger: LoggerNamespace.Logger | null = null; + private _wallpaperController: WallpaperControllerNamespace.WallpaperController | null = null; + private _panelMenu: RandomWallpaperMenuNamespace.RandomWallpaperMenu | null = null; + private _timer: AFTimer.AFTimer | null = null; -function enable() { - // enable Extension - logger = new LoggerModule.Logger("RWG3", "Main"); - wallpaperController = new WallpaperController.WallpaperController(); + enable() { + // Dynamically load own modules. This allows us to use proper ES6 Modules + this._importModules().then(() => { + if (!Logger || !Timer || !WallpaperController || !RandomWallpaperMenu) + throw new Error('Error importing module'); - logger.info("Enable extension."); - panelMenu = new RandomWallpaperMenu.RandomWallpaperMenu(wallpaperController); - panelMenu.init(); -} + this._logger = new Logger.Logger('RWG3', 'Main'); + this._timer = Timer.AFTimer.getTimer(); + this._wallpaperController = new WallpaperController.WallpaperController(); + this._panelMenu = new RandomWallpaperMenu.RandomWallpaperMenu(this._wallpaperController); + + this._logger.info('Enable extension.'); + this._panelMenu.init(); + }).catch(error => { + if (this._logger) { + this._logger.error(error); + logError(error); + } else { + logError(error); + } + }); + } + + disable() { + if (this._logger) + this._logger.info('Disable extension.'); + + if (this._panelMenu) + this._panelMenu.cleanup(); + + // cleanup the timer singleton + if (Timer) + Timer.AFTimer.destroy(); + + this._timer = null; + this._logger = null; + this._panelMenu = null; + this._wallpaperController = null; + + Timer = null; + Logger = null; + WallpaperController = null; + RandomWallpaperMenu = null; + } -function disable() { - // disable Extension - logger.info("Disable extension."); - panelMenu.cleanup(); + private async _importModules() { + // All imports as dynamic loads to work around the fact this module won't be in a topmost + // context inside the gnome shell and can't use import statements (yet). + // PopOS' tiling extension and RoundedCorners Extension work around the above limitation by + // manually rewriting the exported javascript file. We also have to do this but + // not for our own modules. + const loggerPromise = import('./logger.js'); + const timerPromise = import('./timer.js'); + const wallpaperPromise = import('./wallpaperController.js'); + const menuPromise = import('./randomWallpaperMenu.js'); - // destroy timer singleton - Timer.AFTimerDestroySingleton(); + const [moduleLogger, moduleTimer, moduleWallpaper, moduleMenu] = await Promise.all([ + loggerPromise, timerPromise, wallpaperPromise, menuPromise, + ]); - // clear references - wallpaperController = null; - panelMenu = null; - logger = null; + Logger = moduleLogger; + Timer = moduleTimer; + WallpaperController = moduleWallpaper; + RandomWallpaperMenu = moduleMenu; + } } diff --git a/src/history.ts b/src/history.ts index e345049b..2925d42a 100644 --- a/src/history.ts +++ b/src/history.ts @@ -1,177 +1,188 @@ -// Filesystem -const Gio = imports.gi.Gio; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const Prefs = Self.imports.settings; -const Utils = Self.imports.utils; - -const LoggerModule = Self.imports.logger; - -var HistoryEntry = class { - - constructor(author, source, url) { - this.id = null; - this.name = null; - this.path = null; - this.source = null; - this.timestamp = new Date().getTime(); - this.adapter = { - id: null, - type: null - }; - - this.source = { - author: author, - authorUrl: null, - source: source, - sourceUrl: null, - imageDownloadUrl: url, // URL used for downloading the image - imageLinkUrl: url // URL used for linking back to the website of the image - }; - } - -}; - -var HistoryController = class { - - constructor(wallpaperLocation) { - this.logger = new LoggerModule.Logger('RWG3', 'HistoryController'); - this.size = 10; - this.history = []; - this._settings = new Prefs.Settings(); - this._wallpaperLocation = wallpaperLocation; - - this.load(); - } - - insert(historyElement) { - this.history.unshift(historyElement); - this._deleteOldPictures(); - this.save(); - } - - /** - * Set the given id to to the first history element (the current one) - * @param id - * @returns {boolean} - */ - promoteToActive(id) { - let element = this.get(id); - if (element === null) { - return false; - } - - element.timestamp = new Date().getTime(); - this.history = this.history.sort((elem1, elem2) => { - return elem1.timestamp < elem2.timestamp - }); - this.save(); - - return true; - } - - /** - * Returns the corresponding HistoryEntry or null - * @param id - * @returns {*} - */ - get(id) { - for (let elem of this.history) { - if (elem.id == id) { - return elem; - } - } - - return null; - } - - /** - * Get the current history element. - * @returns {HistoryElement} - */ - getCurrentElement() { - return this.history[0]; - } - - /** - * Get a random HistoryEntry. - * @returns {HistoryEntry} - */ - getRandom() { - return this.history[Utils.Utils.getRandomNumber(this.history.length)]; - } - - /** - * Load the history from the gschema - */ - load() { - this.size = this._settings.get('history-length', 'int'); - let stringHistory = this._settings.get('history', 'strv'); - this.history = stringHistory.map(elem => { - return JSON.parse(elem) - }); - } - - /** - * Save the history to the gschema - */ - save() { - let stringHistory = this.history.map(elem => { - return JSON.stringify(elem) - }); - this._settings.set('history', 'strv', stringHistory); - Gio.Settings.sync(); - } - - /** - * Clear the history and delete all photos except the current one. - * @returns {boolean} - */ - clear() { - let firstHistoryElement = this.history[0]; - - if (firstHistoryElement) - this.history = [firstHistoryElement]; - - let directory = Gio.file_new_for_path(this._wallpaperLocation); - let enumerator = directory.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null); - - let fileinfo; - let deleteFile; - - do { - - fileinfo = enumerator.next_file(null); - - if (!fileinfo) { - break; - } - - let id = fileinfo.get_name(); - - // ignore hidden files and first element - if (id[0] != '.' && id != firstHistoryElement.id) { - deleteFile = Gio.file_new_for_path(this._wallpaperLocation + id); - deleteFile.delete(null); - } - - } while (fileinfo); - - this.save(); - return true; - } - - /** - * Delete all pictures that have no slot in the history. - * @private - */ - _deleteOldPictures() { - this.size = this._settings.get('history-length', 'int'); - let deleteFile; - while (this.history.length > this.size) { - deleteFile = Gio.file_new_for_path(this.history.pop().path); - deleteFile.delete(null); - } - } - -}; +import * as Gio from 'gi://Gio'; + +import * as Utils from './utils.js'; + +import {Settings} from './settings.js'; + +interface SourceInfo { + author: string | null; + authorUrl: string | null; + source: string | null; + sourceUrl: string | null; + imageDownloadUrl: string; + imageLinkUrl: string | null; +} + +interface AdapterInfo { + id: string | null; + type: number | null; +} + +class HistoryEntry { + timestamp = new Date().getTime(); + id: string; + name: string; + path: string | null = null; + source: SourceInfo; + adapter: AdapterInfo = { + id: null, + type: null, + }; + + constructor(author: string | null, source: string | null, url: string) { + this.source = { + author, + authorUrl: null, + source, + sourceUrl: null, + imageDownloadUrl: url, // URL used for downloading the image + imageLinkUrl: url, // URL used for linking back to the website of the image + }; + + // extract the name from the url + this.name = Utils.fileName(this.source.imageDownloadUrl); + this.id = `${this.timestamp}_${this.name}`; + } +} + +class HistoryController { + history: HistoryEntry[] = []; + size = 10; + + private _settings = new Settings(); + private _wallpaperLocation: string; + + constructor(wallpaperLocation: string) { + this._wallpaperLocation = wallpaperLocation; + + this.load(); + } + + insert(historyElement: HistoryEntry) { + this.history.unshift(historyElement); + this._deleteOldPictures(); + this.save(); + } + + /** + * Set the given id to to the first history element (the current one) + * + * @param {string} id ID of the historyEntry + */ + promoteToActive(id: string): boolean { + const element = this.get(id); + if (element === null) + return false; + + + element.timestamp = new Date().getTime(); + this.history = this.history.sort((elem1, elem2) => { + return elem1.timestamp < elem2.timestamp ? 1 : 0; + }); + this.save(); + + return true; + } + + /** + * Returns the corresponding HistoryEntry or null + * + * @param {string} id ID of the historyEntry + */ + get(id: string): HistoryEntry | null { + for (const elem of this.history) { + if (elem.id === id) + return elem; + } + + return null; + } + + /** + * Get the current history element. + */ + getCurrentElement(): HistoryEntry { + return this.history[0]; + } + + /** + * Get a random HistoryEntry. + */ + getRandom(): HistoryEntry { + return this.history[Utils.getRandomNumber(this.history.length)]; + } + + /** + * Load the history from the schema + */ + load() { + this.size = this._settings.getInt('history-length'); + + const stringHistory: string[] = this._settings.getStrv('history'); + this.history = stringHistory.map((elem: string) => { + return JSON.parse(elem); + }); + } + + /** + * Save the history to the schema + */ + save() { + const stringHistory = this.history.map(elem => { + return JSON.stringify(elem); + }); + this._settings.setStrv('history', stringHistory); + Gio.Settings.sync(); + } + + /** + * Clear the history and delete all photos except the current one. + */ + clear(): boolean { + const firstHistoryElement = this.history[0]; + + if (firstHistoryElement) + this.history = [firstHistoryElement]; + + const directory = Gio.file_new_for_path(this._wallpaperLocation); + const enumerator = directory.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null); + + let fileInfo; + let deleteFile; + + do { + fileInfo = enumerator.next_file(null); + + if (!fileInfo) + break; + + const id = fileInfo.get_name(); + + // ignore hidden files and first element + if (id[0] !== '.' && id !== firstHistoryElement.id) { + deleteFile = Gio.file_new_for_path(this._wallpaperLocation + id); + deleteFile.delete(null); + } + } while (fileInfo); + + this.save(); + return true; + } + + /** + * Delete all pictures that have no slot in the history. + */ + private _deleteOldPictures() { + this.size = this._settings.getInt('history-length'); + let deleteFile; + while (this.history.length > this.size) { + const path = this.history.pop()?.path; + if (path) { + deleteFile = Gio.file_new_for_path(path); + deleteFile.delete(null); + } + } + } +} + +export {HistoryEntry, HistoryController}; diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 2c10983b..d89d1264 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -1,261 +1,256 @@ -const PopupMenu = imports.ui.popupMenu; -const St = imports.gi.St; -const Util = imports.misc.util; -const GdkPixbuf = imports.gi.GdkPixbuf; -const Clutter = imports.gi.Clutter; -const Cogl = imports.gi.Cogl; -const GLib = imports.gi.GLib; -const Gio = imports.gi.Gio; -const Gtk = imports.gi.Gtk; -const GObject = imports.gi.GObject; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const Settings = Self.imports.settings; -const LoggerModule = Self.imports.logger; -const Timer = Self.imports.timer; - -var HistoryElement = GObject.registerClass({ - GTypeName: 'HistoryElement', -}, class HistoryElement extends PopupMenu.PopupSubMenuMenuItem { - _init(historyEntry, index) { - super._init("", false); - this.logger = new LoggerModule.Logger('RWG3', 'HistoryElement'); - this.historyEntry = null; - this.setAsWallpaperItem = null; - this.previewItem = null; - this._previewActor = null; - this._settings = new Settings.Settings(); - - let timestamp = historyEntry.timestamp; - let date = new Date(timestamp); - - let timeString = date.toLocaleTimeString(); - let dateString = date.toLocaleDateString(); - - let prefixText = String(index) + '.'; - this.prefixLabel = new St.Label({ - text: prefixText, - style_class: 'rwg-history-index' - }); - - if (index === 0) { - this.label.text = 'Current Background'; - } else { - this.actor.insert_child_above(this.prefixLabel, this.label); - this.label.destroy(); - } - - this._container = new St.BoxLayout({ - vertical: true - }); - - this.dateLabel = new St.Label({ - text: dateString, - style_class: 'rwg-history-date' - }); - this._container.add_child(this.dateLabel); - - this.timeLabel = new St.Label({ - text: timeString, - style_class: 'rwg-history-time' - }); - this._container.add_child(this.timeLabel); - - this.historyEntry = historyEntry; - this.actor.historyId = historyEntry.id; // extend the actor with the historyId - - if (index !== 0) { - this.actor.insert_child_above(this._container, this.prefixLabel); - } - - this.menu.actor.add_style_class_name("rwg-history-element-content"); - - if (this.historyEntry.source !== null) { - if (this.historyEntry.source.author !== null - && this.historyEntry.source.authorUrl !== null) { - this.authorItem = new PopupMenu.PopupMenuItem('Image By: ' + this.historyEntry.source.author); - this.authorItem.connect('activate', () => { - Util.spawn(['xdg-open', this.historyEntry.source.authorUrl]); - }); - - this.menu.addMenuItem(this.authorItem); - } - - if (this.historyEntry.source.source !== null - && this.historyEntry.source.sourceUrl !== null) { - this.sourceItem = new PopupMenu.PopupMenuItem('Image From: ' + this.historyEntry.source.source); - this.sourceItem.connect('activate', () => { - Util.spawn(['xdg-open', this.historyEntry.source.sourceUrl]); - }); - - this.menu.addMenuItem(this.sourceItem); - } - - this.imageUrlItem = new PopupMenu.PopupMenuItem('Open Image In Browser'); - this.imageUrlItem.connect('activate', () => { - Util.spawn(['xdg-open', this.historyEntry.source.imageLinkUrl]); - }); - - this.menu.addMenuItem(this.imageUrlItem); - } else { - this.menu.addMenuItem(new PopupMenu.PopupMenuItem('Unknown source.')); - } - - this.previewItem = new PopupMenu.PopupBaseMenuItem({ can_focus: false, reactive: false }); - this.menu.addMenuItem(this.previewItem); - - this.setAsWallpaperItem = new PopupMenu.PopupMenuItem('Set As Wallpaper'); - this.setAsWallpaperItem.connect('activate', () => { - this.emit('activate', null); // Fixme: not sure what the second parameter should be. null seems to work fine for now. - }); - - if (index !== 0) { - // this.menu.addMenuItem(new PopupMenu.PopupBaseMenuItem({ can_focus: false, reactive: false })); // theme independent spacing - this.menu.addMenuItem(this.setAsWallpaperItem); - } - - this.copyToFavorites = new PopupMenu.PopupMenuItem('Save For Later'); - this.copyToFavorites.connect('activate', () => { - this._saveImage(); - }); - this.menu.addMenuItem(this.copyToFavorites); - - // Static URLs can't block images (yet?) - if (historyEntry.adapter.type !== 5) { - this.blockImage = new PopupMenu.PopupMenuItem('Add To Blocklist'); - this.blockImage.connect('activate', () => { - this._addToBlocklist(historyEntry); - }); - this.menu.addMenuItem(this.blockImage); - } - - /* - Load the image on first opening of the sub menu instead of during creation of the history list. - */ - this.menu.connect('open-state-changed', (self, open) => { - if (open) { - if (this._previewActor !== null) { - return; - } - - try { - let width = 270; // 270 looks good for the now fixed 350px menu width - // let width = this.menu.actor.get_width(); // This should be correct but gives different results per element? - let pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(this.historyEntry.path, width, -1, true); - let height = pixbuf.get_height(); - - let image = new Clutter.Image(); - let pixelFormat = pixbuf.get_has_alpha() ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888; - image.set_data( - pixbuf.get_pixels(), - pixelFormat, - width, - height, - pixbuf.get_rowstride() - ); - this._previewActor = new Clutter.Actor({ height: height, width: width }); - this._previewActor.set_content(image); - - this.previewItem.actor.add_actor(this._previewActor); - } catch (exeption) { - this.logger.error(exeption); - } - } - }) - } - - _addToBlocklist(element) { - if (element.adapter.id === null || element.adapter.id === -1) { - return; - } - - let path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${element.adapter.id}/`; - let generalSettings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); - let blockedFilenames = generalSettings.get('blocked-images', 'strv'); - - if (blockedFilenames.includes(element.name)) { - return; - } - - blockedFilenames.push(element.name); - generalSettings.set('blocked-images', 'strv', blockedFilenames); - } - - async _saveImage() { - let sourceFile = Gio.File.new_for_path(this.historyEntry.path); - let targetFolder = Gio.File.new_for_path(this._settings.get('favorites-folder', 'string')); - let targetFile = targetFolder.get_child(this.historyEntry.name); - let targetInfoFile = targetFolder.get_child(`${this.historyEntry.name}.json`); - - try { - if (!targetFolder.make_directory_with_parents(null)) { - this.logger.warn('Could not create directories.'); - return; - } - } catch (error) { - if (error === Gio.IOErrorEnum.EXISTS) { } - } - - try { // This try is for promise rejections. GJS mocks about missing this despite all examples omitting this try-catch-block - let copyResult = await new Promise((resolve, reject) => { - sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null, (file, result) => { - try { - resolve(file.copy_finish(result)); - } catch (e) { - reject(e); - } - }); - }); - - if (copyResult === false) { - this.logger.warn('Failed copying image.'); - return; - } else if (copyResult === Gio.IOErrorEnum.EXISTS) { - this.logger.warn('Image already exists in location.'); - return; - } - - // https://gjs.guide/guides/gio/file-operations.html#writing-file-contents - const [, etag] = await new Promise((resolve, reject) => { - let bytes = new GLib.Bytes(JSON.stringify(this.historyEntry.source, null, '\t')); - targetInfoFile.replace_contents_bytes_async( - bytes, - null, - false, - Gio.FileCreateFlags.NONE, - null, - (file, result) => { - try { - resolve(file.replace_contents_finish(result)); - } catch (error) { - reject(error); - } - } - ); - }); - } catch (error) { - this.logger.warn(`Error saving image: ${error}`); - } - } - - setIndex(index) { - this.prefixLabel.set_text(String(index)); - } -} -); +import * as GdkPixbuf from 'gi://GdkPixbuf'; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; +import * as GObject from 'gi://GObject'; +import * as Gtk from 'gi://Gtk'; -var CurrentImageElement = GObject.registerClass({ - GTypeName: 'CurrentImageElement', -}, class CurrentImageElement extends HistoryElement { +import * as Clutter from '@gi-types/clutter'; +import * as Cogl from '@gi-types/cogl'; +import * as St from '@gi-types/st'; + +import * as PopupMenu from '@gi/ui/popupMenu'; + +import * as HistoryModule from './history.js'; +import * as Settings from './settings.js'; +import * as Utils from './utils.js'; - _init(historyElement) { - super._init(historyElement, 0); +import {AFTimer as Timer} from './timer.js'; +import {Logger} from './logger.js'; - if (this.setAsWallpaperItem !== null) { - this.setAsWallpaperItem.destroy(); - } - } +// https://gjs.guide/guides/gjs/asynchronous-programming.html#promisify-helper +Gio._promisify(Gio.File.prototype, 'copy_async', 'copy_finish'); +Gio._promisify(Gio.File.prototype, 'replace_contents_bytes_async', 'replace_contents_finish'); +const HistoryElement = GObject.registerClass({ + GTypeName: 'HistoryElement', +}, class HistoryElement extends PopupMenu.PopupSubMenuMenuItem { + private _logger = new Logger('RWG3', 'HistoryElement'); + private _settings = new Settings.Settings(); + + private _prefixLabel; + private _container; + private _dateLabel; + private _previewActor: Clutter.Actor | null = null; + + protected _setAsWallpaperItem; + + historyId: string; + historyEntry: HistoryModule.HistoryEntry; + + constructor(params: object | undefined, historyEntry: HistoryModule.HistoryEntry, index: number) { + super('', false); + + this.historyEntry = historyEntry; + this.historyId = this.historyEntry.id; // extend the actor with the historyId + + const timestamp = this.historyEntry.timestamp; + const date = new Date(timestamp); + + const timeString = date.toLocaleTimeString(); + const dateString = date.toLocaleDateString(); + + const prefixText = `${String(index)}.`; + this._prefixLabel = new St.Label({ + text: prefixText, + style_class: 'rwg-history-index', + }); + + if (index === 0) { + this.label.text = 'Current Background'; + } else { + this.actor.insert_child_above(this._prefixLabel, this.label); + this.label.destroy(); + } + + this._container = new St.BoxLayout({ + vertical: true, + }); + + this._dateLabel = new St.Label({ + text: dateString, + style_class: 'rwg-history-date', + }); + this._container.add_child(this._dateLabel); + + const timeLabel = new St.Label({ + text: timeString, + style_class: 'rwg-history-time', + }); + this._container.add_child(timeLabel); + + if (index !== 0) + this.actor.insert_child_above(this._container, this._prefixLabel); + + this.menu.actor.add_style_class_name('rwg-history-element-content'); + + if (this.historyEntry.source !== null) { + if (this.historyEntry.source.author !== null && + this.historyEntry.source.authorUrl !== null) { + const authorItem = new PopupMenu.PopupMenuItem(`Image By: ${this.historyEntry.source.author}`); + authorItem.connect('activate', () => { + if (this.historyEntry.source.authorUrl) + Utils.execCheck(['xdg-open', this.historyEntry.source.authorUrl]).catch(logError); + }); + + this.menu.addMenuItem(authorItem); + } + + if (this.historyEntry.source.source !== null && + this.historyEntry.source.sourceUrl !== null) { + const sourceItem = new PopupMenu.PopupMenuItem(`Image From: ${this.historyEntry.source.source}`); + sourceItem.connect('activate', () => { + if (this.historyEntry.source.sourceUrl) + Utils.execCheck(['xdg-open', this.historyEntry.source.sourceUrl]).catch(logError); + }); + + this.menu.addMenuItem(sourceItem); + } + + const imageUrlItem = new PopupMenu.PopupMenuItem('Open Image In Browser'); + imageUrlItem.connect('activate', () => { + if (this.historyEntry.source.imageLinkUrl) + Utils.execCheck(['xdg-open', this.historyEntry.source.imageLinkUrl]).catch(logError); + }); + + this.menu.addMenuItem(imageUrlItem); + } else { + this.menu.addMenuItem(new PopupMenu.PopupMenuItem('Unknown source.')); + } + + const previewItem = new PopupMenu.PopupBaseMenuItem({can_focus: false, reactive: false}); + this.menu.addMenuItem(previewItem); + + this._setAsWallpaperItem = new PopupMenu.PopupMenuItem('Set As Wallpaper'); + this._setAsWallpaperItem.connect('activate', () => { + this.emit('activate', null); // Fixme: not sure what the second parameter should be. null seems to work fine for now. + }); + + if (index !== 0) { + // this.menu.addMenuItem(new PopupMenu.PopupBaseMenuItem({ can_focus: false, reactive: false })); // theme independent spacing + this.menu.addMenuItem(this._setAsWallpaperItem); + } + + const copyToFavorites = new PopupMenu.PopupMenuItem('Save For Later'); + copyToFavorites.connect('activate', () => { + this._saveImage().catch(logError); + }); + this.menu.addMenuItem(copyToFavorites); + + // Static URLs can't block images (yet?) + if (this.historyEntry.adapter.type !== 5) { + const blockImage = new PopupMenu.PopupMenuItem('Add To Blocklist'); + blockImage.connect('activate', () => { + this._addToBlocklist(this.historyEntry); + }); + this.menu.addMenuItem(blockImage); + } + + /* + Load the image on first opening of the sub menu instead of during creation of the history list. + */ + this.menu.connect('open-state-changed', (_, open: boolean | unknown) => { + if (open) { + if (this._previewActor !== null) + return; + + if (!this.historyEntry.path) { + this._logger.error('Image path in entry not found'); + return; + } + + try { + const width = 270; // 270 looks good for the now fixed 350px menu width + // const width = this.menu.actor.get_width(); // This should be correct but gives different results per element? + const pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(this.historyEntry.path, width, -1, true); + const height = pixbuf.get_height(); + + const image = new Clutter.Image(); + const pixelFormat = pixbuf.get_has_alpha() ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888; + image.set_data( + pixbuf.get_pixels(), + pixelFormat, + width, + height, + pixbuf.get_rowstride() + ); + this._previewActor = new Clutter.Actor({height, width}); + this._previewActor.set_content(image); + + previewItem.actor.add_actor(this._previewActor); + } catch (exception) { + this._logger.error(String(exception)); + } + } + }); + } + + private _addToBlocklist(entry: HistoryModule.HistoryEntry) { + if (!entry.adapter.id || entry.adapter.id === '-1' || !entry.name) { + this._logger.error('Image entry is missing information'); + return; + } + + const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${entry.adapter.id}/`; + const generalSettings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); + const blockedFilenames = generalSettings.getStrv('blocked-images'); + + if (blockedFilenames.includes(entry.name)) + return; + + blockedFilenames.push(entry.name); + generalSettings.setStrv('blocked-images', blockedFilenames); + } + + private async _saveImage() { + if (!this.historyEntry.path || !this.historyEntry.name) + throw new Error('Image entry is missing information'); + + const sourceFile = Gio.File.new_for_path(this.historyEntry.path); + const targetFolder = Gio.File.new_for_path(this._settings.getString('favorites-folder')); + const targetFile = targetFolder.get_child(this.historyEntry.name); + const targetInfoFile = targetFolder.get_child(`${this.historyEntry.name}.json`); + + try { + if (!targetFolder.make_directory_with_parents(null)) + throw new Error('Could not create directories.'); + } catch (error) { + if (error === Gio.IOErrorEnum.EXISTS) { /** noop */ } + } + + // This function was rewritten by Gio._promisify + // @ts-expect-error + if (!await sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null)) + throw new Error('Failed copying image.'); + + // https://gjs.guide/guides/gio/file-operations.html#writing-file-contents + // This function was rewritten by Gio._promisify + // @ts-expect-error + const [success, message]: [boolean, string] = await targetInfoFile.replace_contents_bytes_async( + // @ts-expect-error Don't know from where to import + new TextEncoder().encode(JSON.stringify(this.historyEntry.source, null, '\t')), + null, + false, + Gio.FileCreateFlags.NONE, + null); + + if (!success) + throw new Error(`Failed writing file contents: ${message}`); + } + + setIndex(index: number) { + this._prefixLabel.set_text(`${String(index)}.`); + } +}); + +const CurrentImageElement = GObject.registerClass({ + GTypeName: 'CurrentImageElement', +}, class CurrentImageElement extends HistoryElement { + constructor(params: object | undefined, historyEntry: HistoryModule.HistoryEntry) { + super(params, historyEntry, 0); + + if (this._setAsWallpaperItem) + this._setAsWallpaperItem.destroy(); + } }); /** @@ -263,160 +258,180 @@ var CurrentImageElement = GObject.registerClass({ * feature. * The remaining time will only be displayed if the af-feature is activated. */ -var NewWallpaperElement = GObject.registerClass({ - GTypeName: 'NewWallpaperElement', -}, class NewWallpaperElement extends PopupMenu.PopupBaseMenuItem { - - _init(params) { - super._init(params); - - this._timer = new Timer.AFTimer(); - - this._container = new St.BoxLayout({ - vertical: true - }); - - this._newWPLabel = new St.Label({ - text: 'New Wallpaper', - style_class: 'rwg-new-lable' - }); - this._container.add_child(this._newWPLabel); - - this._remainingLabel = new St.Label({ - text: '1 minute remaining' - }); - this._container.add_child(this._remainingLabel); - - this.actor.add_child(this._container); - } - - show() { - if (this._timer.isActive()) { - let remainingMinutes = this._timer.remainingMinutes(); - let minutes = remainingMinutes % 60; - let hours = Math.floor(remainingMinutes / 60); - - let hoursText = hours.toString(); - hoursText += (hours === 1) ? ' hour' : ' hours'; - let minText = minutes.toString(); - minText += (minutes === 1) ? ' minute' : ' minutes'; - - if (hours >= 1) { - this._remainingLabel.text = '... ' + hoursText + ' and ' + minText + ' remaining.' - } else { - this._remainingLabel.text = '... ' + minText + ' remaining.' - } - - this._remainingLabel.show(); - } else { - this._remainingLabel.hide(); - } - } - +const NewWallpaperElement = GObject.registerClass({ + GTypeName: 'NewWallpaperElement', +}, +class NewWallpaperElement extends PopupMenu.PopupBaseMenuItem { + private _timer = Timer.getTimer(); + private _remainingLabel; + + constructor(params: object | undefined) { + super(params); + + const container = new St.BoxLayout({ + vertical: true, + }); + + const newWPLabel = new St.Label({ + text: 'New Wallpaper', + style_class: 'rwg-new-label', + }); + container.add_child(newWPLabel); + + this._remainingLabel = new St.Label({ + text: '1 minute remaining', + }); + container.add_child(this._remainingLabel); + + this.actor.add_child(container); + } + + show() { + if (this._timer.isActive()) { + const remainingMinutes = this._timer.remainingMinutes(); + const minutes = remainingMinutes % 60; + const hours = Math.floor(remainingMinutes / 60); + + let hoursText = hours.toString(); + hoursText += hours === 1 ? ' hour' : ' hours'; + let minText = minutes.toString(); + minText += minutes === 1 ? ' minute' : ' minutes'; + + if (hours >= 1) + this._remainingLabel.text = `... ${hoursText} and ${minText} remaining.`; + else + this._remainingLabel.text = `... ${minText} remaining.`; + + + this._remainingLabel.show(); + } else { + this._remainingLabel.hide(); + } + } }); -var StatusElement = class { - - constructor() { - this.icon = new St.Icon({ - icon_name: 'preferences-desktop-wallpaper-symbolic', - style_class: 'system-status-icon' - }); - } - - startLoading() { - this.icon.ease({ - opacity: 20, - duration: 1337, - mode: Clutter.AnimationMode.EASE_IN_OUT_SINE, - autoReverse: true, - repeatCount: -1 - }); - } - - stopLoading() { - this.icon.remove_all_transitions(); - this.icon.opacity = 255; - } - -}; - -var HistorySection = class extends PopupMenu.PopupMenuSection { - - constructor() { - super(); - - /** - * Cache HistoryElements for performance of long histories. - */ - this._historySectionCache = {}; - - this._historyCache = []; - - this.actor = new St.ScrollView({ - hscrollbar_policy: Gtk.PolicyType.NEVER, - vscrollbar_policy: Gtk.PolicyType.AUTOMATIC - }); - - this.actor.add_actor(this.box); - } - - updateList(history, onEnter, onLeave, onSelect) { - if (this._historyCache.length <= 1) { - this.removeAll(); // remove empty history element - } - - let existingHistoryElements = []; - - for (let i = 1; i < history.length; i++) { - let historyID = history[i].id; - let tmp; - - if (!(historyID in this._historySectionCache)) { - tmp = new HistoryElement(history[i], i); - - tmp.actor.connect('key-focus-in', onEnter); - tmp.actor.connect('key-focus-out', onLeave); - tmp.actor.connect('enter-event', onEnter); - - tmp.connect('activate', onSelect); - this._historySectionCache[historyID] = tmp; - - this.addMenuItem(tmp, i - 1); - } else { - tmp = this._historySectionCache[historyID]; - tmp.setIndex(i); - } - - existingHistoryElements.push(historyID); - } - - this._cleanupHistoryCache(existingHistoryElements); - this._historyCache = history; - } - - _cleanupHistoryCache(existingIDs) { - let destroyIDs = Object.keys(this._historySectionCache).filter((i) => existingIDs.indexOf(i) === -1); - - destroyIDs.map(id => { - this._historySectionCache[id].destroy(); - delete this._historySectionCache[id]; - }); - } - - clear() { - this._cleanupHistoryCache([]); - this.removeAll(); - this.addMenuItem( - new PopupMenu.PopupMenuItem('No recent wallpaper ...', { - activate: false, - hover: false, - style_class: 'rwg-recent-lable', - can_focus: false - }) - ); +class StatusElement { + icon; + + constructor() { + this.icon = new St.Icon({ + icon_name: 'preferences-desktop-wallpaper-symbolic', + style_class: 'system-status-icon', + }); + } + + startLoading() { + // @ts-expect-error Don't know where this is defined + this.icon.ease({ + opacity: 20, + duration: 1337, + mode: Clutter.AnimationMode.EASE_IN_OUT_SINE, + autoReverse: true, + repeatCount: -1, + }); + } + + stopLoading() { + this.icon.remove_all_transitions(); + this.icon.opacity = 255; + } +} - this._historyCache = []; - } +class HistorySection extends PopupMenu.PopupMenuSection { + /** + * Cache HistoryElements for performance of long histories. + */ + private _historySectionCache = new Map(); + private _historyCache: HistoryModule.HistoryEntry[] = []; + + constructor() { + super(); + + this.actor = new St.ScrollView({ + hscrollbar_policy: Gtk.PolicyType.NEVER, + vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, + }); + + this.actor.add_actor(this.box); + } + + // eslint-disable-next-line no-unused-vars + updateList(history: HistoryModule.HistoryEntry[], onEnter: (actor: typeof HistoryElement) => void, onLeave: (actor: typeof HistoryElement) => void, onSelect: (actor: typeof HistoryElement) => void) { + if (this._historyCache.length <= 1) + this.removeAll(); // remove empty history element + + const existingHistoryElements = []; + + for (let i = 1; i < history.length; i++) { + const historyID = history[i].id; + + if (!historyID) + continue; + + // Typing fails here for our own class derived from GObject.registerClass + // FIXME: Expect a whole lot of ignore comments here: + + let cachedHistoryElement = this._historySectionCache.get(historyID); + if (!cachedHistoryElement) { + // @ts-expect-error + cachedHistoryElement = new HistoryElement(undefined, history[i], i); + // @ts-expect-error + cachedHistoryElement.actor.connect('key-focus-in', onEnter); + // @ts-expect-error + cachedHistoryElement.actor.connect('key-focus-out', onLeave); + // @ts-expect-error + cachedHistoryElement.actor.connect('enter-event', onEnter); + + // @ts-expect-error + cachedHistoryElement.connect('activate', onSelect); + // @ts-expect-error + this._historySectionCache.set(historyID, cachedHistoryElement); + + // @ts-expect-error + this.addMenuItem(cachedHistoryElement, i - 1); + } else { + // @ts-expect-error + cachedHistoryElement.setIndex(i); + } + + existingHistoryElements.push(historyID); + } + + this._cleanupHistoryCache(existingHistoryElements); + this._historyCache = history; + } + + private _cleanupHistoryCache(existingIDs: string[]) { + const destroyIDs = Array.from(this._historySectionCache.keys()).filter(i => existingIDs.indexOf(i) === -1); + + destroyIDs.forEach(id => { + // Same as the block above, typing from GObject.registerClass fails + // @ts-expect-error + this._historySectionCache.get(id)?.destroy(); + this._historySectionCache.delete(id); + }); + } + + clear() { + this._cleanupHistoryCache([]); + this.removeAll(); + this.addMenuItem( + new PopupMenu.PopupMenuItem('No recent wallpaper ...', { + activate: false, + hover: false, + style_class: 'rwg-recent-label', + can_focus: false, + }) + ); + + this._historyCache = []; + } +} +export { + StatusElement, + NewWallpaperElement, + HistorySection, + CurrentImageElement, + HistoryElement }; diff --git a/src/hydraPaper.ts b/src/hydraPaper.ts index 9f0423fa..1f47d805 100644 --- a/src/hydraPaper.ts +++ b/src/hydraPaper.ts @@ -1,88 +1,73 @@ -const Gio = imports.gi.Gio; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const Utils = Self.imports.utils; - -var HydraPaper = class { - #hydraPaperCommand = null; - #hydraPaperCancellable = null; - - constructor() { } - - /** - * Check whether HydraPaper is available on this system. - * @returns {boolean} - Whether HydraPaper is available - */ - async isAvailable() { - if (this.#hydraPaperCommand !== null) { - return true; - } - - try { - // Normal installation: - await Utils.Utils.execCheck(['hydrapaper', '--help']); - - this.#hydraPaperCommand = ['hydrapaper']; - return true; - } catch (error) { - // logError(error); - } - - try { - // FlatPak installation: - await Utils.Utils.execCheck(['org.gabmus.hydrapaper', '--help']); - - this.#hydraPaperCommand = ['org.gabmus.hydrapaper']; - return true; - } catch (error) { - // logError(error); - } - - return this.#hydraPaperCommand !== null; - } - - /** - * Cancel all running processes - * @returns - */ - cancelRunning() { - if (this.#hydraPaperCancellable === null) { - return; - } - - this.#hydraPaperCancellable.cancel(); - this.#hydraPaperCancellable = null; - } - - /** - * Generate a new combined wallpaper from multiple paths. - * - * @param {Array} wallpaperArray Array of wallpaper paths matching the monitor count - * @param {boolean} darkmode Turn on darkmode, this results into a different cache file - */ - async run(wallpaperArray, darkmode) { - // Cancel already running processes before starting new ones - this.cancelRunning(); - - // Needs a copy here - let hydraPaperCommand = [...this.#hydraPaperCommand]; - - if (darkmode) { - hydraPaperCommand.push('--darkmode'); - } - - hydraPaperCommand.push('--cli'); - hydraPaperCommand = hydraPaperCommand.concat(wallpaperArray); - - try { - this.#hydraPaperCancellable = new Gio.Cancellable(); - - // hydrapaper [--darkmode] --cli PATH PATH PATH - await Utils.Utils.execCheck(hydraPaperCommand, this.#hydraPaperCancellable); - - this.#hydraPaperCancellable = null; - } catch (error) { - logError(error); - } - } +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; + +import * as Utils from './utils.js'; + +class HydraPaper { + private _command: string[] | null = null; + private _cancellable: Gio.Cancellable | null = null; + + isAvailable(): boolean { + if (this._command !== null) + return true; + + const normalPath = GLib.find_program_in_path('hydrapaper'); + if (normalPath) { + this._command = [normalPath]; + return true; + } + + const flatpakPath = GLib.find_program_in_path('org.gabmus.hydrapaper'); + if (flatpakPath) { + this._command = [flatpakPath]; + return true; + } + + return this._command !== null; + } + + cancelRunning() { + if (this._cancellable === null) + return; + + this._cancellable.cancel(); + this._cancellable = null; + } + + /** + * Run HydraPaper in CLI mode. + * + * HydraPaper will combine all images in wallpaperArray into a single image and save + * it into the users cache folder. + * Afterward HydraPaper will set the mode to 'spanned' and the 'picture-uri' or 'picture-uri-dark'. + * + * @param {string[]} wallpaperArray Array of image paths + * @param {boolean} darkmode Use darkmode, gives different image in cache path + */ + async run(wallpaperArray: string[], darkmode: boolean = false) { + // Cancel already running processes before starting new ones + this.cancelRunning(); + + // TODO: Proper error handling + if (this._command === null) + return; + + // Needs a copy here + let command = [...this._command]; + + if (darkmode) + command.push('--darkmode'); + + command.push('--cli'); + command = command.concat(wallpaperArray); + + this._cancellable = new Gio.Cancellable(); + + // hydrapaper [--darkmode] --cli PATH PATH PATH + await Utils.execCheck(command, this._cancellable); + + this._cancellable = null; + } } + +export {HydraPaper}; diff --git a/src/jsonPath.ts b/src/jsonPath.ts index 0c74385a..3c2b9a85 100644 --- a/src/jsonPath.ts +++ b/src/jsonPath.ts @@ -1,114 +1,109 @@ -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const Utils = Self.imports.utils; - -var JSONPathParser = class { - - /** - * Access a simple json path expression of an object. - * Returns the accessed value or null if the access was not possible. - * - * @param inputObject the object to access - * @param inputString the json path expression - * @param randomElements the predefined random Elements - * @param newRandomness whether to ignore previously defined random Elements - * @returns {*} - */ - static access(inputObject, inputString, randomElements = null, newRandomness = true) { - if (inputObject === null || inputObject === undefined) { - return null; - } - - if (inputString.length === 0) { - return { - Object: inputObject, - RandomElements: randomElements, - }; - } - - if (randomElements === null) { - randomElements = []; - newRandomness = true; - } - - let startDot = inputString.indexOf('.'); - if (startDot === -1) { - startDot = inputString.length; - } - - let keyString = inputString.slice(0, startDot); - let inputStringTail = inputString.slice(startDot + 1); - - let startParentheses = keyString.indexOf('['); - - if (startParentheses === -1) { - - let targetObject = this._getTargetObject(inputObject, keyString); - if (targetObject == null) { - return null; - } - - return this.access(targetObject, inputStringTail, randomElements, newRandomness); - - } else { - - let indexString = keyString.slice(startParentheses + 1, keyString.length - 1); - keyString = keyString.slice(0, startParentheses); - - let targetObject = this._getTargetObject(inputObject, keyString); - if (targetObject == null) { - return null; - } - - switch (indexString) { - case "@random": - let randomNumber = null; - if (!newRandomness && randomElements.length >= 1) { - // Take and remove first element - randomNumber = randomElements.shift(); - } else if (!newRandomness && randomElements.length < 1) { - randomNumber = this.randomElement(targetObject); - } else { - randomNumber = this.randomElement(targetObject); - randomElements.push(randomNumber); - } - - return this.access(randomNumber, inputStringTail, randomElements, newRandomness); - // add special keywords here - default: - // expecting integer - return this.access(targetObject[parseInt(indexString)], inputStringTail, randomElements, newRandomness); - } - - } - - }; - - /** - * Check validity of the key string and return the target object or null. - * @param inputObject - * @param keyString - * @returns {*} - * @private - */ - static _getTargetObject(inputObject, keyString) { - if (!keyString.empty && keyString !== "$" && !inputObject.hasOwnProperty(keyString)) { - return null; - } - - return (keyString === "$") ? inputObject : inputObject[keyString]; - }; - - /** - * Returns the value of a random key of a given object. - * - * @param inputObject - * @returns {*} - */ - static randomElement(inputObject) { - let keys = Object.keys(inputObject); - let randomIndex = Utils.Utils.getRandomNumber(keys.length); - - return inputObject[keys[randomIndex]]; - } - -}; +import * as Utils from './utils.js'; + +/** + * Access a simple json path expression of an object. + * Returns the accessed value or null if the access was not possible. + * Accepts predefined number values to access the same elements as previously + * and allows to override the use of these values. + * + * @param {unknown} inputObject A JSON object + * @param {string} inputString JSONPath to follow, see wiki for syntax + * @param {number[]} randomNumbers Array of pre-generated numbers + * @param {boolean} newRandomness Whether to ignore a given randomNumbers array + */ +function getTarget(inputObject: unknown, inputString: string, randomNumbers?: number[], newRandomness?: boolean): { Object: unknown, RandomNumbers?: number[] } | null { + if (!inputObject) + return null; + + if (inputString.length === 0) { + return { + Object: inputObject, + RandomNumbers: randomNumbers, + }; + } + + if (!randomNumbers) { + randomNumbers = []; + newRandomness = true; + } + + let startDot = inputString.indexOf('.'); + if (startDot === -1) + startDot = inputString.length; + + let keyString = inputString.slice(0, startDot); + const inputStringTail = inputString.slice(startDot + 1); + + const startParentheses = keyString.indexOf('['); + + if (startParentheses === -1) { + // Expect Object here + const targetObject = _getObjectMember(inputObject, keyString); + if (!targetObject) + return null; + + return getTarget(targetObject, inputStringTail, randomNumbers, newRandomness); + } else { + const indexString = keyString.slice(startParentheses + 1, keyString.length - 1); + keyString = keyString.slice(0, startParentheses); + + // Expect an Array at this point + const targetObject = _getObjectMember(inputObject, keyString); + if (!targetObject || !Array.isArray(targetObject)) + return null; + + switch (indexString) { + case '@random': { + let randomNumber: number = -1; + let randomElement: unknown = null; + + if (!newRandomness && randomNumbers.length > 0) { + // Take and remove first element + randomNumber = randomNumbers.shift() ?? -1; + randomElement = targetObject[randomNumber]; + } else { + [randomElement, randomNumber] = _randomElement(targetObject); + + if (newRandomness) + randomNumbers.push(randomNumber); + } + + return getTarget(randomElement, inputStringTail, randomNumbers, newRandomness); + } + // add special keywords here + default: + // expecting integer + return getTarget(targetObject[parseInt(indexString)], inputStringTail, randomNumbers, newRandomness); + } + } +} + +/** + * Check validity of the key string and return the target member or null. + * + * @param {object} inputObject JSON object + * @param {string} keyString Name of the key in the object + */ +function _getObjectMember(inputObject: object, keyString: string): unknown | null { + if (keyString === '$') + return inputObject; + + for (const [key, value] of Object.entries(inputObject)) { + if (key === keyString) + return value; + } + + return null; +} + +/** + * Returns the value of a random key of a given array. + * + * @param {Array} array Array with values + */ +function _randomElement(array: Array): [T, number] { + const randomNumber = Utils.getRandomNumber(array.length); + return [array[randomNumber], randomNumber]; +} + +export {getTarget}; diff --git a/src/logger.ts b/src/logger.ts index 0558f675..73f73c92 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,52 +1,55 @@ // TODO: use an enum once moved to TS const LOG_LEVEL = { - SILENT: 0, - ERROR: 1, - WARN: 2, - INFO: 3, - DEBUG: 4, -} + SILENT: 0, + ERROR: 1, + WARN: 2, + INFO: 3, + DEBUG: 4, +}; // TODO: add UI option or at least ENV variable (this is a quick workaround to conform to extension review requirements) const CURRENT_LOG_LEVEL = LOG_LEVEL.WARN; -var Logger = class { +class Logger { + private _prefix: string; + private _callingClass: string; - constructor(prefix, callingClass) { - this._prefix = prefix; - this._callingClass = callingClass; - } + constructor(prefix: string, callingClass: string) { + this._prefix = prefix; + this._callingClass = callingClass; + } - _log(level, message) { - log(this._prefix + ' [' + level + '] >> ' + this._callingClass + ' :: ' + message); - } + private _log(level: string, message: unknown) { + log(`${this._prefix} [${level}] >> ${this._callingClass} :: ${message}`); + } - debug(message) { - if (CURRENT_LOG_LEVEL < LOG_LEVEL.DEBUG) - return; + debug(message: string) { + if (CURRENT_LOG_LEVEL < LOG_LEVEL.DEBUG) + return; - this._log('DEBUG', message); - } + this._log('DEBUG', message); + } - info(message) { - if (CURRENT_LOG_LEVEL < LOG_LEVEL.INFO) - return; + info(message: string) { + if (CURRENT_LOG_LEVEL < LOG_LEVEL.INFO) + return; - this._log('INFO', message); - } + this._log('INFO', message); + } - warn(message) { - if (CURRENT_LOG_LEVEL < LOG_LEVEL.WARN) - return; + warn(message: string) { + if (CURRENT_LOG_LEVEL < LOG_LEVEL.WARN) + return; - this._log('WARNING', message); - } + this._log('WARNING', message); + } - error(message) { - if (CURRENT_LOG_LEVEL < LOG_LEVEL.ERROR) - return; + error(message: string) { + if (CURRENT_LOG_LEVEL < LOG_LEVEL.ERROR) + return; - this._log('ERROR', message); - } + this._log('ERROR', message); + } +} -}; +export {Logger}; diff --git a/src/metadata.json b/src/metadata.json index f17fe8ad..47728ee8 100644 --- a/src/metadata.json +++ b/src/metadata.json @@ -1,10 +1,10 @@ { - "shell-version": [ "40", "40.0", "40.1", "41", "42", "43" ], + "shell-version": [ "43" ], "uuid": "randomwallpaper@iflow.space", "settings-schema": "org.gnome.shell.extensions.space.iflow.randomwallpaper", "name": "Random Wallpaper", "description": "Fetches a random wallpaper from an online source and sets it as desktop background. \nThe desktop background can be updated periodically or manually.", - "version": 29, - "semantic-version": "2.7.3", + "version": 32, + "semantic-version": "3.0.0", "url": "https://github.com/ifl0w/RandomWallpaperGnome3" } diff --git a/src/prefs.ts b/src/prefs.ts index 461778a9..27ede15e 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -1,20 +1,27 @@ -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; -const ExtensionUtils = imports.misc.extensionUtils; +import * as Gio from 'gi://Gio'; +import * as Gtk from 'gi://Gtk'; -const Self = ExtensionUtils.getCurrentExtension(); -const HydraPaper = Self.imports.hydraPaper; -const SourceRow = Self.imports.ui.sourceRow; -const Settings = Self.imports.settings; -const Utils = Self.imports.utils; -const WallpaperController = Self.imports.wallpaperController; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as Adw from '@gi/gtk4/adw/adw'; +import type * as SettingsNamespace from './settings.js'; +import type * as UtilsNamespace from './utils.js'; +import type * as LoggerNamespace from './logger.js'; +import type * as SourceRowNamespace from './ui/sourceRow.js'; + +let Settings: typeof SettingsNamespace; +let Utils: typeof UtilsNamespace; +let Logger: typeof LoggerNamespace; +let SourceRow: typeof SourceRowNamespace.SourceRow; -const LoggerModule = Self.imports.logger; +const Self = ExtensionUtils.getCurrentExtension(); -function init(metaData) { - //Convenience.initTranslations(); +/** + * + */ +// eslint-disable-next-line no-unused-vars +function init() { + // Convenience.initTranslations(); } // https://gjs.guide/extensions/overview/anatomy.html#prefs-js @@ -24,232 +31,282 @@ function init(metaData) { // https://gjs.guide/extensions/development/preferences.html#preferences-window // Gnome 42+ -function fillPreferencesWindow(window) { - window.set_default_size(-1, 720); - new RandomWallpaperSettings(window); +/** + * + * @param {Adw.PreferencesWindow} window Window the extension should fill + */ +// eslint-disable-next-line no-unused-vars +function fillPreferencesWindow(window: Adw.PreferencesWindow) { + window.set_default_size(-1, 720); + // temporary fill window to prevent error message until modules are loaded + const tmpPage = new Adw.PreferencesPage(); + window.add(tmpPage); + + new RandomWallpaperSettings(window, tmpPage); } + // 40 < Gnome < 42 // function buildPrefsWidget() { -// let window = new Adw.PreferencesWindow(); -// new RandomWallpaperSettings(window); -// return window; +// let window = new Adw.PreferencesWindow(); +// new RandomWallpaperSettings(window); +// return window; // } /* UI Setup */ -var RandomWallpaperSettings = class { - constructor(window) { - this.logger = new LoggerModule.Logger('RWG3', 'RandomWallpaper.Settings'); - - this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA); - this._backendConnection = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); - this._backendConnection.set('pause-timer', 'boolean', true); - - this._sources = []; - this._loadSources(); - - this._builder = new Gtk.Builder(); - //this._builder.set_translation_domain(Self.metadata['gettext-domain']); - this._builder.add_from_file(Self.path + '/ui/pageGeneral.ui'); - this._builder.add_from_file(Self.path + '/ui/pageSources.ui'); - - this._fillTypeComboRow(); - - this._settings.bind('minutes', - this._builder.get_object('duration_minutes'), - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('hours', - this._builder.get_object('duration_hours'), - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('auto-fetch', - this._builder.get_object('af_switch'), - 'enable-expansion', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('disable-hover-preview', - this._builder.get_object('disable_hover_preview'), - 'active', - Gio.SettingsBindFlags.DEFAULT) - this._settings.bind('hide-panel-icon', - this._builder.get_object('hide_panel_icon'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('fetch-on-startup', - this._builder.get_object('fetch_on_startup'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('general-post-command', - this._builder.get_object('general_post_command'), - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('multiple-displays', - this._builder.get_object('enable_multiple_displays'), - 'active', - Gio.SettingsBindFlags.DEFAULT); - - this._bindButtons(); - this._bindHistorySection(window); - - window.connect('close-request', () => { - this._backendConnection.set('pause-timer', 'boolean', false); - }); - - window.add(this._builder.get_object('page_general')); - window.add(this._builder.get_object('page_sources')); - - this._sources.forEach(id => { - let sourceRow = new SourceRow.SourceRow(id); - this._builder.get_object('sources_list').add(sourceRow); - - sourceRow.button_delete.connect('clicked', () => { - sourceRow.clearConfig(); - this._builder.get_object('sources_list').remove(sourceRow); - Utils.Utils.removeItemOnce(this._sources, id); - this._saveSources(); - }); - }); - - try { - new HydraPaper.HydraPaper().isAvailable().then(result => { - if (result === true) { - this._builder.get_object('multiple_displays_row').set_sensitive(true); - } - }); - } catch (error) { - // Should already be handled although in a different context - } - } - - _fillTypeComboRow() { - let comboRow = this._builder.get_object('combo_background_type'); - - // Fill combo from settings enum - let availableTypes = this._settings.getSchema().get_key('change-type').get_range(); //GLib.Variant (sv) - // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) - // s should be 'enum' - // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. - // v is 'as' - availableTypes = availableTypes.get_child_value(1).get_variant().get_strv(); - - let stringList = Gtk.StringList.new(availableTypes); - comboRow.model = stringList; - comboRow.selected = this._settings.get('change-type', 'enum'); - - comboRow.connect('notify::selected', comboRow => { - this._settings.set('change-type', 'enum', comboRow.selected); - }); - } - - _bindButtons() { - let newWallpaperButton = this._builder.get_object('request_new_wallpaper'); - let origNewWallpaperText = newWallpaperButton.get_child().get_label(); - newWallpaperButton.connect('activated', () => { - newWallpaperButton.get_child().set_label("Loading ..."); - newWallpaperButton.set_sensitive(false); - - // The backend sets this back to false after fetching the image - listen for that event. - let handler = this._backendConnection.observe('request-new-wallpaper', () => { - if (!this._backendConnection.get('request-new-wallpaper', 'boolean')) { - newWallpaperButton.get_child().set_label(origNewWallpaperText); - newWallpaperButton.set_sensitive(true); - this._backendConnection.disconnect(handler); - } - }); - - this._backendConnection.set('request-new-wallpaper', 'boolean', true); - }); - - let sourceRowList = this._builder.get_object('sources_list'); - this._builder.get_object('button_new_source').connect('clicked', () => { - let sourceRow = new SourceRow.SourceRow(); - sourceRowList.add(sourceRow); - this._sources.push(String(sourceRow.id)); - this._saveSources(); - - sourceRow.button_delete.connect('clicked', () => { - sourceRow.clearConfig(); - sourceRowList.remove(sourceRow); - Utils.Utils.removeItemOnce(this._sources, sourceRow.id); - this._saveSources(); - }); - }); - } - - _bindHistorySection(window) { - let entryRow = this._builder.get_object('row_favorites_folder'); - entryRow.text = this._settings.get('favorites-folder', 'string'); - - this._settings.bind('history-length', - this._builder.get_object('history_length'), - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('favorites-folder', - entryRow, - 'text', - Gio.SettingsBindFlags.DEFAULT); - - this._builder.get_object('clear_history').connect('clicked', () => { - this._backendConnection.set('clear-history', 'boolean', true); - }); - - this._builder.get_object('open_wallpaper_folder').connect('clicked', () => { - this._backendConnection.set('open-folder', 'boolean', true); - }); - - this._builder.get_object('button_favorites_folder').connect('clicked', () => { - // For GTK 4.10+ - // Gtk.FileDialog(); - - // https://stackoverflow.com/a/54487948 - this._saveDialog = new Gtk.FileChooserNative({ - title: 'Choose a Wallpaper Folder', - action: Gtk.FileChooserAction.SELECT_FOLDER, - accept_label: 'Open', - cancel_label: 'Cancel', - transient_for: window, - modal: true, - }); - - this._saveDialog.connect('response', (dialog, response_id) => { - if (response_id === Gtk.ResponseType.ACCEPT) { - entryRow.text = this._saveDialog.get_file().get_path(); - } - this._saveDialog.destroy(); - }); - - this._saveDialog.show(); - }); - } - - /** - * Load the config from the gschema - */ - _loadSources() { - this._sources = this._settings.get('sources', 'strv'); - - // this._sources.sort((a, b) => { - // let path1 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${a}/`; - // let settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); - // let path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; - // let settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); - - // const nameA = settingsGeneral1.get('name', 'string').toUpperCase(); - // const nameB = settingsGeneral2.get('name', 'string').toUpperCase(); - - // return nameA.localeCompare(nameB); - // }); - - this._sources.sort((a, b) => { - let path1 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${a}/`; - let settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); - let path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; - let settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); - return settingsGeneral1.get('type', 'enum') - settingsGeneral2.get('type', 'enum'); - }); - } - - _saveSources() { - this._settings.set('sources', 'strv', this._sources); - } -}; +class RandomWallpaperSettings { + private _logger!: LoggerNamespace.Logger; + private _settings!: SettingsNamespace.Settings; + private _backendConnection!: SettingsNamespace.Settings; + + private _sources: string[] = []; + private _builder = new Gtk.Builder(); + private _saveDialog: Gtk.FileChooserNative | undefined; + + constructor(window: Adw.PreferencesWindow, tmpPage: Adw.PreferencesPage) { + // Dynamically load own modules. This allows us to use proper ES6 Modules + this._importModules().then(() => { + window.remove(tmpPage); + + if (!Logger || !Settings || !Utils || !SourceRow) + throw new Error('Error with imports'); + + this._logger = new Logger.Logger('RWG3', 'RandomWallpaper.Settings'); + this._settings = new Settings.Settings(); + this._backendConnection = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); + + this._backendConnection.setBoolean('pause-timer', true); + this._loadSources(); + + // this._builder.set_translation_domain(Self.metadata['gettext-domain']); + this._builder.add_from_file(`${Self.path}/ui/pageGeneral.ui`); + this._builder.add_from_file(`${Self.path}/ui/pageSources.ui`); + + this._fillTypeComboRow(); + + this._settings.bind('minutes', + this._builder.get_object('duration_minutes'), + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('hours', + this._builder.get_object('duration_hours'), + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('auto-fetch', + this._builder.get_object('af_switch'), + 'enable-expansion', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('disable-hover-preview', + this._builder.get_object('disable_hover_preview'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('hide-panel-icon', + this._builder.get_object('hide_panel_icon'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('fetch-on-startup', + this._builder.get_object('fetch_on_startup'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('general-post-command', + this._builder.get_object('general_post_command'), + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('multiple-displays', + this._builder.get_object('enable_multiple_displays'), + 'active', + Gio.SettingsBindFlags.DEFAULT); + + this._bindButtons(); + this._bindHistorySection(window); + + window.connect('close-request', () => { + this._backendConnection.setBoolean('pause-timer', false); + }); + + window.add(this._builder.get_object('page_general')); + window.add(this._builder.get_object('page_sources')); + + this._sources.forEach(id => { + const sourceRow = new SourceRow(undefined, id); + // eslint-disable-next-line no-extra-parens + (this._builder.get_object('sources_list') as Adw.PreferencesGroup).add(sourceRow); + + sourceRow.button_delete.connect('clicked', () => { + sourceRow.clearConfig(); + // eslint-disable-next-line no-extra-parens + (this._builder.get_object('sources_list') as Adw.PreferencesGroup).remove(sourceRow); + Utils.removeItemOnce(this._sources, id); + this._saveSources(); + }); + }); + + import('./hydraPaper.js').then(module => { + if (new module.HydraPaper().isAvailable()) + // eslint-disable-next-line no-extra-parens + (this._builder.get_object('multiple_displays_row') as Adw.ActionRow).set_sensitive(true); + }).catch(logError); + }).catch(error => { + logError(error); + throw error; + }); + } + + /** + * Import modules ES6 style instead the built-in gjs style. + * Allows proper async/await in imported modules. + */ + private async _importModules() { + // All imports as dynamic loads to work around the fact this module won't be in a topmost + // context inside the gnome shell and can't use import statements (yet). + // PopOS' tiling extension and RoundedCorners Extension work around the above limitation by + // manually rewriting the exported javascript file. We also have to do this but + // not for our own modules. + const loggerPromise = import('./logger.js'); + const utilsPromise = import('./utils.js'); + const sourceRowPromise = import('./ui/sourceRow.js'); + const settingsPromise = import('./settings.js'); + + const [moduleLogger, moduleUtils, moduleSourceRow, moduleSettings] = await Promise.all( + [loggerPromise, utilsPromise, sourceRowPromise, settingsPromise]); + Logger = moduleLogger; + Utils = moduleUtils; + SourceRow = moduleSourceRow.SourceRow; + Settings = moduleSettings; + } + + private _fillTypeComboRow() { + const comboRow: Adw.ComboRow = this._builder.get_object('combo_background_type'); + + // Fill combo from settings enum + const availableTypes = this._settings.getSchema().get_key('change-type').get_range(); // GLib.Variant (sv) + // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) + // s should be 'enum' + // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. + // v is 'as' + const availableTypesNames = availableTypes.get_child_value(1).get_variant().get_strv(); + + const stringList = Gtk.StringList.new(availableTypesNames); + comboRow.model = stringList; + comboRow.selected = this._settings.getEnum('change-type'); + + comboRow.connect('notify::selected', _comboRow => { + this._settings.setEnum('change-type', _comboRow.selected); + }); + } + + private _bindButtons() { + const newWallpaperButton: Adw.ActionRow = this._builder.get_object('request_new_wallpaper'); + const newWallpaperButtonLabel = newWallpaperButton.get_child() as Gtk.Label | null; + const origNewWallpaperText = newWallpaperButtonLabel?.get_label() ?? 'Request New Wallpaper'; + newWallpaperButton.connect('activated', () => { + newWallpaperButtonLabel?.set_label('Loading ...'); + newWallpaperButton.set_sensitive(false); + + // The backend sets this back to false after fetching the image - listen for that event. + const handler = this._backendConnection.observe('request-new-wallpaper', () => { + if (!this._backendConnection.getBoolean('request-new-wallpaper')) { + newWallpaperButtonLabel?.set_label(origNewWallpaperText); + newWallpaperButton.set_sensitive(true); + this._backendConnection.disconnect(handler); + } + }); + + this._backendConnection.setBoolean('request-new-wallpaper', true); + }); + + const sourceRowList = this._builder.get_object('sources_list') as Adw.PreferencesGroup; + this._builder.get_object('button_new_source').connect('clicked', () => { + const sourceRow = new SourceRow(); + sourceRowList.add(sourceRow); + this._sources.push(String(sourceRow.id)); + this._saveSources(); + + sourceRow.button_delete.connect('clicked', () => { + sourceRow.clearConfig(); + sourceRowList.remove(sourceRow); + Utils.removeItemOnce(this._sources, sourceRow.id); + this._saveSources(); + }); + }); + } + + private _bindHistorySection(window: Adw.PreferencesWindow) { + const entryRow = this._builder.get_object('row_favorites_folder') as Adw.EntryRow; + entryRow.text = this._settings.getString('favorites-folder'); + + this._settings.bind('history-length', + this._builder.get_object('history_length'), + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('favorites-folder', + entryRow, + 'text', + Gio.SettingsBindFlags.DEFAULT); + + this._builder.get_object('clear_history').connect('clicked', () => { + this._backendConnection.setBoolean('clear-history', true); + }); + + this._builder.get_object('open_wallpaper_folder').connect('clicked', () => { + this._backendConnection.setBoolean('open-folder', true); + }); + + this._builder.get_object('button_favorites_folder').connect('clicked', () => { + // For GTK 4.10+ + // Gtk.FileDialog(); + + // https://stackoverflow.com/a/54487948 + this._saveDialog = new Gtk.FileChooserNative({ + title: 'Choose a Wallpaper Folder', + action: Gtk.FileChooserAction.SELECT_FOLDER, + accept_label: 'Open', + cancel_label: 'Cancel', + transient_for: window, + modal: true, + }); + + this._saveDialog.connect('response', (dialog, response_id) => { + if (response_id === Gtk.ResponseType.ACCEPT) { + const text = dialog.get_file()?.get_path(); + if (text) + entryRow.text = text; + } + dialog.destroy(); + }); + + this._saveDialog.show(); + }); + } + + /** + * Load the config from the schema + */ + private _loadSources() { + this._sources = this._settings.getStrv('sources'); + + // this._sources.sort((a, b) => { + // let path1 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${a}/`; + // let settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); + // let path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; + // let settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); + + // const nameA = settingsGeneral1.get('name', 'string').toUpperCase(); + // const nameB = settingsGeneral2.get('name', 'string').toUpperCase(); + + // return nameA.localeCompare(nameB); + // }); + + this._sources.sort((a, b) => { + let path1 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${a}/`; + let settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); + let path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; + let settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); + return settingsGeneral1.getEnum('type') - settingsGeneral2.getEnum('type'); + }); + } + + private _saveSources() { + this._settings.setStrv('sources', this._sources); + } +} diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 0c468b4b..684bb711 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -1,209 +1,226 @@ -const GLib = imports.gi.GLib; -const Shell = imports.gi.Shell; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; -//self -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const LoggerModule = Self.imports.logger; -const Timer = Self.imports.timer; - -// UI Imports -const PanelMenu = imports.ui.panelMenu; -const PopupMenu = imports.ui.popupMenu; -const CustomElements = Self.imports.elements; -const Main = imports.ui.main; - -// Filesystem -const Gio = imports.gi.Gio; - -// Settings -const Prefs = Self.imports.settings; - -var RandomWallpaperMenu = class { - - constructor(wallpaperController) { - this.panelMenu = new PanelMenu.Button(0, "Random wallpaper"); - this.settings = new Prefs.Settings(); - this.wallpaperController = wallpaperController; - this.logger = new LoggerModule.Logger('RWG3', 'RandomWallpaperEntry'); - this.hidePanelIconHandler = this.settings.observe('hide-panel-icon', this.updatePanelMenuVisibility.bind(this)); - this._backendConnection = new Prefs.Settings(Prefs.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); - - // Panelmenu Icon - this.statusIcon = new CustomElements.StatusElement(); - this.panelMenu.add_child(this.statusIcon.icon); - - // new wallpaper button - this.newWallpaperItem = new CustomElements.NewWallpaperElement(); - this.panelMenu.menu.addMenuItem(this.newWallpaperItem); - - this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - - // Set fixed width so the preview images don't widen the menu - this.panelMenu.menu.actor.set_width(350); - - // current background section - this.currentBackgroundSection = new PopupMenu.PopupMenuSection(); - this.panelMenu.menu.addMenuItem(this.currentBackgroundSection); - this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - - // history section - this.historySection = new CustomElements.HistorySection(); - this.panelMenu.menu.addMenuItem(this.historySection); - - this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - - // Temporarily pause timer - this._pauseTimerItem = new PopupMenu.PopupSwitchMenuItem('Pause timer', false, {}); - this._pauseTimerItem.sensitive = this.settings.get('auto-fetch', 'boolean'); - this._pauseTimerItem.setToggleState(this._backendConnection.get('pause-timer', 'boolean')); - - this._pauseTimerItem.connect('toggled', (item, state) => { - this._backendConnection.set('pause-timer', 'boolean', state); - }); - - this.settings.observe('auto-fetch', () => { - this._pauseTimerItem.sensitive = this.settings.get('auto-fetch', 'boolean'); - }); - - this._backendConnection.observe('pause-timer', () => { - this._pauseTimerItem.setToggleState(this._backendConnection.get('pause-timer', 'boolean')); - }); - - this.panelMenu.menu.addMenuItem(this._pauseTimerItem); - - // clear history button - this.clearHistoryItem = new PopupMenu.PopupMenuItem('Clear History'); - this.panelMenu.menu.addMenuItem(this.clearHistoryItem); - - // open wallpaper folder button - this.openFolder = new PopupMenu.PopupMenuItem('Open Wallpaper Folder'); - this.panelMenu.menu.addMenuItem(this.openFolder); - - // settings button - this.openSettings = new PopupMenu.PopupMenuItem('Settings'); - this.panelMenu.menu.addMenuItem(this.openSettings); - - /* - add eventlistener - */ - this.wallpaperController.registerStartLoadingHook(() => this.statusIcon.startLoading()); - this.wallpaperController.registerStopLoadingHook(() => this.statusIcon.stopLoading()); - this.wallpaperController.registerStopLoadingHook(() => this.setHistoryList()); - - // new wallpaper event - this.newWallpaperItem.connect('activate', () => { - this.wallpaperController.fetchNewWallpaper(); - }); - - // clear history event - this.clearHistoryItem.connect('activate', () => { - this.wallpaperController.deleteHistory(); - }); - - // Open Wallpaper Folder - this.openFolder.connect('activate', (event) => { - let uri = GLib.filename_to_uri(this.wallpaperController.wallpaperLocation, ""); - Gio.AppInfo.launch_default_for_uri(uri, global.create_app_launch_context(0, -1)) - }); - - this.openSettings.connect("activate", () => { - // FIXME: Unhandled promise rejection. To suppress this warning, add an error handler to your promise chain with .catch() or a try-catch block around your await expression. - Gio.DBus.session.call( - 'org.gnome.Shell.Extensions', - '/org/gnome/Shell/Extensions', - 'org.gnome.Shell.Extensions', - 'OpenExtensionPrefs', - new GLib.Variant('(ssa{sv})', [Self.uuid, '', {}]), - null, - Gio.DBusCallFlags.NONE, - -1, - null); - }); - - this.panelMenu.menu.actor.connect('show', () => { - this.newWallpaperItem.show(); - }); - - // when the popupmenu disapears, check if the wallpaper is the original and - // reset it if needed - this.panelMenu.menu.actor.connect('hide', () => { - this.wallpaperController.resetWallpaper(); - }); - - this.panelMenu.menu.actor.connect('leave-event', () => { - this.wallpaperController.resetWallpaper(); - }); - - this.settings.observe('history', this.setHistoryList.bind(this)); - } - - init() { - this.updatePanelMenuVisibility(); - this.setHistoryList(); - - // add to panel - Main.panel.addToStatusArea("random-wallpaper-menu", this.panelMenu); - } - - cleanup() { - this.clearHistoryList(); - this.panelMenu.destroy(); - - // remove all signal handlers - if (this.hidePanelIconHandler !== null) { - this.settings.disconnect(this.hidePanelIconHandler); - } - } - - updatePanelMenuVisibility() { - if (this.settings.get('hide-panel-icon', 'boolean')) { - this.panelMenu.hide(); - } else { - this.panelMenu.show(); - } - } - - setCurrentBackgroundElement() { - this.currentBackgroundSection.removeAll(); - - let historyController = this.wallpaperController.getHistoryController(); - let history = historyController.history; - - if (history.length > 0) { - let currentImage = new CustomElements.CurrentImageElement(history[0]); - this.currentBackgroundSection.addMenuItem(currentImage); - } - } - - setHistoryList() { - this.wallpaperController.update(); - this.setCurrentBackgroundElement(); - - let historyController = this.wallpaperController.getHistoryController(); - let history = historyController.history; - - if (history.length <= 1) { - this.clearHistoryList(); - return; - } - - function onLeave(actor) { - this.wallpaperController.resetWallpaper(); - } - - function onEnter(actor) { - this.wallpaperController.previewWallpaper(actor.historyId); - } - - function onSelect(actor) { - this.wallpaperController.setWallpaper(actor.historyEntry.id); - } - - this.historySection.updateList(history, onEnter.bind(this), onLeave.bind(this), onSelect.bind(this)); - } - - clearHistoryList() { - this.historySection.clear(); - } - -}; +import * as Main from '@gi/ui/main'; +import * as PanelMenu from '@gi/ui/panelMenu'; +import * as PopupMenu from '@gi/ui/popupMenu'; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as CustomElements from './historyMenuElements.js'; +import * as Settings from './settings.js'; +import * as Utils from './utils.js'; + +import {Logger} from './logger.js'; +import {WallpaperController} from './wallpaperController.js'; + +const Self = ExtensionUtils.getCurrentExtension(); + +class RandomWallpaperMenu { + private _logger = new Logger('RWG3', 'RandomWallpaperEntry'); + private _settings = new Settings.Settings(); + private _backendConnection = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); + + private _wallpaperController; + private _currentBackgroundSection; + private _historySection; + private _hidePanelIconHandler; + + private panelMenu; + + constructor(wallpaperController: WallpaperController) { + this._wallpaperController = wallpaperController; + + this.panelMenu = new PanelMenu.Button(0, 'Random wallpaper'); + + // PanelMenu Icon + const statusIcon = new CustomElements.StatusElement(); + this.panelMenu.add_child(statusIcon.icon); + this._hidePanelIconHandler = this._settings.observe('hide-panel-icon', this.updatePanelMenuVisibility.bind(this)); + + // new wallpaper button + const newWallpaperItem = new CustomElements.NewWallpaperElement({}); + this.panelMenu.menu.addMenuItem(newWallpaperItem); + + this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + + // Set fixed width so the preview images don't widen the menu + this.panelMenu.menu.actor.set_width(350); + + // current background section + this._currentBackgroundSection = new PopupMenu.PopupMenuSection(); + this.panelMenu.menu.addMenuItem(this._currentBackgroundSection); + this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + + // history section + this._historySection = new CustomElements.HistorySection(); + this.panelMenu.menu.addMenuItem(this._historySection); + + this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + + // Temporarily pause timer + const pauseTimerItem = new PopupMenu.PopupSwitchMenuItem('Pause timer', false); + pauseTimerItem.sensitive = this._settings.getBoolean('auto-fetch'); + pauseTimerItem.setToggleState(this._backendConnection.getBoolean('pause-timer')); + + pauseTimerItem.connect('toggled', (_, state: boolean) => { + this._backendConnection.setBoolean('pause-timer', state); + }); + + this._settings.observe('auto-fetch', () => { + pauseTimerItem.sensitive = this._settings.getBoolean('auto-fetch'); + }); + + this._backendConnection.observe('pause-timer', () => { + pauseTimerItem.setToggleState(this._backendConnection.getBoolean('pause-timer')); + }); + + this.panelMenu.menu.addMenuItem(pauseTimerItem); + + // clear history button + const clearHistoryItem = new PopupMenu.PopupMenuItem('Clear History'); + this.panelMenu.menu.addMenuItem(clearHistoryItem); + + // open wallpaper folder button + const openFolder = new PopupMenu.PopupMenuItem('Open Wallpaper Folder'); + this.panelMenu.menu.addMenuItem(openFolder); + + // settings button + const openSettings = new PopupMenu.PopupMenuItem('Settings'); + this.panelMenu.menu.addMenuItem(openSettings); + + // add eventlistener + this._wallpaperController.registerStartLoadingHook(() => statusIcon.startLoading()); + this._wallpaperController.registerStopLoadingHook(() => statusIcon.stopLoading()); + this._wallpaperController.registerStopLoadingHook(() => this.setHistoryList()); + + // new wallpaper event + newWallpaperItem.connect('activate', () => { + this._wallpaperController.fetchNewWallpaper().catch(logError); + }); + + // clear history event + clearHistoryItem.connect('activate', () => { + this._wallpaperController.deleteHistory(); + }); + + // Open Wallpaper Folder + openFolder.connect('activate', () => { + const uri = GLib.filename_to_uri(this._wallpaperController.wallpaperLocation, ''); + Utils.execCheck(['xdg-open', uri]).catch(logError); + }); + + openSettings.connect('activate', () => { + // FIXME: Unhandled promise rejection. To suppress this warning, add an error handler to your promise chain with .catch() or a try-catch block around your await expression. + Gio.DBus.session.call( + 'org.gnome.Shell.Extensions', + '/org/gnome/Shell/Extensions', + 'org.gnome.Shell.Extensions', + 'OpenExtensionPrefs', + new GLib.Variant('(ssa{sv})', [Self.uuid, '', {}]), + null, + Gio.DBusCallFlags.NONE, + -1, + null); + }); + + this.panelMenu.menu.actor.connect('show', () => { + newWallpaperItem.show(); + }); + + // when the popupMenu disappears, check if the wallpaper is the original and + // reset it if needed + this.panelMenu.menu.actor.connect('hide', () => { + this._wallpaperController.resetWallpaper(); + }); + + this.panelMenu.menu.actor.connect('leave-event', () => { + this._wallpaperController.resetWallpaper(); + }); + + this._settings.observe('history', this.setHistoryList.bind(this)); + } + + init() { + this.updatePanelMenuVisibility(); + this.setHistoryList(); + + // add to panel + Main.panel.addToStatusArea('random-wallpaper-menu', this.panelMenu); + } + + cleanup() { + this.clearHistoryList(); + this.panelMenu.destroy(); + + // remove all signal handlers + if (this._hidePanelIconHandler !== null) + this._settings.disconnect(this._hidePanelIconHandler); + } + + updatePanelMenuVisibility() { + if (this._settings.getBoolean('hide-panel-icon')) + this.panelMenu.hide(); + else + this.panelMenu.show(); + } + + setCurrentBackgroundElement() { + this._currentBackgroundSection.removeAll(); + + const historyController = this._wallpaperController.getHistoryController(); + const history = historyController.history; + + if (history.length > 0) { + const currentImage = new CustomElements.CurrentImageElement(undefined, history[0]); + this._currentBackgroundSection.addMenuItem(currentImage); + } + } + + setHistoryList() { + this._wallpaperController.update(); + this.setCurrentBackgroundElement(); + + const historyController = this._wallpaperController.getHistoryController(); + const history = historyController.history; + + if (history.length <= 1) { + this.clearHistoryList(); + return; + } + + /** + * @this {RandomWallpaperMenu} RandomWallpaperMenu + * @param {CustomElements.HistoryElement} actor The activating panel item + */ + // eslint-disable-next-line no-unused-vars + function onLeave(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { + this._wallpaperController.resetWallpaper(); + } + + /** + * @this {RandomWallpaperMenu} RandomWallpaperMenu + * @param {CustomElements.HistoryElement} actor The activating panel item + */ + function onEnter(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { + // @ts-expect-error Typing fails for GObject.registerClass + this._wallpaperController.previewWallpaper(actor.historyEntry.id); + } + + /** + * @this {RandomWallpaperMenu} RandomWallpaperMenu + * @param {CustomElements.HistoryElement} actor The activating panel item + */ + function onSelect(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { + // @ts-expect-error Typing fails for GObject.registerClass + this._wallpaperController.setWallpaper(actor.historyEntry.id); + } + + this._historySection.updateList(history, onEnter.bind(this), onLeave.bind(this), onSelect.bind(this)); + } + + clearHistoryList() { + this._historySection.clear(); + } +} + +export {RandomWallpaperMenu}; diff --git a/src/settings.ts b/src/settings.ts index c00da49e..c92d26b7 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,59 +1,165 @@ -const Gio = imports.gi.Gio; -const Utils = imports.misc.extensionUtils; - -var RWG_SETTINGS_SCHEMA = 'org.gnome.shell.extensions.space.iflow.randomwallpaper'; -var RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.backend-connection'; -var RWG_SETTINGS_SCHEMA_SOURCES_GENERAL = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.general'; -var RWG_SETTINGS_SCHEMA_SOURCES_GENERIC_JSON = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.genericJSON'; -var RWG_SETTINGS_SCHEMA_SOURCES_LOCAL_FOLDER = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.localFolder'; -var RWG_SETTINGS_SCHEMA_SOURCES_REDDIT = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.reddit'; -var RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.unsplash'; -var RWG_SETTINGS_SCHEMA_SOURCES_URL_SOURCE = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.urlSource'; -var RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.wallhaven'; - -var RWG_SETTINGS_SCHEMA_PATH = `/org/gnome/shell/extensions/space-iflow-randomwallpaper`; - -var Settings = class { - - /** - * Settings object. - * - * @param [schema] - * @private - */ - constructor(schema, path = null) { - this._settings = Utils.getSettings(schema, path); - } - - bind(keyName, gObject, property, settingsBindFlags) { - this._settings.bind(keyName, gObject, property, settingsBindFlags); - } - - disconnect(handler) { - return this._settings.disconnect(handler); - } - - get(key, type) { - return this._settings['get_' + type](key); - } - - getSchema() { - return this._settings.settings_schema; - } - - observe(key, callback) { - return this._settings.connect('changed::' + key, callback); - } - - reset(keyName) { - this._settings.reset(keyName); - } - - set(key, type, value) { - if (this._settings['set_' + type](key, value)) { - Gio.Settings.sync(); // wait for write - } else { - throw "Could not set " + key + " (type: " + type + ") with the value " + value; - } - } +import * as Gio from 'gi://Gio'; +import * as GObject from 'gi://GObject'; + +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +const Self = ExtensionUtils.getCurrentExtension(); + +const RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.backend-connection'; +const RWG_SETTINGS_SCHEMA_SOURCES_GENERAL = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.general'; +const RWG_SETTINGS_SCHEMA_SOURCES_GENERIC_JSON = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.genericJSON'; +const RWG_SETTINGS_SCHEMA_SOURCES_LOCAL_FOLDER = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.localFolder'; +const RWG_SETTINGS_SCHEMA_SOURCES_REDDIT = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.reddit'; +const RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.unsplash'; +const RWG_SETTINGS_SCHEMA_SOURCES_URL_SOURCE = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.urlSource'; +const RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN = 'org.gnome.shell.extensions.space.iflow.randomwallpaper.sources.wallhaven'; + +const RWG_SETTINGS_SCHEMA_PATH = '/org/gnome/shell/extensions/space-iflow-randomwallpaper'; + +class Settings { + private _settings: Gio.Settings; + + constructor(schemaId?: string, schemaPath?: string) { + if (schemaPath === undefined) { + this._settings = ExtensionUtils.getSettings(schemaId); + } else { + // We can't give a path so we need to rebuild the function: + const schemaObj = this._getSchema(schemaId); + + // Everything above for… this… + this._settings = new Gio.Settings({settings_schema: schemaObj, path: schemaPath}); + } + } + + bind(keyName: string, gObject: GObject.Object, property: string, settingsBindFlags: Gio.SettingsBindFlags) { + this._settings.bind(keyName, gObject, property, settingsBindFlags); + } + + disconnect(handler: number) { + return this._settings.disconnect(handler); + } + + getBoolean(key: string): boolean { + return this._settings.get_boolean(key); + } + + getEnum(key: string): number { + return this._settings.get_enum(key); + } + + getInt(key: string): number { + return this._settings.get_int(key); + } + + getInt64(key: string): number { + return this._settings.get_int64(key); + } + + getString(key: string): string { + return this._settings.get_string(key); + } + + getStrv(key: string): string[] { + return this._settings.get_strv(key); + } + + getSchema() { + return this._settings.settings_schema; + } + + isWritable(key: string) { + return this._settings.is_writable(key); + } + + listKeys() { + return this._settings.list_keys(); + } + + // eslint-disable-next-line no-unused-vars + observe(key: string, callback: (...args: any[]) => any) { + return this._settings.connect(`changed::${key}`, callback); + } + + reset(keyName: string) { + this._settings.reset(keyName); + } + + setBoolean(key: string, value: boolean) { + if (this._settings.set_boolean(key, value)) + this._save(); + else + throw new Error(`Could not set ${key} (type: boolean) with the value ${value}`); + } + + setEnum(key: string, value: number) { + if (this._settings.set_enum(key, value)) + this._save(); + else + throw new Error(`Could not set ${key} (type: number) with the value ${value}`); + } + + setInt64(key: string, value: number) { + if (this._settings.set_int64(key, value)) + this._save(); + else + throw new Error(`Could not set ${key} (type: number64) with the value ${value}`); + } + + setString(key: string, value: string) { + if (this._settings.set_string(key, value)) + this._save(); + else + throw new Error(`Could not set ${key} (type: string) with the value ${value}`); + } + + setStrv(key: string, value: string[]) { + if (this._settings.set_strv(key, value)) + this._save(); + else + throw new Error(`Could not set ${key} (type: string[]) with the value ${value}`); + } + + private _save() { + Gio.Settings.sync(); // Necessary: http://stackoverflow.com/questions/9985140 + } + + private _getSchema(schemaId?: string) { + // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/gnome-43/js/misc/extensionUtils.js#L211 + if (!schemaId) + schemaId = Self.metadata['settings-schema']; + + + // Expect USER extensions to have a schemas/ subfolder, otherwise assume a + // SYSTEM extension that has been installed in the same prefix as the shell + const schemaDir = Self.dir.get_child('schemas'); + let schemaSource; + const schemaPath = schemaDir.get_path(); + if (schemaDir.query_exists(null) && schemaPath !== null) { + schemaSource = Gio.SettingsSchemaSource.new_from_directory(schemaPath, + Gio.SettingsSchemaSource.get_default(), + false); + } else { + schemaSource = Gio.SettingsSchemaSource.get_default(); + } + + const schemaObj = schemaSource?.lookup(schemaId, true); + if (!schemaObj) + throw new Error(`Schema ${schemaId} could not be found for extension ${Self.metadata.uuid}. Please check your installation`); + + + return schemaObj; + } +} + +export { + Settings, + RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION, + RWG_SETTINGS_SCHEMA_PATH, + RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, + RWG_SETTINGS_SCHEMA_SOURCES_GENERIC_JSON, + RWG_SETTINGS_SCHEMA_SOURCES_LOCAL_FOLDER, + RWG_SETTINGS_SCHEMA_SOURCES_REDDIT, + RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH, + RWG_SETTINGS_SCHEMA_SOURCES_URL_SOURCE, + RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN }; diff --git a/src/soupBowl.ts b/src/soupBowl.ts index ebe542e0..8e4425dc 100644 --- a/src/soupBowl.ts +++ b/src/soupBowl.ts @@ -1,55 +1,63 @@ /** * A compatibility and convenience wrapper around the Soup API. + * + * libSoup is accessed through the SoupBowl wrapper to support libSoup3 and libSoup2.4 simultaneously in the extension + * runtime and in the preferences window. */ -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const LoggerModule = Self.imports.logger; +import * as Soup from 'gi://Soup'; -const _Soup = imports.gi.Soup; +import {Logger} from './logger.js'; -var Bowl = class { +class SoupBowl { + MessageFlags = Soup.MessageFlags; - Soup = _Soup; + private _logger = new Logger('RWG3', 'BaseAdapter'); + private _session = new Soup.Session(); - constructor() { - this.logger = new LoggerModule.Logger('RWG3', 'BaseAdapter'); - - this.session = new _Soup.Session(); - - if (_Soup.get_major_version() === 2) { - this.send_and_receive = this._send_and_receive_soup24; - } else if (_Soup.get_major_version() === 3) { - this.send_and_receive = this._send_and_receive_soup30; - } else { - this.logger.error("Unknown libsoup version"); - } + send_and_receive(soupMessage: Soup.Message): Promise { + if (Soup.get_major_version() === 2) + return this._send_and_receive_soup24(soupMessage); + else if (Soup.get_major_version() === 3) + return this._send_and_receive_soup30(soupMessage); + else + throw new Error('Unknown libsoup version'); } - /* stub */ - send_and_receive(soupMessage, callback) {}; - - _send_and_receive_soup24(soupMessage, callback) { - this.session.queue_message(soupMessage, (session, msg) => { - if (!msg.response_body) { - callback(null); - return; - } + newGetMessage(uri: string) { + return Soup.Message.new('GET', uri); + } - const response_body_bytes = msg.response_body.flatten().get_data(); - callback(response_body_bytes); + private _send_and_receive_soup24(soupMessage: Soup.Message): Promise { + return new Promise((resolve, reject) => { + // @ts-ignore Possibly wrong version here + this._session.queue_message(soupMessage, (session, msg) => { + if (!msg.response_body) { + reject(new Error('Message has no response body')); + return; + } + + const response_body_bytes = msg.response_body.flatten().get_data(); + resolve(response_body_bytes); + }); }); } - _send_and_receive_soup30(soupMessage, callback) { - this.session.send_and_read_async(soupMessage, 0, null, (session, message) => { - const res_data = session.send_and_read_finish(message); - if (!res_data) { - callback(null); - return; - } - - const response_body_bytes = res_data.get_data(); - callback(response_body_bytes); + private _send_and_receive_soup30(soupMessage: Soup.Message): Promise { + return new Promise((resolve, reject) => { + // @ts-ignore Possibly wrong version here + this._session.send_and_read_async(soupMessage, 0, null, (session: Soup.Session, message: Soup.Message) => { + // @ts-ignore Possibly wrong version here + const res_data = session.send_and_read_finish(message); + if (!res_data) { + reject(new Error('Message has no response body')); + return; + } + + const response_body_bytes = res_data.get_data(); + resolve(response_body_bytes); + }); }); } - } + +export {SoupBowl}; diff --git a/src/stylesheet.css b/src/stylesheet.css index 1b2b452a..3f7ae199 100644 --- a/src/stylesheet.css +++ b/src/stylesheet.css @@ -1,8 +1,8 @@ -.rwg-new-lable { +.rwg-new-label { font-size: 130%; } -.rwg-recent-lable { +.rwg-recent-label { font-size: 90%; } @@ -20,5 +20,3 @@ .rwg-history-time { font-size: 80%; } - -.rwg-history-element-content { } diff --git a/src/timer.ts b/src/timer.ts index 01ec2416..00b22c42 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -1,153 +1,135 @@ -const GLib = imports.gi.GLib; - -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const Prefs = Self.imports.settings; -const LoggerModule = Self.imports.logger; - -let _afTimerInstance = null; - -// Singleton implementation of _AFTimer -var AFTimer = function () { - if (!_afTimerInstance) { - _afTimerInstance = new _AFTimer(); - } - return _afTimerInstance; -}; - -var AFTimerDestroySingleton = function () { - // ensure timer is removed - if (_afTimerInstance != null) { - _afTimerInstance.cleanup(); - } - - // clear reference - _afTimerInstance = null; -} +import * as GLib from 'gi://GLib'; + +import {Logger} from './logger.js'; +import {Settings} from './settings.js'; /** * Timer for the auto fetch feature. */ -var _AFTimer = class { - - constructor() { - this.logger = new LoggerModule.Logger('RWG3', 'Timer'); - - this._timeout = null; - this._timoutEndCallback = null; - this._minutes = 30; - - this._settings = new Prefs.Settings(); - } - - isActive() { - return this._settings.get('auto-fetch', 'boolean'); - } - - remainingMinutes() { - let minutesElapsed = this._minutesElapsed(); - let remainder = minutesElapsed % this._minutes; - return Math.max(this._minutes - remainder, 0); - } - - registerCallback(callback) { - this._timoutEndCallback = callback; - } - - /** - * Sets the minutes of the timer. - * - * @param minutes - */ - setMinutes(minutes) { - this._minutes = minutes; - } - - /** - * Start the timer. - * - * @return void - */ - start() { - this.cleanup(); - - let last = this._settings.get('timer-last-trigger', 'int64'); - if (last === 0) { - this.reset(); - } - - let millisRemaining = this.remainingMinutes() * 60 * 1000; - - // set new wallpaper if the interval was surpassed and set the timestamp to when it should have been updated - if (this._surpassedInterval()) { - if (this._timoutEndCallback) { - this._timoutEndCallback(); - } - let millisOverdue = (this._minutes * 60 * 1000) - millisRemaining; - this._settings.set('timer-last-trigger', 'int64', Date.now() - millisOverdue); - } - - // actual timer function - this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, millisRemaining, () => { - if (this._timoutEndCallback) { - this._timoutEndCallback(); - } - - this.reset(); // reset timer - this.start(); // restart timer - }); - } - - /** - * Stop the timer. - * - * @return void - */ - stop() { - this._settings.set('timer-last-trigger', 'int64', 0); - this.cleanup(); - } - - /** - * Cleanup the timeout callback if it exists. - * - * @return void - */ - cleanup() { - if (this._timeout) { // only remove if a timeout is active - GLib.source_remove(this._timeout); - this._timeout = null; - } - } - - /** - * Reset the timer. - * - * @return void - */ - reset() { - this._settings.set('timer-last-trigger', 'int64', new Date().getTime()); - this.cleanup(); - } - - _minutesElapsed() { - let now = Date.now(); - let last = this._settings.get('timer-last-trigger', 'int64'); - - if (last === 0) { - return 0; - } - - let elapsed = Math.max(now - last, 0); - return Math.floor(elapsed / (60 * 1000)); - } - - _surpassedInterval() { - let now = Date.now(); - let last = this._settings.get('timer-last-trigger', 'int64'); - let diff = now - last; - let intervalLength = this._minutes * 60 * 1000; - - return diff > intervalLength; - } - -}; +class AFTimer { + private static _afTimerInstance?: AFTimer | null = null; + + private _logger = new Logger('RWG3', 'Timer'); + private _settings = new Settings(); + private _timeout?: number = undefined; + private _timeoutEndCallback?: () => void = undefined; + private _minutes = 30; + + static getTimer(): AFTimer { + if (!this._afTimerInstance) + this._afTimerInstance = new AFTimer(); + + return this._afTimerInstance; + } + + static destroy() { + if (this._afTimerInstance) + this._afTimerInstance.cleanup(); + + this._afTimerInstance = null; + } + + isActive() { + return this._settings.getBoolean('auto-fetch'); + } + + remainingMinutes() { + const minutesElapsed = this._minutesElapsed(); + const remainder = minutesElapsed % this._minutes; + return Math.max(this._minutes - remainder, 0); + } + + registerCallback(callback: () => void) { + this._timeoutEndCallback = callback; + } + + /** + * Sets the minutes of the timer. + * + * @param {number} minutes Number in minutes + */ + setMinutes(minutes: number) { + this._minutes = minutes; + } + + /** + * Start the timer. + */ + start() { + this.cleanup(); + + const last = this._settings.getInt64('timer-last-trigger'); + if (last === 0) + this.reset(); + + const millisecondsRemaining = this.remainingMinutes() * 60 * 1000; + + // set new wallpaper if the interval was surpassed and set the timestamp to when it should have been updated + if (this._surpassedInterval()) { + if (this._timeoutEndCallback) + this._timeoutEndCallback(); + + const millisecondsOverdue = (this._minutes * 60 * 1000) - millisecondsRemaining; + this._settings.setInt64('timer-last-trigger', Date.now() - millisecondsOverdue); + } + + // actual timer function + this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, millisecondsRemaining, () => { + if (this._timeoutEndCallback) + this._timeoutEndCallback(); + + this.reset(); // reset timer + this.start(); // restart timer + + return true; + }); + } + + /** + * Stop the timer. + */ + stop() { + this._settings.setInt64('timer-last-trigger', 0); + this.cleanup(); + } + + /** + * Cleanup the timeout callback if it exists. + */ + cleanup() { + if (this._timeout) { // only remove if a timeout is active + GLib.source_remove(this._timeout); + this._timeout = undefined; + } + } + + /** + * Reset the timer. + */ + reset() { + this._settings.setInt64('timer-last-trigger', new Date().getTime()); + this.cleanup(); + } + + private _minutesElapsed() { + const now = Date.now(); + const last: number = this._settings.getInt64('timer-last-trigger'); + + if (last === 0) + return 0; + + const elapsed = Math.max(now - last, 0); + return Math.floor(elapsed / (60 * 1000)); + } + + private _surpassedInterval() { + const now = Date.now(); + const last = this._settings.getInt64('timer-last-trigger'); + const diff = now - last; + const intervalLength = this._minutes * 60 * 1000; + + return diff > intervalLength; + } +} + +export {AFTimer}; diff --git a/src/ui/genericJson.blp b/src/ui/genericJson.blp index 3fdbcc52..4837e400 100644 --- a/src/ui/genericJson.blp +++ b/src/ui/genericJson.blp @@ -2,106 +2,106 @@ using Gtk 4.0; using Adw 1; template GenericJsonSettingsGroup : Adw.PreferencesGroup { - // title: _("Source Settings"); - description: _("This feature requires some know how. However, many different wallpaper providers can be used with this generic JSON source.\nYou have to specify an URL to a JSON response and a path to the target image URL within the JSON response.\nYou can also define a prefix that will be added to the image URL."); + // title: _("Source Settings"); + description: _("This feature requires some know how. However, many different wallpaper providers can be used with this generic JSON source.\nYou have to specify an URL to a JSON response and a path to the target image URL within the JSON response.\nYou can also define a prefix that will be added to the image URL."); - header-suffix: LinkButton { - valign: center; - uri: "https://github.com/ifl0w/RandomWallpaperGnome3/wiki/Generic-JSON-Source"; - - Adw.ButtonContent { - icon-name: "globe-symbolic"; - } - - styles [ - "flat", - ] - }; - - Adw.PreferencesGroup { - title: _("General"); - - Adw.EntryRow domain { - title: _("Domain"); - input-purpose: url; - - LinkButton { + header-suffix: LinkButton { valign: center; - uri: bind domain.text; + uri: "https://github.com/ifl0w/RandomWallpaperGnome3/wiki/Generic-JSON-Source"; Adw.ButtonContent { - icon-name: "globe-symbolic"; + icon-name: "globe-symbolic"; } styles [ - "flat", + "flat", ] - } - } + }; - Adw.EntryRow request_url { - title: _("Request URL"); - input-purpose: url; + Adw.PreferencesGroup { + title: _("General"); - LinkButton { - valign: center; - uri: bind request_url.text; + Adw.EntryRow domain { + title: _("Domain"); + input-purpose: url; - Adw.ButtonContent { - icon-name: "globe-symbolic"; + LinkButton { + valign: center; + uri: bind domain.text; + + Adw.ButtonContent { + icon-name: "globe-symbolic"; + } + + styles [ + "flat", + ] + } } - styles [ - "flat", - ] - } - } - } + Adw.EntryRow request_url { + title: _("Request URL"); + input-purpose: url; - Adw.PreferencesGroup { - title: _("Image"); + LinkButton { + valign: center; + uri: bind request_url.text; - Adw.EntryRow image_path { - title: _("JSON Path"); - input-purpose: free_form; - } + Adw.ButtonContent { + icon-name: "globe-symbolic"; + } - Adw.EntryRow image_prefix { - title: _("URL prefix"); - input-purpose: free_form; + styles [ + "flat", + ] + } + } } - } - Adw.PreferencesGroup { - title: _("Post"); + Adw.PreferencesGroup { + title: _("Image"); - Adw.EntryRow post_path { - title: _("JSON Path"); - input-purpose: free_form; - } + Adw.EntryRow image_path { + title: _("JSON Path"); + input-purpose: free_form; + } - Adw.EntryRow post_prefix { - title: _("URL Prefix"); - input-purpose: free_form; + Adw.EntryRow image_prefix { + title: _("URL prefix"); + input-purpose: free_form; + } } - } - Adw.PreferencesGroup { - title: _("Author"); + Adw.PreferencesGroup { + title: _("Post"); - Adw.EntryRow author_name_path { - title: _("Name JSON Path"); - input-purpose: free_form; - } + Adw.EntryRow post_path { + title: _("JSON Path"); + input-purpose: free_form; + } - Adw.EntryRow author_url_path { - title: _("URL JSON Path"); - input-purpose: free_form; + Adw.EntryRow post_prefix { + title: _("URL Prefix"); + input-purpose: free_form; + } } - Adw.EntryRow author_url_prefix { - title: _("URL URL prefix"); - input-purpose: free_form; + Adw.PreferencesGroup { + title: _("Author"); + + Adw.EntryRow author_name_path { + title: _("Name JSON Path"); + input-purpose: free_form; + } + + Adw.EntryRow author_url_path { + title: _("URL JSON Path"); + input-purpose: free_form; + } + + Adw.EntryRow author_url_prefix { + title: _("URL prefix"); + input-purpose: free_form; + } } - } } diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index 73e922b6..f221248b 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -1,80 +1,97 @@ -const Adw = imports.gi.Adw; -const ExtensionUtils = imports.misc.extensionUtils; -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; +import * as GObject from 'gi://GObject'; + +import * as Adw from '@gi/gtk4/adw/adw'; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as Settings from './../settings.js'; const Self = ExtensionUtils.getCurrentExtension(); -const Settings = Self.imports.settings; -var GenericJsonSettingsGroup = GObject.registerClass({ - GTypeName: 'GenericJsonSettingsGroup', - Template: GLib.filename_to_uri(Self.path + '/ui/genericJson.ui', null), - InternalChildren: [ - 'author_name_path', - 'author_url_path', - 'author_url_prefix', - 'domain', - 'image_path', - 'image_prefix', - 'post_path', - 'post_prefix', - 'request_url' - ] +const GenericJsonSettingsGroup = GObject.registerClass({ + GTypeName: 'GenericJsonSettingsGroup', + Template: GLib.filename_to_uri(`${Self.path}/ui/genericJson.ui`, null), + InternalChildren: [ + 'author_name_path', + 'author_url_path', + 'author_url_prefix', + 'domain', + 'image_path', + 'image_prefix', + 'post_path', + 'post_prefix', + 'request_url', + ], }, class GenericJsonSettingsGroup extends Adw.PreferencesGroup { - constructor(id, params = {}) { - super(params); + // InternalChildren + private _author_name_path!: Adw.EntryRow; + private _author_url_path!: Adw.EntryRow; + private _author_url_prefix!: Adw.EntryRow; + private _domain!: Adw.EntryRow; + private _image_path!: Adw.EntryRow; + private _image_prefix!: Adw.EntryRow; + private _post_path!: Adw.EntryRow; + private _post_prefix!: Adw.EntryRow; + private _request_url!: Adw.EntryRow; + + private _settings; - const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/genericJSON/${id}/`; - this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERIC_JSON, path); + constructor(params: any | undefined, id: string) { + super(params); - this._settings.bind('domain', - this._domain, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('request-url', - this._request_url, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('image-path', - this._image_path, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('image-prefix', - this._image_prefix, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('post-path', - this._post_path, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('post-prefix', - this._post_prefix, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('author-name-path', - this._author_name_path, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('author-url-path', - this._author_url_path, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('author-url-prefix', - this._author_url_prefix, - 'text', - Gio.SettingsBindFlags.DEFAULT); - } + const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/genericJSON/${id}/`; + this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERIC_JSON, path); - clearConfig() { - this._settings.reset('domain'); - this._settings.reset('request-url'); - this._settings.reset('image-path'); - this._settings.reset('image-prefix'); - this._settings.reset('post-path'); - this._settings.reset('post-prefix'); - this._settings.reset('author-name-path'); - this._settings.reset('author-url-path'); - this._settings.reset('author-url-prefix'); - } + this._settings.bind('domain', + this._domain, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('request-url', + this._request_url, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('image-path', + this._image_path, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('image-prefix', + this._image_prefix, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('post-path', + this._post_path, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('post-prefix', + this._post_prefix, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('author-name-path', + this._author_name_path, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('author-url-path', + this._author_url_path, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('author-url-prefix', + this._author_url_prefix, + 'text', + Gio.SettingsBindFlags.DEFAULT); + } + + clearConfig() { + this._settings.reset('author-name-path'); + this._settings.reset('author-url-path'); + this._settings.reset('author-url-prefix'); + this._settings.reset('domain'); + this._settings.reset('image-path'); + this._settings.reset('image-prefix'); + this._settings.reset('post-path'); + this._settings.reset('post-prefix'); + this._settings.reset('request-url'); + } }); + +export {GenericJsonSettingsGroup}; diff --git a/src/ui/localFolder.blp b/src/ui/localFolder.blp index 203e1f71..853bbbea 100644 --- a/src/ui/localFolder.blp +++ b/src/ui/localFolder.blp @@ -2,17 +2,17 @@ using Gtk 4.0; using Adw 1; template LocalFolderSettingsGroup : Adw.PreferencesGroup { - title: _("General"); + title: _("General"); - Adw.EntryRow folder_row { - title: _("Folder"); + Adw.EntryRow folder_row { + title: _("Folder"); - Button folder { - valign: center; + Button folder { + valign: center; - Adw.ButtonContent { - icon-name: "folder-open-symbolic"; - } + Adw.ButtonContent { + icon-name: "folder-open-symbolic"; + } + } } - } } diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index 353c370e..69658d43 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -1,60 +1,72 @@ -const Adw = imports.gi.Adw; -const ExtensionUtils = imports.misc.extensionUtils; -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; +import * as GObject from 'gi://GObject'; +import * as Gtk from 'gi://Gtk'; + +import * as Adw from '@gi/gtk4/adw/adw'; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as Settings from './../settings.js'; const Self = ExtensionUtils.getCurrentExtension(); -const Settings = Self.imports.settings; - -var LocalFolderSettingsGroup = GObject.registerClass({ - GTypeName: 'LocalFolderSettingsGroup', - Template: GLib.filename_to_uri(Self.path + '/ui/localFolder.ui', null), - InternalChildren: [ - 'folder', - 'folder_row' - ] + +const LocalFolderSettingsGroup = GObject.registerClass({ + GTypeName: 'LocalFolderSettingsGroup', + Template: GLib.filename_to_uri(`${Self.path}/ui/localFolder.ui`, null), + InternalChildren: [ + 'folder', + 'folder_row', + ], }, class LocalFolderSettingsGroup extends Adw.PreferencesGroup { - _saveDialog = null; - - constructor(id, params = {}) { - super(params); - - const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/localFolder/${id}/`; - this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_LOCAL_FOLDER, path); - - this._settings.bind('folder', - this._folder_row, - 'text', - Gio.SettingsBindFlags.DEFAULT); - - this._folder.connect('clicked', () => { - // For GTK 4.10+ - // Gtk.FileDialog(); - - // https://stackoverflow.com/a/54487948 - this._saveDialog = new Gtk.FileChooserNative({ - title: 'Choose a Wallpaper Folder', - action: Gtk.FileChooserAction.SELECT_FOLDER, - accept_label: 'Open', - cancel_label: 'Cancel', - transient_for: this.get_root(), - modal: true, - }); - - this._saveDialog.connect('response', (dialog, response_id) => { - if (response_id === Gtk.ResponseType.ACCEPT) { - this._folder_row.text = this._saveDialog.get_file().get_path(); - } - this._saveDialog.destroy(); - }); - - this._saveDialog.show(); - }); - } - - clearConfig() { - this._settings.reset('folder'); - } + // InternalChildren + private _folder!: Gtk.Button; + private _folder_row!: Adw.EntryRow; + + private _saveDialog: Gtk.FileChooserNative | undefined; + private _settings; + + constructor(params: any | undefined, id: string) { + super(params); + + const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/localFolder/${id}/`; + this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_LOCAL_FOLDER, path); + + this._settings.bind('folder', + this._folder_row, + 'text', + Gio.SettingsBindFlags.DEFAULT); + + this._folder.connect('clicked', () => { + // TODO: GTK 4.10+ + // Gtk.FileDialog(); + + // https://stackoverflow.com/a/54487948 + this._saveDialog = new Gtk.FileChooserNative({ + title: 'Choose a Wallpaper Folder', + action: Gtk.FileChooserAction.SELECT_FOLDER, + accept_label: 'Open', + cancel_label: 'Cancel', + transient_for: this.get_root() as Gtk.Window ?? undefined, + modal: true, + }); + + this._saveDialog.connect('response', (_dialog, response_id) => { + if (response_id === Gtk.ResponseType.ACCEPT) { + const chosenPath = _dialog.get_file()?.get_path(); + + if (chosenPath) + this._folder_row.text = chosenPath; + } + _dialog.destroy(); + }); + + this._saveDialog.show(); + }); + } + + clearConfig() { + this._settings.reset('folder'); + } }); + +export {LocalFolderSettingsGroup}; diff --git a/src/ui/pageGeneral.blp b/src/ui/pageGeneral.blp index f3394117..23fe6620 100644 --- a/src/ui/pageGeneral.blp +++ b/src/ui/pageGeneral.blp @@ -2,182 +2,182 @@ using Gtk 4.0; using Adw 1; Adw.PreferencesPage page_general { - title: _("General"); - icon-name: "preferences-system-symbolic"; - - Adw.PreferencesGroup { - Adw.ActionRow request_new_wallpaper { - title: _("Request New Wallpaper"); - activatable: true; - - styles [ - "suggested-action", - "title-3", - ] - - // I don't know how to center the title so just overwrite it with a label - child: Label { - label: _("Request New Wallpaper"); - height-request: 50; - }; - } - } - - Adw.PreferencesGroup { - title: _("General Settings"); - - Adw.ComboRow combo_background_type { - title: _("Change type"); - use-subtitle: true; - } - - Adw.ActionRow { - title: _("Hide the panel icon"); - subtitle: _("You won't be able to access the history and the settings through the panel menu. Enabling this option currently is only reasonable in conjunction with the Auto-Fetching feature.\nOnly enable this option if you know how to open the settings without the panel icon!"); - - Switch hide_panel_icon { - valign: center; - } + title: _("General"); + icon-name: "preferences-system-symbolic"; + + Adw.PreferencesGroup { + Adw.ActionRow request_new_wallpaper { + title: _("Request New Wallpaper"); + activatable: true; + + styles [ + "suggested-action", + "title-3", + ] + + // I don't know how to center the title so just overwrite it with a label + child: Label { + label: _("Request New Wallpaper"); + height-request: 50; + }; + } } - Adw.ActionRow { - title: _("Disable hover preview"); - subtitle: _("Disable the desktop preview of the background while hovering the history items. Try enabling if you encounter crashes or lags of the gnome-shell while using the extension."); - - Switch disable_hover_preview { - valign: center; - } - } + Adw.PreferencesGroup { + title: _("General Settings"); - Adw.EntryRow general_post_command { - title: _("Run post-command - available variables: %wallpaper_path%"); - } + Adw.ComboRow combo_background_type { + title: _("Change type"); + use-subtitle: true; + } - Adw.ActionRow multiple_displays_row { - title: _("Different wallpapers on multiple displays"); - subtitle: _("Requires HydraPaper.\nFills from History."); - sensitive: false; + Adw.ActionRow { + title: _("Hide the panel icon"); + subtitle: _("You won't be able to access the history and the settings through the panel menu. Enabling this option currently is only reasonable in conjunction with the Auto-Fetching feature.\nOnly enable this option if you know how to open the settings without the panel icon!"); - Switch enable_multiple_displays { - valign: center; - } - } - } + Switch hide_panel_icon { + valign: center; + } + } - Adw.PreferencesGroup { - title: _("History"); + Adw.ActionRow { + title: _("Disable hover preview"); + subtitle: _("Disable the desktop preview of the background while hovering the history items. Try enabling if you encounter crashes or lags of the gnome-shell while using the extension."); - header-suffix: Box { - spacing: 14; + Switch disable_hover_preview { + valign: center; + } + } - Button open_wallpaper_folder { - Adw.ButtonContent { - icon-name: "folder-open-symbolic"; - label: _("Open"); + Adw.EntryRow general_post_command { + title: _("Run post-command - available variables: %wallpaper_path%"); } - styles [ - "flat", - ] - } + Adw.ActionRow multiple_displays_row { + title: _("Different wallpapers on multiple displays"); + subtitle: _("Requires HydraPaper.\nFills from History."); + sensitive: false; - Button clear_history { - Adw.ButtonContent { - icon-name: "user-trash-symbolic"; - label: _("Delete"); + Switch enable_multiple_displays { + valign: center; + } } + } - styles [ - "destructive-action", - ] - } - }; - - Adw.ActionRow { - title: _("History length"); - subtitle: _("The number of wallpapers that will be shown in the history and stored in the wallpaper folder of this extension."); - - SpinButton { - valign: center; - numeric: true; - - adjustment: Adjustment history_length { - lower: 1; - upper: 100; - value: 10; - step-increment: 1; - page-increment: 10; + Adw.PreferencesGroup { + title: _("History"); + + header-suffix: Box { + spacing: 14; + + Button open_wallpaper_folder { + Adw.ButtonContent { + icon-name: "folder-open-symbolic"; + label: _("Open"); + } + + styles [ + "flat", + ] + } + + Button clear_history { + Adw.ButtonContent { + icon-name: "user-trash-symbolic"; + label: _("Delete"); + } + + styles [ + "destructive-action", + ] + } }; - } - } - Adw.EntryRow row_favorites_folder{ - title: _("Save for later folder"); + Adw.ActionRow { + title: _("History length"); + subtitle: _("The number of wallpapers that will be shown in the history and stored in the wallpaper folder of this extension."); + + SpinButton { + valign: center; + numeric: true; + + adjustment: Adjustment history_length { + lower: 1; + upper: 100; + value: 10; + step-increment: 1; + page-increment: 10; + }; + } + } - Button button_favorites_folder { - valign: center; + Adw.EntryRow row_favorites_folder{ + title: _("Save for later folder"); - Adw.ButtonContent { - icon-name: "folder-open-symbolic"; + Button button_favorites_folder { + valign: center; + + Adw.ButtonContent { + icon-name: "folder-open-symbolic"; + } + } } - } } - } - - Adw.PreferencesGroup { - title: "Auto-Fetching"; - - Adw.ExpanderRow af_switch { - title: _("Auto-Fetching"); - subtitle: _("Automatically fetch new wallpapers based on an interval."); - show-enable-switch: true; - - Adw.ActionRow { - title: _("Hours"); - - Scale duration_slider_hours { - draw-value: true; - orientation: horizontal; - hexpand: true; - digits: 0; - - adjustment: Adjustment duration_hours { - value: 1; - step-increment: 1; - page-increment: 10; - lower: 0; - upper: 23; - }; - } - } - - Adw.ActionRow { - title: _("Minutes"); - - Scale duration_slider_minutes { - draw-value: true; - orientation: horizontal; - hexpand: true; - digits: 0; - - adjustment: Adjustment duration_minutes { - value: 30; - step-increment: 1; - page-increment: 10; - lower: 1; - upper: 59; - }; + + Adw.PreferencesGroup { + title: "Auto-Fetching"; + + Adw.ExpanderRow af_switch { + title: _("Auto-Fetching"); + subtitle: _("Automatically fetch new wallpapers based on an interval."); + show-enable-switch: true; + + Adw.ActionRow { + title: _("Hours"); + + Scale duration_slider_hours { + draw-value: true; + orientation: horizontal; + hexpand: true; + digits: 0; + + adjustment: Adjustment duration_hours { + value: 1; + step-increment: 1; + page-increment: 10; + lower: 0; + upper: 23; + }; + } + } + + Adw.ActionRow { + title: _("Minutes"); + + Scale duration_slider_minutes { + draw-value: true; + orientation: horizontal; + hexpand: true; + digits: 0; + + adjustment: Adjustment duration_minutes { + value: 30; + step-increment: 1; + page-increment: 10; + lower: 1; + upper: 59; + }; + } + } } - } - } - Adw.ActionRow { - title: _("Fetch on startup (experimental)"); - subtitle: _("Fetch a new wallpaper during the startup of the extension. Rebooting your system, and enabling the extension will trigger a new wallpaper request.\nWARNING: Do not enable this feature if you observe crashes when requesting new wallpapers! This could render your system unstable as crashes could repeatedly happen on startup! In the case, you encounter such a problem, you will have to disable the extension or the feature manually from the commandline for your user."); + Adw.ActionRow { + title: _("Fetch on startup (experimental)"); + subtitle: _("Fetch a new wallpaper during the startup of the extension. Rebooting your system, and enabling the extension will trigger a new wallpaper request.\nWARNING: Do not enable this feature if you observe crashes when requesting new wallpapers! This could render your system unstable as crashes could repeatedly happen on startup! In the case, you encounter such a problem, you will have to disable the extension or the feature manually from the commandline for your user."); - Switch fetch_on_startup { - valign: center; - } + Switch fetch_on_startup { + valign: center; + } + } } - } } diff --git a/src/ui/pageSources.blp b/src/ui/pageSources.blp index d33da251..d17af06c 100644 --- a/src/ui/pageSources.blp +++ b/src/ui/pageSources.blp @@ -2,21 +2,21 @@ using Gtk 4.0; using Adw 1; Adw.PreferencesPage page_sources { - title: _("Wallpaper Sources"); - icon-name: "download-symbolic"; + title: _("Wallpaper Sources"); + icon-name: "download-symbolic"; - Adw.PreferencesGroup sources_list { - // title: _("Configured Wallpaper Sources"); + Adw.PreferencesGroup sources_list { + // title: _("Configured Wallpaper Sources"); - header-suffix: Button button_new_source { - styles [ - "suggested-action", - ] + header-suffix: Button button_new_source { + styles [ + "suggested-action", + ] - Adw.ButtonContent { - icon-name: "add-symbolic"; - label: _("Add Source"); - } - }; - } + Adw.ButtonContent { + icon-name: "add-symbolic"; + label: _("Add Source"); + } + }; + } } diff --git a/src/ui/reddit.blp b/src/ui/reddit.blp index 0ed64c54..c79f1cb7 100644 --- a/src/ui/reddit.blp +++ b/src/ui/reddit.blp @@ -2,82 +2,82 @@ using Gtk 4.0; using Adw 1; template RedditSettingsGroup : Adw.PreferencesGroup { - title: _("General"); + title: _("General"); - Adw.EntryRow subreddits { - title: _("Subreddits - e.g.: wallpaper, wallpapers, minimalwallpaper"); - } - - Adw.ActionRow { - title: _("Minimal resolution"); - - SpinButton { - valign: center; - numeric: true; - - adjustment: Adjustment min_width { - step-increment: 1; - page-increment: 10; - lower: 1; - upper: 1000000; - }; + Adw.EntryRow subreddits { + title: _("Subreddits - e.g.: wallpaper, wallpapers, minimalwallpaper"); } - Label { - label: "x"; + Adw.ActionRow { + title: _("Minimal resolution"); + + SpinButton { + valign: center; + numeric: true; + + adjustment: Adjustment min_width { + step-increment: 1; + page-increment: 10; + lower: 1; + upper: 1000000; + }; + } + + Label { + label: "x"; + } + + SpinButton { + valign: center; + numeric: true; + + adjustment: Adjustment min_height { + step-increment: 1; + page-increment: 10; + lower: 1; + upper: 1000000; + }; + } } - SpinButton { - valign: center; - numeric: true; - - adjustment: Adjustment min_height { - step-increment: 1; - page-increment: 10; - lower: 1; - upper: 1000000; - }; - } - } - - Adw.ActionRow { - title: _("Minimal image ratio"); - - SpinButton { - valign: center; - numeric: true; - - adjustment: Adjustment image_ratio1 { - step-increment: 1; - page-increment: 10; - lower: 1; - upper: 1000000; - }; - } - - Label { - label: ":"; - } - - SpinButton { - valign: center; - numeric: true; - - adjustment: Adjustment image_ratio2 { - step-increment: 1; - page-increment: 10; - lower: 1; - upper: 1000000; - }; + Adw.ActionRow { + title: _("Minimal image ratio"); + + SpinButton { + valign: center; + numeric: true; + + adjustment: Adjustment image_ratio1 { + step-increment: 1; + page-increment: 10; + lower: 1; + upper: 1000000; + }; + } + + Label { + label: ":"; + } + + SpinButton { + valign: center; + numeric: true; + + adjustment: Adjustment image_ratio2 { + step-increment: 1; + page-increment: 10; + lower: 1; + upper: 1000000; + }; + } } - } - Adw.ActionRow { - title: "SFW"; - subtitle: _("Safe for work"); + Adw.ActionRow { + title: "SFW"; + subtitle: _("Safe for work"); - Switch allow_sfw { - valign: center; + Switch allow_sfw { + valign: center; + } } - } } diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index 3fc7624d..e3de3850 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -1,62 +1,77 @@ -const Adw = imports.gi.Adw; -const ExtensionUtils = imports.misc.extensionUtils; -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; +import * as GObject from 'gi://GObject'; +import * as Gtk from 'gi://Gtk'; + +import * as Adw from '@gi/gtk4/adw/adw'; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as Settings from './../settings.js'; const Self = ExtensionUtils.getCurrentExtension(); -const Settings = Self.imports.settings; - -var RedditSettingsGroup = GObject.registerClass({ - GTypeName: 'RedditSettingsGroup', - Template: GLib.filename_to_uri(Self.path + '/ui/reddit.ui', null), - InternalChildren: [ - 'allow_sfw', - 'image_ratio1', - 'image_ratio2', - 'min_height', - 'min_width', - 'subreddits' - ] + +const RedditSettingsGroup = GObject.registerClass({ + GTypeName: 'RedditSettingsGroup', + Template: GLib.filename_to_uri(`${Self.path}/ui/reddit.ui`, null), + InternalChildren: [ + 'allow_sfw', + 'image_ratio1', + 'image_ratio2', + 'min_height', + 'min_width', + 'subreddits', + ], }, class RedditSettingsGroup extends Adw.PreferencesGroup { - constructor(id, params = {}) { - super(params); - - const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/reddit/${id}/`; - this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_REDDIT, path); - - this._settings.bind('allow-sfw', - this._allow_sfw, - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('image-ratio1', - this._image_ratio1, - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('image-ratio2', - this._image_ratio2, - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('min-height', - this._min_height, - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('min-width', - this._min_width, - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('subreddits', - this._subreddits, - 'text', - Gio.SettingsBindFlags.DEFAULT); - } - - clearConfig() { - this._settings.reset('allow-sfw'); - this._settings.reset('min-height'); - this._settings.reset('min-width'); - this._settings.reset('image-ratio1'); - this._settings.reset('image-ratio2'); - this._settings.reset('subreddits'); - } + // InternalChildren + private _allow_sfw!: Gtk.Switch; + private _image_ratio1!: Gtk.Adjustment; + private _image_ratio2!: Gtk.Adjustment; + private _min_height!: Gtk.Adjustment; + private _min_width!: Gtk.Adjustment; + private _subreddits!: Adw.EntryRow; + + private _settings; + + constructor(params: any | undefined, id: string) { + super(params); + + const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/reddit/${id}/`; + this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_REDDIT, path); + + this._settings.bind('allow-sfw', + this._allow_sfw, + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('image-ratio1', + this._image_ratio1, + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('image-ratio2', + this._image_ratio2, + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('min-height', + this._min_height, + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('min-width', + this._min_width, + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('subreddits', + this._subreddits, + 'text', + Gio.SettingsBindFlags.DEFAULT); + } + + clearConfig() { + this._settings.reset('allow-sfw'); + this._settings.reset('image-ratio1'); + this._settings.reset('image-ratio2'); + this._settings.reset('min-height'); + this._settings.reset('min-width'); + this._settings.reset('subreddits'); + } }); + +export {RedditSettingsGroup}; diff --git a/src/ui/sourceRow.blp b/src/ui/sourceRow.blp index b6b4a0c3..289cedc6 100644 --- a/src/ui/sourceRow.blp +++ b/src/ui/sourceRow.blp @@ -2,69 +2,69 @@ using Gtk 4.0; using Adw 1; template SourceRow : Adw.ExpanderRow { - title: bind source_name.text; - show-enable-switch: true; + title: bind source_name.text; + show-enable-switch: true; - // Doesn't look good and prone to missclicks - // [action] - // Button button_delete { - // valign: center; + // Doesn't look good and prone to missclicks + // [action] + // Button button_delete { + // valign: center; - // styles [ - // "destructive-action", - // ] + // styles [ + // "destructive-action", + // ] - // Adw.ButtonContent { - // icon-name: "user-trash-symbolic"; - // valign: center; - // } - // } + // Adw.ButtonContent { + // icon-name: "user-trash-symbolic"; + // valign: center; + // } + // } - Box { - orientation: vertical; - spacing: 14; + Box { + orientation: vertical; + spacing: 14; - Adw.Clamp { - Adw.PreferencesGroup { - title: _("Meta"); + Adw.Clamp { + Adw.PreferencesGroup { + title: _("Meta"); - Adw.EntryRow source_name { - title: _("Name"); - input-purpose: free_form; - text: _("My Source - (1080p)"); - } + Adw.EntryRow source_name { + title: _("Name"); + input-purpose: free_form; + text: _("My Source - (1080p)"); + } - Adw.ComboRow combo { - title: _("Type"); - } + Adw.ComboRow combo { + title: _("Type"); + } - Adw.ActionRow { - title: _("Delete this source"); + Adw.ActionRow { + title: _("Delete this source"); - Button button_delete { - valign: center; + Button button_delete { + valign: center; - styles [ - "destructive-action", - ] + styles [ + "destructive-action", + ] - Adw.ButtonContent { - icon-name: "user-trash-symbolic"; - valign: center; - } - } - } + Adw.ButtonContent { + icon-name: "user-trash-symbolic"; + valign: center; + } + } + } - Adw.ExpanderRow blocked_images_list { - title: _("Blocked Images"); - sensitive: false; + Adw.ExpanderRow blocked_images_list { + title: _("Blocked Images"); + sensitive: false; + } + } } - } - } - Adw.Clamp settings_container { } + Adw.Clamp settings_container { } - // FIXME: Additional PreferencesGroup solely for spacing to the next row when expanded - Adw.PreferencesGroup { } - } + // FIXME: Additional PreferencesGroup solely for spacing to the next row when expanded + Adw.PreferencesGroup { } + } } diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 9a107612..28577147 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -1,165 +1,184 @@ -const Adw = imports.gi.Adw; -const ExtensionUtils = imports.misc.extensionUtils; -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; +import * as GObject from 'gi://GObject'; +import * as Gtk from 'gi://Gtk'; -const Self = ExtensionUtils.getCurrentExtension(); -const Settings = Self.imports.settings; -const Utils = Self.imports.utils; +import * as Adw from '@gi/gtk4/adw/adw'; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as Settings from './../settings.js'; +import * as Utils from './../utils.js'; + +import {Logger} from './../logger.js'; -const GenericJson = Self.imports.ui.genericJson; -const LocalFolder = Self.imports.ui.localFolder; -const Reddit = Self.imports.ui.reddit; -const Unsplash = Self.imports.ui.unsplash; -const UrlSource = Self.imports.ui.urlSource; -const Wallhaven = Self.imports.ui.wallhaven; +import {GenericJsonSettingsGroup} from './genericJson.js'; +import {LocalFolderSettingsGroup} from './localFolder.js'; +import {RedditSettingsGroup} from './reddit.js'; +import {UnsplashSettingsGroup} from './unsplash.js'; +import {UrlSourceSettingsGroup} from './urlSource.js'; +import {WallhavenSettingsGroup} from './wallhaven.js'; + +const Self = ExtensionUtils.getCurrentExtension(); // https://gitlab.gnome.org/GNOME/gjs/-/blob/master/examples/gtk4-template.js -var SourceRow = GObject.registerClass({ - GTypeName: 'SourceRow', - Template: GLib.filename_to_uri(Self.path + '/ui/sourceRow.ui', null), - Children: [ - 'button_delete' - ], - InternalChildren: [ - 'blocked_images_list', - 'combo', - 'settings_container', - 'source_name' - ] +const SourceRow = GObject.registerClass({ + GTypeName: 'SourceRow', + Template: GLib.filename_to_uri(`${Self.path}/ui/sourceRow.ui`, null), + Children: [ + 'button_delete', + ], + InternalChildren: [ + 'blocked_images_list', + 'combo', + 'settings_container', + 'source_name', + ], }, class SourceRow extends Adw.ExpanderRow { - // This list is the same across all rows - static _stringList = null; - - constructor(id = null, params = {}) { - super(params); - - if (id === null) { - // New row - this.id = Date.now(); - } else { - this.id = id; - } - - const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${this.id}/`; - this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); - - if (this._stringList === null || this._stringList === undefined) { - // Fill combo from settings enum - - let availableTypes = this._settings.getSchema().get_key('type').get_range(); //GLib.Variant (sv) - // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) - // s should be 'enum' - // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. - // v is 'as' - availableTypes = availableTypes.get_child_value(1).get_variant().get_strv(); - - this._stringList = Gtk.StringList.new(availableTypes); - } - this._combo.model = this._stringList; - this._combo.selected = this._settings.get('type', 'enum'); - - this._settings.bind('name', - this._source_name, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('enabled', - this, - 'enable-expansion', - Gio.SettingsBindFlags.DEFAULT); - // Binding an enum isn't possible straight away. - // This would need bind_with_mapping() which isn't available in gjs? - // this._settings.bind('type', - // this._combo, - // 'selected', - // Gio.SettingsBindFlags.DEFAULT); - - this._combo.connect('notify::selected', comboRow => { - this._settings.set('type', 'enum', comboRow.selected); - this._fillRow(comboRow.selected); - }); - - this._fillRow(this._combo.selected); - - let blockedImages = this._settings.get('blocked-images', 'strv'); - blockedImages.forEach(filename => { - let blockedImageRow = new Adw.ActionRow(); - blockedImageRow.set_title(filename); - - let button = new Gtk.Button(); - button.set_valign(Gtk.Align.CENTER); - button.connect('clicked', () => { - this._removeBlockedImage(filename); - this._blocked_images_list.remove(blockedImageRow); - }); - - let buttonContent = new Adw.ButtonContent(); - buttonContent.set_icon_name("user-trash-symbolic") - - button.set_child(buttonContent); - blockedImageRow.add_suffix(button); - this._blocked_images_list.add_row(blockedImageRow); - this._blocked_images_list.set_sensitive(true); - }); - } - - _fillRow(type) { - let targetWidget = this._getSettingsGroup(type); - if (targetWidget !== null) { - this._settings_container.set_child(targetWidget); - } - } - - _getSettingsGroup(type = 0) { - let targetWidget; - switch (type) { - case 0: // unsplash - targetWidget = new Unsplash.UnsplashSettingsGroup(this.id); - break; - case 1: // wallhaven - targetWidget = new Wallhaven.WallhavenSettingsGroup(this.id); - break; - case 2: // reddit - targetWidget = new Reddit.RedditSettingsGroup(this.id); - break; - case 3: // generic JSON - targetWidget = new GenericJson.GenericJsonSettingsGroup(this.id); - break; - case 4: // Local Folder - targetWidget = new LocalFolder.LocalFolderSettingsGroup(this.id); - break; - case 5: // Static URL - targetWidget = new UrlSource.UrlSourceSettingsGroup(this.id); - break; - default: - targetWidget = null; - this.logger.error("The selected source has no corresponding widget!") - break; - } - return targetWidget; - } - - _removeBlockedImage(filename) { - let blockedImages = this._settings.get('blocked-images', 'strv'); - if (!blockedImages.includes(filename)) { - return; - } - - blockedImages = Utils.Utils.removeItemOnce(blockedImages, filename); - this._settings.set('blocked-images', 'strv', blockedImages); - } - - clearConfig() { - for (const i of Array(6).keys()) { - let widget = this._getSettingsGroup(i); - widget.clearConfig(); - } - - this._settings.reset('blocked-images'); - this._settings.reset('enabled'); - this._settings.reset('name'); - this._settings.reset('type'); - } + // This list is the same across all rows + static _stringList: Gtk.StringList; + + // Children + button_delete!: Gtk.Button; + + // InternalChildren + private _blocked_images_list!: Adw.ExpanderRow; + private _combo!: Adw.ComboRow; + private _settings_container!: Adw.Clamp; + private _source_name!: Adw.EntryRow; + + private _settings; + private _logger = new Logger('RWG3', 'SourceRow'); + + id = String(Date.now()); + + constructor(params: object | undefined, id?: string | null) { + super(params); + + if (id) + this.id = id; + + const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${this.id}/`; + this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); + + if (!SourceRow._stringList) { + // Fill combo from settings enum + + const availableTypes = this._settings.getSchema().get_key('type').get_range(); // GLib.Variant (sv) + // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) + // s should be 'enum' + // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. + // v is 'as' + const availableTypeNames = availableTypes.get_child_value(1).get_variant().get_strv(); + + SourceRow._stringList = Gtk.StringList.new(availableTypeNames); + } + this._combo.model = SourceRow._stringList; + this._combo.selected = this._settings.getEnum('type'); + + this._settings.bind('name', + this._source_name, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('enabled', + this, + 'enable-expansion', + Gio.SettingsBindFlags.DEFAULT); + // Binding an enum isn't possible straight away. + // This would need bind_with_mapping() which isn't available in gjs? + // this._settings.bind('type', + // this._combo, + // 'selected', + // Gio.SettingsBindFlags.DEFAULT); + + this._combo.connect('notify::selected', (comboRow: Adw.ComboRow) => { + this._settings.setEnum('type', comboRow.selected); + this._fillRow(comboRow.selected); + }); + + this._fillRow(this._combo.selected); + + const blockedImages: string[] = this._settings.getStrv('blocked-images'); + blockedImages.forEach(filename => { + const blockedImageRow = new Adw.ActionRow(); + blockedImageRow.set_title(filename); + + const button = new Gtk.Button(); + button.set_valign(Gtk.Align.CENTER); + button.connect('clicked', () => { + this._removeBlockedImage(filename); + this._blocked_images_list.remove(blockedImageRow); + }); + + const buttonContent = new Adw.ButtonContent(); + buttonContent.set_icon_name('user-trash-symbolic'); + + button.set_child(buttonContent); + blockedImageRow.add_suffix(button); + this._blocked_images_list.add_row(blockedImageRow); + this._blocked_images_list.set_sensitive(true); + }); + } + + private _fillRow(type: number) { + let targetWidget = this._getSettingsGroup(type); + if (targetWidget !== null) + this._settings_container.set_child(targetWidget); + } + + private _getSettingsGroup(type = 0) { + let targetWidget = null; + switch (type) { + case 0: // unsplash + targetWidget = new UnsplashSettingsGroup(undefined, this.id); + break; + case 1: // wallhaven + targetWidget = new WallhavenSettingsGroup(undefined, this.id); + break; + case 2: // reddit + targetWidget = new RedditSettingsGroup(undefined, this.id); + break; + case 3: // generic JSON + targetWidget = new GenericJsonSettingsGroup(undefined, this.id); + break; + case 4: // Local Folder + targetWidget = new LocalFolderSettingsGroup(undefined, this.id); + break; + case 5: // Static URL + targetWidget = new UrlSourceSettingsGroup(undefined, this.id); + break; + default: + targetWidget = null; + this._logger.error('The selected source has no corresponding widget!'); + break; + } + return targetWidget; + } + + private _removeBlockedImage(filename: string) { + let blockedImages = this._settings.getStrv('blocked-images'); + if (!blockedImages.includes(filename)) + return; + + + blockedImages = Utils.removeItemOnce(blockedImages, filename); + this._settings.setStrv('blocked-images', blockedImages); + } + + /** + * Clear all keys associated to this ID across all adapter + */ + clearConfig() { + for (const i of Array(6).keys()) { + const widget = this._getSettingsGroup(i); + if (widget) + widget.clearConfig(); + } + + this._settings.reset('blocked-images'); + this._settings.reset('enabled'); + this._settings.reset('name'); + this._settings.reset('type'); + } }); + +export {SourceRow}; diff --git a/src/ui/unsplash.blp b/src/ui/unsplash.blp index de5ff4dc..ab5dde95 100644 --- a/src/ui/unsplash.blp +++ b/src/ui/unsplash.blp @@ -2,65 +2,65 @@ using Gtk 4.0; using Adw 1; template UnsplashSettingsGroup : Adw.PreferencesGroup { - title: _("General"); + title: _("General"); - Adw.EntryRow keyword { - title: _("Keywords - Comma seperated"); - input-purpose: free_form; - } + Adw.EntryRow keyword { + title: _("Keywords - Comma seperated"); + input-purpose: free_form; + } - Adw.ActionRow { - title: _("Only Featured Images"); - subtitle: _("This option results in a smaller image pool, but the images are considered to be of higher quality."); + Adw.ActionRow { + title: _("Only Featured Images"); + subtitle: _("This option results in a smaller image pool, but the images are considered to be of higher quality."); - Switch featured_only { - valign: center; + Switch featured_only { + valign: center; + } } - } - Adw.ActionRow { - title: _("Image Dimensions"); + Adw.ActionRow { + title: _("Image Dimensions"); - SpinButton { - valign: center; - numeric: true; + SpinButton { + valign: center; + numeric: true; - adjustment: Adjustment image_width { - step-increment: 1; - page-increment: 10; - lower: 1; - upper: 1000000; - }; - } + adjustment: Adjustment image_width { + step-increment: 1; + page-increment: 10; + lower: 1; + upper: 1000000; + }; + } - Label { - label: "x"; - } + Label { + label: "x"; + } - SpinButton { - valign: center; - numeric: true; + SpinButton { + valign: center; + numeric: true; - adjustment: Adjustment image_height { - step-increment: 1; - page-increment: 10; - lower: 1; - upper: 1000000; - }; + adjustment: Adjustment image_height { + step-increment: 1; + page-increment: 10; + lower: 1; + upper: 1000000; + }; + } } - } - Adw.PreferencesGroup { - title: _("Contraint"); + Adw.PreferencesGroup { + title: _("Contraint"); - // TODO: ExpanderRow with switch? - // Nested ExpanderRows behave strangely - Adw.ComboRow constraint_type { - title: _("Type"); - } + // TODO: ExpanderRow with switch? + // Nested ExpanderRows behave strangely + Adw.ComboRow constraint_type { + title: _("Type"); + } - Adw.EntryRow constraint_value { - title: _("Value"); + Adw.EntryRow constraint_value { + title: _("Value"); + } } - } } diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index b3807325..c5bdc140 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -1,102 +1,112 @@ -const Adw = imports.gi.Adw; -const ExtensionUtils = imports.misc.extensionUtils; -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; +import * as GObject from 'gi://GObject'; +import * as Gtk from 'gi://Gtk'; + +import * as Adw from '@gi/gtk4/adw/adw'; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as Settings from './../settings.js'; const Self = ExtensionUtils.getCurrentExtension(); -const Settings = Self.imports.settings; - -var UnsplashSettingsGroup = GObject.registerClass({ - GTypeName: 'UnsplashSettingsGroup', - Template: GLib.filename_to_uri(Self.path + '/ui/unsplash.ui', null), - InternalChildren: [ - 'constraint_type', - 'constraint_value', - 'featured_only', - 'image_height', - 'image_width', - 'keyword' - ] + +const UnsplashSettingsGroup = GObject.registerClass({ + GTypeName: 'UnsplashSettingsGroup', + Template: GLib.filename_to_uri(`${Self.path}/ui/unsplash.ui`, null), + InternalChildren: [ + 'constraint_type', + 'constraint_value', + 'featured_only', + 'image_height', + 'image_width', + 'keyword', + ], }, class UnsplashSettingsGroup extends Adw.PreferencesGroup { - // This list is the same across all rows - static _stringList = null; - - constructor(id, params = {}) { - super(params); - - const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id}/`; - this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH, path); - - if (this._stringList === null || this._stringList === undefined) { - // Fill combo from settings enum - - let availableTypes = this._settings.getSchema().get_key('constraint-type').get_range(); //GLib.Variant (sv) - // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) - // s should be 'enum' - // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. - // v is 'as' - availableTypes = availableTypes.get_child_value(1).get_variant().get_strv(); - - this._stringList = Gtk.StringList.new(availableTypes); - } - - this._constraint_type.model = this._stringList; - this._constraint_type.selected = this._settings.get('constraint-type', 'enum'); - - this._settings.bind('keyword', - this._keyword, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('image-width', - this._image_width, - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('image-height', - this._image_height, - 'value', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('featured-only', - this._featured_only, - 'active', - Gio.SettingsBindFlags.DEFAULT); - // Binding an enum isn't possible straight away. - // This would need bind_with_mapping() which isn't available in gjs? - // this._settings.bind('constraint-type', - // this._constraint_type, - // 'selected', - // Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('constraint-value', - this._constraint_value, - 'text', - Gio.SettingsBindFlags.DEFAULT); - - this._unsplashUnconstrained(this._constraint_type, true, this._featured_only); - this._unsplashUnconstrained(this._constraint_type, false, this._constraint_value); - this._constraint_type.connect('notify::selected', (comboRow) => { - this._unsplashUnconstrained(comboRow, true, this._featured_only); - this._unsplashUnconstrained(comboRow, false, this._constraint_value); - this._settings.set('constraint-type', 'enum', comboRow.selected); - - this._featured_only.set_active(false); - }); - } - - _unsplashUnconstrained(comboRow, enable, targetElement) { - if (comboRow.selected === 0) { - targetElement.set_sensitive(enable); - } else { - targetElement.set_sensitive(!enable); - } - } - - clearConfig() { - this._settings.reset('keyword'); - this._settings.reset('image-width'); - this._settings.reset('image-height'); - this._settings.reset('featured-only'); - this._settings.reset('constraint-type'); - this._settings.reset('constraint-value'); - } + // This list is the same across all rows + static _stringList: Gtk.StringList; + + // InternalChildren + private _constraint_type!: Adw.ComboRow; + private _constraint_value!: Adw.EntryRow; + private _featured_only!: Gtk.Switch; + private _image_height!: Gtk.Adjustment; + private _image_width!: Gtk.Adjustment; + private _keyword!: Adw.EntryRow; + + private _settings; + + constructor(params: any | undefined, id: string) { + super(params); + + const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id}/`; + this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH, path); + + if (!UnsplashSettingsGroup._stringList) { + // Fill combo from settings enum + + const availableTypes = this._settings.getSchema().get_key('constraint-type').get_range(); // GLib.Variant (sv) + // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) + // s should be 'enum' + // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. + // v is 'as' + const availableTypeNames = availableTypes.get_child_value(1).get_variant().get_strv(); + + UnsplashSettingsGroup._stringList = Gtk.StringList.new(availableTypeNames); + } + + this._constraint_type.model = UnsplashSettingsGroup._stringList; + this._constraint_type.selected = this._settings.getEnum('constraint-type'); + + this._settings.bind('keyword', + this._keyword, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('image-width', + this._image_width, + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('image-height', + this._image_height, + 'value', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('featured-only', + this._featured_only, + 'active', + Gio.SettingsBindFlags.DEFAULT); + + // Binding an enum isn't possible straight away. + // This would need bind_with_mapping() which isn't available in gjs? + this._settings.bind('constraint-value', + this._constraint_value, + 'text', + Gio.SettingsBindFlags.DEFAULT); + + this._unsplashUnconstrained(this._constraint_type, true, this._featured_only); + this._unsplashUnconstrained(this._constraint_type, false, this._constraint_value); + this._constraint_type.connect('notify::selected', (comboRow: Adw.ComboRow) => { + this._unsplashUnconstrained(comboRow, true, this._featured_only); + this._unsplashUnconstrained(comboRow, false, this._constraint_value); + this._settings.setEnum('constraint-type', comboRow.selected); + + this._featured_only.set_active(false); + }); + } + + private _unsplashUnconstrained(comboRow: Adw.ComboRow, enable: boolean, targetElement: Gtk.Widget) { + if (comboRow.selected === 0) + targetElement.set_sensitive(enable); + else + targetElement.set_sensitive(!enable); + } + + clearConfig() { + this._settings.reset('constraint-type'); + this._settings.reset('constraint-value'); + this._settings.reset('featured-only'); + this._settings.reset('image-height'); + this._settings.reset('image-width'); + this._settings.reset('keyword'); + } }); + +export {UnsplashSettingsGroup}; diff --git a/src/ui/urlSource.blp b/src/ui/urlSource.blp index f1aff9fa..8a0d2ceb 100644 --- a/src/ui/urlSource.blp +++ b/src/ui/urlSource.blp @@ -2,74 +2,74 @@ using Gtk 4.0; using Adw 1; template UrlSourceSettingsGroup : Adw.PreferencesGroup { - Adw.PreferencesGroup { - title: _("General"); + Adw.PreferencesGroup { + title: _("General"); - Adw.EntryRow domain { - title: _("Domain"); - input-purpose: url; + Adw.EntryRow domain { + title: _("Domain"); + input-purpose: url; - LinkButton { - valign: center; - uri: bind domain.text; + LinkButton { + valign: center; + uri: bind domain.text; - Adw.ButtonContent { - icon-name: "globe-symbolic"; + Adw.ButtonContent { + icon-name: "globe-symbolic"; + } + + styles [ + "flat", + ] + } } - styles [ - "flat", - ] - } - } + Adw.EntryRow image_url { + title: _("Image URL"); - Adw.EntryRow image_url { - title: _("Image URL"); + LinkButton { + valign: center; + uri: bind image_url.text; - LinkButton { - valign: center; - uri: bind image_url.text; + Adw.ButtonContent { + icon-name: "globe-symbolic"; + } - Adw.ButtonContent { - icon-name: "globe-symbolic"; + styles [ + "flat", + ] + } } - styles [ - "flat", - ] - } - } + Adw.EntryRow post_url { + title: _("Post URL"); + input-purpose: free_form; - Adw.EntryRow post_url { - title: _("Post URL"); - input-purpose: free_form; + LinkButton { + valign: center; + uri: bind post_url.text; - LinkButton { - valign: center; - uri: bind post_url.text; + Adw.ButtonContent { + icon-name: "globe-symbolic"; + } - Adw.ButtonContent { - icon-name: "globe-symbolic"; + styles [ + "flat", + ] + } } - - styles [ - "flat", - ] - } } - } - Adw.PreferencesGroup { - title: _("Author"); + Adw.PreferencesGroup { + title: _("Author"); - Adw.EntryRow author_name { - title: _("Name"); - input-purpose: free_form; - } + Adw.EntryRow author_name { + title: _("Name"); + input-purpose: free_form; + } - Adw.EntryRow author_url { - title: _("URL"); - input-purpose: free_form; + Adw.EntryRow author_url { + title: _("URL"); + input-purpose: free_form; + } } - } } diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index 05e95aca..83e3a4cd 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -1,24 +1,35 @@ -const Adw = imports.gi.Adw; -const ExtensionUtils = imports.misc.extensionUtils; -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; +import * as GObject from 'gi://GObject'; + +import * as Adw from '@gi/gtk4/adw/adw'; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as Settings from './../settings.js'; const Self = ExtensionUtils.getCurrentExtension(); -const Settings = Self.imports.settings; -var UrlSourceSettingsGroup = GObject.registerClass({ +const UrlSourceSettingsGroup = GObject.registerClass({ GTypeName: 'UrlSourceSettingsGroup', - Template: GLib.filename_to_uri(Self.path + '/ui/urlSource.ui', null), + Template: GLib.filename_to_uri(`${Self.path}/ui/urlSource.ui`, null), InternalChildren: [ 'author_name', 'author_url', 'domain', 'image_url', 'post_url', - ] + ], }, class UrlSourceSettingsGroup extends Adw.PreferencesGroup { - constructor(id, params = {}) { + // InternalChildren + private _author_name!: Adw.EntryRow; + private _author_url!: Adw.EntryRow; + private _domain!: Adw.EntryRow; + private _image_url!: Adw.EntryRow; + private _post_url!: Adw.EntryRow; + + private _settings; + + constructor(params: any | undefined, id: string) { super(params); const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/urlSource/${id}/`; @@ -54,3 +65,5 @@ var UrlSourceSettingsGroup = GObject.registerClass({ this._settings.reset('post-url'); } }); + +export {UrlSourceSettingsGroup}; diff --git a/src/ui/wallhaven.blp b/src/ui/wallhaven.blp index 0ba3f901..d8c2c620 100644 --- a/src/ui/wallhaven.blp +++ b/src/ui/wallhaven.blp @@ -2,123 +2,123 @@ using Gtk 4.0; using Adw 1; template WallhavenSettingsGroup : Adw.PreferencesGroup { - // title: _("Source Settings"); + // title: _("Source Settings"); - Adw.PreferencesGroup { - title: _("General"); + Adw.PreferencesGroup { + title: _("General"); - Adw.EntryRow keyword { - title: _("Keywords - Comma seperated"); - input-purpose: free_form; - } + Adw.EntryRow keyword { + title: _("Keywords - Comma seperated"); + input-purpose: free_form; + } - Adw.PasswordEntryRow api_key { - title: _("API key"); - input-purpose: password; + Adw.PasswordEntryRow api_key { + title: _("API key"); + input-purpose: password; - LinkButton { - valign: center; - uri: "https://wallhaven.cc/settings/account"; + LinkButton { + valign: center; + uri: "https://wallhaven.cc/settings/account"; - Adw.ButtonContent { - icon-name: "globe-symbolic"; - } + Adw.ButtonContent { + icon-name: "globe-symbolic"; + } - styles [ - "flat", - ] - } - } + styles [ + "flat", + ] + } + } - Adw.EntryRow resolutions { - title: _("Resolutions: 1920x1080, 2560x1440"); - input-purpose: free_form; - text: ""; - } + Adw.EntryRow resolutions { + title: _("Resolutions: 1920x1080, 2560x1440"); + input-purpose: free_form; + text: ""; + } - Adw.ActionRow row_color { - title: _("Search by color"); - subtitle: ""; + Adw.ActionRow row_color { + title: _("Search by color"); + subtitle: ""; - Box { - Button button_color_undo { - valign: center; + Box { + Button button_color_undo { + valign: center; - styles [ - "flat", - ] + styles [ + "flat", + ] - Adw.ButtonContent { - icon-name: "edit-undo-symbolic"; - } - } + Adw.ButtonContent { + icon-name: "edit-undo-symbolic"; + } + } - Button button_color { - valign: center; + Button button_color { + valign: center; - Adw.ButtonContent { - icon-name: "color-select-symbolic"; - } + Adw.ButtonContent { + icon-name: "color-select-symbolic"; + } + } + } } - } } - } - Adw.PreferencesGroup { - title: _("Allowed content ratings"); + Adw.PreferencesGroup { + title: _("Allowed content ratings"); - Adw.ActionRow { - title: "SFW"; - subtitle: _("Safe for work"); + Adw.ActionRow { + title: "SFW"; + subtitle: _("Safe for work"); - Switch allow_sfw { - valign: center; - } - } + Switch allow_sfw { + valign: center; + } + } - Adw.ActionRow { - title: "Sketchy"; + Adw.ActionRow { + title: "Sketchy"; - Switch allow_sketchy { - valign: center; - } - } + Switch allow_sketchy { + valign: center; + } + } - Adw.ActionRow { - title: "NSFW"; - subtitle: _("Not safe for work"); + Adw.ActionRow { + title: "NSFW"; + subtitle: _("Not safe for work"); - Switch allow_nsfw { - valign: center; - } + Switch allow_nsfw { + valign: center; + } + } } - } - Adw.PreferencesGroup { - title: _("Categories"); + Adw.PreferencesGroup { + title: _("Categories"); - Adw.ActionRow { - title: "General"; + Adw.ActionRow { + title: "General"; - Switch category_general { - valign: center; - } - } + Switch category_general { + valign: center; + } + } - Adw.ActionRow { - title: "Anime"; + Adw.ActionRow { + title: "Anime"; - Switch category_anime { - valign: center; - } - } + Switch category_anime { + valign: center; + } + } - Adw.ActionRow { - title: "People"; + Adw.ActionRow { + title: "People"; - Switch category_people { - valign: center; - } + Switch category_people { + valign: center; + } + } } - } } diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index d73f38f9..acc0b58e 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -1,140 +1,164 @@ -const Adw = imports.gi.Adw; -const ExtensionUtils = imports.misc.extensionUtils; -const Gdk = imports.gi.Gdk; -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; -const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; +import * as Gdk from 'gi://Gdk'; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; +import * as GObject from 'gi://GObject'; +import * as Gtk from 'gi://Gtk'; + +import * as Adw from '@gi/gtk4/adw/adw'; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; + +import * as Settings from './../settings.js'; const Self = ExtensionUtils.getCurrentExtension(); -const Settings = Self.imports.settings; - -var WallhavenSettingsGroup = GObject.registerClass({ - GTypeName: 'WallhavenSettingsGroup', - Template: GLib.filename_to_uri(Self.path + '/ui/wallhaven.ui', null), - InternalChildren: [ - 'allow_sfw', - 'allow_sketchy', - 'allow_nsfw', - 'api_key', - 'button_color', - 'button_color_undo', - 'category_anime', - 'category_general', - 'category_people', - 'keyword', - 'resolutions', - 'row_color' - ] + +const WallhavenSettingsGroup = GObject.registerClass({ + GTypeName: 'WallhavenSettingsGroup', + Template: GLib.filename_to_uri(`${Self.path}/ui/wallhaven.ui`, null), + InternalChildren: [ + 'allow_nsfw', + 'allow_sfw', + 'allow_sketchy', + 'api_key', + 'button_color_undo', + 'button_color', + 'category_anime', + 'category_general', + 'category_people', + 'keyword', + 'resolutions', + 'row_color', + ], }, class WallhavenSettingsGroup extends Adw.PreferencesGroup { - constructor(id, params = {}) { - super(params); - - const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/wallhaven/${id}/`; - this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN, path); - - this._settings.bind('allow-nsfw', - this._allow_nsfw, - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('allow-sfw', - this._allow_sfw, - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('allow-sketchy', - this._allow_sketchy, - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('api-key', - this._api_key, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('category-anime', - this._category_anime, - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('category-general', - this._category_general, - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('category-people', - this._category_people, - 'active', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('color', - this._row_color, - 'subtitle', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('keyword', - this._keyword, - 'text', - Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('resolutions', - this._resolutions, - 'text', - Gio.SettingsBindFlags.DEFAULT); - - this._button_color_undo.connect('clicked', () => { - this._row_color.subtitle = ""; - }); - - const availableColors = [ - "#660000", "#990000", "#cc0000", "#cc3333", "#ea4c88", - "#993399", "#663399", "#333399", "#0066cc", "#0099cc", - "#66cccc", "#77cc33", "#669900", "#336600", "#666600", - "#999900", "#cccc33", "#ffff00", "#ffcc33", "#ff9900", - "#ff6600", "#cc6633", "#996633", "#663300", "#000000", - "#999999", "#cccccc", "#ffffff", "#424153", - ]; - - this._colorPalette = []; - - availableColors.forEach(hexColor => { - let rgbaColor = new Gdk.RGBA(); - rgbaColor.parse(hexColor); - this._colorPalette.push(rgbaColor); - }); - - this._button_color.connect('clicked', () => { - // For GTK 4.10+ - // Gtk.ColorDialog(); - - // https://stackoverflow.com/a/54487948 - this._colorDialog = new Gtk.ColorChooserDialog({ - title: 'Choose a Color', - transient_for: this.get_root(), - modal: true, - }); - this._colorDialog.set_use_alpha(false); - this._colorDialog.add_palette(Gtk.Orientation.HORIZONTAL, 10, this._colorPalette); - - this._colorDialog.connect('response', (dialog, response_id) => { - if (response_id === Gtk.ResponseType.OK) { - // result is a Gdk.RGBA which uses float - let rgba = this._colorDialog.get_rgba(); - // convert to rgba so it's useful - let rgbaString = rgba.to_string(); // rgb(0,0,0) - let rgbaArray = rgbaString.replace("rgb(", "").replace(")", "").split(",") - let hexString = `${parseInt(rgbaArray[0]).toString(16).padStart(2, "0")}${parseInt(rgbaArray[1]).toString(16).padStart(2, "0")}${parseInt(rgbaArray[2]).toString(16).padStart(2, "0")}`; - this._row_color.subtitle = hexString; - } - this._colorDialog.destroy(); - }); - - this._colorDialog.show(); - }); - } - - clearConfig() { - this._settings.reset('allow-nsfw'); - this._settings.reset('allow-sfw'); - this._settings.reset('allow-sketchy'); - this._settings.reset('api-key'); - this._settings.reset('category-anime'); - this._settings.reset('category-general'); - this._settings.reset('category-people'); - this._settings.reset('color'); - this._settings.reset('keyword'); - this._settings.reset('resolutions'); - } + private static _colorPalette: Gdk.RGBA[]; + private static _availableColors: string[] = [ + '#660000', '#990000', '#cc0000', '#cc3333', '#ea4c88', + '#993399', '#663399', '#333399', '#0066cc', '#0099cc', + '#66cccc', '#77cc33', '#669900', '#336600', '#666600', + '#999900', '#cccc33', '#ffff00', '#ffcc33', '#ff9900', + '#ff6600', '#cc6633', '#996633', '#663300', '#000000', + '#999999', '#cccccc', '#ffffff', '#424153', + ]; + + // InternalChildren + private _allow_nsfw!: Gtk.Switch; + private _allow_sfw!: Gtk.Switch; + private _allow_sketchy!: Gtk.Switch; + private _api_key!: Adw.EntryRow; + private _button_color_undo!: Gtk.Button; + private _button_color!: Gtk.Button; + private _category_anime!: Gtk.Switch; + private _category_general!: Gtk.Switch; + private _category_people!: Gtk.Switch; + private _keyword!: Adw.EntryRow; + private _resolutions!: Adw.EntryRow; + private _row_color!: Adw.ActionRow; + + private _colorDialog: Gtk.ColorChooserDialog | undefined; + private _settings; + + constructor(params: object | undefined, id: string) { + super(params); + + const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/wallhaven/${id}/`; + this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN, path); + + this._settings.bind('allow-nsfw', + this._allow_nsfw, + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('allow-sfw', + this._allow_sfw, + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('allow-sketchy', + this._allow_sketchy, + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('api-key', + this._api_key, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('category-anime', + this._category_anime, + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('category-general', + this._category_general, + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('category-people', + this._category_people, + 'active', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('color', + this._row_color, + 'subtitle', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('keyword', + this._keyword, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('resolutions', + this._resolutions, + 'text', + Gio.SettingsBindFlags.DEFAULT); + + this._button_color_undo.connect('clicked', () => { + this._row_color.subtitle = ''; + }); + + this._button_color.connect('clicked', () => { + // TODO: For GTK 4.10+ + // Gtk.ColorDialog(); + + // https://stackoverflow.com/a/54487948 + this._colorDialog = new Gtk.ColorChooserDialog({ + title: 'Choose a Color', + transient_for: this.get_root() as Gtk.Window ?? undefined, + modal: true, + }); + this._colorDialog.set_use_alpha(false); + + if (!WallhavenSettingsGroup._colorPalette) { + WallhavenSettingsGroup._colorPalette = []; + + WallhavenSettingsGroup._availableColors.forEach(hexColor => { + const rgbaColor = new Gdk.RGBA(); + rgbaColor.parse(hexColor); + WallhavenSettingsGroup._colorPalette.push(rgbaColor); + }); + } + this._colorDialog.add_palette(Gtk.Orientation.HORIZONTAL, 10, WallhavenSettingsGroup._colorPalette); + + this._colorDialog.connect('response', (dialog, response_id) => { + if (response_id === Gtk.ResponseType.OK) { + // result is a Gdk.RGBA which uses float + const rgba = dialog.get_rgba(); + // convert to rgba so it's useful + const rgbaString = rgba.to_string(); // rgb(0,0,0) + const rgbaArray = rgbaString.replace('rgb(', '').replace(')', '').split(','); + const hexString = `${parseInt(rgbaArray[0]).toString(16).padStart(2, '0')}${parseInt(rgbaArray[1]).toString(16).padStart(2, '0')}${parseInt(rgbaArray[2]).toString(16).padStart(2, '0')}`; + this._row_color.subtitle = hexString; + } + dialog.destroy(); + }); + + this._colorDialog.show(); + }); + } + + clearConfig() { + this._settings.reset('allow-nsfw'); + this._settings.reset('allow-sfw'); + this._settings.reset('allow-sketchy'); + this._settings.reset('api-key'); + this._settings.reset('category-anime'); + this._settings.reset('category-general'); + this._settings.reset('category-people'); + this._settings.reset('color'); + this._settings.reset('keyword'); + this._settings.reset('resolutions'); + } }); + +export {WallhavenSettingsGroup}; diff --git a/src/utils.ts b/src/utils.ts index 79c9cbe0..af5fdef4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,84 +1,145 @@ -const Gdk = imports.gi.Gdk; -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; - -var Utils = class { - /** - * Get the monitor count for the default "seat". - * @returns Number - */ - static getMonitorCount() { - // Gdk 4.8+ - // Gdk.DisplayManager.get() - // displayManager.get_default_display() - // display.get_monitors() - // monitors.get_n_items() <- Monitor count, number - - // let defaultDisplay = Gdk.Display.get_default(); // default "seat" which can have multiple monitors - // let monitorList = defaultDisplay.get_monitors(); // Gio.ListModel containing all "Gdk.Monitor" - // return monitorList.get_n_items(); - - // Gdk < 4.8 - let defaultDisplay = Gdk.Display.get_default(); // default "seat" which can have multiple monitors - return defaultDisplay.get_n_monitors(); - } - - static getRandomNumber(size) { - return Math.floor(Math.random() * size); - } - - // https://gjs.guide/guides/gio/subprocesses.html#complete-examples - /** - * Execute a command asynchronously and check the exit status. - * - * If given, @cancellable can be used to stop the process before it finishes. - * - * @param {string[]} argv - a list of string arguments - * @param {Gio.Cancellable} [cancellable] - optional cancellable object - * @returns {Promise<>} - The process success - */ - static async execCheck(argv, cancellable = null) { - let cancelId = 0; - let proc = new Gio.Subprocess({ - argv: argv, - flags: Gio.SubprocessFlags.NONE - }); - proc.init(cancellable); - - if (cancellable instanceof Gio.Cancellable) { - cancelId = cancellable.connect(() => proc.force_exit()); - } - - return new Promise((resolve, reject) => { - proc.wait_check_async(null, (proc, res) => { - try { - if (!proc.wait_check_finish(res)) { - let status = proc.get_exit_status(); - - throw new Gio.IOErrorEnum({ - code: Gio.io_error_from_errno(status), - message: GLib.strerror(status) - }); - } - - resolve(); - } catch (e) { - reject(e); - } finally { - if (cancelId > 0) { - cancellable.disconnect(cancelId); - } - } - }); - }); - } - - // https://stackoverflow.com/a/5767357 - static removeItemOnce(arr, value) { - var index = arr.indexOf(value); - if (index > -1) { - arr.splice(index, 1); - } - return arr; - } +import * as Gdk from 'gi://Gdk'; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; + +/** + * Returns a promise which resolves cleanly or rejects according to the underlying subprocess. + * + * @param {string[]} argv String array of command and parameter + * @param {Gio.Cancellable} [cancellable] Object to cancel the command later in lifetime + */ +function execCheck(argv: string[], cancellable?: Gio.Cancellable | null) { + let cancelId = 0; + const proc = new Gio.Subprocess({ + argv, + flags: Gio.SubprocessFlags.NONE, + }); + + // This does not take "undefined" despite the docs saying otherwise + proc.init(cancellable ?? null); + + if (cancellable instanceof Gio.Cancellable) + cancelId = cancellable.connect(() => proc.force_exit()); + + return new Promise((resolve, reject) => { + // This does not take "undefined" despite the docs saying otherwise + proc.wait_check_async(cancellable ?? null, (_proc, res) => { + if (_proc === null) { + reject(new Error('Failed getting process.')); + return; + } + + try { + if (!_proc.wait_check_finish(res)) { + const status = _proc.get_exit_status(); + + throw new Gio.IOErrorEnum({ + code: Gio.io_error_from_errno(status).code, + message: GLib.strerror(status), + }); + } + + resolve(); + } catch (e) { + reject(e); + } finally { + if (cancellable instanceof Gio.Cancellable && cancelId > 0) + cancellable.disconnect(cancelId); + } + }); + }); +} + +/** + * Retrieves the file name part of an URI + * + * @param {string} uri URI to scan + */ +function fileName(uri: string) { + while (_isURIEncoded(uri)) + uri = decodeURIComponent(uri); + + + let base = uri.substring(uri.lastIndexOf('/') + 1); + if (base.indexOf('?') >= 0) + base = base.substring(0, base.indexOf('?')); + + return base; +} + +// https://stackoverflow.com/a/32859917 +/** + * + * @param {string} str1 String to compare + * @param {string} str2 String to compare + */ +function findFirstDifference(str1: string, str2: string) { + let i = 0; + if (str1 === str2) + return -1; + while (str1[i] === str2[i]) + i++; + return i; } + +/** + * + */ +function getMonitorCount() { + // Gdk 4.8+ + // Gdk.DisplayManager.get() + // displayManager.get_default_display() + // display.get_monitors() + // monitors.get_n_items() <- Monitor count, number + + // let defaultDisplay = Gdk.Display.get_default(); // default "seat" which can have multiple monitors + // let monitorList = defaultDisplay.get_monitors(); // Gio.ListModel containing all "Gdk.Monitor" + // return monitorList.get_n_items(); + + // Gdk < 4.8 + const defaultDisplay = Gdk.Display.get_default(); + // FIXME: wrong version in definition + // @ts-expect-error + return defaultDisplay.get_n_monitors(); +} + +/** + * + * @param {number} size Maximum + */ +function getRandomNumber(size: number) { + return Math.floor(Math.random() * size); +} + +/** + * + * @param {string} uri The URI to check + */ +function _isURIEncoded(uri: string) { + uri = uri || ''; + + return uri !== decodeURIComponent(uri); +} + +// https://stackoverflow.com/a/5767357 +/** + * + * @param {Array} array Array of items + * @param {T} value Item to remove + */ +function removeItemOnce(array: T[], value: T) { + const index = array.indexOf(value); + if (index > -1) + array.splice(index, 1); + + return array; +} + +export { + getMonitorCount, + getRandomNumber, + execCheck, + findFirstDifference, + fileName, + removeItemOnce +}; diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index bee43d50..c4f7d997 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -1,569 +1,537 @@ -const Mainloop = imports.gi.GLib; +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; -// Filesystem -const Gio = imports.gi.Gio; -const GLib = imports.gi.GLib; +import * as ExtensionUtils from '@gi/misc/extensionUtils'; -//self -const Self = imports.misc.extensionUtils.getCurrentExtension(); -const HistoryModule = Self.imports.history; -const HydraPaper = Self.imports.hydraPaper; -const LoggerModule = Self.imports.logger; -const Prefs = Self.imports.settings; -const Utils = Self.imports.utils; -const Timer = Self.imports.timer; +import * as HistoryModule from './history.js'; +import * as SettingsModule from './settings.js'; +import * as Utils from './utils.js'; + +import {AFTimer as Timer} from './timer.js'; +import {HydraPaper} from './hydraPaper.js'; +import {Logger} from './logger.js'; // SourceAdapter -const GenericJsonAdapter = Self.imports.adapter.genericJson; -const LocalFolderAdapter = Self.imports.adapter.localFolder; -const RedditAdapter = Self.imports.adapter.reddit; -const UnsplashAdapter = Self.imports.adapter.unsplash; -const UrlSourceAdapter = Self.imports.adapter.urlSource; -const WallhavenAdapter = Self.imports.adapter.wallhaven; - -var WallpaperController = class { - _backendConnection = null; - _prohibitTimer = false; - - constructor() { - this.logger = new LoggerModule.Logger('RWG3', 'WallpaperController'); - let xdg_cache_home = Mainloop.getenv('XDG_CACHE_HOME') - if (!xdg_cache_home) { - xdg_cache_home = `${Mainloop.getenv('HOME')}/.cache` - } - this.wallpaperLocation = `${xdg_cache_home}/${Self.metadata['uuid']}/wallpapers/`; - let mode = parseInt('0755', 8); - Mainloop.mkdir_with_parents(this.wallpaperLocation, mode) - - this._autoFetch = { - active: false, - duration: 30, - }; - - // functions will be called upon loading a new wallpaper - this._startLoadingHooks = []; - // functions will be called when loading a new wallpaper stopped. If an error occurred then the error will be passed as parameter. - this._stopLoadingHooks = []; - - this._backendConnection = new Prefs.Settings(Prefs.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); - - // Bring values to defined stage - this._backendConnection.set('clear-history', 'boolean', false); - this._backendConnection.set('open-folder', 'boolean', false); - this._backendConnection.set('pause-timer', 'boolean', false); - this._backendConnection.set('request-new-wallpaper', 'boolean', false); - - // Track value changes - this._backendConnection.observe('clear-history', () => this._clearHistory()); - this._backendConnection.observe('open-folder', () => this._openFolder()); - this._backendConnection.observe('pause-timer', () => this._pauseTimer()); - this._backendConnection.observe('request-new-wallpaper', () => this._requestNewWallpaper()); - - this._timer = new Timer.AFTimer(); - this._historyController = new HistoryModule.HistoryController(this.wallpaperLocation); - this._hydraPaper = new HydraPaper.HydraPaper(); - - this._settings = new Prefs.Settings(); - this._settings.observe('history-length', () => this._updateHistory()); - this._settings.observe('auto-fetch', () => this._updateAutoFetching()); - this._settings.observe('minutes', () => this._updateAutoFetching()); - this._settings.observe('hours', () => this._updateAutoFetching()); - - this._updateHistory(); - this._updateAutoFetching(); - - // load a new wallpaper on startup - if (this._settings.get("fetch-on-startup", "boolean")) { - this.fetchNewWallpaper(); - } - - // Initialize favorites folder - // TODO: There's probably a better place for this - let favoritesFolder = this._settings.get('favorites-folder', 'string'); - if (favoritesFolder === "") { - const directoryPictures = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES); - - if (directoryPictures === null) { - // Pictures not set up - const directoryDownloads = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOWNLOAD); - - if (directoryDownloads === null) { - const xdg_data_home = GLib.get_user_data_dir(); - favoritesFolder = Gio.File.new_for_path(xdg_data_home); - } else { - favoritesFolder = Gio.File.new_for_path(directoryDownloads); - } - } else { - favoritesFolder = Gio.File.new_for_path(directoryPictures); - } - - favoritesFolder = favoritesFolder.get_child(Self.metadata['uuid']); - - this._settings.set('favorites-folder', 'string', favoritesFolder.get_path()); - } - } - - _clearHistory() { - if (this._backendConnection.get('clear-history', 'boolean')) { - this.update(); - this.deleteHistory(); - this._backendConnection.set('clear-history', 'boolean', false); - } - } - - _openFolder() { - if (this._backendConnection.get('open-folder', 'boolean')) { - let uri = GLib.filename_to_uri(this.wallpaperLocation, ""); - Gio.AppInfo.launch_default_for_uri(uri, Gio.AppLaunchContext.new()); - this._backendConnection.set('open-folder', 'boolean', false); - } - } - - _pauseTimer() { - if (this._backendConnection.get('pause-timer', 'boolean')) { - this._prohibitTimer = true; - this._updateAutoFetching(); - } else { - this._prohibitTimer = false; - this._updateAutoFetching(); - } - } - - _requestNewWallpaper() { - if (this._backendConnection.get('request-new-wallpaper', 'boolean')) { - this.update(); - this.fetchNewWallpaper(() => { - this.update(); - this._backendConnection.set('request-new-wallpaper', 'boolean', false); - }); - } - } - - _updateHistory() { - this._historyController.load(); - } - - _updateAutoFetching() { - let duration = 0; - duration += this._settings.get('minutes', 'int'); - duration += this._settings.get('hours', 'int') * 60; - this._autoFetch.duration = duration; - this._autoFetch.active = this._settings.get('auto-fetch', 'boolean'); - - // only start timer if not in context of preferences window - if (!this._prohibitTimer && this._autoFetch.active) { - this._timer.registerCallback(() => this.fetchNewWallpaper()); - this._timer.setMinutes(this._autoFetch.duration); - this._timer.start(); - } else { - this._timer.stop(); - } - } - - /* - randomly returns an enabled and configured SourceAdapter - returns a default UnsplashAdapter in case of failure - */ - _getRandomAdapter() { - let imageSourceAdapter = null; - let sourceID = this._getRandomSource(); - - let path = `${Prefs.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${sourceID}/`; - let settingsGeneral = new Prefs.Settings(Prefs.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); - - let sourceName = settingsGeneral.get('name', 'string'); - let sourceType = settingsGeneral.get('type', 'enum'); - - if (sourceID === -1) { - sourceType = null; - } - - try { - switch (sourceType) { - case 0: - imageSourceAdapter = new UnsplashAdapter.UnsplashAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 1: - imageSourceAdapter = new WallhavenAdapter.WallhavenAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 2: - imageSourceAdapter = new RedditAdapter.RedditAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 3: - imageSourceAdapter = new GenericJsonAdapter.GenericJsonAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 4: - imageSourceAdapter = new LocalFolderAdapter.LocalFolderAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 5: - imageSourceAdapter = new UrlSourceAdapter.UrlSourceAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - default: - imageSourceAdapter = new UnsplashAdapter.UnsplashAdapter(null, null, this.wallpaperLocation); - sourceType = 0; - break; - } - } catch (error) { - this.logger.warn("Had errors, fetching with default settings."); - imageSourceAdapter = new UnsplashAdapter.UnsplashAdapter(null, null, this.wallpaperLocation); - sourceType = 0; - } - - return { - adapter: imageSourceAdapter, - adapterId: sourceID, - adapterType: sourceType - }; - } - - _getRandomSource() { - let sources = this._settings.get('sources', 'strv'); - - if (sources === null || sources.length < 1) { - return -1; - } - - let enabled_sources = sources.filter(element => { - let path = `${Prefs.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${element}/`; - let settingsGeneral = new Prefs.Settings(Prefs.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); - return settingsGeneral.get('enabled', 'boolean'); - }); - - if (enabled_sources === null || enabled_sources.length < 1) { - return -1; - } - - // https://stackoverflow.com/a/5915122 - return enabled_sources[Utils.Utils.getRandomNumber(enabled_sources.length)]; - } - - /** - * Sets the wallpaper and the lockscreen when enabled to the given path. Executes the callback on success. - * @param path - * @param callback - * @private - */ - async _setBackground(path, callback) { - let monitorCount = Utils.Utils.getMonitorCount(); - let background_setting = new Gio.Settings({ schema: "org.gnome.desktop.background" }); - let screensaver_setting = new Gio.Settings({ schema: "org.gnome.desktop.screensaver" }); - let wallpaperUri = "file://" + path; - - let changeType = this._settings.get('change-type', 'enum'); - // - // - // - // TODO: - - if (changeType === 0 || changeType === 2) { - try { - if (this._settings.get('multiple-displays', 'boolean') && this._hydraPaper !== null && await this._hydraPaper.isAvailable()) { - let wallpaperArray = [path]; - - // Abuse history to fill missing images - for (let index = 0; index < monitorCount - 1; index++) { - let historyElement; - do { - historyElement = this._historyController.getRandom(); - } while (this._historyController.history.length > monitorCount && wallpaperArray.includes(historyElement.path, 1)) - // ensure different wallpaper for all displays if possible - - wallpaperArray.push(historyElement.path); - } - - await this._hydraPaper.run(wallpaperArray); - - // Manually set key for darkmode because that's way faster - background_setting.set_string("picture-uri-dark", background_setting.get_string("picture-uri")); - - Gio.Settings.sync(); - } else { - // set "picture-options" to "zoom" for single wallpapers - // hydrapaper changes this to "spanned" - background_setting.set_string('picture-options', 'zoom'); - this._setPictureUriOfSettingsObject(background_setting, wallpaperUri); - } - } catch (error) { - this.logger.warn(error); - } - } - - if (changeType === 1) { - try { - if (this._settings.get('multiple-displays', 'boolean') && this._hydraPaper !== null && await this._hydraPaper.isAvailable()) { - let wallpaperArray = [path]; - - // Abuse history to fill missing images - for (let index = 0; index < monitorCount - 1; index++) { - let historyElement; - do { - historyElement = this._historyController.getRandom(); - } while (this._historyController.history.length > monitorCount && wallpaperArray.includes(historyElement.path, 1)) - // ensure different wallpaper for all displays if possible - - wallpaperArray.push(historyElement.path); - } - - // Remember keys, HydraPaper will change these - let tmpBackground = background_setting.get_string("picture-uri-dark"); - let tmpMode = background_setting.get_string("picture-options"); - - // Force HydraPaper to target a different resulting image by using darkmode - await this._hydraPaper.run(wallpaperArray, true); - - screensaver_setting.set_string("picture-options", "spanned"); - this._setPictureUriOfSettingsObject(screensaver_setting, background_setting.get_string("picture-uri-dark")) - - // HydraPaper possibly changed these, change them back - background_setting.set_string("picture-uri-dark", tmpBackground); - background_setting.set_string("picture-options", tmpMode); - - Gio.Settings.sync(); - } else { - // set "picture-options" to "zoom" for single wallpapers - screensaver_setting.set_string('picture-options', 'zoom'); - this._setPictureUriOfSettingsObject(screensaver_setting, wallpaperUri); - } - } catch (error) { - this.logger.warn(error); - } - } - - if (changeType === 2) { - this._setPictureUriOfSettingsObject(screensaver_setting, background_setting.get_string('picture-uri')); - } - - // Run general post command - let commandString = this._settings.get('general-post-command', 'string'); - let generalPostCommandArray = this._getCommandArray(commandString, path); - if (generalPostCommandArray !== null) { - try { - await Utils.Utils.execCheck(generalPostCommandArray); - } catch (error) { - this.logger.warn(error); - } - } - - // call callback if given - if (callback) { - callback(); - } - } - - /** - * Set the picture-uri property of the given settings object to the path. - * Precondition: the settings object has to be a valid Gio settings object with the picture-uri property. - * @param settings - * @param path - * @param callback - * @private - */ - _setPictureUriOfSettingsObject(settings, path, callback) { - /* - inspired from: - https://bitbucket.org/LukasKnuth/backslide/src/7e36a49fc5e1439fa9ed21e39b09b61eca8df41a/backslide@codeisland.org/settings.js?at=master - */ - let set_prop = (property) => { - if (settings.is_writable(property)) { - // Set a new Background-Image (should show up immediately): - if (!settings.set_string(property, path)) { - this._bailOutWithCallback(`Failed to write property: ${property}`, callback); - } - } else { - this._bailOutWithCallback(`Property not writable: ${property}`, callback); - } - } - - const availableKeys = settings.list_keys(); - - let property = "picture-uri"; - if (availableKeys.indexOf(property) !== -1) { - set_prop(property); - } - - property = "picture-uri-dark"; - if (availableKeys.indexOf(property) !== -1) { - set_prop(property); - } - - Gio.Settings.sync(); // Necessary: http://stackoverflow.com/questions/9985140 - - // call callback if given - if (callback) { - callback(); - } - } - - setWallpaper(historyId) { - let historyElement = this._historyController.get(historyId); - - if (this._historyController.promoteToActive(historyElement.id)) { - this._setBackground(historyElement.path); - } else { - this.logger.warn("The history id (" + historyElement.id + ") could not be found.") - // TODO: Error handling history id not found. - } - } - - fetchNewWallpaper(callback) { - this._startLoadingHooks.forEach((element) => { - element(); - }); - - if (!this._prohibitTimer) { - this._timer.reset(); // reset timer - } - - let returnObject = this._getRandomAdapter(); - returnObject.adapter.requestRandomImage((historyElement, error) => { - if (historyElement == null || error) { - this._bailOutWithCallback("Could not fetch wallpaper location.", callback); - this._stopLoadingHooks.map(element => element(null)); - return; - } - - this.logger.info("Requesting image: " + historyElement.source.imageDownloadUrl); - - returnObject.adapter.fetchFile(historyElement.source.imageDownloadUrl, (historyId, path, error) => { - if (error) { - this._bailOutWithCallback(`Could not load new wallpaper: ${error}`, callback); - this._stopLoadingHooks.forEach(element => element(null)); - return; - } - - historyElement.name = String(historyId); - historyElement.id = `${historyElement.timestamp}_${historyElement.name}`; // timestamp ensures uniqueness - historyElement.adapter.id = returnObject.adapterId; - historyElement.adapter.type = returnObject.adapterType; - - // Move file to unique naming - let sourceFile = Gio.File.new_for_path(path); - let targetFolder = sourceFile.get_parent(); - let targetFile = targetFolder.get_child(historyElement.id); - - try { - if (!sourceFile.move(targetFile, Gio.FileCopyFlags.NONE, null, null)) { - this.logger.warn('Failed copying unique image.'); - return; - } - } catch (error) { - if (error === Gio.IOErrorEnum.EXISTS) { - this.logger.warn('Image already exists in location.'); - return; - } - } - - historyElement.path = targetFile.get_path(); - - this._setBackground(historyElement.path, () => { - // insert file into history - this._historyController.insert(historyElement); - - this._stopLoadingHooks.forEach(element => element(null)); - - // call callback if given - if (callback) { - callback(); - } - }); - }); - }); - } - - // TODO: Change to original historyElement if more variable get exposed - _getCommandArray(commandString, historyElementPath) { - let string = commandString; - if (string === "") { - return null; - } - - // Replace variables - let variables = new Map(); - variables.set('%wallpaper_path%', historyElementPath); - - variables.forEach((value, key) => { - string = string.replaceAll(key, value); - }); - - try { - // https://gjs-docs.gnome.org/glib20/glib.shell_parse_argv - // Parses a command line into an argument vector, in much the same way - // the shell would, but without many of the expansions the shell would - // perform (variable expansion, globs, operators, filename expansion, - // etc. are not supported). - return GLib.shell_parse_argv(string)[1]; - } catch (e) { - this.logger.warn(e); - } - - return null; - } - - _backgroundTimeout(delay) { - if (this.timeout) { - return; - } - - delay = delay || 200; - - this.timeout = Mainloop.timeout_add(Mainloop.PRIORITY_DEFAULT, delay, () => { - this.timeout = null; - if (this._resetWallpaper) { - this._setBackground(this._historyController.getCurrentElement().path); - this._resetWallpaper = false; - } else { - this._setBackground(this.wallpaperLocation + this.previewId); - } - return false; - }); - } - - previewWallpaper(historyid, delay) { - if (!this._settings.get('disable-hover-preview', 'boolean')) { - this.previewId = historyid; - this._resetWallpaper = false; - - this._backgroundTimeout(delay); - } - } - - resetWallpaper() { - if (!this._settings.get('disable-hover-preview', 'boolean')) { - this._resetWallpaper = true; - this._backgroundTimeout(); - } - } - - getHistoryController() { - return this._historyController; - } - - deleteHistory() { - this._historyController.clear(); - } - - update() { - this._updateHistory(); - } - - registerStartLoadingHook(fn) { - if (typeof fn === "function") { - this._startLoadingHooks.push(fn) - } - } - - registerStopLoadingHook(fn) { - if (typeof fn === "function") { - this._stopLoadingHooks.push(fn) - } - } - - _bailOutWithCallback(msg, callback) { - this.logger.error(msg); - - if (callback) { - callback(); - } - } - -}; +import {BaseAdapter} from './adapter/baseAdapter.js'; +import {GenericJsonAdapter} from './adapter/genericJson.js'; +import {LocalFolderAdapter} from './adapter/localFolder.js'; +import {RedditAdapter} from './adapter/reddit.js'; +import {UnsplashAdapter} from './adapter/unsplash.js'; +import {UrlSourceAdapter} from './adapter/urlSource.js'; +import {WallhavenAdapter} from './adapter/wallhaven.js'; + +const Self = ExtensionUtils.getCurrentExtension(); + +// https://gjs.guide/guides/gjs/asynchronous-programming.html#promisify-helper +Gio._promisify(Gio.File.prototype, 'move_async', 'move_finish'); + +class WallpaperController { + wallpaperLocation: string; + + private _backendConnection = new SettingsModule.Settings(SettingsModule.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); + private _logger = new Logger('RWG3', 'WallpaperController'); + private _settings = new SettingsModule.Settings(); + private _timer = Timer.getTimer(); + private _prohibitTimer = false; + private _historyController: HistoryModule.HistoryController; + private _hydraPaper = new HydraPaper(); + private _autoFetch = {active: false, duration: 30}; + private _previewId: string | undefined; + private _resetWallpaper = false; + private _timeout: number | null = null; + /** functions will be called upon loading a new wallpaper */ + private _startLoadingHooks: (() => void)[] = []; + /** functions will be called when loading a new wallpaper stopped. If an error occurred then the error will be passed as parameter. */ + private _stopLoadingHooks: (() => void)[] = []; + + constructor() { + let xdg_cache_home = GLib.getenv('XDG_CACHE_HOME'); + if (!xdg_cache_home) + xdg_cache_home = `${GLib.getenv('HOME')}/.cache`; + + + this.wallpaperLocation = `${xdg_cache_home}/${Self.metadata['uuid']}/wallpapers/`; + let mode = 0o0755; + GLib.mkdir_with_parents(this.wallpaperLocation, mode); + + this._historyController = new HistoryModule.HistoryController(this.wallpaperLocation); + + // Bring values to defined stage + this._backendConnection.setBoolean('clear-history', false); + this._backendConnection.setBoolean('open-folder', false); + this._backendConnection.setBoolean('pause-timer', false); + this._backendConnection.setBoolean('request-new-wallpaper', false); + + // Track value changes + this._backendConnection.observe('clear-history', () => this._clearHistory()); + this._backendConnection.observe('open-folder', () => this._openFolder()); + this._backendConnection.observe('pause-timer', () => this._pauseTimer()); + this._backendConnection.observe('request-new-wallpaper', () => this._requestNewWallpaper().catch(logError)); + + this._settings.observe('history-length', () => this._updateHistory()); + this._settings.observe('auto-fetch', () => this._updateAutoFetching()); + this._settings.observe('minutes', () => this._updateAutoFetching()); + this._settings.observe('hours', () => this._updateAutoFetching()); + + this._updateHistory(); + this._updateAutoFetching(); + + // load a new wallpaper on startup + if (this._settings.getBoolean('fetch-on-startup')) + this.fetchNewWallpaper().catch(logError); + + // Initialize favorites folder + // TODO: There's probably a better place for this + const favoritesFolderSetting = this._settings.getString('favorites-folder'); + let favoritesFolder: Gio.File; + if (favoritesFolderSetting === '') { + const directoryPictures = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES); + + if (directoryPictures === null) { + // Pictures not set up + const directoryDownloads = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DOWNLOAD); + + if (directoryDownloads === null) { + const xdg_data_home = GLib.get_user_data_dir(); + favoritesFolder = Gio.File.new_for_path(xdg_data_home); + } else { + favoritesFolder = Gio.File.new_for_path(directoryDownloads); + } + } else { + favoritesFolder = Gio.File.new_for_path(directoryPictures); + } + + favoritesFolder = favoritesFolder.get_child(Self.metadata['uuid']); + + const favoritesFolderPath = favoritesFolder.get_path(); + if (favoritesFolderPath) + this._settings.setString('favorites-folder', favoritesFolderPath); + } + } + + private _clearHistory() { + if (this._backendConnection.getBoolean('clear-history')) { + this.update(); + this.deleteHistory(); + this._backendConnection.setBoolean('clear-history', false); + } + } + + private _openFolder() { + if (this._backendConnection.getBoolean('open-folder')) { + let uri = GLib.filename_to_uri(this.wallpaperLocation, ''); + Gio.AppInfo.launch_default_for_uri(uri, Gio.AppLaunchContext.new()); + this._backendConnection.setBoolean('open-folder', false); + } + } + + private _pauseTimer() { + if (this._backendConnection.getBoolean('pause-timer')) { + this._prohibitTimer = true; + this._updateAutoFetching(); + } else { + this._prohibitTimer = false; + this._updateAutoFetching(); + } + } + + private async _requestNewWallpaper() { + if (this._backendConnection.getBoolean('request-new-wallpaper')) { + this.update(); + try { + await this.fetchNewWallpaper(); + } finally { + this.update(); + this._backendConnection.setBoolean('request-new-wallpaper', false); + } + } + } + + private _updateHistory() { + this._historyController.load(); + } + + private _updateAutoFetching() { + let duration = 0; + duration += this._settings.getInt('minutes'); + duration += this._settings.getInt('hours') * 60; + this._autoFetch.duration = duration; + this._autoFetch.active = this._settings.getBoolean('auto-fetch'); + + // only start timer if not in context of preferences window + if (!this._prohibitTimer && this._autoFetch.active) { + this._timer.registerCallback(() => { + this.fetchNewWallpaper().catch(logError); + }); + this._timer.setMinutes(this._autoFetch.duration); + this._timer.start(); + } else { + this._timer.stop(); + } + } + + /** + randomly returns an enabled and configured SourceAdapter + returns a default UnsplashAdapter in case of failure + */ + private _getRandomAdapter() { + const sourceID = this._getRandomSource(); + + let imageSourceAdapter: BaseAdapter; + let sourceName = 'undefined'; + let sourceType = -1; + + if (sourceID !== '-1') { + const path = `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${sourceID}/`; + const settingsGeneral = new SettingsModule.Settings(SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); + + sourceName = settingsGeneral.getString('name'); + sourceType = settingsGeneral.getEnum('type'); + } + + try { + switch (sourceType) { + case 0: + imageSourceAdapter = new UnsplashAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 1: + imageSourceAdapter = new WallhavenAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 2: + imageSourceAdapter = new RedditAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 3: + imageSourceAdapter = new GenericJsonAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 4: + imageSourceAdapter = new LocalFolderAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 5: + imageSourceAdapter = new UrlSourceAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + default: + imageSourceAdapter = new UnsplashAdapter(null, null, this.wallpaperLocation); + sourceType = 0; + break; + } + } catch (error) { + this._logger.warn('Had errors, fetching with default settings.'); + imageSourceAdapter = new UnsplashAdapter(null, null, this.wallpaperLocation); + sourceType = 0; + } + + return { + adapter: imageSourceAdapter, + adapterId: sourceID, + adapterType: sourceType, + }; + } + + private _getRandomSource() { + const sources: string[] = this._settings.getStrv('sources'); + + if (sources === null || sources.length < 1) + return '-1'; + + + const enabled_sources = sources.filter(element => { + const path = `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${element}/`; + const settingsGeneral = new SettingsModule.Settings(SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); + return settingsGeneral.getBoolean('enabled'); + }); + + if (enabled_sources === null || enabled_sources.length < 1) + return '-1'; + + + // https://stackoverflow.com/a/5915122 + return enabled_sources[Utils.getRandomNumber(enabled_sources.length)]; + } + + /** + * Sets the wallpaper and the lock screen when enabled to the given path. + * + * @param {string} path Path to the image + */ + private async _setBackground(path: string) { + const background_setting = new SettingsModule.Settings('org.gnome.desktop.background'); + const screensaver_setting = new SettingsModule.Settings('org.gnome.desktop.screensaver'); + const wallpaperUri = `file://${path}`; + + // + // + // + // TODO: + const changeType = this._settings.getEnum('change-type'); + + if (changeType === 0 || changeType === 2) { + try { + if (this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable()) { + const wallpaperArray = this._fillMonitorsFromHistory(path); + + await this._hydraPaper.run(wallpaperArray); + + // Manually set key for darkmode because that's way faster + background_setting.setString('picture-uri-dark', background_setting.getString('picture-uri')); + } else { + // set "picture-options" to "zoom" for single wallpapers + // hydrapaper changes this to "spanned" + background_setting.setString('picture-options', 'zoom'); + this._setPictureUriOfSettingsObject(background_setting, wallpaperUri); + } + } catch (error) { + this._logger.warn(String(error)); + } + } + + if (changeType === 1) { + try { + if (this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable()) { + const wallpaperArray = this._fillMonitorsFromHistory(path); + + // Remember keys, HydraPaper will change these + const tmpBackground = background_setting.getString('picture-uri-dark'); + const tmpMode = background_setting.getString('picture-options'); + + // Force HydraPaper to target a different resulting image by using darkmode + await this._hydraPaper.run(wallpaperArray, true); + + screensaver_setting.setString('picture-options', 'spanned'); + this._setPictureUriOfSettingsObject(screensaver_setting, background_setting.getString('picture-uri-dark')); + + // HydraPaper possibly changed these, change them back + background_setting.setString('picture-uri-dark', tmpBackground); + background_setting.setString('picture-options', tmpMode); + } else { + // set "picture-options" to "zoom" for single wallpapers + screensaver_setting.setString('picture-options', 'zoom'); + this._setPictureUriOfSettingsObject(screensaver_setting, wallpaperUri); + } + } catch (error) { + this._logger.warn(String(error)); + } + } + + if (changeType === 2) + this._setPictureUriOfSettingsObject(screensaver_setting, background_setting.getString('picture-uri')); + + + // Run general post command + const commandString = this._settings.getString('general-post-command'); + const generalPostCommandArray = this._getCommandArray(commandString, path); + if (generalPostCommandArray !== null) { + try { + await Utils.execCheck(generalPostCommandArray); + } catch (error) { + this._logger.warn(String(error)); + } + } + } + + private _fillMonitorsFromHistory(newWallpaperPath: string) { + const monitorCount = Utils.getMonitorCount(); + const wallpaperArray = [newWallpaperPath]; + + // Abuse history to fill missing images + for (let index = 1; index < monitorCount; index++) { + let historyElement; + do + historyElement = this._historyController.getRandom(); + while (this._historyController.history.length > monitorCount && historyElement.path && wallpaperArray.includes(historyElement.path)); + // ensure different wallpaper for all displays if possible + + if (historyElement.path) + wallpaperArray.push(historyElement.path); + } + + return wallpaperArray; + } + + /** + * Set the picture-uri property of the given settings object to the path. + * Precondition: the settings object has to be a valid Gio settings object with the picture-uri property. + * + * @param {SettingsModule.Settings} settings The settings schema object containing the keys to change + * @param {string} uri The picture URI to be set + */ + private _setPictureUriOfSettingsObject(settings: SettingsModule.Settings, uri: string) { + /* + inspired from: + https://bitbucket.org/LukasKnuth/backslide/src/7e36a49fc5e1439fa9ed21e39b09b61eca8df41a/backslide@codeisland.org/settings.js?at=master + */ + const setProp = (property: string) => { + if (settings.isWritable(property)) { + // Set a new Background-Image (should show up immediately): + settings.setString(property, uri); + } else { + throw new Error(`Property not writable: ${property}`); + } + }; + + const availableKeys = settings.listKeys(); + + let property = 'picture-uri'; + if (availableKeys.indexOf(property) !== -1) + setProp(property); + + + property = 'picture-uri-dark'; + if (availableKeys.indexOf(property) !== -1) + setProp(property); + } + + setWallpaper(historyId: string) { + const historyElement = this._historyController.get(historyId); + + if (historyElement?.id && historyElement.path && this._historyController.promoteToActive(historyElement.id)) + this._setBackground(historyElement.path).catch(logError); + else + this._logger.warn(`The history id (${historyId}) could not be found.`); + // TODO: Error handling history id not found. + } + + async fetchNewWallpaper() { + this._startLoadingHooks.forEach(element => element()); + + try { + if (!this._prohibitTimer) + this._timer.reset(); // reset timer + + const returnObject = this._getRandomAdapter(); + + let historyEntry: HistoryModule.HistoryEntry; + let sourceFile: Gio.File; + historyEntry = await returnObject.adapter.requestRandomImage(); + + this._logger.info(`Requesting image: ${historyEntry.source.imageDownloadUrl}`); + sourceFile = await returnObject.adapter.fetchFile(historyEntry); + + historyEntry.adapter.id = returnObject.adapterId; + historyEntry.adapter.type = returnObject.adapterType; + + // Move file to unique naming + const targetFolder = sourceFile.get_parent(); + const targetFile = targetFolder?.get_child(historyEntry.id); + + if (!targetFile) + throw new Error('Failed getting targetFile'); + + try { + // This function is Gio._promisified + if (!await sourceFile.move_async(targetFile, Gio.FileCopyFlags.NONE, 0, null, null)) + throw new Error('Failed copying unique image.'); + } catch (moveError) { + if (moveError === Gio.IOErrorEnum.EXISTS) + this._logger.warn('Image already exists in location.'); + else + throw moveError; + } + + historyEntry.path = targetFile.get_path(); + + if (!historyEntry.path) + throw new Error('Failed getting historyEntry.path'); + + await this._setBackground(historyEntry.path); + + // insert file into history + this._historyController.insert(historyEntry); + } finally { + this._stopLoadingHooks.forEach(element => element()); + } + } + + // TODO: Change to original historyElement if more variable get exposed + private _getCommandArray(commandString: string, historyElementPath: string) { + let string = commandString; + if (string === '') + return null; + + // Replace variables + const variables = new Map(); + variables.set('%wallpaper_path%', historyElementPath); + + variables.forEach((value, key) => { + string = string.replaceAll(key, value); + }); + + try { + // https://gjs-docs.gnome.org/glib20/glib.shell_parse_argv + // Parses a command line into an argument vector, in much the same way + // the shell would, but without many of the expansions the shell would + // perform (variable expansion, globs, operators, filename expansion, + // etc. are not supported). + return GLib.shell_parse_argv(string)[1]; + } catch (e) { + this._logger.warn(String(e)); + } + + return null; + } + + private _backgroundTimeout(delay?: number) { + if (this._timeout) + return; + + delay = delay || 200; + + this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => { + this._timeout = null; + const currentWallpaperPath = this._historyController.getCurrentElement().path; + if (this._resetWallpaper && currentWallpaperPath) { + this._setBackground(currentWallpaperPath).catch(logError); + this._resetWallpaper = false; + } else if (this._previewId !== undefined) { + this._setBackground(this.wallpaperLocation + this._previewId).catch(logError); + } + return false; + }); + } + + previewWallpaper(historyId: string, delay?: number) { + if (!this._settings.getBoolean('disable-hover-preview')) { + this._previewId = historyId; + this._resetWallpaper = false; + + this._backgroundTimeout(delay); + } + } + + resetWallpaper() { + if (!this._settings.getBoolean('disable-hover-preview')) { + this._resetWallpaper = true; + this._backgroundTimeout(); + } + } + + getHistoryController() { + return this._historyController; + } + + deleteHistory() { + this._historyController.clear(); + } + + update() { + this._updateHistory(); + } + + registerStartLoadingHook(fn: () => void) { + if (typeof fn === 'function') + this._startLoadingHooks.push(fn); + } + + registerStopLoadingHook(fn: () => void) { + if (typeof fn === 'function') + this._stopLoadingHooks.push(fn); + } + + private _bailOutWithCallback(msg: string, callback?: () => void) { + this._logger.error(msg); + + if (callback) + callback(); + } +} + +export {WallpaperController}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..4d4cf18a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "target": "ES2020", + "module": "es2020", + "sourceMap": false, + "strict": true, + "pretty": true, + "removeComments": false, + "baseUrl": "./src", + "allowSyntheticDefaultImports": true, + "outDir": "./randomwallpaper@iflow.space", + "moduleResolution": "node", + "skipLibCheck": true, + "lib": [ + "ES2021" + ], + "paths": { + "@gi/*": ["../types/*"], + }, + "typeRoots": [ + "./node_modules/@gi-types", + ], + "types": [ + "gjs-environment", + "base-types", + "gtk4-types", + ], + + }, + "include": [ + "src/**/*.ts", + "randomwallpaper@iflow.space/**/*.js" + ], + "exclude": [ + "./node_modules/**", + "./types/**" + ], +} diff --git a/types/gtk4/adw/adw.d.ts b/types/gtk4/adw/adw.d.ts new file mode 100644 index 00000000..f5dace63 --- /dev/null +++ b/types/gtk4/adw/adw.d.ts @@ -0,0 +1,3718 @@ +// https://github.com/gi-ts/gtk4/tree/master/packages/%40gi-types/adw1 +// Manually extended with Adw.EntryRow which is somehow missing form the original file + +/** + * Adw 1 + * + * Generated from 1.1.2 + */ + +import * as Gtk from "@gi-types/gtk4"; +import * as GObject from "@gi-types/gobject2"; +import * as Gio from "@gi-types/gio2"; +import * as GLib from "@gi-types/glib2"; +import * as Gdk from "@gi-types/gdk4"; +import * as Gsk from "@gi-types/gsk4"; + +export const DURATION_INFINITE: number; +export const MAJOR_VERSION: number; +export const MICRO_VERSION: number; +export const MINOR_VERSION: number; +export const VERSION_S: string; +export function easing_ease(self: Easing, value: number): number; +export function get_enable_animations(widget: Gtk.Widget): boolean; +export function get_major_version(): number; +export function get_micro_version(): number; +export function get_minor_version(): number; +export function init(): void; +export function is_initialized(): boolean; +export function lerp(a: number, b: number, t: number): number; +export type AnimationTargetFunc = (value: number) => void; + +export namespace AnimationState { + export const $gtype: GObject.GType; +} + +export enum AnimationState { + IDLE = 0, + PAUSED = 1, + PLAYING = 2, + FINISHED = 3, +} + +export namespace CenteringPolicy { + export const $gtype: GObject.GType; +} + +export enum CenteringPolicy { + LOOSE = 0, + STRICT = 1, +} + +export namespace ColorScheme { + export const $gtype: GObject.GType; +} + +export enum ColorScheme { + DEFAULT = 0, + FORCE_LIGHT = 1, + PREFER_LIGHT = 2, + PREFER_DARK = 3, + FORCE_DARK = 4, +} + +export namespace Easing { + export const $gtype: GObject.GType; +} + +export enum Easing { + LINEAR = 0, + EASE_IN_QUAD = 1, + EASE_OUT_QUAD = 2, + EASE_IN_OUT_QUAD = 3, + EASE_IN_CUBIC = 4, + EASE_OUT_CUBIC = 5, + EASE_IN_OUT_CUBIC = 6, + EASE_IN_QUART = 7, + EASE_OUT_QUART = 8, + EASE_IN_OUT_QUART = 9, + EASE_IN_QUINT = 10, + EASE_OUT_QUINT = 11, + EASE_IN_OUT_QUINT = 12, + EASE_IN_SINE = 13, + EASE_OUT_SINE = 14, + EASE_IN_OUT_SINE = 15, + EASE_IN_EXPO = 16, + EASE_OUT_EXPO = 17, + EASE_IN_OUT_EXPO = 18, + EASE_IN_CIRC = 19, + EASE_OUT_CIRC = 20, + EASE_IN_OUT_CIRC = 21, + EASE_IN_ELASTIC = 22, + EASE_OUT_ELASTIC = 23, + EASE_IN_OUT_ELASTIC = 24, + EASE_IN_BACK = 25, + EASE_OUT_BACK = 26, + EASE_IN_OUT_BACK = 27, + EASE_IN_BOUNCE = 28, + EASE_OUT_BOUNCE = 29, + EASE_IN_OUT_BOUNCE = 30, +} + +export namespace FlapFoldPolicy { + export const $gtype: GObject.GType; +} + +export enum FlapFoldPolicy { + NEVER = 0, + ALWAYS = 1, + AUTO = 2, +} + +export namespace FlapTransitionType { + export const $gtype: GObject.GType; +} + +export enum FlapTransitionType { + OVER = 0, + UNDER = 1, + SLIDE = 2, +} + +export namespace FoldThresholdPolicy { + export const $gtype: GObject.GType; +} + +export enum FoldThresholdPolicy { + MINIMUM = 0, + NATURAL = 1, +} + +export namespace LeafletTransitionType { + export const $gtype: GObject.GType; +} + +export enum LeafletTransitionType { + OVER = 0, + UNDER = 1, + SLIDE = 2, +} + +export namespace NavigationDirection { + export const $gtype: GObject.GType; +} + +export enum NavigationDirection { + BACK = 0, + FORWARD = 1, +} + +export namespace SqueezerTransitionType { + export const $gtype: GObject.GType; +} + +export enum SqueezerTransitionType { + NONE = 0, + CROSSFADE = 1, +} + +export namespace ToastPriority { + export const $gtype: GObject.GType; +} + +export enum ToastPriority { + NORMAL = 0, + HIGH = 1, +} + +export namespace ViewSwitcherPolicy { + export const $gtype: GObject.GType; +} + +export enum ViewSwitcherPolicy { + NARROW = 0, + WIDE = 1, +} +export module ActionRow { + export interface ConstructorProperties extends PreferencesRow.ConstructorProperties { + [key: string]: any; + activatable_widget: Gtk.Widget; + activatableWidget: Gtk.Widget; + icon_name: string; + iconName: string; + subtitle: string; + subtitle_lines: number; + subtitleLines: number; + title_lines: number; + titleLines: number; + } +} +export class ActionRow + extends PreferencesRow + implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get activatable_widget(): Gtk.Widget; + set activatable_widget(val: Gtk.Widget); + get activatableWidget(): Gtk.Widget; + set activatableWidget(val: Gtk.Widget); + get icon_name(): string; + set icon_name(val: string); + get iconName(): string; + set iconName(val: string); + get subtitle(): string; + set subtitle(val: string); + get subtitle_lines(): number; + set subtitle_lines(val: number); + get subtitleLines(): number; + set subtitleLines(val: number); + get title_lines(): number; + set title_lines(val: number); + get titleLines(): number; + set titleLines(val: number); + + // Signals + + connect(id: string, callback: (...args: any[]) => any): number; + connect_after(id: string, callback: (...args: any[]) => any): number; + emit(id: string, ...args: any[]): void; + connect(signal: "activated", callback: (_source: this) => void): number; + connect_after(signal: "activated", callback: (_source: this) => void): number; + emit(signal: "activated"): void; + + // Constructors + + static ["new"](): ActionRow; + + // Members + + activate(): void; + // Conflicted with Gtk.Widget.activate + activate(...args: never[]): any; + add_prefix(widget: Gtk.Widget): void; + add_suffix(widget: Gtk.Widget): void; + get_activatable_widget(): Gtk.Widget | null; + get_icon_name(): string | null; + get_subtitle(): string | null; + get_subtitle_lines(): number; + get_title_lines(): number; + remove(widget: Gtk.Widget): void; + set_activatable_widget(widget?: Gtk.Widget | null): void; + set_icon_name(icon_name?: string | null): void; + set_subtitle(subtitle: string): void; + set_subtitle_lines(subtitle_lines: number): void; + set_title_lines(title_lines: number): void; + vfunc_activate(): void; +} +export module Animation { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + state: AnimationState; + target: AnimationTarget; + value: number; + widget: Gtk.Widget; + } +} +export abstract class Animation extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get state(): AnimationState; + get target(): AnimationTarget; + set target(val: AnimationTarget); + get value(): number; + get widget(): Gtk.Widget; + + // Signals + + connect(id: string, callback: (...args: any[]) => any): number; + connect_after(id: string, callback: (...args: any[]) => any): number; + emit(id: string, ...args: any[]): void; + connect(signal: "done", callback: (_source: this) => void): number; + connect_after(signal: "done", callback: (_source: this) => void): number; + emit(signal: "done"): void; + + // Members + + get_state(): AnimationState; + get_target(): AnimationTarget; + get_value(): number; + get_widget(): Gtk.Widget; + pause(): void; + play(): void; + reset(): void; + resume(): void; + skip(): void; +} +export module AnimationTarget { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + } +} +export abstract class AnimationTarget extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; +} +export module Application { + export interface ConstructorProperties extends Gtk.Application.ConstructorProperties { + [key: string]: any; + style_manager: StyleManager; + styleManager: StyleManager; + } +} +export class Application extends Gtk.Application implements Gio.ActionGroup, Gio.ActionMap { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get style_manager(): StyleManager; + get styleManager(): StyleManager; + + // Constructors + + static ["new"](application_id: string | null, flags: Gio.ApplicationFlags): Application; + + // Members + + get_style_manager(): StyleManager; +} +export module ApplicationWindow { + export interface ConstructorProperties extends Gtk.ApplicationWindow.ConstructorProperties { + [key: string]: any; + content: Gtk.Widget; + } +} +export class ApplicationWindow + extends Gtk.ApplicationWindow + implements + Gio.ActionGroup, + Gio.ActionMap, + Gtk.Accessible, + Gtk.Buildable, + Gtk.ConstraintTarget, + Gtk.Native, + Gtk.Root, + Gtk.ShortcutManager { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get content(): Gtk.Widget; + set content(val: Gtk.Widget); + + // Constructors + + static ["new"](app: Gtk.Application): ApplicationWindow; + // Conflicted with Gtk.Window.new + static ["new"](...args: never[]): any; + + // Members + + get_content(): Gtk.Widget | null; + set_content(content?: Gtk.Widget | null): void; + + // Implemented Members + + action_added(action_name: string): void; + action_enabled_changed(action_name: string, enabled: boolean): void; + action_removed(action_name: string): void; + action_state_changed(action_name: string, state: GLib.Variant): void; + activate_action(action_name: string, parameter?: GLib.Variant | null): void; + // Conflicted with Gtk.Widget.activate_action + activate_action(...args: never[]): any; + change_action_state(action_name: string, value: GLib.Variant): void; + get_action_enabled(action_name: string): boolean; + get_action_parameter_type(action_name: string): GLib.VariantType | null; + get_action_state(action_name: string): GLib.Variant | null; + get_action_state_hint(action_name: string): GLib.Variant | null; + get_action_state_type(action_name: string): GLib.VariantType | null; + has_action(action_name: string): boolean; + list_actions(): string[]; + query_action( + action_name: string + ): [boolean, boolean, GLib.VariantType | null, GLib.VariantType | null, GLib.Variant | null, GLib.Variant | null]; + vfunc_action_added(action_name: string): void; + vfunc_action_enabled_changed(action_name: string, enabled: boolean): void; + vfunc_action_removed(action_name: string): void; + vfunc_action_state_changed(action_name: string, state: GLib.Variant): void; + vfunc_activate_action(action_name: string, parameter?: GLib.Variant | null): void; + vfunc_change_action_state(action_name: string, value: GLib.Variant): void; + vfunc_get_action_enabled(action_name: string): boolean; + vfunc_get_action_parameter_type(action_name: string): GLib.VariantType | null; + vfunc_get_action_state(action_name: string): GLib.Variant | null; + vfunc_get_action_state_hint(action_name: string): GLib.Variant | null; + vfunc_get_action_state_type(action_name: string): GLib.VariantType | null; + vfunc_has_action(action_name: string): boolean; + vfunc_list_actions(): string[]; + vfunc_query_action( + action_name: string + ): [boolean, boolean, GLib.VariantType | null, GLib.VariantType | null, GLib.Variant | null, GLib.Variant | null]; + add_action(action: Gio.Action): void; + add_action_entries(entries: Gio.ActionEntry[], user_data?: any | null): void; + lookup_action(action_name: string): Gio.Action | null; + remove_action(action_name: string): void; + vfunc_add_action(action: Gio.Action): void; + vfunc_lookup_action(action_name: string): Gio.Action | null; + vfunc_remove_action(action_name: string): void; +} +export module Avatar { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + custom_image: Gdk.Paintable; + customImage: Gdk.Paintable; + icon_name: string; + iconName: string; + show_initials: boolean; + showInitials: boolean; + size: number; + text: string; + } +} +export class Avatar extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get custom_image(): Gdk.Paintable; + set custom_image(val: Gdk.Paintable); + get customImage(): Gdk.Paintable; + set customImage(val: Gdk.Paintable); + get icon_name(): string; + set icon_name(val: string); + get iconName(): string; + set iconName(val: string); + get show_initials(): boolean; + set show_initials(val: boolean); + get showInitials(): boolean; + set showInitials(val: boolean); + get size(): number; + set size(val: number); + get text(): string; + set text(val: string); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](size: number, text: string | null, show_initials: boolean): Avatar; + + // Members + + draw_to_texture(scale_factor: number): Gdk.Texture; + get_custom_image(): Gdk.Paintable | null; + get_icon_name(): string | null; + get_show_initials(): boolean; + get_size(): number; + get_text(): string | null; + set_custom_image(custom_image?: Gdk.Paintable | null): void; + set_icon_name(icon_name?: string | null): void; + set_show_initials(show_initials: boolean): void; + set_size(size: number): void; + set_text(text?: string | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module Bin { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + } +} +export class Bin extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + set child(val: Gtk.Widget); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): Bin; + + // Members + + get_child(): Gtk.Widget | null; + set_child(child?: Gtk.Widget | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module ButtonContent { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + icon_name: string; + iconName: string; + label: string; + use_underline: boolean; + useUnderline: boolean; + } +} +export class ButtonContent extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get icon_name(): string; + set icon_name(val: string); + get iconName(): string; + set iconName(val: string); + get label(): string; + set label(val: string); + get use_underline(): boolean; + set use_underline(val: boolean); + get useUnderline(): boolean; + set useUnderline(val: boolean); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): ButtonContent; + + // Members + + get_icon_name(): string; + get_label(): string; + get_use_underline(): boolean; + set_icon_name(icon_name: string): void; + set_label(label: string): void; + set_use_underline(use_underline: boolean): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module CallbackAnimationTarget { + export interface ConstructorProperties extends AnimationTarget.ConstructorProperties { + [key: string]: any; + } +} +export class CallbackAnimationTarget extends AnimationTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Constructors + + static ["new"](): CallbackAnimationTarget; +} +export module Carousel { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + allow_long_swipes: boolean; + allowLongSwipes: boolean; + allow_mouse_drag: boolean; + allowMouseDrag: boolean; + allow_scroll_wheel: boolean; + allowScrollWheel: boolean; + interactive: boolean; + n_pages: number; + nPages: number; + position: number; + reveal_duration: number; + revealDuration: number; + scroll_params: SpringParams; + scrollParams: SpringParams; + spacing: number; + } +} +export class Carousel + extends Gtk.Widget + implements Swipeable, Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get allow_long_swipes(): boolean; + set allow_long_swipes(val: boolean); + get allowLongSwipes(): boolean; + set allowLongSwipes(val: boolean); + get allow_mouse_drag(): boolean; + set allow_mouse_drag(val: boolean); + get allowMouseDrag(): boolean; + set allowMouseDrag(val: boolean); + get allow_scroll_wheel(): boolean; + set allow_scroll_wheel(val: boolean); + get allowScrollWheel(): boolean; + set allowScrollWheel(val: boolean); + get interactive(): boolean; + set interactive(val: boolean); + get n_pages(): number; + get nPages(): number; + get position(): number; + get reveal_duration(): number; + set reveal_duration(val: number); + get revealDuration(): number; + set revealDuration(val: number); + get scroll_params(): SpringParams; + set scroll_params(val: SpringParams); + get scrollParams(): SpringParams; + set scrollParams(val: SpringParams); + get spacing(): number; + set spacing(val: number); + + // Signals + + connect(id: string, callback: (...args: any[]) => any): number; + connect_after(id: string, callback: (...args: any[]) => any): number; + emit(id: string, ...args: any[]): void; + connect(signal: "page-changed", callback: (_source: this, index: number) => void): number; + connect_after(signal: "page-changed", callback: (_source: this, index: number) => void): number; + emit(signal: "page-changed", index: number): void; + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](): Carousel; + + // Members + + append(child: Gtk.Widget): void; + get_allow_long_swipes(): boolean; + get_allow_mouse_drag(): boolean; + get_allow_scroll_wheel(): boolean; + get_interactive(): boolean; + get_n_pages(): number; + get_nth_page(n: number): Gtk.Widget; + get_position(): number; + get_reveal_duration(): number; + get_scroll_params(): SpringParams; + get_spacing(): number; + insert(child: Gtk.Widget, position: number): void; + prepend(child: Gtk.Widget): void; + remove(child: Gtk.Widget): void; + reorder(child: Gtk.Widget, position: number): void; + scroll_to(widget: Gtk.Widget, animate: boolean): void; + set_allow_long_swipes(allow_long_swipes: boolean): void; + set_allow_mouse_drag(allow_mouse_drag: boolean): void; + set_allow_scroll_wheel(allow_scroll_wheel: boolean): void; + set_interactive(interactive: boolean): void; + set_reveal_duration(reveal_duration: number): void; + set_scroll_params(params: SpringParams): void; + set_spacing(spacing: number): void; + + // Implemented Members + + get_cancel_progress(): number; + get_distance(): number; + get_progress(): number; + get_snap_points(): number[]; + get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; + vfunc_get_cancel_progress(): number; + vfunc_get_distance(): number; + vfunc_get_progress(): number; + vfunc_get_snap_points(): number[]; + vfunc_get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module CarouselIndicatorDots { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + carousel: Carousel; + } +} +export class CarouselIndicatorDots + extends Gtk.Widget + implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get carousel(): Carousel; + set carousel(val: Carousel); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](): CarouselIndicatorDots; + + // Members + + get_carousel(): Carousel | null; + set_carousel(carousel?: Carousel | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module CarouselIndicatorLines { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + carousel: Carousel; + } +} +export class CarouselIndicatorLines + extends Gtk.Widget + implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get carousel(): Carousel; + set carousel(val: Carousel); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](): CarouselIndicatorLines; + + // Members + + get_carousel(): Carousel | null; + set_carousel(carousel?: Carousel | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module Clamp { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + maximum_size: number; + maximumSize: number; + tightening_threshold: number; + tighteningThreshold: number; + } +} +export class Clamp extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + set child(val: Gtk.Widget); + get maximum_size(): number; + set maximum_size(val: number); + get maximumSize(): number; + set maximumSize(val: number); + get tightening_threshold(): number; + set tightening_threshold(val: number); + get tighteningThreshold(): number; + set tighteningThreshold(val: number); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](): Clamp; + + // Members + + get_child(): Gtk.Widget | null; + get_maximum_size(): number; + get_tightening_threshold(): number; + set_child(child?: Gtk.Widget | null): void; + set_maximum_size(maximum_size: number): void; + set_tightening_threshold(tightening_threshold: number): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module ClampLayout { + export interface ConstructorProperties extends Gtk.LayoutManager.ConstructorProperties { + [key: string]: any; + maximum_size: number; + maximumSize: number; + tightening_threshold: number; + tighteningThreshold: number; + } +} +export class ClampLayout extends Gtk.LayoutManager implements Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get maximum_size(): number; + set maximum_size(val: number); + get maximumSize(): number; + set maximumSize(val: number); + get tightening_threshold(): number; + set tightening_threshold(val: number); + get tighteningThreshold(): number; + set tighteningThreshold(val: number); + + // Implemented Properties + + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](): ClampLayout; + + // Members + + get_maximum_size(): number; + get_tightening_threshold(): number; + set_maximum_size(maximum_size: number): void; + set_tightening_threshold(tightening_threshold: number): void; + + // Implemented Members + + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module ClampScrollable { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + maximum_size: number; + maximumSize: number; + tightening_threshold: number; + tighteningThreshold: number; + } +} +export class ClampScrollable + extends Gtk.Widget + implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable, Gtk.Scrollable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + set child(val: Gtk.Widget); + get maximum_size(): number; + set maximum_size(val: number); + get maximumSize(): number; + set maximumSize(val: number); + get tightening_threshold(): number; + set tightening_threshold(val: number); + get tighteningThreshold(): number; + set tighteningThreshold(val: number); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + get hadjustment(): Gtk.Adjustment; + set hadjustment(val: Gtk.Adjustment); + get hscroll_policy(): Gtk.ScrollablePolicy; + set hscroll_policy(val: Gtk.ScrollablePolicy); + get hscrollPolicy(): Gtk.ScrollablePolicy; + set hscrollPolicy(val: Gtk.ScrollablePolicy); + get vadjustment(): Gtk.Adjustment; + set vadjustment(val: Gtk.Adjustment); + get vscroll_policy(): Gtk.ScrollablePolicy; + set vscroll_policy(val: Gtk.ScrollablePolicy); + get vscrollPolicy(): Gtk.ScrollablePolicy; + set vscrollPolicy(val: Gtk.ScrollablePolicy); + + // Constructors + + static ["new"](): ClampScrollable; + + // Members + + get_child(): Gtk.Widget | null; + get_maximum_size(): number; + get_tightening_threshold(): number; + set_child(child?: Gtk.Widget | null): void; + set_maximum_size(maximum_size: number): void; + set_tightening_threshold(tightening_threshold: number): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; + get_border(): [boolean, Gtk.Border]; + get_hadjustment(): Gtk.Adjustment | null; + get_hscroll_policy(): Gtk.ScrollablePolicy; + get_vadjustment(): Gtk.Adjustment | null; + get_vscroll_policy(): Gtk.ScrollablePolicy; + set_hadjustment(hadjustment?: Gtk.Adjustment | null): void; + set_hscroll_policy(policy: Gtk.ScrollablePolicy): void; + set_vadjustment(vadjustment?: Gtk.Adjustment | null): void; + set_vscroll_policy(policy: Gtk.ScrollablePolicy): void; + vfunc_get_border(): [boolean, Gtk.Border]; +} +export module ComboRow { + export interface ConstructorProperties extends ActionRow.ConstructorProperties { + [key: string]: any; + expression: Gtk.Expression; + factory: Gtk.ListItemFactory; + list_factory: Gtk.ListItemFactory; + listFactory: Gtk.ListItemFactory; + model: Gio.ListModel; + selected: number; + selected_item: GObject.Object; + selectedItem: GObject.Object; + use_subtitle: boolean; + useSubtitle: boolean; + } +} +export class ComboRow extends ActionRow implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get expression(): Gtk.Expression; + set expression(val: Gtk.Expression); + get factory(): Gtk.ListItemFactory; + set factory(val: Gtk.ListItemFactory); + get list_factory(): Gtk.ListItemFactory; + set list_factory(val: Gtk.ListItemFactory); + get listFactory(): Gtk.ListItemFactory; + set listFactory(val: Gtk.ListItemFactory); + get model(): Gio.ListModel; + set model(val: Gio.ListModel); + get selected(): number; + set selected(val: number); + get selected_item(): GObject.Object; + get selectedItem(): GObject.Object; + get use_subtitle(): boolean; + set use_subtitle(val: boolean); + get useSubtitle(): boolean; + set useSubtitle(val: boolean); + + // Constructors + + static ["new"](): ComboRow; + + // Members + + get_expression(): Gtk.Expression | null; + get_factory(): Gtk.ListItemFactory | null; + get_list_factory(): Gtk.ListItemFactory | null; + get_model(): Gio.ListModel | null; + get_selected(): number; + get_selected_item(): T; + get_use_subtitle(): boolean; + set_expression(expression?: Gtk.Expression | null): void; + set_factory(factory?: Gtk.ListItemFactory | null): void; + set_list_factory(factory?: Gtk.ListItemFactory | null): void; + set_model(model?: Gio.ListModel | null): void; + set_selected(position: number): void; + set_use_subtitle(use_subtitle: boolean): void; +} +export module EnumListItem { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + name: string; + nick: string; + value: number; + } +} +export class EnumListItem extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get name(): string; + get nick(): string; + get value(): number; + + // Members + + get_name(): string; + get_nick(): string; + get_value(): number; +} +export module EnumListModel { + export interface ConstructorProperties + extends GObject.Object.ConstructorProperties { + [key: string]: any; + enum_type: GObject.GType; + enumType: GObject.GType; + } +} +export class EnumListModel + extends GObject.Object + implements Gio.ListModel +{ + static $gtype: GObject.GType; + + constructor(properties?: Partial>, ...args: any[]); + _init(properties?: Partial>, ...args: any[]): void; + + // Properties + get enum_type(): GObject.GType; + get enumType(): GObject.GType; + + // Constructors + + static ["new"](enum_type: GObject.GType): EnumListModel; + + // Members + + find_position(value: number): number; + get_enum_type(): GObject.GType; + + // Implemented Members + + get_item_type(): GObject.GType; + get_n_items(): number; + get_item(position: number): A | null; + items_changed(position: number, removed: number, added: number): void; + vfunc_get_item(position: number): A | null; + vfunc_get_item_type(): GObject.GType; + vfunc_get_n_items(): number; +} +export module EntryRow { + export interface ConstructorProperties extends PreferencesRow.ConstructorProperties { + [key: string]: any; + activates_default: boolean; + attributes: unknown; + enable_emoji_completion: boolean; + input_hints: unknown; + input_purpose: unknown; + show_apply_button: boolean; + test: string; + } +} +export class EntryRow extends PreferencesRow implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.Editable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get activates_default(): boolean; + set activates_default(val: boolean); + get attributes(): unknown; + set attributes(val: unknown); + get enable_emoji_completion(): boolean; + set enable_emoji_completion(val: boolean); + get input_hints(): unknown; + set input_hints(val: unknown); + get input_purpose(): unknown; + set input_purpose(val: unknown); + get show_apply_button(): boolean; + set show_apply_button(val: boolean); + get text(): string; + set text(val: string); + + static ["new"](): EntryRow; + + add_prefix(val: Gtk.Widget): void; + add_suffix(val: Gtk.Widget): void; + remove(val: Gtk.Widget): void; + + // autogenerated: + + cursor_position: number; + cursorPosition: number; + editable: boolean; + enable_undo: boolean; + enableUndo: boolean; + max_width_chars: number; + maxWidthChars: number; + selection_bound: number; + selectionBound: number; + width_chars: number; + widthChars: number; + xalign: number; + delete_selection(): void; + delete_text(start_pos: number, end_pos: number): void; + finish_delegate(): void; + get_alignment(): number; + get_chars(start_pos: number, end_pos: number): string; + get_delegate(): Gtk.EditablePrototype | null; + get_editable(): boolean; + get_enable_undo(): boolean; + get_max_width_chars(): number; + get_position(): number; + get_selection_bounds(): [boolean, number, number]; + get_text(): string; + get_width_chars(): number; + init_delegate(): void; + insert_text(text: string, length: number, position: number): number; + select_region(start_pos: number, end_pos: number): void; + set_alignment(xalign: number): void; + set_editable(is_editable: boolean): void; + set_enable_undo(enable_undo: boolean): void; + set_max_width_chars(n_chars: number): void; + set_position(position: number): void; + set_text(text: string): void; + set_width_chars(n_chars: number): void; + vfunc_changed(): void; + vfunc_delete_text(start_pos: number, end_pos: number): void; + vfunc_do_delete_text(start_pos: number, end_pos: number): void; + vfunc_do_insert_text(text: string, length: number, position: number): number; + vfunc_get_delegate(): Gtk.EditablePrototype | null; + vfunc_get_selection_bounds(): [boolean, number, number]; + vfunc_get_text(): string; + vfunc_insert_text(text: string, length: number, position: number): number; + vfunc_set_selection_bounds(start_pos: number, end_pos: number): void; +} +export module ExpanderRow { + export interface ConstructorProperties extends PreferencesRow.ConstructorProperties { + [key: string]: any; + enable_expansion: boolean; + enableExpansion: boolean; + expanded: boolean; + icon_name: string; + iconName: string; + show_enable_switch: boolean; + showEnableSwitch: boolean; + subtitle: string; + } +} +export class ExpanderRow + extends PreferencesRow + implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get enable_expansion(): boolean; + set enable_expansion(val: boolean); + get enableExpansion(): boolean; + set enableExpansion(val: boolean); + get expanded(): boolean; + set expanded(val: boolean); + get icon_name(): string; + set icon_name(val: string); + get iconName(): string; + set iconName(val: string); + get show_enable_switch(): boolean; + set show_enable_switch(val: boolean); + get showEnableSwitch(): boolean; + set showEnableSwitch(val: boolean); + get subtitle(): string; + set subtitle(val: string); + + // Constructors + + static ["new"](): ExpanderRow; + + // Members + + add_action(widget: Gtk.Widget): void; + add_prefix(widget: Gtk.Widget): void; + add_row(child: Gtk.Widget): void; + get_enable_expansion(): boolean; + get_expanded(): boolean; + get_icon_name(): string | null; + get_show_enable_switch(): boolean; + get_subtitle(): string; + remove(child: Gtk.Widget): void; + set_enable_expansion(enable_expansion: boolean): void; + set_expanded(expanded: boolean): void; + set_icon_name(icon_name?: string | null): void; + set_show_enable_switch(show_enable_switch: boolean): void; + set_subtitle(subtitle: string): void; +} +export module Flap { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + content: Gtk.Widget; + flap: Gtk.Widget; + flap_position: Gtk.PackType; + flapPosition: Gtk.PackType; + fold_duration: number; + foldDuration: number; + fold_policy: FlapFoldPolicy; + foldPolicy: FlapFoldPolicy; + fold_threshold_policy: FoldThresholdPolicy; + foldThresholdPolicy: FoldThresholdPolicy; + folded: boolean; + locked: boolean; + modal: boolean; + reveal_flap: boolean; + revealFlap: boolean; + reveal_params: SpringParams; + revealParams: SpringParams; + reveal_progress: number; + revealProgress: number; + separator: Gtk.Widget; + swipe_to_close: boolean; + swipeToClose: boolean; + swipe_to_open: boolean; + swipeToOpen: boolean; + transition_type: FlapTransitionType; + transitionType: FlapTransitionType; + } +} +export class Flap + extends Gtk.Widget + implements Swipeable, Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get content(): Gtk.Widget; + set content(val: Gtk.Widget); + get flap(): Gtk.Widget; + set flap(val: Gtk.Widget); + get flap_position(): Gtk.PackType; + set flap_position(val: Gtk.PackType); + get flapPosition(): Gtk.PackType; + set flapPosition(val: Gtk.PackType); + get fold_duration(): number; + set fold_duration(val: number); + get foldDuration(): number; + set foldDuration(val: number); + get fold_policy(): FlapFoldPolicy; + set fold_policy(val: FlapFoldPolicy); + get foldPolicy(): FlapFoldPolicy; + set foldPolicy(val: FlapFoldPolicy); + get fold_threshold_policy(): FoldThresholdPolicy; + set fold_threshold_policy(val: FoldThresholdPolicy); + get foldThresholdPolicy(): FoldThresholdPolicy; + set foldThresholdPolicy(val: FoldThresholdPolicy); + get folded(): boolean; + get locked(): boolean; + set locked(val: boolean); + get modal(): boolean; + set modal(val: boolean); + get reveal_flap(): boolean; + set reveal_flap(val: boolean); + get revealFlap(): boolean; + set revealFlap(val: boolean); + get reveal_params(): SpringParams; + set reveal_params(val: SpringParams); + get revealParams(): SpringParams; + set revealParams(val: SpringParams); + get reveal_progress(): number; + get revealProgress(): number; + get separator(): Gtk.Widget; + set separator(val: Gtk.Widget); + get swipe_to_close(): boolean; + set swipe_to_close(val: boolean); + get swipeToClose(): boolean; + set swipeToClose(val: boolean); + get swipe_to_open(): boolean; + set swipe_to_open(val: boolean); + get swipeToOpen(): boolean; + set swipeToOpen(val: boolean); + get transition_type(): FlapTransitionType; + set transition_type(val: FlapTransitionType); + get transitionType(): FlapTransitionType; + set transitionType(val: FlapTransitionType); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](): Flap; + + // Members + + get_content(): Gtk.Widget | null; + get_flap(): Gtk.Widget | null; + get_flap_position(): Gtk.PackType; + get_fold_duration(): number; + get_fold_policy(): FlapFoldPolicy; + get_fold_threshold_policy(): FoldThresholdPolicy; + get_folded(): boolean; + get_locked(): boolean; + get_modal(): boolean; + get_reveal_flap(): boolean; + get_reveal_params(): SpringParams; + get_reveal_progress(): number; + get_separator(): Gtk.Widget | null; + get_swipe_to_close(): boolean; + get_swipe_to_open(): boolean; + get_transition_type(): FlapTransitionType; + set_content(content?: Gtk.Widget | null): void; + set_flap(flap?: Gtk.Widget | null): void; + set_flap_position(position: Gtk.PackType): void; + set_fold_duration(duration: number): void; + set_fold_policy(policy: FlapFoldPolicy): void; + set_fold_threshold_policy(policy: FoldThresholdPolicy): void; + set_locked(locked: boolean): void; + set_modal(modal: boolean): void; + set_reveal_flap(reveal_flap: boolean): void; + set_reveal_params(params: SpringParams): void; + set_separator(separator?: Gtk.Widget | null): void; + set_swipe_to_close(swipe_to_close: boolean): void; + set_swipe_to_open(swipe_to_open: boolean): void; + set_transition_type(transition_type: FlapTransitionType): void; + + // Implemented Members + + get_cancel_progress(): number; + get_distance(): number; + get_progress(): number; + get_snap_points(): number[]; + get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; + vfunc_get_cancel_progress(): number; + vfunc_get_distance(): number; + vfunc_get_progress(): number; + vfunc_get_snap_points(): number[]; + vfunc_get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module HeaderBar { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + centering_policy: CenteringPolicy; + centeringPolicy: CenteringPolicy; + decoration_layout: string; + decorationLayout: string; + show_end_title_buttons: boolean; + showEndTitleButtons: boolean; + show_start_title_buttons: boolean; + showStartTitleButtons: boolean; + title_widget: Gtk.Widget; + titleWidget: Gtk.Widget; + } +} +export class HeaderBar extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get centering_policy(): CenteringPolicy; + set centering_policy(val: CenteringPolicy); + get centeringPolicy(): CenteringPolicy; + set centeringPolicy(val: CenteringPolicy); + get decoration_layout(): string; + set decoration_layout(val: string); + get decorationLayout(): string; + set decorationLayout(val: string); + get show_end_title_buttons(): boolean; + set show_end_title_buttons(val: boolean); + get showEndTitleButtons(): boolean; + set showEndTitleButtons(val: boolean); + get show_start_title_buttons(): boolean; + set show_start_title_buttons(val: boolean); + get showStartTitleButtons(): boolean; + set showStartTitleButtons(val: boolean); + get title_widget(): Gtk.Widget; + set title_widget(val: Gtk.Widget); + get titleWidget(): Gtk.Widget; + set titleWidget(val: Gtk.Widget); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): HeaderBar; + + // Members + + get_centering_policy(): CenteringPolicy; + get_decoration_layout(): string | null; + get_show_end_title_buttons(): boolean; + get_show_start_title_buttons(): boolean; + get_title_widget(): Gtk.Widget | null; + pack_end(child: Gtk.Widget): void; + pack_start(child: Gtk.Widget): void; + remove(child: Gtk.Widget): void; + set_centering_policy(centering_policy: CenteringPolicy): void; + set_decoration_layout(layout?: string | null): void; + set_show_end_title_buttons(setting: boolean): void; + set_show_start_title_buttons(setting: boolean): void; + set_title_widget(title_widget?: Gtk.Widget | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module Leaflet { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + can_navigate_back: boolean; + canNavigateBack: boolean; + can_navigate_forward: boolean; + canNavigateForward: boolean; + can_unfold: boolean; + canUnfold: boolean; + child_transition_params: SpringParams; + childTransitionParams: SpringParams; + child_transition_running: boolean; + childTransitionRunning: boolean; + fold_threshold_policy: FoldThresholdPolicy; + foldThresholdPolicy: FoldThresholdPolicy; + folded: boolean; + homogeneous: boolean; + mode_transition_duration: number; + modeTransitionDuration: number; + pages: Gtk.SelectionModel; + transition_type: LeafletTransitionType; + transitionType: LeafletTransitionType; + visible_child: Gtk.Widget; + visibleChild: Gtk.Widget; + visible_child_name: string; + visibleChildName: string; + } +} +export class Leaflet + extends Gtk.Widget + implements Swipeable, Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get can_navigate_back(): boolean; + set can_navigate_back(val: boolean); + get canNavigateBack(): boolean; + set canNavigateBack(val: boolean); + get can_navigate_forward(): boolean; + set can_navigate_forward(val: boolean); + get canNavigateForward(): boolean; + set canNavigateForward(val: boolean); + get can_unfold(): boolean; + set can_unfold(val: boolean); + get canUnfold(): boolean; + set canUnfold(val: boolean); + get child_transition_params(): SpringParams; + set child_transition_params(val: SpringParams); + get childTransitionParams(): SpringParams; + set childTransitionParams(val: SpringParams); + get child_transition_running(): boolean; + get childTransitionRunning(): boolean; + get fold_threshold_policy(): FoldThresholdPolicy; + set fold_threshold_policy(val: FoldThresholdPolicy); + get foldThresholdPolicy(): FoldThresholdPolicy; + set foldThresholdPolicy(val: FoldThresholdPolicy); + get folded(): boolean; + get homogeneous(): boolean; + set homogeneous(val: boolean); + get mode_transition_duration(): number; + set mode_transition_duration(val: number); + get modeTransitionDuration(): number; + set modeTransitionDuration(val: number); + get pages(): Gtk.SelectionModel; + get transition_type(): LeafletTransitionType; + set transition_type(val: LeafletTransitionType); + get transitionType(): LeafletTransitionType; + set transitionType(val: LeafletTransitionType); + get visible_child(): Gtk.Widget; + set visible_child(val: Gtk.Widget); + get visibleChild(): Gtk.Widget; + set visibleChild(val: Gtk.Widget); + get visible_child_name(): string; + set visible_child_name(val: string); + get visibleChildName(): string; + set visibleChildName(val: string); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](): Leaflet; + + // Members + + append(child: Gtk.Widget): LeafletPage; + get_adjacent_child(direction: NavigationDirection): Gtk.Widget | null; + get_can_navigate_back(): boolean; + get_can_navigate_forward(): boolean; + get_can_unfold(): boolean; + get_child_by_name(name: string): Gtk.Widget | null; + get_child_transition_params(): SpringParams; + get_child_transition_running(): boolean; + get_fold_threshold_policy(): FoldThresholdPolicy; + get_folded(): boolean; + get_homogeneous(): boolean; + get_mode_transition_duration(): number; + get_page(child: Gtk.Widget): LeafletPage; + get_pages(): Gtk.SelectionModel; + get_transition_type(): LeafletTransitionType; + get_visible_child(): Gtk.Widget | null; + get_visible_child_name(): string | null; + insert_child_after(child: Gtk.Widget, sibling?: Gtk.Widget | null): LeafletPage; + navigate(direction: NavigationDirection): boolean; + prepend(child: Gtk.Widget): LeafletPage; + remove(child: Gtk.Widget): void; + reorder_child_after(child: Gtk.Widget, sibling?: Gtk.Widget | null): void; + set_can_navigate_back(can_navigate_back: boolean): void; + set_can_navigate_forward(can_navigate_forward: boolean): void; + set_can_unfold(can_unfold: boolean): void; + set_child_transition_params(params: SpringParams): void; + set_fold_threshold_policy(policy: FoldThresholdPolicy): void; + set_homogeneous(homogeneous: boolean): void; + set_mode_transition_duration(duration: number): void; + set_transition_type(transition: LeafletTransitionType): void; + set_visible_child(visible_child: Gtk.Widget): void; + set_visible_child_name(name: string): void; + + // Implemented Members + + get_cancel_progress(): number; + get_distance(): number; + get_progress(): number; + get_snap_points(): number[]; + get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; + vfunc_get_cancel_progress(): number; + vfunc_get_distance(): number; + vfunc_get_progress(): number; + vfunc_get_snap_points(): number[]; + vfunc_get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module LeafletPage { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + name: string; + navigatable: boolean; + } +} +export class LeafletPage extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + get name(): string; + set name(val: string); + get navigatable(): boolean; + set navigatable(val: boolean); + + // Members + + get_child(): Gtk.Widget; + get_name(): string | null; + get_navigatable(): boolean; + set_name(name?: string | null): void; + set_navigatable(navigatable: boolean): void; +} +export module PreferencesGroup { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + description: string; + header_suffix: Gtk.Widget; + headerSuffix: Gtk.Widget; + title: string; + } +} +export class PreferencesGroup extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get description(): string; + set description(val: string); + get header_suffix(): Gtk.Widget; + set header_suffix(val: Gtk.Widget); + get headerSuffix(): Gtk.Widget; + set headerSuffix(val: Gtk.Widget); + get title(): string; + set title(val: string); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): PreferencesGroup; + + // Members + + add(child: Gtk.Widget): void; + get_description(): string | null; + get_header_suffix(): Gtk.Widget | null; + get_title(): string; + remove(child: Gtk.Widget): void; + set_description(description?: string | null): void; + set_header_suffix(suffix?: Gtk.Widget | null): void; + set_title(title: string): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module PreferencesPage { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + icon_name: string; + iconName: string; + name: string; + title: string; + use_underline: boolean; + useUnderline: boolean; + } +} +export class PreferencesPage extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get icon_name(): string; + set icon_name(val: string); + get iconName(): string; + set iconName(val: string); + get name(): string; + set name(val: string); + get title(): string; + set title(val: string); + get use_underline(): boolean; + set use_underline(val: boolean); + get useUnderline(): boolean; + set useUnderline(val: boolean); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): PreferencesPage; + + // Members + + add(group: PreferencesGroup): void; + get_icon_name(): string | null; + get_name(): string | null; + // Conflicted with Gtk.Widget.get_name + get_name(...args: never[]): any; + get_title(): string; + get_use_underline(): boolean; + remove(group: PreferencesGroup): void; + set_icon_name(icon_name?: string | null): void; + set_name(name?: string | null): void; + // Conflicted with Gtk.Widget.set_name + set_name(...args: never[]): any; + set_title(title: string): void; + set_use_underline(use_underline: boolean): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module PreferencesRow { + export interface ConstructorProperties extends Gtk.ListBoxRow.ConstructorProperties { + [key: string]: any; + title: string; + title_selectable: boolean; + titleSelectable: boolean; + use_underline: boolean; + useUnderline: boolean; + } +} +export class PreferencesRow + extends Gtk.ListBoxRow + implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get title(): string; + set title(val: string); + get title_selectable(): boolean; + set title_selectable(val: boolean); + get titleSelectable(): boolean; + set titleSelectable(val: boolean); + get use_underline(): boolean; + set use_underline(val: boolean); + get useUnderline(): boolean; + set useUnderline(val: boolean); + + // Implemented Properties + + get action_name(): string; + set action_name(val: string); + get actionName(): string; + set actionName(val: string); + get action_target(): GLib.Variant; + set action_target(val: GLib.Variant); + get actionTarget(): GLib.Variant; + set actionTarget(val: GLib.Variant); + + // Constructors + + static ["new"](): PreferencesRow; + + // Members + + get_title(): string; + get_title_selectable(): boolean; + get_use_underline(): boolean; + set_title(title: string): void; + set_title_selectable(title_selectable: boolean): void; + set_use_underline(use_underline: boolean): void; + + // Implemented Members + + get_action_name(): string | null; + get_action_target_value(): GLib.Variant | null; + set_action_name(action_name?: string | null): void; + set_action_target_value(target_value?: GLib.Variant | null): void; + set_detailed_action_name(detailed_action_name: string): void; + vfunc_get_action_name(): string | null; + vfunc_get_action_target_value(): GLib.Variant | null; + vfunc_set_action_name(action_name?: string | null): void; + vfunc_set_action_target_value(target_value?: GLib.Variant | null): void; +} +export module PreferencesWindow { + export interface ConstructorProperties extends Window.ConstructorProperties { + [key: string]: any; + can_navigate_back: boolean; + canNavigateBack: boolean; + search_enabled: boolean; + searchEnabled: boolean; + visible_page: Gtk.Widget; + visiblePage: Gtk.Widget; + visible_page_name: string; + visiblePageName: string; + } +} +export class PreferencesWindow + extends Window + implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Native, Gtk.Root, Gtk.ShortcutManager { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get can_navigate_back(): boolean; + set can_navigate_back(val: boolean); + get canNavigateBack(): boolean; + set canNavigateBack(val: boolean); + get search_enabled(): boolean; + set search_enabled(val: boolean); + get searchEnabled(): boolean; + set searchEnabled(val: boolean); + get visible_page(): Gtk.Widget; + set visible_page(val: Gtk.Widget); + get visiblePage(): Gtk.Widget; + set visiblePage(val: Gtk.Widget); + get visible_page_name(): string; + set visible_page_name(val: string); + get visiblePageName(): string; + set visiblePageName(val: string); + + // Constructors + + static ["new"](): PreferencesWindow; + + // Members + + add(page: PreferencesPage): void; + add_toast(toast: Toast): void; + close_subpage(): void; + get_can_navigate_back(): boolean; + get_search_enabled(): boolean; + get_visible_page(): PreferencesPage | null; + get_visible_page_name(): string | null; + present_subpage(subpage: Gtk.Widget): void; + remove(page: PreferencesPage): void; + set_can_navigate_back(can_navigate_back: boolean): void; + set_search_enabled(search_enabled: boolean): void; + set_visible_page(page: PreferencesPage): void; + set_visible_page_name(name: string): void; +} +export module SplitButton { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + direction: Gtk.ArrowType; + icon_name: string; + iconName: string; + label: string; + menu_model: Gio.MenuModel; + menuModel: Gio.MenuModel; + popover: Gtk.Popover; + use_underline: boolean; + useUnderline: boolean; + } +} +export class SplitButton + extends Gtk.Widget + implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + set child(val: Gtk.Widget); + get direction(): Gtk.ArrowType; + set direction(val: Gtk.ArrowType); + get icon_name(): string; + set icon_name(val: string); + get iconName(): string; + set iconName(val: string); + get label(): string; + set label(val: string); + get menu_model(): Gio.MenuModel; + set menu_model(val: Gio.MenuModel); + get menuModel(): Gio.MenuModel; + set menuModel(val: Gio.MenuModel); + get popover(): Gtk.Popover; + set popover(val: Gtk.Popover); + get use_underline(): boolean; + set use_underline(val: boolean); + get useUnderline(): boolean; + set useUnderline(val: boolean); + + // Signals + + connect(id: string, callback: (...args: any[]) => any): number; + connect_after(id: string, callback: (...args: any[]) => any): number; + emit(id: string, ...args: any[]): void; + connect(signal: "activate", callback: (_source: this) => void): number; + connect_after(signal: "activate", callback: (_source: this) => void): number; + emit(signal: "activate"): void; + connect(signal: "clicked", callback: (_source: this) => void): number; + connect_after(signal: "clicked", callback: (_source: this) => void): number; + emit(signal: "clicked"): void; + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get action_name(): string; + set action_name(val: string); + get actionName(): string; + set actionName(val: string); + get action_target(): GLib.Variant; + set action_target(val: GLib.Variant); + get actionTarget(): GLib.Variant; + set actionTarget(val: GLib.Variant); + + // Constructors + + static ["new"](): SplitButton; + + // Members + + get_child(): Gtk.Widget | null; + get_direction(): Gtk.ArrowType; + // Conflicted with Gtk.Widget.get_direction + get_direction(...args: never[]): any; + get_icon_name(): string | null; + get_label(): string | null; + get_menu_model(): Gio.MenuModel | null; + get_popover(): Gtk.Popover | null; + get_use_underline(): boolean; + popdown(): void; + popup(): void; + set_child(child?: Gtk.Widget | null): void; + set_direction(direction: Gtk.ArrowType): void; + // Conflicted with Gtk.Widget.set_direction + set_direction(...args: never[]): any; + set_icon_name(icon_name: string): void; + set_label(label: string): void; + set_menu_model(menu_model?: Gio.MenuModel | null): void; + set_popover(popover?: Gtk.Popover | null): void; + set_use_underline(use_underline: boolean): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_action_name(): string | null; + get_action_target_value(): GLib.Variant | null; + set_action_name(action_name?: string | null): void; + set_action_target_value(target_value?: GLib.Variant | null): void; + set_detailed_action_name(detailed_action_name: string): void; + vfunc_get_action_name(): string | null; + vfunc_get_action_target_value(): GLib.Variant | null; + vfunc_set_action_name(action_name?: string | null): void; + vfunc_set_action_target_value(target_value?: GLib.Variant | null): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module SpringAnimation { + export interface ConstructorProperties extends Animation.ConstructorProperties { + [key: string]: any; + clamp: boolean; + epsilon: number; + estimated_duration: number; + estimatedDuration: number; + initial_velocity: number; + initialVelocity: number; + spring_params: SpringParams; + springParams: SpringParams; + value_from: number; + valueFrom: number; + value_to: number; + valueTo: number; + velocity: number; + } +} +export class SpringAnimation extends Animation { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get clamp(): boolean; + set clamp(val: boolean); + get epsilon(): number; + set epsilon(val: number); + get estimated_duration(): number; + get estimatedDuration(): number; + get initial_velocity(): number; + set initial_velocity(val: number); + get initialVelocity(): number; + set initialVelocity(val: number); + get spring_params(): SpringParams; + set spring_params(val: SpringParams); + get springParams(): SpringParams; + set springParams(val: SpringParams); + get value_from(): number; + set value_from(val: number); + get valueFrom(): number; + set valueFrom(val: number); + get value_to(): number; + set value_to(val: number); + get valueTo(): number; + set valueTo(val: number); + get velocity(): number; + + // Constructors + + static ["new"]( + widget: Gtk.Widget, + from: number, + to: number, + spring_params: SpringParams, + target: AnimationTarget + ): SpringAnimation; + + // Members + + get_clamp(): boolean; + get_epsilon(): number; + get_estimated_duration(): number; + get_initial_velocity(): number; + get_spring_params(): SpringParams; + get_value_from(): number; + get_value_to(): number; + get_velocity(): number; + set_clamp(clamp: boolean): void; + set_epsilon(epsilon: number): void; + set_initial_velocity(velocity: number): void; + set_spring_params(spring_params: SpringParams): void; + set_value_from(value: number): void; + set_value_to(value: number): void; +} +export module Squeezer { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + allow_none: boolean; + allowNone: boolean; + homogeneous: boolean; + interpolate_size: boolean; + interpolateSize: boolean; + pages: Gtk.SelectionModel; + switch_threshold_policy: FoldThresholdPolicy; + switchThresholdPolicy: FoldThresholdPolicy; + transition_duration: number; + transitionDuration: number; + transition_running: boolean; + transitionRunning: boolean; + transition_type: SqueezerTransitionType; + transitionType: SqueezerTransitionType; + visible_child: Gtk.Widget; + visibleChild: Gtk.Widget; + xalign: number; + yalign: number; + } +} +export class Squeezer + extends Gtk.Widget + implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get allow_none(): boolean; + set allow_none(val: boolean); + get allowNone(): boolean; + set allowNone(val: boolean); + get homogeneous(): boolean; + set homogeneous(val: boolean); + get interpolate_size(): boolean; + set interpolate_size(val: boolean); + get interpolateSize(): boolean; + set interpolateSize(val: boolean); + get pages(): Gtk.SelectionModel; + get switch_threshold_policy(): FoldThresholdPolicy; + set switch_threshold_policy(val: FoldThresholdPolicy); + get switchThresholdPolicy(): FoldThresholdPolicy; + set switchThresholdPolicy(val: FoldThresholdPolicy); + get transition_duration(): number; + set transition_duration(val: number); + get transitionDuration(): number; + set transitionDuration(val: number); + get transition_running(): boolean; + get transitionRunning(): boolean; + get transition_type(): SqueezerTransitionType; + set transition_type(val: SqueezerTransitionType); + get transitionType(): SqueezerTransitionType; + set transitionType(val: SqueezerTransitionType); + get visible_child(): Gtk.Widget; + get visibleChild(): Gtk.Widget; + get xalign(): number; + set xalign(val: number); + get yalign(): number; + set yalign(val: number); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](): Squeezer; + + // Members + + add(child: Gtk.Widget): SqueezerPage; + get_allow_none(): boolean; + get_homogeneous(): boolean; + get_interpolate_size(): boolean; + get_page(child: Gtk.Widget): SqueezerPage; + get_pages(): Gtk.SelectionModel; + get_switch_threshold_policy(): FoldThresholdPolicy; + get_transition_duration(): number; + get_transition_running(): boolean; + get_transition_type(): SqueezerTransitionType; + get_visible_child(): Gtk.Widget | null; + get_xalign(): number; + get_yalign(): number; + remove(child: Gtk.Widget): void; + set_allow_none(allow_none: boolean): void; + set_homogeneous(homogeneous: boolean): void; + set_interpolate_size(interpolate_size: boolean): void; + set_switch_threshold_policy(policy: FoldThresholdPolicy): void; + set_transition_duration(duration: number): void; + set_transition_type(transition: SqueezerTransitionType): void; + set_xalign(xalign: number): void; + set_yalign(yalign: number): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module SqueezerPage { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + enabled: boolean; + } +} +export class SqueezerPage extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + get enabled(): boolean; + set enabled(val: boolean); + + // Members + + get_child(): Gtk.Widget; + get_enabled(): boolean; + set_enabled(enabled: boolean): void; +} +export module StatusPage { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + description: string; + icon_name: string; + iconName: string; + paintable: Gdk.Paintable; + title: string; + } +} +export class StatusPage extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + set child(val: Gtk.Widget); + get description(): string; + set description(val: string); + get icon_name(): string; + set icon_name(val: string); + get iconName(): string; + set iconName(val: string); + get paintable(): Gdk.Paintable; + set paintable(val: Gdk.Paintable); + get title(): string; + set title(val: string); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): StatusPage; + + // Members + + get_child(): Gtk.Widget | null; + get_description(): string | null; + get_icon_name(): string | null; + get_paintable(): Gdk.Paintable | null; + get_title(): string; + set_child(child?: Gtk.Widget | null): void; + set_description(description?: string | null): void; + set_icon_name(icon_name?: string | null): void; + set_paintable(paintable?: Gdk.Paintable | null): void; + set_title(title: string): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module StyleManager { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + color_scheme: ColorScheme; + colorScheme: ColorScheme; + dark: boolean; + display: Gdk.Display; + high_contrast: boolean; + highContrast: boolean; + system_supports_color_schemes: boolean; + systemSupportsColorSchemes: boolean; + } +} +export class StyleManager extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get color_scheme(): ColorScheme; + set color_scheme(val: ColorScheme); + get colorScheme(): ColorScheme; + set colorScheme(val: ColorScheme); + get dark(): boolean; + get display(): Gdk.Display; + get high_contrast(): boolean; + get highContrast(): boolean; + get system_supports_color_schemes(): boolean; + get systemSupportsColorSchemes(): boolean; + + // Members + + get_color_scheme(): ColorScheme; + get_dark(): boolean; + get_display(): Gdk.Display; + get_high_contrast(): boolean; + get_system_supports_color_schemes(): boolean; + set_color_scheme(color_scheme: ColorScheme): void; + static get_default(): StyleManager; + static get_for_display(display: Gdk.Display): StyleManager; +} +export module SwipeTracker { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + allow_long_swipes: boolean; + allowLongSwipes: boolean; + allow_mouse_drag: boolean; + allowMouseDrag: boolean; + enabled: boolean; + reversed: boolean; + swipeable: Swipeable; + } +} +export class SwipeTracker extends GObject.Object implements Gtk.Orientable { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get allow_long_swipes(): boolean; + set allow_long_swipes(val: boolean); + get allowLongSwipes(): boolean; + set allowLongSwipes(val: boolean); + get allow_mouse_drag(): boolean; + set allow_mouse_drag(val: boolean); + get allowMouseDrag(): boolean; + set allowMouseDrag(val: boolean); + get enabled(): boolean; + set enabled(val: boolean); + get reversed(): boolean; + set reversed(val: boolean); + get swipeable(): Swipeable; + + // Signals + + connect(id: string, callback: (...args: any[]) => any): number; + connect_after(id: string, callback: (...args: any[]) => any): number; + emit(id: string, ...args: any[]): void; + connect(signal: "begin-swipe", callback: (_source: this) => void): number; + connect_after(signal: "begin-swipe", callback: (_source: this) => void): number; + emit(signal: "begin-swipe"): void; + connect(signal: "end-swipe", callback: (_source: this, velocity: number, to: number) => void): number; + connect_after(signal: "end-swipe", callback: (_source: this, velocity: number, to: number) => void): number; + emit(signal: "end-swipe", velocity: number, to: number): void; + connect(signal: "prepare", callback: (_source: this, direction: NavigationDirection) => void): number; + connect_after(signal: "prepare", callback: (_source: this, direction: NavigationDirection) => void): number; + emit(signal: "prepare", direction: NavigationDirection): void; + connect(signal: "update-swipe", callback: (_source: this, progress: number) => void): number; + connect_after(signal: "update-swipe", callback: (_source: this, progress: number) => void): number; + emit(signal: "update-swipe", progress: number): void; + + // Implemented Properties + + get orientation(): Gtk.Orientation; + set orientation(val: Gtk.Orientation); + + // Constructors + + static ["new"](swipeable: Swipeable): SwipeTracker; + + // Members + + get_allow_long_swipes(): boolean; + get_allow_mouse_drag(): boolean; + get_enabled(): boolean; + get_reversed(): boolean; + get_swipeable(): Swipeable; + set_allow_long_swipes(allow_long_swipes: boolean): void; + set_allow_mouse_drag(allow_mouse_drag: boolean): void; + set_enabled(enabled: boolean): void; + set_reversed(reversed: boolean): void; + shift_position(delta: number): void; + + // Implemented Members + + get_orientation(): Gtk.Orientation; + set_orientation(orientation: Gtk.Orientation): void; +} +export module TabBar { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + autohide: boolean; + end_action_widget: Gtk.Widget; + endActionWidget: Gtk.Widget; + expand_tabs: boolean; + expandTabs: boolean; + inverted: boolean; + is_overflowing: boolean; + isOverflowing: boolean; + start_action_widget: Gtk.Widget; + startActionWidget: Gtk.Widget; + tabs_revealed: boolean; + tabsRevealed: boolean; + view: TabView; + } +} +export class TabBar extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get autohide(): boolean; + set autohide(val: boolean); + get end_action_widget(): Gtk.Widget; + set end_action_widget(val: Gtk.Widget); + get endActionWidget(): Gtk.Widget; + set endActionWidget(val: Gtk.Widget); + get expand_tabs(): boolean; + set expand_tabs(val: boolean); + get expandTabs(): boolean; + set expandTabs(val: boolean); + get inverted(): boolean; + set inverted(val: boolean); + get is_overflowing(): boolean; + get isOverflowing(): boolean; + get start_action_widget(): Gtk.Widget; + set start_action_widget(val: Gtk.Widget); + get startActionWidget(): Gtk.Widget; + set startActionWidget(val: Gtk.Widget); + get tabs_revealed(): boolean; + get tabsRevealed(): boolean; + get view(): TabView; + set view(val: TabView); + + // Signals + + connect(id: string, callback: (...args: any[]) => any): number; + connect_after(id: string, callback: (...args: any[]) => any): number; + emit(id: string, ...args: any[]): void; + connect( + signal: "extra-drag-drop", + callback: (_source: this, page: TabPage, value: GObject.Value) => boolean + ): number; + connect_after( + signal: "extra-drag-drop", + callback: (_source: this, page: TabPage, value: GObject.Value) => boolean + ): number; + emit(signal: "extra-drag-drop", page: TabPage, value: GObject.Value | any): void; + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): TabBar; + + // Members + + get_autohide(): boolean; + get_end_action_widget(): Gtk.Widget | null; + get_expand_tabs(): boolean; + get_inverted(): boolean; + get_is_overflowing(): boolean; + get_start_action_widget(): Gtk.Widget | null; + get_tabs_revealed(): boolean; + get_view(): TabView | null; + set_autohide(autohide: boolean): void; + set_end_action_widget(widget?: Gtk.Widget | null): void; + set_expand_tabs(expand_tabs: boolean): void; + set_inverted(inverted: boolean): void; + set_start_action_widget(widget?: Gtk.Widget | null): void; + set_view(view?: TabView | null): void; + setup_extra_drop_target(actions: Gdk.DragAction, types?: GObject.GType[] | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module TabPage { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + icon: Gio.Icon; + indicator_activatable: boolean; + indicatorActivatable: boolean; + indicator_icon: Gio.Icon; + indicatorIcon: Gio.Icon; + loading: boolean; + needs_attention: boolean; + needsAttention: boolean; + pinned: boolean; + selected: boolean; + title: string; + tooltip: string; + } +} +export class TabPage extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + get icon(): Gio.Icon; + set icon(val: Gio.Icon); + get indicator_activatable(): boolean; + set indicator_activatable(val: boolean); + get indicatorActivatable(): boolean; + set indicatorActivatable(val: boolean); + get indicator_icon(): Gio.Icon; + set indicator_icon(val: Gio.Icon); + get indicatorIcon(): Gio.Icon; + set indicatorIcon(val: Gio.Icon); + get loading(): boolean; + set loading(val: boolean); + get needs_attention(): boolean; + set needs_attention(val: boolean); + get needsAttention(): boolean; + set needsAttention(val: boolean); + get pinned(): boolean; + get selected(): boolean; + get title(): string; + set title(val: string); + get tooltip(): string; + set tooltip(val: string); + + // Members + + get_child(): Gtk.Widget; + get_icon(): Gio.Icon | null; + get_indicator_activatable(): boolean; + get_indicator_icon(): Gio.Icon | null; + get_loading(): boolean; + get_needs_attention(): boolean; + get_parent(): TabPage | null; + get_pinned(): boolean; + get_selected(): boolean; + get_title(): string; + get_tooltip(): string | null; + set_icon(icon?: Gio.Icon | null): void; + set_indicator_activatable(activatable: boolean): void; + set_indicator_icon(indicator_icon?: Gio.Icon | null): void; + set_loading(loading: boolean): void; + set_needs_attention(needs_attention: boolean): void; + set_title(title: string): void; + set_tooltip(tooltip: string): void; +} +export module TabView { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + default_icon: Gio.Icon; + defaultIcon: Gio.Icon; + is_transferring_page: boolean; + isTransferringPage: boolean; + menu_model: Gio.MenuModel; + menuModel: Gio.MenuModel; + n_pages: number; + nPages: number; + n_pinned_pages: number; + nPinnedPages: number; + pages: Gtk.SelectionModel; + selected_page: TabPage; + selectedPage: TabPage; + } +} +export class TabView extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get default_icon(): Gio.Icon; + set default_icon(val: Gio.Icon); + get defaultIcon(): Gio.Icon; + set defaultIcon(val: Gio.Icon); + get is_transferring_page(): boolean; + get isTransferringPage(): boolean; + get menu_model(): Gio.MenuModel; + set menu_model(val: Gio.MenuModel); + get menuModel(): Gio.MenuModel; + set menuModel(val: Gio.MenuModel); + get n_pages(): number; + get nPages(): number; + get n_pinned_pages(): number; + get nPinnedPages(): number; + get pages(): Gtk.SelectionModel; + get selected_page(): TabPage; + set selected_page(val: TabPage); + get selectedPage(): TabPage; + set selectedPage(val: TabPage); + + // Signals + + connect(id: string, callback: (...args: any[]) => any): number; + connect_after(id: string, callback: (...args: any[]) => any): number; + emit(id: string, ...args: any[]): void; + connect(signal: "close-page", callback: (_source: this, page: TabPage) => boolean): number; + connect_after(signal: "close-page", callback: (_source: this, page: TabPage) => boolean): number; + emit(signal: "close-page", page: TabPage): void; + connect(signal: "create-window", callback: (_source: this) => TabView | null): number; + connect_after(signal: "create-window", callback: (_source: this) => TabView | null): number; + emit(signal: "create-window"): void; + connect(signal: "indicator-activated", callback: (_source: this, page: TabPage) => void): number; + connect_after(signal: "indicator-activated", callback: (_source: this, page: TabPage) => void): number; + emit(signal: "indicator-activated", page: TabPage): void; + connect(signal: "page-attached", callback: (_source: this, page: TabPage, position: number) => void): number; + connect_after(signal: "page-attached", callback: (_source: this, page: TabPage, position: number) => void): number; + emit(signal: "page-attached", page: TabPage, position: number): void; + connect(signal: "page-detached", callback: (_source: this, page: TabPage, position: number) => void): number; + connect_after(signal: "page-detached", callback: (_source: this, page: TabPage, position: number) => void): number; + emit(signal: "page-detached", page: TabPage, position: number): void; + connect(signal: "page-reordered", callback: (_source: this, page: TabPage, position: number) => void): number; + connect_after(signal: "page-reordered", callback: (_source: this, page: TabPage, position: number) => void): number; + emit(signal: "page-reordered", page: TabPage, position: number): void; + connect(signal: "setup-menu", callback: (_source: this, page: TabPage | null) => void): number; + connect_after(signal: "setup-menu", callback: (_source: this, page: TabPage | null) => void): number; + emit(signal: "setup-menu", page: TabPage | null): void; + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): TabView; + + // Members + + add_page(child: Gtk.Widget, parent?: TabPage | null): TabPage; + append(child: Gtk.Widget): TabPage; + append_pinned(child: Gtk.Widget): TabPage; + close_other_pages(page: TabPage): void; + close_page(page: TabPage): void; + close_page_finish(page: TabPage, confirm: boolean): void; + close_pages_after(page: TabPage): void; + close_pages_before(page: TabPage): void; + get_default_icon(): Gio.Icon; + get_is_transferring_page(): boolean; + get_menu_model(): Gio.MenuModel | null; + get_n_pages(): number; + get_n_pinned_pages(): number; + get_nth_page(position: number): TabPage; + get_page(child: Gtk.Widget): TabPage; + get_page_position(page: TabPage): number; + get_pages(): Gtk.SelectionModel; + get_selected_page(): TabPage | null; + insert(child: Gtk.Widget, position: number): TabPage; + insert_pinned(child: Gtk.Widget, position: number): TabPage; + prepend(child: Gtk.Widget): TabPage; + prepend_pinned(child: Gtk.Widget): TabPage; + reorder_backward(page: TabPage): boolean; + reorder_first(page: TabPage): boolean; + reorder_forward(page: TabPage): boolean; + reorder_last(page: TabPage): boolean; + reorder_page(page: TabPage, position: number): boolean; + select_next_page(): boolean; + select_previous_page(): boolean; + set_default_icon(default_icon: Gio.Icon): void; + set_menu_model(menu_model?: Gio.MenuModel | null): void; + set_page_pinned(page: TabPage, pinned: boolean): void; + set_selected_page(selected_page: TabPage): void; + transfer_page(page: TabPage, other_view: TabView, position: number): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module TimedAnimation { + export interface ConstructorProperties extends Animation.ConstructorProperties { + [key: string]: any; + alternate: boolean; + duration: number; + easing: Easing; + repeat_count: number; + repeatCount: number; + reverse: boolean; + value_from: number; + valueFrom: number; + value_to: number; + valueTo: number; + } +} +export class TimedAnimation extends Animation { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get alternate(): boolean; + set alternate(val: boolean); + get duration(): number; + set duration(val: number); + get easing(): Easing; + set easing(val: Easing); + get repeat_count(): number; + set repeat_count(val: number); + get repeatCount(): number; + set repeatCount(val: number); + get reverse(): boolean; + set reverse(val: boolean); + get value_from(): number; + set value_from(val: number); + get valueFrom(): number; + set valueFrom(val: number); + get value_to(): number; + set value_to(val: number); + get valueTo(): number; + set valueTo(val: number); + + // Constructors + + static ["new"]( + widget: Gtk.Widget, + from: number, + to: number, + duration: number, + target: AnimationTarget + ): TimedAnimation; + + // Members + + get_alternate(): boolean; + get_duration(): number; + get_easing(): Easing; + get_repeat_count(): number; + get_reverse(): boolean; + get_value_from(): number; + get_value_to(): number; + set_alternate(alternate: boolean): void; + set_duration(duration: number): void; + set_easing(easing: Easing): void; + set_repeat_count(repeat_count: number): void; + set_reverse(reverse: boolean): void; + set_value_from(value: number): void; + set_value_to(value: number): void; +} +export module Toast { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + action_name: string; + actionName: string; + action_target: GLib.Variant; + actionTarget: GLib.Variant; + button_label: string; + buttonLabel: string; + priority: ToastPriority; + timeout: number; + title: string; + } +} +export class Toast extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get action_name(): string; + set action_name(val: string); + get actionName(): string; + set actionName(val: string); + get action_target(): GLib.Variant; + set action_target(val: GLib.Variant); + get actionTarget(): GLib.Variant; + set actionTarget(val: GLib.Variant); + get button_label(): string; + set button_label(val: string); + get buttonLabel(): string; + set buttonLabel(val: string); + get priority(): ToastPriority; + set priority(val: ToastPriority); + get timeout(): number; + set timeout(val: number); + get title(): string; + set title(val: string); + + // Signals + + connect(id: string, callback: (...args: any[]) => any): number; + connect_after(id: string, callback: (...args: any[]) => any): number; + emit(id: string, ...args: any[]): void; + connect(signal: "dismissed", callback: (_source: this) => void): number; + connect_after(signal: "dismissed", callback: (_source: this) => void): number; + emit(signal: "dismissed"): void; + + // Constructors + + static ["new"](title: string): Toast; + + // Members + + dismiss(): void; + get_action_name(): string | null; + get_action_target_value(): GLib.Variant | null; + get_button_label(): string | null; + get_priority(): ToastPriority; + get_timeout(): number; + get_title(): string; + set_action_name(action_name?: string | null): void; + set_action_target_value(action_target?: GLib.Variant | null): void; + set_button_label(button_label?: string | null): void; + set_detailed_action_name(detailed_action_name?: string | null): void; + set_priority(priority: ToastPriority): void; + set_timeout(timeout: number): void; + set_title(title: string): void; +} +export module ToastOverlay { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + child: Gtk.Widget; + } +} +export class ToastOverlay extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get child(): Gtk.Widget; + set child(val: Gtk.Widget); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): ToastOverlay; + + // Members + + add_toast(toast: Toast): void; + get_child(): Gtk.Widget | null; + set_child(child?: Gtk.Widget | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module ViewStack { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + hhomogeneous: boolean; + pages: Gtk.SelectionModel; + vhomogeneous: boolean; + visible_child: Gtk.Widget; + visibleChild: Gtk.Widget; + visible_child_name: string; + visibleChildName: string; + } +} +export class ViewStack extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get hhomogeneous(): boolean; + set hhomogeneous(val: boolean); + get pages(): Gtk.SelectionModel; + get vhomogeneous(): boolean; + set vhomogeneous(val: boolean); + get visible_child(): Gtk.Widget; + set visible_child(val: Gtk.Widget); + get visibleChild(): Gtk.Widget; + set visibleChild(val: Gtk.Widget); + get visible_child_name(): string; + set visible_child_name(val: string); + get visibleChildName(): string; + set visibleChildName(val: string); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): ViewStack; + + // Members + + add(child: Gtk.Widget): ViewStackPage; + add_named(child: Gtk.Widget, name?: string | null): ViewStackPage; + add_titled(child: Gtk.Widget, name: string | null, title: string): ViewStackPage; + get_child_by_name(name: string): Gtk.Widget | null; + get_hhomogeneous(): boolean; + get_page(child: Gtk.Widget): ViewStackPage; + get_pages(): Gtk.SelectionModel; + get_vhomogeneous(): boolean; + get_visible_child(): Gtk.Widget | null; + get_visible_child_name(): string | null; + remove(child: Gtk.Widget): void; + set_hhomogeneous(hhomogeneous: boolean): void; + set_vhomogeneous(vhomogeneous: boolean): void; + set_visible_child(child: Gtk.Widget): void; + set_visible_child_name(name: string): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module ViewStackPage { + export interface ConstructorProperties extends GObject.Object.ConstructorProperties { + [key: string]: any; + badge_number: number; + badgeNumber: number; + child: Gtk.Widget; + icon_name: string; + iconName: string; + name: string; + needs_attention: boolean; + needsAttention: boolean; + title: string; + use_underline: boolean; + useUnderline: boolean; + visible: boolean; + } +} +export class ViewStackPage extends GObject.Object { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get badge_number(): number; + set badge_number(val: number); + get badgeNumber(): number; + set badgeNumber(val: number); + get child(): Gtk.Widget; + get icon_name(): string; + set icon_name(val: string); + get iconName(): string; + set iconName(val: string); + get name(): string; + set name(val: string); + get needs_attention(): boolean; + set needs_attention(val: boolean); + get needsAttention(): boolean; + set needsAttention(val: boolean); + get title(): string; + set title(val: string); + get use_underline(): boolean; + set use_underline(val: boolean); + get useUnderline(): boolean; + set useUnderline(val: boolean); + get visible(): boolean; + set visible(val: boolean); + + // Members + + get_badge_number(): number; + get_child(): Gtk.Widget; + get_icon_name(): string | null; + get_name(): string | null; + get_needs_attention(): boolean; + get_title(): string | null; + get_use_underline(): boolean; + get_visible(): boolean; + set_badge_number(badge_number: number): void; + set_icon_name(icon_name?: string | null): void; + set_name(name?: string | null): void; + set_needs_attention(needs_attention: boolean): void; + set_title(title?: string | null): void; + set_use_underline(use_underline: boolean): void; + set_visible(visible: boolean): void; +} +export module ViewSwitcher { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + policy: ViewSwitcherPolicy; + stack: ViewStack; + } +} +export class ViewSwitcher extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get policy(): ViewSwitcherPolicy; + set policy(val: ViewSwitcherPolicy); + get stack(): ViewStack; + set stack(val: ViewStack); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): ViewSwitcher; + + // Members + + get_policy(): ViewSwitcherPolicy; + get_stack(): ViewStack | null; + set_policy(policy: ViewSwitcherPolicy): void; + set_stack(stack?: ViewStack | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module ViewSwitcherBar { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + reveal: boolean; + stack: ViewStack; + } +} +export class ViewSwitcherBar extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get reveal(): boolean; + set reveal(val: boolean); + get stack(): ViewStack; + set stack(val: ViewStack); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): ViewSwitcherBar; + + // Members + + get_reveal(): boolean; + get_stack(): ViewStack | null; + set_reveal(reveal: boolean): void; + set_stack(stack?: ViewStack | null): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module ViewSwitcherTitle { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + stack: ViewStack; + subtitle: string; + title: string; + title_visible: boolean; + titleVisible: boolean; + view_switcher_enabled: boolean; + viewSwitcherEnabled: boolean; + } +} +export class ViewSwitcherTitle extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get stack(): ViewStack; + set stack(val: ViewStack); + get subtitle(): string; + set subtitle(val: string); + get title(): string; + set title(val: string); + get title_visible(): boolean; + get titleVisible(): boolean; + get view_switcher_enabled(): boolean; + set view_switcher_enabled(val: boolean); + get viewSwitcherEnabled(): boolean; + set viewSwitcherEnabled(val: boolean); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](): ViewSwitcherTitle; + + // Members + + get_stack(): ViewStack | null; + get_subtitle(): string; + get_title(): string; + get_title_visible(): boolean; + get_view_switcher_enabled(): boolean; + set_stack(stack?: ViewStack | null): void; + set_subtitle(subtitle: string): void; + set_title(title: string): void; + set_view_switcher_enabled(enabled: boolean): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} +export module Window { + export interface ConstructorProperties extends Gtk.Window.ConstructorProperties { + [key: string]: any; + content: Gtk.Widget; + } +} +export class Window + extends Gtk.Window + implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Native, Gtk.Root, Gtk.ShortcutManager { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get content(): Gtk.Widget; + set content(val: Gtk.Widget); + + // Constructors + + static ["new"](): Window; + + // Members + + get_content(): Gtk.Widget | null; + set_content(content?: Gtk.Widget | null): void; + + // Implemented Members + + get_renderer(): Gsk.Renderer; + get_surface(): Gdk.Surface; + get_surface_transform(): [number, number]; + realize(): void; + unrealize(): void; + get_display(): Gdk.Display; + get_focus(): Gtk.Widget | null; + set_focus(focus?: Gtk.Widget | null): void; + vfunc_add_controller(controller: Gtk.ShortcutController): void; + vfunc_remove_controller(controller: Gtk.ShortcutController): void; +} +export module WindowTitle { + export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { + [key: string]: any; + subtitle: string; + title: string; + } +} +export class WindowTitle extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get subtitle(): string; + set subtitle(val: string); + get title(): string; + set title(val: string); + + // Implemented Properties + + get accessible_role(): Gtk.AccessibleRole; + set accessible_role(val: Gtk.AccessibleRole); + get accessibleRole(): Gtk.AccessibleRole; + set accessibleRole(val: Gtk.AccessibleRole); + + // Constructors + + static ["new"](title: string, subtitle: string): WindowTitle; + + // Members + + get_subtitle(): string; + get_title(): string; + set_subtitle(subtitle: string): void; + set_title(title: string): void; + + // Implemented Members + + get_accessible_role(): Gtk.AccessibleRole; + reset_property(property: Gtk.AccessibleProperty): void; + reset_relation(relation: Gtk.AccessibleRelation): void; + reset_state(state: Gtk.AccessibleState): void; + update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; + update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; + update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; + get_buildable_id(): string | null; + vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; + vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; + vfunc_custom_tag_start( + builder: Gtk.Builder, + child: GObject.Object | null, + tagname: string + ): [boolean, Gtk.BuildableParser, any]; + vfunc_get_id(): string; + vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; + vfunc_parser_finished(builder: Gtk.Builder): void; + vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; + vfunc_set_id(id: string): void; +} + +export class SpringParams { + static $gtype: GObject.GType; + + constructor(damping_ratio: number, mass: number, stiffness: number); + constructor(copy: SpringParams); + + // Constructors + static ["new"](damping_ratio: number, mass: number, stiffness: number): SpringParams; + static new_full(damping: number, mass: number, stiffness: number): SpringParams; + + // Members + get_damping(): number; + get_damping_ratio(): number; + get_mass(): number; + get_stiffness(): number; + ref(): SpringParams; + unref(): void; +} + +export interface SwipeableNamespace { + $gtype: GObject.GType; + prototype: SwipeablePrototype; +} +export type Swipeable = SwipeablePrototype; +export interface SwipeablePrototype extends Gtk.Widget { + // Members + + get_cancel_progress(): number; + get_distance(): number; + get_progress(): number; + get_snap_points(): number[]; + get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; + vfunc_get_cancel_progress(): number; + vfunc_get_distance(): number; + vfunc_get_progress(): number; + vfunc_get_snap_points(): number[]; + vfunc_get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; +} + +export const Swipeable: SwipeableNamespace; diff --git a/types/misc/extensionUtils.d.ts b/types/misc/extensionUtils.d.ts new file mode 100644 index 00000000..f6aecdf5 --- /dev/null +++ b/types/misc/extensionUtils.d.ts @@ -0,0 +1,102 @@ +// https://github.com/yilozt/rounded-window-corners/blob/main/%40imports/misc/extensionUtils.d.ts +// GPL3 + +import * as Gio from '@gi-types/gio2'; + +/** + * getCurrentExtension: + * + * @returns {?object} - The current extension, or null if not called from + * an extension. + */ +declare function getCurrentExtension(): { + uuid: string, + path: string, + dir: Gio.File, + metadata: { + 'settings-schema': string, + uuid: string, + } +}; +/** + * initTranslations: + * @param {string=} domain - the gettext domain to use + * + * Initialize Gettext to load translations from extensionsdir/locale. + * If @domain is not provided, it will be taken from metadata['gettext-domain'] + */ +declare function initTranslations(domain?: string | undefined): void; +/** + * gettext: + * @param {string} str - the string to translate + * + * Translate @str using the extension's gettext domain + * + * @returns {string} - the translated string + * + */ +declare function gettext(str: string): string; +/** + * ngettext: + * @param {string} str - the string to translate + * @param {string} strPlural - the plural form of the string + * @param {number} n - the quantity for which translation is needed + * + * Translate @str and choose plural form using the extension's + * gettext domain + * + * @returns {string} - the translated string + * + */ +declare function ngettext(str: string, strPlural: string, n: number): string; +/** + * pgettext: + * @param {string} context - context to disambiguate @str + * @param {string} str - the string to translate + * + * Translate @str in the context of @context using the extension's + * gettext domain + * + * @returns {string} - the translated string + * + */ +declare function pgettext(context: string, str: string): string; +declare function callExtensionGettextFunc(func: any, ...args: any[]): any; +/** + * getSettings: + * @param {string?} schema - the GSettings schema id + * @returns {Gio.Settings} - a new settings object for @schema + * + * Builds and returns a GSettings schema for @schema, using schema files + * in extensionsdir/schemas. If @schema is omitted, it is taken from + * metadata['settings-schema']. + */ +declare function getSettings(schema?: string | undefined): Gio.Settings; +/** + * openPrefs: + * + * Open the preference dialog of the current extension + */ +declare function openPrefs(): Promise; +declare function isOutOfDate(extension: any): boolean; +declare function serializeExtension(extension: any): {}; +declare function deserializeExtension(variant: any): { + metadata: {}; +}; +declare function installImporter(extension: any): void; +declare const Gettext: any; +declare const Config: any; +declare namespace ExtensionType { + const SYSTEM: number; + const PER_USER: number; +} +declare namespace ExtensionState { + const ENABLED: number; + const DISABLED: number; + const ERROR: number; + const OUT_OF_DATE: number; + const DOWNLOADING: number; + const INITIALIZED: number; + const UNINSTALLED: number; +} +declare const SERIALIZED_PROPERTIES: string[]; diff --git a/types/ui/main.d.ts b/types/ui/main.d.ts new file mode 100644 index 00000000..9ac0cf40 --- /dev/null +++ b/types/ui/main.d.ts @@ -0,0 +1,8 @@ +import * as St from '@gi-types/st'; +import * as PanelMenu from '@gi/ui/panelMenu'; + +declare class Panel extends St.Widget { + addToStatusArea(role: string, indicator: PanelMenu.Button, position?: number, box?: unknown): PanelMenu.Button +} + +export const panel: Panel; diff --git a/types/ui/panelMenu.d.ts b/types/ui/panelMenu.d.ts new file mode 100644 index 00000000..f3836405 --- /dev/null +++ b/types/ui/panelMenu.d.ts @@ -0,0 +1,12 @@ +/* eslint-disable */ + +import {PopupMenu} from '@gi/ui/popupMenu'; +import * as St from '@gi-types/st'; + +declare class ButtonBox extends St.Widget{} + +export class Button extends ButtonBox { + menu: PopupMenu; + + constructor(menuAlignment: number, nameText: string, dontCreateMenu?: boolean); +} diff --git a/types/ui/popupMenu.d.ts b/types/ui/popupMenu.d.ts new file mode 100644 index 00000000..405dd26c --- /dev/null +++ b/types/ui/popupMenu.d.ts @@ -0,0 +1,74 @@ +/* eslint-disable */ + +// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/popupMenu.js + +import * as Clutter from '@gi-types/clutter'; +import * as St from '@gi-types/st'; + + +// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/misc/signals.js +declare class EventEmitter { + connectObject(args: unknown): unknown; + connect_object(args: unknown): unknown; + disconnect_object(args: unknown): unknown; + disconnectObject(args: unknown): unknown; + + // don't know where these are: + connect(key: string, callback: (actor: typeof this, state: unknown[]) => void): void; +} + +declare class PopupMenuBase extends EventEmitter { + actor: Clutter.Actor; + box: St.BoxLayout; + + addMenuItem(menuItem: PopupMenuSection | PopupSubMenuMenuItem | PopupSeparatorMenuItem | PopupBaseMenuItem, position?: number): void; + removeAll(): void; +} + +declare class PopupBaseMenuItem extends St.BoxLayout { + actor: typeof this; + // get actor(): typeof this; + get sensitive(): boolean; + set sensitive(sensitive: boolean); +} + +export class PopupMenu extends PopupMenuBase { + constructor(sourceActor: Clutter.Actor, arrowAlignment: unknown, arrowSide: unknown) +} + +export class PopupMenuItem extends PopupBaseMenuItem { + constructor(text: string, params?: unknown) + label: St.Label; +} + +export class PopupSubMenuMenuItem extends PopupBaseMenuItem { + constructor(text: string, wantIcon: boolean) + + label: St.Label; + menu: PopupSubMenu; +} + +export class PopupSubMenu extends PopupMenuBase { + actor: St.ScrollView; +} + +export class PopupMenuSection extends PopupMenuBase { + actor: St.BoxLayout | Clutter.Actor; +} + +export class PopupSeparatorMenuItem extends PopupBaseMenuItem {} +export class Switch extends St.Bin {} +export class PopupSwitchMenuItem extends PopupBaseMenuItem { + constructor(text: string, active: boolean, params?: { + reactive: boolean | undefined, + activate: boolean | undefined, + hover: boolean | undefined, + style_class: unknown | null | undefined, + can_focus: boolean | undefined + }) + + setToggleState(state: boolean): void; +} +export class PopupImageMenuItem extends PopupBaseMenuItem {} +export class PopupDummyMenu extends EventEmitter {} +export class PopupMenuManager {} From c0cf5c6382c1c2f15285694e1a44e7e265858d7d Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 10 Dec 2022 21:16:14 +0100 Subject: [PATCH 03/87] Alot of stuff * allow requesting multiple images * timer fixes * hover preview fixes Don't fetch more images than needed: Before this always the amount of displays were requested even if not used later on. --- build.sh | 1 + src/adapter/baseAdapter.ts | 17 ++- src/adapter/genericJson.ts | 129 +++++++++++---------- src/adapter/localFolder.ts | 25 ++-- src/adapter/reddit.ts | 81 +++++++------ src/adapter/unsplash.ts | 15 ++- src/adapter/urlSource.ts | 4 +- src/adapter/wallhaven.ts | 38 ++++--- src/history.ts | 23 +++- src/randomWallpaperMenu.ts | 80 +++++++------ src/timer.ts | 3 +- src/wallpaperController.ts | 228 +++++++++++++++++++++++-------------- 12 files changed, 375 insertions(+), 269 deletions(-) diff --git a/build.sh b/build.sh index ca9364ac..3a939da2 100755 --- a/build.sh +++ b/build.sh @@ -78,6 +78,7 @@ compile_js() { compile_schemas() { check_command "glib-compile-schemas" + mkdir -p "$DESTDIR/schemas/" # the pack command also compiles the schemas but only into the zip file glib-compile-schemas --targetdir="$DESTDIR/schemas" "$SRCDIR/schemas/" diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index b399c9b5..7d4d4577 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -6,7 +6,7 @@ import {HistoryEntry} from './../history.js'; import {Logger} from './../logger.js'; import {SoupBowl} from './../soupBowl.js'; -class BaseAdapter { +abstract class BaseAdapter { logger: Logger; protected _settings: SettingsModule.Settings; @@ -38,10 +38,10 @@ class BaseAdapter { /** * Retrieves a new url for an image and crafts a new HistoryEntry. * + * @param {number} count Number of requested wallpaper */ - requestRandomImage(): Promise { - throw new Error('requestRandomImage not implemented'); - } + // eslint-disable-next-line no-unused-vars + abstract requestRandomImage (count: number): Promise; /** * copy file from uri to local wallpaper directory and returns the full filepath @@ -70,6 +70,15 @@ class BaseAdapter { return file; } + protected _includesWallpaper(array: HistoryEntry[], uri: string) { + for (const element of array) { + if (element.source.imageDownloadUrl === uri) + return true; + } + + return false; + } + /** * Check if this image is in the list of blocked images. * diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index cb9f9a39..fbaaff28 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -22,7 +22,9 @@ class GenericJsonAdapter extends BaseAdapter { }); } - private async _getHistoryEntry() { + private async _getHistoryEntry(count: number) { + const wallpaperResult: HistoryEntry[] = []; + let url = this._settings.getString('request-url'); url = encodeURI(url); @@ -41,86 +43,90 @@ class GenericJsonAdapter extends BaseAdapter { const authorNameJSONPath = this._settings.getString('author-name-path'); const authorUrlJSONPath = this._settings.getString('author-url-path'); - let returnObject; - let imageDownloadUrl; - for (let i = 0; i < 5; i++) { - returnObject = JSONPath.getTarget(response_body, imageJSONPath); - - if (returnObject && (typeof returnObject.Object === 'string' || typeof returnObject.Object === 'number') && returnObject.Object !== '') { - imageDownloadUrl = this._settings.getString('image-prefix') + String(returnObject.Object); + for (let i = 0; i < 5 && wallpaperResult.length < count; i++) { + const returnObject = JSONPath.getTarget(response_body, imageJSONPath); + if (!returnObject || (typeof returnObject.Object !== 'string' && typeof returnObject.Object !== 'number') || returnObject.Object === '') + throw new Error('Unexpected json member found'); - const imageBlocked = this._isImageBlocked(Utils.fileName(imageDownloadUrl)); + const imageDownloadUrl = this._settings.getString('image-prefix') + String(returnObject.Object); + const imageBlocked = this._isImageBlocked(Utils.fileName(imageDownloadUrl)); - if (!imageBlocked) - break; - - // Only retry with @random present in JSONPath - if (imageBlocked && !imageJSONPath.includes('@random')) { - // Abort and try again - return null; - } + // Don't retry without @random present in JSONPath + if (imageBlocked && !imageJSONPath.includes('@random')) { + // Abort and try again + return null; } - imageDownloadUrl = null; - } + if (imageBlocked) + continue; - if (!imageDownloadUrl) - throw new Error('Only blocked images found.'); + // '@random' would yield different results so lets make sure the values stay + // the same as long as the path is identical + const samePath = imageJSONPath.substring(0, Utils.findFirstDifference(imageJSONPath, postJSONPath)); - // '@random' would yield different results so lets make sure the values stay - // the same as long as the path is identical - const samePath = imageJSONPath.substring(0, Utils.findFirstDifference(imageJSONPath, postJSONPath)); + // count occurrences of '@random' to slice the array later + // https://stackoverflow.com/a/4009768 + const occurrences = (samePath.match(/@random/g) || []).length; + const slicedRandomNumbers = returnObject?.RandomNumbers?.slice(0, occurrences); - // count occurrences of '@random' to slice the array later - // https://stackoverflow.com/a/4009768 - const occurrences = (samePath.match(/@random/g) || []).length; - const slicedRandomNumbers = returnObject?.RandomNumbers?.slice(0, occurrences); + // A bit cumbersome to handle "unknown" in the following parts: + // https://github.com/microsoft/TypeScript/issues/27706 - // A bit cumbersome to handle "unknown" in the following parts: - // https://github.com/microsoft/TypeScript/issues/27706 + let postUrl: string; + const postUrlObject = JSONPath.getTarget(response_body, postJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + if (typeof postUrlObject === 'string' || typeof postUrlObject === 'number') + postUrl = this._settings.getString('post-prefix') + String(postUrlObject); + else + postUrl = ''; - let postUrl: string; - const postUrlObject = JSONPath.getTarget(response_body, postJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; - if (typeof postUrlObject === 'string' || typeof postUrlObject === 'number') - postUrl = this._settings.getString('post-prefix') + String(postUrlObject); - else - postUrl = ''; + let authorName: string | null = null; + const authorNameObject = JSONPath.getTarget(response_body, authorNameJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + if (typeof authorNameObject === 'string' && authorNameObject !== '') + authorName = authorNameObject; - let authorName: string | null = null; - const authorNameObject = JSONPath.getTarget(response_body, authorNameJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; - if (typeof authorNameObject === 'string' && authorNameObject !== '') - authorName = authorNameObject; + let authorUrl: string; + const authorUrlObject = JSONPath.getTarget(response_body, authorUrlJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + if (typeof authorUrlObject === 'string' || typeof authorUrlObject === 'number') + authorUrl = this._settings.getString('author-url-prefix') + String(authorUrlObject); + else + authorUrl = ''; - let authorUrl: string; - const authorUrlObject = JSONPath.getTarget(response_body, authorUrlJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; - if (typeof authorUrlObject === 'string' || typeof authorUrlObject === 'number') - authorUrl = this._settings.getString('author-url-prefix') + String(authorUrlObject); - else - authorUrl = ''; + const historyEntry = new HistoryEntry(authorName, this._sourceName, imageDownloadUrl); - const historyEntry = new HistoryEntry(authorName, this._sourceName, imageDownloadUrl); + if (authorUrl !== '') + historyEntry.source.authorUrl = authorUrl; - if (authorUrl !== '') - historyEntry.source.authorUrl = authorUrl; + if (postUrl !== '') + historyEntry.source.imageLinkUrl = postUrl; - if (postUrl !== '') - historyEntry.source.imageLinkUrl = postUrl; + if (domainUrl !== '') + historyEntry.source.sourceUrl = domainUrl; - if (domainUrl !== '') - historyEntry.source.sourceUrl = domainUrl; + if (!this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) + wallpaperResult.push(historyEntry); + } - return historyEntry; + if (wallpaperResult.length === 0) + throw new Error('Only blocked images found.'); + + return wallpaperResult; } - async requestRandomImage() { - for (let i = 0; i < 5; i++) { + async requestRandomImage(count: number) { + const wallpaperResult: HistoryEntry[] = []; + + for (let i = 0; i < 5 && wallpaperResult.length < count; i++) { try { // This should run sequentially // eslint-disable-next-line no-await-in-loop - const historyEntry = await this._getHistoryEntry(); + const historyArray = await this._getHistoryEntry(count); - if (historyEntry) - return historyEntry; + if (historyArray) { + historyArray.forEach(element => { + if (!this._includesWallpaper(wallpaperResult, element.source.imageDownloadUrl)) + wallpaperResult.push(element); + }); + } } catch (error) { this.logger.warn(`Failed getting image: ${error}`); // Do not escalate yet, try again @@ -129,7 +135,10 @@ class GenericJsonAdapter extends BaseAdapter { // Image blocked, try again } - throw new Error('Only blocked images found.'); + if (wallpaperResult.length === 0) + throw new Error('Only blocked images found.'); + + return wallpaperResult; } } diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index de85f66c..42455d55 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -22,36 +22,38 @@ class LocalFolderAdapter extends BaseAdapter { }); } - requestRandomImage(): Promise { + requestRandomImage(count: number): Promise { return new Promise((resolve, reject) => { const folder = Gio.File.new_for_path(this._settings.getString('folder')); const files = this._listDirectory(folder); + const wallpaperResult: HistoryEntry[] = []; if (files.length < 1) { reject(new Error('No files found')); return; } - let randomFilePath: string | null = null; - for (let i = 0; i < 5; i++) { + for (let i = 0; i < 20 && wallpaperResult.length < count; i++) { const randomFile = files[Utils.getRandomNumber(files.length)]; - randomFilePath = randomFile.get_uri(); + const randomFilePath = randomFile.get_uri(); const randomFileName = randomFile.get_basename(); - if (randomFileName && !this._isImageBlocked(randomFileName)) - break; + if (!randomFileName || this._isImageBlocked(randomFileName)) + continue; - randomFilePath = null; + const historyEntry = new HistoryEntry(null, this._sourceName, randomFilePath); + historyEntry.source.sourceUrl = this._wallpaperLocation; + + if (!this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) + wallpaperResult.push(historyEntry); } - if (!randomFilePath) { + if (wallpaperResult.length === 0) { reject(new Error('Only blocked images found')); return; } - const historyEntry = new HistoryEntry(null, this._sourceName, randomFilePath); - historyEntry.source.sourceUrl = this._wallpaperLocation; - resolve(historyEntry); + resolve(wallpaperResult); }); } @@ -80,7 +82,6 @@ class LocalFolderAdapter extends BaseAdapter { if (info === null) break; - const child = iterator.get_child(info); const type = info.get_file_type(); diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index c7b7f0a4..34e61817 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -49,7 +49,8 @@ class RedditAdapter extends BaseAdapter { return string.replace(/&/g, '&'); } - async requestRandomImage() { + async requestRandomImage(count: number) { + const wallpaperResult: HistoryEntry[] = []; const subreddits = this._settings.getString('subreddits').split(',').map(s => s.trim()).join('+'); const require_sfw = this._settings.getBoolean('allow-sfw'); @@ -58,55 +59,51 @@ class RedditAdapter extends BaseAdapter { const response_body_bytes = await this._bowl.send_and_receive(message); - try { - const response_body: RedditResponse = JSON.parse(ByteArray.toString(response_body_bytes)); - - const submissions = response_body.data.children.filter(child => { - if (child.data.post_hint !== 'image') - return false; - if (require_sfw) - return child.data.over_18 === false; - - const minWidth = this._settings.getInt('min-width'); - const minHeight = this._settings.getInt('min-height'); - if (child.data.preview.images[0].source.width < minWidth) - return false; - if (child.data.preview.images[0].source.height < minHeight) - return false; - - const imageRatio1 = this._settings.getInt('image-ratio1'); - const imageRatio2 = this._settings.getInt('image-ratio2'); - if (child.data.preview.images[0].source.width / imageRatio1 * imageRatio2 < child.data.preview.images[0].source.height) - return false; - return true; - }); - - if (submissions.length === 0) - throw new Error('No suitable submissions found!'); - - let submission = null; - let imageDownloadUrl = null; - for (let i = 0; i < 5; i++) { - const random = Utils.getRandomNumber(submissions.length); - submission = submissions[random].data; - imageDownloadUrl = this._ampDecode(submission.preview.images[0].source.url); + const response_body: RedditResponse = JSON.parse(ByteArray.toString(response_body_bytes)); + + const filteredSubmissions = response_body.data.children.filter(child => { + if (child.data.post_hint !== 'image') + return false; + if (require_sfw) + return child.data.over_18 === false; + + const minWidth = this._settings.getInt('min-width'); + const minHeight = this._settings.getInt('min-height'); + if (child.data.preview.images[0].source.width < minWidth) + return false; + if (child.data.preview.images[0].source.height < minHeight) + return false; + + const imageRatio1 = this._settings.getInt('image-ratio1'); + const imageRatio2 = this._settings.getInt('image-ratio2'); + if (child.data.preview.images[0].source.width / imageRatio1 * imageRatio2 < child.data.preview.images[0].source.height) + return false; + return true; + }); - if (!this._isImageBlocked(Utils.fileName(imageDownloadUrl))) - break; + if (filteredSubmissions.length === 0) + throw new Error('No suitable submissions found!'); - imageDownloadUrl = null; - } + for (let i = 0; i < 20 && wallpaperResult.length < count; i++) { + const random = Utils.getRandomNumber(filteredSubmissions.length); + const submission = filteredSubmissions[random].data; + const imageDownloadUrl = this._ampDecode(submission.preview.images[0].source.url); - if (!imageDownloadUrl || !submission) - throw new Error('Only blocked images found.'); + if (this._isImageBlocked(Utils.fileName(imageDownloadUrl))) + continue; const historyEntry = new HistoryEntry(null, this._sourceName, imageDownloadUrl); historyEntry.source.sourceUrl = `https://www.reddit.com/${submission.subreddit_name_prefixed}`; historyEntry.source.imageLinkUrl = `https://www.reddit.com/${submission.permalink}`; - return historyEntry; - } catch (e) { - throw new Error(`Could not create request. (${e})`); + + if (!this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) + wallpaperResult.push(historyEntry); } + + if (wallpaperResult.length === 0) + throw new Error('Only blocked images found.'); + + return wallpaperResult; } } diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 279c1a99..19cbb7ac 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -67,15 +67,17 @@ class UnsplashAdapter extends BaseAdapter { return historyEntry; } - async requestRandomImage() { - for (let i = 0; i < 5; i++) { + async requestRandomImage(count: number) { + const wallpaperResult: HistoryEntry[] = []; + + for (let i = 0; i < 5 && wallpaperResult.length < count; i++) { try { // This should run sequentially // eslint-disable-next-line no-await-in-loop const historyEntry = await this._getHistoryEntry(); - if (historyEntry) - return historyEntry; + if (historyEntry && !this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) + wallpaperResult.push(historyEntry); } catch (error) { this.logger.warn(`Failed getting image: ${error}`); // Do not escalate yet, try again @@ -84,7 +86,10 @@ class UnsplashAdapter extends BaseAdapter { // Image blocked, try again } - throw new Error('Only blocked images found.'); + if (wallpaperResult.length === 0) + throw new Error('Only blocked images found.'); + + return wallpaperResult; } private _generateOptionsString() { diff --git a/src/adapter/urlSource.ts b/src/adapter/urlSource.ts index d0365b52..fc3825bd 100644 --- a/src/adapter/urlSource.ts +++ b/src/adapter/urlSource.ts @@ -15,7 +15,7 @@ class UrlSourceAdapter extends BaseAdapter { }); } - requestRandomImage() { + requestRandomImage(unusedCount: number) { const imageDownloadUrl = this._settings.getString('image-url'); let authorName: string | null = this._settings.getString('author-name'); const authorUrl = this._settings.getString('author-url'); @@ -40,7 +40,7 @@ class UrlSourceAdapter extends BaseAdapter { if (domainUrl !== '') historyEntry.source.sourceUrl = domainUrl; - return Promise.resolve(historyEntry); + return Promise.resolve([historyEntry]); } } diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index a7eea5c1..76889014 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -51,7 +51,9 @@ class WallhavenAdapter extends BaseAdapter { }); } - async requestRandomImage() { + async requestRandomImage(count: number) { + const wallpaperResult: HistoryEntry[] = []; + this._readOptionsFromSettings(); const optionsString = this._generateOptionsString(this._options); @@ -69,31 +71,31 @@ class WallhavenAdapter extends BaseAdapter { if (!response || response.length === 0) throw new Error('Empty response'); - let downloadURL: string | null = null; - let siteURL: string = ''; - for (let i = 0; i < 5; i++) { + for (let i = 0; i < 10 && wallpaperResult.length < count; i++) { // get a random entry from the array const entry = response[Utils.getRandomNumber(response.length)]; - downloadURL = entry.path; - siteURL = entry.url; + const siteURL = entry.url; + let downloadURL = entry.path; + + if (this._isImageBlocked(Utils.fileName(downloadURL))) + continue; - if (!this._isImageBlocked(Utils.fileName(downloadURL))) - break; + const apiKey = this._options['apikey']; + if (apiKey !== '') + downloadURL += `?apikey=${apiKey}`; - downloadURL = null; + const historyEntry = new HistoryEntry(null, this._sourceName, downloadURL); + historyEntry.source.sourceUrl = 'https://wallhaven.cc/'; + historyEntry.source.imageLinkUrl = siteURL; + + if (!this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) + wallpaperResult.push(historyEntry); } - if (!downloadURL) + if (wallpaperResult.length === 0) throw new Error('Only blocked images found.'); - const apiKey = this._options['apikey']; - if (apiKey !== '') - downloadURL += `?apikey=${apiKey}`; - - const historyEntry = new HistoryEntry(null, this._sourceName, downloadURL); - historyEntry.source.sourceUrl = 'https://wallhaven.cc/'; - historyEntry.source.imageLinkUrl = siteURL; - return historyEntry; + return wallpaperResult; } private _generateOptionsString(options: T) { diff --git a/src/history.ts b/src/history.ts index 2925d42a..6ea6253b 100644 --- a/src/history.ts +++ b/src/history.ts @@ -14,13 +14,17 @@ interface SourceInfo { } interface AdapterInfo { + /** Identifier to access the settings path */ id: string | null; + /** Adapter type as enum */ type: number | null; } class HistoryEntry { timestamp = new Date().getTime(); + /** Unique identifier, concat of timestamp and name */ id: string; + /** Basename of URI */ name: string; path: string | null = null; source: SourceInfo; @@ -69,7 +73,7 @@ class HistoryController { * * @param {string} id ID of the historyEntry */ - promoteToActive(id: string): boolean { + promoteToActive(id: string) { const element = this.get(id); if (element === null) return false; @@ -89,7 +93,7 @@ class HistoryController { * * @param {string} id ID of the historyEntry */ - get(id: string): HistoryEntry | null { + get(id: string) { for (const elem of this.history) { if (elem.id === id) return elem; @@ -101,14 +105,23 @@ class HistoryController { /** * Get the current history element. */ - getCurrentElement(): HistoryEntry { + getCurrentEntry() { return this.history[0]; } + getEntryByPath(path: string) { + for (const element of this.history) { + if (element.path === path) + return element; + } + + return null; + } + /** * Get a random HistoryEntry. */ - getRandom(): HistoryEntry { + getRandom() { return this.history[Utils.getRandomNumber(this.history.length)]; } @@ -138,7 +151,7 @@ class HistoryController { /** * Clear the history and delete all photos except the current one. */ - clear(): boolean { + clear() { const firstHistoryElement = this.history[0]; if (firstHistoryElement) diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 684bb711..df533532 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -17,45 +17,44 @@ const Self = ExtensionUtils.getCurrentExtension(); class RandomWallpaperMenu { private _logger = new Logger('RWG3', 'RandomWallpaperEntry'); - private _settings = new Settings.Settings(); private _backendConnection = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); + private _settings = new Settings.Settings(); - private _wallpaperController; private _currentBackgroundSection; - private _historySection; private _hidePanelIconHandler; - - private panelMenu; + private _historySection; + private _panelMenu; + private _wallpaperController; constructor(wallpaperController: WallpaperController) { this._wallpaperController = wallpaperController; - this.panelMenu = new PanelMenu.Button(0, 'Random wallpaper'); + this._panelMenu = new PanelMenu.Button(0, 'Random wallpaper'); // PanelMenu Icon const statusIcon = new CustomElements.StatusElement(); - this.panelMenu.add_child(statusIcon.icon); + this._panelMenu.add_child(statusIcon.icon); this._hidePanelIconHandler = this._settings.observe('hide-panel-icon', this.updatePanelMenuVisibility.bind(this)); // new wallpaper button const newWallpaperItem = new CustomElements.NewWallpaperElement({}); - this.panelMenu.menu.addMenuItem(newWallpaperItem); + this._panelMenu.menu.addMenuItem(newWallpaperItem); - this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + this._panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); // Set fixed width so the preview images don't widen the menu - this.panelMenu.menu.actor.set_width(350); + this._panelMenu.menu.actor.set_width(350); // current background section this._currentBackgroundSection = new PopupMenu.PopupMenuSection(); - this.panelMenu.menu.addMenuItem(this._currentBackgroundSection); - this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + this._panelMenu.menu.addMenuItem(this._currentBackgroundSection); + this._panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); // history section this._historySection = new CustomElements.HistorySection(); - this.panelMenu.menu.addMenuItem(this._historySection); + this._panelMenu.menu.addMenuItem(this._historySection); - this.panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + this._panelMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); // Temporarily pause timer const pauseTimerItem = new PopupMenu.PopupSwitchMenuItem('Pause timer', false); @@ -74,19 +73,19 @@ class RandomWallpaperMenu { pauseTimerItem.setToggleState(this._backendConnection.getBoolean('pause-timer')); }); - this.panelMenu.menu.addMenuItem(pauseTimerItem); + this._panelMenu.menu.addMenuItem(pauseTimerItem); // clear history button const clearHistoryItem = new PopupMenu.PopupMenuItem('Clear History'); - this.panelMenu.menu.addMenuItem(clearHistoryItem); + this._panelMenu.menu.addMenuItem(clearHistoryItem); // open wallpaper folder button const openFolder = new PopupMenu.PopupMenuItem('Open Wallpaper Folder'); - this.panelMenu.menu.addMenuItem(openFolder); + this._panelMenu.menu.addMenuItem(openFolder); // settings button const openSettings = new PopupMenu.PopupMenuItem('Settings'); - this.panelMenu.menu.addMenuItem(openSettings); + this._panelMenu.menu.addMenuItem(openSettings); // add eventlistener this._wallpaperController.registerStartLoadingHook(() => statusIcon.startLoading()); @@ -95,7 +94,13 @@ class RandomWallpaperMenu { // new wallpaper event newWallpaperItem.connect('activate', () => { - this._wallpaperController.fetchNewWallpaper().catch(logError); + this._wallpaperController.prohibitNewWallpaper = true; + this._wallpaperController.fetchNewWallpaper().then(() => { + this._wallpaperController.prohibitNewWallpaper = false; + }).catch(error => { + this._wallpaperController.prohibitNewWallpaper = false; + logError(error); + }); }); // clear history event @@ -123,18 +128,20 @@ class RandomWallpaperMenu { null); }); - this.panelMenu.menu.actor.connect('show', () => { + this._panelMenu.menu.actor.connect('show', () => { newWallpaperItem.show(); }); // when the popupMenu disappears, check if the wallpaper is the original and // reset it if needed - this.panelMenu.menu.actor.connect('hide', () => { - this._wallpaperController.resetWallpaper(); + this._panelMenu.menu.actor.connect('hide', () => { + if (!this._wallpaperController.prohibitNewWallpaper) + this._wallpaperController.resetWallpaper(); }); - this.panelMenu.menu.actor.connect('leave-event', () => { - this._wallpaperController.resetWallpaper(); + this._panelMenu.menu.actor.connect('leave-event', () => { + if (!this._wallpaperController.prohibitNewWallpaper) + this._wallpaperController.resetWallpaper(); }); this._settings.observe('history', this.setHistoryList.bind(this)); @@ -145,12 +152,12 @@ class RandomWallpaperMenu { this.setHistoryList(); // add to panel - Main.panel.addToStatusArea('random-wallpaper-menu', this.panelMenu); + Main.panel.addToStatusArea('random-wallpaper-menu', this._panelMenu); } cleanup() { this.clearHistoryList(); - this.panelMenu.destroy(); + this._panelMenu.destroy(); // remove all signal handlers if (this._hidePanelIconHandler !== null) @@ -159,9 +166,9 @@ class RandomWallpaperMenu { updatePanelMenuVisibility() { if (this._settings.getBoolean('hide-panel-icon')) - this.panelMenu.hide(); + this._panelMenu.hide(); else - this.panelMenu.show(); + this._panelMenu.show(); } setCurrentBackgroundElement() { @@ -194,7 +201,8 @@ class RandomWallpaperMenu { */ // eslint-disable-next-line no-unused-vars function onLeave(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { - this._wallpaperController.resetWallpaper(); + if (!this._wallpaperController.prohibitNewWallpaper) + this._wallpaperController.resetWallpaper(); } /** @@ -202,8 +210,10 @@ class RandomWallpaperMenu { * @param {CustomElements.HistoryElement} actor The activating panel item */ function onEnter(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { - // @ts-expect-error Typing fails for GObject.registerClass - this._wallpaperController.previewWallpaper(actor.historyEntry.id); + if (!this._wallpaperController.prohibitNewWallpaper) { + // @ts-expect-error Typing fails for GObject.registerClass + this._wallpaperController.previewWallpaper(actor.historyEntry.id); + } } /** @@ -211,8 +221,14 @@ class RandomWallpaperMenu { * @param {CustomElements.HistoryElement} actor The activating panel item */ function onSelect(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { + this._wallpaperController.prohibitNewWallpaper = true; // @ts-expect-error Typing fails for GObject.registerClass - this._wallpaperController.setWallpaper(actor.historyEntry.id); + this._wallpaperController.setWallpaper(actor.historyEntry.id).then(() => { + this._wallpaperController.prohibitNewWallpaper = false; + }).catch(error => { + this._wallpaperController.prohibitNewWallpaper = false; + logError(error); + }); } this._historySection.updateList(history, onEnter.bind(this), onLeave.bind(this), onSelect.bind(this)); diff --git a/src/timer.ts b/src/timer.ts index 00b22c42..844d98a4 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -79,9 +79,8 @@ class AFTimer { this._timeoutEndCallback(); this.reset(); // reset timer - this.start(); // restart timer - return true; + return GLib.SOURCE_CONTINUE; }); } diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index c4f7d997..380fd4b5 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -27,6 +27,7 @@ Gio._promisify(Gio.File.prototype, 'move_async', 'move_finish'); class WallpaperController { wallpaperLocation: string; + prohibitNewWallpaper = false; private _backendConnection = new SettingsModule.Settings(SettingsModule.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); private _logger = new Logger('RWG3', 'WallpaperController'); @@ -56,7 +57,7 @@ class WallpaperController { this._historyController = new HistoryModule.HistoryController(this.wallpaperLocation); - // Bring values to defined stage + // Bring values to defined state this._backendConnection.setBoolean('clear-history', false); this._backendConnection.setBoolean('open-folder', false); this._backendConnection.setBoolean('pause-timer', false); @@ -131,7 +132,19 @@ class WallpaperController { this._updateAutoFetching(); } else { this._prohibitTimer = false; + + // Switching the switch in the menu closes the menu which triggers a hover event + // Prohibit that from emitting because a paused timer could have surpassed the interval + // and try to fetch new wallpaper which would be interrupted by a wallpaper reset caused + // the the closing menu event. + this.prohibitNewWallpaper = true; this._updateAutoFetching(); + + // And activate emitting again after a second + GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => { + this.prohibitNewWallpaper = false; + return GLib.SOURCE_REMOVE; + }); } } @@ -165,6 +178,8 @@ class WallpaperController { }); this._timer.setMinutes(this._autoFetch.duration); this._timer.start(); + } else if (this._prohibitTimer && this._autoFetch.active) { + this._timer.cleanup(); } else { this._timer.stop(); } @@ -251,12 +266,19 @@ class WallpaperController { /** * Sets the wallpaper and the lock screen when enabled to the given path. * - * @param {string} path Path to the image + * @param {string[]} wallpaperArray Array of paths to the image + * @param {number} monitorCount Number of monitors to fill */ - private async _setBackground(path: string) { - const background_setting = new SettingsModule.Settings('org.gnome.desktop.background'); - const screensaver_setting = new SettingsModule.Settings('org.gnome.desktop.screensaver'); - const wallpaperUri = `file://${path}`; + private async _setBackground(wallpaperArray: string[], monitorCount: number) { + const multiMonitor = monitorCount > 1 && this._hydraPaper.isAvailable(); + const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); + const screensaverSettings = new SettingsModule.Settings('org.gnome.desktop.screensaver'); + + if (wallpaperArray.length < 1) + throw new Error('Empty wallpaper array'); + + const wallpaperUri = `file://${wallpaperArray[0]}`; + let usedWallpaperPaths: string[] = []; // // @@ -265,60 +287,62 @@ class WallpaperController { const changeType = this._settings.getEnum('change-type'); if (changeType === 0 || changeType === 2) { - try { - if (this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable()) { - const wallpaperArray = this._fillMonitorsFromHistory(path); + if (multiMonitor) { + const newWallpaperPaths = this._fillMonitorsFromHistory(wallpaperArray, monitorCount); - await this._hydraPaper.run(wallpaperArray); + await this._hydraPaper.run(newWallpaperPaths); - // Manually set key for darkmode because that's way faster - background_setting.setString('picture-uri-dark', background_setting.getString('picture-uri')); - } else { - // set "picture-options" to "zoom" for single wallpapers - // hydrapaper changes this to "spanned" - background_setting.setString('picture-options', 'zoom'); - this._setPictureUriOfSettingsObject(background_setting, wallpaperUri); - } - } catch (error) { - this._logger.warn(String(error)); + usedWallpaperPaths = newWallpaperPaths; + + // Manually set key for darkmode because that's way faster + backgroundSettings.setString('picture-uri-dark', backgroundSettings.getString('picture-uri')); + } else { + // set "picture-options" to "zoom" for single wallpapers + // hydrapaper changes this to "spanned" + backgroundSettings.setString('picture-options', 'zoom'); + this._setPictureUriOfSettingsObject(backgroundSettings, wallpaperUri); + usedWallpaperPaths.push(wallpaperUri); } } if (changeType === 1) { - try { - if (this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable()) { - const wallpaperArray = this._fillMonitorsFromHistory(path); + if (multiMonitor) { + const newWallpaperPaths = this._fillMonitorsFromHistory(wallpaperArray, monitorCount); - // Remember keys, HydraPaper will change these - const tmpBackground = background_setting.getString('picture-uri-dark'); - const tmpMode = background_setting.getString('picture-options'); + // Remember keys, HydraPaper will change these + const tmpBackground = backgroundSettings.getString('picture-uri-dark'); + const tmpMode = backgroundSettings.getString('picture-options'); - // Force HydraPaper to target a different resulting image by using darkmode - await this._hydraPaper.run(wallpaperArray, true); + // Force HydraPaper to target a different resulting image by using darkmode + await this._hydraPaper.run(newWallpaperPaths, true); - screensaver_setting.setString('picture-options', 'spanned'); - this._setPictureUriOfSettingsObject(screensaver_setting, background_setting.getString('picture-uri-dark')); + newWallpaperPaths.forEach(path => { + if (!usedWallpaperPaths.includes(path)) + usedWallpaperPaths.push(path); + }); - // HydraPaper possibly changed these, change them back - background_setting.setString('picture-uri-dark', tmpBackground); - background_setting.setString('picture-options', tmpMode); - } else { - // set "picture-options" to "zoom" for single wallpapers - screensaver_setting.setString('picture-options', 'zoom'); - this._setPictureUriOfSettingsObject(screensaver_setting, wallpaperUri); - } - } catch (error) { - this._logger.warn(String(error)); + screensaverSettings.setString('picture-options', 'spanned'); + this._setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); + + // HydraPaper possibly changed these, change them back + backgroundSettings.setString('picture-uri-dark', tmpBackground); + backgroundSettings.setString('picture-options', tmpMode); + } else { + // set "picture-options" to "zoom" for single wallpapers + screensaverSettings.setString('picture-options', 'zoom'); + this._setPictureUriOfSettingsObject(screensaverSettings, wallpaperUri); + if (!usedWallpaperPaths.includes(wallpaperUri)) + usedWallpaperPaths.push(wallpaperUri); } } if (changeType === 2) - this._setPictureUriOfSettingsObject(screensaver_setting, background_setting.getString('picture-uri')); - + this._setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + // TODO: this ignores the lock-screen // Run general post command const commandString = this._settings.getString('general-post-command'); - const generalPostCommandArray = this._getCommandArray(commandString, path); + const generalPostCommandArray = this._getCommandArray(commandString, backgroundSettings.getString('picture-uri')); if (generalPostCommandArray !== null) { try { await Utils.execCheck(generalPostCommandArray); @@ -326,25 +350,26 @@ class WallpaperController { this._logger.warn(String(error)); } } + + return usedWallpaperPaths; } - private _fillMonitorsFromHistory(newWallpaperPath: string) { - const monitorCount = Utils.getMonitorCount(); - const wallpaperArray = [newWallpaperPath]; + private _fillMonitorsFromHistory(wallpaperArray: string[], monitorCount: number) { + const newWallpaperArray: string[] = [...wallpaperArray]; // Abuse history to fill missing images - for (let index = 1; index < monitorCount; index++) { + for (let index = newWallpaperArray.length; index < monitorCount; index++) { let historyElement; do historyElement = this._historyController.getRandom(); - while (this._historyController.history.length > monitorCount && historyElement.path && wallpaperArray.includes(historyElement.path)); + while (this._historyController.history.length > monitorCount && historyElement.path && newWallpaperArray.includes(historyElement.path)); // ensure different wallpaper for all displays if possible if (historyElement.path) - wallpaperArray.push(historyElement.path); + newWallpaperArray.push(historyElement.path); } - return wallpaperArray; + return newWallpaperArray; } /** @@ -380,62 +405,83 @@ class WallpaperController { setProp(property); } - setWallpaper(historyId: string) { + async setWallpaper(historyId: string) { const historyElement = this._historyController.get(historyId); - if (historyElement?.id && historyElement.path && this._historyController.promoteToActive(historyElement.id)) - this._setBackground(historyElement.path).catch(logError); - else + if (historyElement?.id && historyElement.path && this._historyController.promoteToActive(historyElement.id)) { + const monitorCount = this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable() ? Utils.getMonitorCount() : 1; + const usedWallpapers = (await this._setBackground([historyElement.path], monitorCount)).reverse(); + usedWallpapers.forEach(path => { + const id = this._historyController.getEntryByPath(path)?.id; + if (id) + this._historyController.promoteToActive(id); + }); + } else { this._logger.warn(`The history id (${historyId}) could not be found.`); - // TODO: Error handling history id not found. + } + // TODO: Error handling history id not found. } async fetchNewWallpaper() { this._startLoadingHooks.forEach(element => element()); try { + const monitorCount = this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable() ? Utils.getMonitorCount() : 1; + const imageAdapters = this._getRandomAdapter(monitorCount); + if (!this._prohibitTimer) this._timer.reset(); // reset timer - const returnObject = this._getRandomAdapter(); - - let historyEntry: HistoryModule.HistoryEntry; - let sourceFile: Gio.File; - historyEntry = await returnObject.adapter.requestRandomImage(); + const imageAdapter = this._getRandomAdapter(); + const newWallpapers = await imageAdapter.adapter.requestRandomImage(monitorCount); + const fetchPromises = newWallpapers.map(element => { + element.adapter.id = imageAdapter.adapterId; + element.adapter.type = imageAdapter.adapterType; - this._logger.info(`Requesting image: ${historyEntry.source.imageDownloadUrl}`); - sourceFile = await returnObject.adapter.fetchFile(historyEntry); + this._logger.info(`Requesting image: ${element.source.imageDownloadUrl}`); + return imageAdapter.adapter.fetchFile(element); + }); - historyEntry.adapter.id = returnObject.adapterId; - historyEntry.adapter.type = returnObject.adapterType; + // wait for all fetching images + // FIXME: shove this into the adapter itself so rate limiting can be adjusted + const imagePaths = await Promise.all(fetchPromises); // Move file to unique naming - const targetFolder = sourceFile.get_parent(); - const targetFile = targetFolder?.get_child(historyEntry.id); + const movePromises = imagePaths.map((path, index) => { + const targetFolder = path.get_parent(); + const targetFile = targetFolder?.get_child(newWallpapers[index].id); - if (!targetFile) - throw new Error('Failed getting targetFile'); + if (!targetFile) + throw new Error('Failed getting targetFile'); + + newWallpapers[index].path = targetFile.get_path(); - try { // This function is Gio._promisified - if (!await sourceFile.move_async(targetFile, Gio.FileCopyFlags.NONE, 0, null, null)) - throw new Error('Failed copying unique image.'); - } catch (moveError) { - if (moveError === Gio.IOErrorEnum.EXISTS) - this._logger.warn('Image already exists in location.'); - else - throw moveError; - } + return path.move_async(targetFile, Gio.FileCopyFlags.NONE, 0, null, null); + }); - historyEntry.path = targetFile.get_path(); + // wait for all images to be moved + await Promise.all(movePromises); - if (!historyEntry.path) - throw new Error('Failed getting historyEntry.path'); + const wallpaperPaths = newWallpapers.map(element => { + if (element.path) + return element.path; - await this._setBackground(historyEntry.path); + // eslint-disable-next-line + return; + }) as string[]; // cast because we made sure it's defined + const usedWallpapers = (await this._setBackground(wallpaperPaths, monitorCount)).reverse(); - // insert file into history - this._historyController.insert(historyEntry); + usedWallpapers.forEach(path => { + const id = this._historyController.getEntryByPath(path)?.id; + if (id) + this._historyController.promoteToActive(id); + }); + + // insert new wallpapers into history + newWallpapers.reverse().forEach(element => { + this._historyController.insert(element); + }); } finally { this._stopLoadingHooks.forEach(element => element()); } @@ -477,13 +523,21 @@ class WallpaperController { this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => { this._timeout = null; - const currentWallpaperPath = this._historyController.getCurrentElement().path; - if (this._resetWallpaper && currentWallpaperPath) { - this._setBackground(currentWallpaperPath).catch(logError); + + const currentWallpaperPaths: string[] = []; + for (let index = 0; index < Utils.getMonitorCount() && index < this._historyController.history.length; index++) { + const path = this._historyController.history[index].path; + if (path) + currentWallpaperPaths.push(path); + } + + if (this._resetWallpaper) { + this._setBackground(currentWallpaperPaths, 1).catch(logError); this._resetWallpaper = false; } else if (this._previewId !== undefined) { - this._setBackground(this.wallpaperLocation + this._previewId).catch(logError); + this._setBackground([this.wallpaperLocation + this._previewId], Utils.getMonitorCount()).catch(logError); } + return false; }); } From ff39a29955d5aa5e5556174ece0c3f5f50949330 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 11 Dec 2022 16:06:01 +0100 Subject: [PATCH 04/87] Allow requesting multiple images from different adapter --- src/adapter/baseAdapter.ts | 4 +- src/adapter/localFolder.ts | 4 +- src/utils.ts | 1 + src/wallpaperController.ts | 189 ++++++++++++++++++++++++------------- 4 files changed, 133 insertions(+), 65 deletions(-) diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 7d4d4577..85a451c0 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -67,7 +67,9 @@ abstract class BaseAdapter { fstream.write(response_data_bytes, null); fstream.close(null); - return file; + historyEntry.path = file.get_path(); + + return historyEntry; } protected _includesWallpaper(array: HistoryEntry[], uri: string) { diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 42455d55..164c771f 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -68,7 +68,9 @@ class LocalFolderAdapter extends BaseAdapter { if (!await sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null)) throw new Error('Failed copying image.'); - return targetFile; + historyEntry.path = targetFile.get_path(); + + return historyEntry; } // https://gjs.guide/guides/gio/file-operations.html#recursively-deleting-a-directory diff --git a/src/utils.ts b/src/utils.ts index af5fdef4..aaa999a1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -108,6 +108,7 @@ function getMonitorCount() { * @param {number} size Maximum */ function getRandomNumber(size: number) { + // https://stackoverflow.com/a/5915122 return Math.floor(Math.random() * size); } diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 380fd4b5..438959e8 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -25,6 +25,13 @@ const Self = ExtensionUtils.getCurrentExtension(); // https://gjs.guide/guides/gjs/asynchronous-programming.html#promisify-helper Gio._promisify(Gio.File.prototype, 'move_async', 'move_finish'); +interface RandomAdapterResult { + adapter: BaseAdapter, + id: string, + type: number, + imageCount: number + } + class WallpaperController { wallpaperLocation: string; prohibitNewWallpaper = false; @@ -188,79 +195,123 @@ class WallpaperController { /** randomly returns an enabled and configured SourceAdapter returns a default UnsplashAdapter in case of failure + * + * @param {number} count The amount of adapter requested */ - private _getRandomAdapter() { - const sourceID = this._getRandomSource(); + private _getRandomAdapter(count: number) { + const sourceIDs = this._getRandomSource(count); + const randomAdapterResult: RandomAdapterResult[] = []; + + if (sourceIDs.length < 1 || sourceIDs[0] === '-1') { + randomAdapterResult.push({ + adapter: new UnsplashAdapter(null, null, this.wallpaperLocation), + id: '-1', + type: 0, + imageCount: count, + }); + return randomAdapterResult; + } - let imageSourceAdapter: BaseAdapter; - let sourceName = 'undefined'; - let sourceType = -1; + /** + * + * @param {RandomAdapterResult[]} array Array of already chosen adapter + * @param {number} type Type of the source + */ + function _arrayIncludes(array: RandomAdapterResult[], type: number) { + for (const element of array) { + if (element.type === type) + return element; + } + return null; + } - if (sourceID !== '-1') { + for (let index = 0; index < sourceIDs.length; index++) { + const sourceID = sourceIDs[index]; const path = `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${sourceID}/`; const settingsGeneral = new SettingsModule.Settings(SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); + let imageSourceAdapter: BaseAdapter; + let sourceName = 'undefined'; + let sourceType = -1; + sourceName = settingsGeneral.getString('name'); sourceType = settingsGeneral.getEnum('type'); - } - try { - switch (sourceType) { - case 0: - imageSourceAdapter = new UnsplashAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 1: - imageSourceAdapter = new WallhavenAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 2: - imageSourceAdapter = new RedditAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 3: - imageSourceAdapter = new GenericJsonAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 4: - imageSourceAdapter = new LocalFolderAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - case 5: - imageSourceAdapter = new UrlSourceAdapter(sourceID, sourceName, this.wallpaperLocation); - break; - default: + const availableAdapter = _arrayIncludes(randomAdapterResult, sourceType); + if (availableAdapter) { + availableAdapter.imageCount++; + continue; + } + + try { + switch (sourceType) { + case 0: + imageSourceAdapter = new UnsplashAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 1: + imageSourceAdapter = new WallhavenAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 2: + imageSourceAdapter = new RedditAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 3: + imageSourceAdapter = new GenericJsonAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 4: + imageSourceAdapter = new LocalFolderAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + case 5: + imageSourceAdapter = new UrlSourceAdapter(sourceID, sourceName, this.wallpaperLocation); + break; + default: + imageSourceAdapter = new UnsplashAdapter(null, null, this.wallpaperLocation); + sourceType = 0; + break; + } + } catch (error) { + this._logger.warn('Had errors, fetching with default settings.'); imageSourceAdapter = new UnsplashAdapter(null, null, this.wallpaperLocation); sourceType = 0; - break; } - } catch (error) { - this._logger.warn('Had errors, fetching with default settings.'); - imageSourceAdapter = new UnsplashAdapter(null, null, this.wallpaperLocation); - sourceType = 0; + + randomAdapterResult.push({ + adapter: imageSourceAdapter, + id: sourceID, + type: sourceType, + imageCount: 1, + }); } - return { - adapter: imageSourceAdapter, - adapterId: sourceID, - adapterType: sourceType, - }; + return randomAdapterResult; } - private _getRandomSource() { + /** + * + * @param {number} count Amount of requested source IDs + * @returns Array of source IDs or ['-1'] in case of failure + */ + private _getRandomSource(count: number) { + const sourceResult: string[] = []; const sources: string[] = this._settings.getStrv('sources'); if (sources === null || sources.length < 1) - return '-1'; - + return ['-1']; - const enabled_sources = sources.filter(element => { + const enabledSources = sources.filter(element => { const path = `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${element}/`; const settingsGeneral = new SettingsModule.Settings(SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); return settingsGeneral.getBoolean('enabled'); }); - if (enabled_sources === null || enabled_sources.length < 1) - return '-1'; + if (enabledSources === null || enabledSources.length < 1) + return ['-1']; + for (let index = 0; index < count; index++) { + const chosenSource = enabledSources[Utils.getRandomNumber(enabledSources.length)]; + sourceResult.push(chosenSource); + } - // https://stackoverflow.com/a/5915122 - return enabled_sources[Utils.getRandomNumber(enabled_sources.length)]; + return sourceResult; } /** @@ -426,44 +477,56 @@ class WallpaperController { this._startLoadingHooks.forEach(element => element()); try { + this._timer.reset(); // reset timer + const monitorCount = this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable() ? Utils.getMonitorCount() : 1; const imageAdapters = this._getRandomAdapter(monitorCount); - if (!this._prohibitTimer) - this._timer.reset(); // reset timer + const randomImagePromises = imageAdapters.map(element => { + return element.adapter.requestRandomImage(element.imageCount); + }); + const newWallpapers = await Promise.all(randomImagePromises); + + const fetchPromises = newWallpapers.flatMap((array, index) => { + const fetchPromiseArray: Promise[] = []; - const imageAdapter = this._getRandomAdapter(); - const newWallpapers = await imageAdapter.adapter.requestRandomImage(monitorCount); - const fetchPromises = newWallpapers.map(element => { - element.adapter.id = imageAdapter.adapterId; - element.adapter.type = imageAdapter.adapterType; + for (const element of array) { + element.adapter.id = imageAdapters[index].id; + element.adapter.type = imageAdapters[index].type; - this._logger.info(`Requesting image: ${element.source.imageDownloadUrl}`); - return imageAdapter.adapter.fetchFile(element); + this._logger.info(`Requesting image: ${element.source.imageDownloadUrl}`); + fetchPromiseArray.push(imageAdapters[index].adapter.fetchFile(element)); + } + + return fetchPromiseArray; }); // wait for all fetching images // FIXME: shove this into the adapter itself so rate limiting can be adjusted - const imagePaths = await Promise.all(fetchPromises); + const newImageEntries = await Promise.all(fetchPromises); // Move file to unique naming - const movePromises = imagePaths.map((path, index) => { - const targetFolder = path.get_parent(); - const targetFile = targetFolder?.get_child(newWallpapers[index].id); + const movePromises = newImageEntries.map(entry => { + if (!entry.path) + return Promise.resolve(false); + + const file = Gio.File.new_for_path(entry.path); + const targetFolder = file.get_parent(); + const targetFile = targetFolder?.get_child(entry.id); if (!targetFile) throw new Error('Failed getting targetFile'); - newWallpapers[index].path = targetFile.get_path(); + entry.path = targetFile.get_path(); // This function is Gio._promisified - return path.move_async(targetFile, Gio.FileCopyFlags.NONE, 0, null, null); + return file.move_async(targetFile, Gio.FileCopyFlags.NONE, 0, null, null); }); // wait for all images to be moved await Promise.all(movePromises); - const wallpaperPaths = newWallpapers.map(element => { + const wallpaperPaths = newImageEntries.map(element => { if (element.path) return element.path; @@ -479,7 +542,7 @@ class WallpaperController { }); // insert new wallpapers into history - newWallpapers.reverse().forEach(element => { + newImageEntries.reverse().forEach(element => { this._historyController.insert(element); }); } finally { From a8a8d3d3ab2eb431995a1004170d634caea9d7ed Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 11 Dec 2022 18:39:18 +0100 Subject: [PATCH 05/87] Fix timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …for real --- src/timer.ts | 76 ++++++++++++++++++++++++++++++-------- src/wallpaperController.ts | 19 +++------- 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/timer.ts b/src/timer.ts index 844d98a4..04eff9b9 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -12,8 +12,9 @@ class AFTimer { private _logger = new Logger('RWG3', 'Timer'); private _settings = new Settings(); private _timeout?: number = undefined; - private _timeoutEndCallback?: () => void = undefined; + private _timeoutEndCallback?: () => Promise = undefined; private _minutes = 30; + private _paused = false; static getTimer(): AFTimer { if (!this._afTimerInstance) @@ -29,17 +30,46 @@ class AFTimer { this._afTimerInstance = null; } + /** + * Continue a paused timer. + * + * Removes the pause lock and starts the timer. + * If the trigger time was surpassed while paused the callback gets + * called directly and the next trigger is scheduled at the + * next correct time frame repeatedly. + */ + continue() { + this._paused = false; + this.start(); + } + isActive() { return this._settings.getBoolean('auto-fetch'); } + isPaused() { + return this._paused; + } + + /** + * Pauses the timer. + * + * This stops any currently running timer and prohibits starting + * until continue() was called. + * 'timer-last-trigger' stays the same. + */ + pause() { + this._paused = true; + this.cleanup(); + } + remainingMinutes() { const minutesElapsed = this._minutesElapsed(); const remainder = minutesElapsed % this._minutes; return Math.max(this._minutes - remainder, 0); } - registerCallback(callback: () => void) { + registerCallback(callback: () => Promise) { this._timeoutEndCallback = callback; } @@ -54,20 +84,34 @@ class AFTimer { /** * Start the timer. + * + * Starts the timer if not paused. + * Removes any previously running timer. + * If the trigger time was surpassed the callback gets started + * directly and the next trigger is scheduled at the + * next correct time frame repeatedly. */ - start() { + async start() { + if (this._paused) + return; + this.cleanup(); const last = this._settings.getInt64('timer-last-trigger'); if (last === 0) - this.reset(); + this._reset(); const millisecondsRemaining = this.remainingMinutes() * 60 * 1000; // set new wallpaper if the interval was surpassed and set the timestamp to when it should have been updated if (this._surpassedInterval()) { - if (this._timeoutEndCallback) - this._timeoutEndCallback(); + if (this._timeoutEndCallback) { + try { + await this._timeoutEndCallback(); + } catch (error) { + this._logger.error(error); + } + } const millisecondsOverdue = (this._minutes * 60 * 1000) - millisecondsRemaining; this._settings.setInt64('timer-last-trigger', Date.now() - millisecondsOverdue); @@ -75,12 +119,15 @@ class AFTimer { // actual timer function this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, millisecondsRemaining, () => { - if (this._timeoutEndCallback) - this._timeoutEndCallback(); - - this.reset(); // reset timer - - return GLib.SOURCE_CONTINUE; + if (this._timeoutEndCallback) { + this._timeoutEndCallback().then(() => { + this._reset(); + this.start().catch(this._logger.error); + }).catch(this._logger.error).finally(() => { + return GLib.SOURCE_REMOVE; + }); + } + return GLib.SOURCE_REMOVE; }); } @@ -103,11 +150,10 @@ class AFTimer { } /** - * Reset the timer. + * Sets the last activation time to [now]. This doesn't affect already running timer. */ - reset() { + private _reset() { this._settings.setInt64('timer-last-trigger', new Date().getTime()); - this.cleanup(); } private _minutesElapsed() { diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 438959e8..64348e13 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -40,7 +40,6 @@ class WallpaperController { private _logger = new Logger('RWG3', 'WallpaperController'); private _settings = new SettingsModule.Settings(); private _timer = Timer.getTimer(); - private _prohibitTimer = false; private _historyController: HistoryModule.HistoryController; private _hydraPaper = new HydraPaper(); private _autoFetch = {active: false, duration: 30}; @@ -135,17 +134,15 @@ class WallpaperController { private _pauseTimer() { if (this._backendConnection.getBoolean('pause-timer')) { - this._prohibitTimer = true; - this._updateAutoFetching(); + this._timer.pause(); } else { - this._prohibitTimer = false; + this._timer.continue(); // Switching the switch in the menu closes the menu which triggers a hover event // Prohibit that from emitting because a paused timer could have surpassed the interval // and try to fetch new wallpaper which would be interrupted by a wallpaper reset caused - // the the closing menu event. + // by the closing menu event. this.prohibitNewWallpaper = true; - this._updateAutoFetching(); // And activate emitting again after a second GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => { @@ -179,14 +176,12 @@ class WallpaperController { this._autoFetch.active = this._settings.getBoolean('auto-fetch'); // only start timer if not in context of preferences window - if (!this._prohibitTimer && this._autoFetch.active) { + if (this._autoFetch.active) { this._timer.registerCallback(() => { - this.fetchNewWallpaper().catch(logError); + return this.fetchNewWallpaper(); }); this._timer.setMinutes(this._autoFetch.duration); - this._timer.start(); - } else if (this._prohibitTimer && this._autoFetch.active) { - this._timer.cleanup(); + this._timer.start().catch(this._logger.error); } else { this._timer.stop(); } @@ -477,8 +472,6 @@ class WallpaperController { this._startLoadingHooks.forEach(element => element()); try { - this._timer.reset(); // reset timer - const monitorCount = this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable() ? Utils.getMonitorCount() : 1; const imageAdapters = this._getRandomAdapter(monitorCount); From 661d089edff03fd9b36d6a3034a37cfbbbd74e11 Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 13 Dec 2022 16:36:42 +0100 Subject: [PATCH 06/87] Selectable log level --- .eslintrc.yml | 6 +- src/extension.ts | 8 +-- src/historyMenuElements.ts | 8 +-- src/logger.ts | 59 ++++++++++++------- src/prefs.ts | 34 +++-------- src/randomWallpaperMenu.ts | 11 ++-- src/schemas/.gitignore | 1 - ...ns.space.iflow.randomwallpaper.gschema.xml | 14 +++++ src/settings.ts | 2 - src/ui/.gitignore | 1 - src/ui/pageGeneral.blp | 5 ++ src/utils.ts | 37 ++++++++++-- src/wallpaperController.ts | 9 ++- 13 files changed, 117 insertions(+), 78 deletions(-) delete mode 100644 src/schemas/.gitignore delete mode 100644 src/ui/.gitignore diff --git a/.eslintrc.yml b/.eslintrc.yml index 2c100f96..58368f1c 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -165,7 +165,8 @@ rules: no-return-assign: error no-return-await: error no-self-compare: error - no-shadow: error + no-shadow: off + "@typescript-eslint/no-shadow": error no-shadow-restricted-names: error no-spaced-func: error no-tabs: error @@ -175,7 +176,8 @@ rules: no-undef-init: error no-unneeded-ternary: error no-unused-expressions: error - no-unused-vars: + no-unused-vars: off + "@typescript-eslint/no-unused-vars": - error # Vars use a suffix _ instead of a prefix because of file-scope private vars - varsIgnorePattern: (^unused|_$) diff --git a/src/extension.ts b/src/extension.ts index fcff5572..f9f91a78 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -11,7 +11,7 @@ let RandomWallpaperMenu: typeof RandomWallpaperMenuNamespace | null = null; /** * */ -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars function init() { return new Extension(); } @@ -36,12 +36,10 @@ class Extension { this._logger.info('Enable extension.'); this._panelMenu.init(); }).catch(error => { - if (this._logger) { + if (this._logger) this._logger.error(error); + else logError(error); - } else { - logError(error); - } }); } diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index d89d1264..f6145232 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -89,7 +89,7 @@ const HistoryElement = GObject.registerClass({ const authorItem = new PopupMenu.PopupMenuItem(`Image By: ${this.historyEntry.source.author}`); authorItem.connect('activate', () => { if (this.historyEntry.source.authorUrl) - Utils.execCheck(['xdg-open', this.historyEntry.source.authorUrl]).catch(logError); + Utils.execCheck(['xdg-open', this.historyEntry.source.authorUrl]).catch(this._logger.error); }); this.menu.addMenuItem(authorItem); @@ -100,7 +100,7 @@ const HistoryElement = GObject.registerClass({ const sourceItem = new PopupMenu.PopupMenuItem(`Image From: ${this.historyEntry.source.source}`); sourceItem.connect('activate', () => { if (this.historyEntry.source.sourceUrl) - Utils.execCheck(['xdg-open', this.historyEntry.source.sourceUrl]).catch(logError); + Utils.execCheck(['xdg-open', this.historyEntry.source.sourceUrl]).catch(this._logger.error); }); this.menu.addMenuItem(sourceItem); @@ -109,7 +109,7 @@ const HistoryElement = GObject.registerClass({ const imageUrlItem = new PopupMenu.PopupMenuItem('Open Image In Browser'); imageUrlItem.connect('activate', () => { if (this.historyEntry.source.imageLinkUrl) - Utils.execCheck(['xdg-open', this.historyEntry.source.imageLinkUrl]).catch(logError); + Utils.execCheck(['xdg-open', this.historyEntry.source.imageLinkUrl]).catch(this._logger.error); }); this.menu.addMenuItem(imageUrlItem); @@ -132,7 +132,7 @@ const HistoryElement = GObject.registerClass({ const copyToFavorites = new PopupMenu.PopupMenuItem('Save For Later'); copyToFavorites.connect('activate', () => { - this._saveImage().catch(logError); + this._saveImage().catch(this._logger.error); }); this.menu.addMenuItem(copyToFavorites); diff --git a/src/logger.ts b/src/logger.ts index 73f73c92..0b4164f8 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,51 +1,68 @@ -// TODO: use an enum once moved to TS -const LOG_LEVEL = { - SILENT: 0, - ERROR: 1, - WARN: 2, - INFO: 3, - DEBUG: 4, -}; - -// TODO: add UI option or at least ENV variable (this is a quick workaround to conform to extension review requirements) -const CURRENT_LOG_LEVEL = LOG_LEVEL.WARN; +// https://gitlab.gnome.org/GNOME/gjs/-/blob/master/doc/Logging.md + +import {Settings} from './settings.js'; + +const enum LogLevel { + SILENT, + ERROR, + WARNING, + INFO, + DEBUG, +} +type LogLevelStrings = keyof typeof LogLevel; class Logger { private _prefix: string; private _callingClass: string; + private _settings = new Settings(); constructor(prefix: string, callingClass: string) { this._prefix = prefix; this._callingClass = callingClass; } - private _log(level: string, message: unknown) { - log(`${this._prefix} [${level}] >> ${this._callingClass} :: ${message}`); + private _log(level: LogLevelStrings, message: unknown) { + let errorMessage = String(message); + + if (message instanceof Error) + errorMessage = message.message; + + // This logs messages with GLib.LogLevelFlags.LEVEL_MESSAGE + log(`${this._prefix} [${level}] >> ${this._callingClass} :: ${errorMessage}`); + + // Log stack trace if available + if (message instanceof Error && message.stack) + // This logs messages with GLib.LogLevelFlags.LEVEL_WARNING + logError(message); + } + + private _selectedLogLevel() { + return this._settings.getEnum('log-level'); } - debug(message: string) { - if (CURRENT_LOG_LEVEL < LOG_LEVEL.DEBUG) + debug(message: unknown) { + if (this._selectedLogLevel() < LogLevel.DEBUG) return; this._log('DEBUG', message); } - info(message: string) { - if (CURRENT_LOG_LEVEL < LOG_LEVEL.INFO) + info(message: unknown) { + if (this._selectedLogLevel() < LogLevel.INFO) return; this._log('INFO', message); } - warn(message: string) { - if (CURRENT_LOG_LEVEL < LOG_LEVEL.WARN) + warn(message: unknown) { + if (this._selectedLogLevel() < LogLevel.WARNING) return; this._log('WARNING', message); } - error(message: string) { - if (CURRENT_LOG_LEVEL < LOG_LEVEL.ERROR) + error(message: unknown) { + if (this._selectedLogLevel() < LogLevel.ERROR) return; this._log('ERROR', message); diff --git a/src/prefs.ts b/src/prefs.ts index 27ede15e..a2466040 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -1,9 +1,9 @@ import * as Gio from 'gi://Gio'; import * as Gtk from 'gi://Gtk'; +import * as Adw from '@gi/gtk4/adw/adw'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; -import * as Adw from '@gi/gtk4/adw/adw'; import type * as SettingsNamespace from './settings.js'; import type * as UtilsNamespace from './utils.js'; import type * as LoggerNamespace from './logger.js'; @@ -19,7 +19,7 @@ const Self = ExtensionUtils.getCurrentExtension(); /** * */ -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars function init() { // Convenience.initTranslations(); } @@ -35,9 +35,9 @@ function init() { * * @param {Adw.PreferencesWindow} window Window the extension should fill */ -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars function fillPreferencesWindow(window: Adw.PreferencesWindow) { - window.set_default_size(-1, 720); + window.set_default_size(600, 720); // temporary fill window to prevent error message until modules are loaded const tmpPage = new Adw.PreferencesPage(); window.add(tmpPage); @@ -45,7 +45,6 @@ function fillPreferencesWindow(window: Adw.PreferencesWindow) { new RandomWallpaperSettings(window, tmpPage); } - // 40 < Gnome < 42 // function buildPrefsWidget() { // let window = new Adw.PreferencesWindow(); @@ -82,7 +81,8 @@ class RandomWallpaperSettings { this._builder.add_from_file(`${Self.path}/ui/pageGeneral.ui`); this._builder.add_from_file(`${Self.path}/ui/pageSources.ui`); - this._fillTypeComboRow(); + Utils.fillComboRowFromEnum(this._builder.get_object('combo_background_type'), this._settings, 'change-type'); + Utils.fillComboRowFromEnum(this._builder.get_object('log_level'), this._settings, 'log-level'); this._settings.bind('minutes', this._builder.get_object('duration_minutes'), @@ -145,7 +145,7 @@ class RandomWallpaperSettings { if (new module.HydraPaper().isAvailable()) // eslint-disable-next-line no-extra-parens (this._builder.get_object('multiple_displays_row') as Adw.ActionRow).set_sensitive(true); - }).catch(logError); + }).catch(this._logger.error); }).catch(error => { logError(error); throw error; @@ -175,26 +175,6 @@ class RandomWallpaperSettings { Settings = moduleSettings; } - private _fillTypeComboRow() { - const comboRow: Adw.ComboRow = this._builder.get_object('combo_background_type'); - - // Fill combo from settings enum - const availableTypes = this._settings.getSchema().get_key('change-type').get_range(); // GLib.Variant (sv) - // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) - // s should be 'enum' - // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. - // v is 'as' - const availableTypesNames = availableTypes.get_child_value(1).get_variant().get_strv(); - - const stringList = Gtk.StringList.new(availableTypesNames); - comboRow.model = stringList; - comboRow.selected = this._settings.getEnum('change-type'); - - comboRow.connect('notify::selected', _comboRow => { - this._settings.setEnum('change-type', _comboRow.selected); - }); - } - private _bindButtons() { const newWallpaperButton: Adw.ActionRow = this._builder.get_object('request_new_wallpaper'); const newWallpaperButtonLabel = newWallpaperButton.get_child() as Gtk.Label | null; diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index df533532..b5bf6c8b 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -99,7 +99,7 @@ class RandomWallpaperMenu { this._wallpaperController.prohibitNewWallpaper = false; }).catch(error => { this._wallpaperController.prohibitNewWallpaper = false; - logError(error); + this._logger.error(error); }); }); @@ -111,7 +111,7 @@ class RandomWallpaperMenu { // Open Wallpaper Folder openFolder.connect('activate', () => { const uri = GLib.filename_to_uri(this._wallpaperController.wallpaperLocation, ''); - Utils.execCheck(['xdg-open', uri]).catch(logError); + Utils.execCheck(['xdg-open', uri]).catch(this._logger.error); }); openSettings.connect('activate', () => { @@ -197,10 +197,9 @@ class RandomWallpaperMenu { /** * @this {RandomWallpaperMenu} RandomWallpaperMenu - * @param {CustomElements.HistoryElement} actor The activating panel item + * @param {CustomElements.HistoryElement} unusedActor The activating panel item */ - // eslint-disable-next-line no-unused-vars - function onLeave(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { + function onLeave(this: RandomWallpaperMenu, unusedActor: typeof CustomElements.HistoryElement) { if (!this._wallpaperController.prohibitNewWallpaper) this._wallpaperController.resetWallpaper(); } @@ -227,7 +226,7 @@ class RandomWallpaperMenu { this._wallpaperController.prohibitNewWallpaper = false; }).catch(error => { this._wallpaperController.prohibitNewWallpaper = false; - logError(error); + this._logger.error(error); }); } diff --git a/src/schemas/.gitignore b/src/schemas/.gitignore deleted file mode 100644 index a9bbac5d..00000000 --- a/src/schemas/.gitignore +++ /dev/null @@ -1 +0,0 @@ -gschemas.compiled diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 243e3d2d..224bf5e3 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -8,6 +8,14 @@ + + + + + + + + @@ -62,6 +70,12 @@ Allows to choose what backgrounds will be changed. + + "Warning" + Tier of logs + Choose what minimal tier of logs should appear in the journal. + + false Disable hover preview diff --git a/src/settings.ts b/src/settings.ts index c92d26b7..9288143c 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -128,7 +128,6 @@ class Settings { if (!schemaId) schemaId = Self.metadata['settings-schema']; - // Expect USER extensions to have a schemas/ subfolder, otherwise assume a // SYSTEM extension that has been installed in the same prefix as the shell const schemaDir = Self.dir.get_child('schemas'); @@ -146,7 +145,6 @@ class Settings { if (!schemaObj) throw new Error(`Schema ${schemaId} could not be found for extension ${Self.metadata.uuid}. Please check your installation`); - return schemaObj; } } diff --git a/src/ui/.gitignore b/src/ui/.gitignore deleted file mode 100644 index 1a826ead..00000000 --- a/src/ui/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.ui diff --git a/src/ui/pageGeneral.blp b/src/ui/pageGeneral.blp index 23fe6620..a0e5caff 100644 --- a/src/ui/pageGeneral.blp +++ b/src/ui/pageGeneral.blp @@ -62,6 +62,11 @@ Adw.PreferencesPage page_general { valign: center; } } + + Adw.ComboRow log_level { + title: _("Log level"); + subtitle: _("Set the tier of warnings appearing in the journal"); + } } Adw.PreferencesGroup { diff --git a/src/utils.ts b/src/utils.ts index aaa999a1..e826f5c0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,11 @@ import * as Gdk from 'gi://Gdk'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; +import * as Gtk from 'gi://Gtk'; + +import * as Adw from '@gi/gtk4/adw/adw'; + +import {Settings} from './settings.js'; /** * Returns a promise which resolves cleanly or rejects according to the underlying subprocess. @@ -59,7 +64,6 @@ function fileName(uri: string) { while (_isURIEncoded(uri)) uri = decodeURIComponent(uri); - let base = uri.substring(uri.lastIndexOf('/') + 1); if (base.indexOf('?') >= 0) base = base.substring(0, base.indexOf('?')); @@ -67,6 +71,30 @@ function fileName(uri: string) { return base; } +/** + * + * @param {Adw.ComboRow} comboRow ComboRow to fill and connect + * @param {Settings} settings Settings schema to scan values for + * @param {string} key Key where to find values in the settings schema + */ +function fillComboRowFromEnum(comboRow: Adw.ComboRow, settings: Settings, key: string) { + // Fill combo from settings enum + const availableTypes = settings.getSchema().get_key(key).get_range(); // GLib.Variant (sv) + // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) + // s should be 'enum' + // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. + // v is 'as' + const availableTypesNames = availableTypes.get_child_value(1).get_variant().get_strv(); + + const stringList = Gtk.StringList.new(availableTypesNames); + comboRow.model = stringList; + comboRow.selected = settings.getEnum(key); + + comboRow.connect('notify::selected', (_comboRow: Adw.ComboRow) => { + settings.setEnum(key, _comboRow.selected); + }); +} + // https://stackoverflow.com/a/32859917 /** * @@ -137,10 +165,11 @@ function removeItemOnce(array: T[], value: T) { } export { - getMonitorCount, - getRandomNumber, execCheck, - findFirstDifference, fileName, + fillComboRowFromEnum, + findFirstDifference, + getMonitorCount, + getRandomNumber, removeItemOnce }; diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 64348e13..b09736ea 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -56,7 +56,6 @@ class WallpaperController { if (!xdg_cache_home) xdg_cache_home = `${GLib.getenv('HOME')}/.cache`; - this.wallpaperLocation = `${xdg_cache_home}/${Self.metadata['uuid']}/wallpapers/`; let mode = 0o0755; GLib.mkdir_with_parents(this.wallpaperLocation, mode); @@ -73,7 +72,7 @@ class WallpaperController { this._backendConnection.observe('clear-history', () => this._clearHistory()); this._backendConnection.observe('open-folder', () => this._openFolder()); this._backendConnection.observe('pause-timer', () => this._pauseTimer()); - this._backendConnection.observe('request-new-wallpaper', () => this._requestNewWallpaper().catch(logError)); + this._backendConnection.observe('request-new-wallpaper', () => this._requestNewWallpaper().catch(this._logger.error)); this._settings.observe('history-length', () => this._updateHistory()); this._settings.observe('auto-fetch', () => this._updateAutoFetching()); @@ -85,7 +84,7 @@ class WallpaperController { // load a new wallpaper on startup if (this._settings.getBoolean('fetch-on-startup')) - this.fetchNewWallpaper().catch(logError); + this.fetchNewWallpaper().catch(this._logger.error); // Initialize favorites folder // TODO: There's probably a better place for this @@ -588,10 +587,10 @@ class WallpaperController { } if (this._resetWallpaper) { - this._setBackground(currentWallpaperPaths, 1).catch(logError); + this._setBackground(currentWallpaperPaths, 1).catch(this._logger.error); this._resetWallpaper = false; } else if (this._previewId !== undefined) { - this._setBackground([this.wallpaperLocation + this._previewId], Utils.getMonitorCount()).catch(logError); + this._setBackground([this.wallpaperLocation + this._previewId], Utils.getMonitorCount()).catch(this._logger.error); } return false; From 995d3ac681c72496856df67a8a889a6d5819d944 Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 14 Dec 2022 15:14:09 +0100 Subject: [PATCH 07/87] Expect `historyEntry.adapter` as null Older history entries from gsettings might not yet have a adapter associated. This properly types this and fixes related null checks missing. --- src/history.ts | 4 ++-- src/historyMenuElements.ts | 4 ++-- src/wallpaperController.ts | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/history.ts b/src/history.ts index 6ea6253b..4325f875 100644 --- a/src/history.ts +++ b/src/history.ts @@ -25,10 +25,10 @@ class HistoryEntry { /** Unique identifier, concat of timestamp and name */ id: string; /** Basename of URI */ - name: string; + name: string | null; // This can be null when an entry from an older version is mapped from settings path: string | null = null; source: SourceInfo; - adapter: AdapterInfo = { + adapter: AdapterInfo | null = { // This can be null when an entry from an older version is mapped from settings id: null, type: null, }; diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index f6145232..2e3f4b00 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -137,7 +137,7 @@ const HistoryElement = GObject.registerClass({ this.menu.addMenuItem(copyToFavorites); // Static URLs can't block images (yet?) - if (this.historyEntry.adapter.type !== 5) { + if (this.historyEntry.adapter?.type !== 5) { const blockImage = new PopupMenu.PopupMenuItem('Add To Blocklist'); blockImage.connect('activate', () => { this._addToBlocklist(this.historyEntry); @@ -185,7 +185,7 @@ const HistoryElement = GObject.registerClass({ } private _addToBlocklist(entry: HistoryModule.HistoryEntry) { - if (!entry.adapter.id || entry.adapter.id === '-1' || !entry.name) { + if (!entry.adapter?.id || entry.adapter.id === '-1' || !entry.name) { this._logger.error('Image entry is missing information'); return; } diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index b09736ea..13d85f6e 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -483,8 +483,10 @@ class WallpaperController { const fetchPromiseArray: Promise[] = []; for (const element of array) { - element.adapter.id = imageAdapters[index].id; - element.adapter.type = imageAdapters[index].type; + element.adapter = { + id: imageAdapters[index].id, + type: imageAdapters[index].type, + }; this._logger.info(`Requesting image: ${element.source.imageDownloadUrl}`); fetchPromiseArray.push(imageAdapters[index].adapter.fetchFile(element)); From 1e2553077b2a6b38f1b96701f2dbafa7ee76989e Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 14 Dec 2022 17:28:38 +0100 Subject: [PATCH 08/87] Return the full JsonPath if the object was found Introduces new function to resolve a new random path according to an already resolved path with similar elements. Reference: https://github.com/ifl0w/RandomWallpaperGnome3/pull/143#discussion_r1047859443 --- src/adapter/genericJson.ts | 21 +++------- src/jsonPath.ts | 80 +++++++++++++++++++++----------------- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index fbaaff28..6a3a7920 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -44,11 +44,11 @@ class GenericJsonAdapter extends BaseAdapter { const authorUrlJSONPath = this._settings.getString('author-url-path'); for (let i = 0; i < 5 && wallpaperResult.length < count; i++) { - const returnObject = JSONPath.getTarget(response_body, imageJSONPath); - if (!returnObject || (typeof returnObject.Object !== 'string' && typeof returnObject.Object !== 'number') || returnObject.Object === '') + const [returnObject, resolvedPath] = JSONPath.getTarget(response_body, imageJSONPath); + if (!returnObject || (typeof returnObject !== 'string' && typeof returnObject !== 'number') || returnObject === '') throw new Error('Unexpected json member found'); - const imageDownloadUrl = this._settings.getString('image-prefix') + String(returnObject.Object); + const imageDownloadUrl = this._settings.getString('image-prefix') + String(returnObject); const imageBlocked = this._isImageBlocked(Utils.fileName(imageDownloadUrl)); // Don't retry without @random present in JSONPath @@ -60,32 +60,23 @@ class GenericJsonAdapter extends BaseAdapter { if (imageBlocked) continue; - // '@random' would yield different results so lets make sure the values stay - // the same as long as the path is identical - const samePath = imageJSONPath.substring(0, Utils.findFirstDifference(imageJSONPath, postJSONPath)); - - // count occurrences of '@random' to slice the array later - // https://stackoverflow.com/a/4009768 - const occurrences = (samePath.match(/@random/g) || []).length; - const slicedRandomNumbers = returnObject?.RandomNumbers?.slice(0, occurrences); - // A bit cumbersome to handle "unknown" in the following parts: // https://github.com/microsoft/TypeScript/issues/27706 let postUrl: string; - const postUrlObject = JSONPath.getTarget(response_body, postJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + const postUrlObject = JSONPath.getTarget(response_body, JSONPath.replaceRandomInPath(postJSONPath, resolvedPath))[0]; if (typeof postUrlObject === 'string' || typeof postUrlObject === 'number') postUrl = this._settings.getString('post-prefix') + String(postUrlObject); else postUrl = ''; let authorName: string | null = null; - const authorNameObject = JSONPath.getTarget(response_body, authorNameJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + const authorNameObject = JSONPath.getTarget(response_body, JSONPath.replaceRandomInPath(authorNameJSONPath, resolvedPath))[0]; if (typeof authorNameObject === 'string' && authorNameObject !== '') authorName = authorNameObject; let authorUrl: string; - const authorUrlObject = JSONPath.getTarget(response_body, authorUrlJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object; + const authorUrlObject = JSONPath.getTarget(response_body, JSONPath.replaceRandomInPath(authorUrlJSONPath, resolvedPath))[0]; if (typeof authorUrlObject === 'string' || typeof authorUrlObject === 'number') authorUrl = this._settings.getString('author-url-prefix') + String(authorUrlObject); else diff --git a/src/jsonPath.ts b/src/jsonPath.ts index 3c2b9a85..648a122d 100644 --- a/src/jsonPath.ts +++ b/src/jsonPath.ts @@ -8,24 +8,13 @@ import * as Utils from './utils.js'; * * @param {unknown} inputObject A JSON object * @param {string} inputString JSONPath to follow, see wiki for syntax - * @param {number[]} randomNumbers Array of pre-generated numbers - * @param {boolean} newRandomness Whether to ignore a given randomNumbers array */ -function getTarget(inputObject: unknown, inputString: string, randomNumbers?: number[], newRandomness?: boolean): { Object: unknown, RandomNumbers?: number[] } | null { +function getTarget(inputObject: unknown, inputString: string): [object: unknown, chosenPath: string] { if (!inputObject) - return null; + return [null, '']; - if (inputString.length === 0) { - return { - Object: inputObject, - RandomNumbers: randomNumbers, - }; - } - - if (!randomNumbers) { - randomNumbers = []; - newRandomness = true; - } + if (inputString.length === 0) + return [inputObject, inputString]; let startDot = inputString.indexOf('.'); if (startDot === -1) @@ -40,9 +29,10 @@ function getTarget(inputObject: unknown, inputString: string, randomNumbers?: nu // Expect Object here const targetObject = _getObjectMember(inputObject, keyString); if (!targetObject) - return null; + return [null, '']; - return getTarget(targetObject, inputStringTail, randomNumbers, newRandomness); + const [object, path] = getTarget(targetObject, inputStringTail); + return [object, inputString.slice(0, inputString.length - inputStringTail.length) + path]; } else { const indexString = keyString.slice(startParentheses + 1, keyString.length - 1); keyString = keyString.slice(0, startParentheses); @@ -50,30 +40,20 @@ function getTarget(inputObject: unknown, inputString: string, randomNumbers?: nu // Expect an Array at this point const targetObject = _getObjectMember(inputObject, keyString); if (!targetObject || !Array.isArray(targetObject)) - return null; + return [null, '']; switch (indexString) { case '@random': { - let randomNumber: number = -1; - let randomElement: unknown = null; - - if (!newRandomness && randomNumbers.length > 0) { - // Take and remove first element - randomNumber = randomNumbers.shift() ?? -1; - randomElement = targetObject[randomNumber]; - } else { - [randomElement, randomNumber] = _randomElement(targetObject); - - if (newRandomness) - randomNumbers.push(randomNumber); - } - - return getTarget(randomElement, inputStringTail, randomNumbers, newRandomness); + const [chosenElement, chosenNumber] = _randomElement(targetObject); + const [object, path] = getTarget(chosenElement, inputStringTail); + return [object, inputString.slice(0, inputString.length - inputStringTail.length).replace('@random', String(chosenNumber)) + path]; } // add special keywords here - default: + default: { // expecting integer - return getTarget(targetObject[parseInt(indexString)], inputStringTail, randomNumbers, newRandomness); + const [object, path] = getTarget(targetObject[parseInt(indexString)], inputStringTail); + return [object, inputString.slice(0, inputString.length - inputStringTail.length) + path]; + } } } } @@ -106,4 +86,32 @@ function _randomElement(array: Array): [T, number] { return [array[randomNumber], randomNumber]; } -export {getTarget}; +/** + * Replace '@random' according to an already resolved path. + * + * '@random' would yield different results so this makes sure the values stay + * the same as long as the path is identical. + * + * @param {string} randomPath Path containing '@random' to resolve + * @param {string} resolvedPath Path with resolved '@random' + */ +function replaceRandomInPath(randomPath: string, resolvedPath: string): string { + if (!randomPath.includes('@random')) + return randomPath; + + let newPath = randomPath; + while (newPath.includes('@random')) { + const startRandom = newPath.indexOf('@random'); + + // abort if path is not equal up to this point + if (newPath.substring(0, startRandom) !== resolvedPath.substring(0, startRandom)) + break; + + const endParenthesis = resolvedPath.indexOf(']', startRandom); + newPath = newPath.replace('@random', resolvedPath.substring(startRandom, endParenthesis)); + } + + return newPath; +} + +export {getTarget, replaceRandomInPath}; From e04af319113e27002b8de7273b36d4fae9311797 Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 15 Dec 2022 00:27:22 +0100 Subject: [PATCH 09/87] Install script fixes: * Prefix HOME variable to correctly get the users home path * Prevent whitespace issues by quoting the users home path variable Reference: https://github.com/ifl0w/RandomWallpaperGnome3/pull/143#discussion_r1049067865 --- install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index 8c0599e6..f02bd440 100755 --- a/install.sh +++ b/install.sh @@ -1,6 +1,6 @@ #!/bin/bash -datahome="${XDG_DATA_HOME:-HOME/.local/share}" +datahome="${XDG_DATA_HOME:-$HOME/.local/share}" extensionFolder="randomwallpaper@iflow.space" sourcepath="$PWD/$extensionFolder" @@ -11,7 +11,7 @@ if [ "$1" = "uninstall" ]; then rm "$targetpath/$extensionFolder" else echo "# Making extension directory" - mkdir -p $targetpath + mkdir -p "$targetpath" echo "# Linking extension folder" ln -s "$sourcepath" "$targetpath" fi From 33ebaa42fde7feaf131befe080f84866c49c6565 Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 15 Dec 2022 18:44:53 +0100 Subject: [PATCH 10/87] Support independent background and lock screen --- ...ns.space.iflow.randomwallpaper.gschema.xml | 2 +- src/utils.ts | 8 +- src/wallpaperController.ts | 159 +++++++++++------- 3 files changed, 105 insertions(+), 64 deletions(-) diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 224bf5e3..69c2f41a 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -5,7 +5,7 @@ - + diff --git a/src/utils.ts b/src/utils.ts index e826f5c0..bbbbb3b1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -113,7 +113,7 @@ function findFirstDifference(str1: string, str2: string) { /** * */ -function getMonitorCount() { +function getMonitorCount(): number { // Gdk 4.8+ // Gdk.DisplayManager.get() // displayManager.get_default_display() @@ -126,9 +126,13 @@ function getMonitorCount() { // Gdk < 4.8 const defaultDisplay = Gdk.Display.get_default(); + + if (!defaultDisplay) + return 1; + // FIXME: wrong version in definition // @ts-expect-error - return defaultDisplay.get_n_monitors(); + return defaultDisplay.get_n_monitors() as number; } /** diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 13d85f6e..363abb8e 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -311,33 +311,26 @@ class WallpaperController { /** * Sets the wallpaper and the lock screen when enabled to the given path. * - * @param {string[]} wallpaperArray Array of paths to the image - * @param {number} monitorCount Number of monitors to fill + * Types: + * 0: Background + * 1: Lock screen + * 2: Background and lock screen + * + * @param {string[]} wallpaperPaths Array of paths to the image + * @param {number} type Types to change */ - private async _setBackground(wallpaperArray: string[], monitorCount: number) { - const multiMonitor = monitorCount > 1 && this._hydraPaper.isAvailable(); + private async _setBackground(wallpaperPaths: string[], type: number = 0) { const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); const screensaverSettings = new SettingsModule.Settings('org.gnome.desktop.screensaver'); - if (wallpaperArray.length < 1) + if (wallpaperPaths.length < 1) throw new Error('Empty wallpaper array'); - const wallpaperUri = `file://${wallpaperArray[0]}`; - let usedWallpaperPaths: string[] = []; - - // - // - // - // TODO: - const changeType = this._settings.getEnum('change-type'); - - if (changeType === 0 || changeType === 2) { - if (multiMonitor) { - const newWallpaperPaths = this._fillMonitorsFromHistory(wallpaperArray, monitorCount); - - await this._hydraPaper.run(newWallpaperPaths); + const wallpaperUri = `file://${wallpaperPaths[0]}`; - usedWallpaperPaths = newWallpaperPaths; + if (type === 0 || type === 2) { + if (wallpaperPaths.length > 1) { + await this._hydraPaper.run(wallpaperPaths); // Manually set key for darkmode because that's way faster backgroundSettings.setString('picture-uri-dark', backgroundSettings.getString('picture-uri')); @@ -346,25 +339,17 @@ class WallpaperController { // hydrapaper changes this to "spanned" backgroundSettings.setString('picture-options', 'zoom'); this._setPictureUriOfSettingsObject(backgroundSettings, wallpaperUri); - usedWallpaperPaths.push(wallpaperUri); } } - if (changeType === 1) { - if (multiMonitor) { - const newWallpaperPaths = this._fillMonitorsFromHistory(wallpaperArray, monitorCount); - + if (type === 1) { + if (wallpaperPaths.length > 1) { // Remember keys, HydraPaper will change these const tmpBackground = backgroundSettings.getString('picture-uri-dark'); const tmpMode = backgroundSettings.getString('picture-options'); // Force HydraPaper to target a different resulting image by using darkmode - await this._hydraPaper.run(newWallpaperPaths, true); - - newWallpaperPaths.forEach(path => { - if (!usedWallpaperPaths.includes(path)) - usedWallpaperPaths.push(path); - }); + await this._hydraPaper.run(wallpaperPaths, true); screensaverSettings.setString('picture-options', 'spanned'); this._setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); @@ -376,45 +361,47 @@ class WallpaperController { // set "picture-options" to "zoom" for single wallpapers screensaverSettings.setString('picture-options', 'zoom'); this._setPictureUriOfSettingsObject(screensaverSettings, wallpaperUri); - if (!usedWallpaperPaths.includes(wallpaperUri)) - usedWallpaperPaths.push(wallpaperUri); } } - if (changeType === 2) + if (type === 2) this._setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + } - // TODO: this ignores the lock-screen - // Run general post command + // Run general post command + private _runPostCommands() { + const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); const commandString = this._settings.getString('general-post-command'); - const generalPostCommandArray = this._getCommandArray(commandString, backgroundSettings.getString('picture-uri')); + + // Read the current wallpaper uri from settings because it could be a merged wallpaper from HydraPaper + const currentWallpaperPath = backgroundSettings.getString('picture-uri'); + + // TODO: this ignores the lock-screen + const generalPostCommandArray = this._getCommandArray(commandString, currentWallpaperPath); if (generalPostCommandArray !== null) { - try { - await Utils.execCheck(generalPostCommandArray); - } catch (error) { - this._logger.warn(String(error)); - } + // Do not await this call, let it be one shot + Utils.execCheck(generalPostCommandArray).catch(this._logger.error); } - - return usedWallpaperPaths; } - private _fillMonitorsFromHistory(wallpaperArray: string[], monitorCount: number) { + private _fillDisplaysFromHistory(wallpaperArray: string[], requestCount?: number) { + const count = requestCount ?? this._getCurrentDisplayCount(); const newWallpaperArray: string[] = [...wallpaperArray]; // Abuse history to fill missing images - for (let index = newWallpaperArray.length; index < monitorCount; index++) { - let historyElement; + for (let index = newWallpaperArray.length; index < count; index++) { + let historyElement: HistoryModule.HistoryEntry; do historyElement = this._historyController.getRandom(); - while (this._historyController.history.length > monitorCount && historyElement.path && newWallpaperArray.includes(historyElement.path)); - // ensure different wallpaper for all displays if possible + while (this._historyController.history.length > count && historyElement.path && newWallpaperArray.includes(historyElement.path)); + // try to ensure different wallpaper for all displays if possible if (historyElement.path) newWallpaperArray.push(historyElement.path); } - return newWallpaperArray; + // Trim array if we have too many images, possibly by having a too long input array + return newWallpaperArray.slice(0, count); } /** @@ -454,9 +441,18 @@ class WallpaperController { const historyElement = this._historyController.get(historyId); if (historyElement?.id && historyElement.path && this._historyController.promoteToActive(historyElement.id)) { - const monitorCount = this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable() ? Utils.getMonitorCount() : 1; - const usedWallpapers = (await this._setBackground([historyElement.path], monitorCount)).reverse(); - usedWallpapers.forEach(path => { + const changeType = this._settings.getEnum('change-type'); + const usedWallpaperPaths = this._fillDisplaysFromHistory([historyElement.path]); + + // ignore changeType === 3 because that doesn't make sense + // when requesting a specific history entry + if (changeType > 2) + await this._setBackground(usedWallpaperPaths, 2); + else + await this._setBackground(usedWallpaperPaths, changeType); + + this._runPostCommands(); + usedWallpaperPaths.reverse().forEach(path => { const id = this._historyController.getEntryByPath(path)?.id; if (id) this._historyController.promoteToActive(id); @@ -471,7 +467,17 @@ class WallpaperController { this._startLoadingHooks.forEach(element => element()); try { - const monitorCount = this._settings.getBoolean('multiple-displays') && this._hydraPaper.isAvailable() ? Utils.getMonitorCount() : 1; + // + // + // + // + const changeType = this._settings.getEnum('change-type'); + let monitorCount = this._getCurrentDisplayCount(); + + // Request double the amount of displays if we need background and lock screen + if (changeType === 3) + monitorCount *= 2; + const imageAdapters = this._getRandomAdapter(monitorCount); const randomImagePromises = imageAdapters.map(element => { @@ -520,16 +526,25 @@ class WallpaperController { // wait for all images to be moved await Promise.all(movePromises); - const wallpaperPaths = newImageEntries.map(element => { + const newWallpaperPaths = newImageEntries.map(element => { if (element.path) return element.path; // eslint-disable-next-line return; }) as string[]; // cast because we made sure it's defined - const usedWallpapers = (await this._setBackground(wallpaperPaths, monitorCount)).reverse(); + const usedWallpaperPaths = this._fillDisplaysFromHistory(newWallpaperPaths, monitorCount); - usedWallpapers.forEach(path => { + if (changeType === 3) { + // Half the images for the background + await this._setBackground(usedWallpaperPaths.slice(0, monitorCount / 2), 0); + // Half the images for the lock screen + await this._setBackground(usedWallpaperPaths.slice(monitorCount / 2), 1); + } else { + await this._setBackground(usedWallpaperPaths, changeType); + } + + usedWallpaperPaths.reverse().forEach(path => { const id = this._historyController.getEntryByPath(path)?.id; if (id) this._historyController.promoteToActive(id); @@ -539,6 +554,8 @@ class WallpaperController { newImageEntries.reverse().forEach(element => { this._historyController.insert(element); }); + + this._runPostCommands(); } finally { this._stopLoadingHooks.forEach(element => element()); } @@ -572,6 +589,22 @@ class WallpaperController { return null; } + /** + * Get the current number of displays. + * + * This also takes the user setting and HydraPaper availability into account + * and lies accordingly by reporting only 1 display. + */ + private _getCurrentDisplayCount() { + if (!this._settings.getBoolean('multiple-displays')) + return 1; + + if (!this._hydraPaper.isAvailable()) + return 1; + + return Utils.getMonitorCount(); + } + private _backgroundTimeout(delay?: number) { if (this._timeout) return; @@ -582,20 +615,24 @@ class WallpaperController { this._timeout = null; const currentWallpaperPaths: string[] = []; - for (let index = 0; index < Utils.getMonitorCount() && index < this._historyController.history.length; index++) { + for (let index = 0; index < this._getCurrentDisplayCount() && index < this._historyController.history.length; index++) { const path = this._historyController.history[index].path; if (path) currentWallpaperPaths.push(path); } + const oldWallpaperPaths = this._fillDisplaysFromHistory(currentWallpaperPaths); + // Only change the background - the lock screen wouldn't be visible anyway + // because this function is only used for hover preview if (this._resetWallpaper) { - this._setBackground(currentWallpaperPaths, 1).catch(this._logger.error); + this._setBackground(oldWallpaperPaths, 0).catch(this._logger.error); this._resetWallpaper = false; } else if (this._previewId !== undefined) { - this._setBackground([this.wallpaperLocation + this._previewId], Utils.getMonitorCount()).catch(this._logger.error); + const newWallpaperPaths = this._fillDisplaysFromHistory([this.wallpaperLocation + this._previewId]); + this._setBackground(newWallpaperPaths, 0).catch(this._logger.error); } - return false; + return GLib.SOURCE_REMOVE; }); } From d1d1325ed2e7a6d7d1ea615e88578086da06b72b Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 16 Dec 2022 01:25:41 +0100 Subject: [PATCH 11/87] Fix hover preview for independent usage --- src/randomWallpaperMenu.ts | 53 +++++++++++++++++++++++---------- src/wallpaperController.ts | 60 ++++++++++++++++++++++++-------------- 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index b5bf6c8b..530ac704 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -18,6 +18,7 @@ const Self = ExtensionUtils.getCurrentExtension(); class RandomWallpaperMenu { private _logger = new Logger('RWG3', 'RandomWallpaperEntry'); private _backendConnection = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); + private _savedBackgroundUri: string | null = null; private _settings = new Settings.Settings(); private _currentBackgroundSection; @@ -94,6 +95,7 @@ class RandomWallpaperMenu { // new wallpaper event newWallpaperItem.connect('activate', () => { + // Make sure no other preview or reset event overwrites our setWallpaper! this._wallpaperController.prohibitNewWallpaper = true; this._wallpaperController.fetchNewWallpaper().then(() => { this._wallpaperController.prohibitNewWallpaper = false; @@ -128,21 +130,31 @@ class RandomWallpaperMenu { null); }); - this._panelMenu.menu.actor.connect('show', () => { - newWallpaperItem.show(); + this._panelMenu.menu.connect('open-state-changed', (_, open) => { + if (open) { + // Save currently used background so we can reset to this + // in case only the lock screen was changed while the preview + // used the normal background + const backgroundSettings = new Settings.Settings('org.gnome.desktop.background'); + this._savedBackgroundUri = backgroundSettings.getString('picture-uri'); + + // Update remaining time label + newWallpaperItem.show(); + } else { + // Reset to the saved background image on popup closing + if (!this._wallpaperController.prohibitNewWallpaper && this._savedBackgroundUri) + this._wallpaperController.resetWallpaper(this._savedBackgroundUri); + + this._savedBackgroundUri = null; + } }); - // when the popupMenu disappears, check if the wallpaper is the original and - // reset it if needed - this._panelMenu.menu.actor.connect('hide', () => { - if (!this._wallpaperController.prohibitNewWallpaper) - this._wallpaperController.resetWallpaper(); - }); - - this._panelMenu.menu.actor.connect('leave-event', () => { - if (!this._wallpaperController.prohibitNewWallpaper) - this._wallpaperController.resetWallpaper(); - }); + // FIXME?: This triggers by leaving the underlying popupMenu and blocks previewing the items + // when entering from any side other than another item. (eg. spacer or the sides) + // this._panelMenu.menu.actor.connect('leave-event', () => { + // if (!this._wallpaperController.prohibitNewWallpaper) + // this._wallpaperController.resetWallpaper(this._savedBackgroundUri); + // }); this._settings.observe('history', this.setHistoryList.bind(this)); } @@ -200,8 +212,8 @@ class RandomWallpaperMenu { * @param {CustomElements.HistoryElement} unusedActor The activating panel item */ function onLeave(this: RandomWallpaperMenu, unusedActor: typeof CustomElements.HistoryElement) { - if (!this._wallpaperController.prohibitNewWallpaper) - this._wallpaperController.resetWallpaper(); + if (!this._wallpaperController.prohibitNewWallpaper && this._savedBackgroundUri) + this._wallpaperController.resetWallpaper(this._savedBackgroundUri); } /** @@ -220,10 +232,21 @@ class RandomWallpaperMenu { * @param {CustomElements.HistoryElement} actor The activating panel item */ function onSelect(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { + // Make sure no other preview or reset event overwrites our setWallpaper! this._wallpaperController.prohibitNewWallpaper = true; // @ts-expect-error Typing fails for GObject.registerClass this._wallpaperController.setWallpaper(actor.historyEntry.id).then(() => { this._wallpaperController.prohibitNewWallpaper = false; + + if (this._settings.getEnum('change-type') === 1 && this._savedBackgroundUri) { + // Reset background after previewing the lock screen options + this._wallpaperController.resetWallpaper(this._savedBackgroundUri); + } else { + // Update saved background with newly set background image + // so we don't revert to an older state when closing the menu + const backgroundSettings = new Settings.Settings('org.gnome.desktop.background'); + this._savedBackgroundUri = backgroundSettings.getString('picture-uri'); + } }).catch(error => { this._wallpaperController.prohibitNewWallpaper = false; this._logger.error(error); diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 363abb8e..c8345e5e 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -335,9 +335,13 @@ class WallpaperController { // Manually set key for darkmode because that's way faster backgroundSettings.setString('picture-uri-dark', backgroundSettings.getString('picture-uri')); } else { - // set "picture-options" to "zoom" for single wallpapers - // hydrapaper changes this to "spanned" - backgroundSettings.setString('picture-options', 'zoom'); + if (wallpaperUri.includes('merged_wallpaper')) + // merged wallpapers need mode "spanned" + backgroundSettings.setString('picture-options', 'spanned'); + else + // single wallpapers need mode "zoom" + backgroundSettings.setString('picture-options', 'zoom'); + this._setPictureUriOfSettingsObject(backgroundSettings, wallpaperUri); } } @@ -358,14 +362,27 @@ class WallpaperController { backgroundSettings.setString('picture-uri-dark', tmpBackground); backgroundSettings.setString('picture-options', tmpMode); } else { - // set "picture-options" to "zoom" for single wallpapers - screensaverSettings.setString('picture-options', 'zoom'); + if (wallpaperUri.includes('merged_wallpaper')) + // merged wallpapers need mode "spanned" + screensaverSettings.setString('picture-options', 'spanned'); + else + // single wallpapers need mode "zoom" + screensaverSettings.setString('picture-options', 'zoom'); + this._setPictureUriOfSettingsObject(screensaverSettings, wallpaperUri); } } - if (type === 2) + if (type === 2) { + if (wallpaperUri.includes('merged_wallpaper')) + // merged wallpapers need mode "spanned" + screensaverSettings.setString('picture-options', 'spanned'); + else + // single wallpapers need mode "zoom" + screensaverSettings.setString('picture-options', 'zoom'); + this._setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + } } // Run general post command @@ -605,8 +622,8 @@ class WallpaperController { return Utils.getMonitorCount(); } - private _backgroundTimeout(delay?: number) { - if (this._timeout) + private _backgroundTimeout(paths?: string[], delay?: number) { + if (this._timeout || !paths) return; delay = delay || 200; @@ -614,22 +631,13 @@ class WallpaperController { this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => { this._timeout = null; - const currentWallpaperPaths: string[] = []; - for (let index = 0; index < this._getCurrentDisplayCount() && index < this._historyController.history.length; index++) { - const path = this._historyController.history[index].path; - if (path) - currentWallpaperPaths.push(path); - } - const oldWallpaperPaths = this._fillDisplaysFromHistory(currentWallpaperPaths); - // Only change the background - the lock screen wouldn't be visible anyway // because this function is only used for hover preview if (this._resetWallpaper) { - this._setBackground(oldWallpaperPaths, 0).catch(this._logger.error); + this._setBackground(paths, 0).catch(this._logger.error); this._resetWallpaper = false; } else if (this._previewId !== undefined) { - const newWallpaperPaths = this._fillDisplaysFromHistory([this.wallpaperLocation + this._previewId]); - this._setBackground(newWallpaperPaths, 0).catch(this._logger.error); + this._setBackground(paths, 0).catch(this._logger.error); } return GLib.SOURCE_REMOVE; @@ -641,14 +649,22 @@ class WallpaperController { this._previewId = historyId; this._resetWallpaper = false; - this._backgroundTimeout(delay); + // Do not fill other displays here. + // This is so HydraPaper will not overwrite the current merged background path + // with the preview image. + // We could move the image to a safe place with caveats: + // * temporarily (seems expensive for a simple preview) + // TODO: verify: * permanently (would break HydraPaperDaemon) + const newWallpaperPaths = [this.wallpaperLocation + this._previewId]; + + this._backgroundTimeout(newWallpaperPaths, delay); } } - resetWallpaper() { + resetWallpaper(uri: string) { if (!this._settings.getBoolean('disable-hover-preview')) { this._resetWallpaper = true; - this._backgroundTimeout(); + this._backgroundTimeout([GLib.filename_from_uri(uri)[0]]); } } From cc5fd8d3b24dd8bd5f2466c6abce486f9e6ed4d5 Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 15 Dec 2022 20:11:10 +0100 Subject: [PATCH 12/87] timer fix --- src/timer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/timer.ts b/src/timer.ts index 04eff9b9..d99fd95f 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -39,6 +39,9 @@ class AFTimer { * next correct time frame repeatedly. */ continue() { + if (!this.isActive()) + return; + this._paused = false; this.start(); } From 10cf3534a570bef817f0b59ac2c085900bb7df9f Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 16 Dec 2022 12:41:08 +0100 Subject: [PATCH 13/87] Reuse soup session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …and assume soup figures out throttling on its own to not overload servers. --- src/adapter/baseAdapter.ts | 10 +++++----- src/adapter/genericJson.ts | 3 --- src/adapter/reddit.ts | 3 --- src/adapter/unsplash.ts | 2 -- src/adapter/wallhaven.ts | 2 -- src/wallpaperController.ts | 1 - 6 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 85a451c0..21e95da4 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -7,6 +7,7 @@ import {Logger} from './../logger.js'; import {SoupBowl} from './../soupBowl.js'; abstract class BaseAdapter { + protected _bowl = new SoupBowl(); logger: Logger; protected _settings: SettingsModule.Settings; @@ -50,15 +51,14 @@ abstract class BaseAdapter { * @param {HistoryEntry} historyEntry The historyEntry to fetch */ async fetchFile(historyEntry: HistoryEntry) { - const bowl = new SoupBowl(); - const file = Gio.file_new_for_path(`${this._wallpaperLocation}/${String(historyEntry.name)}`); const fstream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); - // start the download - let request = bowl.newGetMessage(historyEntry.source.imageDownloadUrl); + // craft new message from details + let request = this._bowl.newGetMessage(historyEntry.source.imageDownloadUrl); - const response_data_bytes = await bowl.send_and_receive(request); + // start the download + const response_data_bytes = await this._bowl.send_and_receive(request); if (!response_data_bytes) { fstream.close(null); throw new Error('Not a valid image response'); diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 6a3a7920..aad618a5 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -6,11 +6,8 @@ import * as Utils from './../utils.js'; import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; -import {SoupBowl} from './../soupBowl.js'; class GenericJsonAdapter extends BaseAdapter { - private _bowl = new SoupBowl(); - constructor(id: string, name: string, wallpaperLocation: string) { super({ defaultName: 'Generic JSON Source', diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index 34e61817..bf89c20c 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -5,7 +5,6 @@ import * as Utils from './../utils.js'; import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; -import {SoupBowl} from './../soupBowl.js'; interface RedditResponse { data: { @@ -32,8 +31,6 @@ interface RedditSubmission { } class RedditAdapter extends BaseAdapter { - private _bowl = new SoupBowl(); - constructor(id: string, name: string, wallpaperLocation: string) { super({ defaultName: 'Reddit', diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 19cbb7ac..1001769a 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -3,10 +3,8 @@ import * as Utils from './../utils.js'; import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; -import {SoupBowl} from './../soupBowl.js'; class UnsplashAdapter extends BaseAdapter { - private _bowl = new SoupBowl(); private _sourceUrl = 'https://source.unsplash.com'; // default query options diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index 76889014..e3ee473a 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -5,7 +5,6 @@ import * as Utils from './../utils.js'; import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; -import {SoupBowl} from './../soupBowl.js'; interface QueryOptions { q: string, @@ -29,7 +28,6 @@ interface WallhavenSearchResponse { } class WallhavenAdapter extends BaseAdapter { - private _bowl = new SoupBowl(); private _options: QueryOptions = { q: '', apikey: '', diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index c8345e5e..f5dfaca2 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -519,7 +519,6 @@ class WallpaperController { }); // wait for all fetching images - // FIXME: shove this into the adapter itself so rate limiting can be adjusted const newImageEntries = await Promise.all(fetchPromises); // Move file to unique naming From 0f23d97e0b993e3979ba0c10c99ac495449cad08 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 16 Dec 2022 15:45:40 +0100 Subject: [PATCH 14/87] Properly type UI settingsGroups --- src/ui/genericJson.ts | 2 +- src/ui/localFolder.ts | 2 +- src/ui/reddit.ts | 2 +- src/ui/sourceRow.ts | 2 +- src/ui/unsplash.ts | 2 +- src/ui/urlSource.ts | 2 +- src/ui/wallhaven.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index f221248b..e5ee5f6c 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -37,7 +37,7 @@ const GenericJsonSettingsGroup = GObject.registerClass({ private _settings; - constructor(params: any | undefined, id: string) { + constructor(params: Partial | undefined, id: string) { super(params); const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/genericJSON/${id}/`; diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index 69658d43..fcf25721 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -25,7 +25,7 @@ const LocalFolderSettingsGroup = GObject.registerClass({ private _saveDialog: Gtk.FileChooserNative | undefined; private _settings; - constructor(params: any | undefined, id: string) { + constructor(params: Partial | undefined, id: string) { super(params); const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/localFolder/${id}/`; diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index e3de3850..cfdd28fe 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -32,7 +32,7 @@ const RedditSettingsGroup = GObject.registerClass({ private _settings; - constructor(params: any | undefined, id: string) { + constructor(params: Partial | undefined, id: string) { super(params); const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/reddit/${id}/`; diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 28577147..6460c691 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -51,7 +51,7 @@ const SourceRow = GObject.registerClass({ id = String(Date.now()); - constructor(params: object | undefined, id?: string | null) { + constructor(params: Partial | undefined, id?: string | null) { super(params); if (id) diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index c5bdc140..53a4010f 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -35,7 +35,7 @@ const UnsplashSettingsGroup = GObject.registerClass({ private _settings; - constructor(params: any | undefined, id: string) { + constructor(params: Partial | undefined, id: string) { super(params); const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id}/`; diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index 83e3a4cd..fb8ea5e3 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -29,7 +29,7 @@ const UrlSourceSettingsGroup = GObject.registerClass({ private _settings; - constructor(params: any | undefined, id: string) { + constructor(params: Partial | undefined, id: string) { super(params); const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/urlSource/${id}/`; diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index acc0b58e..de878209 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -56,7 +56,7 @@ const WallhavenSettingsGroup = GObject.registerClass({ private _colorDialog: Gtk.ColorChooserDialog | undefined; private _settings; - constructor(params: object | undefined, id: string) { + constructor(params: Partial | undefined, id: string) { super(params); const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/wallhaven/${id}/`; From edf6ca127c83e9f82e5f374139d5f2d0fb483ef6 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 16 Dec 2022 16:17:58 +0100 Subject: [PATCH 15/87] Properly type `HistoryElement` Solution found at https://github.com/yilozt/rounded-window-corners --- src/historyMenuElements.ts | 23 +++++++---------------- src/randomWallpaperMenu.ts | 18 ++++++++---------- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 2e3f4b00..0a65eefe 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -341,7 +341,7 @@ class HistorySection extends PopupMenu.PopupMenuSection { /** * Cache HistoryElements for performance of long histories. */ - private _historySectionCache = new Map(); + private _historySectionCache = new Map>(); private _historyCache: HistoryModule.HistoryEntry[] = []; constructor() { @@ -355,8 +355,12 @@ class HistorySection extends PopupMenu.PopupMenuSection { this.actor.add_actor(this.box); } - // eslint-disable-next-line no-unused-vars - updateList(history: HistoryModule.HistoryEntry[], onEnter: (actor: typeof HistoryElement) => void, onLeave: (actor: typeof HistoryElement) => void, onSelect: (actor: typeof HistoryElement) => void) { + updateList( + history: HistoryModule.HistoryEntry[], + onEnter: (actor: InstanceType) => void, + onLeave: (actor: InstanceType) => void, + onSelect: (actor: InstanceType) => void + ) { if (this._historyCache.length <= 1) this.removeAll(); // remove empty history element @@ -368,29 +372,18 @@ class HistorySection extends PopupMenu.PopupMenuSection { if (!historyID) continue; - // Typing fails here for our own class derived from GObject.registerClass - // FIXME: Expect a whole lot of ignore comments here: - let cachedHistoryElement = this._historySectionCache.get(historyID); if (!cachedHistoryElement) { - // @ts-expect-error cachedHistoryElement = new HistoryElement(undefined, history[i], i); - // @ts-expect-error cachedHistoryElement.actor.connect('key-focus-in', onEnter); - // @ts-expect-error cachedHistoryElement.actor.connect('key-focus-out', onLeave); - // @ts-expect-error cachedHistoryElement.actor.connect('enter-event', onEnter); - // @ts-expect-error cachedHistoryElement.connect('activate', onSelect); - // @ts-expect-error this._historySectionCache.set(historyID, cachedHistoryElement); - // @ts-expect-error this.addMenuItem(cachedHistoryElement, i - 1); } else { - // @ts-expect-error cachedHistoryElement.setIndex(i); } @@ -405,8 +398,6 @@ class HistorySection extends PopupMenu.PopupMenuSection { const destroyIDs = Array.from(this._historySectionCache.keys()).filter(i => existingIDs.indexOf(i) === -1); destroyIDs.forEach(id => { - // Same as the block above, typing from GObject.registerClass fails - // @ts-expect-error this._historySectionCache.get(id)?.destroy(); this._historySectionCache.delete(id); }); diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 530ac704..47efa54f 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -209,32 +209,30 @@ class RandomWallpaperMenu { /** * @this {RandomWallpaperMenu} RandomWallpaperMenu - * @param {CustomElements.HistoryElement} unusedActor The activating panel item + * @param {InstanceType} unusedActor The activating panel item */ - function onLeave(this: RandomWallpaperMenu, unusedActor: typeof CustomElements.HistoryElement) { + function onLeave(this: RandomWallpaperMenu, unusedActor: InstanceType) { if (!this._wallpaperController.prohibitNewWallpaper && this._savedBackgroundUri) this._wallpaperController.resetWallpaper(this._savedBackgroundUri); } /** * @this {RandomWallpaperMenu} RandomWallpaperMenu - * @param {CustomElements.HistoryElement} actor The activating panel item + * @param {InstanceType} actor The activating panel item */ - function onEnter(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { - if (!this._wallpaperController.prohibitNewWallpaper) { - // @ts-expect-error Typing fails for GObject.registerClass + function onEnter(this: RandomWallpaperMenu, actor: InstanceType) { + if (!this._wallpaperController.prohibitNewWallpaper) this._wallpaperController.previewWallpaper(actor.historyEntry.id); - } } /** * @this {RandomWallpaperMenu} RandomWallpaperMenu - * @param {CustomElements.HistoryElement} actor The activating panel item + * @param {InstanceType} actor The activating panel item */ - function onSelect(this: RandomWallpaperMenu, actor: typeof CustomElements.HistoryElement) { + function onSelect(this: RandomWallpaperMenu, actor: InstanceType) { // Make sure no other preview or reset event overwrites our setWallpaper! this._wallpaperController.prohibitNewWallpaper = true; - // @ts-expect-error Typing fails for GObject.registerClass + this._wallpaperController.setWallpaper(actor.historyEntry.id).then(() => { this._wallpaperController.prohibitNewWallpaper = false; From 3c91fc91d6c2b116ac5416dafa222ed5daca0eeb Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 25 Dec 2022 15:53:06 +0100 Subject: [PATCH 16/87] Fix false eslint rule matches --- .eslintrc.yml | 3 ++- src/adapter/wallhaven.ts | 1 - src/prefs.ts | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 58368f1c..cb1bdc53 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -106,7 +106,8 @@ rules: - error - allowEmptyCatch: true no-extra-bind: error - no-extra-parens: + no-extra-parens: off + '@typescript-eslint/no-extra-parens': - error - all - conditionalAssign: false diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index e3ee473a..550e1170 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -102,7 +102,6 @@ class WallhavenAdapter extends BaseAdapter { for (const key in options) { if (options.hasOwnProperty(key)) { if (Array.isArray(options[key])) - // eslint-disable-next-line no-extra-parens optionsString += `${key}=${(options[key] as Array).join()}&`; else if (options[key] !== '') optionsString += `${key}=${options[key]}&`; diff --git a/src/prefs.ts b/src/prefs.ts index a2466040..f22e36f5 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -129,12 +129,10 @@ class RandomWallpaperSettings { this._sources.forEach(id => { const sourceRow = new SourceRow(undefined, id); - // eslint-disable-next-line no-extra-parens (this._builder.get_object('sources_list') as Adw.PreferencesGroup).add(sourceRow); sourceRow.button_delete.connect('clicked', () => { sourceRow.clearConfig(); - // eslint-disable-next-line no-extra-parens (this._builder.get_object('sources_list') as Adw.PreferencesGroup).remove(sourceRow); Utils.removeItemOnce(this._sources, id); this._saveSources(); @@ -143,7 +141,6 @@ class RandomWallpaperSettings { import('./hydraPaper.js').then(module => { if (new module.HydraPaper().isAvailable()) - // eslint-disable-next-line no-extra-parens (this._builder.get_object('multiple_displays_row') as Adw.ActionRow).set_sensitive(true); }).catch(this._logger.error); }).catch(error => { From de318fa522219d42fd9db3f5fe2a762272c21dc4 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 25 Dec 2022 16:07:41 +0100 Subject: [PATCH 17/87] =?UTF-8?q?[Wallhaven]=20Find=20more=20possible=20wa?= =?UTF-8?q?llpaper=20by=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …replacing the fixed resolution with ratios and minimal resolution --- src/adapter/wallhaven.ts | 14 ++++++++------ ...ensions.space.iflow.randomwallpaper.gschema.xml | 14 ++++++++++---- src/ui/wallhaven.blp | 10 ++++++++-- src/ui/wallhaven.ts | 14 ++++++++++---- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index 550e1170..6e06850a 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -12,10 +12,10 @@ interface QueryOptions { purity: string, sorting: string, categories: string, - resolutions: string[], + // resolutions: string[], colors: string, - // atleast: string, - // ratios: string[], + atleast: string, + ratios: string[], // order: string, // topRange: string, } @@ -34,7 +34,8 @@ class WallhavenAdapter extends BaseAdapter { purity: '110', // SFW, sketchy sorting: 'random', categories: '111', // General, Anime, People - resolutions: ['1920x1200', '2560x1440'], + atleast: '1920x1080', + ratios: ['16x9'], colors: '', }; @@ -119,8 +120,9 @@ class WallhavenAdapter extends BaseAdapter { } this._options.apikey = this._settings.getString('api-key'); - this._options.resolutions = this._settings.getString('resolutions').split(','); - this._options.resolutions = this._options.resolutions.map(elem => { + this._options.atleast = this._settings.getString('minimal-resolution'); + this._options.ratios = this._settings.getString('aspect-ratios').split(','); + this._options.ratios = this._options.ratios.map(elem => { return elem.trim(); }); diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 69c2f41a..68cadd3d 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -295,10 +295,16 @@ The keyword will be used to search images. - - "1920x1200, 1920x1080, 2560x1440, 2560x1600, 3840x1080" - Resolutions - The acceptable resolutions. + + "1920x1080" + Minimal Resolution + The least acceptable resolution. + + + + "16x9,16x10" + Aspect ratios + List of acceptable aspect ratios. diff --git a/src/ui/wallhaven.blp b/src/ui/wallhaven.blp index d8c2c620..6a554492 100644 --- a/src/ui/wallhaven.blp +++ b/src/ui/wallhaven.blp @@ -30,8 +30,14 @@ template WallhavenSettingsGroup : Adw.PreferencesGroup { } } - Adw.EntryRow resolutions { - title: _("Resolutions: 1920x1080, 2560x1440"); + Adw.EntryRow minimal_resolution { + title: _("Minimal resolution: 1920x1080"); + input-purpose: free_form; + text: ""; + } + + Adw.EntryRow aspect_ratios { + title: _("Allowed aspect ratios: 16x9,16x10"); input-purpose: free_form; text: ""; } diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index de878209..130d5930 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -19,13 +19,14 @@ const WallhavenSettingsGroup = GObject.registerClass({ 'allow_sfw', 'allow_sketchy', 'api_key', + 'aspect_ratios', 'button_color_undo', 'button_color', 'category_anime', 'category_general', 'category_people', 'keyword', - 'resolutions', + 'minimal_resolution', 'row_color', ], }, class WallhavenSettingsGroup extends Adw.PreferencesGroup { @@ -44,13 +45,14 @@ const WallhavenSettingsGroup = GObject.registerClass({ private _allow_sfw!: Gtk.Switch; private _allow_sketchy!: Gtk.Switch; private _api_key!: Adw.EntryRow; + private _aspect_ratios!: Adw.EntryRow; private _button_color_undo!: Gtk.Button; private _button_color!: Gtk.Button; private _category_anime!: Gtk.Switch; private _category_general!: Gtk.Switch; private _category_people!: Gtk.Switch; private _keyword!: Adw.EntryRow; - private _resolutions!: Adw.EntryRow; + private _minimal_resolution!: Adw.EntryRow; private _row_color!: Adw.ActionRow; private _colorDialog: Gtk.ColorChooserDialog | undefined; @@ -98,8 +100,12 @@ const WallhavenSettingsGroup = GObject.registerClass({ this._keyword, 'text', Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('resolutions', - this._resolutions, + this._settings.bind('minimal-resolution', + this._minimal_resolution, + 'text', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('aspect-ratios', + this._aspect_ratios, 'text', Gio.SettingsBindFlags.DEFAULT); From f80018ed2db5f28fc19f6d6fefb07b1797ba51e7 Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 14 Dec 2022 14:34:05 +0100 Subject: [PATCH 18/87] Overall more logging --- debug.sh | 3 ++- src/adapter/baseAdapter.ts | 12 ++++++------ src/adapter/genericJson.ts | 2 +- src/adapter/localFolder.ts | 1 + src/adapter/unsplash.ts | 4 ++-- src/adapter/wallhaven.ts | 1 + src/hydraPaper.ts | 4 ++++ src/timer.ts | 9 ++++++++- src/wallpaperController.ts | 3 ++- 9 files changed, 27 insertions(+), 12 deletions(-) diff --git a/debug.sh b/debug.sh index 7576081e..dfc15a1c 100755 --- a/debug.sh +++ b/debug.sh @@ -3,5 +3,6 @@ if [ "$1" = "prefs" ]; then journalctl -f /usr/bin/gjs else - journalctl -f /usr/bin/gnome-shell + # https://gjs.guide/extensions/development/debugging.html#logging + journalctl -f GNOME_SHELL_EXTENSION_UUID=randomwallpaper@iflow.space fi diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 21e95da4..941e491b 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -8,12 +8,12 @@ import {SoupBowl} from './../soupBowl.js'; abstract class BaseAdapter { protected _bowl = new SoupBowl(); - logger: Logger; + protected _generalSettings: SettingsModule.Settings; + protected _logger: Logger; protected _settings: SettingsModule.Settings; - protected _wallpaperLocation: string; protected _sourceName: string; - protected _generalSettings: SettingsModule.Settings; + protected _wallpaperLocation: string; constructor(params: { defaultName: string; @@ -24,7 +24,7 @@ abstract class BaseAdapter { wallpaperLocation: string; }) { const path = `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${params.id}/`; - this.logger = new Logger('RWG3', `${params.defaultName} adapter`); + this._logger = new Logger('RWG3', `${params.defaultName} adapter`); this._wallpaperLocation = params.wallpaperLocation; this._settings = new SettingsModule.Settings(params.schemaID, params.schemaPath); @@ -90,7 +90,7 @@ abstract class BaseAdapter { const blockedFilenames = this._generalSettings.getStrv('blocked-images'); if (blockedFilenames.includes(filename)) { - this.logger.warn(`Image is blocked: ${filename}`); + this._logger.info(`Image is blocked: ${filename}`); return true; } @@ -100,7 +100,7 @@ abstract class BaseAdapter { // eslint-disable-next-line no-unused-vars protected _error(err: string, callback?: (element: null, error: { error: string }) => void) { const error = {error: err}; - this.logger.error(JSON.stringify(error)); + this._logger.error(JSON.stringify(error)); if (callback) callback(null, error); diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index aad618a5..8a1e771d 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -116,7 +116,7 @@ class GenericJsonAdapter extends BaseAdapter { }); } } catch (error) { - this.logger.warn(`Failed getting image: ${error}`); + this._logger.warn(`Failed getting image: ${error}`); // Do not escalate yet, try again } diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 164c771f..c1bf973c 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -32,6 +32,7 @@ class LocalFolderAdapter extends BaseAdapter { reject(new Error('No files found')); return; } + this._logger.debug(`Found ${files.length} possible wallpaper in "${folder.get_path()}"`); for (let i = 0; i < 20 && wallpaperResult.length < count; i++) { const randomFile = files[Utils.getRandomNumber(files.length)]; diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 1001769a..bfb66f67 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -35,7 +35,7 @@ class UnsplashAdapter extends BaseAdapter { let url = `https://source.unsplash.com${optionsString}`; url = encodeURI(url); - this.logger.info(`Unsplash request to: ${url}`); + this._logger.debug(`Unsplash request to: ${url}`); const message = this._bowl.newGetMessage(url); @@ -77,7 +77,7 @@ class UnsplashAdapter extends BaseAdapter { if (historyEntry && !this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) wallpaperResult.push(historyEntry); } catch (error) { - this.logger.warn(`Failed getting image: ${error}`); + this._logger.warn(`Failed getting image: ${error}`); // Do not escalate yet, try again } diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index 6e06850a..40bf7f13 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -59,6 +59,7 @@ class WallhavenAdapter extends BaseAdapter { const url = `https://wallhaven.cc/api/v1/search?${encodeURI(optionsString)}`; const message = this._bowl.newGetMessage(url); + this._logger.debug(`Search URL: ${url}`); const response_body_bytes = await this._bowl.send_and_receive(message); let response: WallhavenSearchResponse['data']; diff --git a/src/hydraPaper.ts b/src/hydraPaper.ts index 1f47d805..03e40d75 100644 --- a/src/hydraPaper.ts +++ b/src/hydraPaper.ts @@ -1,11 +1,13 @@ import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; +import {Logger} from './logger.js'; import * as Utils from './utils.js'; class HydraPaper { private _command: string[] | null = null; private _cancellable: Gio.Cancellable | null = null; + private _logger = new Logger('RWG3', 'HydraPaper'); isAvailable(): boolean { if (this._command !== null) @@ -30,6 +32,7 @@ class HydraPaper { if (this._cancellable === null) return; + this._logger.debug('Stopping running HydraPaper process.'); this._cancellable.cancel(); this._cancellable = null; } @@ -64,6 +67,7 @@ class HydraPaper { this._cancellable = new Gio.Cancellable(); // hydrapaper [--darkmode] --cli PATH PATH PATH + this._logger.debug(`Running command: ${command}`); await Utils.execCheck(command, this._cancellable); this._cancellable = null; diff --git a/src/timer.ts b/src/timer.ts index d99fd95f..2ced7100 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -42,6 +42,7 @@ class AFTimer { if (!this.isActive()) return; + this._logger.debug('Continuing timer'); this._paused = false; this.start(); } @@ -62,6 +63,7 @@ class AFTimer { * 'timer-last-trigger' stays the same. */ pause() { + this._logger.debug('Timer paused'); this._paused = true; this.cleanup(); } @@ -109,6 +111,8 @@ class AFTimer { // set new wallpaper if the interval was surpassed and set the timestamp to when it should have been updated if (this._surpassedInterval()) { if (this._timeoutEndCallback) { + this._logger.debug('Timer surpassed, running callback now'); + try { await this._timeoutEndCallback(); } catch (error) { @@ -121,6 +125,7 @@ class AFTimer { } // actual timer function + this._logger.debug(`Starting timer, will run callback in ${millisecondsRemaining}ms`); this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, millisecondsRemaining, () => { if (this._timeoutEndCallback) { this._timeoutEndCallback().then(() => { @@ -147,13 +152,15 @@ class AFTimer { */ cleanup() { if (this._timeout) { // only remove if a timeout is active + this._logger.debug('Removing running timer'); GLib.source_remove(this._timeout); this._timeout = undefined; } } /** - * Sets the last activation time to [now]. This doesn't affect already running timer. + * Sets the last activation time to [now]. This doesn't affect already running timer + * and will be ignored if the timer is paused. */ private _reset() { this._settings.setInt64('timer-last-trigger', new Date().getTime()); diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index f5dfaca2..ad25429b 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -511,7 +511,7 @@ class WallpaperController { type: imageAdapters[index].type, }; - this._logger.info(`Requesting image: ${element.source.imageDownloadUrl}`); + this._logger.debug(`Requesting image: ${element.source.imageDownloadUrl}`); fetchPromiseArray.push(imageAdapters[index].adapter.fetchFile(element)); } @@ -520,6 +520,7 @@ class WallpaperController { // wait for all fetching images const newImageEntries = await Promise.all(fetchPromises); + this._logger.info(`Requested ${newImageEntries.length} new images.`); // Move file to unique naming const movePromises = newImageEntries.map(entry => { From fcc7e2f16c81e1387600a7606976d4f3f16546da Mon Sep 17 00:00:00 2001 From: Lucki Date: Mon, 26 Dec 2022 03:21:59 +0100 Subject: [PATCH 19/87] More efficient history insert --- src/history.ts | 6 ++++-- src/wallpaperController.ts | 4 +--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/history.ts b/src/history.ts index 4325f875..f8b82a2a 100644 --- a/src/history.ts +++ b/src/history.ts @@ -62,8 +62,10 @@ class HistoryController { this.load(); } - insert(historyElement: HistoryEntry) { - this.history.unshift(historyElement); + insert(historyElements: HistoryEntry[]) { + for (const historyElement of historyElements) + this.history.unshift(historyElement); + this._deleteOldPictures(); this.save(); } diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index ad25429b..713d8aa4 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -568,9 +568,7 @@ class WallpaperController { }); // insert new wallpapers into history - newImageEntries.reverse().forEach(element => { - this._historyController.insert(element); - }); + this._historyController.insert(newImageEntries.reverse()); this._runPostCommands(); } finally { From 7cf9832798afcf2cf61d003464344d9950988f41 Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 27 Dec 2022 14:20:44 +0100 Subject: [PATCH 20/87] Correct stopLoadingHooks description --- src/wallpaperController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 713d8aa4..ae892603 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -48,7 +48,7 @@ class WallpaperController { private _timeout: number | null = null; /** functions will be called upon loading a new wallpaper */ private _startLoadingHooks: (() => void)[] = []; - /** functions will be called when loading a new wallpaper stopped. If an error occurred then the error will be passed as parameter. */ + /** functions will be called when loading a new wallpaper stopped. */ private _stopLoadingHooks: (() => void)[] = []; constructor() { From a90da5e32b75b41e646c5e05b7eaa361b7234492 Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 27 Dec 2022 15:22:01 +0100 Subject: [PATCH 21/87] Move HydraPaper to subfolder --- src/{ => manager}/hydraPaper.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{ => manager}/hydraPaper.ts (100%) diff --git a/src/hydraPaper.ts b/src/manager/hydraPaper.ts similarity index 100% rename from src/hydraPaper.ts rename to src/manager/hydraPaper.ts From 1d38ce741dc60e73b66e4f5b0ea00470de675918 Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 27 Dec 2022 15:12:24 +0100 Subject: [PATCH 22/87] Preparation for more manager --- src/manager/hydraPaper.ts | 39 +++++++++++-- src/manager/wallpaperManager.ts | 37 +++++++++++++ src/prefs.ts | 4 +- src/utils.ts | 36 +++++++++++- src/wallpaperController.ts | 98 +++++++++------------------------ 5 files changed, 134 insertions(+), 80 deletions(-) create mode 100644 src/manager/wallpaperManager.ts diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 03e40d75..55fcb3d6 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -1,10 +1,13 @@ import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; -import {Logger} from './logger.js'; -import * as Utils from './utils.js'; +import * as Utils from '../utils.js'; -class HydraPaper { +import {Logger} from '../logger.js'; +import {WallpaperManager} from './wallpaperManager.js'; +import {Settings} from '../settings.js'; + +class HydraPaper implements WallpaperManager { private _command: string[] | null = null; private _cancellable: Gio.Cancellable | null = null; private _logger = new Logger('RWG3', 'HydraPaper'); @@ -47,7 +50,7 @@ class HydraPaper { * @param {string[]} wallpaperArray Array of image paths * @param {boolean} darkmode Use darkmode, gives different image in cache path */ - async run(wallpaperArray: string[], darkmode: boolean = false) { + private async _run(wallpaperArray: string[], darkmode: boolean = false) { // Cancel already running processes before starting new ones this.cancelRunning(); @@ -72,6 +75,34 @@ class HydraPaper { this._cancellable = null; } + + async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings?: Settings, screensaverSettings?: Settings) { + if ((mode === 0 || mode === 2) && backgroundSettings) { + await this._run(wallpaperPaths); + + // Manually set key for darkmode because that's way faster + backgroundSettings.setString('picture-uri-dark', backgroundSettings.getString('picture-uri')); + } + + if (mode === 1 && backgroundSettings && screensaverSettings) { + // Remember keys, HydraPaper will change these + const tmpBackground = backgroundSettings.getString('picture-uri-dark'); + const tmpMode = backgroundSettings.getString('picture-options'); + + // Force HydraPaper to target a different resulting image by using darkmode + await this._run(wallpaperPaths, true); + + screensaverSettings.setString('picture-options', 'spanned'); + Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); + + // HydraPaper possibly changed these, change them back + backgroundSettings.setString('picture-uri-dark', tmpBackground); + backgroundSettings.setString('picture-options', tmpMode); + } + + if (mode === 2 && screensaverSettings && backgroundSettings) + Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + } } export {HydraPaper}; diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts new file mode 100644 index 00000000..c73dcf60 --- /dev/null +++ b/src/manager/wallpaperManager.ts @@ -0,0 +1,37 @@ +import type {Settings} from './../settings.js'; + +import {HydraPaper} from './hydraPaper.js'; + +abstract class WallpaperManager { + abstract isAvailable(): boolean; + abstract cancelRunning(): void; + + /** + * Set the wallpapers for a given mode. + * + * Modes: + * 0: Background + * 1: Lock screen + * 2: Background and lock screen + * + * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count + * @param {number} mode Mode to operate in + * @param {Settings} backgroundSettings Settings object of the background + * @param {Settings} screensaverSettings Settings object of the screensaver + */ + abstract setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings: Settings, screensaverSettings: Settings): Promise; +} + +/** + * + */ +function getWallpaperManager(): WallpaperManager | null { + const hydraPaper = new HydraPaper(); + + if (hydraPaper.isAvailable()) + return hydraPaper; + + return null; +} + +export {WallpaperManager, getWallpaperManager}; diff --git a/src/prefs.ts b/src/prefs.ts index f22e36f5..fc273cbf 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -139,8 +139,8 @@ class RandomWallpaperSettings { }); }); - import('./hydraPaper.js').then(module => { - if (new module.HydraPaper().isAvailable()) + import('./manager/wallpaperManager.js').then(module => { + if (module.getWallpaperManager()?.isAvailable()) (this._builder.get_object('multiple_displays_row') as Adw.ActionRow).set_sensitive(true); }).catch(this._logger.error); }).catch(error => { diff --git a/src/utils.ts b/src/utils.ts index bbbbb3b1..f7bd2e6c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -168,6 +168,39 @@ function removeItemOnce(array: T[], value: T) { return array; } +/** + * Set the picture-uri property of the given settings object to the path. + * Precondition: the settings object has to be a valid Gio settings object with the picture-uri property. + * + * @param {Settings} settings The settings schema object containing the keys to change + * @param {string} uri The picture URI to be set + */ +function setPictureUriOfSettingsObject(settings: Settings, uri: string) { + /* + * inspired from: + * https://bitbucket.org/LukasKnuth/backslide/src/7e36a49fc5e1439fa9ed21e39b09b61eca8df41a/backslide@codeisland.org/settings.js?at=master + */ + const setProp = (property: string) => { + if (settings.isWritable(property)) { + // Set a new Background-Image (should show up immediately): + settings.setString(property, uri); + } else { + throw new Error(`Property not writable: ${property}`); + } + }; + + const availableKeys = settings.listKeys(); + + let property = 'picture-uri'; + if (availableKeys.indexOf(property) !== -1) + setProp(property); + + + property = 'picture-uri-dark'; + if (availableKeys.indexOf(property) !== -1) + setProp(property); +} + export { execCheck, fileName, @@ -175,5 +208,6 @@ export { findFirstDifference, getMonitorCount, getRandomNumber, - removeItemOnce + removeItemOnce, + setPictureUriOfSettingsObject }; diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index ae892603..91015439 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -8,7 +8,7 @@ import * as SettingsModule from './settings.js'; import * as Utils from './utils.js'; import {AFTimer as Timer} from './timer.js'; -import {HydraPaper} from './hydraPaper.js'; +import {getWallpaperManager} from './manager/wallpaperManager.js'; import {Logger} from './logger.js'; // SourceAdapter @@ -41,7 +41,7 @@ class WallpaperController { private _settings = new SettingsModule.Settings(); private _timer = Timer.getTimer(); private _historyController: HistoryModule.HistoryController; - private _hydraPaper = new HydraPaper(); + private _wallpaperManager = getWallpaperManager(); private _autoFetch = {active: false, duration: 30}; private _previewId: string | undefined; private _resetWallpaper = false; @@ -328,52 +328,37 @@ class WallpaperController { const wallpaperUri = `file://${wallpaperPaths[0]}`; + if (wallpaperPaths.length > 1 && this._wallpaperManager) { + await this._wallpaperManager.setWallpaper(wallpaperPaths, type, backgroundSettings, screensaverSettings); + return; + } + if (type === 0 || type === 2) { - if (wallpaperPaths.length > 1) { - await this._hydraPaper.run(wallpaperPaths); + // FIXME: 'merged_wallpaper' is hardcoded for HydraPaper + if (wallpaperUri.includes('merged_wallpaper')) + // merged wallpapers need mode "spanned" + backgroundSettings.setString('picture-options', 'spanned'); + else + // single wallpapers need mode "zoom" + backgroundSettings.setString('picture-options', 'zoom'); - // Manually set key for darkmode because that's way faster - backgroundSettings.setString('picture-uri-dark', backgroundSettings.getString('picture-uri')); - } else { - if (wallpaperUri.includes('merged_wallpaper')) - // merged wallpapers need mode "spanned" - backgroundSettings.setString('picture-options', 'spanned'); - else - // single wallpapers need mode "zoom" - backgroundSettings.setString('picture-options', 'zoom'); - - this._setPictureUriOfSettingsObject(backgroundSettings, wallpaperUri); - } + Utils.setPictureUriOfSettingsObject(backgroundSettings, wallpaperUri); } if (type === 1) { - if (wallpaperPaths.length > 1) { - // Remember keys, HydraPaper will change these - const tmpBackground = backgroundSettings.getString('picture-uri-dark'); - const tmpMode = backgroundSettings.getString('picture-options'); - - // Force HydraPaper to target a different resulting image by using darkmode - await this._hydraPaper.run(wallpaperPaths, true); - + // FIXME: 'merged_wallpaper' is hardcoded for HydraPaper + if (wallpaperUri.includes('merged_wallpaper')) + // merged wallpapers need mode "spanned" screensaverSettings.setString('picture-options', 'spanned'); - this._setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); + else + // single wallpapers need mode "zoom" + screensaverSettings.setString('picture-options', 'zoom'); - // HydraPaper possibly changed these, change them back - backgroundSettings.setString('picture-uri-dark', tmpBackground); - backgroundSettings.setString('picture-options', tmpMode); - } else { - if (wallpaperUri.includes('merged_wallpaper')) - // merged wallpapers need mode "spanned" - screensaverSettings.setString('picture-options', 'spanned'); - else - // single wallpapers need mode "zoom" - screensaverSettings.setString('picture-options', 'zoom'); - - this._setPictureUriOfSettingsObject(screensaverSettings, wallpaperUri); - } + Utils.setPictureUriOfSettingsObject(screensaverSettings, wallpaperUri); } if (type === 2) { + // FIXME: 'merged_wallpaper' is hardcoded for HydraPaper if (wallpaperUri.includes('merged_wallpaper')) // merged wallpapers need mode "spanned" screensaverSettings.setString('picture-options', 'spanned'); @@ -381,7 +366,7 @@ class WallpaperController { // single wallpapers need mode "zoom" screensaverSettings.setString('picture-options', 'zoom'); - this._setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); } } @@ -421,39 +406,6 @@ class WallpaperController { return newWallpaperArray.slice(0, count); } - /** - * Set the picture-uri property of the given settings object to the path. - * Precondition: the settings object has to be a valid Gio settings object with the picture-uri property. - * - * @param {SettingsModule.Settings} settings The settings schema object containing the keys to change - * @param {string} uri The picture URI to be set - */ - private _setPictureUriOfSettingsObject(settings: SettingsModule.Settings, uri: string) { - /* - inspired from: - https://bitbucket.org/LukasKnuth/backslide/src/7e36a49fc5e1439fa9ed21e39b09b61eca8df41a/backslide@codeisland.org/settings.js?at=master - */ - const setProp = (property: string) => { - if (settings.isWritable(property)) { - // Set a new Background-Image (should show up immediately): - settings.setString(property, uri); - } else { - throw new Error(`Property not writable: ${property}`); - } - }; - - const availableKeys = settings.listKeys(); - - let property = 'picture-uri'; - if (availableKeys.indexOf(property) !== -1) - setProp(property); - - - property = 'picture-uri-dark'; - if (availableKeys.indexOf(property) !== -1) - setProp(property); - } - async setWallpaper(historyId: string) { const historyElement = this._historyController.get(historyId); @@ -614,7 +566,7 @@ class WallpaperController { if (!this._settings.getBoolean('multiple-displays')) return 1; - if (!this._hydraPaper.isAvailable()) + if (!this._wallpaperManager?.isAvailable()) return 1; return Utils.getMonitorCount(); From c51c2fce0c8d4e9a1355a31c7cc8d6c5c89248e3 Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 27 Dec 2022 15:16:56 +0100 Subject: [PATCH 23/87] let to const --- src/history.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/history.ts b/src/history.ts index f8b82a2a..0a2c457a 100644 --- a/src/history.ts +++ b/src/history.ts @@ -163,8 +163,6 @@ class HistoryController { const enumerator = directory.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null); let fileInfo; - let deleteFile; - do { fileInfo = enumerator.next_file(null); @@ -175,7 +173,7 @@ class HistoryController { // ignore hidden files and first element if (id[0] !== '.' && id !== firstHistoryElement.id) { - deleteFile = Gio.file_new_for_path(this._wallpaperLocation + id); + const deleteFile = Gio.file_new_for_path(this._wallpaperLocation + id); deleteFile.delete(null); } } while (fileInfo); @@ -189,11 +187,10 @@ class HistoryController { */ private _deleteOldPictures() { this.size = this._settings.getInt('history-length'); - let deleteFile; while (this.history.length > this.size) { const path = this.history.pop()?.path; if (path) { - deleteFile = Gio.file_new_for_path(path); + const deleteFile = Gio.file_new_for_path(path); deleteFile.delete(null); } } From 26101ffb6c8faaea2a4cfd3ebd4f9b85127808f3 Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 28 Dec 2022 02:31:27 +0100 Subject: [PATCH 24/87] Possibility to use Superpaper instead HydraPaper --- src/manager/superPaper.ts | 90 +++++++++++++++++++++++++++++++++ src/manager/wallpaperManager.ts | 6 ++- src/ui/pageGeneral.blp | 2 +- src/wallpaperController.ts | 12 ++--- 4 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 src/manager/superPaper.ts diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts new file mode 100644 index 00000000..529c0953 --- /dev/null +++ b/src/manager/superPaper.ts @@ -0,0 +1,90 @@ +import * as Gio from 'gi://Gio'; +import * as GLib from 'gi://GLib'; + +import * as Utils from '../utils.js'; + +import {Logger} from './../logger.js'; +import {Settings} from './../settings.js'; +import {WallpaperManager} from './wallpaperManager.js'; + +class Superpaper implements WallpaperManager { + private _command: string[] | null = null; + private _cancellable: Gio.Cancellable | null = null; + private _logger = new Logger('RWG3', 'Superpaper'); + + isAvailable(): boolean { + if (this._command !== null) + return true; + + const path = GLib.find_program_in_path('superpaper'); + if (path) { + this._command = [path]; + return true; + } + + return false; + } + + cancelRunning(): void { + if (!this._cancellable) + return; + + this._logger.debug('Stopping running HydraPaper process.'); + this._cancellable.cancel(); + this._cancellable = null; + } + + async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings: Settings, screensaverSettings: Settings): Promise { + if ((mode === 0 || mode === 2) && backgroundSettings) + await this._run(wallpaperPaths); + + if (mode === 1 && backgroundSettings && screensaverSettings) { + // Remember keys, Superpaper will change these + const tmpBackground = backgroundSettings.getString('picture-uri'); + const tmpBackgroundDark = backgroundSettings.getString('picture-uri-dark'); + const tmpMode = backgroundSettings.getString('picture-options'); + + await this._run(wallpaperPaths); + + screensaverSettings.setString('picture-options', 'spanned'); + Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); + + // Superpaper possibly changed these, change them back + backgroundSettings.setString('picture-uri', tmpBackground); + backgroundSettings.setString('picture-uri-dark', tmpBackgroundDark); + backgroundSettings.setString('picture-options', tmpMode); + } + + if (mode === 2 && screensaverSettings && backgroundSettings) + Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + } + + // https://github.com/hhannine/superpaper/blob/master/docs/cli-usage.md + // * Saves merged images alternating in "$XDG_CACHE_HOME/superpaper/temp/cli-{a,b}.png" + // * Sets picture-option to spanned + // * Sets both picture-uri options + // * Can use only single images + private async _run(wallpaperArray: string[]) { + // Cancel already running processes before starting new ones + this.cancelRunning(); + + if (!this._command) + return; + + // Needs a copy here + let command = [...this._command]; + + // cspell:disable-next-line + command.push('--setimages'); + command = command.concat(wallpaperArray); + + this._cancellable = new Gio.Cancellable(); + + this._logger.debug(`Running command: "${command}"`); + await Utils.execCheck(command, this._cancellable); + + this._cancellable = null; + } +} + +export {Superpaper}; diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts index c73dcf60..9f6cc787 100644 --- a/src/manager/wallpaperManager.ts +++ b/src/manager/wallpaperManager.ts @@ -1,6 +1,7 @@ import type {Settings} from './../settings.js'; import {HydraPaper} from './hydraPaper.js'; +import {Superpaper} from './superPaper.js'; abstract class WallpaperManager { abstract isAvailable(): boolean; @@ -27,10 +28,13 @@ abstract class WallpaperManager { */ function getWallpaperManager(): WallpaperManager | null { const hydraPaper = new HydraPaper(); - if (hydraPaper.isAvailable()) return hydraPaper; + const superpaper = new Superpaper(); + if (superpaper.isAvailable()) + return superpaper; + return null; } diff --git a/src/ui/pageGeneral.blp b/src/ui/pageGeneral.blp index a0e5caff..e5661e4c 100644 --- a/src/ui/pageGeneral.blp +++ b/src/ui/pageGeneral.blp @@ -55,7 +55,7 @@ Adw.PreferencesPage page_general { Adw.ActionRow multiple_displays_row { title: _("Different wallpapers on multiple displays"); - subtitle: _("Requires HydraPaper.\nFills from History."); + subtitle: _("Requires HydraPaper or Superpaper.\nFills from History."); sensitive: false; Switch enable_multiple_displays { diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 91015439..7766798d 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -334,8 +334,8 @@ class WallpaperController { } if (type === 0 || type === 2) { - // FIXME: 'merged_wallpaper' is hardcoded for HydraPaper - if (wallpaperUri.includes('merged_wallpaper')) + // FIXME: These are currently hardcoded for the few available wallpaperManager + if (wallpaperUri.includes('merged_wallpaper') || wallpaperUri.includes('cli-a') || wallpaperUri.includes('cli-b')) // merged wallpapers need mode "spanned" backgroundSettings.setString('picture-options', 'spanned'); else @@ -346,8 +346,8 @@ class WallpaperController { } if (type === 1) { - // FIXME: 'merged_wallpaper' is hardcoded for HydraPaper - if (wallpaperUri.includes('merged_wallpaper')) + // FIXME: These are currently hardcoded for the few available wallpaperManager + if (wallpaperUri.includes('merged_wallpaper') || wallpaperUri.includes('cli-a') || wallpaperUri.includes('cli-b')) // merged wallpapers need mode "spanned" screensaverSettings.setString('picture-options', 'spanned'); else @@ -358,8 +358,8 @@ class WallpaperController { } if (type === 2) { - // FIXME: 'merged_wallpaper' is hardcoded for HydraPaper - if (wallpaperUri.includes('merged_wallpaper')) + // FIXME: These are currently hardcoded for the few available wallpaperManager + if (wallpaperUri.includes('merged_wallpaper') || wallpaperUri.includes('cli-a') || wallpaperUri.includes('cli-b')) // merged wallpapers need mode "spanned" screensaverSettings.setString('picture-options', 'spanned'); else From 098de76d6f323186d997d2714b137151da9eac54 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 11 Feb 2023 00:42:39 +0100 Subject: [PATCH 25/87] Trim file:// from wallpaper path for post commands --- src/wallpaperController.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 7766798d..c386dceb 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -375,8 +375,9 @@ class WallpaperController { const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); const commandString = this._settings.getString('general-post-command'); - // Read the current wallpaper uri from settings because it could be a merged wallpaper from HydraPaper - const currentWallpaperPath = backgroundSettings.getString('picture-uri'); + // Read the current wallpaper uri from settings because it could be a merged wallpaper + // Remove prefix "file://" to get the real path + const currentWallpaperPath = backgroundSettings.getString('picture-uri').slice(7); // TODO: this ignores the lock-screen const generalPostCommandArray = this._getCommandArray(commandString, currentWallpaperPath); From 43ff6c1899216690f6ffb3123375102550fcea8a Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 21 Feb 2023 23:58:45 +0100 Subject: [PATCH 26/87] [Local folder] Allow all image types Implementing review feedback from PR #145 https://github.com/ifl0w/RandomWallpaperGnome3/pull/145#discussion_r1113579640 --- src/adapter/localFolder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index c1bf973c..9765d16f 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -98,7 +98,7 @@ class LocalFolderAdapter extends BaseAdapter { } const contentType = info.get_content_type(); - if (contentType === 'image/png' || contentType === 'image/jpeg') + if (contentType?.startsWith('image/')) files.push(child); } From 17712096e88dc233923c4c035d18d7041b7279f7 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 26 Feb 2023 02:48:19 +0100 Subject: [PATCH 27/87] Throw error if folder creation fails https://github.com/ifl0w/RandomWallpaperGnome3/pull/147/files#r1117888581 --- src/historyMenuElements.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 0a65eefe..53eeb442 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -214,7 +214,10 @@ const HistoryElement = GObject.registerClass({ if (!targetFolder.make_directory_with_parents(null)) throw new Error('Could not create directories.'); } catch (error) { - if (error === Gio.IOErrorEnum.EXISTS) { /** noop */ } + if (error instanceof GLib.Error && error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) // noop + this._logger.debug('Folder already exists.'); + else // escalate + throw error; } // This function was rewritten by Gio._promisify From 46343cdb56b923f4b0ce27f1ecf2d9629d399001 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 26 Feb 2023 02:13:22 +0100 Subject: [PATCH 28/87] Remove unnecessary file moving https://github.com/ifl0w/RandomWallpaperGnome3/pull/147#discussion_r1117938578 --- src/adapter/baseAdapter.ts | 7 +----- src/adapter/genericJson.ts | 3 +-- src/adapter/localFolder.ts | 10 +++----- src/adapter/reddit.ts | 3 +-- src/adapter/unsplash.ts | 3 +-- src/adapter/urlSource.ts | 3 +-- src/adapter/wallhaven.ts | 3 +-- src/history.ts | 13 +++++++---- src/wallpaperController.ts | 47 +++++++++----------------------------- 9 files changed, 28 insertions(+), 64 deletions(-) diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 941e491b..3827d06a 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -13,7 +13,6 @@ abstract class BaseAdapter { protected _logger: Logger; protected _settings: SettingsModule.Settings; protected _sourceName: string; - protected _wallpaperLocation: string; constructor(params: { defaultName: string; @@ -21,12 +20,10 @@ abstract class BaseAdapter { name: string | null; schemaID: string; schemaPath: string; - wallpaperLocation: string; }) { const path = `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${params.id}/`; this._logger = new Logger('RWG3', `${params.defaultName} adapter`); - this._wallpaperLocation = params.wallpaperLocation; this._settings = new SettingsModule.Settings(params.schemaID, params.schemaPath); this._sourceName = params.name ?? params.defaultName; @@ -51,7 +48,7 @@ abstract class BaseAdapter { * @param {HistoryEntry} historyEntry The historyEntry to fetch */ async fetchFile(historyEntry: HistoryEntry) { - const file = Gio.file_new_for_path(`${this._wallpaperLocation}/${String(historyEntry.name)}`); + const file = Gio.file_new_for_path(historyEntry.path); const fstream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); // craft new message from details @@ -67,8 +64,6 @@ abstract class BaseAdapter { fstream.write(response_data_bytes, null); fstream.close(null); - historyEntry.path = file.get_path(); - return historyEntry; } diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 8a1e771d..d6cbc628 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -8,14 +8,13 @@ import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; class GenericJsonAdapter extends BaseAdapter { - constructor(id: string, name: string, wallpaperLocation: string) { + constructor(id: string, name: string) { super({ defaultName: 'Generic JSON Source', id, name, schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_GENERIC_JSON, schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/genericJSON/${id}/`, - wallpaperLocation, }); } diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 9765d16f..88d8bf5e 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -11,14 +11,13 @@ import {HistoryEntry} from './../history.js'; Gio._promisify(Gio.File.prototype, 'copy_async', 'copy_finish'); class LocalFolderAdapter extends BaseAdapter { - constructor(id: string, name: string, wallpaperLocation: string) { + constructor(id: string, name: string) { super({ defaultName: 'Local Folder', id, name, schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_LOCAL_FOLDER, schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/localFolder/${id}/`, - wallpaperLocation, }); } @@ -43,7 +42,7 @@ class LocalFolderAdapter extends BaseAdapter { continue; const historyEntry = new HistoryEntry(null, this._sourceName, randomFilePath); - historyEntry.source.sourceUrl = this._wallpaperLocation; + historyEntry.source.sourceUrl = randomFilePath; if (!this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) wallpaperResult.push(historyEntry); @@ -60,8 +59,7 @@ class LocalFolderAdapter extends BaseAdapter { async fetchFile(historyEntry: HistoryEntry) { const sourceFile = Gio.File.new_for_uri(historyEntry.source.imageDownloadUrl); - const name = sourceFile.get_basename(); - const targetFile = Gio.File.new_for_path(this._wallpaperLocation + String(name)); + const targetFile = Gio.File.new_for_path(historyEntry.path); // https://gjs.guide/guides/gio/file-operations.html#copying-and-moving-files // This function was rewritten by Gio._promisify @@ -69,8 +67,6 @@ class LocalFolderAdapter extends BaseAdapter { if (!await sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null)) throw new Error('Failed copying image.'); - historyEntry.path = targetFile.get_path(); - return historyEntry; } diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index bf89c20c..ba724ed9 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -31,14 +31,13 @@ interface RedditSubmission { } class RedditAdapter extends BaseAdapter { - constructor(id: string, name: string, wallpaperLocation: string) { + constructor(id: string, name: string) { super({ defaultName: 'Reddit', id, name, schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_REDDIT, schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/reddit/${id}/`, - wallpaperLocation, }); } diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index bfb66f67..90f3717d 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -17,14 +17,13 @@ class UnsplashAdapter extends BaseAdapter { 'constraintValue': '', }; - constructor(id: string | null, name: string | null, wallpaperLocation: string) { + constructor(id: string | null, name: string | null) { super({ defaultName: 'Unsplash', id: id ?? '-1', name, schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH, schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id}/`, - wallpaperLocation, }); } diff --git a/src/adapter/urlSource.ts b/src/adapter/urlSource.ts index fc3825bd..b7448e1a 100644 --- a/src/adapter/urlSource.ts +++ b/src/adapter/urlSource.ts @@ -4,14 +4,13 @@ import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; class UrlSourceAdapter extends BaseAdapter { - constructor(id: string, name: string, wallpaperLocation: string) { + constructor(id: string, name: string) { super({ defaultName: 'Static URL', id, name, schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_URL_SOURCE, schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/urlSource/${id}/`, - wallpaperLocation, }); } diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index 40bf7f13..bad8f207 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -39,12 +39,11 @@ class WallhavenAdapter extends BaseAdapter { colors: '', }; - constructor(id: string, name: string, wallpaperLocation: string) { + constructor(id: string, name: string) { super({ id, schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN, schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/wallhaven/${id}/`, - wallpaperLocation, name, defaultName: 'Wallhaven', }); diff --git a/src/history.ts b/src/history.ts index 0a2c457a..a2fbae21 100644 --- a/src/history.ts +++ b/src/history.ts @@ -4,6 +4,9 @@ import * as Utils from './utils.js'; import {Settings} from './settings.js'; +// Gets filled by the HistoryController which is constructed at extension startup +let _wallpaperLocation: string; + interface SourceInfo { author: string | null; authorUrl: string | null; @@ -26,7 +29,7 @@ class HistoryEntry { id: string; /** Basename of URI */ name: string | null; // This can be null when an entry from an older version is mapped from settings - path: string | null = null; + path: string; source: SourceInfo; adapter: AdapterInfo | null = { // This can be null when an entry from an older version is mapped from settings id: null, @@ -46,6 +49,7 @@ class HistoryEntry { // extract the name from the url this.name = Utils.fileName(this.source.imageDownloadUrl); this.id = `${this.timestamp}_${this.name}`; + this.path = `${_wallpaperLocation}/${this.id}`; } } @@ -54,10 +58,9 @@ class HistoryController { size = 10; private _settings = new Settings(); - private _wallpaperLocation: string; constructor(wallpaperLocation: string) { - this._wallpaperLocation = wallpaperLocation; + _wallpaperLocation = wallpaperLocation; this.load(); } @@ -159,7 +162,7 @@ class HistoryController { if (firstHistoryElement) this.history = [firstHistoryElement]; - const directory = Gio.file_new_for_path(this._wallpaperLocation); + const directory = Gio.file_new_for_path(_wallpaperLocation); const enumerator = directory.enumerate_children('', Gio.FileQueryInfoFlags.NONE, null); let fileInfo; @@ -173,7 +176,7 @@ class HistoryController { // ignore hidden files and first element if (id[0] !== '.' && id !== firstHistoryElement.id) { - const deleteFile = Gio.file_new_for_path(this._wallpaperLocation + id); + const deleteFile = Gio.file_new_for_path(_wallpaperLocation + id); deleteFile.delete(null); } } while (fileInfo); diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index c386dceb..24cc77ae 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -198,7 +198,7 @@ class WallpaperController { if (sourceIDs.length < 1 || sourceIDs[0] === '-1') { randomAdapterResult.push({ - adapter: new UnsplashAdapter(null, null, this.wallpaperLocation), + adapter: new UnsplashAdapter(null, null), id: '-1', type: 0, imageCount: count, @@ -240,31 +240,31 @@ class WallpaperController { try { switch (sourceType) { case 0: - imageSourceAdapter = new UnsplashAdapter(sourceID, sourceName, this.wallpaperLocation); + imageSourceAdapter = new UnsplashAdapter(sourceID, sourceName); break; case 1: - imageSourceAdapter = new WallhavenAdapter(sourceID, sourceName, this.wallpaperLocation); + imageSourceAdapter = new WallhavenAdapter(sourceID, sourceName); break; case 2: - imageSourceAdapter = new RedditAdapter(sourceID, sourceName, this.wallpaperLocation); + imageSourceAdapter = new RedditAdapter(sourceID, sourceName); break; case 3: - imageSourceAdapter = new GenericJsonAdapter(sourceID, sourceName, this.wallpaperLocation); + imageSourceAdapter = new GenericJsonAdapter(sourceID, sourceName); break; case 4: - imageSourceAdapter = new LocalFolderAdapter(sourceID, sourceName, this.wallpaperLocation); + imageSourceAdapter = new LocalFolderAdapter(sourceID, sourceName); break; case 5: - imageSourceAdapter = new UrlSourceAdapter(sourceID, sourceName, this.wallpaperLocation); + imageSourceAdapter = new UrlSourceAdapter(sourceID, sourceName); break; default: - imageSourceAdapter = new UnsplashAdapter(null, null, this.wallpaperLocation); + imageSourceAdapter = new UnsplashAdapter(null, null); sourceType = 0; break; } } catch (error) { this._logger.warn('Had errors, fetching with default settings.'); - imageSourceAdapter = new UnsplashAdapter(null, null, this.wallpaperLocation); + imageSourceAdapter = new UnsplashAdapter(null, null); sourceType = 0; } @@ -475,34 +475,9 @@ class WallpaperController { const newImageEntries = await Promise.all(fetchPromises); this._logger.info(`Requested ${newImageEntries.length} new images.`); - // Move file to unique naming - const movePromises = newImageEntries.map(entry => { - if (!entry.path) - return Promise.resolve(false); - - const file = Gio.File.new_for_path(entry.path); - const targetFolder = file.get_parent(); - const targetFile = targetFolder?.get_child(entry.id); - - if (!targetFile) - throw new Error('Failed getting targetFile'); - - entry.path = targetFile.get_path(); - - // This function is Gio._promisified - return file.move_async(targetFile, Gio.FileCopyFlags.NONE, 0, null, null); - }); - - // wait for all images to be moved - await Promise.all(movePromises); - const newWallpaperPaths = newImageEntries.map(element => { - if (element.path) - return element.path; - - // eslint-disable-next-line - return; - }) as string[]; // cast because we made sure it's defined + return element.path; + }); const usedWallpaperPaths = this._fillDisplaysFromHistory(newWallpaperPaths, monitorCount); if (changeType === 3) { From ac5d72bb429f71b6879ca040301d83de8365233c Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 26 Feb 2023 14:38:44 +0100 Subject: [PATCH 29/87] Add note for Gnome 44+ --- build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.sh b/build.sh index 3a939da2..f55b24dd 100755 --- a/build.sh +++ b/build.sh @@ -76,6 +76,8 @@ compile_js() { sed -i -E "s#export \{\};##g" "$DESTDIR/extension.js" } +# TODO: Drop compiled schemas when only targeting Gnome 44+ +# https://gjs.guide/extensions/upgrading/gnome-shell-44.html#gsettings-schema compile_schemas() { check_command "glib-compile-schemas" mkdir -p "$DESTDIR/schemas/" From 5d64420c2ed17e51dfb8bcaddb6aec76ddd56bde Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 25 Feb 2023 13:10:16 +0100 Subject: [PATCH 30/87] Tighten required typing Do not allow implicit any --- .eslintrc.yml | 1 + build.sh | 14 ++++++++++++- src/adapter/baseAdapter.ts | 2 +- src/adapter/genericJson.ts | 5 +++-- src/adapter/localFolder.ts | 3 ++- src/adapter/reddit.ts | 18 +++++++++++++++- src/adapter/unsplash.ts | 5 +++-- src/adapter/wallhaven.ts | 30 +++++++++++++++++--------- src/extension.ts | 4 +++- src/history.ts | 23 ++++++++++++++++++-- src/historyMenuElements.ts | 35 +++++++++++++++++++++++-------- src/jsonPath.ts | 2 +- src/manager/hydraPaper.ts | 2 +- src/manager/superPaper.ts | 2 +- src/prefs.ts | 36 +++++++++++++++++-------------- src/randomWallpaperMenu.ts | 9 +++++--- src/settings.ts | 4 ++-- src/soupBowl.ts | 19 ++++++++++++----- src/timer.ts | 13 +++++++++--- src/ui/sourceRow.ts | 2 +- src/utils.ts | 1 + src/wallpaperController.ts | 43 +++++++++++++++++++++++++++----------- 22 files changed, 198 insertions(+), 75 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index cb1bdc53..2daa659a 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -5,6 +5,7 @@ env: es2021: true extends: - 'eslint:recommended' + - 'plugin:@typescript-eslint/recommended-requiring-type-checking' parser: "@typescript-eslint/parser" plugins: - jsdoc diff --git a/build.sh b/build.sh index f55b24dd..20e7bca0 100755 --- a/build.sh +++ b/build.sh @@ -89,8 +89,20 @@ compile_schemas() { format_js() { check_command "npm" + # Circumvent not found typescript rules that might be mentioned in code comments but will give an error + # when only checking with javascript rules + # https://stackoverflow.com/questions/64614131/how-can-i-disable-definition-for-rule-custom-rule-was-not-found-errors + shopt -s globstar nullglob + for file in "$DESTDIR"/**/*.js; do + sed -i -E "s#@typescript-eslint/await-thenable##g" "$file" + sed -i -E "s#@typescript-eslint/no-unused-vars##g" "$file" + sed -i -E "s#@typescript-eslint/no-unsafe-argument##g" "$file" + sed -i -E "s#@typescript-eslint/no-unsafe-member-access##g" "$file" + sed -i -E "s#@typescript-eslint/no-unsafe-call##g" "$file" + done + # Format js using the official gjs stylesheet and a few manual quirks - npx --silent eslint --config "$SCRIPTDIR/.eslintrc-gjs.yml" --fix "$DESTDIR/**/*.js" + npx --silent eslint --no-eslintrc --config "$SCRIPTDIR/.eslintrc-gjs.yml" --fix "$DESTDIR/**/*.js" } check_ts() { diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 3827d06a..4c761c99 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -52,7 +52,7 @@ abstract class BaseAdapter { const fstream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); // craft new message from details - let request = this._bowl.newGetMessage(historyEntry.source.imageDownloadUrl); + const request = this._bowl.newGetMessage(historyEntry.source.imageDownloadUrl); // start the download const response_data_bytes = await this._bowl.send_and_receive(request); diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index d6cbc628..5765a8df 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -32,7 +32,7 @@ class GenericJsonAdapter extends BaseAdapter { if (!response_body_bytes) throw new Error('Error fetching response.'); - const response_body = JSON.parse(ByteArray.toString(response_body_bytes)); + const response_body: unknown = JSON.parse(ByteArray.toString(response_body_bytes)); const imageJSONPath = this._settings.getString('image-path'); const postJSONPath = this._settings.getString('post-path'); const domainUrl = this._settings.getString('domain'); @@ -115,7 +115,8 @@ class GenericJsonAdapter extends BaseAdapter { }); } } catch (error) { - this._logger.warn(`Failed getting image: ${error}`); + this._logger.warn('Failed getting image'); + this._logger.warn(error); // Do not escalate yet, try again } diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 88d8bf5e..401c5840 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -31,7 +31,7 @@ class LocalFolderAdapter extends BaseAdapter { reject(new Error('No files found')); return; } - this._logger.debug(`Found ${files.length} possible wallpaper in "${folder.get_path()}"`); + this._logger.debug(`Found ${files.length} possible wallpaper in "${this._settings.getString('folder')}"`); for (let i = 0; i < 20 && wallpaperResult.length < count; i++) { const randomFile = files[Utils.getRandomNumber(files.length)]; @@ -64,6 +64,7 @@ class LocalFolderAdapter extends BaseAdapter { // https://gjs.guide/guides/gio/file-operations.html#copying-and-moving-files // This function was rewritten by Gio._promisify // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/await-thenable if (!await sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null)) throw new Error('Failed copying image.'); diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index ba724ed9..24133b64 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -55,7 +55,9 @@ class RedditAdapter extends BaseAdapter { const response_body_bytes = await this._bowl.send_and_receive(message); - const response_body: RedditResponse = JSON.parse(ByteArray.toString(response_body_bytes)); + const response_body = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + if (!this._isRedditResponse(response_body)) + throw new Error('Unexpected response'); const filteredSubmissions = response_body.data.children.filter(child => { if (child.data.post_hint !== 'image') @@ -101,6 +103,20 @@ class RedditAdapter extends BaseAdapter { return wallpaperResult; } + + private _isRedditResponse(object: unknown): object is RedditResponse { + if (typeof object === 'object' && + object && + 'data' in object && + typeof object.data === 'object' && + object.data && + 'children' in object.data && + Array.isArray(object.data.children) + ) + return true; + + return false; + } } export {RedditAdapter}; diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 90f3717d..78e188b4 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -23,7 +23,7 @@ class UnsplashAdapter extends BaseAdapter { id: id ?? '-1', name, schemaID: SettingsModule.RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH, - schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id}/`, + schemaPath: `${SettingsModule.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id ?? '-1'}/`, }); } @@ -76,7 +76,8 @@ class UnsplashAdapter extends BaseAdapter { if (historyEntry && !this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) wallpaperResult.push(historyEntry); } catch (error) { - this._logger.warn(`Failed getting image: ${error}`); + this._logger.warn('Failed getting image.'); + this._logger.warn(error); // Do not escalate yet, try again } diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index bad8f207..d16b4d70 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -61,12 +61,11 @@ class WallhavenAdapter extends BaseAdapter { this._logger.debug(`Search URL: ${url}`); const response_body_bytes = await this._bowl.send_and_receive(message); - let response: WallhavenSearchResponse['data']; - try { - response = JSON.parse(ByteArray.toString(response_body_bytes)).data; - } catch { - throw new Error('Error parsing API.'); - } + const wallhavenResponse = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + if (!this._isWallhavenResponse(wallhavenResponse)) + throw new Error('Unexpected response'); + + const response = wallhavenResponse.data; if (!response || response.length === 0) throw new Error('Empty response'); @@ -104,14 +103,25 @@ class WallhavenAdapter extends BaseAdapter { if (options.hasOwnProperty(key)) { if (Array.isArray(options[key])) optionsString += `${key}=${(options[key] as Array).join()}&`; - else if (options[key] !== '') - optionsString += `${key}=${options[key]}&`; + else if (typeof options[key] === 'string' && options[key] !== '') + optionsString += `${key}=${options[key] as string}&`; } } return optionsString; } + private _isWallhavenResponse(object: unknown): object is WallhavenSearchResponse { + if (typeof object === 'object' && + object && + 'data' in object && + Array.isArray(object.data) + ) + return true; + + return false; + } + private _readOptionsFromSettings() { const keywords = this._settings.getString('keyword').split(','); if (keywords.length > 0) { @@ -126,13 +136,13 @@ class WallhavenAdapter extends BaseAdapter { return elem.trim(); }); - let categories = []; + const categories = []; categories.push(Number(this._settings.getBoolean('category-general'))); // + is implicit conversion to int categories.push(Number(this._settings.getBoolean('category-anime'))); categories.push(Number(this._settings.getBoolean('category-people'))); this._options.categories = categories.join(''); - let purity = []; + const purity = []; purity.push(Number(this._settings.getBoolean('allow-sfw'))); purity.push(Number(this._settings.getBoolean('allow-sketchy'))); purity.push(Number(this._settings.getBoolean('allow-nsfw'))); diff --git a/src/extension.ts b/src/extension.ts index f9f91a78..bb523c7a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -38,8 +38,10 @@ class Extension { }).catch(error => { if (this._logger) this._logger.error(error); - else + else if (error instanceof Error) logError(error); + else + logError(new Error('Unknown error')); }); } diff --git a/src/history.ts b/src/history.ts index a2fbae21..aa1b3bb9 100644 --- a/src/history.ts +++ b/src/history.ts @@ -137,8 +137,12 @@ class HistoryController { this.size = this._settings.getInt('history-length'); const stringHistory: string[] = this._settings.getStrv('history'); - this.history = stringHistory.map((elem: string) => { - return JSON.parse(elem); + this.history = stringHistory.map((elem: string) => { + const unknownObject = JSON.parse(elem) as unknown; + if (!this._isHistoryEntry(unknownObject)) + throw new Error('Failed loading history data.'); + + return unknownObject; }); } @@ -198,6 +202,21 @@ class HistoryController { } } } + + private _isHistoryEntry(object: unknown): object is HistoryEntry { + if (typeof object === 'object' && + object && + 'timestamp' in object && + typeof object.timestamp === 'number' && + 'id' in object && + typeof object.id === 'string' && + 'path' in object && + typeof object.path === 'string' + ) + return true; + + return false; + } } export {HistoryEntry, HistoryController}; diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 53eeb442..a995f09b 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -88,8 +88,11 @@ const HistoryElement = GObject.registerClass({ this.historyEntry.source.authorUrl !== null) { const authorItem = new PopupMenu.PopupMenuItem(`Image By: ${this.historyEntry.source.author}`); authorItem.connect('activate', () => { - if (this.historyEntry.source.authorUrl) - Utils.execCheck(['xdg-open', this.historyEntry.source.authorUrl]).catch(this._logger.error); + if (this.historyEntry.source.authorUrl) { + Utils.execCheck(['xdg-open', this.historyEntry.source.authorUrl]).catch(error => { + this._logger.error(error); + }); + } }); this.menu.addMenuItem(authorItem); @@ -99,8 +102,11 @@ const HistoryElement = GObject.registerClass({ this.historyEntry.source.sourceUrl !== null) { const sourceItem = new PopupMenu.PopupMenuItem(`Image From: ${this.historyEntry.source.source}`); sourceItem.connect('activate', () => { - if (this.historyEntry.source.sourceUrl) - Utils.execCheck(['xdg-open', this.historyEntry.source.sourceUrl]).catch(this._logger.error); + if (this.historyEntry.source.sourceUrl) { + Utils.execCheck(['xdg-open', this.historyEntry.source.sourceUrl]).catch(error => { + this._logger.error(error); + }); + } }); this.menu.addMenuItem(sourceItem); @@ -108,8 +114,11 @@ const HistoryElement = GObject.registerClass({ const imageUrlItem = new PopupMenu.PopupMenuItem('Open Image In Browser'); imageUrlItem.connect('activate', () => { - if (this.historyEntry.source.imageLinkUrl) - Utils.execCheck(['xdg-open', this.historyEntry.source.imageLinkUrl]).catch(this._logger.error); + if (this.historyEntry.source.imageLinkUrl) { + Utils.execCheck(['xdg-open', this.historyEntry.source.imageLinkUrl]).catch(error => { + this._logger.error(error); + }); + } }); this.menu.addMenuItem(imageUrlItem); @@ -132,7 +141,9 @@ const HistoryElement = GObject.registerClass({ const copyToFavorites = new PopupMenu.PopupMenuItem('Save For Later'); copyToFavorites.connect('activate', () => { - this._saveImage().catch(this._logger.error); + this._saveImage().catch(error => { + this._logger.error(error); + }); }); this.menu.addMenuItem(copyToFavorites); @@ -222,14 +233,18 @@ const HistoryElement = GObject.registerClass({ // This function was rewritten by Gio._promisify // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/await-thenable if (!await sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null)) throw new Error('Failed copying image.'); // https://gjs.guide/guides/gio/file-operations.html#writing-file-contents // This function was rewritten by Gio._promisify // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/await-thenable const [success, message]: [boolean, string] = await targetInfoFile.replace_contents_bytes_async( - // @ts-expect-error Don't know from where to import + // FIXME: Don't know from where to import + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call new TextEncoder().encode(JSON.stringify(this.historyEntry.source, null, '\t')), null, false, @@ -324,7 +339,9 @@ class StatusElement { } startLoading() { - // @ts-expect-error Don't know where this is defined + // FIXME: Don't know where this is defined + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-call this.icon.ease({ opacity: 20, duration: 1337, diff --git a/src/jsonPath.ts b/src/jsonPath.ts index 648a122d..4d7f0bb4 100644 --- a/src/jsonPath.ts +++ b/src/jsonPath.ts @@ -44,7 +44,7 @@ function getTarget(inputObject: unknown, inputString: string): [object: unknown, switch (indexString) { case '@random': { - const [chosenElement, chosenNumber] = _randomElement(targetObject); + const [chosenElement, chosenNumber] = _randomElement(targetObject); const [object, path] = getTarget(chosenElement, inputStringTail); return [object, inputString.slice(0, inputString.length - inputStringTail.length).replace('@random', String(chosenNumber)) + path]; } diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 55fcb3d6..447fc2d9 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -70,7 +70,7 @@ class HydraPaper implements WallpaperManager { this._cancellable = new Gio.Cancellable(); // hydrapaper [--darkmode] --cli PATH PATH PATH - this._logger.debug(`Running command: ${command}`); + this._logger.debug(`Running command: ${command.toString()}`); await Utils.execCheck(command, this._cancellable); this._cancellable = null; diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts index 529c0953..2dd79493 100644 --- a/src/manager/superPaper.ts +++ b/src/manager/superPaper.ts @@ -80,7 +80,7 @@ class Superpaper implements WallpaperManager { this._cancellable = new Gio.Cancellable(); - this._logger.debug(`Running command: "${command}"`); + this._logger.debug(`Running command: ${command.toString()}`); await Utils.execCheck(command, this._cancellable); this._cancellable = null; diff --git a/src/prefs.ts b/src/prefs.ts index fc273cbf..b0071106 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -129,11 +129,11 @@ class RandomWallpaperSettings { this._sources.forEach(id => { const sourceRow = new SourceRow(undefined, id); - (this._builder.get_object('sources_list') as Adw.PreferencesGroup).add(sourceRow); + this._builder.get_object('sources_list').add(sourceRow); sourceRow.button_delete.connect('clicked', () => { sourceRow.clearConfig(); - (this._builder.get_object('sources_list') as Adw.PreferencesGroup).remove(sourceRow); + this._builder.get_object('sources_list').remove(sourceRow); Utils.removeItemOnce(this._sources, id); this._saveSources(); }); @@ -141,11 +141,15 @@ class RandomWallpaperSettings { import('./manager/wallpaperManager.js').then(module => { if (module.getWallpaperManager()?.isAvailable()) - (this._builder.get_object('multiple_displays_row') as Adw.ActionRow).set_sensitive(true); - }).catch(this._logger.error); + this._builder.get_object('multiple_displays_row').set_sensitive(true); + }).catch(error => { + this._logger.error(error); + }); }).catch(error => { - logError(error); - throw error; + if (error instanceof Error) + logError(error); + else + logError(new Error('Unknown error')); }); } @@ -192,7 +196,7 @@ class RandomWallpaperSettings { this._backendConnection.setBoolean('request-new-wallpaper', true); }); - const sourceRowList = this._builder.get_object('sources_list') as Adw.PreferencesGroup; + const sourceRowList = this._builder.get_object('sources_list'); this._builder.get_object('button_new_source').connect('clicked', () => { const sourceRow = new SourceRow(); sourceRowList.add(sourceRow); @@ -209,7 +213,7 @@ class RandomWallpaperSettings { } private _bindHistorySection(window: Adw.PreferencesWindow) { - const entryRow = this._builder.get_object('row_favorites_folder') as Adw.EntryRow; + const entryRow = this._builder.get_object('row_favorites_folder'); entryRow.text = this._settings.getString('favorites-folder'); this._settings.bind('history-length', @@ -263,10 +267,10 @@ class RandomWallpaperSettings { this._sources = this._settings.getStrv('sources'); // this._sources.sort((a, b) => { - // let path1 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${a}/`; - // let settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); - // let path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; - // let settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); + // const path1 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${a}/`; + // const settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); + // const path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; + // const settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); // const nameA = settingsGeneral1.get('name', 'string').toUpperCase(); // const nameB = settingsGeneral2.get('name', 'string').toUpperCase(); @@ -275,10 +279,10 @@ class RandomWallpaperSettings { // }); this._sources.sort((a, b) => { - let path1 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${a}/`; - let settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); - let path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; - let settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); + const path1 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${a}/`; + const settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); + const path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; + const settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); return settingsGeneral1.getEnum('type') - settingsGeneral2.getEnum('type'); }); } diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 47efa54f..7235e4ab 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -113,11 +113,12 @@ class RandomWallpaperMenu { // Open Wallpaper Folder openFolder.connect('activate', () => { const uri = GLib.filename_to_uri(this._wallpaperController.wallpaperLocation, ''); - Utils.execCheck(['xdg-open', uri]).catch(this._logger.error); + Utils.execCheck(['xdg-open', uri]).catch(error => { + this._logger.error(error); + }); }); openSettings.connect('activate', () => { - // FIXME: Unhandled promise rejection. To suppress this warning, add an error handler to your promise chain with .catch() or a try-catch block around your await expression. Gio.DBus.session.call( 'org.gnome.Shell.Extensions', '/org/gnome/Shell/Extensions', @@ -127,7 +128,9 @@ class RandomWallpaperMenu { null, Gio.DBusCallFlags.NONE, -1, - null); + null).catch(error => { + this._logger.error(error); + }); }); this._panelMenu.menu.connect('open-state-changed', (_, open) => { diff --git a/src/settings.ts b/src/settings.ts index 9288143c..23dc3511 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -88,7 +88,7 @@ class Settings { if (this._settings.set_boolean(key, value)) this._save(); else - throw new Error(`Could not set ${key} (type: boolean) with the value ${value}`); + throw new Error(`Could not set ${key} (type: boolean) with the value ${String(value)}`); } setEnum(key: string, value: number) { @@ -116,7 +116,7 @@ class Settings { if (this._settings.set_strv(key, value)) this._save(); else - throw new Error(`Could not set ${key} (type: string[]) with the value ${value}`); + throw new Error(`Could not set ${key} (type: string[]) with the value "${value.toString()}"`); } private _save() { diff --git a/src/soupBowl.ts b/src/soupBowl.ts index 8e4425dc..2994baee 100644 --- a/src/soupBowl.ts +++ b/src/soupBowl.ts @@ -4,6 +4,7 @@ * libSoup is accessed through the SoupBowl wrapper to support libSoup3 and libSoup2.4 simultaneously in the extension * runtime and in the preferences window. */ +import * as GLib from 'gi://GLib'; import * as Soup from 'gi://Soup'; import {Logger} from './logger.js'; @@ -27,9 +28,10 @@ class SoupBowl { return Soup.Message.new('GET', uri); } + // Possibly wrong version here causing ignores to type checks private _send_and_receive_soup24(soupMessage: Soup.Message): Promise { return new Promise((resolve, reject) => { - // @ts-ignore Possibly wrong version here + // @ts-ignore this._session.queue_message(soupMessage, (session, msg) => { if (!msg.response_body) { reject(new Error('Message has no response body')); @@ -42,19 +44,26 @@ class SoupBowl { }); } + // Possibly wrong version here causing ignores to type checks private _send_and_receive_soup30(soupMessage: Soup.Message): Promise { return new Promise((resolve, reject) => { - // @ts-ignore Possibly wrong version here + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-call this._session.send_and_read_async(soupMessage, 0, null, (session: Soup.Session, message: Soup.Message) => { - // @ts-ignore Possibly wrong version here - const res_data = session.send_and_read_finish(message); + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + const res_data = session.send_and_read_finish(message) as GLib.Bytes | null; if (!res_data) { reject(new Error('Message has no response body')); return; } const response_body_bytes = res_data.get_data(); - resolve(response_body_bytes); + + if (response_body_bytes) + resolve(response_body_bytes); + else + reject(new Error('Empty response')); }); }); } diff --git a/src/timer.ts b/src/timer.ts index 2ced7100..8ac3723b 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -44,7 +44,10 @@ class AFTimer { this._logger.debug('Continuing timer'); this._paused = false; - this.start(); + + // We don't care about awaiting. This should start immediately and + // run continuously in the background. + void this.start(); } isActive() { @@ -130,8 +133,12 @@ class AFTimer { if (this._timeoutEndCallback) { this._timeoutEndCallback().then(() => { this._reset(); - this.start().catch(this._logger.error); - }).catch(this._logger.error).finally(() => { + this.start().catch(error => { + this._logger.error(error); + }); + }).catch(error => { + this._logger.error(error); + }).finally(() => { return GLib.SOURCE_REMOVE; }); } diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 6460c691..6db4924a 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -120,7 +120,7 @@ const SourceRow = GObject.registerClass({ } private _fillRow(type: number) { - let targetWidget = this._getSettingsGroup(type); + const targetWidget = this._getSettingsGroup(type); if (targetWidget !== null) this._settings_container.set_child(targetWidget); } diff --git a/src/utils.ts b/src/utils.ts index f7bd2e6c..f6df5a3a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -132,6 +132,7 @@ function getMonitorCount(): number { // FIXME: wrong version in definition // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-call return defaultDisplay.get_n_monitors() as number; } diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 24cc77ae..5f164839 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -53,11 +53,17 @@ class WallpaperController { constructor() { let xdg_cache_home = GLib.getenv('XDG_CACHE_HOME'); - if (!xdg_cache_home) - xdg_cache_home = `${GLib.getenv('HOME')}/.cache`; + if (!xdg_cache_home) { + const home = GLib.getenv('HOME'); + + if (home) + xdg_cache_home = `${home}/.cache`; + else + xdg_cache_home = '/tmp'; + } this.wallpaperLocation = `${xdg_cache_home}/${Self.metadata['uuid']}/wallpapers/`; - let mode = 0o0755; + const mode = 0o0755; GLib.mkdir_with_parents(this.wallpaperLocation, mode); this._historyController = new HistoryModule.HistoryController(this.wallpaperLocation); @@ -72,7 +78,9 @@ class WallpaperController { this._backendConnection.observe('clear-history', () => this._clearHistory()); this._backendConnection.observe('open-folder', () => this._openFolder()); this._backendConnection.observe('pause-timer', () => this._pauseTimer()); - this._backendConnection.observe('request-new-wallpaper', () => this._requestNewWallpaper().catch(this._logger.error)); + this._backendConnection.observe('request-new-wallpaper', () => this._requestNewWallpaper().catch(error => { + this._logger.error(error); + })); this._settings.observe('history-length', () => this._updateHistory()); this._settings.observe('auto-fetch', () => this._updateAutoFetching()); @@ -83,8 +91,11 @@ class WallpaperController { this._updateAutoFetching(); // load a new wallpaper on startup - if (this._settings.getBoolean('fetch-on-startup')) - this.fetchNewWallpaper().catch(this._logger.error); + if (this._settings.getBoolean('fetch-on-startup')) { + this.fetchNewWallpaper().catch(error => { + this._logger.error(error); + }); + } // Initialize favorites folder // TODO: There's probably a better place for this @@ -125,7 +136,7 @@ class WallpaperController { private _openFolder() { if (this._backendConnection.getBoolean('open-folder')) { - let uri = GLib.filename_to_uri(this.wallpaperLocation, ''); + const uri = GLib.filename_to_uri(this.wallpaperLocation, ''); Gio.AppInfo.launch_default_for_uri(uri, Gio.AppLaunchContext.new()); this._backendConnection.setBoolean('open-folder', false); } @@ -180,7 +191,9 @@ class WallpaperController { return this.fetchNewWallpaper(); }); this._timer.setMinutes(this._autoFetch.duration); - this._timer.start().catch(this._logger.error); + this._timer.start().catch(error => { + this._logger.error(error); + }); } else { this._timer.stop(); } @@ -383,7 +396,9 @@ class WallpaperController { const generalPostCommandArray = this._getCommandArray(commandString, currentWallpaperPath); if (generalPostCommandArray !== null) { // Do not await this call, let it be one shot - Utils.execCheck(generalPostCommandArray).catch(this._logger.error); + Utils.execCheck(generalPostCommandArray).catch(error => { + this._logger.error(error); + }); } } @@ -511,7 +526,7 @@ class WallpaperController { return null; // Replace variables - const variables = new Map(); + const variables = new Map(); variables.set('%wallpaper_path%', historyElementPath); variables.forEach((value, key) => { @@ -560,10 +575,14 @@ class WallpaperController { // Only change the background - the lock screen wouldn't be visible anyway // because this function is only used for hover preview if (this._resetWallpaper) { - this._setBackground(paths, 0).catch(this._logger.error); + this._setBackground(paths, 0).catch(error => { + this._logger.error(error); + }); this._resetWallpaper = false; } else if (this._previewId !== undefined) { - this._setBackground(paths, 0).catch(this._logger.error); + this._setBackground(paths, 0).catch(error => { + this._logger.error(error); + }); } return GLib.SOURCE_REMOVE; From 02de30578472e90c24f74c1c3710e37809d025ee Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 3 Mar 2023 00:24:06 +0100 Subject: [PATCH 31/87] Allow reverting whole schema at once --- src/settings.ts | 5 +++++ src/ui/genericJson.ts | 10 +--------- src/ui/localFolder.ts | 2 +- src/ui/reddit.ts | 7 +------ src/ui/sourceRow.ts | 5 +---- src/ui/unsplash.ts | 7 +------ src/ui/urlSource.ts | 6 +----- src/ui/wallhaven.ts | 11 +---------- 8 files changed, 12 insertions(+), 41 deletions(-) diff --git a/src/settings.ts b/src/settings.ts index 23dc3511..66167c56 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -84,6 +84,11 @@ class Settings { this._settings.reset(keyName); } + resetSchema() { + for (const key of this._settings.settings_schema.list_keys()) + this.reset(key); + } + setBoolean(key: string, value: boolean) { if (this._settings.set_boolean(key, value)) this._save(); diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index e5ee5f6c..a521f57d 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -82,15 +82,7 @@ const GenericJsonSettingsGroup = GObject.registerClass({ } clearConfig() { - this._settings.reset('author-name-path'); - this._settings.reset('author-url-path'); - this._settings.reset('author-url-prefix'); - this._settings.reset('domain'); - this._settings.reset('image-path'); - this._settings.reset('image-prefix'); - this._settings.reset('post-path'); - this._settings.reset('post-prefix'); - this._settings.reset('request-url'); + this._settings.resetSchema(); } }); diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index fcf25721..935ffe9c 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -65,7 +65,7 @@ const LocalFolderSettingsGroup = GObject.registerClass({ } clearConfig() { - this._settings.reset('folder'); + this._settings.resetSchema(); } }); diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index cfdd28fe..4349cd83 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -65,12 +65,7 @@ const RedditSettingsGroup = GObject.registerClass({ } clearConfig() { - this._settings.reset('allow-sfw'); - this._settings.reset('image-ratio1'); - this._settings.reset('image-ratio2'); - this._settings.reset('min-height'); - this._settings.reset('min-width'); - this._settings.reset('subreddits'); + this._settings.resetSchema(); } }); diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 6db4924a..5b278f11 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -174,10 +174,7 @@ const SourceRow = GObject.registerClass({ widget.clearConfig(); } - this._settings.reset('blocked-images'); - this._settings.reset('enabled'); - this._settings.reset('name'); - this._settings.reset('type'); + this._settings.resetSchema(); } }); diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index 53a4010f..73130344 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -100,12 +100,7 @@ const UnsplashSettingsGroup = GObject.registerClass({ } clearConfig() { - this._settings.reset('constraint-type'); - this._settings.reset('constraint-value'); - this._settings.reset('featured-only'); - this._settings.reset('image-height'); - this._settings.reset('image-width'); - this._settings.reset('keyword'); + this._settings.resetSchema(); } }); diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index fb8ea5e3..c0be4120 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -58,11 +58,7 @@ const UrlSourceSettingsGroup = GObject.registerClass({ } clearConfig() { - this._settings.reset('author-name'); - this._settings.reset('author-url'); - this._settings.reset('domain'); - this._settings.reset('image-url'); - this._settings.reset('post-url'); + this._settings.resetSchema(); } }); diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index 130d5930..0f33d1c3 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -154,16 +154,7 @@ const WallhavenSettingsGroup = GObject.registerClass({ } clearConfig() { - this._settings.reset('allow-nsfw'); - this._settings.reset('allow-sfw'); - this._settings.reset('allow-sketchy'); - this._settings.reset('api-key'); - this._settings.reset('category-anime'); - this._settings.reset('category-general'); - this._settings.reset('category-people'); - this._settings.reset('color'); - this._settings.reset('keyword'); - this._settings.reset('resolutions'); + this._settings.resetSchema(); } }); From 5c7a337f805b913fe2856b8a3389f7a4467413db Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 3 Mar 2023 17:05:56 +0100 Subject: [PATCH 32/87] Move enum into code Having the enum in the schema makes it hard to iterate, switch or compare while the only benefit is a handy string representation which can't be translated --- src/historyMenuElements.ts | 2 +- src/prefs.ts | 2 +- ...ns.space.iflow.randomwallpaper.gschema.xml | 13 +------ src/settings.ts | 7 ++++ src/ui/sourceRow.ts | 38 ++++++++---------- src/utils.ts | 39 +++++++++++++++++++ src/wallpaperController.ts | 16 ++++---- 7 files changed, 75 insertions(+), 42 deletions(-) diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index a995f09b..70f35994 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -148,7 +148,7 @@ const HistoryElement = GObject.registerClass({ this.menu.addMenuItem(copyToFavorites); // Static URLs can't block images (yet?) - if (this.historyEntry.adapter?.type !== 5) { + if (this.historyEntry.adapter?.type !== Utils.SourceType.STATIC_URL) { const blockImage = new PopupMenu.PopupMenuItem('Add To Blocklist'); blockImage.connect('activate', () => { this._addToBlocklist(this.historyEntry); diff --git a/src/prefs.ts b/src/prefs.ts index b0071106..dda6fcbd 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -283,7 +283,7 @@ class RandomWallpaperSettings { const settingsGeneral1 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path1); const path2 = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${b}/`; const settingsGeneral2 = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path2); - return settingsGeneral1.getEnum('type') - settingsGeneral2.getEnum('type'); + return settingsGeneral1.getInt('type') - settingsGeneral2.getInt('type'); }); } diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 68cadd3d..76134fc4 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -143,15 +143,6 @@ - - - - - - - - - @@ -172,8 +163,8 @@ Name for this source. - - "Unsplash" + + 0 Type The type of this source. diff --git a/src/settings.ts b/src/settings.ts index 66167c56..035725b0 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -103,6 +103,13 @@ class Settings { throw new Error(`Could not set ${key} (type: number) with the value ${value}`); } + setInt(key: string, value: number) { + if (this._settings.set_int(key, value)) + this._save(); + else + throw new Error(`Could not set ${key} (type: number) with the value ${value}`); + } + setInt64(key: string, value: number) { if (this._settings.set_int64(key, value)) this._save(); diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 5b278f11..386a2a09 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -61,19 +61,21 @@ const SourceRow = GObject.registerClass({ this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); if (!SourceRow._stringList) { - // Fill combo from settings enum + const availableTypeNames: string[] = []; - const availableTypes = this._settings.getSchema().get_key('type').get_range(); // GLib.Variant (sv) - // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) - // s should be 'enum' - // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. - // v is 'as' - const availableTypeNames = availableTypes.get_child_value(1).get_variant().get_strv(); + // Fill combo from enum + // https://stackoverflow.com/a/39372911 + for (const type in Utils.SourceType) { + if (isNaN(Number(type))) + continue; + + availableTypeNames.push(Utils.getSourceTypeName(Number(type))); + } SourceRow._stringList = Gtk.StringList.new(availableTypeNames); } this._combo.model = SourceRow._stringList; - this._combo.selected = this._settings.getEnum('type'); + this._combo.selected = this._settings.getInt('type'); this._settings.bind('name', this._source_name, @@ -83,15 +85,9 @@ const SourceRow = GObject.registerClass({ this, 'enable-expansion', Gio.SettingsBindFlags.DEFAULT); - // Binding an enum isn't possible straight away. - // This would need bind_with_mapping() which isn't available in gjs? - // this._settings.bind('type', - // this._combo, - // 'selected', - // Gio.SettingsBindFlags.DEFAULT); this._combo.connect('notify::selected', (comboRow: Adw.ComboRow) => { - this._settings.setEnum('type', comboRow.selected); + this._settings.setInt('type', comboRow.selected); this._fillRow(comboRow.selected); }); @@ -128,22 +124,22 @@ const SourceRow = GObject.registerClass({ private _getSettingsGroup(type = 0) { let targetWidget = null; switch (type) { - case 0: // unsplash + case Utils.SourceType.UNSPLASH: targetWidget = new UnsplashSettingsGroup(undefined, this.id); break; - case 1: // wallhaven + case Utils.SourceType.WALLHAVEN: targetWidget = new WallhavenSettingsGroup(undefined, this.id); break; - case 2: // reddit + case Utils.SourceType.REDDIT: targetWidget = new RedditSettingsGroup(undefined, this.id); break; - case 3: // generic JSON + case Utils.SourceType.GENERIC_JSON: targetWidget = new GenericJsonSettingsGroup(undefined, this.id); break; - case 4: // Local Folder + case Utils.SourceType.LOCAL_FOLDER: targetWidget = new LocalFolderSettingsGroup(undefined, this.id); break; - case 5: // Static URL + case Utils.SourceType.STATIC_URL: targetWidget = new UrlSourceSettingsGroup(undefined, this.id); break; default: diff --git a/src/utils.ts b/src/utils.ts index f6df5a3a..af9c2d21 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -7,6 +7,43 @@ import * as Adw from '@gi/gtk4/adw/adw'; import {Settings} from './settings.js'; +// Generated code produces a no-shadow rule error: +// 'SourceType' is already declared in the upper scope on line 7 column 5 no-shadow +/* eslint-disable */ +enum SourceType { + UNSPLASH = 0, + WALLHAVEN, + REDDIT, + GENERIC_JSON, + LOCAL_FOLDER, + STATIC_URL, +} +/* eslint-enable */ + +/** + * + * @param {SourceType} value The enum value to request + */ +function getSourceTypeName(value: SourceType): string { + switch (value) { + case SourceType.UNSPLASH: + return 'Unsplash'; + case SourceType.WALLHAVEN: + return 'Wallhaven'; + case SourceType.REDDIT: + return 'Reddit'; + case SourceType.GENERIC_JSON: + return 'Generic JSON'; + case SourceType.LOCAL_FOLDER: + return 'Local Folder'; + case SourceType.STATIC_URL: + return 'Static URL'; + + default: + return 'Unsplash'; + } +} + /** * Returns a promise which resolves cleanly or rejects according to the underlying subprocess. * @@ -203,6 +240,8 @@ function setPictureUriOfSettingsObject(settings: Settings, uri: string) { } export { + SourceType, + getSourceTypeName, execCheck, fileName, fillComboRowFromEnum, diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 5f164839..469f94c4 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -242,7 +242,7 @@ class WallpaperController { let sourceType = -1; sourceName = settingsGeneral.getString('name'); - sourceType = settingsGeneral.getEnum('type'); + sourceType = settingsGeneral.getInt('type'); const availableAdapter = _arrayIncludes(randomAdapterResult, sourceType); if (availableAdapter) { @@ -252,22 +252,22 @@ class WallpaperController { try { switch (sourceType) { - case 0: + case Utils.SourceType.UNSPLASH: imageSourceAdapter = new UnsplashAdapter(sourceID, sourceName); break; - case 1: + case Utils.SourceType.WALLHAVEN: imageSourceAdapter = new WallhavenAdapter(sourceID, sourceName); break; - case 2: + case Utils.SourceType.REDDIT: imageSourceAdapter = new RedditAdapter(sourceID, sourceName); break; - case 3: + case Utils.SourceType.GENERIC_JSON: imageSourceAdapter = new GenericJsonAdapter(sourceID, sourceName); break; - case 4: + case Utils.SourceType.LOCAL_FOLDER: imageSourceAdapter = new LocalFolderAdapter(sourceID, sourceName); break; - case 5: + case Utils.SourceType.STATIC_URL: imageSourceAdapter = new UrlSourceAdapter(sourceID, sourceName); break; default: @@ -278,7 +278,7 @@ class WallpaperController { } catch (error) { this._logger.warn('Had errors, fetching with default settings.'); imageSourceAdapter = new UnsplashAdapter(null, null); - sourceType = 0; + sourceType = Utils.SourceType.UNSPLASH; } randomAdapterResult.push({ From 1496a5a179ab62a17bf74b454f8ddfee46223503 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 3 Mar 2023 14:38:16 +0100 Subject: [PATCH 33/87] Remove obsolete comment --- src/adapter/wallhaven.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index d16b4d70..f89546f4 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -137,7 +137,7 @@ class WallhavenAdapter extends BaseAdapter { }); const categories = []; - categories.push(Number(this._settings.getBoolean('category-general'))); // + is implicit conversion to int + categories.push(Number(this._settings.getBoolean('category-general'))); categories.push(Number(this._settings.getBoolean('category-anime'))); categories.push(Number(this._settings.getBoolean('category-people'))); this._options.categories = categories.join(''); From e0d8b8c3543ac7293e569b218bfdd49369dc0a3f Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 3 Mar 2023 14:46:55 +0100 Subject: [PATCH 34/87] Change max retry handling --- src/adapter/genericJson.ts | 10 ++++++++-- src/adapter/reddit.ts | 2 +- src/adapter/unsplash.ts | 5 ++++- src/adapter/wallhaven.ts | 5 ++--- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 5765a8df..9cd94776 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -7,6 +7,12 @@ import * as Utils from './../utils.js'; import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; +/** How many times the service should be queried at maximum. */ +const MAX_SERVICE_RETRIES = 5; +/** How many times we should try to get a new image from an array. + * No new request are being made. */ +const MAX_ARRAY_RETRIES = 5; + class GenericJsonAdapter extends BaseAdapter { constructor(id: string, name: string) { super({ @@ -39,7 +45,7 @@ class GenericJsonAdapter extends BaseAdapter { const authorNameJSONPath = this._settings.getString('author-name-path'); const authorUrlJSONPath = this._settings.getString('author-url-path'); - for (let i = 0; i < 5 && wallpaperResult.length < count; i++) { + for (let i = 0; i < MAX_ARRAY_RETRIES && wallpaperResult.length < count; i++) { const [returnObject, resolvedPath] = JSONPath.getTarget(response_body, imageJSONPath); if (!returnObject || (typeof returnObject !== 'string' && typeof returnObject !== 'number') || returnObject === '') throw new Error('Unexpected json member found'); @@ -102,7 +108,7 @@ class GenericJsonAdapter extends BaseAdapter { async requestRandomImage(count: number) { const wallpaperResult: HistoryEntry[] = []; - for (let i = 0; i < 5 && wallpaperResult.length < count; i++) { + for (let i = 0; i < MAX_SERVICE_RETRIES && wallpaperResult.length < count; i++) { try { // This should run sequentially // eslint-disable-next-line no-await-in-loop diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index 24133b64..693b9d6c 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -82,7 +82,7 @@ class RedditAdapter extends BaseAdapter { if (filteredSubmissions.length === 0) throw new Error('No suitable submissions found!'); - for (let i = 0; i < 20 && wallpaperResult.length < count; i++) { + for (let i = 0; i < filteredSubmissions.length && wallpaperResult.length < count; i++) { const random = Utils.getRandomNumber(filteredSubmissions.length); const submission = filteredSubmissions[random].data; const imageDownloadUrl = this._ampDecode(submission.preview.images[0].source.url); diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 78e188b4..87f5b746 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -4,6 +4,9 @@ import * as Utils from './../utils.js'; import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; +/** How many times the service should be queried at maximum. */ +const MAX_SERVICE_RETRIES = 5; + class UnsplashAdapter extends BaseAdapter { private _sourceUrl = 'https://source.unsplash.com'; @@ -67,7 +70,7 @@ class UnsplashAdapter extends BaseAdapter { async requestRandomImage(count: number) { const wallpaperResult: HistoryEntry[] = []; - for (let i = 0; i < 5 && wallpaperResult.length < count; i++) { + for (let i = 0; i < MAX_SERVICE_RETRIES && wallpaperResult.length < count; i++) { try { // This should run sequentially // eslint-disable-next-line no-await-in-loop diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index f89546f4..22528ce2 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -69,9 +69,8 @@ class WallhavenAdapter extends BaseAdapter { if (!response || response.length === 0) throw new Error('Empty response'); - for (let i = 0; i < 10 && wallpaperResult.length < count; i++) { - // get a random entry from the array - const entry = response[Utils.getRandomNumber(response.length)]; + for (let i = 0; i < response.length && wallpaperResult.length < count; i++) { + const entry = response[i]; const siteURL = entry.url; let downloadURL = entry.path; From 5fca47963e3603312a470db67c003e035d1b8f6e Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 31 Mar 2023 03:13:45 +0200 Subject: [PATCH 35/87] Refactor image dimensions descriptions. --- ...xtensions.space.iflow.randomwallpaper.gschema.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 76134fc4..ac736845 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -310,26 +310,26 @@ 16 - Image Ratio - The minimal ratio 1. + Image width ratio + The minimal width ratio part. 10 - Image Width - The minimal ratio 2. + Image height ratio + The minimal height ratio part. 1080 Image Height - The minimal height of fetched image. + The minimal height of the fetched image. 1920 Image Width - The minimal width of fetched image. + The minimal width of the fetched image. From 9e1cd788d409c00afbd8f5a38c2817f92159f6ad Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 9 May 2023 19:07:00 +0200 Subject: [PATCH 36/87] Always try at least for number of displays https://github.com/ifl0w/RandomWallpaperGnome3/pull/154#discussion_r1186678302 --- src/adapter/genericJson.ts | 4 ++-- src/adapter/unsplash.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 9cd94776..35fd41b4 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -45,7 +45,7 @@ class GenericJsonAdapter extends BaseAdapter { const authorNameJSONPath = this._settings.getString('author-name-path'); const authorUrlJSONPath = this._settings.getString('author-url-path'); - for (let i = 0; i < MAX_ARRAY_RETRIES && wallpaperResult.length < count; i++) { + for (let i = 0; i < MAX_ARRAY_RETRIES + count && wallpaperResult.length < count; i++) { const [returnObject, resolvedPath] = JSONPath.getTarget(response_body, imageJSONPath); if (!returnObject || (typeof returnObject !== 'string' && typeof returnObject !== 'number') || returnObject === '') throw new Error('Unexpected json member found'); @@ -108,7 +108,7 @@ class GenericJsonAdapter extends BaseAdapter { async requestRandomImage(count: number) { const wallpaperResult: HistoryEntry[] = []; - for (let i = 0; i < MAX_SERVICE_RETRIES && wallpaperResult.length < count; i++) { + for (let i = 0; i < MAX_SERVICE_RETRIES + count && wallpaperResult.length < count; i++) { try { // This should run sequentially // eslint-disable-next-line no-await-in-loop diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 87f5b746..fa66854a 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -70,7 +70,7 @@ class UnsplashAdapter extends BaseAdapter { async requestRandomImage(count: number) { const wallpaperResult: HistoryEntry[] = []; - for (let i = 0; i < MAX_SERVICE_RETRIES && wallpaperResult.length < count; i++) { + for (let i = 0; i < MAX_SERVICE_RETRIES + count && wallpaperResult.length < count; i++) { try { // This should run sequentially // eslint-disable-next-line no-await-in-loop From 81ef3558f4ee95d82df1fe6a79bd2d9b0659cc7c Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 9 May 2023 19:12:13 +0200 Subject: [PATCH 37/87] Use regex for file path manipulation --- src/wallpaperController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 469f94c4..e594d68d 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -390,7 +390,7 @@ class WallpaperController { // Read the current wallpaper uri from settings because it could be a merged wallpaper // Remove prefix "file://" to get the real path - const currentWallpaperPath = backgroundSettings.getString('picture-uri').slice(7); + const currentWallpaperPath = backgroundSettings.getString('picture-uri').replace(/^file:\/\//, ''); // TODO: this ignores the lock-screen const generalPostCommandArray = this._getCommandArray(commandString, currentWallpaperPath); From 604899d92c8841745c024c03b531c7b90caf334e Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 9 May 2023 19:41:43 +0200 Subject: [PATCH 38/87] Add link to source and describe additional changes --- .eslintrc-gjs.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.eslintrc-gjs.yml b/.eslintrc-gjs.yml index c1677c93..ef0396b1 100644 --- a/.eslintrc-gjs.yml +++ b/.eslintrc-gjs.yml @@ -1,9 +1,16 @@ +# Try our best to adhere to the guidelines: +# https://gjs.guide/extensions/review-guidelines/review-guidelines.html#code-must-not-be-obfuscated + +# Contains some additional styling rules to automatically fix generated javascript: +# * At the 'padding-line-between-statements:' rule: +# https://eslint.org/docs/latest/rules/padding-line-between-statements +# * 'sourceType: "module"' as additional 'parserOptions:' + +# Official GJS styling: https://gitlab.gnome.org/GNOME/gjs/-/blob/fb11886617fe6b332427cdb5b23b740fc4193d2c/.eslintrc.yml --- # SPDX-License-Identifier: MIT OR LGPL-2.0-or-later # SPDX-FileCopyrightText: 2018 Claudio André -# Contains some additional styling rules to autofix generated javascript -# https://eslint.org/docs/latest/rules/padding-line-between-statements env: es2021: true extends: 'eslint:recommended' From efabd110b8a0e663981f70f36a76d5645d84b061 Mon Sep 17 00:00:00 2001 From: Lucki Date: Tue, 9 May 2023 20:24:19 +0200 Subject: [PATCH 39/87] Warning if less than requested images are found --- src/adapter/genericJson.ts | 3 +++ src/adapter/localFolder.ts | 3 +++ src/adapter/reddit.ts | 3 +++ src/adapter/unsplash.ts | 3 +++ src/adapter/wallhaven.ts | 3 +++ 5 files changed, 15 insertions(+) diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 35fd41b4..1393e0b0 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -132,6 +132,9 @@ class GenericJsonAdapter extends BaseAdapter { if (wallpaperResult.length === 0) throw new Error('Only blocked images found.'); + if (wallpaperResult.length < count) + this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + return wallpaperResult; } } diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 401c5840..6357c06e 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -53,6 +53,9 @@ class LocalFolderAdapter extends BaseAdapter { return; } + if (wallpaperResult.length < count) + this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + resolve(wallpaperResult); }); } diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index 693b9d6c..2b1517df 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -101,6 +101,9 @@ class RedditAdapter extends BaseAdapter { if (wallpaperResult.length === 0) throw new Error('Only blocked images found.'); + if (wallpaperResult.length < count) + this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + return wallpaperResult; } diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index fa66854a..3fd34598 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -90,6 +90,9 @@ class UnsplashAdapter extends BaseAdapter { if (wallpaperResult.length === 0) throw new Error('Only blocked images found.'); + if (wallpaperResult.length < count) + this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + return wallpaperResult; } diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index 22528ce2..3f6fdbad 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -92,6 +92,9 @@ class WallhavenAdapter extends BaseAdapter { if (wallpaperResult.length === 0) throw new Error('Only blocked images found.'); + if (wallpaperResult.length < count) + this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + return wallpaperResult; } From 44a6a76364b422e9b39d2119abe8dc964ddff73f Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 10 May 2023 03:45:03 +0200 Subject: [PATCH 40/87] Throw if not all requested images can be resolved. --- src/adapter/baseAdapter.ts | 1 + src/adapter/genericJson.ts | 56 ++++++++++++++++++++++---------------- src/adapter/localFolder.ts | 11 ++++---- src/adapter/reddit.ts | 21 ++++++++------ src/adapter/unsplash.ts | 15 +++++----- src/adapter/urlSource.ts | 6 ++-- src/adapter/wallhaven.ts | 21 ++++++++------ src/wallpaperController.ts | 39 +++++++++++++++++++++++--- 8 files changed, 109 insertions(+), 61 deletions(-) diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 4c761c99..0a28e696 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -37,6 +37,7 @@ abstract class BaseAdapter { * Retrieves a new url for an image and crafts a new HistoryEntry. * * @param {number} count Number of requested wallpaper + * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty */ // eslint-disable-next-line no-unused-vars abstract requestRandomImage (count: number): Promise; diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 1393e0b0..9f760e7a 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -31,12 +31,16 @@ class GenericJsonAdapter extends BaseAdapter { url = encodeURI(url); const message = this._bowl.newGetMessage(url); - if (message === null) - throw new Error('Could not create request.'); + if (message === null) { + this._logger.error('Could not create request.'); + throw wallpaperResult; + } const response_body_bytes = await this._bowl.send_and_receive(message); - if (!response_body_bytes) - throw new Error('Error fetching response.'); + if (!response_body_bytes) { + this._logger.error('Error fetching response.'); + throw wallpaperResult; + } const response_body: unknown = JSON.parse(ByteArray.toString(response_body_bytes)); const imageJSONPath = this._settings.getString('image-path'); @@ -47,8 +51,10 @@ class GenericJsonAdapter extends BaseAdapter { for (let i = 0; i < MAX_ARRAY_RETRIES + count && wallpaperResult.length < count; i++) { const [returnObject, resolvedPath] = JSONPath.getTarget(response_body, imageJSONPath); - if (!returnObject || (typeof returnObject !== 'string' && typeof returnObject !== 'number') || returnObject === '') - throw new Error('Unexpected json member found'); + if (!returnObject || (typeof returnObject !== 'string' && typeof returnObject !== 'number') || returnObject === '') { + this._logger.error('Unexpected json member found'); + break; + } const imageDownloadUrl = this._settings.getString('image-prefix') + String(returnObject); const imageBlocked = this._isImageBlocked(Utils.fileName(imageDownloadUrl)); @@ -56,7 +62,7 @@ class GenericJsonAdapter extends BaseAdapter { // Don't retry without @random present in JSONPath if (imageBlocked && !imageJSONPath.includes('@random')) { // Abort and try again - return null; + break; } if (imageBlocked) @@ -99,8 +105,10 @@ class GenericJsonAdapter extends BaseAdapter { wallpaperResult.push(historyEntry); } - if (wallpaperResult.length === 0) - throw new Error('Only blocked images found.'); + if (wallpaperResult.length < count) { + this._logger.warn('Returning less images than requested.'); + throw wallpaperResult; + } return wallpaperResult; } @@ -109,31 +117,33 @@ class GenericJsonAdapter extends BaseAdapter { const wallpaperResult: HistoryEntry[] = []; for (let i = 0; i < MAX_SERVICE_RETRIES + count && wallpaperResult.length < count; i++) { + let historyArray: HistoryEntry[] = []; + try { // This should run sequentially // eslint-disable-next-line no-await-in-loop - const historyArray = await this._getHistoryEntry(count); - - if (historyArray) { - historyArray.forEach(element => { - if (!this._includesWallpaper(wallpaperResult, element.source.imageDownloadUrl)) - wallpaperResult.push(element); - }); - } + historyArray = await this._getHistoryEntry(count); } catch (error) { this._logger.warn('Failed getting image'); - this._logger.warn(error); + + if (Array.isArray(error) && error.length > 0 && error[0] instanceof HistoryEntry) + historyArray = error as HistoryEntry[]; + // Do not escalate yet, try again + } finally { + historyArray.forEach(element => { + if (!this._includesWallpaper(wallpaperResult, element.source.imageDownloadUrl)) + wallpaperResult.push(element); + }); } // Image blocked, try again } - if (wallpaperResult.length === 0) - throw new Error('Only blocked images found.'); - - if (wallpaperResult.length < count) - this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + if (wallpaperResult.length < count) { + this._logger.warn('Returning less images than requested.'); + throw wallpaperResult; + } return wallpaperResult; } diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 6357c06e..0069395d 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -28,7 +28,8 @@ class LocalFolderAdapter extends BaseAdapter { const wallpaperResult: HistoryEntry[] = []; if (files.length < 1) { - reject(new Error('No files found')); + this._logger.error('No files found'); + reject(wallpaperResult); return; } this._logger.debug(`Found ${files.length} possible wallpaper in "${this._settings.getString('folder')}"`); @@ -48,14 +49,12 @@ class LocalFolderAdapter extends BaseAdapter { wallpaperResult.push(historyEntry); } - if (wallpaperResult.length === 0) { - reject(new Error('Only blocked images found')); + if (wallpaperResult.length < count) { + this._logger.warn('Returning less images than requested.'); + reject(wallpaperResult); return; } - if (wallpaperResult.length < count) - this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); - resolve(wallpaperResult); }); } diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index 2b1517df..4a33600d 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -56,8 +56,10 @@ class RedditAdapter extends BaseAdapter { const response_body_bytes = await this._bowl.send_and_receive(message); const response_body = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; - if (!this._isRedditResponse(response_body)) - throw new Error('Unexpected response'); + if (!this._isRedditResponse(response_body)) { + this._logger.error('Unexpected response'); + throw wallpaperResult; + } const filteredSubmissions = response_body.data.children.filter(child => { if (child.data.post_hint !== 'image') @@ -79,8 +81,10 @@ class RedditAdapter extends BaseAdapter { return true; }); - if (filteredSubmissions.length === 0) - throw new Error('No suitable submissions found!'); + if (filteredSubmissions.length === 0) { + this._logger.error('No suitable submissions found!'); + throw wallpaperResult; + } for (let i = 0; i < filteredSubmissions.length && wallpaperResult.length < count; i++) { const random = Utils.getRandomNumber(filteredSubmissions.length); @@ -98,11 +102,10 @@ class RedditAdapter extends BaseAdapter { wallpaperResult.push(historyEntry); } - if (wallpaperResult.length === 0) - throw new Error('Only blocked images found.'); - - if (wallpaperResult.length < count) - this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + if (wallpaperResult.length < count) { + this._logger.warn('Returning less images than requested.'); + throw wallpaperResult; + } return wallpaperResult; } diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 3fd34598..de1dca17 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -30,7 +30,7 @@ class UnsplashAdapter extends BaseAdapter { }); } - private async _getHistoryEntry(): Promise { + private async _getHistoryEntry(): Promise { this._readOptionsFromSettings(); const optionsString = this._generateOptionsString(); @@ -57,7 +57,7 @@ class UnsplashAdapter extends BaseAdapter { if (this._isImageBlocked(Utils.fileName(imageLinkUrl))) { // Abort and try again - return null; + throw new Error('Image blocked'); } const historyEntry = new HistoryEntry(null, this._sourceName, imageLinkUrl); @@ -76,7 +76,7 @@ class UnsplashAdapter extends BaseAdapter { // eslint-disable-next-line no-await-in-loop const historyEntry = await this._getHistoryEntry(); - if (historyEntry && !this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) + if (!this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) wallpaperResult.push(historyEntry); } catch (error) { this._logger.warn('Failed getting image.'); @@ -87,11 +87,10 @@ class UnsplashAdapter extends BaseAdapter { // Image blocked, try again } - if (wallpaperResult.length === 0) - throw new Error('Only blocked images found.'); - - if (wallpaperResult.length < count) - this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + if (wallpaperResult.length < count) { + this._logger.warn('Returning less images than requested.'); + throw wallpaperResult; + } return wallpaperResult; } diff --git a/src/adapter/urlSource.ts b/src/adapter/urlSource.ts index b7448e1a..3a78d488 100644 --- a/src/adapter/urlSource.ts +++ b/src/adapter/urlSource.ts @@ -21,8 +21,10 @@ class UrlSourceAdapter extends BaseAdapter { const domainUrl = this._settings.getString('domain'); const postUrl = this._settings.getString('domain'); - if (imageDownloadUrl === '') - throw new Error('Missing download url'); + if (imageDownloadUrl === '') { + this._logger.error('Missing download url'); + throw new Array(0); + } if (authorName === '') authorName = null; diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index 3f6fdbad..a0850a28 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -62,12 +62,16 @@ class WallhavenAdapter extends BaseAdapter { const response_body_bytes = await this._bowl.send_and_receive(message); const wallhavenResponse = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; - if (!this._isWallhavenResponse(wallhavenResponse)) - throw new Error('Unexpected response'); + if (!this._isWallhavenResponse(wallhavenResponse)) { + this._logger.error('Unexpected response'); + throw wallpaperResult; + } const response = wallhavenResponse.data; - if (!response || response.length === 0) - throw new Error('Empty response'); + if (!response || response.length === 0) { + this._logger.error('Empty response'); + throw wallpaperResult; + } for (let i = 0; i < response.length && wallpaperResult.length < count; i++) { const entry = response[i]; @@ -89,11 +93,10 @@ class WallhavenAdapter extends BaseAdapter { wallpaperResult.push(historyEntry); } - if (wallpaperResult.length === 0) - throw new Error('Only blocked images found.'); - - if (wallpaperResult.length < count) - this._logger.warn('Found some blocked images after multiple retries. Returning less images than requested.'); + if (wallpaperResult.length < count) { + this._logger.warn('Returning less images than requested.'); + throw wallpaperResult; + } return wallpaperResult; } diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index e594d68d..74ef6114 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -468,10 +468,19 @@ class WallpaperController { const randomImagePromises = imageAdapters.map(element => { return element.adapter.requestRandomImage(element.imageCount); }); - const newWallpapers = await Promise.all(randomImagePromises); + const newWallpapers = await Promise.allSettled(randomImagePromises); - const fetchPromises = newWallpapers.flatMap((array, index) => { + const fetchPromises = newWallpapers.flatMap((object, index) => { const fetchPromiseArray: Promise[] = []; + let array: HistoryModule.HistoryEntry[] = []; + + // rejected promises + if ('reason' in object && Array.isArray(object.reason) && object.reason.length > 0 && object.reason[0] instanceof HistoryModule.HistoryEntry) + array = object.reason as HistoryModule.HistoryEntry[]; + + // fulfilled promises + if ('value' in object) + array = object.value; for (const element of array) { element.adapter = { @@ -486,13 +495,33 @@ class WallpaperController { return fetchPromiseArray; }); + if (fetchPromises.length < 1) + throw new Error('Unable to request new images.'); + // wait for all fetching images - const newImageEntries = await Promise.all(fetchPromises); - this._logger.info(`Requested ${newImageEntries.length} new images.`); + this._logger.info(`Requesting ${fetchPromises.length} new images.`); + const newImageEntriesPromiseResults = await Promise.allSettled(fetchPromises); + + const newImageEntries = newImageEntriesPromiseResults.map(element => { + if (element.status !== 'fulfilled' && !('value' in element)) + return null; + + return element.value; + }).filter(element => { + return element instanceof HistoryModule.HistoryEntry; + }) as HistoryModule.HistoryEntry[]; + this._logger.debug(`Fetched ${newImageEntries.length} new images.`); const newWallpaperPaths = newImageEntries.map(element => { return element.path; }); + + if (newWallpaperPaths.length < 1) + throw new Error('Unable to fetch new images.'); + + if (newWallpaperPaths.length < monitorCount) + this._logger.warn('Unable to fill all displays with new images.'); + const usedWallpaperPaths = this._fillDisplaysFromHistory(newWallpaperPaths, monitorCount); if (changeType === 3) { @@ -514,6 +543,8 @@ class WallpaperController { this._historyController.insert(newImageEntries.reverse()); this._runPostCommands(); + } catch (error) { + this._logger.error(error); } finally { this._stopLoadingHooks.forEach(element => element()); } From 5718f982e7fbe3b090cf36f2cfaaa7a5ec782d2d Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 10 May 2023 20:58:29 +0200 Subject: [PATCH 41/87] Force return types --- .eslintrc.yml | 1 + src/adapter/baseAdapter.ts | 8 +++--- src/adapter/genericJson.ts | 4 +-- src/adapter/localFolder.ts | 2 +- src/adapter/reddit.ts | 4 +-- src/adapter/unsplash.ts | 6 ++--- src/adapter/urlSource.ts | 2 +- src/adapter/wallhaven.ts | 6 ++--- src/extension.ts | 8 +++--- src/history.ts | 20 +++++++-------- src/historyMenuElements.ts | 18 +++++++------- src/logger.ts | 12 ++++----- src/manager/hydraPaper.ts | 6 ++--- src/manager/superPaper.ts | 2 +- src/prefs.ts | 14 +++++------ src/randomWallpaperMenu.ts | 18 +++++++------- src/settings.ts | 32 ++++++++++++------------ src/soupBowl.ts | 2 +- src/timer.ts | 28 ++++++++++----------- src/ui/genericJson.ts | 2 +- src/ui/localFolder.ts | 2 +- src/ui/reddit.ts | 2 +- src/ui/sourceRow.ts | 14 ++++++++--- src/ui/unsplash.ts | 4 +-- src/ui/urlSource.ts | 2 +- src/ui/wallhaven.ts | 2 +- src/utils.ts | 18 +++++++------- src/wallpaperController.ts | 50 +++++++++++++++++++------------------- 28 files changed, 148 insertions(+), 141 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 2daa659a..0043ff9f 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -52,6 +52,7 @@ rules: - property eol-last: error eqeqeq: error + "@typescript-eslint/explicit-function-return-type": error func-call-spacing: error func-name-matching: error func-style: diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 0a28e696..238c1431 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -48,7 +48,7 @@ abstract class BaseAdapter { * * @param {HistoryEntry} historyEntry The historyEntry to fetch */ - async fetchFile(historyEntry: HistoryEntry) { + async fetchFile(historyEntry: HistoryEntry): Promise { const file = Gio.file_new_for_path(historyEntry.path); const fstream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); @@ -68,7 +68,7 @@ abstract class BaseAdapter { return historyEntry; } - protected _includesWallpaper(array: HistoryEntry[], uri: string) { + protected _includesWallpaper(array: HistoryEntry[], uri: string): boolean { for (const element of array) { if (element.source.imageDownloadUrl === uri) return true; @@ -82,7 +82,7 @@ abstract class BaseAdapter { * * @param {string} filename Name of the image */ - protected _isImageBlocked(filename: string) { + protected _isImageBlocked(filename: string): boolean { const blockedFilenames = this._generalSettings.getStrv('blocked-images'); if (blockedFilenames.includes(filename)) { @@ -94,7 +94,7 @@ abstract class BaseAdapter { } // eslint-disable-next-line no-unused-vars - protected _error(err: string, callback?: (element: null, error: { error: string }) => void) { + protected _error(err: string, callback?: (element: null, error: { error: string }) => void): void { const error = {error: err}; this._logger.error(JSON.stringify(error)); diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 9f760e7a..9340e26c 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -24,7 +24,7 @@ class GenericJsonAdapter extends BaseAdapter { }); } - private async _getHistoryEntry(count: number) { + private async _getHistoryEntry(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; let url = this._settings.getString('request-url'); @@ -113,7 +113,7 @@ class GenericJsonAdapter extends BaseAdapter { return wallpaperResult; } - async requestRandomImage(count: number) { + async requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; for (let i = 0; i < MAX_SERVICE_RETRIES + count && wallpaperResult.length < count; i++) { diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 0069395d..99ebb2ea 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -59,7 +59,7 @@ class LocalFolderAdapter extends BaseAdapter { }); } - async fetchFile(historyEntry: HistoryEntry) { + async fetchFile(historyEntry: HistoryEntry): Promise { const sourceFile = Gio.File.new_for_uri(historyEntry.source.imageDownloadUrl); const targetFile = Gio.File.new_for_path(historyEntry.path); diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index 4a33600d..c5e8f929 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -41,11 +41,11 @@ class RedditAdapter extends BaseAdapter { }); } - private _ampDecode(string: string) { + private _ampDecode(string: string): string { return string.replace(/&/g, '&'); } - async requestRandomImage(count: number) { + async requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; const subreddits = this._settings.getString('subreddits').split(',').map(s => s.trim()).join('+'); const require_sfw = this._settings.getBoolean('allow-sfw'); diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index de1dca17..f3d73d11 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -67,7 +67,7 @@ class UnsplashAdapter extends BaseAdapter { return historyEntry; } - async requestRandomImage(count: number) { + async requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; for (let i = 0; i < MAX_SERVICE_RETRIES + count && wallpaperResult.length < count; i++) { @@ -95,7 +95,7 @@ class UnsplashAdapter extends BaseAdapter { return wallpaperResult; } - private _generateOptionsString() { + private _generateOptionsString(): string { const options = this._options; let optionsString = ''; @@ -128,7 +128,7 @@ class UnsplashAdapter extends BaseAdapter { return optionsString; } - private _readOptionsFromSettings() { + private _readOptionsFromSettings(): void { this._options.w = this._settings.getInt('image-width'); this._options.h = this._settings.getInt('image-height'); diff --git a/src/adapter/urlSource.ts b/src/adapter/urlSource.ts index 3a78d488..67dabb55 100644 --- a/src/adapter/urlSource.ts +++ b/src/adapter/urlSource.ts @@ -14,7 +14,7 @@ class UrlSourceAdapter extends BaseAdapter { }); } - requestRandomImage(unusedCount: number) { + requestRandomImage(unusedCount: number): Promise { const imageDownloadUrl = this._settings.getString('image-url'); let authorName: string | null = this._settings.getString('author-name'); const authorUrl = this._settings.getString('author-url'); diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index a0850a28..a6ab05a5 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -49,7 +49,7 @@ class WallhavenAdapter extends BaseAdapter { }); } - async requestRandomImage(count: number) { + async requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; this._readOptionsFromSettings(); @@ -101,7 +101,7 @@ class WallhavenAdapter extends BaseAdapter { return wallpaperResult; } - private _generateOptionsString(options: T) { + private _generateOptionsString(options: T): string { let optionsString = ''; for (const key in options) { @@ -127,7 +127,7 @@ class WallhavenAdapter extends BaseAdapter { return false; } - private _readOptionsFromSettings() { + private _readOptionsFromSettings(): void { const keywords = this._settings.getString('keyword').split(','); if (keywords.length > 0) { const randomKeyword = keywords[Utils.getRandomNumber(keywords.length)]; diff --git a/src/extension.ts b/src/extension.ts index bb523c7a..0d6f0cf5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,7 +12,7 @@ let RandomWallpaperMenu: typeof RandomWallpaperMenuNamespace | null = null; * */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -function init() { +function init(): Extension { return new Extension(); } @@ -22,7 +22,7 @@ class Extension { private _panelMenu: RandomWallpaperMenuNamespace.RandomWallpaperMenu | null = null; private _timer: AFTimer.AFTimer | null = null; - enable() { + enable(): void { // Dynamically load own modules. This allows us to use proper ES6 Modules this._importModules().then(() => { if (!Logger || !Timer || !WallpaperController || !RandomWallpaperMenu) @@ -45,7 +45,7 @@ class Extension { }); } - disable() { + disable(): void { if (this._logger) this._logger.info('Disable extension.'); @@ -67,7 +67,7 @@ class Extension { RandomWallpaperMenu = null; } - private async _importModules() { + private async _importModules(): Promise { // All imports as dynamic loads to work around the fact this module won't be in a topmost // context inside the gnome shell and can't use import statements (yet). // PopOS' tiling extension and RoundedCorners Extension work around the above limitation by diff --git a/src/history.ts b/src/history.ts index aa1b3bb9..74378730 100644 --- a/src/history.ts +++ b/src/history.ts @@ -65,7 +65,7 @@ class HistoryController { this.load(); } - insert(historyElements: HistoryEntry[]) { + insert(historyElements: HistoryEntry[]): void { for (const historyElement of historyElements) this.history.unshift(historyElement); @@ -78,7 +78,7 @@ class HistoryController { * * @param {string} id ID of the historyEntry */ - promoteToActive(id: string) { + promoteToActive(id: string): boolean { const element = this.get(id); if (element === null) return false; @@ -98,7 +98,7 @@ class HistoryController { * * @param {string} id ID of the historyEntry */ - get(id: string) { + get(id: string): HistoryEntry | null { for (const elem of this.history) { if (elem.id === id) return elem; @@ -110,11 +110,11 @@ class HistoryController { /** * Get the current history element. */ - getCurrentEntry() { + getCurrentEntry(): HistoryEntry { return this.history[0]; } - getEntryByPath(path: string) { + getEntryByPath(path: string): HistoryEntry | null { for (const element of this.history) { if (element.path === path) return element; @@ -126,14 +126,14 @@ class HistoryController { /** * Get a random HistoryEntry. */ - getRandom() { + getRandom(): HistoryEntry { return this.history[Utils.getRandomNumber(this.history.length)]; } /** * Load the history from the schema */ - load() { + load(): void { this.size = this._settings.getInt('history-length'); const stringHistory: string[] = this._settings.getStrv('history'); @@ -149,7 +149,7 @@ class HistoryController { /** * Save the history to the schema */ - save() { + save(): void { const stringHistory = this.history.map(elem => { return JSON.stringify(elem); }); @@ -160,7 +160,7 @@ class HistoryController { /** * Clear the history and delete all photos except the current one. */ - clear() { + clear(): boolean { const firstHistoryElement = this.history[0]; if (firstHistoryElement) @@ -192,7 +192,7 @@ class HistoryController { /** * Delete all pictures that have no slot in the history. */ - private _deleteOldPictures() { + private _deleteOldPictures(): void { this.size = this._settings.getInt('history-length'); while (this.history.length > this.size) { const path = this.history.pop()?.path; diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 70f35994..fdc89417 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -195,7 +195,7 @@ const HistoryElement = GObject.registerClass({ }); } - private _addToBlocklist(entry: HistoryModule.HistoryEntry) { + private _addToBlocklist(entry: HistoryModule.HistoryEntry): void { if (!entry.adapter?.id || entry.adapter.id === '-1' || !entry.name) { this._logger.error('Image entry is missing information'); return; @@ -212,7 +212,7 @@ const HistoryElement = GObject.registerClass({ generalSettings.setStrv('blocked-images', blockedFilenames); } - private async _saveImage() { + private async _saveImage(): Promise { if (!this.historyEntry.path || !this.historyEntry.name) throw new Error('Image entry is missing information'); @@ -255,7 +255,7 @@ const HistoryElement = GObject.registerClass({ throw new Error(`Failed writing file contents: ${message}`); } - setIndex(index: number) { + setIndex(index: number): void { this._prefixLabel.set_text(`${String(index)}.`); } }); @@ -304,7 +304,7 @@ class NewWallpaperElement extends PopupMenu.PopupBaseMenuItem { this.actor.add_child(container); } - show() { + show(): void { if (this._timer.isActive()) { const remainingMinutes = this._timer.remainingMinutes(); const minutes = remainingMinutes % 60; @@ -338,7 +338,7 @@ class StatusElement { }); } - startLoading() { + startLoading(): void { // FIXME: Don't know where this is defined // @ts-expect-error // eslint-disable-next-line @typescript-eslint/no-unsafe-call @@ -351,7 +351,7 @@ class StatusElement { }); } - stopLoading() { + stopLoading(): void { this.icon.remove_all_transitions(); this.icon.opacity = 255; } @@ -380,7 +380,7 @@ class HistorySection extends PopupMenu.PopupMenuSection { onEnter: (actor: InstanceType) => void, onLeave: (actor: InstanceType) => void, onSelect: (actor: InstanceType) => void - ) { + ): void { if (this._historyCache.length <= 1) this.removeAll(); // remove empty history element @@ -414,7 +414,7 @@ class HistorySection extends PopupMenu.PopupMenuSection { this._historyCache = history; } - private _cleanupHistoryCache(existingIDs: string[]) { + private _cleanupHistoryCache(existingIDs: string[]): void { const destroyIDs = Array.from(this._historySectionCache.keys()).filter(i => existingIDs.indexOf(i) === -1); destroyIDs.forEach(id => { @@ -423,7 +423,7 @@ class HistorySection extends PopupMenu.PopupMenuSection { }); } - clear() { + clear(): void { this._cleanupHistoryCache([]); this.removeAll(); this.addMenuItem( diff --git a/src/logger.ts b/src/logger.ts index 0b4164f8..38e30035 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -21,7 +21,7 @@ class Logger { this._callingClass = callingClass; } - private _log(level: LogLevelStrings, message: unknown) { + private _log(level: LogLevelStrings, message: unknown): void { let errorMessage = String(message); if (message instanceof Error) @@ -36,32 +36,32 @@ class Logger { logError(message); } - private _selectedLogLevel() { + private _selectedLogLevel(): number { return this._settings.getEnum('log-level'); } - debug(message: unknown) { + debug(message: unknown): void { if (this._selectedLogLevel() < LogLevel.DEBUG) return; this._log('DEBUG', message); } - info(message: unknown) { + info(message: unknown): void { if (this._selectedLogLevel() < LogLevel.INFO) return; this._log('INFO', message); } - warn(message: unknown) { + warn(message: unknown): void { if (this._selectedLogLevel() < LogLevel.WARNING) return; this._log('WARNING', message); } - error(message: unknown) { + error(message: unknown): void { if (this._selectedLogLevel() < LogLevel.ERROR) return; diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 447fc2d9..5bd87a21 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -31,7 +31,7 @@ class HydraPaper implements WallpaperManager { return this._command !== null; } - cancelRunning() { + cancelRunning(): void { if (this._cancellable === null) return; @@ -50,7 +50,7 @@ class HydraPaper implements WallpaperManager { * @param {string[]} wallpaperArray Array of image paths * @param {boolean} darkmode Use darkmode, gives different image in cache path */ - private async _run(wallpaperArray: string[], darkmode: boolean = false) { + private async _run(wallpaperArray: string[], darkmode: boolean = false): Promise { // Cancel already running processes before starting new ones this.cancelRunning(); @@ -76,7 +76,7 @@ class HydraPaper implements WallpaperManager { this._cancellable = null; } - async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings?: Settings, screensaverSettings?: Settings) { + async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings?: Settings, screensaverSettings?: Settings): Promise { if ((mode === 0 || mode === 2) && backgroundSettings) { await this._run(wallpaperPaths); diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts index 2dd79493..a6d2ab13 100644 --- a/src/manager/superPaper.ts +++ b/src/manager/superPaper.ts @@ -64,7 +64,7 @@ class Superpaper implements WallpaperManager { // * Sets picture-option to spanned // * Sets both picture-uri options // * Can use only single images - private async _run(wallpaperArray: string[]) { + private async _run(wallpaperArray: string[]): Promise { // Cancel already running processes before starting new ones this.cancelRunning(); diff --git a/src/prefs.ts b/src/prefs.ts index dda6fcbd..34cc5788 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -20,7 +20,7 @@ const Self = ExtensionUtils.getCurrentExtension(); * */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -function init() { +function init(): void { // Convenience.initTranslations(); } @@ -36,7 +36,7 @@ function init() { * @param {Adw.PreferencesWindow} window Window the extension should fill */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -function fillPreferencesWindow(window: Adw.PreferencesWindow) { +function fillPreferencesWindow(window: Adw.PreferencesWindow): void { window.set_default_size(600, 720); // temporary fill window to prevent error message until modules are loaded const tmpPage = new Adw.PreferencesPage(); @@ -157,7 +157,7 @@ class RandomWallpaperSettings { * Import modules ES6 style instead the built-in gjs style. * Allows proper async/await in imported modules. */ - private async _importModules() { + private async _importModules(): Promise { // All imports as dynamic loads to work around the fact this module won't be in a topmost // context inside the gnome shell and can't use import statements (yet). // PopOS' tiling extension and RoundedCorners Extension work around the above limitation by @@ -176,7 +176,7 @@ class RandomWallpaperSettings { Settings = moduleSettings; } - private _bindButtons() { + private _bindButtons(): void { const newWallpaperButton: Adw.ActionRow = this._builder.get_object('request_new_wallpaper'); const newWallpaperButtonLabel = newWallpaperButton.get_child() as Gtk.Label | null; const origNewWallpaperText = newWallpaperButtonLabel?.get_label() ?? 'Request New Wallpaper'; @@ -212,7 +212,7 @@ class RandomWallpaperSettings { }); } - private _bindHistorySection(window: Adw.PreferencesWindow) { + private _bindHistorySection(window: Adw.PreferencesWindow): void { const entryRow = this._builder.get_object('row_favorites_folder'); entryRow.text = this._settings.getString('favorites-folder'); @@ -263,7 +263,7 @@ class RandomWallpaperSettings { /** * Load the config from the schema */ - private _loadSources() { + private _loadSources(): void { this._sources = this._settings.getStrv('sources'); // this._sources.sort((a, b) => { @@ -287,7 +287,7 @@ class RandomWallpaperSettings { }); } - private _saveSources() { + private _saveSources(): void { this._settings.setStrv('sources', this._sources); } } diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 7235e4ab..9076052d 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -162,7 +162,7 @@ class RandomWallpaperMenu { this._settings.observe('history', this.setHistoryList.bind(this)); } - init() { + init(): void { this.updatePanelMenuVisibility(); this.setHistoryList(); @@ -170,7 +170,7 @@ class RandomWallpaperMenu { Main.panel.addToStatusArea('random-wallpaper-menu', this._panelMenu); } - cleanup() { + cleanup(): void { this.clearHistoryList(); this._panelMenu.destroy(); @@ -179,14 +179,14 @@ class RandomWallpaperMenu { this._settings.disconnect(this._hidePanelIconHandler); } - updatePanelMenuVisibility() { + updatePanelMenuVisibility(): void { if (this._settings.getBoolean('hide-panel-icon')) this._panelMenu.hide(); else this._panelMenu.show(); } - setCurrentBackgroundElement() { + setCurrentBackgroundElement(): void { this._currentBackgroundSection.removeAll(); const historyController = this._wallpaperController.getHistoryController(); @@ -198,7 +198,7 @@ class RandomWallpaperMenu { } } - setHistoryList() { + setHistoryList(): void { this._wallpaperController.update(); this.setCurrentBackgroundElement(); @@ -214,7 +214,7 @@ class RandomWallpaperMenu { * @this {RandomWallpaperMenu} RandomWallpaperMenu * @param {InstanceType} unusedActor The activating panel item */ - function onLeave(this: RandomWallpaperMenu, unusedActor: InstanceType) { + function onLeave(this: RandomWallpaperMenu, unusedActor: InstanceType): void { if (!this._wallpaperController.prohibitNewWallpaper && this._savedBackgroundUri) this._wallpaperController.resetWallpaper(this._savedBackgroundUri); } @@ -223,7 +223,7 @@ class RandomWallpaperMenu { * @this {RandomWallpaperMenu} RandomWallpaperMenu * @param {InstanceType} actor The activating panel item */ - function onEnter(this: RandomWallpaperMenu, actor: InstanceType) { + function onEnter(this: RandomWallpaperMenu, actor: InstanceType): void { if (!this._wallpaperController.prohibitNewWallpaper) this._wallpaperController.previewWallpaper(actor.historyEntry.id); } @@ -232,7 +232,7 @@ class RandomWallpaperMenu { * @this {RandomWallpaperMenu} RandomWallpaperMenu * @param {InstanceType} actor The activating panel item */ - function onSelect(this: RandomWallpaperMenu, actor: InstanceType) { + function onSelect(this: RandomWallpaperMenu, actor: InstanceType): void { // Make sure no other preview or reset event overwrites our setWallpaper! this._wallpaperController.prohibitNewWallpaper = true; @@ -257,7 +257,7 @@ class RandomWallpaperMenu { this._historySection.updateList(history, onEnter.bind(this), onLeave.bind(this), onSelect.bind(this)); } - clearHistoryList() { + clearHistoryList(): void { this._historySection.clear(); } } diff --git a/src/settings.ts b/src/settings.ts index 035725b0..9b33520a 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -31,11 +31,11 @@ class Settings { } } - bind(keyName: string, gObject: GObject.Object, property: string, settingsBindFlags: Gio.SettingsBindFlags) { + bind(keyName: string, gObject: GObject.Object, property: string, settingsBindFlags: Gio.SettingsBindFlags): void { this._settings.bind(keyName, gObject, property, settingsBindFlags); } - disconnect(handler: number) { + disconnect(handler: number): void { return this._settings.disconnect(handler); } @@ -63,79 +63,79 @@ class Settings { return this._settings.get_strv(key); } - getSchema() { + getSchema(): Gio.SettingsSchema { return this._settings.settings_schema; } - isWritable(key: string) { + isWritable(key: string): boolean { return this._settings.is_writable(key); } - listKeys() { + listKeys(): string[] { return this._settings.list_keys(); } // eslint-disable-next-line no-unused-vars - observe(key: string, callback: (...args: any[]) => any) { + observe(key: string, callback: (...args: any[]) => any): number { return this._settings.connect(`changed::${key}`, callback); } - reset(keyName: string) { + reset(keyName: string): void { this._settings.reset(keyName); } - resetSchema() { + resetSchema(): void { for (const key of this._settings.settings_schema.list_keys()) this.reset(key); } - setBoolean(key: string, value: boolean) { + setBoolean(key: string, value: boolean): void { if (this._settings.set_boolean(key, value)) this._save(); else throw new Error(`Could not set ${key} (type: boolean) with the value ${String(value)}`); } - setEnum(key: string, value: number) { + setEnum(key: string, value: number): void { if (this._settings.set_enum(key, value)) this._save(); else throw new Error(`Could not set ${key} (type: number) with the value ${value}`); } - setInt(key: string, value: number) { + setInt(key: string, value: number): void { if (this._settings.set_int(key, value)) this._save(); else throw new Error(`Could not set ${key} (type: number) with the value ${value}`); } - setInt64(key: string, value: number) { + setInt64(key: string, value: number): void { if (this._settings.set_int64(key, value)) this._save(); else throw new Error(`Could not set ${key} (type: number64) with the value ${value}`); } - setString(key: string, value: string) { + setString(key: string, value: string): void { if (this._settings.set_string(key, value)) this._save(); else throw new Error(`Could not set ${key} (type: string) with the value ${value}`); } - setStrv(key: string, value: string[]) { + setStrv(key: string, value: string[]): void { if (this._settings.set_strv(key, value)) this._save(); else throw new Error(`Could not set ${key} (type: string[]) with the value "${value.toString()}"`); } - private _save() { + private _save(): void { Gio.Settings.sync(); // Necessary: http://stackoverflow.com/questions/9985140 } - private _getSchema(schemaId?: string) { + private _getSchema(schemaId?: string): Gio.SettingsSchema { // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/gnome-43/js/misc/extensionUtils.js#L211 if (!schemaId) schemaId = Self.metadata['settings-schema']; diff --git a/src/soupBowl.ts b/src/soupBowl.ts index 2994baee..857968fc 100644 --- a/src/soupBowl.ts +++ b/src/soupBowl.ts @@ -24,7 +24,7 @@ class SoupBowl { throw new Error('Unknown libsoup version'); } - newGetMessage(uri: string) { + newGetMessage(uri: string): Soup.Message { return Soup.Message.new('GET', uri); } diff --git a/src/timer.ts b/src/timer.ts index 8ac3723b..04646cd7 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -23,7 +23,7 @@ class AFTimer { return this._afTimerInstance; } - static destroy() { + static destroy(): void { if (this._afTimerInstance) this._afTimerInstance.cleanup(); @@ -38,7 +38,7 @@ class AFTimer { * called directly and the next trigger is scheduled at the * next correct time frame repeatedly. */ - continue() { + continue(): void { if (!this.isActive()) return; @@ -50,11 +50,11 @@ class AFTimer { void this.start(); } - isActive() { + isActive(): boolean { return this._settings.getBoolean('auto-fetch'); } - isPaused() { + isPaused(): boolean { return this._paused; } @@ -65,19 +65,19 @@ class AFTimer { * until continue() was called. * 'timer-last-trigger' stays the same. */ - pause() { + pause(): void { this._logger.debug('Timer paused'); this._paused = true; this.cleanup(); } - remainingMinutes() { + remainingMinutes(): number { const minutesElapsed = this._minutesElapsed(); const remainder = minutesElapsed % this._minutes; return Math.max(this._minutes - remainder, 0); } - registerCallback(callback: () => Promise) { + registerCallback(callback: () => Promise): void { this._timeoutEndCallback = callback; } @@ -86,7 +86,7 @@ class AFTimer { * * @param {number} minutes Number in minutes */ - setMinutes(minutes: number) { + setMinutes(minutes: number): void { this._minutes = minutes; } @@ -99,7 +99,7 @@ class AFTimer { * directly and the next trigger is scheduled at the * next correct time frame repeatedly. */ - async start() { + async start(): Promise { if (this._paused) return; @@ -149,7 +149,7 @@ class AFTimer { /** * Stop the timer. */ - stop() { + stop(): void { this._settings.setInt64('timer-last-trigger', 0); this.cleanup(); } @@ -157,7 +157,7 @@ class AFTimer { /** * Cleanup the timeout callback if it exists. */ - cleanup() { + cleanup(): void { if (this._timeout) { // only remove if a timeout is active this._logger.debug('Removing running timer'); GLib.source_remove(this._timeout); @@ -169,11 +169,11 @@ class AFTimer { * Sets the last activation time to [now]. This doesn't affect already running timer * and will be ignored if the timer is paused. */ - private _reset() { + private _reset(): void { this._settings.setInt64('timer-last-trigger', new Date().getTime()); } - private _minutesElapsed() { + private _minutesElapsed(): number { const now = Date.now(); const last: number = this._settings.getInt64('timer-last-trigger'); @@ -184,7 +184,7 @@ class AFTimer { return Math.floor(elapsed / (60 * 1000)); } - private _surpassedInterval() { + private _surpassedInterval(): boolean { const now = Date.now(); const last = this._settings.getInt64('timer-last-trigger'); const diff = now - last; diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index a521f57d..71dd1eb8 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -81,7 +81,7 @@ const GenericJsonSettingsGroup = GObject.registerClass({ Gio.SettingsBindFlags.DEFAULT); } - clearConfig() { + clearConfig(): void { this._settings.resetSchema(); } }); diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index 935ffe9c..35f042d5 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -64,7 +64,7 @@ const LocalFolderSettingsGroup = GObject.registerClass({ }); } - clearConfig() { + clearConfig(): void { this._settings.resetSchema(); } }); diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index 4349cd83..feb343d5 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -64,7 +64,7 @@ const RedditSettingsGroup = GObject.registerClass({ Gio.SettingsBindFlags.DEFAULT); } - clearConfig() { + clearConfig(): void { this._settings.resetSchema(); } }); diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 386a2a09..177f5373 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -115,13 +115,19 @@ const SourceRow = GObject.registerClass({ }); } - private _fillRow(type: number) { + private _fillRow(type: number): void { const targetWidget = this._getSettingsGroup(type); if (targetWidget !== null) this._settings_container.set_child(targetWidget); } - private _getSettingsGroup(type = 0) { + private _getSettingsGroup(type = 0): GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> + | GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> + | GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> + | GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> + | GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> + | GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> + | null { let targetWidget = null; switch (type) { case Utils.SourceType.UNSPLASH: @@ -150,7 +156,7 @@ const SourceRow = GObject.registerClass({ return targetWidget; } - private _removeBlockedImage(filename: string) { + private _removeBlockedImage(filename: string): void { let blockedImages = this._settings.getStrv('blocked-images'); if (!blockedImages.includes(filename)) return; @@ -163,7 +169,7 @@ const SourceRow = GObject.registerClass({ /** * Clear all keys associated to this ID across all adapter */ - clearConfig() { + clearConfig(): void { for (const i of Array(6).keys()) { const widget = this._getSettingsGroup(i); if (widget) diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index 73130344..b592fdfa 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -92,14 +92,14 @@ const UnsplashSettingsGroup = GObject.registerClass({ }); } - private _unsplashUnconstrained(comboRow: Adw.ComboRow, enable: boolean, targetElement: Gtk.Widget) { + private _unsplashUnconstrained(comboRow: Adw.ComboRow, enable: boolean, targetElement: Gtk.Widget): void { if (comboRow.selected === 0) targetElement.set_sensitive(enable); else targetElement.set_sensitive(!enable); } - clearConfig() { + clearConfig(): void { this._settings.resetSchema(); } }); diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index c0be4120..e322ccd5 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -57,7 +57,7 @@ const UrlSourceSettingsGroup = GObject.registerClass({ Gio.SettingsBindFlags.DEFAULT); } - clearConfig() { + clearConfig(): void { this._settings.resetSchema(); } }); diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index 0f33d1c3..1f848c35 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -153,7 +153,7 @@ const WallhavenSettingsGroup = GObject.registerClass({ }); } - clearConfig() { + clearConfig(): void { this._settings.resetSchema(); } }); diff --git a/src/utils.ts b/src/utils.ts index af9c2d21..538ac9a2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -50,7 +50,7 @@ function getSourceTypeName(value: SourceType): string { * @param {string[]} argv String array of command and parameter * @param {Gio.Cancellable} [cancellable] Object to cancel the command later in lifetime */ -function execCheck(argv: string[], cancellable?: Gio.Cancellable | null) { +function execCheck(argv: string[], cancellable?: Gio.Cancellable | null): Promise { let cancelId = 0; const proc = new Gio.Subprocess({ argv, @@ -97,7 +97,7 @@ function execCheck(argv: string[], cancellable?: Gio.Cancellable | null) { * * @param {string} uri URI to scan */ -function fileName(uri: string) { +function fileName(uri: string): string { while (_isURIEncoded(uri)) uri = decodeURIComponent(uri); @@ -114,7 +114,7 @@ function fileName(uri: string) { * @param {Settings} settings Settings schema to scan values for * @param {string} key Key where to find values in the settings schema */ -function fillComboRowFromEnum(comboRow: Adw.ComboRow, settings: Settings, key: string) { +function fillComboRowFromEnum(comboRow: Adw.ComboRow, settings: Settings, key: string): void { // Fill combo from settings enum const availableTypes = settings.getSchema().get_key(key).get_range(); // GLib.Variant (sv) // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) @@ -138,7 +138,7 @@ function fillComboRowFromEnum(comboRow: Adw.ComboRow, settings: Settings, key: s * @param {string} str1 String to compare * @param {string} str2 String to compare */ -function findFirstDifference(str1: string, str2: string) { +function findFirstDifference(str1: string, str2: string): number { let i = 0; if (str1 === str2) return -1; @@ -177,7 +177,7 @@ function getMonitorCount(): number { * * @param {number} size Maximum */ -function getRandomNumber(size: number) { +function getRandomNumber(size: number): number { // https://stackoverflow.com/a/5915122 return Math.floor(Math.random() * size); } @@ -186,7 +186,7 @@ function getRandomNumber(size: number) { * * @param {string} uri The URI to check */ -function _isURIEncoded(uri: string) { +function _isURIEncoded(uri: string): boolean { uri = uri || ''; return uri !== decodeURIComponent(uri); @@ -198,7 +198,7 @@ function _isURIEncoded(uri: string) { * @param {Array} array Array of items * @param {T} value Item to remove */ -function removeItemOnce(array: T[], value: T) { +function removeItemOnce(array: T[], value: T): T[] { const index = array.indexOf(value); if (index > -1) array.splice(index, 1); @@ -213,12 +213,12 @@ function removeItemOnce(array: T[], value: T) { * @param {Settings} settings The settings schema object containing the keys to change * @param {string} uri The picture URI to be set */ -function setPictureUriOfSettingsObject(settings: Settings, uri: string) { +function setPictureUriOfSettingsObject(settings: Settings, uri: string): void { /* * inspired from: * https://bitbucket.org/LukasKnuth/backslide/src/7e36a49fc5e1439fa9ed21e39b09b61eca8df41a/backslide@codeisland.org/settings.js?at=master */ - const setProp = (property: string) => { + const setProp = (property: string): void => { if (settings.isWritable(property)) { // Set a new Background-Image (should show up immediately): settings.setString(property, uri); diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 74ef6114..2f5e6e0d 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -126,7 +126,7 @@ class WallpaperController { } } - private _clearHistory() { + private _clearHistory(): void { if (this._backendConnection.getBoolean('clear-history')) { this.update(); this.deleteHistory(); @@ -134,7 +134,7 @@ class WallpaperController { } } - private _openFolder() { + private _openFolder(): void { if (this._backendConnection.getBoolean('open-folder')) { const uri = GLib.filename_to_uri(this.wallpaperLocation, ''); Gio.AppInfo.launch_default_for_uri(uri, Gio.AppLaunchContext.new()); @@ -142,7 +142,7 @@ class WallpaperController { } } - private _pauseTimer() { + private _pauseTimer(): void { if (this._backendConnection.getBoolean('pause-timer')) { this._timer.pause(); } else { @@ -162,7 +162,7 @@ class WallpaperController { } } - private async _requestNewWallpaper() { + private async _requestNewWallpaper(): Promise { if (this._backendConnection.getBoolean('request-new-wallpaper')) { this.update(); try { @@ -174,11 +174,11 @@ class WallpaperController { } } - private _updateHistory() { + private _updateHistory(): void { this._historyController.load(); } - private _updateAutoFetching() { + private _updateAutoFetching(): void { let duration = 0; duration += this._settings.getInt('minutes'); duration += this._settings.getInt('hours') * 60; @@ -205,7 +205,7 @@ class WallpaperController { * * @param {number} count The amount of adapter requested */ - private _getRandomAdapter(count: number) { + private _getRandomAdapter(count: number): RandomAdapterResult[] { const sourceIDs = this._getRandomSource(count); const randomAdapterResult: RandomAdapterResult[] = []; @@ -224,7 +224,7 @@ class WallpaperController { * @param {RandomAdapterResult[]} array Array of already chosen adapter * @param {number} type Type of the source */ - function _arrayIncludes(array: RandomAdapterResult[], type: number) { + function _arrayIncludes(array: RandomAdapterResult[], type: number): RandomAdapterResult | null { for (const element of array) { if (element.type === type) return element; @@ -297,7 +297,7 @@ class WallpaperController { * @param {number} count Amount of requested source IDs * @returns Array of source IDs or ['-1'] in case of failure */ - private _getRandomSource(count: number) { + private _getRandomSource(count: number): string[] { const sourceResult: string[] = []; const sources: string[] = this._settings.getStrv('sources'); @@ -332,7 +332,7 @@ class WallpaperController { * @param {string[]} wallpaperPaths Array of paths to the image * @param {number} type Types to change */ - private async _setBackground(wallpaperPaths: string[], type: number = 0) { + private async _setBackground(wallpaperPaths: string[], type: number = 0): Promise { const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); const screensaverSettings = new SettingsModule.Settings('org.gnome.desktop.screensaver'); @@ -384,7 +384,7 @@ class WallpaperController { } // Run general post command - private _runPostCommands() { + private _runPostCommands(): void { const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); const commandString = this._settings.getString('general-post-command'); @@ -402,7 +402,7 @@ class WallpaperController { } } - private _fillDisplaysFromHistory(wallpaperArray: string[], requestCount?: number) { + private _fillDisplaysFromHistory(wallpaperArray: string[], requestCount?: number): string[] { const count = requestCount ?? this._getCurrentDisplayCount(); const newWallpaperArray: string[] = [...wallpaperArray]; @@ -422,7 +422,7 @@ class WallpaperController { return newWallpaperArray.slice(0, count); } - async setWallpaper(historyId: string) { + async setWallpaper(historyId: string): Promise { const historyElement = this._historyController.get(historyId); if (historyElement?.id && historyElement.path && this._historyController.promoteToActive(historyElement.id)) { @@ -448,7 +448,7 @@ class WallpaperController { // TODO: Error handling history id not found. } - async fetchNewWallpaper() { + async fetchNewWallpaper(): Promise { this._startLoadingHooks.forEach(element => element()); try { @@ -551,7 +551,7 @@ class WallpaperController { } // TODO: Change to original historyElement if more variable get exposed - private _getCommandArray(commandString: string, historyElementPath: string) { + private _getCommandArray(commandString: string, historyElementPath: string): string[] | null { let string = commandString; if (string === '') return null; @@ -584,7 +584,7 @@ class WallpaperController { * This also takes the user setting and HydraPaper availability into account * and lies accordingly by reporting only 1 display. */ - private _getCurrentDisplayCount() { + private _getCurrentDisplayCount(): number { if (!this._settings.getBoolean('multiple-displays')) return 1; @@ -594,7 +594,7 @@ class WallpaperController { return Utils.getMonitorCount(); } - private _backgroundTimeout(paths?: string[], delay?: number) { + private _backgroundTimeout(paths?: string[], delay?: number): void { if (this._timeout || !paths) return; @@ -620,7 +620,7 @@ class WallpaperController { }); } - previewWallpaper(historyId: string, delay?: number) { + previewWallpaper(historyId: string, delay?: number): void { if (!this._settings.getBoolean('disable-hover-preview')) { this._previewId = historyId; this._resetWallpaper = false; @@ -637,36 +637,36 @@ class WallpaperController { } } - resetWallpaper(uri: string) { + resetWallpaper(uri: string): void { if (!this._settings.getBoolean('disable-hover-preview')) { this._resetWallpaper = true; this._backgroundTimeout([GLib.filename_from_uri(uri)[0]]); } } - getHistoryController() { + getHistoryController(): HistoryModule.HistoryController { return this._historyController; } - deleteHistory() { + deleteHistory(): void { this._historyController.clear(); } - update() { + update(): void { this._updateHistory(); } - registerStartLoadingHook(fn: () => void) { + registerStartLoadingHook(fn: () => void): void { if (typeof fn === 'function') this._startLoadingHooks.push(fn); } - registerStopLoadingHook(fn: () => void) { + registerStopLoadingHook(fn: () => void): void { if (typeof fn === 'function') this._stopLoadingHooks.push(fn); } - private _bailOutWithCallback(msg: string, callback?: () => void) { + private _bailOutWithCallback(msg: string, callback?: () => void): void { this._logger.error(msg); if (callback) From aa7c1fb2515ac7fa75c4f25eb276a5be842dd060 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 26 May 2023 17:30:52 +0200 Subject: [PATCH 42/87] Use upstream Adw and extend the EntryRow --- build.sh | 3 +- package-lock.json | 29 + package.json | 1 + src/prefs.ts | 5 +- src/ui/genericJson.ts | 21 +- src/ui/localFolder.ts | 5 +- src/ui/reddit.ts | 5 +- src/ui/sourceRow.ts | 5 +- src/ui/unsplash.ts | 7 +- src/ui/urlSource.ts | 13 +- src/ui/wallhaven.ts | 11 +- src/utils.ts | 3 +- tsconfig.json | 1 + types/gtk4/adw/adw.d.ts | 3718 ------------------------------- types/gtk4/adw/adwEntryRow.d.ts | 96 + 15 files changed, 170 insertions(+), 3753 deletions(-) delete mode 100644 types/gtk4/adw/adw.d.ts create mode 100644 types/gtk4/adw/adwEntryRow.d.ts diff --git a/build.sh b/build.sh index 20e7bca0..4fc4083e 100755 --- a/build.sh +++ b/build.sh @@ -56,7 +56,8 @@ compile_js() { sed -i -E "s#import \* as (.*) from 'gi://.*';#const \1 = imports.gi.\1;#g" "$file" # Libadwaita seems somehow missing from gi:// - sed -i -E "s#import \* as Adw from '@gi/gtk4/adw/adw';#const Adw = imports.gi.Adw;#g" "$file" + sed -i -E "s#import \* as Adw from '@gi-types/adw1';#const Adw = imports.gi.Adw;#g" "$file" + sed -i -E "s#import \* as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow';##g" "$file" # Special module naming sed -i -E "s#import \* as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray';#const ByteArray = imports.byteArray;#g" "$file" diff --git a/package-lock.json b/package-lock.json index 4b3f51be..5d4c8973 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "devDependencies": { + "@gi-types/adw1": "^1.1.1", "@gi-types/base-types": "^1.0.0", "@gi-types/gjs-environment": "^1.1.0", "@gi-types/gtk4-types": "^1.0.0", @@ -64,6 +65,20 @@ "@gi-types/gobject2": "^2.72.1" } }, + "node_modules/@gi-types/adw1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@gi-types/adw1/-/adw1-1.1.1.tgz", + "integrity": "sha512-w+NxplCUf2AeVdSwkicbVrIOB5QJd9BQo1BI3Xq+2IZkgMRePIUG9/eGsrvmzxKU00EiH211KEwqGe/2UOmomQ==", + "dev": true, + "dependencies": { + "@gi-types/gdk4": "^4.0.1", + "@gi-types/gio2": "^2.68.0", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0", + "@gi-types/gsk4": "^4.0.1", + "@gi-types/gtk4": "^4.6.1" + } + }, "node_modules/@gi-types/atk": { "version": "2.36.7", "resolved": "https://registry.npmjs.org/@gi-types/atk/-/atk-2.36.7.tgz", @@ -2522,6 +2537,20 @@ "@gi-types/gobject2": "^2.72.1" } }, + "@gi-types/adw1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@gi-types/adw1/-/adw1-1.1.1.tgz", + "integrity": "sha512-w+NxplCUf2AeVdSwkicbVrIOB5QJd9BQo1BI3Xq+2IZkgMRePIUG9/eGsrvmzxKU00EiH211KEwqGe/2UOmomQ==", + "dev": true, + "requires": { + "@gi-types/gdk4": "^4.0.1", + "@gi-types/gio2": "^2.68.0", + "@gi-types/glib2": "^2.68.0", + "@gi-types/gobject2": "^2.68.0", + "@gi-types/gsk4": "^4.0.1", + "@gi-types/gtk4": "^4.6.1" + } + }, "@gi-types/atk": { "version": "2.36.7", "resolved": "https://registry.npmjs.org/@gi-types/atk/-/atk-2.36.7.tgz", diff --git a/package.json b/package.json index bed63905..8c02b763 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "devDependencies": { + "@gi-types/adw1": "^1.1.1", "@gi-types/base-types": "^1.0.0", "@gi-types/gjs-environment": "^1.1.0", "@gi-types/gtk4-types": "^1.0.0", diff --git a/src/prefs.ts b/src/prefs.ts index 34cc5788..dfbc6f00 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -1,7 +1,8 @@ +import * as Adw from '@gi-types/adw1'; import * as Gio from 'gi://Gio'; import * as Gtk from 'gi://Gtk'; -import * as Adw from '@gi/gtk4/adw/adw'; +import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import type * as SettingsNamespace from './settings.js'; @@ -213,7 +214,7 @@ class RandomWallpaperSettings { } private _bindHistorySection(window: Adw.PreferencesWindow): void { - const entryRow = this._builder.get_object('row_favorites_folder'); + const entryRow = this._builder.get_object('row_favorites_folder'); entryRow.text = this._settings.getString('favorites-folder'); this._settings.bind('history-length', diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index 71dd1eb8..cdee5c0a 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -1,8 +1,9 @@ +import * as Adw from '@gi-types/adw1'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as GObject from 'gi://GObject'; -import * as Adw from '@gi/gtk4/adw/adw'; +import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -25,15 +26,15 @@ const GenericJsonSettingsGroup = GObject.registerClass({ ], }, class GenericJsonSettingsGroup extends Adw.PreferencesGroup { // InternalChildren - private _author_name_path!: Adw.EntryRow; - private _author_url_path!: Adw.EntryRow; - private _author_url_prefix!: Adw.EntryRow; - private _domain!: Adw.EntryRow; - private _image_path!: Adw.EntryRow; - private _image_prefix!: Adw.EntryRow; - private _post_path!: Adw.EntryRow; - private _post_prefix!: Adw.EntryRow; - private _request_url!: Adw.EntryRow; + private _author_name_path!: AdwEntryRow.EntryRow; + private _author_url_path!: AdwEntryRow.EntryRow; + private _author_url_prefix!: AdwEntryRow.EntryRow; + private _domain!: AdwEntryRow.EntryRow; + private _image_path!: AdwEntryRow.EntryRow; + private _image_prefix!: AdwEntryRow.EntryRow; + private _post_path!: AdwEntryRow.EntryRow; + private _post_prefix!: AdwEntryRow.EntryRow; + private _request_url!: AdwEntryRow.EntryRow; private _settings; diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index 35f042d5..e7e01615 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -1,9 +1,10 @@ +import * as Adw from '@gi-types/adw1'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as GObject from 'gi://GObject'; import * as Gtk from 'gi://Gtk'; -import * as Adw from '@gi/gtk4/adw/adw'; +import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -20,7 +21,7 @@ const LocalFolderSettingsGroup = GObject.registerClass({ }, class LocalFolderSettingsGroup extends Adw.PreferencesGroup { // InternalChildren private _folder!: Gtk.Button; - private _folder_row!: Adw.EntryRow; + private _folder_row!: AdwEntryRow.EntryRow; private _saveDialog: Gtk.FileChooserNative | undefined; private _settings; diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index feb343d5..767e3a3d 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -1,9 +1,10 @@ +import * as Adw from '@gi-types/adw1'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as GObject from 'gi://GObject'; import * as Gtk from 'gi://Gtk'; -import * as Adw from '@gi/gtk4/adw/adw'; +import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -28,7 +29,7 @@ const RedditSettingsGroup = GObject.registerClass({ private _image_ratio2!: Gtk.Adjustment; private _min_height!: Gtk.Adjustment; private _min_width!: Gtk.Adjustment; - private _subreddits!: Adw.EntryRow; + private _subreddits!: AdwEntryRow.EntryRow; private _settings; diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 177f5373..1ef2d3ed 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -1,9 +1,10 @@ +import * as Adw from '@gi-types/adw1'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as GObject from 'gi://GObject'; import * as Gtk from 'gi://Gtk'; -import * as Adw from '@gi/gtk4/adw/adw'; +import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -44,7 +45,7 @@ const SourceRow = GObject.registerClass({ private _blocked_images_list!: Adw.ExpanderRow; private _combo!: Adw.ComboRow; private _settings_container!: Adw.Clamp; - private _source_name!: Adw.EntryRow; + private _source_name!: AdwEntryRow.EntryRow; private _settings; private _logger = new Logger('RWG3', 'SourceRow'); diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index b592fdfa..d3c8d42c 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -1,9 +1,10 @@ +import * as Adw from '@gi-types/adw1'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as GObject from 'gi://GObject'; import * as Gtk from 'gi://Gtk'; -import * as Adw from '@gi/gtk4/adw/adw'; +import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -27,11 +28,11 @@ const UnsplashSettingsGroup = GObject.registerClass({ // InternalChildren private _constraint_type!: Adw.ComboRow; - private _constraint_value!: Adw.EntryRow; + private _constraint_value!: AdwEntryRow.EntryRow; private _featured_only!: Gtk.Switch; private _image_height!: Gtk.Adjustment; private _image_width!: Gtk.Adjustment; - private _keyword!: Adw.EntryRow; + private _keyword!: AdwEntryRow.EntryRow; private _settings; diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index e322ccd5..dfe2653c 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -1,8 +1,9 @@ +import * as Adw from '@gi-types/adw1'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as GObject from 'gi://GObject'; -import * as Adw from '@gi/gtk4/adw/adw'; +import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -21,11 +22,11 @@ const UrlSourceSettingsGroup = GObject.registerClass({ ], }, class UrlSourceSettingsGroup extends Adw.PreferencesGroup { // InternalChildren - private _author_name!: Adw.EntryRow; - private _author_url!: Adw.EntryRow; - private _domain!: Adw.EntryRow; - private _image_url!: Adw.EntryRow; - private _post_url!: Adw.EntryRow; + private _author_name!: AdwEntryRow.EntryRow; + private _author_url!: AdwEntryRow.EntryRow; + private _domain!: AdwEntryRow.EntryRow; + private _image_url!: AdwEntryRow.EntryRow; + private _post_url!: AdwEntryRow.EntryRow; private _settings; diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index 1f848c35..99fa6f32 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -1,10 +1,11 @@ +import * as Adw from '@gi-types/adw1'; import * as Gdk from 'gi://Gdk'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as GObject from 'gi://GObject'; import * as Gtk from 'gi://Gtk'; -import * as Adw from '@gi/gtk4/adw/adw'; +import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -44,15 +45,15 @@ const WallhavenSettingsGroup = GObject.registerClass({ private _allow_nsfw!: Gtk.Switch; private _allow_sfw!: Gtk.Switch; private _allow_sketchy!: Gtk.Switch; - private _api_key!: Adw.EntryRow; - private _aspect_ratios!: Adw.EntryRow; + private _api_key!: AdwEntryRow.EntryRow; + private _aspect_ratios!: AdwEntryRow.EntryRow; private _button_color_undo!: Gtk.Button; private _button_color!: Gtk.Button; private _category_anime!: Gtk.Switch; private _category_general!: Gtk.Switch; private _category_people!: Gtk.Switch; - private _keyword!: Adw.EntryRow; - private _minimal_resolution!: Adw.EntryRow; + private _keyword!: AdwEntryRow.EntryRow; + private _minimal_resolution!: AdwEntryRow.EntryRow; private _row_color!: Adw.ActionRow; private _colorDialog: Gtk.ColorChooserDialog | undefined; diff --git a/src/utils.ts b/src/utils.ts index 538ac9a2..489ac777 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,10 +1,9 @@ +import * as Adw from '@gi-types/adw1'; import * as Gdk from 'gi://Gdk'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as Gtk from 'gi://Gtk'; -import * as Adw from '@gi/gtk4/adw/adw'; - import {Settings} from './settings.js'; // Generated code produces a no-shadow rule error: diff --git a/tsconfig.json b/tsconfig.json index 4d4cf18a..6c770f9a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,7 @@ "gjs-environment", "base-types", "gtk4-types", + "adw1", ], }, diff --git a/types/gtk4/adw/adw.d.ts b/types/gtk4/adw/adw.d.ts deleted file mode 100644 index f5dace63..00000000 --- a/types/gtk4/adw/adw.d.ts +++ /dev/null @@ -1,3718 +0,0 @@ -// https://github.com/gi-ts/gtk4/tree/master/packages/%40gi-types/adw1 -// Manually extended with Adw.EntryRow which is somehow missing form the original file - -/** - * Adw 1 - * - * Generated from 1.1.2 - */ - -import * as Gtk from "@gi-types/gtk4"; -import * as GObject from "@gi-types/gobject2"; -import * as Gio from "@gi-types/gio2"; -import * as GLib from "@gi-types/glib2"; -import * as Gdk from "@gi-types/gdk4"; -import * as Gsk from "@gi-types/gsk4"; - -export const DURATION_INFINITE: number; -export const MAJOR_VERSION: number; -export const MICRO_VERSION: number; -export const MINOR_VERSION: number; -export const VERSION_S: string; -export function easing_ease(self: Easing, value: number): number; -export function get_enable_animations(widget: Gtk.Widget): boolean; -export function get_major_version(): number; -export function get_micro_version(): number; -export function get_minor_version(): number; -export function init(): void; -export function is_initialized(): boolean; -export function lerp(a: number, b: number, t: number): number; -export type AnimationTargetFunc = (value: number) => void; - -export namespace AnimationState { - export const $gtype: GObject.GType; -} - -export enum AnimationState { - IDLE = 0, - PAUSED = 1, - PLAYING = 2, - FINISHED = 3, -} - -export namespace CenteringPolicy { - export const $gtype: GObject.GType; -} - -export enum CenteringPolicy { - LOOSE = 0, - STRICT = 1, -} - -export namespace ColorScheme { - export const $gtype: GObject.GType; -} - -export enum ColorScheme { - DEFAULT = 0, - FORCE_LIGHT = 1, - PREFER_LIGHT = 2, - PREFER_DARK = 3, - FORCE_DARK = 4, -} - -export namespace Easing { - export const $gtype: GObject.GType; -} - -export enum Easing { - LINEAR = 0, - EASE_IN_QUAD = 1, - EASE_OUT_QUAD = 2, - EASE_IN_OUT_QUAD = 3, - EASE_IN_CUBIC = 4, - EASE_OUT_CUBIC = 5, - EASE_IN_OUT_CUBIC = 6, - EASE_IN_QUART = 7, - EASE_OUT_QUART = 8, - EASE_IN_OUT_QUART = 9, - EASE_IN_QUINT = 10, - EASE_OUT_QUINT = 11, - EASE_IN_OUT_QUINT = 12, - EASE_IN_SINE = 13, - EASE_OUT_SINE = 14, - EASE_IN_OUT_SINE = 15, - EASE_IN_EXPO = 16, - EASE_OUT_EXPO = 17, - EASE_IN_OUT_EXPO = 18, - EASE_IN_CIRC = 19, - EASE_OUT_CIRC = 20, - EASE_IN_OUT_CIRC = 21, - EASE_IN_ELASTIC = 22, - EASE_OUT_ELASTIC = 23, - EASE_IN_OUT_ELASTIC = 24, - EASE_IN_BACK = 25, - EASE_OUT_BACK = 26, - EASE_IN_OUT_BACK = 27, - EASE_IN_BOUNCE = 28, - EASE_OUT_BOUNCE = 29, - EASE_IN_OUT_BOUNCE = 30, -} - -export namespace FlapFoldPolicy { - export const $gtype: GObject.GType; -} - -export enum FlapFoldPolicy { - NEVER = 0, - ALWAYS = 1, - AUTO = 2, -} - -export namespace FlapTransitionType { - export const $gtype: GObject.GType; -} - -export enum FlapTransitionType { - OVER = 0, - UNDER = 1, - SLIDE = 2, -} - -export namespace FoldThresholdPolicy { - export const $gtype: GObject.GType; -} - -export enum FoldThresholdPolicy { - MINIMUM = 0, - NATURAL = 1, -} - -export namespace LeafletTransitionType { - export const $gtype: GObject.GType; -} - -export enum LeafletTransitionType { - OVER = 0, - UNDER = 1, - SLIDE = 2, -} - -export namespace NavigationDirection { - export const $gtype: GObject.GType; -} - -export enum NavigationDirection { - BACK = 0, - FORWARD = 1, -} - -export namespace SqueezerTransitionType { - export const $gtype: GObject.GType; -} - -export enum SqueezerTransitionType { - NONE = 0, - CROSSFADE = 1, -} - -export namespace ToastPriority { - export const $gtype: GObject.GType; -} - -export enum ToastPriority { - NORMAL = 0, - HIGH = 1, -} - -export namespace ViewSwitcherPolicy { - export const $gtype: GObject.GType; -} - -export enum ViewSwitcherPolicy { - NARROW = 0, - WIDE = 1, -} -export module ActionRow { - export interface ConstructorProperties extends PreferencesRow.ConstructorProperties { - [key: string]: any; - activatable_widget: Gtk.Widget; - activatableWidget: Gtk.Widget; - icon_name: string; - iconName: string; - subtitle: string; - subtitle_lines: number; - subtitleLines: number; - title_lines: number; - titleLines: number; - } -} -export class ActionRow - extends PreferencesRow - implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get activatable_widget(): Gtk.Widget; - set activatable_widget(val: Gtk.Widget); - get activatableWidget(): Gtk.Widget; - set activatableWidget(val: Gtk.Widget); - get icon_name(): string; - set icon_name(val: string); - get iconName(): string; - set iconName(val: string); - get subtitle(): string; - set subtitle(val: string); - get subtitle_lines(): number; - set subtitle_lines(val: number); - get subtitleLines(): number; - set subtitleLines(val: number); - get title_lines(): number; - set title_lines(val: number); - get titleLines(): number; - set titleLines(val: number); - - // Signals - - connect(id: string, callback: (...args: any[]) => any): number; - connect_after(id: string, callback: (...args: any[]) => any): number; - emit(id: string, ...args: any[]): void; - connect(signal: "activated", callback: (_source: this) => void): number; - connect_after(signal: "activated", callback: (_source: this) => void): number; - emit(signal: "activated"): void; - - // Constructors - - static ["new"](): ActionRow; - - // Members - - activate(): void; - // Conflicted with Gtk.Widget.activate - activate(...args: never[]): any; - add_prefix(widget: Gtk.Widget): void; - add_suffix(widget: Gtk.Widget): void; - get_activatable_widget(): Gtk.Widget | null; - get_icon_name(): string | null; - get_subtitle(): string | null; - get_subtitle_lines(): number; - get_title_lines(): number; - remove(widget: Gtk.Widget): void; - set_activatable_widget(widget?: Gtk.Widget | null): void; - set_icon_name(icon_name?: string | null): void; - set_subtitle(subtitle: string): void; - set_subtitle_lines(subtitle_lines: number): void; - set_title_lines(title_lines: number): void; - vfunc_activate(): void; -} -export module Animation { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - state: AnimationState; - target: AnimationTarget; - value: number; - widget: Gtk.Widget; - } -} -export abstract class Animation extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get state(): AnimationState; - get target(): AnimationTarget; - set target(val: AnimationTarget); - get value(): number; - get widget(): Gtk.Widget; - - // Signals - - connect(id: string, callback: (...args: any[]) => any): number; - connect_after(id: string, callback: (...args: any[]) => any): number; - emit(id: string, ...args: any[]): void; - connect(signal: "done", callback: (_source: this) => void): number; - connect_after(signal: "done", callback: (_source: this) => void): number; - emit(signal: "done"): void; - - // Members - - get_state(): AnimationState; - get_target(): AnimationTarget; - get_value(): number; - get_widget(): Gtk.Widget; - pause(): void; - play(): void; - reset(): void; - resume(): void; - skip(): void; -} -export module AnimationTarget { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - } -} -export abstract class AnimationTarget extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; -} -export module Application { - export interface ConstructorProperties extends Gtk.Application.ConstructorProperties { - [key: string]: any; - style_manager: StyleManager; - styleManager: StyleManager; - } -} -export class Application extends Gtk.Application implements Gio.ActionGroup, Gio.ActionMap { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get style_manager(): StyleManager; - get styleManager(): StyleManager; - - // Constructors - - static ["new"](application_id: string | null, flags: Gio.ApplicationFlags): Application; - - // Members - - get_style_manager(): StyleManager; -} -export module ApplicationWindow { - export interface ConstructorProperties extends Gtk.ApplicationWindow.ConstructorProperties { - [key: string]: any; - content: Gtk.Widget; - } -} -export class ApplicationWindow - extends Gtk.ApplicationWindow - implements - Gio.ActionGroup, - Gio.ActionMap, - Gtk.Accessible, - Gtk.Buildable, - Gtk.ConstraintTarget, - Gtk.Native, - Gtk.Root, - Gtk.ShortcutManager { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get content(): Gtk.Widget; - set content(val: Gtk.Widget); - - // Constructors - - static ["new"](app: Gtk.Application): ApplicationWindow; - // Conflicted with Gtk.Window.new - static ["new"](...args: never[]): any; - - // Members - - get_content(): Gtk.Widget | null; - set_content(content?: Gtk.Widget | null): void; - - // Implemented Members - - action_added(action_name: string): void; - action_enabled_changed(action_name: string, enabled: boolean): void; - action_removed(action_name: string): void; - action_state_changed(action_name: string, state: GLib.Variant): void; - activate_action(action_name: string, parameter?: GLib.Variant | null): void; - // Conflicted with Gtk.Widget.activate_action - activate_action(...args: never[]): any; - change_action_state(action_name: string, value: GLib.Variant): void; - get_action_enabled(action_name: string): boolean; - get_action_parameter_type(action_name: string): GLib.VariantType | null; - get_action_state(action_name: string): GLib.Variant | null; - get_action_state_hint(action_name: string): GLib.Variant | null; - get_action_state_type(action_name: string): GLib.VariantType | null; - has_action(action_name: string): boolean; - list_actions(): string[]; - query_action( - action_name: string - ): [boolean, boolean, GLib.VariantType | null, GLib.VariantType | null, GLib.Variant | null, GLib.Variant | null]; - vfunc_action_added(action_name: string): void; - vfunc_action_enabled_changed(action_name: string, enabled: boolean): void; - vfunc_action_removed(action_name: string): void; - vfunc_action_state_changed(action_name: string, state: GLib.Variant): void; - vfunc_activate_action(action_name: string, parameter?: GLib.Variant | null): void; - vfunc_change_action_state(action_name: string, value: GLib.Variant): void; - vfunc_get_action_enabled(action_name: string): boolean; - vfunc_get_action_parameter_type(action_name: string): GLib.VariantType | null; - vfunc_get_action_state(action_name: string): GLib.Variant | null; - vfunc_get_action_state_hint(action_name: string): GLib.Variant | null; - vfunc_get_action_state_type(action_name: string): GLib.VariantType | null; - vfunc_has_action(action_name: string): boolean; - vfunc_list_actions(): string[]; - vfunc_query_action( - action_name: string - ): [boolean, boolean, GLib.VariantType | null, GLib.VariantType | null, GLib.Variant | null, GLib.Variant | null]; - add_action(action: Gio.Action): void; - add_action_entries(entries: Gio.ActionEntry[], user_data?: any | null): void; - lookup_action(action_name: string): Gio.Action | null; - remove_action(action_name: string): void; - vfunc_add_action(action: Gio.Action): void; - vfunc_lookup_action(action_name: string): Gio.Action | null; - vfunc_remove_action(action_name: string): void; -} -export module Avatar { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - custom_image: Gdk.Paintable; - customImage: Gdk.Paintable; - icon_name: string; - iconName: string; - show_initials: boolean; - showInitials: boolean; - size: number; - text: string; - } -} -export class Avatar extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get custom_image(): Gdk.Paintable; - set custom_image(val: Gdk.Paintable); - get customImage(): Gdk.Paintable; - set customImage(val: Gdk.Paintable); - get icon_name(): string; - set icon_name(val: string); - get iconName(): string; - set iconName(val: string); - get show_initials(): boolean; - set show_initials(val: boolean); - get showInitials(): boolean; - set showInitials(val: boolean); - get size(): number; - set size(val: number); - get text(): string; - set text(val: string); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](size: number, text: string | null, show_initials: boolean): Avatar; - - // Members - - draw_to_texture(scale_factor: number): Gdk.Texture; - get_custom_image(): Gdk.Paintable | null; - get_icon_name(): string | null; - get_show_initials(): boolean; - get_size(): number; - get_text(): string | null; - set_custom_image(custom_image?: Gdk.Paintable | null): void; - set_icon_name(icon_name?: string | null): void; - set_show_initials(show_initials: boolean): void; - set_size(size: number): void; - set_text(text?: string | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module Bin { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - } -} -export class Bin extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - set child(val: Gtk.Widget); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): Bin; - - // Members - - get_child(): Gtk.Widget | null; - set_child(child?: Gtk.Widget | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module ButtonContent { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - icon_name: string; - iconName: string; - label: string; - use_underline: boolean; - useUnderline: boolean; - } -} -export class ButtonContent extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get icon_name(): string; - set icon_name(val: string); - get iconName(): string; - set iconName(val: string); - get label(): string; - set label(val: string); - get use_underline(): boolean; - set use_underline(val: boolean); - get useUnderline(): boolean; - set useUnderline(val: boolean); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): ButtonContent; - - // Members - - get_icon_name(): string; - get_label(): string; - get_use_underline(): boolean; - set_icon_name(icon_name: string): void; - set_label(label: string): void; - set_use_underline(use_underline: boolean): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module CallbackAnimationTarget { - export interface ConstructorProperties extends AnimationTarget.ConstructorProperties { - [key: string]: any; - } -} -export class CallbackAnimationTarget extends AnimationTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Constructors - - static ["new"](): CallbackAnimationTarget; -} -export module Carousel { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - allow_long_swipes: boolean; - allowLongSwipes: boolean; - allow_mouse_drag: boolean; - allowMouseDrag: boolean; - allow_scroll_wheel: boolean; - allowScrollWheel: boolean; - interactive: boolean; - n_pages: number; - nPages: number; - position: number; - reveal_duration: number; - revealDuration: number; - scroll_params: SpringParams; - scrollParams: SpringParams; - spacing: number; - } -} -export class Carousel - extends Gtk.Widget - implements Swipeable, Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get allow_long_swipes(): boolean; - set allow_long_swipes(val: boolean); - get allowLongSwipes(): boolean; - set allowLongSwipes(val: boolean); - get allow_mouse_drag(): boolean; - set allow_mouse_drag(val: boolean); - get allowMouseDrag(): boolean; - set allowMouseDrag(val: boolean); - get allow_scroll_wheel(): boolean; - set allow_scroll_wheel(val: boolean); - get allowScrollWheel(): boolean; - set allowScrollWheel(val: boolean); - get interactive(): boolean; - set interactive(val: boolean); - get n_pages(): number; - get nPages(): number; - get position(): number; - get reveal_duration(): number; - set reveal_duration(val: number); - get revealDuration(): number; - set revealDuration(val: number); - get scroll_params(): SpringParams; - set scroll_params(val: SpringParams); - get scrollParams(): SpringParams; - set scrollParams(val: SpringParams); - get spacing(): number; - set spacing(val: number); - - // Signals - - connect(id: string, callback: (...args: any[]) => any): number; - connect_after(id: string, callback: (...args: any[]) => any): number; - emit(id: string, ...args: any[]): void; - connect(signal: "page-changed", callback: (_source: this, index: number) => void): number; - connect_after(signal: "page-changed", callback: (_source: this, index: number) => void): number; - emit(signal: "page-changed", index: number): void; - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](): Carousel; - - // Members - - append(child: Gtk.Widget): void; - get_allow_long_swipes(): boolean; - get_allow_mouse_drag(): boolean; - get_allow_scroll_wheel(): boolean; - get_interactive(): boolean; - get_n_pages(): number; - get_nth_page(n: number): Gtk.Widget; - get_position(): number; - get_reveal_duration(): number; - get_scroll_params(): SpringParams; - get_spacing(): number; - insert(child: Gtk.Widget, position: number): void; - prepend(child: Gtk.Widget): void; - remove(child: Gtk.Widget): void; - reorder(child: Gtk.Widget, position: number): void; - scroll_to(widget: Gtk.Widget, animate: boolean): void; - set_allow_long_swipes(allow_long_swipes: boolean): void; - set_allow_mouse_drag(allow_mouse_drag: boolean): void; - set_allow_scroll_wheel(allow_scroll_wheel: boolean): void; - set_interactive(interactive: boolean): void; - set_reveal_duration(reveal_duration: number): void; - set_scroll_params(params: SpringParams): void; - set_spacing(spacing: number): void; - - // Implemented Members - - get_cancel_progress(): number; - get_distance(): number; - get_progress(): number; - get_snap_points(): number[]; - get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; - vfunc_get_cancel_progress(): number; - vfunc_get_distance(): number; - vfunc_get_progress(): number; - vfunc_get_snap_points(): number[]; - vfunc_get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module CarouselIndicatorDots { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - carousel: Carousel; - } -} -export class CarouselIndicatorDots - extends Gtk.Widget - implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get carousel(): Carousel; - set carousel(val: Carousel); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](): CarouselIndicatorDots; - - // Members - - get_carousel(): Carousel | null; - set_carousel(carousel?: Carousel | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module CarouselIndicatorLines { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - carousel: Carousel; - } -} -export class CarouselIndicatorLines - extends Gtk.Widget - implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get carousel(): Carousel; - set carousel(val: Carousel); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](): CarouselIndicatorLines; - - // Members - - get_carousel(): Carousel | null; - set_carousel(carousel?: Carousel | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module Clamp { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - maximum_size: number; - maximumSize: number; - tightening_threshold: number; - tighteningThreshold: number; - } -} -export class Clamp extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - set child(val: Gtk.Widget); - get maximum_size(): number; - set maximum_size(val: number); - get maximumSize(): number; - set maximumSize(val: number); - get tightening_threshold(): number; - set tightening_threshold(val: number); - get tighteningThreshold(): number; - set tighteningThreshold(val: number); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](): Clamp; - - // Members - - get_child(): Gtk.Widget | null; - get_maximum_size(): number; - get_tightening_threshold(): number; - set_child(child?: Gtk.Widget | null): void; - set_maximum_size(maximum_size: number): void; - set_tightening_threshold(tightening_threshold: number): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module ClampLayout { - export interface ConstructorProperties extends Gtk.LayoutManager.ConstructorProperties { - [key: string]: any; - maximum_size: number; - maximumSize: number; - tightening_threshold: number; - tighteningThreshold: number; - } -} -export class ClampLayout extends Gtk.LayoutManager implements Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get maximum_size(): number; - set maximum_size(val: number); - get maximumSize(): number; - set maximumSize(val: number); - get tightening_threshold(): number; - set tightening_threshold(val: number); - get tighteningThreshold(): number; - set tighteningThreshold(val: number); - - // Implemented Properties - - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](): ClampLayout; - - // Members - - get_maximum_size(): number; - get_tightening_threshold(): number; - set_maximum_size(maximum_size: number): void; - set_tightening_threshold(tightening_threshold: number): void; - - // Implemented Members - - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module ClampScrollable { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - maximum_size: number; - maximumSize: number; - tightening_threshold: number; - tighteningThreshold: number; - } -} -export class ClampScrollable - extends Gtk.Widget - implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable, Gtk.Scrollable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - set child(val: Gtk.Widget); - get maximum_size(): number; - set maximum_size(val: number); - get maximumSize(): number; - set maximumSize(val: number); - get tightening_threshold(): number; - set tightening_threshold(val: number); - get tighteningThreshold(): number; - set tighteningThreshold(val: number); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - get hadjustment(): Gtk.Adjustment; - set hadjustment(val: Gtk.Adjustment); - get hscroll_policy(): Gtk.ScrollablePolicy; - set hscroll_policy(val: Gtk.ScrollablePolicy); - get hscrollPolicy(): Gtk.ScrollablePolicy; - set hscrollPolicy(val: Gtk.ScrollablePolicy); - get vadjustment(): Gtk.Adjustment; - set vadjustment(val: Gtk.Adjustment); - get vscroll_policy(): Gtk.ScrollablePolicy; - set vscroll_policy(val: Gtk.ScrollablePolicy); - get vscrollPolicy(): Gtk.ScrollablePolicy; - set vscrollPolicy(val: Gtk.ScrollablePolicy); - - // Constructors - - static ["new"](): ClampScrollable; - - // Members - - get_child(): Gtk.Widget | null; - get_maximum_size(): number; - get_tightening_threshold(): number; - set_child(child?: Gtk.Widget | null): void; - set_maximum_size(maximum_size: number): void; - set_tightening_threshold(tightening_threshold: number): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; - get_border(): [boolean, Gtk.Border]; - get_hadjustment(): Gtk.Adjustment | null; - get_hscroll_policy(): Gtk.ScrollablePolicy; - get_vadjustment(): Gtk.Adjustment | null; - get_vscroll_policy(): Gtk.ScrollablePolicy; - set_hadjustment(hadjustment?: Gtk.Adjustment | null): void; - set_hscroll_policy(policy: Gtk.ScrollablePolicy): void; - set_vadjustment(vadjustment?: Gtk.Adjustment | null): void; - set_vscroll_policy(policy: Gtk.ScrollablePolicy): void; - vfunc_get_border(): [boolean, Gtk.Border]; -} -export module ComboRow { - export interface ConstructorProperties extends ActionRow.ConstructorProperties { - [key: string]: any; - expression: Gtk.Expression; - factory: Gtk.ListItemFactory; - list_factory: Gtk.ListItemFactory; - listFactory: Gtk.ListItemFactory; - model: Gio.ListModel; - selected: number; - selected_item: GObject.Object; - selectedItem: GObject.Object; - use_subtitle: boolean; - useSubtitle: boolean; - } -} -export class ComboRow extends ActionRow implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get expression(): Gtk.Expression; - set expression(val: Gtk.Expression); - get factory(): Gtk.ListItemFactory; - set factory(val: Gtk.ListItemFactory); - get list_factory(): Gtk.ListItemFactory; - set list_factory(val: Gtk.ListItemFactory); - get listFactory(): Gtk.ListItemFactory; - set listFactory(val: Gtk.ListItemFactory); - get model(): Gio.ListModel; - set model(val: Gio.ListModel); - get selected(): number; - set selected(val: number); - get selected_item(): GObject.Object; - get selectedItem(): GObject.Object; - get use_subtitle(): boolean; - set use_subtitle(val: boolean); - get useSubtitle(): boolean; - set useSubtitle(val: boolean); - - // Constructors - - static ["new"](): ComboRow; - - // Members - - get_expression(): Gtk.Expression | null; - get_factory(): Gtk.ListItemFactory | null; - get_list_factory(): Gtk.ListItemFactory | null; - get_model(): Gio.ListModel | null; - get_selected(): number; - get_selected_item(): T; - get_use_subtitle(): boolean; - set_expression(expression?: Gtk.Expression | null): void; - set_factory(factory?: Gtk.ListItemFactory | null): void; - set_list_factory(factory?: Gtk.ListItemFactory | null): void; - set_model(model?: Gio.ListModel | null): void; - set_selected(position: number): void; - set_use_subtitle(use_subtitle: boolean): void; -} -export module EnumListItem { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - name: string; - nick: string; - value: number; - } -} -export class EnumListItem extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get name(): string; - get nick(): string; - get value(): number; - - // Members - - get_name(): string; - get_nick(): string; - get_value(): number; -} -export module EnumListModel { - export interface ConstructorProperties - extends GObject.Object.ConstructorProperties { - [key: string]: any; - enum_type: GObject.GType; - enumType: GObject.GType; - } -} -export class EnumListModel - extends GObject.Object - implements Gio.ListModel -{ - static $gtype: GObject.GType; - - constructor(properties?: Partial>, ...args: any[]); - _init(properties?: Partial>, ...args: any[]): void; - - // Properties - get enum_type(): GObject.GType; - get enumType(): GObject.GType; - - // Constructors - - static ["new"](enum_type: GObject.GType): EnumListModel; - - // Members - - find_position(value: number): number; - get_enum_type(): GObject.GType; - - // Implemented Members - - get_item_type(): GObject.GType; - get_n_items(): number; - get_item(position: number): A | null; - items_changed(position: number, removed: number, added: number): void; - vfunc_get_item(position: number): A | null; - vfunc_get_item_type(): GObject.GType; - vfunc_get_n_items(): number; -} -export module EntryRow { - export interface ConstructorProperties extends PreferencesRow.ConstructorProperties { - [key: string]: any; - activates_default: boolean; - attributes: unknown; - enable_emoji_completion: boolean; - input_hints: unknown; - input_purpose: unknown; - show_apply_button: boolean; - test: string; - } -} -export class EntryRow extends PreferencesRow implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.Editable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get activates_default(): boolean; - set activates_default(val: boolean); - get attributes(): unknown; - set attributes(val: unknown); - get enable_emoji_completion(): boolean; - set enable_emoji_completion(val: boolean); - get input_hints(): unknown; - set input_hints(val: unknown); - get input_purpose(): unknown; - set input_purpose(val: unknown); - get show_apply_button(): boolean; - set show_apply_button(val: boolean); - get text(): string; - set text(val: string); - - static ["new"](): EntryRow; - - add_prefix(val: Gtk.Widget): void; - add_suffix(val: Gtk.Widget): void; - remove(val: Gtk.Widget): void; - - // autogenerated: - - cursor_position: number; - cursorPosition: number; - editable: boolean; - enable_undo: boolean; - enableUndo: boolean; - max_width_chars: number; - maxWidthChars: number; - selection_bound: number; - selectionBound: number; - width_chars: number; - widthChars: number; - xalign: number; - delete_selection(): void; - delete_text(start_pos: number, end_pos: number): void; - finish_delegate(): void; - get_alignment(): number; - get_chars(start_pos: number, end_pos: number): string; - get_delegate(): Gtk.EditablePrototype | null; - get_editable(): boolean; - get_enable_undo(): boolean; - get_max_width_chars(): number; - get_position(): number; - get_selection_bounds(): [boolean, number, number]; - get_text(): string; - get_width_chars(): number; - init_delegate(): void; - insert_text(text: string, length: number, position: number): number; - select_region(start_pos: number, end_pos: number): void; - set_alignment(xalign: number): void; - set_editable(is_editable: boolean): void; - set_enable_undo(enable_undo: boolean): void; - set_max_width_chars(n_chars: number): void; - set_position(position: number): void; - set_text(text: string): void; - set_width_chars(n_chars: number): void; - vfunc_changed(): void; - vfunc_delete_text(start_pos: number, end_pos: number): void; - vfunc_do_delete_text(start_pos: number, end_pos: number): void; - vfunc_do_insert_text(text: string, length: number, position: number): number; - vfunc_get_delegate(): Gtk.EditablePrototype | null; - vfunc_get_selection_bounds(): [boolean, number, number]; - vfunc_get_text(): string; - vfunc_insert_text(text: string, length: number, position: number): number; - vfunc_set_selection_bounds(start_pos: number, end_pos: number): void; -} -export module ExpanderRow { - export interface ConstructorProperties extends PreferencesRow.ConstructorProperties { - [key: string]: any; - enable_expansion: boolean; - enableExpansion: boolean; - expanded: boolean; - icon_name: string; - iconName: string; - show_enable_switch: boolean; - showEnableSwitch: boolean; - subtitle: string; - } -} -export class ExpanderRow - extends PreferencesRow - implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get enable_expansion(): boolean; - set enable_expansion(val: boolean); - get enableExpansion(): boolean; - set enableExpansion(val: boolean); - get expanded(): boolean; - set expanded(val: boolean); - get icon_name(): string; - set icon_name(val: string); - get iconName(): string; - set iconName(val: string); - get show_enable_switch(): boolean; - set show_enable_switch(val: boolean); - get showEnableSwitch(): boolean; - set showEnableSwitch(val: boolean); - get subtitle(): string; - set subtitle(val: string); - - // Constructors - - static ["new"](): ExpanderRow; - - // Members - - add_action(widget: Gtk.Widget): void; - add_prefix(widget: Gtk.Widget): void; - add_row(child: Gtk.Widget): void; - get_enable_expansion(): boolean; - get_expanded(): boolean; - get_icon_name(): string | null; - get_show_enable_switch(): boolean; - get_subtitle(): string; - remove(child: Gtk.Widget): void; - set_enable_expansion(enable_expansion: boolean): void; - set_expanded(expanded: boolean): void; - set_icon_name(icon_name?: string | null): void; - set_show_enable_switch(show_enable_switch: boolean): void; - set_subtitle(subtitle: string): void; -} -export module Flap { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - content: Gtk.Widget; - flap: Gtk.Widget; - flap_position: Gtk.PackType; - flapPosition: Gtk.PackType; - fold_duration: number; - foldDuration: number; - fold_policy: FlapFoldPolicy; - foldPolicy: FlapFoldPolicy; - fold_threshold_policy: FoldThresholdPolicy; - foldThresholdPolicy: FoldThresholdPolicy; - folded: boolean; - locked: boolean; - modal: boolean; - reveal_flap: boolean; - revealFlap: boolean; - reveal_params: SpringParams; - revealParams: SpringParams; - reveal_progress: number; - revealProgress: number; - separator: Gtk.Widget; - swipe_to_close: boolean; - swipeToClose: boolean; - swipe_to_open: boolean; - swipeToOpen: boolean; - transition_type: FlapTransitionType; - transitionType: FlapTransitionType; - } -} -export class Flap - extends Gtk.Widget - implements Swipeable, Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get content(): Gtk.Widget; - set content(val: Gtk.Widget); - get flap(): Gtk.Widget; - set flap(val: Gtk.Widget); - get flap_position(): Gtk.PackType; - set flap_position(val: Gtk.PackType); - get flapPosition(): Gtk.PackType; - set flapPosition(val: Gtk.PackType); - get fold_duration(): number; - set fold_duration(val: number); - get foldDuration(): number; - set foldDuration(val: number); - get fold_policy(): FlapFoldPolicy; - set fold_policy(val: FlapFoldPolicy); - get foldPolicy(): FlapFoldPolicy; - set foldPolicy(val: FlapFoldPolicy); - get fold_threshold_policy(): FoldThresholdPolicy; - set fold_threshold_policy(val: FoldThresholdPolicy); - get foldThresholdPolicy(): FoldThresholdPolicy; - set foldThresholdPolicy(val: FoldThresholdPolicy); - get folded(): boolean; - get locked(): boolean; - set locked(val: boolean); - get modal(): boolean; - set modal(val: boolean); - get reveal_flap(): boolean; - set reveal_flap(val: boolean); - get revealFlap(): boolean; - set revealFlap(val: boolean); - get reveal_params(): SpringParams; - set reveal_params(val: SpringParams); - get revealParams(): SpringParams; - set revealParams(val: SpringParams); - get reveal_progress(): number; - get revealProgress(): number; - get separator(): Gtk.Widget; - set separator(val: Gtk.Widget); - get swipe_to_close(): boolean; - set swipe_to_close(val: boolean); - get swipeToClose(): boolean; - set swipeToClose(val: boolean); - get swipe_to_open(): boolean; - set swipe_to_open(val: boolean); - get swipeToOpen(): boolean; - set swipeToOpen(val: boolean); - get transition_type(): FlapTransitionType; - set transition_type(val: FlapTransitionType); - get transitionType(): FlapTransitionType; - set transitionType(val: FlapTransitionType); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](): Flap; - - // Members - - get_content(): Gtk.Widget | null; - get_flap(): Gtk.Widget | null; - get_flap_position(): Gtk.PackType; - get_fold_duration(): number; - get_fold_policy(): FlapFoldPolicy; - get_fold_threshold_policy(): FoldThresholdPolicy; - get_folded(): boolean; - get_locked(): boolean; - get_modal(): boolean; - get_reveal_flap(): boolean; - get_reveal_params(): SpringParams; - get_reveal_progress(): number; - get_separator(): Gtk.Widget | null; - get_swipe_to_close(): boolean; - get_swipe_to_open(): boolean; - get_transition_type(): FlapTransitionType; - set_content(content?: Gtk.Widget | null): void; - set_flap(flap?: Gtk.Widget | null): void; - set_flap_position(position: Gtk.PackType): void; - set_fold_duration(duration: number): void; - set_fold_policy(policy: FlapFoldPolicy): void; - set_fold_threshold_policy(policy: FoldThresholdPolicy): void; - set_locked(locked: boolean): void; - set_modal(modal: boolean): void; - set_reveal_flap(reveal_flap: boolean): void; - set_reveal_params(params: SpringParams): void; - set_separator(separator?: Gtk.Widget | null): void; - set_swipe_to_close(swipe_to_close: boolean): void; - set_swipe_to_open(swipe_to_open: boolean): void; - set_transition_type(transition_type: FlapTransitionType): void; - - // Implemented Members - - get_cancel_progress(): number; - get_distance(): number; - get_progress(): number; - get_snap_points(): number[]; - get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; - vfunc_get_cancel_progress(): number; - vfunc_get_distance(): number; - vfunc_get_progress(): number; - vfunc_get_snap_points(): number[]; - vfunc_get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module HeaderBar { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - centering_policy: CenteringPolicy; - centeringPolicy: CenteringPolicy; - decoration_layout: string; - decorationLayout: string; - show_end_title_buttons: boolean; - showEndTitleButtons: boolean; - show_start_title_buttons: boolean; - showStartTitleButtons: boolean; - title_widget: Gtk.Widget; - titleWidget: Gtk.Widget; - } -} -export class HeaderBar extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get centering_policy(): CenteringPolicy; - set centering_policy(val: CenteringPolicy); - get centeringPolicy(): CenteringPolicy; - set centeringPolicy(val: CenteringPolicy); - get decoration_layout(): string; - set decoration_layout(val: string); - get decorationLayout(): string; - set decorationLayout(val: string); - get show_end_title_buttons(): boolean; - set show_end_title_buttons(val: boolean); - get showEndTitleButtons(): boolean; - set showEndTitleButtons(val: boolean); - get show_start_title_buttons(): boolean; - set show_start_title_buttons(val: boolean); - get showStartTitleButtons(): boolean; - set showStartTitleButtons(val: boolean); - get title_widget(): Gtk.Widget; - set title_widget(val: Gtk.Widget); - get titleWidget(): Gtk.Widget; - set titleWidget(val: Gtk.Widget); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): HeaderBar; - - // Members - - get_centering_policy(): CenteringPolicy; - get_decoration_layout(): string | null; - get_show_end_title_buttons(): boolean; - get_show_start_title_buttons(): boolean; - get_title_widget(): Gtk.Widget | null; - pack_end(child: Gtk.Widget): void; - pack_start(child: Gtk.Widget): void; - remove(child: Gtk.Widget): void; - set_centering_policy(centering_policy: CenteringPolicy): void; - set_decoration_layout(layout?: string | null): void; - set_show_end_title_buttons(setting: boolean): void; - set_show_start_title_buttons(setting: boolean): void; - set_title_widget(title_widget?: Gtk.Widget | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module Leaflet { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - can_navigate_back: boolean; - canNavigateBack: boolean; - can_navigate_forward: boolean; - canNavigateForward: boolean; - can_unfold: boolean; - canUnfold: boolean; - child_transition_params: SpringParams; - childTransitionParams: SpringParams; - child_transition_running: boolean; - childTransitionRunning: boolean; - fold_threshold_policy: FoldThresholdPolicy; - foldThresholdPolicy: FoldThresholdPolicy; - folded: boolean; - homogeneous: boolean; - mode_transition_duration: number; - modeTransitionDuration: number; - pages: Gtk.SelectionModel; - transition_type: LeafletTransitionType; - transitionType: LeafletTransitionType; - visible_child: Gtk.Widget; - visibleChild: Gtk.Widget; - visible_child_name: string; - visibleChildName: string; - } -} -export class Leaflet - extends Gtk.Widget - implements Swipeable, Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get can_navigate_back(): boolean; - set can_navigate_back(val: boolean); - get canNavigateBack(): boolean; - set canNavigateBack(val: boolean); - get can_navigate_forward(): boolean; - set can_navigate_forward(val: boolean); - get canNavigateForward(): boolean; - set canNavigateForward(val: boolean); - get can_unfold(): boolean; - set can_unfold(val: boolean); - get canUnfold(): boolean; - set canUnfold(val: boolean); - get child_transition_params(): SpringParams; - set child_transition_params(val: SpringParams); - get childTransitionParams(): SpringParams; - set childTransitionParams(val: SpringParams); - get child_transition_running(): boolean; - get childTransitionRunning(): boolean; - get fold_threshold_policy(): FoldThresholdPolicy; - set fold_threshold_policy(val: FoldThresholdPolicy); - get foldThresholdPolicy(): FoldThresholdPolicy; - set foldThresholdPolicy(val: FoldThresholdPolicy); - get folded(): boolean; - get homogeneous(): boolean; - set homogeneous(val: boolean); - get mode_transition_duration(): number; - set mode_transition_duration(val: number); - get modeTransitionDuration(): number; - set modeTransitionDuration(val: number); - get pages(): Gtk.SelectionModel; - get transition_type(): LeafletTransitionType; - set transition_type(val: LeafletTransitionType); - get transitionType(): LeafletTransitionType; - set transitionType(val: LeafletTransitionType); - get visible_child(): Gtk.Widget; - set visible_child(val: Gtk.Widget); - get visibleChild(): Gtk.Widget; - set visibleChild(val: Gtk.Widget); - get visible_child_name(): string; - set visible_child_name(val: string); - get visibleChildName(): string; - set visibleChildName(val: string); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](): Leaflet; - - // Members - - append(child: Gtk.Widget): LeafletPage; - get_adjacent_child(direction: NavigationDirection): Gtk.Widget | null; - get_can_navigate_back(): boolean; - get_can_navigate_forward(): boolean; - get_can_unfold(): boolean; - get_child_by_name(name: string): Gtk.Widget | null; - get_child_transition_params(): SpringParams; - get_child_transition_running(): boolean; - get_fold_threshold_policy(): FoldThresholdPolicy; - get_folded(): boolean; - get_homogeneous(): boolean; - get_mode_transition_duration(): number; - get_page(child: Gtk.Widget): LeafletPage; - get_pages(): Gtk.SelectionModel; - get_transition_type(): LeafletTransitionType; - get_visible_child(): Gtk.Widget | null; - get_visible_child_name(): string | null; - insert_child_after(child: Gtk.Widget, sibling?: Gtk.Widget | null): LeafletPage; - navigate(direction: NavigationDirection): boolean; - prepend(child: Gtk.Widget): LeafletPage; - remove(child: Gtk.Widget): void; - reorder_child_after(child: Gtk.Widget, sibling?: Gtk.Widget | null): void; - set_can_navigate_back(can_navigate_back: boolean): void; - set_can_navigate_forward(can_navigate_forward: boolean): void; - set_can_unfold(can_unfold: boolean): void; - set_child_transition_params(params: SpringParams): void; - set_fold_threshold_policy(policy: FoldThresholdPolicy): void; - set_homogeneous(homogeneous: boolean): void; - set_mode_transition_duration(duration: number): void; - set_transition_type(transition: LeafletTransitionType): void; - set_visible_child(visible_child: Gtk.Widget): void; - set_visible_child_name(name: string): void; - - // Implemented Members - - get_cancel_progress(): number; - get_distance(): number; - get_progress(): number; - get_snap_points(): number[]; - get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; - vfunc_get_cancel_progress(): number; - vfunc_get_distance(): number; - vfunc_get_progress(): number; - vfunc_get_snap_points(): number[]; - vfunc_get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module LeafletPage { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - name: string; - navigatable: boolean; - } -} -export class LeafletPage extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - get name(): string; - set name(val: string); - get navigatable(): boolean; - set navigatable(val: boolean); - - // Members - - get_child(): Gtk.Widget; - get_name(): string | null; - get_navigatable(): boolean; - set_name(name?: string | null): void; - set_navigatable(navigatable: boolean): void; -} -export module PreferencesGroup { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - description: string; - header_suffix: Gtk.Widget; - headerSuffix: Gtk.Widget; - title: string; - } -} -export class PreferencesGroup extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get description(): string; - set description(val: string); - get header_suffix(): Gtk.Widget; - set header_suffix(val: Gtk.Widget); - get headerSuffix(): Gtk.Widget; - set headerSuffix(val: Gtk.Widget); - get title(): string; - set title(val: string); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): PreferencesGroup; - - // Members - - add(child: Gtk.Widget): void; - get_description(): string | null; - get_header_suffix(): Gtk.Widget | null; - get_title(): string; - remove(child: Gtk.Widget): void; - set_description(description?: string | null): void; - set_header_suffix(suffix?: Gtk.Widget | null): void; - set_title(title: string): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module PreferencesPage { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - icon_name: string; - iconName: string; - name: string; - title: string; - use_underline: boolean; - useUnderline: boolean; - } -} -export class PreferencesPage extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get icon_name(): string; - set icon_name(val: string); - get iconName(): string; - set iconName(val: string); - get name(): string; - set name(val: string); - get title(): string; - set title(val: string); - get use_underline(): boolean; - set use_underline(val: boolean); - get useUnderline(): boolean; - set useUnderline(val: boolean); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): PreferencesPage; - - // Members - - add(group: PreferencesGroup): void; - get_icon_name(): string | null; - get_name(): string | null; - // Conflicted with Gtk.Widget.get_name - get_name(...args: never[]): any; - get_title(): string; - get_use_underline(): boolean; - remove(group: PreferencesGroup): void; - set_icon_name(icon_name?: string | null): void; - set_name(name?: string | null): void; - // Conflicted with Gtk.Widget.set_name - set_name(...args: never[]): any; - set_title(title: string): void; - set_use_underline(use_underline: boolean): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module PreferencesRow { - export interface ConstructorProperties extends Gtk.ListBoxRow.ConstructorProperties { - [key: string]: any; - title: string; - title_selectable: boolean; - titleSelectable: boolean; - use_underline: boolean; - useUnderline: boolean; - } -} -export class PreferencesRow - extends Gtk.ListBoxRow - implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get title(): string; - set title(val: string); - get title_selectable(): boolean; - set title_selectable(val: boolean); - get titleSelectable(): boolean; - set titleSelectable(val: boolean); - get use_underline(): boolean; - set use_underline(val: boolean); - get useUnderline(): boolean; - set useUnderline(val: boolean); - - // Implemented Properties - - get action_name(): string; - set action_name(val: string); - get actionName(): string; - set actionName(val: string); - get action_target(): GLib.Variant; - set action_target(val: GLib.Variant); - get actionTarget(): GLib.Variant; - set actionTarget(val: GLib.Variant); - - // Constructors - - static ["new"](): PreferencesRow; - - // Members - - get_title(): string; - get_title_selectable(): boolean; - get_use_underline(): boolean; - set_title(title: string): void; - set_title_selectable(title_selectable: boolean): void; - set_use_underline(use_underline: boolean): void; - - // Implemented Members - - get_action_name(): string | null; - get_action_target_value(): GLib.Variant | null; - set_action_name(action_name?: string | null): void; - set_action_target_value(target_value?: GLib.Variant | null): void; - set_detailed_action_name(detailed_action_name: string): void; - vfunc_get_action_name(): string | null; - vfunc_get_action_target_value(): GLib.Variant | null; - vfunc_set_action_name(action_name?: string | null): void; - vfunc_set_action_target_value(target_value?: GLib.Variant | null): void; -} -export module PreferencesWindow { - export interface ConstructorProperties extends Window.ConstructorProperties { - [key: string]: any; - can_navigate_back: boolean; - canNavigateBack: boolean; - search_enabled: boolean; - searchEnabled: boolean; - visible_page: Gtk.Widget; - visiblePage: Gtk.Widget; - visible_page_name: string; - visiblePageName: string; - } -} -export class PreferencesWindow - extends Window - implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Native, Gtk.Root, Gtk.ShortcutManager { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get can_navigate_back(): boolean; - set can_navigate_back(val: boolean); - get canNavigateBack(): boolean; - set canNavigateBack(val: boolean); - get search_enabled(): boolean; - set search_enabled(val: boolean); - get searchEnabled(): boolean; - set searchEnabled(val: boolean); - get visible_page(): Gtk.Widget; - set visible_page(val: Gtk.Widget); - get visiblePage(): Gtk.Widget; - set visiblePage(val: Gtk.Widget); - get visible_page_name(): string; - set visible_page_name(val: string); - get visiblePageName(): string; - set visiblePageName(val: string); - - // Constructors - - static ["new"](): PreferencesWindow; - - // Members - - add(page: PreferencesPage): void; - add_toast(toast: Toast): void; - close_subpage(): void; - get_can_navigate_back(): boolean; - get_search_enabled(): boolean; - get_visible_page(): PreferencesPage | null; - get_visible_page_name(): string | null; - present_subpage(subpage: Gtk.Widget): void; - remove(page: PreferencesPage): void; - set_can_navigate_back(can_navigate_back: boolean): void; - set_search_enabled(search_enabled: boolean): void; - set_visible_page(page: PreferencesPage): void; - set_visible_page_name(name: string): void; -} -export module SplitButton { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - direction: Gtk.ArrowType; - icon_name: string; - iconName: string; - label: string; - menu_model: Gio.MenuModel; - menuModel: Gio.MenuModel; - popover: Gtk.Popover; - use_underline: boolean; - useUnderline: boolean; - } -} -export class SplitButton - extends Gtk.Widget - implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - set child(val: Gtk.Widget); - get direction(): Gtk.ArrowType; - set direction(val: Gtk.ArrowType); - get icon_name(): string; - set icon_name(val: string); - get iconName(): string; - set iconName(val: string); - get label(): string; - set label(val: string); - get menu_model(): Gio.MenuModel; - set menu_model(val: Gio.MenuModel); - get menuModel(): Gio.MenuModel; - set menuModel(val: Gio.MenuModel); - get popover(): Gtk.Popover; - set popover(val: Gtk.Popover); - get use_underline(): boolean; - set use_underline(val: boolean); - get useUnderline(): boolean; - set useUnderline(val: boolean); - - // Signals - - connect(id: string, callback: (...args: any[]) => any): number; - connect_after(id: string, callback: (...args: any[]) => any): number; - emit(id: string, ...args: any[]): void; - connect(signal: "activate", callback: (_source: this) => void): number; - connect_after(signal: "activate", callback: (_source: this) => void): number; - emit(signal: "activate"): void; - connect(signal: "clicked", callback: (_source: this) => void): number; - connect_after(signal: "clicked", callback: (_source: this) => void): number; - emit(signal: "clicked"): void; - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get action_name(): string; - set action_name(val: string); - get actionName(): string; - set actionName(val: string); - get action_target(): GLib.Variant; - set action_target(val: GLib.Variant); - get actionTarget(): GLib.Variant; - set actionTarget(val: GLib.Variant); - - // Constructors - - static ["new"](): SplitButton; - - // Members - - get_child(): Gtk.Widget | null; - get_direction(): Gtk.ArrowType; - // Conflicted with Gtk.Widget.get_direction - get_direction(...args: never[]): any; - get_icon_name(): string | null; - get_label(): string | null; - get_menu_model(): Gio.MenuModel | null; - get_popover(): Gtk.Popover | null; - get_use_underline(): boolean; - popdown(): void; - popup(): void; - set_child(child?: Gtk.Widget | null): void; - set_direction(direction: Gtk.ArrowType): void; - // Conflicted with Gtk.Widget.set_direction - set_direction(...args: never[]): any; - set_icon_name(icon_name: string): void; - set_label(label: string): void; - set_menu_model(menu_model?: Gio.MenuModel | null): void; - set_popover(popover?: Gtk.Popover | null): void; - set_use_underline(use_underline: boolean): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_action_name(): string | null; - get_action_target_value(): GLib.Variant | null; - set_action_name(action_name?: string | null): void; - set_action_target_value(target_value?: GLib.Variant | null): void; - set_detailed_action_name(detailed_action_name: string): void; - vfunc_get_action_name(): string | null; - vfunc_get_action_target_value(): GLib.Variant | null; - vfunc_set_action_name(action_name?: string | null): void; - vfunc_set_action_target_value(target_value?: GLib.Variant | null): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module SpringAnimation { - export interface ConstructorProperties extends Animation.ConstructorProperties { - [key: string]: any; - clamp: boolean; - epsilon: number; - estimated_duration: number; - estimatedDuration: number; - initial_velocity: number; - initialVelocity: number; - spring_params: SpringParams; - springParams: SpringParams; - value_from: number; - valueFrom: number; - value_to: number; - valueTo: number; - velocity: number; - } -} -export class SpringAnimation extends Animation { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get clamp(): boolean; - set clamp(val: boolean); - get epsilon(): number; - set epsilon(val: number); - get estimated_duration(): number; - get estimatedDuration(): number; - get initial_velocity(): number; - set initial_velocity(val: number); - get initialVelocity(): number; - set initialVelocity(val: number); - get spring_params(): SpringParams; - set spring_params(val: SpringParams); - get springParams(): SpringParams; - set springParams(val: SpringParams); - get value_from(): number; - set value_from(val: number); - get valueFrom(): number; - set valueFrom(val: number); - get value_to(): number; - set value_to(val: number); - get valueTo(): number; - set valueTo(val: number); - get velocity(): number; - - // Constructors - - static ["new"]( - widget: Gtk.Widget, - from: number, - to: number, - spring_params: SpringParams, - target: AnimationTarget - ): SpringAnimation; - - // Members - - get_clamp(): boolean; - get_epsilon(): number; - get_estimated_duration(): number; - get_initial_velocity(): number; - get_spring_params(): SpringParams; - get_value_from(): number; - get_value_to(): number; - get_velocity(): number; - set_clamp(clamp: boolean): void; - set_epsilon(epsilon: number): void; - set_initial_velocity(velocity: number): void; - set_spring_params(spring_params: SpringParams): void; - set_value_from(value: number): void; - set_value_to(value: number): void; -} -export module Squeezer { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - allow_none: boolean; - allowNone: boolean; - homogeneous: boolean; - interpolate_size: boolean; - interpolateSize: boolean; - pages: Gtk.SelectionModel; - switch_threshold_policy: FoldThresholdPolicy; - switchThresholdPolicy: FoldThresholdPolicy; - transition_duration: number; - transitionDuration: number; - transition_running: boolean; - transitionRunning: boolean; - transition_type: SqueezerTransitionType; - transitionType: SqueezerTransitionType; - visible_child: Gtk.Widget; - visibleChild: Gtk.Widget; - xalign: number; - yalign: number; - } -} -export class Squeezer - extends Gtk.Widget - implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get allow_none(): boolean; - set allow_none(val: boolean); - get allowNone(): boolean; - set allowNone(val: boolean); - get homogeneous(): boolean; - set homogeneous(val: boolean); - get interpolate_size(): boolean; - set interpolate_size(val: boolean); - get interpolateSize(): boolean; - set interpolateSize(val: boolean); - get pages(): Gtk.SelectionModel; - get switch_threshold_policy(): FoldThresholdPolicy; - set switch_threshold_policy(val: FoldThresholdPolicy); - get switchThresholdPolicy(): FoldThresholdPolicy; - set switchThresholdPolicy(val: FoldThresholdPolicy); - get transition_duration(): number; - set transition_duration(val: number); - get transitionDuration(): number; - set transitionDuration(val: number); - get transition_running(): boolean; - get transitionRunning(): boolean; - get transition_type(): SqueezerTransitionType; - set transition_type(val: SqueezerTransitionType); - get transitionType(): SqueezerTransitionType; - set transitionType(val: SqueezerTransitionType); - get visible_child(): Gtk.Widget; - get visibleChild(): Gtk.Widget; - get xalign(): number; - set xalign(val: number); - get yalign(): number; - set yalign(val: number); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](): Squeezer; - - // Members - - add(child: Gtk.Widget): SqueezerPage; - get_allow_none(): boolean; - get_homogeneous(): boolean; - get_interpolate_size(): boolean; - get_page(child: Gtk.Widget): SqueezerPage; - get_pages(): Gtk.SelectionModel; - get_switch_threshold_policy(): FoldThresholdPolicy; - get_transition_duration(): number; - get_transition_running(): boolean; - get_transition_type(): SqueezerTransitionType; - get_visible_child(): Gtk.Widget | null; - get_xalign(): number; - get_yalign(): number; - remove(child: Gtk.Widget): void; - set_allow_none(allow_none: boolean): void; - set_homogeneous(homogeneous: boolean): void; - set_interpolate_size(interpolate_size: boolean): void; - set_switch_threshold_policy(policy: FoldThresholdPolicy): void; - set_transition_duration(duration: number): void; - set_transition_type(transition: SqueezerTransitionType): void; - set_xalign(xalign: number): void; - set_yalign(yalign: number): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module SqueezerPage { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - enabled: boolean; - } -} -export class SqueezerPage extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - get enabled(): boolean; - set enabled(val: boolean); - - // Members - - get_child(): Gtk.Widget; - get_enabled(): boolean; - set_enabled(enabled: boolean): void; -} -export module StatusPage { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - description: string; - icon_name: string; - iconName: string; - paintable: Gdk.Paintable; - title: string; - } -} -export class StatusPage extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - set child(val: Gtk.Widget); - get description(): string; - set description(val: string); - get icon_name(): string; - set icon_name(val: string); - get iconName(): string; - set iconName(val: string); - get paintable(): Gdk.Paintable; - set paintable(val: Gdk.Paintable); - get title(): string; - set title(val: string); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): StatusPage; - - // Members - - get_child(): Gtk.Widget | null; - get_description(): string | null; - get_icon_name(): string | null; - get_paintable(): Gdk.Paintable | null; - get_title(): string; - set_child(child?: Gtk.Widget | null): void; - set_description(description?: string | null): void; - set_icon_name(icon_name?: string | null): void; - set_paintable(paintable?: Gdk.Paintable | null): void; - set_title(title: string): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module StyleManager { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - color_scheme: ColorScheme; - colorScheme: ColorScheme; - dark: boolean; - display: Gdk.Display; - high_contrast: boolean; - highContrast: boolean; - system_supports_color_schemes: boolean; - systemSupportsColorSchemes: boolean; - } -} -export class StyleManager extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get color_scheme(): ColorScheme; - set color_scheme(val: ColorScheme); - get colorScheme(): ColorScheme; - set colorScheme(val: ColorScheme); - get dark(): boolean; - get display(): Gdk.Display; - get high_contrast(): boolean; - get highContrast(): boolean; - get system_supports_color_schemes(): boolean; - get systemSupportsColorSchemes(): boolean; - - // Members - - get_color_scheme(): ColorScheme; - get_dark(): boolean; - get_display(): Gdk.Display; - get_high_contrast(): boolean; - get_system_supports_color_schemes(): boolean; - set_color_scheme(color_scheme: ColorScheme): void; - static get_default(): StyleManager; - static get_for_display(display: Gdk.Display): StyleManager; -} -export module SwipeTracker { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - allow_long_swipes: boolean; - allowLongSwipes: boolean; - allow_mouse_drag: boolean; - allowMouseDrag: boolean; - enabled: boolean; - reversed: boolean; - swipeable: Swipeable; - } -} -export class SwipeTracker extends GObject.Object implements Gtk.Orientable { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get allow_long_swipes(): boolean; - set allow_long_swipes(val: boolean); - get allowLongSwipes(): boolean; - set allowLongSwipes(val: boolean); - get allow_mouse_drag(): boolean; - set allow_mouse_drag(val: boolean); - get allowMouseDrag(): boolean; - set allowMouseDrag(val: boolean); - get enabled(): boolean; - set enabled(val: boolean); - get reversed(): boolean; - set reversed(val: boolean); - get swipeable(): Swipeable; - - // Signals - - connect(id: string, callback: (...args: any[]) => any): number; - connect_after(id: string, callback: (...args: any[]) => any): number; - emit(id: string, ...args: any[]): void; - connect(signal: "begin-swipe", callback: (_source: this) => void): number; - connect_after(signal: "begin-swipe", callback: (_source: this) => void): number; - emit(signal: "begin-swipe"): void; - connect(signal: "end-swipe", callback: (_source: this, velocity: number, to: number) => void): number; - connect_after(signal: "end-swipe", callback: (_source: this, velocity: number, to: number) => void): number; - emit(signal: "end-swipe", velocity: number, to: number): void; - connect(signal: "prepare", callback: (_source: this, direction: NavigationDirection) => void): number; - connect_after(signal: "prepare", callback: (_source: this, direction: NavigationDirection) => void): number; - emit(signal: "prepare", direction: NavigationDirection): void; - connect(signal: "update-swipe", callback: (_source: this, progress: number) => void): number; - connect_after(signal: "update-swipe", callback: (_source: this, progress: number) => void): number; - emit(signal: "update-swipe", progress: number): void; - - // Implemented Properties - - get orientation(): Gtk.Orientation; - set orientation(val: Gtk.Orientation); - - // Constructors - - static ["new"](swipeable: Swipeable): SwipeTracker; - - // Members - - get_allow_long_swipes(): boolean; - get_allow_mouse_drag(): boolean; - get_enabled(): boolean; - get_reversed(): boolean; - get_swipeable(): Swipeable; - set_allow_long_swipes(allow_long_swipes: boolean): void; - set_allow_mouse_drag(allow_mouse_drag: boolean): void; - set_enabled(enabled: boolean): void; - set_reversed(reversed: boolean): void; - shift_position(delta: number): void; - - // Implemented Members - - get_orientation(): Gtk.Orientation; - set_orientation(orientation: Gtk.Orientation): void; -} -export module TabBar { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - autohide: boolean; - end_action_widget: Gtk.Widget; - endActionWidget: Gtk.Widget; - expand_tabs: boolean; - expandTabs: boolean; - inverted: boolean; - is_overflowing: boolean; - isOverflowing: boolean; - start_action_widget: Gtk.Widget; - startActionWidget: Gtk.Widget; - tabs_revealed: boolean; - tabsRevealed: boolean; - view: TabView; - } -} -export class TabBar extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get autohide(): boolean; - set autohide(val: boolean); - get end_action_widget(): Gtk.Widget; - set end_action_widget(val: Gtk.Widget); - get endActionWidget(): Gtk.Widget; - set endActionWidget(val: Gtk.Widget); - get expand_tabs(): boolean; - set expand_tabs(val: boolean); - get expandTabs(): boolean; - set expandTabs(val: boolean); - get inverted(): boolean; - set inverted(val: boolean); - get is_overflowing(): boolean; - get isOverflowing(): boolean; - get start_action_widget(): Gtk.Widget; - set start_action_widget(val: Gtk.Widget); - get startActionWidget(): Gtk.Widget; - set startActionWidget(val: Gtk.Widget); - get tabs_revealed(): boolean; - get tabsRevealed(): boolean; - get view(): TabView; - set view(val: TabView); - - // Signals - - connect(id: string, callback: (...args: any[]) => any): number; - connect_after(id: string, callback: (...args: any[]) => any): number; - emit(id: string, ...args: any[]): void; - connect( - signal: "extra-drag-drop", - callback: (_source: this, page: TabPage, value: GObject.Value) => boolean - ): number; - connect_after( - signal: "extra-drag-drop", - callback: (_source: this, page: TabPage, value: GObject.Value) => boolean - ): number; - emit(signal: "extra-drag-drop", page: TabPage, value: GObject.Value | any): void; - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): TabBar; - - // Members - - get_autohide(): boolean; - get_end_action_widget(): Gtk.Widget | null; - get_expand_tabs(): boolean; - get_inverted(): boolean; - get_is_overflowing(): boolean; - get_start_action_widget(): Gtk.Widget | null; - get_tabs_revealed(): boolean; - get_view(): TabView | null; - set_autohide(autohide: boolean): void; - set_end_action_widget(widget?: Gtk.Widget | null): void; - set_expand_tabs(expand_tabs: boolean): void; - set_inverted(inverted: boolean): void; - set_start_action_widget(widget?: Gtk.Widget | null): void; - set_view(view?: TabView | null): void; - setup_extra_drop_target(actions: Gdk.DragAction, types?: GObject.GType[] | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module TabPage { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - icon: Gio.Icon; - indicator_activatable: boolean; - indicatorActivatable: boolean; - indicator_icon: Gio.Icon; - indicatorIcon: Gio.Icon; - loading: boolean; - needs_attention: boolean; - needsAttention: boolean; - pinned: boolean; - selected: boolean; - title: string; - tooltip: string; - } -} -export class TabPage extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - get icon(): Gio.Icon; - set icon(val: Gio.Icon); - get indicator_activatable(): boolean; - set indicator_activatable(val: boolean); - get indicatorActivatable(): boolean; - set indicatorActivatable(val: boolean); - get indicator_icon(): Gio.Icon; - set indicator_icon(val: Gio.Icon); - get indicatorIcon(): Gio.Icon; - set indicatorIcon(val: Gio.Icon); - get loading(): boolean; - set loading(val: boolean); - get needs_attention(): boolean; - set needs_attention(val: boolean); - get needsAttention(): boolean; - set needsAttention(val: boolean); - get pinned(): boolean; - get selected(): boolean; - get title(): string; - set title(val: string); - get tooltip(): string; - set tooltip(val: string); - - // Members - - get_child(): Gtk.Widget; - get_icon(): Gio.Icon | null; - get_indicator_activatable(): boolean; - get_indicator_icon(): Gio.Icon | null; - get_loading(): boolean; - get_needs_attention(): boolean; - get_parent(): TabPage | null; - get_pinned(): boolean; - get_selected(): boolean; - get_title(): string; - get_tooltip(): string | null; - set_icon(icon?: Gio.Icon | null): void; - set_indicator_activatable(activatable: boolean): void; - set_indicator_icon(indicator_icon?: Gio.Icon | null): void; - set_loading(loading: boolean): void; - set_needs_attention(needs_attention: boolean): void; - set_title(title: string): void; - set_tooltip(tooltip: string): void; -} -export module TabView { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - default_icon: Gio.Icon; - defaultIcon: Gio.Icon; - is_transferring_page: boolean; - isTransferringPage: boolean; - menu_model: Gio.MenuModel; - menuModel: Gio.MenuModel; - n_pages: number; - nPages: number; - n_pinned_pages: number; - nPinnedPages: number; - pages: Gtk.SelectionModel; - selected_page: TabPage; - selectedPage: TabPage; - } -} -export class TabView extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get default_icon(): Gio.Icon; - set default_icon(val: Gio.Icon); - get defaultIcon(): Gio.Icon; - set defaultIcon(val: Gio.Icon); - get is_transferring_page(): boolean; - get isTransferringPage(): boolean; - get menu_model(): Gio.MenuModel; - set menu_model(val: Gio.MenuModel); - get menuModel(): Gio.MenuModel; - set menuModel(val: Gio.MenuModel); - get n_pages(): number; - get nPages(): number; - get n_pinned_pages(): number; - get nPinnedPages(): number; - get pages(): Gtk.SelectionModel; - get selected_page(): TabPage; - set selected_page(val: TabPage); - get selectedPage(): TabPage; - set selectedPage(val: TabPage); - - // Signals - - connect(id: string, callback: (...args: any[]) => any): number; - connect_after(id: string, callback: (...args: any[]) => any): number; - emit(id: string, ...args: any[]): void; - connect(signal: "close-page", callback: (_source: this, page: TabPage) => boolean): number; - connect_after(signal: "close-page", callback: (_source: this, page: TabPage) => boolean): number; - emit(signal: "close-page", page: TabPage): void; - connect(signal: "create-window", callback: (_source: this) => TabView | null): number; - connect_after(signal: "create-window", callback: (_source: this) => TabView | null): number; - emit(signal: "create-window"): void; - connect(signal: "indicator-activated", callback: (_source: this, page: TabPage) => void): number; - connect_after(signal: "indicator-activated", callback: (_source: this, page: TabPage) => void): number; - emit(signal: "indicator-activated", page: TabPage): void; - connect(signal: "page-attached", callback: (_source: this, page: TabPage, position: number) => void): number; - connect_after(signal: "page-attached", callback: (_source: this, page: TabPage, position: number) => void): number; - emit(signal: "page-attached", page: TabPage, position: number): void; - connect(signal: "page-detached", callback: (_source: this, page: TabPage, position: number) => void): number; - connect_after(signal: "page-detached", callback: (_source: this, page: TabPage, position: number) => void): number; - emit(signal: "page-detached", page: TabPage, position: number): void; - connect(signal: "page-reordered", callback: (_source: this, page: TabPage, position: number) => void): number; - connect_after(signal: "page-reordered", callback: (_source: this, page: TabPage, position: number) => void): number; - emit(signal: "page-reordered", page: TabPage, position: number): void; - connect(signal: "setup-menu", callback: (_source: this, page: TabPage | null) => void): number; - connect_after(signal: "setup-menu", callback: (_source: this, page: TabPage | null) => void): number; - emit(signal: "setup-menu", page: TabPage | null): void; - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): TabView; - - // Members - - add_page(child: Gtk.Widget, parent?: TabPage | null): TabPage; - append(child: Gtk.Widget): TabPage; - append_pinned(child: Gtk.Widget): TabPage; - close_other_pages(page: TabPage): void; - close_page(page: TabPage): void; - close_page_finish(page: TabPage, confirm: boolean): void; - close_pages_after(page: TabPage): void; - close_pages_before(page: TabPage): void; - get_default_icon(): Gio.Icon; - get_is_transferring_page(): boolean; - get_menu_model(): Gio.MenuModel | null; - get_n_pages(): number; - get_n_pinned_pages(): number; - get_nth_page(position: number): TabPage; - get_page(child: Gtk.Widget): TabPage; - get_page_position(page: TabPage): number; - get_pages(): Gtk.SelectionModel; - get_selected_page(): TabPage | null; - insert(child: Gtk.Widget, position: number): TabPage; - insert_pinned(child: Gtk.Widget, position: number): TabPage; - prepend(child: Gtk.Widget): TabPage; - prepend_pinned(child: Gtk.Widget): TabPage; - reorder_backward(page: TabPage): boolean; - reorder_first(page: TabPage): boolean; - reorder_forward(page: TabPage): boolean; - reorder_last(page: TabPage): boolean; - reorder_page(page: TabPage, position: number): boolean; - select_next_page(): boolean; - select_previous_page(): boolean; - set_default_icon(default_icon: Gio.Icon): void; - set_menu_model(menu_model?: Gio.MenuModel | null): void; - set_page_pinned(page: TabPage, pinned: boolean): void; - set_selected_page(selected_page: TabPage): void; - transfer_page(page: TabPage, other_view: TabView, position: number): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module TimedAnimation { - export interface ConstructorProperties extends Animation.ConstructorProperties { - [key: string]: any; - alternate: boolean; - duration: number; - easing: Easing; - repeat_count: number; - repeatCount: number; - reverse: boolean; - value_from: number; - valueFrom: number; - value_to: number; - valueTo: number; - } -} -export class TimedAnimation extends Animation { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get alternate(): boolean; - set alternate(val: boolean); - get duration(): number; - set duration(val: number); - get easing(): Easing; - set easing(val: Easing); - get repeat_count(): number; - set repeat_count(val: number); - get repeatCount(): number; - set repeatCount(val: number); - get reverse(): boolean; - set reverse(val: boolean); - get value_from(): number; - set value_from(val: number); - get valueFrom(): number; - set valueFrom(val: number); - get value_to(): number; - set value_to(val: number); - get valueTo(): number; - set valueTo(val: number); - - // Constructors - - static ["new"]( - widget: Gtk.Widget, - from: number, - to: number, - duration: number, - target: AnimationTarget - ): TimedAnimation; - - // Members - - get_alternate(): boolean; - get_duration(): number; - get_easing(): Easing; - get_repeat_count(): number; - get_reverse(): boolean; - get_value_from(): number; - get_value_to(): number; - set_alternate(alternate: boolean): void; - set_duration(duration: number): void; - set_easing(easing: Easing): void; - set_repeat_count(repeat_count: number): void; - set_reverse(reverse: boolean): void; - set_value_from(value: number): void; - set_value_to(value: number): void; -} -export module Toast { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - action_name: string; - actionName: string; - action_target: GLib.Variant; - actionTarget: GLib.Variant; - button_label: string; - buttonLabel: string; - priority: ToastPriority; - timeout: number; - title: string; - } -} -export class Toast extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get action_name(): string; - set action_name(val: string); - get actionName(): string; - set actionName(val: string); - get action_target(): GLib.Variant; - set action_target(val: GLib.Variant); - get actionTarget(): GLib.Variant; - set actionTarget(val: GLib.Variant); - get button_label(): string; - set button_label(val: string); - get buttonLabel(): string; - set buttonLabel(val: string); - get priority(): ToastPriority; - set priority(val: ToastPriority); - get timeout(): number; - set timeout(val: number); - get title(): string; - set title(val: string); - - // Signals - - connect(id: string, callback: (...args: any[]) => any): number; - connect_after(id: string, callback: (...args: any[]) => any): number; - emit(id: string, ...args: any[]): void; - connect(signal: "dismissed", callback: (_source: this) => void): number; - connect_after(signal: "dismissed", callback: (_source: this) => void): number; - emit(signal: "dismissed"): void; - - // Constructors - - static ["new"](title: string): Toast; - - // Members - - dismiss(): void; - get_action_name(): string | null; - get_action_target_value(): GLib.Variant | null; - get_button_label(): string | null; - get_priority(): ToastPriority; - get_timeout(): number; - get_title(): string; - set_action_name(action_name?: string | null): void; - set_action_target_value(action_target?: GLib.Variant | null): void; - set_button_label(button_label?: string | null): void; - set_detailed_action_name(detailed_action_name?: string | null): void; - set_priority(priority: ToastPriority): void; - set_timeout(timeout: number): void; - set_title(title: string): void; -} -export module ToastOverlay { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - child: Gtk.Widget; - } -} -export class ToastOverlay extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get child(): Gtk.Widget; - set child(val: Gtk.Widget); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): ToastOverlay; - - // Members - - add_toast(toast: Toast): void; - get_child(): Gtk.Widget | null; - set_child(child?: Gtk.Widget | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module ViewStack { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - hhomogeneous: boolean; - pages: Gtk.SelectionModel; - vhomogeneous: boolean; - visible_child: Gtk.Widget; - visibleChild: Gtk.Widget; - visible_child_name: string; - visibleChildName: string; - } -} -export class ViewStack extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get hhomogeneous(): boolean; - set hhomogeneous(val: boolean); - get pages(): Gtk.SelectionModel; - get vhomogeneous(): boolean; - set vhomogeneous(val: boolean); - get visible_child(): Gtk.Widget; - set visible_child(val: Gtk.Widget); - get visibleChild(): Gtk.Widget; - set visibleChild(val: Gtk.Widget); - get visible_child_name(): string; - set visible_child_name(val: string); - get visibleChildName(): string; - set visibleChildName(val: string); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): ViewStack; - - // Members - - add(child: Gtk.Widget): ViewStackPage; - add_named(child: Gtk.Widget, name?: string | null): ViewStackPage; - add_titled(child: Gtk.Widget, name: string | null, title: string): ViewStackPage; - get_child_by_name(name: string): Gtk.Widget | null; - get_hhomogeneous(): boolean; - get_page(child: Gtk.Widget): ViewStackPage; - get_pages(): Gtk.SelectionModel; - get_vhomogeneous(): boolean; - get_visible_child(): Gtk.Widget | null; - get_visible_child_name(): string | null; - remove(child: Gtk.Widget): void; - set_hhomogeneous(hhomogeneous: boolean): void; - set_vhomogeneous(vhomogeneous: boolean): void; - set_visible_child(child: Gtk.Widget): void; - set_visible_child_name(name: string): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module ViewStackPage { - export interface ConstructorProperties extends GObject.Object.ConstructorProperties { - [key: string]: any; - badge_number: number; - badgeNumber: number; - child: Gtk.Widget; - icon_name: string; - iconName: string; - name: string; - needs_attention: boolean; - needsAttention: boolean; - title: string; - use_underline: boolean; - useUnderline: boolean; - visible: boolean; - } -} -export class ViewStackPage extends GObject.Object { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get badge_number(): number; - set badge_number(val: number); - get badgeNumber(): number; - set badgeNumber(val: number); - get child(): Gtk.Widget; - get icon_name(): string; - set icon_name(val: string); - get iconName(): string; - set iconName(val: string); - get name(): string; - set name(val: string); - get needs_attention(): boolean; - set needs_attention(val: boolean); - get needsAttention(): boolean; - set needsAttention(val: boolean); - get title(): string; - set title(val: string); - get use_underline(): boolean; - set use_underline(val: boolean); - get useUnderline(): boolean; - set useUnderline(val: boolean); - get visible(): boolean; - set visible(val: boolean); - - // Members - - get_badge_number(): number; - get_child(): Gtk.Widget; - get_icon_name(): string | null; - get_name(): string | null; - get_needs_attention(): boolean; - get_title(): string | null; - get_use_underline(): boolean; - get_visible(): boolean; - set_badge_number(badge_number: number): void; - set_icon_name(icon_name?: string | null): void; - set_name(name?: string | null): void; - set_needs_attention(needs_attention: boolean): void; - set_title(title?: string | null): void; - set_use_underline(use_underline: boolean): void; - set_visible(visible: boolean): void; -} -export module ViewSwitcher { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - policy: ViewSwitcherPolicy; - stack: ViewStack; - } -} -export class ViewSwitcher extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get policy(): ViewSwitcherPolicy; - set policy(val: ViewSwitcherPolicy); - get stack(): ViewStack; - set stack(val: ViewStack); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): ViewSwitcher; - - // Members - - get_policy(): ViewSwitcherPolicy; - get_stack(): ViewStack | null; - set_policy(policy: ViewSwitcherPolicy): void; - set_stack(stack?: ViewStack | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module ViewSwitcherBar { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - reveal: boolean; - stack: ViewStack; - } -} -export class ViewSwitcherBar extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get reveal(): boolean; - set reveal(val: boolean); - get stack(): ViewStack; - set stack(val: ViewStack); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): ViewSwitcherBar; - - // Members - - get_reveal(): boolean; - get_stack(): ViewStack | null; - set_reveal(reveal: boolean): void; - set_stack(stack?: ViewStack | null): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module ViewSwitcherTitle { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - stack: ViewStack; - subtitle: string; - title: string; - title_visible: boolean; - titleVisible: boolean; - view_switcher_enabled: boolean; - viewSwitcherEnabled: boolean; - } -} -export class ViewSwitcherTitle extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get stack(): ViewStack; - set stack(val: ViewStack); - get subtitle(): string; - set subtitle(val: string); - get title(): string; - set title(val: string); - get title_visible(): boolean; - get titleVisible(): boolean; - get view_switcher_enabled(): boolean; - set view_switcher_enabled(val: boolean); - get viewSwitcherEnabled(): boolean; - set viewSwitcherEnabled(val: boolean); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](): ViewSwitcherTitle; - - // Members - - get_stack(): ViewStack | null; - get_subtitle(): string; - get_title(): string; - get_title_visible(): boolean; - get_view_switcher_enabled(): boolean; - set_stack(stack?: ViewStack | null): void; - set_subtitle(subtitle: string): void; - set_title(title: string): void; - set_view_switcher_enabled(enabled: boolean): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} -export module Window { - export interface ConstructorProperties extends Gtk.Window.ConstructorProperties { - [key: string]: any; - content: Gtk.Widget; - } -} -export class Window - extends Gtk.Window - implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget, Gtk.Native, Gtk.Root, Gtk.ShortcutManager { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get content(): Gtk.Widget; - set content(val: Gtk.Widget); - - // Constructors - - static ["new"](): Window; - - // Members - - get_content(): Gtk.Widget | null; - set_content(content?: Gtk.Widget | null): void; - - // Implemented Members - - get_renderer(): Gsk.Renderer; - get_surface(): Gdk.Surface; - get_surface_transform(): [number, number]; - realize(): void; - unrealize(): void; - get_display(): Gdk.Display; - get_focus(): Gtk.Widget | null; - set_focus(focus?: Gtk.Widget | null): void; - vfunc_add_controller(controller: Gtk.ShortcutController): void; - vfunc_remove_controller(controller: Gtk.ShortcutController): void; -} -export module WindowTitle { - export interface ConstructorProperties extends Gtk.Widget.ConstructorProperties { - [key: string]: any; - subtitle: string; - title: string; - } -} -export class WindowTitle extends Gtk.Widget implements Gtk.Accessible, Gtk.Buildable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; - - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; - - // Properties - get subtitle(): string; - set subtitle(val: string); - get title(): string; - set title(val: string); - - // Implemented Properties - - get accessible_role(): Gtk.AccessibleRole; - set accessible_role(val: Gtk.AccessibleRole); - get accessibleRole(): Gtk.AccessibleRole; - set accessibleRole(val: Gtk.AccessibleRole); - - // Constructors - - static ["new"](title: string, subtitle: string): WindowTitle; - - // Members - - get_subtitle(): string; - get_title(): string; - set_subtitle(subtitle: string): void; - set_title(title: string): void; - - // Implemented Members - - get_accessible_role(): Gtk.AccessibleRole; - reset_property(property: Gtk.AccessibleProperty): void; - reset_relation(relation: Gtk.AccessibleRelation): void; - reset_state(state: Gtk.AccessibleState): void; - update_property(properties: Gtk.AccessibleProperty[], values: GObject.Value[]): void; - update_relation(relations: Gtk.AccessibleRelation[], values: GObject.Value[]): void; - update_state(states: Gtk.AccessibleState[], values: GObject.Value[]): void; - get_buildable_id(): string | null; - vfunc_add_child(builder: Gtk.Builder, child: GObject.Object, type?: string | null): void; - vfunc_custom_finished(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_end(builder: Gtk.Builder, child: GObject.Object | null, tagname: string, data?: any | null): void; - vfunc_custom_tag_start( - builder: Gtk.Builder, - child: GObject.Object | null, - tagname: string - ): [boolean, Gtk.BuildableParser, any]; - vfunc_get_id(): string; - vfunc_get_internal_child(builder: Gtk.Builder, childname: string): T; - vfunc_parser_finished(builder: Gtk.Builder): void; - vfunc_set_buildable_property(builder: Gtk.Builder, name: string, value: GObject.Value | any): void; - vfunc_set_id(id: string): void; -} - -export class SpringParams { - static $gtype: GObject.GType; - - constructor(damping_ratio: number, mass: number, stiffness: number); - constructor(copy: SpringParams); - - // Constructors - static ["new"](damping_ratio: number, mass: number, stiffness: number): SpringParams; - static new_full(damping: number, mass: number, stiffness: number): SpringParams; - - // Members - get_damping(): number; - get_damping_ratio(): number; - get_mass(): number; - get_stiffness(): number; - ref(): SpringParams; - unref(): void; -} - -export interface SwipeableNamespace { - $gtype: GObject.GType; - prototype: SwipeablePrototype; -} -export type Swipeable = SwipeablePrototype; -export interface SwipeablePrototype extends Gtk.Widget { - // Members - - get_cancel_progress(): number; - get_distance(): number; - get_progress(): number; - get_snap_points(): number[]; - get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; - vfunc_get_cancel_progress(): number; - vfunc_get_distance(): number; - vfunc_get_progress(): number; - vfunc_get_snap_points(): number[]; - vfunc_get_swipe_area(navigation_direction: NavigationDirection, is_drag: boolean): Gdk.Rectangle; -} - -export const Swipeable: SwipeableNamespace; diff --git a/types/gtk4/adw/adwEntryRow.d.ts b/types/gtk4/adw/adwEntryRow.d.ts new file mode 100644 index 00000000..d380aef5 --- /dev/null +++ b/types/gtk4/adw/adwEntryRow.d.ts @@ -0,0 +1,96 @@ +// https://github.com/gi-ts/gtk4/tree/master/packages/%40gi-types/adw1 +// Manually extend Adw.EntryRow which is somehow missing from the original file +// TODO: Remove this file and links to it once the original source updates to a recent Adw version. + +import * as Adw from '@gi-types/adw1'; +import * as Gtk from '@gi-types/gtk4'; +import * as GObject from '@gi-types/gobject2'; + +export module EntryRow { + export interface ConstructorProperties extends Adw.PreferencesRow.ConstructorProperties { + [key: string]: any; + activates_default: boolean; + attributes: unknown; + enable_emoji_completion: boolean; + input_hints: unknown; + input_purpose: unknown; + show_apply_button: boolean; + test: string; + } +} + +export class EntryRow extends Adw.PreferencesRow implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.Editable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; + + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; + + // Properties + get activates_default(): boolean; + set activates_default(val: boolean); + get attributes(): unknown; + set attributes(val: unknown); + get enable_emoji_completion(): boolean; + set enable_emoji_completion(val: boolean); + get input_hints(): unknown; + set input_hints(val: unknown); + get input_purpose(): unknown; + set input_purpose(val: unknown); + get show_apply_button(): boolean; + set show_apply_button(val: boolean); + get text(): string; + set text(val: string); + + static ['new'](): EntryRow; + + add_prefix(val: Gtk.Widget): void; + add_suffix(val: Gtk.Widget): void; + remove(val: Gtk.Widget): void; + + // autogenerated: + + cursor_position: number; + cursorPosition: number; + editable: boolean; + enable_undo: boolean; + enableUndo: boolean; + max_width_chars: number; + maxWidthChars: number; + selection_bound: number; + selectionBound: number; + width_chars: number; + widthChars: number; + xalign: number; + delete_selection(): void; + delete_text(start_pos: number, end_pos: number): void; + finish_delegate(): void; + get_alignment(): number; + get_chars(start_pos: number, end_pos: number): string; + get_delegate(): Gtk.EditablePrototype | null; + get_editable(): boolean; + get_enable_undo(): boolean; + get_max_width_chars(): number; + get_position(): number; + get_selection_bounds(): [boolean, number, number]; + get_text(): string; + get_width_chars(): number; + init_delegate(): void; + insert_text(text: string, length: number, position: number): number; + select_region(start_pos: number, end_pos: number): void; + set_alignment(xalign: number): void; + set_editable(is_editable: boolean): void; + set_enable_undo(enable_undo: boolean): void; + set_max_width_chars(n_chars: number): void; + set_position(position: number): void; + set_text(text: string): void; + set_width_chars(n_chars: number): void; + vfunc_changed(): void; + vfunc_delete_text(start_pos: number, end_pos: number): void; + vfunc_do_delete_text(start_pos: number, end_pos: number): void; + vfunc_do_insert_text(text: string, length: number, position: number): number; + vfunc_get_delegate(): Gtk.EditablePrototype | null; + vfunc_get_selection_bounds(): [boolean, number, number]; + vfunc_get_text(): string; + vfunc_insert_text(text: string, length: number, position: number): number; + vfunc_set_selection_bounds(start_pos: number, end_pos: number): void; +} From 6b6c3420f45d5b3ba74ed2db11200124f80d66ea Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 26 May 2023 17:39:50 +0200 Subject: [PATCH 43/87] Update blueprint-compiler syntax to 0.8.0 --- src/ui/genericJson.blp | 2 +- src/ui/localFolder.blp | 2 +- src/ui/reddit.blp | 2 +- src/ui/sourceRow.blp | 2 +- src/ui/unsplash.blp | 2 +- src/ui/urlSource.blp | 2 +- src/ui/wallhaven.blp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ui/genericJson.blp b/src/ui/genericJson.blp index 4837e400..04e0589d 100644 --- a/src/ui/genericJson.blp +++ b/src/ui/genericJson.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template GenericJsonSettingsGroup : Adw.PreferencesGroup { +template $GenericJsonSettingsGroup : Adw.PreferencesGroup { // title: _("Source Settings"); description: _("This feature requires some know how. However, many different wallpaper providers can be used with this generic JSON source.\nYou have to specify an URL to a JSON response and a path to the target image URL within the JSON response.\nYou can also define a prefix that will be added to the image URL."); diff --git a/src/ui/localFolder.blp b/src/ui/localFolder.blp index 853bbbea..973dc6a1 100644 --- a/src/ui/localFolder.blp +++ b/src/ui/localFolder.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template LocalFolderSettingsGroup : Adw.PreferencesGroup { +template $LocalFolderSettingsGroup : Adw.PreferencesGroup { title: _("General"); Adw.EntryRow folder_row { diff --git a/src/ui/reddit.blp b/src/ui/reddit.blp index c79f1cb7..f7e03e1b 100644 --- a/src/ui/reddit.blp +++ b/src/ui/reddit.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template RedditSettingsGroup : Adw.PreferencesGroup { +template $RedditSettingsGroup : Adw.PreferencesGroup { title: _("General"); Adw.EntryRow subreddits { diff --git a/src/ui/sourceRow.blp b/src/ui/sourceRow.blp index 289cedc6..475a9a22 100644 --- a/src/ui/sourceRow.blp +++ b/src/ui/sourceRow.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template SourceRow : Adw.ExpanderRow { +template $SourceRow : Adw.ExpanderRow { title: bind source_name.text; show-enable-switch: true; diff --git a/src/ui/unsplash.blp b/src/ui/unsplash.blp index ab5dde95..4c770066 100644 --- a/src/ui/unsplash.blp +++ b/src/ui/unsplash.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template UnsplashSettingsGroup : Adw.PreferencesGroup { +template $UnsplashSettingsGroup : Adw.PreferencesGroup { title: _("General"); Adw.EntryRow keyword { diff --git a/src/ui/urlSource.blp b/src/ui/urlSource.blp index 8a0d2ceb..9aadc2de 100644 --- a/src/ui/urlSource.blp +++ b/src/ui/urlSource.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template UrlSourceSettingsGroup : Adw.PreferencesGroup { +template $UrlSourceSettingsGroup : Adw.PreferencesGroup { Adw.PreferencesGroup { title: _("General"); diff --git a/src/ui/wallhaven.blp b/src/ui/wallhaven.blp index 6a554492..6309c946 100644 --- a/src/ui/wallhaven.blp +++ b/src/ui/wallhaven.blp @@ -1,7 +1,7 @@ using Gtk 4.0; using Adw 1; -template WallhavenSettingsGroup : Adw.PreferencesGroup { +template $WallhavenSettingsGroup : Adw.PreferencesGroup { // title: _("Source Settings"); Adw.PreferencesGroup { From 5af6174a35fd2c120b51498fc09c8f4f13aed8a2 Mon Sep 17 00:00:00 2001 From: Lucki Date: Mon, 29 May 2023 02:23:59 +0200 Subject: [PATCH 44/87] Workaround crash on shell loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …while using X11. Revert once fixed: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6691 --- src/extension.ts | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 0d6f0cf5..15d8ef37 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,3 +1,5 @@ +import * as GLib from 'gi://GLib'; + import type * as LoggerNamespace from './logger.js'; import type * as AFTimer from './timer.js'; import type * as WallpaperControllerNamespace from './wallpaperController.js'; @@ -23,22 +25,39 @@ class Extension { private _timer: AFTimer.AFTimer | null = null; enable(): void { + // Workaround crash when initializing the gnome shell with this extension active while being on X11 + // https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6691 + // TODO: Remove once that issue is fixed. + const crashWorkaround = new Promise(resolve => { + GLib.timeout_add(GLib.PRIORITY_HIGH, 100, () => { + resolve(); + return GLib.SOURCE_REMOVE; + }); + }); + // Dynamically load own modules. This allows us to use proper ES6 Modules - this._importModules().then(() => { - if (!Logger || !Timer || !WallpaperController || !RandomWallpaperMenu) - throw new Error('Error importing module'); + crashWorkaround.then(() => { + this._importModules().then(() => { + if (!Logger || !Timer || !WallpaperController || !RandomWallpaperMenu) + throw new Error('Error importing module'); - this._logger = new Logger.Logger('RWG3', 'Main'); - this._timer = Timer.AFTimer.getTimer(); - this._wallpaperController = new WallpaperController.WallpaperController(); - this._panelMenu = new RandomWallpaperMenu.RandomWallpaperMenu(this._wallpaperController); + this._logger = new Logger.Logger('RWG3', 'Main'); + this._timer = Timer.AFTimer.getTimer(); + this._wallpaperController = new WallpaperController.WallpaperController(); + this._panelMenu = new RandomWallpaperMenu.RandomWallpaperMenu(this._wallpaperController); - this._logger.info('Enable extension.'); - this._panelMenu.init(); + this._logger.info('Enable extension.'); + this._panelMenu.init(); + }).catch(error => { + if (this._logger) + this._logger.error(error); + else if (error instanceof Error) + logError(error); + else + logError(new Error('Unknown error')); + }); }).catch(error => { - if (this._logger) - this._logger.error(error); - else if (error instanceof Error) + if (error instanceof Error) logError(error); else logError(new Error('Unknown error')); From ddc216cf326b19a20bbbd5a708a77f204b2dccfa Mon Sep 17 00:00:00 2001 From: Lucki Date: Mon, 29 May 2023 02:27:37 +0200 Subject: [PATCH 45/87] Catch and log network and parsing errors --- src/adapter/genericJson.ts | 10 ++++++---- src/adapter/reddit.ts | 10 ++++++++-- src/adapter/wallhaven.ts | 11 +++++++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 9340e26c..f931fef8 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -36,13 +36,15 @@ class GenericJsonAdapter extends BaseAdapter { throw wallpaperResult; } - const response_body_bytes = await this._bowl.send_and_receive(message); - if (!response_body_bytes) { - this._logger.error('Error fetching response.'); + let response_body; + try { + const response_body_bytes = await this._bowl.send_and_receive(message); + response_body = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + } catch (error) { + this._logger.error(error); throw wallpaperResult; } - const response_body: unknown = JSON.parse(ByteArray.toString(response_body_bytes)); const imageJSONPath = this._settings.getString('image-path'); const postJSONPath = this._settings.getString('post-path'); const domainUrl = this._settings.getString('domain'); diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index c5e8f929..c187b956 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -53,9 +53,15 @@ class RedditAdapter extends BaseAdapter { const url = encodeURI(`https://www.reddit.com/r/${subreddits}.json`); const message = this._bowl.newGetMessage(url); - const response_body_bytes = await this._bowl.send_and_receive(message); + let response_body; + try { + const response_body_bytes = await this._bowl.send_and_receive(message); + response_body = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + } catch (error) { + this._logger.error(error); + throw wallpaperResult; + } - const response_body = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; if (!this._isRedditResponse(response_body)) { this._logger.error('Unexpected response'); throw wallpaperResult; diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index a6ab05a5..9f2aadc3 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -59,9 +59,16 @@ class WallhavenAdapter extends BaseAdapter { const message = this._bowl.newGetMessage(url); this._logger.debug(`Search URL: ${url}`); - const response_body_bytes = await this._bowl.send_and_receive(message); - const wallhavenResponse = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + let wallhavenResponse; + try { + const response_body_bytes = await this._bowl.send_and_receive(message); + wallhavenResponse = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + } catch (error) { + this._logger.error(error); + throw wallpaperResult; + } + if (!this._isWallhavenResponse(wallhavenResponse)) { this._logger.error('Unexpected response'); throw wallpaperResult; From 66867452803fddc636d1a6eac1846bf5e9493627 Mon Sep 17 00:00:00 2001 From: Lucki Date: Mon, 29 May 2023 14:13:22 +0200 Subject: [PATCH 46/87] Refactor URL source adapter and settings Provide a switch to declare a URL is responding with different images when queried in a short amount of time. --- src/adapter/urlSource.ts | 33 +++++++++++++------ ...ns.space.iflow.randomwallpaper.gschema.xml | 6 ++++ src/ui/urlSource.blp | 9 +++++ src/ui/urlSource.ts | 7 ++++ 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/adapter/urlSource.ts b/src/adapter/urlSource.ts index 67dabb55..4fa0ed17 100644 --- a/src/adapter/urlSource.ts +++ b/src/adapter/urlSource.ts @@ -14,7 +14,13 @@ class UrlSourceAdapter extends BaseAdapter { }); } - requestRandomImage(unusedCount: number): Promise { + requestRandomImage(count: number): Promise { + const wallpaperResult: HistoryEntry[] = []; + + let requestedEntries = 1; + if (this._settings.getBoolean('different-images')) + requestedEntries = count; + const imageDownloadUrl = this._settings.getString('image-url'); let authorName: string | null = this._settings.getString('author-name'); const authorUrl = this._settings.getString('author-url'); @@ -23,25 +29,32 @@ class UrlSourceAdapter extends BaseAdapter { if (imageDownloadUrl === '') { this._logger.error('Missing download url'); - throw new Array(0); + throw wallpaperResult; } if (authorName === '') authorName = null; - const historyEntry = new HistoryEntry(authorName, this._sourceName, imageDownloadUrl); + for (let i = 0; i < requestedEntries; i++) { + const historyEntry = new HistoryEntry(authorName, this._sourceName, imageDownloadUrl); - if (authorUrl !== '') - historyEntry.source.authorUrl = authorUrl; + if (authorUrl !== '') + historyEntry.source.authorUrl = authorUrl; - if (postUrl !== '') - historyEntry.source.imageLinkUrl = postUrl; + if (postUrl !== '') + historyEntry.source.imageLinkUrl = postUrl; + if (domainUrl !== '') + historyEntry.source.sourceUrl = domainUrl; - if (domainUrl !== '') - historyEntry.source.sourceUrl = domainUrl; + // overwrite historyEntry.id because the name will be the same and the timestamp might be too. + // historyEntry.name can't be null here because we created the entry with the constructor. + historyEntry.id = `${historyEntry.timestamp}_${i}_${historyEntry.name!}`; + + wallpaperResult.push(historyEntry); + } - return Promise.resolve([historyEntry]); + return Promise.resolve(wallpaperResult); } } diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index ac736845..f47667eb 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -416,6 +416,12 @@ The URL to fetch. + + false + Different images + Yields different images on consecutive requests in a short amount of time. + + "" Domain diff --git a/src/ui/urlSource.blp b/src/ui/urlSource.blp index 9aadc2de..90005f94 100644 --- a/src/ui/urlSource.blp +++ b/src/ui/urlSource.blp @@ -57,6 +57,15 @@ template $UrlSourceSettingsGroup : Adw.PreferencesGroup { ] } } + + Adw.ActionRow { + title: _("Yields different images"); + subtitle: _("…on consecutive request in a short amount of time."); + + Switch different_images { + valign: center; + } + } } Adw.PreferencesGroup { diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index dfe2653c..bdef14aa 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -2,6 +2,7 @@ import * as Adw from '@gi-types/adw1'; import * as Gio from 'gi://Gio'; import * as GLib from 'gi://GLib'; import * as GObject from 'gi://GObject'; +import * as Gtk from 'gi://Gtk?version=4.0'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; @@ -16,6 +17,7 @@ const UrlSourceSettingsGroup = GObject.registerClass({ InternalChildren: [ 'author_name', 'author_url', + 'different_images', 'domain', 'image_url', 'post_url', @@ -24,6 +26,7 @@ const UrlSourceSettingsGroup = GObject.registerClass({ // InternalChildren private _author_name!: AdwEntryRow.EntryRow; private _author_url!: AdwEntryRow.EntryRow; + private _different_images!: Gtk.Switch; private _domain!: AdwEntryRow.EntryRow; private _image_url!: AdwEntryRow.EntryRow; private _post_url!: AdwEntryRow.EntryRow; @@ -44,6 +47,10 @@ const UrlSourceSettingsGroup = GObject.registerClass({ this._author_url, 'text', Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('different-images', + this._different_images, + 'active', + Gio.SettingsBindFlags.DEFAULT); this._settings.bind('domain', this._domain, 'text', From 56888b34f32b7f781a8915b406b704be95e658e5 Mon Sep 17 00:00:00 2001 From: Lucki Date: Mon, 29 May 2023 14:28:11 +0200 Subject: [PATCH 47/87] Add Gnome44 to metadata --- src/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/metadata.json b/src/metadata.json index 47728ee8..e12fcbf6 100644 --- a/src/metadata.json +++ b/src/metadata.json @@ -1,5 +1,5 @@ { - "shell-version": [ "43" ], + "shell-version": [ "43", "44" ], "uuid": "randomwallpaper@iflow.space", "settings-schema": "org.gnome.shell.extensions.space.iflow.randomwallpaper", "name": "Random Wallpaper", From ab8da23780431aadf5b96647528e9f455ca99614 Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 1 Jun 2023 01:13:06 +0200 Subject: [PATCH 48/87] Reduce import rewrites by limiting the rewrite to special files. --- build.sh | 15 +++++++++++---- src/adapter/baseAdapter.ts | 2 +- src/adapter/localFolder.ts | 4 ++-- src/extension.ts | 2 +- src/history.ts | 2 +- src/historyMenuElements.ts | 10 +++++----- src/manager/hydraPaper.ts | 4 ++-- src/manager/superPaper.ts | 4 ++-- src/prefs.ts | 4 ++-- src/randomWallpaperMenu.ts | 4 ++-- src/settings.ts | 4 ++-- src/soupBowl.ts | 4 ++-- src/timer.ts | 2 +- src/ui/genericJson.ts | 6 +++--- src/ui/localFolder.ts | 8 ++++---- src/ui/reddit.ts | 8 ++++---- src/ui/sourceRow.ts | 8 ++++---- src/ui/unsplash.ts | 8 ++++---- src/ui/urlSource.ts | 8 ++++---- src/ui/wallhaven.ts | 10 +++++----- src/utils.ts | 8 ++++---- src/wallpaperController.ts | 4 ++-- types/gtk4/adw/adwEntryRow.d.ts | 4 ++-- types/misc/extensionUtils.d.ts | 2 +- 24 files changed, 71 insertions(+), 64 deletions(-) diff --git a/build.sh b/build.sh index 4fc4083e..f0b73a45 100755 --- a/build.sh +++ b/build.sh @@ -52,9 +52,6 @@ compile_js() { # rewrite imports to gjs own module system shopt -s globstar nullglob for file in "$DESTDIR"/**/*.js; do - # I don't know why these aren't available, they should? - sed -i -E "s#import \* as (.*) from 'gi://.*';#const \1 = imports.gi.\1;#g" "$file" - # Libadwaita seems somehow missing from gi:// sed -i -E "s#import \* as Adw from '@gi-types/adw1';#const Adw = imports.gi.Adw;#g" "$file" sed -i -E "s#import \* as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow';##g" "$file" @@ -72,9 +69,19 @@ compile_js() { sed -i -E "s#import \* as (.*) from '@gi-types/.*';#const \1 = imports.gi.\1;#g" "$file" done - # extension.js and prefs.js can't be modules (yet) while dynamically loaded by GJS + # extension.js and prefs.js can't be modules (yet) while dynamically loaded by GJS… + # https://gjs.guide/extensions/overview/imports-and-modules.html#imports-and-modules + # …and TypeScript can't compile specific files to "not a module" in overall module mode. # https://github.com/microsoft/TypeScript/issues/41567 sed -i -E "s#export \{\};##g" "$DESTDIR/extension.js" + + # Work around standard imports not available in files loaded by the shell, those can't be modules (yet) + # > Note that as of GNOME 44, neither GNOME Shell nor Extensions support ESModules, and must use GJS's custom import scheme. + # https://gjs.guide/extensions/overview/imports-and-modules.html#imports-and-modules + # https://gjs-docs.gnome.org/gjs/esmodules.md + # > JS ERROR: Extension randomwallpaper@iflow.space: SyntaxError: import declarations may only appear at top level of a module + sed -i -E "s#import (.*) from 'gi://.*';#const \1 = imports.gi.\1;#g" "$DESTDIR/extension.js" + sed -i -E "s#import (.*) from 'gi://.*';#const \1 = imports.gi.\1;#g" "$DESTDIR/prefs.js" } # TODO: Drop compiled schemas when only targeting Gnome 44+ diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 238c1431..a11943b4 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -1,4 +1,4 @@ -import * as Gio from 'gi://Gio'; +import Gio from 'gi://Gio'; import * as SettingsModule from './../settings.js'; diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 99ebb2ea..ab579fae 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -1,5 +1,5 @@ -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; import * as SettingsModule from './../settings.js'; import * as Utils from './../utils.js'; diff --git a/src/extension.ts b/src/extension.ts index 15d8ef37..50609352 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,4 +1,4 @@ -import * as GLib from 'gi://GLib'; +import GLib from 'gi://GLib'; import type * as LoggerNamespace from './logger.js'; import type * as AFTimer from './timer.js'; diff --git a/src/history.ts b/src/history.ts index 74378730..725966bd 100644 --- a/src/history.ts +++ b/src/history.ts @@ -1,4 +1,4 @@ -import * as Gio from 'gi://Gio'; +import Gio from 'gi://Gio'; import * as Utils from './utils.js'; diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index fdc89417..e9204c6e 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -1,8 +1,8 @@ -import * as GdkPixbuf from 'gi://GdkPixbuf'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as GObject from 'gi://GObject'; -import * as Gtk from 'gi://Gtk'; +import GdkPixbuf from 'gi://GdkPixbuf'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; import * as Clutter from '@gi-types/clutter'; import * as Cogl from '@gi-types/cogl'; diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 5bd87a21..67fb0472 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -1,5 +1,5 @@ -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; import * as Utils from '../utils.js'; diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts index a6d2ab13..2c4aba02 100644 --- a/src/manager/superPaper.ts +++ b/src/manager/superPaper.ts @@ -1,5 +1,5 @@ -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; import * as Utils from '../utils.js'; diff --git a/src/prefs.ts b/src/prefs.ts index dfbc6f00..94f52e22 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -1,6 +1,6 @@ import * as Adw from '@gi-types/adw1'; -import * as Gio from 'gi://Gio'; -import * as Gtk from 'gi://Gtk'; +import Gio from 'gi://Gio'; +import Gtk from 'gi://Gtk'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 9076052d..cf4cc103 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -1,5 +1,5 @@ -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; import * as Main from '@gi/ui/main'; import * as PanelMenu from '@gi/ui/panelMenu'; diff --git a/src/settings.ts b/src/settings.ts index 9b33520a..ebf3e630 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,5 +1,5 @@ -import * as Gio from 'gi://Gio'; -import * as GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/soupBowl.ts b/src/soupBowl.ts index 857968fc..172accc7 100644 --- a/src/soupBowl.ts +++ b/src/soupBowl.ts @@ -4,8 +4,8 @@ * libSoup is accessed through the SoupBowl wrapper to support libSoup3 and libSoup2.4 simultaneously in the extension * runtime and in the preferences window. */ -import * as GLib from 'gi://GLib'; -import * as Soup from 'gi://Soup'; +import GLib from 'gi://GLib'; +import Soup from 'gi://Soup'; import {Logger} from './logger.js'; diff --git a/src/timer.ts b/src/timer.ts index 04646cd7..0cf743c6 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -1,4 +1,4 @@ -import * as GLib from 'gi://GLib'; +import GLib from 'gi://GLib'; import {Logger} from './logger.js'; import {Settings} from './settings.js'; diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index cdee5c0a..3553ed91 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -1,7 +1,7 @@ import * as Adw from '@gi-types/adw1'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index e7e01615..892ffaf5 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -1,8 +1,8 @@ import * as Adw from '@gi-types/adw1'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as GObject from 'gi://GObject'; -import * as Gtk from 'gi://Gtk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index 767e3a3d..67872bca 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -1,8 +1,8 @@ import * as Adw from '@gi-types/adw1'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as GObject from 'gi://GObject'; -import * as Gtk from 'gi://Gtk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 1ef2d3ed..9fff2f72 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -1,8 +1,8 @@ import * as Adw from '@gi-types/adw1'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as GObject from 'gi://GObject'; -import * as Gtk from 'gi://Gtk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index d3c8d42c..b5716bd2 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -1,8 +1,8 @@ import * as Adw from '@gi-types/adw1'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as GObject from 'gi://GObject'; -import * as Gtk from 'gi://Gtk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index bdef14aa..f205cbc1 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -1,8 +1,8 @@ import * as Adw from '@gi-types/adw1'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as GObject from 'gi://GObject'; -import * as Gtk from 'gi://Gtk?version=4.0'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index 99fa6f32..69cb4afd 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -1,9 +1,9 @@ import * as Adw from '@gi-types/adw1'; -import * as Gdk from 'gi://Gdk'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as GObject from 'gi://GObject'; -import * as Gtk from 'gi://Gtk'; +import Gdk from 'gi://Gdk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/src/utils.ts b/src/utils.ts index 489ac777..e1063970 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,8 +1,8 @@ import * as Adw from '@gi-types/adw1'; -import * as Gdk from 'gi://Gdk'; -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; -import * as Gtk from 'gi://Gtk'; +import Gdk from 'gi://Gdk'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import Gtk from 'gi://Gtk'; import {Settings} from './settings.js'; diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 2f5e6e0d..9a475aa9 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -1,5 +1,5 @@ -import * as Gio from 'gi://Gio'; -import * as GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; diff --git a/types/gtk4/adw/adwEntryRow.d.ts b/types/gtk4/adw/adwEntryRow.d.ts index d380aef5..d8de854e 100644 --- a/types/gtk4/adw/adwEntryRow.d.ts +++ b/types/gtk4/adw/adwEntryRow.d.ts @@ -3,8 +3,8 @@ // TODO: Remove this file and links to it once the original source updates to a recent Adw version. import * as Adw from '@gi-types/adw1'; -import * as Gtk from '@gi-types/gtk4'; -import * as GObject from '@gi-types/gobject2'; +import Gtk from 'gi://Gtk'; +import GObject from 'gi://GObject'; export module EntryRow { export interface ConstructorProperties extends Adw.PreferencesRow.ConstructorProperties { diff --git a/types/misc/extensionUtils.d.ts b/types/misc/extensionUtils.d.ts index f6aecdf5..4fab801a 100644 --- a/types/misc/extensionUtils.d.ts +++ b/types/misc/extensionUtils.d.ts @@ -1,7 +1,7 @@ // https://github.com/yilozt/rounded-window-corners/blob/main/%40imports/misc/extensionUtils.d.ts // GPL3 -import * as Gio from '@gi-types/gio2'; +import Gio from 'gi://Gio'; /** * getCurrentExtension: From db84933405f471a34652c4ab560067f346a6c83c Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 2 Jun 2023 00:40:14 +0200 Subject: [PATCH 49/87] Rename adwEntryRow.d.ts --- types/gtk4/adw/{adwEntryRow.d.ts => index.d.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename types/gtk4/adw/{adwEntryRow.d.ts => index.d.ts} (100%) diff --git a/types/gtk4/adw/adwEntryRow.d.ts b/types/gtk4/adw/index.d.ts similarity index 100% rename from types/gtk4/adw/adwEntryRow.d.ts rename to types/gtk4/adw/index.d.ts From f387bc2a7669e3fa2fbe0c130137c2386ee2d561 Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 1 Jun 2023 13:07:53 +0200 Subject: [PATCH 50/87] Further improve Adw importing and extending --- build.sh | 6 +- src/prefs.ts | 5 +- src/ui/genericJson.ts | 21 +++-- src/ui/localFolder.ts | 5 +- src/ui/reddit.ts | 5 +- src/ui/sourceRow.ts | 5 +- src/ui/unsplash.ts | 7 +- src/ui/urlSource.ts | 13 ++- src/ui/wallhaven.ts | 11 ++- src/utils.ts | 2 +- tsconfig.json | 2 + types/gtk4/adw/index.d.ts | 174 +++++++++++++++++++------------------- 12 files changed, 125 insertions(+), 131 deletions(-) diff --git a/build.sh b/build.sh index f0b73a45..c24e5837 100755 --- a/build.sh +++ b/build.sh @@ -49,13 +49,9 @@ compile_js() { # TypeScript to JavaScript, config in tsconfig.json npx --silent tsc - # rewrite imports to gjs own module system + # rewrite shell imports to gjs legacy module system shopt -s globstar nullglob for file in "$DESTDIR"/**/*.js; do - # Libadwaita seems somehow missing from gi:// - sed -i -E "s#import \* as Adw from '@gi-types/adw1';#const Adw = imports.gi.Adw;#g" "$file" - sed -i -E "s#import \* as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow';##g" "$file" - # Special module naming sed -i -E "s#import \* as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray';#const ByteArray = imports.byteArray;#g" "$file" diff --git a/src/prefs.ts b/src/prefs.ts index 94f52e22..8f29419e 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -1,8 +1,7 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gio from 'gi://Gio'; import Gtk from 'gi://Gtk'; -import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import type * as SettingsNamespace from './settings.js'; @@ -214,7 +213,7 @@ class RandomWallpaperSettings { } private _bindHistorySection(window: Adw.PreferencesWindow): void { - const entryRow = this._builder.get_object('row_favorites_folder'); + const entryRow = this._builder.get_object('row_favorites_folder'); entryRow.text = this._settings.getString('favorites-folder'); this._settings.bind('history-length', diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index 3553ed91..a0e70210 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -1,9 +1,8 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; -import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -26,15 +25,15 @@ const GenericJsonSettingsGroup = GObject.registerClass({ ], }, class GenericJsonSettingsGroup extends Adw.PreferencesGroup { // InternalChildren - private _author_name_path!: AdwEntryRow.EntryRow; - private _author_url_path!: AdwEntryRow.EntryRow; - private _author_url_prefix!: AdwEntryRow.EntryRow; - private _domain!: AdwEntryRow.EntryRow; - private _image_path!: AdwEntryRow.EntryRow; - private _image_prefix!: AdwEntryRow.EntryRow; - private _post_path!: AdwEntryRow.EntryRow; - private _post_prefix!: AdwEntryRow.EntryRow; - private _request_url!: AdwEntryRow.EntryRow; + private _author_name_path!: Adw.EntryRow; + private _author_url_path!: Adw.EntryRow; + private _author_url_prefix!: Adw.EntryRow; + private _domain!: Adw.EntryRow; + private _image_path!: Adw.EntryRow; + private _image_prefix!: Adw.EntryRow; + private _post_path!: Adw.EntryRow; + private _post_prefix!: Adw.EntryRow; + private _request_url!: Adw.EntryRow; private _settings; diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index 892ffaf5..c0bbabec 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -1,10 +1,9 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -21,7 +20,7 @@ const LocalFolderSettingsGroup = GObject.registerClass({ }, class LocalFolderSettingsGroup extends Adw.PreferencesGroup { // InternalChildren private _folder!: Gtk.Button; - private _folder_row!: AdwEntryRow.EntryRow; + private _folder_row!: Adw.EntryRow; private _saveDialog: Gtk.FileChooserNative | undefined; private _settings; diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index 67872bca..c4a4f141 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -1,10 +1,9 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -29,7 +28,7 @@ const RedditSettingsGroup = GObject.registerClass({ private _image_ratio2!: Gtk.Adjustment; private _min_height!: Gtk.Adjustment; private _min_width!: Gtk.Adjustment; - private _subreddits!: AdwEntryRow.EntryRow; + private _subreddits!: Adw.EntryRow; private _settings; diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index 9fff2f72..c1c6e2c8 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -1,10 +1,9 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -45,7 +44,7 @@ const SourceRow = GObject.registerClass({ private _blocked_images_list!: Adw.ExpanderRow; private _combo!: Adw.ComboRow; private _settings_container!: Adw.Clamp; - private _source_name!: AdwEntryRow.EntryRow; + private _source_name!: Adw.EntryRow; private _settings; private _logger = new Logger('RWG3', 'SourceRow'); diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index b5716bd2..00dc62da 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -1,10 +1,9 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -28,11 +27,11 @@ const UnsplashSettingsGroup = GObject.registerClass({ // InternalChildren private _constraint_type!: Adw.ComboRow; - private _constraint_value!: AdwEntryRow.EntryRow; + private _constraint_value!: Adw.EntryRow; private _featured_only!: Gtk.Switch; private _image_height!: Gtk.Adjustment; private _image_width!: Gtk.Adjustment; - private _keyword!: AdwEntryRow.EntryRow; + private _keyword!: Adw.EntryRow; private _settings; diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index f205cbc1..938bb260 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -1,10 +1,9 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -24,12 +23,12 @@ const UrlSourceSettingsGroup = GObject.registerClass({ ], }, class UrlSourceSettingsGroup extends Adw.PreferencesGroup { // InternalChildren - private _author_name!: AdwEntryRow.EntryRow; - private _author_url!: AdwEntryRow.EntryRow; + private _author_name!: Adw.EntryRow; + private _author_url!: Adw.EntryRow; private _different_images!: Gtk.Switch; - private _domain!: AdwEntryRow.EntryRow; - private _image_url!: AdwEntryRow.EntryRow; - private _post_url!: AdwEntryRow.EntryRow; + private _domain!: Adw.EntryRow; + private _image_url!: Adw.EntryRow; + private _post_url!: Adw.EntryRow; private _settings; diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index 69cb4afd..e9c742d0 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -1,11 +1,10 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gdk from 'gi://Gdk'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as AdwEntryRow from '@gi/gtk4/adw/adwEntryRow'; import * as ExtensionUtils from '@gi/misc/extensionUtils'; import * as Settings from './../settings.js'; @@ -45,15 +44,15 @@ const WallhavenSettingsGroup = GObject.registerClass({ private _allow_nsfw!: Gtk.Switch; private _allow_sfw!: Gtk.Switch; private _allow_sketchy!: Gtk.Switch; - private _api_key!: AdwEntryRow.EntryRow; - private _aspect_ratios!: AdwEntryRow.EntryRow; + private _api_key!: Adw.EntryRow; + private _aspect_ratios!: Adw.EntryRow; private _button_color_undo!: Gtk.Button; private _button_color!: Gtk.Button; private _category_anime!: Gtk.Switch; private _category_general!: Gtk.Switch; private _category_people!: Gtk.Switch; - private _keyword!: AdwEntryRow.EntryRow; - private _minimal_resolution!: AdwEntryRow.EntryRow; + private _keyword!: Adw.EntryRow; + private _minimal_resolution!: Adw.EntryRow; private _row_color!: Adw.ActionRow; private _colorDialog: Gtk.ColorChooserDialog | undefined; diff --git a/src/utils.ts b/src/utils.ts index e1063970..f557736e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import * as Adw from '@gi-types/adw1'; +import Adw from 'gi://Adw'; import Gdk from 'gi://Gdk'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; diff --git a/tsconfig.json b/tsconfig.json index 6c770f9a..2ef348fe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,12 +20,14 @@ }, "typeRoots": [ "./node_modules/@gi-types", + "./types", ], "types": [ "gjs-environment", "base-types", "gtk4-types", "adw1", + "gtk4/adw", // locally expose 'gi://Adw' and extend with EntryRow ], }, diff --git a/types/gtk4/adw/index.d.ts b/types/gtk4/adw/index.d.ts index d8de854e..8553f180 100644 --- a/types/gtk4/adw/index.d.ts +++ b/types/gtk4/adw/index.d.ts @@ -1,96 +1,100 @@ -// https://github.com/gi-ts/gtk4/tree/master/packages/%40gi-types/adw1 -// Manually extend Adw.EntryRow which is somehow missing from the original file -// TODO: Remove this file and links to it once the original source updates to a recent Adw version. +// The mapping from @gi-types/adw1 to gi://Adw is somehow missing but exists in the real gnome environment +declare module 'gi://Adw' { + export * from '@gi-types/adw1'; -import * as Adw from '@gi-types/adw1'; -import Gtk from 'gi://Gtk'; -import GObject from 'gi://GObject'; + // https://github.com/gi-ts/gtk4/tree/master/packages/%40gi-types/adw1 + // Manually extend Adw.EntryRow which is somehow missing from @gi-types/adw1 + // TODO: Remove this file and links to it once the original source updates to a recent Adw version. + import Adw from 'gi://Adw'; + import Gtk from 'gi://Gtk'; + import GObject from 'gi://GObject'; -export module EntryRow { - export interface ConstructorProperties extends Adw.PreferencesRow.ConstructorProperties { - [key: string]: any; - activates_default: boolean; - attributes: unknown; - enable_emoji_completion: boolean; - input_hints: unknown; - input_purpose: unknown; - show_apply_button: boolean; - test: string; + export module EntryRow { + export interface ConstructorProperties extends Adw.PreferencesRow.ConstructorProperties { + [key: string]: any; + activates_default: boolean; + attributes: unknown; + enable_emoji_completion: boolean; + input_hints: unknown; + input_purpose: unknown; + show_apply_button: boolean; + test: string; + } } -} -export class EntryRow extends Adw.PreferencesRow implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.Editable, Gtk.ConstraintTarget { - static $gtype: GObject.GType; + export class EntryRow extends Adw.PreferencesRow implements Gtk.Accessible, Gtk.Actionable, Gtk.Buildable, Gtk.Editable, Gtk.ConstraintTarget { + static $gtype: GObject.GType; - constructor(properties?: Partial, ...args: any[]); - _init(properties?: Partial, ...args: any[]): void; + constructor(properties?: Partial, ...args: any[]); + _init(properties?: Partial, ...args: any[]): void; - // Properties - get activates_default(): boolean; - set activates_default(val: boolean); - get attributes(): unknown; - set attributes(val: unknown); - get enable_emoji_completion(): boolean; - set enable_emoji_completion(val: boolean); - get input_hints(): unknown; - set input_hints(val: unknown); - get input_purpose(): unknown; - set input_purpose(val: unknown); - get show_apply_button(): boolean; - set show_apply_button(val: boolean); - get text(): string; - set text(val: string); + // Properties + get activates_default(): boolean; + set activates_default(val: boolean); + get attributes(): unknown; + set attributes(val: unknown); + get enable_emoji_completion(): boolean; + set enable_emoji_completion(val: boolean); + get input_hints(): unknown; + set input_hints(val: unknown); + get input_purpose(): unknown; + set input_purpose(val: unknown); + get show_apply_button(): boolean; + set show_apply_button(val: boolean); + get text(): string; + set text(val: string); - static ['new'](): EntryRow; + static ['new'](): EntryRow; - add_prefix(val: Gtk.Widget): void; - add_suffix(val: Gtk.Widget): void; - remove(val: Gtk.Widget): void; + add_prefix(val: Gtk.Widget): void; + add_suffix(val: Gtk.Widget): void; + remove(val: Gtk.Widget): void; - // autogenerated: + // autogenerated: - cursor_position: number; - cursorPosition: number; - editable: boolean; - enable_undo: boolean; - enableUndo: boolean; - max_width_chars: number; - maxWidthChars: number; - selection_bound: number; - selectionBound: number; - width_chars: number; - widthChars: number; - xalign: number; - delete_selection(): void; - delete_text(start_pos: number, end_pos: number): void; - finish_delegate(): void; - get_alignment(): number; - get_chars(start_pos: number, end_pos: number): string; - get_delegate(): Gtk.EditablePrototype | null; - get_editable(): boolean; - get_enable_undo(): boolean; - get_max_width_chars(): number; - get_position(): number; - get_selection_bounds(): [boolean, number, number]; - get_text(): string; - get_width_chars(): number; - init_delegate(): void; - insert_text(text: string, length: number, position: number): number; - select_region(start_pos: number, end_pos: number): void; - set_alignment(xalign: number): void; - set_editable(is_editable: boolean): void; - set_enable_undo(enable_undo: boolean): void; - set_max_width_chars(n_chars: number): void; - set_position(position: number): void; - set_text(text: string): void; - set_width_chars(n_chars: number): void; - vfunc_changed(): void; - vfunc_delete_text(start_pos: number, end_pos: number): void; - vfunc_do_delete_text(start_pos: number, end_pos: number): void; - vfunc_do_insert_text(text: string, length: number, position: number): number; - vfunc_get_delegate(): Gtk.EditablePrototype | null; - vfunc_get_selection_bounds(): [boolean, number, number]; - vfunc_get_text(): string; - vfunc_insert_text(text: string, length: number, position: number): number; - vfunc_set_selection_bounds(start_pos: number, end_pos: number): void; + cursor_position: number; + cursorPosition: number; + editable: boolean; + enable_undo: boolean; + enableUndo: boolean; + max_width_chars: number; + maxWidthChars: number; + selection_bound: number; + selectionBound: number; + width_chars: number; + widthChars: number; + xalign: number; + delete_selection(): void; + delete_text(start_pos: number, end_pos: number): void; + finish_delegate(): void; + get_alignment(): number; + get_chars(start_pos: number, end_pos: number): string; + get_delegate(): Gtk.EditablePrototype | null; + get_editable(): boolean; + get_enable_undo(): boolean; + get_max_width_chars(): number; + get_position(): number; + get_selection_bounds(): [boolean, number, number]; + get_text(): string; + get_width_chars(): number; + init_delegate(): void; + insert_text(text: string, length: number, position: number): number; + select_region(start_pos: number, end_pos: number): void; + set_alignment(xalign: number): void; + set_editable(is_editable: boolean): void; + set_enable_undo(enable_undo: boolean): void; + set_max_width_chars(n_chars: number): void; + set_position(position: number): void; + set_text(text: string): void; + set_width_chars(n_chars: number): void; + vfunc_changed(): void; + vfunc_delete_text(start_pos: number, end_pos: number): void; + vfunc_do_delete_text(start_pos: number, end_pos: number): void; + vfunc_do_insert_text(text: string, length: number, position: number): number; + vfunc_get_delegate(): Gtk.EditablePrototype | null; + vfunc_get_selection_bounds(): [boolean, number, number]; + vfunc_get_text(): string; + vfunc_insert_text(text: string, length: number, position: number): number; + vfunc_set_selection_bounds(start_pos: number, end_pos: number): void; + } } From 0d76314c64678d32834f5e1be69467aa8b208cde Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 1 Jun 2023 13:26:07 +0200 Subject: [PATCH 51/87] Further reduce import rewrites by mapping --- build.sh | 3 --- src/historyMenuElements.ts | 7 +++---- tsconfig.json | 1 + types/mappings/index.d.ts | 12 ++++++++++++ types/ui/main.d.ts | 3 ++- types/ui/panelMenu.d.ts | 3 ++- types/ui/popupMenu.d.ts | 4 ++-- 7 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 types/mappings/index.d.ts diff --git a/build.sh b/build.sh index c24e5837..63736111 100755 --- a/build.sh +++ b/build.sh @@ -60,9 +60,6 @@ compile_js() { sed -i -E "s#import \* as PopupMenu from '@gi/ui/popupMenu';#const PopupMenu = imports.ui.popupMenu;#g" "$file" sed -i -E "s#import \* as PanelMenu from '@gi/ui/panelMenu';#const PanelMenu = imports.ui.panelMenu;#g" "$file" sed -i -E "s#import \* as Main from '@gi/ui/main';#const Main = imports.ui.main;#g" "$file" - - # all remaining - sed -i -E "s#import \* as (.*) from '@gi-types/.*';#const \1 = imports.gi.\1;#g" "$file" done # extension.js and prefs.js can't be modules (yet) while dynamically loaded by GJS… diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index e9204c6e..d06f4ec5 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -1,12 +1,11 @@ +import Clutter from 'gi://Clutter'; +import Cogl from 'gi://Cogl'; import GdkPixbuf from 'gi://GdkPixbuf'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; - -import * as Clutter from '@gi-types/clutter'; -import * as Cogl from '@gi-types/cogl'; -import * as St from '@gi-types/st'; +import St from 'gi://St'; import * as PopupMenu from '@gi/ui/popupMenu'; diff --git a/tsconfig.json b/tsconfig.json index 2ef348fe..41751059 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,7 @@ "gtk4-types", "adw1", "gtk4/adw", // locally expose 'gi://Adw' and extend with EntryRow + "mappings", // missing mappings 'from @gi-types' to 'gi://' ], }, diff --git a/types/mappings/index.d.ts b/types/mappings/index.d.ts new file mode 100644 index 00000000..852ced5f --- /dev/null +++ b/types/mappings/index.d.ts @@ -0,0 +1,12 @@ +// These mappings from '@gi-types/*' to 'gi://*' are somehow missing but exist in the real gnome environment +declare module 'gi://Clutter' { + export * from '@gi-types/clutter'; +} + +declare module 'gi://Cogl' { + export * from '@gi-types/cogl'; +} + +declare module 'gi://St' { + export * from '@gi-types/st'; +} diff --git a/types/ui/main.d.ts b/types/ui/main.d.ts index 9ac0cf40..ffae452f 100644 --- a/types/ui/main.d.ts +++ b/types/ui/main.d.ts @@ -1,4 +1,5 @@ -import * as St from '@gi-types/st'; +import St from 'gi://St'; + import * as PanelMenu from '@gi/ui/panelMenu'; declare class Panel extends St.Widget { diff --git a/types/ui/panelMenu.d.ts b/types/ui/panelMenu.d.ts index f3836405..2fb74e89 100644 --- a/types/ui/panelMenu.d.ts +++ b/types/ui/panelMenu.d.ts @@ -1,7 +1,8 @@ /* eslint-disable */ +import St from 'gi://St'; + import {PopupMenu} from '@gi/ui/popupMenu'; -import * as St from '@gi-types/st'; declare class ButtonBox extends St.Widget{} diff --git a/types/ui/popupMenu.d.ts b/types/ui/popupMenu.d.ts index 405dd26c..fbe39ed7 100644 --- a/types/ui/popupMenu.d.ts +++ b/types/ui/popupMenu.d.ts @@ -2,8 +2,8 @@ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/popupMenu.js -import * as Clutter from '@gi-types/clutter'; -import * as St from '@gi-types/st'; +import Clutter from 'gi://Clutter'; +import St from 'gi://St'; // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/misc/signals.js From 6a09e6bb6ec63a4e2ab886f18bfe7c7be9400cff Mon Sep 17 00:00:00 2001 From: Lucki Date: Thu, 1 Jun 2023 17:21:10 +0200 Subject: [PATCH 52/87] Replace legacy ByteArray with TextDecoder The import seems wrong, it should be defined by global GJS VSCode show weird behavior and giving me a red line despite the actual compilation running fine. --- build.sh | 3 --- src/adapter/genericJson.ts | 4 +--- src/adapter/reddit.ts | 4 +--- src/adapter/wallhaven.ts | 4 +--- src/historyMenuElements.ts | 3 --- tsconfig.json | 5 ++++- 6 files changed, 7 insertions(+), 16 deletions(-) diff --git a/build.sh b/build.sh index 63736111..2290afaf 100755 --- a/build.sh +++ b/build.sh @@ -52,9 +52,6 @@ compile_js() { # rewrite shell imports to gjs legacy module system shopt -s globstar nullglob for file in "$DESTDIR"/**/*.js; do - # Special module naming - sed -i -E "s#import \* as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray';#const ByteArray = imports.byteArray;#g" "$file" - # Special cases for extension internal imports, shell sed -i -E "s#import \* as ExtensionUtils from '@gi/misc/extensionUtils';#const ExtensionUtils = imports.misc.extensionUtils;#g" "$file" sed -i -E "s#import \* as PopupMenu from '@gi/ui/popupMenu';#const PopupMenu = imports.ui.popupMenu;#g" "$file" diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index f931fef8..33c37e83 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -1,5 +1,3 @@ -import * as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray'; - import * as JSONPath from './../jsonPath.js'; import * as SettingsModule from './../settings.js'; import * as Utils from './../utils.js'; @@ -39,7 +37,7 @@ class GenericJsonAdapter extends BaseAdapter { let response_body; try { const response_body_bytes = await this._bowl.send_and_receive(message); - response_body = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + response_body = JSON.parse(new TextDecoder().decode(response_body_bytes)) as unknown; } catch (error) { this._logger.error(error); throw wallpaperResult; diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index c187b956..bf365fff 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -1,5 +1,3 @@ -import * as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray'; - import * as SettingsModule from './../settings.js'; import * as Utils from './../utils.js'; @@ -56,7 +54,7 @@ class RedditAdapter extends BaseAdapter { let response_body; try { const response_body_bytes = await this._bowl.send_and_receive(message); - response_body = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + response_body = JSON.parse(new TextDecoder().decode(response_body_bytes)) as unknown; } catch (error) { this._logger.error(error); throw wallpaperResult; diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index 9f2aadc3..4d3f03db 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -1,5 +1,3 @@ -import * as ByteArray from '@gi-types/gjs-environment/legacyModules/byteArray'; - import * as SettingsModule from './../settings.js'; import * as Utils from './../utils.js'; @@ -63,7 +61,7 @@ class WallhavenAdapter extends BaseAdapter { let wallhavenResponse; try { const response_body_bytes = await this._bowl.send_and_receive(message); - wallhavenResponse = JSON.parse(ByteArray.toString(response_body_bytes)) as unknown; + wallhavenResponse = JSON.parse(new TextDecoder().decode(response_body_bytes)) as unknown; } catch (error) { this._logger.error(error); throw wallpaperResult; diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index d06f4ec5..0d5cf4ba 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -241,9 +241,6 @@ const HistoryElement = GObject.registerClass({ // @ts-expect-error // eslint-disable-next-line @typescript-eslint/await-thenable const [success, message]: [boolean, string] = await targetInfoFile.replace_contents_bytes_async( - // FIXME: Don't know from where to import - // @ts-expect-error - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call new TextEncoder().encode(JSON.stringify(this.historyEntry.source, null, '\t')), null, false, diff --git a/tsconfig.json b/tsconfig.json index 41751059..0752f9ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,10 @@ "moduleResolution": "node", "skipLibCheck": true, "lib": [ - "ES2021" + "ES2021", + "DOM", // FIXME: This is here for TextDecoder which should be defined by global GJS + // https://gjs-docs.gnome.org/gjs/encoding.md + // > The functions in this module are available globally, without import. ], "paths": { "@gi/*": ["../types/*"], From 726c1763929c805248bff1dc76d4d0a0018cd2f8 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 2 Jun 2023 00:13:28 +0200 Subject: [PATCH 53/87] Rename extensionUtils.d.ts --- types/misc/{extensionUtils.d.ts => index.d.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename types/misc/{extensionUtils.d.ts => index.d.ts} (100%) diff --git a/types/misc/extensionUtils.d.ts b/types/misc/index.d.ts similarity index 100% rename from types/misc/extensionUtils.d.ts rename to types/misc/index.d.ts From 0875d33e2d7ba079887986b12c23a75dde98004a Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 2 Jun 2023 00:20:53 +0200 Subject: [PATCH 54/87] Remove all import rewrites --- build.sh | 19 +--- src/extension.ts | 8 +- src/historyMenuElements.ts | 4 +- src/prefs.ts | 37 ++++--- src/randomWallpaperMenu.ts | 10 +- src/settings.ts | 3 +- src/ui/genericJson.ts | 3 +- src/ui/localFolder.ts | 3 +- src/ui/reddit.ts | 3 +- src/ui/sourceRow.ts | 3 +- src/ui/unsplash.ts | 3 +- src/ui/urlSource.ts | 3 +- src/ui/wallhaven.ts | 3 +- src/wallpaperController.ts | 3 +- tsconfig.json | 5 +- types/gtk4/adw/index.d.ts | 6 ++ types/misc/index.d.ts | 205 ++++++++++++++++++++----------------- types/ui/index.d.ts | 17 +++ types/ui/main.d.ts | 16 +-- types/ui/panelMenu.d.ts | 14 +-- types/ui/popupMenu.d.ts | 143 +++++++++++++------------- 21 files changed, 280 insertions(+), 231 deletions(-) create mode 100644 types/ui/index.d.ts diff --git a/build.sh b/build.sh index 2290afaf..cde0a00d 100755 --- a/build.sh +++ b/build.sh @@ -49,29 +49,12 @@ compile_js() { # TypeScript to JavaScript, config in tsconfig.json npx --silent tsc - # rewrite shell imports to gjs legacy module system - shopt -s globstar nullglob - for file in "$DESTDIR"/**/*.js; do - # Special cases for extension internal imports, shell - sed -i -E "s#import \* as ExtensionUtils from '@gi/misc/extensionUtils';#const ExtensionUtils = imports.misc.extensionUtils;#g" "$file" - sed -i -E "s#import \* as PopupMenu from '@gi/ui/popupMenu';#const PopupMenu = imports.ui.popupMenu;#g" "$file" - sed -i -E "s#import \* as PanelMenu from '@gi/ui/panelMenu';#const PanelMenu = imports.ui.panelMenu;#g" "$file" - sed -i -E "s#import \* as Main from '@gi/ui/main';#const Main = imports.ui.main;#g" "$file" - done - # extension.js and prefs.js can't be modules (yet) while dynamically loaded by GJS… # https://gjs.guide/extensions/overview/imports-and-modules.html#imports-and-modules # …and TypeScript can't compile specific files to "not a module" in overall module mode. # https://github.com/microsoft/TypeScript/issues/41567 sed -i -E "s#export \{\};##g" "$DESTDIR/extension.js" - - # Work around standard imports not available in files loaded by the shell, those can't be modules (yet) - # > Note that as of GNOME 44, neither GNOME Shell nor Extensions support ESModules, and must use GJS's custom import scheme. - # https://gjs.guide/extensions/overview/imports-and-modules.html#imports-and-modules - # https://gjs-docs.gnome.org/gjs/esmodules.md - # > JS ERROR: Extension randomwallpaper@iflow.space: SyntaxError: import declarations may only appear at top level of a module - sed -i -E "s#import (.*) from 'gi://.*';#const \1 = imports.gi.\1;#g" "$DESTDIR/extension.js" - sed -i -E "s#import (.*) from 'gi://.*';#const \1 = imports.gi.\1;#g" "$DESTDIR/prefs.js" + sed -i -E "s#export \{\};##g" "$DESTDIR/prefs.js" } # TODO: Drop compiled schemas when only targeting Gnome 44+ diff --git a/src/extension.ts b/src/extension.ts index 50609352..1fbf68ab 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,4 +1,10 @@ -import GLib from 'gi://GLib'; +// Use legacy style importing to work around standard imports not available in files loaded by the shell, those can't be modules (yet) +// > Note that as of GNOME 44, neither GNOME Shell nor Extensions support ESModules, and must use GJS custom import scheme. +// https://gjs.guide/extensions/overview/imports-and-modules.html#imports-and-modules +// https://gjs-docs.gnome.org/gjs/esmodules.md +// > JS ERROR: Extension randomwallpaper@iflow.space: SyntaxError: import declarations may only appear at top level of a module +// For correct typing use: 'InstanceType' +const GLib = imports.gi.GLib; import type * as LoggerNamespace from './logger.js'; import type * as AFTimer from './timer.js'; diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 0d5cf4ba..216dc749 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -7,7 +7,9 @@ import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; import St from 'gi://St'; -import * as PopupMenu from '@gi/ui/popupMenu'; +// Legacy importing style for shell internal bindings not available in standard import format +// For correct typing use: 'InstanceType' +const PopupMenu = imports.ui.popupMenu; import * as HistoryModule from './history.js'; import * as Settings from './settings.js'; diff --git a/src/prefs.ts b/src/prefs.ts index 8f29419e..25fd7c7e 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -1,8 +1,13 @@ -import Adw from 'gi://Adw'; -import Gio from 'gi://Gio'; -import Gtk from 'gi://Gtk'; - -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Use legacy style importing to work around standard imports not available in files loaded by the shell, those can't be modules (yet) +// > Note that as of GNOME 44, neither GNOME Shell nor Extensions support ESModules, and must use GJS custom import scheme. +// https://gjs.guide/extensions/overview/imports-and-modules.html#imports-and-modules +// https://gjs-docs.gnome.org/gjs/esmodules.md +// > JS ERROR: Extension randomwallpaper@iflow.space: SyntaxError: import declarations may only appear at top level of a module +// For correct typing use: 'InstanceType' +const Adw = imports.gi.Adw; +const Gio = imports.gi.Gio; +const Gtk = imports.gi.Gtk; +const ExtensionUtils = imports.misc.extensionUtils; import type * as SettingsNamespace from './settings.js'; import type * as UtilsNamespace from './utils.js'; @@ -36,7 +41,7 @@ function init(): void { * @param {Adw.PreferencesWindow} window Window the extension should fill */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -function fillPreferencesWindow(window: Adw.PreferencesWindow): void { +function fillPreferencesWindow(window: InstanceType): void { window.set_default_size(600, 720); // temporary fill window to prevent error message until modules are loaded const tmpPage = new Adw.PreferencesPage(); @@ -60,9 +65,9 @@ class RandomWallpaperSettings { private _sources: string[] = []; private _builder = new Gtk.Builder(); - private _saveDialog: Gtk.FileChooserNative | undefined; + private _saveDialog: InstanceType | undefined; - constructor(window: Adw.PreferencesWindow, tmpPage: Adw.PreferencesPage) { + constructor(window: InstanceType, tmpPage: InstanceType) { // Dynamically load own modules. This allows us to use proper ES6 Modules this._importModules().then(() => { window.remove(tmpPage); @@ -129,11 +134,11 @@ class RandomWallpaperSettings { this._sources.forEach(id => { const sourceRow = new SourceRow(undefined, id); - this._builder.get_object('sources_list').add(sourceRow); + this._builder.get_object>('sources_list').add(sourceRow); sourceRow.button_delete.connect('clicked', () => { sourceRow.clearConfig(); - this._builder.get_object('sources_list').remove(sourceRow); + this._builder.get_object>('sources_list').remove(sourceRow); Utils.removeItemOnce(this._sources, id); this._saveSources(); }); @@ -141,7 +146,7 @@ class RandomWallpaperSettings { import('./manager/wallpaperManager.js').then(module => { if (module.getWallpaperManager()?.isAvailable()) - this._builder.get_object('multiple_displays_row').set_sensitive(true); + this._builder.get_object>('multiple_displays_row').set_sensitive(true); }).catch(error => { this._logger.error(error); }); @@ -177,8 +182,8 @@ class RandomWallpaperSettings { } private _bindButtons(): void { - const newWallpaperButton: Adw.ActionRow = this._builder.get_object('request_new_wallpaper'); - const newWallpaperButtonLabel = newWallpaperButton.get_child() as Gtk.Label | null; + const newWallpaperButton: InstanceType = this._builder.get_object('request_new_wallpaper'); + const newWallpaperButtonLabel = newWallpaperButton.get_child() as InstanceType | null; const origNewWallpaperText = newWallpaperButtonLabel?.get_label() ?? 'Request New Wallpaper'; newWallpaperButton.connect('activated', () => { newWallpaperButtonLabel?.set_label('Loading ...'); @@ -196,7 +201,7 @@ class RandomWallpaperSettings { this._backendConnection.setBoolean('request-new-wallpaper', true); }); - const sourceRowList = this._builder.get_object('sources_list'); + const sourceRowList = this._builder.get_object>('sources_list'); this._builder.get_object('button_new_source').connect('clicked', () => { const sourceRow = new SourceRow(); sourceRowList.add(sourceRow); @@ -212,8 +217,8 @@ class RandomWallpaperSettings { }); } - private _bindHistorySection(window: Adw.PreferencesWindow): void { - const entryRow = this._builder.get_object('row_favorites_folder'); + private _bindHistorySection(window: InstanceType): void { + const entryRow = this._builder.get_object>('row_favorites_folder'); entryRow.text = this._settings.getString('favorites-folder'); this._settings.bind('history-length', diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index cf4cc103..1bf5aacd 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -1,10 +1,12 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; -import * as Main from '@gi/ui/main'; -import * as PanelMenu from '@gi/ui/panelMenu'; -import * as PopupMenu from '@gi/ui/popupMenu'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +// For correct typing use: 'InstanceType' +const ExtensionUtils = imports.misc.extensionUtils; +const Main = imports.ui.main; +const PanelMenu = imports.ui.panelMenu; +const PopupMenu = imports.ui.popupMenu; import * as CustomElements from './historyMenuElements.js'; import * as Settings from './settings.js'; diff --git a/src/settings.ts b/src/settings.ts index ebf3e630..1e7722c6 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,7 +1,8 @@ import Gio from 'gi://Gio'; import GObject from 'gi://GObject'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; const Self = ExtensionUtils.getCurrentExtension(); diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index a0e70210..41264f46 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -3,7 +3,8 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; import * as Settings from './../settings.js'; diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index c0bbabec..0c82795f 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -4,7 +4,8 @@ import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; import * as Settings from './../settings.js'; diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index c4a4f141..7d59eb60 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -4,7 +4,8 @@ import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; import * as Settings from './../settings.js'; diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index c1c6e2c8..f67279de 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -4,7 +4,8 @@ import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; import * as Settings from './../settings.js'; import * as Utils from './../utils.js'; diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index 00dc62da..22a07743 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -4,7 +4,8 @@ import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; import * as Settings from './../settings.js'; diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index 938bb260..fca37cb4 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -4,7 +4,8 @@ import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; import * as Settings from './../settings.js'; diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index e9c742d0..c69e71b3 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -5,7 +5,8 @@ import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; import Gtk from 'gi://Gtk'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; import * as Settings from './../settings.js'; diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 9a475aa9..241d77c0 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -1,7 +1,8 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; -import * as ExtensionUtils from '@gi/misc/extensionUtils'; +// Legacy importing style for shell internal bindings not available in standard import format +const ExtensionUtils = imports.misc.extensionUtils; import * as HistoryModule from './history.js'; import * as SettingsModule from './settings.js'; diff --git a/tsconfig.json b/tsconfig.json index 0752f9ea..8f3bab76 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,9 +18,6 @@ // https://gjs-docs.gnome.org/gjs/encoding.md // > The functions in this module are available globally, without import. ], - "paths": { - "@gi/*": ["../types/*"], - }, "typeRoots": [ "./node_modules/@gi-types", "./types", @@ -32,6 +29,8 @@ "adw1", "gtk4/adw", // locally expose 'gi://Adw' and extend with EntryRow "mappings", // missing mappings 'from @gi-types' to 'gi://' + "ui", // shell gi imports in legacy import style + "misc", // shell gi imports in legacy import style ], }, diff --git a/types/gtk4/adw/index.d.ts b/types/gtk4/adw/index.d.ts index 8553f180..39c6e8ee 100644 --- a/types/gtk4/adw/index.d.ts +++ b/types/gtk4/adw/index.d.ts @@ -98,3 +98,9 @@ declare module 'gi://Adw' { vfunc_set_selection_bounds(start_pos: number, end_pos: number): void; } } + +// extend gi imports interface with adw +// https://github.com/gi-ts/environment#importsgi +declare interface GjsGiImports { + Adw: typeof import('gi://Adw'); +} diff --git a/types/misc/index.d.ts b/types/misc/index.d.ts index 4fab801a..c372f217 100644 --- a/types/misc/index.d.ts +++ b/types/misc/index.d.ts @@ -1,102 +1,115 @@ -// https://github.com/yilozt/rounded-window-corners/blob/main/%40imports/misc/extensionUtils.d.ts -// GPL3 +/* eslint-disable */ -import Gio from 'gi://Gio'; +declare module 'extensionUtils' { + // https://github.com/yilozt/rounded-window-corners/blob/main/%40imports/misc/extensionUtils.d.ts + // GPL3 -/** - * getCurrentExtension: - * - * @returns {?object} - The current extension, or null if not called from - * an extension. - */ -declare function getCurrentExtension(): { - uuid: string, - path: string, - dir: Gio.File, - metadata: { - 'settings-schema': string, + import Gio from 'gi://Gio'; + + /** + * getCurrentExtension: + * + * @returns {?object} - The current extension, or null if not called from + * an extension. + */ + export function getCurrentExtension(): { uuid: string, + path: string, + dir: Gio.File, + metadata: { + 'settings-schema': string, + uuid: string, + } + }; + /** + * initTranslations: + * @param {string=} domain - the gettext domain to use + * + * Initialize Gettext to load translations from extensionsdir/locale. + * If @domain is not provided, it will be taken from metadata['gettext-domain'] + */ + export function initTranslations(domain?: string | undefined): void; + /** + * gettext: + * @param {string} str - the string to translate + * + * Translate @str using the extension's gettext domain + * + * @returns {string} - the translated string + * + */ + export function gettext(str: string): string; + /** + * ngettext: + * @param {string} str - the string to translate + * @param {string} strPlural - the plural form of the string + * @param {number} n - the quantity for which translation is needed + * + * Translate @str and choose plural form using the extension's + * gettext domain + * + * @returns {string} - the translated string + * + */ + export function ngettext(str: string, strPlural: string, n: number): string; + /** + * pgettext: + * @param {string} context - context to disambiguate @str + * @param {string} str - the string to translate + * + * Translate @str in the context of @context using the extension's + * gettext domain + * + * @returns {string} - the translated string + * + */ + export function pgettext(context: string, str: string): string; + export function callExtensionGettextFunc(func: any, ...args: any[]): any; + /** + * getSettings: + * @param {string?} schema - the GSettings schema id + * @returns {Gio.Settings} - a new settings object for @schema + * + * Builds and returns a GSettings schema for @schema, using schema files + * in extensionsdir/schemas. If @schema is omitted, it is taken from + * metadata['settings-schema']. + */ + export function getSettings(schema?: string | undefined): Gio.Settings; + /** + * openPrefs: + * + * Open the preference dialog of the current extension + */ + export function openPrefs(): Promise; + export function isOutOfDate(extension: any): boolean; + export function serializeExtension(extension: any): {}; + export function deserializeExtension(variant: any): { + metadata: {}; + }; + export function installImporter(extension: any): void; + export const Gettext: any; + export const Config: any; + export namespace ExtensionType { + const SYSTEM: number; + const PER_USER: number; + } + export namespace ExtensionState { + const ENABLED: number; + const DISABLED: number; + const ERROR: number; + const OUT_OF_DATE: number; + const DOWNLOADING: number; + const INITIALIZED: number; + const UNINSTALLED: number; } -}; -/** - * initTranslations: - * @param {string=} domain - the gettext domain to use - * - * Initialize Gettext to load translations from extensionsdir/locale. - * If @domain is not provided, it will be taken from metadata['gettext-domain'] - */ -declare function initTranslations(domain?: string | undefined): void; -/** - * gettext: - * @param {string} str - the string to translate - * - * Translate @str using the extension's gettext domain - * - * @returns {string} - the translated string - * - */ -declare function gettext(str: string): string; -/** - * ngettext: - * @param {string} str - the string to translate - * @param {string} strPlural - the plural form of the string - * @param {number} n - the quantity for which translation is needed - * - * Translate @str and choose plural form using the extension's - * gettext domain - * - * @returns {string} - the translated string - * - */ -declare function ngettext(str: string, strPlural: string, n: number): string; -/** - * pgettext: - * @param {string} context - context to disambiguate @str - * @param {string} str - the string to translate - * - * Translate @str in the context of @context using the extension's - * gettext domain - * - * @returns {string} - the translated string - * - */ -declare function pgettext(context: string, str: string): string; -declare function callExtensionGettextFunc(func: any, ...args: any[]): any; -/** - * getSettings: - * @param {string?} schema - the GSettings schema id - * @returns {Gio.Settings} - a new settings object for @schema - * - * Builds and returns a GSettings schema for @schema, using schema files - * in extensionsdir/schemas. If @schema is omitted, it is taken from - * metadata['settings-schema']. - */ -declare function getSettings(schema?: string | undefined): Gio.Settings; -/** - * openPrefs: - * - * Open the preference dialog of the current extension - */ -declare function openPrefs(): Promise; -declare function isOutOfDate(extension: any): boolean; -declare function serializeExtension(extension: any): {}; -declare function deserializeExtension(variant: any): { - metadata: {}; -}; -declare function installImporter(extension: any): void; -declare const Gettext: any; -declare const Config: any; -declare namespace ExtensionType { - const SYSTEM: number; - const PER_USER: number; + export const SERIALIZED_PROPERTIES: string[]; } -declare namespace ExtensionState { - const ENABLED: number; - const DISABLED: number; - const ERROR: number; - const OUT_OF_DATE: number; - const DOWNLOADING: number; - const INITIALIZED: number; - const UNINSTALLED: number; + +declare interface GjsMiscImports { + extensionUtils: typeof import('extensionUtils'); +} + +// extend imports interface with misc elements +declare interface GjsImports { + misc: GjsMiscImports; } -declare const SERIALIZED_PROPERTIES: string[]; diff --git a/types/ui/index.d.ts b/types/ui/index.d.ts new file mode 100644 index 00000000..3ad783a4 --- /dev/null +++ b/types/ui/index.d.ts @@ -0,0 +1,17 @@ +/* eslint-disable */ +// doing similar to https://github.com/gi-ts/environment + +/// +/// +/// + +declare interface GjsUiImports { + main: typeof import('main'); + panelMenu: typeof import('panelMenu'); + popupMenu: typeof import('popupMenu'); +} + +// extend imports interface with ui elements +declare interface GjsImports { + ui: GjsUiImports; +} diff --git a/types/ui/main.d.ts b/types/ui/main.d.ts index ffae452f..d0c00bfd 100644 --- a/types/ui/main.d.ts +++ b/types/ui/main.d.ts @@ -1,9 +1,13 @@ -import St from 'gi://St'; +/* eslint-disable */ -import * as PanelMenu from '@gi/ui/panelMenu'; +declare module 'main' { + import St from 'gi://St'; -declare class Panel extends St.Widget { - addToStatusArea(role: string, indicator: PanelMenu.Button, position?: number, box?: unknown): PanelMenu.Button -} + import * as PanelMenu from 'panelMenu'; + + export class Panel extends St.Widget { + addToStatusArea(role: string, indicator: PanelMenu.Button, position?: number, box?: unknown): PanelMenu.Button + } -export const panel: Panel; + export const panel: Panel; +} diff --git a/types/ui/panelMenu.d.ts b/types/ui/panelMenu.d.ts index 2fb74e89..a22857b9 100644 --- a/types/ui/panelMenu.d.ts +++ b/types/ui/panelMenu.d.ts @@ -1,13 +1,15 @@ /* eslint-disable */ -import St from 'gi://St'; +declare module 'panelMenu' { + import St from 'gi://St'; -import {PopupMenu} from '@gi/ui/popupMenu'; + import {PopupMenu} from 'popupMenu'; -declare class ButtonBox extends St.Widget{} + export class ButtonBox extends St.Widget{} -export class Button extends ButtonBox { - menu: PopupMenu; + export class Button extends ButtonBox { + menu: PopupMenu; - constructor(menuAlignment: number, nameText: string, dontCreateMenu?: boolean); + constructor(menuAlignment: number, nameText: string, dontCreateMenu?: boolean); + } } diff --git a/types/ui/popupMenu.d.ts b/types/ui/popupMenu.d.ts index fbe39ed7..2d12b9ed 100644 --- a/types/ui/popupMenu.d.ts +++ b/types/ui/popupMenu.d.ts @@ -1,74 +1,75 @@ /* eslint-disable */ -// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/popupMenu.js - -import Clutter from 'gi://Clutter'; -import St from 'gi://St'; - - -// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/misc/signals.js -declare class EventEmitter { - connectObject(args: unknown): unknown; - connect_object(args: unknown): unknown; - disconnect_object(args: unknown): unknown; - disconnectObject(args: unknown): unknown; - - // don't know where these are: - connect(key: string, callback: (actor: typeof this, state: unknown[]) => void): void; -} - -declare class PopupMenuBase extends EventEmitter { - actor: Clutter.Actor; - box: St.BoxLayout; - - addMenuItem(menuItem: PopupMenuSection | PopupSubMenuMenuItem | PopupSeparatorMenuItem | PopupBaseMenuItem, position?: number): void; - removeAll(): void; -} - -declare class PopupBaseMenuItem extends St.BoxLayout { - actor: typeof this; - // get actor(): typeof this; - get sensitive(): boolean; - set sensitive(sensitive: boolean); -} - -export class PopupMenu extends PopupMenuBase { - constructor(sourceActor: Clutter.Actor, arrowAlignment: unknown, arrowSide: unknown) -} - -export class PopupMenuItem extends PopupBaseMenuItem { - constructor(text: string, params?: unknown) - label: St.Label; -} - -export class PopupSubMenuMenuItem extends PopupBaseMenuItem { - constructor(text: string, wantIcon: boolean) - - label: St.Label; - menu: PopupSubMenu; -} - -export class PopupSubMenu extends PopupMenuBase { - actor: St.ScrollView; -} - -export class PopupMenuSection extends PopupMenuBase { - actor: St.BoxLayout | Clutter.Actor; -} - -export class PopupSeparatorMenuItem extends PopupBaseMenuItem {} -export class Switch extends St.Bin {} -export class PopupSwitchMenuItem extends PopupBaseMenuItem { - constructor(text: string, active: boolean, params?: { - reactive: boolean | undefined, - activate: boolean | undefined, - hover: boolean | undefined, - style_class: unknown | null | undefined, - can_focus: boolean | undefined - }) - - setToggleState(state: boolean): void; +declare module 'popupMenu' { + // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/popupMenu.js + + import Clutter from 'gi://Clutter'; + import St from 'gi://St'; + + // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/misc/signals.js + export class EventEmitter { + connectObject(args: unknown): unknown; + connect_object(args: unknown): unknown; + disconnect_object(args: unknown): unknown; + disconnectObject(args: unknown): unknown; + + // don't know where these are: + connect(key: string, callback: (actor: typeof this, state: unknown[]) => void): void; + } + + export class PopupMenuBase extends EventEmitter { + actor: Clutter.Actor; + box: St.BoxLayout; + + addMenuItem(menuItem: PopupMenuSection | PopupSubMenuMenuItem | PopupSeparatorMenuItem | PopupBaseMenuItem, position?: number): void; + removeAll(): void; + } + + export class PopupBaseMenuItem extends St.BoxLayout { + actor: typeof this; + // get actor(): typeof this; + get sensitive(): boolean; + set sensitive(sensitive: boolean); + } + + export class PopupMenu extends PopupMenuBase { + constructor(sourceActor: Clutter.Actor, arrowAlignment: unknown, arrowSide: unknown) + } + + export class PopupMenuItem extends PopupBaseMenuItem { + constructor(text: string, params?: unknown) + label: St.Label; + } + + export class PopupSubMenuMenuItem extends PopupBaseMenuItem { + constructor(text: string, wantIcon: boolean) + + label: St.Label; + menu: PopupSubMenu; + } + + export class PopupSubMenu extends PopupMenuBase { + actor: St.ScrollView; + } + + export class PopupMenuSection extends PopupMenuBase { + actor: St.BoxLayout | Clutter.Actor; + } + + export class PopupSeparatorMenuItem extends PopupBaseMenuItem {} + export class Switch extends St.Bin {} + export class PopupSwitchMenuItem extends PopupBaseMenuItem { + constructor(text: string, active: boolean, params?: { + reactive: boolean | undefined, + activate: boolean | undefined, + hover: boolean | undefined, + style_class: unknown | null | undefined, + can_focus: boolean | undefined + }) + + setToggleState(state: boolean): void; + } + export class PopupImageMenuItem extends PopupBaseMenuItem {} + export class PopupDummyMenu extends EventEmitter {} + export class PopupMenuManager {} } -export class PopupImageMenuItem extends PopupBaseMenuItem {} -export class PopupDummyMenu extends EventEmitter {} -export class PopupMenuManager {} From f1cbfceb3b17478e0686b4ae77ddf0b6dce80448 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 2 Jun 2023 14:39:40 +0200 Subject: [PATCH 55/87] Fix blueprint-compiler for github workflow on ubuntu --- .github/workflows/build.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8af6e6e9..120c392c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,16 @@ jobs: - name: Install dependencies run: | sudo apt -q update - sudo apt -q install --no-install-recommends npm blueprint-compiler libglib2.0-0 bash gnome-shell libgtk-4-bin libgtk-4-common libgtk-4-dev libadwaita-1-dev gir1.2-adw-1 gir1.2-gtk-4.0 zstd + sudo apt -q install --no-install-recommends npm libglib2.0-0 bash gnome-shell libgtk-4-bin libgtk-4-common libgtk-4-dev libadwaita-1-dev gir1.2-adw-1 gir1.2-gtk-4.0 zstd + # TODO: Remove the next step once "Jammy" (22.04) receives version 0.8+ and add to the install list above + # https://launchpad.net/ubuntu/+source/blueprint-compiler + - name: Build recent blueprint-compiler + run: | + sudo apt -q install --no-install-recommends meson ninja-build + git clone https://gitlab.gnome.org/jwestman/blueprint-compiler.git + cd blueprint-compiler + meson _build + sudo ninja -C _build install - name: Check out repository code uses: actions/checkout@v3 - name: Ubuntu specific workarounds From cd1e195024ee66d91bdf454a4b8f70ea591a2bf4 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 2 Jun 2023 15:49:36 +0200 Subject: [PATCH 56/87] Manually backport libadwaita in github workflow --- .github/workflows/build.yml | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 120c392c..96b28a8f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,25 +35,16 @@ jobs: sudo ninja -C _build install - name: Check out repository code uses: actions/checkout@v3 - - name: Ubuntu specific workarounds + # TODO: Remove the next step once "Jammy" (22.04) receives version 1.2+ + # https://launchpad.net/ubuntu/+source/libadwaita-1 + - name: Update to recent libadwaita run: | - cd ${{github.workspace}}/.github/workflows || exit 1 mkdir libadwaita cd libadwaita || exit 1 - wget https://archlinux.org/packages/extra/x86_64/libadwaita/download/ -O libadwaita.tar.zstd - tar -xf libadwaita.tar.zstd - sed -i -E 's#libdir=\$\{prefix\}/lib#libdir=${prefix}/lib/x86_64-linux-gnu#g' usr/lib/pkgconfig/libadwaita-1.pc - sed -i -E 's#Libs: -L\$\{prefix\}/lib -ladwaita-1#Libs: -L${prefix}/lib/x86_64-linux-gnu -ladwaita-1#g' usr/lib/pkgconfig/libadwaita-1.pc - sudo rm /usr/lib/x86_64-linux-gnu/girepository-1.0/Adw-1.typelib - sudo rm -r /usr/include/libadwaita-1 - sudo rm /usr/lib/x86_64-linux-gnu/libadwaita-1.so - sudo rm /usr/share/gir-1.0/Adw-1.gir - sudo rm /usr/lib/x86_64-linux-gnu/pkgconfig/libadwaita-1.pc - sudo mv usr/lib/girepository-1.0/Adw-1.typelib /usr/lib/x86_64-linux-gnu/girepository-1.0/ - sudo mv usr/include/libadwaita-1 /usr/include/ - sudo mv usr/lib/libadwaita-1.so.0 /usr/lib/x86_64-linux-gnu/ - sudo mv usr/lib/pkgconfig/libadwaita-1.pc /usr/lib/x86_64-linux-gnu/pkgconfig/ - sudo mv usr/share/gir-1.0/Adw-1.gir /usr/share/gir-1.0/ + wget https://launchpad.net/ubuntu/+source/libadwaita-1/1.2.0-1ubuntu2/+build/24480571/+files/gir1.2-adw-1_1.2.0-1ubuntu2_amd64.deb + wget https://launchpad.net/ubuntu/+source/libadwaita-1/1.2.0-1ubuntu2/+build/24480571/+files/libadwaita-1-0_1.2.0-1ubuntu2_amd64.deb + wget https://launchpad.net/ubuntu/+source/libadwaita-1/1.2.0-1ubuntu2/+build/24480571/+files/libadwaita-1-dev_1.2.0-1ubuntu2_amd64.deb + sudo dpkg --recursive --install . - run: ${{github.workspace}}/build.sh setup_env - run: ${{github.workspace}}/build.sh build - run: ${{github.workspace}}/build.sh format From 9abbed1c197ca18a7b1369c0ece7e4ea6c8d9209 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 2 Jun 2023 18:24:30 +0200 Subject: [PATCH 57/87] Update deps and replace deprecated lint rule --- .eslintrc-gjs.yml | 5 +- .eslintrc.yml | 5 +- package-lock.json | 679 +++++++++++++++++++++++----------------------- package.json | 20 +- 4 files changed, 364 insertions(+), 345 deletions(-) diff --git a/.eslintrc-gjs.yml b/.eslintrc-gjs.yml index ef0396b1..5ca674bc 100644 --- a/.eslintrc-gjs.yml +++ b/.eslintrc-gjs.yml @@ -78,12 +78,15 @@ rules: jsdoc/check-tag-names: error jsdoc/check-types: error jsdoc/implements-on-classes: error - jsdoc/newline-after-description: error jsdoc/require-jsdoc: error jsdoc/require-param: error jsdoc/require-param-description: error jsdoc/require-param-name: error jsdoc/require-param-type: error + jsdoc/tag-lines: + - error + - never + - startLines: 1 key-spacing: - error - beforeColon: false diff --git a/.eslintrc.yml b/.eslintrc.yml index 0043ff9f..2ae01d8c 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -73,12 +73,15 @@ rules: jsdoc/check-tag-names: error jsdoc/check-types: error jsdoc/implements-on-classes: error - jsdoc/newline-after-description: error jsdoc/require-jsdoc: error jsdoc/require-param: error jsdoc/require-param-description: error jsdoc/require-param-name: error jsdoc/require-param-type: error + jsdoc/tag-lines: + - error + - never + - startLines: 1 key-spacing: - error - beforeColon: false diff --git a/package-lock.json b/package-lock.json index 5d4c8973..7bb659fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,42 +5,66 @@ "packages": { "": { "devDependencies": { - "@gi-types/adw1": "^1.1.1", - "@gi-types/base-types": "^1.0.0", - "@gi-types/gjs-environment": "^1.1.0", - "@gi-types/gtk4-types": "^1.0.0", - "@gi-types/shell": "^0.1.6", - "@typescript-eslint/eslint-plugin": "^5.46.0", - "@typescript-eslint/parser": "^5.46.0", - "eslint": "^8.29.0", - "eslint-plugin-jsdoc": "^39.6.4", - "typescript": "^4.9.4" + "@gi-types/adw1": "latest", + "@gi-types/base-types": "latest", + "@gi-types/gjs-environment": "latest", + "@gi-types/gtk4-types": "latest", + "@gi-types/shell": "latest", + "@typescript-eslint/eslint-plugin": "latest", + "@typescript-eslint/parser": "latest", + "eslint": "latest", + "eslint-plugin-jsdoc": "latest", + "typescript": "latest" } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", - "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", + "version": "0.39.4", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", + "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", "dev": true, "dependencies": { "comment-parser": "1.3.1", - "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "~3.1.0" + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" }, "engines": { - "node": "^14 || ^16 || ^17 || ^18 || ^19" + "node": ">=16" + } + }, + "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.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^9.5.2", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -54,6 +78,15 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/js": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", + "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@gi-types/accountsservice1": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@gi-types/accountsservice1/-/accountsservice1-1.0.1.tgz", @@ -873,9 +906,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -941,30 +974,31 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz", - "integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz", + "integrity": "sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.46.0", - "@typescript-eslint/type-utils": "5.46.0", - "@typescript-eslint/utils": "5.46.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/type-utils": "5.59.8", + "@typescript-eslint/utils": "5.59.8", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" }, @@ -986,14 +1020,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz", - "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.8.tgz", + "integrity": "sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.46.0", - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/typescript-estree": "5.59.8", "debug": "^4.3.4" }, "engines": { @@ -1013,13 +1047,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz", - "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz", + "integrity": "sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/visitor-keys": "5.46.0" + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/visitor-keys": "5.59.8" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1030,13 +1064,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz", - "integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz", + "integrity": "sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.46.0", - "@typescript-eslint/utils": "5.46.0", + "@typescript-eslint/typescript-estree": "5.59.8", + "@typescript-eslint/utils": "5.59.8", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1057,9 +1091,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz", - "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.8.tgz", + "integrity": "sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1070,13 +1104,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz", - "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz", + "integrity": "sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/visitor-keys": "5.46.0", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/visitor-keys": "5.59.8", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1097,18 +1131,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.0.tgz", - "integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.8.tgz", + "integrity": "sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==", "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.46.0", - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/typescript-estree": "5.59.8", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -1123,12 +1157,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz", - "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz", + "integrity": "sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/types": "5.59.8", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1140,9 +1174,9 @@ } }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1200,6 +1234,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -1375,13 +1418,16 @@ } }, "node_modules/eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", + "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.41.0", + "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -1390,24 +1436,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "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.15.0", - "grapheme-splitter": "^1.0.4", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -1415,7 +1459,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -1431,21 +1474,22 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "39.6.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.4.tgz", - "integrity": "sha512-fskvdLCfwmPjHb6e+xNGDtGgbF8X7cDwMtVLAP2WwSf9Htrx68OAx31BESBM1FAwsN2HTQyYQq7m4aW4Q4Nlag==", + "version": "46.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.1.0.tgz", + "integrity": "sha512-NpjpSuWR+Wwxzmssji7AVty1Vu0JvI7v+cTj+Rw1nKVjGv2eMvLGM/SI4VpgTXp82JbLtFOsA2QYLHT3YSmASA==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.36.1", + "@es-joy/jsdoccomment": "~0.39.4", + "are-docs-informative": "^0.0.2", "comment-parser": "1.3.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", - "esquery": "^1.4.0", - "semver": "^7.3.8", + "esquery": "^1.5.0", + "semver": "^7.5.1", "spdx-expression-parse": "^3.0.1" }, "engines": { - "node": "^14 || ^16 || ^17 || ^18 || ^19" + "node": ">=16" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" @@ -1464,46 +1508,22 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "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.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -1511,6 +1531,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -1523,14 +1546,14 @@ } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1540,9 +1563,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "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" @@ -1646,9 +1669,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -1752,9 +1775,9 @@ } }, "node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1792,6 +1815,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "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", @@ -1802,9 +1831,9 @@ } }, "node_modules/ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -1896,16 +1925,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1919,9 +1938,9 @@ } }, "node_modules/jsdoc-type-pratt-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", - "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", "dev": true, "engines": { "node": ">=12.0.0" @@ -2163,9 +2182,9 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" @@ -2191,18 +2210,6 @@ } ] }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2261,9 +2268,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2322,9 +2329,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, "node_modules/strip-ansi": { @@ -2427,16 +2434,16 @@ } }, "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uri-js": { @@ -2499,26 +2506,41 @@ }, "dependencies": { "@es-joy/jsdoccomment": { - "version": "0.36.1", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.36.1.tgz", - "integrity": "sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==", + "version": "0.39.4", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", + "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", "dev": true, "requires": { "comment-parser": "1.3.1", - "esquery": "^1.4.0", - "jsdoc-type-pratt-parser": "~3.1.0" + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" } }, + "@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, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", - "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.15.0", + "espree": "^9.5.2", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -2526,6 +2548,12 @@ "strip-json-comments": "^3.1.1" } }, + "@eslint/js": { + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", + "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "dev": true + }, "@gi-types/accountsservice1": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@gi-types/accountsservice1/-/accountsservice1-1.0.1.tgz", @@ -3345,9 +3373,9 @@ } }, "@humanwhocodes/config-array": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", - "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -3394,82 +3422,83 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz", - "integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz", + "integrity": "sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.46.0", - "@typescript-eslint/type-utils": "5.46.0", - "@typescript-eslint/utils": "5.46.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/type-utils": "5.59.8", + "@typescript-eslint/utils": "5.59.8", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" } }, "@typescript-eslint/parser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.0.tgz", - "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.8.tgz", + "integrity": "sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.46.0", - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/typescript-estree": "5.59.8", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz", - "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz", + "integrity": "sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==", "dev": true, "requires": { - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/visitor-keys": "5.46.0" + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/visitor-keys": "5.59.8" } }, "@typescript-eslint/type-utils": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz", - "integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz", + "integrity": "sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.46.0", - "@typescript-eslint/utils": "5.46.0", + "@typescript-eslint/typescript-estree": "5.59.8", + "@typescript-eslint/utils": "5.59.8", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.0.tgz", - "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.8.tgz", + "integrity": "sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz", - "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz", + "integrity": "sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/visitor-keys": "5.46.0", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/visitor-keys": "5.59.8", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -3478,35 +3507,35 @@ } }, "@typescript-eslint/utils": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.0.tgz", - "integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.8.tgz", + "integrity": "sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==", "dev": true, "requires": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.46.0", - "@typescript-eslint/types": "5.46.0", - "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/scope-manager": "5.59.8", + "@typescript-eslint/types": "5.59.8", + "@typescript-eslint/typescript-estree": "5.59.8", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz", - "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", + "version": "5.59.8", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz", + "integrity": "sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/types": "5.59.8", "eslint-visitor-keys": "^3.3.0" } }, "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "dev": true }, "acorn-jsx": { @@ -3543,6 +3572,12 @@ "color-convert": "^2.0.1" } }, + "are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3674,13 +3709,16 @@ "dev": true }, "eslint": { - "version": "8.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.29.0.tgz", - "integrity": "sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg==", + "version": "8.41.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", + "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.11.6", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.41.0", + "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -3689,24 +3727,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "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.15.0", - "grapheme-splitter": "^1.0.4", + "globals": "^13.19.0", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -3714,16 +3750,15 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -3739,17 +3774,18 @@ } }, "eslint-plugin-jsdoc": { - "version": "39.6.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.6.4.tgz", - "integrity": "sha512-fskvdLCfwmPjHb6e+xNGDtGgbF8X7cDwMtVLAP2WwSf9Htrx68OAx31BESBM1FAwsN2HTQyYQq7m4aW4Q4Nlag==", + "version": "46.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.1.0.tgz", + "integrity": "sha512-NpjpSuWR+Wwxzmssji7AVty1Vu0JvI7v+cTj+Rw1nKVjGv2eMvLGM/SI4VpgTXp82JbLtFOsA2QYLHT3YSmASA==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "~0.36.1", + "@es-joy/jsdoccomment": "~0.39.4", + "are-docs-informative": "^0.0.2", "comment-parser": "1.3.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", - "esquery": "^1.4.0", - "semver": "^7.3.8", + "esquery": "^1.5.0", + "semver": "^7.5.1", "spdx-expression-parse": "^3.0.1" } }, @@ -3763,44 +3799,27 @@ "estraverse": "^4.1.1" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true }, "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -3886,9 +3905,9 @@ "dev": true }, "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -3968,9 +3987,9 @@ } }, "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -3996,6 +4015,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4003,9 +4028,9 @@ "dev": true }, "ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "import-fresh": { @@ -4073,12 +4098,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -4089,9 +4108,9 @@ } }, "jsdoc-type-pratt-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", - "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", "dev": true }, "json-schema-traverse": { @@ -4270,9 +4289,9 @@ "dev": true }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, "queue-microtask": { @@ -4281,12 +4300,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4318,9 +4331,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -4364,9 +4377,9 @@ } }, "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, "strip-ansi": { @@ -4439,9 +4452,9 @@ "dev": true }, "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", "dev": true }, "uri-js": { diff --git a/package.json b/package.json index 8c02b763..d7b3a6d7 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "devDependencies": { - "@gi-types/adw1": "^1.1.1", - "@gi-types/base-types": "^1.0.0", - "@gi-types/gjs-environment": "^1.1.0", - "@gi-types/gtk4-types": "^1.0.0", - "@gi-types/shell": "^0.1.6", - "@typescript-eslint/eslint-plugin": "^5.46.0", - "@typescript-eslint/parser": "^5.46.0", - "eslint": "^8.29.0", - "eslint-plugin-jsdoc": "^39.6.4", - "typescript": "^4.9.4" + "@gi-types/adw1": "latest", + "@gi-types/base-types": "latest", + "@gi-types/gjs-environment": "latest", + "@gi-types/gtk4-types": "latest", + "@gi-types/shell": "latest", + "@typescript-eslint/eslint-plugin": "latest", + "@typescript-eslint/parser": "latest", + "eslint": "latest", + "eslint-plugin-jsdoc": "latest", + "typescript": "latest" } } From b3a55d574240a7171eca43227c61022971370bf6 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 4 Jun 2023 22:52:33 +0200 Subject: [PATCH 58/87] Add missing extension meta object --- src/extension.ts | 3 ++- src/prefs.ts | 3 ++- types/misc/index.d.ts | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1fbf68ab..d608016a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -10,6 +10,7 @@ import type * as LoggerNamespace from './logger.js'; import type * as AFTimer from './timer.js'; import type * as WallpaperControllerNamespace from './wallpaperController.js'; import type * as RandomWallpaperMenuNamespace from './randomWallpaperMenu.js'; +import type {ExtensionMeta} from 'ExtensionMeta'; let Logger: typeof LoggerNamespace | null = null; let Timer: typeof AFTimer | null = null; @@ -20,7 +21,7 @@ let RandomWallpaperMenu: typeof RandomWallpaperMenuNamespace | null = null; * */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -function init(): Extension { +function init(unusedMeta: ExtensionMeta): Extension { return new Extension(); } diff --git a/src/prefs.ts b/src/prefs.ts index 25fd7c7e..0f971a6f 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -13,6 +13,7 @@ import type * as SettingsNamespace from './settings.js'; import type * as UtilsNamespace from './utils.js'; import type * as LoggerNamespace from './logger.js'; import type * as SourceRowNamespace from './ui/sourceRow.js'; +import type {ExtensionMeta} from 'ExtensionMeta'; let Settings: typeof SettingsNamespace; let Utils: typeof UtilsNamespace; @@ -25,7 +26,7 @@ const Self = ExtensionUtils.getCurrentExtension(); * */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars -function init(): void { +function init(unusedMeta: ExtensionMeta): void { // Convenience.initTranslations(); } diff --git a/types/misc/index.d.ts b/types/misc/index.d.ts index c372f217..e9a4589d 100644 --- a/types/misc/index.d.ts +++ b/types/misc/index.d.ts @@ -113,3 +113,35 @@ declare interface GjsMiscImports { declare interface GjsImports { misc: GjsMiscImports; } + +declare module 'ExtensionMeta' { + import type {File} from 'gi://Gio'; + + /** + * An object describing the extension and various properties available for extensions to use. + * + * Some properties may only be available in some versions of GNOME Shell, while others may not be meant for extension authors to use. All properties should be considered read-only. + */ + export class ExtensionMeta { + /** the metadata.json file, parsed as JSON */ + readonly metadata: unknown; + /** the extension UUID */ + readonly uuid: string; + /** the extension type; `1` for system, `2` for user */ + readonly type: number; + /** the extension directory */ + readonly dir: File; + /** the extension directory path */ + readonly path: string; + /** an error message or an empty string if no error */ + readonly error: string; + /** whether the extension has a preferences dialog */ + readonly hasPrefs: boolean; + /** whether the extension has a pending update */ + readonly hasUpdate: boolean; + /** whether the extension can be enabled/disabled */ + readonly canChange: boolean; + /** a list of supported session modes */ + readonly sessionModes: string[]; + } +} From db7c010993e0657a3a103550d372b0c6052aa6f9 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 4 Jun 2023 22:55:26 +0200 Subject: [PATCH 59/87] Require documentation --- .eslintrc.yml | 12 ++- src/adapter/baseAdapter.ts | 41 ++++++++- src/adapter/genericJson.ts | 31 ++++++- src/adapter/localFolder.ts | 28 ++++++ src/adapter/reddit.ts | 30 +++++++ src/adapter/unsplash.ts | 34 ++++++++ src/adapter/urlSource.ts | 18 ++++ src/adapter/wallhaven.ts | 35 ++++++++ src/extension.ts | 41 +++++++-- src/history.ts | 53 +++++++++++- src/historyMenuElements.ts | 75 +++++++++++++++- src/jsonPath.ts | 4 + src/logger.ts | 42 ++++++++- src/manager/hydraPaper.ts | 34 +++++++- src/manager/superPaper.ts | 39 ++++++++- src/manager/wallpaperManager.ts | 21 +++-- src/prefs.ts | 54 ++++++++---- src/randomWallpaperMenu.ts | 44 +++++++++- src/settings.ts | 149 +++++++++++++++++++++++++++++++- src/soupBowl.ts | 34 ++++++-- src/timer.ts | 43 ++++++++- src/ui/genericJson.ts | 11 +++ src/ui/localFolder.ts | 11 +++ src/ui/reddit.ts | 11 +++ src/ui/sourceRow.ts | 27 +++++- src/ui/unsplash.ts | 18 ++++ src/ui/urlSource.ts | 11 +++ src/ui/wallhaven.ts | 11 +++ src/utils.ts | 16 ++++ src/wallpaperController.ts | 139 ++++++++++++++++++++++++++--- 30 files changed, 1042 insertions(+), 75 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 2ae01d8c..f7487a8e 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -6,6 +6,7 @@ env: extends: - 'eslint:recommended' - 'plugin:@typescript-eslint/recommended-requiring-type-checking' + - 'plugin:jsdoc/recommended-typescript-error' parser: "@typescript-eslint/parser" plugins: - jsdoc @@ -73,7 +74,13 @@ rules: jsdoc/check-tag-names: error jsdoc/check-types: error jsdoc/implements-on-classes: error - jsdoc/require-jsdoc: error + jsdoc/no-types: off + jsdoc/require-description: error + jsdoc/require-jsdoc: + - error + - require: + ClassDeclaration: true + MethodDefinition: true jsdoc/require-param: error jsdoc/require-param-description: error jsdoc/require-param-name: error @@ -122,7 +129,8 @@ rules: - error - allow: - '!!' - no-invalid-this: error + no-invalid-this: off + "@typescript-eslint/no-invalid-this": "error" no-iterator: error no-label-var: error no-lonely-if: error diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index a11943b4..fb097807 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -6,6 +6,9 @@ import {HistoryEntry} from './../history.js'; import {Logger} from './../logger.js'; import {SoupBowl} from './../soupBowl.js'; +/** + * Abstract base adapter for subsequent classes to implement. + */ abstract class BaseAdapter { protected _bowl = new SoupBowl(); @@ -14,6 +17,19 @@ abstract class BaseAdapter { protected _settings: SettingsModule.Settings; protected _sourceName: string; + /** + * Create a new base adapter. + * + * Exposes settings and utilities for subsequent classes. + * Previously saved settings will be used if the ID matches. + * + * @param {object} params Parameter object with settings + * @param {string} params.defaultName Default adapter name + * @param {string} params.id Unique ID + * @param {string | null} params.name Custom name, falls back to the default name on null + * @param {string} params.schemaID ID of the adapter specific schema ID + * @param {string} params.schemaPath Path to the adapter specific settings schema + */ constructor(params: { defaultName: string; id: string; @@ -34,19 +50,22 @@ abstract class BaseAdapter { } /** - * Retrieves a new url for an image and crafts a new HistoryEntry. + * Retrieves new URLs for images and crafts new HistoryEntries. * * @param {number} count Number of requested wallpaper + * @returns {HistoryEntry[]} Array of crafted HistoryEntries * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty */ - // eslint-disable-next-line no-unused-vars abstract requestRandomImage (count: number): Promise; /** - * copy file from uri to local wallpaper directory and returns the full filepath - * of the written file. + * Fetches an image according to a given HistoryEntry. + * + * This default implementation requests the image in HistoryEntry.source.imageDownloadUrl + * using Soup and saves it to HistoryEntry.path * * @param {HistoryEntry} historyEntry The historyEntry to fetch + * @returns {Promise} unaltered HistoryEntry */ async fetchFile(historyEntry: HistoryEntry): Promise { const file = Gio.file_new_for_path(historyEntry.path); @@ -68,6 +87,13 @@ abstract class BaseAdapter { return historyEntry; } + /** + * Check if an array already contains a matching HistoryEntry. + * + * @param {HistoryEntry[]} array Array to search in + * @param {string} uri URI to search for + * @returns {boolean} Wether the array contains an item with $uri + */ protected _includesWallpaper(array: HistoryEntry[], uri: string): boolean { for (const element of array) { if (element.source.imageDownloadUrl === uri) @@ -81,6 +107,7 @@ abstract class BaseAdapter { * Check if this image is in the list of blocked images. * * @param {string} filename Name of the image + * @returns {boolean} Whether the image is blocked */ protected _isImageBlocked(filename: string): boolean { const blockedFilenames = this._generalSettings.getStrv('blocked-images'); @@ -94,6 +121,12 @@ abstract class BaseAdapter { } // eslint-disable-next-line no-unused-vars + /** + * asd + * + * @param {string} err asd + * @param {string} callback asd + */ protected _error(err: string, callback?: (element: null, error: { error: string }) => void): void { const error = {error: err}; this._logger.error(JSON.stringify(error)); diff --git a/src/adapter/genericJson.ts b/src/adapter/genericJson.ts index 33c37e83..9bb63272 100644 --- a/src/adapter/genericJson.ts +++ b/src/adapter/genericJson.ts @@ -7,11 +7,22 @@ import {HistoryEntry} from './../history.js'; /** How many times the service should be queried at maximum. */ const MAX_SERVICE_RETRIES = 5; -/** How many times we should try to get a new image from an array. - * No new request are being made. */ +/** + * How many times we should try to get a new image from an array. + * No new request are being made. + */ const MAX_ARRAY_RETRIES = 5; +/** + * Adapter for generic JSON image sources. + */ class GenericJsonAdapter extends BaseAdapter { + /** + * Create a new generic json adapter. + * + * @param {string} id Unique ID + * @param {string} name Custom name of this adapter + */ constructor(id: string, name: string) { super({ defaultName: 'Generic JSON Source', @@ -22,6 +33,13 @@ class GenericJsonAdapter extends BaseAdapter { }); } + /** + * Retrieves new URLs for images and crafts new HistoryEntries. + * + * @param {number} count Number of requested wallpaper + * @returns {HistoryEntry[]} Array of crafted HistoryEntries + * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty + */ private async _getHistoryEntry(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; @@ -113,6 +131,15 @@ class GenericJsonAdapter extends BaseAdapter { return wallpaperResult; } + /** + * Retrieves new URLs for images and crafts new HistoryEntries. + * + * Can internally query the request URL multiple times because it's unknown how many images will be reported back. + * + * @param {number} count Number of requested wallpaper + * @returns {HistoryEntry[]} Array of crafted HistoryEntries + * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty + */ async requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index ab579fae..4d5050c3 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -10,7 +10,16 @@ import {HistoryEntry} from './../history.js'; // https://gjs.guide/guides/gjs/asynchronous-programming.html#promisify-helper Gio._promisify(Gio.File.prototype, 'copy_async', 'copy_finish'); +/** + * Adapter for fetching from the local filesystem. + */ class LocalFolderAdapter extends BaseAdapter { + /** + * Create a new local folder adapter. + * + * @param {string} id Unique ID + * @param {string} name Custom name of this adapter + */ constructor(id: string, name: string) { super({ defaultName: 'Local Folder', @@ -21,6 +30,13 @@ class LocalFolderAdapter extends BaseAdapter { }); } + /** + * Retrieves new URLs for images and crafts new HistoryEntries. + * + * @param {number} count Number of requested wallpaper + * @returns {HistoryEntry[]} Array of crafted HistoryEntries + * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty + */ requestRandomImage(count: number): Promise { return new Promise((resolve, reject) => { const folder = Gio.File.new_for_path(this._settings.getString('folder')); @@ -59,6 +75,12 @@ class LocalFolderAdapter extends BaseAdapter { }); } + /** + * Copies a file from the filesystem to the destination folder. + * + * @param {HistoryEntry} historyEntry The historyEntry to fetch + * @returns {Promise} unaltered HistoryEntry + */ async fetchFile(historyEntry: HistoryEntry): Promise { const sourceFile = Gio.File.new_for_uri(historyEntry.source.imageDownloadUrl); const targetFile = Gio.File.new_for_path(historyEntry.path); @@ -74,6 +96,12 @@ class LocalFolderAdapter extends BaseAdapter { } // https://gjs.guide/guides/gio/file-operations.html#recursively-deleting-a-directory + /** + * Walk recursively through a folder and retrieve a list of all images. + * + * @param {Gio.File} directory Directory to scan + * @returns {Gio.File[]} List of images + */ private _listDirectory(directory: Gio.File): Gio.File[] { const iterator = directory.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null); diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index bf365fff..6856381e 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -28,7 +28,16 @@ interface RedditSubmission { } } +/** + * Adapter for Reddit image sources. + */ class RedditAdapter extends BaseAdapter { + /** + * Create a new Reddit adapter. + * + * @param {string} id Unique ID + * @param {string} name Custom name of this adapter + */ constructor(id: string, name: string) { super({ defaultName: 'Reddit', @@ -39,10 +48,23 @@ class RedditAdapter extends BaseAdapter { }); } + /** + * Replace an HTML & with an actual & symbol. + * + * @param {string} string String to replace in + * @returns {string} String with replaced symbols + */ private _ampDecode(string: string): string { return string.replace(/&/g, '&'); } + /** + * Retrieves new URLs for images and crafts new HistoryEntries. + * + * @param {number} count Number of requested wallpaper + * @returns {HistoryEntry[]} Array of crafted HistoryEntries + * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty + */ async requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; const subreddits = this._settings.getString('subreddits').split(',').map(s => s.trim()).join('+'); @@ -114,6 +136,14 @@ class RedditAdapter extends BaseAdapter { return wallpaperResult; } + /** + * Check if the response is expected to be a response by Reddit. + * + * Primarily in use for typescript typing. + * + * @param {unknown} object Unknown object to narrow down + * @returns {boolean} Wether the response is from Reddit + */ private _isRedditResponse(object: unknown): object is RedditResponse { if (typeof object === 'object' && object && diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index f3d73d11..9ccab5d8 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -7,6 +7,9 @@ import {HistoryEntry} from './../history.js'; /** How many times the service should be queried at maximum. */ const MAX_SERVICE_RETRIES = 5; +/** + * Adapter for image sources using Unsplash. + */ class UnsplashAdapter extends BaseAdapter { private _sourceUrl = 'https://source.unsplash.com'; @@ -20,6 +23,12 @@ class UnsplashAdapter extends BaseAdapter { 'constraintValue': '', }; + /** + * Create a new Unsplash adapter. + * + * @param {string} id Unique ID + * @param {string} name Custom name of this adapter + */ constructor(id: string | null, name: string | null) { super({ defaultName: 'Unsplash', @@ -30,6 +39,12 @@ class UnsplashAdapter extends BaseAdapter { }); } + /** + * Retrieves a new URL for an image and crafts new HistoryEntry. + * + * @returns {HistoryEntry} Crafted HistoryEntry + * @throws {Error} Error with description + */ private async _getHistoryEntry(): Promise { this._readOptionsFromSettings(); const optionsString = this._generateOptionsString(); @@ -67,6 +82,15 @@ class UnsplashAdapter extends BaseAdapter { return historyEntry; } + /** + * Retrieves new URLs for images and crafts new HistoryEntries. + * + * Can internally query the request URL multiple times because only one image will be reported back. + * + * @param {number} count Number of requested wallpaper + * @returns {HistoryEntry[]} Array of crafted HistoryEntries + * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty + */ async requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; @@ -95,6 +119,13 @@ class UnsplashAdapter extends BaseAdapter { return wallpaperResult; } + /** + * Create an option string based on user settings. + * + * Does not refresh settings itself. + * + * @returns {string} Options string + */ private _generateOptionsString(): string { const options = this._options; let optionsString = ''; @@ -128,6 +159,9 @@ class UnsplashAdapter extends BaseAdapter { return optionsString; } + /** + * Freshly read the user settings options. + */ private _readOptionsFromSettings(): void { this._options.w = this._settings.getInt('image-width'); this._options.h = this._settings.getInt('image-height'); diff --git a/src/adapter/urlSource.ts b/src/adapter/urlSource.ts index 4fa0ed17..23060107 100644 --- a/src/adapter/urlSource.ts +++ b/src/adapter/urlSource.ts @@ -3,7 +3,16 @@ import * as SettingsModule from './../settings.js'; import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; +/** + * Adapter for using a single static URL as an image source. + */ class UrlSourceAdapter extends BaseAdapter { + /** + * Create a new static url adapter. + * + * @param {string} id Unique ID + * @param {string} name Custom name of this adapter + */ constructor(id: string, name: string) { super({ defaultName: 'Static URL', @@ -14,6 +23,15 @@ class UrlSourceAdapter extends BaseAdapter { }); } + /** + * Retrieves new URLs for images and crafts new HistoryEntries. + * + * Can internally query the request URL multiple times because only one image will be reported back. + * + * @param {number} count Number of requested wallpaper + * @returns {HistoryEntry[]} Array of crafted HistoryEntries + * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty + */ requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index 4d3f03db..b48e8f36 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -25,6 +25,9 @@ interface WallhavenSearchResponse { }[] } +/** + * Adapter for Wallhaven image sources. + */ class WallhavenAdapter extends BaseAdapter { private _options: QueryOptions = { q: '', @@ -37,6 +40,12 @@ class WallhavenAdapter extends BaseAdapter { colors: '', }; + /** + * Create a new wallhaven adapter. + * + * @param {string} id Unique ID + * @param {string} name Custom name of this adapter + */ constructor(id: string, name: string) { super({ id, @@ -47,6 +56,13 @@ class WallhavenAdapter extends BaseAdapter { }); } + /** + * Retrieves new URLs for images and crafts new HistoryEntries. + * + * @param {number} count Number of requested wallpaper + * @returns {HistoryEntry[]} Array of crafted HistoryEntries + * @throws {HistoryEntry[]} Array of crafted historyEntries, can be empty + */ async requestRandomImage(count: number): Promise { const wallpaperResult: HistoryEntry[] = []; @@ -106,6 +122,14 @@ class WallhavenAdapter extends BaseAdapter { return wallpaperResult; } + /** + * Create an option string based on user settings. + * + * Does not refresh settings itself. + * + * @param {QueryOptions} options Options to check + * @returns {string} Options string + */ private _generateOptionsString(options: T): string { let optionsString = ''; @@ -121,6 +145,14 @@ class WallhavenAdapter extends BaseAdapter { return optionsString; } + /** + * Check if the response is expected to be a response by Wallhaven. + * + * Primarily in use for typescript typing. + * + * @param {unknown} object Unknown object to narrow down + * @returns {boolean} Wether the response is from Reddit + */ private _isWallhavenResponse(object: unknown): object is WallhavenSearchResponse { if (typeof object === 'object' && object && @@ -132,6 +164,9 @@ class WallhavenAdapter extends BaseAdapter { return false; } + /** + * Freshly read the user settings options. + */ private _readOptionsFromSettings(): void { const keywords = this._settings.getString('keyword').split(','); if (keywords.length > 0) { diff --git a/src/extension.ts b/src/extension.ts index d608016a..0bbaf168 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -18,19 +18,38 @@ let WallpaperController: typeof WallpaperControllerNamespace | null = null; let RandomWallpaperMenu: typeof RandomWallpaperMenuNamespace | null = null; /** + * This function is called once when your extension is loaded, not enabled. This + * is a good time to setup translations or anything else you only do once. * + * You MUST NOT make any changes to GNOME Shell, connect any signals or add any + * MainLoop sources here. + * + * @param {ExtensionMeta} unusedMeta An extension meta object, https://gjs.guide/extensions/overview/anatomy.html#extension-meta-object + * @returns {Extension} an object with enable() and disable() methods */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars function init(unusedMeta: ExtensionMeta): Extension { return new Extension(); } +/** + * Own extension class object. Entry point for Gnome Shell hooks. + * + * The functions enable() and disable() are required. + */ class Extension { private _logger: LoggerNamespace.Logger | null = null; private _wallpaperController: WallpaperControllerNamespace.WallpaperController | null = null; private _panelMenu: RandomWallpaperMenuNamespace.RandomWallpaperMenu | null = null; private _timer: AFTimer.AFTimer | null = null; + /** + * This function is called when your extension is enabled, which could be + * done in GNOME Extensions, when you log in or when the screen is unlocked. + * + * This is when you should setup any UI for your extension, change existing + * widgets, connect signals or modify GNOME Shell's behavior. + */ enable(): void { // Workaround crash when initializing the gnome shell with this extension active while being on X11 // https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6691 @@ -71,6 +90,13 @@ class Extension { }); } + /** + * This function is called when your extension is uninstalled, disabled in + * GNOME Extensions, when you log out or when the screen locks. + * + * Anything you created, modified or setup in enable() MUST be undone here. + * Not doing so is the most common reason extensions are rejected in review! + */ disable(): void { if (this._logger) this._logger.info('Disable extension.'); @@ -93,12 +119,17 @@ class Extension { RandomWallpaperMenu = null; } + /** + * Import helper function. + * + * Loads all required modules async. + * This allows to omit the legacy GJS style imports (`const asd = imports.gi.asd`) + * and use proper modules for subsequent files. + * + * When the shell allows proper modules for loaded files (extension.js and prefs.js) + * this function can be removed and replaced by normal import statements. + */ private async _importModules(): Promise { - // All imports as dynamic loads to work around the fact this module won't be in a topmost - // context inside the gnome shell and can't use import statements (yet). - // PopOS' tiling extension and RoundedCorners Extension work around the above limitation by - // manually rewriting the exported javascript file. We also have to do this but - // not for our own modules. const loggerPromise = import('./logger.js'); const timerPromise = import('./timer.js'); const wallpaperPromise = import('./wallpaperController.js'); diff --git a/src/history.ts b/src/history.ts index 725966bd..18a58435 100644 --- a/src/history.ts +++ b/src/history.ts @@ -23,6 +23,9 @@ interface AdapterInfo { type: number | null; } +/** + * Defines an image with core properties. + */ class HistoryEntry { timestamp = new Date().getTime(); /** Unique identifier, concat of timestamp and name */ @@ -36,6 +39,15 @@ class HistoryEntry { type: null, }; + /** + * Create a new HistoryEntry. + * + * The name, id, and path will be prefilled. + * + * @param {string | null} author Author of the image or null + * @param {string | null} source The image source or null + * @param {string} url The request URL of the image + */ constructor(author: string | null, source: string | null, url: string) { this.source = { author, @@ -53,18 +65,35 @@ class HistoryEntry { } } +/** + * Controls the history and related code parts. + */ class HistoryController { history: HistoryEntry[] = []; size = 10; private _settings = new Settings(); + /** + * Create a new HistoryController. + * + * Loads an existing history from the settings schema. + * + * @param {string} wallpaperLocation Root save location for new HistoryEntries. + */ constructor(wallpaperLocation: string) { _wallpaperLocation = wallpaperLocation; this.load(); } + /** + * Insert images at the beginning of the history. + * + * Throws old images out of the stack and saves to the schema. + * + * @param {HistoryEntry[]} historyElements Array of elements to insert + */ insert(historyElements: HistoryEntry[]): void { for (const historyElement of historyElements) this.history.unshift(historyElement); @@ -77,6 +106,7 @@ class HistoryController { * Set the given id to to the first history element (the current one) * * @param {string} id ID of the historyEntry + * @returns {boolean} Whether the sorting was successful */ promoteToActive(id: string): boolean { const element = this.get(id); @@ -94,9 +124,10 @@ class HistoryController { } /** - * Returns the corresponding HistoryEntry or null + * Get a specific HistoryEntry by ID. * - * @param {string} id ID of the historyEntry + * @param {string} id ID of the HistoryEntry + * @returns {HistoryEntry | null} The corresponding HistoryEntry or null */ get(id: string): HistoryEntry | null { for (const elem of this.history) { @@ -109,11 +140,19 @@ class HistoryController { /** * Get the current history element. + * + * @returns {HistoryEntry} Current first entry */ getCurrentEntry(): HistoryEntry { return this.history[0]; } + /** + * Get a HistoryEntry by its file path. + * + * @param {string} path Path to search for + * @returns {HistoryEntry | null} The corresponding HistoryEntry or null + */ getEntryByPath(path: string): HistoryEntry | null { for (const element of this.history) { if (element.path === path) @@ -125,6 +164,8 @@ class HistoryController { /** * Get a random HistoryEntry. + * + * @returns {HistoryEntry} Random entry */ getRandom(): HistoryEntry { return this.history[Utils.getRandomNumber(this.history.length)]; @@ -159,6 +200,8 @@ class HistoryController { /** * Clear the history and delete all photos except the current one. + * + * @returns {boolean} Whether the deletion was successful */ clear(): boolean { const firstHistoryElement = this.history[0]; @@ -203,6 +246,12 @@ class HistoryController { } } + /** + * Check if an object is a HistoryEntry. + * + * @param {unknown} object Object to check + * @returns {boolean} Whether the object is a HistoryEntry + */ private _isHistoryEntry(object: unknown): object is HistoryEntry { if (typeof object === 'object' && object && diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 216dc749..0f86b72c 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -38,7 +38,14 @@ const HistoryElement = GObject.registerClass({ historyId: string; historyEntry: HistoryModule.HistoryEntry; - constructor(params: object | undefined, historyEntry: HistoryModule.HistoryEntry, index: number) { + /** + * Create a new menu element for a HistoryEntry. + * + * @param {object | undefined} unusedParams Unused params object from the PopupMenu.PopupSubMenuMenuItem + * @param {HistoryModule.HistoryEntry} historyEntry HistoryEntry this menu element serves + * @param {number} index Place in history + */ + constructor(unusedParams: object | undefined, historyEntry: HistoryModule.HistoryEntry, index: number) { super('', false); this.historyEntry = historyEntry; @@ -196,6 +203,14 @@ const HistoryElement = GObject.registerClass({ }); } + /** + * Add an image to the blocking list. + * + * Uses the filename for distinction. + * + * @param {HistoryModule.HistoryEntry} entry Entry to block + */ + // FIXME: entry is unnecessary private _addToBlocklist(entry: HistoryModule.HistoryEntry): void { if (!entry.adapter?.id || entry.adapter.id === '-1' || !entry.name) { this._logger.error('Image entry is missing information'); @@ -213,6 +228,9 @@ const HistoryElement = GObject.registerClass({ generalSettings.setStrv('blocked-images', blockedFilenames); } + /** + * Save the image to the favorites folder. + */ private async _saveImage(): Promise { if (!this.historyEntry.path || !this.historyEntry.name) throw new Error('Image entry is missing information'); @@ -253,6 +271,11 @@ const HistoryElement = GObject.registerClass({ throw new Error(`Failed writing file contents: ${message}`); } + /** + * Prefix the menu label with a number. + * + * @param {number} index Number to prefix + */ setIndex(index: number): void { this._prefixLabel.set_text(`${String(index)}.`); } @@ -261,6 +284,12 @@ const HistoryElement = GObject.registerClass({ const CurrentImageElement = GObject.registerClass({ GTypeName: 'CurrentImageElement', }, class CurrentImageElement extends HistoryElement { + /** + * Create a new image element for the currently active wallpaper. + * + * @param {object | undefined} params Option object of PopupMenu.PopupSubMenuMenuItem + * @param {HistoryModule.HistoryEntry} historyEntry History entry this menu is for + */ constructor(params: object | undefined, historyEntry: HistoryModule.HistoryEntry) { super(params, historyEntry, 0); @@ -270,7 +299,7 @@ const CurrentImageElement = GObject.registerClass({ }); /** - * Element for the New Wallpaper button and the remaining time for the auto fetch + * Element for the "New Wallpaper" button and the remaining time for the auto fetch * feature. * The remaining time will only be displayed if the af-feature is activated. */ @@ -281,6 +310,11 @@ class NewWallpaperElement extends PopupMenu.PopupBaseMenuItem { private _timer = Timer.getTimer(); private _remainingLabel; + /** + * Create a button for fetching new wallpaper + * + * @param {object | undefined} params Options object of PopupMenu.PopupBaseMenuItem + */ constructor(params: object | undefined) { super(params); @@ -302,6 +336,9 @@ class NewWallpaperElement extends PopupMenu.PopupBaseMenuItem { this.actor.add_child(container); } + /** + * Checks the AF-setting and shows/hides the remaining minutes section. + */ show(): void { if (this._timer.isActive()) { const remainingMinutes = this._timer.remainingMinutes(); @@ -326,9 +363,15 @@ class NewWallpaperElement extends PopupMenu.PopupBaseMenuItem { } }); +/** + * The status element in the Gnome Shell top panel bar. + */ class StatusElement { icon; + /** + * Create a new menu status element. + */ constructor() { this.icon = new St.Icon({ icon_name: 'preferences-desktop-wallpaper-symbolic', @@ -336,6 +379,9 @@ class StatusElement { }); } + /** + * Pulsate the icon opacity as a loading animation. + */ startLoading(): void { // FIXME: Don't know where this is defined // @ts-expect-error @@ -349,12 +395,18 @@ class StatusElement { }); } + /** + * Stop pulsating the icon opacity. + */ stopLoading(): void { this.icon.remove_all_transitions(); this.icon.opacity = 255; } } +/** + * The history section holding multiple history elements. + */ class HistorySection extends PopupMenu.PopupMenuSection { /** * Cache HistoryElements for performance of long histories. @@ -362,6 +414,9 @@ class HistorySection extends PopupMenu.PopupMenuSection { private _historySectionCache = new Map>(); private _historyCache: HistoryModule.HistoryEntry[] = []; + /** + * Create a new history section. + */ constructor() { super(); @@ -373,6 +428,14 @@ class HistorySection extends PopupMenu.PopupMenuSection { this.actor.add_actor(this.box); } + /** + * Clear and rebuild the history element list using cached elements where possible. + * + * @param {HistoryModule.HistoryEntry[]} history History list to rebuild from. + * @param {(HistoryElement) => void} onEnter Function to call on menu element key-focus-in + * @param {(HistoryElement) => void} onLeave Function to call on menu element key-focus-out + * @param {(HistoryElement) => void} onSelect Function to call on menu element enter-event + */ updateList( history: HistoryModule.HistoryEntry[], onEnter: (actor: InstanceType) => void, @@ -412,6 +475,11 @@ class HistorySection extends PopupMenu.PopupMenuSection { this._historyCache = history; } + /** + * Cleanup the cache for entries not in $existingIDs. + * + * @param {string[]} existingIDs List with IDs that exists in the history + */ private _cleanupHistoryCache(existingIDs: string[]): void { const destroyIDs = Array.from(this._historySectionCache.keys()).filter(i => existingIDs.indexOf(i) === -1); @@ -421,6 +489,9 @@ class HistorySection extends PopupMenu.PopupMenuSection { }); } + /** + * Clear and remove all history elements. + */ clear(): void { this._cleanupHistoryCache([]); this.removeAll(); diff --git a/src/jsonPath.ts b/src/jsonPath.ts index 4d7f0bb4..d1192428 100644 --- a/src/jsonPath.ts +++ b/src/jsonPath.ts @@ -8,6 +8,7 @@ import * as Utils from './utils.js'; * * @param {unknown} inputObject A JSON object * @param {string} inputString JSONPath to follow, see wiki for syntax + * @returns {[unknown, string]} Tuple with an object of unknown type and a chosen JSONPath string */ function getTarget(inputObject: unknown, inputString: string): [object: unknown, chosenPath: string] { if (!inputObject) @@ -63,6 +64,7 @@ function getTarget(inputObject: unknown, inputString: string): [object: unknown, * * @param {object} inputObject JSON object * @param {string} keyString Name of the key in the object + * @returns {unknown | null} Found object member or null */ function _getObjectMember(inputObject: object, keyString: string): unknown | null { if (keyString === '$') @@ -80,6 +82,7 @@ function _getObjectMember(inputObject: object, keyString: string): unknown | nul * Returns the value of a random key of a given array. * * @param {Array} array Array with values + * @returns {[T, number]} Tuple with an array member and index of that member */ function _randomElement(array: Array): [T, number] { const randomNumber = Utils.getRandomNumber(array.length); @@ -94,6 +97,7 @@ function _randomElement(array: Array): [T, number] { * * @param {string} randomPath Path containing '@random' to resolve * @param {string} resolvedPath Path with resolved '@random' + * @returns {string} Input string with replaced '@random' */ function replaceRandomInPath(randomPath: string, resolvedPath: string): string { if (!randomPath.includes('@random')) diff --git a/src/logger.ts b/src/logger.ts index 38e30035..13f95989 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -11,16 +11,31 @@ const enum LogLevel { } type LogLevelStrings = keyof typeof LogLevel; +/** + * + */ class Logger { private _prefix: string; private _callingClass: string; private _settings = new Settings(); + /** + * Create a new logging helper. + * + * @param {string} prefix Custom string to prepend + * @param {string} callingClass Class this logger writes messages for + */ constructor(prefix: string, callingClass: string) { this._prefix = prefix; this._callingClass = callingClass; } + /** + * Helper function to safely log to the console. + * + * @param {LogLevelStrings} level String representation of the selected log level + * @param {unknown} message Message to send, ideally an Error() or string + */ private _log(level: LogLevelStrings, message: unknown): void { let errorMessage = String(message); @@ -36,10 +51,20 @@ class Logger { logError(message); } - private _selectedLogLevel(): number { + /** + * Get the log level selected by the user. + * + * @returns {LogLevel} Log level + */ + private _selectedLogLevel(): LogLevel { return this._settings.getEnum('log-level'); } + /** + * Log a DEBUG message. + * + * @param {unknown} message Message to send, ideally an Error() or string + */ debug(message: unknown): void { if (this._selectedLogLevel() < LogLevel.DEBUG) return; @@ -47,6 +72,11 @@ class Logger { this._log('DEBUG', message); } + /** + * Log an INFO message. + * + * @param {unknown} message Message to send, ideally an Error() or string + */ info(message: unknown): void { if (this._selectedLogLevel() < LogLevel.INFO) return; @@ -54,6 +84,11 @@ class Logger { this._log('INFO', message); } + /** + * Log a WARN message. + * + * @param {unknown} message Message to send, ideally an Error() or string + */ warn(message: unknown): void { if (this._selectedLogLevel() < LogLevel.WARNING) return; @@ -61,6 +96,11 @@ class Logger { this._log('WARNING', message); } + /** + * Log an ERROR message. + * + * @param {unknown} message Message to send, ideally an Error() or string + */ error(message: unknown): void { if (this._selectedLogLevel() < LogLevel.ERROR) return; diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 67fb0472..2903e034 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -7,11 +7,19 @@ import {Logger} from '../logger.js'; import {WallpaperManager} from './wallpaperManager.js'; import {Settings} from '../settings.js'; +/** + * Wrapper for HydraPaper using it as a manager. + */ class HydraPaper implements WallpaperManager { private _command: string[] | null = null; private _cancellable: Gio.Cancellable | null = null; private _logger = new Logger('RWG3', 'HydraPaper'); + /** + * Checks if Superpaper is available in the $PATH. + * + * @returns {boolean} Whether Superpaper is found + */ isAvailable(): boolean { if (this._command !== null) return true; @@ -31,6 +39,9 @@ class HydraPaper implements WallpaperManager { return this._command !== null; } + /** + * Forcefully stop a previously started HydraPaper process. + */ cancelRunning(): void { if (this._cancellable === null) return; @@ -43,11 +54,13 @@ class HydraPaper implements WallpaperManager { /** * Run HydraPaper in CLI mode. * - * HydraPaper will combine all images in wallpaperArray into a single image and save - * it into the users cache folder. - * Afterward HydraPaper will set the mode to 'spanned' and the 'picture-uri' or 'picture-uri-dark'. + * HydraPaper: + * - Saves merged images in the cache folder. + * - Sets picture-option to spanned + * - Sets picture-uri or picture-uri-dark depending on $darkmode + * - Needs matching image path count and display count * - * @param {string[]} wallpaperArray Array of image paths + * @param {string[]} wallpaperArray Array of image paths, should match the display count * @param {boolean} darkmode Use darkmode, gives different image in cache path */ private async _run(wallpaperArray: string[], darkmode: boolean = false): Promise { @@ -76,6 +89,19 @@ class HydraPaper implements WallpaperManager { this._cancellable = null; } + /** + * Set the wallpapers for a given mode. + * + * Modes: + * - 0: Background + * - 1: Lock screen + * - 2: Background and lock screen + * + * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count + * @param {number} mode Enum indicating what images to change + * @param {Settings} backgroundSettings Settings object containing the background settings + * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings + */ async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings?: Settings, screensaverSettings?: Settings): Promise { if ((mode === 0 || mode === 2) && backgroundSettings) { await this._run(wallpaperPaths); diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts index 2c4aba02..874f82f8 100644 --- a/src/manager/superPaper.ts +++ b/src/manager/superPaper.ts @@ -7,11 +7,19 @@ import {Logger} from './../logger.js'; import {Settings} from './../settings.js'; import {WallpaperManager} from './wallpaperManager.js'; +/** + * Wrapper for Superpaper using it as a manager. + */ class Superpaper implements WallpaperManager { private _command: string[] | null = null; private _cancellable: Gio.Cancellable | null = null; private _logger = new Logger('RWG3', 'Superpaper'); + /** + * Checks if Superpaper is available in the $PATH. + * + * @returns {boolean} Whether Superpaper is found + */ isAvailable(): boolean { if (this._command !== null) return true; @@ -25,6 +33,9 @@ class Superpaper implements WallpaperManager { return false; } + /** + * Forcefully stop a previously started Superpaper process. + */ cancelRunning(): void { if (!this._cancellable) return; @@ -34,6 +45,19 @@ class Superpaper implements WallpaperManager { this._cancellable = null; } + /** + * Set the wallpapers for a given mode. + * + * Modes: + * - 0: Background + * - 1: Lock screen + * - 2: Background and lock screen + * + * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count + * @param {number} mode Enum indicating what images to change + * @param {Settings} backgroundSettings Settings object containing the background settings + * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings + */ async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings: Settings, screensaverSettings: Settings): Promise { if ((mode === 0 || mode === 2) && backgroundSettings) await this._run(wallpaperPaths); @@ -60,10 +84,17 @@ class Superpaper implements WallpaperManager { } // https://github.com/hhannine/superpaper/blob/master/docs/cli-usage.md - // * Saves merged images alternating in "$XDG_CACHE_HOME/superpaper/temp/cli-{a,b}.png" - // * Sets picture-option to spanned - // * Sets both picture-uri options - // * Can use only single images + /** + * Run Superpaper in CLI mode. + * + * Superpaper: + * - Saves merged images alternating in "$XDG_CACHE_HOME/superpaper/temp/cli-{a,b}.png" + * - Sets picture-option to spanned + * - Sets both picture-uri options, light and dark + * - Can use only single images + * + * @param {string[]} wallpaperArray Array of paths to the desired wallpapers, should match the display count, can be a single image + */ private async _run(wallpaperArray: string[]): Promise { // Cancel already running processes before starting new ones this.cancelRunning(); diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts index 9f6cc787..bc06cf18 100644 --- a/src/manager/wallpaperManager.ts +++ b/src/manager/wallpaperManager.ts @@ -3,6 +3,11 @@ import type {Settings} from './../settings.js'; import {HydraPaper} from './hydraPaper.js'; import {Superpaper} from './superPaper.js'; +/** + * Wallpaper manager is a base class for external manager to implement. + * + * Currently this is only used when in multiple monitor mode. + */ abstract class WallpaperManager { abstract isAvailable(): boolean; abstract cancelRunning(): void; @@ -11,20 +16,24 @@ abstract class WallpaperManager { * Set the wallpapers for a given mode. * * Modes: - * 0: Background - * 1: Lock screen - * 2: Background and lock screen + * - 0: Background + * - 1: Lock screen + * - 2: Background and lock screen * * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count - * @param {number} mode Mode to operate in - * @param {Settings} backgroundSettings Settings object of the background - * @param {Settings} screensaverSettings Settings object of the screensaver + * @param {number} mode Enum indicating what images to change + * @param {Settings} backgroundSettings Settings object containing the background settings + * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings */ abstract setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings: Settings, screensaverSettings: Settings): Promise; } /** + * Get a wallpaper manager. + * + * Checks for HydraPaper first and then for Superpaper. * + * @returns {WallpaperManager | null} Wallpaper manager if found or null */ function getWallpaperManager(): WallpaperManager | null { const hydraPaper = new HydraPaper(); diff --git a/src/prefs.ts b/src/prefs.ts index 0f971a6f..65e71846 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -23,7 +23,9 @@ let SourceRow: typeof SourceRowNamespace.SourceRow; const Self = ExtensionUtils.getCurrentExtension(); /** + * Like `extension.js` this is used for any one-time setup like translations. * + * @param {ExtensionMeta} unusedMeta - An extension meta object, https://gjs.guide/extensions/overview/anatomy.html#extension-meta-object */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars function init(unusedMeta: ExtensionMeta): void { @@ -38,8 +40,13 @@ function init(unusedMeta: ExtensionMeta): void { // https://gjs.guide/extensions/development/preferences.html#preferences-window // Gnome 42+ /** + * This function is called when the preferences window is first created to fill + * the `Adw.PreferencesWindow`. * - * @param {Adw.PreferencesWindow} window Window the extension should fill + * This function will only be called by GNOME 42 and later. If this function is + * present, `buildPrefsWidget()` will NOT be called. + * + * @param {Adw.PreferencesWindow} window - The preferences window */ // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars function fillPreferencesWindow(window: InstanceType): void { @@ -51,14 +58,9 @@ function fillPreferencesWindow(window: InstanceType | undefined; + /** + * Create a new ui settings class. + * + * Replaces the placeholder $tmpPage once the modules are loaded and the real pages are available. + * + * @param {Adw.PreferencesWindow} window Window to fill with settings + * @param {Adw.PreferencesPage} tmpPage Placeholder settings page to replace + */ constructor(window: InstanceType, tmpPage: InstanceType) { // Dynamically load own modules. This allows us to use proper ES6 Modules this._importModules().then(() => { @@ -160,15 +170,16 @@ class RandomWallpaperSettings { } /** - * Import modules ES6 style instead the built-in gjs style. - * Allows proper async/await in imported modules. + * Import helper function. + * + * Loads all required modules async. + * This allows to omit the legacy GJS style imports (`const asd = imports.gi.asd`) + * and use proper modules for subsequent files. + * + * When the shell allows proper modules for loaded files (extension.js and prefs.js) + * this function can be removed and replaced by normal import statements. */ private async _importModules(): Promise { - // All imports as dynamic loads to work around the fact this module won't be in a topmost - // context inside the gnome shell and can't use import statements (yet). - // PopOS' tiling extension and RoundedCorners Extension work around the above limitation by - // manually rewriting the exported javascript file. We also have to do this but - // not for our own modules. const loggerPromise = import('./logger.js'); const utilsPromise = import('./utils.js'); const sourceRowPromise = import('./ui/sourceRow.js'); @@ -182,6 +193,9 @@ class RandomWallpaperSettings { Settings = moduleSettings; } + /** + * Bind button clicks to logic. + */ private _bindButtons(): void { const newWallpaperButton: InstanceType = this._builder.get_object('request_new_wallpaper'); const newWallpaperButtonLabel = newWallpaperButton.get_child() as InstanceType | null; @@ -218,6 +232,11 @@ class RandomWallpaperSettings { }); } + /** + * Bind button clicks related to the history. + * + * @param {Adw.PreferencesWindow} window Preferences window + */ private _bindHistorySection(window: InstanceType): void { const entryRow = this._builder.get_object>('row_favorites_folder'); entryRow.text = this._settings.getString('favorites-folder'); @@ -293,6 +312,9 @@ class RandomWallpaperSettings { }); } + /** + * Save all configured sources to the settings. + */ private _saveSources(): void { this._settings.setStrv('sources', this._sources); } diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 1bf5aacd..7bcf59c1 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -1,3 +1,9 @@ +// These two rules contradict each other in TS and JS mode for @this in function descriptions below. +// @this can be removed in TS but then JS complains about missing @this in documentation. +// Disabling these rules for this specific file for now. +/* eslint-disable jsdoc/check-tag-names */ +/* eslint-disable jsdoc/valid-types */ + import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; @@ -17,6 +23,9 @@ import {WallpaperController} from './wallpaperController.js'; const Self = ExtensionUtils.getCurrentExtension(); +/** + * PanelMenu for this extension. + */ class RandomWallpaperMenu { private _logger = new Logger('RWG3', 'RandomWallpaperEntry'); private _backendConnection = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); @@ -29,6 +38,11 @@ class RandomWallpaperMenu { private _panelMenu; private _wallpaperController; + /** + * Create a new PanelMenu. + * + * @param {WallpaperController} wallpaperController The wallpaper controller controlling the wallpapers :D + */ constructor(wallpaperController: WallpaperController) { this._wallpaperController = wallpaperController; @@ -164,6 +178,9 @@ class RandomWallpaperMenu { this._settings.observe('history', this.setHistoryList.bind(this)); } + /** + * Initialize remaining PanelMenu bits. + */ init(): void { this.updatePanelMenuVisibility(); this.setHistoryList(); @@ -172,6 +189,9 @@ class RandomWallpaperMenu { Main.panel.addToStatusArea('random-wallpaper-menu', this._panelMenu); } + /** + * Remove the PanelMenu and remnants. + */ cleanup(): void { this.clearHistoryList(); this._panelMenu.destroy(); @@ -181,6 +201,9 @@ class RandomWallpaperMenu { this._settings.disconnect(this._hidePanelIconHandler); } + /** + * Hide or show the PanelMenu based on user settings. + */ updatePanelMenuVisibility(): void { if (this._settings.getBoolean('hide-panel-icon')) this._panelMenu.hide(); @@ -188,6 +211,9 @@ class RandomWallpaperMenu { this._panelMenu.show(); } + /** + * Recreates the current background section based on the history. + */ setCurrentBackgroundElement(): void { this._currentBackgroundSection.removeAll(); @@ -200,6 +226,9 @@ class RandomWallpaperMenu { } } + /** + * Recreates the history list based on the history. + */ setHistoryList(): void { this._wallpaperController.update(); this.setCurrentBackgroundElement(); @@ -213,8 +242,10 @@ class RandomWallpaperMenu { } /** - * @this {RandomWallpaperMenu} RandomWallpaperMenu + * Function for events that should happen on element leave. + * * @param {InstanceType} unusedActor The activating panel item + * @this RandomWallpaperMenu */ function onLeave(this: RandomWallpaperMenu, unusedActor: InstanceType): void { if (!this._wallpaperController.prohibitNewWallpaper && this._savedBackgroundUri) @@ -222,8 +253,10 @@ class RandomWallpaperMenu { } /** - * @this {RandomWallpaperMenu} RandomWallpaperMenu + * Function for events that should happen on element enter. + * * @param {InstanceType} actor The activating panel item + * @this RandomWallpaperMenu */ function onEnter(this: RandomWallpaperMenu, actor: InstanceType): void { if (!this._wallpaperController.prohibitNewWallpaper) @@ -231,8 +264,10 @@ class RandomWallpaperMenu { } /** - * @this {RandomWallpaperMenu} RandomWallpaperMenu + * Function for events that should happen on element select. + * * @param {InstanceType} actor The activating panel item + * @this RandomWallpaperMenu */ function onSelect(this: RandomWallpaperMenu, actor: InstanceType): void { // Make sure no other preview or reset event overwrites our setWallpaper! @@ -259,6 +294,9 @@ class RandomWallpaperMenu { this._historySection.updateList(history, onEnter.bind(this), onLeave.bind(this), onSelect.bind(this)); } + /** + * Remove the history section + */ clearHistoryList(): void { this._historySection.clear(); } diff --git a/src/settings.ts b/src/settings.ts index 1e7722c6..b052e9c9 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -17,79 +17,181 @@ const RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN = 'org.gnome.shell.extensions.space. const RWG_SETTINGS_SCHEMA_PATH = '/org/gnome/shell/extensions/space-iflow-randomwallpaper'; +/** + * Wrapper around gnome settings. + */ class Settings { private _settings: Gio.Settings; + /** + * Create a new settings object. + * + * Will default to the general extension settings. + * + * @param {string | undefined} schemaId Schema ID or undefined, defaults to the extension schema ID + * @param {string | undefined} schemaPath Schema path or undefined + */ constructor(schemaId?: string, schemaPath?: string) { if (schemaPath === undefined) { this._settings = ExtensionUtils.getSettings(schemaId); } else { - // We can't give a path so we need to rebuild the function: + // ExtensionUtils.getSettings() doesn't allow specifying a schema path + // We need the schema path to allow for account style settings (having the + // same settings schema id for multiple similar but distinctive settings objects). + // So we have to rebuild the original getSettings() function to get the raw + // schema object and build the Gio.Settings on our own with the schema path. const schemaObj = this._getSchema(schemaId); - // Everything above for… this… this._settings = new Gio.Settings({settings_schema: schemaObj, path: schemaPath}); } } + /** + * Bind a settings key to a GObject property. + * + * A GObject can only bind to one setting at a time. + * See observe() for one-way tracking with multiple watchers. + * + * @param {string} keyName Name of the setting key + * @param {GObject.Object} gObject GObject to bind to + * @param {string} property Name of the GObject property to bind to + * @param {Gio.SettingsBindFlags} settingsBindFlags Flags + */ bind(keyName: string, gObject: GObject.Object, property: string, settingsBindFlags: Gio.SettingsBindFlags): void { this._settings.bind(keyName, gObject, property, settingsBindFlags); } + /** + * Disconnect a watcher initiated by observe(). + * + * @param {number} handler ID of the observer to disconnect + */ disconnect(handler: number): void { - return this._settings.disconnect(handler); + this._settings.disconnect(handler); } + /** + * Get a boolean saved in a key. + * + * @param {string} key Key to query + * @returns {boolean} The saved value + */ getBoolean(key: string): boolean { return this._settings.get_boolean(key); } + /** + * Get an Enum saved in a key. + * + * @param {string} key Key to query + * @returns {number} The saved value + */ getEnum(key: string): number { return this._settings.get_enum(key); } + /** + * Get a number saved in a key. + * + * @param {string} key Key to query + * @returns {number} The saved value + */ getInt(key: string): number { return this._settings.get_int(key); } + /** + * Get a number saved in a key. + * + * @param {string} key Key to query + * @returns {number} The saved value + */ getInt64(key: string): number { return this._settings.get_int64(key); } + /** + * Get a string saved in a key. + * + * @param {string} key Key to query + * @returns {string} The saved value + */ getString(key: string): string { return this._settings.get_string(key); } + /** + * Get a list of strings saved in a key. + * + * @param {string} key Key to query + * @returns {string[]} The saved value + */ getStrv(key: string): string[] { return this._settings.get_strv(key); } + /** + * Get the current settings schema. + * + * @returns {Gio.SettingsSchema} The schema in use + */ getSchema(): Gio.SettingsSchema { return this._settings.settings_schema; } + /** + * Check if the schema key is writable. + * + * @param {string} key Key to query + * @returns {boolean} Whether the key is writable + */ isWritable(key: string): boolean { return this._settings.is_writable(key); } + /** + * List all keys available in the schema. + * + * @returns {string[]} List of keys + */ listKeys(): string[] { return this._settings.list_keys(); } - // eslint-disable-next-line no-unused-vars + /** + * Watch a setting for changes. + * + * @param {string} key Settings key to watch for changes + * @param {(...args: any[]) => any} callback Function to call on value changes + * @returns {number} Handler ID, use for disconnect + */ observe(key: string, callback: (...args: any[]) => any): number { return this._settings.connect(`changed::${key}`, callback); } + /** + * Resets a key to its default value affectively removing this key. + * + * @param {string} keyName Key to reset + */ reset(keyName: string): void { this._settings.reset(keyName); } + /** + * Reset a whole schema to its default value affectively removing this schema. + */ resetSchema(): void { for (const key of this._settings.settings_schema.list_keys()) this.reset(key); } + /** + * Save a boolean to a key. + * + * @param {string} key Key to save in + * @param {boolean} value Value to save + */ setBoolean(key: string, value: boolean): void { if (this._settings.set_boolean(key, value)) this._save(); @@ -97,6 +199,12 @@ class Settings { throw new Error(`Could not set ${key} (type: boolean) with the value ${String(value)}`); } + /** + * Save an Enum to a key. + * + * @param {string} key Key to save in + * @param {number} value Value to save + */ setEnum(key: string, value: number): void { if (this._settings.set_enum(key, value)) this._save(); @@ -104,6 +212,12 @@ class Settings { throw new Error(`Could not set ${key} (type: number) with the value ${value}`); } + /** + * Save a number to a key. + * + * @param {string} key Key to save in + * @param {number} value Value to save + */ setInt(key: string, value: number): void { if (this._settings.set_int(key, value)) this._save(); @@ -111,6 +225,12 @@ class Settings { throw new Error(`Could not set ${key} (type: number) with the value ${value}`); } + /** + * Save a number to a key. + * + * @param {string} key Key to save in + * @param {number} value Value to save + */ setInt64(key: string, value: number): void { if (this._settings.set_int64(key, value)) this._save(); @@ -118,6 +238,12 @@ class Settings { throw new Error(`Could not set ${key} (type: number64) with the value ${value}`); } + /** + * Save a string to a key. + * + * @param {string} key Key to save in + * @param {string} value Value to save + */ setString(key: string, value: string): void { if (this._settings.set_string(key, value)) this._save(); @@ -125,6 +251,12 @@ class Settings { throw new Error(`Could not set ${key} (type: string) with the value ${value}`); } + /** + * Save a list of strings to a key. + * + * @param {string} key Key to save in + * @param {string[]} value Value to save + */ setStrv(key: string, value: string[]): void { if (this._settings.set_strv(key, value)) this._save(); @@ -132,10 +264,19 @@ class Settings { throw new Error(`Could not set ${key} (type: string[]) with the value "${value.toString()}"`); } + /** + * Sync the settings object to disk. + */ private _save(): void { Gio.Settings.sync(); // Necessary: http://stackoverflow.com/questions/9985140 } + /** + * Helper function to get the extension settings schema object. + * + * @param {string | undefined} schemaId Schema ID, defaults to the extension settings schema ID + * @returns {Gio.SettingsSchema} Settings schema object for the given ID + */ private _getSchema(schemaId?: string): Gio.SettingsSchema { // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/gnome-43/js/misc/extensionUtils.js#L211 if (!schemaId) diff --git a/src/soupBowl.ts b/src/soupBowl.ts index 172accc7..2272f853 100644 --- a/src/soupBowl.ts +++ b/src/soupBowl.ts @@ -1,20 +1,26 @@ +import GLib from 'gi://GLib'; +import Soup from 'gi://Soup'; + +import {Logger} from './logger.js'; + /** * A compatibility and convenience wrapper around the Soup API. * * libSoup is accessed through the SoupBowl wrapper to support libSoup3 and libSoup2.4 simultaneously in the extension * runtime and in the preferences window. */ -import GLib from 'gi://GLib'; -import Soup from 'gi://Soup'; - -import {Logger} from './logger.js'; - class SoupBowl { MessageFlags = Soup.MessageFlags; private _logger = new Logger('RWG3', 'BaseAdapter'); private _session = new Soup.Session(); + /** + * Send a request with Soup. + * + * @param {Soup.Message} soupMessage Message to send + * @returns {Promise} Raw byte answer + */ send_and_receive(soupMessage: Soup.Message): Promise { if (Soup.get_major_version() === 2) return this._send_and_receive_soup24(soupMessage); @@ -24,11 +30,23 @@ class SoupBowl { throw new Error('Unknown libsoup version'); } + /** + * Craft a new GET request. + * + * @param {string} uri Request address + * @returns {Soup.Message} Crafted message + */ newGetMessage(uri: string): Soup.Message { return Soup.Message.new('GET', uri); } // Possibly wrong version here causing ignores to type checks + /** + * Send a request using Soup 2.4 + * + * @param {Soup.Message} soupMessage Request message + * @returns {Promise} Raw byte answer + */ private _send_and_receive_soup24(soupMessage: Soup.Message): Promise { return new Promise((resolve, reject) => { // @ts-ignore @@ -45,6 +63,12 @@ class SoupBowl { } // Possibly wrong version here causing ignores to type checks + /** + * Send a request using Soup 3.0 + * + * @param {Soup.Message} soupMessage Request message + * @returns {Promise} Raw byte answer + */ private _send_and_receive_soup30(soupMessage: Soup.Message): Promise { return new Promise((resolve, reject) => { // @ts-ignore diff --git a/src/timer.ts b/src/timer.ts index 0cf743c6..74d3edc0 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -16,6 +16,11 @@ class AFTimer { private _minutes = 30; private _paused = false; + /** + * Get the timer singleton. + * + * @returns {AFTimer} Timer object + */ static getTimer(): AFTimer { if (!this._afTimerInstance) this._afTimerInstance = new AFTimer(); @@ -23,6 +28,9 @@ class AFTimer { return this._afTimerInstance; } + /** + * Remove the timer singleton. + */ static destroy(): void { if (this._afTimerInstance) this._afTimerInstance.cleanup(); @@ -50,10 +58,20 @@ class AFTimer { void this.start(); } + /** + * Check if the timer is currently set as activated. + * + * @returns {boolean} Wether the timer is activated + */ isActive(): boolean { return this._settings.getBoolean('auto-fetch'); } + /** + * Check if the timer is currently paused. + * + * @returns {boolean} Wether the timer is paused + */ isPaused(): boolean { return this._paused; } @@ -71,12 +89,24 @@ class AFTimer { this.cleanup(); } + /** + * Get the minutes until the timer activates. + * + * @returns {number} Minutes to activation + */ remainingMinutes(): number { const minutesElapsed = this._minutesElapsed(); const remainder = minutesElapsed % this._minutes; return Math.max(this._minutes - remainder, 0); } + /** + * Register a function which gets called on timer activation. + * + * Overwrites previously registered function. + * + * @param {() => Promise} callback Function to call + */ registerCallback(callback: () => Promise): void { this._timeoutEndCallback = callback; } @@ -166,13 +196,17 @@ class AFTimer { } /** - * Sets the last activation time to [now]. This doesn't affect already running timer - * and will be ignored if the timer is paused. + * Sets the last activation time to [now]. This doesn't affect an already running timer. */ private _reset(): void { this._settings.setInt64('timer-last-trigger', new Date().getTime()); } + /** + * Get the elapsed minutes since the last timer activation. + * + * @returns {number} Elapsed time in minutes + */ private _minutesElapsed(): number { const now = Date.now(); const last: number = this._settings.getInt64('timer-last-trigger'); @@ -184,6 +218,11 @@ class AFTimer { return Math.floor(elapsed / (60 * 1000)); } + /** + * Checks if the configured timer interval has surpassed since the last timer activation. + * + * @returns {boolean} Whether the interval was surpassed + */ private _surpassedInterval(): boolean { const now = Date.now(); const last = this._settings.getInt64('timer-last-trigger'); diff --git a/src/ui/genericJson.ts b/src/ui/genericJson.ts index 41264f46..cd88ea51 100644 --- a/src/ui/genericJson.ts +++ b/src/ui/genericJson.ts @@ -38,6 +38,14 @@ const GenericJsonSettingsGroup = GObject.registerClass({ private _settings; + /** + * Craft a new adapter using an unique ID. + * + * Previously saved settings will be used if the adapter and ID match. + * + * @param {Partial | undefined} params Properties for Adw.PreferencesGroup or undefined + * @param {string} id Unique ID + */ constructor(params: Partial | undefined, id: string) { super(params); @@ -82,6 +90,9 @@ const GenericJsonSettingsGroup = GObject.registerClass({ Gio.SettingsBindFlags.DEFAULT); } + /** + * Clear all config options associated to this specific adapter. + */ clearConfig(): void { this._settings.resetSchema(); } diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index 0c82795f..333943a3 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -26,6 +26,14 @@ const LocalFolderSettingsGroup = GObject.registerClass({ private _saveDialog: Gtk.FileChooserNative | undefined; private _settings; + /** + * Craft a new adapter using an unique ID. + * + * Previously saved settings will be used if the adapter and ID match. + * + * @param {Partial | undefined} params Properties for Adw.PreferencesGroup or undefined + * @param {string} id Unique ID + */ constructor(params: Partial | undefined, id: string) { super(params); @@ -65,6 +73,9 @@ const LocalFolderSettingsGroup = GObject.registerClass({ }); } + /** + * Clear all config options associated to this specific adapter. + */ clearConfig(): void { this._settings.resetSchema(); } diff --git a/src/ui/reddit.ts b/src/ui/reddit.ts index 7d59eb60..6db01c49 100644 --- a/src/ui/reddit.ts +++ b/src/ui/reddit.ts @@ -33,6 +33,14 @@ const RedditSettingsGroup = GObject.registerClass({ private _settings; + /** + * Craft a new adapter using an unique ID. + * + * Previously saved settings will be used if the adapter and ID match. + * + * @param {Partial | undefined} params Properties for Adw.PreferencesGroup or undefined + * @param {string} id Unique ID + */ constructor(params: Partial | undefined, id: string) { super(params); @@ -65,6 +73,9 @@ const RedditSettingsGroup = GObject.registerClass({ Gio.SettingsBindFlags.DEFAULT); } + /** + * Clear all config options associated to this specific adapter. + */ clearConfig(): void { this._settings.resetSchema(); } diff --git a/src/ui/sourceRow.ts b/src/ui/sourceRow.ts index f67279de..b342cd70 100644 --- a/src/ui/sourceRow.ts +++ b/src/ui/sourceRow.ts @@ -52,6 +52,15 @@ const SourceRow = GObject.registerClass({ id = String(Date.now()); + /** + * Craft a new source row using an unique ID. + * + * Default unique ID is Date.now() + * Previously saved settings will be used if the ID matches. + * + * @param {Partial | undefined} params Properties for Adw.ExpanderRow or undefined + * @param {string | null} id Unique ID or null + */ constructor(params: Partial | undefined, id?: string | null) { super(params); @@ -116,13 +125,24 @@ const SourceRow = GObject.registerClass({ }); } + /** + * Fill this source row with adapter settings. + * + * @param {number} type Enum of the adapter to use + */ private _fillRow(type: number): void { const targetWidget = this._getSettingsGroup(type); if (targetWidget !== null) this._settings_container.set_child(targetWidget); } - private _getSettingsGroup(type = 0): GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> + /** + * Get a new adapter based on an enum source type. + * + * @param {Utils.SourceType} type Enum of the adapter to get + * @returns {object | null} Newly crafted adapter or null + */ + private _getSettingsGroup(type: Utils.SourceType = Utils.SourceType.UNSPLASH): GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> | GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> | GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> | GObject.RegisteredPrototype, { [key: string]: GObject.ParamSpec; }, unknown[]> @@ -157,6 +177,11 @@ const SourceRow = GObject.registerClass({ return targetWidget; } + /** + * Remove an image name from the blocked image list. + * + * @param {string} filename Image name to remove + */ private _removeBlockedImage(filename: string): void { let blockedImages = this._settings.getStrv('blocked-images'); if (!blockedImages.includes(filename)) diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index 22a07743..ed35479c 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -36,6 +36,14 @@ const UnsplashSettingsGroup = GObject.registerClass({ private _settings; + /** + * Craft a new adapter using an unique ID. + * + * Previously saved settings will be used if the adapter and ID match. + * + * @param {Partial | undefined} params Properties for Adw.PreferencesGroup or undefined + * @param {string} id Unique ID + */ constructor(params: Partial | undefined, id: string) { super(params); @@ -93,6 +101,13 @@ const UnsplashSettingsGroup = GObject.registerClass({ }); } + /** + * Switch element sensitivity based on a selected combo row entry. + * + * @param {Adw.ComboRow} comboRow ComboRow with selected entry + * @param {boolean} enable Wether to make the element sensitive + * @param {Gtk.Widget} targetElement The element to target the sensitivity setting + */ private _unsplashUnconstrained(comboRow: Adw.ComboRow, enable: boolean, targetElement: Gtk.Widget): void { if (comboRow.selected === 0) targetElement.set_sensitive(enable); @@ -100,6 +115,9 @@ const UnsplashSettingsGroup = GObject.registerClass({ targetElement.set_sensitive(!enable); } + /** + * Clear all config options associated to this specific adapter. + */ clearConfig(): void { this._settings.resetSchema(); } diff --git a/src/ui/urlSource.ts b/src/ui/urlSource.ts index fca37cb4..3c2b9468 100644 --- a/src/ui/urlSource.ts +++ b/src/ui/urlSource.ts @@ -33,6 +33,14 @@ const UrlSourceSettingsGroup = GObject.registerClass({ private _settings; + /** + * Craft a new adapter using an unique ID. + * + * Previously saved settings will be used if the adapter and ID match. + * + * @param {Partial | undefined} params Properties for Adw.PreferencesGroup or undefined + * @param {string} id Unique ID + */ constructor(params: Partial | undefined, id: string) { super(params); @@ -65,6 +73,9 @@ const UrlSourceSettingsGroup = GObject.registerClass({ Gio.SettingsBindFlags.DEFAULT); } + /** + * Clear all config options associated to this specific adapter. + */ clearConfig(): void { this._settings.resetSchema(); } diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index c69e71b3..3e33a592 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -59,6 +59,14 @@ const WallhavenSettingsGroup = GObject.registerClass({ private _colorDialog: Gtk.ColorChooserDialog | undefined; private _settings; + /** + * Craft a new adapter using an unique ID. + * + * Previously saved settings will be used if the adapter and ID match. + * + * @param {Partial | undefined} params Properties for Adw.PreferencesGroup or undefined + * @param {string} id Unique ID + */ constructor(params: Partial | undefined, id: string) { super(params); @@ -154,6 +162,9 @@ const WallhavenSettingsGroup = GObject.registerClass({ }); } + /** + * Clear all config options associated to this specific adapter. + */ clearConfig(): void { this._settings.resetSchema(); } diff --git a/src/utils.ts b/src/utils.ts index f557736e..2c620b26 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -20,8 +20,10 @@ enum SourceType { /* eslint-enable */ /** + * Get the string representation of an enum SourceType. * * @param {SourceType} value The enum value to request + * @returns {string} Name of the corresponding source type */ function getSourceTypeName(value: SourceType): string { switch (value) { @@ -95,6 +97,7 @@ function execCheck(argv: string[], cancellable?: Gio.Cancellable | null): Promis * Retrieves the file name part of an URI * * @param {string} uri URI to scan + * @returns {string} Filename part */ function fileName(uri: string): string { while (_isURIEncoded(uri)) @@ -108,6 +111,7 @@ function fileName(uri: string): string { } /** + * Takes a GSettings schema and key to an enum and fills a combo row with it. * * @param {Adw.ComboRow} comboRow ComboRow to fill and connect * @param {Settings} settings Settings schema to scan values for @@ -133,9 +137,13 @@ function fillComboRowFromEnum(comboRow: Adw.ComboRow, settings: Settings, key: s // https://stackoverflow.com/a/32859917 /** + * Compare two strings and return the first char they differentiate. + * + * Begins counting on 0 and returns -1 if the strings are identical. * * @param {string} str1 String to compare * @param {string} str2 String to compare + * @returns {number} First different char or -1 */ function findFirstDifference(str1: string, str2: string): number { let i = 0; @@ -147,7 +155,9 @@ function findFirstDifference(str1: string, str2: string): number { } /** + * Get the amount of currently connected displays. * + * @returns {number} Connected display count */ function getMonitorCount(): number { // Gdk 4.8+ @@ -173,8 +183,10 @@ function getMonitorCount(): number { } /** + * Get a random number between 0 and a given value. * * @param {number} size Maximum + * @returns {number} Random number between 0 and $size */ function getRandomNumber(size: number): number { // https://stackoverflow.com/a/5915122 @@ -182,8 +194,10 @@ function getRandomNumber(size: number): number { } /** + * Check if a string is an URI. * * @param {string} uri The URI to check + * @returns {boolean} Whether the given string is an URI */ function _isURIEncoded(uri: string): boolean { uri = uri || ''; @@ -193,9 +207,11 @@ function _isURIEncoded(uri: string): boolean { // https://stackoverflow.com/a/5767357 /** + * Remove the first matching item of an array. * * @param {Array} array Array of items * @param {T} value Item to remove + * @returns {Array} Array with first encountered item removed */ function removeItemOnce(array: T[], value: T): T[] { const index = array.indexOf(value); diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 241d77c0..267012da 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -33,6 +33,9 @@ interface RandomAdapterResult { imageCount: number } +/** + * The main wallpaper handler. + */ class WallpaperController { wallpaperLocation: string; prohibitNewWallpaper = false; @@ -52,6 +55,12 @@ class WallpaperController { /** functions will be called when loading a new wallpaper stopped. */ private _stopLoadingHooks: (() => void)[] = []; + /** + * Create a new controller. + * + * Should only exists once to avoid weird shenanigans because the extension background + * and preferences page existing in two different contexts. + */ constructor() { let xdg_cache_home = GLib.getenv('XDG_CACHE_HOME'); if (!xdg_cache_home) { @@ -127,6 +136,9 @@ class WallpaperController { } } + /** + * Empty the history. (Background settings observer edition) + */ private _clearHistory(): void { if (this._backendConnection.getBoolean('clear-history')) { this.update(); @@ -135,6 +147,9 @@ class WallpaperController { } } + /** + * Open the internal wallpaper cache folder. (Background settings observer edition) + */ private _openFolder(): void { if (this._backendConnection.getBoolean('open-folder')) { const uri = GLib.filename_to_uri(this.wallpaperLocation, ''); @@ -143,6 +158,9 @@ class WallpaperController { } } + /** + * Pause or resume the timer. (Background settings observer edition) + */ private _pauseTimer(): void { if (this._backendConnection.getBoolean('pause-timer')) { this._timer.pause(); @@ -163,6 +181,9 @@ class WallpaperController { } } + /** + * Get a fresh wallpaper. (Background settings observer edition) + */ private async _requestNewWallpaper(): Promise { if (this._backendConnection.getBoolean('request-new-wallpaper')) { this.update(); @@ -175,10 +196,18 @@ class WallpaperController { } } + /** + * Update the history. + * + * Loads from settings. + */ private _updateHistory(): void { this._historyController.load(); } + /** + * Update settings related to the auto fetching. + */ private _updateAutoFetching(): void { let duration = 0; duration += this._settings.getInt('minutes'); @@ -186,7 +215,6 @@ class WallpaperController { this._autoFetch.duration = duration; this._autoFetch.active = this._settings.getBoolean('auto-fetch'); - // only start timer if not in context of preferences window if (this._autoFetch.active) { this._timer.registerCallback(() => { return this.fetchNewWallpaper(); @@ -201,10 +229,14 @@ class WallpaperController { } /** - randomly returns an enabled and configured SourceAdapter - returns a default UnsplashAdapter in case of failure + * Get an array of random adapter needed to fill the display $count. * - * @param {number} count The amount of adapter requested + * A single adapter can be assigned for multiple images so you may get less than $count adapter back. + * + * Returns a default UnsplashAdapter in case of failure. + * + * @param {number} count The amount of wallpaper requested + * @returns {RandomAdapterResult[]} Array of info objects how many images are needed for each adapter */ private _getRandomAdapter(count: number): RandomAdapterResult[] { const sourceIDs = this._getRandomSource(count); @@ -221,9 +253,11 @@ class WallpaperController { } /** + * Check if we've chosen the same adapter type before. * * @param {RandomAdapterResult[]} array Array of already chosen adapter * @param {number} type Type of the source + * @returns {RandomAdapterResult | null} Found adapter or null */ function _arrayIncludes(array: RandomAdapterResult[], type: number): RandomAdapterResult | null { for (const element of array) { @@ -294,9 +328,12 @@ class WallpaperController { } /** + * Gets randomly $count amount of enabled sources. + * + * The same source can appear multiple times in the resulting array. * * @param {number} count Amount of requested source IDs - * @returns Array of source IDs or ['-1'] in case of failure + * @returns {string[]} Array of source IDs or ['-1'] in case of failure */ private _getRandomSource(count: number): string[] { const sourceResult: string[] = []; @@ -384,7 +421,9 @@ class WallpaperController { } } - // Run general post command + /** + * Run a configured post command. + */ private _runPostCommands(): void { const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); const commandString = this._settings.getString('general-post-command'); @@ -403,6 +442,13 @@ class WallpaperController { } } + /** + * Fill an array with images from the history until $count. + * + * @param {string[]} wallpaperArray Array of wallpaper paths + * @param {number | undefined} requestCount Amount of wallpaper paths $wallpaperArray should contain, defaults to the value reported by _getCurrentDisplayCount() + * @returns {string[]} Array of wallpaper paths matching the length of $count + */ private _fillDisplaysFromHistory(wallpaperArray: string[], requestCount?: number): string[] { const count = requestCount ?? this._getCurrentDisplayCount(); const newWallpaperArray: string[] = [...wallpaperArray]; @@ -423,6 +469,11 @@ class WallpaperController { return newWallpaperArray.slice(0, count); } + /** + * Set an existing history entry as wallpaper. + * + * @param {string} historyId Unique ID + */ async setWallpaper(historyId: string): Promise { const historyElement = this._historyController.get(historyId); @@ -449,6 +500,9 @@ class WallpaperController { // TODO: Error handling history id not found. } + /** + * Fetch fresh wallpaper. + */ async fetchNewWallpaper(): Promise { this._startLoadingHooks.forEach(element => element()); @@ -552,6 +606,16 @@ class WallpaperController { } // TODO: Change to original historyElement if more variable get exposed + /** + * Get a command array from a string. + * + * Fills variables if found: + * - %wallpaper_path% + * + * @param {string} commandString String to parse an array from + * @param {string} historyElementPath Wallpaper path to fill into the variable + * @returns {string[] | null} Command array or null + */ private _getCommandArray(commandString: string, historyElementPath: string): string[] | null { let string = commandString; if (string === '') @@ -582,8 +646,10 @@ class WallpaperController { /** * Get the current number of displays. * - * This also takes the user setting and HydraPaper availability into account + * This also takes the user setting and wallpaper manager availability into account * and lies accordingly by reporting only 1 display. + * + * @returns {number} Amount of currently activated displays or 1 */ private _getCurrentDisplayCount(): number { if (!this._settings.getBoolean('multiple-displays')) @@ -595,6 +661,15 @@ class WallpaperController { return Utils.getMonitorCount(); } + /** + * Set a background after a $delay + * + * Prohibits quick wallpaper changing by blocking additional change requests + * within a timeout. + * + * @param {string[] | undefined} paths Array of wallpaper paths + * @param {number | undefined} delay Delay, defaults to 200ms + */ private _backgroundTimeout(paths?: string[], delay?: number): void { if (this._timeout || !paths) return; @@ -621,52 +696,92 @@ class WallpaperController { }); } + /** + * Preview an image in the history. + * + * @param {string} historyId Unique ID + * @param {number} delay Delay, defaults to 200ms + */ previewWallpaper(historyId: string, delay?: number): void { if (!this._settings.getBoolean('disable-hover-preview')) { this._previewId = historyId; this._resetWallpaper = false; // Do not fill other displays here. - // This is so HydraPaper will not overwrite the current merged background path - // with the preview image. - // We could move the image to a safe place with caveats: - // * temporarily (seems expensive for a simple preview) - // TODO: verify: * permanently (would break HydraPaperDaemon) + // Merging images can take a long time and hurt the quick preview purpose. + // Therefor only an array with a single wallpaper path here: const newWallpaperPaths = [this.wallpaperLocation + this._previewId]; this._backgroundTimeout(newWallpaperPaths, delay); } } + /** + * Set the wallpaper to an URI. + * + * @param {string} uri Wallpaper URI + */ resetWallpaper(uri: string): void { if (!this._settings.getBoolean('disable-hover-preview')) { this._resetWallpaper = true; + // FIXME: With an already running timeout this reset request will be ignored this._backgroundTimeout([GLib.filename_from_uri(uri)[0]]); } } + /** + * Get the HistoryController. + * + * @returns {HistoryModule.HistoryController} The history controller + */ getHistoryController(): HistoryModule.HistoryController { return this._historyController; } + /** + * Empty the history. + */ deleteHistory(): void { this._historyController.clear(); } + /** + * Update the history. + */ update(): void { this._updateHistory(); } + /** + * Register a function which gets called on wallpaper fetching. + * + * Can take multiple hooks. + * + * @param {() => void} fn Function to call + */ registerStartLoadingHook(fn: () => void): void { if (typeof fn === 'function') this._startLoadingHooks.push(fn); } + /** + * Register a function which gets called when done wallpaper fetching. + * + * Can take multiple hooks. + * + * @param {() => void} fn Function to call + */ registerStopLoadingHook(fn: () => void): void { if (typeof fn === 'function') this._stopLoadingHooks.push(fn); } + /** + * asd + * + * @param {string} msg asd + * @param {string} callback ads + */ private _bailOutWithCallback(msg: string, callback?: () => void): void { this._logger.error(msg); From 6c3b2897009339cec2cc7c299bd073d32ba1d351 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 4 Jun 2023 22:59:00 +0200 Subject: [PATCH 60/87] Remove unnecessary HistoryEntry parameter --- src/historyMenuElements.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 0f86b72c..848ef49f 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -159,7 +159,7 @@ const HistoryElement = GObject.registerClass({ if (this.historyEntry.adapter?.type !== Utils.SourceType.STATIC_URL) { const blockImage = new PopupMenu.PopupMenuItem('Add To Blocklist'); blockImage.connect('activate', () => { - this._addToBlocklist(this.historyEntry); + this._addToBlocklist(); }); this.menu.addMenuItem(blockImage); } @@ -207,24 +207,21 @@ const HistoryElement = GObject.registerClass({ * Add an image to the blocking list. * * Uses the filename for distinction. - * - * @param {HistoryModule.HistoryEntry} entry Entry to block */ - // FIXME: entry is unnecessary - private _addToBlocklist(entry: HistoryModule.HistoryEntry): void { - if (!entry.adapter?.id || entry.adapter.id === '-1' || !entry.name) { + private _addToBlocklist(): void { + if (!this.historyEntry.adapter?.id || this.historyEntry.adapter.id === '-1' || !this.historyEntry.name) { this._logger.error('Image entry is missing information'); return; } - const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${entry.adapter.id}/`; + const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/general/${this.historyEntry.adapter.id}/`; const generalSettings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_GENERAL, path); const blockedFilenames = generalSettings.getStrv('blocked-images'); - if (blockedFilenames.includes(entry.name)) + if (blockedFilenames.includes(this.historyEntry.name)) return; - blockedFilenames.push(entry.name); + blockedFilenames.push(this.historyEntry.name); generalSettings.setStrv('blocked-images', blockedFilenames); } From cae71cc3dfd132dccc9a1d355f7ffc61df53a20b Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 4 Jun 2023 23:33:09 +0200 Subject: [PATCH 61/87] Move identical statements to finally block --- src/randomWallpaperMenu.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 7bcf59c1..552fe722 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -114,10 +114,10 @@ class RandomWallpaperMenu { // Make sure no other preview or reset event overwrites our setWallpaper! this._wallpaperController.prohibitNewWallpaper = true; this._wallpaperController.fetchNewWallpaper().then(() => { - this._wallpaperController.prohibitNewWallpaper = false; }).catch(error => { - this._wallpaperController.prohibitNewWallpaper = false; this._logger.error(error); + }).finally(() => { + this._wallpaperController.prohibitNewWallpaper = false; }); }); From a05bf602b05871e1686a2db211836eeaa54c9f07 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 4 Jun 2023 23:39:32 +0200 Subject: [PATCH 62/87] Clean up unused remnants --- src/adapter/baseAdapter.ts | 15 --------------- src/wallpaperController.ts | 13 ------------- 2 files changed, 28 deletions(-) diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index fb097807..426e418c 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -119,21 +119,6 @@ abstract class BaseAdapter { return false; } - - // eslint-disable-next-line no-unused-vars - /** - * asd - * - * @param {string} err asd - * @param {string} callback asd - */ - protected _error(err: string, callback?: (element: null, error: { error: string }) => void): void { - const error = {error: err}; - this._logger.error(JSON.stringify(error)); - - if (callback) - callback(null, error); - } } export {BaseAdapter}; diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 267012da..ce5ebb33 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -775,19 +775,6 @@ class WallpaperController { if (typeof fn === 'function') this._stopLoadingHooks.push(fn); } - - /** - * asd - * - * @param {string} msg asd - * @param {string} callback ads - */ - private _bailOutWithCallback(msg: string, callback?: () => void): void { - this._logger.error(msg); - - if (callback) - callback(); - } } export {WallpaperController}; From b174bb5c2fb6649f73e9a3a1f23baea058897c2f Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 10 Jun 2023 00:31:30 +0200 Subject: [PATCH 63/87] Improve local folder logic * do not include blocked images in the first place * only shuffle items once at the beginning --- src/adapter/localFolder.ts | 18 +++++++++--------- src/utils.ts | 19 ++++++++++++++++++- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 4d5050c3..40c2de9b 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -50,19 +50,16 @@ class LocalFolderAdapter extends BaseAdapter { } this._logger.debug(`Found ${files.length} possible wallpaper in "${this._settings.getString('folder')}"`); - for (let i = 0; i < 20 && wallpaperResult.length < count; i++) { - const randomFile = files[Utils.getRandomNumber(files.length)]; - const randomFilePath = randomFile.get_uri(); - const randomFileName = randomFile.get_basename(); + const shuffledFiles = Utils.shuffleArray(files); - if (!randomFileName || this._isImageBlocked(randomFileName)) - continue; + for (let i = 0; i < count && i < shuffledFiles.length; i++) { + const randomFile = shuffledFiles[i]; + const randomFilePath = randomFile.get_uri(); const historyEntry = new HistoryEntry(null, this._sourceName, randomFilePath); historyEntry.source.sourceUrl = randomFilePath; - if (!this._includesWallpaper(wallpaperResult, historyEntry.source.imageDownloadUrl)) - wallpaperResult.push(historyEntry); + wallpaperResult.push(historyEntry); } if (wallpaperResult.length < count) { @@ -99,6 +96,8 @@ class LocalFolderAdapter extends BaseAdapter { /** * Walk recursively through a folder and retrieve a list of all images. * + * This already checks for blocked filenames and omits them from the returned list. + * * @param {Gio.File} directory Directory to scan * @returns {Gio.File[]} List of images */ @@ -125,7 +124,8 @@ class LocalFolderAdapter extends BaseAdapter { } const contentType = info.get_content_type(); - if (contentType?.startsWith('image/')) + const filename = child.get_basename(); + if (contentType?.startsWith('image/') && filename && !this._isImageBlocked(filename)) files.push(child); } diff --git a/src/utils.ts b/src/utils.ts index 2c620b26..97e76c38 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -193,6 +193,22 @@ function getRandomNumber(size: number): number { return Math.floor(Math.random() * size); } +// https://stackoverflow.com/a/12646864 +/** + * Shuffle all entries in an array into random order. + * + * @param {T[]} array Array to shuffle + * @returns {T[]} Shuffled array + */ +function shuffleArray(array: T[]): T[] { + for (let i = array.length - 1; i > 0; i--) { + const j = getRandomNumber(i + 1); + [array[i], array[j]] = [array[j], array[i]]; + } + + return array; +} + /** * Check if a string is an URI. * @@ -264,5 +280,6 @@ export { getMonitorCount, getRandomNumber, removeItemOnce, - setPictureUriOfSettingsObject + setPictureUriOfSettingsObject, + shuffleArray }; From 5260787b61fdf76e3053db4eddfb913d32c176fd Mon Sep 17 00:00:00 2001 From: Lucki Date: Mon, 12 Jun 2023 23:17:47 +0200 Subject: [PATCH 64/87] More line padding for generated files `padding-line-between-statements` and `lines-around-comment` conflict in adding two lines where both apply. https://github.com/eslint/eslint/issues/15125 --- .eslintrc-gjs.yml | 71 +++++++++++++++++++++++++++++--------- src/historyMenuElements.ts | 1 + src/jsonPath.ts | 2 +- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/.eslintrc-gjs.yml b/.eslintrc-gjs.yml index 5ca674bc..0a6b2ac7 100644 --- a/.eslintrc-gjs.yml +++ b/.eslintrc-gjs.yml @@ -98,10 +98,12 @@ rules: linebreak-style: - error - unix - lines-between-class-members: - - error - - always - - exceptAfterSingleLine: true + # Each class member has a forced doc which is a block comment. + # Block comment spacing is forced too, disabling this rule. + # lines-between-class-members: + # - error + # - always + # - exceptAfterSingleLine: true max-nested-callbacks: error max-statements-per-line: error new-parens: error @@ -259,30 +261,39 @@ rules: yoda: error padding-line-between-statements: - error - - blankLine: always - prev: function - next: function - blankLine: always prev: class next: function - blankLine: always prev: function next: class - - blankLine: always - prev: class - next: class - blankLine: always prev: import next: function + - blankLine: always + prev: import + next: var + - blankLine: always + prev: import + next: let + - blankLine: always + prev: import + next: const - blankLine: always prev: const - next: function + next: import + - blankLine: always + prev: const + next: let - blankLine: always prev: let + next: const + - blankLine: always + prev: const next: function - blankLine: always - prev: import - next: class + prev: let + next: function - blankLine: always prev: class next: export @@ -294,16 +305,42 @@ rules: next: return - blankLine: always prev: '*' - next: block-like + next: if - blankLine: always - prev: block-like + prev: if next: '*' - blankLine: always prev: '*' - next: if + next: try - blankLine: always - prev: if + prev: try next: '*' + - blankLine: always + prev: '*' + next: switch + - blankLine: always + prev: switch + next: '*' + - blankLine: always + prev: '*' + next: while + - blankLine: always + prev: while + next: '*' + - blankLine: always + prev: '*' + next: do + - blankLine: always + prev: '*' + next: for + - blankLine: always + prev: for + next: '*' + lines-around-comment: + - error + - allowClassStart: true + allowBlockStart: true + beforeLineComment: true settings: jsdoc: mode: typescript diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 848ef49f..9366d3f2 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -22,6 +22,7 @@ import {Logger} from './logger.js'; Gio._promisify(Gio.File.prototype, 'copy_async', 'copy_finish'); Gio._promisify(Gio.File.prototype, 'replace_contents_bytes_async', 'replace_contents_finish'); +// GJS style class extending const HistoryElement = GObject.registerClass({ GTypeName: 'HistoryElement', }, class HistoryElement extends PopupMenu.PopupSubMenuMenuItem { diff --git a/src/jsonPath.ts b/src/jsonPath.ts index d1192428..f6e61021 100644 --- a/src/jsonPath.ts +++ b/src/jsonPath.ts @@ -43,13 +43,13 @@ function getTarget(inputObject: unknown, inputString: string): [object: unknown, if (!targetObject || !Array.isArray(targetObject)) return [null, '']; + // add special keywords here switch (indexString) { case '@random': { const [chosenElement, chosenNumber] = _randomElement(targetObject); const [object, path] = getTarget(chosenElement, inputStringTail); return [object, inputString.slice(0, inputString.length - inputStringTail.length).replace('@random', String(chosenNumber)) + path]; } - // add special keywords here default: { // expecting integer const [object, path] = getTarget(targetObject[parseInt(indexString)], inputStringTail); From ae166e3306badbe50f795fcc675b259b8553a5dd Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 25 Jun 2023 15:33:32 +0200 Subject: [PATCH 65/87] =?UTF-8?q?Try=20workaround=20manually=20deleting=20?= =?UTF-8?q?files=20=E2=80=A6causing=20invalid=20states.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/history.ts | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/history.ts b/src/history.ts index 18a58435..14075dbd 100644 --- a/src/history.ts +++ b/src/history.ts @@ -1,7 +1,9 @@ import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; import * as Utils from './utils.js'; +import {Logger} from './logger.js'; import {Settings} from './settings.js'; // Gets filled by the HistoryController which is constructed at extension startup @@ -72,6 +74,7 @@ class HistoryController { history: HistoryEntry[] = []; size = 10; + private _logger = new Logger('RWG3', 'HistoryController'); private _settings = new Settings(); /** @@ -224,7 +227,7 @@ class HistoryController { // ignore hidden files and first element if (id[0] !== '.' && id !== firstHistoryElement.id) { const deleteFile = Gio.file_new_for_path(_wallpaperLocation + id); - deleteFile.delete(null); + this._deleteFile(deleteFile); } } while (fileInfo); @@ -239,10 +242,37 @@ class HistoryController { this.size = this._settings.getInt('history-length'); while (this.history.length > this.size) { const path = this.history.pop()?.path; - if (path) { - const deleteFile = Gio.file_new_for_path(path); - deleteFile.delete(null); + if (!path) + continue; + + const file = Gio.file_new_for_path(path); + this._deleteFile(file); + } + } + + /** + * Helper function to delete files. + * + * Has some special treatments factored in to ignore file not found issues + * when the parent path is available. + * + * @param {Gio.FilePrototype} file The file to delete + * @throws On any other error than Gio.IOErrorEnum.NOT_FOUND + */ + private _deleteFile(file: Gio.FilePrototype): void { + try { + file.delete(null); + } catch (error) { + /** + * Ignore deletion errors when the file doesn't exist but the parent path is accessible. + * This tries to avoid invalid states later on because we would have thrown here and therefore skip saving. + */ + if (file.get_parent()?.query_exists(null) && error instanceof GLib.Error && error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND)) { + this._logger.warn(`Ignoring Gio.IOErrorEnum.NOT_FOUND: ${file.get_path() ?? 'undefined'}`); + return; } + + throw error; } } From 4b8807dd4b5e1ea84ac44c09c143516878af28f2 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 25 Jun 2023 15:40:46 +0200 Subject: [PATCH 66/87] More specific function description --- src/history.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/history.ts b/src/history.ts index 14075dbd..512fb005 100644 --- a/src/history.ts +++ b/src/history.ts @@ -204,9 +204,9 @@ class HistoryController { /** * Clear the history and delete all photos except the current one. * - * @returns {boolean} Whether the deletion was successful + * This function clears the cache folder, ignoring if the image appears in the history or not. */ - clear(): boolean { + clear(): void { const firstHistoryElement = this.history[0]; if (firstHistoryElement) @@ -232,7 +232,6 @@ class HistoryController { } while (fileInfo); this.save(); - return true; } /** From 1361bb8c76f53a22e6b9aeefff5e8920228096a2 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 25 Jun 2023 16:57:09 +0200 Subject: [PATCH 67/87] Simplify calling timeoutEndCallback --- src/timer.ts | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/timer.ts b/src/timer.ts index 74d3edc0..d34239d5 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -128,8 +128,10 @@ class AFTimer { * If the trigger time was surpassed the callback gets started * directly and the next trigger is scheduled at the * next correct time frame repeatedly. + * + * @param {boolean | undefined} forceTrigger Force calling the timeoutEndCallback on initial call */ - async start(): Promise { + async start(forceTrigger: boolean = false): Promise { if (this._paused) return; @@ -141,10 +143,11 @@ class AFTimer { const millisecondsRemaining = this.remainingMinutes() * 60 * 1000; - // set new wallpaper if the interval was surpassed and set the timestamp to when it should have been updated - if (this._surpassedInterval()) { + // set new wallpaper if the interval was surpassed… + const intervalSurpassed = this._surpassedInterval(); + if (forceTrigger || intervalSurpassed) { if (this._timeoutEndCallback) { - this._logger.debug('Timer surpassed, running callback now'); + this._logger.debug('Running callback now'); try { await this._timeoutEndCallback(); @@ -152,7 +155,10 @@ class AFTimer { this._logger.error(error); } } + } + // …and set the timestamp to when it should have been updated + if (intervalSurpassed) { const millisecondsOverdue = (this._minutes * 60 * 1000) - millisecondsRemaining; this._settings.setInt64('timer-last-trigger', Date.now() - millisecondsOverdue); } @@ -160,18 +166,14 @@ class AFTimer { // actual timer function this._logger.debug(`Starting timer, will run callback in ${millisecondsRemaining}ms`); this._timeout = GLib.timeout_add(GLib.PRIORITY_DEFAULT, millisecondsRemaining, () => { - if (this._timeoutEndCallback) { - this._timeoutEndCallback().then(() => { - this._reset(); - this.start().catch(error => { - this._logger.error(error); - }); - }).catch(error => { - this._logger.error(error); - }).finally(() => { - return GLib.SOURCE_REMOVE; - }); - } + // Reset time immediately to avoid shifting the timer + this._reset(); + + // Call this function again and forcefully skip the surpassed timer check so it will run the timeoutEndCallback + this.start(true).catch(error => { + this._logger.error(error); + }); + return GLib.SOURCE_REMOVE; }); } From eda9f8f33074ce0ad21a8dbc3fb4f1f2f08130cb Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 25 Jun 2023 17:04:35 +0200 Subject: [PATCH 68/87] Try unblocking the UI on startup --- src/wallpaperController.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index ce5ebb33..fe024e09 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -98,14 +98,22 @@ class WallpaperController { this._settings.observe('hours', () => this._updateAutoFetching()); this._updateHistory(); - this._updateAutoFetching(); - // load a new wallpaper on startup - if (this._settings.getBoolean('fetch-on-startup')) { - this.fetchNewWallpaper().catch(error => { - this._logger.error(error); - }); - } + // Fetching and merging wallpaper can be quite heavy on load so try doing this only when idle + GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + // This may start the timer which might load a new wallpaper on interval surpassed + this._updateAutoFetching(); + + // FIXME: try skipping this if the timer is enabled and already fetched because of a surpassed interval when calling _updateAutoFetching above + // load a new wallpaper on startup + if (this._settings.getBoolean('fetch-on-startup')) { + this.fetchNewWallpaper().catch(error => { + this._logger.error(error); + }); + } + + return GLib.SOURCE_REMOVE; + }); // Initialize favorites folder // TODO: There's probably a better place for this From fcab543b5c8651bff75fa325517a170849e4bfa5 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 25 Jun 2023 17:24:44 +0200 Subject: [PATCH 69/87] Clarify enabled timer --- src/historyMenuElements.ts | 2 +- src/timer.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 9366d3f2..5bedf968 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -338,7 +338,7 @@ class NewWallpaperElement extends PopupMenu.PopupBaseMenuItem { * Checks the AF-setting and shows/hides the remaining minutes section. */ show(): void { - if (this._timer.isActive()) { + if (this._timer.isEnabled()) { const remainingMinutes = this._timer.remainingMinutes(); const minutes = remainingMinutes % 60; const hours = Math.floor(remainingMinutes / 60); diff --git a/src/timer.ts b/src/timer.ts index d34239d5..a600c418 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -47,7 +47,7 @@ class AFTimer { * next correct time frame repeatedly. */ continue(): void { - if (!this.isActive()) + if (!this.isEnabled()) return; this._logger.debug('Continuing timer'); @@ -59,11 +59,11 @@ class AFTimer { } /** - * Check if the timer is currently set as activated. + * Check if the timer is currently set as enabled. * - * @returns {boolean} Wether the timer is activated + * @returns {boolean} Wether the timer is enabled */ - isActive(): boolean { + isEnabled(): boolean { return this._settings.getBoolean('auto-fetch'); } @@ -211,7 +211,7 @@ class AFTimer { */ private _minutesElapsed(): number { const now = Date.now(); - const last: number = this._settings.getInt64('timer-last-trigger'); + const last = this._settings.getInt64('timer-last-trigger'); if (last === 0) return 0; From e40232934f24c868131524177890ced18691bc0c Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 28 Jun 2023 11:30:24 +0200 Subject: [PATCH 70/87] Prevent double fetching on startup when the dedicated option and the timer are enabled --- src/timer.ts | 4 ++-- src/wallpaperController.ts | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/timer.ts b/src/timer.ts index a600c418..756543ea 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -95,7 +95,7 @@ class AFTimer { * @returns {number} Minutes to activation */ remainingMinutes(): number { - const minutesElapsed = this._minutesElapsed(); + const minutesElapsed = this.minutesElapsed(); const remainder = minutesElapsed % this._minutes; return Math.max(this._minutes - remainder, 0); } @@ -209,7 +209,7 @@ class AFTimer { * * @returns {number} Elapsed time in minutes */ - private _minutesElapsed(): number { + minutesElapsed(): number { const now = Date.now(); const last = this._settings.getInt64('timer-last-trigger'); diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index fe024e09..85d56459 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -104,9 +104,8 @@ class WallpaperController { // This may start the timer which might load a new wallpaper on interval surpassed this._updateAutoFetching(); - // FIXME: try skipping this if the timer is enabled and already fetched because of a surpassed interval when calling _updateAutoFetching above - // load a new wallpaper on startup - if (this._settings.getBoolean('fetch-on-startup')) { + // load a new wallpaper on startup, but don't when the timer already fetched one because of a surpassed timer interval + if (this._settings.getBoolean('fetch-on-startup') && (!this._timer.isEnabled() || this._timer.minutesElapsed() > 1)) { this.fetchNewWallpaper().catch(error => { this._logger.error(error); }); From 6105539c4681b5fa18afe5f1dc67158f87d4429f Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 1 Jul 2023 16:24:35 +0200 Subject: [PATCH 71/87] Move hardcoded strings to wallpaper manager --- src/manager/wallpaperManager.ts | 30 +++++++++++++++++++++++++++++- src/wallpaperController.ts | 19 ++++++++----------- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts index bc06cf18..d00df4a9 100644 --- a/src/manager/wallpaperManager.ts +++ b/src/manager/wallpaperManager.ts @@ -47,4 +47,32 @@ function getWallpaperManager(): WallpaperManager | null { return null; } -export {WallpaperManager, getWallpaperManager}; +/** + * Check if a filename matches a merged wallpaper name. + * + * @param {string} filename Naming to check + * @returns {boolean} Wether the image is a merged wallpaper + */ +// Check these outside of the class in case the user switched the manager +function isImageMerged(filename: string): boolean { + const mergedWallpaperNames = [ + // HydraPaper + 'merged_wallpaper', + // Superpaper + 'cli-a', + 'cli-b', + ]; + + for (const name of mergedWallpaperNames) { + if (filename.includes(name)) + return true; + } + + return false; +} + +export { + WallpaperManager, + getWallpaperManager, + isImageMerged +}; diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 85d56459..bca59723 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -9,7 +9,7 @@ import * as SettingsModule from './settings.js'; import * as Utils from './utils.js'; import {AFTimer as Timer} from './timer.js'; -import {getWallpaperManager} from './manager/wallpaperManager.js'; +import {getWallpaperManager, isImageMerged} from './manager/wallpaperManager.js'; import {Logger} from './logger.js'; // SourceAdapter @@ -392,32 +392,29 @@ class WallpaperController { } if (type === 0 || type === 2) { - // FIXME: These are currently hardcoded for the few available wallpaperManager - if (wallpaperUri.includes('merged_wallpaper') || wallpaperUri.includes('cli-a') || wallpaperUri.includes('cli-b')) - // merged wallpapers need mode "spanned" + if (isImageMerged(wallpaperUri)) + // merged wallpapers need mode "spanned" backgroundSettings.setString('picture-options', 'spanned'); else - // single wallpapers need mode "zoom" + // single wallpapers need mode "zoom" backgroundSettings.setString('picture-options', 'zoom'); Utils.setPictureUriOfSettingsObject(backgroundSettings, wallpaperUri); } if (type === 1) { - // FIXME: These are currently hardcoded for the few available wallpaperManager - if (wallpaperUri.includes('merged_wallpaper') || wallpaperUri.includes('cli-a') || wallpaperUri.includes('cli-b')) - // merged wallpapers need mode "spanned" + if (isImageMerged(wallpaperUri)) + // merged wallpapers need mode "spanned" screensaverSettings.setString('picture-options', 'spanned'); else - // single wallpapers need mode "zoom" + // single wallpapers need mode "zoom" screensaverSettings.setString('picture-options', 'zoom'); Utils.setPictureUriOfSettingsObject(screensaverSettings, wallpaperUri); } if (type === 2) { - // FIXME: These are currently hardcoded for the few available wallpaperManager - if (wallpaperUri.includes('merged_wallpaper') || wallpaperUri.includes('cli-a') || wallpaperUri.includes('cli-b')) + if (isImageMerged(wallpaperUri)) // merged wallpapers need mode "spanned" screensaverSettings.setString('picture-options', 'spanned'); else From 8f64775610f8888f520669f244a6bce836849502 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 1 Jul 2023 16:35:47 +0200 Subject: [PATCH 72/87] Remove UI todos - it may need an complete overhaul --- src/ui/sourceRow.blp | 2 +- src/ui/unsplash.blp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ui/sourceRow.blp b/src/ui/sourceRow.blp index 475a9a22..4553389e 100644 --- a/src/ui/sourceRow.blp +++ b/src/ui/sourceRow.blp @@ -64,7 +64,7 @@ template $SourceRow : Adw.ExpanderRow { Adw.Clamp settings_container { } - // FIXME: Additional PreferencesGroup solely for spacing to the next row when expanded + // Additional PreferencesGroup solely for spacing to the next row when expanded Adw.PreferencesGroup { } } } diff --git a/src/ui/unsplash.blp b/src/ui/unsplash.blp index 4c770066..7efadf98 100644 --- a/src/ui/unsplash.blp +++ b/src/ui/unsplash.blp @@ -53,8 +53,6 @@ template $UnsplashSettingsGroup : Adw.PreferencesGroup { Adw.PreferencesGroup { title: _("Contraint"); - // TODO: ExpanderRow with switch? - // Nested ExpanderRows behave strangely Adw.ComboRow constraint_type { title: _("Type"); } From a71fd5f18bce4e5756f7b0731c0efab3550d02f8 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 1 Jul 2023 20:45:27 +0200 Subject: [PATCH 73/87] Deduplicate manager code --- src/manager/hydraPaper.ts | 113 +++++++++----------------------- src/manager/superPaper.ts | 68 ++++--------------- src/manager/wallpaperManager.ts | 67 ++++++++++++++++++- 3 files changed, 106 insertions(+), 142 deletions(-) diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 2903e034..27b9324e 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -1,6 +1,3 @@ -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; - import * as Utils from '../utils.js'; import {Logger} from '../logger.js'; @@ -10,84 +7,9 @@ import {Settings} from '../settings.js'; /** * Wrapper for HydraPaper using it as a manager. */ -class HydraPaper implements WallpaperManager { - private _command: string[] | null = null; - private _cancellable: Gio.Cancellable | null = null; - private _logger = new Logger('RWG3', 'HydraPaper'); - - /** - * Checks if Superpaper is available in the $PATH. - * - * @returns {boolean} Whether Superpaper is found - */ - isAvailable(): boolean { - if (this._command !== null) - return true; - - const normalPath = GLib.find_program_in_path('hydrapaper'); - if (normalPath) { - this._command = [normalPath]; - return true; - } - - const flatpakPath = GLib.find_program_in_path('org.gabmus.hydrapaper'); - if (flatpakPath) { - this._command = [flatpakPath]; - return true; - } - - return this._command !== null; - } - - /** - * Forcefully stop a previously started HydraPaper process. - */ - cancelRunning(): void { - if (this._cancellable === null) - return; - - this._logger.debug('Stopping running HydraPaper process.'); - this._cancellable.cancel(); - this._cancellable = null; - } - - /** - * Run HydraPaper in CLI mode. - * - * HydraPaper: - * - Saves merged images in the cache folder. - * - Sets picture-option to spanned - * - Sets picture-uri or picture-uri-dark depending on $darkmode - * - Needs matching image path count and display count - * - * @param {string[]} wallpaperArray Array of image paths, should match the display count - * @param {boolean} darkmode Use darkmode, gives different image in cache path - */ - private async _run(wallpaperArray: string[], darkmode: boolean = false): Promise { - // Cancel already running processes before starting new ones - this.cancelRunning(); - - // TODO: Proper error handling - if (this._command === null) - return; - - // Needs a copy here - let command = [...this._command]; - - if (darkmode) - command.push('--darkmode'); - - command.push('--cli'); - command = command.concat(wallpaperArray); - - this._cancellable = new Gio.Cancellable(); - - // hydrapaper [--darkmode] --cli PATH PATH PATH - this._logger.debug(`Running command: ${command.toString()}`); - await Utils.execCheck(command, this._cancellable); - - this._cancellable = null; - } +class HydraPaper extends WallpaperManager { + readonly _possibleCommands = ['hydrapaper', 'org.gabmus.hydrapaper']; + _logger = new Logger('RWG3', 'HydraPaper'); /** * Set the wallpapers for a given mode. @@ -104,7 +26,7 @@ class HydraPaper implements WallpaperManager { */ async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings?: Settings, screensaverSettings?: Settings): Promise { if ((mode === 0 || mode === 2) && backgroundSettings) { - await this._run(wallpaperPaths); + await this._createCommandAndRun(wallpaperPaths); // Manually set key for darkmode because that's way faster backgroundSettings.setString('picture-uri-dark', backgroundSettings.getString('picture-uri')); @@ -116,7 +38,7 @@ class HydraPaper implements WallpaperManager { const tmpMode = backgroundSettings.getString('picture-options'); // Force HydraPaper to target a different resulting image by using darkmode - await this._run(wallpaperPaths, true); + await this._createCommandAndRun(wallpaperPaths, true); screensaverSettings.setString('picture-options', 'spanned'); Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); @@ -129,6 +51,31 @@ class HydraPaper implements WallpaperManager { if (mode === 2 && screensaverSettings && backgroundSettings) Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); } + + /** + * Run HydraPaper in CLI mode. + * + * HydraPaper: + * - Saves merged images in the cache folder. + * - Sets `picture-option` to `spanned` + * - Sets `picture-uri` or `picture-uri-dark` depending on {@link darkmode} + * - Needs matching image path count and display count + * + * @param {string[]} wallpaperArray Array of image paths, should match the display count + * @param {boolean} darkmode Use darkmode, gives different image in cache path + */ + private async _createCommandAndRun(wallpaperArray: string[], darkmode: boolean = false): Promise { + let command = []; + + if (darkmode) + command.push('--darkmode'); + + // hydrapaper [--darkmode] --cli PATH PATH PATH + command.push('--cli'); + command = command.concat(wallpaperArray); + + await this._runExternal(command); + } } export {HydraPaper}; diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts index 874f82f8..59a9ff22 100644 --- a/src/manager/superPaper.ts +++ b/src/manager/superPaper.ts @@ -1,6 +1,3 @@ -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; - import * as Utils from '../utils.js'; import {Logger} from './../logger.js'; @@ -10,40 +7,9 @@ import {WallpaperManager} from './wallpaperManager.js'; /** * Wrapper for Superpaper using it as a manager. */ -class Superpaper implements WallpaperManager { - private _command: string[] | null = null; - private _cancellable: Gio.Cancellable | null = null; - private _logger = new Logger('RWG3', 'Superpaper'); - - /** - * Checks if Superpaper is available in the $PATH. - * - * @returns {boolean} Whether Superpaper is found - */ - isAvailable(): boolean { - if (this._command !== null) - return true; - - const path = GLib.find_program_in_path('superpaper'); - if (path) { - this._command = [path]; - return true; - } - - return false; - } - - /** - * Forcefully stop a previously started Superpaper process. - */ - cancelRunning(): void { - if (!this._cancellable) - return; - - this._logger.debug('Stopping running HydraPaper process.'); - this._cancellable.cancel(); - this._cancellable = null; - } +class Superpaper extends WallpaperManager { + readonly _possibleCommands = ['superpaper']; + _logger = new Logger('RWG3', 'Superpaper'); /** * Set the wallpapers for a given mode. @@ -60,7 +26,7 @@ class Superpaper implements WallpaperManager { */ async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings: Settings, screensaverSettings: Settings): Promise { if ((mode === 0 || mode === 2) && backgroundSettings) - await this._run(wallpaperPaths); + await this._createCommandAndRun(wallpaperPaths); if (mode === 1 && backgroundSettings && screensaverSettings) { // Remember keys, Superpaper will change these @@ -68,7 +34,7 @@ class Superpaper implements WallpaperManager { const tmpBackgroundDark = backgroundSettings.getString('picture-uri-dark'); const tmpMode = backgroundSettings.getString('picture-options'); - await this._run(wallpaperPaths); + await this._createCommandAndRun(wallpaperPaths); screensaverSettings.setString('picture-options', 'spanned'); Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); @@ -88,33 +54,21 @@ class Superpaper implements WallpaperManager { * Run Superpaper in CLI mode. * * Superpaper: - * - Saves merged images alternating in "$XDG_CACHE_HOME/superpaper/temp/cli-{a,b}.png" - * - Sets picture-option to spanned - * - Sets both picture-uri options, light and dark + * - Saves merged images alternating in `$XDG_CACHE_HOME/superpaper/temp/cli-{a,b}.png` + * - Sets `picture-option` to `spanned` + * - Always sets both `picture-uri` and `picture-uri-dark` options * - Can use only single images * * @param {string[]} wallpaperArray Array of paths to the desired wallpapers, should match the display count, can be a single image */ - private async _run(wallpaperArray: string[]): Promise { - // Cancel already running processes before starting new ones - this.cancelRunning(); - - if (!this._command) - return; - - // Needs a copy here - let command = [...this._command]; + private async _createCommandAndRun(wallpaperArray: string[]): Promise { + let command = []; // cspell:disable-next-line command.push('--setimages'); command = command.concat(wallpaperArray); - this._cancellable = new Gio.Cancellable(); - - this._logger.debug(`Running command: ${command.toString()}`); - await Utils.execCheck(command, this._cancellable); - - this._cancellable = null; + await this._runExternal(command); } } diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts index d00df4a9..7bf957ec 100644 --- a/src/manager/wallpaperManager.ts +++ b/src/manager/wallpaperManager.ts @@ -1,4 +1,9 @@ +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; + +import {Logger} from 'logger.js'; import type {Settings} from './../settings.js'; +import * as Utils from '../utils.js'; import {HydraPaper} from './hydraPaper.js'; import {Superpaper} from './superPaper.js'; @@ -9,8 +14,66 @@ import {Superpaper} from './superPaper.js'; * Currently this is only used when in multiple monitor mode. */ abstract class WallpaperManager { - abstract isAvailable(): boolean; - abstract cancelRunning(): void; + private _cancellable: Gio.Cancellable | null = null; + protected static _command: string[] | null = null; + protected abstract readonly _possibleCommands: string[]; + protected abstract _logger: Logger; + + /** + * Forcefully stop a previously started manager process. + */ + private _cancelRunning(): void { + if (this._cancellable === null) + return; + + this._logger.debug('Stopping manager process.'); + this._cancellable.cancel(); + this._cancellable = null; + } + + /** + * Checks if the current manager is available in the `$PATH`. + * + * @returns {boolean} Whether the manager is found + */ + isAvailable(): boolean { + if (WallpaperManager._command !== null) + return true; + + for (const command of this._possibleCommands) { + const path = GLib.find_program_in_path(command); + + if (path) { + WallpaperManager._command = [path]; + break; + } + } + + return WallpaperManager._command !== null; + } + + /** + * Wrapper around calling the program command together with arguments. + * + * @param {string[]} commandArguments Arguments to append + */ + protected async _runExternal(commandArguments: string[]): Promise { + // Cancel already running processes before starting new ones + this._cancelRunning(); + + if (!WallpaperManager._command || WallpaperManager._command.length < 1) + throw new Error('Command empty!'); + + // Needs a copy here + const command = WallpaperManager._command.concat(commandArguments); + + this._cancellable = new Gio.Cancellable(); + + this._logger.debug(`Running command: ${command.toString()}`); + await Utils.execCheck(command, this._cancellable); + + this._cancellable = null; + } /** * Set the wallpapers for a given mode. From 7bd7f127ade0333f6395196f8b1eb99a30aaa298 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 1 Jul 2023 20:55:53 +0200 Subject: [PATCH 74/87] Ignore prefer rule for generated code --- .eslintrc-gjs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc-gjs.yml b/.eslintrc-gjs.yml index 0a6b2ac7..6e475f5c 100644 --- a/.eslintrc-gjs.yml +++ b/.eslintrc-gjs.yml @@ -219,7 +219,7 @@ rules: # prefer-destructuring: error prefer-numeric-literals: error prefer-promise-reject-errors: error - prefer-rest-params: error + prefer-rest-params: off # Generated code triggered this, turn off for now prefer-spread: error prefer-template: error quotes: From f0173dc40f6401c438e9aafa6cf6657264d53a34 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 2 Jul 2023 00:43:41 +0200 Subject: [PATCH 75/87] Introduce a default manager --- src/manager/defaultWallpaperManager.ts | 105 ++++++++++++++++++ src/manager/externalWallpaperManager.ts | 138 ++++++++++++++++++++++++ src/manager/hydraPaper.ts | 87 +++++++++------ src/manager/superPaper.ts | 86 +++++++++------ src/manager/wallpaperManager.ts | 131 ++++------------------ src/prefs.ts | 5 +- src/utils.ts | 42 ++++++++ src/wallpaperController.ts | 80 +++++--------- 8 files changed, 441 insertions(+), 233 deletions(-) create mode 100644 src/manager/defaultWallpaperManager.ts create mode 100644 src/manager/externalWallpaperManager.ts diff --git a/src/manager/defaultWallpaperManager.ts b/src/manager/defaultWallpaperManager.ts new file mode 100644 index 00000000..4f25d8e9 --- /dev/null +++ b/src/manager/defaultWallpaperManager.ts @@ -0,0 +1,105 @@ +import * as Utils from '../utils.js'; + +import {WallpaperManager} from './wallpaperManager.js'; +import {Logger} from '../logger.js'; +import type {Settings} from '../settings.js'; + +/** + * A general default wallpaper manager. + * + * Unable to handle multiple displays. + */ +class DefaultWallpaperManager extends WallpaperManager { + protected _logger = new Logger('RWG3', 'DefaultManager'); + + /** + * Sets the background image in light and dark mode. + * + * @param {string[]} wallpaperPaths Array of strings to image files, expects a single image only. + * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri + * @returns {Promise} Only resolves + */ + protected async _setBackground(wallpaperPaths: string[], backgroundSettings: Settings): Promise { + // The default manager can't handle multiple displays + if (wallpaperPaths.length > 1) + this._logger.warn('Single handling manager called with multiple images!'); + + await DefaultWallpaperManager.setSingleBackground(`file://${wallpaperPaths[0]}`, backgroundSettings); + + return Promise.resolve(); + } + + /** + * Sets the lock screen image in light and dark mode. + * + * @param {string[]} wallpaperPaths Array of strings to image files, expects a single image only. + * @param {Settings} _backgroundSettings Unused settings object + * @param {Settings} screensaverSettings Settings object holding the screensaver picture-uri + * @returns {Promise} Only resolves + */ + protected async _setLockScreen(wallpaperPaths: string[], _backgroundSettings: Settings, screensaverSettings: Settings): Promise { + // The default manager can't handle multiple displays + if (wallpaperPaths.length > 1) + this._logger.warn('Single handling manager called with multiple images!'); + + await DefaultWallpaperManager.setSingleLockScreen(`file://${wallpaperPaths[0]}`, _backgroundSettings, screensaverSettings); + + return Promise.resolve(); + } + + /** + * Default fallback function to set a single image background. + * + * @param {string} wallpaperURI URI to image file + * @param {Settings} backgroundSettings Settings containing the background `picture-uri` key + * @returns {Promise} Only resolves + */ + static setSingleBackground(wallpaperURI: string, backgroundSettings: Settings): Promise { + if (Utils.isImageMerged(wallpaperURI)) + // merged wallpapers need mode "spanned" + backgroundSettings.setString('picture-options', 'spanned'); + else + // single wallpapers need mode "zoom" + backgroundSettings.setString('picture-options', 'zoom'); + + Utils.setPictureUriOfSettingsObject(backgroundSettings, wallpaperURI); + return Promise.resolve(); + } + + /** + *Default fallback function to set a single image lock screen. + * + * @param {string} wallpaperURI URI to image file + * @param {Settings} backgroundSettings Settings containing the background `picture-uri` key + * @param {Settings} screensaverSettings Settings containing the lock screen `picture-uri` key + * @returns {Promise} Only resolves + */ + static setSingleLockScreen(wallpaperURI: string, backgroundSettings: Settings, screensaverSettings: Settings): Promise { + if (Utils.isImageMerged(wallpaperURI)) + // merged wallpapers need mode "spanned" + screensaverSettings.setString('picture-options', 'spanned'); + else + // single wallpapers need mode "zoom" + screensaverSettings.setString('picture-options', 'zoom'); + + Utils.setPictureUriOfSettingsObject(screensaverSettings, wallpaperURI); + return Promise.resolve(); + } + + /** + * Check if a filename matches a merged wallpaper name. + * + * Merged wallpaper need special handling as these are single images + * but span across all displays. + * + * @param {string} _filename Unused naming to check + * @returns {boolean} Wether the image is a merged wallpaper + */ + static isImageMerged(_filename: string): boolean { + // This manager can't create merged wallpaper + return false; + } +} + + +export {DefaultWallpaperManager}; diff --git a/src/manager/externalWallpaperManager.ts b/src/manager/externalWallpaperManager.ts new file mode 100644 index 00000000..3a4d58ad --- /dev/null +++ b/src/manager/externalWallpaperManager.ts @@ -0,0 +1,138 @@ +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; + +import * as Utils from '../utils.js'; + +import {DefaultWallpaperManager} from './defaultWallpaperManager.js'; +import {Mode, WallpaperManager} from './wallpaperManager.js'; +import type {Settings} from '../settings.js'; + +/** + * Abstract base class for external manager to implement. + */ +abstract class ExternalWallpaperManager extends WallpaperManager { + private _cancellable: Gio.Cancellable | null = null; + protected static _command: string[] | null = null; + protected abstract readonly _possibleCommands: string[]; + canHandleMultipleImages = true; + + /** + * Checks if the current manager is available in the `$PATH`. + * + * @returns {boolean} Whether the manager is found + */ + isAvailable(): boolean { + if (ExternalWallpaperManager._command !== null) + return true; + + for (const command of this._possibleCommands) { + const path = GLib.find_program_in_path(command); + + if (path) { + ExternalWallpaperManager._command = [path]; + break; + } + } + + return ExternalWallpaperManager._command !== null; + } + + /** + * Set the wallpapers for a given mode. + * + * Modes: + * - 0: Background + * - 1: Lock screen + * - 2: Background and lock screen + * + * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count + * @param {Mode} mode Enum indicating what images to change + * @param {Settings} backgroundSettings Settings object containing the background settings + * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings + */ + async setWallpaper(wallpaperPaths: string[], mode: Mode, backgroundSettings: Settings, screensaverSettings: Settings): Promise { + // Cancel already running processes before setting new images + this._cancelRunning(); + + // Fallback to default manager, all currently supported external manager don't support setting single images + if (wallpaperPaths.length === 1) { + const promises = []; + + if (mode === Mode.BACKGROUND || mode === Mode.BACKGROUND_AND_LOCKSCREEN) + promises.push(DefaultWallpaperManager.setSingleBackground(`file://${wallpaperPaths[0]}`, backgroundSettings)); + + if (mode === Mode.LOCKSCREEN || mode === Mode.BACKGROUND_AND_LOCKSCREEN) + promises.push(DefaultWallpaperManager.setSingleLockScreen(`file://${wallpaperPaths[0]}`, backgroundSettings, screensaverSettings)); + + await Promise.allSettled(promises); + return; + } + + /** + * Don't run these concurrently! + * External manager may need to shove settings around to circumvent the fact the manager writes multiple settings on its own. + * These are called in this fixed order so external manager can rely on functions ran previously. + */ + + if (mode === Mode.BACKGROUND || mode === Mode.BACKGROUND_AND_LOCKSCREEN) + await this._setBackground(wallpaperPaths, backgroundSettings); + + if (mode === Mode.LOCKSCREEN) + await this._setLockScreen(wallpaperPaths, backgroundSettings, screensaverSettings); + + if (mode === Mode.BACKGROUND_AND_LOCKSCREEN) + await this._setLockScreenAfterBackground(wallpaperPaths, backgroundSettings, screensaverSettings); + } + + /** + * Forcefully stop a previously started manager process. + */ + private _cancelRunning(): void { + if (this._cancellable === null) + return; + + this._logger.debug('Stopping manager process.'); + this._cancellable.cancel(); + this._cancellable = null; + } + + /** + * Wrapper around calling the program command together with arguments. + * + * @param {string[]} commandArguments Arguments to append + */ + protected async _runExternal(commandArguments: string[]): Promise { + // Cancel already running processes before starting new ones + this._cancelRunning(); + + if (!ExternalWallpaperManager._command || ExternalWallpaperManager._command.length < 1) + throw new Error('Command empty!'); + + // Needs a copy here + const command = ExternalWallpaperManager._command.concat(commandArguments); + + this._cancellable = new Gio.Cancellable(); + + this._logger.debug(`Running command: ${command.toString()}`); + await Utils.execCheck(command, this._cancellable); + + this._cancellable = null; + } + + /** + * Sync the lock screen to the background. + * + * This function exists to save compute time on identical background and lock screen images. + * + * @param {string[]} _wallpaperPaths Unused array of strings to image files + * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri + * @param {Settings} screensaverSettings Settings object holding the screensaver picture-uri + * @returns {Promise} Only resolves + */ + protected _setLockScreenAfterBackground(_wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise { + Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + return Promise.resolve(); + } +} + +export {ExternalWallpaperManager}; diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 27b9324e..4e3403b3 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -1,55 +1,50 @@ import * as Utils from '../utils.js'; +import {ExternalWallpaperManager} from './externalWallpaperManager.js'; import {Logger} from '../logger.js'; -import {WallpaperManager} from './wallpaperManager.js'; import {Settings} from '../settings.js'; /** * Wrapper for HydraPaper using it as a manager. */ -class HydraPaper extends WallpaperManager { - readonly _possibleCommands = ['hydrapaper', 'org.gabmus.hydrapaper']; - _logger = new Logger('RWG3', 'HydraPaper'); +class HydraPaper extends ExternalWallpaperManager { + protected readonly _possibleCommands = ['hydrapaper', 'org.gabmus.hydrapaper']; + protected _logger = new Logger('RWG3', 'HydraPaper'); /** - * Set the wallpapers for a given mode. + * Sets the background image in light and dark mode. * - * Modes: - * - 0: Background - * - 1: Lock screen - * - 2: Background and lock screen - * - * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count - * @param {number} mode Enum indicating what images to change - * @param {Settings} backgroundSettings Settings object containing the background settings - * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings + * @param {string[]} wallpaperPaths Array of strings to image files + * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri */ - async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings?: Settings, screensaverSettings?: Settings): Promise { - if ((mode === 0 || mode === 2) && backgroundSettings) { - await this._createCommandAndRun(wallpaperPaths); - - // Manually set key for darkmode because that's way faster - backgroundSettings.setString('picture-uri-dark', backgroundSettings.getString('picture-uri')); - } + protected async _setBackground(wallpaperPaths: string[], backgroundSettings: Settings): Promise { + await this._createCommandAndRun(wallpaperPaths); - if (mode === 1 && backgroundSettings && screensaverSettings) { - // Remember keys, HydraPaper will change these - const tmpBackground = backgroundSettings.getString('picture-uri-dark'); - const tmpMode = backgroundSettings.getString('picture-options'); + // Manually set key for darkmode because that's way faster than merging two times the same images + Utils.setPictureUriOfSettingsObject(backgroundSettings, backgroundSettings.getString('picture-uri')); + } - // Force HydraPaper to target a different resulting image by using darkmode - await this._createCommandAndRun(wallpaperPaths, true); + /** + * Sets the lock screen image in light and dark mode. + * + * @param {string[]} wallpaperPaths Array of strings to image files + * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri + * @param {Settings} screensaverSettings Settings object holding the screensaver picture-uri + */ + protected async _setLockScreen(wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise { + // Remember keys, HydraPaper will change these + const tmpBackground = backgroundSettings.getString('picture-uri-dark'); + const tmpMode = backgroundSettings.getString('picture-options'); - screensaverSettings.setString('picture-options', 'spanned'); - Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); + // Force HydraPaper to target a different resulting image by using darkmode + await this._createCommandAndRun(wallpaperPaths, true); - // HydraPaper possibly changed these, change them back - backgroundSettings.setString('picture-uri-dark', tmpBackground); - backgroundSettings.setString('picture-options', tmpMode); - } + screensaverSettings.setString('picture-options', 'spanned'); + Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); - if (mode === 2 && screensaverSettings && backgroundSettings) - Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + // HydraPaper possibly changed these, change them back + backgroundSettings.setString('picture-uri-dark', tmpBackground); + backgroundSettings.setString('picture-options', tmpMode); } /** @@ -76,6 +71,28 @@ class HydraPaper extends WallpaperManager { await this._runExternal(command); } + + /** + * Check if a filename matches a merged wallpaper name. + * + * Merged wallpaper need special handling as these are single images + * but span across all displays. + * + * @param {string} filename Naming to check + * @returns {boolean} Wether the image is a merged wallpaper + */ + static isImageMerged(filename: string): boolean { + const mergedWallpaperNames = [ + 'merged_wallpaper', + ]; + + for (const name of mergedWallpaperNames) { + if (filename.includes(name)) + return true; + } + + return false; + } } export {HydraPaper}; diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts index 59a9ff22..71ea3dce 100644 --- a/src/manager/superPaper.ts +++ b/src/manager/superPaper.ts @@ -1,52 +1,49 @@ import * as Utils from '../utils.js'; +import {ExternalWallpaperManager} from './externalWallpaperManager.js'; import {Logger} from './../logger.js'; import {Settings} from './../settings.js'; -import {WallpaperManager} from './wallpaperManager.js'; /** * Wrapper for Superpaper using it as a manager. */ -class Superpaper extends WallpaperManager { - readonly _possibleCommands = ['superpaper']; - _logger = new Logger('RWG3', 'Superpaper'); +class Superpaper extends ExternalWallpaperManager { + protected readonly _possibleCommands = ['superpaper']; + protected _logger = new Logger('RWG3', 'Superpaper'); /** - * Set the wallpapers for a given mode. + * Sets the background image in light and dark mode. * - * Modes: - * - 0: Background - * - 1: Lock screen - * - 2: Background and lock screen - * - * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count - * @param {number} mode Enum indicating what images to change - * @param {Settings} backgroundSettings Settings object containing the background settings - * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings + * @param {string[]} wallpaperPaths Array of strings to image files + * @param {Settings} _backgroundSettings Unused settings object */ - async setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings: Settings, screensaverSettings: Settings): Promise { - if ((mode === 0 || mode === 2) && backgroundSettings) - await this._createCommandAndRun(wallpaperPaths); - - if (mode === 1 && backgroundSettings && screensaverSettings) { - // Remember keys, Superpaper will change these - const tmpBackground = backgroundSettings.getString('picture-uri'); - const tmpBackgroundDark = backgroundSettings.getString('picture-uri-dark'); - const tmpMode = backgroundSettings.getString('picture-options'); + // We don't need the settings object because Superpaper already set both picture-uri on it's own. + protected async _setBackground(wallpaperPaths: string[], _backgroundSettings: Settings): Promise { + await this._createCommandAndRun(wallpaperPaths); + } - await this._createCommandAndRun(wallpaperPaths); + /** + * Sets the lock screen image in light and dark mode. + * + * @param {string[]} wallpaperPaths Array of strings to image files + * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri + * @param {Settings} screensaverSettings Settings object holding the screensaver picture-uri + */ + protected async _setLockScreen(wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise { + // Remember keys, Superpaper will change these + const tmpBackground = backgroundSettings.getString('picture-uri'); + const tmpBackgroundDark = backgroundSettings.getString('picture-uri-dark'); + const tmpMode = backgroundSettings.getString('picture-options'); - screensaverSettings.setString('picture-options', 'spanned'); - Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); + await this._createCommandAndRun(wallpaperPaths); - // Superpaper possibly changed these, change them back - backgroundSettings.setString('picture-uri', tmpBackground); - backgroundSettings.setString('picture-uri-dark', tmpBackgroundDark); - backgroundSettings.setString('picture-options', tmpMode); - } + screensaverSettings.setString('picture-options', 'spanned'); + Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); - if (mode === 2 && screensaverSettings && backgroundSettings) - Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + // Superpaper possibly changed these, change them back + backgroundSettings.setString('picture-uri', tmpBackground); + backgroundSettings.setString('picture-uri-dark', tmpBackgroundDark); + backgroundSettings.setString('picture-options', tmpMode); } // https://github.com/hhannine/superpaper/blob/master/docs/cli-usage.md @@ -70,6 +67,29 @@ class Superpaper extends WallpaperManager { await this._runExternal(command); } + + /** + * Check if a filename matches a merged wallpaper name. + * + * Merged wallpaper need special handling as these are single images + * but span across all displays. + * + * @param {string} filename Naming to check + * @returns {boolean} Wether the image is a merged wallpaper + */ + static isImageMerged(filename: string): boolean { + const mergedWallpaperNames = [ + 'cli-a', + 'cli-b', + ]; + + for (const name of mergedWallpaperNames) { + if (filename.includes(name)) + return true; + } + + return false; + } } export {Superpaper}; diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts index 7bf957ec..e4bad543 100644 --- a/src/manager/wallpaperManager.ts +++ b/src/manager/wallpaperManager.ts @@ -1,79 +1,19 @@ -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; - -import {Logger} from 'logger.js'; +import {Logger} from '../logger.js'; import type {Settings} from './../settings.js'; -import * as Utils from '../utils.js'; -import {HydraPaper} from './hydraPaper.js'; -import {Superpaper} from './superPaper.js'; +const enum Mode { + BACKGROUND, + LOCKSCREEN, + BACKGROUND_AND_LOCKSCREEN, + BACKGROUND_AND_LOCKSCREEN_INDEPENDENT, +} /** - * Wallpaper manager is a base class for external manager to implement. - * - * Currently this is only used when in multiple monitor mode. + * Wallpaper manager is a base class for manager to implement. */ abstract class WallpaperManager { - private _cancellable: Gio.Cancellable | null = null; - protected static _command: string[] | null = null; - protected abstract readonly _possibleCommands: string[]; protected abstract _logger: Logger; - - /** - * Forcefully stop a previously started manager process. - */ - private _cancelRunning(): void { - if (this._cancellable === null) - return; - - this._logger.debug('Stopping manager process.'); - this._cancellable.cancel(); - this._cancellable = null; - } - - /** - * Checks if the current manager is available in the `$PATH`. - * - * @returns {boolean} Whether the manager is found - */ - isAvailable(): boolean { - if (WallpaperManager._command !== null) - return true; - - for (const command of this._possibleCommands) { - const path = GLib.find_program_in_path(command); - - if (path) { - WallpaperManager._command = [path]; - break; - } - } - - return WallpaperManager._command !== null; - } - - /** - * Wrapper around calling the program command together with arguments. - * - * @param {string[]} commandArguments Arguments to append - */ - protected async _runExternal(commandArguments: string[]): Promise { - // Cancel already running processes before starting new ones - this._cancelRunning(); - - if (!WallpaperManager._command || WallpaperManager._command.length < 1) - throw new Error('Command empty!'); - - // Needs a copy here - const command = WallpaperManager._command.concat(commandArguments); - - this._cancellable = new Gio.Cancellable(); - - this._logger.debug(`Running command: ${command.toString()}`); - await Utils.execCheck(command, this._cancellable); - - this._cancellable = null; - } + canHandleMultipleImages = false; /** * Set the wallpapers for a given mode. @@ -84,58 +24,27 @@ abstract class WallpaperManager { * - 2: Background and lock screen * * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count - * @param {number} mode Enum indicating what images to change + * @param {Mode} mode Enum indicating what images to change * @param {Settings} backgroundSettings Settings object containing the background settings * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings */ - abstract setWallpaper(wallpaperPaths: string[], mode: number, backgroundSettings: Settings, screensaverSettings: Settings): Promise; -} - -/** - * Get a wallpaper manager. - * - * Checks for HydraPaper first and then for Superpaper. - * - * @returns {WallpaperManager | null} Wallpaper manager if found or null - */ -function getWallpaperManager(): WallpaperManager | null { - const hydraPaper = new HydraPaper(); - if (hydraPaper.isAvailable()) - return hydraPaper; + async setWallpaper(wallpaperPaths: string[], mode: Mode, backgroundSettings: Settings, screensaverSettings: Settings): Promise { + const promises = []; - const superpaper = new Superpaper(); - if (superpaper.isAvailable()) - return superpaper; + if (mode === Mode.BACKGROUND || mode === Mode.BACKGROUND_AND_LOCKSCREEN) + promises.push(this._setBackground(wallpaperPaths, backgroundSettings)); - return null; -} - -/** - * Check if a filename matches a merged wallpaper name. - * - * @param {string} filename Naming to check - * @returns {boolean} Wether the image is a merged wallpaper - */ -// Check these outside of the class in case the user switched the manager -function isImageMerged(filename: string): boolean { - const mergedWallpaperNames = [ - // HydraPaper - 'merged_wallpaper', - // Superpaper - 'cli-a', - 'cli-b', - ]; + if (mode === Mode.LOCKSCREEN || mode === Mode.BACKGROUND_AND_LOCKSCREEN) + promises.push(this._setLockScreen(wallpaperPaths, backgroundSettings, screensaverSettings)); - for (const name of mergedWallpaperNames) { - if (filename.includes(name)) - return true; + await Promise.allSettled(promises); } - return false; + protected abstract _setBackground(wallpaperPaths: string[], backgroundSettings: Settings): Promise; + protected abstract _setLockScreen(wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise; } export { WallpaperManager, - getWallpaperManager, - isImageMerged + Mode }; diff --git a/src/prefs.ts b/src/prefs.ts index 65e71846..d7a27aca 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -155,8 +155,9 @@ class RandomWallpaperSettings { }); }); - import('./manager/wallpaperManager.js').then(module => { - if (module.getWallpaperManager()?.isAvailable()) + import('./utils.js').then(module => { + const manager = module.getWallpaperManager(); + if (manager.canHandleMultipleImages) this._builder.get_object>('multiple_displays_row').set_sensitive(true); }).catch(error => { this._logger.error(error); diff --git a/src/utils.ts b/src/utils.ts index 97e76c38..d0905ad1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,6 +5,10 @@ import GLib from 'gi://GLib'; import Gtk from 'gi://Gtk'; import {Settings} from './settings.js'; +import {DefaultWallpaperManager} from './manager/defaultWallpaperManager.js'; +import {HydraPaper} from './manager/hydraPaper.js'; +import {Superpaper} from './manager/superPaper.js'; +import type {WallpaperManager} from './manager/wallpaperManager.js'; // Generated code produces a no-shadow rule error: // 'SourceType' is already declared in the upper scope on line 7 column 5 no-shadow @@ -270,9 +274,47 @@ function setPictureUriOfSettingsObject(settings: Settings, uri: string): void { setProp(property); } +/** + * Get a wallpaper manager. + * + * Checks for HydraPaper first and then for Superpaper. Falls back to the default manager. + * + * @returns {WallpaperManager} Wallpaper manager, falls back to the default manager + */ +// This function is here instead of wallpaperManager.js to work around looping import errors +function getWallpaperManager(): WallpaperManager { + const hydraPaper = new HydraPaper(); + if (hydraPaper.isAvailable()) + return hydraPaper; + + const superpaper = new Superpaper(); + if (superpaper.isAvailable()) + return superpaper; + + return new DefaultWallpaperManager(); +} + +/** + * Check if a filename matches a merged wallpaper name. + * + * Merged wallpaper need special handling as these are single images + * but span across all displays. + * + * @param {string} filename Naming to check + * @returns {boolean} Wether the image is a merged wallpaper + */ +// This function is here instead of wallpaperManager.js to work around looping import errors +function isImageMerged(filename: string): boolean { + return DefaultWallpaperManager.isImageMerged(filename) || + HydraPaper.isImageMerged(filename) || + Superpaper.isImageMerged(filename); +} + export { SourceType, getSourceTypeName, + getWallpaperManager, + isImageMerged, execCheck, fileName, fillComboRowFromEnum, diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index bca59723..b50ec914 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -9,8 +9,8 @@ import * as SettingsModule from './settings.js'; import * as Utils from './utils.js'; import {AFTimer as Timer} from './timer.js'; -import {getWallpaperManager, isImageMerged} from './manager/wallpaperManager.js'; import {Logger} from './logger.js'; +import {Mode} from './manager/wallpaperManager.js'; // SourceAdapter import {BaseAdapter} from './adapter/baseAdapter.js'; @@ -45,7 +45,7 @@ class WallpaperController { private _settings = new SettingsModule.Settings(); private _timer = Timer.getTimer(); private _historyController: HistoryModule.HistoryController; - private _wallpaperManager = getWallpaperManager(); + private _wallpaperManager = Utils.getWallpaperManager(); private _autoFetch = {active: false, duration: 30}; private _previewId: string | undefined; private _resetWallpaper = false; @@ -97,6 +97,13 @@ class WallpaperController { this._settings.observe('minutes', () => this._updateAutoFetching()); this._settings.observe('hours', () => this._updateAutoFetching()); + /** + * When the user installs a manager we won't notice that it's available. + * The preference window however checks on startup for availability and will allow this setting + * to change. Let's listen for that change and update our manager accordingly. + */ + this._settings.observe('multiple-displays', () => this._updateWallpaperManager()); + this._updateHistory(); // Fetching and merging wallpaper can be quite heavy on load so try doing this only when idle @@ -235,6 +242,13 @@ class WallpaperController { } } + /** + * Update the wallpaper manager on settings change. + */ + private _updateWallpaperManager(): void { + this._wallpaperManager = Utils.getWallpaperManager(); + } + /** * Get an array of random adapter needed to fill the display $count. * @@ -369,60 +383,22 @@ class WallpaperController { /** * Sets the wallpaper and the lock screen when enabled to the given path. * - * Types: + * Modes: * 0: Background * 1: Lock screen * 2: Background and lock screen * * @param {string[]} wallpaperPaths Array of paths to the image - * @param {number} type Types to change + * @param {Mode} mode Types to change */ - private async _setBackground(wallpaperPaths: string[], type: number = 0): Promise { + private async _setBackground(wallpaperPaths: string[], mode: Mode = Mode.BACKGROUND): Promise { const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); const screensaverSettings = new SettingsModule.Settings('org.gnome.desktop.screensaver'); if (wallpaperPaths.length < 1) throw new Error('Empty wallpaper array'); - const wallpaperUri = `file://${wallpaperPaths[0]}`; - - if (wallpaperPaths.length > 1 && this._wallpaperManager) { - await this._wallpaperManager.setWallpaper(wallpaperPaths, type, backgroundSettings, screensaverSettings); - return; - } - - if (type === 0 || type === 2) { - if (isImageMerged(wallpaperUri)) - // merged wallpapers need mode "spanned" - backgroundSettings.setString('picture-options', 'spanned'); - else - // single wallpapers need mode "zoom" - backgroundSettings.setString('picture-options', 'zoom'); - - Utils.setPictureUriOfSettingsObject(backgroundSettings, wallpaperUri); - } - - if (type === 1) { - if (isImageMerged(wallpaperUri)) - // merged wallpapers need mode "spanned" - screensaverSettings.setString('picture-options', 'spanned'); - else - // single wallpapers need mode "zoom" - screensaverSettings.setString('picture-options', 'zoom'); - - Utils.setPictureUriOfSettingsObject(screensaverSettings, wallpaperUri); - } - - if (type === 2) { - if (isImageMerged(wallpaperUri)) - // merged wallpapers need mode "spanned" - screensaverSettings.setString('picture-options', 'spanned'); - else - // single wallpapers need mode "zoom" - screensaverSettings.setString('picture-options', 'zoom'); - - Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); - } + await this._wallpaperManager.setWallpaper(wallpaperPaths, mode, backgroundSettings, screensaverSettings); } /** @@ -487,8 +463,8 @@ class WallpaperController { // ignore changeType === 3 because that doesn't make sense // when requesting a specific history entry - if (changeType > 2) - await this._setBackground(usedWallpaperPaths, 2); + if (changeType > Mode.BACKGROUND_AND_LOCKSCREEN) + await this._setBackground(usedWallpaperPaths, Mode.BACKGROUND_AND_LOCKSCREEN); else await this._setBackground(usedWallpaperPaths, changeType); @@ -583,11 +559,11 @@ class WallpaperController { const usedWallpaperPaths = this._fillDisplaysFromHistory(newWallpaperPaths, monitorCount); - if (changeType === 3) { + if (changeType === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT) { // Half the images for the background - await this._setBackground(usedWallpaperPaths.slice(0, monitorCount / 2), 0); + await this._setBackground(usedWallpaperPaths.slice(0, monitorCount / 2), Mode.BACKGROUND); // Half the images for the lock screen - await this._setBackground(usedWallpaperPaths.slice(monitorCount / 2), 1); + await this._setBackground(usedWallpaperPaths.slice(monitorCount / 2), Mode.LOCKSCREEN); } else { await this._setBackground(usedWallpaperPaths, changeType); } @@ -659,7 +635,7 @@ class WallpaperController { if (!this._settings.getBoolean('multiple-displays')) return 1; - if (!this._wallpaperManager?.isAvailable()) + if (!this._wallpaperManager.canHandleMultipleImages) return 1; return Utils.getMonitorCount(); @@ -686,12 +662,12 @@ class WallpaperController { // Only change the background - the lock screen wouldn't be visible anyway // because this function is only used for hover preview if (this._resetWallpaper) { - this._setBackground(paths, 0).catch(error => { + this._setBackground(paths, Mode.BACKGROUND).catch(error => { this._logger.error(error); }); this._resetWallpaper = false; } else if (this._previewId !== undefined) { - this._setBackground(paths, 0).catch(error => { + this._setBackground(paths, Mode.BACKGROUND).catch(error => { this._logger.error(error); }); } From 68ad2b0ba3b95a9457264041aa5a28074345b4c2 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 2 Jul 2023 16:50:19 +0200 Subject: [PATCH 76/87] Request monitor count from Mutter instead of Gdk For some reason Gdk was now unable to get my monitor count. --- src/utils.ts | 32 ++++++++++++-------------------- types/mappings/index.d.ts | 4 ++++ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index d0905ad1..8160e44e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,13 +1,14 @@ import Adw from 'gi://Adw'; -import Gdk from 'gi://Gdk'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import Gtk from 'gi://Gtk'; +import type Meta from 'gi://Meta'; -import {Settings} from './settings.js'; import {DefaultWallpaperManager} from './manager/defaultWallpaperManager.js'; import {HydraPaper} from './manager/hydraPaper.js'; +import {Logger} from './logger.js'; import {Superpaper} from './manager/superPaper.js'; +import {Settings} from './settings.js'; import type {WallpaperManager} from './manager/wallpaperManager.js'; // Generated code produces a no-shadow rule error: @@ -164,26 +165,17 @@ function findFirstDifference(str1: string, str2: string): number { * @returns {number} Connected display count */ function getMonitorCount(): number { - // Gdk 4.8+ - // Gdk.DisplayManager.get() - // displayManager.get_default_display() - // display.get_monitors() - // monitors.get_n_items() <- Monitor count, number - - // let defaultDisplay = Gdk.Display.get_default(); // default "seat" which can have multiple monitors - // let monitorList = defaultDisplay.get_monitors(); // Gio.ListModel containing all "Gdk.Monitor" - // return monitorList.get_n_items(); - - // Gdk < 4.8 - const defaultDisplay = Gdk.Display.get_default(); + // FIXME: Figure out where the 'global' thing can be imported from + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const currentDisplay = global?.display as Meta.Display; + const count = currentDisplay?.get_n_monitors(); - if (!defaultDisplay) - return 1; + if (count) + return count; - // FIXME: wrong version in definition - // @ts-expect-error - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - return defaultDisplay.get_n_monitors() as number; + new Logger('RWG3', 'Utils').warn('Unable to get monitor count!'); + return 1; } /** diff --git a/types/mappings/index.d.ts b/types/mappings/index.d.ts index 852ced5f..271e65af 100644 --- a/types/mappings/index.d.ts +++ b/types/mappings/index.d.ts @@ -7,6 +7,10 @@ declare module 'gi://Cogl' { export * from '@gi-types/cogl'; } +declare module 'gi://Meta' { + export * from '@gi-types/meta'; +} + declare module 'gi://St' { export * from '@gi-types/st'; } From 39a66e26aabbaa20219de6f9f9d8b3f8228b689f Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 5 Jul 2023 14:29:34 +0200 Subject: [PATCH 77/87] Use API key header instead of parameter This stops spoiling the API key in sidecar files when saving an image. --- src/adapter/wallhaven.ts | 48 +++++++++++++++---- ...ns.space.iflow.randomwallpaper.gschema.xml | 2 +- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index b48e8f36..dd46fc34 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -1,3 +1,5 @@ +import Gio from 'gi://Gio'; + import * as SettingsModule from './../settings.js'; import * as Utils from './../utils.js'; @@ -6,7 +8,6 @@ import {HistoryEntry} from './../history.js'; interface QueryOptions { q: string, - apikey: string, purity: string, sorting: string, categories: string, @@ -31,7 +32,6 @@ interface WallhavenSearchResponse { class WallhavenAdapter extends BaseAdapter { private _options: QueryOptions = { q: '', - apikey: '', purity: '110', // SFW, sketchy sorting: 'random', categories: '111', // General, Anime, People @@ -72,6 +72,10 @@ class WallhavenAdapter extends BaseAdapter { const url = `https://wallhaven.cc/api/v1/search?${encodeURI(optionsString)}`; const message = this._bowl.newGetMessage(url); + const apiKey = this._settings.getString('api-key'); + if (apiKey !== '') + message.requestHeaders.append('X-API-Key', apiKey); + this._logger.debug(`Search URL: ${url}`); let wallhavenResponse; @@ -97,15 +101,11 @@ class WallhavenAdapter extends BaseAdapter { for (let i = 0; i < response.length && wallpaperResult.length < count; i++) { const entry = response[i]; const siteURL = entry.url; - let downloadURL = entry.path; + const downloadURL = entry.path; if (this._isImageBlocked(Utils.fileName(downloadURL))) continue; - const apiKey = this._options['apikey']; - if (apiKey !== '') - downloadURL += `?apikey=${apiKey}`; - const historyEntry = new HistoryEntry(null, this._sourceName, downloadURL); historyEntry.source.sourceUrl = 'https://wallhaven.cc/'; historyEntry.source.imageLinkUrl = siteURL; @@ -122,6 +122,39 @@ class WallhavenAdapter extends BaseAdapter { return wallpaperResult; } + /** + * Fetches an image according to a given HistoryEntry. + * + * This implementation requests the image in HistoryEntry.source.imageDownloadUrl + * using Soup and saves it to HistoryEntry.path while setting the X-API-Key header. + * + * @param {HistoryEntry} historyEntry The historyEntry to fetch + * @returns {Promise} unaltered HistoryEntry + */ + async fetchFile(historyEntry: HistoryEntry): Promise { + const file = Gio.file_new_for_path(historyEntry.path); + const fstream = file.replace(null, false, Gio.FileCreateFlags.NONE, null); + + // craft new message from details + const request = this._bowl.newGetMessage(historyEntry.source.imageDownloadUrl); + + const apiKey = this._settings.getString('api-key'); + if (apiKey !== '') + request.requestHeaders.append('X-API-Key', apiKey); + + // start the download + const response_data_bytes = await this._bowl.send_and_receive(request); + if (!response_data_bytes) { + fstream.close(null); + throw new Error('Not a valid image response'); + } + + fstream.write(response_data_bytes, null); + fstream.close(null); + + return historyEntry; + } + /** * Create an option string based on user settings. * @@ -173,7 +206,6 @@ class WallhavenAdapter extends BaseAdapter { const randomKeyword = keywords[Utils.getRandomNumber(keywords.length)]; this._options.q = randomKeyword.trim(); } - this._options.apikey = this._settings.getString('api-key'); this._options.atleast = this._settings.getString('minimal-resolution'); this._options.ratios = this._settings.getString('aspect-ratios').split(','); diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index f47667eb..5df3e518 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -253,7 +253,7 @@ "" Api key - The wallheven api key. + The Wallhaven API key. From ea75f9efbac29ca46d07c888a4b587a7344019e6 Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 5 Jul 2023 14:34:11 +0200 Subject: [PATCH 78/87] Correct spelling error throughout the project. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit weather → whether --- src/adapter/baseAdapter.ts | 2 +- src/adapter/reddit.ts | 2 +- src/adapter/wallhaven.ts | 2 +- src/manager/defaultWallpaperManager.ts | 2 +- src/manager/hydraPaper.ts | 2 +- src/manager/superPaper.ts | 2 +- ...xtensions.space.iflow.randomwallpaper.gschema.xml | 12 ++++++------ src/timer.ts | 4 ++-- src/ui/unsplash.ts | 2 +- src/utils.ts | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/adapter/baseAdapter.ts b/src/adapter/baseAdapter.ts index 426e418c..ae5d3930 100755 --- a/src/adapter/baseAdapter.ts +++ b/src/adapter/baseAdapter.ts @@ -92,7 +92,7 @@ abstract class BaseAdapter { * * @param {HistoryEntry[]} array Array to search in * @param {string} uri URI to search for - * @returns {boolean} Wether the array contains an item with $uri + * @returns {boolean} Whether the array contains an item with $uri */ protected _includesWallpaper(array: HistoryEntry[], uri: string): boolean { for (const element of array) { diff --git a/src/adapter/reddit.ts b/src/adapter/reddit.ts index 6856381e..9aae7ab6 100644 --- a/src/adapter/reddit.ts +++ b/src/adapter/reddit.ts @@ -142,7 +142,7 @@ class RedditAdapter extends BaseAdapter { * Primarily in use for typescript typing. * * @param {unknown} object Unknown object to narrow down - * @returns {boolean} Wether the response is from Reddit + * @returns {boolean} Whether the response is from Reddit */ private _isRedditResponse(object: unknown): object is RedditResponse { if (typeof object === 'object' && diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index dd46fc34..ae1b56d3 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -184,7 +184,7 @@ class WallhavenAdapter extends BaseAdapter { * Primarily in use for typescript typing. * * @param {unknown} object Unknown object to narrow down - * @returns {boolean} Wether the response is from Reddit + * @returns {boolean} Whether the response is from Reddit */ private _isWallhavenResponse(object: unknown): object is WallhavenSearchResponse { if (typeof object === 'object' && diff --git a/src/manager/defaultWallpaperManager.ts b/src/manager/defaultWallpaperManager.ts index 4f25d8e9..5b12ed1e 100644 --- a/src/manager/defaultWallpaperManager.ts +++ b/src/manager/defaultWallpaperManager.ts @@ -93,7 +93,7 @@ class DefaultWallpaperManager extends WallpaperManager { * but span across all displays. * * @param {string} _filename Unused naming to check - * @returns {boolean} Wether the image is a merged wallpaper + * @returns {boolean} Whether the image is a merged wallpaper */ static isImageMerged(_filename: string): boolean { // This manager can't create merged wallpaper diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 4e3403b3..98ce5f3b 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -79,7 +79,7 @@ class HydraPaper extends ExternalWallpaperManager { * but span across all displays. * * @param {string} filename Naming to check - * @returns {boolean} Wether the image is a merged wallpaper + * @returns {boolean} Whether the image is a merged wallpaper */ static isImageMerged(filename: string): boolean { const mergedWallpaperNames = [ diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts index 71ea3dce..0e474d08 100644 --- a/src/manager/superPaper.ts +++ b/src/manager/superPaper.ts @@ -75,7 +75,7 @@ class Superpaper extends ExternalWallpaperManager { * but span across all displays. * * @param {string} filename Naming to check - * @returns {boolean} Wether the image is a merged wallpaper + * @returns {boolean} Whether the image is a merged wallpaper */ static isImageMerged(filename: string): boolean { const mergedWallpaperNames = [ diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 5df3e518..3b74d3c1 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -235,19 +235,19 @@ false NSFW - Weather not safe images are allowed. + Whether not safe images are allowed. true SFW - Weather safe images are allowed. + Whether safe images are allowed. false Sketchy - Weather sketchy images are allowed. + Whether sketchy images are allowed. @@ -259,19 +259,19 @@ true Category Anime - Weather the anime category should be searched. + Whether the anime category should be searched. true Category General - Weather the general category should be searched. + Whether the general category should be searched. true Category People - Weather the people category should be searched. + Whether the people category should be searched. diff --git a/src/timer.ts b/src/timer.ts index 756543ea..40370bec 100644 --- a/src/timer.ts +++ b/src/timer.ts @@ -61,7 +61,7 @@ class AFTimer { /** * Check if the timer is currently set as enabled. * - * @returns {boolean} Wether the timer is enabled + * @returns {boolean} Whether the timer is enabled */ isEnabled(): boolean { return this._settings.getBoolean('auto-fetch'); @@ -70,7 +70,7 @@ class AFTimer { /** * Check if the timer is currently paused. * - * @returns {boolean} Wether the timer is paused + * @returns {boolean} Whether the timer is paused */ isPaused(): boolean { return this._paused; diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index ed35479c..4d794931 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -105,7 +105,7 @@ const UnsplashSettingsGroup = GObject.registerClass({ * Switch element sensitivity based on a selected combo row entry. * * @param {Adw.ComboRow} comboRow ComboRow with selected entry - * @param {boolean} enable Wether to make the element sensitive + * @param {boolean} enable Whether to make the element sensitive * @param {Gtk.Widget} targetElement The element to target the sensitivity setting */ private _unsplashUnconstrained(comboRow: Adw.ComboRow, enable: boolean, targetElement: Gtk.Widget): void { diff --git a/src/utils.ts b/src/utils.ts index 8160e44e..5aebce84 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -293,7 +293,7 @@ function getWallpaperManager(): WallpaperManager { * but span across all displays. * * @param {string} filename Naming to check - * @returns {boolean} Wether the image is a merged wallpaper + * @returns {boolean} Whether the image is a merged wallpaper */ // This function is here instead of wallpaperManager.js to work around looping import errors function isImageMerged(filename: string): boolean { From f3399b439bd65c0ff5214ae21fd72badc6643d84 Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 5 Jul 2023 14:56:37 +0200 Subject: [PATCH 79/87] [Wallhaven] Add undocumented `ai_art_filter` --- src/adapter/wallhaven.ts | 22 +++++++++++++------ ...ns.space.iflow.randomwallpaper.gschema.xml | 6 +++++ src/ui/wallhaven.blp | 8 +++++++ src/ui/wallhaven.ts | 6 +++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/adapter/wallhaven.ts b/src/adapter/wallhaven.ts index ae1b56d3..fcd9ab50 100644 --- a/src/adapter/wallhaven.ts +++ b/src/adapter/wallhaven.ts @@ -7,16 +7,21 @@ import {BaseAdapter} from './../adapter/baseAdapter.js'; import {HistoryEntry} from './../history.js'; interface QueryOptions { - q: string, - purity: string, - sorting: string, + /** + * Filter AI generated images. + * + * - 0 = Include them in search results + * - 1 = Don't include them in search results + */ + ai_art_filter: string, + + atleast: string, categories: string, - // resolutions: string[], colors: string, - atleast: string, + purity: string, + q: string, ratios: string[], - // order: string, - // topRange: string, + sorting: string, } interface WallhavenSearchResponse { @@ -31,6 +36,7 @@ interface WallhavenSearchResponse { */ class WallhavenAdapter extends BaseAdapter { private _options: QueryOptions = { + ai_art_filter: '1', q: '', purity: '110', // SFW, sketchy sorting: 'random', @@ -225,6 +231,8 @@ class WallhavenAdapter extends BaseAdapter { purity.push(Number(this._settings.getBoolean('allow-nsfw'))); this._options.purity = purity.join(''); + this._options.ai_art_filter = this._settings.getBoolean('ai-art') ? '0' : '1'; + this._options.colors = this._settings.getString('color'); } } diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 3b74d3c1..0c413c50 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -232,6 +232,12 @@ + + false + AI Art + Whether AI generated images should be included. + + false NSFW diff --git a/src/ui/wallhaven.blp b/src/ui/wallhaven.blp index 6309c946..05cbafd0 100644 --- a/src/ui/wallhaven.blp +++ b/src/ui/wallhaven.blp @@ -68,6 +68,14 @@ template $WallhavenSettingsGroup : Adw.PreferencesGroup { } } } + + Adw.ActionRow { + title: _("Allow AI generated images"); + + Switch ai_art { + valign: center; + } + } } Adw.PreferencesGroup { diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index 3e33a592..731620d3 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -16,6 +16,7 @@ const WallhavenSettingsGroup = GObject.registerClass({ GTypeName: 'WallhavenSettingsGroup', Template: GLib.filename_to_uri(`${Self.path}/ui/wallhaven.ui`, null), InternalChildren: [ + 'ai_art', 'allow_nsfw', 'allow_sfw', 'allow_sketchy', @@ -42,6 +43,7 @@ const WallhavenSettingsGroup = GObject.registerClass({ ]; // InternalChildren + private _ai_art!: Gtk.Switch; private _allow_nsfw!: Gtk.Switch; private _allow_sfw!: Gtk.Switch; private _allow_sketchy!: Gtk.Switch; @@ -73,6 +75,10 @@ const WallhavenSettingsGroup = GObject.registerClass({ const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/wallhaven/${id}/`; this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_WALLHAVEN, path); + this._settings.bind('ai-art', + this._ai_art, + 'active', + Gio.SettingsBindFlags.DEFAULT); this._settings.bind('allow-nsfw', this._allow_nsfw, 'active', From ddfedcb0a0132767345ae2b4692de631dff01e23 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 8 Jul 2023 17:07:00 +0200 Subject: [PATCH 80/87] Move mode enum to code --- src/manager/wallpaperManager.ts | 59 ++++++++++++++++++- src/prefs.ts | 19 +++++- src/randomWallpaperMenu.ts | 3 +- ...ns.space.iflow.randomwallpaper.gschema.xml | 11 +--- src/utils.ts | 30 +--------- src/wallpaperController.ts | 12 ++-- 6 files changed, 83 insertions(+), 51 deletions(-) diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts index e4bad543..4df4cb69 100644 --- a/src/manager/wallpaperManager.ts +++ b/src/manager/wallpaperManager.ts @@ -1,12 +1,20 @@ import {Logger} from '../logger.js'; import type {Settings} from './../settings.js'; -const enum Mode { +// Generated code produces a no-shadow rule error +/* eslint-disable */ +enum Mode { + /** Only change the desktop background */ BACKGROUND, + /** Only change the lock screen background */ LOCKSCREEN, + /** Change the desktop and lock screen background to the same image. */ + // This allows for optimizations when processing images. BACKGROUND_AND_LOCKSCREEN, + /** Change each - the desktop and lock screen background - to different images. */ BACKGROUND_AND_LOCKSCREEN_INDEPENDENT, } +/* eslint-enable */ /** * Wallpaper manager is a base class for manager to implement. @@ -44,7 +52,54 @@ abstract class WallpaperManager { protected abstract _setLockScreen(wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise; } +/** + * Retrieve the human readable enum name. + * + * @param {Mode} mode The mode to name + * @returns {string} Name + */ +function _getModeName(mode: Mode): string { + let name: string; + + switch (mode) { + case Mode.BACKGROUND: + name = 'Background'; + break; + case Mode.LOCKSCREEN: + name = 'Lockscreen'; + break; + case Mode.BACKGROUND_AND_LOCKSCREEN: + name = 'Background and lockscreen'; + break; + case Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT: + name = 'Background and lockscreen independently'; + break; + + default: + name = 'Mode name not found'; + break; + } + + return name; +} + +/** + * Get a list of human readable enum entries. + * + * @returns {string[]} Array with key names + */ +function getModeNameList(): string[] { + const list: string[] = []; + + const values = Object.values(Mode).filter(v => !isNaN(Number(v))); + for (const i of values) + list.push(_getModeName(i as Mode)); + + return list; +} + export { WallpaperManager, - Mode + Mode, + getModeNameList }; diff --git a/src/prefs.ts b/src/prefs.ts index d7a27aca..64287979 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -97,8 +97,23 @@ class RandomWallpaperSettings { this._builder.add_from_file(`${Self.path}/ui/pageGeneral.ui`); this._builder.add_from_file(`${Self.path}/ui/pageSources.ui`); - Utils.fillComboRowFromEnum(this._builder.get_object('combo_background_type'), this._settings, 'change-type'); - Utils.fillComboRowFromEnum(this._builder.get_object('log_level'), this._settings, 'log-level'); + import('./manager/wallpaperManager.js').then(module => { + const comboBackgroundType = this._builder.get_object>('combo_background_type'); + comboBackgroundType.model = Gtk.StringList.new(module.getModeNameList()); + comboBackgroundType.selected = this._settings.getInt('change-type'); + comboBackgroundType.connect('notify::selected', (_comboBackgroundType: InstanceType) => { + this._settings.setInt('change-type', _comboBackgroundType.selected); + }); + }).catch(error => { + this._logger.error(error); + }); + + const comboLogLevel = this._builder.get_object>('log_level'); + Utils.fillComboRowFromEnum(comboLogLevel, this._settings); + comboLogLevel.selected = this._settings.getInt('log-level'); + comboLogLevel.connect('notify::selected', (_comboLogLevel: InstanceType) => { + this._settings.setInt('log-level', _comboLogLevel.selected); + }); this._settings.bind('minutes', this._builder.get_object('duration_minutes'), diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index 552fe722..e8a8aedd 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -20,6 +20,7 @@ import * as Utils from './utils.js'; import {Logger} from './logger.js'; import {WallpaperController} from './wallpaperController.js'; +import {Mode} from './manager/wallpaperManager.js'; const Self = ExtensionUtils.getCurrentExtension(); @@ -276,7 +277,7 @@ class RandomWallpaperMenu { this._wallpaperController.setWallpaper(actor.historyEntry.id).then(() => { this._wallpaperController.prohibitNewWallpaper = false; - if (this._settings.getEnum('change-type') === 1 && this._savedBackgroundUri) { + if (this._settings.getInt('change-type') as Mode === Mode.LOCKSCREEN && this._savedBackgroundUri) { // Reset background after previewing the lock screen options this._wallpaperController.resetWallpaper(this._savedBackgroundUri); } else { diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 0c413c50..47325c0c 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -1,13 +1,6 @@ - - - - - - - @@ -64,8 +57,8 @@ A JS timestamp of the last timer callback trigger. Zero if no last change registered. - - "Background" + + 0 Choose what should be changed Allows to choose what backgrounds will be changed. diff --git a/src/utils.ts b/src/utils.ts index 5aebce84..a7ba3e1b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,5 @@ -import Adw from 'gi://Adw'; import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; -import Gtk from 'gi://Gtk'; import type Meta from 'gi://Meta'; import {DefaultWallpaperManager} from './manager/defaultWallpaperManager.js'; @@ -9,7 +7,7 @@ import {HydraPaper} from './manager/hydraPaper.js'; import {Logger} from './logger.js'; import {Superpaper} from './manager/superPaper.js'; import {Settings} from './settings.js'; -import type {WallpaperManager} from './manager/wallpaperManager.js'; +import {type WallpaperManager} from './manager/wallpaperManager.js'; // Generated code produces a no-shadow rule error: // 'SourceType' is already declared in the upper scope on line 7 column 5 no-shadow @@ -115,31 +113,6 @@ function fileName(uri: string): string { return base; } -/** - * Takes a GSettings schema and key to an enum and fills a combo row with it. - * - * @param {Adw.ComboRow} comboRow ComboRow to fill and connect - * @param {Settings} settings Settings schema to scan values for - * @param {string} key Key where to find values in the settings schema - */ -function fillComboRowFromEnum(comboRow: Adw.ComboRow, settings: Settings, key: string): void { - // Fill combo from settings enum - const availableTypes = settings.getSchema().get_key(key).get_range(); // GLib.Variant (sv) - // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) - // s should be 'enum' - // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. - // v is 'as' - const availableTypesNames = availableTypes.get_child_value(1).get_variant().get_strv(); - - const stringList = Gtk.StringList.new(availableTypesNames); - comboRow.model = stringList; - comboRow.selected = settings.getEnum(key); - - comboRow.connect('notify::selected', (_comboRow: Adw.ComboRow) => { - settings.setEnum(key, _comboRow.selected); - }); -} - // https://stackoverflow.com/a/32859917 /** * Compare two strings and return the first char they differentiate. @@ -309,7 +282,6 @@ export { isImageMerged, execCheck, fileName, - fillComboRowFromEnum, findFirstDifference, getMonitorCount, getRandomNumber, diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index b50ec914..6a6ab7e0 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -458,10 +458,10 @@ class WallpaperController { const historyElement = this._historyController.get(historyId); if (historyElement?.id && historyElement.path && this._historyController.promoteToActive(historyElement.id)) { - const changeType = this._settings.getEnum('change-type'); + const changeType = this._settings.getInt('change-type') as Mode; const usedWallpaperPaths = this._fillDisplaysFromHistory([historyElement.path]); - // ignore changeType === 3 because that doesn't make sense + // ignore changeType === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT because that doesn't make sense // when requesting a specific history entry if (changeType > Mode.BACKGROUND_AND_LOCKSCREEN) await this._setBackground(usedWallpaperPaths, Mode.BACKGROUND_AND_LOCKSCREEN); @@ -487,15 +487,11 @@ class WallpaperController { this._startLoadingHooks.forEach(element => element()); try { - // - // - // - // - const changeType = this._settings.getEnum('change-type'); + const changeType = this._settings.getInt('change-type') as Mode; let monitorCount = this._getCurrentDisplayCount(); // Request double the amount of displays if we need background and lock screen - if (changeType === 3) + if (changeType === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT) monitorCount *= 2; const imageAdapters = this._getRandomAdapter(monitorCount); From 2202b361e2d77ea431f8d4ade16028dc7dae4e97 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 8 Jul 2023 17:11:53 +0200 Subject: [PATCH 81/87] Remove obsolete function --- src/manager/defaultWallpaperManager.ts | 11 +++----- src/manager/externalWallpaperManager.ts | 35 +++++++++++-------------- src/manager/hydraPaper.ts | 22 +++++++--------- src/manager/superPaper.ts | 24 +++++++---------- src/manager/wallpaperManager.ts | 28 +++++++++----------- src/wallpaperController.ts | 35 +++++-------------------- 6 files changed, 58 insertions(+), 97 deletions(-) diff --git a/src/manager/defaultWallpaperManager.ts b/src/manager/defaultWallpaperManager.ts index 5b12ed1e..c68bffd3 100644 --- a/src/manager/defaultWallpaperManager.ts +++ b/src/manager/defaultWallpaperManager.ts @@ -16,15 +16,14 @@ class DefaultWallpaperManager extends WallpaperManager { * Sets the background image in light and dark mode. * * @param {string[]} wallpaperPaths Array of strings to image files, expects a single image only. - * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri * @returns {Promise} Only resolves */ - protected async _setBackground(wallpaperPaths: string[], backgroundSettings: Settings): Promise { + protected async _setBackground(wallpaperPaths: string[]): Promise { // The default manager can't handle multiple displays if (wallpaperPaths.length > 1) this._logger.warn('Single handling manager called with multiple images!'); - await DefaultWallpaperManager.setSingleBackground(`file://${wallpaperPaths[0]}`, backgroundSettings); + await DefaultWallpaperManager.setSingleBackground(`file://${wallpaperPaths[0]}`, this._backgroundSettings); return Promise.resolve(); } @@ -33,16 +32,14 @@ class DefaultWallpaperManager extends WallpaperManager { * Sets the lock screen image in light and dark mode. * * @param {string[]} wallpaperPaths Array of strings to image files, expects a single image only. - * @param {Settings} _backgroundSettings Unused settings object - * @param {Settings} screensaverSettings Settings object holding the screensaver picture-uri * @returns {Promise} Only resolves */ - protected async _setLockScreen(wallpaperPaths: string[], _backgroundSettings: Settings, screensaverSettings: Settings): Promise { + protected async _setLockScreen(wallpaperPaths: string[]): Promise { // The default manager can't handle multiple displays if (wallpaperPaths.length > 1) this._logger.warn('Single handling manager called with multiple images!'); - await DefaultWallpaperManager.setSingleLockScreen(`file://${wallpaperPaths[0]}`, _backgroundSettings, screensaverSettings); + await DefaultWallpaperManager.setSingleLockScreen(`file://${wallpaperPaths[0]}`, this._backgroundSettings, this._screensaverSettings); return Promise.resolve(); } diff --git a/src/manager/externalWallpaperManager.ts b/src/manager/externalWallpaperManager.ts index 3a4d58ad..a7c7e99c 100644 --- a/src/manager/externalWallpaperManager.ts +++ b/src/manager/externalWallpaperManager.ts @@ -5,16 +5,17 @@ import * as Utils from '../utils.js'; import {DefaultWallpaperManager} from './defaultWallpaperManager.js'; import {Mode, WallpaperManager} from './wallpaperManager.js'; -import type {Settings} from '../settings.js'; /** * Abstract base class for external manager to implement. */ abstract class ExternalWallpaperManager extends WallpaperManager { - private _cancellable: Gio.Cancellable | null = null; + public canHandleMultipleImages = true; + protected static _command: string[] | null = null; protected abstract readonly _possibleCommands: string[]; - canHandleMultipleImages = true; + + private _cancellable: Gio.Cancellable | null = null; /** * Checks if the current manager is available in the `$PATH`. @@ -40,17 +41,13 @@ abstract class ExternalWallpaperManager extends WallpaperManager { /** * Set the wallpapers for a given mode. * - * Modes: - * - 0: Background - * - 1: Lock screen - * - 2: Background and lock screen - * * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count * @param {Mode} mode Enum indicating what images to change - * @param {Settings} backgroundSettings Settings object containing the background settings - * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings */ - async setWallpaper(wallpaperPaths: string[], mode: Mode, backgroundSettings: Settings, screensaverSettings: Settings): Promise { + async setWallpaper(wallpaperPaths: string[], mode: Mode): Promise { + if (wallpaperPaths.length < 1) + throw new Error('Empty wallpaper array'); + // Cancel already running processes before setting new images this._cancelRunning(); @@ -59,10 +56,10 @@ abstract class ExternalWallpaperManager extends WallpaperManager { const promises = []; if (mode === Mode.BACKGROUND || mode === Mode.BACKGROUND_AND_LOCKSCREEN) - promises.push(DefaultWallpaperManager.setSingleBackground(`file://${wallpaperPaths[0]}`, backgroundSettings)); + promises.push(DefaultWallpaperManager.setSingleBackground(`file://${wallpaperPaths[0]}`, this._backgroundSettings)); if (mode === Mode.LOCKSCREEN || mode === Mode.BACKGROUND_AND_LOCKSCREEN) - promises.push(DefaultWallpaperManager.setSingleLockScreen(`file://${wallpaperPaths[0]}`, backgroundSettings, screensaverSettings)); + promises.push(DefaultWallpaperManager.setSingleLockScreen(`file://${wallpaperPaths[0]}`, this._backgroundSettings, this._screensaverSettings)); await Promise.allSettled(promises); return; @@ -75,13 +72,13 @@ abstract class ExternalWallpaperManager extends WallpaperManager { */ if (mode === Mode.BACKGROUND || mode === Mode.BACKGROUND_AND_LOCKSCREEN) - await this._setBackground(wallpaperPaths, backgroundSettings); + await this._setBackground(wallpaperPaths); if (mode === Mode.LOCKSCREEN) - await this._setLockScreen(wallpaperPaths, backgroundSettings, screensaverSettings); + await this._setLockScreen(wallpaperPaths); if (mode === Mode.BACKGROUND_AND_LOCKSCREEN) - await this._setLockScreenAfterBackground(wallpaperPaths, backgroundSettings, screensaverSettings); + await this._setLockScreenAfterBackground(wallpaperPaths); } /** @@ -125,12 +122,10 @@ abstract class ExternalWallpaperManager extends WallpaperManager { * This function exists to save compute time on identical background and lock screen images. * * @param {string[]} _wallpaperPaths Unused array of strings to image files - * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri - * @param {Settings} screensaverSettings Settings object holding the screensaver picture-uri * @returns {Promise} Only resolves */ - protected _setLockScreenAfterBackground(_wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise { - Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri')); + protected _setLockScreenAfterBackground(_wallpaperPaths: string[]): Promise { + Utils.setPictureUriOfSettingsObject(this._screensaverSettings, this._backgroundSettings.getString('picture-uri')); return Promise.resolve(); } } diff --git a/src/manager/hydraPaper.ts b/src/manager/hydraPaper.ts index 98ce5f3b..5a6d52b4 100644 --- a/src/manager/hydraPaper.ts +++ b/src/manager/hydraPaper.ts @@ -2,7 +2,6 @@ import * as Utils from '../utils.js'; import {ExternalWallpaperManager} from './externalWallpaperManager.js'; import {Logger} from '../logger.js'; -import {Settings} from '../settings.js'; /** * Wrapper for HydraPaper using it as a manager. @@ -15,36 +14,33 @@ class HydraPaper extends ExternalWallpaperManager { * Sets the background image in light and dark mode. * * @param {string[]} wallpaperPaths Array of strings to image files - * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri */ - protected async _setBackground(wallpaperPaths: string[], backgroundSettings: Settings): Promise { + protected async _setBackground(wallpaperPaths: string[]): Promise { await this._createCommandAndRun(wallpaperPaths); // Manually set key for darkmode because that's way faster than merging two times the same images - Utils.setPictureUriOfSettingsObject(backgroundSettings, backgroundSettings.getString('picture-uri')); + Utils.setPictureUriOfSettingsObject(this._backgroundSettings, this._backgroundSettings.getString('picture-uri')); } /** * Sets the lock screen image in light and dark mode. * * @param {string[]} wallpaperPaths Array of strings to image files - * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri - * @param {Settings} screensaverSettings Settings object holding the screensaver picture-uri */ - protected async _setLockScreen(wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise { + protected async _setLockScreen(wallpaperPaths: string[]): Promise { // Remember keys, HydraPaper will change these - const tmpBackground = backgroundSettings.getString('picture-uri-dark'); - const tmpMode = backgroundSettings.getString('picture-options'); + const tmpBackground = this._backgroundSettings.getString('picture-uri-dark'); + const tmpMode = this._backgroundSettings.getString('picture-options'); // Force HydraPaper to target a different resulting image by using darkmode await this._createCommandAndRun(wallpaperPaths, true); - screensaverSettings.setString('picture-options', 'spanned'); - Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); + this._screensaverSettings.setString('picture-options', 'spanned'); + Utils.setPictureUriOfSettingsObject(this._screensaverSettings, this._backgroundSettings.getString('picture-uri-dark')); // HydraPaper possibly changed these, change them back - backgroundSettings.setString('picture-uri-dark', tmpBackground); - backgroundSettings.setString('picture-options', tmpMode); + this._backgroundSettings.setString('picture-uri-dark', tmpBackground); + this._backgroundSettings.setString('picture-options', tmpMode); } /** diff --git a/src/manager/superPaper.ts b/src/manager/superPaper.ts index 0e474d08..d43e9435 100644 --- a/src/manager/superPaper.ts +++ b/src/manager/superPaper.ts @@ -2,7 +2,6 @@ import * as Utils from '../utils.js'; import {ExternalWallpaperManager} from './externalWallpaperManager.js'; import {Logger} from './../logger.js'; -import {Settings} from './../settings.js'; /** * Wrapper for Superpaper using it as a manager. @@ -15,10 +14,9 @@ class Superpaper extends ExternalWallpaperManager { * Sets the background image in light and dark mode. * * @param {string[]} wallpaperPaths Array of strings to image files - * @param {Settings} _backgroundSettings Unused settings object */ // We don't need the settings object because Superpaper already set both picture-uri on it's own. - protected async _setBackground(wallpaperPaths: string[], _backgroundSettings: Settings): Promise { + protected async _setBackground(wallpaperPaths: string[]): Promise { await this._createCommandAndRun(wallpaperPaths); } @@ -26,24 +24,22 @@ class Superpaper extends ExternalWallpaperManager { * Sets the lock screen image in light and dark mode. * * @param {string[]} wallpaperPaths Array of strings to image files - * @param {Settings} backgroundSettings Settings object holding the desktop background picture-uri - * @param {Settings} screensaverSettings Settings object holding the screensaver picture-uri */ - protected async _setLockScreen(wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise { + protected async _setLockScreen(wallpaperPaths: string[]): Promise { // Remember keys, Superpaper will change these - const tmpBackground = backgroundSettings.getString('picture-uri'); - const tmpBackgroundDark = backgroundSettings.getString('picture-uri-dark'); - const tmpMode = backgroundSettings.getString('picture-options'); + const tmpBackground = this._backgroundSettings.getString('picture-uri'); + const tmpBackgroundDark = this._backgroundSettings.getString('picture-uri-dark'); + const tmpMode = this._backgroundSettings.getString('picture-options'); await this._createCommandAndRun(wallpaperPaths); - screensaverSettings.setString('picture-options', 'spanned'); - Utils.setPictureUriOfSettingsObject(screensaverSettings, backgroundSettings.getString('picture-uri-dark')); + this._screensaverSettings.setString('picture-options', 'spanned'); + Utils.setPictureUriOfSettingsObject(this._screensaverSettings, this._backgroundSettings.getString('picture-uri-dark')); // Superpaper possibly changed these, change them back - backgroundSettings.setString('picture-uri', tmpBackground); - backgroundSettings.setString('picture-uri-dark', tmpBackgroundDark); - backgroundSettings.setString('picture-options', tmpMode); + this._backgroundSettings.setString('picture-uri', tmpBackground); + this._backgroundSettings.setString('picture-uri-dark', tmpBackgroundDark); + this._backgroundSettings.setString('picture-options', tmpMode); } // https://github.com/hhannine/superpaper/blob/master/docs/cli-usage.md diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts index 4df4cb69..566063f8 100644 --- a/src/manager/wallpaperManager.ts +++ b/src/manager/wallpaperManager.ts @@ -1,5 +1,5 @@ import {Logger} from '../logger.js'; -import type {Settings} from './../settings.js'; +import {Settings} from './../settings.js'; // Generated code produces a no-shadow rule error /* eslint-disable */ @@ -20,36 +20,34 @@ enum Mode { * Wallpaper manager is a base class for manager to implement. */ abstract class WallpaperManager { + public canHandleMultipleImages = false; + protected abstract _logger: Logger; - canHandleMultipleImages = false; + protected _backgroundSettings = new Settings('org.gnome.desktop.background'); + protected _screensaverSettings = new Settings('org.gnome.desktop.screensaver'); /** * Set the wallpapers for a given mode. * - * Modes: - * - 0: Background - * - 1: Lock screen - * - 2: Background and lock screen - * * @param {string[]} wallpaperPaths Array of paths to the desired wallpapers, should match the display count * @param {Mode} mode Enum indicating what images to change - * @param {Settings} backgroundSettings Settings object containing the background settings - * @param {Settings} screensaverSettings Settings object containing the screensaver/lockscreen settings */ - async setWallpaper(wallpaperPaths: string[], mode: Mode, backgroundSettings: Settings, screensaverSettings: Settings): Promise { - const promises = []; + async setWallpaper(wallpaperPaths: string[], mode: Mode = Mode.BACKGROUND): Promise { + if (wallpaperPaths.length < 1) + throw new Error('Empty wallpaper array'); + const promises = []; if (mode === Mode.BACKGROUND || mode === Mode.BACKGROUND_AND_LOCKSCREEN) - promises.push(this._setBackground(wallpaperPaths, backgroundSettings)); + promises.push(this._setBackground(wallpaperPaths)); if (mode === Mode.LOCKSCREEN || mode === Mode.BACKGROUND_AND_LOCKSCREEN) - promises.push(this._setLockScreen(wallpaperPaths, backgroundSettings, screensaverSettings)); + promises.push(this._setLockScreen(wallpaperPaths)); await Promise.allSettled(promises); } - protected abstract _setBackground(wallpaperPaths: string[], backgroundSettings: Settings): Promise; - protected abstract _setLockScreen(wallpaperPaths: string[], backgroundSettings: Settings, screensaverSettings: Settings): Promise; + protected abstract _setBackground(wallpaperPaths: string[]): Promise; + protected abstract _setLockScreen(wallpaperPaths: string[]): Promise; } /** diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 6a6ab7e0..77d25eee 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -380,27 +380,6 @@ class WallpaperController { return sourceResult; } - /** - * Sets the wallpaper and the lock screen when enabled to the given path. - * - * Modes: - * 0: Background - * 1: Lock screen - * 2: Background and lock screen - * - * @param {string[]} wallpaperPaths Array of paths to the image - * @param {Mode} mode Types to change - */ - private async _setBackground(wallpaperPaths: string[], mode: Mode = Mode.BACKGROUND): Promise { - const backgroundSettings = new SettingsModule.Settings('org.gnome.desktop.background'); - const screensaverSettings = new SettingsModule.Settings('org.gnome.desktop.screensaver'); - - if (wallpaperPaths.length < 1) - throw new Error('Empty wallpaper array'); - - await this._wallpaperManager.setWallpaper(wallpaperPaths, mode, backgroundSettings, screensaverSettings); - } - /** * Run a configured post command. */ @@ -464,9 +443,9 @@ class WallpaperController { // ignore changeType === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT because that doesn't make sense // when requesting a specific history entry if (changeType > Mode.BACKGROUND_AND_LOCKSCREEN) - await this._setBackground(usedWallpaperPaths, Mode.BACKGROUND_AND_LOCKSCREEN); + await this._wallpaperManager.setWallpaper(usedWallpaperPaths, Mode.BACKGROUND_AND_LOCKSCREEN); else - await this._setBackground(usedWallpaperPaths, changeType); + await this._wallpaperManager.setWallpaper(usedWallpaperPaths, changeType); this._runPostCommands(); usedWallpaperPaths.reverse().forEach(path => { @@ -557,11 +536,11 @@ class WallpaperController { if (changeType === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT) { // Half the images for the background - await this._setBackground(usedWallpaperPaths.slice(0, monitorCount / 2), Mode.BACKGROUND); + await this._wallpaperManager.setWallpaper(usedWallpaperPaths.slice(0, monitorCount / 2), Mode.BACKGROUND); // Half the images for the lock screen - await this._setBackground(usedWallpaperPaths.slice(monitorCount / 2), Mode.LOCKSCREEN); + await this._wallpaperManager.setWallpaper(usedWallpaperPaths.slice(monitorCount / 2), Mode.LOCKSCREEN); } else { - await this._setBackground(usedWallpaperPaths, changeType); + await this._wallpaperManager.setWallpaper(usedWallpaperPaths, changeType); } usedWallpaperPaths.reverse().forEach(path => { @@ -658,12 +637,12 @@ class WallpaperController { // Only change the background - the lock screen wouldn't be visible anyway // because this function is only used for hover preview if (this._resetWallpaper) { - this._setBackground(paths, Mode.BACKGROUND).catch(error => { + this._wallpaperManager.setWallpaper(paths).catch(error => { this._logger.error(error); }); this._resetWallpaper = false; } else if (this._previewId !== undefined) { - this._setBackground(paths, Mode.BACKGROUND).catch(error => { + this._wallpaperManager.setWallpaper(paths).catch(error => { this._logger.error(error); }); } From 238c87fd69f3e55c27e2e5a397894f3c53fcc28e Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 8 Jul 2023 17:23:44 +0200 Subject: [PATCH 82/87] Remove log level enum from gsettings --- src/logger.ts | 61 ++++++++++++++++++- src/prefs.ts | 18 +++--- ...ns.space.iflow.randomwallpaper.gschema.xml | 12 +--- 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index 13f95989..d2755cfb 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -2,13 +2,16 @@ import {Settings} from './settings.js'; -const enum LogLevel { +// Generated code produces a no-shadow rule error +/* eslint-disable */ +enum LogLevel { SILENT, ERROR, WARNING, INFO, DEBUG, } +/* eslint-enable */ type LogLevelStrings = keyof typeof LogLevel; /** @@ -57,7 +60,7 @@ class Logger { * @returns {LogLevel} Log level */ private _selectedLogLevel(): LogLevel { - return this._settings.getEnum('log-level'); + return this._settings.getInt('log-level') as LogLevel; } /** @@ -109,4 +112,56 @@ class Logger { } } -export {Logger}; +/** + * Retrieve the human readable enum name. + * + * @param {LogLevel} level The mode to name + * @returns {string} Name + */ +function _getLogLevelName(level: LogLevel): string { + let name: string; + + switch (level) { + case LogLevel.SILENT: + name = 'Silent'; + break; + case LogLevel.ERROR: + name = 'Error'; + break; + case LogLevel.WARNING: + name = 'Warning'; + break; + case LogLevel.INFO: + name = 'Info'; + break; + case LogLevel.DEBUG: + name = 'Debug'; + break; + + default: + name = 'LogLevel name not found'; + break; + } + + return name; +} + +/** + * Get a list of human readable enum entries. + * + * @returns {string[]} Array with key names + */ +function getLogLevelNameList(): string[] { + const list: string[] = []; + + const values = Object.values(LogLevel).filter(v => !isNaN(Number(v))); + for (const i of values) + list.push(_getLogLevelName(i as LogLevel)); + + return list; +} + +export { + Logger, + getLogLevelNameList +}; diff --git a/src/prefs.ts b/src/prefs.ts index 64287979..1f9fde14 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -100,20 +100,20 @@ class RandomWallpaperSettings { import('./manager/wallpaperManager.js').then(module => { const comboBackgroundType = this._builder.get_object>('combo_background_type'); comboBackgroundType.model = Gtk.StringList.new(module.getModeNameList()); - comboBackgroundType.selected = this._settings.getInt('change-type'); - comboBackgroundType.connect('notify::selected', (_comboBackgroundType: InstanceType) => { - this._settings.setInt('change-type', _comboBackgroundType.selected); - }); + this._settings.bind('change-type', + comboBackgroundType, + 'selected', + Gio.SettingsBindFlags.DEFAULT); }).catch(error => { this._logger.error(error); }); const comboLogLevel = this._builder.get_object>('log_level'); - Utils.fillComboRowFromEnum(comboLogLevel, this._settings); - comboLogLevel.selected = this._settings.getInt('log-level'); - comboLogLevel.connect('notify::selected', (_comboLogLevel: InstanceType) => { - this._settings.setInt('log-level', _comboLogLevel.selected); - }); + comboLogLevel.model = Gtk.StringList.new(Logger.getLogLevelNameList()); + this._settings.bind('log-level', + comboLogLevel, + 'selected', + Gio.SettingsBindFlags.DEFAULT); this._settings.bind('minutes', this._builder.get_object('duration_minutes'), diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 47325c0c..066dd1cb 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -1,14 +1,6 @@ - - - - - - - - @@ -63,8 +55,8 @@ Allows to choose what backgrounds will be changed. - - "Warning" + + 2 Tier of logs Choose what minimal tier of logs should appear in the journal. From 0b0a69eb4dce383136f2acb829c7bb5e99f03001 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 8 Jul 2023 17:39:48 +0200 Subject: [PATCH 83/87] Remove constraint-type enum from gsettings --- src/adapter/unsplash.ts | 64 ++++++++++++++++++- ...ns.space.iflow.randomwallpaper.gschema.xml | 11 +--- src/ui/unsplash.ts | 40 +++++------- 3 files changed, 79 insertions(+), 36 deletions(-) diff --git a/src/adapter/unsplash.ts b/src/adapter/unsplash.ts index 9ccab5d8..d6e99bd4 100644 --- a/src/adapter/unsplash.ts +++ b/src/adapter/unsplash.ts @@ -7,6 +7,16 @@ import {HistoryEntry} from './../history.js'; /** How many times the service should be queried at maximum. */ const MAX_SERVICE_RETRIES = 5; +// Generated code produces a no-shadow rule error +/* eslint-disable */ +enum ConstraintType { + UNCONSTRAINED, + USER, + USERS_LIKES, + COLLECTION_ID, +} +/* eslint-enable */ + /** * Adapter for image sources using Unsplash. */ @@ -166,7 +176,7 @@ class UnsplashAdapter extends BaseAdapter { this._options.w = this._settings.getInt('image-width'); this._options.h = this._settings.getInt('image-height'); - this._options.constraintType = this._settings.getEnum('constraint-type'); + this._options.constraintType = this._settings.getInt('constraint-type'); this._options.constraintValue = this._settings.getString('constraint-value'); const keywords = this._settings.getString('keyword').split(','); @@ -179,4 +189,54 @@ class UnsplashAdapter extends BaseAdapter { } } -export {UnsplashAdapter}; +/** + * Retrieve the human readable enum name. + * + * @param {ConstraintType} type The type to name + * @returns {string} Name + */ +function _getConstraintTypeName(type: ConstraintType): string { + let name: string; + + switch (type) { + case ConstraintType.UNCONSTRAINED: + name = 'Unconstrained'; + break; + case ConstraintType.USER: + name = 'User'; + break; + case ConstraintType.USERS_LIKES: + name = 'User\'s Likes'; + break; + case ConstraintType.COLLECTION_ID: + name = 'Collection ID'; + break; + + default: + name = 'Constraint type name not found'; + break; + } + + return name; +} + +/** + * Get a list of human readable enum entries. + * + * @returns {string[]} Array with key names + */ +function getConstraintTypeNameList(): string[] { + const list: string[] = []; + + const values = Object.values(ConstraintType).filter(v => !isNaN(Number(v))); + for (const i of values) + list.push(_getConstraintTypeName(i as ConstraintType)); + + return list; +} + +export { + UnsplashAdapter, + ConstraintType, + getConstraintTypeNameList +}; diff --git a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml index 066dd1cb..62535fde 100644 --- a/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.space.iflow.randomwallpaper.gschema.xml @@ -156,13 +156,6 @@ - - - - - - - @@ -201,8 +194,8 @@ This results in a smaller wallpaper pool but the images are considered to have higher quality. - - 'Unconstraint' + + 0 Constraint Type The constraint of the Unsplash Source API. diff --git a/src/ui/unsplash.ts b/src/ui/unsplash.ts index 4d794931..b371dbe9 100644 --- a/src/ui/unsplash.ts +++ b/src/ui/unsplash.ts @@ -8,6 +8,7 @@ import Gtk from 'gi://Gtk'; const ExtensionUtils = imports.misc.extensionUtils; import * as Settings from './../settings.js'; +import {getConstraintTypeNameList} from '../adapter/unsplash.js'; const Self = ExtensionUtils.getCurrentExtension(); @@ -50,26 +51,23 @@ const UnsplashSettingsGroup = GObject.registerClass({ const path = `${Settings.RWG_SETTINGS_SCHEMA_PATH}/sources/unsplash/${id}/`; this._settings = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_SOURCES_UNSPLASH, path); - if (!UnsplashSettingsGroup._stringList) { - // Fill combo from settings enum - - const availableTypes = this._settings.getSchema().get_key('constraint-type').get_range(); // GLib.Variant (sv) - // (sv) = Tuple(%G_VARIANT_TYPE_STRING, %G_VARIANT_TYPE_VARIANT) - // s should be 'enum' - // v should be an array enumerating the possible values. Each item in the array is a possible valid value and no other values are valid. - // v is 'as' - const availableTypeNames = availableTypes.get_child_value(1).get_variant().get_strv(); - - UnsplashSettingsGroup._stringList = Gtk.StringList.new(availableTypeNames); - } + if (!UnsplashSettingsGroup._stringList) + UnsplashSettingsGroup._stringList = Gtk.StringList.new(getConstraintTypeNameList()); this._constraint_type.model = UnsplashSettingsGroup._stringList; - this._constraint_type.selected = this._settings.getEnum('constraint-type'); - this._settings.bind('keyword', - this._keyword, + this._settings.bind('constraint-type', + this._constraint_type, + 'selected', + Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('constraint-value', + this._constraint_value, 'text', Gio.SettingsBindFlags.DEFAULT); + this._settings.bind('featured-only', + this._featured_only, + 'active', + Gio.SettingsBindFlags.DEFAULT); this._settings.bind('image-width', this._image_width, 'value', @@ -78,15 +76,8 @@ const UnsplashSettingsGroup = GObject.registerClass({ this._image_height, 'value', Gio.SettingsBindFlags.DEFAULT); - this._settings.bind('featured-only', - this._featured_only, - 'active', - Gio.SettingsBindFlags.DEFAULT); - - // Binding an enum isn't possible straight away. - // This would need bind_with_mapping() which isn't available in gjs? - this._settings.bind('constraint-value', - this._constraint_value, + this._settings.bind('keyword', + this._keyword, 'text', Gio.SettingsBindFlags.DEFAULT); @@ -95,7 +86,6 @@ const UnsplashSettingsGroup = GObject.registerClass({ this._constraint_type.connect('notify::selected', (comboRow: Adw.ComboRow) => { this._unsplashUnconstrained(comboRow, true, this._featured_only); this._unsplashUnconstrained(comboRow, false, this._constraint_value); - this._settings.setEnum('constraint-type', comboRow.selected); this._featured_only.set_active(false); }); From 50f65d87e7c8bf55832fcb0592e118dc28b2595b Mon Sep 17 00:00:00 2001 From: Lucki Date: Sat, 8 Jul 2023 19:08:43 +0200 Subject: [PATCH 84/87] Let the manager handle all states --- src/manager/externalWallpaperManager.ts | 17 ++++++++++++++++- src/manager/wallpaperManager.ts | 10 ++++++++++ src/wallpaperController.ts | 9 +-------- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/manager/externalWallpaperManager.ts b/src/manager/externalWallpaperManager.ts index a7c7e99c..94db416f 100644 --- a/src/manager/externalWallpaperManager.ts +++ b/src/manager/externalWallpaperManager.ts @@ -52,7 +52,7 @@ abstract class ExternalWallpaperManager extends WallpaperManager { this._cancelRunning(); // Fallback to default manager, all currently supported external manager don't support setting single images - if (wallpaperPaths.length === 1) { + if (wallpaperPaths.length === 1 || (mode === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT && wallpaperPaths.length === 2)) { const promises = []; if (mode === Mode.BACKGROUND || mode === Mode.BACKGROUND_AND_LOCKSCREEN) @@ -61,6 +61,16 @@ abstract class ExternalWallpaperManager extends WallpaperManager { if (mode === Mode.LOCKSCREEN || mode === Mode.BACKGROUND_AND_LOCKSCREEN) promises.push(DefaultWallpaperManager.setSingleLockScreen(`file://${wallpaperPaths[0]}`, this._backgroundSettings, this._screensaverSettings)); + if (mode === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT) { + if (wallpaperPaths.length < 2) + throw new Error('Not enough wallpaper'); + + // Half the images for the background + promises.push(DefaultWallpaperManager.setSingleBackground(`file://${wallpaperPaths[0]}`, this._backgroundSettings)); + // Half the images for the lock screen + promises.push(DefaultWallpaperManager.setSingleLockScreen(`file://${wallpaperPaths[1]}`, this._backgroundSettings, this._screensaverSettings)); + } + await Promise.allSettled(promises); return; } @@ -79,6 +89,11 @@ abstract class ExternalWallpaperManager extends WallpaperManager { if (mode === Mode.BACKGROUND_AND_LOCKSCREEN) await this._setLockScreenAfterBackground(wallpaperPaths); + + if (mode === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT) { + await this._setBackground(wallpaperPaths.slice(0, wallpaperPaths.length / 2)); + await this._setLockScreen(wallpaperPaths.slice(wallpaperPaths.length / 2)); + } } /** diff --git a/src/manager/wallpaperManager.ts b/src/manager/wallpaperManager.ts index 566063f8..5e06dfb6 100644 --- a/src/manager/wallpaperManager.ts +++ b/src/manager/wallpaperManager.ts @@ -43,6 +43,16 @@ abstract class WallpaperManager { if (mode === Mode.LOCKSCREEN || mode === Mode.BACKGROUND_AND_LOCKSCREEN) promises.push(this._setLockScreen(wallpaperPaths)); + if (mode === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT) { + if (wallpaperPaths.length < 2) + throw new Error('Not enough wallpaper'); + + // Half the images for the background + promises.push(this._setBackground(wallpaperPaths.slice(0, wallpaperPaths.length / 2))); + // Half the images for the lock screen + promises.push(this._setLockScreen(wallpaperPaths.slice(wallpaperPaths.length / 2))); + } + await Promise.allSettled(promises); } diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index 77d25eee..f540513d 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -534,14 +534,7 @@ class WallpaperController { const usedWallpaperPaths = this._fillDisplaysFromHistory(newWallpaperPaths, monitorCount); - if (changeType === Mode.BACKGROUND_AND_LOCKSCREEN_INDEPENDENT) { - // Half the images for the background - await this._wallpaperManager.setWallpaper(usedWallpaperPaths.slice(0, monitorCount / 2), Mode.BACKGROUND); - // Half the images for the lock screen - await this._wallpaperManager.setWallpaper(usedWallpaperPaths.slice(monitorCount / 2), Mode.LOCKSCREEN); - } else { - await this._wallpaperManager.setWallpaper(usedWallpaperPaths, changeType); - } + await this._wallpaperManager.setWallpaper(usedWallpaperPaths, changeType); usedWallpaperPaths.reverse().forEach(path => { const id = this._historyController.getEntryByPath(path)?.id; From c90d85956529fbb55305bb49d154cb2dde25a806 Mon Sep 17 00:00:00 2001 From: Lucki Date: Wed, 12 Jul 2023 17:34:47 +0200 Subject: [PATCH 85/87] Stop observing when disabling the extension When pausing the timer, locking the screen and unlocking again, I got two new wallpaper in a short amount of time the next time the timer runs. The observer were still active and unpausing activated all still observing timer. --- src/extension.ts | 3 +++ src/wallpaperController.ts | 35 +++++++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 0bbaf168..f6ae12ee 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -108,6 +108,9 @@ class Extension { if (Timer) Timer.AFTimer.destroy(); + if (this._wallpaperController) + this._wallpaperController.cleanup(); + this._timer = null; this._logger = null; this._panelMenu = null; diff --git a/src/wallpaperController.ts b/src/wallpaperController.ts index f540513d..e3d610cf 100644 --- a/src/wallpaperController.ts +++ b/src/wallpaperController.ts @@ -54,6 +54,8 @@ class WallpaperController { private _startLoadingHooks: (() => void)[] = []; /** functions will be called when loading a new wallpaper stopped. */ private _stopLoadingHooks: (() => void)[] = []; + private _observedValues: number[] = []; + private _observedBackgroundValues: number[] = []; /** * Create a new controller. @@ -85,24 +87,24 @@ class WallpaperController { this._backendConnection.setBoolean('request-new-wallpaper', false); // Track value changes - this._backendConnection.observe('clear-history', () => this._clearHistory()); - this._backendConnection.observe('open-folder', () => this._openFolder()); - this._backendConnection.observe('pause-timer', () => this._pauseTimer()); - this._backendConnection.observe('request-new-wallpaper', () => this._requestNewWallpaper().catch(error => { + this._observedBackgroundValues.push(this._backendConnection.observe('clear-history', () => this._clearHistory())); + this._observedBackgroundValues.push(this._backendConnection.observe('open-folder', () => this._openFolder())); + this._observedBackgroundValues.push(this._backendConnection.observe('pause-timer', () => this._pauseTimer())); + this._observedBackgroundValues.push(this._backendConnection.observe('request-new-wallpaper', () => this._requestNewWallpaper().catch(error => { this._logger.error(error); - })); + }))); - this._settings.observe('history-length', () => this._updateHistory()); - this._settings.observe('auto-fetch', () => this._updateAutoFetching()); - this._settings.observe('minutes', () => this._updateAutoFetching()); - this._settings.observe('hours', () => this._updateAutoFetching()); + this._observedValues.push(this._settings.observe('history-length', () => this._updateHistory())); + this._observedValues.push(this._settings.observe('auto-fetch', () => this._updateAutoFetching())); + this._observedValues.push(this._settings.observe('minutes', () => this._updateAutoFetching())); + this._observedValues.push(this._settings.observe('hours', () => this._updateAutoFetching())); /** * When the user installs a manager we won't notice that it's available. * The preference window however checks on startup for availability and will allow this setting * to change. Let's listen for that change and update our manager accordingly. */ - this._settings.observe('multiple-displays', () => this._updateWallpaperManager()); + this._observedValues.push(this._settings.observe('multiple-displays', () => this._updateWallpaperManager())); this._updateHistory(); @@ -150,6 +152,19 @@ class WallpaperController { } } + /** + * Clean up extension remnants. + */ + cleanup(): void { + for (const observedValue of this._observedValues) + this._settings.disconnect(observedValue); + this._observedValues = []; + + for (const observedValue of this._observedBackgroundValues) + this._backendConnection.disconnect(observedValue); + this._observedBackgroundValues = []; + } + /** * Empty the history. (Background settings observer edition) */ From 23ffa7cb13be13c34822953807f077933ed189b0 Mon Sep 17 00:00:00 2001 From: Lucki Date: Fri, 14 Jul 2023 17:57:16 +0200 Subject: [PATCH 86/87] Also "unobserve" in the menu --- src/randomWallpaperMenu.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/randomWallpaperMenu.ts b/src/randomWallpaperMenu.ts index e8a8aedd..12d2492b 100644 --- a/src/randomWallpaperMenu.ts +++ b/src/randomWallpaperMenu.ts @@ -32,9 +32,10 @@ class RandomWallpaperMenu { private _backendConnection = new Settings.Settings(Settings.RWG_SETTINGS_SCHEMA_BACKEND_CONNECTION); private _savedBackgroundUri: string | null = null; private _settings = new Settings.Settings(); + private _observedValues: number[] = []; + private _observedBackgroundValues: number[] = []; private _currentBackgroundSection; - private _hidePanelIconHandler; private _historySection; private _panelMenu; private _wallpaperController; @@ -52,7 +53,7 @@ class RandomWallpaperMenu { // PanelMenu Icon const statusIcon = new CustomElements.StatusElement(); this._panelMenu.add_child(statusIcon.icon); - this._hidePanelIconHandler = this._settings.observe('hide-panel-icon', this.updatePanelMenuVisibility.bind(this)); + this._observedValues.push(this._settings.observe('hide-panel-icon', this.updatePanelMenuVisibility.bind(this))); // new wallpaper button const newWallpaperItem = new CustomElements.NewWallpaperElement({}); @@ -83,13 +84,13 @@ class RandomWallpaperMenu { this._backendConnection.setBoolean('pause-timer', state); }); - this._settings.observe('auto-fetch', () => { + this._observedValues.push(this._settings.observe('auto-fetch', () => { pauseTimerItem.sensitive = this._settings.getBoolean('auto-fetch'); - }); + })); - this._backendConnection.observe('pause-timer', () => { + this._observedBackgroundValues.push(this._backendConnection.observe('pause-timer', () => { pauseTimerItem.setToggleState(this._backendConnection.getBoolean('pause-timer')); - }); + })); this._panelMenu.menu.addMenuItem(pauseTimerItem); @@ -176,7 +177,7 @@ class RandomWallpaperMenu { // this._wallpaperController.resetWallpaper(this._savedBackgroundUri); // }); - this._settings.observe('history', this.setHistoryList.bind(this)); + this._observedValues.push(this._settings.observe('history', this.setHistoryList.bind(this))); } /** @@ -198,8 +199,13 @@ class RandomWallpaperMenu { this._panelMenu.destroy(); // remove all signal handlers - if (this._hidePanelIconHandler !== null) - this._settings.disconnect(this._hidePanelIconHandler); + for (const observedValue of this._observedValues) + this._settings.disconnect(observedValue); + this._observedValues = []; + + for (const observedValue of this._observedBackgroundValues) + this._backendConnection.disconnect(observedValue); + this._observedBackgroundValues = []; } /** From b4c31d71e4a40b7e1f821d3e5f3c257a033bfb50 Mon Sep 17 00:00:00 2001 From: Lucki Date: Sun, 13 Aug 2023 01:54:21 +0200 Subject: [PATCH 87/87] Pin dependencies to current latest version --- build.sh | 2 + package-lock.json | 747 +++++++++++++++++-------------------- package.json | 20 +- src/adapter/localFolder.ts | 3 +- src/historyMenuElements.ts | 12 +- src/jsonPath.ts | 2 +- src/prefs.ts | 2 + src/settings.ts | 4 +- src/soupBowl.ts | 3 + src/ui/localFolder.ts | 2 + src/ui/wallhaven.ts | 2 + src/utils.ts | 2 +- types/ui/popupMenu.d.ts | 2 +- 13 files changed, 376 insertions(+), 427 deletions(-) diff --git a/build.sh b/build.sh index cde0a00d..9ea30c86 100755 --- a/build.sh +++ b/build.sh @@ -80,6 +80,8 @@ format_js() { sed -i -E "s#@typescript-eslint/no-unsafe-argument##g" "$file" sed -i -E "s#@typescript-eslint/no-unsafe-member-access##g" "$file" sed -i -E "s#@typescript-eslint/no-unsafe-call##g" "$file" + sed -i -E "s#@typescript-eslint/ban-ts-comment##g" "$file" + sed -i -E "s#@typescript-eslint/no-unsafe-enum-comparison##g" "$file" done # Format js using the official gjs stylesheet and a few manual quirks diff --git a/package-lock.json b/package-lock.json index 7bb659fa..fc005308 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,25 +5,34 @@ "packages": { "": { "devDependencies": { - "@gi-types/adw1": "latest", - "@gi-types/base-types": "latest", - "@gi-types/gjs-environment": "latest", - "@gi-types/gtk4-types": "latest", - "@gi-types/shell": "latest", - "@typescript-eslint/eslint-plugin": "latest", - "@typescript-eslint/parser": "latest", - "eslint": "latest", - "eslint-plugin-jsdoc": "latest", - "typescript": "latest" + "@gi-types/adw1": "^1.1.1", + "@gi-types/base-types": "^1.0.0", + "@gi-types/gjs-environment": "^1.1.0", + "@gi-types/gtk4-types": "^1.0.0", + "@gi-types/shell": "^0.1.6", + "@typescript-eslint/eslint-plugin": "^6.3.0", + "@typescript-eslint/parser": "^6.3.0", + "eslint": "^8.47.0", + "eslint-plugin-jsdoc": "^46.4.6", + "typescript": "^5.1.6" + } + }, + "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/@es-joy/jsdoccomment": { - "version": "0.39.4", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", - "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz", + "integrity": "sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==", "dev": true, "dependencies": { - "comment-parser": "1.3.1", + "comment-parser": "1.4.0", "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" }, @@ -47,23 +56,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -79,9 +88,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", - "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -986,32 +995,34 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz", - "integrity": "sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.3.0.tgz", + "integrity": "sha512-IZYjYZ0ifGSLZbwMqIip/nOamFiWJ9AH+T/GYNZBWkVcyNQOFGtSMoWV7RvY4poYCMZ/4lHzNl796WOSNxmk8A==", "dev": true, "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.8", - "@typescript-eslint/type-utils": "5.59.8", - "@typescript-eslint/utils": "5.59.8", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.3.0", + "@typescript-eslint/type-utils": "6.3.0", + "@typescript-eslint/utils": "6.3.0", + "@typescript-eslint/visitor-keys": "6.3.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.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" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1020,25 +1031,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.8.tgz", - "integrity": "sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.3.0.tgz", + "integrity": "sha512-ibP+y2Gr6p0qsUkhs7InMdXrwldjxZw66wpcQq9/PzAroM45wdwyu81T+7RibNCh8oc0AgrsyCwJByncY0Ongg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.59.8", - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/typescript-estree": "5.59.8", + "@typescript-eslint/scope-manager": "6.3.0", + "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/typescript-estree": "6.3.0", + "@typescript-eslint/visitor-keys": "6.3.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1047,16 +1059,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz", - "integrity": "sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.3.0.tgz", + "integrity": "sha512-WlNFgBEuGu74ahrXzgefiz/QlVb+qg8KDTpknKwR7hMH+lQygWyx0CQFoUmMn1zDkQjTBBIn75IxtWss77iBIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/visitor-keys": "5.59.8" + "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/visitor-keys": "6.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1064,25 +1076,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz", - "integrity": "sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.3.0.tgz", + "integrity": "sha512-7Oj+1ox1T2Yc8PKpBvOKWhoI/4rWFd1j7FA/rPE0lbBPXTKjdbtC+7Ev0SeBjEKkIhKWVeZSP+mR7y1Db1CdfQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.8", - "@typescript-eslint/utils": "5.59.8", + "@typescript-eslint/typescript-estree": "6.3.0", + "@typescript-eslint/utils": "6.3.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1091,12 +1103,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.8.tgz", - "integrity": "sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.3.0.tgz", + "integrity": "sha512-K6TZOvfVyc7MO9j60MkRNWyFSf86IbOatTKGrpTQnzarDZPYPVy0oe3myTMq7VjhfsUAbNUW8I5s+2lZvtx1gg==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1104,21 +1116,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz", - "integrity": "sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.3.0.tgz", + "integrity": "sha512-Xh4NVDaC4eYKY4O3QGPuQNp5NxBAlEvNQYOqJquR2MePNxO11E5K3t5x4M4Mx53IZvtpW+mBxIT0s274fLUocg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/visitor-keys": "5.59.8", + "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/visitor-keys": "6.3.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1131,42 +1143,41 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.8.tgz", - "integrity": "sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==", - "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.59.8", - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/typescript-estree": "5.59.8", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.3.0.tgz", + "integrity": "sha512-hLLg3BZE07XHnpzglNBG8P/IXq/ZVXraEbgY7FM0Cnc1ehM8RMdn9mat3LubJ3KBeYXXPxV1nugWbQPjGeJk6Q==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.3.0", + "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/typescript-estree": "6.3.0", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz", - "integrity": "sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.3.0.tgz", + "integrity": "sha512-kEhRRj7HnvaSjux1J9+7dBen15CdWmDnwrpyiHsFX6Qx2iW5LOBUgNefOFeh2PjWPlNwN8TOn6+4eBU3J/gupw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.8", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.3.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -1174,9 +1185,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1286,6 +1297,18 @@ "node": ">=8" } }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1330,9 +1353,9 @@ "dev": true }, "node_modules/comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.0.tgz", + "integrity": "sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==", "dev": true, "engines": { "node": ">= 12.0.0" @@ -1418,27 +1441,27 @@ } }, "node_modules/eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", - "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", + "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "^8.47.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.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.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "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", @@ -1448,7 +1471,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -1458,9 +1480,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -1474,18 +1495,19 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "46.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.1.0.tgz", - "integrity": "sha512-NpjpSuWR+Wwxzmssji7AVty1Vu0JvI7v+cTj+Rw1nKVjGv2eMvLGM/SI4VpgTXp82JbLtFOsA2QYLHT3YSmASA==", + "version": "46.4.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz", + "integrity": "sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.39.4", + "@es-joy/jsdoccomment": "~0.40.1", "are-docs-informative": "^0.0.2", - "comment-parser": "1.3.1", + "comment-parser": "1.4.0", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", - "semver": "^7.5.1", + "is-builtin-module": "^3.2.1", + "semver": "^7.5.4", "spdx-expression-parse": "^3.0.1" }, "engines": { @@ -1496,23 +1518,14 @@ } }, "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==", + "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": "^4.1.1" + "estraverse": "^5.2.0" }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1520,15 +1533,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "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, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1536,22 +1545,13 @@ "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.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "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.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -1574,15 +1574,6 @@ "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", @@ -1595,7 +1586,7 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -1604,15 +1595,6 @@ "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", @@ -1629,9 +1611,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -1775,9 +1757,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1809,12 +1791,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -1880,6 +1856,21 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2066,17 +2057,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "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", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -2268,9 +2259,9 @@ } }, "node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "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" @@ -2388,25 +2379,16 @@ "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==", + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, "engines": { - "node": ">= 6" + "node": ">=16.13.0" }, "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" + "typescript": ">=4.2.0" } }, "node_modules/type-check": { @@ -2434,9 +2416,9 @@ } }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2470,15 +2452,6 @@ "node": ">= 8" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2505,13 +2478,19 @@ } }, "dependencies": { + "@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 + }, "@es-joy/jsdoccomment": { - "version": "0.39.4", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", - "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz", + "integrity": "sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==", "dev": true, "requires": { - "comment-parser": "1.3.1", + "comment-parser": "1.4.0", "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" } @@ -2526,20 +2505,20 @@ } }, "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true }, "@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -2549,9 +2528,9 @@ } }, "@eslint/js": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz", - "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", "dev": true }, "@gi-types/accountsservice1": { @@ -3434,108 +3413,110 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.8.tgz", - "integrity": "sha512-JDMOmhXteJ4WVKOiHXGCoB96ADWg9q7efPWHRViT/f09bA8XOMLAVHHju3l0MkZnG1izaWXYmgvQcUjTRcpShQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.3.0.tgz", + "integrity": "sha512-IZYjYZ0ifGSLZbwMqIip/nOamFiWJ9AH+T/GYNZBWkVcyNQOFGtSMoWV7RvY4poYCMZ/4lHzNl796WOSNxmk8A==", "dev": true, "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.8", - "@typescript-eslint/type-utils": "5.59.8", - "@typescript-eslint/utils": "5.59.8", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.3.0", + "@typescript-eslint/type-utils": "6.3.0", + "@typescript-eslint/utils": "6.3.0", + "@typescript-eslint/visitor-keys": "6.3.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/parser": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.8.tgz", - "integrity": "sha512-AnR19RjJcpjoeGojmwZtCwBX/RidqDZtzcbG3xHrmz0aHHoOcbWnpDllenRDmDvsV0RQ6+tbb09/kyc+UT9Orw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.3.0.tgz", + "integrity": "sha512-ibP+y2Gr6p0qsUkhs7InMdXrwldjxZw66wpcQq9/PzAroM45wdwyu81T+7RibNCh8oc0AgrsyCwJByncY0Ongg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.59.8", - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/typescript-estree": "5.59.8", + "@typescript-eslint/scope-manager": "6.3.0", + "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/typescript-estree": "6.3.0", + "@typescript-eslint/visitor-keys": "6.3.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz", - "integrity": "sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.3.0.tgz", + "integrity": "sha512-WlNFgBEuGu74ahrXzgefiz/QlVb+qg8KDTpknKwR7hMH+lQygWyx0CQFoUmMn1zDkQjTBBIn75IxtWss77iBIQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/visitor-keys": "5.59.8" + "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/visitor-keys": "6.3.0" } }, "@typescript-eslint/type-utils": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.8.tgz", - "integrity": "sha512-+5M518uEIHFBy3FnyqZUF3BMP+AXnYn4oyH8RF012+e7/msMY98FhGL5SrN29NQ9xDgvqCgYnsOiKp1VjZ/fpA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.3.0.tgz", + "integrity": "sha512-7Oj+1ox1T2Yc8PKpBvOKWhoI/4rWFd1j7FA/rPE0lbBPXTKjdbtC+7Ev0SeBjEKkIhKWVeZSP+mR7y1Db1CdfQ==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.59.8", - "@typescript-eslint/utils": "5.59.8", + "@typescript-eslint/typescript-estree": "6.3.0", + "@typescript-eslint/utils": "6.3.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.8.tgz", - "integrity": "sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.3.0.tgz", + "integrity": "sha512-K6TZOvfVyc7MO9j60MkRNWyFSf86IbOatTKGrpTQnzarDZPYPVy0oe3myTMq7VjhfsUAbNUW8I5s+2lZvtx1gg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz", - "integrity": "sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.3.0.tgz", + "integrity": "sha512-Xh4NVDaC4eYKY4O3QGPuQNp5NxBAlEvNQYOqJquR2MePNxO11E5K3t5x4M4Mx53IZvtpW+mBxIT0s274fLUocg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/visitor-keys": "5.59.8", + "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/visitor-keys": "6.3.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/utils": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.8.tgz", - "integrity": "sha512-Tr65630KysnNn9f9G7ROF3w1b5/7f6QVCJ+WK9nhIocWmx9F+TmCAcglF26Vm7z8KCTwoKcNEBZrhlklla3CKg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.3.0.tgz", + "integrity": "sha512-hLLg3BZE07XHnpzglNBG8P/IXq/ZVXraEbgY7FM0Cnc1ehM8RMdn9mat3LubJ3KBeYXXPxV1nugWbQPjGeJk6Q==", "dev": true, "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.8", - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/typescript-estree": "5.59.8", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.3.0", + "@typescript-eslint/types": "6.3.0", + "@typescript-eslint/typescript-estree": "6.3.0", + "semver": "^7.5.4" } }, "@typescript-eslint/visitor-keys": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz", - "integrity": "sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.3.0.tgz", + "integrity": "sha512-kEhRRj7HnvaSjux1J9+7dBen15CdWmDnwrpyiHsFX6Qx2iW5LOBUgNefOFeh2PjWPlNwN8TOn6+4eBU3J/gupw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.59.8", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.3.0", + "eslint-visitor-keys": "^3.4.1" } }, "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true }, "acorn-jsx": { @@ -3615,6 +3596,12 @@ "fill-range": "^7.0.1" } }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3647,9 +3634,9 @@ "dev": true }, "comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.0.tgz", + "integrity": "sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==", "dev": true }, "concat-map": { @@ -3709,27 +3696,27 @@ "dev": true }, "eslint": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz", - "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==", + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", + "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "^8.47.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.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.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "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", @@ -3739,7 +3726,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -3749,69 +3735,51 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" - }, - "dependencies": { - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "eslint-plugin-jsdoc": { - "version": "46.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.1.0.tgz", - "integrity": "sha512-NpjpSuWR+Wwxzmssji7AVty1Vu0JvI7v+cTj+Rw1nKVjGv2eMvLGM/SI4VpgTXp82JbLtFOsA2QYLHT3YSmASA==", + "version": "46.4.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz", + "integrity": "sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "~0.39.4", + "@es-joy/jsdoccomment": "~0.40.1", "are-docs-informative": "^0.0.2", - "comment-parser": "1.3.1", + "comment-parser": "1.4.0", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", - "semver": "^7.5.1", + "is-builtin-module": "^3.2.1", + "semver": "^7.5.4", "spdx-expression-parse": "^3.0.1" } }, "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==", + "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, "requires": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "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 }, "espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } @@ -3823,14 +3791,6 @@ "dev": true, "requires": { "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "esrecurse": { @@ -3840,20 +3800,12 @@ "dev": true, "requires": { "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } } }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, "esutils": { @@ -3869,9 +3821,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -3987,9 +3939,9 @@ } }, "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -4009,12 +3961,6 @@ "slash": "^3.0.0" } }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -4065,6 +4011,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "requires": { + "builtin-modules": "^3.3.0" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4212,17 +4167,17 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" } }, "p-limit": { @@ -4331,9 +4286,9 @@ } }, "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "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, "requires": { "lru-cache": "^6.0.0" @@ -4421,20 +4376,12 @@ "is-number": "^7.0.0" } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", "dev": true, - "requires": { - "tslib": "^1.8.1" - } + "requires": {} }, "type-check": { "version": "0.4.0", @@ -4452,9 +4399,9 @@ "dev": true }, "typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true }, "uri-js": { @@ -4475,12 +4422,6 @@ "isexe": "^2.0.0" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index d7b3a6d7..43b0273d 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "devDependencies": { - "@gi-types/adw1": "latest", - "@gi-types/base-types": "latest", - "@gi-types/gjs-environment": "latest", - "@gi-types/gtk4-types": "latest", - "@gi-types/shell": "latest", - "@typescript-eslint/eslint-plugin": "latest", - "@typescript-eslint/parser": "latest", - "eslint": "latest", - "eslint-plugin-jsdoc": "latest", - "typescript": "latest" + "@gi-types/adw1": "^1.1.1", + "@gi-types/base-types": "^1.0.0", + "@gi-types/gjs-environment": "^1.1.0", + "@gi-types/gtk4-types": "^1.0.0", + "@gi-types/shell": "^0.1.6", + "@typescript-eslint/eslint-plugin": "^6.3.0", + "@typescript-eslint/parser": "^6.3.0", + "eslint": "^8.47.0", + "eslint-plugin-jsdoc": "^46.4.6", + "typescript": "^5.1.6" } } diff --git a/src/adapter/localFolder.ts b/src/adapter/localFolder.ts index 40c2de9b..0426848f 100644 --- a/src/adapter/localFolder.ts +++ b/src/adapter/localFolder.ts @@ -83,8 +83,7 @@ class LocalFolderAdapter extends BaseAdapter { const targetFile = Gio.File.new_for_path(historyEntry.path); // https://gjs.guide/guides/gio/file-operations.html#copying-and-moving-files - // This function was rewritten by Gio._promisify - // @ts-expect-error + // @ts-expect-error This function was rewritten by Gio._promisify // eslint-disable-next-line @typescript-eslint/await-thenable if (!await sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null)) throw new Error('Failed copying image.'); diff --git a/src/historyMenuElements.ts b/src/historyMenuElements.ts index 5bedf968..ef5813bb 100644 --- a/src/historyMenuElements.ts +++ b/src/historyMenuElements.ts @@ -168,8 +168,8 @@ const HistoryElement = GObject.registerClass({ /* Load the image on first opening of the sub menu instead of during creation of the history list. */ - this.menu.connect('open-state-changed', (_, open: boolean | unknown) => { - if (open) { + this.menu.connect('open-state-changed', (_, open) => { + if (typeof open === 'boolean' && open) { if (this._previewActor !== null) return; @@ -248,15 +248,13 @@ const HistoryElement = GObject.registerClass({ throw error; } - // This function was rewritten by Gio._promisify - // @ts-expect-error + // @ts-expect-error This function was rewritten by Gio._promisify // eslint-disable-next-line @typescript-eslint/await-thenable if (!await sourceFile.copy_async(targetFile, Gio.FileCopyFlags.NONE, GLib.PRIORITY_DEFAULT, null, null)) throw new Error('Failed copying image.'); // https://gjs.guide/guides/gio/file-operations.html#writing-file-contents - // This function was rewritten by Gio._promisify - // @ts-expect-error + // @ts-expect-error This function was rewritten by Gio._promisify // eslint-disable-next-line @typescript-eslint/await-thenable const [success, message]: [boolean, string] = await targetInfoFile.replace_contents_bytes_async( new TextEncoder().encode(JSON.stringify(this.historyEntry.source, null, '\t')), @@ -382,7 +380,7 @@ class StatusElement { */ startLoading(): void { // FIXME: Don't know where this is defined - // @ts-expect-error + // @ts-expect-error Don't know where this is defined // eslint-disable-next-line @typescript-eslint/no-unsafe-call this.icon.ease({ opacity: 20, diff --git a/src/jsonPath.ts b/src/jsonPath.ts index f6e61021..066612c3 100644 --- a/src/jsonPath.ts +++ b/src/jsonPath.ts @@ -66,7 +66,7 @@ function getTarget(inputObject: unknown, inputString: string): [object: unknown, * @param {string} keyString Name of the key in the object * @returns {unknown | null} Found object member or null */ -function _getObjectMember(inputObject: object, keyString: string): unknown | null { +function _getObjectMember(inputObject: object, keyString: string): unknown { if (keyString === '$') return inputObject; diff --git a/src/prefs.ts b/src/prefs.ts index 1f9fde14..02f78621 100644 --- a/src/prefs.ts +++ b/src/prefs.ts @@ -289,6 +289,8 @@ class RandomWallpaperSettings { }); this._saveDialog.connect('response', (dialog, response_id) => { + // FIXME: ESLint complains about this comparison somehow? + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison if (response_id === Gtk.ResponseType.ACCEPT) { const text = dialog.get_file()?.get_path(); if (text) diff --git a/src/settings.ts b/src/settings.ts index b052e9c9..1f8fef8d 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -162,10 +162,10 @@ class Settings { * Watch a setting for changes. * * @param {string} key Settings key to watch for changes - * @param {(...args: any[]) => any} callback Function to call on value changes + * @param {(...args: unknown[]) => unknown} callback Function to call on value changes * @returns {number} Handler ID, use for disconnect */ - observe(key: string, callback: (...args: any[]) => any): number { + observe(key: string, callback: (...args: unknown[]) => unknown): number { return this._settings.connect(`changed::${key}`, callback); } diff --git a/src/soupBowl.ts b/src/soupBowl.ts index 2272f853..f86373f1 100644 --- a/src/soupBowl.ts +++ b/src/soupBowl.ts @@ -49,6 +49,7 @@ class SoupBowl { */ private _send_and_receive_soup24(soupMessage: Soup.Message): Promise { return new Promise((resolve, reject) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore this._session.queue_message(soupMessage, (session, msg) => { if (!msg.response_body) { @@ -71,9 +72,11 @@ class SoupBowl { */ private _send_and_receive_soup30(soupMessage: Soup.Message): Promise { return new Promise((resolve, reject) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-call this._session.send_and_read_async(soupMessage, 0, null, (session: Soup.Session, message: Soup.Message) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-call const res_data = session.send_and_read_finish(message) as GLib.Bytes | null; diff --git a/src/ui/localFolder.ts b/src/ui/localFolder.ts index 333943a3..9e165ab9 100644 --- a/src/ui/localFolder.ts +++ b/src/ui/localFolder.ts @@ -60,6 +60,8 @@ const LocalFolderSettingsGroup = GObject.registerClass({ }); this._saveDialog.connect('response', (_dialog, response_id) => { + // FIXME: ESLint complains about this comparison somehow? + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison if (response_id === Gtk.ResponseType.ACCEPT) { const chosenPath = _dialog.get_file()?.get_path(); diff --git a/src/ui/wallhaven.ts b/src/ui/wallhaven.ts index 731620d3..8805f0e5 100644 --- a/src/ui/wallhaven.ts +++ b/src/ui/wallhaven.ts @@ -152,6 +152,8 @@ const WallhavenSettingsGroup = GObject.registerClass({ this._colorDialog.add_palette(Gtk.Orientation.HORIZONTAL, 10, WallhavenSettingsGroup._colorPalette); this._colorDialog.connect('response', (dialog, response_id) => { + // FIXME: ESLint complains about this comparison somehow? + // eslint-disable-next-line @typescript-eslint/no-unsafe-enum-comparison if (response_id === Gtk.ResponseType.OK) { // result is a Gdk.RGBA which uses float const rgba = dialog.get_rgba(); diff --git a/src/utils.ts b/src/utils.ts index a7ba3e1b..87041254 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -139,7 +139,7 @@ function findFirstDifference(str1: string, str2: string): number { */ function getMonitorCount(): number { // FIXME: Figure out where the 'global' thing can be imported from - // @ts-expect-error + // @ts-expect-error Figure out where the 'global' thing can be imported from // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const currentDisplay = global?.display as Meta.Display; const count = currentDisplay?.get_n_monitors(); diff --git a/types/ui/popupMenu.d.ts b/types/ui/popupMenu.d.ts index 2d12b9ed..69f52806 100644 --- a/types/ui/popupMenu.d.ts +++ b/types/ui/popupMenu.d.ts @@ -14,7 +14,7 @@ declare module 'popupMenu' { disconnectObject(args: unknown): unknown; // don't know where these are: - connect(key: string, callback: (actor: typeof this, state: unknown[]) => void): void; + connect(key: string, callback: (actor: typeof this, ...args: unknown[]) => unknown): void; } export class PopupMenuBase extends EventEmitter {