Compare commits

195 Commits

Author SHA1 Message Date
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
GitHub Actions
46d2969cfb chore: bump version to 4.11.24 [skip ci] 2026-02-24 11:29:50 +00:00
31225233c5 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 10s
Build / build-windows (push) Failing after 59s
Build / create-release (push) Has been skipped
2026-02-24 22:29:41 +11:00
796659318e refactor: update project metadata and remove arm64 support from build process 2026-02-24 22:29:20 +11:00
GitHub Actions
bd544193be chore: bump version to 4.11.24 [skip ci] 2026-02-24 11:05:53 +00:00
f0428b7bfc feat: update Squirrel setup paths and add arm64 support for uploads
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-windows (push) Successful in 5m12s
Build / build-linux (push) Successful in 5m36s
Build / create-release (push) Successful in 31s
2026-02-24 22:05:44 +11:00
GitHub Actions
0073678b1f chore: bump version to 4.11.23 [skip ci] 2026-02-24 10:58:41 +00:00
933f83aca9 fix: remove invalid squirrelWindows properties
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Failing after 4m39s
Build / build-linux (push) Successful in 5m35s
Build / create-release (push) Successful in 18s
2026-02-24 21:58:29 +11:00
GitHub Actions
fe99952fd5 chore: bump version to 4.11.22 [skip ci] 2026-02-24 10:49:16 +00:00
litruv
6b160b76de 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 1m43s
Build / build-windows (push) Failing after 3m37s
Build / create-release (push) Has been skipped
2026-02-24 21:49:07 +11:00
litruv
39d33b9a3f feat: update build process for Squirrel installer and add new upload steps 2026-02-24 21:49:06 +11:00
GitHub Actions
b01a2b69b3 chore: bump version to 4.11.21 [skip ci] 2026-02-21 16:58:23 +00:00
litruv
83ee6d868b 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-windows (push) Successful in 3m11s
Build / build-linux (push) Successful in 5m32s
Build / create-release (push) Successful in 21s
2026-02-22 03:58:17 +11:00
litruv
8ee4250231 chore: mark subproject as dirty in cinny 2026-02-22 03:58:14 +11:00
GitHub Actions
91e92e8b7d chore: bump version to 4.11.20 [skip ci] 2026-02-21 16:41:20 +00:00
litruv
d1a458feb3 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-windows (push) Successful in 3m10s
Build / build-linux (push) Successful in 5m23s
Build / create-release (push) Successful in 20s
2026-02-22 03:41:13 +11:00
litruv
b27669f98f feat: add clipboard text writing functionality and update config 2026-02-22 03:41:10 +11:00
GitHub Actions
1e9599ee1a chore: bump version to 4.11.19 [skip ci] 2026-02-21 16:18:03 +00:00
litruv
6115b7fe6f 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-windows (push) Successful in 3m11s
Build / build-linux (push) Successful in 5m35s
Build / create-release (push) Successful in 21s
2026-02-22 03:17:54 +11:00
litruv
7336454a05 chore: update subproject commit reference 2026-02-22 03:17:52 +11:00
GitHub Actions
8a2d6d715e chore: bump version to 4.11.18 [skip ci] 2026-02-21 14:53:28 +00:00
72cb5b6fe4 Update README.md
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m11s
Build / build-linux (push) Successful in 5m30s
Build / create-release (push) Successful in 19s
2026-02-22 00:53:22 +10:00
GitHub Actions
47ba333368 chore: bump version to 4.11.17 [skip ci] 2026-02-21 14:45:15 +00:00
litruv
70ed4513d8 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-windows (push) Successful in 3m10s
Build / build-linux (push) Successful in 5m29s
Build / create-release (push) Successful in 46s
2026-02-22 01:45:08 +11:00
litruv
feb3188516 feat: add publish URL for generic provider in electron-builder configuration 2026-02-22 01:45:06 +11:00
GitHub Actions
252d515970 chore: bump version to 4.11.16 [skip ci] 2026-02-21 14:40:00 +00:00
litruv
853a1db0bb Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Failing after 3m0s
Build / build-linux (push) Failing after 4m50s
Build / create-release (push) Has been skipped
2026-02-22 01:39:53 +11:00
litruv
015769bdaf feat: implement Gitea update feed configuration and enhance auto-updater logic 2026-02-22 01:39:51 +11:00
GitHub Actions
7663e1d1c0 chore: bump version to 4.11.15 [skip ci] 2026-02-21 14:30:49 +00:00
1b06e46b06 Update README.md
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m10s
Build / build-linux (push) Successful in 5m26s
Build / create-release (push) Successful in 19s
2026-02-22 00:30:41 +10:00
GitHub Actions
033d323b6f chore: bump version to 4.11.14 [skip ci] 2026-02-21 14:20:09 +00:00
litruv
cae4296b92 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-windows (push) Successful in 3m10s
Build / build-linux (push) Successful in 5m31s
Build / create-release (push) Successful in 18s
2026-02-22 01:20:02 +11:00
litruv
232aa3daa7 fix: update subproject commit and change publish provider to generic 2026-02-22 01:19:59 +11:00
GitHub Actions
170947fe1e chore: bump version to 4.11.13 [skip ci] 2026-02-21 14:09:16 +00:00
2d73d3a043 Update README.md
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-windows (push) Successful in 3m12s
Build / build-linux (push) Successful in 5m25s
Build / create-release (push) Successful in 23s
2026-02-22 00:09:08 +10:00
GitHub Actions
342ca4cfe0 chore: bump version to 4.11.12 [skip ci] 2026-02-21 13:59:26 +00:00
litruv
b570d3e992 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-windows (push) Successful in 3m11s
Build / build-linux (push) Successful in 5m34s
Build / create-release (push) Successful in 20s
2026-02-22 00:59:19 +11:00
litruv
9dd115698b fix: mark subproject as dirty in cinny 2026-02-22 00:59:16 +11:00
GitHub Actions
c7897d1516 chore: bump version to 4.11.11 [skip ci] 2026-02-21 13:41:48 +00:00
litruv
2063350683 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-windows (push) Successful in 3m11s
Build / build-linux (push) Successful in 5m26s
Build / create-release (push) Successful in 20s
2026-02-22 00:41:42 +11:00
litruv
f6f72d4b81 fix: mark subproject as dirty in cinny 2026-02-22 00:41:39 +11:00
GitHub Actions
0bb14b5f22 chore: bump version to 4.11.10 [skip ci] 2026-02-21 13:27:34 +00:00
litruv
d636fc0708 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-windows (push) Successful in 3m20s
Build / build-linux (push) Successful in 5m27s
Build / create-release (push) Successful in 23s
2026-02-22 00:27:26 +11:00
litruv
62392d3197 feat: add AppImage upload step to build workflow 2026-02-22 00:27:25 +11:00
GitHub Actions
f652ce8c61 chore: bump version to 4.11.9 [skip ci] 2026-02-21 13:13:34 +00:00
litruv
324b9ed562 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-windows (push) Successful in 3m55s
Build / build-linux (push) Successful in 5m12s
Build / create-release (push) Successful in 16s
2026-02-22 00:13:27 +11:00
litruv
3c16a796d6 stuff 2026-02-22 00:13:19 +11:00
GitHub Actions
baa82f1f0f chore: bump version to 4.11.8 [skip ci] 2026-02-21 13:13:17 +00:00
litruv
e38c9d5dcc Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Has started running
Build / build-linux (push) Has been cancelled
Build / create-release (push) Has been cancelled
2026-02-22 00:13:09 +11:00
litruv
e076bde9b0 feat: add metadata upload for Windows and Linux updates, update release file handling, and implement app version retrieval 2026-02-22 00:13:00 +11:00
GitHub Actions
f3b118aacc chore: bump version to 4.11.7 [skip ci] 2026-02-21 12:57:30 +00:00
litruv
00080782a3 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-windows (push) Successful in 3m12s
Build / build-linux (push) Successful in 5m3s
Build / create-release (push) Successful in 16s
2026-02-21 23:57:22 +11:00
litruv
413957b6d3 chore: update package version to 4.11.5 in package-lock.json 2026-02-21 23:55:17 +11:00
GitHub Actions
be1781518c chore: bump version to 4.11.6 [skip ci] 2026-02-21 11:24:54 +00:00
litruv
e105a1fd6b feat: add server field to room details in API documentation
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m13s
Build / build-linux (push) Successful in 5m3s
Build / create-release (push) Successful in 17s
2026-02-21 22:24:30 +11:00
GitHub Actions
0768ed0116 chore: bump version to 4.11.5 [skip ci] 2026-02-21 11:06:05 +00:00
litruv
24bed419a2 feat: add auto-updating Postman collection for API testing
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m15s
Build / build-linux (push) Successful in 4m56s
Build / create-release (push) Successful in 19s
2026-02-21 22:05:57 +11:00
GitHub Actions
f23a07384c chore: bump version to 4.11.4 [skip ci] 2026-02-21 10:43:50 +00:00
litruv
4e782409ee feat: Add Paarrot API server with endpoints for controlling app features
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m14s
Build / build-linux (push) Successful in 4m58s
Build / create-release (push) Successful in 16s
- Implemented API server using Express, including endpoints for health check, status, mute, deafen, channel management, and message sending.
- Added middleware for CORS and JSON body parsing.
- Created test scripts (Node.js and Bash) for API endpoint verification.
- Documented API usage and integration in new API documentation files.
- Updated package.json to include necessary dependencies (express, cors, body-parser).
2026-02-21 21:43:36 +11:00
GitHub Actions
b9e8fec8d4 chore: bump version to 4.11.3 [skip ci] 2026-02-21 09:06:23 +00:00
litruv
59adbcbdf4 fix: use correct icon path for tray/window in packaged app
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m9s
Build / build-linux (push) Successful in 5m0s
Build / create-release (push) Successful in 17s
2026-02-21 20:06:15 +11:00
GitHub Actions
448c4c2dfd chore: bump version to 4.11.2 [skip ci] 2026-02-21 08:54:52 +00:00
litruv
b926ea9c88 chore: update cinny submodule with Electron updater fix and debug logging
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m7s
Build / build-linux (push) Successful in 4m56s
Build / create-release (push) Successful in 15s
2026-02-21 19:54:45 +11:00
GitHub Actions
bcdeaf6e48 chore: bump version to 4.11.1 [skip ci] 2026-02-21 08:38:04 +00:00
litruv
7945e2d0be chore: bump version to 4.11.0 (next build will be 4.11.1)
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m2s
Build / build-linux (push) Successful in 4m58s
Build / create-release (push) Successful in 18s
2026-02-21 19:37:55 +11:00
litruv
b839d5e98a chore: update cinny submodule with relative base path fix
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Has started running
Build / create-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-02-21 19:37:24 +11:00
litruv
7b65b5a8c2 fix: clean dist folders before building to prevent stale files
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-windows (push) Successful in 3m6s
Build / build-linux (push) Successful in 4m59s
Build / create-release (push) Successful in 17s
2026-02-21 19:27:24 +11:00
litruv
dbf70fd9c4 fix: remove AppImage and arm64 builds, keep only deb/rpm x64
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m6s
Build / build-linux (push) Successful in 4m54s
Build / create-release (push) Successful in 17s
2026-02-21 19:15:32 +11:00
litruv
775f18f601 fix: correct artifact upload paths for Linux builds
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m8s
Build / build-linux (push) Successful in 7m14s
Build / create-release (push) Successful in 34s
2026-02-21 18:59:38 +11:00
litruv
58b911fbd5 fix: add homepage, author email, and maintainer for deb builds
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m7s
Build / build-linux (push) Successful in 5m40s
Build / create-release (push) Successful in 9s
2026-02-21 18:50:30 +11:00
litruv
ebe9fa3983 fix: remove license field from appImage config
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-linux (push) Failing after 2m5s
Build / build-windows (push) Successful in 3m6s
Build / create-release (push) Successful in 9s
2026-02-21 18:25:05 +11:00
litruv
72193d2490 fix: add linux build scripts, remove mac builds, disable auto-publish
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Failing after 1m52s
Build / build-windows (push) Successful in 3m9s
Build / create-release (push) Successful in 10s
2026-02-21 18:14:06 +11:00
litruv
62c42f7b63 feat: add post-installation and post-removal scripts for package management 2026-02-21 18:12:59 +11:00
litruv
3df5432127 fix: use desktop.entry for electron-builder 26.x
Some checks failed
Build / increment-version (push) Successful in 7s
Build / build-linux (push) Failing after 1m39s
Build / build-windows (push) Failing after 3m21s
Build / create-release (push) Has been skipped
2026-02-21 17:58:50 +11:00
litruv
1e455a00f2 fix: handle submodules manually on Windows to avoid path issue 2026-02-21 17:57:32 +11:00
litruv
7508ebacab feat: add default capabilities configuration and main entry point
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Failing after 24s
Build / build-linux (push) Failing after 1m45s
Build / create-release (push) Has been skipped
2026-02-21 17:51:17 +11:00
litruv
22f5c5e7e4 electron 2026-02-21 17:50:50 +11:00
litruv
caf1869e3e Refactor code structure for improved readability and maintainability
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 3m5s
Build / build-linux (push) Successful in 11m26s
Build / build-android (push) Successful in 9m19s
Build / create-release (push) Successful in 14s
2026-02-21 16:16:18 +11:00
GitHub Actions
c112fd23b0 chore: bump version to 4.10.26 [skip ci] 2026-02-21 01:17:01 +00:00
1533b38cac Update README.md
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-windows (push) Successful in 4m2s
Build / build-linux (push) Successful in 11m59s
Build / build-android (push) Successful in 10m15s
Build / create-release (push) Successful in 15s
2026-02-21 11:16:53 +10:00
GitHub Actions
3ba3c0ca82 chore: bump version to 4.10.25 [skip ci] 2026-02-20 14:28:21 +00:00
b44917e52d 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-windows (push) Successful in 4m14s
Build / build-linux (push) Successful in 11m41s
Build / build-android (push) Successful in 11m45s
Build / create-release (push) Successful in 16s
2026-02-21 01:28:14 +11:00
158da6f608 fix: add missing updater permissions in capabilities configuration 2026-02-21 01:28:13 +11:00
GitHub Actions
55679915c5 chore: bump version to 4.10.24 [skip ci] 2026-02-20 07:41:23 +00:00
9e6417c47e fix: update subproject commit reference
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 6m13s
Build / build-linux (push) Successful in 11m19s
Build / build-android (push) Successful in 23m19s
Build / create-release (push) Successful in 14s
2026-02-20 18:41:14 +11:00
f5515b421d Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / increment-version (push) Has been cancelled
2026-02-20 18:41:10 +11:00
3b0092d972 feat: add updater permissions to default capability configuration 2026-02-20 18:40:56 +11:00
GitHub Actions
eb806e4054 chore: bump version to 4.10.23 [skip ci] 2026-02-20 07:06:09 +00:00
5d9304e3ba feat: add MSI signature handling and upload steps in build workflow
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 6m14s
Build / build-linux (push) Successful in 11m59s
Build / build-android (push) Successful in 25m31s
Build / create-release (push) Successful in 15s
2026-02-20 18:05:59 +11:00
GitHub Actions
9e74e65d2d chore: bump version to 4.10.22 [skip ci] 2026-02-20 05:59:10 +00:00
a00e257845 fix: update Windows MSI download URL in build workflow
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 6m48s
Build / build-linux (push) Successful in 11m19s
Build / build-android (push) Successful in 26m12s
Build / create-release (push) Successful in 14s
2026-02-20 16:59:01 +11:00
GitHub Actions
d62a964758 chore: bump version to 4.10.21 [skip ci] 2026-02-20 05:28:12 +00:00
dafd8c538e feat: add YouTube streaming support using yt-dlp and update CSP for frame sources
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 5m24s
Build / build-linux (push) Successful in 11m15s
Build / build-android (push) Successful in 16m3s
Build / create-release (push) Successful in 13s
2026-02-20 16:28:00 +11:00
GitHub Actions
d51c787c2a chore: bump version to 4.10.20 [skip ci] 2026-02-20 04:29:13 +00:00
e77eda66e9 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-windows (push) Successful in 6m37s
Build / build-linux (push) Successful in 11m29s
Build / build-android (push) Successful in 28m47s
Build / create-release (push) Successful in 14s
2026-02-20 15:29:05 +11:00
ba636fe6fc fix: update build script to sort files by modification time for better accuracy 2026-02-20 15:29:02 +11:00
GitHub Actions
d94a1e66b8 chore: bump version to 4.10.19 [skip ci] 2026-02-20 04:18:20 +00:00
809e06f9c9 fix: update version to 4.10.2 and mark subproject as dirty
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 7m22s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-02-20 15:18:10 +11:00
GitHub Actions
f67a3cd498 chore: bump version to 4.10.18 [skip ci] 2026-02-20 02:27:31 +00:00
8739ddd5c4 fix: update version to 4.10.17 and mark subproject as dirty
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 7m32s
Build / build-linux (push) Successful in 11m21s
Build / build-android (push) Successful in 26m3s
Build / create-release (push) Successful in 14s
2026-02-20 13:27:21 +11:00
GitHub Actions
fb1430ee59 chore: bump version to 4.10.17 [skip ci] 2026-02-19 14:39:50 +00:00
32ad73fd65 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-windows (push) Successful in 4m55s
Build / build-linux (push) Successful in 11m17s
Build / build-android (push) Successful in 13m30s
Build / create-release (push) Successful in 16s
2026-02-20 01:39:41 +11:00
22ac1bf4b7 fix: mark subproject as dirty to indicate uncommitted changes 2026-02-20 01:39:39 +11:00
GitHub Actions
93043e39cc chore: bump version to 4.10.16 [skip ci] 2026-02-19 13:46:28 +00:00
0eae03cb60 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-windows (push) Successful in 4m7s
Build / build-linux (push) Successful in 11m13s
Build / build-android (push) Successful in 12m2s
Build / create-release (push) Successful in 1m23s
2026-02-20 00:46:19 +11:00
3aac8e79db feat: update short description to reflect application theme 2026-02-20 00:46:18 +11:00
GitHub Actions
08cd0d0a30 chore: bump version to 4.10.15 [skip ci] 2026-02-19 13:23:39 +00:00
176f1e1604 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-windows (push) Successful in 5m9s
Build / build-linux (push) Successful in 11m14s
Build / build-android (push) Successful in 13m47s
Build / create-release (push) Successful in 18s
2026-02-20 00:23:31 +11:00
b2f7697865 feat: enhance permissions for webview and notifications in default capabilities 2026-02-20 00:23:25 +11:00
GitHub Actions
bd7db55251 chore: bump version to 4.10.14 [skip ci] 2026-02-19 11:11:10 +00:00
cb001319ae chore: update dependencies in Cargo.lock
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 4m19s
Build / build-linux (push) Successful in 11m17s
Build / build-android (push) Successful in 12m39s
Build / create-release (push) Successful in 14s
2026-02-19 22:10:57 +11:00
GitHub Actions
5248194ceb chore: bump version to 4.10.13 [skip ci] 2026-02-19 10:57:45 +00:00
f57f127951 fix: update subproject commit reference
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Failing after 1m33s
Build / build-linux (push) Failing after 1m54s
Build / build-android (push) Failing after 1m23s
Build / create-release (push) Has been skipped
2026-02-19 21:57:33 +11:00
3ecf1a5f03 feat: add HTTP plugin and permissions for fetch operations
- Updated capabilities.json to include new HTTP permissions.
- Enhanced desktop-schema.json and windows-schema.json with detailed fetch operation permissions.
- Integrated the HTTP plugin in lib.rs to enable HTTP functionalities.
2026-02-19 21:57:17 +11:00
GitHub Actions
f7e2cc8250 chore: bump version to 4.10.12 [skip ci] 2026-02-19 09:43:51 +00:00
187534dd6c feat: enhance capabilities and permissions for dialog and process management
All checks were successful
Build / increment-version (push) Successful in 7s
Build / build-windows (push) Successful in 4m37s
Build / build-linux (push) Successful in 12m6s
Build / build-android (push) Successful in 11m49s
Build / create-release (push) Successful in 16s
- Updated capabilities.json to include new permissions for dialog and process features.
- Expanded desktop-schema.json and windows-schema.json with detailed dialog and process permissions, including allow/deny options for various commands.
- Integrated dialog and process plugins in lib.rs to enable new functionalities in the application.
2026-02-19 20:43:39 +11:00
GitHub Actions
9b20339c57 chore: bump version to 4.10.11 [skip ci] 2026-02-19 09:23:56 +00:00
0194d6acfd fix: mark subproject as dirty to indicate uncommitted changes
Some checks failed
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 4m14s
Build / build-linux (push) Successful in 10m44s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-02-19 20:23:46 +11:00
GitHub Actions
0b4b8f11e7 chore: bump version to 4.10.10 [skip ci] 2026-02-19 09:03:29 +00:00
a0aee6ddbd feat: add version bumping and release update functionality in build workflow
All checks were successful
Build / increment-version (push) Successful in 6s
Build / build-windows (push) Successful in 5m4s
Build / build-linux (push) Successful in 10m40s
Build / build-android (push) Successful in 13m1s
Build / create-release (push) Successful in 15s
2026-02-19 20:03:19 +11:00
ce3c236e2e feat: implement create or update release functionality in build workflow
All checks were successful
Build / build-windows (push) Successful in 4m20s
Build / build-linux (push) Successful in 10m41s
Build / build-android (push) Successful in 11m31s
Build / create-release (push) Successful in 12s
2026-02-19 19:32:17 +11:00
41159d206c fix: update build workflow to ensure proper file copying and directory creation
Some checks failed
Build / build-windows (push) Successful in 5m49s
Build / build-linux (push) Successful in 10m29s
Build / build-android (push) Successful in 12m13s
Build / create-release (push) Failing after 11s
2026-02-19 19:11:03 +11:00
87bb7347e3 fix: adjust submodule checkout settings for improved compatibility
Some checks failed
Build / build-windows (push) Successful in 4m45s
Build / build-linux (push) Successful in 10m41s
Build / build-android (push) Has started running
Build / create-release (push) Has been cancelled
2026-02-19 18:43:58 +11:00
11cdf0acf2 refactor: remove version check job and streamline release process
Some checks failed
Build / build-windows (push) Successful in 6m5s
Build / build-android (push) Failing after 27s
Build / build-linux (push) Successful in 10m41s
Build / create-release (push) Successful in 15s
2026-02-19 18:34:01 +11:00
c69c5f3c9c Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-windows (push) Has started running
Build / build-linux (push) Has started running
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
2026-02-19 18:29:58 +11:00
b53c5d34ef disable cache 2026-02-19 18:29:48 +11:00
GitHub Actions
887a3ef7b3 chore: bump version to 4.10.9 [skip ci] 2026-02-19 07:16:02 +00:00
3ee4def6db fix: update job runners to use ubuntu-latest for consistency across workflows
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-windows (push) Has started running
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-02-19 18:15:38 +11:00
3e09e908f5 fix: enhance versioning logic and checkout process in build workflow
Some checks failed
Build / check-version (push) Has been cancelled
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
2026-02-19 18:11:12 +11:00
ebeede58d2 fix: update build configuration for consistency and mark subproject as dirty
Some checks failed
Build / check-version (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-02-19 18:07:40 +11:00
da43506c01 fix: update submodule checkout method for Windows compatibility
Some checks failed
Build / build-linux (push) Waiting to run
Build / check-version (push) Successful in 6s
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-02-19 17:59:51 +11:00
1f23647433 fix: update subproject commit reference
Some checks failed
Build / build-windows (push) Failing after 12s
Build / build-android (push) Failing after 13s
Build / check-version (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / create-release (push) Has been cancelled
2026-02-19 17:50:04 +11:00
55cf5fdf8a fix: mark subproject as dirty to indicate uncommitted changes
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 5m36s
Build / build-windows (push) Successful in 6m27s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-02-06 15:27:22 +11:00
20a764b870 Refactor code structure for improved readability and maintainability
Some checks failed
Build / check-version (push) Successful in 7s
Build / build-windows (push) Successful in 5m14s
Build / build-linux (push) Successful in 5m38s
Build / build-android (push) Failing after 3h9m36s
Build / create-release (push) Has been cancelled
2026-02-06 05:53:49 +11:00
5ad2747a2f feat: add background sync for Matrix client on mobile platforms
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 9m42s
Build / build-windows (push) Successful in 10m16s
Build / build-android (push) Failing after 1h57m27s
Build / create-release (push) Has been cancelled
- Introduced background sync functionality for the Matrix client, allowing notifications to be received even when the app is backgrounded.
- Added new commands to start, stop, and get the state of background sync.
- Implemented a BackgroundSyncManager to manage sync state and credentials.
- Integrated matrix-sdk for handling Matrix interactions and notifications.
- Updated Cargo.toml to include necessary dependencies for background sync.
- Modified mobile capabilities to include opener permissions.
- Enhanced lib.rs to handle new commands and integrate background sync logic.
2026-02-05 19:20:55 +11:00
ece77ccba6 feat: enhance SyncService and MainActivity for better battery optimization and service reliability
All checks were successful
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 5m32s
Build / build-windows (push) Successful in 8m23s
Build / build-android (push) Successful in 11m26s
Build / create-release (push) Has been skipped
2026-02-05 07:48:09 +11:00
89b0da0854 feat: add Linux permission handling for media access and configure main window in tauri.conf.json
All checks were successful
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 8m58s
Build / build-windows (push) Successful in 9m17s
Build / build-android (push) Successful in 12m35s
Build / create-release (push) Has been skipped
- Implemented automatic permission handling for microphone and camera access on Linux using webkit2gtk.
- Updated tauri.conf.json to define the main application window with specific dimensions and properties.
2026-02-05 04:57:45 +11:00
4b185556f8 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
All checks were successful
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 5m45s
Build / build-windows (push) Successful in 8m1s
Build / build-android (push) Successful in 14m28s
Build / create-release (push) Has been skipped
2026-02-05 04:02:33 +11:00
579b77e8d5 fix: enhance Android build process with improved keystore handling and error checks 2026-02-05 04:02:32 +11:00
0090b6bd84 Update README.md
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-windows (push) Successful in 8m1s
Build / build-linux (push) Successful in 18m11s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-02-05 02:28:50 +10:00
7296b0cd34 Refactor code structure for improved readability and maintainability
Some checks failed
Build / build-windows (push) Failing after 2m33s
Build / check-version (push) Successful in 9s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-02-05 03:24:42 +11:00
7a5e258f23 refactor: update permission sets and schemas for opener functionality
Some checks failed
Build / build-windows (push) Successful in 18m42s
Build / check-version (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
- Changed permission descriptions and constants in android-schema.json and mobile-schema.json to reflect new opener functionality.
- Updated capabilities.json to remove deprecated shell permissions and include new opener permissions.
- Refactored schema definitions to align with the new opener API, including changes to allowed and denied commands.
- Adjusted application references in schemas to clarify usage with URLs and paths.
2026-01-29 23:19:50 +11:00
3ccae09dca fix: update mobile capabilities to replace opener permission with shell:allow-open
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 6m5s
Build / build-windows (push) Successful in 13m39s
Build / build-android (push) Failing after 11m54s
Build / create-release (push) Has been skipped
2026-01-25 22:41:52 +11:00
90407c49c6 feat: add open_external_url command to open URLs in the default browser
All checks were successful
Build / check-version (push) Successful in 6s
Build / build-windows (push) Successful in 6m12s
Build / build-linux (push) Successful in 9m27s
Build / build-android (push) Successful in 26m23s
Build / create-release (push) Has been skipped
2026-01-25 21:50:17 +11:00
61c1e3857d fix: update window capabilities to allow all windows and enhance opener permissions
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 6m7s
Build / build-windows (push) Successful in 7m45s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 21:38:44 +11:00
6cc8d06e18 fix: restrict window capability to 'main' and enhance opener permissions
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 6m51s
Build / build-windows (push) Successful in 12m10s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 21:16:12 +11:00
27a7e61ff8 fix: update opener permissions to allow default URLs and remove unnecessary entries
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 5m43s
Build / build-windows (push) Successful in 7m46s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 21:06:46 +11:00
72af2b3ef7 fix: update Android build command to target aarch64 architecture and enhance opener permissions
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 6m11s
Build / build-windows (push) Successful in 7m49s
Build / build-android (push) Has started running
Build / create-release (push) Has been cancelled
2026-01-25 20:53:12 +11:00
c612dfa824 fix: update capabilities for mobile and desktop, enhance window configuration
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 6m1s
Build / build-windows (push) Successful in 6m15s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 20:09:08 +11:00
ca049e53f5 chore: update cinny submodule with link click debugging
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 6m13s
Build / build-windows (push) Successful in 15m34s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 19:49:49 +11:00
5a3cbd02e6 Update README.md
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 5m46s
Build / build-windows (push) Successful in 13m28s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 18:34:03 +10:00
02bc4caaaf chore: update cinny submodule with opener fix
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 6m1s
Build / build-windows (push) Successful in 12m49s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 18:40:32 +11:00
72b9a1480c fix: remove duplicate mobile_entry_point macro
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-01-25 17:53:11 +11:00
ca56725984 fix: separate capabilities for desktop (autostart) and mobile
Some checks failed
Build / check-version (push) Successful in 7s
Build / build-linux (push) Successful in 6m0s
Build / build-windows (push) Successful in 10m28s
Build / build-android (push) Failing after 7m53s
Build / create-release (push) Has been skipped
2026-01-25 14:59:34 +11:00
e4d8f6152e perf: limit Android build to arm64-v8a only (99%+ of devices)
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Successful in 6m8s
Build / build-windows (push) Successful in 11m41s
Build / build-android (push) Failing after 7m51s
Build / create-release (push) Has been skipped
2026-01-25 14:33:11 +11:00
a191fb0609 chore: remove unused GitHub Actions workflow
Some checks failed
Build / check-version (push) Successful in 10s
Build / build-windows (push) Successful in 6m53s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-01-25 14:21:08 +11:00
b7031f85e0 feat: add MSI updater files (.msi.zip, .msi.zip.sig) to CI workflow
Some checks failed
Build / check-version (push) Successful in 7s
Build / build-windows (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-01-25 14:20:01 +11:00
0e6d140d01 fix: add xdg-utils and patchelf to Linux CI dependencies
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-windows (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-01-25 14:16:53 +11:00
2d0398f5d3 fix: update tauri script to use Node.js for file copying
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Failing after 4m21s
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-01-25 14:09:54 +11:00
b6c07916af chore: update subproject commit reference
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Failing after 4m32s
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-01-25 14:02:30 +11:00
37141d3d31 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-windows (push) Failing after 1m56s
Build / create-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 13:59:00 +11:00
58626aa942 refactor: update permission identifiers and descriptions in windows-schema.json
- Changed permission identifiers from `shell` to `opener` for clarity.
- Updated descriptions to reflect new functionality for opening URLs and revealing items in directories.
- Removed deprecated permissions and added new autostart permissions in the schema.
- Adjusted the structure of allowed and denied entries for better organization.

feat: implement system tray functionality in lib.rs

- Added system tray support with show and quit options.
- Implemented event handling for tray icon clicks and menu interactions.
- Modified application run logic to minimize to tray on window close.
- Updated plugins to use `tauri_plugin_opener` instead of `tauri_plugin_shell`.
2026-01-25 13:58:58 +11:00
d4264f0e8d Update README.md
All checks were successful
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 5m43s
Build / build-windows (push) Successful in 13m22s
Build / build-android (push) Successful in 34m12s
Build / create-release (push) Has been skipped
2026-01-25 11:29:37 +10:00
90fc67c805 Merge branch 'main' of http://synbox.ruv.wtf:8418/litruv/cinny-desktop
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Successful in 10m38s
Build / build-windows (push) Successful in 12m19s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 11:32:03 +11:00
6cabc01f77 feat: update capabilities and add clipboard image reading functionality for Linux
- Updated capabilities.json to allow opening shell commands.
- Implemented read_clipboard_image function for Linux using arboard to read images from the clipboard and encode them as base64 PNG.
- Added a stub for read_clipboard_image on non-Linux platforms.
- Registered the new command in the Tauri application.
2026-01-25 11:31:54 +11:00
d2619004b5 Update README.md
All checks were successful
Build / check-version (push) Successful in 4s
Build / build-linux (push) Successful in 5m40s
Build / build-windows (push) Successful in 6m28s
Build / build-android (push) Successful in 13m6s
Build / create-release (push) Has been skipped
2026-01-25 04:45:46 +10:00
eaa8e3949a Refactor code structure for improved readability and maintainability
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 5m34s
Build / build-windows (push) Successful in 6m35s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 04:20:38 +11:00
6a32c023ee Refactor code structure for improved readability and maintainability
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Successful in 13m23s
Build / build-windows (push) Successful in 14m23s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 03:10:17 +11:00
773c036404 fix: update package name to 'paarrot' and add png2icons dependency
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Successful in 12m53s
Build / build-windows (push) Successful in 19m1s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 01:50:31 +11:00
2a8c3a1475 Refactor code structure for improved readability and maintainability
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 12m45s
Build / build-windows (push) Failing after 15m39s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 01:09:30 +11:00
a749db75f7 fix: mark subproject as dirty in cinny
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 9m39s
Build / build-windows (push) Successful in 23m30s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 00:40:46 +11:00
d16a55e06d fix: add caching for Cargo registry and build artifacts in Tauri workflow
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-windows (push) Has been cancelled
Build / build-android (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / create-release (push) Has been cancelled
2026-01-25 00:31:54 +11:00
93085b7029 feat: add Rust linker configuration for multiple targets
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Successful in 9m24s
Build / build-windows (push) Successful in 13m28s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-25 00:14:11 +11:00
fce3c8ea59 fix: optimize build performance by adjusting Rust compilation settings
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Successful in 6m13s
Build / build-windows (push) Successful in 17m18s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-24 23:54:46 +11:00
d73a6874f6 feat: add Android ARM64 build support in Tauri workflow
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Successful in 5m10s
Build / build-windows (push) Successful in 12m0s
Build / build-android (push) Has started running
Build / create-release (push) Has been cancelled
2026-01-24 23:31:16 +11:00
8531cfdbf6 fix: update schema reference in default capabilities and remove unused desktop capabilities file
All checks were successful
Build / check-version (push) Successful in 3s
Build / build-windows (push) Successful in 11m28s
Build / build-linux (push) Successful in 12m18s
Build / build-android (push) Successful in 26m24s
Build / create-release (push) Successful in 14s
2026-01-24 08:30:45 +11:00
430f37a8a4 fix: update identifier in desktop capabilities configuration
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Failing after 3m16s
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-01-24 08:26:14 +11:00
1c4165b536 fix: update cinny subproject commit reference
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Failing after 3m18s
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-01-24 08:21:42 +11:00
6f987b6be3 fix: mark cinny subproject as dirty
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Failing after 1m53s
Build / build-windows (push) Failing after 1m58s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-24 08:18:48 +11:00
1264065aeb fix: update cinny subproject commit reference
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Failing after 2m3s
Build / build-windows (push) Failing after 2m3s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-24 08:15:55 +11:00
058d55af7b fix: mark cinny subproject as dirty
Some checks failed
Build / check-version (push) Successful in 5s
Build / build-linux (push) Failing after 1m58s
Build / build-windows (push) Failing after 2m4s
Build / build-android (push) Has started running
Build / create-release (push) Has been cancelled
2026-01-24 08:12:41 +11:00
a7156f1a76 bump version to 4.10.8 in tauri configuration
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-windows (push) Failing after 1m58s
Build / build-linux (push) Failing after 1m54s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-24 08:09:19 +11:00
94b72d0fe8 bump version to 4.10.7 in tauri configuration
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Failing after 1m59s
Build / build-windows (push) Failing after 2m2s
Build / build-android (push) Failing after 2m13s
Build / create-release (push) Has been skipped
2026-01-24 08:04:29 +11:00
3198e496c4 feat: add desktop capability schema with default permissions
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-linux (push) Failing after 2m2s
Build / build-windows (push) Failing after 1m43s
Build / create-release (push) Has been cancelled
Build / build-android (push) Has been cancelled
2026-01-24 07:59:49 +11:00
1de0ed67a2 bump version to 4.10.6 in tauri configuration
Some checks failed
Build / check-version (push) Successful in 6s
Build / build-linux (push) Failing after 3m24s
Build / build-windows (push) Has started running
Build / build-android (push) Has started running
Build / create-release (push) Has been cancelled
2026-01-24 07:51:53 +11:00
47f58730ee Update cinny submodule reference
Some checks failed
Build / check-version (push) Successful in 4s
Build / build-android (push) Failing after 9s
Build / create-release (push) Has been cancelled
Build / build-windows (push) Has been cancelled
Build / build-linux (push) Has been cancelled
2026-01-24 07:49:47 +11:00
01312be50d feat: add deep link and notification permissions to mobile and android schemas
- Introduced new permissions for deep link functionality including `allow-get-current`, `allow-is-registered`, `allow-register`, and `allow-unregister`.
- Added comprehensive notification permissions such as `allow-notify`, `allow-request-permission`, and others to enhance notification capabilities.
- Updated the mobile and android schemas to reflect these changes.
- Removed deprecated window state permissions from schemas.
- Updated the capabilities schema to include new default permissions for deep link and notification features.
- Refactored the Tauri application initialization to support new deep link plugin for mobile platforms.
2026-01-24 07:48:09 +11:00
8637654e88 Fix Linux package rename step - use find to avoid glob issues
All checks were successful
Build / check-version (push) Successful in 3s
Build / build-linux (push) Successful in 5m12s
Build / build-windows (push) Successful in 13m24s
Build / build-android (push) Successful in 31m8s
Build / create-release (push) Successful in 24s
2026-01-24 05:33:03 +11:00
0fd03b8496 Fix release workflow - create tag and release in same workflow run, bump to 4.10.5
Some checks failed
Build / check-version (push) Successful in 3s
Build / build-linux (push) Failing after 5m9s
Build / build-windows (push) Has started running
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
2026-01-24 05:25:14 +11:00
890b060ab4 Bump version to 4.10.3 in tauri configuration
All checks were successful
Build / check-version (push) Successful in 5s
Build / create-tag (push) Successful in 6s
Build / build-windows (push) Successful in 13m7s
Build / build-linux (push) Successful in 16m11s
Build / build-android (push) Successful in 27m0s
Build / create-release (push) Has been skipped
2026-01-24 04:45:13 +11:00
9d02cc6c5b Add caching for npm, Cargo, and Gradle to speed up builds
Some checks failed
Build / check-version (push) Successful in 6s
Build / create-tag (push) Has been skipped
Build / build-android (push) Has been cancelled
Build / create-release (push) Has been cancelled
Build / build-linux (push) Has been cancelled
Build / build-windows (push) Has been cancelled
2026-01-24 04:44:05 +11:00
101 changed files with 9753 additions and 17921 deletions

View File

@@ -8,321 +8,278 @@ on:
tags:
- 'v*'
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
jobs:
check-version:
increment-version:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/')
if: github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[skip ci]')
outputs:
should_release: ${{ steps.check.outputs.should_release }}
version: ${{ steps.check.outputs.version }}
version: ${{ steps.bump.outputs.VERSION }}
should_build: ${{ steps.bump.outputs.SHOULD_BUILD }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Check if version tag exists
id: check
- name: Bump patch version
id: bump
run: |
VERSION=$(grep -Po '"version":\s*"\K[^"]+' src-tauri/tauri.conf.json | head -1)
echo "version=$VERSION" >> $GITHUB_OUTPUT
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
echo "Tag v$VERSION already exists"
echo "should_release=false" >> $GITHUB_OUTPUT
else
echo "Tag v$VERSION does not exist, will create release"
echo "should_release=true" >> $GITHUB_OUTPUT
fi
create-tag:
runs-on: ubuntu-latest
needs: check-version
if: needs.check-version.outputs.should_release == 'true'
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Get current version from package.json
CURRENT=$(grep -Po '"version":\s*"\K[^"]+' package.json | head -1)
echo "Current version: $CURRENT"
# Split and increment patch
MAJOR=$(echo $CURRENT | cut -d. -f1)
MINOR=$(echo $CURRENT | cut -d. -f2)
PATCH=$(echo $CURRENT | cut -d. -f3)
NEW_PATCH=$((PATCH + 1))
NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}"
echo "New version: $NEW_VERSION"
# Update package.json
sed -i "s/\"version\": \"${CURRENT}\"/\"version\": \"${NEW_VERSION}\"/" package.json
# Verify the change
grep '"version"' package.json | head -1
echo "VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "SHOULD_BUILD=true" >> $GITHUB_OUTPUT
- name: Create and push tag
- name: Commit and push version bump
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git tag -a "v${{ needs.check-version.outputs.version }}" -m "Release v${{ needs.check-version.outputs.version }}"
git push origin "v${{ needs.check-version.outputs.version }}"
git add package.json
git commit -m "chore: bump version to ${{ steps.bump.outputs.VERSION }} [skip ci]"
git push
build-windows:
runs-on: windows
needs: increment-version
if: always() && (needs.increment-version.outputs.should_build == 'true' || needs.increment-version.result == 'skipped')
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
submodules: false
ref: ${{ github.ref_name }}
- name: Checkout submodules manually
shell: powershell
run: |
git submodule update --init --recursive
- name: Pull latest (after version bump)
if: github.ref == 'refs/heads/main'
shell: powershell
run: git pull origin main
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Install dependencies (root)
run: npm install
run: npm install --prefer-offline
- name: Install dependencies (cinny)
working-directory: ./cinny
run: npm install
run: npm install --prefer-offline
- name: Build Tauri app
run: npm run tauri build
- name: Rename MSI
- name: Clean previous builds
shell: powershell
run: |
$msi = Get-ChildItem "src-tauri/target/release/bundle/msi/*.msi" | Select-Object -First 1
$newName = "Cinny-Windows-x64.msi"
Copy-Item $msi.FullName "src-tauri/target/release/bundle/msi/$newName"
if (Test-Path cinny/dist) { Remove-Item -Recurse -Force cinny/dist }
if (Test-Path dist-electron) { Remove-Item -Recurse -Force dist-electron }
- name: Upload MSI installer
- name: Build Electron app
run: npm run build:win
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Squirrel Setup (x64)
uses: actions/upload-artifact@v3
with:
name: Cinny-Windows-x64.msi
path: src-tauri/target/release/bundle/msi/Cinny-Windows-x64.msi
name: Paarrot-Setup-x64.exe
path: dist-electron/squirrel-windows/Paarrot-*-win-x64.exe
- 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')
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
ref: ${{ github.ref_name }}
- name: Pull latest (after version bump)
if: github.ref == 'refs/heads/main'
run: git pull origin main
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev rpm
- name: Install dependencies (root)
run: npm install
run: npm ci --prefer-offline
- name: Install dependencies (cinny)
working-directory: ./cinny
run: npm install
run: npm ci --prefer-offline
- name: Build Tauri app
run: npm run tauri build
- name: Rename Linux packages
- name: Clean previous builds
run: |
cp src-tauri/target/release/bundle/appimage/*.AppImage src-tauri/target/release/bundle/appimage/Cinny-Linux-x64.AppImage
cp src-tauri/target/release/bundle/deb/*.deb src-tauri/target/release/bundle/deb/Cinny-Linux-x64.deb
cp src-tauri/target/release/bundle/rpm/*.rpm src-tauri/target/release/bundle/rpm/Cinny-Linux-x64.rpm
# Copy signature file for updater
if [ -f src-tauri/target/release/bundle/appimage/*.AppImage.sig ]; then
cp src-tauri/target/release/bundle/appimage/*.AppImage.sig src-tauri/target/release/bundle/appimage/Cinny-Linux-x64.AppImage.sig
fi
rm -rf cinny/dist dist-electron
- name: Build Electron app
run: npm run build:linux
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload AppImage
uses: actions/upload-artifact@v3
with:
name: Cinny-Linux-x64.AppImage
path: src-tauri/target/release/bundle/appimage/Cinny-Linux-x64.AppImage
- name: Upload AppImage Signature
uses: actions/upload-artifact@v3
if: always()
with:
name: Cinny-Linux-x64.AppImage.sig
path: src-tauri/target/release/bundle/appimage/Cinny-Linux-x64.AppImage.sig
- name: Upload DEB package
uses: actions/upload-artifact@v3
with:
name: Cinny-Linux-x64.deb
path: src-tauri/target/release/bundle/deb/Cinny-Linux-x64.deb
- name: Upload RPM package
uses: actions/upload-artifact@v3
with:
name: Cinny-Linux-x64.rpm
path: src-tauri/target/release/bundle/rpm/Cinny-Linux-x64.rpm
build-android:
runs-on: windows
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
target: aarch64-linux-android
- name: Add Android targets
run: |
rustup target add aarch64-linux-android --toolchain stable
rustup target add armv7-linux-androideabi --toolchain stable
rustup target add x86_64-linux-android --toolchain stable
rustup target add i686-linux-android --toolchain stable
- name: Install dependencies (root)
run: npm install
- name: Install dependencies (cinny)
working-directory: ./cinny
run: npm install
- name: Build Android APK
shell: powershell
run: |
$env:PATH = "$env:USERPROFILE\.cargo\bin;$env:PATH"
npm run tauri android build
env:
ANDROID_HOME: ${{ env.ANDROID_HOME }}
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/25.2.9519653
RUSTUP_TOOLCHAIN: stable
- name: Sign APK
shell: powershell
run: |
$unsignedApk = "src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release-unsigned.apk"
$signedApk = "src-tauri/gen/android/app/build/outputs/apk/universal/release/cinny-release.apk"
$keystore = "$env:USERPROFILE\.android\debug.keystore"
# Find Android SDK
$androidHome = if ($env:ANDROID_HOME) { $env:ANDROID_HOME }
elseif ($env:ANDROID_SDK_ROOT) { $env:ANDROID_SDK_ROOT }
elseif (Test-Path "$env:LOCALAPPDATA\Android\Sdk") { "$env:LOCALAPPDATA\Android\Sdk" }
else { "$env:USERPROFILE\AppData\Local\Android\Sdk" }
Write-Host "Using Android SDK at: $androidHome"
# Use debug keystore for signing (create if missing)
if (-not (Test-Path $keystore)) {
keytool -genkey -v -keystore $keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US"
}
# Find build tools
$buildTools = Get-ChildItem "$androidHome\build-tools" | Sort-Object Name -Descending | Select-Object -First 1
$apksigner = "$($buildTools.FullName)\apksigner.bat"
$zipalign = "$($buildTools.FullName)\zipalign.exe"
Write-Host "Using build tools: $($buildTools.FullName)"
# Align the APK
& $zipalign -v -p 4 $unsignedApk "$unsignedApk.aligned"
# Sign the APK
& $apksigner sign --ks $keystore --ks-pass pass:android --key-pass pass:android --out $signedApk "$unsignedApk.aligned"
# Verify
& $apksigner verify $signedApk
Write-Host "Signed APK created at: $signedApk"
# Rename to final name
Copy-Item $signedApk "src-tauri/gen/android/app/build/outputs/apk/universal/release/Cinny-Android.apk"
- name: Upload APK
uses: actions/upload-artifact@v3
with:
name: Cinny-Android.apk
path: src-tauri/gen/android/app/build/outputs/apk/universal/release/Cinny-Android.apk
name: Paarrot-Linux-x64.AppImage
path: dist-electron/Paarrot-*.AppImage
create-release:
runs-on: ubuntu-latest
needs: [build-windows, build-linux, build-android]
if: startsWith(github.ref, 'refs/tags/v')
needs: [increment-version, build-windows, build-linux]
if: always() && (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: Get version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Create update.json
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
APPIMAGE_SIG=""
if [ -f "artifacts/Cinny-Linux-x64.AppImage.sig/Cinny-Linux-x64.AppImage.sig" ]; then
APPIMAGE_SIG=$(cat "artifacts/Cinny-Linux-x64.AppImage.sig/Cinny-Linux-x64.AppImage.sig")
fi
cat > update.json << EOF
{
"version": "${VERSION}",
"notes": "Update to version ${VERSION}",
"pub_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"platforms": {
"linux-x86_64": {
"signature": "${APPIMAGE_SIG}",
"url": "http://synbox.ruv.wtf:8418/litruv/cinny-desktop/releases/download/v${VERSION}/Cinny-Linux-x64.AppImage"
}
}
}
EOF
cat update.json
- name: Prepare release files
run: |
mkdir -p release-files
# Move artifacts out of subdirectories
find artifacts -type f \( -name "*.msi" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" -o -name "*.apk" -o -name "*.sig" \) -exec cp {} release-files/ \;
cp update.json 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 Release
uses: softprops/action-gh-release@v1
with:
files: release-files/*
draft: false
prerelease: false
generate_release_notes: true
- name: Create or Update Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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 ""
done
echo "Release complete!"

View File

@@ -1,173 +0,0 @@
name: "Publish Tauri App"
on:
release:
types: [published]
jobs:
# Windows-x86_64
windows-x86_64:
runs-on: windows-latest
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: dtolnay/rust-toolchain@stable
- 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 }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
NODE_OPTIONS: "--max_old_space_size=4096"
with:
releaseId: ${{ github.event.release.upload_url }}
- name: Get app version (windows)
run: |
$json = (Get-Content "src-tauri\tauri.conf.json" -Raw) | ConvertFrom-Json
$version = $json.package.version
echo "Version: ${version}"
echo "TAURI_VERSION=${version}" >> $Env:GITHUB_ENV
echo "${Env:TAURI_VERSION}"
shell: pwsh
- name: Move msi
run: Move-Item "src-tauri\target\release\bundle\msi\Cinny_${{ env.TAURI_VERSION }}_x64_en-US.msi" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi"
shell: pwsh
- name: Move msi.zip
run: Move-Item "src-tauri\target\release\bundle\msi\Cinny_${{ env.TAURI_VERSION }}_x64_en-US.msi.zip" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi.zip"
shell: pwsh
- name: Move msi.zip.sig
run: Move-Item "src-tauri\target\release\bundle\msi\Cinny_${{ env.TAURI_VERSION }}_x64_en-US.msi.zip.sig" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi.zip.sig"
shell: pwsh
- name: Upload tagged release
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
with:
files: |
src-tauri/target/release/bundle/msi/Cinny_desktop-x86_64.msi
src-tauri/target/release/bundle/msi/Cinny_desktop-x86_64.msi.zip
src-tauri/target/release/bundle/msi/Cinny_desktop-x86_64.msi.zip.sig
# Linux-x86_64
linux-x86_64:
runs-on: ubuntu-22.04
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: dtolnay/rust-toolchain@stable
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev 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 }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
NODE_OPTIONS: "--max_old_space_size=4096"
with:
releaseId: ${{ github.event.release.upload_url }}
- name: Get app version
id: vars
run: echo "tag=$(jq .package.version src-tauri/tauri.conf.json | tr -d '"')" >> $GITHUB_OUTPUT
- name: Move deb
run: mv "src-tauri/target/release/bundle/deb/cinny_${{ steps.vars.outputs.tag }}_amd64.deb" "src-tauri/target/release/bundle/deb/Cinny_desktop-x86_64.deb"
- name: Move AppImage
run: mv "src-tauri/target/release/bundle/appimage/cinny_${{ steps.vars.outputs.tag }}_amd64.AppImage" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage"
- name: Move AppImage.tar.gz
run: mv "src-tauri/target/release/bundle/appimage/cinny_${{ steps.vars.outputs.tag }}_amd64.AppImage.tar.gz" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage.tar.gz"
- name: Move AppImage.tar.gz.sig
run: mv "src-tauri/target/release/bundle/appimage/cinny_${{ steps.vars.outputs.tag }}_amd64.AppImage.tar.gz.sig" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage.tar.gz.sig"
- name: Upload tagged release
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
with:
files: |
src-tauri/target/release/bundle/deb/Cinny_desktop-x86_64.deb
src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage
src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage.tar.gz
src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage.tar.gz.sig
# macos-universal
macos-universal:
runs-on: macos-latest
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: dtolnay/rust-toolchain@stable
with:
target: aarch64-apple-darwin,x86_64-apple-darwin
- 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 }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
NODE_OPTIONS: "--max_old_space_size=4096"
with:
releaseId: ${{ github.event.release.upload_url }}
args: "--target universal-apple-darwin"
- name: Get app version
id: vars
run: echo "tag=$(jq .package.version src-tauri/tauri.conf.json | tr -d '"')" >> $GITHUB_OUTPUT
- name: Move dmg
run: mv "src-tauri/target/universal-apple-darwin/release/bundle/dmg/Cinny_${{ steps.vars.outputs.tag }}_universal.dmg" "src-tauri/target/universal-apple-darwin/release/bundle/dmg/Cinny_desktop-universal.dmg"
- name: Move app.tar.gz
run: mv "src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny.app.tar.gz" "src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny_desktop-universal.app.tar.gz"
- name: Move app.tar.gz.sig
run: mv "src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny.app.tar.gz.sig" "src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny_desktop-universal.app.tar.gz.sig"
- name: Upload tagged release
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
with:
files: |
src-tauri/target/universal-apple-darwin/release/bundle/dmg/Cinny_desktop-universal.dmg
src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny_desktop-universal.app.tar.gz
src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny_desktop-universal.app.tar.gz.sig
# Upload release.json
release-update:
if: always()
needs: [windows-x86_64, linux-x86_64, macos-universal]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4.2.0
- name: Install dependencies
run: npm ci
- name: Run release.json
run: npm run release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

172
API-QUICKSTART.md Normal file
View File

@@ -0,0 +1,172 @@
# 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 Normal file
View File

@@ -0,0 +1,537 @@
# 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,39 +1,31 @@
# Cinny desktop
<a href="https://github.com/cinnyapp/cinny-desktop/releases">
<img alt="GitHub release downloads" src="https://img.shields.io/github/downloads/cinnyapp/cinny-desktop/total?style=social"></a>
Cinny is a matrix client focusing primarily on simple, elegant and secure interface. The desktop app is made with Tauri.
# 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.
## Download
Installers for macOS, Windows and Linux can be downloaded from [Github releases](https://github.com/cinnyapp/cinny-desktop/releases). Releases are signed with a [Ed25519](https://ed25519.cr.yp.to/) public-key.
Installers for Windows and Linux can be downloaded from [releases](http://synbox.ruv.wtf:8418/litruv/cinny-desktop/releases).
Operating System | Download
---|---
Windows | <a href='https://github.com/cinnyapp/cinny-desktop/releases/latest/download/Cinny_desktop-x86_64.msi'>Get it on Windows</a>
macOS | <a href='https://github.com/cinnyapp/cinny-desktop/releases/latest/download/Cinny_desktop-universal.dmg'>Get it on macOS</a>
Linux | <a href='https://github.com/cinnyapp/cinny-desktop/releases/latest/download/Cinny_desktop-x86_64.AppImage'>Get it on Linux</a> · <a href='https://flathub.org/apps/details/in.cinny.Cinny'>Flatpak</a>
Windows (x64) | <a href='http://synbox.ruv.wtf:8418/litruv/cinny-desktop/releases'>Get it on Windows</a>
Linux (AppImage) | <a href='http://synbox.ruv.wtf:8418/litruv/cinny-desktop/releases'>Get it on Linux</a>
Decoded public key:
> RWRflTUQD3RHFtn25QNANCmePR9+4LSK89kAKTMEEB4OKpOFpLMgc64z
### Linux Installation
To verify release files, you need to download [minisign](https://jedisct1.github.io/minisign/) tool and [decode](https://www.base64decode.org/) the *.sig* file before running:
> minisign -Vm ***RELEASE_FILE.msi.zip*** -P RWRflTUQD3RHFtn25QNANCmePR9+4LSK89kAKTMEEB4OKpOFpLMgc64z -x ***SINGATURE.msi.zip.sig***
For the best AppImage experience, we recommend using [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) which automatically integrates AppImages into your system.
## Local development
Firstly, to setup Rust, NodeJS and build tools follow [Tauri documentation](https://tauri.app/v1/guides/getting-started/prerequisites).
Now, to setup development locally run the following commands:
* `git clone --recursive https://github.com/cinnyapp/cinny-desktop.git`
To setup development locally run the following commands:
* `git clone --recursive http://synbox.ruv.wtf:8418/litruv/cinny-desktop.git`
* `cd cinny-desktop/cinny`
* `npm ci`
* `cd ..`
* `npm ci`
To build the app locally, run:
* `npm run tauri build`
* `npm run build`
To start local dev server, run:
* `npm run tauri dev`
* `npm run dev`

BIN
appicon-square.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

BIN
appicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Post-installation script for .deb and .rpm packages
# Update desktop database for the .desktop file
if command -v update-desktop-database &> /dev/null; then
update-desktop-database -q /usr/share/applications || true
fi
# Update icon cache
if command -v gtk-update-icon-cache &> /dev/null; then
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor || true
fi
# Set appropriate permissions for chrome-sandbox (required for Electron)
SANDBOX_PATH="/opt/Paarrot/chrome-sandbox"
if [ -f "$SANDBOX_PATH" ]; then
chmod 4755 "$SANDBOX_PATH" || true
fi
exit 0

View File

@@ -0,0 +1,14 @@
#!/bin/bash
# Post-removal script for .deb and .rpm packages
# Update desktop database after removal
if command -v update-desktop-database &> /dev/null; then
update-desktop-database -q /usr/share/applications || true
fi
# Update icon cache
if command -v gtk-update-icon-cache &> /dev/null; then
gtk-update-icon-cache -q -t -f /usr/share/icons/hicolor || true
fi
exit 0

2
cinny

Submodule cinny updated: 54f5fa95a6...58966aec19

93
electron-builder.json5 Normal file
View File

@@ -0,0 +1,93 @@
{
"appId": "com.paarrot.app",
"productName": "Paarrot",
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}",
"copyright": "Copyright © 2026 Mates.Media",
"directories": {
"output": "dist-electron",
"buildResources": "build-resources"
},
"files": [
"electron/**/*",
"cinny/dist/**/*",
"!**/*.map",
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
"!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
"!**/node_modules/*.d.ts",
"!**/node_modules/.bin",
"!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}",
"!.editorconfig",
"!**/._*",
"!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}",
"!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}",
"!**/{appveyor.yml,.travis.yml,circle.yml}",
"!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}"
],
"extraResources": [
{
"from": "icons",
"to": "icons",
"filter": ["**/*"]
}
],
"linux": {
"target": [
{
"target": "AppImage",
"arch": ["x64"]
}
],
"category": "Network;InstantMessaging",
"maintainer": "Mates.Media <mates.media@users.noreply.github.com>",
"icon": "icons/icon.png",
"desktop": {
"entry": {
"Name": "Paarrot",
"GenericName": "Matrix Client",
"Comment": "A Matrix client built with Cinny",
"Categories": "Network;InstantMessaging;",
"Keywords": "matrix;chat;messaging;",
"StartupWMClass": "paarrot"
}
}
},
"deb": {
"depends": [
"libnotify4",
"libxtst6",
"libnss3"
],
"afterInstall": "build-resources/linux/after-install.sh",
"afterRemove": "build-resources/linux/after-remove.sh"
},
"rpm": {
"depends": [
"libnotify",
"libXtst",
"nss"
],
"afterInstall": "build-resources/linux/after-install.sh",
"afterRemove": "build-resources/linux/after-remove.sh"
},
"win": {
"target": [
{
"target": "squirrel",
"arch": ["x64"]
}
],
"icon": "icons/icon.ico",
"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
},
"publish": {
"provider": "generic",
"url": "http://synbox.ruv.wtf:8418/litruv/cinny-desktop/releases/download/v${version}"
}
}

267
electron/api-server.js Normal file
View File

@@ -0,0 +1,267 @@
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
class PaarrotAPIServer {
constructor(mainWindow) {
this.mainWindow = mainWindow;
this.app = express();
this.server = null;
this.port = 33384; // Default port for Paarrot API
this.setupMiddleware();
this.setupRoutes();
}
setupMiddleware() {
// Enable CORS for all origins (you can restrict this if needed)
this.app.use(cors());
// Parse JSON bodies
this.app.use(bodyParser.json());
// Log all requests
this.app.use((req, res, next) => {
console.log(`Paarrot API: ${req.method} ${req.path}`);
next();
});
}
setupRoutes() {
// Health check
this.app.get('/health', (req, res) => {
res.json({ status: 'ok', app: 'Paarrot API', version: '1.0.0' });
});
// Get current status
this.app.get('/status', async (req, res) => {
try {
const status = await this.executeAction('get-status');
res.json({ success: true, data: status });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Mute/unmute
this.app.post('/mute', async (req, res) => {
try {
const { muted } = req.body;
const result = await this.executeAction('set-mute', { muted });
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Toggle mute
this.app.post('/mute/toggle', async (req, res) => {
try {
const result = await this.executeAction('toggle-mute');
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Deafen/undeafen
this.app.post('/deafen', async (req, res) => {
try {
const { deafened } = req.body;
const result = await this.executeAction('set-deafen', { deafened });
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Toggle deafen
this.app.post('/deafen/toggle', async (req, res) => {
try {
const result = await this.executeAction('toggle-deafen');
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Change channel/room
this.app.post('/channel', async (req, res) => {
try {
const { roomId } = req.body;
if (!roomId) {
return res.status(400).json({ success: false, error: 'roomId is required' });
}
const result = await this.executeAction('change-channel', { roomId });
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Get list of rooms/channels
this.app.get('/channels', async (req, res) => {
try {
const result = await this.executeAction('get-channels');
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Send message
this.app.post('/message', async (req, res) => {
try {
const { roomId, message } = req.body;
if (!roomId || !message) {
return res.status(400).json({
success: false,
error: 'roomId and message are required'
});
}
const result = await this.executeAction('send-message', { roomId, message });
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Send message to current room
this.app.post('/message/current', async (req, res) => {
try {
const { message } = req.body;
if (!message) {
return res.status(400).json({
success: false,
error: 'message is required'
});
}
const result = await this.executeAction('send-message-current', { message });
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Get current room info
this.app.get('/room/current', async (req, res) => {
try {
const result = await this.executeAction('get-current-room');
res.json({ success: true, data: result });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// 404 handler
this.app.use((req, res) => {
res.status(404).json({
success: false,
error: 'Endpoint not found',
availableEndpoints: [
'GET /health',
'GET /status',
'POST /mute',
'POST /mute/toggle',
'POST /deafen',
'POST /deafen/toggle',
'POST /channel',
'GET /channels',
'POST /message',
'POST /message/current',
'GET /room/current'
]
});
});
// Error handler
this.app.use((err, req, res, next) => {
console.error('Paarrot API Error:', err);
res.status(500).json({ success: false, error: err.message });
});
}
/**
* Execute an action by sending it to the renderer process
* @param {string} action - The action to execute
* @param {object} params - Parameters for the action
* @returns {Promise<any>} - Result from the renderer
*/
executeAction(action, params = {}) {
return new Promise((resolve, reject) => {
if (!this.mainWindow || this.mainWindow.isDestroyed()) {
return reject(new Error('Main window is not available'));
}
// Create a unique response channel
const responseChannel = `api-response-${Date.now()}-${Math.random()}`;
// Set up one-time listener for the response
const { ipcMain } = require('electron');
const timeout = setTimeout(() => {
ipcMain.removeHandler(responseChannel);
reject(new Error('Action timed out'));
}, 10000); // 10 second timeout
ipcMain.handleOnce(responseChannel, async (event, result) => {
clearTimeout(timeout);
if (result.success) {
resolve(result.data);
} else {
reject(new Error(result.error || 'Unknown error'));
}
});
// Send the action to the renderer
this.mainWindow.webContents.send('api-action', {
action,
params,
responseChannel
});
});
}
start(port = this.port) {
return new Promise((resolve, reject) => {
this.server = this.app.listen(port, '127.0.0.1', () => {
console.log(`Paarrot API server listening on http://127.0.0.1:${port}`);
console.log('Available endpoints:');
console.log(' GET /health');
console.log(' GET /status');
console.log(' POST /mute');
console.log(' POST /mute/toggle');
console.log(' POST /deafen');
console.log(' POST /deafen/toggle');
console.log(' POST /channel');
console.log(' GET /channels');
console.log(' POST /message');
console.log(' POST /message/current');
console.log(' GET /room/current');
resolve(port);
}).on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Paarrot API: Port ${port} is already in use`);
reject(new Error(`Port ${port} is already in use`));
} else {
console.error('Paarrot API: Failed to start server:', err);
reject(err);
}
});
});
}
stop() {
return new Promise((resolve) => {
if (this.server) {
this.server.close(() => {
console.log('Paarrot API server stopped');
resolve();
});
} else {
resolve();
}
});
}
}
module.exports = PaarrotAPIServer;

743
electron/main.js Normal file
View File

@@ -0,0 +1,743 @@
const { app, BrowserWindow, ipcMain, shell, Tray, Menu, nativeImage, clipboard, session, desktopCapturer } = require('electron');
const path = require('path');
const fs = require('fs');
const { exec } = require('child_process');
const { promisify } = require('util');
const Store = require('electron-store');
const open = require('open');
const { autoUpdater } = require('electron-updater');
const PaarrotAPIServer = require('./api-server');
const https = require('https');
const http = require('http');
// Handle Squirrel events for Windows installer
if (require('electron-squirrel-startup')) {
app.quit();
}
// Handle Squirrel.Windows installation/update events
if (process.platform === 'win32') {
const handleSquirrelEvent = () => {
if (process.argv.length === 1) {
return false;
}
const squirrelEvent = process.argv[1];
switch (squirrelEvent) {
case '--squirrel-install':
case '--squirrel-updated':
// Install desktop and start menu shortcuts
const cp = require('child_process');
const updateDotExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
const target = path.basename(process.execPath);
const createShortcut = `${updateDotExe} --createShortcut=${target}`;
cp.exec(createShortcut, () => {
app.quit();
});
return true;
case '--squirrel-uninstall':
// Remove desktop and start menu shortcuts
const cpUninstall = require('child_process');
const updateDotExeUninstall = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
const targetUninstall = path.basename(process.execPath);
const removeShortcut = `${updateDotExeUninstall} --removeShortcut=${targetUninstall}`;
cpUninstall.exec(removeShortcut, () => {
app.quit();
});
return true;
case '--squirrel-obsolete':
// This is called on the outgoing version of your app before
// we update to the new version - it's the opposite of
// --squirrel-updated
app.quit();
return true;
}
return false;
};
if (handleSquirrelEvent()) {
// Squirrel event handled and app will exit in 1000ms, so don't do anything else
return;
}
}
const execAsync = promisify(exec);
const store = new Store();
let mainWindow = null;
let tray = null;
let isQuitting = false;
let apiServer = null;
// Configure auto-updater
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = true;
// Gitea update configuration
const GITEA_API_BASE = 'http://synbox.ruv.wtf:8418/api/v1';
const GITEA_REPO = 'litruv/cinny-desktop';
// Function to fetch latest release from Gitea and configure update feed
async function configureGiteaUpdateFeed() {
try {
const url = `${GITEA_API_BASE}/repos/${GITEA_REPO}/releases?limit=1`;
console.log('Fetching latest release from:', url);
return new Promise((resolve, reject) => {
http.get(url, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
try {
const releases = JSON.parse(data);
if (releases && releases.length > 0) {
const latestRelease = releases[0];
const version = latestRelease.tag_name.replace(/^v/, '');
const downloadUrl = `http://synbox.ruv.wtf:8418/${GITEA_REPO}/releases/download/${latestRelease.tag_name}`;
console.log('Latest release version:', version);
console.log('Download URL:', downloadUrl);
// Configure auto-updater with the versioned release URL
autoUpdater.setFeedURL({
provider: 'generic',
url: downloadUrl,
channel: 'latest'
});
resolve({ version, url: downloadUrl });
} else {
reject(new Error('No releases found'));
}
} catch (err) {
reject(err);
}
});
}).on('error', reject);
});
} catch (error) {
console.error('Failed to configure Gitea update feed:', error);
throw error;
}
}
// Auto-updater event handlers
autoUpdater.on('checking-for-update', () => {
console.log('Checking for updates...');
});
autoUpdater.on('update-available', (info) => {
console.log('Update available:', info.version);
if (mainWindow) {
mainWindow.webContents.send('update-available', info);
}
});
autoUpdater.on('update-not-available', (info) => {
console.log('No updates available');
});
autoUpdater.on('error', (err) => {
console.error('Auto-updater error:', err);
});
autoUpdater.on('download-progress', (progressObj) => {
console.log(`Download progress: ${progressObj.percent}%`);
if (mainWindow) {
mainWindow.webContents.send('update-download-progress', progressObj);
}
});
autoUpdater.on('update-downloaded', (info) => {
console.log('Update downloaded:', info.version);
if (mainWindow) {
mainWindow.webContents.send('update-downloaded', info);
}
});
// Single instance lock
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on('second-instance', () => {
// Someone tried to run a second instance, focus our window
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.show();
mainWindow.focus();
}
});
}
// Development mode detection
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
const VITE_DEV_SERVER = 'http://localhost:8080';
const PORT = 44548;
// Helper to get correct icon path in dev vs packaged app
function getIconPath(iconName) {
if (isDev) {
return path.join(__dirname, '../icons', iconName);
}
// In packaged app, extraResources puts icons in resources/icons/
return path.join(process.resourcesPath, 'icons', iconName);
}
function createWindow() {
// Restore window state or use defaults
const windowState = store.get('windowState', {
width: 1280,
height: 905,
x: undefined,
y: undefined,
isMaximized: false
});
mainWindow = new BrowserWindow({
width: windowState.width,
height: windowState.height,
x: windowState.x,
y: windowState.y,
title: 'Paarrot',
frame: false, // Custom window decorations
resizable: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
webSecurity: true,
allowRunningInsecureContent: false,
// Enable navigation gestures (works on macOS and some Linux environments)
enableBlinkFeatures: 'OverscrollHistoryNavigation'
},
icon: getIconPath(process.platform === 'win32' ? 'icon.ico' : 'icon.png'),
show: false // Don't show until ready
});
// Enable trackpad swipe gestures for navigation on macOS
if (process.platform === 'darwin') {
try {
mainWindow.on('swipe', (event, direction) => {
console.log(`Paarrot: Swipe gesture detected: ${direction}`);
if (direction === 'left' && mainWindow.webContents.canGoForward()) {
console.log('Paarrot: Navigating forward');
mainWindow.webContents.goForward();
} else if (direction === 'right' && mainWindow.webContents.canGoBack()) {
console.log('Paarrot: Navigating back');
mainWindow.webContents.goBack();
}
});
} catch (error) {
console.error('Paarrot: Failed to set up swipe gestures:', error);
}
}
// On Linux, the OverscrollHistoryNavigation Blink feature should handle it automatically
// On Windows, mouse buttons 4 & 5 are typically used for navigation (handled by Chromium)
// Restore maximized state
if (windowState.isMaximized) {
mainWindow.maximize();
}
// Save window state on resize/move
const saveWindowState = () => {
if (!mainWindow.isMaximized() && !mainWindow.isMinimized() && !mainWindow.isFullScreen()) {
const bounds = mainWindow.getBounds();
store.set('windowState', {
width: bounds.width,
height: bounds.height,
x: bounds.x,
y: bounds.y,
isMaximized: false
});
}
};
mainWindow.on('resize', saveWindowState);
mainWindow.on('move', saveWindowState);
mainWindow.on('maximize', () => {
store.set('windowState.isMaximized', true);
});
mainWindow.on('unmaximize', () => {
store.set('windowState.isMaximized', false);
});
// Set up session handlers BEFORE loading the app
// Auto-approve media permissions including screen capture
mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
const allowedPermissions = ['media', 'microphone', 'camera', 'display-capture'];
if (allowedPermissions.includes(permission)) {
console.log(`Paarrot: Auto-approving permission: ${permission}`);
callback(true);
} else {
console.log(`Paarrot: Denying permission: ${permission}`);
callback(false);
}
});
// Handle screen capture requests - always allow
mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
console.log(`Paarrot: Permission check - ${permission} from ${requestingOrigin}`);
return true; // Allow all permissions
});
// Handle display media request (screen sharing) - this is the key for Electron screen capture
mainWindow.webContents.session.setDisplayMediaRequestHandler(async (request, callback) => {
console.log('Paarrot: Display media request received', request);
try {
const sources = await desktopCapturer.getSources({
types: ['screen', 'window'],
thumbnailSize: { width: 150, height: 150 }
});
console.log('Paarrot: Available sources:', sources.length);
sources.forEach((s, i) => console.log(` ${i}: ${s.name} (${s.id})`));
if (sources.length === 0) {
console.log('Paarrot: No sources available, denying request');
callback({});
return;
}
// Prefer screens over windows
const source = sources.find(s => s.id.startsWith('screen:')) || sources[0];
console.log('Paarrot: Selected source:', source.name, source.id);
// Return the selected source - video is required
callback({ video: source });
} catch (error) {
console.error('Paarrot: Error getting display sources:', error);
callback({});
}
}, { useSystemPicker: false });
// Disable default context menu, only show for text selection
mainWindow.webContents.on('context-menu', (event, params) => {
const { selectionText, isEditable } = params;
// Only show context menu if there's selected text or in an editable field
if (selectionText || isEditable) {
// Build a minimal menu for text operations
const template = [];
if (params.misspelledWord) {
// Add spelling suggestions
params.dictionarySuggestions.slice(0, 5).forEach(suggestion => {
template.push({
label: suggestion,
click: () => mainWindow.webContents.replaceMisspelling(suggestion)
});
});
if (template.length > 0) {
template.push({ type: 'separator' });
}
}
if (selectionText) {
template.push(
{
label: 'Copy',
role: 'copy',
accelerator: 'CmdOrCtrl+C'
}
);
}
if (isEditable) {
if (selectionText) {
template.push(
{
label: 'Cut',
role: 'cut',
accelerator: 'CmdOrCtrl+X'
}
);
}
template.push(
{
label: 'Paste',
role: 'paste',
accelerator: 'CmdOrCtrl+V'
}
);
if (selectionText) {
template.push({ type: 'separator' });
template.push(
{
label: 'Select All',
role: 'selectAll',
accelerator: 'CmdOrCtrl+A'
}
);
}
}
if (template.length > 0) {
const menu = Menu.buildFromTemplate(template);
menu.popup(mainWindow);
}
}
// If no text selected and not editable, suppress the context menu entirely
});
// Load the app
if (isDev) {
mainWindow.loadURL(VITE_DEV_SERVER);
// Open DevTools in development
mainWindow.webContents.openDevTools();
} else {
// In production, serve from built files
mainWindow.loadFile(path.join(__dirname, '../cinny/dist/index.html'));
}
// Show window when ready
mainWindow.once('ready-to-show', () => {
// Check for --minimized flag (autostart)
if (!process.argv.includes('--minimized')) {
mainWindow.show();
}
});
// Handle window close - minimize to tray
mainWindow.on('close', (event) => {
if (!isQuitting) {
event.preventDefault();
mainWindow.hide();
return false;
}
});
mainWindow.on('closed', () => {
mainWindow = null;
});
// Open external links in default browser
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('http://') || url.startsWith('https://')) {
shell.openExternal(url);
return { action: 'deny' };
}
return { action: 'allow' };
});
// Navigation handler - open external links in browser
mainWindow.webContents.on('will-navigate', (event, url) => {
const allowedOrigins = [
'http://localhost:8080',
'http://localhost:44548',
'http://127.0.0.1:8080',
'http://127.0.0.1:44548'
];
const isAllowed = allowedOrigins.some(origin => url.startsWith(origin)) ||
url.startsWith('blob:') ||
url.startsWith('data:');
if (!isAllowed && (url.startsWith('http://') || url.startsWith('https://'))) {
event.preventDefault();
shell.openExternal(url);
}
});
return mainWindow;
}
function createTray() {
const iconPath = getIconPath('icon.png');
const trayIcon = nativeImage.createFromPath(iconPath);
tray = new Tray(trayIcon.resize({ width: 16, height: 16 }));
const contextMenu = Menu.buildFromTemplate([
{
label: 'Show Paarrot',
click: () => {
if (mainWindow) {
mainWindow.show();
mainWindow.focus();
}
}
},
{ type: 'separator' },
{
label: 'Quit',
click: () => {
isQuitting = true;
app.quit();
}
}
]);
tray.setContextMenu(contextMenu);
tray.setToolTip('Paarrot');
// Click tray icon to show window
tray.on('click', () => {
if (mainWindow) {
if (mainWindow.isVisible()) {
mainWindow.hide();
} else {
mainWindow.show();
mainWindow.focus();
}
}
});
}
// App lifecycle
app.whenReady().then(() => {
createWindow();
createTray();
// Start API server
apiServer = new PaarrotAPIServer(mainWindow);
apiServer.start().catch(err => {
console.error('Failed to start API server:', err);
});
// Check for updates (not in development mode)
if (!isDev) {
// Check for updates on start (after 3 seconds)
setTimeout(async () => {
try {
await configureGiteaUpdateFeed();
await autoUpdater.checkForUpdates();
} catch (err) {
console.error('Failed to check for updates:', err);
}
}, 3000);
// Check for updates every 6 hours
setInterval(async () => {
try {
await configureGiteaUpdateFeed();
await autoUpdater.checkForUpdates();
} catch (err) {
console.error('Failed to check for updates:', err);
}
}, 6 * 60 * 60 * 1000);
}
});
app.on('window-all-closed', () => {
// On macOS, apps stay active until Cmd+Q
if (process.platform !== 'darwin') {
// But we want to stay in tray, so don't quit
// app.quit();
}
});
app.on('activate', () => {
// On macOS, recreate window when dock icon is clicked
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
} else if (mainWindow) {
mainWindow.show();
}
});
app.on('before-quit', () => {
isQuitting = true;
// Stop API server
if (apiServer) {
apiServer.stop();
}
});
// ==================== IPC Handlers ====================
// Window controls
ipcMain.handle('window:minimize', () => {
if (mainWindow) mainWindow.minimize();
});
ipcMain.handle('window:maximize', () => {
if (mainWindow) {
if (mainWindow.isMaximized()) {
mainWindow.unmaximize();
} else {
mainWindow.maximize();
}
}
});
ipcMain.handle('window:unmaximize', () => {
if (mainWindow) mainWindow.unmaximize();
});
ipcMain.handle('window:close', () => {
if (mainWindow) mainWindow.close();
});
ipcMain.handle('window:is-maximized', () => {
return mainWindow ? mainWindow.isMaximized() : false;
});
ipcMain.handle('window:start-drag', () => {
if (mainWindow) mainWindow.webContents.startDrag({ file: '', icon: nativeImage.createEmpty() });
});
// Get app version (Tauri-compatible API)
ipcMain.handle('plugin:app|version', () => {
return app.getVersion();
});
// Open external URL
ipcMain.handle('open-external-url', async (event, url) => {
try {
await shell.openExternal(url);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
});
// Read clipboard image
ipcMain.handle('read-clipboard-image', async () => {
try {
const image = clipboard.readImage();
if (image.isEmpty()) {
return { success: true, data: null };
}
// Convert to PNG base64
const pngBuffer = image.toPNG();
const base64 = pngBuffer.toString('base64');
return {
success: true,
data: `data:image/png;base64,${base64}`
};
} catch (error) {
return { success: false, error: error.message };
}
});
// Write text to clipboard
ipcMain.handle('write-clipboard-text', async (event, text) => {
try {
clipboard.writeText(text);
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
});
// Get YouTube stream using yt-dlp
ipcMain.handle('get-youtube-stream', async (event, url) => {
try {
// Check if yt-dlp is available
try {
await execAsync('yt-dlp --version');
} catch {
return {
success: false,
error: 'yt-dlp is not installed. Please install yt-dlp to use YouTube features.'
};
}
// Get title
let title = 'YouTube Video';
try {
const titleResult = await execAsync(`yt-dlp --get-title "${url}"`);
title = titleResult.stdout.trim();
} catch (e) {
console.warn('Failed to get video title:', e.message);
}
// Get video URL
const result = await execAsync(
`yt-dlp -g -f "best[height<=1080]/bestvideo[height<=1080]+bestaudio/best" "${url}"`
);
const videoUrl = result.stdout.trim().split('\n')[0];
if (!videoUrl) {
return { success: false, error: 'yt-dlp returned empty URL' };
}
return {
success: true,
data: { video_url: videoUrl, title }
};
} catch (error) {
return {
success: false,
error: `yt-dlp error: ${error.message}`
};
}
});
// Background sync stubs (desktop doesn't need it)
ipcMain.handle('start-background-sync', async () => {
return { success: true };
});
ipcMain.handle('stop-background-sync', async () => {
return { success: true };
});
ipcMain.handle('get-background-sync-state', async () => {
return { success: true, data: 'NotApplicable' };
});
// Auto-updater IPC handlers
ipcMain.handle('check-for-updates', async () => {
if (isDev) {
return { success: false, error: 'Updates are not available in development mode' };
}
try {
// First, fetch latest release info from Gitea and configure feed URL
await configureGiteaUpdateFeed();
const result = await autoUpdater.checkForUpdates();
return { success: true, data: result };
} catch (error) {
return { success: false, error: error.message };
}
});
ipcMain.handle('download-update', async () => {
if (isDev) {
return { success: false, error: 'Updates are not available in development mode' };
}
try {
await autoUpdater.downloadUpdate();
return { success: true };
} catch (error) {
return { success: false, error: error.message };
}
});
ipcMain.handle('install-update', () => {
if (isDev) {
return { success: false, error: 'Updates are not available in development mode' };
}
autoUpdater.quitAndInstall(false, true);
return { success: true };
});
// Screen capture / desktopCapturer handler
ipcMain.handle('get-desktop-sources', async (event, opts) => {
try {
const sources = await desktopCapturer.getSources(opts);
return { success: true, data: sources };
} catch (error) {
console.error('Failed to get desktop sources:', error);
return { success: false, error: error.message };
}
});
console.log('Paarrot Electron main process started');
console.log('Development mode:', isDev);
console.log('Platform:', process.platform);

157
electron/preload.js Normal file
View File

@@ -0,0 +1,157 @@
const { contextBridge, ipcRenderer } = require('electron');
// Map command names to Electron IPC channels
const commandMap = {
'window_minimize': 'window:minimize',
'window_maximize': 'window:maximize',
'window_unmaximize': 'window:unmaximize',
'window_close': 'window:close',
'window_is_maximized': 'window:is-maximized',
'window_start_drag': 'window:start-drag',
'read_clipboard_image': 'read-clipboard-image',
'write_clipboard_text': 'write-clipboard-text',
'open_external_url': 'open-external-url',
'get_youtube_stream': 'get-youtube-stream',
'start_background_sync': 'start-background-sync',
'stop_background_sync': 'stop-background-sync',
'get_background_sync_state': 'get-background-sync-state',
'check_for_updates': 'check-for-updates',
'download_update': 'download-update',
'install_update': 'install-update',
'get_desktop_sources': 'get-desktop-sources'
};
// Shared invoke function for legacy API compatibility
const invokeHandler = async (command, args = {}) => {
try {
const channel = commandMap[command] || command;
const result = await ipcRenderer.invoke(channel, args);
// Return response in expected format
if (result && result.success === false) {
throw new Error(result.error || 'Unknown error');
}
// For commands that return data in a 'data' field
if (result && result.data !== undefined) {
return result.data;
}
// For simple commands that just return success/failure
return result;
} catch (error) {
console.error(`[Electron IPC] Error invoking ${command}:`, error);
throw error;
}
};
// Expose protected methods that allow the renderer process to use
// ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('__TAURI_INTERNALS__', {
// Legacy API compatibility layer for existing frontend code
metadata: {
currentVersion: {
version: '2.0.0',
platform: process.platform,
arch: process.arch
}
},
// Add invoke function for legacy plugins
invoke: invokeHandler,
// Add stub for transformCallback to prevent errors with legacy plugins
transformCallback: (callback) => {
return callback;
}
});
contextBridge.exposeInMainWorld('__TAURI__', {
// Core IPC invoke - legacy API
invoke: invokeHandler,
// Event listeners (if needed for future events)
listen: (event, handler) => {
const subscription = (_, ...args) => handler(...args);
ipcRenderer.on(event, subscription);
// Return unsubscribe function
return () => {
ipcRenderer.removeListener(event, subscription);
};
}
});
// Also expose under electron namespace for direct Electron APIs
contextBridge.exposeInMainWorld('electron', {
platform: process.platform,
arch: process.arch,
versions: process.versions,
// Window controls
window: {
minimize: () => ipcRenderer.invoke('window:minimize'),
maximize: () => ipcRenderer.invoke('window:maximize'),
unmaximize: () => ipcRenderer.invoke('window:unmaximize'),
close: () => ipcRenderer.invoke('window:close'),
isMaximized: () => ipcRenderer.invoke('window:is-maximized'),
startDrag: () => ipcRenderer.invoke('window:start-drag')
},
// Clipboard
clipboard: {
readImage: () => ipcRenderer.invoke('read-clipboard-image'),
writeText: (text) => ipcRenderer.invoke('write-clipboard-text', text)
},
// External URLs
shell: {
openExternal: (url) => ipcRenderer.invoke('open-external-url', url)
},
// YouTube
youtube: {
getStream: (url) => ipcRenderer.invoke('get-youtube-stream', url)
},
// Background sync (stubs for desktop)
sync: {
start: (args) => ipcRenderer.invoke('start-background-sync', args),
stop: () => ipcRenderer.invoke('stop-background-sync'),
getState: () => ipcRenderer.invoke('get-background-sync-state')
},
// Auto-updater
updater: {
checkForUpdates: () => ipcRenderer.invoke('check-for-updates'),
downloadUpdate: () => ipcRenderer.invoke('download-update'),
installUpdate: () => ipcRenderer.invoke('install-update'),
onUpdateAvailable: (callback) => {
ipcRenderer.on('update-available', (_, info) => callback(info));
},
onUpdateDownloadProgress: (callback) => {
ipcRenderer.on('update-download-progress', (_, progress) => callback(progress));
},
onUpdateDownloaded: (callback) => {
ipcRenderer.on('update-downloaded', (_, info) => callback(info));
}
},
// Desktop capturer (for screen sharing)
desktopCapturer: {
getSources: (opts) => ipcRenderer.invoke('get-desktop-sources', opts)
},
// API action handler (for external API server)
api: {
onAction: (callback) => {
const handler = (_, data) => callback(data);
ipcRenderer.on('api-action', handler);
return () => ipcRenderer.removeListener('api-action', handler);
},
sendResponse: (channel, result) => {
ipcRenderer.invoke(channel, result);
}
}
});
// Preload script ready

BIN
icons/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

View File

@@ -0,0 +1,274 @@
{
"info": {
"_postman_id": "paarrot-api-collection",
"name": "Paarrot API",
"description": "API endpoints for controlling Paarrot (Cinny Desktop) features like muting, deafening, channel navigation, and messaging.",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "paarrot-api"
},
"item": [
{
"name": "Health & Status",
"item": [
{
"name": "Health Check",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/health",
"host": [
"{{baseUrl}}"
],
"path": [
"health"
]
},
"description": "Check if the API server is running and healthy."
}
},
{
"name": "Get Status",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/status",
"host": [
"{{baseUrl}}"
],
"path": [
"status"
]
},
"description": "Get current application status including mute/deafen state, current room, connection status, and user ID."
}
}
]
},
{
"name": "Audio Controls",
"item": [
{
"name": "Toggle Mute",
"request": {
"method": "POST",
"header": [],
"url": {
"raw": "{{baseUrl}}/mute/toggle",
"host": [
"{{baseUrl}}"
],
"path": [
"mute",
"toggle"
]
},
"description": "Toggle microphone mute state. Works only when in an active call."
}
},
{
"name": "Set Mute",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"muted\": true\n}"
},
"url": {
"raw": "{{baseUrl}}/mute",
"host": [
"{{baseUrl}}"
],
"path": [
"mute"
]
},
"description": "Set microphone mute to a specific state (true/false). Works only when in an active call."
}
},
{
"name": "Toggle Deafen",
"request": {
"method": "POST",
"header": [],
"url": {
"raw": "{{baseUrl}}/deafen/toggle",
"host": [
"{{baseUrl}}"
],
"path": [
"deafen",
"toggle"
]
},
"description": "Toggle deafen state (mutes all incoming audio and your microphone). Works only when in an active call."
}
},
{
"name": "Set Deafen",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"deafened\": true\n}"
},
"url": {
"raw": "{{baseUrl}}/deafen",
"host": [
"{{baseUrl}}"
],
"path": [
"deafen"
]
},
"description": "Set deafen to a specific state (true/false). Works only when in an active call."
}
}
]
},
{
"name": "Rooms & Channels",
"item": [
{
"name": "Get Channels",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/channels",
"host": [
"{{baseUrl}}"
],
"path": [
"channels"
]
},
"description": "Get list of all available rooms/channels with their details (roomId, name, isDirect, avatar)."
}
},
{
"name": "Get Current Room",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/room/current",
"host": [
"{{baseUrl}}"
],
"path": [
"room",
"current"
]
},
"description": "Get information about the currently active room."
}
},
{
"name": "Change Channel",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"roomId\": \"!YourRoomId:server.com\"\n}"
},
"url": {
"raw": "{{baseUrl}}/channel",
"host": [
"{{baseUrl}}"
],
"path": [
"channel"
]
},
"description": "Navigate to a different room/channel by providing its roomId."
}
}
]
},
{
"name": "Messaging",
"item": [
{
"name": "Send Message to Room",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"roomId\": \"!YourRoomId:server.com\",\n \"message\": \"Hello from Postman!\"\n}"
},
"url": {
"raw": "{{baseUrl}}/message",
"host": [
"{{baseUrl}}"
],
"path": [
"message"
]
},
"description": "Send a text message to a specific room by providing roomId and message content."
}
},
{
"name": "Send Message to Current Room",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"message\": \"Hello from Postman!\"\n}"
},
"url": {
"raw": "{{baseUrl}}/message/current",
"host": [
"{{baseUrl}}"
],
"path": [
"message",
"current"
]
},
"description": "Send a text message to the currently active room."
}
}
]
}
],
"variable": [
{
"key": "baseUrl",
"value": "http://127.0.0.1:33384",
"type": "string"
}
]
}

