7d3140334e
- conversations: dm (unique pair, find-or-create), group (titled), project (customer-bound); participant-only access - messages: server-sanitized rich text, attachments referencing uploads from the generic POST /api/v1/files endpoint (rate limited, MIME sniffed) - unread counts via aggregation; mark-read pushes readBy receipts - chat files served only to conversation participants (or uploader) - @mentions in chat notify the mentioned participant - typing indicator: client WS event relayed to other participants - WS targeted delivery (SendTo) + 15s polling fallback - two-pane messages UI: conversation list with unread badges, composer (contenteditable + formatting buttons), drag & drop upload, inline image previews with lightbox, new-conversation dialog with user search Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
73 lines
3.1 KiB
HTML
73 lines
3.1 KiB
HTML
{{define "content"}}
|
||
<div class="error-box" id="error" role="alert"></div>
|
||
<div class="chat-layout">
|
||
<aside class="chat-sidebar card">
|
||
<div class="spread">
|
||
<h2>Messages</h2>
|
||
<button class="btn small primary" id="conv-new">+ New</button>
|
||
</div>
|
||
<ul id="conv-list" class="conv-list"></ul>
|
||
</aside>
|
||
<section class="chat-main card">
|
||
<div class="spread">
|
||
<h2 id="chat-title">Select a conversation</h2>
|
||
<span class="muted" id="typing-indicator" aria-live="polite"></span>
|
||
</div>
|
||
<div id="chat-messages" class="chat-messages" aria-live="polite"></div>
|
||
<form id="chat-form" hidden>
|
||
<div id="chat-attachments" class="chat-attachments"></div>
|
||
<div class="chat-toolbar" role="toolbar" aria-label="Formatting">
|
||
<button class="btn small" type="button" data-cmd="bold" aria-label="Bold"><b>B</b></button>
|
||
<button class="btn small" type="button" data-cmd="italic" aria-label="Italic"><i>I</i></button>
|
||
<button class="btn small" type="button" data-cmd="underline" aria-label="Underline"><u>U</u></button>
|
||
<button class="btn small" type="button" data-cmd="strikeThrough" aria-label="Strikethrough"><s>S</s></button>
|
||
<button class="btn small" type="button" data-cmd="insertUnorderedList" aria-label="Bulleted list">• list</button>
|
||
<label class="btn small" style="cursor:pointer">📎<input type="file" id="chat-file" multiple hidden></label>
|
||
</div>
|
||
<div id="chat-composer" class="chat-composer" contenteditable="true" role="textbox"
|
||
aria-multiline="true" aria-label="Message" data-placeholder="Write a message… (drag & drop files)"></div>
|
||
<div class="spread mt">
|
||
<span class="hint">Enter to send · Shift+Enter for newline</span>
|
||
<button class="btn primary" type="submit">Send</button>
|
||
</div>
|
||
</form>
|
||
</section>
|
||
</div>
|
||
|
||
<dialog id="newconv-dialog">
|
||
<form method="dialog" id="newconv-form" class="stack" style="min-width:420px">
|
||
<h2>New conversation</h2>
|
||
<div class="field">
|
||
<label for="nc-kind">Kind</label>
|
||
<select id="nc-kind">
|
||
<option value="dm">Direct message</option>
|
||
<option value="group">Group</option>
|
||
<option value="project">Project</option>
|
||
</select>
|
||
</div>
|
||
<div class="field" id="nc-title-wrap" hidden>
|
||
<label for="nc-title">Title</label>
|
||
<input type="text" id="nc-title">
|
||
</div>
|
||
<div class="field" id="nc-customer-wrap" hidden>
|
||
<label for="nc-customer">Customer id</label>
|
||
<input type="text" id="nc-customer" placeholder="paste customer id">
|
||
</div>
|
||
<div class="field">
|
||
<label for="nc-search">Participants</label>
|
||
<input type="search" id="nc-search" placeholder="Search people…">
|
||
<div id="nc-results" class="stack" style="max-height:160px;overflow:auto"></div>
|
||
<div id="nc-selected" class="mt"></div>
|
||
</div>
|
||
<div class="spread">
|
||
<button class="btn" type="button" id="nc-cancel">Cancel</button>
|
||
<button class="btn primary" type="submit">Start conversation</button>
|
||
</div>
|
||
</form>
|
||
</dialog>
|
||
|
||
<dialog id="lightbox" class="lightbox">
|
||
<img id="lightbox-img" alt="">
|
||
</dialog>
|
||
{{end}}
|