From 41962edefe6311c266c2ab5a2ff8e753fc650183 Mon Sep 17 00:00:00 2001 From: Isaac Flath Date: Sun, 20 Oct 2024 15:23:31 -0400 Subject: [PATCH] Docs updates --- {ex_nbs => docs}/99_main.ipynb | 270 ++++---- docs/API Reference.ipynb | 655 ++++++++++++++++++ {ex_nbs => docs}/GettingStarted.md | 0 docs/Utils.ipynb | 136 ++++ {ex_nbs => docs}/api_reference.py | 0 {ex_nbs => docs}/app/GettingStarted.md | 0 docs/app/api_reference.py | 239 +++++++ {ex_nbs => docs}/app/auth.py | 8 +- {ex_nbs => docs}/app/cards.py | 34 +- {ex_nbs => docs}/app/dashboard.py | 24 +- {ex_nbs => docs}/app/data/mail.json | 0 {ex_nbs => docs}/app/data/status_list.json | 0 {ex_nbs => docs}/app/data/statuses.json | 0 {ex_nbs => docs}/app/forms.py | 24 +- {ex_nbs => docs}/app/mail.py | 28 +- docs/app/main.py | 129 ++++ {ex_nbs => docs}/app/music.py | 30 +- {ex_nbs => docs}/app/playground.py | 14 +- {ex_nbs => docs}/app/requirements.txt | 0 {ex_nbs => docs}/app/tasks.py | 32 +- docs/app/utils.py | 57 ++ {ex_nbs => docs}/auth.py | 0 {ex_nbs => docs}/cards.py | 0 {ex_nbs => docs}/dashboard.py | 0 {ex_nbs => docs}/data | 0 .../06_auth.ipynb => docs/example_auth.ipynb | 19 +- .../example_cards.ipynb | 24 +- .../example_dashboard.ipynb | 17 +- .../example_forms.ipynb | 18 +- .../08_mail.ipynb => docs/example_mail.ipynb | 20 +- .../example_music.ipynb | 21 +- .../example_playground.ipynb | 20 +- .../example_tasks.ipynb | 22 +- {ex_nbs => docs}/forms.py | 0 {ex_nbs => docs}/mail.py | 0 {ex_nbs => docs}/main.py | 0 {ex_nbs => docs}/music.py | 0 {ex_nbs => docs}/playground.py | 0 {ex_nbs => docs}/requirements.txt | 0 {ex_nbs => docs}/tasks.py | 0 docs/utils.py | 1 + ex_nbs/API Reference.ipynb | 208 ------ ex_nbs/app/api_reference.py | 51 -- ex_nbs/app/main.py | 97 --- fh_frankenui/core.py | 11 +- lib_nbs/01_core.ipynb | 61 +- 46 files changed, 1521 insertions(+), 749 deletions(-) rename {ex_nbs => docs}/99_main.ipynb (53%) create mode 100644 docs/API Reference.ipynb rename {ex_nbs => docs}/GettingStarted.md (100%) create mode 100644 docs/Utils.ipynb rename {ex_nbs => docs}/api_reference.py (100%) rename {ex_nbs => docs}/app/GettingStarted.md (100%) create mode 100644 docs/app/api_reference.py rename {ex_nbs => docs}/app/auth.py (93%) rename {ex_nbs => docs}/app/cards.py (94%) rename {ex_nbs => docs}/app/dashboard.py (90%) rename {ex_nbs => docs}/app/data/mail.json (100%) rename {ex_nbs => docs}/app/data/status_list.json (100%) rename {ex_nbs => docs}/app/data/statuses.json (100%) rename {ex_nbs => docs}/app/forms.py (95%) rename {ex_nbs => docs}/app/mail.py (93%) create mode 100644 docs/app/main.py rename {ex_nbs => docs}/app/music.py (93%) rename {ex_nbs => docs}/app/playground.py (92%) rename {ex_nbs => docs}/app/requirements.txt (100%) rename {ex_nbs => docs}/app/tasks.py (92%) create mode 100644 docs/app/utils.py rename {ex_nbs => docs}/auth.py (100%) rename {ex_nbs => docs}/cards.py (100%) rename {ex_nbs => docs}/dashboard.py (100%) rename {ex_nbs => docs}/data (100%) rename ex_nbs/06_auth.ipynb => docs/example_auth.ipynb (92%) rename ex_nbs/02_cards.ipynb => docs/example_cards.ipynb (96%) rename ex_nbs/03_dashboard.ipynb => docs/example_dashboard.ipynb (98%) rename ex_nbs/04_forms.ipynb => docs/example_forms.ipynb (98%) rename ex_nbs/08_mail.ipynb => docs/example_mail.ipynb (94%) rename ex_nbs/05_music.ipynb => docs/example_music.ipynb (95%) rename ex_nbs/07_playground.ipynb => docs/example_playground.ipynb (93%) rename ex_nbs/01_tasks.ipynb => docs/example_tasks.ipynb (94%) rename {ex_nbs => docs}/forms.py (100%) rename {ex_nbs => docs}/mail.py (100%) rename {ex_nbs => docs}/main.py (100%) rename {ex_nbs => docs}/music.py (100%) rename {ex_nbs => docs}/playground.py (100%) rename {ex_nbs => docs}/requirements.txt (100%) rename {ex_nbs => docs}/tasks.py (100%) create mode 120000 docs/utils.py delete mode 100644 ex_nbs/API Reference.ipynb delete mode 100644 ex_nbs/app/api_reference.py delete mode 100644 ex_nbs/app/main.py diff --git a/ex_nbs/99_main.ipynb b/docs/99_main.ipynb similarity index 53% rename from ex_nbs/99_main.ipynb rename to docs/99_main.ipynb index 9999341..9c18755 100644 --- a/ex_nbs/99_main.ipynb +++ b/docs/99_main.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 94, "id": "0cc276d0", "metadata": {}, "outputs": [], @@ -30,7 +30,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 95, "id": "d5abdcb0", "metadata": {}, "outputs": [], @@ -41,61 +41,86 @@ "from fh_frankenui.components import *\n", "from fh_frankenui.core import *\n", "import re\n", - "from fasthtml.components import Uk_theme_switcher" + "from fasthtml.components import Uk_theme_switcher\n", + "from utils import hjs" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 96, "id": "9238fa2a", "metadata": {}, "outputs": [], "source": [ "#| hide\n", "#| eval: false\n", - "from isaac_research.core import create_server" + "from utils import create_server\n", + "from fasthtml.jupyter import *" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 129, "id": "9b2ce5c5", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "app,rt = fast_app(hdrs=Theme.blue.headers(), pico=False)" + "\n", + "app,rt = fast_app(pico=False, hdrs=(*Theme.blue.headers(),*hjs))" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 98, "id": "d53a9e4d", "metadata": {}, "outputs": [], "source": [ "#| hide\n", "#| eval: false\n", + "\n", "server, Show = create_server(app)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "33b9cbb3", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def with_layout(func):\n", + " def wrapper():\n", + " original_content = func()\n", + " return Body(cls=\"bg-background text-foreground\")(\n", + " Div(cls=\"flex flex-col md:flex-row w-full\")(\n", + " Button(UkIcon(\"menu\",50,50,cls='mt-4'), cls=\"md:hidden mb-4\", uk_toggle=\"target: #mobile-sidebar\"),\n", + " Div(sidebar, id='mobile-sidebar', hidden=True),\n", + " Div(cls=\"md:flex w-full\")(\n", + " Div(sidebar, cls=\"hidden md:block\"),\n", + " Div(original_content, cls='w-full', id=\"mobile-sidebar\")\n", + " )\n", + " ),\n", + " )\n", + " wrapper.__name__ = func.__name__\n", + " return wrapper" + ] + }, { "cell_type": "markdown", - "id": "4819b3b5", - "metadata": { - "heading_collapsed": true - }, + "id": "661a800c", + "metadata": {}, "source": [ "## Examples" ] }, { "cell_type": "code", - "execution_count": 30, - "id": "25ddcd89", - "metadata": { - "hidden": true - }, + "execution_count": 100, + "id": "52e3819e", + "metadata": {}, "outputs": [], "source": [ "#| export\n", @@ -111,55 +136,58 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 101, "id": "77bc464f", - "metadata": { - "hidden": true - }, + "metadata": {}, "outputs": [], "source": [ "#| export\n", "@rt\n", + "@with_layout\n", "def tasks(): return tasks_homepage\n", "@rt\n", + "@with_layout\n", "def cards(): return cards_homepage\n", "@rt\n", + "@with_layout\n", "def dashboard(): return dashboard_homepage\n", "@rt \n", + "@with_layout\n", "def forms(): return forms_homepage\n", "@rt\n", + "@with_layout\n", "def music(): return music_homepage\n", "@rt\n", + "@with_layout\n", "def auth(): return auth_homepage\n", "@rt\n", + "@with_layout\n", "def playground(): return playground_homepage\n", "@rt\n", + "@with_layout\n", "def mail(): return mail_homepage" ] }, { "cell_type": "markdown", "id": "d25174b5", - "metadata": { - "heading_collapsed": true - }, + "metadata": {}, "source": [ "## Getting Started" ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 102, "id": "7f0694dd", - "metadata": { - "hidden": true - }, + "metadata": {}, "outputs": [], "source": [ "#| export\n", "@rt\n", + "@with_layout\n", "def getting_started():\n", - " return render_md(open('GettingStarted.md').read())" + " return Container(render_md(open('GettingStarted.md').read()))" ] }, { @@ -172,27 +200,62 @@ }, { "cell_type": "code", - "execution_count": 33, - "id": "4e9c3978", + "execution_count": 103, + "id": "1ee741bc", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "from api_reference import docs_button, docs_heading" + "import api_reference" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "id": "9e464c60", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(#7) ['docs_articles_containers_sections','docs_button','docs_cards','docs_headers','docs_heading','docs_lists','docs_textstyle']" + ] + }, + "execution_count": 104, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#| export\n", + "reference_fns = L([o for o in dir(api_reference) if o.startswith('docs_')])\n", + "reference_fns" ] }, { "cell_type": "code", - "execution_count": 34, - "id": "57d52b36", + "execution_count": 105, + "id": "71c1ef02", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "@rt\n", - "def buttons(): return Container(docs_button)\n", - "@rt\n", - "def headings(): return Container(docs_heading)" + "def fnname2title(ref_fn_name): return ref_fn_name[5:].replace('_',' | ').title() " + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "id": "b2108baa", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "#| export\n", + "api_ref_rts = [(f\"/{o}\", app.add_route(f\"/{o}\", with_layout(getattr(api_reference, o)))) for o in reference_fns]" ] }, { @@ -208,137 +271,88 @@ { "cell_type": "markdown", "id": "25c2fb51", - "metadata": { - "heading_collapsed": true - }, + "metadata": {}, "source": [ "## Theme" ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 107, "id": "ad65f276", - "metadata": { - "hidden": true - }, + "metadata": {}, "outputs": [], "source": [ "#| export\n", "@rt\n", + "@with_layout\n", "def themeswitcher(): return Div(Uk_theme_switcher(),cls=\"p-12\")" ] }, { "cell_type": "markdown", - "id": "cb19d7b7", + "id": "28525f93", "metadata": {}, "source": [ - "## Main Page" + "# Page Layout" ] }, { "cell_type": "code", - "execution_count": 37, - "id": "d463e684", + "execution_count": 108, + "id": "1b257864", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "@rt\n", - "def index():\n", - " return Body(cls=\"bg-background text-foreground\")(Div(cls=\"flex w-full\")(\n", - " NavContainer(\n", - " Li(A(\"Getting Started\", hx_get=getting_started, hx_target='#content')),\n", - " NavParentLi(\n", - " A(\"API Reference\"),\n", - " NavContainer(\n", - " Li(A('Buttons',hx_get=buttons, hx_target='#content')),\n", - " Li(A('Heading',hx_get=headings, hx_target='#content')),\n", - " parent=False\n", - " )),\n", - " NavParentLi(\n", - " A('Examples'),\n", - " NavContainer(\n", - " Li(A('Tasks', hx_get=tasks, hx_target='#content')),\n", - " Li(A('Cards', hx_get=cards, hx_target='#content')),\n", - " Li(A('Dashboard', hx_get=dashboard, hx_target='#content')),\n", - " Li(A('Form', hx_get=forms, hx_target='#content')),\n", - " Li(A('Music', hx_get=music, hx_target='#content')),\n", - " Li(A('Auth', hx_get=auth, hx_target='#content')),\n", - " Li(A('Playground',hx_get=playground, hx_target='#content')),\n", - " Li(A('Mail', hx_get=mail, hx_target='#content')),\n", - " parent=False)),\n", - " Li(A(\"Theme\",hx_get=themeswitcher, hx_target='#content')),\n", + "sidebar = NavContainer(\n", + " Li(A(\"Getting Started\", href=getting_started)),\n", + " NavParentLi(\n", + " A(FullySpacedDiv(\"API Reference\",UkIcon('chevron-down'))),\n", + " NavContainer(\n", + " *[Li(A(fnname2title(o), href=f\"/{o}\")) for o in reference_fns],\n", + " parent=False),\n", + " ),\n", + " NavParentLi(\n", + " A(FullySpacedDiv('Examples',UkIcon('chevron-down'))),\n", + " NavContainer(\n", + " Li(A('Tasks', href=tasks)),\n", + " Li(A('Cards', href=cards)),\n", + " Li(A('Dashboard', href=dashboard)),\n", + " Li(A('Form', href=forms)),\n", + " Li(A('Music', href=music)),\n", + " Li(A('Auth', href=auth)),\n", + " Li(A('Playground',href=playground)),\n", + " Li(A('Mail', href=mail)),\n", + " parent=False),\n", + " ),\n", + " Li(A(\"Theme\",href=themeswitcher)),\n", "\n", - " uk_nav=True, cls=(NavT.primary,\"space-y-4 p-4 w-1/7\")),\n", - " Div(getting_started(), id='content',cls='w-full')))" + " uk_nav=False, cls=(NavT.primary,\"space-y-4 p-4 w-full md:w-1/5\"))" ] }, { "cell_type": "code", - "execution_count": 38, - "id": "94e3c1e4", + "execution_count": 71, + "id": "d463e684", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "serve()" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "id": "4b0c1895", - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "text/html": [ - "Open in new tab" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "#| hide\n", - "#| eval: false\n", - "Show(index(), iframe_height='750px')" + "@rt\n", + "def index():return getting_started()" ] }, { "cell_type": "code", - "execution_count": null, - "id": "6644c3cb", + "execution_count": 53, + "id": "94e3c1e4", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "#| export\n", + "serve()" + ] } ], "metadata": { diff --git a/docs/API Reference.ipynb b/docs/API Reference.ipynb new file mode 100644 index 0000000..e78dfb4 --- /dev/null +++ b/docs/API Reference.ipynb @@ -0,0 +1,655 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d658774c", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# API Reference\n", + "\n", + "> Reference to all FrankenUI Components" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "51edf9a5", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| default_exp api_reference" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "46a13904", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "from fasthtml.common import *\n", + "\n", + "from fh_frankenui.core import *\n", + "from nbdev.showdoc import *\n", + "import inspect\n", + "\n", + "from enum import EnumType\n", + "from collections.abc import Callable" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a74595d2", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| hide\n", + "#| eval: false\n", + "from utils import create_server" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "60b096aa", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| hide\n", + "#| eval: false\n", + "app, rt = fast_app(pico=False, hdrs=(*Theme.blue.headers()))\n", + "server, Show = create_server(app)\n" + ] + }, + { + "cell_type": "markdown", + "id": "f2090c46", + "metadata": {}, + "source": [ + "# Helper Functions" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "2cf43a87", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def enum_to_html_table(enum_class):\n", + " headers = [\"Option\", \"Value\"]\n", + " rows = [[name, value.value] for name, value in enum_class.__members__.items()]\n", + " return Div(\n", + " Hr(cls='uk-divider-icon my-4'),\n", + " H3(enum_class.__name__,cls='my-4'),\n", + " P(I(enum_class.__doc__)),\n", + " TableFromLists(headers, rows, cls=(TableT.hover, 'uk-table-small')),)" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "01e97137", + "metadata": {}, + "outputs": [], + "source": [ + "%%ai\n", + "\n", + "I want 2 names for a button that toggle between showing code and showing the rendered version of the code. But I want the words to be the same lenght. What are some good options?" + ] + }, + { + "cell_type": "markdown", + "id": "0bc4f04f", + "metadata": {}, + "source": [ + "Here are two pairs of names for a toggle button that switch between showing code and its rendered version, with each pair having words of equal length:\n", + "\n", + "1. \"Code\" and \"View\"\n", + "2. \"Source\" and \"Output\"\n", + "\n", + "Both pairs maintain the same character count within each option, ensuring visual consistency when toggling." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "7fd3baa5", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "from uuid import uuid4\n", + "def render_content(c):\n", + " if isinstance(c, str): return render_md(c)\n", + " elif isinstance(c, EnumType): return enum_to_html_table(c)\n", + " elif isinstance(c, FT): return c\n", + " elif isinstance(c, tuple): \n", + " _id = 'f'+str(uuid4())\n", + " return Card(\n", + " Button(FullySpacedDiv(\"See Source\",UkIcon('corner-down-right', 20, 20, 3)), uk_toggle=f\"target: #{_id}\", id=_id, cls=ButtonT.primary),\n", + " Button(FullySpacedDiv(\"See Output\",UkIcon('corner-down-right', 20, 20, 3)), uk_toggle=f\"target: #{_id}\", id=_id, cls=ButtonT.primary, hidden=True),\n", + " Div(c[0], id=_id),\n", + " Div(Pre(Code(c[1])), id=_id, hidden=True, cls=\"mockup-code\"),\n", + " cls='my-8')\n", + " elif isinstance(c, Callable): \n", + " _html = show_doc(c, renderer=BasicHtmlRenderer)._repr_html_()\n", + " return NotStr(apply_classes(_html, class_map_mods={\"table\":'uk-table uk-table-hover uk-table-small'}))\n", + " else: return c " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "84a3b655", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def create_doc_section(*content, title, md_content=None):\n", + " return lambda: Section(H1(title,cls='mb-10'), *map(render_content, content))" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "id": "2eee17cf", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def string2code_string(code: str) -> tuple: return eval(code), code\n", + "\n", + "def extract_function_body(func):\n", + " source = inspect.getsource(func)\n", + " body_start = source.index(':') + 1\n", + " body = source[body_start:]\n", + " return body.replace('return ', '', 1)\n", + "\n", + "def fn2code_string(fn: Callable) -> tuple: return fn(), extract_function_body(fn)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b52923e", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "c29c05f9", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# Buttons " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fe88ca5", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "def ex_buttons(): \n", + " return Div(\n", + " Button(\"Default\", cls=ButtonT.default),\n", + " Button(\"Primary\", cls=ButtonT.primary),\n", + " Button(\"Secondary\", cls=ButtonT.secondary),\n", + " Button(\"Danger\", cls=ButtonT.danger),\n", + " Button(\"Text\", cls=ButtonT.text),\n", + " Button(\"Link\", cls=ButtonT.link),\n", + " Button(\"Ghost\", cls=ButtonT.ghost),\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "4408b9e0", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "docs_button = create_doc_section(Button, \n", + " ButtonT, \n", + " fn2code_string(ex_buttons),\n", + " title=\"Buttons\")" + ] + }, + { + "cell_type": "markdown", + "id": "cfc93853", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# Headings" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47bd9c48", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "def ex_headings():\n", + " return Div(\n", + " H1(\"Level 1 Heading (H1)\"), \n", + " H2(\"Level 2 Heading (H2)\"), \n", + " H3(\"Level 3 Heading (H3)\"), \n", + " H4(\"Level 4 Heading (H4)\")\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "9b56ffc1", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "docs_heading = create_doc_section(\n", + " fn2code_string(ex_headings),\n", + " H1, H2, H3, H4, \n", + " title=\"Headings\")" + ] + }, + { + "cell_type": "markdown", + "id": "c4fea3cf", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# Headers" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "1c205cec", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "docs_headers = create_doc_section( \n", + " \"To get headers with a default theme use `hdrs=Theme..headers()`. For example for the blue theme you would use `hdrs=Theme.blue.headers(). Theme options are:\",\n", + " \"> Note: Tailwind is included in the headers for convenience\",\n", + " Theme,\n", + " title=\"Headers\")" + ] + }, + { + "cell_type": "markdown", + "id": "fd128781", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# Text Style" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c7c17539", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "def ex_textfont():\n", + " return Div(\n", + " P('muted_sm', cls=TextFont.muted_sm),\n", + " P('muted_lg', cls=TextFont.muted_lg), \n", + " P('bold_sm', cls=TextFont.bold_sm),\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a34ee45", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "def ex_textt():\n", + " return Grid(\n", + " P('lead', cls=TextT.lead),\n", + " P('meta', cls=TextT.meta),\n", + " P('italic', cls=TextT.italic),\n", + " P('small', cls=TextT.small),\n", + " P('default', cls=TextT.default),\n", + " P('large', cls=TextT.large),\n", + " P('light', cls=TextT.light),\n", + " P('normal', cls=TextT.normal),\n", + " P('bold', cls=TextT.bold),\n", + " P('lighter', cls=TextT.lighter),\n", + " P('bolder', cls=TextT.bolder),\n", + " P('capitalize', cls=TextT.capitalize),\n", + " P('uppercase', cls=TextT.uppercase),\n", + " P('lowercase', cls=TextT.lowercase),\n", + " P('decoration_none',cls=TextT.decoration_none),\n", + " P('muted', cls=TextT.muted),\n", + " P('primary', cls=TextT.primary),\n", + " P('secondary', cls=TextT.secondary),\n", + " P('success', cls=TextT.success),\n", + " P('warning', cls=TextT.warning),\n", + " P('danger', cls=TextT.danger),\n", + " P('left', cls=TextT.left),\n", + " P('right', cls=TextT.right),\n", + " P('center', cls=TextT.center),\n", + " P('justify', cls=TextT.justify),\n", + " P('top', cls=TextT.top),\n", + " P('middle', cls=TextT.middle),\n", + " P('bottom', cls=TextT.bottom),\n", + " P('baseline', cls=TextT.baseline),\n", + " P('truncate', cls=TextT.truncate),\n", + " P('break_', cls=TextT.break_),\n", + " P('nowrap', cls=TextT.nowrap),\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "0bfd6f7a", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "docs_textstyle = create_doc_section( \n", + " \"Styling text is possibly the most common style thing to do, so we have a couple of helpers for discoverability inside python. `TextFont` is intended to be combinations are are widely applicable and used often, where `TextT` is intended to be more flexible options for you to combine together yourself.\",\n", + " TextFont,\n", + " fn2code_string(ex_textfont),\n", + " TextT,\n", + " fn2code_string(ex_textt),\n", + " title=\"Text Style\")" + ] + }, + { + "cell_type": "markdown", + "id": "4f8e5fb9", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# Articles | Containers | Sections" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "d8393a7a", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "def ex_articles():\n", + " return Article(\n", + " ArticleTitle(\"Sample Article Title\"), \n", + " ArticleMeta(\"By: John Doe\"),\n", + " P('lorem ipsum dolor sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d489a5ec", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "def ex_containers():\n", + " return Container(\n", + " \"This is a sample container with custom styling.\",\n", + " cls=ContainerT.xsmall,\n", + " style=\"background-color: #FFA500; color: #000000\")" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "970fe071", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "docs_articles_containers_sections = create_doc_section(\n", + " ArticleMeta,\n", + " ArticleTitle,\n", + " Article,\n", + " fn2code_string(ex_articles),\n", + " Container,\n", + " ContainerT,\n", + " fn2code_string(ex_containers),\n", + " Section,\n", + " SectionT,\n", + " title=\"Articles, Containers & Sections\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "6c6538b4", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# Cards" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dce45f9e", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "def ex_card():\n", + " return Card(\n", + " Form(LabelInput(\"Input\"),\n", + " LabelRange(\"Range\")),\n", + " header=Div(\n", + " CardTitle(\"Header\"),\n", + " P(\"A card with header and footer\",cls=TextFont.muted_sm)),\n", + " footer=LAlignedDiv(Button(\"Footer Submit Button\")))" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "74a3d9df", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "def Tags(cats): return Div(cls='space-x-2')(map(Label, cats))\n", + "\n", + "def ex_card2():\n", + " return Card(\n", + " LAlignedDiv(\n", + " A(Img(src=\"https://isaac-flath.github.io/website/posts/_TopicImages/FastHtml.jpg\", style=\"width:200px\"),href=\"#\"),\n", + " Div(cls='space-y-3 uk-width-expand')(\n", + " H4(\"Creating Custom FastHTML Tags for Markdown Rendering\"),\n", + " P(\"A step by step tutorial to rendering markdown in FastHTML using zero-md inside of DaisyUI chat bubbles\"),\n", + " FullySpacedDiv(map(Span, [\"Isaac Flath\", \"20-October-2024\"]), cls=TextFont.muted_sm),\n", + " FullySpacedDiv(\n", + " Tags([\"FastHTML\", \"HTMX\", \"Web Apps\"]),\n", + " Button(\"Read\", cls=(ButtonT.primary,'h-6'))))))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e422761", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "docs_cards = create_doc_section(\n", + " Card,\n", + " H3(\"Example Usage\"),\n", + " fn2code_string(ex_card),\n", + " fn2code_string(ex_card2),\n", + " CardTitle,\n", + " CardT,\n", + " \"The remainder of these are only needed if you're doing something really special. They are used in the `Card` function to generate the boilerplate for you.\",\n", + " CardContainer,\n", + " CardHeader,\n", + " CardBody,\n", + " CardFooter,\n", + " title=\"Cards\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "427392cd", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# Lists\n" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "id": "0a01debd", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export \n", + "def ex_lists():\n", + " list_options = [(style,str(cls)) for style,cls in ListT.__members__.items()]\n", + " lists = [Div(H4(f\"{style} List:\"), List(Li(\"Item 1\"), Li(\"Item 2\"), cls=cls)) for style, cls in list_options]\n", + " return Grid(*lists)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "274a82c0", + "metadata": { + "hidden": true + }, + "outputs": [], + "source": [ + "#| export\n", + "docs_lists = create_doc_section(\n", + " List,\n", + " fn2code_string(ex_lists),\n", + " ListT,\n", + " title=\"Lists\")" + ] + }, + { + "cell_type": "markdown", + "id": "c0e0c543", + "metadata": { + "heading_collapsed": true + }, + "source": [ + "# Links (A)" + ] + }, + { + "cell_type": "markdown", + "id": "66650e8f", + "metadata": {}, + "source": [ + "# _" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c8f374c", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8de06f7", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ex_nbs/GettingStarted.md b/docs/GettingStarted.md similarity index 100% rename from ex_nbs/GettingStarted.md rename to docs/GettingStarted.md diff --git a/docs/Utils.ipynb b/docs/Utils.ipynb new file mode 100644 index 0000000..7b77048 --- /dev/null +++ b/docs/Utils.ipynb @@ -0,0 +1,136 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e79166fd", + "metadata": {}, + "source": [ + "# Utils\n", + "\n", + "> Utilities for building the docs page that don't belong anywhere else" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2535ee4e", + "metadata": {}, + "outputs": [], + "source": [ + "#| default_exp utils" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4783d9ce", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "from IPython.display import display, HTML\n", + "from fasthtml.common import *\n", + "from fh_frankenui.core import *\n", + "from fasthtml.jupyter import *\n", + "from uuid import uuid4" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b2943fa8", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def HShow(comp, iframe_height=\"auto\", app=None, port=8000):\n", + " route = f'/{uuid4()}'\n", + " app.get(route)(lambda: comp)\n", + " display(HTML(f'Open in new tab'))\n", + " return HTMX(route,port=port,iframe_height=iframe_height)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "bfa48e0b", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def create_server(app, stop_server=True,server_varname='server'):\n", + " if stop_server and server_varname in globals(): globals()[server_varname].stop()\n", + " for port in range(8000,8030): \n", + " if is_port_free(port):\n", + " server = JupyUvi(app, port=port)\n", + " Show = partial(HShow, app=app, port=port)\n", + " return server, Show" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b8e1807f", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "hjs = (Style('html.dark .hljs-copy-button {background-color: #e0e0e0; color: #2d2b57;}'),\n", + " Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/atom-one-dark.css', disabled=True),\n", + " Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/atom-one-light.css', disabled=True),\n", + " Script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js'),\n", + " Script(src='https://cdn.jsdelivr.net/gh/arronhunt/highlightjs-copy/dist/highlightjs-copy.min.js'),\n", + " Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/arronhunt/highlightjs-copy/dist/highlightjs-copy.min.css'),\n", + " Style('.hljs-copy-button {background-color: #2d2b57;}'),\n", + " Script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/languages/python.min.js'),\n", + " Script(\"hljs.addPlugin(new CopyButtonPlugin());\\r\\nhljs.configure({'cssSelector': 'pre code'});\\r\\nhtmx.onLoad(hljs.highlightAll);\", type='module'),\n", + " Script('''const observer = new MutationObserver(mutations => {\n", + " mutations.forEach(mutation => {\n", + " if (mutation.target.tagName === 'HTML' && mutation.attributeName === 'class') {\n", + " const isDark = mutation.target.classList.contains('dark');\n", + " document.querySelector('link[href*=\"atom-one-dark.css\"]').disabled = !isDark;\n", + " document.querySelector('link[href*=\"atom-one-light.css\"]').disabled = isDark;\n", + " }\n", + " });\n", + " });\n", + "\n", + " observer.observe(document.documentElement, { attributes: true });\n", + "\n", + " // Initial setup\n", + " const isDark = document.documentElement.classList.contains('dark');\n", + " document.querySelector('link[href*=\"atom-one-dark.css\"]').disabled = !isDark;\n", + " document.querySelector('link[href*=\"atom-one-light.css\"]').disabled = isDark;\n", + " '''))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6a70a7a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ex_nbs/api_reference.py b/docs/api_reference.py similarity index 100% rename from ex_nbs/api_reference.py rename to docs/api_reference.py diff --git a/ex_nbs/app/GettingStarted.md b/docs/app/GettingStarted.md similarity index 100% rename from ex_nbs/app/GettingStarted.md rename to docs/app/GettingStarted.md diff --git a/docs/app/api_reference.py b/docs/app/api_reference.py new file mode 100644 index 0000000..2358369 --- /dev/null +++ b/docs/app/api_reference.py @@ -0,0 +1,239 @@ +"""Reference to all FrankenUI Components""" + +# AUTOGENERATED! DO NOT EDIT! File to edit: ../API Reference.ipynb. + +# %% auto 0 +__all__ = ['docs_button', 'docs_heading', 'docs_headers', 'docs_textstyle', 'docs_articles_containers_sections', 'docs_cards', + 'docs_lists', 'enum_to_html_table', 'render_content', 'create_doc_section', 'string2code_string', + 'extract_function_body', 'fn2code_string', 'ex_buttons', 'ex_headings', 'ex_textfont', 'ex_textt', + 'ex_articles', 'ex_containers', 'ex_card', 'Tags', 'ex_card2', 'ex_lists'] + +# %% ../API Reference.ipynb +from fasthtml.common import * + +from fh_frankenui.core import * +from nbdev.showdoc import * +import inspect + +from enum import EnumType +from collections.abc import Callable + +# %% ../API Reference.ipynb +def enum_to_html_table(enum_class): + headers = ["Option", "Value"] + rows = [[name, value.value] for name, value in enum_class.__members__.items()] + return Div( + Hr(cls='uk-divider-icon my-4'), + H3(enum_class.__name__,cls='my-4'), + P(I(enum_class.__doc__)), + TableFromLists(headers, rows, cls=(TableT.hover, 'uk-table-small')),) + +# %% ../API Reference.ipynb +from uuid import uuid4 +def render_content(c): + if isinstance(c, str): return render_md(c) + elif isinstance(c, EnumType): return enum_to_html_table(c) + elif isinstance(c, FT): return c + elif isinstance(c, tuple): + _id = 'f'+str(uuid4()) + return Card( + Button(FullySpacedDiv("See Source",UkIcon('corner-down-right', 20, 20, 3)), uk_toggle=f"target: #{_id}", id=_id, cls=ButtonT.primary), + Button(FullySpacedDiv("See Output",UkIcon('corner-down-right', 20, 20, 3)), uk_toggle=f"target: #{_id}", id=_id, cls=ButtonT.primary, hidden=True), + Div(c[0], id=_id), + Div(Pre(Code(c[1])), id=_id, hidden=True, cls="mockup-code"), + cls='my-8') + elif isinstance(c, Callable): + _html = show_doc(c, renderer=BasicHtmlRenderer)._repr_html_() + return NotStr(apply_classes(_html, class_map_mods={"table":'uk-table uk-table-hover uk-table-small'})) + else: return c + +# %% ../API Reference.ipynb +def create_doc_section(*content, title, md_content=None): + return lambda: Section(H1(title,cls='mb-10'), *map(render_content, content)) + +# %% ../API Reference.ipynb +def string2code_string(code: str) -> tuple: return eval(code), code + +def extract_function_body(func): + source = inspect.getsource(func) + body_start = source.index(':') + 1 + body = source[body_start:] + return body.replace('return ', '', 1) + +def fn2code_string(fn: Callable) -> tuple: return fn(), extract_function_body(fn) + +# %% ../API Reference.ipynb +def ex_buttons(): + return Div( + Button("Default", cls=ButtonT.default), + Button("Primary", cls=ButtonT.primary), + Button("Secondary", cls=ButtonT.secondary), + Button("Danger", cls=ButtonT.danger), + Button("Text", cls=ButtonT.text), + Button("Link", cls=ButtonT.link), + Button("Ghost", cls=ButtonT.ghost), + ) + +# %% ../API Reference.ipynb +docs_button = create_doc_section(Button, + ButtonT, + fn2code_string(ex_buttons), + title="Buttons") + +# %% ../API Reference.ipynb +def ex_headings(): + return Div( + H1("Level 1 Heading (H1)"), + H2("Level 2 Heading (H2)"), + H3("Level 3 Heading (H3)"), + H4("Level 4 Heading (H4)") + ) + +# %% ../API Reference.ipynb +docs_heading = create_doc_section( + fn2code_string(ex_headings), + H1, H2, H3, H4, + title="Headings") + +# %% ../API Reference.ipynb +docs_headers = create_doc_section( + "To get headers with a default theme use `hdrs=Theme..headers()`. For example for the blue theme you would use `hdrs=Theme.blue.headers(). Theme options are:", + "> Note: Tailwind is included in the headers for convenience", + Theme, + title="Headers") + +# %% ../API Reference.ipynb +def ex_textfont(): + return Div( + P('muted_sm', cls=TextFont.muted_sm), + P('muted_lg', cls=TextFont.muted_lg), + P('bold_sm', cls=TextFont.bold_sm), + ) + +# %% ../API Reference.ipynb +def ex_textt(): + return Grid( + P('lead', cls=TextT.lead), + P('meta', cls=TextT.meta), + P('italic', cls=TextT.italic), + P('small', cls=TextT.small), + P('default', cls=TextT.default), + P('large', cls=TextT.large), + P('light', cls=TextT.light), + P('normal', cls=TextT.normal), + P('bold', cls=TextT.bold), + P('lighter', cls=TextT.lighter), + P('bolder', cls=TextT.bolder), + P('capitalize', cls=TextT.capitalize), + P('uppercase', cls=TextT.uppercase), + P('lowercase', cls=TextT.lowercase), + P('decoration_none',cls=TextT.decoration_none), + P('muted', cls=TextT.muted), + P('primary', cls=TextT.primary), + P('secondary', cls=TextT.secondary), + P('success', cls=TextT.success), + P('warning', cls=TextT.warning), + P('danger', cls=TextT.danger), + P('left', cls=TextT.left), + P('right', cls=TextT.right), + P('center', cls=TextT.center), + P('justify', cls=TextT.justify), + P('top', cls=TextT.top), + P('middle', cls=TextT.middle), + P('bottom', cls=TextT.bottom), + P('baseline', cls=TextT.baseline), + P('truncate', cls=TextT.truncate), + P('break_', cls=TextT.break_), + P('nowrap', cls=TextT.nowrap), + ) + +# %% ../API Reference.ipynb +docs_textstyle = create_doc_section( + "Styling text is possibly the most common style thing to do, so we have a couple of helpers for discoverability inside python. `TextFont` is intended to be combinations are are widely applicable and used often, where `TextT` is intended to be more flexible options for you to combine together yourself.", + TextFont, + fn2code_string(ex_textfont), + TextT, + fn2code_string(ex_textt), + title="Text Style") + +# %% ../API Reference.ipynb +def ex_articles(): + return Article( + ArticleTitle("Sample Article Title"), + ArticleMeta("By: John Doe"), + P('lorem ipsum dolor sit amet consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.')) + +# %% ../API Reference.ipynb +def ex_containers(): + return Container( + "This is a sample container with custom styling.", + cls=ContainerT.xsmall, + style="background-color: #FFA500; color: #000000") + +# %% ../API Reference.ipynb +docs_articles_containers_sections = create_doc_section( + ArticleMeta, + ArticleTitle, + Article, + fn2code_string(ex_articles), + Container, + ContainerT, + fn2code_string(ex_containers), + Section, + SectionT, + title="Articles, Containers & Sections" +) + +# %% ../API Reference.ipynb +def ex_card(): + return Card( + Form(LabelInput("Input"), + LabelRange("Range")), + header=Div( + CardTitle("Header"), + P("A card with header and footer",cls=TextFont.muted_sm)), + footer=LAlignedDiv(Button("Footer Submit Button"))) + +# %% ../API Reference.ipynb +def Tags(cats): return Div(cls='space-x-2')(map(Label, cats)) + +def ex_card2(): + return Card( + LAlignedDiv( + A(Img(src="https://isaac-flath.github.io/website/posts/_TopicImages/FastHtml.jpg", style="width:200px"),href="#"), + Div(cls='space-y-3 uk-width-expand')( + H4("Creating Custom FastHTML Tags for Markdown Rendering"), + P("A step by step tutorial to rendering markdown in FastHTML using zero-md inside of DaisyUI chat bubbles"), + FullySpacedDiv(map(Span, ["Isaac Flath", "20-October-2024"]), cls=TextFont.muted_sm), + FullySpacedDiv( + Tags(["FastHTML", "HTMX", "Web Apps"]), + Button("Read", cls=(ButtonT.primary,'h-6')))))) + +# %% ../API Reference.ipynb +docs_cards = create_doc_section( + Card, + H3("Example Usage"), + fn2code_string(ex_card), + fn2code_string(ex_card2), + CardTitle, + CardT, + "The remainder of these are only needed if you're doing something really special. They are used in the `Card` function to generate the boilerplate for you.", + CardContainer, + CardHeader, + CardBody, + CardFooter, + title="Cards" +) + +# %% ../API Reference.ipynb +def ex_lists(): + list_options = [(style,str(cls)) for style,cls in ListT.__members__.items()] + lists = [Div(H4(f"{style} List:"), List(Li("Item 1"), Li("Item 2"), cls=cls)) for style, cls in list_options] + return Grid(*lists) + +# %% ../API Reference.ipynb +docs_lists = create_doc_section( + List, + fn2code_string(ex_lists), + ListT, + title="Lists") diff --git a/ex_nbs/app/auth.py b/docs/app/auth.py similarity index 93% rename from ex_nbs/app/auth.py rename to docs/app/auth.py index f701662..239bbe0 100644 --- a/ex_nbs/app/auth.py +++ b/docs/app/auth.py @@ -1,9 +1,9 @@ -# AUTOGENERATED! DO NOT EDIT! File to edit: ../06_auth.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../example_auth.ipynb. # %% auto 0 __all__ = ['auth_homepage', 'page'] -# %% ../06_auth.ipynb +# %% ../example_auth.ipynb from fasthtml.common import * import fasthtml.common as fh from fh_frankenui import * @@ -13,7 +13,7 @@ from fasthtml.svg import * -# %% ../06_auth.ipynb +# %% ../example_auth.ipynb def page(): left = Div(cls="col-span-1 hidden flex-col justify-between bg-zinc-900 p-8 text-white lg:flex")( Div(cls=(TextT.bold,TextT.default))("Acme Inc"), @@ -40,5 +40,5 @@ def page(): return Grid(left,right,cols=2, gap=0,cls='h-screen') -# %% ../06_auth.ipynb +# %% ../example_auth.ipynb auth_homepage = page() diff --git a/ex_nbs/app/cards.py b/docs/app/cards.py similarity index 94% rename from ex_nbs/app/cards.py rename to docs/app/cards.py index 2639719..09122e0 100644 --- a/ex_nbs/app/cards.py +++ b/docs/app/cards.py @@ -1,13 +1,13 @@ """FrankenUI Cards Example""" -# AUTOGENERATED! DO NOT EDIT! File to edit: ../02_cards.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../example_cards.ipynb. # %% auto 0 __all__ = ['CreateAccount', 'Card1Svg', 'Card2Svg', 'AppleSvg', 'PaymentMethod', 'area_opts', 'severity_opts', 'ReportIssue', 'FlexBlockCentered', 'franken_desc', 'FrankenUI', 'CookieSettings', 'team_members', 'body', 'TeamMembers', 'access_roles', 'ShareDocument', 'DateCard', 'section_content', 'Notifications', 'cards_homepage', 'page'] -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb from fasthtml.common import * import fasthtml.common as fh from fasthtml.components import Uk_input_tag @@ -17,7 +17,7 @@ from fh_frankenui.components import * import calendar -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb CreateAccount = Card(Grid(Button(UkIcon('github',cls='uk-margin-small-right'),'Github'), Button(UkIcon('google',cls='uk-margin-small-right'),'Google'), cols=2,cls='gap-6'), @@ -28,12 +28,12 @@ footer=Button(cls=(ButtonT.primary,'w-full'))('Create Account'), body_cls='space-y-4 py-0') -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb Card1Svg = Svg(viewBox="0 0 24 24", fill="none", stroke="currentColor", stroke_linecap="round", stroke_linejoin="round", stroke_width="2", cls="h-6 w-6 mr-1")(Rect(width="20", height="14", x="2", y="5", rx="2"),Path(d="M2 10h20")) Card2Svg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d="M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z", fill="currentColor")), AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d="M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701", fill="currentColor")) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb PaymentMethod = Card( Grid( Button(CenteredDiv(Card1Svg, "Card"), cls='h-20 w-full border-2 border-primary'), @@ -49,7 +49,7 @@ cols=3,cls='gap-4')), header=(H3('Payment Method'),P(cls=TextFont.muted_sm)('Add a new payment method to your account.'))) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb area_opts = ('Team','Billing','Account','Deployment','Support') severity_opts = ('Severity 1 (Highest)', 'Severity 2', 'Severity 3', 'Severity 4 (Lowest)') ReportIssue = Card( @@ -66,10 +66,10 @@ Button(cls=ButtonT.primary)('Submit'))) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb FlexBlockCentered = (FlexT.block,FlexT.center) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb franken_desc ="HTML-first, framework-agnostic, beautifully designed components that you can truly copy and paste into your site. Accessible. Customizable. Open Source." FrankenUI = Card(H4("franken/ui"), P(cls=TextFont.muted_sm)(franken_desc), @@ -77,7 +77,7 @@ Div(cls=FlexBlockCentered)("TypeScript"), Div(cls=FlexBlockCentered)(UkIcon('star'),"20k"),"Updated April 2023")) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb CookieSettings = Card( Div(H5('Strictly Necessary'), P(cls=(TextFont.muted_sm,TextT.normal))('These cookies are essential in order to use the website and use its features.'), @@ -94,7 +94,7 @@ header=(H4('Cookie Settings'),P(cls=(TextFont.muted_sm, 'mt-1.5'))('Manage your cookie settings here.')), footer=Button(cls=(ButtonT.primary, 'w-full'))('Save Preferences'),) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb team_members = [("Sofia Davis", "m@example.com", "Owner"),("Jackson Lee", "p@example.com", "Member"),] body = [Div(cls=(*FlexBlockCentered, 'space-x-4'))( @@ -114,13 +114,13 @@ TeamMembers = Card(*body, header = (H4('Team Members'),Div('Invite your team members to collaborate.', cls=('mt-1.5', TextFont.muted_sm))),) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb access_roles = ("Read and write access", "Read-only access") team_members = [("Olivia Martin", "m@example.com", "Read and write access"), ("Isabella Nguyen", "b@example.com", "Read-only access"), ("Sofia Davis", "p@example.com", "Read-only access")] -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb ShareDocument = Card( Div(cls='flex gap-x-2')( Input(value='http://example.com/link/to/document',cls='flex-1'), @@ -135,15 +135,15 @@ UkSelect(*Options(*access_roles, selected_idx=access_roles.index(r))), cls='gap-4') for n,e,r in team_members], header = (H4('Share this document'),Div('Anyone with the link can view this document.', cls=('mt-1.5',TextFont.muted_sm)))) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb DateCard = Card(Button('Jan 20, 2024 - Feb 09, 2024')) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb section_content =(('bell','Everything',"Email digest, mentions & all activity."), ('user',"Available","Only mentions and comments"), ('ban',"Ignoring","Turn of all notifications")) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb Notifications = Card( NavContainer( *[Li(cls='-mx-1')(A(Div(cls="flex gap-x-4")(UkIcon(icon),Div(cls='flex-1')(P(name),P(cls=TextFont.muted_sm)(desc))))) @@ -152,7 +152,7 @@ header = (H4('Notification'),Div('Choose what you want to be notified about.', cls=('mt-1.5', TextFont.muted_sm))), body_cls='pt-0') -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb def page(): return Title("Custom"),Grid( *map(lambda x: Div(x, cls='space-y-4'),( @@ -163,5 +163,5 @@ def page(): cols=1, cls=(GridT.small,'md:grid-cols-3', 'sm:grid-cols-2'), ) -# %% ../02_cards.ipynb +# %% ../example_cards.ipynb cards_homepage = page() diff --git a/ex_nbs/app/dashboard.py b/docs/app/dashboard.py similarity index 90% rename from ex_nbs/app/dashboard.py rename to docs/app/dashboard.py index 64b5112..2494d17 100644 --- a/ex_nbs/app/dashboard.py +++ b/docs/app/dashboard.py @@ -1,13 +1,13 @@ """FrankenUI Dashboard Example""" -# AUTOGENERATED! DO NOT EDIT! File to edit: ../03_dashboard.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../example_dashboard.ipynb. # %% auto 0 __all__ = ['rev', 'sub', 'sal', 'act', 'top_info_row', 'recent_sales', 'teams', 'opt_hdrs', 'team_dropdown', 'hotkeys', 'avatar_dropdown', 'top_nav', 'dashboard_homepage', 'InfoCard', 'AvatarItem', 'generate_chart', 'NavSpacedLi', 'page'] -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb from fasthtml.common import * from fh_frankenui import * from fh_frankenui.core import * @@ -17,23 +17,23 @@ import numpy as np import matplotlib.pylab as plt -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb def InfoCard(title, value, change): return Div(Card( Div(H3(value), P(change, cls=TextFont.muted_sm)), header = H4(title))) -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb rev = InfoCard("Total Revenue", "$45,231.89", "+20.1% from last month") sub = InfoCard("Subscriptions", "+2350", "+180.1% from last month") sal = InfoCard("Sales", "+12,234", "+19% from last month") act = InfoCard("Active Now", "+573", "+201 since last hour") -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb top_info_row = Grid(rev,sub,sal,act,cols=4, cls=GridT.small) -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb def AvatarItem(name, email, amount): return Div(cls="flex items-center")( DiceBearAvatar(name, 9,9), @@ -56,13 +56,13 @@ def AvatarItem(name, email, amount): cls='lg:col-span-3') -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb @matplotlib2fasthtml def generate_chart(num_points): plotdata = [np.random.exponential(1) for _ in range(num_points)] plt.plot(range(len(plotdata)), plotdata) -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb teams = [["Alicia Koch"],['Acme Inc', 'Monster Inc.'],['Create a Team']] opt_hdrs = ["Personal", "Team", ""] @@ -75,7 +75,7 @@ def generate_chart(num_points): Option(A("Monster Inc."))), Option(A("Create a Team"))) -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb hotkeys = [('Profile','⇧⌘P'),('Billing','⌘B'),('Settings','⌘S'),('New Team', ''), ('Logout', '')] def NavSpacedLi(t,s): return NavCloseLi(A(FullySpacedDiv(P(t),P(s,cls=TextFont.muted_sm)))) @@ -86,7 +86,7 @@ def NavSpacedLi(t,s): return NavCloseLi(A(FullySpacedDiv(P(t),P(s,cls=TextFont.m NavHeaderLi('sveltecult',NavSubtitle("leader@sveltecult.com")), *[NavSpacedLi(*hk) for hk in hotkeys],)) -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb top_nav = NavBarContainer( NavBarLSide( NavBarNav( @@ -103,7 +103,7 @@ def NavSpacedLi(t,s): return NavCloseLi(A(FullySpacedDiv(P(t),P(s,cls=TextFont.m avatar_dropdown, cls='flex items-center'))) -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb def page(): return Div(cls="space-y-4")( Div(cls="border-b border-border px-4")(top_nav), @@ -120,5 +120,5 @@ def page(): recent_sales, gap=4,cls='lg:grid-cols-7')) -# %% ../03_dashboard.ipynb +# %% ../example_dashboard.ipynb dashboard_homepage = page() diff --git a/ex_nbs/app/data/mail.json b/docs/app/data/mail.json similarity index 100% rename from ex_nbs/app/data/mail.json rename to docs/app/data/mail.json diff --git a/ex_nbs/app/data/status_list.json b/docs/app/data/status_list.json similarity index 100% rename from ex_nbs/app/data/status_list.json rename to docs/app/data/status_list.json diff --git a/ex_nbs/app/data/statuses.json b/docs/app/data/statuses.json similarity index 100% rename from ex_nbs/app/data/statuses.json rename to docs/app/data/statuses.json diff --git a/ex_nbs/app/forms.py b/docs/app/forms.py similarity index 95% rename from ex_nbs/app/forms.py rename to docs/app/forms.py index 252a489..70a0aef 100644 --- a/ex_nbs/app/forms.py +++ b/docs/app/forms.py @@ -1,12 +1,12 @@ """FrankenUI Forms Example""" -# AUTOGENERATED! DO NOT EDIT! File to edit: ../04_forms.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../example_forms.ipynb. # %% auto 0 __all__ = ['sidebar_items', 'sidebar', 'forms_homepage', 'heading', 'profile_form', 'account_form', 'appearance_form', 'notifications_form', 'display_form', 'page'] -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb from fasthtml.common import * from fh_frankenui import * from fh_frankenui.core import * @@ -14,22 +14,22 @@ from fasthtml.svg import * -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb def heading(): return Div(cls="space-y-5")( H2("Settings"), P("Manage your account settings and set e-mail preferences.", cls=TextFont.muted_lg), UkHSplit()) -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb sidebar_items = ["Profile", "Account", "Appearance", "Notifications", "Display"] -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb sidebar = NavContainer(*map(lambda x: Li(A(x)),sidebar_items), uk_switcher="connect: #component-nav; animation: uk-animation-fade", cls=(NavT.secondary,"space-y-4 p-4 w-1/5")) -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb def profile_form(): content = (Div(cls="space-y-2")( LabelInput("Username", placeholder='sveltecult', id='username'), @@ -52,7 +52,7 @@ def profile_form(): return UkFormSection('Profile', 'This is how others will see you on the site.', button_txt='Update profile', *content) -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb def account_form(): content = ( Div(cls="space-y-2")( @@ -69,7 +69,7 @@ def account_form(): return UkFormSection('Account', 'Update your account settings. Set your preferred language and timezone.', button_txt='Update profile', *content) -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb def appearance_form(): content = ( Div(cls="space-y-2")( @@ -106,7 +106,7 @@ def appearance_form(): return UkFormSection('Appearance', 'Customize the appearance of the app. Automatically switch between day and night themes.', button_txt='Update preferences', *content) -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb def notifications_form(): content = [ Div(cls="space-y-2")( @@ -134,7 +134,7 @@ def notifications_form(): return UkFormSection('Notifications', 'Configure how you receive notifications.', *content, button_txt="Update notifications") -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb def display_form(): content = ( Div(cls="space-y-2")( @@ -145,7 +145,7 @@ def display_form(): for i, label in enumerate(["Recents", "Home", "Applications", "Desktop", "Downloads", "Documents"])])) return UkFormSection('Display', 'Turn items on or off to control what\'s displayed in the app.', button_txt='Update display', *content) -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb def page(): return Div(cls="p-6 lg:p-10")( heading(), @@ -160,5 +160,5 @@ def page(): Li()(display_form()) ))))) -# %% ../04_forms.ipynb +# %% ../example_forms.ipynb forms_homepage = page() diff --git a/ex_nbs/app/mail.py b/docs/app/mail.py similarity index 93% rename from ex_nbs/app/mail.py rename to docs/app/mail.py index 76b192b..ea9f260 100644 --- a/ex_nbs/app/mail.py +++ b/docs/app/mail.py @@ -1,12 +1,12 @@ """FrankenUI Mail Example""" -# AUTOGENERATED! DO NOT EDIT! File to edit: ../08_mail.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../example_mail.ipynb. # %% auto 0 __all__ = ['sidebar_group1', 'sidebar_group2', 'sidebar', 'mail_data', 'mail_homepage', 'NavItem', 'NavGroup', 'MailSbLi', 'format_date', 'MailItem', 'MailList', 'MailContent', 'IconNavItem', 'IconNav', 'MailDetailView'] -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb from fasthtml.common import * import fasthtml.common as fh from fh_frankenui import * @@ -18,7 +18,7 @@ import json from datetime import datetime -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def NavItem(icon, text, quantity=None): cls = 'flex items-center space-x-2 rounded-md px-3 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground' content = [UkIcon(icon), Span(text)] @@ -26,11 +26,11 @@ def NavItem(icon, text, quantity=None): content.append(Span(quantity, cls='ml-auto text-background bg-primary rounded-full px-2 py-0.5 text-xs')) return Li(A(*content, href='#', cls=cls)) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def NavGroup(items): return Nav(cls='uk-nav-default space-y-3')(*[NavItem(i, t, q) for i, t, q in items if q or t != 'Trash']) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb sidebar_group1 = (('home', 'Inbox', '128'), ('file-text', 'Drafts', '9'), (' arrow-up-right', 'Sent', ''), ('ban', 'Junk', '23'), ('trash', 'Trash', ''), ('folder', 'Archive', '')) @@ -50,15 +50,15 @@ def MailSbLi(icon, title, cnt): cls='space-y-6')) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb mail_data = json.load(open(pathlib.Path('data/mail.json'))) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def format_date(date_str): date_obj = datetime.fromisoformat(date_str) return date_obj.strftime("%Y-%m-%d %I:%M %p") -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def MailItem(mail): cls_base = 'relative rounded-lg border border-border p-3 text-sm hover:bg-accent' cls = f"{cls_base} {'bg-muted' if mail == mail_data[0] else ''} {'tag-unread' if not mail['read'] else 'tag-mail'}" @@ -76,10 +76,10 @@ def MailItem(mail): *[A(label, cls=f"uk-label relative z-10 {'uk-label-primary' if label == 'work' else ''}", href='#') for label in mail['labels']]))) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def MailList(mails): return Ul(cls='js-filter space-y-2 p-4 pt-0')(*[MailItem(mail) for mail in mails]) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def MailContent(): return Div(cls='flex flex-col',uk_filter="target: .js-filter")( Div(cls='flex px-4 py-2 ')( @@ -94,11 +94,11 @@ def MailContent(): Input(placeholder='Search'))), Div(cls='flex-1 overflow-y-auto max-h-[600px]')(MailList(mail_data)))) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def IconNavItem(*d): return [Li(A(UkIcon(o[0],uk_tooltip=o[1]))) for o in d] def IconNav(*c,cls=''): return Ul(cls=f'uk-iconnav {cls}')(*c) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def MailDetailView(mail): return Div(cls='flex flex-col')( Div(cls='flex h-14 flex-none items-center border-b border-border p-2')( @@ -133,7 +133,7 @@ def MailDetailView(mail): LabelSwitch('Mute this thread',id='mute'), # cls='inline-flex items-center gap-x-2 text-xs' Button('Send', cls=ButtonT.primary)))) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb def mail_homepage(): return Div(cls='flex divide-x divide-border')( sidebar, @@ -141,5 +141,5 @@ def mail_homepage(): MailDetailView(mail_data[0]), cols=2, gap=0, cls='flex-1 divide-x divide-border')) -# %% ../08_mail.ipynb +# %% ../example_mail.ipynb mail_homepage = mail_homepage() diff --git a/docs/app/main.py b/docs/app/main.py new file mode 100644 index 0000000..3a03853 --- /dev/null +++ b/docs/app/main.py @@ -0,0 +1,129 @@ +"""FrankenUI Tasks Example""" + +# AUTOGENERATED! DO NOT EDIT! File to edit: ../99_main.ipynb. + +# %% auto 0 +__all__ = ['app', 'rt', 'reference_fns', 'api_ref_rts', 'sidebar', 'with_layout', 'tasks', 'cards', 'dashboard', 'forms', 'music', + 'auth', 'playground', 'mail', 'getting_started', 'fnname2title', 'themeswitcher', 'index'] + +# %% ../99_main.ipynb +from fasthtml.common import * +import fasthtml.common as fh +from fh_frankenui.components import * +from fh_frankenui.core import * +import re +from fasthtml.components import Uk_theme_switcher +from utils import hjs + +# %% ../99_main.ipynb +app,rt = fast_app(pico=False, hdrs=(*Theme.blue.headers(),*hjs)) + +# %% ../99_main.ipynb +def with_layout(func): + def wrapper(): + original_content = func() + return Body(cls="bg-background text-foreground")( + Div(cls="flex flex-col md:flex-row w-full")( + Button(UkIcon("menu",50,50,cls='mt-4'), cls="md:hidden mb-4", uk_toggle="target: #mobile-sidebar"), + Div(sidebar, id='mobile-sidebar', hidden=True), + Div(cls="md:flex w-full")( + Div(sidebar, cls="hidden md:block"), + Div(original_content, cls='w-full', id="mobile-sidebar") + ) + ), + ) + wrapper.__name__ = func.__name__ + return wrapper + +# %% ../99_main.ipynb +from tasks import tasks_homepage +from cards import cards_homepage +from dashboard import dashboard_homepage +from forms import forms_homepage +from music import music_homepage +from auth import auth_homepage +from playground import playground_homepage +from mail import mail_homepage + +# %% ../99_main.ipynb +@rt +@with_layout +def tasks(): return tasks_homepage +@rt +@with_layout +def cards(): return cards_homepage +@rt +@with_layout +def dashboard(): return dashboard_homepage +@rt +@with_layout +def forms(): return forms_homepage +@rt +@with_layout +def music(): return music_homepage +@rt +@with_layout +def auth(): return auth_homepage +@rt +@with_layout +def playground(): return playground_homepage +@rt +@with_layout +def mail(): return mail_homepage + +# %% ../99_main.ipynb +@rt +@with_layout +def getting_started(): + return Container(render_md(open('GettingStarted.md').read())) + +# %% ../99_main.ipynb +import api_reference + +# %% ../99_main.ipynb +reference_fns = L([o for o in dir(api_reference) if o.startswith('docs_')]) +reference_fns + +# %% ../99_main.ipynb +def fnname2title(ref_fn_name): return ref_fn_name[5:].replace('_',' | ').title() + +# %% ../99_main.ipynb +api_ref_rts = [(f"/{o}", app.add_route(f"/{o}", with_layout(getattr(api_reference, o)))) for o in reference_fns] + +# %% ../99_main.ipynb +@rt +@with_layout +def themeswitcher(): return Div(Uk_theme_switcher(),cls="p-12") + +# %% ../99_main.ipynb +sidebar = NavContainer( + Li(A("Getting Started", href=getting_started)), + NavParentLi( + A(FullySpacedDiv("API Reference",UkIcon('chevron-down'))), + NavContainer( + *[Li(A(fnname2title(o), href=f"/{o}")) for o in reference_fns], + parent=False), + ), + NavParentLi( + A(FullySpacedDiv('Examples',UkIcon('chevron-down'))), + NavContainer( + Li(A('Tasks', href=tasks)), + Li(A('Cards', href=cards)), + Li(A('Dashboard', href=dashboard)), + Li(A('Form', href=forms)), + Li(A('Music', href=music)), + Li(A('Auth', href=auth)), + Li(A('Playground',href=playground)), + Li(A('Mail', href=mail)), + parent=False), + ), + Li(A("Theme",href=themeswitcher)), + + uk_nav=False, cls=(NavT.primary,"space-y-4 p-4 w-full md:w-1/5")) + +# %% ../99_main.ipynb +@rt +def index():return getting_started() + +# %% ../99_main.ipynb +serve() diff --git a/ex_nbs/app/music.py b/docs/app/music.py similarity index 93% rename from ex_nbs/app/music.py rename to docs/app/music.py index bc48018..4ddcb3c 100644 --- a/ex_nbs/app/music.py +++ b/docs/app/music.py @@ -1,6 +1,6 @@ """FrankenUI Music Example""" -# AUTOGENERATED! DO NOT EDIT! File to edit: ../05_music.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../example_music.ipynb. # %% auto 0 __all__ = ['music_items', 'file_dd_items', 'edit_actions', 'view_dd_data', 'account_dd_data', 'music_headers', @@ -8,14 +8,14 @@ 'playlists_data', 'sb', 'music_homepage', 'MusicLi', 'AlbumImg', 'AlbumFooter', 'Album', 'create_album_grid', 'podcast_tab', 'LAlignedIconTxts', 'MusicSidebarLi', 'page'] -# %% ../05_music.ipynb +# %% ../example_music.ipynb from fasthtml.common import * import fasthtml.common as fh from fh_frankenui import * from fh_frankenui.core import * from fh_frankenui.components import * -# %% ../05_music.ipynb +# %% ../example_music.ipynb def MusicLi(t,hk=''): return Li(A(FullySpacedDiv(t,P(hk,cls=TextFont.muted_sm)))) music_items = [("About Music", ""),("Preferences", "⌘"),("Hide Music", "⌘H"),("Hide Others", "⇧⌘H"),("Quit Music", "⌘Q")] @@ -31,7 +31,7 @@ def MusicLi(t,hk=''): return Li(A(FullySpacedDiv(t,P(hk,cls=TextFont.muted_sm))) account_dd_data = [Span("Switch Account", cls="ml-6"), [SpacedPP("Andy"), LAlignedTxtIcon("Benoit", 'plus-circle', 0.5, icon_right=False), SpacedPP("Luis")], SpacedPPs("Manage Family"), SpacedPPs("Add Account")] -# %% ../05_music.ipynb +# %% ../example_music.ipynb music_headers =NavBarContainer( NavBarLSide( NavBarNav( @@ -54,7 +54,7 @@ def MusicLi(t,hk=''): return Li(A(FullySpacedDiv(t,P(hk,cls=TextFont.muted_sm))) MusicLi("Add Account"))), cls='space-x-4'))) -# %% ../05_music.ipynb +# %% ../example_music.ipynb def AlbumImg(url): return Div(cls="overflow-hidden rounded-md")(Img(cls="transition-transform duration-200 hover:scale-105", src=url)) @@ -64,14 +64,14 @@ def AlbumFooter(title, artist): def Album(url,title,artist): return Div(AlbumImg(url),AlbumFooter(title,artist)) -# %% ../05_music.ipynb +# %% ../example_music.ipynb listen_now_albums = (("Roar", "Catty Perry"), ("Feline on a Prayer", "Cat Jovi"),("Fur Elise", "Ludwig van Beethovpurr"),("Purrple Rain", "Prince's Cat")) made_for_you_albums = [("Like a Feline", "Catdonna"),("Livin' La Vida Purrda", "Ricky Catin"),("Meow Meow Rocket", "Elton Cat"), ("Rolling in the Purr", "Catdelle",),("Purrs of Silence", "Cat Garfunkel"),("Meow Me Maybe", "Carly Rae Purrsen"),] -# %% ../05_music.ipynb +# %% ../example_music.ipynb def create_album_grid(albums, cols=4): return Grid(*[Div(cls="uk-grid-small")( Div(cls="overflow-hidden rounded-md")( @@ -81,7 +81,7 @@ def create_album_grid(albums, cols=4): P(album['artist'], cls="text-xs text-muted-foreground"))) for album in albums], cols,gap=4) -# %% ../05_music.ipynb +# %% ../example_music.ipynb _album = lambda t,a: Album('https://ucarecdn.com/e5607eaf-2b2a-43b9-ada9-330824b6afd7/music1.webp',t,a) music_content = (Div(H3("Listen Now"), cls="mt-6 space-y-1"), @@ -93,7 +93,7 @@ def create_album_grid(albums, cols=4): UkHLine(), Grid(*[_album(t,a) for t,a in made_for_you_albums], cols=6, cls=GridT.small)) -# %% ../05_music.ipynb +# %% ../example_music.ipynb tabs = TabContainer( Li(A('Music', href='#'),cls='uk-active'), Li(A('Podcasts', href='#')), @@ -101,7 +101,7 @@ def create_album_grid(albums, cols=4): uk_switcher='connect: #component-nav; animation: uk-animation-fade', alt=True) -# %% ../05_music.ipynb +# %% ../example_music.ipynb def podcast_tab(): return Div( Div(cls="space-y-3")( @@ -115,15 +115,15 @@ def podcast_tab(): P("You have not added any podcasts. Add one below.", cls=TextFont.muted_sm), Button("Add Podcast", cls=ButtonT.primary)))) -# %% ../05_music.ipynb +# %% ../example_music.ipynb def LAlignedIconTxts(ns, icns): return [Li(A(LAlignedIconTxt(n,i))) for n,i in zip(ns,icns)] -# %% ../05_music.ipynb +# %% ../example_music.ipynb discoved_data = [("play-circle","Listen Now"), ("binoculars", "Browse"), ("rss","Radio")] library_data = [("play-circle", "Playlists"), ("music", "Songs"), ("user", "Made for You"), ("users", "Artists"), ("bookmark", "Albums")] playlists_data = [("library","Recently Added"), ("library","Recently Played")] -# %% ../05_music.ipynb +# %% ../example_music.ipynb def MusicSidebarLi(icon, text): return Li(A(LAlignedDiv(UkIcon(icon), P(text),cls='space-x-2'))) sb = NavContainer( NavHeaderLi(H3("Discover")),*[MusicSidebarLi(*o) for o in discoved_data], @@ -132,7 +132,7 @@ def MusicSidebarLi(icon, text): return Li(A(LAlignedDiv(UkIcon(icon), P(text),cl cls=(NavT.primary,'space-y-3','pl-8'), ) -# %% ../05_music.ipynb +# %% ../example_music.ipynb def page(): return Div(Container(music_headers,cls='py-8'),UkHSplit(), Grid(sb, @@ -146,5 +146,5 @@ def page(): Li(podcast_tab())))), cols=5)) -# %% ../05_music.ipynb +# %% ../example_music.ipynb music_homepage = page() diff --git a/ex_nbs/app/playground.py b/docs/app/playground.py similarity index 92% rename from ex_nbs/app/playground.py rename to docs/app/playground.py index 0e1c14a..e05c1b0 100644 --- a/ex_nbs/app/playground.py +++ b/docs/app/playground.py @@ -1,11 +1,11 @@ """FrankenUI Playground Example""" -# AUTOGENERATED! DO NOT EDIT! File to edit: ../07_playground.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../example_playground.ipynb. # %% auto 0 __all__ = ['preset_options', 'rsidebar', 'playground_homepage', 'playground_navbar', 'page'] -# %% ../07_playground.ipynb +# %% ../example_playground.ipynb from fasthtml.common import * import fasthtml.common as fh from fh_frankenui import * @@ -14,12 +14,12 @@ from fasthtml.svg import * -# %% ../07_playground.ipynb +# %% ../example_playground.ipynb preset_options = ["Grammatical Standard English", "Summarize for a 2nd grader", "Text to command","Q&A","English to other languages","Parse unstructured data", "Classification","Natural language to Python","Explain code","Chat","More examples"] -# %% ../07_playground.ipynb +# %% ../example_playground.ipynb def playground_navbar(): save_modal = Modal( ModalTitle("Save preset"), @@ -52,7 +52,7 @@ def playground_navbar(): NavBarRSide(rnav), cls='mt-2') -# %% ../07_playground.ipynb +# %% ../example_playground.ipynb rsidebar = NavContainer( UkSelect( Optgroup(map(Option,("text-davinci-003", "text-curie-001", "text-babbage-001", "text-ada-001")),label='GPT-3'), @@ -65,7 +65,7 @@ def playground_navbar(): cls='space-y-6 mt-8' ) -# %% ../07_playground.ipynb +# %% ../example_playground.ipynb def page(): navbar = playground_navbar() main_content = Div( @@ -80,5 +80,5 @@ def page(): return Div(navbar, Div(cls="flex w-full")(main_content, rsidebar), bottom_buttons) -# %% ../07_playground.ipynb +# %% ../example_playground.ipynb playground_homepage = page() diff --git a/ex_nbs/app/requirements.txt b/docs/app/requirements.txt similarity index 100% rename from ex_nbs/app/requirements.txt rename to docs/app/requirements.txt diff --git a/ex_nbs/app/tasks.py b/docs/app/tasks.py similarity index 92% rename from ex_nbs/app/tasks.py rename to docs/app/tasks.py index b0feabe..262ba44 100644 --- a/ex_nbs/app/tasks.py +++ b/docs/app/tasks.py @@ -1,13 +1,13 @@ """FrankenUI Tasks Example""" -# AUTOGENERATED! DO NOT EDIT! File to edit: ../01_tasks.ipynb. +# AUTOGENERATED! DO NOT EDIT! File to edit: ../example_tasks.ipynb. # %% auto 0 __all__ = ['priority_dd', 'rows_per_page_dd', 'status_dd', 'hotkeys_a', 'hotkeys_b', 'avatar_opts', 'page_heading', 'table_controls', 'task_columns', 'tasks_table', 'tasks_ui', 'tasks_homepage', 'create_hotkey_li', 'CreateTaskModal', 'task_dropdown', 'header_render', 'cell_render', 'footer'] -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb from fasthtml.common import * import fasthtml.common as fh from fh_frankenui.core import * @@ -15,24 +15,24 @@ from fasthtml.svg import * import json -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb with open('data/status_list.json', 'r') as f: data = json.load(f) with open('data/statuses.json', 'r') as f: statuses = json.load(f) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb priority_dd = [{'priority': "low", 'count': 36 }, {'priority': "medium", 'count': 33 }, {'priority': "high", 'count': 31 }] rows_per_page_dd = [10,20,30,40,50] status_dd = [{'status': "backlog", 'count': 21 },{'status': "todo", 'count': 21 },{'status': "progress", 'count': 20 },{'status': "done",'count': 19 },{'status': "cancelled", 'count': 19 }] -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb def create_hotkey_li(hotkey): return NavCloseLi(A(cls='justify-between')(hotkey[0], Span(hotkey[1], cls=TextFont.muted_sm))) hotkeys_a = (('Profile','⇧⌘P'),('Billing','⌘B'),('Settings','⌘S'),('New Team','')) hotkeys_b = (('Logout',''), ) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb avatar_opts = DropDownNavContainer( NavHeaderLi(P('sveltecult'),NavSubtitle('leader@sveltecult.com')), NavDividerLi(), @@ -40,7 +40,7 @@ def create_hotkey_li(hotkey): return NavCloseLi(A(cls='justify-between')(hotkey[ NavDividerLi(), *map(create_hotkey_li, hotkeys_b),) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb def CreateTaskModal(): return Modal( Div(cls='p-6')( @@ -58,13 +58,13 @@ def CreateTaskModal(): cls='space-x-5'))), id='TaskForm') -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb page_heading = FullySpacedDiv(cls='space-y-2')( Div(cls='space-y-2')( H2('Welcome back!'),P("Here's a list of your tasks for this month!", cls=TextFont.muted_sm)), Div(DiceBearAvatar("sveltcult",8,8),avatar_opts)) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb table_controls =(Input(cls='w-[250px]',placeholder='Filter task'), Button("Status"), DropDownNavContainer(map(NavCloseLi,[A(FullySpacedDiv(P(a['status']), P(a['count'])),cls=TextT.capitalize) for a in status_dd])), @@ -74,7 +74,7 @@ def CreateTaskModal(): DropDownNavContainer(map(NavCloseLi,[A(LAlignedIconTxt(o, icon="check")) for o in ['Title','Status','Priority']])), Button('Create Task',cls=(ButtonT.primary, TextFont.bold_sm), uk_toggle="target: #TaskForm")) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb def task_dropdown(): return Div(Button(UkIcon('ellipsis')), DropDownNavContainer( @@ -84,7 +84,7 @@ def task_dropdown(): A('Favorite',), A(SpacedPP('Delete', '⌘⌫'))]))) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb def header_render(col): cls = 'p-2 ' + 'uk-table-shrink' if col in ('Done','Actions') else '' match col: @@ -92,7 +92,7 @@ def header_render(col): case 'Actions': return Th("", cls=cls) case _: return Th(col, cls=cls) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb def cell_render(col, row): def _Td(*args,cls='', **kwargs): return Td(*args, cls=f'p-2 {cls}',**kwargs) match col: @@ -103,7 +103,7 @@ def _Td(*args,cls='', **kwargs): return Td(*args, cls=f'p-2 {cls}',**kwargs) case "Actions": return _Td(cls='uk-table-shrink')(task_dropdown()) case _: raise ValueError(f"Unknown column: {col}") -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb task_columns = ["Done", 'Task', 'Title', 'Status', 'Priority', 'Actions'] tasks_table = Div(cls='uk-overflow-auto mt-4 rounded-md border border-border')(TableFromDicts( @@ -114,7 +114,7 @@ def _Td(*args,cls='', **kwargs): return Td(*args, cls=f'p-2 {cls}',**kwargs) sortable=True)) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb def footer(): hw_cls = 'h-4 w-4' return FullySpacedDiv(cls='mt-4 px-2 py-2')( @@ -127,12 +127,12 @@ def footer(): UkIconLink(icon='chevron-right', button=True), UkIconLink(icon='chevrons-right', button=True)))) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb tasks_ui = Div( FullySpacedDiv(cls='mt-8')( Div(cls='flex flex-1 gap-4')(table_controls)), tasks_table, footer(),) -# %% ../01_tasks.ipynb +# %% ../example_tasks.ipynb tasks_homepage = CreateTaskModal(), Div(cls='p-8')(page_heading, tasks_ui) diff --git a/docs/app/utils.py b/docs/app/utils.py new file mode 100644 index 0000000..bc7a07b --- /dev/null +++ b/docs/app/utils.py @@ -0,0 +1,57 @@ +"""Utilities for building the docs page that don't belong anywhere else""" + +# AUTOGENERATED! DO NOT EDIT! File to edit: ../Utils.ipynb. + +# %% auto 0 +__all__ = ['hjs', 'HShow', 'create_server'] + +# %% ../Utils.ipynb +from IPython.display import display, HTML +from fasthtml.common import * +from fh_frankenui.core import * +from fasthtml.jupyter import * +from uuid import uuid4 + +# %% ../Utils.ipynb +def HShow(comp, iframe_height="auto", app=None, port=8000): + route = f'/{uuid4()}' + app.get(route)(lambda: comp) + display(HTML(f'Open in new tab')) + return HTMX(route,port=port,iframe_height=iframe_height) + +# %% ../Utils.ipynb +def create_server(app, stop_server=True,server_varname='server'): + if stop_server and server_varname in globals(): globals()[server_varname].stop() + for port in range(8000,8030): + if is_port_free(port): + server = JupyUvi(app, port=port) + Show = partial(HShow, app=app, port=port) + return server, Show + +# %% ../Utils.ipynb +hjs = (Style('html.dark .hljs-copy-button {background-color: #e0e0e0; color: #2d2b57;}'), + Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/atom-one-dark.css', disabled=True), + Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/atom-one-light.css', disabled=True), + Script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js'), + Script(src='https://cdn.jsdelivr.net/gh/arronhunt/highlightjs-copy/dist/highlightjs-copy.min.js'), + Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/arronhunt/highlightjs-copy/dist/highlightjs-copy.min.css'), + Style('.hljs-copy-button {background-color: #2d2b57;}'), + Script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/languages/python.min.js'), + Script("hljs.addPlugin(new CopyButtonPlugin());\r\nhljs.configure({'cssSelector': 'pre code'});\r\nhtmx.onLoad(hljs.highlightAll);", type='module'), + Script('''const observer = new MutationObserver(mutations => { + mutations.forEach(mutation => { + if (mutation.target.tagName === 'HTML' && mutation.attributeName === 'class') { + const isDark = mutation.target.classList.contains('dark'); + document.querySelector('link[href*="atom-one-dark.css"]').disabled = !isDark; + document.querySelector('link[href*="atom-one-light.css"]').disabled = isDark; + } + }); + }); + + observer.observe(document.documentElement, { attributes: true }); + + // Initial setup + const isDark = document.documentElement.classList.contains('dark'); + document.querySelector('link[href*="atom-one-dark.css"]').disabled = !isDark; + document.querySelector('link[href*="atom-one-light.css"]').disabled = isDark; + ''')) diff --git a/ex_nbs/auth.py b/docs/auth.py similarity index 100% rename from ex_nbs/auth.py rename to docs/auth.py diff --git a/ex_nbs/cards.py b/docs/cards.py similarity index 100% rename from ex_nbs/cards.py rename to docs/cards.py diff --git a/ex_nbs/dashboard.py b/docs/dashboard.py similarity index 100% rename from ex_nbs/dashboard.py rename to docs/dashboard.py diff --git a/ex_nbs/data b/docs/data similarity index 100% rename from ex_nbs/data rename to docs/data diff --git a/ex_nbs/06_auth.ipynb b/docs/example_auth.ipynb similarity index 92% rename from ex_nbs/06_auth.ipynb rename to docs/example_auth.ipynb index f6c4fc8..5f22d95 100644 --- a/ex_nbs/06_auth.ipynb +++ b/docs/example_auth.ipynb @@ -36,22 +36,9 @@ "source": [ "#| hide\n", "#| eval: false\n", - "\n", - "from fasthtml.jupyter import *\n", - "from IPython.display import display, HTML\n", - "\n", - "if is_port_free(8000):\n", - " app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", - " server = JupyUvi(app)\n", - "app \n", - "def HShow(comp, app):\n", - " @app.get('/')\n", - " def get(): return comp\n", - "\n", - " display(HTML(f'Open in new tab'))\n", - " return HTMX(\"/\")\n", - "\n", - "Show = partial(HShow, app=app)" + "from utils import create_server\n", + "app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", + "server, Show = create_server(app)" ] }, { diff --git a/ex_nbs/02_cards.ipynb b/docs/example_cards.ipynb similarity index 96% rename from ex_nbs/02_cards.ipynb rename to docs/example_cards.ipynb index a60b28b..970297b 100644 --- a/ex_nbs/02_cards.ipynb +++ b/docs/example_cards.ipynb @@ -47,27 +47,9 @@ "source": [ "#| hide\n", "#| eval: false\n", - "\n", - "from IPython.display import display, HTML\n", - "from fasthtml.jupyter import *\n", - "\n", - "def HShow(comp, iframe_height=\"auto\", app=None, port=8000):\n", - " app.get('/')(lambda: comp)\n", - " display(HTML(f'Open in new tab'))\n", - " return HTMX(\"/\",port=port,iframe_height=iframe_height)\n", - "\n", - "def create_server(app,rt,stop_server=True):\n", - " if stop_server and 'server' in globals(): globals()['server'].stop()\n", - " for port in range(8000,8030): \n", - " if is_port_free(port): \n", - " server = JupyUvi(app, port=port)\n", - " Show = partial(HShow, app=app, port=port)\n", - " break\n", - " return server, Show\n", - "\n", - "if IN_NOTEBOOK: \n", - " app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", - " server, Show = create_server(app,rt)" + "from utils import create_server\n", + "app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", + "server, Show = create_server(app)" ] }, { diff --git a/ex_nbs/03_dashboard.ipynb b/docs/example_dashboard.ipynb similarity index 98% rename from ex_nbs/03_dashboard.ipynb rename to docs/example_dashboard.ipynb index 9cb9f98..a267452 100644 --- a/ex_nbs/03_dashboard.ipynb +++ b/docs/example_dashboard.ipynb @@ -100,20 +100,9 @@ "source": [ "#| hide\n", "#| eval: false\n", - "from fasthtml.jupyter import *\n", - "from IPython.display import display, HTML\n", - "\n", - "if is_port_free(8000) and IN_NOTEBOOK::\n", - " app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", - " server = JupyUvi(app)\n", - "def HShow(comp, app):\n", - " @app.get('/')\n", - " def get(): return comp\n", - "\n", - " display(HTML(f'Open in new tab'))\n", - " return HTMX(\"/\")\n", - "\n", - "Show = partial(HShow, app=app)" + "from utils import create_server\n", + "app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", + "server, Show = create_server(app)" ] }, { diff --git a/ex_nbs/04_forms.ipynb b/docs/example_forms.ipynb similarity index 98% rename from ex_nbs/04_forms.ipynb rename to docs/example_forms.ipynb index 0bfee90..a939f0b 100644 --- a/ex_nbs/04_forms.ipynb +++ b/docs/example_forms.ipynb @@ -45,21 +45,9 @@ "source": [ "#| hide\n", "#| eval: false\n", - "\n", - "from fasthtml.jupyter import *\n", - "from IPython.display import display, HTML\n", - "\n", - "if is_port_free(8000) and IN_NOTEBOOK::\n", - " app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", - " server = JupyUvi(app)\n", - "def HShow(comp, app):\n", - " @app.get('/')\n", - " def get(): return comp\n", - "\n", - " display(HTML(f'Open in new tab'))\n", - " return HTMX(\"/\")\n", - "\n", - "Show = partial(HShow, app=app)" + "from utils import create_server\n", + "app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", + "server, Show = create_server(app)" ] }, { diff --git a/ex_nbs/08_mail.ipynb b/docs/example_mail.ipynb similarity index 94% rename from ex_nbs/08_mail.ipynb rename to docs/example_mail.ipynb index 497d51d..de1ffc7 100644 --- a/ex_nbs/08_mail.ipynb +++ b/docs/example_mail.ipynb @@ -57,23 +57,9 @@ "source": [ "#| hide\n", "#| eval: false\n", - "def create_server(app,rt):\n", - " if 'server' in globals(): globals()['server'].stop()\n", - " if IN_NOTEBOOK:\n", - " for port in range(8000,8030): \n", - " if is_port_free(port):\n", - " server = JupyUvi(app, port=port)\n", - "\n", - " def HShow(comp, app):\n", - " @app.get('/')\n", - " def get(): return comp\n", - " display(HTML(f'Open in new tab'))\n", - " return HTMX(\"/\",port=port)\n", - " print(f\"Starter server on port {port}\")\n", - " Show = partial(HShow, app=app)\n", - " return app, rt, server, HShow, Show\n", - "app, rt = fast_app(pico=False, hdrs=Theme.blue.headers())\n", - "app, rt, server, HShow, Show = create_server(app, rt)" + "from utils import create_server\n", + "app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", + "server, Show = create_server(app)" ] }, { diff --git a/ex_nbs/05_music.ipynb b/docs/example_music.ipynb similarity index 95% rename from ex_nbs/05_music.ipynb rename to docs/example_music.ipynb index 3521f5f..193265f 100644 --- a/ex_nbs/05_music.ipynb +++ b/docs/example_music.ipynb @@ -52,24 +52,9 @@ "source": [ "#| hide\n", "#| eval: false\n", - "\n", - "def create_server(app,rt):\n", - " if 'server' in globals(): globals()['server'].stop()\n", - " if IN_NOTEBOOK:\n", - " for port in range(8000,8030): \n", - " if is_port_free(port):\n", - " server = JupyUvi(app, port=port)\n", - "\n", - " def HShow(comp, app):\n", - " @app.get('/')\n", - " def get(): return comp\n", - " display(HTML(f'Open in new tab'))\n", - " return HTMX(\"/\",port=port)\n", - " print(f\"Starter server on port {port}\")\n", - " Show = partial(HShow, app=app)\n", - " return app, rt, server, HShow, Show\n", - "app, rt = fast_app(pico=False, hdrs=Theme.blue.headers())\n", - "app, rt, server, HShow, Show = create_server(app, rt)" + "from utils import create_server\n", + "app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", + "server, Show = create_server(app)" ] }, { diff --git a/ex_nbs/07_playground.ipynb b/docs/example_playground.ipynb similarity index 93% rename from ex_nbs/07_playground.ipynb rename to docs/example_playground.ipynb index e5bcfcc..707cb8f 100644 --- a/ex_nbs/07_playground.ipynb +++ b/docs/example_playground.ipynb @@ -66,23 +66,9 @@ "source": [ "#| hide\n", "#| eval: false\n", - "def create_server(app,rt):\n", - " if 'server' in globals(): globals()['server'].stop()\n", - " if IN_NOTEBOOK:\n", - " for port in range(8000,8030): \n", - " if is_port_free(port):\n", - " server = JupyUvi(app, port=port)\n", - "\n", - " def HShow(comp, app):\n", - " @app.get('/')\n", - " def get(): return comp\n", - " display(HTML(f'Open in new tab'))\n", - " return HTMX(\"/\",port=port)\n", - " print(f\"Starter server on port {port}\")\n", - " Show = partial(HShow, app=app)\n", - " return app, rt, server, HShow, Show\n", - "app, rt = fast_app(pico=False, hdrs=Theme.blue.headers())\n", - "app, rt, server, HShow, Show = create_server(app, rt)" + "from utils import create_server\n", + "app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", + "server, Show = create_server(app)" ] }, { diff --git a/ex_nbs/01_tasks.ipynb b/docs/example_tasks.ipynb similarity index 94% rename from ex_nbs/01_tasks.ipynb rename to docs/example_tasks.ipynb index 02aa260..7ac5c5e 100644 --- a/ex_nbs/01_tasks.ipynb +++ b/docs/example_tasks.ipynb @@ -59,25 +59,9 @@ "source": [ "#| hide\n", "#| eval: false\n", - "\n", - "def create_server(app,rt):\n", - " if 'server' in globals(): globals()['server'].stop()\n", - " if IN_NOTEBOOK:\n", - " for port in range(8000,8030): \n", - " if is_port_free(port):\n", - " server = JupyUvi(app, port=port)\n", - "\n", - " def HShow(comp, app):\n", - " @app.get('/')\n", - " def get(): return comp\n", - " display(HTML(f'Open in new tab'))\n", - " return HTMX(\"/\",port=port)\n", - " print(f\"Starter server on port {port}\")\n", - " Show = partial(HShow, app=app)\n", - " return app, rt, server, HShow, Show\n", - " \n", - "app, rt = fast_app(pico=False, hdrs=Theme.blue.headers())\n", - "app, rt, server, HShow, Show = create_server(app, rt)" + "from utils import create_server\n", + "app, rt = fh.fast_app(pico=False, hdrs=Theme.blue.headers())\n", + "server, Show = create_server(app)" ] }, { diff --git a/ex_nbs/forms.py b/docs/forms.py similarity index 100% rename from ex_nbs/forms.py rename to docs/forms.py diff --git a/ex_nbs/mail.py b/docs/mail.py similarity index 100% rename from ex_nbs/mail.py rename to docs/mail.py diff --git a/ex_nbs/main.py b/docs/main.py similarity index 100% rename from ex_nbs/main.py rename to docs/main.py diff --git a/ex_nbs/music.py b/docs/music.py similarity index 100% rename from ex_nbs/music.py rename to docs/music.py diff --git a/ex_nbs/playground.py b/docs/playground.py similarity index 100% rename from ex_nbs/playground.py rename to docs/playground.py diff --git a/ex_nbs/requirements.txt b/docs/requirements.txt similarity index 100% rename from ex_nbs/requirements.txt rename to docs/requirements.txt diff --git a/ex_nbs/tasks.py b/docs/tasks.py similarity index 100% rename from ex_nbs/tasks.py rename to docs/tasks.py diff --git a/docs/utils.py b/docs/utils.py new file mode 120000 index 0000000..e674aaa --- /dev/null +++ b/docs/utils.py @@ -0,0 +1 @@ +app/utils.py \ No newline at end of file diff --git a/ex_nbs/API Reference.ipynb b/ex_nbs/API Reference.ipynb deleted file mode 100644 index e3e1660..0000000 --- a/ex_nbs/API Reference.ipynb +++ /dev/null @@ -1,208 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "d658774c", - "metadata": {}, - "source": [ - "# API Reference\n", - "\n", - "> Reference to all FrankenUI Components" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "id": "51edf9a5", - "metadata": {}, - "outputs": [], - "source": [ - "#| default_exp api_reference" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "id": "46a13904", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "from fasthtml.common import *\n", - "from fh_frankenui.core import *\n", - "from nbdev.showdoc import *\n", - "\n", - "from enum import EnumType\n", - "from collections.abc import Callable" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "id": "a74595d2", - "metadata": {}, - "outputs": [], - "source": [ - "#| hide\n", - "#| eval: false\n", - "from isaac_research.core import create_server" - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "id": "60b096aa", - "metadata": {}, - "outputs": [], - "source": [ - "#| hide\n", - "#| eval: false\n", - "app, rt = fast_app(pico=False, hdrs=Theme.blue.headers())\n", - "server, Show = create_server(app)" - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "id": "2cf43a87", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "def enum_to_html_table(enum_class):\n", - " headers = [\"Option\", \"Value\"]\n", - " rows = [[name, value.value] for name, value in enum_class.__members__.items()]\n", - " return Div(\n", - " Hr(cls='uk-divider-icon my-4'),\n", - " H3(enum_class.__name__,cls='my-4'),\n", - " P(I(enum_class.__doc__)),\n", - " TableFromLists(headers, rows, cls=(TableT.hover, 'uk-table-small')),)" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "id": "84a3b655", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "def create_doc_section(*content, title, md_content=None):\n", - " res = []\n", - " for c in content:\n", - " if isinstance(c, str): res.append(render_md(c))\n", - " elif isinstance(c, EnumType): res.append(enum_to_html_table(c))\n", - " elif isinstance(c, FT): res.append(c)\n", - " elif isinstance(c, Callable): \n", - " _html = str(show_doc(c, renderer=AdvHtmlRenderer))\n", - " res.append(NotStr(apply_classes(_html, class_map_mods={\"table\":'uk-table uk-table-hover uk-table-small'})))\n", - " else: res.append(c)\n", - " return Section(H1(title,cls='mb-10'), *res)" - ] - }, - { - "cell_type": "code", - "execution_count": 84, - "id": "9b56ffc1", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "docs_button = create_doc_section(Button, \n", - " ButtonT, \n", - " enum_to_html_table(ButtonT),\n", - " title=\"Buttons\")\n", - "\n", - "docs_heading = create_doc_section(H1, H2, H3, H4, \n", - " Card(H1(\"Level 1 Heading (H1)\"), \n", - " H2(\"Level 2 Heading (H2)\"), \n", - " H3(\"Level 3 Heading (H3)\"), \n", - " H4(\"Level 4 Heading (H4)\"),\n", - " cls='mt-8'),\n", - " title=\"Headings\")" - ] - }, - { - "cell_type": "code", - "execution_count": 85, - "id": "508f3b7e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "Open in new tab" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 85, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Show(\n", - "Container(docs_button,docs_headings\n", - " )\n", - ",iframe_height='1000px')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "150556e5", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c8de06f7", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/ex_nbs/app/api_reference.py b/ex_nbs/app/api_reference.py deleted file mode 100644 index 085b869..0000000 --- a/ex_nbs/app/api_reference.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Reference to all FrankenUI Components""" - -# AUTOGENERATED! DO NOT EDIT! File to edit: ../API Reference.ipynb. - -# %% auto 0 -__all__ = ['docs_button', 'docs_heading', 'enum_to_html_table', 'create_doc_section'] - -# %% ../API Reference.ipynb -from fasthtml.common import * -from fh_frankenui.core import * -from nbdev.showdoc import * - -from enum import EnumType -from collections.abc import Callable - -# %% ../API Reference.ipynb -def enum_to_html_table(enum_class): - headers = ["Option", "Value"] - rows = [[name, value.value] for name, value in enum_class.__members__.items()] - return Div( - Hr(cls='uk-divider-icon my-4'), - H3(enum_class.__name__,cls='my-4'), - P(I(enum_class.__doc__)), - TableFromLists(headers, rows, cls=(TableT.hover, 'uk-table-small')),) - -# %% ../API Reference.ipynb -def create_doc_section(*content, title, md_content=None): - res = [] - for c in content: - if isinstance(c, str): res.append(render_md(c)) - elif isinstance(c, EnumType): res.append(enum_to_html_table(c)) - elif isinstance(c, FT): res.append(c) - elif isinstance(c, Callable): - _html = str(show_doc(c, renderer=AdvHtmlRenderer)) - res.append(NotStr(apply_classes(_html, class_map_mods={"table":'uk-table uk-table-hover uk-table-small'}))) - else: res.append(c) - return Section(H1(title,cls='mb-10'), *res) - -# %% ../API Reference.ipynb -docs_button = create_doc_section(Button, - ButtonT, - enum_to_html_table(ButtonT), - title="Buttons") - -docs_heading = create_doc_section(H1, H2, H3, H4, - Card(H1("Level 1 Heading (H1)"), - H2("Level 2 Heading (H2)"), - H3("Level 3 Heading (H3)"), - H4("Level 4 Heading (H4)"), - cls='mt-8'), - title="Headings") diff --git a/ex_nbs/app/main.py b/ex_nbs/app/main.py deleted file mode 100644 index 349fe49..0000000 --- a/ex_nbs/app/main.py +++ /dev/null @@ -1,97 +0,0 @@ -"""FrankenUI Tasks Example""" - -# AUTOGENERATED! DO NOT EDIT! File to edit: ../99_main.ipynb. - -# %% auto 0 -__all__ = ['app', 'rt', 'tasks', 'cards', 'dashboard', 'forms', 'music', 'auth', 'playground', 'mail', 'getting_started', - 'buttons', 'headings', 'themeswitcher', 'index'] - -# %% ../99_main.ipynb -from fasthtml.common import * -import fasthtml.common as fh -from fh_frankenui.components import * -from fh_frankenui.core import * -import re -from fasthtml.components import Uk_theme_switcher - -# %% ../99_main.ipynb -app,rt = fast_app(hdrs=Theme.blue.headers(), pico=False) - -# %% ../99_main.ipynb -from tasks import tasks_homepage -from cards import cards_homepage -from dashboard import dashboard_homepage -from forms import forms_homepage -from music import music_homepage -from auth import auth_homepage -from playground import playground_homepage -from mail import mail_homepage - -# %% ../99_main.ipynb -@rt -def tasks(): return tasks_homepage -@rt -def cards(): return cards_homepage -@rt -def dashboard(): return dashboard_homepage -@rt -def forms(): return forms_homepage -@rt -def music(): return music_homepage -@rt -def auth(): return auth_homepage -@rt -def playground(): return playground_homepage -@rt -def mail(): return mail_homepage - -# %% ../99_main.ipynb -@rt -def getting_started(): - return render_md(open('GettingStarted.md').read()) - -# %% ../99_main.ipynb -from api_reference import docs_button, docs_heading - -# %% ../99_main.ipynb -@rt -def buttons(): return Container(docs_button) -@rt -def headings(): return Container(docs_heading) - -# %% ../99_main.ipynb -@rt -def themeswitcher(): return Div(Uk_theme_switcher(),cls="p-12") - -# %% ../99_main.ipynb -@rt -def index(): - return Body(cls="bg-background text-foreground")(Div(cls="flex w-full")( - NavContainer( - Li(A("Getting Started", hx_get=getting_started, hx_target='#content')), - NavParentLi( - A("API Reference"), - NavContainer( - Li(A('Buttons',hx_get=buttons, hx_target='#content')), - Li(A('Heading',hx_get=headings, hx_target='#content')), - parent=False - )), - NavParentLi( - A('Examples'), - NavContainer( - Li(A('Tasks', hx_get=tasks, hx_target='#content')), - Li(A('Cards', hx_get=cards, hx_target='#content')), - Li(A('Dashboard', hx_get=dashboard, hx_target='#content')), - Li(A('Form', hx_get=forms, hx_target='#content')), - Li(A('Music', hx_get=music, hx_target='#content')), - Li(A('Auth', hx_get=auth, hx_target='#content')), - Li(A('Playground',hx_get=playground, hx_target='#content')), - Li(A('Mail', hx_get=mail, hx_target='#content')), - parent=False)), - Li(A("Theme",hx_get=themeswitcher, hx_target='#content')), - - uk_nav=True, cls=(NavT.primary,"space-y-4 p-4 w-1/7")), - Div(getting_started(), id='content',cls='w-full'))) - -# %% ../99_main.ipynb -serve() diff --git a/fh_frankenui/core.py b/fh_frankenui/core.py index f85e11f..9cdb010 100644 --- a/fh_frankenui/core.py +++ b/fh_frankenui/core.py @@ -157,12 +157,12 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('bu ghost = auto() # %% ../lib_nbs/01_core.ipynb -def Button(*c:str|FT, # Components to go inside the Button - cls:str|Enum=ButtonT.default, # cls for the Button (see ButtonT for style options) - **kwargs # any other kwargs will be passed to the button - )-> FT: # Button w/ `type=button` and `uk-button` cls +def Button(*c: Union[str, FT], + cls: Union[str, Enum]=ButtonT.default, + **kwargs + ) -> FT: "A Button with Uk Styling" - return fh.Button(*c, cls=('uk-button',stringify(cls)), type='button', **kwargs) + return fh.Button(*c, cls=('uk-button', stringify(cls)), type='button', **kwargs) # %% ../lib_nbs/01_core.ipynb def H1(*c:FT|str, # Components to go inside the Heading @@ -265,7 +265,6 @@ def CheckboxX(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-chec def Range(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-range',stringify(cls)), type='range', **kwargs) def Toggle_switch(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-toggle-switch',stringify(cls)), type='checkbox', **kwargs) def TextArea(*c, cls=(), **kwargs): return fh.Textarea(*c, cls=('uk-textarea',stringify(cls)), **kwargs) -def Button(*c, cls=ButtonT.default, **kwargs):return fh.Button(*c, cls=('uk-button',stringify(cls)), type='button', **kwargs) def Switch(*c, cls='min-w-9', **kwargs): return fh.Input(*c, cls=('uk-toggle-switch',stringify(cls)), type='checkbox', **kwargs) # %% ../lib_nbs/01_core.ipynb diff --git a/lib_nbs/01_core.ipynb b/lib_nbs/01_core.ipynb index 48c271b..cd9643b 100644 --- a/lib_nbs/01_core.ipynb +++ b/lib_nbs/01_core.ipynb @@ -11,7 +11,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -34,7 +34,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -57,7 +57,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -69,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -112,7 +112,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -122,7 +122,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -158,7 +158,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -181,7 +181,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -205,7 +205,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -237,7 +237,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -247,7 +247,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -292,7 +292,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -325,7 +325,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -334,7 +334,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -359,7 +359,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -377,7 +377,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -396,7 +396,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -405,17 +405,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "#| export\n", - "def Button(*c:str|FT, # Components to go inside the Button\n", - " cls:str|Enum=ButtonT.default, # cls for the Button (see ButtonT for style options)\n", - " **kwargs # any other kwargs will be passed to the button \n", - " )-> FT: # Button w/ `type=button` and `uk-button` cls\n", + "def Button(*c: Union[str, FT],\n", + " cls: Union[str, Enum]=ButtonT.default,\n", + " **kwargs\n", + " ) -> FT:\n", " \"A Button with Uk Styling\"\n", - " return fh.Button(*c, cls=('uk-button',stringify(cls)), type='button', **kwargs)" + " return fh.Button(*c, cls=('uk-button', stringify(cls)), type='button', **kwargs)" ] }, { @@ -811,7 +811,6 @@ "def Range(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-range',stringify(cls)), type='range', **kwargs)\n", "def Toggle_switch(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-toggle-switch',stringify(cls)), type='checkbox', **kwargs)\n", "def TextArea(*c, cls=(), **kwargs): return fh.Textarea(*c, cls=('uk-textarea',stringify(cls)), **kwargs)\n", - "def Button(*c, cls=ButtonT.default, **kwargs):return fh.Button(*c, cls=('uk-button',stringify(cls)), type='button', **kwargs)\n", "def Switch(*c, cls='min-w-9', **kwargs): return fh.Input(*c, cls=('uk-toggle-switch',stringify(cls)), type='checkbox', **kwargs)" ] }, @@ -3319,9 +3318,21 @@ ], "metadata": { "kernelspec": { - "display_name": "python3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.4" } }, "nbformat": 4,