Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/rules/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ paths:

When shipping a user-facing feature, update in the same commit:

- **`README.md`** — add a bullet under the relevant `## Features` subsection; if the feature adds a keyboard shortcut, add a row to the `## Keyboard Shortcuts` table
- **`README.md`** — add a bullet under the relevant `## Features` subsection
- **`samples/README.md`** — showcase the feature where applicable (new markdown syntax gets a demo section; new shortcuts go in the shortcuts table). The `samples/` folder also doubles as a demo workspace, so add or update sibling files when introducing workspace-level features (wikilinks, backlinks, etc.)

Treat these as part of the feature's definition of done, not a follow-up.
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,24 @@ jobs:
working-directory: src-tauri
run: cargo clippy -- -D warnings

validate-packaging:
name: Validate packaging metadata
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install validators
run: |
sudo apt-get update
sudo apt-get install -y appstream desktop-file-utils python3-yaml
- name: Validate Flatpak AppStream metainfo
run: appstreamcli validate --no-net flatpak/com.hamidfzm.glyph.metainfo.xml
- name: Validate Flatpak desktop entry
run: desktop-file-validate flatpak/com.hamidfzm.glyph.desktop
- name: Validate YAML manifests parse
run: |
python3 -c "import yaml,sys; yaml.safe_load(open('flatpak/com.hamidfzm.glyph.yml'))"
python3 -c "import yaml,sys; yaml.safe_load(open('snap/snapcraft.yaml'))"

build:
name: Build (${{ matrix.platform }})
needs: [lint, typecheck, test-frontend, test-rust, clippy]
Expand Down
134 changes: 134 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ jobs:

### Linux
```bash
# Flathub (Flatpak)
flatpak install flathub com.hamidfzm.glyph

# Snap Store
sudo snap install glyph

# Arch (AUR)
yay -S glyph-md-bin

Expand Down Expand Up @@ -134,6 +140,12 @@ jobs:

### Linux
```bash
# Flathub (Flatpak)
flatpak install flathub com.hamidfzm.glyph

# Snap Store
sudo snap install glyph

# Arch (AUR)
yay -S glyph-md-bin

Expand Down Expand Up @@ -625,3 +637,125 @@ jobs:
git commit -m "chore: update packages to $VERSION"
git push
}

publish-snap:
needs: build
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-22.04
- arch: arm64
runner: ubuntu-22.04-arm
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v6
# The snap is built from the released .deb (see snap/snapcraft.yaml), so
# this job needs no toolchain. It is a no-op until the maintainer adds the
# SNAPCRAFT_STORE_CREDENTIALS secret (snapcraft export-login), so it never
# fails releases before the Snap Store account is set up.
- name: Check for store credentials
id: gate
env:
CREDS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
run: |
if [ -n "$CREDS" ]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
else
echo "No SNAPCRAFT_STORE_CREDENTIALS secret; skipping snap publish."
echo "enabled=false" >> "$GITHUB_OUTPUT"
fi
# snapcraft.yaml discovers the latest release and pulls the .deb for the
# build architecture. This job runs after the release is published, so
# "latest" is the tag that triggered this run. (Host env vars are not
# forwarded into the snapcraft build instance, so the version can't be
# passed that way.)
- name: Build snap
if: steps.gate.outputs.enabled == 'true'
id: build
uses: snapcore/action-build@v1
- name: Publish snap to stable
if: steps.gate.outputs.enabled == 'true'
uses: snapcore/action-publish@v1
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
with:
snap: ${{ steps.build.outputs.snap }}
release: stable

publish-flathub:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# The canonical Flatpak manifest lives in this repo (flatpak/). Flathub
# builds from its own app repo (flathub/com.hamidfzm.glyph), so each
# release we rewrite the version + per-arch sha256 in the manifest and
# push it (with the desktop + metainfo files) to that repo, whose
# buildbot then builds and publishes. No-op until FLATHUB_TOKEN (a PAT
# with push access to the Flathub app repo) is set, so it never fails a
# release before the Flathub submission is accepted.
- name: Check for Flathub token
id: gate
env:
TOKEN: ${{ secrets.FLATHUB_TOKEN }}
run: |
if [ -n "$TOKEN" ]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
else
echo "No FLATHUB_TOKEN secret; skipping Flathub publish."
echo "enabled=false" >> "$GITHUB_OUTPUT"
fi
- name: Get version
if: steps.gate.outputs.enabled == 'true'
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Download debs and compute sha256
if: steps.gate.outputs.enabled == 'true'
id: sha
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${{ steps.version.outputs.version }}"
gh release download "${{ github.ref_name }}" \
--repo "${{ github.repository }}" \
--pattern "Glyph_${VERSION}_amd64.deb" \
--pattern "Glyph_${VERSION}_arm64.deb"
echo "amd64=$(sha256sum "Glyph_${VERSION}_amd64.deb" | awk '{print $1}')" >> "$GITHUB_OUTPUT"
echo "arm64=$(sha256sum "Glyph_${VERSION}_arm64.deb" | awk '{print $1}')" >> "$GITHUB_OUTPUT"
- name: Update manifest and push to the Flathub app repo
if: steps.gate.outputs.enabled == 'true'
env:
GH_TOKEN: ${{ secrets.FLATHUB_TOKEN }}
run: |
VERSION="${{ steps.version.outputs.version }}"
SHA_AMD64="${{ steps.sha.outputs.amd64 }}"
SHA_ARM64="${{ steps.sha.outputs.arm64 }}"

