fix(web): improve inline code chip visibility in markdown (#948)

This commit is contained in:
Carlos Florêncio
2026-05-19 17:56:35 +01:00
committed by GitHub
parent 3c3ae81f00
commit 2bfdd518b6
4 changed files with 144 additions and 3 deletions
+20
View File
@@ -80,11 +80,31 @@ func handlePrompt(e *emitter, prompt, model string) {
emitTodoSequence(e, model)
case strings.EqualFold(cmd, "/mermaid"):
emitMermaidSequence(e, model)
case strings.EqualFold(cmd, "/markdown"):
emitMarkdownShowcase(e, model)
case strings.EqualFold(cmd, "/sleep") || strings.HasPrefix(strings.ToLower(cmd), "/sleep "):
emitSleep(e, cmd)
default:
emitRandomResponse(e, cmd, model)
}
}
// emitSleep sleeps for the requested duration (default 10s) then responds.
// Useful for simulating a slow agent turn without any tool calls.
func emitSleep(e *emitter, cmd string) {
d := 10 * time.Second
parts := strings.Fields(cmd)
if len(parts) >= 2 {
if secs, err := time.ParseDuration(parts[1] + "s"); err == nil && secs > 0 {
d = secs
} else if parsed, err2 := time.ParseDuration(parts[1]); err2 == nil && parsed > 0 {
d = parsed
}
}
time.Sleep(d)
e.text(fmt.Sprintf("Slept for %s.", d))
}
// emitError emits an error message.
func emitError(e *emitter, model string) {
randomDelay(model)
+2
View File
@@ -264,6 +264,8 @@ func mockAvailableCommands() []acp.AvailableCommand {
{Name: "tool:edit", Description: "Emit an edit file tool call"},
{Name: "tool:exec", Description: "Emit a shell exec tool call"},
{Name: "tool:search", Description: "Emit a search tool call"},
{Name: "markdown", Description: "Render all markdown/text types"},
{Name: "sleep", Description: "Sleep without output (default 10s)", Input: hint("seconds (e.g. 30)")},
}
}
+116
View File
@@ -215,6 +215,122 @@ func emitMermaidSequence(e *emitter, model string) {
"- Database connections use a **connection pool** (max 50)\n")
}
// emitMarkdownShowcase emits a rich multi-message demonstration of every
// rendered text/markdown type: headings, paragraphs (short and long), inline
// code, fenced code blocks, lists, tables, blockquotes, bold/italic, and hr.
func emitMarkdownShowcase(e *emitter, model string) {
randomDelay(model)
e.text(markdownHeadingsMsg())
randomDelay(model)
e.text(markdownLongParaMsg())
randomDelay(model)
e.text(markdownCodeBlocksMsg())
randomDelay(model)
e.text(markdownListsMsg())
randomDelay(model)
e.text(markdownTableBlockquoteMsg())
}
func markdownHeadingsMsg() string {
return "# Heading 1\n\n" +
"## Heading 2\n\n" +
"### Heading 3\n\n" +
"Short paragraph. Inline code looks like `const x = 42` in the middle of a sentence.\n\n" +
"Another short one: call `os.Exit(1)` to terminate, or use `fmt.Errorf(\"wrap: %w\", err)` for wrapping errors."
}
func markdownLongParaMsg() string {
return "## Long Paragraph\n\n" +
"This is a deliberately long paragraph to test line wrapping and reading comfort at various chat panel widths. " +
"When a user writes a prompt that requires careful reasoning, the agent first decomposes the request into sub-problems, " +
"then resolves each one in dependency order before synthesising a final answer. " +
"The approach mirrors how a senior engineer would tackle an unfamiliar codebase: read the entry points, " +
"trace the data flow, identify the invariants that must hold, and only then start making changes. " +
"Getting this sequence right matters because changes made without understanding the invariants tend to " +
"introduce subtle regressions that only surface under production load — exactly the kind of bug that is " +
"hardest to diagnose after the fact.\n\n" +
"A second long paragraph follows immediately to check spacing between blocks. " +
"The spacing should feel consistent with single-paragraph messages, neither too tight nor too loose. " +
"Inter-paragraph spacing is controlled by `margin-top` on `.markdown-body p + p`, " +
"which currently sits at `0.5em` — enough breathing room without pushing content off-screen on short viewports."
}
func markdownCodeBlocksMsg() string {
return "## Code Blocks\n\n" +
"TypeScript:\n\n" +
"```typescript\n" +
"interface Session {\n" +
" id: string;\n" +
" createdAt: Date;\n" +
" messages: Message[];\n" +
"}\n\n" +
"async function fetchSession(id: string): Promise<Session> {\n" +
" const res = await fetch(`/api/sessions/${id}`);\n" +
" if (!res.ok) throw new Error(`HTTP ${res.status}`);\n" +
" return res.json();\n" +
"}\n" +
"```\n\n" +
"Go:\n\n" +
"```go\n" +
"func (s *Service) CreateTask(ctx context.Context, req CreateTaskRequest) (*Task, error) {\n" +
"\tif req.Title == \"\" {\n" +
"\t\treturn nil, fmt.Errorf(\"title is required\")\n" +
"\t}\n" +
"\ttask := &Task{\n" +
"\t\tID: uuid.New().String(),\n" +
"\t\tTitle: req.Title,\n" +
"\t\tCreatedAt: time.Now(),\n" +
"\t}\n" +
"\treturn s.repo.Save(ctx, task)\n" +
"}\n" +
"```\n\n" +
"Shell:\n\n" +
"```bash\n" +
"# Build and run tests\n" +
"make -C apps/backend build && \\\n" +
" go test ./... -race -count=1 | tee /tmp/test.log\n" +
"```"
}
func markdownListsMsg() string {
return "## Lists\n\n" +
"Unordered:\n\n" +
"- First item with `inline code` in it\n" +
"- Second item — **bold text** and *italic text* inline\n" +
"- Third item with a longer description: this tests wrapping inside list items at narrow panel widths, " +
"which is a common layout scenario in split-pane editors\n" +
" - Nested item A\n" +
" - Nested item B\n\n" +
"Ordered:\n\n" +
"1. Read the spec\n" +
"2. Write a failing test\n" +
"3. Implement the feature\n" +
"4. Refactor until the code is clean\n" +
"5. Open a PR and request review"
}
func markdownTableBlockquoteMsg() string {
return "## Table\n\n" +
"| Command | Description | Example |\n" +
"|---------|-------------|----------|\n" +
"| `/slow` | Slow response | `/slow 10s` |\n" +
"| `/thinking` | Reasoning blocks | `/thinking` |\n" +
"| `/mermaid` | Mermaid diagram | `/mermaid` |\n" +
"| `/markdown` | This showcase | `/markdown` |\n\n" +
"---\n\n" +
"## Blockquote\n\n" +
"> Programs must be written for people to read, and only incidentally for machines to execute.\n" +
"> — Harold Abelson\n\n" +
"> A second blockquote paragraph to verify spacing between consecutive quote blocks " +
"and to ensure the left-border styling extends for the full height of wrapped lines.\n\n" +
"---\n\n" +
"## Inline Styles\n\n" +
"**Bold**, *italic*, ~~strikethrough~~, and `inline code` all in one sentence. " +
"A path like `apps/web/app/globals.css` mid-sentence, then more prose after it. " +
"And a longer inline snippet: `color-mix(in oklch, var(--foreground) 10%, var(--muted))` " +
"should wrap cleanly without breaking the surrounding text flow."
}
// emitWebFetch emits a WebFetch tool call.
func emitWebFetch(e *emitter, model string) {
toolID := nextToolID()
+6 -3
View File
@@ -715,12 +715,14 @@
margin-bottom: 0.25rem;
}
/* Inline code: neutral foreground on a soft muted background.
/* Inline code: neutral foreground on a slightly elevated background with a
subtle border so chips read clearly against the card surface.
Linked code (a > code) drops the background and inherits the link
color so file references read as links, not as another code chip. */
.markdown-body :not(pre) > code,
.markdown-body code:not(pre code) {
background: var(--muted);
background: color-mix(in oklch, var(--foreground) 10%, var(--muted));
border: 1px solid color-mix(in oklch, var(--foreground) 10%, transparent);
color: var(--foreground);
padding: 0.1rem 0.375rem;
border-radius: 0.25rem;
@@ -744,12 +746,13 @@
.markdown-body:not(.markdown-body-user) a > code,
.markdown-body:not(.markdown-body-user) a code:not(pre code) {
background: transparent;
border: none;
color: inherit;
}
.markdown-body:not(.markdown-body-user) a:hover > code,
.markdown-body:not(.markdown-body-user) a:hover code:not(pre code) {
background: var(--muted);
background: color-mix(in oklch, var(--foreground) 10%, var(--muted));
}
.markdown-body blockquote {