BIN
paarrot.afdesign Normal file

Binary file not shown.

6958
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +1,45 @@
{
"name": "cinny",
"version": "4.10.2",
"description": "Yet another matrix client",
"main": "index.js",
"name": "paarrot",
"version": "4.11.25",
"description": "Paarrot - A Matrix client based on Cinny",
"homepage": "http://synbox.ruv.wtf:8418/litruv/cinny-desktop",
"main": "electron/main.js",
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"tauri": "powershell Copy-Item config.json cinny/; tauri",
"release": "node scripts/release.mjs"
"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",
"build:linux": "npm run build -- --linux",
"build:win": "npm run build -- --win",
"postman:generate": "node scripts/generate-postman-collection.js"
},
"keywords": [],
"author": "Ajay Bura",
"author": {
"name": "Mates.Media",
"email": "mates.media@users.noreply.github.com"
},
"license": "AGPL-3.0-only",
"dependencies": {
"@tauri-apps/api": "^2.0.0"
"body-parser": "2.2.2",
"cors": "2.8.6",
"electron-squirrel-startup": "^1.0.1",
"electron-store": "^8.2.0",
"electron-updater": "^6.3.9",
"express": "5.2.1",
"open": "11.0.0"
},
"devDependencies": {
"@actions/github": "6.0.0",
"@tauri-apps/cli": "^2.0.0",
"node-fetch": "3.3.2"
"@electron/rebuild": "4.0.3",
"concurrently": "9.2.1",
"electron": "40.6.0",
"electron-builder": "26.8.1",
"node-fetch": "3.3.2",
"png2icons": "2.0.1",
"sharp": "0.34.5",
"wait-on": "9.0.4"
}
}

