Add rooms, per-network file sharing, and file transfer UX
Additional rooms: - Store tracks customRooms per networkId; createRoom action slugifies and deduplicates, switches active room on creation - Sidebar: + button next to Rooms label opens an inline input; Escape dismisses; Enter creates the room Per-network share directories: - Store tracks sharedFilesByNetwork keyed by networkId instead of a single global map; setSharedFiles(files, networkId?) defaults to activeNetworkId - FolderPicker reads per-network map to display accurate file count label File transfer UX: - Incoming file-offer now emits pending_offer instead of auto-accepting; user sees Accept / Reject buttons in the sidebar Transfers section - Progress events emitted per chunk during receive; Transfers panel shows a live progress bar with received/total sizes - file-cancel protocol message: sender or receiver can cancel an in-flight transfer; DataChannel is closed and buffers discarded - PeerConn: acceptOffer(), rejectOffer(), cancelRecv(), cancelSend() - BrowserAdapter: acceptOffer(), rejectOffer(), cancelTransfer() - Store: pendingOffers, fileProgress, acceptOffer, rejectOffer, cancelTransfer - Transfers component renders in sidebar; auto-hides when no activity Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,12 @@ details summary { color: var(--muted); font-size: 12px; cursor: pointer; }
|
||||
.sidebar-section { display: flex; flex-direction: column; padding: 8px 0; border-bottom: 1px solid var(--border); }
|
||||
.sidebar-section:last-child { border-bottom: none; flex: 1; }
|
||||
.sidebar-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); padding: 4px 12px 2px; }
|
||||
.sidebar-label-row { display: flex; align-items: center; justify-content: space-between; padding-right: 8px; }
|
||||
.sidebar-label-row .sidebar-label { padding-right: 0; }
|
||||
.sidebar-add { background: none; color: var(--muted); font-size: 14px; padding: 0 4px; line-height: 1; }
|
||||
.sidebar-add:hover { color: var(--accent); background: none; }
|
||||
.sidebar-new-room { padding: 2px 8px 4px; }
|
||||
.sidebar-new-room input { font-size: 12px; padding: 4px 8px; }
|
||||
.sidebar-item { background: none; color: var(--text); text-align: left; padding: 5px 12px; border-radius: 0; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
|
||||
.sidebar-item:hover { background: rgba(255,255,255,0.04); }
|
||||
.sidebar-item.active { background: var(--accent); color: #fff; }
|
||||
@@ -88,6 +94,19 @@ details summary { color: var(--muted); font-size: 12px; cursor: pointer; }
|
||||
.compose input { flex: 1; width: auto; }
|
||||
.compose button { flex-shrink: 0; }
|
||||
|
||||
/* ── transfers panel (in sidebar) ── */
|
||||
.transfer-row { display: flex; flex-direction: column; gap: 3px; padding: 6px 12px; border-bottom: 1px solid var(--border); }
|
||||
.transfer-row:last-child { border-bottom: none; }
|
||||
.transfer-name { font-size: 12px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.transfer-meta { font-size: 10px; color: var(--muted); }
|
||||
.transfer-actions { display: flex; gap: 4px; margin-top: 2px; }
|
||||
.transfer-btn { font-size: 11px; padding: 2px 8px; }
|
||||
.transfer-btn.accept { background: #3a7a3a; }
|
||||
.transfer-btn.reject { background: #7a3a3a; }
|
||||
.transfer-btn:hover { opacity: 0.85; }
|
||||
.transfer-progress { height: 3px; background: var(--border); border-radius: 2px; overflow: hidden; margin-top: 2px; }
|
||||
.transfer-progress-bar { height: 100%; background: var(--accent); transition: width 0.1s; }
|
||||
|
||||
/* ── file browser panel ── */
|
||||
.chat-layout.has-file-browser { grid-template-columns: var(--sidebar-w) 1fr 280px; }
|
||||
.file-browser { display: flex; flex-direction: column; background: var(--surface); border-left: 1px solid var(--border); }
|
||||
|
||||
Reference in New Issue
Block a user