Files
LLeMbas-Desktop/ui/index.html
T
HomerandClaude Opus 5.5 ac0d4ce623
ci / check (push) Failing after 2s
ci / start (push) Failing after 2s
Terminal: a shell on the agent chat's computer, before the first message too
The Terminal panel opens a shell on the device an agent chat works on, in its
folder: for a new agent chat as soon as the device and folder are chosen (the
server's /api/v2/devices/{device}/terminal/ws?directory=, which the chat's first
message takes over -- the same socket carries on), and for an open agent chat
from the header (/api/v2/chats/{chat}/terminal/ws, the web page's shell).
Offered where GET /devices says the device opens a terminal.

The socket is Rust's (src-tauri/src/terminal.rs, tungstenite on the rustls
ureq already brings): the bearer token goes in the handshake and never reaches
the page, which gets the frames as `terminal` events and sends keys, sizes and
close through commands. 4401 signs the app out as a 401 does.

xterm and its fit addon are the server's vendored build, pinned through
fetch-vendor.sh like CodeMirror and loaded on the first open. Send to chat puts
the selection, the last marked command and its output, or the screen into the
composer, never sent. The terminal and the activity panel take turns in the
panel column. Continue in a terminal stays in the chat's menu.

tests/ui: seven B3 cases with a mocked socket, at 1280 and 390 px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 09:06:01 +00:00

182 lines
10 KiB
HTML