View File

@@ -0,0 +1,63 @@
import sharp from 'sharp';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import png2icons from 'png2icons';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(__dirname, '..');
const sourceIcon = path.join(rootDir, 'appicon-square.png');
// Electron desktop icons
const electronIcons = [
{ name: '16x16.png', size: 16 },
{ name: '24x24.png', size: 24 },
{ name: '32x32.png', size: 32 },
{ name: '48x48.png', size: 48 },
{ name: '64x64.png', size: 64 },
{ name: '128x128.png', size: 128 },
{ name: '256x256.png', size: 256 },
{ name: '512x512.png', size: 512 },
{ name: '1024x1024.png', size: 1024 },
{ name: 'icon.png', size: 512 },
];
async function generateElectronIcons() {
const iconsDir = path.join(rootDir, 'icons');
// Ensure icons directory exists
await fs.mkdir(iconsDir, { recursive: true });
for (const icon of electronIcons) {
const outputPath = path.join(iconsDir, icon.name);
await sharp(sourceIcon)
.resize(icon.size, icon.size)
.png()
.toFile(outputPath);
console.log(`Generated: ${icon.name}`);
}
// Generate ICO file (Windows)
const sourceBuffer = await fs.readFile(sourceIcon);
const icoBuffer = png2icons.createICO(sourceBuffer, png2icons.BEZIER, 0, true, true);
await fs.writeFile(path.join(iconsDir, 'icon.ico'), icoBuffer);
console.log('Generated: icon.ico');
// Generate ICNS file (macOS)
const icnsBuffer = png2icons.createICNS(sourceBuffer, png2icons.BEZIER, 0);
await fs.writeFile(path.join(iconsDir, 'icon.icns'), icnsBuffer);
console.log('Generated: icon.icns');
}
async function main() {
console.log('Generating icons from:', sourceIcon);
console.log('');
console.log('=== Electron Desktop Icons ===');
await generateElectronIcons();
console.log('');
console.log('Done!');
}
main().catch(console.error);

