Release 0.3.0 #5
@@ -75,6 +75,9 @@ release PR may merge — and a section is closed at the commit that bumps it.
|
||||
|
||||
### 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
|
||||
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
|
||||
|
||||
+15
-1
@@ -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 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 |
|
||||
| ---- | ------- | ----- |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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-transcribe.js` | `<models-transcribe-section>` | Transcription 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 |
|
||||
| `shared/settings-page.js` | `<settings-page>` | Mobile settings: per-user avatar, locale picker (`I18nMixin`), profile/preferences |
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* Agent Inbox page — desktop layout. Card styles live in inbox-cards.css. */
|
||||
|
||||
agent-inbox-page {
|
||||
display: none;
|
||||
display: none; /* toggled by JS → flex */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
|
||||
+2
-1
@@ -1,9 +1,10 @@
|
||||
/* ── Agents page layout ───────────────────────────────────────────────────── */
|
||||
|
||||
agents-page {
|
||||
display: none;
|
||||
display: none; /* toggled by JS → flex */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
background: var(--bs-body-bg);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
approval-groups-page {
|
||||
display: none; /* toggled by JS */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
approval-rules-page {
|
||||
display: none; /* toggled by JS */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
config-page {
|
||||
display: none;
|
||||
display: none; /* toggled by JS → flex */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
background: var(--bs-body-bg);
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,9 +1,10 @@
|
||||
/* ── Dashboard page ────────────────────────────────────────────────────────── */
|
||||
|
||||
dashboard-page {
|
||||
display: none;
|
||||
display: none; /* toggled by JS → flex */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
||||
+17
-23
@@ -1,5 +1,12 @@
|
||||
/* ── 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 {
|
||||
display: none; /* toggled by JS */
|
||||
flex-direction: column;
|
||||
@@ -10,36 +17,16 @@ tasks-page {
|
||||
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,
|
||||
models-hub-page {
|
||||
display: none; /* toggled by JS */
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
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 {
|
||||
display: none; /* toggled by JS */
|
||||
flex-direction: column;
|
||||
@@ -50,8 +37,15 @@ projects-page {
|
||||
border-right: 1px solid var(--toolbar-border, #e5e9f0);
|
||||
}
|
||||
|
||||
project-list-section,
|
||||
project-board-section {
|
||||
/* The three section multiplexers (`#tasks`, `#projects`, `#models`) render one
|
||||
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;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
Reference in New Issue
Block a user