Release 0.3.0 #5

Merged
dguiducci merged 18 commits from main into release 2026-08-24 22:01:48 +01:00
9 changed files with 45 additions and 28 deletions
Showing only changes of commit e5e8ccc92f - Show all commits
+3
View File
@@ -75,6 +75,9 @@ release PR may merge — and a section is closed at the commit that bumps it.
### Fixed ### Fixed
- **Models → Text-to-speech** now fills the window like every other page. It was rendering
as a narrow strip in the middle of an otherwise empty screen, which made the model list
and its forms unreadably cramped.
- A connector that fails to start no longer leaves its process behind. One that started - A connector that fails to start no longer leaves its process behind. One that started
but answered the handshake wrong — a broken or mismatched connector — was left running but answered the handshake wrong — a broken or mismatched connector — was left running
on every retry, and the accumulated processes eventually used up every file handle the on every retry, and the accumulated processes eventually used up every file handle the
+15 -1
View File
@@ -32,6 +32,19 @@ All extend `LightElement` from `web/lib/base.js` (Lit). `ChatSession` (`web/lib/
The role editor (`roles-page.js`) sets the default group + an allowed-groups checklist (→ `attrs.permission_groups`) + a **default-assistant** select (→ `attrs.chat_agent`) fed by `GET /api/agents` filtered to `type:chat` minus `project-coordinator` (source-driven); the same exclusion is enforced server-side in the roles API (`validate_chat_agent`). The role editor (`roles-page.js`) sets the default group + an allowed-groups checklist (→ `attrs.permission_groups`) + a **default-assistant** select (→ `attrs.chat_agent`) fed by `GET /api/agents` filtered to `type:chat` minus `project-coordinator` (source-driven); the same exclusion is enforced server-side in the roles API (`validate_chat_agent`).
## The page shell — a new page renders narrow until it is sized (`web/css/page-shell.css`)
**The trap, in one sentence: a custom element the browser has never heard of is `display: inline`, and every page host in this app is a `display: flex` container — so a page nobody wrote a CSS rule for becomes a content-sized flex item and renders as a narrow column in the middle of the workspace.** It looks like a broken stylesheet inside the page; it is the absence of a rule *about* the page. `<models-tts-section>` shipped this way — added to the Models hub beside its three siblings, never added to the sizing block that listed them by name.
Two independent things have to be true, and both were violated at some point:
- **The host needs `flex-direction: column`.** Every page host is toggled `display: none``flex` by its own component (`this.style.display = this._open ? 'flex' : 'none'`), which means the value in CSS is always `none` and the flex direction is never obvious from reading the block. In the default `row`, a child is sized by its content on the main axis; in `column`, the cross axis stretches and the child fills the width no matter what it declares. Several pages hid this for years behind a `width: 100%` on their own root `<div>` (`.pv-page`, `.page-panel`, `.apr-page`, `.llmr-page`) — which works, but only defends the one page that remembered it. `.llm-page`, shared by all four Models sections, carries `max-width: 100%` and no `width`, which is why the section with no host rule collapsed and its three siblings did not.
- **The child needs `flex: 1; min-height: 0; min-width: 0`.** Otherwise it fills the width but not the height, and a wide table inside it pushes the whole workspace row wider than the viewport.
**The fix is a descendant selector, not a longer list.** The three multiplexer pages (`tasks-page`, `projects-page`, `models-hub-page`) each render exactly one sub-section at a time as their only child, so `page-shell.css` sizes them as `tasks-page > *, projects-page > *, models-hub-page > *`. The enumeration *was* the bug: a list of element names is a thing to forget, and forgetting it is silent — no console error, no failed build, just a narrow page. A new section now inherits the sizing by existing. `plugin-page-host > [plugin-id]` had already reached the same conclusion for plugin-contributed fragments.
**So: adding a page to the app is two edits, not one.** Write the component, *and* give the element a rule — `display: none` + `flex-direction: column` + `flex: 1` + `min-width: 0` — in `page-shell.css` or the page's own stylesheet. If it hangs off one of the three multiplexers, the `> *` rule already covers it and you write nothing. To check the whole set at once, look for a host that sets `style.display = 'flex'` in `web/components/` and has no matching element selector in `web/css/`.
| File | Element | Notes | | File | Element | Notes |
| ---- | ------- | ----- | | ---- | ------- | ----- |
| `copilot.js` | `<app-copilot>` | The chat surface (`_wsSource='web'`): full/dock roving layout, welcome hero empty state, privacy chip, composer with model pill, slash-command autocomplete | | `copilot.js` | `<app-copilot>` | The chat surface (`_wsSource='web'`): full/dock roving layout, welcome hero empty state, privacy chip, composer with model pill, slash-command autocomplete |
@@ -61,9 +74,10 @@ The role editor (`roles-page.js`) sets the default group + an allowed-groups che
| `connector-detail.js` | `<connector-detail-page>` | A connector's own page (`#connector?name=X`): env/secret form + Test, the **OAuth login panel** (sign in → paste code → complete, §15), global enable. Access grants live **only** on the Users page (`users-page.js` — the `#users/{id}` page's connectors section, with the plugin grants right below it), so "who has what" has a single surface | | `connector-detail.js` | `<connector-detail-page>` | A connector's own page (`#connector?name=X`): env/secret form + Test, the **OAuth login panel** (sign in → paste code → complete, §15), global enable. Access grants live **only** on the Users page (`users-page.js` — the `#users/{id}` page's connectors section, with the plugin grants right below it), so "who has what" has a single surface |
| `shared/connector-common.js` | (helpers) | Shared Connectors vocabulary: `statusOf` (incl. `needs_login` for a pending OAuth row), `STATUS_LABEL`, schema normalization, `jf` fetch | | `shared/connector-common.js` | (helpers) | Shared Connectors vocabulary: `statusOf` (incl. `needs_login` for a pending OAuth row), `STATUS_LABEL`, schema normalization, `jf` fetch |
| `llm-providers.js` | `<llm-providers-page>` | LLM provider management | | `llm-providers.js` | `<llm-providers-page>` | LLM provider management |
| `models-hub.js` | `<models-hub-page>` | Models hub landing (LLM / Transcription / Image) | | `models-hub.js` | `<models-hub-page>` | Models hub landing (LLM / Transcription / Image / TTS); renders one section at a time, sized by the `models-hub-page > *` rule |
| `models-llm.js` | `<models-llm-section>` | LLM model CRUD + drag-and-drop priority | | `models-llm.js` | `<models-llm-section>` | LLM model CRUD + drag-and-drop priority |
| `models-transcribe.js` | `<models-transcribe-section>` | Transcription model CRUD | | `models-transcribe.js` | `<models-transcribe-section>` | Transcription model CRUD |
| `models-image.js` | `<models-image-section>` | Image generation model CRUD | | `models-image.js` | `<models-image-section>` | Image generation model CRUD |
| `models-tts.js` | `<models-tts-section>` | Text-to-speech model CRUD |
| `mobile-app.js` | `<mobile-app>` | Mobile app shell | | `mobile-app.js` | `<mobile-app>` | Mobile app shell |
| `shared/settings-page.js` | `<settings-page>` | Mobile settings: per-user avatar, locale picker (`I18nMixin`), profile/preferences | | `shared/settings-page.js` | `<settings-page>` | Mobile settings: per-user avatar, locale picker (`I18nMixin`), profile/preferences |
+2 -1
View File
@@ -1,7 +1,8 @@
/* Agent Inbox page — desktop layout. Card styles live in inbox-cards.css. */ /* Agent Inbox page — desktop layout. Card styles live in inbox-cards.css. */
agent-inbox-page { agent-inbox-page {
display: none; display: none; /* toggled by JS → flex */
flex-direction: column;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
+2 -1
View File
@@ -1,9 +1,10 @@
/* ── Agents page layout ───────────────────────────────────────────────────── */ /* ── Agents page layout ───────────────────────────────────────────────────── */
agents-page { agents-page {
display: none; display: none; /* toggled by JS → flex */
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
min-width: 0;
overflow-y: auto; overflow-y: auto;
background: var(--bs-body-bg); background: var(--bs-body-bg);
} }
+1
View File
@@ -2,6 +2,7 @@
approval-groups-page { approval-groups-page {
display: none; /* toggled by JS */ display: none; /* toggled by JS */
flex-direction: column;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
overflow-y: auto; overflow-y: auto;
+1
View File
@@ -2,6 +2,7 @@
approval-rules-page { approval-rules-page {
display: none; /* toggled by JS */ display: none; /* toggled by JS */
flex-direction: column;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
overflow-y: auto; overflow-y: auto;
+2 -1
View File
@@ -1,7 +1,8 @@
config-page { config-page {
display: none; display: none; /* toggled by JS → flex */
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
min-width: 0;
overflow-y: auto; overflow-y: auto;
background: var(--bs-body-bg); background: var(--bs-body-bg);
} }
+2 -1
View File
@@ -1,9 +1,10 @@
/* ── Dashboard page ────────────────────────────────────────────────────────── */ /* ── Dashboard page ────────────────────────────────────────────────────────── */
dashboard-page { dashboard-page {
display: none; display: none; /* toggled by JS → flex */
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
min-width: 0;
overflow-y: auto; overflow-y: auto;
} }
+17 -23
View File
@@ -1,5 +1,12 @@
/* ── Custom element display + layout shell ──────────────────────────────────── */ /* ── Custom element display + layout shell ──────────────────────────────────── */
/* Every page host below is switched to `display: flex` by its component, so its
root <div> is a flex item. `flex-direction: column` is therefore load-bearing
and not cosmetic: in the default `row`, a flex item is sized by its content,
so a page whose root div forgot `width: 100%` renders as a narrow column in
the middle of the workspace. Column direction makes the cross axis stretch,
which fills the width whatever the inner div declares. Never omit it. */
tasks-page { tasks-page {
display: none; /* toggled by JS */ display: none; /* toggled by JS */
flex-direction: column; flex-direction: column;
@@ -10,36 +17,16 @@ tasks-page {
border-right: 1px solid var(--toolbar-border, #e5e9f0); border-right: 1px solid var(--toolbar-border, #e5e9f0);
} }
task-running-section,
task-cron-jobs-section,
task-scheduled-section,
task-history-section {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
min-width: 0;
}
llm-providers-page, llm-providers-page,
models-hub-page { models-hub-page {
display: none; /* toggled by JS */ display: none; /* toggled by JS */
flex-direction: column;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
overflow-y: auto; overflow-y: auto;
border-right: 1px solid var(--toolbar-border, #e5e9f0); border-right: 1px solid var(--toolbar-border, #e5e9f0);
} }
models-llm-section,
models-transcribe-section,
models-image-section {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
min-width: 0;
}
projects-page { projects-page {
display: none; /* toggled by JS */ display: none; /* toggled by JS */
flex-direction: column; flex-direction: column;
@@ -50,8 +37,15 @@ projects-page {
border-right: 1px solid var(--toolbar-border, #e5e9f0); border-right: 1px solid var(--toolbar-border, #e5e9f0);
} }
project-list-section, /* The three section multiplexers (`#tasks`, `#projects`, `#models`) render one
project-board-section { sub-section element at a time as their only child. Sized by descendant
selector rather than by naming each section: the enumeration was the bug
`models-tts-section` was added to the hub and never listed here, so it fell
back to an inline element sized by its content and the page rendered narrow.
A new section now inherits the sizing by existing. */
tasks-page > *,
projects-page > *,
models-hub-page > * {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;