View File

@@ -0,0 +1,224 @@
#!/usr/bin/env node
/**
* Generate Postman collection from API documentation
* This script reads the API.md file and generates a Postman collection
*/
const fs = require('fs');
const path = require('path');
const collection = {
info: {
_postman_id: 'paarrot-api-collection',
name: 'Paarrot API',
description: 'API endpoints for controlling Paarrot (Cinny Desktop) features like muting, deafening, channel navigation, and messaging.',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json',
_exporter_id: 'paarrot-api',
},
item: [
{
name: 'Health & Status',
item: [
{
name: 'Health Check',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/health',
host: ['{{baseUrl}}'],
path: ['health'],
},
description: 'Check if the API server is running and healthy.',
},
},
{
name: 'Get Status',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/status',
host: ['{{baseUrl}}'],
path: ['status'],
},
description: 'Get current application status including mute/deafen state, current room, connection status, and user ID.',
},
},
],
},
{
name: 'Audio Controls',
item: [
{
name: 'Toggle Mute',
request: {
method: 'POST',
header: [],
url: {
raw: '{{baseUrl}}/mute/toggle',
host: ['{{baseUrl}}'],
path: ['mute', 'toggle'],
},
description: 'Toggle microphone mute state. Works only when in an active call.',
},
},
{
name: 'Set Mute',
request: {
method: 'POST',
header: [{ key: 'Content-Type', value: 'application/json' }],
body: {
mode: 'raw',
raw: JSON.stringify({ muted: true }, null, 2),
},
url: {
raw: '{{baseUrl}}/mute',
host: ['{{baseUrl}}'],
path: ['mute'],
},
description: 'Set microphone mute to a specific state (true/false). Works only when in an active call.',
},
},
{
name: 'Toggle Deafen',
request: {
method: 'POST',
header: [],
url: {
raw: '{{baseUrl}}/deafen/toggle',
host: ['{{baseUrl}}'],
path: ['deafen', 'toggle'],
},
description: 'Toggle deafen state (mutes all incoming audio and your microphone). Works only when in an active call.',
},
},
{
name: 'Set Deafen',
request: {
method: 'POST',
header: [{ key: 'Content-Type', value: 'application/json' }],
body: {
mode: 'raw',
raw: JSON.stringify({ deafened: true }, null, 2),
},
url: {
raw: '{{baseUrl}}/deafen',
host: ['{{baseUrl}}'],
path: ['deafen'],
},
description: 'Set deafen to a specific state (true/false). Works only when in an active call.',
},
},
],
},
{
name: 'Rooms & Channels',
item: [
{
name: 'Get Channels',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/channels',
host: ['{{baseUrl}}'],
path: ['channels'],
},
description: 'Get list of all available rooms/channels with their details (roomId, name, isDirect, avatar).',
},
},
{
name: 'Get Current Room',
request: {
method: 'GET',
header: [],
url: {
raw: '{{baseUrl}}/room/current',
host: ['{{baseUrl}}'],
path: ['room', 'current'],
},
description: 'Get information about the currently active room.',
},
},
{
name: 'Change Channel',
request: {
method: 'POST',
header: [{ key: 'Content-Type', value: 'application/json' }],
body: {
mode: 'raw',
raw: JSON.stringify({ roomId: '!YourRoomId:server.com' }, null, 2),
},
url: {
raw: '{{baseUrl}}/channel',
host: ['{{baseUrl}}'],
path: ['channel'],
},
description: 'Navigate to a different room/channel by providing its roomId.',
},
},
],
},
{
name: 'Messaging',
item: [
{
name: 'Send Message to Room',
request: {
method: 'POST',
header: [{ key: 'Content-Type', value: 'application/json' }],
body: {
mode: 'raw',
raw: JSON.stringify(
{
roomId: '!YourRoomId:server.com',
message: 'Hello from Postman!',
},
null,
2
),
},
url: {
raw: '{{baseUrl}}/message',
host: ['{{baseUrl}}'],
path: ['message'],
},
description: 'Send a text message to a specific room by providing roomId and message content.',
},
},
{
name: 'Send Message to Current Room',
request: {
method: 'POST',
header: [{ key: 'Content-Type', value: 'application/json' }],
body: {
mode: 'raw',
raw: JSON.stringify({ message: 'Hello from Postman!' }, null, 2),
},
url: {
raw: '{{baseUrl}}/message/current',
host: ['{{baseUrl}}'],
path: ['message', 'current'],
},
description: 'Send a text message to the currently active room.',
},
},
],
},
],
variable: [
{
key: 'baseUrl',
value: 'http://127.0.0.1:33384',
type: 'string',
},
],
};
// Write the collection to file (in the project root)
const outputPath = path.join(__dirname, '..', 'paarrot-api.postman_collection.json');
fs.writeFileSync(outputPath, JSON.stringify(collection, null, 2) + '\n');
console.log('✅ Postman collection generated:', outputPath);

