new modes

This commit is contained in:
2026-04-30 19:16:41 -04:00
parent ca6db6628b
commit fdd9d71ee9
4 changed files with 72 additions and 16 deletions

View File

@@ -34,8 +34,8 @@ ALLOWED_CHARS = string.ascii_letters + string.digits + '_-'
# Hidden dial codes - kept server-side so the client JS bundle never reveals them.
# Sequence the user types on the in-app dialpad maps to a server action.
DIAL_CODES = {
'*4201#': 'trippy_on',
'*4202#': 'trippy_off',
'*420#': 'trippy_toggle', # toggles UI trippy mode globally for everyone
'*1337#': 'rainbow_nick_toggle', # toggles the dialer's own rainbow nick
}
DIAL_MAX_LEN = 32
@@ -211,7 +211,7 @@ async def websocket_handler(request: web.Request) -> web.WebSocketResponse:
await ws.prepare(request)
client_id = str(uuid.uuid4())[:8]
clients[client_id] = {'ws': ws, 'username': None, 'cam_on': False, 'mic_on': True, 'screen_on': False}
clients[client_id] = {'ws': ws, 'username': None, 'cam_on': False, 'mic_on': True, 'screen_on': False, 'rainbow_nick': False}
logging.info(f'[{client_id}] Connected')
@@ -274,7 +274,7 @@ async def handle_message(client_id: str, data: dict):
reconnect_tokens[reconnect_token] = {'username': username, 'expires': time.time() + 3600}
users = [
{'id': cid, 'username': c['username'], 'cam_on': c.get('cam_on', False), 'mic_on': c.get('mic_on', True), 'screen_on': c.get('screen_on', False)}
{'id': cid, 'username': c['username'], 'cam_on': c.get('cam_on', False), 'mic_on': c.get('mic_on', True), 'screen_on': c.get('screen_on', False), 'rainbow_nick': c.get('rainbow_nick', False)}
for cid, c in clients.items()
if c['username'] and cid != client_id
]
@@ -338,7 +338,7 @@ async def handle_message(client_id: str, data: dict):
reconnect_tokens[new_token] = {'username': username, 'expires': time.time() + 3600}
users = [
{'id': cid, 'username': c['username'], 'cam_on': c.get('cam_on', False), 'mic_on': c.get('mic_on', True), 'screen_on': c.get('screen_on', False)}
{'id': cid, 'username': c['username'], 'cam_on': c.get('cam_on', False), 'mic_on': c.get('mic_on', True), 'screen_on': c.get('screen_on', False), 'rainbow_nick': c.get('rainbow_nick', False)}
for cid, c in clients.items()
if c['username'] and cid != client_id
]
@@ -428,14 +428,21 @@ async def handle_message(client_id: str, data: dict):
if len(sequence) > DIAL_MAX_LEN:
return
action = DIAL_CODES.get(sequence)
if action == 'trippy_on' and not trippy_mode:
trippy_mode = True
logging.info(f'[{client_id}] Trippy mode ENABLED')
await broadcast_all({'type': 'trippy_status', 'enabled': True})
elif action == 'trippy_off' and trippy_mode:
trippy_mode = False
logging.info(f'[{client_id}] Trippy mode DISABLED')
await broadcast_all({'type': 'trippy_status', 'enabled': False})
if action == 'trippy_toggle':
trippy_mode = not trippy_mode
logging.info(f'[{client_id}] Trippy mode -> {trippy_mode}')
await broadcast_all({'type': 'trippy_status', 'enabled': trippy_mode})
elif action == 'rainbow_nick_toggle':
# Per-user toggle: only flips the dialer's own nick. Broadcast so every
# other client renders the rainbow effect on this user in their list.
current = clients[client_id].get('rainbow_nick', False)
clients[client_id]['rainbow_nick'] = not current
logging.info(f'[{client_id}] Rainbow nick -> {not current}')
await broadcast_all({
'type' : 'nick_status',
'id' : client_id,
'rainbow' : not current
})
async def broadcast(sender_id: str, message: dict):

View File

@@ -344,11 +344,18 @@ function handleSignal(data) {
// Preserve local state on reconnect, or initialize
if (!state.users['local']) {
state.users['local'] = { username: state.username, camOn: state.camEnabled, micOn: state.micEnabled, screenOn: state.screenEnabled, speaking: false };
state.users['local'] = { username: state.username, camOn: state.camEnabled, micOn: state.micEnabled, screenOn: state.screenEnabled, rainbowNick: false, speaking: false };
}
data.users.forEach(user => {
state.users[user.id] = { username: user.username, camOn: user.cam_on, micOn: user.mic_on !== false, screenOn: user.screen_on || false, speaking: false };
state.users[user.id] = {
username: user.username,
camOn: user.cam_on,
micOn: user.mic_on !== false,
screenOn: user.screen_on || false,
rainbowNick: !!user.rainbow_nick,
speaking: false
};
createPeerConnection(user.id, user.username, true);
});
@@ -484,6 +491,17 @@ function handleSignal(data) {
setTrippyMode(!!data.enabled);
break;
case 'nick_status':
// Per-user rainbow nick toggle. Server tells us when ANY user (including
// us) flips theirs - we just mirror it into local state and re-render.
if (data.id === state.myId) {
if (state.users['local']) state.users['local'].rainbowNick = !!data.rainbow;
} else if (state.users[data.id]) {
state.users[data.id].rainbowNick = !!data.rainbow;
}
updateUsersList();
break;
}
}

View File

@@ -268,7 +268,7 @@ function updateUsersList() {
<div class="user-item ${user.speaking ? 'speaking' : ''} ${user.isLocal ? 'local' : ''}" data-id="${user.id}">
<div class="user-info">
${user.isLocal ? '' : getNetworkQualityHTML(user.id)}
<span class="user-name">${escapeHtml(user.username)}</span>
<span class="user-name${user.rainbowNick ? ' rainbow-nick' : ''}">${escapeHtml(user.username)}</span>
<div class="user-indicators">
${user.micOn === false ? '<svg class="indicator mic-muted" viewBox="0 0 24 24" fill="currentColor" title="Muted"><path d="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM4.27 3L3 4.27l6.01 6.01V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z"/></svg>' : ''}
${user.camOn ? '<svg class="indicator cam-on" viewBox="0 0 24 24" fill="currentColor" title="Camera On"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>' : ''}

View File

@@ -1968,3 +1968,34 @@ body.trippy-mode .video-tile {
body.trippy-mode .video-tile video {
animation: trippy-hue-counter 12s linear infinite;
}
/* ========== RAINBOW NICK ==========
* Per-user toggle (server-side, broadcast to everyone). Renders the username text
* as a flowing rainbow gradient. Works underneath trippy-mode too - the trippy
* filter just hue-shifts the rainbow, which still looks rainbow. */
.rainbow-nick {
background: linear-gradient(
90deg,
hsl(0, 90%, 60%),
hsl(45, 95%, 60%),
hsl(90, 85%, 55%),
hsl(135, 80%, 55%),
hsl(180, 85%, 55%),
hsl(225, 90%, 65%),
hsl(270, 85%, 65%),
hsl(315, 90%, 60%),
hsl(360, 90%, 60%)
);
background-size: 200% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-fill-color: transparent;
animation: rainbow-nick-shift 3s linear infinite;
font-weight: 600;
}
@keyframes rainbow-nick-shift {
0% { background-position: 0% 50%; }
100% { background-position: 200% 50%; }
}