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,14 @@ import { html, nothing } from 'lit';
|
||||
import { LightElement } from '../../lib/base.js';
|
||||
import { t } from '../../lib/i18n.js';
|
||||
import { fileWatcher } from '../../lib/file-watcher.js';
|
||||
import { setSlice, clearSlice } from '../../lib/view-context.js';
|
||||
|
||||
/// The view-context slice this component owns (see `lib/view-context.js`).
|
||||
/// One explorer is on screen at a time — `#files` renders `nothing` when it is
|
||||
/// not the open page, and the project board is a page of its own — so a single
|
||||
/// key is right and two explorers cannot both claim it.
|
||||
const VIEW_SLICE = 'path';
|
||||
const VIEW_LABEL = 'Open folder';
|
||||
|
||||
/// A live file explorer over one subtree of the caller's namespace.
|
||||
///
|
||||
@@ -29,6 +37,18 @@ import { fileWatcher } from '../../lib/file-watcher.js';
|
||||
/// fires only for a click, never for a `rel` the host itself set — so echoing
|
||||
/// the event back as a property is a no-op, and a host that ignores the event
|
||||
/// entirely (`project-board.js`) still gets a working explorer.
|
||||
///
|
||||
/// **It also tells the assistant which folder is open.** The current directory
|
||||
/// is published as the `path` view-context slice, in agent-path vocabulary — the
|
||||
/// same string the fs-tools take — so "what is in this folder?" needs no
|
||||
/// explaining. That it works inside the project board as well as on `#files` is
|
||||
/// the whole reason the store is push-based: nobody threads a handle down here.
|
||||
/// The slice is cleared on `disconnectedCallback`, which is when both hosts drop
|
||||
/// the element (`#files` renders `nothing` while it is not the open page, and so
|
||||
/// does the projects page). The board hiding the explorer behind its *Sharing*
|
||||
/// tab leaves the slice standing, deliberately: the project's folder is still
|
||||
/// the folder the page is about, and which tab is showing is the board's own
|
||||
/// slice to publish.
|
||||
export class FileExplorer extends LightElement {
|
||||
static properties = {
|
||||
/// Agent path of the subtree to browse (`~`, `shared/x`, `projects/a/b`,
|
||||
@@ -80,6 +100,7 @@ export class FileExplorer extends LightElement {
|
||||
disconnectedCallback() {
|
||||
this._unwatch?.();
|
||||
clearTimeout(this._reloadTimer);
|
||||
clearSlice(VIEW_SLICE);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
@@ -87,11 +108,20 @@ export class FileExplorer extends LightElement {
|
||||
return this._rel ? `${this.root}/${this._rel}` : this.root;
|
||||
}
|
||||
|
||||
/// Say which folder is open, before the listing lands: the path is known the
|
||||
/// moment we navigate, and a message sent while the fetch is in flight is
|
||||
/// still a message about *this* folder.
|
||||
_publishViewContext() {
|
||||
if (!this.root) return;
|
||||
setSlice(VIEW_SLICE, [{ label: VIEW_LABEL, value: this._dirPath() }]);
|
||||
}
|
||||
|
||||
async _open(rel) {
|
||||
this._unwatch?.();
|
||||
this._unwatch = null;
|
||||
this._rel = rel;
|
||||
this._error = null;
|
||||
this._publishViewContext();
|
||||
await this._load();
|
||||
// Live updates for the open directory (best-effort: a dead watcher just
|
||||
// means manual refresh; auto-reconnect + re-subscribe are handled inside).
|
||||
|
||||
Reference in New Issue
Block a user