Browse Source

change jumble to imwald

imwald
Silberengel 4 weeks ago
parent
commit
8c83c75162
  1. 24
      LOGGING.md
  2. 16
      PROXY_SETUP.md
  3. 27
      README.md
  4. 2
      REFACTORING_COMPLETE.md
  5. 2
      docker-compose.prod.yml
  6. 10
      docs/NIP66-MONITOR-SECURITY.md
  7. 2
      electron/main.cjs
  8. 4
      electron/preload.cjs
  9. 34
      index.html
  10. 4
      package-lock.json
  11. 8
      package.json
  12. 6
      public/manifest.webmanifest
  13. 8
      scripts/README-deploy.md
  14. 12
      scripts/build-and-push-prod.sh
  15. 6
      src/components/AboutInfoDialog/index.tsx
  16. 2
      src/components/AccountManager/index.tsx
  17. 2
      src/components/CacheRelaysSetting/index.tsx
  18. 8
      src/components/Donation/index.tsx
  19. 2
      src/components/ErrorBoundary.tsx
  20. 10
      src/components/EventArchiveCacheSettings/index.tsx
  21. 4
      src/components/Note/MarkdownArticle/MarkdownArticle.tsx
  22. 2
      src/components/Note/index.tsx
  23. 6
      src/components/NoteOptions/useMenuActions.tsx
  24. 2
      src/components/PostEditor/PostOptions.tsx
  25. 4
      src/components/RssFeedList/ArticleUrlsSection.tsx
  26. 4
      src/components/RssFeedList/RssUnifiedScopeSection.tsx
  27. 2
      src/components/Settings/SettingsMenuBody.tsx
  28. 2
      src/components/Sidebar/DownloadDesktopSidebarButton.tsx
  29. 6
      src/components/WebPreview/index.tsx
  30. 20
      src/constants.ts
  31. 14
      src/i18n/locales/ar.ts
  32. 14
      src/i18n/locales/de.ts
  33. 14
      src/i18n/locales/en.ts
  34. 14
      src/i18n/locales/es.ts
  35. 14
      src/i18n/locales/fa.ts
  36. 14
      src/i18n/locales/fr.ts
  37. 16
      src/i18n/locales/hi.ts
  38. 16
      src/i18n/locales/it.ts
  39. 14
      src/i18n/locales/ja.ts
  40. 14
      src/i18n/locales/ko.ts
  41. 14
      src/i18n/locales/pl.ts
  42. 14
      src/i18n/locales/pt-BR.ts
  43. 14
      src/i18n/locales/pt-PT.ts
  44. 16
      src/i18n/locales/ru.ts
  45. 14
      src/i18n/locales/th.ts
  46. 14
      src/i18n/locales/zh.ts
  47. 6
      src/lib/client-platform.ts
  48. 15
      src/lib/content-spacing-debug.ts
  49. 13
      src/lib/debug-utils.ts
  50. 4
      src/lib/draft-event.ts
  51. 8
      src/lib/event-archive-config.ts
  52. 2
      src/lib/event.ts
  53. 8
      src/lib/logger.ts
  54. 23
      src/lib/nip89-utils.ts
  55. 6
      src/lib/nostr-from-http-url.ts
  56. 6
      src/lib/piper-tts-cache-policy.ts
  57. 2
      src/lib/read-aloud.ts
  58. 2
      src/lib/vite-proxy-url.ts
  59. 4
      src/main.tsx
  60. 32
      src/pages/secondary/NotePage/index.tsx
  61. 30
      src/pages/secondary/ProfilePage/index.tsx
  62. 4
      src/pages/secondary/RssFeedSettingsPage/index.tsx
  63. 4
      src/providers/NostrProvider/index.tsx
  64. 6
      src/services/lightning.service.ts
  65. 30
      src/services/local-storage.service.ts
  66. 20
      src/services/nip89.service.ts
  67. 6
      src/services/session-feed-snapshot.service.ts
  68. 17
      src/services/web.service.ts
  69. 2
      src/vite-env.d.ts

24
LOGGING.md

@ -18,21 +18,23 @@ In development mode, you can control logging from the browser console: @@ -18,21 +18,23 @@ In development mode, you can control logging from the browser console:
```javascript
// Enable debug logging
jumbleDebug.enable()
imwaldDebug.enable()
// Disable debug logging
jumbleDebug.disable()
// Disable debug logging
imwaldDebug.disable()
// Check current status
jumbleDebug.status()
imwaldDebug.status()
// Use debug logging directly
jumbleDebug.log('Debug message', data)
jumbleDebug.warn('Warning message', data)
jumbleDebug.error('Error message', data)
jumbleDebug.perf('Performance message', data)
imwaldDebug.log('Debug message', data)
imwaldDebug.warn('Warning message', data)
imwaldDebug.error('Error message', data)
imwaldDebug.perf('Performance message', data)
```
(`jumbleDebug` is still exposed as an alias for compatibility.)
### For Code
Use the logger instead of direct console statements:
@ -69,7 +71,7 @@ logger.perf('Performance metric', data) @@ -69,7 +71,7 @@ logger.perf('Performance metric', data)
The logger automatically configures itself based on:
1. **Environment**: Debug logging is disabled in production builds
2. **Local Storage**: `jumble-debug=true` enables debug mode
2. **Local Storage**: `imwald-debug=true` enables debug mode (legacy: `jumble-debug=true`)
3. **Environment Variable**: `VITE_DEBUG=true` enables debug mode
## Performance Impact
@ -103,12 +105,12 @@ To enable debug mode: @@ -103,12 +105,12 @@ To enable debug mode:
1. **In Browser Console** (development only):
```javascript
jumbleDebug.enable()
imwaldDebug.enable()
```
2. **Via Local Storage**:
```javascript
localStorage.setItem('jumble-debug', 'true')
localStorage.setItem('imwald-debug', 'true')
```
3. **Via Environment Variable**:

16
PROXY_SETUP.md

@ -11,7 +11,7 @@ When building and deploying on the remote server, you need to build the Docker i @@ -11,7 +11,7 @@ When building and deploying on the remote server, you need to build the Docker i
**IMPORTANT:** `VITE_PROXY_SERVER` must be set during Docker BUILD (as a build argument), NOT at runtime. It gets baked into the JavaScript bundle.
Rebuild the Jumble image with the correct proxy URL:
Rebuild the Imwald image with the correct proxy URL:
```bash
# Build with the correct proxy URL (baked into the JS bundle)
@ -101,7 +101,7 @@ docker-compose up -d @@ -101,7 +101,7 @@ docker-compose up -d
The client uses **`POST /api/piper-tts`** on the **same host** as the app (default build: `VITE_READ_ALOUD_TTS_URL=/api/piper-tts`) so the browser does not need cross-origin CORS to aitherboard.
Add these **before** the catch-all `ProxyPass /` to the Jumble static container (same ordering as `/sites/`):
Add these **before** the catch-all `ProxyPass /` to the Imwald static container (same ordering as `/sites/`):
```apache
ProxyPass /api/piper-tts http://127.0.0.1:9876/api/piper-tts
@ -117,7 +117,7 @@ curl -sS -o /tmp/t.wav -w "%{http_code}\n" -H "Content-Type: application/json" \ @@ -117,7 +117,7 @@ curl -sS -o /tmp/t.wav -w "%{http_code}\n" -H "Content-Type: application/json" \
Expect **200** and a WAV file. **Local dev:** `npm run dev` proxies `/api/piper-tts``http://127.0.0.1:9876` in `vite.config.ts`.
Rebuild the Jumble image after changing `VITE_READ_ALOUD_TTS_URL`; `Dockerfile` passes `ARG`/`ENV` `VITE_READ_ALOUD_TTS_URL` into `npm run build`.
Rebuild the Imwald image after changing `VITE_READ_ALOUD_TTS_URL`; `Dockerfile` passes `ARG`/`ENV` `VITE_READ_ALOUD_TTS_URL` into `npm run build`.
## Update Proxy Server's ALLOW_ORIGIN
@ -194,7 +194,7 @@ sudo systemctl restart apache2 @@ -194,7 +194,7 @@ sudo systemctl restart apache2
RequestHeader set Host "127.0.0.1:8090"
</Location>
# Reverse Proxy for the main Jumble app (needs Host header preserved)
# Reverse Proxy for the main Imwald app (needs Host header preserved)
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:32768/
ProxyPassReverse / http://127.0.0.1:32768/
@ -228,10 +228,10 @@ sudo systemctl reload apache2 @@ -228,10 +228,10 @@ sudo systemctl reload apache2
# Test with a real URL - the code constructs /proxy/sites/{encoded-url}
curl https://jumble.imwald.eu/proxy/sites/https%3A%2F%2Fexample.com
# Should return example.com's HTML, NOT jumble.imwald.eu's HTML
# If you see Jumble HTML, the proxy server is using the Host header instead of the URL path
# If you see Imwald HTML, the proxy server is using the Host header instead of the URL path
```
**If the test returns Jumble HTML instead of the requested site's HTML:**
**If the test returns Imwald HTML instead of the requested site's HTML:**
The proxy server is using the `Host` header (`jumble.imwald.eu`) to determine what to fetch. Update your Apache config to use `ProxyPreserveHost Off` for the `/proxy/` path:
@ -272,9 +272,9 @@ docker build \ @@ -272,9 +272,9 @@ docker build \
## Troubleshooting
### If Proxy Returns Jumble HTML Instead of Requested Site
### If Proxy Returns Imwald HTML Instead of Requested Site
If you've set `ProxyPreserveHost Off` but still get Jumble HTML, test the proxy server directly:
If you've set `ProxyPreserveHost Off` but still get Imwald HTML, test the proxy server directly:
**1. Test the proxy server directly (bypassing Apache):**
```bash

27
README.md

