Migrate to oxlint + oxfmt

This commit is contained in:
schroda
2026-03-10 22:28:19 +01:00
parent 98622ad6cd
commit 5dda4304d9
40 changed files with 1232 additions and 1315 deletions

1
.gitignore vendored
View File

@@ -8,3 +8,4 @@ build/*
dev-dist/* dev-dist/*
src/lib/graphql/schema.json src/lib/graphql/schema.json
.oxlintrc.jsonc

10
.oxfmtrc.json Normal file
View File

@@ -0,0 +1,10 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"ignorePatterns": ["src/lib/graphql/generated/**", ".github/**"]
}

483
.oxlintrc.jsonc Normal file
View File

@@ -0,0 +1,483 @@
{
"plugins": [
"react",
"typescript",
"import",
"jsx-a11y"
],
"jsPlugins": [
"eslint-plugin-lingui",
"eslint-plugin-no-relative-import-paths"
],
"categories": {
"correctness": "off"
},
"rules": {
// ── Core JS: correctness (from js.configs.recommended) ──
"constructor-super": "error",
"for-direction": "error",
"getter-return": "error",
"no-async-promise-executor": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": "error",
"no-const-assign": "error",
"no-constant-binary-expression": "error",
"no-constant-condition": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-class-members": "error",
"no-dupe-else-if": "error",
"no-dupe-keys": "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-fallthrough": "error",
"no-func-assign": "error",
"no-global-assign": "error",
"no-import-assign": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-new-native-nonconstructor": "error",
"no-nonoctal-decimal-escape": "error",
"no-obj-calls": "error",
"no-prototype-builtins": "error",
"no-redeclare": "error",
"no-regex-spaces": "error",
"no-self-assign": "error",
"no-setter-return": "error",
"no-shadow-restricted-names": "error",
"no-sparse-arrays": "error",
"no-this-before-super": "error",
"no-undef": "error",
"no-unreachable": "error",
"no-unsafe-finally": "error",
"no-unsafe-negation": "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",
"valid-typeof": "error",
"no-array-constructor": "error",
"no-unused-expressions": "error",
// ── Core JS: Airbnb-style rules ──
"no-console": "warn",
"no-nested-ternary": "error",
"no-else-return": [
"error",
{
"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-proto": "error",
"no-extend-native": "error",
"no-caller": "error",
"no-alert": "warn",
"no-void": "error",
"no-bitwise": "error",
"no-continue": "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"
}
],
"guard-for-in": "error",
"default-case": [
"error",
{
"commentPattern": "^no default$"
}
],
"default-case-last": "error",
"radix": "error",
"yoda": "error",
"max-classes-per-file": [
"error",
1
],
"prefer-template": "error",
"prefer-destructuring": [
"error",
{
"VariableDeclarator": {
"array": false,
"object": true
},
"AssignmentExpression": {
"array": true,
"object": false
}
},
{
"enforceForRenamedProperties": false
}
],
"prefer-exponentiation-operator": "error",
"prefer-object-spread": "error",
"prefer-promise-reject-errors": [
"error",
{
"allowEmptyReject": true
}
],
"no-param-reassign": [
"error",
{
"props": true,
"ignorePropertyModificationsForRegex": [
"^draft"
]
}
],
"no-plusplus": [
"error",
{
"allowForLoopAfterthoughts": true
}
],
"no-restricted-imports": [
"error",
{
"patterns": [
{
"group": [
"@mui/*",
"!@mui/material/",
"!@mui/icons-material/",
"!@mui/x-date-pickers/"
]
},
{
"group": [
"@mui/*/*/*"
]
}
]
}
],
// ── 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",
// ── 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": "warn",
"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",
// ── Lingui (via jsPlugin) ──
"lingui/t-call-in-function": "error",
"lingui/no-single-tag-to-translate": "warn",
"lingui/no-single-variables-to-translate": "warn",
"lingui/no-trans-inside-trans": "warn",
"lingui/no-expression-in-message": "off",
// ── Header — handled by minimal ESLint fallback ──
// ── No relative import paths (via jsPlugin) ──
"no-relative-import-paths/no-relative-import-paths": [
"error",
{
"rootDir": "src",
"prefix": "@"
}
],
// ── 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-direct-mutation-state": "error",
"react/no-find-dom-node": "error",
"react/no-is-mounted": "error",
"react/no-render-return-value": "error",
"react/no-string-refs": "error",
"react/no-unescaped-entities": "error",
"react/no-unknown-property": "error",
"react/require-render-return": "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": "warn",
"react/no-this-in-sfc": "error",
"react/state-in-constructor": [
"error",
"always"
],
// ── 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"
},
"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/components/settings/NumberSetting.tsx",
"src/base/utils/MediaQuery.tsx",
"src/features/authentication/AuthManager.ts",
"src/features/browse/extensions/components/ExtensionCard.tsx",
"src/features/chapter/services/Chapters.ts",
"src/features/extension/info/components/ActionButton.tsx",
"src/features/manga/components/TrackMangaButton.tsx",
"src/features/manga/services/Mangas.ts",
"src/features/metadata/services/MetadataMigrations.ts",
"src/features/migration/components/MigrateDialog.tsx",
"src/features/navigation-bar/components/MobileBottomBar.tsx",
"src/features/reader/services/ReaderControls.ts",
"src/features/reader/services/ReaderService.ts",
"src/features/settings/screens/ImageProcessingSetting.tsx",
"src/features/source/browse/components/SourceOptions.tsx",
"src/features/source/browse/components/filters/SelectFilter.tsx",
"src/features/source/services/Sources.ts",
"src/lib/dayjs/LocaleImporter.ts",
"src/lib/dnd-kit/DndKitUtil.ts",
"src/lib/requests/RequestManager.ts",
"src/lib/requests/client/GraphQLClient.ts",
"src/lib/service-worker/ImageCache.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/**"
]
}

View File

@@ -1,7 +0,0 @@
{
"tabWidth": 4,
"singleQuote": true,
"printWidth": 120,
"semi": true,
"trailingComma": "all"
}

View File

@@ -7,12 +7,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased] (Preview) ## [Unreleased] (Preview)
### Added ### Added
- (**Theme**) Add an option to save the dynamic color theme on the manga page as a custom theme - (**Theme**) Add an option to save the dynamic color theme on the manga page as a custom theme
- (**WebUI Update**) Add an option to (partially) disable showing information when the webUI got updated - (**WebUI Update**) Add an option to (partially) disable showing information when the webUI got updated
- (**Server Update**) Add an option to disable showing information when the server got updated - (**Server Update**) Add an option to disable showing information when the server got updated
- (**Manga**) Add an option to include client data during migration - (**Manga**) Add an option to include client data during migration
### Changed ### Changed
- (**General**) Preserve refresh token (UI Login) over sessions - (**General**) Preserve refresh token (UI Login) over sessions
- (**General**) Reduce base font size - (**General**) Reduce base font size
- (**General**) Save selected app language on the server - (**General**) Save selected app language on the server
@@ -24,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**Browse**) Merge languages filter of the sources and extensions into one - (**Browse**) Merge languages filter of the sources and extensions into one
### Fixed ### Fixed
- (**General**) Fix saving large client data on the server (e.g., custom source filters) - (**General**) Fix saving large client data on the server (e.g., custom source filters)
- (**Library**) Fix total library size chip color in light mode - (**Library**) Fix total library size chip color in light mode
- (**Browse**) Fix missing pinned sources in the source language filter - (**Browse**) Fix missing pinned sources in the source language filter
@@ -34,6 +37,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [20251230.01] (r2937) - 2025-12-30 ## [20251230.01] (r2937) - 2025-12-30
### Added ### Added
- (**Navigation**) Show extension update information in app navigation - (**Navigation**) Show extension update information in app navigation
- (**General**) Add support for "ui login" authentication mode - (**General**) Add support for "ui login" authentication mode
- (**General**) Add support for hosting on subpaths - (**General**) Add support for hosting on subpaths
@@ -50,6 +54,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**Browse**) Add "open in webview" button in source browse page - (**Browse**) Add "open in webview" button in source browse page
### Changed ### Changed
- (**General**) Improve loading of images - (**General**) Improve loading of images
- (**Category**) Prevent creating categories without a name - (**Category**) Prevent creating categories without a name
- (**WebUI Update**) Do not require a forced page refresh when an update has been detected in case the app just got opened - (**WebUI Update**) Do not require a forced page refresh when an update has been detected in case the app just got opened
@@ -57,6 +62,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**Settings**) Move "Clear cache" setting to new "Images" setting page - (**Settings**) Move "Clear cache" setting to new "Images" setting page
### Fixed ### Fixed
- (**General**) Fix tooltips sometimes causing a layout shift - (**General**) Fix tooltips sometimes causing a layout shift
- (**General**) Fix back button under some specific conditions (e.g., `library category X``mange``reader``manga` → back button → `library`; should have opened `library category X`) - (**General**) Fix back button under some specific conditions (e.g., `library category X``mange``reader``manga` → back button → `library`; should have opened `library category X`)
- (**Manga**) Fix failing migration with disabled "tracking" data - (**Manga**) Fix failing migration with disabled "tracking" data
@@ -79,14 +85,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- (**Reader**) Fix broken scrolling in continuous horizontal reading mode - (**Reader**) Fix broken scrolling in continuous horizontal reading mode
### Translations ### Translations
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/) Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/)
Thanks to everyone that contributed to the translation of this project. Thanks to everyone that contributed to the translation of this project.
#### Added #### Added
- Russian (by Micka149) - Russian (by Micka149)
#### Updated #### Updated
- Tamil (by தமிழ்நேரம்) - Tamil (by தமிழ்நேரம்)
- Polish (by UnknownSkyrimPasserby) - Polish (by UnknownSkyrimPasserby)
- Korean (by Kim KKAng, jun) - Korean (by Kim KKAng, jun)
@@ -102,6 +111,7 @@ Thanks to everyone that contributed to the translation of this project.
- Turkish (by Metin) - Turkish (by Metin)
### Contributors ### Contributors
Thanks to everyone that contributed to this release Thanks to everyone that contributed to this release
@schroda, @github-actions[bot], @weblate, @cpiber, @ginocic, @infyProductions, @Metincloup, @Micka149, @dejavui, @TheRay82, @plum7x, @aizhimoran, @zeedif, @AdrianRSouza, @ritma-bws, @CBujeda, @rogervc, @UnknownSkyrimPasserby, @mrintrepide, @q1114938967, @sksper1-stack, @renjfk, @Smileskun, @bornav, @9811pc, @imammansyur, @cho558, @marimo-nekomimi, @Dusky-dev @schroda, @github-actions[bot], @weblate, @cpiber, @ginocic, @infyProductions, @Metincloup, @Micka149, @dejavui, @TheRay82, @plum7x, @aizhimoran, @zeedif, @AdrianRSouza, @ritma-bws, @CBujeda, @rogervc, @UnknownSkyrimPasserby, @mrintrepide, @q1114938967, @sksper1-stack, @renjfk, @Smileskun, @bornav, @9811pc, @imammansyur, @cho558, @marimo-nekomimi, @Dusky-dev
@@ -109,9 +119,11 @@ Thanks to everyone that contributed to this release
## [20250801.01] (r2717) - 2025-08-01 ## [20250801.01] (r2717) - 2025-08-01
### Fixed ### Fixed
- (**General**) Fix white screen on page load - (**General**) Fix white screen on page load
### Contributors ### Contributors
Thanks to everyone that contributed to this release Thanks to everyone that contributed to this release
@schroda @schroda
@@ -119,6 +131,7 @@ Thanks to everyone that contributed to this release
## [20250731.01] (r2715) - 2025-07-31 ## [20250731.01] (r2715) - 2025-07-31
### Added ### Added
- (**General**) Add support for the suwayomi WebView - (**General**) Add support for the suwayomi WebView
- (**Settings**) Add new OPDS server settings - (**Settings**) Add new OPDS server settings
- (**Settings**) Add new "simple login" authentication setting - (**Settings**) Add new "simple login" authentication setting
@@ -128,6 +141,7 @@ Thanks to everyone that contributed to this release
- (**Manga**) Add support for private track bindings - (**Manga**) Add support for private track bindings
### Fixed ### Fixed
- (**General**) Fix drag and drop on touch devices - (**General**) Fix drag and drop on touch devices
- (**Reader**) Fix auto scrolling with static overlay - (**Reader**) Fix auto scrolling with static overlay
- (**Extension**) Fix clicking on action button (install, uninstall, update, ...) opening the extension info page - (**Extension**) Fix clicking on action button (install, uninstall, update, ...) opening the extension info page
@@ -136,11 +150,13 @@ Thanks to everyone that contributed to this release
- (**Chapter**) Fix updating read status of already read chapters when using the mark previous as read option - (**Chapter**) Fix updating read status of already read chapters when using the mark previous as read option
### Translations ### Translations
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/) Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/)
Thanks to everyone that contributed to the translation of this project. Thanks to everyone that contributed to the translation of this project.
#### Updated #### Updated
- Vietnamese (by Nguyễn Trung Đức) - Vietnamese (by Nguyễn Trung Đức)
- Chinese (Simplified) (by 清水汐音) - Chinese (Simplified) (by 清水汐音)
- Japanese (by 9811pc) - Japanese (by 9811pc)
@@ -148,6 +164,7 @@ Thanks to everyone that contributed to the translation of this project.
- German (by Constantin Piber) - German (by Constantin Piber)
### Contributors ### Contributors
Thanks to everyone that contributed to this release Thanks to everyone that contributed to this release
@weblate, @cpiber, @EugeneCage, @dejavui, @UnknownSkyrimPasserby, @aizhimoran, @schroda, @9811pc, @AlirezaGh1993, @gianlucalauro, @junmusk, @JiPaix, @leollo98, @LycusCoder, @Sawadikhap, @yutthaphon, @Oxara, @Mamotromico, @plum7x, @jintaxi, @TamilNeram, @shirishsaxena @weblate, @cpiber, @EugeneCage, @dejavui, @UnknownSkyrimPasserby, @aizhimoran, @schroda, @9811pc, @AlirezaGh1993, @gianlucalauro, @junmusk, @JiPaix, @leollo98, @LycusCoder, @Sawadikhap, @yutthaphon, @Oxara, @Mamotromico, @plum7x, @jintaxi, @TamilNeram, @shirishsaxena
@@ -155,6 +172,7 @@ Thanks to everyone that contributed to this release
## [20250703.01] (r2643) - 2025-07-03 ## [20250703.01] (r2643) - 2025-07-03
### Added ### Added
- (**Manga**) Add functionality to click on title/artist/author/genre to trigger library/source/global search - (**Manga**) Add functionality to click on title/artist/author/genre to trigger library/source/global search
- (**Library**) Add option to perform current search globally - (**Library**) Add option to perform current search globally
- (**Source**) Add option to disable sources of an extension - (**Source**) Add option to disable sources of an extension
@@ -174,6 +192,7 @@ Thanks to everyone that contributed to this release
- (**Chapter**) Show missing chapters info between chapters in the chapter list - (**Chapter**) Show missing chapters info between chapters in the chapter list
### Changed ### Changed
- (**Manga**) Require confirmation before removing an entry from your library - (**Manga**) Require confirmation before removing an entry from your library
- (**Chapter**) Require confirmation for the following actions - (**Chapter**) Require confirmation for the following actions
- Download - Download
@@ -200,9 +219,11 @@ Thanks to everyone that contributed to this release
- (**Setting**) Prevent setting up basic auth with both an empty username and password. This is an issue on iOS whose native basic auth prompt disables the "login" button in case both fields are empty. - (**Setting**) Prevent setting up basic auth with both an empty username and password. This is an issue on iOS whose native basic auth prompt disables the "login" button in case both fields are empty.
### Removed ### Removed
- (**Source**) Remove "popular" button on source card - (**Source**) Remove "popular" button on source card
### Fixed ### Fixed
- (**General**) Fix custom long press causing native mobile long press menu to get opened - (**General**) Fix custom long press causing native mobile long press menu to get opened
- (**General**) Fix refreshing data after importing a backup - (**General**) Fix refreshing data after importing a backup
- (**Reader**) Fix each key press triggering a keybind (example: "n" → next page, "ctrl+n" → next chapter - previously "ctrl+n" would have triggered both keybinds) - (**Reader**) Fix each key press triggering a keybind (example: "n" → next page, "ctrl+n" → next chapter - previously "ctrl+n" would have triggered both keybinds)
@@ -221,14 +242,17 @@ Thanks to everyone that contributed to this release
- (**Download**) Fix downloads start/stop button not updating - (**Download**) Fix downloads start/stop button not updating
### Translations ### Translations
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/) Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/)
Thanks to everyone that contributed to the translation of this project. Thanks to everyone that contributed to the translation of this project.
#### Added #### Added
- Portuguese (Brazil) (by Psico, Jorge Adriano Cavalcante Alves) - Portuguese (Brazil) (by Psico, Jorge Adriano Cavalcante Alves)
#### Updated #### Updated
- Chinese (Traditional) (by plum7x) - Chinese (Traditional) (by plum7x)
- Vietnamese (by Nguyễn Trung Đức) - Vietnamese (by Nguyễn Trung Đức)
- Italian (by xAizawa) - Italian (by xAizawa)
@@ -242,6 +266,7 @@ Thanks to everyone that contributed to the translation of this project.
- German (by Constantin Piber) - German (by Constantin Piber)
### Contributors ### Contributors
Thanks to everyone that contributed to this release Thanks to everyone that contributed to this release
@weblate, @9811pc, @cpiber, @yutthaphon, @dejavui, @UnknownSkyrimPasserby, @dpkass, @marimo-nekomimi, @TamilNeram, @aizhimoran, @schroda, @junmusk, @adriano1816, @Zereef, @plum7x, @FedericoRossiIT, @Well2333, @ketw09, @IDika31, @kosmik7, @letroll, @KrachDev, @MageSneaky, @Tankudoraiba, @tizio04, @xAizawa @weblate, @9811pc, @cpiber, @yutthaphon, @dejavui, @UnknownSkyrimPasserby, @dpkass, @marimo-nekomimi, @TamilNeram, @aizhimoran, @schroda, @junmusk, @adriano1816, @Zereef, @plum7x, @FedericoRossiIT, @Well2333, @ketw09, @IDika31, @kosmik7, @letroll, @KrachDev, @MageSneaky, @Tankudoraiba, @tizio04, @xAizawa
@@ -249,17 +274,21 @@ Thanks to everyone that contributed to this release
## [1.5.1] (r2467) - 2025-04-07 ## [1.5.1] (r2467) - 2025-04-07
### Fixed ### Fixed
- (**Server update**) Fix detection of available server update - (**Server update**) Fix detection of available server update
### Translations ### Translations
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/) Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/)
Thanks to everyone that contributed to the translation of this project. Thanks to everyone that contributed to the translation of this project.
#### Updated #### Updated
- Polish (by UnknownSkyrimPasserby) - Polish (by UnknownSkyrimPasserby)
### Contributors ### Contributors
Thanks to everyone that contributed to this release Thanks to everyone that contributed to this release
@schroda, @weblate, @UnknownSkyrimPasserby @schroda, @weblate, @UnknownSkyrimPasserby
@@ -267,13 +296,16 @@ Thanks to everyone that contributed to this release
## [1.5.0] (r2461) - 2025-04-05 ## [1.5.0] (r2461) - 2025-04-05
> ### Required action > ### Required action
>
> - If you have installed the web app as a PWA you need to reinstall the app after it has been updated to the latest version > - If you have installed the web app as a PWA you need to reinstall the app after it has been updated to the latest version
### Highlights ### Highlights
- Completely new reader - Completely new reader
- Use predefined UI themes or create your own - Use predefined UI themes or create your own
### Added ### Added
- (**General**) Add labels to the navigation side bar icons - (**General**) Add labels to the navigation side bar icons
- (**General**) Add option to expand and collapse the navigation side bar - (**General**) Add option to expand and collapse the navigation side bar
- (**General**) Update app styling - (**General**) Update app styling
@@ -336,6 +368,7 @@ Thanks to everyone that contributed to this release
- (**Migrate**) Add sort options for migratable sources (by source name, manga count; order ascending/descending) - (**Migrate**) Add sort options for migratable sources (by source name, manga count; order ascending/descending)
### Changed ### Changed
- (**General**) Introduce "More" page - (**General**) Introduce "More" page
- Remove "Settings" from navigation bar - Remove "Settings" from navigation bar
- Remove "Downloads" from mobile navigation bar - Remove "Downloads" from mobile navigation bar
@@ -350,9 +383,11 @@ Thanks to everyone that contributed to this release
- (**Settings**) Move link to "Category settings" to "library settings" and "More" page - (**Settings**) Move link to "Category settings" to "library settings" and "More" page
### Removed ### Removed
- (**Settings**) Remove "graphql debug level" setting (was removed by the server) - (**Settings**) Remove "graphql debug level" setting (was removed by the server)
### Fixed ### Fixed
- (**General**) Fix setting browser locale for used date library (some supported browser locales were not correctly detected) - (**General**) Fix setting browser locale for used date library (some supported browser locales were not correctly detected)
- (**General**) Fix server subscriptions not updating to new server after changing the server address - (**General**) Fix server subscriptions not updating to new server after changing the server address
- (**General**) Fix missing polyfill to support older browsers - (**General**) Fix missing polyfill to support older browsers
@@ -417,15 +452,18 @@ Thanks to everyone that contributed to this release
- (**Category**) Fix missing error message on category update/creation failure - (**Category**) Fix missing error message on category update/creation failure
### Translations ### Translations
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/) Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/)
Thanks to everyone that contributed to the translation of this project. Thanks to everyone that contributed to the translation of this project.
#### Added #### Added
- Tamil (by தமிழ்நேரம்) - Tamil (by தமிழ்நேரம்)
- Polish (by UnknownSkyrimPasserby) - Polish (by UnknownSkyrimPasserby)
#### Updated #### Updated
- Chinese (Simplified) (by 清水汐音, Joshua Astray) - Chinese (Simplified) (by 清水汐音, Joshua Astray)
- Chinese (Traditional) (by plum7x, aaron mo) - Chinese (Traditional) (by plum7x, aaron mo)
- French (by Jean-Philippe ALLEGRO, Tycoon3819) - French (by Jean-Philippe ALLEGRO, Tycoon3819)
@@ -437,6 +475,7 @@ Thanks to everyone that contributed to the translation of this project.
- Vietnamese (by Nguyễn Trung Đức, PandaKewt) - Vietnamese (by Nguyễn Trung Đức, PandaKewt)
#### Removed (less than 75% translated) #### Removed (less than 75% translated)
- "Arabic (ar)" (71.7%) - "Arabic (ar)" (71.7%)
- "Bengali (bn)" (1.8%) - "Bengali (bn)" (1.8%)
- "Danish (da)" (13.6%) - "Danish (da)" (13.6%)
@@ -455,6 +494,7 @@ Thanks to everyone that contributed to the translation of this project.
- "Cantonese (Traditional Han script) (yue-Hant)" (0%) - "Cantonese (Traditional Han script) (yue-Hant)" (0%)
### Contributors ### Contributors
Thanks to everyone that contributed to this release Thanks to everyone that contributed to this release
@schroda, @weblate, @cpiber, @infyProductions, @leollo98, @dejavui, @UnknownSkyrimPasserby, @plum7x, @aizhimoran, @ykxkykx, @HanJoJ, @jicedtea, @Daviid-P, @kalebC, @TamilNeram, @Duoduo12138, @crystailx, @Banchon999, @jesusFx, @Runkandel, @lengzero, @tizio04, @zeedif, @Wybxc, @Mamotromico, @CladZo91, @senseimon1, @krizopraz, @mipo89-dev, @Robonau, @Meliodas-Sama, @991n1nd12, @RikCost, @JiPaix, @LycusCoder, @Oxara, @PandaKewt, @BrutuZ, @Yinr, @leqord, @RickyLam11, @Hada45, @gianlucalauro, @lucaschristofaro, @marimo-nekomimi @schroda, @weblate, @cpiber, @infyProductions, @leollo98, @dejavui, @UnknownSkyrimPasserby, @plum7x, @aizhimoran, @ykxkykx, @HanJoJ, @jicedtea, @Daviid-P, @kalebC, @TamilNeram, @Duoduo12138, @crystailx, @Banchon999, @jesusFx, @Runkandel, @lengzero, @tizio04, @zeedif, @Wybxc, @Mamotromico, @CladZo91, @senseimon1, @krizopraz, @mipo89-dev, @Robonau, @Meliodas-Sama, @991n1nd12, @RikCost, @JiPaix, @LycusCoder, @Oxara, @PandaKewt, @BrutuZ, @Yinr, @leqord, @RickyLam11, @Hada45, @gianlucalauro, @lucaschristofaro, @marimo-nekomimi
@@ -462,12 +502,14 @@ Thanks to everyone that contributed to this release
## [1.1.0] (r1689) - 2024-06-14 ## [1.1.0] (r1689) - 2024-06-14
### Highlights ### Highlights
- Tracking support - Tracking support
- Different UI settings per device (e.g. reader settings) - Different UI settings per device (e.g. reader settings)
- Save searches in source browse - Save searches in source browse
- Chapter download selection improvement - Chapter download selection improvement
### Added ### Added
- (**Internationalization**) Apply right to left styling for languages that are read right to left - (**Internationalization**) Apply right to left styling for languages that are read right to left
- (**Library**) New setting to remove manga from categories when removing them from the library - (**Library**) New setting to remove manga from categories when removing them from the library
- (**Library**) Filter library for manga that have bookmarked chapters - (**Library**) Filter library for manga that have bookmarked chapters
@@ -484,7 +526,7 @@ Thanks to everyone that contributed to this release
- From library - From library
- (**Manga**) Copy manga title on long press in manga page - (**Manga**) Copy manga title on long press in manga page
- (**Settings**) UI specific settings that are stored on the server are now saved per device (devices can be managed in the settings) - (**Settings**) UI specific settings that are stored on the server are now saved per device (devices can be managed in the settings)
- A device name is allowed to have 16 chars (a-Z, 0-9, -, _) (e.g. "My_Phone-1") - A device name is allowed to have 16 chars (a-Z, 0-9, -, \_) (e.g. "My_Phone-1")
- Device specific settings - Device specific settings
- Reader - Reader
- Default settings - Default settings
@@ -527,12 +569,14 @@ Thanks to everyone that contributed to this release
- Refreshes the tab on close - Refreshes the tab on close
### Changed ### Changed
- (**Library**) The category selection dialog is not shown when adding a manga to the library without having created categories - (**Library**) The category selection dialog is not shown when adding a manga to the library without having created categories
- (**Library**) The current library manga selection now gets unselected after triggering an action - (**Library**) The current library manga selection now gets unselected after triggering an action
- (**Manga**) The continue read/resume button now uses the first unread chapter as the resume point - (**Manga**) The continue read/resume button now uses the first unread chapter as the resume point
- (**Download**) The download queue clear button is now always enabled - (**Download**) The download queue clear button is now always enabled
### Fixed ### Fixed
- (**General**) Fix white screen on older browsers - (**General**) Fix white screen on older browsers
- (**General**) Fix old data still being shown after - (**General**) Fix old data still being shown after
- Changing the server url - Changing the server url
@@ -564,11 +608,13 @@ Thanks to everyone that contributed to this release
- (**Manga grid**) Fix jittering/flickering of manga grid items - (**Manga grid**) Fix jittering/flickering of manga grid items
### Translations ### Translations
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/) Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/)
Thanks to everyone that contributed to the translation of this project. Thanks to everyone that contributed to the translation of this project.
#### Added #### Added
- Norwegian Bokmål (by VR Kek) - Norwegian Bokmål (by VR Kek)
- Swedish (by Alexander) - Swedish (by Alexander)
- Turkish (by Efe Devirgen) - Turkish (by Efe Devirgen)
@@ -576,6 +622,7 @@ Thanks to everyone that contributed to the translation of this project.
- Bengali (by Akhlak Ur Rahman) - Bengali (by Akhlak Ur Rahman)
#### Updated #### Updated
- Chinese (Simplified) (by 清水汐音, Kouki Kitamura) - Chinese (Simplified) (by 清水汐音, Kouki Kitamura)
- Italian (by tizio04, Roberto Palmese) - Italian (by tizio04, Roberto Palmese)
- Japanese (by marimo, Siamese) - Japanese (by marimo, Siamese)
@@ -589,6 +636,7 @@ Thanks to everyone that contributed to the translation of this project.
- Indonesian (by Axel C) - Indonesian (by Axel C)
### Contributors ### Contributors
Thanks to everyone that contributed to this release Thanks to everyone that contributed to this release
@leollo98, @kiritsumafuyu, @marimo-nekomimi, @tizio04, @aizhimoran, @schroda, @akhlakurrahman1011, @xxx1SET1xxx, @jesusFx, @SySen04, @dejavui, @JoHena, @jintaxi, @okpo2188513, @axlchr12, @Lafrend, @EfeDevirgen, @HugoLeBoennec, @PandaKewt, @rpalmese, @ZerOri, @Xsrt251, @plum7x, @GR2066878693, @taos15, @Topru333, @chancez, @MageSneaky, @Azokul01, @gabrielssevero, @0QwQ0, @Rintan, @zyzz8520, @guohuageng @leollo98, @kiritsumafuyu, @marimo-nekomimi, @tizio04, @aizhimoran, @schroda, @akhlakurrahman1011, @xxx1SET1xxx, @jesusFx, @SySen04, @dejavui, @JoHena, @jintaxi, @okpo2188513, @axlchr12, @Lafrend, @EfeDevirgen, @HugoLeBoennec, @PandaKewt, @rpalmese, @ZerOri, @Xsrt251, @plum7x, @GR2066878693, @taos15, @Topru333, @chancez, @MageSneaky, @Azokul01, @gabrielssevero, @0QwQ0, @Rintan, @zyzz8520, @guohuageng
@@ -596,6 +644,7 @@ Thanks to everyone that contributed to this release
## [1.0.0] (r1411) - 2024-02-23 ## [1.0.0] (r1411) - 2024-02-23
### Added ### Added
- (**General**) Added internationalization (help translating on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/)) - (**General**) Added internationalization (help translating on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/))
- (**General**) Get notified when a new server version has been released - (**General**) Get notified when a new server version has been released
- (**General**) Manually check for new versions (server and webUI) - (**General**) Manually check for new versions (server and webUI)
@@ -629,14 +678,17 @@ Thanks to everyone that contributed to this release
- (**Settings**) Server settings can now be changed from the UI - (**Settings**) Server settings can now be changed from the UI
### Fixed ### Fixed
- A lot (and added new ones for the future, lul) - A lot (and added new ones for the future, lul)
### Translations ### Translations
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/). Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/).
Thanks to everyone that contributed to the translation of this project. Thanks to everyone that contributed to the translation of this project.
#### Added #### Added
- Arabic (by abdelbasset jabrane, Bander AL-shreef) - Arabic (by abdelbasset jabrane, Bander AL-shreef)
- Chinese (Simplified) (by misaka10843, 蓝云Reyes, Nite07, 志明, ccms, 宮河ひより, 清水汐音, DevCoz) - Chinese (Simplified) (by misaka10843, 蓝云Reyes, Nite07, 志明, ccms, 宮河ひより, 清水汐音, DevCoz)
- Chinese (Traditional) (by plum7x, 蓝云Reyes) - Chinese (Traditional) (by plum7x, 蓝云Reyes)
@@ -652,6 +704,7 @@ Thanks to everyone that contributed to the translation of this project.
- Vietnamese (by xconkhi9x) - Vietnamese (by xconkhi9x)
### Contributors ### Contributors
Thanks to everyone that contributed to this release Thanks to everyone that contributed to this release
@schroda, @jesusFx, @QuietBlade, @anvstin, @guohuageng, @plum7x, @HiyoriTUK, @aizhimoran, @JiPaix, @Yuhyeong, @a18ccms, @chancez, @rickymcmuffin, @zmmx, @alexandrejournet, @ibaraki-douji, @nitezs, @misaka10843, @Becods, @skrewde, @xconkhi9x, @cnmorocho, @Wip-Sama, @Kefir2105, @RafieHardinur, @SuperMario229, @Alexandre-P-J, @AriaMoradi, @NathanBnm, @FumoVite, @JoHena, @bandysharif, @DevCoz, @comradekingu, @Zereef, @akabhirav @schroda, @jesusFx, @QuietBlade, @anvstin, @guohuageng, @plum7x, @HiyoriTUK, @aizhimoran, @JiPaix, @Yuhyeong, @a18ccms, @chancez, @rickymcmuffin, @zmmx, @alexandrejournet, @ibaraki-douji, @nitezs, @misaka10843, @Becods, @skrewde, @xconkhi9x, @cnmorocho, @Wip-Sama, @Kefir2105, @RafieHardinur, @SuperMario229, @Alexandre-P-J, @AriaMoradi, @NathanBnm, @FumoVite, @JoHena, @bandysharif, @DevCoz, @comradekingu, @Zereef, @akabhirav

View File

@@ -1,20 +1,26 @@
# Contributing # Contributing
## Where should I start? ## Where should I start?
Everything from https://github.com/Suwayomi/Suwayomi-Server/blob/master/CONTRIBUTING.md#where-should-i-start applies here. Everything from https://github.com/Suwayomi/Suwayomi-Server/blob/master/CONTRIBUTING.md#where-should-i-start applies here.
## About this project ## About this project
### Building the app ### Building the app
See [BUILDING.md](./BUILDING.md) for more information See [BUILDING.md](./BUILDING.md) for more information
## WebUI to [Server](https://github.com/Suwayomi/Suwayomi-Server) mapping ## WebUI to [Server](https://github.com/Suwayomi/Suwayomi-Server) mapping
### Explanation ### Explanation
For the server to be able to automatically download the latest compatible WebUI version, the [version to server version mapping file](versionToServerVersionMapping.json) has to be provided.<br/> For the server to be able to automatically download the latest compatible WebUI version, the [version to server version mapping file](versionToServerVersionMapping.json) has to be provided.<br/>
The order of the version mapping is important and has to be sorted by latest WebUI version to the oldest version.<br/> The order of the version mapping is important and has to be sorted by latest WebUI version to the oldest version.<br/>
The latest version will always be `PREVIEW`. The latest version will always be `PREVIEW`.
### When to update ### When to update
- changes get added that require a new minimum server version - changes get added that require a new minimum server version
- **update:** the mapped server version for the `PREVIEW` version - **update:** the mapped server version for the `PREVIEW` version
- releasing a new version - releasing a new version
@@ -23,7 +29,9 @@ The latest version will always be `PREVIEW`.
- changed: add a new entry below the `PREVIEW` version with the mapped server version from `PREVIEW` - changed: add a new entry below the `PREVIEW` version with the mapped server version from `PREVIEW`
## Coding Style Guide ## Coding Style Guide
**Note:** Some of the bellow are new, refactor the code to match the style guide where you see inconsistency. **Note:** Some of the bellow are new, refactor the code to match the style guide where you see inconsistency.
- Don't use relative imports. - Don't use relative imports.
- We are using MUI v5, the all stylings must be applied with the new system. - We are using MUI v5, the all stylings must be applied with the new system.
- Never use the `style` prop, there's always a cleaner solution with `sx` or `styled`. - Never use the `style` prop, there's always a cleaner solution with `sx` or `styled`.

View File

@@ -1,10 +1,12 @@
# Suwayomi-WebUI # Suwayomi-WebUI
This is the repository of the default client of [Suwayomi-Server](https://github.com/Suwayomi/Suwayomi-Server). This is the repository of the default client of [Suwayomi-Server](https://github.com/Suwayomi/Suwayomi-Server).
The server has this web app bundled by default and is able to automatically update to the latest versions. The server has this web app bundled by default and is able to automatically update to the latest versions.
Thus, there is no need to manually download any builds unless you want to host the app yourself instead of having it hosted by the Suwayomi-Server. Thus, there is no need to manually download any builds unless you want to host the app yourself instead of having it hosted by the Suwayomi-Server.
## Features ## Features
- Library management - Library management
- Library page - manga management - Library page - manga management
- Filter/Sort/Search your manga - Filter/Sort/Search your manga
@@ -47,6 +49,7 @@ Thus, there is no need to manually download any builds unless you want to host t
- Dynamic theme on manga pages - Dynamic theme on manga pages
## Preview ## Preview
An ongoing changelog of all relevant changes since the last stable release can be found [here](https://github.com/Suwayomi/Suwayomi-WebUI/issues/749) An ongoing changelog of all relevant changes since the last stable release can be found [here](https://github.com/Suwayomi/Suwayomi-WebUI/issues/749)
To use the preview version you can select the PREVIEW channel in the settings of your Suwayomi-Server. To use the preview version you can select the PREVIEW channel in the settings of your Suwayomi-Server.
@@ -58,12 +61,14 @@ In case your server is outdated, it will automatically downgrade to the latest c
Minified builds of WebUI can be found here [Suwayomi-WebUI-preview](https://github.com/Suwayomi/Suwayomi-WebUI-preview). Minified builds of WebUI can be found here [Suwayomi-WebUI-preview](https://github.com/Suwayomi/Suwayomi-WebUI-preview).
Additionally, there is an online build of the WebUI preview version that is available [here](https://suwayomi-webui-preview.github.io/). Additionally, there is an online build of the WebUI preview version that is available [here](https://suwayomi-webui-preview.github.io/).
*Make sure to set your Suwayomi-Server hostname in Settings or you'll get infinite loading.* Also note that its the **latest** revision of WebUI and might not work correctly if you connect to a stable build of Suwayomi-Server. _Make sure to set your Suwayomi-Server hostname in Settings or you'll get infinite loading._ Also note that its the **latest** revision of WebUI and might not work correctly if you connect to a stable build of Suwayomi-Server.
## Contributing and Technical info ## Contributing and Technical info
See [CONTRIBUTING.md](./CONTRIBUTING.md). See [CONTRIBUTING.md](./CONTRIBUTING.md).
## Translation ## Translation
Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/) Feel free to translate the project on [Weblate](https://hosted.weblate.org/projects/suwayomi/suwayomi-webui/)
<details><summary>Translation Progress</summary> <details><summary>Translation Progress</summary>

View File

@@ -1,187 +1,23 @@
import js from '@eslint/js'; /*
import globals from 'globals'; * Minimal ESLint config — only rules not yet supported natively in oxlint.
* The bulk of linting is done by oxlint (.oxlintrc.json).
*/
import tseslint from 'typescript-eslint'; import tseslint from 'typescript-eslint';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import importX, { importXResolverCompat } from 'eslint-plugin-import-x';
import * as tsResolver from 'eslint-import-resolver-typescript';
import unusedImports from 'eslint-plugin-unused-imports';
import noRelativeImportPaths from 'eslint-plugin-no-relative-import-paths';
import header from '@tony.ganchev/eslint-plugin-header'; import header from '@tony.ganchev/eslint-plugin-header';
import lingui from 'eslint-plugin-lingui';
import prettierRecommended from 'eslint-plugin-prettier/recommended';
export default tseslint.config( export default tseslint.config(
{ ignores: ['src/lib/graphql/generated/**'] }, { ignores: ['src/lib/graphql/generated/**', 'eslint.config.js'] },
// Base configs
js.configs.recommended,
...tseslint.configs.recommended,
reactPlugin.configs.flat.recommended,
reactPlugin.configs.flat['jsx-runtime'],
reactHooksPlugin.configs['recommended-latest'],
jsxA11y.flatConfigs.recommended,
importX.flatConfigs.recommended,
lingui.configs['flat/recommended'],
// Main rules
{ {
files: ['**/*.{ts,tsx,js,jsx}'],
languageOptions: { languageOptions: {
globals: { ...globals.browser }, parser: tseslint.parser,
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
settings: {
react: { version: 'detect' },
'import-x/extensions': ['.ts', '.tsx', '.js', '.jsx'],
'import-x/external-module-folders': ['node_modules', 'node_modules/@types'],
'import-x/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import-x/resolver-next': [
importXResolverCompat(tsResolver, {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
}),
],
}, },
plugins: { plugins: {
'unused-imports': unusedImports,
'no-relative-import-paths': noRelativeImportPaths,
header, header,
}, },
rules: { rules: {
// Lingui // MPL 2.0 license header
'lingui/no-expression-in-message': 'off',
// Core JS rules from Airbnb
'no-console': 'warn',
'no-nested-ternary': 'error',
'no-else-return': ['error', { 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-proto': 'error',
'no-extend-native': 'error',
'no-caller': 'error',
'no-alert': 'warn',
'no-void': 'error',
'no-bitwise': 'error',
'no-continue': '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-unreachable-loop': 'error',
'no-useless-concat': 'error',
'no-useless-return': 'error',
'no-useless-computed-key': 'error',
'no-useless-rename': 'error',
'no-underscore-dangle': 'error',
'no-restricted-exports': ['error', { restrictedNamedExports: ['default', 'then'] }],
eqeqeq: ['error', 'always', { null: 'ignore' }],
'consistent-return': 'error',
'guard-for-in': 'error',
'default-case': ['error', { commentPattern: '^no default$' }],
'default-case-last': 'error',
radix: 'error',
yoda: 'error',
'one-var': ['error', 'never'],
'max-classes-per-file': ['error', 1],
'prefer-template': 'error',
'prefer-destructuring': [
'error',
{
VariableDeclarator: { array: false, object: true },
AssignmentExpression: { array: true, object: false },
},
{ enforceForRenamedProperties: false },
],
'prefer-exponentiation-operator': 'error',
'prefer-object-spread': 'error',
'prefer-promise-reject-errors': ['error', { allowEmptyReject: true }],
'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }],
'object-shorthand': ['error', 'always', { ignoreConstructors: false, avoidQuotes: true }],
'no-param-reassign': ['error', { props: true, ignorePropertyModificationsForRegex: ['^draft'] }],
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
'class-methods-use-this': 'off',
// TypeScript rules (non-type-checked)
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
'no-loop-func': 'off',
'@typescript-eslint/no-loop-func': 'error',
'@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: true, variables: true }],
'default-param-last': 'off',
'@typescript-eslint/default-param-last': 'error',
// TypeScript rules (type-checked)
'no-implied-eval': 'off',
'@typescript-eslint/no-implied-eval': 'error',
'@typescript-eslint/only-throw-error': 'error',
'dot-notation': 'off',
'@typescript-eslint/dot-notation': 'error',
'@typescript-eslint/return-await': ['error', 'in-try-catch'],
'@typescript-eslint/naming-convention': [
'error',
{ selector: 'variable', format: ['camelCase', 'PascalCase', 'UPPER_CASE'] },
{ selector: 'function', format: ['camelCase', 'PascalCase'] },
{ selector: 'typeLike', format: ['PascalCase'] },
],
// TypeScript — relax rules not effectively enforced in old config
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
vars: 'all',
args: 'after-used',
ignoreRestSiblings: true,
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_|^e$|^error$',
},
],
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-unsafe-function-type': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
// Unused imports
'unused-imports/no-unused-imports': 'error',
// Import rules from Airbnb
'import-x/no-cycle': 'off', // TODO: enable after resolving circular dependencies
'import-x/no-mutable-exports': 'error',
'import-x/first': 'error',
'import-x/newline-after-import': 'error',
'import-x/no-amd': 'error',
'import-x/no-absolute-path': 'error',
'import-x/no-self-import': 'error',
'import-x/no-useless-path-segments': ['error', { noUselessIndex: true }],
'import-x/no-extraneous-dependencies': 'error',
'import-x/order': ['error', { groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'] }],
// Import rules (import → import-x)
'import-x/prefer-default-export': 'off',
'import-x/no-default-export': 'error',
'import-x/extensions': 'off',
'import-x/no-named-as-default': 'off',
'import-x/no-named-as-default-member': 'off',
'import-x/named': 'off',
'import-x/no-unresolved': ['error', { ignore: ['^@dnd-kit/core/dist', '^i18next$', '^public/'] }],
// Header
'header/header': [ 'header/header': [
'error', 'error',
'block', 'block',
@@ -196,136 +32,6 @@ export default tseslint.config(
], ],
2, 2,
], ],
// Prettier
'prettier/prettier': 'error',
// React hooks — relax rules that are stricter in v5
'react-hooks/exhaustive-deps': 'off',
// React rules from Airbnb
'react/destructuring-assignment': ['error', 'always'],
'react/no-unused-prop-types': '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': 'warn',
'react/no-this-in-sfc': 'error',
'react/state-in-constructor': ['error', 'always'],
// React — match Airbnb overrides
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
'react/jsx-no-bind': 'off',
'react/jsx-props-no-spreading': 'off',
'react/require-default-props': 'off',
'react/function-component-definition': 'off',
'react/display-name': 'off',
'react/no-unstable-nested-components': ['error', { allowAsProps: true }],
// jsx-a11y — match Airbnb (allow autoFocus on non-DOM elements)
'jsx-a11y/no-autofocus': ['error', { ignoreNonDOM: true }],
// Restricted imports (MUI pattern)
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['@mui/*', '!@mui/material/', '!@mui/icons-material/', '!@mui/x-date-pickers/'],
},
{
group: ['@mui/*/*/*'],
},
],
},
],
// Restricted syntax (SxProps check)
'no-restricted-syntax': [
'error',
{
selector: 'TSTypeReference[typeName.name="SxProps"]:not([typeArguments])',
message: 'SxProps must have Theme parameter to avoid significant compiler slowdown.',
},
],
// No relative import paths
'no-relative-import-paths/no-relative-import-paths': [
'error',
{
rootDir: 'src',
prefix: '@',
},
],
}, },
}, },
// Files using hooks in class static methods (valid namespace pattern, flagged by react-hooks v5)
{
files: [
'src/base/components/settings/NumberSetting.tsx',
'src/base/utils/MediaQuery.tsx',
'src/features/authentication/AuthManager.ts',
'src/features/browse/extensions/components/ExtensionCard.tsx',
'src/features/chapter/services/Chapters.ts',
'src/features/extension/info/components/ActionButton.tsx',
'src/features/manga/components/TrackMangaButton.tsx',
'src/features/manga/services/Mangas.ts',
'src/features/metadata/services/MetadataMigrations.ts',
'src/features/migration/components/MigrateDialog.tsx',
'src/features/navigation-bar/components/MobileBottomBar.tsx',
'src/features/reader/services/ReaderControls.ts',
'src/features/reader/services/ReaderService.ts',
'src/features/settings/screens/ImageProcessingSetting.tsx',
'src/features/source/browse/components/SourceOptions.tsx',
'src/features/source/browse/components/filters/SelectFilter.tsx',
'src/features/source/services/Sources.ts',
'src/lib/dayjs/LocaleImporter.ts',
'src/lib/dnd-kit/DndKitUtil.ts',
'src/lib/requests/RequestManager.ts',
'src/lib/requests/client/GraphQLClient.ts',
'src/lib/service-worker/ImageCache.ts',
'src/lib/virtuoso/Virtuoso.util.tsx',
],
rules: {
'react-hooks/rules-of-hooks': 'off',
},
},
// Tools/scripts override
{
files: ['tools/scripts/**/*'],
rules: {
'no-console': 'off',
'no-relative-import-paths/no-relative-import-paths': 'off',
'import-x/no-extraneous-dependencies': ['error', { devDependencies: true }],
'@typescript-eslint/naming-convention': 'off',
},
},
// Config files — not part of tsconfig, disable type-checked linting and project-specific rules
{
files: ['eslint.config.js'],
...tseslint.configs.disableTypeChecked,
rules: {
...tseslint.configs.disableTypeChecked.rules,
'header/header': 'off',
'import-x/no-default-export': 'off',
'import-x/no-extraneous-dependencies': 'off',
'import-x/default': 'off',
},
},
// Prettier must be last
prettierRecommended,
); );

