mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-22 02:04:22 +01:00
Styling share
This commit is contained in:
@@ -18,11 +18,13 @@ import {
|
||||
IconSparkles,
|
||||
IconUserCircle,
|
||||
IconChevronDown,
|
||||
IconCommandLine,
|
||||
IconChevronRight,
|
||||
IconPencilSquare,
|
||||
IconWrenchScrewdriver,
|
||||
} from "./icons"
|
||||
import DiffView from "./DiffView"
|
||||
import CodeBlock from "./CodeBlock"
|
||||
import styles from "./share.module.css"
|
||||
import { type UIMessage } from "ai"
|
||||
import { createStore, reconcile } from "solid-js/store"
|
||||
@@ -199,6 +201,70 @@ function TextPart(props: TextPartProps) {
|
||||
)
|
||||
}
|
||||
|
||||
interface TerminalPartProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
||||
text: string
|
||||
expand?: boolean
|
||||
}
|
||||
function TerminalPart(props: TerminalPartProps) {
|
||||
const [local, rest] = splitProps(props, ["text", "expand"])
|
||||
const [expanded, setExpanded] = createSignal(false)
|
||||
const [overflowed, setOverflowed] = createSignal(false)
|
||||
let preEl: HTMLElement | undefined
|
||||
|
||||
function checkOverflow() {
|
||||
if (!preEl) return
|
||||
|
||||
const code = preEl.getElementsByTagName("code")[0]
|
||||
|
||||
if (code && !local.expand) {
|
||||
console.log(preEl.clientHeight, code.offsetHeight)
|
||||
setOverflowed(preEl.clientHeight < code.offsetHeight)
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
checkOverflow()
|
||||
window.addEventListener("resize", checkOverflow)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
local.text
|
||||
setTimeout(checkOverflow, 0)
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
window.removeEventListener("resize", checkOverflow)
|
||||
})
|
||||
|
||||
return (
|
||||
<div
|
||||
data-element-message-terminal
|
||||
data-expanded={expanded() || local.expand === true}
|
||||
{...rest}
|
||||
>
|
||||
<div data-section="body">
|
||||
<div data-section="header"></div>
|
||||
<div data-section="content">
|
||||
<CodeBlock
|
||||
lang="ansi"
|
||||
ref={(el) => (preEl = el)}
|
||||
code={`\x1b[90m>\x1b[0m ${local.text}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{overflowed() && (
|
||||
<button
|
||||
type="button"
|
||||
data-element-button-text
|
||||
onClick={() => setExpanded((e) => !e)}
|
||||
>
|
||||
{expanded() ? "Show less" : "Show more"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function PartFooter(props: { time: number }) {
|
||||
return (
|
||||
<span
|
||||
@@ -478,7 +544,7 @@ export default function Share(props: { api: string }) {
|
||||
{(part) => (
|
||||
<div data-section="part" data-part-type="user-text">
|
||||
<div data-section="decoration">
|
||||
<div>
|
||||
<div title="Message">
|
||||
<IconUserCircle width={18} height={18} />
|
||||
</div>
|
||||
<div></div>
|
||||
@@ -505,7 +571,7 @@ export default function Share(props: { api: string }) {
|
||||
{(part) => (
|
||||
<div data-section="part" data-part-type="ai-text">
|
||||
<div data-section="decoration">
|
||||
<div>
|
||||
<div title="AI response">
|
||||
<IconSparkles width={18} height={18} />
|
||||
</div>
|
||||
<div></div>
|
||||
@@ -570,7 +636,7 @@ export default function Share(props: { api: string }) {
|
||||
data-part-type="system-text"
|
||||
>
|
||||
<div data-section="decoration">
|
||||
<div>
|
||||
<div title="System message">
|
||||
<IconCpuChip width={18} height={18} />
|
||||
</div>
|
||||
<div></div>
|
||||
@@ -610,7 +676,7 @@ export default function Share(props: { api: string }) {
|
||||
data-part-type="tool-edit"
|
||||
>
|
||||
<div data-section="decoration">
|
||||
<div>
|
||||
<div title="Edit file">
|
||||
<IconPencilSquare width={18} height={18} />
|
||||
</div>
|
||||
<div></div>
|
||||
@@ -618,11 +684,12 @@ export default function Share(props: { api: string }) {
|
||||
<div data-section="content">
|
||||
<div data-part-tool-body>
|
||||
<span data-part-title data-size="md">
|
||||
Edit {filePath}
|
||||
<span data-element-label>Edit</span>
|
||||
<b>{filePath}</b>
|
||||
</span>
|
||||
<div data-part-tool-edit>
|
||||
<DiffView
|
||||
class={styles["code-block"]}
|
||||
class={styles["diff-code-block"]}
|
||||
changes={metadata()?.changes || []}
|
||||
lang={getFileType(filePath)}
|
||||
/>
|
||||
@@ -634,6 +701,44 @@ export default function Share(props: { api: string }) {
|
||||
)
|
||||
}}
|
||||
</Match>
|
||||
{/* Bash tool */}
|
||||
<Match
|
||||
when={
|
||||
msg.role === "assistant" &&
|
||||
part.type === "tool-invocation" &&
|
||||
part.toolInvocation.toolName === "opencode_bash" &&
|
||||
part
|
||||
}
|
||||
>
|
||||
{(part) => {
|
||||
const id = part().toolInvocation.toolCallId
|
||||
const command = part().toolInvocation.args.command
|
||||
const stdout = msg.metadata?.tool[id]?.stdout
|
||||
const result = stdout || (part().toolInvocation.state === "result" && part().toolInvocation.result)
|
||||
return (
|
||||
<div
|
||||
data-section="part"
|
||||
data-part-type="tool-edit"
|
||||
>
|
||||
<div data-section="decoration">
|
||||
<div title="Bash command">
|
||||
<IconCommandLine width={18} height={18} />
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<div data-section="content">
|
||||
<div data-part-tool-body>
|
||||
<TerminalPart
|
||||
data-size="sm"
|
||||
text={command + (result ? `\n${result}` : "")}
|
||||
/>
|
||||
</div>
|
||||
<PartFooter time={time} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</Match>
|
||||
{/* Tool call */}
|
||||
<Match
|
||||
when={
|
||||
@@ -648,7 +753,7 @@ export default function Share(props: { api: string }) {
|
||||
data-part-type="tool-fallback"
|
||||
>
|
||||
<div data-section="decoration">
|
||||
<div>
|
||||
<div title="Tool call">
|
||||
<IconWrenchScrewdriver
|
||||
width={18}
|
||||
height={18}
|
||||
|
||||
Reference in New Issue
Block a user