Code Block
Example:
def example() -> ComponentType:
return codeblock(inspect.getsource(example), code_class="language-python")
Code example:
import inspect
from htmy import ComponentType
from htmui.basecoat.codeblock import codeblock
def example() -> ComponentType:
return codeblock(inspect.getsource(example), code_class="language-python")
Component implementation:
For more details, see the BasecoatUI documentation.
from htmy import ComponentType, SafeStr, html, join_classes
__version__ = "0.2.0"
__framework__ = "BasecoatUI"
__framework_version__ = "1"
__framework_url__ = "https://basecoatui.com/"
copy_icon = SafeStr(
'<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" '
'stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" '
'd="M8.25 7.5V6.108c0-1.135.845-2.098 1.976-2.192.373-.03.748-.057 1.123-.08M15.75 18H18a2.25 '
"2.25 0 0 0 2.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 0 0-1.123-.08M15.75 "
"18.75v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5A3.375 3.375 "
"0 0 0 6.375 7.5H5.25m11.9-3.664A2.251 2.251 0 0 0 15 2.25h-1.5a2.251 2.251 0 0 0-2.15 1.586m5.8 "
"0c.065.21.1.433.1.664v.75h-6V4.5c0-.231.035-.454.1-.664M6.75 7.5H4.875c-.621 0-1.125.504-1.125 "
'1.125v12c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V16.5a9 9 0 0 0-9-9Z" /></svg>'
)
"""`clipboard-document` icon from https://heroicons.com/."""
copy_button_click_handler = SafeStr("""
const button = this;
const code = this.parentElement.querySelector('pre code');
if (!code) return;
const clickedClass = 'text-green-700';
const svg = this.parentElement.querySelector('svg');
navigator.clipboard.writeText(code.textContent || '').then(() => {
svg?.classList.add(clickedClass);
setTimeout(() => {
svg?.classList.remove(clickedClass);
}, 2000);
}).catch(err => {
console.error('Failed to copy text: ', err);
});""")
"""JavaScript for handling copy button click events."""
def copy_button(icon: ComponentType = copy_icon) -> ComponentType:
return html.button(
icon,
onclick=copy_button_click_handler,
class_="btn size-8 absolute right-2.5 top-2 group hover:text-foreground text-muted-foreground",
data_variant="ghost",
data_size="icon",
)
def codeblock(code: str, *, code_class: str | None = None) -> ComponentType:
return html.pre(
html.code(code, class_=join_classes(code_class, "!bg-muted/40 !p-3.5")),
copy_button(),
class_="grid text-sm max-h-[650px] overflow-y-auto rounded-xl scrollbar relative",
)
_default_hljs_version = "11.11.1"
def highlightjs_theme(theme: str = "default", *, version: str = _default_hljs_version) -> SafeStr:
return SafeStr(
f'<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{version}/styles/{theme}.min.css">'
)
def highlightjs_js(*, version: str = _default_hljs_version) -> SafeStr:
return SafeStr(
f'<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{version}/highlight.min.js"></script>'
)
def highlightjs_languages(*languages: str, version: str = _default_hljs_version) -> SafeStr:
return SafeStr(
"\n".join(
(
f'<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{version}/languages/{lang}.min.js"></script>'
for lang in languages
)
)
)
class highlightjs_events:
dom_content_loaded = "DOMContentLoaded"
htmx_after_swap = "htmx:after:swap"
def highlightjs_on_load(*events: str) -> SafeStr:
return SafeStr(
"\n".join(
(
"<script>",
"const highlight = () => {",
" if (!window.hljs) return;",
" document",
" .querySelectorAll(",
" 'pre code:not([data-highlighted]), code.highlight:not([data-highlighted])'",
")",
" .forEach(el => window.hljs.highlightElement(el));",
"};",
"",
"if (!window._hljsInit) {",
" window._hljsInit = true;",
*(f" document.addEventListener('{event}', highlight);" for event in events),
"}",
"</script>",
)
)
)