View File

@@ -1,29 +1,26 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<script> <script>
(() => { (() => {
if (document.querySelector('base')) { if (document.querySelector('base')) {
return return;
} }
const base = document.createElement('base'); const base = document.createElement('base');
base.href = '/'; base.href = '/';
document.head.prepend(base); document.head.prepend(base);
})() })();
</script> </script>
<title>Suwayomi</title> <title>Suwayomi</title>
<meta name="apple-mobile-web-app-title" content="Suwayomi" /> <meta name="apple-mobile-web-app-title" content="Suwayomi" />
<meta <meta name="description" content="A manga reader that runs tachiyomi's extensions" />
name="description"
content="A manga reader that runs tachiyomi's extensions"
/>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name='viewport' content='minimum-scale=1, initial-scale=1, viewport-fit=cover, width=device-width'> <meta name="viewport" content="minimum-scale=1, initial-scale=1, viewport-fit=cover, width=device-width" />
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" /> <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
@@ -41,11 +38,11 @@
<script type="module" src="./src/index.tsx"></script> <script type="module" src="./src/index.tsx"></script>
<script> <script>
const backgroundColor = (() => { const backgroundColor = (() => {
const storageValue = window.localStorage.getItem("theme_background") const storageValue = window.localStorage.getItem('theme_background');
try { try {
return JSON.parse(storageValue) return JSON.parse(storageValue);
} catch (e) { } catch (e) {
return storageValue return storageValue;
} }
})(); })();