<!doctype html>
<html lang="en" data-theme="moria" data-base="moria">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark light">
<title>LLeMbas</title>
<link rel="stylesheet" href="tokens.css">
<link rel="stylesheet" href="app.css">
<script src="i18n.js"></script>
<script type="module" src="js/main.js"></script>
</head>
<body>
<div class="app">
<header class="bar" data-tauri-drag-region>
<button class="bar__btn" id="side-toggle" type="button" aria-label="Sidebar" title="Sidebar" aria-expanded="true" aria-controls="side" data-i18n-aria-label data-i18n-title></button>
<img class="bar__leaf" src="logo-mark.svg" alt="" width="18" height="18" data-tauri-drag-region>
<span class="bar__name" data-tauri-drag-region><b>LL</b>e<b>M</b>bas</span>
<span class="bar__host" id="host" data-tauri-drag-region></span>
<button class="bar__update" id="update" type="button" hidden>update</button>
<button class="bar__agent" id="agent" type="button" aria-haspopup="dialog" aria-expanded="false">
<span class="dot" aria-hidden="true"></span><span class="bar__agent-text" id="agent-text">agent off</span>
</button>
<button class="bar__btn" id="menu" type="button" aria-label="Menu" title="Menu" data-i18n-aria-label data-i18n-title></button>
<span class="bar__sep" aria-hidden="true"></span>
<button class="bar__btn" id="minimize" type="button" aria-label="Minimise" title="Minimise" data-i18n-aria-label data-i18n-title></button>
<button class="bar__btn" id="maximize" type="button" aria-label="Maximise" title="Maximise" data-i18n-aria-label data-i18n-title></button>
<button class="bar__btn bar__btn--close" id="close" type="button" aria-label="Close" title="Close (LLeMbas stays in the tray)" data-i18n-aria-label data-i18n-title></button>
</header>
<div class="setup" id="setup" hidden>
<main class="card" aria-labelledby="setup-title">
<header class="brand">
<img src="logo-mark.svg" alt="" width="40" height="40">
<div>
<h1 id="setup-title"><b>LL</b>e<b>M</b>bas</h1>
<p class="muted">Desktop <span id="setup-version"></span></p>
</div>
</header>
<div class="setup__body" id="setup-body"></div>
</main>
</div>
<div class="shell" id="shell" hidden data-side="true" data-viewer="false" data-view="chat">
<nav class="side" id="side" aria-label="Chats" data-i18n-aria-label>
<div class="side__top">
<button type="button" class="side__new" id="new-chat-btn"><span class="side__new-label" data-i18n>New chat</span><kbd class="kbd">Ctrl N</kbd></button>
<label class="search">
<span class="search__icon" id="side-search-icon"></span>
<input type="search" id="side-search" placeholder="Search chats" aria-label="Search chats" autocomplete="off" spellcheck="false" data-i18n-placeholder data-i18n-aria-label>
</label>
</div>
<div class="side__nav">
<button type="button" class="navrow" id="nav-scheduled" data-view="scheduled"><span class="navrow__icon"></span><span data-i18n>Scheduled</span></button>
<button type="button" class="navrow" id="nav-files" data-view="files"><span class="navrow__icon"></span><span data-i18n>Files</span></button>
</div>
<ul class="list" id="chat-list" aria-label="Chats" data-i18n-aria-label></ul>
<div class="side__foot">
<button type="button" class="navrow navrow--account" id="settings-btn" data-view="settings"></button>
</div>
</nav>
<div class="scrim" id="scrim" aria-hidden="true"></div>
<main class="main" id="main" aria-labelledby="chat-title" data-new="false">
<header class="head">
<h1 class="head__title" id="chat-title"></h1>
<div class="head__place" id="chat-place"></div>
<div class="head__tools" id="chat-tools"></div>
</header>
<div class="thread" id="thread">
<div class="hello" id="new-chat" hidden></div>
<div class="messages" id="messages"></div>
</div>
<div class="dock" id="dock">
<p class="thread__note is-error" id="thread-note" hidden></p>
<div class="cards" id="cards" hidden></div>
<div class="notice" id="link-notice" hidden></div>
<div class="strip" id="activity-strip" hidden aria-live="polite"></div>
<form class="composer" id="composer" hidden data-running="false" data-drag="false">
<ul class="composer__files" id="composer-files" hidden></ul>
<textarea id="composer-text" rows="1" placeholder="Message" aria-label="Message" data-i18n-placeholder data-i18n-aria-label></textarea>
<div class="composer__bar">
<button type="button" class="tool composer__attach" id="attach" aria-label="Attach files" title="Attach files" data-i18n-aria-label data-i18n-title></button>
<div class="composer__chips" id="composer-chips"></div>
<button type="button" class="round round--stop" id="stop" hidden aria-label="Stop" title="Stop" data-i18n-aria-label data-i18n-title></button>
<button type="submit" class="round" id="send" aria-label="Send" title="Send" data-i18n-aria-label data-i18n-title></button>
</div>
<p class="composer__drop" aria-hidden="true" data-i18n>Drop the files to attach them</p>
</form>
</div>
</main>
<section class="page" id="page-files" hidden aria-labelledby="files-title">
<header class="head">
<h1 class="head__title" id="files-title" data-i18n>Files</h1>
<div class="head__place"></div>
<div class="head__tools"><div class="tabs" role="tablist" id="files-tabs"></div></div>
</header>
<div class="page__body files">
<section class="files__pane" id="pane-local" aria-label="This computer" data-i18n-aria-label>
<div class="files__head">
<div class="files__where" id="local-where"></div>
<button type="button" class="tool" id="local-refresh" aria-label="Read again" title="Read again" data-i18n-aria-label data-i18n-title></button>
</div>
<div class="files__tree" id="local-tree"></div>
</section>
<section class="files__pane" id="pane-server" hidden aria-label="Server" data-i18n-aria-label>
<div class="files__head">
<div class="files__where muted" id="server-where"></div>
<button type="button" class="tool" id="server-refresh" aria-label="Read again" title="Read again" data-i18n-aria-label data-i18n-title></button>
</div>
<div class="files__tree" id="server-tree"></div>
</section>
</div>
</section>
<section class="page" id="page-scheduled" hidden aria-labelledby="scheduled-title">
<header class="head">
<h1 class="head__title" id="scheduled-title" data-i18n>Scheduled</h1>
<div class="head__place" id="scheduled-place"></div>
<div class="head__tools" id="scheduled-tools"></div>
</header>
<div class="page__body" id="scheduled-body"></div>
</section>
<section class="page settings" id="settings" hidden aria-labelledby="settings-title">
<header class="head">
<h1 class="head__title" id="settings-title" data-i18n>Settings</h1>
<div class="head__place"></div>
<div class="head__tools"><button type="button" class="tool" id="settings-close" aria-label="Close" title="Close" data-i18n-aria-label data-i18n-title></button></div>
</header>
<div class="settings__layout">
<nav class="settings__nav" id="settings-nav" aria-label="Settings" data-i18n-aria-label></nav>
<div class="settings__body" id="settings-body"></div>
</div>
</section>
<aside class="termpanel" id="terminal" hidden aria-labelledby="terminal-title">
<header class="viewer__head termpanel__head">
<span class="termpanel__icon" id="terminal-icon" aria-hidden="true"></span>
<div class="viewer__id">
<h2 class="viewer__name" id="terminal-title" data-i18n>Terminal</h2>
<p class="viewer__where termpanel__where" id="terminal-where"></p>
</div>
<button type="button" class="tool" id="terminal-prev" hidden aria-label="The previous command" title="The previous command (Ctrl+Shift+↑)" data-i18n-aria-label data-i18n-title></button>
<button type="button" class="tool" id="terminal-next" hidden aria-label="The next command" title="The next command (Ctrl+Shift+↓)" data-i18n-aria-label data-i18n-title></button>
<button type="button" class="tool" id="terminal-copy" aria-label="Copy the last command and its output" title="Copy the last command and its output" data-i18n-aria-label data-i18n-title></button>
<button type="button" class="tool" id="terminal-close" aria-label="Close terminal" title="Close terminal" data-i18n-aria-label data-i18n-title></button>
</header>
<div class="termpanel__screen" id="terminal-screen"></div>
<footer class="termpanel__foot">
<p class="termpanel__message" id="terminal-message" aria-live="polite"></p>
<span class="termpanel__last" id="terminal-last"></span>
<button type="button" class="btn btn--sm termpanel__send" id="terminal-send" title="Put the last command and its output into the message box" data-i18n-title><span data-i18n>Send to chat</span></button>
</footer>
</aside>
<aside class="activity" id="activity" hidden aria-labelledby="activity-title">
<header class="viewer__head activity__head">
<button type="button" class="tool" id="activity-back" hidden aria-label="Back" title="Back" data-i18n-aria-label data-i18n-title></button>
<h2 class="viewer__name" id="activity-title"></h2>
<button type="button" class="tool" id="activity-close" aria-label="Close the activity panel" title="Close the activity panel" data-i18n-aria-label data-i18n-title></button>
</header>
<div class="activity__body" id="activity-body"></div>
</aside>
<aside class="viewer" id="viewer" hidden aria-labelledby="viewer-name">
<header class="viewer__head">
<div class="viewer__id">
<h2 class="viewer__name" id="viewer-name"></h2>
<p class="viewer__where" id="viewer-where"></p>
</div>
<div class="viewer__modes" id="viewer-modes"></div>
<button type="button" class="tool" id="viewer-close" aria-label="Close" title="Close" data-i18n-aria-label data-i18n-title></button>
</header>
<div class="viewer__body" id="viewer-body"></div>
</aside>
</div>
</div>
</body>
</html>