feat(app): feed customization options
This commit is contained in:
@@ -332,14 +332,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-content"]:has([data-component="edit-content"]),
|
||||
[data-slot="collapsible-content"]:has([data-component="write-content"]) {
|
||||
border: 1px solid var(--border-weak-base);
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-component="bash-output"] {
|
||||
width: 100%;
|
||||
border: 1px solid var(--border-weak-base);
|
||||
@@ -399,11 +391,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="collapsible-content"]:has([data-component="edit-content"]) [data-component="edit-content"],
|
||||
[data-slot="collapsible-content"]:has([data-component="write-content"]) [data-component="write-content"] {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
[data-component="edit-trigger"],
|
||||
[data-component="write-trigger"] {
|
||||
display: flex;
|
||||
@@ -492,9 +479,8 @@
|
||||
[data-component="edit-content"] {
|
||||
border-radius: inherit;
|
||||
border-top: 1px solid var(--border-weaker-base);
|
||||
max-height: 420px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow-y: visible;
|
||||
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
@@ -512,9 +498,8 @@
|
||||
[data-component="write-content"] {
|
||||
border-radius: inherit;
|
||||
border-top: 1px solid var(--border-weaker-base);
|
||||
max-height: 240px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow-y: visible;
|
||||
|
||||
[data-component="code"] {
|
||||
padding-bottom: 0 !important;
|
||||
@@ -1212,11 +1197,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="edit-tool"],
|
||||
[data-component="write-tool"],
|
||||
[data-component="apply-patch-tool"] {
|
||||
> [data-component="collapsible"].tool-collapsible {
|
||||
gap: 0px;
|
||||
}
|
||||
|
||||
> [data-component="collapsible"] > [data-slot="collapsible-content"] {
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
> [data-component="collapsible"] > [data-slot="collapsible-trigger"][aria-expanded="true"] {
|
||||
position: sticky;
|
||||
top: var(--sticky-accordion-top, 0px);
|
||||
@@ -1298,7 +1290,7 @@
|
||||
[data-component="apply-patch-file-diff"] {
|
||||
border-radius: inherit;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow-y: visible;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
|
||||
@@ -276,12 +276,24 @@ function renderable(part: PartType, showReasoningSummaries = true) {
|
||||
return !!PART_MAPPING[part.type]
|
||||
}
|
||||
|
||||
function toolDefaultOpen(tool: string, shell = false, edit = false) {
|
||||
if (tool === "bash") return shell
|
||||
if (tool === "edit" || tool === "write" || tool === "apply_patch") return edit
|
||||
}
|
||||
|
||||
function partDefaultOpen(part: PartType, shell = false, edit = false) {
|
||||
if (part.type !== "tool") return
|
||||
return toolDefaultOpen(part.tool, shell, edit)
|
||||
}
|
||||
|
||||
export function AssistantParts(props: {
|
||||
messages: AssistantMessage[]
|
||||
showAssistantCopyPartID?: string | null
|
||||
turnDurationMs?: number
|
||||
working?: boolean
|
||||
showReasoningSummaries?: boolean
|
||||
shellToolDefaultOpen?: boolean
|
||||
editToolDefaultOpen?: boolean
|
||||
}) {
|
||||
const data = useData()
|
||||
const emptyParts: PartType[] = []
|
||||
@@ -372,6 +384,7 @@ export function AssistantParts(props: {
|
||||
message={entry().message}
|
||||
showAssistantCopyPartID={props.showAssistantCopyPartID}
|
||||
turnDurationMs={props.turnDurationMs}
|
||||
defaultOpen={partDefaultOpen(entry().part, props.shellToolDefaultOpen, props.editToolDefaultOpen)}
|
||||
/>
|
||||
)}
|
||||
</Show>
|
||||
@@ -900,6 +913,42 @@ export const ToolRegistry = {
|
||||
render: getTool,
|
||||
}
|
||||
|
||||
function ToolFileAccordion(props: { path: string; actions?: JSX.Element; children: JSX.Element }) {
|
||||
const value = createMemo(() => props.path || "tool-file")
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
multiple
|
||||
data-scope="apply-patch"
|
||||
style={{ "--sticky-accordion-offset": "40px" }}
|
||||
defaultValue={[value()]}
|
||||
>
|
||||
<Accordion.Item value={value()}>
|
||||
<StickyAccordionHeader>
|
||||
<Accordion.Trigger>
|
||||
<div data-slot="apply-patch-trigger-content">
|
||||
<div data-slot="apply-patch-file-info">
|
||||
<FileIcon node={{ path: props.path, type: "file" }} />
|
||||
<div data-slot="apply-patch-file-name-container">
|
||||
<Show when={props.path.includes("/")}>
|
||||
<span data-slot="apply-patch-directory">{`\u202A${getDirectory(props.path)}\u202C`}</span>
|
||||
</Show>
|
||||
<span data-slot="apply-patch-filename">{getFilename(props.path)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div data-slot="apply-patch-trigger-actions">
|
||||
{props.actions}
|
||||
<Icon name="chevron-grabber-vertical" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Trigger>
|
||||
</StickyAccordionHeader>
|
||||
<Accordion.Content>{props.children}</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
)
|
||||
}
|
||||
|
||||
PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
||||
const data = useData()
|
||||
const i18n = useI18n()
|
||||
@@ -1479,57 +1528,67 @@ ToolRegistry.register({
|
||||
const i18n = useI18n()
|
||||
const diffComponent = useDiffComponent()
|
||||
const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
|
||||
const path = createMemo(() => props.metadata?.filediff?.file || props.input.filePath || "")
|
||||
const filename = () => getFilename(props.input.filePath ?? "")
|
||||
const pending = () => props.status === "pending" || props.status === "running"
|
||||
return (
|
||||
<BasicTool
|
||||
{...props}
|
||||
icon="code-lines"
|
||||
defer
|
||||
trigger={
|
||||
<div data-component="edit-trigger">
|
||||
<div data-slot="message-part-title-area">
|
||||
<div data-slot="message-part-title">
|
||||
<span data-slot="message-part-title-text">
|
||||
<Show when={pending()} fallback={i18n.t("ui.messagePart.title.edit")}>
|
||||
<TextShimmer text={i18n.t("ui.messagePart.title.edit")} />
|
||||
<div data-component="edit-tool">
|
||||
<BasicTool
|
||||
{...props}
|
||||
icon="code-lines"
|
||||
defer
|
||||
trigger={
|
||||
<div data-component="edit-trigger">
|
||||
<div data-slot="message-part-title-area">
|
||||
<div data-slot="message-part-title">
|
||||
<span data-slot="message-part-title-text">
|
||||
<Show when={pending()} fallback={i18n.t("ui.messagePart.title.edit")}>
|
||||
<TextShimmer text={i18n.t("ui.messagePart.title.edit")} />
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={!pending()}>
|
||||
<span data-slot="message-part-title-filename">{filename()}</span>
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={!pending()}>
|
||||
<span data-slot="message-part-title-filename">{filename()}</span>
|
||||
</div>
|
||||
<Show when={!pending() && props.input.filePath?.includes("/")}>
|
||||
<div data-slot="message-part-path">
|
||||
<span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<div data-slot="message-part-actions">
|
||||
<Show when={!pending() && props.metadata.filediff}>
|
||||
<DiffChanges changes={props.metadata.filediff} />
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={!pending() && props.input.filePath?.includes("/")}>
|
||||
<div data-slot="message-part-path">
|
||||
<span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<div data-slot="message-part-actions">
|
||||
<Show when={!pending() && props.metadata.filediff}>
|
||||
<DiffChanges changes={props.metadata.filediff} />
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Show when={props.metadata.filediff?.path || props.input.filePath}>
|
||||
<div data-component="edit-content">
|
||||
<Dynamic
|
||||
component={diffComponent}
|
||||
before={{
|
||||
name: props.metadata?.filediff?.file || props.input.filePath,
|
||||
contents: props.metadata?.filediff?.before || props.input.oldString,
|
||||
}}
|
||||
after={{
|
||||
name: props.metadata?.filediff?.file || props.input.filePath,
|
||||
contents: props.metadata?.filediff?.after || props.input.newString,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
<DiagnosticsDisplay diagnostics={diagnostics()} />
|
||||
</BasicTool>
|
||||
}
|
||||
>
|
||||
<Show when={path()}>
|
||||
<ToolFileAccordion
|
||||
path={path()}
|
||||
actions={
|
||||
<Show when={!pending() && props.metadata.filediff}>{(diff) => <DiffChanges changes={diff()} />}</Show>
|
||||
}
|
||||
>
|
||||
<div data-component="edit-content">
|
||||
<Dynamic
|
||||
component={diffComponent}
|
||||
before={{
|
||||
name: props.metadata?.filediff?.file || props.input.filePath,
|
||||
contents: props.metadata?.filediff?.before || props.input.oldString,
|
||||
}}
|
||||
after={{
|
||||
name: props.metadata?.filediff?.file || props.input.filePath,
|
||||
contents: props.metadata?.filediff?.after || props.input.newString,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ToolFileAccordion>
|
||||
</Show>
|
||||
<DiagnosticsDisplay diagnostics={diagnostics()} />
|
||||
</BasicTool>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -1540,51 +1599,56 @@ ToolRegistry.register({
|
||||
const i18n = useI18n()
|
||||
const codeComponent = useCodeComponent()
|
||||
const diagnostics = createMemo(() => getDiagnostics(props.metadata.diagnostics, props.input.filePath))
|
||||
const path = createMemo(() => props.input.filePath || "")
|
||||
const filename = () => getFilename(props.input.filePath ?? "")
|
||||
const pending = () => props.status === "pending" || props.status === "running"
|
||||
return (
|
||||
<BasicTool
|
||||
{...props}
|
||||
icon="code-lines"
|
||||
defer
|
||||
trigger={
|
||||
<div data-component="write-trigger">
|
||||
<div data-slot="message-part-title-area">
|
||||
<div data-slot="message-part-title">
|
||||
<span data-slot="message-part-title-text">
|
||||
<Show when={pending()} fallback={i18n.t("ui.messagePart.title.write")}>
|
||||
<TextShimmer text={i18n.t("ui.messagePart.title.write")} />
|
||||
<div data-component="write-tool">
|
||||
<BasicTool
|
||||
{...props}
|
||||
icon="code-lines"
|
||||
defer
|
||||
trigger={
|
||||
<div data-component="write-trigger">
|
||||
<div data-slot="message-part-title-area">
|
||||
<div data-slot="message-part-title">
|
||||
<span data-slot="message-part-title-text">
|
||||
<Show when={pending()} fallback={i18n.t("ui.messagePart.title.write")}>
|
||||
<TextShimmer text={i18n.t("ui.messagePart.title.write")} />
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={!pending()}>
|
||||
<span data-slot="message-part-title-filename">{filename()}</span>
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={!pending()}>
|
||||
<span data-slot="message-part-title-filename">{filename()}</span>
|
||||
</div>
|
||||
<Show when={!pending() && props.input.filePath?.includes("/")}>
|
||||
<div data-slot="message-part-path">
|
||||
<span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={!pending() && props.input.filePath?.includes("/")}>
|
||||
<div data-slot="message-part-path">
|
||||
<span data-slot="message-part-directory">{getDirectory(props.input.filePath!)}</span>
|
||||
</div>
|
||||
</Show>
|
||||
<div data-slot="message-part-actions">{/* <DiffChanges diff={diff} /> */}</div>
|
||||
</div>
|
||||
<div data-slot="message-part-actions">{/* <DiffChanges diff={diff} /> */}</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Show when={props.input.content}>
|
||||
<div data-component="write-content">
|
||||
<Dynamic
|
||||
component={codeComponent}
|
||||
file={{
|
||||
name: props.input.filePath,
|
||||
contents: props.input.content,
|
||||
cacheKey: checksum(props.input.content),
|
||||
}}
|
||||
overflow="scroll"
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
<DiagnosticsDisplay diagnostics={diagnostics()} />
|
||||
</BasicTool>
|
||||
}
|
||||
>
|
||||
<Show when={props.input.content && path()}>
|
||||
<ToolFileAccordion path={path()}>
|
||||
<div data-component="write-content">
|
||||
<Dynamic
|
||||
component={codeComponent}
|
||||
file={{
|
||||
name: props.input.filePath,
|
||||
contents: props.input.content,
|
||||
cacheKey: checksum(props.input.content),
|
||||
}}
|
||||
overflow="scroll"
|
||||
/>
|
||||
</div>
|
||||
</ToolFileAccordion>
|
||||
</Show>
|
||||
<DiagnosticsDisplay diagnostics={diagnostics()} />
|
||||
</BasicTool>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -1731,45 +1795,73 @@ ToolRegistry.register({
|
||||
}
|
||||
>
|
||||
{(file) => (
|
||||
<BasicTool
|
||||
{...props}
|
||||
icon="code-lines"
|
||||
defer
|
||||
trigger={
|
||||
<div data-component="edit-trigger">
|
||||
<div data-slot="message-part-title-area">
|
||||
<div data-slot="message-part-title">
|
||||
<span data-slot="message-part-title-text">
|
||||
<Show when={pending()} fallback={i18n.t("ui.tool.patch")}>
|
||||
<TextShimmer text={i18n.t("ui.tool.patch")} />
|
||||
<div data-component="apply-patch-tool">
|
||||
<BasicTool
|
||||
{...props}
|
||||
icon="code-lines"
|
||||
defer
|
||||
trigger={
|
||||
<div data-component="edit-trigger">
|
||||
<div data-slot="message-part-title-area">
|
||||
<div data-slot="message-part-title">
|
||||
<span data-slot="message-part-title-text">
|
||||
<Show when={pending()} fallback={i18n.t("ui.tool.patch")}>
|
||||
<TextShimmer text={i18n.t("ui.tool.patch")} />
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={!pending()}>
|
||||
<span data-slot="message-part-title-filename">{getFilename(file().relativePath)}</span>
|
||||
</Show>
|
||||
</span>
|
||||
<Show when={!pending()}>
|
||||
<span data-slot="message-part-title-filename">{getFilename(file().relativePath)}</span>
|
||||
</div>
|
||||
<Show when={!pending() && file().relativePath.includes("/")}>
|
||||
<div data-slot="message-part-path">
|
||||
<span data-slot="message-part-directory">{getDirectory(file().relativePath)}</span>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<div data-slot="message-part-actions">
|
||||
<Show when={!pending()}>
|
||||
<DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} />
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={!pending() && file().relativePath.includes("/")}>
|
||||
<div data-slot="message-part-path">
|
||||
<span data-slot="message-part-directory">{getDirectory(file().relativePath)}</span>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<div data-slot="message-part-actions">
|
||||
<Show when={!pending()}>
|
||||
<DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} />
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<ToolFileAccordion
|
||||
path={file().relativePath}
|
||||
actions={
|
||||
<Switch>
|
||||
<Match when={file().type === "add"}>
|
||||
<span data-slot="apply-patch-change" data-type="added">
|
||||
{i18n.t("ui.patch.action.created")}
|
||||
</span>
|
||||
</Match>
|
||||
<Match when={file().type === "delete"}>
|
||||
<span data-slot="apply-patch-change" data-type="removed">
|
||||
{i18n.t("ui.patch.action.deleted")}
|
||||
</span>
|
||||
</Match>
|
||||
<Match when={file().type === "move"}>
|
||||
<span data-slot="apply-patch-change" data-type="modified">
|
||||
{i18n.t("ui.patch.action.moved")}
|
||||
</span>
|
||||
</Match>
|
||||
<Match when={true}>
|
||||
<DiffChanges changes={{ additions: file().additions, deletions: file().deletions }} />
|
||||
</Match>
|
||||
</Switch>
|
||||
}
|
||||
>
|
||||
<div data-component="apply-patch-file-diff">
|
||||
<Dynamic
|
||||
component={diffComponent}
|
||||
before={{ name: file().filePath, contents: file().before }}
|
||||
after={{ name: file().movePath ?? file().filePath, contents: file().after }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div data-component="edit-content">
|
||||
<Dynamic
|
||||
component={diffComponent}
|
||||
before={{ name: file().filePath, contents: file().before }}
|
||||
after={{ name: file().movePath ?? file().filePath, contents: file().after }}
|
||||
/>
|
||||
</div>
|
||||
</BasicTool>
|
||||
</ToolFileAccordion>
|
||||
</BasicTool>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
)
|
||||
|
||||
@@ -140,6 +140,8 @@ export function SessionTurn(
|
||||
messageID: string
|
||||
lastUserMessageID?: string
|
||||
showReasoningSummaries?: boolean
|
||||
shellToolDefaultOpen?: boolean
|
||||
editToolDefaultOpen?: boolean
|
||||
onUserInteracted?: () => void
|
||||
classes?: {
|
||||
root?: string
|
||||
@@ -369,6 +371,8 @@ export function SessionTurn(
|
||||
turnDurationMs={turnDurationMs()}
|
||||
working={working()}
|
||||
showReasoningSummaries={showReasoningSummaries()}
|
||||
shellToolDefaultOpen={props.shellToolDefaultOpen}
|
||||
editToolDefaultOpen={props.editToolDefaultOpen}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
Reference in New Issue
Block a user