View File

@@ -1,108 +0,0 @@
import fetch from "node-fetch";
import { getOctokit, context } from "@actions/github";
async function getAssetSign(url) {
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/octet-stream",
},
});
return response.text();
}
async function createTauriRelease() {
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error("GITHUB_TOKEN is not found!");
}
const github = getOctokit(process.env.GITHUB_TOKEN);
const { repos } = github.rest;
const repoMetaData = {
owner: context.repo.owner,
repo: context.repo.repo,
};
const tagsResult = await repos.listTags({ ...repoMetaData, per_page: 10, page: 1 });
const latestTag = tagsResult.data.find((tag) => tag.name.startsWith("v"));
console.log(latestTag);
const latestRelease = await repos.getReleaseByTag({ ...repoMetaData, tag: latestTag.name });
const latestAssets = latestRelease.data.assets;
const windowsX86_64 = {};
const linuxX86_64 = {};
const darwinX86_64 = {};
const darwinAarch64 = {};
const promises = latestAssets.map(async (asset) => {
const { name, browser_download_url } = asset;
if (/\.msi\.zip$/.test(name)) {
windowsX86_64.url = browser_download_url;
}
if (/\.msi\.zip\.sig$/.test(name)) {
windowsX86_64.signature = await getAssetSign(browser_download_url);
}
if (/\.AppImage\.tar\.gz$/.test(name)) {
linuxX86_64.url = browser_download_url;
}
if (/\.AppImage\.tar\.gz\.sig$/.test(name)) {
linuxX86_64.signature = await getAssetSign(browser_download_url);
}
if (/universal\.app\.tar\.gz$/.test(name)) {
darwinX86_64.url = browser_download_url;
}
if (/universal\.app\.tar\.gz\.sig$/.test(name)) {
darwinX86_64.signature = await getAssetSign(browser_download_url);
}
if (/universal\.app\.tar\.gz$/.test(name)) {
darwinAarch64.url = browser_download_url;
}
if (/universal\.app\.tar\.gz\.sig$/.test(name)) {
darwinAarch64.signature = await getAssetSign(browser_download_url);
}
});
await Promise.allSettled(promises);
const releaseData = {
name: latestTag.name,
notes: `https://github.com/${repoMetaData.owner}/${repoMetaData.repo}/releases/tag/${latestTag.name}`,
pub_date: new Date().toISOString(),
platforms: {},
};
if (windowsX86_64.url) releaseData.platforms["windows-x86_64"] = windowsX86_64;
else console.error('Failed to get release for windowsX86_64');
if (linuxX86_64.url) releaseData.platforms["linux-x86_64"] = linuxX86_64;
else console.error('Failed to get release for linuxX86_64');
if (darwinX86_64.url) releaseData.platforms["darwin-x86_64"] = darwinX86_64;
else console.error('Failed to get release for darwinX86_64');
if (darwinAarch64.url) releaseData.platforms["darwin-aarch64"] = darwinAarch64;
else console.error('Failed to get release for darwinAarch64');
const releaseResult = await repos.getReleaseByTag({ ...repoMetaData, tag: 'tauri' });
const tauriRelease = releaseResult.data;
const prevReleaseAsset = tauriRelease.assets.find((asset) => asset.name === 'release.json');
if (prevReleaseAsset) {
await repos.deleteReleaseAsset({ ...repoMetaData, asset_id: prevReleaseAsset.id });
}
console.log(releaseData);
await repos.uploadReleaseAsset({
...repoMetaData,
release_id: tauriRelease.id,
name: 'release.json',
data: JSON.stringify(releaseData, null, 2),
});
}
createTauriRelease();

