Skip to content
Merged
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
100 changes: 100 additions & 0 deletions .github/workflows/release-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Release Linux

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: Existing release tag, for example v0.1.5
required: true
type: string

permissions:
contents: write

concurrency:
group: release-linux-${{ github.event.inputs.tag || github.ref_name }}
cancel-in-progress: false

jobs:
build:
name: Build Linux x86_64
runs-on: ubuntu-22.04
timeout-minutes: 30
env:
RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
GH_TOKEN: ${{ github.token }}

steps:
- name: Check out release source
uses: actions/checkout@v6

- name: Install Tauri system dependencies
run: |
sudo apt-get update
sudo apt-get install --yes \
libwebkit2gtk-4.1-dev \
libxdo-dev \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev

- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.0

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Validate release inputs
run: |
: "${TAURI_SIGNING_PRIVATE_KEY:?TAURI_SIGNING_PRIVATE_KEY is not configured}"
VERSION="${RELEASE_TAG#v}"
CONFIG_VERSION="$(node -p "require('./src-tauri/tauri.conf.json').version")"
test "$VERSION" = "$CONFIG_VERSION" || {
echo "Tag $RELEASE_TAG does not match app version $CONFIG_VERSION"
exit 1
}
echo "VERSION=$VERSION" >> "$GITHUB_ENV"

- name: Build AppImage and Debian package
run: bunx tauri build --bundles appimage,deb

- name: Prepare release artifacts
run: |
APPIMAGE="$(find src-tauri/target/release/bundle/appimage -maxdepth 1 -name '*.AppImage' -print -quit)"
DEB="$(find src-tauri/target/release/bundle/deb -maxdepth 1 -name '*.deb' -print -quit)"
test -n "$APPIMAGE" && test -f "$APPIMAGE.sig"
test -n "$DEB"

mkdir -p release-artifacts
install -m 755 "$APPIMAGE" "release-artifacts/Markd_${VERSION}_amd64.AppImage"
cp "$APPIMAGE.sig" "release-artifacts/Markd_${VERSION}_amd64.AppImage.sig"
cp "$DEB" "release-artifacts/Markd_${VERSION}_amd64.deb"

bun scripts/generate-linux-update-manifest.js \
"$VERSION" \
"release-artifacts/Markd_${VERSION}_amd64.AppImage.sig" \
"release-artifacts/latest-linux.json" \
"Markd ${VERSION} for Linux"

- name: Keep a workflow artifact
uses: actions/upload-artifact@v4
with:
name: markd-linux-${{ env.VERSION }}
path: release-artifacts/
if-no-files-found: error

