Skip to content

Commit

Permalink
Chatbot code syntax highlighting (#4048)
Browse files Browse the repository at this point in the history
* first pass

* fixes

* more fixes

* remove breaks

* format

* version

* pr fixes

* changelog

* test fix

* background color

* format

* revert test fix

* changes

* changes

* test

* changes

* changes

* changes

* changes

* changes

---------

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
Co-authored-by: Ali Abid <aabid94@gmail.com>
  • Loading branch information
3 people authored May 4, 2023
1 parent ff21ecb commit c06901e
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
## New Features:

- Add support for `visual-question-answering`, `document-question-answering`, and `image-to-text` using `gr.Interface.load("models/...")` and `gr.Interface.from_pipeline` by [@osanseviero](https://github.com/osanseviero) in [PR 3887](https://github.com/gradio-app/gradio/pull/3887)
- Add code block support in `gr.Chatbot()`, by [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 4048](https://github.com/gradio-app/gradio/pull/4048)
- Adds the ability to blocklist filepaths (and also improves the allowlist mechanism) by [@abidlabs](https://github.com/abidlabs) in [PR 4047](https://github.com/gradio-app/gradio/pull/4047).
- Adds the ability to specify the upload directory via an environment variable by [@abidlabs](https://github.com/abidlabs) in [PR 4047](https://github.com/gradio-app/gradio/pull/4047).

Expand All @@ -27,6 +28,7 @@ No changes to highlight.
## Breaking Changes:

- `gr.HuggingFaceDatasetSaver` behavior changed internally. The `flagging/` folder is not a `.git/` folder anymore when using it. `organization` parameter is now ignored in favor of passing a full dataset id as `dataset_name` (e.g. `"username/my-dataset"`).
- New lines (`\n`) are not automatically converted to `<br>` in `gr.Markdown()` or `gr.Chatbot()`. For multiple new lines, a developer must add multiple `<br>` tags.

## Full Changelog:

Expand Down
13 changes: 6 additions & 7 deletions gradio/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from enum import Enum
from pathlib import Path
from types import ModuleType
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Set, Tuple, Type
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Set, Tuple, Type, cast

import aiofiles
import altair as alt
Expand Down Expand Up @@ -4593,12 +4593,11 @@ def _postprocess_chat_messages(
"is_file": True,
}
elif isinstance(chat_message, str):
children = self.md.parseInline(chat_message)[0].children
if children and any("code" in child.tag for child in children):
return self.md.render(chat_message)
else:
chat_message = chat_message.replace("\n", "<br>")
return self.md.renderInline(chat_message)
chat_message = inspect.cleandoc(chat_message)
chat_message = cast(str, self.md.render(chat_message))
if chat_message.startswith("<p>") and chat_message.endswith("</p>\n"):
chat_message = chat_message[3:-5]
return chat_message
else:
raise ValueError(f"Invalid message for Chatbot component: {chat_message}")

Expand Down
11 changes: 11 additions & 0 deletions gradio/themes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,8 @@ def set(
section_header_text_size=None,
section_header_text_weight=None,
# Component Atoms: These set the style for elements within components.
chatbot_code_background_color=None,
chatbot_code_background_color_dark=None,
checkbox_background_color=None,
checkbox_background_color_dark=None,
checkbox_background_color_focus=None,
Expand Down Expand Up @@ -800,6 +802,8 @@ def set(
panel_border_width_dark: The border width of a panel in dark mode.
section_header_text_size: The text size of a section header (e.g. tab name).
section_header_text_weight: The text weight of a section header (e.g. tab name).
chatbot_code_background_color: The background color of code blocks in the chatbot.
chatbot_code_background_color_dark: The background color of code blocks in the chatbot in dark mode.
checkbox_background_color: The background of a checkbox square or radio circle.
checkbox_background_color_dark: The background of a checkbox square or radio circle in dark mode.
checkbox_background_color_focus: The background of a checkbox square or radio circle when focused.
Expand Down Expand Up @@ -1201,6 +1205,13 @@ def set(
self, "section_header_text_weight", "400"
)
# Component Atoms
self.chatbot_code_background_color = chatbot_code_background_color or getattr(
self, "chatbot_code_background_color", "*neutral_100"
)
self.chatbot_code_background_color_dark = (
chatbot_code_background_color_dark
or getattr(self, "chatbot_code_background_color_dark", "*neutral_800")
)
self.checkbox_background_color = checkbox_background_color or getattr(
self, "checkbox_background_color", "*background_fill_primary"
)
Expand Down
15 changes: 14 additions & 1 deletion gradio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
import matplotlib
import requests
from markdown_it import MarkdownIt
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import get_lexer_by_name
from mdit_py_plugins.dollarmath.index import dollarmath_plugin
from mdit_py_plugins.footnote.index import footnote_plugin
from pydantic import BaseModel, parse_obj_as
Expand Down Expand Up @@ -1007,6 +1010,16 @@ def get_class_that_defined_method(meth: Callable):
return cls.__name__


def highlight_code(code, name, attrs):
try:
lexer = get_lexer_by_name(name)
except:
lexer = get_lexer_by_name("text")
formatter = HtmlFormatter()

return highlight(code, lexer, formatter)


def get_markdown_parser() -> MarkdownIt:
md = (
MarkdownIt(
Expand All @@ -1015,7 +1028,7 @@ def get_markdown_parser() -> MarkdownIt:
"linkify": True,
"typographer": True,
"html": True,
"breaks": True,
"highlight": highlight_code,
},
)
.use(dollarmath_plugin, renderer=tex2svg, allow_digits=False)
Expand Down
2 changes: 1 addition & 1 deletion gradio/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.28.1
3.28.2
39 changes: 38 additions & 1 deletion js/chatbot/src/ChatBot.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { beforeUpdate, afterUpdate, createEventDispatcher } from "svelte";
import type { Styles, SelectData } from "@gradio/utils";
import type { FileData } from "@gradio/upload";
import "./manni.css";
export let value: Array<
[string | FileData | null, string | FileData | null]
Expand Down Expand Up @@ -36,6 +37,31 @@
});
});
}
div.querySelectorAll("pre > code").forEach((n) => {
let code_node = n as HTMLElement;
let node = n.parentElement as HTMLElement;
node.style.position = "relative";
const button = document.createElement("button");
button.className = "copy-button";
button.innerHTML = "Copy";
button.style.position = "absolute";
button.style.right = "0";
button.style.top = "0";
button.style.zIndex = "1";
button.style.padding = "var(--spacing-md)";
button.style.marginTop = "12px";
button.style.fontSize = "var(--text-sm)";
button.style.borderBottomLeftRadius = "var(--radius-sm)";
button.style.backgroundColor = "var(--block-label-background-fill)";
button.addEventListener("click", () => {
navigator.clipboard.writeText(code_node.innerText.trimEnd());
button.innerHTML = "Copied!";
setTimeout(() => {
button.innerHTML = "Copy";
}, 1000);
});
node.appendChild(button);
});
});
$: {
Expand All @@ -56,14 +82,18 @@
{#if value !== null}
{#each value as message_pair, i}
{#each message_pair as message, j}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
data-testid={j == 0 ? "user" : "bot"}
class:latest={i === value.length - 1}
class="message {j == 0 ? 'user' : 'bot'}"
class:hide={message === null}
class:selectable
on:click={() =>
dispatch("select", { index: [i, j], value: message })}
dispatch("select", {
index: [i, j],
value: message
})}
>
{#if typeof message === "string"}
{@html message}
Expand Down Expand Up @@ -217,6 +247,13 @@
.dot-flashing:nth-child(3) {
animation-delay: 0.66s;
}
.message-wrap > div :global(.highlight) {
margin-top: var(--spacing-xs);
margin-bottom: var(--spacing-xs);
border-radius: var(--radius-md);
background: var(--chatbot-code-background-color);
padding-left: var(--spacing-xxl);
}
/* Small screen */
@media (max-width: 480px) {
Expand Down
Loading

0 comments on commit c06901e

Please sign in to comment.