Compare commits

..
195 changed files with 36523 additions and 48827 deletions

No files matched your search

@@ -10,11 +10,9 @@ if [[ -n "${PAPERLESS_CONSUMER_DISABLE}" ]]; then
else else
cd ${PAPERLESS_SRC_DIR} cd ${PAPERLESS_SRC_DIR}
# Django system checks already ran once in init-system-checks, skip repeating them
if [[ -n "${USER_IS_NON_ROOT}" ]]; then if [[ -n "${USER_IS_NON_ROOT}" ]]; then
exec python3 manage.py document_consumer --skip-checks exec python3 manage.py document_consumer
else else
exec s6-setuidgid paperless python3 manage.py document_consumer --skip-checks exec s6-setuidgid paperless python3 manage.py document_consumer
fi fi
fi fi
@@ -3,9 +3,8 @@
cd ${PAPERLESS_SRC_DIR} cd ${PAPERLESS_SRC_DIR}
# Django system checks already ran once in init-system-checks, skip repeating them
if [[ -n "${USER_IS_NON_ROOT}" ]]; then if [[ -n "${USER_IS_NON_ROOT}" ]]; then
exec /usr/local/bin/celery --skip-checks --app paperless beat --loglevel INFO exec /usr/local/bin/celery --app paperless beat --loglevel INFO
else else
exec s6-setuidgid paperless /usr/local/bin/celery --skip-checks --app paperless beat --loglevel INFO exec s6-setuidgid paperless /usr/local/bin/celery --app paperless beat --loglevel INFO
fi fi
@@ -3,9 +3,8 @@
cd ${PAPERLESS_SRC_DIR} cd ${PAPERLESS_SRC_DIR}
# Django system checks already ran once in init-system-checks, skip repeating them
if [[ -n "${USER_IS_NON_ROOT}" ]]; then if [[ -n "${USER_IS_NON_ROOT}" ]]; then
exec /usr/local/bin/celery --skip-checks --app paperless worker --loglevel INFO --without-mingle --without-gossip exec /usr/local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip
else else
exec s6-setuidgid paperless /usr/local/bin/celery --skip-checks --app paperless worker --loglevel INFO --without-mingle --without-gossip exec s6-setuidgid paperless /usr/local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip
fi fi
-86
View File
@@ -1,91 +1,5 @@
# Changelog # Changelog
## paperless-ngx 3.4.0
### Features / Enhancements
- Enhancement: support custom fields in workflow title templates [@shamoon](https://github.com/shamoon) ([#14564](https://github.com/paperless-ngx/paperless-ngx/pull/14564))
- Enhancement: relative date custom field queries [@shamoon](https://github.com/shamoon) ([#14549](https://github.com/paperless-ngx/paperless-ngx/pull/14549))
- Performance: reduce tasks badge load times [@JanKleine](https://github.com/JanKleine) ([#14552](https://github.com/paperless-ngx/paperless-ngx/pull/14552))
- Enhancement: skip local 2FA for trusted OIDC AMR [@shamoon](https://github.com/shamoon) ([#14403](https://github.com/paperless-ngx/paperless-ngx/pull/14403))
- Feature: Generate PDF thumbnails via poppler utils [@stumpylog](https://github.com/stumpylog) ([#14420](https://github.com/paperless-ngx/paperless-ngx/pull/14420))
- Enhancement: add upload count to button, link to tray, reduce notifications [@shamoon](https://github.com/shamoon) ([#14501](https://github.com/paperless-ngx/paperless-ngx/pull/14501))
- Enhancement: notifications improvements [@shamoon](https://github.com/shamoon) ([#14500](https://github.com/paperless-ngx/paperless-ngx/pull/14500))
- Enhancement: doc details ux improvements [@shamoon](https://github.com/shamoon) ([#14485](https://github.com/paperless-ngx/paperless-ngx/pull/14485))
- Enhancement (QoL): allow independently toggling child + parent tags in WF tag fields [@shamoon](https://github.com/shamoon) ([#14400](https://github.com/paperless-ngx/paperless-ngx/pull/14400))
- Tweak: drop the user dropdown caret [@shamoon](https://github.com/shamoon) ([#14444](https://github.com/paperless-ngx/paperless-ngx/pull/14444))
- Enhancement: navbar upload button [@shamoon](https://github.com/shamoon) ([#14442](https://github.com/paperless-ngx/paperless-ngx/pull/14442))
- Enhancement: AI chat UX tweaks [@shamoon](https://github.com/shamoon) ([#14415](https://github.com/paperless-ngx/paperless-ngx/pull/14415))
- Enhancement: support markdown in AI chat [@shamoon](https://github.com/shamoon) ([#14409](https://github.com/paperless-ngx/paperless-ngx/pull/14409))
### Bug Fixes
- Fix: truncate long version labels in dropdown [@shamoon](https://github.com/shamoon) ([#14578](https://github.com/paperless-ngx/paperless-ngx/pull/14578))
- Fixhancement: show better result for revoked / expired tasks [@shamoon](https://github.com/shamoon) ([#14566](https://github.com/paperless-ngx/paperless-ngx/pull/14566))
- Fix: move re-index after workflow runs on doc edit [@shamoon](https://github.com/shamoon) ([#14558](https://github.com/paperless-ngx/paperless-ngx/pull/14558))
- Fix: folder export with --delete fails on a stale manifest.json.tmp [@benpetty](https://github.com/benpetty) ([#14526](https://github.com/paperless-ngx/paperless-ngx/pull/14526))
- Fix: fix list type for api schema document object filters [@shamoon](https://github.com/shamoon) ([#14524](https://github.com/paperless-ngx/paperless-ngx/pull/14524))
- Fix: standardize some dropdown sizing [@shamoon](https://github.com/shamoon) ([#14510](https://github.com/paperless-ngx/paperless-ngx/pull/14510))
- Fix: prevent doc permissions queries on schema view [@shamoon](https://github.com/shamoon) ([#14503](https://github.com/paperless-ngx/paperless-ngx/pull/14503))
- Fix: dont defer tag assignment when barcode will not result in a split [@shamoon](https://github.com/shamoon) ([#14497](https://github.com/paperless-ngx/paperless-ngx/pull/14497))
### Dependencies
<details>
<summary>6 changes</summary>
- Chore: bump some frontend deps for security fixes [@shamoon](https://github.com/shamoon) ([#14581](https://github.com/paperless-ngx/paperless-ngx/pull/14581))
- Chore: bump brotli, djangorestframework, filelock, msgpack, setuptools for security fixes [@stumpylog](https://github.com/stumpylog) ([#14575](https://github.com/paperless-ngx/paperless-ngx/pull/14575))
- Chore: bump django-allauth to 65.20 and oauthlib to 4.0 [@stumpylog](https://github.com/stumpylog) ([#14570](https://github.com/paperless-ngx/paperless-ngx/pull/14570))
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14517](https://github.com/paperless-ngx/paperless-ngx/pull/14517))
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14493](https://github.com/paperless-ngx/paperless-ngx/pull/14493))
- Chore: Drop ImageMagik as a dependency [@stumpylog](https://github.com/stumpylog) ([#14440](https://github.com/paperless-ngx/paperless-ngx/pull/14440))
</details>
### All App Changes
<details>
<summary>35 changes</summary>
- Chore: Consolidate test sample files [@stumpylog](https://github.com/stumpylog) ([#14430](https://github.com/paperless-ngx/paperless-ngx/pull/14430))
- Chore: bump some frontend deps for security fixes [@shamoon](https://github.com/shamoon) ([#14581](https://github.com/paperless-ngx/paperless-ngx/pull/14581))
- Enhancement: support custom fields in workflow title templates [@shamoon](https://github.com/shamoon) ([#14564](https://github.com/paperless-ngx/paperless-ngx/pull/14564))
- Enhancement: relative date custom field queries [@shamoon](https://github.com/shamoon) ([#14549](https://github.com/paperless-ngx/paperless-ngx/pull/14549))
- Fix: truncate long version labels in dropdown [@shamoon](https://github.com/shamoon) ([#14578](https://github.com/paperless-ngx/paperless-ngx/pull/14578))
- Chore: bump brotli, djangorestframework, filelock, msgpack, setuptools for security fixes [@stumpylog](https://github.com/stumpylog) ([#14575](https://github.com/paperless-ngx/paperless-ngx/pull/14575))
- Chore: bump django-allauth to 65.20 and oauthlib to 4.0 [@stumpylog](https://github.com/stumpylog) ([#14570](https://github.com/paperless-ngx/paperless-ngx/pull/14570))
- Fixhancement: show better result for revoked / expired tasks [@shamoon](https://github.com/shamoon) ([#14566](https://github.com/paperless-ngx/paperless-ngx/pull/14566))
- Fix: move re-index after workflow runs on doc edit [@shamoon](https://github.com/shamoon) ([#14558](https://github.com/paperless-ngx/paperless-ngx/pull/14558))
- Performance: reduce tasks badge load times [@JanKleine](https://github.com/JanKleine) ([#14552](https://github.com/paperless-ngx/paperless-ngx/pull/14552))
- Enhancement: skip local 2FA for trusted OIDC AMR [@shamoon](https://github.com/shamoon) ([#14403](https://github.com/paperless-ngx/paperless-ngx/pull/14403))
- Chore: consolidate direct permission grant lookups into one helper [@stumpylog](https://github.com/stumpylog) ([#14456](https://github.com/paperless-ngx/paperless-ngx/pull/14456))
- Chore: drop get\_groups\_with\_only\_permission [@stumpylog](https://github.com/stumpylog) ([#14454](https://github.com/paperless-ngx/paperless-ngx/pull/14454))
- Chore: Consolidate to a single way to set permission on an object [@stumpylog](https://github.com/stumpylog) ([#14453](https://github.com/paperless-ngx/paperless-ngx/pull/14453))
- Chore: Refactor bulk edit to remove PDF knowledge [@stumpylog](https://github.com/stumpylog) ([#14445](https://github.com/paperless-ngx/paperless-ngx/pull/14445))
- Fix: folder export with --delete fails on a stale manifest.json.tmp [@benpetty](https://github.com/benpetty) ([#14526](https://github.com/paperless-ngx/paperless-ngx/pull/14526))
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14517](https://github.com/paperless-ngx/paperless-ngx/pull/14517))
- Fix: fix list type for api schema document object filters [@shamoon](https://github.com/shamoon) ([#14524](https://github.com/paperless-ngx/paperless-ngx/pull/14524))
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14493](https://github.com/paperless-ngx/paperless-ngx/pull/14493))
- Chore: drop get\_objects\_for\_user\_owner\_aware [@stumpylog](https://github.com/stumpylog) ([#14457](https://github.com/paperless-ngx/paperless-ngx/pull/14457))
- Chore: update Angular components to 22.2.2 [@shamoon](https://github.com/shamoon) ([#14515](https://github.com/paperless-ngx/paperless-ngx/pull/14515))
- Chore: share one base class for the method-mapped permission classes [@stumpylog](https://github.com/stumpylog) ([#14458](https://github.com/paperless-ngx/paperless-ngx/pull/14458))
- Fix: standardize some dropdown sizing [@shamoon](https://github.com/shamoon) ([#14510](https://github.com/paperless-ngx/paperless-ngx/pull/14510))
- Chore: Drop ImageMagik as a dependency [@stumpylog](https://github.com/stumpylog) ([#14440](https://github.com/paperless-ngx/paperless-ngx/pull/14440))
- Feature: Generate PDF thumbnails via poppler utils [@stumpylog](https://github.com/stumpylog) ([#14420](https://github.com/paperless-ngx/paperless-ngx/pull/14420))
- Fix: prevent doc permissions queries on schema view [@shamoon](https://github.com/shamoon) ([#14503](https://github.com/paperless-ngx/paperless-ngx/pull/14503))
- Enhancement: add upload count to button, link to tray, reduce notifications [@shamoon](https://github.com/shamoon) ([#14501](https://github.com/paperless-ngx/paperless-ngx/pull/14501))
- Enhancement: notifications improvements [@shamoon](https://github.com/shamoon) ([#14500](https://github.com/paperless-ngx/paperless-ngx/pull/14500))
- Enhancement: doc details ux improvements [@shamoon](https://github.com/shamoon) ([#14485](https://github.com/paperless-ngx/paperless-ngx/pull/14485))
- Enhancement (QoL): allow independently toggling child + parent tags in WF tag fields [@shamoon](https://github.com/shamoon) ([#14400](https://github.com/paperless-ngx/paperless-ngx/pull/14400))
- Tweak: drop the user dropdown caret [@shamoon](https://github.com/shamoon) ([#14444](https://github.com/paperless-ngx/paperless-ngx/pull/14444))
- Enhancement: navbar upload button [@shamoon](https://github.com/shamoon) ([#14442](https://github.com/paperless-ngx/paperless-ngx/pull/14442))
- Enhancement: AI chat UX tweaks [@shamoon](https://github.com/shamoon) ([#14415](https://github.com/paperless-ngx/paperless-ngx/pull/14415))
- Enhancement: support markdown in AI chat [@shamoon](https://github.com/shamoon) ([#14409](https://github.com/paperless-ngx/paperless-ngx/pull/14409))
- Fix: dont defer tag assignment when barcode will not result in a split [@shamoon](https://github.com/shamoon) ([#14497](https://github.com/paperless-ngx/paperless-ngx/pull/14497))
</details>
## paperless-ngx 3.3.4 ## paperless-ngx 3.3.4
### All App Changes ### All App Changes
+3 -3
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "paperless-ngx" name = "paperless-ngx"
version = "3.4.0" version = "3.3.4"
description = """\ description = """\
A community-supported supercharged document management system: scan, index and archive all your physical documents\ A community-supported supercharged document management system: scan, index and archive all your physical documents\
""" """
@@ -256,8 +256,8 @@ isort.force-single-line = true
[tool.codespell] [tool.codespell]
ignore-words-list = "criterias,afterall,valeu,ureue,equest,ure,assertIn,Oktober,commitish,NIN,nin,reprot" ignore-words-list = "criterias,afterall,valeu,ureue,equest,ure,assertIn,Oktober,commitish,NIN,nin,reprot"
skip = """\ skip = """\
src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless/tests/samples/mail/*,src/documents/tests/samples\ src-ui/src/locale/*,src-ui/pnpm-lock.yaml,src-ui/e2e/*,src/paperless_mail/tests/samples/*,src/paperless/tests/samples\
/*,src/paperless_testing/sample_files/*,*.po,*.json\ /mail/*,src/documents/tests/samples/*,*.po,*.json\
""" """
write-changes = true write-changes = true
+2 -12
View File
@@ -43,7 +43,6 @@ def configure_environment(instance_root: Path) -> None:
def seed_database() -> None: def seed_database() -> None:
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.management import call_command from django.core.management import call_command
from django.utils import timezone from django.utils import timezone
@@ -64,18 +63,11 @@ def seed_database() -> None:
username="playwright", username="playwright",
password="playwright", # NOSONAR password="playwright", # NOSONAR
) )
viewer = User.objects.create_user( User.objects.create_user(
username="viewer", username="viewer",
password="viewer", # NOSONAR password="viewer", # NOSONAR
) )
viewer.user_permissions.add(
Permission.objects.get(
content_type__app_label="documents",
codename="view_uisettings",
),
)
inbox = Tag.objects.create(name="Inbox", is_inbox_tag=True, owner=admin) inbox = Tag.objects.create(name="Inbox", is_inbox_tag=True, owner=admin)
quick_filter = Tag.objects.create(name="Another Sample Tag", owner=admin) quick_filter = Tag.objects.create(name="Another Sample Tag", owner=admin)
Tag.objects.create(name="TagWithPartial", owner=admin) Tag.objects.create(name="TagWithPartial", owner=admin)
@@ -142,9 +134,7 @@ def seed_database() -> None:
originals.mkdir(parents=True) originals.mkdir(parents=True)
thumbnails = Path(os.environ["PAPERLESS_MEDIA_ROOT"]) / "documents" / "thumbnails" thumbnails = Path(os.environ["PAPERLESS_MEDIA_ROOT"]) / "documents" / "thumbnails"
thumbnails.mkdir(parents=True) thumbnails.mkdir(parents=True)
sample_pdf = ( sample_pdf = SOURCE_ROOT / "documents" / "tests" / "samples" / "simple.pdf"
SOURCE_ROOT / "paperless_testing" / "sample_files" / "simple-digital.pdf"
)
for document in documents: for document in documents:
shutil.copyfile(sample_pdf, originals / document.filename) shutil.copyfile(sample_pdf, originals / document.filename)
shutil.copyfile( shutil.copyfile(
@@ -80,21 +80,12 @@ test('should show a list of notes', async ({ page }) => {
test('should support quick filters', async ({ page }) => { test('should support quick filters', async ({ page }) => {
await page.goto('/documents/1/details') await page.goto('/documents/1/details')
// filter button is available before tags have loaded
const selectedTags = page.locator('pngx-input-tags .ng-value pngx-tag')
await expect(selectedTags).toHaveCount(2)
await expect(
selectedTags.getByText('Another Sample Tag', { exact: true })
).toBeVisible()
await expect(selectedTags.getByText('Inbox', { exact: true })).toBeVisible()
await page await page
.getByRole('button', { name: 'Filter documents with these Tags' }) .getByRole('button', { name: 'Filter documents with these Tags' })
.click() .click()
await expect(page).toHaveURL((url) => { await expect(page).toHaveURL(
const tags = url.searchParams.get('tags__id__all')?.split(',').sort() /tags__id__all=2,1&sort=created&reverse=1&page=1/
return url.pathname === '/documents' && tags?.join(',') === '1,2' )
})
await expect(page.locator('pngx-document-list')).toHaveText(/one document/i)
}) })
test('should finish reloading the preview after a remote document update', async ({ test('should finish reloading the preview after a remote document update', async ({
@@ -6,15 +6,8 @@ test.use({
}, },
}) })
test.beforeEach(async ({ page }) => {
const settingsLoaded = page.waitForResponse((response) =>
response.url().endsWith('/api/ui_settings/')
)
await page.goto('/dashboard')
expect((await settingsLoaded).status()).toBe(200)
})
test('should not allow user to edit settings', async ({ page }) => { test('should not allow user to edit settings', async ({ page }) => {
await page.goto('/dashboard')
await expect(page.getByRole('link', { name: 'Settings' })).not.toBeAttached() await expect(page.getByRole('link', { name: 'Settings' })).not.toBeAttached()
await page.goto('/settings') await page.goto('/settings')
await expect(page.locator('body')).toHaveText( await expect(page.locator('body')).toHaveText(
@@ -23,6 +16,7 @@ test('should not allow user to edit settings', async ({ page }) => {
}) })
test('should not allow user to view documents', async ({ page }) => { test('should not allow user to view documents', async ({ page }) => {
await page.goto('/dashboard')
await expect( await expect(
page.locator('nav').getByRole('link', { name: 'Documents' }) page.locator('nav').getByRole('link', { name: 'Documents' })
).not.toBeAttached() ).not.toBeAttached()
@@ -37,6 +31,7 @@ test('should not allow user to view documents', async ({ page }) => {
}) })
test('should not allow user to view correspondents', async ({ page }) => { test('should not allow user to view correspondents', async ({ page }) => {
await page.goto('/dashboard')
await expect( await expect(
page.getByRole('link', { name: 'Attributes' }) page.getByRole('link', { name: 'Attributes' })
).not.toBeAttached() ).not.toBeAttached()
@@ -47,6 +42,7 @@ test('should not allow user to view correspondents', async ({ page }) => {
}) })
test('should not allow user to view tags', async ({ page }) => { test('should not allow user to view tags', async ({ page }) => {
await page.goto('/dashboard')
await expect( await expect(
page.getByRole('link', { name: 'Attributes' }) page.getByRole('link', { name: 'Attributes' })
).not.toBeAttached() ).not.toBeAttached()
@@ -57,6 +53,7 @@ test('should not allow user to view tags', async ({ page }) => {
}) })
test('should not allow user to view document types', async ({ page }) => { test('should not allow user to view document types', async ({ page }) => {
await page.goto('/dashboard')
await expect( await expect(
page.getByRole('link', { name: 'Attributes' }) page.getByRole('link', { name: 'Attributes' })
).not.toBeAttached() ).not.toBeAttached()
@@ -67,6 +64,7 @@ test('should not allow user to view document types', async ({ page }) => {
}) })
test('should not allow user to view storage paths', async ({ page }) => { test('should not allow user to view storage paths', async ({ page }) => {
await page.goto('/dashboard')
await expect( await expect(
page.getByRole('link', { name: 'Attributes' }) page.getByRole('link', { name: 'Attributes' })
).not.toBeAttached() ).not.toBeAttached()
@@ -77,6 +75,7 @@ test('should not allow user to view storage paths', async ({ page }) => {
}) })
test('should not allow user to view logs', async ({ page }) => { test('should not allow user to view logs', async ({ page }) => {
await page.goto('/dashboard')
await expect(page.getByRole('link', { name: 'Logs' })).not.toBeAttached() await expect(page.getByRole('link', { name: 'Logs' })).not.toBeAttached()
await page.goto('/logs') await page.goto('/logs')
await expect(page.locator('body')).toHaveText( await expect(page.locator('body')).toHaveText(
@@ -85,6 +84,7 @@ test('should not allow user to view logs', async ({ page }) => {
}) })
test('should not allow user to view tasks', async ({ page }) => { test('should not allow user to view tasks', async ({ page }) => {
await page.goto('/dashboard')
await expect(page.getByRole('link', { name: 'Tasks' })).not.toBeAttached() await expect(page.getByRole('link', { name: 'Tasks' })).not.toBeAttached()
await page.goto('/tasks') await page.goto('/tasks')
await expect(page.locator('body')).toHaveText( await expect(page.locator('body')).toHaveText(
+5 -5
View File
@@ -10334,18 +10334,18 @@
<context context-type="linenumber">294,295</context> <context context-type="linenumber">294,295</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="3420321797707163677" datatype="html"> <trans-unit id="2179847500064178686" datatype="html">
<source>Preview document</source> <source>Edit document</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">328</context> <context context-type="linenumber">328</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2179847500064178686" datatype="html"> <trans-unit id="3420321797707163677" datatype="html">
<source>Edit document</source> <source>Preview document</source>
<context-group purpose="location"> <context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context> <context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">331</context> <context context-type="linenumber">329</context>
</context-group> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="4512084577073831437" datatype="html"> <trans-unit id="4512084577073831437" datatype="html">
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "paperless-ngx-ui", "name": "paperless-ngx-ui",
"version": "3.4.0", "version": "3.3.4",
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"ng": "ng", "ng": "ng",
+35 -228
View File
@@ -4,10 +4,6 @@ settings:
autoInstallPeers: false autoInstallPeers: false
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides:
vite@>=8.3.0 <8.3.3: ^8.3.3
zod@<4.6.6: ^4.6.6
importers: importers:
.: .:
@@ -1654,9 +1650,6 @@ packages:
'@oxc-project/types@0.150.0': '@oxc-project/types@0.150.0':
resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==}
'@oxc-project/types@0.155.0':
resolution: {integrity: sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==}
'@parcel/watcher-android-arm64@2.6.0': '@parcel/watcher-android-arm64@2.6.0':
resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==} resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
@@ -1755,85 +1748,42 @@ packages:
'@popperjs/core@2.11.8': '@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
'@rolldown/binding-android-arm-eabi@1.2.15':
resolution: {integrity: sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [android]
'@rolldown/binding-android-arm-eabi@1.2.8': '@rolldown/binding-android-arm-eabi@1.2.8':
resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==} resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm] cpu: [arm]
os: [android] os: [android]
'@rolldown/binding-android-arm64@1.2.15':
resolution: {integrity: sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@rolldown/binding-android-arm64@1.2.8': '@rolldown/binding-android-arm64@1.2.8':
resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==} resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
'@rolldown/binding-darwin-arm64@1.2.15':
resolution: {integrity: sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-arm64@1.2.8': '@rolldown/binding-darwin-arm64@1.2.8':
resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==} resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@rolldown/binding-darwin-x64@1.2.15':
resolution: {integrity: sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.2.8': '@rolldown/binding-darwin-x64@1.2.8':
resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==} resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@rolldown/binding-freebsd-x64@1.2.15':
resolution: {integrity: sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@rolldown/binding-freebsd-x64@1.2.8': '@rolldown/binding-freebsd-x64@1.2.8':
resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==} resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
'@rolldown/binding-linux-arm-gnueabihf@1.2.15':
resolution: {integrity: sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm-gnueabihf@1.2.8': '@rolldown/binding-linux-arm-gnueabihf@1.2.8':
resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==} resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
'@rolldown/binding-linux-arm64-gnu@1.2.15':
resolution: {integrity: sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-gnu@1.2.8': '@rolldown/binding-linux-arm64-gnu@1.2.8':
resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==} resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@@ -1841,13 +1791,6 @@ packages:
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@rolldown/binding-linux-arm64-musl@1.2.15':
resolution: {integrity: sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rolldown/binding-linux-arm64-musl@1.2.8': '@rolldown/binding-linux-arm64-musl@1.2.8':
resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==} resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@@ -1855,13 +1798,6 @@ packages:
os: [linux] os: [linux]
libc: [musl] libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.2.15':
resolution: {integrity: sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-ppc64-gnu@1.2.8': '@rolldown/binding-linux-ppc64-gnu@1.2.8':
resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==} resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@@ -1869,13 +1805,6 @@ packages:
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.2.15':
resolution: {integrity: sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.2.8': '@rolldown/binding-linux-s390x-gnu@1.2.8':
resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==} resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@@ -1883,13 +1812,6 @@ packages:
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.2.15':
resolution: {integrity: sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.2.8': '@rolldown/binding-linux-x64-gnu@1.2.8':
resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==} resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@@ -1897,13 +1819,6 @@ packages:
os: [linux] os: [linux]
libc: [glibc] libc: [glibc]
'@rolldown/binding-linux-x64-musl@1.2.15':
resolution: {integrity: sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@rolldown/binding-linux-x64-musl@1.2.8': '@rolldown/binding-linux-x64-musl@1.2.8':
resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==} resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@@ -1911,36 +1826,18 @@ packages:
os: [linux] os: [linux]
libc: [musl] libc: [musl]
'@rolldown/binding-openharmony-arm64@1.2.15':
resolution: {integrity: sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@rolldown/binding-openharmony-arm64@1.2.8': '@rolldown/binding-openharmony-arm64@1.2.8':
resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==} resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [openharmony] os: [openharmony]
'@rolldown/binding-win32-arm64-msvc@1.2.15':
resolution: {integrity: sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-arm64-msvc@1.2.8': '@rolldown/binding-win32-arm64-msvc@1.2.8':
resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==} resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@rolldown/binding-win32-x64-msvc@1.2.15':
resolution: {integrity: sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.2.8': '@rolldown/binding-win32-x64-msvc@1.2.8':
resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==} resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@@ -2235,7 +2132,7 @@ packages:
resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==} resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
peerDependencies: peerDependencies:
vite: ^8.3.3 vite: ^6.0.0 || ^7.0.0 || ^8.0.0
acorn-jsx@5.3.2: acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
@@ -2364,8 +2261,8 @@ packages:
peerDependencies: peerDependencies:
'@popperjs/core': ^2.11.8 '@popperjs/core': ^2.11.8
brace-expansion@2.1.7: brace-expansion@2.1.4:
resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
brace-expansion@5.0.12: brace-expansion@5.0.12:
resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==}
@@ -2700,8 +2597,8 @@ packages:
fast-string-width@3.0.2: fast-string-width@3.0.2:
resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==}
fast-uri@3.1.9: fast-uri@3.1.8:
resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==} resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==}
fast-wrap-ansi@0.2.2: fast-wrap-ansi@0.2.2:
resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==}
@@ -3132,8 +3029,8 @@ packages:
js-tokens@4.0.0: js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-yaml@3.15.2: js-yaml@3.15.1:
resolution: {integrity: sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==} resolution: {integrity: sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==}
hasBin: true hasBin: true
jsdom@26.1.0: jsdom@26.1.0:
@@ -3383,11 +3280,6 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
nanoid@3.3.20:
resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
napi-postinstall@0.3.4: napi-postinstall@0.3.4:
resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@@ -3618,10 +3510,6 @@ packages:
resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
postcss@8.5.31:
resolution: {integrity: sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==}
engines: {node: ^10 || ^12 || >=14}
prelude-ls@1.2.1: prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@@ -3713,11 +3601,6 @@ packages:
resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
engines: {node: '>=18'} engines: {node: '>=18'}
rolldown@1.2.15:
resolution: {integrity: sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
rolldown@1.2.8: rolldown@1.2.8:
resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==} resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@@ -3890,8 +3773,8 @@ packages:
resolution: {integrity: sha512-aBY19bn/XA+hKOKX0qL7Z0UfdBsbQvc9hn993U8ALUjzxCvDcuZqLoRXjGJrUARWAlwMnsRVD9sw2AFNOYvalA==} resolution: {integrity: sha512-aBY19bn/XA+hKOKX0qL7Z0UfdBsbQvc9hn993U8ALUjzxCvDcuZqLoRXjGJrUARWAlwMnsRVD9sw2AFNOYvalA==}
engines: {node: '>=22'} engines: {node: '>=22'}
source-map-js@1.2.2: source-map-js@1.2.1:
resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
source-map-support@0.5.21: source-map-support@0.5.21:
@@ -4122,13 +4005,13 @@ packages:
resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==}
engines: {node: '>=18.12.0'} engines: {node: '>=18.12.0'}
vite@8.3.5: vite@8.3.0:
resolution: {integrity: sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==} resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0 '@types/node': ^20.19.0 || >=22.12.0
'@vitejs/devtools': ^0.7.1 || ^0.8.0 '@vitejs/devtools': ^0.7.1
esbuild: ^0.27.0 || ^0.28.0 esbuild: ^0.27.0 || ^0.28.0
jiti: '>=1.21.0' jiti: '>=1.21.0'
less: ^4.0.0 less: ^4.0.0
@@ -4285,8 +4168,8 @@ packages:
resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==}
engines: {node: '>=18'} engines: {node: '>=18'}
zod@4.6.7: zod@4.6.5:
resolution: {integrity: sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==} resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==}
snapshots: snapshots:
@@ -4486,7 +4369,7 @@ snapshots:
'@babel/core': 8.0.5 '@babel/core': 8.0.5
'@inquirer/confirm': 6.3.2(@types/node@26.6.2) '@inquirer/confirm': 6.3.2(@types/node@26.6.2)
'@parcel/watcher': 2.6.0 '@parcel/watcher': 2.6.0
'@vitejs/plugin-basic-ssl': 2.3.0(vite@8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1)) '@vitejs/plugin-basic-ssl': 2.3.0(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1))
beasties: 0.5.4 beasties: 0.5.4
browserslist: 4.29.1 browserslist: 4.29.1
chokidar: 5.0.0 chokidar: 5.0.0
@@ -4508,7 +4391,7 @@ snapshots:
tinyglobby: 0.2.17 tinyglobby: 0.2.17
tslib: 2.8.1 tslib: 2.8.1
typescript: 6.0.3 typescript: 6.0.3
vite: 8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1) vite: 8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1)
xxhash-wasm: 1.1.0 xxhash-wasm: 1.1.0
optionalDependencies: optionalDependencies:
'@angular/core': 22.2.2(@angular/compiler@22.2.2)(rxjs@7.8.2) '@angular/core': 22.2.2(@angular/compiler@22.2.2)(rxjs@7.8.2)
@@ -4554,7 +4437,7 @@ snapshots:
parse5-html-rewriting-stream: 8.0.1 parse5-html-rewriting-stream: 8.0.1
semver: 7.8.5 semver: 7.8.5
yargs: 18.1.0 yargs: 18.1.0
zod: 4.6.7 zod: 4.6.5
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- chokidar - chokidar
@@ -4598,7 +4481,7 @@ snapshots:
'@standard-schema/spec': 1.1.0 '@standard-schema/spec': 1.1.0
rxjs: 7.8.2 rxjs: 7.8.2
tslib: 2.8.1 tslib: 2.8.1
zod: 4.6.7 zod: 4.6.5
'@angular/localize@22.2.2(@angular/compiler-cli@22.2.2(@angular/compiler@22.2.2)(typescript@6.0.3))(@angular/compiler@22.2.2)': '@angular/localize@22.2.2(@angular/compiler-cli@22.2.2(@angular/compiler@22.2.2)(typescript@6.0.3))(@angular/compiler@22.2.2)':
dependencies: dependencies:
@@ -5235,7 +5118,7 @@ snapshots:
camelcase: 5.3.1 camelcase: 5.3.1
find-up: 4.1.0 find-up: 4.1.0
get-package-type: 0.1.0 get-package-type: 0.1.0
js-yaml: 3.15.2 js-yaml: 3.15.1
resolve-from: 5.0.0 resolve-from: 5.0.0
'@istanbuljs/schema@0.1.6': {} '@istanbuljs/schema@0.1.6': {}
@@ -5529,12 +5412,12 @@ snapshots:
'@modelcontextprotocol/core@2.3.1': '@modelcontextprotocol/core@2.3.1':
dependencies: dependencies:
zod: 4.6.7 zod: 4.6.5
'@modelcontextprotocol/server@2.3.1': '@modelcontextprotocol/server@2.3.1':
dependencies: dependencies:
'@modelcontextprotocol/core': 2.3.1 '@modelcontextprotocol/core': 2.3.1
zod: 4.6.7 zod: 4.6.5
'@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4':
optional: true optional: true
@@ -5769,8 +5652,6 @@ snapshots:
'@oxc-project/types@0.150.0': {} '@oxc-project/types@0.150.0': {}
'@oxc-project/types@0.155.0': {}
'@parcel/watcher-android-arm64@2.6.0': '@parcel/watcher-android-arm64@2.6.0':
optional: true optional: true
@@ -5838,93 +5719,48 @@ snapshots:
'@popperjs/core@2.11.8': {} '@popperjs/core@2.11.8': {}
'@rolldown/binding-android-arm-eabi@1.2.15':
optional: true
'@rolldown/binding-android-arm-eabi@1.2.8': '@rolldown/binding-android-arm-eabi@1.2.8':
optional: true optional: true
'@rolldown/binding-android-arm64@1.2.15':
optional: true
'@rolldown/binding-android-arm64@1.2.8': '@rolldown/binding-android-arm64@1.2.8':
optional: true optional: true
'@rolldown/binding-darwin-arm64@1.2.15':
optional: true
'@rolldown/binding-darwin-arm64@1.2.8': '@rolldown/binding-darwin-arm64@1.2.8':
optional: true optional: true
'@rolldown/binding-darwin-x64@1.2.15':
optional: true
'@rolldown/binding-darwin-x64@1.2.8': '@rolldown/binding-darwin-x64@1.2.8':
optional: true optional: true
'@rolldown/binding-freebsd-x64@1.2.15':
optional: true
'@rolldown/binding-freebsd-x64@1.2.8': '@rolldown/binding-freebsd-x64@1.2.8':
optional: true optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.2.15':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.2.8': '@rolldown/binding-linux-arm-gnueabihf@1.2.8':
optional: true optional: true
'@rolldown/binding-linux-arm64-gnu@1.2.15':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.2.8': '@rolldown/binding-linux-arm64-gnu@1.2.8':
optional: true optional: true
'@rolldown/binding-linux-arm64-musl@1.2.15':
optional: true
'@rolldown/binding-linux-arm64-musl@1.2.8': '@rolldown/binding-linux-arm64-musl@1.2.8':
optional: true optional: true
'@rolldown/binding-linux-ppc64-gnu@1.2.15':
optional: true
'@rolldown/binding-linux-ppc64-gnu@1.2.8': '@rolldown/binding-linux-ppc64-gnu@1.2.8':
optional: true optional: true
'@rolldown/binding-linux-s390x-gnu@1.2.15':
optional: true
'@rolldown/binding-linux-s390x-gnu@1.2.8': '@rolldown/binding-linux-s390x-gnu@1.2.8':
optional: true optional: true
'@rolldown/binding-linux-x64-gnu@1.2.15':
optional: true
'@rolldown/binding-linux-x64-gnu@1.2.8': '@rolldown/binding-linux-x64-gnu@1.2.8':
optional: true optional: true
'@rolldown/binding-linux-x64-musl@1.2.15':
optional: true
'@rolldown/binding-linux-x64-musl@1.2.8': '@rolldown/binding-linux-x64-musl@1.2.8':
optional: true optional: true
'@rolldown/binding-openharmony-arm64@1.2.15':
optional: true
'@rolldown/binding-openharmony-arm64@1.2.8': '@rolldown/binding-openharmony-arm64@1.2.8':
optional: true optional: true
'@rolldown/binding-win32-arm64-msvc@1.2.15':
optional: true
'@rolldown/binding-win32-arm64-msvc@1.2.8': '@rolldown/binding-win32-arm64-msvc@1.2.8':
optional: true optional: true
'@rolldown/binding-win32-x64-msvc@1.2.15':
optional: true
'@rolldown/binding-win32-x64-msvc@1.2.8': '@rolldown/binding-win32-x64-msvc@1.2.8':
optional: true optional: true
@@ -6215,9 +6051,9 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.12.2': '@unrs/resolver-binding-win32-x64-msvc@1.12.2':
optional: true optional: true
'@vitejs/plugin-basic-ssl@2.3.0(vite@8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1))': '@vitejs/plugin-basic-ssl@2.3.0(vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1))':
dependencies: dependencies:
vite: 8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1) vite: 8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1)
acorn-jsx@5.3.2(acorn@8.18.0): acorn-jsx@5.3.2(acorn@8.18.0):
dependencies: dependencies:
@@ -6247,7 +6083,7 @@ snapshots:
ajv@8.20.0: ajv@8.20.0:
dependencies: dependencies:
fast-deep-equal: 3.1.3 fast-deep-equal: 3.1.3
fast-uri: 3.1.9 fast-uri: 3.1.8
json-schema-traverse: 1.0.0 json-schema-traverse: 1.0.0
require-from-string: 2.0.2 require-from-string: 2.0.2
@@ -6362,7 +6198,7 @@ snapshots:
dependencies: dependencies:
'@popperjs/core': 2.11.8 '@popperjs/core': 2.11.8
brace-expansion@2.1.7: brace-expansion@2.1.4:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
@@ -6707,7 +6543,7 @@ snapshots:
dependencies: dependencies:
fast-string-truncated-width: 3.0.3 fast-string-truncated-width: 3.0.3
fast-uri@3.1.9: {} fast-uri@3.1.8: {}
fast-wrap-ansi@0.2.2: fast-wrap-ansi@0.2.2:
dependencies: dependencies:
@@ -7357,7 +7193,7 @@ snapshots:
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
js-yaml@3.15.2: js-yaml@3.15.1:
dependencies: dependencies:
argparse: 1.0.10 argparse: 1.0.10
esprima: 4.0.1 esprima: 4.0.1
@@ -7557,7 +7393,7 @@ snapshots:
minimatch@9.0.9: minimatch@9.0.9:
dependencies: dependencies:
brace-expansion: 2.1.7 brace-expansion: 2.1.4
minimist@1.2.8: {} minimist@1.2.8: {}
@@ -7592,8 +7428,6 @@ snapshots:
nanoid@3.3.18: {} nanoid@3.3.18: {}
nanoid@3.3.20: {}
napi-postinstall@0.3.4: {} napi-postinstall@0.3.4: {}
natural-compare@1.4.0: {} natural-compare@1.4.0: {}
@@ -7840,13 +7674,7 @@ snapshots:
dependencies: dependencies:
nanoid: 3.3.18 nanoid: 3.3.18
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.2 source-map-js: 1.2.1
postcss@8.5.31:
dependencies:
nanoid: 3.3.20
picocolors: 1.1.1
source-map-js: 1.2.2
prelude-ls@1.2.1: {} prelude-ls@1.2.1: {}
@@ -7914,27 +7742,6 @@ snapshots:
onetime: 7.0.0 onetime: 7.0.0
signal-exit: 4.1.0 signal-exit: 4.1.0
rolldown@1.2.15:
dependencies:
'@oxc-project/types': 0.155.0
'@rolldown/pluginutils': 1.0.1
optionalDependencies:
'@rolldown/binding-android-arm-eabi': 1.2.15
'@rolldown/binding-android-arm64': 1.2.15
'@rolldown/binding-darwin-arm64': 1.2.15
'@rolldown/binding-darwin-x64': 1.2.15
'@rolldown/binding-freebsd-x64': 1.2.15
'@rolldown/binding-linux-arm-gnueabihf': 1.2.15
'@rolldown/binding-linux-arm64-gnu': 1.2.15
'@rolldown/binding-linux-arm64-musl': 1.2.15
'@rolldown/binding-linux-ppc64-gnu': 1.2.15
'@rolldown/binding-linux-s390x-gnu': 1.2.15
'@rolldown/binding-linux-x64-gnu': 1.2.15
'@rolldown/binding-linux-x64-musl': 1.2.15
'@rolldown/binding-openharmony-arm64': 1.2.15
'@rolldown/binding-win32-arm64-msvc': 1.2.15
'@rolldown/binding-win32-x64-msvc': 1.2.15
rolldown@1.2.8: rolldown@1.2.8:
dependencies: dependencies:
'@oxc-project/types': 0.149.0 '@oxc-project/types': 0.149.0
@@ -8055,7 +7862,7 @@ snapshots:
dependencies: dependencies:
chokidar: 5.0.0 chokidar: 5.0.0
immutable: 5.1.9 immutable: 5.1.9
source-map-js: 1.2.2 source-map-js: 1.2.1
optionalDependencies: optionalDependencies:
'@parcel/watcher': 2.6.0 '@parcel/watcher': 2.6.0
@@ -8084,7 +7891,7 @@ snapshots:
ansi-styles: 6.2.3 ansi-styles: 6.2.3
is-fullwidth-code-point: 5.1.0 is-fullwidth-code-point: 5.1.0
source-map-js@1.2.2: {} source-map-js@1.2.1: {}
source-map-support@0.5.21: source-map-support@0.5.21:
dependencies: dependencies:
@@ -8311,12 +8118,12 @@ snapshots:
verkit@0.3.2: {} verkit@0.3.2: {}
vite@8.3.5(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1): vite@8.3.0(@types/node@26.6.2)(esbuild@0.28.2)(jiti@2.7.0)(sass-embedded@1.104.1)(sass@1.104.1):
dependencies: dependencies:
lightningcss: 1.33.0 lightningcss: 1.33.0
picomatch: 4.0.7 picomatch: 4.0.7
postcss: 8.5.31 postcss: 8.5.28
rolldown: 1.2.15 rolldown: 1.2.8
tinyglobby: 0.2.17 tinyglobby: 0.2.17
optionalDependencies: optionalDependencies:
'@types/node': 26.6.2 '@types/node': 26.6.2
@@ -8434,4 +8241,4 @@ snapshots:
yoctocolors@2.2.0: {} yoctocolors@2.2.0: {}
zod@4.6.7: {} zod@4.6.5: {}
-3
View File
@@ -24,6 +24,3 @@ onlyBuiltDependencies:
ignoredBuiltDependencies: ignoredBuiltDependencies:
- normalize-diacritics - normalize-diacritics
- unrs-resolver - unrs-resolver
overrides:
"vite@>=8.3.0 <8.3.3": "^8.3.3"
"zod@<4.6.6": "^4.6.6"
@@ -1,3 +1,10 @@
.custom-fields-dropdown { .custom-fields-dropdown {
min-width: 300px; min-width: 300px;
// correct position on mobile
@media (max-width: 575.98px) {
&.show {
margin-left: -245px !important;
}
}
} }
@@ -308,7 +308,7 @@
</tr> </tr>
<tr class="align-middle"> <tr class="align-middle">
<td i18n>Original SHA256 checksum</td> <td i18n>Original SHA256 checksum</td>
<td><span class="badge bg-light text-dark font-monospace text-wrap text-break text-start">{{metadata()?.original_checksum}}</span></td> <td><span class="badge bg-light font-monospace text-wrap text-break text-start">{{metadata()?.original_checksum}}</span></td>
</tr> </tr>
<tr> <tr>
<td i18n>Original file size</td> <td i18n>Original file size</td>
@@ -465,7 +465,7 @@
@switch (archiveContentRenderType) { @switch (archiveContentRenderType) {
@case (ContentRenderType.PDF) { @case (ContentRenderType.PDF) {
@if (!useNativePdfViewer) { @if (!useNativePdfViewer) {
<div class="preview-sticky pdf-viewer-container" tabindex="-1"> <div class="preview-sticky pdf-viewer-container">
<pngx-pdf-viewer <pngx-pdf-viewer
[src]="pdfSource()" [src]="pdfSource()"
[sourceRevision]="previewRevision()" [sourceRevision]="previewRevision()"
@@ -477,7 +477,7 @@
(loadError)="onError($event)" (loadError)="onError($event)"
(afterLoadComplete)="pdfPreviewLoaded($event)"> (afterLoadComplete)="pdfPreviewLoaded($event)">
</pngx-pdf-viewer> </pngx-pdf-viewer>
<div class="preview-controls position-absolute bottom-0 start-50 translate-middle-x z-1 mb-3 d-flex gap-2 p-1 bg-body border rounded shadow"> <div class="position-absolute bottom-0 start-50 translate-middle-x z-1 mb-3 d-flex gap-2 p-1 bg-body border rounded shadow">
@if (previewNumPages()) { @if (previewNumPages()) {
<div class="input-group input-group-sm w-auto flex-nowrap"> <div class="input-group input-group-sm w-auto flex-nowrap">
<input class="form-control flex-grow-0 w-auto" type="number" min="1" [max]="previewNumPages()" [ngModelOptions]="{standalone: true}" [ngModel]="previewCurrentPage()" (ngModelChange)="previewCurrentPage.set($event)" aria-label="Page" i18n-aria-label /> <input class="form-control flex-grow-0 w-auto" type="number" min="1" [max]="previewNumPages()" [ngModelOptions]="{standalone: true}" [ngModel]="previewCurrentPage()" (ngModelChange)="previewCurrentPage.set($event)" aria-label="Page" i18n-aria-label />
@@ -12,22 +12,6 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
&:focus {
outline: none;
}
.preview-controls {
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease-in-out;
}
&:hover .preview-controls,
&:focus-within .preview-controls {
opacity: 1;
pointer-events: auto;
}
} }
.btn-group .dropdown-toggle-split { .btn-group .dropdown-toggle-split {
@@ -72,7 +72,7 @@
<a class="btn btn-sm btn-outline-secondary" (click)="clickMoreLike.emit()"> <a class="btn btn-sm btn-outline-secondary" (click)="clickMoreLike.emit()">
<i-bs name="diagram-3" class="me-1"></i-bs><span class="d-none d-md-inline" i18n>More like this</span> <i-bs name="diagram-3" class="me-1"></i-bs><span class="d-none d-md-inline" i18n>More like this</span>
</a> </a>
<a routerLink="/documents/{{document().id}}" class="btn btn-sm btn-outline-secondary"> <a routerLink="/documents/{{document().id}}" class="btn btn-sm btn-outline-secondary" *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }">
<i-bs name="file-earmark-richtext" class="me-1"></i-bs><span class="d-none d-md-inline" i18n>Open</span> <i-bs name="file-earmark-richtext" class="me-1"></i-bs><span class="d-none d-md-inline" i18n>Open</span>
</a> </a>
<pngx-preview-popup [document]="document()" #popupPreview> <pngx-preview-popup [document]="document()" #popupPreview>
@@ -10,6 +10,7 @@ import {
NgbTooltipModule, NgbTooltipModule,
} from '@ng-bootstrap/ng-bootstrap' } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
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'
import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe' import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe'
import { IsNumberPipe } from 'src/app/pipes/is-number.pipe' import { IsNumberPipe } from 'src/app/pipes/is-number.pipe'
@@ -51,6 +52,7 @@ describe('DocumentCardLargeComponent', () => {
DocumentCardLargeComponent, DocumentCardLargeComponent,
DocumentTitlePipe, DocumentTitlePipe,
CustomDatePipe, CustomDatePipe,
IfPermissionsDirective,
SafeUrlPipe, SafeUrlPipe,
IsNumberPipe, IsNumberPipe,
PreviewPopupComponent, PreviewPopupComponent,
@@ -20,6 +20,7 @@ import {
Document, Document,
} from 'src/app/data/document' } from 'src/app/data/document'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings' import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe' import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe'
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'
@@ -50,6 +51,7 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
CorrespondentNamePipe, CorrespondentNamePipe,
DocumentTypeNamePipe, DocumentTypeNamePipe,
StoragePathNamePipe, StoragePathNamePipe,
IfPermissionsDirective,
CustomDatePipe, CustomDatePipe,
RouterModule, RouterModule,
NgbTooltipModule, NgbTooltipModule,
@@ -144,7 +144,7 @@
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<div class="btn-group w-100"> <div class="btn-group w-100">
@if (document()) { @if (document()) {
<a routerLink="/documents/{{document().id}}" class="btn btn-sm btn-outline-secondary" title="Open" i18n-title> <a routerLink="/documents/{{document().id}}" class="btn btn-sm btn-outline-secondary" title="Open" i18n-title *pngxIfPermissions="{ action: PermissionAction.Change, type: PermissionType.Document }" i18n-title>
<i-bs name="file-earmark-richtext"></i-bs> <i-bs name="file-earmark-richtext"></i-bs>
</a> </a>
<pngx-preview-popup [document]="document()" #popupPreview> <pngx-preview-popup [document]="document()" #popupPreview>
@@ -20,6 +20,7 @@ import {
Document, Document,
} from 'src/app/data/document' } from 'src/app/data/document'
import { SETTINGS_KEYS } from 'src/app/data/ui-settings' import { SETTINGS_KEYS } from 'src/app/data/ui-settings'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe' import { CorrespondentNamePipe } from 'src/app/pipes/correspondent-name.pipe'
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'
@@ -50,6 +51,7 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
CorrespondentNamePipe, CorrespondentNamePipe,
DocumentTypeNamePipe, DocumentTypeNamePipe,
StoragePathNamePipe, StoragePathNamePipe,
IfPermissionsDirective,
CustomDatePipe, CustomDatePipe,
RouterModule, RouterModule,
NgbTooltipModule, NgbTooltipModule,
@@ -208,7 +208,7 @@
i18n>Correspondent</th> i18n>Correspondent</th>
} }
@if (activeDisplayFields.includes(DisplayField.TITLE)) { @if (activeDisplayFields.includes(DisplayField.TITLE)) {
<th class="cursor-pointer ps-5" <th class="cursor-pointer"
pngxSortable="title" pngxSortable="title"
title="Sort by title" i18n-title title="Sort by title" i18n-title
[currentSortField]="list.sortField" [currentSortField]="list.sortField"
@@ -322,13 +322,13 @@
</td> </td>
} }
@if (activeDisplayFields.includes(DisplayField.TITLE) || activeDisplayFields.includes(DisplayField.TAGS)) { @if (activeDisplayFields.includes(DisplayField.TITLE) || activeDisplayFields.includes(DisplayField.TAGS)) {
<td width="30%" class="position-relative" [class.ps-5]="activeDisplayFields.includes(DisplayField.TITLE)"> <td width="30%">
@if (activeDisplayFields.includes(DisplayField.TITLE)) { @if (activeDisplayFields.includes(DisplayField.TITLE)) {
<div class="d-inline-block" (mouseleave)="popupPreview.close()"> <div class="d-inline-block" (mouseleave)="popupPreview.close()">
<pngx-preview-popup class="position-absolute start-0" [document]="d" linkClasses="btn btn-sm btn-link text-secondary" linkTitle="Preview document" (click)="$event.stopPropagation()" i18n-linkTitle #popupPreview> <a routerLink="/documents/{{d.id}}" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a>
<pngx-preview-popup [document]="d" linkClasses="btn btn-sm btn-link text-secondary" linkTitle="Preview document" (click)="$event.stopPropagation()" i18n-linkTitle #popupPreview>
<i-bs name="eye"></i-bs> <i-bs name="eye"></i-bs>
</pngx-preview-popup> </pngx-preview-popup>
<a routerLink="/documents/{{d.id}}" title="Edit document" i18n-title style="overflow-wrap: anywhere;">{{d.title | documentTitle}}</a>
</div> </div>
} }
@if (activeDisplayFields.includes(DisplayField.TAGS)) { @if (activeDisplayFields.includes(DisplayField.TAGS)) {
@@ -87,10 +87,6 @@ $paperless-card-breakpoints: (
} }
} }
.table .ps-5 {
padding-left: 2.2rem !important;
}
a { a {
cursor: pointer; cursor: pointer;
} }
+1 -1
View File
@@ -8,7 +8,7 @@ export const environment = {
apiVersion: '10', // match src/paperless/settings.py apiVersion: '10', // match src/paperless/settings.py
appTitle: DEFAULT_APP_TITLE, appTitle: DEFAULT_APP_TITLE,
tag: 'prod', tag: 'prod',
version: '3.4.0', version: '3.3.4',
webSocketHost: window.location.host, webSocketHost: window.location.host,
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:', webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
webSocketBaseUrl: base_url.pathname + 'ws/', webSocketBaseUrl: base_url.pathname + 'ws/',
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
-38
View File
@@ -11,8 +11,6 @@ from typing import TYPE_CHECKING
import pytest import pytest
from paperless_testing import samples
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Generator from collections.abc import Generator
from pathlib import Path from pathlib import Path
@@ -151,39 +149,3 @@ def fake_progress_manager(
monkeypatch.setattr("documents.tasks.ProgressManager", FakeProgressManager) monkeypatch.setattr("documents.tasks.ProgressManager", FakeProgressManager)
return FakeProgressManager return FakeProgressManager
@pytest.fixture(scope="session")
def shared_samples_dir() -> Path:
"""Directory of sample files used by more than one app's tests."""
return samples.SHARED_SAMPLES_DIR
@pytest.fixture(scope="session")
def simple_digital_pdf_file() -> Path:
"""One-page PDF with a text layer."""
return samples.SIMPLE_DIGITAL_PDF
@pytest.fixture(scope="session")
def multi_page_digital_pdf_file() -> Path:
"""Three-page PDF with a text layer."""
return samples.MULTI_PAGE_DIGITAL_PDF
@pytest.fixture(scope="session")
def with_form_pdf_file() -> Path:
"""PDF containing a fillable form."""
return samples.WITH_FORM_PDF
@pytest.fixture(scope="session")
def multi_page_images_pdf_file() -> Path:
"""Multi-page PDF of scanned images, no text layer."""
return samples.MULTI_PAGE_IMAGES_PDF
@pytest.fixture(scope="session")
def thumbnail_webp_file() -> Path:
"""Small WebP thumbnail."""
return samples.THUMBNAIL_WEBP
+3 -7
View File
@@ -10,6 +10,9 @@ from typing import TYPE_CHECKING
import regex as regex_mod import regex as regex_mod
from django.conf import settings from django.conf import settings
from pdf2image import convert_from_path from pdf2image import convert_from_path
from pikepdf import Page
from pikepdf import PasswordError
from pikepdf import Pdf
from documents.converters import convert_from_tiff_to_pdf from documents.converters import convert_from_tiff_to_pdf
from documents.data_models import ConsumableDocument from documents.data_models import ConsumableDocument
@@ -31,7 +34,6 @@ from documents.utils import maybe_override_pixel_limit
from paperless.config import BarcodeConfig from paperless.config import BarcodeConfig
if TYPE_CHECKING: if TYPE_CHECKING:
from pikepdf import Page
from PIL import Image from PIL import Image
logger = logging.getLogger("paperless.barcodes") logger = logging.getLogger("paperless.barcodes")
@@ -280,8 +282,6 @@ class BarcodePlugin(ConsumeTaskPlugin):
# No op if not a TIFF # No op if not a TIFF
self.convert_from_tiff_to_pdf() self.convert_from_tiff_to_pdf()
from pikepdf import PasswordError
try: try:
self.barcodes = scan_pdf( self.barcodes = scan_pdf(
self.pdf_file, self.pdf_file,
@@ -433,8 +433,6 @@ class BarcodePlugin(ConsumeTaskPlugin):
document_paths = [] document_paths = []
fname: str = self.input_doc.original_file.stem fname: str = self.input_doc.original_file.stem
from pikepdf import Pdf
with Pdf.open(self.pdf_file) as input_pdf: with Pdf.open(self.pdf_file) as input_pdf:
# Start with an empty document # Start with an empty document
current_document: list[Page] = [] current_document: list[Page] = []
@@ -518,8 +516,6 @@ def scan_pdf(pdf_path: Path, settings: BarcodeConfig, work_dir: Path) -> list[Ba
""" """
barcodes: list[Barcode] = [] barcodes: list[Barcode] = []
from pikepdf import Pdf
with Pdf.open(pdf_path) as pdf: with Pdf.open(pdf_path) as pdf:
num_of_pages = len(pdf.pages) num_of_pages = len(pdf.pages)
logger.debug(f"PDF has {num_of_pages} pages") logger.debug(f"PDF has {num_of_pages} pages")
+1 -2
View File
@@ -1,5 +1,6 @@
from pathlib import Path from pathlib import Path
import img2pdf
from PIL import Image from PIL import Image
from documents.utils import copy_basic_file_stats from documents.utils import copy_basic_file_stats
@@ -16,8 +17,6 @@ def convert_from_tiff_to_pdf(tiff_path: Path, target_directory: Path) -> Path:
Returns the path of the PDF created. Returns the path of the PDF created.
""" """
import img2pdf
# override pixel setting if needed # override pixel setting if needed
maybe_override_pixel_limit() maybe_override_pixel_limit()
+1 -2
View File
@@ -6,6 +6,7 @@ from pathlib import Path
from typing import Final from typing import Final
from django.conf import settings from django.conf import settings
from pikepdf import Pdf
from documents.consumer import ConsumerError from documents.consumer import ConsumerError
from documents.converters import convert_from_tiff_to_pdf from documents.converters import convert_from_tiff_to_pdf
@@ -84,8 +85,6 @@ class CollatePlugin(NoCleanupPluginMixin, NoSetupPluginMixin, ConsumeTaskPlugin)
valid_staging_exists = True valid_staging_exists = True
if valid_staging_exists: if valid_staging_exists:
from pikepdf import Pdf
try: try:
# Collate pages from second PDF in reverse order # Collate pages from second PDF in reverse order
with Pdf.open(staging) as pdf1, Pdf.open(pdf_file) as pdf2: with Pdf.open(staging) as pdf1, Pdf.open(pdf_file) as pdf2:
+1 -2
View File
@@ -11,6 +11,7 @@ from decimal import InvalidOperation
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Any from typing import Any
import dateparser
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Case from django.db.models import Case
@@ -349,8 +350,6 @@ class DateQueryField(serializers.DateField):
if not isinstance(value, str): if not isinstance(value, str):
raise raise
import dateparser
try: try:
parsed = dateparser.parse( parsed = dateparser.parse(
value, value,
+2 -14
View File
@@ -14,6 +14,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import NamedTuple from typing import NamedTuple
import pikepdf
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Callable from collections.abc import Callable
from collections.abc import Iterable from collections.abc import Iterable
@@ -41,8 +43,6 @@ def rotate_pdf(src: Path, dst: Path, degrees: int) -> None:
Rotate every page relatively on the opened document, not a rebuild, so Info, Rotate every page relatively on the opened document, not a rebuild, so Info,
XMP and outlines are kept. ``src`` is not modified. XMP and outlines are kept. ``src`` is not modified.
""" """
import pikepdf
with pikepdf.open(src) as pdf: with pikepdf.open(src) as pdf:
for page in pdf.pages: for page in pdf.pages:
page.rotate(degrees, relative=True) page.rotate(degrees, relative=True)
@@ -58,8 +58,6 @@ def remove_pages(src: Path, dst: Path, pages: Iterable[int]) -> None:
Duplicates are ignored. Pages are removed highest first so earlier removals Duplicates are ignored. Pages are removed highest first so earlier removals
never shift the index of later ones. never shift the index of later ones.
""" """
import pikepdf
unique = sorted(set(pages)) unique = sorted(set(pages))
_require_positive(unique) _require_positive(unique)
with pikepdf.open(src) as pdf: with pikepdf.open(src) as pdf:
@@ -83,8 +81,6 @@ def build_pdfs(
(Info, XMP, outlines) is not carried over. Returns the written paths in output (Info, XMP, outlines) is not carried over. Returns the written paths in output
order. order.
""" """
import pikepdf
for specs, _ in outputs: for specs, _ in outputs:
_require_positive(spec.page for spec in specs) _require_positive(spec.page for spec in specs)
@@ -144,8 +140,6 @@ def needs_decrypt(src: Path) -> bool:
True if ``src`` is encrypted. A PDF that needs a password to open at all True if ``src`` is encrypted. A PDF that needs a password to open at all
counts as encrypted. counts as encrypted.
""" """
import pikepdf
try: try:
with pikepdf.open(src) as pdf: with pikepdf.open(src) as pdf:
return bool(pdf.is_encrypted) return bool(pdf.is_encrypted)
@@ -160,8 +154,6 @@ def decrypt_pdf(src: Path, make_dst: Callable[[], Path], password: str) -> Path:
``make_dst`` is only called once the password has been accepted, so a wrong ``make_dst`` is only called once the password has been accepted, so a wrong
password never leaves a destination behind. password never leaves a destination behind.
""" """
import pikepdf
with pikepdf.open(src, password=password) as pdf: with pikepdf.open(src, password=password) as pdf:
pdf.remove_unreferenced_resources() pdf.remove_unreferenced_resources()
dst = make_dst() dst = make_dst()
@@ -178,8 +170,6 @@ class PdfMerger:
""" """
def __init__(self) -> None: def __init__(self) -> None:
import pikepdf
self._pdf = pikepdf.new() self._pdf = pikepdf.new()
self._version: str = self._pdf.pdf_version self._version: str = self._pdf.pdf_version
@@ -195,8 +185,6 @@ class PdfMerger:
self._pdf.close() self._pdf.close()
def add(self, path: Path) -> None: def add(self, path: Path) -> None:
import pikepdf
with pikepdf.open(str(path)) as pdf: with pikepdf.open(str(path)) as pdf:
self._version = max(self._version, pdf.pdf_version) self._version = max(self._version, pdf.pdf_version)
self._pdf.pages.extend(pdf.pages) self._pdf.pages.extend(pdf.pages)
+2 -2
View File
@@ -7,6 +7,8 @@ from dataclasses import dataclass
from types import TracebackType from types import TracebackType
from typing import Self from typing import Self
import dateparser
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -78,8 +80,6 @@ class DateParserPluginBase(ABC):
Uses configuration from `self.config`. Uses configuration from `self.config`.
""" """
import dateparser
try: try:
return dateparser.parse( return dateparser.parse(
date_string, date_string,
+6 -6
View File
@@ -476,13 +476,13 @@ def update_filename_and_move_files(
return return
with FileLock(settings.MEDIA_LOCK): with FileLock(settings.MEDIA_LOCK):
try: # If this was waiting for the lock, the filename or archive_filename
# If this was waiting for the lock, the filename or archive_filename # of this document may have been updated. This happens if multiple updates
# of this document may have been updated. This happens if multiple updates # get queued from the UI for the same document
# get queued from the UI for the same document # So freshen up the data before doing anything
# So freshen up the data before doing anything instance.refresh_from_db()
instance.refresh_from_db()
try:
old_filename = instance.filename old_filename = instance.filename
old_source_path = instance.source_path old_source_path = instance.source_path
move_original = False move_original = False
+3 -8
View File
@@ -76,6 +76,9 @@ from paperless.logging import consume_task_id
from paperless.parsers import ParserContext from paperless.parsers import ParserContext
from paperless.parsers.registry import get_parser_registry from paperless.parsers.registry import get_parser_registry
from paperless_ai.exceptions import LLMTimeoutError from paperless_ai.exceptions import LLMTimeoutError
from paperless_ai.indexing import llm_index_add_or_update_document
from paperless_ai.indexing import llm_index_remove_document
from paperless_ai.indexing import update_llm_index
if settings.AUDIT_LOG_ENABLED: if settings.AUDIT_LOG_ENABLED:
from auditlog.models import LogEntry from auditlog.models import LogEntry
@@ -336,8 +339,6 @@ def bulk_update_documents(document_ids) -> None:
ai_config = AIConfig() ai_config = AIConfig()
if ai_config.llm_index_enabled: if ai_config.llm_index_enabled:
from paperless_ai.indexing import update_llm_index
update_llm_index( update_llm_index(
rebuild=False, rebuild=False,
document_ids=document_ids, document_ids=document_ids,
@@ -498,8 +499,6 @@ def update_document_content_maybe_archive_file(
ai_config = AIConfig() ai_config = AIConfig()
if ai_config.llm_index_enabled: if ai_config.llm_index_enabled:
from paperless_ai.indexing import llm_index_add_or_update_document
llm_index_add_or_update_document(document) llm_index_add_or_update_document(document)
clear_document_caches(document.pk) clear_document_caches(document.pk)
@@ -801,15 +800,11 @@ def apply_ai_suggestions(self, action_id: int, document_id: int) -> None:
@shared_task @shared_task
def update_document_in_llm_index(document) -> None: def update_document_in_llm_index(document) -> None:
from paperless_ai.indexing import llm_index_add_or_update_document
llm_index_add_or_update_document(document) llm_index_add_or_update_document(document)
@shared_task @shared_task
def remove_document_from_llm_index(document) -> None: def remove_document_from_llm_index(document) -> None:
from paperless_ai.indexing import llm_index_remove_document
llm_index_remove_document(document) llm_index_remove_document(document)
+4 -1
View File
@@ -44,7 +44,10 @@ def localize_date(value: date | datetime | str, format: str, locale: str) -> str
def format_datetime(value: str | datetime, format: str) -> str: def format_datetime(value: str | datetime, format: str) -> str:
if isinstance(value, str): if isinstance(value, str):
value = parse_date(value) parsed = parse_datetime(value) or parse_date(value)
if parsed is None:
raise ValueError(f"Invalid date or datetime string: {value!r}")
return parsed.strftime(format=format)
return value.strftime(format=format) return value.strftime(format=format)
+10 -6
View File
@@ -12,25 +12,29 @@ if TYPE_CHECKING:
from paperless_testing.dirs import PaperlessDirs from paperless_testing.dirs import PaperlessDirs
@pytest.fixture(scope="session")
def document_samples_dir() -> Path:
"""Path to the shared test sample documents."""
return Path(__file__).parent / "samples" / "documents"
@pytest.fixture() @pytest.fixture()
def sample_doc( def sample_doc(
paperless_dirs: "PaperlessDirs", paperless_dirs: "PaperlessDirs",
simple_digital_pdf_file: Path, document_samples_dir: Path,
multi_page_images_pdf_file: Path,
thumbnail_webp_file: Path,
) -> "Document": ) -> "Document":
"""Create a document with valid files and matching checksums.""" """Create a document with valid files and matching checksums."""
with filelock.FileLock(paperless_dirs.media_lock): with filelock.FileLock(paperless_dirs.media_lock):
shutil.copy( shutil.copy(
simple_digital_pdf_file, document_samples_dir / "originals" / "0000001.pdf",
paperless_dirs.originals_dir / "0000001.pdf", paperless_dirs.originals_dir / "0000001.pdf",
) )
shutil.copy( shutil.copy(
multi_page_images_pdf_file, document_samples_dir / "archive" / "0000001.pdf",
paperless_dirs.archive_dir / "0000001.pdf", paperless_dirs.archive_dir / "0000001.pdf",
) )
shutil.copy( shutil.copy(
thumbnail_webp_file, document_samples_dir / "thumbnails" / "0000001.webp",
paperless_dirs.thumbnail_dir / "0000001.webp", paperless_dirs.thumbnail_dir / "0000001.webp",
) )
@@ -71,7 +71,7 @@ class TestParseString:
): ):
# We still need to mock dateparser.parse to force the exception # We still need to mock dateparser.parse to force the exception
mocker.patch( mocker.patch(
"dateparser.parse", "documents.plugins.date_parsing.base.dateparser.parse",
side_effect=ValueError( side_effect=ValueError(
"Parsing error: 01/01/2024", "Parsing error: 01/01/2024",
), ),
@@ -260,7 +260,7 @@ class TestRegexDateParser:
parser = RegexDateParserPlugin(base_config) parser = RegexDateParserPlugin(base_config)
# Patch the dateparser.parse # Patch the dateparser.parse
target = "dateparser.parse" target = "documents.plugins.date_parsing.base.dateparser.parse"
def fake_parse( def fake_parse(
date_string: str, date_string: str,
@@ -335,7 +335,7 @@ class TestRegexDateParser:
) )
parser = RegexDateParserPlugin(cfg) parser = RegexDateParserPlugin(cfg)
target = "dateparser.parse" target = "documents.plugins.date_parsing.base.dateparser.parse"
def fake_parse( def fake_parse(
date_string: str, date_string: str,
@@ -400,7 +400,7 @@ class TestRegexDateParser:
parser = RegexDateParserPlugin(cfg) parser = RegexDateParserPlugin(cfg)
# Patch the module's dateparser.parse so we can inspect calls # Patch the module's dateparser.parse so we can inspect calls
target = "dateparser.parse" target = "documents.plugins.date_parsing.base.dateparser.parse"
def fake_parse( def fake_parse(
date_string: str, date_string: str,
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.
+1
View File
@@ -0,0 +1 @@
This is a test file.
+5 -5
View File
@@ -1070,7 +1070,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with ( with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update, patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists, patch("paperless.views.llm_index_exists") as mock_exists,
): ):
mock_exists.return_value = False mock_exists.return_value = False
self.client.patch( self.client.patch(
@@ -1095,7 +1095,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with ( with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update, patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless_ai.indexing.llm_index_exists", return_value=False), patch("paperless.views.llm_index_exists", return_value=False),
): ):
self.client.patch( self.client.patch(
f"{self.ENDPOINT}1/", f"{self.ENDPOINT}1/",
@@ -1115,7 +1115,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with ( with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update, patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists, patch("paperless.views.llm_index_exists") as mock_exists,
): ):
mock_exists.return_value = True mock_exists.return_value = True
self.client.patch( self.client.patch(
@@ -1136,7 +1136,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with ( with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update, patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists, patch("paperless.views.llm_index_exists") as mock_exists,
): ):
mock_exists.return_value = True mock_exists.return_value = True
self.client.patch( self.client.patch(
@@ -1174,7 +1174,7 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
with ( with (
patch("documents.tasks.llmindex_index.apply_async") as mock_update, patch("documents.tasks.llmindex_index.apply_async") as mock_update,
patch("paperless_ai.indexing.llm_index_exists") as mock_exists, patch("paperless.views.llm_index_exists") as mock_exists,
): ):
mock_exists.return_value = True mock_exists.return_value = True
self.client.patch( self.client.patch(
@@ -53,7 +53,7 @@ class TestBulkDownload(DirectoriesMixin, SampleDirMixin, APITestCase):
archive_checksum="D", archive_checksum="D",
) )
shutil.copy(self.SIMPLE_PDF, self.doc2.source_path) shutil.copy(self.SAMPLE_DIR / "simple.pdf", self.doc2.source_path)
shutil.copy(self.SAMPLE_DIR / "simple.png", self.doc2b.source_path) shutil.copy(self.SAMPLE_DIR / "simple.png", self.doc2b.source_path)
shutil.copy(self.SAMPLE_DIR / "simple.jpg", self.doc3.source_path) shutil.copy(self.SAMPLE_DIR / "simple.jpg", self.doc3.source_path)
shutil.copy(self.SAMPLE_DIR / "test_with_bom.pdf", self.doc3.archive_path) shutil.copy(self.SAMPLE_DIR / "test_with_bom.pdf", self.doc3.archive_path)
+2 -2
View File
@@ -61,7 +61,7 @@ class TestChatStreamingViewInputValidation(APITestCase):
return_value=self._mock_ai_enabled(), return_value=self._mock_ai_enabled(),
), ),
mock.patch( mock.patch(
"paperless_ai.chat.stream_chat_with_documents", "documents.views.stream_chat_with_documents",
return_value=iter(chunks), return_value=iter(chunks),
), ),
): ):
@@ -104,7 +104,7 @@ class TestChatStreamingViewUnrestrictedFlag:
""" """
mocker.patch("documents.views.AIConfig").return_value.ai_enabled = True mocker.patch("documents.views.AIConfig").return_value.ai_enabled = True
return mocker.patch( return mocker.patch(
"paperless_ai.chat.stream_chat_with_documents", "documents.views.stream_chat_with_documents",
return_value=iter(()), return_value=iter(()),
) )
@@ -1110,7 +1110,7 @@ class TestVersionActionPermissions(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
with ( with (
mock.patch("documents.views.AIConfig") as ai_config, mock.patch("documents.views.AIConfig") as ai_config,
mock.patch("paperless_ai.chat.stream_chat_with_documents") as chat, mock.patch("documents.views.stream_chat_with_documents") as chat,
): ):
ai_config.return_value.ai_enabled = True ai_config.return_value.ai_enabled = True
response = self.client.post( response = self.client.post(
+34 -50
View File
@@ -57,8 +57,6 @@ from paperless_testing.http import read_streaming_response
from paperless_testing.permissions import grant_all_global from paperless_testing.permissions import grant_all_global
from paperless_testing.permissions import grant_global from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object from paperless_testing.permissions import grant_object
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
from paperless_testing.samples import THUMBNAIL_WEBP
class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase): class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
@@ -1919,11 +1917,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SimpleUploadedFile( with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f}, {"document": f},
@@ -1951,7 +1945,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
payload = SimpleUploadedFile( payload = SimpleUploadedFile(
"../../outside.pdf", "../../outside.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(), (Path(__file__).parent / "samples" / "simple.pdf").read_bytes(),
content_type="application/pdf", content_type="application/pdf",
) )
@@ -1980,7 +1974,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
pdf_bytes = SIMPLE_DIGITAL_PDF.read_bytes() pdf_bytes = (Path(__file__).parent / "samples" / "simple.pdf").read_bytes()
boundary = "paperless-boundary" boundary = "paperless-boundary"
payload = ( payload = (
( (
@@ -2056,7 +2050,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
def test_upload_insufficient_permissions(self) -> None: def test_upload_insufficient_permissions(self) -> None:
self.client.force_authenticate(user=UserFactory(username="testuser2")) self.client.force_authenticate(user=UserFactory(username="testuser2"))
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f}, {"document": f},
@@ -2069,11 +2063,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SimpleUploadedFile( with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{ {
@@ -2106,7 +2096,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"documenst": f}, {"documenst": f},
@@ -2132,7 +2122,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "title": "my custom title"}, {"document": f, "title": "my custom title"},
@@ -2152,7 +2142,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
) )
c = Correspondent.objects.create(name="test-corres") c = Correspondent.objects.create(name="test-corres")
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "correspondent": c.id}, {"document": f, "correspondent": c.id},
@@ -2171,7 +2161,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "correspondent": 3456}, {"document": f, "correspondent": 3456},
@@ -2186,7 +2176,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
) )
dt = DocumentType.objects.create(name="invoice") dt = DocumentType.objects.create(name="invoice")
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "document_type": dt.id}, {"document": f, "document_type": dt.id},
@@ -2205,7 +2195,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "document_type": 34578}, {"document": f, "document_type": 34578},
@@ -2220,7 +2210,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
) )
sp = StoragePath.objects.create(name="invoices") sp = StoragePath.objects.create(name="invoices")
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "storage_path": sp.id}, {"document": f, "storage_path": sp.id},
@@ -2239,7 +2229,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "storage_path": 34578}, {"document": f, "storage_path": 34578},
@@ -2255,7 +2245,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
t1 = Tag.objects.create(name="tag1") t1 = Tag.objects.create(name="tag1")
t2 = Tag.objects.create(name="tag2") t2 = Tag.objects.create(name="tag2")
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "tags": [t2.id, t1.id]}, {"document": f, "tags": [t2.id, t1.id]},
@@ -2276,7 +2266,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
t1 = Tag.objects.create(name="tag1") t1 = Tag.objects.create(name="tag1")
t2 = Tag.objects.create(name="tag2") t2 = Tag.objects.create(name="tag2")
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "tags": [t2.id, t1.id, 734563]}, {"document": f, "tags": [t2.id, t1.id, 734563]},
@@ -2300,7 +2290,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
0, 0,
tzinfo=zoneinfo.ZoneInfo("America/Los_Angeles"), tzinfo=zoneinfo.ZoneInfo("America/Los_Angeles"),
) )
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "created": created}, {"document": f, "created": created},
@@ -2316,11 +2306,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SimpleUploadedFile( with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "archive_serial_number": 500}, {"document": f, "archive_serial_number": 500},
@@ -2347,11 +2333,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
data_type=CustomField.FieldDataType.STRING, data_type=CustomField.FieldDataType.STRING,
) )
with SimpleUploadedFile( with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{ {
@@ -2406,7 +2388,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
w1.actions.add(action1) w1.actions.add(action1)
w1.save() w1.save()
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{ {
@@ -2447,11 +2429,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
data_type=CustomField.FieldDataType.INT, data_type=CustomField.FieldDataType.INT,
) )
with SimpleUploadedFile( with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
"simple.pdf",
SIMPLE_DIGITAL_PDF.read_bytes(),
content_type="application/pdf",
) as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{ {
@@ -2500,7 +2478,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
] ]
for payload in error_payloads: for payload in error_payloads:
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
data = {"document": f, **payload} data = {"document": f, **payload}
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
@@ -2558,7 +2536,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
id=str(uuid.uuid4()), id=str(uuid.uuid4()),
) )
with SIMPLE_DIGITAL_PDF.open("rb") as f: with (Path(__file__).parent / "samples" / "simple.pdf").open("rb") as f:
response = self.client.post( response = self.client.post(
"/api/documents/post_document/", "/api/documents/post_document/",
{"document": f, "from_webui": True}, {"document": f, "from_webui": True},
@@ -2597,8 +2575,14 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
archive_filename="archive.pdf", archive_filename="archive.pdf",
) )
source_file: Path = THUMBNAIL_WEBP source_file: Path = (
archive_file: Path = SIMPLE_DIGITAL_PDF Path(__file__).parent
/ "samples"
/ "documents"
/ "thumbnails"
/ "0000001.webp"
)
archive_file: Path = Path(__file__).parent / "samples" / "simple.pdf"
shutil.copy(source_file, doc.source_path) shutil.copy(source_file, doc.source_path)
shutil.copy(archive_file, doc.archive_path) shutil.copy(archive_file, doc.archive_path)
@@ -2631,7 +2615,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
mime_type="application/pdf", mime_type="application/pdf",
) )
shutil.copy(SIMPLE_DIGITAL_PDF, doc.source_path) shutil.copy(Path(__file__).parent / "samples" / "simple.pdf", doc.source_path)
response = self.client.get(f"/api/documents/{doc.pk}/metadata/") response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -2686,7 +2670,7 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
response = self.client.get("/api/documents/34676/suggestions/") response = self.client.get("/api/documents/34676/suggestions/")
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@mock.patch("paperless_ai.ai_classifier.get_ai_document_classification") @mock.patch("documents.views.get_ai_document_classification")
@override_settings(AI_ENABLED=True) @override_settings(AI_ENABLED=True)
def test_suggestions_still_uses_classifier_when_ai_enabled( def test_suggestions_still_uses_classifier_when_ai_enabled(
self, self,
@@ -4397,8 +4381,8 @@ class TestDocumentApi(DirectoriesMixin, ConsumeTaskMixin, APITestCase):
filename="test2.pdf", filename="test2.pdf",
) )
archive_file = SIMPLE_DIGITAL_PDF archive_file = Path(__file__).parent / "samples" / "simple.pdf"
source_file = SIMPLE_DIGITAL_PDF source_file = Path(__file__).parent / "samples" / "simple.pdf"
shutil.copy(archive_file, doc.archive_path) shutil.copy(archive_file, doc.archive_path)
shutil.copy(source_file, doc2.source_path) shutil.copy(source_file, doc2.source_path)
+5 -8
View File
@@ -12,9 +12,6 @@ from documents.tests.utils import SampleDirMixin
from paperless_testing.dirs import DirectoriesMixin from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import UserFactory from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_global from paperless_testing.permissions import grant_global
from paperless_testing.samples import MULTI_PAGE_DIGITAL_PDF
from paperless_testing.samples import SIMPLE_DIGITAL_PDF
from paperless_testing.samples import WITH_FORM_PDF
class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase): class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
@@ -45,15 +42,15 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
# Copy sample files to document paths (using different files to distinguish versions) # Copy sample files to document paths (using different files to distinguish versions)
shutil.copy( shutil.copy(
SIMPLE_DIGITAL_PDF, self.SAMPLE_DIR / "documents" / "originals" / "0000001.pdf",
self.doc1.archive_path, self.doc1.archive_path,
) )
shutil.copy( shutil.copy(
MULTI_PAGE_DIGITAL_PDF, self.SAMPLE_DIR / "documents" / "originals" / "0000002.pdf",
self.doc1.source_path, self.doc1.source_path,
) )
shutil.copy( shutil.copy(
WITH_FORM_PDF, self.SAMPLE_DIR / "documents" / "originals" / "0000003.pdf",
self.doc2.source_path, self.doc2.source_path,
) )
@@ -380,7 +377,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
checksum="3", checksum="3",
filename="test3.pdf", filename="test3.pdf",
) )
shutil.copy(self.SIMPLE_PDF, doc3.source_path) shutil.copy(self.SAMPLE_DIR / "simple.pdf", doc3.source_path)
doc4 = Document.objects.create( doc4 = Document.objects.create(
title="test1", title="test1",
@@ -389,7 +386,7 @@ class TestEmail(DirectoriesMixin, SampleDirMixin, APITestCase):
checksum="4", checksum="4",
filename="test4.pdf", filename="test4.pdf",
) )
shutil.copy(self.SIMPLE_PDF, doc4.source_path) shutil.copy(self.SAMPLE_DIR / "simple.pdf", doc4.source_path)
response = self.client.post( response = self.client.post(
self.ENDPOINT, self.ENDPOINT,
Loaded 100 of 195 files, more files were not shown because too many files have changed in this diff. Show more