- name: Upload to GitHub release
run: |
gh release view "$RELEASE_TAG" >/dev/null 2>&1 || \
gh release create "$RELEASE_TAG" --verify-tag --generate-notes --title "Markd ${VERSION}"
gh release upload "$RELEASE_TAG" release-artifacts/* --clobber
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Markd — agent guide

Local-first markdown notes app for macOS. Tauri 2 (Rust) + React 19 + Vite + Tailwind v4 + Tiptap 3 + Zustand.
Local-first markdown notes app for macOS and GNU/Linux. Tauri 2 (Rust) + React 19 + Vite + Tailwind v4 + Tiptap 3 + Zustand.

## Commands

- `bun tauri dev` — run the app (vite on port 1420 + cargo)
- `bun run build` — typecheck (tsc) + vite production build
- `bunx tsc --noEmit` — typecheck only
- `cd src-tauri && cargo test` — Rust unit tests
- `bun run release` — full signed release (scripts/release.sh)
- `bun run release`: full signed and notarized macOS release (scripts/release.sh)
- `.github/workflows/release-linux.yml`: signed x86_64 AppImage and Debian release

## Architecture

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ See [AGENTS.md](./AGENTS.md) for the full architecture guide. The short version:

## Reporting bugs

Open an issue with: macOS version, Markd version, and repro steps. If it's a vault/data issue, mention whether it reproduces with a fresh vault folder.
Open an issue with your operating system, Markd version, and reproduction steps. If it is a vault or data issue, mention whether it reproduces with a fresh vault folder.
53 changes: 36 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

**Local-first notes for people who write.**

Markd is a macOS-first notes app built for developers and content creators who care about speed, privacy, and ownership.
Markd is a fast notes app for macOS and Linux, built for people who care about speed, privacy, and ownership.

No accounts.
No cloud.
No sync (for now).

Your notes live on your Mac as plain `.md` files. Markd simply makes writing and finding them fast.
Your notes live on your disk as plain `.md` files. Markd simply makes writing and finding them fast.

---

Expand All @@ -20,16 +20,35 @@ Markd releases are Developer ID signed and notarized by Apple before distributio

---

## Installing on Linux

Download the latest AppImage or Debian package from the [GitHub releases](https://github.com/starc007/markd/releases/latest) page.

Run the AppImage:

```bash
chmod +x Markd_*_amd64.AppImage
./Markd_*_amd64.AppImage
```

Or install the Debian package:

```bash
sudo apt install ./Markd_*_amd64.deb
```

---

## Features

- **WYSIWYG markdown editor** — write in a rich editor, saved as clean markdown on disk
- **Folders & subfolders** organize notes in real, Finder-visible folders
- **Todos** — a standalone task list with tags and filtering
- **Bookmarks** save links with auto-fetched title, image, and favicon; tag and filter them too
- **⌘K command palette** jump to any note, folder, or page instantly
- **Instant search** title + content, ranked, in milliseconds
- **Monochrome UI** light, dark, or system theme; no color noise
- **Portable vault** — plain files, no IDs, no required metadata, no lock-in
- **WYSIWYG markdown editor:** write in a rich editor, saved as clean markdown on disk
- **Folders and subfolders:** organize notes in real, file-manager-visible folders
- **Todos:** a standalone task list with tags and filtering
- **Bookmarks:** save links with an auto-fetched title, image, and favicon
- **Command palette:** press Ctrl/Cmd+K to jump to any note, folder, or page instantly
- **Instant search:** title and content, ranked in milliseconds
- **Monochrome UI:** light, dark, or system theme with no color noise
- **Portable vault:** plain files, no IDs, no required metadata, no lock-in

---

Expand All @@ -39,17 +58,17 @@ Pick any folder on disk as your vault:

```
<vault>/
├── notes/ plain .md files filename is the title, folders are real folders
├── notes/ plain .md files, filename is the title, folders are real folders
└── .markd/ app data: todos, bookmarks, tags, pasted images
```

Notes are addressed by path, never by ID. Deletes go to the OS trash. Edit notes externally (vim, VS Code, whatever) — Markd picks up changes on window focus.
Notes are addressed by path, never by ID. Deletes go to the OS trash. Edit notes externally with vim, VS Code, or another editor. Markd picks up changes on window focus.

---

## Getting started

Requirements: [Bun](https://bun.sh), [Rust](https://rustup.rs), and Xcode Command Line Tools.
Requirements: [Bun](https://bun.sh), [Rust](https://rustup.rs), and the platform dependencies listed in the [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/).

```bash
bun install
Expand All @@ -59,7 +78,7 @@ bun tauri dev # run the app
Build a release bundle:

```bash
bun tauri build # produces a .app and .dmg under src-tauri/target/release/bundle
bun tauri build
```

Maintainers can follow [NOTARIZATION.md](./NOTARIZATION.md) to configure Developer ID signing and Apple notarization for releases.
Expand All @@ -72,16 +91,16 @@ See [AGENTS.md](./AGENTS.md) for architecture details, or [CONTRIBUTING.md](./CO

- Notes are stored locally as user-owned files
- No analytics, tracking, accounts, or note-content uploads
- Markd connects to `usemarkd.app` to check for application updates
- Markd connects to `usemarkd.app` on macOS or GitHub Releases on Linux to check for application updates
- Saving a bookmark fetches that page's title, preview image, and favicon
- Export your notes anytime they're already just files
- Export your notes anytime because they are already just files

---

## Status

Markd is under active development.
Sync, encryption, and publishing may be added later without compromising local-first performance.
Sync, encryption, and publishing may be added later without compromising local-first performance.

## License

Expand Down
4 changes: 2 additions & 2 deletions scripts/generate-latest-json.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const windowsSigPath = join(
const linuxSigPath = join(
buildDir,
"appimage",
`${APP_NAME}_${version}_amd64.AppImage.tar.gz.sig`
`${APP_NAME}_${version}_amd64.AppImage.sig`
);

// Also try alternative paths (Tauri v2 might use different structure)
Expand Down Expand Up @@ -132,7 +132,7 @@ if (windowsSig) {
if (linuxSig) {
latestJson.platforms["linux-x86_64"] = {
signature: linuxSig,
url: `${WEBSITE_URL}/${APP_NAME}_${version}_amd64.AppImage.tar.gz`,
url: `${WEBSITE_URL}/${APP_NAME}_${version}_amd64.AppImage`,
};
console.log("✓ Added Linux platform");
} else {
Expand Down
33 changes: 33 additions & 0 deletions scripts/generate-linux-update-manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env node

import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";

const [version, signaturePath, outputPath, notes = `Markd ${version}`] =
process.argv.slice(2);

if (!version || !signaturePath || !outputPath) {
console.error(
"Usage: bun scripts/generate-linux-update-manifest.js <version> <signature> <output> [notes]",
);
process.exit(1);
}

const normalizedVersion = version.replace(/^v/, "");
const fileName = `Markd_${normalizedVersion}_amd64.AppImage`;
const manifest = {
version: normalizedVersion,
notes,
pub_date: new Date().toISOString(),
platforms: {
"linux-x86_64": {
signature: readFileSync(resolve(signaturePath), "utf8").trim(),
url: `https://github.com/starc007/markd/releases/download/v${normalizedVersion}/${fileName}`,
},
},
};

const resolvedOutput = resolve(outputPath);
mkdirSync(dirname(resolvedOutput), { recursive: true });
writeFileSync(resolvedOutput, `${JSON.stringify(manifest, null, 2)}\n`);
console.log(`Generated ${resolvedOutput}`);
1 change: 1 addition & 0 deletions site/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ cloudflare-env.d.ts
# Only latest.json (tiny, versioned) is committed; the bundles are not.
public/downloads/*
public/updates/*.tar.gz
public/updates/*.AppImage
!public/downloads/.gitkeep

# TS build cache
Expand Down
2 changes: 1 addition & 1 deletion site/app/changelog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { GITHUB } from "@/lib/config";
export const metadata: Metadata = {
title: "Changelog | Markd",
description:
"See what is new in Markd, the local-first Markdown notes app for macOS.",
"See what is new in Markd, the local-first Markdown notes app for macOS and Linux.",
alternates: {
canonical: "/changelog",
},
Expand Down
Loading