diff --git a/.gitignore b/.gitignore index ebe4ee2c..319c2af8 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,7 @@ yarn-error.log* .vercel # Hugo resources folder -resources \ No newline at end of file +resources + +# Env +.env* \ No newline at end of file diff --git a/api/github.js b/api/github.js new file mode 100644 index 00000000..bceb2841 --- /dev/null +++ b/api/github.js @@ -0,0 +1,47 @@ +// Fetches star count, contributor count and top contributors from the Github API +import axios from "axios"; +import parse from "parse-link-header"; + +const options = { + headers: { Authorization: `Bearer ${process.env.GITHUB_ACCESS_TOKEN}` }, +}; + +export default async function handler(request, response) { + try { + // Fetch the data + const [starsData, totalContributorsData, topContributorsData] = + await Promise.all([ + // Stars + axios.get("https://api.github.com/repos/deepset-ai/haystack", options), + + // Total contributors + axios.get( + "https://api.github.com/repos/deepset-ai/haystack/contributors?per_page=1", + options + ), + + // Top 10 contributors + axios.get( + "https://api.github.com/repos/deepset-ai/haystack/contributors?per_page=10", + options + ), + ]); + + const stars = starsData.data.stargazers_count; + const parsed = parse(totalContributorsData.headers.link); // parse link header to get total pages + const contributors = parsed.last.page; + const topContributors = topContributorsData.data.map((contrib) => ({ + name: contrib.login, + image: contrib.avatar_url, + contributions: contrib.contributions, + })); + + return response.status(200).json({ + stars, + contributors, + top_contributors: topContributors, + }); + } catch (e) { + return response.status(500).json({ message: e.message }); + } +} diff --git a/archetypes/tutorials.md b/archetypes/tutorials.md new file mode 100644 index 00000000..ce1dc819 --- /dev/null +++ b/archetypes/tutorials.md @@ -0,0 +1,10 @@ +--- +layout: tutorial +title: "{{ replace .Name "-" " " | title }}" +date: {{ dateFormat "2006-01-02" .Date }} +last-update: {{ dateFormat "2006-01-02" .Date }} +draft: true +category: +level: +description: +--- \ No newline at end of file diff --git a/config.toml b/config.toml index 44090ba1..446eaace 100644 --- a/config.toml +++ b/config.toml @@ -3,6 +3,34 @@ languageCode = 'en-us' title = 'Haystack Home' theme = "haystack" +[permalinks] + tutorials = "/tutorials/:filename/" + +[markup] + # Code blocks + [markup.highlight] + anchorLineNos = false + codeFences = true + guessSyntax = false + hl_Lines = '' + hl_inline = false + lineAnchors = '' + lineNoStart = 1 + lineNos = false + lineNumbersInTable = true + noClasses = true + noHl = false + style = 'solarized-light' + tabWidth = 4 + + # Toc link levels (h2) + [markup.tableOfContents] + endLevel = 2 + startLevel = 2 + + # Allow html in markdown + [markup.goldmark.renderer] + unsafe= true # Main navigation [menu] @@ -72,41 +100,3 @@ theme = "haystack" url = '/use-cases/' parent = 'overview' weight = 7 - -# Community children -[[menu.main]] - name = 'Discord Community' - url = '/discord-community/' - parent = 'community' - weight = 1 - -[[menu.main]] - name = 'Open NLP Meetup' - url = '/open-nlp-meetup/' - parent = 'community' - weight = 2 - -[[menu.main]] - name = 'Open Source Community' - url = '/open-source-community/' - parent = 'community' - weight = 3 - -[[menu.main]] - name = 'deepset on Hugging Face' - url = 'https://huggingface.co/deepset' - parent = 'community' - weight = 4 - -# Tutorials children -[[menu.main]] - name = 'Tutorial 1' - url = '/tutorial-1/' - parent = 'tutorials' - weight = 1 - -[[menu.main]] - name = 'Tutorial 2' - url = '/tutorial-2/' - parent = 'tutorials' - weight = 1 \ No newline at end of file diff --git a/content/_index.md b/content/_index.md index 1b47da9d..a6e1b623 100644 --- a/content/_index.md +++ b/content/_index.md @@ -63,4 +63,20 @@ github: url: https://github.com/deepset-ai/haystack contributors: title: Most active contributors + +# Community +community: + discord: + title: Join our community + text: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam! + icon: /images/icons/discord.svg + buttons: + - buttonText: Join Discord + url: https://discord.com/invite/VBpFzsgRVF + newsletter: + title: Sign up to future newsletters + text: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam! + icon: /images/icons/email.svg + inputPlaceholder: Email address.. + buttonText: Submit --- \ No newline at end of file diff --git a/content/tutorials/1.md b/content/tutorials/1.md new file mode 100644 index 00000000..f5fc8129 --- /dev/null +++ b/content/tutorials/1.md @@ -0,0 +1,300 @@ +--- +layout: tutorial +title: "Build Your First QA System" +date: "2020-09-03" +last-update: "2022-11-09" +category: QA +level: "beginner" +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 1 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial1_Basic_QA_Pipeline.ipynb +--- + + + +Question Answering can be used in a variety of use cases. A very common one: Using it to navigate through complex knowledge bases or long documents ("search setting"). + +A "knowledge base" could for example be your website, an internal wiki or a collection of financial reports. +In this tutorial we will work on a slightly different domain: "Game of Thrones". + +Let's see how we can use a bunch of Wikipedia articles to answer a variety of questions about the +marvellous seven kingdoms. + + +### Prepare environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + +```python +# Make sure you have a GPU running +!nvidia-smi +``` + + +```python +# Install the latest release of Haystack in your own environment +#! pip install farm-haystack + +# Install the latest main of Haystack +!pip install --upgrade pip +!pip install git+https://github.com/deepset-ai/haystack.git#egg=farm-haystack[colab] +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: + + +```python +import logging + +logging.basicConfig(format="%(levelname)s - %(name)s - %(message)s", level=logging.WARNING) +logging.getLogger("haystack").setLevel(logging.INFO) +``` + + +```python +from haystack.utils import clean_wiki_text, convert_files_to_docs, fetch_archive_from_http, print_answers +from haystack.nodes import FARMReader, TransformersReader +``` + +## Document Store + +Haystack finds answers to queries within the documents stored in a `DocumentStore`. The current implementations of `DocumentStore` include `ElasticsearchDocumentStore`, `FAISSDocumentStore`, `SQLDocumentStore`, and `InMemoryDocumentStore`. + +**Here:** We recommended Elasticsearch as it comes preloaded with features like [full-text queries](https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html), [BM25 retrieval](https://www.elastic.co/elasticon/conf/2016/sf/improved-text-scoring-with-bm25), and [vector storage for text embeddings](https://www.elastic.co/guide/en/elasticsearch/reference/7.6/dense-vector.html). + +**Alternatives:** If you are unable to setup an Elasticsearch instance, then follow the [Tutorial 3](https://github.com/deepset-ai/haystack/blob/main/tutorials/Tutorial3_Basic_QA_Pipeline_without_Elasticsearch.ipynb) for using SQL/InMemory document stores. + +**Hint**: This tutorial creates a new document store instance with Wikipedia articles on Game of Thrones. However, you can configure Haystack to work with your existing document stores. + +### Start an Elasticsearch server +You can start Elasticsearch on your local machine instance using Docker. If Docker is not readily available in your environment (e.g. in Colab notebooks), then you can manually download and execute Elasticsearch from source. + + +```python +# Recommended: Start Elasticsearch using Docker via the Haystack utility function +from haystack.utils import launch_es + +launch_es() +``` + + +```python +# In Colab / No Docker environments: Start Elasticsearch from source +! wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.9.2-linux-x86_64.tar.gz -q +! tar -xzf elasticsearch-7.9.2-linux-x86_64.tar.gz +! chown -R daemon:daemon elasticsearch-7.9.2 + +import os +from subprocess import Popen, PIPE, STDOUT + +es_server = Popen( + ["elasticsearch-7.9.2/bin/elasticsearch"], stdout=PIPE, stderr=STDOUT, preexec_fn=lambda: os.setuid(1) # as daemon +) +# wait until ES has started +! sleep 30 +``` + + +```python +# Connect to Elasticsearch + +from haystack.document_stores import ElasticsearchDocumentStore + +document_store = ElasticsearchDocumentStore(host="localhost", username="", password="", index="document") +``` + +## Preprocessing of documents + +Haystack provides a customizable pipeline for: + - converting files into texts + - cleaning texts + - splitting texts + - writing them to a Document Store + +In this tutorial, we download Wikipedia articles about Game of Thrones, apply a basic cleaning function, and index them in Elasticsearch. + + +```python +# Let's first fetch some documents that we want to query +# Here: 517 Wikipedia articles for Game of Thrones +doc_dir = "data/tutorial1" +s3_url = "https://s3.eu-central-1.amazonaws.com/deepset.ai-farm-qa/datasets/documents/wiki_gameofthrones_txt1.zip" +fetch_archive_from_http(url=s3_url, output_dir=doc_dir) + +# Convert files to dicts +# You can optionally supply a cleaning function that is applied to each doc (e.g. to remove footers) +# It must take a str as input, and return a str. +docs = convert_files_to_docs(dir_path=doc_dir, clean_func=clean_wiki_text, split_paragraphs=True) + +# We now have a list of dictionaries that we can write to our document store. +# If your texts come from a different source (e.g. a DB), you can of course skip convert_files_to_dicts() and create the dictionaries yourself. +# The default format here is: +# { +# 'content': "", +# 'meta': {'name': "", ...} +# } +# (Optionally: you can also add more key-value-pairs here, that will be indexed as fields in Elasticsearch and +# can be accessed later for filtering or shown in the responses of the Pipeline) + +# Let's have a look at the first 3 entries: +print(docs[:3]) + +# Now, let's write the dicts containing documents to our DB. +document_store.write_documents(docs) +``` + +## Initialize Retriever, Reader & Pipeline + +### Retriever + +Retrievers help narrowing down the scope for the Reader to smaller units of text where a given question could be answered. +They use some simple but fast algorithm. + +**Here:** We use Elasticsearch's default BM25 algorithm + +**Alternatives:** + +- Customize the `BM25Retriever`with custom queries (e.g. boosting) and filters +- Use `TfidfRetriever` in combination with a SQL or InMemory Document store for simple prototyping and debugging +- Use `EmbeddingRetriever` to find candidate documents based on the similarity of embeddings (e.g. created via Sentence-BERT) +- Use `DensePassageRetriever` to use different embedding models for passage and query (see Tutorial 6) + + +```python +from haystack.nodes import BM25Retriever + +retriever = BM25Retriever(document_store=document_store) +``` + + +```python +# Alternative: An in-memory TfidfRetriever based on Pandas dataframes for building quick-prototypes with SQLite document store. + +# from haystack.nodes import TfidfRetriever +# retriever = TfidfRetriever(document_store=document_store) +``` + +### Reader + +A Reader scans the texts returned by retrievers in detail and extracts the k best answers. They are based +on powerful, but slower deep learning models. + +Haystack currently supports Readers based on the frameworks FARM and Transformers. +With both you can either load a local model or one from Hugging Face's model hub (https://huggingface.co/models). + +**Here:** a medium sized RoBERTa QA model using a Reader based on FARM (https://huggingface.co/deepset/roberta-base-squad2) + +**Alternatives (Reader):** TransformersReader (leveraging the `pipeline` of the Transformers package) + +**Alternatives (Models):** e.g. "distilbert-base-uncased-distilled-squad" (fast) or "deepset/bert-large-uncased-whole-word-masking-squad2" (good accuracy) + +**Hint:** You can adjust the model to return "no answer possible" with the no_ans_boost. Higher values mean the model prefers "no answer possible" + +#### FARMReader + + +```python +# Load a local model or any of the QA models on +# Hugging Face's model hub (https://huggingface.co/models) + +reader = FARMReader(model_name_or_path="deepset/roberta-base-squad2", use_gpu=True) +``` + +#### TransformersReader + + +```python +# Alternative: +# reader = TransformersReader(model_name_or_path="distilbert-base-uncased-distilled-squad", tokenizer="distilbert-base-uncased", use_gpu=-1) +``` + +### Pipeline + +With a Haystack `Pipeline` you can stick together your building blocks to a search pipeline. +Under the hood, `Pipelines` are Directed Acyclic Graphs (DAGs) that you can easily customize for your own use cases. +To speed things up, Haystack also comes with a few predefined Pipelines. One of them is the `ExtractiveQAPipeline` that combines a retriever and a reader to answer our questions. +You can learn more about `Pipelines` in the [docs](https://haystack.deepset.ai/docs/latest/pipelinesmd). + + +```python +from haystack.pipelines import ExtractiveQAPipeline + +pipe = ExtractiveQAPipeline(reader, retriever) +``` + +## Voilà! Ask a question! + + +```python +# You can configure how many candidates the Reader and Retriever shall return +# The higher top_k_retriever, the better (but also the slower) your answers. +prediction = pipe.run( + query="Who is the father of Arya Stark?", params={"Retriever": {"top_k": 10}, "Reader": {"top_k": 5}} +) +``` + + +```python +# prediction = pipe.run(query="Who created the Dothraki vocabulary?", params={"Reader": {"top_k": 5}}) +# prediction = pipe.run(query="Who is the sister of Sansa?", params={"Reader": {"top_k": 5}}) +``` + + +```python +# Now you can either print the object directly... +from pprint import pprint + +pprint(prediction) + +# Sample output: +# { +# 'answers': [ , +# , +# ... +# ] +# 'documents': [ , +# , +# ... +# ], +# 'no_ans_gap': 11.688868522644043, +# 'node_id': 'Reader', +# 'params': {'Reader': {'top_k': 5}, 'Retriever': {'top_k': 5}}, +# 'query': 'Who is the father of Arya Stark?', +# 'root_node': 'Query' +# } +``` + + +```python +# ...or use a util to simplify the output +# Change `minimum` to `medium` or `all` to raise the level of detail +print_answers(prediction, details="minimum") +``` + +## About us + +This [Haystack](https://github.com/deepset-ai/haystack/) notebook was made with love by [deepset](https://deepset.ai/) in Berlin, Germany + +We bring NLP to the industry via open source! +Our focus: Industry specific language models & large scale QA systems. + +Some of our other work: +- [German BERT](https://deepset.ai/german-bert) +- [GermanQuAD and GermanDPR](https://deepset.ai/germanquad) +- [FARM](https://github.com/deepset-ai/FARM) + +Get in touch: +[Twitter](https://twitter.com/deepset_ai) | [LinkedIn](https://www.linkedin.com/company/deepset-ai/) | [Slack](https://haystack.deepset.ai/community/join) | [GitHub Discussions](https://github.com/deepset-ai/haystack/discussions) | [Website](https://deepset.ai) + +By the way: [we're hiring!](https://www.deepset.ai/jobs) + diff --git a/content/tutorials/18.md b/content/tutorials/18.md new file mode 100644 index 00000000..46d6465c --- /dev/null +++ b/content/tutorials/18.md @@ -0,0 +1,297 @@ +--- +layout: tutorial +title: "GPL Domain Adaptation" +date: "2022-06-22" +last-update: "2022-09-09" +category: QA +level: "advanced" +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 1 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial18_GPL.ipynb +--- + +*Note: Adapted to Haystack from Nils Riemers' original [notebook](https://colab.research.google.com/gist/jamescalam/d2c888775c87f9882bb7c379a96adbc8/gpl-domain-adaptation.ipynb#scrollTo=183ff7ab) + +The NLP models we use every day were trained on a corpus of data that reflects the world from the past. In the meantime, we've experienced world-changing events, like the COVID pandemics, and we'd like our models to know about them. Training a model from scratch is tedious work but what if we could just update the models with new data? Generative Pseudo Labeling comes to the rescue. + +The example below shows you how to use GPL to fine-tune a model so that it can answer the query: "How is COVID-19 transmitted?". + +We're using TAS-B: A DistilBERT model that achieves state-of-the-art performance on MS MARCO (500k queries from Bing Search Engine). Both DistilBERT and MS MARCO were created with data from 2018 and before, hence, it lacks the knowledge of any COVID-related information. + +For this example, we're using just four documents. When you ask the model ""How is COVID-19 transmitted?", here are the answers that you get (dot-score and document): +- 94.84 Ebola is transmitted via direct contact with blood +- 92.87 HIV is transmitted via sex or sharing needles +- 92.31 Corona is transmitted via the air +- 91.54 Polio is transmitted via contaminated water or food + + +You can see that the correct document is only third, outranked by Ebola and HIV information. Let's see how we can make this better. + +## Efficient Domain Adaptation with GPL +This notebook demonstrates [Generative Pseudo Labeling (GPL)](https://arxiv.org/abs/2112.07577), an efficient approach to adapt existing dense retrieval models to new domains and data. + +We get a collection of 10k scientific papers on COVID-19 and then fine-tune the model within 15-60 minutes (depending on your GPU) so that it includes the COVID knowledge. + +If we search again with the updated model, we get the search results we would expect: +- Query: How is COVID-19 transmitted +- 97.70 Corona is transmitted via the air +- 96.71 Ebola is transmitted via direct contact with blood +- 95.14 Polio is transmitted via contaminated water or food +- 94.13 HIV is transmitted via sex or sharing needles + +### Prepare the Environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + + +```python +!nvidia-smi +``` + + +```python +!pip install -q datasets +!pip install "faiss-gpu>=1.6.3,<2" +!pip install -q git+https://github.com/deepset-ai/haystack.git +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: + + +```python +import logging + +logging.basicConfig(format="%(levelname)s - %(name)s - %(message)s", level=logging.WARNING) +logging.getLogger("haystack").setLevel(logging.INFO) +``` + + +```python +from sentence_transformers import SentenceTransformer, util +from transformers import AutoTokenizer, AutoModelForSeq2SeqLM +from datasets import load_dataset +``` + + +```python +# We load the TAS-B model, a state-of-the-art model trained on MS MARCO +max_seq_length = 200 +model_name = "msmarco-distilbert-base-tas-b" + +org_model = SentenceTransformer(model_name) +org_model.max_seq_length = max_seq_length +``` + + +```python +# We define a simple query and some documents how diseases are transmitted +# As TAS-B was trained on rather out-dated data (2018 and older), it has now idea about COVID-19 +# So in the below example, it fails to recognize the relationship between COVID-19 and Corona + + +def show_examples(model): + query = "How is COVID-19 transmitted" + docs = [ + "Corona is transmitted via the air", + "Ebola is transmitted via direct contact with blood", + "HIV is transmitted via sex or sharing needles", + "Polio is transmitted via contaminated water or food", + ] + + query_emb = model.encode(query) + docs_emb = model.encode(docs) + scores = util.dot_score(query_emb, docs_emb)[0] + doc_scores = sorted(zip(docs, scores), key=lambda x: x[1], reverse=True) + + print("Query:", query) + for doc, score in doc_scores: + # print(doc, score) + print(f"{score:0.02f}\t{doc}") + + +print("Original Model") +show_examples(org_model) +``` + +## Get Some Data on COVID-19 +We select 10k scientific publications (title + abstract) that are connected to COVID-19. As a dataset, we use [TREC-COVID-19](https://huggingface.co/datasets/nreimers/trec-covid). + + +```python +dataset = load_dataset("nreimers/trec-covid", split="train") +num_documents = 10000 +corpus = [] +for row in dataset: + if len(row["title"]) > 20 and len(row["text"]) > 100: + text = row["title"] + " " + row["text"] + + text_lower = text.lower() + + # The dataset also contains many papers on other diseases. To make the training in this demo + # more efficient, we focus on papers that talk about COVID. + if "covid" in text_lower or "corona" in text_lower or "sars-cov-2" in text_lower: + corpus.append(text) + + if len(corpus) >= num_documents: + break + +print("Len Corpus:", len(corpus)) +``` + +## Initialize Haystack Retriever and DocumentStore + +Let's add corpus documents to `FAISSDocumentStore` and update corpus embeddings via `EmbeddingRetriever` + + +```python +from haystack.nodes.retriever import EmbeddingRetriever +from haystack.document_stores import FAISSDocumentStore + +document_store = FAISSDocumentStore(faiss_index_factory_str="Flat", similarity="cosine") +document_store.write_documents([{"content": t} for t in corpus]) + + +retriever = EmbeddingRetriever( + document_store=document_store, + embedding_model="sentence-transformers/msmarco-distilbert-base-tas-b", + model_format="sentence_transformers", + max_seq_len=max_seq_length, + progress_bar=False, +) +document_store.update_embeddings(retriever) +``` + +## (Optional) Download Pre-Generated Questions or Generate Them Outside of Haystack + +The first step of the GPL algorithm requires us to generate questions for a given text passage. Even though our pre-COVID trained model hasn't seen any COVID-related content, it can still produce sensible queries by copying words from the input text. As generating questions from 10k documents is a bit slow (depending on the GPU used), we'll download question/document pairs directly from the Hugging Face hub. + + + +```python +from tqdm.auto import tqdm + +query_doc_pairs = [] + +load_queries_from_hub = True + +# Generation of the queries is quite slow in Colab due to the old GPU and the limited CPU +# I pre-computed the queries and uploaded these to the HF dataset hub. Here we just download them +if load_queries_from_hub: + generated_queries = load_dataset("nreimers/trec-covid-generated-queries", split="train") + for row in generated_queries: + query_doc_pairs.append({"question": row["query"], "document": row["doc"]}) +else: + # Load doc2query model + t5_name = "doc2query/msmarco-t5-base-v1" + t5_tokenizer = AutoTokenizer.from_pretrained(t5_name) + t5_model = AutoModelForSeq2SeqLM.from_pretrained(t5_name).cuda() + + batch_size = 32 + queries_per_doc = 3 + + for start_idx in tqdm(range(0, len(corpus), batch_size)): + corpus_batch = corpus[start_idx : start_idx + batch_size] + enc_inp = t5_tokenizer( + corpus_batch, max_length=max_seq_length, truncation=True, padding=True, return_tensors="pt" + ) + + outputs = t5_model.generate( + input_ids=enc_inp["input_ids"].cuda(), + attention_mask=enc_inp["attention_mask"].cuda(), + max_length=64, + do_sample=True, + top_p=0.95, + num_return_sequences=queries_per_doc, + ) + + decoded_output = t5_tokenizer.batch_decode(outputs, skip_special_tokens=True) + + for idx, query in enumerate(decoded_output): + corpus_id = int(idx / queries_per_doc) + query_doc_pairs.append({"question": query, "document": corpus_batch[corpus_id]}) + + +print("Generated queries:", len(query_doc_pairs)) +``` + +## Use PseudoLabelGenerator to Genenerate Retriever Adaptation Training Data + +PseudoLabelGenerator run will execute all three steps of the GPL [algorithm](https://github.com/UKPLab/gpl#how-does-gpl-work): + 1. Question generation - optional step + 2. Negative mining + 3. Pseudo labeling (margin scoring) + +The output of the `PseudoLabelGenerator` is the training data we'll use to adapt our `EmbeddingRetriever`. + + + +```python +from haystack.nodes.question_generator import QuestionGenerator +from haystack.nodes.label_generator import PseudoLabelGenerator + +use_question_generator = False + + +if use_question_generator: + questions_producer = QuestionGenerator( + model_name_or_path="doc2query/msmarco-t5-base-v1", + max_length=64, + split_length=128, + batch_size=32, + num_queries_per_doc=3, + ) + +else: + questions_producer = query_doc_pairs + +# We can use either QuestionGenerator or already generated questions in PseudoLabelGenerator +psg = PseudoLabelGenerator(questions_producer, retriever, max_questions_per_document=10, batch_size=32, top_k=10) +output, pipe_id = psg.run(documents=document_store.get_all_documents()) +``` + +## Update the Retriever + +Now that we have the generated training data produced by `PseudoLabelGenerator`, we'll update the `EmbeddingRetriever`. Let's take a peek at the training data. + + +```python +output["gpl_labels"][0] +``` + + +```python +len(output["gpl_labels"]) +``` + + +```python +retriever.train(output["gpl_labels"]) +``` + +## Verify that EmbeddingRetriever Is Adapted and Save It For Future Use + +Let's repeat our query to see if the Retriever learned about COVID and can now rank it as #1 among the answers. + + +```python +print("Original Model") +show_examples(org_model) + +print("\n\nAdapted Model") +show_examples(retriever.embedding_encoder.embedding_model) +``` + + +```python +retriever.save("adapted_retriever") +``` diff --git a/content/tutorials/2.md b/content/tutorials/2.md new file mode 100644 index 00000000..9b8d33f0 --- /dev/null +++ b/content/tutorials/2.md @@ -0,0 +1,171 @@ +--- +layout: tutorial +title: "Fine-tuning a model on your own data" +date: "2020-09-03" +last-update: "2022-09-09" +category: Summarization +level: "intermediate" +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 1 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial2_Finetune_a_model_on_your_data.ipynb +--- + +For many use cases it is sufficient to just use one of the existing public models that were trained on SQuAD or other public QA datasets (e.g. Natural Questions). +However, if you have domain-specific questions, fine-tuning your model on custom examples will very likely boost your performance. +While this varies by domain, we saw that ~ 2000 examples can easily increase performance by +5-20%. + +This tutorial shows you how to fine-tune a pretrained model on your own dataset. + +### Prepare environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + +```python +# Make sure you have a GPU running +!nvidia-smi +``` + + +```python +# Install the latest release of Haystack in your own environment +#! pip install farm-haystack + +# Install the latest main of Haystack +!pip install --upgrade pip +!pip install git+https://github.com/deepset-ai/haystack.git#egg=farm-haystack[colab] +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: + + +```python +import logging + +logging.basicConfig(format="%(levelname)s - %(name)s - %(message)s", level=logging.WARNING) +logging.getLogger("haystack").setLevel(logging.INFO) +``` + + +```python +from haystack.nodes import FARMReader +from haystack.utils import fetch_archive_from_http +``` + + +## Create Training Data + +There are two ways to generate training data + +1. **Annotation**: You can use the [annotation tool](https://haystack.deepset.ai/guides/annotation) to label your data, i.e. highlighting answers to your questions in a document. The tool supports structuring your workflow with organizations, projects, and users. The labels can be exported in SQuAD format that is compatible for training with Haystack. + +![Snapshot of the annotation tool](https://raw.githubusercontent.com/deepset-ai/haystack/main/docs/img/annotation_tool.png) + +2. **Feedback**: For production systems, you can collect training data from direct user feedback via Haystack's [REST API interface](https://github.com/deepset-ai/haystack#rest-api). This includes a customizable user feedback API for providing feedback on the answer returned by the API. The API provides a feedback export endpoint to obtain the feedback data for fine-tuning your model further. + + +## Fine-tune your model + +Once you have collected training data, you can fine-tune your base models. +We initialize a reader as a base model and fine-tune it on our own custom dataset (should be in SQuAD-like format). +We recommend using a base model that was trained on SQuAD or a similar QA dataset before to benefit from Transfer Learning effects. + +**Recommendation**: Run training on a GPU. +If you are using Colab: Enable this in the menu "Runtime" > "Change Runtime type" > Select "GPU" in dropdown. +Then change the `use_gpu` arguments below to `True` + + +```python +reader = FARMReader(model_name_or_path="distilbert-base-uncased-distilled-squad", use_gpu=True) +data_dir = "data/squad20" +# data_dir = "PATH/TO_YOUR/TRAIN_DATA" +reader.train(data_dir=data_dir, train_filename="dev-v2.0.json", use_gpu=True, n_epochs=1, save_dir="my_model") +``` + + +```python +# Saving the model happens automatically at the end of training into the `save_dir` you specified +# However, you could also save a reader manually again via: +reader.save(directory="my_model") +``` + + +```python +# If you want to load it at a later point, just do: +new_reader = FARMReader(model_name_or_path="my_model") +``` + +## Distill your model +In this case, we have used "distilbert-base-uncased" as our base model. This model was trained using a process called distillation. In this process, a bigger model is trained first and is used to train a smaller model which increases its accuracy. This is why "distilbert-base-uncased" can achieve quite competitive performance while being very small. + +Sometimes, however, you can't use an already distilled model and have to distil it yourself. For this case, haystack has implemented [distillation features](https://haystack.deepset.ai/guides/model-distillation). + +### Augmenting your training data +To get the most out of model distillation, we recommend increasing the size of your training data by using data augmentation. You can do this by running the [`augment_squad.py` script](https://github.com/deepset-ai/haystack/blob/main/haystack/utils/augment_squad.py): + + +```python +# Downloading script +!wget https://raw.githubusercontent.com/deepset-ai/haystack/main/haystack/utils/augment_squad.py + +doc_dir = "data/tutorial2" + +# Downloading smaller glove vector file (only for demonstration purposes) +glove_url = "https://nlp.stanford.edu/data/glove.6B.zip" +fetch_archive_from_http(url=glove_url, output_dir=doc_dir) + +# Downloading very small dataset to make tutorial faster (please use a bigger dataset for real use cases) +s3_url = "https://s3.eu-central-1.amazonaws.com/deepset.ai-farm-qa/datasets/documents/squad_small.json.zip" +fetch_archive_from_http(url=s3_url, output_dir=doc_dir) + +# Just replace the path with your dataset and adjust the output (also please remove glove path to use bigger glove vector file) +!python augment_squad.py --squad_path squad_small.json --output_path augmented_dataset.json --multiplication_factor 2 --glove_path glove.6B.300d.txt +``` + +In this case, we use a multiplication factor of 2 to keep this example lightweight. Usually you would use a factor like 20 depending on the size of your training data. Augmenting this small dataset with a multiplication factor of 2, should take about 5 to 10 minutes to run on one V100 GPU. + +### Running distillation +Distillation in haystack is done in two steps: First, you run intermediate layer distillation on the augmented dataset to ensure the two models behave similarly. After that, you run the prediction layer distillation on the non-augmented dataset to optimize the model for your specific task. + +If you want, you can leave out the intermediate layer distillation step and only run the prediction layer distillation. This way you also do not need to perform data augmentation. However, this will make the model significantly less accurate. + + +```python +# Loading a fine-tuned model as teacher e.g. "deepset/​bert-​base-​uncased-​squad2" +teacher = FARMReader(model_name_or_path="my_model", use_gpu=True) + +# You can use any pre-trained language model as teacher that uses the same tokenizer as the teacher model. +# The number of the layers in the teacher model also needs to be a multiple of the number of the layers in the student. +student = FARMReader(model_name_or_path="huawei-noah/TinyBERT_General_6L_768D", use_gpu=True) + +student.distil_intermediate_layers_from(teacher, data_dir=".", train_filename="augmented_dataset.json", use_gpu=True) +student.distil_prediction_layer_from(teacher, data_dir="data/squad20", train_filename="dev-v2.0.json", use_gpu=True) + +student.save(directory="my_distilled_model") +``` + +## About us + +This [Haystack](https://github.com/deepset-ai/haystack/) notebook was made with love by [deepset](https://deepset.ai/) in Berlin, Germany + +We bring NLP to the industry via open source! +Our focus: Industry specific language models & large scale QA systems. + +Some of our other work: +- [German BERT](https://deepset.ai/german-bert) +- [GermanQuAD and GermanDPR](https://deepset.ai/germanquad) +- [FARM](https://github.com/deepset-ai/FARM) + +Get in touch: +[Twitter](https://twitter.com/deepset_ai) | [LinkedIn](https://www.linkedin.com/company/deepset-ai/) | [Slack](https://haystack.deepset.ai/community/join) | [GitHub Discussions](https://github.com/deepset-ai/haystack/discussions) | [Website](https://deepset.ai) + +By the way: [we're hiring!](https://www.deepset.ai/jobs) diff --git a/content/tutorials/4.md b/content/tutorials/4.md new file mode 100644 index 00000000..74f9c67a --- /dev/null +++ b/content/tutorials/4.md @@ -0,0 +1,202 @@ +--- +layout: tutorial +title: "Utilizing existing FAQs for Question Answering" +date: "2020-09-03" +last-update: "2022-10-09" +category: Summarization +level: "beginner" +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 2 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial4_FAQ_style_QA.ipynb +--- + +While *extractive Question Answering* works on pure texts and is therefore more generalizable, there's also a common alternative that utilizes existing FAQ data. + +**Pros**: + +- Very fast at inference time +- Utilize existing FAQ data +- Quite good control over answers + +**Cons**: + +- Generalizability: We can only answer questions that are similar to existing ones in FAQ + +In some use cases, a combination of extractive QA and FAQ-style can also be an interesting option. + +### Prepare environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + +```python +# Make sure you have a GPU running +!nvidia-smi +``` + + +```python +# Install the latest release of Haystack in your own environment +#! pip install farm-haystack + +# Install the latest main of Haystack +!pip install --upgrade pip +!pip install git+https://github.com/deepset-ai/haystack.git#egg=farm-haystack[colab] +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: + + +```python +import logging + +logging.basicConfig(format="%(levelname)s - %(name)s - %(message)s", level=logging.WARNING) +logging.getLogger("haystack").setLevel(logging.INFO) +``` + + +```python +from haystack.document_stores import ElasticsearchDocumentStore + +from haystack.nodes import EmbeddingRetriever +import pandas as pd +``` + +### Start an Elasticsearch server +You can start Elasticsearch on your local machine instance using Docker. If Docker is not readily available in your environment (eg., in Colab notebooks), then you can manually download and execute Elasticsearch from source. + + +```python +# Recommended: Start Elasticsearch using Docker via the Haystack utility function +from haystack.utils import launch_es + +launch_es() +``` + + +```python +# In Colab / No Docker environments: Start Elasticsearch from source +! wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.9.2-linux-x86_64.tar.gz -q +! tar -xzf elasticsearch-7.9.2-linux-x86_64.tar.gz +! chown -R daemon:daemon elasticsearch-7.9.2 + +import os +from subprocess import Popen, PIPE, STDOUT + +es_server = Popen( + ["elasticsearch-7.9.2/bin/elasticsearch"], stdout=PIPE, stderr=STDOUT, preexec_fn=lambda: os.setuid(1) # as daemon +) +# wait until ES has started +! sleep 30 +``` + +### Init the DocumentStore +In contrast to Tutorial 1 (extractive QA), we: + +* specify the name of our `text_field` in Elasticsearch that we want to return as an answer +* specify the name of our `embedding_field` in Elasticsearch where we'll store the embedding of our question and that is used later for calculating our similarity to the incoming user question +* set `excluded_meta_data=["question_emb"]` so that we don't return the huge embedding vectors in our search results + + +```python +from haystack.document_stores import ElasticsearchDocumentStore + +document_store = ElasticsearchDocumentStore( + host="localhost", + username="", + password="", + index="document", + embedding_field="question_emb", + embedding_dim=384, + excluded_meta_data=["question_emb"], + similarity="cosine", +) +``` + +### Create a Retriever using embeddings +Instead of retrieving via Elasticsearch's plain BM25, we want to use vector similarity of the questions (user question vs. FAQ ones). +We can use the `EmbeddingRetriever` for this purpose and specify a model that we use for the embeddings. + + +```python +retriever = EmbeddingRetriever( + document_store=document_store, + embedding_model="sentence-transformers/all-MiniLM-L6-v2", + use_gpu=True, + scale_score=False, +) +``` + +### Prepare & Index FAQ data +We create a pandas dataframe containing some FAQ data (i.e curated pairs of question + answer) and index those in elasticsearch. +Here: We download some question-answer pairs related to COVID-19 + + +```python +from haystack.utils import fetch_archive_from_http + +# Download +doc_dir = "data/tutorial4" +s3_url = "https://s3.eu-central-1.amazonaws.com/deepset.ai-farm-qa/datasets/documents/small_faq_covid.csv.zip" +fetch_archive_from_http(url=s3_url, output_dir=doc_dir) + +# Get dataframe with columns "question", "answer" and some custom metadata +df = pd.read_csv(f"{doc_dir}/small_faq_covid.csv") +# Minimal cleaning +df.fillna(value="", inplace=True) +df["question"] = df["question"].apply(lambda x: x.strip()) +print(df.head()) + +# Get embeddings for our questions from the FAQs +questions = list(df["question"].values) +df["question_emb"] = retriever.embed_queries(texts=questions) +df = df.rename(columns={"question": "content"}) + +# Convert Dataframe to list of dicts and index them in our DocumentStore +docs_to_index = df.to_dict(orient="records") +document_store.write_documents(docs_to_index) +``` + +### Ask questions +Initialize a Pipeline (this time without a reader) and ask questions + + +```python +from haystack.pipelines import FAQPipeline + +pipe = FAQPipeline(retriever=retriever) +``` + + +```python +from haystack.utils import print_answers + +prediction = pipe.run(query="How is the virus spreading?", params={"Retriever": {"top_k": 10}}) +print_answers(prediction, details="medium") +``` + +## About us + +This [Haystack](https://github.com/deepset-ai/haystack/) notebook was made with love by [deepset](https://deepset.ai/) in Berlin, Germany + +We bring NLP to the industry via open source! +Our focus: Industry specific language models & large scale QA systems. + +Some of our other work: +- [German BERT](https://deepset.ai/german-bert) +- [GermanQuAD and GermanDPR](https://deepset.ai/germanquad) +- [FARM](https://github.com/deepset-ai/FARM) + +Get in touch: +[Twitter](https://twitter.com/deepset_ai) | [LinkedIn](https://www.linkedin.com/company/deepset-ai/) | [Slack](https://haystack.deepset.ai/community/join) | [GitHub Discussions](https://github.com/deepset-ai/haystack/discussions) | [Website](https://deepset.ai) + +By the way: [we're hiring!](https://www.deepset.ai/jobs) diff --git a/content/tutorials/_index.md b/content/tutorials/_index.md new file mode 100644 index 00000000..e48bc260 --- /dev/null +++ b/content/tutorials/_index.md @@ -0,0 +1,16 @@ +--- +layout: tutorials +title: Tutorials + +subtitle: We maintain these tutorials in an open source repository in GitHub. **Want to contribute?**
Go to the repository to submit your edits or suggest a new tutorial. +contribute: + text: Contribute + url: / +categoryFilters: + enabled: false + list: + - QA + - Summarization + - Lorem + - Ipsum +--- \ No newline at end of file diff --git a/content/tutorials/test.md b/content/tutorials/test.md new file mode 100644 index 00000000..cc2244e9 --- /dev/null +++ b/content/tutorials/test.md @@ -0,0 +1,57 @@ +--- +layout: tutorial +title: Third Beginner Tutorial +date: 2022-09-18 +last-update: 2022-09-18 +category: QA +level: beginner +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 3 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial4_FAQ_style_QA.ipynb +--- + +While *extractive Question Answering* works on pure texts and is therefore more generalizable, there's also a common alternative that utilizes existing FAQ data. + +**Pros**: + +- Very fast at inference time +- Utilize existing FAQ data +- Quite good control over answers + +**Cons**: + +- Generalizability: We can only answer questions that are similar to existing ones in FAQ + +In some use cases, a combination of extractive QA and FAQ-style can also be an interesting option. + + +### Prepare environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + +```python +# Make sure you have a GPU running +!nvidia-smi +``` + + +```python +# Install the latest release of Haystack in your own environment +#! pip install farm-haystack + +# Install the latest main of Haystack +!pip install --upgrade pip +!pip install git+https://github.com/deepset-ai/haystack.git#egg=farm-haystack[colab] +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: diff --git a/content/tutorials/test2.md b/content/tutorials/test2.md new file mode 100644 index 00000000..2a9972fe --- /dev/null +++ b/content/tutorials/test2.md @@ -0,0 +1,57 @@ +--- +layout: tutorial +title: Second Intermediate Tutorial +date: 2022-09-18 +last-update: 2022-09-18 +category: QA +level: intermediate +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 2 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial4_FAQ_style_QA.ipynb +--- + +While *extractive Question Answering* works on pure texts and is therefore more generalizable, there's also a common alternative that utilizes existing FAQ data. + +**Pros**: + +- Very fast at inference time +- Utilize existing FAQ data +- Quite good control over answers + +**Cons**: + +- Generalizability: We can only answer questions that are similar to existing ones in FAQ + +In some use cases, a combination of extractive QA and FAQ-style can also be an interesting option. + + +### Prepare environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + +```python +# Make sure you have a GPU running +!nvidia-smi +``` + + +```python +# Install the latest release of Haystack in your own environment +#! pip install farm-haystack + +# Install the latest main of Haystack +!pip install --upgrade pip +!pip install git+https://github.com/deepset-ai/haystack.git#egg=farm-haystack[colab] +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: diff --git a/content/tutorials/test3.md b/content/tutorials/test3.md new file mode 100644 index 00000000..96027f45 --- /dev/null +++ b/content/tutorials/test3.md @@ -0,0 +1,57 @@ +--- +layout: tutorial +title: Third Intermediate Tutorial +date: 2022-09-18 +last-update: 2022-09-18 +category: Summarization +level: intermediate +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 3 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial4_FAQ_style_QA.ipynb +--- + +While *extractive Question Answering* works on pure texts and is therefore more generalizable, there's also a common alternative that utilizes existing FAQ data. + +**Pros**: + +- Very fast at inference time +- Utilize existing FAQ data +- Quite good control over answers + +**Cons**: + +- Generalizability: We can only answer questions that are similar to existing ones in FAQ + +In some use cases, a combination of extractive QA and FAQ-style can also be an interesting option. + + +### Prepare environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + +```python +# Make sure you have a GPU running +!nvidia-smi +``` + + +```python +# Install the latest release of Haystack in your own environment +#! pip install farm-haystack + +# Install the latest main of Haystack +!pip install --upgrade pip +!pip install git+https://github.com/deepset-ai/haystack.git#egg=farm-haystack[colab] +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: diff --git a/content/tutorials/test4.md b/content/tutorials/test4.md new file mode 100644 index 00000000..bb3ac422 --- /dev/null +++ b/content/tutorials/test4.md @@ -0,0 +1,57 @@ +--- +layout: tutorial +title: Second Advanced Tutorial +date: 2022-09-18 +last-update: 2022-09-18 +category: Summarization +level: advanced +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 2 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial4_FAQ_style_QA.ipynb +--- + +While *extractive Question Answering* works on pure texts and is therefore more generalizable, there's also a common alternative that utilizes existing FAQ data. + +**Pros**: + +- Very fast at inference time +- Utilize existing FAQ data +- Quite good control over answers + +**Cons**: + +- Generalizability: We can only answer questions that are similar to existing ones in FAQ + +In some use cases, a combination of extractive QA and FAQ-style can also be an interesting option. + + +### Prepare environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + +```python +# Make sure you have a GPU running +!nvidia-smi +``` + + +```python +# Install the latest release of Haystack in your own environment +#! pip install farm-haystack + +# Install the latest main of Haystack +!pip install --upgrade pip +!pip install git+https://github.com/deepset-ai/haystack.git#egg=farm-haystack[colab] +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: diff --git a/content/tutorials/test5.md b/content/tutorials/test5.md new file mode 100644 index 00000000..713cde53 --- /dev/null +++ b/content/tutorials/test5.md @@ -0,0 +1,57 @@ +--- +layout: tutorial +title: Third Advanced Tutorial +date: 2022-09-18 +last-update: 2022-09-18 +category: QA +level: advanced +description: Lorem ipsum dolor sit amet, consectetur adipisicing elit, nisi quisquam et eveniet nesciunt repellendus. +weight: 3 +colab: https://colab.research.google.com/github/deepset-ai/haystack/blob/main/tutorials/Tutorial4_FAQ_style_QA.ipynb +--- + +While *extractive Question Answering* works on pure texts and is therefore more generalizable, there's also a common alternative that utilizes existing FAQ data. + +**Pros**: + +- Very fast at inference time +- Utilize existing FAQ data +- Quite good control over answers + +**Cons**: + +- Generalizability: We can only answer questions that are similar to existing ones in FAQ + +In some use cases, a combination of extractive QA and FAQ-style can also be an interesting option. + + +### Prepare environment + +#### Colab: Enable the GPU runtime +Make sure you enable the GPU runtime to experience decent speed in this tutorial. +**Runtime -> Change Runtime type -> Hardware accelerator -> GPU** + + + + +```python +# Make sure you have a GPU running +!nvidia-smi +``` + + +```python +# Install the latest release of Haystack in your own environment +#! pip install farm-haystack + +# Install the latest main of Haystack +!pip install --upgrade pip +!pip install git+https://github.com/deepset-ai/haystack.git#egg=farm-haystack[colab] +``` + +## Logging + +We configure how logging messages should be displayed and which log level should be used before importing Haystack. +Example log message: +INFO - haystack.utils.preprocessing - Converting data/tutorial1/218_Olenna_Tyrell.txt +Default log level in basicConfig is WARNING so the explicit parameter is not necessary but can be changed easily: diff --git a/content/tutorials/tutorial1.md b/content/tutorials/tutorial1.md deleted file mode 100644 index e69de29b..00000000 diff --git a/package-lock.json b/package-lock.json index a15c293f..d546cbd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,51 +5,1630 @@ "packages": { "": { "devDependencies": { + "autoprefixer": "^10.4.12", + "axios": "^0.27.2", + "parse-link-header": "^2.0.0", + "postcss-cli": "^10.0.0", "prettier": "^2.7.1", "prettier-plugin-go-template": "^0.0.13" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.12", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz", + "integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001407", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001409", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz", + "integrity": "sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.256", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", + "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-link-header": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz", + "integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==", + "dev": true, + "dependencies": { + "xtend": "~4.0.1" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "peer": true, + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-cli": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-10.0.0.tgz", + "integrity": "sha512-Wjy/00wBBEgQqnSToznxLWDnATznokFGXsHtF/3G8glRZpz5KYlfHcBW/VMJmWAeF2x49zjgy4izjM3/Wx1dKA==", + "dev": true, + "dependencies": { + "chokidar": "^3.3.0", + "dependency-graph": "^0.11.0", + "fs-extra": "^10.0.0", + "get-stdin": "^9.0.0", + "globby": "^13.0.0", + "picocolors": "^1.0.0", + "postcss-load-config": "^4.0.0", + "postcss-reporter": "^7.0.0", + "pretty-hrtime": "^1.0.3", + "read-cache": "^1.0.0", + "slash": "^4.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "postcss": "index.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-reporter": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.5.tgz", + "integrity": "sha512-glWg7VZBilooZGOFPhN9msJ3FQs19Hie7l5a/eE6WglzYqVeH3ong3ShFcp9kDWJT1g2Y/wd59cocf9XxBtkWA==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "thenby": "^1.3.4" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "node_modules/prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-go-template": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/prettier-plugin-go-template/-/prettier-plugin-go-template-0.0.13.tgz", + "integrity": "sha512-gG/xT5kd+kCzoMaTchXvdfBdsunyRCV6G8cgdPGPd2V5JGGKXUG7SjzBKU7jaGh2RTeblcAdBb/E+S/duOAMsA==", + "dev": true, + "dependencies": { + "ulid": "^2.3.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "prettier": "^2.0.0" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/thenby": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", + "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ulid": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", + "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "dev": true, + "bin": { + "ulid": "bin/cli.js" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + } + }, + "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" } }, - "node_modules/prettier-plugin-go-template": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/prettier-plugin-go-template/-/prettier-plugin-go-template-0.0.13.tgz", - "integrity": "sha512-gG/xT5kd+kCzoMaTchXvdfBdsunyRCV6G8cgdPGPd2V5JGGKXUG7SjzBKU7jaGh2RTeblcAdBb/E+S/duOAMsA==", + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "dependencies": { - "ulid": "^2.3.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "prettier": "^2.0.0" + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "node_modules/ulid": { + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "autoprefixer": { + "version": "10.4.12", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.12.tgz", + "integrity": "sha512-WrCGV9/b97Pa+jtwf5UGaRjgQIg7OK3D06GnoYoZNcG1Xb8Gt3EfuKjlhh9i/VtT16g6PYjZ69jdJ2g8FxSC4Q==", + "dev": true, + "requires": { + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001407", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "caniuse-lite": { + "version": "1.0.30001409", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001409.tgz", + "integrity": "sha512-V0mnJ5dwarmhYv8/MzhJ//aW68UpvnQBXv8lJ2QUsvn2pHcmAuNtu8hQEDz37XnA1iE+lRR9CIfGWWpgJ5QedQ==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "electron-to-chromium": { + "version": "1.4.256", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz", + "integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "peer": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, + "parse-link-header": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-link-header/-/parse-link-header-2.0.0.tgz", + "integrity": "sha512-xjU87V0VyHZybn2RrCX5TIFGxTVZE6zqqZWMPlIKiSKuWh/X5WZdt+w1Ki1nXB+8L/KtL+nZ4iq+sfI6MrhhMw==", + "dev": true, + "requires": { + "xtend": "~4.0.1" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", - "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", "dev": true, - "bin": { - "ulid": "bin/cli.js" + "peer": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" } - } - }, - "dependencies": { + }, + "postcss-cli": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-10.0.0.tgz", + "integrity": "sha512-Wjy/00wBBEgQqnSToznxLWDnATznokFGXsHtF/3G8glRZpz5KYlfHcBW/VMJmWAeF2x49zjgy4izjM3/Wx1dKA==", + "dev": true, + "requires": { + "chokidar": "^3.3.0", + "dependency-graph": "^0.11.0", + "fs-extra": "^10.0.0", + "get-stdin": "^9.0.0", + "globby": "^13.0.0", + "picocolors": "^1.0.0", + "postcss-load-config": "^4.0.0", + "postcss-reporter": "^7.0.0", + "pretty-hrtime": "^1.0.3", + "read-cache": "^1.0.0", + "slash": "^4.0.0", + "yargs": "^17.0.0" + } + }, + "postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dev": true, + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + } + }, + "postcss-reporter": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.5.tgz", + "integrity": "sha512-glWg7VZBilooZGOFPhN9msJ3FQs19Hie7l5a/eE6WglzYqVeH3ong3ShFcp9kDWJT1g2Y/wd59cocf9XxBtkWA==", + "dev": true, + "requires": { + "picocolors": "^1.0.0", + "thenby": "^1.3.4" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "prettier": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", @@ -65,11 +1644,176 @@ "ulid": "^2.3.0" } }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "requires": { + "pify": "^2.3.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "peer": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "thenby": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", + "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "ulid": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz", "integrity": "sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw==", "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yaml": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", + "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true } } } diff --git a/package.json b/package.json index 655b9f41..4181488d 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,9 @@ { "devDependencies": { + "autoprefixer": "^10.4.12", + "axios": "^0.27.2", + "parse-link-header": "^2.0.0", + "postcss-cli": "^10.0.0", "prettier": "^2.7.1", "prettier-plugin-go-template": "^0.0.13" } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..bb282ecf --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: [require("autoprefixer")], +}; diff --git a/static/images/icons/arrow.svg b/static/images/icons/arrow.svg new file mode 100644 index 00000000..f6f152d1 --- /dev/null +++ b/static/images/icons/arrow.svg @@ -0,0 +1,15 @@ + + + \ No newline at end of file diff --git a/static/images/icons/caret.svg b/static/images/icons/caret.svg new file mode 100644 index 00000000..18315197 --- /dev/null +++ b/static/images/icons/caret.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/images/icons/discord.svg b/static/images/icons/discord.svg new file mode 100644 index 00000000..a5cf6c30 --- /dev/null +++ b/static/images/icons/discord.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/static/images/icons/email.svg b/static/images/icons/email.svg new file mode 100644 index 00000000..7514eb0d --- /dev/null +++ b/static/images/icons/email.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/static/images/icons/tutorials-caret.svg b/static/images/icons/tutorials-caret.svg new file mode 100644 index 00000000..18315197 --- /dev/null +++ b/static/images/icons/tutorials-caret.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/themes/haystack/assets/js/accordions.js b/themes/haystack/assets/js/accordions.js new file mode 100644 index 00000000..d1fe34e9 --- /dev/null +++ b/themes/haystack/assets/js/accordions.js @@ -0,0 +1,121 @@ +export const accordions = () => { + const elements = document.querySelectorAll(".accordion-js"); + + if (elements.length > 0) { + elements.forEach((el) => { + new Accordion(el); + }); + } +}; + +class Accordion { + constructor(el) { + this.el = el; + this.summary = el.querySelector("summary"); + this.content = el.querySelector(".content"); + + this.animation = null; + this.isClosing = false; + this.isExpanding = false; + this.summary.addEventListener("click", (e) => this.onClick(e)); + } + + onClick(e) { + e.preventDefault(); + this.el.style.overflow = "hidden"; + // Check if the element is being closed or is already closed + if (this.isClosing || !this.el.open) { + this.open(); + // Check if the element is being openned or is already open + } else if (this.isExpanding || this.el.open) { + this.shrink(); + } + } + + shrink() { + // Set the element as "being closed" + this.isClosing = true; + + // Store the current height of the element + const startHeight = `${this.el.offsetHeight}px`; + // Calculate the height of the summary + const endHeight = `${this.summary.offsetHeight}px`; + + // If there is already an animation running + if (this.animation) { + // Cancel the current animation + this.animation.cancel(); + } + + // Start a WAAPI animation + this.animation = this.el.animate( + { + // Set the keyframes from the startHeight to endHeight + height: [startHeight, endHeight], + }, + { + duration: 250, + easing: "ease-out", + } + ); + + // When the animation is complete, call onAnimationFinish() + this.animation.onfinish = () => this.onAnimationFinish(false); + // If the animation is cancelled, isClosing variable is set to false + this.animation.oncancel = () => (this.isClosing = false); + } + + open() { + // Apply a fixed height on the element + this.el.style.height = `${this.el.offsetHeight}px`; + // Force the [open] attribute on the details element + this.el.open = true; + // Wait for the next frame to call the expand function + window.requestAnimationFrame(() => this.expand()); + } + + expand() { + // Set the element as "being expanding" + this.isExpanding = true; + // Get the current fixed height of the element + const startHeight = `${this.el.offsetHeight}px`; + // Calculate the open height of the element (summary height + content height) + const endHeight = `${ + this.summary.offsetHeight + this.content.offsetHeight + }px`; + + // If there is already an animation running + if (this.animation) { + // Cancel the current animation + this.animation.cancel(); + } + + // Start a WAAPI animation + this.animation = this.el.animate( + { + // Set the keyframes from the startHeight to endHeight + height: [startHeight, endHeight], + }, + { + duration: 250, + easing: "ease-out", + } + ); + // When the animation is complete, call onAnimationFinish() + this.animation.onfinish = () => this.onAnimationFinish(true); + // If the animation is cancelled, isExpanding variable is set to false + this.animation.oncancel = () => (this.isExpanding = false); + } + + onAnimationFinish(open) { + // Set the open attribute based on the parameter + this.el.open = open; + // Clear the stored animation + this.animation = null; + // Reset isClosing & isExpanding + this.isClosing = false; + this.isExpanding = false; + // Remove the overflow hidden and the fixed height + this.el.style.height = this.el.style.overflow = ""; + } +} diff --git a/themes/haystack/assets/js/code-copy-buttons.js b/themes/haystack/assets/js/code-copy-buttons.js new file mode 100644 index 00000000..e88c4e33 --- /dev/null +++ b/themes/haystack/assets/js/code-copy-buttons.js @@ -0,0 +1,30 @@ +// Adds copy to clipboard buttons to code blocks + +document.addEventListener("DOMContentLoaded", addCopyButtons); + +function addCopyButtons() { + document.querySelectorAll("pre > code").forEach(function (codeBlock) { + const button = document.createElement("button"); + button.className = "btn copy-code-button"; + button.type = "button"; + button.innerText = "Copy"; + + button.addEventListener("click", function () { + navigator.clipboard.writeText(codeBlock.textContent).then( + function () { + button.blur(); + button.innerText = "Copied!"; + setTimeout(function () { + button.innerText = "Copy"; + }, 2000); + }, + function (error) { + button.innerText = "Error"; + console.error(error); + } + ); + }); + + codeBlock.parentNode.insertBefore(button, codeBlock.parentNode.nextSibling); + }); +} diff --git a/themes/haystack/assets/js/github-stats.js b/themes/haystack/assets/js/github-stats.js new file mode 100644 index 00000000..196128a7 --- /dev/null +++ b/themes/haystack/assets/js/github-stats.js @@ -0,0 +1,61 @@ +import { kFormatter } from "./utils"; + +// Fetches github data from api and populates html with stars and contributors +export const githubStats = async () => { + const starContainers = document.querySelectorAll(".github-stars-js"); + const contributorsContainers = document.querySelectorAll( + ".github-contributors-js" + ); + const topContributorsContainer = document.querySelector( + ".top-contributors-container-js" + ); + + if ( + [...starContainers, ...contributorsContainers].length > 0 || + topContributorsContainer + ) { + const response = await fetch("/api/github", { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + if (response.ok) { + const data = await response.json(); + + // Populate stars + if (starContainers.length > 0) { + starContainers.forEach((container) => { + container.innerHTML = kFormatter(data.stars); + }); + } + + // Populate contributors + if (contributorsContainers.length > 0) { + contributorsContainers.forEach((container) => { + container.innerHTML = data.contributors; + }); + } + + // Populate top contributors + if (topContributorsContainer) { + topContributorsContainer.innerHTML = ""; + data.top_contributors.forEach((contributor) => { + const card = document.createElement("li"); + const image = document.createElement("img"); + const userName = document.createElement("span"); + const contributions = document.createElement("span"); + + image.src = contributor.image; + image.alt = contributor.name; + userName.innerHTML = contributor.name; + contributions.innerHTML = `${contributor.contributions} contributions`; + + card.append(image, userName, contributions); + topContributorsContainer.append(card); + }); + } + } + } +}; diff --git a/themes/haystack/assets/js/github.js b/themes/haystack/assets/js/github.js deleted file mode 100644 index 97a8e2a1..00000000 --- a/themes/haystack/assets/js/github.js +++ /dev/null @@ -1,55 +0,0 @@ -import { kFormatter } from "./utils"; - -// Fetch github stars -export const fetchGithubStars = async (githubStarContainers) => { - const response = await fetch( - "https://api.github.com/repos/deepset-ai/haystack", - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - } - ); - - if (response.ok) { - const data = await response.json(); - - // Populate elements with stars - githubStarContainers.forEach((container) => { - container.innerHTML = kFormatter(data.stargazers_count); - }); - } -}; - -// Fetch top contributors -export const fetchTopContributors = async (topContributorsContainer) => { - const response = await fetch( - "https://api.github.com/repos/deepset-ai/haystack/contributors?per_page=10", - { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - } - ); - - if (response.ok) { - // Create elements on page - const data = await response.json(); - data.forEach((contributor) => { - const card = document.createElement("li"); - const image = document.createElement("img"); - const userName = document.createElement("span"); - const contributions = document.createElement("span"); - - image.src = contributor.avatar_url; - image.alt = contributor.login; - userName.innerHTML = contributor.login; - contributions.innerHTML = `${contributor.contributions} contributions`; - - card.append(image, userName, contributions); - topContributorsContainer.append(card); - }); - } -}; diff --git a/themes/haystack/assets/js/navigation.js b/themes/haystack/assets/js/navigation.js new file mode 100644 index 00000000..2601919b --- /dev/null +++ b/themes/haystack/assets/js/navigation.js @@ -0,0 +1,14 @@ +// Open & close mobile nav +export const navigation = () => { + const navOpenButton = document.querySelector("button.nav-toggle-open"); + const navCloseButton = document.querySelector("button.nav-toggle-close"); + const nav = document.querySelector("nav.mobile-nav"); + + navOpenButton.addEventListener("click", () => { + nav.classList.add("open"); + }); + + navCloseButton.addEventListener("click", (e) => { + nav.classList.remove("open"); + }); +}; diff --git a/themes/haystack/assets/js/newsletters.js b/themes/haystack/assets/js/newsletters.js new file mode 100644 index 00000000..32346f4a --- /dev/null +++ b/themes/haystack/assets/js/newsletters.js @@ -0,0 +1,52 @@ +// Handles newsletter submission +export const newsletters = () => { + const forms = document.querySelectorAll(".js-newsletter-form"); + + if (forms.length > 0) { + // Regex string to validate email + const re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,4})+$/; + + forms.forEach((form) => { + // Listen to input, validate email, disable/enable submit btn + form.addEventListener("input", (e) => { + if (re.test(e.target.value)) { + form.classList.remove("disabled"); + form[1].disabled = false; + } else { + form.classList.add("disabled"); + form[1].disabled = true; + } + }); + + // Submit + form.addEventListener("submit", async (e) => { + e.preventDefault(); + const res = await fetch( + "https://api.hsforms.com/submissions/v3/integration/submit/4561480/103aed2c-3c5d-4889-b029-46d289857db3", + { + body: JSON.stringify({ + fields: [{ name: "email", value: e.target.email.value }], + }), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + } + ); + + const result = await res.json(); + + if (result.status === "error") { + e.target.reset(); + e.target.email.placeholder = "Error processing request."; + form.classList.add("disabled"); + } else { + form.classList.add("success"); + setTimeout(() => { + form.classList.add("visible"); + }, 250); + } + }); + }); + } +}; diff --git a/themes/haystack/assets/js/script.js b/themes/haystack/assets/js/script.js index e0bdff48..6e0c3db4 100644 --- a/themes/haystack/assets/js/script.js +++ b/themes/haystack/assets/js/script.js @@ -1,16 +1,18 @@ -import { fetchGithubStars, fetchTopContributors } from "./github"; +// Main js file loaded in the footer partial +import { navigation } from "./navigation"; +import { accordions } from "./accordions"; +import { newsletters } from "./newsletters"; +import { githubStats } from "./github-stats"; -// Github stars -// Fetch stars from the Github API and populate containers -const githubStarContainers = document.querySelectorAll(".github-stars-js"); -if (githubStarContainers.length > 0) { - fetchGithubStars(githubStarContainers); -} +const ready = (fn) => { + if (document.readyState != "loading") { + fn(); + } else { + document.addEventListener("DOMContentLoaded", fn); + } +}; -// Top contributors -const topContributorsContainer = document.querySelector( - ".top-contributors-container-js" -); -if (topContributorsContainer) { - fetchTopContributors(topContributorsContainer); -} +ready(navigation); +ready(accordions); +ready(newsletters); +ready(githubStats); diff --git a/themes/haystack/assets/js/table-of-contents.js b/themes/haystack/assets/js/table-of-contents.js new file mode 100644 index 00000000..dc8fcbb5 --- /dev/null +++ b/themes/haystack/assets/js/table-of-contents.js @@ -0,0 +1,21 @@ +// Add active class to table of contents links on scroll +window.addEventListener("DOMContentLoaded", () => { + const tocLinks = document.querySelectorAll(`#TableOfContents > ul > li > a`); + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + const id = entry.target.getAttribute("id"); + if (entry.intersectionRatio > 0) { + tocLinks.forEach((i) => i.classList.remove("active")); + document + .querySelector(`#TableOfContents > ul > li > a[href*="${id}"]`) + .classList.add("active"); + } + }); + }); + + // Track every h2 that has an id + document.querySelectorAll("h2[id]").forEach((h) => { + observer.observe(h); + }); +}); diff --git a/themes/haystack/assets/js/tutorial-filters.js b/themes/haystack/assets/js/tutorial-filters.js new file mode 100644 index 00000000..7ce7f47e --- /dev/null +++ b/themes/haystack/assets/js/tutorial-filters.js @@ -0,0 +1,71 @@ +// Handles filtering of tutorials by category on /tutorials page +document.addEventListener("DOMContentLoaded", () => { + const filterButtons = document.querySelectorAll(".filter-btn"); + const cards = document.querySelectorAll(".tutorial-card"); + + filterButtons.forEach((filterBtn) => { + // Get category from btn text + const category = filterBtn.textContent.toLowerCase().trim(); + + // Listen for clicks + filterBtn.addEventListener("click", () => { + // Check if any filter is disabled + const filterDisabled = Array.from(filterButtons).find((btn) => + btn.classList.contains("disabled") + ); + + // If all filters are enabled, disable all except clicked + if (!filterDisabled) { + filterButtons.forEach((btn) => { + if (!btn.classList.contains(`category-${category}`)) { + btn.classList.remove("enabled"); + btn.classList.add("disabled"); + } + }); + + cards.forEach((card) => { + if (!card.classList.contains(`category-${category}`)) { + card.style.display = "none"; + } + }); + + // if the last filter gets disabled, re-enable all + } else if ( + Array.from(filterButtons).filter((btn) => + btn.classList.contains("enabled") + ).length === 1 && + filterBtn.classList.contains("enabled") + ) { + filterButtons.forEach((btn) => { + btn.classList.remove("disabled"); + btn.classList.add("enabled"); + }); + + cards.forEach((card) => { + card.style.display = "flex"; + }); + } else if (filterBtn.classList.contains("enabled")) { + // Else if category is enabled, disable it and hide related tutorials + cards.forEach((card) => { + if (card.classList.contains(`category-${category}`)) { + card.style.display = "none"; + } + }); + + filterBtn.classList.remove("enabled"); + filterBtn.classList.add("disabled"); + + // If category is disabled, enable it and show related tutorials + } else if (filterBtn.classList.contains("disabled")) { + cards.forEach((card) => { + if (card.classList.contains(`category-${category}`)) { + card.style.display = "flex"; + } + }); + + filterBtn.classList.remove("disabled"); + filterBtn.classList.add("enabled"); + } + }); + }); +}); diff --git a/themes/haystack/assets/sass/abstracts/_variables.scss b/themes/haystack/assets/sass/abstracts/_variables.scss index d4ea7395..1214b301 100644 --- a/themes/haystack/assets/sass/abstracts/_variables.scss +++ b/themes/haystack/assets/sass/abstracts/_variables.scss @@ -15,14 +15,27 @@ --color-green: #03af9d; --color-yellow: #ffc55c; + // RBG colors + --color-white-rgb: 254, 254, 253; + --color-light-grey-rgb: 216, 216, 229; + --color-medium-grey-rgb: 160, 160, 192; + --color-dark-grey-rgb: 144, 144, 178; + --color-blue-rgb: 24, 139, 245; + --color-dark-blue-rgb: 43, 47, 85; + --color-green-rgb: 3, 175, 157; + --color-yellow-rgb: 255, 197, 92; + + // Background colors --color-bg-white: #fefefd; - --color-bg-light-grey: #f4f6f7; + --color-bg-light-grey: #f3f3f7; --color-bg-grey: #f3f3f7; + --color-bg-blue: #48a7ff; --color-bg-dark-blue: #2b2f55; - - --color-nav-link-active: #f5a106; + --color-bg-yellow: #ffd78f; + --color-bg-red: #ed6a5e; // Font Sizes + --text-label: 0.8125rem; --text-base: 1rem; --text-body: 1.1875rem; --text-small: 1rem; @@ -32,11 +45,13 @@ --h1-size: 2.25rem; --h2-size: 1.75rem; --h3-size: 1.375rem; + --h4-size: 1.2rem; @include md { --h1-size: 2.625rem; --h2-size: 1.9375rem; --h3-size: 1.4375rem; + --h4-size: 1.275rem; } @include lg { @@ -47,6 +62,7 @@ --h1-size: 3.125rem; --h2-size: 2.1875rem; --h3-size: 1.5625rem; + --h4-size: 1.325rem; } // Font weight @@ -59,19 +75,28 @@ --line-height-heading: 1.2; // Text spacing - --big-text-spacing: 1.25rem; - --small-text-spacing: 0.75rem; + --sm-text-spacing: 1rem; + --md-text-spacing: 1.5rem; + --lg-text-spacing: 2rem; + + @include md { + --sm-text-spacing: 1.5rem; + --md-text-spacing: 2.5rem; + --lg-text-spacing: 3rem; + } // Site container --container-max-width: 90rem; // 1440px // Paddings --container-padding: 2rem; // Container right & left + --container-padding-lg: 2rem; --header-padding: 1.5rem; // Header top/bottom --inner-padding: 3rem; // Inner section top/bottom --footer-padding: 2.5rem; @include md { + --container-padding-lg: 5rem; --header-padding: 2rem; --inner-padding: 5rem; --footer-padding: 4rem; @@ -79,10 +104,15 @@ @include lg { --container-padding: 3rem; + --container-padding-lg: 8rem; --inner-padding: 7.5rem; --footer-padding: 5rem; } + // Borders + --border-radius-sm: 0.125rem; + --border-radius-md: 0.3125rem; + // Transitions --transition-fast: 0.25s; --ease: cubic-bezier(0.25, 0, 0, 1); @@ -94,4 +124,8 @@ 0 1.12694px 10.0172px rgba(0, 0, 0, 0.03), 0 0.598509px 5.32008px rgba(0, 0, 0, 0.0242336), 0 0.249053px 2.21381px rgba(0, 0, 0, 0.0168687); + + --shadow-filter: drop-shadow(0 5px 15px rgba(0, 0, 0, 0.05)) + drop-shadow(0 -2px 5px rgba(0, 0, 0, 0.05)) + drop-shadow(0 0 2px rgba(0, 0, 0, 0.05)); } diff --git a/themes/haystack/assets/sass/base/_base.scss b/themes/haystack/assets/sass/base/_base.scss index 7f98090c..4b06e975 100644 --- a/themes/haystack/assets/sass/base/_base.scss +++ b/themes/haystack/assets/sass/base/_base.scss @@ -1,3 +1,7 @@ +html { + scroll-behavior: smooth; +} + body { background: var(--color-white); } @@ -10,7 +14,17 @@ body { margin: 0 auto; } +// Extra padded container +.container.container-padded { + padding: 0 var(--container-padding-lg); +} + // Inner content sections .inner { padding: var(--inner-padding) 0; } + +// First inner under header +.inner.inner-top { + padding-top: 3rem; +} diff --git a/themes/haystack/assets/sass/base/_reset.scss b/themes/haystack/assets/sass/base/_reset.scss index 110b62e5..73a7d116 100644 --- a/themes/haystack/assets/sass/base/_reset.scss +++ b/themes/haystack/assets/sass/base/_reset.scss @@ -73,3 +73,16 @@ select { scroll-behavior: auto !important; } } + +input, +textarea, +button, +select, +a { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +// Remove markers on summary +details summary::-webkit-details-marker { + display: none; +} diff --git a/themes/haystack/assets/sass/base/_typography.scss b/themes/haystack/assets/sass/base/_typography.scss index abdcccd4..f35b2376 100644 --- a/themes/haystack/assets/sass/base/_typography.scss +++ b/themes/haystack/assets/sass/base/_typography.scss @@ -18,6 +18,10 @@ h3 { font-size: var(--h3-size); } +h4 { + font-size: var(--h4-size); +} + h1, h2, h3, @@ -51,14 +55,33 @@ a { /** * Article typography */ -.article { - h2, - h3, - h4 { - margin-top: var(--big-text-spacing); +.article article, +.tutorial article { + h2 { + margin-top: var(--lg-text-spacing); + } + h3 { + margin-top: var(--md-text-spacing); + } + + h4, + p, + img, + pre { + margin-top: var(--sm-text-spacing); + } + + a { + text-decoration: none; + color: var(--color-blue); + font-weight: var(--font-weight-links); + } + + ::marker { + color: var(--color-blue); } - p { - margin-top: var(--small-text-spacing); + li { + margin-top: 0.5rem; } } diff --git a/themes/haystack/assets/sass/components/_article.scss b/themes/haystack/assets/sass/components/_article.scss index e69de29b..a0ce9843 100644 --- a/themes/haystack/assets/sass/components/_article.scss +++ b/themes/haystack/assets/sass/components/_article.scss @@ -0,0 +1,251 @@ +/** + * Tutorial article + */ + +.tutorial { + display: grid; + grid-template-columns: 1fr; + + @include md { + grid-template-columns: 15rem 1fr; + gap: 2rem; + } + + @include lg { + grid-template-columns: 15rem 1fr 12rem; + gap: 2rem; + } +} + +.article-content { + overflow: hidden; +} + +// Left sidebar / TOC +.tutorials-sidebar { + display: none; + + @include md { + display: block; + width: 100%; + border-right: 2px solid var(--color-light-grey); + padding: 1rem 2rem 1rem 0; + position: sticky; + top: 0; + overflow-y: auto; + max-height: 100vh; + flex-shrink: 0; + + &::-webkit-scrollbar { + width: 0.5rem; + } + + &::-webkit-scrollbar-track { + background: var(--color-bg-light-grey); + } + + &::-webkit-scrollbar-thumb { + background: var(--color-light-grey); + } + + .accordions { + display: flex; + flex-direction: column; + gap: 1.5rem; + + .accordion-title { + display: flex; + gap: 0.25rem; + position: relative; + opacity: 1; + border: none; + outline: none; + font-size: var(--text-body); + font-weight: var(--font-weight-heading); + color: var(--color-medium-grey); + user-select: none; + cursor: pointer; + transition: opacity var(--transition-fast) var(--ease); + + .accordion-title-child { + display: flex; + gap: 0.25rem; + + &::after { + content: url("/images/icons/caret.svg"); + padding-top: 0.125rem; + width: 1rem; + transform: rotate(-90deg); + } + } + } + + details[open] .accordion-title-child::after { + transform: rotate(0deg); + } + + summary { + display: flex !important; + } + + .content { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-top: 0.5rem; + } + + a { + text-decoration: none; + color: var(--color-medium-grey); + font-weight: var(--font-weight-links); + } + + a.active { + color: var(--color-dark-blue); + } + + // Inner accordian + .accordion-child { + .accordion-title { + color: var(--color-dark-blue); + font-weight: var(--font-weight-links); + + &::after { + display: none; + } + } + + .content { + border-left: 2px solid var(--color-light-grey); + } + } + } + } + + // Page TOC + #TableOfContents { + ul { + list-style: none; + padding: 0 0 0.5rem 0.75rem; + margin: 0; + display: flex; + flex-direction: column; + gap: 0.5rem; + opacity: 1; + + li { + line-height: 1.15; + } + + a { + font-size: var(--text-base); + line-height: 1.15; + + &:hover { + opacity: 0.9; + } + } + } + } +} + +// Right sidebar +.right-sidebar { + display: flex; + flex-direction: column; + gap: 1rem; + margin-top: 2rem; + + .sidebar-buttons { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + + .btn { + width: 100%; + background-color: var(--color-white); + color: var(--color-medium-grey); + font-size: var(--text-base); + border: 2px solid var(--color-medium-grey); + text-align: center; + transition: all var(--transition-fast) var(--ease); + + &:hover { + border: 2px solid var(--color-yellow); + color: var(--color-blue); + filter: var(--shadow-filter); + } + } + } + + @include md { + grid-column: 2; + + .sidebar-buttons { + flex-wrap: nowrap; + } + } + + @include lg { + grid-column: 3; + width: 100%; + position: sticky; + top: 0; + align-self: flex-start; + flex-shrink: 0; + // border-left: 2px solid var(--color-light-grey); + padding: 1rem 0 0 0; + margin-top: 0; + + .sidebar-buttons { + flex-direction: column; + } + } +} + +// Next / Prev links +.article-pagination { + margin-top: var(--md-text-spacing); + display: flex; + width: 100%; + flex-wrap: wrap; + justify-content: space-between; + gap: 0.5rem; + + .next-article { + margin-left: auto; + } + + @include md { + flex-wrap: nowrap; + } + + > div > a { + gap: 0.5rem; + width: 100%; + background-color: var(--color-white); + color: var(--color-medium-grey); + border: 2px solid var(--color-medium-grey); + text-align: center; + transition-property: border-color, color, filter, -webkit-filter, fill; + transition-duration: var(--transition-fast); + transition-timing-function: var(--ease); + + > svg { + fill: var(--color-medium-grey); + transition: fill var(--transition-fast) var(--ease); + flex-shrink: 0; + } + + &:hover { + border: 2px solid var(--color-yellow); + color: var(--color-blue); + filter: var(--shadow-filter); + + > svg { + fill: var(--color-blue); + } + } + } +} diff --git a/themes/haystack/assets/sass/components/_buttons.scss b/themes/haystack/assets/sass/components/_buttons.scss index c1e70c3d..0ddef5a8 100644 --- a/themes/haystack/assets/sass/components/_buttons.scss +++ b/themes/haystack/assets/sass/components/_buttons.scss @@ -40,43 +40,228 @@ button.nav-toggle-close { } } -// Arrow button -.arrow-button { - display: inline-block; - background-color: var(--color-green); - color: var(--color-white); +// Base button +.btn { + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + width: fit-content; + max-width: 100%; text-decoration: none; font-weight: var(--font-weight-heading); - padding: 0.375rem 0.8125rem 0.3125rem; - border-radius: 2px; - overflow: hidden; + padding: 0.375rem 0.8125rem 0.3125rem 0.8125rem; + border: none; + border-radius: var(--border-radius-sm); + text-decoration: none; + cursor: pointer; + user-select: none; + transition: background-color var(--transition-fast) var(--ease); + + &.btn-green { + background-color: rgba($color: #{var(--color-green-rgb)}, $alpha: 1); + color: var(--color-white); + + &:hover { + background-color: rgba($color: #{var(--color-green-rgb)}, $alpha: 0.9); + } + } + &.btn-blue { + background-color: rgba($color: #{var(--color-blue-rgb)}, $alpha: 1); + color: var(--color-white); + + &:hover { + background-color: rgba($color: #{var(--color-blue-rgb)}, $alpha: 0.9); + } + } + + &.btn-dark-blue { + background-color: rgba($color: #{var(--color-dark-blue-rgb)}, $alpha: 1); + color: var(--color-white); + + &:hover { + background-color: rgba( + $color: #{var(--color-dark-blue-rgb)}, + $alpha: 0.9 + ); + } + } +} + +// Arrow buttons +.arrow-button, +.arrow-link { .button-wrapper { display: flex; + width: fit-content; + max-width: calc(100% + 1.75rem); align-items: center; - gap: 0.75rem; - margin-left: -1.7rem; + margin-left: -1.625rem; + transform: translate3d(0, 0, 0); + transition: transform var(--transition-fast) var(--ease); + white-space: nowrap; - svg { - width: 0.8125rem; + @include md { + margin-left: -1.3125rem; + } + } + + .button-arrow { + width: 0.8125rem; + flex-shrink: 0; + color: var(--color-white); + transition: opacity var(--transition-fast) var(--ease); + + &.first { + opacity: 0; + } + + &.second { + opacity: 1; + } + } + + .text-wrapper { + overflow: hidden; + flex-grow: 2; + margin: 0 0.75rem; + font-weight: var(--font-weight-heading); + text-overflow: ellipsis; + white-space: nowrap; + + @include md { + margin: 0 0.5rem; + } + } + + &:hover, + &:focus-within { + .button-wrapper { + transform: translate3d(1.625rem, 0, 0); + + @include md { + transform: translate3d(1.3125rem, 0, 0); + } + } + + .button-arrow.first { + opacity: 1; + } + + .button-arrow.second { + opacity: 0; } + } +} - svg, - .text-wrapper { - transition: transform var(--transition-fast) var(--ease); +// Arrow links used in nav +.arrow-link { + overflow: visible; + background-color: transparent; + color: var(--color-dark-blue); + padding: 0; + + .button-wrapper { + margin-right: 1.7rem; + svg { + opacity: 0; } } + .text-wrapper { + font-weight: var(--font-weight-links); + } + &:hover { + background-color: transparent; .button-wrapper { - svg, - .text-wrapper { - transform: translate3d(1.7rem, 0px, 0px); + svg { + opacity: 1; } } } } +// Copy code button +.copy-code-button { + color: var(--color-medium-grey); + background-color: var(--color-white); + border: 2px solid var(--color-light-grey); + border-radius: var(--border-radius-sm); + display: block; + position: absolute; + top: 0.5rem; + right: 0.5rem; + padding: 3px 8px; + font-size: var(--text-small); + font-family: var(--font-sans-serif); + font-weight: var(--font-weight-links); + opacity: 0; + transition-property: opacity, border-color, color; + transition-duration: var(--transition-fast); + transition-timing-function: var(--ease); +} + +.highlight:hover .copy-code-button { + opacity: 0.7; +} + +.copy-code-button:hover, +.copy-code-button:focus { + border: 2px solid var(--color-yellow); + color: var(--color-dark-blue); + + opacity: 1; +} + +// Github stars button +.github-stars-btn { + display: flex; + align-self: flex-start; + align-items: center; + gap: 0.5rem; + text-decoration: none; + color: var(--color-dark-grey); + font-weight: var(--font-weight-links); + + svg { + width: 1.25rem; + flex-shrink: 0; + fill: var(--color-dark-grey); + transition: fill var(--transition-fast) var(--ease); + padding-bottom: 0.2rem; + } + + &:hover { + color: var(--color-dark-blue); + + svg { + fill: var(--color-dark-blue); + } + } + + .loader { + margin: 0; + width: 1rem; + + svg { + fill: var(--color-medium-grey); + width: 1.25rem; + } + } +} + +// Colab button +.colab-btn { + gap: 0.5rem; + margin-top: var(--sm-text-spacing); + font-weight: var(--font-weight-links); + > svg { + width: 2rem; + } +} + // button container .button-container { display: flex; diff --git a/themes/haystack/assets/sass/components/_code.scss b/themes/haystack/assets/sass/components/_code.scss new file mode 100644 index 00000000..cd5da491 --- /dev/null +++ b/themes/haystack/assets/sass/components/_code.scss @@ -0,0 +1,20 @@ +/** + * Codeblocks + */ + +.highlight { + position: relative; + max-width: 100%; +} +code { + font-family: var(--font-monospaced); + word-spacing: normal; + word-break: normal; + word-wrap: normal; +} + +pre { + overflow: auto; + padding: 1rem; + background-color: var(--color-bg-light-grey) !important; +} diff --git a/themes/haystack/assets/sass/components/_footer.scss b/themes/haystack/assets/sass/components/_footer.scss index e128d4b8..6999cbb9 100644 --- a/themes/haystack/assets/sass/components/_footer.scss +++ b/themes/haystack/assets/sass/components/_footer.scss @@ -48,10 +48,19 @@ // Secondary links .footer-secondary-links { // Logo - a.footer-deepset-logo { - svg { - height: 2rem; + a.footer-deepset-logo svg { + height: 2rem; + path, + circle { fill: var(--color-dark-grey); + transition: fill var(--transition-fast) var(--ease); + } + + &:hover { + path, + circle { + fill: var(--color-dark-blue); + } } } @@ -72,7 +81,9 @@ svg { height: 1.45rem; fill: var(--color-medium-grey); - transition: all var(--transition-fast) var(--ease); + transition-property: fill, opacity; + transition-duration: var(--transition-fast); + transition-timing-function: var(--ease); opacity: 0.5; } @@ -102,3 +113,31 @@ margin-top: 3rem; } } + +.footer-alt { + background-color: var(--color-bg-dark-blue); + + a.footer-deepset-logo svg { + height: 2rem; + path, + circle { + fill: var(--color-light-grey); + transition: fill var(--transition-fast) var(--ease); + } + + &:hover { + path, + circle { + fill: var(--color-medium-grey); + } + } + } + + a { + color: var(--color-light-grey); + + &:hover { + color: var(--color-medium-grey); + } + } +} diff --git a/themes/haystack/assets/sass/components/_forms.scss b/themes/haystack/assets/sass/components/_forms.scss new file mode 100644 index 00000000..0a8cd74d --- /dev/null +++ b/themes/haystack/assets/sass/components/_forms.scss @@ -0,0 +1,91 @@ +// Newsletter form +.js-newsletter-form { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.5rem; + + > input, + > button { + font-size: var(--text-body); + line-height: 1; + } + + > input { + appearance: none; + background: var(--color-white); + color: var(--color-dark-blue); + border: 2px solid var(--color-light-grey); + border-radius: var(--border-radius-sm); + height: 2.45rem; + min-width: 6rem; + padding: 0 0.5rem; + flex-grow: 1; + transition: border-color var(--transition-fast) var(--ease); + + &::placeholder { + color: var(--color-medium-grey); + } + + &:focus-within, + &:hover { + outline: none; + border-color: var(--color-yellow); + } + } + + > button { + border: none; + height: 2.45rem; + flex-shrink: 0; + transition: opacity var(--transition-fast) var(--ease); + opacity: 1; + } + + .success-message { + display: none; + opacity: 0; + transition: opacity var(--transition-fast) var(--ease); + font-weight: var(--font-weight-heading); + font-size: var(--text-body); + color: var(--color-dark-blue); + } +} + +.js-newsletter-form.disabled { + > button { + opacity: 0.5; + cursor: not-allowed; + + .button-wrapper { + transform: unset; + + .button-arrow { + &.first { + opacity: 0; + } + + &.second { + opacity: 1; + } + } + } + } +} + +.js-newsletter-form.success { + > input, + > button { + display: none; + } + + .success-message { + display: block; + } +} + +.js-newsletter-form.visible { + .success-message { + opacity: 1; + } +} diff --git a/themes/haystack/assets/sass/components/_header.scss b/themes/haystack/assets/sass/components/_header.scss index b7fa9772..0338b380 100644 --- a/themes/haystack/assets/sass/components/_header.scss +++ b/themes/haystack/assets/sass/components/_header.scss @@ -9,14 +9,11 @@ .site-title > svg { display: block; - width: 100%; - height: 2rem; + height: 2.5rem; @include md { - height: 3rem; - + height: 3rem; } } - } } diff --git a/themes/haystack/assets/sass/components/_loader.scss b/themes/haystack/assets/sass/components/_loader.scss new file mode 100644 index 00000000..4a968e98 --- /dev/null +++ b/themes/haystack/assets/sass/components/_loader.scss @@ -0,0 +1,21 @@ +.loader { + display: inline-block; + animation: loader 1s linear infinite; + display: flex; + justify-content: center; + width: 3rem; + margin-bottom: 1rem; + + svg { + fill: var(--color-light-grey); + } +} + +@keyframes loader { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/themes/haystack/assets/sass/components/_navigation.scss b/themes/haystack/assets/sass/components/_navigation.scss index 63ed0ec8..04e420d1 100644 --- a/themes/haystack/assets/sass/components/_navigation.scss +++ b/themes/haystack/assets/sass/components/_navigation.scss @@ -15,7 +15,7 @@ // Current page link li.active > a { - color: var(--color-nav-link-active); + color: var(--color-yellow); } ul { @@ -32,10 +32,11 @@ top: 1rem; right: 1rem; padding: 2rem; - border-radius: 2px; + border-radius: var(--border-radius-sm); background-color: var(--color-bg-white); box-shadow: var(--box-shadow); opacity: 0; + visibility: hidden; pointer-events: none; transition: opacity var(--transition-fast) var(--ease); z-index: 10; @@ -58,13 +59,17 @@ } .menu-label { - font-weight: var(--font-weight-links); + font-weight: var(--font-weight-heading); color: var(--color-dark-grey); + font-size: var(--text-label); + text-transform: uppercase; + letter-spacing: 0.06rem; } } .mobile-nav.open { - opacity: 100; + opacity: 1; + visibility: visible; pointer-events: auto; } @@ -115,7 +120,7 @@ gap: 0.5rem; margin-top: 0.5rem; padding: 2rem; - border-radius: 2px; + border-radius: var(--border-radius-sm); width: auto; white-space: nowrap; background-color: var(--color-bg-white); @@ -124,24 +129,39 @@ transition: opacity var(--transition-fast) var(--ease); pointer-events: none; z-index: 10; + visibility: hidden; + + > li:not(.active) a { + color: var(--color-dark-blue); + } + + li.active { + .button-wrapper { + transform: none !important; + } + svg { + visibility: hidden; + } + } } // Show dropdown menu on hover &:hover { ul.sub-menu { - opacity: 100; + opacity: 1; pointer-events: auto; + visibility: visible; } } } // Prevent last dropdown menu from going out of page - li.dropdown-menu:last-of-type { - ul.sub-menu { - left: unset; - transform: unset; - right: 0%; - } - } + // li.dropdown-menu:last-of-type { + // ul.sub-menu { + // left: unset; + // transform: unset; + // right: 0%; + // } + // } } } diff --git a/themes/haystack/assets/sass/main.scss b/themes/haystack/assets/sass/main.scss index 633b3773..db28212e 100644 --- a/themes/haystack/assets/sass/main.scss +++ b/themes/haystack/assets/sass/main.scss @@ -13,10 +13,11 @@ /** * Components */ -@import "components/header", "components/navigation", "components/article", - "components/buttons", "components/footer"; +@import "components/buttons", "components/header", "components/navigation", + "components/article", "components/loader", "components/forms", + "components/code", "components/footer"; /** * Pages */ -@import "pages/index"; +@import "pages/index", "pages/tutorials"; diff --git a/themes/haystack/assets/sass/pages/_index.scss b/themes/haystack/assets/sass/pages/_index.scss index 293a2272..275f9390 100644 --- a/themes/haystack/assets/sass/pages/_index.scss +++ b/themes/haystack/assets/sass/pages/_index.scss @@ -14,11 +14,6 @@ position: relative; padding-top: 3rem; - @include lg { - width: 90%; - margin: 0 auto; - } - // Hero text section .hero-text { position: relative; @@ -39,6 +34,7 @@ // Hero subtitle > p { font-size: var(--text-medium); + line-height: var(--line-height-heading); } // Hero bullet points @@ -149,17 +145,10 @@ } .showcase-image { - border-radius: 5px; + border-radius: var(--border-radius-md); } } } - - .inner { - @include lg { - width: 90%; - margin: 0 auto; - } - } } /** @@ -206,13 +195,6 @@ * Github section */ .index-github { - .inner { - @include lg { - width: 90%; - margin: 0 auto; - } - } - .top-section { background-color: var(--color-bg-light-grey); border-bottom: 0.125rem solid var(--color-light-grey); @@ -274,10 +256,18 @@ } .top-contributors-container-js { + position: relative; display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); gap: 2rem; + .loader { + position: absolute; + left: 0; + right: 0; + margin: 0 auto; + } + @include md { grid-template-columns: repeat(5, 1fr); gap: 3rem; @@ -318,6 +308,42 @@ /** * Newsletter section */ -.index-newsletter { +.index-community { background-color: var(--color-bg-dark-blue); + + .inner { + display: grid; + grid-template-columns: 1fr; + gap: 2rem; + + @include md { + grid-template-columns: 1fr 1fr; + } + } + + .discord-card, + .newsletter-card { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: flex-start; + padding: 2rem; + border-radius: var(--border-radius-md); + + img { + width: 5rem; + } + + .arrow-button { + background-color: var(--color-green); + } + } + + .discord-card { + background-color: var(--color-bg-light-grey); + } + + .newsletter-card { + background-color: var(--color-bg-light-grey); + } } diff --git a/themes/haystack/assets/sass/pages/_tutorials.scss b/themes/haystack/assets/sass/pages/_tutorials.scss new file mode 100644 index 00000000..5a699d68 --- /dev/null +++ b/themes/haystack/assets/sass/pages/_tutorials.scss @@ -0,0 +1,153 @@ +.tutorials { + // Heading + .tutorials-heading { + display: flex; + flex-direction: column; + + > h1, + > p { + text-align: center; + } + + > h1 { + margin-bottom: 2rem; + + @include md { + } + } + } + + // Buttons + .tutorials-buttons { + display: flex; + flex-direction: column-reverse; + flex-wrap: wrap; + gap: 3rem; + align-items: center; + justify-content: space-between; + margin-top: 1rem; + + @include md { + flex-direction: row; + margin-top: 3rem; + } + + .btn { + display: flex; + align-items: center; + gap: 0.5rem; + + > svg { + width: 1.2rem; + fill: var(--color-white); + margin-bottom: 0.2rem; + } + } + + .filter-btn { + background-color: transparent; + color: var(--color-dark-blue); + border: 2px solid var(--color-yellow); + opacity: 1; + transition-property: opacity, border-color; + transition-duration: var(--transition-fast); + transition-timing-function: var(--ease); + } + + .filter-btn.disabled { + opacity: 0.5; + border: 2px solid var(--color-light-grey); + } + } + + // Accordion container + .accordions { + margin-top: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; + + // Accordions + .accordion-js { + border-radius: var(--border-radius-md); + overflow: hidden; + + .content { + padding: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; + background-color: var(--color-bg-light-grey); + border: 2px solid var(--color-bg-dark-blue); + border-top: none; + border-radius: 0 0 var(--border-radius-md) var(--border-radius-md); + + @include md { + padding: 2rem; + gap: 2rem; + } + + // Tutorial cards + .tutorial-card { + display: flex; + flex-direction: column; + gap: 0.5rem; + text-decoration: none; + border: 2px solid var(--color-light-grey); + border-radius: var(--border-radius-md); + background: var(--color-white); + padding: 1rem; + transition: all var(--transition-fast) var(--ease); + user-select: none; + + @include md { + padding: 2rem; + } + + &:hover { + border: 2px solid var(--color-yellow); + filter: var(--shadow-filter); + } + } + } + } + + .accordion-js summary::-webkit-details-marker { + display: none; + } + + .accordion-title { + display: flex; + gap: 0.5rem; + position: relative; + background-color: var(--color-bg-dark-blue); + color: var(--color-white); + opacity: 1; + border: none; + outline: none; + font-size: var(--h3-size); + font-weight: var(--font-weight-heading); + padding: 1rem; + user-select: none; + cursor: pointer; + transition: opacity var(--transition-fast) var(--ease); + + > svg { + width: 1.25rem; + transform: rotate(-90deg); + } + + &:hover { + opacity: 0.95; + } + + @include md { + padding: 2rem; + } + } + + .accordion-js[open] .accordion-title svg { + transform: rotate(0deg); + } + } +} diff --git a/themes/haystack/layouts/_default/index.html b/themes/haystack/layouts/_default/index.html index 75ab72bf..bd736322 100644 --- a/themes/haystack/layouts/_default/index.html +++ b/themes/haystack/layouts/_default/index.html @@ -1,7 +1,8 @@ +{{/* Homepage */}} {{ define "main" }} {{ partial "index-hero" . }} {{ partial "index-showcase" . }} {{ partial "index-features" . }} {{ partial "index-github" . }} - {{ partial "index-newsletter" . }} + {{ partial "index-community" . }} {{ end }} diff --git a/themes/haystack/layouts/_default/tutorial.html b/themes/haystack/layouts/_default/tutorial.html new file mode 100644 index 00000000..6c333565 --- /dev/null +++ b/themes/haystack/layouts/_default/tutorial.html @@ -0,0 +1,81 @@ +{{/* Single tutorial */}} +{{ define "main" }} +
+
+
+ {{/* Toc sidebar */}} + {{ partial "tutorial-sidebar" . }} + + {{/* Tutorial */}} +
+

