From 28a8b6ea76cd7ad9e6d4dcba81c470734a3be76a Mon Sep 17 00:00:00 2001 From: Lucas Dohmen Date: Thu, 22 Aug 2024 15:40:22 +0200 Subject: [PATCH] Convert to Bridgetown I could not get the setup to work, so I instead copied the setup from my homepage. This also updates some parts of the content (mainly legal stuff) and cleans up the CSS and JS. --- .eslintrc | 2 - .github/dependabot.yml | 7 +- .github/workflows/tests.yml | 31 +- .gitignore | 9 +- Gemfile | 4 + Gemfile.lock | 170 + README.md | 27 +- Rakefile | 35 + config.ru | 7 + config/esbuild.defaults.js | 360 + config/initializers.rb | 101 + config/puma.rb | 33 + content/defaults.js | 7 - esbuild.config.js | 43 + faucet.config.js | 44 - frontend/fonts/titillium-web-bold.woff2 | Bin 0 -> 19300 bytes frontend/fonts/titillium-web-italic.woff2 | Bin 0 -> 23172 bytes frontend/fonts/titillium-web-regular.woff2 | Bin 0 -> 20368 bytes frontend/javascript/index.js | 9 + frontend/styles/index.css | 147 + frontend/styles/syntax-highlighting.css | 79 + jsconfig.json | 10 + justfile | 12 + lib/app.js | 16 - lib/components/bar/_index.scss | 13 - lib/components/bar/index.jsx | 28 - lib/components/code/_index.scss | 151 - lib/components/iconlist/_index.scss | 8 - lib/components/iconlist/index.jsx | 14 - lib/components/layout/_index.scss | 25 - lib/components/layout/index.jsx | 87 - lib/components/navigation/index.jsx | 63 - lib/components/skip/index.jsx | 5 - lib/components/teaser/index.jsx | 18 - lib/images/twitter.svg | 1 - lib/styles/_base.scss | 68 - lib/styles/_settings.scss | 58 - lib/styles/index.scss | 11 - package-lock.json | 12017 +++++----------- package.json | 43 +- postcss.config.js | 11 + server/roda_app.rb | 17 + src/_components/bar.css | 22 + src/_components/bar.erb | 16 + src/_components/bar.rb | 5 + .../_components/navigation.css | 26 +- src/_components/navigation.erb | 45 + src/_components/navigation.rb | 10 + .../_index.scss => src/_components/skip.css | 11 +- src/_components/skip.erb | 1 + src/_components/skip.rb | 5 + .../_index.scss => src/_components/teaser.css | 20 +- src/_components/teaser.erb | 9 + src/_components/teaser.rb | 5 + src/_data/site_metadata.yml | 7 + src/_layouts/default.erb | 36 + {content => src}/build-integration.md | 3 + {content => src}/build-pipeline.md | 3 + {content => src}/cli.md | 3 + {content => src}/contributing.md | 3 + {content => src}/css.md | 3 + {content => src}/faq.md | 3 + {content => src}/images.md | 3 + .../images/faucet-logotype-monochrome.svg | 0 {lib => src}/images/favicon.svg | 0 {lib => src}/images/github.svg | 0 {lib => src}/images/logo.png | Bin {lib => src}/images/logo.webm | Bin {lib => src}/images/npm.svg | 0 src/impressum.md | 109 + {content => src}/index.md | 5 +- {content => src}/js.md | 3 + {content => src}/manifest.md | 4 +- {content => src}/philosophy.md | 3 + {content => src}/rails.md | 3 + {content => src}/sass.md | 3 + {content => src}/spring-boot.md | 3 + {content => src}/static.md | 3 + {content => src}/watching.md | 3 + views/index.jsx | 21 - views/util.js | 13 - 81 files changed, 4816 insertions(+), 9387 deletions(-) delete mode 100644 .eslintrc create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Rakefile create mode 100644 config.ru create mode 100644 config/esbuild.defaults.js create mode 100644 config/initializers.rb create mode 100644 config/puma.rb delete mode 100644 content/defaults.js create mode 100644 esbuild.config.js delete mode 100644 faucet.config.js create mode 100644 frontend/fonts/titillium-web-bold.woff2 create mode 100644 frontend/fonts/titillium-web-italic.woff2 create mode 100644 frontend/fonts/titillium-web-regular.woff2 create mode 100644 frontend/javascript/index.js create mode 100644 frontend/styles/index.css create mode 100644 frontend/styles/syntax-highlighting.css create mode 100644 jsconfig.json create mode 100644 justfile delete mode 100644 lib/app.js delete mode 100644 lib/components/bar/_index.scss delete mode 100644 lib/components/bar/index.jsx delete mode 100644 lib/components/code/_index.scss delete mode 100644 lib/components/iconlist/_index.scss delete mode 100644 lib/components/iconlist/index.jsx delete mode 100644 lib/components/layout/_index.scss delete mode 100644 lib/components/layout/index.jsx delete mode 100644 lib/components/navigation/index.jsx delete mode 100644 lib/components/skip/index.jsx delete mode 100644 lib/components/teaser/index.jsx delete mode 100644 lib/images/twitter.svg delete mode 100644 lib/styles/_base.scss delete mode 100644 lib/styles/_settings.scss delete mode 100644 lib/styles/index.scss create mode 100644 postcss.config.js create mode 100644 server/roda_app.rb create mode 100644 src/_components/bar.css create mode 100644 src/_components/bar.erb create mode 100644 src/_components/bar.rb rename lib/components/navigation/_index.scss => src/_components/navigation.css (50%) create mode 100644 src/_components/navigation.erb create mode 100644 src/_components/navigation.rb rename lib/components/skip/_index.scss => src/_components/skip.css (51%) create mode 100644 src/_components/skip.erb create mode 100644 src/_components/skip.rb rename lib/components/teaser/_index.scss => src/_components/teaser.css (54%) create mode 100644 src/_components/teaser.erb create mode 100644 src/_components/teaser.rb create mode 100644 src/_data/site_metadata.yml create mode 100644 src/_layouts/default.erb rename {content => src}/build-integration.md (98%) rename {content => src}/build-pipeline.md (99%) rename {content => src}/cli.md (97%) rename {content => src}/contributing.md (98%) rename {content => src}/css.md (99%) rename {content => src}/faq.md (97%) rename {content => src}/images.md (99%) rename {lib => src}/images/faucet-logotype-monochrome.svg (100%) rename {lib => src}/images/favicon.svg (100%) rename {lib => src}/images/github.svg (100%) rename {lib => src}/images/logo.png (100%) rename {lib => src}/images/logo.webm (100%) rename {lib => src}/images/npm.svg (100%) create mode 100644 src/impressum.md rename {content => src}/index.md (98%) rename {content => src}/js.md (99%) rename {content => src}/manifest.md (99%) rename {content => src}/philosophy.md (99%) rename {content => src}/rails.md (88%) rename {content => src}/sass.md (98%) rename {content => src}/spring-boot.md (88%) rename {content => src}/static.md (98%) rename {content => src}/watching.md (96%) delete mode 100644 views/index.jsx delete mode 100644 views/util.js diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 082e5fc..0000000 --- a/.eslintrc +++ /dev/null @@ -1,2 +0,0 @@ -root: true -extends: fnd-jsx diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5dd4d4b..d9f40fa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,13 +1,16 @@ version: 2 updates: - # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" - # Maintain dependencies for npm - package-ecosystem: "npm" directory: "/" schedule: interval: "daily" + + - package-ecosystem: "bundler" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 257f083..b2131aa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,18 +1,19 @@ name: tests on: -- push + - push jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: - - 18.x - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm install-test - env: - CI: true + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22.x' + cache: 'npm' + - run: npm ci + - uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + - run: bundle exec bridgetown frontend:build + - run: bundle exec bridgetown build diff --git a/.gitignore b/.gitignore index 3526419..a563d04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ /node_modules -/.eslintcache - -/dist +/.gems +/.bridgetown-cache +/compact_index +/output +/tmp +/.envrc diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..29deb70 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source "https://rubygems.org" + +gem "bridgetown", "~> 2.0.0.beta2" +gem "puma" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..b958c88 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,170 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (7.2.2.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + amazing_print (1.7.2) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.9) + bridgetown (2.0.0.beta4) + bridgetown-builder (= 2.0.0.beta4) + bridgetown-core (= 2.0.0.beta4) + bridgetown-foundation (= 2.0.0.beta4) + bridgetown-paginate (= 2.0.0.beta4) + bridgetown-builder (2.0.0.beta4) + bridgetown-core (= 2.0.0.beta4) + bridgetown-core (2.0.0.beta4) + activesupport (>= 6.0, < 8.0) + addressable (~> 2.4) + amazing_print (~> 1.2) + bridgetown-foundation (= 2.0.0.beta4) + csv (~> 3.2) + dry-inflector (>= 1.0) + erubi (~> 1.9) + faraday (~> 2.0) + faraday-follow_redirects (~> 0.3) + i18n (~> 1.0) + irb (>= 1.14) + kramdown (~> 2.1) + kramdown-parser-gfm (~> 1.0) + liquid (>= 5.0, < 5.5) + listen (~> 3.0) + rack (>= 3.0) + rackup (~> 2.0) + rake (>= 13.0) + roda (~> 3.46) + rouge (>= 3.0, < 5.0) + serbea (~> 2.1) + signalize (~> 1.3) + streamlined (>= 0.6.0) + thor (~> 1.1) + tilt (~> 2.0) + zeitwerk (~> 2.5) + bridgetown-foundation (2.0.0.beta4) + hash_with_dot_access (~> 2.0) + inclusive (~> 1.0) + zeitwerk (~> 2.5) + bridgetown-paginate (2.0.0.beta4) + bridgetown-core (= 2.0.0.beta4) + concurrent-ruby (1.3.5) + connection_pool (2.5.0) + csv (3.3.2) + date (3.4.1) + drb (2.2.1) + dry-inflector (1.2.0) + erubi (1.13.1) + faraday (2.12.2) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-follow_redirects (0.3.0) + faraday (>= 1, < 3) + faraday-net_http (3.4.0) + net-http (>= 0.5.0) + ffi (1.17.1) + ffi (1.17.1-aarch64-linux-gnu) + ffi (1.17.1-aarch64-linux-musl) + ffi (1.17.1-arm-linux-gnu) + ffi (1.17.1-arm-linux-musl) + ffi (1.17.1-arm64-darwin) + ffi (1.17.1-x86-linux-gnu) + ffi (1.17.1-x86-linux-musl) + ffi (1.17.1-x86_64-darwin) + ffi (1.17.1-x86_64-linux-gnu) + ffi (1.17.1-x86_64-linux-musl) + hash_with_dot_access (2.1.1) + i18n (1.14.7) + concurrent-ruby (~> 1.0) + inclusive (1.0.0) + io-console (0.8.0) + irb (1.15.1) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + json (2.9.1) + kramdown (2.5.1) + rexml (>= 3.3.9) + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + liquid (5.4.0) + listen (3.9.0) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + logger (1.6.5) + minitest (5.25.4) + net-http (0.6.0) + uri + nio4r (2.7.4) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + psych (5.2.3) + date + stringio + public_suffix (6.0.1) + puma (6.5.0) + nio4r (~> 2.0) + rack (3.1.8) + rackup (2.2.1) + rack (>= 3) + rake (13.2.1) + rb-fsevent (0.11.2) + rb-inotify (0.11.1) + ffi (~> 1.0) + rdoc (6.11.0) + psych (>= 4.0.0) + reline (0.6.0) + io-console (~> 0.5) + rexml (3.4.0) + roda (3.88.0) + rack + rouge (4.5.1) + securerandom (0.4.1) + serbea (2.2.0) + erubi (>= 1.10) + tilt (~> 2.0) + signalize (1.3.0) + concurrent-ruby (~> 1.2) + streamlined (0.6.0) + serbea (>= 2.1) + zeitwerk (~> 2.5) + stringio (3.1.2) + thor (1.3.2) + tilt (2.6.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + uri (1.0.2) + zeitwerk (2.7.1) + +PLATFORMS + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + ruby + x86-linux-gnu + x86-linux-musl + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + bridgetown (~> 2.0.0.beta2) + puma + +BUNDLED WITH + 2.5.11 diff --git a/README.md b/README.md index be80e57..a4ff8e5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,3 @@ -[![Netlify -Status](https://api.netlify.com/api/v1/badges/79fe58f2-05cd-4e71-9563-703a16605716/deploy-status)](https://app.netlify.com/sites/faucet-pipeline/deploys) - # faucet-pipeline Documentation This repository contains the documentation for the faucet-pipeline project. @@ -8,13 +5,21 @@ The documentation can be found at: [www.faucet-pipeline.org](https://www.faucet-pipeline.org) -It is deployed automatically via Netlify. - ## Contributing -* ensure [Node](http://nodejs.org) is installed -* `npm install` downloads dependencies -* `npm run compile` generates the site at `dist/site` -* `npm start` automatically recompiles while monitoring code changes - the site - is available at http://localhost:8000 -* `npm test` checks code for stylistic consistency +You need Ruby & Node, then run: + +* `bundle` +* `npm i` +* `bundle exec bridgetown start` (or `just start`) +* and you are good to go! + +## Deploy + +If you have `just` installed, run `just deploy`. +If not, run it manually: + +* `bundle exec bridgetown clean` +* `bundle exec bridgetown frontend:build` +* `bundle exec bridgetown build` +* `rsync -uvcr --delete output/ $TARGET` diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..f44eb35 --- /dev/null +++ b/Rakefile @@ -0,0 +1,35 @@ +require "bridgetown" + +Bridgetown.load_tasks + +# Run rake without specifying any command to execute a deploy build by default. +task default: :deploy + +desc "Build the Bridgetown site for deployment" +task :deploy => [:clean, "frontend:build"] do + Bridgetown::Commands::Build.start +end + +desc "Build the site in a test environment" +task :test do + ENV["BRIDGETOWN_ENV"] = "test" + Bridgetown::Commands::Build.start +end + +desc "Runs the clean command" +task :clean do + Bridgetown::Commands::Clean.start +end + +namespace :frontend do + desc "Build the frontend with esbuild for deployment" + task :build do + sh "npm run esbuild" + end + + desc "Watch the frontend with esbuild during development" + task :dev do + sh "npm run esbuild-dev" + rescue Interrupt + end +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..80ee349 --- /dev/null +++ b/config.ru @@ -0,0 +1,7 @@ +# This file is used by Rack-based servers during the Bridgetown boot process. + +require "bridgetown-core/rack/boot" + +Bridgetown::Rack.boot + +run RodaApp.freeze.app # see server/roda_app.rb diff --git a/config/esbuild.defaults.js b/config/esbuild.defaults.js new file mode 100644 index 0000000..e65a158 --- /dev/null +++ b/config/esbuild.defaults.js @@ -0,0 +1,360 @@ +// This file is created and managed by Bridgetown. +// Instead of editing this file, add your overrides to `esbuild.config.js` +// +// To update this file to the latest version provided by Bridgetown, +// run `bridgetown esbuild update`. Any changes to this file will be overwritten +// when an update is applied hence we strongly recommend adding overrides to +// `esbuild.config.js` instead of editing this file. +// +// Shipped with Bridgetown v2.0.0.beta2 + +// DO NOT MANUALLY EDIT THIS CONFIGURATION: +const autogeneratedBridgetownConfig = { + "source": "src", + "destination": "output", + "componentsDir": "_components", + "islandsDir": "_islands" +} + +const path = require("path") +const fsLib = require("fs") +const fs = fsLib.promises +const { pathToFileURL, fileURLToPath } = require("url") +const glob = require("glob") +const postcss = require("postcss") +const postCssImport = require("postcss-import") +const readCache = require("read-cache") + +// Detect if an NPM package is available +const moduleAvailable = name => { + try { + require.resolve(name) + return true + } catch (e) { } + return false +} + +// Generate a Source Map URL (used by the Sass plugin) +const generateSourceMappingURL = sourceMap => { + const data = Buffer.from(JSON.stringify(sourceMap), "utf-8").toString("base64") + return `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${data} */` +} + +// Import Sass if available +let sass +if (moduleAvailable("sass")) { + sass = require("sass") +} + +// Glob plugin derived from: +// https://github.com/thomaschaaf/esbuild-plugin-import-glob +// https://github.com/xiaohui-zhangxh/jsbundling-rails/commit/b15025dcc20f664b2b0eb238915991afdbc7cb58 +const importGlobPlugin = (options, bridgetownConfig) => ({ + name: "import-glob", + setup: (build) => { + build.onResolve({ filter: /\*/ }, async (args) => { + if (args.resolveDir === "") { + return; // Ignore unresolvable paths + } + + const adjustedPath = args.path + .replace(/^\$components\/\*\*/, `../../${bridgetownConfig.source}/${bridgetownConfig.componentsDir}/**`) + .replace(/^bridgetownComponents\//, `../../${bridgetownConfig.source}/${bridgetownConfig.componentsDir}/`) // legacy + + return { + path: adjustedPath, + namespace: "import-glob", + pluginData: { + path: adjustedPath, + resolveDir: args.resolveDir, + }, + } + }) + + build.onLoad({ filter: /.*/, namespace: "import-glob" }, async (args) => { + const files = glob.sync(args.pluginData.path, { + cwd: args.pluginData.resolveDir, + }) + .filter(module => options.excludeFilter ? !options.excludeFilter.test(module) : true) + .sort() + .map(module => module.replace(`../../${bridgetownConfig.source}/${bridgetownConfig.componentsDir}/`, "$components/")) + .map(module => module.match(/^[a-zA-Z0-9]/) ? `./${module}` : module) + + const importerCode = ` + ${files + .map((module, index) => `import * as module${index} from '${module}'`) + .join(';')} + const modules = {${files + .map((module, index) => ` + "${module.replace("$components/", "")}": module${index},`) + .join("")} + }; + export default modules; + ` + + return { contents: importerCode, resolveDir: args.pluginData.resolveDir } + }) + }, +}) + +// Plugin for PostCSS +const importPostCssPlugin = (options, configuration) => ({ + name: "postcss", + async setup(build) { + // Process .css files with PostCSS + build.onLoad({ filter: (configuration.filter || /\.css$/) }, async (args) => { + if (args.path.endsWith(".lit.css")) return; + + const additionalFilePaths = [] + const css = await fs.readFile(args.path, "utf8") + + // Configure import plugin so PostCSS can properly resolve `@import`ed CSS files + const importPlugin = postCssImport({ + filter: itemPath => !itemPath.startsWith("/"), // ensure it doesn't try to import source-relative paths + load: async filename => { + let contents = await readCache(filename, "utf-8") + const filedir = path.dirname(filename) + // We'll want to track any imports later when in watch mode: + additionalFilePaths.push(filename) + + // We need to transform `url(...)` in imported CSS so the filepaths are properly + // relative to the entrypoint. Seems icky to have to hack this! C'est la vie... + contents = contents.replace(/url\(['"]?\.\/(.*?)['"]?\)/g, (_match, p1) => { + const relpath = path.relative(args.path, path.resolve(filedir, p1)).replace(/^\.\.\//, "") + return `url("${relpath}")` + }) + return contents + } + }) + + // Process the file through PostCSS + let outputCSS = "" + try { + const result = await postcss([importPlugin, ...options.plugins]).process(css, { + map: true, + ...options.options, + from: args.path, + }) + outputCSS = result.css + } catch(err) { + console.error(`\nerror: "${err.reason}" while processing CSS file:\n${err.file}:${err.line}:${err.column}\n`) + } + return { + contents: outputCSS, + loader: "css", + watchFiles: [args.path, ...additionalFilePaths], + } + }) + }, +}) + +// Plugin for Sass +const sassPlugin = (options) => ({ + name: "sass", + async setup(build) { + // Process .scss and .sass files with Sass + build.onLoad({ filter: /\.(sass|scss)$/ }, async (args) => { + if (!sass) { + console.error("error: Sass is not installed. Try running `npm i sass -D` and then building again.") + return + } + + const modulesFolder = pathToFileURL("node_modules/") + + const localOptions = { + importers: [{ + // An importer that redirects relative URLs starting with "~" to + // `node_modules`. + findFileUrl(url) { + if (!url.startsWith('~')) return null + return new URL(url.substring(1), modulesFolder) + } + }], + sourceMap: true, + ...options + } + const result = sass.compile(args.path, localOptions) + + const watchPaths = result.loadedUrls + .filter((x) => x.protocol === "file:" && !x.pathname.startsWith(modulesFolder.pathname)) + .map((x) => x.pathname) + + let cssOutput = result.css.toString() + + if (result.sourceMap) { + const basedir = process.cwd() + const sourceMap = result.sourceMap + + const promises = sourceMap.sources.map(async source => { + const sourceFile = await fs.readFile(fileURLToPath(source), "utf8") + return sourceFile + }) + sourceMap.sourcesContent = await Promise.all(promises) + + sourceMap.sources = sourceMap.sources.map(source => { + return path.relative(basedir, fileURLToPath(source)) + }) + + cssOutput += '\n' + generateSourceMappingURL(sourceMap) + } + + return { + contents: cssOutput, + loader: "css", + watchFiles: [args.path, ...watchPaths], + } + }) + }, +}) + +// Set up defaults and generate frontend bundling manifest file +const bridgetownPreset = (bridgetownConfig) => ({ + name: "bridgetownPreset", + async setup(build) { + // Ensure any imports anywhere starting with `/` are left verbatim + // so they can be used in-browser for actual `src` repo files + build.onResolve({ filter: /^\// }, args => { + return { path: args.path, external: true } + }) + + build.onStart(() => { + console.log("esbuild: frontend bundling started...") + }) + + // Generate the final output manifest + build.onEnd(async (result) => { + if (!result.metafile) { + console.warn("esbuild: build process error, cannot write manifest") + return + } + + const manifest = {} + const entrypoints = [] + + // Clean up entrypoint naming + const stripPrefix = (str) => str + .replace(/^frontend\//, "") + .replace(RegExp(String.raw`^${bridgetownConfig.source}\/${bridgetownConfig.islandsDir}\/`), "islands/") + + // For calculating the file size of bundle output + const fileSize = (path) => { + const { size } = fsLib.statSync(path) + const i = Math.floor(Math.log(size) / Math.log(1024)) + return (size / Math.pow(1024, i)).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i] + } + + const pathShortener = new RegExp(String.raw`^${bridgetownConfig.destination}\/_bridgetown\/static\/`, "g") + + // Let's loop through all the various outputs + for (const key in result.metafile.outputs) { + const value = result.metafile.outputs[key] + const inputs = Object.keys(value.inputs) + const outputPath = key.replace(pathShortener, "") + + if (value.entryPoint) { + // We have an entrypoint! + manifest[stripPrefix(value.entryPoint)] = outputPath + entrypoints.push([outputPath, fileSize(key)]) + } else if (key.match(/index(\.js)?\.[^-.]*\.css/) && inputs.find(item => item.match(/frontend.*\.(s?css|sass)$/))) { + // Special treatment for index.css + const input = inputs.find(item => item.match(/frontend.*\.(s?css|sass)$/)) + manifest[stripPrefix(input)] = outputPath + entrypoints.push([outputPath, fileSize(key)]) + } else if (inputs.length > 0) { + // Naive implementation, we'll just grab the first input and hope it's accurate + manifest[stripPrefix(inputs[0])] = outputPath + } + } + + const manifestFolder = path.join(process.cwd(), ".bridgetown-cache", "frontend-bundling") + await fs.mkdir(manifestFolder, { recursive: true }) + await fs.writeFile(path.join(manifestFolder, "manifest.json"), JSON.stringify(manifest)) + + console.log("esbuild: frontend bundling complete!") + console.log("esbuild: entrypoints processed:") + entrypoints.forEach(entrypoint => { + const [entrypointName, entrypointSize] = entrypoint + console.log(` - ${entrypointName}: ${entrypointSize}`) + }) + }) + } +}) + +const bridgetownConfigured = (bridgetownConfig, outputFolder) => { + bridgetownConfig = {...autogeneratedBridgetownConfig, ...bridgetownConfig} + if (outputFolder) bridgetownConfig.destination = outputFolder + + return bridgetownConfig +} + +// Load the PostCSS config from postcss.config.js or whatever else is a supported location/format +const postcssrc = require("postcss-load-config") + +module.exports = async (esbuildOptions, ...args) => { + let outputFolder; + if (typeof esbuildOptions === "string") { // legacy syntax where first argument is output folder + outputFolder = esbuildOptions + esbuildOptions = args[0] + } + const bridgetownConfig = bridgetownConfigured(esbuildOptions.bridgetownConfig, outputFolder) + + esbuildOptions.plugins = esbuildOptions.plugins || [] + // Add the PostCSS & glob plugins to the top of the plugin stack + const postCssConfig = await postcssrc() + esbuildOptions.plugins.unshift(importPostCssPlugin(postCssConfig, esbuildOptions.postCssPluginConfig || {})) + if (esbuildOptions.postCssPluginConfig) delete esbuildOptions.postCssPluginConfig + // Add the Glob plugin + esbuildOptions.plugins.unshift(importGlobPlugin(esbuildOptions.globOptions || {}, bridgetownConfig)) + if (esbuildOptions.globOptions) delete esbuildOptions.globOptions + // Add the Sass plugin + esbuildOptions.plugins.push(sassPlugin(esbuildOptions.sassOptions || {})) + if (esbuildOptions.sassOptions) delete esbuildOptions.sassOptions + // Add the Bridgetown preset + esbuildOptions.plugins.push(bridgetownPreset(bridgetownConfig)) + if (esbuildOptions.bridgetownConfig) delete esbuildOptions.bridgetownConfig + + const esbuild = require("esbuild") + const entryPoints = esbuildOptions.entryPoints || ["./frontend/javascript/index.js"] + if (esbuildOptions.entryPoints) delete esbuildOptions.entryPoints + + const islands = glob.sync(`./${bridgetownConfig.source}/${bridgetownConfig.islandsDir}/*.{js,js.rb}`).map(item => `./${item}`) + + esbuild.context({ + bundle: true, + loader: { + ".jpg": "file", + ".jpeg": "file", + ".png": "file", + ".gif": "file", + ".svg": "file", + ".avif": "file", + ".jxl": "file", + ".webp": "file", + ".woff": "file", + ".woff2": "file", + ".ttf": "file", + ".eot": "file", + }, + resolveExtensions: [".tsx", ".ts", ".jsx", ".js", ".css", ".scss", ".sass", ".json", ".js.rb"], + minify: process.argv.includes("--minify"), + sourcemap: true, + target: "es2020", + entryPoints: [...entryPoints, ...islands], + entryNames: "[dir]/[name].[hash]", + outdir: path.join(process.cwd(), `${bridgetownConfig.destination}/_bridgetown/static`), + publicPath: "/_bridgetown/static", + metafile: true, + ...esbuildOptions, + }).then(context => { + if (process.argv.includes("--watch")) { + // Enable watch mode + context.watch() + } else { + // Build once and exit if not in watch mode + context.rebuild().then(result => { + context.dispose() + }) + } + process.on('SIGINT', () => process.exit()) + }).catch(() => process.exit(1)) +} diff --git a/config/initializers.rb b/config/initializers.rb new file mode 100644 index 0000000..e346eab --- /dev/null +++ b/config/initializers.rb @@ -0,0 +1,101 @@ +# Welcome to Bridgetown! +# +# This configuration file is for settings which affect your whole site. +# +# For more documentation on using this initializers file, visit: +# https://www.bridgetownrb.com/docs/configuration/initializers/ +# +# A list of all available configuration options can be found here: +# https://www.bridgetownrb.com/docs/configuration/options +# +# For technical reasons, this file is *NOT* reloaded automatically when you use +# `bin/bridgetown start`. If you change this file, please restart the server process. +# +# For reloadable site metadata like title, SEO description, social media +# handles, etc., take a look at `src/_data/site_metadata.yml` + +Bridgetown.configure do |config| + # The base hostname & protocol for your site, e.g. https://example.com + url "" + + # Available options are `erb` (default), `serbea`, or `liquid` + template_engine "erb" + + # Other options you might want to investigate: + + # See list of timezone values here: + # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + # + # timezone "America/Los_Angeles" + + # Add collection pagination features to your site. Documentation here: + # https://www.bridgetownrb.com/docs/content/pagination + # + # pagination do + # enabled true + # end + + # Configure the permalink style for pages and posts. Custom collections can be + # configured separately under the `collections` key. Documentation here: + # https://www.bridgetownrb.com/docs/content/permalinks + # + # permalink "simple" + + # Optionally host your site off a path, e.g. /blog. If you set this option, + # ensure you use the `relative_url` helper for all links and assets in your HTML. + # If you're using esbuild for frontend assets, edit `esbuild.config.js` to + # update `publicPath`. + # + # base_path "/" + + # You can also modify options on this configuration object directly, like so: + # + # config.autoload_paths << "models" + + # If you find you're having trouble using the new Fast Refresh feature in development, + # you can disable it to force full rebuilds instead: + # + # fast_refresh false + + # You can use `init` to initialize various Bridgetown features or plugin gems. + # For example, you can use the Dotenv gem to load environment variables from + # `.env`. Just `bundle add dotenv` and then uncomment this: + # + # init :dotenv + # + + # Uncomment to use Bridgetown SSR (aka dynamic rendering of content via Roda): + # + # init :ssr + # + + # Uncomment to use file-based dynamic template routing via Roda (make sure you + # uncomment the gem dependency in your `Gemfile` as well): + # + # init :"bridgetown-routes" + # + + # We also recommend that if you're using Roda routes you include this plugin + # so you can get a generated routes list in `.routes.json`. You can then run + # `bin/bridgetown roda:routes` to print the routes. (This will require you to + # comment your route blocks. See example in `server/routes/hello.rb.sample`.) + # + # only :server do + # init :parse_routes + # end + # + + # You can configure the inflector used by Zeitwerk and models. A few acronyms are provided + # by default like HTML, CSS, and JS, so a file like `html_processor.rb` could be defined by + # `HTMLProcessor`. You can add more like so: + # + # config.inflector.configure do |inflections| + # inflections.acronym "W3C" + # end + # + # Bridgetown's inflector is based on Dry::Inflector so you can read up on how to add inflection + # rules here: https://dry-rb.org/gems/dry-inflector/1.0/#custom-inflection-rules + + # For more documentation on how to configure your site using this initializers file, + # visit: https://edge.bridgetownrb.com/docs/configuration/initializers/ +end diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000..e93a86b --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,33 @@ +# Puma is a fast, concurrent web server for Ruby & Rack +# +# Learn more at: https://puma.io +# Bridgetown configuration documentation: +# https://edge.bridgetownrb.com/docs/configuration/puma + +# This port number can be overriden by a bind configuration option +# +port ENV.fetch("BRIDGETOWN_PORT") { 4000 } + +# You can adjust the number of workers (separate processes) and threads +# (per process) based on your production system +# +if ENV["BRIDGETOWN_ENV"] == "production" + workers ENV.fetch("BRIDGETOWN_CONCURRENCY") { 4 } +end + +max_threads_count = ENV.fetch("BRIDGETOWN_MAX_THREADS") { 5 } +min_threads_count = ENV.fetch("BRIDGETOWN_MIN_THREADS") { max_threads_count } +threads min_threads_count, max_threads_count + +pidfile ENV["PIDFILE"] || "tmp/pids/server.pid" + +# Preload the application for maximum performance +# +preload_app! + +# Use the Bridgetown logger format +# +require "bridgetown-core/rack/logger" +log_formatter do |msg| + Bridgetown::Rack::Logger.message_with_prefix msg +end diff --git a/content/defaults.js b/content/defaults.js deleted file mode 100644 index 5660a6e..0000000 --- a/content/defaults.js +++ /dev/null @@ -1,7 +0,0 @@ -export let fullName = "faucet-pipeline"; -export let shortName = "faucet"; -export let tagline = "The no-nonsense asset pipeline for humans"; -export let claims = { - default: `${fullName} – ${tagline.replace(/ /g, " ").toLowerCase()}`, - alt: `painless pre-processing of JavaScript, CSS and more` -}; diff --git a/esbuild.config.js b/esbuild.config.js new file mode 100644 index 0000000..842c855 --- /dev/null +++ b/esbuild.config.js @@ -0,0 +1,43 @@ +const build = require("./config/esbuild.defaults.js") + +// You can customize this as you wish, perhaps to add new esbuild plugins. +// +// ``` +// const path = require("path") +// const esbuildCopy = require('esbuild-plugin-copy').default +// const esbuildOptions = { +// plugins: [ +// esbuildCopy({ +// assets: { +// from: [path.resolve(__dirname, 'node_modules/somepackage/files/*')], +// to: [path.resolve(__dirname, 'output/_bridgetown/somepackage/files')], +// }, +// verbose: false +// }), +// ] +// } +// ``` +// +// You can also support custom base_path deployments via changing `publicPath`. +// +// ``` +// const esbuildOptions = { +// publicPath: "/my_subfolder/_bridgetown/static", +// ... +// } +// ``` + +/** + * @typedef { import("esbuild").BuildOptions } BuildOptions + * @type {BuildOptions} + */ +const esbuildOptions = { + plugins: [ + // add new plugins here... + ], + globOptions: { + excludeFilter: /\.(dsd|lit)\.css$/ + } +} + +build(esbuildOptions) diff --git a/faucet.config.js b/faucet.config.js deleted file mode 100644 index 46791d3..0000000 --- a/faucet.config.js +++ /dev/null @@ -1,44 +0,0 @@ -"use strict"; - -let site = "./dist/site"; -let titillium = "./node_modules/@openfonts/titillium-web_latin/files"; - -module.exports = { - watchDirs: ["./lib", "./views"], - manifest: { - target: "./dist/manifest.json", - key: "short", - webRoot: site, - baseURI: "./" - }, - sass: [{ - source: "./lib/styles/index.scss", - target: `${site}/bundle.css` - }], - js: [{ - source: "./views/index.jsx", - target: "./dist/views.js", - fingerprint: false, - format: "CommonJS", - jsx: { pragma: "createElement" } - }], - static: [{ - source: "./lib/images", - target: `${site}/img` - }, { - source: `${titillium}/titillium-web-latin-400.woff2`, - target: `${site}/fonts/titillium-web-regular.woff2` - }, { - source: `${titillium}/titillium-web-latin-700.woff2`, - target: `${site}/fonts/titillium-web-bold.woff2` - }, { - source: `${titillium}/titillium-web-latin-400-italic.woff2`, - target: `${site}/fonts/titillium-web-italic.woff2` - }, { - source: "./lib/app.js", - target: `${site}/app.js` - }, { - source: "prismjs/prism.js", - target: `${site}/prism.js` - }] -}; diff --git a/frontend/fonts/titillium-web-bold.woff2 b/frontend/fonts/titillium-web-bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..c34f1342709cf74cdd95be678c97b7117ae49fea GIT binary patch literal 19300 zcmV)oK%BpKPew8T0RR91083;54*&oF0MsY|080h{0RR9100000000000000000000 z0000Q78`+tP#l8}24Db#BoPP-f$KBXapbyhgb)aVGIWw&#@P9 z=S#+LI{@v!|LkWZLbpSTX!bwH*@y@>4gfd$;j;h#Pf99dYz*8v2LZLpRH;lxYK7Mp z*4v14c3)uu$8HUKVyPz8YGAB^9fooGz!fqH+X;hG)8@5)%E0x-w5Sd<>43 z1QwKB%b6KnqN2ChWri=YGjq(6J-cT*8EVr4zyE%Pd&9FOs(j_!aj&CHsGQdaZ~EuY zae4{IX4*`m9aBjw@zTYBJHLcb=1zE??4I}Bne{w!pYRCb7^u^WTsC zZ|!~GM?GnyGECPdjfzJ;sfb3=MD0ZCgL*rs<6PVK(D_5>X;rF=|_cCRCtU zbAA1d#0Ll_>Qm2gW48Ts(u03{7-(A%X#1FO(%Nbz9s zEJ48{0g=QaEr2D+wS9wgI-@QxltqQc02Y8pNNJY`Kmq&@xJ`RVnE_Z0*rah&x$5We zxEFcfH?^uHd!b5mXgXlS5;|D`Zjdy#$B?(miw^#PyY0X3>|c=AoW%g)OwU^Y`n)@n z=IrS5Um&FhDB1XwS<4~8xNf}&F}kgXPt1vh|1;HWF-inax&SB2nR29@QMtXC=#WZZ z<>#W={e!_`cCZUz!399Br~t}3g0%QZ@B)cTn4MV=1bNR8NbyogASFGEr*n$24k@&D z6;fZORJo`sH-(=o^F05{O6gqfDJ9m>Y(7w8Zom|nA*!kF4Nw2{I>b^bBY)*q?hSD) zhY$|;F`%iGN9#0RWI)UfO$|6V_2Qhi+mfBENvs>QI-nv@7v^`ZlZUVQU+m{pW5#8> zmP0F-5S!2hzx?(sev{R^FMhPg3NG@=zeIu!>0pO2gkc&Md+zqKH+)?mX)j-2Cr+%0 z7;UW5%G1jH`E&Y6lxqVH!je%;HY4Ed*!Qm;e`ESJ{MNkIvRMSr3ylhfW{+h)jhQ_LK2c?7+^k)S{YLDG~eseEwV`)=QwusOIsnL|<3J>^TDz&4z# zNF@6`rJQdzr*?bR5L#o3jY=+#?3fS8YV(^Z^T~AFoxLs!DWNN+B@V6SMk<-m*`>_W z$2SH)j7`pR5u%kcT~%VwDG|nS5Sw`XO!1`F7mv+ScB!G^ViCtXxG(;tnjBu|)Fk$1 z-@e;wPEK64hU;8ov&_!VoI8kjHG_huX{Mx+Zm7~ej;RBi?Bx4D7>a#S{0_7>$6}Mk zG0D#EbH<1Cv(;hb!?DdhI>--@d`XrDTsI*cz9fyjip7F&B|QmUdvf<)jx2Tiw5{jF zV)d(hdURH&ZfBe(b~FNuXn)iX(qvaX=DA>^x=?XB#}p^(c;8h4;Oyi+V{s!J%^J1? zkTS#6SoZ9g$K3tH&msUyk5P27gi6^s#bVWc{2vFFU}tL~mapi%!Rbe}HkG-j`2b}> zy>rF6BEhOo4)Xf(!9H;FeO(shd<$DoxV`wBpi z6G`bcP#4lJ?XLcIHqY2}#!PeN%xAW;g%+t;DFD{mN3fao*vxGXwzO3|oc+P}4oX6D zcEHh9vpc!o5NG$;EXX!{+t0~iCvcrIBh_7tWIhn+_HUraQlQu8K%bT1eqVeuVC@|^ z?1x`Q{F&|kn8nPqu?Wp`@r<8)35)M(1PQICyB=RDv2!b=o8tTSs*EKZEx&0XmlF~J znCelUOi2^0S#01O*VP~jE@(15Cg*Eue_YbiQKCl33$97Zg4Bgis{`XNE;Llib71fn zph4Xi6q~wn+?E0w%=|VbiURFmz>NxRx+$m7T9`YAyd5{K6d!mH?06gHL*Yst?6A~I zF+7f|)$qUomgFP1WtVoOxYA7x^%%J#1vHQ@7$A|1IMy)?5jdI|Yh&W7Jp*$)yF1a16#{3L#}RjpOJ73a|tU}cW`eZLBK$MQeDX67vSTY6dI&GO9X zCid9F*lwezhW3WKnCF;T%oWUbgGPe_g9w_h7%&W|YD84(A?Q!&Tj+80=8T~9t+=N5 zN^eT@eaADss}l{#5E5r`~;;;;(88UNlGuyP(E1mS|8WKiT3LQq(#nWIrK zirgy9cFe3pD7>P6L;bjVi|ThCMNkMqA&z544;^-$LvC=<)5mV6jgodb1@S{i(o=j=F|DtT$Dpl-^N5mOu*RKmf@Q z6&^tbWupv8f-!E39{pfc7}D11lOOuLj?M#}2&fzy4`s0l$Q&dKCgT%jrL9=NXJ{Na zQ?QH`p>6NxJC-n3N=0-c7XI=V0Yr;H(gghvt5gbBi}g__q&pR?>thz0&$dK&2JTG# zsmxQYWy^Hc6E6J=dBoRULq^KwiLSctW|<%K z%nJ*DdXa%|p!`BNa6X+HG$8i8XJPfnu^r%IZfVhS7aMm#yF2|DFlfj~xjfEQ*WE1h z1D<(dp)RgQ4*&p802BnlG6!Zp`|PvNKKl$Ign}RlOc@A*ARIS3ZhYn)DH}ZHkx>niDW7{wK%gl zmt06LC0CMb$&JjL-Olc2_fb4V;X^Tv!jIx9isvYTC|;s?4eu?y_wYW#Z$g167=@zn zLf0sCv%pbBPedJ)CMQl!oSrxfWux58`I$Sn--Z1yBlkG&CUT$So*eh$(tMb{qx+5v zq^Gg!u#>nIP6S1$f^JGiD$z;@I*Cjn)5shp&tsP^yK>r%%Wj=^vfsxa za4_(Q@Bm9NEQS&pk^2K!;ta(w9s1VpSG|8FJTf`}fPn;p$7j&eQ$Gz;pN;6Q3^ef( z8h_ce^ex^$0|^Fcn>uXrvhc*RKEr>0y_V50;><}OX>*DPL~Oy!#!Tmz3>-$juHtJv z>xUKGE)EGHZ~pfHBmi4thzE(o*Am0PR4SUyI9iQrnpzYS#S~ymv~7ymDH#q>!6;%? zgSwWD2Om2LNPr|ph?N9&L%hgVgQH(srrcdT%R_oHbqVeiwY3nDBkAtiH# zA@t)qN};pO<(6R`CO*j-MXz79QF>MmA)o2^<*Da_UY6HD zZ@u69c!On27#^@Yaxc5x*czBJBJTv0q-SL``lrtPXm*Gb<0e39{64tjI6f+@0{5ew zsg~8FUnWO$%#J$|-C6qT45-}r$m{mr-I*{p97m)^YZ_~#t8ZxBrmz{yBoQS>=g@bI zRFk8KN~6cw$L;arE0=>stZcDs+P!0Zy zwpSdLM5R$#R324CNJn>RFmgujC~yd4BD_4sk(SYAu5W2w7#Nqso)iUI%r#Cgp2{`T zou~iaciZo({|;7(XWa63>L(^CoiAky@Cq+_g z+P;{u))YH8#mn0eY*Z8M2u&36Dl=KgmLpf5{7CnaDe|O9K{tg~)Zh(ewX5o=?`YHn zF6Qf{L8B(}p@dXMTA|RE_RyhImu@|JOCPu20S6s&q#R|BIqpQ$nO1ePHChd*l{)nr zG-@i%x)!b4v=@}sGcY!j#+z0(8ZJATR`)L`O0yq-A86b5eOwK~YIrMYUii;Rub= zWNT^b=<4Yk7#bOy96hC)=^{;|?4q;H`4-9&D{C7}VL=?e30&e?+ez)`b};FvCQ->% z6sRwZbe}vY$T(-j47TpZo;IgcB*wIa!$E`ORl(f zP3%uTa}8h$3cm`#6c3mnFa+9KLLKWF7@3$AN{Yot=n~0PdW@=AOm^HG?)4?JyK+NN zJEo8D!1nALaq7G?k9LOW4m|lVLTaRU_CbK##t877+jmM9U=Hm4RMYK+h&)lI9*rU> zqJHP}-2g>VzHvcc!<9Y>FjKYCr!Q^MzEoZV2&_}A~9*?nhNrx`E`Cuo-(f0bW>|}>NRN8RGJT3 zv})7dOfyTy3L86z&cS)g(2+APaU0HP9Z#4rl~d#l<=nvqmt1kp4PLk1T@FkEhCnd1 z_b$zDZ$;ny5%%MAHGB2>IV}B>lbT)$H3$Sb501g64C=& zz7lMQnf2mbE`Z=8kx7?-+9+<7C{?ChMUXnWu4XM-(cc`^GPs`&TpA>&j?yFz+)g_XdweO%Ff0`jF`!at>VP(ESHBa z%C%K@o~9Q~=~KcetlMRdv7^V6SqNNJA!!x^a%WEXwIJP9bwgBj)vj(mAbex&ys&}a zZvP1DJ=#B?W`^9GO^c*Al~IS)C3Y*^KI*lK5LXgu;_1Prf45F$GyLZ0F30S+6TejV zqmBcN5NL9d0!k{&m}xj4q0ySU z+Sb)KG@hSYHRB?EjG9Ah5`gOKPWRi>j(k#w93`kU`spq>IBBjfPy8=m`O>XF>r1WE zC9MR`kyMq2*0NEgcjW71g)@7=sHrt=YFTrrl2Q|?wL0}0G-@i%r53H)w7(YBGfJgS zk|arf6UmSR87M;?m*F)#I@!riZcC>sXDH`N7hH11wQH68o_yxQB#?FyWm##*)9=)Y+YoH8eF2n0B>r|&YMFBuj6h$qZ z8v|skciR@u?c7OspY>U3dh;;1aqANovWVCkoSo`ar}{{8pLEC;9Mvc-rjDM0k;(2% z6Qzyqg=Y=FQFZJ`uA>G$2C=GY7q9K3oZEEq@h@y|=e^$~60i!gg<2W7j27R%k>C`@?Cxo{Q84HAH&RBjJew@^aZ90D;%CcD)J45N-O${AG<$|*k+vM@`RTofT@9) zLkY)HW-V(dPbnHGtEy|(7DL9qj%@U74b^qvb#B@ysm;2m6TOfTZ^lMo`I3c{7fHZY$3L?%`QX6YU6}IT0n`ac1n9) zazCo*F?PiyvtlzCO?*!Asa?D44o`WR-~1}~R`s;f1`O0Y>t@`Ti-M^ha0q?P57hKs}^$%!!QhE z2M#IbA?Qs8yp;|#m#Q8y(dBCP?8 zIA#M^ViSVx2oLCOD+i6Q{SZB${ng2vXc!v{!d6W}a1T9R6rUwmJ|ezP7=`)~W+e`L6& zLz+(Ug_2OJOu32)c{TPP=B(%dwOXxKtJMTS5QJ*3VM@$9mNizhv2*CW4w7Kc?%;)? zHD_{h8(t5z3Q@xVt_MLK@pUy>bS(u3qm5%-Zr@#RrDBjF)gxUv+u(E_khK! z!D%D@!5qgHl9^p@?%tf+l8CFs4j->UBCxuUY_E*5cX z;D{VflNzInQ^cNq4poks6+3>bpXQHKyptgXFdG;lwpDqv{D^doEQ`FMs0Cyc5G-WV z2(SO#zLLNK004lT6j=rU0PxKqw4etg2q1s}0)_zqfNuyVhX=r+Z$~WfDHpPc*!nqj z&_M@4mSuzxI#iT__a|Y3_C$mbN&ui;5(d*8F{bp^A+WOJ-j~klbe}Nmh&qjT&Uv&PW!3FFA|RxJ{SY&D?#`hs1}wpGBavy(2k-C%h#xk-ueAllEnid6kD>KT z{r{M;2_~9kvMHvTX1W<>nq{^*=9*`|1r}Q5di5GKY9iOHMJt6i?K*Vo(yd3YKK%v^ z8Zu(N4K~_jvn?*TXxe30U3b%MGw!UMMK!ScYaSc{80~)99H6=-ph+m|Sy`M^yscgVCkgSt^vW&Nb8)3fDy}SPvdXKd za`8AdhF4yF_14ArTS$pv#4NT{vQ&IhwMi_gxSMjaj}npL%z+ad9vLyrW;pOd5`ZhN zoN~@e96807;bGxwHI#DNV$hsB6S&W$Z$>mS^d5lR+>HOVEFsPR!9$B|FoT+GHi&!49y${p@S1r+T1&wWbx#c-2avnhJ^$lS>YX_+Uf-zDm4(;5sEk zkdXN+DIlLD^fe#xG+KWFQ!cw5&SdJ?uGy+AsH7eZ9gBM{4MW(I)6Nw_M2L}SM{kLU z3c`c*$P%XEWjJX*pjlk927bAc1k0sEmMIgNRHQ`>IMIoZBx6jbl`J5IL~A)o-GPLk zy5&JuCZizbgh41sIv3(nNl6TQg^HFkG_oB^MJrP5j$d;LkQQne3GcL?%Tb{HgzQZ4 z^|`q4-Z?w;-{7cLgb^bH3Jhu`$q$2bRL`q)}UrhLgXWqiZD0GjE$P~hllLz;o-kgZ`EX}Ao-vSZSeAyA(eldut#Fg^3<$c3 z22iV+*9jQ*rlJYM&UY9j`cZ?nGs04BX^>J!s$iqe;Z$^V;ZUfVS`PFG@I6RrYs-em zM7I;!6U*G4Lu%L++4kz3`Q>_3dbi5K3PP6F)&@a17qM7URwhoLRFtcwdKrZ@I{_K{IxvPAhi2TjVm4 zJ#YJAmz%|Ysa@h;E2n)Y_nAqBT$uk%dU-t70>tpQ7M?%+;-{q-Dq8` zSiE7fYp~@&P_%vGWv0dZ~FqP22 zl;(DiwAx{3z{JQf=Hw_WAE zs_Y*fx8J^?CBaTUw>LE-gIPX*KKmTehPml*##-`*JROQQe=eJaR<1zYS&tn`T{x5AFl0q=Cr(DUj;N8<^=x3@f;3)g!%N4A z3heKg;Km3_z(5c|xn>BtFhpD4*urL6V^pL9HFANA+`w6j28n($Sj` zvOO=5QZ0j`JW(N)?|(cTaNU79nW@!Q-MVC`n+M6k0P0Z#HEQFr$OVjl)&OOw} zy`20Chss??BK74LDnW5IqGI(yFEhV%vmk;J*02uLz(5xci8(q!zJQP$Yq*LMmBNiD zQViB$@!}5Q4qc{cFOb$~$4RK%XX1ciKcf|E+BLkpk>PmR=jhkDp{ej{>}7`tW#=BV z*9i0xOEPg8`rp@qJ*~wMvf#+(fXy>bK89ayVc>k}npTyoz-b;1YkW})=7y*gejxBHtGiz#zO zfeQY-J?G9iGrf8ntW0e}oXRC!AfPkWg4+V-Xm-|!F&)unksVuC*~_<~^|YIwHEPDP z8z<0?A3D?*;G8_zaO8Y!vW0Xiqi*6KrlVPGa*M!Z>M*}7aGQR*#hpWI9pvG*d>7uZ z4opo*P^bs~xZH6Bge8s{pyHz7tXX!6B&EFG0P4dC-cZ1S;9AX2zuWF|am+fRh@cb` z>fK{KZzs*4h7Cm6W3dg^^})JP3fq}u_v0t=GO zPMR}X+NswwdjkGp^!(3MI`PBgMlwHP$K9wIQYb;7`h%R_cu)PY*#k(ZS+JIr>n+j6 zj`3G6g;}t9z7dg^PoG>T10Lv-X&Um=tCCFp45SiKAR23 z#{=Ai`|}CA&Fz3-di483E*P|D0-Zg($FAlZCzNn>=Lse=R$KQ53_{W9P#P)rqXtulD4m;xWi%H!JIRy9vxqdKIIfUmY}$5WXPSPNaHwrXuHo3k2GqIW^z0@*=}#MyEB>ifrzeVt(8rKh{sO5Ny~g z!)af4q^QGPhrgw0jl#a@ptNHA@Wp6Z3tpLA`Wa5Q&P^9G948@6E*^fpBhdHJ5yNe~ zzSVF48KwO%3K}j z-T}Rr+vZ(e&7G3yAfqvOG2-nJcwb@i4KpmpCaziBg=Sx(Q;G1NKEV=yUC}qO9%|IYyA${2+~&iWR+#cnxr zmCccrlfIM<{af|SJgVq&KF+!us?7W-p~;i$LUS**RVKz!(rp(p94Ue2Z_ipu`Es)< zB^p+FNku~bD<$gYS!O~15TySrj;l0~SrlzZjcUB$;5C&4zaC67Qi8{BG$2&*MTr^& z9#P-`Ower{m(&anIsJvc(TT2}iyPMM0keZx$A1G;9L_M|L=lh@{ri;`Z?Q>G|)7u@~uedNo%JpwP$^8NxvVnDNp7_?{>) zB(+$OFw8>qIQIYxs*+);1RH#gS2VFJQ5}GIv@oUuI!%j?6vc#a*Ol+aVtq}J{Z0@; zB(50&N%`mMb0y{0i7QL4PfIaK-N{K``iG@epgEG&zxTRm5n39js|UCi=gN(_OEKOR z!G@AIuGYogcyj#yXBR+&Ibh;1sG?c)!D68HvKUWTIg3^mz;O#ryf;V9~8CqT}qnk8uzRnsP@1C(t7nPLDvt#F+H zOA@hSSSq41ElTkK4Xgdfl~5z*w_<`KU6>S(k-IzBl5WBXsG!enu;TS3CN5r6E=-XR zG7P;NX}Ke0tQ1Nj7iL4RJ@J^NKTKRMG65Pi0RTMqymdR=vr7_Q^MNj)IApjk;2`wC zmsa|k&k|@^wA;z>27!Z^VP%;yoS2}v#+`W_g$$K68!^%%IFguT$AC#68u4Ipb$C9E zNp%{ygh;k>n4e#Y(iW_cI14L0AhN{8EHHSiVSqanZLmO&@Foi$vf3?>S&1jB2aNI7 zzu=tlcPqwZ;xD4`Xrcx#OP~p_(mPXDJ#=CtGsYKoX(W0);}BArVs}Fm9_P6q0_*Mt zGsYlYYu6f0rT-wVU+P0w0x&r@q#`$DB*yAWM7~#=`ijWmNEM0WQH)4iO`bqHv8G8fBxTBPTw; zOBcCtNPczRaf@2NMo&|C?RT!!r_JE<_x>d?x}j^%tLu-={(o(}+e$ZYnS?(7u~G~? z=K-}n&tB7g_YXVhEwwA1G}zg_Q_@r_2u`{t2utwd9mD+;!o*OBB`cw*dOyS-OI}hMm+qOV8JH^m;67 zdfXA+(77f?&I@wD}FY42b1wugzG%pB0^)|5E*`8i<2|tT3 z=lFT;V6rQ(1giTQ*vjS*h&iaeUF8*5{dx|zeZPbW_g1OQ0~gW-bRi)n3r->^K?Tat zjB!=t&=|SG|KPt;E#Hl>mpQj4I#jB-Yz^Ep&zG-9QB(2@xM&Xmz z)HV}&<@CskaVN@QpkLdSraEbqqgkxhBv;{6EFo88u9THlBxIoj+Xl&&McATn#3TD+ zg8$3V^M(!AUH2mldiFyNC~qZBMn%&$+_073qm$pCdp7;Hb#JR*)7;mC*atKxN+C;i zhnGsBkdt9xVSnm?WFUv$Ikhmg`p(-V+j4O2cg4NxoNDmnQcjgHO9@#LtPDjI2df&p zYG#M2Ga&l=vP@&*LilB=0)$R0OQ-8T4Wcdxg5TBKnr+SsW8B2dAJA4LK+a@i_LFpn zBUC{p{QXpr;rsI^&V9mtN;?+RVbl@SZgi45`)~q3mx?0J38K0^BSYNN(vme|)a#Uy z#kg2e8aq}HYn|t`uD3iVJCd0gjIHZ%Iq)};t^Z>0-e6o9pFKvrt<+&^;oS;2Cm>68 zDofy0<3Y!JRRuNoN!g}J7=_%R1Bw%k9^~dv;5AgV1tkNPhwZ zP;QhH%K>$)dkj1R>1l!LHAKc%0N^#0t>YWc3`=kWz&WF?o zmkUc|c@7dC8!9qWsvGI-ns%DVv|otS)yV)$m`o(QbG^T>E}^I>`$>&xrQ%?G$M&YM zc>OnX(T?WOeT7bLPRYOzQ!%#jJ){Qr{JA-3-txF9CC1~_;iJ~R9_ej~Mc}Pi!KAiL&8^R%A;?5# z{c!MIo#>%}2Po!a-D6#GA;@a!Pk`jvY$%_ji>c6bXl~2Ese*yyfQ6S$%1+;g3`qRN zQWn1BAJ(T@K=P5)(2b@6eAW=gEcXS?syg!$)-%TI15+g<#!~snw(f0!qvWi*KD9G7 z@ZwNzIK?kHQ38vV<>2bxAmm3ByKc{FnqxzA)wcF_`%aul85%p%sS1@OsCg9cB;At< zRo||Cdw72CRx6$w9Z)f%=cX3i1Zcy%RHDTSqw@cnW$u2AzkdIM;9VL*0VjoK!DyYb z4fNp`rwFv*#?@ohq*13Ro__iEim>hAwYmVkJJW{7)2~RLE?7ShTWAEyPe(S0jnY1n zxjPdP!149?>-ZuoI>$hKQeWzoTx?55m=(d94|llmPV(6Lw*YJWq|{-{a_RD@gA}mF z4@-$xmM>fW1Tt2nm(n`X30q1h#1*3hNx3L8L|CRdau=KWftJLJf;58DM!$>{;|laI zv|?OO4QB)WrNnhMj;$aiKa~ycaq8Rg)I{c+`Zz;TTZ)in2Hb3DCLGojGat&9HjxjN z;Yh8mf%?XKWs`5za?G=}8_sIU=!iU-a;O@dJ z#klIH=S_TiDr6{Z!LH;-x{XuyTrG(9%nve;yyP7o!O5-57laiWkP*Troj_r{IV1>G zY@W=G%&Oq$v5NG_>Ur&2K$BFyT7J|);Q%IImMxb}E2gSyCD2+))szBs?&{gJ>ksPJ zQ{qShVA?O*Sxe@;6(U;Ui_ufJYeonaLTA9XT~%wfYh4$)7ehv>-q*gL*Y&@c1`-u| zGyNl#$W8r`+x=j17)Cry2sbl3 zZgv_DNfcr)aKfKIkrX)NuP@{R$3KE=F;P_5YFDHd{g?dVch$!v`EZ&!MB1#zUGV>( zd7jozh#(A(_rJE|MnEwfA{3&iqSQ(hzZ|?dJ=vu1Z|<5OQ>BwtnPc9HyM|!S!UcAb z-C;Q2Xt1~UoqbXe?66Y0*f^;uv#c30|M;a^B7RS%S8__K8hBDyFRv$y$>==q$SA8; zhEm=kFK>drObnF!8X{jvJ;S{NDM7BN`3|PjIdM#i-N^D*Xf6vDX_=A9&Nt8-u`qsK z1$aIQe7n0Y#`Nw4mECaXk-ib&l26K?`{5`V?2-yCxu*9&Btq^*Kwp)wHo3OQTfl>V zlJ8`KZb?*29(=r2u{nL5kR>K-9V}P0eLm{^GkD zVmkP7nq!E;Q(8C4)Fm64?L1tO}@O0&fK zt~X?_+mag*8qS0KSXCs5q1 zB3EUVIN0Z31jv>4@H%XCT}#=@D`2qF=v9Ydmnov57_Yj<fGk#sq0>nyJ`1*+T zT^*|(zq=xa!-uQ7HfM!{k*bR=9 z&&p;;IssfbL>ltmgXIeOhQbXbu;H{^BHt{pl!NZILMWL*E}iJv&C&T>3bwGKf^fDWFQ=C z8IA*UI2mRcpzg6eDk}OUh?K*R`?dN_`0e%s&Isv5o-}WdbWesfW4!)`^al7b|CSr% zaSx?s&4xkJgH(kkaU1XRl7TAILYClu-hm=0mBL|(({yd55hKP2Wnvr+g>jjx0IK+r zNf5(=f$(gf!NBsW{F2;4ZKK`nx$7X)$zoGDCo^w5-g?SN z8U_azv8jP_11?TC);7k{gMy(uW*_>HISUrmwBZY+i@54OG4}{dc1zTV)6#-8|!;{Z0n%+T@(NJ<&evL80$1-q8yVF z+9xLlD|MfmOBsP&IE8!T9ZLP3ycdSx|}f#=!~>N712Z=S2ThXayTJoEK@8 zX->3?xX^pYs`qd3*yB~lxAke`M9v(k9)BO`DSGsLuecO*C(j=ET!v+e4kwuBaP1}~__7Xo7PWdj> zM@jp^{+HI$8Pr+Gt{UXj3|5dBn-Us*!$|EcBJpqtlYq-u7Wr>dB2E)CJcnh+wqSoL zd%G;5&hQj_27}LF@|n>PMp6rLgxE?ReB+@efi4sf~q31>heTp$J$RV&c4kDMT%h>hvQmuMmylG zB-KJDM|>2^&t%>j`wPqCO3L;&e|V9TSCUV+fQ1nlCu?Ke*=(05Pjr%`s&OVM-tO!v zj?G>>K@yc3$JbAid$2Z!3x*jv1CKq4he}@w^<__ySeZ=SR%OU4amuFrGglI*Dq`Sf zWKK`ryU9yQh3%Y;%(;}t^+5bq=?y7y-}coB2!AdsfgfQ)OiS{ZfWNVvCT514*k;9h zj#Ua;Dn{Iift@q_uMtN0^Yatq-+nINw;zjdHSvB$kfqt&#XK_b*qb_2`f~7I>aI#R z9Lb!vzo$s=x;-$p^Rj%+Y!6#zbZhyUMNHF&ic?Y|CFGsNmBsWl4`G^cF$)@#6^VJB#na6wjzk%pnHaT~7X@d^z~qR@VOL0zSMuR(Kf`sw?AT2lW;|?-uSB!T z;LO>qgxWswabZcA4^ckIByLd8B0M*Z*9K>$#vP87^fm#dnrYsrk-VFC(caJ36_&-| zaigvy9vnQUl>m*jx3_@z@DQ|%{LH5pi z@}i9T2NWq)qINlJ^Cf|JGhX|vM10}X$ac&E@?!Vojx8F=yg8QR({U{^Y;P|8GgC-a z@y&$$aCn^@9^5CBt}F%peKI_~S-MaR4vogHg+6c)===^(?de>oO>ZKEc0CAivJ%8| zNm>0=h$jiE2v|jXt%9bERP}o#A*!n2edh0V%vzgjl{1n<*Gy7cI+f`eLy z1MD8hv4(xt(Ey-7>P%YPo?^PV=@EJGI(DUmlBg3_$Cs#OnC@$dXP6@F7wut47;h9J zz5?I0M*rgc0vdV}7dN?Em!ykI??~mS@@zc#trgB~CUakyy8=@Zxnjo{i?P8G=&23Y zvr>GvHe*PYfV-x8o3@4ZI1HwH`o$f0G5y^%>Y;$in%66wb^-R6t4A~E%B^l%&5xU; zE!$E>kjoX2Y;KGJk5Kt!G(rL8+{UltU$%vbbp_OQStdT${t(# zBos$* zVt%R^mci0bVE{5JAg!1%k^CiE=T>Q9-s&2XkAXYv@8r=5HxlX<@I%z@@8 zTf=TBZ>(OjwaYG8<9HqCQ`dzXGGY7uVNvyl$xq1%V5$5nxN6UIWWE!$azBhmsMUsz zES%exD{dIITFLAsGLmjauW!jduXb{E!zjtqds^fCL;aw6@W8v-ry0>%9hg*^QNxMx z3};;unO3qvl}g{PCvKya`(!00-Nmy1R=T|7`X@;pkodB$=`7AV&HJ*DUv(1|XrF2^> z5NSDOx+Vz$iD#)Gz^~c@(I^D64;`yWdZhO9s`<=Mz9N}|EV8+l-{Yf_+sp63P1okOeI5N-MO;1r;#cKfZ}^eJH4&kz^;ERgM7WoKn2E>_-5THi6A@- zO8%r=3oZo1lM#+94l4{xGX=-??RT@Mgq`ZlOmCS43ExaI{A3$^lz|zK5=7cDc4gO$ z0Xa-upb7%hJzrZn$YxR?CeyDqQoR1ZXF((HVhrn=&FaisU*>;~{o#6YwxC@1z437y zaX^U1$&jK~FaXj#nHsCMB| z$vo>J#&rP!3;y)to}==kJ+F+~74f$YFd6We9PJ4>-Qw=!An*kr^Br@wqahl5K~1<( z`z4&!rXCl0CiGgq+Z}n=c2+mdPFyZKSOIE(eM;Q{H?g1Lt_Pr_7L9cFsdYi}Z=aP@ zwYqSrlTTQdZdp<}=$ZfI_kW#GkD$4g#ADh?d{L4q3Y^q!JiZNRLOd| z3@^8a4GCTeA?ESAuvlN%I$fbgnI+B^(Dw=!GL!$2>T*vafcE8tZ(KYz`w9w8`%~v= zsLy4kquF_QG~zV!G{UOGN>6hya<3*B3UbxT%Ax$EH0+G?j106jT872K0uephByDQ~ z5I=ph(=YE{VlWAiT>>!q`lmN100#J(PbzTtW}p0;y?tS$R?|sCO=V+G237W;uKY1g zPtT$wB?W0^p~Z#}VH~2Z?8Jwx6~;=|+evX{x~6ho2KuHbMLWwUi^2fmZsAwLZ5HDf zM;2+>#7*{_vS?BJEru!9!}~#UPDBfydB!Y~HzaBXzcdepklI9|2=N{Md8hSMiB(Lw zRg8p+ud{OF-vO}8&C)~SPbz&F3X6&b>vX=rawuWJ<{{eg*^n%beOg1}6CRbEm=gn_ zb2$}uFUm>tXN(gL-2!1p+=|>3Ztl_*Da{Sc|F$Yk*Lr|SdO*-hm#((`8}+X*Sa()> zR0_babc}Zyq3%j((QFubka+>@l(xmTvWcPBbHr|qZDZp@2qEN`R)tmw?#C0mL_dC( zK%e-?1@T%Z225!$A^G*RMC)XsZ)CIuR~M4M;zh@UW2&^xc7*gbH8o)r%bYT7ha%Xk z!iAR1AE9RtWZ-45m?POl=fY%JpLOgh*?;uTso6f0BWr#3z?(WPCoT8-9a71TsvV!C zJEb5#Vo`>#Dk6R9T`#|~F7z||!w>X@_c6nYl4g?Tb&}_kXFd|t=A{SvF7QdC{NP0; z36IBrf7BPiqHNz5Wc}W|zpu{v(N6uk3j0afk%Q~k4>=!4EsaYizhL?CO39zh_Xk&+ z{bcg*zS>(qR2l!3H-5d(T$`KMKTkt{3BX(L#sz?PuJ-Z2_}8Jh4VMy+N;vtwn$JAk z+}^)Gh`i3nKKXE;d&9eP=d8>IlsZY-%=tuLfb#=t-1Ob5nLw=WJ3Vl684h5aWwxe- z^_H1uXu6J7x71d#3j(YM zTj!XbZ)e|7C+}!;y&{SCHh$&NUfcUON8@)A)G4Fz`DK}T_`H&*rtZ%&{@JUa9d~ig z!q-|qzgZ`fdC_{css0gV76%fo3Sn`OtXzTUho_6uO}V{hpN^!y?;NY-F*xqLEYn&R zr96hrue5WCO$52nWJRyZ$s*Yw*~bDI4zNLZvvyKe6-FHq8n01pmCfF-G>svv4nFH zA-eM2m)F6%tX8s$L|c%ps7=@}_0{@?x4)f%J(rFxDNK zIz)>~2C}&|EtZ_aVt~b-5(y{F*VbXS1MN$>E`Tkzge0CXV5%*Vvl(atO5>@yT{nNQ zGgt}$2-_O0@O-?VU(F!pR!PMI{GGENVOt8d;Ic<*_F-?dY4;NoHMF zk2U0rhf)Kr#>+7o_S0vfZMbNG(D6UrfMQPHjXCj>>^y5dhb>@YreyRt>I!8Nx}htz zJXrBuWu8D$mOE+clfl6Fz2s_0kMvLvY2QnZ;nV^xA6u z@_Eiach`GAe73sq`E~9tWoOb^c7uMO7GYY?`Yf_#z$YhN==ozqrQF2&|X?{v0^ z)bhcO$rvauTe+^A4Z{JQ8m0U6OMXEcYPDbNOC8XLevvP<8N9u-ySI-HjpJX|ik^Nd z`JOw^>(B2WvTVoo{2=@_=35il=-jgaPQ=v+wleVc*tyZ0n{`1#V zDy->->B#w=cUkVl46lt(N?Jx%PF_J#Nm(VuT9=wS435yy)Y8_`)zddH%;;ewV-r&| zBnoYAk*%twm9>p628+WJh&$K3Rb%w;2tqJ|VmLwOou6KjNi@T9ydX-lqH22oBVyT( z>-mW1bTY~;t88z)HI|$N3Ck^SKb_~$Gqnrd1gbz6(`wKsCYl24~VL&uEt?U z9CyOsjyh&qy(!K1xa6#JCR1Bo^)=L3Q}UW?sWnAywbxPSu0!Acc3TV^x88^)JN4RV zmoEJVoJwzfyA{1a>)X)AHnsVAvxKGlJ=zmm{5K)mDDj;@>9jvZ`KT5Nwgd^kj$4s? zo~qe@{zdR7t4dl{KB{(J5A6Tn3uT74a5A=+=BZt?@PP+%J3|7lK_u0+G9s3`KLj8a zc&()*l{J4rv>^Vd7Tal)G&ieQ*%1Vp=t27(JV)? zrTS+q3myl9*E)3i1j#x~LMM}H)-^DctaT3$QI+T!y$5Mgu+Vvjo^GmxX4^YCx&{me zF&e;zX?FO!fSa93R0B_nnV#@TL_-JPRY^x=-C-OU|B^bsAV>d=iX8B`Wm3!ux!Eis zuUbL%VP5oRhkXlGpb4s&~Nw;JPoqiGQhLN>cjW>$`-x$ zi{6*jpBqM2*g6d{?B5+(5B-*l!#BjN>sK>eRJjwK!Zn(r3ueYG9BV$lDPv8eiO~wT ztJI^B@e0nuGOa5XGp?B!e64p(2msQ6lmrMO0HFrq`^?rgkQF%}uFbeQ(+;p{UqLba zR|4HWH!3Oaz5sLx6FTM^13wu?Cv)t$HHvb3{GkC z`uF#jIcX9j6D_yZ-#_v;>kG8R(}PgLJ(PG~unv?KihUsryffQ-n1%y^&C_kDx3V5V zy_LI1p@bbqnSza>09z)pScoh>(Faj%htag_Pmqz`py`IQZkbf24}C-OoTCwJ2zZ<5hVYru1P-ksQJ1Znn*rjBv%Xh)$~`H&6Xkkp`8AOY)i4D`KwLt zajwH!YLi)&`O4RvpX{OjJs~BxA&ej~C3p$D;YTlc;Gx&**o_9o#vYfmiqA?ZX1O;i z^O%Ap!GD! z1&G{$Fv3Gadp0?zbjD*9OPdwQ_Wkhw&cf(GCfsr~&@>-7lH&Vs354W?EuevbkBaHjV6O_C ze!We}+J81_Pge`rc8NtM=-Ya8mrsS2Z_&nfyBH^ zlE|cY9lJ#z-1Hxm<^8(Ye;xrC4J@Je`Tx?p_S{K(tU(nbr(p`zs%d=`bK}m77~14* z0l12w0yM*C%Pf{q@Wbg%-HD(p61fn(P&S)#w7>iBF8W3WRbAy3#AI*VI|kW-Ws-sENMWaa2ixLfaarRbbAr)*c6>}_pi z@K$Z-XiL+AVzF8YDQipv1?2<61Of(!KK9>#fAxNDIK|vEC{5UHe{h(h?Ho6j=DrX8 zg?AbMhIblA&lZ`wkd^p^Ymh2n+g2%UVn|^~5vGGx50!4a_+6Fh@(8cphsfJ=ZLD=V zog-^P*hbjJIXDg9Ss_&|gA!V8xlQD*1zxrJx|CEahq|y{QF++Jg%S`H22K?d^hy10 za(=SBKGUgJqN2Kasf8rSV95N?uTn*f32%djO;daIS(S79OBvK2+pYT?*UKrA^+j-) zDM?1C6p>)X8IMJ%4b-SwOvk$W^-d7RKtwaN2s6X;G?~@Iml>-}sC(oZh7bsXh{z3> z_HAgVuaE5g+J4rHm7r%y5)J}s2NBZU=NrzhQyp>E%mz=bwhn{tpHN>pw$zSm+}d#Z zt4@S>E{2dysrBnCP5=rJUCt^9K_bJjzSG&q%dy@R)^KXTv;X@7&`|YOaKa!z<2y6p z4*{{Bgd5IaP{t2nKn~YW{B=@X3h~!}RDAW}GZBXhdX&_lWx1O4i$~9BeWC}K<^OA#T z*;n)L?^hOmZ>KwAs~*jQ`C825>)2`T?;RqjxW*{Z>MW?eJ9@cFukF=~yv@5y;^ zL}P6RAyOu7Q%5M^ks12%g)Rf|!zFUB$Lm#3>to#f*XihFa{{@(?iXHSmjOW^3LZy++{XPTwoE;r8B%cmZF^C4`f>1yonr9bsK|&7chYGD` zSc%}AlHb@>T1*7brXODQlY$~La95pBf??e^kv&B~;I*~Lk=V2Spl!q7W|+?f7(epW z@r^ezx?K-k7END>m}K4X8iRG6((t6c%~kcF%_*spu(xgjEMeAHHhbSJej8tT?7A)6 z0bH4pz98C0u)iVe7oy@(&azt;UJ}7JUAJxX1L2>BRJiu-jp2{7YR(AiMW5q_8b)RT;nU(76z-khy*o z!%=6j!O;yUvq#Gw7p8a?M1MoJYk!7D*bnkK;(1(jY!O5Bds71-z3(IClJok?JuoGq zN?nFvBG3Kht5(d_^c zAAbHZ@mDL_ES_5IB-?DaPbC{vEuw9dBf|AnDZ@n|K{$bGB25l`QTWa%w%bdQ!MKlF zbrf>Z-XA(%K^`Ju>+q`{>hg*WqVO*-RPtoEJE+kbkkJ`qNyUvPpa>p{s$-*>OjsC( zwU7?_2iH~_KK%ipV@4(fB&dQ93Mt7HshmZ*X${SLNs*$PCc{B#(WXyFv{c+Vq(Xx_ z$SbsT_`whfO&WF9b1+*Prnk<-Z~}N4W)fs@kK8v(=B#vE3o_oY9`k)K(UZ$dEEKw{ z(s4CQMc^{bYa~nfS~fvqo-wUdn1~DFytSy9+6#J}>Lh1H4kENi^0+O>zG)DF z6#o*y7k(@tmOsd9i@YBKxFe(_rDZ&zW(TOMzUa{7wqZ(iFbQFK?KnKt39XPGA*cs0 zc0ZD$hbacA;+lP zTjJqD}6Pu3^E#aXuePW_q4vW@I}dONwR_liqE+^ zvEltMn6TiH;EEuT-@b2g%PsHqj{K)Ur(ouAo?$Sp^o8s*d1W+$4Nm{tWU2j(!BF5l z#6r;46{gRE1LUjx37(ZyNWBIP>Qa=XP{rfA07MJ|Aj&SF*gD`mZv0n5)P;Px^qlbN zLK{($Yg?p}ayVfv!Dt=6Yz>rk8MCAzRxl)@fQKS<-vDh5d{m7IxV&?_;72@@#K#8@ z{FSKKR48f99*SJaIb~1Ve3pUTgbLJED_&e8pTfOgA<$>r*4aF<_tQM~8S3_*m@af5 z(o|HH86^%0k=CtEU7^!>9chEGKe{J;XC?&M;Fa-za5Vm!xrn2$SN!_ z*lSOai1c|hsAH8{&U+Aigie{oQ}LA2ZccLFnI#&f+HrcmpRgF!1;V*Lpiny%UHIbB zSk^~4P%MV6XzvKc$U9yy<_>9wn5<$pgDPg@IA}Dcpw=yd17@ua)_szO+{2D?j<-&3 zzR79&^@Fz!6bLvd?HQP2677GKv4@ zKuq-@=nnM{9&@0rIwkw3k4n|MjCCx5Hsrq??4OY zL_`QeAVLs?A_PE$dey_4NTP};q6q>Jguw_x;RFzY1RlBijT*_#vF{)&n&+!kePBt3 zMWf-e>6ICS*`U%biPanRmRq&HVX-+}?wnnGlT-8>hUzGwfzYm$Iyy>PYP}eIvH8V? zYK1DoQLQ2kHyebQ4L&gh7Se4l11>A{|CEw0{EF9a`yRr*bu0YJ-r;vE^W- zWnmbHc}fO4Dea9QlcW(4Hz}^{7-et-B}Hu&U8GtiZ{9faEOJscM$T)sY3AYG!2D=7 zAVWlw5+89Qb~2mPtnyeS(+HJQuSHj}!1D=f_K~mCQcdy^T~J7?l&cDAM5%Ww^vJpl zvl2e{3c!TlaDz})=w~xwVEl(_z*Dd)MrmbJ$a&DkK4C7APmo2LtInzY zbYWG#BDHskw!uja zR{j1fq;>|*OqRPqC=Yj`9fG#Y2sh(0(j1gWqe89S8u#ooO~1AWnv1rEisvTLyXXhn z4uGJue<(1*D1342HE7xd8(K%kIlNDh!XFqG-((Du?Jg1I9) zIVuP?*5S2A@}z>o$ox;6No=h*rNiiW zKVdMKgd|M1i!U;BX0)4is`6JQZ8jRt{HGI;&FRe4>r{qW0SP1$1`3H^NbUyDk%n9Ru? zHO(#p)4}#iEmjOfu6pCjI|T&Z&Z}_aZoKp!116kvUGB&8CdZOk`GXDc;=mfjea$_9 zzZiJ|k9%zoIxS{iw{d2HmK<`Sm`f#dj6x)1nWc3Wh1KvoCG;XW(6<4KBlt?PR<)@B!^0Q$7Fl@S zAgxFY7W7P%4r#Or^3vCC)a1q`8%1$%fLvoumYRs{^&^NyXE0f#1bP|)Qc17c z(A$&ecZjVy7;s#2W;oo+>o#O!Gtkr?{eeB@>eNOB9 zInw`SU5y~pTB;7Ql6?nZ^u<|vVPfGk~Em0fS?h%>-f`wpUNb|4`6y+{@Shp7}Ru$&7 z)vBmd<3+(e9ScF-buzTa>Ymr z`Ia8uW+`28e4S|R`G`v4GN${s{bpdIYca5@Vnvu!qFwiQdftrNnbCfrkaSAflHz)_ z5MUsMB{?<4KA5Ue2ohI}BQP;?bEJ?=GS4PKs5DWqC81y zX4vJ{Y2@aBJc`W-?J?D;o-P~Bk;GVrC-oGXYeeww=NgyXd6q>T{X)*@?T370Q`>MR z&rtXU)GC->Q4eLqMij0m>)sm8!x-I>! zfB?P9gs!T*vclTJ%JS-5P5u@e0}FF)n1_|Qt-i6fp~c#pC*F8-V)i(&D$ASqacOF6 zY;8{ek-XIG6s>lr-PPRE)YjNK4VY4)B1e}c9gSSVYO}&+GXIX-U7_LBgCsoB#vqzg zTb!rc=Nl+cXpq(j4HN(*fN zRo0jKl?2)5Tc9D=XT6%0x@NKTf@R0$?4m$F$R=HpKl-L(hI+|7>*dt?zkoMOy~)sh z-NnLBS6gLb{u-uZ!W0OyFeT@go5+fx9gacfc0Ra-j)CDa*Li8;hKnzPCch$+zofGy z)eV)=Y#iDwQ+)?1VqrJ^{EILdMI_TIcLTkQUPceY%lnBJzrhEsZTc0>*kyMS^f!zS zIxl;_Nb|Ub-l-8)B_hQU-o(K&STq@3lyQTNHED$gx?0a--7F{if}OREu%oUE7ycT5 zza?*aiQF*Mf`c&MkXlY*fx$*+j$GthIMI0{531@@>#6R*l^1(6qNL=g-|={ui;HH= z?JA-y*E1JAse4+A1r?nrK##cczn1Jg%XyaVEJaz8%sAS8`c^_fR9Hw6*%BBYP9d2n zLQ`C&dqX7}i9+eN%PAgPJlFVn15T*frD%GT<(?W83~G|7H)tz);g>~yvp zBtGLOlHKGw?mYZotTbbk5>Y?_1op@0BWpMe50j!LnO8(LM^|Te4-!3qQ&Kz=8LtNq zA)}-utE#Ll)1&<+cs*K2Dm1OPTdC37X3DwLfizd4xzh z+jgml{ox}3X+eNS2EuAR1VM!z8h!B*8fMfyh(_i+!B_!2_A!|VBc#%9nEruh%RTb!2Mx44Wk zQB_%8W2~^WHn}>{-wt2KUF|(;O=ZY*OPT)RyRxOep{1(FWjcZ@E=}j?wD-OK?Edlf z;pIsm=V4%DKF5$)jCfc>1Oc1fa54FUt%=1PI@QXw)vQSJyiL+p(XPu4qea`B;8hxI z$uQROa{J`kYb=)MFeFZ2K*54>S_GyLfB*tUQ-oC`P@N|HPM#(aoH8Z$KMN>Cyk>!l zeIGeld0B*F7}x+wF-~=(Wr;FCfsrv_n3q{QQ$)JDaczx#ZFe1WNNmP9Jv)}Mv4=2U z!qp%Xh`|Cb4iP=!`Rh^w!!1jzJ$yL>2Y~4fc2ky`7 zzhsd+vm?ZG5SEmRRq{V+?w6pNI`&_4VE*4|&1Zc?`O}PC{SE}Mjrq?}Pvbpwxt&8q z2Z8}ofub)acK{VeUM`SSqqOqY-kcx9bz>Q+*f7WTe^U(Tv1%ArR z@1{zgBc!=LrXA1tI^Un zvG>^p4rLuIC;?A9-tY*My_VdPh$$uD4OIY9jO$~uo|sR@$L8(8s=7-$K^do?+?^q> zH>fdi+Q5OM8MBzr?2clR=JDV&otEd2uwXGG#fZeGV%jXU%Twz$G;KyMg`nyADT!+ zv&|1Rf^?JErM<@u7hEM90FQu?5f?6W7 zqtp8DCLoa^GDP`AL+qR3v*b(Zkz6RH3TlBpAu}({xS469kfy%69^Yj=Wt`J!cp7eU z-|2SV#)l!ty_37Gaaojv_o&_a#~IwgN(2Hj=LJCN+&)%=kJG=T|J8srxzvJu`BEQpF|*ifIy6#W zA4XMu)fAUqh^c~Zm@e2>+3WD>;f>+*<$4_WuzC;F|3=@7)EZaI`L=iv2=4hcV#&!) z@RW?ImFk2{R&JL6D|B{7Z~%GpS(xImA96i|$8tR`J$u$YFH+W4P+C;9Yjwrp2h`TY z+`S6b^Zo#1ZkiVpkq|{^vE*QG+6-x#>)6Gmz1@qavm|4(mA4W#S^xkF8b}yJ@BSxp zXZ0R6cf7o4B=!aofXq_AcPOCj z(Tcr7Kg16VqYECRss!yR*FHlOcVzpYS;euopK`l|CN|7RsJ>n30 zmGUm>NmNpjl9IHQ62>4TVX|9vsdKh^ozOWo{tt1?BQ#t>>q+I7z{M%ST$*v-F&Z-m zEJ)$ItZteGtBPi9t%$`dtW!yqBWrYego?4TK<^r%rmCuBzmQUdzgBrFPjOptAye5w zfGh?74Cz$@zQd1gvLx1_jbUZyZDYP}SGU`3coIINur*G*s}UyeRxh{_e)b%sM@8K; znSoTrxdegE?Qkrrae=S^{);6BWC#rs0n79l<`54pi{3%!XgyGkYg9AxsorP2LRsuZ z;u|hk7bp&mgnKX#7toppVBuvyL-@f^`tBdQivZ`!td7J#Mgn{V@8_|wioxV{3>f=j zClMgF=CTPuIgd|JI7d?^Sp4pdVPwqVeRaU!2o)lj8!DKR-h`Tzm4gdhZ)ETM|QnqLoD4@j@9=;x0q6DiErPf)gN9Nr`Q$P8NX^bZA zy3e&b@`dvz&M)8>t~dAv=Z$O}TLdVJu+b>@8Zm@bbj|kyfPCq_e#iLXv2U5c5%rQ# zcHozON9oQWx9b8w;fnwWO7#d21lRXx^0-VJsnwRSZK&B?cSzPmvlMbyaDaQArcXEa zeY>!_-nx=E<7*H_7MJRN2wZg&2!qa=0gx4IyGIuF7mRqY^Z>_VXW^AdbHLwtF)OVB zDJv$YpDR5D7cXDI5KMIIM1tMx1cSS)#S_9OUnyHg|6bDXK?wfoq{^qQ5|#Rl>e(@*ePtyA2_~h0z(-Sf z%+&4&;<6pQ8wRiG65{B=ROJAywiX}O3pnERo-^9g||+Z8FD}3d=R;HuY9>QzZS)BL;pn< z@9xn4p_KliKHR;uYJ4I{{CFN2I1gh7*ghn9nPfSt@xM5cqvjh>$t0?$kaClt)h$nx zG|d`t=qv+Aa2X`M>jClw`E+F1aNuvUodi&uyDv3PS#R&Q;Pd(L?czt7kaO3{7H8MK ztYG=C82TE|3=2aT><}xJY(y>1`Tx=>+?buPvF%9wKTWYzRPow%z^t+cntv10Ma6u% zm0vVTElxJt!%%<#0?~R!_G84dyhnaH>-d()RhOwwDf!Q# z`joR)*;aY(FAVY-7|oW}vU#wx7V-)CWP$611(1)O32$m{PxB6Xhg46)?!LpM_egz= zE;Is28bL@hLe+xi`ry0&L{u+YQZv?0W@p-gnY&-uo#07$>auJ(a>+yzNkIg-!DbJo zL`v3HrvJxY4x=wwEp#B`SQaos4cF6*iGMZsu9mU!VGJRJru-k5FYN~B*w&dLEg;KO!<#ZPzs@O&$6Biev`O4S1?PkNI5J)%n&#u$d3kP}D zxajfw{PX9H)>A}v)KLCYL<*AE?}tZ7S67G5y4E`5w3(QIbP?is1QsSXMpkZ42IhNW zPDqGB2xDw8#uiy(31f`P_oRQ4@9!oCF<4BilO-EeuSc_NooeI40d#Sq5ogu`3^#@C zBOI?UnvN%vdXHOz{8O0S<&{?(O7lakKnKXnXN$G#{xF}?XlNz>6Rs;L5O_#!h(pTr zhx~~tD#~p|X(VCHkAxknOTjD3E&x~?Y%8b9PBoKDQe0by)5my>M{478WV=3q5d>yl z1?>~Sv|5)d=7A?@HD?akPmZ@43}Evh=FUf*8snGv88TcCo_+@unC6JAQZ-F;WLq~x zr}(_!I8T$Et0Zo?TrL~uiK1v5mYH_Eo-dc|`|}B>YU&rMtYNSjt)|-{$v=P~-7Z(F z_VWKn4QDc*4J4&rwOMbrUE`||9+S)Ia>3n618xZ85XvU3MW7f41_~oMpd?l(5)8vb z@(qp7D6BV_$b{v7#o=^5iIo)-6s_Cy7yR!_s%jvCkjzxT)A4&dJBBvzra}M4`L!N& z=V}He4*76m<20!5orykPhPtU14n+QrWx;P(vWw$pfjP5UE!ymMg*6L2X-&lo`hNV4 z5F{Zj$u<~Q?zgCIg>l-5Au|NNVY4MIWbWzde#XOA;{_iah??=Rr{8P^@aJCuL# zY%c(>8wa^uvYyhK{0;6Uy#sX6(VsoLh9(Ray69Ofo1I;J`9S?@XXmXS@R zJmp5D4C1~dmWd>!kD%^k55cqZJC~5Zdwuxwf`0ISxXgT7biNaP6u|k&O?Wa0p-DVV!qsN#Z#U6?t!*lw(_TJz?EQQh zp7fRYf9-!y*P33;C-2YF(5m*H`)$$n_Zclvf(1<1MF}m~^5XUBnQ574m|HV8GafS% zF^6D`Lz$3$eo?*~w?5_p9S`N2ca&eb(2nVJPhR?uVF9}QQbFcQUsio!KK1hYn!KH( zyV26!92FbuNyHElQfdI{3c zfb!#B0CAYu46y>z*=n_mL$tE)WW9WMen!M@8rXRyTEBUW;hcz0_2#=pp0lD=EQNKN zvwS~`c2DaOjWQ4N8b7U6+BumdHb*vQIN_G(dgodnoYFM~CBnJOv}(z@aJAdyWVBni z+INXycsOo@V_0iK0yTrTSdJKiwH}Ze35Uo~8rUv37(!Gh+_m&a_X!?=XsBR)#mO(W zf^>BaR3XKoYn3D#MI=U~9f!Z$m5B_I3-@RQx;!kl*V(0q*&qNY=;{`IYgbdHXeywN z&VLP51gVJtr$l4)u$QGZi9NR`z8u(?=<BG9pa6a54lZXL-}t?8J_ zD54uWAZIeDH#jgZEE#B4SL6kv+h|^%S1?9NwUEih-z-X#=^-aw$md`{LXv$GDGdDx zo4lfRCK4C{7c z*g|(F0cThySmgT1$F!m!YGbE0wNX@z>#AxT8e0Jpnr#q{WjA_Coa#p_GP5Dn1bu7R zCqpknwA0suYdUhq3EB0d-!e7^SaRpx%16(k;(D`c+)WO|D?i+i$tr2hm6p|2mYlH{ zfh&X~$RfXK7u}Gz-dJv7?C}1qk~s#5X#k)muBG3VvTBnQ?IRJ;d1MV9kE}$>m#PXa zB?<{@F;%IXTn-S@Pg%lYKNY1A0Z7{~DJ(ls#gmOMJ19fWvzdWGgK`80D7r6$7l4In zwdIc#zqEQ!G4uo%j4%C(^|p$Da1}j0YzP$L#C$q?F`IX{&>eOt06HEV1Sii;99vcfc zTVSN<7DQX}pk@F&~1d@aUq`4j;jL226uW^5?edaG20p#yy1H3oNv34c0x4=U5>uYN5egh4W zD~b?DNcjK=H%8&rRkta+1TE)PRP;M)Ja11j)o+hi+`mk(Op>Pka*3}T);+OLohRLr zkg!AabDgMKX+Ji*J1ta-x;qoPd5XBoJ>_e`V}ghN%YScIzn%ve!Uv57mA#P zp&-bLKAj5YDa4!NLfSQq(+hS80%ZLsth{b>7zkBVMoR}zR^HklM4nW%u`yMxZ=0-+ zW}`89A^fy92%*GUAM!*ajRjx$37=OYR8n#Ga+N=a5F?QAV=GDck_Jc2w%as|)g57F zuPobYWVDMrpB8Abd~+ZxMLH(%PbWykl2$x@HYS(A4npUi{OL#ZbVwd}OW=jI+~6fZ zm0_rcG~IrRQV|%nuEfsYHSrLaz2^z@EV1(JK5FgiplE&(!{0kMM79DmHtC+iSIt~V z1f`v=u5Dz@iwL+YBXcXdK{0~8Yd;V@H@>UCu0SxDV9%^jA_;^S7Q>GUw+>~K|NCRD6#OrHTw!Vn9r3*M7q-vhpLTVqO}#Fv*-KIV2q4=pL+ zj`>Zxki;bQksb78d=iYbH6E^ZPMjV1bTJ_d8*HlUn6#rngCSFANK5=yu)5wO@)s(N3H-dWWWVufK$IBoAg3~e=ud?}QsUAWqLZ)3V9zMa7 zf-2UoNr7K9YXH(Tb&cw&UtnEe=|2o;w5TxH&j&w>l#RnNz#kR0ZR88_I~z;k5dt>M zk|n{H%5r*XsXEnX5>{HE2$k^4V85jV9ck;^Rnf_>AIDmP&uTWBp=#EN7N{yy`FE*s zm%Isl(r*tiSDKupv5+qnL7g%_c~_T%-V>tP)lDx& z#XD=d)wYHEmJ-ws#ItcD9&oZ{7?Y$joN!grRNl6dl)Y5Ulyt7FVy0Oc8t?@9^h^Il zqu2F(uK<`!OjbNzZZ1_B{JB+i1&J+yrc{qLG5#R(BAr@)+3yw-=tiUjK>nGQuvgyP zJa#Ypkxex6!u4OZER{D>oGz}h=YokXoNc$=);^}3vnN%dF!h*U(C}DNp`&NIt1CDsX+s3MdX* zK+wb9tX0S`ql@b&4)xeQvtjId`6e0Knnn)=$7JEtP74s4`0H604}dbY{rf`V?g4n( z#39j7TShk`vfB@Dkm#;>7Y_%(E5S&lR6!5>`;2b32%6cp=diHvXlE_*@8_o)f-YS_ zvt^O*drea2K4tlHcz?#QT}f%zF~KL>z;A8LEf8CDnL+(}h{begWfm2g4qZ6nuuwuMeR$G~ibYQ#dvIRxzTq`E!J;uV!^JOsOq z5DqnwxB`V%_YXCo}s1mfRY_7jK+c=c&wzg3b*m0+4pw!Y2aVM~!BE|%A zMe&QGY&MtU!KvXdSaHMkb;7-$D9L!~5rLuIiK(VE zF}zkgIQe$S9fQ|xoJyw?dpJ@;61K3L^!{O;{Zk=OEcgN+S_BH$(}C)`s7CS9f$o#*_4H82)&jH9vlyZ-wm^DZQUkI`KL zmW0Bq`=Tjrbs>DH2;b4`(DiW(!bS?|{tAFHS?9S{@wX5@MCsQ%O>7)438++#Dpje- zuI096RE&YCqXms8v^F8PPH}6qJBEo2?h-XGTg{i7N4iJk+>QwqO=@v%eEfm6k>gvw zI212@3arQmUQ5^6+~OIUQL;7@qM#HF7DjUupkljUw)j*Dg_LppCRjH8ef@7jx^nWd zrXvpphBlUT6TK%gn)?75K&7OR&7(#2juY>Wlru@5b5|VRO=7EQVq9qFTj|g{Sb>6$ zQRxl7j~-Fp3E^ERTXT4(t$rQ&vP&Pxm`H!AYyQ)=(=58JCI2#p+p%IwbG0a>1P3Em z&3Av(nRlpr{gIX@VNi<=8P{~a6NfG_=e6OYuCJa@%_I=}h%J3v4{u^RO){GV^dd zz$`&pZA-R+n3I?Ix~l@5TnwV1E(1fqhykNYNbtK&wH?x}0^ro&BP!#*;r=+Au-mi_ zZa3lPH(zAv0StDOPgk5b2yVxKFkEq?CrZ`F9-c@x`@g+My67u%$V5ceB1*3K5sv+K zlepnTr^;3mH4CyYRDP8zczUbSS6nYlkaeQkoU0F;_1H{GJe&30+nn}s_6dX-J45m~ z$~s~MI(B4=11GgbvVm|Lmvff zJnSsm*4p=A>8#l^EyND7lbnNXIyE@pMx8BL>h+_$Sk-)$TgQNT@o6X|x&R9+c~Z%-8+k|gd%rx0>|q*69?7HF)MAFt^cC(5*IG^ZfhMH`oRwWQ^~C#@>lpAw~w-YpFKV{y-{XCpQ?AN{ux98TD1nzhdHYsg^t z7`5j+!cBbiJ(MjwHXgBciTw@&$Mun~EXc)Am4js*BAiZm8ZW$w zzXpVe$Vds7iZK=AB$IY;{rCWfdYanGpw(pCSn@5de|<0K4^hpy*={wg7JRHJ|JZw| z3Yp|QRlR%(AHv*u1bZ5(^_Q&%&V>V{Jc1))y0SWhktsY1+}AB&Sdw#X=Rt^m83(?( zXvBV@;FlGD&{il>L#{~FhMOD0lRW;e+MK5mtgx%Pn1z9Ob>w4Y>=FeIjeTa4dXAj# zSE>?KYwYW07qTsvnX93%qh&rv;zMCk39X4^Pn(IN)AG-n?{~*e&!A`MJ0pX(>Zkmn zk#qt>-^PE<=RFCE>J!UBYY}uF{J_}3PXKC6XGWu^J)X*B8WsH5syb=#TtQ^I^P7w_ zZQMF(RGQdfvK;r>n@@GSpWrxKx4gZuoPE~3)fD(4?YS?)La8O@*du@nSE)+$p$1Zm zkJ{rKA@yfH;?Migbf~kxXl`Jr%kb{A@9&qIR=bt737bJxaT&Rt(*;sf#7A#bqf4Wa z;IRU%MVIYk0CjrLAS0%{I`=2a^^_a=_SJ$gxBa;1b#!9j{Z9o|;!$8=is znP+P?c9>oF&=sLPJi%qAcFdV6oox7!Vk|KgmvV_=P>Z}5bi<&4i67EVDM)g(3FITU zsgt9%JipD$Gj7(*LbiRLeQYoCp@TPbie6mH$HorZOl+pq{4g^$0!Ikz-{{d}y-CoS zL*vKhV6dY)Y@wnR(#elLVw#DjByOUQXS;99hFN`W8HkJ_9UYueEUnVQq=v?fH&-D4 zLR!r{-w15SsrU7{`Z#CR6NkUkA2#ln1wq4asMwHdoP{*7#k-dgLaDxbpOryXGzHcxgq{5-u6D4>fXE*ono+2@h3nZ($~RIi%ooFs+T;*XZ6ZHpoQ)ux)8OsF_2QYZ1j zFso|fTrTs6%H?@e;6x+RwOVc(9_K0tXqlivHJ0YbOn!e>q8mcre8Dj3ds6XQ4y#z zF`OKXo;DekBqt+CnCMvAFRLfcrG-6=?lo0l^T{B!k^sSmK8khIu2@ctU}m!AD;MR) zu5#HQyR9OUhZ>+h80cUaS`SR$w+D4{iGs-0R(PiP5JgMo(k3aBM~izxn!bzVaM%Si z>d(xr$e>JX=9s#@Pcos;*Sz}8xahT|b1(SfChNX&GsV8Et&RGt#Kh=IFI}PF?pjx8 zYTcwA!-#*R4zb}6tyYkeXfuNk6?eW+jt3aKiQHYAbpHvd<-zH7vC*i8r*%ztvSBOJ zlq4fVY9sq7cX1L7JK;nPcod{`oG&&E--0jKK3#olRk3n8Z$9T2i?eEZocu#=u?=J9 z=$)fVI#LbdUhVM8(vExEBvm}Hz^9v~Lnbm=i6=P`D^#ALZPu}k-&FIR+5EUmg)o%_ zRW_4sC;muS1gF?$*CqmsjORmZ2zne=t4E+KB^wXrs4XClSj^{rQ?=@ z*DMQ4cN7fKfmou;1bT%DjZ;6kVeNQ&!OsLt%xtsZI^FxAGAPcB<;G_zz#tdMpHni6 z`(-k_Kg`bmO_Aw*;jDdH%28~Hzvdslj|?%u;-JH4QO{op>fYaqdf6TWEBpAYR4g~$ zaYH4Cw(e&O_<+V1eB&?j(#ha={`S2S?D4^6{Pdo^xuRkQUx>jIny$TS%=cEQZ1$`;_ljwb|4X-3? zgBNDcp>zhfx2Liix_x}BrjAB7YHBu_ZEQBcUer%h>Fw-+CPFkT^g*=TlF26UX=Seo z2eTvhER9kCb7Hs;obJ}ZUphJd_T!VTjz&syB_FTx2V=BkB!wLflQ$-sI_QBo=|nm@ z{ja|e=6|XVJ*LFTykk^HSryNU6{>QgsHuh;QI`A%6!e4 z>iT?usnq=p1Oe#voeN{o2ac>5$FvjIb?6YItjt3+bWft5mWy|tE|B7;&-0T}S|M{% zzU2Xa078j>7J@UJ@TX@+`t9vCn zjg9^8Tn{-#7gj$l&eHb_cV0vdHvDdcb1+-lpgUZjjt=$0*>q70pQY<8koI8`c}mP# zP#NY?D;N7kF|{1PO&CauBC@WqNq~r%Ja!Zj*}XJ@A)PM!e@_lh_eW@I(n;j;ZUFJ} z<|6BbfT@uqQjg8Vc~nq?vR*v#TDc3}hu!+TS$vJ{0E5CAFS1)7yb_HYvq16Pu%`_oADd+L5njE9iYwCuPj- zywo2?7QeE?X)r;WTbQ%7>?IV~Y3V}e&g~bnCA4c&xaH}#*dL?muUa1T-5j|2Q6whVZfiN%W! zSs4z6p~sGA?3e}8-yFQb8!9uHR+i~2F#^$vG#S7Eh2dw+rpzT#k%DqL&~aze7_Rc@ zKN)fCK&PT}=-AAD%L0iTI?|=+>pyZD)%ht1y?a}N>^wTC{$LeRZ8?LMKLR&Gj&6B{ zitfOj-NfS{wA^tU((GdUgkkVhWD$2!StbFx4{qbB^t5z5&vOtDesb_ipqXNfc)9&z za-z@%-0?TfK8<+UC;e3Ek#D1A;=rAYHQ;?&l=*Tl)%D+^c(oX+*|TX6q5{g!39R~){eoQnH{ z@ReaT-ifHsI?IH;Zui9-oL1cp*8|Up(xKWV>q)l_zig!Cc%CE$V5Jo_VP7p2)>db6prg_LhWGhLWnWmr(MTaN`yE3eO?J z@3)X2DG*nj=#Im?V?9!XiKYZ7djC97;x|i190)cd5@Voy8d|Z>NI-;_JbLr}>EP&- z?3jH0$FS&&2Rz1p1G28;M{l^z6ZIttj^sFDY;Xy{V6^v_ti3}00Thg%21(j=fN>m? zWsYdRzr>6QobCsTW2m!?${0p_?FXyxd_$|>8Ut{DYfNr3TIoOI!XMmoihvC(hEf|=kfw|HMKZxPiX|Nd9M!P%=+;@3# zESQALi*W=hNqFCjkn|?}?haHFkn4}LB)WSt)zS^aV()Kq1Hxz-dGCK=dXqQM(IdY~ zQh*3~Q5vA(ATXlPyti&H*BMQ)reX|Stf3KO;9v}sd(#)+`0K>Fwd=p5tV6z2nGb@m zcMAbl_~qA$15pswP0zBs&g3n^8bqo5TjtNJ?;ZI%k5Fl4$-f1E`USpp+HdQB2l(7( zjW`0_X=$!tY;KJe=x5F&BXrfu9M8m_v>%>(ENpG1Y@DC8Unl^> zz~A|6R&m~3oefvn)nb0dBYzk>jvU&$LHwucKdt-Pb2%jD0hJNsd-wwcXHW)U2nlbe`zs11$y;c560-U2g z(zBrbXr>nKT8eTFMHz`uXK=7zn=KGHPF^y>cVj2p{V5qe|0emkb26FTo+`Vy-l=H< zne! zv>ME+O!yt7QFBrT|p>+tyR3$W;EHX69-C;>7ZGWghPYND0w zy`hdbj1=l3IOtYNP^0MkWAwAYk95z_;^YH|l%lF+_jU4OrsEImYK`!iF7eYV5-Sr`QNrG(jLR3~e&&;w=9W1qTwV30%fM$Z(d89yMl`Ti>G|@g zVC#kzzpZx?cj-XGt_l%H0EIT&UBxJAMnED z=u6Q_!ZUxH1I!@vTBftgPxGt)-j}u7SSi15QquU74x-|q*QJ}wb|zJJ0aYG&ws>8| z4;0|bN`3)THX>;|k|ia#Px91iV0FIn2|sc@a!svH^T|0Iy{mc|egpnl%2CB3ere zfs#p1!_F6+jA-ylP05!=B1aoKA~F^mlQVmf=-u9w2sAncB#W2cScKD+F?bVyg!?NrX zpA_ef_r~aX@OgQtL&^d=>-rZ}ajkRzHaai=YC0g~dZp{G@|@YULf}QqU=Lc@h5UQ~ z%>5U+{Kg{)jIAIx2tmm@myjbl(%P>g-$WLTQ)v4d@g>WPFv*d3u@Yg7M1)X`6b`}} z13{a~cLu<{?SnUmdNO0K`}h}p%vkmfW~-(rL(KER_i^@2G^Ky|kqO5h%_DiDi78kj zC%LA=s9t_Xs!21k*B;Q*3;`a4cW5~XhzCmWXDIV(-)F(T(!DQ>bJ-Az$&vsD5KD3? z5`30U8h2I_GMd`5=Cdns=ql0DJoD=j>^QGk%mN@=U3Znl>gE-P0cgG>oyA#giFNwS z%>bv4Z&7dwI3(8VH)YZ$YV=A`95Ci3yyQSg%G9B1&lU)px?Ieh+;pfC(p8uy(@K~= zLx$pGb5gz|K)x%M5wg6Jttu9Aqd8&qR<2kK2qbz9@$azscgQ*lVCgEuLy0RLORX0G z*@#hQ*akvVH3N)C$5nEZCkKoZ3JsUYW6xNz-jYc|^_WECKfsvQ5C5bh*e6yaB`IYG zPZ!=k)QZ{*LL6PF_{=mrEK_MHj)60k;fV~SAo&Z1A&O)Q{1r;Al~*^*_er&d8~=Rp zeHTO{hnDIRAM&Wl94py>t>Gq<< zC0mwk0|sEhukYA6OW6L=A3C{r7r;d+Nv|Mq!FR8=PsfT|O)gN8m&v(3I9O<^TIj#R zGjHB|u=kbfL+uqwFrs z<4d(6yMvq@v2D3KDo6c`W9*Ix*#lurhFpwRfx6;(_zqmp(M#pobMff3fxq#hgz7Y_ zN3~(7HV(}Ga#SBLyHeHf#US`?zU7|tZ*lNRXzey?sarb$-dEL(q8U$cxva*Nq*6G-NGgWNp7*Am}}7pY_nHkCvi#g5f%~4yAe|2$_{u1w^bn z7-82A$mzX~RuHj{M3pouaF3V8x7GL7o|K8^^Ftk1fyhhZ_p= zJCOdI70+^kM!n@a&p+-EtvV1;#=bn~BE;KjRV8SoYP&ohmsld*~39}qn~We>ZkaLE&)Wf$-jk^F&?5D*5R!nur^`owfHGKo>_h}ara z-rP%og=kSM&0iN(!E0t0bYTBB6f0hUXX~WA5EoN}E!*4`QP9%ZAZO82akPrY4~99B z%SfKA3J$yv|NW@)p%bf2n=EjC$)vYUmf2GRXL9UGrc^dk-Z6&YkD9l67N}&=STyOM z;%ja_;9q~oS=pnN{yIE-YH${Kvh%}l9Ai8}#haA79+NKHW|ye=_@FbWP)5Zl|9O&(;HMxilRGU>Dq5pA*#!xAwm z1;K-ao?NGe3av-rR4d6{@aPJry8wQ>)us{G)ldlYp@W_+6gTcnxUgQKq z05+m4nH0O^u<9QcGDv=mMKsJeA`FhPxPC<}E{`!NYseI+F#GHw^9TKk^$MI%CS^aA zfOEoQOG>82-&3&r!|zp*Uo5_58YM{1FGEPiAk^2q@FU}c#5nkJkNf#Y@ItiOO(4V~ zyY;ao1j#&7GNmUTgxM=lU+VL>_Km1j@XECph7-#W{CS7uh7Lf71Vv+H9hk6`8)YXV z%BHEQeoLw}N_;hfC*fIGgNdTQ{<~pDTW9CLq~jC>5M7WAOqgG+XIA-}s-znK+o2P? zGA$Bt2{{H4s!NzWSB^c3vh?I#gc66_wtxkU34+D(o9Jyy5Gey+j1LJUr4cLuHM7p? zUq6>HV#>oW@!TVKx^TQp;>6>jY+`wNFg)YPRIZ$lQ?U>nlC>%`+jliK zOD<>mHC#0xCHnY$7a$}2BX3f{Iq~)oPO@p1k6!zb13q6ju{<))!#4ws6%u^NvpVZsJI%XZLzSP6yV>^A0Je#S?sj(Q&-O>ov0&YWQ?0`-~td zKGQ(n?wKVLae_)tIs8Z&3iS6J9?C*d1EQ|s!Cm(X!^%Gwt8qCsvtBF>mDmjfL&mmC z{fF?>K|e5Gbkf$_HwA8f+P5aB#R{u0T{(F0xOdzGYW|;LTTzsf-Zm4PgE0LiL@zDQ z@sqg!hPmI3egO6QWljdqT`kdIz&+2{pJQ2nCY}`j8Scxv%)QJHk9)+r63)2t=*C<2 zjZ*=v)U6rA`Q3jP{4t!7FurGP6YFep0E;~xvnakw&;OEB7DHRJyd(JxV2YcncePaR zYn*xXj&13x2&DTHMEmG69xqNc zd?#qZ>XiZ798D|*C)3&YZ6Sogtzl&I!{bjyaU^LHFzG=oOb_Nm-NtStdap=B2e8~m zpT`~i#)SuknlYjCAADoOea0UCDY?CiyCK$gi~E^bcp$NL4tIHs4S1Hz%(6z?|ZwH5@sve-RU- zr(FNq0C&WH5p?T(@wK2E^~B5tf|ZnQGVH1DARPARgKrPHkCPH;m18$f6kMi{8h6jdnT=#sZQqA+FU_&T-AO^ zw%q$P)Pr@^P5>rO(^*<$hDUYsyZQCwN^k8zK&tn)_fc?45vu8E7z+FAhMCXEL-1tG zv+X~v$Z*_uNlx+kC4}m3j$K;5e^)!EH@uCvoWX@YE%ZT7v(SxE+WN}7qUBP}{#)lP zoBNiuAv8a^wDko?PhC=b>&$jB(Y5lv`N2UMfkNH$EsNe%xA;v}pQf{k^eyc8qoH~D z$_IKrRETB?Uys|;@rU6z4IfGawzDi8d#e3fR0BPdG2vBADQ63t*Q#)QXPXYn3km8H zQ2rgHpu(MFOa;jhM%Na}l^~9Qx(P}$#F!lGux8#%uOI{e3IG(ia~#2CNQ9*m-DG5% z1PTI*Pk98iiCT$>wxIzdgf`0)Qda9o1uvkMY6~4hOJ*oP(QaitEop&r-1ZXC&W>_= zn7Ir~K$oxuv-}DxP&KdS> zOGDTW+BXYpkLihCi8V(zRMS1RvC7p1WJwpss;h;aVJQH>u{Erfw6!YlB`GuwUDeH3 z@%kWx(wMqpi7`r81iyI8QU`kNk%xC=tF{69-k-Y&LrqsVaqDQuAJCsei-pxjRpo)_ zDSp&*>iX>5JyqSQ>x;WZuWIQrLC@`(wxSlbH36;!0jIG0J@`h$H?Sn@8#fINi=yJn z4vHh9K<>RyG}9dEeqt;OXmzSKPpv}hyGAuy!KZeC%e%UDdm*Q^3D0)mWwV{tF$tc~ zd&VwI$Q{f9?O1L(Z}IZE<(l)4>(MpJ_T~r7=lalNwFdIU3chH*4?C z;6ZuWh)I#zbmZc82s1ww;I_I?UM)q`LmQ)7r7P?1ERu~Cp|zo9_fU>ULUX`UNM0(d zDs_LMMsa9RIGdS~AF?7euB^I(nd!~?ADB>UbxP7IH6ck%t{S4EyOL~BRhKEW)S6~7 zB9}@gm8$88ghF#_^zV$`D3Y6OcnJlWNljx?9*>_$SwA;YOm2<|RP`!lUV}v<2-S^g zr5j-M=V7c=3hJ80OT0uhOD0ywn0Pv8YLOBjJgroyocXa8i!2|C#DaLne=?ew1T9aV z?jy%Uf|hts+27ThijK?H8fLSq)~MDjN@Ql}MM^Zbt|g0@31yPUZ2Tp2Yet3Yu}Y>yhoDuP0zzAJ@GnKO!+&X>|F!vuF&+Os0!*|C{l^ISf{FGI5iHDf zH4Ywu7vfa(z5o6Kda-^_bQu_!SlBqYc=!Z_M8qVdWaJc-RMa%IbTa8>$(F+)SDt(Y z3bO;>JGx$f%u25;)LU-MHXAL~G47>bXFtvMI2SuTyMLA2?#t(|>+RzIva?>1v7hPu zyFd2~_Vo{Jymx(QxPj&O0k?!>AH3cERat3yWkuCm)|%>CTYcS}hBx0dH8#(mchEYm zEo~NfxASs5BO0b6$xhW~R2rSZWU)D1 z9$z4weV{}t(ibRH#MqHc%!*lh;ib)${2u;oovQv z&+HFjz80-bnzG*kXYBK%v)Y}q$PVWmH|=O`7HHR@Q1qz zt%j}f(I$gNZP91B6%I9MsEh3@4I43P%=l6LYqp3UMLWB{%=`0XVOAJszX&^-_tq9K!Fc z3?r245%x3jy^wo}_w;;|dAU~#ilM|1+oBxfkaE^LMwo&ULt;@+njk!rR1v(KUoN_Xpn zl)mB%BXGpONDHpW>fhE-3$&yDJV`S|hxK_qQ&-QDh7`#w2wG+GC$&|U@+yTpCAaAa zPvCLeCq4ny<#$KZH5yf)7#~luj1`}&F6y&W|yAB%Cs@d{-5eb8V-9dV*^+#}` sRORuQ^MwZkbOTJZjJ`3%USk&r1w}L-^ExBW&|Jwhj0gyVGIWwdA?WB zZTl59BXZjSpxN}gj(J8RbUP?Y59n+6z(9QjF*WWejn66cJ-0rZ@$0te!Elpi%_QIpArixG3*GG|D~LpjO@SD1LQQ zH$2r;)Ee+bp?=>%vN?xo+CqO^yr(=wsXA4fldG$pODFnlrWU$TlOu4jKFs8e`k3L$YRguNv}Sjyj;D35g% z_VEvK)c)J^0$N%+tz*3Lzyix4jx!7};KoZKz0xl80^%VahHYcoMTR}M+i7j`dh%Sy zi)FL=fuapk^IQQ06hMFqa}Lu1D|JB0#&?X@@}^UA8kV14@{k2G1+_;G)g-54d^s(5 zCHu>+G@+T`#seGfQ1bfDMc?oLotd!GtqHSeO2r95L8Z}eh4B9Ycm7W``#ToML8O9i zdw?(g3@H|nG{c%Oh=vy8hBg@gnH-ui$A2aI-f_DO{F~N5kHpx-#68tk&E|QMMKMG zC2R!{2PCbJWeTeB2{>#2%{qTYuwzmT1VO<<%U?k6^}lNU_yz>ny8id3rtN+P5fnWh z!ihR(N<)RrqSH|+^6Xs3)M4Mp$KvnDg5Yuw;$koGNChq_o=NcThCu;800e>4&Elw( z0(lN0hSt4n6{XwGQfqfEn#x7*ac7?Y|7HI_+mkoy1^FcIZTZ|U;Ye2Wt=liGUlbwN z=+Qqo4ozxu{gCb@D_j&3g~T=ak}r=aLlhF3B*T-1L>R~M_fD(3Yb)GS`+G)X(;5_I zW$zhA5Mc(vr{UM)m4}4SL=Y7#YYDB;GFJ1w+xhl7Iqm(@D?RQlgy4zP1Y?8{Mrgu# z$KS4{+AOFe(ij;x>_Za93g!r5NGnelrA=d+XGT~2w`*k>LBNfIYz0Z$|9wFq1^^6E zZ_^?WF+ivN`sMHd==(Rr0h$B&J_E#jde<4S{QM~n&_DzP%-tsx1I&{zV8}fBkS
Kc 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 * { + max-width: var(--wrapper-max-width); +} + +.layout > nav { + flex: 0 1 content; + order: var(--site-nav-order); + padding-right: var(--spacer-m); +} + +.layout > main { + flex: 1 1 var(--main-base-width); +} diff --git a/frontend/styles/syntax-highlighting.css b/frontend/styles/syntax-highlighting.css new file mode 100644 index 0000000..085f7e9 --- /dev/null +++ b/frontend/styles/syntax-highlighting.css @@ -0,0 +1,79 @@ +/* + Syntax Highlighting for Code Snippets + https://www.bridgetownrb.com/docs/liquid/tags#stylesheets-for-syntax-highlighting + + Based on Twilight: + https://gist.github.com/dansimpson/803005 +*/ + +pre.highlight { + color: white; + background: hsl(0, 0%, 8%); + font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; + font-size: 1.3em; + text-align: left; + text-shadow: 0 -.1em .2em black; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + tab-size: 4; + hyphens: none; + + border-radius: var(--base-border-radius); + margin: var(--base-block-margin); + overflow: auto; + padding: 1em; +} + +pre.highlight .hll { background-color: #ffffcc } +pre.highlight .c { color: #5F5A60; font-style: italic } /* Comment */ +pre.highlight .err { border:#B22518; } /* Error */ +pre.highlight .k { color: #CDA869 } /* Keyword */ +pre.highlight .cm { color: #5F5A60; font-style: italic } /* Comment.Multiline */ +pre.highlight .cp { color: #5F5A60 } /* Comment.Preproc */ +pre.highlight .c1 { color: #5F5A60; font-style: italic } /* Comment.Single */ +pre.highlight .cs { color: #5F5A60; font-style: italic } /* Comment.Special */ +pre.highlight .gd { background: #420E09 } /* Generic.Deleted */ +pre.highlight .ge { font-style: italic } /* Generic.Emph */ +pre.highlight .gr { background: #B22518 } /* Generic.Error */ +pre.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +pre.highlight .gi { background: #253B22 } /* Generic.Inserted */ +pre.highlight .gp { font-weight: bold } /* Generic.Prompt */ +pre.highlight .gs { font-weight: bold } /* Generic.Strong */ +pre.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +pre.highlight .kd { color: #e9df8f; } /* Keyword.Declaration */ +pre.highlight .kp { color: #9B703F } /* Keyword.Pseudo */ +pre.highlight .na { color: #F9EE98 } /* Name.Attribute */ +pre.highlight .nb { color: #CDA869 } /* Name.Builtin */ +pre.highlight .nc { color: #9B859D; font-weight: bold } /* Name.Class */ +pre.highlight .no { color: #9B859D } /* Name.Constant */ +pre.highlight .nd { color: #7587A6 } /* Name.Decorator */ +pre.highlight .ni { color: #CF6A4C; font-weight: bold } /* Name.Entity */ +pre.highlight .nf { color: #9B703F; font-weight: bold } /* Name.Function */ +pre.highlight .nn { color: #9B859D; font-weight: bold } /* Name.Namespace */ +pre.highlight .nt { color: #CDA869; font-weight: bold } /* Name.Tag */ +pre.highlight .nv { color: #7587A6 } /* Name.Variable */ +pre.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +pre.highlight .w { color: #141414 } /* Text.Whitespace */ +pre.highlight .mf { color: #CF6A4C } /* Literal.Number.Float */ +pre.highlight .mh { color: #CF6A4C } /* Literal.Number.Hex */ +pre.highlight .mi { color: #CF6A4C } /* Literal.Number.Integer */ +pre.highlight .mo { color: #CF6A4C } /* Literal.Number.Oct */ +pre.highlight .sb { color: #8F9D6A } /* Literal.String.Backtick */ +pre.highlight .sc { color: #8F9D6A } /* Literal.String.Char */ +pre.highlight .sd { color: #8F9D6A; font-style: italic; } /* Literal.String.Doc */ +pre.highlight .s2 { color: #8F9D6A } /* Literal.String.Double */ +pre.highlight .se { color: #F9EE98; font-weight: bold; } /* Literal.String.Escape */ +pre.highlight .sh { color: #8F9D6A } /* Literal.String.Heredoc */ +pre.highlight .si { color: #DAEFA3; font-weight: bold; } /* Literal.String.Interpol */ +pre.highlight .sx { color: #8F9D6A } /* Literal.String.Other */ +pre.highlight .sr { color: #E9C062 } /* Literal.String.Regex */ +pre.highlight .s1 { color: #8F9D6A } /* Literal.String.Single */ +pre.highlight .ss { color: #CF6A4C } /* Literal.String.Symbol */ +pre.highlight .bp { color: #00aaaa } /* Name.Builtin.Pseudo */ +pre.highlight .vc { color: #7587A6 } /* Name.Variable.Class */ +pre.highlight .vg { color: #7587A6 } /* Name.Variable.Global */ +pre.highlight .vi { color: #7587A6 } /* Name.Variable.Instance */ +pre.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..b95c7c1 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "$styles/*": ["./frontend/styles/*"], + "$javascript/*": ["./frontend/javascript/*"], + "$components/*": ["./src/_components/*"] + } + }, +} diff --git a/justfile b/justfile new file mode 100644 index 0000000..eaa9bd1 --- /dev/null +++ b/justfile @@ -0,0 +1,12 @@ +start: + bundle exec bridgetown start + +deploy: build + rsync -uvcr --delete output/ $TARGET + +build: clean + bundle exec bridgetown frontend:build + bundle exec bridgetown build + +clean: + bundle exec bridgetown clean diff --git a/lib/app.js b/lib/app.js deleted file mode 100644 index effc93c..0000000 --- a/lib/app.js +++ /dev/null @@ -1,16 +0,0 @@ -(function() { -// code highlighting, adding Prism-specific class -// (necessary because Markdown only supports this for code blocks) -let nodes = document.querySelectorAll("p > code"); -Array.prototype.forEach.call(nodes, function(node) { - if(!node.hasAttributes()) { - node.className = "language-generic"; - } -}); - -// animated logo -let video = document.querySelector("video"); -if(video) { - video.addEventListener("click", video.play.bind(video)); -} -}()); diff --git a/lib/components/bar/_index.scss b/lib/components/bar/_index.scss deleted file mode 100644 index 56762d1..0000000 --- a/lib/components/bar/_index.scss +++ /dev/null @@ -1,13 +0,0 @@ -.bar { - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--spacer-m); - padding: var(--spacer-m) var(--spacer-xl); - background-color: var(--main-color); -} - -.bar > :first-child, -.bar > :last-child { - flex-grow: 1; -} diff --git a/lib/components/bar/index.jsx b/lib/components/bar/index.jsx deleted file mode 100644 index aa8c723..0000000 --- a/lib/components/bar/index.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import { assetURI } from "../../../views/util"; -import { shortName } from "../../../content/defaults"; -import { Iconlist, Iconlink } from "../iconlist"; -import { createElement } from "complate-stream"; - -let logo = assetURI("faucet-logotype-monochrome.svg"); - -export function Bar({ TagName }) { - return - - - - - privacy - imprint - - - - - - - - - ; -} diff --git a/lib/components/code/_index.scss b/lib/components/code/_index.scss deleted file mode 100644 index a2d91ea..0000000 --- a/lib/components/code/_index.scss +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Based on the prism.js Twilight theme - * which is based (more or less) on the Twilight theme originally of Textmate fame. - * @author Remy Bach - */ -code[class*="language-"], -pre[class*="language-"] { - color: white; - background: none; - font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; - font-size: 1em; - text-align: left; - text-shadow: 0 -.1em .2em black; - white-space: pre; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - line-height: 1.5; - tab-size: 4; - hyphens: none; -} - -pre[class*="language-"], -:not(pre) > code[class*="language-"] { - background: hsl(0, 0%, 8%); -} - -/* Code blocks */ -// removed border & box-shadow -pre[class*="language-"] { - border-radius: var(--base-border-radius); // adjusted for consistency - margin: var(--base-block-margin); // adjusted for consistency - overflow: auto; - padding: 1em; -} - -pre[class*="language-"]::-moz-selection { - /* Firefox */ - background: hsl(200, 4%, 16%); -} - -pre[class*="language-"]::selection { - /* Safari */ - background: hsl(200, 4%, 16%); -} - -/* Text Selection colour */ -pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, -code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { - text-shadow: none; - background: hsla(0, 0%, 93%, 0.15); -} - -pre[class*="language-"]::selection, pre[class*="language-"] ::selection, -code[class*="language-"]::selection, code[class*="language-"] ::selection { - text-shadow: none; - background: hsla(0, 0%, 93%, 0.15); -} - -/* Inline code */ -:not(pre) > code[class*="language-"] { - border-radius: .3em; - border: .13em solid hsl(0, 0%, 33%); - box-shadow: 1px 1px .3em -.1em black inset; - padding: .15em .2em .05em; - white-space: normal; -} - -.token.comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: hsl(0deg 0% 65%); // adjusted for better contrast -} - -.token.punctuation { - opacity: .7; -} - -.token.namespace { - opacity: .7; -} - -.token.tag, -.token.boolean, -.token.number, -.token.deleted { - color: hsl(14, 58%, 55%); -} - -.token.keyword, -.token.property, -.token.selector, -.token.constant, -.token.symbol, -.token.builtin { - color: hsl(53, 89%, 79%); -} - -.token.attr-name, -.token.attr-value, -.token.string, -.token.char, -.token.operator, -.token.entity, -.token.url, -.language-css .token.string, -.style .token.string, -.token.variable, -.token.inserted { - color: hsl(76, 21%, 52%); -} - -.token.atrule { - color: hsl(218, 22%, 55%); -} - -.token.regex, -.token.important { - color: hsl(42, 75%, 65%); -} - -.token.important, -.token.bold { - font-weight: bold; -} -.token.italic { - font-style: italic; -} - -.token.entity { - cursor: help; -} - -pre[data-line] { - padding: 1em 0 1em 3em; - position: relative; -} - -/* Markup */ -.language-markup .token.tag, -.language-markup .token.attr-name, -.language-markup .token.punctuation { - color: hsl(33, 33%, 52%); -} - -/* Make the tokens sit above the line highlight so the colours don't look faded. */ -.token { - position: relative; - z-index: 1; -} diff --git a/lib/components/iconlist/_index.scss b/lib/components/iconlist/_index.scss deleted file mode 100644 index 56f5e72..0000000 --- a/lib/components/iconlist/_index.scss +++ /dev/null @@ -1,8 +0,0 @@ -.iconlist { - display: flex; - justify-content: flex-end; - - a { - margin-left: var(--spacer-xl); - } -} diff --git a/lib/components/iconlist/index.jsx b/lib/components/iconlist/index.jsx deleted file mode 100644 index 57b2243..0000000 --- a/lib/components/iconlist/index.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import { assetURI } from "../../../views/util"; -import { createElement } from "complate-stream"; - -export function Iconlist(props, ...icons) { - return ; -} - -export function Iconlink({ href, src, alt, width, height }) { - return - {alt} - ; -} diff --git a/lib/components/layout/_index.scss b/lib/components/layout/_index.scss deleted file mode 100644 index 0c93d4f..0000000 --- a/lib/components/layout/_index.scss +++ /dev/null @@ -1,25 +0,0 @@ -header { - margin-bottom: var(--spacer-xl); -} - -.layout { - display: flex; - flex-wrap: wrap; - justify-content: center; - max-width: var(--wrapper-max-width); - margin: 0 var(--wrapper-margin) var(--spacer-xl); - - & > * { - max-width: var(--wrapper-max-width); - } - - & > nav { - flex: 0 1 content; - order: var(--site-nav-order); - padding-right: var(--spacer-m); - } - - & > main { - flex: 1 1 var(--main-base-width); - } -} diff --git a/lib/components/layout/index.jsx b/lib/components/layout/index.jsx deleted file mode 100644 index 08ac40f..0000000 --- a/lib/components/layout/index.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import { Teaser } from "../teaser"; -import { Navigation } from "../navigation"; -import { Bar } from "../bar"; -import { SkipLink } from "../skip"; -import { assetURI, repr } from "../../../views/util"; -import { createElement } from "complate-stream"; - -let layouts = new Set(["baroque", "tiny"]); - -let stylesheets = [ - assetURI("bundle.css") -]; -let scripts = [ - assetURI("prism.js"), - assetURI("app.js") -]; - -export default function DefaultLayout({ docTitle, claim, layout, slug }, ...children) { - if(!docTitle) { - throw new Error("missing document title"); - } - if(!layout) { - layout = "tiny"; - } - if(!layouts.has(layout)) { - throw new Error(`invalid page layout: ${repr(layout)}`); - } - - return - - - {docTitle} - - - - - - - - - {renderStyleSheets(stylesheets)} - - {renderScripts(scripts)} - - - - - - { layout === "tiny" ? : } - -
-
- {children} -
- - -
- - { layout === "baroque" ? : null } - - ; -} - -function renderScripts(items) { - return items.map(uri => { - return + + + <%= render Skip.new(href: "#main") %> + <% if resource_title == "About" %> + <%= render Teaser.new(tagline: site.metadata.tagline) %> + <% else %> + <%= render Bar.new() %> + <% end %> + +
+
<%= yield %>
+ <%= render Navigation.new(resource: resource) %> +
+ + <% if resource_title == "About" %> + <%= render Bar.new(tag: "footer") %> + <% end %> + + diff --git a/content/build-integration.md b/src/build-integration.md similarity index 98% rename from content/build-integration.md rename to src/build-integration.md index c0e2080..8f7647a 100644 --- a/content/build-integration.md +++ b/src/build-integration.md @@ -1,4 +1,7 @@ +--- +layout: default title: Build an Integration +--- This guide will help you if you want to use faucet-pipeline with your favorite Web framework or programming language and there is no integration written yet. diff --git a/content/build-pipeline.md b/src/build-pipeline.md similarity index 99% rename from content/build-pipeline.md rename to src/build-pipeline.md index 8c1eca0..e766a2b 100644 --- a/content/build-pipeline.md +++ b/src/build-pipeline.md @@ -1,4 +1,7 @@ +--- +layout: default title: Build a Pipeline +--- If you want to support other JS dialects or CSS preprocessors, you can contribute an additional pipeline to the project. We recommend that you read the diff --git a/content/cli.md b/src/cli.md similarity index 97% rename from content/cli.md rename to src/cli.md index a89e827..35d08b0 100644 --- a/content/cli.md +++ b/src/cli.md @@ -1,4 +1,7 @@ +--- +layout: default title: CLI +--- If you call the `faucet` command without any arguments, it will default to building your project according to the configuration in the file diff --git a/content/contributing.md b/src/contributing.md similarity index 98% rename from content/contributing.md rename to src/contributing.md index 42141e4..9baf5ea 100644 --- a/content/contributing.md +++ b/src/contributing.md @@ -1,4 +1,7 @@ +--- +layout: default title: Contributing in General +--- **Please note that this project that all contributions to this project must adhere to the [Contributor diff --git a/content/css.md b/src/css.md similarity index 99% rename from content/css.md rename to src/css.md index cb4d6ed..7ff8388 100644 --- a/content/css.md +++ b/src/css.md @@ -1,4 +1,7 @@ +--- +layout: default title: faucet-pipeline-css +--- faucet-pipeline-css offers bundling for files written in CSS. diff --git a/content/faq.md b/src/faq.md similarity index 97% rename from content/faq.md rename to src/faq.md index 50772bb..7a3f3fb 100644 --- a/content/faq.md +++ b/src/faq.md @@ -1,4 +1,7 @@ +--- +layout: default title: Troubleshooting / FAQ +--- Here we want to address some common questions. diff --git a/content/images.md b/src/images.md similarity index 99% rename from content/images.md rename to src/images.md index 6f8c822..62bca9e 100644 --- a/content/images.md +++ b/src/images.md @@ -1,4 +1,7 @@ +--- +layout: default title: faucet-pipeline-images +--- faucet-pipeline-images offers image optimization and transformation. It supports JPG, PNG, SVG, WebP and AVIF. diff --git a/lib/images/faucet-logotype-monochrome.svg b/src/images/faucet-logotype-monochrome.svg similarity index 100% rename from lib/images/faucet-logotype-monochrome.svg rename to src/images/faucet-logotype-monochrome.svg diff --git a/lib/images/favicon.svg b/src/images/favicon.svg similarity index 100% rename from lib/images/favicon.svg rename to src/images/favicon.svg diff --git a/lib/images/github.svg b/src/images/github.svg similarity index 100% rename from lib/images/github.svg rename to src/images/github.svg diff --git a/lib/images/logo.png b/src/images/logo.png similarity index 100% rename from lib/images/logo.png rename to src/images/logo.png diff --git a/lib/images/logo.webm b/src/images/logo.webm similarity index 100% rename from lib/images/logo.webm rename to src/images/logo.webm diff --git a/lib/images/npm.svg b/src/images/npm.svg similarity index 100% rename from lib/images/npm.svg rename to src/images/npm.svg diff --git a/src/impressum.md b/src/impressum.md new file mode 100644 index 0000000..187de32 --- /dev/null +++ b/src/impressum.md @@ -0,0 +1,109 @@ +--- +layout: default +title: "Impressum" +--- + +~~~ +Lucas Dohmen +c/o COCENTER +Koppoldstr. 1 +86551 Aichach +~~~ + +## Kontakt + +E-Mail: lucas@dohmen.io + +## Haftungsausschluss + +### Haftung für Inhalte + +Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. +Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte +können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind +wir gemäß § 7 Abs.1 DDG für eigene Inhalte auf diesen Seiten nach +den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 DDG sind +wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte +oder gespeicherte fremde Informationen zu überwachen oder nach +Umständen zu forschen, die auf eine rechtswidrige Tätigkeit +hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung +von Informationen nach den allgemeinen Gesetzen bleiben hiervon +unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem +Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. +Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir +diese Inhalte umgehend entfernen. + +### Haftung für Links + +Unser Angebot enthält Links zu externen Webseiten Dritter, auf +deren Inhalte wir keinen Einfluss haben. Deshalb können wir für +diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte +der verlinkten Seiten ist stets der jeweilige Anbieter oder +Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden +zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. +Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht +erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten +Seiten ist jedoch ohne konkrete Anhaltspunkte einer +Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von +Rechtsverletzungen werden wir derartige Links umgehend entfernen. + +### Urheberrecht + +Die durch die Seitenbetreiber erstellten Inhalte und Werke auf +diesen Seiten unterliegen dem deutschen Urheberrecht. Die +Vervielfältigung, Bearbeitung, Verbreitung und jede Art der +Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der +schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. +Downloads und Kopien dieser Seite sind nur für den privaten, nicht +kommerziellen Gebrauch gestattet. Soweit die Inhalte auf dieser +Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte +Dritter beachtet. Insbesondere werden Inhalte Dritter als solche +gekennzeichnet. Sollten Sie trotzdem auf eine +Urheberrechtsverletzung aufmerksam werden, bitten wir um einen +entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen +werden wir derartige Inhalte umgehend entfernen. + +### Datenschutzhinweise + +**TL;DR: Wir nehmen Datensparsamkeit ernst und verarbeiten so wenig potenziell personenbezogene +Daten wie möglich. Der Webserver steht in Deutschland. Keine Cookies, kein sonstiges Tracking. +IP-Adressen werden gekürzt geloggt und nach 7 Tagen gelöscht. E-Mails werden je nach Zweck +unterschiedlich lange aufbewahrt.** + +Der Inhalt dieser Website ist allen Besuchern ohne jegliche Registrierung zugänglich. Damit du dir +die Seite ansehen kannst, wird deine IP-Adresse vom **Webserver** für die Dauer des Besuchs +verarbeitet. Dieser Webserver sowie der Mailserver werden von [Uberspace](https://uberspace.de) +bereitgestellt und befinden sich in Deutschland. Hierfür wurde ein Auftragsverarbeitungsvertrag nach +Art. 28 DSGVO geschlossen. + +Zusätzlich werden gekürzte IP-Adresse geloggt um **Aktivitäten** auf den einzelnen Seiten +nachvollziehen und **Fehler** ausfindig machen zu können (Access-Log und Error-Log). Um die +Privatsphäre der Besucher zu schützen, protokollieren wir nur die ersten 16 Bits einer IPv4-Adresse +bzw. die ersten 32 Bits einer IPv6-Adresse und löschen den Rest. Diese Logs werden nach 7 Tagen +gelöscht. + +Es werden natürlich auch keine Browser-Cookies oder andere Trackingtechnologien genutzt. + +**Rechtsgrundlage** für die Verarbeitung dieser Daten ist Art. 6 Abs. 1 lit. f) DSGVO. Das +berechtigte Interesse ist hier die Gewährleistung der Integrität, Vertraulichkeit und Verfügbarkeit +der Daten, die über diese Internetseiten verarbeitet werden. + +Wenn du uns eine E-Mail sendest, so erfolgt dies, soweit möglich, stets auf freiwilliger Basis. +Dabei werden diese Daten von uns gespeichert, allerdings ohne deine ausdrückliche Zustimmung nicht +an Dritte weitergegeben; insbesondere nicht für deren Werbezwecke. Mindestens jährlich wird durch +uns geprüft, ob eine weitere Speicherung erforderlich ist oder Aufbewahrungspflichten für die +E-Mails bestehen. Abhängig davon werden E-Mails weiter gespeichert oder gelöscht. + +Wir weisen darauf hin, dass die Datenübertragung im Internet (z. B. bei der Kommunikation per +E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser Schutz der Daten vor dem Zugriff durch +Dritte ist nicht möglich. Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten +Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich angeforderter Werbung und +Informationsmaterialien wird hiermit ausdrücklich widersprochen. Die Betreiber dieser Website +behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von +Werbeinformationen, etwa durch Spam-Mails, vor. + +Falls du dein **Recht auf Auskunft, Berichtigung, Widerspruch der Verarbeitung oder Löschung von +personenbezogenen Daten** wahrnehmen willst, kontaktiere mich bitte. Auch das Beschwerderecht bei +einer Aufsichtsbehörde steht dir zu. + +Website Impressum basiert auf [impressum-generator.de](https://www.impressum-generator.de) von der [Kanzlei Hasselbach](https://www.kanzlei-hasselbach.de). diff --git a/content/index.md b/src/index.md similarity index 98% rename from content/index.md rename to src/index.md index 0394f2d..12f7ae3 100644 --- a/content/index.md +++ b/src/index.md @@ -1,4 +1,7 @@ -layout: baroque +--- +layout: default +title: About +--- **faucet-pipeline** takes care of **[pre-processing CSS](css)**, **[compiling modern JavaScript](js)** and framework-independent diff --git a/content/js.md b/src/js.md similarity index 99% rename from content/js.md rename to src/js.md index 589fbe5..a9d835d 100644 --- a/content/js.md +++ b/src/js.md @@ -1,4 +1,7 @@ +--- +layout: default title: faucet-pipeline-js +--- The configuration is an array of bundles you want to create. Each entry of the array is an object with at least two keys: `source` is the file that should diff --git a/content/manifest.md b/src/manifest.md similarity index 99% rename from content/manifest.md rename to src/manifest.md index 4d78b85..df50a9a 100644 --- a/content/manifest.md +++ b/src/manifest.md @@ -1,5 +1,7 @@ +--- +layout: default title: Fingerprinting & Manifest - +--- `faucet-pipeline` can fingerprint the generated files for you: It adds a hash of the file content to the file name. You can cache files with a fingerprint diff --git a/content/philosophy.md b/src/philosophy.md similarity index 99% rename from content/philosophy.md rename to src/philosophy.md index 85230a1..c6b20c8 100644 --- a/content/philosophy.md +++ b/src/philosophy.md @@ -1,4 +1,7 @@ +--- +layout: default title: Motivation & Philosophy +--- > Given the choice between making something _my_ problem, and making something > _the user's_ problem, I'll choose to make it my problem every time. diff --git a/content/rails.md b/src/rails.md similarity index 88% rename from content/rails.md rename to src/rails.md index 553529a..a4feb15 100644 --- a/content/rails.md +++ b/src/rails.md @@ -1,4 +1,7 @@ +--- +layout: default title: Rails +--- If you want to use faucet-pipeline with Rails, you can use the `faucet_pipeline_rails` gem. It is [documented diff --git a/content/sass.md b/src/sass.md similarity index 98% rename from content/sass.md rename to src/sass.md index 6d4c93c..9878a24 100644 --- a/content/sass.md +++ b/src/sass.md @@ -1,4 +1,7 @@ +--- +layout: default title: faucet-pipeline-sass +--- faucet-pipeline-sass offers bundling for files written in SCSS. diff --git a/content/spring-boot.md b/src/spring-boot.md similarity index 88% rename from content/spring-boot.md rename to src/spring-boot.md index 7208900..5a52046 100644 --- a/content/spring-boot.md +++ b/src/spring-boot.md @@ -1,4 +1,7 @@ +--- +layout: default title: Spring Boot +--- If you want to use faucet-pipeline with Spring Boot, you can use [this Spring Boot diff --git a/content/static.md b/src/static.md similarity index 98% rename from content/static.md rename to src/static.md index ae0924d..9d98302 100644 --- a/content/static.md +++ b/src/static.md @@ -1,4 +1,7 @@ +--- +layout: default title: faucet-pipeline-static +--- The configuration is an array of folders or single files you want to copy. Each entry of the array is an object with two keys: `source` is the source folder, diff --git a/content/watching.md b/src/watching.md similarity index 96% rename from content/watching.md rename to src/watching.md index 22f0e65..6e50929 100644 --- a/content/watching.md +++ b/src/watching.md @@ -1,4 +1,7 @@ +--- +layout: default title: File Watching +--- You don’t need to configure anything for file watching; you enable it with the `--watch` CLI option. However, if you want to be gentle with your file watching diff --git a/views/index.jsx b/views/index.jsx deleted file mode 100644 index a7e9307..0000000 --- a/views/index.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import DefaultLayout from "../lib/components/layout"; -import { fullName, claims } from "../content/defaults"; -import Renderer, { createElement, safe } from "complate-stream"; - -let { registerView, renderView } = new Renderer(); - -registerView(render); - -export default renderView; - -function render({ slug, meta, html }) { - let { title, layout } = meta; - - let docTitle = title ? `${title} | ${fullName}` : claims.default; - let claim = docTitle === claims.default ? claims.alt : claims.default; - - return - {safe(html)} - ; -} diff --git a/views/util.js b/views/util.js deleted file mode 100644 index c285b19..0000000 --- a/views/util.js +++ /dev/null @@ -1,13 +0,0 @@ -let manifest = require("./manifest.json"); // NB: relative to bundle - -export function assetURI(filepath) { - let res = manifest[filepath]; - if(!res) { - throw new Error(`unknown asset: ${repr(filepath)}`); - } - return res; -} - -export function repr(value) { - return `\`${JSON.stringify(value)}\``; -}