wip: desktop work

This commit is contained in:
Adam
2025-10-29 14:08:52 -05:00
parent a9cae7b335
commit cdeb82e9ca
3 changed files with 112 additions and 95 deletions

View File

@@ -670,98 +670,6 @@ export default function Page() {
<Message message={message} parts={parts()} /> <Message message={message} parts={parts()} />
</div> </div>
</Show> </Show>
{/* Response */}
<div class="w-full flex flex-col gap-2">
<Collapsible variant="ghost" open={expanded()} onOpenChange={setExpanded}>
<Collapsible.Trigger class="text-text-weak hover:text-text-strong">
<div class="flex items-center gap-1 self-stretch">
<h2 class="text-12-medium">
<Switch>
<Match when={expanded()}>Hide steps</Match>
<Match when={!expanded()}>Show steps</Match>
</Switch>
</h2>
<Collapsible.Arrow />
</div>
</Collapsible.Trigger>
<Collapsible.Content>
<div class="w-full flex flex-col items-start self-stretch gap-8">
<For each={assistantMessages()}>
{(assistantMessage) => {
const parts = createMemo(() => sync.data.part[assistantMessage.id])
return <Message message={assistantMessage} parts={parts()} />
}}
</For>
</div>
</Collapsible.Content>
</Collapsible>
<Show when={working() && !expanded()}>
{(_) => {
const lastMessageWithText = createMemo(() =>
assistantMessages().findLast((m) => {
const parts = sync.data.part[m.id]
return parts?.find((p) => p.type === "text")
}),
)
const lastMessageWithReasoning = createMemo(() =>
assistantMessages().findLast((m) => {
const parts = sync.data.part[m.id]
return parts?.find((p) => p.type === "reasoning")
}),
)
const lastMessageWithTool = createMemo(() =>
assistantMessages().findLast((m) => {
const parts = sync.data.part[m.id]
return parts?.find(
(p) => p.type === "tool" && p.state.status === "completed",
)
}),
)
return (
<div class="w-full flex flex-col gap-2">
<Switch>
<Match when={lastMessageWithText()}>
{(last) => {
const lastTextPart = createMemo(() =>
sync.data.part[last().id].findLast((p) => p.type === "text"),
)
return (
<Part message={last()} part={lastTextPart()!} hideDetails />
)
}}
</Match>
<Match when={lastMessageWithReasoning()}>
{(last) => {
const lastReasoningPart = createMemo(() =>
sync.data.part[last().id].findLast(
(p) => p.type === "reasoning",
),
)
return (
<Part
message={last()}
part={lastReasoningPart()!}
hideDetails
/>
)
}}
</Match>
</Switch>
<Show when={lastMessageWithTool()}>
{(last) => {
const lastToolPart = createMemo(() =>
sync.data.part[last().id].findLast(
(p) => p.type === "tool" && p.state.status === "completed",
),
)
return <Part message={last()} part={lastToolPart()!} hideDetails />
}}
</Show>
</div>
)
}}
</Show>
</div>
{/* Summary */} {/* Summary */}
<Show when={!working()}> <Show when={!working()}>
<div class="w-full flex flex-col gap-6 items-start self-stretch"> <div class="w-full flex flex-col gap-6 items-start self-stretch">
@@ -817,6 +725,48 @@ export default function Page() {
</Accordion> </Accordion>
</div> </div>
</Show> </Show>
{/* Response */}
<div class="w-full">
<Switch>
<Match when={working()}>
<div class="w-full flex flex-col-reverse items-start self-stretch gap-6 max-h-30 overflow-y-auto no-scrollbar pointer-events-none mask-alpha mask-y-from-66% mask-y-from-background-base mask-y-to-transparent">
<For each={assistantMessages()?.toReversed()}>
{(assistantMessage) => {
const parts = createMemo(() => sync.data.part[assistantMessage.id])
return <Message message={assistantMessage} parts={parts()} />
}}
</For>
</div>
</Match>
<Match when={!working()}>
<Collapsible variant="ghost" open={expanded()} onOpenChange={setExpanded}>
<Collapsible.Trigger class="text-text-weak hover:text-text-strong">
<div class="flex items-center gap-1 self-stretch">
<div class="text-12-medium">
<Switch>
<Match when={expanded()}>Hide steps</Match>
<Match when={!expanded()}>Show steps</Match>
</Switch>
</div>
<Collapsible.Arrow />
</div>
</Collapsible.Trigger>
<Collapsible.Content>
<div class="w-full flex flex-col-reverse items-start self-stretch gap-8">
<For each={assistantMessages()}>
{(assistantMessage) => {
const parts = createMemo(
() => sync.data.part[assistantMessage.id],
)
return <Message message={assistantMessage} parts={parts()} />
}}
</For>
</div>
</Collapsible.Content>
</Collapsible>
</Match>
</Switch>
</div>
</div> </div>
) )
}} }}

