Compare commits

...

7 commits

Author SHA1 Message Date
smartfrigde
8bc66125cb chore: update rolldown 2026-09-04 10:20:39 +02:00
smartfrigde
74c955387a chore: update electron 2026-09-04 10:14:17 +02:00
dylanjkl
155ecd1f9a
fix(screenshare): give each picker tile its own preview (#1155) 2026-09-04 10:13:00 +02:00
ewof
133df0f620
fix: themes tab (#1151) 2026-09-04 10:05:21 +02:00
youtsuho
a010f5a0ed
perf: limit 1500ms startup delay to Linux only (#1157) 2026-09-04 10:03:56 +02:00
youtsuho
d3ac25a575
perf: use config cache in getEntireConfig IPC handler (#1158) 2026-09-04 10:03:33 +02:00
youtsuho
288cf7a43b
fix: resolve RPC large_image and small_image independently (#1160) 2026-09-04 10:02:49 +02:00
14 changed files with 329 additions and 217 deletions

View file

@ -16,6 +16,7 @@
"packageQuick": "pnpm run build && electron-builder --dir",
"lint": "biome check",
"lint:fix": "biome check --write",
"test": "node --experimental-strip-types --import ./tests/register-ts.mjs --test tests/screenshare-previews.test.ts",
"postinstall": "electron-builder install-app-deps",
"CIbuild": "pnpm run build && electron-builder --linux zip && electron-builder --windows zip && electron-builder --macos zip",
"updateMeta": "node --experimental-strip-types scripts/utils/updateMeta.ts"
@ -44,11 +45,11 @@
"@xmldom/xmldom": "^0.9.10",
"app-builder-lib": "^26.15.3",
"babel-preset-solid": "^1.9.12",
"electron": "43.2.0",
"electron": "44.2.0",
"electron-builder": "26.15.7",
"lucide-solid": "^1.26.0",
"monaco-editor": "0.56.0",
"rolldown": "1.2.0",
"rolldown": "1.2.7",
"rollup-plugin-copy": "^3.5.0",
"solid-js": "^1.9.14",
"solid-motionone": "^1.0.4",

249
pnpm-lock.yaml generated
View file

@ -47,7 +47,7 @@ importers:
version: 2.5.5
'@rolldown-plugin/solid':
specifier: ^0.1.0
version: 0.1.0(csstype@3.2.3)(rolldown@1.2.0)(seroval@1.5.6)(solid-js@1.9.14)
version: 0.1.0(csstype@3.2.3)(rolldown@1.2.7)(seroval@1.5.6)(solid-js@1.9.14)
'@rollup/plugin-esm-shim':
specifier: ^0.1.8
version: 0.1.8
@ -79,8 +79,8 @@ importers:
specifier: ^1.9.12
version: 1.9.12(@babel/core@8.0.1)(solid-js@1.9.14)
electron:
specifier: 43.2.0
version: 43.2.0
specifier: 44.2.0
version: 44.2.0
electron-builder:
specifier: 26.15.7
version: 26.15.7(electron-builder-squirrel-windows@26.15.3)
@ -91,8 +91,8 @@ importers:
specifier: 0.56.0
version: 0.56.0
rolldown:
specifier: 1.2.0
version: 1.2.0
specifier: 1.2.7
version: 1.2.7
rollup-plugin-copy:
specifier: ^3.5.0
version: 3.5.0
@ -613,12 +613,6 @@ packages:
'@napi-rs/wasm-runtime@0.2.12':
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
'@napi-rs/wasm-runtime@1.1.6':
resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
peerDependencies:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
'@noble/hashes@1.4.0':
resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==}
engines: {node: '>= 16'}
@ -642,8 +636,8 @@ packages:
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
engines: {node: '>= 8'}
'@oxc-project/types@0.140.0':
resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==}
'@oxc-project/types@0.148.0':
resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==}
'@parcel/watcher-android-arm64@2.6.0':
resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==}
@ -750,8 +744,14 @@ packages:
seroval: ~1.5.0
solid-js: '>=1.9.13'
'@rolldown/binding-android-arm64@1.2.0':
resolution: {integrity: sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==}
'@rolldown/binding-android-arm-eabi@1.2.7':
resolution: {integrity: sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [android]
'@rolldown/binding-android-arm64@1.2.7':
resolution: {integrity: sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
@ -761,8 +761,8 @@ packages:
cpu: [arm64]
os: [darwin]
'@rolldown/binding-darwin-arm64@1.2.0':
resolution: {integrity: sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==}
'@rolldown/binding-darwin-arm64@1.2.7':
resolution: {integrity: sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
@ -772,8 +772,8 @@ packages:
cpu: [x64]
os: [darwin]
'@rolldown/binding-darwin-x64@1.2.0':
resolution: {integrity: sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==}
'@rolldown/binding-darwin-x64@1.2.7':
resolution: {integrity: sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
@ -783,8 +783,8 @@ packages:
cpu: [x64]
os: [freebsd]
'@rolldown/binding-freebsd-x64@1.2.0':
resolution: {integrity: sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==}
'@rolldown/binding-freebsd-x64@1.2.7':
resolution: {integrity: sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
@ -794,8 +794,8 @@ packages:
cpu: [arm]
os: [linux]
'@rolldown/binding-linux-arm-gnueabihf@1.2.0':
resolution: {integrity: sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==}
'@rolldown/binding-linux-arm-gnueabihf@1.2.7':
resolution: {integrity: sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
@ -806,8 +806,8 @@ packages:
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-arm64-gnu@1.2.0':
resolution: {integrity: sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==}
'@rolldown/binding-linux-arm64-gnu@1.2.7':
resolution: {integrity: sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
@ -819,22 +819,22 @@ packages:
os: [linux]
libc: [musl]
'@rolldown/binding-linux-arm64-musl@1.2.0':
resolution: {integrity: sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==}
'@rolldown/binding-linux-arm64-musl@1.2.7':
resolution: {integrity: sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rolldown/binding-linux-ppc64-gnu@1.2.0':
resolution: {integrity: sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==}
'@rolldown/binding-linux-ppc64-gnu@1.2.7':
resolution: {integrity: sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-s390x-gnu@1.2.0':
resolution: {integrity: sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==}
'@rolldown/binding-linux-s390x-gnu@1.2.7':
resolution: {integrity: sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
@ -846,8 +846,8 @@ packages:
os: [linux]
libc: [glibc]
'@rolldown/binding-linux-x64-gnu@1.2.0':
resolution: {integrity: sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==}
'@rolldown/binding-linux-x64-gnu@1.2.7':
resolution: {integrity: sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
@ -859,15 +859,15 @@ packages:
os: [linux]
libc: [musl]
'@rolldown/binding-linux-x64-musl@1.2.0':
resolution: {integrity: sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==}
'@rolldown/binding-linux-x64-musl@1.2.7':
resolution: {integrity: sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@rolldown/binding-openharmony-arm64@1.2.0':
resolution: {integrity: sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==}
'@rolldown/binding-openharmony-arm64@1.2.7':
resolution: {integrity: sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
@ -877,18 +877,13 @@ packages:
engines: {node: '>=14.21.3'}
cpu: [wasm32]
'@rolldown/binding-wasm32-wasi@1.2.0':
resolution: {integrity: sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [wasm32]
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.1':
resolution: {integrity: sha512-NSccQD7+9vhEfDMc8HyODuUU1jLYEsEiICc1zwmbeg0FXx1pwpFpZZQby4bAMnK2obav7D9FfsruYWodhNdIqQ==}
cpu: [arm64]
os: [win32]
'@rolldown/binding-win32-arm64-msvc@1.2.0':
resolution: {integrity: sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==}
'@rolldown/binding-win32-arm64-msvc@1.2.7':
resolution: {integrity: sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
@ -903,8 +898,8 @@ packages:
cpu: [x64]
os: [win32]
'@rolldown/binding-win32-x64-msvc@1.2.0':
resolution: {integrity: sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==}
'@rolldown/binding-win32-x64-msvc@1.2.7':
resolution: {integrity: sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
@ -1144,10 +1139,12 @@ packages:
'@xmldom/xmldom@0.8.13':
resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==}
engines: {node: '>=10.0.0'}
deprecated: this version has critical issues, please update to the latest version
'@xmldom/xmldom@0.9.10':
resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==}
engines: {node: '>=14.6'}
deprecated: this version has critical issues, please update to the latest version
abbrev@4.0.0:
resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==}
@ -1278,11 +1275,6 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
baseline-browser-mapping@2.10.32:
resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==}
engines: {node: '>=6.0.0'}
hasBin: true
baseline-browser-mapping@2.11.1:
resolution: {integrity: sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==}
engines: {node: '>=6.0.0'}
@ -1313,11 +1305,6 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browserslist@4.28.2:
resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
browserslist@4.28.7:
resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@ -1350,9 +1337,6 @@ packages:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
caniuse-lite@1.0.30001793:
resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==}
caniuse-lite@1.0.30001806:
resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==}
@ -1563,9 +1547,6 @@ packages:
electron-publish@26.15.3:
resolution: {integrity: sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==}
electron-to-chromium@1.5.363:
resolution: {integrity: sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA==}
electron-to-chromium@1.5.395:
resolution: {integrity: sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA==}
@ -1576,8 +1557,8 @@ packages:
resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==}
engines: {node: '>=8.0.0'}
electron@43.2.0:
resolution: {integrity: sha512-80zvrgG7ZRXD+tD0IyLvrnN9n+veSxadMRsMaC9wKKP3iUbtC7rGM8+dVuCmOb0Rrwwv8ESW4awnUZh9Hbp1fA==}
electron@44.2.0:
resolution: {integrity: sha512-oK1icjhapp3xsUZycO9WZaLmd3hJnA7ieobC4mpdtO1pEN+PPGWpA3m1Mgzzuc1wzSD2Wai4zcH4yfpGJqxFMA==}
engines: {node: '>= 22.12.0'}
hasBin: true
@ -1970,10 +1951,6 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
js-yaml@4.3.0:
resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==}
hasBin: true
@ -2252,10 +2229,6 @@ packages:
node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
node-releases@2.0.46:
resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==}
engines: {node: '>=18'}
node-releases@2.0.51:
resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==}
engines: {node: '>=18'}
@ -2483,8 +2456,8 @@ packages:
'@babel/runtime':
optional: true
rolldown@1.2.0:
resolution: {integrity: sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==}
rolldown@1.2.7:
resolution: {integrity: sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
@ -2529,11 +2502,6 @@ packages:
engines: {node: '>=10'}
hasBin: true
semver@7.8.1:
resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==}
engines: {node: '>=10'}
hasBin: true
semver@7.8.5:
resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
engines: {node: '>=10'}
@ -2913,7 +2881,7 @@ snapshots:
import-meta-resolve: 4.2.0
json5: 2.2.3
obug: 2.1.1
semver: 7.8.1
semver: 7.8.5
'@babel/generator@7.29.7':
dependencies:
@ -2948,9 +2916,9 @@ snapshots:
dependencies:
'@babel/compat-data': 8.0.0
'@babel/helper-validator-option': 8.0.0
browserslist: 4.28.2
browserslist: 4.28.7
lru-cache: 11.5.2
semver: 7.8.1
semver: 7.8.5
'@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)':
dependencies:
@ -3440,13 +3408,6 @@ snapshots:
'@tybys/wasm-util': 0.10.3
optional: true
'@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)':
dependencies:
'@emnapi/core': 1.11.2
'@emnapi/runtime': 1.11.2
'@tybys/wasm-util': 0.10.3
optional: true
'@noble/hashes@1.4.0': {}
'@noble/hashes@2.2.0': {}
@ -3465,7 +3426,7 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.20.1
'@oxc-project/types@0.140.0': {}
'@oxc-project/types@0.148.0': {}
'@parcel/watcher-android-arm64@2.6.0':
optional: true
@ -3546,7 +3507,7 @@ snapshots:
tslib: 2.8.1
webcrypto-core: 1.9.2
'@rolldown-plugin/solid@0.1.0(csstype@3.2.3)(rolldown@1.2.0)(seroval@1.5.6)(solid-js@1.9.14)':
'@rolldown-plugin/solid@0.1.0(csstype@3.2.3)(rolldown@1.2.7)(seroval@1.5.6)(solid-js@1.9.14)':
dependencies:
'@babel/core': 8.0.1
'@babel/helper-module-imports': 8.0.0
@ -3558,68 +3519,71 @@ snapshots:
csstype: 3.2.3
html-entities: 2.6.0
parse5: 8.0.1
rolldown: 1.2.0
rolldown: 1.2.7
seroval: 1.5.6
solid-js: 1.9.14
'@rolldown/binding-android-arm64@1.2.0':
'@rolldown/binding-android-arm-eabi@1.2.7':
optional: true
'@rolldown/binding-android-arm64@1.2.7':
optional: true
'@rolldown/binding-darwin-arm64@1.0.0-beta.1':
optional: true
'@rolldown/binding-darwin-arm64@1.2.0':
'@rolldown/binding-darwin-arm64@1.2.7':
optional: true
'@rolldown/binding-darwin-x64@1.0.0-beta.1':
optional: true
'@rolldown/binding-darwin-x64@1.2.0':
'@rolldown/binding-darwin-x64@1.2.7':
optional: true
'@rolldown/binding-freebsd-x64@1.0.0-beta.1':
optional: true
'@rolldown/binding-freebsd-x64@1.2.0':
'@rolldown/binding-freebsd-x64@1.2.7':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.1':
optional: true
'@rolldown/binding-linux-arm-gnueabihf@1.2.0':
'@rolldown/binding-linux-arm-gnueabihf@1.2.7':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.1':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.2.0':
'@rolldown/binding-linux-arm64-gnu@1.2.7':
optional: true
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.1':
optional: true
'@rolldown/binding-linux-arm64-musl@1.2.0':
'@rolldown/binding-linux-arm64-musl@1.2.7':
optional: true
'@rolldown/binding-linux-ppc64-gnu@1.2.0':
'@rolldown/binding-linux-ppc64-gnu@1.2.7':
optional: true
'@rolldown/binding-linux-s390x-gnu@1.2.0':
'@rolldown/binding-linux-s390x-gnu@1.2.7':
optional: true
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.1':
optional: true
'@rolldown/binding-linux-x64-gnu@1.2.0':
'@rolldown/binding-linux-x64-gnu@1.2.7':
optional: true
'@rolldown/binding-linux-x64-musl@1.0.0-beta.1':
optional: true
'@rolldown/binding-linux-x64-musl@1.2.0':
'@rolldown/binding-linux-x64-musl@1.2.7':
optional: true
'@rolldown/binding-openharmony-arm64@1.2.0':
'@rolldown/binding-openharmony-arm64@1.2.7':
optional: true
'@rolldown/binding-wasm32-wasi@1.0.0-beta.1':
@ -3627,17 +3591,10 @@ snapshots:
'@napi-rs/wasm-runtime': 0.2.12
optional: true
'@rolldown/binding-wasm32-wasi@1.2.0':
dependencies:
'@emnapi/core': 1.11.2
'@emnapi/runtime': 1.11.2
'@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
optional: true
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.1':
optional: true
'@rolldown/binding-win32-arm64-msvc@1.2.0':
'@rolldown/binding-win32-arm64-msvc@1.2.7':
optional: true
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.1':
@ -3646,7 +3603,7 @@ snapshots:
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.1':
optional: true
'@rolldown/binding-win32-x64-msvc@1.2.0':
'@rolldown/binding-win32-x64-msvc@1.2.7':
optional: true
'@rolldown/pluginutils@1.0.1': {}
@ -4055,8 +4012,6 @@ snapshots:
base64-js@1.5.1: {}
baseline-browser-mapping@2.10.32: {}
baseline-browser-mapping@2.11.1: {}
binary-extensions@2.3.0: {}
@ -4083,14 +4038,6 @@ snapshots:
dependencies:
fill-range: 7.1.1
browserslist@4.28.2:
dependencies:
baseline-browser-mapping: 2.10.32
caniuse-lite: 1.0.30001793
electron-to-chromium: 1.5.363
node-releases: 2.0.46
update-browserslist-db: 1.2.3(browserslist@4.28.2)
browserslist@4.28.7:
dependencies:
baseline-browser-mapping: 2.11.1
@ -4146,8 +4093,6 @@ snapshots:
es-errors: 1.3.0
function-bind: 1.1.2
caniuse-lite@1.0.30001793: {}
caniuse-lite@1.0.30001806: {}
chalk@4.1.2:
@ -4312,7 +4257,7 @@ snapshots:
app-builder-lib: 26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3)
builder-util: 26.15.3
fs-extra: 10.1.0
js-yaml: 4.1.1
js-yaml: 4.3.0
transitivePeerDependencies:
- electron-builder-squirrel-windows
- supports-color
@ -4404,8 +4349,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron-to-chromium@1.5.363: {}
electron-to-chromium@1.5.395: {}
electron-updater@6.8.9:
@ -4433,7 +4376,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron@43.2.0:
electron@44.2.0:
dependencies:
'@electron-internal/extract-zip': 1.0.4
'@electron/get': 5.0.0
@ -4862,10 +4805,6 @@ snapshots:
js-tokens@4.0.0: {}
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
js-yaml@4.3.0:
dependencies:
argparse: 2.0.1
@ -5102,8 +5041,6 @@ snapshots:
node-int64@0.4.0: {}
node-releases@2.0.46: {}
node-releases@2.0.51: {}
nopt@9.0.0:
@ -5336,26 +5273,26 @@ snapshots:
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.1
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.1
rolldown@1.2.0:
rolldown@1.2.7:
dependencies:
'@oxc-project/types': 0.140.0
'@oxc-project/types': 0.148.0
'@rolldown/pluginutils': 1.0.1
optionalDependencies:
'@rolldown/binding-android-arm64': 1.2.0
'@rolldown/binding-darwin-arm64': 1.2.0
'@rolldown/binding-darwin-x64': 1.2.0
'@rolldown/binding-freebsd-x64': 1.2.0
'@rolldown/binding-linux-arm-gnueabihf': 1.2.0
'@rolldown/binding-linux-arm64-gnu': 1.2.0
'@rolldown/binding-linux-arm64-musl': 1.2.0
'@rolldown/binding-linux-ppc64-gnu': 1.2.0
'@rolldown/binding-linux-s390x-gnu': 1.2.0
'@rolldown/binding-linux-x64-gnu': 1.2.0
'@rolldown/binding-linux-x64-musl': 1.2.0
'@rolldown/binding-openharmony-arm64': 1.2.0
'@rolldown/binding-wasm32-wasi': 1.2.0
'@rolldown/binding-win32-arm64-msvc': 1.2.0
'@rolldown/binding-win32-x64-msvc': 1.2.0
'@rolldown/binding-android-arm-eabi': 1.2.7
'@rolldown/binding-android-arm64': 1.2.7
'@rolldown/binding-darwin-arm64': 1.2.7
'@rolldown/binding-darwin-x64': 1.2.7
'@rolldown/binding-freebsd-x64': 1.2.7
'@rolldown/binding-linux-arm-gnueabihf': 1.2.7
'@rolldown/binding-linux-arm64-gnu': 1.2.7
'@rolldown/binding-linux-arm64-musl': 1.2.7
'@rolldown/binding-linux-ppc64-gnu': 1.2.7
'@rolldown/binding-linux-s390x-gnu': 1.2.7
'@rolldown/binding-linux-x64-gnu': 1.2.7
'@rolldown/binding-linux-x64-musl': 1.2.7
'@rolldown/binding-openharmony-arm64': 1.2.7
'@rolldown/binding-win32-arm64-msvc': 1.2.7
'@rolldown/binding-win32-x64-msvc': 1.2.7
rollup-plugin-copy@3.5.0:
dependencies:
@ -5397,8 +5334,6 @@ snapshots:
semver@7.7.4: {}
semver@7.8.1: {}
semver@7.8.5: {}
serialize-error@7.0.1:
@ -5642,12 +5577,6 @@ snapshots:
graceful-fs: 4.2.11
node-int64: 0.4.0
update-browserslist-db@1.2.3(browserslist@4.28.2):
dependencies:
browserslist: 4.28.2
escalade: 3.2.0
picocolors: 1.1.1
update-browserslist-db@1.2.3(browserslist@4.28.7):
dependencies:
browserslist: 4.28.7

View file

@ -6,7 +6,7 @@ allowBuilds:
koffi: true
minimumReleaseAgeExclude:
- electron@42.3.3 || 43.2.0
- electron@42.3.3 || 43.2.0 || 44.2.0
overrides:
yauzl: "^3.3.1" # Electron still can't get their shit together https://github.com/electron/electron/issues/51619

View file

@ -113,6 +113,9 @@ export function getConfigLocation(): string {
const storagePath = join(userDataPath, "/storage/");
return `${storagePath}settings.json`;
}
export function getEntireConfig(): Settings {
return ensureConfigCache();
}
export function getConfig<K extends keyof Settings>(object: K): Settings[K] {
if (process.argv.includes("--safe-mode")) {

View file

@ -18,7 +18,7 @@ import {
unblacklistGame as blacklistGameRemove,
getBlacklist,
} from "../common/blacklistGame.js";
import { getConfig, getConfigLocation, setConfig, setConfigBulk } from "../common/config.js";
import { getConfig, getConfigLocation, getEntireConfig, setConfig, setConfigBulk } from "../common/config.js";
import { addDetectable, getDetectables, removeDetectable } from "../common/detectables.js";
import { getLang, getLangName, getRawLang, setLang } from "../common/lang.js";
import {
@ -331,8 +331,7 @@ export function registerIpc(passedWindow: BrowserWindow): void {
refreshGlobalKeybinds();
});
ipcMain.on("getEntireConfig", (event) => {
const rawData = readFileSync(getConfigLocation(), "utf-8");
event.returnValue = JSON.parse(rawData) as Settings;
event.returnValue = getEntireConfig();
});
ipcMain.on("getTranslations", (event) => {
event.returnValue = getRawLang();
@ -372,7 +371,7 @@ export function registerIpc(passedWindow: BrowserWindow): void {
event.returnValue = os.release();
});
ipcMain.on("copyDebugInfo", () => {
const settingsFileContent = readFileSync(getConfigLocation(), "utf-8");
const settingsFileContent = JSON.stringify(getEntireConfig(), null, 4);
clipboard.writeText(
`**OS:** ${os.platform()} ${os.version()}\n**Architecture:** ${os.arch()}\n**Legcord version:** ${getVersion()}\n**Electron version:** ${
process.versions.electron

View file

@ -11,7 +11,8 @@ import type { venmicListObject } from "../venmic.js";
interface IPCSources {
id: string;
name: string;
thumbnail: HTMLCanvasElement;
thumbnail: string;
appIcon?: string;
}
interface LegcordPluginInfo {
id: string;

View file

@ -1,5 +1,6 @@
import { desktopCapturer, ipcMain, type Streams, session } from "electron";
import { getConfig } from "../common/config.js";
import { serializeCapturerSources } from "./screenshareSources.js";
import { mainWindows } from "./window.js";
export function registerCustomHandler(): void {
@ -9,6 +10,8 @@ export function registerCustomHandler(): void {
const sources = await desktopCapturer
.getSources({
types: ["window", "screen"],
thumbnailSize: { width: 320, height: 180 },
fetchWindowIcons: true,
})
.catch((err) => console.error(err));
@ -46,8 +49,9 @@ export function registerCustomHandler(): void {
}
}
});
const pickerSources = serializeCapturerSources(sources);
mainWindows.every((window) => {
window.webContents.send("getSources", sources);
window.webContents.send("getSources", pickerSources);
return true;
});
},

View file

@ -0,0 +1,99 @@
export type NativeImageLike = {
toDataURL?: (type?: string) => string;
isEmpty?: () => boolean;
};
export type CapturerSourceLike = {
id: string;
name: string;
thumbnail?: NativeImageLike | string | null;
appIcon?: NativeImageLike | string | null;
};
export type PickerSource = {
id: string;
name: string;
thumbnail: string;
appIcon?: string;
};
function imageToDataUrl(image: NativeImageLike | string | null | undefined): string {
if (!image) return "";
if (typeof image === "string") return image;
try {
if (image.isEmpty?.()) return "";
const url = image.toDataURL?.();
return typeof url === "string" ? url : "";
} catch {
return "";
}
}
function hashString(value: string): number {
let hash = 2166136261;
for (let i = 0; i < value.length; i++) {
hash ^= value.charCodeAt(i);
hash = Math.imul(hash, 16777619);
}
return hash >>> 0;
}
function initialsFromName(name: string): string {
const parts = name
.replace(/[()[\]{}]/g, " ")
.split(/\s+/)
.filter(Boolean);
if (parts.length === 0) return "?";
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
return `${parts[0][0] ?? ""}${parts[1][0] ?? ""}`.toUpperCase();
}
/** Distinct, source-specific placeholder when thumbnails/icons are missing or duplicated. */
export function fallbackSourceVisual(id: string, name: string): string {
const hue = hashString(id || name) % 360;
const initials = initialsFromName(name || id || "Source");
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="320" height="180" viewBox="0 0 320 180">
<rect width="320" height="180" fill="hsl(${hue} 42% 28%)"/>
<text x="160" y="102" text-anchor="middle" font-family="Segoe UI,sans-serif" font-size="48" font-weight="700" fill="#fff">${initials}</text>
</svg>`;
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
}
export function serializeCapturerSources(sources: CapturerSourceLike[]): PickerSource[] {
const prepared = sources.map((source) => ({
id: source.id,
name: source.name,
thumbnailRaw: imageToDataUrl(source.thumbnail),
appIcon: imageToDataUrl(source.appIcon),
}));
const thumbnailCounts = new Map<string, number>();
for (const source of prepared) {
if (!source.thumbnailRaw) continue;
thumbnailCounts.set(source.thumbnailRaw, (thumbnailCounts.get(source.thumbnailRaw) ?? 0) + 1);
}
const usedVisuals = new Set<string>();
return prepared.map((source) => {
const thumbnailIsDuplicate =
Boolean(source.thumbnailRaw) && (thumbnailCounts.get(source.thumbnailRaw) ?? 0) > 1;
let visual = source.thumbnailRaw;
if (!visual || thumbnailIsDuplicate) {
if (source.appIcon && !usedVisuals.has(source.appIcon)) {
visual = source.appIcon;
}
}
if (!visual || usedVisuals.has(visual)) {
visual = fallbackSourceVisual(source.id, source.name);
}
usedVisuals.add(visual);
const result: PickerSource = {
id: source.id,
name: source.name,
thumbnail: visual,
};
if (source.appIcon) result.appIcon = source.appIcon;
return result;
});
}

View file

@ -354,13 +354,17 @@ if (!app.requestSingleInstanceLock() && getConfig("multiInstance") === false) {
process.on("SIGINT", () => app.quit());
process.on("SIGTERM", () => app.quit());
// Patch for linux bug to ensure things are loaded before window creation (fixes transparency on some linux systems)
await new Promise<void>((resolve) =>
setTimeout(() => {
init().then(() => {
resolve();
});
}, 1500),
);
if (process.platform === "linux") {
await new Promise<void>((resolve) =>
setTimeout(() => {
init().then(() => {
resolve();
});
}, 1500),
);
} else {
await init();
}
session.defaultSession.setPermissionRequestHandler(async (_webContents, permission, callback) => {
switch (permission) {
case "fullscreen":

View file

@ -42,33 +42,20 @@ async function listen(msg: {
FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...msg });
return console.log(`Game ${gameName} (${appId}) is blacklisted, skipping...`);
}
if (
msg.activity?.assets?.large_image?.startsWith("https://") ??
msg.activity?.assets?.small_image?.startsWith("https://")
) {
if (typeof msg.activity.assets.large_image === "string") {
msg.activity.assets.large_image = await fetchExternalAsset(
msg.activity.application_id,
msg.activity.assets.large_image,
);
if (msg.activity?.assets) {
const { large_image, small_image } = msg.activity.assets;
if (typeof large_image === "string") {
msg.activity.assets.large_image = large_image.startsWith("https://")
? await fetchExternalAsset(msg.activity.application_id, large_image)
: await fetchAssetId(msg.activity.application_id, large_image);
}
if (typeof msg.activity.assets.small_image === "string") {
msg.activity.assets.small_image = await fetchExternalAsset(
msg.activity.application_id,
msg.activity.assets.small_image,
);
if (typeof small_image === "string") {
msg.activity.assets.small_image = small_image.startsWith("https://")
? await fetchExternalAsset(msg.activity.application_id, small_image)
: await fetchAssetId(msg.activity.application_id, small_image);
}
} else {
if (msg.activity?.assets?.large_image)
msg.activity.assets.large_image = await fetchAssetId(
msg.activity.application_id,
msg.activity.assets.large_image,
);
if (msg.activity?.assets?.small_image)
msg.activity.assets.small_image = await fetchAssetId(
msg.activity.application_id,
msg.activity.assets.small_image,
);
}
console.log("RPC activity update", msg.activity);

View file

@ -5,7 +5,8 @@ import classes from "./SourceCard.module.css";
export interface IPCSources {
id: string;
name: string;
thumbnail: HTMLCanvasElement;
thumbnail: string;
appIcon?: string;
}
interface SourceCardProps {
source: IPCSources;
@ -37,7 +38,7 @@ export const SourceCard = ({ selected_name, source, onSelect }: SourceCardProps)
</Show>
<div class={classes.thumbnailWrapper}>
<img
src={source.thumbnail.toDataURL()}
src={source.thumbnail || source.appIcon}
alt={source.name}
class={isSelected() ? classes.thumbnailSelected : classes.thumbnailUnselected}
/>

View file

@ -66,23 +66,18 @@ export const ThemesCard = (props: { theme: ThemeManifest }) => {
</Show>
<div class={classes.actions}>
<button
use:tooltip={store.i18n["themes-delete"]}
title={store.i18n["themes-delete"]}
type="button"
onClick={removeTheme}
class={`${classes.btn} ${classes.btnDanger}`}
>
<img class={classes.icon} alt={store.i18n["themes-delete"]} src="legcord://assets/Trash.png" />
</button>
<button use:tooltip={store.i18n["themes-edit"]} type="button" onClick={editTheme} class={classes.btn}>
<button title={store.i18n["themes-edit"]} type="button" onClick={editTheme} class={classes.btn}>
<img class={classes.icon} alt={store.i18n["themes-edit"]} src="legcord://assets/Edit.png" />
</button>
<Show when={showUpdate}>
<button
use:tooltip={store.i18n["themes-update"]}
type="button"
onClick={updateTheme}
class={classes.btn}
>
<button title={store.i18n["themes-update"]} type="button" onClick={updateTheme} class={classes.btn}>
<img
class={classes.icon}
alt={store.i18n["themes-update"]}
@ -90,12 +85,7 @@ export const ThemesCard = (props: { theme: ThemeManifest }) => {
/>
</button>
</Show>
<button
use:tooltip={store.i18n["themes-open"]}
type="button"
onClick={openThemesFolder}
class={classes.btn}
>
<button title={store.i18n["themes-open"]} type="button" onClick={openThemesFolder} class={classes.btn}>
<img class={classes.icon} alt={store.i18n["themes-open"]} src="legcord://assets/Folder.png" />
</button>
</div>

17
tests/register-ts.mjs Normal file
View file

@ -0,0 +1,17 @@
import { existsSync } from "node:fs";
import { registerHooks } from "node:module";
import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
registerHooks({
resolve(specifier, context, nextResolve) {
if (typeof specifier === "string" && specifier.endsWith(".js") && specifier.startsWith(".")) {
const parent = context.parentURL ? fileURLToPath(context.parentURL) : process.cwd();
const tsPath = join(dirname(parent), specifier.replace(/\.js$/, ".ts"));
if (existsSync(tsPath)) {
return { url: pathToFileURL(tsPath).href, shortCircuit: true };
}
}
return nextResolve(specifier, context);
},
});

View file

@ -0,0 +1,77 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { serializeCapturerSources } from "../src/discord/screenshareSources.ts";
function fakeImage(dataUrl: string) {
return {
toDataURL: () => dataUrl,
isEmpty: () => dataUrl.length === 0,
};
}
describe("serializeCapturerSources", () => {
it("gives each source a distinct usable image and keeps names", () => {
const sources = [
{ id: "screen:0:0", name: "Entire screen", thumbnail: fakeImage("data:image/png;base64,SCREEN0") },
{ id: "window:11:0", name: "Jokel Comms DOD", thumbnail: fakeImage("data:image/png;base64,JOKEL") },
{ id: "window:22:0", name: "MSI Afterburner", thumbnail: fakeImage("data:image/png;base64,MSIAB") },
];
const result = serializeCapturerSources(sources);
assert.equal(result.length, 3);
const images = result.map((source) => source.thumbnail);
assert.equal(new Set(images).size, 3);
for (const [index, source] of result.entries()) {
assert.equal(source.id, sources[index].id);
assert.equal(source.name, sources[index].name);
assert.ok(source.thumbnail.startsWith("data:image/"), `expected data URL for ${source.name}`);
assert.ok(source.thumbnail.length > "data:image/".length);
}
});
it("uses a source-specific app icon when the thumbnail is missing", () => {
const sources = [
{
id: "window:1:0",
name: "Friends List",
thumbnail: fakeImage(""),
appIcon: fakeImage("data:image/png;base64,FRIENDICON"),
},
{
id: "window:2:0",
name: "Pull requests",
thumbnail: fakeImage(""),
appIcon: fakeImage("data:image/png;base64,PRICON"),
},
];
const result = serializeCapturerSources(sources);
assert.equal(result[0].name, "Friends List");
assert.equal(result[0].thumbnail, "data:image/png;base64,FRIENDICON");
assert.equal(result[1].name, "Pull requests");
assert.equal(result[1].thumbnail, "data:image/png;base64,PRICON");
assert.notEqual(result[0].thumbnail, result[1].thumbnail);
});
it("does not reuse a duplicated compositor thumbnail across unrelated sources", () => {
const sameFrame = "data:image/png;base64,SAMEFRAME";
const sources = [
{ id: "screen:0:0", name: "Entire screen", thumbnail: fakeImage(sameFrame) },
{
id: "window:4432:0",
name: "Jokel Comms DOD",
thumbnail: fakeImage(sameFrame),
appIcon: fakeImage("data:image/png;base64,JOKELICON"),
},
{ id: "window:9:0", name: "Legcord Screen Share Low FP", thumbnail: fakeImage(sameFrame) },
];
const result = serializeCapturerSources(sources);
const images = result.map((source) => source.thumbnail);
assert.equal(new Set(images).size, 3);
assert.equal(result[1].thumbnail, "data:image/png;base64,JOKELICON");
for (const source of result) {
assert.ok(source.thumbnail.startsWith("data:image/"));
}
});
});