+ {{ .Params.title }} +

+ + {{/* Colab button */}} + {{ with .Params.colab }} + + + + + + + + + + + Open in Colab + + {{ end }} + + {{/* Article content */}} + {{ .Content }} + + +
+ {{ partial "prev-tutorial" (dict "context" . "level" .Params.level "url" .RelPermalink) }} + {{ partial "next-tutorial" (dict "context" . "level" .Params.level "url" .RelPermalink) }} +
+
+ + {{/* Stars/contribute sidebar */}} + +
+
+
+{{ end }} diff --git a/themes/haystack/layouts/_default/tutorials.html b/themes/haystack/layouts/_default/tutorials.html new file mode 100644 index 00000000..7fda1f4f --- /dev/null +++ b/themes/haystack/layouts/_default/tutorials.html @@ -0,0 +1,119 @@ +{{/* Tutorials page */}} +{{ define "main" }} +
+
+ {{/* Heading & subtitle */}} +
+

{{ .Params.title }}

+

{{ .Params.subtitle | markdownify }}

+
+ +
+ {{/* Filter buttons */}} +
+ {{ if eq .Params.categoryFilters.enabled true }} + {{ range .Params.categoryFilters.list }} + + {{ end }} + {{ end }} +
+ + {{/* Contribute btn */}} + + + + + {{ .Params.contribute.text }} + +
+ + {{/* Tutorial accordions */}} +
+ {{/* Beginner */}} +
+ + Beginner + + + + +
+ {{ range where (where .Pages "Section" "tutorials") ".Params.level" "beginner" }} + +

