feat: Matcha! social integration — friends, chat, DMs, avatars, presence

Add full Matcha social panel (butter.lat API) as a right-side slide-out:

Backend (matchaService.js):
- HTTP client for auth, friends, messages, unread, avatar, heartbeat APIs
- WebSocket with auto-reconnect (exponential backoff, no hard cap)
- Token management via config, presence heartbeat every 30s
- WS error message type handling, game running presence

Renderer (matcha.js):
- State machine UI: intro → login/register → app (friends/chat/DMs/profile)
- Two-phase registration with master key display and verification
- Friends list with presence dots, collapsible requests, 12s polling
- Global chat + DM with optimistic rendering, cursor pagination
- Scroll position preserved on load-more, separate loading flags
- Clickable URLs in messages (linkify with proper escaping)
- User profile popup with avatar upload/delete
- Unread badges (messages + friend requests) on nav icon
- Escape key closes panel/overlay, try/catch on auth flows

IPC bridge (preload.js + main.js):
- 21 IPC invoke methods + 8 WS event listeners
- Avatar upload via file picker dialog in main process
- Game launch sets in_game heartbeat state

CSS (style.css):
- ~1500 lines: panel, auth screens, friends, chat, profile, toast
- Responsive panel width, improved contrast, no overflow clipping
- Loading states, disabled states, pulse animations

Credits: Powered by Butter Launcher & Matcha! (butterlauncher.tech)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
sanasol
2026-03-02 00:49:37 +01:00
parent 57056e5b7a
commit a40465cac9
9 changed files with 4219 additions and 5 deletions

View File

@@ -1087,6 +1087,48 @@ function _generateWindowsWrapper(stripFlags, alwaysArgs, serverArgs) {
return lines.join('\r\n');
}
// =============================================================================
// MATCHA SOCIAL AUTH
// =============================================================================
function saveMatchaToken(token) {
saveConfig({ matchaToken: token || null });
}
function loadMatchaToken() {
const config = loadConfig();
return config.matchaToken || null;
}
function saveMatchaHandle(handle) {
saveConfig({ matchaHandle: handle || null });
}
function loadMatchaHandle() {
const config = loadConfig();
return config.matchaHandle || null;
}
function saveMatchaUserId(id) {
saveConfig({ matchaUserId: id || null });
}
function loadMatchaUserId() {
const config = loadConfig();
return config.matchaUserId || null;
}
function clearMatchaAuth() {
const config = loadConfig();
delete config.matchaToken;
delete config.matchaUserId;
delete config.matchaHandle;
const data = JSON.stringify(config, null, 2);
fs.writeFileSync(CONFIG_TEMP, data, 'utf8');
if (fs.existsSync(CONFIG_FILE)) fs.copyFileSync(CONFIG_FILE, CONFIG_BACKUP);
fs.renameSync(CONFIG_TEMP, CONFIG_FILE);
}
// =============================================================================
// EXPORTS
// =============================================================================
@@ -1162,6 +1204,15 @@ module.exports = {
resetWrapperConfig,
generateWrapperScript,
// Matcha Social
saveMatchaToken,
loadMatchaToken,
saveMatchaHandle,
loadMatchaHandle,
saveMatchaUserId,
loadMatchaUserId,
clearMatchaAuth,
// Constants
CONFIG_FILE,
UUID_STORE_FILE