diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e60682d..8433467 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: run: opam install ocamlformat.0.15.0 if: ${{ matrix.os == 'ubuntu-latest' }} - - name: Install opam packages + - name: Install Opam packages run: opam install . --deps-only --with-test - name: Check formatting @@ -45,8 +45,8 @@ jobs: if: ${{ matrix.os == 'ubuntu-latest' && always() }} - name: Run build - run: make build + run: opam exec -- dune build --root . --ignore-promoted-rules - name: Run the unit tests - run: make test + run: opam exec -- dune runtest --root . --ignore-promoted-rules timeout-minutes: 1 diff --git a/.gitignore b/.gitignore index bdb234f..14fe699 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,24 @@ -_build -_opam +# Dune generated files +_build/ +*.install + +# Local OPAM switch +_opam/ + +# Merlin configuration files .merlin + +# Normal npm stuff +npm-debug.log +yarn-error.log +node_modules/ +.cache/ +dist/ +build/ + +# ReScript /lib/bs /lib/**/*.js -.vscode \ No newline at end of file + +# IDE related +.vscode* diff --git a/Makefile b/Makefile index 9a96851..365c319 100644 --- a/Makefile +++ b/Makefile @@ -7,24 +7,35 @@ $(eval $(ARGS):;@:) all: opam exec -- dune build --root . @install -.PHONY: dev -dev: ## Install development dependencies - opam switch create . --no-install -y +.PHONY: deps +deps: ## Install development dependencies opam install -y dune-release ocamlformat utop ocaml-lsp-server + npm install opam install --deps-only --with-test --with-doc -y . +.PHONY: create_switch +create_switch: + opam switch create . 4.10.2 --no-install + +.PHONY: switch +switch: create_switch deps ## Create an opam switch and install development dependencies + +.PHONY: lock +lock: ## Generate a lock file + opam lock -y . + .PHONY: build build: ## Build the project, including non installable libraries and executables opam exec -- dune build --root . -.PHONY: start -start: all ## Start the project - opam exec -- dune exec --root . src/bin/main.exe $(ARGS) - .PHONY: install install: all ## Install the packages on the system opam exec -- dune install --root . +.PHONY: preview +preview: all ## Run the produced executable + cd src/ood-preview/ && opam exec -- script/watch.sh + .PHONY: test test: ## Run the unit tests opam exec -- dune runtest --root . @@ -43,7 +54,7 @@ servedoc: doc ## Open odoc documentation with default web browser .PHONY: fmt fmt: ## Format the codebase with ocamlformat - opam exec -- dune build --root . @fmt --auto-promote + opam exec -- dune build --root . --auto-promote @fmt .PHONY: watch watch: ## Watch for the filesystem and rebuild on every change diff --git a/data/books/algorithmen-datenstrukturen-funktionale-programmierung-eine-praktische-einfuhrung-mit-caml-light.md b/data/books/en/algorithmen-datenstrukturen-funktionale-programmierung-eine-praktische-einfuhrung-mit-caml-light.md similarity index 97% rename from data/books/algorithmen-datenstrukturen-funktionale-programmierung-eine-praktische-einfuhrung-mit-caml-light.md rename to data/books/en/algorithmen-datenstrukturen-funktionale-programmierung-eine-praktische-einfuhrung-mit-caml-light.md index 8bb3414..f5d3a8b 100644 --- a/data/books/algorithmen-datenstrukturen-funktionale-programmierung-eine-praktische-einfuhrung-mit-caml-light.md +++ b/data/books/en/algorithmen-datenstrukturen-funktionale-programmierung-eine-praktische-einfuhrung-mit-caml-light.md @@ -8,7 +8,7 @@ description: > authors: - Juergen Wolff von Gudenberg published: "1996" -cover: ./img/wolff.gif +cover: books/wolff.gif language: german --- diff --git a/data/books/apprendre-a-programmer-avec-ocaml.md b/data/books/en/apprendre-a-programmer-avec-ocaml.md similarity index 98% rename from data/books/apprendre-a-programmer-avec-ocaml.md rename to data/books/en/apprendre-a-programmer-avec-ocaml.md index 69140ec..8b39f90 100644 --- a/data/books/apprendre-a-programmer-avec-ocaml.md +++ b/data/books/en/apprendre-a-programmer-avec-ocaml.md @@ -12,7 +12,7 @@ authors: - Jean-Christophe Filliâtre - Sylvain Conchon published: "2014" -cover: ./img/apprendre_ocaml_cover.png +cover: books/apprendre_ocaml_cover.png language: french isbn: "2-21213-678-1" --- diff --git a/data/books/apprentissage-de-la-programmation-avec-ocaml.md b/data/books/en/apprentissage-de-la-programmation-avec-ocaml.md similarity index 97% rename from data/books/apprentissage-de-la-programmation-avec-ocaml.md rename to data/books/en/apprentissage-de-la-programmation-avec-ocaml.md index c2e70dc..8f3fd7e 100644 --- a/data/books/apprentissage-de-la-programmation-avec-ocaml.md +++ b/data/books/en/apprentissage-de-la-programmation-avec-ocaml.md @@ -10,7 +10,7 @@ authors: - Catherine Dubois - Valérie Ménissier Morain published: "2004" -cover: ./img/dubois-menissier.gif +cover: books/dubois-menissier.gif language: french isbn: "2-7462-0819-9" --- diff --git a/data/books/approche-fonctionnelle-de-la-programmation.md b/data/books/en/approche-fonctionnelle-de-la-programmation.md similarity index 96% rename from data/books/approche-fonctionnelle-de-la-programmation.md rename to data/books/en/approche-fonctionnelle-de-la-programmation.md index 70df674..fb9f775 100644 --- a/data/books/approche-fonctionnelle-de-la-programmation.md +++ b/data/books/en/approche-fonctionnelle-de-la-programmation.md @@ -5,7 +5,7 @@ authors: - Guy Cousineau - Michel Mauny published: "1995" -cover: ./img/cousineau-mauny-fr.gif +cover: books/cousineau-mauny-fr.gif language: french isbn: "2-84074-114-8" --- diff --git a/data/books/concepts-et-outils-de-programmation.md b/data/books/en/concepts-et-outils-de-programmation.md similarity index 96% rename from data/books/concepts-et-outils-de-programmation.md rename to data/books/en/concepts-et-outils-de-programmation.md index a925658..a82da3f 100644 --- a/data/books/concepts-et-outils-de-programmation.md +++ b/data/books/en/concepts-et-outils-de-programmation.md @@ -6,7 +6,7 @@ authors: - Thérèse Accart Hardin - Véronique Donzeau-Gouge Viguié published: "1992" -cover: ./img/hardin-donzeau-gouge.gif +cover: books/hardin-donzeau-gouge.gif language: french isbn: "2-7296-0419-7" --- diff --git a/data/books/cour-et-exercises-d-informatique.md b/data/books/en/cour-et-exercises-d-informatique.md similarity index 97% rename from data/books/cour-et-exercises-d-informatique.md rename to data/books/en/cour-et-exercises-d-informatique.md index c294b13..389459c 100644 --- a/data/books/cour-et-exercises-d-informatique.md +++ b/data/books/en/cour-et-exercises-d-informatique.md @@ -10,7 +10,7 @@ description: > authors: - Luc Albert published: "1997" -cover: ./img/albert.gif +cover: books/albert.gif language: french isbn: "2-84180-106-3" --- diff --git a/data/books/developing-applications-with-ocaml.md b/data/books/en/developing-applications-with-ocaml.md similarity index 95% rename from data/books/developing-applications-with-ocaml.md rename to data/books/en/developing-applications-with-ocaml.md index a6ae255..d34575e 100644 --- a/data/books/developing-applications-with-ocaml.md +++ b/data/books/en/developing-applications-with-ocaml.md @@ -7,7 +7,7 @@ authors: - Pascal Manoury - Bruno Pagano published: "2002" -cover: ./img/logocaml-oreilly.gif +cover: books/logocaml-oreilly.gif language: english --- diff --git a/data/books/developpement-d-applications-avec-objective-caml.md b/data/books/en/developpement-d-applications-avec-objective-caml.md similarity index 96% rename from data/books/developpement-d-applications-avec-objective-caml.md rename to data/books/en/developpement-d-applications-avec-objective-caml.md index 425ff28..6db68f7 100644 --- a/data/books/developpement-d-applications-avec-objective-caml.md +++ b/data/books/en/developpement-d-applications-avec-objective-caml.md @@ -7,7 +7,7 @@ authors: - Pascal Manoury - Bruno Pagano published: "2000" -cover: ./img/chailloux-manoury-pagano.jpg +cover: books/chailloux-manoury-pagano.jpg language: french isbn: "2-84177-121-0" --- diff --git a/data/books/initiation-a-la-programmation-fonctionnelle-en-ocaml.md b/data/books/en/initiation-a-la-programmation-fonctionnelle-en-ocaml.md similarity index 94% rename from data/books/initiation-a-la-programmation-fonctionnelle-en-ocaml.md rename to data/books/en/initiation-a-la-programmation-fonctionnelle-en-ocaml.md index df8a60a..287e506 100644 --- a/data/books/initiation-a-la-programmation-fonctionnelle-en-ocaml.md +++ b/data/books/en/initiation-a-la-programmation-fonctionnelle-en-ocaml.md @@ -6,7 +6,7 @@ description: > authors: - Mohammed-Said Habet published: "2015" -cover: ./img/Initiation_a_la_programmation_fonctionnelle_en_OCaml.jpg +cover: books/Initiation_a_la_programmation_fonctionnelle_en_OCaml.jpg language: french isbn: "9782332978400" --- diff --git a/data/books/introduction-to-ocaml.md b/data/books/en/introduction-to-ocaml.md similarity index 97% rename from data/books/introduction-to-ocaml.md rename to data/books/en/introduction-to-ocaml.md index 4486027..04ad533 100644 --- a/data/books/introduction-to-ocaml.md +++ b/data/books/en/introduction-to-ocaml.md @@ -5,7 +5,6 @@ description: > authors: - Jason Hickey published: "2008" -cover: ./img/default.png language: english --- diff --git a/data/books/introduzione-alla-programmazione-funzionale.md b/data/books/en/introduzione-alla-programmazione-funzionale.md similarity index 86% rename from data/books/introduzione-alla-programmazione-funzionale.md rename to data/books/en/introduzione-alla-programmazione-funzionale.md index 8f0a6f9..b46a579 100644 --- a/data/books/introduzione-alla-programmazione-funzionale.md +++ b/data/books/en/introduzione-alla-programmazione-funzionale.md @@ -6,7 +6,7 @@ authors: - Carla Limongelli - Marta Cialdea published: "2002" -cover: ./img/limongelli-cialdea.gif +cover: books/limongelli-cialdea.gif language: italian isbn: "88-7488-031-6" --- diff --git a/data/books/le-langage-caml.md b/data/books/en/le-langage-caml.md similarity index 96% rename from data/books/le-langage-caml.md rename to data/books/en/le-langage-caml.md index e3b4ca1..3fc2419 100644 --- a/data/books/le-langage-caml.md +++ b/data/books/en/le-langage-caml.md @@ -12,7 +12,7 @@ authors: - Xavier Leroy - Pierre Weis published: "1993" -cover: ./img/leroy-weis.jpg +cover: books/le-language-caml-cover.jpg language: french isbn: "2-10-004383-8" --- diff --git a/data/books/manuel-de-reference-du-langage-caml.md b/data/books/en/manuel-de-reference-du-langage-caml.md similarity index 95% rename from data/books/manuel-de-reference-du-langage-caml.md rename to data/books/en/manuel-de-reference-du-langage-caml.md index e2e08d6..f52dcdf 100644 --- a/data/books/manuel-de-reference-du-langage-caml.md +++ b/data/books/en/manuel-de-reference-du-langage-caml.md @@ -6,7 +6,7 @@ authors: - Xavier Leroy - Pierre Weis published: "1993" -cover: ./img/leroy-weis.jpg +cover: books/manuel-de-reference-du-langage-caml-cover.jpg language: french isbn: "2-7296-0492-8" --- diff --git a/data/books/more-ocaml-algorithms-methods-diversions.md b/data/books/en/more-ocaml-algorithms-methods-diversions.md similarity index 97% rename from data/books/more-ocaml-algorithms-methods-diversions.md rename to data/books/en/more-ocaml-algorithms-methods-diversions.md index 03c7d54..7f1678b 100644 --- a/data/books/more-ocaml-algorithms-methods-diversions.md +++ b/data/books/en/more-ocaml-algorithms-methods-diversions.md @@ -7,7 +7,7 @@ description: > authors: - John Whitington published: 2014-08-26 -cover: ./img/more-ocaml-300-376.png +cover: books/more-ocaml-300-376.png language: english --- diff --git a/data/books/nouveaux-exercises-d-algorithmique.md b/data/books/en/nouveaux-exercises-d-algorithmique.md similarity index 97% rename from data/books/nouveaux-exercises-d-algorithmique.md rename to data/books/en/nouveaux-exercises-d-algorithmique.md index e58804d..97d3c90 100644 --- a/data/books/nouveaux-exercises-d-algorithmique.md +++ b/data/books/en/nouveaux-exercises-d-algorithmique.md @@ -8,7 +8,7 @@ description: > authors: - Michel Quercia published: "2000" -cover: ./img/quercia.gif +cover: books/quercia.gif language: french isbn: "2-7117-8990" --- diff --git a/data/books/ocaml-book.md b/data/books/en/ocaml-book.md similarity index 94% rename from data/books/ocaml-book.md rename to data/books/en/ocaml-book.md index 4fa7638..0510d48 100644 --- a/data/books/ocaml-book.md +++ b/data/books/en/ocaml-book.md @@ -5,7 +5,6 @@ description: > authors: - Hongbo Zhang published: "2011" -cover: ./img/default.png language: english --- diff --git a/data/books/ocaml-for-scientists.md b/data/books/en/ocaml-for-scientists.md similarity index 96% rename from data/books/ocaml-for-scientists.md rename to data/books/en/ocaml-for-scientists.md index c6e2016..676d40d 100644 --- a/data/books/ocaml-for-scientists.md +++ b/data/books/en/ocaml-for-scientists.md @@ -6,7 +6,7 @@ description: > authors: - "Jon D. Harrop" published: "2005" -cover: ./img/more-ocaml-300-376.png +cover: books/harrop-book.gif language: english --- diff --git a/data/books/ocaml-from-the-very-beginning.md b/data/books/en/ocaml-from-the-very-beginning.md similarity index 96% rename from data/books/ocaml-from-the-very-beginning.md rename to data/books/en/ocaml-from-the-very-beginning.md index 58fe74a..f1559e9 100644 --- a/data/books/ocaml-from-the-very-beginning.md +++ b/data/books/en/ocaml-from-the-very-beginning.md @@ -7,7 +7,7 @@ description: > authors: - John Whitington published: 2013-06-07 -cover: ./img/OCaml_from_beginning.png +cover: books/OCaml_from_beginning.png language: english --- diff --git a/data/books/ocaml-programacao-funcional-na-practica.md b/data/books/en/ocaml-programacao-funcional-na-practica.md similarity index 97% rename from data/books/ocaml-programacao-funcional-na-practica.md rename to data/books/en/ocaml-programacao-funcional-na-practica.md index 5e93015..3db3d52 100644 --- a/data/books/ocaml-programacao-funcional-na-practica.md +++ b/data/books/en/ocaml-programacao-funcional-na-practica.md @@ -7,7 +7,7 @@ description: > authors: - Andrei de Araújo Formiga published: "2015" -cover: ./img/opfp.png +cover: books/opfp.png language: portugese --- diff --git a/data/books/ocaml-system.md b/data/books/en/ocaml-system.md similarity index 96% rename from data/books/ocaml-system.md rename to data/books/en/ocaml-system.md index af394f2..380cab0 100644 --- a/data/books/ocaml-system.md +++ b/data/books/en/ocaml-system.md @@ -7,6 +7,7 @@ authors: - Jacques Garrigue - Didier Rémy - Jérôme Vouillon +cover: books/colour-icon-170x148.png language: english --- diff --git a/data/books/option-informatique-mpmp.md b/data/books/en/option-informatique-mpmp.md similarity index 96% rename from data/books/option-informatique-mpmp.md rename to data/books/en/option-informatique-mpmp.md index 31f2b21..118f8ef 100644 --- a/data/books/option-informatique-mpmp.md +++ b/data/books/en/option-informatique-mpmp.md @@ -8,7 +8,7 @@ description: > authors: - Denis Monasse published: "1997" -cover: ./img/monasse-2.jpg +cover: books/monasse-2.jpg language: french isbn: "2-7117-8839-3" --- diff --git a/data/books/option-informatique-mpsi.md b/data/books/en/option-informatique-mpsi.md similarity index 97% rename from data/books/option-informatique-mpsi.md rename to data/books/en/option-informatique-mpsi.md index 3c3b660..b9e7c6d 100644 --- a/data/books/option-informatique-mpsi.md +++ b/data/books/en/option-informatique-mpsi.md @@ -7,7 +7,7 @@ description: > authors: - Denis Monasse published: "1996" -cover: ./img/monasse-1.gif +cover: books/monasse-1.gif language: french isbn: "2-7117-8831-8" --- diff --git a/data/books/programmation-de-droite-a-gauche-et-vice-versa.md b/data/books/en/programmation-de-droite-a-gauche-et-vice-versa.md similarity index 90% rename from data/books/programmation-de-droite-a-gauche-et-vice-versa.md rename to data/books/en/programmation-de-droite-a-gauche-et-vice-versa.md index 62b9caf..d304802 100644 --- a/data/books/programmation-de-droite-a-gauche-et-vice-versa.md +++ b/data/books/en/programmation-de-droite-a-gauche-et-vice-versa.md @@ -5,7 +5,7 @@ description: > authors: - Pascal Manoury published: "2005" -cover: ./img/manoury.jpg +cover: books/manoury.png language: french isbn: "978-2-916466-05-7" --- diff --git a/data/books/programmation-en-caml.md b/data/books/en/programmation-en-caml.md similarity index 97% rename from data/books/programmation-en-caml.md rename to data/books/en/programmation-en-caml.md index d787ab4..e5be084 100644 --- a/data/books/programmation-en-caml.md +++ b/data/books/en/programmation-en-caml.md @@ -7,7 +7,7 @@ description: > authors: - Jacques Rouablé published: "1997" -cover: ./img/rouable.jpg +cover: books/rouable.jpg language: french isbn: "2-212-08944-9" --- diff --git a/data/books/programmation-fonctionnelle-generique-et-objet-une-introduction-avec-le-langage-ocaml.md b/data/books/en/programmation-fonctionnelle-generique-et-objet-une-introduction-avec-le-langage-ocaml.md similarity index 90% rename from data/books/programmation-fonctionnelle-generique-et-objet-une-introduction-avec-le-langage-ocaml.md rename to data/books/en/programmation-fonctionnelle-generique-et-objet-une-introduction-avec-le-langage-ocaml.md index b115a2e..e5dff0e 100644 --- a/data/books/programmation-fonctionnelle-generique-et-objet-une-introduction-avec-le-langage-ocaml.md +++ b/data/books/en/programmation-fonctionnelle-generique-et-objet-une-introduction-avec-le-langage-ocaml.md @@ -5,7 +5,7 @@ description: > authors: - Philippe Narbel published: "2005" -cover: ./img/narbel.jpg +cover: books/narbel.jpg language: french isbn: "2-7117-4843-X" --- diff --git a/data/books/programmazione-funzionale-una-semplice-introduzione.md b/data/books/en/programmazione-funzionale-una-semplice-introduzione.md similarity index 95% rename from data/books/programmazione-funzionale-una-semplice-introduzione.md rename to data/books/en/programmazione-funzionale-una-semplice-introduzione.md index 14c5763..c086c41 100644 --- a/data/books/programmazione-funzionale-una-semplice-introduzione.md +++ b/data/books/en/programmazione-funzionale-una-semplice-introduzione.md @@ -5,7 +5,6 @@ description: > authors: - Massimo Maria Ghisalberti published: "2015" -cover: ./img/default.png language: italian --- diff --git a/data/books/real-world-ocaml.md b/data/books/en/real-world-ocaml.md similarity index 97% rename from data/books/real-world-ocaml.md rename to data/books/en/real-world-ocaml.md index 7ec266f..e99b377 100644 --- a/data/books/real-world-ocaml.md +++ b/data/books/en/real-world-ocaml.md @@ -9,7 +9,7 @@ authors: - Anil Madhavapeddy - Yaron Minsky published: 2013-11-25 -cover: ./img/real-world-ocaml.jpg +cover: books/real-world-ocaml.jpg language: english --- diff --git a/data/books/seize-problemes-d-informatique.md b/data/books/en/seize-problemes-d-informatique.md similarity index 97% rename from data/books/seize-problemes-d-informatique.md rename to data/books/en/seize-problemes-d-informatique.md index 4943699..59327b9 100644 --- a/data/books/seize-problemes-d-informatique.md +++ b/data/books/en/seize-problemes-d-informatique.md @@ -7,7 +7,7 @@ description: > authors: - Bruno Petazzoni published: "2001" -cover: ./img/petazzoni.jpg +cover: books/petazzoni.jpg language: french isbn: "3-540-67387-3" --- diff --git a/data/books/the-functional-approach-to-programming.md b/data/books/en/the-functional-approach-to-programming.md similarity index 96% rename from data/books/the-functional-approach-to-programming.md rename to data/books/en/the-functional-approach-to-programming.md index 2da7040..0f3511c 100644 --- a/data/books/the-functional-approach-to-programming.md +++ b/data/books/en/the-functional-approach-to-programming.md @@ -5,7 +5,7 @@ description: > authors: - Guy Cousineau published: "1998" -cover: ./img/cousineau-mauny-en.gif +cover: books/cousineau-mauny-en.gif language: english isbn: "0-521-57681-4" --- diff --git a/data/books/think-ocaml-how-to-think-like-a-functional-programmer.md b/data/books/en/think-ocaml-how-to-think-like-a-functional-programmer.md similarity index 94% rename from data/books/think-ocaml-how-to-think-like-a-functional-programmer.md rename to data/books/en/think-ocaml-how-to-think-like-a-functional-programmer.md index 08e9905..d8a1887 100644 --- a/data/books/think-ocaml-how-to-think-like-a-functional-programmer.md +++ b/data/books/en/think-ocaml-how-to-think-like-a-functional-programmer.md @@ -6,7 +6,7 @@ authors: - Nicholas Monje - Allen Downey published: "2008" -cover: ./img/default.png +cover: books/thinkocaml_cover_web.png language: english --- diff --git a/data/books/unix-system-programming-in-ocaml.md b/data/books/en/unix-system-programming-in-ocaml.md similarity index 96% rename from data/books/unix-system-programming-in-ocaml.md rename to data/books/en/unix-system-programming-in-ocaml.md index c51f8d8..68fc9cf 100644 --- a/data/books/unix-system-programming-in-ocaml.md +++ b/data/books/en/unix-system-programming-in-ocaml.md @@ -6,7 +6,6 @@ authors: - Xavier Leroy - Didier Rémy published: 2010-05-01 -cover: ./img/default.png language: english --- diff --git a/data/books/using-understanding-and-unraveling-ocaml.md b/data/books/en/using-understanding-and-unraveling-ocaml.md similarity index 96% rename from data/books/using-understanding-and-unraveling-ocaml.md rename to data/books/en/using-understanding-and-unraveling-ocaml.md index d49ebab..7871cda 100644 --- a/data/books/using-understanding-and-unraveling-ocaml.md +++ b/data/books/en/using-understanding-and-unraveling-ocaml.md @@ -6,7 +6,6 @@ description: > authors: - Didier Rémy published: 2002-09-20 -cover: ./img/default.png language: english --- diff --git a/data/media/books/Initiation_a_la_programmation_fonctionnelle_en_OCaml.jpg b/data/media/books/Initiation_a_la_programmation_fonctionnelle_en_OCaml.jpg new file mode 100644 index 0000000..5e5ccd8 Binary files /dev/null and b/data/media/books/Initiation_a_la_programmation_fonctionnelle_en_OCaml.jpg differ diff --git a/data/media/books/OCaml_from_beginning.png b/data/media/books/OCaml_from_beginning.png new file mode 100644 index 0000000..3cf312b Binary files /dev/null and b/data/media/books/OCaml_from_beginning.png differ diff --git a/data/books/img/albert.gif b/data/media/books/albert.gif similarity index 100% rename from data/books/img/albert.gif rename to data/media/books/albert.gif diff --git a/data/media/books/apprendre_ocaml_cover.png b/data/media/books/apprendre_ocaml_cover.png new file mode 100644 index 0000000..b5716d5 Binary files /dev/null and b/data/media/books/apprendre_ocaml_cover.png differ diff --git a/data/books/img/chailloux-manoury-pagano.jpg b/data/media/books/chailloux-manoury-pagano.jpg similarity index 100% rename from data/books/img/chailloux-manoury-pagano.jpg rename to data/media/books/chailloux-manoury-pagano.jpg diff --git a/data/media/books/colour-icon-170x148.png b/data/media/books/colour-icon-170x148.png new file mode 100644 index 0000000..ee8e1bc Binary files /dev/null and b/data/media/books/colour-icon-170x148.png differ diff --git a/data/books/img/cousineau-mauny-en.gif b/data/media/books/cousineau-mauny-en.gif similarity index 100% rename from data/books/img/cousineau-mauny-en.gif rename to data/media/books/cousineau-mauny-en.gif diff --git a/data/books/img/cousineau-mauny-fr.gif b/data/media/books/cousineau-mauny-fr.gif similarity index 100% rename from data/books/img/cousineau-mauny-fr.gif rename to data/media/books/cousineau-mauny-fr.gif diff --git a/data/books/img/dubois-menissier.gif b/data/media/books/dubois-menissier.gif similarity index 100% rename from data/books/img/dubois-menissier.gif rename to data/media/books/dubois-menissier.gif diff --git a/data/books/img/hardin-donzeau-gouge.gif b/data/media/books/hardin-donzeau-gouge.gif similarity index 100% rename from data/books/img/hardin-donzeau-gouge.gif rename to data/media/books/hardin-donzeau-gouge.gif diff --git a/data/media/books/harrop-book.gif b/data/media/books/harrop-book.gif new file mode 100644 index 0000000..8f994e9 Binary files /dev/null and b/data/media/books/harrop-book.gif differ diff --git a/data/books/img/weis-leroy.jpg b/data/media/books/le-language-caml-cover.jpg similarity index 100% rename from data/books/img/weis-leroy.jpg rename to data/media/books/le-language-caml-cover.jpg diff --git a/data/books/img/limongelli-cialdea.gif b/data/media/books/limongelli-cialdea.gif similarity index 100% rename from data/books/img/limongelli-cialdea.gif rename to data/media/books/limongelli-cialdea.gif diff --git a/data/media/books/logocaml-oreilly.gif b/data/media/books/logocaml-oreilly.gif new file mode 100644 index 0000000..afc3966 Binary files /dev/null and b/data/media/books/logocaml-oreilly.gif differ diff --git a/data/books/img/manoury.png b/data/media/books/manoury.png similarity index 100% rename from data/books/img/manoury.png rename to data/media/books/manoury.png diff --git a/data/books/img/leroy-weis.jpg b/data/media/books/manuel-de-reference-du-langage-caml-cover.jpg similarity index 100% rename from data/books/img/leroy-weis.jpg rename to data/media/books/manuel-de-reference-du-langage-caml-cover.jpg diff --git a/data/books/img/monasse-1.gif b/data/media/books/monasse-1.gif similarity index 100% rename from data/books/img/monasse-1.gif rename to data/media/books/monasse-1.gif diff --git a/data/books/img/monasse-2.jpg b/data/media/books/monasse-2.jpg similarity index 100% rename from data/books/img/monasse-2.jpg rename to data/media/books/monasse-2.jpg diff --git a/data/media/books/more-ocaml-300-376.png b/data/media/books/more-ocaml-300-376.png new file mode 100644 index 0000000..bf8a6b3 Binary files /dev/null and b/data/media/books/more-ocaml-300-376.png differ diff --git a/data/books/img/narbel.jpg b/data/media/books/narbel.jpg similarity index 100% rename from data/books/img/narbel.jpg rename to data/media/books/narbel.jpg diff --git a/data/media/books/opfp.png b/data/media/books/opfp.png new file mode 100644 index 0000000..fad9724 Binary files /dev/null and b/data/media/books/opfp.png differ diff --git a/data/books/img/petazzoni.jpg b/data/media/books/petazzoni.jpg similarity index 100% rename from data/books/img/petazzoni.jpg rename to data/media/books/petazzoni.jpg diff --git a/data/books/img/quercia.gif b/data/media/books/quercia.gif similarity index 100% rename from data/books/img/quercia.gif rename to data/media/books/quercia.gif diff --git a/data/media/books/real-world-ocaml.jpg b/data/media/books/real-world-ocaml.jpg new file mode 100644 index 0000000..4e1fedf Binary files /dev/null and b/data/media/books/real-world-ocaml.jpg differ diff --git a/data/books/img/rouable.jpg b/data/media/books/rouable.jpg similarity index 100% rename from data/books/img/rouable.jpg rename to data/media/books/rouable.jpg diff --git a/data/media/books/thinkocaml_cover_web.png b/data/media/books/thinkocaml_cover_web.png new file mode 100644 index 0000000..4659ca6 Binary files /dev/null and b/data/media/books/thinkocaml_cover_web.png differ diff --git a/data/books/img/wolff.gif b/data/media/books/wolff.gif similarity index 100% rename from data/books/img/wolff.gif rename to data/media/books/wolff.gif diff --git a/data/media/astree-thumb.gif b/data/media/success-stories/astree-thumb.gif similarity index 100% rename from data/media/astree-thumb.gif rename to data/media/success-stories/astree-thumb.gif diff --git a/data/media/coq-thumb.jpg b/data/media/success-stories/coq-thumb.jpg similarity index 100% rename from data/media/coq-thumb.jpg rename to data/media/success-stories/coq-thumb.jpg diff --git a/data/media/fftw-thumb.png b/data/media/success-stories/fftw-thumb.png similarity index 100% rename from data/media/fftw-thumb.png rename to data/media/success-stories/fftw-thumb.png diff --git a/data/media/jane-street-thumb.jpg b/data/media/success-stories/jane-street-thumb.jpg similarity index 100% rename from data/media/jane-street-thumb.jpg rename to data/media/success-stories/jane-street-thumb.jpg diff --git a/data/media/leroy-weis.jpg b/data/media/success-stories/leroy-weis.jpg similarity index 100% rename from data/media/leroy-weis.jpg rename to data/media/success-stories/leroy-weis.jpg diff --git a/data/media/lexifi-thumb.jpg b/data/media/success-stories/lexifi-thumb.jpg similarity index 100% rename from data/media/lexifi-thumb.jpg rename to data/media/success-stories/lexifi-thumb.jpg diff --git a/data/media/mldonkey-thumb.jpg b/data/media/success-stories/mldonkey-thumb.jpg similarity index 100% rename from data/media/mldonkey-thumb.jpg rename to data/media/success-stories/mldonkey-thumb.jpg diff --git a/data/media/unison-thumb.jpg b/data/media/success-stories/unison-thumb.jpg similarity index 100% rename from data/media/unison-thumb.jpg rename to data/media/success-stories/unison-thumb.jpg diff --git a/data/success_stories/en/astree.md b/data/success_stories/en/astree.md index b07c283..27beb69 100644 --- a/data/success_stories/en/astree.md +++ b/data/success_stories/en/astree.md @@ -1,6 +1,6 @@ --- title: The ASTRÉE Static Analyzer -image: astree-thumb.gif +image: success-stories/astree-thumb.gif url: http://www.astree.ens.fr/ --- diff --git a/data/success_stories/en/coq.md b/data/success_stories/en/coq.md index cd61f22..0dc9d7f 100644 --- a/data/success_stories/en/coq.md +++ b/data/success_stories/en/coq.md @@ -1,6 +1,6 @@ --- title: Coq -image: coq-thumb.jpg +image: success-stories/coq-thumb.jpg url: http://coq.inria.fr/ --- diff --git a/data/success_stories/en/fftw.md b/data/success_stories/en/fftw.md index 25afe3b..523a664 100644 --- a/data/success_stories/en/fftw.md +++ b/data/success_stories/en/fftw.md @@ -1,6 +1,6 @@ --- title: FFTW -image: fftw-thumb.png +image: success-stories/fftw-thumb.png url: http://www.fftw.org/ --- diff --git a/data/success_stories/en/jane_street.md b/data/success_stories/en/jane_street.md index 7cac024..4e215b8 100644 --- a/data/success_stories/en/jane_street.md +++ b/data/success_stories/en/jane_street.md @@ -1,6 +1,6 @@ --- title: Jane Street -image: jane-street-thumb.jpg +image: success-stories/jane-street-thumb.jpg url: http://janestreet.com/technology/ --- diff --git a/data/success_stories/en/lexifi.md b/data/success_stories/en/lexifi.md index ae26b64..1dd9943 100644 --- a/data/success_stories/en/lexifi.md +++ b/data/success_stories/en/lexifi.md @@ -1,6 +1,6 @@ --- title: LexiFi's Modeling Language for Finance -image: lexifi-thumb.jpg +image: success-stories/lexifi-thumb.jpg url: http://www.lexifi.com/ --- diff --git a/data/success_stories/en/mldonkey.md b/data/success_stories/en/mldonkey.md index 1455553..a0eadfc 100644 --- a/data/success_stories/en/mldonkey.md +++ b/data/success_stories/en/mldonkey.md @@ -1,6 +1,6 @@ --- title: The MLdonkey peer-to-peer client -image: mldonkey-thumb.jpg +image: success-stories/mldonkey-thumb.jpg url: http://mldonkey.sourceforge.net/Main_Page --- diff --git a/data/success_stories/en/unison.md b/data/success_stories/en/unison.md index 62c835f..6600ae1 100644 --- a/data/success_stories/en/unison.md +++ b/data/success_stories/en/unison.md @@ -1,6 +1,6 @@ --- title: The Unison File Synchronizer -image: unison-thumb.jpg +image: success-stories/unison-thumb.jpg url: http://www.cis.upenn.edu/%7Ebcpierce/unison/ --- diff --git a/data/success_stories/fr/astree.md b/data/success_stories/fr/astree.md index d8a0032..279acc5 100644 --- a/data/success_stories/fr/astree.md +++ b/data/success_stories/fr/astree.md @@ -1,6 +1,6 @@ --- title: L'analyseur statique ASTRÉE -image: astree-thumb.gif +image: success-stories/astree-thumb.gif url: http://www.astree.ens.fr/ --- diff --git a/data/success_stories/fr/coq.md b/data/success_stories/fr/coq.md index e70225d..028e2ab 100644 --- a/data/success_stories/fr/coq.md +++ b/data/success_stories/fr/coq.md @@ -1,6 +1,6 @@ --- title: Coq -image: coq-thumb.jpg +image: success-stories/coq-thumb.jpg url: http://coq.inria.fr/ --- diff --git a/data/success_stories/fr/fftw.md b/data/success_stories/fr/fftw.md index f6c9e0e..3b906a9 100644 --- a/data/success_stories/fr/fftw.md +++ b/data/success_stories/fr/fftw.md @@ -1,6 +1,6 @@ --- title: FFTW -image: fftw-thumb.png +image: success-stories/fftw-thumb.png url: http://www.fftw.org/ --- diff --git a/data/success_stories/fr/jane_street.md b/data/success_stories/fr/jane_street.md index 9ff71fa..57529c7 100644 --- a/data/success_stories/fr/jane_street.md +++ b/data/success_stories/fr/jane_street.md @@ -1,6 +1,6 @@ --- title: Jane Street -image: jane-street-thumb.jpg +image: success-stories/jane-street-thumb.jpg url: http://janestreet.com/technology/ --- diff --git a/data/success_stories/fr/lexifi.md b/data/success_stories/fr/lexifi.md index 7da0c35..1b54e6a 100644 --- a/data/success_stories/fr/lexifi.md +++ b/data/success_stories/fr/lexifi.md @@ -1,6 +1,6 @@ --- title: Le Langage de Modélisation Financière de LexiFi -image: lexifi-thumb.jpg +image: success-stories/lexifi-thumb.jpg url: http://www.lexifi.com/ --- diff --git a/data/success_stories/fr/mldonkey.md b/data/success_stories/fr/mldonkey.md index 17f0482..559a8b3 100644 --- a/data/success_stories/fr/mldonkey.md +++ b/data/success_stories/fr/mldonkey.md @@ -1,6 +1,6 @@ --- title: Le client pair-à-pair MLdonkey -image: mldonkey-thumb.jpg +image: success-stories/mldonkey-thumb.jpg url: http://mldonkey.sourceforge.net/Main_Page --- diff --git a/data/success_stories/fr/unison.md b/data/success_stories/fr/unison.md index f958dec..87ad1c2 100644 --- a/data/success_stories/fr/unison.md +++ b/data/success_stories/fr/unison.md @@ -1,6 +1,6 @@ --- title: Le synchroniseur de fichiers Unison -image: unison-thumb.jpg +image: success-stories/unison-thumb.jpg url: http://www.cis.upenn.edu/%7Ebcpierce/unison/ --- diff --git a/data/tutorials/en/a_first_hour_with_ocaml.md b/data/tutorials/en/001_a_first_hour_with_ocaml.md similarity index 100% rename from data/tutorials/en/a_first_hour_with_ocaml.md rename to data/tutorials/en/001_a_first_hour_with_ocaml.md diff --git a/data/tutorials/en/guidelines.md b/data/tutorials/en/002_guidelines.md similarity index 100% rename from data/tutorials/en/guidelines.md rename to data/tutorials/en/002_guidelines.md diff --git a/data/tutorials/en/compiling_ocaml_projects.md b/data/tutorials/en/003_compiling_ocaml_projects.md similarity index 100% rename from data/tutorials/en/compiling_ocaml_projects.md rename to data/tutorials/en/003_compiling_ocaml_projects.md diff --git a/data/tutorials/en/data_types_and_matching.md b/data/tutorials/en/004_data_types_and_matching.md similarity index 100% rename from data/tutorials/en/data_types_and_matching.md rename to data/tutorials/en/004_data_types_and_matching.md diff --git a/data/tutorials/en/functional_programming.md b/data/tutorials/en/005_functional_programming.md similarity index 100% rename from data/tutorials/en/functional_programming.md rename to data/tutorials/en/005_functional_programming.md diff --git a/data/tutorials/en/if_statements_loops_and_recursion.md b/data/tutorials/en/006_if_statements_loops_and_recursion.md similarity index 100% rename from data/tutorials/en/if_statements_loops_and_recursion.md rename to data/tutorials/en/006_if_statements_loops_and_recursion.md diff --git a/data/tutorials/en/modules.md b/data/tutorials/en/007_modules.md similarity index 100% rename from data/tutorials/en/modules.md rename to data/tutorials/en/007_modules.md diff --git a/data/tutorials/en/labels.md b/data/tutorials/en/008_labels.md similarity index 100% rename from data/tutorials/en/labels.md rename to data/tutorials/en/008_labels.md diff --git a/data/tutorials/en/pointers.md b/data/tutorials/en/009_pointers.md similarity index 100% rename from data/tutorials/en/pointers.md rename to data/tutorials/en/009_pointers.md diff --git a/data/tutorials/en/null_pointers_and_warnings.md b/data/tutorials/en/010_null_pointers_and_warnings.md similarity index 100% rename from data/tutorials/en/null_pointers_and_warnings.md rename to data/tutorials/en/010_null_pointers_and_warnings.md diff --git a/data/tutorials/en/functors.md b/data/tutorials/en/011_functors.md similarity index 100% rename from data/tutorials/en/functors.md rename to data/tutorials/en/011_functors.md diff --git a/data/tutorials/en/objects.md b/data/tutorials/en/012_objects.md similarity index 100% rename from data/tutorials/en/objects.md rename to data/tutorials/en/012_objects.md diff --git a/dune b/dune index 9721a35..48ae4ee 100644 --- a/dune +++ b/dune @@ -6,3 +6,5 @@ (with-stdout-to %{targets} (run %{bin:ood-cli} config)))) + +(data_only_dirs node_modules) diff --git a/dune-project b/dune-project index 1fbe977..90f1864 100644 --- a/dune-project +++ b/dune-project @@ -36,11 +36,33 @@ yaml ppx_import ppx_deriving_yaml - netlify-cms fmt cmdliner bos - alcotest)) + alcotest + (netlify-cms + (= :version)) + (ood + (= :version)))) + +(package + (name ood-preview) + (synopsis "Preview ood content") + (description "Preview ood content") + (depends + (ocaml + (>= 4.08.0)) + dune + crunch + dream + dream-cli + dream-livereload + yaml + omd + (ood + (= :version)) + (alcotest :with-test) + (odoc :with-doc))) (package (name netlify-cms) diff --git a/ood-cli.opam b/ood-cli.opam index a962940..bdf3264 100644 --- a/ood-cli.opam +++ b/ood-cli.opam @@ -15,11 +15,12 @@ depends: [ "yaml" "ppx_import" "ppx_deriving_yaml" - "netlify-cms" "fmt" "cmdliner" "bos" "alcotest" + "netlify-cms" {= version} + "ood" {= version} "odoc" {with-doc} ] build: [ diff --git a/ood-preview.opam b/ood-preview.opam new file mode 100644 index 0000000..956dbda --- /dev/null +++ b/ood-preview.opam @@ -0,0 +1,42 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "Preview ood content" +description: "Preview ood content" +maintainer: ["pf341@patricoferris.com"] +authors: ["Patrick Ferris"] +license: "ISC" +homepage: "https://github.com/patricoferris/ood" +bug-reports: "https://github.com/patricoferris/ood/issues" +depends: [ + "ocaml" {>= "4.08.0"} + "dune" {>= "2.8"} + "crunch" + "dream" + "dream-cli" + "dream-livereload" + "yaml" + "omd" + "ood" {= version} + "alcotest" {with-test} + "odoc" {with-doc} +] +dev-repo: "git+https://github.com/patricoferris/ood.git" +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "--ignore-promoted-rules" + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +pin-depends: [ + ["dream-cli.~dev" "git+https://github.com/tmattio/dream-cli.git"] + ["dream-livereload.~dev" "git+https://github.com/tmattio/dream-livereload.git"] +] diff --git a/ood-preview.opam.template b/ood-preview.opam.template new file mode 100644 index 0000000..4c4e51d --- /dev/null +++ b/ood-preview.opam.template @@ -0,0 +1,19 @@ +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "--ignore-promoted-rules" + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +pin-depends: [ + ["dream-cli.~dev" "git+https://github.com/tmattio/dream-cli.git"] + ["dream-livereload.~dev" "git+https://github.com/tmattio/dream-livereload.git"] +] diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8ff61c7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1079 @@ +{ + "name": "ood", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@fullhuman/postcss-purgecss": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz", + "integrity": "sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA==", + "dev": true, + "requires": { + "purgecss": "^3.1.3" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, + "@tailwindcss/aspect-ratio": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.2.1.tgz", + "integrity": "sha512-aDFi80aHQ3JM3symJ5iKU70lm151ugIGFCI0yRZGpyjgQSDS+Fbe93QwypC1tCEllQE8p0S7TUu20ih1b9IKLA==", + "dev": true + }, + "@tailwindcss/forms": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.3.2.tgz", + "integrity": "sha512-aj2/rJsGb2whAZ/BQWHWWQRSbhH0r/l1ozOByiv+ZNjBD84GMvb5dhAyfpeasFky+EJrAwX5eaqft8NQMZFWvA==", + "dev": true, + "requires": { + "mini-svg-data-uri": "^1.2.3" + } + }, + "@tailwindcss/typography": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.4.1.tgz", + "integrity": "sha512-ovPPLUhs7zAIJfr0y1dbGlyCuPhpuv/jpBoFgqAc658DWGGrOBWBMpAWLw2KlzbNeVk4YBJMzue1ekvIbdw6XA==", + "dev": true, + "requires": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "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" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "autoprefixer": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.6.tgz", + "integrity": "sha512-8lChSmdU6dCNMCQopIf4Pe5kipkAGj/fvTMslCsih0uHpOrXOPUEVOmYMMqmw3cekQkSD7EhIeuYl5y0BLdKqg==", + "dev": true, + "requires": { + "browserslist": "^4.16.6", + "caniuse-lite": "^1.0.30001230", + "colorette": "^1.2.2", + "fraction.js": "^4.1.1", + "normalize-range": "^0.1.2", + "postcss-value-parser": "^4.1.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "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 + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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.16.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz", + "integrity": "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001219", + "colorette": "^1.2.2", + "electron-to-chromium": "^1.3.723", + "escalade": "^3.1.1", + "node-releases": "^1.1.71" + } + }, + "bs-platform": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-9.0.2.tgz", + "integrity": "sha512-Ye9JqJ4Oa7mcjjoOVRYI8Uc2Cf8N7jQLWDcdUplY7996d/YErSR7WitmV7XnSwr4EvdrbwjEsg1NxNjUQv3ChA==" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001231", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001231.tgz", + "integrity": "sha512-WAFFv31GgU4DiwNAy77qMo3nNyycEhH3ikcCVHvkQpPe/fO8Tb2aRYzss8kgyLQBm8mJ7OryW4X6Y4vsBCIqag==", + "dev": true + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz", + "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.4" + }, + "dependencies": { + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + } + } + }, + "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 + }, + "color-string": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz", + "integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz", + "integrity": "sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==", + "dev": true + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "didyoumean": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz", + "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=", + "dev": true + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.742", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz", + "integrity": "sha512-ihL14knI9FikJmH2XUIDdZFWJxvr14rPSdOhJ7PpS27xbz8qmaRwCwyg/bmFwjWKmWK9QyamiCZVCvXm5CH//Q==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fastq": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "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" + } + }, + "fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==", + "dev": true + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "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 + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "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" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "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-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "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" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", + "dev": true + }, + "lodash.topath": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", + "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "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.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "mini-svg-data-uri": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.3.3.tgz", + "integrity": "sha512-+fA2oRcR1dJI/7ITmeQJDrYWks0wodlOz0pAEhKYJ2IVc1z0AnwJUsKY2fzFmPAM3Jo9J0rBx8JAA9QQSJ5PuA==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "modern-normalize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/modern-normalize/-/modern-normalize-1.1.0.tgz", + "integrity": "sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA==", + "dev": true + }, + "nanoid": { + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "dev": true + }, + "node-emoji": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "dev": true, + "requires": { + "lodash.toarray": "^4.4.0" + } + }, + "node-releases": { + "version": "1.1.72", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz", + "integrity": "sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==", + "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": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "postcss": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.0.tgz", + "integrity": "sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ==", + "dev": true, + "requires": { + "colorette": "^1.2.2", + "nanoid": "^3.1.23", + "source-map-js": "^0.6.2" + } + }, + "postcss-functions": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-functions/-/postcss-functions-3.0.0.tgz", + "integrity": "sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=", + "dev": true, + "requires": { + "glob": "^7.1.2", + "object-assign": "^4.1.1", + "postcss": "^6.0.9", + "postcss-value-parser": "^3.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-js": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-3.0.3.tgz", + "integrity": "sha512-gWnoWQXKFw65Hk/mi2+WTQTHdPD5UJdDXZmX073EY/B3BWnYjO4F4t0VneTCnCGQ5E5GsCdMkzPaTXwl3r5dJw==", + "dev": true, + "requires": { + "camelcase-css": "^2.0.1", + "postcss": "^8.1.6" + } + }, + "postcss-nested": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.5.tgz", + "integrity": "sha512-GSRXYz5bccobpTzLQZXOnSOfKl6TwVr5CyAQJUPub4nuRJSOECK5AqurxVgmtxP48p0Kc/ndY/YyS1yqldX0Ew==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-selector-parser": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz", + "integrity": "sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "purgecss": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-3.1.3.tgz", + "integrity": "sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ==", + "dev": true, + "requires": { + "commander": "^6.0.0", + "glob": "^7.0.0", + "postcss": "^8.2.1", + "postcss-selector-parser": "^6.0.2" + } + }, + "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 + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dev": true, + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + } + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "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" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", + "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tailwindcss": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-2.1.2.tgz", + "integrity": "sha512-T5t+wwd+/hsOyRw2HJuFuv0LTUm3MUdHm2DJ94GPVgzqwPPFa9XxX0KlwLWupUuiOUj6uiKURCzYPHFcuPch/w==", + "dev": true, + "requires": { + "@fullhuman/postcss-purgecss": "^3.1.3", + "bytes": "^3.0.0", + "chalk": "^4.1.0", + "chokidar": "^3.5.1", + "color": "^3.1.3", + "detective": "^5.2.0", + "didyoumean": "^1.2.1", + "dlv": "^1.1.3", + "fast-glob": "^3.2.5", + "fs-extra": "^9.1.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.21", + "lodash.topath": "^4.5.2", + "modern-normalize": "^1.0.0", + "node-emoji": "^1.8.1", + "normalize-path": "^3.0.0", + "object-hash": "^2.1.1", + "parse-glob": "^3.0.4", + "postcss-functions": "^3", + "postcss-js": "^3.0.3", + "postcss-nested": "5.0.5", + "postcss-selector-parser": "^6.0.4", + "postcss-value-parser": "^4.1.0", + "pretty-hrtime": "^1.0.3", + "quick-lru": "^5.1.1", + "reduce-css-calc": "^2.1.8", + "resolve": "^1.20.0" + } + }, + "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" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + } + } +} diff --git a/package.json b/package.json index b9a29e1..35e398b 100644 --- a/package.json +++ b/package.json @@ -13,5 +13,12 @@ }, "dependencies": { "bs-platform": "9.0.2" + }, + "devDependencies": { + "@tailwindcss/aspect-ratio": "^0.2.0", + "@tailwindcss/forms": "^0.3.2", + "@tailwindcss/typography": "^0.4.0", + "autoprefixer": "^10.2.5", + "tailwindcss": "^2.1.2" } } diff --git a/src/ood-cli/data.ml b/src/ood-cli/data.ml index e631e1f..d239fc5 100644 --- a/src/ood-cli/data.ml +++ b/src/ood-cli/data.ml @@ -232,7 +232,7 @@ end module Book = struct type t = [%import: Ood.Book.t] [@@deriving yaml] - let path = "data/books" + let path = "data/books/en" let widget_of_t = Widget. diff --git a/src/ood-cli/dune b/src/ood-cli/dune index b3ade89..6acd5c9 100644 --- a/src/ood-cli/dune +++ b/src/ood-cli/dune @@ -20,7 +20,7 @@ (package ood-cli) (deps main.exe - (source_tree ../../data)) + (source_tree %{workspace_root}/data)) (action (chdir %{workspace_root} diff --git a/src/ood-preview/.ocamlformat b/src/ood-preview/.ocamlformat new file mode 100644 index 0000000..651c8fd --- /dev/null +++ b/src/ood-preview/.ocamlformat @@ -0,0 +1,18 @@ +profile = sparse +break-cases = nested +break-fun-decl = smart +cases-exp-indent = 2 +if-then-else = fit-or-vertical +parens-tuple = multi-line-only +parens-tuple-patterns = multi-line-only +parens-ite = false +infix-precedence = parens +break-infix-before-func = false +sequence-style = terminator +sequence-blank-line = compact +indicate-multiline-delimiters = no +ocp-indent-compat = true +wrap-comments = true +parse-docstrings = true +let-binding-spacing = compact +type-decl = sparse \ No newline at end of file diff --git a/src/ood-preview/asset/default.png b/src/ood-preview/asset/default.png new file mode 100644 index 0000000..1421ff9 Binary files /dev/null and b/src/ood-preview/asset/default.png differ diff --git a/src/ood-preview/asset/dune b/src/ood-preview/asset/dune new file mode 100644 index 0000000..5b4cfad --- /dev/null +++ b/src/ood-preview/asset/dune @@ -0,0 +1,23 @@ +(rule + (target main.css) + (package ood-preview) + (deps + (:data ../config/tailwind.config.js) + (source_tree ../node_modules) + (source_tree ../lib/ood_preview_web/templates)) + (mode + (promote (until-clean))) + (action + (chdir + %{workspace_root} + ; We use node_modules/tailwindcss/lib/cli.js instead of `npx tailwindcss` + ; because dune won't include node_modules/.bin in the build, no matter + ; what I do. + (run + node + node_modules/tailwindcss/lib/cli.js + build + -c + src/ood-preview/config/tailwind.config.js + -o + %{target})))) diff --git a/src/ood-preview/asset/favicon.ico b/src/ood-preview/asset/favicon.ico new file mode 100644 index 0000000..a856608 Binary files /dev/null and b/src/ood-preview/asset/favicon.ico differ diff --git a/src/ood-preview/asset/logo1.jpeg b/src/ood-preview/asset/logo1.jpeg new file mode 100644 index 0000000..e3db945 Binary files /dev/null and b/src/ood-preview/asset/logo1.jpeg differ diff --git a/src/ood-preview/asset/main.css b/src/ood-preview/asset/main.css new file mode 100644 index 0000000..a9fe625 --- /dev/null +++ b/src/ood-preview/asset/main.css @@ -0,0 +1,2017 @@ +/*! tailwindcss v2.1.2 | MIT License | https://tailwindcss.com *//*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ + +/* +Document +======== +*/ + +/** +Use a better box model (opinionated). +*/ + +*, +::before, +::after { + box-sizing: border-box; +} + +/** +Use a more readable tab size (opinionated). +*/ + +html { + -moz-tab-size: 4; + tab-size: 4; +} + +/** +1. Correct the line height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +*/ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* +Sections +======== +*/ + +/** +Remove the margin in all browsers. +*/ + +body { + margin: 0; +} + +/** +Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +*/ + +body { + font-family: + system-ui, + -apple-system, /* Firefox supports this but not yet `system-ui` */ + 'Segoe UI', + Roboto, + Helvetica, + Arial, + sans-serif, + 'Apple Color Emoji', + 'Segoe UI Emoji'; +} + +/* +Grouping content +================ +*/ + +/** +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +*/ + +hr { + height: 0; /* 1 */ + color: inherit; /* 2 */ +} + +/* +Text-level semantics +==================== +*/ + +/** +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr[title] { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/** +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/** +1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) +2. Correct the odd 'em' font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: + ui-monospace, + SFMono-Regular, + Consolas, + 'Liberation Mono', + Menlo, + monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/** +Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +Tabular data +============ +*/ + +/** +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +*/ + +table { + text-indent: 0; /* 1 */ + border-color: inherit; /* 2 */ +} + +/* +Forms +===== +*/ + +/** +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** +Remove the inheritance of text transform in Edge and Firefox. +1. Remove the inheritance of text transform in Firefox. +*/ + +button, +select { /* 1 */ + text-transform: none; +} + +/** +Correct the inability to style clickable types in iOS and Safari. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; +} + +/** +Remove the inner border and padding in Firefox. +*/ + +::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** +Restore the focus styles unset by the previous rule. +*/ + +:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** +Remove the additional ':invalid' styles in Firefox. +See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737 +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/** +Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. +*/ + +legend { + padding: 0; +} + +/** +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/** +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/** +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to 'inherit' in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* +Interactive +=========== +*/ + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +}/** + * Manually forked from SUIT CSS Base: https://github.com/suitcss/base + * A thin layer on top of normalize.css that provides a starting point more + * suitable for web applications. + */ + +/** + * Removes the default spacing and border for appropriate elements. + */ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +button { + background-color: transparent; + background-image: none; +} + +/** + * Work around a Firefox/IE bug where the transparent `button` background + * results in a loss of the default `button` focus styles. + */ + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +fieldset { + margin: 0; + padding: 0; +} + +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} + +/** + * Tailwind custom reset styles + */ + +/** + * 1. Use the user's configured `sans` font-family (with Tailwind's default + * sans-serif font stack as a fallback) as a sane default. + * 2. Use Tailwind's default "normal" line-height so the user isn't forced + * to override it to ensure consistency even when using the default theme. + */ + +html { + font-family: Inter var, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 1 */ + line-height: 1.5; /* 2 */ +} + + +/** + * Inherit font-family and line-height from `html` so users can set them as + * a class directly on the `html` element. + */ + +body { + font-family: inherit; + line-height: inherit; +} + +/** + * 1. Prevent padding and border from affecting element width. + * + * We used to set this in the html element and inherit from + * the parent element for everything else. This caused issues + * in shadow-dom-enhanced elements like
where the content + * is wrapped by a div with box-sizing set to `content-box`. + * + * https://github.com/mozdevs/cssremedy/issues/4 + * + * + * 2. Allow adding a border to an element by just adding a border-width. + * + * By default, the way the browser specifies that an element should have no + * border is by setting it's border-style to `none` in the user-agent + * stylesheet. + * + * In order to easily add borders to elements by just setting the `border-width` + * property, we change the default border-style for all elements to `solid`, and + * use border-width to hide them instead. This way our `border` utilities only + * need to set the `border-width` property instead of the entire `border` + * shorthand, making our border utilities much more straightforward to compose. + * + * https://github.com/tailwindcss/tailwindcss/pull/116 + */ + +*, +::before, +::after { + box-sizing: border-box; /* 1 */ + border-width: 0; /* 2 */ + border-style: solid; /* 2 */ + border-color: #e5e7eb; /* 2 */ +} + +/* + * Ensure horizontal rules are visible by default + */ + +hr { + border-top-width: 1px; +} + +/** + * Undo the `border-style: none` reset that Normalize applies to images so that + * our `border-{width}` utilities have the expected effect. + * + * The Normalize reset is unnecessary for us since we default the border-width + * to 0 on all elements. + * + * https://github.com/tailwindcss/tailwindcss/issues/362 + */ + +img { + border-style: solid; +} + +textarea { + resize: vertical; +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + color: #9ca3af; +} + +button, +[role="button"] { + cursor: pointer; +} + +table { + border-collapse: collapse; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/** + * Reset links to optimize for opt-in styling instead of + * opt-out. + */ + +a { + color: inherit; + text-decoration: inherit; +} + +/** + * Reset form element properties that are easy to forget to + * style explicitly so you don't inadvertently introduce + * styles that deviate from your design system. These styles + * supplement a partial reset that is already applied by + * normalize.css. + */ + +button, +input, +optgroup, +select, +textarea { + padding: 0; + line-height: inherit; + color: inherit; +} + +/** + * Use the configured 'mono' font family for elements that + * are expected to be rendered with a monospace font, falling + * back to the system monospace stack if there is no configured + * 'mono' font family. + */ + +pre, +code, +kbd, +samp { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +/** + * Make replaced elements `display: block` by default as that's + * the behavior you want almost all of the time. Inspired by + * CSS Remedy, with `svg` added as well. + * + * https://github.com/mozdevs/cssremedy/issues/14 + */ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + vertical-align: middle; +} + +/** + * Constrain images and videos to the parent width and preserve + * their intrinsic aspect ratio. + * + * https://github.com/mozdevs/cssremedy/issues/14 + */ + +img, +video { + max-width: 100%; + height: auto; +} + +* { + --tw-shadow: 0 0 #0000; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; +} + +[type='text'],[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; +} + +[type='text']:focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + border-color: #2563eb; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + color-adjust: exact; +} + +[multiple] { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px auto -webkit-focus-ring-color; +} + .prose { + color: #374151; + max-width: 65ch; +} + .prose [class~="lead"] { + color: #4b5563; + font-size: 1.25em; + line-height: 1.6; + margin-top: 1.2em; + margin-bottom: 1.2em; +} + .prose a { + color: #111827; + text-decoration: underline; + font-weight: 500; +} + .prose strong { + color: #111827; + font-weight: 600; +} + .prose ol[type="A"] { + --list-counter-style: upper-alpha; +} + .prose ol[type="a"] { + --list-counter-style: lower-alpha; +} + .prose ol[type="A" s] { + --list-counter-style: upper-alpha; +} + .prose ol[type="a" s] { + --list-counter-style: lower-alpha; +} + .prose ol[type="I"] { + --list-counter-style: upper-roman; +} + .prose ol[type="i"] { + --list-counter-style: lower-roman; +} + .prose ol[type="I" s] { + --list-counter-style: upper-roman; +} + .prose ol[type="i" s] { + --list-counter-style: lower-roman; +} + .prose ol[type="1"] { + --list-counter-style: decimal; +} + .prose ol > li { + position: relative; + padding-left: 1.75em; +} + .prose ol > li::before { + content: counter(list-item, var(--list-counter-style, decimal)) "."; + position: absolute; + font-weight: 400; + color: #6b7280; + left: 0; +} + .prose ul > li { + position: relative; + padding-left: 1.75em; +} + .prose ul > li::before { + content: ""; + position: absolute; + background-color: #d1d5db; + border-radius: 50%; + width: 0.375em; + height: 0.375em; + top: calc(0.875em - 0.1875em); + left: 0.25em; +} + .prose hr { + border-color: #e5e7eb; + border-top-width: 1px; + margin-top: 3em; + margin-bottom: 3em; +} + .prose blockquote { + font-weight: 500; + font-style: italic; + color: #111827; + border-left-width: 0.25rem; + border-left-color: #e5e7eb; + quotes: "\201C""\201D""\2018""\2019"; + margin-top: 1.6em; + margin-bottom: 1.6em; + padding-left: 1em; +} + .prose blockquote p:first-of-type::before { + content: open-quote; +} + .prose blockquote p:last-of-type::after { + content: close-quote; +} + .prose h1 { + color: #111827; + font-weight: 800; + font-size: 2.25em; + margin-top: 0; + margin-bottom: 0.8888889em; + line-height: 1.1111111; +} + .prose h2 { + color: #111827; + font-weight: 700; + font-size: 1.5em; + margin-top: 2em; + margin-bottom: 1em; + line-height: 1.3333333; +} + .prose h3 { + color: #111827; + font-weight: 600; + font-size: 1.25em; + margin-top: 1.6em; + margin-bottom: 0.6em; + line-height: 1.6; +} + .prose h4 { + color: #111827; + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; + line-height: 1.5; +} + .prose figure figcaption { + color: #6b7280; + font-size: 0.875em; + line-height: 1.4285714; + margin-top: 0.8571429em; +} + .prose code { + color: #111827; + font-weight: 600; + font-size: 0.875em; +} + .prose code::before { + content: "`"; +} + .prose code::after { + content: "`"; +} + .prose a code { + color: #111827; +} + .prose pre { + color: #e5e7eb; + background-color: #1f2937; + overflow-x: auto; + font-size: 0.875em; + line-height: 1.7142857; + margin-top: 1.7142857em; + margin-bottom: 1.7142857em; + border-radius: 0.375rem; + padding-top: 0.8571429em; + padding-right: 1.1428571em; + padding-bottom: 0.8571429em; + padding-left: 1.1428571em; +} + .prose pre code { + background-color: transparent; + border-width: 0; + border-radius: 0; + padding: 0; + font-weight: 400; + color: inherit; + font-size: inherit; + font-family: inherit; + line-height: inherit; +} + .prose pre code::before { + content: none; +} + .prose pre code::after { + content: none; +} + .prose table { + width: 100%; + table-layout: auto; + text-align: left; + margin-top: 2em; + margin-bottom: 2em; + font-size: 0.875em; + line-height: 1.7142857; +} + .prose thead { + color: #111827; + font-weight: 600; + border-bottom-width: 1px; + border-bottom-color: #d1d5db; +} + .prose thead th { + vertical-align: bottom; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} + .prose tbody tr { + border-bottom-width: 1px; + border-bottom-color: #e5e7eb; +} + .prose tbody tr:last-child { + border-bottom-width: 0; +} + .prose tbody td { + vertical-align: top; + padding-top: 0.5714286em; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} + .prose { + font-size: 1rem; + line-height: 1.75; +} + .prose p { + margin-top: 1.25em; + margin-bottom: 1.25em; +} + .prose img { + margin-top: 2em; + margin-bottom: 2em; +} + .prose video { + margin-top: 2em; + margin-bottom: 2em; +} + .prose figure { + margin-top: 2em; + margin-bottom: 2em; +} + .prose figure > * { + margin-top: 0; + margin-bottom: 0; +} + .prose h2 code { + font-size: 0.875em; +} + .prose h3 code { + font-size: 0.9em; +} + .prose ol { + margin-top: 1.25em; + margin-bottom: 1.25em; +} + .prose ul { + margin-top: 1.25em; + margin-bottom: 1.25em; +} + .prose li { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + .prose > ul > li p { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + .prose > ul > li > *:first-child { + margin-top: 1.25em; +} + .prose > ul > li > *:last-child { + margin-bottom: 1.25em; +} + .prose > ol > li > *:first-child { + margin-top: 1.25em; +} + .prose > ol > li > *:last-child { + margin-bottom: 1.25em; +} + .prose ul ul, .prose ul ol, .prose ol ul, .prose ol ol { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + .prose hr + * { + margin-top: 0; +} + .prose h2 + * { + margin-top: 0; +} + .prose h3 + * { + margin-top: 0; +} + .prose h4 + * { + margin-top: 0; +} + .prose thead th:first-child { + padding-left: 0; +} + .prose thead th:last-child { + padding-right: 0; +} + .prose tbody td:first-child { + padding-left: 0; +} + .prose tbody td:last-child { + padding-right: 0; +} + .prose > :first-child { + margin-top: 0; +} + .prose > :last-child { + margin-bottom: 0; +} + .prose-lg { + font-size: 1.125rem; + line-height: 1.7777778; +} + .prose-lg p { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; +} + .prose-lg [class~="lead"] { + font-size: 1.2222222em; + line-height: 1.4545455; + margin-top: 1.0909091em; + margin-bottom: 1.0909091em; +} + .prose-lg blockquote { + margin-top: 1.6666667em; + margin-bottom: 1.6666667em; + padding-left: 1em; +} + .prose-lg h1 { + font-size: 2.6666667em; + margin-top: 0; + margin-bottom: 0.8333333em; + line-height: 1; +} + .prose-lg h2 { + font-size: 1.6666667em; + margin-top: 1.8666667em; + margin-bottom: 1.0666667em; + line-height: 1.3333333; +} + .prose-lg h3 { + font-size: 1.3333333em; + margin-top: 1.6666667em; + margin-bottom: 0.6666667em; + line-height: 1.5; +} + .prose-lg h4 { + margin-top: 1.7777778em; + margin-bottom: 0.4444444em; + line-height: 1.5555556; +} + .prose-lg img { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; +} + .prose-lg video { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; +} + .prose-lg figure { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; +} + .prose-lg figure > * { + margin-top: 0; + margin-bottom: 0; +} + .prose-lg figure figcaption { + font-size: 0.8888889em; + line-height: 1.5; + margin-top: 1em; +} + .prose-lg code { + font-size: 0.8888889em; +} + .prose-lg h2 code { + font-size: 0.8666667em; +} + .prose-lg h3 code { + font-size: 0.875em; +} + .prose-lg pre { + font-size: 0.8888889em; + line-height: 1.75; + margin-top: 2em; + margin-bottom: 2em; + border-radius: 0.375rem; + padding-top: 1em; + padding-right: 1.5em; + padding-bottom: 1em; + padding-left: 1.5em; +} + .prose-lg ol { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; +} + .prose-lg ul { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; +} + .prose-lg li { + margin-top: 0.6666667em; + margin-bottom: 0.6666667em; +} + .prose-lg ol > li { + padding-left: 1.6666667em; +} + .prose-lg ol > li::before { + left: 0; +} + .prose-lg ul > li { + padding-left: 1.6666667em; +} + .prose-lg ul > li::before { + width: 0.3333333em; + height: 0.3333333em; + top: calc(0.8888889em - 0.1666667em); + left: 0.2222222em; +} + .prose-lg > ul > li p { + margin-top: 0.8888889em; + margin-bottom: 0.8888889em; +} + .prose-lg > ul > li > *:first-child { + margin-top: 1.3333333em; +} + .prose-lg > ul > li > *:last-child { + margin-bottom: 1.3333333em; +} + .prose-lg > ol > li > *:first-child { + margin-top: 1.3333333em; +} + .prose-lg > ol > li > *:last-child { + margin-bottom: 1.3333333em; +} + .prose-lg ul ul, .prose-lg ul ol, .prose-lg ol ul, .prose-lg ol ol { + margin-top: 0.8888889em; + margin-bottom: 0.8888889em; +} + .prose-lg hr { + margin-top: 3.1111111em; + margin-bottom: 3.1111111em; +} + .prose-lg hr + * { + margin-top: 0; +} + .prose-lg h2 + * { + margin-top: 0; +} + .prose-lg h3 + * { + margin-top: 0; +} + .prose-lg h4 + * { + margin-top: 0; +} + .prose-lg table { + font-size: 0.8888889em; + line-height: 1.5; +} + .prose-lg thead th { + padding-right: 0.75em; + padding-bottom: 0.75em; + padding-left: 0.75em; +} + .prose-lg thead th:first-child { + padding-left: 0; +} + .prose-lg thead th:last-child { + padding-right: 0; +} + .prose-lg tbody td { + padding-top: 0.75em; + padding-right: 0.75em; + padding-bottom: 0.75em; + padding-left: 0.75em; +} + .prose-lg tbody td:first-child { + padding-left: 0; +} + .prose-lg tbody td:last-child { + padding-right: 0; +} + .prose-lg > :first-child { + margin-top: 0; +} + .prose-lg > :last-child { + margin-bottom: 0; +} + .prose-indigo a { + color: #4f46e5; +} + .prose-indigo a code { + color: #4f46e5; +} + .aspect-w-1, +.aspect-w-2, +.aspect-w-3, +.aspect-w-4, +.aspect-w-5, +.aspect-w-6, +.aspect-w-7, +.aspect-w-8, +.aspect-w-9, +.aspect-w-10, +.aspect-w-11, +.aspect-w-12, +.aspect-w-13, +.aspect-w-14, +.aspect-w-15, +.aspect-w-16 { + position: relative; + padding-bottom: calc(var(--tw-aspect-h) / var(--tw-aspect-w) * 100%); +} + .aspect-w-1 > *, +.aspect-w-2 > *, +.aspect-w-3 > *, +.aspect-w-4 > *, +.aspect-w-5 > *, +.aspect-w-6 > *, +.aspect-w-7 > *, +.aspect-w-8 > *, +.aspect-w-9 > *, +.aspect-w-10 > *, +.aspect-w-11 > *, +.aspect-w-12 > *, +.aspect-w-13 > *, +.aspect-w-14 > *, +.aspect-w-15 > *, +.aspect-w-16 > * { + position: absolute; + height: 100%; + width: 100%; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + .aspect-w-3 { + --tw-aspect-w: 3; +} + .aspect-h-2 { + --tw-aspect-h: 2; +} + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + .pointer-events-none { + pointer-events: none; +} + .relative { + position: relative; +} + .absolute { + position: absolute; +} + .inset-x-0 { + left: 0px; + right: 0px; +} + .left-1\/2 { + left: 50%; +} + .top-0 { + top: 0px; +} + .z-10 { + z-index: 10; +} + .-m-3 { + margin: -0.75rem; +} + .mx-auto { + margin-left: auto; + margin-right: auto; +} + .-my-2 { + margin-top: -0.5rem; + margin-bottom: -0.5rem; +} + .mt-2 { + margin-top: 0.5rem; +} + .mt-6 { + margin-top: 1.5rem; +} + .mt-5 { + margin-top: 1.25rem; +} + .mt-1 { + margin-top: 0.25rem; +} + .ml-2 { + margin-left: 0.5rem; +} + .mt-3 { + margin-top: 0.75rem; +} + .-mr-2 { + margin-right: -0.5rem; +} + .ml-4 { + margin-left: 1rem; +} + .mt-8 { + margin-top: 2rem; +} + .block { + display: block; +} + .inline-block { + display: inline-block; +} + .flex { + display: flex; +} + .inline-flex { + display: inline-flex; +} + .table { + display: table; +} + .grid { + display: grid; +} + .hidden { + display: none; +} + .h-5 { + height: 1.25rem; +} + .h-8 { + height: 2rem; +} + .h-6 { + height: 1.5rem; +} + .h-10 { + height: 2.5rem; +} + .w-5 { + width: 1.25rem; +} + .w-screen { + width: 100vw; +} + .w-auto { + width: auto; +} + .w-6 { + width: 1.5rem; +} + .w-10 { + width: 2.5rem; +} + .min-w-full { + min-width: 100%; +} + .max-w-prose { + max-width: 65ch; +} + .max-w-md { + max-width: 28rem; +} + .max-w-xl { + max-width: 36rem; +} + .max-w-7xl { + max-width: 80rem; +} + .max-w-xs { + max-width: 20rem; +} + .max-w-3xl { + max-width: 48rem; +} + .max-w-2xl { + max-width: 42rem; +} + .max-w-full { + max-width: 100%; +} + .flex-shrink-0 { + flex-shrink: 0; +} + .transform { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + transform: translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .origin-top-right { + transform-origin: top right; +} + .translate-y-1 { + --tw-translate-y: 0.25rem; +} + .translate-y-0 { + --tw-translate-y: 0px; +} + .-translate-x-1\/2 { + --tw-translate-x: -50%; +} + .scale-95 { + --tw-scale-x: .95; + --tw-scale-y: .95; +} + .scale-100 { + --tw-scale-x: 1; + --tw-scale-y: 1; +} + .grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + .grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + .flex-col { + flex-direction: column; +} + .items-center { + align-items: center; +} + .justify-between { + justify-content: space-between; +} + .justify-center { + justify-content: center; +} + .gap-6 { + gap: 1.5rem; +} + .gap-x-4 { + column-gap: 1rem; +} + .gap-y-8 { + row-gap: 2rem; +} + .space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + .space-x-10 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(2.5rem * var(--tw-space-x-reverse)); + margin-left: calc(2.5rem * calc(1 - var(--tw-space-x-reverse))); +} + .space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + .divide-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(2px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(2px * var(--tw-divide-y-reverse)); +} + .divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} + .divide-gray-50 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgba(249, 250, 251, var(--tw-divide-opacity)); +} + .divide-gray-200 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-divide-opacity)); +} + .overflow-hidden { + overflow: hidden; +} + .overflow-x-auto { + overflow-x: auto; +} + .truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .rounded-lg { + border-radius: 0.5rem; +} + .rounded-md { + border-radius: 0.375rem; +} + .rounded-full { + border-radius: 9999px; +} + .border-b { + border-bottom-width: 1px; +} + .border-t { + border-top-width: 1px; +} + .border-gray-200 { + --tw-border-opacity: 1; + border-color: rgba(229, 231, 235, var(--tw-border-opacity)); +} + .bg-white { + --tw-bg-opacity: 1; + background-color: rgba(255, 255, 255, var(--tw-bg-opacity)); +} + .bg-indigo-500 { + --tw-bg-opacity: 1; + background-color: rgba(99, 102, 241, var(--tw-bg-opacity)); +} + .bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgba(229, 231, 235, var(--tw-bg-opacity)); +} + .bg-graylight { + --tw-bg-opacity: 1; + background-color: rgba(245, 245, 245, var(--tw-bg-opacity)); +} + .bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgba(209, 250, 229, var(--tw-bg-opacity)); +} + .bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgba(249, 250, 251, var(--tw-bg-opacity)); +} + .bg-black { + --tw-bg-opacity: 1; + background-color: rgba(0, 0, 0, var(--tw-bg-opacity)); +} + .object-cover { + object-fit: cover; +} + .p-3 { + padding: 0.75rem; +} + .p-2 { + padding: 0.5rem; +} + .px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + .py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} + .py-16 { + padding-top: 4rem; + padding-bottom: 4rem; +} + .px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + .px-5 { + padding-left: 1.25rem; + padding-right: 1.25rem; +} + .py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} + .px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + .py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + .py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + .py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + .px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + .py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} + .pt-5 { + padding-top: 1.25rem; +} + .pb-6 { + padding-bottom: 1.5rem; +} + .pb-8 { + padding-bottom: 2rem; +} + .text-center { + text-align: center; +} + .text-left { + text-align: left; +} + .align-middle { + vertical-align: middle; +} + .text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + .text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + .text-base { + font-size: 1rem; + line-height: 1.5rem; +} + .text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + .text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + .text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + .text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + .font-extrabold { + font-weight: 800; +} + .font-semibold { + font-weight: 600; +} + .font-medium { + font-weight: 500; +} + .uppercase { + text-transform: uppercase; +} + .leading-8 { + line-height: 2rem; +} + .leading-5 { + line-height: 1.25rem; +} + .leading-6 { + line-height: 1.5rem; +} + .tracking-tight { + letter-spacing: -0.025em; +} + .tracking-wide { + letter-spacing: 0.025em; +} + .tracking-wider { + letter-spacing: 0.05em; +} + .text-gray-900 { + --tw-text-opacity: 1; + color: rgba(17, 24, 39, var(--tw-text-opacity)); +} + .text-indigo-600 { + --tw-text-opacity: 1; + color: rgba(79, 70, 229, var(--tw-text-opacity)); +} + .text-gray-500 { + --tw-text-opacity: 1; + color: rgba(107, 114, 128, var(--tw-text-opacity)); +} + .text-gray-400 { + --tw-text-opacity: 1; + color: rgba(156, 163, 175, var(--tw-text-opacity)); +} + .text-gray-600 { + --tw-text-opacity: 1; + color: rgba(75, 85, 99, var(--tw-text-opacity)); +} + .text-white { + --tw-text-opacity: 1; + color: rgba(255, 255, 255, var(--tw-text-opacity)); +} + .text-green-800 { + --tw-text-opacity: 1; + color: rgba(6, 95, 70, var(--tw-text-opacity)); +} + .antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + .opacity-0 { + opacity: 0; +} + .opacity-100 { + opacity: 1; +} + .shadow-lg { + --tw-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + .shadow { + --tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + .ring-1 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + .ring-black { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(0, 0, 0, var(--tw-ring-opacity)); +} + .ring-opacity-5 { + --tw-ring-opacity: 0.05; +} + .transition { + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + .duration-200 { + transition-duration: 200ms; +} + .duration-150 { + transition-duration: 150ms; +} + .duration-100 { + transition-duration: 100ms; +} + .ease-out { + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +} + .ease-in { + transition-timing-function: cubic-bezier(0.4, 0, 1, 1); +} + .hover\:bg-gray-50:hover { + --tw-bg-opacity: 1; + background-color: rgba(249, 250, 251, var(--tw-bg-opacity)); +} + .hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgba(243, 244, 246, var(--tw-bg-opacity)); +} + .hover\:text-gray-900:hover { + --tw-text-opacity: 1; + color: rgba(17, 24, 39, var(--tw-text-opacity)); +} + .hover\:text-gray-500:hover { + --tw-text-opacity: 1; + color: rgba(107, 114, 128, var(--tw-text-opacity)); +} + .focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + .focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + .focus\:ring-inset:focus { + --tw-ring-inset: inset; +} + .focus\:ring-indigo-500:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgba(99, 102, 241, var(--tw-ring-opacity)); +} + .focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; +} + .group:hover .group-hover\:text-gray-500 { + --tw-text-opacity: 1; + color: rgba(107, 114, 128, var(--tw-text-opacity)); +} + @media (min-width: 640px) { + + .sm\:aspect-w-1, +.sm\:aspect-w-2, +.sm\:aspect-w-3, +.sm\:aspect-w-4, +.sm\:aspect-w-5, +.sm\:aspect-w-6, +.sm\:aspect-w-7, +.sm\:aspect-w-8, +.sm\:aspect-w-9, +.sm\:aspect-w-10, +.sm\:aspect-w-11, +.sm\:aspect-w-12, +.sm\:aspect-w-13, +.sm\:aspect-w-14, +.sm\:aspect-w-15, +.sm\:aspect-w-16 { + position: relative; + padding-bottom: calc(var(--tw-aspect-h) / var(--tw-aspect-w) * 100%); + } + + .sm\:aspect-w-1 > *, +.sm\:aspect-w-2 > *, +.sm\:aspect-w-3 > *, +.sm\:aspect-w-4 > *, +.sm\:aspect-w-5 > *, +.sm\:aspect-w-6 > *, +.sm\:aspect-w-7 > *, +.sm\:aspect-w-8 > *, +.sm\:aspect-w-9 > *, +.sm\:aspect-w-10 > *, +.sm\:aspect-w-11 > *, +.sm\:aspect-w-12 > *, +.sm\:aspect-w-13 > *, +.sm\:aspect-w-14 > *, +.sm\:aspect-w-15 > *, +.sm\:aspect-w-16 > * { + position: absolute; + height: 100%; + width: 100%; + top: 0; + right: 0; + bottom: 0; + left: 0; + } + + .sm\:aspect-w-3 { + --tw-aspect-w: 3; + } + + .sm\:aspect-h-4 { + --tw-aspect-h: 4; + } + + .sm\:col-span-4 { + grid-column: span 4 / span 4; + } + + .sm\:-mx-6 { + margin-left: -1.5rem; + margin-right: -1.5rem; + } + + .sm\:grid { + display: grid; + } + + .sm\:h-10 { + height: 2.5rem; + } + + .sm\:max-w-3xl { + max-width: 48rem; + } + + .sm\:grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .sm\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .sm\:items-start { + align-items: flex-start; + } + + .sm\:gap-6 { + gap: 1.5rem; + } + + .sm\:gap-8 { + gap: 2rem; + } + + .sm\:gap-x-6 { + column-gap: 1.5rem; + } + + .sm\:space-y-0 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0px * var(--tw-space-y-reverse)); + } + + .sm\:rounded-lg { + border-radius: 0.5rem; + } + + .sm\:p-8 { + padding: 2rem; + } + + .sm\:px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; + } + + .sm\:py-24 { + padding-top: 6rem; + padding-bottom: 6rem; + } + + .sm\:px-0 { + padding-left: 0px; + padding-right: 0px; + } + + .sm\:text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; + } + + .sm\:text-5xl { + font-size: 3rem; + line-height: 1; + } + + .sm\:tracking-tight { + letter-spacing: -0.025em; + } +} + @media (min-width: 768px) { + + .md\:flex { + display: flex; + } + + .md\:hidden { + display: none; + } + + .md\:flex-1 { + flex: 1 1 0%; + } + + .md\:items-center { + align-items: center; + } + + .md\:justify-start { + justify-content: flex-start; + } + + .md\:justify-between { + justify-content: space-between; + } + + .md\:space-x-10 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(2.5rem * var(--tw-space-x-reverse)); + margin-left: calc(2.5rem * calc(1 - var(--tw-space-x-reverse))); + } +} + @media (min-width: 1024px) { + + .lg\:col-span-1 { + grid-column: span 1 / span 1; + } + + .lg\:col-span-3 { + grid-column: span 3 / span 3; + } + + .lg\:col-start-2 { + grid-column-start: 2; + } + + .lg\:-mx-8 { + margin-left: -2rem; + margin-right: -2rem; + } + + .lg\:max-w-7xl { + max-width: 80rem; + } + + .lg\:grid-flow-col-dense { + grid-auto-flow: column dense; + } + + .lg\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:px-8 { + padding-left: 2rem; + padding-right: 2rem; + } + + .lg\:py-32 { + padding-top: 8rem; + padding-bottom: 8rem; + } + + .lg\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } +} + @media (min-width: 1280px) { + + .xl\:gap-x-8 { + column-gap: 2rem; + } +} + \ No newline at end of file diff --git a/src/ood-preview/asset/manifest.json b/src/ood-preview/asset/manifest.json new file mode 100644 index 0000000..ddeb963 --- /dev/null +++ b/src/ood-preview/asset/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "Ood preview", + "name": "Ood preview", + "icons": [ + { + "src": "favicon.ico", + "sizes": "128x128 64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": "/", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} \ No newline at end of file diff --git a/src/ood-preview/asset/robots.txt b/src/ood-preview/asset/robots.txt new file mode 100644 index 0000000..f89c874 --- /dev/null +++ b/src/ood-preview/asset/robots.txt @@ -0,0 +1,2 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * \ No newline at end of file diff --git a/src/ood-preview/bin/dune b/src/ood-preview/bin/dune new file mode 100644 index 0000000..652d562 --- /dev/null +++ b/src/ood-preview/bin/dune @@ -0,0 +1,4 @@ +(executable + (name server) + (modules server) + (libraries ood_preview_web)) diff --git a/src/ood-preview/bin/server.ml b/src/ood-preview/bin/server.ml new file mode 100644 index 0000000..2544b7b --- /dev/null +++ b/src/ood-preview/bin/server.ml @@ -0,0 +1,3 @@ +(** Main entry point for our application. *) + +let () = Ood_preview_web.run () diff --git a/src/ood-preview/bin/server.mli b/src/ood-preview/bin/server.mli new file mode 100644 index 0000000..85f012e --- /dev/null +++ b/src/ood-preview/bin/server.mli @@ -0,0 +1 @@ +(** Main entry point for our application. *) diff --git a/src/ood-preview/config/tailwind.config.js b/src/ood-preview/config/tailwind.config.js new file mode 100644 index 0000000..77c1dc3 --- /dev/null +++ b/src/ood-preview/config/tailwind.config.js @@ -0,0 +1,29 @@ +const defaultTheme = require("tailwindcss/defaultTheme"); + +module.exports = { + mode: "jit", + purge: ["**/*.eml"], + darkMode: false, // or 'media' or 'class' + theme: { + extend: { + colors: { + 'ocamlorange': '#c15540', + graylight: '#f5f5f5', + orangedark: '#ed7109', + orangedarker: '#dd6705', + yellowdark: '#ffb800', + }, + fontFamily: { + sans: ["Inter var", ...defaultTheme.fontFamily.sans], + }, + }, + }, + variants: { + extend: {}, + }, + plugins: [ + require("@tailwindcss/forms"), + require("@tailwindcss/typography"), + require("@tailwindcss/aspect-ratio"), + ], +}; \ No newline at end of file diff --git a/src/ood-preview/dune b/src/ood-preview/dune new file mode 100644 index 0000000..e61a3dd --- /dev/null +++ b/src/ood-preview/dune @@ -0,0 +1,8 @@ +(env + (dev + (flags + (:standard -w +A-48-42-44 -warn-error +A-3)))) + +; Do not include node_modules in the build + +(data_only_dirs node_modules) diff --git a/src/ood-preview/lib/ood_preview/book.ml b/src/ood-preview/lib/ood_preview/book.ml new file mode 100644 index 0000000..9b1cee4 --- /dev/null +++ b/src/ood-preview/lib/ood_preview/book.ml @@ -0,0 +1,42 @@ +type metadata = Ood.Book.t = + { title : string + ; description : string + ; authors : string list + ; language : string + ; published : string option + ; cover : string option + ; isbn : string option + } +[@@deriving yaml] + +type t = + { title : string + ; description : string + ; authors : string list + ; language : string + ; published : string option + ; cover : string option + ; isbn : string option + ; body : string + } + +let all () = + Utils.map_files + (fun content -> + let metadata, body = Utils.extract_metadata_body content in + let metadata = Utils.decode_or_raise metadata_of_yaml metadata in + let body = Omd.of_string body |> Omd.to_html in + { title = metadata.title + ; description = metadata.description + ; authors = metadata.authors + ; language = metadata.language + ; published = metadata.published + ; cover = metadata.cover + ; isbn = metadata.isbn + ; body + }) + "books/en" + +let slug (t : t) = Utils.slugify t.title + +let get_by_slug id = all () |> List.find_opt (fun book -> slug book = id) diff --git a/src/ood-preview/lib/ood_preview/dune b/src/ood-preview/lib/ood_preview/dune new file mode 100644 index 0000000..8408bcb --- /dev/null +++ b/src/ood-preview/lib/ood_preview/dune @@ -0,0 +1,14 @@ +(library + (name ood_preview) + (libraries ood omd yaml) + (preprocess + (pps ppx_deriving_yaml))) + +(rule + (targets data.ml) + (deps + (source_tree %{workspace_root}/data/)) + (action + (with-stdout-to + %{null} + (run %{bin:ocaml-crunch} -m plain %{workspace_root}/data -o %{targets})))) diff --git a/src/ood-preview/lib/ood_preview/event.ml b/src/ood-preview/lib/ood_preview/event.ml new file mode 100644 index 0000000..e0553cd --- /dev/null +++ b/src/ood-preview/lib/ood_preview/event.ml @@ -0,0 +1,27 @@ +type t = Ood.Events.Event.t = + { title : string + ; description : string + ; url : string + ; date : string + ; tags : string list + ; online : bool + ; textual_location : string option + ; location : string option + } +[@@deriving yaml] + +let decode s = + let yaml = Utils.decode_or_raise Yaml.of_string s in + match yaml with + | `O [ ("events", `A xs) ] -> + List.map (Utils.decode_or_raise of_yaml) xs + | _ -> + raise (Exn.Decode_error "expected a list of events") + +let all () = + let content = Data.read "events.yml" |> Option.get in + decode content + +let slug (t : t) = Utils.slugify t.title + +let get_by_slug id = all () |> List.find_opt (fun event -> slug event = id) diff --git a/src/ood-preview/lib/ood_preview/exn.ml b/src/ood-preview/lib/ood_preview/exn.ml new file mode 100644 index 0000000..14941af --- /dev/null +++ b/src/ood-preview/lib/ood_preview/exn.ml @@ -0,0 +1 @@ +exception Decode_error of string diff --git a/src/ood-preview/lib/ood_preview/import.ml b/src/ood-preview/lib/ood_preview/import.ml new file mode 100644 index 0000000..97a6c87 --- /dev/null +++ b/src/ood-preview/lib/ood_preview/import.ml @@ -0,0 +1,237 @@ +module Result = struct + include Stdlib.Result + + let both a b = + match a with + | Error e -> + Error e + | Ok a -> + (match b with Error e -> Error e | Ok b -> Ok (a, b)) + + module Syntax = struct + let ( >>= ) t f = bind t f + + let ( >>| ) t f = map f t + + let ( let* ) = ( >>= ) + + let ( let+ ) = ( >>| ) + + let ( and+ ) = both + end + + open Syntax + + module List = struct + let map f t = + let rec loop acc = function + | [] -> + Ok (List.rev acc) + | x :: xs -> + f x >>= fun x -> loop (x :: acc) xs + in + loop [] t + + let all = + let rec loop acc = function + | [] -> + Ok (List.rev acc) + | t :: l -> + t >>= fun x -> loop (x :: acc) l + in + fun l -> loop [] l + + let concat_map = + let rec loop f acc = function + | [] -> + Ok (List.rev acc) + | x :: l -> + f x >>= fun y -> loop f (List.rev_append y acc) l + in + fun l f -> loop f [] l + + let rec iter f t = + match t with [] -> Ok () | x :: xs -> f x >>= fun () -> iter f xs + + let rec fold_left f init t = + match t with + | [] -> + Ok init + | x :: xs -> + f init x >>= fun init -> fold_left f init xs + + let rec iter_left f t = + match t with [] -> Ok () | x :: xs -> f x >>= fun () -> iter_left f xs + + let filter_map t f = + fold_left + (fun acc x -> f x >>| function None -> acc | Some y -> y :: acc) + [] + t + >>| List.rev + end +end + +module String = struct + include Stdlib.String + + let lsplit2_exn on s = + let i = index s on in + sub s 0 i, sub s (i + 1) (length s - i - 1) + + let lsplit2 on s = try Some (lsplit2_exn s on) with Not_found -> None + + let prefix s len = try sub s 0 len with Invalid_argument _ -> "" + + let suffix s len = + try sub s (length s - len) len with Invalid_argument _ -> "" + + let drop_prefix s len = sub s len (length s - len) + + let drop_suffix s len = sub s 0 (length s - len) + + (* ripped off stringext, itself ripping it off from one of dbuenzli's libs *) + let cut s ~on = + let sep_max = length on - 1 in + if sep_max < 0 then + invalid_arg "Stringext.cut: empty separator" + else + let s_max = length s - 1 in + if s_max < 0 then + None + else + let k = ref 0 in + let i = ref 0 in + (* We run from the start of [s] to end with [i] trying to match the + first character of [on] in [s]. If this matches, we verify that the + whole [on] is matched using [k]. If it doesn't match we continue to + look for [on] with [i]. If it matches we exit the loop and extract a + substring from the start of [s] to the position before the [on] we + found and another from the position after the [on] we found to end of + string. If [i] is such that no separator can be found we exit the + loop and return the no match case. *) + try + while !i + sep_max <= s_max do + (* Check remaining [on] chars match, access to unsafe s (!i + !k) is + guaranteed by loop invariant. *) + if unsafe_get s !i <> unsafe_get on 0 then + incr i + else ( + k := 1; + while + !k <= sep_max && unsafe_get s (!i + !k) = unsafe_get on !k + do + incr k + done; + if !k <= sep_max then (* no match *) incr i else raise Exit) + done; + None (* no match in the whole string. *) + with + | Exit -> + (* i is at the beginning of the separator *) + let left_end = !i - 1 in + let right_start = !i + sep_max + 1 in + Some + (sub s 0 (left_end + 1), sub s right_start (s_max - right_start + 1)) + + let rcut s ~on = + let sep_max = length on - 1 in + if sep_max < 0 then + invalid_arg "Stringext.rcut: empty separator" + else + let s_max = length s - 1 in + if s_max < 0 then + None + else + let k = ref 0 in + let i = ref s_max in + (* We run from the end of [s] to the beginning with [i] trying to match + the last character of [on] in [s]. If this matches, we verify that + the whole [on] is matched using [k] (we do that backwards). If it + doesn't match we continue to look for [on] with [i]. If it matches we + exit the loop and extract a substring from the start of [s] to the + position before the [on] we found and another from the position after + the [on] we found to end of string. If [i] is such that no separator + can be found we exit the loop and return the no match case. *) + try + while !i >= sep_max do + if unsafe_get s !i <> unsafe_get on sep_max then + decr i + else + (* Check remaining [on] chars match, access to unsafe_get s + (sep_start + !k) is guaranteed by loop invariant. *) + let sep_start = !i - sep_max in + k := sep_max - 1; + while + !k >= 0 && unsafe_get s (sep_start + !k) = unsafe_get on !k + do + decr k + done; + if !k >= 0 then (* no match *) decr i else raise Exit + done; + None (* no match in the whole string. *) + with + | Exit -> + (* i is at the end of the separator *) + let left_end = !i - sep_max - 1 in + let right_start = !i + 1 in + Some + (sub s 0 (left_end + 1), sub s right_start (s_max - right_start + 1)) +end + +module Glob = struct + (* From https://github.com/simonjbeaumont/ocaml-glob *) + + let split c s = + let len = String.length s in + let rec loop acc last_pos pos = + if pos = -1 then + String.sub s 0 last_pos :: acc + else if s.[pos] = c then + let pos1 = pos + 1 in + let sub_str = String.sub s pos1 (last_pos - pos1) in + loop (sub_str :: acc) pos (pos - 1) + else + loop acc last_pos (pos - 1) + in + loop [] len (len - 1) + + (** Returns list of indices of occurances of substr in x *) + let find_substrings ?(start_point = 0) substr x = + let len_s = String.length substr + and len_x = String.length x in + let rec aux acc i = + if len_x - i < len_s then + acc + else if String.sub x i len_s = substr then + aux (i :: acc) (i + 1) + else + aux acc (i + 1) + in + aux [] start_point + + let matches_glob ~glob x = + let rec contains_all_sections = function + | _, [] | _, [ "" ] -> + true + | i, [ g ] -> + (* need to find a match that matches to end of string *) + find_substrings ~start_point:i g x + |> List.exists (fun j -> j + String.length g = String.length x) + | 0, "" :: g :: gs -> + find_substrings g x + |> List.exists (fun j -> + contains_all_sections (j + String.length g, gs)) + | i, g :: gs -> + find_substrings ~start_point:i g x + |> List.exists (fun j -> + (if i = 0 then j = 0 else true) + && contains_all_sections (j + String.length g, gs)) + in + contains_all_sections (0, split '*' glob) + + let matches_globs ~globs x = + List.exists (fun glob -> matches_glob ~glob x) globs + + let filter_files ~globs files = List.filter (matches_globs ~globs) files +end diff --git a/src/ood-preview/lib/ood_preview/media.ml b/src/ood-preview/lib/ood_preview/media.ml new file mode 100644 index 0000000..0fbdc99 --- /dev/null +++ b/src/ood-preview/lib/ood_preview/media.ml @@ -0,0 +1 @@ +let read s = Data.read (Filename.concat "media" s) diff --git a/src/ood-preview/lib/ood_preview/ood_preview.ml b/src/ood-preview/lib/ood_preview/ood_preview.ml new file mode 100644 index 0000000..138cffd --- /dev/null +++ b/src/ood-preview/lib/ood_preview/ood_preview.ml @@ -0,0 +1,8 @@ +module Exn = Exn +module Media = Media +module Book = Book +module Paper = Paper +module Event = Event +module Video = Video +module Success_story = Success_story +module Tutorial = Tutorial diff --git a/src/ood-preview/lib/ood_preview/paper.ml b/src/ood-preview/lib/ood_preview/paper.ml new file mode 100644 index 0000000..3b02bf8 --- /dev/null +++ b/src/ood-preview/lib/ood_preview/paper.ml @@ -0,0 +1,29 @@ +type t = Ood.Papers.Paper.t = + { title : string + ; publication : string + ; authors : string list + ; abstract : string + ; tags : string list + ; year : int + ; links : string list + } +[@@deriving yaml] + +let decode_or_raise f x = + match f x with Ok x -> x | Error (`Msg err) -> raise (Exn.Decode_error err) + +let decode s = + let yaml = decode_or_raise Yaml.of_string s in + match yaml with + | `O [ ("papers", `A xs) ] -> + List.map (decode_or_raise of_yaml) xs + | _ -> + raise (Exn.Decode_error "expected a list of papers") + +let all () = + let content = Data.read "papers.yml" |> Option.get in + decode content + +let slug (t : t) = Utils.slugify t.title + +let get_by_slug id = all () |> List.find_opt (fun paper -> slug paper = id) diff --git a/src/ood-preview/lib/ood_preview/success_story.ml b/src/ood-preview/lib/ood_preview/success_story.ml new file mode 100644 index 0000000..a504427 --- /dev/null +++ b/src/ood-preview/lib/ood_preview/success_story.ml @@ -0,0 +1,31 @@ +type metadata = Ood.Success_story.t = + { title : string + ; image : string option + ; url : string option + } +[@@deriving yaml] + +type t = + { title : string + ; image : string option + ; url : string option + ; body : string + } + +let all () = + Utils.map_files + (fun content -> + let metadata, body = Utils.extract_metadata_body content in + let metadata = Utils.decode_or_raise metadata_of_yaml metadata in + let body = Omd.of_string body |> Omd.to_html in + { title = metadata.title + ; image = metadata.image + ; url = metadata.url + ; body + }) + "success_stories/en" + +let slug (t : t) = Utils.slugify t.title + +let get_by_slug id = + all () |> List.find_opt (fun success_story -> slug success_story = id) diff --git a/src/ood-preview/lib/ood_preview/tutorial.ml b/src/ood-preview/lib/ood_preview/tutorial.ml new file mode 100644 index 0000000..c22bcdf --- /dev/null +++ b/src/ood-preview/lib/ood_preview/tutorial.ml @@ -0,0 +1,54 @@ +type proficiency = + [ `Beginner + | `Intermediate + | `Advanced + ] + +type metadata = + { title : string + ; description : string + ; date : string + ; tags : string list + ; users : string list + } +[@@deriving yaml] + +type t = + { title : string + ; description : string + ; date : string + ; tags : string list + ; users : proficiency list + ; body : string + } + +let proficiency_list_of_string_list = + List.map (fun x -> + match Ood.Meta.Proficiency.of_string x with + | Ok x -> + x + | Error (`Msg err) -> + raise (Exn.Decode_error err)) + +let all () = + Utils.map_files + (fun content -> + let metadata, body = Utils.extract_metadata_body content in + let metadata = Utils.decode_or_raise metadata_of_yaml metadata in + let body = Omd.of_string body |> Omd.to_html in + { title = metadata.title + ; description = metadata.description + ; date = metadata.date + ; tags = metadata.tags + ; users = proficiency_list_of_string_list metadata.users + ; body + }) + "tutorials/en/*.md" + +let slug (t : t) = Utils.slugify t.title + +let get_by_slug id = + all () |> List.find_opt (fun success_story -> slug success_story = id) + +let filter_by_tag ~tag tutorials = + List.filter (fun (x : t) -> List.mem tag x.tags) tutorials diff --git a/src/ood-preview/lib/ood_preview/utils.ml b/src/ood-preview/lib/ood_preview/utils.ml new file mode 100644 index 0000000..f39774d --- /dev/null +++ b/src/ood-preview/lib/ood_preview/utils.ml @@ -0,0 +1,43 @@ +open Import + +let extract_metadata_body s = + let sep = "---\n" in + let win_sep = "---\r\n" in + match String.cut ~on:sep s, String.cut ~on:win_sep s with + | None, None -> + raise (Exn.Decode_error "expected metadata at the top of the file") + | Some (pre, post), _ | _, Some (pre, post) -> + if String.length pre = 0 then + match String.cut ~on:sep post, String.cut ~on:win_sep post with + | None, None -> + raise (Exn.Decode_error "expected metadata at the top of the file") + | Some (yaml, body), _ | _, Some (yaml, body) -> + (match Yaml.of_string yaml with + | Ok yaml -> + yaml, body + | Error (`Msg err) -> + raise (Exn.Decode_error err)) + else + raise (Exn.Decode_error "expected metadata at the top of the file") + +let decode_or_raise f x = + match f x with Ok x -> x | Error (`Msg err) -> raise (Exn.Decode_error err) + +let read_from_dir dir = + let len = String.length dir in + Data.file_list + |> List.filter_map (fun x -> + if String.prefix x len = dir then + Data.read x + else if Glob.matches_glob ~glob:dir x then + Data.read x + else + None) + +let map_files f dir = read_from_dir dir |> List.map f + +let slugify value = + value + |> Str.global_replace (Str.regexp " ") "-" + |> String.lowercase_ascii + |> Str.global_replace (Str.regexp "[^a-z0-9\\-]") "" diff --git a/src/ood-preview/lib/ood_preview/video.ml b/src/ood-preview/lib/ood_preview/video.ml new file mode 100644 index 0000000..ff717e9 --- /dev/null +++ b/src/ood-preview/lib/ood_preview/video.ml @@ -0,0 +1,69 @@ +[@@@ocaml.warning "-66"] + +type metadata = + { title : string + ; description : string + ; people : string list + ; kind : string + ; tags : string list + ; paper : string option + ; link : string + ; embed : string option + ; year : int + } +[@@deriving yaml] + +type kind = + [ `Conference + | `Mooc + | `Lecture + ] + +type t = Ood.Videos.Video.t = + { title : string + ; description : string + ; people : string list + ; kind : kind + ; tags : string list + ; paper : string option + ; link : string + ; embed : string option + ; year : int + } + +let decode s = + let yaml = Utils.decode_or_raise Yaml.of_string s in + match yaml with + | `O [ ("videos", `A xs) ] -> + List.map + (fun x -> + let (metadata : metadata) = Utils.decode_or_raise metadata_of_yaml x in + let kind = + match Ood.Videos.Video.kind_of_string metadata.kind with + | Ok x -> + x + | Error (`Msg err) -> + raise (Exn.Decode_error err) + in + ({ title = metadata.title + ; description = metadata.description + ; people = metadata.people + ; kind + ; tags = metadata.tags + ; paper = metadata.paper + ; link = metadata.link + ; embed = metadata.embed + ; year = metadata.year + } + : t)) + xs + | _ -> + raise (Exn.Decode_error "expected a list of videos") + +let all () = + let content = Data.read "videos.yml" |> Option.get in + decode content + +let slug (t : t) = Utils.slugify t.title + +let get_by_slug id = all () |> List.find_opt (fun video -> slug video = id) diff --git a/src/ood-preview/lib/ood_preview_web/dune b/src/ood-preview/lib/ood_preview_web/dune new file mode 100644 index 0000000..8df85c1 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/dune @@ -0,0 +1,35 @@ +(library + (name ood_preview_web) + (libraries ood_preview dream dream-cli dream-livereload)) + +(rule + (targets asset.ml) + (deps + ../../asset/main.css + (source_tree ../../asset)) + (action + (with-stdout-to + %{null} + (run + %{bin:ocaml-crunch} + -e + ico + -e + jpeg + -e + png + -e + js + -e + svg + -e + txt + -e + css + -m + plain + ../../asset + -o + %{targets})))) + +(include_subdirs unqualified) diff --git a/src/ood-preview/lib/ood_preview_web/handlers/page_handler.ml b/src/ood-preview/lib/ood_preview_web/handlers/page_handler.ml new file mode 100644 index 0000000..4f1e514 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/handlers/page_handler.ml @@ -0,0 +1,47 @@ +let index _req = + Layout_template.render ~title:"Welcome to Dream!" Index_template.render + |> Dream.html + +let papers _req = + let papers = Ood_preview.Paper.all () in + Layout_template.render ~title:"Papers" (Papers_template.render papers) + |> Dream.html + +let success_stories _req = + let success_stories = Ood_preview.Success_story.all () in + Layout_template.render + ~title:"Success Stories" + (Success_stories_template.render success_stories) + |> Dream.html + +let books _req = + let books = Ood_preview.Book.all () in + Layout_template.render ~title:"Books" (Books_template.render books) + |> Dream.html + +let events _req = + let events = Ood_preview.Event.all () in + Layout_template.render ~title:"Events" (Events_template.render events) + |> Dream.html + +let videos _req = + let videos = Ood_preview.Video.all () in + Layout_template.render ~title:"Videos" (Videos_template.render videos) + |> Dream.html + +let tutorial req = + let slug = Dream.param "id" req in + let tutorials = Ood_preview.Tutorial.all () in + match Ood_preview.Tutorial.get_by_slug slug with + | Some tutorial -> + Layout_template.render + ~title:tutorial.Ood_preview.Tutorial.title + (Tutorial_template.render tutorials tutorial) + |> Dream.html + | None -> + Dream.not_found req + +let tutorials req = + let first = Ood_preview.Tutorial.all () |> List.hd in + let slug = Ood_preview.Tutorial.slug first in + Dream.redirect req ("/tutorials/" ^ slug) diff --git a/src/ood-preview/lib/ood_preview_web/navigation.ml b/src/ood-preview/lib/ood_preview_web/navigation.ml new file mode 100644 index 0000000..b241450 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/navigation.ml @@ -0,0 +1,30 @@ +type menu_item = + { label : string + ; url : string + ; icon : string + ; text : string + } + +type menu = + { header : string + ; entries : menu_item list + } + +type t = menu list + +let t = + [ { header = "Content" + ; entries = + [ { label = "Papers Archive"; url = "/papers"; icon = ""; text = "" } + ; { label = "Success Stories" + ; url = "/success-stories" + ; icon = "" + ; text = "" + } + ; { label = "Books"; url = "/books"; icon = ""; text = "" } + ; { label = "Events"; url = "/events"; icon = ""; text = "" } + ; { label = "Videos"; url = "/videos"; icon = ""; text = "" } + ; { label = "Tutorials"; url = "/tutorials"; icon = ""; text = "" } + ] + } + ] diff --git a/src/ood-preview/lib/ood_preview_web/ood_preview_web.ml b/src/ood-preview/lib/ood_preview_web/ood_preview_web.ml new file mode 100644 index 0000000..601436b --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/ood_preview_web.ml @@ -0,0 +1,11 @@ +module Handlers = struct + module Page = Page_handler +end + +let run () = + Dream_cli.run ~debug:true + @@ Dream.logger + @@ Dream_livereload.inject_script () + @@ Router.router + @@ Dream_livereload.router + @@ Dream.not_found diff --git a/src/ood-preview/lib/ood_preview_web/router.ml b/src/ood-preview/lib/ood_preview_web/router.ml new file mode 100644 index 0000000..0056ac0 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/router.ml @@ -0,0 +1,28 @@ +let loader _root path _request = + match Asset.read path with + | None -> + Dream.empty `Not_Found + | Some asset -> + Dream.respond asset + +let media_loader _root path _request = + match Ood_preview.Media.read path with + | None -> + Dream.empty `Not_Found + | Some asset -> + Dream.respond asset + +let routes = + [ Dream.get "/" Page_handler.index + ; Dream.get "/papers" Page_handler.papers + ; Dream.get "/success-stories" Page_handler.success_stories + ; Dream.get "/books" Page_handler.books + ; Dream.get "/events" Page_handler.events + ; Dream.get "/videos" Page_handler.videos + ; Dream.get "/tutorials" Page_handler.tutorials + ; Dream.get "/tutorials/:id" Page_handler.tutorial + ; Dream.get "/assets/**" (Dream.static ~loader "") + ; Dream.get "/media/**" (Dream.static ~loader:media_loader "") + ] + +let router = Dream.router routes diff --git a/src/ood-preview/lib/ood_preview_web/templates/books_template.eml b/src/ood-preview/lib/ood_preview_web/templates/books_template.eml new file mode 100644 index 0000000..71a7dcb --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/books_template.eml @@ -0,0 +1,33 @@ +let render_book (book : Ood_preview.Book.t) = + let open Ood_preview.Book in + let src = Option.map (fun x -> "/media/" ^ x) book.cover |> Option.value ~default:"/assets/default.png" in +
+
+ +
+
+
+
+