View File

@@ -1,6 +0,0 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
.tauri-private.key

View File

@@ -1 +0,0 @@
dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDExREU1NkVBMDY4MzcxMjAKUldRZ2NZTUc2bGJlRVNaQldnMXpJcHlocVpCWUNNaUdicjBGMVRsck1GQXhvTnJiOUNhVVRHSzQK

5473
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +0,0 @@
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[package]
name = "cinny"
version = "4.10.2"
description = "Yet another matrix client"
authors = ["Ajay Bura"]
license = "AGPL-3.0-only"
repository = "https://github.com/cinnyapp/cinny-desktop"
default-run = "cinny"
edition = "2021"
rust-version = "1.70"
[lib]
name = "cinny_lib"
crate-type = ["staticlib", "cdylib", "lib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "2", features = ["devtools"] }
tauri-plugin-localhost = "2"
tauri-plugin-window-state = "2"
tauri-plugin-shell = "2"
tauri-plugin-notification = "2"
[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies]
tauri-plugin-deep-link = "2"
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
tauri-plugin-single-instance = "2"
tauri-plugin-updater = "2"
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]

View File

@@ -1,3 +0,0 @@
fn main() {
tauri_build::build()
}

View File

@@ -0,0 +1,29 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Default capability for all windows",
"windows": ["*"],
"platforms": ["linux", "windows", "macOS"],
"permissions": [
"core:default",
"core:webview:allow-internal-toggle-devtools",
"opener:default",
"opener:allow-open-url",
"opener:allow-default-urls",
"notification:default",
"notification:allow-register-listener",
"autostart:default",
"updater:default",
"updater:allow-check",
"updater:allow-download",
"updater:allow-download-and-install",
"dialog:default",
"process:allow-restart",
{
"identifier": "http:default",
"allow": [
{ "url": "https://api.telegram.org/**" }
]
}
]
}