View File

@@ -13,7 +13,9 @@
"test": "node -e \"console.log('imagine')\"", "test": "node -e \"console.log('imagine')\"",
"build-md5": "find build -type f | sort | xargs md5sum | awk '{ print $1 }' | tr -d '\n' | md5sum| awk '{ print $1 }' > buildZip/md5sum ", "build-md5": "find build -type f | sort | xargs md5sum | awk '{ print $1 }' | tr -d '\n' | md5sum| awk '{ print $1 }' > buildZip/md5sum ",
"build-zip": "cd build && rev=$(git rev-list HEAD --count) && echo r$rev > revision && zip -9 -r ../buildZip/Suwayomi-WebUI-r$rev *", "build-zip": "cd build && rev=$(git rev-list HEAD --count) && echo r$rev > revision && zip -9 -r ../buildZip/Suwayomi-WebUI-r$rev *",
"lint": "eslint src tools --max-warnings=0 --cache", "lint": "oxlint src tools && eslint src tools --max-warnings=0",
"format": "oxfmt --write .",
"format:check": "oxfmt --check .",
"createCommitChangelog": "tsx tools/scripts/release/createCommitChangelog.ts", "createCommitChangelog": "tsx tools/scripts/release/createCommitChangelog.ts",
"createTranslationChangelog": "tsx tools/scripts/release/createTranslationChangelog.ts", "createTranslationChangelog": "tsx tools/scripts/release/createTranslationChangelog.ts",
"createReleaseChangelog": "tsx tools/scripts/release/createReleaseChangelog.ts", "createReleaseChangelog": "tsx tools/scripts/release/createReleaseChangelog.ts",
@@ -31,15 +33,6 @@
"tsc:legacy": "tsc", "tsc:legacy": "tsc",
"prepare": "husky" "prepare": "husky"
}, },
"engines": {
"node": ">=24"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": "eslint --fix"
},
"resolutions": {
"@swc/core": "1.15.11"
},
"dependencies": { "dependencies": {
"@apollo/client": "4.1.6", "@apollo/client": "4.1.6",
"@dnd-kit/core": "6.3.1", "@dnd-kit/core": "6.3.1",
@@ -102,6 +95,7 @@
"@lingui/format-po": "5.9.2", "@lingui/format-po": "5.9.2",
"@lingui/swc-plugin": "5.11.0", "@lingui/swc-plugin": "5.11.0",
"@lingui/vite-plugin": "5.9.2", "@lingui/vite-plugin": "5.9.2",
"@tony.ganchev/eslint-plugin-header": "3.2.6",
"@types/node": "24.10.1", "@types/node": "24.10.1",
"@types/react": "19.2.14", "@types/react": "19.2.14",
"@types/react-beautiful-dnd": "13.1.8", "@types/react-beautiful-dnd": "13.1.8",
@@ -110,29 +104,20 @@
"@types/stylis": "4.2.7", "@types/stylis": "4.2.7",
"@types/webfontloader": "1.6.38", "@types/webfontloader": "1.6.38",
"@types/yargs": "17.0.35", "@types/yargs": "17.0.35",
"@tony.ganchev/eslint-plugin-header": "3.2.6", "@typescript/native-preview": "7.0.0-dev.20260309.1",
"@vitejs/plugin-legacy": "7.2.1", "@vitejs/plugin-legacy": "7.2.1",
"@vitejs/plugin-react-swc": "4.2.3", "@vitejs/plugin-react-swc": "4.2.3",
"dotenv": "17.3.1", "dotenv": "17.3.1",
"eslint": "9.39.4", "eslint": "9.39.4",
"eslint-config-prettier": "10.1.8",
"eslint-import-resolver-typescript": "4.4.4",
"eslint-plugin-import-x": "4.16.1",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-lingui": "0.11.0", "eslint-plugin-lingui": "0.11.0",
"eslint-plugin-no-relative-import-paths": "1.6.1", "eslint-plugin-no-relative-import-paths": "1.6.1",
"eslint-plugin-prettier": "5.5.5",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "5.2.0",
"eslint-plugin-unused-imports": "4.4.1",
"globals": "15.15.0",
"husky": "9.1.7", "husky": "9.1.7",
"lint-staged": "16.3.2", "lint-staged": "16.3.2",
"prettier": "3.8.1", "oxfmt": "0.37.0",
"oxlint": "1.52.0",
"syncyarnlock": "1.0.19", "syncyarnlock": "1.0.19",
"terser": "5.46.0", "terser": "5.46.0",
"tsx": "4.21.0", "tsx": "4.21.0",
"@typescript/native-preview": "7.0.0-dev.20260309.1",
"typescript": "5.9.3", "typescript": "5.9.3",
"typescript-eslint": "8.57.0", "typescript-eslint": "8.57.0",
"vite": "7.3.1", "vite": "7.3.1",
@@ -142,5 +127,19 @@
"workbox-build": "7.4.0", "workbox-build": "7.4.0",
"workbox-window": "7.4.0", "workbox-window": "7.4.0",
"yargs": "18.0.0" "yargs": "18.0.0"
},
"resolutions": {
"@swc/core": "1.15.11"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"oxfmt --write",
"oxlint --fix",
"eslint --fix"
],
"*.{json,md,yml,yaml,css,scss,html,graphql}": "oxfmt --write"
},
"engines": {
"node": ">=24"
} }
} }

