relay: file attachments, outbox, CC-self, chat view, mobile layout

Compose: attach files (encrypted, hex-named, linked via relay_uid),
CC-self checkbox tags copy with recipient name. Outbox section shows
sent messages with delete. Chat view: session-based threads/history/
send endpoints, split layout with sidebar, read receipts, 5s poll.
Mobile: threads as horizontal scroll bar above messages. Fix chat
input pushed offscreen by adding min-height:0 on flex container.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
thandClaude Opus 4.6 committed 2026-04-13 08:48:23 +02:00
1 parent 9f85e484c4
commit 6c0ca4373b
8 files changed
+547 -20

No files matched your search

+34 -1
View File
@@ -22,6 +22,7 @@ if (!defined('RELAY_BOOTSTRAPPED')) { http_response_code(404); exit; }
<div id="nav-buttons" class="hidden">
<span id="nav-user" class="user"></span>
<button id="nav-compose" class="nav-btn active" onclick="showView('compose')"><i class="ph ph-pencil-line"></i> compose</button>
<button id="nav-chat" class="nav-btn" onclick="showView('chat')"><i class="ph ph-chat-circle-dots"></i> chat</button>
<button id="nav-account" class="nav-btn" onclick="showView('account')"><i class="ph ph-user-circle"></i> account</button>
<button id="nav-admin" class="nav-btn hidden" onclick="showView('admin')"><i class="ph ph-users"></i> users</button>
<button class="nav-btn danger" onclick="doLogout()"><i class="ph ph-sign-out"></i> logout</button>
@@ -91,12 +92,18 @@ if (!defined('RELAY_BOOTSTRAPPED')) { http_response_code(404); exit; }
<label>body</label>
<textarea id="compose-body" placeholder="write something..."></textarea>
</div>
<div id="compose-attachments" class="compose-attachments"></div>
<label class="checkbox" id="compose-cc-self-wrap" style="margin-top:4px;">
<input type="checkbox" id="compose-cc-self"> keep a copy (tagged with recipient name)
</label>
<div class="actions">
<button class="btn btn-secondary" onclick="clearCompose()"><i class="ph ph-x"></i> clear</button>
<button id="compose-attach-btn" class="btn btn-secondary"><i class="ph ph-paperclip"></i> attach</button>
<input type="file" id="compose-file" multiple class="hidden">
<span class="spacer"></span>
<span id="compose-status" class="status"></span>
<button class="btn btn-primary" onclick="sendCompose()"><i class="ph ph-paper-plane-tilt"></i> send</button>
</div>
<div id="compose-status" class="compose-status-bar"></div>
</div>
<div id="inbox-section" class="section">
<h2><i class="ph ph-tray"></i> your inbox <span class="count" id="inbox-count">0</span>
@@ -105,6 +112,10 @@ if (!defined('RELAY_BOOTSTRAPPED')) { http_response_code(404); exit; }
</h2>
<div id="inbox-list"></div>
</div>
<div id="outbox-section" class="section">
<h2><i class="ph ph-paper-plane-tilt"></i> sent <span class="count" id="outbox-count">0</span></h2>
<div id="outbox-list"></div>
</div>
</div>
<!-- ── Forced password change (first login) ── -->
@@ -193,6 +204,28 @@ if (!defined('RELAY_BOOTSTRAPPED')) { http_response_code(404); exit; }
</div>
</div>
<!-- ── Chat view ── -->
<div id="view-chat" class="hidden">
<div class="chat-layout">
<div class="chat-sidebar">
<div class="chat-sidebar-header">
<span class="pixel" style="font-size:10px;color:var(--accent);">conversations</span>
</div>
<div id="chat-thread-list"></div>
</div>
<div class="chat-main">
<div id="chat-header" class="chat-header">
<span id="chat-peer-name" class="pixel" style="font-size:11px;color:var(--bright);">select a conversation</span>
</div>
<div id="chat-messages" class="chat-messages"></div>
<div id="chat-input-bar" class="chat-input-bar hidden">
<input type="text" id="chat-input" placeholder="type a message..." autocomplete="off">
<button id="chat-send-btn" class="btn btn-primary" style="padding:6px 14px;"><i class="ph ph-paper-plane-tilt"></i></button>
</div>
</div>
</div>
</div>
</main>
<!-- Confirm dialog (replaces native confirm()) -->