feat(chat): view context — tell the assistant what you're looking at
Nightly Build / build (push) Successful in 7m51s
Nightly Build / build (push) Successful in 7m51s
An eye next to the paperclip shares what the user has open with their next
message: the page, the folder being browsed, the file open in the viewer and
any highlighted passage (line numbers where a source view exists), plus which
entity a detail page is about. The bag is client-authored {label, value} pairs
in English — the backend only clamps (chars, never bytes), neutralizes the
harness tag and renders one <system-extra> block per message, deduped
consecutively so it appears exactly when the view changed. On by default,
per-device toggle, hover/tap to preview, a chip on every sent message;
docs/view-context.md for users, an updated harness.md clause for the model.
This commit is contained in:
@@ -2,6 +2,7 @@ import { html, nothing } from 'lit';
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||
import { LightElement } from '../lib/base.js';
|
||||
import { t } from '../lib/i18n.js';
|
||||
import { setSlice, clearSlice } from '../lib/view-context.js';
|
||||
import { connectorIconUrl } from './shared/connector-common.js';
|
||||
|
||||
// Users admin — the list at `#users`, one user's page at `#users/{id}`.
|
||||
@@ -29,6 +30,12 @@ function avatarColor(name) {
|
||||
return `hsl(${h % 360}, 55%, 52%)`;
|
||||
}
|
||||
|
||||
// The view-context slice this page owns (see `lib/view-context.js`). Qualified
|
||||
// per page, like every `entity` contributor: these pages stay in the DOM while
|
||||
// hidden, and a shared key would let a page that hides *after* another one
|
||||
// shows wipe the fresh slice (listener order on `llm-page-change`).
|
||||
const VIEW_SLICE = 'entity@users';
|
||||
|
||||
export class UsersPage extends LightElement {
|
||||
|
||||
static get properties() {
|
||||
@@ -84,6 +91,15 @@ export class UsersPage extends LightElement {
|
||||
this._connSaved = false;
|
||||
this._plugSaved = false;
|
||||
this._trgSaved = false;
|
||||
clearSlice(VIEW_SLICE);
|
||||
}
|
||||
|
||||
// The entity slice: which person's page is open — the username, and never a
|
||||
// profile field (a detail page says *which* object, not what it contains).
|
||||
_publishViewContext() {
|
||||
const u = this._user;
|
||||
setSlice(VIEW_SLICE,
|
||||
this._view === 'user' && u ? [{ label: 'Open user', value: u.username }] : null);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@@ -94,6 +110,7 @@ export class UsersPage extends LightElement {
|
||||
this._open = e.detail.page === 'users';
|
||||
this.style.display = this._open ? 'flex' : 'none';
|
||||
if (this._open) { this._syncViewFromHash(); this._load(); }
|
||||
else clearSlice(VIEW_SLICE);
|
||||
});
|
||||
window.addEventListener('hashchange', () => {
|
||||
if (this._open) this._syncViewFromHash();
|
||||
@@ -102,6 +119,7 @@ export class UsersPage extends LightElement {
|
||||
|
||||
disconnectedCallback() {
|
||||
window.removeEventListener('locale-changed', this.__onLocaleChanged);
|
||||
clearSlice(VIEW_SLICE);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
@@ -117,6 +135,7 @@ export class UsersPage extends LightElement {
|
||||
this._users = await uRes.json();
|
||||
this._roles = await rRes.json();
|
||||
if (this._view === 'user') this._enterDetail();
|
||||
this._publishViewContext();
|
||||
} catch (e) {
|
||||
this._error = e.message;
|
||||
}
|
||||
@@ -137,6 +156,7 @@ export class UsersPage extends LightElement {
|
||||
this._userId = id;
|
||||
if (this._users) this._enterDetail();
|
||||
}
|
||||
this._publishViewContext();
|
||||
}
|
||||
|
||||
get _user() { return (this._users ?? []).find(u => u.id === this._userId) ?? null; }
|
||||
|
||||
Reference in New Issue
Block a user