pdfdesk 0.1.0__py3-none-any.whl

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 (34) hide show
  1. pdfdesk/__init__.py +3 -0
  2. pdfdesk/__main__.py +3 -0
  3. pdfdesk/cli.py +107 -0
  4. pdfdesk/static/app.js +683 -0
  5. pdfdesk/static/index.html +35 -0
  6. pdfdesk/static/styles.css +294 -0
  7. pdfdesk/static/vendor/fonts/OFL-ibmplexmono.txt +93 -0
  8. pdfdesk/static/vendor/fonts/OFL-ibmplexsans.txt +93 -0
  9. pdfdesk/static/vendor/fonts/OFL-sourceserif4.txt +93 -0
  10. pdfdesk/static/vendor/fonts/fonts.css +151 -0
  11. pdfdesk/static/vendor/fonts/ibm-plex-mono-400-latin-ext.woff2 +0 -0
  12. pdfdesk/static/vendor/fonts/ibm-plex-mono-400-latin.woff2 +0 -0
  13. pdfdesk/static/vendor/fonts/ibm-plex-mono-500-latin-ext.woff2 +0 -0
  14. pdfdesk/static/vendor/fonts/ibm-plex-mono-500-latin.woff2 +0 -0
  15. pdfdesk/static/vendor/fonts/ibm-plex-sans-400-latin-ext.woff2 +0 -0
  16. pdfdesk/static/vendor/fonts/ibm-plex-sans-400-latin.woff2 +0 -0
  17. pdfdesk/static/vendor/fonts/ibm-plex-sans-500-latin-ext.woff2 +0 -0
  18. pdfdesk/static/vendor/fonts/ibm-plex-sans-500-latin.woff2 +0 -0
  19. pdfdesk/static/vendor/fonts/ibm-plex-sans-600-latin-ext.woff2 +0 -0
  20. pdfdesk/static/vendor/fonts/ibm-plex-sans-600-latin.woff2 +0 -0
  21. pdfdesk/static/vendor/fonts/source-serif-4-400-latin-ext.woff2 +0 -0
  22. pdfdesk/static/vendor/fonts/source-serif-4-400-latin.woff2 +0 -0
  23. pdfdesk/static/vendor/fonts/source-serif-4-600-latin-ext.woff2 +0 -0
  24. pdfdesk/static/vendor/fonts/source-serif-4-600-latin.woff2 +0 -0
  25. pdfdesk/static/vendor/fonts/source-serif-4-700-latin-ext.woff2 +0 -0
  26. pdfdesk/static/vendor/fonts/source-serif-4-700-latin.woff2 +0 -0
  27. pdfdesk/static/vendor/pdfjs/LICENSE +177 -0
  28. pdfdesk/static/vendor/pdfjs/pdf.min.js +22 -0
  29. pdfdesk/static/vendor/pdfjs/pdf.worker.min.js +22 -0
  30. pdfdesk-0.1.0.dist-info/METADATA +209 -0
  31. pdfdesk-0.1.0.dist-info/RECORD +34 -0
  32. pdfdesk-0.1.0.dist-info/WHEEL +4 -0
  33. pdfdesk-0.1.0.dist-info/entry_points.txt +2 -0
  34. pdfdesk-0.1.0.dist-info/licenses/LICENSE +21 -0