View File

@@ -54,18 +54,20 @@ export function Diff<T>(props: DiffProps<T>) {
// When ready to render, simply call .render with old/new file, optional // When ready to render, simply call .render with old/new file, optional
// annotations and a container element to hold the diff // annotations and a container element to hold the diff
createEffect(() => { createEffect(() => {
// @ts-expect-error
const instance = new FileDiff<T>({ const instance = new FileDiff<T>({
theme: "pierre-light", // theme: "pierre-light",
// theme: "pierre-light",
// Or can also provide a 'themes' prop, which allows the code to adapt // Or can also provide a 'themes' prop, which allows the code to adapt
// to your OS light or dark theme // to your OS light or dark theme
// themes: { dark: 'pierre-night', light: 'pierre-light' }, themes: { dark: "pierre-dark", light: "pierre-light" },
// When using the 'themes' prop, 'themeType' allows you to force 'dark' // When using the 'themes' prop, 'themeType' allows you to force 'dark'
// or 'light' theme, or inherit from the OS ('system') theme. // or 'light' theme, or inherit from the OS ('system') theme.
themeType: "system", themeType: "system",
// Disable the line numbers for your diffs, generally not recommended // Disable the line numbers for your diffs, generally not recommended
disableLineNumbers: false, disableLineNumbers: false,
// Whether code should 'wrap' with long lines or 'scroll'. // Whether code should 'wrap' with long lines or 'scroll'.
overflow: "scroll", overflow: "wrap",
// Normally you shouldn't need this prop, but if you don't provide a // Normally you shouldn't need this prop, but if you don't provide a
// valid filename or your file doesn't have an extension you may want to // valid filename or your file doesn't have an extension you may want to
// override the automatic detection. You can specify that language here: // override the automatic detection. You can specify that language here:

View File

@@ -48,6 +48,71 @@
border-width: 0; border-width: 0;
} }
.scroller {
/* --fade-height: 1.5rem; */
/**/
/* --mask-top: linear-gradient(to bottom, transparent, black var(--fade-height)); */
/* --mask-bottom: linear-gradient(to top, transparent, black var(--fade-height)); */
/**/
/* mask-image: var(--mask-top), var(--mask-bottom); */
/* mask-repeat: no-repeat; */
/* mask-size: 100% var(--fade-height); */
animation: scroll-fade linear;
animation-timeline: scroll(self);
}
/* Define the keyframes for the mask.
These percentages now map to scroll positions:
0% = Scrolled to the top
100% = Scrolled to the bottom
*/
@keyframes scroll-fade {
/* At the very top (0% scroll) */
0% {
mask-image: linear-gradient(
to bottom,
black 90%,
/* Opaque, but start fade to bottom */ transparent 100%
);
}
/* A small amount scrolled (e.g., 5%)
This is where the top fade should be fully visible.
*/
5% {
mask-image: linear-gradient(
to bottom,
transparent 0%,
black 10%,
/* Fade-in top */ black 90%,
/* Fade-out bottom */ transparent 100%
);
}
/* Nearing the bottom (e.g., 95%)
The bottom fade should start disappearing.
*/
95% {
mask-image: linear-gradient(
to bottom,
transparent 0%,
black 10%,
/* Fade-in top */ black 90%,
/* Fade-out bottom */ transparent 100%
);
}
/* At the very bottom (100% scroll) */
100% {
mask-image: linear-gradient(
to bottom,
transparent 0%,
black 10% /* Opaque, but start fade from top */
);
}
}
.truncate-start { .truncate-start {
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;