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

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