View File

@@ -10,7 +10,6 @@ declare module 'apollo-upload-client/UploadHttpLink.mjs' {
import { ApolloLink } from '@apollo/client'; import { ApolloLink } from '@apollo/client';
import type { BaseHttpLink } from '@apollo/client/link/http'; import type { BaseHttpLink } from '@apollo/client/link/http';
// eslint-disable-next-line import-x/no-default-export
export default class UploadHttpLink extends ApolloLink { export default class UploadHttpLink extends ApolloLink {
constructor(options?: BaseHttpLink.ConstructorOptions); constructor(options?: BaseHttpLink.ConstructorOptions);
} }

View File

@@ -22,7 +22,7 @@ interface State {
} }
class RealErrorBoundary extends Component<Props, State> { class RealErrorBoundary extends Component<Props, State> {
// eslint-disable-next-line react/state-in-constructor // oxlint-disable-next-line react/state-in-constructor
public state: State = { error: null }; public state: State = { error: null };
private prevPath: string = ''; private prevPath: string = '';
@@ -45,10 +45,9 @@ class RealErrorBoundary extends Component<Props, State> {
} }
public componentDidCatch(error: Error, errorInfo: ErrorInfo) { public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// eslint-disable-next-line no-console // oxlint-disable-next-line no-console
console.error('Uncaught error:', error, errorInfo); console.error('Uncaught error:', error, errorInfo);
// eslint-disable-next-line react/destructuring-assignment
this.props.setTrackPathChange(true); this.props.setTrackPathChange(true);
} }

