bzl

self-hosted ephemeral community engine
Log | Files | Refs | README | LICENSE

index.html (32710B)


      1 <!doctype html>
      2 <html lang="en">
      3   <head>
      4     <meta charset="UTF-8" />
      5     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      6     <title>Bzl - Hives</title>
      7     <link rel="stylesheet" href="/styles.css?v=130" />
      8   </head>
      9   <body>
     10       <div class="app">
     11         <button id="showSidebar" class="ghost smallBtn sidebarToggle hidden" type="button" title="Show sidebar">Show</button>
     12         <button id="togglePeople" class="ghost smallBtn peopleToggle" type="button" title="Show people">People</button>
     13         <button id="showRightRack" class="ghost smallBtn rightRackToggle hidden" type="button" title="Show right rack">Right</button>
     14         <aside class="sidebar">
     15         <div class="sidebarScroll">
     16           <div class="brand">
     17             <div id="instanceTitle" class="logo">Bzl</div>
     18             <div id="instanceSubtitle" class="subtitle">Ephemeral hives + chat</div>
     19           </div>
     20 
     21         <div class="statusBlock">
     22           <div id="connBadge" class="badge badge-warn">Connecting...</div>
     23           <div class="row">
     24             <button id="enableNotifs" class="ghost smallBtn grow" type="button">Enable notifications</button>
     25           </div>
     26           <div id="notifStatus" class="small muted"></div>
     27         </div>
     28 
     29         <section id="viewPanel" class="panel">
     30           <div class="panelTitle">View</div>
     31           <div class="uiHint">Use layout presets for quick panel setups. Shortcuts: <b>[</b>/<b>]</b> cycle presets, <b>-</b>/<b>=</b> cycle hives/chats in the active panel, <b>?</b> opens shortcut help.</div>
     32           <label class="checkRow" style="margin-top:8px;">
     33             <span>Rack layout (experimental)</span>
     34             <input id="toggleRackLayout" type="checkbox" />
     35           </label>
     36           <label style="margin-top:10px;">
     37             <span>Layout preset</span>
     38             <select id="layoutPreset">
     39               <option value="discordLike">Discord-like</option>
     40               <option value="chat">Chat</option>
     41               <option value="browsing">Browsing</option>
     42               <option value="maps">Maps</option>
     43               <option value="moderation">Moderation</option>
     44               <option value="focus">Focus</option>
     45               <option value="clean">Clean</option>
     46               <option value="ops">Ops</option>
     47             </select>
     48           </label>
     49           <label class="checkRow" style="margin-top:8px;">
     50             <span>Side panels</span>
     51             <input id="toggleSideRack" type="checkbox" checked />
     52           </label>
     53           <label class="checkRow" style="margin-top:8px;">
     54             <span>Right rack</span>
     55             <input id="toggleRightRack" type="checkbox" checked />
     56           </label>
     57           <label class="checkRow" style="margin-top:8px;">
     58             <span>Show reactions bar</span>
     59             <input id="toggleReactions" type="checkbox" />
     60           </label>
     61           <label class="checkRow" style="margin-top:8px;">
     62             <span>Stay connected</span>
     63             <input id="stayConnected" type="checkbox" />
     64           </label>
     65           <label class="checkRow" style="margin-top:8px;">
     66             <span>Enable hints</span>
     67             <input id="enableHints" type="checkbox" />
     68           </label>
     69           <label style="margin-top:10px;">
     70             <span>Chat send key</span>
     71             <select id="chatEnterMode">
     72               <option value="ctrlEnter">Ctrl/Cmd + Enter sends</option>
     73               <option value="enter">Enter sends (Shift+Enter newline)</option>
     74             </select>
     75           </label>
     76           <div class="row" style="margin-top:8px; gap:8px; flex-wrap:wrap;">
     77             <button id="openShortcutHelp" class="ghost smallBtn" type="button">Shortcut help</button>
     78             <button id="resetCurrentLayout" class="ghost smallBtn" type="button">Reset layout</button>
     79           </div>
     80 
     81           <details style="margin-top:10px;">
     82             <summary class="small muted" style="cursor:pointer;user-select:none;">Advanced display</summary>
     83             <div style="margin-top:10px;">
     84               <label>
     85                 <span>Text size</span>
     86                 <select id="uiScale">
     87                   <option value="auto" selected>Auto</option>
     88                   <option value="xs">Compact</option>
     89                   <option value="sm">Small</option>
     90                   <option value="md">Default</option>
     91                   <option value="lg">Large</option>
     92                 </select>
     93               </label>
     94               <label style="margin-top:10px;">
     95                 <span>Device layout</span>
     96                 <select id="deviceLayout">
     97                   <option value="auto" selected>Auto</option>
     98                   <option value="widescreen">16:9 / 16:10</option>
     99                   <option value="fourThree">4:3</option>
    100                   <option value="threeTwo">3:2</option>
    101                   <option value="ultrawide">Ultrawide</option>
    102                   <option value="portrait">Portrait</option>
    103                 </select>
    104               </label>
    105             </div>
    106           </details>
    107         </section>
    108 
    109         <section id="accountPanel" class="panel">
    110           <div class="panelTitle">Account</div>
    111           <div class="small muted" id="authHint">Sign in to post, chat, and boost.</div>
    112           <div class="uiHint">New here: create an account, then open a hive and tap <b>Chat</b> to join a conversation.</div>
    113 
    114           <div class="small muted">Signed in as</div>
    115           <div id="userLabel" class="userLine">Signed out</div>
    116 
    117           <form id="authForm" class="form">
    118             <label>
    119               <span>Username</span>
    120               <input id="authUser" autocomplete="username" />
    121             </label>
    122             <label>
    123               <span>Password</span>
    124               <input id="authPass" type="password" autocomplete="current-password" />
    125             </label>
    126             <label id="codeRow" class="hidden">
    127               <span>Registration code</span>
    128               <input id="authCode" autocomplete="one-time-code" />
    129             </label>
    130             <div class="row">
    131               <button class="primary grow" type="submit">Sign in</button>
    132               <button id="registerBtn" class="ghost" type="button">Create account</button>
    133             </div>
    134             <button id="logoutBtn" class="ghost hidden" type="button">Sign out</button>
    135             <div class="small muted">
    136               Note: this is a prototype; don't reuse important passwords.
    137             </div>
    138           </form>
    139           <div id="onboardingCard" class="onboardingCard hidden">
    140             <div class="panelTitle">Onboarding</div>
    141             <div id="onboardingBody" class="small muted"></div>
    142             <div class="row" style="margin-top:8px;">
    143               <button id="onboardingAccept" class="primary grow hidden" type="button">Accept and continue</button>
    144               <button id="onboardingRefresh" class="ghost" type="button">Refresh</button>
    145             </div>
    146           </div>
    147         </section>
    148 
    149         <section id="profilePanel" class="panel">
    150           <div class="panelTitle">Profile</div>
    151           <div class="small muted">Set how your name appears.</div>
    152           <div class="uiHint">Your profile card appears in People and when someone opens your profile from a post or chat.</div>
    153           <label>
    154             <span>Profile picture</span>
    155             <input id="profileImage" type="file" accept="image/*" />
    156           </label>
    157           <div class="row">
    158             <div class="pfpBox">
    159               <img id="profilePreview" alt="Profile preview" />
    160             </div>
    161             <div class="grow">
    162               <label>
    163                 <span>Color</span>
    164                 <input id="nameColor" type="color" value="#ff3ea5" />
    165               </label>
    166               <button id="removeProfileImage" class="ghost" type="button">Remove picture</button>
    167             </div>
    168           </div>
    169           <button id="saveProfile" class="primary" type="button">Save profile</button>
    170           <div id="profileStatus" class="small muted"></div>
    171         </section>
    172 
    173         </div>
    174 
    175         <div class="sidebarFooter">
    176           <div class="poweredByTile" aria-label="Powered by Bzl">
    177             <img class="poweredByLogo" src="/assets/logobzl.png" alt="Bzl logo" />
    178             <div class="poweredByText">
    179               <div class="poweredByTitle">Powered by Bzl</div>
    180               <div class="poweredByByline">by Azakaela Erin Redfire</div>
    181             </div>
    182           </div>
    183           <button id="toggleSidebar" class="ghost smallBtn" type="button" title="Hide sidebar">Hide</button>
    184         </div>
    185       </aside>
    186 
    187       <div id="sidebarResizeHandle" class="panelResizeHandle sidebarResizeHandle" title="Drag to resize sidebar" aria-hidden="true"></div>
    188 
    189       <main class="main">
    190         <div id="mainRack" class="mainRack">
    191           <button id="showSideRack" class="ghost smallBtn sideRackToggle hidden" type="button" title="Show side panels">Side</button>
    192           <div id="mainWorkspaceRack" class="workspaceRack" aria-label="Workspace">
    193         <section id="hivesPanel" class="panel panelFill">
    194           <div class="panelHeader">
    195             <div class="panelTitle">Hives</div>
    196             <div class="filters">
    197               <input id="filterAuthor" placeholder="Filter by author (@name)" />
    198               <input id="filterKeywords" placeholder="Filter by keywords (comma separated)" />
    199               <select id="sortBy">
    200                 <option value="activity">Most recent activity</option>
    201                 <option value="popular">Most popular</option>
    202                 <option value="expiring">About to expire</option>
    203               </select>
    204               <button id="mobileHiveSearch" class="ghost smallBtn mobileOnlyHiveControl" type="button" title="Search hives">🔎</button>
    205               <button id="mobileSortCycle" class="ghost smallBtn mobileOnlyHiveControl" type="button" title="Cycle sort">Recent</button>
    206               <button id="clearFilter" type="button">Clear</button>
    207               <button id="toggleComposer" class="mobileComposerToggle" type="button">New Hive</button>
    208             </div>
    209           </div>
    210           <div class="uiHint">Use filters to narrow posts, then tap <b>Chat</b> on a hive card. Cards now show latest chat/typing. Shortcut: <b>-</b>/<b>=</b> cycles collections/views.</div>
    211           <div class="hiveTabs" id="hiveTabs">
    212             <button type="button" data-hiveview="all" class="primary">All</button>
    213             <button type="button" data-hiveview="starred" class="ghost">Starred</button>
    214             <button type="button" data-hiveview="hidden" class="ghost">Hidden</button>
    215           </div>
    216           <div id="feed" class="feed"></div>
    217         </section>
    218 
    219         <section id="onboardingPanel" class="panel panelFill hidden">
    220           <div class="panelHeader">
    221             <div>
    222               <div class="panelTitle">Onboarding</div>
    223               <div class="small muted">About, rules, and first steps.</div>
    224             </div>
    225           </div>
    226           <div class="panelBody onboardingPanelBody">
    227             <div class="uiHint">Read About and Rules first. If required, accept rules to unlock posting and chat.</div>
    228             <div id="onboardingPanelBody" class="small muted"></div>
    229             <div class="row" style="margin-top:10px;">
    230               <button id="onboardingPanelAccept" class="primary grow hidden" type="button">Accept and continue</button>
    231               <button id="onboardingPanelRefresh" class="ghost" type="button">Refresh</button>
    232             </div>
    233           </div>
    234         </section>
    235 
    236         <section id="profileViewPanel" class="panel panelFill hidden">
    237           <div class="panelHeader">
    238             <div>
    239               <div class="panelTitle" id="profileViewTitle">Profile</div>
    240               <div id="profileViewMeta" class="small muted"></div>
    241             </div>
    242             <div class="row">
    243               <button id="profileBackBtn" class="ghost smallBtn" type="button">Back to hives</button>
    244               <button id="profileEditToggleBtn" class="ghost smallBtn hidden" type="button">Edit profile</button>
    245             </div>
    246           </div>
    247           <div class="uiHint">Tip: open someone from People, a hive card, or a chat message to view their profile here.</div>
    248           <div id="profileViewBody" class="profileViewBody">
    249             <div id="profileCard" class="profileCard"></div>
    250           </div>
    251           <div id="profileEditPanel" class="profileEditPanel hidden">
    252             <label>
    253               <span>Pronouns</span>
    254               <input id="profilePronouns" maxlength="40" placeholder="she/her, they/them, etc" />
    255             </label>
    256             <label>
    257               <span>Theme song</span>
    258               <div class="row">
    259                 <input id="profileThemeSongUrl" placeholder="Upload audio for your theme song" readonly />
    260                 <button id="profileThemeSongUploadBtn" class="ghost" type="button">Upload audio</button>
    261                 <button id="profileThemeSongClearBtn" class="ghost" type="button">Remove</button>
    262               </div>
    263               <audio id="profileThemeSongPreview" controls preload="none" class="hidden"></audio>
    264               <input id="profileThemeSongFile" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    265             </label>
    266             <div class="editorShell">
    267               <div class="toolbar" id="profileBioToolbar" role="toolbar" aria-label="Profile bio formatting">
    268                 <button type="button" data-profilecmd="bold"><b>B</b></button>
    269                 <button type="button" data-profilecmd="italic"><i>I</i></button>
    270                 <button type="button" data-profilecmd="underline"><u>U</u></button>
    271                 <button type="button" data-profilecmd="strikeThrough"><s>S</s></button>
    272                 <span class="sep"></span>
    273                 <button type="button" data-profilecmd="insertUnorderedList">List</button>
    274                 <button type="button" data-profilecmd="insertOrderedList">1. List</button>
    275                 <button type="button" data-profilelink="1">Link</button>
    276                 <button type="button" data-profileimg="1">GIF/Image</button>
    277                 <button type="button" data-profileaudio="1">Audio</button>
    278                 <button type="button" data-profileemoji="1">Emoji</button>
    279                 <button type="button" data-profilecmd="removeFormat">Clear</button>
    280               </div>
    281               <div id="profileBioEditor" class="editor profileBioEditor" contenteditable="true" aria-label="Profile bio editor"></div>
    282               <input id="profileBioImageFile" class="hidden" type="file" accept="image/*,.gif" />
    283               <input id="profileBioAudioFile" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    284             </div>
    285             <div class="profileLinksHead">
    286               <span>Social links</span>
    287               <button id="profileAddLinkBtn" class="ghost smallBtn" type="button">Add link</button>
    288             </div>
    289             <div id="profileLinksEditor" class="profileLinksEditor"></div>
    290             <div class="row">
    291               <button id="profileSaveBtn" class="primary" type="button">Save profile</button>
    292               <button id="profileCancelBtn" class="ghost" type="button">Cancel</button>
    293             </div>
    294           </div>
    295         </section>
    296 
    297         <section id="pollinatePanel" class="panel composerCollapsed">
    298           <div class="panelHeader">
    299             <div class="panelTitle">Create Hive</div>
    300             <button id="toggleComposerInline" class="ghost smallBtn" type="button">Hide</button>
    301           </div>
    302           <div class="uiHint">Keep titles short and clear. Add keywords so others can find your hive faster.</div>
    303           <form id="newPostForm" class="form">
    304             <label>
    305               <span>Title (max 96 chars)</span>
    306               <input id="postTitle" maxlength="96" placeholder="Short title for this hive" />
    307             </label>
    308             <div class="editorShell">
    309               <div class="toolbar" role="toolbar" aria-label="Formatting">
    310                 <button type="button" data-cmd="bold"><b>B</b></button>
    311                 <button type="button" data-cmd="italic"><i>I</i></button>
    312                 <button type="button" data-cmd="underline"><u>U</u></button>
    313                 <button type="button" data-cmd="strikeThrough"><s>S</s></button>
    314                 <span class="sep"></span>
    315                 <button type="button" data-cmd="insertUnorderedList">List</button>
    316                 <button type="button" data-cmd="insertOrderedList">1. List</button>
    317                 <button type="button" data-link="1">Link</button>
    318                 <button type="button" data-postimg="1">GIF/Image</button>
    319                 <button type="button" data-postaudio="1">Audio</button>
    320                 <button type="button" data-postemoji="1">Emoji</button>
    321                 <button type="button" data-cmd="removeFormat">Clear</button>
    322               </div>
    323               <div id="editor" class="editor" contenteditable="true" aria-label="Post editor"></div>
    324               <input id="postImage" class="hidden" type="file" accept="image/*,.gif" />
    325               <input id="postAudio" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    326             </div>
    327 
    328             <div class="row">
    329               <label class="grow">
    330                 <span>Collection</span>
    331                 <select id="postCollection"></select>
    332               </label>
    333               <label class="grow">
    334                 <span>Keywords (comma separated, up to 6)</span>
    335                 <input id="keywords" placeholder="hive, code, idea" />
    336               </label>
    337               <label>
    338                 <span>TTL (minutes)</span>
    339                 <input id="ttlMinutes" type="number" min="1" max="2880" value="60" />
    340               </label>
    341             </div>
    342 
    343             <button class="primary" type="submit">Send</button>
    344             <div class="row">
    345               <label class="grow">
    346                 <span>Protected</span>
    347                 <input id="isProtected" type="checkbox" />
    348               </label>
    349               <label class="grow">
    350                 <span>Walkie Talkie</span>
    351                 <input id="isWalkie" type="checkbox" />
    352               </label>
    353               <label class="grow">
    354                 <span>Post password</span>
    355                 <input id="postPassword" type="password" autocomplete="new-password" placeholder="Required if protected" disabled />
    356               </label>
    357             </div>
    358           </form>
    359         </section>
    360           </div>
    361           <div id="mainSideRack" class="sideRack" aria-label="Side panels"></div>
    362         </div>
    363       </main>
    364 
    365       <div id="chatResizeHandle" class="panelResizeHandle chatResizeHandle" title="Drag to resize chat" aria-hidden="true"></div>
    366 
    367       <aside class="chat">
    368         <div class="panelHeader">
    369           <div>
    370             <div class="panelTitle" id="chatTitle">Chat</div>
    371             <div id="chatMeta" class="small muted">Select a post to chat.</div>
    372           </div>
    373           <div class="row chatHeaderActions">
    374             <select id="chatContextSelect" class="chatContextSelect" aria-label="Open chats"></select>
    375             <button id="chatBackToList" class="ghost smallBtn hidden" type="button">Back</button>
    376           </div>
    377         </div>
    378         <div class="uiHint">Select a hive chat or DM first, then type your message and press Send. Shortcut in Chat: <b>-</b>/<b>=</b> cycles chat list entries.</div>
    379         <div id="chatMessages" class="chatMessages"></div>
    380         <div id="typingIndicator" class="typingIndicator small muted"></div>
    381         <div id="walkieBar" class="walkieBar hidden" aria-label="Walkie talkie controls">
    382           <button id="walkieRecordBtn" class="primary" type="button">Hold to talk</button>
    383           <div class="walkieHint small muted">
    384             Hold <span class="tag">~</span> or press-and-hold this button to record. Release to send.
    385           </div>
    386           <div id="walkieStatus" class="small muted"></div>
    387         </div>
    388         <form id="chatForm" class="chatForm">
    389           <div id="chatReplyBanner" class="chatReplyBanner hidden">
    390             <div class="small">
    391               Replying to <span id="chatReplyWho"></span>
    392               <span id="chatReplyText" class="muted"></span>
    393             </div>
    394             <button id="chatReplyCancel" class="ghost smallBtn" type="button">Cancel</button>
    395           </div>
    396             <div class="chatComposer">
    397             <div class="toolbar" role="toolbar" aria-label="Chat formatting">
    398               <button type="button" data-chatcmd="bold"><b>B</b></button>
    399               <button type="button" data-chatcmd="italic"><i>I</i></button>
    400               <button type="button" data-chatcmd="underline"><u>U</u></button>
    401               <button type="button" data-chatcmd="strikeThrough"><s>S</s></button>
    402               <span class="sep"></span>
    403               <button type="button" data-chatcmd="insertUnorderedList">List</button>
    404               <button type="button" data-chatcmd="insertOrderedList">1. List</button>
    405               <button type="button" data-chatlink="1">Link</button>
    406               <button type="button" data-chatimg="1">GIF/Image</button>
    407               <button type="button" data-chataudio="1">Audio</button>
    408               <button type="button" data-chatemoji="1">Emoji</button>
    409               <button type="button" data-chatcmd="removeFormat">Clear</button>
    410               <label id="chatModToggleWrap" class="checkRow chatModToggle hidden" title="Send as moderator/system message (left rail)">
    411                 <span>Mod</span>
    412                 <input id="chatModToggle" type="checkbox" />
    413               </label>
    414             </div>
    415             <div id="chatEditor" class="editor chatEditor" contenteditable="true" aria-label="Chat editor"></div>
    416             <div id="mentionMenu" class="mentionMenu hidden" role="listbox" aria-label="Mention suggestions"></div>
    417             <input id="chatImage" class="hidden" type="file" accept="image/*" />
    418             <input id="chatAudio" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    419           </div>
    420           <div class="uiHint">Use Link, GIF/Image, and Audio to attach media quickly.</div>
    421           <button class="primary" type="submit">Send</button>
    422         </form>
    423       </aside>
    424 
    425       <div id="mainResizeHandle" class="panelResizeHandle mainResizeHandle" title="Drag to resize moderation panel" aria-hidden="true"></div>
    426 
    427       <aside id="modPanel" class="moderation hidden">
    428         <div class="panelHeader">
    429           <div class="panelTitle">Moderation</div>
    430         </div>
    431         <div class="uiHint">Use tabs to review reports, manage users/hives, configure server settings, and publish onboarding content.</div>
    432         <div class="modTabs">
    433           <button type="button" class="ghost" data-modtab="reports">Reports</button>
    434           <button type="button" class="ghost" data-modtab="users">Users</button>
    435           <button type="button" class="ghost" data-modtab="hives">Hives</button>
    436           <button type="button" class="ghost" data-modtab="server">Server</button>
    437           <button type="button" class="ghost" data-modtab="onboarding">Onboarding</button>
    438           <button type="button" class="ghost" data-modtab="log">Log</button>
    439         </div>
    440         <div class="modFilters">
    441           <select id="modReportStatus">
    442             <option value="open">Open reports</option>
    443             <option value="resolved">Resolved reports</option>
    444             <option value="dismissed">Dismissed reports</option>
    445             <option value="">All reports</option>
    446           </select>
    447           <button id="modRefresh" class="ghost" type="button">Refresh</button>
    448         </div>
    449         <div id="modBody" class="modBody small"></div>
    450       </aside>
    451 
    452       <div id="mobileScreenHost" class="mobileScreenHost" aria-label="Mobile screen host"></div>
    453     </div>
    454     <aside id="peopleDrawer" class="peopleDrawer hidden">
    455       <div id="peopleResizeHandle" class="peopleResizeHandle" title="Drag to resize people panel" aria-hidden="true"></div>
    456       <div class="panelHeader">
    457         <div class="panelTitle">People</div>
    458         <button id="closePeople" class="ghost smallBtn" type="button">Close</button>
    459       </div>
    460       <div class="peopleTabs">
    461         <button id="peopleMembersTab" class="primary" type="button">Members</button>
    462         <button id="peopleDmsTab" class="ghost" type="button">DMs</button>
    463       </div>
    464       <div id="peopleMembersView">
    465         <div class="uiHint">Members list lets you open profiles and start DMs. Mods can also send <b>Mod DM</b> from member/profile actions.</div>
    466         <div class="peopleFilters">
    467           <input id="peopleSearch" placeholder="Search members" />
    468         </div>
    469         <div id="peopleList" class="peopleList small"></div>
    470       </div>
    471       <div id="peopleDmsView" class="peopleDms small hidden">
    472         <div class="uiHint">DM requests must be accepted before chat opens. Active threads show <b>Open</b>, and mods get a <b>Mod DM</b> action.</div>
    473         DMs coming soon.
    474       </div>
    475     </aside>
    476     <div id="mobileNav" class="mobileNav hidden" aria-label="Mobile navigation">
    477       <button type="button" data-mobilescreen="account">Account</button>
    478       <button type="button" data-mobilescreen="hives">Hives</button>
    479       <button type="button" data-mobilescreen="chat">Chat</button>
    480       <button id="mobileFourthBtn" type="button" data-mobilescreen="people">People</button>
    481       <button type="button" data-mobilescreen="profile">Profile</button>
    482       <button type="button" data-mobilescreen="more">More</button>
    483     </div>
    484 
    485     <div id="mobileMoreSheet" class="mobileMoreSheet hidden" role="dialog" aria-modal="true" aria-label="More">
    486       <div class="mobileMoreBackdrop" data-mobilemoreclose="1"></div>
    487       <div class="mobileMoreCard panel">
    488         <div class="panelHeader">
    489           <div class="panelTitle">More</div>
    490           <div class="row">
    491             <button id="mobileMoreClose" class="ghost smallBtn" type="button">Close</button>
    492           </div>
    493         </div>
    494         <div class="mobileMoreBody">
    495           <input id="mobileMoreSearch" placeholder="Search…" />
    496           <div id="mobileMoreList" class="mobileMoreList"></div>
    497         </div>
    498       </div>
    499     </div>
    500 
    501     <div id="editModal" class="modal hidden" role="dialog" aria-modal="true" aria-label="Edit content">
    502       <div class="modalBackdrop" data-modalclose="1"></div>
    503       <div class="modalCard panel">
    504         <div class="panelHeader">
    505           <div class="panelTitle" id="editModalTitle">Edit</div>
    506           <div class="row">
    507             <button id="editModalClose" class="ghost smallBtn" type="button">Close</button>
    508           </div>
    509         </div>
    510         <div class="modalBody">
    511           <label id="editModalPostTitleRow" class="hidden">
    512             <span>Title</span>
    513             <input id="editModalPostTitle" maxlength="96" />
    514           </label>
    515 
    516           <div id="editModalPostMeta" class="hidden editMetaGrid" aria-label="Post settings">
    517             <label>
    518               <span>Keywords</span>
    519               <input id="editModalKeywords" maxlength="120" placeholder="comma separated (max 6)" />
    520             </label>
    521             <label>
    522               <span>Collection</span>
    523               <select id="editModalCollection"></select>
    524             </label>
    525             <label class="checkRow">
    526               <span>Password protected</span>
    527               <input id="editModalProtected" type="checkbox" />
    528             </label>
    529             <label class="checkRow">
    530               <span>Walkie-only</span>
    531               <input id="editModalWalkie" type="checkbox" />
    532             </label>
    533             <label id="editModalPasswordRow" class="hidden">
    534               <span>Password</span>
    535               <input
    536                 id="editModalPassword"
    537                 type="password"
    538                 minlength="4"
    539                 maxlength="80"
    540                 placeholder="Leave blank to keep current"
    541                 autocomplete="new-password"
    542               />
    543             </label>
    544           </div>
    545 
    546           <div class="editorShell">
    547             <div class="toolbar" id="editModalToolbar" role="toolbar" aria-label="Edit formatting">
    548               <button type="button" data-editcmd="bold"><b>B</b></button>
    549               <button type="button" data-editcmd="italic"><i>I</i></button>
    550               <button type="button" data-editcmd="underline"><u>U</u></button>
    551               <button type="button" data-editcmd="strikeThrough"><s>S</s></button>
    552               <span class="sep"></span>
    553               <button type="button" data-editcmd="insertUnorderedList">List</button>
    554               <button type="button" data-editcmd="insertOrderedList">1. List</button>
    555               <button type="button" data-editlink="1">Link</button>
    556               <button type="button" data-editimg="1">GIF/Image</button>
    557               <button type="button" data-editaudio="1">Audio</button>
    558               <button type="button" data-editemoji="1">Emoji</button>
    559               <button type="button" data-editcmd="removeFormat">Clear</button>
    560             </div>
    561             <div id="editModalEditor" class="editor editModalEditor" contenteditable="true" aria-label="Edit content"></div>
    562             <input id="editModalImage" class="hidden" type="file" accept="image/*,.gif" />
    563             <input id="editModalAudio" class="hidden" type="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.webm" />
    564           </div>
    565 
    566           <div class="row modalActions">
    567             <button id="editModalSave" class="primary" type="button">Save</button>
    568             <button id="editModalCancel" class="ghost" type="button">Cancel</button>
    569           </div>
    570           <div id="editModalStatus" class="small muted"></div>
    571         </div>
    572       </div>
    573     </div>
    574 
    575     <div id="modModal" class="modal hidden" role="dialog" aria-modal="true" aria-label="Moderation">
    576       <div class="modalBackdrop" data-modmodalclose="1"></div>
    577       <div class="modalCard panel">
    578         <div class="panelHeader">
    579           <div class="panelTitle" id="modModalTitle">Moderation</div>
    580           <div class="row">
    581             <button id="modModalClose" class="ghost smallBtn" type="button">Close</button>
    582           </div>
    583         </div>
    584         <div class="modalBody" id="modModalBody"></div>
    585         <div class="row modalActions">
    586           <button id="modModalPrimary" class="primary" type="button">Save</button>
    587           <button id="modModalCancel" class="ghost" type="button">Cancel</button>
    588         </div>
    589         <div id="modModalStatus" class="small muted" style="padding:0 12px 12px"></div>
    590       </div>
    591     </div>
    592 
    593     <div id="mediaModal" class="modal hidden" role="dialog" aria-modal="true" aria-label="Media preview">
    594       <div class="modalBackdrop" data-mediamodalclose="1"></div>
    595       <div class="modalCard panel">
    596         <div class="panelHeader">
    597           <div class="panelTitle" id="mediaModalTitle">Media</div>
    598           <div class="row">
    599             <button id="mediaModalClose" class="ghost smallBtn" type="button">Close</button>
    600           </div>
    601         </div>
    602         <div class="modalBody" id="mediaModalBody">
    603           <img id="mediaModalImg" alt="Expanded media" />
    604           <div class="row modalActions" style="justify-content:flex-start">
    605             <a id="mediaModalOpenLink" class="ghost" href="#" target="_blank" rel="noreferrer">Open original</a>
    606             <button id="mediaModalCopyLink" class="ghost" type="button">Copy link</button>
    607           </div>
    608           <div id="mediaModalStatus" class="small muted"></div>
    609         </div>
    610       </div>
    611     </div>
    612 
    613     <div id="shortcutHelpModal" class="modal hidden" role="dialog" aria-modal="true" aria-label="Keyboard shortcuts">
    614       <div class="modalBackdrop" data-shortcutclose="1"></div>
    615       <div class="modalCard panel">
    616         <div class="panelHeader">
    617           <div class="panelTitle">Keyboard Shortcuts</div>
    618           <div class="row">
    619             <button id="shortcutHelpClose" class="ghost smallBtn" type="button">Close</button>
    620           </div>
    621         </div>
    622         <div class="modalBody shortcutHelpBody">
    623           <div><span class="tag">[ / ]</span> Cycle layout presets</div>
    624           <div><span class="tag">- / =</span> Cycle hives or chats in active panel</div>
    625           <div><span class="tag">?</span> Open this shortcut help</div>
    626           <div><span class="tag">`</span> Hold for walkie talkie (when enabled)</div>
    627           <div><span class="tag">Esc</span> Close menus/modals</div>
    628         </div>
    629       </div>
    630     </div>
    631 
    632     <div id="dockHotbar" class="dockHotbar hidden" aria-label="Docked panels"></div>
    633     <script src="/app.js?v=152"></script>
    634   </body>
    635 </html>