djpress-tiptap 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. djpress_tiptap-0.2.0/PKG-INFO +40 -0
  2. djpress_tiptap-0.2.0/README.md +28 -0
  3. djpress_tiptap-0.2.0/pyproject.toml +49 -0
  4. djpress_tiptap-0.2.0/src/djpress_tiptap/__init__.py +1 -0
  5. djpress_tiptap-0.2.0/src/djpress_tiptap/apps.py +10 -0
  6. djpress_tiptap-0.2.0/src/djpress_tiptap/conf.py +121 -0
  7. djpress_tiptap-0.2.0/src/djpress_tiptap/static/dj_tiptap/content.bundle.js +5 -0
  8. djpress_tiptap-0.2.0/src/djpress_tiptap/static/dj_tiptap/content.css +2 -0
  9. djpress_tiptap-0.2.0/src/djpress_tiptap/static/dj_tiptap/djtiptap.bundle.js +736 -0
  10. djpress_tiptap-0.2.0/src/djpress_tiptap/static/dj_tiptap/djtiptap.css +2 -0
  11. djpress_tiptap-0.2.0/src/djpress_tiptap/static/djpress_tiptap/content.bundle.js +5 -0
  12. djpress_tiptap-0.2.0/src/djpress_tiptap/static/djpress_tiptap/content.css +2 -0
  13. djpress_tiptap-0.2.0/src/djpress_tiptap/static/djpress_tiptap/djtiptap.bundle.js +736 -0
  14. djpress_tiptap-0.2.0/src/djpress_tiptap/static/djpress_tiptap/djtiptap.css +2 -0
  15. djpress_tiptap-0.2.0/src/djpress_tiptap/templates/djpress_tiptap/attachment_browse.html +29 -0
  16. djpress_tiptap-0.2.0/src/djpress_tiptap/templates/djpress_tiptap/widgets/djpress_tiptap_editor.html +63 -0
  17. djpress_tiptap-0.2.0/src/djpress_tiptap/urls.py +11 -0
  18. djpress_tiptap-0.2.0/src/djpress_tiptap/views.py +184 -0
  19. djpress_tiptap-0.2.0/src/djpress_tiptap/widgets.py +49 -0
  20. djpress_tiptap-0.2.0/tests/__init__.py +1 -0
  21. djpress_tiptap-0.2.0/tests/conftest.py +7 -0
  22. djpress_tiptap-0.2.0/tests/fixtures/clip.mp4 +0 -0
  23. djpress_tiptap-0.2.0/tests/fixtures/photo.png +0 -0
  24. djpress_tiptap-0.2.0/tests/fixtures/pixel.png +0 -0
  25. djpress_tiptap-0.2.0/tests/test_conf.py +106 -0
  26. djpress_tiptap-0.2.0/tests/test_editor_e2e.py +615 -0
  27. djpress_tiptap-0.2.0/tests/test_views.py +199 -0
  28. djpress_tiptap-0.2.0/tests/test_widgets.py +75 -0