View File

@@ -1,12 +0,0 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false

View File

@@ -1,19 +0,0 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
build
/captures
.externalNativeBuild
.cxx
local.properties
key.properties
/.tauri
/tauri.settings.gradle

View File

@@ -1,6 +0,0 @@
/src/main/java/in/cinny/app/generated
/src/main/jniLibs/**/*.so
/src/main/assets/tauri.conf.json
/tauri.build.gradle.kts
/proguard-tauri.pro
/tauri.properties

View File

@@ -1,70 +0,0 @@
import java.util.Properties
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("rust")
}
val tauriProperties = Properties().apply {
val propFile = file("tauri.properties")
if (propFile.exists()) {
propFile.inputStream().use { load(it) }
}
}
android {
compileSdk = 36
namespace = "in.cinny.app"
defaultConfig {
manifestPlaceholders["usesCleartextTraffic"] = "false"
applicationId = "in.cinny.app"
minSdk = 24
targetSdk = 36
versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
}
buildTypes {
getByName("debug") {
manifestPlaceholders["usesCleartextTraffic"] = "true"
isDebuggable = true
isJniDebuggable = true
isMinifyEnabled = false
packaging { jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
jniLibs.keepDebugSymbols.add("*/x86/*.so")
jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
}
}
getByName("release") {
isMinifyEnabled = true
proguardFiles(
*fileTree(".") { include("**/*.pro") }
.plus(getDefaultProguardFile("proguard-android-optimize.txt"))
.toList().toTypedArray()
)
}
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
buildConfig = true
}
}
rust {
rootDirRel = "../../../"
}
dependencies {
implementation("androidx.webkit:webkit:1.14.0")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("androidx.activity:activity-ktx:1.10.1")
implementation("com.google.android.material:material:1.12.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.4")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
}
apply(from = "tauri.build.gradle.kts")

