Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions docs/user/web.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,19 +247,38 @@ conversation.

### Settings

The same pages as [`/settings`](/user/settings), in the same order, and what the epics spent:
Settings has two groups. **App** is the page's own: how it is drawn, the daemons it reaches and
what it tells you of them. It is the same whichever daemon is on screen. **Daemon** is the same
pages as [`/settings`](/user/settings), in the same order, and what the epics spent, all of one
daemon:

| Page | What is there |
| --- | --- |
| **General** | Whether a turn's working is shown, the [`/btw` agent](/user/btw), and [error reports](/user/reporting) |
| **Themes** | The [themes](#themes) of this browser, and which two **Auto** follows your system with |
| **Daemons** | The daemons this page reaches: **Local**, and each SSH host added or imported |
| **Notifications** | Which notifications appear, from every daemon, and whether the system shows them |
| **General** | Whether a turn's working is shown, the [`/btw` agent](/user/btw), and [error reports](/user/reporting) |
| **Accounts** | Every [account](/user/settings#accounts) by its CLI, way in and the names of what it sets: ask what it runs again, **Edit** it (what you leave blank stays as it was), copy it to another CLI, remove it, or make one |
| **Fallback** | Each [step](/user/settings#fallback): where a turn goes when a place cannot take it, and how it is tried again |
| **Runtimes** | Every runtime: add one from its backend's fields, change, check or remove it, or bring hosts in from your ssh config |
| **Backup** | Every [backup target](/user/backups): its repository and branch, when an epic goes to it (**On session close**, **Every N minutes**, **On epic end**, or none: by hand only), and whether it is a **Default**; add, edit or remove one. A URL is shown with nothing signed into it |
| **Workspace** | What a directory was last set up with, and forgetting it -- which also takes it out of the sidebar where no epic ran there |
| **Usage** | What the epics spent, of every directory or of one, by day and by flow, looking back 7, 30, 90 or 365 days |

Above the **Daemon** pages, **Daemon** picks which daemon they read and write: the one on screen
until another is picked. It goes into the address as `?daemon=<name>`, so a daemon's page can be
kept and opened again. Picking one changes nothing else on screen.

**Batch apply**, on **General**, **Fallback**, **Runtimes** and **Backup**, copies what the page
holds to other daemons at once. Tick the daemons, or **All**, then **Apply**. Each daemon then
says **Applied** or why it refused. **General** sends the form as it stands, saved or not.
**Backup** sends every target and default, in place of the daemon's own. A target signed into its URL
is refused there, since the URL is never shown whole. **Fallback** and
**Runtimes** send each step or runtime, written over the one of the same name there, or added.
Nothing a daemon already has is taken away. **Accounts** cannot be copied, since their secrets
are never shown (**Secrets stay put**). **Workspace** and **Usage** are of one daemon's
directories, and have no **Batch apply**.

An account is never shown by its values: a page lists the names of the variables it sets, and
nothing else. A way in that runs a CLI's own sign-in, such as `login`, needs a terminal: it is
offered as `(at a terminal)` and made from `/settings` in `hmz`.
Expand Down
7 changes: 4 additions & 3 deletions src/hmz/web/static/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import { follow } from './shell.js'
* @param {string} [made.placeholder]
* @param {string} [made.id]
* @param {(path: string) => void} [made.changed] Told the path whenever it changes.
* @param {string} [made.from] Whose paths: '' for the daemon the page is on, else `/d/<name>`.
*/
export function pathPicker({ value = '', files = false, label, placeholder = files ? '~/file' : '~/folder', id, changed = () => {} }) {
export function pathPicker({ value = '', files = false, label, placeholder = files ? '~/file' : '~/folder', id, changed = () => {}, from = '' }) {
const list = `paths-${Math.random().toString(36).slice(2, 8)}`
const input = h('input', { type: 'text', value, placeholder, 'aria-label': label, id, list, autocomplete: 'off', spellcheck: 'false', 'data-hmz': 'path-input' })
const offered = h('datalist', { id: list })
Expand All @@ -37,7 +38,7 @@ export function pathPicker({ value = '', files = false, label, placeholder = fil
clearTimeout(typed)
typed = setTimeout(async () => {
try {
const { paths } = await get(`/api/paths?typed=${encodeURIComponent(input.value)}`)
const { paths } = await get(`${from}/api/paths?typed=${encodeURIComponent(input.value)}`)
fill(offered, paths.map((one) => h('option', { value: one })))
} catch {
fill(offered)
Expand All @@ -48,7 +49,7 @@ export function pathPicker({ value = '', files = false, label, placeholder = fil
async function show(dir) {
let read
try {
read = await get(`/api/files?at=${encodeURIComponent(dir)}${files ? '&files=1' : ''}${hidden ? '&hidden=1' : ''}`)
read = await get(`${from}/api/files?at=${encodeURIComponent(dir)}${files ? '&files=1' : ''}${hidden ? '&hidden=1' : ''}`)
} catch (error) {
if (dir !== '~') return show('~')
fill(panel, h('p', { class: 'error' }, error.message))
Expand Down
11 changes: 7 additions & 4 deletions src/hmz/web/static/pickers.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ const failed = new Map()

/** Reads the CLIs installed again -- as a workspace runs them -- and tells every picker. */
let reading = ''
/** Whose CLIs they are: '' for the daemon the page is on, else `/d/<name>`, as Settings picks. */
let base = ''

export async function readBackends(workspace = reading) {
export async function readBackends(workspace = reading, from = '') {
reading = workspace
backends = await get(`/api/backends?workspace=${encodeURIComponent(workspace)}`)
base = from
backends = await get(`${from}/api/backends?workspace=${encodeURIComponent(workspace)}`)
for (const one of told) one()
return backends
}
Expand All @@ -31,12 +34,12 @@ function ask(cli, account) {
if (!asking.has(key)) {
asking.set(
key,
post('/api/accounts/models', { cli, name: account })
post(`${base}/api/accounts/models`, { cli, name: account })
.then(() => failed.delete(key))
.catch((error) => failed.set(key, error.message))
.finally(() => {
asking.delete(key)
return readBackends()
return readBackends(reading, base)
}),
)
}
Expand Down
67 changes: 67 additions & 0 deletions src/hmz/web/static/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -3845,3 +3845,70 @@ input[aria-invalid='true'] {
-webkit-user-select: none;
user-select: none;
}

/* settings daemons */
.settings-group {
margin: 12px 10px 4px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--ink-3);
}

.settings-nav h2 + .settings-group {
margin-top: 0;
}

.settings-daemon {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 20px;
border-bottom: 1px solid var(--line);
}

.settings-daemon select {
min-width: 0;
max-width: 100%;
}

.batch-dialog {
width: min(calc(100vw - 32px), 420px);
max-height: calc(100vh - 32px);
overflow-y: auto;
padding: 16px 20px;
color: var(--ink);
background: var(--paper);
border: 2px solid var(--ink);
box-shadow: 8px 8px 0 var(--cyan);
}

.batch-dialog::backdrop {
background: var(--scrim);
}

.batch-dialog h3 {
margin: 0 0 4px;
}

.batch-results {
margin: 12px 0;
padding: 0;
list-style: none;
display: grid;
gap: 4px;
overflow-wrap: anywhere;
}

@media (max-width: 860px) {
.settings-group {
margin: 0 4px 0 8px;
align-self: center;
white-space: nowrap;
}

.settings-daemon {
padding: 8px 12px;
}
}
Loading
Loading