From 1ba685b132ef77cd19686977b9e9eb35a96ee7b4 Mon Sep 17 00:00:00 2001 From: Davor Svetinovic Date: Tue, 26 Mar 2024 15:47:47 +0400 Subject: [PATCH] Initial commit --- .docker/Dockerfile | 30 ++ .docker/entrypoint.sh | 27 ++ .docker/run.sh | 37 ++ .github/DISCUSSION_TEMPLATE/general.yaml | 35 ++ .github/DISCUSSION_TEMPLATE/q-a.yaml | 35 ++ .github/ISSUE_TEMPLATE/config.yml | 8 + .github/ISSUE_TEMPLATE/issue.yaml | 38 ++ .github/pull_request_template.md | 13 + .github/user_pull_request_template.md | 4 + .github/workflows/build-preview.yaml | 58 +++ .github/workflows/build-site.yaml | 56 +++ .github/workflows/first-time-setup.yaml | 118 ++++++ .github/workflows/on-pages.yaml | 27 ++ .github/workflows/on-pull-request.yml | 22 ++ .github/workflows/on-push.yml | 24 ++ .github/workflows/on-schedule.yaml | 27 ++ .github/workflows/update-citations.yaml | 84 +++++ .github/workflows/update-url.yaml | 69 ++++ .github/workflows/versioning.yaml | 135 +++++++ .gitignore | 15 + 404.md | 11 + CHANGELOG.md | 146 ++++++++ CITATION.cff | 14 + Gemfile | 16 + Gemfile.lock | 157 ++++++++ LICENSE.md | 29 ++ README.md | 30 ++ _cite/.cache/cache.db | Bin 0 -> 49152 bytes _cite/cite.py | 188 ++++++++++ _cite/plugins/google-scholar.py | 61 ++++ _cite/plugins/orcid.py | 109 ++++++ _cite/plugins/pubmed.py | 46 +++ _cite/plugins/sources.py | 6 + _cite/requirements.txt | 7 + _cite/util.py | 234 ++++++++++++ _config.yaml | 73 ++++ _data/citations.yaml | 247 +++++++++++++ _data/orcid.yaml | 1 + _data/projects.yaml | 47 +++ _data/sources.yaml | 23 ++ _data/types.yaml | 216 +++++++++++ _includes/alert.html | 10 + _includes/analytics.html | 3 + _includes/button.html | 24 ++ _includes/card.html | 47 +++ _includes/citation.html | 109 ++++++ _includes/cols.html | 6 + _includes/content.html | 32 ++ _includes/fallback.html | 1 + _includes/feature.html | 27 ++ _includes/figure.html | 25 ++ _includes/float.html | 11 + _includes/fonts.html | 17 + _includes/footer.html | 42 +++ _includes/grid.html | 3 + _includes/head.html | 8 + _includes/header.html | 59 +++ _includes/icon.html | 10 + _includes/list.html | 58 +++ _includes/manubot.svg | 78 ++++ _includes/meta.html | 99 +++++ _includes/portrait.html | 45 +++ _includes/post-excerpt.html | 60 ++++ _includes/post-info.html | 37 ++ _includes/post-nav.html | 18 + _includes/scripts.html | 10 + _includes/search-box.html | 16 + _includes/search-info.html | 1 + _includes/section.html | 10 + _includes/site-search.html | 6 + _includes/styles.html | 28 ++ _includes/tags.html | 29 ++ _includes/verification.html | 3 + _layouts/default.html | 11 + _layouts/member.html | 51 +++ _layouts/post.html | 24 ++ _members/jane-smith.md | 19 + _members/john-doe.md | 10 + _members/sarah-johnson.md | 11 + _plugins/array.rb | 25 ++ _plugins/file.rb | 20 ++ _plugins/hash.rb | 28 ++ _plugins/misc.rb | 87 +++++ _plugins/regex.rb | 28 ++ _posts/2019-01-07-example-post-1.md | 10 + _posts/2021-09-30-example-post-2.md | 6 + _posts/2023-02-23-example-post-3.md | 8 + _scripts/anchors.js | 47 +++ _scripts/dark-mode.js | 25 ++ _scripts/fetch-tags.js | 67 ++++ _scripts/search.js | 215 +++++++++++ _scripts/site-search.js | 14 + _scripts/table-wrap.js | 25 ++ _scripts/tooltip.js | 41 +++ _styles/-theme.scss | 54 +++ _styles/alert.scss | 37 ++ _styles/all.scss | 11 + _styles/anchor.scss | 24 ++ _styles/background.scss | 21 ++ _styles/body.scss | 15 + _styles/bold.scss | 7 + _styles/button.scss | 51 +++ _styles/card.scss | 52 +++ _styles/checkbox.scss | 6 + _styles/citation.scss | 103 ++++++ _styles/code.scss | 38 ++ _styles/cols.scss | 39 ++ _styles/dark-toggle.scss | 31 ++ _styles/feature.scss | 53 +++ _styles/figure.scss | 26 ++ _styles/float.scss | 38 ++ _styles/font.scss | 5 + _styles/footer.scss | 25 ++ _styles/form.scss | 9 + _styles/grid.scss | 54 +++ _styles/header.scss | 166 +++++++++ _styles/heading.scss | 50 +++ _styles/highlight.scss | 7 + _styles/icon.scss | 16 + _styles/image.scss | 7 + _styles/link.scss | 16 + _styles/list.scss | 24 ++ _styles/main.scss | 8 + _styles/paragraph.scss | 8 + _styles/portrait.scss | 76 ++++ _styles/post-excerpt.scss | 69 ++++ _styles/post-info.scss | 33 ++ _styles/post-nav.scss | 39 ++ _styles/quote.scss | 16 + _styles/rule.scss | 9 + _styles/search-box.scss | 26 ++ _styles/search-info.scss | 9 + _styles/section.scss | 39 ++ _styles/table.scss | 18 + _styles/tags.scss | 34 ++ _styles/textbox.scss | 17 + _styles/tooltip.scss | 65 ++++ _styles/util.scss | 14 + blog/index.md | 21 ++ contact/index.md | 77 ++++ images/background.jpg | Bin 0 -> 82867 bytes images/fallback.svg | 10 + images/icon.png | Bin 0 -> 19174 bytes images/logo.svg | 68 ++++ images/photo.jpg | Bin 0 -> 7310 bytes images/share.jpg | Bin 0 -> 40015 bytes index.md | 94 +++++ projects/index.md | 27 ++ research/index.md | 27 ++ team/index.md | 35 ++ testbed.md | 437 +++++++++++++++++++++++ 151 files changed, 6488 insertions(+) create mode 100644 .docker/Dockerfile create mode 100755 .docker/entrypoint.sh create mode 100755 .docker/run.sh create mode 100644 .github/DISCUSSION_TEMPLATE/general.yaml create mode 100644 .github/DISCUSSION_TEMPLATE/q-a.yaml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/issue.yaml create mode 100644 .github/pull_request_template.md create mode 100644 .github/user_pull_request_template.md create mode 100644 .github/workflows/build-preview.yaml create mode 100644 .github/workflows/build-site.yaml create mode 100644 .github/workflows/first-time-setup.yaml create mode 100644 .github/workflows/on-pages.yaml create mode 100644 .github/workflows/on-pull-request.yml create mode 100644 .github/workflows/on-push.yml create mode 100644 .github/workflows/on-schedule.yaml create mode 100644 .github/workflows/update-citations.yaml create mode 100644 .github/workflows/update-url.yaml create mode 100644 .github/workflows/versioning.yaml create mode 100644 .gitignore create mode 100644 404.md create mode 100644 CHANGELOG.md create mode 100644 CITATION.cff create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 _cite/.cache/cache.db create mode 100644 _cite/cite.py create mode 100644 _cite/plugins/google-scholar.py create mode 100644 _cite/plugins/orcid.py create mode 100644 _cite/plugins/pubmed.py create mode 100644 _cite/plugins/sources.py create mode 100644 _cite/requirements.txt create mode 100644 _cite/util.py create mode 100644 _config.yaml create mode 100644 _data/citations.yaml create mode 100644 _data/orcid.yaml create mode 100644 _data/projects.yaml create mode 100644 _data/sources.yaml create mode 100644 _data/types.yaml create mode 100644 _includes/alert.html create mode 100644 _includes/analytics.html create mode 100644 _includes/button.html create mode 100644 _includes/card.html create mode 100644 _includes/citation.html create mode 100644 _includes/cols.html create mode 100644 _includes/content.html create mode 100644 _includes/fallback.html create mode 100644 _includes/feature.html create mode 100644 _includes/figure.html create mode 100644 _includes/float.html create mode 100644 _includes/fonts.html create mode 100644 _includes/footer.html create mode 100644 _includes/grid.html create mode 100644 _includes/head.html create mode 100644 _includes/header.html create mode 100644 _includes/icon.html create mode 100644 _includes/list.html create mode 100644 _includes/manubot.svg create mode 100644 _includes/meta.html create mode 100644 _includes/portrait.html create mode 100644 _includes/post-excerpt.html create mode 100644 _includes/post-info.html create mode 100644 _includes/post-nav.html create mode 100644 _includes/scripts.html create mode 100644 _includes/search-box.html create mode 100644 _includes/search-info.html create mode 100644 _includes/section.html create mode 100644 _includes/site-search.html create mode 100644 _includes/styles.html create mode 100644 _includes/tags.html create mode 100644 _includes/verification.html create mode 100644 _layouts/default.html create mode 100644 _layouts/member.html create mode 100644 _layouts/post.html create mode 100644 _members/jane-smith.md create mode 100644 _members/john-doe.md create mode 100644 _members/sarah-johnson.md create mode 100644 _plugins/array.rb create mode 100644 _plugins/file.rb create mode 100644 _plugins/hash.rb create mode 100644 _plugins/misc.rb create mode 100644 _plugins/regex.rb create mode 100644 _posts/2019-01-07-example-post-1.md create mode 100644 _posts/2021-09-30-example-post-2.md create mode 100644 _posts/2023-02-23-example-post-3.md create mode 100644 _scripts/anchors.js create mode 100644 _scripts/dark-mode.js create mode 100644 _scripts/fetch-tags.js create mode 100644 _scripts/search.js create mode 100644 _scripts/site-search.js create mode 100644 _scripts/table-wrap.js create mode 100644 _scripts/tooltip.js create mode 100644 _styles/-theme.scss create mode 100644 _styles/alert.scss create mode 100644 _styles/all.scss create mode 100644 _styles/anchor.scss create mode 100644 _styles/background.scss create mode 100644 _styles/body.scss create mode 100644 _styles/bold.scss create mode 100644 _styles/button.scss create mode 100644 _styles/card.scss create mode 100644 _styles/checkbox.scss create mode 100644 _styles/citation.scss create mode 100644 _styles/code.scss create mode 100644 _styles/cols.scss create mode 100644 _styles/dark-toggle.scss create mode 100644 _styles/feature.scss create mode 100644 _styles/figure.scss create mode 100644 _styles/float.scss create mode 100644 _styles/font.scss create mode 100644 _styles/footer.scss create mode 100644 _styles/form.scss create mode 100644 _styles/grid.scss create mode 100644 _styles/header.scss create mode 100644 _styles/heading.scss create mode 100644 _styles/highlight.scss create mode 100644 _styles/icon.scss create mode 100644 _styles/image.scss create mode 100644 _styles/link.scss create mode 100644 _styles/list.scss create mode 100644 _styles/main.scss create mode 100644 _styles/paragraph.scss create mode 100644 _styles/portrait.scss create mode 100644 _styles/post-excerpt.scss create mode 100644 _styles/post-info.scss create mode 100644 _styles/post-nav.scss create mode 100644 _styles/quote.scss create mode 100644 _styles/rule.scss create mode 100644 _styles/search-box.scss create mode 100644 _styles/search-info.scss create mode 100644 _styles/section.scss create mode 100644 _styles/table.scss create mode 100644 _styles/tags.scss create mode 100644 _styles/textbox.scss create mode 100644 _styles/tooltip.scss create mode 100644 _styles/util.scss create mode 100644 blog/index.md create mode 100644 contact/index.md create mode 100644 images/background.jpg create mode 100644 images/fallback.svg create mode 100644 images/icon.png create mode 100644 images/logo.svg create mode 100644 images/photo.jpg create mode 100644 images/share.jpg create mode 100644 index.md create mode 100644 projects/index.md create mode 100644 research/index.md create mode 100644 team/index.md create mode 100644 testbed.md diff --git a/.docker/Dockerfile b/.docker/Dockerfile new file mode 100644 index 0000000..caab63b --- /dev/null +++ b/.docker/Dockerfile @@ -0,0 +1,30 @@ +# start with official ruby docker image as base +FROM ruby:3.1.2 + +# set working directory within container +WORKDIR /usr/src/app + +# pull in ruby (jekyll) and python (cite process) package info +COPY Gemfile Gemfile.lock _cite/requirements.txt ./ + +# install ruby packages +RUN VERSION=$(grep -A 1 'BUNDLED WITH' Gemfile.lock | tail -n 1 | xargs); \ + gem install bundler --version ${VERSION} && \ + bundle _${VERSION}_ install + +# install python +RUN apt update && apt install -y python3 python3-pip + +# install python packages +RUN python3 -m pip install --no-cache-dir --upgrade --requirement requirements.txt + +# install python package for listening for file changes +RUN pip install "watchdog[watchmedo]==3.0.0" + +# ports used by jekyll +EXPOSE 4000 +EXPOSE 35729 + +# run jekyll and cite process +COPY .docker/entrypoint.sh /var +CMD [ "/var/entrypoint.sh" ] diff --git a/.docker/entrypoint.sh b/.docker/entrypoint.sh new file mode 100755 index 0000000..77c697a --- /dev/null +++ b/.docker/entrypoint.sh @@ -0,0 +1,27 @@ +#! /bin/bash + +# print folder contents for debugging +echo "Contents:" +echo "" +ls +echo "" + +# run cite process +python3 _cite/cite.py + +# run jekyll serve in hot-reload mode +# rerun whenever _config.yaml changes (jekyll hot-reload doesn't work with this file) +watchmedo auto-restart \ + --debug-force-polling \ + --patterns="_config.yaml" \ + --signal SIGTERM \ + -- bundle exec jekyll serve --open-url --force_polling --livereload --trace --host=0.0.0.0 \ + | sed "s/LiveReload address.*//g;s/0.0.0.0/localhost/g" & + +# rerun cite process whenever _data files change +watchmedo shell-command \ + --debug-force-polling \ + --recursive \ + --wait \ + --command="python3 _cite/cite.py" \ + --patterns="_data/sources*;_data/orcid*;_data/pubmed*;_data/google-scholar*" \ diff --git a/.docker/run.sh b/.docker/run.sh new file mode 100755 index 0000000..34ffd05 --- /dev/null +++ b/.docker/run.sh @@ -0,0 +1,37 @@ +#! /bin/bash + +# name of image +IMAGE=lab-website-renderer:latest + +# name of running container +CONTAINER=lab-website-renderer + +# choose platform flag +PLATFORM="" + +# default vars +DOCKER_RUN="docker run" +WORKING_DIR=$(pwd) + +# fix windows faux linux shells/tools +if [[ $OSTYPE == msys* ]] || [[ $OSTYPE == cygwin* ]]; then + DOCKER_RUN="winpty docker run" + WORKING_DIR=$(cmd //c cd) +fi + +# build docker image +docker build ${PLATFORM} \ + --tag ${IMAGE} \ + --file ./.docker/Dockerfile . && \ + +# run built docker image +${DOCKER_RUN} ${PLATFORM} \ + --name ${CONTAINER} \ + --init \ + --rm \ + --interactive \ + --tty \ + --publish 4000:4000 \ + --publish 35729:35729 \ + --volume "${WORKING_DIR}:/usr/src/app" \ + ${IMAGE} "$@" diff --git a/.github/DISCUSSION_TEMPLATE/general.yaml b/.github/DISCUSSION_TEMPLATE/general.yaml new file mode 100644 index 0000000..66b2c49 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/general.yaml @@ -0,0 +1,35 @@ +body: + - type: checkboxes + attributes: + label: Checks + options: + - label: I have searched **[the docs](https://greene-lab.gitbook.io/lab-website-template-docs)**, [existing issues](https://github.com/greenelab/lab-website-template/issues), and [existing discussions](https://github.com/greenelab/lab-website-template/discussions) for answers first. + required: true + + - type: input + id: repo + attributes: + label: Link to your website repo + description: "In almost all cases, **we cannot help you if you don't provide this**." + placeholder: ex. https://github.com/greenelab/greenelab.com + validations: + required: true + + - type: input + id: version + attributes: + label: Version of Lab Website Template you are using + description: See your `CITATION.cff` file. + placeholder: ex. 1.0.0 + validations: + required: true + + - type: textarea + id: description + attributes: + label: Description + description: | + Describe your issue in as much detail as possible. For example: What happened? What did you expect to happen? How can we reproduce the problem? What browser are you seeing the problem in? + placeholder: Description + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/q-a.yaml b/.github/DISCUSSION_TEMPLATE/q-a.yaml new file mode 100644 index 0000000..66b2c49 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/q-a.yaml @@ -0,0 +1,35 @@ +body: + - type: checkboxes + attributes: + label: Checks + options: + - label: I have searched **[the docs](https://greene-lab.gitbook.io/lab-website-template-docs)**, [existing issues](https://github.com/greenelab/lab-website-template/issues), and [existing discussions](https://github.com/greenelab/lab-website-template/discussions) for answers first. + required: true + + - type: input + id: repo + attributes: + label: Link to your website repo + description: "In almost all cases, **we cannot help you if you don't provide this**." + placeholder: ex. https://github.com/greenelab/greenelab.com + validations: + required: true + + - type: input + id: version + attributes: + label: Version of Lab Website Template you are using + description: See your `CITATION.cff` file. + placeholder: ex. 1.0.0 + validations: + required: true + + - type: textarea + id: description + attributes: + label: Description + description: | + Describe your issue in as much detail as possible. For example: What happened? What did you expect to happen? How can we reproduce the problem? What browser are you seeing the problem in? + placeholder: Description + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..7d0b7de --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: 💬 Start a discussion + url: https://github.com/greenelab/lab-website-template/discussions + about: I need help, I have a question, or other discussion. + - name: 📚 Docs issue + url: https://github.com/greenelab/lab-website-template-docs/issues + about: I have a question or issue related to the template documentation. diff --git a/.github/ISSUE_TEMPLATE/issue.yaml b/.github/ISSUE_TEMPLATE/issue.yaml new file mode 100644 index 0000000..d812555 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/issue.yaml @@ -0,0 +1,38 @@ +name: 🐞 Create an issue +description: I think I've discovered a bug, I want to request a feature/change, or other issue. + +body: + - type: checkboxes + attributes: + label: Checks + options: + - label: I have searched **[the docs](https://greene-lab.gitbook.io/lab-website-template-docs)**, [existing issues](https://github.com/greenelab/lab-website-template/issues), and [existing discussions](https://github.com/greenelab/lab-website-template/discussions) for answers first. + required: true + + - type: input + id: repo + attributes: + label: Link to your website repo + description: "In almost all cases, **we cannot help you if you don't provide this**." + placeholder: ex. https://github.com/greenelab/greenelab.com + validations: + required: true + + - type: input + id: version + attributes: + label: Version of Lab Website Template you are using + description: See your `CITATION.cff` file. + placeholder: ex. 1.0.0 + validations: + required: true + + - type: textarea + id: description + attributes: + label: Description + description: | + Describe your issue in as much detail as possible. For example: What happened? What did you expect to happen? How can we reproduce the problem? What browser are you seeing the problem in? + placeholder: Description + validations: + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..6aaf76b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +STOP!!! + +You are about to open this pull request against THE TEMPLATE ITSELF. You probably meant to open it against your own website repo. + +--- + +FOR THE TEMPLATE MAINTAINER(S) + +New template version checklist: + +- [ ] I have updated CITATION and CHANGELOG as appropriate. +- [ ] I have updated lab-website-template-docs as appropriate. +- [ ] I have checked the testbed as appropriate. diff --git a/.github/user_pull_request_template.md b/.github/user_pull_request_template.md new file mode 100644 index 0000000..00a0e71 --- /dev/null +++ b/.github/user_pull_request_template.md @@ -0,0 +1,4 @@ +This website is based on the Lab Website Template. +See its documentation for working with this site: + +https://greene-lab.gitbook.io/lab-website-template-docs diff --git a/.github/workflows/build-preview.yaml b/.github/workflows/build-preview.yaml new file mode 100644 index 0000000..3ef0404 --- /dev/null +++ b/.github/workflows/build-preview.yaml @@ -0,0 +1,58 @@ +name: build-preview +run-name: build pull request preview + +on: + # run when called from another workflow + workflow_call: + + # run if user manually requests it + workflow_dispatch: + +# variables +env: + PREVIEWS_FOLDER: preview + +permissions: + contents: write + pull-requests: write + +jobs: + build-preview: + runs-on: ubuntu-latest + + steps: + # for debugging + - uses: crazy-max/ghaction-dump-context@v2 + + - name: Checkout branch contents + uses: actions/checkout@v4 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.head_ref }} + + - name: Install Ruby packages + if: github.event.action != 'closed' + uses: ruby/setup-ruby@v1.172.0 + with: + ruby-version: "3.1" + bundler-cache: true + + - name: Get Pages url + if: github.event.action != 'closed' + id: pages + uses: actions/configure-pages@v4 + + # for debugging + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + + - name: Build preview version of site + if: github.event.action != 'closed' + run: | + JEKYLL_ENV=production bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path || '' }}/${{ env.PREVIEWS_FOLDER }}/pr-${{ github.event.number }}" + + - name: Commit preview to Pages branch + uses: rossjrw/pr-preview-action@v1.4.7 + with: + source-dir: _site + umbrella-dir: ${{ env.PREVIEWS_FOLDER }} diff --git a/.github/workflows/build-site.yaml b/.github/workflows/build-site.yaml new file mode 100644 index 0000000..6aa3e5a --- /dev/null +++ b/.github/workflows/build-site.yaml @@ -0,0 +1,56 @@ +name: build-site +run-name: build live site + +on: + # run when called from another workflow + workflow_call: + + # run if user manually requests it + workflow_dispatch: + +# variables +env: + PREVIEWS_FOLDER: preview + +permissions: + contents: write + +jobs: + build-site: + runs-on: ubuntu-latest + + steps: + # for debugging + - uses: crazy-max/ghaction-dump-context@v2 + + - name: Checkout branch contents + uses: actions/checkout@v4 + + - name: Install Ruby packages + uses: ruby/setup-ruby@v1.172.0 + with: + ruby-version: "3.1" + bundler-cache: true + + - name: Get Pages url + id: pages + uses: actions/configure-pages@v4 + + # for debugging + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + + - name: Set root url + run: | + echo "\n\nurl: ${{ steps.pages.outputs.origin }}" >> _config.yaml + + - name: Build live version of site + run: | + JEKYLL_ENV=production bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path || '' }}" + + - name: Commit live site to Pages branch + uses: JamesIves/github-pages-deploy-action@v4.5.0 + with: + folder: _site + clean-exclude: ${{ env.PREVIEWS_FOLDER }} + force: false diff --git a/.github/workflows/first-time-setup.yaml b/.github/workflows/first-time-setup.yaml new file mode 100644 index 0000000..51d3273 --- /dev/null +++ b/.github/workflows/first-time-setup.yaml @@ -0,0 +1,118 @@ +name: first-time-setup +run-name: first time setup of repo + +on: + # run if user manually requests it + workflow_dispatch: + +permissions: + contents: write + +jobs: + first-time-setup: + runs-on: ubuntu-latest + + steps: + # for debugging + - uses: crazy-max/ghaction-dump-context@v2 + + - name: Create Pages branch + uses: peterjgrainger/action-create-branch@v3.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + branch: "gh-pages" + + - name: Checkout Pages branch + uses: actions/checkout@v4 + with: + ref: gh-pages + + # for debugging + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + + # clean slate, as if starting from orphan branch + - name: Clear Pages branch + run: rm -rf * .github .docker .gitignore + + # prevent GitHub from running Jekyll a second time after build + - name: Make .nojekyll file + run: touch .nojekyll + + - name: Make placeholder homepage + run: echo "Placeholder homepage" > index.html + + - name: Commit changes to Pages branch + uses: stefanzweifel/git-auto-commit-action@v5 + with: + branch: gh-pages + commit_message: "Clear branch" + + - name: Checkout main branch + uses: actions/checkout@v4 + + - name: Remove files user doesn't need + run: | + rm -rf \ + CHANGELOG.md \ + testbed.md \ + .github/ISSUE_TEMPLATE \ + .github/DISCUSSION_TEMPLATE \ + .github/workflows/versioning.yaml \ + .github/pull_request_template.md \ + + - name: Rename files + run: | + mv -f .github/user_pull_request_template.md .github/pull_request_template.md + + - name: Set vars for personalization + run: | + user="${{ github.repository_owner }}" + description="An engaging 1-3 sentence description of your lab." + echo "USER=${user}" >> $GITHUB_ENV + echo "DESCRIPTION=${description}" >> $GITHUB_ENV + + - name: Personalize readme for user + run: | + echo " + # ${{ env.USER }}'s Website + + Visit **[website url](#)** 🚀 + + _Built with [Lab Website Template](https://greene-lab.gitbook.io/lab-website-template-docs)_ + " > README.md + + - name: Personalize Jekyll config for user + uses: actions/github-script@v7 + with: + script: | + const { readFileSync, writeFileSync } = require("fs"); + const file = "_config.yaml"; + const contents = readFileSync(file) + .toString() + .replace(/(^title: ).*$/m, "$1${{ env.USER }}") + .replace(/(^subtitle: ).*$/m, "$1") + .replace(/(^description: ).*$/m, "$1${{ env.DESCRIPTION }}") + .replace(/(^ email: ).*$/m, "$1contact@${{ env.USER }}.com") + .replace(/(^ github: ).*$/m, "$1${{ env.USER }}") + .replace(/(^ twitter: ).*$/m, "$1${{ env.USER }}") + .replace(/(^ youtube: ).*$/m, "$1${{ env.USER }}"); + writeFileSync(file, contents); + + - name: Personalize homepage for user + uses: actions/github-script@v7 + with: + script: | + const { readFileSync, writeFileSync } = require("fs"); + const file = "index.md"; + let contents = readFileSync(file).toString(); + const find = /\# Lab Website Template[\s\S]+({% include section\.html)/; + const replace = `# ${{ env.USER }}'s Website\n\n${{ env.DESCRIPTION }}\n\n$1`; + contents = contents.replace(find, replace); + writeFileSync(file, contents); + + - name: Commit changed files + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Setup repo" diff --git a/.github/workflows/on-pages.yaml b/.github/workflows/on-pages.yaml new file mode 100644 index 0000000..b61c13b --- /dev/null +++ b/.github/workflows/on-pages.yaml @@ -0,0 +1,27 @@ +name: on-pages +run-name: on pages deploy + +on: + workflow_run: + workflows: [pages-build-deployment] + types: + - completed + branches: + - gh-pages + + # run if user manually requests it + workflow_dispatch: + +permissions: + contents: write + +jobs: + update-url: + # only run on user instance of template, not template itself + if: github.repository != 'greenelab/lab-website-template' + uses: ./.github/workflows/update-url.yaml + + build-site: + needs: update-url + if: needs.update-url.outputs.changed == 'true' + uses: ./.github/workflows/build-site.yaml diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml new file mode 100644 index 0000000..5cdf21a --- /dev/null +++ b/.github/workflows/on-pull-request.yml @@ -0,0 +1,22 @@ +name: on-pull-request +run-name: on pull request activity + +on: + pull_request_target: + types: + - opened + - reopened + - synchronize + - closed + +permissions: + contents: write + pull-requests: write + +jobs: + update-citations: + uses: ./.github/workflows/update-citations.yaml + + build-preview: + needs: update-citations + uses: ./.github/workflows/build-preview.yaml diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml new file mode 100644 index 0000000..06dd001 --- /dev/null +++ b/.github/workflows/on-push.yml @@ -0,0 +1,24 @@ +name: on-push +run-name: on push to main + +on: + push: + branches: + - main + + # run if user manually requests it + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-citations: + # skip first run because nothing enabled or setup yet + if: github.run_number != 1 + uses: ./.github/workflows/update-citations.yaml + + build-site: + needs: update-citations + uses: ./.github/workflows/build-site.yaml diff --git a/.github/workflows/on-schedule.yaml b/.github/workflows/on-schedule.yaml new file mode 100644 index 0000000..ad1fe95 --- /dev/null +++ b/.github/workflows/on-schedule.yaml @@ -0,0 +1,27 @@ +name: on-schedule +run-name: on schedule + +on: + schedule: + # weekly + - cron: "0 0 * * 1" + + # run if user manually requests it + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-citations: + # only run on user instance of template, not template itself + if: github.repository != 'greenelab/lab-website-template' + uses: ./.github/workflows/update-citations.yaml + with: + open-pr: true + + build-preview: + needs: update-citations + if: needs.update-citations.outputs.changed == 'true' + uses: ./.github/workflows/build-preview.yaml diff --git a/.github/workflows/update-citations.yaml b/.github/workflows/update-citations.yaml new file mode 100644 index 0000000..f6ff6ec --- /dev/null +++ b/.github/workflows/update-citations.yaml @@ -0,0 +1,84 @@ +name: update-citations +run-name: update citations + +on: + # run when called from another workflow + workflow_call: + inputs: + open-pr: + type: boolean + outputs: + changed: + value: ${{ jobs.update-citations.outputs.changed }} + + # run if user manually requests it + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +env: + FORCE_COLOR: true + GOOGLE_SCHOLAR_API_KEY: ${{ secrets.GOOGLE_SCHOLAR_API_KEY }} + +jobs: + update-citations: + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + # for debugging + - uses: crazy-max/ghaction-dump-context@v2 + + - name: Checkout branch contents + uses: actions/checkout@v4 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.head_ref }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: "pip" + cache-dependency-path: "**/requirements.txt" + + - name: Install Python packages + run: | + python -m pip install --upgrade --requirement ./_cite/requirements.txt + + # for debugging + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + + - name: Build updated citations + run: python _cite/cite.py + timeout-minutes: 15 + + - name: Check if citations changed + id: changed + uses: tj-actions/verify-changed-files@v18 + with: + files: | + _data/citations.yaml + + - name: Commit updated citations to branch + if: | + steps.changed.outputs.files_changed == 'true' && + inputs.open-pr != true + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Update citations" + + - name: Open pull request with updated citations + if: | + steps.changed.outputs.files_changed == 'true' && + inputs.open-pr == true + uses: peter-evans/create-pull-request@v6 + with: + branch: citation-update + title: Periodic citation update + + outputs: + changed: ${{ steps.changed.outputs.files_changed }} diff --git a/.github/workflows/update-url.yaml b/.github/workflows/update-url.yaml new file mode 100644 index 0000000..b3573a5 --- /dev/null +++ b/.github/workflows/update-url.yaml @@ -0,0 +1,69 @@ +name: update-url +run-name: update site after url change + +on: + # run when called from another workflow + workflow_call: + outputs: + changed: + value: ${{ jobs.update-url.outputs.changed }} + + # run if user manually requests it + workflow_dispatch: + +permissions: + contents: write + +jobs: + update-url: + runs-on: ubuntu-latest + + steps: + # for debugging + - uses: crazy-max/ghaction-dump-context@v2 + + - name: Get Pages url + id: pages + uses: actions/configure-pages@v4 + + - name: Checkout branch contents + uses: actions/checkout@v4 + + # for debugging + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + + # update link to site in readme + - name: Update readme + uses: actions/github-script@v7 + with: + script: | + const { readFileSync, writeFileSync } = require("fs"); + const file = "README.md"; + let contents = readFileSync(file).toString(); + const find = /\*\*\[.*\]\(.*\)\*\*/; + const host = "${{ steps.pages.outputs.host }}"; + const path = "${{ steps.pages.outputs.base_path }}"; + const url = "${{ steps.pages.outputs.base_url }}"; + const replace = `**[${host}${path}](${url})**`; + if (contents.match(find)) + contents = contents.replace(find, replace); + else + contents = `Visit ${replace} 🚀\n\n` + contents; + writeFileSync(file, contents); + + - name: Check if readme changed + id: changed + uses: tj-actions/verify-changed-files@v18 + with: + files: | + README.md + + - name: Commit changed files + if: steps.changed.outputs.files_changed == 'true' + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Update url" + + outputs: + changed: ${{ steps.changed.outputs.files_changed }} diff --git a/.github/workflows/versioning.yaml b/.github/workflows/versioning.yaml new file mode 100644 index 0000000..e148f36 --- /dev/null +++ b/.github/workflows/versioning.yaml @@ -0,0 +1,135 @@ +name: versioning +run-name: versioning tasks + +on: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + contents: write + +jobs: + pull-request: + # only run on template itself, not user instance of template + if: | + github.repository == 'greenelab/lab-website-template' && + github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + # for debugging + - uses: crazy-max/ghaction-dump-context@v2 + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + + - name: Checkout base branch contents + uses: actions/checkout@v4 + with: + ref: main + path: base + + - name: Checkout pr branch contents + uses: actions/checkout@v4 + with: + path: pr + + - name: Install packages + run: npm install yaml semver + + - name: Check version, date, changelog + uses: actions/github-script@v7 + with: + script: | + const { readFileSync } = require("fs"); + const { lte, valid } = require("semver"); + const { parse } = require("yaml"); + + // load and parse file contents + const { version: oldVersion, "date-released": oldDate } = parse( + readFileSync("base/CITATION.cff").toString() + ); + const { version: newVersion, "date-released": newDate } = parse( + readFileSync("pr/CITATION.cff").toString() + ); + const changelog = readFileSync("pr/CHANGELOG.md") + .toString() + .split(/^## /m) + .map((section) => { + const [heading, ...body] = section.split("\n"); + return [heading.trim(), body.join("\n").trim()]; + }); + + // check version + if (!valid(newVersion)) throw Error("Version not valid"); + if (lte(newVersion, oldVersion)) throw Error("Version not updated"); + + // check date + if (new Date(newDate).toISOString().split("T")[0] !== newDate) + throw Error("Date not valid"); + if (new Date(newDate) <= new Date(oldDate)) throw Error("Date not updated"); + + // check changelog + const newSection = changelog.find( + ([heading, body]) => + heading.includes(newVersion) && heading.includes(newDate) && body + ); + if (!newSection) throw Error("Changelog not updated or not valid"); + + push: + # only run on template itself, not user instance of template + if: | + github.repository == 'greenelab/lab-website-template' && + github.event_name == 'push' + runs-on: ubuntu-latest + steps: + # for debugging + - uses: crazy-max/ghaction-dump-context@v2 + + - name: Checkout branch contents + uses: actions/checkout@v4 + + - name: Install packages + run: npm install yaml semver + + # for debugging + - if: runner.debug == '1' + uses: mxschmitt/action-tmate@v3 + + - name: Get version and body + id: version + uses: actions/github-script@v7 + with: + script: | + const { readFileSync } = require("fs"); + const { parse } = require("yaml"); + + // load and parse file contents + const { version, "date-released": date } = parse( + readFileSync("CITATION.cff").toString() + ); + const changelog = readFileSync("CHANGELOG.md") + .toString() + .split(/^## /m) + .map((section) => { + const [heading, ...body] = section.split("\n"); + return [heading.trim(), body.join("\n").trim()]; + }); + + // find changelog body for version + const [, body = ""] = + changelog.find( + ([heading]) => heading.includes(version) && heading.includes(date) + ) || []; + + return { version, body }; + + - name: Create GitHub release + uses: ncipollo/release-action@v1.14.0 + with: + commit: ${{ github.ref }} + tag: v${{ fromJson(steps.version.outputs.result).version }} + name: v${{ fromJson(steps.version.outputs.result).version }} + body: ${{ fromJson(steps.version.outputs.result).body }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c3511ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +_site +.sass-cache +.jekyll-cache +.jekyll-metadata +vendor +debug.log +__pycache__ +.cache +!cache.db +.DS_STORE +.env* +package.json +package-lock.json +yarn.lock +node_modules diff --git a/404.md b/404.md new file mode 100644 index 0000000..64b5a4a --- /dev/null +++ b/404.md @@ -0,0 +1,11 @@ +--- +title: 404 +permalink: /404.html +--- + +## {% include icon.html icon="fa-solid fa-heart-crack" %} Page Not Found + +Try searching the whole site for the content you want: +{:.center} + +{% include site-search.html %} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d702c3e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,146 @@ +# Changelog + +Reference: common-changelog.org + +## 1.2.0 - 2024-03-08 + +### Changed + +- Update all GitHub Actions to fix "Node v16 deprecated" warnings. +- Sources that Manubot doesn't know how to cite (e.g. wosuid:12345) are now ignored by default if they're from metasources. +- Fix bug where passing tags to tags component manually doesn't work. +- Fix bug in citation (and other) components when `lookup` is blank. +- Fix nested tables bug. +- Dark mode tweaks. +- Various CSS tweaks and fixes. + +### Added + +- Add `image` param to support blog post thumbnails. +- Add `html-proofer` plugin that checks for broken images/links/etc. +- Add `remove` flag to remove a source from a metasource. + +## 1.1.6 - 2023-10-06 + +### Changed + +- Use latest minor versions of Python packages in auto-cite script. + +## 1.1.5 - 2023-05-19 + +### Changed + +- Fix ORCID plugin bug and other cite process tweaks. + +## 1.1.4 - 2023-04-28 + +### Changed + +- Fix ORCID plugin and other cite process bugs. + +## 1.1.3 - 2023-04-20 + +### Changed + +- Fix first-time-setup mv bug. +- Fix citation, float, and portrait component CSS. +- Filter and trim citation info fields. + +## 1.1.2 - 2023-04-11 + +### Changed + +- Fix first-time-setup rm bug. + +## 1.1.1 - 2023-04-06 + +### Changed + +- Change member profile page from col layout to float. +- Fix first time setup. Preserve config formatting and comments. +- Improve Docker cite process behavior. +- Fix post excerpt component start/end markers and special search attr chars. +- Fix misc CSS. + +### Added + +- Add show-title and show-subtitle site config options. +- Include site subtitle in description meta tag. +- Add user pull request template. +- Add title and link fallbacks to citation component. + +## 1.1.0 - 2023-03-17 + +Add alert component, Docker support, accessibility fixes. + +### Changed + +- Fix Lighthouse accessibility issues. +- De-href components when link isn't provided (no hand cursor icon on hover or nav on click). +- In search script, limit highlights by total count instead of char length. +- Grid and link style tweaks. +- Take ORCID icon from Font Awesome. +- Misc bug fixes in tags script, float component. + +### Added + +- Add Docker configuration and scripts for local previewing. +- Add alert component and types. +- Role icon in portrait component hoisted to top left. + +## 1.0.0 - 2023-02-28 + +First official release. + +High-level comparison with pre-releases: + +- Simpler configuration. +- More automation, less setup. +- More customization and flexibility. +- Redesigned components. +- New docs. +- Complete rewrite. +- Culmination of years of feedback. + +### Changed + +- Template is no longer limited to GitHub Pages white-listed Jekyll plugins. Any plugins possible. +- Pull request previews happen right within GitHub instead of needing Netlify. +- Better versioning. `CITATION.cff` file now source of truth for version, and tags/releases enforced. +- Citation-related files in `/_data` must now be named prefixed with the cite plugin they are to be run with, e.g. `sources-2020.yaml` or `orcid-students.yaml`. +- Folder renames for clarity and for better separation of template and user content: `/auto-cite` → `/_cite`, `/css` → `/_styles`, `/js` → `/_scripts`. +- Rename "Tools" page to "Projects" to be more clear and general purpose. +- Rename `extra-links` to `buttons` in `sources.yaml` files. +- Rename `theme.scss` to `-theme.scss`. +- Rename/repurpose components: link → button, two-col → cols, gallery → grid. +- Combine "link" and "role" data lists into single `types.yaml` map. +- Redesign components, change parameters and behavior. +- Update Font Awesome icon names from v5 to v6. +- Change placeholder text, images, and other images. +- Use CSS variables instead of Sass variables. +- Simplify caching method in cite process. +- Simplify Liquid code by including custom Ruby plugins. +- Simplify styles and scripts. + +### Added + +- New docs at greene-lab.gitbook.io/lab-website-template-docs. +- Add automations for first time setup and URL change. +- Write PubMed and Google Scholar automatic citation plugins. +- Automatic citations through GitHub Actions should now work from (most) forks. +- Add optional description and type params for citations. +- Add periodic cite process run that opens a pull request. +- List component filters can now accept arbitrary regex. +- Add light/dark mode toggle. +- Pre-install selection of useful Jekyll plugins, namely Jekyll Spaceship. +- Add author portrait and updated date for blog posts. +- Add richer metadata for SEO. +- Google Fonts link determined automatically from theme file. + +### Removed + +- Remove options from `_config.yaml` to simplify configuration: `baseurl`, `auto-cite`, `logo`. +- Remove `/favicons` folder, hardcode files for logo, icon, and share in `/images`. +- Remove `palettes.scss` and `mixins.scss`. +- Remove banner component (same thing can be achieved with full width section and figure components). +- Remove role component. Combine with portrait component. diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..dcb59dc --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,14 @@ +# citation metadata for the template itself + +title: "Lab Website Template" +version: 1.2.0 +date-released: 2024-03-08 +url: "https://github.com/greenelab/lab-website-template" +authors: + - family-names: "Rubinetti" + given-names: "Vincent" + orcid: "https://orcid.org/0000-0002-4655-3773" + - family-names: "Greene" + given-names: "Casey" + orcid: "https://orcid.org/0000-0001-8713-9213" +cff-version: 1.2.0 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..cbdd53f --- /dev/null +++ b/Gemfile @@ -0,0 +1,16 @@ +source "https://rubygems.org" + +# jekyll +gem "jekyll", "~> 4.3" +gem "webrick", "~> 1.7" + +gem "html-proofer", "~> 5.0" + +# plugins +group :jekyll_plugins do + gem "jekyll-spaceship" + gem "jekyll-sitemap" + gem "jekyll-redirect-from" + gem "jekyll-feed" + gem "jekyll-last-modified-at" +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..ee2cb12 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,157 @@ +GEM + remote: https://rubygems.org/ + specs: + Ascii85 (1.1.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) + afm (0.2.2) + async (2.8.1) + console (~> 1.10) + fiber-annotation + io-event (~> 1.1) + timers (~> 4.1) + colorator (1.1.0) + concurrent-ruby (1.2.2) + console (1.23.4) + fiber-annotation + fiber-local + json + em-websocket (0.5.3) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0) + ethon (0.16.0) + ffi (>= 1.15.0) + eventmachine (1.2.7) + ffi (1.15.5) + ffi (1.15.5-x64-mingw-ucrt) + fiber-annotation (0.2.0) + fiber-local (1.0.0) + forwardable-extended (2.6.0) + gemoji (3.0.1) + google-protobuf (3.22.0) + google-protobuf (3.22.0-arm64-darwin) + google-protobuf (3.22.0-x64-mingw-ucrt) + hashery (2.1.2) + html-proofer (5.0.8) + addressable (~> 2.3) + async (~> 2.1) + nokogiri (~> 1.13) + pdf-reader (~> 2.11) + rainbow (~> 3.0) + typhoeus (~> 1.3) + yell (~> 2.0) + zeitwerk (~> 2.5) + http_parser.rb (0.8.0) + i18n (1.12.0) + concurrent-ruby (~> 1.0) + io-event (1.4.4) + jekyll (4.3.2) + addressable (~> 2.4) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 1.0) + jekyll-sass-converter (>= 2.0, < 4.0) + jekyll-watch (~> 2.0) + kramdown (~> 2.3, >= 2.3.1) + kramdown-parser-gfm (~> 1.0) + liquid (~> 4.0) + mercenary (>= 0.3.6, < 0.5) + pathutil (~> 0.9) + rouge (>= 3.0, < 5.0) + safe_yaml (~> 1.0) + terminal-table (>= 1.8, < 4.0) + webrick (~> 1.7) + jekyll-feed (0.17.0) + jekyll (>= 3.7, < 5.0) + jekyll-last-modified-at (1.3.0) + jekyll (>= 3.7, < 5.0) + posix-spawn (~> 0.3.9) + jekyll-redirect-from (0.16.0) + jekyll (>= 3.3, < 5.0) + jekyll-sass-converter (3.0.0) + sass-embedded (~> 1.54) + jekyll-sitemap (1.4.0) + jekyll (>= 3.7, < 5.0) + jekyll-spaceship (0.10.2) + gemoji (~> 3.0) + jekyll (>= 3.6, < 5.0) + nokogiri (~> 1.6) + rainbow (~> 3.0) + jekyll-watch (2.2.1) + listen (~> 3.0) + json (2.7.1) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (4.0.4) + listen (3.8.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.4.0) + mini_portile2 (2.8.1) + nokogiri (1.13.10) + mini_portile2 (~> 2.8.0) + racc (~> 1.4) + nokogiri (1.13.10-arm64-darwin) + racc (~> 1.4) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + pdf-reader (2.12.0) + Ascii85 (~> 1.0) + afm (~> 0.2.1) + hashery (~> 2.0) + ruby-rc4 + ttfunk + posix-spawn (0.3.15) + public_suffix (5.0.1) + racc (1.6.2) + rainbow (3.1.1) + rake (13.1.0) + rb-fsevent (0.11.2) + rb-inotify (0.10.1) + ffi (~> 1.0) + rexml (3.2.5) + rouge (3.30.0) + ruby-rc4 (0.1.5) + safe_yaml (1.0.5) + sass-embedded (1.58.3) + google-protobuf (~> 3.21) + rake (>= 10.0.0) + sass-embedded (1.58.3-arm64-darwin) + google-protobuf (~> 3.21) + sass-embedded (1.58.3-x64-mingw-ucrt) + google-protobuf (~> 3.21) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + timers (4.3.5) + ttfunk (1.7.0) + typhoeus (1.4.1) + ethon (>= 0.9.0) + unicode-display_width (2.4.2) + webrick (1.8.1) + yell (2.2.2) + zeitwerk (2.6.13) + +PLATFORMS + aarch64-linux + linux + universal-darwin-21 + universal-darwin-22 + x64-mingw-ucrt + x64-mingw32 + x64-unknown + x86_64-linux + +DEPENDENCIES + html-proofer (~> 5.0) + jekyll (~> 4.3) + jekyll-feed + jekyll-last-modified-at + jekyll-redirect-from + jekyll-sitemap + jekyll-spaceship + webrick (~> 1.7) + +BUNDLED WITH + 2.5.6 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..70c6b2a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Greene Laboratory +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..29bf9a2 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +

Lab Website Template

+

+Lab Website Template +

+ +Lab Website Template (LWT) is an easy-to-use, flexible website template for labs. +Spend less time worrying about managing a website and citations, and more time running your lab. + +👇👇 **Get Started** 👇👇 + +[**Documentation**](https://greene-lab.gitbook.io/lab-website-template-docs) + +## Key Features + +- 🤖 Based on Git, GitHub, and Jekyll. +- 📜 Automatically generated citations from simple identifiers (DOI, PubMed, ORCID, and many more) using Manubot. E.g. `doi:1234/5678` -> `title`, `authors`, `publisher`, `date`, etc. +- 🧱 A comprehensive and flexible suite of pre-made components (building blocks) for structuring and styling your website: + - Formatted tables, code blocks, figures, and other basic elements. + - Citations with thumbnails and other rich details. + - List large sets of data with flexible filters and components. + - ...many more +- 👁️ Automatic pull request previews. +- ⚙️ Easy and automated configuration. +- 👥 Team member pages with bios, roles, and social media links. +- 🖋️ Blog posts with tags and rich content. +- 📱 Works and looks good on desktop and mobile. +- 🤝 Great documentation and support (if we do say so ourselves). +- ... and much more! + +![GitHub last commit](https://img.shields.io/github/last-commit/greenelab/lab-website-template) diff --git a/_cite/.cache/cache.db b/_cite/.cache/cache.db new file mode 100644 index 0000000000000000000000000000000000000000..2ca6d980786a072bb0cc0da17cbe3bc7aadd1d82 GIT binary patch literal 49152 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 IhuhZw|I-8U&;S4c literal 0 HcmV?d00001 diff --git a/_cite/cite.py b/_cite/cite.py new file mode 100644 index 0000000..6cd5bef --- /dev/null +++ b/_cite/cite.py @@ -0,0 +1,188 @@ +""" +cite process to convert sources and metasources into full citations +""" + +import traceback +from importlib import import_module +from pathlib import Path +from dotenv import load_dotenv +from util import * + + +# load environment variables +load_dotenv() + + +# error flag +error = False + +# output citations file +output_file = "_data/citations.yaml" + + +log() + +log("Compiling sources") + +# compiled list of sources +sources = [] + +# in-order list of plugins to run +plugins = ["google-scholar", "pubmed", "orcid", "sources"] + +# loop through plugins +for plugin in plugins: + # convert into path object + plugin = Path(f"plugins/{plugin}.py") + + log(f"Running {plugin.stem} plugin") + + # get all data files to process with current plugin + files = Path.cwd().glob(f"_data/{plugin.stem}*.*") + files = list(filter(lambda p: p.suffix in [".yaml", ".yml", ".json"], files)) + + log(f"Found {len(files)} {plugin.stem}* data file(s)", 1) + + # loop through data files + for file in files: + log(f"Processing data file {file.name}", 1) + + # load data from file + try: + data = load_data(file) + # check if file in correct format + if not list_of_dicts(data): + raise Exception("File not a list of dicts") + except Exception as e: + log(e, 2, "ERROR") + error = True + continue + + # loop through data entries + for index, entry in enumerate(data): + log(f"Processing entry {index + 1} of {len(data)}, {label(entry)}", 2) + + # run plugin on data entry to expand into multiple sources + try: + expanded = import_module(f"plugins.{plugin.stem}").main(entry) + # check that plugin returned correct format + if not list_of_dicts(expanded): + raise Exception("Plugin didn't return list of dicts") + # catch any plugin error + except Exception as e: + # log detailed pre-formatted/colored trace + print(traceback.format_exc()) + # log high-level error + log(e, 3, "ERROR") + error = True + continue + + # loop through sources + for source in expanded: + if plugin.stem != "sources": + log(label(source), 3) + + # include meta info about source + source["plugin"] = plugin.name + source["file"] = file.name + + # add source to compiled list + sources.append(source) + + if plugin.stem != "sources": + log(f"{len(expanded)} source(s)", 3) + + +log("Merging sources by id") + +# merge sources with matching (non-blank) ids +for a in range(0, len(sources)): + a_id = get_safe(sources, f"{a}.id", "") + if not a_id: + continue + for b in range(a + 1, len(sources)): + b_id = get_safe(sources, f"{b}.id", "") + if b_id == a_id: + log(f"Found duplicate {b_id}", 2) + sources[a].update(sources[b]) + sources[b] = {} +sources = [entry for entry in sources if entry] + + +log(f"{len(sources)} total source(s) to cite") + + +log() + +log("Generating citations") + +# list of new citations +citations = [] + + +# loop through compiled sources +for index, source in enumerate(sources): + log(f"Processing source {index + 1} of {len(sources)}, {label(source)}") + + # if explicitly flagged, remove/ignore entry + if get_safe(source, "remove", False) == True: + continue + + # new citation data for source + citation = {} + + # source id + _id = get_safe(source, "id", "").strip() + + # Manubot doesn't work without an id + if _id: + log("Using Manubot to generate citation", 1) + + try: + # run Manubot and set citation + citation = cite_with_manubot(_id) + + # if Manubot cannot cite source + except Exception as e: + # if regular source (id entered by user), throw error + if get_safe(source, "plugin", "") == "sources.py": + log(e, 3, "ERROR") + error = True + # otherwise, if from metasource (id retrieved from some third-party API), just warn + else: + log(e, 3, "WARNING") + # discard source from citations + continue + + # preserve fields from input source, overriding existing fields + citation.update(source) + + # ensure date in proper format for correct date sorting + if get_safe(citation, "date", ""): + citation["date"] = format_date(get_safe(citation, "date", "")) + + # add new citation to list + citations.append(citation) + + +log() + +log("Saving updated citations") + + +# save new citations +try: + save_data(output_file, citations) +except Exception as e: + log(e, level="ERROR") + error = True + + +# exit at end, so user can see all errors in one run +if error: + log("Error(s) occurred above", level="ERROR") + exit(1) +else: + log("All done!", level="SUCCESS") + +log("\n") diff --git a/_cite/plugins/google-scholar.py b/_cite/plugins/google-scholar.py new file mode 100644 index 0000000..e3841b8 --- /dev/null +++ b/_cite/plugins/google-scholar.py @@ -0,0 +1,61 @@ +import os +from serpapi import GoogleSearch +from util import * + + +def main(entry): + """ + receives single list entry from google-scholar data file + returns list of sources to cite + """ + + # get api key (serp api key to access google scholar) + api_key = os.environ.get("GOOGLE_SCHOLAR_API_KEY", "") + if not api_key: + raise Exception('No "GOOGLE_SCHOLAR_API_KEY" env var') + + # serp api properties + params = { + "engine": "google_scholar_author", + "api_key": api_key, + "num": 100, # max allowed + } + + # get id from entry + _id = get_safe(entry, "gsid", "") + if not _id: + raise Exception('No "gsid" key') + + # query api + @log_cache + @cache.memoize(name=__file__, expire=1 * (60 * 60 * 24)) + def query(_id): + params["author_id"] = _id + return get_safe(GoogleSearch(params).get_dict(), "articles", []) + + response = query(_id) + + # list of sources to return + sources = [] + + # go through response and format sources + for work in response: + # create source + year = get_safe(work, "year", "") + source = { + "id": get_safe(work, "citation_id", ""), + # api does not provide Manubot-citeable id, so keep citation details + "title": get_safe(work, "title", ""), + "authors": list(map(str.strip, get_safe(work, "authors", "").split(","))), + "publisher": get_safe(work, "publication", ""), + "date": (year + "-01-01") if year else "", + "link": get_safe(work, "link", ""), + } + + # copy fields from entry to source + source.update(entry) + + # add source to list + sources.append(source) + + return sources diff --git a/_cite/plugins/orcid.py b/_cite/plugins/orcid.py new file mode 100644 index 0000000..0017145 --- /dev/null +++ b/_cite/plugins/orcid.py @@ -0,0 +1,109 @@ +import json +from urllib.request import Request, urlopen +from util import * + + +def main(entry): + """ + receives single list entry from orcid data file + returns list of sources to cite + """ + + # orcid api + endpoint = "https://pub.orcid.org/v3.0/$ORCID/works" + headers = {"Accept": "application/json"} + + # get id from entry + _id = get_safe(entry, "orcid", "") + if not _id: + raise Exception('No "orcid" key') + + # query api + @log_cache + @cache.memoize(name=__file__, expire=1 * (60 * 60 * 24)) + def query(_id): + url = endpoint.replace("$ORCID", _id) + request = Request(url=url, headers=headers) + response = json.loads(urlopen(request).read()) + return get_safe(response, "group", []) + + response = query(_id) + + # list of sources to return + sources = [] + + # go through response structure and pull out ids e.g. doi:1234/56789 + for work in response: + # get list of ids + ids = get_safe(work, "external-ids.external-id", []) + for summary in get_safe(work, "work-summary", []): + ids = ids + get_safe(summary, "external-ids.external-id", []) + + # prefer doi id type, or fallback to first id + _id = next( + (id for id in ids if get_safe(id, "external-id-type", "") == "doi"), + ids[0] if len(ids) > 0 else {}, + ) + + # get id and id-type from response + id_type = get_safe(_id, "external-id-type", "") + id_value = get_safe(_id, "external-id-value", "") + + # create source + source = {"id": f"{id_type}:{id_value}"} + + # if not a doi, Manubot likely can't cite, so keep citation details + if id_type != "doi": + # get summaries + summaries = get_safe(work, "work-summary", []) + + # sort summary entries by most recent + summaries = sorted( + summaries, + key=lambda summary: (get_safe(summary, "last-modified-date.value", 0)) + or get_safe(summary, "created-date.value", 0) + or 0, + reverse=True, + ) + + # get first summary with defined sub-value + def first(get_func): + return next( + (value for value in map(get_func, summaries) if value), None + ) + + # get title + title = first(lambda s: get_safe(s, "title.title.value", "")) + + # get publisher + publisher = first(lambda s: get_safe(s, "journal-title.value", "")) + + # get date + date = ( + get_safe(work, "last-modified-date.value") + or first(lambda s: get_safe(s, "last-modified-date.value")) + or get_safe(work, "created-date.value") + or first(lambda s: get_safe(s, "created-date.value")) + or 0 + ) + + # get link + link = first(lambda s: get_safe(s, "url.value", "")) + + # keep available details + if title: + source["title"] = title + if publisher: + source["publisher"] = publisher + if date: + source["date"] = format_date(date) + if link: + source["link"] = link + + # copy fields from entry to source + source.update(entry) + + # add source to list + sources.append(source) + + return sources diff --git a/_cite/plugins/pubmed.py b/_cite/plugins/pubmed.py new file mode 100644 index 0000000..0f2ee60 --- /dev/null +++ b/_cite/plugins/pubmed.py @@ -0,0 +1,46 @@ +import json +from urllib.request import Request, urlopen +from urllib.parse import quote +from util import * + + +def main(entry): + """ + receives single list entry from pubmed data file + returns list of sources to cite + """ + + # ncbi api + endpoint = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=$TERM&retmode=json&retmax=1000&usehistory=y" + + # get id from entry + _id = get_safe(entry, "term", "") + if not _id: + raise Exception('No "term" key') + + # query api + @log_cache + @cache.memoize(name=__file__, expire=1 * (60 * 60 * 24)) + def query(_id): + url = endpoint.replace("$TERM", quote(_id)) + request = Request(url=url) + response = json.loads(urlopen(request).read()) + return get_safe(response, "esearchresult.idlist", []) + + response = query(_id) + + # list of sources to return + sources = [] + + # go through response and format sources + for _id in response: + # create source + source = {"id": f"pubmed:{_id}"} + + # copy fields from entry to source + source.update(entry) + + # add source to list + sources.append(source) + + return sources diff --git a/_cite/plugins/sources.py b/_cite/plugins/sources.py new file mode 100644 index 0000000..9dde4fa --- /dev/null +++ b/_cite/plugins/sources.py @@ -0,0 +1,6 @@ +def main(entry): + """ + receives single list entry from sources data file + returns list of sources to cite + """ + return [entry] diff --git a/_cite/requirements.txt b/_cite/requirements.txt new file mode 100644 index 0000000..9808e76 --- /dev/null +++ b/_cite/requirements.txt @@ -0,0 +1,7 @@ +manubot~=0.6 +PyYAML~=6.0 +diskcache~=5.6 +rich~=13.6 +python-dotenv~=0.21 +google-search-results~=2.4 + diff --git a/_cite/util.py b/_cite/util.py new file mode 100644 index 0000000..466a7d5 --- /dev/null +++ b/_cite/util.py @@ -0,0 +1,234 @@ +""" +utility functions for cite process and plugins +""" + +import subprocess +import json +import yaml +from yaml.loader import SafeLoader +from pathlib import Path +from datetime import datetime +from rich import print +from diskcache import Cache + + +# cache for time-consuming network requests +cache = Cache("./_cite/.cache") + + +# clear expired items from cache +cache.expire() + + +def log_cache(func): + """ + decorator to use around memoized function to log if cached or or not + """ + + def wrap(*args): + key = func.__cache_key__(*args) + if key in cache: + log(" (from cache)", level="INFO", newline=False) + return func(*args) + + return wrap + + +def log(message="\n--------------------\n", indent=0, level="", newline=True): + """ + log to terminal, color determined by indent and level + """ + + palette = { + 0: "[orange1]", + 1: "[salmon1]", + 2: "[violet]", + 3: "[sky_blue1]", + "ERROR": "[white on #F43F5E]", + "WARNING": "[black on #EAB308]", + "SUCCESS": "[black on #10B981]", + "INFO": "[grey70]", + } + color = get_safe(palette, level, "") or get_safe(palette, indent, "") or "[white]" + if newline: + print() + print(indent * " " + color + str(message) + "[/]", end="", flush=True) + + +def label(entry): + """ + get "label" of dict entry (for logging purposes) + """ + + return str(list(entry.keys())[0]) + ": " + str(list(entry.values())[0]) + + +def get_safe(item, path, default=None): + """ + safely access value in nested lists/dicts + """ + + for part in str(path).split("."): + try: + part = int(part) + except ValueError: + part = part + try: + item = item[part] + except (KeyError, IndexError, AttributeError, TypeError): + return default + return item + + +def list_of_dicts(data): + """ + check if data is list of dicts + """ + + return isinstance(data, list) and all(isinstance(entry, dict) for entry in data) + + +def format_date(date): + """ + format date as YYYY-MM-DD, or no date if malformed + """ + + if isinstance(date, int): + return datetime.fromtimestamp(date // 1000.0).strftime("%Y-%m-%d") + try: + return datetime.strptime(date, "%Y-%m-%d").strftime("%Y-%m-%d") + except Exception: + return "" + + +def load_data(path): + """ + read data from yaml or json file + """ + + # convert to path object + path = Path(path) + + # check if file exists + if not path.is_file(): + raise Exception("Can't find file") + + # try to open file + try: + file = open(path, encoding="utf8") + except Exception as e: + raise Exception(e or "Can't open file") + + # try to parse as yaml + try: + with file: + data = yaml.load(file, Loader=SafeLoader) + except Exception: + raise Exception("Can't parse file. Make sure it's valid YAML.") + + # if no errors, return data + return data + + +def save_data(path, data): + """ + write data to yaml file + """ + + # convert to path object + path = Path(path) + + # try to open file + try: + file = open(path, mode="w") + except Exception: + raise Exception("Can't open file for writing") + + # prevent yaml anchors/aliases (pointers) + yaml.Dumper.ignore_aliases = lambda *args: True + + # try to save data as yaml + try: + with file: + yaml.dump(data, file, default_flow_style=False, sort_keys=False) + except Exception: + raise Exception("Can't save YAML to file") + + # write warning note to top of file + note = "# DO NOT EDIT, GENERATED AUTOMATICALLY" + try: + with open(path, "r") as file: + data = file.read() + with open(path, "w") as file: + file.write(f"{note}\n\n{data}") + except Exception: + raise Exception("Can't write to file") + + +@log_cache +@cache.memoize(name="manubot", expire=90 * (60 * 60 * 24)) +def cite_with_manubot(_id): + """ + generate citation data for source id with Manubot + """ + + # run Manubot + try: + commands = ["manubot", "cite", _id, "--log-level=WARNING"] + output = subprocess.Popen(commands, stdout=subprocess.PIPE).communicate() + except Exception as e: + log(e, 3) + raise Exception("Manubot could not generate citation") + + # parse results as json + try: + manubot = json.loads(output[0])[0] + except Exception: + raise Exception("Couldn't parse Manubot response") + + # new citation with only needed info + citation = {} + + # original id + citation["id"] = _id + + # title + citation["title"] = get_safe(manubot, "title", "").strip() + + # authors + citation["authors"] = [] + for author in get_safe(manubot, "author", {}): + given = get_safe(author, "given", "").strip() + family = get_safe(author, "family", "").strip() + if given or family: + citation["authors"].append(" ".join([given, family])) + + # publisher + container = get_safe(manubot, "container-title", "").strip() + collection = get_safe(manubot, "collection-title", "").strip() + publisher = get_safe(manubot, "publisher", "").strip() + citation["publisher"] = container or publisher or collection or "" + + # extract date part + def date_part(citation, index): + try: + return citation["issued"]["date-parts"][0][index] + except (KeyError, IndexError, TypeError): + return "" + + # date + year = date_part(manubot, 0) + if year: + # fallbacks for month and day + month = date_part(manubot, 1) or "1" + day = date_part(manubot, 2) or "1" + citation["date"] = format_date(f"{year}-{month}-{day}") + else: + # if no year, consider date missing data + citation["date"] = "" + + # link + citation["link"] = get_safe(manubot, "URL", "").strip() + + # return citation data + return citation diff --git a/_config.yaml b/_config.yaml new file mode 100644 index 0000000..1c6b790 --- /dev/null +++ b/_config.yaml @@ -0,0 +1,73 @@ +# site properties and page defaults +title: Lab Website Template +subtitle: by the Greene Lab +description: An easy-to-use, flexible website template for labs, with automatic citations, GitHub tag imports, pre-built components, and more. +header: images/background.jpg +footer: images/background.jpg +proofer: false + +# site social media and other links +links: + email: contact@your-lab.com + orcid: 0000-0001-8713-9213 + google-scholar: ETJoidYAAAAJ + github: your-lab + twitter: YourLabHandle + youtube: YourLabChannel + +### jekyll settings + +# front matter defaults +defaults: + # all markdown files + - scope: + path: "" + values: + layout: default + # markdown files in /_members + - scope: + type: "members" + values: + layout: member + # markdown files in /_posts + - scope: + type: "posts" + values: + layout: post + +collections: + # generate page for each member + members: + output: true + # generate page for each post + posts: + output: true + +# jekyll plugins +plugins: + - jekyll-spaceship + - jekyll-sitemap + - jekyll-redirect-from + - jekyll-feed + - jekyll-last-modified-at + +# code block syntax highlighting +highlighter: rouge + +# jekyll theme +theme: null + +# sass settings +sass: + sass_dir: _styles + +# force jekyll to include certain files/folders +include: + - _styles + - _scripts + +# force jekyll to exclude certain files/folders +exclude: + - README.md + - LICENSE.md + - CITATION.cff diff --git a/_data/citations.yaml b/_data/citations.yaml new file mode 100644 index 0000000..db4e4b0 --- /dev/null +++ b/_data/citations.yaml @@ -0,0 +1,247 @@ +# DO NOT EDIT, GENERATED AUTOMATICALLY + +- id: doi:10.1093/nar/gkad1082 + title: "The Monarch Initiative in 2024: an analytic platform integrating phenotypes,\ + \ genes\_and diseases across species" + authors: + - Tim E Putman + - Kevin Schaper + - Nicolas Matentzoglu + - "Vincent\_P Rubinetti" + - "Faisal\_S Alquaddoomi" + - Corey Cox + - J Harry Caufield + - Glass Elsarboukh + - Sarah Gehrke + - Harshad Hegde + - "Justin\_T Reese" + - Ian Braun + - "Richard\_M Bruskiewich" + - Luca Cappelletti + - Seth Carbon + - "Anita\_R Caron" + - "Lauren\_E Chan" + - "Christopher\_G Chute" + - "Katherina\_G Cortes" + - "Vin\xEDcius De\_Souza" + - Tommaso Fontana + - "Nomi\_L Harris" + - "Emily\_L Hartley" + - Eric Hurwitz + - "Julius\_O B Jacobsen" + - Madan Krishnamurthy + - "Bryan\_J Laraway" + - "James\_A McLaughlin" + - "Julie\_A McMurry" + - "Sierra\_A T Moxon" + - "Kathleen\_R Mullen" + - "Shawn\_T O\u2019Neil" + - "Kent\_A Shefchek" + - Ray Stefancsik + - Sabrina Toro + - "Nicole\_A Vasilevsky" + - "Ramona\_L Walls" + - "Patricia\_L Whetzel" + - David Osumi-Sutherland + - Damian Smedley + - "Peter\_N Robinson" + - "Christopher\_J Mungall" + - "Melissa\_A Haendel" + - "Monica\_C Munoz-Torres" + publisher: Nucleic Acids Research + date: '2023-11-24' + link: https://doi.org/gs6kmr + orcid: 0000-0002-4655-3773 + plugin: orcid.py + file: orcid.yaml +- id: doi:10.1101/2023.10.11.560955 + title: Integration of 168,000 samples reveals global patterns of the human gut microbiome + authors: + - Richard J. Abdill + - Samantha P. Graham + - Vincent Rubinetti + - Frank W. Albert + - Casey S. Greene + - Sean Davis + - Ran Blekhman + publisher: Cold Spring Harbor Laboratory + date: '2023-10-11' + link: https://doi.org/gsvf5z + orcid: 0000-0002-4655-3773 + plugin: orcid.py + file: orcid.yaml +- id: doi:10.1093/nar/gkad289 + title: 'MyGeneset.info: an interactive and programmatic platform for community-curated + and user-created collections of genes' + authors: + - Ricardo Avila + - Vincent Rubinetti + - Xinghua Zhou + - Dongbo Hu + - Zhongchao Qian + - Marco Alvarado Cano + - Everaldo Rodolpho + - Ginger Tsueng + - Casey Greene + - Chunlei Wu + publisher: Nucleic Acids Research + date: '2023-04-18' + link: https://doi.org/gr5hb5 + orcid: 0000-0002-4655-3773 + plugin: orcid.py + file: orcid.yaml +- id: doi:10.1101/2023.01.05.522941 + title: Hetnet connectivity search provides rapid insights into how two biomedical + entities are related + authors: + - Daniel S. Himmelstein + - Michael Zietz + - Vincent Rubinetti + - Kyle Kloster + - Benjamin J. Heil + - Faisal Alquaddoomi + - Dongbo Hu + - David N. Nicholson + - Yun Hao + - Blair D. Sullivan + - Michael W. Nagle + - Casey S. Greene + publisher: Cold Spring Harbor Laboratory + date: '2023-01-07' + link: https://doi.org/grmcb9 + orcid: 0000-0002-4655-3773 + plugin: orcid.py + file: orcid.yaml +- id: doi:10.1093/gigascience/giad047 + title: Hetnet connectivity search provides rapid insights into how biomedical entities + are related + authors: + - Daniel S Himmelstein + - Michael Zietz + - Vincent Rubinetti + - Kyle Kloster + - Benjamin J Heil + - Faisal Alquaddoomi + - Dongbo Hu + - David N Nicholson + - Yun Hao + - Blair D Sullivan + - Michael W Nagle + - Casey S Greene + publisher: GigaScience + date: '2022-12-28' + link: https://doi.org/gsd85n + orcid: 0000-0002-4655-3773 + plugin: orcid.py + file: orcid.yaml +- id: doi:10.1101/2022.02.18.461833 + title: 'MolEvolvR: A web-app for characterizing proteins using molecular evolution + and phylogeny' + authors: + - Jacob D Krol + - Joseph T Burke + - Samuel Z Chen + - Lo Sosinski + - Faisal S Alquaddoomi + - Evan P Brenner + - Ethan P Wolfe + - Vince P Rubinetti + - Shaddai Amolitos + - Kellen M Reason + - John B Johnston + - Janani Ravi + publisher: Cold Spring Harbor Laboratory + date: '2022-02-22' + link: https://doi.org/gstx7j + orcid: 0000-0002-4655-3773 + plugin: orcid.py + file: orcid.yaml +- id: doi:10.1186/s13059-020-02021-3 + title: Compressing gene expression data using multiple latent space dimensionalities + learns complementary biological representations + authors: + - Gregory P. Way + - Michael Zietz + - Vincent Rubinetti + - Daniel S. Himmelstein + - Casey S. Greene + publisher: Genome Biology + date: '2020-05-11' + link: https://doi.org/gg2mjh + orcid: 0000-0002-4655-3773 + plugin: orcid.py + file: orcid.yaml +- id: doi:10.1371/journal.pcbi.1007128 + title: Open collaborative writing with Manubot + authors: + - Daniel S. Himmelstein + - Vincent Rubinetti + - David R. Slochower + - Dongbo Hu + - Venkat S. Malladi + - Casey S. Greene + - Anthony Gitter + publisher: PLOS Computational Biology + date: '2020-12-04' + link: https://doi.org/c7np + orcid: 0000-0002-4655-3773 + plugin: sources.py + file: sources.yaml + type: paper + description: Lorem ipsum _dolor_ **sit amet**, consectetur adipiscing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna aliqua. + image: https://journals.plos.org/ploscompbiol/article/figure/image?size=inline&id=info:doi/10.1371/journal.pcbi.1007128.g001&rev=2 + buttons: + - type: manubot + link: https://greenelab.github.io/meta-review/ + - type: source + text: Manuscript Source + link: https://github.com/greenelab/meta-review + - type: website + link: http://manubot.org/ + tags: + - open science + - collaboration + repo: greenelab/meta-review +- id: doi:10.1101/573782 + title: Sequential compression of gene expression across dimensionalities and methods + reveals no single best method or dimensionality + authors: + - Gregory P. Way + - Michael Zietz + - Vincent Rubinetti + - Daniel S. Himmelstein + - Casey S. Greene + publisher: Cold Spring Harbor Laboratory + date: '2019-03-11' + link: https://doi.org/gfxjxf + orcid: 0000-0002-4655-3773 + plugin: orcid.py + file: orcid.yaml +- id: doi:10.1016/j.csbj.2020.05.017 + title: Constructing knowledge graphs and their biomedical applications + authors: + - David N. Nicholson + - Casey S. Greene + publisher: Computational and Structural Biotechnology Journal + date: '2020-01-01' + link: https://doi.org/gg7m48 + image: https://ars.els-cdn.com/content/image/1-s2.0-S2001037020302804-gr1.jpg + plugin: sources.py + file: sources.yaml +- id: doi:10.7554/eLife.32822 + title: Sci-Hub provides access to nearly all scholarly literature + authors: + - Daniel S Himmelstein + - Ariel Rodriguez Romero + - Jacob G Levernier + - Thomas Anthony Munro + - Stephen Reid McLaughlin + - Bastian Greshake Tzovaras + - Casey S Greene + publisher: eLife + date: '2018-03-01' + link: https://doi.org/ckcj + image: https://iiif.elifesciences.org/lax:32822%2Felife-32822-fig8-v3.tif/full/863,/0/default.webp + plugin: sources.py + file: sources.yaml diff --git a/_data/orcid.yaml b/_data/orcid.yaml new file mode 100644 index 0000000..005d380 --- /dev/null +++ b/_data/orcid.yaml @@ -0,0 +1 @@ +- orcid: 0000-0002-4655-3773 diff --git a/_data/projects.yaml b/_data/projects.yaml new file mode 100644 index 0000000..b3eb2a7 --- /dev/null +++ b/_data/projects.yaml @@ -0,0 +1,47 @@ +- title: Cool Dataset + subtitle: a subtitle + group: featured + image: images/photo.jpg + link: https://github.com/ + description: Lorem ipsum _dolor sit amet_, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + repo: greenelab/lab-website-template + tags: + - resource + +- title: Cool Package + subtitle: a subtitle + group: featured + image: images/photo.jpg + link: https://github.com/ + description: Lorem ipsum _dolor sit amet_, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + repo: greenelab/lab-website-template + tags: + - resource + +- title: Cool Tutorial + subtitle: a subtitle + image: images/photo.jpg + link: https://github.com/ + description: Lorem ipsum _dolor sit amet_, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + repo: greenelab/lab-website-template + tags: + - resource + - publication + +- title: Cool Web App + subtitle: a subtitle + image: images/photo.jpg + link: https://github.com/ + description: Lorem ipsum _dolor sit amet_, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + repo: greenelab/lab-website-template + tags: + - software + +- title: Cool Web Server + subtitle: a subtitle + image: images/photo.jpg + link: https://github.com/ + description: Lorem ipsum _dolor sit amet_, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + repo: greenelab/lab-website-template + tags: + - software diff --git a/_data/sources.yaml b/_data/sources.yaml new file mode 100644 index 0000000..62cd922 --- /dev/null +++ b/_data/sources.yaml @@ -0,0 +1,23 @@ +- id: doi:10.1371/journal.pcbi.1007128 + type: paper + description: Lorem ipsum _dolor_ **sit amet**, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + date: 2020-12-4 + image: https://journals.plos.org/ploscompbiol/article/figure/image?size=inline&id=info:doi/10.1371/journal.pcbi.1007128.g001&rev=2 + buttons: + - type: manubot + link: https://greenelab.github.io/meta-review/ + - type: source + text: Manuscript Source + link: https://github.com/greenelab/meta-review + - type: website + link: http://manubot.org/ + tags: + - open science + - collaboration + repo: greenelab/meta-review + +- id: doi:10.1016/j.csbj.2020.05.017 + image: https://ars.els-cdn.com/content/image/1-s2.0-S2001037020302804-gr1.jpg + +- id: doi:10.7554/eLife.32822 + image: https://iiif.elifesciences.org/lax:32822%2Felife-32822-fig8-v3.tif/full/863,/0/default.webp diff --git a/_data/types.yaml b/_data/types.yaml new file mode 100644 index 0000000..2909b0a --- /dev/null +++ b/_data/types.yaml @@ -0,0 +1,216 @@ +# map general type to default/fallback icon, text, etc. + +# team member roles + +pi: + icon: fa-solid fa-microscope + description: Principal Investigator + +postdoc: + icon: fa-solid fa-glasses + description: Postdoctoral Researcher + +phd: + icon: fa-solid fa-graduation-cap + description: PhD Student + +undergrad: + icon: fa-solid fa-user-graduate + description: Undergraduate Student + +programmer: + icon: fa-solid fa-code + description: Programmer + +mascot: + icon: fa-solid fa-dog + description: Mascot + +# general + +link: + icon: fa-solid fa-globe + text: Link + tooltip: Link + +website: + icon: fa-solid fa-globe + text: Website + tooltip: Website + +external: + icon: fa-solid fa-up-right-from-square + text: External Link + tooltip: External link + +home-page: + icon: fa-solid fa-house-user + text: Home Page + tooltip: Home page + +email: + icon: fa-solid fa-envelope + text: Email + tooltip: Email + link: mailto:$VALUE + +phone: + icon: fa-solid fa-phone + text: Phone Number + tooltip: Phone number + link: tel:$VALUE + +address: + icon: fa-solid fa-map-location-dot + text: Address + tooltip: Address + +search: + icon: fa-solid fa-magnifying-glass + text: Search + tooltip: Search + +# social media + +orcid: + icon: fa-brands fa-orcid + text: ORCID + tooltip: ORCID + link: https://orcid.org/$VALUE + +google-scholar: + icon: fa-brands fa-google + text: Google Scholar + tooltip: Google Scholar + link: https://scholar.google.com/citations?user=$VALUE + +github: + icon: fa-brands fa-github + text: GitHub + tooltip: GitHub + link: https://github.com/$VALUE + +twitter: + icon: fa-brands fa-twitter + text: Twitter + tooltip: Twitter + link: https://twitter.com/$VALUE + +facebook: + icon: fa-brands fa-facebook + text: Facebook + tooltip: Facebook + link: https://facebook.com/$VALUE + +instagram: + icon: fa-brands fa-instagram + text: Instagram + tooltip: Instagram + link: https://instagram.com/$VALUE + +youtube: + icon: fa-brands fa-youtube + text: YouTube + tooltip: YouTube + link: https://youtube.com/$VALUE + +linkedin: + icon: fa-brands fa-linkedin + text: LinkedIn + tooltip: LinkedIn + link: https://www.linkedin.com/in/$VALUE + +# alerts + +tip: + icon: fa-solid fa-lightbulb + color: "#d946ef" + +help: + icon: fa-solid fa-circle-question + color: "#6366f1" + +info: + icon: fa-solid fa-circle-info + color: "#0ea5e9" + +success: + icon: fa-solid fa-circle-check + color: "#22c55e" + +warning: + icon: fa-solid fa-circle-exclamation + color: "#F59E0B" + +error: + icon: fa-solid fa-ban + color: "#ef4444" + +# publications + +paper: + icon: fa-solid fa-scroll + text: Paper + tooltip: Paper + +book: + icon: fa-solid fa-book + text: Book + tooltip: Book + +article: + icon: fa-solid fa-newspaper + text: Article + tooltip: Article + +journal: + icon: fa-regular fa-newspaper + text: Journal + tooltip: Journal + +preprint: + icon: fa-regular fa-eye + text: Preprint + tooltip: Preprint + +unpublished: + icon: fa-regular fa-eye + text: Unpublished + tooltip: Unpublished + +manubot: + icon: manubot.svg + text: Manubot + tooltip: Manubot + +# software + +docs: + icon: fa-solid fa-book + text: Documentation + tooltip: Documentation + +source: + icon: fa-solid fa-code + text: Source + tooltip: Source code + +server: + icon: fa-solid fa-server + text: Server + tooltip: Server + +app: + icon: fa-solid fa-hand-pointer + text: App + tooltip: App + +data: + icon: fa-solid fa-database + text: Data + tooltip: Data + +package: + icon: fa-solid fa-box + text: Package + tooltip: Package diff --git a/_includes/alert.html b/_includes/alert.html new file mode 100644 index 0000000..6fc9878 --- /dev/null +++ b/_includes/alert.html @@ -0,0 +1,10 @@ +{% assign color = site.data.types[include.type].color | default: "#808080" %} +
+ {% assign icon = site.data.types[include.type].icon + | default: "fa-solid fa-circle-info" + %} + {% include icon.html icon=icon %} +
+ {{ include.content | markdownify }} +
+
diff --git a/_includes/analytics.html b/_includes/analytics.html new file mode 100644 index 0000000..6af6788 --- /dev/null +++ b/_includes/analytics.html @@ -0,0 +1,3 @@ + diff --git a/_includes/button.html b/_includes/button.html new file mode 100644 index 0000000..8d1730d --- /dev/null +++ b/_includes/button.html @@ -0,0 +1,24 @@ +{% assign button = include %} +{% assign button = button | hash_default: site.data.types[include.type] %} + +{% if button.link or button.icon or button.text %} + +{% endif %} diff --git a/_includes/card.html b/_includes/card.html new file mode 100644 index 0000000..600cc97 --- /dev/null +++ b/_includes/card.html @@ -0,0 +1,47 @@ +{{ " " }} +
+ + {{ include.title | default: + + +
+ {% if include.title %} + + {{ include.title }} + + {% endif %} + + {% if include.subtitle %} + {{ include.subtitle }} + {% endif %} + + {% if include.description %} +

+ {{ include.description | markdownify | remove: "

" | remove: "

" }} +

+ {% endif %} + + {% if include.tags or include.repo %} + {% include tags.html tags=include.tags repo=include.repo %} + {% endif %} +
+
diff --git a/_includes/citation.html b/_includes/citation.html new file mode 100644 index 0000000..3eb8b2b --- /dev/null +++ b/_includes/citation.html @@ -0,0 +1,109 @@ +{% if include.lookup %} + {% assign citation = site.data.citations + | where_exp: "citation", + "citation.id == include.lookup or citation.title contains include.lookup" + | first + %} +{% else %} + {% assign citation = include %} +{% endif %} + +
+
+ {% if include.style == "rich" %} + + {{ citation.title | default: + + {% endif %} + +
+ {% assign type = site.data.types[citation.type] %} + {% include icon.html icon=type.icon %} + + + {{ citation.title | default: "[no title info]" }} + + +
10 %} + data-tooltip="{{ citation.authors | join: ", " }} " + {% endif %} + tabindex="0" + > + {{ + citation.authors + | join: "," + | split: "," + | array_carve: 5 + | join: ", " + | markdownify + | remove: "

" | remove: "

" + | default: "[no author info]" + }} +
+ +
+ + {{- citation.publisher | default: "[no publisher info]" -}} + +  ·  + + {{- citation.date | default: "[no date info]" | date: "%d %b %Y" -}} + +  ·  + + {{- citation.id | default: "[no id info]" -}} + +
+ + {% if include.style == "rich" %} + {% if citation.description %} +
+ {{ + citation.description + | markdownify + | remove: "

" + | remove: "

" + }} +
+ {% endif %} + + {% if citation.buttons.size > 0 %} +
+ {% for button in citation.buttons %} + {% + include button.html + type=button.type + icon=button.icon + text=button.text + link=button.link + style="bare" + %} + {% endfor %} +
+ {% endif %} + + {% if citation.tags.size > 0 or citation.repo %} + {% include tags.html tags=citation.tags repo=citation.repo %} + {% endif %} + {% endif %} +
+
+
diff --git a/_includes/cols.html b/_includes/cols.html new file mode 100644 index 0000000..20dd865 --- /dev/null +++ b/_includes/cols.html @@ -0,0 +1,6 @@ +
+ {% for param in include %} + {% assign key = param[0] %} +
{{ include[key] | markdownify }}
+ {% endfor %} +
diff --git a/_includes/content.html b/_includes/content.html new file mode 100644 index 0000000..f9d01c8 --- /dev/null +++ b/_includes/content.html @@ -0,0 +1,32 @@ + + +{% assign content = include.content %} + +{% assign sections = content | split: "" | array_filter %} + +{% for section in sections %} + {% assign dark = section | regex_scan: "dark: (.*);" | default: "" %} + {% assign background = section + | regex_scan: "background: (.*);" + | default: nil + %} + {% assign size = section | regex_scan: "size: (.*);" | default: "page" %} + +
+ {{ section }} +
+{% endfor %} diff --git a/_includes/fallback.html b/_includes/fallback.html new file mode 100644 index 0000000..5d60a80 --- /dev/null +++ b/_includes/fallback.html @@ -0,0 +1 @@ +onerror="this.src = '{{ "images/fallback.svg" | relative_url }}'; this.onerror = null;" diff --git a/_includes/feature.html b/_includes/feature.html new file mode 100644 index 0000000..27e33fa --- /dev/null +++ b/_includes/feature.html @@ -0,0 +1,27 @@ +
+ + {{ include.title | default: + +
+ {% if include.title %} +

{{ include.title }}

+ {% endif %} + {{ include.text | markdownify }} +
+
diff --git a/_includes/figure.html b/_includes/figure.html new file mode 100644 index 0000000..a426ff4 --- /dev/null +++ b/_includes/figure.html @@ -0,0 +1,25 @@ +
+ + {{ include.caption | default: + + {% if include.caption %} +
+ {{ include.caption | markdownify | remove: "

" | remove: "

" }} +
+ {% endif %} +
diff --git a/_includes/float.html b/_includes/float.html new file mode 100644 index 0000000..a5d2955 --- /dev/null +++ b/_includes/float.html @@ -0,0 +1,11 @@ +
+ {{ include.content | markdownify }} +
diff --git a/_includes/fonts.html b/_includes/fonts.html new file mode 100644 index 0000000..153709b --- /dev/null +++ b/_includes/fonts.html @@ -0,0 +1,17 @@ + + +{% assign googlefonts = "_styles/-theme.scss" | file_read | google_fonts %} + + + + +{% assign fontawesome = "https://use.fontawesome.com/releases/v6.5.0/css/all.css" %} + + diff --git a/_includes/footer.html b/_includes/footer.html new file mode 100644 index 0000000..8230d53 --- /dev/null +++ b/_includes/footer.html @@ -0,0 +1,42 @@ +{% assign image = page.footer | default: site.footer | relative_url %} +{% assign dark = page.footer-dark | is_nil: site.footer-dark | is_nil: true %} + +
+ + +
+ {% for link in site.links %} + {% assign key = link[0] %} + {% assign value = link[1] %} + {% include button.html type=key text="" link=value style="bare" %} + {% endfor %} +
+ +
+ © {{ site.time | date: "%Y" }} + {{ site.title }} +   |   Built with + + Lab Website Template + +
+ + +
diff --git a/_includes/grid.html b/_includes/grid.html new file mode 100644 index 0000000..40ea2cf --- /dev/null +++ b/_includes/grid.html @@ -0,0 +1,3 @@ +
+ {{ include.content | markdownify }} +
diff --git a/_includes/head.html b/_includes/head.html new file mode 100644 index 0000000..c21a44a --- /dev/null +++ b/_includes/head.html @@ -0,0 +1,8 @@ + + {% include analytics.html %} + {% include verification.html %} + {% include meta.html %} + {% include fonts.html %} + {% include styles.html %} + {% include scripts.html %} + diff --git a/_includes/header.html b/_includes/header.html new file mode 100644 index 0000000..4dbdac0 --- /dev/null +++ b/_includes/header.html @@ -0,0 +1,59 @@ +{% assign image = page.header | default: site.header | relative_url %} +{% assign dark = page.header-dark | is_nil: site.header-dark | is_nil: true %} + +{% assign svg = "images/logo.svg" | file_exists %} +{% assign png = "images/logo.png" | file_exists %} +{% assign jpg = "images/logo.jpg" | file_exists %} +{% assign logo = svg | default: png | default: jpg | default: nil %} + +
+ + {% if logo %} + + {% endif %} + {% if site.logo-text != false %} + + {% if site.title and site.show-title != false %} + {{ site.title }} + {% endif %} + {% if site.subtitle and site.show-subtitle != false %} + {{ site.subtitle }} + {% endif %} + + {% endif %} + + + + + +
diff --git a/_includes/icon.html b/_includes/icon.html new file mode 100644 index 0000000..0448e93 --- /dev/null +++ b/_includes/icon.html @@ -0,0 +1,10 @@ +{%- if include.icon contains ".svg" -%} + {%- capture inline -%} + {%- include {{ include.icon }} -%} + {%- endcapture -%} + + {{- inline | strip_newlines -}} + +{%- elsif include.icon and include.icon != "" -%} + +{%- endif -%} diff --git a/_includes/list.html b/_includes/list.html new file mode 100644 index 0000000..92026fd --- /dev/null +++ b/_includes/list.html @@ -0,0 +1,58 @@ +{% assign emptyarray = "" | split: "," %} +{% assign data = site.data[include.data] + | default: site[include.data] + | default: emptyarray + | data_filter: include.filters +%} + +{% assign years = data + | group_by_exp: "d", "d.date | date: '%Y'" + | sort: "name" + | reverse +%} + +{% for year in years %} + {% assign data = year.items %} + + {% if years.size > 1 %} + {{--}}

{{ year.name }}

+ {% assign data = data | sort: "date" | reverse %} + {% endif %} + + {% for d in data %} + {% assign style = d.style | default: include.style %} + + {% + include {{ include.component | append: ".html" }} + author=d.author + authors=d.authors + buttons=d.buttons + caption=d.caption + content=d.content + date=d.date + description=d.description + excerpt=d.excerpt + height=d.height + icon=d.icon + id=d.id + image=d.image + last_modified_at=d.last_modified_at + link=d.link + lookup=d.lookup + name=d.name + publisher=d.publisher + repo=d.repo + role=d.role + slug=d.slug + style=style + subtitle=d.subtitle + tags=d.tags + text=d.text + title=d.title + tooltip=d.tooltip + type=d.type + url=d.url + width=d.width + %} + {% endfor %} +{% endfor %} diff --git a/_includes/manubot.svg b/_includes/manubot.svg new file mode 100644 index 0000000..24b0c2e --- /dev/null +++ b/_includes/manubot.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + diff --git a/_includes/meta.html b/_includes/meta.html new file mode 100644 index 0000000..1336e30 --- /dev/null +++ b/_includes/meta.html @@ -0,0 +1,99 @@ +{% assign filename = page.path | split: "/" | last %} +{% if page.name and page.name != filename %} + {% assign title = page.name %} +{% elsif page.title %} + {% assign title = page.title %} +{% else %} + {% assign title = nil %} +{% endif %} + +{% assign fulltitle = "" | split: "," %} +{% if title %} + {% assign fulltitle = fulltitle | push: title %} +{% endif %} +{% if site.title %} + {% assign fulltitle = fulltitle | push: site.title %} +{% endif %} +{% assign fulltitle = fulltitle | join: " | " %} + +{% assign subtitle = site.subtitle %} + +{% assign description = page.description | default: site.description %} +{% if site.subtitle %} + {% capture description -%} + {{ site.subtitle }}. {{ description }} + {%- endcapture %} +{% endif %} +{% capture url -%} + {{ site.url }}{{ site.baseurl }} +{%- endcapture %} + +{% assign png = "images/icon.png" | file_exists %} +{% assign jpg = "images/icon.jpg" | file_exists %} +{% assign icon = png | default: jpg | relative_url %} + +{% assign jpg = "images/share.jpg" | file_exists %} +{% assign png = "images/share.png" | file_exists %} +{% assign share = jpg | default: png | relative_url %} + +{% assign published = page.date | date_to_xmlschema %} +{% assign updated = page.last_modified_at | date_to_xmlschema %} + +{% assign feed = "feed.xml" | absolute_url %} + + + +{{ fulltitle }} + + + + + + + + + + + + + + + + + + + +{% if page.author %} + + + + + + +{% else %} + +{% endif %} + + + + diff --git a/_includes/portrait.html b/_includes/portrait.html new file mode 100644 index 0000000..3b77f3d --- /dev/null +++ b/_includes/portrait.html @@ -0,0 +1,45 @@ +{% if include.lookup %} + {% assign member = site.members + | where_exp: "member", "member.slug == include.lookup" + | first + %} +{% else %} + {% assign member = include %} +{% endif %} + + diff --git a/_includes/post-excerpt.html b/_includes/post-excerpt.html new file mode 100644 index 0000000..d01e111 --- /dev/null +++ b/_includes/post-excerpt.html @@ -0,0 +1,60 @@ +{% if include.lookup %} + {% assign post = site.posts + | where_exp: "post", "post.slug == include.lookup" + | first + | default: include + %} +{% else %} + {% assign post = include %} +{% endif %} + +
+
+ {% assign url = post.url %} + {% assign title = post.title %} + {% assign image = post.image %} + + {% if image %} + + {{ title | default: + + {% endif %} + +
+ {{ title }} + + {% + include post-info.html + author=post.author + published=post.date + updated=post.last_modified_at + tags=post.tags + %} + + {% assign excerpt = post.content + | default: "" + | regex_scan: "(.*)", true + | default: post.excerpt + | default: "" + | strip_html + %} + {% assign search = post.content + | strip_html + | strip_newlines + | regex_strip + %} +

+ {{ excerpt }} +

+
+
+
diff --git a/_includes/post-info.html b/_includes/post-info.html new file mode 100644 index 0000000..c0585d7 --- /dev/null +++ b/_includes/post-info.html @@ -0,0 +1,37 @@ + + +{% if include.tags %} + {% include tags.html tags=include.tags link="blog" %} +{% endif %} diff --git a/_includes/post-nav.html b/_includes/post-nav.html new file mode 100644 index 0000000..ac75bbf --- /dev/null +++ b/_includes/post-nav.html @@ -0,0 +1,18 @@ +
+ + {% if include.post.previous %} + {% include icon.html icon="fa-solid fa-angle-left" %} Previous post
+ + {{ include.post.previous.title }} + + {% endif %} +
+ + {% if include.post.next %} + Next post {% include icon.html icon="fa-solid fa-angle-right" %}
+ + {{ include.post.next.title }} + + {% endif %} +
+
diff --git a/_includes/scripts.html b/_includes/scripts.html new file mode 100644 index 0000000..30b7ac2 --- /dev/null +++ b/_includes/scripts.html @@ -0,0 +1,10 @@ + + + + + + +{% assign scripts = site.static_files | where_exp: "file", "file.path contains '/_scripts'" %} +{% for script in scripts %} + +{% endfor %} diff --git a/_includes/search-box.html b/_includes/search-box.html new file mode 100644 index 0000000..17d7a8b --- /dev/null +++ b/_includes/search-box.html @@ -0,0 +1,16 @@ + diff --git a/_includes/search-info.html b/_includes/search-info.html new file mode 100644 index 0000000..165fdd4 --- /dev/null +++ b/_includes/search-info.html @@ -0,0 +1 @@ +
diff --git a/_includes/section.html b/_includes/section.html new file mode 100644 index 0000000..d7255a8 --- /dev/null +++ b/_includes/section.html @@ -0,0 +1,10 @@ +{% comment %} + see content.html +{% endcomment %} + + + diff --git a/_includes/site-search.html b/_includes/site-search.html new file mode 100644 index 0000000..3926ff8 --- /dev/null +++ b/_includes/site-search.html @@ -0,0 +1,6 @@ +
+ + +
diff --git a/_includes/styles.html b/_includes/styles.html new file mode 100644 index 0000000..a506e87 --- /dev/null +++ b/_includes/styles.html @@ -0,0 +1,28 @@ + + + + + +{% assign styles = site.pages + | where_exp: "file", "file.url contains '/_styles'" +%} +{% for style in styles %} + {% unless style.url contains ".map" %} + + {% endunless %} +{% endfor %} + + +{% assign styles = site.static_files + | where_exp: "file", + "file.path contains '/_styles' and file.path contains '.css'" +%} +{% for style in styles %} + +{% endfor %} diff --git a/_includes/tags.html b/_includes/tags.html new file mode 100644 index 0000000..fb16266 --- /dev/null +++ b/_includes/tags.html @@ -0,0 +1,29 @@ +{% assign tags = include.tags + | object_items + | join: "," + | split: "," + | array_filter + | uniq +%} +{% assign link = include.link | default: page.dir | absolute_url %} +{% if tags.size > 0 or include.repo %} +
+ {% for tag in tags %} + + {{ tag }} + + {% endfor %} +
+{% endif %} diff --git a/_includes/verification.html b/_includes/verification.html new file mode 100644 index 0000000..e72f39b --- /dev/null +++ b/_includes/verification.html @@ -0,0 +1,3 @@ + diff --git a/_layouts/default.html b/_layouts/default.html new file mode 100644 index 0000000..b5ac58d --- /dev/null +++ b/_layouts/default.html @@ -0,0 +1,11 @@ + + + {% include head.html %} + + {% include header.html %} +
+ {% include content.html content=content %} +
+ {% include footer.html %} + + diff --git a/_layouts/member.html b/_layouts/member.html new file mode 100644 index 0000000..034b5d0 --- /dev/null +++ b/_layouts/member.html @@ -0,0 +1,51 @@ +--- +layout: default +--- + +{% capture floatcontent %} + +{% include portrait.html lookup=page.slug %} + +
+ {% for link in page.links %} + {% assign key = link[0] %} + {% assign value = link[1] %} + {% include button.html type=key link=value style="bare" %}
+ {% endfor %} +
+ +{% endcapture %} + +{% include float.html content=floatcontent %} + +{{ content }} + +{% assign aliases = page.aliases + | default: page.name + | default: page.title + | join: "," + | split: "," + | array_filter +%} + +{% capture search -%} + research/?search={% for alias in aliases %}"{{ alias }}" {% endfor %} +{%- endcapture %} + +

+ + Search for {{ page.name | default: page.title }}'s papers on the Research page + +

+ +{% capture search -%} + blog/?search={{ page.name }} +{%- endcapture %} + + diff --git a/_layouts/post.html b/_layouts/post.html new file mode 100644 index 0000000..09fd57b --- /dev/null +++ b/_layouts/post.html @@ -0,0 +1,24 @@ +--- +layout: default +--- + +{% include section.html background=page.image %} + +

{{ page.title }}

+ +{% + include post-info.html + author=page.author + member=page.member + published=page.date + updated=page.last_modified_at + tags=page.tags +%} + +{% include section.html %} + +{{ content }} + +{% include section.html %} + +{% include post-nav.html post=page %} diff --git a/_members/jane-smith.md b/_members/jane-smith.md new file mode 100644 index 0000000..f4cae89 --- /dev/null +++ b/_members/jane-smith.md @@ -0,0 +1,19 @@ +--- +name: Jane Smith +image: images/photo.jpg +role: pi +aliases: + - J. Smith + - J Smith +links: + home-page: https://janesmith.com + orcid: 0000-0001-8713-9213 +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Faucibus purus in massa tempor nec feugiat nisl pretium fusce. +Elit at imperdiet dui accumsan. +Duis tristique sollicitudin nibh sit amet commodo nulla facilisi. +Vitae elementum curabitur vitae nunc sed velit dignissim sodales. +Lacinia at quis risus sed vulputate odio ut. +Magna eget est lorem ipsum. diff --git a/_members/john-doe.md b/_members/john-doe.md new file mode 100644 index 0000000..0e441c6 --- /dev/null +++ b/_members/john-doe.md @@ -0,0 +1,10 @@ +--- +name: John Doe +image: images/photo.jpg +role: phd +group: alum +links: + github: john-doe +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. diff --git a/_members/sarah-johnson.md b/_members/sarah-johnson.md new file mode 100644 index 0000000..b0d97b4 --- /dev/null +++ b/_members/sarah-johnson.md @@ -0,0 +1,11 @@ +--- +name: Sarah Johnson +image: images/photo.jpg +description: Lead Programmer +role: programmer +links: + email: sarah.johnson@gmail.com + twitter: sarahjohnson +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. diff --git a/_plugins/array.rb b/_plugins/array.rb new file mode 100644 index 0000000..871626b --- /dev/null +++ b/_plugins/array.rb @@ -0,0 +1,25 @@ +require 'liquid' + +module Jekyll + module ArrayFilters + # filter out empty and trim entries in array + def array_filter(array) + return array + .map{|x| x.is_a?(String) ? x.strip() : x} + .select{|x| x and x != ""} + end + + # omit middle items of array with ellipsis, leave N items on either side + def array_carve(array, length = 3) + if array.length <= length * 2 + return array + else + left = array.slice(0, length) || [] + right = array.slice(-length, length) || [] + return [left, "...", right].flatten() + end + end + end +end + +Liquid::Template.register_filter(Jekyll::ArrayFilters) diff --git a/_plugins/file.rb b/_plugins/file.rb new file mode 100644 index 0000000..32a0af2 --- /dev/null +++ b/_plugins/file.rb @@ -0,0 +1,20 @@ +require 'liquid' + +module Jekyll + module FileFilters + # check if file exists + def file_exists(file) + path = File.join(Dir.getwd, file) + # pass back filename if exists + return File.file?(path) ? file : nil + end + + # read text contents of file + def file_read(file) + path = File.join(Dir.getwd, file) + return File.file?(path) ? File.read(path) : nil + end + end +end + +Liquid::Template.register_filter(Jekyll::FileFilters) diff --git a/_plugins/hash.rb b/_plugins/hash.rb new file mode 100644 index 0000000..2344589 --- /dev/null +++ b/_plugins/hash.rb @@ -0,0 +1,28 @@ +require 'liquid' + +module Jekyll + module HashFilters + # merge main hash with another hash of defaults + def hash_default(hash, defaults) + if not hash.is_a?(Hash) or not defaults.is_a?(Hash) + return hash + end + defaults.each do |key, value| + # substitute main string into default string and set main item + if value.is_a?(String) and value.include?"$VALUE" + if hash[key].is_a?(String) + hash[key] = value.sub"$VALUE", hash[key] + end + # set main item to default item if not defined + else + if hash[key] == nil or !hash.key?(key) + hash[key] = value + end + end + end + return hash + end + end +end + +Liquid::Template.register_filter(Jekyll::HashFilters) diff --git a/_plugins/misc.rb b/_plugins/misc.rb new file mode 100644 index 0000000..fe19bd8 --- /dev/null +++ b/_plugins/misc.rb @@ -0,0 +1,87 @@ +require 'liquid' +require 'html-proofer' + +module Jekyll + module MiscFilters + # fallback if value unspecified + def is_nil(value, fallback) + return value == nil ? fallback : value + end + + # get list of hash keys or array entries + def object_items(object) + if object.is_a?(Hash) + return object.keys + elsif object.is_a?(Array) + return object + end + return object + end + + # filter a list of hashes by comma-sep'd field:value pairs + def data_filter(data, filters) + if not data.is_a?(Array) or not filters.is_a?(String) + return data + end + data = data.clone + for filter in array_filter(filters.split(",")) + key, value = array_filter(filter.split(":")) + # find unspecified fields + if value == nil + data.select!{|d| d[key] == nil} + # find fields that match regex + elsif value.is_a?(String) + data.select!{|d| d[key].to_s =~ /#{value}/m} + end + end + return data + end + + # from css text, find font family definitions and construct google font url + def google_fonts(css) + names = regex_scan(css, '--\S*:\s*"(.*)",?.*;', false, true).sort.uniq + weights = regex_scan(css, '--\S*:\s(\d{3});', false, true).sort.uniq + url = "https://fonts.googleapis.com/css2?display=swap&" + for name in names do + name.sub!" ", "+" + url += "&family=#{name}:ital,wght@" + for ital in [0, 1] do + for weight in weights do + url += "#{ital},#{weight};" + end + end + url.delete_suffix!(";") + end + return url + end + end + + # based on https://github.com/episource/jekyll-html-proofer + module HtmlProofer + priority = Jekyll::Hooks::PRIORITY_MAP[:high] + 1000 + + Jekyll::Hooks.register(:site, :post_write, priority: priority) do |site| + if not site.config["proofer"] == false + options = { + allow_missing_href: true, + enforce_https: false, + ignore_files: [/.*testbed.html/], + ignore_urls: [ + /fonts\.gstatic\.com/, + /localhost:/, + /0\.0\.0\.0:/, + ], + } + + begin + HTMLProofer.check_directory(site.dest, options).run + rescue Exception => error + STDERR.puts error + # raise error + end + end + end + end +end + +Liquid::Template.register_filter(Jekyll::MiscFilters) diff --git a/_plugins/regex.rb b/_plugins/regex.rb new file mode 100644 index 0000000..f7cd02e --- /dev/null +++ b/_plugins/regex.rb @@ -0,0 +1,28 @@ +require 'liquid' + +module Jekyll + module RegexFilters + # search string for regex capture group, return first or all matches + def regex_scan(string, search, multi = false, all = false) + regex = multi ? /#{search}/m : /#{search}/ + matches = string.scan(regex).flatten + if matches.length + return all ? matches : matches[0] + else + return "" + end + end + + # find regex capture group in string and replace + def regex_replace(string, search, replace) + return string.gsub(/#{search}/m, replace) + end + + # strip all non-letter and non-number characters from string + def regex_strip(string) + return string.gsub(/[^\p{L}\p{N}]/u, " ") + end + end +end + +Liquid::Template.register_filter(Jekyll::RegexFilters) diff --git a/_posts/2019-01-07-example-post-1.md b/_posts/2019-01-07-example-post-1.md new file mode 100644 index 0000000..d586270 --- /dev/null +++ b/_posts/2019-01-07-example-post-1.md @@ -0,0 +1,10 @@ +--- +title: Example post 1 +author: sarah-johnson +tags: + - biology + - medicine + - big data +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. diff --git a/_posts/2021-09-30-example-post-2.md b/_posts/2021-09-30-example-post-2.md new file mode 100644 index 0000000..889c897 --- /dev/null +++ b/_posts/2021-09-30-example-post-2.md @@ -0,0 +1,6 @@ +--- +title: Example post 2 +author: jane-smith +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. diff --git a/_posts/2023-02-23-example-post-3.md b/_posts/2023-02-23-example-post-3.md new file mode 100644 index 0000000..0aa5a15 --- /dev/null +++ b/_posts/2023-02-23-example-post-3.md @@ -0,0 +1,8 @@ +--- +title: Example post 3 +image: images/photo.jpg +author: john-doe +tags: biology, medicine +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. diff --git a/_scripts/anchors.js b/_scripts/anchors.js new file mode 100644 index 0000000..58daabc --- /dev/null +++ b/_scripts/anchors.js @@ -0,0 +1,47 @@ +/* + creates link next to each heading that links to that section. +*/ + +{ + const onLoad = () => { + // for each heading + const headings = document.querySelectorAll( + "h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]" + ); + for (const heading of headings) { + // create anchor link + const link = document.createElement("a"); + link.classList.add("icon", "fa-solid", "fa-link", "anchor"); + link.href = "#" + heading.id; + link.setAttribute("aria-label", "link to this section"); + heading.append(link); + + // if first heading in the section, move id to parent section + if (heading.matches("section > :first-child")) { + heading.parentElement.id = heading.id; + heading.removeAttribute("id"); + } + } + }; + + // scroll to target of url hash + const scrollToTarget = () => { + const id = window.location.hash.replace("#", ""); + const target = document.getElementById(id); + + if (!target) return; + const offset = document.querySelector("header").clientHeight || 0; + window.scrollTo({ + top: target.getBoundingClientRect().top + window.scrollY - offset, + behavior: "smooth", + }); + }; + + // after page loads + window.addEventListener("load", onLoad); + window.addEventListener("load", scrollToTarget); + window.addEventListener("tagsfetched", scrollToTarget); + + // when hash nav happens + window.addEventListener("hashchange", scrollToTarget); +} diff --git a/_scripts/dark-mode.js b/_scripts/dark-mode.js new file mode 100644 index 0000000..b75b25e --- /dev/null +++ b/_scripts/dark-mode.js @@ -0,0 +1,25 @@ +/* + manages light/dark mode. +*/ + +{ + // immediately load saved (or default) mode before page renders + document.documentElement.dataset.dark = + window.localStorage.getItem("dark-mode") ?? "false"; + + const onLoad = () => { + // update toggle button to match loaded mode + document.querySelector(".dark-toggle").checked = + document.documentElement.dataset.dark === "true"; + }; + + // after page loads + window.addEventListener("load", onLoad); + + // when user toggles mode button + window.onDarkToggleChange = (event) => { + const value = event.target.checked; + document.documentElement.dataset.dark = value; + window.localStorage.setItem("dark-mode", value); + }; +} diff --git a/_scripts/fetch-tags.js b/_scripts/fetch-tags.js new file mode 100644 index 0000000..c843b67 --- /dev/null +++ b/_scripts/fetch-tags.js @@ -0,0 +1,67 @@ +/* + fetches tags (aka "topics") from a given GitHub repo and adds them to row of + tag buttons. specify repo in data-repo attribute on row. +*/ + +{ + const onLoad = async () => { + // get tag rows with specified repos + const rows = document.querySelectorAll("[data-repo]"); + + // for each repo + for (const row of rows) { + // get props from tag row + const repo = row.dataset.repo.trim(); + const link = row.dataset.link.trim(); + + // get tags from github + if (!repo) continue; + let tags = await fetchTags(repo); + + // filter out tags already present in row + let existing = [...row.querySelectorAll(".tag")].map((tag) => + window.normalizeTag(tag.innerText) + ); + tags = tags.filter((tag) => !existing.includes(normalizeTag(tag))); + + // add tags to row + for (const tag of tags) { + const a = document.createElement("a"); + a.classList.add("tag"); + a.innerHTML = tag; + a.href = `${link}?search="tag: ${tag}"`; + a.dataset.tooltip = `Show items with the tag "${tag}"`; + row.append(a); + } + + // delete tags container if empty + if (!row.innerText.trim()) row.remove(); + } + + // emit "tags done" event for other scripts to listen for + window.dispatchEvent(new Event("tagsfetched")); + }; + + // after page loads + window.addEventListener("load", onLoad); + + // GitHub topics endpoint + const api = "https://api.github.com/repos/REPO/topics"; + const headers = new Headers(); + headers.set("Accept", "application/vnd.github+json"); + + // get tags from GitHub based on repo name + const fetchTags = async (repo) => { + const url = api.replace("REPO", repo); + try { + const response = await (await fetch(url)).json(); + if (response.names) return response.names; + else throw new Error(JSON.stringify(response)); + } catch (error) { + console.groupCollapsed("GitHub fetch tags error"); + console.log(error); + console.groupEnd(); + return []; + } + }; +} diff --git a/_scripts/search.js b/_scripts/search.js new file mode 100644 index 0000000..fa23ca4 --- /dev/null +++ b/_scripts/search.js @@ -0,0 +1,215 @@ +/* + filters elements on page based on url or search box. + syntax: term1 term2 "full phrase 1" "full phrase 2" "tag: tag 1" + match if: all terms AND at least one phrase AND at least one tag +*/ +{ + // elements to filter + const elementSelector = ".card, .citation, .post-excerpt"; + // search box element + const searchBoxSelector = ".search-box"; + // results info box element + const infoBoxSelector = ".search-info"; + // tags element + const tagSelector = ".tag"; + + // split search query into terms, phrases, and tags + const splitQuery = (query) => { + // split into parts, preserve quotes + const parts = query.match(/"[^"]*"|\S+/g) || []; + + // bins + const terms = []; + const phrases = []; + const tags = []; + + // put parts into bins + for (let part of parts) { + if (part.startsWith('"')) { + part = part.replaceAll('"', "").trim(); + if (part.startsWith("tag:")) + tags.push(normalizeTag(part.replace(/tag:\s*/, ""))); + else phrases.push(part.toLowerCase()); + } else terms.push(part.toLowerCase()); + } + + return { terms, phrases, tags }; + }; + + // normalize tag string for comparison + window.normalizeTag = (tag) => + tag.trim().toLowerCase().replaceAll(/-|\s+/g, " "); + + // get data attribute contents of element and children + const getAttr = (element, attr) => + [element, ...element.querySelectorAll(`[data-${attr}]`)] + .map((element) => element.dataset[attr]) + .join(" "); + + // determine if element should show up in results based on query + const elementMatches = (element, { terms, phrases, tags }) => { + // tag elements within element + const tagElements = [...element.querySelectorAll(".tag")]; + + // check if text content exists in element + const hasText = (string) => + ( + element.innerText + + getAttr(element, "tooltip") + + getAttr(element, "search") + ) + .toLowerCase() + .includes(string); + // check if text matches a tag in element + const hasTag = (string) => + tagElements.some((tag) => normalizeTag(tag.innerText) === string); + + // match logic + return ( + (terms.every(hasText) || !terms.length) && + (phrases.some(hasText) || !phrases.length) && + (tags.some(hasTag) || !tags.length) + ); + }; + + // loop through elements, hide/show based on query, and return results info + const filterElements = (parts) => { + let elements = document.querySelectorAll(elementSelector); + + // results info + let x = 0; + let n = elements.length; + let tags = parts.tags; + + // filter elements + for (const element of elements) { + if (elementMatches(element, parts)) { + element.style.display = ""; + x++; + } else element.style.display = "none"; + } + + return [x, n, tags]; + }; + + // highlight search terms + const highlightMatches = async ({ terms, phrases }) => { + // make sure Mark library available + if (typeof Mark === "undefined") return; + + // reset + new Mark(document.body).unmark(); + + // limit number of highlights to avoid slowdown + let counter = 0; + const filter = () => counter++ < 100; + + // highlight terms and phrases + new Mark(elementSelector) + .mark(terms, { separateWordSearch: true, filter }) + .mark(phrases, { separateWordSearch: false, filter }); + }; + + // update search box based on query + const updateSearchBox = (query = "") => { + const boxes = document.querySelectorAll(searchBoxSelector); + + for (const box of boxes) { + const input = box.querySelector("input"); + const button = box.querySelector("button"); + const icon = box.querySelector("button i"); + input.value = query; + icon.className = input.value.length + ? "icon fa-solid fa-xmark" + : "icon fa-solid fa-magnifying-glass"; + button.disabled = input.value.length ? false : true; + } + }; + + // update info box based on query and results + const updateInfoBox = (query, x, n) => { + const boxes = document.querySelectorAll(infoBoxSelector); + + if (query.trim()) { + // show all info boxes + boxes.forEach((info) => (info.style.display = "")); + + // info template + let info = ""; + info += `Showing ${x.toLocaleString()} of ${n.toLocaleString()} results
`; + info += "Clear search"; + + // set info HTML string + boxes.forEach((el) => (el.innerHTML = info)); + } + // if nothing searched + else { + // hide all info boxes + boxes.forEach((info) => (info.style.display = "none")); + } + }; + + // update tags based on query + const updateTags = (query) => { + const { tags } = splitQuery(query); + document.querySelectorAll(tagSelector).forEach((tag) => { + // set active if tag is in query + if (tags.includes(normalizeTag(tag.innerText))) + tag.setAttribute("data-active", ""); + else tag.removeAttribute("data-active"); + }); + }; + + // run search with query + const runSearch = (query = "") => { + const parts = splitQuery(query); + const [x, n] = filterElements(parts); + updateSearchBox(query); + updateInfoBox(query, x, n); + updateTags(query); + highlightMatches(parts); + }; + + // update url based on query + const updateUrl = (query = "") => { + const url = new URL(window.location); + let params = new URLSearchParams(url.search); + params.set("search", query); + url.search = params.toString(); + window.history.replaceState(null, null, url); + }; + + // search based on url param + const searchFromUrl = () => { + const query = + new URLSearchParams(window.location.search).get("search") || ""; + runSearch(query); + }; + + // return func that runs after delay + const debounce = (callback, delay = 250) => { + let timeout; + return (...args) => { + window.clearTimeout(timeout); + timeout = window.setTimeout(() => callback(...args), delay); + }; + }; + + // when user types into search box + const debouncedRunSearch = debounce(runSearch, 1000); + window.onSearchInput = (target) => { + debouncedRunSearch(target.value); + updateUrl(target.value); + }; + + // when user clears search box with button + window.onSearchClear = () => { + runSearch(); + updateUrl(); + }; + + // after page loads + window.addEventListener("load", searchFromUrl); + // after tags load + window.addEventListener("tagsfetched", searchFromUrl); +} diff --git a/_scripts/site-search.js b/_scripts/site-search.js new file mode 100644 index 0000000..caff0a6 --- /dev/null +++ b/_scripts/site-search.js @@ -0,0 +1,14 @@ +/* + for site search component. searches site/domain via google. +*/ + +{ + // when user submits site search form/box + window.onSiteSearchSubmit = (event) => { + event.preventDefault(); + const google = "https://www.google.com/search?q=site:"; + const site = window.location.origin; + const query = event.target.elements.query.value; + window.location = google + site + " " + query; + }; +} diff --git a/_scripts/table-wrap.js b/_scripts/table-wrap.js new file mode 100644 index 0000000..4c5bddd --- /dev/null +++ b/_scripts/table-wrap.js @@ -0,0 +1,25 @@ +/* + put a wrapper around each table to allow scrolling. +*/ + +{ + const onLoad = () => { + // for each top-level table + const tables = document.querySelectorAll("table:not(table table)"); + for (const table of tables) { + // create wrapper with scroll + const wrapper = document.createElement("div"); + wrapper.style.overflowX = "auto"; + + // undo css force-text-wrap + table.style.overflowWrap = "normal"; + + // add wrapper around table + table.parentNode.insertBefore(wrapper, table); + wrapper.appendChild(table); + } + }; + + // after page loads + window.addEventListener("load", onLoad); +} diff --git a/_scripts/tooltip.js b/_scripts/tooltip.js new file mode 100644 index 0000000..49eccfc --- /dev/null +++ b/_scripts/tooltip.js @@ -0,0 +1,41 @@ +/* + shows a popup of text on hover/focus of any element with the data-tooltip + attribute. +*/ + +{ + const onLoad = () => { + // make sure Tippy library available + if (typeof tippy === "undefined") return; + + // get elements with non-empty tooltips + const elements = [...document.querySelectorAll("[data-tooltip]")].filter( + (element) => element.dataset.tooltip.trim() && !element._tippy + ); + + // add tooltip to elements + tippy(elements, { + content: (element) => element.dataset.tooltip.trim(), + delay: [200, 0], + offset: [0, 20], + allowHTML: true, + interactive: true, + appendTo: () => document.body, + aria: { + content: "describedby", + expanded: null, + }, + onShow: ({ reference, popper }) => { + const dark = reference.closest("[data-dark]")?.dataset.dark; + if (dark === "false") popper.dataset.dark = true; + if (dark === "true") popper.dataset.dark = false; + }, + // onHide: () => false, // debug + }); + }; + + // after page loads + window.addEventListener("load", onLoad); + // after tags load + window.addEventListener("tagsfetched", onLoad); +} diff --git a/_styles/-theme.scss b/_styles/-theme.scss new file mode 100644 index 0000000..0caecc6 --- /dev/null +++ b/_styles/-theme.scss @@ -0,0 +1,54 @@ +--- +--- + +// colors +[data-dark="false"] { + --primary: #0795d9; + --secondary: #7dd3fc; + --text: #000000; + --background: #ffffff; + --background-alt: #fafafa; + --light-gray: #e0e0e0; + --gray: #808080; + --dark-gray: #404040; + --overlay: #00000020; +} +[data-dark="true"] { + --primary: #0795d9; + --secondary: #075985; + --text: #ffffff; + --background: #181818; + --background-alt: #1c1c1c; + --light-gray: #404040; + --gray: #808080; + --dark-gray: #b0b0b0; + --overlay: #ffffff10; +} + +:root { + // font families + --title: "Barlow", sans-serif; + --heading: "Barlow", sans-serif; + --body: "Barlow", sans-serif; + --code: "Roboto Mono", monospace; + + // font sizes + --large: 1.2rem; + --xl: 1.4rem; + --xxl: 1.6rem; + + // font weights + --thin: 200; + --regular: 400; + --semi-bold: 500; + --bold: 600; + + // text line spacing + --spacing: 2; + --compact: 1.5; + + // effects + --rounded: 3px; + --shadow: 0 0 10px 0 var(--overlay); + --transition: 0.2s ease; +} diff --git a/_styles/alert.scss b/_styles/alert.scss new file mode 100644 index 0000000..6e77eec --- /dev/null +++ b/_styles/alert.scss @@ -0,0 +1,37 @@ +--- +--- + +.alert { + position: relative; + display: flex; + gap: 20px; + align-items: center; + margin: 20px 0; + padding: 20px; + border-radius: var(--rounded); + overflow: hidden; + text-align: left; + line-height: var(--spacing); +} + +.alert:before { + content: ""; + position: absolute; + inset: 0; + opacity: 0.1; + background: var(--color); + z-index: -1; +} + +.alert > .icon { + color: var(--color); + font-size: var(--large); +} + +.alert-content > :first-child { + margin-top: 0; +} + +.alert-content > :last-child { + margin-bottom: 0; +} diff --git a/_styles/all.scss b/_styles/all.scss new file mode 100644 index 0000000..a8aeeaa --- /dev/null +++ b/_styles/all.scss @@ -0,0 +1,11 @@ +--- +--- + +*, +::before, +::after { + box-sizing: border-box; + -moz-text-size-adjust: none; + -webkit-text-size-adjust: none; + text-size-adjust: none; +} diff --git a/_styles/anchor.scss b/_styles/anchor.scss new file mode 100644 index 0000000..65c18d8 --- /dev/null +++ b/_styles/anchor.scss @@ -0,0 +1,24 @@ +--- +--- + +.anchor { + display: inline-block; + position: relative; + width: 0; + margin: 0; + left: 0.5em; + color: var(--primary) !important; + opacity: 0; + font-size: 0.75em; + text-decoration: none; + transition: opacity var(--transition), color var(--transition); +} + +:hover > .anchor, +.anchor:focus { + opacity: 1; +} + +.anchor:hover { + color: var(--text) !important; +} diff --git a/_styles/background.scss b/_styles/background.scss new file mode 100644 index 0000000..15a7ba4 --- /dev/null +++ b/_styles/background.scss @@ -0,0 +1,21 @@ +--- +--- + +.background { + position: relative; + background: var(--background); + color: var(--text); + z-index: 1; +} + +.background:before { + content: ""; + position: absolute; + inset: 0; + background-image: var(--image); + background-size: cover; + background-repeat: no-repeat; + background-position: 50% 50%; + opacity: 0.25; + z-index: -1; +} diff --git a/_styles/body.scss b/_styles/body.scss new file mode 100644 index 0000000..91ecffc --- /dev/null +++ b/_styles/body.scss @@ -0,0 +1,15 @@ +--- +--- + +body { + display: flex; + flex-direction: column; + margin: 0; + padding: 0; + min-height: 100vh; + background: var(--background); + color: var(--text); + font-family: var(--body); + text-align: center; + line-height: var(--compact); +} diff --git a/_styles/bold.scss b/_styles/bold.scss new file mode 100644 index 0000000..01c72f6 --- /dev/null +++ b/_styles/bold.scss @@ -0,0 +1,7 @@ +--- +--- + +b, +strong { + font-weight: var(--bold); +} diff --git a/_styles/button.scss b/_styles/button.scss new file mode 100644 index 0000000..ed497f0 --- /dev/null +++ b/_styles/button.scss @@ -0,0 +1,51 @@ +--- +--- + +button { + cursor: pointer; +} + +.button-wrapper { + display: contents; +} + +.button { + display: inline-flex; + justify-content: center; + align-items: center; + gap: 10px; + max-width: calc(100% - 5px - 5px); + margin: 5px; + padding: 10px 15px; + border: none; + border-radius: var(--rounded); + background: var(--primary); + color: var(--background); + text-align: center; + font: inherit; + font-family: var(--heading); + font-weight: var(--semi-bold); + text-decoration: none; + vertical-align: middle; + appearance: none; + transition: background var(--transition), color var(--transition); +} + +.button:hover { + background: var(--text); + color: var(--background); +} + +.button[data-style="bare"] { + padding: 5px; + background: none; + color: var(--primary); + + &:hover { + color: var(--text); + } +} + +.button[data-flip] { + flex-direction: row-reverse; +} diff --git a/_styles/card.scss b/_styles/card.scss new file mode 100644 index 0000000..d95888e --- /dev/null +++ b/_styles/card.scss @@ -0,0 +1,52 @@ +--- +--- + +.card { + display: inline-flex; + justify-content: stretch; + align-items: center; + flex-direction: column; + width: 350px; + max-width: calc(100% - 20px - 20px); + margin: 20px; + background: var(--background); + border-radius: var(--rounded); + overflow: hidden; + box-shadow: var(--shadow); + vertical-align: top; +} + +.card[data-style="small"] { + width: 250px; +} + +.card-image img { + aspect-ratio: 3 / 2; + object-fit: cover; + width: 100%; + // box-shadow: var(--shadow); +} + +.card-text { + display: inline-flex; + justify-content: flex-start; + align-items: center; + flex-direction: column; + gap: 20px; + max-width: 100%; + padding: 20px; +} + +.card-text > * { + margin: 0 !important; +} + +.card-title { + font-family: var(--heading); + font-weight: var(--semi-bold); +} + +.card-subtitle { + margin-top: -10px !important; + font-style: italic; +} diff --git a/_styles/checkbox.scss b/_styles/checkbox.scss new file mode 100644 index 0000000..e5dbda8 --- /dev/null +++ b/_styles/checkbox.scss @@ -0,0 +1,6 @@ +--- +--- + +input[type="checkbox"] { + cursor: pointer; +} diff --git a/_styles/citation.scss b/_styles/citation.scss new file mode 100644 index 0000000..dc6c95e --- /dev/null +++ b/_styles/citation.scss @@ -0,0 +1,103 @@ +--- +--- + +$thumb-size: 180px; +$wrap: 800px; + +.citation-container { + container-type: inline-size; +} + +.citation { + display: flex; + margin: 20px 0; + border-radius: var(--rounded); + background: var(--background); + overflow: hidden; + box-shadow: var(--shadow); +} + +.citation-image { + position: relative; + width: $thumb-size; + flex-shrink: 0; + // box-shadow: var(--shadow); +} + +.citation-image img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: contain; +} + +.citation-text { + position: relative; + display: inline-flex; + flex-wrap: wrap; + gap: 10px; + max-width: 100%; + height: min-content; + padding: 20px; + padding-left: 30px; + text-align: left; + overflow-wrap: break-word; + z-index: 0; +} + +.citation-title, +.citation-authors, +.citation-details, +.citation-description { + width: 100%; +} + +.citation-title { + font-weight: var(--semi-bold); +} + +.citation-text > .icon { + position: absolute; + top: 20px; + right: 20px; + color: var(--light-gray); + opacity: 0.5; + font-size: 30px; + z-index: -1; +} + +.citation-publisher { + text-transform: capitalize; +} + +.citation-description { + color: var(--gray); +} + +.citation-buttons { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.citation-buttons .button { + margin: 0; +} + +.citation-text > .tags { + display: inline-flex; + justify-content: flex-start; + margin: 0; +} + +@container (max-width: #{$wrap}) { + .citation { + flex-direction: column; + } + + .citation-image { + width: unset; + height: $thumb-size; + } +} diff --git a/_styles/code.scss b/_styles/code.scss new file mode 100644 index 0000000..4a50657 --- /dev/null +++ b/_styles/code.scss @@ -0,0 +1,38 @@ +--- +--- + +pre, +code, +pre *, +code * { + font-family: var(--code); +} + +// inline code +code.highlighter-rouge { + padding: 2px 6px; + background: var(--light-gray); + border-radius: var(--rounded); +} + +// code block +div.highlighter-rouge { + width: 100%; + margin: 40px 0; + border-radius: var(--rounded); + overflow-x: auto; + overflow-y: auto; + text-align: left; + + div.highlight { + display: contents; + + pre.highlight { + width: fit-content; + min-width: 100%; + margin: 0; + padding: 20px; + color: var(--white); + } + } +} diff --git a/_styles/cols.scss b/_styles/cols.scss new file mode 100644 index 0000000..a3500b3 --- /dev/null +++ b/_styles/cols.scss @@ -0,0 +1,39 @@ +--- +--- + +$two: 750px; +$one: 500px; + +.cols { + display: grid; + --repeat: min(3, var(--cols)); + grid-template-columns: repeat(var(--repeat), 1fr); + align-items: flex-start; + gap: 40px; + margin: 40px 0; +} + +.cols > * { + min-width: 0; + min-height: 0; +} + +.cols > div > :first-child { + margin-top: 0 !important; +} + +.cols > div > :last-child { + margin-bottom: 0 !important; +} + +@media (max-width: $two) { + .cols { + --repeat: min(2, var(--cols)); + } +} + +@media (max-width: $one) { + .cols { + --repeat: min(1, var(--cols)); + } +} diff --git a/_styles/dark-toggle.scss b/_styles/dark-toggle.scss new file mode 100644 index 0000000..ade9c05 --- /dev/null +++ b/_styles/dark-toggle.scss @@ -0,0 +1,31 @@ +--- +--- + +.dark-toggle { + position: relative; + width: 40px; + height: 25px; + margin: 0; + border-radius: 999px; + background: var(--primary); + appearance: none; + transition: background var(--transition); +} + +.dark-toggle:after { + content: "\f185"; + position: absolute; + left: 12px; + top: 50%; + color: var(--text); + font-size: 15px; + font-family: "Font Awesome 6 Free"; + font-weight: 900; + transform: translate(-50%, -50%); + transition: left var(--transition); +} + +.dark-toggle:checked:after { + content: "\f186"; + left: calc(100% - 12px); +} diff --git a/_styles/feature.scss b/_styles/feature.scss new file mode 100644 index 0000000..3d2a53f --- /dev/null +++ b/_styles/feature.scss @@ -0,0 +1,53 @@ +--- +--- + +$wrap: 800px; + +.feature { + display: flex; + justify-content: center; + align-items: center; + gap: 40px; + margin: 40px 0; +} + +.feature-image { + flex-shrink: 0; + width: 40%; + aspect-ratio: 3 / 2; + border-radius: var(--rounded); + overflow: hidden; + box-shadow: var(--shadow); +} + +.feature-image img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.feature-text { + flex-grow: 1; +} + +.feature-title { + font-size: var(--large); + text-align: center; + font-family: var(--heading); + font-weight: var(--semi-bold); +} + +.feature[data-flip] { + flex-direction: row-reverse; +} + +@media (max-width: $wrap) { + .feature { + flex-direction: column !important; + } + + .feature-image { + width: 100%; + max-width: calc($wrap / 2); + } +} diff --git a/_styles/figure.scss b/_styles/figure.scss new file mode 100644 index 0000000..3b3c6ef --- /dev/null +++ b/_styles/figure.scss @@ -0,0 +1,26 @@ +--- +--- + +.figure { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 10px; + margin: 40px 0; +} + +.figure-image { + display: contents; +} + +.figure-image img { + border-radius: var(--rounded); + overflow: hidden; + box-shadow: var(--shadow); +} + +.figure-caption { + font-style: italic; + text-align: center; +} diff --git a/_styles/float.scss b/_styles/float.scss new file mode 100644 index 0000000..ba8d9e2 --- /dev/null +++ b/_styles/float.scss @@ -0,0 +1,38 @@ +--- +--- + +$wrap: 600px; + +.float { + margin-bottom: 20px; + max-width: 50%; +} + +.float > * { + margin: 0 !important; +} + +.float:not([data-flip]) { + float: left; + margin-right: 40px; +} + +.float[data-flip] { + float: right; + margin-left: 40px; +} + +.float[data-clear] { + float: unset; + clear: both; + margin: 0; +} + +@media (max-width: $wrap) { + .float { + float: unset !important; + clear: both !important; + margin: auto !important; + max-width: unset; + } +} diff --git a/_styles/font.scss b/_styles/font.scss new file mode 100644 index 0000000..162db3d --- /dev/null +++ b/_styles/font.scss @@ -0,0 +1,5 @@ +--- +--- + +@font-face { +} diff --git a/_styles/footer.scss b/_styles/footer.scss new file mode 100644 index 0000000..d0d5277 --- /dev/null +++ b/_styles/footer.scss @@ -0,0 +1,25 @@ +--- +--- + +footer { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 20px; + padding: 40px; + line-height: var(--spacing); + box-shadow: var(--shadow); +} + +footer a { + color: var(--text) !important; +} + +footer a:hover { + color: var(--primary) !important; +} + +footer .icon { + font-size: var(--xl); +} diff --git a/_styles/form.scss b/_styles/form.scss new file mode 100644 index 0000000..ce6129a --- /dev/null +++ b/_styles/form.scss @@ -0,0 +1,9 @@ +--- +--- + +form { + display: flex; + justify-content: center; + align-items: center; + gap: 10px; +} diff --git a/_styles/grid.scss b/_styles/grid.scss new file mode 100644 index 0000000..8ff6d2e --- /dev/null +++ b/_styles/grid.scss @@ -0,0 +1,54 @@ +--- +--- + +$two: 750px; +$one: 500px; + +.grid { + display: grid; + --repeat: 3; + grid-template-columns: repeat(var(--repeat), 1fr); + justify-content: center; + align-items: flex-start; + gap: 40px; + margin: 40px 0; +} + +.grid > * { + min-width: 0; + min-height: 0; + width: 100%; + // max-height: 50vh; + margin: 0 !important; +} + +@media (max-width: $two) { + .grid { + --repeat: 2; + } +} + +@media (max-width: $one) { + .grid { + --repeat: 1; + } +} + +.grid[data-style="square"] { + align-items: center; + + & > * { + aspect-ratio: 1 / 1; + } + + & img { + aspect-ratio: 1 / 1; + object-fit: cover; + max-width: unset; + max-height: unset; + } +} + +.grid > :where(h1, h2, h3, h4, h5, h6) { + display: none; +} diff --git a/_styles/header.scss b/_styles/header.scss new file mode 100644 index 0000000..045e293 --- /dev/null +++ b/_styles/header.scss @@ -0,0 +1,166 @@ +--- +--- + +$logo-big: 80px; +$logo: 40px; +$big-padding: 100px; +$collapse: 700px; +$sticky: true; + +header { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 20px; + padding: 20px; + box-shadow: var(--shadow); + + @if $sticky { + position: sticky !important; + top: 0; + z-index: 10 !important; + } +} + +header a { + color: var(--text); + text-decoration: none; +} + +.home { + display: flex; + justify-content: flex-start; + align-items: center; + gap: 10px; + flex-basis: 0; + flex-grow: 1; + max-width: 100%; +} + +.logo { + height: $logo; +} + +.logo > * { + height: 100%; +} + +.title-text { + display: flex; + justify-content: flex-start; + align-items: baseline; + flex-wrap: wrap; + gap: 5px; + min-width: 0; + font-family: var(--title); + text-align: left; +} + +.title { + font-size: var(--large); +} + +.subtitle { + opacity: 0.65; + font-weight: var(--thin); +} + +.nav-toggle { + display: none; + position: relative; + width: 30px; + height: 30px; + margin: 0; + color: var(--text); + appearance: none; + transition: background var(--transition); +} + +.nav-toggle:after { + content: "\f0c9"; + position: absolute; + left: 50%; + top: 50%; + color: var(--text); + font-size: 15px; + font-family: "Font Awesome 6 Free"; + font-weight: 900; + transform: translate(-50%, -50%); +} + +.nav-toggle:checked:after { + content: "\f00d"; +} + +nav { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + gap: 10px; + font-family: var(--heading); + text-transform: uppercase; +} + +nav > a { + padding: 5px; +} + +nav > a:hover { + color: var(--primary); +} + +header:not([data-big]) { + @media (max-width: $collapse) { + justify-content: flex-end; + + .nav-toggle { + display: flex; + } + + .nav-toggle:not(:checked) + nav { + display: none; + } + + nav { + align-items: flex-end; + flex-direction: column; + width: 100%; + } + } +} + +header[data-big] { + justify-content: center; + align-items: center; + flex-direction: column; + padding: $big-padding 20px; + + @if $sticky { + top: unset; + } + + .home { + flex-direction: column; + flex-grow: 0; + } + + .logo { + height: $logo-big; + } + + .title-text { + flex-direction: column; + align-items: center; + text-align: center; + } + + .title { + font-size: var(--xxl); + } + + .subtitle { + font-size: var(--large); + } +} diff --git a/_styles/heading.scss b/_styles/heading.scss new file mode 100644 index 0000000..2ea35a4 --- /dev/null +++ b/_styles/heading.scss @@ -0,0 +1,50 @@ +--- +--- + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 40px 0 20px 0; + font-family: var(--heading); + font-weight: var(--semi-bold); + text-align: left; + letter-spacing: 1px; +} + +h1 { + font-size: 1.6rem; + font-weight: var(--regular); + text-transform: uppercase; + text-align: center; +} + +h2 { + font-size: 1.6rem; + padding-bottom: 5px; + border-bottom: solid 1px var(--light-gray); + font-weight: var(--regular); +} + +h3 { + font-size: 1.5rem; +} + +h4 { + font-size: 1.3rem; +} + +h5 { + font-size: 1.15rem; +} + +h6 { + font-size: 1rem; +} + +:where(h1, h2, h3, h4, h5, h6) > .icon { + margin-right: 1em; + color: var(--light-gray); +} diff --git a/_styles/highlight.scss b/_styles/highlight.scss new file mode 100644 index 0000000..d41524a --- /dev/null +++ b/_styles/highlight.scss @@ -0,0 +1,7 @@ +--- +--- + +mark { + background: #fef08a; + color: #000000; +} diff --git a/_styles/icon.scss b/_styles/icon.scss new file mode 100644 index 0000000..c434ff1 --- /dev/null +++ b/_styles/icon.scss @@ -0,0 +1,16 @@ +--- +--- + +.icon { + font-size: 1em; +} + +span.icon { + line-height: 1; +} + +span.icon > svg { + position: relative; + top: 0.1em; + height: 1em; +} diff --git a/_styles/image.scss b/_styles/image.scss new file mode 100644 index 0000000..d288263 --- /dev/null +++ b/_styles/image.scss @@ -0,0 +1,7 @@ +--- +--- + +img { + max-width: 100%; + max-height: 100%; +} diff --git a/_styles/link.scss b/_styles/link.scss new file mode 100644 index 0000000..41230d3 --- /dev/null +++ b/_styles/link.scss @@ -0,0 +1,16 @@ +--- +--- + +a { + color: var(--primary); + transition: color var(--transition); + overflow-wrap: break-word; +} + +a:hover { + color: var(--text); +} + +a:not([href]) { + color: var(--text); +} diff --git a/_styles/list.scss b/_styles/list.scss new file mode 100644 index 0000000..d769a6a --- /dev/null +++ b/_styles/list.scss @@ -0,0 +1,24 @@ +--- +--- + +ul, +ol { + margin: 20px 0; + padding-left: 40px; +} + +ul { + list-style-type: square; +} + +li { + margin: 5px 0; + padding-left: 10px; + text-align: justify; + line-height: var(--spacing); + + ul, + ol { + margin: 0; + } +} diff --git a/_styles/main.scss b/_styles/main.scss new file mode 100644 index 0000000..36a8a79 --- /dev/null +++ b/_styles/main.scss @@ -0,0 +1,8 @@ +--- +--- + +main { + display: flex; + flex-direction: column; + flex-grow: 1; +} diff --git a/_styles/paragraph.scss b/_styles/paragraph.scss new file mode 100644 index 0000000..08b05a3 --- /dev/null +++ b/_styles/paragraph.scss @@ -0,0 +1,8 @@ +--- +--- + +p { + margin: 20px 0; + text-align: justify; + line-height: var(--spacing); +} diff --git a/_styles/portrait.scss b/_styles/portrait.scss new file mode 100644 index 0000000..fa0c039 --- /dev/null +++ b/_styles/portrait.scss @@ -0,0 +1,76 @@ +--- +--- + +.portrait-wrapper { + display: contents; +} + +.portrait { + position: relative; + display: inline-flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 20px; + margin: 20px; + width: 175px; + max-width: calc(100% - 20px - 20px); + text-decoration: none; +} + +.portrait[data-style="small"] { + width: 100px; +} + +.portrait[data-style="tiny"] { + flex-direction: row; + gap: 15px; + width: unset; + text-align: left; +} + +.portrait-image { + width: 100%; + aspect-ratio: 1 / 1; + border-radius: 999px; + object-fit: cover; + box-shadow: var(--shadow); +} + +.portrait[data-style="tiny"] .portrait-image { + width: 50px; +} + +.portrait[data-style="tiny"] .portrait-role { + display: none; +} + +.portrait-text { + display: flex; + flex-direction: column; +} + +.portrait-name { + font-family: var(--heading); + font-weight: var(--semi-bold); +} + +.portrait-role .icon { + position: absolute; + left: 0; + top: 0; + display: flex; + justify-content: center; + align-items: center; + width: calc(20px + 10%); + aspect-ratio: 1 / 1; + border-radius: 999px; + background: var(--background); + box-shadow: var(--shadow); + transform: translate(14%, 14%); +} + +.portrait[data-style="small"] .portrait-role .icon { + left: -2px; + top: -2px; +} diff --git a/_styles/post-excerpt.scss b/_styles/post-excerpt.scss new file mode 100644 index 0000000..27c7a1d --- /dev/null +++ b/_styles/post-excerpt.scss @@ -0,0 +1,69 @@ +--- +--- + +$thumb-size: 200px; +$wrap: 800px; + +.post-excerpt-container { + container-type: inline-size; +} + +.post-excerpt { + display: flex; + margin: 20px 0; + border-radius: var(--rounded); + background: var(--background); + overflow: hidden; + box-shadow: var(--shadow); +} + +.post-excerpt-image { + position: relative; + width: $thumb-size; + flex-shrink: 0; + // box-shadow: var(--shadow); +} + +.post-excerpt-image img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; +} + +.post-excerpt-text { + display: flex; + flex-wrap: wrap; + gap: 20px; + padding: 20px 30px; + text-align: left; +} + +.post-excerpt-text > * { + margin: 0 !important; +} + +.post-excerpt-text > a:first-child { + width: 100%; + font-weight: var(--semi-bold); +} + +.post-excerpt-text > div { + justify-content: flex-start; +} + +.post-excerpt-text > p { + width: 100%; +} + +@container (max-width: #{$wrap}) { + .post-excerpt { + flex-direction: column; + } + + .post-excerpt-image { + width: unset; + height: $thumb-size; + } +} diff --git a/_styles/post-info.scss b/_styles/post-info.scss new file mode 100644 index 0000000..65c86cb --- /dev/null +++ b/_styles/post-info.scss @@ -0,0 +1,33 @@ +--- +--- + +.post-info { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + gap: 20px; + margin: 20px 0; + color: var(--dark-gray); +} + +.post-info .portrait { + margin: 0; +} + +.post-info .icon { + margin-right: 0.5em; +} + +.post-info a { + color: inherit; +} + +.post-info a:hover { + color: var(--primary); +} + +.post-info > span { + text-align: center; + white-space: nowrap; +} diff --git a/_styles/post-nav.scss b/_styles/post-nav.scss new file mode 100644 index 0000000..40b5dd1 --- /dev/null +++ b/_styles/post-nav.scss @@ -0,0 +1,39 @@ +--- +--- + +$wrap: 600px; + +.post-nav { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 10px; + color: var(--gray); +} + +.post-nav > :first-child { + text-align: left; +} + +.post-nav > :last-child { + text-align: right; +} + +.post-nav > :first-child .icon { + margin-right: 0.5em; +} + +.post-nav > :last-child .icon { + margin-left: 0.5em; +} + +@media (max-width: $wrap) { + .post-nav { + align-items: center; + flex-direction: column; + } + + .post-nav > * { + text-align: center !important; + } +} diff --git a/_styles/quote.scss b/_styles/quote.scss new file mode 100644 index 0000000..68b2f3a --- /dev/null +++ b/_styles/quote.scss @@ -0,0 +1,16 @@ +--- +--- + +blockquote { + margin: 20px 0; + padding: 10px 20px; + border-left: solid 4px var(--light-gray); +} + +blockquote > :first-child { + margin-top: 0; +} + +blockquote > :last-child { + margin-bottom: 0; +} diff --git a/_styles/rule.scss b/_styles/rule.scss new file mode 100644 index 0000000..abf797b --- /dev/null +++ b/_styles/rule.scss @@ -0,0 +1,9 @@ +--- +--- + +hr { + margin: 40px 0; + background: var(--light-gray); + border: none; + height: 1px; +} diff --git a/_styles/search-box.scss b/_styles/search-box.scss new file mode 100644 index 0000000..5f20a78 --- /dev/null +++ b/_styles/search-box.scss @@ -0,0 +1,26 @@ +--- +--- + +.search-box { + position: relative; + height: 40px; +} + +.search-box .search-input { + width: 100%; + height: 100%; + padding-right: 40px; +} + +.search-box button { + position: absolute; + inset: 0 0 0 auto; + display: flex; + justify-content: center; + align-items: center; + padding: 0; + aspect-ratio: 1 / 1; + background: none; + color: var(--black); + border: none; +} diff --git a/_styles/search-info.scss b/_styles/search-info.scss new file mode 100644 index 0000000..63f9a17 --- /dev/null +++ b/_styles/search-info.scss @@ -0,0 +1,9 @@ +--- +--- + +.search-info { + margin: 20px 0; + text-align: center; + font-style: italic; + line-height: var(--spacing); +} diff --git a/_styles/section.scss b/_styles/section.scss new file mode 100644 index 0000000..332deb6 --- /dev/null +++ b/_styles/section.scss @@ -0,0 +1,39 @@ +--- +--- + +$page: 1000px; +$padding: 40px; + +section { + padding: $padding max($padding, calc((100% - $page) / 2)); + transition: background var(--transition), color var(--transition); +} + +section[data-size="wide"] { + padding: $padding; +} + +section[data-size="full"] { + padding: 0; +} + +section[data-size="full"] > * { + margin: 0; + border-radius: 0; +} + +section[data-size="full"] img { + border-radius: 0; +} + +main > section:last-of-type { + flex-grow: 1; +} + +main > section:nth-of-type(odd) { + background: var(--background); +} + +main > section:nth-of-type(even) { + background: var(--background-alt); +} diff --git a/_styles/table.scss b/_styles/table.scss new file mode 100644 index 0000000..995c700 --- /dev/null +++ b/_styles/table.scss @@ -0,0 +1,18 @@ +--- +--- + +table { + margin: 40px auto; + border-collapse: collapse; + overflow-wrap: anywhere; +} + +th { + font-weight: var(--semi-bold); +} + +th, +td { + padding: 10px 15px; + border: solid 1px var(--light-gray); +} diff --git a/_styles/tags.scss b/_styles/tags.scss new file mode 100644 index 0000000..4e98207 --- /dev/null +++ b/_styles/tags.scss @@ -0,0 +1,34 @@ +--- +--- + +.tags { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + gap: 10px; + max-width: 100%; + margin: 20px 0; +} + +.tag { + max-width: 100%; + margin: 0; + padding: 5px 10px; + border-radius: 999px; + background: var(--secondary); + color: var(--text); + text-decoration: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + transition: background var(--transition), color var(--transition); +} + +.tag:hover { + background: var(--light-gray); +} + +.tag[data-active] { + background: var(--light-gray); +} diff --git a/_styles/textbox.scss b/_styles/textbox.scss new file mode 100644 index 0000000..6d33ced --- /dev/null +++ b/_styles/textbox.scss @@ -0,0 +1,17 @@ +--- +--- + +input[type="text"] { + width: 100%; + height: 40px; + margin: 0; + padding: 5px 10px; + border: solid 1px var(--light-gray); + border-radius: var(--rounded); + background: var(--background); + color: var(--text); + font-family: inherit; + font-size: inherit; + appearance: none; + box-shadow: var(--shadow); +} diff --git a/_styles/tooltip.scss b/_styles/tooltip.scss new file mode 100644 index 0000000..0f21d2e --- /dev/null +++ b/_styles/tooltip.scss @@ -0,0 +1,65 @@ +--- +--- + +.tippy-box { + background: var(--background); + color: var(--text); + padding: 7.5px; + text-align: left; + box-shadow: var(--shadow); +} + +.tippy-arrow { + width: 30px; + height: 30px; +} + +.tippy-arrow:before { + width: 10px; + height: 10px; + background: var(--background); + box-shadow: var(--shadow); +} + +// correct tippy arrow styles to support intuitive arrow styles above +.tippy-arrow { + overflow: hidden; + pointer-events: none; +} +.tippy-box[data-placement="top"] .tippy-arrow { + inset: unset; + top: 100%; +} +.tippy-box[data-placement="bottom"] .tippy-arrow { + inset: unset; + bottom: 100%; +} +.tippy-box[data-placement="left"] .tippy-arrow { + inset: unset; + left: 100%; +} +.tippy-box[data-placement="right"] .tippy-arrow { + inset: unset; + right: 100%; +} +.tippy-arrow:before { + border: unset !important; + transform-origin: center !important; + transform: translate(-50%, -50%) rotate(45deg) !important; +} +.tippy-box[data-placement="top"] .tippy-arrow:before { + left: 50% !important; + top: 0 !important; +} +.tippy-box[data-placement="bottom"] .tippy-arrow:before { + left: 50% !important; + top: 100% !important; +} +.tippy-box[data-placement="left"] .tippy-arrow:before { + left: 0 !important; + top: 50% !important; +} +.tippy-box[data-placement="right"] .tippy-arrow:before { + left: 100% !important; + top: 50% !important; +} diff --git a/_styles/util.scss b/_styles/util.scss new file mode 100644 index 0000000..308c3c1 --- /dev/null +++ b/_styles/util.scss @@ -0,0 +1,14 @@ +--- +--- + +.left { + text-align: left; +} + +.center { + text-align: center; +} + +.right { + text-align: right; +} diff --git a/blog/index.md b/blog/index.md new file mode 100644 index 0000000..b86f359 --- /dev/null +++ b/blog/index.md @@ -0,0 +1,21 @@ +--- +title: Blog +nav: + order: 4 + tooltip: Musings and miscellany +--- + +# {% include icon.html icon="fa-solid fa-feather-pointed" %}Blog + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + +{% include section.html %} + +{% include search-box.html %} + +{% include tags.html tags=site.tags %} + +{% include search-info.html %} + +{% include list.html data="posts" component="post-excerpt" %} diff --git a/contact/index.md b/contact/index.md new file mode 100644 index 0000000..d9b7e4b --- /dev/null +++ b/contact/index.md @@ -0,0 +1,77 @@ +--- +title: Contact +nav: + order: 5 + tooltip: Email, address, and location +--- + +# {% include icon.html icon="fa-regular fa-envelope" %}Contact + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + +{% + include button.html + type="email" + text="jane@smith.com" + link="jane@smith.com" +%} +{% + include button.html + type="phone" + text="(555) 867-5309" + link="+1-555-867-5309" +%} +{% + include button.html + type="address" + tooltip="Our location on Google Maps for easy navigation" + link="https://www.google.com/maps" +%} + +{% include section.html %} + +{% capture col1 %} + +{% + include figure.html + image="images/photo.jpg" + caption="Lorem ipsum" +%} + +{% endcapture %} + +{% capture col2 %} + +{% + include figure.html + image="images/photo.jpg" + caption="Lorem ipsum" +%} + +{% endcapture %} + +{% include cols.html col1=col1 col2=col2 %} + +{% include section.html dark=true %} + +{% capture col1 %} +Lorem ipsum dolor sit amet +consectetur adipiscing elit +sed do eiusmod tempor +{% endcapture %} + +{% capture col2 %} +Lorem ipsum dolor sit amet +consectetur adipiscing elit +sed do eiusmod tempor +{% endcapture %} + +{% capture col3 %} +Lorem ipsum dolor sit amet +consectetur adipiscing elit +sed do eiusmod tempor +{% endcapture %} + +{% include cols.html col1=col1 col2=col2 col3=col3 %} diff --git a/images/background.jpg b/images/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5b7c14613469d676414e3be069fd1130fa7e401c GIT binary patch literal 82867 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 + + + + + diff --git a/images/icon.png b/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..774c4478f6121930cef7db4b711dc2ecff7dc860 GIT binary patch literal 19174 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 z + + + + + + + + + + diff --git a/images/photo.jpg b/images/photo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..691a9880a3b78af188f26ff1e92c56ad4e56d34c GIT binary patch literal 7310 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 TT&cusacc^!DX^vhWKQsJb-NTg literal 0 HcmV?d00001 diff --git a/images/share.jpg b/images/share.jpg new file mode 100644 index 0000000000000000000000000000000000000000..268a3410f5545ff22bc0f6c00897cf6c8d4cb630 GIT binary patch literal 40015 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 E0$$>LS^xk5 literal 0 HcmV?d00001 diff --git a/index.md b/index.md new file mode 100644 index 0000000..bea6980 --- /dev/null +++ b/index.md @@ -0,0 +1,94 @@ +--- +--- + +# Lab Website Template + +[Lab Website Template](https://github.com/greenelab/lab-website-template) is an easy-to-use, flexible website template for [labs](https://www.greenelab.com/). +Spend less time worrying about managing a website and citations, and more time running your lab. + +{% + include button.html + type="docs" + link="https://greene-lab.gitbook.io/lab-website-template-docs" +%} +{% + include button.html + type="github" + text="On GitHub" + link="greenelab/lab-website-template" +%} + +{% include section.html %} + +## Highlights + +{% capture text %} + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +{% + include button.html + link="research" + text="See our publications" + icon="fa-solid fa-arrow-right" + flip=true + style="bare" +%} + +{% endcapture %} + +{% + include feature.html + image="images/photo.jpg" + link="research" + title="Our Research" + text=text +%} + +{% capture text %} + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +{% + include button.html + link="projects" + text="Browse our projects" + icon="fa-solid fa-arrow-right" + flip=true + style="bare" +%} + +{% endcapture %} + +{% + include feature.html + image="images/photo.jpg" + link="projects" + title="Our Projects" + flip=true + style="bare" + text=text +%} + +{% capture text %} + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +{% + include button.html + link="team" + text="Meet our team" + icon="fa-solid fa-arrow-right" + flip=true + style="bare" +%} + +{% endcapture %} + +{% + include feature.html + image="images/photo.jpg" + link="team" + title="Our Team" + text=text +%} diff --git a/projects/index.md b/projects/index.md new file mode 100644 index 0000000..1d29a19 --- /dev/null +++ b/projects/index.md @@ -0,0 +1,27 @@ +--- +title: Projects +nav: + order: 2 + tooltip: Software, datasets, and more +--- + +# {% include icon.html icon="fa-solid fa-wrench" %}Projects + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + +{% include tags.html tags="publication, resource, website" %} + +{% include search-info.html %} + +{% include section.html %} + +## Featured + +{% include list.html component="card" data="projects" filters="group: featured" %} + +{% include section.html %} + +## More + +{% include list.html component="card" data="projects" filters="group: " style="small" %} diff --git a/research/index.md b/research/index.md new file mode 100644 index 0000000..0f700f4 --- /dev/null +++ b/research/index.md @@ -0,0 +1,27 @@ +--- +title: Research +nav: + order: 1 + tooltip: Published works +--- + +# {% include icon.html icon="fa-solid fa-microscope" %}Research + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + +{% include section.html %} + +## Highlighted + +{% include citation.html lookup="Open collaborative writing with Manubot" style="rich" %} + +{% include section.html %} + +## All + +{% include search-box.html %} + +{% include search-info.html %} + +{% include list.html data="citations" component="citation" style="rich" %} diff --git a/team/index.md b/team/index.md new file mode 100644 index 0000000..08318c9 --- /dev/null +++ b/team/index.md @@ -0,0 +1,35 @@ +--- +title: Team +nav: + order: 3 + tooltip: About our team +--- + +# {% include icon.html icon="fa-solid fa-users" %}Team + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + +{% include section.html %} + +{% include list.html data="members" component="portrait" filters="role: pi" %} +{% include list.html data="members" component="portrait" filters="role: ^(?!pi$)" %} + +{% include section.html background="images/background.jpg" dark=true %} + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis +nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + +{% include section.html %} + +{% capture content %} + +{% include figure.html image="images/photo.jpg" %} +{% include figure.html image="images/photo.jpg" %} +{% include figure.html image="images/photo.jpg" %} + +{% endcapture %} + +{% include grid.html style="square" content=content %} diff --git a/testbed.md b/testbed.md new file mode 100644 index 0000000..6b343a5 --- /dev/null +++ b/testbed.md @@ -0,0 +1,437 @@ +--- +title: Testbed +header: https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg +footer: https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg/1024px-Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg +header-dark: false +footer-dark: false +--- + +# Testbed + +{% include section.html %} + +# Basic formatting + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + +[External link](https://some-website.org/) + +[Internal link](team) + +_italic text_ + +**bold text** + +~~strike-through text~~ + +
+
+Text with extra blank lines above and below +
+
+ +- list item a +- list item b +- list item c + +1. ordered list item 1 +1. ordered list item 2 +1. ordered list item 3 + + + +1. top level list item + - nested list item + 1. even deeper nested list item + +Plain image: + +![plain image](/images/photo.jpg) + +# Heading 1 + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +## Heading 2 + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +### Heading 3 + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +#### Heading 4 + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +##### Heading 5 + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +###### Heading 6 + +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + +--- + +| TABLE | Game 1 | Game 2 | Game 3 | Total | +| :---- | :----: | :----: | :----: | ----: | +| Anna | 144 | 123 | 218 | 485 | +| Bill | 90 | 175 | 120 | 385 | +| Cara | 102 | 214 | 233 | 549 | + +> It was the best of times it was the worst of times. +> It was the age of wisdom, it was the age of foolishness. +> It was the spring of hope, it was the winter of despair. + +```javascript +// some code with syntax highlighting +const popup = document.querySelector("#popup"); +popup.style.width = "100%"; +popup.innerText = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; +``` + +This sentence has `inline code`, useful for making references to variables, packages, versions, etc. within a sentence. + +Lorem ipsum dolor sit amet. +{:.left} +Consectetur adipiscing elit. +{:.center} +Sed do eiusmod tempor incididunt. +{:.right} + +{% include section.html %} + +# Jekyll Spaceship + +| Stage | Direct Products | ATP Yields | +| -----------------: | --------------: | ---------: | +| Glycolysis | 2 ATP | | +| ^^ | 2 NADH | 3--5 ATP | +| Pyruvaye oxidation | 2 NADH | 5 ATP | +| Citric acid cycle | 2 ATP | | +| ^^ | 6 NADH | 15 ATP | +| ^^ | 2 FADH | 3 ATP | +| 30--32 ATP | | | + +$ a \* b = c ^ b $ + +$ 2^{\frac{n-1}{3}} $ + +$ \int_a^b f(x)\,dx. $ + +```mermaid! +pie title Pets adopted by volunteers + "Dogs" : 386 + "Cats" : 85 + "Rats" : 35 +``` + +{% include section.html %} + +# Components + +## Section + +{% include section.html background="images/background.jpg" %} + +Section, `background` + +{% include section.html dark=true %} + +Section, `dark=true` + +{% include section.html background="images/background.jpg" dark=true %} + +Section, `background` `dark=true` + +{% include section.html size="wide" %} + +Section, `size=wide` + +{% include section.html size="full" %} + +Section, `size=full` w/ figure + +{% include figure.html image="https://images.rawpixel.com/image_1000/cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIyLTA1L2ZsMjYyODgwODcyMjYtaW1hZ2VfMS1rb3k1Zzkxay5qcGc.jpg" link="team" width="100%" %} + +{% include section.html %} + +## Figure + +{% include figure.html image="images/icon.png" %} +{% include figure.html image="images/icon.png" caption="_Lorem_ **ipsum**." %} +{% include figure.html image="images/icon.png" caption="_Lorem_ **ipsum**. `px` width" width="400px" %} +{% include figure.html image="images/icon.png" caption="_Lorem_ **ipsum**. `%` width" link="team" width="50%" %} +{% include figure.html image="images/icon.png" caption="_Lorem_ **ipsum**. `px` height" link="team" height="200px" %} +{% include figure.html image="images/fallback.svg" caption="_Lorem_ **ipsum**. `px` width, svg" link="team" width="400px" %} +{% include figure.html image="images/fallback.svg" caption="_Lorem_ **ipsum**. `%` width, svg" link="team" width="50%" %} +{% include figure.html image="images/fallback.svg" caption="_Lorem_ **ipsum**. `px` height, svg" link="team" height="200px" %} + +{% include section.html %} + +## Button + +{% include button.html type="github" %} +{% include button.html type="github" style="bare" %} +{% include button.html type="github" icon="fa-brands fa-youtube" text="Override Text" tooltip="Override tooltip" %} +{% include button.html type="github" text="" style="bare" %} +{% include button.html type="github" text="" link="github-handle" %} + +{% include section.html %} + +## Icon + +{% include icon.html icon="fa-solid fa-bacteria" %} +{% include icon.html icon="fa-solid fa-virus" %} +{% include icon.html icon="fa-solid fa-flask" %} +{% include icon.html icon="manubot.svg" %} + +{% include icon.html icon="fa-brands fa-github" %} Lorem +{% include icon.html icon="fa-solid fa-microscope" %} Ipsum +{% include icon.html icon="manubot.svg" %} Dolor + +{% include section.html %} + +## Feature + +{% capture text %} +_Lorem_ **ipsum** dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +{% endcapture%} +{% include feature.html image="images/icon.png" link="team" title="Title" text=text %} +{% include feature.html image="images/icon.png" title="Title" text=text flip=true %} +{% include feature.html link="team" %} + +{% include section.html %} + +## List + +### List citations + +{% include list.html data="citations" component="citation" %} + +--- + +### List projects + +{% include list.html data="projects" component="card" %} + +--- + +### List team members + +{% include list.html data="members" component="portrait" %} + +--- + +### List blog posts + +{% include list.html data="posts" component="post-excerpt" %} + +{% include section.html %} + +## Citation + +{% include citation.html lookup="doi:10.1016/j.csbj.2020.05.017" %} +{% include citation.html lookup="Open collaborative writing" style="rich" %} +{% include citation.html title="Manual title" authors="Manual authors" %} + +{% include section.html %} + +## Card + +{% include card.html image="images/icon.png" link="https://nasa.gov/" title="A Large Card" subtitle="A cool card" description="A cool description" tooltip="A cool tooltip" tags="manual tag" repo="greenelab/lab-website-template" %} +{% include card.html image="images/icon.png" title="A Small Card" subtitle="A cool card" description="_Lorem_ **ipsum**" tooltip="A cool tooltip" tags="manual tag" repo="greenelab/lab-website-template" style="small" %} + +{% include section.html %} + +## Portrait + +{% include portrait.html lookup="jane-smith" %} +{% include portrait.html lookup="john-doe" style="small" %} +{% include portrait.html name="Manual name" style="small" %} +{% include portrait.html style="small" %} + +{% include section.html %} + +## Post Excerpt + +{% include post-excerpt.html lookup="example-post-1" %} +{% include post-excerpt.html title="Manual title" author="Manual author" date="2020-02-20" last_modified_at="" %} + +{% include section.html %} + +## Alert + +{% capture lorem %} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +{% endcapture %} +{% capture content %}**Tip** {{ lorem }}{% endcapture %} +{% include alert.html type="tip" content=content %} +{% capture content %}**Help** {{ lorem }}{% endcapture %} +{% include alert.html type="help" content=content %} +{% capture content %}**Info** {{ lorem }}{% endcapture %} +{% include alert.html type="info" content=content %} +{% capture content %}**Success** {{ lorem }}{% endcapture %} +{% include alert.html type="success" content=content %} +{% capture content %}**Warning** {{ lorem }}{% endcapture %} +{% include alert.html type="warning" content=content %} +{% capture content %}**Error** {{ lorem }}{% endcapture %} +{% include alert.html type="error" content=content %} + +{% include section.html %} + +## Tags + +{% include tags.html tags="ovarian cancer, dataset, gene expression" repo="greenelab/lab-website-template" link="blog" %} + +{% include section.html %} + +## Float + +### Figures + +{% capture content %} +{% include figure.html image="images/icon.png" caption="Caption" width="200px" %} +{% endcapture %} +{% include float.html content=content %} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +{% include float.html clear=true %} + +### Code + +{% capture content %} + +```javascript +const test = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; +``` + +{% endcapture %} +{% include float.html content=content flip=true %} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nulla facilisi etiam dignissim diam quis. Id aliquet lectus proin nibh nisl condimentum id venenatis a. Tristique magna sit amet purus gravida quis blandit turpis cursus. Ultrices eros in cursus turpis massa tincidunt dui ut ornare. A cras semper auctor neque vitae tempus quam pellentesque nec. At tellus at urna condimentum mattis pellentesque. Ipsum consequat nisl vel pretium. Ultrices mi tempus imperdiet nulla malesuada pellentesque elit eget gravida. Integer vitae justo eget magna fermentum iaculis eu non diam. Mus mauris vitae ultricies leo integer malesuada nunc vel. Leo integer malesuada nunc vel risus. Ornare arcu odio ut sem nulla pharetra. Purus semper eget duis at tellus at urna condimentum. Enim neque volutpat ac tincidunt vitae semper quis lectus. + +{% include section.html %} + +## Grid + +### Regular + +With Markdown images + +{% capture content %} +![image](https://journals.plos.org/ploscompbiol/article/figure/image?size=inline&id=info:doi/10.1371/journal.pcbi.1007128.g001&rev=2) + +![image](https://ars.els-cdn.com/content/image/1-s2.0-S2001037020302804-gr1.jpg) + +![image](https://iiif.elifesciences.org/lax:32822%2Felife-32822-fig8-v3.tif/full/863,/0/default.webp) + +![image]({{ "/images/icon.png" | relative_url }}) + +![image]({{ "/images/icon.png" | relative_url }}) + +![image]({{ "/images/icon.png" | relative_url }}) +{% endcapture %} +{% include grid.html content=content %} + +### Square + +With figure components + +{% capture content %} +{% include figure.html image="https://journals.plos.org/ploscompbiol/article/figure/image?size=inline&id=info:doi/10.1371/journal.pcbi.1007128.g001&rev=2" %} +{% include figure.html image="https://ars.els-cdn.com/content/image/1-s2.0-S2001037020302804-gr1.jpg" %} +{% include figure.html image="https://iiif.elifesciences.org/lax:32822%2Felife-32822-fig8-v3.tif/full/863,/0/default.webp" %} +{% include figure.html image="images/icon.png" %} +{% include figure.html image="images/icon.png" %} +{% include figure.html image="images/icon.png" %} +{% endcapture %} +{% include grid.html style="square" content=content %} + +### Grid of citations + +{% capture content %} +{% include list.html data="citations" component="citation" style="rich" %} +{% endcapture %} +{% include grid.html content=content %} + +### Grid of blog posts + +{% capture content %} +{% include list.html data="posts" component="post-excerpt" %} +{% endcapture %} +{% include grid.html content=content %} + +{% include section.html %} + +## Cols + +### Text + +{% capture col1 %} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +{% endcapture %} +{% capture col2 %} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nulla facilisi etiam dignissim diam quis. Id aliquet lectus proin nibh nisl condimentum id venenatis a. +{% endcapture %} +{% capture col3 %} +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nulla facilisi etiam dignissim diam quis. Id aliquet lectus proin nibh nisl condimentum id venenatis a. Tristique magna sit amet purus gravida quis blandit turpis cursus. Ultrices eros in cursus turpis massa tincidunt dui ut ornare. A cras semper auctor neque vitae tempus quam pellentesque nec. At tellus at urna condimentum mattis pellentesque. Ipsum consequat nisl vel pretium. Ultrices mi tempus imperdiet nulla malesuada pellentesque elit eget gravida. Integer vitae justo eget magna fermentum iaculis eu non diam. Mus mauris vitae ultricies leo integer malesuada nunc vel. Leo integer malesuada nunc vel risus. Ornare arcu odio ut sem nulla pharetra. Purus semper eget duis at tellus at urna condimentum. Enim neque volutpat ac tincidunt vitae semper quis lectus. +{% endcapture %} +{% include cols.html col1=col1 col2=col2 col3=col3 %} + +### Images + +{% capture col1 %} +{% include figure.html image="images/icon.png" caption="Fig. 1a" %} +Lorem _ipsum_ dolor **sit** amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +{% endcapture %} +{% capture col2 %} +{% include figure.html image="images/icon.png" caption="Fig. 1b" %} +Lorem _ipsum_ dolor **sit** amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +{% endcapture %} +{% capture col3 %} +{% include figure.html image="images/icon.png" caption="Fig. 1c" %} +Lorem _ipsum_ dolor **sit** amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. +{% endcapture %} +{% include cols.html col1=col1 col2=col2 col3=col3 %} + +### Code + +{% capture col1 %} + +```javascript +const test = "Lorem ipsum dolor sit amet"; +``` + +{% endcapture %} +{% capture col2 %} + +```javascript +const test = "Lorem ipsum dolor sit amet"; +``` + +{% endcapture %} +{% capture col3 %} + +```javascript +const test = "Lorem ipsum dolor sit amet"; +``` + +{% endcapture %} +{% include cols.html col1=col1 col2=col2 col3=col3 %} + +{% include section.html %} + +## Search + +{% include search-box.html %} +{% include search-info.html %} + +{% include section.html %} + +## Site Search + +{% include site-search.html %}