View File

@@ -27,7 +27,7 @@ export class AuthManager {
private static refreshingToken: boolean = false; private static refreshingToken: boolean = false;
private static subscribe(callback: () => void): () => void { private static subscribe(callback: () => void): () => void {
// eslint-disable-next-line no-plusplus // oxlint-disable-next-line no-plusplus
const key = AuthManager.subscribedCount++; const key = AuthManager.subscribedCount++;
this.subscribers.set(key, callback); this.subscribers.set(key, callback);

View File

@@ -103,7 +103,7 @@ export const ChapterCard = memo((props: IProps) => {
return; return;
} }
// eslint-disable-next-line no-param-reassign // oxlint-disable-next-line no-param-reassign
event.shiftKey = true; event.shiftKey = true;
handleClick(event); handleClick(event);
}); });

View File

@@ -76,10 +76,7 @@ const querySearchManga = (
{ title, genre: genres, description, artist, author, source, sourceId }: TMangaQueryFilter, { title, genre: genres, description, artist, author, source, sourceId }: TMangaQueryFilter,
): boolean => ): boolean =>
performSearch([query], [title]) || performSearch([query], [title]) ||
performSearch( performSearch(query?.split(','), genres.map((genre) => enhancedCleanup(genre))) ||
query?.split(','),
genres.map((genre) => enhancedCleanup(genre)),
) ||
performSearch([query], [description]) || performSearch([query], [description]) ||
performSearch([query], [artist]) || performSearch([query], [artist]) ||
performSearch([query], [author]) || performSearch([query], [author]) ||

