mirror of
https://github.com/sst/opencode.git
synced 2025-08-30 17:57:25 +00:00
refactor share
This commit is contained in:
parent
9d7c5efb9b
commit
041a080a13
2 changed files with 153 additions and 75 deletions
|
@ -54,7 +54,7 @@ function getPartTitle(role: string, type: string): string | undefined {
|
||||||
: role === "user"
|
: role === "user"
|
||||||
? undefined
|
? undefined
|
||||||
: type === "text"
|
: type === "text"
|
||||||
? "AI"
|
? undefined
|
||||||
: type
|
: type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,36 +69,38 @@ function getStatusText(status: [Status, string?]): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function TextPart(props: { text: string, highlight?: boolean }) {
|
function TextPart(
|
||||||
|
props: { text: string, expand?: boolean, highlight?: boolean }
|
||||||
|
) {
|
||||||
const [expanded, setExpanded] = createSignal(false)
|
const [expanded, setExpanded] = createSignal(false)
|
||||||
const [overflowed, setOverflowed] = createSignal(false);
|
const [overflowed, setOverflowed] = createSignal(false)
|
||||||
let preEl: HTMLPreElement | undefined;
|
let preEl: HTMLPreElement | undefined
|
||||||
|
|
||||||
const checkOverflow = () => {
|
function checkOverflow() {
|
||||||
if (preEl) {
|
if (preEl && !props.expand) {
|
||||||
setOverflowed(preEl.scrollHeight > preEl.clientHeight + 1);
|
setOverflowed(preEl.scrollHeight > preEl.clientHeight + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
checkOverflow();
|
checkOverflow()
|
||||||
window.addEventListener('resize', checkOverflow);
|
window.addEventListener("resize", checkOverflow)
|
||||||
});
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
props.text;
|
props.text
|
||||||
setTimeout(checkOverflow, 0);
|
setTimeout(checkOverflow, 0)
|
||||||
});
|
})
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
window.removeEventListener('resize', checkOverflow);
|
window.removeEventListener("resize", checkOverflow)
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-element-message-text
|
data-element-message-text
|
||||||
data-expanded={expanded()}
|
|
||||||
data-highlight={props.highlight}
|
data-highlight={props.highlight}
|
||||||
|
data-expanded={expanded() || props.expand === true}
|
||||||
>
|
>
|
||||||
<pre ref={el => (preEl = el)}>{props.text}</pre>
|
<pre ref={el => (preEl = el)}>{props.text}</pre>
|
||||||
{overflowed() &&
|
{overflowed() &&
|
||||||
|
@ -114,6 +116,16 @@ function TextPart(props: { text: string, highlight?: boolean }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function PartFooter(props: { time: number }) {
|
||||||
|
return (
|
||||||
|
<span title={
|
||||||
|
DateTime.fromMillis(props.time).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)
|
||||||
|
}>
|
||||||
|
{DateTime.fromMillis(props.time).toLocaleString(DateTime.TIME_WITH_SECONDS)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function Share(props: { api: string }) {
|
export default function Share(props: { api: string }) {
|
||||||
let params = new URLSearchParams(document.location.search)
|
let params = new URLSearchParams(document.location.search)
|
||||||
const sessionId = params.get("id")
|
const sessionId = params.get("id")
|
||||||
|
@ -224,16 +236,6 @@ export default function Share(props: { api: string }) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
function renderTime(time: number) {
|
|
||||||
return (
|
|
||||||
<span title={
|
|
||||||
DateTime.fromMillis(time).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)
|
|
||||||
}>
|
|
||||||
{DateTime.fromMillis(time).toLocaleString(DateTime.TIME_WITH_SECONDS)}
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const metrics = createMemo(() => {
|
const metrics = createMemo(() => {
|
||||||
const result = {
|
const result = {
|
||||||
cost: 0,
|
cost: 0,
|
||||||
|
@ -268,8 +270,8 @@ export default function Share(props: { api: string }) {
|
||||||
<ul data-section="stats">
|
<ul data-section="stats">
|
||||||
<li>
|
<li>
|
||||||
<span data-element-label>Cost</span>
|
<span data-element-label>Cost</span>
|
||||||
{metrics().cost ?
|
{metrics().cost !== undefined ?
|
||||||
<span>{metrics().cost}</span>
|
<span>${metrics().cost.toFixed(2)}</span>
|
||||||
:
|
:
|
||||||
<span data-placeholder>—</span>
|
<span data-placeholder>—</span>
|
||||||
}
|
}
|
||||||
|
@ -324,20 +326,103 @@ export default function Share(props: { api: string }) {
|
||||||
>
|
>
|
||||||
<div class={styles.parts}>
|
<div class={styles.parts}>
|
||||||
<For each={messages()}>
|
<For each={messages()}>
|
||||||
{(msg) => (
|
{(msg, msgIndex) => (
|
||||||
<For each={msg.parts}>
|
<For each={msg.parts}>
|
||||||
{(part) => (
|
{(part, partIndex) => {
|
||||||
|
const isLastPart = createMemo(() =>
|
||||||
|
(messages().length === msgIndex() + 1)
|
||||||
|
&& (msg.parts.length === partIndex() + 1)
|
||||||
|
)
|
||||||
|
const time = msg.metadata?.time.completed
|
||||||
|
|| msg.metadata?.time.created
|
||||||
|
|| 0
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
data-section="part"
|
data-section="part"
|
||||||
data-message-role={msg.role}
|
|
||||||
data-part-type={part.type}
|
data-part-type={part.type}
|
||||||
|
data-message-role={msg.role}
|
||||||
>
|
>
|
||||||
|
<Switch>
|
||||||
|
{ /* User text */}
|
||||||
|
<Match when={
|
||||||
|
msg.role === "user" && part.type === "text" && part
|
||||||
|
}>
|
||||||
|
{part =>
|
||||||
|
<>
|
||||||
|
<div data-section="decoration">
|
||||||
|
<div>
|
||||||
|
<IconUserCircle width={18} height={18} />
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div data-section="content">
|
||||||
|
<TextPart
|
||||||
|
highlight
|
||||||
|
text={part().text}
|
||||||
|
expand={isLastPart()}
|
||||||
|
/>
|
||||||
|
<PartFooter time={time} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</Match>
|
||||||
|
{ /* AI text */}
|
||||||
|
<Match when={
|
||||||
|
msg.role === "assistant"
|
||||||
|
&& part.type === "text"
|
||||||
|
&& part
|
||||||
|
}>
|
||||||
|
{part =>
|
||||||
|
<>
|
||||||
|
<div data-section="decoration">
|
||||||
|
<div><IconSparkles width={18} height={18} /></div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div data-section="content">
|
||||||
|
<TextPart
|
||||||
|
text={part().text}
|
||||||
|
expand={isLastPart()}
|
||||||
|
/>
|
||||||
|
<PartFooter time={time} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</Match>
|
||||||
|
{ /* System text */}
|
||||||
|
<Match when={
|
||||||
|
msg.role === "system"
|
||||||
|
&& part.type === "text"
|
||||||
|
&& part
|
||||||
|
}>
|
||||||
|
{part =>
|
||||||
|
<>
|
||||||
|
<div data-section="decoration">
|
||||||
|
<div>
|
||||||
|
<IconCpuChip width={18} height={18} />
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<div data-section="content">
|
||||||
|
<span data-element-label>System</span>
|
||||||
|
<TextPart
|
||||||
|
text={part().text}
|
||||||
|
expand={isLastPart()}
|
||||||
|
/>
|
||||||
|
<PartFooter time={time} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</Match>
|
||||||
|
{ /* Step start */}
|
||||||
|
<Match when={part.type === "step-start"}>{null}</Match>
|
||||||
|
{ /* Fallback */}
|
||||||
|
<Match when={true}>
|
||||||
<div data-section="decoration">
|
<div data-section="decoration">
|
||||||
<div>
|
<div>
|
||||||
<Switch fallback={
|
<Switch fallback={
|
||||||
<IconWrenchScrewdriver width={16} height={16} />
|
<IconWrenchScrewdriver width={16} height={16} />
|
||||||
}>
|
}>
|
||||||
<Match when={msg.role === "assistant" && (part.type === "text" || part.type === "step-start")}>
|
<Match when={msg.role === "assistant" && part.type !== "tool-invocation"}>
|
||||||
<IconSparkles width={18} height={18} />
|
<IconSparkles width={18} height={18} />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={msg.role === "system"}>
|
<Match when={msg.role === "system"}>
|
||||||
|
@ -351,27 +436,15 @@ export default function Share(props: { api: string }) {
|
||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
<div data-section="content">
|
<div data-section="content">
|
||||||
{getPartTitle(msg.role, part.type)
|
<span data-element-label>{part.type}</span>
|
||||||
? <span data-element-label>
|
<TextPart text={JSON.stringify(part, null, 2)} />
|
||||||
{getPartTitle(msg.role, part.type)}
|
<PartFooter time={time} />
|
||||||
</span>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
{part.type === "text"
|
|
||||||
? <TextPart
|
|
||||||
text={part.text}
|
|
||||||
highlight={msg.role === "user"}
|
|
||||||
/>
|
|
||||||
: <TextPart text={JSON.stringify(part, null, 2)} />
|
|
||||||
}
|
|
||||||
{renderTime(
|
|
||||||
msg.metadata?.time.completed
|
|
||||||
|| msg.metadata?.time.created
|
|
||||||
|| 0
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
|
}}
|
||||||
</For>
|
</For>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
|
|
@ -45,8 +45,11 @@
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
line-height: 1.125;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.375rem;
|
gap: 0.375rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
@ -131,16 +134,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-section="content"] {
|
[data-section="content"] {
|
||||||
padding: 3px 0 0.375rem;
|
padding: 1px 0 0.375rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
|
||||||
span:first-child {
|
span:first-child {
|
||||||
|
padding-top: 2px;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
span:last-child {
|
span:last-child {
|
||||||
|
align-self: flex-start;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
color: var(--sl-color-text-dimmed);
|
color: var(--sl-color-text-dimmed);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue