From 995abe4b206dca0cab2286c852e1a56a8d96c8b6 Mon Sep 17 00:00:00 2001 From: Krishna Lodha <47075664+krishnaglodha@users.noreply.github.com> Date: Fri, 23 Jun 2023 05:40:32 +0530 Subject: [PATCH] Converted docs to using material theme (#132) * Converted docs to using material theme * removed olderd mkdocs * removed unwanted js and css files --- web/docs/assets/javascripts/custom.js | 180 ++++++++++++++++ web/docs/assets/javascripts/termynal.js | 264 +++++++++++++++++++++++ web/docs/assets/stylesheets/custom.css | 146 +++++++++++++ web/docs/assets/stylesheets/termynal.css | 109 ++++++++++ web/docs/docker.md | 5 +- web/docs/index.md | 51 +++-- web/mkdocs.yml | 33 ++- web/requirements.txt | 2 +- 8 files changed, 772 insertions(+), 18 deletions(-) create mode 100644 web/docs/assets/javascripts/custom.js create mode 100644 web/docs/assets/javascripts/termynal.js create mode 100644 web/docs/assets/stylesheets/custom.css create mode 100644 web/docs/assets/stylesheets/termynal.css diff --git a/web/docs/assets/javascripts/custom.js b/web/docs/assets/javascripts/custom.js new file mode 100644 index 0000000..8e3be4c --- /dev/null +++ b/web/docs/assets/javascripts/custom.js @@ -0,0 +1,180 @@ +const div = document.querySelector('.github-topic-projects') + +async function getDataBatch(page) { + const response = await fetch(`https://api.github.com/search/repositories?q=topic:fastapi&per_page=100&page=${page}`, { headers: { Accept: 'application/vnd.github.mercy-preview+json' } }) + const data = await response.json() + return data +} + +async function getData() { + let page = 1 + let data = [] + let dataBatch = await getDataBatch(page) + data = data.concat(dataBatch.items) + const totalCount = dataBatch.total_count + while (data.length < totalCount) { + page += 1 + dataBatch = await getDataBatch(page) + data = data.concat(dataBatch.items) + } + return data +} + +function setupTermynal() { + document.querySelectorAll(".use-termynal").forEach(node => { + node.style.display = "block"; + new Termynal(node, { + lineDelay: 500 + }); + }); + const progressLiteralStart = "---> 100%"; + const promptLiteralStart = "$ "; + const customPromptLiteralStart = "# "; + const termynalActivateClass = "termy"; + let termynals = []; + + function createTermynals() { + document + .querySelectorAll(`.${termynalActivateClass} .highlight`) + .forEach(node => { + const text = node.textContent; + const lines = text.split("\n"); + const useLines = []; + let buffer = []; + function saveBuffer() { + if (buffer.length) { + let isBlankSpace = true; + buffer.forEach(line => { + if (line) { + isBlankSpace = false; + } + }); + dataValue = {}; + if (isBlankSpace) { + dataValue["delay"] = 0; + } + if (buffer[buffer.length - 1] === "") { + // A last single
won't have effect + // so put an additional one + buffer.push(""); + } + const bufferValue = buffer.join("
"); + dataValue["value"] = bufferValue; + useLines.push(dataValue); + buffer = []; + } + } + for (let line of lines) { + if (line === progressLiteralStart) { + saveBuffer(); + useLines.push({ + type: "progress" + }); + } else if (line.startsWith(promptLiteralStart)) { + saveBuffer(); + const value = line.replace(promptLiteralStart, "").trimEnd(); + useLines.push({ + type: "input", + value: value + }); + } else if (line.startsWith("// ")) { + saveBuffer(); + const value = "💬 " + line.replace("// ", "").trimEnd(); + useLines.push({ + value: value, + class: "termynal-comment", + delay: 0 + }); + } else if (line.startsWith(customPromptLiteralStart)) { + saveBuffer(); + const promptStart = line.indexOf(promptLiteralStart); + if (promptStart === -1) { + console.error("Custom prompt found but no end delimiter", line) + } + const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, "") + let value = line.slice(promptStart + promptLiteralStart.length); + useLines.push({ + type: "input", + value: value, + prompt: prompt + }); + } else { + buffer.push(line); + } + } + saveBuffer(); + const div = document.createElement("div"); + node.replaceWith(div); + const termynal = new Termynal(div, { + lineData: useLines, + noInit: true, + lineDelay: 500 + }); + termynals.push(termynal); + }); + } + + function loadVisibleTermynals() { + termynals = termynals.filter(termynal => { + if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) { + termynal.init(); + return false; + } + return true; + }); + } + window.addEventListener("scroll", loadVisibleTermynals); + createTermynals(); + loadVisibleTermynals(); +} + +function shuffle(array) { + var currentIndex = array.length, temporaryValue, randomIndex; + while (0 !== currentIndex) { + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + temporaryValue = array[currentIndex]; + array[currentIndex] = array[randomIndex]; + array[randomIndex] = temporaryValue; + } + return array; +} + +async function showRandomAnnouncement(groupId, timeInterval) { + const announceFastAPI = document.getElementById(groupId); + if (announceFastAPI) { + let children = [].slice.call(announceFastAPI.children); + children = shuffle(children) + let index = 0 + const announceRandom = () => { + children.forEach((el, i) => {el.style.display = "none"}); + children[index].style.display = "block" + index = (index + 1) % children.length + } + announceRandom() + setInterval(announceRandom, timeInterval + ) + } +} + +async function main() { + if (div) { + data = await getData() + div.innerHTML = '' + const ul = document.querySelector('.github-topic-projects ul') + data.forEach(v => { + if (v.full_name === 'tiangolo/fastapi') { + return + } + const li = document.createElement('li') + li.innerHTML = `★ ${v.stargazers_count} - ${v.full_name} by @${v.owner.login}` + ul.append(li) + }) + } + + setupTermynal(); + showRandomAnnouncement('announce-left', 5000) + showRandomAnnouncement('announce-right', 10000) +} + +main() diff --git a/web/docs/assets/javascripts/termynal.js b/web/docs/assets/javascripts/termynal.js new file mode 100644 index 0000000..4ac3270 --- /dev/null +++ b/web/docs/assets/javascripts/termynal.js @@ -0,0 +1,264 @@ +/** + * termynal.js + * A lightweight, modern and extensible animated terminal window, using + * async/await. + * + * @author Ines Montani + * @version 0.0.1 + * @license MIT + */ + +'use strict'; + +/** Generate a terminal widget. */ +class Termynal { + /** + * Construct the widget's settings. + * @param {(string|Node)=} container - Query selector or container element. + * @param {Object=} options - Custom settings. + * @param {string} options.prefix - Prefix to use for data attributes. + * @param {number} options.startDelay - Delay before animation, in ms. + * @param {number} options.typeDelay - Delay between each typed character, in ms. + * @param {number} options.lineDelay - Delay between each line, in ms. + * @param {number} options.progressLength - Number of characters displayed as progress bar. + * @param {string} options.progressChar – Character to use for progress bar, defaults to █. + * @param {number} options.progressPercent - Max percent of progress. + * @param {string} options.cursor – Character to use for cursor, defaults to ▋. + * @param {Object[]} lineData - Dynamically loaded line data objects. + * @param {boolean} options.noInit - Don't initialise the animation. + */ + constructor(container = '#termynal', options = {}) { + this.container = (typeof container === 'string') ? document.querySelector(container) : container; + this.pfx = `data-${options.prefix || 'ty'}`; + this.originalStartDelay = this.startDelay = options.startDelay + || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600; + this.originalTypeDelay = this.typeDelay = options.typeDelay + || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90; + this.originalLineDelay = this.lineDelay = options.lineDelay + || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500; + this.progressLength = options.progressLength + || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40; + this.progressChar = options.progressChar + || this.container.getAttribute(`${this.pfx}-progressChar`) || '█'; + this.progressPercent = options.progressPercent + || parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100; + this.cursor = options.cursor + || this.container.getAttribute(`${this.pfx}-cursor`) || '▋'; + this.lineData = this.lineDataToElements(options.lineData || []); + this.loadLines() + if (!options.noInit) this.init() + } + + loadLines() { + // Load all the lines and create the container so that the size is fixed + // Otherwise it would be changing and the user viewport would be constantly + // moving as she/he scrolls + const finish = this.generateFinish() + finish.style.visibility = 'hidden' + this.container.appendChild(finish) + // Appends dynamically loaded lines to existing line elements. + this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData); + for (let line of this.lines) { + line.style.visibility = 'hidden' + this.container.appendChild(line) + } + const restart = this.generateRestart() + restart.style.visibility = 'hidden' + this.container.appendChild(restart) + this.container.setAttribute('data-termynal', ''); + } + + /** + * Initialise the widget, get lines, clear container and start animation. + */ + init() { + /** + * Calculates width and height of Termynal container. + * If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS. + */ + const containerStyle = getComputedStyle(this.container); + this.container.style.width = containerStyle.width !== '0px' ? + containerStyle.width : undefined; + this.container.style.minHeight = containerStyle.height !== '0px' ? + containerStyle.height : undefined; + + this.container.setAttribute('data-termynal', ''); + this.container.innerHTML = ''; + for (let line of this.lines) { + line.style.visibility = 'visible' + } + this.start(); + } + + /** + * Start the animation and rener the lines depending on their data attributes. + */ + async start() { + this.addFinish() + await this._wait(this.startDelay); + + for (let line of this.lines) { + const type = line.getAttribute(this.pfx); + const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay; + + if (type == 'input') { + line.setAttribute(`${this.pfx}-cursor`, this.cursor); + await this.type(line); + await this._wait(delay); + } + + else if (type == 'progress') { + await this.progress(line); + await this._wait(delay); + } + + else { + this.container.appendChild(line); + await this._wait(delay); + } + + line.removeAttribute(`${this.pfx}-cursor`); + } + this.addRestart() + this.finishElement.style.visibility = 'hidden' + this.lineDelay = this.originalLineDelay + this.typeDelay = this.originalTypeDelay + this.startDelay = this.originalStartDelay + } + + generateRestart() { + const restart = document.createElement('a') + restart.onclick = (e) => { + e.preventDefault() + this.container.innerHTML = '' + this.init() + } + restart.href = '#' + restart.setAttribute('data-terminal-control', '') + restart.innerHTML = "restart ↻" + return restart + } + + generateFinish() { + const finish = document.createElement('a') + finish.onclick = (e) => { + e.preventDefault() + this.lineDelay = 0 + this.typeDelay = 0 + this.startDelay = 0 + } + finish.href = '#' + finish.setAttribute('data-terminal-control', '') + finish.innerHTML = "fast →" + this.finishElement = finish + return finish + } + + addRestart() { + const restart = this.generateRestart() + this.container.appendChild(restart) + } + + addFinish() { + const finish = this.generateFinish() + this.container.appendChild(finish) + } + + /** + * Animate a typed line. + * @param {Node} line - The line element to render. + */ + async type(line) { + const chars = [...line.textContent]; + line.textContent = ''; + this.container.appendChild(line); + + for (let char of chars) { + const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay; + await this._wait(delay); + line.textContent += char; + } + } + + /** + * Animate a progress bar. + * @param {Node} line - The line element to render. + */ + async progress(line) { + const progressLength = line.getAttribute(`${this.pfx}-progressLength`) + || this.progressLength; + const progressChar = line.getAttribute(`${this.pfx}-progressChar`) + || this.progressChar; + const chars = progressChar.repeat(progressLength); + const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`) + || this.progressPercent; + line.textContent = ''; + this.container.appendChild(line); + + for (let i = 1; i < chars.length + 1; i++) { + await this._wait(this.typeDelay); + const percent = Math.round(i / chars.length * 100); + line.textContent = `${chars.slice(0, i)} ${percent}%`; + if (percent>progressPercent) { + break; + } + } + } + + /** + * Helper function for animation delays, called with `await`. + * @param {number} time - Timeout, in ms. + */ + _wait(time) { + return new Promise(resolve => setTimeout(resolve, time)); + } + + /** + * Converts line data objects into line elements. + * + * @param {Object[]} lineData - Dynamically loaded lines. + * @param {Object} line - Line data object. + * @returns {Element[]} - Array of line elements. + */ + lineDataToElements(lineData) { + return lineData.map(line => { + let div = document.createElement('div'); + div.innerHTML = `${line.value || ''}`; + + return div.firstElementChild; + }); + } + + /** + * Helper function for generating attributes string. + * + * @param {Object} line - Line data object. + * @returns {string} - String of attributes. + */ + _attributes(line) { + let attrs = ''; + for (let prop in line) { + // Custom add class + if (prop === 'class') { + attrs += ` class=${line[prop]} ` + continue + } + if (prop === 'type') { + attrs += `${this.pfx}="${line[prop]}" ` + } else if (prop !== 'value') { + attrs += `${this.pfx}-${prop}="${line[prop]}" ` + } + } + + return attrs; + } +} + +/** +* HTML API: If current script has container(s) specified, initialise Termynal. +*/ +if (document.currentScript.hasAttribute('data-termynal-container')) { + const containers = document.currentScript.getAttribute('data-termynal-container'); + containers.split('|') + .forEach(container => new Termynal(container)) +} diff --git a/web/docs/assets/stylesheets/custom.css b/web/docs/assets/stylesheets/custom.css new file mode 100644 index 0000000..066b517 --- /dev/null +++ b/web/docs/assets/stylesheets/custom.css @@ -0,0 +1,146 @@ +.termynal-comment { + color: #4a968f; + font-style: italic; + display: block; +} + +.termy { + /* For right to left languages */ + direction: ltr; +} + +.termy [data-termynal] { + white-space: pre-wrap; +} + +a.external-link { + /* For right to left languages */ + direction: ltr; + display: inline-block; +} + +a.external-link::after { + /* \00A0 is a non-breaking space + to make the mark be on the same line as the link + */ + content: "\00A0[↪]"; +} + +a.internal-link::after { + /* \00A0 is a non-breaking space + to make the mark be on the same line as the link + */ + content: "\00A0↪"; +} + +.shadow { + box-shadow: 5px 5px 10px #999; +} + +/* Give space to lower icons so Gitter chat doesn't get on top of them */ +.md-footer-meta { + padding-bottom: 2em; +} + +.user-list { + display: flex; + flex-wrap: wrap; + margin-bottom: 2rem; +} + +.user-list-center { + justify-content: space-evenly; +} + +.user { + margin: 1em; + min-width: 7em; +} + +.user .avatar-wrapper { + width: 80px; + height: 80px; + margin: 10px auto; + overflow: hidden; + border-radius: 50%; + position: relative; +} + +.user .avatar-wrapper img { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.user .title { + text-align: center; +} + +.user .count { + font-size: 80%; + text-align: center; +} + +a.announce-link:link, +a.announce-link:visited { + color: #fff; +} + +a.announce-link:hover { + color: var(--md-accent-fg-color); +} + +.announce-wrapper { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + align-items: center; +} + +.announce-wrapper div.item { + display: none; +} + +.announce-wrapper .sponsor-badge { + display: block; + position: absolute; + top: -10px; + right: 0; + font-size: 0.5rem; + color: #999; + background-color: #666; + border-radius: 10px; + padding: 0 10px; + z-index: 10; +} + +.announce-wrapper .sponsor-image { + display: block; + border-radius: 20px; +} + +.announce-wrapper>div { + min-height: 40px; + display: flex; + align-items: center; +} + +.twitter { + color: #00acee; +} + +/* Right to left languages */ +code { + direction: ltr; + display: inline-block; +} + +.md-content__inner h1 { + direction: ltr !important; +} + +.illustration { + margin-top: 2em; + margin-bottom: 2em; +} diff --git a/web/docs/assets/stylesheets/termynal.css b/web/docs/assets/stylesheets/termynal.css new file mode 100644 index 0000000..406c008 --- /dev/null +++ b/web/docs/assets/stylesheets/termynal.css @@ -0,0 +1,109 @@ +/** + * termynal.js + * + * @author Ines Montani + * @version 0.0.1 + * @license MIT + */ + +:root { + --color-bg: #252a33; + --color-text: #eee; + --color-text-subtle: #a2a2a2; +} + +[data-termynal] { + width: 750px; + max-width: 100%; + background: var(--color-bg); + color: var(--color-text); + /* font-size: 18px; */ + font-size: 15px; + /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ + font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; + border-radius: 4px; + padding: 75px 45px 35px; + position: relative; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +[data-termynal]:before { + content: ''; + position: absolute; + top: 15px; + left: 15px; + display: inline-block; + width: 15px; + height: 15px; + border-radius: 50%; + /* A little hack to display the window buttons in one pseudo element. */ + background: #d9515d; + -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; + box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; +} + +[data-termynal]:after { + content: 'bash'; + position: absolute; + color: var(--color-text-subtle); + top: 5px; + left: 0; + width: 100%; + text-align: center; +} + +a[data-terminal-control] { + text-align: right; + display: block; + color: #aebbff; +} + +[data-ty] { + display: block; + line-height: 2; +} + +[data-ty]:before { + /* Set up defaults and ensure empty lines are displayed. */ + content: ''; + display: inline-block; + vertical-align: middle; +} + +[data-ty="input"]:before, +[data-ty-prompt]:before { + margin-right: 0.75em; + color: var(--color-text-subtle); +} + +[data-ty="input"]:before { + content: '$'; +} + +[data-ty][data-ty-prompt]:before { + content: attr(data-ty-prompt); +} + +[data-ty-cursor]:after { + content: attr(data-ty-cursor); + font-family: monospace; + margin-left: 0.5em; + -webkit-animation: blink 1s infinite; + animation: blink 1s infinite; +} + + +/* Cursor animation */ + +@-webkit-keyframes blink { + 50% { + opacity: 0; + } +} + +@keyframes blink { + 50% { + opacity: 0; + } +} diff --git a/web/docs/docker.md b/web/docs/docker.md index 9a91c5d..d508dc6 100644 --- a/web/docs/docker.md +++ b/web/docs/docker.md @@ -51,9 +51,12 @@ Some notes: If all goes well, you should be able to run Docker from the command line as follows: [^2] -```bash +
+ +```console $ docker --version Docker version 20.10.17, build 100c701 $ docker-compose --version Docker Compose version v2.6.1 ``` +
diff --git a/web/docs/index.md b/web/docs/index.md index 4476f9d..d5a8f57 100644 --- a/web/docs/index.md +++ b/web/docs/index.md @@ -46,11 +46,18 @@ Having said this, please feel free to bring your own! Examples: Ensure Docker is running on your computer, then verify that the `docker` and `docker-compose` commands are working and available: -```bash -docker version -docker-compose --version + +
+ +```console +$ docker version + +$ docker-compose --version ``` +
+ + If `docker-compose` gives a 'program not found' error: > In recent versions of Docker the Docker Compose program is part @@ -66,43 +73,59 @@ If `docker-compose` gives a 'program not found' error: Below we will download and run the workshop content. -```bash +
+ +```console curl -O https://codeload.github.com/geopython/geopython-workshop/zip/master unzip master cd geopython-workshop-master/workshop -# start the workshop +// start the workshop + ./geopython-workshop-ctl.sh start -# display URL and open in default web browser +// display URL and open in default web browser + ./geopython-workshop-ctl.sh url -# stop workshop +// stop workshop + ./geopython-workshop-ctl.sh stop ``` +
+ If the above `.sh` script does not work on your system you can execute `docker-compose` directly via: -```bash -# in dir geopython-workshop-master/workshop +
+ +```console +// in dir geopython-workshop-master/workshop docker-compose up -d docker logs --follow geopython-workshop-jupyter -# look for URL+Token and Copy/Paste in browser +// look for URL+Token and Copy/Paste in browser ``` +
Below are utility commands. Use when stopped to clean and update. -```bash +
+ +```console +// update the workshop Docker Images in case of new versions -# update the workshop Docker Images in case of new versions ./geopython-workshop-ctl.sh update -# clean your Docker environment from dangling Images/Containers -# (does not remove the workshop's images, only obsolete ones) +// clean your Docker environment from dangling Images/Containers +// (does not remove the workshop's images, only obsolete ones) + ./geopython-workshop-ctl.sh clean ``` +
+ + ## Installation Issues diff --git a/web/mkdocs.yml b/web/mkdocs.yml index a57fb8d..5c0dd8a 100644 --- a/web/mkdocs.yml +++ b/web/mkdocs.yml @@ -11,6 +11,35 @@ nav: - Home: index.md - Docker installation: docker.md -theme: spacelab +theme: + name: material + icon: + logo: material/language-python + palette: + # primary: light blue + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode -plugins: [] + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to light mode + +plugins: + - search + +extra_css: + - assets/stylesheets/termynal.css + - assets/stylesheets/custom.css + +extra_javascript: + - assets/javascripts/termynal.js + - assets/javascripts/custom.js + +markdown_extensions: + - pymdownx.superfences \ No newline at end of file diff --git a/web/requirements.txt b/web/requirements.txt index 7be02d5..21f876b 100644 --- a/web/requirements.txt +++ b/web/requirements.txt @@ -1,2 +1,2 @@ mkdocs -mkdocs-bootswatch +mkdocs-material \ No newline at end of file