@ -2,20 +2,20 @@ @@ -2,20 +2,20 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./resources/logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="./resources/logo-light.svg">
<img src="./resources/logo-light.svg" alt="Jumble Logo" width="400" />
<img src="./resources/logo-light.svg" alt="Imwald logo" width="400" />
</picture>
<p>logo designed by <a href="http://wolfertdan.com/">Daniel David</a></p>
</div>
# Jumble — **ImWald Edition**
# Imwald
**Maintainer: [Silberengel](https://github.com/Silberengel)** · Hard fork of [Cody Tseng’s Jumble](https://github.com/CodyTseng/jumble)
**Maintainer: [Silberengel](https://github.com/Silberengel)** · Evolved from [Cody Tseng’s Jumble](https://github.com/CodyTseng/jumble)
A Nostr web client focused on relay feeds, discovery, and spells. This repository is the **ImWald** line: same core ideas as upstream, with a substantial navigation and information-architecture rewrite (see below).
A Nostr web client focused on relay feeds, discovery, and spells. **Imwald** keeps the same core ideas as upstream, with a substantial navigation and information-architecture rewrite (see below). The public instance lives at [jumble.imwald.eu](https://jumble.imwald.eu).
---
## Major rewrite (this fork)
## Major rewrite (this line)
High-level changes versus a “stock” Jumble-style layout:
@ -47,11 +47,10 @@ High-level changes versus a “stock” Jumble-style layout: @@ -47,11 +47,10 @@ High-level changes versus a “stock” Jumble-style layout:
### Other
- Sidebar layout tuned for **long translations** (e.g. German) so labels don’t sit on the divider.
- Branding in-app: **Im Wald**.
---
## Features (still core to Jumble)
## Features
- **Relay feeds:** Browse content through relays, sets, and favorites
- **Relay-friendly requests:** Efficient subscriptions where possible
@ -60,17 +59,17 @@ High-level changes versus a “stock” Jumble-style layout: @@ -60,17 +59,17 @@ High-level changes versus a “stock” Jumble-style layout:
## Screenshots
<img src="./screenshots/01.png" alt="Jumble Screenshot 01" width="650" />
<img src="./screenshots/01.png" alt="Imwald screenshot 01" width="650" />
<div>
<img src="./screenshots/02.png" alt="Jumble Screenshot 02" width="200" />
<img src="./screenshots/03.png" alt="Jumble Screenshot 03" width="200" />
<img src="./screenshots/04.png" alt="Jumble Screenshot 04" width="200" />
<img src="./screenshots/02.png" alt="Imwald screenshot 02" width="200" />
<img src="./screenshots/03.png" alt="Imwald screenshot 03" width="200" />
<img src="./screenshots/04.png" alt="Imwald screenshot 04" width="200" />
</div>
## Upstream & related forks
- **Original project:** [CodyTseng/jumble](https://github.com/CodyTseng/jumble) — design, sponsorship, and donation links below still refer to Cody’s work where applicable.
- **This fork:** [Silberengel/jumble](https://github.com/Silberengel/jumble) — Im Wald / rewrite described above.
- **Original project:** [CodyTseng/jumble](https://github.com/CodyTseng/jumble) — upstream design and history.
- **This repository:** [Silberengel/jumble](https://github.com/Silberengel/jumble) — Imwald source and releases.
- Other public forks (examples): [grouped-notes.dtonon.com](https://grouped-notes.dtonon.com/), [jumblekat.shakespeare.wtf](https://jumblekat.shakespeare.wtf/).
## Run locally
@ -97,7 +96,7 @@ Then open: http://localhost:8089 @@ -97,7 +96,7 @@ Then open: http://localhost:8089
Built packages are **not** committed to this repository (only source). They are published as **release assets** when a maintainer uploads them.
- **Download:** [GitHub Releases — latest](https://github.com/Silberengel/jumble/releases/latest) — get the `*.deb` (or AppImage) attached to a release.
- **Install the `.deb`:** `sudo apt install ./Jumble_*_amd64.deb` (use the exact filename from the download folder; `./` is required so `apt` uses the local file). After install, Jumble should appear in your app menu (often under **Network**).
- **Install the `.deb`:** `sudo apt install ./Imwald_*_amd64.deb` (use the exact filename from the download folder; `./` is required so `apt` uses the local file). After install, Imwald should appear in your app menu (often under **Network**).
**Maintainers — build artifacts locally:**

2
REFACTORING_COMPLETE.md

@ -114,7 +114,7 @@ The following responsibilities remain in `ClientService` as they represent core @@ -114,7 +114,7 @@ The following responsibilities remain in `ClientService` as they represent core
### Logger Integration
- ✅ Efficient logger implementation
- ✅ Development: Browser console
- ✅ Production: Console GUI in Jumble app
- ✅ Production: Console GUI in Imwald app
- ✅ Performance logging included
## Migration Status

2
docker-compose.prod.yml

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
# VITE_PROXY_SERVER / VITE_READ_ALOUD_TTS_URL are baked at image build — see scripts/build-and-push-prod.sh
#
# NIP-66 monitor: set NIP66_MONITOR_NSEC (and optionally NIP66_MONITOR_NPUB) in the host env or .env.
# - Cron (jumble-nip66-monitor) uses NIP66_MONITOR_NSEC to publish 30166/10166; nsec never goes to the client.
# - Cron service `jumble-nip66-monitor` (Imwald NIP-66 monitor image) uses NIP66_MONITOR_NSEC to publish 30166/10166; nsec never goes to the client.
# - Set NIP66_MONITOR_NPUB (npub1... derived from the same key) so the relay info page shows the monitor's avatar and handle in the NIP-66 liveliness section.
services:

10
docs/NIP66-MONITOR-SECURITY.md

@ -9,8 +9,8 @@ The monitor **nsec** (`NIP66_MONITOR_NSEC`) is used only in the **nip66-cron** c @@ -9,8 +9,8 @@ The monitor **nsec** (`NIP66_MONITOR_NSEC`) is used only in the **nip66-cron** c
| Location | Allowed? | Notes |
|----------|----------|--------|
| **Host env** (e.g. `.env`) | ✅ | Operator sets it; not in repo. |
| **jumble-nip66-monitor container env** | ✅ | Only service that needs it. |
| **jumble container env** | ❌ | Removed: nsec is not passed to the web app. |
| **jumble-nip66-monitor container env** (Imwald monitor) | ✅ | Only service that needs it. |
| **jumble** web app container env | ❌ | Removed: nsec is not passed to the web app. |
| **config.json** (served to browser) | ❌ | Entrypoint writes only `NIP66_MONITOR_NPUB` or `{}`; never nsec. |
| **Frontend (Window.__RUNTIME_CONFIG__)** | ❌ | Type and fetch only include `NIP66_MONITOR_NPUB`. |
| **Vite / build** | ❌ | No `VITE_NIP66_*` or nsec in bundle. |
@ -18,13 +18,13 @@ The monitor **nsec** (`NIP66_MONITOR_NSEC`) is used only in the **nip66-cron** c @@ -18,13 +18,13 @@ The monitor **nsec** (`NIP66_MONITOR_NSEC`) is used only in the **nip66-cron** c
## Checks performed
1. **docker-entrypoint.sh** – Writes config.json only from `NIP66_MONITOR_NPUB`; does not read or write `NIP66_MONITOR_NSEC`.
2. **docker-compose.prod.yml**`NIP66_MONITOR_NSEC` is set only on the **jumble-nip66-monitor** service; **jumble** has only `NIP66_MONITOR_NPUB`.
2. **docker-compose.prod.yml**`NIP66_MONITOR_NSEC` is set only on the **jumble-nip66-monitor** service; the **jumble** (Imwald SPA) service has only `NIP66_MONITOR_NPUB`.
3. **main.tsx** – Fetches config and types only `NIP66_MONITOR_NPUB`; no nsec in `Window.__RUNTIME_CONFIG__`.
4. **Frontend** – No monitor signing code; 30166/10166 publishing is server-only (nip66-cron / jumble-nip66-monitor).
4. **Frontend** – No monitor signing code; 30166/10166 publishing is server-only (nip66-cron / Imwald NIP-66 monitor container).
5. **nip66-cron/index.mjs** – Reads nsec from `process.env.NIP66_MONITOR_NSEC` only; never logs it or passes it to `log()`; comment added to never log or expose it.
6. **RelayInfo / RelayLivelinessSection** – Use only `window.__RUNTIME_CONFIG__.NIP66_MONITOR_NPUB` (npub) for display.
## Recommendation
- Keep **NIP66_MONITOR_NSEC** only in the host env and in the **jumble-nip66-monitor** service.
- Do not add nsec to the jumble service env, config.json, or any client-exposed config.
- Do not add nsec to the **jumble** (web app) service env, config.json, or any client-exposed config.

2
electron/main.cjs

@ -46,7 +46,7 @@ function createWindow() { @@ -46,7 +46,7 @@ function createWindow() {
}
app.whenReady().then(() => {
ipcMain.handle('jumble:reload-app', async (event) => {
ipcMain.handle('imwald:reload-app', async (event) => {
const win = BrowserWindow.fromWebContents(event.sender)
if (!win || win.isDestroyed()) return false
loadRenderer(win)

4
electron/preload.cjs

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('jumbleElectron', {
contextBridge.exposeInMainWorld('imwaldElectron', {
isElectron: true,
reloadApp: () => ipcRenderer.invoke('jumble:reload-app')
reloadApp: () => ipcRenderer.invoke('imwald:reload-app')
})

34
index.html

@ -4,17 +4,17 @@ @@ -4,17 +4,17 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<title>Jumble - Imwald Edition 🌲</title>
<title>Imwald 🌲</title>
<meta
name="description"
content="A user-friendly Nostr client focused on relay feed browsing and relay discovery"
content="Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery"
/>
<meta
name="keywords"
content="jumble, nostr, web, client, relay, feed, social, pwa, simple, clean"
content="imwald, nostr, web, client, relay, feed, social, pwa, simple, clean"
/>
<meta name="apple-mobile-web-app-title" content="Jumble" />
<meta name="apple-mobile-web-app-title" content="Imwald" />
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22%3E%3Ctext y=%22.9em%22 font-size=%2290%22%3E🌲%3C/text%3E%3C/svg%3E" type="image/svg+xml" />
@ -23,25 +23,25 @@ @@ -23,25 +23,25 @@
<meta property="og:url" content="https://jumble.imwald.eu" />
<meta property="og:type" content="website" />
<meta property="og:title" content="Jumble - Imwald Edition 🌲" />
<meta property="og:title" content="Imwald 🌲" />
<meta
property="og:description"
content="A user-friendly Nostr client focused on relay feed browsing and relay discovery. The Imwald edition focuses on publications and articles."
content="Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery."
/>
<meta
property="og:image"
content="https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true"
content="https://jumble.imwald.eu/pwa-512x512.png"
/>
<meta property="og:site_name" content="Jumble" />
<meta property="og:site_name" content="Imwald" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Jumble - Imwald Edition 🌲" />
<meta name="twitter:description" content="Jumble.imwald.eu - A user-friendly Nostr client focused on relay feed browsing and relay discovery." />
<meta name="twitter:image" content="https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true" />
<meta name="twitter:title" content="Imwald 🌲" />
<meta name="twitter:description" content="jumble.imwald.eu — a user-friendly Nostr client focused on relay feed browsing and relay discovery." />
<meta name="twitter:image" content="https://jumble.imwald.eu/pwa-512x512.png" />
</head>
<body>
<div id="root">
<div
id="jumble-boot-splash"
id="imwald-boot-splash"
style="
position: fixed;
inset: 0;
@ -63,24 +63,24 @@ @@ -63,24 +63,24 @@
border: 3px solid #e5e5e5;
border-top-color: #404040;
border-radius: 50%;
animation: jumble-spin 0.7s linear infinite;
animation: imwald-spin 0.7s linear infinite;
"
aria-hidden="true"
></div>
<p style="margin: 0; max-width: 18rem; text-align: center">Loading Jumble</p>
<p style="margin: 0; max-width: 18rem; text-align: center">Loading Imwald</p>
</div>
<style>
@keyframes jumble-spin {
@keyframes imwald-spin {
to {
transform: rotate(360deg);
}
}
@media (prefers-color-scheme: dark) {
#jumble-boot-splash {
#imwald-boot-splash {
color: #a3a3a3;
background: #171717;
}
#jumble-boot-splash div[aria-hidden] {
#imwald-boot-splash div[aria-hidden] {
border-color: #404040;
border-top-color: #d4d4d4;
}

4
package-lock.json generated

@ -1,11 +1,11 @@ @@ -1,11 +1,11 @@
{
"name": "jumble-imwald",
"name": "imwald",
"version": "21.4.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jumble-imwald",
"name": "imwald",
"version": "21.4.0",
"license": "MIT",
"dependencies": {

8
package.json

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
{
"name": "jumble-imwald",
"name": "imwald",
"version": "21.4.0",
"description": "A user-friendly Nostr client focused on relay feed browsing and relay discovery, forked from Jumble",
"description": "Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery",
"private": true,
"type": "module",
"main": "electron/main.cjs",
@ -136,8 +136,8 @@ @@ -136,8 +136,8 @@
"electron-builder": "^26.8.1"
},
"build": {
"appId": "eu.imwald.jumble",
"productName": "Jumble",
"appId": "eu.imwald.app",
"productName": "Imwald",
"copyright": "Copyright © Silberengel",
"directories": {
"output": "release"

6
public/manifest.webmanifest

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
{
"name": "Jumble Imwald Edition",
"name": "Imwald",
"author": "Silberengel",
"short_name": "Jumble",
"description": "A user-friendly Nostr client focused on relay feed browsing and relay discovery",
"short_name": "Imwald",
"description": "Imwald — a user-friendly Nostr client focused on relay feed browsing and relay discovery",
"start_url": "/",
"display": "standalone",
"background_color": "#FFFFFF",

8
scripts/README-deploy.md

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
# Deploy Jumble with docker-compose.prod.yml (remote server)
# Deploy Imwald with docker-compose.prod.yml (remote server)
Workflow: **build and push locally****pull and run on the server**.
@ -13,8 +13,10 @@ docker login # once, if needed @@ -13,8 +13,10 @@ docker login # once, if needed
This builds both images and pushes two tags each (`latest` and the version from `package.json`, e.g. `17.0.0`):
- **Main app:** `silberengel/imwald-jumble`
- **NIP-66 monitor:** `silberengel/imwald-jumble-nip66-monitor`
- **Main app (Imwald):** `silberengel/imwald-jumble`
- **NIP-66 monitor:** `silberengel/imwald-jumble-nip66-monitor`
Registry paths keep the historical `imwald-jumble` name; retagging to e.g. `silberengel/imwald` is optional and requires updating `docker-compose.prod.yml` and pull scripts.
## Remote server: one-time setup

12
scripts/build-and-push-prod.sh

@ -4,8 +4,8 @@ @@ -4,8 +4,8 @@
# Run from repo root. Requires: docker, docker login, git.
#
# Optional env:
# JUMBLE_PROXY_SERVER_URL — build-arg VITE_PROXY_SERVER (default https://jumble.imwald.eu).
# Must match the public origin where Apache serves the app; Apache proxies /sites/ → :8090, not this container.
# IMWALD_PROXY_SERVER_URL — build-arg VITE_PROXY_SERVER (default https://jumble.imwald.eu).
# Alias: JUMBLE_PROXY_SERVER_URL (deprecated). Must match the public origin where Apache serves the app.
# READ_ALOUD_TTS_URL — build-arg VITE_READ_ALOUD_TTS_URL (default /api/piper-tts).
# Same-origin: Apache proxies /api/piper-tts → aitherboard (e.g. :9876). Override only if you use CORS on another host.
set -e
@ -20,13 +20,13 @@ IMAGE_MONITOR="silberengel/imwald-jumble-nip66-monitor" @@ -20,13 +20,13 @@ IMAGE_MONITOR="silberengel/imwald-jumble-nip66-monitor"
# OG / link-preview HTML: VITE_PROXY_SERVER is baked into the client bundle at image build time (not runtime).
# Use public origin only (no /proxy path): web.service builds <origin>/sites/?url=…
# Override: JUMBLE_PROXY_SERVER_URL=https://other.example ./scripts/build-and-push-prod.sh
JUMBLE_PROXY_SERVER_URL="${JUMBLE_PROXY_SERVER_URL:-https://jumble.imwald.eu}"
# Override: IMWALD_PROXY_SERVER_URL=https://other.example ./scripts/build-and-push-prod.sh
PROXY_ORIGIN="${IMWALD_PROXY_SERVER_URL:-${JUMBLE_PROXY_SERVER_URL:-https://jumble.imwald.eu}}"
READ_ALOUD_TTS_URL="${READ_ALOUD_TTS_URL:-/api/piper-tts}"
echo "Building main app (version: $VERSION, VITE_PROXY_SERVER=$JUMBLE_PROXY_SERVER_URL, VITE_READ_ALOUD_TTS_URL=$READ_ALOUD_TTS_URL)"
echo "Building main app (version: $VERSION, VITE_PROXY_SERVER=$PROXY_ORIGIN, VITE_READ_ALOUD_TTS_URL=$READ_ALOUD_TTS_URL)"
docker build \
--build-arg "VITE_PROXY_SERVER=$JUMBLE_PROXY_SERVER_URL" \
--build-arg "VITE_PROXY_SERVER=$PROXY_ORIGIN" \
--build-arg "VITE_READ_ALOUD_TTS_URL=$READ_ALOUD_TTS_URL" \
-t "$IMAGE_APP:latest" -t "$IMAGE_APP:$VERSION" .

6
src/components/AboutInfoDialog/index.tsx

@ -43,9 +43,9 @@ export default function AboutInfoDialog({ children }: { children: React.ReactNod @@ -43,9 +43,9 @@ export default function AboutInfoDialog({ children }: { children: React.ReactNod
const content = (
<>
<div className="text-xl font-semibold">Jumble 🌲</div>
<div className="text-xl font-semibold">Imwald 🌲</div>
<div className="text-muted-foreground">
A user-friendly Nostr client focused on relay feed browsing and relay discovery
A user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery
</div>
<div className="text-sm text-muted-foreground">
Version: v{import.meta.env.APP_VERSION}
@ -74,7 +74,7 @@ export default function AboutInfoDialog({ children }: { children: React.ReactNod @@ -74,7 +74,7 @@ export default function AboutInfoDialog({ children }: { children: React.ReactNod
Imwald fork
</Button>
<div className="text-sm text-muted-foreground mt-1">
If you like Jumble, please consider giving it a star
If you like Imwald, please consider giving it a star
</div>
</div>
</>

2
src/components/AccountManager/index.tsx

@ -76,7 +76,7 @@ function AccountManagerNav({ @@ -76,7 +76,7 @@ function AccountManagerNav({
onClick={() => {
const wizard = new NstartModal({
baseUrl: 'https://nstart.me',
an: 'Jumble',
an: 'Imwald',
am: themeSetting,
al: i18n.language.slice(0, 2),
onComplete: ({ nostrLogin }) => {

2
src/components/CacheRelaysSetting/index.tsx

@ -221,7 +221,7 @@ export default function CacheRelaysSetting() { @@ -221,7 +221,7 @@ export default function CacheRelaysSetting() {
await indexedDb.clearPiperTtsCache()
// Clear localStorage (but keep essential settings like theme, accounts, etc.)
// We'll only clear Jumble-specific cache keys, not all localStorage
// We'll only clear Imwald-specific cache keys, not all localStorage
const cacheKeys = Object.values(StorageKey).filter(key =>
key.includes('CACHE') || key.includes('EVENT') || key.includes('FEED') || key.includes('NOTIFICATION')
)

8
src/components/Donation/index.tsx

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { Button } from '@/components/ui/button'
import { JUMBLE_PUBKEY } from '@/constants'
import { IMWALD_MAINTAINER_PUBKEY } from '@/constants'
import { cn } from '@/lib/utils'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -14,9 +14,9 @@ export default function Donation({ className }: { className?: string }) { @@ -14,9 +14,9 @@ export default function Donation({ className }: { className?: string }) {
return (
<div className={cn('p-4 border rounded-lg space-y-4', className)}>
<div className="text-center font-semibold">{t('Enjoying Jumble?')}</div>
<div className="text-center font-semibold">{t('Enjoying Imwald?')}</div>
<div className="text-center text-muted-foreground">
{t('Your donation helps me maintain Jumble and make it better! 😊')}
{t('Your donation helps me maintain Imwald and make it better! 😊')}
</div>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{[
@ -45,7 +45,7 @@ export default function Donation({ className }: { className?: string }) { @@ -45,7 +45,7 @@ export default function Donation({ className }: { className?: string }) {
<ZapDialog
open={open}
setOpen={setOpen}
pubkey={JUMBLE_PUBKEY}
pubkey={IMWALD_MAINTAINER_PUBKEY}
defaultAmount={donationAmount}
/>
</div>

2
src/components/ErrorBoundary.tsx

@ -6,7 +6,7 @@ import logger from '@/lib/logger' @@ -6,7 +6,7 @@ import logger from '@/lib/logger'
import { isChunkLoadFailureMessage, tryStaleChunkReloadOnce } from '@/lib/stale-chunk-recovery'
const ISSUES_URL =
'https://gitrepublic.imwald.eu/repos/npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z/jumble-imwald-edition?tab=issues'
'https://gitrepublic.imwald.eu/repos/npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z/imwald?tab=issues'
/** HMR can remount children before parents; context hooks throw. One recovery reload fixes it. */
const CONTEXT_RECOVERY_RELOAD_KEY = 'jumble-context-recovery-reload-at'

10
src/components/EventArchiveCacheSettings/index.tsx

@ -7,7 +7,7 @@ import { @@ -7,7 +7,7 @@ import {
EVENT_ARCHIVE_DEFAULTS,
getEventArchiveConfig
} from '@/lib/event-archive-config'
import { isJumbleElectron, isMobileBrowserProfile } from '@/lib/client-platform'
import { isImwaldElectron, isMobileBrowserProfile } from '@/lib/client-platform'
import client from '@/services/client.service'
import { invalidateArchiveFootprintCache } from '@/services/event-archive.service'
import { useCallback, useEffect, useMemo, useState } from 'react'
@ -15,7 +15,7 @@ import { useTranslation } from 'react-i18next' @@ -15,7 +15,7 @@ import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
function platformLabel(): string {
if (isJumbleElectron()) return 'desktop-app'
if (isImwaldElectron()) return 'desktop-app'
if (isMobileBrowserProfile()) return 'mobile-web'
return 'desktop-web'
}
@ -101,7 +101,7 @@ export default function EventArchiveCacheSettings() { @@ -101,7 +101,7 @@ export default function EventArchiveCacheSettings() {
id="archive-max-mb"
inputMode="numeric"
placeholder={String(
isJumbleElectron()
isImwaldElectron()
? EVENT_ARCHIVE_DEFAULTS.maxMbElectron
: isMobileBrowserProfile()
? EVENT_ARCHIVE_DEFAULTS.maxMbMobile
@ -119,7 +119,7 @@ export default function EventArchiveCacheSettings() { @@ -119,7 +119,7 @@ export default function EventArchiveCacheSettings() {
id="archive-max-events"
inputMode="numeric"
placeholder={String(
isJumbleElectron()
isImwaldElectron()
? EVENT_ARCHIVE_DEFAULTS.maxEventsElectron
: isMobileBrowserProfile()
? EVENT_ARCHIVE_DEFAULTS.maxEventsMobile
@ -139,7 +139,7 @@ export default function EventArchiveCacheSettings() { @@ -139,7 +139,7 @@ export default function EventArchiveCacheSettings() {
id="session-lru"
inputMode="numeric"
placeholder={String(
isJumbleElectron()
isImwaldElectron()
? EVENT_ARCHIVE_DEFAULTS.sessionLruElectron
: isMobileBrowserProfile()
? EVENT_ARCHIVE_DEFAULTS.sessionLruMobile

4
src/components/Note/MarkdownArticle/MarkdownArticle.tsx

@ -3924,7 +3924,7 @@ function parseInlineMarkdownLegacy( @@ -3924,7 +3924,7 @@ function parseInlineMarkdownLegacy(
): React.ReactNode[] {
if (isContentSpacingDebug() && text.includes('nostr:')) {
// eslint-disable-next-line no-console
console.log('[jumble content-spacing] parseInlineMarkdown:before-normalize', {
console.log('[imwald content-spacing] parseInlineMarkdown:before-normalize', {
keyPrefix,
repr: reprString(text)
})
@ -3936,7 +3936,7 @@ function parseInlineMarkdownLegacy( @@ -3936,7 +3936,7 @@ function parseInlineMarkdownLegacy(
text = text.replace(/[ \t]{2,}/g, ' ')
if (isContentSpacingDebug() && text.includes('nostr:')) {
// eslint-disable-next-line no-console
console.log('[jumble content-spacing] parseInlineMarkdown:after-normalize', {
console.log('[imwald content-spacing] parseInlineMarkdown:after-normalize', {
keyPrefix,
repr: reprString(text)
})

2
src/components/Note/index.tsx

@ -470,7 +470,7 @@ export default function Note({ @@ -470,7 +470,7 @@ export default function Note({
data-username
className={`font-semibold truncate text-foreground ${size === 'small' ? 'text-sm' : ''}`}
>
{t('Jumble Imwald synthetic event')}
{t('Imwald synthetic event')}
</span>
<ClientTag event={event} />
</div>

6
src/components/NoteOptions/useMenuActions.tsx

@ -847,7 +847,7 @@ export function useMenuActions({ @@ -847,7 +847,7 @@ export function useMenuActions({
: []),
{
icon: Link,
label: t('Share with Jumble'),
label: t('Share with Imwald'),
onClick: () => {
const noteId = getNoteBech32Id(event)
// Contextual URL when on Spells (e.g. discussions faux-spell); plain /notes/{id} otherwise
@ -859,8 +859,8 @@ export function useMenuActions({ @@ -859,8 +859,8 @@ export function useMenuActions({
: currentPrimaryPage === 'follows-latest'
? `/follows-latest/notes/${noteId}`
: `/notes/${noteId}`
const jumbleUrl = `https://jumble.imwald.eu${path}`
navigator.clipboard.writeText(jumbleUrl)
const appShareUrl = `https://jumble.imwald.eu${path}`
navigator.clipboard.writeText(appShareUrl)
closeDrawer()
}
},

2
src/components/PostEditor/PostOptions.tsx

@ -54,7 +54,7 @@ export default function PostOptions({ @@ -54,7 +54,7 @@ export default function PostOptions({
/>
</div>
<div className="text-muted-foreground text-xs">
{t('Show others this was sent via Jumble')}
{t('Show others this was sent via Imwald')}
</div>
</div>

4
src/components/RssFeedList/ArticleUrlsSection.tsx

@ -12,10 +12,10 @@ export function ArticleUrlsSection({ @@ -12,10 +12,10 @@ export function ArticleUrlsSection({
}) {
const { t } = useTranslation()
return (
<section className="space-y-3" aria-labelledby="jumble-article-urls-heading">
<section className="space-y-3" aria-labelledby="imwald-article-urls-heading">
<div className="space-y-1 px-0.5">
<h2
id="jumble-article-urls-heading"
id="imwald-article-urls-heading"
className="text-xs font-semibold uppercase tracking-wide text-muted-foreground"
>
{t('Article URLs')}

4
src/components/RssFeedList/RssUnifiedScopeSection.tsx

@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next' @@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next'
export function RssUnifiedScopeSection({ children }: { children: ReactNode }) {
const { t } = useTranslation()
return (
<section className="space-y-3" aria-labelledby="jumble-rss-unified-heading">
<section className="space-y-3" aria-labelledby="imwald-rss-unified-heading">
<div className="space-y-1 px-0.5">
<h2
id="jumble-rss-unified-heading"
id="imwald-rss-unified-heading"
className="text-xs font-semibold uppercase tracking-wide text-muted-foreground"
>
{t('RSS feed column title')}

2
src/components/Settings/SettingsMenuBody.tsx

@ -156,7 +156,7 @@ export default function SettingsMenuBody({ className }: { className?: string }) @@ -156,7 +156,7 @@ export default function SettingsMenuBody({ className }: { className?: string })
</SettingItem>
</AboutInfoDialog>
<div className="py-6 text-center text-muted-foreground">
<div className="text-lg font-semibold">Jumble</div>
<div className="text-lg font-semibold">Imwald</div>
<div className="font-semibold text-green-600 dark:text-green-500">Im Wald</div>
</div>
</div>

2
src/components/Sidebar/DownloadDesktopSidebarButton.tsx

@ -17,7 +17,7 @@ function resolveDesktopDownloadUrl(): string | null { @@ -17,7 +17,7 @@ function resolveDesktopDownloadUrl(): string | null {
/** Bottom-of-sidebar link to native (Electron) builds; hidden in the packaged app and when URL is disabled. */
export default function DownloadDesktopSidebarButton() {
const { t } = useTranslation()
if (typeof window !== 'undefined' && window.jumbleElectron?.isElectron) {
if (typeof window !== 'undefined' && window.imwaldElectron?.isElectron) {
return null
}
const href = resolveDesktopDownloadUrl()

6
src/components/WebPreview/index.tsx

@ -145,7 +145,7 @@ export default function WebPreview({ url, className }: { url: string; className? @@ -145,7 +145,7 @@ export default function WebPreview({ url, className }: { url: string; className?
}
}, [cleanedUrl])
const isInternalJumbleLink = useMemo(() => hostname === 'jumble.imwald.eu', [hostname])
const isInternalAppLink = useMemo(() => hostname === 'jumble.imwald.eu', [hostname])
// Extract replaceable event info (d-tag and pubkey) from URL patterns
// This is separate from nostrIdentifier to allow fetching without kind
@ -462,10 +462,10 @@ export default function WebPreview({ url, className }: { url: string; className? @@ -462,10 +462,10 @@ export default function WebPreview({ url, className }: { url: string; className?
}
// Prefer the page's own Open Graph / meta when the fetch returns anything useful.
const hasOpengraphData = !isInternalJumbleLink && (title || description || image)
const hasOpengraphData = !isInternalAppLink && (title || description || image)
// While OG is loading for external URLs, avoid flashing the nostr / hostname fallback.
if (!isInternalJumbleLink && ogLoading) {
if (!isInternalAppLink && ogLoading) {
return (
<div
className={cn('p-2 flex w-full border rounded-lg overflow-hidden gap-2 max-w-full', className)}

20
src/constants.ts

@ -1,8 +1,15 @@ @@ -1,8 +1,15 @@
import { kinds, type Filter } from 'nostr-tools'
/** API base URL; override with VITE_JUMBLE_API_BASE_URL for forks (e.g. https://api.jumble.imwald.eu). */
export const JUMBLE_API_BASE_URL =
(import.meta.env.VITE_JUMBLE_API_BASE_URL as string | undefined) ?? 'https://api.jumble.imwald.eu'
/**
* API base URL. Prefer `VITE_IMWALD_API_BASE_URL`; `VITE_JUMBLE_API_BASE_URL` is still read for existing deploys.
*/
export const IMWALD_API_BASE_URL =
(import.meta.env.VITE_IMWALD_API_BASE_URL as string | undefined)?.trim() ||
(import.meta.env.VITE_JUMBLE_API_BASE_URL as string | undefined)?.trim() ||
'https://api.jumble.imwald.eu'
/** @deprecated Use {@link IMWALD_API_BASE_URL} */
export const JUMBLE_API_BASE_URL = IMWALD_API_BASE_URL
/** Git Republic web UI for repository links; override with VITE_GITREPUBLIC_WEB_BASE_URL for self-hosted. */
export const GITREPUBLIC_WEB_BASE_URL = (
@ -689,7 +696,12 @@ export const EMOJI_REGEX = @@ -689,7 +696,12 @@ export const EMOJI_REGEX =
export const YOUTUBE_URL_REGEX =
/https?:\/\/(?:(?:www|m)\.)?(?:youtube\.com\/(?:watch\?[^#\s]*|embed\/[\w-]+|shorts\/[\w-]+|live\/[\w-]+)|youtu\.be\/[\w-]+)(?:\?[^#\s]*)?(?:#[^\s]*)?/gi
export const JUMBLE_PUBKEY = 'f4eb8e62add1340b9cadcd9861e669b2e907cea534e0f7f3ac974c11c758a51a'
/** Maintainer / official zap recipient pubkey for this distribution. */
export const IMWALD_MAINTAINER_PUBKEY =
'f4eb8e62add1340b9cadcd9861e669b2e907cea534e0f7f3ac974c11c758a51a'
/** @deprecated Use {@link IMWALD_MAINTAINER_PUBKEY} */
export const JUMBLE_PUBKEY = IMWALD_MAINTAINER_PUBKEY
export const CODY_PUBKEY = '8125b911ed0e94dbe3008a0be48cfe5cd0c0b05923cfff917ae7e87da8400883'
export const SILBERENGEL_PUBKEY = 'fd208ee8c8f283780a9552896e4823cc9dc6bfd442063889577106940fd927c1'

14
src/i18n/locales/ar.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'مشاركة مع Jumble',
'Share with Imwald': 'مشاركة مع Imwald',
'Share with Alexandria': 'مشاركة مع Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -309,7 +309,7 @@ export default { @@ -309,7 +309,7 @@ export default {
'Add an Account': 'إضافة حساب',
'More options': 'المزيد من الخيارات',
'Add client tag': 'إضافة وسم العميل',
'Show others this was sent via Jumble': 'عرض أن هذه الرسالة أُرسلت عبر Jumble',
'Show others this was sent via Imwald': 'عرض أن هذه الرسالة أُرسلت عبر Imwald',
'Are you sure you want to logout?': 'هل أنت متأكد أنك تريد تسجيل الخروج؟',
'relay sets': 'مجموعات الريلاي',
edit: 'تعديل',
@ -463,7 +463,7 @@ export default { @@ -463,7 +463,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -497,9 +497,9 @@ export default { @@ -497,9 +497,9 @@ export default {
Boosts: 'Boosts',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'هل تستمتع بـ Jumble؟',
'Your donation helps me maintain Jumble and make it better! 😊':
'تبرعك يساعد في صيانة Jumble وتحسينه! 😊',
'Enjoying Imwald?': 'هل تستمتع بـ Imwald؟',
'Your donation helps me maintain Imwald and make it better! 😊':
'تبرعك يساعد في صيانة Imwald وتحسينه! 😊',
'Earlier notifications': 'الإشعارات السابقة',
'Temporarily display this note': 'عرض هذه الملاحظة مؤقتاً',
buttonFollowing: 'جارٍ المتابعة',
@ -668,7 +668,7 @@ export default { @@ -668,7 +668,7 @@ export default {
Translation: 'الترجمة',
Balance: 'الرصيد',
characters: 'الحروف',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'يمكنك استخدام مفتاح API هذا في أي مكان آخر يدعم LibreTranslate. عنوان الخدمة هو {{serviceUrl}}',
'Top up': 'إعادة شحن',
'Will receive: {n} characters': 'ستتلقى: {{n}} حروف',

14
src/i18n/locales/de.ts

@ -178,7 +178,7 @@ export default { @@ -178,7 +178,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Speichern…',
'Share with Jumble': 'Mit Jumble teilen',
'Share with Imwald': 'Mit Imwald teilen',
'Share with Alexandria': 'Mit Alexandria teilen',
'Start video call': 'Videoanruf starten',
'Copy call invite link': 'Anruf-Einladungslink kopieren',
@ -322,7 +322,7 @@ export default { @@ -322,7 +322,7 @@ export default {
'Add an Account': 'Konto hinzufügen',
'More options': 'Mehr Optionen',
'Add client tag': 'Client-Tag hinzufügen',
'Show others this was sent via Jumble': 'Anderen zeigen, dass dies über Jumble gesendet wurde',
'Show others this was sent via Imwald': 'Anderen zeigen, dass dies über Imwald gesendet wurde',
'Are you sure you want to logout?': 'Bist du sicher, dass du dich abmelden möchtest?',
'relay sets': 'Relay-Sets',
edit: 'bearbeiten',
@ -479,7 +479,7 @@ export default { @@ -479,7 +479,7 @@ export default {
'Notification boost detail': 'Die Vorschau darüber ist der Originalbeitrag.',
'Notification poll vote summary': 'Hat an der Umfrage darüber teilgenommen.',
'Notification poll vote options count': '{{count}} Option(en) gewählt',
'Jumble Imwald synthetic event': 'Jumble Imwald – synthetisches Ereignis',
'Imwald synthetic event': 'Imwald – synthetisches Ereignis',
'+ Add a URL to this list': 'URL zur Liste hinzufügen',
'Add a web URL': 'Web-URL hinzufügen',
'Add web URL to feed description':
@ -513,9 +513,9 @@ export default { @@ -513,9 +513,9 @@ export default {
Boosts: 'Boosts',
Badges: 'Abzeichen',
Reports: 'Meldungen',
'Enjoying Jumble?': 'Gefällt dir Jumble?',
'Your donation helps me maintain Jumble and make it better! 😊':
'Deine Spende hilft mir, Jumble zu pflegen und zu verbessern! 😊',
'Enjoying Imwald?': 'Gefällt dir Imwald?',
'Your donation helps me maintain Imwald and make it better! 😊':
'Deine Spende hilft mir, Imwald zu pflegen und zu verbessern! 😊',
'Earlier notifications': 'Frühere Benachrichtigungen',
'Temporarily display this note': 'Notiz vorübergehend anzeigen',
buttonFollowing: 'Folge',
@ -695,7 +695,7 @@ export default { @@ -695,7 +695,7 @@ export default {
Translation: 'Übersetzung',
Balance: 'Guthaben',
characters: 'Zeichen',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'Du kannst diesen API-Schlüssel überall dort verwenden, wo LibreTranslate unterstützt wird. Die Service-URL ist {{serviceUrl}}',
'Top up': 'Aufladen',
'Will receive: {n} characters': 'Erhalte: {{n}} Zeichen',

14
src/i18n/locales/en.ts

@ -174,7 +174,7 @@ export default { @@ -174,7 +174,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Share with Jumble',
'Share with Imwald': 'Share with Imwald',
'Share with Alexandria': 'Share with Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -321,7 +321,7 @@ export default { @@ -321,7 +321,7 @@ export default {
'Add an Account': 'Add an Account',
'More options': 'More options',
'Add client tag': 'Add client tag',
'Show others this was sent via Jumble': 'Show others this was sent via Jumble',
'Show others this was sent via Imwald': 'Show others this was sent via Imwald',
'Are you sure you want to logout?': 'Are you sure you want to logout?',
'relay sets': 'relay sets',
edit: 'edit',
@ -476,7 +476,7 @@ export default { @@ -476,7 +476,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -510,9 +510,9 @@ export default { @@ -510,9 +510,9 @@ export default {
Boosts: 'Boosts',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Enjoying Jumble?',
'Your donation helps me maintain Jumble and make it better! 😊':
'Your donation helps me maintain Jumble and make it better! 😊',
'Enjoying Imwald?': 'Enjoying Imwald?',
'Your donation helps me maintain Imwald and make it better! 😊':
'Your donation helps me maintain Imwald and make it better! 😊',
'Earlier notifications': 'Earlier notifications',
'Temporarily display this note': 'Temporarily display this note',
buttonFollowing: 'Following',
@ -692,7 +692,7 @@ export default { @@ -692,7 +692,7 @@ export default {
Translation: 'Translation',
Balance: 'Balance',
characters: 'characters',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'You can use this API key anywhere else that supports LibreTranslate. The service URL is {{serviceUrl}}',
'Top up': 'Top up',
'Will receive: {n} characters': 'Will receive: {{n}} characters',

14
src/i18n/locales/es.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Compartir con Jumble',
'Share with Imwald': 'Compartir con Imwald',
'Share with Alexandria': 'Compartir con Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': 'Agregar una cuenta',
'More options': 'Más opciones',
'Add client tag': 'Agregar etiqueta de cliente',
'Show others this was sent via Jumble': 'Mostrar a otros que esto se envió vía Jumble',
'Show others this was sent via Imwald': 'Mostrar a otros que esto se envió vía Imwald',
'Are you sure you want to logout?': '¿Estás seguro de que deseas cerrar sesión?',
'relay sets': 'conjuntos de relés',
edit: 'editar',
@ -467,7 +467,7 @@ export default { @@ -467,7 +467,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -501,9 +501,9 @@ export default { @@ -501,9 +501,9 @@ export default {
Boosts: 'Boosts',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': '¿Te gusta Jumble?',
'Your donation helps me maintain Jumble and make it better! 😊':
'¡Tu donación me ayuda a mantener y mejorar Jumble! 😊',
'Enjoying Imwald?': '¿Te gusta Imwald?',
'Your donation helps me maintain Imwald and make it better! 😊':
'¡Tu donación me ayuda a mantener y mejorar Imwald! 😊',
'Earlier notifications': 'Notificaciones anteriores',
'Temporarily display this note': 'Mostrar esta nota temporalmente',
buttonFollowing: 'Siguiendo',
@ -673,7 +673,7 @@ export default { @@ -673,7 +673,7 @@ export default {
Translation: 'Traducción',
Balance: 'Saldo',
characters: 'caracteres',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'Puedes usar esta clave API en cualquier otro lugar que soporte LibreTranslate. La URL del servicio es {{serviceUrl}}',
'Top up': 'Recargar',
'Will receive: {n} characters': 'Recibirás: {{n}} caracteres',

14
src/i18n/locales/fa.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'اشتراکگذاری با Jumble',
'Share with Imwald': 'اشتراکگذاری با Imwald',
'Share with Alexandria': 'اشتراکگذاری با Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': 'افزودن حساب',
'More options': 'گزینههای بیشتر',
'Add client tag': 'افزودن برچسب کلاینت',
'Show others this was sent via Jumble': 'به دیگران نشان دهید که از طریق Jumble ارسال شده',
'Show others this was sent via Imwald': 'به دیگران نشان دهید که از طریق Imwald ارسال شده',
'Are you sure you want to logout?': 'آیا مطمئن هستید که میخواهید خارج شوید؟',
'relay sets': 'مجموعههای رله',
edit: 'ویرایش',
@ -466,7 +466,7 @@ export default { @@ -466,7 +466,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -500,9 +500,9 @@ export default { @@ -500,9 +500,9 @@ export default {
Boosts: 'بوستها',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'از Jumble لذت میبرید؟',
'Your donation helps me maintain Jumble and make it better! 😊':
'کمک مالی شما به من در نگهداری Jumble و بهتر کردن آن کمک میکند! 😊',
'Enjoying Imwald?': 'از Imwald لذت میبرید؟',
'Your donation helps me maintain Imwald and make it better! 😊':
'کمک مالی شما به من در نگهداری Imwald و بهتر کردن آن کمک میکند! 😊',
'Earlier notifications': 'اعلانهای قبلی',
'Temporarily display this note': 'نمایش موقت این یادداشت',
buttonFollowing: 'دنبال میکنم',
@ -671,7 +671,7 @@ export default { @@ -671,7 +671,7 @@ export default {
Translation: 'ترجمه',
Balance: 'موجودی',
characters: 'کاراکتر',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'میتوانید از این کلید API در هر جای دیگری که از LibreTranslate پشتیبانی میکند استفاده کنید. آدرس سرویس {{serviceUrl}} است',
'Top up': 'شارژ',
'Will receive: {n} characters': 'دریافت خواهید کرد: {{n}} کاراکتر',

14
src/i18n/locales/fr.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Partager avec Jumble',
'Share with Imwald': 'Partager avec Imwald',
'Share with Alexandria': 'Partager avec Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': 'Ajouter un compte',
'More options': "Plus d'options",
'Add client tag': 'Ajouter une étiquette client',
'Show others this was sent via Jumble': 'Montrer aux autres que cela a été envoyé via Jumble',
'Show others this was sent via Imwald': 'Montrer aux autres que cela a été envoyé via Imwald',
'Are you sure you want to logout?': 'Êtes-vous sûr de vouloir vous déconnecter ?',
'relay sets': 'groupes de relais',
edit: 'modifier',
@ -466,7 +466,7 @@ export default { @@ -466,7 +466,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -500,9 +500,9 @@ export default { @@ -500,9 +500,9 @@ export default {
Boosts: 'Boosts',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Vous appréciez Jumble ?',
'Your donation helps me maintain Jumble and make it better! 😊':
"Votre don m'aide à maintenir Jumble et à l'améliorer ! 😊",
'Enjoying Imwald?': 'Vous appréciez Imwald ?',
'Your donation helps me maintain Imwald and make it better! 😊':
"Votre don m'aide à maintenir Imwald et à l'améliorer ! 😊",
'Earlier notifications': 'Notifications antérieures',
'Temporarily display this note': 'Afficher temporairement cette note',
buttonFollowing: 'Suivi',
@ -673,7 +673,7 @@ export default { @@ -673,7 +673,7 @@ export default {
Translation: 'Traduction',
Balance: 'Solde',
characters: 'caractères',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'Vous pouvez utiliser cette clé API ailleurs qui prend en charge LibreTranslate. L’URL du service est {{serviceUrl}}',
'Top up': 'Recharger',
'Will receive: {n} characters': 'Vous recevrez : {{n}} caractères',

16
src/i18n/locales/hi.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Jumbleथ शयर कर',
'Share with Imwald': 'Imwaldथ शयर कर',
'Share with Alexandria': 'Alexandria कथ शयर कर',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,8 +310,8 @@ export default { @@ -310,8 +310,8 @@ export default {
'Add an Account': 'अकट ज',
'More options': 'अधिक विकलप',
'Add client tag': 'कट टग ज',
'Show others this was sent via Jumble':
'दसरिि यह Jumbleयम स गय',
'Show others this was sent via Imwald':
'दसरिि यह Imwaldयम स गय',
'Are you sure you want to logout?': 'क आप वकई लगआउट करनहत?',
'relay sets': 'रिट',
edit: 'सित कर',
@ -467,7 +467,7 @@ export default { @@ -467,7 +467,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -501,9 +501,9 @@ export default { @@ -501,9 +501,9 @@ export default {
Boosts: 'बट',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Jumble आनद ल रह?',
'Your donation helps me maintain Jumble and make it better! 😊':
'आपकन म Jumble बनए रखन और इसहतर बन मदद करत! 😊',
'Enjoying Imwald?': 'Imwald आनद ल रह?',
'Your donation helps me maintain Imwald and make it better! 😊':
'आपकन म Imwald बनए रखन और इसहतर बन मदद करत! 😊',
'Earlier notifications': 'पचन',
'Temporarily display this note': 'इस नट क असप सरदरित कर',
buttonFollowing: 'फ कर रह',
@ -672,7 +672,7 @@ export default { @@ -672,7 +672,7 @@ export default {
Translation: 'अनद',
Balance: 'बस',
characters: 'अकषर',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'आप इस API क कह उपयग कर सकत LibreTranslate क समरथन करत। स URL ह {{serviceUrl}}',
'Top up': 'टप अप',
'Will receive: {n} characters': 'पत ह: {{n}} अकषर',

16
src/i18n/locales/it.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Condividi con Jumble',
'Share with Imwald': 'Condividi con Imwald',
'Share with Alexandria': 'Condividi con Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,8 +310,8 @@ export default { @@ -310,8 +310,8 @@ export default {
'Add an Account': 'Aggiungi un Account',
'More options': 'Più opzioni',
'Add client tag': 'Aggiungi etichetta del client',
'Show others this was sent via Jumble':
'Mostra agli altri che questo è stato inviato tramite Jumble',
'Show others this was sent via Imwald':
'Mostra agli altri che questo è stato inviato tramite Imwald',
'Are you sure you want to logout?': 'Sei sicuro di volerti scollegare?',
'relay sets': 'set di relay',
edit: 'modifica',
@ -467,7 +467,7 @@ export default { @@ -467,7 +467,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -501,9 +501,9 @@ export default { @@ -501,9 +501,9 @@ export default {
Boosts: 'Boost',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Ti sta piacendo Jumble?',
'Your donation helps me maintain Jumble and make it better! 😊':
'La tua donazione mi aiuta a mantenere Jumble e a migliorarlo! 😊',
'Enjoying Imwald?': 'Ti sta piacendo Imwald?',
'Your donation helps me maintain Imwald and make it better! 😊':
'La tua donazione mi aiuta a mantenere Imwald e a migliorarlo! 😊',
'Earlier notifications': 'Notifiche precedenti',
'Temporarily display this note': 'Visualizza temporaneamente questa nota',
buttonFollowing: 'Seguendo',
@ -673,7 +673,7 @@ export default { @@ -673,7 +673,7 @@ export default {
Translation: 'Traduzione',
Balance: 'Saldo',
characters: 'caratteri',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
"Puoi utilizzare questa chiave API ovunque supporti LibreTranslate. L'URL del servizio è {{serviceUrl}}",
'Top up': 'Torna al saldo',
'Will receive: {n} characters': 'Riceverai: {{n}} caratteri',

14
src/i18n/locales/ja.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Jumbleで共有',
'Share with Imwald': 'Imwaldで共有',
'Share with Alexandria': 'Alexandriaで共有',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': 'アカウントを追加',
'More options': 'その他のオプション',
'Add client tag': 'クライアントタグを追加',
'Show others this was sent via Jumble': 'これがJumble経由で送信されたことを表示',
'Show others this was sent via Imwald': 'これがImwald経由で送信されたことを表示',
'Are you sure you want to logout?': '本当にログアウトしますか?',
'relay sets': 'リレイセット',
edit: '編集',
@ -465,7 +465,7 @@ export default { @@ -465,7 +465,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -499,9 +499,9 @@ export default { @@ -499,9 +499,9 @@ export default {
Boosts: 'ブースト',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Jumbleをお楽しみですか?',
'Your donation helps me maintain Jumble and make it better! 😊':
'あなたの寄付はJumbleの維持と改善に役立ちます! 😊',
'Enjoying Imwald?': 'Imwaldをお楽しみですか?',
'Your donation helps me maintain Imwald and make it better! 😊':
'あなたの寄付はImwaldの維持と改善に役立ちます! 😊',
'Earlier notifications': '以前の通知',
'Temporarily display this note': 'このノートを一時的に表示',
buttonFollowing: 'フォロー中',
@ -669,7 +669,7 @@ export default { @@ -669,7 +669,7 @@ export default {
Translation: '翻訳',
Balance: '残高',
characters: '文字',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'このAPIキーは、LibreTranslateをサポートする他の場所でも使用できます。サービスURLは{{serviceUrl}}です',
'Top up': 'チャージ',
'Will receive: {n} characters': '受け取る文字数: {{n}} 文字',

14
src/i18n/locales/ko.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Jumble로 공유',
'Share with Imwald': 'Imwald로 공유',
'Share with Alexandria': 'Alexandria로 공유',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': '계정 추가',
'More options': '더 많은 옵션',
'Add client tag': '클라이언트 태그 추가',
'Show others this was sent via Jumble': '이 노트가 Jumble을 통해 전송되었음을 표시',
'Show others this was sent via Imwald': '이 노트가 Imwald을 통해 전송되었음을 표시',
'Are you sure you want to logout?': '로그아웃 하시겠습니까?',
'relay sets': '릴레이 세트',
edit: '편집',
@ -464,7 +464,7 @@ export default { @@ -464,7 +464,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -498,9 +498,9 @@ export default { @@ -498,9 +498,9 @@ export default {
Boosts: '부스트',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Jumble이 마음에 드시나요?',
'Your donation helps me maintain Jumble and make it better! 😊':
'후원해주시면 Jumble을 더 잘 유지하고 발전시킬 수 있습니다! 😊',
'Enjoying Imwald?': 'Imwald이 마음에 드시나요?',
'Your donation helps me maintain Imwald and make it better! 😊':
'후원해주시면 Imwald을 더 잘 유지하고 발전시킬 수 있습니다! 😊',
'Earlier notifications': '이전 알림',
'Temporarily display this note': '이 노트 임시 표시',
buttonFollowing: '팔로잉 중',
@ -668,7 +668,7 @@ export default { @@ -668,7 +668,7 @@ export default {
Translation: '번역',
Balance: '잔액',
characters: '글자',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'이 API 키는 LibreTranslate를 지원하는 모든 곳에서 사용할 수 있습니다. 서비스 주소: {{serviceUrl}}',
'Top up': '충전',
'Will receive: {n} characters': '{{n}} 글자를 받게 됩니다',

14
src/i18n/locales/pl.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Udostępnij przez Jumble',
'Share with Imwald': 'Udostępnij przez Imwald',
'Share with Alexandria': 'Udostępnij przez Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': 'Dodaj Konto',
'More options': 'Więcej opcji',
'Add client tag': 'Dodaj tag klienta',
'Show others this was sent via Jumble': 'Pokaż innym, że zostało to wysłane przez Jumble',
'Show others this was sent via Imwald': 'Pokaż innym, że zostało to wysłane przez Imwald',
'Are you sure you want to logout?': 'Czy na pewno chcesz się wylogować?',
'relay sets': 'Zestawy transmiterów',
edit: 'edytuj',
@ -464,7 +464,7 @@ export default { @@ -464,7 +464,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -498,9 +498,9 @@ export default { @@ -498,9 +498,9 @@ export default {
Boosts: 'Boosty',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Podoba ci się Jumble?',
'Your donation helps me maintain Jumble and make it better! 😊':
'Twoja darowizna pomoże mi utrzymać i ulepszać Jumble! 😊',
'Enjoying Imwald?': 'Podoba ci się Imwald?',
'Your donation helps me maintain Imwald and make it better! 😊':
'Twoja darowizna pomoże mi utrzymać i ulepszać Imwald! 😊',
'Earlier notifications': 'Wcześniejsze powiadomienia',
'Temporarily display this note': 'Tymczas wyświetl ten wpis',
buttonFollowing: 'Obserwujesz',
@ -670,7 +670,7 @@ export default { @@ -670,7 +670,7 @@ export default {
Translation: 'Tłumaczenie',
Balance: 'Saldo',
characters: 'znaków',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'Ten klucz API możesz używać wszędzie tam, gdzie obsługiwane jest LibreTranslate. Adres usługi to {{serviceUrl}}',
'Top up': 'Doładuj',
'Will receive: {n} characters': 'Otrzymasz: {{n}} znaków',

14
src/i18n/locales/pt-BR.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Compartilhar com Jumble',
'Share with Imwald': 'Compartilhar com Imwald',
'Share with Alexandria': 'Compartilhar com Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': 'Acessar conta',
'More options': 'Mais opções',
'Add client tag': 'Adicionar tag de cliente',
'Show others this was sent via Jumble': 'Mostrar aos outros que isso foi enviado via Jumble',
'Show others this was sent via Imwald': 'Mostrar aos outros que isso foi enviado via Imwald',
'Are you sure you want to logout?': 'Tem certeza de que deseja sair?',
'relay sets': 'Conjuntos de relay',
edit: 'Editar',
@ -466,7 +466,7 @@ export default { @@ -466,7 +466,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -500,9 +500,9 @@ export default { @@ -500,9 +500,9 @@ export default {
Boosts: 'Boosts',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Gostando do Jumble?',
'Your donation helps me maintain Jumble and make it better! 😊':
'Sua doação me ajuda a manter o Jumble e torná-lo melhor! 😊',
'Enjoying Imwald?': 'Gostando do Imwald?',
'Your donation helps me maintain Imwald and make it better! 😊':
'Sua doação me ajuda a manter o Imwald e torná-lo melhor! 😊',
'Earlier notifications': 'Notificações anteriores',
'Temporarily display this note': 'Exibir esta nota temporariamente',
buttonFollowing: 'Seguindo',
@ -672,7 +672,7 @@ export default { @@ -672,7 +672,7 @@ export default {
Translation: 'Tradução',
Balance: 'Saldo',
characters: 'caracteres',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'Esta chave API pode ser usada em qualquer outro lugar que suporte LibreTranslate. O URL do serviço é {{serviceUrl}}',
'Top up': 'Carregar saldo',
'Will receive: {n} characters': 'Receberá: {{n}} caracteres',

14
src/i18n/locales/pt-PT.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Compartilhar com Jumble',
'Share with Imwald': 'Compartilhar com Imwald',
'Share with Alexandria': 'Compartilhar com Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': 'Adicionar uma Conta',
'More options': 'Mais opções',
'Add client tag': 'Adicionar tag de cliente',
'Show others this was sent via Jumble': 'Mostrar aos outros que isso foi enviado via Jumble',
'Show others this was sent via Imwald': 'Mostrar aos outros que isso foi enviado via Imwald',
'Are you sure you want to logout?': 'Tem certeza de que deseja sair?',
'relay sets': 'conjuntos de relé',
edit: 'editar',
@ -466,7 +466,7 @@ export default { @@ -466,7 +466,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -500,9 +500,9 @@ export default { @@ -500,9 +500,9 @@ export default {
Boosts: 'Boosts',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Gostando do Jumble?',
'Your donation helps me maintain Jumble and make it better! 😊':
'Sua doação me ajuda a manter o Jumble e torná-lo melhor! 😊',
'Enjoying Imwald?': 'Gostando do Imwald?',
'Your donation helps me maintain Imwald and make it better! 😊':
'Sua doação me ajuda a manter o Imwald e torná-lo melhor! 😊',
'Earlier notifications': 'Notificações anteriores',
'Temporarily display this note': 'Exibir esta nota temporariamente',
buttonFollowing: 'Seguindo',
@ -672,7 +672,7 @@ export default { @@ -672,7 +672,7 @@ export default {
Translation: 'Tradução',
Balance: 'Saldo',
characters: 'caracteres',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'Esta chave API pode ser usada em qualquer outro lugar que suporte LibreTranslate. O URL do serviço é {{serviceUrl}}',
'Top up': 'Carregar',
'Will receive: {n} characters': 'Receberá: {{n}} caracteres',

16
src/i18n/locales/ru.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'Поделиться через Jumble',
'Share with Imwald': 'Поделиться через Imwald',
'Share with Alexandria': 'Поделиться через Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,8 +310,8 @@ export default { @@ -310,8 +310,8 @@ export default {
'Add an Account': 'Добавить аккаунт',
'More options': 'Больше опций',
'Add client tag': 'Добавить тег клиента',
'Show others this was sent via Jumble':
'Показать другим, что сообщение отправлено через Jumble',
'Show others this was sent via Imwald':
'Показать другим, что сообщение отправлено через Imwald',
'Are you sure you want to logout?': 'Вы уверены, что хотите выйти?',
'relay sets': 'наборы ретрансляторов',
edit: 'редактировать',
@ -467,7 +467,7 @@ export default { @@ -467,7 +467,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -501,9 +501,9 @@ export default { @@ -501,9 +501,9 @@ export default {
Boosts: 'Бусты',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'Нравится Jumble?',
'Your donation helps me maintain Jumble and make it better! 😊':
'Ваше пожертвование помогает поддерживать и улучшать Jumble! 😊',
'Enjoying Imwald?': 'Нравится Imwald?',
'Your donation helps me maintain Imwald and make it better! 😊':
'Ваше пожертвование помогает поддерживать и улучшать Imwald! 😊',
'Earlier notifications': 'Ранние уведомления',
'Temporarily display this note': 'Временно отобразить эту заметку',
buttonFollowing: 'Подписан',
@ -672,7 +672,7 @@ export default { @@ -672,7 +672,7 @@ export default {
Translation: 'Перевод',
Balance: 'Баланс',
characters: 'символов',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'Вы можете использовать этот API-ключ в любом другом месте, которое поддерживает LibreTranslate. URL сервиса: {{serviceUrl}}',
'Top up': 'Пополнить',
'Will receive: {n} characters': 'Получите: {{n}} символов',

14
src/i18n/locales/th.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': 'แชราน Jumble',
'Share with Imwald': 'แชราน Imwald',
'Share with Alexandria': 'แชราน Alexandria',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -310,7 +310,7 @@ export default { @@ -310,7 +310,7 @@ export default {
'Add an Account': 'เพมบญช',
'More options': 'ตวเลอกเพมเตม',
'Add client tag': 'เพมแทกไคลเอนต',
'Show others this was sent via Jumble': 'แสดงใหนเหนวาสงผาน Jumble',
'Show others this was sent via Imwald': 'แสดงใหนเหนวาสงผาน Imwald',
'Are you sure you want to logout?': 'คณแนใจหรอไมาตองการออกจากระบบ?',
'relay sets': 'ชดรเลย',
edit: 'แกไข',
@ -464,7 +464,7 @@ export default { @@ -464,7 +464,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -498,9 +498,9 @@ export default { @@ -498,9 +498,9 @@ export default {
Boosts: 'บสต',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': 'ชอบ Jumble ไหม?',
'Your donation helps me maintain Jumble and make it better! 😊':
'การบรจาคของคณชวยใหนดแลและพฒนา Jumble ใหน! 😊',
'Enjoying Imwald?': 'ชอบ Imwald ไหม?',
'Your donation helps me maintain Imwald and make it better! 😊':
'การบรจาคของคณชวยใหนดแลและพฒนา Imwald ใหน! 😊',
'Earlier notifications': 'การแจงเตอนกอนหนา',
'Temporarily display this note': 'แสดงโนตนวคราว',
buttonFollowing: 'กำลงตดตาม',
@ -668,7 +668,7 @@ export default { @@ -668,7 +668,7 @@ export default {
Translation: 'การแปล',
Balance: 'ยอดคงเหลอ',
characters: 'ตวอกษร',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'คณสามารถใช API key นบทนทรองรบ LibreTranslate ทอยบรการคอ {{serviceUrl}}',
'Top up': 'เตมเงน',
'Will receive: {n} characters': 'จะไดบ: {{n}} ตวอกษร',

14
src/i18n/locales/zh.ts

@ -169,7 +169,7 @@ export default { @@ -169,7 +169,7 @@ export default {
'Profile event tags (e.g. lud16, nip05, website). Saved with kind 0.',
'Tag value': 'Tag value',
'Saving…': 'Saving…',
'Share with Jumble': '通过Jumble分享',
'Share with Imwald': '通过Imwald分享',
'Share with Alexandria': '通过Alexandria分享',
'Start video call': 'Start video call',
'Copy call invite link': 'Copy call invite link',
@ -309,7 +309,7 @@ export default { @@ -309,7 +309,7 @@ export default {
'Add an Account': '添加账户',
'More options': '更多选项',
'Add client tag': '添加客户端标签',
'Show others this was sent via Jumble': '告诉别人这是通过 Jumble 发送的',
'Show others this was sent via Imwald': '告诉别人这是通过 Imwald 发送的',
'Are you sure you want to logout?': '确定要退出登录吗?',
'relay sets': '服务器组',
edit: '编辑',
@ -463,7 +463,7 @@ export default { @@ -463,7 +463,7 @@ export default {
'Notification boost detail': 'The preview above is the original post.',
'Notification poll vote summary': 'Voted on the poll above.',
'Notification poll vote options count': '{{count}} option(s) selected',
'Jumble Imwald synthetic event': 'Jumble Imwald synthetic event',
'Imwald synthetic event': 'Imwald synthetic event',
'+ Add a URL to this list': 'Add a URL to this list',
'Add a web URL': 'Add a web URL',
'Add web URL to feed description':
@ -497,9 +497,9 @@ export default { @@ -497,9 +497,9 @@ export default {
Boosts: '助推',
Badges: 'Badges',
Reports: 'Reports',
'Enjoying Jumble?': '喜欢 Jumble 吗?',
'Your donation helps me maintain Jumble and make it better! 😊':
'您的捐赠帮助我维护 Jumble 并使其更好!😊',
'Enjoying Imwald?': '喜欢 Imwald 吗?',
'Your donation helps me maintain Imwald and make it better! 😊':
'您的捐赠帮助我维护 Imwald 并使其更好!😊',
'Earlier notifications': '更早的通知',
'Temporarily display this note': '临时显示此笔记',
buttonFollowing: '已关注',
@ -667,7 +667,7 @@ export default { @@ -667,7 +667,7 @@ export default {
Translation: '翻译',
Balance: '余额',
characters: '字符',
jumbleTranslateApiKeyDescription:
translateApiKeyDescription:
'您可以在任何支持 LibreTranslate 的地方使用此 API key。服务地址是 {{serviceUrl}}',
'Top up': '充值',
'Will receive: {n} characters': '将获得: {{n}} 字符',

6
src/lib/client-platform.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
/** True when running inside the packaged Electron shell ({@link electron/preload.cjs}). */
export function isJumbleElectron(): boolean {
return typeof window !== 'undefined' && window.jumbleElectron?.isElectron === true
export function isImwaldElectron(): boolean {
return typeof window !== 'undefined' && window.imwaldElectron?.isElectron === true
}
/**
@ -8,7 +8,7 @@ export function isJumbleElectron(): boolean { @@ -8,7 +8,7 @@ export function isJumbleElectron(): boolean {
* Used for smaller in-memory LRU and tighter disk archive defaults (not a substitute for real UA tests).
*/
export function isMobileBrowserProfile(): boolean {
if (typeof window === 'undefined' || isJumbleElectron()) return false
if (typeof window === 'undefined' || isImwaldElectron()) return false
const narrow = window.matchMedia?.('(max-width: 768px)')?.matches ?? false
const coarse = window.matchMedia?.('(pointer: coarse)')?.matches ?? false
return narrow || (coarse && (window.innerWidth ?? 1024) <= 900)

15
src/lib/content-spacing-debug.ts

@ -1,15 +1,20 @@ @@ -1,15 +1,20 @@
/**
* Verbose content/spacing traces for debugging (e.g. "Name: nostr:npub…" collapsing).
*
* Enable in dev: localStorage.setItem('jumble-debug-content', 'true') then reload.
* Disable: localStorage.removeItem('jumble-debug-content')
* Enable in dev: localStorage.setItem('imwald-debug-content', 'true') then reload.
* Disable: localStorage.removeItem('imwald-debug-content')
* Legacy key `jumble-debug-content` is still honored.
*/
const STORAGE_KEY = 'jumble-debug-content'
const STORAGE_KEY = 'imwald-debug-content'
const LEGACY_STORAGE_KEY = 'jumble-debug-content'
export function isContentSpacingDebug(): boolean {
try {
return import.meta.env.DEV && typeof localStorage !== 'undefined' && localStorage.getItem(STORAGE_KEY) === 'true'
if (!import.meta.env.DEV || typeof localStorage === 'undefined') return false
return (
localStorage.getItem(STORAGE_KEY) === 'true' || localStorage.getItem(LEGACY_STORAGE_KEY) === 'true'
)
} catch {
return false
}
@ -24,5 +29,5 @@ export function reprString(s: string, maxLen = 500): string { @@ -24,5 +29,5 @@ export function reprString(s: string, maxLen = 500): string {
export function logContentSpacing(phase: string, detail: Record<string, unknown>): void {
if (!isContentSpacingDebug()) return
// eslint-disable-next-line no-console
console.log(`[jumble content-spacing] ${phase}`, detail)
console.log(`[imwald content-spacing] ${phase}`, detail)
}

13
src/lib/debug-utils.ts

@ -2,9 +2,9 @@ @@ -2,9 +2,9 @@
* Debug utilities for development and troubleshooting
*
* Usage in browser console:
* - jumbleDebug.enable() - Enable debug logging
* - jumbleDebug.disable() - Disable debug logging
* - jumbleDebug.status() - Check current debug status
* - imwaldDebug.enable() - Enable debug logging
* - imwaldDebug.disable() - Disable debug logging
* - imwaldDebug.status() - Check current debug status
*/
import logger from './logger'
@ -22,17 +22,17 @@ interface DebugUtils { @@ -22,17 +22,17 @@ interface DebugUtils {
const debugUtils: DebugUtils = {
enable: () => {
logger.setDebugMode(true)
logger.info('🔧 Jumble debug logging enabled')
logger.info('🔧 Imwald debug logging enabled')
},
disable: () => {
logger.setDebugMode(false)
logger.info('🔧 Jumble debug logging disabled')
logger.info('🔧 Imwald debug logging disabled')
},
status: () => {
const enabled = logger.isDebugEnabled()
logger.info(`🔧 Jumble debug status: ${enabled ? 'ENABLED' : 'DISABLED'}`)
logger.info(`🔧 Imwald debug status: ${enabled ? 'ENABLED' : 'DISABLED'}`)
return { enabled, level: enabled ? 'debug' : 'info' }
},
@ -55,6 +55,7 @@ const debugUtils: DebugUtils = { @@ -55,6 +55,7 @@ const debugUtils: DebugUtils = {
// Expose debug utilities globally in development
if (import.meta.env.DEV) {
;(window as any).imwaldDebug = debugUtils
;(window as any).jumbleDebug = debugUtils
}

4
src/lib/draft-event.ts

@ -1394,7 +1394,7 @@ export function buildClientTag(handlerPubkey?: string, handlerIdentifier?: strin @@ -1394,7 +1394,7 @@ export function buildClientTag(handlerPubkey?: string, handlerIdentifier?: strin
// Use NIP-89 format if handler information is provided
if (handlerPubkey && handlerIdentifier) {
const aTag = `31990:${handlerPubkey}:${handlerIdentifier}`
const tag = ['client', 'Jumble ImWald', aTag]
const tag = ['client', 'Imwald', aTag]
if (relay) {
tag.push(relay)
}
@ -1402,7 +1402,7 @@ export function buildClientTag(handlerPubkey?: string, handlerIdentifier?: strin @@ -1402,7 +1402,7 @@ export function buildClientTag(handlerPubkey?: string, handlerIdentifier?: strin
}
// Fallback to simple format for backward compatibility
return ['client', 'jumble']
return ['client', 'imwald']
}
export function buildAltTag() {

8
src/lib/event-archive-config.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { StorageKey } from '@/constants'
import { isJumbleElectron, isMobileBrowserProfile } from '@/lib/client-platform'
import { isImwaldElectron, isMobileBrowserProfile } from '@/lib/client-platform'
/** Platform defaults (overridable in Cache settings). */
export const EVENT_ARCHIVE_DEFAULTS = {
@ -44,19 +44,19 @@ function readPositiveInt(key: string, fallback: number): number { @@ -44,19 +44,19 @@ function readPositiveInt(key: string, fallback: number): number {
}
function defaultSessionLruMax(): number {
if (isJumbleElectron()) return EVENT_ARCHIVE_DEFAULTS.sessionLruElectron
if (isImwaldElectron()) return EVENT_ARCHIVE_DEFAULTS.sessionLruElectron
if (isMobileBrowserProfile()) return EVENT_ARCHIVE_DEFAULTS.sessionLruMobile
return EVENT_ARCHIVE_DEFAULTS.sessionLruDesktopBrowser
}
function defaultMaxMb(): number {
if (isJumbleElectron()) return EVENT_ARCHIVE_DEFAULTS.maxMbElectron
if (isImwaldElectron()) return EVENT_ARCHIVE_DEFAULTS.maxMbElectron
if (isMobileBrowserProfile()) return EVENT_ARCHIVE_DEFAULTS.maxMbMobile
return EVENT_ARCHIVE_DEFAULTS.maxMbDesktopBrowser
}
function defaultMaxEvents(): number {
if (isJumbleElectron()) return EVENT_ARCHIVE_DEFAULTS.maxEventsElectron
if (isImwaldElectron()) return EVENT_ARCHIVE_DEFAULTS.maxEventsElectron
if (isMobileBrowserProfile()) return EVENT_ARCHIVE_DEFAULTS.maxEventsMobile
return EVENT_ARCHIVE_DEFAULTS.maxEventsDesktopBrowser
}

2
src/lib/event.ts

@ -50,7 +50,7 @@ function getParentETagCommentOrDiscussion(event: Event): string[] | undefined { @@ -50,7 +50,7 @@ function getParentETagCommentOrDiscussion(event: Event): string[] | undefined {
}
/**
* Root `e` for kind 1111 / voice comment: prefer `root` marker, else uppercase `E` (Jumble / NIP-22),
* Root `e` for kind 1111 / voice comment: prefer `root` marker, else uppercase `E` (Imwald / NIP-22),
* else first `e` when multiple (NIP-10 root-before-reply), else single `e`.
*/
function getRootETagCommentOrDiscussion(event: Event): string[] | undefined {

8
src/lib/logger.ts

@ -24,7 +24,11 @@ class Logger { @@ -24,7 +24,11 @@ class Logger {
constructor() {
// In production, disable debug logging for better performance
const isDev = import.meta.env.DEV
const isDebugEnabled = isDev && (localStorage.getItem('jumble-debug') === 'true' || import.meta.env.VITE_DEBUG === 'true')
const isDebugEnabled =
isDev &&
(localStorage.getItem('imwald-debug') === 'true' ||
localStorage.getItem('jumble-debug') === 'true' ||
import.meta.env.VITE_DEBUG === 'true')
this.config = {
level: isDebugEnabled ? 'debug' : 'info',
@ -117,6 +121,7 @@ class Logger { @@ -117,6 +121,7 @@ class Logger {
setDebugMode(enabled: boolean): void {
this.config.enableDebug = enabled
this.config.level = enabled ? 'debug' : 'info'
localStorage.setItem('imwald-debug', enabled.toString())
localStorage.setItem('jumble-debug', enabled.toString())
}
@ -147,6 +152,7 @@ const logger = new Logger() @@ -147,6 +152,7 @@ const logger = new Logger()
// Expose debug toggle for development
if (import.meta.env.DEV) {
;(window as any).imwaldLogger = logger
;(window as any).jumbleLogger = logger
}

23
src/lib/nip89-utils.ts

@ -2,23 +2,12 @@ import { Event } from 'nostr-tools' @@ -2,23 +2,12 @@ import { Event } from 'nostr-tools'
import nip89Service from '@/services/nip89.service'
/**
* Create the Jumble ImWald application handler info event (kind 31990)
* This can be published using the existing publish function from NostrProvider
* Create the Imwald application handler info event (kind 31990).
* This can be published using the existing publish function from NostrProvider.
*/
export function createJumbleImWaldHandlerInfoEvent(pubkey: string): Omit<Event, 'id' | 'sig'> {
return nip89Service.createJumbleImWaldHandlerInfo(pubkey)
export function createImwaldHandlerInfoEvent(pubkey: string): Omit<Event, 'id' | 'sig'> {
return nip89Service.createImwaldHandlerInfo(pubkey)
}
/**
* Example usage in a component:
*
* const { pubkey, signEvent, publish } = useNostr()
*
* const handlePublishHandlerInfo = async () => {
* if (!pubkey) return
*
* const handlerInfoEvent = createJumbleImWaldHandlerInfoEvent(pubkey)
* const signedEvent = await signEvent(handlerInfoEvent)
* await publish(signedEvent)
* }
*/
/** @deprecated Use {@link createImwaldHandlerInfoEvent} */
export const createJumbleImWaldHandlerInfoEvent = createImwaldHandlerInfoEvent

6
src/lib/nostr-from-http-url.ts

@ -49,7 +49,7 @@ function extractHex64(s: string): string | null { @@ -49,7 +49,7 @@ function extractHex64(s: string): string | null {
* True if this hostname serves this web app: current tab origin and/or known production/dev hosts.
* Needed so `https://jumble.imwald.eu/.../notes/nevent…` embeds while the dev server runs on localhost.
*/
export function urlHostnameIsKnownJumbleAppHost(
export function urlHostnameIsKnownImwaldWebHost(
urlHostname: string,
appOrigin: string | null
): boolean {
@ -67,7 +67,7 @@ export function urlHostnameIsKnownJumbleAppHost( @@ -67,7 +67,7 @@ export function urlHostnameIsKnownJumbleAppHost(
}
/**
* In-app HTTP(S) links to our routes embed like `nostr:…` (same tab origin or known jumble/localhost host).
* In-app HTTP(S) links to our routes embed like `nostr:…` (same tab origin or known Imwald/localhost host).
*/
export function parseSameOriginAppNostrUrl(urlStr: string, appOrigin: string | null): NostrUrlExtract | null {
let u: URL
@ -76,7 +76,7 @@ export function parseSameOriginAppNostrUrl(urlStr: string, appOrigin: string | n @@ -76,7 +76,7 @@ export function parseSameOriginAppNostrUrl(urlStr: string, appOrigin: string | n
} catch {
return null
}
if (!urlHostnameIsKnownJumbleAppHost(u.hostname, appOrigin)) return null
if (!urlHostnameIsKnownImwaldWebHost(u.hostname, appOrigin)) return null
let path = u.pathname
if (path.length > 1 && path.endsWith('/')) path = path.slice(0, -1)

6
src/lib/piper-tts-cache-policy.ts

@ -1,15 +1,15 @@ @@ -1,15 +1,15 @@
import { isJumbleElectron, isMobileBrowserProfile } from '@/lib/client-platform'
import { isImwaldElectron, isMobileBrowserProfile } from '@/lib/client-platform'
/** How long we keep Piper WAV blobs (per device class). */
export function getPiperTtsCacheTtlMs(): number {
if (isJumbleElectron()) return 7 * 24 * 60 * 60 * 1000
if (isImwaldElectron()) return 7 * 24 * 60 * 60 * 1000
if (isMobileBrowserProfile()) return 24 * 60 * 60 * 1000
return 48 * 60 * 60 * 1000
}
/** Caps so TTS audio cannot grow without bound (evicts oldest after TTL pass). */
export function getPiperTtsCacheBudget(): { maxEntries: number; maxBytes: number } {
if (isJumbleElectron()) return { maxEntries: 400, maxBytes: 400 * 1024 * 1024 }
if (isImwaldElectron()) return { maxEntries: 400, maxBytes: 400 * 1024 * 1024 }
if (isMobileBrowserProfile()) return { maxEntries: 80, maxBytes: 45 * 1024 * 1024 }
return { maxEntries: 200, maxBytes: 180 * 1024 * 1024 }
}

2
src/lib/read-aloud.ts

@ -380,7 +380,7 @@ function playPiperBlob(blob: Blob, signal: AbortSignal): Promise<'ok' | 'error' @@ -380,7 +380,7 @@ function playPiperBlob(blob: Blob, signal: AbortSignal): Promise<'ok' | 'error'
audio.src = audioUrl
audio.preload = 'auto'
try {
audio.setAttribute('data-jumble-read-aloud', '')
audio.setAttribute('data-imwald-read-aloud', '')
audio.style.display = 'none'
document.body.appendChild(audio)
} catch {

2
src/lib/vite-proxy-url.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/**
* Builds the browser fetch URL for Jumble's server-side fetch proxy (`VITE_PROXY_SERVER`).
* Builds the browser fetch URL for Imwald's server-side fetch proxy (`VITE_PROXY_SERVER`).
* Shared by OG/HTML fetches and RSS so both hit the same proxy contract.
*/
export function buildViteProxySitesFetchUrl(originalUrl: string, proxyServer: string): string {

4
src/main.tsx

@ -34,7 +34,7 @@ const SESSION_STORAGE_KEY = 'jumble:session' @@ -34,7 +34,7 @@ const SESSION_STORAGE_KEY = 'jumble:session'
async function bootstrap() {
// Always defined: fetch does not throw on 4xx/5xx, so non-OK responses must not leave this unset.
window.__RUNTIME_CONFIG__ = {}
console.info('[jumble] Boot: opening storage and loading config…')
console.info('[imwald] Boot: opening storage and loading config…')
await Promise.all([
initI18n(),
storage.initAsync(),
@ -52,7 +52,7 @@ async function bootstrap() { @@ -52,7 +52,7 @@ async function bootstrap() {
}
})()
])
console.info('[jumble] Boot: mounting React (UI shell will appear; Nostr session restores next)')
console.info('[imwald] Boot: mounting React (UI shell will appear; Nostr session restores next)')
restoreSessionFeedSnapshotsAfterHardRefresh()
// Mark session storage as used so it's visible in DevTools; VersionUpdateBanner and NotePage also use it.
try {

32
src/pages/secondary/NotePage/index.tsx

@ -259,18 +259,18 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }: @@ -259,18 +259,18 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }:
// Reset to default meta tags with richer information
const defaultUrl = window.location.href
const truncatedDefaultUrl = defaultUrl.length > 150 ? defaultUrl.substring(0, 147) + '...' : defaultUrl
updateMetaTag('og:title', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:title', 'Imwald 🌲')
updateMetaTag('og:description', `${truncatedDefaultUrl} - A user-friendly Nostr client focused on relay feed browsing and relay discovery. The Imwald edition focuses on publications and articles.`)
updateMetaTag('og:image', 'https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true')
updateMetaTag('og:image', 'https://jumble.imwald.eu/pwa-512x512.png')
updateMetaTag('og:type', 'website')
updateMetaTag('og:url', window.location.href)
updateMetaTag('og:site_name', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:site_name', 'Imwald 🌲')
// Twitter card meta tags
updateMetaTag('twitter:card', 'summary_large_image')
updateMetaTag('twitter:title', 'Jumble - Imwald Edition 🌲')
updateMetaTag('twitter:title', 'Imwald 🌲')
updateMetaTag('twitter:description', `${truncatedDefaultUrl} - A user-friendly Nostr client focused on relay feed browsing and relay discovery. The Imwald edition focuses on publications and articles.`)
updateMetaTag('twitter:image', 'https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true')
updateMetaTag('twitter:image', 'https://jumble.imwald.eu/pwa-512x512.png')
// Remove article:tag if it exists
const articleTagMeta = document.querySelector('meta[property="article:tag"]')
@ -359,7 +359,7 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }: @@ -359,7 +359,7 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }:
}
if (!image) {
// Use default OG image with green forest theme
image = 'https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true'
image = 'https://jumble.imwald.eu/pwa-512x512.png'
}
const tags = eventMetadata?.tags || []
@ -370,18 +370,18 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }: @@ -370,18 +370,18 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }:
// Enhanced title with profile info
const ogTitle = authorName
? `${eventTitle} by @${authorName} - Jumble Imwald Edition 🌲`
: `${eventTitle} - Jumble Imwald Edition 🌲`
? `${eventTitle} by @${authorName} - Imwald 🌲`
: `${eventTitle} - Imwald 🌲`
updateMetaTag('og:title', ogTitle)
updateMetaTag('og:description', ogDescription)
updateMetaTag('og:image', image)
updateMetaTag('og:image:width', '1200')
updateMetaTag('og:image:height', '630')
updateMetaTag('og:image:alt', `${eventTitle}${authorName ? ` by @${authorName}` : ''} on Jumble Imwald`)
updateMetaTag('og:image:alt', `${eventTitle}${authorName ? ` by @${authorName}` : ''} on Imwald`)
updateMetaTag('og:type', ogType)
updateMetaTag('og:url', window.location.href)
updateMetaTag('og:site_name', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:site_name', 'Imwald 🌲')
// Add profile data - always include if available
if (authorProfile) {
@ -408,7 +408,7 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }: @@ -408,7 +408,7 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }:
updateMetaTag('twitter:title', ogTitle)
updateMetaTag('twitter:description', ogDescription.length > 200 ? ogDescription.substring(0, 197) + '...' : ogDescription)
updateMetaTag('twitter:image', image)
updateMetaTag('twitter:image:alt', `${eventTitle}${authorName ? ` by @${authorName}` : ''} on Jumble Imwald`)
updateMetaTag('twitter:image:alt', `${eventTitle}${authorName ? ` by @${authorName}` : ''} on Imwald`)
// Remove old article:tag if it exists
const oldArticleTagMeta = document.querySelector('meta[property="article:tag"]')
@ -427,19 +427,19 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }: @@ -427,19 +427,19 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }:
}
// Update document title
document.title = `${eventTitle} - Jumble Imwald Edition`
document.title = `${eventTitle} - Imwald`
// Cleanup function
return () => {
// Reset to default on unmount with richer information
const cleanupUrl = window.location.href
const truncatedCleanupUrl = cleanupUrl.length > 150 ? cleanupUrl.substring(0, 147) + '...' : cleanupUrl
updateMetaTag('og:title', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:title', 'Imwald 🌲')
updateMetaTag('og:description', `${truncatedCleanupUrl} - A user-friendly Nostr client focused on relay feed browsing and relay discovery. The Imwald edition focuses on publications and articles.`)
updateMetaTag('og:image', 'https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true')
updateMetaTag('og:image', 'https://jumble.imwald.eu/pwa-512x512.png')
updateMetaTag('og:type', 'website')
updateMetaTag('og:url', window.location.href)
updateMetaTag('og:site_name', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:site_name', 'Imwald 🌲')
// Remove article:tag meta tags
document.querySelectorAll('meta[property="article:tag"]').forEach(meta => meta.remove())
@ -448,7 +448,7 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }: @@ -448,7 +448,7 @@ const NotePage = forwardRef(({ id, index, hideTitlebar = false, initialEvent }:
authorMeta.remove()
}
document.title = 'Jumble - Imwald Edition 🌲'
document.title = 'Imwald 🌲'
}
}, [finalEvent, articleMetadata, authorProfile])

30
src/pages/secondary/ProfilePage/index.tsx

@ -48,25 +48,25 @@ const ProfilePage = forwardRef(({ id, index, hideTitlebar = false }: { id?: stri @@ -48,25 +48,25 @@ const ProfilePage = forwardRef(({ id, index, hideTitlebar = false }: { id?: stri
// Reset to default meta tags
const defaultUrl = window.location.href
const truncatedDefaultUrl = defaultUrl.length > 150 ? defaultUrl.substring(0, 147) + '...' : defaultUrl
updateMetaTag('og:title', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:title', 'Imwald 🌲')
updateMetaTag('og:description', `${truncatedDefaultUrl} - A user-friendly Nostr client focused on relay feed browsing and relay discovery. The Imwald edition focuses on publications and articles.`)
updateMetaTag('og:image', 'https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true')
updateMetaTag('og:image', 'https://jumble.imwald.eu/pwa-512x512.png')
updateMetaTag('og:type', 'profile')
updateMetaTag('og:url', window.location.href)
updateMetaTag('og:site_name', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:site_name', 'Imwald 🌲')
// Twitter card meta tags
updateMetaTag('twitter:card', 'summary')
updateMetaTag('twitter:title', 'Jumble - Imwald Edition 🌲')
updateMetaTag('twitter:title', 'Imwald 🌲')
updateMetaTag('twitter:description', `${truncatedDefaultUrl} - Profile`)
updateMetaTag('twitter:image', 'https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true')
updateMetaTag('twitter:image', 'https://jumble.imwald.eu/pwa-512x512.png')
return
}
// Build description matching fallback card: username, hostname, URL
const username = profile.username || ''
const ogTitle = username ? `@${username} - Jumble Imwald Edition 🌲` : 'Profile - Jumble Imwald Edition 🌲'
const ogTitle = username ? `@${username} - Imwald 🌲` : 'Profile - Imwald 🌲'
// Truncate URL to 150 chars
const fullUrl = window.location.href
@ -86,17 +86,17 @@ const ProfilePage = forwardRef(({ id, index, hideTitlebar = false }: { id?: stri @@ -86,17 +86,17 @@ const ProfilePage = forwardRef(({ id, index, hideTitlebar = false }: { id?: stri
// Use profile avatar or default image with green theme
const image = profile.avatar
? `https://jumble.imwald.eu/api/avatar/${profile.pubkey}`
: 'https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true'
: 'https://jumble.imwald.eu/pwa-512x512.png'
updateMetaTag('og:title', ogTitle)
updateMetaTag('og:description', ogDescription)
updateMetaTag('og:image', image)
updateMetaTag('og:image:width', '1200')
updateMetaTag('og:image:height', '630')
updateMetaTag('og:image:alt', `${username ? `@${username}` : 'Profile'} on Jumble Imwald`)
updateMetaTag('og:image:alt', `${username ? `@${username}` : 'Profile'} on Imwald`)
updateMetaTag('og:type', 'profile')
updateMetaTag('og:url', window.location.href)
updateMetaTag('og:site_name', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:site_name', 'Imwald 🌲')
// Add profile-specific meta tags
if (profile.username) {
@ -111,23 +111,23 @@ const ProfilePage = forwardRef(({ id, index, hideTitlebar = false }: { id?: stri @@ -111,23 +111,23 @@ const ProfilePage = forwardRef(({ id, index, hideTitlebar = false }: { id?: stri
updateMetaTag('twitter:title', ogTitle)
updateMetaTag('twitter:description', ogDescription.length > 200 ? ogDescription.substring(0, 197) + '...' : ogDescription)
updateMetaTag('twitter:image', image)
updateMetaTag('twitter:image:alt', `${username ? `@${username}` : 'Profile'} on Jumble Imwald`)
updateMetaTag('twitter:image:alt', `${username ? `@${username}` : 'Profile'} on Imwald`)
// Update document title
document.title = `${ogTitle} - Jumble Imwald Edition`
document.title = `${ogTitle} - Imwald`
// Cleanup function
return () => {
// Reset to default on unmount
const cleanupUrl = window.location.href
const truncatedCleanupUrl = cleanupUrl.length > 150 ? cleanupUrl.substring(0, 147) + '...' : cleanupUrl
updateMetaTag('og:title', 'Jumble - Imwald Edition 🌲')
updateMetaTag('og:title', 'Imwald 🌲')
updateMetaTag('og:description', `${truncatedCleanupUrl} - A user-friendly Nostr client focused on relay feed browsing and relay discovery. The Imwald edition focuses on publications and articles.`)
updateMetaTag('og:image', 'https://github.com/CodyTseng/jumble/blob/master/resources/og-image.png?raw=true')
updateMetaTag('og:image', 'https://jumble.imwald.eu/pwa-512x512.png')
updateMetaTag('og:type', 'website')
updateMetaTag('og:url', window.location.href)
updateMetaTag('og:site_name', 'Jumble - Imwald Edition 🌲')
document.title = 'Jumble - Imwald Edition 🌲'
updateMetaTag('og:site_name', 'Imwald 🌲')
document.title = 'Imwald 🌲'
}
}, [profile])

4
src/pages/secondary/RssFeedSettingsPage/index.tsx

@ -261,8 +261,8 @@ const RssFeedSettingsPage = forwardRef(({ index, hideTitlebar = false }: { index @@ -261,8 +261,8 @@ const RssFeedSettingsPage = forwardRef(({ index, hideTitlebar = false }: { index
}
try {
const opmlContent = generateOpml(normalizedUrls, 'Jumble RSS Feeds')
const filename = `jumble-rss-feeds-${new Date().toISOString().split('T')[0]}.opml`
const opmlContent = generateOpml(normalizedUrls, 'Imwald RSS Feeds')
const filename = `imwald-rss-feeds-${new Date().toISOString().split('T')[0]}.opml`
downloadFile(opmlContent, filename, 'application/xml')
toast.success(t('RSS feeds exported to OPML file'))
} catch (error) {

4
src/providers/NostrProvider/index.tsx

@ -1171,13 +1171,13 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { @@ -1171,13 +1171,13 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
const normalizeDraftEventTags = (draftEvent: TDraftEvent): TDraftEvent => {
const draft = JSON.parse(JSON.stringify(draftEvent)) as TDraftEvent
const jumbleAttributionAlt = buildAltTag()[1]
const imwaldAttributionAlt = buildAltTag()[1]
const existingTags = Array.isArray(draft.tags) ? draft.tags : []
const sanitizedTags = existingTags.filter(
(tag) =>
Array.isArray(tag) &&
tag[0] !== 'client' &&
!(tag[0] === 'alt' && tag[1] === jumbleAttributionAlt)
!(tag[0] === 'alt' && tag[1] === imwaldAttributionAlt)
)
draft.tags = [...sanitizedTags, buildClientTag(), buildAltTag()]
return draft

6
src/services/lightning.service.ts

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { FAST_READ_RELAY_URLS, CODY_PUBKEY, JUMBLE_PUBKEY } from '@/constants'
import { FAST_READ_RELAY_URLS, CODY_PUBKEY, IMWALD_MAINTAINER_PUBKEY } from '@/constants'
import { getZapInfoFromEvent } from '@/lib/event-metadata'
import {
buildZapPollVoteRequestTemplate,
@ -23,7 +23,7 @@ import logger from '@/lib/logger' @@ -23,7 +23,7 @@ import logger from '@/lib/logger'
export type TRecentSupporter = { pubkey: string; amount: number; comment?: string }
const OFFICIAL_PUBKEYS = [JUMBLE_PUBKEY, CODY_PUBKEY]
const OFFICIAL_PUBKEYS = [IMWALD_MAINTAINER_PUBKEY, CODY_PUBKEY]
class LightningService {
static instance: LightningService
@ -34,7 +34,7 @@ class LightningService { @@ -34,7 +34,7 @@ class LightningService {
if (!LightningService.instance) {
LightningService.instance = this
init({
appName: 'Jumble',
appName: 'Imwald',
showBalance: false
})
}

30
src/services/local-storage.service.ts

@ -138,6 +138,13 @@ class LocalStorageService { @@ -138,6 +138,13 @@ class LocalStorageService {
this.accounts = accountsStr ? JSON.parse(accountsStr) : []
const currentAccountStr = window.localStorage.getItem(StorageKey.CURRENT_ACCOUNT)
this.currentAccount = currentAccountStr ? JSON.parse(currentAccountStr) : null
if (
this.currentAccount != null &&
!this.accounts.some((a) => isSameAccount(a, this.currentAccount))
) {
this.currentAccount = null
window.localStorage.setItem(StorageKey.CURRENT_ACCOUNT, JSON.stringify(null))
}
const noteListModeStr = window.localStorage.getItem(StorageKey.NOTE_LIST_MODE)
this.noteListMode =
noteListModeStr && ['posts', 'postsAndReplies', 'pictures'].includes(noteListModeStr)
@ -487,6 +494,13 @@ class LocalStorageService { @@ -487,6 +494,13 @@ class LocalStorageService {
if (accountsStr != null) this.accounts = JSON.parse(accountsStr) as TAccount[]
const currentAccountStr = get(StorageKey.CURRENT_ACCOUNT)
if (currentAccountStr != null) this.currentAccount = JSON.parse(currentAccountStr) as TAccount | null
if (
this.currentAccount != null &&
!this.accounts.some((a) => isSameAccount(a, this.currentAccount))
) {
this.currentAccount = null
this.persistSetting(StorageKey.CURRENT_ACCOUNT, JSON.stringify(null))
}
const relaySetsStr = get(StorageKey.RELAY_SETS)
if (relaySetsStr != null) this.relaySets = JSON.parse(relaySetsStr) as TRelaySet[]
const defaultZapSatsStr = get(StorageKey.DEFAULT_ZAP_SATS)
@ -654,10 +668,26 @@ class LocalStorageService { @@ -654,10 +668,26 @@ class LocalStorageService {
removeAccount(account: TAccount) {
this.accounts = this.accounts.filter((act) => !isSameAccount(act, account))
this.persistSetting(StorageKey.ACCOUNTS, JSON.stringify(this.accounts))
if (isSameAccount(this.currentAccount, account)) {
this.currentAccount = null
this.persistSetting(StorageKey.CURRENT_ACCOUNT, JSON.stringify(null))
} else if (
this.currentAccount != null &&
!this.accounts.some((a) => isSameAccount(a, this.currentAccount))
) {
this.currentAccount = null
this.persistSetting(StorageKey.CURRENT_ACCOUNT, JSON.stringify(null))
}
return this.accounts
}
switchAccount(account: TAccount | null) {
if (account === null) {
if (this.currentAccount === null) return
this.currentAccount = null
this.persistSetting(StorageKey.CURRENT_ACCOUNT, JSON.stringify(null))
return
}
if (isSameAccount(this.currentAccount, account)) {
return
}

20
src/services/nip89.service.ts

@ -200,14 +200,14 @@ class Nip89Service { @@ -200,14 +200,14 @@ class Nip89Service {
}
/**
* Create the Jumble ImWald application handler info event
* Create the Imwald application handler info event (NIP-89).
*/
createJumbleImWaldHandlerInfo(pubkey: string): Omit<Event, 'id' | 'sig'> {
createImwaldHandlerInfo(pubkey: string): Omit<Event, 'id' | 'sig'> {
const handlerInfo: ApplicationHandlerInfo = {
name: 'Jumble ImWald',
name: 'Imwald',
description: 'A modern Nostr client with advanced features for content discovery, discussions, and community building.',
website: 'https://jumble.gitcitadel.eu',
picture: 'https://jumble.gitcitadel.eu/logo.png',
website: 'https://jumble.imwald.eu',
picture: 'https://jumble.imwald.eu/pwa-512x512.png',
supportedKinds: [
kinds.ShortTextNote,
kinds.Repost,
@ -229,10 +229,10 @@ class Nip89Service { @@ -229,10 +229,10 @@ class Nip89Service {
ExtendedKind.WIKI_ARTICLE,
],
platforms: {
web: 'https://jumble.gitcitadel.eu/note/bech32',
ios: 'jumble://note/bech32',
android: 'jumble://note/bech32',
desktop: 'jumble://note/bech32'
web: 'https://jumble.imwald.eu/note/bech32',
ios: 'imwald://note/bech32',
android: 'imwald://note/bech32',
desktop: 'imwald://note/bech32'
},
relays: [
'wss://relay.damus.io',
@ -242,7 +242,7 @@ class Nip89Service { @@ -242,7 +242,7 @@ class Nip89Service {
]
}
return this.createApplicationHandlerInfoEvent(pubkey, handlerInfo, 'jumble-imwald')
return this.createApplicationHandlerInfoEvent(pubkey, handlerInfo, 'imwald')
}
}

6
src/services/session-feed-snapshot.service.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import type { Event } from 'nostr-tools'
import logger from '@/lib/logger'
import { isJumbleElectron } from '@/lib/client-platform'
import { isImwaldElectron } from '@/lib/client-platform'
/** Max events stored per feed key (matches typical initial timeline cap). */
const MAX_EVENTS_PER_FEED = 120
@ -47,8 +47,8 @@ export function setSessionFeedSnapshot(key: string, events: readonly Event[]): v @@ -47,8 +47,8 @@ export function setSessionFeedSnapshot(key: string, events: readonly Event[]): v
*/
export function hardReloadPreservingFeedSnapshots(): void {
persistSessionFeedSnapshotsForHardRefresh()
if (isJumbleElectron() && typeof window.jumbleElectron?.reloadApp === 'function') {
void window.jumbleElectron.reloadApp()
if (isImwaldElectron() && typeof window.imwaldElectron?.reloadApp === 'function') {
void window.imwaldElectron.reloadApp()
return
}
window.location.reload()

17
src/services/web.service.ts

@ -17,7 +17,7 @@ function htmlLooksLikeLocalDevAppShell(html: string): boolean { @@ -17,7 +17,7 @@ function htmlLooksLikeLocalDevAppShell(html: string): boolean {
const HTML_FETCH_HEADERS = {
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'User-Agent': 'Mozilla/5.0 (compatible; Jumble/1.0; +https://jumble.imwald.eu)'
'User-Agent': 'Mozilla/5.0 (compatible; Imwald/1.0; +https://jumble.imwald.eu)'
}
async function tryFetchHtml(fetchUrl: string, timeoutMs: number): Promise<string | null> {
@ -123,15 +123,16 @@ function parseOpenGraphFromHtml(html: string, pageUrl: string): TWebMetadata { @@ -123,15 +123,16 @@ function parseOpenGraphFromHtml(html: string, pageUrl: string): TWebMetadata {
try {
const urlObj = new URL(pageUrl)
const isJumbleDomain =
urlObj.hostname === 'jumble.imwald.eu' || urlObj.hostname.includes('jumble')
const isJumbleDefaultTitle =
title?.includes('Jumble - Imwald Edition') || title?.includes('Jumble Imwald Edition')
const isJumbleDefaultDesc = description?.includes(
const isAppCanonicalHost = urlObj.hostname === 'jumble.imwald.eu'
const isAppDefaultTitle =
title?.includes('Imwald 🌲') ||
title?.includes('Jumble - Imwald Edition') ||
title?.includes('Jumble Imwald Edition')
const isAppDefaultDesc = description?.includes(
'A user-friendly Nostr client focused on relay feed browsing'
)
if (!isJumbleDomain && (isJumbleDefaultTitle || isJumbleDefaultDesc)) {
logger.debug('[WebService] Filtered out Jumble default OG tags for external domain', {
if (!isAppCanonicalHost && (isAppDefaultTitle || isAppDefaultDesc)) {
logger.debug('[WebService] Filtered out Imwald default OG tags for external domain', {
url: pageUrl,
hostname: urlObj.hostname
})

2
src/vite-env.d.ts vendored

@ -10,7 +10,7 @@ declare global { @@ -10,7 +10,7 @@ declare global {
interface Window {
nostr?: TNip07
/** Set by {@link electron/preload.cjs} when running inside Electron. */
jumbleElectron?: {
imwaldElectron?: {
isElectron: true
/** Ask Electron main to reload index safely (avoids file:// history path reload issues). */
reloadApp?: () => Promise<boolean>

Loading…
Cancel
Save