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

View File

@@ -421,7 +421,7 @@
} }
} }
.message-lsp { .message-error {
background-color: var(--sl-color-bg-surface); background-color: var(--sl-color-bg-surface);
padding: 0.5rem calc(0.5rem + 3px); padding: 0.5rem calc(0.5rem + 3px);
border-radius: 0.25rem; border-radius: 0.25rem;
@@ -432,24 +432,47 @@
align-self: flex-start; align-self: flex-start;
max-width: var(--md-tool-width); max-width: var(--md-tool-width);
padding: 0.5rem calc(0.5rem + 3px); [data-section="content"] {
p {
pre { margin-bottom: 0.5rem;
--shiki-dark-bg: var(--sl-color-bg-surface) !important; line-height: 1.5;
background-color: var(--sl-color-bg-surface) !important;
line-height: 1.4;
font-size: 0.75rem; font-size: 0.75rem;
white-space: pre-wrap; white-space: pre-wrap;
word-break: break-word; 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"] { &[data-expanded="true"] {
pre { [data-section="content"] {
display: block; display: block;
} }
} }
&[data-expanded="false"] { &[data-expanded="false"] {
pre { [data-section="content"] {
display: -webkit-box; display: -webkit-box;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
-webkit-line-clamp: 7; -webkit-line-clamp: 7;