View File

@@ -158,7 +158,7 @@ export const SOURCES_BY_MANGA_TYPE: Record<MangaType, string[]> = {
* The actual matching data is in {@link MANGA_TAGS_BY_MANGA_TYPE} below. * The actual matching data is in {@link MANGA_TAGS_BY_MANGA_TYPE} below.
*/ */
// @ts-ignore - see comment // @ts-ignore - see comment
// eslint-disable-next-line @typescript-eslint/no-unused-vars // oxlint-disable-next-line no-unused-vars
const MANGA_TAG_DESCRIPTORS_BY_MANGA_TYPE: Record<MangaType, MessageDescriptor[]> = { const MANGA_TAG_DESCRIPTORS_BY_MANGA_TYPE: Record<MangaType, MessageDescriptor[]> = {
[MangaType.MANGA]: [msg`Manga`], [MangaType.MANGA]: [msg`Manga`],
[MangaType.COMIC]: [msg`Comic`], [MangaType.COMIC]: [msg`Comic`],

View File

@@ -169,7 +169,7 @@ const VerticalGrid = ({
/> />
</Box> </Box>
{/* render div to prevent UI jumping around when showing/hiding loading placeholder */} {/* render div to prevent UI jumping around when showing/hiding loading placeholder */}
{/* eslint-disable no-nested-ternary */} {/* oxlint-disable no-nested-ternary */}
{isSelectModeActive && gridLayout === GridLayout.List ? ( {isSelectModeActive && gridLayout === GridLayout.List ? (
<Box sx={{ paddingBottom: DEFAULT_FULL_FAB_HEIGHT }} /> <Box sx={{ paddingBottom: DEFAULT_FULL_FAB_HEIGHT }} />
) : isLoading ? ( ) : isLoading ? (
@@ -177,7 +177,7 @@ const VerticalGrid = ({
) : hasNextPage ? ( ) : hasNextPage ? (
<div style={{ height: '75px' }} /> <div style={{ height: '75px' }} />
) : null} ) : null}
{/* eslint-enable no-nested-ternary */} {/* oxlint-enable no-nested-ternary */}
</> </>
); );

View File

@@ -96,7 +96,7 @@ export const MangaCard = memo((props: MangaCardProps) => {
const longPressBind = useLongPress( const longPressBind = useLongPress(
useCallback( useCallback(
(e: any, { context }: any) => { (e: any, { context }: any) => {
// eslint-disable-next-line no-param-reassign // oxlint-disable-next-line no-param-reassign
e.shiftKey = true; e.shiftKey = true;
handleClick(e, context as () => {}); handleClick(e, context as () => {});
}, },

View File

@@ -549,7 +549,7 @@ export class Mangas {
// the migration actions (copy, cleanup) are supposed to be run sequentially to ensure that the cleanup // the migration actions (copy, cleanup) are supposed to be run sequentially to ensure that the cleanup
// only happens in case the copy succeeded // only happens in case the copy succeeded
// eslint-disable-next-line no-await-in-loop // oxlint-disable-next-line no-await-in-loop
await performMigrationAction(migrationAction, ...actions); await performMigrationAction(migrationAction, ...actions);
} }
}; };

View File

@@ -6,7 +6,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
// eslint-disable-next-line no-restricted-imports
import deepmerge from '@mui/utils/deepmerge'; import deepmerge from '@mui/utils/deepmerge';
import { AppMetadataKeys, IMetadataMigration } from '@/features/metadata/Metadata.types.ts'; import { AppMetadataKeys, IMetadataMigration } from '@/features/metadata/Metadata.types.ts';
import { import {

View File

@@ -120,7 +120,7 @@ export class ReaderTapZoneService {
context.beginPath(); context.beginPath();
/* eslint-disable no-param-reassign */ /* oxlint-disable no-param-reassign */
context.rect(x, y, width, height); context.rect(x, y, width, height);
context.fillStyle = color; context.fillStyle = color;
context.strokeStyle = color; context.strokeStyle = color;
@@ -141,7 +141,7 @@ export class ReaderTapZoneService {
context.lineWidth = 1; context.lineWidth = 1;
context.fillStyle = 'white'; context.fillStyle = 'white';
context.fillText(text, rectCenterX, rectCenterY); context.fillText(text, rectCenterX, rectCenterY);
/* eslint-enable no-param-reassign */ /* oxlint-enable no-param-reassign */
}); });
} }

View File

@@ -162,7 +162,7 @@ const BaseReaderChapterViewer = ({
isCurrentChapterRef.current = isCurrentChapter; isCurrentChapterRef.current = isCurrentChapter;
if (isCurrentChapter) { if (isCurrentChapter) {
// eslint-disable-next-line no-param-reassign // oxlint-disable-next-line no-param-reassign
globalImageRefs.current = imageRefs.current; globalImageRefs.current = imageRefs.current;
} }

View File

@@ -86,7 +86,6 @@ const BaseReaderTransitionPage = ({
handleBack, handleBack,
}: Pick<NavbarContextType, 'readerNavBarWidth'> & { }: Pick<NavbarContextType, 'readerNavBarWidth'> & {
// gets used in the "source props creators" of the "withPropsFrom" call // gets used in the "source props creators" of the "withPropsFrom" call
// eslint-disable-next-line react/no-unused-prop-types
chapterId: ChapterIdInfo['id']; chapterId: ChapterIdInfo['id'];
currentChapterName?: ChapterType['name']; currentChapterName?: ChapterType['name'];
currentChapterScanlator?: ChapterType['scanlator']; currentChapterScanlator?: ChapterType['scanlator'];

View File

@@ -87,7 +87,7 @@ const BaseBasePager = ({
const setRef = useCallback( const setRef = useCallback(
(pagesIndex: number, element: HTMLElement | null) => { (pagesIndex: number, element: HTMLElement | null) => {
// eslint-disable-next-line no-param-reassign // oxlint-disable-next-line no-param-reassign
imageRefs.current[pagesIndex] = element; imageRefs.current[pagesIndex] = element;
}, },
[imageRefs], [imageRefs],

View File

@@ -123,7 +123,7 @@ export const addStableIdToKeyValueItems = (
items: (SettingsDownloadConversionHeader | TSettingsDownloadConversionKeyValueItem)[], items: (SettingsDownloadConversionHeader | TSettingsDownloadConversionKeyValueItem)[],
): TSettingsDownloadConversionKeyValueItem[] => ): TSettingsDownloadConversionKeyValueItem[] =>
items.map((item) => ({ items.map((item) => ({
// eslint-disable-next-line no-plusplus // oxlint-disable-next-line no-plusplus
id: (item as TSettingsDownloadConversionKeyValueItem).id ?? COUNTER++, id: (item as TSettingsDownloadConversionKeyValueItem).id ?? COUNTER++,
...item, ...item,
})); }));
@@ -132,7 +132,7 @@ export const addStableIdToConversions = (
conversions: (SettingsDownloadConversion | TSettingsDownloadConversion)[], conversions: (SettingsDownloadConversion | TSettingsDownloadConversion)[],
): TSettingsDownloadConversion[] => ): TSettingsDownloadConversion[] =>
conversions.map((conversion) => ({ conversions.map((conversion) => ({
// eslint-disable-next-line no-plusplus // oxlint-disable-next-line no-plusplus
id: (conversion as TSettingsDownloadConversion).id ?? COUNTER++, id: (conversion as TSettingsDownloadConversion).id ?? COUNTER++,
...conversion, ...conversion,
mode: getTargetMode(normalizeMimeType(conversion.target)), mode: getTargetMode(normalizeMimeType(conversion.target)),

View File

@@ -51,7 +51,6 @@ export const getMetadataServerSettings = async (): Promise<MetadataServerSetting
const { data, error } = await requestManager.getGlobalMeta().response; const { data, error } = await requestManager.getGlobalMeta().response;
if (error) { if (error) {
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw error; throw error;
} }

View File

@@ -50,7 +50,7 @@ export const TriStateFilter: React.FC<Props> = (props) => {
const [val, setval] = React.useState(convertTriStateToNumber(state)); const [val, setval] = React.useState(convertTriStateToNumber(state));
const handleChange = (checked: boolean | null | undefined) => { const handleChange = (checked: boolean | null | undefined) => {
// eslint-disable-next-line no-nested-ternary // oxlint-disable-next-line no-nested-ternary
const newState = checked === undefined ? 0 : checked ? 1 : 2; const newState = checked === undefined ? 0 : checked ? 1 : 2;
setval(newState); setval(newState);
const upd = update.filter( const upd = update.filter(

View File

@@ -16,7 +16,7 @@ import {
useTheme, useTheme,
} from '@mui/material/styles'; } from '@mui/material/styles';
import { useCallback } from 'react'; import { useCallback } from 'react';
// eslint-disable-next-line no-restricted-imports // oxlint-disable-next-line no-restricted-imports
import { deepmerge } from '@mui/utils'; import { deepmerge } from '@mui/utils';
import { complement, hsl, parseToHsl } from 'polished'; import { complement, hsl, parseToHsl } from 'polished';
import { HslaColor, HslColor } from 'polished/lib/types/color'; import { HslaColor, HslColor } from 'polished/lib/types/color';

View File

@@ -87,7 +87,7 @@ export class ThemeFontLoader {
result[font] = new Set([...(result[font] ?? []), ...weights].toSorted((a, b) => a - b)); result[font] = new Set([...(result[font] ?? []), ...weights].toSorted((a, b) => a - b));
}); });
// eslint-disable-next-line no-continue // oxlint-disable-next-line no-continue
continue; continue;
} }

View File

@@ -13,6 +13,6 @@ export const defaultPromiseErrorHandler =
return; return;
} }
// eslint-disable-next-line no-console // oxlint-disable-next-line no-console
console.error(`${name} failed due to`, error); console.error(`${name} failed due to`, error);
}; };

View File

@@ -30,7 +30,7 @@ export const importDayJsLocale = async (locale: string): Promise<DayJsLocale> =>
const dayjsLocale = getDayJsLocale(locale); const dayjsLocale = getDayJsLocale(locale);
try { try {
// eslint-disable-next-line @typescript-eslint/no-use-before-define // oxlint-disable-next-line no-use-before-define
await localesToImport[dayjsLocale](); await localesToImport[dayjsLocale]();
} catch (e) { } catch (e) {
// ignore - dayjs falls back to "en" by default // ignore - dayjs falls back to "en" by default

View File

@@ -103,7 +103,7 @@ export abstract class BaseClient<Client, ClientConfig, Fetcher> {
return SubpathUtil.getApiBaseUrl(serverBaseURL); return SubpathUtil.getApiBaseUrl(serverBaseURL);
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // oxlint-disable-next-line no-unused-vars
protected shouldQueueRequest(operationName?: string): boolean { protected shouldQueueRequest(operationName?: string): boolean {
return AuthManager.shouldQueueRequests(); return AuthManager.shouldQueueRequests();
} }

View File

@@ -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/.
*/ */
/* eslint-disable max-classes-per-file */ /* oxlint-disable max-classes-per-file */
import { jsonSaveParse } from '@/lib/HelperFunctions.ts'; import { jsonSaveParse } from '@/lib/HelperFunctions.ts';
export class Storage { export class Storage {

View File

@@ -1,8 +1,6 @@
{ {
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"include": [ "include": ["./**/*"],
"./**/*"
],
"compilerOptions": { "compilerOptions": {
"moduleResolution": "node" "moduleResolution": "node"
} }

View File

@@ -1,18 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"paths": { "paths": {
"@/*": [ "@/*": ["./src/*"]
"./src/*"
]
}, },
"target": "ES2020", "target": "ES2020",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": [ "lib": ["ESNEXT", "ES2020", "DOM", "DOM.Iterable"],
"ESNEXT",
"ES2020",
"DOM",
"DOM.Iterable"
],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
/* Bundler mode */ /* Bundler mode */
@@ -31,9 +24,7 @@
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },
"include": [ "include": ["src"],
"src"
],
"exclude": ["**/node_modules"], "exclude": ["**/node_modules"],
"references": [ "references": [
{ {

View File

@@ -6,9 +6,5 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowSyntheticDefaultImports": true "allowSyntheticDefaultImports": true
}, },
"include": [ "include": ["vite.config.ts", "gql_codegen.ts", "lingui.config.ts"]
"vite.config.ts",
"gql_codegen.ts",
"lingui.config.ts"
]
} }

View File

@@ -6,8 +6,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. * file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/ */
/* eslint-disable import-x/no-extraneous-dependencies */
import path from 'path'; import path from 'path';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc'; import react from '@vitejs/plugin-react-swc';
@@ -18,7 +16,6 @@ import { lingui } from '@lingui/vite-plugin';
import 'dotenv/config'; import 'dotenv/config';
import { d } from 'koration'; import { d } from 'koration';
// eslint-disable-next-line import-x/no-default-export
export default defineConfig(({ command }) => ({ export default defineConfig(({ command }) => ({
base: command === 'serve' ? process.env.VITE_SUBPATH || './' : './', base: command === 'serve' ? process.env.VITE_SUBPATH || './' : './',
build: { build: {

820
yarn.lock

File diff suppressed because it is too large Load Diff