From b207ed2b7b4080c3c6e0b2bc8430abcb4a894cad Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 25 Sep 2025 07:04:36 -0500 Subject: wip: better desktop file status state and timeline --- packages/app/src/components/code.tsx | 2 +- packages/app/src/components/markdown.tsx | 3 +- packages/app/src/components/session-timeline.tsx | 324 +++++++++++++++-------- 3 files changed, 215 insertions(+), 114 deletions(-) (limited to 'packages/app/src/components') diff --git a/packages/app/src/components/code.tsx b/packages/app/src/components/code.tsx index e4121d121..63f527c46 100644 --- a/packages/app/src/components/code.tsx +++ b/packages/app/src/components/code.tsx @@ -435,7 +435,7 @@ function transformerUnifiedDiff(): ShikiTransformer { out.push(s) } - return out.join("\n") + return out.join("\n").trimEnd() }, code(node) { if (isDiff) this.addClassToHast(node, "code-diff") diff --git a/packages/app/src/components/markdown.tsx b/packages/app/src/components/markdown.tsx index ab6232ecf..a60fad149 100644 --- a/packages/app/src/components/markdown.tsx +++ b/packages/app/src/components/markdown.tsx @@ -6,8 +6,7 @@ function strip(text: string): string { const match = text.match(wrappedRe) return match ? match[2] : text } - -export default function Markdown(props: { text: string; class?: string }) { +export function Markdown(props: { text: string; class?: string }) { const marked = useMarked() const [html] = createResource( () => strip(props.text), diff --git a/packages/app/src/components/session-timeline.tsx b/packages/app/src/components/session-timeline.tsx index ac8519a9c..99fa5fac8 100644 --- a/packages/app/src/components/session-timeline.tsx +++ b/packages/app/src/components/session-timeline.tsx @@ -1,5 +1,5 @@ import { useLocal, useSync } from "@/context" -import { Collapsible, Icon, type IconProps } from "@/ui" +import { Collapsible, Icon } from "@/ui" import type { Part, ToolPart } from "@opencode-ai/sdk" import { DateTime } from "luxon" import { @@ -13,58 +13,14 @@ import { type ParentProps, createEffect, createMemo, + Show, } from "solid-js" import { getFilename } from "@/utils" -import Markdown from "./markdown" +import { Markdown } from "./markdown" import { Code } from "./code" import { createElementSize } from "@solid-primitives/resize-observer" import { createScrollPosition } from "@solid-primitives/scroll" -function TimelineIcon(props: { name: IconProps["name"]; class?: string }) { - return ( -
- -
- ) -} - -function CollapsibleTimelineIcon(props: { name: IconProps["name"]; class?: string }) { - return ( - <> - - - - - ) -} - -function ToolIcon(props: { part: ToolPart }) { - return ( - }> - - - - - - - - - - - ) -} - function Part(props: ParentProps & ComponentProps<"div">) { const [local, others] = splitProps(props, ["class", "classList", "children"]) return ( @@ -97,9 +53,13 @@ function CollapsiblePart(props: { title: ParentProps["children"] } & ParentProps } function ReadToolPart(props: { part: ToolPart }) { + const sync = useSync() const local = useLocal() return ( + + Reading file... + {(state) => { const path = state().input["filePath"] as string @@ -110,13 +70,27 @@ function ReadToolPart(props: { part: ToolPart }) { ) }} + + {(state) => ( +
+ + Read {getFilename(state().input["filePath"] as string)} + +
{sync.sanitize(state().error)}
+
+ )} +
) } function EditToolPart(props: { part: ToolPart }) { + const sync = useSync() return ( + + Preparing edit... + {(state) => ( )} + + {(state) => ( + + Edit {getFilename(state().input["filePath"] as string)} + + } + > +
{sync.sanitize(state().error)}
+
+ )} +
) } function WriteToolPart(props: { part: ToolPart }) { + const sync = useSync() return ( + + Preparing write... + {(state) => ( )} + + {(state) => ( +
+ + Write {getFilename(state().input["filePath"] as string)} + +
{sync.sanitize(state().error)}
+
+ )} +
) } -function ToolPart(props: { part: ToolPart }) { +function BashToolPart(props: { part: ToolPart }) { + const sync = useSync() return ( - - {props.part.type}:{props.part.tool} - - } - > - -
- -
+ + + Writing shell command... - -
- -
+ + {(state) => ( + + Run command: {state().input["command"]} + + } + > + + + )} - -
- -
+ + {(state) => ( + + Shell {state().input["command"]} + + } + > +
{sync.sanitize(state().error)}
+
+ )}
) } +function ToolPart(props: { part: ToolPart }) { + // read + // edit + // write + // bash + // ls + // glob + // grep + // todowrite + // todoread + // webfetch + // websearch + // patch + // task + return ( +
+ + {props.part.type}:{props.part.tool} + + } + > + + + + + + + + + + + + + +
+ ) +} + export default function SessionTimeline(props: { session: string; class?: string }) { const sync = useSync() const [scrollElement, setScrollElement] = createSignal(undefined) @@ -196,6 +247,7 @@ export default function SessionTimeline(props: { session: string; class?: string const scroll = createScrollPosition(scrollElement) onMount(() => sync.session.sync(props.session)) + const session = createMemo(() => sync.session.get(props.session)) const messages = createMemo(() => sync.data.message[props.session] ?? []) const working = createMemo(() => { const last = messages()[messages().length - 1] @@ -285,60 +337,33 @@ export default function SessionTimeline(props: { session: string; class?: string
- - {(message) => ( -
    +
      + + {(message) => ( {(part) => ( -
    • -
      -
      -
      - -
      -
      - } - > - - - - - - - - - - - - - - {(part) => } -
      +
    • {part.type}
}> {(part) => ( -
+

{part().text}

-

12:07pm · adam

+

+ {DateTime.fromMillis(message.time.created).toRelative()} ·{" "} + {sync.data.config.username ?? "user"} +

- + )} @@ -347,9 +372,11 @@ export default function SessionTimeline(props: { session: string; class?: string {(part) => ( - Thought for {duration(part())}s - + Thinking}> + + Thought for {duration(part())}s + + } > @@ -361,9 +388,84 @@ export default function SessionTimeline(props: { session: string; class?: string )} - - )} - + )} + + + + + +
+ + Raw Session Data + +
+
+ +
    +
  • + + +
    + + session + +
    +
    + + + +
    +
  • + + {(message) => ( + <> +
  • + + +
    + + {message.role === "user" ? "user" : "assistant"} + +
    +
    + + + +
    +
  • + + {(part) => ( +
  • + + +
    + + {part.type} + +
    +
    + + + +
    +
  • + )} +
    + + )} +
    +
+
+
+
) } -- cgit v1.2.3