Compare commits

370 Commits

Author SHA1 Message Date
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
GitHub Actions
aafda0d437 chore: bump version to 4.11.111 [skip ci] 2026-05-12 19:55:17 +00:00
f97a8d4929 fix: correct formatting in local development setup instructions
All checks were successful
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Successful in 1m59s
Build / build-windows (push) Successful in 5m6s
Build / create-release (push) Successful in 1m20s
2026-05-13 05:55:01 +10:00
GitHub Actions
a8c742376f chore: bump version to 4.11.110 [skip ci] 2026-05-12 19:47:30 +00:00
938ab53d4b fix: mark subproject as dirty to indicate uncommitted changes
Some checks failed
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Successful in 1m59s
Build / build-windows (push) Successful in 6m51s
Build / create-release (push) Has been cancelled
2026-05-13 05:47:12 +10:00
GitHub Actions
62352412d3 chore: bump version to 4.11.109 [skip ci] 2026-05-12 19:17:41 +00:00
fd33b3f6b7 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 6m46s
Build / create-release (push) Successful in 1m20s
2026-05-13 05:17:28 +10:00
3c0b4cf108 fix: mark subproject as dirty to indicate uncommitted changes 2026-05-13 05:17:27 +10:00
GitHub Actions
4eb48726ae chore: bump version to 4.11.108 [skip ci] 2026-05-12 18:59:32 +00:00
ba17993001 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 1m58s
Build / build-windows (push) Successful in 6m36s
Build / create-release (push) Successful in 1m28s
2026-05-13 04:59:19 +10:00
cf39f0acd9 fix: update npm install to npm ci for better dependency management and add differentialPackage option 2026-05-13 04:59:18 +10:00
GitHub Actions
bce2cbec94 chore: bump version to 4.11.107 [skip ci] 2026-05-12 18:33:09 +00:00
f401b5ea3d 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 4m54s
Build / create-release (push) Successful in 1m31s
2026-05-13 04:32:55 +10:00
4a5e3a1691 feat: add cleanup for old releases in build workflow 2026-05-13 04:32:54 +10:00
GitHub Actions
dc50f74971 chore: bump version to 4.11.106 [skip ci] 2026-05-12 18:23:18 +00:00
a198eb6896 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 2m0s
Build / build-windows (push) Successful in 4m13s
Build / create-release (push) Successful in 1m9s
2026-05-13 04:23:04 +10:00
dc9b12dd26 refactor: update build workflow to use NSIS installer and clean up artifact uploads 2026-05-13 04:23:03 +10:00
GitHub Actions
6ea0e7a4fd chore: bump version to 4.11.105 [skip ci] 2026-05-12 18:15:36 +00:00
51b13e0fcd autoupdate
Some checks failed
Build / increment-version (push) Successful in 11s
Build / build-linux (push) Successful in 2m5s
Build / build-windows (push) Failing after 4m44s
Build / create-release (push) Successful in 44s
2026-05-13 04:15:20 +10:00
GitHub Actions
1d7a48c57e chore: bump version to 4.11.104 [skip ci] 2026-05-12 17:38:19 +00:00
0e34ca2063 fix: update .gitignore to include dist-electron and update subproject commit
All checks were successful
Build / increment-version (push) Successful in 35s
Build / build-linux (push) Successful in 2m1s
Build / build-windows (push) Successful in 4m53s
Build / create-release (push) Successful in 2m12s
2026-05-13 03:37:38 +10:00
GitHub Actions
6248d5843a chore: bump version to 4.11.103 [skip ci] 2026-04-30 10:23:58 +00:00
5af79606c2 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 7m6s
Build / create-release (push) Successful in 1m49s
2026-04-30 20:23:48 +10:00
3cc31d8e05 fix: mark subproject as dirty in cinny 2026-04-30 20:23:45 +10:00
GitHub Actions
0f7dd5f55c chore: bump version to 4.11.102 [skip ci] 2026-04-30 08:39:43 +00:00
de4f6ebbc9 feat: enable GPU acceleration for improved performance
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m10s
Build / build-windows (push) Successful in 12m36s
Build / create-release (push) Successful in 1m48s
2026-04-30 18:39:30 +10:00
GitHub Actions
fd25143489 chore: bump version to 4.11.101 [skip ci] 2026-04-28 12:52:06 +00:00
5265674a58 fix: mark subproject as dirty in cinny
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m10s
Build / build-windows (push) Successful in 8m1s
Build / create-release (push) Successful in 1m50s
2026-04-28 22:51:51 +10:00
GitHub Actions
39376e0003 chore: bump version to 4.11.100 [skip ci] 2026-04-23 22:39:04 +00:00
cf1f7fcec8 fix: update subproject commit reference in cinny
Some checks failed
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Failing after 12s
Build / build-windows (push) Failing after 2m15s
Build / create-release (push) Has been skipped
2026-04-24 08:38:37 +10:00
GitHub Actions
153e55705a chore: bump version to 4.11.99 [skip ci] 2026-04-23 04:53:15 +00:00
77cdd66027 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m4s
Build / build-windows (push) Successful in 6m21s
Build / create-release (push) Successful in 1m48s
2026-04-23 14:53:05 +10:00
80580391cb fix: mark subproject as dirty in cinny 2026-04-23 14:53:03 +10:00
GitHub Actions
6f0988f873 chore: bump version to 4.11.98 [skip ci] 2026-04-22 20:57:34 +00:00
99358803d2 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m1s
Build / build-windows (push) Successful in 3m52s
Build / create-release (push) Successful in 1m48s
2026-04-23 06:57:24 +10:00
6a347d977f feat: add Squirrel target for Windows installer 2026-04-23 06:57:23 +10:00
GitHub Actions
c03a4d3f41 chore: bump version to 4.11.97 [skip ci] 2026-04-22 20:34:08 +00:00
50818f1d76 fix: mark subproject as dirty in cinny
Some checks failed
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m1s
Build / build-windows (push) Failing after 4m5s
Build / create-release (push) Successful in 42s
2026-04-23 06:33:56 +10:00
GitHub Actions
0f8c9b83f4 chore: bump version to 4.11.96 [skip ci] 2026-04-22 20:13:56 +00:00
76cae46005 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m5s
Build / build-windows (push) Failing after 4m48s
Build / create-release (push) Successful in 42s
2026-04-23 06:13:45 +10:00
ca22449d54 feat: update Windows installer target to NSIS and configure shortcuts 2026-04-23 06:13:42 +10:00
GitHub Actions
dda1041268 chore: bump version to 4.11.95 [skip ci] 2026-04-22 19:28:48 +00:00
b4f8f13213 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 5m32s
Build / create-release (push) Successful in 1m50s
2026-04-23 05:28:38 +10:00
9ad9971a03 update 2026-04-23 05:28:31 +10:00
c38dc7bbf6 feat: enhance plugin download handling with validation and extraction improvements 2026-04-23 05:11:10 +10:00
GitHub Actions
aaed71e37b chore: bump version to 4.11.94 [skip ci] 2026-04-22 18:41:45 +00:00
ed85051480 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m8s
Build / build-windows (push) Successful in 7m1s
Build / create-release (push) Successful in 1m50s
2026-04-23 04:41:34 +10:00
d0524c2bc7 refactor: move development mode detection to the top of main.js 2026-04-23 04:41:33 +10:00
GitHub Actions
8a0f8f5422 chore: bump version to 4.11.93 [skip ci] 2026-04-21 17:54:02 +00:00
8a95d9e67d Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m8s
Build / build-windows (push) Successful in 5m12s
Build / create-release (push) Successful in 1m50s
2026-04-22 03:53:51 +10:00
b8a126fd69 feat: enhance build process and add local build scripts for Windows and Linux 2026-04-22 03:53:50 +10:00
GitHub Actions
6b18e5c8ee chore: bump version to 4.11.92 [skip ci] 2026-04-21 17:41:52 +00:00
27c2ce021d Update README.md
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m29s
Build / build-windows (push) Successful in 6m4s
Build / create-release (push) Successful in 52s
2026-04-22 03:41:42 +10:00
GitHub Actions
de6620245c chore: bump version to 4.11.91 [skip ci] 2026-04-21 17:28:56 +00:00
21e895efff Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 9s
Build / build-linux (push) Successful in 2m29s
Build / build-windows (push) Successful in 6m6s
Build / create-release (push) Successful in 1m2s
2026-04-22 03:28:46 +10:00
e9487c3043 fix: mark subproject as dirty and clean up config.json 2026-04-22 03:28:43 +10:00
GitHub Actions
686749ac55 chore: bump version to 4.11.90 [skip ci] 2026-04-21 17:25:32 +00:00
dd790d3336 fix: update homepage URL and configure auto-updater in main process
Some checks failed
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m32s
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-04-22 03:25:21 +10:00
GitHub Actions
c8ceeccc8c chore: bump version to 4.11.89 [skip ci] 2026-04-21 16:47:35 +00:00
a592766e0a Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m34s
Build / build-windows (push) Successful in 7m1s
Build / create-release (push) Successful in 54s
2026-04-22 02:47:24 +10:00
7acf8ea877 fix: mark subproject as dirty in cinny 2026-04-22 02:47:22 +10:00
GitHub Actions
2a87133d14 chore: bump version to 4.11.88 [skip ci] 2026-04-21 14:54:32 +00:00
c960bc5410 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m30s
Build / build-windows (push) Successful in 6m27s
Build / create-release (push) Successful in 53s
2026-04-22 00:54:20 +10:00
fde412fafa feat: add app.asar file for Electron packaging 2026-04-22 00:54:18 +10:00
GitHub Actions
476d0c71fc chore: bump version to 4.11.87 [skip ci] 2026-04-21 14:40:13 +00:00
df6fa0fa42 moved docs
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m24s
Build / build-windows (push) Successful in 6m13s
Build / create-release (push) Successful in 51s
2026-04-22 00:39:43 +10:00
GitHub Actions
246521b7b3 chore: bump version to 4.11.86 [skip ci] 2026-04-21 14:29:25 +00:00
8ccc3ecb78 feat: Add Plugin Button Registration API documentation and Plugin System implementation summary
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m29s
Build / build-windows (push) Successful in 6m50s
Build / create-release (push) Successful in 52s
- Introduced PLUGIN_BUTTON_API.md detailing button registration, UI locations, and usage examples.
- Added PLUGIN_SYSTEM_IMPLEMENTATION.md summarizing implemented features, usage examples, and API details for the plugin system.
2026-04-22 00:29:14 +10:00
GitHub Actions
19ce4b43ca chore: bump version to 4.11.85 [skip ci] 2026-04-21 11:36:02 +00:00
8ea4c58a5b Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m29s
Build / build-windows (push) Successful in 6m38s
Build / create-release (push) Successful in 50s
2026-04-21 21:35:52 +10:00
465fa6b57b fix: mark subproject as dirty 2026-04-21 21:35:48 +10:00
GitHub Actions
12f0d7f8d3 chore: bump version to 4.11.84 [skip ci] 2026-04-20 21:57:39 +00:00
f11078bb5c Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m28s
Build / build-windows (push) Successful in 6m10s
Build / create-release (push) Successful in 50s
2026-04-21 07:57:31 +10:00
4e6835c741 fix: mark subproject as dirty 2026-04-21 07:57:30 +10:00
GitHub Actions
4584367041 chore: bump version to 4.11.83 [skip ci] 2026-04-20 21:25:21 +00:00
bf71bd9726 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m28s
Build / build-windows (push) Successful in 6m58s
Build / create-release (push) Successful in 50s
2026-04-21 07:25:14 +10:00
8cfc96688e fix: mark subproject as dirty 2026-04-21 07:25:11 +10:00
GitHub Actions
eedd040203 chore: bump version to 4.11.82 [skip ci] 2026-04-20 12:23:57 +00:00
8396834b08 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m29s
Build / build-windows (push) Successful in 5m27s
Build / create-release (push) Successful in 53s
2026-04-20 22:23:49 +10:00
cb136002b9 fix: update subproject commit reference 2026-04-20 22:23:46 +10:00
GitHub Actions
9a6dc126db chore: bump version to 4.11.81 [skip ci] 2026-04-20 11:16:00 +00:00
c9352327eb Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m27s
Build / build-windows (push) Successful in 5m7s
Build / create-release (push) Successful in 50s
2026-04-20 21:15:52 +10:00
9985b0931c fix: update subproject commit reference 2026-04-20 21:15:48 +10:00
GitHub Actions
5faf0056d7 chore: bump version to 4.11.80 [skip ci] 2026-04-20 11:09:35 +00:00
cf6bedf58a Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m28s
Build / build-windows (push) Has started running
Build / create-release (push) Has been cancelled
2026-04-20 21:09:26 +10:00
03ecdbb54f fix: mark subproject commit as dirty 2026-04-20 21:09:23 +10:00
GitHub Actions
1222651d17 chore: bump version to 4.11.79 [skip ci] 2026-04-18 21:57:25 +00:00
1f34f726bf Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 3m3s
Build / build-windows (push) Successful in 6m12s
Build / create-release (push) Successful in 50s
2026-04-19 07:57:17 +10:00
5b1bd1cffa fix: update subproject commit reference 2026-04-19 07:57:16 +10:00
050352fd2b fix: update subproject commit reference 2026-04-19 07:55:23 +10:00
GitHub Actions
422b680b18 chore: bump version to 4.11.78 [skip ci] 2026-04-18 21:37:31 +00:00
6510522b6b Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m41s
Build / build-windows (push) Successful in 6m9s
Build / create-release (push) Successful in 49s
2026-04-19 07:37:23 +10:00
48a189ed6a fix: update subproject commit reference 2026-04-19 07:37:09 +10:00
GitHub Actions
a72758d9ef chore: bump version to 4.11.77 [skip ci] 2026-04-18 21:00:39 +00:00
7ee178f6e9 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m40s
Build / build-windows (push) Successful in 5m59s
Build / create-release (push) Successful in 52s
2026-04-19 07:00:29 +10:00
50885a9da4 fix: update subproject commit reference 2026-04-19 07:00:28 +10:00
GitHub Actions
5d39692e41 chore: bump version to 4.11.76 [skip ci] 2026-04-18 20:58:30 +00:00
de87a55582 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Failing after 25s
Build / build-windows (push) Failing after 40s
Build / create-release (push) Has been skipped
2026-04-19 06:58:22 +10:00
a3c2510830 fix: mark subproject as dirty to indicate uncommitted changes 2026-04-19 06:58:21 +10:00
GitHub Actions
6b9a57e243 chore: bump version to 4.11.75 [skip ci] 2026-04-18 20:52:55 +00:00
4fa0ce0d4a Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Failing after 51s
Build / build-windows (push) Failing after 2m26s
Build / create-release (push) Has been skipped
2026-04-19 06:52:47 +10:00
e90b8d43cb Refactor code structure for improved readability and maintainability 2026-04-19 06:52:43 +10:00
GitHub Actions
379a957a40 chore: bump version to 4.11.74 [skip ci] 2026-04-17 17:56:57 +00:00
fc18b0b902 yep
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m33s
Build / build-windows (push) Successful in 14m15s
Build / create-release (push) Successful in 51s
2026-04-18 03:56:24 +10:00
a4429fe9c2 feat: Add comprehensive plugin API documentation and example plugin
- Introduced PLUGIN_API.md for detailed plugin development guidelines.
- Implemented PLUGIN_SYSTEM_IMPLEMENTATION.md summarizing all requested features.
- Created example-plugin showcasing all API features including commands, message interceptors, settings, and notifications.
- Added plugin-metadata.json for the example plugin with relevant metadata.
2026-04-18 02:55:38 +10:00
GitHub Actions
fc4229cea7 chore: bump version to 4.11.73 [skip ci] 2026-04-17 09:05:05 +00:00
69fcfd12f3 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m25s
Build / build-windows (push) Successful in 6m10s
Build / create-release (push) Successful in 50s
2026-04-17 19:04:55 +10:00
843b00166e fix: update GitHub token secret reference in build workflow 2026-04-17 19:04:54 +10:00
GitHub Actions
4dbc8c93d1 chore: bump version to 4.11.72 [skip ci] 2026-04-17 09:04:52 +00:00
a196d2706f Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 6s
Build / create-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-04-17 19:04:42 +10:00
643c8a7291 fix: update GitHub token secret reference in release creation step 2026-04-17 19:04:41 +10:00
GitHub Actions
e503829d3a chore: bump version to 4.11.71 [skip ci] 2026-04-17 08:55:46 +00:00
78fce9b5b0 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Failing after 1m49s
Build / build-windows (push) Failing after 6m26s
Build / create-release (push) Has been skipped
2026-04-17 18:55:38 +10:00
42f21c274f chore: update build workflow to create GitHub releases and modify publish settings 2026-04-17 18:55:35 +10:00
GitHub Actions
d39d006296 chore: bump version to 4.11.70 [skip ci] 2026-04-12 06:26:26 +00:00
8e8b5a732c Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m8s
Build / build-windows (push) Successful in 4m38s
Build / create-release (push) Successful in 17s
Build Paarrot Windows / start-vm (push) Failing after 5m34s
Build Paarrot Windows / build (push) Has been skipped
2026-04-12 16:26:15 +10:00
edd2c457ed chore: update subproject commit and clean up electron-builder configuration 2026-04-12 16:26:14 +10:00
GitHub Actions
097f45d8ca chore: bump version to 4.11.69 [skip ci] 2026-04-09 15:36:30 +00:00
53422c8718 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m5s
Build Paarrot Windows / start-vm (push) Failing after 5m33s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 7m43s
Build / create-release (push) Successful in 17s
2026-04-10 01:36:19 +10:00
89ecc91acd chore: mark subproject as dirty 2026-04-10 01:36:17 +10:00
GitHub Actions
abd22d4875 chore: bump version to 4.11.68 [skip ci] 2026-04-08 16:20:33 +00:00
ccb922fbe0 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build Paarrot Windows / build (push) Has been cancelled
Build Paarrot Windows / start-vm (push) Has been cancelled
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m7s
Build / build-windows (push) Successful in 6m6s
Build / create-release (push) Successful in 18s
2026-04-09 02:15:28 +10:00
b80128f029 chore: mark subproject as dirty 2026-04-09 02:15:27 +10:00
GitHub Actions
c737edfa43 chore: bump version to 4.11.67 [skip ci] 2026-04-07 06:44:40 +00:00
735511fad0 chore: update subproject commit reference
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 1m56s
Build Paarrot Windows / start-vm (push) Failing after 5m34s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 4m48s
Build / create-release (push) Successful in 16s
2026-04-07 16:44:30 +10:00
GitHub Actions
d650e34a8f chore: bump version to 4.11.66 [skip ci] 2026-04-03 10:14:13 +00:00
1cb8fd6e49 feat: add sound resource handling and IPC for sound URL retrieval
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 1m56s
Build / build-windows (push) Successful in 5m16s
Build Paarrot Windows / start-vm (push) Failing after 5m33s
Build Paarrot Windows / build (push) Has been skipped
Build / create-release (push) Successful in 17s
2026-04-03 21:14:01 +11:00
GitHub Actions
7857e42c5a chore: bump version to 4.11.65 [skip ci] 2026-04-03 09:47:29 +00:00
7e667a1708 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m0s
Build Paarrot Windows / start-vm (push) Failing after 5m33s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 5m59s
Build / create-release (push) Successful in 18s
2026-04-03 20:47:20 +11:00
42a01c357f feat: enhance window state restoration with screen bounds validation 2026-04-03 20:47:17 +11:00
GitHub Actions
8bd238c793 chore: bump version to 4.11.64 [skip ci] 2026-04-03 05:48:40 +00:00
7b28d2fcba Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 1m55s
Build Paarrot Windows / start-vm (push) Failing after 5m34s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 5m28s
Build / create-release (push) Successful in 17s
2026-04-03 16:48:30 +11:00
362ca62296 feat: add update download handling and installation methods 2026-04-03 16:48:26 +11:00
GitHub Actions
d0bfb72581 chore: bump version to 4.11.63 [skip ci] 2026-04-03 05:35:57 +00:00
c5d011958c chore: mark subproject as dirty
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m0s
Build Paarrot Windows / start-vm (push) Failing after 5m33s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 5m45s
Build / create-release (push) Successful in 18s
2026-04-03 16:35:45 +11:00
GitHub Actions
4b7e9245b6 chore: bump version to 4.11.62 [skip ci] 2026-04-02 11:42:06 +00:00
d906a3bc73 k
Some checks failed
Build / increment-version (push) Successful in 20s
Build / build-linux (push) Failing after 1m6s
Build Paarrot Windows / start-vm (push) Failing after 5m47s
Build Paarrot Windows / build (push) Has been skipped
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-04-02 22:41:36 +11:00
f287257a27 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build Paarrot Windows / build (push) Has been cancelled
Build Paarrot Windows / start-vm (push) Has been cancelled
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / increment-version (push) Has been cancelled
2026-04-02 22:41:23 +11:00
5931d14cae feat: enable autoplay for notification sounds without user gesture 2026-04-02 22:40:38 +11:00
GitHub Actions
2a98dc1f25 chore: bump version to 4.11.61 [skip ci] 2026-03-25 23:24:18 +00:00
46f72b94fa chore: mark subproject as dirty
Some checks failed
Build / increment-version (push) Successful in 30s
Build / build-linux (push) Failing after 52s
Build / build-windows (push) Failing after 2m57s
Build / create-release (push) Has been skipped
Build Paarrot Windows / start-vm (push) Failing after 5m59s
Build Paarrot Windows / build (push) Has been skipped
2026-03-26 10:23:42 +11:00
GitHub Actions
0bcaedda73 chore: bump version to 4.11.60 [skip ci] 2026-03-24 20:17:39 +00:00
2dfef751c0 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Failing after 49s
Build / build-windows (push) Failing after 2m2s
Build / create-release (push) Has been skipped
Build Paarrot Windows / start-vm (push) Failing after 5m34s
Build Paarrot Windows / build (push) Has been skipped
2026-03-25 07:17:30 +11:00
bf786742a5 feat: enhance update installation process for Linux, Windows, and macOS 2026-03-25 07:17:28 +11:00
GitHub Actions
567980feae chore: bump version to 4.11.59 [skip ci] 2026-03-24 19:56:24 +00:00
90a0dad0ec feat: add download and install update functionality in IPC
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Failing after 51s
Build / build-windows (push) Failing after 2m11s
Build / create-release (push) Has been skipped
Build Paarrot Windows / start-vm (push) Failing after 5m34s
Build Paarrot Windows / build (push) Has been skipped
2026-03-25 06:56:13 +11:00
GitHub Actions
abc5ad66be chore: bump version to 4.11.58 [skip ci] 2026-03-24 19:44:38 +00:00
18dae047e9 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 1m57s
Build Paarrot Windows / start-vm (push) Failing after 5m34s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 5m40s
Build / create-release (push) Successful in 19s
2026-03-25 06:44:28 +11:00
3849f52852 fix: mark subproject as dirty in cinny 2026-03-25 06:44:27 +11:00
GitHub Actions
79547c2884 chore: bump version to 4.11.57 [skip ci] 2026-03-24 19:35:51 +00:00
4b5cc29266 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 1m58s
Build Paarrot Windows / start-vm (push) Failing after 5m35s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 5m28s
Build / create-release (push) Successful in 20s
2026-03-25 06:35:42 +11:00
b54a003a96 refactor: remove auto-updater and implement Gitea update handling 2026-03-25 06:35:41 +11:00
GitHub Actions
7563926d8f chore: bump version to 4.11.56 [skip ci] 2026-03-24 19:21:28 +00:00
1fea022306 fix: mark subproject as dirty in cinny
Some checks failed
Build / increment-version (push) Successful in 31s
Build / build-linux (push) Successful in 1m59s
Build Paarrot Windows / start-vm (push) Failing after 5m59s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 6m21s
Build / create-release (push) Successful in 20s
2026-03-25 06:20:50 +11:00
9bfa6e3040 feat: add window flash frame and audio notification sound handling 2026-03-25 06:14:14 +11:00
GitHub Actions
be822877a9 chore: bump version to 4.11.55 [skip ci] 2026-03-23 13:15:12 +00:00
ff13ae817e fix: mark subproject as dirty in cinny
Some checks failed
Build / increment-version (push) Successful in 14s
Build / build-linux (push) Successful in 2m21s
Build Paarrot Windows / start-vm (push) Failing after 5m35s
Build Paarrot Windows / build (push) Has been skipped
Build / build-windows (push) Successful in 5m52s
Build / create-release (push) Successful in 20s
2026-03-24 00:14:54 +11:00
GitHub Actions
a59287669b chore: bump version to 4.11.54 [skip ci] 2026-03-23 11:13:45 +00:00
09cfe7dc84 YT Fixes
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m17s
Build / build-windows (push) Successful in 4m45s
Build / create-release (push) Successful in 20s
Build Paarrot Windows / start-vm (push) Failing after 5m35s
Build Paarrot Windows / build (push) Has been skipped
2026-03-23 22:13:34 +11:00
GitHub Actions
823fd8593b chore: bump version to 4.11.53 [skip ci] 2026-03-23 10:53:40 +00:00
62e53b05ff fix: mark subproject as dirty in cinny
Some checks failed
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m23s
Build / build-windows (push) Successful in 3m52s
Build / create-release (push) Successful in 20s
Build Paarrot Windows / start-vm (push) Failing after 5m35s
Build Paarrot Windows / build (push) Has been skipped
2026-03-23 21:53:28 +11:00
GitHub Actions
aaa9bae16b chore: bump version to 4.11.52 [skip ci] 2026-03-23 09:34:40 +00:00
07feddd44b Add Paarrot Stream Deck Plugin with core functionality
Some checks failed
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m26s
Build Paarrot Windows / build (push) Has been cancelled
Build Paarrot Windows / start-vm (push) Has been cancelled
Build / build-windows (push) Successful in 4m28s
Build / create-release (push) Successful in 30s
- Implemented main plugin structure including manifest, HTML, and JavaScript files.
- Added actions for toggling mute, toggling deafen, changing channels, sending messages, and getting status.
- Created property inspector for changing channels and sending messages.
- Developed SVG icons for actions and status display.
- Established WebSocket connection for communication with the Stream Deck.
- Implemented API calls to interact with the Paarrot voice chat application.
- Added validation script to ensure proper plugin structure and manifest compliance.
- Included test Electron application for window creation and loading content.
2026-03-23 20:34:26 +11:00
GitHub Actions
b71268a38f chore: bump version to 4.11.51 [skip ci] 2026-03-15 12:45:04 +00:00
3904eec992 fix: mark subproject as dirty in cinny
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m26s
Build / build-windows (push) Successful in 5m36s
Build / create-release (push) Successful in 21s
2026-03-15 23:44:55 +11:00
GitHub Actions
5c6d69ba51 chore: bump version to 4.11.50 [skip ci] 2026-03-15 10:53:12 +00:00
6cc20baad6 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 1m59s
Build / build-windows (push) Successful in 5m33s
Build / create-release (push) Successful in 20s
2026-03-15 21:53:04 +11:00
ce0c8277ec fix: mark subproject as dirty in cinny 2026-03-15 21:53:00 +11:00
GitHub Actions
9a3e24f55c chore: bump version to 4.11.49 [skip ci] 2026-03-15 10:51:34 +00:00
d03712a430 fix: mark subproject as dirty in cinny
Some checks failed
Build / increment-version (push) Successful in 7s
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-03-15 21:51:22 +11:00
GitHub Actions
e7d240e2d2 chore: bump version to 4.11.48 [skip ci] 2026-03-15 08:49:39 +00:00
9ec515e351 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 5m5s
Build / create-release (push) Successful in 20s
2026-03-15 19:49:29 +11:00
4be9d2bc21 fix: mark subproject as dirty in cinny 2026-03-15 19:49:26 +11:00
GitHub Actions
1666724c91 chore: bump version to 4.11.47 [skip ci] 2026-03-15 08:23:34 +00:00
5d2e91befa fix: mark subproject as dirty in cinny
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m8s
Build / build-windows (push) Successful in 5m51s
Build / create-release (push) Successful in 19s
2026-03-15 19:23:24 +11:00
GitHub Actions
03a12320a1 chore: bump version to 4.11.46 [skip ci] 2026-03-15 06:25:05 +00:00
94fcc02138 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 5m2s
Build / create-release (push) Successful in 19s
2026-03-15 17:24:57 +11:00
855c3c45ac feat: add video title fetching from YouTube oEmbed API in YouTubeEmbed component 2026-03-15 17:24:56 +11:00
GitHub Actions
280582548c chore: bump version to 4.11.45 [skip ci] 2026-03-15 06:17:21 +00:00
6a2dceaeac Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 5m6s
Build / create-release (push) Successful in 19s
2026-03-15 17:17:13 +11:00
8deec2e7f8 chore: mark subproject commit as dirty 2026-03-15 17:17:09 +11:00
GitHub Actions
3fe7eaf482 chore: bump version to 4.11.44 [skip ci] 2026-03-15 05:26:06 +00:00
4a2352edd4 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m7s
Build / build-windows (push) Successful in 5m21s
Build / create-release (push) Successful in 20s
2026-03-15 16:25:57 +11:00
a87c475c1a chore: mark subproject commit as dirty 2026-03-15 16:25:54 +11:00
GitHub Actions
a575e073e3 chore: bump version to 4.11.43 [skip ci] 2026-03-12 14:48:10 +00:00
571427aa09 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m10s
Build / build-windows (push) Successful in 6m52s
Build / create-release (push) Successful in 20s
2026-03-13 01:48:01 +11:00
1a04f9b503 chore: mark subproject commit as dirty 2026-03-13 01:48:00 +11:00
637d1ab3c4 chore: mark subproject commit as dirty 2026-03-13 01:19:58 +11:00
GitHub Actions
d17df23b29 chore: bump version to 4.11.42 [skip ci] 2026-03-12 13:54:31 +00:00
7675365a82 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m9s
Build / build-windows (push) Successful in 6m26s
Build / create-release (push) Successful in 23s
2026-03-13 00:54:20 +11:00
40691222a1 chore: mark subproject commit as dirty 2026-03-13 00:54:17 +11:00
GitHub Actions
e37ea9c7ad chore: bump version to 4.11.41 [skip ci] 2026-03-12 13:44:46 +00:00
750976ad71 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m13s
Build / build-windows (push) Successful in 6m25s
Build / create-release (push) Successful in 20s
2026-03-13 00:44:37 +11:00
65e3cad890 chore: mark subproject commit as dirty 2026-03-13 00:44:34 +11:00
GitHub Actions
435d83c4cc chore: bump version to 4.11.40 [skip ci] 2026-03-12 12:08:19 +00:00
7af732f24c Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m4s
Build / build-windows (push) Successful in 5m36s
Build / create-release (push) Successful in 20s
2026-03-12 23:08:10 +11:00
c3f78ab76e chore: mark subproject commit as dirty 2026-03-12 23:08:05 +11:00
GitHub Actions
75af85a300 chore: bump version to 4.11.39 [skip ci] 2026-03-12 11:18:17 +00:00
c0a1a7266d Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m6s
Build / build-windows (push) Successful in 5m55s
Build / create-release (push) Successful in 20s
2026-03-12 22:18:09 +11:00
3da53b6ded chore: mark subproject commit as dirty 2026-03-12 22:18:07 +11:00
GitHub Actions
914a8a2ba4 chore: bump version to 4.11.38 [skip ci] 2026-03-12 09:50:16 +00:00
9bde8b088b Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m6s
Build / build-windows (push) Successful in 5m0s
Build / create-release (push) Successful in 20s
2026-03-12 20:50:06 +11:00
58a1545774 chore: mark subproject commit as dirty 2026-03-12 20:50:04 +11:00
GitHub Actions
4cfd30ef79 chore: bump version to 4.11.37 [skip ci] 2026-03-11 23:58:51 +00:00
45aa4c8a1b chore: mark subproject commit as dirty
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 5m59s
Build / create-release (push) Successful in 20s
2026-03-12 10:58:40 +11:00
GitHub Actions
01ebebdb18 chore: bump version to 4.11.36 [skip ci] 2026-03-11 14:27:00 +00:00
2490ec7de5 chore: update subproject commit reference to 2c2560f
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m2s
Build / build-windows (push) Successful in 4m58s
Build / create-release (push) Successful in 20s
2026-03-12 01:26:48 +11:00
GitHub Actions
0d8eba16c6 chore: bump version to 4.11.35 [skip ci] 2026-03-11 14:08:33 +00:00
fb1c4622bd Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 8s
Build / build-linux (push) Successful in 2m7s
Build / build-windows (push) Successful in 5m2s
Build / create-release (push) Successful in 20s
2026-03-12 01:08:22 +11:00
56d155448e multiuser 2026-03-12 01:08:19 +11:00
GitHub Actions
227079ed9d chore: bump version to 4.11.34 [skip ci] 2026-03-08 10:54:18 +00:00
7cb3349da9 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 32s
Build / build-linux (push) Successful in 2m59s
Build / build-windows (push) Successful in 5m37s
Build / create-release (push) Successful in 23s
2026-03-08 21:53:43 +11:00
22378c001e stuff 2026-03-08 21:53:36 +11:00
GitHub Actions
5d074aa17e chore: bump version to 4.11.33 [skip ci] 2026-02-27 13:55:08 +00:00
386cfc7710 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m4s
Build / build-windows (push) Successful in 3m55s
Build / create-release (push) Successful in 20s
2026-02-28 00:54:59 +11:00
1c87bbc6a3 fix: mark subproject as dirty to indicate uncommitted changes 2026-02-28 00:54:58 +11:00
GitHub Actions
8eb0cd9e1c chore: bump version to 4.11.32 [skip ci] 2026-02-27 13:10:49 +00:00
b757e1f553 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m2s
Build / build-windows (push) Successful in 4m1s
Build / create-release (push) Successful in 19s
2026-02-28 00:10:41 +11:00
497923ecb9 fix: mark subproject as dirty to indicate uncommitted changes 2026-02-28 00:10:37 +11:00
GitHub Actions
faad0cca71 chore: bump version to 4.11.31 [skip ci] 2026-02-27 12:05:16 +00:00
e016eb4d5f Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 2m3s
Build / build-windows (push) Successful in 4m3s
Build / create-release (push) Successful in 19s
2026-02-27 23:05:07 +11:00
9cf7113dbe fix: update app user model ID handling for Windows and improve icon path resolution 2026-02-27 23:05:05 +11:00
GitHub Actions
3ba8df423c chore: bump version to 4.11.30 [skip ci] 2026-02-27 11:56:51 +00:00
edcd995b88 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 1m59s
Build / build-windows (push) Successful in 3m54s
Build / create-release (push) Successful in 19s
2026-02-27 22:56:44 +11:00
2e0bf52a40 fix: update app user model ID handling and improve icon loading 2026-02-27 22:56:41 +11:00
GitHub Actions
17ad5f7f51 chore: bump version to 4.11.29 [skip ci] 2026-02-27 11:45:30 +00:00
a1afb05683 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m1s
Build / build-windows (push) Successful in 3m59s
Build / create-release (push) Successful in 19s
2026-02-27 22:45:21 +11:00
90ee0aff90 feat: enhance window focus handling and notification system 2026-02-27 22:45:15 +11:00
GitHub Actions
209d337640 chore: bump version to 4.11.28 [skip ci] 2026-02-27 11:13:10 +00:00
a2e71269e1 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 2m5s
Build / build-windows (push) Successful in 4m48s
Build / create-release (push) Successful in 20s
2026-02-27 22:12:53 +11:00
3493508b8a notifications 2026-02-27 22:12:49 +11:00
GitHub Actions
9fc058954f chore: bump version to 4.11.27 [skip ci] 2026-02-24 11:57:13 +00:00
687b42872c fix: add channel to publish configuration in electron-builder
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Successful in 2m6s
Build / build-windows (push) Successful in 5m40s
Build / create-release (push) Successful in 20s
2026-02-24 22:57:03 +11:00
GitHub Actions
9f090145fe chore: bump version to 4.11.26 [skip ci] 2026-02-24 11:35:30 +00:00
8b43d7546e Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Successful in 1m58s
Build / build-windows (push) Successful in 5m27s
Build / create-release (push) Successful in 20s
2026-02-24 22:35:21 +11:00
66b9dde909 refactor: remove publisherName from Windows build configuration 2026-02-24 22:35:19 +11:00
GitHub Actions
70d66fdf1d chore: bump version to 4.11.25 [skip ci] 2026-02-24 11:32:37 +00:00
54bedf0fcd Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Failing after 1m33s
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-02-24 22:32:27 +11:00
2ef6e225f4 fix: resolve version conflict in package.json 2026-02-24 22:32:11 +11:00
55 changed files with 10798 additions and 3904 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')
needs: [increment-version, prepare-release]
if: always() && needs.prepare-release.result == 'success'
steps:
- name: Checkout repository
@@ -70,7 +214,32 @@ jobs:
- name: Checkout submodules manually
shell: powershell
run: |
$ErrorActionPreference = "Stop"
# Use the same host/scheme the runner already used for origin (not public HTTPS).
$origin = (git remote get-url origin).Trim()
$origin = $origin.TrimEnd('/')
if ($origin.EndsWith('.git')) { $origin = $origin.Substring(0, $origin.Length - 4) }
$base = $origin.Substring(0, $origin.LastIndexOf('/'))
$cinnyUrl = "$base/cinny.git"
Write-Host "Rewriting submodule URL to $cinnyUrl"
git config submodule.cinny.url $cinnyUrl
git submodule sync --recursive
git submodule update --init --recursive
if ($LASTEXITCODE -eq 0) {
Write-Host "Submodules checked out at pinned commits."
exit 0
}
Write-Warning "Pinned submodule commit is unavailable on remote. Falling back to remote submodule HEAD."
git submodule deinit -f --all
git config submodule.cinny.url $cinnyUrl
git submodule sync --recursive
git submodule update --init --recursive --remote
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Write-Host "Resolved submodule commit:"
git -C cinny rev-parse HEAD
- name: Pull latest (after version bump)
if: github.ref == 'refs/heads/main'
@@ -83,11 +252,11 @@ jobs:
node-version: '20'
- name: Install dependencies (root)
run: npm install --prefer-offline
run: npm ci --prefer-offline
- name: Install dependencies (cinny)
working-directory: ./cinny
run: npm install --prefer-offline
run: npm ci --prefer-offline
- name: Clean previous builds
shell: powershell
@@ -96,54 +265,141 @@ jobs:
if (Test-Path dist-electron) { Remove-Item -Recurse -Force dist-electron }
- name: Build Electron app
run: npm run build:win
run: npm run build:local -- --win
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GHTOKEN }}
- name: Upload Squirrel Setup (x64)
uses: actions/upload-artifact@v3
with:
name: Paarrot-Setup-x64.exe
path: dist-electron/squirrel-windows/Paarrot-*-win-x64.exe
- name: Upload Windows assets to releases
shell: powershell
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GHTOKEN }}
run: |
$ErrorActionPreference = "Stop"
$TAG_NAME = "${{ needs.prepare-release.outputs.tag }}"
$API_BASE = "${{ github.server_url }}/api/v1"
$REPO = "${{ github.repository }}"
$GITHUB_REPO = "Paarrot/Paarrot-Desktop"
function Invoke-JsonCurl([string[]]$CurlArgs) {
$raw = & curl.exe @CurlArgs
if ($LASTEXITCODE -ne 0) { throw "curl failed: $($CurlArgs -join ' ')" }
return ($raw | ConvertFrom-Json)
}
$giteaRelease = Invoke-JsonCurl @(
"-fsS", "-H", "Authorization: token $env:GITEA_TOKEN",
"$API_BASE/repos/$REPO/releases/tags/$TAG_NAME"
)
$ghRelease = Invoke-JsonCurl @(
"-fsS", "-H", "Authorization: token $env:GH_TOKEN",
"https://api.github.com/repos/$GITHUB_REPO/releases/tags/$TAG_NAME"
)
# GitHub returns upload_url like .../assets{?name,label}
$ghUploadUrl = [string]$ghRelease.upload_url
if ([string]::IsNullOrWhiteSpace($ghUploadUrl)) {
throw "GitHub release $($ghRelease.id) has no upload_url"
}
$ghUploadUrl = $ghUploadUrl -replace '\{\?[^}]*\}', ''
Write-Host "GitHub upload base: $ghUploadUrl"
function Upload-One([string]$File) {
$filename = [System.IO.Path]::GetFileName($File)
$sizeMb = [math]::Round((Get-Item $File).Length / 1MB, 1)
Write-Host "Uploading $filename (${sizeMb} MB)..."
$giteaAssets = Invoke-JsonCurl @(
"-fsS", "-H", "Authorization: token $env:GITEA_TOKEN",
"$API_BASE/repos/$REPO/releases/$($giteaRelease.id)/assets"
)
foreach ($asset in @($giteaAssets)) {
if ($asset.name -eq $filename) {
& curl.exe -fsS -X DELETE -H "Authorization: token $env:GITEA_TOKEN" `
"$API_BASE/repos/$REPO/releases/$($giteaRelease.id)/assets/$($asset.id)" | Out-Null
}
}
& curl.exe -fsS --max-time 0 --retry 5 --retry-delay 10 `
-X POST `
-H "Authorization: token $env:GITEA_TOKEN" `
-H "Content-Type: application/octet-stream" `
--data-binary "@$File" `
"$API_BASE/repos/$REPO/releases/$($giteaRelease.id)/assets?name=$filename" | Out-Null
if ($LASTEXITCODE -ne 0) { throw "Gitea upload failed for $filename" }
Write-Host " Gitea: ok"
$ghAssets = Invoke-JsonCurl @(
"-fsS", "-H", "Authorization: token $env:GH_TOKEN",
"https://api.github.com/repos/$GITHUB_REPO/releases/$($ghRelease.id)/assets"
)
foreach ($asset in @($ghAssets)) {
if ($asset.name -eq $filename) {
& curl.exe -fsS -X DELETE -H "Authorization: token $env:GH_TOKEN" `
"https://api.github.com/repos/$GITHUB_REPO/releases/assets/$($asset.id)" | Out-Null
}
}
# Brace the variable — `$url?name=` is parsed as PowerShell's null-conditional.
$ghAssetUrl = "${ghUploadUrl}?name=$([uri]::EscapeDataString($filename))"
Write-Host " GitHub URL: $ghAssetUrl"
& curl.exe -fsS --max-time 0 --retry 5 --retry-delay 10 `
-X POST `
-H "Authorization: token $env:GH_TOKEN" `
-H "Content-Type: application/octet-stream" `
--data-binary "@$File" `
$ghAssetUrl | Out-Null
if ($LASTEXITCODE -ne 0) { throw "GitHub upload failed for $filename" }
Write-Host " GitHub: ok"
}
Get-ChildItem -Path dist-electron -File |
Where-Object { $_.Name -like 'Paarrot-*-win-x64.exe' -or $_.Name -eq 'latest.yml' } |
ForEach-Object { Upload-One $_.FullName }
- name: List build output
shell: powershell
run: |
Get-ChildItem -Path dist-electron -Recurse | Select-Object FullName
- name: Copy RELEASES file for upload
shell: powershell
run: |
if (Test-Path "dist-electron/squirrel-windows/RELEASES") {
Copy-Item "dist-electron/squirrel-windows/RELEASES" -Destination "dist-electron/RELEASES"
} else {
Write-Error "RELEASES file not found for x64"
exit 1
}
- name: Upload Squirrel RELEASES
uses: actions/upload-artifact@v3
with:
name: RELEASES
path: dist-electron/RELEASES
- name: Upload Squirrel nupkg (x64)
uses: actions/upload-artifact@v3
with:
name: Paarrot-x64.nupkg
path: dist-electron/squirrel-windows/*.nupkg
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'
@@ -166,120 +422,130 @@ jobs:
rm -rf cinny/dist dist-electron
- name: Build Electron app
run: npm run build:linux
run: npm run build:local -- --linux
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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 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 "*.nupkg" -o -name "RELEASES*" \) -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"
# 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 ""
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
[ -n "$OLD_ID" ] || continue
OLD_TAG=$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" \
"${API_BASE}/repos/${REPO}/releases/${OLD_ID}" | jq -r '.tag_name')
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
echo "Release complete!"
- name: Cleanup old GitHub releases
env:
GH_TOKEN: ${{ secrets.GHTOKEN }}
run: |
set -euo pipefail
TAG_NAME="${{ needs.prepare-release.outputs.tag }}"
GITHUB_REPO="Paarrot/Paarrot-Desktop"
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
[ -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')
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
echo "Release finalize complete for ${TAG_NAME}"

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
github: ajbura
liberapay: ajbura
open_collective: cinny

View File

@@ -1,58 +0,0 @@
name: 🐞 Bug Report
description: Report a bug
labels: 'type: bug'
body:
- type: markdown
attributes:
value: |
## First of all
1. Please search for [existing issues](https://github.com/cinnyapp/cinny-desktop/issues?q=is%3Aissue) about this problem first.
2. Make sure Cinny is up to date.
3. Make sure it's an issue with Cinny and not something else you are using.
4. Remember to be friendly.
- type: textarea
id: description
attributes:
label: Describe the bug
description: A clear description of what the bug is. Include screenshots if applicable.
placeholder: Bug description
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction
description: Steps to reproduce the behavior.
placeholder: |
1. Go to ...
2. Click on ...
3. See error
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: A clear description of what you expected to happen.
- type: textarea
id: info
attributes:
label: Platform and versions
description: "Provide OS, browser and Cinny version with your Homeserver."
placeholder: |
1. OS: [e.g. Windows 10, MacOS]
2. Cinny version: [e.g. 1.8.1]
3. Matrix homeserver: [e.g. matrix.org]
4. Downloaded from: [e.g. GitHub, Flatpak]
render: shell
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context about the problem here.

View File

@@ -1,4 +0,0 @@
contact_links:
- name: 💬 Matrix Chat
url: https://matrix.to/#/#cinny:matrix.org
about: Ask questions and talk to other Cinny users and the maintainers

View File

@@ -1,34 +0,0 @@
name: 💡 Feature Request
description: Suggest an idea
labels: 'type: feature'
body:
- type: textarea
id: problem
attributes:
label: Describe the problem
description: A clear description of the problem this feature would solve
placeholder: "I'm always frustrated when..."
validations:
required: true
- type: textarea
id: solution
attributes:
label: "Describe the solution you'd like"
description: A clear description of what change you would like
placeholder: "I would like to..."
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: "Any alternative solutions you've considered"
- type: textarea
id: context
attributes:
label: Additional context
description: Add any other context about the problem here.

View File

@@ -1,22 +0,0 @@
<!-- Please read https://github.com/cinnyapp/cinny/blob/dev/CONTRIBUTING.md before submitting your pull request -->
### Description
<!-- Please include a summary of the change. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
Fixes #
#### Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
### Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings

3
.github/SECURITY.md vendored
View File

@@ -1,3 +0,0 @@
# Reporting a Vulnerability
**If you've found a security vulnerability, please report it to cinnyapp@gmail.com**

View File

@@ -1,30 +0,0 @@
# Docs: <https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/customizing-dependency-updates>
version: 2
updates:
# - package-ecosystem: npm
# directory: /
# schedule:
# interval: weekly
# day: "tuesday"
# time: "01:00"
# timezone: "Asia/Kolkata"
# open-pull-requests-limit: 15
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: "tuesday"
time: "01:00"
timezone: "Asia/Kolkata"
open-pull-requests-limit: 5
# - package-ecosystem: cargo
# directory: /src-tauri/
# schedule:
# interval: weekly
# day: "tuesday"
# time: "01:00"
# timezone: "Asia/Kolkata"
# open-pull-requests-limit: 5

21
.github/renovate.json vendored
View File

@@ -1,21 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboardApproval"
],
"labels": [
"Dependencies"
],
"packageRules": [
{
"matchUpdateTypes": [
"lockFileMaintenance"
]
}
],
"lockFileMaintenance": {
"enabled": true
},
"dependencyDashboard": true
}

View File

@@ -1,22 +0,0 @@
name: "Upload zip-archive"
on:
release:
types: [published]
jobs:
zip-archive:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4.2.0
with:
submodules: true
- name: Create zip including submodules
run: |
cd ..
zip ${{ github.event.repository.name }}/${{ github.event.repository.name }}-${{ github.ref_name }}.zip ${{ github.event.repository.name }} -r
- name: Upload zip to release
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
with:
files: |
${{ github.event.repository.name }}-${{ github.ref_name }}.zip

View File

@@ -1,36 +0,0 @@
name: 'CLA Assistant'
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened, closed, synchronize]
jobs:
CLAssistant:
runs-on: ubuntu-latest
steps:
- name: 'CLA Assistant'
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
# Beta Release
uses: cla-assistant/github-action@v2.6.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# the below token should have repo scope and must be manually added by you in the repository's secret
PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_PAT }}
with:
path-to-signatures: 'signatures.json'
path-to-document: 'https://github.com/cinnyapp/cla/blob/main/cla.md' # e.g. a CLA or a DCO document
# branch should not be protected
branch: 'main'
allowlist: ajbura,bot*
#below are the optional inputs - If the optional inputs are not given, then default values will be taken
remote-organization-name: cinnyapp
remote-repository-name: cla
#create-file-commit-message: 'For example: Creating file for storing CLA Signatures'
#signed-commit-message: 'For example: $contributorName has signed the CLA in #$pullRequestNo'
#custom-notsigned-prcomment: 'pull request comment with Introductory message to ask new contributors to sign'
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
#use-dco-flag: true - If you are using DCO instead of CLA

View File

@@ -1,26 +0,0 @@
name: NPM Lockfile Changes
on:
pull_request:
paths:
- 'package-lock.json'
jobs:
lockfile_changes:
runs-on: ubuntu-latest
# Permission overwrite is required for Dependabot PRs, see "Common issues" below.
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4.2.0
- name: NPM Lockfile Changes
uses: codepunkt/npm-lockfile-changes@b40543471c36394409466fdb277a73a0856d7891
with:
token: ${{ secrets.GITHUB_TOKEN }}
# Optional inputs, can be deleted safely if you are happy with default values.
collapsibleThreshold: 25
failOnDowngrade: false
path: package-lock.json
updateComment: true

View File

@@ -1,40 +0,0 @@
name: "Build pull request"
on:
#pull_request:
#types: ['opened', 'synchronize']
jobs:
publish-tauri:
strategy:
fail-fast: false
matrix:
platform: [macos-latest, ubuntu-20.04, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout repository
uses: actions/checkout@v4.2.0
with:
submodules: true
- name: Setup node
uses: actions/setup-node@v4.4.0
with:
node-version: 20.12.2
cache: 'npm'
- name: Install Rust stable
uses: actions-rs/toolchain@v1.0.7
with:
toolchain: stable
- name: Install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-20.04'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: Install cinny dependencies
run: cd cinny && npm ci
- name: Install tauri dependencies
run: npm ci
- name: Build desktop app with Tauri
uses: tauri-apps/tauri-action@v0.5.14
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ node_modules
devAssets
.DS_Store
dist-electron/

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

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"liveServer.settings.port": 5501
}

View File

@@ -1,172 +0,0 @@
# Paarrot API Quick Start
A local HTTP API server for controlling Paarrot from external devices like Stream Deck, scripts, and automation tools.
## 🚀 Getting Started
The API server starts automatically when you run Paarrot. It listens on `http://127.0.0.1:33384`.
### Test the API
**Option 1: Using Postman**
Import the Postman collection for easy testing:
1. Open Postman
2. Click **Import****File**
3. Select `paarrot-api.postman_collection.json`
4. All endpoints will be ready to use!
The collection is automatically updated on git push.
**Option 2: Using the test script**
```bash
node test-api.js
```
**Option 3: Using curl**
```bash
./test-api.sh
```
**Option 4: Manual curl commands**
```bash
# Health check
curl http://127.0.0.1:33384/health
# Toggle mute
curl -X POST http://127.0.0.1:33384/mute/toggle
# Send message
curl -X POST http://127.0.0.1:33384/message/current \
-H "Content-Type: application/json" \
-d '{"message": "Hello from API!"}'
```
## 📝 Quick Reference
### Common Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/health` | GET | Check if API is running |
| `/status` | GET | Get app status (mute, deafen, etc.) |
| `/mute/toggle` | POST | Toggle microphone mute |
| `/deafen/toggle` | POST | Toggle deafen |
| `/channels` | GET | Get list of rooms |
| `/channel` | POST | Switch to a room |
| `/message/current` | POST | Send message to current room |
## 🎮 Stream Deck Integration
1. Install the "API Ninja" or "HTTP Request" plugin
2. Create buttons with these settings:
**Mute Toggle Button:**
- URL: `http://127.0.0.1:33384/mute/toggle`
- Method: POST
**Quick Message Button:**
- URL: `http://127.0.0.1:33384/message/current`
- Method: POST
- Body: `{"message": "BRB!"}`
## 🔧 Integration with Paarrot
To enable full functionality, you need to implement the action handlers in the Cinny frontend:
1. Import the API handler in your app initialization:
```typescript
import { initPaarrotAPI } from './app/paarrot-api';
// After Matrix client is initialized
initPaarrotAPI(matrixClient);
```
2. Implement the TODO items in `cinny/src/app/paarrot-api.ts`:
- Mute/unmute logic (WebRTC audio)
- Deafen logic (WebRTC audio output)
- Navigation to rooms (router integration)
- Get current room from URL/state
## 📖 Full Documentation
See [API.md](API.md) for complete API documentation including:
- All available endpoints
- Request/response formats
- Error handling
- Code examples in multiple languages
- Detailed integration guide
## 🛠️ Configuration
Edit `electron/api-server.js` to customize:
- Port number (default: 33384)
- Timeout duration (default: 10s)
- CORS settings
## 🐛 Troubleshooting
**API not responding:**
1. Check if Paarrot is running
2. Look for "Paarrot API server listening" in console logs
3. Verify port 33384 is not in use: `lsof -i :33384`
**Actions not working:**
1. Check browser console for errors
2. Ensure `initPaarrotAPI()` is called in your app
3. Implement the TODO items in `paarrot-api.ts`
**Port in use:**
Edit `electron/api-server.js` and change the port number in the constructor.
## 📦 Files
- `electron/api-server.js` - API server implementation
- `cinny/src/app/paarrot-api.ts` - Client-side handler (needs implementation)
- `test-api.js` - Node.js test script
- `test-api.sh` - Bash test script
- `API.md` - Full documentation
## 🔐 Security
- API only listens on localhost (127.0.0.1)
- Not accessible from network
- No authentication required (local only)
- CORS enabled for all origins (safe since localhost only)
## 💡 Examples
**Python Script:**
```python
import requests
def toggle_mute():
r = requests.post('http://127.0.0.1:33384/mute/toggle')
print(r.json())
toggle_mute()
```
**JavaScript:**
```javascript
async function sendQuickMessage(msg) {
const res = await fetch('http://127.0.0.1:33384/message/current', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: msg })
});
return res.json();
}
```
## 🎯 Next Steps
1. Run Paarrot: `npm run dev`
2. Test the API: `node test-api.js`
3. Integrate the handler: Import and call `initPaarrotAPI()`
4. Implement the TODO items in `paarrot-api.ts`
5. Create Stream Deck buttons or automation scripts!
---
For more details, see [API.md](API.md)

537
API.md
View File

@@ -1,537 +0,0 @@
# Paarrot API Server Documentation
The Paarrot API server provides a local HTTP API for controlling the Paarrot desktop app from external applications like Stream Deck, keyboard macros, scripts, and other automation tools.
## Server Details
- **Base URL**: `http://127.0.0.1:33384`
- **Protocol**: HTTP
- **Content-Type**: `application/json`
- **CORS**: Enabled for all origins
## Authentication
Currently, no authentication is required. The API server only listens on localhost (127.0.0.1) for security.
## API Endpoints
### Health Check
Check if the API server is running.
**Endpoint**: `GET /health`
**Response**:
```json
{
"status": "ok",
"app": "Paarrot API",
"version": "1.0.0"
}
```
---
### Get Status
Get the current status of the app (mute, deafen, current room, etc.).
**Endpoint**: `GET /status`
**Response**:
```json
{
"success": true,
"data": {
"muted": false,
"deafened": false,
"currentRoom": "!abc123:matrix.org",
"connected": true
}
}
```
---
### Mute/Unmute
Set the mute status.
**Endpoint**: `POST /mute`
**Request Body**:
```json
{
"muted": true
}
```
**Response**:
```json
{
"success": true,
"data": {
"muted": true
}
}
```
---
### Toggle Mute
Toggle the current mute status.
**Endpoint**: `POST /mute/toggle`
**Response**:
```json
{
"success": true,
"data": {
"muted": true
}
}
```
---
### Deafen/Undeafen
Set the deafen status (deafening also mutes).
**Endpoint**: `POST /deafen`
**Request Body**:
```json
{
"deafened": true
}
```
**Response**:
```json
{
"success": true,
"data": {
"deafened": true
}
}
```
---
### Toggle Deafen
Toggle the current deafen status.
**Endpoint**: `POST /deafen/toggle`
**Response**:
```json
{
"success": true,
"data": {
"deafened": true
}
}
```
---
### Change Channel/Room
Switch to a different Matrix room.
**Endpoint**: `POST /channel`
**Request Body**:
```json
{
"roomId": "!abc123:matrix.org"
}
```
**Response**:
```json
{
"success": true,
"data": {
"roomId": "!abc123:matrix.org",
"roomName": "General"
}
}
```
---
### Get Channels/Rooms
Get a list of available rooms/channels.
**Endpoint**: `GET /channels`
**Response**:
```json
{
"success": true,
"data": [
{
"roomId": "!abc123:matrix.org",
"name": "General",
"server": "matrix.org",
"isDirect": false,
"avatar": null
},
{
"roomId": "!def456:matrix.org",
"name": "Random",
"server": "matrix.org",
"isDirect": false,
"avatar": null
},
{
"roomId": "!xyz789:example.com",
"name": "General",
"server": "example.com",
"isDirect": false,
"avatar": null
}
]
}
```
**Note**: The `server` field helps distinguish between rooms with the same name on different servers.
---
### Send Message
Send a message to a specific room.
**Endpoint**: `POST /message`
**Request Body**:
```json
{
"roomId": "!abc123:matrix.org",
"message": "Hello from the API!"
}
```
**Response**:
```json
{
"success": true,
"data": {
"eventId": "$abc123",
"roomId": "!abc123:matrix.org"
}
}
```
---
### Send Message to Current Room
Send a message to the currently active room.
**Endpoint**: `POST /message/current`
**Request Body**:
```json
{
"message": "Hello from the API!"
}
```
**Response**:
```json
{
"success": true,
"data": {
"eventId": "$abc123",
"roomId": "!abc123:matrix.org"
}
}
```
---
### Get Current Room
Get information about the currently active room.
**Endpoint**: `GET /room/current`
**Response**:
```json
{
"success": true,
"data": {
"roomId": "!abc123:matrix.org",
"name": "General",
"server": "matrix.org",
"avatar": "mxc://matrix.org/abc123",
"isDirect": false
}
}
```
---
## Error Responses
All endpoints return error responses in the following format:
```json
{
"success": false,
"error": "Error message here"
}
```
Common HTTP status codes:
- `200 OK` - Request successful
- `400 Bad Request` - Missing or invalid parameters
- `404 Not Found` - Endpoint not found
- `500 Internal Server Error` - Server error
---
## Example Usage
### cURL
```bash
# Toggle mute
curl -X POST http://127.0.0.1:33384/mute/toggle
# Send message to current room
curl -X POST http://127.0.0.1:33384/message/current \
-H "Content-Type: application/json" \
-d '{"message": "Hello!"}'
# Change channel
curl -X POST http://127.0.0.1:33384/channel \
-H "Content-Type: application/json" \
-d '{"roomId": "!abc123:matrix.org"}'
```
### JavaScript/Node.js
```javascript
const fetch = require('node-fetch');
// Toggle mute
async function toggleMute() {
const response = await fetch('http://127.0.0.1:33384/mute/toggle', {
method: 'POST'
});
const data = await response.json();
console.log('Muted:', data.data.muted);
}
// Send message
async function sendMessage(roomId, message) {
const response = await fetch('http://127.0.0.1:33384/message', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ roomId, message })
});
const data = await response.json();
console.log('Message sent:', data.data.eventId);
}
```
### Python
```python
import requests
# Toggle mute
def toggle_mute():
response = requests.post('http://127.0.0.1:33384/mute/toggle')
data = response.json()
print('Muted:', data['data']['muted'])
# Send message
def send_message(room_id, message):
response = requests.post('http://127.0.0.1:33384/message', json={
'roomId': room_id,
'message': message
})
data = response.json()
print('Message sent:', data['data']['eventId'])
```
---
## Stream Deck Integration
To integrate with Stream Deck:
1. Install the "API Ninja" or "HTTP Request" plugin for Stream Deck
2. Create a button with the following configuration:
- **URL**: `http://127.0.0.1:33384/mute/toggle`
- **Method**: POST
- **Headers**: `Content-Type: application/json`
Example configurations:
**Mute Toggle Button**:
- URL: `http://127.0.0.1:33384/mute/toggle`
- Method: POST
**Deafen Toggle Button**:
- URL: `http://127.0.0.1:33384/deafen/toggle`
- Method: POST
**Send Quick Message**:
- URL: `http://127.0.0.1:33384/message/current`
- Method: POST
- Body: `{"message": "BRB!"}`
---
## Client-Side Implementation
To enable the API functionality in the Paarrot app, you need to implement the action handlers in your Matrix client code. The API server sends actions via IPC to the renderer process, which should handle them and respond.
### Example Handler (TypeScript/React)
Add this to your app initialization:
```typescript
// Listen for API actions from the Electron API server
if (window.electron?.api?.onAction) {
window.electron.api.onAction(async (action: {
action: string;
params: any;
responseChannel: string;
}) => {
try {
let result;
switch (action.action) {
case 'toggle-mute':
// Toggle microphone mute
result = await toggleMicrophone();
break;
case 'set-mute':
// Set microphone mute state
result = await setMicrophone(action.params.muted);
break;
case 'toggle-deafen':
// Toggle deafen (mute speakers)
result = await toggleDeafen();
break;
case 'set-deafen':
// Set deafen state
result = await setDeafen(action.params.deafened);
break;
case 'change-channel':
// Navigate to a different room
result = await changeRoom(action.params.roomId);
break;
case 'get-channels':
// Get list of rooms
result = await getRoomList();
break;
case 'send-message':
// Send message to specific room
result = await sendMessage(action.params.roomId, action.params.message);
break;
case 'send-message-current':
// Send message to current room
result = await sendMessageToCurrent(action.params.message);
break;
case 'get-current-room':
// Get current room info
result = await getCurrentRoomInfo();
break;
case 'get-status':
// Get current app status
result = await getAppStatus();
break;
default:
throw new Error(`Unknown action: ${action.action}`);
}
// Send success response back
window.electron.api.sendResponse(action.responseChannel, {
success: true,
data: result
});
} catch (error) {
// Send error response back
window.electron.api.sendResponse(action.responseChannel, {
success: false,
error: error.message
});
}
});
}
```
---
## Configuration
The API server can be configured by modifying `electron/api-server.js`:
- **Default Port**: 33384 (can be changed in the constructor)
- **Bind Address**: 127.0.0.1 (localhost only for security)
- **Timeout**: 10 seconds for actions
---
## Security Notes
1. The API server only listens on localhost (127.0.0.1) and is not accessible from the network
2. No authentication is implemented - anyone with local access can control the app
3. Consider adding API key authentication if needed for your use case
4. CORS is enabled for all origins since it's localhost only
---
## Troubleshooting
**API server not starting**:
- Check if port 33384 is already in use
- Check console logs for errors
- Verify Express is installed: `npm install express cors body-parser`
**Actions not working**:
- Ensure the client-side handler is implemented
- Check browser console for errors
- Verify the action names match between API server and client
**Timeout errors**:
- Actions must respond within 10 seconds
- Check if the Matrix client is initialized
- Verify the action handler is registered
---
## Future Enhancements
Potential features to add:
- WebSocket support for real-time events
- Authentication with API keys
- Voice channel controls (join/leave)
- User status updates
- Notification controls
- Screen sharing controls

View File

@@ -1,4 +1,4 @@
# Paarrot
# Paarrot
Paarrot is a Matrix client focusing primarily on simple, elegant and secure interface. The desktop app is built with Electron and based on Cinny.
@@ -22,10 +22,57 @@ To setup development locally run the following commands:
* `cd cinny-desktop/cinny`
* `npm ci`
* `cd ..`
* `npm ci`
* `npm ci`
To build the app locally, run:
* `npm run build`
To start local dev server, run:
* `npm run dev`
## Plugin System
Paarrot includes a plugin system for extending and customising the app with JavaScript modules.
### Installing Plugins
1. Drop a plugin folder into your plugins directory:
- **Windows**: `%APPDATA%\paarrot\plugins\<plugin-name>\`
- **Linux**: `~/.config/Paarrot/plugins/<plugin-name>/`
- **macOS**: `~/Library/Application Support/Paarrot/plugins/<plugin-name>/`
2. Enable it in **Settings → Plugins → Installed**
### What Plugins Can Do
- Register custom slash commands
- Intercept and modify messages
- Inject buttons into 11 UI locations (nav lists, toolbars, headers, sidebar, menus)
- Register custom themes
- Hook into raw Matrix events
- Run background tasks
- Show system notifications
### Plugin Locations (UI Buttons)
Plugins can inject buttons as **nav list rows** or **icon buttons** across the app:
| Location | Style | Where |
|---|---|---|
| `channel-list` | Nav row | Space channel list |
| `home-section` | Nav row | Home panel, above room list |
| `direct-messages` | Nav row | DMs panel, below "Create Chat" |
| `sidebar-actions` | Icon | Left sidebar — above Explore and above Search |
| `text-composer-toolbar` | Icon | Message composer toolbar |
| `composer-actions` | Icon | Beside the `+` attach button |
| `room-header` | Icon | Room header bar |
| `room-menu` | Icon | Room ⋮ dropdown |
| `message-actions` | Icon | Message hover bar |
| `user-menu` | Icon | Right-click on user avatar |
| `search-notification-section` | Icon | Notifications page header |
### Documentation
- [Plugin System Overview](PLUGINS.md)
- [Full Plugin API Reference](docs/PLUGIN_API.md)
- [Button Registration API](docs/PLUGIN_BUTTON_API.md)
- [Example Plugins](plugins/)

10
build-and-publish.sh Normal file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
# Set GitHub token for publishing
export GH_TOKEN="ghtoken"
# Run the build and publish
npm run build
# Exit with the build's exit code
exit $?

2
cinny

Submodule cinny updated: 58966aec19...99a294791e

View File

@@ -9,30 +9,13 @@
"xmr.se"
],
"allowCustomHomeservers": true,
"calling": {
"livekitServiceUrl": "https://b.ruv.wtf/matrix-rtc/livekit/jwt"
},
"featuredCommunities": {
"openAsDefault": false,
"spaces": [
"#cinny-space:matrix.org",
"#community:matrix.org",
"#space:envs.net",
"#science-space:matrix.org",
"#libregaming-games:tchncs.de",
"#mathematics-on:matrix.org"
],
"rooms": [
"#cinny:matrix.org",
"#freesoftware:matrix.org",
"#pcapdroid:matrix.org",
"#gentoo:matrix.org",
"#PrivSec.dev:arcticfoxes.net",
"#disroot:aria-net.org"
],
"servers": ["envs.net", "matrix.org", "monero.social", "mozilla.org"]
"servers": []
},
"hashRouter": {

4
dev-app-update.yml Normal file
View File

@@ -0,0 +1,4 @@
provider: github
owner: Paarrot
repo: Paarrot-Desktop
updaterCacheDirName: paarrot-updater

Binary file not shown.

View File

@@ -10,6 +10,7 @@
"files": [
"electron/**/*",
"cinny/dist/**/*",
"dev-app-update.yml",
"!**/*.map",
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
"!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
@@ -28,6 +29,17 @@
"from": "icons",
"to": "icons",
"filter": ["**/*"]
},
{
"from": "cinny/public/sound",
"to": "sound",
"filter": ["**/*"]
}
],
"protocols": [
{
"name": "Paarrot URL",
"schemes": ["paarrot"]
}
],
"linux": {
@@ -45,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"
@@ -72,23 +85,30 @@
"win": {
"target": [
{
"target": "squirrel",
"target": "nsis",
"arch": ["x64"]
}
],
"icon": "icons/icon.ico",
"publisherName": "Mates.Media",
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}"
},
"squirrelWindows": {
"name": "Paarrot",
"loadingGif": "icons/icon.png",
"iconUrl": "http://synbox.ruv.wtf:8418/litruv/cinny-desktop/raw/branch/main/icons/icon.ico",
"remoteReleases": false,
"useAppIdAsId": true
"nsis": {
"oneClick": true,
"perMachine": false,
"allowToChangeInstallationDirectory": false,
"deleteAppDataOnUninstall": false,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "Paarrot",
"differentialPackage": false
},
"publish": {
"provider": "generic",
"url": "http://synbox.ruv.wtf:8418/litruv/cinny-desktop/releases/download/v${version}"
}
"compression": "normal",
"publish": [
{
"provider": "github",
"owner": "Paarrot",
"repo": "Paarrot-Desktop",
"releaseType": "release"
}
]
}

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
@@ -93,7 +94,9 @@ contextBridge.exposeInMainWorld('electron', {
unmaximize: () => ipcRenderer.invoke('window:unmaximize'),
close: () => ipcRenderer.invoke('window:close'),
isMaximized: () => ipcRenderer.invoke('window:is-maximized'),
startDrag: () => ipcRenderer.invoke('window:start-drag')
startDrag: () => ipcRenderer.invoke('window:start-drag'),
focus: () => ipcRenderer.invoke('window:focus'),
flashFrame: (flash = true) => ipcRenderer.invoke('window:flash-frame', flash)
},
// Clipboard
@@ -101,8 +104,12 @@ contextBridge.exposeInMainWorld('electron', {
readImage: () => ipcRenderer.invoke('read-clipboard-image'),
writeText: (text) => ipcRenderer.invoke('write-clipboard-text', text)
},
// External URLs
// Audio
audio: {
playNotificationSound: (soundType = 'message') => ipcRenderer.invoke('play-notification-sound', soundType),
getSoundBaseUrl: () => ipcRenderer.invoke('get-sound-base-url'),
},
// External URLs
shell: {
openExternal: (url) => ipcRenderer.invoke('open-external-url', url)
},
@@ -122,8 +129,8 @@ contextBridge.exposeInMainWorld('electron', {
// Auto-updater
updater: {
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
downloadUpdate: () => ipcRenderer.invoke('download-update'),
installUpdate: () => ipcRenderer.invoke('install-update'),
downloadAndInstallUpdate: () => ipcRenderer.invoke('download-and-install-update'),
isMock: () => ipcRenderer.invoke('updater-is-mock'),
onUpdateAvailable: (callback) => {
ipcRenderer.on('update-available', (_, info) => callback(info));
},
@@ -132,7 +139,12 @@ 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'),
},
// Desktop capturer (for screen sharing)
@@ -150,6 +162,31 @@ contextBridge.exposeInMainWorld('electron', {
sendResponse: (channel, result) => {
ipcRenderer.invoke(channel, result);
}
},
// Desktop notifications
notification: {
show: (options) => ipcRenderer.invoke('show-notification', options),
onNavigate: (callback) => {
const handler = (_, data) => callback(data);
ipcRenderer.on('notification:navigate', handler);
return () => ipcRenderer.removeListener('notification:navigate', handler);
}
},
// Plugin management
plugins: {
getPath: () => ipcRenderer.invoke('plugin:get-path'),
download: (pluginId, downloadUrl, name) => ipcRenderer.invoke('plugin:download', { pluginId, downloadUrl, name }),
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')
}
});

161
example-plugin/index.js Normal file
View File

@@ -0,0 +1,161 @@
// Example plugin for Paarrot - Demonstrates all plugin API features
// This plugin showcases commands, message interceptors, settings, logging, and more
module.exports = {
name: "Example Plugin",
version: "2.0.0",
/**
* Called when the plugin is loaded
* @param {PluginContext} ctx - The plugin context with access to all APIs
*/
onLoad: async (ctx) => {
ctx.log('🎉 Example plugin is loading...');
try {
// 1. Register commands with args
ctx.commands.register({
name: "shrug",
description: "Send a shrug emoji",
run: () => {
ctx.log('Shrug command executed');
return "¯\\_(ツ)_/¯";
}
});
ctx.commands.register({
name: "echo",
args: ["text"],
description: "Echo back your text",
run: ({ text }) => {
ctx.log('Echo command:', text);
return text || "Nothing to echo!";
}
});
ctx.commands.register({
name: "wave",
description: "Wave at someone",
args: ["name"],
run: ({ name }) => {
return `👋 Hey ${name || 'everyone'}!`;
}
});
// 2. Message interceptors
ctx.messages.onBeforeSend((msg) => {
// Auto-expand abbreviations
if (msg.content === "brb") {
msg.content = "be right back!";
ctx.log('Expanded brb to full phrase');
}
if (msg.content === "omw") {
msg.content = "on my way!";
}
});
ctx.messages.onReceive((msg) => {
// Log all received messages (for debugging)
ctx.log(`Received message in ${msg.roomId}:`, msg.content.substring(0, 50));
});
// 3. Define settings
ctx.settings.define({
autoShrug: {
type: "boolean",
label: "Auto Shrug",
description: "Automatically append ¯\\_(ツ)_/¯ to messages ending with '?'",
default: false
},
theme: {
type: "select",
label: "Plugin Theme",
description: "Choose your preferred theme",
options: [
{ value: "dark", label: "Dark" },
{ value: "light", label: "Light" },
{ value: "purple", label: "Purple" }
],
default: "dark"
},
customPrefix: {
type: "string",
label: "Custom Prefix",
description: "Prefix for plugin commands",
default: "[Plugin]"
}
});
// Use settings
const autoShrug = ctx.settings.get('autoShrug');
if (autoShrug) {
ctx.log('Auto shrug is enabled!');
ctx.messages.onBeforeSend((msg) => {
if (msg.content.endsWith('?')) {
msg.content += ' ¯\\_(ツ)_/¯';
}
});
}
// 4. Listen to raw Matrix events
ctx.matrix.on('Room.timeline', (event) => {
if (event.getType() === 'm.room.message') {
const content = event.getContent();
if (content.body?.includes('hello')) {
ctx.log('Someone said hello!');
}
}
});
// 5. Background tasks
ctx.timers.setInterval(() => {
ctx.log('Background task running... (every 30s)');
// You could check for notifications, clean up data, etc.
}, 30000);
// 6. Notifications
ctx.notify({
title: "Example Plugin",
body: "Plugin loaded successfully!",
type: "success"
});
// 7. Custom UI renderer (conceptual - would need UI integration)
ctx.ui.registerRenderer("custom-message", (msg) => {
ctx.log('Rendering custom message:', msg);
// Return custom React component
return null;
});
// 8. Export API for other plugins
this.exports = {
greet: (name) => {
return `Hello, ${name}!`;
},
version: "2.0.0"
};
ctx.log('✅ Example plugin loaded successfully!');
ctx.log('Registered commands: /shrug, /echo, /wave');
ctx.log('Settings:', {
autoShrug: ctx.settings.get('autoShrug'),
theme: ctx.settings.get('theme'),
prefix: ctx.settings.get('customPrefix')
});
} catch (error) {
ctx.error('Failed to load example plugin:', error);
}
},
/**
* Called when the plugin is unloaded or disabled
*/
onUnload: async () => {
console.log('[ExamplePlugin] Plugin is unloading...');
console.log('[ExamplePlugin] Cleaned up successfully!');
},
// Exports for other plugins to use via ctx.require()
exports: null // Will be set during onLoad
};

View File

@@ -0,0 +1,9 @@
{
"name": "Example Plugin",
"version": "2.0.0",
"description": "A comprehensive example plugin demonstrating all plugin API features including commands, interceptors, settings, and more",
"author": "Paarrot Team",
"homepage": "https://github.com/Paarrot/cinny-desktop",
"thumbnail": "https://raw.githubusercontent.com/Paarrot/cinny-desktop/main/icons/icon.png",
"tags": ["example", "demo", "utility", "commands", "interceptors"]
}

3944
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,22 @@
{
"name": "paarrot",
<<<<<<< HEAD
"version": "4.11.24",
"version": "4.11.168",
"description": "Paarrot - A Matrix client based on Cinny",
"homepage": "http://synbox.ruv.wtf:8418/litruv/cinny-desktop",
=======
"version": "4.11.24",
"description": "Yet another matrix client",
"homepage": "https://github.com/ajbura/cinny",
>>>>>>> bd544193be537882c7c69a153c38cc211293c741
"homepage": "https://github.com/Paarrot/Paarrot-Desktop",
"repository": {
"type": "git",
"url": "https://github.com/Paarrot/Paarrot-Desktop.git"
},
"main": "electron/main.js",
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"dev": "concurrently \"npm run dev:vite\" \"npm run dev:electron\"",
"dev:vite": "cd cinny && npm start",
"dev:electron": "wait-on http://localhost:8080 && NODE_ENV=development electron .",
"build": "node -e \"require('fs').copyFileSync('config.json', 'cinny/config.json')\" && cd cinny && npm run build && cd .. && electron-builder --publish never",
"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:linux": "npm run build -- --linux",
"build:win": "npm run build -- --win",
"postman:generate": "node scripts/generate-postman-collection.js"
@@ -29,23 +28,28 @@
},
"license": "AGPL-3.0-only",
"dependencies": {
"body-parser": "2.2.2",
"adm-zip": "^0.5.18",
"body-parser": "2.3.0",
"cors": "2.8.6",
"electron-squirrel-startup": "^1.0.1",
"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",
"electron": "40.6.0",
"electron-builder": "26.8.1",
"@actions/github": "9.1.1",
"@electron/rebuild": "4.2.0",
"concurrently": "10.0.3",
"cross-env": "10.1.0",
"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
}
}

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.`);

26
streamdeck-plugin/.gitignore vendored Normal file
View File

@@ -0,0 +1,26 @@
# Distribution files
dist/
*.streamDeckPlugin
# Node modules (if any dependencies are added)
node_modules/
# OS files
.DS_Store
Thumbs.db
desktop.ini
# IDE files
.vscode/
.idea/
*.swp
*.swo
*~
# Logs
*.log
npm-debug.log*
# Temporary files
*.tmp
*.temp

View File

@@ -0,0 +1,207 @@
# Quick Start Guide - Paarrot Stream Deck Plugin
## For Users
### Installation (3 simple steps)
1. **Download the plugin**
- Get `com.paarrot.streamdeck-v1.0.0.streamDeckPlugin` from releases
2. **Install**
- Double-click the `.streamDeckPlugin` file
- Stream Deck software will open and install it automatically
3. **Add to your Stream Deck**
- Open Stream Deck software
- Find "Paarrot" in the actions list (under "Voice & Chat")
- Drag actions to your buttons
### Quick Action Guide
| Action | What it does | Setup needed? |
|--------|--------------|---------------|
| Toggle Mute | Mute/unmute mic | No |
| Toggle Deafen | Mute/unmute speakers | No |
| Change Channel | Switch rooms | Yes - pick room |
| Send Message | Send preset message | Yes - type message |
| Get Status | Show mute/deafen state | No |
### Configuration Examples
**Change Channel**:
1. Drag to button
2. Click button in Stream Deck to open settings
3. Select channel from dropdown
4. Done!
**Send Message**:
1. Drag to button
2. Click button in Stream Deck to open settings
3. Type message (e.g., "BRB!")
4. Done!
---
## For Developers
### Prerequisites
- Node.js (for build scripts)
- Stream Deck software
- Text editor
### Development Setup
```powershell
# Clone or download the repo
cd streamdeck-plugin
# Validate plugin structure
node validate.js
# Install to Stream Deck (Windows)
Copy-Item -Recurse -Force com.paarrot.streamdeck.sdPlugin "$env:APPDATA\Elgato\StreamDeck\Plugins\"
# Install to Stream Deck (macOS)
cp -r com.paarrot.streamdeck.sdPlugin ~/Library/Application\ Support/com.elgato.StreamDeck/Plugins/
# Restart Stream Deck software
```
### Making Changes
1. Edit files in `com.paarrot.streamdeck.sdPlugin/`
2. Run validation: `node validate.js`
3. Restart Stream Deck software
4. Test your changes
### Building for Distribution
```powershell
# Validate first
node validate.js
# Build .streamDeckPlugin file
node build.js
# Output will be in dist/ folder
```
### File Guide
```
com.paarrot.streamdeck.sdPlugin/
├── manifest.json # Edit: plugin info, actions
├── plugin/
│ └── index.js # Edit: main logic, API calls
├── propertyinspector/
│ ├── change-channel.html # Edit: channel picker UI
│ └── send-message.html # Edit: message input UI
└── images/ # Edit: replace SVGs with your icons
└── *.svg
```
### Adding a New Action
1. **Add to manifest.json**:
```json
{
"Icon": "images/my-action",
"Name": "My Action",
"States": [{ "Image": "images/my-action" }],
"Tooltip": "Does something cool",
"UUID": "com.paarrot.streamdeck.myaction"
}
```
2. **Add icon**: `images/my-action.svg`
3. **Add logic in plugin/index.js**:
```javascript
const ACTIONS = {
MY_ACTION: 'com.paarrot.streamdeck.myaction'
};
// In handleKeyDown:
case ACTIONS.MY_ACTION:
// Your code here
break;
```
4. **Test**: Restart Stream Deck software
### Testing
1. Enable Stream Deck console: `View > Open Console`
2. Watch for errors when pressing buttons
3. Test all actions with Paarrot running
4. Test with Paarrot not running (should fail gracefully)
### API Reference
The plugin calls these Paarrot API endpoints:
```javascript
// Status polling (every 1 second)
GET http://127.0.0.1:33384/status
// Toggle actions
POST http://127.0.0.1:33384/mute/toggle
POST http://127.0.0.1:33384/deafen/toggle
// Configured actions
POST http://127.0.0.1:33384/channel
Body: { "roomId": "!abc:server.com" }
POST http://127.0.0.1:33384/message/current
Body: { "message": "Hello!" }
// Property inspector
GET http://127.0.0.1:33384/channels
```
### Common Issues
**Plugin not loading**:
- Check manifest.json syntax (use validator)
- Ensure folder name is exactly `com.paarrot.streamdeck.sdPlugin`
- Restart Stream Deck software
**Actions not working**:
- Open console (View > Open Console)
- Check for JavaScript errors
- Verify Paarrot API is running
**Icons not showing**:
- Ensure files exist at paths specified in manifest
- Use .svg or .png format
- Check file paths are relative to plugin root
### Resources
- [Stream Deck SDK Documentation](https://docs.elgato.com/sdk)
- [Paarrot API Documentation](../API.md)
- [Example Plugins](https://github.com/elgatosf/streamdeck-plugins)
### Publishing Checklist
- [ ] Update version in manifest.json
- [ ] Run `node validate.js` (no errors)
- [ ] Test all actions
- [ ] Test with Paarrot off (graceful failure)
- [ ] Update README.md version history
- [ ] Run `node build.js`
- [ ] Test .streamDeckPlugin file installs correctly
- [ ] Create GitHub release with .streamDeckPlugin file
---
## Support
**Users**: See README.md troubleshooting section
**Developers**: Open an issue on GitHub with:
- What you're trying to do
- What's happening
- Console errors
- Plugin version

263
streamdeck-plugin/README.md Normal file
View File

@@ -0,0 +1,263 @@
# Paarrot Stream Deck Plugin
Control your Paarrot desktop app directly from your Elgato Stream Deck! Toggle mute, deafen, switch channels, and send quick messages with the press of a button.
## Features
- **Toggle Mute** - Mute/unmute your microphone with visual feedback
- **Toggle Deafen** - Deafen/undeafen (mute speakers) with visual feedback
- **Change Channel** - Switch between rooms/channels instantly
- **Send Message** - Send predefined messages to the current room
- **Get Status** - Display current mute/deafen status
## Requirements
- Elgato Stream Deck (any model)
- Stream Deck software version 6.0 or later
- Paarrot desktop app running with API server enabled
- Windows 10+ or macOS 10.14+
## Installation
### Method 1: Install from Release (easiest)
1. Download the latest `.streamDeckPlugin` file from the [Releases](https://github.com/yourusername/paarrot-streamdeck/releases) page
2. Double-click the file to install
3. Stream Deck software will open and install the plugin automatically
### Method 2: Manual Installation
1. Download or clone this repository
2. Copy the `com.paarrot.streamdeck.sdPlugin` folder to:
- **Windows**: `%APPDATA%\Elgato\StreamDeck\Plugins\`
- **macOS**: `~/Library/Application Support/com.elgato.StreamDeck/Plugins/`
3. Restart the Stream Deck software
### Method 3: Developer Mode
1. Open Stream Deck software
2. Open the Plugins view
3. Enable "Developer Mode" in settings
4. Drag the `com.paarrot.streamdeck.sdPlugin` folder into the Stream Deck window
## Setup
### 1. Ensure Paarrot API is Running
Make sure your Paarrot desktop app is running with the API server enabled on port 33384. You can test this by visiting:
```
http://127.0.0.1:33384/health
```
You should see:
```json
{
"status": "ok",
"app": "Paarrot API",
"version": "1.0.0"
}
```
### 2. Add Actions to Stream Deck
1. Open Stream Deck software
2. Find "Paarrot" in the actions list
3. Drag actions onto your Stream Deck buttons
4. Configure actions as needed (see below)
## Actions
### Toggle Mute
**What it does**: Toggles your microphone mute status
**Configuration**: No configuration needed
**Visual Feedback**:
- Gray microphone icon = unmuted
- Red X over microphone = muted
**Usage**: Press to toggle between muted and unmuted. The button automatically updates to show the current state.
---
### Toggle Deafen
**What it does**: Toggles deafen status (mutes speakers and microphone)
**Configuration**: No configuration needed
**Visual Feedback**:
- Gray speaker icon = not deafened
- Red X over speaker = deafened
**Usage**: Press to toggle between deafened and not deafened. The button automatically updates to show the current state.
---
### Change Channel
**What it does**: Switches to a different room/channel
**Configuration**:
1. Drag action to a button
2. Click the button in Stream Deck to open settings
3. Select a channel from the dropdown OR enter a room ID manually
4. Click away to save
**Usage**: Press to instantly switch to the configured channel.
**Tips**:
- Channels are grouped by server in the dropdown
- Use manual room ID for rooms not in the list
- Room ID format: `!abc123:matrix.org`
---
### Send Message
**What it does**: Sends a predefined message to the currently active room
**Configuration**:
1. Drag action to a button
2. Click the button in Stream Deck to open settings
3. Type your message in the text area
4. Click away to save
**Usage**: Press to send the message to whichever room is currently active.
**Example Messages**:
- "BRB!" - Be right back
- "AFK for 5 minutes"
- "On my way!"
- "Thanks everyone!"
- "GG!" - Good game
---
### Get Status
**What it does**: Displays current mute/deafen status on the button
**Configuration**: No configuration needed
**Visual Feedback**:
- Shows "OK" when not muted or deafened
- Shows "M" when muted
- Shows "D" when deafened
- Shows "MD" when both muted and deafened
**Usage**: Button automatically updates every second. Press to force an immediate update.
## Troubleshooting
### Plugin not showing up
- Restart Stream Deck software
- Check that the plugin folder is in the correct location
- Ensure folder is named exactly `com.paarrot.streamdeck.sdPlugin`
### Actions not working
- Verify Paarrot is running
- Test API at `http://127.0.0.1:33384/health`
- Check console for errors (View > Open Console in Stream Deck)
- Ensure port 33384 is not blocked by firewall
### Button states not updating
- Check that Paarrot API is responding
- Try pressing the action to force an update
- Check Stream Deck console for connection errors
### "No channels available" in dropdown
- Ensure you're logged into Paarrot
- Verify you've joined at least one room
- Try entering the room ID manually instead
### Actions show red X (alert)
This means the API call failed. Common causes:
- Paarrot app not running
- API server not started
- Network/firewall blocking localhost connections
- Invalid room ID or message configuration
## Development
### Building from Source
No build step required! The plugin uses vanilla JavaScript.
### File Structure
```
com.paarrot.streamdeck.sdPlugin/
├── manifest.json # Plugin metadata and action definitions
├── plugin/
│ ├── index.html # Plugin entry point
│ └── index.js # Main plugin logic
├── propertyinspector/
│ ├── change-channel.html # Channel selector UI
│ └── send-message.html # Message input UI
└── images/ # Action icons
├── mute-off.svg
├── mute-on.svg
├── deafen-off.svg
├── deafen-on.svg
├── change-channel.svg
├── send-message.svg
├── status.svg
├── toggle-mute.svg
├── toggle-deafen.svg
├── plugin-icon.svg
└── category-icon.svg
```
### Modifying the Plugin
1. Make changes to the files
2. Restart Stream Deck software to reload the plugin
3. Test your changes
### API Endpoints Used
- `GET /health` - Check API is running
- `GET /status` - Get current mute/deafen state
- `POST /mute/toggle` - Toggle mute
- `POST /deafen/toggle` - Toggle deafen
- `POST /channel` - Change channel
- `POST /message/current` - Send message
- `GET /channels` - Get channel list
## Support
If you encounter issues:
1. Check the troubleshooting section above
2. Enable Stream Deck console logging (View > Open Console)
3. Check Paarrot's console for API errors
4. File an issue on GitHub with logs and details
## License
This plugin is provided as-is. See LICENSE file for details.
## Credits
Created for Paarrot desktop app.
Icons designed specifically for this plugin.
## Version History
### 1.0.0 (Initial Release)
- Toggle Mute action
- Toggle Deafen action
- Change Channel action with dropdown
- Send Message action
- Get Status action
- Auto-updating button states
- Visual feedback for all actions

View File

@@ -0,0 +1,69 @@
/**
* Build script for Paarrot Stream Deck Plugin
* Creates a distributable .streamDeckPlugin file
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const PLUGIN_DIR = 'com.paarrot.streamdeck.sdPlugin';
const OUTPUT_DIR = 'dist';
console.log('Building Paarrot Stream Deck Plugin...');
// Create dist directory if it doesn't exist
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR);
}
// Check if plugin directory exists
if (!fs.existsSync(PLUGIN_DIR)) {
console.error(`Error: Plugin directory '${PLUGIN_DIR}' not found!`);
process.exit(1);
}
// Read manifest to get version
const manifest = JSON.parse(
fs.readFileSync(path.join(PLUGIN_DIR, 'manifest.json'), 'utf8')
);
const version = manifest.Version || '1.0.0.0';
const uuid = manifest.UUID || 'com.paarrot.streamdeck';
const outputPath = path.join(OUTPUT_DIR, `${uuid}-v${version}.streamDeckPlugin`);
console.log(`Creating plugin archive: ${outputPath}`);
try {
// Clean up old archive
if (fs.existsSync(outputPath)) {
fs.unlinkSync(outputPath);
}
if (fs.existsSync(`${outputPath}.zip`)) {
fs.unlinkSync(`${outputPath}.zip`);
}
// Use PowerShell's Compress-Archive on Windows
// IMPORTANT: Archive must contain the .sdPlugin folder at root level
if (process.platform === 'win32') {
const command = `powershell -Command "Compress-Archive -Path '${PLUGIN_DIR}' -DestinationPath '${outputPath}.zip' -Force"`;
execSync(command);
// Rename .zip to .streamDeckPlugin
if (fs.existsSync(`${outputPath}.zip`)) {
fs.renameSync(`${outputPath}.zip`, outputPath);
}
} else {
// Use zip command on macOS/Linux - include the folder itself
execSync(`zip -r "${outputPath}" "${PLUGIN_DIR}"`);
}
console.log('✓ Plugin built successfully!');
console.log(` Output: ${outputPath}`);
console.log(` Version: ${version}`);
console.log('\nInstallation:');
console.log(` Double-click ${path.basename(outputPath)} to install`);
} catch (error) {
console.error('Error building plugin:', error.message);
process.exit(1);
}

View File

@@ -0,0 +1,9 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<rect x="30" y="30" width="40" height="40" rx="8" fill="#666" stroke="none"/>
<rect x="74" y="30" width="40" height="40" rx="8" fill="#888" stroke="none"/>
<rect x="30" y="74" width="40" height="40" rx="8" fill="#666" stroke="none"/>
<rect x="74" y="74" width="40" height="40" rx="8" fill="#666" stroke="none"/>
<path d="M50 50 L50 42 L58 50 L50 58 Z" fill="#444" stroke="none"/>
<path d="M94 50 L94 42 L102 50 L94 58 Z" fill="#fff" stroke="none"/>
</svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@@ -0,0 +1,6 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<path d="M40 50 L40 94 L56 94 L76 110 L76 34 L56 50 Z" fill="#888" stroke="none"/>
<path d="M88 58 C94 64 94 80 88 86" stroke="#888" stroke-width="6" stroke-linecap="round" fill="none"/>
<path d="M98 50 C108 60 108 84 98 94" stroke="#888" stroke-width="6" stroke-linecap="round" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@@ -0,0 +1,7 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<path d="M40 50 L40 94 L56 94 L76 110 L76 34 L56 50 Z" fill="#444" stroke="none"/>
<path d="M88 58 C94 64 94 80 88 86" stroke="#444" stroke-width="6" stroke-linecap="round" fill="none"/>
<path d="M98 50 C108 60 108 84 98 94" stroke="#444" stroke-width="6" stroke-linecap="round" fill="none"/>
<line x1="112" y1="32" x2="32" y2="112" stroke="#ff4444" stroke-width="8" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

View File

@@ -0,0 +1,5 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<path d="M72 36 L90 54 L90 90 C90 100 82 108 72 108 C62 108 54 100 54 90 L54 54 Z" fill="#888" stroke="none"/>
<ellipse cx="72" cy="54" rx="18" ry="24" fill="#888"/>
</svg>

After

Width:  |  Height:  |  Size: 315 B

View File

@@ -0,0 +1,6 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<path d="M72 36 L90 54 L90 90 C90 100 82 108 72 108 C62 108 54 100 54 90 L54 54 Z" fill="#444" stroke="none"/>
<ellipse cx="72" cy="54" rx="18" ry="24" fill="#444"/>
<line x1="108" y1="36" x2="36" y2="108" stroke="#ff4444" stroke-width="8" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 416 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 889 KiB

View File

@@ -0,0 +1,5 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<path d="M30 72 L114 36 L90 108 L64 88 L74 64 L50 78 Z" fill="#888" stroke="none"/>
<line x1="64" y1="88" x2="74" y2="64" stroke="#1a1a1a" stroke-width="4"/>
</svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@@ -0,0 +1,7 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<circle cx="72" cy="72" r="36" fill="none" stroke="#888" stroke-width="6"/>
<circle cx="72" cy="72" r="8" fill="#888"/>
<line x1="72" y1="72" x2="72" y2="48" stroke="#888" stroke-width="6" stroke-linecap="round"/>
<line x1="72" y1="72" x2="90" y2="72" stroke="#888" stroke-width="6" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 461 B

View File

@@ -0,0 +1,7 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<path d="M40 50 L40 94 L56 94 L76 110 L76 34 L56 50 Z" fill="#888" stroke="none"/>
<path d="M88 58 C94 64 94 80 88 86" stroke="#888" stroke-width="6" stroke-linecap="round" fill="none"/>
<path d="M98 50 C108 60 108 84 98 94" stroke="#888" stroke-width="6" stroke-linecap="round" fill="none"/>
<path d="M108 54 L118 64 M108 90 L118 80" stroke="#888" stroke-width="4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 544 B

View File

@@ -0,0 +1,6 @@
<svg width="144" height="144" viewBox="0 0 144 144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="#1a1a1a"/>
<path d="M72 36 L90 54 L90 90 C90 100 82 108 72 108 C62 108 54 100 54 90 L54 54 Z" fill="#888" stroke="none"/>
<ellipse cx="72" cy="54" rx="18" ry="24" fill="#888"/>
<path d="M98 58 L108 68 M98 86 L108 76" stroke="#888" stroke-width="4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 413 B

View File

@@ -0,0 +1,113 @@
{
"$schema": "https://schemas.elgato.com/streamdeck/plugins/manifest.json",
"Actions": [
{
"DisableAutomaticStates": true,
"Icon": "images/toggle-mute",
"Name": "Toggle Mute",
"States": [
{
"Image": "images/mute-off",
"TitleAlignment": "bottom",
"ShowTitle": true
},
{
"Image": "images/mute-on",
"TitleAlignment": "bottom",
"ShowTitle": true
}
],
"SupportedInMultiActions": true,
"Tooltip": "Toggle microphone mute",
"UUID": "com.paarrot.streamdeck.togglemute"
},
{
"DisableAutomaticStates": true,
"Icon": "images/toggle-deafen",
"Name": "Toggle Deafen",
"States": [
{
"Image": "images/deafen-off",
"TitleAlignment": "bottom",
"ShowTitle": true
},
{
"Image": "images/deafen-on",
"TitleAlignment": "bottom",
"ShowTitle": true
}
],
"SupportedInMultiActions": true,
"Tooltip": "Toggle deafen (mute speakers)",
"UUID": "com.paarrot.streamdeck.toggledeafen"
},
{
"Icon": "images/change-channel",
"Name": "Change Channel",
"PropertyInspectorPath": "propertyinspector/change-channel.html",
"States": [
{
"Image": "images/change-channel",
"TitleAlignment": "bottom",
"ShowTitle": true
}
],
"SupportedInMultiActions": true,
"Tooltip": "Switch to a specific channel/room",
"UUID": "com.paarrot.streamdeck.changechannel"
},
{
"Icon": "images/send-message",
"Name": "Send Message",
"PropertyInspectorPath": "propertyinspector/send-message.html",
"States": [
{
"Image": "images/send-message",
"TitleAlignment": "bottom",
"ShowTitle": true
}
],
"SupportedInMultiActions": true,
"Tooltip": "Send a message to current room",
"UUID": "com.paarrot.streamdeck.sendmessage"
},
{
"Icon": "images/status",
"Name": "Get Status",
"States": [
{
"Image": "images/status",
"TitleAlignment": "bottom",
"ShowTitle": true
}
],
"SupportedInMultiActions": false,
"Tooltip": "Display current status (mute/deafen state)",
"UUID": "com.paarrot.streamdeck.getstatus"
}
],
"Author": "Paarrot",
"Category": "Paarrot",
"CategoryIcon": "images/paarrot",
"CodePath": "plugin/index.html",
"Description": "Control Paarrot voice chat (mute, deafen, channels, messages)",
"Icon": "images/paarrot",
"Name": "Paarrot",
"OS": [
{
"Platform": "windows",
"MinimumVersion": "10"
},
{
"Platform": "mac",
"MinimumVersion": "10.14"
}
],
"SDKVersion": 2,
"Software": {
"MinimumVersion": "6.6"
},
"UUID": "com.paarrot.streamdeck",
"URL": "https://github.com/yourusername/paarrot-streamdeck",
"Version": "1.0.0.0"
}

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Paarrot Plugin</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>

View File

@@ -0,0 +1,371 @@
/**
* Paarrot Stream Deck Plugin
* Controls the Paarrot desktop app via its local API
*/
const API_BASE_URL = 'http://127.0.0.1:33384';
const POLL_INTERVAL = 1000; // Poll status every second
/**
* Global plugin instance
*/
let websocket = null;
let pluginUUID = null;
let statusPollInterval = null;
/**
* Track state for each action instance
*/
const actionStates = new Map();
/**
* Action UUIDs
*/
const ACTIONS = {
TOGGLE_MUTE: 'com.paarrot.streamdeck.togglemute',
TOGGLE_DEAFEN: 'com.paarrot.streamdeck.toggledeafen',
CHANGE_CHANNEL: 'com.paarrot.streamdeck.changechannel',
SEND_MESSAGE: 'com.paarrot.streamdeck.sendmessage',
GET_STATUS: 'com.paarrot.streamdeck.getstatus'
};
/**
* Make API call to Paarrot
*/
async function callAPI(endpoint, method = 'GET', body = null) {
try {
const options = {
method,
headers: {
'Content-Type': 'application/json'
}
};
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(`${API_BASE_URL}${endpoint}`, options);
const data = await response.json();
if (!data.success) {
throw new Error(data.error || 'API call failed');
}
return data.data;
} catch (error) {
console.error(`API Error (${endpoint}):`, error);
showAlert();
throw error;
}
}
/**
* Get current app status
*/
async function getStatus() {
return await callAPI('/status');
}
/**
* Set mute state explicitly
* @param {boolean} muted - Whether to mute
*/
async function setMute(muted) {
return await callAPI('/mute', 'POST', { muted });
}
/**
* Set deafen state explicitly
* @param {boolean} deafened - Whether to deafen
*/
async function setDeafen(deafened) {
return await callAPI('/deafen', 'POST', { deafened });
}
/**
* Change channel
*/
async function changeChannel(roomId) {
return await callAPI('/channel', 'POST', { roomId });
}
/**
* Send message to current room
*/
async function sendMessage(message) {
return await callAPI('/message/current', 'POST', { message });
}
/**
* Get list of channels
*/
async function getChannels() {
return await callAPI('/channels');
}
/**
* Update button state based on status
*/
function updateButtonStates(status) {
actionStates.forEach((state, context) => {
if (state.action === ACTIONS.TOGGLE_MUTE) {
const newState = status.muted ? 1 : 0;
if (state.currentState !== newState) {
setState(context, newState);
state.currentState = newState;
}
} else if (state.action === ACTIONS.TOGGLE_DEAFEN) {
const newState = status.deafened ? 1 : 0;
if (state.currentState !== newState) {
setState(context, newState);
state.currentState = newState;
}
} else if (state.action === ACTIONS.GET_STATUS) {
const title = `${status.muted ? 'M' : ''}${status.deafened ? 'D' : ''}${!status.muted && !status.deafened ? 'OK' : ''}`;
setTitle(context, title);
}
});
}
/**
* Start polling for status updates
*/
function startStatusPolling() {
if (statusPollInterval) {
return;
}
statusPollInterval = setInterval(async () => {
try {
const status = await getStatus();
updateButtonStates(status);
} catch (error) {
// Silently fail - server might not be running
}
}, POLL_INTERVAL);
}
/**
* Stop polling for status updates
*/
function stopStatusPolling() {
if (statusPollInterval) {
clearInterval(statusPollInterval);
statusPollInterval = null;
}
}
/**
* Send command to Stream Deck
*/
function sendToStreamDeck(event, context, payload = {}) {
if (websocket && websocket.readyState === WebSocket.OPEN) {
const message = {
event,
context,
...payload
};
websocket.send(JSON.stringify(message));
}
}
/**
* Set button state
*/
function setState(context, state) {
sendToStreamDeck('setState', context, { payload: { state } });
}
/**
* Set button title
*/
function setTitle(context, title) {
sendToStreamDeck('setTitle', context, { payload: { title } });
}
/**
* Show alert on button
*/
function showAlert(context) {
if (context) {
sendToStreamDeck('showAlert', context);
}
}
/**
* Show OK on button
*/
function showOk(context) {
sendToStreamDeck('showOk', context);
}
/**
* Handle button press
*/
async function handleKeyDown(context, settings, action) {
try {
switch (action) {
case ACTIONS.TOGGLE_MUTE: {
// Get current state and set opposite
const status = await getStatus();
await setMute(!status.muted);
showOk(context);
break;
}
case ACTIONS.TOGGLE_DEAFEN: {
// Get current state and set opposite
const status = await getStatus();
await setDeafen(!status.deafened);
showOk(context);
break;
}
case ACTIONS.CHANGE_CHANNEL:
if (settings.roomId) {
await changeChannel(settings.roomId);
showOk(context);
} else {
showAlert(context);
}
break;
case ACTIONS.SEND_MESSAGE:
if (settings.message) {
await sendMessage(settings.message);
showOk(context);
} else {
showAlert(context);
}
break;
case ACTIONS.GET_STATUS:
const status = await getStatus();
updateButtonStates(status);
showOk(context);
break;
}
// Force immediate status update
const status = await getStatus();
updateButtonStates(status);
} catch (error) {
showAlert(context);
}
}
/**
* Handle action appearing
*/
function handleWillAppear(context, settings, action) {
actionStates.set(context, {
action,
settings,
currentState: 0
});
// Get initial state
getStatus()
.then(status => updateButtonStates(status))
.catch(() => {});
}
/**
* Handle action disappearing
*/
function handleWillDisappear(context) {
actionStates.delete(context);
}
/**
* Handle settings change
*/
function handleDidReceiveSettings(context, settings, action) {
const state = actionStates.get(context);
if (state) {
state.settings = settings;
}
}
/**
* Handle property inspector request
*/
function handleSendToPlugin(context, action, payload) {
// Handle requests from property inspector
if (payload.event === 'getChannels') {
getChannels()
.then(channels => {
sendToStreamDeck('sendToPropertyInspector', context, {
action,
payload: {
event: 'channelList',
channels
}
});
})
.catch(error => {
console.error('Failed to get channels:', error);
});
}
}
/**
* Setup WebSocket connection to Stream Deck
*/
function connectElgatoStreamDeckSocket(inPort, inPluginUUID, inRegisterEvent, inInfo) {
pluginUUID = inPluginUUID;
websocket = new WebSocket(`ws://127.0.0.1:${inPort}`);
websocket.onopen = () => {
const registerPayload = {
event: inRegisterEvent,
uuid: inPluginUUID
};
websocket.send(JSON.stringify(registerPayload));
// Start status polling
startStatusPolling();
};
websocket.onmessage = (evt) => {
try {
const message = JSON.parse(evt.data);
const { event, action, context, payload } = message;
const settings = payload?.settings || {};
switch (event) {
case 'keyDown':
handleKeyDown(context, settings, action);
break;
case 'willAppear':
handleWillAppear(context, settings, action);
break;
case 'willDisappear':
handleWillDisappear(context);
break;
case 'didReceiveSettings':
handleDidReceiveSettings(context, settings, action);
break;
case 'sendToPlugin':
handleSendToPlugin(context, action, payload);
break;
}
} catch (error) {
console.error('WebSocket message error:', error);
}
};
websocket.onerror = (error) => {
console.error('WebSocket error:', error);
};
websocket.onclose = () => {
stopStatusPolling();
};
}

View File

@@ -0,0 +1,178 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Change Channel Settings</title>
<style>
body {
margin: 10px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-size: 13px;
color: #d0d0d0;
background-color: #2d2d2d;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
select, input {
width: 100%;
padding: 6px 8px;
margin-bottom: 15px;
background-color: #3d3d3d;
border: 1px solid #555;
border-radius: 4px;
color: #d0d0d0;
font-size: 13px;
}
select:focus, input:focus {
outline: none;
border-color: #0e99e0;
}
.info {
padding: 8px;
background-color: #3d3d3d;
border-left: 3px solid #0e99e0;
margin-bottom: 15px;
font-size: 12px;
}
.loading {
color: #888;
font-style: italic;
}
option {
background-color: #3d3d3d;
color: #d0d0d0;
}
</style>
</head>
<body>
<div class="info">
Select a room/channel to switch to when this button is pressed.
</div>
<label for="roomId">Channel/Room:</label>
<select id="roomId">
<option value="">Loading channels...</option>
</select>
<label for="customRoomId">Or enter Room ID manually:</label>
<input type="text" id="customRoomId" placeholder="!abc123:matrix.org" />
<script>
let websocket = null;
let uuid = null;
let actionInfo = null;
let currentSettings = {};
function connectElgatoStreamDeckSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) {
uuid = inUUID;
actionInfo = JSON.parse(inActionInfo);
currentSettings = actionInfo.payload.settings || {};
websocket = new WebSocket('ws://127.0.0.1:' + inPort);
websocket.onopen = function() {
const json = {
event: inRegisterEvent,
uuid: inUUID
};
websocket.send(JSON.stringify(json));
// Request channel list
sendToPlugin({ event: 'getChannels' });
};
websocket.onmessage = function(evt) {
const message = JSON.parse(evt.data);
if (message.event === 'sendToPropertyInspector') {
if (message.payload.event === 'channelList') {
populateChannels(message.payload.channels);
}
}
};
// Set current values
if (currentSettings.roomId) {
document.getElementById('customRoomId').value = currentSettings.roomId;
}
}
function sendToPlugin(payload) {
if (websocket && websocket.readyState === WebSocket.OPEN) {
const message = {
action: actionInfo.action,
event: 'sendToPlugin',
context: uuid,
payload: payload
};
websocket.send(JSON.stringify(message));
}
}
function saveSettings() {
const settings = {
roomId: document.getElementById('customRoomId').value || document.getElementById('roomId').value
};
if (websocket && websocket.readyState === WebSocket.OPEN) {
const message = {
event: 'setSettings',
context: uuid,
payload: settings
};
websocket.send(JSON.stringify(message));
}
}
function populateChannels(channels) {
const select = document.getElementById('roomId');
select.innerHTML = '<option value="">-- Select a channel --</option>';
if (!channels || channels.length === 0) {
select.innerHTML = '<option value="">No channels available</option>';
return;
}
// Group by server
const grouped = {};
channels.forEach(channel => {
if (!grouped[channel.server]) {
grouped[channel.server] = [];
}
grouped[channel.server].push(channel);
});
// Add options grouped by server
Object.keys(grouped).sort().forEach(server => {
const optgroup = document.createElement('optgroup');
optgroup.label = server;
grouped[server].forEach(channel => {
const option = document.createElement('option');
option.value = channel.roomId;
option.textContent = channel.name;
if (currentSettings.roomId === channel.roomId) {
option.selected = true;
}
optgroup.appendChild(option);
});
select.appendChild(optgroup);
});
}
// Event listeners
document.getElementById('roomId').addEventListener('change', saveSettings);
document.getElementById('customRoomId').addEventListener('input', saveSettings);
</script>
</body>
</html>

View File

@@ -0,0 +1,145 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Send Message Settings</title>
<style>
body {
margin: 10px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
font-size: 13px;
color: #d0d0d0;
background-color: #2d2d2d;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
textarea {
width: 100%;
padding: 6px 8px;
margin-bottom: 15px;
background-color: #3d3d3d;
border: 1px solid #555;
border-radius: 4px;
color: #d0d0d0;
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
resize: vertical;
min-height: 80px;
}
textarea:focus {
outline: none;
border-color: #0e99e0;
}
.info {
padding: 8px;
background-color: #3d3d3d;
border-left: 3px solid #0e99e0;
margin-bottom: 15px;
font-size: 12px;
}
.examples {
padding: 8px;
background-color: #3d3d3d;
border-left: 3px solid #666;
margin-top: 10px;
font-size: 11px;
}
.examples h4 {
margin: 0 0 8px 0;
font-size: 12px;
color: #aaa;
}
.examples ul {
margin: 0;
padding-left: 20px;
}
.examples li {
margin-bottom: 4px;
color: #888;
}
</style>
</head>
<body>
<div class="info">
Enter the message to send to the currently active room when this button is pressed.
</div>
<label for="message">Message:</label>
<textarea id="message" placeholder="Type your message here..."></textarea>
<div class="examples">
<h4>Example Messages:</h4>
<ul>
<li>BRB! (Be Right Back)</li>
<li>AFK for a few minutes</li>
<li>On my way!</li>
<li>Thanks everyone!</li>
</ul>
</div>
<script>
let websocket = null;
let uuid = null;
let actionInfo = null;
let currentSettings = {};
let saveTimeout = null;
function connectElgatoStreamDeckSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) {
uuid = inUUID;
actionInfo = JSON.parse(inActionInfo);
currentSettings = actionInfo.payload.settings || {};
websocket = new WebSocket('ws://127.0.0.1:' + inPort);
websocket.onopen = function() {
const json = {
event: inRegisterEvent,
uuid: inUUID
};
websocket.send(JSON.stringify(json));
};
// Set current value
if (currentSettings.message) {
document.getElementById('message').value = currentSettings.message;
}
}
function saveSettings() {
// Debounce saving
if (saveTimeout) {
clearTimeout(saveTimeout);
}
saveTimeout = setTimeout(() => {
const settings = {
message: document.getElementById('message').value
};
if (websocket && websocket.readyState === WebSocket.OPEN) {
const message = {
event: 'setSettings',
context: uuid,
payload: settings
};
websocket.send(JSON.stringify(message));
}
}, 300);
}
// Event listener
document.getElementById('message').addEventListener('input', saveSettings);
</script>
</body>
</html>

View File

@@ -0,0 +1,18 @@
{
"name": "paarrot-streamdeck-plugin",
"version": "1.0.0",
"description": "Elgato Stream Deck plugin for Paarrot voice chat app",
"scripts": {
"build": "node build.js",
"validate": "node validate.js"
},
"keywords": [
"stream-deck",
"elgato",
"paarrot",
"voice-chat",
"matrix"
],
"author": "Paarrot",
"license": "MIT"
}

View File

@@ -0,0 +1,156 @@
/**
* Validation script for Paarrot Stream Deck Plugin
* Checks plugin structure and manifest
*/
const fs = require('fs');
const path = require('path');
const PLUGIN_DIR = 'com.paarrot.streamdeck.sdPlugin';
let errors = 0;
let warnings = 0;
function error(message) {
console.error(`❌ ERROR: ${message}`);
errors++;
}
function warn(message) {
console.warn(`⚠️ WARNING: ${message}`);
warnings++;
}
function success(message) {
console.log(`${message}`);
}
console.log('Validating Paarrot Stream Deck Plugin...\n');
// Check plugin directory exists
if (!fs.existsSync(PLUGIN_DIR)) {
error(`Plugin directory '${PLUGIN_DIR}' not found`);
process.exit(1);
}
// Check manifest.json
const manifestPath = path.join(PLUGIN_DIR, 'manifest.json');
if (!fs.existsSync(manifestPath)) {
error('manifest.json not found');
} else {
success('manifest.json exists');
try {
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
// Validate required fields
const requiredFields = ['Actions', 'Author', 'CodePath', 'Description', 'Name', 'Version', 'SDKVersion'];
requiredFields.forEach(field => {
if (!manifest[field]) {
error(`manifest.json missing required field: ${field}`);
}
});
if (manifest.Actions && Array.isArray(manifest.Actions)) {
success(`Found ${manifest.Actions.length} actions`);
// Validate each action
manifest.Actions.forEach((action, index) => {
const actionName = action.Name || `Action ${index}`;
if (!action.UUID) {
error(`${actionName}: Missing UUID`);
}
if (!action.Icon) {
error(`${actionName}: Missing Icon`);
} else {
// Check if icon file exists
const iconPath = path.join(PLUGIN_DIR, `${action.Icon}.svg`);
const iconPathPng = path.join(PLUGIN_DIR, `${action.Icon}.png`);
if (!fs.existsSync(iconPath) && !fs.existsSync(iconPathPng)) {
warn(`${actionName}: Icon file not found: ${action.Icon}`);
}
}
if (!action.States || !Array.isArray(action.States) || action.States.length === 0) {
error(`${actionName}: Missing or invalid States`);
} else {
action.States.forEach((state, stateIndex) => {
if (!state.Image) {
warn(`${actionName} State ${stateIndex}: Missing Image`);
} else {
const imagePath = path.join(PLUGIN_DIR, `${state.Image}.svg`);
const imagePathPng = path.join(PLUGIN_DIR, `${state.Image}.png`);
if (!fs.existsSync(imagePath) && !fs.existsSync(imagePathPng)) {
warn(`${actionName} State ${stateIndex}: Image file not found: ${state.Image}`);
}
}
});
}
if (action.PropertyInspectorPath) {
const piPath = path.join(PLUGIN_DIR, action.PropertyInspectorPath);
if (!fs.existsSync(piPath)) {
error(`${actionName}: PropertyInspector file not found: ${action.PropertyInspectorPath}`);
}
}
});
}
} catch (e) {
error(`manifest.json parse error: ${e.message}`);
}
}
// Check plugin files
const pluginPath = path.join(PLUGIN_DIR, 'plugin', 'index.html');
if (!fs.existsSync(pluginPath)) {
error('plugin/index.html not found');
} else {
success('plugin/index.html exists');
}
const pluginJsPath = path.join(PLUGIN_DIR, 'plugin', 'index.js');
if (!fs.existsSync(pluginJsPath)) {
error('plugin/index.js not found');
} else {
success('plugin/index.js exists');
}
// Check property inspectors
const piDir = path.join(PLUGIN_DIR, 'propertyinspector');
if (fs.existsSync(piDir)) {
const piFiles = fs.readdirSync(piDir);
success(`Found ${piFiles.length} property inspector file(s)`);
} else {
warn('propertyinspector directory not found');
}
// Check images directory
const imagesDir = path.join(PLUGIN_DIR, 'images');
if (fs.existsSync(imagesDir)) {
const imageFiles = fs.readdirSync(imagesDir);
success(`Found ${imageFiles.length} image file(s)`);
if (imageFiles.length === 0) {
warn('images directory is empty');
}
} else {
error('images directory not found');
}
// Summary
console.log('\n' + '='.repeat(50));
if (errors === 0 && warnings === 0) {
console.log('✓ Plugin validation passed!');
process.exit(0);
} else {
console.log(`Found ${errors} error(s) and ${warnings} warning(s)`);
if (errors > 0) {
console.log('\nPlease fix errors before building the plugin.');
process.exit(1);
} else {
console.log('\nPlugin can be built, but consider fixing warnings.');
process.exit(0);
}
}

26
test-electron.js Normal file
View File

@@ -0,0 +1,26 @@
const { app, BrowserWindow } = require('electron');
console.log('TEST: Electron starting...');
app.whenReady().then(() => {
console.log('TEST: App ready, creating window...');
const win = new BrowserWindow({
width: 800,
height: 600,
title: 'TEST WINDOW',
show: true,
center: true
});
console.log('TEST: Window created, loading content...');
win.loadURL('https://www.google.com');
win.on('ready-to-show', () => {
console.log('TEST: Window ready-to-show fired');
});
win.on('show', () => {
console.log('TEST: Window shown!');
});
});