chore: v2.8.13 — Artifacts reinstall/edit/installs, agent steps in chat, pick a local file to attach, Windows paths as links
- Chat: an Agent step shows its task, status and tool calls and opens its conversation in place; Windows paths (E:\…, C:/…) in replies open as files - Prompt: attach a file on this computer by path (native picker), clearer too-large messages - Artifacts: edit title/description and a skill's files (new snapshot), Reinstall, install history, Hub-only rows, skills installed into ~/.claude/skills and ~/.agents/skills - Files: batch upload of small files in one extract - AI Tools: npm install falls back to --prefix ~/.local when the global prefix is not writable
This commit is contained in:
@@ -73,6 +73,7 @@ import { api } from "../../lib/api";
|
||||
import { eventBus } from "../../lib/eventBus";
|
||||
import { isRemoteDataRefreshMessage } from "../../lib/remoteDataEvents";
|
||||
import { MessageList, promptTokens } from "./MessageList";
|
||||
import { agentNotices, type AgentLinks, type AgentRef } from "./AgentDetails";
|
||||
import { useSpin } from "../../hooks/useSpin";
|
||||
import { useOnScreen, isShown } from "../../hooks/useOnScreen";
|
||||
import { throttle } from "../../lib/throttle";
|
||||
@@ -102,6 +103,9 @@ interface ConversationViewProps {
|
||||
/** Fill the parent as a chat (a workspace's session view): no toolbar, no
|
||||
* page-height cap — the parent sizes it and puts a composer under it. */
|
||||
fill?: boolean;
|
||||
/** A subagent's conversation inside its step: no toolbar, as tall as its
|
||||
* content up to 24rem, then it scrolls. */
|
||||
compact?: boolean;
|
||||
/** Opens a file named in a turn's "Edited N files" card. */
|
||||
onOpenFile?: (path: string) => void;
|
||||
/** The session runs in a pane: show the step in progress. */
|
||||
@@ -127,6 +131,7 @@ function ConversationViewPane({
|
||||
sessionId,
|
||||
initialTranscriptId,
|
||||
fill = false,
|
||||
compact = false,
|
||||
onOpenFile,
|
||||
live = false,
|
||||
follow = 0,
|
||||
@@ -186,6 +191,27 @@ function ConversationViewPane({
|
||||
// Refresh-button spinner state - separate from initial `loading` so the
|
||||
// existing skeleton doesn't blink during a manual refresh.
|
||||
|
||||
// A subagent's own conversation opens inside its step as a small chat: this
|
||||
// same view, on that agent's transcript.
|
||||
const renderAgent = useCallback(
|
||||
(a: AgentRef) => (
|
||||
<ConversationView
|
||||
key={a.agentId}
|
||||
sessionId={sessionId}
|
||||
initialTranscriptId={a.agentId}
|
||||
compact
|
||||
live={live && a.status === "running"}
|
||||
ended={a.status !== "running"}
|
||||
/>
|
||||
),
|
||||
[sessionId, live]
|
||||
);
|
||||
const agentLinks = useMemo<AgentLinks>(() => {
|
||||
const ids = new Map<string, string>();
|
||||
for (const tr of transcripts) if (tr.tool_use_id) ids.set(tr.tool_use_id, tr.id);
|
||||
return { ids, notices: agentNotices(messages), live, render: renderAgent };
|
||||
}, [transcripts, messages, live, renderAgent]);
|
||||
|
||||
// Load available transcript list (also rescanned on a short interval so
|
||||
// newly-spawned subagents appear in the dropdown without a page reload).
|
||||
useEffect(() => {
|
||||
@@ -518,7 +544,7 @@ function ConversationViewPane({
|
||||
<div className={`relative flex flex-col ${fill ? "h-full" : ""}`} style={{ minHeight: 0 }}>
|
||||
{/* Toolbar - always rendered after the initial load so users can
|
||||
refresh even when no messages have streamed yet. */}
|
||||
{!loading && !fill && (
|
||||
{!loading && !fill && !compact && (
|
||||
<div className="flex items-center gap-3 mb-3 flex-shrink-0">
|
||||
{transcripts.length > 1 && (
|
||||
<div className="relative">
|
||||
@@ -566,7 +592,13 @@ function ConversationViewPane({
|
||||
ref={scrollContainerRef}
|
||||
onScroll={handleScroll}
|
||||
className="flex-1 overflow-y-auto [scrollbar-gutter:stable]"
|
||||
style={fill ? { minHeight: 0 } : { maxHeight: "calc(100vh - 320px)", minHeight: 200 }}
|
||||
style={
|
||||
compact
|
||||
? { maxHeight: "24rem" }
|
||||
: fill
|
||||
? { minHeight: 0 }
|
||||
: { maxHeight: "calc(100vh - 320px)", minHeight: 200 }
|
||||
}
|
||||
>
|
||||
{/* A chat keeps its first turn clear of the pane's top-right controls
|
||||
(the outline's file button floats there). */}
|
||||
@@ -589,7 +621,9 @@ function ConversationViewPane({
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center py-12 text-fg-muted text-sm">
|
||||
<div
|
||||
className={`flex items-center justify-center text-fg-muted text-sm ${compact ? "py-3" : "py-12"}`}
|
||||
>
|
||||
{t("conversation.loading")}
|
||||
</div>
|
||||
) : messages.length === 0 && notStarted ? (
|
||||
@@ -624,6 +658,8 @@ function ConversationViewPane({
|
||||
waiting={waiting}
|
||||
onOpenTerminal={onOpenTerminal}
|
||||
stopped={stopped}
|
||||
agentLinks={agentLinks}
|
||||
compact={compact}
|
||||
sessionId={
|
||||
selectedTranscript && selectedTranscript !== "main" ? undefined : sessionId
|
||||
}
|
||||
@@ -653,7 +689,7 @@ function ConversationViewPane({
|
||||
)}
|
||||
|
||||
{/* New messages indicator */}
|
||||
{showNewMsg && !fill && (
|
||||
{showNewMsg && !fill && !compact && (
|
||||
<button
|
||||
onClick={() => {
|
||||
scrollToBottom();
|
||||
|
||||
Reference in New Issue
Block a user