Archived
fix(ui): restore in-pane minimise chevron for session warnings
Re-add the minimise chevron to the expanded session-warnings pane in
ChatView, wired to the existing unified onToggleWarnings (toggle ≡
collapse in the expanded state). The status-bar warning toggle from
a13778c is retained unchanged; both controls share the single
sessionWarningVisibility mutation, so they cannot desync.
- ChatView: onToggleWarnings prop + handleToggleWarnings; chevron
rendered inline via html (no svg re-import), guarded by
onToggleWarnings === undefined.
- PiWebApp: renderChatView <chat-view> passes .onToggleWarnings.
- shared.ts: restore .session-warnings-controls / -collapse / icon CSS.
- ChatView.test.ts: restore the chevron-wiring test against
onToggleWarnings via the session-warnings-collapse marker.
Relay restore-warning-chevron leg 1.
This commit is contained in:
@@ -141,6 +141,24 @@ describe("ChatView session-warning dismiss wiring", () => {
|
|||||||
expect(onDismissWarning).toHaveBeenCalledExactlyOnceWith("anthropicExtraUsage");
|
expect(onDismissWarning).toHaveBeenCalledExactlyOnceWith("anthropicExtraUsage");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Escape hatch: this verifies the minimise chevron's Lit callback wiring in
|
||||||
|
// the node test environment, anchored to its stable semantic class marker.
|
||||||
|
// The chevron is wired to the unified onToggleWarnings (toggle ≡ collapse in
|
||||||
|
// the expanded state), so this also proves the single visibility mutation.
|
||||||
|
it("invokes onToggleWarnings from the visible warning area", () => {
|
||||||
|
const view = withStatus(new ChatView(), warningStatus([
|
||||||
|
{ severity: "warning", message: "subscription auth is active" },
|
||||||
|
]));
|
||||||
|
const onToggleWarnings = vi.fn();
|
||||||
|
view.onToggleWarnings = onToggleWarnings;
|
||||||
|
|
||||||
|
const rendered = renderWarnings(view);
|
||||||
|
if (rendered === null) throw new Error("expected a warnings banner");
|
||||||
|
templateEventHandlerAfterMarker(rendered, "session-warnings-collapse")(new Event("click"));
|
||||||
|
|
||||||
|
expect(onToggleWarnings).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
it("removes the warning area while presentation is collapsed or there are no warnings", () => {
|
it("removes the warning area while presentation is collapsed or there are no warnings", () => {
|
||||||
const view = withStatus(new ChatView(), warningStatus([
|
const view = withStatus(new ChatView(), warningStatus([
|
||||||
{ severity: "warning", message: "subscription auth is active" },
|
{ severity: "warning", message: "subscription auth is active" },
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ export class ChatView extends LitElement {
|
|||||||
@property({ attribute: false }) onDismissNotification?: (notificationId: string) => void;
|
@property({ attribute: false }) onDismissNotification?: (notificationId: string) => void;
|
||||||
@property({ attribute: false }) onDismissAllNotifications?: () => void;
|
@property({ attribute: false }) onDismissAllNotifications?: () => void;
|
||||||
@property({ type: Boolean }) warningsVisible = true;
|
@property({ type: Boolean }) warningsVisible = true;
|
||||||
|
@property({ attribute: false }) onToggleWarnings?: () => void;
|
||||||
@property({ attribute: false }) onLoadMore?: () => void;
|
@property({ attribute: false }) onLoadMore?: () => void;
|
||||||
@query(".chat") private chat?: HTMLDivElement;
|
@query(".chat") private chat?: HTMLDivElement;
|
||||||
@query("dialog.image-zoom") private imageZoomDialog?: HTMLDialogElement;
|
@query("dialog.image-zoom") private imageZoomDialog?: HTMLDialogElement;
|
||||||
@@ -252,6 +253,9 @@ export class ChatView extends LitElement {
|
|||||||
private readonly handleClearServerQueue = (): void => {
|
private readonly handleClearServerQueue = (): void => {
|
||||||
this.onClearServerQueue?.();
|
this.onClearServerQueue?.();
|
||||||
};
|
};
|
||||||
|
private readonly handleToggleWarnings = (): void => {
|
||||||
|
this.onToggleWarnings?.();
|
||||||
|
};
|
||||||
|
|
||||||
override connectedCallback(): void {
|
override connectedCallback(): void {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
@@ -511,6 +515,22 @@ export class ChatView extends LitElement {
|
|||||||
if (!this.warningsVisible || rows.length === 0) return null;
|
if (!this.warningsVisible || rows.length === 0) return null;
|
||||||
return html`
|
return html`
|
||||||
<aside class="session-warnings" role="alert" aria-live="polite">
|
<aside class="session-warnings" role="alert" aria-live="polite">
|
||||||
|
${this.onToggleWarnings === undefined ? null : html`
|
||||||
|
<div class="session-warnings-controls">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="session-warnings-collapse"
|
||||||
|
title="Minimise warnings"
|
||||||
|
aria-label="Minimise warnings"
|
||||||
|
@click=${this.handleToggleWarnings}
|
||||||
|
>
|
||||||
|
<svg class="session-warnings-collapse-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||||||
|
<path d="m18 15-6-6-6 6"></path>
|
||||||
|
</svg>
|
||||||
|
<span>Minimise</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
${rows.map((row) => {
|
${rows.map((row) => {
|
||||||
const dismissId = row.dismissId;
|
const dismissId = row.dismissId;
|
||||||
return html`
|
return html`
|
||||||
|
|||||||
@@ -2125,7 +2125,7 @@ export class PiWebApp extends LitElement {
|
|||||||
|
|
||||||
private renderChatView(state: AppState, session: SessionInfo) {
|
private renderChatView(state: AppState, session: SessionInfo) {
|
||||||
return html`
|
return html`
|
||||||
<chat-view .sessionId=${session.id} .messages=${state.messages} .messageStart=${state.messagePageStart} .messageEnd=${state.messagePageEnd} .messageTotal=${state.messagePageTotal} .hasMore=${state.messagePageStart > 0} .loadingMore=${state.isLoadingEarlierMessages} .isSendingPrompt=${state.sendingPrompts[session.id] === true} .isCompacting=${state.status?.isCompacting === true} .pendingMessageCount=${state.status?.pendingMessageCount ?? 0} .clientQueuedMessages=${state.clientQueuedSessionMessages[session.id] ?? []} .status=${state.status} .activity=${state.activity} .notificationInbox=${selectedNotificationView(state.selectedNotificationInbox)} .canClearServerQueue=${this.canClearServerQueue()} .onClearServerQueue=${this.handleClearServerQueue} .onDismissWarning=${this.handleDismissWarning} .onDismissNotification=${this.handleDismissNotification} .onDismissAllNotifications=${this.handleDismissAllNotifications} .warningsVisible=${!this.sessionWarningVisibility.collapsed} .onLoadMore=${() => this.withChatPrependTransition(() => this.sessions.loadEarlierMessages())}></chat-view>
|
<chat-view .sessionId=${session.id} .messages=${state.messages} .messageStart=${state.messagePageStart} .messageEnd=${state.messagePageEnd} .messageTotal=${state.messagePageTotal} .hasMore=${state.messagePageStart > 0} .loadingMore=${state.isLoadingEarlierMessages} .isSendingPrompt=${state.sendingPrompts[session.id] === true} .isCompacting=${state.status?.isCompacting === true} .pendingMessageCount=${state.status?.pendingMessageCount ?? 0} .clientQueuedMessages=${state.clientQueuedSessionMessages[session.id] ?? []} .status=${state.status} .activity=${state.activity} .notificationInbox=${selectedNotificationView(state.selectedNotificationInbox)} .canClearServerQueue=${this.canClearServerQueue()} .onClearServerQueue=${this.handleClearServerQueue} .onDismissWarning=${this.handleDismissWarning} .onDismissNotification=${this.handleDismissNotification} .onDismissAllNotifications=${this.handleDismissAllNotifications} .warningsVisible=${!this.sessionWarningVisibility.collapsed} .onToggleWarnings=${this.handleToggleWarnings} .onLoadMore=${() => this.withChatPrependTransition(() => this.sessions.loadEarlierMessages())}></chat-view>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -307,6 +307,11 @@ export const chatStyles = css`
|
|||||||
.top-notices { box-sizing: border-box; flex: 0 0 auto; max-height: 40%; min-height: 0; display: flex; flex-direction: column; overflow: hidden; border-bottom: 1px solid var(--pi-border); background: var(--pi-bg-overlay); }
|
.top-notices { box-sizing: border-box; flex: 0 0 auto; max-height: 40%; min-height: 0; display: flex; flex-direction: column; overflow: hidden; border-bottom: 1px solid var(--pi-border); background: var(--pi-bg-overlay); }
|
||||||
.session-warnings { flex: 0 1 auto; display: grid; gap: 8px; max-height: 50%; min-height: 0; overflow-y: auto; box-sizing: border-box; padding: 10px 16px; border-bottom: 1px solid var(--pi-border-muted); }
|
.session-warnings { flex: 0 1 auto; display: grid; gap: 8px; max-height: 50%; min-height: 0; overflow-y: auto; box-sizing: border-box; padding: 10px 16px; border-bottom: 1px solid var(--pi-border-muted); }
|
||||||
.session-warnings:only-child { flex: 1 1 auto; max-height: 100%; border-bottom: 0; }
|
.session-warnings:only-child { flex: 1 1 auto; max-height: 100%; border-bottom: 0; }
|
||||||
|
.session-warnings-controls { display: flex; justify-content: flex-end; }
|
||||||
|
.session-warnings-collapse { display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--pi-border); border-radius: 6px; background: var(--pi-surface); color: var(--pi-muted); padding: 4px 7px; font: 12px system-ui, sans-serif; cursor: pointer; }
|
||||||
|
.session-warnings-collapse:hover, .session-warnings-collapse:focus-visible { color: var(--pi-text-bright); border-color: var(--pi-accent); background: var(--pi-bg-overlay); }
|
||||||
|
.session-warnings-collapse:focus-visible { outline: 1px solid var(--pi-border); outline-offset: 2px; }
|
||||||
|
.session-warnings-collapse-icon { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
|
||||||
.session-warning { position: relative; display: grid; gap: 4px; box-sizing: border-box; padding: 10px 34px 10px 12px; border: 1px solid var(--pi-warning-border); border-radius: 10px; background: var(--pi-warning-surface); color: var(--pi-text); }
|
.session-warning { position: relative; display: grid; gap: 4px; box-sizing: border-box; padding: 10px 34px 10px 12px; border: 1px solid var(--pi-warning-border); border-radius: 10px; background: var(--pi-warning-surface); color: var(--pi-text); }
|
||||||
.session-warning.error { border-color: var(--pi-danger); background: color-mix(in srgb, var(--pi-danger) 12%, var(--pi-surface)); }
|
.session-warning.error { border-color: var(--pi-danger); background: color-mix(in srgb, var(--pi-danger) 12%, var(--pi-surface)); }
|
||||||
.session-warning.info { border-color: var(--pi-accent-border); background: var(--pi-selection-bg); }
|
.session-warning.info { border-color: var(--pi-accent-border); background: var(--pi-selection-bg); }
|
||||||
|
|||||||
Reference in New Issue
Block a user