# Rewrite the version in both download URLs and the per-arch sha256.
# Each `url:` line is immediately followed by its `sha256:` line, so
# tag the arch on the url and replace the next sha256 accordingly.
awk -v v="$VERSION" -v a="$SHA_AMD64" -v r="$SHA_ARM64" '
/releases\/download\/.*_amd64\.deb/ { sub(/v[0-9][0-9.]*\/Glyph_[0-9][0-9.]*_/, "v" v "/Glyph_" v "_"); arch="amd64" }
/releases\/download\/.*_arm64\.deb/ { sub(/v[0-9][0-9.]*\/Glyph_[0-9][0-9.]*_/, "v" v "/Glyph_" v "_"); arch="arm64" }
/^[[:space:]]*sha256:/ {
if (arch == "amd64") { sub(/sha256:.*/, "sha256: " a); arch="" }
else if (arch == "arm64") { sub(/sha256:.*/, "sha256: " r); arch="" }
}
{ print }
' flatpak/com.hamidfzm.glyph.yml > /tmp/manifest.yml

git clone "https://x-access-token:${GH_TOKEN}@github.com/flathub/com.hamidfzm.glyph.git" /tmp/flathub
cp /tmp/manifest.yml /tmp/flathub/com.hamidfzm.glyph.yml
cp flatpak/com.hamidfzm.glyph.desktop /tmp/flathub/
cp flatpak/com.hamidfzm.glyph.metainfo.xml /tmp/flathub/