{{ .Params.title }}

+

{{ .Params.Description }}

+
+ {{ end }} +
+
+ + {{/* Intermediate */}} +
+ + Intermediate + + + + +
+ {{ range where (where .Pages "Section" "tutorials") ".Params.level" "intermediate" }} + +

{{ .Params.title }}

+

{{ .Params.Description }}

+
+ {{ end }} +
+
+ + {{/* Advanced */}} +
+ + Advanced + + + + +
+ {{ range where (where .Pages "Section" "tutorials") ".Params.level" "advanced" }} + +

{{ .Params.title }}

+

{{ .Params.Description }}

+
+ {{ end }} +
+
+
+
+
+{{ end }} diff --git a/themes/haystack/layouts/partials/arrow-button.html b/themes/haystack/layouts/partials/arrow-button.html index fdc0a76a..c3f91f7d 100644 --- a/themes/haystack/layouts/partials/arrow-button.html +++ b/themes/haystack/layouts/partials/arrow-button.html @@ -1,19 +1,83 @@ - +{{ if eq .type "link" }} +
- - - -
- {{ .buttonText }} -
- - - + + + +
+ {{ .text }} +
+ + +
-
\ No newline at end of file + +{{ end }} + +{{ if eq .type "submitButton" }} + +{{ end }} diff --git a/themes/haystack/layouts/partials/footer.html b/themes/haystack/layouts/partials/footer.html index f4227281..a7c23c20 100644 --- a/themes/haystack/layouts/partials/footer.html +++ b/themes/haystack/layouts/partials/footer.html @@ -1,4 +1,8 @@ -