## Understand the Problem
I currently have trouble following the SolveIt process while building FastHTML apps. I am able to build up an app's pieces as strings of HTML and think about if the strings are correct, but showing those pieces as rendered components in a notebook always trips me up.
from fasthtml.common import * from fasthtml.jupyter import * from IPython.display import display, HTML, IFrame
app, rt = fast_app()
server = JupyUvi(app)
if 'server' in globals(): server.stop()
def RectP(c): return P(c, style="border: 1px gray solid")
RectP("Hi")
Starting with the canonical docs:
After importing fasthtml.jupyter and calling render_ft(), FT components render directly in the notebook.
# render_ft()
RectP("Hi")
That worked! Though how do I view the string version of it now?
to_xml(RectP("Hello"))
That worked too!
It feels a bit weird to change what RectP
does. Ideally I can render the component only when I want to, not always. For now I am commenting out render_ft
above, so that I can restart this notebook's kernel and continue without it.
Let's explore what render_ft
does.
def render_ft(): @patch def _repr_markdown_(self:FT): return to_xml(Div(self, Script('if (window.htmx) htmx.process(document.body)')))
I asked AI: Wait so it patches a FT component's repr_markdown method?
Yes - it patches the _repr_markdown_
method of the FT
class, which is what Jupyter uses to determine how to display objects. The patch wraps the component in a Div
with an HTMX processing script, and converts it to XML. This is why components automatically render as HTML after calling render_ft()
.
That's also why to_xml()
still works to see the string version - it's a different method that isn't affected by the patch.
htmx.process()
tells HTMX to scan the given element (in this case document.body
) and initialize any HTMX attributes it finds. This is necessary when HTML is dynamically added to the page, as HTMX only automatically processes elements when the page first loads. Without this call, any HTMX attributes (like hx-get
, hx-post
, etc.) in dynamically rendered components wouldn't be activated.
Since Jupyter dynamically inserts our component's HTML into the notebook after the page has loaded, we need htmx.process()
to ensure any HTMX functionality in the component gets initialized. Even if our current simple RectP
component doesn't use HTMX features, the script is included by default since FastHTML components often do use HTMX attributes and we want them to work properly when rendered in the notebook.
def show_ft(c): html = to_xml(Div(c, Script('if (window.htmx) htmx.process(document.body)'))) display(HTML(html))
show_ft(RectP("Hi"))
Let's take a step back to understand IPython display, not worrying about JS for now.
display(HTML('<p style="border: 1px red solid">Hi</p>'))
RectP("Uma")
uma = RectP("Uma") uma
display(HTML(to_xml(uma)))
display(HTML(to_xml(RectP("Uma"))))
show
Functiondef show(c): return display(HTML(to_xml(c)))
show(RectP("Uma is a girl who loves crafts, science, and magic. "*20))
show
Output: MonsterUIfrom monsterui.all import Card
c = Card("I'm a card. "*20, header="Prepare yourself, it's coming", footer="Thank you for your attention") c
show(c)
I guess it would be nice to grab the card CSS without all of MonsterUI here, and put it into Style()
. I don't think MonsterUI has that feature, but Pygments does. I'll try that.
show
Output: Pygmentsfrom pygments import highlight from pygments.lexers import PythonLexer from pygments.formatters import HtmlFormatter
fm = HtmlFormatter(style='paraiso-light')
c = highlight('print("Hey")', lexer=PythonLexer(), formatter=fm) c
NotStr(c)
Div(c)
Div(NotStr(c))
Working examples directly below
display(HTML(to_xml(Div(NotStr(c),Style(fm.get_style_defs())))))
Note: the following cell only works when the previous cell works:
display(HTML(c))
def show_highlight(c): return display(HTML(to_xml(Div(NotStr(c),Style(fm.get_style_defs())))))
c = highlight('styles = L(pygments.styles.STYLES.items()).itemgot(1).itemgot(1)', lexer=PythonLexer(), formatter=fm) show_highlight(c)
def show_highlight(c): return display(HTML(to_xml(Div(NotStr(c),Style(fm.get_style_defs())))))
To be continued with scoped CSS...
show
Output: My Owndef RectP(c): return P(c, style="border:1px lightgray solid;padding:6px;margin:0;")
show(RectP("Hi, I'm Audrey. Danny and Uma are sleeping. "*20))
To be continued...