Updated chat ui (#3370)

* test

* changes

* chagnes

* changes

* changes

* changes

* changes

* Update CHANGELOG.md

* changes

* Update demo/chatbot_multimodal/run.py

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* Update demo/chatbot_simple_demo/run.py

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>

* changes

* changes

* changes

---------

Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
aliabid94 2023-03-03 18:17:48 -08:00 committed by GitHub
parent 99945c951b
commit 02fb8f5a1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 163 additions and 150 deletions

View File

@ -76,10 +76,11 @@ component by [@abidlabs](https://github.com/abidlabs) in [PR 3275](https://githu
- Flaky python tests no longer cancel non-flaky tests by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3344](https://github.com/gradio-app/gradio/pull/3344)
## Breaking Changes:
No changes to highlight.
- Chatbot bubble colors can no longer be set by `chatbot.style(color_map=)` by [@aliabid94] in [PR 3370](https://github.com/gradio-app/gradio/pull/3370)
## Full Changelog:
- Fixed comment typo in components.py by [@eltociear](https://github.com/eltociear) in [PR 3235](https://github.com/gradio-app/gradio/pull/3235)
- Cleaned up chatbot ui look and feel by [@aliabid94] in [PR 3370](https://github.com/gradio-app/gradio/pull/3370)
## Contributors Shoutout:
No changes to highlight.

View File

@ -1 +1 @@
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: chatbot_multimodal"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "def add_text(state, text):\n", " state = state + [(text, text + \"?\")]\n", " return state, state\n", "\n", "def add_image(state, image):\n", " state = state + [(f\"![](/file={image.name})\", \"Cool pic!\")]\n", " return state, state\n", "\n", "\n", "with gr.Blocks(css=\"#chatbot .overflow-y-auto{height:500px}\") as demo:\n", " chatbot = gr.Chatbot(elem_id=\"chatbot\")\n", " state = gr.State([])\n", " \n", " with gr.Row():\n", " with gr.Column(scale=0.85):\n", " txt = gr.Textbox(show_label=False, placeholder=\"Enter text and press enter, or upload an image\").style(container=False)\n", " with gr.Column(scale=0.15, min_width=0):\n", " btn = gr.UploadButton(\"\ud83d\uddbc\ufe0f\", file_types=[\"image\"])\n", " \n", " txt.submit(add_text, [state, txt], [state, chatbot])\n", " txt.submit(lambda :\"\", None, txt)\n", " btn.upload(add_image, [state, btn], [state, chatbot])\n", " \n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: chatbot_multimodal"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "from urllib.parse import quote\n", "\n", "def add_text(history, text):\n", " history = history + [(text, text + \"?\")]\n", " return history\n", "\n", "def add_image(history, image):\n", " history = history + [(f\"![](/file={quote(image.name)})\", \"Cool pic!\")]\n", " return history\n", "\n", "\n", "with gr.Blocks(css=\"#chatbot .overflow-y-auto{height:500px}\") as demo:\n", " chatbot = gr.Chatbot(elem_id=\"chatbot\")\n", " \n", " with gr.Row():\n", " with gr.Column(scale=0.85):\n", " txt = gr.Textbox(show_label=False, placeholder=\"Enter text and press enter, or upload an image\").style(container=False)\n", " with gr.Column(scale=0.15, min_width=0):\n", " btn = gr.UploadButton(\"\ud83d\uddbc\ufe0f\", file_types=[\"image\"])\n", " \n", " txt.submit(add_text, [chatbot, txt], [chatbot])\n", " txt.submit(lambda :\"\", None, txt, queue=False)\n", " btn.upload(add_image, [chatbot, btn], [chatbot])\n", " \n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -1,17 +1,17 @@
import gradio as gr
from urllib.parse import quote
def add_text(state, text):
state = state + [(text, text + "?")]
return state, state
def add_text(history, text):
history = history + [(text, text + "?")]
return history
def add_image(state, image):
state = state + [(f"![](/file={image.name})", "Cool pic!")]
return state, state
def add_image(history, image):
history = history + [(f"![](/file={quote(image.name)})", "Cool pic!")]
return history
with gr.Blocks(css="#chatbot .overflow-y-auto{height:500px}") as demo:
chatbot = gr.Chatbot(elem_id="chatbot")
state = gr.State([])
with gr.Row():
with gr.Column(scale=0.85):
@ -19,9 +19,9 @@ with gr.Blocks(css="#chatbot .overflow-y-auto{height:500px}") as demo:
with gr.Column(scale=0.15, min_width=0):
btn = gr.UploadButton("🖼️", file_types=["image"])
txt.submit(add_text, [state, txt], [state, chatbot])
txt.submit(lambda :"", None, txt)
btn.upload(add_image, [state, btn], [state, chatbot])
txt.submit(add_text, [chatbot, txt], [chatbot])
txt.submit(lambda :"", None, txt, queue=False)
btn.upload(add_image, [chatbot, btn], [chatbot])
if __name__ == "__main__":
demo.launch()

View File

@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: chatbot_simple_demo"]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "import random\n", "\n", "def respond(chat_history, message):\n", " response = random.choice([\"Yes\", \"No\"])\n", " return chat_history + [[message, response]]\n", "\n", "with gr.Blocks() as demo:\n", " chatbot = gr.Chatbot()\n", " msg = gr.Textbox()\n", " clear = gr.Button(\"Clear\")\n", "\n", " msg.submit(respond, [chatbot, msg], chatbot)\n", " clear.click(lambda: None, None, chatbot, queue=False)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

View File

@ -0,0 +1,17 @@
import gradio as gr
import random
def respond(chat_history, message):
response = random.choice(["Yes", "No"])
return chat_history + [[message, response]]
with gr.Blocks() as demo:
chatbot = gr.Chatbot()
msg = gr.Textbox()
clear = gr.Button("Clear")
msg.submit(respond, [chatbot, msg], chatbot)
clear.click(lambda: None, None, chatbot, queue=False)
if __name__ == "__main__":
demo.launch()

View File

@ -4004,16 +4004,12 @@ class Chatbot(Changeable, IOComponent, JSONSerializable):
)
return y
def style(self, *, color_map: Tuple[str, str] | None = None, **kwargs):
def style(self, **kwargs):
"""
This method can be used to change the appearance of the Chatbot component.
Parameters:
color_map: Tuple containing colors to apply to user and response chat bubbles.
Returns:
"""
if color_map is not None:
self._style["color_map"] = color_map
if kwargs.get("color_map") is not None:
warnings.warn("The 'color_map' parameter has been deprecated.")
return Component.style(
self,

View File

@ -912,6 +912,7 @@ def get_markdown_parser() -> MarkdownIt:
"linkify": True,
"typographer": True,
"html": True,
"breaks": True,
},
)
.use(dollarmath_plugin, renderer=tex2svg, allow_digits=False)

View File

@ -13,7 +13,18 @@ This tutorial will show how to take a pretrained chatbot model and deploy it wit
<iframe src="https://dawood-chatbot-guide.hf.space" frameBorder="0" height="350" title="Gradio app" class="container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>
Chatbots are *stateful*, meaning that the model's prediction can change depending on how the user has previously interacted with the model. So, in this tutorial, we will also cover how to use **state** with Gradio demos.
## A Simple Chatbot Demo
Let's start with a simple demo, with no actual model. Our bot will randomly respond "yes" or "no" to any input.
$code_chatbot_simple_demo
$demo_chatbot_simple_demo
The chatbot value stores the entire history of the conversation, as a list of response pairs between the user and bot. We pass the entire history of the chatbot to the function and back to the component. To clear the chatbot, we pass it `None`.
### Using a Model
Chatbots are *stateful*, meaning we need to track how the user has previously interacted with the model. So, in this tutorial, we will also cover how to use **state** with Gradio demos.
### Prerequisites

View File

@ -12,6 +12,7 @@
export let label: string;
export let show_label: boolean = true;
export let color_map: Record<string, string> = {};
export let root: string;
$: if (!style.color_map && Object.keys(color_map).length) {
style.color_map = color_map;
@ -30,7 +31,7 @@
/>
{/if}
<ChatBot
{style}
{root}
{value}
pending_message={loading_status?.status === "pending"}
on:change

View File

@ -21,7 +21,8 @@ describe("Chatbot", () => {
const { getAllByTestId } = render(Chatbot, {
loading_status,
label: "hello",
value: [["user message one", "bot message one"]]
value: [["user message one", "bot message one"]],
root: ""
});
const bot = getAllByTestId("user")[0];
@ -35,7 +36,8 @@ describe("Chatbot", () => {
const { component, getAllByTestId } = render(Chatbot, {
loading_status,
label: "hello",
value: [["user message one", "bot message one"]]
value: [["user message one", "bot message one"]],
root: ""
});
const bot = getAllByTestId("user");

View File

@ -1,19 +1,25 @@
<script lang="ts">
import { beforeUpdate, afterUpdate, createEventDispatcher } from "svelte";
import { colors } from "@gradio/theme";
import type { Styles } from "@gradio/utils";
export let value: Array<[string | null, string | null]> | null;
let old_value: Array<[string | null, string | null]> | null;
export let style: Styles = {};
export let pending_message: boolean = false;
export let root: string;
let div: HTMLDivElement;
let autoscroll: Boolean;
const dispatch = createEventDispatcher<{ change: undefined }>();
const redirect_src_url = (src: string) =>
src.replace('src="/file', `src="${root}file`);
$: _value = value || [];
$: _value = value
? value.map(([user_msg, bot_msg]) => [
user_msg ? redirect_src_url(user_msg) : null,
bot_msg ? redirect_src_url(bot_msg) : null
])
: [];
beforeUpdate(() => {
autoscroll =
div && div.offsetHeight + div.scrollTop > div.scrollHeight - 20;
@ -36,24 +42,6 @@
dispatch("change");
}
}
$: _colors = get_colors();
function get_color(c: string) {
if (c in colors) {
return colors[c as keyof typeof colors].primary;
} else {
return c;
}
}
function get_colors() {
if (!style.color_map) {
return ["#fb923c", "#9ca3af"];
} else {
return [get_color(style.color_map[0]), get_color(style.color_map[1])];
}
}
</script>
<div class="wrap" bind:this={div}>
@ -64,7 +52,6 @@
class:latest={i === _value.length - 1}
class="message user"
class:hide={message[0] === null}
style={"background-color:" + _colors[0]}
>
{@html message[0]}
</div>
@ -73,17 +60,12 @@
class:latest={i === _value.length - 1}
class="message bot"
class:hide={message[1] === null}
style={"background-color:" + _colors[1]}
>
{@html message[1]}
</div>
{/each}
{#if pending_message}
<div
data-testid="bot"
class="message user pending"
style={"background-color:" + _colors[0]}
>
<div data-testid="bot" class="message pending">
<div class="dot-flashing" />
&nbsp;
<div class="dot-flashing" />
@ -96,61 +78,58 @@
<style>
.wrap {
margin-top: var(--size-4);
height: 100%;
max-height: 480px;
overflow-y: auto;
}
.message-wrap {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: var(--size-4);
padding: var(--size-3);
}
.message-wrap > * + * {
margin-top: var(--size-4);
}
.message-wrap > div :global(img) {
border-radius: 13px;
max-width: 30vw;
}
.message {
border-width: var(--chatbot-border-width);
border-width: 1px;
border-style: solid;
border-radius: var(--chatbot-border-radius);
padding: var(--size-2) var(--size-3);
max-width: 75%;
font-size: var(--scale-00);
line-height: var(--line-xs);
border-radius: var(--size-2);
padding: var(--size-3);
font-size: var(--scale-0);
line-height: var(--line-md);
overflow-wrap: break-word;
}
.user {
border-color: var(--chatbot-user-border-color-base);
margin-left: var(--size-6);
border-color: var(--color-accent-light);
border-bottom-right-radius: 0;
background: var(--chatbot-user-background-base);
color: var(--chatbot-user-text-color-base);
background: var(--color-accent-soft);
color: var(--color-text-body);
}
.user.latest {
border-color: var(--chatbot-user-border-color-latest);
background: var(--chatbot-user-background-latest);
color: var(--chatbot-user-text-color-latest);
}
.pending,
.bot {
place-self: start;
border-color: var(--chatbot-bot-border-color-base);
border-bottom-left-radius: 0;
background: var(--chatbot-bot-background-base);
color: var(--chatbot-bot-text-color-base);
border-color: var(--color-border-primary);
background: var(--color-background-secondary);
}
.bot.latest {
border-color: var(--chatbot-bot-border-color-latest);
background: var(--chatbot-bot-background-latest);
color: var(--chatbot-bot-text-color-latest);
.bot {
margin-right: var(--size-6);
border-bottom-left-radius: 0;
padding-left: var(--size-9);
}
.pending {
margin: 0 var(--size-6);
}
:global(.dark) .user {
border-color: var(--color-border-primary);
background: var(--color-grey-700);
color: var(--color-text-body);
}
.pending {
@ -162,10 +141,10 @@
.dot-flashing {
animation: dot-flashing 1s infinite linear alternate;
border-radius: 5px;
background-color: white;
background-color: var(--color-text-subdued);
width: 5px;
height: 5px;
color: white;
color: var(--color-text-subdued);
}
.dot-flashing:nth-child(2) {
animation-delay: 0.33s;
@ -174,6 +153,17 @@
animation-delay: 0.66s;
}
/* Small screen */
@media (max-width: 480px) {
.user {
align-self: flex-end;
}
.bot {
align-self: flex-start;
padding-left: var(--size-3);
}
}
@keyframes dot-flashing {
0% {
opacity: 0.8;
@ -185,6 +175,14 @@
opacity: 0.8;
}
}
.message-wrap .message :global(img) {
margin: var(--size-2);
max-height: 200px;
}
.message-wrap .message :global(a) {
color: var(--color-text-link);
text-decoration: underline;
}
.hide {
display: none;

View File

@ -36,6 +36,7 @@ const foundation_light = {
highlight: "color.accent.base"
},
accent: {
soft: "var(--color-orange-50)",
base: "var(--color-orange-500)",
light: "var(--color-orange-300)",
dark: "var(--color-orange-700)"
@ -315,29 +316,6 @@ const theme_light = {
}
}
},
chatbot: {
border: {
radius: "var(--radius-3xl)",
width: "0"
},
// do we do this via theme or constructor?
user: {
background: {
base: "var(--color-orange-400)",
latest: "var(--color-orange-400)"
},
text: { color: { base: "white", latest: "white" } },
border: { color: { base: "transparent", latest: "transparent" } }
},
bot: {
background: {
base: "var(--color-grey-400)",
latest: "var(--color-grey-400)"
},
text: { color: { base: "white", latest: "white" } },
border: { color: { base: "transparent", latest: "transparent" } }
}
},
label: {
gradient: {
from: "var(--color-orange-400)",

93
ui/pnpm-lock.yaml generated
View File

@ -1,4 +1,4 @@
lockfileVersion: 5.4
lockfileVersion: 5.3
importers:
@ -48,7 +48,7 @@ importers:
'@tailwindcss/forms': 0.5.0_tailwindcss@3.1.6
'@testing-library/dom': 8.11.3
'@testing-library/svelte': 3.1.0_svelte@3.49.0
'@testing-library/user-event': 13.5.0_gzufz4q333be4gqfrvipwvqt6a
'@testing-library/user-event': 13.5.0_@testing-library+dom@8.11.3
autoprefixer: 10.4.4_postcss@8.4.6
babylonjs: 5.18.0
babylonjs-loaders: 5.18.0
@ -65,15 +65,15 @@ importers:
postcss-nested: 5.0.6_postcss@8.4.6
postcss-prefix-selector: 1.16.0_postcss@8.4.6
prettier: 2.6.2
prettier-plugin-css-order: 1.3.0_ob5okuz2s5mlecytbeo2erc43a
prettier-plugin-svelte: 2.7.0_3cyj5wbackxvw67rnaarcmbw7y
prettier-plugin-css-order: 1.3.0_postcss@8.4.6+prettier@2.6.2
prettier-plugin-svelte: 2.7.0_prettier@2.6.2+svelte@3.49.0
sirv: 2.0.2
sirv-cli: 2.0.2
svelte: 3.49.0
svelte-check: 2.8.0_mgmdnb6x5rpawk37gozc2sbtta
svelte-check: 2.8.0_postcss@8.4.6+svelte@3.49.0
svelte-i18n: 3.3.13_svelte@3.49.0
svelte-preprocess: 4.10.6_mlkquajfpxs65rn6bdfntu7nmy
tailwindcss: 3.1.6_postcss@8.4.6
svelte-preprocess: 4.10.6_62d50a01257de5eec5be08cad9d3ed66
tailwindcss: 3.1.6
tinyspy: 0.3.0
typescript: 4.7.4
vite: 2.9.9
@ -332,7 +332,7 @@ importers:
'@gradio/utils': link:../utils
'@rollup/plugin-json': 5.0.2
plotly.js-dist-min: 2.11.1
svelte-vega: 1.2.0_36sthfwhgi34qytpvkzggbhnle
svelte-vega: 1.2.0_vega-lite@5.6.0+vega@5.22.1
vega: 5.22.1
vega-lite: 5.6.0_vega@5.22.1
@ -464,14 +464,14 @@ importers:
'@gradio/video': link:../video
svelte: 3.49.0
devDependencies:
'@sveltejs/adapter-auto': 1.0.0-next.91_b2bjiolq6much32vueqoio7eoy
'@sveltejs/adapter-auto': 1.0.0-next.91_@sveltejs+kit@1.0.0-next.318
'@sveltejs/kit': 1.0.0-next.318_svelte@3.49.0
autoprefixer: 10.4.2_postcss@8.4.6
postcss: 8.4.6
postcss-load-config: 3.1.1
svelte-check: 2.4.1_onvlxjpnd23pr3hxbmout2wrjm
svelte-preprocess: 4.10.2_2udzbozq3wemyrf2xz7puuv2zy
tailwindcss: 3.1.6_postcss@8.4.6
svelte-check: 2.4.1_736abba5ed1eb6f8ecf70b1d49ead14b
svelte-preprocess: 4.10.2_d50790bb30dd88cc44babe7efa52bace
tailwindcss: 3.1.6
tslib: 2.3.1
typescript: 4.5.5
@ -673,7 +673,7 @@ packages:
picomatch: 2.3.1
dev: false
/@sveltejs/adapter-auto/1.0.0-next.91_b2bjiolq6much32vueqoio7eoy:
/@sveltejs/adapter-auto/1.0.0-next.91_@sveltejs+kit@1.0.0-next.318:
resolution: {integrity: sha512-U57tQdzTfFINim8tzZSARC9ztWPzwOoHwNOpGdb2o6XrD0mEQwU9DsII7dBblvzg+xCnmd0pw7PDtXz5c5t96w==}
peerDependencies:
'@sveltejs/kit': ^1.0.0-next.587
@ -729,7 +729,7 @@ packages:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
dependencies:
mini-svg-data-uri: 1.4.4
tailwindcss: 3.1.6_postcss@8.4.6
tailwindcss: 3.1.6
dev: false
/@testing-library/dom/7.31.2:
@ -770,7 +770,7 @@ packages:
svelte: 3.49.0
dev: false
/@testing-library/user-event/13.5.0_gzufz4q333be4gqfrvipwvqt6a:
/@testing-library/user-event/13.5.0_@testing-library+dom@8.11.3:
resolution: {integrity: sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==}
engines: {node: '>=10', npm: '>=6'}
peerDependencies:
@ -2975,25 +2975,25 @@ packages:
postcss-value-parser: 4.2.0
dev: false
/postcss-import/14.1.0_postcss@8.4.6:
/postcss-import/14.1.0_postcss@8.4.21:
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
engines: {node: '>=10.0.0'}
peerDependencies:
postcss: ^8.0.0
dependencies:
postcss: 8.4.6
postcss: 8.4.21
postcss-value-parser: 4.2.0
read-cache: 1.0.0
resolve: 1.22.1
/postcss-js/4.0.0_postcss@8.4.6:
/postcss-js/4.0.0_postcss@8.4.21:
resolution: {integrity: sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==}
engines: {node: ^12 || ^14 || >= 16}
peerDependencies:
postcss: ^8.3.3
dependencies:
camelcase-css: 2.0.1
postcss: 8.4.6
postcss: 8.4.21
/postcss-less/6.0.0_postcss@8.4.6:
resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==}
@ -3017,7 +3017,7 @@ packages:
yaml: 1.10.2
dev: true
/postcss-load-config/3.1.4_postcss@8.4.6:
/postcss-load-config/3.1.4_postcss@8.4.21:
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
engines: {node: '>= 10'}
peerDependencies:
@ -3030,9 +3030,18 @@ packages:
optional: true
dependencies:
lilconfig: 2.0.6
postcss: 8.4.6
postcss: 8.4.21
yaml: 1.10.2
/postcss-nested/5.0.6_postcss@8.4.21:
resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==}
engines: {node: '>=12.0'}
peerDependencies:
postcss: ^8.2.14
dependencies:
postcss: 8.4.21
postcss-selector-parser: 6.0.9
/postcss-nested/5.0.6_postcss@8.4.6:
resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==}
engines: {node: '>=12.0'}
@ -3041,6 +3050,7 @@ packages:
dependencies:
postcss: 8.4.6
postcss-selector-parser: 6.0.9
dev: false
/postcss-prefix-selector/1.16.0_postcss@8.4.21:
resolution: {integrity: sha512-rdVMIi7Q4B0XbXqNUEI+Z4E+pueiu/CS5E6vRCQommzdQ/sgsS4dK42U7GX8oJR+TJOtT+Qv3GkNo6iijUMp3Q==}
@ -3099,7 +3109,6 @@ packages:
nanoid: 3.3.4
picocolors: 1.0.0
source-map-js: 1.0.2
dev: false
/postcss/8.4.6:
resolution: {integrity: sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==}
@ -3109,7 +3118,7 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/prettier-plugin-css-order/1.3.0_ob5okuz2s5mlecytbeo2erc43a:
/prettier-plugin-css-order/1.3.0_postcss@8.4.6+prettier@2.6.2:
resolution: {integrity: sha512-wOS4qlbUARCoiiuOG0TiB/j751soC3+gUnMMva5HVWKvHJdLNYqh+jXK3MvvixR6xkJVPxHSF7rIIhkHIuHTFg==}
engines: {node: '>=14'}
peerDependencies:
@ -3124,7 +3133,7 @@ packages:
- postcss
dev: false
/prettier-plugin-svelte/2.7.0_3cyj5wbackxvw67rnaarcmbw7y:
/prettier-plugin-svelte/2.7.0_prettier@2.6.2+svelte@3.49.0:
resolution: {integrity: sha512-fQhhZICprZot2IqEyoiUYLTRdumULGRvw0o4dzl5jt0jfzVWdGqeYW27QTWAeXhoupEZJULmNoH3ueJwUWFLIA==}
peerDependencies:
prettier: ^1.16.4 || ^2.0.0
@ -3582,7 +3591,7 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
/svelte-check/2.4.1_onvlxjpnd23pr3hxbmout2wrjm:
/svelte-check/2.4.1_736abba5ed1eb6f8ecf70b1d49ead14b:
resolution: {integrity: sha512-xhf3ShP5rnRwBokrgTBJ/0cO9QIc1DAVu1NWNRTfCDsDBNjGmkS3HgitgUadRuoMKj1+irZR/yHJ+Uqobnkbrw==}
hasBin: true
peerDependencies:
@ -3596,7 +3605,7 @@ packages:
sade: 1.8.1
source-map: 0.7.3
svelte: 3.49.0
svelte-preprocess: 4.10.2_2udzbozq3wemyrf2xz7puuv2zy
svelte-preprocess: 4.10.2_d50790bb30dd88cc44babe7efa52bace
typescript: 4.5.5
transitivePeerDependencies:
- '@babel/core'
@ -3611,7 +3620,7 @@ packages:
- sugarss
dev: true
/svelte-check/2.8.0_mgmdnb6x5rpawk37gozc2sbtta:
/svelte-check/2.8.0_postcss@8.4.6+svelte@3.49.0:
resolution: {integrity: sha512-HRL66BxffMAZusqe5I5k26mRWQ+BobGd9Rxm3onh7ZVu0nTk8YTKJ9vu3LVPjUGLU9IX7zS+jmwPVhJYdXJ8vg==}
hasBin: true
peerDependencies:
@ -3624,7 +3633,7 @@ packages:
picocolors: 1.0.0
sade: 1.8.1
svelte: 3.49.0
svelte-preprocess: 4.10.6_mlkquajfpxs65rn6bdfntu7nmy
svelte-preprocess: 4.10.6_62d50a01257de5eec5be08cad9d3ed66
typescript: 4.7.4
transitivePeerDependencies:
- '@babel/core'
@ -3662,7 +3671,7 @@ packages:
tiny-glob: 0.2.9
dev: false
/svelte-preprocess/4.10.2_2udzbozq3wemyrf2xz7puuv2zy:
/svelte-preprocess/4.10.2_d50790bb30dd88cc44babe7efa52bace:
resolution: {integrity: sha512-aPpkCreSo8EL/y8kJSa1trhiX0oyAtTjlNNM7BNjRAsMJ8Yy2LtqHt0zyd4pQPXt+D4PzbO3qTjjio3kwOxDlA==}
engines: {node: '>= 9.11.2'}
requiresBuild: true
@ -3715,7 +3724,7 @@ packages:
typescript: 4.5.5
dev: true
/svelte-preprocess/4.10.6_mlkquajfpxs65rn6bdfntu7nmy:
/svelte-preprocess/4.10.6_62d50a01257de5eec5be08cad9d3ed66:
resolution: {integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==}
engines: {node: '>= 9.11.2'}
requiresBuild: true
@ -3771,7 +3780,7 @@ packages:
resolution: {integrity: sha512-VTWHOdwDyWbndGZnI0PQJY9DO7hgQlNubtCcCL6Wlypv5dU4vEsc4A1sX9TWMuvebEe4332SgsQQHzOdZ+guhQ==}
dev: false
/svelte-vega/1.2.0_36sthfwhgi34qytpvkzggbhnle:
/svelte-vega/1.2.0_vega-lite@5.6.0+vega@5.22.1:
resolution: {integrity: sha512-MsDdO+l7o/d9d4mVkh8MBDhqZvJ45lpuprBaTj0V/ZilIG902QERHFQlam3ZFcR9C9OIKSpmPqINssWNPkDdcA==}
peerDependencies:
vega: '*'
@ -3779,7 +3788,7 @@ packages:
dependencies:
fast-deep-equal: 3.1.3
vega: 5.22.1
vega-embed: 6.21.0_36sthfwhgi34qytpvkzggbhnle
vega-embed: 6.21.0_vega-lite@5.6.0+vega@5.22.1
vega-lite: 5.6.0_vega@5.22.1
dev: false
@ -3806,12 +3815,10 @@ packages:
resolution: {integrity: sha512-hIdwt/c/e1ONnr2RJmfBxEAj/J6KQQWKdToF3Qw8ZNRsTNNteGkOe63rQy9I7J5UNlr8Yl0wkzIr9wgLY94x0Q==}
dev: false
/tailwindcss/3.1.6_postcss@8.4.6:
/tailwindcss/3.1.6:
resolution: {integrity: sha512-7skAOY56erZAFQssT1xkpk+kWt2NrO45kORlxFPXUt3CiGsVPhH1smuH5XoDH6sGPXLyBv+zgCKA2HWBsgCytg==}
engines: {node: '>=12.13.0'}
hasBin: true
peerDependencies:
postcss: ^8.0.9
dependencies:
arg: 5.0.2
chokidar: 3.5.3
@ -3826,11 +3833,11 @@ packages:
normalize-path: 3.0.0
object-hash: 3.0.0
picocolors: 1.0.0
postcss: 8.4.6
postcss-import: 14.1.0_postcss@8.4.6
postcss-js: 4.0.0_postcss@8.4.6
postcss-load-config: 3.1.4_postcss@8.4.6
postcss-nested: 5.0.6_postcss@8.4.6
postcss: 8.4.21
postcss-import: 14.1.0_postcss@8.4.21
postcss-js: 4.0.0_postcss@8.4.21
postcss-load-config: 3.1.4_postcss@8.4.21
postcss-nested: 5.0.6_postcss@8.4.21
postcss-selector-parser: 6.0.10
postcss-value-parser: 4.2.0
quick-lru: 5.1.1
@ -4013,7 +4020,7 @@ packages:
- encoding
dev: false
/vega-embed/6.21.0_36sthfwhgi34qytpvkzggbhnle:
/vega-embed/6.21.0_vega-lite@5.6.0+vega@5.22.1:
resolution: {integrity: sha512-Tzo9VAfgNRb6XpxSFd7uphSeK2w5OxDY2wDtmpsQ+rQlPSEEI9TE6Jsb2nHRLD5J4FrmXKLrTcORqidsNQSXEg==}
peerDependencies:
vega: ^5.21.0
@ -4027,7 +4034,7 @@ packages:
vega-interpreter: 1.0.4
vega-lite: 5.6.0_vega@5.22.1
vega-schema-url-parser: 2.2.0
vega-themes: 2.12.0_36sthfwhgi34qytpvkzggbhnle
vega-themes: 2.12.0_vega-lite@5.6.0+vega@5.22.1
vega-tooltip: 0.28.0
dev: false
bundledDependencies:
@ -4246,7 +4253,7 @@ packages:
d3-array: 3.1.1
dev: false
/vega-themes/2.12.0_36sthfwhgi34qytpvkzggbhnle:
/vega-themes/2.12.0_vega-lite@5.6.0+vega@5.22.1:
resolution: {integrity: sha512-gHNYCzDgexSQDmGzQsxH57OYgFVbAOmvhIYN3MPOvVucyI+zhbUawBVIVNzG9ftucRp0MaaMVXi6ctC5HLnBsg==}
peerDependencies:
vega: '*'

View File

@ -3,6 +3,6 @@
Run the following commands in order:
- `pip install -r requirements.txt`
- `npm install`
- `npm run build` (or `npm run build-mac` on Mac OSX)
- `npm run build --url a`
The website will be built in the build/ folder as a static website. To launch, run: `cd build && python3 -m http.server`