diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c6168b909..ef85e7265e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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).
@@ -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 `
` in `gr.Markdown()` or `gr.Chatbot()`. For multiple new lines, a developer must add multiple `
` tags.
## Full Changelog:
diff --git a/gradio/components.py b/gradio/components.py
index e03290a7a1..77069f96cc 100644
--- a/gradio/components.py
+++ b/gradio/components.py
@@ -20,7 +20,7 @@ from copy import deepcopy
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
@@ -4593,12 +4593,11 @@ class Chatbot(Changeable, Selectable, IOComponent, JSONSerializable):
"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", "
")
- 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("
") and chat_message.endswith("
\n"): + chat_message = chat_message[3:-5] + return chat_message else: raise ValueError(f"Invalid message for Chatbot component: {chat_message}") diff --git a/gradio/themes/base.py b/gradio/themes/base.py index c96ab7a2f1..85dba4f44b 100644 --- a/gradio/themes/base.py +++ b/gradio/themes/base.py @@ -568,6 +568,8 @@ class Base(ThemeClass): 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, @@ -800,6 +802,8 @@ class Base(ThemeClass): 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. @@ -1201,6 +1205,13 @@ class Base(ThemeClass): 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" ) diff --git a/gradio/utils.py b/gradio/utils.py index 66c3d55910..cc35d1ce35 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -41,6 +41,9 @@ import httpx 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 @@ -1007,6 +1010,16 @@ def get_serializer_name(block: Block) -> str | None: 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( @@ -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) diff --git a/gradio/version.txt b/gradio/version.txt index 54d1636c6c..2a5310b5a9 100644 --- a/gradio/version.txt +++ b/gradio/version.txt @@ -1 +1 @@ -3.28.1 +3.28.2 diff --git a/js/chatbot/src/ChatBot.svelte b/js/chatbot/src/ChatBot.svelte index 71c617f613..9437d27d35 100644 --- a/js/chatbot/src/ChatBot.svelte +++ b/js/chatbot/src/ChatBot.svelte @@ -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] @@ -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); + }); }); $: { @@ -56,6 +82,7 @@ {#if value !== null} {#each value as message_pair, i} {#each message_pair as message, j} +