Compare commits

...
40 Commits
Author SHA1 Message Date
Crowdin Bot a7f7987906 New Crowdin translations by GitHub Action 2026-10-03 13:51:29 +00:00
dependabot[bot] 6ce43be400 Chore(deps): Bump the actions group across 1 directory with 4 updates (#14332)
Bumps the actions group with 4 updates in the / directory: [github/codeql-action/upload-sarif](https://github.com/github/codeql-action), [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [crowdin/github-action](https://github.com/crowdin/github-action).


Updates `github/codeql-action/upload-sarif` from 4.38.1 to 4.38.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1c5b675653bb5c22dbe9b12b556ec555138e09fd...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

Updates `github/codeql-action/init` from 4.38.1 to 4.38.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1c5b675653bb5c22dbe9b12b556ec555138e09fd...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

Updates `github/codeql-action/analyze` from 4.38.1 to 4.38.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1c5b675653bb5c22dbe9b12b556ec555138e09fd...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

Updates `crowdin/github-action` from 3.2.0 to 3.3.0
- [Release notes](https://github.com/crowdin/github-action/releases)
- [Commits](https://github.com/crowdin/github-action/compare/df474cdfb9f41d6ae777118749477c2cdf7cacc8...9c23991700c0ec5256fd41089b9d9d7d540e424e)

---
updated-dependencies:
- dependency-name: crowdin/github-action
  dependency-version: 3.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-02 20:42:36 -07:00
dependabot[bot] 0204009b66 Chore(deps): Bump the uv group across 1 directory with 2 updates (#14325)
Bumps the uv group with 2 updates in the / directory: [django](https://github.com/django/django) and [tornado](https://github.com/tornadoweb/tornado).


Updates `django` from 5.2.16 to 5.2.17
- [Commits](https://github.com/django/django/compare/5.2.16...5.2.17)

Updates `tornado` from 6.5.8 to 6.5.9
- [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst)
- [Commits](https://github.com/tornadoweb/tornado/compare/v6.5.8...v6.5.9)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.17
  dependency-type: direct:production
- dependency-name: tornado
  dependency-version: 6.5.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-03 01:07:57 +00:00
GitHub Actions cec0b99d2e Auto translate strings 2026-10-02 23:10:15 +00:00
Trenton H 3615a5a2a5 Fix: Ensure log rotation settings are converted to integers (#14343)
* Fix: Ensure the log rotatation options are integers

* This might have the same problem

* Just in case, transition these to the helper too
2026-10-02 23:08:48 +00:00
Trenton H 56687614df Fix: Wrap apt calls into a retry so we can ideally jump a slow mirror (#14344)
* Fix: Wrap apt calls into a retry so we can ideally jump from a slow mirror to a faster one

* Use the different synxtax for an in-repo usage
2026-10-02 15:55:54 -07:00
GitHub Actions f1ae9c00ac Auto translate strings 2026-10-02 05:59:48 +00:00
dependabot[bot] bc2ff953ed Chore(deps): Bump the frontend-angular-dependencies group (#14329)
Bumps the frontend-angular-dependencies group in /src-ui with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `22.1.7` | `22.2.0` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `22.1.7` | `22.2.0` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `22.1.7` | `22.2.0` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `22.1.7` | `22.2.0` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `22.1.7` | `22.2.0` |
| [@angular/localize](https://github.com/angular/angular) | `22.1.7` | `22.2.0` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `22.1.7` | `22.2.0` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `22.1.7` | `22.2.0` |
| [@angular-devkit/core](https://github.com/angular/angular-cli) | `22.1.8` | `22.2.0` |
| [@angular-devkit/schematics](https://github.com/angular/angular-cli) | `22.1.8` | `22.2.0` |
| [@angular/build](https://github.com/angular/angular-cli) | `22.1.8` | `22.2.0` |
| [@angular/cli](https://github.com/angular/angular-cli) | `22.1.8` | `22.2.0` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `22.1.7` | `22.2.0` |


Updates `@angular/cdk` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/v22.1.7...v22.2.0)

Updates `@angular/common` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.2.0/packages/common)

Updates `@angular/compiler` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.2.0/packages/compiler)

Updates `@angular/core` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.2.0/packages/core)

Updates `@angular/forms` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.2.0/packages/forms)

Updates `@angular/localize` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/compare/v22.1.7...v22.2.0)

Updates `@angular/platform-browser` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.2.0/packages/platform-browser)

Updates `@angular/router` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.2.0/packages/router)

Updates `@angular-devkit/core` from 22.1.8 to 22.2.0
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v22.1.8...v22.2.0)

Updates `@angular-devkit/schematics` from 22.1.8 to 22.2.0
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v22.1.8...v22.2.0)

Updates `@angular/build` from 22.1.8 to 22.2.0
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v22.1.8...v22.2.0)

Updates `@angular/cli` from 22.1.8 to 22.2.0
- [Release notes](https://github.com/angular/angular-cli/releases)
- [Changelog](https://github.com/angular/angular-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/angular-cli/compare/v22.1.8...v22.2.0)

Updates `@angular/compiler-cli` from 22.1.7 to 22.2.0
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.2.0/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-version: 22.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/common"
  dependency-version: 22.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler"
  dependency-version: 22.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/core"
  dependency-version: 22.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/forms"
  dependency-version: 22.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/localize"
  dependency-version: 22.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/platform-browser"
  dependency-version: 22.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/router"
  dependency-version: 22.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/core"
  dependency-version: 22.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular-devkit/schematics"
  dependency-version: 22.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/build"
  dependency-version: 22.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/cli"
  dependency-version: 22.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler-cli"
  dependency-version: 22.2.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-angular-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-02 05:58:14 +00:00
dependabot[bot] e17689a91b Chore(deps-dev): Bump prettier from 3.9.8 to 3.9.9 in /src-ui (#14331)
Bumps [prettier](https://github.com/prettier/prettier) from 3.9.8 to 3.9.9.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.8...3.9.9)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.9.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-02 05:42:48 +00:00
dependabot[bot] 764d1a492d Chore(deps-dev): Bump the frontend-eslint-dependencies group (#14330)
Bumps the frontend-eslint-dependencies group in /src-ui with 3 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin), [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) and [@typescript-eslint/utils](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/utils).


Updates `@typescript-eslint/eslint-plugin` from 8.70.0 to 8.70.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.70.0 to 8.70.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.1/packages/parser)

Updates `@typescript-eslint/utils` from 8.70.0 to 8.70.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.70.1/packages/utils)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.70.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-eslint-dependencies
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.70.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-eslint-dependencies
- dependency-name: "@typescript-eslint/utils"
  dependency-version: 8.70.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-eslint-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-02 05:31:40 +00:00
9d50a3b261 Fix: ship pdf.js CMaps so CJK documents render in the viewer (#14318)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2026-10-02 03:27:15 +00:00
dependabot[bot] da3de299fc Chore(deps-dev): Bump zensical (#14326)
Bumps the development group with 1 update: [zensical](https://github.com/zensical/zensical).


Updates `zensical` from 0.0.64 to 0.0.65
- [Release notes](https://github.com/zensical/zensical/releases)
- [Commits](https://github.com/zensical/zensical/compare/v0.0.64...v0.0.65)

---
updated-dependencies:
- dependency-name: zensical
  dependency-version: 0.0.65
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 20:32:40 +00:00
GitHub Actions 3a022e2dc4 Auto translate strings 2026-10-01 20:02:19 +00:00
shamoon 58bda48682 Enhancement: more control over suggestion requests (#14258) 2026-10-01 20:00:52 +00:00
dependabot[bot] b2a2e15ead Chore(deps): Bump the uv group across 1 directory with 2 updates (#14314)
Bumps the uv group with 2 updates in the / directory: [pyjwt](https://github.com/jpadilla/pyjwt) and [urllib3](https://github.com/urllib3/urllib3).


Updates `pyjwt` from 2.13.0 to 2.15.0
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.13.0...2.15.0)

Updates `urllib3` from 2.7.0 to 2.8.0
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.7.0...2.8.0)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-version: 2.15.0
  dependency-type: indirect
- dependency-name: urllib3
  dependency-version: 2.8.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-01 17:14:42 +00:00
shamoon e865185425 Chore: anchor admin url pattern (#14316) 2026-10-01 01:22:06 -07:00
shamoon d02c19a09e Chorehancement: set manifest CORS for credentials (#14307) 2026-09-30 08:03:04 -07:00
dependabot[bot]andstumpylog 4a106454ce Chore(deps): Bump the utilities-minor group across 1 directory with 7 updates (#14305)
* Chore(deps): Bump the utilities-minor group across 1 directory with 7 updates

Bumps the utilities-minor group with 7 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [django-guardian](https://github.com/django-guardian/django-guardian) | `3.4.1` | `3.5.0` |
| [flower](https://github.com/mher/flower) | `2.1.0` | `2.2.0` |
| [sentence-transformers](https://github.com/huggingface/sentence-transformers) | `6.0.1` | `6.1.0` |
| [watchfiles](https://github.com/samuelcolvin/watchfiles) | `1.2.0` | `1.3.0` |
| [mysqlclient](https://github.com/PyMySQL/mysqlclient) | `2.2.8` | `2.3.0` |
| [faker](https://github.com/joke2k/faker) | `40.38.0` | `40.39.0` |
| [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) | `16.6.1` | `16.7` |



Updates `django-guardian` from 3.4.1 to 3.5.0
- [Release notes](https://github.com/django-guardian/django-guardian/releases)
- [Commits](https://github.com/django-guardian/django-guardian/compare/3.4.1...3.5.0)

Updates `flower` from 2.1.0 to 2.2.0
- [Release notes](https://github.com/mher/flower/releases)
- [Commits](https://github.com/mher/flower/compare/v2.1.0...v2.2.0)

Updates `sentence-transformers` from 6.0.1 to 6.1.0
- [Release notes](https://github.com/huggingface/sentence-transformers/releases)
- [Commits](https://github.com/huggingface/sentence-transformers/compare/v6.0.1...v6.1.0)

Updates `watchfiles` from 1.2.0 to 1.3.0
- [Release notes](https://github.com/samuelcolvin/watchfiles/releases)
- [Commits](https://github.com/samuelcolvin/watchfiles/compare/v1.2.0...v1.3.0)

Updates `mysqlclient` from 2.2.8 to 2.3.0
- [Release notes](https://github.com/PyMySQL/mysqlclient/releases)
- [Changelog](https://github.com/PyMySQL/mysqlclient/blob/main/HISTORY.rst)
- [Commits](https://github.com/PyMySQL/mysqlclient/compare/v2.2.8...v2.3.0)

Updates `faker` from 40.38.0 to 40.39.0
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.md)
- [Commits](https://github.com/joke2k/faker/compare/v40.38.0...v40.39.0)

Updates `pytest-rerunfailures` from 16.6.1 to 16.7
- [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst)
- [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/16.6.1...16.7)

---
updated-dependencies:
- dependency-name: django-guardian
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: utilities-minor
- dependency-name: faker
  dependency-version: 40.39.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: utilities-minor
- dependency-name: flower
  dependency-version: 2.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: utilities-minor
- dependency-name: mysqlclient
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: utilities-minor
- dependency-name: pytest-rerunfailures
  dependency-version: '16.7'
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: utilities-minor
- dependency-name: sentence-transformers
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: utilities-minor
- dependency-name: watchfiles
  dependency-version: 1.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: utilities-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Linting

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: stumpylog <797416+stumpylog@users.noreply.github.com>
2026-09-29 13:28:35 -07:00
dependabot[bot] 4254100fae docker-compose(deps): bump greenmail/standalone in /docker/compose (#14281)
Bumps greenmail/standalone from 2.1.13 to 2.1.14.

---
updated-dependencies:
- dependency-name: greenmail/standalone
  dependency-version: 2.1.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-29 11:31:43 -07:00
dependabot[bot] 74f727b1f3 docker(deps): Bump astral-sh/uv (#14282)
Bumps [astral-sh/uv](https://github.com/astral-sh/uv) from 0.12.16-python3.14-trixie-slim to 0.12.20-python3.14-trixie-slim.
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/uv/compare/0.12.16...0.12.20)

---
updated-dependencies:
- dependency-name: astral-sh/uv
  dependency-version: 0.12.19-python3.14-trixie-slim
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-29 10:08:39 -07:00
dependabot[bot] fb6fad952e Chore(deps): Bump the pre-commit-dependencies group across 1 directory with 3 updates (#14283)
Bumps the pre-commit-dependencies group with 3 updates in the / directory: [https://github.com/rbubley/mirrors-prettier](https://github.com/rbubley/mirrors-prettier), prettier and [https://github.com/astral-sh/ruff-pre-commit](https://github.com/astral-sh/ruff-pre-commit).


Updates `https://github.com/rbubley/mirrors-prettier` from v3.9.6 to 3.9.8
- [Commits](https://github.com/rbubley/mirrors-prettier/compare/v3.9.6...v3.9.8)

Updates `prettier` from 3.9.6 to 3.9.8

Updates `https://github.com/astral-sh/ruff-pre-commit` from v0.16.7 to 0.16.8
- [Release notes](https://github.com/astral-sh/ruff-pre-commit/releases)
- [Commits](https://github.com/astral-sh/ruff-pre-commit/compare/v0.16.7...v0.16.8)

---
updated-dependencies:
- dependency-name: https://github.com/astral-sh/ruff-pre-commit
  dependency-version: 0.16.8
  dependency-type: direct:production
  dependency-group: pre-commit-dependencies
- dependency-name: https://github.com/rbubley/mirrors-prettier
  dependency-version: 3.9.8
  dependency-type: direct:production
  dependency-group: pre-commit-dependencies
- dependency-name: prettier
  dependency-version: 3.9.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: pre-commit-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-29 09:33:49 -07:00
dependabot[bot] f9b4e0c8e0 Chore(deps): Bump the utilities-patch group across 1 directory with 6 updates (#14297)
Bumps the utilities-patch group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [llama-index-core](https://github.com/run-llama/llama_index) | `0.14.24` | `0.14.25` |
| [tantivy](https://github.com/quickwit-oss/tantivy-py) | `0.26.0` | `0.26.2` |
| [psycopg-pool](https://github.com/psycopg/psycopg) | `3.3.1` | `3.3.2` |
| [zensical](https://github.com/zensical/zensical) | `0.0.60` | `0.0.64` |
| [prek](https://github.com/j178/prek) | `0.5.2` | `0.5.3` |
| [ruff](https://github.com/astral-sh/ruff) | `0.16.7` | `0.16.8` |



Updates `llama-index-core` from 0.14.24 to 0.14.25
- [Release notes](https://github.com/run-llama/llama_index/releases)
- [Changelog](https://github.com/run-llama/llama_index/blob/main/CHANGELOG.md)
- [Commits](https://github.com/run-llama/llama_index/compare/v0.14.24...v0.14.25)

Updates `tantivy` from 0.26.0 to 0.26.2
- [Release notes](https://github.com/quickwit-oss/tantivy-py/releases)
- [Commits](https://github.com/quickwit-oss/tantivy-py/compare/0.26.0...0.26.2)

Updates `psycopg-pool` from 3.3.1 to 3.3.2
- [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst)
- [Commits](https://github.com/psycopg/psycopg/compare/3.3.1...3.3.2)

Updates `zensical` from 0.0.60 to 0.0.64
- [Release notes](https://github.com/zensical/zensical/releases)
- [Commits](https://github.com/zensical/zensical/compare/v0.0.60...v0.0.64)

Updates `prek` from 0.5.2 to 0.5.3
- [Release notes](https://github.com/j178/prek/releases)
- [Changelog](https://github.com/j178/prek/blob/master/CHANGELOG.md)
- [Commits](https://github.com/j178/prek/compare/v0.5.2...v0.5.3)

Updates `ruff` from 0.16.7 to 0.16.8
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.16.7...0.16.8)

---
updated-dependencies:
- dependency-name: llama-index-core
  dependency-version: 0.14.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: utilities-patch
- dependency-name: prek
  dependency-version: 0.5.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: utilities-patch
- dependency-name: psycopg-pool
  dependency-version: 3.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: utilities-patch
- dependency-name: ruff
  dependency-version: 0.16.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: utilities-patch
- dependency-name: tantivy
  dependency-version: 0.26.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: utilities-patch
- dependency-name: zensical
  dependency-version: 0.0.63
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: utilities-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-29 08:52:09 -07:00
dependabot[bot] e5041097af Chore(deps): Bump the actions group across 1 directory with 10 updates (#14301)
Bumps the actions group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) | `10.1.0` | `10.2.0` |
| [codecov/codecov-action](https://github.com/codecov/codecov-action) | `7.0.0` | `7.1.1` |
| [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `4.3.0` | `4.4.1` |
| [docker/build-push-action](https://github.com/docker/build-push-action) | `7.3.0` | `7.4.0` |
| [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |
| [stumpylog/image-cleaner-action/ephemeral](https://github.com/stumpylog/image-cleaner-action) | `0.12.0` | `0.13.0` |
| [stumpylog/image-cleaner-action/untagged](https://github.com/stumpylog/image-cleaner-action) | `0.12.0` | `0.13.0` |
| [github/codeql-action/init](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |
| [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.38.0` | `4.38.1` |
| [crowdin/github-action](https://github.com/crowdin/github-action) | `3.0.2` | `3.2.0` |



Updates `astral-sh/setup-uv` from 10.1.0 to 10.2.0
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](https://github.com/astral-sh/setup-uv/compare/bec219d24cd3e171d82865faccec33120bb574f4...c18668ad3cf93ea998bef934396af7bb5c839dc7)

Updates `codecov/codecov-action` from 7.0.0 to 7.1.1
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/fb8b3582c8e4def4969c97caa2f19720cb33a72f...303a32d7a59b442fa8d48b6a1cc6825c09c847a5)

Updates `docker/setup-buildx-action` from 4.3.0 to 4.4.1
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/37fe631027851001ddb9b187196cc803df7f5f0e...f87e5991a6d7451dcb8d9637bfbc97413f497069)

Updates `docker/build-push-action` from 7.3.0 to 7.4.0
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/53b7df96c91f9c12dcc8a07bcb9ccacbed38856a...c3c9e263c25d99ce0380d002d59b67737d91b0dc)

Updates `github/codeql-action/upload-sarif` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

Updates `stumpylog/image-cleaner-action/ephemeral` from 0.12.0 to 0.13.0
- [Release notes](https://github.com/stumpylog/image-cleaner-action/releases)
- [Changelog](https://github.com/stumpylog/image-cleaner-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stumpylog/image-cleaner-action/compare/4fe057d991d63b8f6d5d22c40f17c1bca2226537...21f875bab2376314e0525c614e923c669434f0e4)

Updates `stumpylog/image-cleaner-action/untagged` from 0.12.0 to 0.13.0
- [Release notes](https://github.com/stumpylog/image-cleaner-action/releases)
- [Changelog](https://github.com/stumpylog/image-cleaner-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stumpylog/image-cleaner-action/compare/4fe057d991d63b8f6d5d22c40f17c1bca2226537...21f875bab2376314e0525c614e923c669434f0e4)

Updates `github/codeql-action/init` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

Updates `github/codeql-action/analyze` from 4.38.0 to 4.38.1
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...1c5b675653bb5c22dbe9b12b556ec555138e09fd)

Updates `crowdin/github-action` from 3.0.2 to 3.2.0
- [Release notes](https://github.com/crowdin/github-action/releases)
- [Commits](https://github.com/crowdin/github-action/compare/0d5670f539973aea2f01abce61a8989934df0025...df474cdfb9f41d6ae777118749477c2cdf7cacc8)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 10.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: codecov/codecov-action
  dependency-version: 7.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: docker/setup-buildx-action
  dependency-version: 4.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: docker/build-push-action
  dependency-version: 7.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: stumpylog/image-cleaner-action/ephemeral
  dependency-version: 0.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: stumpylog/image-cleaner-action/untagged
  dependency-version: 0.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: crowdin/github-action
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-29 08:18:28 -07:00
Trenton H 61f36eb253 Chore: Disallow prerelease resolution in uv (#14296)
uv's default "if-necessary" prerelease policy will silently select a
prerelease package when no stable release satisfies the combined
constraint set, even without an explicit opt-in.  Disallow pre-releases
instead
2026-09-28 18:49:21 +00:00
Trenton H e50bcc2efb Chore: Updates typing baseline files (#14295)
* Chore: Refresh the mypy and pyrefly baselines

* Chore: Type factory calls as the model they build for mypy

factory-boy leaves Factory() unannotated, so mypy took UserFactory() to
be a factory instance rather than a User. A shared TypedModelFactory
base declares the call as returning the model for type checking only.

* Refresh again after rebase
2026-09-28 11:32:05 -07:00
dependabot[bot] 88000aab45 Chore(deps-dev): Bump the frontend-jest-dependencies group (#14286)
Bumps the frontend-jest-dependencies group in /src-ui with 2 updates: [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) and [jest-environment-jsdom](https://github.com/jestjs/jest/tree/HEAD/packages/jest-environment-jsdom).


Updates `jest` from 30.5.1 to 30.5.2
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.5.2/packages/jest)

Updates `jest-environment-jsdom` from 30.5.1 to 30.5.2
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.5.2/packages/jest-environment-jsdom)

---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.5.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-jest-dependencies
- dependency-name: jest-environment-jsdom
  dependency-version: 30.5.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-jest-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 09:22:21 +00:00
dependabot[bot] 01b4ab392c Chore(deps-dev): Bump eslint (#14287)
Bumps the frontend-eslint-dependencies group in /src-ui with 1 update: [eslint](https://github.com/eslint/eslint).


Updates `eslint` from 10.10.0 to 10.11.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v10.10.0...v10.11.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: frontend-eslint-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 09:11:55 +00:00
dependabot[bot] 52aa8758e3 Chore(deps-dev): Bump @types/node from 26.5.0 to 26.6.2 in /src-ui (#14288)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.5.0 to 26.6.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.6.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 09:00:39 +00:00
dependabot[bot] 4b7cc1c0c3 Chore(deps-dev): Bump prettier from 3.9.6 to 3.9.8 in /src-ui (#14289)
Bumps [prettier](https://github.com/prettier/prettier) from 3.9.6 to 3.9.8.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.6...3.9.8)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.9.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 08:48:03 +00:00
GitHub Actions f02d7455be Auto translate strings 2026-09-28 08:32:00 +00:00
dependabot[bot] a61c35fcd8 Chore(deps): Bump the frontend-angular-dependencies group (#14285)
Bumps the frontend-angular-dependencies group in /src-ui with 10 updates:

| Package | From | To |
| --- | --- | --- |
| [@angular/cdk](https://github.com/angular/components) | `22.1.6` | `22.1.7` |
| [@angular/common](https://github.com/angular/angular/tree/HEAD/packages/common) | `22.1.6` | `22.1.7` |
| [@angular/compiler](https://github.com/angular/angular/tree/HEAD/packages/compiler) | `22.1.6` | `22.1.7` |
| [@angular/core](https://github.com/angular/angular/tree/HEAD/packages/core) | `22.1.6` | `22.1.7` |
| [@angular/forms](https://github.com/angular/angular/tree/HEAD/packages/forms) | `22.1.6` | `22.1.7` |
| [@angular/localize](https://github.com/angular/angular) | `22.1.6` | `22.1.7` |
| [@angular/platform-browser](https://github.com/angular/angular/tree/HEAD/packages/platform-browser) | `22.1.6` | `22.1.7` |
| [@angular/router](https://github.com/angular/angular/tree/HEAD/packages/router) | `22.1.6` | `22.1.7` |
| [@ng-select/ng-select](https://github.com/ng-select/ng-select) | `24.1.1` | `24.1.2` |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli) | `22.1.6` | `22.1.7` |


Updates `@angular/cdk` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/components/releases)
- [Changelog](https://github.com/angular/components/blob/main/CHANGELOG.md)
- [Commits](https://github.com/angular/components/compare/v22.1.6...v22.1.7)

Updates `@angular/common` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/common)

Updates `@angular/compiler` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/compiler)

Updates `@angular/core` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/core)

Updates `@angular/forms` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/forms)

Updates `@angular/localize` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/compare/v22.1.6...v22.1.7)

Updates `@angular/platform-browser` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/platform-browser)

Updates `@angular/router` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/router)

Updates `@ng-select/ng-select` from 24.1.1 to 24.1.2
- [Release notes](https://github.com/ng-select/ng-select/releases)
- [Commits](https://github.com/ng-select/ng-select/compare/v24.1.1...v24.1.2)

Updates `@angular/compiler-cli` from 22.1.6 to 22.1.7
- [Release notes](https://github.com/angular/angular/releases)
- [Commits](https://github.com/angular/angular/commits/v22.1.7/packages/compiler-cli)

---
updated-dependencies:
- dependency-name: "@angular/cdk"
  dependency-version: 22.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/common"
  dependency-version: 22.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler"
  dependency-version: 22.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/core"
  dependency-version: 22.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/forms"
  dependency-version: 22.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/localize"
  dependency-version: 22.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/platform-browser"
  dependency-version: 22.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/router"
  dependency-version: 22.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@ng-select/ng-select"
  dependency-version: 24.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
- dependency-name: "@angular/compiler-cli"
  dependency-version: 22.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-angular-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 08:30:31 +00:00
GitHub Actions 64552d5d5b Auto translate strings 2026-09-28 06:27:28 +00:00
shamoon a59edf3e5b Fix: use version page_count for versioned document (#14280) 2026-09-27 23:26:00 -07:00
Bernát Gábor 6a5d06ee26 Chore: replace bleach with turbohtml (#14269)
bleach's repository is archived, and it depends on html5lib, which has not had
a release since 2020. turbohtml provides a bleach-compatible clean and a native
linkify that runs in linear time, so the 2048-character cap on email
linkification from #13187 is no longer needed and long messages get their
email addresses linked again.
2026-09-27 16:50:19 -07:00
GitHub Actions 126ec414a8 Auto translate strings 2026-09-27 14:08:00 +00:00
shamoon 773744d04b Enhancement: include Django admin with 2FA (#14270) 2026-09-27 07:06:26 -07:00
shamoon 7b98085b31 Fix: prevent dev pdf viewer link flash (#14268) 2026-09-26 07:02:49 -07:00
shamoon b3bf220f56 Fix: rest of PDF link fix 2026-09-25 20:08:36 -07:00
shamoon d4429c3cc7 Fix: allow pointer events for pdf links in pngx viewer (#14264) 2026-09-25 18:35:21 -07:00
Trenton H 4f777d7438 Feature: propagate resolved secrets to interactive container shells (#14254)
docker exec bash bypasses s6, so *_FILE secrets resolved by init-env-file
into /run/s6/container_environment aren't visible even though s6 services
and management wrappers already see them via with-contenv.

Add /etc/profile.d/contenv.sh, sourced by both login and non-login
interactive bash shells, to export those resolved values into the shell.
2026-09-24 13:07:41 -07:00
150 changed files with 28091 additions and 31613 deletions
+53
View File
@@ -0,0 +1,53 @@
name: Install apt packages
description: >
Installs apt packages with a per-attempt timeout and retries. The hosted runner's Azure apt mirror intermittently serves archives at a crawl, which apt's own timeout does not catch. A stuck attempt is killed and retried, which usually lands on a healthy mirror node.
inputs:
packages:
description: Space separated list of packages to install
required: true
attempts:
description: Maximum number of attempts
default: "3"
attempt-timeout:
description: Seconds before a single attempt is killed
default: "180"
runs:
using: composite
steps:
- name: Install ${{ inputs.packages }}
shell: bash
env:
PACKAGES: ${{ inputs.packages }}
ATTEMPTS: ${{ inputs.attempts }}
ATTEMPT_TIMEOUT: ${{ inputs.attempt-timeout }}
run: |
SUDO=""
if [ "$(id -u)" -ne 0 ]; then
SUDO="sudo"
fi
attempt_install() {
$SUDO apt-get update -q
# Intentionally unquoted, this is a list of package names
$SUDO apt-get install -q --yes --no-install-recommends $PACKAGES
}
export -f attempt_install
export SUDO PACKAGES
for attempt in $(seq 1 "${ATTEMPTS}"); do
echo "::group::apt install attempt ${attempt}/${ATTEMPTS}"
start=$(date +%s)
if timeout --kill-after=10 "${ATTEMPT_TIMEOUT}" bash -c attempt_install; then
echo "::endgroup::"
echo "Installed in $(( $(date +%s) - start ))s"
exit 0
fi
echo "::endgroup::"
echo "::warning::apt install attempt ${attempt}/${ATTEMPTS} failed or exceeded ${ATTEMPT_TIMEOUT}s"
# A killed attempt can leave dpkg half configured
$SUDO dpkg --configure -a || true
done
echo "::error::apt install failed after ${ATTEMPTS} attempts"
exit 1
-1
View File
@@ -59,7 +59,6 @@ updates:
- "drf-*"
- "djangorestframework"
- "whitenoise"
- "bleach"
- "jinja2"
# Async, Task Queuing & Caching
async-tasks:
+8 -9
View File
@@ -102,17 +102,16 @@ jobs:
with:
python-version: "${{ matrix.python-version }}"
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: true
python-version: ${{ steps.setup-python.outputs.python-version }}
- name: Install system dependencies
timeout-minutes: 10
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends \
unpaper tesseract-ocr imagemagick ghostscript poppler-utils
timeout-minutes: 12
uses: $/.github/actions/apt-install
with:
packages: unpaper tesseract-ocr imagemagick ghostscript poppler-utils
- name: Configure ImageMagick
run: |
sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml
@@ -139,13 +138,13 @@ jobs:
pytest
- name: Upload test results to Codecov
if: always()
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
flags: backend-python-${{ matrix.python-version }}
files: junit.xml
report_type: test_results
- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
flags: backend-python-${{ matrix.python-version }}
files: coverage.xml
@@ -176,7 +175,7 @@ jobs:
with:
python-version: "${{ env.DEFAULT_PYTHON }}"
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: true
+3 -3
View File
@@ -106,7 +106,7 @@ jobs:
echo "repository=${repo_name}"
echo "name=${repo_name}" >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Login to GitHub Container Registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
@@ -132,7 +132,7 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
- name: Build and push by digest
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
file: ./Dockerfile
@@ -182,7 +182,7 @@ jobs:
echo "Downloaded digests:"
ls -la /tmp/digests/
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Login to GitHub Container Registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
+1 -1
View File
@@ -78,7 +78,7 @@ jobs:
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: true
+7 -6
View File
@@ -174,13 +174,13 @@ jobs:
run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
- name: Upload test results to Codecov
if: always()
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
flags: frontend-node-${{ matrix.node-version }}
directory: src-ui/
report_type: test_results
- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
flags: frontend-node-${{ matrix.node-version }}
directory: src-ui/coverage/
@@ -216,7 +216,7 @@ jobs:
with:
python-version: '3.12'
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: '0.12.x'
enable-cache: false
@@ -231,9 +231,10 @@ jobs:
- name: Install dependencies
run: cd src-ui && pnpm install --frozen-lockfile
- name: Install backend system dependencies
run: |
apt-get update
apt-get install --yes --quiet --no-install-recommends libmagic1
timeout-minutes: 12
uses: $/.github/actions/apt-install
with:
packages: libmagic1
- name: Install backend dependencies
env:
PYTHON_VERSION: ${{ steps.setup-python.outputs.python-version }}
+6 -5
View File
@@ -59,7 +59,7 @@ jobs:
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: false
@@ -70,9 +70,10 @@ jobs:
run: |
uv sync --python "${PYTHON_VERSION}" --dev --frozen
- name: Install system dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends gettext liblept5
timeout-minutes: 12
uses: $/.github/actions/apt-install
with:
packages: gettext liblept5
# ---- Build Documentation ----
- name: Build documentation
env:
@@ -212,7 +213,7 @@ jobs:
with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: false
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
- name: Run Semgrep
run: semgrep scan --config auto --sarif-output results.sarif
- name: Upload results to GitHub code scanning
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
if: always()
with:
sarif_file: results.sarif
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
steps:
- name: Clean temporary images
if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/ephemeral@4fe057d991d63b8f6d5d22c40f17c1bca2226537 # v0.12.0
uses: stumpylog/image-cleaner-action/ephemeral@21f875bab2376314e0525c614e923c669434f0e4 # v0.13.0
with:
token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}"
@@ -56,7 +56,7 @@ jobs:
steps:
- name: Clean untagged images
if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/untagged@4fe057d991d63b8f6d5d22c40f17c1bca2226537 # v0.12.0
uses: stumpylog/image-cleaner-action/untagged@21f875bab2376314e0525c614e923c669434f0e4 # v0.13.0
with:
token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}"
+2 -2
View File
@@ -39,7 +39,7 @@ jobs:
persist-credentials: false
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -47,4 +47,4 @@ jobs:
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
token: ${{ secrets.PNGX_BOT_PAT }}
persist-credentials: false
- name: crowdin action
uses: crowdin/github-action@0d5670f539973aea2f01abce61a8989934df0025 # v3.0.2
uses: crowdin/github-action@9c23991700c0ec5256fd41089b9d9d7d540e424e # v3.3.0
with:
upload_translations: false
download_translations: true
+5 -4
View File
@@ -25,11 +25,12 @@ jobs:
id: setup-python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- name: Install system dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -qq --no-install-recommends gettext
timeout-minutes: 12
uses: $/.github/actions/apt-install
with:
packages: gettext
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: true
+706 -456
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -38,7 +38,7 @@ repos:
- json
# See https://github.com/prettier/prettier/issues/15742 for the fork reason
- repo: https://github.com/rbubley/mirrors-prettier
rev: 'v3.9.6'
rev: 'v3.9.8'
hooks:
- id: prettier
types_or:
@@ -46,11 +46,11 @@ repos:
- ts
- markdown
additional_dependencies:
- prettier@3.9.6
- prettier@3.9.8
- 'prettier-plugin-organize-imports@4.3.0'
# Python hooks
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.7
rev: v0.16.8
hooks:
- id: ruff-check
- id: ruff-format
+4804 -12941
View File
File diff suppressed because one or more lines are too long
+4 -2
View File
@@ -30,7 +30,7 @@ RUN set -eux \
# Purpose: Installs s6-overlay and rootfs
# Comments:
# - Don't leave anything extra in here either
FROM ghcr.io/astral-sh/uv:0.12.16-python3.14-trixie-slim AS s6-overlay-base
FROM ghcr.io/astral-sh/uv:0.12.20-python3.14-trixie-slim AS s6-overlay-base
WORKDIR /usr/src/s6
@@ -171,7 +171,9 @@ RUN set -eux \
&& cp /etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml \
&& echo "Cleaning up image layer" \
&& rm --force --verbose *.deb \
&& rm --recursive --force --verbose /var/lib/apt/lists/*
&& rm --recursive --force --verbose /var/lib/apt/lists/* \
&& echo "Configuring interactive shells to source the s6 container environment" \
&& echo '. /etc/profile.d/contenv.sh' >> /etc/bash.bashrc
WORKDIR /usr/src/paperless/src/
+1 -1
View File
@@ -24,7 +24,7 @@ services:
network_mode: host
restart: unless-stopped
greenmail:
image: docker.io/greenmail/standalone:2.1.13
image: docker.io/greenmail/standalone:2.1.14
hostname: greenmail
container_name: greenmail
environment:
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
# Source s6 container environment for interactive shells.
# Ensures variables resolved from *_FILE secret injection are visible
# when using 'docker exec bash'. Does not affect s6 services (those
# use with-contenv directly). Has no effect in non-container contexts
# because the directory will not exist.
# Note: sh/dash shells opened via 'docker exec sh' are not covered;
# only bash-based sessions benefit from this file.
_pngx_contenv="/run/s6/container_environment"
if [ -d "${_pngx_contenv}" ]; then
for _pngx_f in "${_pngx_contenv}"/*; do
[ -f "${_pngx_f}" ] || continue
_pngx_name=$(basename "${_pngx_f}")
_pngx_val=$(cat "${_pngx_f}")
export "${_pngx_name}=${_pngx_val}"
done
fi
unset _pngx_contenv _pngx_f _pngx_name _pngx_val
+9 -7
View File
@@ -136,13 +136,15 @@ for suggested generation and embedding models.
### AI-assisted suggestions
With AI enabled, Paperless-ngx can suggest a title, tags, correspondent, document type,
storage path and dates by sending the document to the LLM. This is **opt-in per request**
and surfaces through the "Suggest" control on the document detail page, alongside the
classic classifier-based suggestions — it does not disable them. Suggestions are requested
automatically when you open a document that carries an inbox tag unless "Automatically request
suggestions for inbox documents" under Settings > Documents is disabled. Suggestion output
language can be steered with
[`PAPERLESS_AI_LLM_OUTPUT_LANGUAGE`](configuration.md#PAPERLESS_AI_LLM_OUTPUT_LANGUAGE)
storage path and dates by sending the document to the LLM using "Suggest" button on the document
detail page. You can choose which type of suggestions are requested by default under Settings >
Documents, either ML (classifier-based) suggestions, AI suggestions, or both. When both are requested
the results are combined.
Suggestions are requested automatically when you open a document that carries an inbox tag
unless "Automatically request suggestions for inbox documents" under Settings > Documents is disabled.
Suggestion output language can be steered with [`PAPERLESS_AI_LLM_OUTPUT_LANGUAGE`](configuration.md#PAPERLESS_AI_LLM_OUTPUT_LANGUAGE)
(otherwise it follows the user's UI language).
### The LLM index (RAG) and similar documents
+7 -7
View File
@@ -19,7 +19,6 @@ classifiers = [
dependencies = [
"azure-ai-documentintelligence>=1.0.2",
"babel>=2.17",
"bleach~=6.4.0",
"celery[redis]~=5.6.2",
"channels~=4.2",
"channels-redis~=4.2",
@@ -35,7 +34,7 @@ dependencies = [
"django-cors-headers~=4.9.0",
"django-extensions~=4.1",
"django-filter~=25.1",
"django-guardian>=3.3.3,<3.5",
"django-guardian>=3.3.3,<3.6",
"django-multiselectfield~=1.0.1",
"django-rich~=2.2.0",
"django-soft-delete~=1.0.18",
@@ -45,7 +44,7 @@ dependencies = [
"drf-spectacular-sidecar>=2026.7.1,<2026.10",
"drf-writable-nested~=0.7.1",
"filelock~=3.32.0",
"flower>=2.0.1,<2.2",
"flower>=2.0.1,<2.3",
"gotenberg-client[httpx]~=1.0",
"httpx-oauth~=0.17",
"ijson>=3.5.1",
@@ -77,6 +76,7 @@ dependencies = [
"tantivy~=0.26.0",
"tika-client[httpx]~=1.0",
"torch>=2.13,<2.15",
"turbohtml~=1.10.0",
"watchfiles>=1.2",
"whitenoise~=6.11",
"whoosh-compat[tantivy]==0.3",
@@ -84,13 +84,13 @@ dependencies = [
]
[project.optional-dependencies]
mariadb = [
"mysqlclient~=2.2.7",
"mysqlclient>=2.2.7,<2.4",
]
postgres = [
"psycopg[c,pool]==3.3.4",
# Direct dependency for proper resolution of the pre-built wheels
"psycopg-c==3.3.4",
"psycopg-pool==3.3.1",
"psycopg-pool==3.3.2",
]
webserver = [
"granian[uvloop]>=2.7,<2.9",
@@ -112,7 +112,7 @@ lint = [
testing = [
"daphne",
"factory-boy~=3.3.1",
"faker>=40.36,<40.39",
"faker>=40.36,<40.40",
"imagehash",
"pytest~=9.1.1",
"pytest-cov~=7.1.0",
@@ -136,7 +136,6 @@ typing = [
"mypy",
"mypy-baseline",
"pyrefly",
"types-bleach",
"types-channels",
"types-colorama",
"types-dateparser",
@@ -151,6 +150,7 @@ typing = [
[tool.uv]
required-version = ">=0.9.0"
prerelease = "disallow"
environments = [
"sys_platform == 'darwin'",
"sys_platform == 'linux'",
+5
View File
@@ -91,6 +91,11 @@
"glob": "**/*",
"input": "node_modules/pdfjs-dist/iccs/",
"output": "/assets/iccs/"
},
{
"glob": "**/*",
"input": "node_modules/pdfjs-dist/cmaps/",
"output": "/assets/cmaps/"
}
],
"styles": [
+280 -217
View File
File diff suppressed because it is too large Load Diff
+22 -22
View File
@@ -15,16 +15,16 @@
},
"private": true,
"dependencies": {
"@angular/cdk": "^22.1.6",
"@angular/common": "~22.1.6",
"@angular/compiler": "~22.1.6",
"@angular/core": "~22.1.6",
"@angular/forms": "~22.1.6",
"@angular/localize": "~22.1.6",
"@angular/platform-browser": "~22.1.6",
"@angular/router": "~22.1.6",
"@angular/cdk": "^22.2.0",
"@angular/common": "~22.2.0",
"@angular/compiler": "~22.2.0",
"@angular/core": "~22.2.0",
"@angular/forms": "~22.2.0",
"@angular/localize": "~22.2.0",
"@angular/platform-browser": "~22.2.0",
"@angular/router": "~22.2.0",
"@ng-bootstrap/ng-bootstrap": "^21.0.0",
"@ng-select/ng-select": "~24.1.1",
"@ng-select/ng-select": "~24.1.2",
"@ngneat/dirty-check-forms": "^3.0.3",
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.8",
@@ -45,29 +45,29 @@
},
"devDependencies": {
"@angular-builders/jest": "^22.0.1",
"@angular-devkit/core": "^22.1.8",
"@angular-devkit/schematics": "^22.1.8",
"@angular-devkit/core": "^22.2.0",
"@angular-devkit/schematics": "^22.2.0",
"@angular-eslint/builder": "22.5.0",
"@angular-eslint/eslint-plugin": "22.5.0",
"@angular-eslint/eslint-plugin-template": "22.5.0",
"@angular-eslint/schematics": "22.5.0",
"@angular-eslint/template-parser": "22.5.0",
"@angular/build": "22.1.8",
"@angular/cli": "22.1.8",
"@angular/compiler-cli": "~22.1.6",
"@angular/build": "22.2.0",
"@angular/cli": "22.2.0",
"@angular/compiler-cli": "~22.2.0",
"@playwright/test": "^1.62.1",
"@types/jest": "^30.0.0",
"@types/node": "^26.5.0",
"@typescript-eslint/eslint-plugin": "^8.70.0",
"@typescript-eslint/parser": "^8.70.0",
"@typescript-eslint/utils": "^8.70.0",
"eslint": "^10.10.0",
"jest": "30.5.1",
"jest-environment-jsdom": "^30.5.1",
"@types/node": "^26.6.2",
"@typescript-eslint/eslint-plugin": "^8.70.1",
"@typescript-eslint/parser": "^8.70.1",
"@typescript-eslint/utils": "^8.70.1",
"eslint": "^10.11.0",
"jest": "30.5.2",
"jest-environment-jsdom": "^30.5.2",
"jest-junit": "^17.0.0",
"jest-preset-angular": "^17.0.0",
"jest-websocket-mock": "^2.5.0",
"prettier": "^3.9.6",
"prettier": "^3.9.9",
"prettier-plugin-organize-imports": "^4.3.0",
"ts-node": "~10.9.2",
"typescript": "^6.0.3"
+1312 -1960
View File
File diff suppressed because it is too large Load Diff
@@ -253,6 +253,24 @@
</div>
</div>
@if (aiEnabled) {
<div class="row mb-3">
<div class="col-md-3 col-form-label pt-0">
<span i18n>Suggestions default to</span>
</div>
<div class="col">
<fieldset class="btn-group btn-group-sm">
<input type="radio" class="btn-check" id="suggestionSourceBoth" [value]="SuggestionSource.Both" formControlName="documentEditingSuggestionSource">
<label class="btn btn-outline-primary" for="suggestionSourceBoth"><ng-container i18n>Both</ng-container></label>
<input type="radio" class="btn-check" id="suggestionSourceML" [value]="SuggestionSource.ML" formControlName="documentEditingSuggestionSource">
<label class="btn btn-outline-primary" for="suggestionSourceML"><i-bs class="me-1" name="cpu"></i-bs><ng-container i18n>ML only</ng-container></label>
<input type="radio" class="btn-check" id="suggestionSourceAI" [value]="SuggestionSource.AI" formControlName="documentEditingSuggestionSource">
<label class="btn btn-outline-primary" for="suggestionSourceAI"><i-bs class="me-1" name="stars"></i-bs><ng-container i18n>AI only</ng-container></label>
</fieldset>
</div>
</div>
}
<div class="row">
<div class="col">
<pngx-input-check i18n-title title="Automatically request suggestions for inbox documents" i18n-hint hint="If un-checked, suggestions must be requested via the Suggest button." formControlName="documentEditingAutoSuggest"></pngx-input-check>
@@ -307,7 +307,7 @@ describe('SettingsComponent', () => {
expect(toastErrorSpy).toHaveBeenCalled()
expect(storeSpy).toHaveBeenCalled()
expect(appearanceSettingsSpy).not.toHaveBeenCalled()
expect(setSpy).toHaveBeenCalledTimes(34)
expect(setSpy).toHaveBeenCalledTimes(35)
expect(setSpy).toHaveBeenCalledWith(SETTINGS_KEYS.SIDEBAR_HIDDEN_ITEMS, [
HideableSidebarItemID.Workflows,
])
@@ -44,6 +44,7 @@ import {
HIDEABLE_SIDEBAR_ITEM_IDS,
HideableSidebarItemID,
SETTINGS_KEYS,
SuggestionSource,
} from 'src/app/data/ui-settings'
import { User } from 'src/app/data/user'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
@@ -184,6 +185,7 @@ export class SettingsComponent
documentEditingRemoveInboxTags: new FormControl(null),
documentEditingOverlayThumbnail: new FormControl(null),
documentEditingAutoSuggest: new FormControl(null),
documentEditingSuggestionSource: new FormControl(null),
documentDetailsHiddenFields: new FormControl([]),
searchDbOnly: new FormControl(null),
searchLink: new FormControl(null),
@@ -217,6 +219,11 @@ export class SettingsComponent
public readonly PdfZoomScale = PdfZoomScale
public readonly PdfEditorEditMode = PdfEditorEditMode
public readonly SuggestionSource = SuggestionSource
get aiEnabled(): boolean {
return this.settings.get(SETTINGS_KEYS.AI_ENABLED)
}
public readonly documentDetailFieldOptions = documentDetailFieldOptions
public readonly sidebarItemOptions = HIDEABLE_SIDEBAR_ITEM_IDS.map((id) => ({
@@ -404,6 +411,9 @@ export class SettingsComponent
documentEditingAutoSuggest: this.settings.get(
SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST
),
documentEditingSuggestionSource: this.settings.get(
SETTINGS_KEYS.DOCUMENT_EDITING_SUGGESTION_SOURCE
),
documentDetailsHiddenFields: this.settings.get(
SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS
),
@@ -625,6 +635,10 @@ export class SettingsComponent
SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST,
this.settingsForm.value.documentEditingAutoSuggest
)
this.settings.set(
SETTINGS_KEYS.DOCUMENT_EDITING_SUGGESTION_SOURCE,
this.settingsForm.value.documentEditingSuggestionSource
)
this.settings.set(
SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS,
this.settingsForm.value.documentDetailsHiddenFields
@@ -154,11 +154,29 @@
& section {
position: absolute;
text-align: initial;
pointer-events: auto;
box-sizing: border-box;
transform-origin: 0 0;
}
& :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a {
position: absolute;
inset: 0;
font-size: 1em;
transition: none;
}
& :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder)
> a:hover {
opacity: 0.2;
background-color: rgb(255 255 0);
}
& .annotationTextContent {
opacity: 0;
}
}
:host ::ng-deep .textLayer.selecting ~ .annotationLayer section {
pointer-events: none;
}
@@ -1,7 +1,11 @@
import { SimpleChange } from '@angular/core'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import * as pdfjs from 'pdfjs-dist/legacy/build/pdf.mjs'
import { PDFSinglePageViewer, PDFViewer } from 'pdfjs-dist/web/pdf_viewer.mjs'
import {
LinkTarget,
PDFSinglePageViewer,
PDFViewer,
} from 'pdfjs-dist/web/pdf_viewer.mjs'
import { PngxPdfViewerComponent } from './pdf-viewer.component'
import { PdfRenderMode, PdfZoomLevel, PdfZoomScale } from './pdf-viewer.types'
@@ -58,6 +62,16 @@ describe('PngxPdfViewerComponent', () => {
expect((component as any).pdfViewer).toBeInstanceOf(PDFViewer)
})
it('opens external links in a new tab', () => {
const linkService = (component as any).linkService
expect(linkService.options).toEqual(
expect.objectContaining({
externalLinkTarget: LinkTarget.BLANK,
externalLinkRel: 'noopener noreferrer nofollow',
})
)
})
it('resolves the worker source relative to the document base URI', async () => {
setBaseHref('/paperless/')
const getDocumentSpy = jest.spyOn(pdfjs, 'getDocument')
@@ -76,6 +90,7 @@ describe('PngxPdfViewerComponent', () => {
withCredentials: true,
wasmUrl: expect.stringContaining('/paperless/assets/wasm/'),
iccUrl: expect.stringContaining('/paperless/assets/iccs/'),
cMapUrl: expect.stringContaining('/paperless/assets/cmaps/'),
})
})
@@ -21,6 +21,7 @@ import {
} from 'pdfjs-dist/legacy/build/pdf.mjs'
import {
EventBus,
LinkTarget,
PDFFindController,
PDFLinkService,
PDFSinglePageViewer,
@@ -75,7 +76,11 @@ export class PngxPdfViewerComponent
private lastViewerPage?: number
private readonly eventBus = new EventBus()
private readonly linkService = new PDFLinkService({ eventBus: this.eventBus })
private readonly linkService = new PDFLinkService({
eventBus: this.eventBus,
externalLinkTarget: LinkTarget.BLANK,
externalLinkRel: 'noopener noreferrer nofollow',
})
private readonly findController = new PDFFindController({
eventBus: this.eventBus,
linkService: this.linkService,
@@ -185,6 +190,7 @@ export class PngxPdfViewerComponent
withCredentials: true,
wasmUrl: new URL('assets/wasm/', this.document.baseURI).toString(),
iccUrl: new URL('assets/iccs/', this.document.baseURI).toString(),
cMapUrl: new URL('assets/cmaps/', this.document.baseURI).toString(),
}
this.loadingTask = getDocument(initOptions)
try {
@@ -1,58 +1,84 @@
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-primary" (click)="clickSuggest()" [disabled]="disabled() || loading() || (suggestions() && !aiEnabled())" [aria-label]="noSuggestions ? 'No suggestions' : 'Suggest'" i18n-aria-label>
@if (loading()) {
<div class="spinner-border spinner-border-sm" role="status"></div>
} @else if (noSuggestions) {
<i-bs width="1.2em" height="1.2em" name="check-circle"></i-bs>
} @else {
<i-bs width="1.2em" height="1.2em" name="stars"></i-bs>
<div class="d-flex align-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-primary" (click)="clickSuggest()" [disabled]="disabled() || loading() || (suggestions() && !aiEnabled())" [aria-label]="noSuggestions ? 'No suggestions' : 'Suggest'" i18n-aria-label>
@if (loading()) {
<div class="spinner-border spinner-border-sm" role="status"></div>
} @else if (noSuggestions) {
<i-bs width="1.2em" height="1.2em" name="check-circle"></i-bs>
} @else {
<i-bs width="1.2em" height="1.2em" name="lightbulb"></i-bs>
}
@if (noSuggestions) {
<span class="d-none d-lg-inline ps-1" i18n>No suggestions</span>
} @else {
<span class="d-none d-lg-inline ps-1" i18n>Suggest</span>
}
@if (totalSuggestions > 0) {
<span class="badge bg-primary ms-2">{{ totalSuggestions }}</span>
}
</button>
@if (aiEnabled()) {
<div class="btn-group" ngbDropdown #dropdown="ngbDropdown" [popperOptions]="popperOptions">
<button type="button" class="btn btn-sm btn-outline-primary" ngbDropdownToggle [disabled]="disabled() || loading() || !suggestions()" aria-expanded="false" aria-controls="suggestionsDropdown" aria-label="Suggestions dropdown">
<span class="visually-hidden" i18n>Show suggestions</span>
</button>
<div ngbDropdownMenu aria-labelledby="suggestionsDropdown" class="shadow suggestions-dropdown">
<div class="list-group list-group-flush small pb-0">
@if (novelSuggestions === 0 && reusableSuggestions === 0) {
<div class="list-group-item text-muted fst-italic">
<small class="text-muted small fst-italic" i18n>No novel suggestions</small>
</div>
}
@if (suggestions()?.suggested_tags?.length > 0) {
<small class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="tags"></i-bs><ng-container i18n>Tags</ng-container></small>
@for (tag of suggestions().suggested_tags; track tag) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addTag.emit(tag)">{{ tag }}</button>
}
}
@if (suggestions()?.suggested_document_types?.length > 0) {
<div class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="hash"></i-bs><ng-container i18n>Document Types</ng-container></div>
@for (type of suggestions().suggested_document_types; track type) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addDocumentType.emit(type)">{{ type }}</button>
}
}
@if (suggestions()?.suggested_correspondents?.length > 0) {
<div class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="person"></i-bs><ng-container i18n>Correspondents</ng-container></div>
@for (correspondent of suggestions().suggested_correspondents; track correspondent) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addCorrespondent.emit(correspondent)">{{ correspondent }}</button>
}
}
@if (reusableSuggestions > 0) {
<div class="list-group-item text-muted fst-italic">
<small class="text-muted small fst-italic" i18n>{reusableSuggestions, plural, =1 {1 existing value suggested below} other {{{reusableSuggestions}} existing values suggested below}}</small>
</div>
}
</div>
</div>
</div>
}
@if (noSuggestions) {
<span class="d-none d-lg-inline ps-1" i18n>No suggestions</span>
} @else {
<span class="d-none d-lg-inline ps-1" i18n>Suggest</span>
}
@if (totalSuggestions > 0) {
<span class="badge bg-primary ms-2">{{ totalSuggestions }}</span>
}
</button>
</div>
@if (aiEnabled()) {
<div class="btn-group" ngbDropdown #dropdown="ngbDropdown" [popperOptions]="popperOptions">
<button type="button" class="btn btn-sm btn-outline-primary" ngbDropdownToggle [disabled]="disabled() || loading() || !suggestions()" aria-expanded="false" aria-controls="suggestionsDropdown" aria-label="Suggestions dropdown">
<span class="visually-hidden" i18n>Show suggestions</span>
<div ngbDropdown autoClose="outside" placement="bottom-end" [popperOptions]="popperOptions">
<button type="button" class="btn btn-sm btn-link position-relative" ngbDropdownToggle [disabled]="disabled() || loading()" i18n-title title="Suggestion options">
<i-bs name="three-dots"></i-bs>
@if (source() !== defaultSource()) {
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-primary border border-light rounded-circle">
<span class="visually-hidden" i18n>Not using default</span>
</span>
}
</button>
<div ngbDropdownMenu aria-labelledby="suggestionsDropdown" class="shadow suggestions-dropdown">
<div class="list-group list-group-flush small pb-0">
@if (novelSuggestions === 0 && reusableSuggestions === 0) {
<div class="list-group-item text-muted fst-italic">
<small class="text-muted small fst-italic" i18n>No novel suggestions</small>
</div>
}
@if (suggestions()?.suggested_tags?.length > 0) {
<small class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="tags"></i-bs><ng-container i18n>Tags</ng-container></small>
@for (tag of suggestions().suggested_tags; track tag) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addTag.emit(tag)">{{ tag }}</button>
}
}
@if (suggestions()?.suggested_document_types?.length > 0) {
<div class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="hash"></i-bs><ng-container i18n>Document Types</ng-container></div>
@for (type of suggestions().suggested_document_types; track type) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addDocumentType.emit(type)">{{ type }}</button>
}
}
@if (suggestions()?.suggested_correspondents?.length > 0) {
<div class="list-group-item text-uppercase text-muted small"><i-bs class="me-2" name="person"></i-bs><ng-container i18n>Correspondents</ng-container></div>
@for (correspondent of suggestions().suggested_correspondents; track correspondent) {
<button type="button" class="list-group-item list-group-item-action bg-light" (click)="addCorrespondent.emit(correspondent)">{{ correspondent }}</button>
}
}
@if (reusableSuggestions > 0) {
<div class="list-group-item text-muted fst-italic">
<small class="text-muted small fst-italic" i18n>{reusableSuggestions, plural, =1 {1 existing value suggested below} other {{{reusableSuggestions}} existing values suggested below}}</small>
</div>
}
<div ngbDropdownMenu class="shadow p-3">
<div class="small text-muted mb-2" i18n>Suggest using:</div>
<div class="form-check small">
<input class="form-check-input" type="checkbox" id="suggestionSourceML" [checked]="useML" [disabled]="useML && !useAI" (change)="setSources($event.target.checked, useAI)">
<label class="form-check-label d-inline-flex align-items-center gap-1" for="suggestionSourceML"><i-bs name="cpu"></i-bs><ng-container i18n>ML</ng-container></label>
</div>
<div class="form-check small">
<input class="form-check-input" type="checkbox" id="suggestionSourceAI" [checked]="useAI" [disabled]="useAI && !useML" (change)="setSources(useML, $event.target.checked)">
<label class="form-check-label d-inline-flex align-items-center gap-1" for="suggestionSourceAI"><i-bs name="stars"></i-bs><ng-container i18n>AI</ng-container></label>
</div>
</div>
</div>
@@ -1,3 +1,7 @@
.suggestions-dropdown {
min-width: 250px;
}
.btn-link.dropdown-toggle::after {
display: none;
}
@@ -1,6 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { SuggestionSource } from 'src/app/data/ui-settings'
import { SuggestionsDropdownComponent } from './suggestions-dropdown.component'
describe('SuggestionsDropdownComponent', () => {
@@ -179,14 +180,71 @@ describe('SuggestionsDropdownComponent', () => {
it('should toggle dropdown when clickSuggest is called and suggestions are not null', () => {
fixture.componentRef.setInput('aiEnabled', true)
fixture.componentRef.setInput('fetchedSources', [SuggestionSource.ML])
fixture.detectChanges()
fixture.componentRef.setInput('suggestions', {
suggested_correspondents: [],
suggested_tags: [],
suggested_document_types: [],
})
fixture.detectChanges()
component.clickSuggest()
expect(component.dropdown.open).toBeTruthy()
expect(component.dropdown.isOpen()).toBeTruthy()
expect(fixture.nativeElement.textContent).toContain('No novel suggestions')
})
it('should fetch unfetched sources and show existing suggestions', () => {
jest.spyOn(component.getSuggestions, 'emit')
fixture.componentRef.setInput('aiEnabled', true)
fixture.componentRef.setInput('source', SuggestionSource.Both)
fixture.componentRef.setInput('fetchedSources', [SuggestionSource.ML])
fixture.componentRef.setInput('suggestions', { tags: [1] })
fixture.detectChanges()
component.clickSuggest()
expect(component.getSuggestions.emit).toHaveBeenCalledWith(
SuggestionSource.Both
)
expect(component.dropdown.isOpen()).toBeTruthy()
})
it('should only show source options when AI is enabled', () => {
expect(
fixture.nativeElement.querySelector('#suggestionSourceML')
).toBeNull()
fixture.componentRef.setInput('aiEnabled', true)
fixture.detectChanges()
fixture.nativeElement
.querySelector('button[title="Suggestion options"]')
.click()
fixture.detectChanges()
expect(
fixture.nativeElement.querySelector('#suggestionSourceML')
).not.toBeNull()
})
it('should emit source changes and never allow no source', () => {
const emitSpy = jest.spyOn(component.sourceChange, 'emit')
component.setSources(true, true)
expect(emitSpy).toHaveBeenCalledWith(SuggestionSource.Both)
component.setSources(true, false)
expect(emitSpy).toHaveBeenCalledWith(SuggestionSource.ML)
component.setSources(false, true)
expect(emitSpy).toHaveBeenCalledWith(SuggestionSource.AI)
emitSpy.mockClear()
component.setSources(false, false)
expect(emitSpy).not.toHaveBeenCalled()
})
it('should indicate a non-default source', () => {
fixture.componentRef.setInput('aiEnabled', true)
fixture.componentRef.setInput('source', SuggestionSource.AI)
fixture.componentRef.setInput('defaultSource', SuggestionSource.AI)
fixture.detectChanges()
expect(fixture.nativeElement.textContent).not.toContain('Not using default')
fixture.componentRef.setInput('source', SuggestionSource.Both)
fixture.detectChanges()
expect(fixture.nativeElement.textContent).toContain('Not using default')
})
})
@@ -8,6 +8,7 @@ import {
import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { DocumentSuggestions } from 'src/app/data/document-suggestions'
import { SuggestionSource } from 'src/app/data/ui-settings'
import { pngxPopperOptions } from 'src/app/utils/popper-options'
@Component({
@@ -18,12 +19,16 @@ import { pngxPopperOptions } from 'src/app/utils/popper-options'
})
export class SuggestionsDropdownComponent {
public popperOptions = pngxPopperOptions
public readonly SuggestionSource = SuggestionSource
@ViewChild('dropdown') dropdown: NgbDropdown
readonly suggestions = input<DocumentSuggestions>(null)
readonly aiEnabled = input(false)
readonly loading = input(false)
readonly disabled = input(false)
readonly source = input<SuggestionSource>(SuggestionSource.ML)
readonly defaultSource = input<SuggestionSource>(SuggestionSource.ML)
readonly fetchedSources = input<SuggestionSource[]>([])
readonly appliedTags = input<number[]>([])
readonly appliedCorrespondent = input<number>(null)
@@ -31,8 +36,10 @@ export class SuggestionsDropdownComponent {
readonly appliedStoragePath = input<number>(null)
@Output()
getSuggestions: EventEmitter<SuggestionsDropdownComponent> =
new EventEmitter()
getSuggestions: EventEmitter<SuggestionSource> = new EventEmitter()
@Output()
sourceChange: EventEmitter<SuggestionSource> = new EventEmitter()
@Output()
addTag: EventEmitter<string> = new EventEmitter()
@@ -53,12 +60,42 @@ export class SuggestionsDropdownComponent {
}
if (!this.suggestions()) {
this.getSuggestions.emit(this)
this.getSuggestions.emit(this.source())
} else if (this.hasUnfetchedSources) {
// sources changed, fetch the rest and show what we have meanwhile
this.getSuggestions.emit(this.source())
this.dropdown?.open()
} else {
this.dropdown?.toggle()
}
}
get useML(): boolean {
return this.source() !== SuggestionSource.AI
}
get useAI(): boolean {
return this.source() !== SuggestionSource.ML
}
get hasUnfetchedSources(): boolean {
const fetched = this.fetchedSources()
return (
(this.useML && !fetched.includes(SuggestionSource.ML)) ||
(this.useAI && !fetched.includes(SuggestionSource.AI))
)
}
public setSources(ml: boolean, ai: boolean) {
if (ml && ai) {
this.sourceChange.emit(SuggestionSource.Both)
} else if (ml) {
this.sourceChange.emit(SuggestionSource.ML)
} else if (ai) {
this.sourceChange.emit(SuggestionSource.AI)
}
}
get novelSuggestions(): number {
return (
(this.suggestions()?.suggested_correspondents?.length ?? 0) +
@@ -134,11 +134,15 @@
[loading]="suggestionsLoading()"
[suggestions]="suggestions()"
[aiEnabled]="aiEnabled"
[source]="suggestionSource"
[defaultSource]="defaultSuggestionSource"
[fetchedSources]="fetchedSuggestionSources()"
[appliedTags]="documentForm.value.tags"
[appliedCorrespondent]="documentForm.value.correspondent"
[appliedDocumentType]="documentForm.value.document_type"
[appliedStoragePath]="documentForm.value.storage_path"
(getSuggestions)="getSuggestions()"
(getSuggestions)="getSuggestions($event)"
(sourceChange)="suggestionSourceOverride.set($event)"
(addTag)="createTag($event)"
(addDocumentType)="createDocumentType($event)"
(addCorrespondent)="createCorrespondent($event)">
@@ -43,7 +43,7 @@ import {
} from 'src/app/data/filter-rule-type'
import { StoragePath } from 'src/app/data/storage-path'
import { Tag } from 'src/app/data/tag'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { SETTINGS_KEYS, SuggestionSource } from 'src/app/data/ui-settings'
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
@@ -1528,6 +1528,113 @@ describe('DocumentDetailComponent', () => {
expect(component.suggestionsLoading()).toBeFalsy()
})
it('should get and merge ML and AI suggestions when source is both', () => {
settingsService.set(
SETTINGS_KEYS.DOCUMENT_EDITING_SUGGESTION_SOURCE,
SuggestionSource.Both
)
const getSetting = settingsService.get.bind(settingsService)
jest
.spyOn(settingsService, 'get')
.mockImplementation((key) =>
key === SETTINGS_KEYS.AI_ENABLED ? true : getSetting(key)
)
const suggestionsSpy = jest
.spyOn(documentService, 'getSuggestions')
.mockReturnValue(of({ tags: [42], dates: ['2024-01-01'] }))
const aiSuggestionsSpy = jest
.spyOn(documentService, 'getAiSuggestions')
.mockReturnValue(
of({ title: 'AI title', tags: [42, 43], suggested_tags: ['New'] })
)
initNormally()
expect(suggestionsSpy).toHaveBeenCalled()
expect(aiSuggestionsSpy).toHaveBeenCalled()
expect(component.suggestions().title).toEqual('AI title')
expect(component.suggestions().tags).toEqual([42, 43])
expect(component.suggestions().suggested_tags).toEqual(['New'])
expect(component.suggestions().dates).toEqual(['2024-01-01'])
})
it('should only fetch sources not yet fetched for the document', () => {
settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST, false)
settingsService.set(
SETTINGS_KEYS.DOCUMENT_EDITING_SUGGESTION_SOURCE,
SuggestionSource.ML
)
const getSetting = settingsService.get.bind(settingsService)
jest
.spyOn(settingsService, 'get')
.mockImplementation((key) =>
key === SETTINGS_KEYS.AI_ENABLED ? true : getSetting(key)
)
const suggestionsSpy = jest
.spyOn(documentService, 'getSuggestions')
.mockReturnValue(of({ tags: [42] }))
const aiSuggestionsSpy = jest
.spyOn(documentService, 'getAiSuggestions')
.mockReturnValue(of({ tags: [43] }))
initNormally()
component.getSuggestions()
expect(suggestionsSpy).toHaveBeenCalledTimes(1)
expect(aiSuggestionsSpy).not.toHaveBeenCalled()
component.getSuggestions(SuggestionSource.Both)
expect(suggestionsSpy).toHaveBeenCalledTimes(1)
expect(aiSuggestionsSpy).toHaveBeenCalledTimes(1)
expect(component.suggestions().tags).toEqual([42, 43])
component.getSuggestions(SuggestionSource.Both)
expect(suggestionsSpy).toHaveBeenCalledTimes(1)
expect(aiSuggestionsSpy).toHaveBeenCalledTimes(1)
})
it('should use the per-document source override and reset it on document change', () => {
settingsService.set(SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST, false)
const getSetting = settingsService.get.bind(settingsService)
jest
.spyOn(settingsService, 'get')
.mockImplementation((key) =>
key === SETTINGS_KEYS.AI_ENABLED ? true : getSetting(key)
)
initNormally()
expect(component.suggestionSource).toEqual(SuggestionSource.AI)
component.suggestionSourceOverride.set(SuggestionSource.ML)
expect(component.suggestionSource).toEqual(SuggestionSource.ML)
jest
.spyOn(documentService, 'get')
.mockReturnValueOnce(of(Object.assign({}, doc)))
;(component as any).loadDocument(doc.id, true)
expect(component.suggestionSourceOverride()).toBeNull()
expect(component.fetchedSuggestionSources()).toEqual([])
})
it('should keep suggestions from one source if the other fails', () => {
settingsService.set(
SETTINGS_KEYS.DOCUMENT_EDITING_SUGGESTION_SOURCE,
SuggestionSource.Both
)
const getSetting = settingsService.get.bind(settingsService)
jest
.spyOn(settingsService, 'get')
.mockImplementation((key) =>
key === SETTINGS_KEYS.AI_ENABLED ? true : getSetting(key)
)
const errorSpy = jest.spyOn(toastService, 'showError')
jest
.spyOn(documentService, 'getSuggestions')
.mockReturnValue(of({ tags: [42] }))
jest
.spyOn(documentService, 'getAiSuggestions')
.mockReturnValue(throwError(() => new Error('failed')))
initNormally()
expect(errorSpy).toHaveBeenCalled()
expect(component.suggestions().tags).toEqual([42])
expect(component.fetchedSuggestionSources()).toEqual([SuggestionSource.ML])
})
it('should show error if needed for get suggestions', () => {
const suggestionsSpy = jest.spyOn(documentService, 'getSuggestions')
const errorSpy = jest.spyOn(toastService, 'showError')
@@ -28,7 +28,7 @@ import {
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { DeviceDetectorService } from 'ngx-device-detector'
import { BehaviorSubject, Observable, of, Subject, timer } from 'rxjs'
import { BehaviorSubject, merge, Observable, of, Subject, timer } from 'rxjs'
import {
catchError,
debounceTime,
@@ -48,7 +48,10 @@ import { DataType } from 'src/app/data/datatype'
import { Document, DocumentVersionInfo } from 'src/app/data/document'
import { DocumentMetadata } from 'src/app/data/document-metadata'
import { DocumentNote } from 'src/app/data/document-note'
import { DocumentSuggestions } from 'src/app/data/document-suggestions'
import {
DocumentSuggestions,
mergeSuggestions,
} from 'src/app/data/document-suggestions'
import { DocumentType } from 'src/app/data/document-type'
import { FilterRule } from 'src/app/data/filter-rule'
import {
@@ -63,7 +66,7 @@ import {
import { ObjectWithId } from 'src/app/data/object-with-id'
import { StoragePath } from 'src/app/data/storage-path'
import { Tag } from 'src/app/data/tag'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { SETTINGS_KEYS, SuggestionSource } from 'src/app/data/ui-settings'
import { User } from 'src/app/data/user'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe'
@@ -240,6 +243,10 @@ export class DocumentDetailComponent
private readonly autoSuggestSetting = this.settings.getSignal<boolean>(
SETTINGS_KEYS.DOCUMENT_EDITING_AUTO_SUGGEST
)
private readonly suggestionSourceSetting =
this.settings.getSignal<SuggestionSource>(
SETTINGS_KEYS.DOCUMENT_EDITING_SUGGESTION_SOURCE
)
private readonly hiddenFieldsSetting = this.settings.getSignal<
DocumentDetailFieldID[]
>(SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS)
@@ -261,6 +268,9 @@ export class DocumentDetailComponent
readonly metadata = signal<DocumentMetadata>(undefined)
readonly suggestions = signal<DocumentSuggestions>(undefined)
readonly suggestionsLoading = signal(false)
// per-document, resets on navigation
readonly suggestionSourceOverride = signal<SuggestionSource>(null)
readonly fetchedSuggestionSources = signal<SuggestionSource[]>([])
readonly users = signal<User[]>(undefined)
readonly title = signal<string>(undefined)
@@ -365,6 +375,15 @@ export class DocumentDetailComponent
return this.autoSuggestSetting()
}
get defaultSuggestionSource(): SuggestionSource {
return this.aiEnabled ? this.suggestionSourceSetting() : SuggestionSource.ML
}
get suggestionSource(): SuggestionSource {
if (!this.aiEnabled) return SuggestionSource.ML
return this.suggestionSourceOverride() ?? this.defaultSuggestionSource
}
get archiveContentRenderType(): ContentRenderType {
const hasArchiveVersion =
this.metadata()?.has_archive_version ??
@@ -590,6 +609,8 @@ export class DocumentDetailComponent
}
this.documentId.set(doc.id)
this.suggestions.set(null)
this.suggestionSourceOverride.set(null)
this.fetchedSuggestionSources.set([])
const openDocument = this.openDocumentService.getOpenDocument(
this.documentId()
)
@@ -1077,29 +1098,44 @@ export class DocumentDetailComponent
return this.documentForm.get('custom_fields') as FormArray
}
getSuggestions() {
getSuggestions(source: SuggestionSource = this.suggestionSource) {
const sources = (
source === SuggestionSource.Both
? [SuggestionSource.ML, SuggestionSource.AI]
: [source]
).filter((s) => !this.fetchedSuggestionSources().includes(s))
if (!sources.length) return
this.suggestionsLoading.set(true)
const suggestionsObservable = this.aiEnabled
? this.documentsService.getAiSuggestions(this.documentId())
: this.documentsService.getSuggestions(this.documentId())
suggestionsObservable
merge(
...sources.map((s) =>
(s === SuggestionSource.AI
? this.documentsService.getAiSuggestions(this.documentId())
: this.documentsService.getSuggestions(this.documentId())
).pipe(
first(),
map((result) => ({ source: s, result })),
catchError((error) => {
this.toastService.showError(
$localize`Error retrieving suggestions.`,
error
)
return of(null)
})
)
)
)
.pipe(
first(),
takeUntil(this.unsubscribeNotifier),
takeUntil(this.docChangeNotifier),
finalize(() => this.suggestionsLoading.set(false))
)
.subscribe({
next: (result) => {
this.suggestions.set(result)
},
error: (error) => {
this.suggestions.set(null)
this.toastService.showError(
$localize`Error retrieving suggestions.`,
error
)
},
.subscribe((response) => {
if (!response) return
this.fetchedSuggestionSources.update((f) => [...f, response.source])
this.suggestions.set(
mergeSuggestions(this.suggestions(), response.result)
)
})
}
@@ -15,3 +15,33 @@ export interface DocumentSuggestions {
dates?: string[] // ISO-formatted date string e.g. 2022-11-03
}
const union = <T>(a: T[] = [], b: T[] = []): T[] => [...new Set([...a, ...b])]
export function mergeSuggestions(
a: DocumentSuggestions,
b: DocumentSuggestions
): DocumentSuggestions {
if (!a) return b
return {
title: a.title || b.title,
tags: union(a.tags, b.tags),
suggested_tags: union(a.suggested_tags, b.suggested_tags),
correspondents: union(a.correspondents, b.correspondents),
suggested_correspondents: union(
a.suggested_correspondents,
b.suggested_correspondents
),
document_types: union(a.document_types, b.document_types),
suggested_document_types: union(
a.suggested_document_types,
b.suggested_document_types
),
storage_paths: union(a.storage_paths, b.storage_paths),
suggested_storage_paths: union(
a.suggested_storage_paths,
b.suggested_storage_paths
),
dates: union(a.dates, b.dates),
}
}
+13
View File
@@ -20,6 +20,12 @@ export enum GlobalSearchType {
TITLE_CONTENT = 'title-content',
}
export enum SuggestionSource {
ML = 'ml',
AI = 'ai',
Both = 'both',
}
export enum CollapsibleSection {
ATTRIBUTES = 'attributes',
}
@@ -98,6 +104,8 @@ export const SETTINGS_KEYS = {
'general-settings:document-editing:overlay-thumbnail',
DOCUMENT_EDITING_AUTO_SUGGEST:
'general-settings:document-editing:auto-suggest',
DOCUMENT_EDITING_SUGGESTION_SOURCE:
'general-settings:document-editing:suggestion-source',
DOCUMENT_DETAILS_HIDDEN_FIELDS:
'general-settings:document-details:hidden-fields',
SEARCH_DB_ONLY: 'general-settings:search:db-only',
@@ -326,6 +334,11 @@ export const SETTINGS: UiSetting[] = [
type: 'boolean',
default: true,
},
{
key: SETTINGS_KEYS.DOCUMENT_EDITING_SUGGESTION_SOURCE,
type: 'string',
default: SuggestionSource.AI,
},
{
key: SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS,
type: 'array',
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4
View File
@@ -74,6 +74,7 @@ import {
clipboardCheckFill,
clipboardFill,
clockHistory,
cpu,
creditCard,
dash,
dashCircle,
@@ -118,6 +119,7 @@ import {
infoCircle,
journalBookmarkFill,
journals,
lightbulb,
link,
list,
listNested,
@@ -322,6 +324,7 @@ const icons = {
clipboardCheckFill,
clipboardFill,
clockHistory,
cpu,
cash,
creditCard,
dash,
@@ -367,6 +370,7 @@ const icons = {
infoCircle,
journalBookmarkFill,
journals,
lightbulb,
link,
list,
listNested,
+1
View File
@@ -292,6 +292,7 @@ a.btn-link:active,
a.btn-link:focus-visible,
.btn-link:hover,
.btn-link:active,
.btn-link.show,
.btn-link:focus-visible {
color: var(--pngx-primary-lighten-10) !important;
.primary-light & {
@@ -25,10 +25,20 @@ export class PDFFindController {
onIsPageVisible?: () => boolean
}
export const LinkTarget = {
NONE: 0,
SELF: 1,
BLANK: 2,
PARENT: 3,
TOP: 4,
}
export class PDFLinkService {
private document?: unknown
private viewer?: unknown
constructor(readonly options: Record<string, unknown> = {}) {}
setDocument(document: unknown): void {
this.document = document
}
+10
View File
@@ -1079,6 +1079,16 @@ class DocumentSerializer(
)
def get_page_count(self, obj) -> int | None:
# Like content versions get their own page count from the newest version,
# use the prefetched versions cache to avoid an extra query
prefetched_cache = getattr(obj, "_prefetched_objects_cache", None)
prefetched_versions = (
prefetched_cache.get("versions")
if isinstance(prefetched_cache, dict)
else None
)
if obj.root_document_id is None and prefetched_versions:
return sort_versions_newest_first(prefetched_versions)[0].page_count
return obj.page_count
@extend_schema_field(DuplicateDocumentSummarySerializer(many=True))
+1 -1
View File
@@ -12,7 +12,7 @@
<meta name="robots" content="noindex,nofollow">
<meta name="author" content="The Paperless-ngx Team">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="manifest" href="{% static webmanifest %}">
<link rel="manifest" href="{% static webmanifest %}" crossorigin="use-credentials">
<link rel="stylesheet" href="{% static styles_css %}">
<link rel="apple-touch-icon" href="{% static apple_touch_icon %}">
</head>
@@ -19,6 +19,7 @@ from documents.models import Document
from documents.versioning import annotate_effective_content
from documents.views import DocumentSelectionMixin
from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import UserFactory
from paperless_testing.http import read_streaming_response
from paperless_testing.permissions import grant_global
@@ -821,6 +822,26 @@ class TestDocumentVersioningApi(DirectoriesMixin, APITestCase):
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(resp.data["content"], "v1-content")
def test_page_count_resolves_to_latest_version(self) -> None:
root = DocumentFactory(page_count=2)
DocumentFactory(root_document=root, version_index=1, page_count=1)
unversioned = DocumentFactory(page_count=5)
resp = self.client.get("/api/documents/?fields=id,page_count")
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(
{doc["id"]: doc["page_count"] for doc in resp.data["results"]},
{root.id: 1, unversioned.id: 5},
)
resp = self.client.get(f"/api/documents/{root.id}/")
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(resp.data["page_count"], 1)
resp = self.client.get(f"/api/documents/{root.id}/?version={root.id}")
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(resp.data["page_count"], 2)
def _make_root_with_out_of_order_versions(self) -> tuple[Document, ...]:
"""
A root whose newest version has a *lower* id than an older one, which is
+6 -2
View File
@@ -1192,6 +1192,7 @@ class DocumentViewSet(
"version_label",
"root_document_id",
"version_index",
"page_count",
),
),
"tags",
@@ -1271,13 +1272,16 @@ class DocumentViewSet(
if (
"version" not in request.query_params
or not isinstance(response.data, dict)
or "content" not in response.data
or not ({"content", "page_count"} & response.data.keys())
):
return response
root_doc = self.get_object()
content_doc = self._resolve_file_doc(root_doc, request)
response.data["content"] = content_doc.content or ""
if "content" in response.data:
response.data["content"] = content_doc.content or ""
if "page_count" in response.data:
response.data["page_count"] = content_doc.page_count
return response
def update(self, request, *args, **kwargs):
+71 -67
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-18 01:29+0000\n"
"PO-Revision-Date: 2026-09-18 01:30\n"
"POT-Creation-Date: 2026-10-02 23:09+0000\n"
"PO-Revision-Date: 2026-10-02 23:10\n"
"Last-Translator: \n"
"Language-Team: Afrikaans\n"
"Language: af_ZA\n"
@@ -1615,7 +1615,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:514 documents/serialisers.py:871
#: documents/serialisers.py:2883 documents/views.py:343 documents/views.py:2726
#: documents/serialisers.py:2895 documents/views.py:342 documents/views.py:2729
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1624,38 +1624,38 @@ msgstr ""
msgid "Invalid color."
msgstr "Ongeldige kleur."
#: documents/serialisers.py:2350
#: documents/serialisers.py:2362
#, python-format
msgid "File type %(type)s not supported"
msgstr "Lêertipe %(type)s word nie ondersteun nie"
#: documents/serialisers.py:2394
#: documents/serialisers.py:2406
#, python-format
msgid "Custom field id must be an integer: %(id)s"
msgstr ""
#: documents/serialisers.py:2401
#: documents/serialisers.py:2413
#, python-format
msgid "Custom field with id %(id)s does not exist"
msgstr ""
#: documents/serialisers.py:2418 documents/serialisers.py:2428
#: documents/serialisers.py:2430 documents/serialisers.py:2440
msgid "Custom fields must be a list of integers or an object mapping ids to values."
msgstr ""
#: documents/serialisers.py:2423
#: documents/serialisers.py:2435
msgid "Some custom fields don't exist or were specified twice."
msgstr ""
#: documents/serialisers.py:2570
#: documents/serialisers.py:2582
msgid "Invalid variable detected."
msgstr "Ongeldige veranderlike bespeur."
#: documents/serialisers.py:2939
#: documents/serialisers.py:2951
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2969 documents/views.py:4763
#: documents/serialisers.py:2981 documents/views.py:4784
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1910,40 +1910,40 @@ msgstr ""
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2723
#: documents/views.py:335 documents/views.py:2726
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1670
#: documents/views.py:1673
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1681
#: documents/views.py:1684
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1693
#: documents/views.py:1696
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2548 documents/views.py:2864
#: documents/views.py:2551 documents/views.py:2867
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4776
#: documents/views.py:4797
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4822
#: documents/views.py:4843
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4886
#: documents/views.py:4907
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4900
#: documents/views.py:4921
msgid "The share link bundle is unavailable."
msgstr ""
@@ -2188,194 +2188,198 @@ msgid "Sets the LLM embedding model"
msgstr ""
#: paperless/models.py:369
msgid "Sets the LLM embedding endpoint, optional"
msgid "Sets the LLM embedding API key"
msgstr ""
#: paperless/models.py:376
msgid "Sets the LLM embedding endpoint, optional"
msgstr ""
#: paperless/models.py:383
msgid "Sets the LLM embedding chunk size"
msgstr ""
#: paperless/models.py:382
#: paperless/models.py:389
msgid "Sets the LLM context size"
msgstr ""
#: paperless/models.py:388
#: paperless/models.py:395
msgid "Sets the LLM backend"
msgstr ""
#: paperless/models.py:396
#: paperless/models.py:403
msgid "Sets the LLM model"
msgstr ""
#: paperless/models.py:403
#: paperless/models.py:410
msgid "Sets the LLM API key"
msgstr ""
#: paperless/models.py:410
#: paperless/models.py:417
msgid "Sets the LLM endpoint, optional"
msgstr ""
#: paperless/models.py:417
#: paperless/models.py:424
msgid "Sets the LLM output language"
msgstr ""
#: paperless/models.py:424
#: paperless/models.py:431
msgid "Sets the LLM timeout in seconds"
msgstr ""
#: paperless/models.py:430
#: paperless/models.py:437
msgid "paperless application settings"
msgstr ""
#: paperless/settings/__init__.py:558
#: paperless/settings/__init__.py:560
msgid "English (US)"
msgstr "Engels (VS)"
#: paperless/settings/__init__.py:559
#: paperless/settings/__init__.py:561
msgid "Arabic"
msgstr "Arabies"
#: paperless/settings/__init__.py:560
#: paperless/settings/__init__.py:562
msgid "Afrikaans"
msgstr ""
#: paperless/settings/__init__.py:561
#: paperless/settings/__init__.py:563
msgid "Belarusian"
msgstr "Belorussies"
#: paperless/settings/__init__.py:562
#: paperless/settings/__init__.py:564
msgid "Bulgarian"
msgstr ""
#: paperless/settings/__init__.py:563
#: paperless/settings/__init__.py:565
msgid "Catalan"
msgstr "Katalaans"
#: paperless/settings/__init__.py:564
#: paperless/settings/__init__.py:566
msgid "Czech"
msgstr "Tsjeggies"
#: paperless/settings/__init__.py:565
#: paperless/settings/__init__.py:567
msgid "Danish"
msgstr "Deens"
#: paperless/settings/__init__.py:566
#: paperless/settings/__init__.py:568
msgid "German"
msgstr "Duits"
#: paperless/settings/__init__.py:567
#: paperless/settings/__init__.py:569
msgid "Greek"
msgstr ""
#: paperless/settings/__init__.py:568
#: paperless/settings/__init__.py:570
msgid "English (GB)"
msgstr "Engels (GB)"
#: paperless/settings/__init__.py:569
#: paperless/settings/__init__.py:571
msgid "Spanish"
msgstr "Spaans"
#: paperless/settings/__init__.py:570
#: paperless/settings/__init__.py:572
msgid "Persian"
msgstr ""
#: paperless/settings/__init__.py:571
#: paperless/settings/__init__.py:573
msgid "Finnish"
msgstr "Fins"
#: paperless/settings/__init__.py:572
#: paperless/settings/__init__.py:574
msgid "French"
msgstr "Frans"
#: paperless/settings/__init__.py:573
#: paperless/settings/__init__.py:575
msgid "Hungarian"
msgstr ""
#: paperless/settings/__init__.py:574
#: paperless/settings/__init__.py:576
msgid "Indonesian"
msgstr ""
#: paperless/settings/__init__.py:575
#: paperless/settings/__init__.py:577
msgid "Italian"
msgstr "Italiaans"
#: paperless/settings/__init__.py:576
#: paperless/settings/__init__.py:578
msgid "Japanese"
msgstr ""
#: paperless/settings/__init__.py:577
#: paperless/settings/__init__.py:579
msgid "Korean"
msgstr ""
#: paperless/settings/__init__.py:578
#: paperless/settings/__init__.py:580
msgid "Luxembourgish"
msgstr "Luxemburgs"
#: paperless/settings/__init__.py:579
#: paperless/settings/__init__.py:581
msgid "Norwegian"
msgstr ""
#: paperless/settings/__init__.py:580
#: paperless/settings/__init__.py:582
msgid "Dutch"
msgstr "Nederlands"
#: paperless/settings/__init__.py:581
#: paperless/settings/__init__.py:583
msgid "Polish"
msgstr "Pools"
#: paperless/settings/__init__.py:582
#: paperless/settings/__init__.py:584
msgid "Portuguese (Brazil)"
msgstr "Portugees (Brasilië)"
#: paperless/settings/__init__.py:583
#: paperless/settings/__init__.py:585
msgid "Portuguese"
msgstr "Portugees"
#: paperless/settings/__init__.py:584
#: paperless/settings/__init__.py:586
msgid "Romanian"
msgstr "Roemeens"
#: paperless/settings/__init__.py:585
#: paperless/settings/__init__.py:587
msgid "Russian"
msgstr "Russies"
#: paperless/settings/__init__.py:586
#: paperless/settings/__init__.py:588
msgid "Slovak"
msgstr ""
#: paperless/settings/__init__.py:587
#: paperless/settings/__init__.py:589
msgid "Slovenian"
msgstr "Sloweens"
#: paperless/settings/__init__.py:588
#: paperless/settings/__init__.py:590
msgid "Serbian"
msgstr "Serwies"
#: paperless/settings/__init__.py:589
#: paperless/settings/__init__.py:591
msgid "Swedish"
msgstr "Sweeds"
#: paperless/settings/__init__.py:590
#: paperless/settings/__init__.py:592
msgid "Turkish"
msgstr "Turks"
#: paperless/settings/__init__.py:591
#: paperless/settings/__init__.py:593
msgid "Ukrainian"
msgstr ""
#: paperless/settings/__init__.py:592
#: paperless/settings/__init__.py:594
msgid "Vietnamese"
msgstr ""
#: paperless/settings/__init__.py:593
#: paperless/settings/__init__.py:595
msgid "Chinese Simplified"
msgstr "Vereenvoudigde Sjinees"
#: paperless/settings/__init__.py:594
#: paperless/settings/__init__.py:596
msgid "Chinese Traditional"
msgstr ""
#: paperless/urls.py:435
#: paperless/urls.py:438
msgid "Paperless-ngx administration"
msgstr "Paperless-ngx administrasie"
+71 -67
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-18 01:29+0000\n"
"PO-Revision-Date: 2026-09-18 01:30\n"
"POT-Creation-Date: 2026-10-02 23:09+0000\n"
"PO-Revision-Date: 2026-10-02 23:10\n"
"Last-Translator: \n"
"Language-Team: Amharic\n"
"Language: am_ET\n"
@@ -1615,7 +1615,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:514 documents/serialisers.py:871
#: documents/serialisers.py:2883 documents/views.py:343 documents/views.py:2726
#: documents/serialisers.py:2895 documents/views.py:342 documents/views.py:2729
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1624,38 +1624,38 @@ msgstr ""
msgid "Invalid color."
msgstr ""
#: documents/serialisers.py:2350
#: documents/serialisers.py:2362
#, python-format
msgid "File type %(type)s not supported"
msgstr ""
#: documents/serialisers.py:2394
#: documents/serialisers.py:2406
#, python-format
msgid "Custom field id must be an integer: %(id)s"
msgstr ""
#: documents/serialisers.py:2401
#: documents/serialisers.py:2413
#, python-format
msgid "Custom field with id %(id)s does not exist"
msgstr ""
#: documents/serialisers.py:2418 documents/serialisers.py:2428
#: documents/serialisers.py:2430 documents/serialisers.py:2440
msgid "Custom fields must be a list of integers or an object mapping ids to values."
msgstr ""
#: documents/serialisers.py:2423
#: documents/serialisers.py:2435
msgid "Some custom fields don't exist or were specified twice."
msgstr ""
#: documents/serialisers.py:2570
#: documents/serialisers.py:2582
msgid "Invalid variable detected."
msgstr ""
#: documents/serialisers.py:2939
#: documents/serialisers.py:2951
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2969 documents/views.py:4763
#: documents/serialisers.py:2981 documents/views.py:4784
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1910,40 +1910,40 @@ msgstr ""
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2723
#: documents/views.py:335 documents/views.py:2726
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1670
#: documents/views.py:1673
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1681
#: documents/views.py:1684
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1693
#: documents/views.py:1696
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2548 documents/views.py:2864
#: documents/views.py:2551 documents/views.py:2867
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4776
#: documents/views.py:4797
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4822
#: documents/views.py:4843
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4886
#: documents/views.py:4907
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4900
#: documents/views.py:4921
msgid "The share link bundle is unavailable."
msgstr ""
@@ -2188,194 +2188,198 @@ msgid "Sets the LLM embedding model"
msgstr ""
#: paperless/models.py:369
msgid "Sets the LLM embedding endpoint, optional"
msgid "Sets the LLM embedding API key"
msgstr ""
#: paperless/models.py:376
msgid "Sets the LLM embedding endpoint, optional"
msgstr ""
#: paperless/models.py:383
msgid "Sets the LLM embedding chunk size"
msgstr ""
#: paperless/models.py:382
#: paperless/models.py:389
msgid "Sets the LLM context size"
msgstr ""
#: paperless/models.py:388
#: paperless/models.py:395
msgid "Sets the LLM backend"
msgstr ""
#: paperless/models.py:396
#: paperless/models.py:403
msgid "Sets the LLM model"
msgstr ""
#: paperless/models.py:403
#: paperless/models.py:410
msgid "Sets the LLM API key"
msgstr ""
#: paperless/models.py:410
#: paperless/models.py:417
msgid "Sets the LLM endpoint, optional"
msgstr ""
#: paperless/models.py:417
#: paperless/models.py:424
msgid "Sets the LLM output language"
msgstr ""
#: paperless/models.py:424
#: paperless/models.py:431
msgid "Sets the LLM timeout in seconds"
msgstr ""
#: paperless/models.py:430
#: paperless/models.py:437
msgid "paperless application settings"
msgstr ""
#: paperless/settings/__init__.py:558
#: paperless/settings/__init__.py:560
msgid "English (US)"
msgstr ""
#: paperless/settings/__init__.py:559
#: paperless/settings/__init__.py:561
msgid "Arabic"
msgstr ""
#: paperless/settings/__init__.py:560
#: paperless/settings/__init__.py:562
msgid "Afrikaans"
msgstr ""
#: paperless/settings/__init__.py:561
#: paperless/settings/__init__.py:563
msgid "Belarusian"
msgstr ""
#: paperless/settings/__init__.py:562
#: paperless/settings/__init__.py:564
msgid "Bulgarian"
msgstr ""
#: paperless/settings/__init__.py:563
#: paperless/settings/__init__.py:565
msgid "Catalan"
msgstr ""
#: paperless/settings/__init__.py:564
#: paperless/settings/__init__.py:566
msgid "Czech"
msgstr ""
#: paperless/settings/__init__.py:565
#: paperless/settings/__init__.py:567
msgid "Danish"
msgstr ""
#: paperless/settings/__init__.py:566
#: paperless/settings/__init__.py:568
msgid "German"
msgstr ""
#: paperless/settings/__init__.py:567
#: paperless/settings/__init__.py:569
msgid "Greek"
msgstr ""
#: paperless/settings/__init__.py:568
#: paperless/settings/__init__.py:570
msgid "English (GB)"
msgstr ""
#: paperless/settings/__init__.py:569
#: paperless/settings/__init__.py:571
msgid "Spanish"
msgstr ""
#: paperless/settings/__init__.py:570
#: paperless/settings/__init__.py:572
msgid "Persian"
msgstr ""
#: paperless/settings/__init__.py:571
#: paperless/settings/__init__.py:573
msgid "Finnish"
msgstr ""
#: paperless/settings/__init__.py:572
#: paperless/settings/__init__.py:574
msgid "French"
msgstr ""
#: paperless/settings/__init__.py:573
#: paperless/settings/__init__.py:575
msgid "Hungarian"
msgstr ""
#: paperless/settings/__init__.py:574
#: paperless/settings/__init__.py:576
msgid "Indonesian"
msgstr ""
#: paperless/settings/__init__.py:575
#: paperless/settings/__init__.py:577
msgid "Italian"
msgstr ""
#: paperless/settings/__init__.py:576
#: paperless/settings/__init__.py:578
msgid "Japanese"
msgstr ""
#: paperless/settings/__init__.py:577
#: paperless/settings/__init__.py:579
msgid "Korean"
msgstr ""
#: paperless/settings/__init__.py:578
#: paperless/settings/__init__.py:580
msgid "Luxembourgish"
msgstr ""
#: paperless/settings/__init__.py:579
#: paperless/settings/__init__.py:581
msgid "Norwegian"
msgstr ""
#: paperless/settings/__init__.py:580
#: paperless/settings/__init__.py:582
msgid "Dutch"
msgstr ""
#: paperless/settings/__init__.py:581
#: paperless/settings/__init__.py:583
msgid "Polish"
msgstr ""
#: paperless/settings/__init__.py:582
#: paperless/settings/__init__.py:584
msgid "Portuguese (Brazil)"
msgstr ""
#: paperless/settings/__init__.py:583
#: paperless/settings/__init__.py:585
msgid "Portuguese"
msgstr ""
#: paperless/settings/__init__.py:584
#: paperless/settings/__init__.py:586
msgid "Romanian"
msgstr ""
#: paperless/settings/__init__.py:585
#: paperless/settings/__init__.py:587
msgid "Russian"
msgstr ""
#: paperless/settings/__init__.py:586
#: paperless/settings/__init__.py:588
msgid "Slovak"
msgstr ""
#: paperless/settings/__init__.py:587
#: paperless/settings/__init__.py:589
msgid "Slovenian"
msgstr ""
#: paperless/settings/__init__.py:588
#: paperless/settings/__init__.py:590
msgid "Serbian"
msgstr ""
#: paperless/settings/__init__.py:589
#: paperless/settings/__init__.py:591
msgid "Swedish"
msgstr ""
#: paperless/settings/__init__.py:590
#: paperless/settings/__init__.py:592
msgid "Turkish"
msgstr ""
#: paperless/settings/__init__.py:591
#: paperless/settings/__init__.py:593
msgid "Ukrainian"
msgstr ""
#: paperless/settings/__init__.py:592
#: paperless/settings/__init__.py:594
msgid "Vietnamese"
msgstr ""
#: paperless/settings/__init__.py:593
#: paperless/settings/__init__.py:595
msgid "Chinese Simplified"
msgstr ""
#: paperless/settings/__init__.py:594
#: paperless/settings/__init__.py:596
msgid "Chinese Traditional"
msgstr ""
#: paperless/urls.py:435
#: paperless/urls.py:438
msgid "Paperless-ngx administration"
msgstr ""
+71 -67
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-18 01:29+0000\n"
"PO-Revision-Date: 2026-09-18 01:30\n"
"POT-Creation-Date: 2026-10-02 23:09+0000\n"
"PO-Revision-Date: 2026-10-02 23:10\n"
"Last-Translator: \n"
"Language-Team: Arabic\n"
"Language: ar_SA\n"
@@ -1615,7 +1615,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:514 documents/serialisers.py:871
#: documents/serialisers.py:2883 documents/views.py:343 documents/views.py:2726
#: documents/serialisers.py:2895 documents/views.py:342 documents/views.py:2729
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1624,38 +1624,38 @@ msgstr ""
msgid "Invalid color."
msgstr "لون خاطئ."
#: documents/serialisers.py:2350
#: documents/serialisers.py:2362
#, python-format
msgid "File type %(type)s not supported"
msgstr "نوع الملف %(type)s غير مدعوم"
#: documents/serialisers.py:2394
#: documents/serialisers.py:2406
#, python-format
msgid "Custom field id must be an integer: %(id)s"
msgstr ""
#: documents/serialisers.py:2401
#: documents/serialisers.py:2413
#, python-format
msgid "Custom field with id %(id)s does not exist"
msgstr ""
#: documents/serialisers.py:2418 documents/serialisers.py:2428
#: documents/serialisers.py:2430 documents/serialisers.py:2440
msgid "Custom fields must be a list of integers or an object mapping ids to values."
msgstr ""
#: documents/serialisers.py:2423
#: documents/serialisers.py:2435
msgid "Some custom fields don't exist or were specified twice."
msgstr ""
#: documents/serialisers.py:2570
#: documents/serialisers.py:2582
msgid "Invalid variable detected."
msgstr "اكتشاف متغير خاطئ."
#: documents/serialisers.py:2939
#: documents/serialisers.py:2951
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2969 documents/views.py:4763
#: documents/serialisers.py:2981 documents/views.py:4784
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1911,40 +1911,40 @@ msgstr ""
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2723
#: documents/views.py:335 documents/views.py:2726
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1670
#: documents/views.py:1673
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1681
#: documents/views.py:1684
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1693
#: documents/views.py:1696
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2548 documents/views.py:2864
#: documents/views.py:2551 documents/views.py:2867
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4776
#: documents/views.py:4797
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4822
#: documents/views.py:4843
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4886
#: documents/views.py:4907
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4900
#: documents/views.py:4921
msgid "The share link bundle is unavailable."
msgstr ""
@@ -2189,194 +2189,198 @@ msgid "Sets the LLM embedding model"
msgstr ""
#: paperless/models.py:369
msgid "Sets the LLM embedding endpoint, optional"
msgid "Sets the LLM embedding API key"
msgstr ""
#: paperless/models.py:376
msgid "Sets the LLM embedding endpoint, optional"
msgstr ""
#: paperless/models.py:383
msgid "Sets the LLM embedding chunk size"
msgstr ""
#: paperless/models.py:382
#: paperless/models.py:389
msgid "Sets the LLM context size"
msgstr ""
#: paperless/models.py:388
#: paperless/models.py:395
msgid "Sets the LLM backend"
msgstr ""
#: paperless/models.py:396
#: paperless/models.py:403
msgid "Sets the LLM model"
msgstr ""
#: paperless/models.py:403
#: paperless/models.py:410
msgid "Sets the LLM API key"
msgstr ""
#: paperless/models.py:410
#: paperless/models.py:417
msgid "Sets the LLM endpoint, optional"
msgstr ""
#: paperless/models.py:417
#: paperless/models.py:424
msgid "Sets the LLM output language"
msgstr ""
#: paperless/models.py:424
#: paperless/models.py:431
msgid "Sets the LLM timeout in seconds"
msgstr ""
#: paperless/models.py:430
#: paperless/models.py:437
msgid "paperless application settings"
msgstr "إعدادات التطبيق paperless"
#: paperless/settings/__init__.py:558
#: paperless/settings/__init__.py:560
msgid "English (US)"
msgstr "الإنجليزية (الولايات المتحدة)"
#: paperless/settings/__init__.py:559
#: paperless/settings/__init__.py:561
msgid "Arabic"
msgstr "العربية"
#: paperless/settings/__init__.py:560
#: paperless/settings/__init__.py:562
msgid "Afrikaans"
msgstr "اللغة الأفريقانية"
#: paperless/settings/__init__.py:561
#: paperless/settings/__init__.py:563
msgid "Belarusian"
msgstr "البيلاروسية"
#: paperless/settings/__init__.py:562
#: paperless/settings/__init__.py:564
msgid "Bulgarian"
msgstr "البلغارية"
#: paperless/settings/__init__.py:563
#: paperless/settings/__init__.py:565
msgid "Catalan"
msgstr "اللغة الكتالونية"
#: paperless/settings/__init__.py:564
#: paperless/settings/__init__.py:566
msgid "Czech"
msgstr "التشيكية"
#: paperless/settings/__init__.py:565
#: paperless/settings/__init__.py:567
msgid "Danish"
msgstr "الدانماركية"
#: paperless/settings/__init__.py:566
#: paperless/settings/__init__.py:568
msgid "German"
msgstr "الألمانية"
#: paperless/settings/__init__.py:567
#: paperless/settings/__init__.py:569
msgid "Greek"
msgstr "اليونانية"
#: paperless/settings/__init__.py:568
#: paperless/settings/__init__.py:570
msgid "English (GB)"
msgstr "الإنجليزية (المملكة المتحدة)"
#: paperless/settings/__init__.py:569
#: paperless/settings/__init__.py:571
msgid "Spanish"
msgstr "الإسبانية"
#: paperless/settings/__init__.py:570
#: paperless/settings/__init__.py:572
msgid "Persian"
msgstr ""
#: paperless/settings/__init__.py:571
#: paperless/settings/__init__.py:573
msgid "Finnish"
msgstr "الفنلندية"
#: paperless/settings/__init__.py:572
#: paperless/settings/__init__.py:574
msgid "French"
msgstr "الفرنسية"
#: paperless/settings/__init__.py:573
#: paperless/settings/__init__.py:575
msgid "Hungarian"
msgstr "المجرية"
#: paperless/settings/__init__.py:574
#: paperless/settings/__init__.py:576
msgid "Indonesian"
msgstr ""
#: paperless/settings/__init__.py:575
#: paperless/settings/__init__.py:577
msgid "Italian"
msgstr "الإيطالية"
#: paperless/settings/__init__.py:576
#: paperless/settings/__init__.py:578
msgid "Japanese"
msgstr "اليابانية"
#: paperless/settings/__init__.py:577
#: paperless/settings/__init__.py:579
msgid "Korean"
msgstr ""
#: paperless/settings/__init__.py:578
#: paperless/settings/__init__.py:580
msgid "Luxembourgish"
msgstr "اللوكسمبرجية"
#: paperless/settings/__init__.py:579
#: paperless/settings/__init__.py:581
msgid "Norwegian"
msgstr "النرويجية"
#: paperless/settings/__init__.py:580
#: paperless/settings/__init__.py:582
msgid "Dutch"
msgstr "الهولندية"
#: paperless/settings/__init__.py:581
#: paperless/settings/__init__.py:583
msgid "Polish"
msgstr "البولندية"
#: paperless/settings/__init__.py:582
#: paperless/settings/__init__.py:584
msgid "Portuguese (Brazil)"
msgstr "البرتغالية (البرازيل)"
#: paperless/settings/__init__.py:583
#: paperless/settings/__init__.py:585
msgid "Portuguese"
msgstr "البرتغالية"
#: paperless/settings/__init__.py:584
#: paperless/settings/__init__.py:586
msgid "Romanian"
msgstr "الرومانية"
#: paperless/settings/__init__.py:585
#: paperless/settings/__init__.py:587
msgid "Russian"
msgstr "الروسية"
#: paperless/settings/__init__.py:586
#: paperless/settings/__init__.py:588
msgid "Slovak"
msgstr "السلوفاكية"
#: paperless/settings/__init__.py:587
#: paperless/settings/__init__.py:589
msgid "Slovenian"
msgstr "السلوفانية"
#: paperless/settings/__init__.py:588
#: paperless/settings/__init__.py:590
msgid "Serbian"
msgstr "الصربية"
#: paperless/settings/__init__.py:589
#: paperless/settings/__init__.py:591
msgid "Swedish"
msgstr "السويدية"
#: paperless/settings/__init__.py:590
#: paperless/settings/__init__.py:592
msgid "Turkish"
msgstr "التركية"
#: paperless/settings/__init__.py:591
#: paperless/settings/__init__.py:593
msgid "Ukrainian"
msgstr "الأوكرانية"
#: paperless/settings/__init__.py:592
#: paperless/settings/__init__.py:594
msgid "Vietnamese"
msgstr ""
#: paperless/settings/__init__.py:593
#: paperless/settings/__init__.py:595
msgid "Chinese Simplified"
msgstr "الصينية المبسطة"
#: paperless/settings/__init__.py:594
#: paperless/settings/__init__.py:596
msgid "Chinese Traditional"
msgstr ""
#: paperless/urls.py:435
#: paperless/urls.py:438
msgid "Paperless-ngx administration"
msgstr "Paperless-ngx الإدارة"
+71 -67
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-18 01:29+0000\n"
"PO-Revision-Date: 2026-09-18 01:30\n"
"POT-Creation-Date: 2026-10-02 23:09+0000\n"
"PO-Revision-Date: 2026-10-02 23:10\n"
"Last-Translator: \n"
"Language-Team: Belarusian\n"
"Language: be_BY\n"
@@ -1615,7 +1615,7 @@ msgid "workflow runs"
msgstr ""
#: documents/serialisers.py:514 documents/serialisers.py:871
#: documents/serialisers.py:2883 documents/views.py:343 documents/views.py:2726
#: documents/serialisers.py:2895 documents/views.py:342 documents/views.py:2729
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1624,38 +1624,38 @@ msgstr ""
msgid "Invalid color."
msgstr "Няправільны колер."
#: documents/serialisers.py:2350
#: documents/serialisers.py:2362
#, python-format
msgid "File type %(type)s not supported"
msgstr "Тып файла %(type)s не падтрымліваецца"
#: documents/serialisers.py:2394
#: documents/serialisers.py:2406
#, python-format
msgid "Custom field id must be an integer: %(id)s"
msgstr ""
#: documents/serialisers.py:2401
#: documents/serialisers.py:2413
#, python-format
msgid "Custom field with id %(id)s does not exist"
msgstr ""
#: documents/serialisers.py:2418 documents/serialisers.py:2428
#: documents/serialisers.py:2430 documents/serialisers.py:2440
msgid "Custom fields must be a list of integers or an object mapping ids to values."
msgstr ""
#: documents/serialisers.py:2423
#: documents/serialisers.py:2435
msgid "Some custom fields don't exist or were specified twice."
msgstr ""
#: documents/serialisers.py:2570
#: documents/serialisers.py:2582
msgid "Invalid variable detected."
msgstr "Выяўлена няправільная зменная."
#: documents/serialisers.py:2939
#: documents/serialisers.py:2951
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2969 documents/views.py:4763
#: documents/serialisers.py:2981 documents/views.py:4784
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1910,40 +1910,40 @@ msgstr ""
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2723
#: documents/views.py:335 documents/views.py:2726
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1670
#: documents/views.py:1673
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1681
#: documents/views.py:1684
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1693
#: documents/views.py:1696
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2548 documents/views.py:2864
#: documents/views.py:2551 documents/views.py:2867
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4776
#: documents/views.py:4797
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4822
#: documents/views.py:4843
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4886
#: documents/views.py:4907
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4900
#: documents/views.py:4921
msgid "The share link bundle is unavailable."
msgstr ""
@@ -2188,194 +2188,198 @@ msgid "Sets the LLM embedding model"
msgstr ""
#: paperless/models.py:369
msgid "Sets the LLM embedding endpoint, optional"
msgid "Sets the LLM embedding API key"
msgstr ""
#: paperless/models.py:376
msgid "Sets the LLM embedding endpoint, optional"
msgstr ""
#: paperless/models.py:383
msgid "Sets the LLM embedding chunk size"
msgstr ""
#: paperless/models.py:382
#: paperless/models.py:389
msgid "Sets the LLM context size"
msgstr ""
#: paperless/models.py:388
#: paperless/models.py:395
msgid "Sets the LLM backend"
msgstr ""
#: paperless/models.py:396
#: paperless/models.py:403
msgid "Sets the LLM model"
msgstr ""
#: paperless/models.py:403
#: paperless/models.py:410
msgid "Sets the LLM API key"
msgstr ""
#: paperless/models.py:410
#: paperless/models.py:417
msgid "Sets the LLM endpoint, optional"
msgstr ""
#: paperless/models.py:417
#: paperless/models.py:424
msgid "Sets the LLM output language"
msgstr ""
#: paperless/models.py:424
#: paperless/models.py:431
msgid "Sets the LLM timeout in seconds"
msgstr ""
#: paperless/models.py:430
#: paperless/models.py:437
msgid "paperless application settings"
msgstr ""
#: paperless/settings/__init__.py:558
#: paperless/settings/__init__.py:560
msgid "English (US)"
msgstr "Англійская (ЗША)"
#: paperless/settings/__init__.py:559
#: paperless/settings/__init__.py:561
msgid "Arabic"
msgstr ""
#: paperless/settings/__init__.py:560
#: paperless/settings/__init__.py:562
msgid "Afrikaans"
msgstr ""
#: paperless/settings/__init__.py:561
#: paperless/settings/__init__.py:563
msgid "Belarusian"
msgstr "Беларуская"
#: paperless/settings/__init__.py:562
#: paperless/settings/__init__.py:564
msgid "Bulgarian"
msgstr ""
#: paperless/settings/__init__.py:563
#: paperless/settings/__init__.py:565
msgid "Catalan"
msgstr ""
#: paperless/settings/__init__.py:564
#: paperless/settings/__init__.py:566
msgid "Czech"
msgstr "Чэшская"
#: paperless/settings/__init__.py:565
#: paperless/settings/__init__.py:567
msgid "Danish"
msgstr "Дацкая"
#: paperless/settings/__init__.py:566
#: paperless/settings/__init__.py:568
msgid "German"
msgstr "Нямецкая"
#: paperless/settings/__init__.py:567
#: paperless/settings/__init__.py:569
msgid "Greek"
msgstr ""
#: paperless/settings/__init__.py:568
#: paperless/settings/__init__.py:570
msgid "English (GB)"
msgstr "Англійская (Вялікабрытанія)"
#: paperless/settings/__init__.py:569
#: paperless/settings/__init__.py:571
msgid "Spanish"
msgstr "Іспанская"
#: paperless/settings/__init__.py:570
#: paperless/settings/__init__.py:572
msgid "Persian"
msgstr ""
#: paperless/settings/__init__.py:571
#: paperless/settings/__init__.py:573
msgid "Finnish"
msgstr ""
#: paperless/settings/__init__.py:572
#: paperless/settings/__init__.py:574
msgid "French"
msgstr "Французская"
#: paperless/settings/__init__.py:573
#: paperless/settings/__init__.py:575
msgid "Hungarian"
msgstr ""
#: paperless/settings/__init__.py:574
#: paperless/settings/__init__.py:576
msgid "Indonesian"
msgstr ""
#: paperless/settings/__init__.py:575
#: paperless/settings/__init__.py:577
msgid "Italian"
msgstr "Італьянская"
#: paperless/settings/__init__.py:576
#: paperless/settings/__init__.py:578
msgid "Japanese"
msgstr ""
#: paperless/settings/__init__.py:577
#: paperless/settings/__init__.py:579
msgid "Korean"
msgstr ""
#: paperless/settings/__init__.py:578
#: paperless/settings/__init__.py:580
msgid "Luxembourgish"
msgstr "Люксембургская"
#: paperless/settings/__init__.py:579
#: paperless/settings/__init__.py:581
msgid "Norwegian"
msgstr ""
#: paperless/settings/__init__.py:580
#: paperless/settings/__init__.py:582
msgid "Dutch"
msgstr "Нідэрландская"
#: paperless/settings/__init__.py:581
#: paperless/settings/__init__.py:583
msgid "Polish"
msgstr "Польская"
#: paperless/settings/__init__.py:582
#: paperless/settings/__init__.py:584
msgid "Portuguese (Brazil)"
msgstr "Партугальская (Бразілія)"
#: paperless/settings/__init__.py:583
#: paperless/settings/__init__.py:585
msgid "Portuguese"
msgstr "Партугальская"
#: paperless/settings/__init__.py:584
#: paperless/settings/__init__.py:586
msgid "Romanian"
msgstr "Румынская"
#: paperless/settings/__init__.py:585
#: paperless/settings/__init__.py:587
msgid "Russian"
msgstr "Руская"
#: paperless/settings/__init__.py:586
#: paperless/settings/__init__.py:588
msgid "Slovak"
msgstr ""
#: paperless/settings/__init__.py:587
#: paperless/settings/__init__.py:589
msgid "Slovenian"
msgstr "Славенская"
#: paperless/settings/__init__.py:588
#: paperless/settings/__init__.py:590
msgid "Serbian"
msgstr "Сербская"
#: paperless/settings/__init__.py:589
#: paperless/settings/__init__.py:591
msgid "Swedish"
msgstr "Шведская"
#: paperless/settings/__init__.py:590
#: paperless/settings/__init__.py:592
msgid "Turkish"
msgstr "Турэцкая"
#: paperless/settings/__init__.py:591
#: paperless/settings/__init__.py:593
msgid "Ukrainian"
msgstr ""
#: paperless/settings/__init__.py:592
#: paperless/settings/__init__.py:594
msgid "Vietnamese"
msgstr ""
#: paperless/settings/__init__.py:593
#: paperless/settings/__init__.py:595
msgid "Chinese Simplified"
msgstr "Кітайская спрошчаная"
#: paperless/settings/__init__.py:594
#: paperless/settings/__init__.py:596
msgid "Chinese Traditional"
msgstr ""
#: paperless/urls.py:435
#: paperless/urls.py:438
msgid "Paperless-ngx administration"
msgstr "Адміністраванне Paperless-ngx"
+71 -67
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-18 01:29+0000\n"
"PO-Revision-Date: 2026-09-18 01:30\n"
"POT-Creation-Date: 2026-10-02 23:09+0000\n"
"PO-Revision-Date: 2026-10-02 23:10\n"
"Last-Translator: \n"
"Language-Team: Bulgarian\n"
"Language: bg_BG\n"
@@ -1615,7 +1615,7 @@ msgid "workflow runs"
msgstr "стартиране на работните процеси"
#: documents/serialisers.py:514 documents/serialisers.py:871
#: documents/serialisers.py:2883 documents/views.py:343 documents/views.py:2726
#: documents/serialisers.py:2895 documents/views.py:342 documents/views.py:2729
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr ""
@@ -1624,38 +1624,38 @@ msgstr ""
msgid "Invalid color."
msgstr "Невалиден цвят."
#: documents/serialisers.py:2350
#: documents/serialisers.py:2362
#, python-format
msgid "File type %(type)s not supported"
msgstr "Файловия тип %(type)s не се поддържа"
#: documents/serialisers.py:2394
#: documents/serialisers.py:2406
#, python-format
msgid "Custom field id must be an integer: %(id)s"
msgstr ""
#: documents/serialisers.py:2401
#: documents/serialisers.py:2413
#, python-format
msgid "Custom field with id %(id)s does not exist"
msgstr ""
#: documents/serialisers.py:2418 documents/serialisers.py:2428
#: documents/serialisers.py:2430 documents/serialisers.py:2440
msgid "Custom fields must be a list of integers or an object mapping ids to values."
msgstr ""
#: documents/serialisers.py:2423
#: documents/serialisers.py:2435
msgid "Some custom fields don't exist or were specified twice."
msgstr ""
#: documents/serialisers.py:2570
#: documents/serialisers.py:2582
msgid "Invalid variable detected."
msgstr "Засечена е невалидна променлива."
#: documents/serialisers.py:2939
#: documents/serialisers.py:2951
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2969 documents/views.py:4763
#: documents/serialisers.py:2981 documents/views.py:4784
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1911,40 +1911,40 @@ msgstr ""
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2723
#: documents/views.py:335 documents/views.py:2726
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1670
#: documents/views.py:1673
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1681
#: documents/views.py:1684
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1693
#: documents/views.py:1696
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2548 documents/views.py:2864
#: documents/views.py:2551 documents/views.py:2867
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4776
#: documents/views.py:4797
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr ""
#: documents/views.py:4822
#: documents/views.py:4843
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4886
#: documents/views.py:4907
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4900
#: documents/views.py:4921
msgid "The share link bundle is unavailable."
msgstr ""
@@ -2189,194 +2189,198 @@ msgid "Sets the LLM embedding model"
msgstr ""
#: paperless/models.py:369
msgid "Sets the LLM embedding endpoint, optional"
msgid "Sets the LLM embedding API key"
msgstr ""
#: paperless/models.py:376
msgid "Sets the LLM embedding endpoint, optional"
msgstr ""
#: paperless/models.py:383
msgid "Sets the LLM embedding chunk size"
msgstr ""
#: paperless/models.py:382
#: paperless/models.py:389
msgid "Sets the LLM context size"
msgstr ""
#: paperless/models.py:388
#: paperless/models.py:395
msgid "Sets the LLM backend"
msgstr ""
#: paperless/models.py:396
#: paperless/models.py:403
msgid "Sets the LLM model"
msgstr ""
#: paperless/models.py:403
#: paperless/models.py:410
msgid "Sets the LLM API key"
msgstr ""
#: paperless/models.py:410
#: paperless/models.py:417
msgid "Sets the LLM endpoint, optional"
msgstr ""
#: paperless/models.py:417
#: paperless/models.py:424
msgid "Sets the LLM output language"
msgstr ""
#: paperless/models.py:424
#: paperless/models.py:431
msgid "Sets the LLM timeout in seconds"
msgstr ""
#: paperless/models.py:430
#: paperless/models.py:437
msgid "paperless application settings"
msgstr "настройки на софтуера"
#: paperless/settings/__init__.py:558
#: paperless/settings/__init__.py:560
msgid "English (US)"
msgstr "Английски (САЩ)"
#: paperless/settings/__init__.py:559
#: paperless/settings/__init__.py:561
msgid "Arabic"
msgstr "Арабски"
#: paperless/settings/__init__.py:560
#: paperless/settings/__init__.py:562
msgid "Afrikaans"
msgstr "Африканс"
#: paperless/settings/__init__.py:561
#: paperless/settings/__init__.py:563
msgid "Belarusian"
msgstr "Беларуски"
#: paperless/settings/__init__.py:562
#: paperless/settings/__init__.py:564
msgid "Bulgarian"
msgstr "Български"
#: paperless/settings/__init__.py:563
#: paperless/settings/__init__.py:565
msgid "Catalan"
msgstr "Каталунски"
#: paperless/settings/__init__.py:564
#: paperless/settings/__init__.py:566
msgid "Czech"
msgstr "Чешки"
#: paperless/settings/__init__.py:565
#: paperless/settings/__init__.py:567
msgid "Danish"
msgstr "Датски"
#: paperless/settings/__init__.py:566
#: paperless/settings/__init__.py:568
msgid "German"
msgstr "Немски"
#: paperless/settings/__init__.py:567
#: paperless/settings/__init__.py:569
msgid "Greek"
msgstr "Гръцки"
#: paperless/settings/__init__.py:568
#: paperless/settings/__init__.py:570
msgid "English (GB)"
msgstr "Английски (Великобритания)"
#: paperless/settings/__init__.py:569
#: paperless/settings/__init__.py:571
msgid "Spanish"
msgstr "Испански"
#: paperless/settings/__init__.py:570
#: paperless/settings/__init__.py:572
msgid "Persian"
msgstr ""
#: paperless/settings/__init__.py:571
#: paperless/settings/__init__.py:573
msgid "Finnish"
msgstr "Финландски"
#: paperless/settings/__init__.py:572
#: paperless/settings/__init__.py:574
msgid "French"
msgstr "Френски"
#: paperless/settings/__init__.py:573
#: paperless/settings/__init__.py:575
msgid "Hungarian"
msgstr "Унгарски"
#: paperless/settings/__init__.py:574
#: paperless/settings/__init__.py:576
msgid "Indonesian"
msgstr ""
#: paperless/settings/__init__.py:575
#: paperless/settings/__init__.py:577
msgid "Italian"
msgstr "Италиански"
#: paperless/settings/__init__.py:576
#: paperless/settings/__init__.py:578
msgid "Japanese"
msgstr "Японски"
#: paperless/settings/__init__.py:577
#: paperless/settings/__init__.py:579
msgid "Korean"
msgstr "Корейски"
#: paperless/settings/__init__.py:578
#: paperless/settings/__init__.py:580
msgid "Luxembourgish"
msgstr "Люксембургски"
#: paperless/settings/__init__.py:579
#: paperless/settings/__init__.py:581
msgid "Norwegian"
msgstr "Норвежки"
#: paperless/settings/__init__.py:580
#: paperless/settings/__init__.py:582
msgid "Dutch"
msgstr "Холандски"
#: paperless/settings/__init__.py:581
#: paperless/settings/__init__.py:583
msgid "Polish"
msgstr "Полски"
#: paperless/settings/__init__.py:582
#: paperless/settings/__init__.py:584
msgid "Portuguese (Brazil)"
msgstr "Португалски (Бразилия)"
#: paperless/settings/__init__.py:583
#: paperless/settings/__init__.py:585
msgid "Portuguese"
msgstr "Португалски"
#: paperless/settings/__init__.py:584
#: paperless/settings/__init__.py:586
msgid "Romanian"
msgstr "Румънски"
#: paperless/settings/__init__.py:585
#: paperless/settings/__init__.py:587
msgid "Russian"
msgstr "Руски"
#: paperless/settings/__init__.py:586
#: paperless/settings/__init__.py:588
msgid "Slovak"
msgstr "Словашки"
#: paperless/settings/__init__.py:587
#: paperless/settings/__init__.py:589
msgid "Slovenian"
msgstr "Словенски"
#: paperless/settings/__init__.py:588
#: paperless/settings/__init__.py:590
msgid "Serbian"
msgstr "Сръбски"
#: paperless/settings/__init__.py:589
#: paperless/settings/__init__.py:591
msgid "Swedish"
msgstr "Шведски"
#: paperless/settings/__init__.py:590
#: paperless/settings/__init__.py:592
msgid "Turkish"
msgstr "Турски"
#: paperless/settings/__init__.py:591
#: paperless/settings/__init__.py:593
msgid "Ukrainian"
msgstr "Украински"
#: paperless/settings/__init__.py:592
#: paperless/settings/__init__.py:594
msgid "Vietnamese"
msgstr ""
#: paperless/settings/__init__.py:593
#: paperless/settings/__init__.py:595
msgid "Chinese Simplified"
msgstr "Китайски опростен"
#: paperless/settings/__init__.py:594
#: paperless/settings/__init__.py:596
msgid "Chinese Traditional"
msgstr "Китайски традиционен"
#: paperless/urls.py:435
#: paperless/urls.py:438
msgid "Paperless-ngx administration"
msgstr "Paperless-ngx администрация"
+71 -67
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-18 01:29+0000\n"
"PO-Revision-Date: 2026-09-18 01:30\n"
"POT-Creation-Date: 2026-10-02 23:09+0000\n"
"PO-Revision-Date: 2026-10-02 23:10\n"
"Last-Translator: \n"
"Language-Team: Catalan\n"
"Language: ca_ES\n"
@@ -1615,7 +1615,7 @@ msgid "workflow runs"
msgstr "flux corrents"
#: documents/serialisers.py:514 documents/serialisers.py:871
#: documents/serialisers.py:2883 documents/views.py:343 documents/views.py:2726
#: documents/serialisers.py:2895 documents/views.py:342 documents/views.py:2729
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Permisos insuficients."
@@ -1624,38 +1624,38 @@ msgstr "Permisos insuficients."
msgid "Invalid color."
msgstr "Color Invàlid."
#: documents/serialisers.py:2350
#: documents/serialisers.py:2362
#, python-format
msgid "File type %(type)s not supported"
msgstr "Tipus arxiu %(type)s no suportat"
#: documents/serialisers.py:2394
#: documents/serialisers.py:2406
#, python-format
msgid "Custom field id must be an integer: %(id)s"
msgstr "ID de camp personalizat ha de ser enter: %(id)s"
#: documents/serialisers.py:2401
#: documents/serialisers.py:2413
#, python-format
msgid "Custom field with id %(id)s does not exist"
msgstr "Camp personalitzat amb ID %(id)s no existeix"
#: documents/serialisers.py:2418 documents/serialisers.py:2428
#: documents/serialisers.py:2430 documents/serialisers.py:2440
msgid "Custom fields must be a list of integers or an object mapping ids to values."
msgstr "Camps personalitzats han de ser una llista d'enters o un objecte que mapegi els identificadors amb els valors."
#: documents/serialisers.py:2423
#: documents/serialisers.py:2435
msgid "Some custom fields don't exist or were specified twice."
msgstr "Alguns camps personalitzats no existeixen o s'han especificat dues vegades."
#: documents/serialisers.py:2570
#: documents/serialisers.py:2582
msgid "Invalid variable detected."
msgstr "Variable detectada invàlida."
#: documents/serialisers.py:2939
#: documents/serialisers.py:2951
msgid "Duplicate document identifiers are not allowed."
msgstr "Duplicat d'identificadors de documents no permès."
#: documents/serialisers.py:2969 documents/views.py:4763
#: documents/serialisers.py:2981 documents/views.py:4784
#, python-format
msgid "Documents not found: %(ids)s"
msgstr "Documents no trobats: %(ids)s"
@@ -1911,40 +1911,40 @@ msgstr "L'esquema d'URI '{parts.scheme}' no està permès. Esquemes permesos: {'
msgid "Unable to parse URI {value}"
msgstr "No s'ha pogut analitzar l'URI {value}"
#: documents/views.py:336 documents/views.py:2723
#: documents/views.py:335 documents/views.py:2726
msgid "Invalid more_like_id"
msgstr "Invalid more_like_id"
#: documents/views.py:1670
#: documents/views.py:1673
msgid "Invalid AI configuration."
msgstr "Configuració AI invàlida."
#: documents/views.py:1681
#: documents/views.py:1684
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1693
#: documents/views.py:1696
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2548 documents/views.py:2864
#: documents/views.py:2551 documents/views.py:2867
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Especifica només un dels següents valors: text, title_search, query o more_like_id."
#: documents/views.py:4776
#: documents/views.py:4797
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Permisos insuficients per compartir document %(id)s."
#: documents/views.py:4822
#: documents/views.py:4843
msgid "Bundle is already being processed."
msgstr "Paquet ja s'està processant."
#: documents/views.py:4886
#: documents/views.py:4907
msgid "The share link bundle is still being prepared. Please try again later."
msgstr "El paquet de link encarà s'està preparant. Prova de nou més tard."
#: documents/views.py:4900
#: documents/views.py:4921
msgid "The share link bundle is unavailable."
msgstr "El paquet d'enllaç no està disponible."
@@ -2189,194 +2189,198 @@ msgid "Sets the LLM embedding model"
msgstr "Defineix model d'incrustació LLM"
#: paperless/models.py:369
msgid "Sets the LLM embedding API key"
msgstr ""
#: paperless/models.py:376
msgid "Sets the LLM embedding endpoint, optional"
msgstr "Defineix el punt final d'incrustació de LLM, opcional"
#: paperless/models.py:376
#: paperless/models.py:383
msgid "Sets the LLM embedding chunk size"
msgstr "Defineix la mida del fragment d'incrustació de LLM"
#: paperless/models.py:382
#: paperless/models.py:389
msgid "Sets the LLM context size"
msgstr "Defineix la mida del context LLM"
#: paperless/models.py:388
#: paperless/models.py:395
msgid "Sets the LLM backend"
msgstr "Estableix backend LLM"
#: paperless/models.py:396
#: paperless/models.py:403
msgid "Sets the LLM model"
msgstr "Estableix model LLM"
#: paperless/models.py:403
#: paperless/models.py:410
msgid "Sets the LLM API key"
msgstr "Estableix clau API de LLM"
#: paperless/models.py:410
#: paperless/models.py:417
msgid "Sets the LLM endpoint, optional"
msgstr "Estableix el punt final de l'LLM, opcional"
#: paperless/models.py:417
#: paperless/models.py:424
msgid "Sets the LLM output language"
msgstr "Estableix l'idioma de sortida de LLM"
#: paperless/models.py:424
#: paperless/models.py:431
msgid "Sets the LLM timeout in seconds"
msgstr "Estableix temps d'espera en segons"
#: paperless/models.py:430
#: paperless/models.py:437
msgid "paperless application settings"
msgstr "configuració de l'aplicació paperless"
#: paperless/settings/__init__.py:558
#: paperless/settings/__init__.py:560
msgid "English (US)"
msgstr "Anglès (US)"
#: paperless/settings/__init__.py:559
#: paperless/settings/__init__.py:561
msgid "Arabic"
msgstr "Àrab"
#: paperless/settings/__init__.py:560
#: paperless/settings/__init__.py:562
msgid "Afrikaans"
msgstr "Africà"
#: paperless/settings/__init__.py:561
#: paperless/settings/__init__.py:563
msgid "Belarusian"
msgstr "Bielorús"
#: paperless/settings/__init__.py:562
#: paperless/settings/__init__.py:564
msgid "Bulgarian"
msgstr "Búlgar"
#: paperless/settings/__init__.py:563
#: paperless/settings/__init__.py:565
msgid "Catalan"
msgstr "Català"
#: paperless/settings/__init__.py:564
#: paperless/settings/__init__.py:566
msgid "Czech"
msgstr "Txec"
#: paperless/settings/__init__.py:565
#: paperless/settings/__init__.py:567
msgid "Danish"
msgstr "Danès"
#: paperless/settings/__init__.py:566
#: paperless/settings/__init__.py:568
msgid "German"
msgstr "Alemany"
#: paperless/settings/__init__.py:567
#: paperless/settings/__init__.py:569
msgid "Greek"
msgstr "Grec"
#: paperless/settings/__init__.py:568
#: paperless/settings/__init__.py:570
msgid "English (GB)"
msgstr "Anglès (GB)"
#: paperless/settings/__init__.py:569
#: paperless/settings/__init__.py:571
msgid "Spanish"
msgstr "Espanyol"
#: paperless/settings/__init__.py:570
#: paperless/settings/__init__.py:572
msgid "Persian"
msgstr "Persa"
#: paperless/settings/__init__.py:571
#: paperless/settings/__init__.py:573
msgid "Finnish"
msgstr "Finès"
#: paperless/settings/__init__.py:572
#: paperless/settings/__init__.py:574
msgid "French"
msgstr "Francès"
#: paperless/settings/__init__.py:573
#: paperless/settings/__init__.py:575
msgid "Hungarian"
msgstr "Hongarès"
#: paperless/settings/__init__.py:574
#: paperless/settings/__init__.py:576
msgid "Indonesian"
msgstr "Indonesi"
#: paperless/settings/__init__.py:575
#: paperless/settings/__init__.py:577
msgid "Italian"
msgstr "Italià"
#: paperless/settings/__init__.py:576
#: paperless/settings/__init__.py:578
msgid "Japanese"
msgstr "Japonès"
#: paperless/settings/__init__.py:577
#: paperless/settings/__init__.py:579
msgid "Korean"
msgstr "Coreà"
#: paperless/settings/__init__.py:578
#: paperless/settings/__init__.py:580
msgid "Luxembourgish"
msgstr "Luxemburguès"
#: paperless/settings/__init__.py:579
#: paperless/settings/__init__.py:581
msgid "Norwegian"
msgstr "Noruec"
#: paperless/settings/__init__.py:580
#: paperless/settings/__init__.py:582
msgid "Dutch"
msgstr "Holandès"
#: paperless/settings/__init__.py:581
#: paperless/settings/__init__.py:583
msgid "Polish"
msgstr "Polonès"
#: paperless/settings/__init__.py:582
#: paperless/settings/__init__.py:584
msgid "Portuguese (Brazil)"
msgstr "Portuguès (Brasil)"
#: paperless/settings/__init__.py:583
#: paperless/settings/__init__.py:585
msgid "Portuguese"
msgstr "Portuguès"
#: paperless/settings/__init__.py:584
#: paperless/settings/__init__.py:586
msgid "Romanian"
msgstr "Romanès"
#: paperless/settings/__init__.py:585
#: paperless/settings/__init__.py:587
msgid "Russian"
msgstr "Rus"
#: paperless/settings/__init__.py:586
#: paperless/settings/__init__.py:588
msgid "Slovak"
msgstr "Eslovac"
#: paperless/settings/__init__.py:587
#: paperless/settings/__init__.py:589
msgid "Slovenian"
msgstr "Eslovè"
#: paperless/settings/__init__.py:588
#: paperless/settings/__init__.py:590
msgid "Serbian"
msgstr "Serbi"
#: paperless/settings/__init__.py:589
#: paperless/settings/__init__.py:591
msgid "Swedish"
msgstr "Suec"
#: paperless/settings/__init__.py:590
#: paperless/settings/__init__.py:592
msgid "Turkish"
msgstr "Turc"
#: paperless/settings/__init__.py:591
#: paperless/settings/__init__.py:593
msgid "Ukrainian"
msgstr "Ucranià"
#: paperless/settings/__init__.py:592
#: paperless/settings/__init__.py:594
msgid "Vietnamese"
msgstr "Vietnamita"
#: paperless/settings/__init__.py:593
#: paperless/settings/__init__.py:595
msgid "Chinese Simplified"
msgstr "Xinès Simplificat"
#: paperless/settings/__init__.py:594
#: paperless/settings/__init__.py:596
msgid "Chinese Traditional"
msgstr "Xinès tradicional"
#: paperless/urls.py:435
#: paperless/urls.py:438
msgid "Paperless-ngx administration"
msgstr "Administració Paperless-ngx"
+71 -67
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-18 01:29+0000\n"
"PO-Revision-Date: 2026-09-18 01:30\n"
"POT-Creation-Date: 2026-10-02 23:09+0000\n"
"PO-Revision-Date: 2026-10-02 23:10\n"
"Last-Translator: \n"
"Language-Team: Czech\n"
"Language: cs_CZ\n"
@@ -1615,7 +1615,7 @@ msgid "workflow runs"
msgstr "spuštění pracovních postupů"
#: documents/serialisers.py:514 documents/serialisers.py:871
#: documents/serialisers.py:2883 documents/views.py:343 documents/views.py:2726
#: documents/serialisers.py:2895 documents/views.py:342 documents/views.py:2729
#: paperless_mail/serialisers.py:156
msgid "Insufficient permissions."
msgstr "Nedostatečná oprávnění."
@@ -1624,38 +1624,38 @@ msgstr "Nedostatečná oprávnění."
msgid "Invalid color."
msgstr "Neplatná barva."
#: documents/serialisers.py:2350
#: documents/serialisers.py:2362
#, python-format
msgid "File type %(type)s not supported"
msgstr "Typ souboru %(type)s není podporován"
#: documents/serialisers.py:2394
#: documents/serialisers.py:2406
#, python-format
msgid "Custom field id must be an integer: %(id)s"
msgstr "Vlastní ID pole musí být celé číslo: %(id)s"
#: documents/serialisers.py:2401
#: documents/serialisers.py:2413
#, python-format
msgid "Custom field with id %(id)s does not exist"
msgstr "Vlastní pole s ID %(id)s neexistuje"
#: documents/serialisers.py:2418 documents/serialisers.py:2428
#: documents/serialisers.py:2430 documents/serialisers.py:2440
msgid "Custom fields must be a list of integers or an object mapping ids to values."
msgstr "Vlastní pole musí být seznam celých čísel nebo ID pro mapování objektů na hodnoty."
#: documents/serialisers.py:2423
#: documents/serialisers.py:2435
msgid "Some custom fields don't exist or were specified twice."
msgstr "Některá vlastní pole neexistují nebo byla zadána dvakrát."
#: documents/serialisers.py:2570
#: documents/serialisers.py:2582
msgid "Invalid variable detected."
msgstr "Zjištěna neplatná proměnná."
#: documents/serialisers.py:2939
#: documents/serialisers.py:2951
msgid "Duplicate document identifiers are not allowed."
msgstr ""
#: documents/serialisers.py:2969 documents/views.py:4763
#: documents/serialisers.py:2981 documents/views.py:4784
#, python-format
msgid "Documents not found: %(ids)s"
msgstr ""
@@ -1912,40 +1912,40 @@ msgstr "URI schéma '{parts.scheme}' není povoleno. Povolená schémata: {',\n"
msgid "Unable to parse URI {value}"
msgstr "Nelze zpracovat URI {value}"
#: documents/views.py:336 documents/views.py:2723
#: documents/views.py:335 documents/views.py:2726
msgid "Invalid more_like_id"
msgstr ""
#: documents/views.py:1670
#: documents/views.py:1673
msgid "Invalid AI configuration."
msgstr ""
#: documents/views.py:1681
#: documents/views.py:1684
msgid "AI backend request timed out."
msgstr ""
#: documents/views.py:1693
#: documents/views.py:1696
msgid "AI backend rejected the request. Check logs for details."
msgstr ""
#: documents/views.py:2548 documents/views.py:2864
#: documents/views.py:2551 documents/views.py:2867
msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr ""
#: documents/views.py:4776
#: documents/views.py:4797
#, python-format
msgid "Insufficient permissions to share document %(id)s."
msgstr "Nedostatečná oprávnění ke sdílení dokumentu %(id)s."
#: documents/views.py:4822
#: documents/views.py:4843
msgid "Bundle is already being processed."
msgstr ""
#: documents/views.py:4886
#: documents/views.py:4907
msgid "The share link bundle is still being prepared. Please try again later."
msgstr ""
#: documents/views.py:4900
#: documents/views.py:4921
msgid "The share link bundle is unavailable."
msgstr ""
@@ -2190,194 +2190,198 @@ msgid "Sets the LLM embedding model"
msgstr "Nastaví model vkládání LLM"
#: paperless/models.py:369
msgid "Sets the LLM embedding API key"
msgstr ""
#: paperless/models.py:376
msgid "Sets the LLM embedding endpoint, optional"
msgstr "Nastaví koncový bod vkládání LLM, volitelné"
#: paperless/models.py:376
#: paperless/models.py:383
msgid "Sets the LLM embedding chunk size"
msgstr "Nastaví velikost bloků vkládání LLM"
#: paperless/models.py:382
#: paperless/models.py:389
msgid "Sets the LLM context size"
msgstr "Nastaví velikost kontextu LLM"
#: paperless/models.py:388
#: paperless/models.py:395
msgid "Sets the LLM backend"
msgstr "Nastaví backend LLM"
#: paperless/models.py:396
#: paperless/models.py:403
msgid "Sets the LLM model"
msgstr "Nastaví model LLM"
#: paperless/models.py:403
#: paperless/models.py:410
msgid "Sets the LLM API key"
msgstr "Nastaví klíč API LLM"
#: paperless/models.py:410
#: paperless/models.py:417
msgid "Sets the LLM endpoint, optional"
msgstr "Nastaví koncový bod LLM, volitelné"
#: paperless/models.py:417
#: paperless/models.py:424
msgid "Sets the LLM output language"
msgstr "Nastaví výstupní jazyk LLM"
#: paperless/models.py:424
#: paperless/models.py:431
msgid "Sets the LLM timeout in seconds"
msgstr "Nastaví časový limit LLM v sekundách"
#: paperless/models.py:430
#: paperless/models.py:437
msgid "paperless application settings"
msgstr "nastavení aplikace paperless"
#: paperless/settings/__init__.py:558
#: paperless/settings/__init__.py:560
msgid "English (US)"
msgstr "Angličtina (US)"
#: paperless/settings/__init__.py:559
#: paperless/settings/__init__.py:561
msgid "Arabic"
msgstr "Arabština"
#: paperless/settings/__init__.py:560
#: paperless/settings/__init__.py:562
msgid "Afrikaans"
msgstr "Afrikánština"
#: paperless/settings/__init__.py:561
#: paperless/settings/__init__.py:563
msgid "Belarusian"
msgstr "Běloruština"
#: paperless/settings/__init__.py:562
#: paperless/settings/__init__.py:564
msgid "Bulgarian"
msgstr "Bulharština"
#: paperless/settings/__init__.py:563
#: paperless/settings/__init__.py:565
msgid "Catalan"
msgstr "Katalánština"
#: paperless/settings/__init__.py:564
#: paperless/settings/__init__.py:566
msgid "Czech"
msgstr "Čeština"
#: paperless/settings/__init__.py:565
#: paperless/settings/__init__.py:567
msgid "Danish"
msgstr "Dánština"
#: paperless/settings/__init__.py:566
#: paperless/settings/__init__.py:568
msgid "German"
msgstr "Němčina"
#: paperless/settings/__init__.py:567
#: paperless/settings/__init__.py:569
msgid "Greek"
msgstr "Řečtina"
#: paperless/settings/__init__.py:568
#: paperless/settings/__init__.py:570
msgid "English (GB)"
msgstr "Angličtina (GB)"
#: paperless/settings/__init__.py:569
#: paperless/settings/__init__.py:571
msgid "Spanish"
msgstr "Španělština"
#: paperless/settings/__init__.py:570
#: paperless/settings/__init__.py:572
msgid "Persian"
msgstr "Perština"
#: paperless/settings/__init__.py:571
#: paperless/settings/__init__.py:573
msgid "Finnish"
msgstr "Finština"
#: paperless/settings/__init__.py:572
#: paperless/settings/__init__.py:574
msgid "French"
msgstr "Francouzština"
#: paperless/settings/__init__.py:573
#: paperless/settings/__init__.py:575
msgid "Hungarian"
msgstr "Maďarština"
#: paperless/settings/__init__.py:574
#: paperless/settings/__init__.py:576
msgid "Indonesian"
msgstr "Indonéština"
#: paperless/settings/__init__.py:575
#: paperless/settings/__init__.py:577
msgid "Italian"
msgstr "Italština"
#: paperless/settings/__init__.py:576
#: paperless/settings/__init__.py:578
msgid "Japanese"
msgstr "Japonština"
#: paperless/settings/__init__.py:577
#: paperless/settings/__init__.py:579
msgid "Korean"
msgstr "Korejština"
#: paperless/settings/__init__.py:578
#: paperless/settings/__init__.py:580
msgid "Luxembourgish"
msgstr "Lucemburština"
#: paperless/settings/__init__.py:579
#: paperless/settings/__init__.py:581
msgid "Norwegian"
msgstr "Norština"
#: paperless/settings/__init__.py:580
#: paperless/settings/__init__.py:582
msgid "Dutch"
msgstr "Holandština"
#: paperless/settings/__init__.py:581
#: paperless/settings/__init__.py:583
msgid "Polish"
msgstr "Polština"
#: paperless/settings/__init__.py:582
#: paperless/settings/__init__.py:584
msgid "Portuguese (Brazil)"
msgstr "Portugalština (Brazílie)"
#: paperless/settings/__init__.py:583
#: paperless/settings/__init__.py:585
msgid "Portuguese"
msgstr "Portugalština"
#: paperless/settings/__init__.py:584
#: paperless/settings/__init__.py:586
msgid "Romanian"
msgstr "Rumunština"
#: paperless/settings/__init__.py:585
#: paperless/settings/__init__.py:587
msgid "Russian"
msgstr "Ruština"
#: paperless/settings/__init__.py:586
#: paperless/settings/__init__.py:588
msgid "Slovak"
msgstr "Slovenština"
#: paperless/settings/__init__.py:587
#: paperless/settings/__init__.py:589
msgid "Slovenian"
msgstr "Slovinština"
#: paperless/settings/__init__.py:588
#: paperless/settings/__init__.py:590
msgid "Serbian"
msgstr "Srbština"
#: paperless/settings/__init__.py:589
#: paperless/settings/__init__.py:591
msgid "Swedish"
msgstr "Švédština"
#: paperless/settings/__init__.py:590
#: paperless/settings/__init__.py:592
msgid "Turkish"
msgstr "Turečtina"
#: paperless/settings/__init__.py:591
#: paperless/settings/__init__.py:593
msgid "Ukrainian"
msgstr "Ukrajinština"
#: paperless/settings/__init__.py:592
#: paperless/settings/__init__.py:594
msgid "Vietnamese"
msgstr "Vietnamština"
#: paperless/settings/__init__.py:593
#: paperless/settings/__init__.py:595
msgid "Chinese Simplified"
msgstr "Čínština (zjednodušená)"
#: paperless/settings/__init__.py:594
#: paperless/settings/__init__.py:596
msgid "Chinese Traditional"
msgstr "Čínština (tradiční)"
#: paperless/urls.py:435
#: paperless/urls.py:438
msgid "Paperless-ngx administration"
msgstr "Administrace Paperless-ngx"

Some files were not shown because too many files have changed in this diff Show More