cd /tmp/flathub
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add -A
git diff --cached --quiet || {
git commit -m "Update to $VERSION"
git push
}
33 changes: 13 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ scoop bucket add hamidfzm https://github.com/hamidfzm/scoop-bucket
scoop install glyph
```

### Linux (Flathub)

```bash
flatpak install flathub com.hamidfzm.glyph
flatpak run com.hamidfzm.glyph
```

### Linux (Snap)

```bash
sudo snap install glyph
```

### Arch Linux (AUR)

```bash
Expand Down Expand Up @@ -192,26 +205,6 @@ pnpm check # Lint + format + organize imports
cd src-tauri && cargo clippy # Lint Rust
```

## Keyboard Shortcuts

| Shortcut | Action |
|----------|--------|
| `Cmd+O` / `Ctrl+O` | Open file(s) |
| `Cmd+Shift+O` / `Ctrl+Shift+O` | Open folder |
| `Cmd+K` / `Ctrl+K` | Command palette (files, headings, app actions) |
| `Cmd+P` / `Ctrl+P` | Print / Export to PDF |
| `Cmd+F` / `Ctrl+F` | Find in document |
| `Cmd+=` / `Ctrl+=` | Zoom in |
| `Cmd+-` / `Ctrl+-` | Zoom out |
| `Cmd+0` / `Ctrl+0` | Reset zoom |
| `Cmd+Z` / `Ctrl+Z` | Undo last in-document edit (e.g. task checkbox toggle) |
| `Cmd+Shift+Z` / `Ctrl+Shift+Z` (also `Ctrl+Y` on Windows/Linux) | Redo |
| `Cmd+B` / `Ctrl+B` | Toggle files sidebar |
| `Cmd+\` / `Ctrl+\` | Toggle outline sidebar |
| `Cmd+,` / `Ctrl+,` | Settings |
| `Cmd+W` / `Ctrl+W` | Close tab |
| `Cmd+Shift+W` / `Ctrl+Shift+W` | Close window |

## Comparison with Other Markdown Apps

Glyph is built around speed, native feel, and offline-first usage. The tables below compare its current capabilities against widely used markdown apps. Items marked "planned" track to issues on the [roadmap](https://github.com/hamidfzm/glyph/issues).
Expand Down
13 changes: 13 additions & 0 deletions flatpak/com.hamidfzm.glyph.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Desktop Entry]
Type=Application
Name=Glyph
GenericName=Markdown Viewer
Comment=Cross-platform markdown viewer and editor
Exec=glyph %F
Icon=com.hamidfzm.glyph
Terminal=false
Categories=Office;Utility;TextEditor;
MimeType=text/markdown;
Keywords=markdown;md;viewer;editor;notes;
StartupNotify=true
StartupWMClass=Glyph
76 changes: 76 additions & 0 deletions flatpak/com.hamidfzm.glyph.metainfo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Maintainer: hamidfzm <hamidfzm@users.noreply.github.com> -->
<component type="desktop-application">
<id>com.hamidfzm.glyph</id>

<name>Glyph</name>
<summary>Cross-platform markdown viewer and editor</summary>

<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license>

<developer id="com.hamidfzm">
<name>hamidfzm</name>
</developer>

<description>
<p>
Glyph is a modern, cross-platform markdown viewer and editor with
platform-native styling. Built with Tauri v2, React 19, and TypeScript.
</p>
<p>Features:</p>
<ul>
<li>GitHub Flavored Markdown with syntax highlighting</li>
<li>KaTeX math and Mermaid diagrams</li>
<li>Multi-file tabs and a table-of-contents outline</li>
<li>Live file watching that reloads on disk changes</li>
<li>Wikilinks, backlinks, and workspace navigation</li>
<li>Optional AI summarization (Claude, OpenAI, Ollama) and text-to-speech</li>
<li>Light and dark themes that follow the system appearance</li>
</ul>
</description>

<launchable type="desktop-id">com.hamidfzm.glyph.desktop</launchable>

<url type="homepage">https://github.com/hamidfzm/glyph</url>
<url type="bugtracker">https://github.com/hamidfzm/glyph/issues</url>
<url type="vcs-browser">https://github.com/hamidfzm/glyph</url>

<screenshots>
<screenshot type="default">
<image>https://raw.githubusercontent.com/hamidfzm/glyph/main/docs/assets/hero.png</image>
<caption>Glyph rendering a markdown document with the outline sidebar</caption>
</screenshot>
</screenshots>

<categories>
<category>Office</category>
<category>Utility</category>
<category>TextEditor</category>
</categories>

<keywords>
<keyword>markdown</keyword>
<keyword>md</keyword>
<keyword>viewer</keyword>
<keyword>editor</keyword>
<keyword>notes</keyword>
</keywords>

<provides>
<binary>glyph</binary>
</provides>

<content_rating type="oars-1.1" />

<branding>
<color type="primary" scheme_preference="light">#f5f5f5</color>
<color type="primary" scheme_preference="dark">#1e1e1e</color>
</branding>

<releases>
<release version="0.10.0" date="2026-06-11">
<url type="details">https://github.com/hamidfzm/glyph/releases/tag/v0.10.0</url>
</release>
</releases>
</component>
64 changes: 64 additions & 0 deletions flatpak/com.hamidfzm.glyph.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Flatpak manifest for Glyph.
#
# This installs the released Debian package into the GNOME runtime rather than
# recompiling from source, matching the AUR/PPA pattern already used in
# `.github/workflows/release.yml`. flatpak-builder understands `.deb` archives
# and unpacks the embedded data tarball, so `usr/...` paths below come straight
# from the Tauri-generated package.
#
# The `url` versions and `sha256` checksums below are placeholders. They are
# filled in per release (the SHA256s are the same ones computed by the
# `publish-aur` / `publish-debian` jobs). Validate this manifest in a Linux
# environment with:
# flatpak-builder --user --install --force-clean build-dir flatpak/com.hamidfzm.glyph.yml
#
# See the Tauri Flatpak guide: https://v2.tauri.app/distribute/flatpak/
id: com.hamidfzm.glyph
runtime: org.gnome.Platform
runtime-version: "50"
sdk: org.gnome.Sdk
command: glyph

finish-args:
- --socket=wayland
- --socket=fallback-x11
- --device=dri
- --share=ipc
# Network access is needed for the optional AI features (Claude/OpenAI/Ollama).
- --share=network
# Read/write access to the user's documents so markdown files can be opened.
- --filesystem=home
# Desktop notifications.
- --talk-name=org.freedesktop.Notifications

modules:
- name: glyph
buildsystem: simple
build-commands:
- install -Dm755 usr/bin/glyph /app/bin/glyph
- install -Dm644 com.hamidfzm.glyph.desktop /app/share/applications/com.hamidfzm.glyph.desktop
- install -Dm644 com.hamidfzm.glyph.metainfo.xml /app/share/metainfo/com.hamidfzm.glyph.metainfo.xml
# Re-home every Tauri-generated PNG icon under the Flatpak app-id,
# preserving its hicolor size directory (the .deb ships 32x32, 128x128,
# and 256x256@2). Iterating over whatever exists avoids hardcoding the
# exact set of sizes the bundler emits.
- |
for src in $(find usr/share/icons -name glyph.png); do
dest="/app/${src#usr/}"
install -Dm644 "$src" "${dest%/glyph.png}/com.hamidfzm.glyph.png"
done
sources:
- type: archive
only-arches:
- x86_64
url: https://github.com/hamidfzm/glyph/releases/download/v0.10.0/Glyph_0.10.0_amd64.deb
sha256: 93e5d40ba44ee78563bc27e717ee954c714bbe6e63337d361a801c6c8cb53e4d
- type: archive
only-arches:
- aarch64
url: https://github.com/hamidfzm/glyph/releases/download/v0.10.0/Glyph_0.10.0_arm64.deb
sha256: 86dbc1e9b0f29e8c20464b7e2aaa33daead86f270d65a3b0f8ebed7f204c8dcb
- type: file
path: com.hamidfzm.glyph.desktop
- type: file
path: com.hamidfzm.glyph.metainfo.xml
Loading
Loading