Compare commits

178 Commits

Author SHA1 Message Date
GitHub Actions
afc4ba1087 chore: bump version to 4.11.191 [skip ci] 2026-08-24 14:03:10 +00:00
f022155aa1 Update responsive image attachments
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 16s
Build / build-windows (push) Successful in 7m8s
Build / build-linux (push) Successful in 3m10s
Build / finalize-release (push) Successful in 6s
Advance Cinny to the responsive message image fix.
2026-08-25 00:02:49 +10:00
GitHub Actions
f4ec39f229 chore: bump version to 4.11.190 [skip ci] 2026-08-24 13:58:49 +00:00
848283da42 Add TikTok player embed
Some checks failed
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-linux (push) Has been cancelled
Build / finalize-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
Use TikTok's supported iframe player and safely invoke yt-dlp for YouTube streams.
2026-08-24 23:58:35 +10:00
GitHub Actions
2cd9409e3d chore: bump version to 4.11.189 [skip ci] 2026-08-24 10:07:31 +00:00
b85774e911 Bump cinny for Capacitor HTTP.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 6m37s
Build / build-linux (push) Successful in 3m10s
Build / finalize-release (push) Successful in 4s
Include the native HTTP configuration required for mobile collectible catalog downloads.
2026-08-24 20:07:17 +10:00
GitHub Actions
73d31a2cb4 chore: bump version to 4.11.188 [skip ci] 2026-08-24 09:53:09 +00:00
f9bc1c7c2f Bump cinny for mobile collectibles support.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 6m44s
Build / build-linux (push) Successful in 3m6s
Build / finalize-release (push) Successful in 4s
Use the mobile catalog and CDN fallback for profile cosmetics outside Electron.
2026-08-24 19:52:54 +10:00
GitHub Actions
85995d0744 chore: bump version to 4.11.187 [skip ci] 2026-08-24 09:30:06 +00:00
9d7ae1d63b Use published Discord collectibles catalog.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 6m54s
Build / build-linux (push) Successful in 3m5s
Build / finalize-release (push) Successful in 4s
Fetch and persist the public release catalog locally while retiring client-side Discord token handling.
2026-08-24 19:29:53 +10:00
GitHub Actions
ce7b2104ad chore: bump version to 4.11.186 [skip ci] 2026-08-24 08:49:07 +00:00
60d65a29ff Bump cinny for configurable sidebar nameplate background.
Some checks failed
Build / increment-version (push) Successful in 12s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 6m49s
Build / build-linux (push) Failing after 3m18s
Build / finalize-release (push) Successful in 4s
Include immediate collectible rendering updates and sidebar opacity controls.
2026-08-24 18:48:53 +10:00
GitHub Actions
ce29b24c28 chore: bump version to 4.11.185 [skip ci] 2026-08-23 14:55:15 +00:00
608cbaf682 Add Discord collectibles IPC and bump cinny for profile overlays.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / prepare-release (push) Successful in 15s
Build / build-windows (push) Successful in 6m55s
Build / build-linux (push) Successful in 3m10s
Build / finalize-release (push) Successful in 4s
Wire electron-side catalog fetch and asset download, exposing collectibles APIs to the renderer for live Discord shop browsing and Matrix profile uploads.
2026-08-24 00:55:01 +10:00
GitHub Actions
8ca7be8b42 chore: bump version to 4.11.184 [skip ci] 2026-08-23 06:55:17 +00:00
3c55fe65a3 Bump cinny: What's New dialog fixes and readable dev class names.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 7m2s
Build / build-linux (push) Successful in 3m4s
Build / finalize-release (push) Successful in 4s
2026-08-23 16:55:04 +10:00
GitHub Actions
f99b3c85d4 chore: bump version to 4.11.183 [skip ci] 2026-08-23 06:18:56 +00:00
fc4f087c68 Bump cinny for Android About/updates fixes and overlay user color crash guard.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 6m40s
Build / build-linux (push) Successful in 3m3s
Build / finalize-release (push) Successful in 4s
2026-08-23 16:18:43 +10:00
GitHub Actions
6d347a6f6b chore: bump version to 4.11.182 [skip ci] 2026-08-22 22:38:44 +00:00
316a5514e6 Bump cinny for Android View updates dialog fix.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 15s
Build / build-windows (push) Successful in 6m44s
Build / build-linux (push) Successful in 3m2s
Build / finalize-release (push) Successful in 4s
2026-08-23 08:38:29 +10:00
GitHub Actions
8392e3f7f5 chore: bump version to 4.11.181 [skip ci] 2026-08-22 22:26:52 +00:00
61b3cbc6d8 Point desktop build at cinny-local update manifest script for mobile CI.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 15s
Build / build-windows (push) Successful in 6m46s
Build / build-linux (push) Successful in 3m1s
Build / finalize-release (push) Successful in 7s
2026-08-23 08:26:33 +10:00
GitHub Actions
9befaa14a5 chore: bump version to 4.11.180 [skip ci] 2026-08-22 22:23:43 +00:00
ea45c4551b Bump cinny for What's New auto-show on first launch after update.
Some checks failed
Build / increment-version (push) Successful in 12s
Build / prepare-release (push) Successful in 14s
Build / finalize-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-08-23 08:23:23 +10:00
GitHub Actions
146927fc10 chore: bump version to 4.11.179 [skip ci] 2026-08-22 22:20:20 +00:00
8edbc1b19a Bump cinny for Android updates dialog fix and add release notes manifest workflow.
Some checks failed
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Has started running
Build / finalize-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-08-23 08:20:07 +10:00
GitHub Actions
9700c645a3 chore: bump version to 4.11.178 [skip ci] 2026-08-22 22:07:17 +00:00
2828ae425c Add native media save dialog and bump cinny submodule.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 6m51s
Build / build-linux (push) Successful in 3m11s
Build / finalize-release (push) Successful in 5s
Wire Electron/Tauri save-file IPC for desktop downloads and update the cinny submodule with username colors, release notes, and account profile layout fixes.
2026-08-23 08:07:02 +10:00
GitHub Actions
5e06256973 chore: bump version to 4.11.177 [skip ci] 2026-08-09 08:35:32 +00:00
28c7dedf65 Bump cinny for fullscreen sick emoji confetti spew.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 7m35s
Build / build-linux (push) Successful in 3m13s
Build / finalize-release (push) Successful in 4s
2026-08-09 18:35:17 +10:00
GitHub Actions
ca10121796 chore: bump version to 4.11.176 [skip ci] 2026-08-09 05:59:10 +00:00
1ad1fc7b70 Bump cinny for shadowbox image viewer.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 15s
Build / build-windows (push) Successful in 7m31s
Build / build-linux (push) Successful in 3m16s
Build / finalize-release (push) Successful in 6s
2026-08-09 15:58:52 +10:00
GitHub Actions
f16b84a9f1 chore: bump version to 4.11.175 [skip ci] 2026-08-09 05:50:38 +00:00
e917193b0d Harden Linux clipboard image paste and bump cinny.
Some checks failed
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 15s
Build / build-windows (push) Successful in 7m28s
Build / finalize-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Require a real image/* clipboard format before treating paste as an image, and pick up firework confetti plus URL-paste fixes from cinny.
2026-08-09 15:50:23 +10:00
GitHub Actions
fc5d498673 chore: bump version to 4.11.174 [skip ci] 2026-08-09 03:17:02 +00:00
c01bd9a43e Bump cinny for bot-driven room app UI.
All checks were successful
Build / increment-version (push) Successful in 14s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Successful in 8m3s
Build / build-linux (push) Successful in 3m22s
Build / finalize-release (push) Successful in 4s
2026-08-09 13:16:48 +10:00
GitHub Actions
3f4b1b3fc4 chore: bump version to 4.11.173 [skip ci] 2026-08-03 05:22:25 +00:00
24b59a454a Ensure wine binary exists for Windows NSIS cross-builds.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-linux (push) Successful in 2m58s
Build / build-windows (push) Successful in 5m46s
Build / finalize-release (push) Successful in 4s
2026-08-03 15:22:12 +10:00
GitHub Actions
fac9a49d56 chore: bump version to 4.11.172 [skip ci] 2026-08-03 05:16:00 +00:00
83993efc83 Run Windows Electron builds on Linux with Wine.
Some checks failed
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-linux (push) Successful in 3m2s
Build / build-windows (push) Failing after 4m57s
Build / finalize-release (push) Successful in 4s
2026-08-03 15:15:46 +10:00
GitHub Actions
44a9230df3 chore: bump version to 4.11.171 [skip ci] 2026-08-03 05:08:38 +00:00
658ff1215c Bump cinny: local API message and unread endpoints by scope.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 14s
Build / build-linux (push) Successful in 2m55s
Build / build-windows (push) Successful in 3m54s
Build / finalize-release (push) Successful in 5s
2026-08-03 15:08:25 +10:00
GitHub Actions
829e9b06cb chore: bump version to 4.11.170 [skip ci] 2026-07-29 10:41:07 +00:00
e64279db74 Bump cinny: emoji confetti bursts and component playground.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / prepare-release (push) Successful in 13s
Build / build-linux (push) Successful in 3m13s
Build / build-windows (push) Successful in 6m11s
Build / finalize-release (push) Successful in 4s
Wire the playground npm script through the desktop wrapper.
2026-07-29 20:40:53 +10:00
GitHub Actions
85d95faf11 chore: bump version to 4.11.169 [skip ci] 2026-07-25 10:09:49 +00:00
61de658029 Bump cinny: fix avatar upload for JPEG/WebP/GIF without profile color.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / prepare-release (push) Successful in 16s
Build / build-linux (push) Successful in 3m21s
Build / build-windows (push) Successful in 5m45s
Build / finalize-release (push) Successful in 4s
2026-07-25 20:09:35 +10:00
GitHub Actions
c8b57ba217 chore: bump version to 4.11.168 [skip ci] 2026-07-24 06:52:49 +00:00
8d8809d0c7 Bump cinny: open DMs while space parent maps catch up.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / prepare-release (push) Successful in 14s
Build / build-linux (push) Successful in 2m44s
Build / build-windows (push) Successful in 3m45s
Build / finalize-release (push) Successful in 4s
2026-07-24 16:52:35 +10:00
GitHub Actions
55a0f4f566 chore: bump version to 4.11.167 [skip ci] 2026-07-24 06:24:58 +00:00
f488f32d88 Fix PowerShell mangling GitHub upload URLs with ?name=.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 13s
Build / build-linux (push) Successful in 2m45s
Build / build-windows (push) Successful in 3m43s
Build / finalize-release (push) Successful in 4s
2026-07-24 16:24:44 +10:00
GitHub Actions
9154929de9 chore: bump version to 4.11.166 [skip ci] 2026-07-24 06:20:05 +00:00
c2cc0d5ed5 Fix PowerShell origin URL trimming for submodule checkout.
Some checks failed
Build / increment-version (push) Successful in 13s
Build / prepare-release (push) Successful in 13s
Build / build-linux (push) Successful in 2m44s
Build / build-windows (push) Failing after 3m22s
Build / finalize-release (push) Successful in 8s
2026-07-24 16:19:51 +10:00
GitHub Actions
aa38348fdd chore: bump version to 4.11.165 [skip ci] 2026-07-24 06:17:38 +00:00
02ab544a37 Fix CI Windows upload shell and submodule clone host.
Some checks failed
Build / increment-version (push) Successful in 12s
Build / prepare-release (push) Successful in 14s
Build / build-windows (push) Failing after 11s
Build / finalize-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Use PowerShell + curl.exe on the Windows runner (no WSL), and clone cinny via a relative/origin-derived URL so act runners do not hairpin through public HTTPS.
2026-07-24 16:17:23 +10:00
GitHub Actions
3707a6d38a chore: bump version to 4.11.164 [skip ci] 2026-07-24 06:09:58 +00:00
fe1f4abf2d Bypass flaky Actions artifacts for AppImage/exe uploads.
Some checks failed
Build / increment-version (push) Successful in 12s
Build / prepare-release (push) Successful in 13s
Build / build-windows (push) Failing after 3m27s
Build / finalize-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Publish build outputs straight to Gitea/GitHub releases via the REST API with no HTTP timeout, since Gitea's artifact pipeline keeps timing out on ~130MB binaries.
2026-07-24 16:09:44 +10:00
147389724e Point cinny submodule at git.ruv.wtf. 2026-07-24 16:02:45 +10:00
GitHub Actions
d4d4c73c2b chore: bump version to 4.11.163 [skip ci] 2026-07-24 06:01:21 +00:00
73fbc1c77a Bump cinny: hide Jump to Latest when scrolled to live bottom.
Some checks failed
Build / increment-version (push) Successful in 12s
Build / build-windows (push) Successful in 3m30s
Build / build-linux (push) Failing after 7m44s
Build / create-release (push) Has been cancelled
2026-07-24 16:01:06 +10:00
GitHub Actions
3dc732521f chore: bump version to 4.11.162 [skip ci] 2026-07-23 05:18:40 +00:00
b76aa3fe4a Add mock auto-updater in dev and title-bar download progress.
All checks were successful
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Successful in 1m54s
Build / build-windows (push) Successful in 3m36s
Build / create-release (push) Successful in 1m13s
2026-07-23 15:18:25 +10:00
GitHub Actions
c0d1e87b8d chore: bump version to 4.11.161 [skip ci] 2026-07-23 05:14:07 +00:00
04d4026e78 Fix Linux SSO return via paarrot://desktop and protocol handler repair.
Some checks failed
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m55s
Build / build-windows (push) Successful in 3m35s
Build / create-release (push) Has been cancelled
2026-07-23 15:13:54 +10:00
GitHub Actions
cfbc59d6a9 chore: bump version to 4.11.160 [skip ci] 2026-07-23 04:58:11 +00:00
dfe42fa942 Sync cinny: remove editor autocorrect workaround.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m54s
Build / build-windows (push) Successful in 3m35s
Build / create-release (push) Successful in 1m12s
2026-07-23 14:57:58 +10:00
GitHub Actions
6a8397425e chore: bump version to 4.11.159 [skip ci] 2026-07-23 04:52:37 +00:00
641ec26142 Sync cinny: Shared Media drawer and same-room navigation fixes.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m59s
Build / build-windows (push) Successful in 3m47s
Build / create-release (push) Successful in 1m12s
2026-07-23 14:52:23 +10:00
GitHub Actions
1eaf246806 chore: bump version to 4.11.158 [skip ci] 2026-07-22 14:24:31 +00:00
0e97e4d3b6 Sync cinny: Stationery and Stationery Dark themes.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m58s
Build / build-windows (push) Successful in 3m37s
Build / create-release (push) Successful in 1m13s
2026-07-23 00:24:17 +10:00
GitHub Actions
1636a8d0c6 chore: bump version to 4.11.157 [skip ci] 2026-07-22 10:29:14 +00:00
ac236750aa Sync cinny: fix jumbo emoji regex for production builds.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Successful in 1m52s
Build / build-windows (push) Successful in 3m40s
Build / create-release (push) Successful in 1m12s
2026-07-22 20:29:00 +10:00
GitHub Actions
7e747e6c36 chore: bump version to 4.11.156 [skip ci] 2026-07-22 10:14:27 +00:00
1b2cae5b12 Sync cinny: fix jumbo emoji in production builds.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m56s
Build / build-windows (push) Successful in 3m42s
Build / create-release (push) Successful in 1m11s
2026-07-22 20:14:15 +10:00
GitHub Actions
76921c4e79 chore: bump version to 4.11.155 [skip ci] 2026-07-22 10:00:52 +00:00
688efff7c9 Sync cinny submodule and stop Vite from opening a browser on dev.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m55s
Build / build-windows (push) Successful in 3m36s
Build / create-release (push) Successful in 1m13s
Pull in timeline scroll memory, media height reservation, avatar caching, jumbo emoji, and Unicode 15 emoji font coverage.
2026-07-22 20:00:38 +10:00
GitHub Actions
9bc377b62a chore: bump version to 4.11.154 [skip ci] 2026-07-22 09:11:11 +00:00
836ccce7a3 Revert cinny to pre-jumbo emoji baseline.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m57s
Build / build-windows (push) Successful in 3m37s
Build / create-release (push) Successful in 1m13s
Roll back submodule to 154f4dfd before jumbo emoji and related styling regressions.
2026-07-22 19:10:58 +10:00
GitHub Actions
34c8e6e38c chore: bump version to 4.11.153 [skip ci] 2026-07-22 09:03:00 +00:00
e69bda3aa6 Bump cinny: fix production chat text color in Electron builds.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Successful in 1m51s
Build / build-windows (push) Successful in 3m43s
Build / create-release (push) Successful in 1m13s
2026-07-22 19:02:46 +10:00
GitHub Actions
5d55e5d826 chore: bump version to 4.11.152 [skip ci] 2026-07-22 08:37:39 +00:00
b5902b9fc3 Bump cinny: fix production CSS loading in Electron build.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m51s
Build / build-windows (push) Successful in 3m34s
Build / create-release (push) Successful in 1m12s
2026-07-22 18:37:26 +10:00
GitHub Actions
f60cf0bd3c chore: bump version to 4.11.151 [skip ci] 2026-07-22 08:21:32 +00:00
83dec0ef54 Bump cinny: fix black chat pane and jumbo emoji rendering.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Successful in 1m50s
Build / build-windows (push) Successful in 3m35s
Build / create-release (push) Successful in 1m12s
2026-07-22 18:21:18 +10:00
GitHub Actions
40a76f02cd chore: bump version to 4.11.150 [skip ci] 2026-07-22 08:02:08 +00:00
3def3cc456 Bump cinny: instant room switches, perf cleanup, members drawer prefs.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m53s
Build / build-windows (push) Successful in 3m42s
Build / create-release (push) Successful in 1m13s
2026-07-22 18:01:53 +10:00
GitHub Actions
30bd67d43a chore: bump version to 4.11.149 [skip ci] 2026-07-22 06:16:20 +00:00
ffeccee680 Bump cinny: media placeholders, avatar blob cache, jumbo emoji.
All checks were successful
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Successful in 1m58s
Build / build-windows (push) Successful in 3m50s
Build / create-release (push) Successful in 1m13s
2026-07-22 14:42:19 +10:00
GitHub Actions
facbeee024 chore: bump version to 4.11.148 [skip ci] 2026-07-21 06:21:51 +00:00
09102c7903 Bump cinny: fix jumbo emoji actually rendering at 100px.
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 1m55s
Build / build-windows (push) Successful in 5m42s
Build / create-release (push) Successful in 1m13s
2026-07-21 16:21:37 +10:00
GitHub Actions
fa33ec7ac8 chore: bump version to 4.11.147 [skip ci] 2026-07-21 05:56:18 +00:00
c44f5cf14a Bump cinny: fix jumbo standalone emoji sizing.
All checks were successful
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Successful in 1m57s
Build / build-windows (push) Successful in 5m21s
Build / create-release (push) Successful in 1m14s
2026-07-21 15:56:03 +10:00
GitHub Actions
27793d7c4f chore: bump version to 4.11.146 [skip ci] 2026-07-21 05:38:44 +00:00
32b2902ef7 Bump cinny: fix jumbo emoji vanilla-extract build.
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 2m2s
Build / build-windows (push) Successful in 3m33s
Build / create-release (push) Successful in 1m13s
2026-07-21 15:38:29 +10:00
GitHub Actions
5cdb318854 chore: bump version to 4.11.145 [skip ci] 2026-07-21 05:34:16 +00:00
a28f05bb88 Bump cinny: theme polish, jumbo emoji, and Windows autocorrect fix.
Some checks failed
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Failing after 44s
Build / build-windows (push) Failing after 1m4s
Build / create-release (push) Has been skipped
2026-07-21 15:34:00 +10:00
GitHub Actions
cecfe73b2e chore: bump version to 4.11.144 [skip ci] 2026-07-14 17:31:07 +00:00
b458c7918c chore: bump cinny submodule for emoji font spacing fix
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 5m10s
Build / create-release (push) Successful in 1m13s
2026-07-15 03:30:53 +10:00
GitHub Actions
d8660f4822 chore: bump version to 4.11.143 [skip ci] 2026-07-12 16:40:22 +00:00
dad83773a6 Bump cinny: Twemoji digit fix and Slate emoji focus crash
All checks were successful
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Successful in 1m55s
Build / build-windows (push) Successful in 4m48s
Build / create-release (push) Successful in 1m12s
2026-07-13 02:40:08 +10:00
GitHub Actions
d2d35de46b chore: bump version to 4.11.142 [skip ci] 2026-07-12 16:25:25 +00:00
c752ec0b38 Bump cinny: fix noise/echo cancellation settings
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 1m57s
Build / build-windows (push) Successful in 5m5s
Build / create-release (push) Successful in 1m13s
2026-07-13 02:25:10 +10:00
GitHub Actions
28752eb56e chore: bump version to 4.11.141 [skip ci] 2026-07-12 08:15:59 +00:00
4f243a0fcf Bump cinny: fix vanilla-extract presence CSS build failure.
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 1m57s
Build / build-windows (push) Successful in 4m48s
Build / create-release (push) Successful in 1m13s
2026-07-12 18:15:44 +10:00
GitHub Actions
9bce616f26 chore: bump version to 4.11.140 [skip ci] 2026-07-11 22:19:18 +00:00
f16ab73c9a Allow git deps in npm and bump cinny submodule.
Some checks failed
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Failing after 42s
Build / build-windows (push) Failing after 1m47s
Build / create-release (push) Has been skipped
2026-07-12 08:19:03 +10:00
GitHub Actions
c29a15e073 chore: bump version to 4.11.139 [skip ci] 2026-07-11 21:52:03 +00:00
f5fe61e3eb hidden shit, and online status bars, search settings
Some checks failed
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Failing after 42s
Build / build-windows (push) Failing after 1m29s
Build / create-release (push) Has been skipped
2026-07-12 07:51:47 +10:00
GitHub Actions
a1a752b0fb chore: bump version to 4.11.138 [skip ci] 2026-07-11 21:15:56 +00:00
7769250875 Removed invalid channels
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 1m56s
Build / build-windows (push) Successful in 4m36s
Build / create-release (push) Successful in 1m13s
2026-07-12 07:15:28 +10:00
GitHub Actions
b3af9f3185 chore: bump version to 4.11.137 [skip ci] 2026-07-10 11:40:45 +00:00
7906326790 update base
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 1m56s
Build / build-windows (push) Successful in 4m56s
Build / create-release (push) Successful in 1m13s
2026-07-10 21:40:27 +10:00
GitHub Actions
3b4babb9d7 chore: bump version to 4.11.136 [skip ci] 2026-07-08 18:22:44 +00:00
4b284ec747 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 2m0s
Build / build-windows (push) Successful in 3m28s
Build / create-release (push) Successful in 1m12s
2026-07-09 04:22:28 +10:00
e1b4cfb9c8 chore: bump cinny submodule for compact nav and back routing fixes 2026-07-09 04:22:14 +10:00
GitHub Actions
c0266903ec chore: bump version to 4.11.135 [skip ci] 2026-07-08 16:35:39 +00:00
81964d6f14 chore: bump cinny submodule for React Router 7 routing fixes
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 1m57s
Build / build-windows (push) Successful in 4m33s
Build / create-release (push) Successful in 1m15s
2026-07-09 02:35:18 +10:00
GitHub Actions
47e025cf55 chore: bump version to 4.11.134 [skip ci] 2026-07-08 15:56:06 +00:00
b400070ad0 chore: bump cinny submodule for sync recovery and toolchain upgrades
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 2m19s
Build / build-windows (push) Successful in 3m48s
Build / create-release (push) Successful in 1m14s
2026-07-09 01:55:21 +10:00
529694ce66 chore: update dependencies and improve external link handling in main.js 2026-07-09 01:52:52 +10:00
GitHub Actions
791c3ab4df chore: bump version to 4.11.133 [skip ci] 2026-07-06 06:07:10 +00:00
de8d9db795 chore: update subproject commit reference in cinny to indicate dirty state
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 2m52s
Build / build-windows (push) Successful in 4m32s
Build / create-release (push) Successful in 1m15s
2026-07-06 16:06:52 +10:00
GitHub Actions
ffaf9ff66a chore: bump version to 4.11.132 [skip ci] 2026-07-06 05:35:44 +00:00
f0891ba4cc Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Successful in 2m11s
Build / build-windows (push) Successful in 3m45s
Build / create-release (push) Successful in 1m11s
2026-07-06 15:35:28 +10:00
627b117119 chore: update subproject commit reference in cinny 2026-07-06 15:35:27 +10:00
GitHub Actions
b6c6659a5a chore: bump version to 4.11.131 [skip ci] 2026-07-05 17:29:38 +00:00
d7a73afebc Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Successful in 2m16s
Build / build-windows (push) Successful in 3m53s
Build / create-release (push) Successful in 1m7s
2026-07-06 03:29:23 +10:00
e469d51ba4 fix: improve navigation handler in main.js to better manage external links 2026-07-06 03:29:19 +10:00
GitHub Actions
6ba03277e5 chore: bump version to 4.11.130 [skip ci] 2026-07-05 15:30:44 +00:00
fdb2dca794 fix: update subproject status to dirty in cinny
All checks were successful
Build / increment-version (push) Successful in 13s
Build / build-linux (push) Successful in 2m12s
Build / build-windows (push) Successful in 4m53s
Build / create-release (push) Successful in 1m11s
2026-07-06 01:30:28 +10:00
GitHub Actions
8797bb5a22 chore: bump version to 4.11.129 [skip ci] 2026-07-05 15:07:08 +00:00
4d154fd4f3 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 36s
Build / build-linux (push) Successful in 2m15s
Build / build-windows (push) Successful in 4m8s
Build / create-release (push) Successful in 1m12s
2026-07-06 01:06:28 +10:00
42e8fb71ef fix: mark subproject as dirty in cinny 2026-07-06 01:06:26 +10:00
GitHub Actions
ff2c284984 chore: bump version to 4.11.128 [skip ci] 2026-05-24 15:01:27 +00:00
75be84e472 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 2m1s
Build / build-windows (push) Successful in 4m22s
Build / create-release (push) Successful in 1m10s
2026-05-25 01:01:10 +10:00
a3c91b306a fix: remove element protocol support and update related functionality 2026-05-25 01:01:06 +10:00
GitHub Actions
20edc293bb chore: bump version to 4.11.127 [skip ci] 2026-05-24 14:34:35 +00:00
6803eac8fb Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 2m4s
Build / build-windows (push) Successful in 5m5s
Build / create-release (push) Successful in 1m10s
2026-05-25 00:34:19 +10:00
fa5592f1e4 feat: enhance protocol handling and diagnostics for Paarrot and Element URLs 2026-05-25 00:34:15 +10:00
GitHub Actions
78601be6f4 chore: bump version to 4.11.126 [skip ci] 2026-05-24 14:14:31 +00:00
c940bdc78c feat: add support for element:// deep links and register protocol handler
All checks were successful
Build / increment-version (push) Successful in 15s
Build / build-linux (push) Successful in 2m11s
Build / build-windows (push) Successful in 5m35s
Build / create-release (push) Successful in 1m11s
2026-05-25 00:14:09 +10:00
GitHub Actions
9471fe2f5d chore: bump version to 4.11.125 [skip ci] 2026-05-17 07:08:14 +00:00
57f7fe7086 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Successful in 1m59s
Build / build-windows (push) Successful in 4m46s
Build / create-release (push) Successful in 1m8s
2026-05-17 17:08:00 +10:00
a2364f2622 fix: mark subproject as dirty in cinny 2026-05-17 17:07:58 +10:00
GitHub Actions
df2220581d chore: bump version to 4.11.124 [skip ci] 2026-05-17 06:53:59 +00:00
198eb0fe04 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Successful in 1m59s
Build / build-windows (push) Successful in 5m24s
Build / create-release (push) Successful in 1m18s
2026-05-17 16:53:45 +10:00
fff114abef fix: mark subproject as dirty in cinny 2026-05-17 16:53:41 +10:00
GitHub Actions
7013f1c22d chore: bump version to 4.11.123 [skip ci] 2026-05-17 06:51:55 +00:00
1dbc7d6cc4 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Failing after 47s
Build / build-windows (push) Failing after 1m37s
Build / create-release (push) Has been skipped
2026-05-17 16:51:43 +10:00
f93f109d4a fix: mark subproject as dirty in cinny 2026-05-17 16:51:40 +10:00
GitHub Actions
f707aa98f5 chore: bump version to 4.11.122 [skip ci] 2026-05-17 06:48:56 +00:00
8a0be09d5d Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Failing after 47s
Build / build-windows (push) Failing after 1m30s
Build / create-release (push) Has been skipped
2026-05-17 16:48:43 +10:00
a1b86f00ba chore: update cinny submodule to latest commit 2026-05-17 16:48:39 +10:00
GitHub Actions
c9f569a2b2 chore: bump version to 4.11.121 [skip ci] 2026-05-17 06:46:51 +00:00
7a736b3adf Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Failing after 47s
Build / build-windows (push) Failing after 1m25s
Build / create-release (push) Has been skipped
2026-05-17 16:46:39 +10:00
b6ecd16148 fix: mark subproject as dirty in cinny 2026-05-17 16:46:37 +10:00
GitHub Actions
30d6b8dc74 chore: bump version to 4.11.120 [skip ci] 2026-05-17 06:43:40 +00:00
b6bcd8d05b chore: update cinny submodule to restored main commits
Some checks failed
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Failing after 47s
Build / build-windows (push) Failing after 1m26s
Build / create-release (push) Has been skipped
2026-05-17 16:43:28 +10:00
GitHub Actions
0b5fe5f04c chore: bump version to 4.11.119 [skip ci] 2026-05-17 06:39:23 +00:00
c629c07bf9 chore: point cinny submodule to main head
Some checks failed
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Failing after 47s
Build / build-windows (push) Failing after 1m26s
Build / create-release (push) Has been skipped
2026-05-17 16:38:51 +10:00
GitHub Actions
fcd7d3e7ea chore: bump version to 4.11.118 [skip ci] 2026-05-17 06:30:07 +00:00
db994a5d43 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 12s
Build / build-linux (push) Failing after 50s
Build / build-windows (push) Failing after 1m31s
Build / create-release (push) Has been skipped
2026-05-17 16:29:55 +10:00
fea9ebd501 fix: enhance submodule checkout process in CI workflow 2026-05-17 16:29:49 +10:00
GitHub Actions
9238d80adb chore: bump version to 4.11.117 [skip ci] 2026-05-17 06:27:52 +00:00
a28d3c26b5 fix: improve version bump logic and output handling in CI workflow
Some checks failed
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Failing after 11s
Build / build-windows (push) Failing after 19s
Build / create-release (push) Has been skipped
2026-05-17 16:27:36 +10:00
GitHub Actions
161030927f chore: bump version to 4.11.116 [skip ci] 2026-05-17 06:23:23 +00:00
c431c1d547 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 10s
Build / build-linux (push) Failing after 11s
Build / build-windows (push) Failing after 20s
Build / create-release (push) Has been skipped
2026-05-17 16:23:09 +10:00
739c863601 fix: mark subproject commit as dirty 2026-05-17 16:23:08 +10:00
GitHub Actions
5b6c213101 chore: bump version to 4.11.115 [skip ci] 2026-05-17 03:46:27 +00:00
5acf981bd4 fix: enhance version bump process with retry logic and update subproject commit reference
All checks were successful
Build / increment-version (push) Successful in 10s
Build / build-linux (push) Successful in 1m59s
Build / build-windows (push) Successful in 5m36s
Build / create-release (push) Successful in 1m8s
2026-05-17 13:46:14 +10:00
GitHub Actions
14b27717a6 chore: bump version to 4.11.114 [skip ci] 2026-05-17 03:25:25 +00:00
bd3e1d5b9c fix: update subproject commit reference to a3caafa
Some checks failed
Build / increment-version (push) Failing after 10s
Build / build-linux (push) Successful in 2m7s
Build / build-windows (push) Successful in 5m37s
Build / create-release (push) Successful in 1m9s
2026-05-17 13:24:51 +10:00
a668544c6e fix: update subproject commit reference and bump version to 4.11.113 2026-05-17 13:24:36 +10:00
GitHub Actions
dd8f4eb47d chore: bump version to 4.11.113 [skip ci] 2026-05-13 01:26:36 +00:00
54cb11e55b fix: update subproject commit reference
Some checks failed
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Failing after 48s
Build / build-windows (push) Failing after 1m34s
Build / create-release (push) Has been skipped
2026-05-13 11:26:20 +10:00
dec48f6c1d fix: update subproject commit reference 2026-05-13 10:52:57 +10:00
GitHub Actions
8f776f4f7c chore: bump version to 4.11.112 [skip ci] 2026-05-12 22:00:38 +00:00
c6aff43467 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 5m31s
Build / create-release (push) Successful in 1m12s
2026-05-13 08:00:26 +10:00
9f95ef1ecd fix: mark subproject as dirty to indicate uncommitted changes 2026-05-13 08:00:23 +10:00
19 changed files with 3387 additions and 2631 deletions

View File

@@ -8,13 +8,15 @@ on:
tags:
- 'v*'
# Large AppImage/exe uploads time out on Gitea's actions artifact pipeline.
# Build jobs publish assets straight to Gitea/GitHub releases via the REST API.
jobs:
increment-version:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]')
outputs:
version: ${{ steps.bump.outputs.VERSION }}
should_build: ${{ steps.bump.outputs.SHOULD_BUILD }}
version: ${{ steps.finalize.outputs.VERSION }}
should_build: ${{ steps.decide.outputs.SHOULD_BUILD }}
steps:
- name: Checkout repository
@@ -22,9 +24,35 @@ jobs:
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Decide version behavior
id: decide
run: |
CURRENT=$(grep -Po '"version":\s*"\K[^"]+' package.json | head -1)
COMMIT_MSG="${{ github.event.head_commit.message || '' }}"
SHOULD_BUMP=false
SHOULD_BUILD=true
if [ "${{ github.ref }}" = "refs/heads/main" ] && ! echo "$COMMIT_MSG" | grep -qi '\[skip ci\]'; then
SHOULD_BUMP=true
fi
if echo "$COMMIT_MSG" | grep -qi '\[skip ci\]'; then
SHOULD_BUILD=false
fi
echo "CURRENT_VERSION=$CURRENT" >> $GITHUB_OUTPUT
echo "SHOULD_BUMP=$SHOULD_BUMP" >> $GITHUB_OUTPUT
echo "SHOULD_BUILD=$SHOULD_BUILD" >> $GITHUB_OUTPUT
echo "Current version: $CURRENT"
echo "Should bump: $SHOULD_BUMP"
echo "Should build: $SHOULD_BUILD"
- name: Bump patch version
id: bump
if: ${{ steps.decide.outputs.SHOULD_BUMP == 'true' }}
run: |
# Get current version from package.json
CURRENT=$(grep -Po '"version":\s*"\K[^"]+' package.json | head -1)
@@ -45,20 +73,136 @@ jobs:
grep '"version"' package.json | head -1
echo "VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "SHOULD_BUILD=true" >> $GITHUB_OUTPUT
- name: Commit and push version bump
if: ${{ steps.decide.outputs.SHOULD_BUMP == 'true' }}
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add package.json
git commit -m "chore: bump version to ${{ steps.bump.outputs.VERSION }} [skip ci]"
git push
for attempt in 1 2 3; do
git fetch origin main
git rebase origin/main || {
git rebase --abort || true
echo "Failed to rebase version bump onto latest main"
exit 1
}
if git push origin HEAD:main; then
exit 0
fi
echo "Push rejected on attempt ${attempt}, retrying against latest main"
done
echo "Failed to push version bump after 3 attempts"
exit 1
- name: Finalize version output
id: finalize
run: |
if [ -n "${{ steps.bump.outputs.VERSION }}" ]; then
echo "VERSION=${{ steps.bump.outputs.VERSION }}" >> $GITHUB_OUTPUT
else
echo "VERSION=${{ steps.decide.outputs.CURRENT_VERSION }}" >> $GITHUB_OUTPUT
fi
prepare-release:
runs-on: ubuntu-latest
needs: increment-version
if: always() && needs.increment-version.outputs.should_build == 'true'
outputs:
version: ${{ steps.version.outputs.VERSION }}
tag: ${{ steps.version.outputs.TAG_NAME }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref_name }}
- name: Pull latest (after version bump)
if: github.ref == 'refs/heads/main'
run: git pull origin main
- name: Resolve version
id: version
run: |
if [ -n "${{ needs.increment-version.outputs.version }}" ]; then
VERSION="${{ needs.increment-version.outputs.version }}"
else
VERSION=$(grep -Po '"version":\s*"\K[^"]+' package.json | head -1)
fi
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "TAG_NAME=v$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
- name: Create and push tag
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
TAG_NAME="v${VERSION}"
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
echo "Tag $TAG_NAME already exists"
else
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git tag -a "$TAG_NAME" -m "Release $TAG_NAME"
git push origin "$TAG_NAME"
fi
- name: Ensure Gitea release
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION="${{ steps.version.outputs.VERSION }}"
TAG_NAME="v${VERSION}"
API_BASE="${{ github.server_url }}/api/v1"
REPO="${{ github.repository }}"
RELEASE_ID=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/tags/${TAG_NAME}" | jq -r '.id // empty' || true)
if [ -z "$RELEASE_ID" ]; then
echo "Creating Gitea release ${TAG_NAME}..."
RELEASE_ID=$(curl -fsS -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${TAG_NAME}\", \"name\": \"Release ${TAG_NAME}\", \"body\": \"Release ${VERSION}\", \"draft\": false, \"prerelease\": false}" \
"${API_BASE}/repos/${REPO}/releases" | jq -r '.id')
fi
echo "Gitea release id: ${RELEASE_ID}"
- name: Ensure GitHub release
env:
GH_TOKEN: ${{ secrets.GHTOKEN }}
run: |
set -euo pipefail
VERSION="${{ steps.version.outputs.VERSION }}"
TAG_NAME="v${VERSION}"
GITHUB_REPO="Paarrot/Paarrot-Desktop"
RELEASE_ID=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${TAG_NAME}" | jq -r '.id // empty' || true)
if [ -z "$RELEASE_ID" ]; then
echo "Creating GitHub release ${TAG_NAME}..."
RELEASE_ID=$(curl -fsS -X POST \
-H "Authorization: token ${GH_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${TAG_NAME}\", \"name\": \"Release ${TAG_NAME}\", \"body\": \"Release ${VERSION}\", \"draft\": false, \"prerelease\": false}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases" | jq -r '.id')
fi
echo "GitHub release id: ${RELEASE_ID}"
build-windows:
runs-on: windows
needs: increment-version
if: always() && (needs.increment-version.outputs.should_build == 'true' || needs.increment-version.result == 'skipped')
runs-on: ubuntu-latest
needs: [increment-version, prepare-release]
if: always() && needs.prepare-release.result == 'success'
steps:
- name: Checkout repository
@@ -66,21 +210,64 @@ jobs:
with:
submodules: false
ref: ${{ github.ref_name }}
- name: Checkout submodules manually
shell: powershell
run: |
git submodule update --init --recursive
set -euo pipefail
# Use the same host/scheme the runner already used for origin (not public HTTPS).
ORIGIN_URL=$(git remote get-url origin)
ORIGIN_URL=${ORIGIN_URL%/}
ORIGIN_URL=${ORIGIN_URL%.git}
BASE_URL=${ORIGIN_URL%/*}
CINNY_URL="${BASE_URL}/cinny.git"
echo "Rewriting submodule URL to ${CINNY_URL}"
git config submodule.cinny.url "${CINNY_URL}"
git submodule sync --recursive
if git submodule update --init --recursive; then
echo "Submodules checked out at pinned commits."
exit 0
fi
echo "Pinned submodule commit is unavailable on remote. Falling back to remote submodule HEAD."
git submodule deinit -f --all || true
rm -rf cinny .git/modules/cinny
git config submodule.cinny.url "${CINNY_URL}"
git submodule sync --recursive
git submodule update --init --recursive --remote
echo "Resolved submodule commit:"
git -C cinny rev-parse HEAD
- name: Pull latest (after version bump)
if: github.ref == 'refs/heads/main'
shell: powershell
run: git pull origin main
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Wine (NSIS cross-build)
run: |
set -euo pipefail
# electron-builder spawns `wine` by name; wine64 alone is not enough.
sudo dpkg --add-architecture i386
sudo apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
wine \
wine64 \
wine32 \
|| sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends wine64
if ! command -v wine >/dev/null 2>&1; then
WINE64_BIN=$(command -v wine64)
echo "No wine binary; linking wine64 -> /usr/local/bin/wine (${WINE64_BIN})"
sudo ln -sf "${WINE64_BIN}" /usr/local/bin/wine
fi
command -v wine
wine --version
- name: Install dependencies (root)
run: npm ci --prefer-offline
@@ -90,44 +277,122 @@ jobs:
run: npm ci --prefer-offline
- name: Clean previous builds
shell: powershell
run: |
if (Test-Path cinny/dist) { Remove-Item -Recurse -Force cinny/dist }
if (Test-Path dist-electron) { Remove-Item -Recurse -Force dist-electron }
rm -rf cinny/dist dist-electron
- name: Build Electron app
run: npm run build:local -- --win
env:
GH_TOKEN: ${{ secrets.GHTOKEN }}
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
- name: Upload NSIS Installer (x64)
uses: actions/upload-artifact@v3
with:
name: Paarrot-Setup-x64.exe
path: dist-electron/Paarrot-*-win-x64.exe
- name: Upload latest.yml
uses: actions/upload-artifact@v3
with:
name: latest.yml
path: dist-electron/latest.yml
- name: Upload Windows assets to releases
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GHTOKEN }}
run: |
set -euo pipefail
TAG_NAME="${{ needs.prepare-release.outputs.tag }}"
API_BASE="${{ github.server_url }}/api/v1"
REPO="${{ github.repository }}"
GITHUB_REPO="Paarrot/Paarrot-Desktop"
GITEA_RELEASE_ID=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/tags/${TAG_NAME}" | jq -r '.id')
GH_RELEASE_ID=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${TAG_NAME}" | jq -r '.id')
GH_UPLOAD_URL=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/${GH_RELEASE_ID}" | jq -r '.upload_url' | sed 's/{?name,label}//')
upload_one() {
local FILE="$1"
local FILENAME
FILENAME=$(basename "$FILE")
echo "Uploading ${FILENAME} ($(du -h "$FILE" | cut -f1))..."
OLD_ID=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${GITEA_RELEASE_ID}/assets" \
| jq -r --arg n "$FILENAME" '.[] | select(.name == $n) | .id' || true)
if [ -n "${OLD_ID:-}" ]; then
curl -fsS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${GITEA_RELEASE_ID}/assets/${OLD_ID}" >/dev/null
fi
curl -fsS --max-time 0 --retry 5 --retry-delay 10 \
-X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"${FILE}" \
"${API_BASE}/repos/${REPO}/releases/${GITEA_RELEASE_ID}/assets?name=${FILENAME}" \
>/dev/null
echo " Gitea: ok"
OLD_GH=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/${GH_RELEASE_ID}/assets" \
| jq -r --arg n "$FILENAME" '.[] | select(.name == $n) | .id' || true)
if [ -n "${OLD_GH:-}" ]; then
curl -fsS -X DELETE -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/assets/${OLD_GH}" >/dev/null
fi
curl -fsS --max-time 0 --retry 5 --retry-delay 10 \
-X POST \
-H "Authorization: token ${GH_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"${FILE}" \
"${GH_UPLOAD_URL}?name=${FILENAME}" \
>/dev/null
echo " GitHub: ok"
}
shopt -s nullglob
for FILE in dist-electron/Paarrot-*-win-x64.exe dist-electron/latest.yml; do
upload_one "$FILE"
done
- name: List build output
shell: powershell
run: |
Get-ChildItem -Path dist-electron -Recurse | Select-Object FullName
find dist-electron -type f -printf '%p\n' | sort
build-linux:
runs-on: ubuntu-latest
needs: increment-version
if: always() && (needs.increment-version.outputs.should_build == 'true' || needs.increment-version.result == 'skipped')
needs: [increment-version, prepare-release]
if: always() && needs.prepare-release.result == 'success'
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
submodules: false
ref: ${{ github.ref_name }}
- name: Checkout submodules manually
run: |
set -euo pipefail
# Use the same host/scheme the runner already used for origin (not public HTTPS).
ORIGIN_URL=$(git remote get-url origin)
ORIGIN_URL=${ORIGIN_URL%/}
ORIGIN_URL=${ORIGIN_URL%.git}
BASE_URL=${ORIGIN_URL%/*}
CINNY_URL="${BASE_URL}/cinny.git"
echo "Rewriting submodule URL to ${CINNY_URL}"
git config submodule.cinny.url "${CINNY_URL}"
git submodule sync --recursive
if git submodule update --init --recursive; then
echo "Submodules checked out at pinned commits."
exit 0
fi
echo "Pinned submodule commit is unavailable on remote. Falling back to remote submodule HEAD."
git submodule deinit -f --all || true
rm -rf cinny .git/modules/cinny
git config submodule.cinny.url "${CINNY_URL}"
git submodule sync --recursive
git submodule update --init --recursive --remote
echo "Resolved submodule commit:"
git -C cinny rev-parse HEAD
- name: Pull latest (after version bump)
if: github.ref == 'refs/heads/main'
@@ -154,208 +419,126 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GHTOKEN }}
- name: Upload AppImage
uses: actions/upload-artifact@v3
with:
name: Paarrot-Linux-x64.AppImage
path: dist-electron/Paarrot-*.AppImage
- name: Upload latest-linux.yml
uses: actions/upload-artifact@v3
with:
name: latest-linux.yml
path: dist-electron/latest-linux.yml
- name: Upload Linux assets to releases
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GHTOKEN }}
run: |
set -euo pipefail
VERSION="${{ needs.prepare-release.outputs.version }}"
TAG_NAME="${{ needs.prepare-release.outputs.tag }}"
API_BASE="${{ github.server_url }}/api/v1"
REPO="${{ github.repository }}"
GITHUB_REPO="Paarrot/Paarrot-Desktop"
create-release:
GITEA_RELEASE_ID=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/tags/${TAG_NAME}" | jq -r '.id')
GH_RELEASE_ID=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${TAG_NAME}" | jq -r '.id')
GH_UPLOAD_URL=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/${GH_RELEASE_ID}" | jq -r '.upload_url' | sed 's/{?name,label}//')
upload_one() {
local FILE="$1"
local FILENAME
FILENAME=$(basename "$FILE")
echo "Uploading ${FILENAME} ($(du -h "$FILE" | cut -f1))..."
OLD_ID=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${GITEA_RELEASE_ID}/assets" \
| jq -r --arg n "$FILENAME" '.[] | select(.name == $n) | .id' || true)
if [ -n "${OLD_ID:-}" ]; then
curl -fsS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${GITEA_RELEASE_ID}/assets/${OLD_ID}" >/dev/null
fi
curl -fsS --max-time 0 --retry 5 --retry-delay 10 \
-X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"${FILE}" \
"${API_BASE}/repos/${REPO}/releases/${GITEA_RELEASE_ID}/assets?name=${FILENAME}" \
>/dev/null
echo " Gitea: ok"
OLD_GH=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/${GH_RELEASE_ID}/assets" \
| jq -r --arg n "$FILENAME" '.[] | select(.name == $n) | .id' || true)
if [ -n "${OLD_GH:-}" ]; then
curl -fsS -X DELETE -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/assets/${OLD_GH}" >/dev/null
fi
curl -fsS --max-time 0 --retry 5 --retry-delay 10 \
-X POST \
-H "Authorization: token ${GH_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"${FILE}" \
"${GH_UPLOAD_URL}?name=${FILENAME}" \
>/dev/null
echo " GitHub: ok"
}
shopt -s nullglob
for FILE in dist-electron/Paarrot-*.AppImage dist-electron/latest-linux.yml; do
upload_one "$FILE"
done
finalize-release:
runs-on: ubuntu-latest
needs: [increment-version, build-windows, build-linux]
if: always() && (needs.build-windows.result == 'success' || needs.build-linux.result == 'success')
needs: [increment-version, prepare-release, build-windows, build-linux]
if: always() && needs.prepare-release.result == 'success' && (needs.build-windows.result == 'success' || needs.build-linux.result == 'success')
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref_name }}
- name: Pull latest (after version bump)
if: github.ref == 'refs/heads/main'
run: git pull origin main
- name: Get version
id: version
run: |
# Use version from increment-version job if available, otherwise read from file
if [ -n "${{ needs.increment-version.outputs.version }}" ]; then
VERSION="${{ needs.increment-version.outputs.version }}"
else
VERSION=$(grep -Po '"version":\s*"\K[^"]+' package.json | head -1)
fi
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
- name: Check if tag exists
id: tag_check
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
echo "Tag v$VERSION already exists"
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "Tag v$VERSION does not exist"
echo "exists=false" >> $GITHUB_OUTPUT
fi
- name: Create and push tag
if: steps.tag_check.outputs.exists == 'false'
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git tag -a "v${{ steps.version.outputs.VERSION }}" -m "Release v${{ steps.version.outputs.VERSION }}"
git push origin "v${{ steps.version.outputs.VERSION }}"
- name: Download all artifacts
uses: actions/download-artifact@v3
with:
path: artifacts
- name: Prepare release files
run: |
mkdir -p release-files
find artifacts -type f \( -name "*.exe" -o -name "*.AppImage" -o -name "*.yml" \) -exec cp {} release-files/ \;
ls -la release-files/
- name: Create or Update Release
- name: Cleanup old Gitea releases
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
TAG_NAME="v${VERSION}"
API_BASE="http://synbox.ruv.wtf:8418/api/v1"
REPO="litruv/cinny-desktop"
# Delete old releases (keep only current)
echo "Cleaning up old releases..."
OLD_RELEASES=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases" | jq -r '.[] | select(.tag_name != "'${TAG_NAME}'") | .id')
set -euo pipefail
TAG_NAME="${{ needs.prepare-release.outputs.tag }}"
API_BASE="${{ github.server_url }}/api/v1"
REPO="${{ github.repository }}"
echo "Cleaning up old Gitea releases (keeping ${TAG_NAME})..."
OLD_RELEASES=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases" | jq -r --arg t "$TAG_NAME" '.[] | select(.tag_name != $t) | .id')
for OLD_ID in $OLD_RELEASES; do
echo "Deleting old release ${OLD_ID}..."
OLD_TAG=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \
[ -n "$OLD_ID" ] || continue
OLD_TAG=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${OLD_ID}" | jq -r '.tag_name')
# Delete release
curl -s -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${OLD_ID}"
# Delete tag
if [ -n "$OLD_TAG" ]; then
echo "Deleting old tag ${OLD_TAG}..."
curl -s -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/tags/${OLD_TAG}"
echo "Deleting Gitea release ${OLD_ID} (${OLD_TAG})..."
curl -fsS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${OLD_ID}" >/dev/null || true
if [ -n "$OLD_TAG" ] && [ "$OLD_TAG" != "null" ]; then
curl -fsS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/tags/${OLD_TAG}" >/dev/null || true
fi
done
# Check if release exists
RELEASE_ID=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/tags/${TAG_NAME}" | jq -r '.id // empty')
if [ -z "$RELEASE_ID" ]; then
echo "Creating new release for ${TAG_NAME}..."
RELEASE_ID=$(curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${TAG_NAME}\", \"name\": \"Release ${TAG_NAME}\", \"body\": \"Release ${VERSION}\", \"draft\": false, \"prerelease\": false}" \
"${API_BASE}/repos/${REPO}/releases" | jq -r '.id')
echo "Created release with ID: ${RELEASE_ID}"
else
echo "Release exists with ID: ${RELEASE_ID}"
# Delete existing assets
echo "Deleting existing assets..."
ASSETS=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${RELEASE_ID}/assets" | jq -r '.[].id')
for ASSET_ID in $ASSETS; do
echo "Deleting asset ${ASSET_ID}..."
curl -s -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${RELEASE_ID}/assets/${ASSET_ID}"
done
fi
# Upload new assets
echo "Uploading assets..."
for FILE in release-files/*; do
FILENAME=$(basename "$FILE")
echo "Uploading ${FILENAME}..."
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary "@${FILE}" \
"${API_BASE}/repos/${REPO}/releases/${RELEASE_ID}/assets?name=${FILENAME}"
echo ""
done
echo "Release complete!"
- name: Create GitHub Release
- name: Cleanup old GitHub releases
env:
GH_TOKEN: ${{ secrets.GHTOKEN }}
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
TAG_NAME="v${VERSION}"
set -euo pipefail
TAG_NAME="${{ needs.prepare-release.outputs.tag }}"
GITHUB_REPO="Paarrot/Paarrot-Desktop"
# Delete old GitHub releases (keep only current)
echo "Cleaning up old GitHub releases..."
OLD_RELEASES=$(curl -s -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases" | jq -r '.[] | select(.tag_name != "'${TAG_NAME}'") | .id')
echo "Cleaning up old GitHub releases (keeping ${TAG_NAME})..."
OLD_RELEASES=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases" | jq -r --arg t "$TAG_NAME" '.[] | select(.tag_name != $t) | .id')
for OLD_ID in $OLD_RELEASES; do
echo "Deleting old GitHub release ${OLD_ID}..."
OLD_TAG=$(curl -s -H "Authorization: token ${GH_TOKEN}" \
[ -n "$OLD_ID" ] || continue
OLD_TAG=$(curl -fsS -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/${OLD_ID}" | jq -r '.tag_name')
# Delete release
curl -s -X DELETE -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/${OLD_ID}"
# Delete tag
if [ -n "$OLD_TAG" ]; then
echo "Deleting old GitHub tag ${OLD_TAG}..."
curl -s -X DELETE -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/git/refs/tags/${OLD_TAG}"
echo "Deleting GitHub release ${OLD_ID} (${OLD_TAG})..."
curl -fsS -X DELETE -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/${OLD_ID}" >/dev/null || true
if [ -n "$OLD_TAG" ] && [ "$OLD_TAG" != "null" ]; then
curl -fsS -X DELETE -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/git/refs/tags/${OLD_TAG}" >/dev/null || true
fi
done
# Check if release exists on GitHub
RELEASE_ID=$(curl -s -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${TAG_NAME}" | jq -r '.id // empty')
if [ -z "$RELEASE_ID" ]; then
echo "Creating new GitHub release for ${TAG_NAME}..."
RELEASE_ID=$(curl -s -X POST \
-H "Authorization: token ${GH_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"${TAG_NAME}\", \"name\": \"Release ${TAG_NAME}\", \"body\": \"Release ${VERSION}\", \"draft\": false, \"prerelease\": false}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases" | jq -r '.id')
echo "Created GitHub release with ID: ${RELEASE_ID}"
else
echo "GitHub release exists with ID: ${RELEASE_ID}"
fi
# Upload assets to GitHub
echo "Uploading assets to GitHub..."
UPLOAD_URL=$(curl -s -H "Authorization: token ${GH_TOKEN}" \
"https://api.github.com/repos/${GITHUB_REPO}/releases/${RELEASE_ID}" | jq -r '.upload_url' | sed 's/{?name,label}//')
for FILE in release-files/*; do
FILENAME=$(basename "$FILE")
echo "Uploading ${FILENAME} to GitHub..."
curl -s -X POST \
-H "Authorization: token ${GH_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary "@${FILE}" \
"${UPLOAD_URL}?name=${FILENAME}"
echo ""
done
echo "GitHub release complete!"
echo "Release finalize complete for ${TAG_NAME}"

35
.github/workflows/update-manifest.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Update release notes manifest
on:
push:
paths:
- 'cinny/public/update/*.md'
- 'cinny/scripts/generate-update-manifest.mjs'
permissions:
contents: write
jobs:
manifest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Generate manifest.json
run: node cinny/scripts/generate-update-manifest.mjs
- name: Commit manifest if changed
run: |
if git diff --quiet -- cinny/public/update/manifest.json; then
echo "manifest.json unchanged"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add cinny/public/update/manifest.json
git commit -m "chore: regenerate update manifest.json"
git push

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "cinny"]
path = cinny
url = http://synbox.ruv.wtf:8418/litruv/cinny.git
url = ../cinny.git

3
.npmrc
View File

@@ -1 +1,2 @@
save-exact=true
save-exact=true
allow-git=all

2
cinny

Submodule cinny updated: 43bd6320b8...47979718eb

View File

@@ -36,6 +36,12 @@
"filter": ["**/*"]
}
],
"protocols": [
{
"name": "Paarrot URL",
"schemes": ["paarrot"]
}
],
"linux": {
"target": [
{
@@ -51,6 +57,7 @@
"Name": "Paarrot",
"GenericName": "Matrix Client",
"Comment": "A Matrix client built with Cinny",
"MimeType": "x-scheme-handler/paarrot;",
"Categories": "Network;InstantMessaging;",
"Keywords": "matrix;chat;messaging;",
"StartupWMClass": "paarrot"

View File

@@ -153,6 +153,72 @@ class PaarrotAPIServer {
}
});
// Recent messages across group channels (non-DMs)
this.app.get('/messages/groups', async (req, res) => {
try {
const result = await this.executeAction('get-messages-groups', {
limit: req.query.limit,
});
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Recent messages across DMs
this.app.get('/messages/dms', async (req, res) => {
try {
const result = await this.executeAction('get-messages-dms', {
limit: req.query.limit,
});
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Recent messages across groups + DMs
this.app.get('/messages/combined', async (req, res) => {
try {
const result = await this.executeAction('get-messages-combined', {
limit: req.query.limit,
});
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Unread conversations across group channels (non-DMs)
this.app.get('/unreads/groups', async (req, res) => {
try {
const result = await this.executeAction('get-unreads-groups');
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Unread conversations across DMs
this.app.get('/unreads/dms', async (req, res) => {
try {
const result = await this.executeAction('get-unreads-dms');
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Unread conversations across groups + DMs
this.app.get('/unreads/combined', async (req, res) => {
try {
const result = await this.executeAction('get-unreads-combined');
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// 404 handler
this.app.use((req, res) => {
res.status(404).json({
@@ -169,7 +235,13 @@ class PaarrotAPIServer {
'GET /channels',
'POST /message',
'POST /message/current',
'GET /room/current'
'GET /room/current',
'GET /messages/groups',
'GET /messages/dms',
'GET /messages/combined',
'GET /unreads/groups',
'GET /unreads/dms',
'GET /unreads/combined'
]
});
});
@@ -237,6 +309,12 @@ class PaarrotAPIServer {
console.log(' POST /message');
console.log(' POST /message/current');
console.log(' GET /room/current');
console.log(' GET /messages/groups');
console.log(' GET /messages/dms');
console.log(' GET /messages/combined');
console.log(' GET /unreads/groups');
console.log(' GET /unreads/dms');
console.log(' GET /unreads/combined');
resolve(port);
}).on('error', (err) => {
if (err.code === 'EADDRINUSE') {

View File

@@ -0,0 +1,381 @@
/**
* Discord shop collectibles: fetch a published catalog and download selected assets from Discord CDN.
*/
const ITEM_TYPE_FOLDER = {
0: 'avatar-decorations',
1: 'profile-effects',
2: 'nameplates',
};
const ITEM_TYPE_KEY = {
0: 'avatar_decoration',
1: 'profile_effect',
2: 'nameplate',
};
const CDN_BASE = 'https://cdn.discordapp.com';
const CDN_HOST_RE = /(^|\.)(discordapp\.com|discordapp\.net|discord\.com)$/i;
const DEFAULT_USER_AGENT =
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0 Chrome/120 Electron/28 Safari/537.36';
const PUBLISHED_CATALOG_URLS = [
'https://github.com/litruv/discord-collectibles/releases/download/latest/profileeffects.json',
'https://github.com/litruv/discord-collectibles/releases/download/latest/nameplate.json',
'https://github.com/litruv/discord-collectibles/releases/download/latest/avatardecorations.json',
];
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
function isCdnUrl(value) {
if (typeof value !== 'string' || !value.startsWith('http')) return false;
try {
return CDN_HOST_RE.test(new URL(value).hostname);
} catch {
return false;
}
}
function basenameFromUrl(url) {
try {
const { pathname } = new URL(url);
const last = decodeURIComponent(pathname.split('/').filter(Boolean).pop() || '');
return last || 'asset';
} catch {
return 'asset';
}
}
function guessMimeType(filename) {
const lower = String(filename).toLowerCase();
if (lower.endsWith('.png')) return 'image/png';
if (lower.endsWith('.webp')) return 'image/webp';
if (lower.endsWith('.webm')) return 'video/webm';
if (lower.endsWith('.gif')) return 'image/gif';
if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg';
return 'application/octet-stream';
}
function deepCollectUrls(node, acc = new Set()) {
if (node == null) return acc;
if (typeof node === 'string') {
if (isCdnUrl(node)) acc.add(node);
return acc;
}
if (Array.isArray(node)) {
for (const v of node) deepCollectUrls(v, acc);
return acc;
}
if (typeof node === 'object') {
for (const v of Object.values(node)) deepCollectUrls(v, acc);
}
return acc;
}
function urlsForItem(item) {
const out = [];
const seen = new Set();
function push(url, filename, role) {
if (!url || seen.has(url)) return;
seen.add(url);
out.push({ url, filename, role: role || filename });
}
for (const url of deepCollectUrls(item)) {
let role = basenameFromUrl(url);
if (item.thumbnailPreviewSrc === url) role = 'thumbnail';
else if (item.reducedMotionSrc === url) role = 'reduced_motion';
else if (Array.isArray(item.effects)) {
const effectIndex = item.effects.findIndex((effect) => effect?.src === url);
if (effectIndex >= 0) role = `effect_${effectIndex}`;
}
push(url, basenameFromUrl(url), role);
}
if (item.type === 0 && item.asset) {
const base = `${CDN_BASE}/avatar-decoration-presets/${item.asset}.png`;
push(`${base}?passthrough=true`, 'animated.png', 'animated');
push(`${base}?passthrough=false`, 'static.png', 'static');
}
if (item.type === 2 && item.asset) {
const assetPath = String(item.asset).replace(/^\/+/, '');
push(`${CDN_BASE}/assets/collectibles/${assetPath}static.png`, 'static.png', 'static');
push(`${CDN_BASE}/assets/collectibles/${assetPath}asset.webm`, 'asset.webm', 'animated');
}
return out;
}
function decimalRgbToCss(color) {
const value = Number(color) >>> 0;
const r = (value >> 16) & 0xff;
const g = (value >> 8) & 0xff;
const b = value & 0xff;
return `rgb(${r}, ${g}, ${b})`;
}
function gradientFromColors(colors) {
if (!Array.isArray(colors) || colors.length === 0) return undefined;
if (colors.length === 1) return decimalRgbToCss(colors[0]);
return `linear-gradient(135deg, ${decimalRgbToCss(colors[0])}, ${decimalRgbToCss(colors[1])})`;
}
function previewAspectRatioForItem(item) {
if (item.type === 0) return 1;
if (item.type === 2) return 448 / 84;
const firstEffect = item.effects?.[0];
if (firstEffect?.width && firstEffect?.height) {
return firstEffect.width / firstEffect.height;
}
return 450 / 880;
}
function formatPaletteName(palette) {
if (!palette) return undefined;
return String(palette)
.split('_')
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(' ');
}
const NAMEPLATE_PALETTE_GRADIENTS = {
crimson: 'linear-gradient(135deg, #5c0a1c, #dc143c)',
berry: 'linear-gradient(135deg, #4a1030, #c42d78)',
sky: 'linear-gradient(135deg, #0a2a5c, #3b8eed)',
teal: 'linear-gradient(135deg, #0a3d3d, #2dd4bf)',
forest: 'linear-gradient(135deg, #0a2e1a, #22c55e)',
bubble_gum: 'linear-gradient(135deg, #4a1038, #f472b6)',
violet: 'linear-gradient(135deg, #2d1050, #8b5cf6)',
cobalt: 'linear-gradient(135deg, #0a1448, #3b5bdb)',
clover: 'linear-gradient(135deg, #0a3d20, #4ade80)',
lemon: 'linear-gradient(135deg, #4a3d0a, #fbbf24)',
white: 'linear-gradient(135deg, #888888, #f0f0f0)',
black: 'linear-gradient(135deg, #1a1a1a, #404040)',
};
function nameplatePaletteGradient(palette) {
if (!palette) return undefined;
return NAMEPLATE_PALETTE_GRADIENTS[palette];
}
function thumbnailForItem(item) {
if (item.thumbnailPreviewSrc && isCdnUrl(item.thumbnailPreviewSrc)) {
return item.thumbnailPreviewSrc;
}
if (item.type === 0 && item.asset) {
return `${CDN_BASE}/avatar-decoration-presets/${item.asset}.png?passthrough=false`;
}
if (item.type === 2 && item.asset) {
const assetPath = String(item.asset).replace(/^\/+/, '');
return `${CDN_BASE}/assets/collectibles/${assetPath}static.png`;
}
const assets = urlsForItem(item);
return assets[0]?.url;
}
function* walkProduct(product, category) {
yield { product, category };
for (const bundled of product.bundled_products ?? []) {
yield* walkProduct(bundled, category);
}
for (const variant of product.variants ?? []) {
yield* walkProduct(variant, category);
}
}
function* walkProducts(categories) {
for (const category of categories ?? []) {
for (const product of category.products ?? []) {
yield* walkProduct(product, category);
}
}
}
function extractCollectibleItems(categories) {
const items = [];
const seenItems = new Set();
for (const { product, category } of walkProducts(categories)) {
const categoryName = category?.name || 'Unknown';
for (const item of product.items ?? []) {
if (![0, 1, 2].includes(item.type)) continue;
const skuId = String(item.sku_id ?? item.id ?? product.sku_id ?? 'unknown');
const itemKey = `${item.type}:${skuId}:${item.id ?? ''}`;
if (seenItems.has(itemKey)) continue;
seenItems.add(itemKey);
const assets = urlsForItem(item);
if (assets.length === 0) continue;
const productName = product.name || item.title || item.label || skuId;
const typeKey = ITEM_TYPE_KEY[item.type];
const entry = {
id: `${typeKey}:${skuId}`,
skuId,
name: productName,
type: typeKey,
category: categoryName,
label: item.label ?? item.title ?? productName,
thumbnailUrl: thumbnailForItem(item),
previewAspectRatio: previewAspectRatioForItem(item),
previewColors: product.styles?.background_colors,
palette: item.type === 2 ? item.palette : undefined,
paletteLabel: item.type === 2 ? formatPaletteName(item.palette) : undefined,
previewGradient: item.type === 2 ? nameplatePaletteGradient(item.palette) : undefined,
assets: assets.map((a) => ({
role: a.role,
url: a.url,
filename: a.filename,
mimeType: guessMimeType(a.filename),
})),
};
if (item.type === 1) {
entry.effect = {
animationType: item.animationType,
thumbnailPreviewSrc: item.thumbnailPreviewSrc,
reducedMotionSrc: item.reducedMotionSrc,
effects: item.effects,
};
}
items.push(entry);
}
}
return items;
}
function isPublishedCatalog(document) {
return (
document &&
document.schema_version === 1 &&
typeof document.type === 'string' &&
Array.isArray(document.items) &&
document.items.every(
(item) =>
item &&
typeof item.id === 'string' &&
typeof item.skuId === 'string' &&
typeof item.type === 'string' &&
Array.isArray(item.assets)
)
);
}
async function fetchPublishedCatalog() {
const documents = await Promise.all(
PUBLISHED_CATALOG_URLS.map(async (url) => {
const response = await fetch(url, { headers: { Accept: 'application/json' } });
if (!response.ok) {
throw new Error(`Published collectibles catalog request failed with HTTP ${response.status}.`);
}
const document = await response.json();
if (!isPublishedCatalog(document)) {
throw new Error('Published collectibles catalog has an unsupported schema.');
}
return document;
})
);
const items = documents.flatMap((document) => document.items);
if (items.length === 0) {
throw new Error('Published collectibles catalog is empty.');
}
return items;
}
async function downloadAsset(url, { retries = 4 } = {}) {
for (let attempt = 0; ; attempt++) {
let res;
try {
res = await fetch(url, {
headers: { 'User-Agent': DEFAULT_USER_AGENT, Accept: '*/*' },
});
} catch (err) {
if (attempt >= retries) throw err;
await sleep(500 * 2 ** attempt);
continue;
}
if (res.status === 429) {
const retryAfter = Number(res.headers.get('retry-after')) || 1;
await sleep((retryAfter + 0.5) * 1000);
continue;
}
if (!res.ok) {
if (attempt >= retries) {
throw new Error(`Failed to download asset: ${res.status} ${res.statusText}`);
}
await sleep(500 * 2 ** attempt);
continue;
}
const buf = Buffer.from(await res.arrayBuffer());
return buf;
}
}
function createDiscordCollectiblesService(store) {
let catalogCache = store.get('discordCollectiblesCatalog') || null;
if (
!catalogCache ||
!Array.isArray(catalogCache.items) ||
typeof catalogCache.fetchedAt !== 'string'
) {
catalogCache = null;
}
let catalogCacheAt = catalogCache?.fetchedAt ? Date.parse(catalogCache.fetchedAt) : 0;
const CATALOG_TTL_MS = 6 * 60 * 60 * 1000;
store.delete('discordCollectiblesToken');
async function getCatalog({ force = false } = {}) {
const now = Date.now();
if (!force && catalogCache && now - catalogCacheAt < CATALOG_TTL_MS) {
return { success: true, data: catalogCache };
}
try {
const items = await fetchPublishedCatalog();
catalogCache = { items, fetchedAt: new Date().toISOString() };
catalogCacheAt = now;
store.set('discordCollectiblesCatalog', catalogCache);
return { success: true, data: catalogCache };
} catch (err) {
if (catalogCache) {
return {
success: true,
data: { ...catalogCache, stale: true },
};
}
return { success: false, error: err instanceof Error ? err.message : String(err) };
}
}
async function downloadAssets(assetUrls) {
const results = [];
for (const asset of assetUrls) {
const buffer = await downloadAsset(asset.url);
results.push({
role: asset.role,
url: asset.url,
filename: asset.filename,
mimeType: asset.mimeType || guessMimeType(asset.filename),
data: buffer,
});
}
return results;
}
return {
getCatalog,
downloadAssets,
ITEM_TYPE_FOLDER,
};
}
module.exports = { createDiscordCollectiblesService, extractCollectibleItems, urlsForItem };

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,8 @@ const commandMap = {
'check_for_updates': 'check-for-updates',
'download_update': 'download-update',
'install_update': 'install-update',
'get_desktop_sources': 'get-desktop-sources'
'get_desktop_sources': 'get-desktop-sources',
'get_protocol_status': 'protocol:get-status'
};
// Shared invoke function for legacy API compatibility
@@ -103,6 +104,12 @@ contextBridge.exposeInMainWorld('electron', {
readImage: () => ipcRenderer.invoke('read-clipboard-image'),
writeText: (text) => ipcRenderer.invoke('write-clipboard-text', text)
},
// Media save (native Save dialog for images / videos / files)
media: {
saveFile: ({ filename, mimeType, data }) =>
ipcRenderer.invoke('media:save-file', { filename, mimeType, data }),
},
// Audio
audio: {
playNotificationSound: (soundType = 'message') => ipcRenderer.invoke('play-notification-sound', soundType),
@@ -129,6 +136,7 @@ contextBridge.exposeInMainWorld('electron', {
updater: {
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
downloadAndInstallUpdate: () => ipcRenderer.invoke('download-and-install-update'),
isMock: () => ipcRenderer.invoke('updater-is-mock'),
onUpdateAvailable: (callback) => {
ipcRenderer.on('update-available', (_, info) => callback(info));
},
@@ -138,6 +146,9 @@ contextBridge.exposeInMainWorld('electron', {
onUpdateDownloaded: (callback) => {
ipcRenderer.on('update-downloaded', (_, info) => callback(info));
},
onUpdateNotAvailable: (callback) => {
ipcRenderer.on('update-not-available', (_, info) => callback(info));
},
downloadUpdate: () => ipcRenderer.invoke('download-update'),
installUpdate: () => ipcRenderer.invoke('install-update'),
},
@@ -176,7 +187,19 @@ contextBridge.exposeInMainWorld('electron', {
list: () => ipcRenderer.invoke('plugin:list'),
uninstall: (pluginId) => ipcRenderer.invoke('plugin:uninstall', pluginId),
readPluginCode: (pluginId) => ipcRenderer.invoke('plugin:read-code', pluginId)
}
},
// Desktop protocol diagnostics
protocol: {
getStatus: () => ipcRenderer.invoke('protocol:get-status'),
repair: () => ipcRenderer.invoke('protocol:repair')
},
// Discord shop collectibles (published catalog + on-demand CDN download)
discordCollectibles: {
fetchCatalog: (force = false) => ipcRenderer.invoke('discord-collectibles:fetch-catalog', { force }),
downloadAssets: (assets) => ipcRenderer.invoke('discord-collectibles:download-assets', { assets }),
},
});
// Preload script ready

View File

@@ -208,6 +208,81 @@
{
"name": "Messaging",
"item": [
{
"name": "Get Group Messages",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/messages/groups?limit=10",
"host": [
"{{baseUrl}}"
],
"path": [
"messages",
"groups"
],
"query": [
{
"key": "limit",
"value": "10",
"description": "Number of recent messages (1-100, default 10)"
}
]
},
"description": "Get the most recent messages across joined group channels (non-DMs)."
}
},
{
"name": "Get DM Messages",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/messages/dms?limit=10",
"host": [
"{{baseUrl}}"
],
"path": [
"messages",
"dms"
],
"query": [
{
"key": "limit",
"value": "10",
"description": "Number of recent messages (1-100, default 10)"
}
]
},
"description": "Get the most recent messages across joined direct messages."
}
},
{
"name": "Get Combined Messages",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/messages/combined?limit=10",
"host": [
"{{baseUrl}}"
],
"path": [
"messages",
"combined"
],
"query": [
{
"key": "limit",
"value": "10",
"description": "Number of recent messages (1-100, default 10)"
}
]
},
"description": "Get the most recent messages across groups and DMs together."
}
},
{
"name": "Send Message to Room",
"request": {

3548
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "paarrot",
"version": "4.11.111",
"version": "4.11.191",
"description": "Paarrot - A Matrix client based on Cinny",
"homepage": "https://github.com/Paarrot/Paarrot-Desktop",
"repository": {
@@ -13,13 +13,15 @@
},
"scripts": {
"dev": "concurrently \"npm run dev:vite\" \"npm run dev:electron\"",
"dev:vite": "cd cinny && npm start",
"dev:vite": "cross-env BROWSER=none sh -c 'cd cinny && npm start'",
"dev:electron": "wait-on http://localhost:38347 && cross-env NODE_ENV=development electron .",
"build": "node -e \"require('fs').copyFileSync('config.json', 'cinny/config.json')\" && cd cinny && npm run build && cd .. && electron-builder --publish always",
"build:local": "node -e \"require('fs').copyFileSync('config.json', 'cinny/config.json')\" && cd cinny && npm run build && cd .. && electron-builder",
"build": "node scripts/generate-update-manifest.mjs && node -e \"require('fs').copyFileSync('config.json', 'cinny/config.json')\" && cd cinny && npm run build && cd .. && electron-builder --publish always",
"build:local": "node scripts/generate-update-manifest.mjs && node -e \"require('fs').copyFileSync('config.json', 'cinny/config.json')\" && cd cinny && npm run build && cd .. && electron-builder",
"build:linux": "npm run build -- --linux",
"build:win": "npm run build -- --win",
"postman:generate": "node scripts/generate-postman-collection.js"
"postman:generate": "node scripts/generate-postman-collection.js",
"generate:update-manifest": "node scripts/generate-update-manifest.mjs",
"playground": "npm --prefix cinny run playground"
},
"keywords": [],
"author": {
@@ -28,24 +30,28 @@
},
"license": "AGPL-3.0-only",
"dependencies": {
"adm-zip": "^0.5.16",
"body-parser": "2.2.2",
"adm-zip": "^0.5.18",
"body-parser": "2.3.0",
"cors": "2.8.6",
"electron-store": "^8.2.0",
"electron-updater": "^6.3.9",
"electron-store": "^11.0.2",
"electron-updater": "^6.8.9",
"express": "5.2.1",
"open": "11.0.0"
},
"devDependencies": {
"@actions/github": "6.0.0",
"@electron/rebuild": "4.0.3",
"concurrently": "9.2.1",
"@actions/github": "9.1.1",
"@electron/rebuild": "4.2.0",
"concurrently": "10.0.3",
"cross-env": "10.1.0",
"electron": "40.6.0",
"electron-builder": "26.8.1",
"electron": "43.1.0",
"electron-builder": "26.15.3",
"node-fetch": "3.3.2",
"png2icons": "2.0.1",
"sharp": "0.34.5",
"wait-on": "9.0.4"
"sharp": "0.35.3",
"wait-on": "9.0.10"
},
"allowScripts": {
"electron-winstaller@5.4.0": true,
"electron@43.1.0": true
}
}

View File

@@ -164,6 +164,87 @@ const collection = {
{
name: 'Messaging',
item: [
{
name: 'Get Group Messages',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/messages/groups?limit=10',
host: ['{{baseUrl}}'],
path: ['messages', 'groups'],
query: [{ key: 'limit', value: '10', description: 'Number of recent messages (1-100, default 10)' }],
},
description: 'Get the most recent messages across joined group channels (non-DMs).',
},
},
{
name: 'Get DM Messages',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/messages/dms?limit=10',
host: ['{{baseUrl}}'],
path: ['messages', 'dms'],
query: [{ key: 'limit', value: '10', description: 'Number of recent messages (1-100, default 10)' }],
},
description: 'Get the most recent messages across joined direct messages.',
},
},
{
name: 'Get Combined Messages',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/messages/combined?limit=10',
host: ['{{baseUrl}}'],
path: ['messages', 'combined'],
query: [{ key: 'limit', value: '10', description: 'Number of recent messages (1-100, default 10)' }],
},
description: 'Get the most recent messages across groups and DMs together.',
},
},
{
name: 'Get Group Unreads',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/unreads/groups',
host: ['{{baseUrl}}'],
path: ['unreads', 'groups'],
},
description: 'List unread group conversations (non-DMs), including counts and latest message preview.',
},
},
{
name: 'Get DM Unreads',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/unreads/dms',
host: ['{{baseUrl}}'],
path: ['unreads', 'dms'],
},
description: 'List unread DM conversations, including counts and latest message preview.',
},
},
{
name: 'Get Combined Unreads',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/unreads/combined',
host: ['{{baseUrl}}'],
path: ['unreads', 'combined'],
},
description: 'List unread conversations across groups and DMs.',
},
},
{
name: 'Send Message to Room',
request: {

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env node
/**
* Desktop repo wrapper — manifest generator lives in cinny/scripts/.
*/
import { spawnSync } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';
const script = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'..',
'cinny',
'scripts',
'generate-update-manifest.mjs'
);
const result = spawnSync(process.execPath, [script], { stdio: 'inherit' });
process.exit(result.status ?? 1);

104
scripts/migrate-icons.mjs Normal file
View File

@@ -0,0 +1,104 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const srcRoot = path.resolve(__dirname, '../cinny/src');
const iconsModule = path.resolve(srcRoot, 'app/components/icons');
const ICON_SYMBOLS = new Set(['Icon', 'Icons', 'IconName', 'IconSrc']);
function walk(dir, files = []) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
walk(fullPath, files);
} else if (/\.(tsx?)$/.test(entry.name)) {
files.push(fullPath);
}
}
return files;
}
function getIconsImportPath(filePath) {
const rel = path.relative(path.dirname(filePath), iconsModule).replace(/\\/g, '/');
return rel.startsWith('.') ? rel : `./${rel}`;
}
function splitFoldsImport(importClause) {
const match = importClause.match(/^\{([^}]+)\}(?:\s+as\s+\w+)?$/);
if (!match) return null;
const specifiers = match[1]
.split(',')
.map((part) => part.trim())
.filter(Boolean)
.map((part) => {
const [local, imported] = part.split(/\s+as\s+/).map((s) => s.trim());
return { local: local ?? imported, imported: imported ?? local };
});
const iconSpecs = specifiers.filter((s) => ICON_SYMBOLS.has(s.imported));
const foldsSpecs = specifiers.filter((s) => !ICON_SYMBOLS.has(s.imported));
if (iconSpecs.length === 0) return null;
return { iconSpecs, foldsSpecs };
}
function migrateFile(filePath) {
let content = fs.readFileSync(filePath, 'utf8');
const importRegex = /import\s+(\{[^}]+\})\s+from\s+['"]folds['"];?/g;
let changed = false;
const iconImports = new Map();
content = content.replace(importRegex, (fullMatch, importClause) => {
const split = splitFoldsImport(importClause);
if (!split) return fullMatch;
changed = true;
for (const spec of split.iconSpecs) {
iconImports.set(spec.local, spec.imported);
}
if (split.foldsSpecs.length === 0) {
return '';
}
const foldsImport = split.foldsSpecs.map((s) => (s.local === s.imported ? s.local : `${s.imported} as ${s.local}`)).join(', ');
return `import { ${foldsImport} } from 'folds';`;
});
if (!changed) return false;
const iconsPath = getIconsImportPath(filePath);
const iconExportList = [...iconImports.entries()]
.map(([local, imported]) => (local === imported ? imported : `${imported} as ${local}`))
.join(', ');
const iconsImportLine = `import { ${iconExportList} } from '${iconsPath}';`;
const foldsImportMatch = content.match(/^import\s+\{[^}]+\}\s+from\s+['"]folds['"];?\s*$/m);
if (foldsImportMatch) {
const insertAt = content.indexOf(foldsImportMatch[0]) + foldsImportMatch[0].length;
content = `${content.slice(0, insertAt)}\n${iconsImportLine}${content.slice(insertAt)}`;
} else {
content = `${iconsImportLine}\n${content}`;
}
content = content.replace(/\n{3,}/g, '\n\n');
fs.writeFileSync(filePath, content);
return true;
}
const files = walk(srcRoot);
let migrated = 0;
for (const file of files) {
if (file.includes(`${path.sep}components${path.sep}icons${path.sep}`)) continue;
if (migrateFile(file)) {
migrated += 1;
}
}
console.log(`Migrated ${migrated} files.`);

View File

@@ -18,6 +18,17 @@
"updater:allow-download",
"updater:allow-download-and-install",
"dialog:default",
"fs:default",
"fs:allow-write-file",
{
"identifier": "fs:scope",
"allow": [
{ "path": "$HOME/**" },
{ "path": "$DOWNLOAD/**" },
{ "path": "$DESKTOP/**" },
{ "path": "$DOCUMENT/**" }
]
},
"process:allow-restart",
{
"identifier": "http:default",

View File

@@ -60,6 +60,16 @@ async function runTests() {
// Get current room
await testEndpoint('Get Current Room', 'GET', '/room/current');
// Recent messages by scope
await testEndpoint('Get Group Messages', 'GET', '/messages/groups');
await testEndpoint('Get DM Messages', 'GET', '/messages/dms');
await testEndpoint('Get Combined Messages', 'GET', '/messages/combined?limit=10');
// Unread conversations by scope
await testEndpoint('Get Group Unreads', 'GET', '/unreads/groups');
await testEndpoint('Get DM Unreads', 'GET', '/unreads/dms');
await testEndpoint('Get Combined Unreads', 'GET', '/unreads/combined');
// Toggle mute
await testEndpoint('Toggle Mute', 'POST', '/mute/toggle');