dvm-mdview 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.
- dvm_mdview-0.1.0/.github/workflows/ci.yml +28 -0
- dvm_mdview-0.1.0/.github/workflows/publish.yml +56 -0
- dvm_mdview-0.1.0/.gitignore +8 -0
- dvm_mdview-0.1.0/AGENTS.md +43 -0
- dvm_mdview-0.1.0/CLAUDE.md +1 -0
- dvm_mdview-0.1.0/LICENSE +21 -0
- dvm_mdview-0.1.0/PKG-INFO +170 -0
- dvm_mdview-0.1.0/README.md +141 -0
- dvm_mdview-0.1.0/docs/dev/architecture.md +45 -0
- dvm_mdview-0.1.0/docs/dev/git.md +41 -0
- dvm_mdview-0.1.0/docs/dev/releasing.md +50 -0
- dvm_mdview-0.1.0/docs/dev/testing.md +35 -0
- dvm_mdview-0.1.0/pyproject.toml +58 -0
- dvm_mdview-0.1.0/src/mdview/__init__.py +4 -0
- dvm_mdview-0.1.0/src/mdview/__main__.py +3 -0
- dvm_mdview-0.1.0/src/mdview/_version.py +24 -0
- dvm_mdview-0.1.0/src/mdview/cli.py +64 -0
- dvm_mdview-0.1.0/src/mdview/gui.py +105 -0
- dvm_mdview-0.1.0/src/mdview/render.py +242 -0
- dvm_mdview-0.1.0/src/mdview/server.py +298 -0
- dvm_mdview-0.1.0/src/mdview/tui.py +514 -0
- dvm_mdview-0.1.0/src/mdview/web/app.css +172 -0
- dvm_mdview-0.1.0/src/mdview/web/app.js +638 -0
- dvm_mdview-0.1.0/src/mdview/web/index.html +64 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/LICENSE-katex +21 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/LICENSE-mermaid +21 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Main-Bold.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Main-Italic.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Main-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Math-Italic.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Script-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/katex.min.css +1 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/katex.min.js +1 -0
- dvm_mdview-0.1.0/src/mdview/web/vendor/mermaid.min.js +4329 -0
- dvm_mdview-0.1.0/tests/test_render.py +90 -0
- dvm_mdview-0.1.0/tests/test_server.py +121 -0
- dvm_mdview-0.1.0/tests/test_tui.py +111 -0
- dvm_mdview-0.1.0/uv.lock +743 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
14
|
+
python: ["3.10", "3.13"]
|
|
15
|
+
runs-on: ${{ matrix.os }}
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # the version is derived from git tags
|
|
20
|
+
- uses: astral-sh/setup-uv@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python }}
|
|
23
|
+
- run: uv sync
|
|
24
|
+
- run: uv run pytest -q
|
|
25
|
+
- run: uv build
|
|
26
|
+
- name: Run the built wheel the way uvx does
|
|
27
|
+
shell: bash
|
|
28
|
+
run: uvx --from dist/*.whl dvm-mdview --version
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Pushing a tag such as v1.2.3 builds version 1.2.3 (the version is derived
|
|
4
|
+
# from the tag), creates the GitHub release and uploads to PyPI.
|
|
5
|
+
#
|
|
6
|
+
# PyPI upload uses "trusted publishing": on pypi.org add this repository,
|
|
7
|
+
# workflow "publish.yml" and environment "pypi" as a trusted publisher for
|
|
8
|
+
# the project dvm-mdview. No API token is stored.
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
tags: ["v*"]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
- uses: astral-sh/setup-uv@v6
|
|
21
|
+
- run: uv build
|
|
22
|
+
- name: Check that the built version matches the tag
|
|
23
|
+
run: ls "dist/dvm_mdview-${GITHUB_REF_NAME#v}-py3-none-any.whl" "dist/dvm_mdview-${GITHUB_REF_NAME#v}.tar.gz"
|
|
24
|
+
- uses: actions/upload-artifact@v4
|
|
25
|
+
with:
|
|
26
|
+
name: dist
|
|
27
|
+
path: dist/
|
|
28
|
+
if-no-files-found: error
|
|
29
|
+
|
|
30
|
+
github-release:
|
|
31
|
+
needs: build
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
permissions:
|
|
34
|
+
contents: write
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/download-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
- run: gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "$GITHUB_REF_NAME" --generate-notes --verify-tag
|
|
41
|
+
env:
|
|
42
|
+
GH_TOKEN: ${{ github.token }}
|
|
43
|
+
|
|
44
|
+
pypi:
|
|
45
|
+
needs: build
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
environment: pypi
|
|
48
|
+
permissions:
|
|
49
|
+
id-token: write
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/download-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: dist
|
|
54
|
+
path: dist/
|
|
55
|
+
- uses: astral-sh/setup-uv@v6
|
|
56
|
+
- run: uv publish
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# mdview
|
|
2
|
+
|
|
3
|
+
Markdown viewer and editor with live preview. One Python package (`dvm-mdview` on PyPI, import
|
|
4
|
+
name `mdview`) with a graphical UI and a terminal UI that share one renderer.
|
|
5
|
+
|
|
6
|
+
## Rules that always apply
|
|
7
|
+
|
|
8
|
+
1. **No mention of Claude, Anthropic, AI or AI assistance** in commit messages (including trailers),
|
|
9
|
+
pull request or issue text, comments, or any file. Only `AGENTS.md`, `CLAUDE.md` and
|
|
10
|
+
`docs/dev/git.md` may mention it, because they state the rule.
|
|
11
|
+
2. **Commit as the repository owner**, never under a tool or bot identity. Set author and committer
|
|
12
|
+
on every commit; the exact commands are in `docs/dev/git.md`.
|
|
13
|
+
3. **Must run on macOS and Windows through `uvx`.** Pure Python only, no build step, no new
|
|
14
|
+
dependency without wheels for both.
|
|
15
|
+
|
|
16
|
+
## Everyday commands
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv sync
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv run pytest -q
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv run mdview --browser README.md
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv run mdview --tui README.md
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Read more only when the task needs it
|
|
35
|
+
|
|
36
|
+
| If you are about to... | Read |
|
|
37
|
+
|---|---|
|
|
38
|
+
| commit, push, or write a pull request | `docs/dev/git.md` |
|
|
39
|
+
| change rendering, the backend, or either UI | `docs/dev/architecture.md` |
|
|
40
|
+
| add or debug tests, or check a UI change by hand | `docs/dev/testing.md` |
|
|
41
|
+
| cut a release or update the bundled Mermaid / KaTeX | `docs/dev/releasing.md` |
|
|
42
|
+
|
|
43
|
+
User-facing behaviour, options and keys are documented in `README.md`; keep it in step with changes.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
dvm_mdview-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 divyavanmahajan
|
|
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.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dvm-mdview
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Markdown viewer and live-preview editor with a graphical UI and a terminal UI
|
|
5
|
+
Project-URL: Homepage, https://github.com/divyavanmahajan/mdview
|
|
6
|
+
Project-URL: Issues, https://github.com/divyavanmahajan/mdview/issues
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
License-File: src/mdview/web/vendor/LICENSE-katex
|
|
10
|
+
License-File: src/mdview/web/vendor/LICENSE-mermaid
|
|
11
|
+
Keywords: editor,gui,markdown,preview,tui,viewer
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Environment :: MacOS X
|
|
14
|
+
Classifier: Environment :: Win32 (MS Windows)
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Topic :: Text Editors
|
|
20
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: linkify-it-py>=2.0
|
|
23
|
+
Requires-Dist: markdown-it-py>=3.0
|
|
24
|
+
Requires-Dist: mdit-py-plugins>=0.4.1
|
|
25
|
+
Requires-Dist: pygments>=2.17
|
|
26
|
+
Requires-Dist: pywebview>=5.3
|
|
27
|
+
Requires-Dist: textual>=3.0
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# mdview
|
|
31
|
+
|
|
32
|
+
A Markdown viewer and editor with live preview, for macOS, Windows and Linux.
|
|
33
|
+
It has two interfaces that share one renderer:
|
|
34
|
+
|
|
35
|
+
- a **graphical UI** in a native window, and
|
|
36
|
+
- a **terminal UI** for use in a shell or over SSH.
|
|
37
|
+
|
|
38
|
+
Published on PyPI as **`dvm-mdview`**.
|
|
39
|
+
|
|
40
|
+
## Run it
|
|
41
|
+
|
|
42
|
+
With [uv](https://docs.astral.sh/uv/) installed, nothing else needs installing:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uvx dvm-mdview README.md
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uvx dvm-mdview --tui README.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or install it, which gives you the shorter `mdview` command:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv tool install dvm-mdview
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
mdview notes.md
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Option | Effect |
|
|
63
|
+
|---|---|
|
|
64
|
+
| *(none)* | Graphical UI. Falls back to the terminal UI when there is no display. |
|
|
65
|
+
| `-t`, `--tui` | Terminal UI. |
|
|
66
|
+
| `-b`, `--browser` | Graphical UI in your default browser instead of a native window. |
|
|
67
|
+
| `-e`, `--edit` | Start with editor and preview side by side. |
|
|
68
|
+
| `-r`, `--read` | Start with the preview only. |
|
|
69
|
+
|
|
70
|
+
A file that does not exist yet is opened empty and created when you save.
|
|
71
|
+
Passing a folder opens its `README.md`.
|
|
72
|
+
|
|
73
|
+
## Features
|
|
74
|
+
|
|
75
|
+
- **Live preview.** View, Split (editor and preview side by side) and Edit layouts. The preview
|
|
76
|
+
updates as you type.
|
|
77
|
+
- **Link navigation.** Links to other Markdown files open in place, with back and forward history.
|
|
78
|
+
Links to headings scroll to them. Web links open in your browser. A link to a file that does not
|
|
79
|
+
exist starts a new document at that path.
|
|
80
|
+
- **Markdown.** CommonMark with GitHub tables, task lists, strikethrough and autolinks, plus
|
|
81
|
+
footnotes, YAML front matter and heading anchors.
|
|
82
|
+
- **Code highlighting** in both interfaces.
|
|
83
|
+
- **Mermaid diagrams and LaTeX math** rendered in the graphical UI (shown as source in the terminal).
|
|
84
|
+
Both work offline; the libraries are bundled.
|
|
85
|
+
- **Safe saving.** Files are written atomically, you are asked before unsaved changes are lost, and
|
|
86
|
+
a file changed by another program is reloaded (or flagged, if you have edits of your own).
|
|
87
|
+
- Outline sidebar, light and dark themes, and relative images in the graphical UI.
|
|
88
|
+
|
|
89
|
+
## Keys
|
|
90
|
+
|
|
91
|
+
### Graphical UI
|
|
92
|
+
|
|
93
|
+
`Cmd` replaces `Ctrl` on macOS.
|
|
94
|
+
|
|
95
|
+
| Key | Action |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `Ctrl+E` | Cycle View / Split / Edit |
|
|
98
|
+
| `Ctrl+S`, `Ctrl+Shift+S` | Save, Save as |
|
|
99
|
+
| `Ctrl+O`, `Ctrl+N` | Open, New |
|
|
100
|
+
| `Alt+Left`, `Alt+Right` | Back, Forward |
|
|
101
|
+
| `Ctrl+B`, `Ctrl+I`, `Ctrl+K` | Bold, italic, link |
|
|
102
|
+
|
|
103
|
+
### Terminal UI
|
|
104
|
+
|
|
105
|
+
| Key | Action |
|
|
106
|
+
|---|---|
|
|
107
|
+
| `Ctrl+E` | Cycle View / Split / Edit |
|
|
108
|
+
| `F6` | Switch between editor and preview |
|
|
109
|
+
| `Ctrl+S` | Save |
|
|
110
|
+
| `Ctrl+O` | Open |
|
|
111
|
+
| `Ctrl+L` | List the document's links and follow one |
|
|
112
|
+
| `Alt+Left`, `Alt+Right` | Back, Forward |
|
|
113
|
+
| `Ctrl+P` | Command palette (Save as, New, theme, ...) |
|
|
114
|
+
| `Ctrl+Q` | Quit |
|
|
115
|
+
|
|
116
|
+
Links can also be clicked with the mouse. While the preview has focus, single keys work too:
|
|
117
|
+
`e` edit, `l` links, `o` open, `b` back, `f` forward, `j`/`k` scroll, `q` quit.
|
|
118
|
+
|
|
119
|
+
## How it is built
|
|
120
|
+
|
|
121
|
+
| Part | Library |
|
|
122
|
+
|---|---|
|
|
123
|
+
| Markdown parsing | [markdown-it-py](https://github.com/executablebooks/markdown-it-py) and [mdit-py-plugins](https://github.com/executablebooks/mdit-py-plugins) |
|
|
124
|
+
| Code highlighting | [Pygments](https://pygments.org/) |
|
|
125
|
+
| Native window | [pywebview](https://pywebview.flowrl.com/) (WebKit on macOS, WebView2 on Windows) |
|
|
126
|
+
| Terminal UI | [Textual](https://textual.textualize.io/) |
|
|
127
|
+
| Diagrams and math | [Mermaid](https://mermaid.js.org/) and [KaTeX](https://katex.org/), bundled |
|
|
128
|
+
|
|
129
|
+
The graphical UI is a small web app served by a local backend (`src/mdview/server.py`) that listens
|
|
130
|
+
only on `127.0.0.1` and requires a per-run secret token. Raw HTML in a document is displayed, but a
|
|
131
|
+
strict Content-Security-Policy stops any script in it from running.
|
|
132
|
+
|
|
133
|
+
On Linux a native window needs GTK or Qt bindings for pywebview; without them mdview uses the
|
|
134
|
+
browser automatically.
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uv sync
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
uv run pytest
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
uv run mdview README.md
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Releasing
|
|
151
|
+
|
|
152
|
+
The version comes from the git tag, so the tag and the PyPI version always match.
|
|
153
|
+
|
|
154
|
+
1. Once, on pypi.org, add this repository as a *trusted publisher* for `dvm-mdview`
|
|
155
|
+
(workflow `publish.yml`, environment `pypi`).
|
|
156
|
+
2. Tag a commit on `main` and push the tag:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git tag v1.2.3
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
git push origin v1.2.3
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The `Release` workflow builds version 1.2.3, creates the GitHub release and uploads to PyPI.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT. Bundled Mermaid and KaTeX are MIT licensed; their licences are in `src/mdview/web/vendor/`.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# mdview
|
|
2
|
+
|
|
3
|
+
A Markdown viewer and editor with live preview, for macOS, Windows and Linux.
|
|
4
|
+
It has two interfaces that share one renderer:
|
|
5
|
+
|
|
6
|
+
- a **graphical UI** in a native window, and
|
|
7
|
+
- a **terminal UI** for use in a shell or over SSH.
|
|
8
|
+
|
|
9
|
+
Published on PyPI as **`dvm-mdview`**.
|
|
10
|
+
|
|
11
|
+
## Run it
|
|
12
|
+
|
|
13
|
+
With [uv](https://docs.astral.sh/uv/) installed, nothing else needs installing:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uvx dvm-mdview README.md
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uvx dvm-mdview --tui README.md
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or install it, which gives you the shorter `mdview` command:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv tool install dvm-mdview
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
mdview notes.md
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Option | Effect |
|
|
34
|
+
|---|---|
|
|
35
|
+
| *(none)* | Graphical UI. Falls back to the terminal UI when there is no display. |
|
|
36
|
+
| `-t`, `--tui` | Terminal UI. |
|
|
37
|
+
| `-b`, `--browser` | Graphical UI in your default browser instead of a native window. |
|
|
38
|
+
| `-e`, `--edit` | Start with editor and preview side by side. |
|
|
39
|
+
| `-r`, `--read` | Start with the preview only. |
|
|
40
|
+
|
|
41
|
+
A file that does not exist yet is opened empty and created when you save.
|
|
42
|
+
Passing a folder opens its `README.md`.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Live preview.** View, Split (editor and preview side by side) and Edit layouts. The preview
|
|
47
|
+
updates as you type.
|
|
48
|
+
- **Link navigation.** Links to other Markdown files open in place, with back and forward history.
|
|
49
|
+
Links to headings scroll to them. Web links open in your browser. A link to a file that does not
|
|
50
|
+
exist starts a new document at that path.
|
|
51
|
+
- **Markdown.** CommonMark with GitHub tables, task lists, strikethrough and autolinks, plus
|
|
52
|
+
footnotes, YAML front matter and heading anchors.
|
|
53
|
+
- **Code highlighting** in both interfaces.
|
|
54
|
+
- **Mermaid diagrams and LaTeX math** rendered in the graphical UI (shown as source in the terminal).
|
|
55
|
+
Both work offline; the libraries are bundled.
|
|
56
|
+
- **Safe saving.** Files are written atomically, you are asked before unsaved changes are lost, and
|
|
57
|
+
a file changed by another program is reloaded (or flagged, if you have edits of your own).
|
|
58
|
+
- Outline sidebar, light and dark themes, and relative images in the graphical UI.
|
|
59
|
+
|
|
60
|
+
## Keys
|
|
61
|
+
|
|
62
|
+
### Graphical UI
|
|
63
|
+
|
|
64
|
+
`Cmd` replaces `Ctrl` on macOS.
|
|
65
|
+
|
|
66
|
+
| Key | Action |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `Ctrl+E` | Cycle View / Split / Edit |
|
|
69
|
+
| `Ctrl+S`, `Ctrl+Shift+S` | Save, Save as |
|
|
70
|
+
| `Ctrl+O`, `Ctrl+N` | Open, New |
|
|
71
|
+
| `Alt+Left`, `Alt+Right` | Back, Forward |
|
|
72
|
+
| `Ctrl+B`, `Ctrl+I`, `Ctrl+K` | Bold, italic, link |
|
|
73
|
+
|
|
74
|
+
### Terminal UI
|
|
75
|
+
|
|
76
|
+
| Key | Action |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `Ctrl+E` | Cycle View / Split / Edit |
|
|
79
|
+
| `F6` | Switch between editor and preview |
|
|
80
|
+
| `Ctrl+S` | Save |
|
|
81
|
+
| `Ctrl+O` | Open |
|
|
82
|
+
| `Ctrl+L` | List the document's links and follow one |
|
|
83
|
+
| `Alt+Left`, `Alt+Right` | Back, Forward |
|
|
84
|
+
| `Ctrl+P` | Command palette (Save as, New, theme, ...) |
|
|
85
|
+
| `Ctrl+Q` | Quit |
|
|
86
|
+
|
|
87
|
+
Links can also be clicked with the mouse. While the preview has focus, single keys work too:
|
|
88
|
+
`e` edit, `l` links, `o` open, `b` back, `f` forward, `j`/`k` scroll, `q` quit.
|
|
89
|
+
|
|
90
|
+
## How it is built
|
|
91
|
+
|
|
92
|
+
| Part | Library |
|
|
93
|
+
|---|---|
|
|
94
|
+
| Markdown parsing | [markdown-it-py](https://github.com/executablebooks/markdown-it-py) and [mdit-py-plugins](https://github.com/executablebooks/mdit-py-plugins) |
|
|
95
|
+
| Code highlighting | [Pygments](https://pygments.org/) |
|
|
96
|
+
| Native window | [pywebview](https://pywebview.flowrl.com/) (WebKit on macOS, WebView2 on Windows) |
|
|
97
|
+
| Terminal UI | [Textual](https://textual.textualize.io/) |
|
|
98
|
+
| Diagrams and math | [Mermaid](https://mermaid.js.org/) and [KaTeX](https://katex.org/), bundled |
|
|
99
|
+
|
|
100
|
+
The graphical UI is a small web app served by a local backend (`src/mdview/server.py`) that listens
|
|
101
|
+
only on `127.0.0.1` and requires a per-run secret token. Raw HTML in a document is displayed, but a
|
|
102
|
+
strict Content-Security-Policy stops any script in it from running.
|
|
103
|
+
|
|
104
|
+
On Linux a native window needs GTK or Qt bindings for pywebview; without them mdview uses the
|
|
105
|
+
browser automatically.
|
|
106
|
+
|
|
107
|
+
## Development
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
uv sync
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv run pytest
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
uv run mdview README.md
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Releasing
|
|
122
|
+
|
|
123
|
+
The version comes from the git tag, so the tag and the PyPI version always match.
|
|
124
|
+
|
|
125
|
+
1. Once, on pypi.org, add this repository as a *trusted publisher* for `dvm-mdview`
|
|
126
|
+
(workflow `publish.yml`, environment `pypi`).
|
|
127
|
+
2. Tag a commit on `main` and push the tag:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git tag v1.2.3
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
git push origin v1.2.3
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The `Release` workflow builds version 1.2.3, creates the GitHub release and uploads to PyPI.
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT. Bundled Mermaid and KaTeX are MIT licensed; their licences are in `src/mdview/web/vendor/`.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
All code is in `src/mdview/`.
|
|
4
|
+
|
|
5
|
+
| File | Role |
|
|
6
|
+
|---|---|
|
|
7
|
+
| `cli.py` | Argument parsing; picks terminal UI, native window or browser. |
|
|
8
|
+
| `render.py` | Everything both UIs share: Markdown to HTML, link resolution, link extraction, reading and atomic writing of files. |
|
|
9
|
+
| `server.py` | Loopback HTTP backend for the graphical UI. |
|
|
10
|
+
| `gui.py` | Hosts the web app in a pywebview window, or the default browser as fallback. |
|
|
11
|
+
| `tui.py` | Textual app. |
|
|
12
|
+
| `web/` | The graphical UI: `index.html`, `app.js`, `app.css`, and `vendor/` (bundled Mermaid and KaTeX). |
|
|
13
|
+
|
|
14
|
+
## Rendering
|
|
15
|
+
|
|
16
|
+
- `render.py` builds one markdown-it-py parser (`gfm-like` preset plus front matter, footnotes,
|
|
17
|
+
task lists, anchors, dollar math). Pygments highlights fenced code in Python.
|
|
18
|
+
- Mermaid fences become `<pre class="mermaid">` and math becomes `.math` elements; `app.js` renders
|
|
19
|
+
them in the page with the bundled libraries.
|
|
20
|
+
- The terminal UI uses Textual's own `Markdown` widget. `for_terminal()` rewrites front matter into
|
|
21
|
+
a table first. Mermaid and math stay as source there.
|
|
22
|
+
- Link behaviour must stay identical in both UIs: change `resolve_link()`, not the callers.
|
|
23
|
+
|
|
24
|
+
## Graphical UI
|
|
25
|
+
|
|
26
|
+
- The page calls the backend with `POST /api/<name>` and JSON. Each endpoint is a method
|
|
27
|
+
`api_<name>` on `Handler` in `server.py`.
|
|
28
|
+
- The window host installs hooks on `AppState` (`native_dialog`, `set_title`, `close_window`).
|
|
29
|
+
When they are absent (browser mode) the page uses its own in-page file chooser.
|
|
30
|
+
- `--browser` mode serves exactly the same app, which is how the UI is tested without a display.
|
|
31
|
+
|
|
32
|
+
## Security constraints (do not weaken)
|
|
33
|
+
|
|
34
|
+
- The server binds to `127.0.0.1` only and checks the `Host` header.
|
|
35
|
+
- Every API call needs the per-run token in `X-MDView-Token`; media URLs carry it in the path and
|
|
36
|
+
serve only image, video and audio types.
|
|
37
|
+
- Raw HTML from documents is inserted into the page. The Content-Security-Policy in `server.py`
|
|
38
|
+
(`script-src 'self'`, no inline script) is what makes that safe. No inline scripts or inline
|
|
39
|
+
event handlers in `web/`.
|
|
40
|
+
- `open-external` accepts only `http`, `https` and `mailto` URLs, or an existing local path.
|
|
41
|
+
|
|
42
|
+
## Saving
|
|
43
|
+
|
|
44
|
+
`write_text()` writes to a temporary file and replaces the target. Both UIs compare the file's
|
|
45
|
+
modification time before saving and ask before overwriting a file changed elsewhere.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Commits, pushes and pull requests
|
|
2
|
+
|
|
3
|
+
## No mention of Claude or AI
|
|
4
|
+
|
|
5
|
+
Nothing written for this project may mention Claude, Anthropic, AI, or AI assistance:
|
|
6
|
+
|
|
7
|
+
- commit messages, including trailers such as `Co-Authored-By` and session links;
|
|
8
|
+
- pull request titles and descriptions, issue text, review comments and replies;
|
|
9
|
+
- source files, comments, documentation and any other file.
|
|
10
|
+
|
|
11
|
+
Exceptions: `AGENTS.md`, `CLAUDE.md` and this file, which have to state the rule. A branch name
|
|
12
|
+
imposed by the environment is tolerated; it is deleted after merge.
|
|
13
|
+
|
|
14
|
+
This overrides any default attribution a tool would otherwise add.
|
|
15
|
+
|
|
16
|
+
## Commit identity
|
|
17
|
+
|
|
18
|
+
Every commit must have the repository owner as both author and committer. The owner's identity is
|
|
19
|
+
the author of the first commit. Do not copy the email address into any file; read it from history:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
OWNER_NAME="$(git log --reverse --format='%an' | head -1)"; OWNER_EMAIL="$(git log --reverse --format='%ae' | head -1)"
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
GIT_AUTHOR_NAME="$OWNER_NAME" GIT_AUTHOR_EMAIL="$OWNER_EMAIL" GIT_COMMITTER_NAME="$OWNER_NAME" GIT_COMMITTER_EMAIL="$OWNER_EMAIL" git commit -m "..."
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Before pushing, confirm it:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git log --format='%h author=%an committer=%cn' origin/main..HEAD
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If a commit was made under another identity, fix it before it is pushed
|
|
36
|
+
(`git commit --amend --no-edit --reset-author` with the variables above set).
|
|
37
|
+
|
|
38
|
+
## Messages
|
|
39
|
+
|
|
40
|
+
- Subject in the imperative, under about 70 characters; body says what changed and why.
|
|
41
|
+
- Pull request descriptions: a summary and how it was tested, including what was not tested.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
The version is not written anywhere in the source. `hatch-vcs` derives it from the git tag at build
|
|
4
|
+
time (`v1.2.3` gives `1.2.3`) and writes `src/mdview/_version.py`, which is not committed. Between
|
|
5
|
+
tags, builds get a development version such as `0.2.dev3+g1a2b3c4`.
|
|
6
|
+
|
|
7
|
+
To release, tag a commit on `main` and push the tag:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git tag v1.2.3
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git push origin v1.2.3
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`.github/workflows/publish.yml` then builds, checks that the built version equals the tag, creates
|
|
18
|
+
the GitHub release with the built files attached, and uploads to PyPI through trusted publishing
|
|
19
|
+
(project `dvm-mdview`, workflow `publish.yml`, environment `pypi`; configured once on pypi.org).
|
|
20
|
+
The GitHub release and the PyPI upload are separate jobs, so one can be re-run without the other.
|
|
21
|
+
|
|
22
|
+
Do not create the release by hand in the GitHub UI; the workflow would then fail to create it.
|
|
23
|
+
|
|
24
|
+
To check a build locally:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv build
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uvx --from dist/*.whl dvm-mdview --version
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Builds need git history and tags; in CI, check out with `fetch-depth: 0`.
|
|
35
|
+
|
|
36
|
+
The wheel must contain `mdview/web/` including `vendor/`; `uv build` output can be checked with
|
|
37
|
+
`python -m zipfile -l dist/*.whl`.
|
|
38
|
+
|
|
39
|
+
## Updating bundled Mermaid and KaTeX
|
|
40
|
+
|
|
41
|
+
Currently Mermaid 12.3.0 and KaTeX 0.19.1. They are copied from the npm packages, not built:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm pack mermaid katex
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
From the unpacked packages copy `mermaid/dist/mermaid.min.js`, `katex/dist/katex.min.js`,
|
|
48
|
+
`katex/dist/katex.min.css`, `katex/dist/fonts/*.woff2` and both `LICENSE` files into
|
|
49
|
+
`src/mdview/web/vendor/` (licences as `LICENSE-mermaid` and `LICENSE-katex`). Update the versions
|
|
50
|
+
above, then check a Mermaid block and a math block in the graphical UI.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
```bash
|
|
4
|
+
uv run pytest -q
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
| File | Covers |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `tests/test_render.py` | Rendering features, slugs, link resolution, file reading and writing. |
|
|
10
|
+
| `tests/test_server.py` | Backend endpoints, token and host checks, save conflicts, media serving. |
|
|
11
|
+
| `tests/test_tui.py` | Terminal UI through Textual's pilot. |
|
|
12
|
+
|
|
13
|
+
## Terminal UI tests
|
|
14
|
+
|
|
15
|
+
Actions that open a dialog run in a worker that waits for the dialog. Do not call
|
|
16
|
+
`app.workers.wait_for_complete()` while a dialog is open; it never returns. Use `pilot.pause()`,
|
|
17
|
+
then act on the dialog.
|
|
18
|
+
|
|
19
|
+
## Checking the graphical UI
|
|
20
|
+
|
|
21
|
+
There are no automated browser tests in the repository. To check a change, run the app in browser
|
|
22
|
+
mode and drive it with a browser (by hand or with Playwright):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv run mdview --browser --no-open --port 8765 README.md
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Wait for `body[data-ready]` before interacting. Check at least: live preview while typing, a link to
|
|
29
|
+
another file and back, an anchor link, save, and a Mermaid block and a math block.
|
|
30
|
+
|
|
31
|
+
## What cannot be tested on Linux without a display
|
|
32
|
+
|
|
33
|
+
The native pywebview window, native file dialogs, and the close prompt with unsaved changes. These
|
|
34
|
+
need a manual check on macOS and Windows. CI runs the test suite on all three systems but does not
|
|
35
|
+
open a window.
|