ignore: share error styles

This commit is contained in:
Jay V
2025-06-18 14:10:07 -04:00
parent e05c3b7a76
commit 6e4ef585d8
2 changed files with 93 additions and 62 deletions

View File

@@ -172,13 +172,29 @@ function flattenToolArgs(obj: any, prefix: string = ""): Array<[string, any]> {
return entries
}
export function getDiagnostics(
function formatErrorString(error: string): JSX.Element {
const errorMarker = "Error: "
const startsWithError = error.startsWith(errorMarker)
return startsWithError ? (
<p>
<span data-color="red" data-marker="label" data-separator>
Error
</span>
<span>{error.slice(errorMarker.length)}</span>
</p>
) : (
<p><span data-color="dimmed">{error}</span></p>
)
}
function getDiagnostics(
diagnosticsByFile: Record<string, Diagnostic[]>,
currentFile: string
): string[] {
): JSX.Element[] {
// Return a flat array of error diagnostics, in the format:
// "ERROR [65:20] Property 'x' does not exist on type 'Y'"
const result: string[] = []
// "Error [65:20] Property 'x' does not exist on type 'Y'"
const result: JSX.Element[] = []
if (
diagnosticsByFile === undefined || diagnosticsByFile[currentFile] === undefined
@@ -192,7 +208,15 @@ export function getDiagnostics(
const line = d.range.start.line + 1 // 1-based
const column = d.range.start.character + 1 // 1-based
result.push(`\x1b[31mERROR\x1b[0m \x1b[2m[${line}:${column}]\x1b[0m ${d.message}`)
result.push(
<p>
<span data-color="red" data-marker="label">Error</span>
<span data-color="dimmed" data-separator>
[{line}:{column}]
</span>
<span>{d.message}</span>
</p>
)
}
}
@@ -324,46 +348,44 @@ function TextPart(props: TextPartProps) {
)
}
interface LspPartProps extends JSX.HTMLAttributes<HTMLDivElement> {
text: string
interface ErrorPartProps extends JSX.HTMLAttributes<HTMLDivElement> {
expand?: boolean
}
function LspPart(props: LspPartProps) {
const [local, rest] = splitProps(props, ["text", "expand"])
function ErrorPart(props: ErrorPartProps) {
const [local, rest] = splitProps(props, ["expand", "children"])
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) {
setOverflowed(preEl.clientHeight < code.offsetHeight)
if (preEl && !local.expand) {
setOverflowed(preEl.scrollHeight > preEl.clientHeight + 1)
}
}
onMount(() => {
checkOverflow()
window.addEventListener("resize", checkOverflow)
})
createEffect(() => {
local.children
setTimeout(checkOverflow, 0)
})
onCleanup(() => {
window.removeEventListener("resize", checkOverflow)
})
return (
<div
class={styles["message-lsp"]}
class={styles["message-error"]}
data-expanded={expanded() || local.expand === true}
{...rest}
>
<CodeBlock
lang="ansi"
code={local.text}
onRendered={checkOverflow}
ref={(el) => (preEl = el)}
/>
<div data-section="content" ref={(el) => (preEl = el)}>
{local.children}
</div>
{((!local.expand && overflowed()) || expanded()) && (
<button
type="button"
@@ -512,6 +534,7 @@ export default function Share(props: {
info: SessionInfo
messages: Record<string, SessionMessage>
}) {
let hasScrolled = false
const id = props.id
const anchorId = createMemo<string | null>(() => {
@@ -586,8 +609,9 @@ export default function Share(props: {
const [, messageID] = splits
setStore("messages", messageID, reconcile(d.content))
if (messageID === anchorId()) {
if (!hasScrolled && messageID === anchorId()) {
scrollToAnchor(window.location.hash.slice(1))
hasScrolled = true
}
}
} catch (error) {
@@ -1241,12 +1265,9 @@ export default function Share(props: {
<Switch>
<Match when={hasError()}>
<div data-part-tool-result>
<TextPart
expand
text={toolData()?.result}
data-size="sm"
data-color="dimmed"
/>
<ErrorPart>
{formatErrorString(toolData()?.result)}
</ErrorPart>
</div>
</Match>
<Match when={preview()}>
@@ -1339,19 +1360,14 @@ export default function Share(props: {
<b>{filePath()}</b>
</div>
<Show when={diagnostics().length > 0}>
<LspPart
text={diagnostics().join("\n\n")}
/>
<ErrorPart>{diagnostics()}</ErrorPart>
</Show>
<Switch>
<Match when={hasError()}>
<div data-part-tool-result>
<TextPart
expand
text={toolData()?.result}
data-size="sm"
data-color="dimmed"
/>
<ErrorPart>
{formatErrorString(toolData()?.result)}
</ErrorPart>
</div>
</Match>
<Match when={content()}>
@@ -1429,12 +1445,9 @@ export default function Share(props: {
<Switch>
<Match when={hasError()}>
<div data-part-tool-result>
<TextPart
expand
data-size="sm"
data-color="dimmed"
text={message()}
/>
<ErrorPart>
{formatErrorString(message())}
</ErrorPart>
</div>
</Match>
<Match when={diff()}>
@@ -1448,9 +1461,7 @@ export default function Share(props: {
</Match>
</Switch>
<Show when={diagnostics().length > 0}>
<LspPart
text={diagnostics().join("\n\n")}
/>
<ErrorPart>{diagnostics()}</ErrorPart>
</Show>
</div>
<ToolFooter time={toolData()?.duration || 0} />
@@ -1601,12 +1612,9 @@ export default function Share(props: {
<Switch>
<Match when={hasError()}>
<div data-part-tool-result>
<TextPart
expand
text={toolData()?.result}
data-size="sm"
data-color="dimmed"
/>
<ErrorPart>
{formatErrorString(toolData()?.result)}
</ErrorPart>
</div>
</Match>
<Match when={toolData()?.result}>

View File

@@ -421,7 +421,7 @@
}
}
.message-lsp {
.message-error {
background-color: var(--sl-color-bg-surface);
padding: 0.5rem calc(0.5rem + 3px);
border-radius: 0.25rem;
@@ -432,24 +432,47 @@
align-self: flex-start;
max-width: var(--md-tool-width);
padding: 0.5rem calc(0.5rem + 3px);
pre {
--shiki-dark-bg: var(--sl-color-bg-surface) !important;
background-color: var(--sl-color-bg-surface) !important;
line-height: 1.4;
[data-section="content"] {
p {
margin-bottom: 0.5rem;
line-height: 1.5;
font-size: 0.75rem;
white-space: pre-wrap;
word-break: break-word;
&:last-child {
margin-bottom: 0;
}
span {
margin-right: 0.25rem;
&:last-child {
margin-right: 0;
}
}
span[data-color="red"] {
color: var(--sl-color-red);
}
span[data-color="dimmed"] {
color: var(--sl-color-text-dimmed);
}
span[data-marker="label"] {
text-transform: uppercase;
letter-spacing: -0.5px;
}
span[data-separator] {
margin-right: 0.375rem;
}
}
}
&[data-expanded="true"] {
pre {
[data-section="content"] {
display: block;
}
}
&[data-expanded="false"] {
pre {
[data-section="content"] {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 7;