@@ -0,0 +1,40 @@
1
+ Metadata-Version: 2.1
2
+ Name: djpress-tiptap
3
+ Version: 0.2.0
4
+ Summary: Rich text editor for DJ Press based on Tiptap.
5
+ Author-Email: Stuart Maxwell <stuart@amanzi.nz>
6
+ License: MIT
7
+ Requires-Python: >=3.14
8
+ Requires-Dist: djpress>=0.30.0
9
+ Requires-Dist: pillow~=12.3
10
+ Requires-Dist: puremagic~=2.2
11
+ Description-Content-Type: text/markdown
12
+
13
+ # DJ Tiptap
14
+
15
+ Opinionated TipTap editor implementation for Django sites.
16
+
17
+ Docs are still a work in progress.
18
+ See the example app for usage.
19
+
20
+ ## Settings
21
+
22
+ All settings are optional; the package works with sensible defaults. Upload
23
+ and browse buttons only appear in the toolbar when the corresponding URL is
24
+ configured.
25
+
26
+ | Setting | Default | Purpose |
27
+ | --- | --- | --- |
28
+ | `DJPRESS_TIPTAP_UPLOAD_URL` | unset (uploads disabled) | URL name or path of the attachment upload endpoint |
29
+ | `DJPRESS_TIPTAP_BROWSE_URL` | unset (library disabled) | URL name or path of the media-library browse endpoint |
30
+ | `DJPRESS_TIPTAP_MAX_UPLOAD_SIZE_MB` | `10` | Maximum image upload size |
31
+ | `DJPRESS_TIPTAP_MAX_VIDEO_UPLOAD_SIZE_MB` | `100` | Maximum video upload size |
32
+ | `DJPRESS_TIPTAP_ALLOWED_IMAGE_TYPES` | JPEG/PNG/GIF/WebP | Dict of Pillow format → mime type accepted by the upload view |
33
+ | `DJPRESS_TIPTAP_ALLOWED_VIDEO_TYPES` | `{"video/mp4", "video/webm"}` | Set of video mime types accepted by the upload view; set to `set()` to disable video uploads |
34
+
35
+ Images are inserted as `<img>` and validated with Pillow; videos are inserted
36
+ as HTML5 `<video controls>` elements and validated by magic bytes with
37
+ [puremagic](https://github.com/cdgriffith/puremagic). The upload endpoint
38
+ lives in the host project (see `example/website/views.py` for the reference
39
+ implementation) — its JSON response's `content_type` field tells the editor
40
+ which element to insert.
@@ -0,0 +1,28 @@
1
+ # DJ Tiptap
2
+
3
+ Opinionated TipTap editor implementation for Django sites.
4
+
5
+ Docs are still a work in progress.
6
+ See the example app for usage.
7
+
8
+ ## Settings
9
+
10
+ All settings are optional; the package works with sensible defaults. Upload
11
+ and browse buttons only appear in the toolbar when the corresponding URL is
12
+ configured.
13
+
14
+ | Setting | Default | Purpose |
15
+ | --- | --- | --- |
16
+ | `DJPRESS_TIPTAP_UPLOAD_URL` | unset (uploads disabled) | URL name or path of the attachment upload endpoint |
17
+ | `DJPRESS_TIPTAP_BROWSE_URL` | unset (library disabled) | URL name or path of the media-library browse endpoint |
18
+ | `DJPRESS_TIPTAP_MAX_UPLOAD_SIZE_MB` | `10` | Maximum image upload size |
19
+ | `DJPRESS_TIPTAP_MAX_VIDEO_UPLOAD_SIZE_MB` | `100` | Maximum video upload size |
20
+ | `DJPRESS_TIPTAP_ALLOWED_IMAGE_TYPES` | JPEG/PNG/GIF/WebP | Dict of Pillow format → mime type accepted by the upload view |
21
+ | `DJPRESS_TIPTAP_ALLOWED_VIDEO_TYPES` | `{"video/mp4", "video/webm"}` | Set of video mime types accepted by the upload view; set to `set()` to disable video uploads |
22
+
23
+ Images are inserted as `<img>` and validated with Pillow; videos are inserted
24
+ as HTML5 `<video controls>` elements and validated by magic bytes with
25
+ [puremagic](https://github.com/cdgriffith/puremagic). The upload endpoint
26
+ lives in the host project (see `example/website/views.py` for the reference
27
+ implementation) — its JSON response's `content_type` field tells the editor
28
+ which element to insert.
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "djpress-tiptap"
3
+ version = "0.2.0"
4
+ description = "Rich text editor for DJ Press based on Tiptap."
5
+ authors = [
6
+ { name = "Stuart Maxwell", email = "stuart@amanzi.nz" },
7
+ ]
8
+ requires-python = ">=3.14"
9
+ readme = "README.md"
10
+ dependencies = [
11
+ "djpress>=0.30.0",
12
+ "pillow~=12.3",
13
+ "puremagic~=2.2",
14
+ ]
15
+
16
+ [project.license]
17
+ text = "MIT"
18
+
19
+ [dependency-groups]
20
+ dev = [
21
+ "pytest~=9.1",
22
+ "playwright~=1.61",
23
+ "pytest-django~=4.12",
24
+ "pytest-playwright>=0.8.0",
25
+ ]
26
+
27
+ [build-system]
28
+ requires = [
29
+ "pdm-backend",
30
+ ]
31
+ build-backend = "pdm.backend"
32
+
33
+ [tool.pdm]
34
+ distribution = true
35
+
36
+ [tool.pytest.ini_options]
37
+ pythonpath = ". example"
38
+ DJANGO_SETTINGS_MODULE = "config.settings_testing"
39
+ python_files = "tests.py test_*.py *_tests.py"
40
+ markers = [
41
+ "e2e: browser tests driving the live server (deselect with -m 'not e2e')",
42
+ ]
43
+
44
+ [tool.djangofmt]
45
+ line-length = 240
46
+ indent-width = 2
47
+ profile = "django"
48
+ html-void-self-closing = "never"
49
+ preserve-unquoted-attrs = false
@@ -0,0 +1 @@
1
+ """DJ Press Tiptap."""
@@ -0,0 +1,10 @@
1
+ """Django app configuration for DJ Tiptap."""
2
+
3
+ from django.apps import AppConfig
4
+
5
+
6
+ class DjTiptapBaseConfig(AppConfig):
7
+ """App configuration for DJ Tiptap."""
8
+
9
+ name = "djpress_tiptap"
10
+ verbose_name = "DJ Press Tiptap"
@@ -0,0 +1,121 @@
1
+ """djpress-tiptap configuration.
2
+
3
+ Every setting is optional in the host project: these functions return the
4
+ project's DJPRESS_TIPTAP_* value when set, or the package default otherwise.
5
+
6
+ Functions rather than module-level constants so the settings are read lazily
7
+ (at request/render time): override_settings keeps working in tests, and the
8
+ module can be imported during app loading before settings are configured.
9
+ """
10
+
11
+ from django.conf import settings
12
+ from django.shortcuts import resolve_url
13
+
14
+ # Pillow format name -> mime type. Keys validate what Pillow detected in the
15
+ # upload view; values are stored as Attachment.content_type and forwarded to
16
+ # the editor JS (file picker + drag-drop filter) via the widget's data-accept
17
+ # attribute. No SVG: it can carry scripts, making it an XSS vector.
18
+ DEFAULT_ALLOWED_IMAGE_TYPES = {
19
+ "JPEG": "image/jpeg",
20
+ "PNG": "image/png",
21
+ "GIF": "image/gif",
22
+ "WEBP": "image/webp",
23
+ }
24
+
25
+ # Mime types accepted by the video upload path, as detected by puremagic in
26
+ # the upload view, stored as Attachment.content_type, and forwarded to the
27
+ # editor JS via the widget's data-accept-video attribute. A set of mime types
28
+ # (not a format->mime dict like images) because puremagic reports mime types
29
+ # directly. Only web-playable formats: video/quicktime et al. would upload
30
+ # fine but not play in most browsers' <video> element. Set to an empty set to
31
+ # disable video uploads entirely.
32
+ DEFAULT_ALLOWED_VIDEO_TYPES = {
33
+ "video/mp4",
34
+ "video/webm",
35
+ }
36
+
37
+ DEFAULT_MAX_UPLOAD_SIZE_MB = 10
38
+
39
+ # Videos get their own, larger cap: even a short clip dwarfs any photo.
40
+ DEFAULT_MAX_VIDEO_UPLOAD_SIZE_MB = 100
41
+
42
+ # DJ Press Tiptap views to upload media
43
+ DJPRESS_TIPTAP_UPLOAD_URL = "djpress_tiptap:media_upload"
44
+ DJPRESS_TIPTAP_BROWSE_URL = "djpress_tiptap:media_browse"
45
+
46
+
47
+ def max_upload_size_mb() -> int:
48
+ """Maximum attachment upload size in megabytes.
49
+
50
+ Returns:
51
+ The maximum upload size in megabytes, as configured via DJPRESS_TIPTAP_MAX_UPLOAD_SIZE_MB.
52
+ """
53
+ return getattr(settings, "DJPRESS_TIPTAP_MAX_UPLOAD_SIZE_MB", DEFAULT_MAX_UPLOAD_SIZE_MB)
54
+
55
+
56
+ def max_video_upload_size_mb() -> int:
57
+ """Maximum video upload size in megabytes.
58
+
59
+ Returns:
60
+ The maximum video upload size in megabytes, as configured via
61
+ DJPRESS_TIPTAP_MAX_VIDEO_UPLOAD_SIZE_MB.
62
+ """
63
+ return getattr(settings, "DJPRESS_TIPTAP_MAX_VIDEO_UPLOAD_SIZE_MB", DEFAULT_MAX_VIDEO_UPLOAD_SIZE_MB)
64
+
65
+
66
+ def allowed_image_types() -> dict[str, str]:
67
+ """Mapping of accepted Pillow image formats to their mime types.
68
+
69
+ Returns:
70
+ The allowed image types, as configured via DJPRESS_TIPTAP_ALLOWED_IMAGE_TYPES.
71
+ """
72
+ return getattr(settings, "DJPRESS_TIPTAP_ALLOWED_IMAGE_TYPES", DEFAULT_ALLOWED_IMAGE_TYPES)
73
+
74
+
75
+ def allowed_video_types() -> set[str]:
76
+ """Set of accepted video mime types.
77
+
78
+ Returns:
79
+ The allowed video mime types, as configured via DJPRESS_TIPTAP_ALLOWED_VIDEO_TYPES.
80
+ """
81
+ return getattr(settings, "DJPRESS_TIPTAP_ALLOWED_VIDEO_TYPES", DEFAULT_ALLOWED_VIDEO_TYPES)
82
+
83
+
84
+ def upload_url(override: str | None = None) -> str:
85
+ """URL of the attachment upload endpoint.
86
+
87
+ Priority: explicit widget argument, then DJPRESS_TIPTAP_UPLOAD_URL setting.
88
+ Values may be a URL name or a path (LOGIN_URL semantics); the view just
89
+ has to keep the JSON contract:
90
+ POST multipart {file} -> 201 {url, alt?, ...} or 4xx {error}.
91
+
92
+ When neither is set (or set to None/empty), returns "" and the widget
93
+ disables uploads.
94
+
95
+ Args:
96
+ override: Optional URL override for the upload endpoint.
97
+
98
+ Returns:
99
+ The upload URL, as configured via DJPRESS_TIPTAP_UPLOAD_URL, or "" if unset.
100
+ """
101
+ url = override or getattr(settings, "DJPRESS_TIPTAP_UPLOAD_URL", DJPRESS_TIPTAP_UPLOAD_URL)
102
+ return resolve_url(url) if url else ""
103
+
104
+
105
+ def browse_url(override: str | None = None) -> str:
106
+ """URL of the media-library browse endpoint.
107
+
108
+ Same priority and name-or-path semantics as upload_url, and likewise
109
+ returns "" (feature disabled) when unset. The view returns an HTML
110
+ fragment honouring the picker's data attributes: data-image-url /
111
+ data-image-alt (insert), data-fetch (load another page), data-close
112
+ (dismiss).
113
+
114
+ Args:
115
+ override: Optional URL override for the browse endpoint.
116
+
117
+ Returns:
118
+ The browse URL, as configured via DJPRESS_TIPTAP_BROWSE_URL, or "" if unset.
119
+ """
120
+ url = override or getattr(settings, "DJPRESS_TIPTAP_BROWSE_URL", DJPRESS_TIPTAP_BROWSE_URL)
121
+ return resolve_url(url) if url else ""