litmark 0.1.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.
- litmark-0.1.0/.gitignore +15 -0
- litmark-0.1.0/LICENSE +21 -0
- litmark-0.1.0/PKG-INFO +204 -0
- litmark-0.1.0/README.md +174 -0
- litmark-0.1.0/frontend/index.html +13 -0
- litmark-0.1.0/frontend/package-lock.json +2226 -0
- litmark-0.1.0/frontend/package.json +33 -0
- litmark-0.1.0/frontend/src/App.tsx +639 -0
- litmark-0.1.0/frontend/src/Sidebar.tsx +300 -0
- litmark-0.1.0/frontend/src/api.ts +374 -0
- litmark-0.1.0/frontend/src/chat/Chat.tsx +400 -0
- litmark-0.1.0/frontend/src/editor/MarkdownEditor.tsx +326 -0
- litmark-0.1.0/frontend/src/editor/livePreview.ts +497 -0
- litmark-0.1.0/frontend/src/events.ts +108 -0
- litmark-0.1.0/frontend/src/main.tsx +9 -0
- litmark-0.1.0/frontend/src/markdown.ts +190 -0
- litmark-0.1.0/frontend/src/pdf/PdfViewer.tsx +631 -0
- litmark-0.1.0/frontend/src/styles.css +1125 -0
- litmark-0.1.0/frontend/test/.gitignore +1 -0
- litmark-0.1.0/frontend/test/app-render.test.mjs +131 -0
- litmark-0.1.0/frontend/test/browser.test.mjs +350 -0
- litmark-0.1.0/frontend/test/build-app-bundle.mjs +18 -0
- litmark-0.1.0/frontend/test/marionette.mjs +181 -0
- litmark-0.1.0/frontend/test/markdown.test.mjs +95 -0
- litmark-0.1.0/frontend/test/url-suffix-plugin.mjs +14 -0
- litmark-0.1.0/frontend/tsconfig.json +23 -0
- litmark-0.1.0/frontend/vite.config.ts +41 -0
- litmark-0.1.0/pyproject.toml +84 -0
- litmark-0.1.0/spec.md +346 -0
- litmark-0.1.0/src/litmark/__init__.py +7 -0
- litmark-0.1.0/src/litmark/agent/__init__.py +1 -0
- litmark-0.1.0/src/litmark/agent/base.py +151 -0
- litmark-0.1.0/src/litmark/agent/claude_sdk.py +345 -0
- litmark-0.1.0/src/litmark/agent/fake.py +157 -0
- litmark-0.1.0/src/litmark/agent/prompts.py +74 -0
- litmark-0.1.0/src/litmark/agent/runner.py +470 -0
- litmark-0.1.0/src/litmark/agent/tools.py +689 -0
- litmark-0.1.0/src/litmark/api/__init__.py +1 -0
- litmark-0.1.0/src/litmark/api/app.py +229 -0
- litmark-0.1.0/src/litmark/api/routes.py +649 -0
- litmark-0.1.0/src/litmark/bibliography.py +334 -0
- litmark-0.1.0/src/litmark/cli.py +211 -0
- litmark-0.1.0/src/litmark/db.py +216 -0
- litmark-0.1.0/src/litmark/documents.py +470 -0
- litmark-0.1.0/src/litmark/errors.py +71 -0
- litmark-0.1.0/src/litmark/events.py +135 -0
- litmark-0.1.0/src/litmark/extraction.py +726 -0
- litmark-0.1.0/src/litmark/jobs.py +183 -0
- litmark-0.1.0/src/litmark/references.py +448 -0
- litmark-0.1.0/src/litmark/services.py +170 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- litmark-0.1.0/src/litmark/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- litmark-0.1.0/src/litmark/static/assets/editor-Bn9f9nF2.js +28 -0
- litmark-0.1.0/src/litmark/static/assets/editor-Bn9f9nF2.js.map +1 -0
- litmark-0.1.0/src/litmark/static/assets/index-BdJz60Ic.css +1 -0
- litmark-0.1.0/src/litmark/static/assets/index-C7zeCZgl.js +12 -0
- litmark-0.1.0/src/litmark/static/assets/index-C7zeCZgl.js.map +1 -0
- litmark-0.1.0/src/litmark/static/assets/katex-uLN8M5ZI.js +258 -0
- litmark-0.1.0/src/litmark/static/assets/katex-uLN8M5ZI.js.map +1 -0
- litmark-0.1.0/src/litmark/static/assets/pdf.worker.min-yatZIOMy.mjs +21 -0
- litmark-0.1.0/src/litmark/static/assets/pdfjs-BDm9ube5.js +13 -0
- litmark-0.1.0/src/litmark/static/assets/pdfjs-BDm9ube5.js.map +1 -0
- litmark-0.1.0/src/litmark/static/index.html +17 -0
- litmark-0.1.0/src/litmark/workspace.py +489 -0
- litmark-0.1.0/tests/conftest.py +175 -0
- litmark-0.1.0/tests/test_acceptance.py +966 -0
- litmark-0.1.0/tests/test_bibliography.py +262 -0
- litmark-0.1.0/tests/test_claude_adapter.py +216 -0
- litmark-0.1.0/tests/test_geometry.py +134 -0
- litmark-0.1.0/tests/test_packaging.py +205 -0
- litmark-0.1.0/tests/test_proxy_access.py +115 -0
- litmark-0.1.0/tests/test_smoke.py +61 -0
litmark-0.1.0/.gitignore
ADDED
litmark-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thibaut Lamadon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
litmark-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: litmark
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local research workspace: import papers, read cited summaries, write sourced Markdown notes, chat with a coding agent.
|
|
5
|
+
Project-URL: Homepage, https://github.com/tlamadon/litmark
|
|
6
|
+
Project-URL: Repository, https://github.com/tlamadon/litmark
|
|
7
|
+
Project-URL: Issues, https://github.com/tlamadon/litmark/issues
|
|
8
|
+
Author: Litmark contributors
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,bibtex,markdown,notes,pdf,research
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Web Environment
|
|
14
|
+
Classifier: Framework :: FastAPI
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: fastapi<1.0,>=0.115
|
|
22
|
+
Requires-Dist: pdfplumber<1.0,>=0.11
|
|
23
|
+
Requires-Dist: pydantic<3.0,>=2.7
|
|
24
|
+
Requires-Dist: pypdf<7.0,>=4.2
|
|
25
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
26
|
+
Requires-Dist: uvicorn[standard]<1.0,>=0.30
|
|
27
|
+
Provides-Extra: claude
|
|
28
|
+
Requires-Dist: claude-agent-sdk<1.0,>=0.2; extra == 'claude'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Litmark
|
|
32
|
+
|
|
33
|
+
A local research application: import papers, read short cited summaries, write
|
|
34
|
+
Markdown notes, inspect the cited PDF passages, and chat with a coding agent
|
|
35
|
+
that can search the project and edit its files.
|
|
36
|
+
|
|
37
|
+
**Import papers → ask questions → write a sourced note → inspect the evidence → refine the note.**
|
|
38
|
+
|
|
39
|
+
Everything you write stays as ordinary files in a movable project directory:
|
|
40
|
+
`notes/*.md`, `documents/<id>/original.pdf`, `references.json`. The application
|
|
41
|
+
is a convenient view onto those files, not their owner.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python -m pip install litmark # core reader and editor
|
|
47
|
+
python -m pip install 'litmark[claude]' # plus the agent adapter
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Requires Python 3.11+. Tested on macOS and Linux. The published wheel contains
|
|
51
|
+
the compiled browser assets — **you do not need Node, npm, or a second web
|
|
52
|
+
server to run this application.**
|
|
53
|
+
|
|
54
|
+
With `pipx` or `uv`:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pipx install litmark
|
|
58
|
+
uv tool install litmark
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Use
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
litmark init ./my-research
|
|
65
|
+
litmark serve ./my-research --open
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`serve` binds to `127.0.0.1`, prints the local URL, and serves exactly one
|
|
69
|
+
project per process. Reopening an existing project needs no initialization, and
|
|
70
|
+
`init` never overwrites existing material.
|
|
71
|
+
|
|
72
|
+
### Reaching it through a proxy or port-forward
|
|
73
|
+
|
|
74
|
+
By default the server binds to loopback *and* rejects any `Host` header that is
|
|
75
|
+
not a loopback name. That second check is what stops a malicious page from
|
|
76
|
+
using DNS rebinding to talk to your local server — but behind a reverse proxy
|
|
77
|
+
or a port-forward the header carries the proxy's hostname, so the check would
|
|
78
|
+
reject every real request. Widen it explicitly:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Bind beyond loopback; any Host header is then accepted.
|
|
82
|
+
litmark serve ./my-research --host 0.0.0.0
|
|
83
|
+
|
|
84
|
+
# Or name the proxy's hostname and keep the check strict.
|
|
85
|
+
litmark serve ./my-research --host 0.0.0.0 --allow-host papers.internal
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The per-process session credential embedded in the served page remains the
|
|
89
|
+
access control in both cases. On an untrusted network prefer an SSH tunnel,
|
|
90
|
+
which keeps the default loopback binding intact:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
ssh -N -L 8765:127.0.0.1:8765 you@the-machine
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Check your environment at any time:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
litmark doctor ./my-research
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`doctor` reports on the workspace, the optional agent dependency, the runtime
|
|
103
|
+
it needs, and whether authentication is configured — without printing secrets.
|
|
104
|
+
|
|
105
|
+
## Agent configuration
|
|
106
|
+
|
|
107
|
+
The agent backend is the [Claude Agent SDK for
|
|
108
|
+
Python](https://code.claude.com/docs/en/agent-sdk/python), behind a
|
|
109
|
+
provider-neutral adapter. Provider credentials and billing are separate from
|
|
110
|
+
installing this application; a consumer chat subscription does not by itself
|
|
111
|
+
grant SDK access.
|
|
112
|
+
|
|
113
|
+
The reader and editor work with no agent account at all. Import and text
|
|
114
|
+
extraction still run; automatic summaries wait until an agent is configured,
|
|
115
|
+
and the interface says so plainly.
|
|
116
|
+
|
|
117
|
+
## Project layout
|
|
118
|
+
|
|
119
|
+
| Path | Purpose |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| `project.toml` | Schema version and non-secret project settings. |
|
|
122
|
+
| `documents/<id>/original.pdf` | Original immutable PDF. |
|
|
123
|
+
| `documents/<id>/metadata.json` | Identity, hash, filename, title, page count, status. |
|
|
124
|
+
| `documents/<id>/pages.json` | Per-page text, character geometry, offset mapping. |
|
|
125
|
+
| `documents/<id>/summary.md` | Editable, cited summary. |
|
|
126
|
+
| `notes/*.md` | User and agent notes. |
|
|
127
|
+
| `references.json` | Versioned source registry. |
|
|
128
|
+
| `references.bib` | Generated BibTeX bibliography of the imported documents. |
|
|
129
|
+
| `.research/state.sqlite` | Conversations, events, jobs, run state, change log. |
|
|
130
|
+
| `.research/history/` | Previous file contents, for review and undo. |
|
|
131
|
+
| `.research/runs/` | Staged agent edits and run diagnostics. |
|
|
132
|
+
|
|
133
|
+
## Citations
|
|
134
|
+
|
|
135
|
+
A citation is an ordinary Markdown link with an application-specific
|
|
136
|
+
destination:
|
|
137
|
+
|
|
138
|
+
```markdown
|
|
139
|
+
The identification argument relies on a mobility restriction.
|
|
140
|
+
[Assumption 2, p. 12](source:ref-001)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`ref-001` resolves through `references.json`, which records the document, the
|
|
144
|
+
immutable PDF content hash, the one-based physical page number, the quoted
|
|
145
|
+
passage, and normalized highlight rectangles. Reference resolution validates
|
|
146
|
+
*where* a passage is, not whether it supports the claim — you assess that by
|
|
147
|
+
reading the evidence.
|
|
148
|
+
|
|
149
|
+
## Bibliography
|
|
150
|
+
|
|
151
|
+
**Write .bib** in the sidebar generates `references.bib` in the project
|
|
152
|
+
directory, and **Download** fetches the same file without writing it. There is
|
|
153
|
+
one entry per imported document — a `.bib` entry is a work, so the page each
|
|
154
|
+
reference points at travels in the `\cite[p.~12]{key}` command the export
|
|
155
|
+
returns alongside the entries, not in the entry itself.
|
|
156
|
+
|
|
157
|
+
Entries carry only what the PDF actually supplies. A paper with no author
|
|
158
|
+
metadata gets an entry with no `author` field and a warning saying so; nothing
|
|
159
|
+
is filled in from general knowledge. The output is deterministic, so exporting
|
|
160
|
+
an unchanged bibliography rewrites nothing, and a `.bib` you edited by hand is
|
|
161
|
+
copied into `.research/history/` before it is replaced.
|
|
162
|
+
|
|
163
|
+
## Development
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
uv venv && uv pip install -e . --group dev
|
|
167
|
+
npm --prefix frontend install
|
|
168
|
+
npm --prefix frontend run build # writes src/litmark/static/
|
|
169
|
+
uv run pytest
|
|
170
|
+
uv run litmark serve ./my-research --reload
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`npm run dev` runs the frontend with hot reload against a server started
|
|
174
|
+
separately on port 8765.
|
|
175
|
+
|
|
176
|
+
### Tests
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
uv run pytest # server and acceptance criteria
|
|
180
|
+
uv run pytest --runslow # plus: build the wheel, install it clean, serve it
|
|
181
|
+
npm --prefix frontend test # renderer unit tests, and the bundle run in a DOM
|
|
182
|
+
|
|
183
|
+
# Real-browser checks (A2 live preview, A6 highlight geometry under zoom).
|
|
184
|
+
# Point it at a project whose open note contains a source: citation.
|
|
185
|
+
litmark serve ./my-research &
|
|
186
|
+
LITMARK_BASE_URL=http://127.0.0.1:8765/ npm --prefix frontend run test:browser
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The browser suite drives headless Firefox over Marionette. Headless Chromium is
|
|
190
|
+
deliberately not used: under some sandboxes it cannot reach loopback HTTP — it
|
|
191
|
+
returns an empty document where `curl` succeeds — so a Chromium suite passes
|
|
192
|
+
without having checked anything.
|
|
193
|
+
|
|
194
|
+
## Scope
|
|
195
|
+
|
|
196
|
+
v0.1 covers ingestion of text-based PDFs, cited summaries, Markdown live-preview
|
|
197
|
+
editing, PDF evidence with highlights, chat with one agent backend, and manual
|
|
198
|
+
references. Deferred: OCR, URL downloads, Word/HTML ingestion, Zotero
|
|
199
|
+
integration, semantic search, multiple agent providers, arbitrary analysis
|
|
200
|
+
execution, multi-user collaboration, full mobile editing, and public hosting.
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT.
|
litmark-0.1.0/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Litmark
|
|
2
|
+
|
|
3
|
+
A local research application: import papers, read short cited summaries, write
|
|
4
|
+
Markdown notes, inspect the cited PDF passages, and chat with a coding agent
|
|
5
|
+
that can search the project and edit its files.
|
|
6
|
+
|
|
7
|
+
**Import papers → ask questions → write a sourced note → inspect the evidence → refine the note.**
|
|
8
|
+
|
|
9
|
+
Everything you write stays as ordinary files in a movable project directory:
|
|
10
|
+
`notes/*.md`, `documents/<id>/original.pdf`, `references.json`. The application
|
|
11
|
+
is a convenient view onto those files, not their owner.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
python -m pip install litmark # core reader and editor
|
|
17
|
+
python -m pip install 'litmark[claude]' # plus the agent adapter
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Requires Python 3.11+. Tested on macOS and Linux. The published wheel contains
|
|
21
|
+
the compiled browser assets — **you do not need Node, npm, or a second web
|
|
22
|
+
server to run this application.**
|
|
23
|
+
|
|
24
|
+
With `pipx` or `uv`:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pipx install litmark
|
|
28
|
+
uv tool install litmark
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Use
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
litmark init ./my-research
|
|
35
|
+
litmark serve ./my-research --open
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`serve` binds to `127.0.0.1`, prints the local URL, and serves exactly one
|
|
39
|
+
project per process. Reopening an existing project needs no initialization, and
|
|
40
|
+
`init` never overwrites existing material.
|
|
41
|
+
|
|
42
|
+
### Reaching it through a proxy or port-forward
|
|
43
|
+
|
|
44
|
+
By default the server binds to loopback *and* rejects any `Host` header that is
|
|
45
|
+
not a loopback name. That second check is what stops a malicious page from
|
|
46
|
+
using DNS rebinding to talk to your local server — but behind a reverse proxy
|
|
47
|
+
or a port-forward the header carries the proxy's hostname, so the check would
|
|
48
|
+
reject every real request. Widen it explicitly:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Bind beyond loopback; any Host header is then accepted.
|
|
52
|
+
litmark serve ./my-research --host 0.0.0.0
|
|
53
|
+
|
|
54
|
+
# Or name the proxy's hostname and keep the check strict.
|
|
55
|
+
litmark serve ./my-research --host 0.0.0.0 --allow-host papers.internal
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The per-process session credential embedded in the served page remains the
|
|
59
|
+
access control in both cases. On an untrusted network prefer an SSH tunnel,
|
|
60
|
+
which keeps the default loopback binding intact:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
ssh -N -L 8765:127.0.0.1:8765 you@the-machine
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Check your environment at any time:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
litmark doctor ./my-research
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`doctor` reports on the workspace, the optional agent dependency, the runtime
|
|
73
|
+
it needs, and whether authentication is configured — without printing secrets.
|
|
74
|
+
|
|
75
|
+
## Agent configuration
|
|
76
|
+
|
|
77
|
+
The agent backend is the [Claude Agent SDK for
|
|
78
|
+
Python](https://code.claude.com/docs/en/agent-sdk/python), behind a
|
|
79
|
+
provider-neutral adapter. Provider credentials and billing are separate from
|
|
80
|
+
installing this application; a consumer chat subscription does not by itself
|
|
81
|
+
grant SDK access.
|
|
82
|
+
|
|
83
|
+
The reader and editor work with no agent account at all. Import and text
|
|
84
|
+
extraction still run; automatic summaries wait until an agent is configured,
|
|
85
|
+
and the interface says so plainly.
|
|
86
|
+
|
|
87
|
+
## Project layout
|
|
88
|
+
|
|
89
|
+
| Path | Purpose |
|
|
90
|
+
| --- | --- |
|
|
91
|
+
| `project.toml` | Schema version and non-secret project settings. |
|
|
92
|
+
| `documents/<id>/original.pdf` | Original immutable PDF. |
|
|
93
|
+
| `documents/<id>/metadata.json` | Identity, hash, filename, title, page count, status. |
|
|
94
|
+
| `documents/<id>/pages.json` | Per-page text, character geometry, offset mapping. |
|
|
95
|
+
| `documents/<id>/summary.md` | Editable, cited summary. |
|
|
96
|
+
| `notes/*.md` | User and agent notes. |
|
|
97
|
+
| `references.json` | Versioned source registry. |
|
|
98
|
+
| `references.bib` | Generated BibTeX bibliography of the imported documents. |
|
|
99
|
+
| `.research/state.sqlite` | Conversations, events, jobs, run state, change log. |
|
|
100
|
+
| `.research/history/` | Previous file contents, for review and undo. |
|
|
101
|
+
| `.research/runs/` | Staged agent edits and run diagnostics. |
|
|
102
|
+
|
|
103
|
+
## Citations
|
|
104
|
+
|
|
105
|
+
A citation is an ordinary Markdown link with an application-specific
|
|
106
|
+
destination:
|
|
107
|
+
|
|
108
|
+
```markdown
|
|
109
|
+
The identification argument relies on a mobility restriction.
|
|
110
|
+
[Assumption 2, p. 12](source:ref-001)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`ref-001` resolves through `references.json`, which records the document, the
|
|
114
|
+
immutable PDF content hash, the one-based physical page number, the quoted
|
|
115
|
+
passage, and normalized highlight rectangles. Reference resolution validates
|
|
116
|
+
*where* a passage is, not whether it supports the claim — you assess that by
|
|
117
|
+
reading the evidence.
|
|
118
|
+
|
|
119
|
+
## Bibliography
|
|
120
|
+
|
|
121
|
+
**Write .bib** in the sidebar generates `references.bib` in the project
|
|
122
|
+
directory, and **Download** fetches the same file without writing it. There is
|
|
123
|
+
one entry per imported document — a `.bib` entry is a work, so the page each
|
|
124
|
+
reference points at travels in the `\cite[p.~12]{key}` command the export
|
|
125
|
+
returns alongside the entries, not in the entry itself.
|
|
126
|
+
|
|
127
|
+
Entries carry only what the PDF actually supplies. A paper with no author
|
|
128
|
+
metadata gets an entry with no `author` field and a warning saying so; nothing
|
|
129
|
+
is filled in from general knowledge. The output is deterministic, so exporting
|
|
130
|
+
an unchanged bibliography rewrites nothing, and a `.bib` you edited by hand is
|
|
131
|
+
copied into `.research/history/` before it is replaced.
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
uv venv && uv pip install -e . --group dev
|
|
137
|
+
npm --prefix frontend install
|
|
138
|
+
npm --prefix frontend run build # writes src/litmark/static/
|
|
139
|
+
uv run pytest
|
|
140
|
+
uv run litmark serve ./my-research --reload
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`npm run dev` runs the frontend with hot reload against a server started
|
|
144
|
+
separately on port 8765.
|
|
145
|
+
|
|
146
|
+
### Tests
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
uv run pytest # server and acceptance criteria
|
|
150
|
+
uv run pytest --runslow # plus: build the wheel, install it clean, serve it
|
|
151
|
+
npm --prefix frontend test # renderer unit tests, and the bundle run in a DOM
|
|
152
|
+
|
|
153
|
+
# Real-browser checks (A2 live preview, A6 highlight geometry under zoom).
|
|
154
|
+
# Point it at a project whose open note contains a source: citation.
|
|
155
|
+
litmark serve ./my-research &
|
|
156
|
+
LITMARK_BASE_URL=http://127.0.0.1:8765/ npm --prefix frontend run test:browser
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The browser suite drives headless Firefox over Marionette. Headless Chromium is
|
|
160
|
+
deliberately not used: under some sandboxes it cannot reach loopback HTTP — it
|
|
161
|
+
returns an empty document where `curl` succeeds — so a Chromium suite passes
|
|
162
|
+
without having checked anything.
|
|
163
|
+
|
|
164
|
+
## Scope
|
|
165
|
+
|
|
166
|
+
v0.1 covers ingestion of text-based PDFs, cited summaries, Markdown live-preview
|
|
167
|
+
editing, PDF evidence with highlights, chat with one agent backend, and manual
|
|
168
|
+
references. Deferred: OCR, URL downloads, Word/HTML ingestion, Zotero
|
|
169
|
+
integration, semantic search, multiple agent providers, arbitrary analysis
|
|
170
|
+
execution, multi-user collaboration, full mobile editing, and public hosting.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="litmark-token" content="">
|
|
7
|
+
<title>Litmark</title>
|
|
8
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="app"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|