pdfdesk/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """PDFDesk: a local-first workspace for reading research papers."""
2
+
3
+ __version__ = "0.1.0"
pdfdesk/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from pdfdesk.cli import main
2
+
3
+ raise SystemExit(main())
pdfdesk/cli.py ADDED
@@ -0,0 +1,107 @@
1
+ """`pdfdesk` command: serve the bundled browser app on localhost and open it."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import errno
7
+ import functools
8
+ import http.server
9
+ import socket
10
+ import sys
11
+ import threading
12
+ import webbrowser
13
+ from pathlib import Path
14
+
15
+ from pdfdesk import __version__
16
+
17
+ DEFAULT_PORT = 5173
18
+
19
+ # Windows can map .js to text/plain through the registry, which browsers refuse to run.
20
+ TYPES = {
21
+ ".html": "text/html; charset=utf-8",
22
+ ".js": "text/javascript; charset=utf-8",
23
+ ".mjs": "text/javascript; charset=utf-8",
24
+ ".css": "text/css; charset=utf-8",
25
+ ".json": "application/json; charset=utf-8",
26
+ ".svg": "image/svg+xml",
27
+ ".png": "image/png",
28
+ ".pdf": "application/pdf",
29
+ ".woff2": "font/woff2",
30
+ ".txt": "text/plain; charset=utf-8",
31
+ }
32
+
33
+
34
+ def static_dir() -> Path:
35
+ """The app files: bundled inside the wheel, or prototype/ when run from a checkout."""
36
+ bundled = Path(__file__).parent / "static"
37
+ if (bundled / "index.html").is_file():
38
+ return bundled
39
+ checkout = Path(__file__).parent.parent / "prototype"
40
+ if (checkout / "index.html").is_file():
41
+ return checkout
42
+ raise SystemExit("pdfdesk: app files are missing from this install. Reinstall with `pip install --force-reinstall pdfdesk`.")
43
+
44
+
45
+ class Handler(http.server.SimpleHTTPRequestHandler):
46
+ extensions_map = {**http.server.SimpleHTTPRequestHandler.extensions_map, **TYPES}
47
+
48
+ def end_headers(self) -> None:
49
+ # Revalidate every load, so an upgraded install never runs a stale app.js.
50
+ self.send_header("Cache-Control", "no-cache")
51
+ super().end_headers()
52
+
53
+ def log_message(self, format: str, *args) -> None: # quiet by default
54
+ if self.server.verbose: # type: ignore[attr-defined]
55
+ super().log_message(format, *args)
56
+
57
+
58
+ class Server(http.server.ThreadingHTTPServer):
59
+ # On Windows SO_REUSEADDR lets a second server bind the same port silently, so two copies would
60
+ # "run" at once. Claim the port exclusively there instead; elsewhere keep fast restarts.
61
+ allow_reuse_address = sys.platform != "win32"
62
+ daemon_threads = True
63
+
64
+ def server_bind(self) -> None:
65
+ if sys.platform == "win32" and hasattr(socket, "SO_EXCLUSIVEADDRUSE"):
66
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1)
67
+ super().server_bind()
68
+
69
+
70
+ def main(argv: list[str] | None = None) -> int:
71
+ parser = argparse.ArgumentParser(
72
+ prog="pdfdesk",
73
+ description="Start PDFDesk on this computer and open it in your browser.",
74
+ )
75
+ parser.add_argument("--port", type=int, default=DEFAULT_PORT,
76
+ help=f"port to serve on (default {DEFAULT_PORT}). Your library is stored per port, so keep it the same.")
77
+ parser.add_argument("--no-browser", action="store_true", help="don't open a browser window")
78
+ parser.add_argument("--verbose", action="store_true", help="log every request")
79
+ parser.add_argument("--version", action="version", version=f"pdfdesk {__version__}")
80
+ args = parser.parse_args(argv)
81
+
82
+ handler = functools.partial(Handler, directory=str(static_dir()))
83
+ try:
84
+ server = Server(("127.0.0.1", args.port), handler)
85
+ except OSError as e:
86
+ if e.errno in (errno.EADDRINUSE, errno.EACCES) or getattr(e, "winerror", None) in (10048, 10013):
87
+ print(
88
+ f"pdfdesk: port {args.port} is already in use. PDFDesk may already be running at http://localhost:{args.port}\n"
89
+ "Your library is saved per port, so a different port starts with an empty library.\n"
90
+ "To use another port anyway: pdfdesk --port <number>",
91
+ file=sys.stderr,
92
+ )
93
+ return 1
94
+ raise
95
+ server.verbose = args.verbose # type: ignore[attr-defined]
96
+
97
+ url = f"http://localhost:{args.port}/"
98
+ print(f"PDFDesk {__version__} running at {url} (Ctrl+C to stop)")
99
+ if not args.no_browser:
100
+ threading.Timer(0.3, webbrowser.open, args=(url,)).start()
101
+ try:
102
+ server.serve_forever()
103
+ except KeyboardInterrupt:
104
+ print("\nStopped.")
105
+ finally:
106
+ server.server_close()
107
+ return 0