Rename Wrystr to Vega

Named after Jurij Vega (1754-1802), Slovenian mathematician who made
knowledge accessible through his logarithm tables. Rebrand before
OpenSats application (April 1).

- Product name, window title, identifiers, binary name all renamed
- Cargo package: wrystr -> vega, wrystr_lib -> vega_lib
- PKGBUILD: wrystr -> vega (new AUR package name)
- Release workflow: artifact names, release text updated
- README: new tagline referencing Jurij Vega
- DB migration: auto-renames wrystr.db -> vega.db on first launch
- Keyring service name kept as "wrystr" for backward compatibility
- localStorage keys kept as wrystr_* to preserve existing user data

Pending manual steps:
- Rename GitHub repo hoornet/wrystr -> hoornet/vega
- Create new AUR package vega-git, orphan wrystr-git
- Update local .desktop launcher
This commit is contained in:
Jure
2026-03-30 21:14:02 +02:00
parent 383634fb56
commit 5cbaa7b741
315 changed files with 12550 additions and 95 deletions

View File

@@ -0,0 +1,13 @@
---
name: Podcast playback idea
description: Background audio podcast playback in Wrystr, inspired by Fountain.fm's Nostr integration
type: project
---
**Idea: In-app podcast listening for Nostr-native podcasts**
Fountain.fm publishes podcast episodes on Nostr. Wrystr could support playback the other way around — discover and listen to podcasts from within Wrystr.
**Why:** Desktop app is perfect for background audio. Podcast + Nostr is a growing niche (Fountain.fm, Wavlake). Value4Value streaming sats would tie into existing NWC/zap infrastructure. Differentiator vs other Nostr clients.
**How to apply:** When scoping future media features, consider this alongside the video/media feed idea. Key pieces: NIP-94 media metadata, podcast:item tags, persistent audio player (mini-player bar), background playback, V4V streaming sats via NWC. Research Fountain.fm's Nostr event structure before implementing.

View File

