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
@@ -166,21 +184,22 @@ Thanks to everyone that contributed to this release
- (**Reader**) Add custom scroll amount - (**Reader**) Add custom scroll amount
- (**Source**) Add functionality to pin sources at the top of the source list - (**Source**) Add functionality to pin sources at the top of the source list
- (**Global search**) Add options to show only searches for - (**Global search**) Add options to show only searches for
- pinned sources - pinned sources
- all sources - all sources
- sources with results - sources with results
- (**Navigation**) Show download queue information in app navigation - (**Navigation**) Show download queue information in app navigation
- (**Chapter**) Show total missing chapters info on top of the chapter list - (**Chapter**) Show total missing chapters info on top of the chapter list
- (**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
- Enqueue: bulk action for 300 or more chapters - Enqueue: bulk action for 300 or more chapters
- Delete: single + bulk action - Delete: single + bulk action
- Bookmark removal: bulk action - Bookmark removal: bulk action
- Read status change: bulk action - Read status change: bulk action
- (**Chapter**) Save chapter list options on the server - (**Chapter**) Save chapter list options on the server
- (**Chapter**) Hide chapter list actions while chapters are selected - (**Chapter**) Hide chapter list actions while chapters are selected
- (**Chapter**) Clear selection after performing an action - (**Chapter**) Clear selection after performing an action
@@ -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
@@ -284,30 +316,30 @@ Thanks to everyone that contributed to this release
- (**Themes**) Use dynamic theme colors on manga pages (settings > appearance) - (**Themes**) Use dynamic theme colors on manga pages (settings > appearance)
- (**Source**) Sort sources by name - (**Source**) Sort sources by name
- (**Reader**) Completely new reader - (**Reader**) Completely new reader
- New UI - New UI
- Different default settings per reading mode - Different default settings per reading mode
- Tap zones ("right and left", kindle, edge, ...) - Tap zones ("right and left", kindle, edge, ...)
- Page scale modes (original, width, height, ...) - Page scale modes (original, width, height, ...)
- New resume handling - New resume handling
- From inside the reader - From inside the reader
- Open previous chapter: last page gets resumed - Open previous chapter: last page gets resumed
- Open next chapter: first page gets resumed - Open next chapter: first page gets resumed
- Select specific chapter: first page gets resumed - Select specific chapter: first page gets resumed
- From outside the reader - From outside the reader
- Open read chapter: first page gets resumed - Open read chapter: first page gets resumed
- Open unread chapter: last read page gets resumed - Open unread chapter: last read page gets resumed
- Mobile like mouse drag scrolling - Mobile like mouse drag scrolling
- Auto scrolling - Auto scrolling
- Auto webtoon mode - Auto webtoon mode
- Infinite scrolling - Infinite scrolling
- Transition page between chapters - Transition page between chapters
- Filter options for images - Filter options for images
- Customizable hotkeys - Customizable hotkeys
- Customizable image pre-loading - Customizable image pre-loading
- Only pre-load n images - Only pre-load n images
- Important: For the double page mode n double pages get pre-loaded (e.g. pre-load 5 "images": up to 10 images will get pre-loaded) - Important: For the double page mode n double pages get pre-loaded (e.g. pre-load 5 "images": up to 10 images will get pre-loaded)
- Inform about missing chapters on chapter change - Inform about missing chapters on chapter change
- Inform about changing scanlator on chapter change - Inform about changing scanlator on chapter change
- (**History**) Add rudimentary history page - (**History**) Add rudimentary history page
- (**Extensions**) Add option to configure extension source settings from extension page - (**Extensions**) Add option to configure extension source settings from extension page
- (**General**) Include actual error in snackbar - (**General**) Include actual error in snackbar
@@ -325,9 +357,9 @@ Thanks to everyone that contributed to this release
- (**Library**) Save library options per category - (**Library**) Save library options per category
- (**Library**) Add "started" filter - (**Library**) Add "started" filter
- (**Library**) Improved search - (**Library**) Improved search
- Include description, artist, author, source name - Include description, artist, author, source name
- Do not require each genre to fully match a genre of a manga (e.g. "adv" will match "adventure" instead of having to search for "adventure") - Do not require each genre to fully match a genre of a manga (e.g. "adv" will match "adventure" instead of having to search for "adventure")
- Additionally try to compare by removing all non letter and number characters (e.g. "some title" will match "&some-"!title" instead having to search for "&some-"!title") - Additionally try to compare by removing all non letter and number characters (e.g. "some title" will match "&some-"!title" instead having to search for "&some-"!title")
- (**Reader**) Add mouse wheel scrolling to horizontal mode - (**Reader**) Add mouse wheel scrolling to horizontal mode
- (**Browse**) Remember last browse tab on back navigation - (**Browse**) Remember last browse tab on back navigation
- (**Extensions**) Add option to update all updatable extensions - (**Extensions**) Add option to update all updatable extensions
@@ -336,9 +368,10 @@ 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
- (**General**) Rename "Downloads" to "Download queue" - (**General**) Rename "Downloads" to "Download queue"
- (**Source**) Consider "Local source" to have language "Other". Up till now its langauge was called "Local source" - (**Source**) Consider "Local source" to have language "Other". Up till now its langauge was called "Local source"
- (**Source**) Close the source configuration dialogs when clicking outside of the dialog or pressing "escape" - (**Source**) Close the source configuration dialogs when clicking outside of the dialog or pressing "escape"
@@ -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,35 +502,37 @@ 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
- (**Library**) Filter library for manga that have active track bindings for a tracker - (**Library**) Filter library for manga that have active track bindings for a tracker
- (**Browse**) Optionally hide in library manga from the results - (**Browse**) Optionally hide in library manga from the results
- (**Browse**) Add/remove manga to/from the library directly on the source browse page - (**Browse**) Add/remove manga to/from the library directly on the source browse page
- Via long press - Via long press
- Desktop only: click button which is shown while hovering a manga - Desktop only: click button which is shown while hovering a manga
- (**Browse**) When adding a manga to the library check for duplicates and show an info dialog - (**Browse**) When adding a manga to the library check for duplicates and show an info dialog
- (**Browse**) Added functionality to save searches (saved search name has a limit of 50 characters) - (**Browse**) Added functionality to save searches (saved search name has a limit of 50 characters)
- (**Manga**) Added chapter list chapter menu action to open the chapter on the source site - (**Manga**) Added chapter list chapter menu action to open the chapter on the source site
- (**Manga**) Added tracking support - (**Manga**) Added tracking support
- From manga page - From manga page
- 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
- Settings per manga - Settings per manga
- Download - Download
- Download ahead while reading - Download ahead while reading
- (**Settings**) Added a setting to find all duplicated entries in the library (Settings > Library) - (**Settings**) Added a setting to find all duplicated entries in the library (Settings > Library)
- (**Updates**) Clicking on the thumbnail of an update card will open the manga page of the chapter - (**Updates**) Clicking on the thumbnail of an update card will open the manga page of the chapter
- (**Server update**) Inform about server version updates - (**Server update**) Inform about server version updates
@@ -503,44 +545,46 @@ Thanks to everyone that contributed to this release
- (**Manga**) Last used migration options (include chapters, categories, delete downloaded) are saved - (**Manga**) Last used migration options (include chapters, categories, delete downloaded) are saved
- (**Chapter**) Chapters option menu can be opened via long press - (**Chapter**) Chapters option menu can be opened via long press
- (**Chapter**) Download button now opens a menu to choose the number of chapters to download - (**Chapter**) Download button now opens a menu to choose the number of chapters to download
- Next unread chapter - Next unread chapter
- Next 5, 10, 25 unread chapters - Next 5, 10, 25 unread chapters
- Download ahead (downloads the next n unread chapters in case not enough unread and undownloaded chapters exist - based on the "download ahead while reading" setting) - Download ahead (downloads the next n unread chapters in case not enough unread and undownloaded chapters exist - based on the "download ahead while reading" setting)
- Unread chapters - Unread chapters
- All chapters - All chapters
- (**List/Grid item selection**) Select/deselect range of items between last clicked and clicked item - (**List/Grid item selection**) Select/deselect range of items between last clicked and clicked item
- Via long press - Via long press
- Desktop only: shift + left click - Desktop only: shift + left click
- (**Reader**) Show live preview of reader width changes - (**Reader**) Show live preview of reader width changes
- (**Reader**) Preload pages in single page mode - (**Reader**) Preload pages in single page mode
- (**Reader**) Chapter titles in the chapter selection now include the - (**Reader**) Chapter titles in the chapter selection now include the
- Chapter number - Chapter number
- Chapter title - Chapter title
- Scanlator in case the current chapter list includes more than one scanlator - Scanlator in case the current chapter list includes more than one scanlator
- (**Server update**) Added option to disable checks for a new server version (Settings > Server) - (**Server update**) Added option to disable checks for a new server version (Settings > Server)
- (**Server update**) Added the following options to the info dialog - (**Server update**) Added the following options to the info dialog
- Remind later: closing the info dialog via "close" won't open the dialog for one hour - Remind later: closing the info dialog via "close" won't open the dialog for one hour
- Ignore: won't open the dialog again for the available version update, in case a new version gets available, the dialog will be shown again - Ignore: won't open the dialog again for the available version update, in case a new version gets available, the dialog will be shown again
- (**WebUI update**) On a successful update a dialog gets opened (no matter the current page) which - (**WebUI update**) On a successful update a dialog gets opened (no matter the current page) which
- Informs about the update - Informs about the update
- Provides an option to open the changelog - Provides an option to open the changelog
- 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
- Successfully importing a backup - Successfully importing a backup
- (**General**) Fix basic authentication not working when the server is on a different domain - (**General**) Fix basic authentication not working when the server is on a different domain
- (**General**) Fix server being slow/"unresponsive" when triggering a lot of image requests - (**General**) Fix server being slow/"unresponsive" when triggering a lot of image requests
- Allow only 5 parallel image requests - Allow only 5 parallel image requests
- Abort pending image requests once they are not needed anymore - Abort pending image requests once they are not needed anymore
- (**General**) Fix missing loading placeholders in some pages - (**General**) Fix missing loading placeholders in some pages
- (**General**) Fix infinite loading placeholders in some pages - (**General**) Fix infinite loading placeholders in some pages
- (**General**) Fix back button loop when opening a page that has a depth greater than 2 as the initial page - (**General**) Fix back button loop when opening a page that has a depth greater than 2 as the initial page
@@ -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)
@@ -605,38 +654,41 @@ Thanks to everyone that contributed to this release
- (**Download**) Prevent automatic deletion of bookmarked chapters - (**Download**) Prevent automatic deletion of bookmarked chapters
- (**Library**) Migrate manga between sources - (**Library**) Migrate manga between sources
- (**Library**) Improved library management (single and bulk actions) - (**Library**) Improved library management (single and bulk actions)
- Download - Download
- Delete - Delete
- Mark as read - Mark as read
- Mark as unread - Mark as unread
- Migrate (single action only) - Migrate (single action only)
- Change categories - Change categories
- Remove from library - Remove from library
- (**Library**) Search by genre (`genre1, genre2 genre3, ...`, e.g.: `action, adventure, fantasy`) - (**Library**) Search by genre (`genre1, genre2 genre3, ...`, e.g.: `action, adventure, fantasy`)
- (**Library**) Sort options - (**Library**) Sort options
- By last read - By last read
- By latest fetched chapter - By latest fetched chapter
- By latest uploaded chapter - By latest uploaded chapter
- (**Library**) Show number of manga in whole library and each category (these numbers are based on category manga and will include non library manga) - (**Library**) Show number of manga in whole library and each category (these numbers are based on category manga and will include non library manga)
- (**Library**) Optional continue read button - (**Library**) Optional continue read button
- (**Reader**) Added new settings - (**Reader**) Added new settings
- Skip duplicate chapters (opens the previous/next chapter from the same scanlator as the current one if it exists) - Skip duplicate chapters (opens the previous/next chapter from the same scanlator as the current one if it exists)
- Fit page to window - Fit page to window
- Scale small pages (only when "fit page to window" is enabled) - Scale small pages (only when "fit page to window" is enabled)
- Reader width (only when "fit page to window" is disabled) - Reader width (only when "fit page to window" is disabled)
- Double page mode: offset first page - Double page mode: offset first page
- (**Reader**) Retry failed image requests button - (**Reader**) Retry failed image requests button
- (**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,52 +1,55 @@
# 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
- Use categories to categorize your manga - Use categories to categorize your manga
- Select manga in your library and perform actions (e.g. download, change categories, mark as read, ...) on one or multiple manga - Select manga in your library and perform actions (e.g. download, change categories, mark as read, ...) on one or multiple manga
- Manga page - chapter management - Manga page - chapter management
- Filter/Sort the chapter list - Filter/Sort the chapter list
- Select chapters and perform actions (e.g. download, bookmark, mark as read, ...) on one or multiple manga - Select chapters and perform actions (e.g. download, bookmark, mark as read, ...) on one or multiple manga
- Select a range of manga/chapters by using shift + left click or long press - Select a range of manga/chapters by using shift + left click or long press
- Overview of duplicated manga in your library (settings > library) - Overview of duplicated manga in your library (settings > library)
- Reader - Reader
- Desktop and Mobile UI - Desktop and Mobile UI
- Default settings per reading mode - Default settings per reading mode
- Settings per manga - Settings per manga
- Reading modes (Single/Double Page, Continuous Vertical/Horizontal, Webtoon) - Reading modes (Single/Double Page, Continuous Vertical/Horizontal, Webtoon)
- Page scale modes (limit by width/height/screen, scale small pages, custom reader width) - Page scale modes (limit by width/height/screen, scale small pages, custom reader width)
- Image filters - Image filters
- Customizable keybinds - Customizable keybinds
- Auto scrolling - Auto scrolling
- Infinite chapter scrolling - Infinite chapter scrolling
- Option to ignore duplicated chapters while reading - Option to ignore duplicated chapters while reading
- Option to automatically download next chapters while reading - Option to automatically download next chapters while reading
- Option to automatically delete downloaded chapters after reading them - Option to automatically delete downloaded chapters after reading them
- ... - ...
- Download queue - Download queue
- Reading history (**rudimentary**) - Reading history (**rudimentary**)
- Settings per device (e.g. different reader settings for pc, phone and tablet) - Settings per device (e.g. different reader settings for pc, phone and tablet)
- Sources - Sources
- Migration of manga between sources - Migration of manga between sources
- Hide in library manga while browsing sources - Hide in library manga while browsing sources
- Save source searches to easily reuse them - Save source searches to easily reuse them
- Duplication check when adding a new manga to your library - Duplication check when adding a new manga to your library
- Quick add/remove a manga to your library in the source browse (hover with mouse on pc or long press on touch devices) - Quick add/remove a manga to your library in the source browse (hover with mouse on pc or long press on touch devices)
- App updates - App updates
- Inform about available WebUI and Server updates - Inform about available WebUI and Server updates
- Inform about successful WebUI and Server updates since the last time the app was used - Inform about successful WebUI and Server updates since the last time the app was used
- Themes - Themes
- Use predefined themes - Use predefined themes
- Create your own themes - Create your own themes
- 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,69 +1,66 @@
<!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" />
<link rel="shortcut icon" href="/favicon.ico" /> <link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<!-- <!--
manifest.json provides metadata used when your web app is installed on a manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="/site.webmanifest" crossorigin="use-credentials" /> <link rel="manifest" href="/site.webmanifest" crossorigin="use-credentials" />
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<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;
} }
})(); })();
if (backgroundColor) { if (backgroundColor) {
document.documentElement.style.backgroundColor = backgroundColor; document.documentElement.style.backgroundColor = backgroundColor;
// android chromium-based browser/pwa background color (e.g. top status bar and navigation bar) will change dynamically based on meta theme-color // android chromium-based browser/pwa background color (e.g. top status bar and navigation bar) will change dynamically based on meta theme-color
let themeColorMeta = document.querySelector('meta[name="theme-color"]'); let themeColorMeta = document.querySelector('meta[name="theme-color"]');
if (!themeColorMeta) { if (!themeColorMeta) {
themeColorMeta = document.createElement('meta'); themeColorMeta = document.createElement('meta');
themeColorMeta.setAttribute('name', 'theme-color'); themeColorMeta.setAttribute('name', 'theme-color');
document.head.appendChild(themeColorMeta); document.head.appendChild(themeColorMeta);
} }
if (themeColorMeta.getAttribute('content') !== backgroundColor) { if (themeColorMeta.getAttribute('content') !== backgroundColor) {
themeColorMeta.setAttribute('content', backgroundColor); themeColorMeta.setAttribute('content', backgroundColor);
} }
} }
</script> </script>
<!-- <!--
This HTML file is a template. This HTML file is a template.
If you open it directly in the browser, you will see an empty page. If you open it directly in the browser, you will see an empty page.
@@ -73,5 +70,5 @@
To begin the development, run `npm start` or `yarn start`. To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`. To create a production bundle, use `npm run build` or `yarn build`.
--> -->
</body> </body>
</html> </html>

View File

@@ -1,146 +1,145 @@
{ {
"name": "project", "name": "project",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"ci": "yarn install --frozen-lockfile", "ci": "yarn install --frozen-lockfile",
"setup-env-files": "tsx tools/scripts/setupEnvFiles.ts", "setup-env-files": "tsx tools/scripts/setupEnvFiles.ts",
"setup": "yarn ci && yarn setup-env-files", "setup": "yarn ci && yarn setup-env-files",
"dev": "yarn setup && vite", "dev": "yarn setup && vite",
"preview": "yarn setup && vite preview", "preview": "yarn setup && vite preview",
"build": "yarn setup && vite build", "build": "yarn setup && vite build",
"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",
"createCommitChangelog": "tsx tools/scripts/release/createCommitChangelog.ts", "format": "oxfmt --write .",
"createTranslationChangelog": "tsx tools/scripts/release/createTranslationChangelog.ts", "format:check": "oxfmt --check .",
"createReleaseChangelog": "tsx tools/scripts/release/createReleaseChangelog.ts", "createCommitChangelog": "tsx tools/scripts/release/createCommitChangelog.ts",
"updateDeps": "tsx tools/scripts/updateDependencies.ts", "createTranslationChangelog": "tsx tools/scripts/release/createTranslationChangelog.ts",
"gql:codegen-base": "graphql-codegen --config gql_codegen.ts", "createReleaseChangelog": "tsx tools/scripts/release/createReleaseChangelog.ts",
"gql:codegen-formatter": "tsx tools/scripts/codegenFormatter.ts", "updateDeps": "tsx tools/scripts/updateDependencies.ts",
"gql:codegen": "yarn gql:codegen-base && yarn gql:codegen-formatter", "gql:codegen-base": "graphql-codegen --config gql_codegen.ts",
"i18n:extract": "lingui extract --locale en --clean", "gql:codegen-formatter": "tsx tools/scripts/codegenFormatter.ts",
"i18n:gen-resources": "tsx tools/scripts/weblate/generatei18nResources.ts", "gql:codegen": "yarn gql:codegen-base && yarn gql:codegen-formatter",
"dayjs:gen-locales-array": "tsx tools/scripts/dayjs/generateDayJsLocales.ts", "i18n:extract": "lingui extract --locale en --clean",
"dayjs:gen-locales-import": "tsx tools/scripts/dayjs/generateDayJsLocalesImport.ts", "i18n:gen-resources": "tsx tools/scripts/weblate/generatei18nResources.ts",
"dayjs:gen-locales": "yarn dayjs:gen-locales-array && yarn dayjs:gen-locales-import", "dayjs:gen-locales-array": "tsx tools/scripts/dayjs/generateDayJsLocales.ts",
"manga:gen-type-tags": "tsx tools/scripts/manga/generateMangaTypeTags.ts", "dayjs:gen-locales-import": "tsx tools/scripts/dayjs/generateDayJsLocalesImport.ts",
"tsc": "tsgo", "dayjs:gen-locales": "yarn dayjs:gen-locales-array && yarn dayjs:gen-locales-import",
"tsc:legacy": "tsc", "manga:gen-type-tags": "tsx tools/scripts/manga/generateMangaTypeTags.ts",
"prepare": "husky" "tsc": "tsgo",
}, "tsc:legacy": "tsc",
"engines": { "prepare": "husky"
"node": ">=24" },
}, "dependencies": {
"lint-staged": { "@apollo/client": "4.1.6",
"*.{ts,tsx,js,jsx}": "eslint --fix" "@dnd-kit/core": "6.3.1",
}, "@dnd-kit/sortable": "10.0.0",
"resolutions": { "@dnd-kit/utilities": "3.2.2",
"@swc/core": "1.15.11" "@emotion/cache": "11.14.0",
}, "@emotion/react": "11.14.0",
"dependencies": { "@emotion/styled": "11.14.1",
"@apollo/client": "4.1.6", "@juggle/resize-observer": "3.4.0",
"@dnd-kit/core": "6.3.1", "@lingui/core": "5.9.2",
"@dnd-kit/sortable": "10.0.0", "@lingui/react": "5.9.2",
"@dnd-kit/utilities": "3.2.2", "@loadable/component": "5.16.7",
"@emotion/cache": "11.14.0", "@mantine/hooks": "8.3.16",
"@emotion/react": "11.14.0", "@mui/icons-material": "7.3.9",
"@emotion/styled": "11.14.1", "@mui/material": "7.3.9",
"@juggle/resize-observer": "3.4.0", "@mui/system": "7.3.9",
"@lingui/core": "5.9.2", "@mui/utils": "7.3.9",
"@lingui/react": "5.9.2", "@mui/x-date-pickers": "8.27.2",
"@loadable/component": "5.16.7", "@redux-devtools/extension": "3.3.0",
"@mantine/hooks": "8.3.16", "@vibrant/color": "4.0.4",
"@mui/icons-material": "7.3.9", "apollo-upload-client": "19.0.0",
"@mui/material": "7.3.9", "awaitable-component": "1.0.0",
"@mui/system": "7.3.9", "csstype": "3.2.3",
"@mui/utils": "7.3.9", "dayjs": "1.11.19",
"@mui/x-date-pickers": "8.27.2", "fast-average-color": "9.5.0",
"@redux-devtools/extension": "3.3.0", "file-selector": "2.1.2",
"@vibrant/color": "4.0.4", "graphql": "16.13.1",
"apollo-upload-client": "19.0.0", "graphql-sock": "1.0.1",
"awaitable-component": "1.0.0", "graphql-tag": "2.12.6",
"csstype": "3.2.3", "graphql-ws": "6.0.7",
"dayjs": "1.11.19", "immer": "11.1.4",
"fast-average-color": "9.5.0", "jsonrepair": "3.13.2",
"file-selector": "2.1.2", "koration": "1.0.0",
"graphql": "16.13.1", "material-ui-popup-state": "5.3.6",
"graphql-sock": "1.0.1", "mui-nested-menu": "4.0.3",
"graphql-tag": "2.12.6", "node-vibrant": "4.0.4",
"graphql-ws": "6.0.7", "notistack": "3.0.2",
"immer": "11.1.4", "p-limit": "7.3.0",
"jsonrepair": "3.13.2", "polished": "4.3.1",
"koration": "1.0.0", "react": "19.2.4",
"material-ui-popup-state": "5.3.6", "react-dom": "19.2.4",
"mui-nested-menu": "4.0.3", "react-hotkeys-hook": "5.2.4",
"node-vibrant": "4.0.4", "react-lazily": "0.9.2",
"notistack": "3.0.2", "react-router-dom": "6.26.1",
"p-limit": "7.3.0", "react-virtuoso": "4.18.3",
"polished": "4.3.1", "rxjs": "7.8.2",
"react": "19.2.4", "stylis": "4.3.6",
"react-dom": "19.2.4", "stylis-plugin-rtl": "2.1.1",
"react-hotkeys-hook": "5.2.4", "use-long-press": "3.3.0",
"react-lazily": "0.9.2", "use-query-params": "2.2.2",
"react-router-dom": "6.26.1", "webfontloader": "1.6.28",
"react-virtuoso": "4.18.3", "zustand": "5.0.11"
"rxjs": "7.8.2", },
"stylis": "4.3.6", "devDependencies": {
"stylis-plugin-rtl": "2.1.1", "@graphql-codegen/cli": "6.1.3",
"use-long-press": "3.3.0", "@graphql-codegen/client-preset": "5.2.4",
"use-query-params": "2.2.2", "@graphql-codegen/typescript-apollo-client-helpers": "4.0.0",
"webfontloader": "1.6.28", "@graphql-codegen/typescript-operations": "5.0.9",
"zustand": "5.0.11" "@lingui/cli": "5.9.2",
}, "@lingui/format-po": "5.9.2",
"devDependencies": { "@lingui/swc-plugin": "5.11.0",
"@graphql-codegen/cli": "6.1.3", "@lingui/vite-plugin": "5.9.2",
"@graphql-codegen/client-preset": "5.2.4", "@tony.ganchev/eslint-plugin-header": "3.2.6",
"@graphql-codegen/typescript-apollo-client-helpers": "4.0.0", "@types/node": "24.10.1",
"@graphql-codegen/typescript-operations": "5.0.9", "@types/react": "19.2.14",
"@lingui/cli": "5.9.2", "@types/react-beautiful-dnd": "13.1.8",
"@lingui/format-po": "5.9.2", "@types/react-dom": "19.2.3",
"@lingui/swc-plugin": "5.11.0", "@types/sanitize-html": "2.16.1",
"@lingui/vite-plugin": "5.9.2", "@types/stylis": "4.2.7",
"@types/node": "24.10.1", "@types/webfontloader": "1.6.38",
"@types/react": "19.2.14", "@types/yargs": "17.0.35",
"@types/react-beautiful-dnd": "13.1.8", "@typescript/native-preview": "7.0.0-dev.20260309.1",
"@types/react-dom": "19.2.3", "@vitejs/plugin-legacy": "7.2.1",
"@types/sanitize-html": "2.16.1", "@vitejs/plugin-react-swc": "4.2.3",
"@types/stylis": "4.2.7", "dotenv": "17.3.1",
"@types/webfontloader": "1.6.38", "eslint": "9.39.4",
"@types/yargs": "17.0.35", "eslint-plugin-lingui": "0.11.0",
"@tony.ganchev/eslint-plugin-header": "3.2.6", "eslint-plugin-no-relative-import-paths": "1.6.1",
"@vitejs/plugin-legacy": "7.2.1", "husky": "9.1.7",
"@vitejs/plugin-react-swc": "4.2.3", "lint-staged": "16.3.2",
"dotenv": "17.3.1", "oxfmt": "0.37.0",
"eslint": "9.39.4", "oxlint": "1.52.0",
"eslint-config-prettier": "10.1.8", "syncyarnlock": "1.0.19",
"eslint-import-resolver-typescript": "4.4.4", "terser": "5.46.0",
"eslint-plugin-import-x": "4.16.1", "tsx": "4.21.0",
"eslint-plugin-jsx-a11y": "6.10.2", "typescript": "5.9.3",
"eslint-plugin-lingui": "0.11.0", "typescript-eslint": "8.57.0",
"eslint-plugin-no-relative-import-paths": "1.6.1", "vite": "7.3.1",
"eslint-plugin-prettier": "5.5.5", "vite-plugin-node-polyfills": "0.25.0",
"eslint-plugin-react": "7.37.5", "vite-plugin-pwa": "1.2.0",
"eslint-plugin-react-hooks": "5.2.0", "vite-tsconfig-paths": "6.1.1",
"eslint-plugin-unused-imports": "4.4.1", "workbox-build": "7.4.0",
"globals": "15.15.0", "workbox-window": "7.4.0",
"husky": "9.1.7", "yargs": "18.0.0"
"lint-staged": "16.3.2", },
"prettier": "3.8.1", "resolutions": {
"syncyarnlock": "1.0.19", "@swc/core": "1.15.11"
"terser": "5.46.0", },
"tsx": "4.21.0", "lint-staged": {
"@typescript/native-preview": "7.0.0-dev.20260309.1", "*.{ts,tsx,js,jsx}": [
"typescript": "5.9.3", "oxfmt --write",
"typescript-eslint": "8.57.0", "oxlint --fix",
"vite": "7.3.1", "eslint --fix"
"vite-plugin-node-polyfills": "0.25.0", ],
"vite-plugin-pwa": "1.2.0", "*.{json,md,yml,yaml,css,scss,html,graphql}": "oxfmt --write"
"vite-tsconfig-paths": "6.1.1", },
"workbox-build": "7.4.0", "engines": {
"workbox-window": "7.4.0", "node": ">=24"
"yargs": "18.0.0" }
}
} }

View File

@@ -1,27 +1,27 @@
{ {
"name": "Suwayomi", "name": "Suwayomi",
"short_name": "Suwayomi", "short_name": "Suwayomi",
"start_url": ".", "start_url": ".",
"icons": [ "icons": [
{ {
"src": "./web-app-manifest-192x192.png", "src": "./web-app-manifest-192x192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "./web-app-manifest-192x192.png", "src": "./web-app-manifest-192x192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
}, },
{ {
"src": "./web-app-manifest-512x512.png", "src": "./web-app-manifest-512x512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
} }
], ],
"theme_color": "#3181C0", "theme_color": "#3181C0",
"background_color": "#3181C0", "background_color": "#3181C0",
"display": "standalone" "display": "standalone"
} }

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,9 +1,7 @@
{ {
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"include": [ "include": ["./**/*"],
"./**/*" "compilerOptions": {
], "moduleResolution": "node"
"compilerOptions": { }
"moduleResolution": "node"
}
} }

View File

@@ -1,43 +1,34 @@
{ {
"compilerOptions": { "compilerOptions": {
"paths": { "paths": {
"@/*": [ "@/*": ["./src/*"]
"./src/*" },
] "target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ESNEXT", "ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true
}, },
"target": "ES2020", "include": ["src"],
"useDefineForClassFields": true, "exclude": ["**/node_modules"],
"lib": [ "references": [
"ESNEXT", {
"ES2020", "path": "./tsconfig.node.json"
"DOM", }
"DOM.Iterable" ]
],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true
},
"include": [
"src"
],
"exclude": ["**/node_modules"],
"references": [
{
"path": "./tsconfig.node.json"
}
]
} }

View File

@@ -1,14 +1,10 @@
{ {
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"skipLibCheck": true, "skipLibCheck": true,
"module": "ESNext", "module": "ESNext",
"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