Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/www/index.html

111 KiB, 48 runs

created by r2519314175:1337, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1<!DOCTYPE html>
2<html lang="en" data-theme="dark" data-tone="dark" data-ink="light" data-skin="sharp">
3<head>
4<meta charset="UTF-8">
5<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6<title>Daimond</title>
7<!-- Apply the saved theme (palette) and skin (shape) before the first paint, so
8 a returning user never sees the default flash to their own choice. The full
9 services (DaimondTheme / DaimondSkin) take over once the app boots. -->
10<script>
11(function () {
12 try {
13 // The palette table, mirrored from THEMES in daimond.js because this runs
14 // before any script loads. Each entry is [tone, ink]: the band it is
15 // offered under, and whether its surfaces take dark or light lettering.
16 // verify_theme asserts the two tables agree, so the copy cannot drift.
17 var P = {
18 light: ['light', 'dark'], mist: ['light', 'dark'], linen: ['light', 'dark'],
19 lollypop: ['mid', 'dark'], sage: ['mid', 'dark'], dusk: ['mid', 'light'],
20 dark: ['dark', 'light'], amber: ['dark', 'light'],
21 midnight: ['dark', 'light'],
22 forest: ['dark', 'light'], plum: ['dark', 'light'],
23 };
24 var t = localStorage.getItem('daimond-theme');
25 var spec = P[t] || P.dark;
26 if (P[t]) document.documentElement.setAttribute('data-theme', t);
27 document.documentElement.setAttribute('data-tone', spec[0]);
28 document.documentElement.setAttribute('data-ink', spec[1]);
29 var s = localStorage.getItem('daimond-skin');
30 if (s === 'soft') s = 'warm'; // the warm skin was briefly called "soft".
31 if (s === 'warm' || s === 'sharp') document.documentElement.setAttribute('data-skin', s);
32 // The language too, so the document declares itself before a screen
33 // reader or the browser's own translation offer looks at it.
34 var l = localStorage.getItem('daimond-locale');
35 if (l) document.documentElement.lang = l;
36 } catch (e) { /* private mode: the default attributes stand. */ }
37})();
38</script>
39<link rel="icon" type="image/svg+xml" href="assets/daimond_mark.svg">
40<!-- Installable on a home screen. `app.webmanifest`, NOT `manifest.json`: that
41 name is taken, by the transparency manifest -- the SHA-256 of every served
42 file, which verify/check.mjs and js/verify.js read to prove the code in this
43 browser is the published source. Two different manifests, one of them much
44 older, so the newcomer is the one that moves. -->
45<link rel="manifest" href="app.webmanifest">
46<!-- The colour the platform paints its own furniture with in standalone mode.
47 js/pwa.js keeps it on whichever of the eleven palettes is in use; this is the
48 default, for the moment before any script runs. -->
49<meta name="theme-color" content="#15120F">
50<!-- iOS reads its own tags rather than the manifest. `apple-touch-icon` is the
51 home-screen icon (SVG is not honoured, hence the PNG), and "capable" is what
52 makes a pinned Daimond open without Safari's chrome.
53
54 The status bar stays `default` -- iOS then keeps its own bar above the page
55 rather than letting the page run under it. `black-translucent` would look
56 better on the dark palettes and unreadable on the light ones, because it
57 forces white status text whatever the page is wearing, and Daimond ships
58 eleven palettes of which three are paper. The safe-area insets still matter
59 in every style: the home indicator, and the notch in landscape. -->
60<link rel="apple-touch-icon" href="assets/icons/icon-180.png">
61<meta name="apple-mobile-web-app-capable" content="yes">
62<meta name="mobile-web-app-capable" content="yes">
63<meta name="apple-mobile-web-app-status-bar-style" content="default">
64<meta name="apple-mobile-web-app-title" content="Daimond">
65<link rel="stylesheet" href="css/variables.css">
66<link rel="stylesheet" href="css/app.css">
67<link rel="stylesheet" href="css/render.css">
68<!-- crystal.css AFTER app.css, deliberately. Both sheets name classes beginning
69 `crystal-`: app.css dresses the chrome around the frame (the ask row, the ✎
70 form), crystal.css dresses the frame, the built-in view and the note that
71 says a page failed. Where the two ever named the same class, the later sheet
72 is the frame's, which is the half that owns the appearance. -->
73<link rel="stylesheet" href="css/crystal.css">
74<!-- viewer.css before files.css: the file viewer draws inside the Doc panel that
75 sheet dresses, so the panel's own rules are the ones that win. -->
76<link rel="stylesheet" href="css/viewer.css">
77<link rel="stylesheet" href="css/files.css">
78<link rel="stylesheet" href="css/mail.css">
79<link rel="stylesheet" href="css/graph.css">
80<link rel="stylesheet" href="css/tools.css">
81<link rel="stylesheet" href="css/autoreload.css">
82<link rel="stylesheet" href="css/models.css">
83<link rel="stylesheet" href="css/updater.css">
84<link rel="stylesheet" href="css/spend.css">
85<link rel="stylesheet" href="css/passkey.css">
86<!-- The beta passcode: the dialog a refused registration raises, and the block
87 it leaves behind in the Credits drawer. -->
88<link rel="stylesheet" href="css/passcode.css">
89<link rel="stylesheet" href="css/workspace.css">
90<link rel="stylesheet" href="css/trash.css">
91<link rel="stylesheet" href="css/improve.css">
92<link rel="stylesheet" href="css/tracker.css"><!-- js/tracker.js — the Proposals tracker view -->
93<link rel="stylesheet" href="css/terminal.css">
94<link rel="stylesheet" href="css/lapse.css">
95 <link rel="stylesheet" href="css/release.css">
96 <link rel="stylesheet" href="css/responsive.css">
97<link rel="stylesheet" href="css/mobile.css">
98<!-- Full-screen toggle (phone only) -- just the two icon faces; see js/fullscreen.js. -->
99<link rel="stylesheet" href="css/fullscreen.css">
100<link rel="stylesheet" href="css/skin-warm.css">
101</head>
102<body data-mpanel="ai">
103
104<!-- Main app: browser-only, no server. The whole agent runs in wasm. -->
105<div id="app">
106
107 <!-- What the copy buttons say to a screen reader. They are icon-only and
108 their confirmation is a tick, which is nothing at all to a reader that
109 does not see; one polite region serves the lot. -->
110 <div class="vh" id="copy-say" role="status" aria-live="polite"></div>
111
112 <!-- Top bar -->
113 <header class="topbar" aria-labelledby="app-title">
114 <h1 class="vh" id="app-title">Daimond</h1>
115 <!-- The hamburger: phone only, opens the rail as a left drawer. -->
116 <button id="drawer-btn" title="Menu" aria-label="Open menu" data-i18n-title="topbar.menu" data-i18n-aria-label="topbar.open_menu">
117 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16"/></svg>
118 </button>
119 <div class="brand-group">
120 <a class="brand" href="https://daimond.app" target="_blank" rel="noopener noreferrer" aria-label="Daimond, the daimond.app home page" data-i18n-aria-label="topbar.brand">
121 <img class="brand-wordmark wm-on-dark" src="assets/daimond_word.svg" alt="Daimond">
122 <img class="brand-wordmark wm-on-light" src="assets/daimond_word_dark.svg" alt="" aria-hidden="true">
123 </a>
124 </div>
125 <div class="top-actions">
126 <!-- A tag per CLOSED panel. Click it to open the panel; the panel's own
127 closer sends it back here. -->
128 <div class="panel-tags" id="panel-tags"></div>
129 <button class="update-chip" id="update-chip" hidden title="Daimond is up to date" aria-label="Daimond is up to date" data-i18n-title="topbar.up_to_date" data-i18n-aria-label="topbar.up_to_date">
130 <svg class="ic ic-refresh" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 11a8 8 0 10-.6 4"/><path d="M20 4v5h-5"/></svg>
131 <svg class="ic ic-check" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7"/></svg>
132 </button>
133 <!-- About, in the slot the maker's badge held. The badge said who made
134 Daimond and how, in a 36px lozenge with two invisible hit areas
135 over it: two claims that nobody could read and that a phone dropped
136 entirely. Both survive, in the dialog this opens, where each has
137 room to be a sentence.
138
139 The glyph is an "i" in a circle, which is what "about" is drawn as
140 everywhere. The guide button beside it is a "?" in a circle, so the
141 two are deliberately separated by where the dot sits: the "i" wears
142 its dot on TOP of the stem, the "?" underneath its hook. Two
143 buttons in one row that read the same are two buttons that do the
144 same thing, as far as the user is concerned. -->
145 <button class="icon-btn" id="about-btn" title="About Daimond" aria-label="About Daimond"
146 data-i18n-title="topbar.about" data-i18n-aria-label="topbar.about">
147 <!-- The dot is r=1.2 and not the "?"'s r=0.6, and the stem starts 2.4
148 units below it. At 18px that is the difference between a dotted
149 "i" and a plain vertical bar: 8x crops of both glyphs were read
150 side by side, and the first attempt's r=0.6 dot merged into the
151 stem at every size the bar draws. -->
152 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="7.4" r="1.2" fill="currentColor" stroke="none"/><path d="M12 11.6v5"/></svg>
153 </button>
154 <button class="icon-btn" id="guide-btn" title="Open the user guide" aria-label="User guide" data-i18n-title="topbar.guide" data-i18n-aria-label="topbar.guide_short">
155 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M9.2 9.3a2.8 2.8 0 115.3 1.2c-.4.9-1.5 1.3-2 2-.3.4-.3.9-.3 1.5"/><circle cx="12" cy="17.4" r="0.6" fill="currentColor" stroke="none"/></svg>
156 </button>
157 <button class="icon-btn menu-btn" id="settings-menu-btn" title="Appearance and layout"
158 aria-label="Appearance and layout" aria-haspopup="menu" aria-expanded="false" aria-controls="settings-menu"
159 data-i18n-title="topbar.appearance" data-i18n-aria-label="topbar.appearance">
160 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h10M18 7h2M4 17h2M10 17h10"/><circle cx="16" cy="7" r="2.2"/><circle cx="8" cy="17" r="2.2"/></svg>
161 </button>
162 <!-- Full screen: PHONE ONLY, and only where the browser offers the
163 Fullscreen API. Firefox on Android will not install the PWA
164 standalone, so this is the one route to an immersive view there;
165 desktop has F11 and a real install and never sees it. Shown,
166 hidden and kept in sync by js/fullscreen.js -- the inline
167 display:none is the off state the script clears on a phone. The
168 two faces are the crystal button's corner arrows: out to enter,
169 in to exit. -->
170 <button class="icon-btn" id="fullscreen-btn" type="button" style="display:none"
171 aria-pressed="false" title="Full screen" aria-label="Full screen">
172 <svg class="ic ic-fs-enter" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 4H4v5M15 4h5v5M15 20h5v-5M9 20H4v-5"/></svg>
173 <svg class="ic ic-fs-exit" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 9h5V4M20 9h-5V4M20 15h-5v5M4 15h5v5"/></svg>
174 </button>
175 </div>
176 </header>
177
178 <!-- Every panel there is, searchable, for when the chip row cannot hold
179 them all. Rows carry a pin, which is what decides the row's contents:
180 the user says what sits in the header and nothing reorders itself. -->
181 <div class="pop" id="panel-gallery" hidden role="dialog" aria-label="Panels" data-i18n-aria-label="gallery.title"></div>
182
183 <!-- Appearance and layout: theme, reading size, dock tiling. -->
184 <div class="pop" id="settings-menu" hidden role="dialog" aria-label="Appearance and layout" data-i18n-aria-label="topbar.appearance"></div>
185 <!-- The permission ladder. A `.pop`, like the appearance menu and the panel
186 gallery, so Escape and a click outside dismiss it exactly as they do —
187 and a radio group inside, so the arrow keys work without a line of code. -->
188 <div class="pop mode-pop" id="hand-mode-pop" hidden role="dialog"
189 aria-label="Permission mode" data-i18n-aria-label="permmode.title"></div>
190
191 <!-- The keyboard's way in. Complete at any fleet size, which is what lets
192 the visible surfaces be selective. -->
193 <div class="pal-scrim" id="palette" hidden>
194 <div class="pal-box" role="dialog" aria-modal="true" aria-label="Go to" data-i18n-aria-label="pal.title">
195 <!-- The search box and the way out, on one row. The palette is opened
196 with a key and was closed with one, which is right for the hands
197 that summoned it and no help at all to a thumb: on a phone the box
198 is 358px of a 390px screen. -->
199 <div class="pal-top">
200 <input class="pal-input" id="pal-input" type="text" role="combobox" autocomplete="off"
201 aria-expanded="true" aria-controls="pal-list" aria-autocomplete="list"
202 placeholder="Go to a panel, or a setting…" data-i18n-placeholder="pal.placeholder">
203 <button class="ui-close pal-close" id="pal-close" type="button" title="Close"
204 aria-label="Close the Go to box" data-i18n-title="common.close" data-i18n-aria-label="pal.close"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
205 </div>
206 <ul class="pal-list" id="pal-list" role="listbox" aria-label="Results" data-i18n-aria-label="pal.results"></ul>
207 </div>
208 </div>
209
210 <!-- Four panels -->
211 <div class="main" id="main">
212
213 <!-- Rail: two panes, split by a handle the user can move but not remove.
214 Above: what you are working on. Below: the Admin panel — who you are,
215 how Daimond is set up, and what it is costing. Settings and the forms
216 that configure Daimond live there rather than in a popup, so a chat
217 stays live beside them: the user can ask Daimond what an app password is
218 while the box asking for one is still on screen. -->
219 <aside class="panel rail" id="panel-rail" data-panel="rail" data-zone="rail" data-label="Diamonds" data-i18n-label="panel.rail">
220 <div class="rail-top" id="rail-top">
221 <!-- The global pause, which is not a seventh setting: it is the ROOT
222 of the tree every other traffic light hangs off, so it is green
223 only while everything that could spend is running, red only when
224 none of it is, and amber the rest of the time. It sits above the
225 Diamonds list because that is the first thing the eye reaches,
226 and because a hold on everything belongs over everything.
227 The control itself is built in daimond.js, which owns the DOM. -->
228 <div class="pptw-head" id="pptw-global-row">
229 <span class="pptw-slot" id="pptw-global"></span>
230 <span class="pptw-head-label" title="Pause or resume everything that can spend: every Diamond, every chat, the workers, the mailboxes and any page fetched for you." data-i18n="pause.everything" data-i18n-title="pause.everything_help">Everything</span>
231 <!-- The rail's closer, and it is the RAIL it closes. It stood on the
232 Diamonds head below, in the run of buttons that make a Diamond
233 and draw the graph, where it read as the closer of that section:
234 a cross at the end of a row named "Diamonds" says it puts the
235 Diamonds away, and it took the whole rail — the Chats, the
236 status header and the Admin panel with it. This row is the only
237 one in the rail that speaks for all of it, so the closer belongs
238 here, above everything it acts on and beside the pause that has
239 the same scope. `data-close` and the classes are unchanged; the
240 handler in daimond.js binds by `[data-close]` and does not care
241 where the button sits. -->
242 <button class="addbtn panel-close ui-close" data-close="rail" title="Close panel"
243 aria-label="Close the Diamonds panel" data-i18n-title="common.close_panel" data-i18n-aria-label="rail.close_rail">
244 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
245 </button>
246 </div>
247 <!-- Diamonds above, Chats below, which is the way the ground is:
248 you mine in the conversation underneath and what you bring up
249 is cut and polished above it. The order is not decoration --
250 a chat is raw and a Diamond is worked, and the rail says so
251 without needing a word of explanation. -->
252 <div class="railhead"><span role="heading" aria-level="2" title="A repeatable method, distilled from a conversation." data-i18n="rail.diamonds" data-i18n-title="rail.diamonds_help">Diamonds</span>
253 <button class="addbtn" id="new-diamond-btn" title="New Diamond" data-i18n-title="rail.new_diamond">
254 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
255 </button>
256 <!-- The Diamonds and the links between them, drawn. It belongs on
257 the rail head because the rail is the list the picture is of.
258 It TOGGLES the Graph rather than only opening it, so it is a
259 latched button and says so: `aria-pressed` starts false because
260 nothing is open before the layout engine has read the stored
261 layout, and daimond.js sets it from there and on every press.
262 The latched look is `.addbtn[aria-pressed="true"]` in app.css. -->
263 <button class="addbtn" id="link-graph-btn" aria-pressed="false" title="Graph" aria-label="Graph" data-i18n-title="link.graph_open" data-i18n-aria-label="link.graph_open">
264 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="5.5" r="2.5"/><circle cx="5.5" cy="18" r="2.5"/><circle cx="18.5" cy="18" r="2.5"/><path d="M10.6 7.6L6.9 15.9M13.4 7.6l3.7 8.3M8 18h8"/></svg>
265 </button>
266 <!-- No closer on this row. It is one section of the rail and has
267 nothing of its own to close; the rail's closer is on the
268 Everything row above. -->
269 </div>
270 <!-- Narrow the rail by tag. A search box stood here too and has
271 gone: a Diamond is found by what it is filed under, and two
272 ways of narrowing one short list is one more than it needs. -->
273 <div class="diamond-find">
274 <div class="diamond-filter" id="diamond-filter" style="display:none"></div>
275 </div>
276 <div class="diamond-list" id="diamond-list"></div>
277 <!-- The divider between the two lists. Diamonds accumulate, and the
278 list used to grow until the chat tiles were off the bottom of the
279 rail with no way to get them back. -->
280 <div class="rhandle" id="handle-rail-split" title="Drag to resize, double-click to reset" data-i18n-title="layout.handle"></div>
281 <div class="railhead"><span role="heading" aria-level="2" title="Raw conversation. A Diamond is cut from it." data-i18n="rail.chats" data-i18n-title="rail.chats_help">Chats</span>
282 <button class="addbtn" id="new-session-btn" title="New chat" data-i18n-title="rail.new_chat">
283 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
284 </button>
285 <!-- The section's own overflow, for an act that is about every chat
286 rather than one -- "Delete all chats" lives behind this, not
287 beside the "+" above. A cross next to "+" would have been a
288 second closer that behaved nothing like the rest: every other
289 cross in this app removes the one thing beside it, and a cross
290 that removed everything below it would be the same glyph
291 promoted to a different, much larger act with no visible change
292 to warn a hand that has learned to trust it. daimond.js builds
293 the dropdown; nothing here decides what is in it. -->
294 <button class="addbtn" id="chats-menu-btn" title="More" data-i18n-title="rail.chats_menu" aria-haspopup="menu" aria-expanded="false">
295 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="5" cy="12" r="1.8"/><circle cx="12" cy="12" r="1.8"/><circle cx="19" cy="12" r="1.8"/></svg>
296 </button>
297 </div>
298 <div class="session-list" id="session-list"></div>
299 </div>
300
301 <div class="rhandle" id="handle-split" title="Drag to resize, double-click to reset" data-i18n-title="layout.handle"></div>
302
303 <!-- The Admin panel. -->
304 <div class="admin" id="admin">
305
306 <!-- The status header: the state of the machine, at a glance and
307 without asking. Each row is the answer to a question the user
308 would otherwise have to open something to find out, and each
309 one that can be acted on is a button that goes there. -->
310 <div class="admin-status" id="admin-status">
311 <div class="astat-row astat-id">
312 <button class="user-row" id="user-row" title="Your Daimond identity" data-i18n-title="admin.identity">
313 <span class="user-avatar" id="user-avatar" aria-hidden="true">◈</span>
314 <span class="user-info" id="user-info">Browser-only</span>
315 </button>
316 <!-- The same cog every Diamond tile and every mailbox row wears,
317 not the ⚙ character. A font glyph is drawn by whichever font
318 the platform finds it in, at whatever weight and size that
319 font gives it, so the one cog in the rail's status header
320 read as a different icon from the cogs directly above it.
321 The path is `COG_D` in js/daimond.js, which `DaimondUI.cogIcon`
322 hands to the tiles and to mail.js; this copy is inline because
323 the button is in the static markup and nothing builds it. Both
324 must move together if the drawing ever changes.
325 No size override: `.icon-btn .ic` and `.tile-cog .ic` both take
326 the 16px default, which is what makes the two identical. -->
327 <button class="icon-btn" id="settings-btn" title="Settings" data-i18n-title="admin.settings" aria-label="Settings" data-i18n-aria-label="admin.settings">
328 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 00.3 1.8l.1.1a2 2 0 11-2.8 2.8l-.1-.1a1.7 1.7 0 00-2.9 1.2v.2a2 2 0 11-4 0v-.1a1.7 1.7 0 00-1.1-1.5 1.7 1.7 0 00-1.9.3l-.1.1a2 2 0 11-2.8-2.8l.1-.1a1.7 1.7 0 00-1.2-2.9H2.9a2 2 0 110-4H3a1.7 1.7 0 001.5-1.1 1.7 1.7 0 00-.3-1.9l-.1-.1a2 2 0 112.8-2.8l.1.1a1.7 1.7 0 001.8.3H9a1.7 1.7 0 001-1.5V2.9a2 2 0 114 0V3a1.7 1.7 0 001 1.5 1.7 1.7 0 001.9-.3l.1-.1a2 2 0 112.8 2.8l-.1.1a1.7 1.7 0 00-.3 1.8V9a1.7 1.7 0 001.5 1H21a2 2 0 110 4h-.1a1.7 1.7 0 00-1.5 1z"/></svg>
329 </button>
330 </div>
331 <button class="astat-row astat-btn" id="astat-model"></button>
332 <button class="astat-row astat-btn" id="astat-account"></button>
333 <!-- The second money row: the user's own provider key, when they have
334 one. Two economies sit side by side here and each row names whose
335 money it is -- see js/money.js. -->
336 <button class="astat-row astat-btn" id="astat-byok" style="display:none"></button>
337 <button class="astat-row astat-btn" id="astat-pro"></button>
338 <button class="astat-row astat-btn" id="astat-tools"></button>
339 <button class="astat-row astat-btn" id="astat-hand"></button>
340 <!-- The version row carries a copy button for the build id, which
341 is the string a bug report has to quote. It sits OUTSIDE
342 #astat-release rather than inside it: that row is itself a
343 button (it opens the version history), and a button inside a
344 button is neither valid nor clickable. The row keeps its id
345 and its classes so release.js still paints it. -->
346 <div class="astat-copyrow">
347 <button class="astat-row astat-btn" id="astat-release" title="Version history" data-i18n-title="admin.version_history"></button>
348 <span class="astat-copyslot" id="astat-release-copy"></span>
349 </div>
350 <div class="astat-row" id="astat-store"></div>
351 <!-- The real folder, when the browser can open one. Its size is not a thing the
352 browser will tell us, so it is asked for rather than assumed. -->
353 <div class="astat-row" id="astat-store-native" style="display:none"></div>
354 <!-- Whether this device's work has travelled, which was a pill in the
355 top bar until 2026-08-28. It sat in the row of icon buttons, so
356 it moved every one of them 86px each time a round started and
357 again when it finished; and it faded 1.8s after saying "Synced",
358 so the answer to "did that reach my other machine" was gone
359 before it was asked. Here it is a status row like its neighbours
360 -- one question, answered without opening anything -- and the row
361 is PERMANENT: `js/sync.js` puts its chip in when there is
362 something happening or something standing, and when there is
363 not, `#sync-rest` says when a sync last worked. Nothing above or
364 below it moves either way. -->
365 <div class="astat-row astat-sync" id="astat-sync">
366 <span class="astat-dot" id="sync-rest-dot" aria-hidden="true"></span>
367 <span class="astat-val" id="sync-rest"></span>
368 </div>
369 <!-- Global spend readout: session / week / month, priced per model. -->
370 <div class="spend-row" id="spend-row" style="display:none"></div>
371 </div>
372
373 <div class="admin-body" id="admin-body">
374 <div class="admin-drawer-head">
375 <span class="admin-title" id="admin-drawer-title">Admin</span>
376 <!-- The drawn cross, not the '×' character: a glyph's ink is not
377 centred in its own em box and which box it gets depends on
378 whichever font the platform found it in. `.ui-close` carries
379 the shape and the 44px thumb floor; `.admin-back` stays as the
380 hook the drawer's own rules and two verifiers reach for. -->
381 <button class="admin-back ui-close" id="admin-close" title="Close" aria-label="Close the Admin panel"
382 data-i18n-title="common.close" data-i18n-aria-label="admin.close_drawer"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
383 </div>
384 <div class="admin-scroll" id="admin-scroll">
385
386 <!-- Home: what the panel rests on. Built in JS, because what it
387 offers depends on whether there is an identity to offer it to. -->
388 <div class="admin-view" id="admin-home"></div>
389
390 <!-- Models: a key per provider, and the one model a new chat starts on.
391 Reached from the "Models" status row above, which counts them. -->
392 <div class="admin-view" id="admin-models" style="display:none">
393 <div class="settings-section">
394 <p class="cfg-lead" data-i18n="models.lead">A key for each provider you use. New chats and Diamonds start on the default model; any model can be picked for a single chat.</p>
395 <div id="models-list" class="models-list"></div>
396 <!-- The panel's message line: a provider that would not say what is left,
397 a credit figure that is not a number, a models list that would not
398 load. It looked itself up in `models.js` for a month and NOTHING
399 anywhere created it, so every one of those messages was swallowed.
400 OUTSIDE `models-list`, because `render()` rewrites that list
401 wholesale and would carry the message off with it. Same
402 `login-error` line as `byok-note` below rather than a fourth
403 notice mechanism. -->
404 <div id="models-note" class="login-error" role="status" aria-live="polite"></div>
405 <button type="button" id="models-add" class="models-add" data-i18n="models.add">+ Add provider</button>
406
407 <!-- Adding one: the same three questions, whoever the provider is. -->
408 <div id="byok-form" class="byok-form" style="display:none">
409 <label class="cfg-fieldlabel" for="cfg-provider" data-i18n="models.provider">Provider</label>
410 <select id="cfg-provider" class="settings-select">
411 <option value="" data-i18n="models.choose_provider">Choose a provider…</option>
412 <option value="anthropic">Anthropic</option>
413 <option value="fireworks">Fireworks AI</option>
414 <option value="openrouter">OpenRouter</option>
415 <option value="together">Together AI</option>
416 <option value="groq">Groq</option>
417 <option value="deepinfra">DeepInfra</option>
418 <option value="custom" data-i18n="models.custom_provider">Custom (advanced)…</option>
419 </select>
420 <div id="cfg-url-row" style="display:none;">
421 <label class="cfg-fieldlabel" for="cfg-base-url" data-i18n="models.base_url">Base URL</label>
422 <input type="text" id="cfg-base-url" placeholder="https://…/v1/chat/completions" spellcheck="false" autocomplete="off">
423 </div>
424 <label class="cfg-fieldlabel" for="cfg-api-key" data-i18n="models.api_key">API key</label>
425 <input type="text" id="cfg-api-key" placeholder="Paste your API key" data-i18n-placeholder="models.api_key_ph" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" data-1p-ignore data-lpignore="true">
426 <label class="cfg-fieldlabel" for="cfg-model" data-i18n="models.default_model">Default model</label>
427 <select id="cfg-model" class="settings-select">
428 <option value="" data-i18n="models.choose_provider_first">Choose a provider first…</option>
429 </select>
430 <input type="text" id="cfg-model-custom" placeholder="Model id (type manually)" spellcheck="false" style="display:none;" data-i18n-placeholder="models.model_id_ph">
431 <p class="cfg-fieldnote" data-i18n="models.default_note">New agents use this unless you choose another for them.</p>
432 <button type="button" id="byok-save" data-i18n="models.save_start">Save &amp; start</button>
433 </div>
434 <div id="byok-note" class="login-error"></div>
435 <p class="cfg-hint" data-i18n-html="models.privacy"><strong>Everything stays on this device</strong> — your keys, chats and files live only in this browser; nothing is sent to us.</p>
436 </div>
437
438 <!-- Search: which outside service the agent searches with.
439 Directly under the model picker because it is the same kind of
440 decision — which outside service does my agent use — and it is
441 a SETTING rather than a line in a prompt file, because a prompt
442 line is advice a model may ignore and this has to be enforced.
443
444 EVERY string here is painted by `SearchRow.render` in
445 js/daimond.js and NOT by `data-i18n`, which is deliberate and
446 is about shipping order. `data-i18n` writes `t(key)` into the
447 element, and `t` answers with the KEY when the table has no
448 entry — so a panel reading "search.head" until the i18n lane's
449 eight locales land. `tOr` answers with the English written at
450 the call site instead, so the two halves of this release are
451 safe to arrive in either order. The English below is what
452 shows before the first paint, and only then. -->
453 <div class="settings-section" id="search-section">
454 <h3 id="search-head">Search</h3>
455 <div class="search-engine-row">
456 <label class="cfg-fieldlabel" for="set-search-engine">Search engine</label>
457 <select id="set-search-engine" class="settings-select"></select>
458 <p class="search-engine-note" id="search-engine-note">Which service Daimond searches with. Most give you a free allowance each month if you bring your own key.</p>
459 </div>
460 <!-- Hidden for `credits`: the gateway holds that key and there is
461 nothing here for anyone to paste. -->
462 <div class="search-engine-row" id="search-key-row" style="display:none">
463 <label class="cfg-fieldlabel" for="set-search-key">API key</label>
464 <!-- Masked by installSecretMask, exactly as the provider key
465 and the push token are, and for the same reason: a real
466 password field is what invites a browser or a manager to
467 file somebody else's bearer credential away. -->
468 <input type="text" id="set-search-key" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" data-1p-ignore data-lpignore="true">
469 <p class="search-engine-note" id="search-key-note">Kept on this device, sealed with your passphrase, and sent only with the search it pays for.</p>
470 <p class="search-engine-note" id="search-key-warn" data-warn="1" style="display:none"></p>
471 </div>
472 </div>
473
474 <!-- The Terminal's folder. A terminal is the user at a keyboard and a
475 command is a daimon, so the two may be different sizes -- and the
476 choice is made HERE rather than only in install.sh, because a
477 setting nobody can find is a setting that gets forgotten.
478
479 The options are written by the MACHINE and read out of the hand's
480 hello as `terminal-ceiling:` entries. The page chooses among them
481 and cannot name a folder the machine did not offer, which is the
482 same rule a toolkit follows. Where the user pinned one at a shell
483 with `install.sh --terminal-workspace`, the row says so and the
484 select is disabled: a decision already made is not re-asked. -->
485 <!-- The folder the hand may work in, and the folder a terminal may reach.
486 Two sizes on purpose: a terminal is the user at a keyboard and a
487 command is a daimon. The walk is served by the hand over the
488 machine's own directories -- the browser's own picker answers with
489 a handle carrying a name and no path, and a fence needs the path. -->
490 <div class="settings-section" id="grantroot-section" style="display:none">
491 <h3 id="grantroot-head" data-i18n="grantroot.head">Daimond's folder</h3>
492 <div class="search-engine-row">
493 <code class="termroot-now" id="grantroot-now"></code>
494 <div class="termroot-acts">
495 <button type="button" class="termroot-btn" id="grantroot-browse" data-i18n="termroot.browse">Change…</button>
496 </div>
497 <p class="search-engine-note" id="grantroot-note" data-i18n="grantroot.note">Bounds every command. Applies on reload.</p>
498 </div>
499 <div id="grantroot-walk" class="termroot-walk" style="display:none"></div>
500 </div>
501
502 <div class="settings-section" id="termroot-section" style="display:none">
503 <h3 id="termroot-head" data-i18n="termroot.head">Terminal folder</h3>
504 <div class="search-engine-row">
505 <p class="search-engine-note" id="termroot-note" data-i18n="termroot.note">A terminal is you, so it may reach further than a daimon.</p>
506 <p class="search-engine-note" id="termroot-pinned" style="display:none"></p>
507 </div>
508 </div>
509 </div>
510
511 <!-- Credits: reached from the "Credits" status row. -->
512 <div class="admin-view" id="admin-credits" style="display:none">
513 <div class="settings-section" id="credits-section">
514 <p class="cfg-lead" data-i18n="credits.lead">Buy credits and Daimond runs the model for you, with no provider key to manage. No subscription. You keep what you buy, and nothing is charged again unless you turn on auto-reload.</p>
515 <!-- The closed beta, above everything an account buys, because
516 while registration is shut it is the reason none of it is
517 available. Empty in the served HTML and filled by
518 js/passcode.js — what may honestly be said here depends on
519 what the gateway last answered, so there is nothing to write
520 until it has. `.beta-block:empty` takes the border away with
521 the content, so a device with an account sees no gap. -->
522 <div class="beta-block" id="credits-beta"></div>
523 <div class="pro-block" id="credits-pro"></div>
524 <div class="credits-balance" id="credits-balance"></div>
525 <div class="credits-packs" id="credits-packs"></div>
526 <div id="credits-note" class="login-error"></div>
527 <div id="autoreload" class="autoreload"></div>
528 </div>
529 </div>
530
531 <!-- Push: the credential a `git push` travels with. Its own view and not a
532 row in Models, because a token that can WRITE to a repository is a
533 different kind of secret from a key that buys tokens from a provider,
534 and filing the two together invites pasting one into the other.
535
536 Two boxes and no more. The user name a token travels as is a per-forge
537 constant rather than a choice -- `x-access-token` at GitHub, `oauth2` at
538 GitLab -- so it is inferred from the host; a third box asking for it
539 would mostly be filled in with a person's own login, which fails to
540 authenticate with a message that names neither box. -->
541 <div class="admin-view" id="admin-push" style="display:none">
542 <div class="settings-section">
543 <p class="cfg-lead" data-i18n="push.lead">A token lets Daimond push work you have committed. Without one it still commits, but every push is refused and the work stays on this machine.</p>
544 <div id="push-state" class="cfg-fieldnote"></div>
545 <label class="cfg-fieldlabel" for="cfg-push-host" data-i18n="push.host">Host</label>
546 <input type="text" id="cfg-push-host" placeholder="github.com" data-i18n-placeholder="push.host_ph" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false">
547 <p class="cfg-fieldnote" data-i18n="push.host_note">Just the name, as in github.com. No https://, no port, no path. Only this host is authenticated; a repository anywhere else is refused, and your token never reaches it.</p>
548 <!-- Built exactly as #cfg-api-key is, and masked by installSecretMask
549 for the same reason: a real password field is what makes a browser
550 or a manager offer to save it, and this one belongs in neither. -->
551 <label class="cfg-fieldlabel" for="cfg-push-token" data-i18n="push.token">Token</label>
552 <input type="text" id="cfg-push-token" placeholder="Paste a token with push access" data-i18n-placeholder="push.token_ph" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" data-1p-ignore data-lpignore="true">
553 <p class="cfg-fieldnote" data-i18n="push.token_note">Push access is enough; the token never needs more. Daimond only pushes, only fast-forward, only to origin. Force, delete, mirror and any other remote are refused whatever the token allows. Empty this box and save to remove it.</p>
554 <button type="button" id="push-save" data-i18n="push.save">Save</button>
555 <div id="push-note" class="login-error"></div>
556 <p class="cfg-hint" data-i18n="push.privacy">The token is encrypted under your passphrase and kept in this browser. It is never shown again, never sent to us, never given to a model. Without an account there is nothing to encrypt it with, so it is held for this tab only and asked for again after a reload.</p>
557 </div>
558 </div>
559
560 <!-- A form, built on demand: a mailbox to add, a thing to configure. -->
561 <div class="admin-view" id="admin-form" style="display:none"></div>
562
563 <!-- What you are running, and what came before. -->
564 <div class="admin-view" id="admin-release" style="display:none">
565 <div class="rel-list" id="rel-list"></div>
566 </div>
567 </div>
568 </div>
569 </div>
570 </aside>
571 <div class="phandle" id="handle-rail" data-panel="rail" title="Drag to resize, double-click to reset" data-i18n-title="layout.handle"></div>
572
573 <!-- ── The stage ───────────────────────────────────────────────
574 The middle zone. Its occupants are EXCLUSIVE to it: they take the
575 stage, solo or two side by side, and never dock at the right. AI is
576 the default occupant and comes back whenever the stage would
577 otherwise be empty, because an app whose middle holds no
578 conversation is not Daimond.
579
580 The point of two seats is that you never leave the conversation to
581 do a thing: read the message, watch the page, read the document —
582 with the daimon still there beside it, to be asked about it. -->
583 <main class="stage" id="stage">
584
585 <!-- AI: the conversation. -->
586 <section class="panel ai" id="panel-ai" data-panel="ai" data-zone="stage" data-label="AI" data-i18n-label="panel.ai">
587 <div class="chead">
588 <!-- The mark, shown only on a Diamond's crystal (see showCentre): the
589 crystal is a document you own rather than a conversation, and the
590 panel says which of its two faces is up. -->
591 <img class="chead-mark" id="chead-mark" src="assets/daimond_mark.svg" alt="" aria-hidden="true" style="display:none">
592 <span class="ctitle" id="current-session-name" role="heading" aria-level="2">No chat</span>
593 <!-- A Diamond's two faces. The crystal is the daimon's output; the chat
594 is its conversation, which is persistent and was until now not shown
595 at all. Only drawn on a Diamond — a chat has one face. -->
596 <div class="dview" id="diamond-view" role="group" style="display:none"
597 data-i18n-aria-label="crystal.view_switch" aria-label="Which face of this Diamond">
598 <button class="dview-btn" id="dview-crystal" type="button" aria-pressed="true"
599 data-i18n="crystal.view_crystal">Crystal</button>
600 <button class="dview-btn" id="dview-chat" type="button" aria-pressed="false"
601 data-i18n="crystal.view_chat">Chat</button>
602 </div>
603 <div class="chead-right">
604 <span class="cmeter" id="ai-meter"></span>
605 <!-- The controls that were an icon nobody could read. A word says what a
606 picture of a spanner was only hinting at. -->
607 <span class="csel" id="select-tools" style="display:none">
608 <button class="chip-btn" id="sel-all" data-i18n="chat.select_all">Select all</button>
609 <button class="chip-btn" id="sel-none" data-i18n="chat.deselect_all">Deselect all</button>
610 <button class="chip-btn accent" id="sel-fold" data-i18n="chat.fold_selected">Fold selected</button>
611 </span>
612 <!-- Which permission mode the machine hand is in, where the work is
613 actually watched. A mode you have to remember is one you will be
614 wrong about, and being wrong about this one matters — so it is a
615 word on screen beside the model, not a setting you go and look
616 up. The word IS the state; the accent is only so the eye lands. -->
617 <button class="chip-btn mode-chip" id="hand-mode-chip"
618 aria-haspopup="dialog" aria-expanded="false" aria-controls="hand-mode-pop"
619 title="Permission mode: what Daimond does without asking"
620 data-i18n-title="permmode.chip_help"><span class="mode-chip-dot" aria-hidden="true"></span><span class="mode-chip-txt" id="hand-mode-chip-txt">Guarded</span></button>
621 <!-- Concise: a standing toggle that puts the `/concise` skill in
622 front of every turn this chat sends. A skill, not a hidden
623 system-prompt tweak, so what it asks for is a file in the
624 workspace the user can read and edit. -->
625 <button class="chip-btn icon-chip" id="concise-chip" aria-pressed="false" style="display:none"
626 title="Ask for short answers in this chat. Every message carries the /concise skill, a file in your workspace you can edit."
627 aria-label="Concise" data-i18n-aria-label="chat.concise"
628 data-i18n-title="chat.concise_help"><svg class="ic" viewBox="0 0 24 24"
629 aria-hidden="true"><path d="M4 6h16M4 12h9M4 18h5"/></svg></button>
630 <!-- Fold, on the surface rather than only in the tile dialog.
631 Folding is done MID-FLOW -- when a thread has grown heavy, not
632 when you are configuring something -- and a control you reach
633 for mid-flow belongs where the flow is. Drawn on an ordinary
634 chat that has something in it, and NOT on a Diamond. This said
635 the opposite until 2026-08-20, and was the only place that said
636 it: a daimon folds itself before every round, and its engine's
637 session holds no messages for a hand fold to cut, so the one
638 conversation the button was drawn for was the one it could not
639 touch. -->
640 <button class="chip-btn" id="chat-fold-btn" style="display:none"
641 title="Fold this conversation now, keeping a summary of what came before."
642 data-i18n="chat.fold_now" data-i18n-title="chat.fold_now_help">Fold</button>
643 <!-- The whole conversation, out. Per-message copy has existed since the
644 thread did, and it answers a different question: this is for taking
645 a conversation somewhere else -- a bug report, a handover, another
646 model -- where copying forty messages one at a time is not a
647 workaround, it is a refusal. Drawn for a chat and for a Diamond's
648 chat face alike, because both are conversations. -->
649 <!-- What actually goes to the model. The thread is the payload laid out
650 once; this turns on the parts of it that are normally invisible —
651 the composed system message, whose it is, and the tool schemas
652 nobody has ever seen. -->
653 <!-- THE ONE WORD IN A ROW OF DRAWINGS. Copy, Steps and Concise are
654 verbs and draw as verbs; "wire" is not a picture of anything, and
655 the glyph it had -- five stacked rules -- is the same shape as the
656 Concise glyph beside it and read as a second Concise. The owner
657 asked for the word back, and only for this one.
658 `.label-chip` is what keeps the row's height: the word takes the
659 26px box the icon chips set rather than the row growing to a
660 word's padding. See css/app.css. -->
661 <button class="chip-btn label-chip" id="wire-btn" aria-pressed="false"
662 title="Show what is actually sent to the model"
663 data-i18n="wire.chip"
664 data-i18n-title="wire.chip_help">Wire</button>
665 <button class="chip-btn icon-chip" id="chat-copy-btn"
666 title="Copy the whole conversation to the clipboard"
667 aria-label="Copy the conversation" data-i18n-aria-label="chat.copy_all_aria"
668 data-i18n-title="chat.copy_all_help"><svg class="ic" viewBox="0 0 24 24"
669 aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"/><path
670 d="M5 15V5a2 2 0 012-2h8"/></svg></button>
671 <button class="chip-btn icon-chip" id="steps-toggle-btn"
672 title="Show or hide the tool steps in the thread"
673 aria-label="Tool steps" data-i18n-aria-label="chat.steps"
674 data-i18n-title="chat.steps_help"><svg class="ic" viewBox="0 0 24 24"
675 aria-hidden="true"><path d="M4 6h4v4H4zM10 8h10M4 14h4v4H4zM10 16h10"/></svg></button>
676 <button class="chip-btn" id="collapse-btn" title="Collapse every answer, leaving what you asked — and pick turns to fold" data-i18n-title="chat.collapse_help" aria-label="Collapse every answer" data-i18n-aria-label="chat.collapse">−</button>
677 <!-- Full screen, and only for the crystal face — `showCentre`
678 draws it there and nowhere else. The page a Diamond
679 renders can be an application, and an application inside
680 a panel inside an app reads on a phone as an app inside
681 an app; this takes the chrome away and leaves the page.
682
683 It is LAST in the row so that it keeps its place when
684 everything else in the row is hidden: in full screen this
685 button is the only thing left of the header's right-hand
686 side, and it is the way back. Its words and its title are
687 set from `syncCrystalFullBtn` rather than written here,
688 because both change with the state and the label carries
689 a word as well as a glyph while the mode is on. -->
690 <button class="chip-btn cfull-btn" id="crystal-full-btn" type="button"
691 style="display:none" aria-pressed="false">
692 <svg class="ic ic-cfull-in" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 4H4v5M15 4h5v5M15 20h5v-5M9 20H4v-5"/></svg>
693 <svg class="ic ic-cfull-out" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 9h5V4M20 9h-5V4M20 15h-5v5M4 15h5v5"/></svg>
694 <span class="cfull-txt" id="crystal-full-txt"></span>
695 </button>
696 </div>
697 </div>
698 <div class="chat-output" id="chat-output"></div>
699 <!-- What a screen reader is told when an answer arrives. Said ONCE
700 per turn, not as the answer streams: a live region on the thread
701 itself would read every token mutation, which is a noise nobody
702 can leave switched on, and a live region people switch off is
703 worse than none because the app then looks answerable and is
704 not. Visually hidden — it says nothing a sighted reader cannot
705 already see. -->
706 <div class="vh" id="chat-say" role="status" aria-live="polite"></div>
707 <!-- An ordinary chat's WORKSPACE, and what it has put in front of the
708 model -- two groups, workspace first. See ATTACH_CONTRACT.md §5 and
709 `renderChatAttachments`. Built in JS, and drawn from the moment a
710 chat is in focus: the workspace group appears EMPTY, saying the chat
711 can reach nothing, because that is the state most worth saying and
712 the control that ends it lives in that group's own header. Hidden
713 here only until the first render, so the frame does not flash on a
714 page with no chat open. -->
715 <div class="arte-list chat-attach-strip" id="chat-attachments" style="display:none"></div>
716 <div class="chat-input-bar">
717 <textarea id="chat-input" placeholder="Type a message…" rows="1" data-i18n-placeholder="chat.input_ph"></textarea>
718 <button id="chat-send" title="Send" data-i18n-title="chat.send" aria-label="Send" data-i18n-aria-label="chat.send">➤</button>
719 <!-- Drawn chevrons, not ↑ and ↓: a font's arrowhead is a few
720 pixels of ink on a 32px button, and read as specks beside
721 the stroked icons the rest of the app is made of. -->
722 <button id="chat-jump" title="Jump back to your last message. Press again to walk back through the ones before it." data-i18n-title="chat.jump_help" aria-label="Jump back to your last message" data-i18n-aria-label="chat.jump">
723 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 15l6-6 6 6"/></svg>
724 </button>
725 <button id="chat-end" title="Jump to the end of the chat." data-i18n-title="chat.end_help" aria-label="Jump to the end of the chat" data-i18n-aria-label="chat.end">
726 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
727 </button>
728 </div>
729 <!-- Crystal view: the Diamond command surface (steer + fold), shown in
730 place of the chat when a Diamond is selected. Built in JS. -->
731 <div class="crystal-view" id="crystal-view" style="display:none">
732 <div class="crystal-body" id="crystal-body"></div>
733 <div class="crystal-controls" id="crystal-controls"></div>
734 </div>
735 </section>
736
737 <!-- Web: a page. Shown always; operated only where a driver can reach
738 it — a same-origin page we serve ourselves, or a real tab under
739 Daimond Hands. A cross-origin iframe is opaque to us, and that is
740 the same-origin policy, not a gap to be worked around. -->
741 <aside class="panel web" id="panel-web" data-panel="web" data-zone="stage" data-label="Web" data-i18n-label="panel.web">
742 <div class="chead">
743 <span class="web-mode" id="web-mode" title="Who is driving" data-i18n-title="web.who_drives"></span>
744 <span class="ctitle web-url" id="web-url" role="heading" aria-level="2">The Web panel</span>
745 <div class="chead-right">
746 <!-- `root/web`'s control. A slot, not a drawing: the widget is built
747 by `mountPause` in js/daimond.js, exactly as the rail's global
748 control is, so the app cannot grow a second picture of one
749 state. FIRST in the row, ahead of the page buttons, because it
750 is a statement about what this panel may SPEND rather than
751 another thing to do to the page — and because the refusal
752 sends you here to find it. -->
753 <span class="pptw-slot" id="web-pause"></span>
754 <button class="icon-btn" id="web-back" title="Back" data-i18n-title="web.back" aria-label="Back" data-i18n-aria-label="web.back">‹</button>
755 <button class="icon-btn" id="web-reload" title="Reload" data-i18n-title="web.reload" aria-label="Reload" data-i18n-aria-label="web.reload">⟳</button>
756 <button class="icon-btn" id="web-pop" title="Open in a real tab" data-i18n-title="web.pop_out" aria-label="Open in a real tab" data-i18n-aria-label="web.pop_out">↗</button>
757 <button class="addbtn panel-close" data-close="web" title="Close panel" data-i18n-title="common.close_panel">
758 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
759 </button>
760 </div>
761 </div>
762 <div class="web-body" id="web-body">
763 <!-- WHY THERE ARE TWO OF THEM. With Daimond Hands installed the page
764 is a real browser tab -- that is the only way it can carry the
765 user's own sign-ins and the only way it can be driven at all --
766 and this panel shows a picture of that tab. The app has always
767 said so, in `web.opening`, and then wiped it the moment the page
768 arrived (`adoptPage` calls `note('')`): the sentence was on
769 screen only while the user was waiting, and gone by the time they
770 were looking at a window and a panel at once and wondering why.
771 A STRIP, not the `.web-note` overlay, for exactly that reason --
772 an overlay cannot stay up over a live picture. Same shape as
773 `.web-readonly`, which is this panel's other standing
774 disclosure. -->
775 <div class="web-live" id="web-live" hidden></div>
776 <!-- A frame is a Tab stop, and an untitled one is announced as
777 "frame" and nothing else. -->
778 <iframe class="web-frame" id="web-frame" title="Web" data-i18n-title="panel.web" referrerpolicy="no-referrer"
779 sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox"></iframe>
780 <img class="web-mirror" id="web-mirror" alt="" style="display:none">
781 <div class="web-note" id="web-note"></div>
782 <!-- The blindfold. Not a promise to look away: nothing is sent. -->
783 <div class="web-blind" id="web-blind" style="display:none">
784 <div class="web-blind-eye">◍</div>
785 <div class="web-blind-title" data-i18n="web.blind_title">You're driving. I'm not watching.</div>
786 <div class="web-blind-note" data-i18n-html="web.blind_note">Daimond has stopped reading this page — no text, no picture, no keystrokes. Sign in, then click <b>Resume Daimond</b> in the browser tab to hand the wheel back. That button is in the tab, not here, so this page can never take the wheel from you.</div>
787 <button class="web-takeover" id="web-takeover" data-i18n="web.resumed">I’ve resumed, check</button>
788 </div>
789 </div>
790 </aside>
791
792 <!-- The document itself: the file, where there is room to read it.
793 What it LOOKS like when it is typeset lives in the Preview panel
794 below, because a source and its pages are two things to see at
795 once and not two turns at one panel. -->
796 <aside class="panel doc" id="panel-doc" data-panel="doc" data-zone="stage" data-label="Doc" data-i18n-label="panel.doc">
797 <div class="chead">
798 <span class="ctitle doc-name" id="doc-name" role="heading" aria-level="2"></span>
799 <div class="chead-right">
800 <!-- Line numbers, in the header rather than in the toolbar the
801 text view draws for itself: it is a way of reading the
802 panel, not a thing to do to the file, and it is asked for
803 while looking at the document. Shown only while a text file
804 is up, since a PDF has no lines to number. -->
805 <button class="icon-btn" id="doc-lineno" title="Line numbers" data-i18n-title="files.line_numbers" aria-label="Line numbers" data-i18n-aria-label="files.line_numbers" aria-pressed="false" style="display:none">
806 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h2M4 12h2M4 18h2M10 6h10M10 12h10M10 18h10"/></svg>
807 </button>
808 <button class="addbtn panel-close" data-close="doc" title="Close panel" data-i18n-title="common.close_panel">
809 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
810 </button>
811 </div>
812 </div>
813 <!-- The file as characters: read, or edited. Anything that is not
814 characters is not this panel's — see #panel-preview. -->
815 <div class="doc-view" id="doc-view"></div>
816 </aside>
817
818 <!-- What a document LOOKS like.
819 Three renderings, one at a time, and all three are the same thing:
820 a compiled PDF in the <embed>, a file the browser or the viewer
821 draws in #pv-view, and — standing in front of both while a .typ is
822 being watched — the live typeset pages typstwatch.js mounts here.
823 It is a SEPARATE PANEL from the document because the whole point is
824 to see the source and its pages at once; while they shared one, the
825 two took turns and the loop had to decide whose turn it was. -->
826 <aside class="panel doc preview" id="panel-preview" data-panel="preview" data-zone="stage" data-label="Preview" data-i18n-label="panel.preview">
827 <div class="chead">
828 <span class="ctitle doc-name" id="pv-name" role="heading" aria-level="2"></span>
829 <div class="chead-right">
830 <button class="addbtn panel-close" data-close="preview" title="Close panel" data-i18n-title="common.close_panel">
831 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
832 </button>
833 </div>
834 </div>
835 <div class="doc-view" id="pv-view"></div>
836 <embed class="doc-embed" id="doc-embed" type="application/pdf" style="display:none">
837 </aside>
838
839 <!-- One mail message, where there is room to read it — and, now that
840 the stage seats two, with the chat still beside it. -->
841 <aside class="panel msg" id="panel-msg" data-panel="msg" data-zone="stage" data-label="Email message" data-i18n-label="panel.msg">
842 <div class="chead">
843 <span class="ctitle" id="msg-title" role="heading" aria-level="2">Message</span>
844 <div class="chead-right">
845 <button class="addbtn panel-close" data-close="msg" title="Close panel" data-i18n-title="common.close_panel">
846 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
847 </button>
848 </div>
849 </div>
850 <div class="msg-head" id="msg-head"></div>
851 <div class="msg-body" id="msg-body"></div>
852 </aside>
853
854 <!-- What Daimond can do, and what the rest would cost. A stage guest, because a
855 tool with a price on it is a thing being inspected, and the rail is too
856 narrow to inspect anything in. -->
857 <aside class="panel tools" id="panel-tools" data-panel="tools" data-zone="stage" data-label="Tools" data-i18n-label="panel.tools">
858 <div class="chead">
859 <span class="ctitle" data-i18n="panel.tools" role="heading" aria-level="2">Tools</span>
860 <div class="chead-right">
861 <button class="addbtn panel-close" data-close="tools" title="Close panel" data-i18n-title="common.close_panel">
862 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
863 </button>
864 </div>
865 </div>
866 <div class="tools-body" id="tools-body"></div>
867 </aside>
868
869 <!-- Writing one. A stage guest, not a popup: a message worth writing is worth
870 the room, and the daimon stays beside it, so the draft can be talked over
871 while it is being written. Nothing here sends by itself — the Send button
872 is the only thing in Daimond that puts mail on the wire, and only a person
873 can press it. -->
874 <aside class="panel compose" id="panel-compose" data-panel="compose" data-zone="stage" data-label="Compose" data-i18n-label="panel.compose">
875 <div class="chead">
876 <span class="ctitle" id="compose-title" role="heading" aria-level="2">New message</span>
877 <div class="chead-right">
878 <button class="addbtn panel-close" data-close="compose" title="Close panel" data-i18n-title="common.close_panel">
879 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
880 </button>
881 </div>
882 </div>
883 <div class="compose-head">
884 <label class="compose-row"><span data-i18n="compose.from">From</span><select id="compose-from"></select></label>
885 <label class="compose-row"><span data-i18n="compose.to">To</span><input id="compose-to" type="text" autocomplete="off" placeholder="someone@example.com"></label>
886 <label class="compose-row"><span data-i18n="compose.cc">Cc</span><input id="compose-cc" type="text" autocomplete="off" placeholder="Optional" data-i18n-placeholder="compose.optional"></label>
887 <label class="compose-row"><span data-i18n="compose.subject">Subject</span><input id="compose-subject" type="text" autocomplete="off"></label>
888 </div>
889 <textarea class="compose-text" id="compose-text" spellcheck="true" placeholder="Write your message." data-i18n-placeholder="compose.body_ph"></textarea>
890 <div class="compose-atts" id="compose-atts"></div>
891 <div class="compose-foot">
892 <button class="compose-send" id="compose-send" data-i18n="compose.send">Send</button>
893 <button class="compose-btn" id="compose-save" data-i18n="compose.save_draft">Save draft</button>
894 <button class="compose-btn" id="compose-attach" data-i18n="compose.attach">Attach…</button>
895 <input type="file" id="compose-file" multiple hidden>
896 <button class="compose-btn danger" id="compose-discard" data-i18n="compose.discard">Discard</button>
897 <span class="compose-note" id="compose-note"></span>
898 </div>
899 </aside>
900
901 <!-- The graph: the Diamonds and the links between them, drawn as one
902 still, layered picture. A stage guest because it is an instrument
903 for checking the association structure, and checking needs room. -->
904 <aside class="panel graph" id="panel-graph" data-panel="graph" data-zone="stage" data-label="Graph" data-i18n-label="panel.graph">
905 <div class="chead">
906 <span class="ctitle" data-i18n="panel.graph" role="heading" aria-level="2">Graph</span>
907 <div class="chead-right">
908 <button class="addbtn panel-close" data-close="graph" title="Close panel" data-i18n-title="common.close_panel">
909 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
910 </button>
911 </div>
912 </div>
913 <div class="graph-body" id="graph-body"></div>
914 </aside>
915
916 <!-- Terminal: a real terminal on the user's machine, reached through the
917 machine hand. Everything inside #termp-host is built by js/terminal.js
918 on the first open, never at load: a canvas, its screen model and its
919 screen-reader mirror are not worth building for a panel nobody has
920 asked for.
921
922 A stage guest, and for the same reason the Web panel is one: it is a
923 thing you work IN rather than glance at, and it wants the room and the
924 height to be worked in. It shipped in the dock for a day and the dock
925 is the wrong shape for it twice over -- the dock's own grid seats four
926 below 1900px, so a fifth panel there could only arrive by closing one
927 of the four that were already earning their place.
928
929 The class is `termpanel` and not `term` on purpose. `.term` is the
930 class terminal.js puts on the thing it builds, and a panel wearing it
931 would inherit the terminal's own background, border and 100% height. -->
932 <aside class="panel termpanel" id="panel-term" data-panel="term" data-zone="stage" data-label="Terminal" data-i18n-label="panel.term">
933 <div class="chead">
934 <span class="ctitle" role="heading" aria-level="2" data-i18n="panel.term">Terminal</span>
935 <!-- What the program calls itself (OSC 0 and 2). aria-hidden, because
936 the same words reach a screen reader through the terminal's own
937 live region: a title that changes on every prompt would otherwise
938 interrupt the output being read out. -->
939 <span class="termp-title" id="termp-title" aria-hidden="true"></span>
940 <div class="chead-right">
941 <!-- A bell rung while the eye is elsewhere. The terminal flashes its
942 own edge; this is the same event where the panel can be seen. -->
943 <span class="termp-bell" id="termp-bell" aria-hidden="true"></span>
944 <button class="addbtn" data-act="term-start" title="Start a terminal" aria-label="Start a terminal" data-i18n-title="term.start" data-i18n-aria-label="term.start">
945 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5l11 7-11 7z"/></svg>
946 </button>
947 <button class="addbtn" data-act="term-stop" title="Stop the program" aria-label="Stop the program" data-i18n-title="term.stop" data-i18n-aria-label="term.stop">
948 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><rect x="6.5" y="6.5" width="11" height="11" rx="1.5"/></svg>
949 </button>
950 <button class="addbtn panel-close" data-close="term" title="Close panel" data-i18n-title="common.close_panel">
951 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
952 </button>
953 </div>
954 </div>
955 <div class="termp-card">
956 <!-- Where the terminal is, in a sentence: starting, running, refused,
957 finished. A refusal from the hand or the relay is shown VERBATIM
958 — those sentences are written to be acted on, and a paraphrase
959 loses the only instruction the reader gets. -->
960 <p class="termp-state" id="termp-state" role="status"></p>
961 <!-- Missing output, said BESIDE the stream and never written into it:
962 bytes written into a terminal are drawn, so a marker in the stream
963 would be damage on top of the loss. See handpty.js, "A hole is
964 shown, not stitched". -->
965 <div class="termp-host" id="termp-host">
966 <div class="termp-gaps" id="termp-gaps" role="log" aria-live="polite" aria-label="Notices about this terminal" data-i18n-aria-label="term.notices_label"></div>
967 </div>
968 <!-- The way back out. A terminal takes nearly every key, so the one
969 that leaves it has to be said in writing rather than guessed at. -->
970 <!-- Selection, copy and paste were BUILT and invisible: drag, double-click,
971 triple-click, Ctrl-Shift-C, Ctrl-Shift-V and Ctrl-Shift-A have all worked
972 since the panel shipped, and the only line naming them was `term.hint`,
973 rendered screen-reader-only. The owner reported copy and paste as a
974 missing feature on 2026-08-26. It is the same string, in every locale
975 already, put where a sighted reader meets it. -->
976 <p class="termp-foot" id="termp-keys" data-i18n="term.hint">Type to send keys straight to the program. With text selected, Ctrl-C copies it; with none, Ctrl-C interrupts the program. Ctrl-V pastes, Ctrl-Shift-A selects everything, and right-click offers the same three. Shift with Page Up or Page Down moves through what has scrolled past.</p>
977 <p class="termp-foot" id="termp-foot" data-i18n="term.leave_hint">Press F6 to move the keyboard out of the terminal.</p>
978 </div>
979 </aside>
980 </main>
981
982 <div class="phandle" id="handle-dock" title="Drag to resize, double-click to reset" data-i18n-title="layout.handle"></div>
983 <!-- ── The dock ────────────────────────────────────────────────
984 The right zone: the sources you pull from. A closed dock panel is a
985 tag in the header; clicking the tag docks it. A second column opens
986 only when there is genuinely room for one. -->
987 <div class="dock" id="dock">
988 <div class="pcol" id="dock-a"></div>
989 <div class="pcol" id="dock-b"></div>
990 </div>
991
992 <!-- Dock panels below are relocated into their column by the layout engine. -->
993
994 <!-- Agents: live runs -->
995 <!-- Pending: what a daimon has proposed and is waiting on you for.
996 Notes2: "we have the user approving all outgoing email, so this leads
997 to the need for a new Dock panel, say 'Pending'". Every tile carries
998 one Diamond, a priority the daimon set, a headline that expands, and
999 the three answers: do it, discuss it, drop it. -->
1000 <aside class="panel pending" id="panel-pending" data-panel="pending" data-zone="dock" data-label="Pending" data-i18n-label="panel.pending">
1001 <div class="railhead"><span role="heading" aria-level="2" data-i18n="panel.pending">Pending</span>
1002 <span class="rail-count" id="pending-count"></span>
1003 <span class="pending-actions">
1004 <select class="pending-sort" id="pending-sort" data-i18n-title="pending.sort" title="Sort">
1005 <option value="priority" data-i18n="pending.by_priority">By priority</option>
1006 <option value="newest" data-i18n="pending.by_newest">Newest first</option>
1007 <option value="oldest" data-i18n="pending.by_oldest">Oldest first</option>
1008 </select>
1009 <button class="addbtn panel-close" data-close="pending" title="Close panel" data-i18n-title="common.close_panel">
1010 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
1011 </button>
1012 </span>
1013 </div>
1014 <div class="pending-list" id="pending-list"></div>
1015 </aside>
1016
1017 <aside class="panel agents" id="panel-agents" data-panel="agents" data-zone="dock" data-label="Agents" data-i18n-label="panel.agents">
1018 <div class="railhead"><span role="heading" aria-level="2" data-i18n="panel.agents">Agents</span>
1019 <span class="rail-count" id="agents-count"></span>
1020 <!-- Pause / resume / stop, acting on every agent at once. Pause holds
1021 the pool and hangs up what is running (keeping its work); resume
1022 continues; stop is the kill switch. The same three ride on each
1023 tile. This is the cost brake for a runaway fan-out. -->
1024 <div class="agents-ctl" id="agents-ctl" style="display:none" role="group" aria-label="Control all agents" data-i18n-aria-label="agents.control_all">
1025 <button class="agc" id="agents-pause" title="Pause all agents" aria-label="Pause all agents" data-i18n-title="agents.pause_all" data-i18n-aria-label="agents.pause_all"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><rect x="7" y="6" width="3.4" height="12" rx="1"/><rect x="13.6" y="6" width="3.4" height="12" rx="1"/></svg></button>
1026 <button class="agc" id="agents-play" title="Resume all paused agents" aria-label="Resume all paused agents" data-i18n-title="agents.resume_all" data-i18n-aria-label="agents.resume_all"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5l11 7-11 7z"/></svg></button>
1027 <button class="agc" id="agents-stop" title="Stop all agents" aria-label="Stop all agents" data-i18n-title="agents.stop_all" data-i18n-aria-label="agents.stop_all"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
1028 </div>
1029 <button class="addbtn" id="agents-clear" title="Clear finished agents" style="display:none" data-i18n-title="agents.clear_finished">
1030 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16M9 7V5h6v2M7 7l1 12h8l1-12"/></svg>
1031 </button>
1032 <button class="addbtn panel-close" data-close="agents" title="Close panel" data-i18n-title="common.close_panel">
1033 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
1034 </button>
1035 </div>
1036 <!-- How many agents are running, paused and queued: the fan-out's cost made legible. -->
1037 <div class="agents-stat" id="agents-stat" style="display:none"></div>
1038 <!-- Find a run among many by name, task, Diamond or tag. A flat list
1039 with chips, not a Diamond-and-children tree: the parent a run
1040 belongs to rides on the run as a chip, so the tree's height is
1041 not spent on it. -->
1042 <div class="diamond-find agent-find">
1043 <input type="search" class="diamond-search" id="agent-search" placeholder="Search agents" aria-label="Search agents by name, task, Diamond or tag" autocomplete="off" data-i18n-placeholder="agents.search" data-i18n-aria-label="agents.search_help">
1044 <div class="diamond-filter" id="agent-filter" style="display:none"></div>
1045 </div>
1046 <div class="agents-list" id="agents-list"></div>
1047 </aside>
1048
1049 <!-- Email: the mailboxes, and what is in them -->
1050 <aside class="panel mail" id="panel-mail" data-panel="mail" data-zone="dock" data-label="Email" data-i18n-label="panel.mail">
1051 <div class="railhead"><span role="heading" aria-level="2" data-i18n="panel.mail">Email</span>
1052 <span class="mail-actions">
1053 <button class="addbtn" data-act="mail-new" title="Write a message" aria-label="Write a message" data-i18n-title="mail.write" data-i18n-aria-label="mail.write">
1054 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z"/></svg>
1055 </button>
1056 <button class="addbtn" data-act="mail-add" title="Add a mailbox" aria-label="Add a mailbox" data-i18n-title="mail.add_mailbox" data-i18n-aria-label="mail.add_mailbox">
1057 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
1058 </button>
1059 <button class="addbtn" data-act="mail-sync" title="Sync now" aria-label="Sync now" data-i18n-title="mail.sync_now" data-i18n-aria-label="mail.sync_now">
1060 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 11a8 8 0 10-.6 4"/><path d="M20 4v5h-5"/></svg>
1061 </button>
1062 <button class="addbtn panel-close" data-close="mail" title="Close panel" data-i18n-title="common.close_panel">
1063 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
1064 </button>
1065 </span>
1066 </div>
1067 <div class="mail-card">
1068 <div class="mail-state" id="mail-state"></div>
1069 <div class="mail-accounts" id="mail-accounts"></div>
1070 <!-- Which of the account's folders the list below is showing.
1071 Empty until an account exists to have folders. -->
1072 <div class="mail-folders" id="mail-folders"></div>
1073 <div class="mail-list" id="mail-list"></div>
1074 </div>
1075 </aside>
1076
1077 <!-- Workspace: OPFS file tree -->
1078 <aside class="panel work" id="panel-work" data-panel="work" data-zone="dock" data-label="Workspace" data-i18n-label="panel.work">
1079 <div class="railhead"><span role="heading" aria-level="2" data-i18n="panel.work">Workspace</span>
1080 <span class="files-actions">
1081 <button class="addbtn" data-act="new-file" title="New file" aria-label="New file" data-i18n-title="work.new_file" data-i18n-aria-label="work.new_file">
1082 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M13 3H6a2 2 0 00-2 2v14a2 2 0 002 2h12a2 2 0 002-2V10z"/><path d="M13 3v6h6"/><path d="M12 12.5v5M9.5 15h5"/></svg>
1083 </button>
1084 <button class="addbtn" data-act="new-dir" title="New folder" aria-label="New folder" data-i18n-title="work.new_folder" data-i18n-aria-label="work.new_folder">
1085 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 7a2 2 0 012-2h4l2 2h8a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><path d="M12 11v5M9.5 13.5h5"/></svg>
1086 </button>
1087 <button class="addbtn" data-act="upload" title="Upload files" aria-label="Upload files" data-i18n-title="work.upload" data-i18n-aria-label="work.upload">
1088 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 16V4M8 8l4-4 4 4"/><path d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2"/></svg>
1089 </button>
1090 <button class="addbtn" data-act="refresh" title="Refresh" aria-label="Refresh" data-i18n-title="common.refresh" data-i18n-aria-label="common.refresh">
1091 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 11a8 8 0 10-.6 4"/><path d="M20 4v5h-5"/></svg>
1092 </button>
1093 <button class="addbtn panel-close" data-close="work" title="Close panel" data-i18n-title="common.close_panel">
1094 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
1095 </button>
1096 </span>
1097 </div>
1098 <div class="files-card">
1099 <div class="files-mode"></div>
1100 <button class="instructions-chip" id="instructions-chip" style="display:none"></button>
1101 <div class="files-path"></div>
1102 <div class="files-filter"><input type="text" class="files-filter-input" placeholder="Filter…" autocomplete="off" spellcheck="false" data-i18n-placeholder="work.filter_ph"></div>
1103 <div class="files-tree"></div>
1104 <!-- Daimond's own store, which the tree above deliberately hides: a ×
1105 beside `diamonds/` would delete every Diamond you have. Hiding it
1106 answered the wrong question — what was wanted was to SEE it. Its
1107 own section because the two resolve against different roots, and a
1108 single tree quietly spanning both is the confusion
1109 dev/ROOT_SEPARATION.md exists to prevent. -->
1110 <div class="sys-sec" id="sys-sec">
1111 <!-- Not a `<details>`. Chrome leaves closed details content with a
1112 stale layout box that reports a size while refusing focus, so an
1113 accessibility check sees rows that look on screen and cannot be
1114 reached — and the fix for that is not more CSS, it is the
1115 disclosure the rest of this app already uses. -->
1116 <button class="sys-head" id="sys-head" type="button" aria-expanded="false" aria-controls="sys-body" data-i18n="sys.head">System</button>
1117 <div class="sys-body" id="sys-body" hidden>
1118 <div class="sys-note" data-i18n="sys.note">Daimond’s own files. They live in the browser, not in the folder you have open, and they travel between your devices.</div>
1119 <div class="sys-path" id="sys-path"></div>
1120 <div class="sys-tree" id="sys-tree"></div>
1121 </div>
1122 </div>
1123 </div>
1124 </aside>
1125
1126 <!-- Spending: where inference (your key) and credits (gateway services) go -->
1127 <aside class="panel spend" id="panel-spend" data-panel="spend" data-zone="dock" data-label="Spending" data-i18n-label="panel.spend">
1128 <div class="railhead"><span role="heading" aria-level="2" data-i18n="panel.spend">Spending</span>
1129 <span class="spend-actions">
1130 <button class="addbtn" data-act="spend-refresh" title="Refresh" aria-label="Refresh" data-i18n-title="common.refresh" data-i18n-aria-label="common.refresh">
1131 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 11a8 8 0 10-.6 4"/><path d="M20 4v5h-5"/></svg>
1132 </button>
1133 <button class="addbtn panel-close" data-close="spend" title="Close panel" data-i18n-title="common.close_panel">
1134 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
1135 </button>
1136 </span>
1137 </div>
1138 <div class="spend-view" id="spend-view"></div>
1139 </aside>
1140
1141 <!-- Trash: what has been deleted, and is not gone. Deleting a chat or a
1142 Diamond moves it here and asks nothing; the two questions in this app
1143 that cannot be taken back are both in this panel. See js/trash.js. -->
1144 <aside class="panel trash" id="panel-trash" data-panel="trash" data-zone="dock" data-label="Trash" data-i18n-label="panel.trash">
1145 <div class="railhead"><span role="heading" aria-level="2" data-i18n="panel.trash">Trash</span>
1146 <span class="rail-count" id="trash-count"></span>
1147 <span class="trash-actions">
1148 <button class="addbtn" id="trash-restore-all" data-act="trash-restore-all" title="Restore everything" aria-label="Restore everything" data-i18n-title="trash.restore_all" data-i18n-aria-label="trash.restore_all">
1149 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12a8 8 0 10 2.5-5.8"/><path d="M4 4v5h5"/></svg>
1150 </button>
1151 <button class="addbtn danger" id="trash-empty" data-act="trash-empty" title="Empty trash" aria-label="Empty trash" data-i18n-title="trash.empty" data-i18n-aria-label="trash.empty">
1152 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16M9 7V5h6v2M7 7l1 12h8l1-12"/></svg>
1153 </button>
1154 <button class="addbtn panel-close" data-close="trash" title="Close panel" data-i18n-title="common.close_panel">
1155 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
1156 </button>
1157 </span>
1158 </div>
1159 <!-- What the trash is holding, in bytes, and for how long it holds it.
1160 A trash that grows in silence is how storage fills with things
1161 somebody believes they deleted. -->
1162 <div class="trash-note" id="trash-note"></div>
1163 <div class="trash-list arte-list" id="trash-list"></div>
1164 </aside>
1165
1166 <!-- Social: where a note about Daimond is written, where the proposals
1167 made from notes are read and voted on, and where messages and the
1168 people they come from will live. A note stays on this device until
1169 somebody presses Send on that one note, and what leaves is exactly
1170 the characters on the screen. See js/improve.js and
1171 dev/IMPROVE_CONTRACT.md.
1172
1173 FIVE CHIPS, and two of them lead somewhere that is not built yet --
1174 which those two views SAY, rather than drawing an empty list. An
1175 empty list is the sentence "nothing has arrived", and that is a
1176 different claim from "this is not switched on". `#social-messages`
1177 and `#social-people` are the containers the messaging and people
1178 lanes render into; each carries its own empty line, which those
1179 lanes hide once they have drawn a row. -->
1180 <aside class="panel social" id="panel-social" data-panel="social" data-zone="dock" data-label="Social" data-i18n-label="panel.social">
1181 <div class="railhead"><span role="heading" aria-level="2" data-i18n="panel.social">Social</span>
1182 <span class="imp-chips">
1183 <button type="button" class="imp-chip" data-view="messages" aria-pressed="false" data-i18n="social.messages">Messages</button>
1184 <button type="button" class="imp-chip" data-view="people" aria-pressed="false" data-i18n="social.people">People</button>
1185 <!-- SHARE, and it is here because the module behind it was complete
1186 and unreachable. js/share.js could compose a Diamond, seal it to
1187 one person, choose a carrier, write a .dshare out and read one
1188 back in — and no button in the app called any of it. That is
1189 this project's signature failure and it had shipped three times
1190 before this one. Beside People because a share goes TO somebody,
1191 and the same projection names them in both. -->
1192 <button type="button" class="imp-chip" data-view="share" aria-pressed="false" data-i18n="social.share">Share</button>
1193 <button type="button" class="imp-chip on" data-view="proposals" aria-pressed="true" data-i18n="social.proposals">Proposals</button>
1194 <button type="button" class="imp-chip" data-view="settings" aria-pressed="false" data-i18n="social.settings">Settings</button>
1195 <!-- What this panel is, in the guide. The one circled "i" this app
1196 draws -- the same path as the top bar's About button, because
1197 two information marks drawn differently are two marks the
1198 reader has to learn separately. It opens The interface, which
1199 is the page that names every region of the window; the header
1200 "?" beside About opens the guide's front page, and this is the
1201 same route (`DaimondWeb.guide`) asked for one page of it.
1202
1203 `.icon-btn` and NOT `.addbtn`, which is what the closer beside
1204 it wears. An `.addbtn` is a glyph on a small PLATE, and in the
1205 warm skin that plate's corner is half its side -- a circle. A
1206 circled "i" inside it drew two concentric rings, magnified 6x
1207 and read side by side against the top bar's own mark. The mark
1208 brings its own circle, so it is given no second one, which is
1209 exactly how About and the guide "?" are drawn. -->
1210 </span>
1211 <!-- THE HEAD'S OWN CONTROLS, OUT OF THE CHIP GROUP. They were inside
1212 `.imp-chips`, which wraps, so on a 390px dock they wrapped with
1213 it: the closer sat 248px left of the panel's right edge and 47px
1214 down, alone at the FOOT of the head, while every other panel in
1215 the app holds its cross within 15px of the top-right corner.
1216 Measured 2026-08-28; the census is in dev/lane_closer_audit.mjs.
1217 Ordered ahead of the chips (see app.css) so this row is the head's
1218 first line and the chips take the second. -->
1219 <span class="railhead-acts">
1220 <button type="button" class="icon-btn imp-info" id="social-info"
1221 title="What this panel is, in the guide" data-i18n-title="social.info"
1222 aria-label="What this panel is, in the guide" data-i18n-aria-label="social.info">
1223 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="7.4" r="1.2" fill="currentColor" stroke="none"/><path d="M12 11.6v5"/></svg>
1224 </button>
1225 <button class="addbtn panel-close" data-close="social" title="Close panel" data-i18n-title="common.close_panel">
1226 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
1227 </button>
1228 </span>
1229 </div>
1230
1231 <!-- Messages, and the people they come from. Empty shells with an
1232 honest line in each: js/post.js fills the first, js/trust.js the
1233 second, and both hide the line by calling
1234 `DaimondSocial.filled(view, n)` once they have drawn a row.
1235
1236 EACH LIST IS THAT LANE'S OWN REGION and is cleared wholesale on
1237 every redraw, so nothing belonging to anything else may be parked
1238 inside one. The head, the chips and the empty line above are this
1239 panel's; everything below the line is theirs. -->
1240 <div class="imp-view" id="social-messages" hidden>
1241 <div class="rail-note imp-off" id="social-messages-off" data-i18n="social.messages_off">Messages are not switched on in this build.</div>
1242 <!-- js/post.js OWNS the inside of this list and clears it on every
1243 redraw, so nothing else may keep a node in here. -->
1244 <div class="imp-list" id="social-messages-list"></div>
1245 </div>
1246
1247 <div class="imp-view" id="social-people" hidden>
1248 <div class="rail-note imp-off" id="social-people-off" data-i18n="social.people_off">Nobody yet. Show your code to somebody in the room, or read theirs.</div>
1249 <div class="imp-list" id="social-people-list"></div>
1250 </div>
1251
1252 <!-- js/share.js OWNS the inside of this list and clears it on every
1253 redraw, exactly as post.js owns the messages list above. The head,
1254 the chip and the empty line are the panel's; everything below the
1255 line is share.js's. -->
1256 <div class="imp-view" id="social-share" hidden>
1257 <div class="rail-note imp-off" id="social-share-off" data-i18n="social.share_off">Sharing is not available in this build.</div>
1258 <div class="imp-list" id="social-share-list"></div>
1259 </div>
1260
1261 <!-- Proposals, and the compose box that makes them. Note-capture merged
1262 in here: a note is written at the top and posted straight as a
1263 proposal, verbatim or polished by the model first. There is no
1264 separate Notes view and no Keep -- a note that cannot be sent yet
1265 waits in the queue below the box, not in a list of its own. -->
1266 <div class="imp-view" id="improve-props-view">
1267 <div class="imp-write imp-compose">
1268 <textarea class="imp-box" id="improve-box" rows="3"
1269 placeholder="Where it is, what you expected, and what happened instead."
1270 data-i18n-placeholder="social.box_ph"
1271 aria-label="Write a note about Daimond" data-i18n-aria-label="social.box_label"></textarea>
1272 <!-- What goes with the note, in the exact characters that will
1273 travel, in a row with a closer on it. Closing it takes the
1274 line off the wire as well as off the screen. -->
1275 <div class="imp-with" id="improve-with" hidden>
1276 <div class="imp-with-body">
1277 <span class="imp-with-label" data-i18n="social.with">What goes with it</span>
1278 <div class="imp-with-text" id="improve-with-text"></div>
1279 </div>
1280 <button type="button" class="ui-close imp-with-off" data-act="improve-with-off"
1281 title="Close" data-i18n-title="common.close"
1282 aria-label="Take the details off this note" data-i18n-aria-label="social.with_off"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
1283 </div>
1284 <div class="imp-acts" id="improve-acts">
1285 <button type="button" class="imp-send" data-act="improve-post" data-i18n="social.post"
1286 title="Post exactly what is above to the forge as a proposal." data-i18n-title="social.post_help">Post</button>
1287 <button type="button" class="imp-note-copy" data-act="improve-polish" data-i18n="social.polish"
1288 title="Let the model rewrite it as a proposal, then post that." data-i18n-title="social.polish_help">Polish &amp; post</button>
1289 <span class="imp-as" id="improve-as"></span>
1290 </div>
1291 <div class="imp-say" id="improve-say" role="status" aria-live="polite"></div>
1292 <!-- Said after a note is raised (posted, or queued): "Raised — see it
1293 in Improve", where Improve is a press that opens the hub. Drawn by
1294 js/improve.js. -->
1295 <div class="imp-raised" id="improve-raised" role="status" aria-live="polite" hidden></div>
1296 </div>
1297 <!-- A proposal this device raised that was DECLINED comes back here with
1298 the operator's one-line reason. js/improve.js OWNS it, reads it on
1299 panel open under this device's own voice, and acks each note (per
1300 `when` key) once it has been shown and dismissed. Hidden while empty. -->
1301 <div class="imp-returned" id="improve-returned" role="status" aria-live="polite" hidden></div>
1302 <!-- Notes that could not be sent yet: written offline, or a send that
1303 failed. js/improve.js OWNS this and clears it on every draw; it is
1304 drained automatically when the browser comes back online.
1305
1306 CAPTURE ONLY. The browse/vote list of all proposals is NOT here any
1307 more -- it is the Improve hub (js/tracker.js, #panel-tracker). This
1308 view writes a proposal and shows what is waiting to send; reading and
1309 settling live in the hub, so the two surfaces no longer duplicate a
1310 list. -->
1311 <div class="imp-queue" id="improve-queue"></div>
1312 </div>
1313
1314 <!-- Settings: the voice this device posts with, and the drafts the model
1315 prepared. js/improve.js OWNS the inside and draws all of it. -->
1316 <div class="imp-view" id="social-settings" hidden>
1317 <div class="imp-settings" id="improve-settings"></div>
1318 </div>
1319 </aside>
1320 <!-- ── The Proposals tracker (js/tracker.js) ──────────────────────────
1321 A read-first view of Daimond's own development, tracked as proposals on
1322 the Oregami forge. Reads through the gateway (/api/improve); the owner's
1323 settle controls are dormant until an admin voice is minted. js/tracker.js
1324 OWNS #tracker-view and draws all of it; it self-mounts and reads the
1325 listing the first time the panel is shown. -->
1326 <aside class="panel tracker" id="panel-tracker" data-panel="tracker" data-zone="dock" data-label="Improve" data-i18n-label="panel.tracker">
1327 <div class="railhead"><span role="heading" aria-level="2" data-i18n="panel.tracker">Improve</span>
1328 <button class="addbtn panel-close" data-close="tracker" title="Close panel" data-i18n-title="common.close_panel">
1329 <svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
1330 </button>
1331 </div>
1332 <div id="tracker-view" class="trk"></div>
1333 </aside>
1334 </div>
1335
1336 <!-- Mobile: the panel chip row, which is the DESKTOP header's row moved down
1337 here (js/mobile.js, `placeChips`) and scrolled sideways. It held four
1338 hard-wired destinations -- Chat, Email, Files, Agents -- until 2026-08-28,
1339 which named four of seventeen panels and left the other thirteen behind a
1340 drawer. The owner's verdict on that bar: "the footer in mobile view is
1341 currently useless to me".
1342 The row is MOVED, not copied: `#panel-tags` is the one row the layout
1343 engine renders, the one the unseen counts are painted on, and the one the
1344 gallery asks whether a panel is on. Two of it would be two to keep in
1345 step. The chat is still the floor; a "thing" (Web, Doc, Message, Compose,
1346 Tools) rises as a sheet over it, and the rail is still the left drawer
1347 behind the hamburger. -->
1348 <nav class="mnav" id="mnav" aria-label="Panels" data-i18n-aria-label="gallery.title"></nav>
1349</div>
1350
1351<!-- Phone shell: the scrim behind the drawer, and the sheet that raises a
1352 "thing" over the chat. Both are inert on desktop (see mobile.css). -->
1353<div id="scrim"></div>
1354<div id="msheet" aria-label="Panel" data-i18n-aria-label="sheet.panel">
1355 <div class="msheet-grab" id="msheet-grab">
1356 <span class="msheet-bar" aria-hidden="true"></span>
1357 <span class="msheet-title" id="msheet-title"></span>
1358 <!-- Drawn, like every other closer in the app. The '✕' character it used to
1359 carry sat wherever the platform's fallback font put it, and the sheet
1360 is the one surface where the closer is the only way out that does not
1361 need a drag. -->
1362 <button class="msheet-close ui-close" id="msheet-close" title="Close" aria-label="Close the sheet"
1363 data-i18n-title="common.close" data-i18n-aria-label="sheet.close"><svg class="ic" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
1364 </div>
1365 <div class="msheet-scroll" id="msheet-body"></div>
1366 <div class="msheet-ask" id="msheet-ask">
1367 <input id="msheet-ask-input" type="text" placeholder="Ask about this…" autocomplete="off" autocapitalize="sentences" spellcheck="true" data-i18n-placeholder="sheet.ask_ph">
1368 <button id="msheet-ask-send" title="Ask" aria-label="Ask" data-i18n-title="sheet.ask" data-i18n-aria-label="sheet.ask">➤</button>
1369 </div>
1370</div>
1371
1372<!-- Settings, where there is no rail to hold them. On a phone, and in the band
1373 where the rail folds away, there is no room for a form beside a live chat —
1374 so the settings view is MOVED into this card and back out again. There is
1375 one settings form in the document, not two.
1376
1377 The card carries no heading of its own: the hosted view brings its head with
1378 it, so the title names what is actually on screen. A static "Settings" here
1379 contradicted every view but one. -->
1380<div id="settings-modal" class="modal" style="display:none;">
1381 <div class="modal-card">
1382 <div id="settings-slot"></div>
1383 <button class="modal-close" id="settings-close" data-i18n="common.close">Close</button>
1384 </div>
1385</div>
1386
1387<!-- Identity modal: create a passphrase on first run, unlock on return. The
1388 passphrase encrypts the stored API key and is the on-device Daimond identity. -->
1389<div id="identity-modal" class="modal" style="display:none;">
1390 <div class="modal-card">
1391 <img class="login-logo" id="id-logo" src="assets/daimond_word.svg" alt="Daimond" data-light="assets/daimond_word_dark.svg" data-dark="assets/daimond_word.svg">
1392 <!-- THE FRONT DOOR, for somebody who arrived from daimond.app and has never
1393 seen this app.
1394
1395 "Launch app" on the splash page lands here, on a form headed "Create
1396 your account" — which is a true instruction and a poor answer, because
1397 the account it makes is refused by a gateway in closed beta and the
1398 person is told so only afterwards. The three ways in existed (the
1399 application form at /apply.html, its waitlist half, and the passcode
1400 dialog in js/passcode.js); nothing at the door pointed at any of them.
1401
1402 ABOVE the title, and above the form, because it is the answer to
1403 "what is this and can I have it" and the form is the answer to a
1404 question a stranger has not asked yet. It is three names on one
1405 wrapped row and one line of lead — this card already runs 874px and
1406 scrolls on a laptop, so the strip is the smallest thing that can
1407 carry three routes.
1408
1409 THE HEAD IS INSIDE THE LEAD, on one line, and that is a correction.
1410 Written as a head ABOVE the lead the strip stood 176px tall and put
1411 "Create account" 25px under the card's own bottom edge — a first
1412 visitor could not see the button they were brought here to press.
1413 The question and the answer are one sentence, so they are set as
1414 one, and the two keys stay two so the wording of either can change
1415 without the other. The rest of that 176px is answered by the sticky
1416 foot at `.id-foot` below, which is what actually guarantees the
1417 button; this only stops the strip from spending more of the card
1418 than it needs.
1419
1420 SHOWN TO A STRANGER ONLY. `syncDoors` in js/daimond.js draws it only
1421 while creating, and only when no account in this browser has ever
1422 held an identity. Somebody unlocking sees nothing; somebody adding a
1423 second account to a browser that already has one sees nothing. -->
1424 <div class="id-doors" id="id-doors" style="display:none">
1425 <p class="id-doors-lead"><span class="id-doors-head" data-i18n="door.head">New to Daimond?</span> <span data-i18n="door.lead">It is in closed testing, so accounts are given out by passcode.</span></p>
1426 <div class="id-doors-row">
1427 <!-- Root-absolute, and a new tab, for the reasons the passcode card's
1428 own link states: the app is one document at the site root, and
1429 leaving the page would take a half-typed passphrase with it. -->
1430 <a class="id-door" id="id-door-test" href="/apply.html?for=test" target="_blank" rel="noopener" data-i18n="door.test">Apply to test</a>
1431 <a class="id-door" id="id-door-wait" href="/apply.html?for=waitlist" target="_blank" rel="noopener" data-i18n="door.waitlist">Join the waitlist</a>
1432 <!-- A DISCLOSURE, not a one-way switch. Pressing it reveals the note
1433 below (there is no device key to redeem a code onto yet, so the
1434 order has to be said rather than the field drawn); pressing it
1435 again puts the note away. It was neither before, and
1436 `verify_reversible` was right to call it the only control on this
1437 screen with no way back. `aria-expanded` is what says so to a
1438 reader, and `aria-controls` names the thing it opens. -->
1439 <button type="button" class="id-door" id="id-door-code" aria-expanded="false" aria-controls="id-doors-note" data-i18n="door.code">I have a passcode</button>
1440 </div>
1441 <p class="id-doors-note" id="id-doors-note" style="display:none"></p>
1442 </div>
1443 <h2 id="id-title">Protect this device</h2>
1444 <p class="cfg-lead" id="id-lead"></p>
1445 <!-- Several accounts on one browser: pick whose to unlock, or add another. Built in JS, and
1446 shown only when there is more than one account or one to add. -->
1447 <div class="id-accounts" id="id-accounts" style="display:none"></div>
1448 <!-- A REAL form with REAL password fields, so the browser and the OS
1449 keychain recognise a credential and offer to keep it. The passphrase
1450 is generated (see wordlist.js), which is what makes accepting that
1451 offer safe: there is nothing weak or reused to save. The name doubles
1452 as the username a manager files the entry under, so several accounts
1453 in one browser stay distinguishable. -->
1454 <form id="id-form" autocomplete="on">
1455 <div class="id-fields">
1456 <div id="id-name-row">
1457 <input type="text" id="id-name" name="username" placeholder="Your name" data-i18n-placeholder="identity.your_name" autocomplete="username" autocapitalize="off" autocorrect="off" spellcheck="false" maxlength="40">
1458 </div>
1459 <!-- The generated passphrase, shown so it can be written down. It
1460 mirrors the field below rather than holding a second copy, so a
1461 browser-suggested password cannot diverge from what is displayed. -->
1462 <div class="pass-gen" id="id-genbox" style="display:none">
1463 <div class="pass-gen-words" id="id-genwords"></div>
1464 <div class="pass-gen-acts">
1465 <button type="button" class="pass-gen-btn" id="id-regen" data-i18n="identity.generate_another">Generate another</button>
1466 <button type="button" class="pass-gen-btn" id="id-gencopy" data-i18n="common.copy">Copy</button>
1467 </div>
1468 <p class="pass-gen-note" id="id-gennote"></p>
1469 <label class="pass-gen-ack"><input type="checkbox" id="id-wrote"><span data-i18n="identity.wrote_it_down">I have written this down somewhere safe</span></label>
1470 </div>
1471 <div class="pass-wrap" id="id-pass-row">
1472 <input type="password" id="id-pass" name="password" placeholder="Passphrase" autocomplete="current-password" autocapitalize="off" autocorrect="off" spellcheck="false" data-i18n-placeholder="identity.passphrase">
1473 <button type="button" class="pass-eye icon-btn" id="id-pass-eye" title="Show passphrase" aria-label="Show passphrase" data-i18n-title="identity.show_passphrase" data-i18n-aria-label="identity.show_passphrase"></button>
1474 </div>
1475 <div class="pass-wrap" id="id-pass2-row">
1476 <input type="password" id="id-pass2" placeholder="Confirm passphrase" autocomplete="new-password" autocapitalize="off" autocorrect="off" spellcheck="false" data-i18n-placeholder="identity.confirm_passphrase">
1477 <button type="button" class="pass-eye icon-btn" id="id-pass2-eye" title="Show passphrase" aria-label="Show passphrase" data-i18n-title="identity.show_passphrase" data-i18n-aria-label="identity.show_passphrase"></button>
1478 </div>
1479 </div>
1480 <div id="id-error" class="login-error"></div>
1481 <!-- THE ONE BUTTON THIS SCREEN EXISTS FOR, in a foot that stays on
1482 screen while the card scrolls (see `.id-foot` in css/app.css).
1483 A wrapper and not `position: sticky` on the button itself,
1484 because the button's own 12px top margin would be a transparent
1485 band for the form to scroll through; the foot carries that gap
1486 as padding and paints the card's ground behind it. -->
1487 <div class="id-foot">
1488 <button type="submit" id="id-primary">Create passphrase</button>
1489 </div>
1490 </form>
1491 <button type="button" id="id-choose" class="id-choose" style="display:none" data-i18n="identity.choose_own">Choose my own passphrase instead</button>
1492 <button type="button" id="id-skip" class="modal-close" data-i18n="identity.skip">Skip for now</button>
1493 </div>
1494</div>
1495
1496<!-- accounts.js FIRST: it installs the per-account storage namespace before any
1497 module reads or writes localStorage. -->
1498<script src="js/accounts.js"></script>
1499<!-- breadcrumb.js immediately after: it records the boot itself, and an error
1500 thrown by anything below has to land in the trail rather than in a console
1501 nobody on a phone can open. -->
1502<script src="js/breadcrumb.js"></script>
1503<!-- safe.js next, and it must be next: it reads the trail breadcrumb.js has
1504 just written to decide whether this app is looping, and sync.js consults
1505 its answer from the first moment a session exists. -->
1506<script src="js/safe.js"></script>
1507<!-- i18n.js next: it owns the language and the display currency, and every
1508 money formatter in the app funnels through it. The English table follows
1509 immediately, because it is both the baseline and the fallback. -->
1510<script src="js/i18n.js"></script>
1511<script src="i18n/en.js"></script>
1512<!-- closer.js right after the words it uses: it builds the one cross every
1513 dialog, popover, drawer and sheet wears, and it is loaded before ANY of
1514 them so a classic script and the wasm module can both reach for it. -->
1515<script src="js/closer.js"></script>
1516<!-- journal.js: the write-ahead log for in-flight work. Loaded early so it is
1517 ready before the first turn. -->
1518<script src="js/journal.js"></script>
1519<script src="js/marked.min.js"></script>
1520<script src="js/render.js"></script>
1521<!-- crystal.js beside render.js: it is the other renderer. It owns the sandboxed
1522 frame a Diamond's page runs in, the `dc` channel that page speaks, the
1523 shipped default page and the built-in view that replaces a page which fails.
1524 Classic, before the module, so window.DaimondCrystal exists when the app
1525 mounts one. -->
1526<script src="js/crystal.js"></script>
1527<!-- viewer.js: the Doc panel's file viewer, so that opening a PDF or a PNG shows
1528 the document rather than a screenful of replacement characters. Classic,
1529 before the module, so window.DaimondViewer exists when the app reaches for
1530 it -- the same rule every other panel module here loads under. -->
1531<script src="js/viewer.js"></script>
1532<script src="js/pricing.js"></script>
1533<script src="js/ledger.js"></script>
1534<!-- money.js: which pot of money a figure belongs to, and what may honestly be
1535 said about it. Pure wording rules, before the app that draws them. -->
1536<script src="js/money.js"></script>
1537<!-- signals.js: the local usage index the Optimiser reads. After ledger.js,
1538 which it does not depend on but sits beside conceptually: the ledger is the
1539 account's money, this is how the account is worked. -->
1540<script src="js/signals.js"></script>
1541<script src="js/governor.js"></script>
1542<script src="js/pause.js"></script>
1543<!-- trash.js BEFORE sync.js, daimond.js and graph.js: all three ask it whether
1544 something has been deleted while they build the lists they draw, so the
1545 store has to be there when they do. -->
1546<!-- improve.js next: the panel a note about Daimond is written in, and the
1547 proposals made from notes are read and voted on in. Nothing leaves this
1548 device until somebody presses Send on one note. See dev/IMPROVE_CONTRACT.md. -->
1549<script src="js/trash.js"></script>
1550<!-- rekey.js BEFORE every module that seals a secret under the passphrase —
1551 voice.js is the first of them. Each registers with it AT LOAD, beside its
1552 own sealing code, so that a passphrase change carries every secret across
1553 without anybody keeping a list. See the header of js/rekey.js. -->
1554<script src="js/rekey.js"></script>
1555<!-- voice.js BEFORE improve.js: the panel presents a voice to write with, and
1556 this holds it. Before identity.js is fine — it reaches DaimondIdentity only
1557 inside a call, never while loading. -->
1558<script src="js/voice.js"></script>
1559<!-- drafts.js BEFORE every module that owns a text box — improve.js is the
1560 first of them, daimond.js's composer is the largest. It holds what somebody
1561 is half-way through typing so a reload does not take it, and it is loaded
1562 early because a box binds to it as it is drawn. It sends nothing and
1563 reaches no network: see the header, which says why that is not the send
1564 queue dev/IMPROVE_CONTRACT.md §4 forbids. -->
1565<script src="js/drafts.js"></script>
1566<script src="js/improve.js"></script>
1567<script src="js/triage.js"></script>
1568<script src="js/tracker.js"></script><!-- the Proposals tracker: read-first, self-mounts into #tracker-view -->
1569<!-- approvelist.js AFTER triage.js: it reads triage's held plan into a local
1570 review queue and batch-sends the ticked drafts through improve.js's forge
1571 door. It makes its own host element, so it needs no markup here. -->
1572<script src="js/approvelist.js"></script>
1573<script src="js/triggers.js"></script>
1574<script src="js/verify.js"></script>
1575<!-- wordlist.js before identity.js: the create screen generates a passphrase
1576 before any identity exists to hold it. -->
1577<script src="js/wordlist.js"></script>
1578<!-- The pure-JS Ed25519/X25519 fallback, before identity.js so it is present
1579 when an unlock on an engine without WebCrypto for those curves needs it.
1580 Vendored noble-curves loads first; curvefallback.js wraps it. -->
1581<script src="js/vendor/noble-curves.min.js"></script>
1582<script src="js/curvefallback.js"></script>
1583<script src="js/identity.js"></script>
1584<!-- passkey.js AFTER identity.js: it builds on DaimondIdentity to seal and
1585 recover the passphrase behind a WebAuthn PRF credential. -->
1586<script src="js/passkey.js"></script>
1587<script src="js/gateway.js"></script>
1588<!-- telemetry.js: what a beta tester who agreed sends, and the whole of it —
1589 twenty events, three integers each, and no text field anywhere in the
1590 payload. Read the top of the file; it is written to be read.
1591
1592 LOADED HERE, BEFORE passcode.js, which is the only thing that can start it:
1593 the consent card shown when a passcode is redeemed, and the same question in
1594 the Credits drawer, where it is also turned off again. Loading it does not
1595 arm it — there is no flag to forget to check, only a recorder that does not
1596 exist until somebody presses a button — and dev/verify_telemetry.mjs proves
1597 that at the network, in a session that does real work and sends nothing.
1598
1599 It stayed OUT of this list until three things were true, and each is now a
1600 check in that verifier: consent can be taken back and taking it back stops a
1601 batch already queued; something actually emits, so the question is not
1602 asking permission to send nothing; and the Privacy Policy describes what is
1603 sent, at #beta-telemetry, which the consent card links to. -->
1604<script src="js/telemetry.js"></script>
1605<!-- passcode.js AFTER gateway.js: the refusal it draws is raised by
1606 gateway.js, and the redemption behind its one button is
1607 DaimondGateway.redeemPasscode. Classic, before the module, so
1608 window.DaimondCredits exists when daimond.js first draws the Credits
1609 view. -->
1610<script src="js/passcode.js"></script>
1611<script src="js/chunks.js"></script>
1612<script src="js/cloud.js"></script>
1613<script src="js/sync.js"></script>
1614<script src="js/pairing.js"></script>
1615<!-- trust.js AFTER pairing.js, whose dialog frame and QR drawer it borrows, and
1616 after identity.js (the keys), improve.js (the Social panel it renders the
1617 People list into) and gateway.js. It loads js/qrscan.js itself, on demand,
1618 when somebody opens the scanner — a camera decoder is not something to
1619 fetch on every boot for the readers who never scan anything. -->
1620<script src="js/trust.js"></script>
1621<!-- post.js AFTER trust.js, which holds the keys it seals to and the People
1622 list it names, and after sync.js, whose parcel carries its store. -->
1623<script src="js/post.js"></script>
1624<!-- group.js AFTER post.js, which owns the store its rosters live in, the seal
1625 they travel under and the region it draws into. It holds no storage of its
1626 own and no seal of its own. -->
1627<script src="js/group.js"></script>
1628<!-- report.js and share.js AFTER post.js, which holds the messages the one
1629 reports on and the seal the other borrows. Both couple to post.js through
1630 one delegated listener each and touch none of its DOM, so the order is
1631 about what is on the page when they attach and nothing more. -->
1632<script src="js/report.js"></script>
1633<script src="js/share.js"></script>
1634<script src="js/mail.js"></script>
1635<script src="js/tools.js"></script>
1636<script src="js/models.js"></script>
1637<!-- After models.js: window.DaimondOcr does the authenticated OpenRouter / Mistral POSTs that
1638 turn a PDF or an image into text for `file_read` and the `ocr` tool, and it reads the
1639 provider key from DaimondModels, so that must exist first. -->
1640<script src="js/ocr.js"></script>
1641<!-- After models.js, which it is a miniature of: same registry shape, same
1642 sealing, same credits-or-your-own-key question asked about search. -->
1643<script src="js/search.js"></script>
1644<script src="js/autoreload.js"></script>
1645<script src="js/web.js"></script>
1646<script src="js/hand.js"></script>
1647<script src="js/handpty.js"></script>
1648<script src="js/terminal.js"></script>
1649<script src="js/spend.js"></script>
1650<!-- pwa.js beside updater.js, deliberately: the updater reads build.json and pwa.js
1651 passes what it read to the shell worker, so the worker's cache is emptied by
1652 the same reading that moves the update chip. -->
1653<script src="js/pwa.js"></script>
1654<script src="js/updater.js"></script>
1655<script src="js/workspace.js"></script>
1656<script src="js/release.js"></script>
1657<script src="js/graph.js"></script>
1658<!-- The persistent desktop peer: window.DaimondPeer / DaimondLease /
1659 DaimondPresence, consumed by the guarded glue in daimond.js. Money-safe
1660 (the sequential double-bill is closed, proven in live two-context QA). See
1661 dev/PEER_DESIGN.md. -->
1662<script src="js/peer.js"></script>
1663
1664<!-- The phone shell (drawer + sheet). Classic, before the module, so
1665 window.DaimondSheet/DaimondShell exist when the app wires into them. -->
1666<script src="js/mobile.js"></script>
1667<!-- fullscreen.js AFTER mobile.js: it reads DaimondShell.isPhone for the phone gate. -->
1668<script src="js/fullscreen.js"></script>
1669<!-- The permission ladder's surface. Classic, before the module, so
1670 window.DaimondHandMode exists when daimond.js hands it the wasm setter. -->
1671<script src="js/handmode.js"></script>
1672<!-- The Terms and the Privacy Policy, in Daimond's own Web panel rather than
1673 on another website, and the notices two of their clauses promise before
1674 something of the user's lapses. legal.js first: lapse.js links into a
1675 clause through it. -->
1676<script src="js/legal.js"></script>
1677<script src="js/lapse.js"></script>
1678<!-- Interaction recording, for turning a path somebody actually took into a
1679 permanent check (dev/replay.mjs). INERT until a person presses Ctrl+Alt+R:
1680 it binds one keydown listener at load and nothing else. See dev/RECORD.md
1681 for what it captures, what it refuses to look at, and why. -->
1682<script src="js/record.js"></script>
1683<script type="module" src="js/daimond.js"></script>
1684</body>
1685</html>