+ + <%s book.title %> + +

+

+ <%s book.authors |> String.concat ", " %> +

+
+
+ <%s! book.body %> +
+
+
+
+ +let render books = + <%s! Header_section_template.render "Books" %> +
+
+ <%s! List.map render_book books |> String.concat "" %> +
+
diff --git a/src/ood-preview/lib/ood_preview_web/templates/dune b/src/ood-preview/lib/ood_preview_web/templates/dune new file mode 100644 index 0000000..52d0516 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/dune @@ -0,0 +1,59 @@ +(rule + (targets books_template.ml) + (deps books_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets header_section_template.ml) + (deps header_section_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets header_template.ml) + (deps header_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets index_template.ml) + (deps index_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets layout_template.ml) + (deps layout_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets papers_template.ml) + (deps papers_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets success_stories_template.ml) + (deps success_stories_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets events_template.ml) + (deps events_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets tutorial_template.ml) + (deps tutorial_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) + +(rule + (targets videos_template.ml) + (deps videos_template.eml) + (action + (run %{bin:dream_eml} %{deps} --workspace %{workspace_root}))) diff --git a/src/ood-preview/lib/ood_preview_web/templates/events_template.eml b/src/ood-preview/lib/ood_preview_web/templates/events_template.eml new file mode 100644 index 0000000..c02fa58 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/events_template.eml @@ -0,0 +1,23 @@ +let render_event (event : Ood_preview.Event.t) = + let open Ood_preview.Event in +
+
+

+ + <%s event.title %> + +

+
+
+ <%s! event.description %> +
+
+ +let render events = + <%s! Header_section_template.render "Events" %> +
+
+ <%s! List.map render_event events |> String.concat "" %> +
+
diff --git a/src/ood-preview/lib/ood_preview_web/templates/header_section_template.eml b/src/ood-preview/lib/ood_preview_web/templates/header_section_template.eml new file mode 100644 index 0000000..ce6a7ca --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/header_section_template.eml @@ -0,0 +1,24 @@ +let render_subtitle = function +| Some x -> +

+ <%s x %> +

+| None -> "" + +let render_tag = function +| Some x -> +

+ <%s x %> +

+| None -> "" + +let render ?tag ?subtitle title = +
+
+ <%s! render_tag tag %> +

+ <%s! title %> +

+ <%s! render_subtitle subtitle %> +
+
\ No newline at end of file diff --git a/src/ood-preview/lib/ood_preview_web/templates/header_template.eml b/src/ood-preview/lib/ood_preview_web/templates/header_template.eml new file mode 100644 index 0000000..c1ba812 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/header_template.eml @@ -0,0 +1,139 @@ +let render_menu_item menu_item = + let open Navigation in + class="-m-3 p-3 block rounded-md hover:bg-gray-50"> +

+ <%s menu_item.label %> +

+

+ <%s menu_item.text %> +

+
+ +let render_menu menu = + let open Navigation in +
+ + + + +
+ +let render = +
+
+
+
+ + Workflow + + +
+
+ +
+ +
+ + +
+
\ No newline at end of file diff --git a/src/ood-preview/lib/ood_preview_web/templates/index_template.eml b/src/ood-preview/lib/ood_preview_web/templates/index_template.eml new file mode 100644 index 0000000..7e19e89 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/index_template.eml @@ -0,0 +1,3 @@ +let render = +
+
\ No newline at end of file diff --git a/src/ood-preview/lib/ood_preview_web/templates/layout_template.eml b/src/ood-preview/lib/ood_preview_web/templates/layout_template.eml new file mode 100644 index 0000000..8b87460 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/layout_template.eml @@ -0,0 +1,21 @@ +let render ~title inner = + + + + + + + + + + + <%s title %> + + + + <%s! Header_template.render %> +
+ <%s! inner %> +
+ + diff --git a/src/ood-preview/lib/ood_preview_web/templates/papers_template.eml b/src/ood-preview/lib/ood_preview_web/templates/papers_template.eml new file mode 100644 index 0000000..6063df9 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/papers_template.eml @@ -0,0 +1,68 @@ +[@@@ocaml.warning "-27"] + +let render_paper_col (paper : Ood_preview.Paper.t) = + let open Ood_preview.Paper in + + +
+ <%s paper.title %> +
+ + +
???
+ + + <%i paper.year %> + + + + ??? + + + + <%s paper.abstract %> + + + +let render papers = + <%s! Header_section_template.render ~subtitle:"A selection of OCaml papers through the ages. Filter by the tags or do a search over all of the text." "Papers Archive" %> +
+
+
+
+
+
+ + + + + + + + + + + + <%s! List.map render_paper_col papers |> String.concat "" %> + +
+ Title + + Authors + + Year + + Tags + + Description +
+
+
+
+
+
\ No newline at end of file diff --git a/src/ood-preview/lib/ood_preview_web/templates/success_stories_template.eml b/src/ood-preview/lib/ood_preview_web/templates/success_stories_template.eml new file mode 100644 index 0000000..6f5e066 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/success_stories_template.eml @@ -0,0 +1,21 @@ +let render_story (story : Ood_preview.Success_story.t) = + let open Ood_preview.Success_story in +
+
+

+ + <%s story.title %> + +

+
+
+ <%s! story.body %> +
+
+ +let render success_stories = + <%s! Header_section_template.render "Success Stories" %> +
+ <%s! List.map render_story success_stories |> String.concat "" %> +
diff --git a/src/ood-preview/lib/ood_preview_web/templates/tutorial_template.eml b/src/ood-preview/lib/ood_preview_web/templates/tutorial_template.eml new file mode 100644 index 0000000..3c5b30f --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/tutorial_template.eml @@ -0,0 +1,85 @@ +let render_tutorial_link (tutorial : Ood_preview.Tutorial.t) = + let open Ood_preview.Tutorial in + let slug = slug tutorial in + let href = "/tutorials/" ^ slug in + + class="group flex items-center px-3 py-2 text-sm font-medium text-gray-600 rounded-md hover:text-gray-900 hover:bg-gray-50"> + + <%s tutorial.title %> + + + +let render_nav tutorials = + let open Ood_preview.Tutorial in +
+ +
+ +let render tutorials (tutorial : Ood_preview.Tutorial.t) = + let open Ood_preview.Tutorial in + <%s! Header_section_template.render "Tutorials" %> +
+ <%s! render_nav tutorials %> + +
+
+
+

+ <%s tutorial.title %> +

+

+ <%s tutorial.description %> +

+
+
+
+ <%s! tutorial.body %> +
+
+
+
+
diff --git a/src/ood-preview/lib/ood_preview_web/templates/videos_template.eml b/src/ood-preview/lib/ood_preview_web/templates/videos_template.eml new file mode 100644 index 0000000..5fa5ca7 --- /dev/null +++ b/src/ood-preview/lib/ood_preview_web/templates/videos_template.eml @@ -0,0 +1,17 @@ +let render_video (video : Ood_preview.Video.t) = + let open Ood_preview.Video in +
  • + +

    <%s video.title %>

    +

    <%s video.description %>

    +
  • + +let render videos = + <%s! Header_section_template.render "Videos" %> +
    +
    +
      + <%s! List.map render_video videos |> String.concat "" %> +
    +
    +
    diff --git a/src/ood-preview/script/watch.sh b/src/ood-preview/script/watch.sh new file mode 100755 index 0000000..68ffb5c --- /dev/null +++ b/src/ood-preview/script/watch.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +source_dirs="lib bin asset config lib/ data/" +args=${*:-"bin/server.exe run"} +cmd="dune exec ${args}" + +which fswatch || (echo "you need fswatch to run in watch mode"; exit 1) + +function sigint_handler() { + kill "$(jobs -pr)" + exit 1 +} + +trap sigint_handler SIGINT + +while true; do + make build + $cmd & + fswatch -r -1 $source_dirs + printf "\nRestarting server.exe due to filesystem change\n" + kill "$(jobs -pr)" +done diff --git a/src/ood-preview/test/ood_preview_web/dune b/src/ood-preview/test/ood_preview_web/dune new file mode 100644 index 0000000..57a0535 --- /dev/null +++ b/src/ood-preview/test/ood_preview_web/dune @@ -0,0 +1,4 @@ +(test + (name page_handler_test) + (modules page_handler_test) + (libraries alcotest ood_preview_web)) diff --git a/src/ood-preview/test/ood_preview_web/page_handler_test.ml b/src/ood-preview/test/ood_preview_web/page_handler_test.ml new file mode 100644 index 0000000..0948f16 --- /dev/null +++ b/src/ood-preview/test/ood_preview_web/page_handler_test.ml @@ -0,0 +1,16 @@ +(** Test suite for the Utils module. *) + +let test_case n = Alcotest.test_case n `Quick + +let () = + Alcotest.run + "ood-preview" + [ ( "GET /" + , [ test_case "returns successfully" (fun () -> + let req = Dream.request ~method_:`GET "/" in + let res = Dream.test Ood_preview_web.Handlers.Page.index req in + Dream.status res + |> Dream.status_to_int + |> Alcotest.(check int) "is 200" 200) + ] ) + ]