@@ -61,9 +61,9 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
tagName: ${{ github.ref_name }}
releaseName: Wrystr ${{ github.ref_name }}
releaseName: Vega ${{ github.ref_name }}
releaseBody: |
## Wrystr ${{ github.ref_name }}
## Vega ${{ github.ref_name }}
Cross-platform Nostr desktop client — polished UI, deep Lightning integration, first-class long-form writing.
@@ -128,23 +128,23 @@ jobs:
**Linux (Ubuntu / Debian / Mint):** Download the `.deb` and run:
```
sudo dpkg -i wrystr_*.deb
sudo dpkg -i vega_*.deb
```
**Linux (Fedora / openSUSE):** Download the `.rpm` and run:
```
sudo rpm -i wrystr-*.rpm # Fedora
sudo zypper install wrystr-*.rpm # openSUSE
sudo rpm -i vega-*.rpm # Fedora
sudo zypper install vega-*.rpm # openSUSE
```
**Linux (Arch / Manjaro):** Install from the AUR:
```
yay -S wrystr-git
yay -S vega-git
```
**Windows:** Download the `.exe` installer and run it.
**macOS (Apple Silicon):** Download the `aarch64.dmg` and open it.
---
⚡ Find Wrystr useful? [Zap the developer](https://github.com/hoornet/wrystr#support) or star the repo.
⚡ Find Vega useful? [Zap the developer](https://github.com/hoornet/vega#support) or star the repo.
releaseDraft: false
prerelease: false
includeUpdaterJson: true

View File

@@ -1,4 +1,4 @@
# AGENTS.md — Wrystr
# AGENTS.md — Vega
This file guides AI coding agents (Claude Code, Copilot, etc.) working on this codebase.
Read it fully before making changes. Follow the conventions here strictly.

View File

@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## What This Is
Wrystr is a cross-platform Nostr desktop client built with Tauri 2.0 (Rust) + React + TypeScript. It connects to Nostr relays via NDK (Nostr Dev Kit) and aims for Telegram Desktop-quality UX. Long-form content (NIP-23) is a first-class, distinguishing feature — not an afterthought.
Vega is a cross-platform Nostr desktop client built with Tauri 2.0 (Rust) + React + TypeScript. It connects to Nostr relays via NDK (Nostr Dev Kit) and aims for Telegram Desktop-quality UX. Long-form content (NIP-23) is a first-class, distinguishing feature — not an afterthought.
## AgentDocs
@@ -46,7 +46,7 @@ CI triggers on the tag and builds all three platforms (Ubuntu, Windows, macOS AR
- `bundle.createUpdaterArtifacts: true` must be set in `tauri.conf.json` — without it `.sig` files are never generated even if the signing key is set (Tauri 2 requirement)
- Valid `bundle.targets`: `"deb"`, `"rpm"`, `"nsis"`, `"msi"`, `"dmg"` — do NOT add `"updater"` (that's a plugin, not a bundle format)
- macOS runner is `macos-latest` (ARM only) — `macos-12`/`macos-13` are gone
- Verify after CI: `https://api.github.com/repos/hoornet/wrystr/releases/latest` (check for `.sig` assets + `latest.json`)
- Verify after CI: `https://api.github.com/repos/hoornet/vega/releases/latest` (check for `.sig` assets + `latest.json`)
## Architecture

View File

@@ -1,10 +1,10 @@
# Maintainer: hoornet <hoornet@users.noreply.github.com>
pkgname=wrystr
pkgname=vega
pkgver=0.9.11
pkgrel=1
pkgdesc="Cross-platform Nostr desktop client with Lightning integration"
arch=('x86_64')
url="https://github.com/hoornet/wrystr"
url="https://github.com/hoornet/vega"
license=('MIT')
depends=(
'webkit2gtk-4.1'
@@ -21,7 +21,7 @@ makedepends=(
'nodejs'
'npm'
)
source=("$pkgname-$pkgver::git+https://github.com/hoornet/wrystr.git#tag=v$pkgver")
source=("$pkgname-$pkgver::git+https://github.com/hoornet/vega.git#tag=v$pkgver")
sha256sums=('SKIP')
build() {
@@ -33,19 +33,19 @@ build() {
package() {
cd "$pkgname-$pkgver"
install -Dm755 "src-tauri/target/release/wrystr" \
"$pkgdir/usr/bin/wrystr"
install -Dm755 "src-tauri/target/release/vega" \
"$pkgdir/usr/bin/vega"
install -Dm644 "src-tauri/icons/128x128.png" \
"$pkgdir/usr/share/icons/hicolor/128x128/apps/wrystr.png"
"$pkgdir/usr/share/icons/hicolor/128x128/apps/vega.png"
install -Dm644 /dev/stdin \
"$pkgdir/usr/share/applications/wrystr.desktop" << 'EOF'
"$pkgdir/usr/share/applications/vega.desktop" << 'EOF'
[Desktop Entry]
Name=Wrystr
Name=Vega
Comment=Nostr desktop client
Exec=env WEBKIT_DISABLE_DMABUF_RENDERER=1 /usr/bin/wrystr
Icon=wrystr
Exec=env WEBKIT_DISABLE_DMABUF_RENDERER=1 /usr/bin/vega
Icon=vega
Type=Application
Categories=Network;InstantMessaging;
StartupNotify=true

View File

@@ -1,19 +1,19 @@
# Wrystr
# Vega
A cross-platform desktop Nostr client built with Tauri 2.0 + React + TypeScript. Polished UI, deep Lightning integration, and first-class support for long-form writing.
> Named as a nod to Nostr with a wry twist.
> Named after Jurij Vega (17541802), a Slovenian mathematician who made knowledge accessible through his pioneering logarithm tables — just as Vega makes writing accessible on Nostr.
## Download
Grab the latest release from the [Releases page](https://github.com/hoornet/wrystr/releases).
Grab the latest release from the [Releases page](https://github.com/hoornet/vega/releases).
| Platform | File | Command |
|---|---|---|
| Ubuntu / Debian / Mint | `.deb` | `sudo dpkg -i wrystr_*.deb` |
| Fedora | `.rpm` | `sudo rpm -i wrystr-*.rpm` |
| openSUSE | `.rpm` | `sudo zypper install wrystr-*.rpm` |
| Arch / Manjaro | AUR | `yay -S wrystr-git` |
| Ubuntu / Debian / Mint | `.deb` | `sudo dpkg -i vega_*.deb` |
| Fedora | `.rpm` | `sudo rpm -i vega-*.rpm` |
| openSUSE | `.rpm` | `sudo zypper install vega-*.rpm` |
| Arch / Manjaro | AUR | `yay -S vega-git` |
| Windows | `.exe` installer | run the installer |
| macOS (Apple Silicon) | `aarch64.dmg` | open and drag to Applications |
@@ -77,7 +77,7 @@ sudo dnf install gstreamer1-plugins-base gstreamer1-plugins-good gstreamer1-liba
- **NWC guided wizard** — wallet picker (Alby Hub, Alby Extension, Mutiny, Phoenix) with per-wallet setup steps and inline URI validation
- Send zaps via NWC (NIP-47 + NIP-57) with amount presets, custom amounts, comment
- Zap counts on notes (⚡ N sats inline)
- **Zap history** — Received and Sent tabs with amounts, counterparts, comments
- **Zap history** — Received and Sent tabs with amounts, counterparts, comments, and clickable note previews
- **Support / About page** — zap the developer, Lightning + Bitcoin QR codes, Ko-fi and GitHub links
**Discovery**
@@ -168,11 +168,11 @@ Up next:
## Support
Wrystr is free and open-source. If it's useful to you:
Vega is free and open-source. If it's useful to you:
| Method | Details |
|---|---|
| ⚡ Zap (in-app) | Open the **support** view in Wrystr's sidebar and zap directly |
| ⚡ Zap (in-app) | Open the **support** view in Vega's sidebar and zap directly |
| ⚡ Lightning | `harpos@getalby.com` |
| ₿ Bitcoin | `bc1qcgaupf80j28ca537xjlcs9dm9s03khezjs7crp` |
| ☕ Ko-fi | [ko-fi.com/jure](https://ko-fi.com/jure) |
@@ -181,7 +181,7 @@ Wrystr is free and open-source. If it's useful to you:
## Acknowledgements
Wrystr is built on the shoulders of excellent open-source projects:
Vega is built on the shoulders of excellent open-source projects:
- [Tauri](https://tauri.app/) — the desktop shell that makes cross-platform Rust+Web apps possible
- [NDK (Nostr Dev Kit)](https://github.com/nostr-dev-kit/ndk) by [Pablo Fernandez](https://github.com/pablof7z) — the Nostr protocol library that powers all relay communication

View File

@@ -1,14 +1,14 @@
# Wrystr — Roadmap
# Vega — Roadmap
---
## Vision: more than a Nostr client
Wrystr is not just a great desktop Nostr client. **Long-form content is a first-class,
Vega is not just a great desktop Nostr client. **Long-form content is a first-class,
distinguishing feature** — not an afterthought, not a checkbox NIP.
The article editor (NIP-23), the reading experience, the writing tools around it — these
set Wrystr apart from other clients and define its identity. Think of it as a publishing
set Vega apart from other clients and define its identity. Think of it as a publishing
platform that happens to live on Nostr, not a social feed that happens to support articles.
---
@@ -118,8 +118,8 @@ Bugs found during testing are fixed before Phase N+1 starts. A release is cut be
- ✓ Account switching between local nsec and remote signer accounts
### NIP-05 monetization (Phase 4 idea)
- Offer a paid "Verified NIP-05 name" service (e.g. name@wrystr.app)
- Would need a backend + domain; Wrystr talks to it; users pay sats via Lightning
- Offer a paid "Verified NIP-05 name" service (e.g. name@vega.app)
- Would need a backend + domain; Vega talks to it; users pay sats via Lightning
- Free tier: self-hosted as today; paid tier: managed registration
---
@@ -216,7 +216,7 @@ Bugs found during testing are fixed before Phase N+1 starts. A release is cut be
### v0.3.1
- **Feed tab persists across navigation** — back button now returns to the correct tab (Global/Following) instead of always resetting to Global
- **Available on AUR** — Arch/Manjaro users can install with `yay -S wrystr-git`
- **Available on AUR** — Arch/Manjaro users can install with `yay -S vega-git`
### v0.3.0
- **Instant feedback** — posted notes appear in feed immediately; thread replies show up without waiting for relay

BIN
agentdocs-shared.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

BIN
font-testing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Wrystr</title>
<title>Vega</title>
</head>
<body>

View File

@@ -1,5 +1,5 @@
{
"name": "wrystr",
"name": "vega",
"private": true,
"version": "0.9.11",
"type": "module",

246
phase-4-tests/README.md Normal file
View File

@@ -0,0 +1,246 @@
# Wrystr v0.8.3 Test Results
## Test Checklist
### Trending Feed (all platforms) ✅ TESTED
- Switch to "Trending" tab — should show notes from last ~24h, not ancient posts
- Articles (long-form) should appear mixed in with regular notes (rendered as article cards)
- Recently-posted popular notes should rank higher than older ones with same engagement (time decay)
- Empty state says "No trending notes right now" if nothing qualifies
- Refresh button works
### NIP-46 Remote Signer (all platforms)
- Onboarding: third "Remote signer" tab visible on login screen
- Pasting a bunker:// URI and clicking "Log in" shows "Connecting..." state
- Successfully connects if you have an nsecBunker/Amber instance (skip if no test signer available)
- Login Modal (add account): "Remote signer" tab present with bunker:// input
- Account Switcher: remote signer accounts show "NIP-46" label
- Timeout after 15s with clear error message if signer doesn't respond
- Session restores on app restart (if you can test with a real bunker)
### Media Feed (all platforms) ✅ TESTED
- "Media" item visible in sidebar (after "articles")
- Click opens media feed with header showing All / Videos / Images / Audio tabs
- "All" tab shows notes containing any media (images, videos, YouTube, audio)
- "Videos" tab filters to only video/YouTube/Vimeo content
- "Images" tab filters to only image content
- "Audio" tab filters to only audio/Spotify/Tidal content
- Empty state per tab if no matching content
- Clicking a note opens thread as expected
### Profile Media Gallery (all platforms) ✅ TESTED
- Open any profile — three tabs visible: Notes / Articles / Media
- "Media" tab shows a grid of images/videos/audio from that user's notes
- Images: click opens lightbox, arrow keys navigate, Escape closes
- Videos: show play button overlay, click opens the thread
- Audio: show music note icon, click opens the thread
- Grid layout: 2 columns default, 3 columns on wide screens
- Empty state "No media found." for profiles with no media
### Windows-Specific ✅ DONE. WORKING
- Installer runs (SmartScreen warning expected)
- App launches and connects to relays
- Auto-updater banner appears if upgrading from v0.8.2
- All four features above work
### General Regression
- Global/Following feed still works
- Compose + publish a note
- Articles tab in sidebar still works
- Search still works
- Sidebar collapsed/expanded state preserved
- Keyboard shortcuts (j/k/n//) still work
---
## Detailed Test Results
### Media Feed
**"Media" item visible in sidebar (after "articles")**
✅ YES!
**Click opens media feed with header showing All / Videos / Images / Audio tabs**
✅ YES
**"All" tab shows notes containing any media (images, videos, YouTube, audio)**
✅ YES
**"Videos" tab filters to only video/YouTube/Vimeo content**
✅ YES
**"Images" tab filters to only image content**
⚠️ Mostly true. Some posts appear that can't really be said to have an image:
![Images tab issue](./images/caad1501-daf0-42cb-b162-e30bab841086.png)
**"Audio" tab filters to only audio/Spotify/Tidal content**
❌ Only gave 3 posts (all visible in screenshot) and only one of the three was actually clickable audio. This could maybe be corrected in the podcast sprint when the whole audio thing will be better organized and played?
![Audio tab results](./images/b54f2d8c-e0eb-4bfc-bf5d-9ee1708d7d5e.png)
**Empty state per tab if no matching content**
⚠️ Couldn't test
**Clicking a note opens thread as expected**
✅ This seems to be working too:
![Thread opening](./images/89d5ebaf-1e32-4bea-9da2-799ebe0ea9ca.png)
#### Additional Notes in Media Feed
Clicking on the `...` → "mute" doesn't result in an actual effect. Instead, if you click it again you just see "unmute" WHILE the post has not been muted.
![Mute issue](./images/22c3d6cd-6dc9-4f74-bf9a-a794b19ed738.png)
---
### Trending Feed Test Results
**Switch to "Trending" tab — should show notes from last ~24h, not ancient posts**
✅ WORKS!
**Articles (long-form) should appear mixed in with regular notes (rendered as article cards)**
⚠️ Can't confirm yet
**Recently-posted popular notes should rank higher than older ones with same engagement (time decay)**
⚠️ Can't confirm yet
**Empty state says "No trending notes right now" if nothing qualifies**
⚠️ Can't confirm yet
**Refresh button works**
❌ Didn't see any changes in the trending feed after pressing it a few times. Can't say one way or the other
---
### Windows-Specific
**Installer runs (SmartScreen warning expected)**
✅ CHECK
**App launches and connects to relays**
✅ CHECK
**Auto-updater banner appears if upgrading from v0.8.2**
✅ CHECK
**All four features above work**
See results above
---
### NIP-46 Remote Signer (all platforms)
⚠️ CAN'T TEST most of it yet
**Onboarding: third "Remote signer" tab visible on login screen**
✅ CHECK
**Pasting a bunker:// URI and clicking "Log in" shows "Connecting..." state**
⚠️ Not tested
**Successfully connects if you have an nsecBunker/Amber instance**
⚠️ Skip if no test signer available
**Login Modal (add account): "Remote signer" tab present with bunker:// input**
⚠️ Not tested
**Account Switcher: remote signer accounts show "NIP-46" label**
⚠️ Not tested
**Timeout after 15s with clear error message if signer doesn't respond**
⚠️ Not tested
**Session restores on app restart**
⚠️ Can't test with a real bunker yet
---
### Profile Media Gallery (all platforms)
**Open any profile — three tabs visible: Notes / Articles / Media**
✅ CHECK
**"Media" tab shows a grid of images/videos/audio from that user's notes**
✅ CHECK
**Images: click opens lightbox, arrow keys navigate, Escape closes**
✅ CHECK
**Videos: show play button overlay, click opens the thread**
✅ CHECK
**Audio: show music note icon, click opens the thread**
⚠️ Not tested
**Grid layout: 2 columns default, 3 columns on wide screens**
✅ CHECK!
**Empty state "No media found." for profiles with no media**
⚠️ Can't confirm
---
## Additional Miscellaneous Issues
### 1. Names Sometimes Not Displayed
Names display inconsistently across different views. Examples:
**When viewing a post below a profile:**
![Names missing in profile view](./images/31463015-db70-4356-afcf-82df17955cda.png)
**But clicking the same post in thread view shows full address:**
![Names in thread view](./images/7b7176c6-91b1-4c1b-a3b0-559ac60d64a2.png)
**Correct in Trending list:**
![Names correct in trending](./images/3a3fea63-2f0f-4221-823d-6cb43ae1912a.png)
**But clicking same post shows different name resolution:**
![Names wrong after click](./images/5c0d234b-65eb-4db0-af82-39e441e1d0ba.png)
### 2. Links Sometimes Don't Work
Some links are not clickable. For example, this YouTube video can't be clicked:
![Non-clickable YouTube link](./images/f96d2174-1a40-49a9-8f10-88ae02f13fab.png)
### 3. Missing "Supported NIPs" on README
🔴 **MUST ADD:** "Supported NIPs" section on the README page.
### 4. Notification Switches in Settings Look Ugly
![Ugly notification switches](./images/23dca3f4-4a94-4203-afbc-45b3a5756e46.png)
### 5. Missing Emojis
Where exactly are emojis? Didn't we implement them some time ago?
---
## Summary
**Overall Status:** v0.8.3 has solid progress on Media Feed and Trending features.
**Strong Points:**
- Media feed displays correctly with proper filtering
- Profile media gallery works well (except Audio edge cases)
- Windows installer and auto-updater working
- NIP-46 Remote Signer UI present
**Issues to Address:**
- Audio tab filtering needs work (maybe defer to podcast sprint)
- Mute functionality broken
- Name resolution inconsistent across views
- Some links not clickable
- Notification UI needs refresh
- README missing "Supported NIPs" section
- Trending refresh button ineffective

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

320
phase-4.md Normal file
View File

@@ -0,0 +1,320 @@
20th March 2026 10h
A series of sprints from Claude.ai:
A few things I noticed that directly informed the suggestions:
Writing:
The editor is a raw <textarea> with font-family: var(--font-mono) inherited from body. For a client whose entire identity is long-form writing, this is the single biggest gap. Sprint 1 addresses this directly.
Reading: The prose-article class uses 15px monospace at 1.8 line height. That's readable but not comfortable for long articles. Sprint 2 fixes the typography properly.
The 280 char limit in ComposeBox.tsx is a Twitter holdover — Nostr has no protocol limit. That one's a quick win.
WoT is the big unimplemented feature your own ROADMAP flagged as "needs design session" — Sprint 3 provides that design in enough detail for Claude Code to execute on it.
Everything references the actual file names, function names, and CSS class names from your codebase, so Claude Code should be able to follow it without guesswork.
# wrystr — improvement suggestions for v0.9+
Based on a thorough read of the actual v0.8.1 codebase. Goal: make wrystr
the best desktop Nostr client for reading and writing, bar none.
The vision from ROADMAP.md is right: **long-form content is the identity**.
These suggestions push that vision further, fix real gaps observed in the code,
and add the missing pieces that would make this a daily driver people actively
recommend over every alternative.
---
## Sprint 1 — The writing experience deserves to be great
The article editor currently uses a plain `<textarea>` with a markdown toolbar.
That's fine for basics, but for a client whose identity is long-form writing,
it should feel like a joy to use — not a GitHub PR description box.
### 1.1 Replace textarea with a proper rich editor
The current editor in `ArticleEditor.tsx` is a raw `<textarea>` with toolbar
buttons that inject markdown syntax. This works but feels primitive for the
stated vision. Replace it with a CodeMirror 6 or Milkdown instance:
- CodeMirror 6 with `@codemirror/lang-markdown` gives syntax highlighting
in the editor itself (headings render larger, bold shows bold, links are
underlined) without losing the raw markdown output needed for NIP-23
- Alternatively, Milkdown gives a WYSIWYG mode that still outputs clean markdown
- Either choice keeps the existing `publishArticle()` flow unchanged —
the editor just produces better markdown string output
- Add a proper focus mode: hide the toolbar and sidebar, center the text column,
increase font size, use a proportional serif font instead of JetBrains Mono
for the writing surface (the body is currently rendered in mono which is
readable but not ideal for long prose)
### 1.2 Focus / Zen mode for writing
Currently the editor shares the screen with the sidebar and header chrome.
Add a true fullscreen writing mode (Tauri `set_fullscreen` + hide all chrome):
- Triggered by `F11` or a button in the editor header
- Shows only: title input, content area, word count
- Subtle exit hint at the top (disappears after 2s)
- Saves draft on every keystroke as it does now — no data loss risk
### 1.3 Article table of contents (already in ROADMAP backlog)
Already noted as remaining work. Implementation path:
- Parse h2/h3 headings from the rendered article content in `ArticleView.tsx`
- Render a floating TOC panel on the right side (sticky, hidden if article has
no headings)
- Clicking a heading smoothly scrolls to it
- Highlight the current section as the user scrolls
- The `max-w-2xl mx-auto` container in `ArticleView.tsx` already leaves
whitespace on the right at normal desktop widths — the TOC fits there
without layout changes
### 1.4 Article version history
NIP-23 articles are parameterized replaceable events — every publish
overwrites the previous version on relays, but older versions can still be
fetched with a `since`/`until` filter on the `d` tag. Expose this:
- "History" button in the article reader header (only shown for your own articles)
- Shows a list of previous versions with timestamps and word counts
- Click to view any historical version (read-only)
- Useful for writers who want to see their own edits over time
### 1.5 Auto-save indicator and publish confidence
The editor already auto-saves drafts. Surface this more clearly:
- Show "saved locally X seconds ago" in the editor footer
- Distinguish between "draft saved locally" and "published to relays"
- After publishing, show which relays accepted the event
(NDK's `publish()` returns relay confirmation — wire this up)
- If zero relays confirm, show a warning rather than silent success
---
## Sprint 2 — The reading experience should be world-class
### 2.1 Serif font for the article reader
The current `prose-article` class in `index.css` uses the inherited monospace
font at 15px/1.8. For long-form reading, a proportional serif or sans-serif
reading font is dramatically more comfortable. Specifically:
- Load a reading font via Tauri's asset system (no web request needed):
`iA Writer Quattro` (open source), `Lora` (Google Fonts, can be bundled),
or simply fall back to the system serif (`Georgia, 'Times New Roman', serif`)
- Apply it only to `.prose-article` — the rest of the app keeps JetBrains Mono
- Increase base size to 1718px for reading comfort
- Tighten max-width to ~65ch (roughly 650px) for optimal line length
- The current `max-w-2xl` (42rem / 672px) is close but not precise for the
actual body text column
### 2.2 Reading progress indicator
A thin progress bar at the top of the article view showing scroll position.
Simple CSS + a scroll event listener on the article container. Takes ~20 lines.
High perceived polish for minimal effort.
### 2.3 Article reading history (ROADMAP backlog item)
Already noted as remaining work. The `markArticleRead()` function exists in the
bookmarks store. Extend it to:
- Track *all* articles opened, not just bookmarked ones (use a separate
`readingHistory` store backed by SQLite, not localStorage)
- Show a "Recently read" section on the Articles feed idle screen
- Let users clear history
- Show "you read this X days ago" in the article header if applicable
### 2.4 Estimated reading time refinement
Currently uses `words / 230` which is a reasonable default but ignores:
- Code blocks (should be counted differently or excluded)
- Images (add ~12 seconds per image, common practice)
- Show as a range for very long articles: "1418 min read"
### 2.5 Inline nostr:naddr / nostr:note previews inside articles
Article content can contain `nostr:` links. The feed already renders
`nostr:note1…` as inline cards (`QuotePreview`). Apply the same treatment
inside the article reader's markdown renderer — when a paragraph contains
only a `nostr:` link, render it as an embedded card rather than a plain
hyperlink. This makes cross-referenced articles feel richly interconnected.
---
## Sprint 3 — Web of Trust (ROADMAP: needs design session)
This is the highest-leverage unimplemented feature. The ROADMAP correctly
identifies it as needing a design session — here is that design.
### 3.1 WoT graph computation in Rust
The follows graph (kind:3 events) is already fetched. The missing piece is
computing graph distance:
- Add a Rust command `compute_wot_scores(my_pubkey, follows_json)` that takes
the serialized follow lists and returns a `HashMap<pubkey, depth>` where
depth 1 = direct follow, depth 2 = follows-of-follows, etc.
- Store scores in SQLite (`wot_scores` table with pubkey + score + updated_at)
- Refresh on login and every 6 hours in the background
- Expose as a Zustand store `useWotStore` with `getScore(pubkey): number | null`
### 3.2 WoT feed filter
Add a third tab to the feed: **WoT** (alongside Global and Following):
- Shows notes from depth ≤ 2 (configurable in Settings)
- Dramatically better signal-to-noise than Global
- Sort by a simple engagement-weighted score: `recency + (likes * 0.3) + (zaps * 0.5)`
- No external algorithm server — fully local, fully private
### 3.3 WoT trust badges on profiles
- Show a subtle "✓ 2nd degree" or "✓ follows you" badge on profiles and note
author names
- Use `useWotStore().getScore(pubkey)` — already available once 3.1 is done
- Apply to: note cards, profile view header, DM conversation list
### 3.4 WoT-powered search ranking (ROADMAP backlog item)
When search results come back, re-rank them by WoT score. Notes from people
closer in the graph surface first. Wire into `src/lib/search.ts`.
---
## Sprint 4 — NIP-46 Remote Signer (ROADMAP: listed as incomplete)
### 4.1 NIP-46 connection flow
The ROADMAP notes this as the completion of the multi-account story. NDK has
`NDKNip46Signer` — this is primarily a UX problem, not a protocol problem:
- Add a "Connect remote signer" option in the Add Account flow (alongside
"New account" and "Login with nsec")
- User pastes a `bunker://` URI or scans a QR code
- Wrystr connects to the relay in the URI and negotiates signing
- On event sign, sends the sign request to the remote signer, waits for approval
- Show a "waiting for approval on your signer…" state in the UI during signing
- Compatible with Nsecbunker, Amber (Android), and any NIP-46 server
---
## Sprint 5 — Notes feed quality of life
These are smaller improvements to the core feed experience based on what
the current code reveals is missing.
### 5.1 Compose box: proportional font option
The compose textarea inherits `font-family: var(--font-mono)` from `body`
in `index.css`. Notes are prose, not code. Consider offering a toggle
(persisted to localStorage) to switch the compose area to a proportional font.
This is a personal preference — make it a setting rather than a hard change.
### 5.2 Note character limit is hardcoded at 280
`ComposeBox.tsx` shows a warning at 280 chars and blocks publish (`overLimit`).
Nostr has no protocol character limit — 280 is a Twitter legacy assumption.
Change the limit to something more generous (e.g. 4000 chars) or remove the
hard block entirely and show a soft warning instead. Many Nostr users write
longer notes than 280 chars.
### 5.3 Thread view: load more replies
The thread view fetches replies with `limit: 200` which is fine for most
threads. Add a "load more" button for popular threads where 200 isn't enough.
Also consider lazy-loading nested replies (replies to replies) on demand rather
than all at once.
### 5.4 Hashtag pages
Clicking a `#hashtag` in a note currently goes to a search result page.
It should go to a dedicated hashtag feed — a live subscription to `#t` filter
showing the stream as it comes in, with a header showing the tag name and
perhaps trending articles with that tag at the top.
### 5.5 OS-level push notifications via Tauri
Currently notifications are in-app only (the 🔔 badge clears on open).
Wrystr already has system tray support. Extend to OS-level notifications:
- Use `tauri-plugin-notification` for DMs, zaps received, and mentions
- Ask for permission on first launch (standard OS notification permission flow)
- Per-type toggle in Settings: "Notify me for DMs / zaps / mentions"
- This is the single biggest advantage desktop has over web clients —
it should be a headline feature, not an afterthought
---
## Sprint 6 — Polish and trust signals
These are the details that separate "good" from "the one I recommend to everyone".
### 6.1 NIP-05 verification badge on note cards
NIP-05 verification is checked on profile view already. Show a small ✓ badge
next to author names on note cards too (cached, not re-fetched per note).
Only show for verified accounts where the check has succeeded.
### 6.2 Syntax highlighting in code blocks
The article reader renders ` ```code``` ` blocks with a monospace background
but no syntax highlighting. Add `highlight.js` or `prism.js` (small, can be
loaded from local asset):
- Auto-detect language from the fenced code block label (` ```typescript `)
- Apply in `renderMarkdown()` in `src/lib/nostr/` wherever markdown is rendered
- Use a dark theme consistent with the app's `#0a0a0a` background
### 6.3 Article cover image aspect ratio consistency
Currently `max-h-72 object-cover` in `ArticleView.tsx`. On very tall portrait
images this crops aggressively. Use a fixed aspect ratio container (`aspect-video`
/ 16:9) so all cover images present consistently regardless of source dimensions.
### 6.4 Search relay discovery (ROADMAP backlog item)
The ROADMAP notes `kind:10007` search relay discovery as remaining. When a
user runs a search and the connected relays don't support NIP-50, show a hint:
"Your relays may not support full-text search. Discover search-capable relays →"
with a one-click add for known good ones (e.g. relay.nostr.band).
### 6.5 Export / backup improvements
Data export (bookmarks, follows, relay list) was shipped in v0.8.0. Extend to:
- Export all authored notes and articles as JSON or markdown files
- This is a genuine "your data, your keys" feature that differentiates from
every web client
- Implementation: query the SQLite cache for events authored by the user's pubkey
---
## Context for Claude Code
**Stack:** Tauri 2.0 (Rust) + React 19 + TypeScript + NDK 3.x + Tailwind CSS 4 + Zustand + SQLite (rusqlite)
**Key files:**
- `src/components/article/ArticleEditor.tsx` — article editor (textarea + toolbar)
- `src/components/article/ArticleView.tsx` — article reader
- `src/index.css` — all prose styles (`.prose-article`, `.article-preview`)
- `src/lib/nostr/client.ts` — all Nostr interactions (publishArticle, fetchDMConversations, etc.)
- `src/stores/` — Zustand stores per domain
- `src-tauri/src/lib.rs` — Rust command registration
- `AGENTS.md` — coding conventions (functional components, no `any`, Tailwind only)
**Hard constraints from AGENTS.md:**
- Functional React components only, no class components
- Never use `any` — define types in `src/types/`
- Tailwind classes only — no inline styles (except unavoidable `WebkitUserSelect`)
- Private keys only via Rust `keyring` crate, never in JS
- NDK interactions only through `src/lib/nostr/` wrapper
- New Zustand store per domain for new features
**Priority order:** Reading/writing experience (Sprints 12) → WoT (Sprint 3) →
NIP-46 (Sprint 4) → Feed QoL (Sprint 5) → Polish (Sprint 6)

1
squashfs-root/.DirIcon Symbolic link
View File

@@ -0,0 +1 @@
/home/runner/work/wrystr/wrystr/src-tauri/target/release/bundle/appimage/Wrystr.AppDir/Wrystr.png

12
squashfs-root/AppRun Executable file
View File

@@ -0,0 +1,12 @@
#! /usr/bin/env bash
# autogenerated by linuxdeploy
# make sure errors in sourced scripts will cause this script to stop
set -e
this_dir="$(readlink -f "$(dirname "$0")")"
source "$this_dir"/apprun-hooks/"linuxdeploy-plugin-gtk.sh"
exec "$this_dir"/AppRun.wrapped "$@"

BIN
squashfs-root/AppRun.wrapped Executable file

Binary file not shown.

View File

@@ -0,0 +1 @@
usr/share/applications/Wrystr.desktop

BIN
squashfs-root/Wrystr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,17 @@
#! /usr/bin/env bash
gsettings get org.gnome.desktop.interface gtk-theme 2> /dev/null | grep -qi "dark" && GTK_THEME_VARIANT="dark" || GTK_THEME_VARIANT="light"
APPIMAGE_GTK_THEME="${APPIMAGE_GTK_THEME:-"Adwaita:$GTK_THEME_VARIANT"}" # Allow user to override theme (discouraged)
export APPDIR="${APPDIR:-"$(dirname "$(realpath "$0")")"}" # Workaround to run extracted AppImage
export GTK_DATA_PREFIX="$APPDIR"
export GTK_THEME="$APPIMAGE_GTK_THEME" # Custom themes are broken
export GDK_BACKEND=x11 # Crash with Wayland backend on Wayland - We tested it without it and ended up with this: https://github.com/tauri-apps/tauri/issues/8541
export XDG_DATA_DIRS="$APPDIR/usr/share:/usr/share:$XDG_DATA_DIRS" # g_get_system_data_dirs() from GLib
export GSETTINGS_SCHEMA_DIR="$APPDIR//usr/share/glib-2.0/schemas"
export GTK_EXE_PREFIX="$APPDIR//usr"
export GTK_PATH="$APPDIR//usr/lib/x86_64-linux-gnu/gtk-3.0:/usr/lib64/gtk-3.0:/usr/lib/x86_64-linux-gnu/gtk-3.0"
export GTK_IM_MODULE_FILE="$APPDIR//usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules.cache"
export GDK_PIXBUF_MODULE_FILE="$APPDIR//usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/2.10.0/loaders.cache"
export GIO_EXTRA_MODULES="$APPDIR/usr/lib/x86_64-linux-gnu/gio/modules"

BIN
squashfs-root/usr/bin/wrystr Executable file

Binary file not shown.

1067
squashfs-root/usr/bin/xdg-open Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-am-et.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-broadway.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-cedilla.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-cyrillic-translit.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-inuktitut.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-ipa.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-multipress.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-thai.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-ti-er.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-ti-et.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-viqr.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-wayland.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-waylandgtk.so

View File

@@ -0,0 +1 @@
x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-xim.so

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More