View File

@@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -1,61 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- AndroidTV support -->
<uses-feature android:name="android.software.leanback" android:required="false" />
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.cinny"
android:usesCleartextTraffic="${usesCleartextTraffic}">
<activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
android:launchMode="singleTask"
android:label="@string/main_activity_title"
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<!-- AndroidTV support -->
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
<!-- Background sync service -->
<service
android:name=".SyncService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="dataSync" />
<!-- Boot receiver to restart service -->
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

View File

@@ -1,18 +0,0 @@
package `in`.cinny.app
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
/**
* Receives boot completed broadcast to restart the sync service
* after device reboot.
*/
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
// Start the sync service after boot
SyncService.start(context)
}
}
}

View File

@@ -1,48 +0,0 @@
package `in`.cinny.app
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
/**
* Main activity for Cinny. Handles permission requests and
* starts the background sync service for notifications.
*/
class MainActivity : TauriActivity() {
companion object {
private const val NOTIFICATION_PERMISSION_CODE = 1001
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestNotificationPermission()
startSyncService()
}
override fun onResume() {
super.onResume()
// Ensure sync service is running when app is in foreground
startSyncService()
}
private fun requestNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
NOTIFICATION_PERMISSION_CODE
)
}
}
}
private fun startSyncService() {
SyncService.start(this)
}
}

View File

@@ -1,114 +0,0 @@
package `in`.cinny.app
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.os.PowerManager
import androidx.core.app.NotificationCompat
/**
* Foreground service to keep Matrix sync connection alive in background.
* This service maintains a wake lock and shows a persistent notification
* to prevent Android from killing the app while syncing.
*/
class SyncService : Service() {
companion object {
private const val CHANNEL_ID = "cinny_sync_channel"
private const val NOTIFICATION_ID = 1001
private const val WAKE_LOCK_TAG = "Cinny:SyncWakeLock"
fun start(context: Context) {
val intent = Intent(context, SyncService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
fun stop(context: Context) {
val intent = Intent(context, SyncService::class.java)
context.stopService(intent)
}
}
private var wakeLock: PowerManager.WakeLock? = null
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() {
super.onCreate()
createNotificationChannel()
acquireWakeLock()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val notification = createNotification()
startForeground(NOTIFICATION_ID, notification)
return START_STICKY
}
override fun onDestroy() {
releaseWakeLock()
super.onDestroy()
}
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"Matrix Sync",
NotificationManager.IMPORTANCE_LOW
).apply {
description = "Keeps Cinny connected for message notifications"
setShowBadge(false)
}
val manager = getSystemService(NotificationManager::class.java)
manager.createNotificationChannel(channel)
}
}
private fun createNotification(): Notification {
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Cinny")
.setContentText("Connected to Matrix")
.setSmallIcon(R.mipmap.ic_launcher)
.setOngoing(true)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.build()
}
private fun acquireWakeLock() {
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
WAKE_LOCK_TAG
).apply {
acquire(10 * 60 * 1000L) // 10 minutes, will be renewed
}
}
private fun releaseWakeLock() {
wakeLock?.let {
if (it.isHeld) {
it.release()
}
}
wakeLock = null
}
}

View File

@@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,6 +0,0 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.cinny" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@@ -1,4 +0,0 @@
<resources>
<string name="app_name">Cinny</string>
<string name="main_activity_title">Cinny</string>
</resources>

View File

@@ -1,6 +0,0 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.cinny" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
<cache-path name="my_cache_images" path="." />
</paths>

View File

@@ -1,22 +0,0 @@
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.11.0")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25")
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
tasks.register("clean").configure {
delete("build")
}

View File

@@ -1,23 +0,0 @@
plugins {
`kotlin-dsl`
}
gradlePlugin {
plugins {
create("pluginsForCoolKids") {
id = "rust"
implementationClass = "RustPlugin"
}
}
}
repositories {
google()
mavenCentral()
}
dependencies {
compileOnly(gradleApi())
implementation("com.android.tools.build:gradle:8.11.0")
}

View File

@@ -1,68 +0,0 @@
import java.io.File
import org.apache.tools.ant.taskdefs.condition.Os
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.logging.LogLevel
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
open class BuildTask : DefaultTask() {
@Input
var rootDirRel: String? = null
@Input
var target: String? = null
@Input
var release: Boolean? = null
@TaskAction
fun assemble() {
val executable = """npm""";
try {
runTauriCli(executable)
} catch (e: Exception) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
// Try different Windows-specific extensions
val fallbacks = listOf(
"$executable.exe",
"$executable.cmd",
"$executable.bat",
)
var lastException: Exception = e
for (fallback in fallbacks) {
try {
runTauriCli(fallback)
return
} catch (fallbackException: Exception) {
lastException = fallbackException
}
}
throw lastException
} else {
throw e;
}
}
}
fun runTauriCli(executable: String) {
val rootDirRel = rootDirRel ?: throw GradleException("rootDirRel cannot be null")
val target = target ?: throw GradleException("target cannot be null")
val release = release ?: throw GradleException("release cannot be null")
val args = listOf("run", "--", "tauri", "android", "android-studio-script");
project.exec {
workingDir(File(project.projectDir, rootDirRel))
executable(executable)
args(args)
if (project.logger.isEnabled(LogLevel.DEBUG)) {
args("-vv")
} else if (project.logger.isEnabled(LogLevel.INFO)) {
args("-v")
}
if (release) {
args("--release")
}
args(listOf("--target", target))
}.assertNormalExitValue()
}
}

View File

@@ -1,85 +0,0 @@
import com.android.build.api.dsl.ApplicationExtension
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.get
const val TASK_GROUP = "rust"
open class Config {
lateinit var rootDirRel: String
}
open class RustPlugin : Plugin<Project> {
private lateinit var config: Config
override fun apply(project: Project) = with(project) {
config = extensions.create("rust", Config::class.java)
val defaultAbiList = listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64");
val abiList = (findProperty("abiList") as? String)?.split(',') ?: defaultAbiList
val defaultArchList = listOf("arm64", "arm", "x86", "x86_64");
val archList = (findProperty("archList") as? String)?.split(',') ?: defaultArchList
val targetsList = (findProperty("targetList") as? String)?.split(',') ?: listOf("aarch64", "armv7", "i686", "x86_64")
extensions.configure<ApplicationExtension> {
@Suppress("UnstableApiUsage")
flavorDimensions.add("abi")
productFlavors {
create("universal") {
dimension = "abi"
ndk {
abiFilters += abiList
}
}
defaultArchList.forEachIndexed { index, arch ->
create(arch) {
dimension = "abi"
ndk {
abiFilters.add(defaultAbiList[index])
}
}
}
}
}
afterEvaluate {
for (profile in listOf("debug", "release")) {
val profileCapitalized = profile.replaceFirstChar { it.uppercase() }
val buildTask = tasks.maybeCreate(
"rustBuildUniversal$profileCapitalized",
DefaultTask::class.java
).apply {
group = TASK_GROUP
description = "Build dynamic library in $profile mode for all targets"
}
tasks["mergeUniversal${profileCapitalized}JniLibFolders"].dependsOn(buildTask)
for (targetPair in targetsList.withIndex()) {
val targetName = targetPair.value
val targetArch = archList[targetPair.index]
val targetArchCapitalized = targetArch.replaceFirstChar { it.uppercase() }
val targetBuildTask = project.tasks.maybeCreate(
"rustBuild$targetArchCapitalized$profileCapitalized",
BuildTask::class.java
).apply {
group = TASK_GROUP
description = "Build dynamic library in $profile mode for $targetArch"
rootDirRel = config.rootDirRel
target = targetName
release = profile == "release"
}
buildTask.dependsOn(targetBuildTask)
tasks["merge$targetArchCapitalized${profileCapitalized}JniLibFolders"].dependsOn(
targetBuildTask
)
}
}
}
}
}

View File

@@ -1,24 +0,0 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.nonFinalResIds=false

View File

@@ -1,6 +0,0 @@
#Tue May 10 19:22:52 CST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@@ -1,185 +0,0 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

View File

@@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,3 +0,0 @@
include ':app'
apply from: 'tauri.settings.gradle'

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
{}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,35 +0,0 @@
//! Cinny Desktop library for cross-platform builds including Android
#[cfg(mobile)]
mod mobile;
#[cfg(mobile)]
pub use mobile::*;
/// Runs the Tauri application
pub fn run() {
let port = 44548;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let builder = {
tauri::Builder::default()
.plugin(tauri_plugin_localhost::Builder::new(port).build())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_window_state::Builder::default().build())
.plugin(tauri_plugin_single_instance::init(|_app, _args, _cwd| {}))
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_updater::Builder::new().build())
};
#[cfg(any(target_os = "android", target_os = "ios"))]
let builder = {
tauri::Builder::default()
.plugin(tauri_plugin_localhost::Builder::new(port).build())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_notification::init())
};
builder
.run(tauri::generate_context!())
.expect("error while building tauri application");
}

View File

@@ -4,5 +4,5 @@
)]
fn main() {
cinny_lib::run();
paarrot_lib::run();
}

View File

@@ -1,7 +0,0 @@
//! Mobile-specific entry points for Android and iOS
/// Mobile entry point
#[tauri::mobile_entry_point]
fn main() {
crate::run();
}

View File

@@ -1,79 +0,0 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Cinny",
"version": "4.10.2",
"identifier": "in.cinny.app",
"build": {
"frontendDist": "../cinny/dist",
"devUrl": "http://localhost:8080",
"beforeDevCommand": "cd cinny && npm start",
"beforeBuildCommand": "cd cinny && npm run build"
},
"app": {
"windows": [
{
"title": "Cinny",
"width": 1280,
"height": 905,
"center": true,
"resizable": true,
"fullscreen": false,
"dragDropEnabled": false
}
],
"security": {
"csp": "script-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; img-src 'self' https: http: data: blob:"
}
},
"plugins": {
"notification": {
"sound": true
},
"updater": {
"endpoints": [
"http://synbox.ruv.wtf:8418/litruv/cinny-desktop/releases/download/latest/update.json"
],
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDExREU1NkVBMDY4MzcxMjAKUldRZ2NZTUc2bGJlRVNaQldnMXpJcHlocVpCWUNNaUdicjBGMVRsck1GQXhvTnJiOUNhVVRHSzQK"
}
},
"bundle": {
"active": true,
"targets": ["msi", "appimage", "deb", "rpm"],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "SocialNetworking",
"shortDescription": "Yet another matrix client",
"longDescription": "",
"linux": {
"deb": {
"depends": []
}
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"exceptionDomain": "",
"signingIdentity": null,
"providerShortName": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"wix": {
"bannerPath": "wix/banner.bmp",
"dialogImagePath": "wix/dialogImage.bmp"
},
"nsis": null
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 KiB

91
test-api.js Normal file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env node
/**
* Paarrot API Test Script
*
* Simple test script to verify the API server is working.
* Run this while Paarrot is running to test the API endpoints.
*
* Usage: node test-api.js
*/
const API_BASE = 'http://127.0.0.1:33384';
async function testEndpoint(name, method, path, body = null) {
console.log(`\n🧪 Testing: ${name}`);
console.log(` ${method} ${API_BASE}${path}`);
try {
const options = {
method,
headers: {
'Content-Type': 'application/json',
},
};
if (body) {
options.body = JSON.stringify(body);
console.log(` Body:`, body);
}
const response = await fetch(`${API_BASE}${path}`, options);
const data = await response.json();
if (response.ok) {
console.log(` ✅ Success (${response.status})`);
console.log(` Response:`, JSON.stringify(data, null, 2));
} else {
console.log(` ❌ Failed (${response.status})`);
console.log(` Error:`, data.error || data);
}
return data;
} catch (error) {
console.log(` ❌ Error:`, error.message);
return null;
}
}
async function runTests() {
console.log('🚀 Paarrot API Test Suite\n');
console.log('Make sure Paarrot is running before running these tests!\n');
// Health check
await testEndpoint('Health Check', 'GET', '/health');
// Get status
await testEndpoint('Get Status', 'GET', '/status');
// Get channels
await testEndpoint('Get Channels', 'GET', '/channels');
// Get current room
await testEndpoint('Get Current Room', 'GET', '/room/current');
// Toggle mute
await testEndpoint('Toggle Mute', 'POST', '/mute/toggle');
// Set mute
await testEndpoint('Set Mute (true)', 'POST', '/mute', { muted: true });
await new Promise(resolve => setTimeout(resolve, 500));
await testEndpoint('Set Mute (false)', 'POST', '/mute', { muted: false });
// Toggle deafen
await testEndpoint('Toggle Deafen', 'POST', '/deafen/toggle');
// Send message to current room (commented out to avoid spam)
// await testEndpoint('Send Message to Current', 'POST', '/message/current', {
// message: 'Test message from API'
// });
// Test invalid endpoint
await testEndpoint('Invalid Endpoint (should 404)', 'GET', '/invalid');
console.log('\n✨ Tests complete!\n');
}
// Run tests if this script is executed directly
if (require.main === module) {
runTests().catch(console.error);
}
module.exports = { testEndpoint, runTests };

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