Revert "Support "produced characters" as hotkeys"
This reverts commit bc66d52701.
"useKey" is broken https://github.com/JohannesKlauss/react-hotkeys-hook/issues/1318
This commit is contained in:
870
.oxlintrc.jsonc
870
.oxlintrc.jsonc
@@ -1,419 +1,461 @@
|
|||||||
{
|
{
|
||||||
"plugins": ["react", "unicorn", "typescript", "import", "jsx-a11y"],
|
"plugins": [
|
||||||
"jsPlugins": [
|
"react",
|
||||||
"eslint-plugin-lingui",
|
"unicorn",
|
||||||
"eslint-plugin-no-relative-import-paths",
|
"typescript",
|
||||||
"eslint-plugin-unused-imports",
|
"import",
|
||||||
"eslint-plugin-no-only-tests",
|
"jsx-a11y"
|
||||||
"@tony.ganchev/eslint-plugin-header",
|
],
|
||||||
|
"jsPlugins": [
|
||||||
|
"eslint-plugin-lingui",
|
||||||
|
"eslint-plugin-no-relative-import-paths",
|
||||||
|
"eslint-plugin-unused-imports",
|
||||||
|
"eslint-plugin-no-only-tests",
|
||||||
|
"@tony.ganchev/eslint-plugin-header"
|
||||||
|
],
|
||||||
|
"categories": {
|
||||||
|
"correctness": "off"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
// ── Core JS: correctness (from js.configs.recommended) ──
|
||||||
|
// Rules redundant with TypeScript strict mode have been removed.
|
||||||
|
"for-direction": "error",
|
||||||
|
"no-async-promise-executor": "error",
|
||||||
|
"no-case-declarations": "error",
|
||||||
|
"no-compare-neg-zero": "error",
|
||||||
|
"no-cond-assign": "error",
|
||||||
|
"no-constant-binary-expression": "error",
|
||||||
|
"no-constant-condition": "error",
|
||||||
|
"no-control-regex": "error",
|
||||||
|
"no-debugger": "error",
|
||||||
|
"no-delete-var": "error",
|
||||||
|
"no-dupe-else-if": "error",
|
||||||
|
"no-duplicate-case": "error",
|
||||||
|
"no-empty": "error",
|
||||||
|
"no-empty-character-class": "error",
|
||||||
|
"no-empty-pattern": "error",
|
||||||
|
"no-empty-static-block": "error",
|
||||||
|
"no-ex-assign": "error",
|
||||||
|
"no-extra-boolean-cast": "error",
|
||||||
|
"no-invalid-regexp": "error",
|
||||||
|
"no-irregular-whitespace": "error",
|
||||||
|
"no-loss-of-precision": "error",
|
||||||
|
"no-misleading-character-class": "error",
|
||||||
|
"no-nonoctal-decimal-escape": "error",
|
||||||
|
"no-prototype-builtins": "error",
|
||||||
|
"no-regex-spaces": "error",
|
||||||
|
"no-self-assign": "error",
|
||||||
|
"no-shadow-restricted-names": "error",
|
||||||
|
"no-sparse-arrays": "error",
|
||||||
|
"no-unsafe-finally": "error",
|
||||||
|
"no-unsafe-optional-chaining": "error",
|
||||||
|
"no-unused-labels": "error",
|
||||||
|
"no-unused-private-class-members": "error",
|
||||||
|
"no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"vars": "all",
|
||||||
|
"args": "after-used",
|
||||||
|
"ignoreRestSiblings": true,
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"varsIgnorePattern": "^_",
|
||||||
|
"caughtErrorsIgnorePattern": "^_|^e$|^error$"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"categories": {
|
"no-useless-backreference": "error",
|
||||||
"correctness": "off",
|
"no-useless-catch": "error",
|
||||||
},
|
"no-useless-escape": "error",
|
||||||
"rules": {
|
"no-with": "error",
|
||||||
// ── Core JS: correctness (from js.configs.recommended) ──
|
"require-yield": "error",
|
||||||
// Rules redundant with TypeScript strict mode have been removed.
|
"use-isnan": "error",
|
||||||
"for-direction": "error",
|
"no-array-constructor": "error",
|
||||||
"no-async-promise-executor": "error",
|
"no-unused-expressions": "error",
|
||||||
"no-case-declarations": "error",
|
|
||||||
"no-compare-neg-zero": "error",
|
|
||||||
"no-cond-assign": "error",
|
|
||||||
"no-constant-binary-expression": "error",
|
|
||||||
"no-constant-condition": "error",
|
|
||||||
"no-control-regex": "error",
|
|
||||||
"no-debugger": "error",
|
|
||||||
"no-delete-var": "error",
|
|
||||||
"no-dupe-else-if": "error",
|
|
||||||
"no-duplicate-case": "error",
|
|
||||||
"no-empty": "error",
|
|
||||||
"no-empty-character-class": "error",
|
|
||||||
"no-empty-pattern": "error",
|
|
||||||
"no-empty-static-block": "error",
|
|
||||||
"no-ex-assign": "error",
|
|
||||||
"no-extra-boolean-cast": "error",
|
|
||||||
"no-invalid-regexp": "error",
|
|
||||||
"no-irregular-whitespace": "error",
|
|
||||||
"no-loss-of-precision": "error",
|
|
||||||
"no-misleading-character-class": "error",
|
|
||||||
"no-nonoctal-decimal-escape": "error",
|
|
||||||
"no-prototype-builtins": "error",
|
|
||||||
"no-regex-spaces": "error",
|
|
||||||
"no-self-assign": "error",
|
|
||||||
"no-shadow-restricted-names": "error",
|
|
||||||
"no-sparse-arrays": "error",
|
|
||||||
"no-unsafe-finally": "error",
|
|
||||||
"no-unsafe-optional-chaining": "error",
|
|
||||||
"no-unused-labels": "error",
|
|
||||||
"no-unused-private-class-members": "error",
|
|
||||||
"no-unused-vars": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"vars": "all",
|
|
||||||
"args": "after-used",
|
|
||||||
"ignoreRestSiblings": true,
|
|
||||||
"argsIgnorePattern": "^_",
|
|
||||||
"varsIgnorePattern": "^_",
|
|
||||||
"caughtErrorsIgnorePattern": "^_|^e$|^error$",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"no-useless-backreference": "error",
|
|
||||||
"no-useless-catch": "error",
|
|
||||||
"no-useless-escape": "error",
|
|
||||||
"no-with": "error",
|
|
||||||
"require-yield": "error",
|
|
||||||
"use-isnan": "error",
|
|
||||||
"no-array-constructor": "error",
|
|
||||||
"no-unused-expressions": "error",
|
|
||||||
|
|
||||||
// ── Core JS: style & best practices ──
|
// ── Core JS: style & best practices ──
|
||||||
"no-console": "error",
|
"no-console": "error",
|
||||||
"no-else-return": [
|
"no-else-return": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"allowElseIf": false,
|
"allowElseIf": false
|
||||||
},
|
}
|
||||||
],
|
|
||||||
"no-lonely-if": "error",
|
|
||||||
"no-unneeded-ternary": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"defaultAssignment": false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"no-multi-assign": "error",
|
|
||||||
"no-return-assign": ["error", "always"],
|
|
||||||
"no-sequences": "error",
|
|
||||||
"no-new": "error",
|
|
||||||
"no-new-func": "error",
|
|
||||||
"no-new-wrappers": "error",
|
|
||||||
"no-eval": "error",
|
|
||||||
"no-script-url": "error",
|
|
||||||
"no-self-compare": "error",
|
|
||||||
"no-extend-native": "error",
|
|
||||||
"no-alert": "error",
|
|
||||||
"no-restricted-globals": ["error", "isFinite", "isNaN"],
|
|
||||||
"no-await-in-loop": "error",
|
|
||||||
"no-promise-executor-return": "error",
|
|
||||||
"no-template-curly-in-string": "error",
|
|
||||||
"no-constructor-return": "error",
|
|
||||||
"no-useless-concat": "error",
|
|
||||||
"no-useless-return": "error",
|
|
||||||
"no-useless-computed-key": "error",
|
|
||||||
"no-useless-rename": "error",
|
|
||||||
"eqeqeq": [
|
|
||||||
"error",
|
|
||||||
"always",
|
|
||||||
{
|
|
||||||
"null": "ignore",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"default-case": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"commentPattern": "^no default$",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"default-case-last": "error",
|
|
||||||
"yoda": "error",
|
|
||||||
"prefer-template": "error",
|
|
||||||
"prefer-exponentiation-operator": "error",
|
|
||||||
"prefer-object-spread": "error",
|
|
||||||
"arrow-body-style": ["error", "as-needed"],
|
|
||||||
"prefer-destructuring": "error",
|
|
||||||
"no-nested-ternary": "error",
|
|
||||||
"prefer-promise-reject-errors": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"allowEmptyReject": true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"no-param-reassign": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"props": true,
|
|
||||||
"ignorePropertyModificationsForRegex": ["^draft"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"no-restricted-imports": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"paths": [
|
|
||||||
{
|
|
||||||
"name": "react-hotkeys-hook",
|
|
||||||
"importNames": ["useHotkeys"],
|
|
||||||
"message": "Use src/lib/react-hotkey-hook/useHotkeys instead",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"patterns": [
|
|
||||||
{
|
|
||||||
"group": ["@mui/*", "!@mui/material/", "!@mui/icons-material/", "!@mui/x-date-pickers/"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": ["@mui/*/*/*"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group": ["lodash", "lodash/*", "!lodash/fp/*"],
|
|
||||||
"message": "Use lodash-fp instead.",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
// ── New: modern JS essentials ──
|
|
||||||
"prefer-const": "error",
|
|
||||||
"no-var": "error",
|
|
||||||
"curly": "error",
|
|
||||||
"no-warning-comments": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"terms": ["@nocommit"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
// ── TypeScript rules (non-type-checked) ──
|
|
||||||
"no-shadow": "error",
|
|
||||||
"no-loop-func": "error",
|
|
||||||
"no-use-before-define": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"functions": false,
|
|
||||||
"classes": true,
|
|
||||||
"variables": true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"default-param-last": "error",
|
|
||||||
|
|
||||||
// ── TypeScript rules from recommended ──
|
|
||||||
"typescript/no-duplicate-enum-values": "error",
|
|
||||||
"typescript/no-extra-non-null-assertion": "error",
|
|
||||||
"typescript/no-misused-new": "error",
|
|
||||||
"typescript/no-namespace": "error",
|
|
||||||
"typescript/no-non-null-asserted-optional-chain": "error",
|
|
||||||
"typescript/no-require-imports": "error",
|
|
||||||
"typescript/no-this-alias": "error",
|
|
||||||
"typescript/no-unnecessary-type-constraint": "error",
|
|
||||||
"typescript/no-unsafe-declaration-merging": "error",
|
|
||||||
"typescript/no-wrapper-object-types": "error",
|
|
||||||
"typescript/prefer-as-const": "error",
|
|
||||||
"typescript/prefer-namespace-keyword": "error",
|
|
||||||
"typescript/triple-slash-reference": "error",
|
|
||||||
|
|
||||||
// ── New: TypeScript safety ──
|
|
||||||
"typescript/no-explicit-any": "off",
|
|
||||||
"typescript/no-empty-object-type": "error",
|
|
||||||
"typescript/no-unsafe-function-type": "error",
|
|
||||||
"typescript/consistent-type-imports": "error",
|
|
||||||
"typescript/switch-exhaustiveness-check": "error",
|
|
||||||
|
|
||||||
// ── TypeScript rules (type-checked) ──
|
|
||||||
"typescript/no-implied-eval": "error",
|
|
||||||
"typescript/only-throw-error": "error",
|
|
||||||
"typescript/dot-notation": "error",
|
|
||||||
"typescript/return-await": ["error", "in-try-catch"],
|
|
||||||
|
|
||||||
// ── Import rules ──
|
|
||||||
"import/namespace": "error",
|
|
||||||
"import/default": "error",
|
|
||||||
"import/export": "error",
|
|
||||||
"import/no-duplicates": "error",
|
|
||||||
"import/no-mutable-exports": "error",
|
|
||||||
"import/first": "error",
|
|
||||||
"import/no-amd": "error",
|
|
||||||
"import/no-absolute-path": "error",
|
|
||||||
"import/no-self-import": "error",
|
|
||||||
"import/no-default-export": "error",
|
|
||||||
|
|
||||||
// ── Unicorn: modern JS (auto-fixable) ──
|
|
||||||
"unicorn/prefer-array-flat-map": "error",
|
|
||||||
"unicorn/prefer-structured-clone": "error",
|
|
||||||
"unicorn/prefer-string-replace-all": "error",
|
|
||||||
"unicorn/consistent-function-scoping": "error",
|
|
||||||
|
|
||||||
// ── React rules ──
|
|
||||||
"react/jsx-key": "error",
|
|
||||||
"react/jsx-no-comment-textnodes": "error",
|
|
||||||
"react/jsx-no-duplicate-props": "error",
|
|
||||||
"react/jsx-no-target-blank": "error",
|
|
||||||
"react/jsx-no-undef": "error",
|
|
||||||
"react/no-children-prop": "error",
|
|
||||||
"react/no-danger-with-children": "error",
|
|
||||||
"react/no-render-return-value": "error",
|
|
||||||
"react/no-unescaped-entities": "error",
|
|
||||||
"react/no-unknown-property": "error",
|
|
||||||
"react/rules-of-hooks": "error",
|
|
||||||
"react/no-array-index-key": "error",
|
|
||||||
"react/jsx-no-useless-fragment": "error",
|
|
||||||
"react/jsx-no-constructed-context-values": "error",
|
|
||||||
"react/self-closing-comp": "error",
|
|
||||||
"react/jsx-boolean-value": ["error", "never"],
|
|
||||||
"react/jsx-pascal-case": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"allowAllCaps": true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"react/button-has-type": "error",
|
|
||||||
"react/jsx-curly-brace-presence": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"props": "never",
|
|
||||||
"children": "never",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"react/jsx-fragments": ["error", "syntax"],
|
|
||||||
"react/jsx-no-script-url": "error",
|
|
||||||
"react/style-prop-object": "error",
|
|
||||||
"react/void-dom-elements-no-children": "error",
|
|
||||||
"react/no-danger": "error",
|
|
||||||
|
|
||||||
// ── jsx-a11y rules ──
|
|
||||||
"jsx_a11y/alt-text": "error",
|
|
||||||
"jsx_a11y/anchor-has-content": "error",
|
|
||||||
"jsx_a11y/anchor-is-valid": "error",
|
|
||||||
"jsx_a11y/aria-activedescendant-has-tabindex": "error",
|
|
||||||
"jsx_a11y/aria-props": "error",
|
|
||||||
"jsx_a11y/aria-proptypes": "error",
|
|
||||||
"jsx_a11y/aria-role": "error",
|
|
||||||
"jsx_a11y/aria-unsupported-elements": "error",
|
|
||||||
"jsx_a11y/autocomplete-valid": "error",
|
|
||||||
"jsx_a11y/click-events-have-key-events": "error",
|
|
||||||
"jsx_a11y/heading-has-content": "error",
|
|
||||||
"jsx_a11y/html-has-lang": "error",
|
|
||||||
"jsx_a11y/iframe-has-title": "error",
|
|
||||||
"jsx_a11y/img-redundant-alt": "error",
|
|
||||||
"jsx_a11y/label-has-associated-control": "error",
|
|
||||||
"jsx_a11y/media-has-caption": "error",
|
|
||||||
"jsx_a11y/mouse-events-have-key-events": "error",
|
|
||||||
"jsx_a11y/no-access-key": "error",
|
|
||||||
"jsx_a11y/no-autofocus": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"ignoreNonDOM": true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"jsx_a11y/no-distracting-elements": "error",
|
|
||||||
"jsx_a11y/no-noninteractive-tabindex": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"tags": [],
|
|
||||||
"roles": ["tabpanel"],
|
|
||||||
"allowExpressionValues": true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"jsx_a11y/no-redundant-roles": "error",
|
|
||||||
"jsx_a11y/no-static-element-interactions": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"allowExpressionValues": true,
|
|
||||||
"handlers": ["onClick", "onMouseDown", "onMouseUp", "onKeyPress", "onKeyDown", "onKeyUp"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"jsx_a11y/role-has-required-aria-props": "error",
|
|
||||||
"jsx_a11y/role-supports-aria-props": "error",
|
|
||||||
"jsx_a11y/scope": "error",
|
|
||||||
"jsx_a11y/tabindex-no-positive": "error",
|
|
||||||
|
|
||||||
// ── Lingui rules ──
|
|
||||||
"lingui/t-call-in-function": "error",
|
|
||||||
"lingui/no-single-tag-to-translate": "error",
|
|
||||||
"lingui/no-single-variables-to-translate": "error",
|
|
||||||
"lingui/no-trans-inside-trans": "error",
|
|
||||||
"lingui/no-expression-in-message": "off",
|
|
||||||
|
|
||||||
// ── No relative import paths ──
|
|
||||||
"no-relative-import-paths/no-relative-import-paths": "error",
|
|
||||||
|
|
||||||
// ── Unused imports ──
|
|
||||||
"unused-imports/no-unused-imports": "error",
|
|
||||||
|
|
||||||
// ── No .only in tests ──
|
|
||||||
"no-only-tests/no-only-tests": "error",
|
|
||||||
|
|
||||||
// ── License header ──
|
|
||||||
"@tony.ganchev/header/header": [
|
|
||||||
"error",
|
|
||||||
"block",
|
|
||||||
[
|
|
||||||
"",
|
|
||||||
" * Copyright (C) Contributors to the Suwayomi project",
|
|
||||||
" *",
|
|
||||||
" * This Source Code Form is subject to the terms of the Mozilla Public",
|
|
||||||
" * License, v. 2.0. If a copy of the MPL was not distributed with this",
|
|
||||||
" * file, You can obtain one at https://mozilla.org/MPL/2.0/.",
|
|
||||||
" ",
|
|
||||||
],
|
|
||||||
2,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"jsx-a11y": {
|
|
||||||
"components": {},
|
|
||||||
"attributes": {},
|
|
||||||
},
|
|
||||||
"next": {
|
|
||||||
"rootDir": [],
|
|
||||||
},
|
|
||||||
"react": {
|
|
||||||
"formComponents": [],
|
|
||||||
"linkComponents": [],
|
|
||||||
"version": "19.2.0",
|
|
||||||
"componentWrapperFunctions": [],
|
|
||||||
},
|
|
||||||
"jsdoc": {
|
|
||||||
"ignorePrivate": false,
|
|
||||||
"ignoreInternal": false,
|
|
||||||
"ignoreReplacesDocs": true,
|
|
||||||
"overrideReplacesDocs": true,
|
|
||||||
"augmentsExtendsReplacesDocs": false,
|
|
||||||
"implementsReplacesDocs": false,
|
|
||||||
"exemptDestructuredRootsFromChecks": false,
|
|
||||||
"tagNamePreference": {},
|
|
||||||
},
|
|
||||||
"vitest": {
|
|
||||||
"typecheck": false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"builtin": true,
|
|
||||||
},
|
|
||||||
"globals": {},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"src/base/utils/MediaQuery.tsx",
|
|
||||||
"src/features/authentication/AuthManager.ts",
|
|
||||||
"src/features/chapter/services/Chapters.ts",
|
|
||||||
"src/features/metadata/services/MetadataMigrations.ts",
|
|
||||||
"src/features/migration/MigrationManager.ts",
|
|
||||||
"src/features/reader/services/ReaderControls.ts",
|
|
||||||
"src/features/reader/services/ReaderService.ts",
|
|
||||||
"src/features/source/services/Sources.ts",
|
|
||||||
"src/lib/dnd-kit/DndKitUtil.ts",
|
|
||||||
"src/lib/requests/RequestManager.ts",
|
|
||||||
"src/lib/requests/client/GraphQLClient.ts",
|
|
||||||
"src/lib/virtuoso/Virtuoso.util.tsx",
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"react/rules-of-hooks": "off",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["**/*.config.ts", "**/*.config.js"],
|
|
||||||
"rules": {
|
|
||||||
"import/no-default-export": "off",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["tools/scripts/**/*"],
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-console": "off",
|
|
||||||
"no-relative-import-paths/no-relative-import-paths": "off",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
"ignorePatterns": ["src/lib/graphql/generated/**", "tools/eslint-plugin-header.cjs"],
|
"no-lonely-if": "error",
|
||||||
|
"no-unneeded-ternary": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"defaultAssignment": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-multi-assign": "error",
|
||||||
|
"no-return-assign": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"no-sequences": "error",
|
||||||
|
"no-new": "error",
|
||||||
|
"no-new-func": "error",
|
||||||
|
"no-new-wrappers": "error",
|
||||||
|
"no-eval": "error",
|
||||||
|
"no-script-url": "error",
|
||||||
|
"no-self-compare": "error",
|
||||||
|
"no-extend-native": "error",
|
||||||
|
"no-alert": "error",
|
||||||
|
"no-restricted-globals": [
|
||||||
|
"error",
|
||||||
|
"isFinite",
|
||||||
|
"isNaN"
|
||||||
|
],
|
||||||
|
"no-await-in-loop": "error",
|
||||||
|
"no-promise-executor-return": "error",
|
||||||
|
"no-template-curly-in-string": "error",
|
||||||
|
"no-constructor-return": "error",
|
||||||
|
"no-useless-concat": "error",
|
||||||
|
"no-useless-return": "error",
|
||||||
|
"no-useless-computed-key": "error",
|
||||||
|
"no-useless-rename": "error",
|
||||||
|
"eqeqeq": [
|
||||||
|
"error",
|
||||||
|
"always",
|
||||||
|
{
|
||||||
|
"null": "ignore"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default-case": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"commentPattern": "^no default$"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default-case-last": "error",
|
||||||
|
"yoda": "error",
|
||||||
|
"prefer-template": "error",
|
||||||
|
"prefer-exponentiation-operator": "error",
|
||||||
|
"prefer-object-spread": "error",
|
||||||
|
"arrow-body-style": ["error", "as-needed"],
|
||||||
|
"prefer-destructuring": "error",
|
||||||
|
"no-nested-ternary": "error",
|
||||||
|
"prefer-promise-reject-errors": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowEmptyReject": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-param-reassign": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"props": true,
|
||||||
|
"ignorePropertyModificationsForRegex": [
|
||||||
|
"^draft"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no-restricted-imports": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"patterns": [
|
||||||
|
{
|
||||||
|
"group": [
|
||||||
|
"@mui/*",
|
||||||
|
"!@mui/material/",
|
||||||
|
"!@mui/icons-material/",
|
||||||
|
"!@mui/x-date-pickers/"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": [
|
||||||
|
"@mui/*/*/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": [
|
||||||
|
"lodash",
|
||||||
|
"lodash/*",
|
||||||
|
"!lodash/fp/*"
|
||||||
|
],
|
||||||
|
"message": "Use lodash-fp instead."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// ── New: modern JS essentials ──
|
||||||
|
"prefer-const": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"curly": "error",
|
||||||
|
"no-warning-comments": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"terms": [
|
||||||
|
"@nocommit"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// ── TypeScript rules (non-type-checked) ──
|
||||||
|
"no-shadow": "error",
|
||||||
|
"no-loop-func": "error",
|
||||||
|
"no-use-before-define": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"functions": false,
|
||||||
|
"classes": true,
|
||||||
|
"variables": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default-param-last": "error",
|
||||||
|
|
||||||
|
// ── TypeScript rules from recommended ──
|
||||||
|
"typescript/no-duplicate-enum-values": "error",
|
||||||
|
"typescript/no-extra-non-null-assertion": "error",
|
||||||
|
"typescript/no-misused-new": "error",
|
||||||
|
"typescript/no-namespace": "error",
|
||||||
|
"typescript/no-non-null-asserted-optional-chain": "error",
|
||||||
|
"typescript/no-require-imports": "error",
|
||||||
|
"typescript/no-this-alias": "error",
|
||||||
|
"typescript/no-unnecessary-type-constraint": "error",
|
||||||
|
"typescript/no-unsafe-declaration-merging": "error",
|
||||||
|
"typescript/no-wrapper-object-types": "error",
|
||||||
|
"typescript/prefer-as-const": "error",
|
||||||
|
"typescript/prefer-namespace-keyword": "error",
|
||||||
|
"typescript/triple-slash-reference": "error",
|
||||||
|
|
||||||
|
// ── New: TypeScript safety ──
|
||||||
|
"typescript/no-explicit-any": "off",
|
||||||
|
"typescript/no-empty-object-type": "error",
|
||||||
|
"typescript/no-unsafe-function-type": "error",
|
||||||
|
"typescript/consistent-type-imports": "error",
|
||||||
|
"typescript/switch-exhaustiveness-check": "error",
|
||||||
|
|
||||||
|
// ── TypeScript rules (type-checked) ──
|
||||||
|
"typescript/no-implied-eval": "error",
|
||||||
|
"typescript/only-throw-error": "error",
|
||||||
|
"typescript/dot-notation": "error",
|
||||||
|
"typescript/return-await": [
|
||||||
|
"error",
|
||||||
|
"in-try-catch"
|
||||||
|
],
|
||||||
|
|
||||||
|
// ── Import rules ──
|
||||||
|
"import/namespace": "error",
|
||||||
|
"import/default": "error",
|
||||||
|
"import/export": "error",
|
||||||
|
"import/no-duplicates": "error",
|
||||||
|
"import/no-mutable-exports": "error",
|
||||||
|
"import/first": "error",
|
||||||
|
"import/no-amd": "error",
|
||||||
|
"import/no-absolute-path": "error",
|
||||||
|
"import/no-self-import": "error",
|
||||||
|
"import/no-default-export": "error",
|
||||||
|
|
||||||
|
// ── Unicorn: modern JS (auto-fixable) ──
|
||||||
|
"unicorn/prefer-array-flat-map": "error",
|
||||||
|
"unicorn/prefer-structured-clone": "error",
|
||||||
|
"unicorn/prefer-string-replace-all": "error",
|
||||||
|
"unicorn/consistent-function-scoping": "error",
|
||||||
|
|
||||||
|
// ── React rules ──
|
||||||
|
"react/jsx-key": "error",
|
||||||
|
"react/jsx-no-comment-textnodes": "error",
|
||||||
|
"react/jsx-no-duplicate-props": "error",
|
||||||
|
"react/jsx-no-target-blank": "error",
|
||||||
|
"react/jsx-no-undef": "error",
|
||||||
|
"react/no-children-prop": "error",
|
||||||
|
"react/no-danger-with-children": "error",
|
||||||
|
"react/no-render-return-value": "error",
|
||||||
|
"react/no-unescaped-entities": "error",
|
||||||
|
"react/no-unknown-property": "error",
|
||||||
|
"react/rules-of-hooks": "error",
|
||||||
|
"react/no-array-index-key": "error",
|
||||||
|
"react/jsx-no-useless-fragment": "error",
|
||||||
|
"react/jsx-no-constructed-context-values": "error",
|
||||||
|
"react/self-closing-comp": "error",
|
||||||
|
"react/jsx-boolean-value": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
],
|
||||||
|
"react/jsx-pascal-case": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowAllCaps": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"react/button-has-type": "error",
|
||||||
|
"react/jsx-curly-brace-presence": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"props": "never",
|
||||||
|
"children": "never"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"react/jsx-fragments": [
|
||||||
|
"error",
|
||||||
|
"syntax"
|
||||||
|
],
|
||||||
|
"react/jsx-no-script-url": "error",
|
||||||
|
"react/style-prop-object": "error",
|
||||||
|
"react/void-dom-elements-no-children": "error",
|
||||||
|
"react/no-danger": "error",
|
||||||
|
|
||||||
|
// ── jsx-a11y rules ──
|
||||||
|
"jsx_a11y/alt-text": "error",
|
||||||
|
"jsx_a11y/anchor-has-content": "error",
|
||||||
|
"jsx_a11y/anchor-is-valid": "error",
|
||||||
|
"jsx_a11y/aria-activedescendant-has-tabindex": "error",
|
||||||
|
"jsx_a11y/aria-props": "error",
|
||||||
|
"jsx_a11y/aria-proptypes": "error",
|
||||||
|
"jsx_a11y/aria-role": "error",
|
||||||
|
"jsx_a11y/aria-unsupported-elements": "error",
|
||||||
|
"jsx_a11y/autocomplete-valid": "error",
|
||||||
|
"jsx_a11y/click-events-have-key-events": "error",
|
||||||
|
"jsx_a11y/heading-has-content": "error",
|
||||||
|
"jsx_a11y/html-has-lang": "error",
|
||||||
|
"jsx_a11y/iframe-has-title": "error",
|
||||||
|
"jsx_a11y/img-redundant-alt": "error",
|
||||||
|
"jsx_a11y/label-has-associated-control": "error",
|
||||||
|
"jsx_a11y/media-has-caption": "error",
|
||||||
|
"jsx_a11y/mouse-events-have-key-events": "error",
|
||||||
|
"jsx_a11y/no-access-key": "error",
|
||||||
|
"jsx_a11y/no-autofocus": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"ignoreNonDOM": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"jsx_a11y/no-distracting-elements": "error",
|
||||||
|
"jsx_a11y/no-noninteractive-tabindex": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"tags": [],
|
||||||
|
"roles": [
|
||||||
|
"tabpanel"
|
||||||
|
],
|
||||||
|
"allowExpressionValues": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"jsx_a11y/no-redundant-roles": "error",
|
||||||
|
"jsx_a11y/no-static-element-interactions": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowExpressionValues": true,
|
||||||
|
"handlers": [
|
||||||
|
"onClick",
|
||||||
|
"onMouseDown",
|
||||||
|
"onMouseUp",
|
||||||
|
"onKeyPress",
|
||||||
|
"onKeyDown",
|
||||||
|
"onKeyUp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"jsx_a11y/role-has-required-aria-props": "error",
|
||||||
|
"jsx_a11y/role-supports-aria-props": "error",
|
||||||
|
"jsx_a11y/scope": "error",
|
||||||
|
"jsx_a11y/tabindex-no-positive": "error",
|
||||||
|
|
||||||
|
// ── Lingui rules ──
|
||||||
|
"lingui/t-call-in-function": "error",
|
||||||
|
"lingui/no-single-tag-to-translate": "error",
|
||||||
|
"lingui/no-single-variables-to-translate": "error",
|
||||||
|
"lingui/no-trans-inside-trans": "error",
|
||||||
|
"lingui/no-expression-in-message": "off",
|
||||||
|
|
||||||
|
// ── No relative import paths ──
|
||||||
|
"no-relative-import-paths/no-relative-import-paths": "error",
|
||||||
|
|
||||||
|
// ── Unused imports ──
|
||||||
|
"unused-imports/no-unused-imports": "error",
|
||||||
|
|
||||||
|
// ── No .only in tests ──
|
||||||
|
"no-only-tests/no-only-tests": "error",
|
||||||
|
|
||||||
|
// ── License header ──
|
||||||
|
"@tony.ganchev/header/header": ["error", "block", [
|
||||||
|
"",
|
||||||
|
" * Copyright (C) Contributors to the Suwayomi project",
|
||||||
|
" *",
|
||||||
|
" * This Source Code Form is subject to the terms of the Mozilla Public",
|
||||||
|
" * License, v. 2.0. If a copy of the MPL was not distributed with this",
|
||||||
|
" * file, You can obtain one at https://mozilla.org/MPL/2.0/.",
|
||||||
|
" "
|
||||||
|
], 2]
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"jsx-a11y": {
|
||||||
|
"components": {},
|
||||||
|
"attributes": {}
|
||||||
|
},
|
||||||
|
"next": {
|
||||||
|
"rootDir": []
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"formComponents": [],
|
||||||
|
"linkComponents": [],
|
||||||
|
"version": "19.2.0",
|
||||||
|
"componentWrapperFunctions": []
|
||||||
|
},
|
||||||
|
"jsdoc": {
|
||||||
|
"ignorePrivate": false,
|
||||||
|
"ignoreInternal": false,
|
||||||
|
"ignoreReplacesDocs": true,
|
||||||
|
"overrideReplacesDocs": true,
|
||||||
|
"augmentsExtendsReplacesDocs": false,
|
||||||
|
"implementsReplacesDocs": false,
|
||||||
|
"exemptDestructuredRootsFromChecks": false,
|
||||||
|
"tagNamePreference": {}
|
||||||
|
},
|
||||||
|
"vitest": {
|
||||||
|
"typecheck": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"builtin": true
|
||||||
|
},
|
||||||
|
"globals": {},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"src/base/utils/MediaQuery.tsx",
|
||||||
|
"src/features/authentication/AuthManager.ts",
|
||||||
|
"src/features/chapter/services/Chapters.ts",
|
||||||
|
"src/features/metadata/services/MetadataMigrations.ts",
|
||||||
|
"src/features/migration/MigrationManager.ts",
|
||||||
|
"src/features/reader/services/ReaderControls.ts",
|
||||||
|
"src/features/reader/services/ReaderService.ts",
|
||||||
|
"src/features/source/services/Sources.ts",
|
||||||
|
"src/lib/dnd-kit/DndKitUtil.ts",
|
||||||
|
"src/lib/requests/RequestManager.ts",
|
||||||
|
"src/lib/requests/client/GraphQLClient.ts",
|
||||||
|
"src/lib/virtuoso/Virtuoso.util.tsx"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"react/rules-of-hooks": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"**/*.config.ts",
|
||||||
|
"**/*.config.js"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"import/no-default-export": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"tools/scripts/**/*"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-console": "off",
|
||||||
|
"no-relative-import-paths/no-relative-import-paths": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ignorePatterns": [
|
||||||
|
"src/lib/graphql/generated/**",
|
||||||
|
"tools/eslint-plugin-header.cjs"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
- (**Migration**) Keep the migration page open when aborting during the actual migration execution
|
- (**Migration**) Keep the migration page open when aborting during the actual migration execution
|
||||||
- (**Migration**) Prevent concurrent requests to the same tracker
|
- (**Migration**) Prevent concurrent requests to the same tracker
|
||||||
- (**Migration**) Allow only 2 tracker requests per second
|
- (**Migration**) Allow only 2 tracker requests per second
|
||||||
- (**Reader**) Support "produced characters" as hotkeys. E.g. "!", or "/"
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import IconButton from '@mui/material/IconButton';
|
|||||||
import { useQueryParam, StringParam } from 'use-query-params';
|
import { useQueryParam, StringParam } from 'use-query-params';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useLingui } from '@lingui/react/macro';
|
import { useLingui } from '@lingui/react/macro';
|
||||||
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
|
import { CustomTooltip } from '@/base/components/CustomTooltip.tsx';
|
||||||
import { SearchTextField } from '@/base/components/inputs/SearchTextField.tsx';
|
import { SearchTextField } from '@/base/components/inputs/SearchTextField.tsx';
|
||||||
import { SearchParam } from '@/base/Base.types.ts';
|
import { SearchParam } from '@/base/Base.types.ts';
|
||||||
import { useHotkeys } from '@/lib/react-hotkeys-hook/useHotkeys.ts';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isClosable?: boolean;
|
isClosable?: boolean;
|
||||||
@@ -79,9 +79,13 @@ export const AppbarSearch: React.FunctionComponent<IProps> = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useHotkeys('ctrl+f, F3', () => {
|
useHotkeys(
|
||||||
updateSearchOpenState(true);
|
'ctrl+f, F3',
|
||||||
});
|
() => {
|
||||||
|
updateSearchOpenState(true);
|
||||||
|
},
|
||||||
|
{ preventDefault: true },
|
||||||
|
);
|
||||||
|
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useHotkeysContext } from 'react-hotkeys-hook';
|
import { useHotkeys as useHotKeysHook, useHotkeysContext } from 'react-hotkeys-hook';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { HOTKEY_SCOPES } from '@/features/hotkeys/Hotkeys.constants.ts';
|
import { HOTKEY_SCOPES } from '@/features/hotkeys/Hotkeys.constants.ts';
|
||||||
import { ReaderService } from '@/features/reader/services/ReaderService.ts';
|
import { ReaderService } from '@/features/reader/services/ReaderService.ts';
|
||||||
@@ -30,11 +30,10 @@ import {
|
|||||||
getReaderSettingsStore,
|
getReaderSettingsStore,
|
||||||
useReaderSettingsStore,
|
useReaderSettingsStore,
|
||||||
} from '@/features/reader/stores/ReaderStore.ts';
|
} from '@/features/reader/stores/ReaderStore.ts';
|
||||||
import { useHotkeys } from '@/lib/react-hotkeys-hook/useHotkeys.ts';
|
|
||||||
|
|
||||||
const useReaderHotkeys = (...args: Parameters<typeof useHotkeys>): ReturnType<typeof useHotkeys> => {
|
const useHotkeys = (...args: Parameters<typeof useHotKeysHook>): ReturnType<typeof useHotKeysHook> => {
|
||||||
const [keys, callback, options, dependencies] = args;
|
const [keys, callback, options, dependencies] = args;
|
||||||
return useHotkeys(keys, callback, { ...options, ...HOTKEY_SCOPES.reader }, dependencies);
|
return useHotKeysHook(keys, callback, { preventDefault: true, ...options, ...HOTKEY_SCOPES.reader }, dependencies);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSettingCycleThrough = <Setting extends keyof IReaderSettings>(
|
const updateSettingCycleThrough = <Setting extends keyof IReaderSettings>(
|
||||||
@@ -70,9 +69,9 @@ export const ReaderHotkeys = ({
|
|||||||
const hotkeys = useReaderSettingsStore('hotkeys');
|
const hotkeys = useReaderSettingsStore('hotkeys');
|
||||||
const exitReader = ReaderService.useExit();
|
const exitReader = ReaderService.useExit();
|
||||||
|
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.PREVIOUS_PAGE], () => ReaderControls.openPage('previous'));
|
useHotkeys(hotkeys[ReaderHotkey.PREVIOUS_PAGE], () => ReaderControls.openPage('previous'));
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.NEXT_PAGE], () => ReaderControls.openPage('next'));
|
useHotkeys(hotkeys[ReaderHotkey.NEXT_PAGE], () => ReaderControls.openPage('next'));
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.SCROLL_BACKWARD], () => {
|
useHotkeys(hotkeys[ReaderHotkey.SCROLL_BACKWARD], () => {
|
||||||
const autoScroll = getReaderAutoScrollStore();
|
const autoScroll = getReaderAutoScrollStore();
|
||||||
const { readingMode, readingDirection, scrollAmount } = getReaderSettingsStore();
|
const { readingMode, readingDirection, scrollAmount } = getReaderSettingsStore();
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ export const ReaderHotkeys = ({
|
|||||||
scrollAmount,
|
scrollAmount,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.SCROLL_FORWARD], () => {
|
useHotkeys(hotkeys[ReaderHotkey.SCROLL_FORWARD], () => {
|
||||||
const autoScroll = getReaderAutoScrollStore();
|
const autoScroll = getReaderAutoScrollStore();
|
||||||
const { readingMode, readingDirection, scrollAmount } = getReaderSettingsStore();
|
const { readingMode, readingDirection, scrollAmount } = getReaderSettingsStore();
|
||||||
|
|
||||||
@@ -116,20 +115,20 @@ export const ReaderHotkeys = ({
|
|||||||
scrollAmount,
|
scrollAmount,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
useReaderHotkeys(
|
useHotkeys(
|
||||||
hotkeys[ReaderHotkey.PREVIOUS_CHAPTER],
|
hotkeys[ReaderHotkey.PREVIOUS_CHAPTER],
|
||||||
() => ReaderControls.openChapter(getOptionForDirection('previous', 'next', readerThemeDirection)),
|
() => ReaderControls.openChapter(getOptionForDirection('previous', 'next', readerThemeDirection)),
|
||||||
[readerThemeDirection],
|
[readerThemeDirection],
|
||||||
);
|
);
|
||||||
useReaderHotkeys(
|
useHotkeys(
|
||||||
hotkeys[ReaderHotkey.NEXT_CHAPTER],
|
hotkeys[ReaderHotkey.NEXT_CHAPTER],
|
||||||
() => ReaderControls.openChapter(getOptionForDirection('next', 'previous', readerThemeDirection)),
|
() => ReaderControls.openChapter(getOptionForDirection('next', 'previous', readerThemeDirection)),
|
||||||
[readerThemeDirection],
|
[readerThemeDirection],
|
||||||
);
|
);
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.TOGGLE_MENU], () =>
|
useHotkeys(hotkeys[ReaderHotkey.TOGGLE_MENU], () =>
|
||||||
getReaderOverlayStore().setIsVisible(!getReaderOverlayStore().isVisible),
|
getReaderOverlayStore().setIsVisible(!getReaderOverlayStore().isVisible),
|
||||||
);
|
);
|
||||||
useReaderHotkeys(
|
useHotkeys(
|
||||||
hotkeys[ReaderHotkey.CYCLE_SCALE_TYPE],
|
hotkeys[ReaderHotkey.CYCLE_SCALE_TYPE],
|
||||||
() => {
|
() => {
|
||||||
updateSettingCycleThrough(
|
updateSettingCycleThrough(
|
||||||
@@ -142,13 +141,13 @@ export const ReaderHotkeys = ({
|
|||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.STRETCH_IMAGE], () =>
|
useHotkeys(hotkeys[ReaderHotkey.STRETCH_IMAGE], () =>
|
||||||
ReaderService.updateSetting('shouldStretchPage', !getReaderSettingsStore().shouldStretchPage.value),
|
ReaderService.updateSetting('shouldStretchPage', !getReaderSettingsStore().shouldStretchPage.value),
|
||||||
);
|
);
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.OFFSET_SPREAD_PAGES], () =>
|
useHotkeys(hotkeys[ReaderHotkey.OFFSET_SPREAD_PAGES], () =>
|
||||||
ReaderService.setOffsetDoubleSpreads(!getReaderSettingsStore().shouldOffsetDoubleSpreads.value),
|
ReaderService.setOffsetDoubleSpreads(!getReaderSettingsStore().shouldOffsetDoubleSpreads.value),
|
||||||
);
|
);
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_MODE], () => {
|
useHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_MODE], () => {
|
||||||
updateSettingCycleThrough(
|
updateSettingCycleThrough(
|
||||||
'readingMode',
|
'readingMode',
|
||||||
getReaderSettingsStore().readingMode.value,
|
getReaderSettingsStore().readingMode.value,
|
||||||
@@ -157,7 +156,7 @@ export const ReaderHotkeys = ({
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_DIRECTION], () => {
|
useHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_DIRECTION], () => {
|
||||||
updateSettingCycleThrough(
|
updateSettingCycleThrough(
|
||||||
'readingDirection',
|
'readingDirection',
|
||||||
getReaderSettingsStore().readingDirection.value,
|
getReaderSettingsStore().readingDirection.value,
|
||||||
@@ -166,20 +165,20 @@ export const ReaderHotkeys = ({
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.TOGGLE_AUTO_SCROLL], () => getReaderAutoScrollStore().toggleActive(), {});
|
useHotkeys(hotkeys[ReaderHotkey.TOGGLE_AUTO_SCROLL], () => getReaderAutoScrollStore().toggleActive(), {});
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_DECREASE], () =>
|
useHotkeys(hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_DECREASE], () =>
|
||||||
ReaderService.updateSetting('autoScroll', {
|
ReaderService.updateSetting('autoScroll', {
|
||||||
...getReaderSettingsStore().autoScroll,
|
...getReaderSettingsStore().autoScroll,
|
||||||
value: Math.min(AUTO_SCROLL_SPEED.max, getReaderSettingsStore().autoScroll.value + AUTO_SCROLL_SPEED.step),
|
value: Math.min(AUTO_SCROLL_SPEED.max, getReaderSettingsStore().autoScroll.value + AUTO_SCROLL_SPEED.step),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_INCREASE], () =>
|
useHotkeys(hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_INCREASE], () =>
|
||||||
ReaderService.updateSetting('autoScroll', {
|
ReaderService.updateSetting('autoScroll', {
|
||||||
...getReaderSettingsStore().autoScroll,
|
...getReaderSettingsStore().autoScroll,
|
||||||
value: Math.max(AUTO_SCROLL_SPEED.min, getReaderSettingsStore().autoScroll.value - AUTO_SCROLL_SPEED.step),
|
value: Math.max(AUTO_SCROLL_SPEED.min, getReaderSettingsStore().autoScroll.value - AUTO_SCROLL_SPEED.step),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
useReaderHotkeys(hotkeys[ReaderHotkey.EXIT_READER], exitReader, [exitReader]);
|
useHotkeys(hotkeys[ReaderHotkey.EXIT_READER], exitReader, [exitReader]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
enableScope(HotkeyScope.READER);
|
enableScope(HotkeyScope.READER);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import { useRecordHotkeys } from 'react-hotkeys-hook';
|
import { useRecordHotkeys } from 'react-hotkeys-hook';
|
||||||
import Dialog from '@mui/material/Dialog';
|
import Dialog from '@mui/material/Dialog';
|
||||||
@@ -17,7 +17,6 @@ import Button from '@mui/material/Button';
|
|||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { Trans, useLingui } from '@lingui/react/macro';
|
import { Trans, useLingui } from '@lingui/react/macro';
|
||||||
import { Hotkey } from '@/features/reader/hotkeys/settings/components/Hotkey.tsx';
|
import { Hotkey } from '@/features/reader/hotkeys/settings/components/Hotkey.tsx';
|
||||||
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
|
|
||||||
|
|
||||||
export const RecordHotkey = ({
|
export const RecordHotkey = ({
|
||||||
onClose,
|
onClose,
|
||||||
@@ -30,10 +29,7 @@ export const RecordHotkey = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
|
||||||
const [recordPhysicalKeys, setRecordPhysicalKeys] = useState(true);
|
const [recordedKeys, { start, stop, resetKeys }] = useRecordHotkeys();
|
||||||
|
|
||||||
const [recordedKeys, { start, stop, resetKeys }] = useRecordHotkeys(!recordPhysicalKeys);
|
|
||||||
|
|
||||||
const keys = [[...recordedKeys].join('+')];
|
const keys = [[...recordedKeys].join('+')];
|
||||||
const isExistingKey = keys.some((key) =>
|
const isExistingKey = keys.some((key) =>
|
||||||
existingKeys.map((existingKey) => existingKey.toLowerCase()).includes(key.toLowerCase()),
|
existingKeys.map((existingKey) => existingKey.toLowerCase()).includes(key.toLowerCase()),
|
||||||
@@ -45,7 +41,7 @@ export const RecordHotkey = ({
|
|||||||
return () => {
|
return () => {
|
||||||
stop();
|
stop();
|
||||||
};
|
};
|
||||||
}, [start, stop]);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open onClose={onClose} fullWidth>
|
<Dialog open onClose={onClose} fullWidth>
|
||||||
@@ -58,32 +54,6 @@ export const RecordHotkey = ({
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Stack>
|
</Stack>
|
||||||
{isExistingKey && <Typography color="error">{t`Hotkey already exists`}</Typography>}
|
{isExistingKey && <Typography color="error">{t`Hotkey already exists`}</Typography>}
|
||||||
<CheckboxInput
|
|
||||||
label={
|
|
||||||
<Stack
|
|
||||||
sx={{
|
|
||||||
// Padding comes from the MUI Checkbox component
|
|
||||||
pt: '9px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography>{t`Record physical keys`}</Typography>
|
|
||||||
<Typography variant="body2" color="textSecondary" sx={{ display: 'flex', gap: 1 }}>
|
|
||||||
<Trans>
|
|
||||||
enabled: records <Hotkey keys={['shift+1']} /> when the user presses Shift+1
|
|
||||||
</Trans>
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="body2" color="textSecondary" sx={{ display: 'flex', gap: 1 }}>
|
|
||||||
<Trans>
|
|
||||||
disabled: records <Hotkey keys={['!']} /> when the user presses Shift+1 on a US
|
|
||||||
layout
|
|
||||||
</Trans>
|
|
||||||
</Typography>
|
|
||||||
</Stack>
|
|
||||||
}
|
|
||||||
checked={recordPhysicalKeys}
|
|
||||||
onChange={(_, checked) => setRecordPhysicalKeys(checked)}
|
|
||||||
sx={{ mt: 2, alignItems: 'start' }}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Stack
|
<Stack
|
||||||
|
|||||||
@@ -1329,10 +1329,6 @@ msgstr "Disable authentication"
|
|||||||
msgid "Disabled"
|
msgid "Disabled"
|
||||||
msgstr "Disabled"
|
msgstr "Disabled"
|
||||||
|
|
||||||
#: src/features/reader/hotkeys/settings/components/RecordHotkey.tsx
|
|
||||||
msgid "disabled: records <0/> when the user presses Shift+1 on a US layout"
|
|
||||||
msgstr "disabled: records <0/> when the user presses Shift+1 on a US layout"
|
|
||||||
|
|
||||||
#: src/features/settings/components/koreaderSync/KoreaderSyncSettings.tsx
|
#: src/features/settings/components/koreaderSync/KoreaderSyncSettings.tsx
|
||||||
msgid "Disconnect from KOReader Sync server"
|
msgid "Disconnect from KOReader Sync server"
|
||||||
msgstr "Disconnect from KOReader Sync server"
|
msgstr "Disconnect from KOReader Sync server"
|
||||||
@@ -1504,10 +1500,6 @@ msgstr "Enable debug logs"
|
|||||||
msgid "Enable page read progress"
|
msgid "Enable page read progress"
|
||||||
msgstr "Enable page read progress"
|
msgstr "Enable page read progress"
|
||||||
|
|
||||||
#: src/features/reader/hotkeys/settings/components/RecordHotkey.tsx
|
|
||||||
msgid "enabled: records <0/> when the user presses Shift+1"
|
|
||||||
msgstr "enabled: records <0/> when the user presses Shift+1"
|
|
||||||
|
|
||||||
#: src/features/downloads/screens/DownloadSettings.tsx
|
#: src/features/downloads/screens/DownloadSettings.tsx
|
||||||
msgid "Entries in excluded categories will not be downloaded even if they are also in included categories"
|
msgid "Entries in excluded categories will not be downloaded even if they are also in included categories"
|
||||||
msgstr "Entries in excluded categories will not be downloaded even if they are also in included categories"
|
msgstr "Entries in excluded categories will not be downloaded even if they are also in included categories"
|
||||||
@@ -2787,10 +2779,6 @@ msgstr "Recently read"
|
|||||||
msgid "Record keybind"
|
msgid "Record keybind"
|
||||||
msgstr "Record keybind"
|
msgstr "Record keybind"
|
||||||
|
|
||||||
#: src/features/reader/hotkeys/settings/components/RecordHotkey.tsx
|
|
||||||
msgid "Record physical keys"
|
|
||||||
msgstr "Record physical keys"
|
|
||||||
|
|
||||||
#. placeholder {0}: recordedKeys.size ? <Hotkey keys={keys} /> : <Typography>{t`Press keys`}</Typography>
|
#. placeholder {0}: recordedKeys.size ? <Hotkey keys={keys} /> : <Typography>{t`Press keys`}</Typography>
|
||||||
#: src/features/reader/hotkeys/settings/components/RecordHotkey.tsx
|
#: src/features/reader/hotkeys/settings/components/RecordHotkey.tsx
|
||||||
msgid "Recorded hotkeys: {0}"
|
msgid "Recorded hotkeys: {0}"
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// oxlint-disable-next-line no-restricted-imports
|
|
||||||
import { useHotkeys as orgUseHotkeys } from 'react-hotkeys-hook';
|
|
||||||
|
|
||||||
export const useHotkeys = (...args: Parameters<typeof orgUseHotkeys>): ReturnType<typeof orgUseHotkeys> => {
|
|
||||||
const [keys, callback, options, dependencies] = args;
|
|
||||||
return orgUseHotkeys(
|
|
||||||
keys,
|
|
||||||
callback,
|
|
||||||
{
|
|
||||||
preventDefault: true,
|
|
||||||
useKey: true,
|
|
||||||
...options,
|
|
||||||
},
|
|
||||||
dependencies,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user