Archived
Add queue and steer prompt actions
This commit is contained in:
@@ -102,7 +102,7 @@ export const api = {
|
||||
status: (sessionId: string) => request(`/api/sessions/${sessionId}/status`, parseSessionStatus),
|
||||
commands: (sessionId: string) => request(`/api/sessions/${sessionId}/commands`, arrayOf(parseSlashCommand)),
|
||||
files: (cwd: string, query: string, kind?: FileSuggestion["kind"]) => request(`/api/files?cwd=${encodeURIComponent(cwd)}&q=${encodeURIComponent(query)}${kind !== undefined ? `&kind=${encodeURIComponent(kind)}` : ""}`, arrayOf(parseFileSuggestion)),
|
||||
prompt: (sessionId: string, text: string) => request(`/api/sessions/${sessionId}/prompt`, parseAccepted, { method: "POST", body: JSON.stringify({ text }) }),
|
||||
prompt: (sessionId: string, text: string, streamingBehavior?: "steer" | "followUp") => request(`/api/sessions/${sessionId}/prompt`, parseAccepted, { method: "POST", body: JSON.stringify(streamingBehavior === undefined ? { text } : { text, streamingBehavior }) }),
|
||||
shell: (sessionId: string, text: string) => request(`/api/sessions/${sessionId}/shell`, parseAccepted, { method: "POST", body: JSON.stringify({ text }) }),
|
||||
runCommand: (sessionId: string, text: string) => request(`/api/sessions/${sessionId}/commands/run`, parseCommandResult, { method: "POST", body: JSON.stringify({ text }) }),
|
||||
respondToCommand: (sessionId: string, requestId: string, value: string) => request(`/api/sessions/${sessionId}/commands/respond`, parseCommandResult, { method: "POST", body: JSON.stringify({ requestId, value }) }),
|
||||
|
||||
@@ -114,9 +114,9 @@ export class PiWebApp extends LitElement {
|
||||
<main>
|
||||
${state.error ? html`<div class="error">${state.error}</div>` : null}
|
||||
${state.selectedSession ? html`
|
||||
<status-bar .status=${state.status} .activity=${state.activity} .workspace=${state.selectedWorkspace}></status-bar>
|
||||
<chat-view .sessionId=${state.selectedSession.id} .messages=${state.messages} .messageStart=${state.messagePageStart} .messageTotal=${state.messagePageTotal} .hasMore=${state.messagePageStart > 0} .loadingMore=${state.isLoadingEarlierMessages} .onLoadMore=${() => this.withChatPrependTransition(() => this.sessions.loadEarlierMessages())}></chat-view>
|
||||
<prompt-editor .sessionId=${state.selectedSession.id} .cwd=${state.selectedWorkspace?.path} .onSend=${(text: string) => this.sessions.send(text)} .onStopSession=${() => this.sessions.stopSession()}></prompt-editor>
|
||||
<prompt-editor .sessionId=${state.selectedSession.id} .cwd=${state.selectedWorkspace?.path} .canSteer=${state.status?.isStreaming === true} .onSend=${(text: string, streamingBehavior?: "steer" | "followUp") => this.sessions.send(text, streamingBehavior)} .onStopSession=${() => this.sessions.stopSession()}></prompt-editor>
|
||||
<status-bar .status=${state.status} .activity=${state.activity} .workspace=${state.selectedWorkspace}></status-bar>
|
||||
${state.commandDialog !== undefined ? html`<command-picker .title=${state.commandDialog.title} .options=${state.commandDialog.options} .onPick=${(value: string) => this.sessions.respondToCommand(state.commandDialog?.requestId ?? "", value)} .onCancel=${() => { this.sessions.cancelCommand(); }}></command-picker>` : null}
|
||||
` : html`<div class="empty">Select or start a session.</div>`}
|
||||
</main>
|
||||
|
||||
@@ -10,7 +10,8 @@ export class PromptEditor extends LitElement {
|
||||
@property({ type: Boolean }) disabled = false;
|
||||
@property() sessionId?: string;
|
||||
@property() cwd?: string;
|
||||
@property({ attribute: false }) onSend?: (text: string) => void;
|
||||
@property({ type: Boolean }) canSteer = false;
|
||||
@property({ attribute: false }) onSend?: (text: string, streamingBehavior?: "steer" | "followUp") => void;
|
||||
@property({ attribute: false }) onStopSession?: () => void;
|
||||
@query("textarea") private textarea?: HTMLTextAreaElement;
|
||||
@state() private draft = "";
|
||||
@@ -49,7 +50,8 @@ export class PromptEditor extends LitElement {
|
||||
${shellMode ? html`<div class="mode-hint">Shell command${inputMode.excludeFromContext ? " · excluded from context" : ""}</div>` : null}
|
||||
<autocomplete-menu .items=${this.completions} .selectedIndex=${this.selectedIndex} .onPick=${(item: CompletionItem) => { this.pick(item); }}></autocomplete-menu>
|
||||
</div>
|
||||
<button ?disabled=${this.disabled} @click=${() => { this.send(); }}>Send</button>
|
||||
<button ?disabled=${this.disabled} title=${this.canSteer ? "Queue after the current response" : "Send message"} @click=${() => { this.send("followUp"); }}>${this.canSteer ? "Queue" : "Send"}</button>
|
||||
${this.canSteer ? html`<button ?disabled=${this.disabled} title="Steer the current response before the next model call" @click=${() => { this.send("steer"); }}>Steer</button>` : null}
|
||||
<button ?disabled=${this.disabled} title="Stop only this Pi session from continuing" @click=${() => this.onStopSession?.()}>Stop session</button>
|
||||
</footer>
|
||||
`;
|
||||
@@ -140,7 +142,7 @@ export class PromptEditor extends LitElement {
|
||||
}
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
this.send();
|
||||
this.send(this.canSteer ? "followUp" : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,13 +152,13 @@ export class PromptEditor extends LitElement {
|
||||
this.completions = [];
|
||||
}
|
||||
|
||||
private send() {
|
||||
private send(streamingBehavior?: "steer" | "followUp") {
|
||||
const text = this.draft.trim();
|
||||
if (text === "" || this.disabled) return;
|
||||
this.draft = "";
|
||||
if (this.sessionId !== undefined && this.sessionId !== "") clearDraft(this.sessionId);
|
||||
this.completions = [];
|
||||
this.onSend?.(text);
|
||||
this.onSend?.(text, this.canSteer ? streamingBehavior : undefined);
|
||||
}
|
||||
|
||||
static override styles = promptEditorStyles;
|
||||
|
||||
@@ -104,7 +104,7 @@ export const formattedTextStyles = css`
|
||||
|
||||
export const statusBarStyles = css`
|
||||
:host { display: block; color: #8b949e; font: 12px system-ui, sans-serif; }
|
||||
.bar { display: flex; gap: 12px; align-items: center; min-width: 0; padding: 7px 12px; border-bottom: 1px solid #30363d; background: #0d1117; white-space: nowrap; overflow: hidden; }
|
||||
.bar { display: flex; gap: 12px; align-items: center; min-width: 0; padding: 7px 12px; border-top: 1px solid #30363d; background: #0d1117; white-space: nowrap; overflow: hidden; }
|
||||
span { overflow: hidden; text-overflow: ellipsis; }
|
||||
.bar > span:first-child { flex: 1 1 auto; min-width: 80px; }
|
||||
.activity { display: inline-flex; align-items: center; gap: 6px; color: #8b949e; }
|
||||
|
||||
@@ -81,7 +81,7 @@ export class SessionController {
|
||||
}
|
||||
}
|
||||
|
||||
async send(text: string) {
|
||||
async send(text: string, streamingBehavior?: "steer" | "followUp") {
|
||||
const trimmed = text.trim();
|
||||
if (trimmed.startsWith("/")) return this.runCommand(text);
|
||||
if (isShellInput(text)) return this.runShell(text);
|
||||
@@ -89,7 +89,7 @@ export class SessionController {
|
||||
if (!session) return;
|
||||
this.setState({ messages: [...this.getState().messages, textMessage("user", text)] });
|
||||
try {
|
||||
await api.prompt(session.id, text);
|
||||
await api.prompt(session.id, text, streamingBehavior);
|
||||
} catch (error) {
|
||||
this.setState({ error: String(error) });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user