try with manual writing

This commit is contained in:
konnorrogers
2023-10-20 15:42:55 -04:00
parent 26d83f37d6
commit 9e43e65b6c
2119 changed files with 44635 additions and 2 deletions

View File

@@ -5,6 +5,36 @@
data-shoelace-version="{{ meta.version }}"
>
<head>
<!--
JSPM Generator Import Map
Edit URL: https://generator.jspm.io/#jcs9DoMwDIbhDF16kW4kpFO7cYkeIASTGBmDEvNz+zaduhSJwdInvY9vF6Wub/ViQSHoVMQQqZzoITfW6qeuDWFr/JTgMJLjsLgA2ficT6jBrS77hLOcwPtIilCau37o+rdMhwm84FpIgr68/28LZ9dDFWWkQ7NF4G/coK38NM4TA0vVQY8MRdfa2g8tjhfiKAE
-->
<script type="importmap">
{
"imports": {
"highlight.js/lib/core": "https://ga.jspm.io/npm:highlight.js@11.9.0/es/core.js",
"highlight.js/lib/languages/css": "https://ga.jspm.io/npm:highlight.js@11.9.0/es/languages/css.js",
"highlight.js/lib/languages/javascript": "https://ga.jspm.io/npm:highlight.js@11.9.0/es/languages/javascript.js",
"highlight.js/lib/languages/xml": "https://ga.jspm.io/npm:highlight.js@11.9.0/es/languages/xml.js",
"lit": "https://ga.jspm.io/npm:lit@2.8.0/index.js",
"lit/directives/ref.js": "https://ga.jspm.io/npm:lit@2.8.0/directives/ref.js",
"lit/directives/unsafe-html.js": "https://ga.jspm.io/npm:lit@2.8.0/directives/unsafe-html.js",
"lit/directives/when.js": "https://ga.jspm.io/npm:lit@2.8.0/directives/when.js",
"web-component-define": "https://ga.jspm.io/npm:web-component-define@2.0.11/src/index.js"
},
"scopes": {
"https://ga.jspm.io/": {
"@lit/reactive-element": "https://ga.jspm.io/npm:@lit/reactive-element@1.6.3/reactive-element.js",
"@open-wc/dedupe-mixin": "https://ga.jspm.io/npm:@open-wc/dedupe-mixin@1.4.0/index.js",
"lit-element/lit-element.js": "https://ga.jspm.io/npm:lit-element@3.3.3/lit-element.js",
"lit-html": "https://ga.jspm.io/npm:lit-html@2.8.0/lit-html.js",
"lit-html/": "https://ga.jspm.io/npm:lit-html@2.8.0/"
}
}
}
</script>
<script async src="https://ga.jspm.io/npm:es-module-shims@1.8.0/dist/es-module-shims.js" crossorigin="anonymous"></script>
{# Metadata #}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

View File

@@ -1,5 +1,4 @@
<script type="module">
import "https://cdn.jsdelivr.net/npm/light-pen@1.1.2/+esm"
<script type="module" src="/assets/scripts/light-pen/exports/index.js">
</script>
<light-pen style="height: 100%; padding: 8px;" resize-position="30" sandbox-settings="

View File

@@ -0,0 +1,11 @@
## 1.1.0
- A number of fixes including allowing `<template>` elements to be slotted into `<light-pen>` and `<light-preview>`
## 1.0.2
- fix: issues with `&gt;` and `&lt;`
## 1.0.1
- fix: allow any HTML Element for templating

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Konnor Rogers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,96 @@
# Light(weight) Code(pen)
## Demo
<https://konnorrogers.github.io/light-pen>
## Purpose
A small lightweight editor using `<pre><code></code></pre>` and a `<textarea>`
Inspired by Chris Ferdinandi
## Install
```
npm install light-pen
```
## Getting started
```js
// Auto-register <light-pen>
import "light-pen"
// Registry yourself
import LightPen "light-pen/exports/light-pen.js"
LightPen.define() // Registers `<light-pen>`
// Register under another name
import LightPen "light-pen/exports/light-pen.js"
LightPen.define("other-name")
```
## Usage
```html
<light-pen></light-pen>
```
That's it!
### Slotting in HTML / CSS / JS
```html
<light-pen>
<script type="text/plain" slot="html">
<div style="color: red;">
<div>Hi there friends</div>
</div>
<p>
Sup dude
</p>
</script>
<script type="text/plain" slot="css">
p {
color: green;
}
</script>
<script type="text/plain" slot="js">
console.log("Hi")
</script>
</light-pen>
```
### Changing the title
```html
<light-pen>
<div slot="title">My Awesome Editor</div>
</light-pen>
```
### Opening languages by default
`<light-pen>` Takes a string of comma separated languages to open on initial render.
```
<light-pen open-languages="html,css,js">
</light-pen>
```
More to come for more docs coming on how to change things!
## Roadmap
- [ ] - Implement an extendable `LightPenBase` which includes a pluggable syntax highlighter and theme.
- [ ] - Implement `<textarea>` rendering hooks to add CodeMirror for more robuste setups.
- [ ] - Add vertical resizing of `<textarea>`
- [ ] - More documentation around customization.
- [ ] - Add a console logger
>>>>>>> 7b592c8f5f05ed82d439d7950450eec06dc3ab66

View File

@@ -0,0 +1,44 @@
// @ts-check
// import { expandTypesPlugin } from './expand-types.js'
const globs = ['exports/**/*.{d.ts,js}', 'internal/**/*.{d.ts,js}', 'types/**/*.d.ts']
export default {
/** Globs to analyze */
globs,
/** Globs to exclude */
exclude: ['node_modules', 'docs'],
/** Directory to output CEM to */
outdir: '.',
/** Run in dev mode, provides extra logging */
dev: process.argv.includes("--verbose"),
/** Run in watch mode, runs on file changes */
watch: process.argv.includes("--watch"),
/** Include third party custom elements manifests */
dependencies: true,
/** Output CEM path to `package.json`, defaults to true */
packagejson: true,
/** Enable special handling for litelement */
litelement: true,
/** Enable special handling for catalyst */
catalyst: false,
/** Enable special handling for fast */
fast: false,
/** Enable special handling for stencil */
stencil: false,
// overrideModuleCreation: ({ts, globs}) => {
// const program = ts.createProgram(globs, {target: ts.ScriptTarget.ESNext, module: ts.ModuleKind.ESNext, allowJs: true, checkJs: true});
//
// // If we dont do this, everything blows up.
// program.getTypeChecker()
//
// return program.getSourceFiles().filter(sf => globs.find(glob => {
// return sf.fileName.includes(glob)
// }))
// },
// /** Provide custom plugins */
// plugins: [
// /** You can now pass the typeChecker to your plugins */
// expandTypesPlugin({ globs })
// ],
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
# Bridgetown
output
.bridgetown-cache
.bridgetown-metadata
.bridgetown-webpack
# Dependency folders
node_modules
bower_components
vendor
# Caches
.sass-cache
.npm
.node_repl_history
# Ignore bundler config.
/.bundle
# Ignore Byebug command history file.
.byebug_history
# dotenv environment variables file
.env
# Mac files
.DS_Store
# Yarn
yarn-error.log
yarn-debug.log*
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity
src/bridgetown
src/meta.json

View File

@@ -0,0 +1 @@
ruby-3.0.4

View File

@@ -0,0 +1,49 @@
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
####
# Welcome to your project's Gemfile, used by Rubygems & Bundler.
#
# To install a plugin, run:
#
# bundle add new-plugin-name -g bridgetown_plugins
#
# This will ensure the plugin is added to the correct Bundler group.
#
# When you run Bridgetown commands, we recommend using a binstub like so:
#
# bin/bridgetown start (or console, etc.)
#
# This will help ensure the proper Bridgetown version is running.
####
# If you need to upgrade/switch Bridgetown versions, change the line below
# and then run `bundle update bridgetown`
gem "bridgetown", "~> 1.3"
# Uncomment to add file-based dynamic routing to your project:
# gem "bridgetown-routes", "~> 1.3"
# Uncomment to use the Inspectors API to manipulate the output
# of your HTML or XML resources:
gem "nokogiri", "~> 1.13"
# Puma is a Rack-compatible server used by Bridgetown
# (you can optionally limit this to the "development" group)
gem "puma", "~> 5.6"
gem "bridgetown-quick-search", "~> 2.0"
# gem "asset_mapper", "~> 1.0"
gem "asset_mapper", "~> 1.0"
# gem "rack-cors", "~> 2.0"
gem "rack-cors", "~> 2.0"
gem "custom_elements_manifest_parser", "~> 0.2.4"
gem "ruby-lsp", "~> 0.11.1", :group => :development, require: false
gem "yard", "~> 0.9.34", :group => :development
gem "solargraph", "~> 0.49.0", :group => :development
gem "debug", "~> 1.8", :group => :development

View File

@@ -0,0 +1,241 @@
GEM
remote: https://rubygems.org/
specs:
activemodel (7.0.8)
activesupport (= 7.0.8)
activesupport (7.0.8)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
amazing_print (1.5.0)
asset_mapper (1.0.1)
dry-configurable (~> 1.0)
dry-files (~> 1.0)
dry-initializer (~> 3.0)
dry-types (~> 1.5)
rake (>= 10)
zeitwerk (~> 2.5)
ast (2.4.2)
backport (1.2.0)
base64 (0.1.1)
benchmark (0.2.1)
bridgetown (1.3.1)
bridgetown-builder (= 1.3.1)
bridgetown-core (= 1.3.1)
bridgetown-paginate (= 1.3.1)
bridgetown-builder (1.3.1)
bridgetown-core (= 1.3.1)
bridgetown-core (1.3.1)
activemodel (>= 6.0, < 8.0)
activesupport (>= 6.0, < 8.0)
addressable (~> 2.4)
amazing_print (~> 1.2)
colorator (~> 1.0)
erubi (~> 1.9)
faraday (~> 2.0)
faraday-follow_redirects (~> 0.3)
hash_with_dot_access (~> 1.2)
i18n (~> 1.0)
kramdown (~> 2.1)
kramdown-parser-gfm (~> 1.0)
liquid (~> 5.0)
listen (~> 3.0)
rake (>= 13.0)
roda (~> 3.46)
rouge (~> 3.0)
serbea (~> 1.0)
thor (~> 1.1)
tilt (~> 2.0)
zeitwerk (~> 2.5)
bridgetown-paginate (1.3.1)
bridgetown-core (= 1.3.1)
bridgetown-quick-search (2.0.0)
bridgetown (>= 1.2.0.beta2, < 2.0)
colorator (1.1.0)
concurrent-ruby (1.2.2)
custom_elements_manifest_parser (0.2.4)
dry-struct (~> 1.0)
dry-types (~> 1.0)
dry-validation (~> 1.0)
debug (1.8.0)
irb (>= 1.5.0)
reline (>= 0.3.1)
diff-lcs (1.5.0)
dry-configurable (1.1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-core (1.0.1)
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
dry-files (1.0.1)
dry-inflector (1.0.0)
dry-initializer (3.1.1)
dry-logic (1.5.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-schema (1.13.3)
concurrent-ruby (~> 1.0)
dry-configurable (~> 1.0, >= 1.0.1)
dry-core (~> 1.0, < 2)
dry-initializer (~> 3.0)
dry-logic (>= 1.4, < 2)
dry-types (>= 1.7, < 2)
zeitwerk (~> 2.6)
dry-struct (1.6.0)
dry-core (~> 1.0, < 2)
dry-types (>= 1.7, < 2)
ice_nine (~> 0.11)
zeitwerk (~> 2.6)
dry-types (1.7.1)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0)
dry-inflector (~> 1.0)
dry-logic (~> 1.4)
zeitwerk (~> 2.6)
dry-validation (1.10.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
dry-initializer (~> 3.0)
dry-schema (>= 1.12, < 2)
zeitwerk (~> 2.6)
e2mmap (0.1.0)
erubi (1.12.0)
faraday (2.7.11)
base64
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-follow_redirects (0.3.0)
faraday (>= 1, < 3)
faraday-net_http (3.0.2)
ffi (1.15.5)
hash_with_dot_access (1.2.0)
activesupport (>= 5.0.0, < 8.0)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
io-console (0.6.0)
irb (1.8.1)
rdoc
reline (>= 0.3.8)
jaro_winkler (1.5.6)
json (2.6.3)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
language_server-protocol (3.17.0.3)
liquid (5.4.0)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
minitest (5.20.0)
nio4r (2.5.8)
nokogiri (1.13.9-arm64-darwin)
racc (~> 1.4)
nokogiri (1.13.9-x86_64-linux)
racc (~> 1.4)
parallel (1.23.0)
parser (3.2.2.4)
ast (~> 2.4.1)
racc
psych (5.1.0)
stringio
public_suffix (5.0.3)
puma (5.6.5)
nio4r (~> 2.0)
racc (1.6.0)
rack (3.0.8)
rack-cors (2.0.1)
rack (>= 2.0.0)
rainbow (3.1.1)
rake (13.0.6)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rbs (2.8.4)
rdoc (6.5.0)
psych (>= 4.0.0)
regexp_parser (2.8.1)
reline (0.3.9)
io-console (~> 0.5)
reverse_markdown (2.1.1)
nokogiri
rexml (3.2.6)
roda (3.72.0)
rack
rouge (3.30.0)
rubocop (1.56.4)
base64 (~> 0.1.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.3)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
parser (>= 3.2.1.0)
ruby-lsp (0.11.1)
language_server-protocol (~> 3.17.0)
sorbet-runtime (>= 0.5.5685)
yarp (>= 0.12, < 0.13)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
serbea (1.0.1)
activesupport (>= 6.0)
erubi (>= 1.10)
tilt (~> 2.0)
solargraph (0.49.0)
backport (~> 1.2)
benchmark
bundler (~> 2.0)
diff-lcs (~> 1.4)
e2mmap
jaro_winkler (~> 1.5)
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.1)
parser (~> 3.0)
rbs (~> 2.0)
reverse_markdown (~> 2.0)
rubocop (~> 1.38)
thor (~> 1.0)
tilt (~> 2.0)
yard (~> 0.9, >= 0.9.24)
sorbet-runtime (0.5.11064)
stringio (3.0.8)
thor (1.2.2)
tilt (2.3.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
yard (0.9.34)
yarp (0.12.0)
zeitwerk (2.6.12)
PLATFORMS
arm64-darwin-21
arm64-darwin-22
x86_64-linux
DEPENDENCIES
asset_mapper (~> 1.0)
bridgetown (~> 1.3)
bridgetown-quick-search (~> 2.0)
custom_elements_manifest_parser (~> 0.2.4)
debug (~> 1.8)
nokogiri (~> 1.13)
puma (~> 5.6)
rack-cors (~> 2.0)
ruby-lsp (~> 0.11.1)
solargraph (~> 0.49.0)
yard (~> 0.9.34)
BUNDLED WITH
2.3.5

View File

@@ -0,0 +1,2 @@
bridgetown: bin/bridgetown start --skip-frontend
esbuild: pnpm run esbuild-dev

View File

@@ -0,0 +1,70 @@
# Bridgetown Website README
Welcome to your new Bridgetown website! You can update this README file to provide additional context and setup information for yourself or other contributors.
## Table of Contents
- [Prerequisites](#prerequisites)
- [Install](#install)
- [Development](#development)
- [Commands](#commands)
- [Deployment](#deployment)
- [Contributing](#contributing)
## Prerequisites
- [GCC](https://gcc.gnu.org/install/)
- [Make](https://www.gnu.org/software/make/)
- [Ruby](https://www.ruby-lang.org/en/downloads/)
- `>= 2.7`
- [Bridgetown Gem](https://rubygems.org/gems/bridgetown)
- `gem install bridgetown -N`
- [Node](https://nodejs.org)
- `>= 12`
- [Yarn](https://yarnpkg.com)
## Install
```sh
cd bridgetown-site-folder
bundle install && yarn install
```
> Learn more: [Bridgetown Getting Started Documentation](https://www.bridgetownrb.com/docs/).
## Development
To start your site in development mode, run `bin/bridgetown start` and navigate to [localhost:4000](https://localhost:4000/)!
Use a [theme](https://github.com/topics/bridgetown-theme) or add some [plugins](https://www.bridgetownrb.com/plugins/) to get started quickly.
### Commands
```sh
# running locally
bin/bridgetown start
# build & deploy to production
bin/bridgetown deploy
# load the site up within a Ruby console (IRB)
bin/bridgetown console
```
> Learn more: [Bridgetown CLI Documentation](https://www.bridgetownrb.com/docs/command-line-usage)
## Deployment
You can deploy Bridgetown sites on hosts like Render or Vercel as well as traditional web servers by simply building and copying the output folder to your HTML root.
> Read the [Bridgetown Deployment Documentation](https://www.bridgetownrb.com/docs/deployment) for more information.
## Contributing
If repo is on GitHub:
1. Fork it
2. Clone the fork using `git clone` to your local development machine.
3. Create your feature branch (`git checkout -b my-new-feature`)
4. Commit your changes (`git commit -am 'Add some feature'`)
5. Push to the branch (`git push origin my-new-feature`)
6. Create a new Pull Request

View File

@@ -0,0 +1,49 @@
require "bridgetown"
Bridgetown.load_tasks
# Run rake without specifying any command to execute a deploy build by default.
task default: :deploy
#
# Standard set of tasks, which you can customize if you wish:
#
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 "yarn run esbuild"
end
desc "Watch the frontend with esbuild during development"
task :dev do
sh "yarn run esbuild-dev"
rescue Interrupt
end
end
#
# Add your own Rake tasks here! You can use `environment` as a prerequisite
# in order to write automations or other commands requiring a loaded site.
#
# task :my_task => :environment do
# puts site.root_dir
# automation do
# say_status :rake, "I'm a Rake tast =) #{site.config.url}"
# end
# end

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'bridgetown' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("bridgetown-core", "bridgetown")

View File

@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'bridgetown' is installed as part of a gem, and
# this file is here to facilitate running it.
#
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
bundle_binstub = File.expand_path("bundle", __dir__)
if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end
require "rubygems"
require "bundler/setup"
load Gem.bin_path("bridgetown-core", "bridgetown")

View File

@@ -0,0 +1,48 @@
baseurl: "" # OPTIONAL: the subpath of your site, e.g. /blog
url: "" # the base hostname & protocol for your site, e.g. https://example.com
destination: output
development:
url: "localhost:4000"
# production:
# url: "https://konnorrogers.github.io"
# base_path: "/light-pen"
template_engine: erb
permalink: pretty
collections:
documentation:
output: true
defaults:
- scope:
path: "images"
values:
image: true
- scope:
path: "_documentation"
values:
layout: doc
permalink: /:categories/:slug
category_order: 0
doc_order: 0
- scope:
path: "_documentation/guides"
values:
category: guides
category_order: 10
- scope:
path: "_documentation/components"
values:
category: components
category_order: 20
- scope:
path: "_documentation/references"
values:
category: references
category_order: 30

View File

@@ -0,0 +1,16 @@
# This file is used by Rack-based servers during the Bridgetown boot process.
require "bridgetown-core/rack/boot"
Bridgetown::Rack.boot
require "rack/cors"
use Rack::Cors do
allow do
origins '*'
resource '/*', headers: :any, methods: :get
end
end
run RodaApp.freeze.app # see server/roda_app.rb

View File

@@ -0,0 +1,310 @@
// 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 v1.1.0
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 = () => ({
name: "import-glob",
setup: (build) => {
build.onResolve({ filter: /\*/ }, async (args) => {
if (args.resolveDir === "") {
return; // Ignore unresolvable paths
}
const adjustedPath = args.path.replace(/^bridgetownComponents\//, "../../src/_components/")
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,
}).sort()
const importerCode = `
${files
.map((module, index) => `import * as module${index} from '${module}'`)
.join(';')}
const modules = {${files
.map((module, index) => `
"${module.replace("../../src/_components/", "")}": module${index},`)
.join("")}
};
export default modules;
`
return { contents: importerCode, resolveDir: args.pluginData.resolveDir }
})
},
})
// Plugin for PostCSS
const postCssPlugin = (options, configuration) => ({
name: "postcss",
async setup(build) {
// Process .css files with PostCSS
build.onLoad({ filter: (configuration.filter || /\.css$/) }, async (args) => {
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
const result = await postcss([importPlugin, ...options.plugins]).process(css, {
map: true,
...options.options,
from: args.path,
});
return {
contents: result.css,
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 `yarn add sass` 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 = (outputFolder) => ({
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
}
await fs.writeFile(path.join(outputFolder, 'meta.json'), JSON.stringify(result.metafile), { encoding: "utf8" })
const manifest = {}
const entrypoints = []
// We don't need `frontend/` cluttering up everything
// const stripPrefix = (str) => str.replace(/^frontend\//, "")
// 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]
}
// 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 pathShortener = new RegExp(`^${outputFolder}\\/_bridgetown\\/static\\/`, "g")
// 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(/\.(s?css|sass)$/))) {
// // Special treatment for index.css
// manifest[stripPrefix(inputs.find(item => item.match(/\.(s?css|sass)$/)))] = 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}`)
})
})
}
})
// Load the PostCSS config from postcss.config.js or whatever else is a supported location/format
const postcssrc = require("postcss-load-config")
const postCssConfig = postcssrc.sync()
module.exports = async (outputFolder, esbuildOptions) => {
esbuildOptions.plugins = esbuildOptions.plugins || []
// Add the PostCSS & glob plugins to the top of the plugin stack
esbuildOptions.plugins.unshift(postCssPlugin(postCssConfig, esbuildOptions.postCssPluginConfig || {}))
if (esbuildOptions.postCssPluginConfig) delete esbuildOptions.postCssPluginConfig
esbuildOptions.plugins.unshift(importGlobPlugin())
// Add the Sass plugin
esbuildOptions.plugins.push(sassPlugin(esbuildOptions.sassOptions || {}))
// Add the Bridgetown preset
esbuildOptions.plugins.push(bridgetownPreset(outputFolder))
// esbuild, take it away!
const esbuild = require("esbuild")
const watch = process.argv.includes("--watch")
const config = {
bundle: true,
loader: {
".jpg": "file",
".png": "file",
".gif": "file",
".svg": "file",
".woff": "file",
".woff2": "file",
".ttf": "file",
".eot": "file",
},
resolveExtensions: [".tsx", ".ts", ".jsx", ".js", ".css", ".scss", ".sass", ".json", ".js.rb"],
nodePaths: ["frontend/javascript", "frontend/styles"],
minify: process.argv.includes("--minify"),
sourcemap: true,
target: "es2016",
entryPoints: ["frontend/javascript/index.js"],
entryNames: "[dir]/[name].[hash]",
outdir: path.join(process.cwd(), `${outputFolder}/bridgetown/static`),
publicPath: "/bridgetown/static",
metafile: true,
...esbuildOptions,
}
if (watch) {
const context = await esbuild.context(config).catch(() => process.exit(1))
await context.watch().catch(() => process.exit(1))
} else {
await esbuild.build(config).catch(() => process.exit(1))
}
}

View File

@@ -0,0 +1,20 @@
Bridgetown.configure do |config|
config.url = ENV.fetch("URL", "https://konnorrogers.github.io")
config.base_path = ENV.fetch("BASE_PATH", "/light-pen")
config.base_url = config.url + config.base_path
init :"bridgetown-quick-search"
# init :ssr
# init :"bridgetown-routes"
# only :server do
# roda do |app|
# app.plugin :default_headers,
# 'Content-Type'=>'text/html',
# 'Strict-Transport-Security'=>'max-age=16070400;',
# 'X-Content-Type-Options'=>'nosniff',
# 'X-Frame-Options'=>'deny',
# 'X-XSS-Protection'=>'1; mode=block',
# 'Access-Control-Allow-Origin'=>'*'
# end
# end
end

View File

@@ -0,0 +1,27 @@
# Puma is a fast, concurrent web server for Ruby & Rack
#
# Learn more at: https://puma.io
#
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
# 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

View File

@@ -0,0 +1,57 @@
// @ts-check
const { spawn } = require("child_process");
// const glob = require("glob")
const build = require("./config/esbuild.defaults.js")
const AssetMapper = require("asset-mapper-esbuild").default
// Update this if you need to configure a destination folder other than `output`
const outputFolder = "src"
// 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 = {
// }
// ```
//
// You can also support custom base_path deployments via changing `publicPath`.
//
// ```
// const esbuildOptions = { publicPath: "/my_subfolder/_bridgetown/static" }
// ```
const watch = process.argv.includes("--watch")
const esbuildOptions = {
target: "es2020",
entryPoints: {
"javascript/index": "frontend/javascript/index.js",
"javascript/defer": "frontend/javascript/defer.js",
"light-pen/exports/light-pen": "../exports/light-pen.js",
"light-pen/exports/light-preview": "../exports/light-preview.js",
},
define: {
"process.env.BASE_PATH": `"${process.env.BASE_PATH}"`
},
publicPath: path.join(process.env.BASE_PATH, "bridgetown", "static"),
outdir: path.join(process.cwd(), outputFolder, "bridgetown", "static"),
splitting: true,
format: "esm",
plugins: [
esbuildCopy({
assets: {
from: [path.resolve(__dirname, 'node_modules/@shoelace-style/shoelace/dist/assets/icons/**/*.svg')],
to: [path.resolve(__dirname, 'src/shoelace-assets/assets/icons')],
},
verbose: false
}),
AssetMapper({
manifestFile: path.join(process.cwd(), ".bridgetown-cache", "asset-mapper-manifest.json"),
// outputRoot: path.join(process.cwd(), process.env.BASE_PATH)
}),
]
}
build(outputFolder, esbuildOptions)

View File

@@ -0,0 +1,21 @@
// @ts-check
import { Controller } from "@hotwired/stimulus"
export default class ClipboardController extends Controller {
connect () {
this.element.addEventListener("clipboard-copy", this.showSuccess)
}
showSuccess = () => {
this.element.classList.add("clipboard--success")
this.element.classList.remove("clipboard--idle")
if (this.timeout) {
clearTimeout(this.timeout)
}
this.timeout = setTimeout(() => {
this.element.classList.remove("clipboard--success")
this.element.classList.add("clipboard--idle")
}, 2_000)
}
}

View File

@@ -0,0 +1,6 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
connect() {
// console.log("Hello, Stimulus!", this.element)
}
}

View File

@@ -0,0 +1,72 @@
import { Controller } from "@hotwired/stimulus"
export default class ScrollSpyController extends Controller {
connect () {
this.observer = new IntersectionObserver(this.handleIntersect, { rootMargin: '0px 0px' });
this.linkMap = new WeakMap();
this.visibleSet = new WeakSet();
this.observeLinks()
this.updateActiveLinks()
this.selector = ["1","2","3","4","5","6"].map((str) => "h" + str + "[id]").join(",")
document.querySelectorAll(this.selector).forEach((header) => {
this.observer.observe(header);
});
document.addEventListener("turbo:load", this.observeLinks)
document.addEventListener("turbo:load", this.updateActiveLinks)
this.observeLinks()
this.updateActiveLinks()
}
disconnect () {
this.observer.disconnect()
}
get links () {
return [...document.querySelectorAll('#table-of-contents li a')];
}
handleIntersect = (entries) => {
entries.forEach(entry => {
// Remember which targets are visible
if (entry.isIntersecting) {
this.visibleSet.add(entry.target);
} else {
this.visibleSet.delete(entry.target);
}
});
this.updateActiveLinks();
}
updateActiveLinks = () => {
const links = this.links;
// Find the first visible target and activate the respective link
links.find(link => {
const target = this.linkMap.get(link);
if (target && this.visibleSet.has(target)) {
links.forEach(el => el.parentElement.classList.toggle('is-active', el === link));
return true;
}
return false;
});
}
observeLinks = () => {
this.links.forEach(link => {
const hash = link.hash.slice(1);
const target = hash ? document.querySelector(`main #${hash}`) : null;
if (target) {
this.linkMap.set(link, target);
this.observer.observe(target);
}
});
}
}

View File

@@ -0,0 +1,6 @@
import { Controller } from "@hotwired/stimulus"
export default class SearchController extends Controller {
show () {
document.querySelector("bridgetown-ninja-keys").open()
}
}

View File

@@ -0,0 +1,39 @@
import { Controller } from "@hotwired/stimulus"
export default class SideNavController extends Controller {
async open() {
document.addEventListener("sl-hide", this.reset)
this.drawer.removeAttribute("hidden")
this.scrollTop = window.scrollY;
document.body.classList.add('fixed-body');
// Scroll the wrapper, rather than setting an offset
// via `top` or `transform`.
document.body.scroll(0, this.scrollTop);
await this.drawer.show()
}
reset = () => {
window.scrollTo(0, this.scrollTop);
document.body.classList.remove('fixed-body');
}
async close () {
document.removeEventListener("sl-hide", this.reset)
this.reset()
await this.drawer.hide()
}
async toggle() {
if (this.drawer.open) {
await this.open()
} else {
await this.close()
}
}
get drawer() {
return document.querySelector("#side-nav-drawer")
}
}

View File

@@ -0,0 +1,58 @@
import { Controller } from "@hotwired/stimulus"
export default class ThemeSwitcher extends Controller {
constructor (...args) {
super(...args)
this.handleSelect = (e) => {
window.applyTheme(e.detail.item.value)
}
this.handleShortcut = (event) => {
if (
event.key === '\\' &&
!event.composedPath().some(el => {
return (
['input', 'textarea'].includes(el?.tagName?.toLowerCase()) ||
el.hasAttribute?.("contenteditable") ||
el.getAttribute?.("role") === "textbox"
)
})
) {
event.preventDefault();
window.applyTheme(window.themeIsDark() ? 'light' : 'dark');
}
}
this.setLight = () => {
window.applyTheme("light")
}
this.setDark = () => {
window.applyTheme("dark")
}
// Set the initial theme and sync the UI
window.applyTheme(window.getTheme());
}
connect () {
this.element.addEventListener("sl-select", this.handleSelect)
document.addEventListener("keydown", this.handleShortcut)
// Update the theme when the preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', this.setDark);
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', this.setLight);
// Set the initial theme and sync the UI
window.applyTheme(window.getTheme());
}
disconnect () {
this.element.removeEventListener("sl-select", this.handleSelect)
document.removeEventListener("keydown", this.handleShortcut)
}
}

View File

@@ -0,0 +1,87 @@
import "../styles/defer.css"
import { BridgetownNinjaKeys } from "@konnorr/bridgetown-quick-search/ninja-keys.js"
;(window.requestIdleCallback || window.setTimeout)(async () => {
// const { BridgetownNinjaKeys } = await import("@konnorr/bridgetown-quick-search/ninja-keys.js")
/** @type {import("konnors-ninja-keys").INinjaAction[]} */
const staticData = [
{
id: "theme-light",
icon: "<sl-icon name='sun'></sl-icon>",
title: "Light Mode",
section: "Theme",
keywords: "theme",
handler () {
window.applyTheme("light");
return {keepOpen: true}
}
},
{
id: "theme-dark",
icon: "<sl-icon name='moon'></sl-icon>",
title: "Dark Mode",
section: "Theme",
keywords: "theme",
handler () {
window.applyTheme("dark");
return {keepOpen: true}
}
},
{
id: "theme-system",
icon: "<sl-icon name='display'></sl-icon>",
title: "System",
section: "Theme",
keywords: "theme",
handler () {
window.applyTheme("system");
return {keepOpen: true}
}
},
]
;(class extends BridgetownNinjaKeys {
constructor (...args) {
super(...args)
this.staticData = staticData
}
createData() {
this.results = this.showResultsForQuery(this._search || "*").reverse()
this.results.forEach((result) => {
result.icon = `<sl-icon name="link-45deg"></sl-icon>`
})
return [
...this.staticData,
...this.results,
]
}
open () {
this.scrollTop = window.scrollY;
document.body.classList.add('fixed-body');
// Scroll the wrapper, rather than setting an offset
// via `top` or `transform`.
document.body.scroll(0, this.scrollTop);
this.nonModals.forEach((el) => {
el.setAttribute("inert", "")
})
super.open()
}
close () {
document.body.classList.remove('fixed-body');
window.scrollTo(0, this.scrollTop);
super.close()
this.nonModals.forEach((el) => el.removeAttribute("inert"))
}
get nonModals () {
return [...document.body.children].filter((el) => el.localName !== "bridgetown-ninja-keys")
}
}).define("bridgetown-ninja-keys")
})

View File

@@ -0,0 +1,61 @@
import "../styles/index.css"
import { Application } from "@hotwired/stimulus"
// Shoelace
import { setBasePath } from "@shoelace-style/shoelace/dist/utilities/base-path.js";
import LazyLoader from "./src/lazy-loader.js"
import * as Turbo from "@hotwired/turbo"
window.Turbo = Turbo
import "./src/layout.js"
//
LazyLoader()
//
setBasePath(process.env.BASE_PATH + "/shoelace-assets")
// Import all JavaScript & CSS files from src/_components
import components from "bridgetownComponents/**/*.{js,jsx,js.rb,css}"
window.Stimulus = Application.start()
import controllers from "./controllers/**/*.{js,js.rb}"
Object.entries(controllers).forEach(([filename, controller]) => {
if (filename.includes("_controller.") || filename.includes("-controller.")) {
const identifier = filename.replace("./controllers/", "")
.replace(/[_-]controller..*$/, "")
.replace("_", "-")
.replace("/", "--")
Stimulus.register(identifier, controller.default)
}
})
;(() => {
if (!window.scrollPositions) {
window.scrollPositions = {};
}
function preserveScroll() {
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
scrollPositions[element.id] = element.scrollTop;
});
}
function restoreScroll(event) {
if (event.detail && event.detail.newBody) {
event.detail.newBody.querySelectorAll('[data-preserve-scroll]').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
}
document.querySelectorAll('[data-preserve-scroll').forEach(element => {
element.scrollTop = scrollPositions[element.id];
});
}
window.addEventListener('turbo:before-cache', preserveScroll);
window.addEventListener('turbo:before-render', restoreScroll);
window.addEventListener('turbo:render', restoreScroll);
})();

View File

@@ -0,0 +1,10 @@
class ExternalIcon extends HTMLElement {
connectedCallback () {
this.attachShadow({ mode: "open" })
this.shadowRoot.innerHTML = `<sl-icon name="box-arrow-up-right"></sl-icon><sl-visually-hidden>Opens in new window</sl-visually-hidden>`
}
}
if (!window.customElements.get("external-icon")) {
window.customElements.define("external-icon", ExternalIcon)
}

View File

@@ -0,0 +1,211 @@
import {LitElement, html, css} from "lit"
class KrLayout extends LitElement {
static styles = css`
:host {
display: block;
box-sizing: border-box;
min-height: var(--height);
--height: 100vh;
--height: 100dvh;
--menu-width: auto;
--main-width: 1fr;
--aside-width: auto;
/** This is a best guess. We'll attempt to calculate this with a resize observer. **/
--header-height: 68.33px;
}
:host([variant="documentation"]) {
--menu-width: 250px;
--main-width: 105ch;
--aside-width: auto;
}
:host([variant="documentation"])::part(body) {
justify-content: center;
}
*, *:after, *:before {
box-sizing: border-box;;
}
[part~="base"] {
display: grid;
/** Header, Main, Footer **/
grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
min-height: var(--height);
}
[part~="header"] {
max-width: 100%;
position: sticky;
background: white;
z-index: 2;
}
:is(.header, .aside, .menu, .footer) ::slotted(*) {
height: 100%;
}
:is(.aside, .menu) ::slotted(*) {
min-width: 100%;
width: 100%;
max-width: 100%;
}
[part~="header"] {
top: 0px;
}
[part~="body"] {
display: grid;
/** Menu, Main, Aside **/
grid-template-columns: minmax(0, var(--menu-width)) minmax(0, var(--main-width)) minmax(0, var(--aside-width));
grid-template-rows: minmax(0, 1fr);
}
[part~="aside"],
[part~="menu"] {
display: grid;
grid-template-columns: minmax(0, 1fr);
max-height: calc(var(--height) - var(--header-height));
overflow: auto;
position: sticky;
top: var(--header-height);
overscroll-behavior: contain;
}
[part~="main"] {
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr);
/* main-header, main, main-footer */
grid-template-rows: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
}
[part~="footer"] {}
sl-visually-hidden:not(:focus-within) {
position: absolute !important;
width: 1px !important;
height: 1px !important;
clip: rect(0 0 0 0) !important;
clip-path: inset(50%) !important;
border: none !important;
overflow: hidden !important;
white-space: nowrap !important;
padding: 0 !important;
}
.skip-links {
position: absolute;
top: 0;
left: 0;
height: calc(var(--header-height, 48px) - 2px);
width: 100vw;
z-index: 4;
background-color: inherit;
display: grid;
grid-template-columns: minmax(0, 1fr);
place-items: center;
text-align: center;
}
`
static properties = {
main_id: { attribute: "main-id", reflect: true }
}
constructor () {
super()
this.main_id = "main"
}
createResizeObserver (slot) {
return new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.contentBoxSize) {
const contentBoxSize = entry.borderBoxSize[0];
this.style.setProperty(`--${slot}-height`, `${contentBoxSize.blockSize}px`)
}
}
})
}
connectedCallback () {
super.connectedCallback?.()
this.headerResizeObserver = this.createResizeObserver("header");
this.footerResizeObserver = this.createResizeObserver("footer");
setTimeout(() => {
this.header = this.shadowRoot.querySelector("[part~='header']")
this.headerResizeObserver.observe(this.header)
// this.footer = this.shadowRoot.querySelector("[part~='main-footer']")
// this.footerResizeObserver.observe(this.footer)
})
}
disconnectedCallback () {
super.disconnectedCallback?.()
this.headerResizeObserver.unobserve(this.header)
// this.footerResizeObserver.unobserve(this.footer)
}
render () {
return html`
<sl-visually-hidden class="skip-links" part="skip-links">
<slot name="skip-links">
<a href=${`#${this.main_id}`} part="skip-link">
${this.skipToMain || "Skip to main"}
</a>
</slot>
</sl-visually-hidden>
<div class="base" part="base">
<div class="header" part="header">
<slot name="header"></slot>
</div>
<div class="body" part="body">
<div class="menu" part="menu">
<slot name="menu"></slot>
</div>
<div class="main" part="main">
<div class="main-header" part="main-header">
<slot name="main-header"></slot>
</div>
<div class="main-content" part="main-content"><slot></slot></div>
<div class="main-footer" part="main-footer">
<slot name="main-footer"></slot>
</div>
</div>
<div class="aside" part="aside">
<slot name="aside"></slot>
</div>
</div>
<div class="footer" part="footer">
<slot name="footer"></slot>
</div>
</div>
<div part="dialog" class="dialog">
<slot name="dialog"></slot>
</div>
`
}
}
if (!window.customElements.get("kr-layout")) {
window.customElements.define("kr-layout", KrLayout)
}

View File

@@ -0,0 +1,101 @@
import LazyLoader from "web-component-lazy-loader";
export default function lazyLoader() {
return new LazyLoader({
components: {
"light-pen": {
register () {
import("light-pen/exports/light-pen-register.js")
}
},
"light-preview": {
register () {
import("light-pen/exports/light-preview-register.js")
}
},
"clipboard-copy": {
register() {
import("@github/clipboard-copy-element");
},
},
"external-icon": {
register() {
import("./external-icon.js");
},
},
// Shoelace
"sl-alert": {
register() {
import("@shoelace-style/shoelace/dist/components/alert/alert.js");
},
},
// "sl-breadcrumb": {
// register () { import("@shoelace-style/shoelace/dist/components/breadcrumb/breadcrumb.js"); }
// },
// "sl-breadcrumb-item": {
// register () { import("@shoelace-style/shoelace/dist/components/breadcrumb-item/breadcrumb-item.js"); }
// },
"sl-button": {
register() {
import("@shoelace-style/shoelace/dist/components/button/button.js");
},
},
"sl-divider": {
register() {
import("@shoelace-style/shoelace/dist/components/divider/divider.js");
},
},
"sl-drawer": {
register() {
import("@shoelace-style/shoelace/dist/components/drawer/drawer.js");
},
},
"sl-dropdown": {
register() {
import(
"@shoelace-style/shoelace/dist/components/dropdown/dropdown.js"
);
},
},
"sl-icon": {
register() {
import("@shoelace-style/shoelace/dist/components/icon/icon.js");
},
},
"sl-icon-button": {
register() {
import(
"@shoelace-style/shoelace/dist/components/icon-button/icon-button.js"
);
},
},
"sl-menu": {
register() {
import("@shoelace-style/shoelace/dist/components/menu/menu.js");
},
},
"sl-menu-item": {
register() {
import(
"@shoelace-style/shoelace/dist/components/menu-item/menu-item.js"
);
},
},
"sl-menu-label": {
register() {
import(
"@shoelace-style/shoelace/dist/components/menu-label/menu-label.js"
);
},
},
"sl-visually-hidden": {
register() {
import(
"@shoelace-style/shoelace/dist/components/visually-hidden/visually-hidden.js"
);
},
},
},
}).start();
}

View File

@@ -0,0 +1,24 @@
@import "./components/_alert.css";
@import "./components/_aspect_ratio.css";
@import "./components/_blog.css";
@import "./components/_button.css";
@import "./components/_call_to_action.css";
@import "./components/_clipboard.css";
@import "./components/_contact.css";
@import "./components/_footer.css";
@import "./components/_header.css";
@import "./components/_hero.css";
@import "./components/_input.css";
@import "./components/_layout.css";
@import "./components/_list.css";
@import "./components/_link.css";
@import "./components/_logo.css";
@import "./components/_main_list.css";
@import "./components/_pagination.css";
@import "./components/_side_nav.css";
@import "./components/_syntax_block.css";
@import "./components/_table_of_contents.css";
@import "./components/_text.css";
@import "./components/_theme_switcher.css";
@import "./components/_top_nav.css";
@import "./components/_tables.css";

View File

@@ -0,0 +1,42 @@
/** For for prose sections, blogging concerns, Markdown, etc. */
/** Pretty syntax blocks */
pre.highlight {
padding: 0.85rem 1rem;
margin: 0;
overflow-x: auto;
}
code
.highlighter-rouge,
.highlight {
border-radius: 8px;
font-size: 0.85rem;
line-height: 1.4;
font-family: var(--sl-font-mono);
}
code,
.highlight,
.highlighter-rouge {
position: relative;
}
/* Inline code blocks */
code,
code.highlight,
code.highlighter-rouge {
/** Layout */
display: inline-block;
font-size: 0.85em;
padding: 0.1em 0.4em;
margin: 4px 0;
border-radius: 6px;
white-space: break-spaces;
vertical-align: middle;
/* Decoration */
background-color: var(--code-background-color);
color: var(--code-color);
}

View File

@@ -0,0 +1,195 @@
html {
box-sizing: border-box;
height: 100%;
}
*, *:before, *:after {
box-sizing: border-box;
}
body {
max-width: 100%;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeSpeed;
scrollbar-gutter: stable;
font-family: var(--sl-font-sans);
background-color: var(--body-color);
color: var(--text-color);
display: grid;
grid-template-rows: minmax(0, 1fr);
min-height: 100%;
margin: 0;
padding: 0;
}
[hidden] {
display: none !important;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
body {
font-family: var(--sl-font-sans);
min-height: 100%;
}
button {
appearance: none;
border: none;
background: none;
}
button:hover {
cursor: pointer;
}
h1,h2,h3,h4,h5,h6 {
scroll-margin-top: calc(80px + 2em);
margin: 0;
padding: 0;
margin-top: var(--sl-spacing-large);
margin-bottom: var(--sl-spacing-small);
}
main p {
font-size: 1rem;
}
h1 {
font-size: 1.25rem;
margin: 0;
margin-top: 1rem;
margin-bottom: 1rem;
}
h2 {
font-size: 1.15rem;
}
h3, h4 {
font-size: 1.1rem;
}
h5, h6 {
font-size: 1.05rem;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
padding: 0;
font-size: 1rem;
}
p, li {
line-height: 1.5;
}
li {
font-size: 1rem;
}
ul {
line-height: 1.8;
list-style: disc outside;
}
ul ul {
list-style: circle outside;
}
ul ul ul {
list-style: square outside;
}
ul ul ul ul {
list-style: disc outside;
}
[tabindex="-1"] {
outline: transparent;
}
a,
kr-layout::part(skip-link) {
word-wrap: anywhere;
/* outline: transparent; */
/* Unable to distinguish identifier, this makes links obvious */
text-decoration-line: underline;
text-decoration-style: solid;
text-decoration-color: var(--link-color);
text-underline-offset: 0.25em;
color: var(--link-color);
border-radius: 2px;
display: inline;
padding: 0.25em;
margin: -0.25em;
line-height: 1.2;
transition: all 0.2s;
}
kr-layout::part(skip-link):is(:hover),
a:is(:hover) {
color: var(--link-color-focus);
}
kr-layout::part(skip-link):is(:focus-visible),
a:is(:focus-visible) {
box-shadow: 0px 0px 3px 3px var(--sl-color-primary-600);
outline: transparent;
/* for when Safari supports border-radius */
/* outline: 3px solid var(--sl-color-primary-600); */
/* outline-offset: 4px; */
}
@supports not selector(:focus-visible) {
kr-layout::part(skip-link):is(:focus),
a:is(:focus) {
box-shadow: 0px 0px 3px 3px var(--sl-color-primary-600);
outline: transparent;
/* for when Safari supports border-radius */
/* outline: 3px solid var(--sl-color-primary-600); */
/* outline-offset: 4px; */
}
}
code > span {
line-height: var(--sl-line-height-dense);
}
kbd {
font-family: var(--sl-font-mono);
font-size: 87.5%;
background-color: var(--sl-color-neutral-50);
border-radius: 4px;
border: solid 1px var(--sl-color-neutral-200);
box-shadow: inset 0 1px 0 var(--sl-color-neutral-0);
padding: 2px 5px;
line-height: 1;
}
a external-icon {
padding-top: 0.25em;
font-size: 0.75em;
margin-inline-start: -0.15em;
}
blockquote {
border-inline-start: 4px solid var(--divider-color);
margin-inline-start: 0.5rem;
padding: 0.75rem;
background-color: var(--sl-color-neutral-50);
}

View File

@@ -0,0 +1,2 @@
@import "./overrides/bridgetown-ninja-keys.css";
@import "./overrides/shoelace.css";

View File

@@ -0,0 +1,13 @@
sl-alert {
margin: 1rem 0;
}
sl-alert p {
margin: 0;
padding: 0;
}
sl-alert::part(base) {
box-shadow: 3px 3px 6px 1px var(--sl-color-neutral-100);
}

View File

@@ -0,0 +1,22 @@
[style*="--aspect-ratio"] > :first-child {
width: 100%;
}
[style*="--aspect-ratio"] > img {
height: auto;
}
[style*="--aspect-ratio"] {
position: relative;
}
[style*="--aspect-ratio"]::before {
content: "";
display: block;
padding-bottom: calc(100% / (var(--aspect-ratio)));
}
[style*="--aspect-ratio"] > :first-child {
position: absolute;
top: 0;
left: 0;
height: 100%;
}

View File

@@ -0,0 +1,31 @@
.blog-link:focus {
box-shadow: none;
}
.blog-link:focus .blog-link__card::part(base) {
box-shadow: 0px 0px 4px 1px var(--sl-color-primary-700);
}
.blog-link__card::part(base) {
transform: translateY(0px);
transition: transform 0.5s ease-in-out;
}
.blog-link:is(:focus, :hover) .blog-link__card::part(base) {
transform: translateY(-3px);
}
.blog-link,
.blog-link:focus {
color: var(--sl-color-neutral-700);
}
.blog-link:is(:hover) {
color: var(--sl-color-primary-900);
}
.blog-link:is(:hover) .blog-link__card::part(base) {
border: 1px solid var(--sl-color-primary-700);
background-color: var(--sl-color-primary-50);
}

View File

@@ -0,0 +1,52 @@
/** Button */
.button {
padding: 0.4em 0.6em;
display: block;
position: relative;
width: auto;
cursor: pointer;
display: inline-flex;
align-items: stretch;
justify-content: center;
border-style: solid;
border-width: var(--sl-input-border-width);
font-family: var(--sl-input-font-family);
font-weight: var(--sl-font-weight-semibold);
user-select: none;
white-space: nowrap;
transition: var(--sl-transition-x-fast) background-color, var(--sl-transition-x-fast) color,
var(--sl-transition-x-fast) border, var(--sl-transition-x-fast) box-shadow;
background-color: var(--sl-color-neutral-0);
border-color: var(--sl-color-neutral-300);
color: var(--sl-color-neutral-700);
font-size: var(--sl-button-font-size-medium);
border-radius: var(--sl-input-border-radius-medium);
}
.button::-moz-focus-inner {
border: 0;
}
.button:focus {
outline: transparent;
}
.button:focus-visible {
outline: var(--sl-focus-ring);
outline-offset: var(--sl-focus-ring-offset);
}
.button:hover {
background-color: var(--sl-color-primary-50);
border-color: var(--sl-color-primary-300);
color: var(--sl-color-primary-700);
}
.button:active {
background-color: var(--sl-color-primary-100);
border-color: var(--sl-color-primary-400);
color: var(--sl-color-primary-700);
}

View File

@@ -0,0 +1,24 @@
.call-to-action__items {
margin-top: 1.75rem;
margin-bottom: 3rem;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.5rem;
padding: 0;
}
.reason__grid {
display: grid;
gap: 1rem;
}
.reason__grid > * {
height: 100%;
}
@media screen and (min-width: 1024px) {
.reason__grid {
grid-template-columns: repeat(3, 1fr);
}
}

View File

@@ -0,0 +1,33 @@
/** Clipboard **/
.clipboard.clipboard--success:is(:hover, :focus, :active),
.clipboard.clipboard--success {
background-color: var(--sl-color-success-100);
border-color: var(--sl-color-success-600);
color: var(--sl-color-success-800);
}
.clipboard {
align-items: center;
}
.clipboard sl-icon {
font-size: 1rem;
}
.clipboard__icon--success, .clipboard__icon--idle {
transition: transform .2s ease-in-out;
}
.clipboard .clipboard__icon--idle,
.clipboard.clipboard--success .clipboard__icon--success {
transform: scale(1);
}
.clipboard.clipboard--success .clipboard__icon--idle,
.clipboard.clipboard--idle .clipboard__icon--success {
height: 0;
width: 0;
visibility: hidden;
transform: scale(0);
}

View File

@@ -0,0 +1,6 @@
.contact__title {
line-height: 1.3;
font-size: 1.25rem;
margin-bottom: 0;
}

View File

@@ -0,0 +1,7 @@
.footer {
width: 100%;
margin-top: auto;
padding: var(--sl-spacing-large) var(--sl-spacing-2x-small);
text-align: center;
background-color: var(--sl-color-neutral-50);
}

View File

@@ -0,0 +1,11 @@
.nav-header {
display: grid;
grid-template-columns: minmax(0, 1fr);
padding: 0.5rem;
padding-inline-end: 1.5rem;
font-size: 1.05em;
justify-items: space-between;
border-bottom: 1px solid var(--sl-color-neutral-200);
background-color: var(--body-color);
min-height: var(--header-height);
}

View File

@@ -0,0 +1,30 @@
.hero {
margin: 0 auto;
padding: 16px;
max-width: 600px;
}
.hero--mobile {
display: flex;
margin-top: 1rem;
margin-bottom: 1rem;
}
.hero--mobile a {
display: flex;
justify-content: center;
margin: 0 auto;
width: auto;
flex: 0 0 auto;
}
.hero--mobile .logo__text{
font-size: 2.5rem;
}
.hero__caption {
font-size: 0.85em;
margin-top: 6px;
border-inline-start: 4px solid var(--divider-color);
padding-inline-start: 8px;
}

View File

@@ -0,0 +1,28 @@
.input {
font-size: var(--input-font-size);
font-family: inherit;
padding: var(--input-padding);
background-color: var(--input-bg-color);
border: var(--input-border-width) var(--input-border-style) var(--input-border-color);
border-radius: var(--input-border-radius);
height: var(--sl-spacing-2x-large);
color: var(--sl-color-neutral-700);
}
.input {
transition: 150ms box-shadow ease-in-out;
}
.input:focus,
.input:hover {
border-color: var(--sl-color-primary-500);
}
.input:focus {
box-shadow: 0 0 0 var(--input-focus-ring-width)
hsla(198.6 88.7% 48.4% / 0.4);
/* for users who may have shadows and other colors turned off */
outline: var(--input-focus-ring-width) solid transparent;
}

View File

@@ -0,0 +1,103 @@
:is(.index, .doc, .page, .post) kr-layout {
background-color: var(--sl-color-neutral-50);
}
/* .documentation-content {} */
body:is(.index, .doc, .page, .post) kr-layout::part(main-content) {
background: var(--body-color);
}
kr-layout > [slot='aside'] {
display: none;
}
/* Janky hack for iOS mobile */
.fixed-body {
position: fixed;
height: var(--viewport-height, 100%);
width: 100%;
/* Allow the main content to be scrolled,
so we can adjust the scroll position with JS. */
overflow: auto;
}
body:is(.default, .home) kr-layout {
--menu-width: 0px;
--main-width: 1fr;
--aside-width: 0px;
}
kr-layout {
--menu-width: auto;
--main-width: 105ch;
--aside-width: auto;
}
kr-layout,
kr-layout::part(skip-links) {
background-color: var(--body-color);
}
kr-layout::part(body) {
max-width: 100%;
justify-content: center;
margin: 0 auto;
}
kr-layout::part(main) {
background-color: var(--body-color);
}
main {
max-width: 100%;
padding: 0 var(--main-padding-x) 2rem;
width: 100%;
margin: 0 auto;
background-color: var(--body-color);
color: var(--text-color);
}
:is(.default, .home) main {
max-width: var(--main-width);
}
:is(.default, .home) .footer {
width: 100vw;
}
:is(.doc, .index, .page, .post) main {
background-color: var(--body-color);
}
@media screen and (min-width: 1360px) {
kr-layout > [slot='aside'] {
display: block;
}
}
@media screen and (min-width: 1024px) {
body:is(.index, .doc, .page, .post) kr-layout::part(main),
body:is(.index, .doc, .page, .post) kr-layout::part(menu),
body:is(.index, .doc, .page, .post) kr-layout::part(aside) {
padding-top: 2rem;
}
.documentation-content {
border-radius: 32px;
}
body:is(.index, .doc, .page, .post) kr-layout::part(main) {
background-color: transparent;
margin-inline-end: 16px;
margin-inline-start: 16px;
}
body:is(.index, .doc, .page, .post) kr-layout::part(main-content) {
box-shadow: 0 0 5px 0 rgb(0 0 0 / 10%);
background: var(--body-color);
border-radius: 32px;
}
}

View File

@@ -0,0 +1,47 @@
.logo__link {
display: flex;
flex: 1 0 auto;
width: auto;
align-items: center;
}
.logo__link:is(:focus, :hover) {
text-decoration-line: underline;
text-decoration-style: solid;
text-decoration-color: var(--link-color);
color: var(--link-color-focus);
}
.logo__link:is(:focus, :hover) * {
color: var(--link-color-focus);
}
.blog-link__header,
.contact__link,
.main-list__link,
.side-nav__link,
.nav-link {
font-weight: bold;
color: var(--sl-color-neutral-800);
text-decoration-line: underline;
text-decoration-style: solid;
text-decoration-color: var(--sl-color-primary-500);
text-underline-offset: 0.3em;
}
.link__flex {
display: inline-flex;
gap: 0.25em;
align-items: center;
cursor: pointer;
max-width: max-content;
}
.link--small {
font-size: 0.85em;
}
.link--large {
font-size: 1em;
}

View File

@@ -0,0 +1,3 @@
ul.list li {
line-height: 2.5;
}

View File

@@ -0,0 +1,21 @@
.main-list {
padding: 0;
list-style: none;
display: grid;
gap: 8px;
}
.main-list__item {
padding: 8px;
max-width: 100%;
line-height: 1.3;
font-size: 1.25rem;
text-align: center;
}
@media screen and (min-width: 400px) {
.main-list {
grid-auto-flow: column;
}
}

View File

@@ -0,0 +1,44 @@
/** Pagination */
sl-button:is(.previous-page, .next-page) {
transition: transform 0.15s;
}
sl-button:is(.previous-page, .next-page)::part(label) {
max-width: calc(100% - 24px);
width: 100%;
overflow: hidden;
}
sl-button:is(.next-page)::part(base) {
justify-content: start;
justify-content: flex-start;
}
sl-button:is(.previous-page)::part(base) {
justify-content: end;
justify-content: flex-end;
}
sl-button:is(.next-page, .previous-page):is(:hover, :focus) {
box-shadow: var(--sl-color-neutral-50) 0px 12px 13px;
transform: translateY(-2px);
}
.pagination-footer {
padding-top: 2rem;
margin-top: auto;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
justify-content: space-between;
gap: 1rem;
}
pagination-title {
display: block;
max-width: 100%;
overflow: hidden;
font-size: 1.15rem;
text-overflow: ellipsis;
white-space: nowrap;
}

View File

@@ -0,0 +1,95 @@
.side-nav ul {
list-style-type: " ";
}
.side-nav__menu {
margin: 0;
padding-inline-start: 0;
min-width: 250px;
max-width: 100%;
}
ul.side-nav__menu li::marker {
content: ""
}
.side-nav__item {
padding: 8px;
max-width: 100%;
line-height: 1.3;
font-size: 1rem;
font-weight: normal;
}
.side-nav__link {
font-weight: normal;
text-decoration: none;
color: var(--sl-color-neutral-700);
}
.side-nav__link:is(:hover, :focus) {
color: var(--link-color);
text-decoration: underline;
}
.side-nav--desktop {
display: none;
flex-direction: column;
align-items: flex-end;
max-width: 275px;
max-height: 100%;
padding-bottom: 1rem;
overflow: auto;
}
.side-nav__menu {
list-style: none;
padding: 0;
padding-top: 1rem;
}
.side-nav__drawer__header {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--sl-color-neutral-200);
padding-bottom: 1rem;
}
.side-nav__item.is-active {
font-weight: normal;
border-inline-start: 4px solid var(--sl-color-primary-600);
background-color: var(--sl-color-primary-100);
}
.side-nav__item.is-active,
.side-nav__item.is-active a {
color: var(--sl-color-primary-700);
}
.side-nav__category-header {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.side-nav__category-menu:not(:first-child) {
margin-top: 1.25rem;
}
@media screen and (min-width: 1024px) {
.side-nav--desktop {
display: flex;
}
.side-nav__category-menu {
padding-inline-start: 40px;
}
.side-nav__category-menu:first-of-type {
padding-top: 0;
}
.side-nav__drawer {
display: none;
}
}

View File

@@ -0,0 +1,38 @@
.syntax-block {
position: relative;
margin-block-end: 1rem;
margin-block-start: 1rem;
}
.syntax-block {
border: 1px solid var(--divider-color);
border-radius: var(--sl-border-radius-large);
}
.syntax-block pre {
margin: 0;
}
.syntax-block__actions {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 8px;
}
.syntax-block .highlight {
border-top-right-radius: 0px;
border-top-left-radius: 0px;
}
.syntax-block__badge {
font-size: 0.85em;
color: var(--sl-color-neutral-700);
}
:is(.language-bash, .language-shell, .language-zsh, .language-sh).highlighter-rouge pre.highlight::before {
content: "$";
user-select: none;
padding-inline-end: 0.45em;
color: gray;
}

View File

@@ -0,0 +1,39 @@
.table-of-contents {
margin-inline-start: 1rem;
min-width: 275px;
max-width: 275px;
}
.table-of-contents * {
max-width: 100%;
}
.table-of-contents__header {
margin: 0;
padding: 0;
padding-top: 1rem;
font-weight: normal;
color: var(--sl-color-neutral-800);
font-size: 1.05rem;
}
.table-of-contents__list {
padding: 0;
margin: 0;
margin-top: 1rem;
list-style: none;
}
.table-of-contents__list a {
text-decoration: none;
}
.table-of-contents__list li {
border-inline-start: 4px solid var(--sl-color-neutral-400);
padding-inline-start: 1rem;
}
.table-of-contents__list li {
padding: 0.5rem;
}

View File

@@ -0,0 +1,33 @@
.table-container {
max-width: 100%;
overflow-x: auto;
}
table {
border-collapse: collapse;
min-width: 100%;
height: auto;
}
th {
text-align: start;
font-weight: 500;
font-size: 1.1em;
}
th,
td {
padding: 1rem;
}
tr {
border-bottom: 1px solid var(--divider-color);
}
tbody > tr:nth-of-type(2n) {
background-color: rgba(0,0,0,0.02);
}
tbody > tr code {
border: 1px solid rgba(0,0,0,0.2);
}

View File

@@ -0,0 +1,21 @@
.logo__text {
color: var(--text-color-primary);
font-size: var(--sl-font-size-x-large);
padding-bottom: var(--sl-spacing-x-small);
margin-bottom: calc(var(--sl-spacing-x-small) - (var(--sl-spacing-x-small) * 2));
margin-inline-start: var(--sl-spacing-2x-small);
}
.site-title,
.slogan {
text-align: center;
}
.slogan {
max-width: max(75%, 600px);
margin: 0 auto;
}
.site-title {
margin-top: 0;
}

View File

@@ -0,0 +1,26 @@
/* Themes */
.theme-item {
display: flex;
align-items: center;
gap: 6px;
width: 100%;
}
[data-controller~='theme-switcher'],
[data-controller~='theme-switcher']:not(:defined) {
height: 0;
width: 0;
opacity: 0;
visibility: hidden;
}
[data-controller~='theme-switcher']:defined {
height: unset;
width: unset;
visibility: visible;
opacity: 1;
animation: fadeInAnimation ease 500ms;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}

View File

@@ -0,0 +1,106 @@
.top-nav {
display: flex;
max-width: var(--top-nav-max-width);
justify-content: center;
margin: 0 auto;
background-color: var(--body-color);
width: 100%;
}
.top-nav__search,
.top-nav__go-home,
.top-nav__categories {
display: flex;
align-items: center;
}
.top-nav__go-home {
min-width: 100px;
}
.top-nav__hamburger__button {
font-size: 1.75em;
}
.top-nav__hamburger__button::part(base),
.top-nav__search__button::part(base) {
color: var(--sl-color-gray-600);
font-size: var(--sl-font-size-x-large);
}
.top-nav__hamburger__button::part(base):is(:hover, :focus),
.top-nav__search__button::part(base):is(:hover, :focus) {
color: var(--link-color-focus);
}
.top-nav__hamburger__button::part(base):focus,
.top-nav__search__button::part(base):focus {
border-color: var(--link-color-focus);
box-shadow: 0 0 6px var(--link-color-focus);
}
.top-nav__search {
margin: 0;
margin-inline-start: auto;
}
.top-nav__github sl-icon {
font-size: 2rem;
}
.top-nav__github {
height: auto;
}
.top-nav__links {
display: flex;
align-items: center;
justify-content: flex-end;
flex: 0 1 auto;
}
.top-nav__categories {
display: none;
font-family: var(--sl-font-sans);
font-weight: bold;
min-width: fit-content;
gap: 1rem;
}
.top-nav__categories > * {
margin: 0;
}
.top-nav__github {
padding-top: var(--sl-spacing-2x-small);
margin-inline-start: var(--sl-spacing-medium);
margin-inline-end: 2px;
}
.top-nav__go-home,
.top-nav__command-palette-button {
display: none;
}
@media screen and (min-width: 1024px) {
/* .hero--mobile, */
.top-nav__hamburger__button,
.top-nav__search__button {
display: none;
}
.top-nav__go-home,
.top-nav__command-palette-button {
display: flex;
}
.top-nav__search {
margin: 0 auto 0 auto;
order: unset;
}
.top-nav__categories {
display: flex;
}
}

View File

@@ -0,0 +1,10 @@
/*
* Anything that can be deferred and not cause layout shift.
* IE: colors, fonts, syntax highlighting, etc.
*/
@import "@shoelace-style/shoelace/dist/themes/light.css";
@import "@shoelace-style/shoelace/dist/themes/dark.css";
@import "./tokens/_colors.css";
@import "./defer/_syntax.css";
@import "./defer/_fonts.css";

View File

@@ -0,0 +1,17 @@
@keyframes fadeInAnimation {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOutAnimation {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@@ -0,0 +1,10 @@
/** Nothing here...yet */
/* @font-face { */
/* font-family: 'VT323'; */
/* src: */
/* url('/assets/fonts/VT323.ttf') format('truetype'); */
/* font-display: swap; */
/* font-weight: 400; */
/* font-style: normal; */
/* font-stretch: 100; */
/* } */

View File

@@ -0,0 +1,67 @@
/** Syntax highlighting only */
.highlight { color: black; background-color: #f8f8f8; font-family: var(--sl-font-mono); }
.highlight * { font-family: var(--sl-font-mono); }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #999988; font-style: italic } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #000000; font-weight: bold } /* Keyword */
.highlight .o { color: #000000; font-weight: bold } /* Operator */
.highlight .ch { color: #999988; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #999999; font-weight: bold; font-style: italic } /* Comment.Preproc */
.highlight .cpf { color: #999988; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #999999 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #aaaaaa } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #000000; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #000000; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #009999 } /* Literal.Number */
.highlight .s { color: #dd1144 } /* Literal.String */
.highlight .na { color: #008080 } /* Name.Attribute */
.highlight .nb { color: #0086B3 } /* Name.Builtin */
.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
.highlight .no { color: #008080 } /* Name.Constant */
.highlight .nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */
.highlight .ni { color: #800080 } /* Name.Entity */
.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
.highlight .nl { color: #990000; font-weight: bold } /* Name.Label */
.highlight .nn { color: #555555 } /* Name.Namespace */
.highlight .nt { color: #000080 } /* Name.Tag */
.highlight .nv { color: #008080 } /* Name.Variable */
.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #009999 } /* Literal.Number.Bin */
.highlight .mf { color: #009999 } /* Literal.Number.Float */
.highlight .mh { color: #009999 } /* Literal.Number.Hex */
.highlight .mi { color: #009999 } /* Literal.Number.Integer */
.highlight .mo { color: #009999 } /* Literal.Number.Oct */
.highlight .sb { color: #dd1144 } /* Literal.String.Backtick */
.highlight .sc { color: #dd1144 } /* Literal.String.Char */
.highlight .sd { color: #dd1144 } /* Literal.String.Doc */
.highlight .s2 { color: #dd1144 } /* Literal.String.Double */
.highlight .se { color: #dd1144 } /* Literal.String.Escape */
.highlight .sh { color: #dd1144 } /* Literal.String.Heredoc */
.highlight .si { color: #dd1144 } /* Literal.String.Interpol */
.highlight .sx { color: #dd1144 } /* Literal.String.Other */
.highlight .sr { color: #009926 } /* Literal.String.Regex */
.highlight .s1 { color: #dd1144 } /* Literal.String.Single */
.highlight .ss { color: #990073 } /* Literal.String.Symbol */
.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #008080 } /* Name.Variable.Class */
.highlight .vg { color: #008080 } /* Name.Variable.Global */
.highlight .vi { color: #008080 } /* Name.Variable.Instance */
.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */

View File

@@ -0,0 +1,9 @@
/* Tokens */
@import "./tokens/_layout.css";
@import "./tokens/_typography.css";
@import "./_normalize.css";
@import "./_components.css";
@import "./_content.css";
@import "./_layout.css";
@import "./_overrides.css";

View File

@@ -0,0 +1,30 @@
bridgetown-ninja-keys {
/* This is a hacky media query. We do this so that we can guess for mobile devices we should
be full height / width so we don't need to deal with dialog scrolling bullshit. */
--ninja-top: 0px;
--ninja-width: clamp(200px, 100vw, 100ch);
--ninja-accent-color: var(--sl-color-primary-600);
z-index: 9999;
}
bridgetown-ninja-keys::part(modal-content) {
height: 100%;
--ninja-actions-height: 100%;
border-radius: 0px;
}
@media screen and (min-width: 768px) {
bridgetown-ninja-keys::part(modal-content) {
height: unset;
--ninja-top: 10vh;
--ninja-actions-height: 60vh;
--ninja-width: clamp(200px, 90vw, 100ch);
border-radius: 8px;
}
}
bridgetown-ninja-keys.dark {
--ninja-modal-background: rgba(35, 35, 35, 1);
--ninja-text-color: gray;
}

View File

@@ -0,0 +1,10 @@
:where(sl-visually-hidden:not(:defined)) {
display: none;
}
/** Bypass a11y checker. */
sl-visually-hidden {
background-color: var(--sl-color-neutral-0);
background-color: var(--sl-color-neutral-1000);
}

View File

@@ -0,0 +1,35 @@
:root,
html.sl-theme-light,
html.sl-theme-dark {
/** Colors */
--text-color: var(--sl-color-neutral-1000);
--light-text-color: var(--sl-color-neutral-700);
--body-color: var(--sl-color-neutral-0);
--logo-color: var(--sl-color-neutral-1000);
--input-border-color: var(--divider-color);
--input-bg-color: var(--sl-color-neutral-0);
--input-transition: 100ms box-shadow ease-in-out;
--color-primary: var(--sl-color-primary-600);
--text-color-primary: var(--sl-color-primary-700);
--link-color: var(--sl-color-primary-700);
--link-color-focus: var(--sl-color-primary-500);
--divider-color: var(--sl-color-neutral-300);
--code-color: hsl(209, 27%, 26%);
--code-background-color: hsl(210, 29%, 97%);
/** Shoelace overrides */
/* --sl-color-primary-50: var(--sl-color-emerald-50); */
/* --sl-color-primary-100: var(--sl-color-emerald-100); */
/* --sl-color-primary-200: var(--sl-color-emerald-200); */
/* --sl-color-primary-300: var(--sl-color-emerald-300); */
/* --sl-color-primary-400: var(--sl-color-emerald-400); */
/* --sl-color-primary-500: var(--sl-color-emerald-500); */
/* --sl-color-primary-600: var(--sl-color-emerald-600); */
/* --sl-color-primary-700: var(--sl-color-emerald-700); */
/* --sl-color-primary-800: var(--sl-color-emerald-800); */
/* --sl-color-primary-900: var(--sl-color-emerald-900); */
/* --sl-color-primary-950: var(--sl-color-emerald-950); */
}

View File

@@ -0,0 +1,25 @@
:root,
html.sl-theme-light,
html.sl-theme-dark {
/* Layout related tokens (anything that affects layout shift) */
--z-header: 2;
--z-search: 3;
--top-nav-max-width: 1650px;
--body-max-width: 1800px;
--side-nav-width: 300px;
--main-width: clamp(75ch, 70vw, 100ch);
--main-padding-x: 2rem;
--input-focus-ring-width: 3px;
--input-font-size: var(--sl-font-size-size-medium);
--input-sm-padding: var(--sl-spacing-2x-small) var(--sl-spacing-x-small);
--input-padding: var(--sl-spacing-medium) var(--sl-spacing-large);
--input-border-width: 1px;
--input-border-style: solid;
--input-border-radius: 4px;
--header-height: 58px;
}

View File

@@ -0,0 +1,8 @@
:root,
html.sl-theme-dark,
html.sl-theme-light {
--sl-font-sans:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
--sl-font-serif: Georgia, 'Times New Roman', serif;
--sl-font-mono: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
}

View File

@@ -0,0 +1,11 @@
class Builders::ChangelogGenerator < SiteBuilder
def build
# changelog_file = File.expand_path("../../../CHANGELOG.md", __dir__)
# add_resource :documentation, "references/00-changelog.md" do
# title "Changelog"
# permalink "/references/changelog"
# category "references"
# content File.read(changelog_file)
# end
end
end

View File

@@ -0,0 +1,275 @@
require "cgi"
require "custom_elements_manifest_parser"
class Builders::ComponentGenerator < SiteBuilder
def build
generator do
custom_elements_manifest_path = File.read(File.expand_path("../../../custom-elements.json", __dir__))
manifest = JSON.parse(custom_elements_manifest_path)
# @return [::CustomElementsManifestParser::Parser]
parser = ::CustomElementsManifestParser.parse(manifest)
elements = parser.find_all_tag_names
resources = site.collections.documentation.resources
resources.each do |resource|
component_name = File.basename(resource.relative_path.basename, ".md").to_s
metadata = elements[component_name]
next if metadata.nil?
resource.data.merge!({
"description" => metadata.description
})
slots = metadata.slots
attributes = metadata.members.select { |member| member.attributes[:attribute] }
events = metadata.events
# Use functions instead of methods so we don't clobber built-in #methods method.
functions = metadata.members.select { |member| member.kind == "method" }
parts = metadata.cssParts
resource.content += [
slots_table(slots).html_safe,
attributes_table(attributes).html_safe,
events_table(events).html_safe,
functions_table(functions).html_safe,
parts_table(parts).html_safe
].join("\n\n")
end
end
end
def escape(html)
CGI.escape_html(html || "")
end
def slots_table(slots)
return "" if slots.nil? || slots.empty?
tbody = slots.map do |slot|
<<~HTML
<tr>
<td>
<code>#{slot.name}</code>
</td>
<td>
#{slot.description.to_s.empty? ? "-" : escape(slot.description)}
</td>
</tr>
HTML
end.join("\n")
<<~HTML
## Slots
<div class="table-container">
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
#{tbody}
</tbody>
</table>
</div>
HTML
end
def attributes_table(members)
return "" if members.nil? || members.empty?
tbody = members.map do |member|
type_text = ""
type_text = member.type.text if member.type && member.type.text
<<~HTML
<tr>
<td>
<% if "#{member.attribute}" != "#{member.name}" %>
<small>[Attribute]</small>
<br>
<code>#{member.attribute}</code>
<br><br>
<small>[Property]</small>
<br>
<code>#{member.name}</code>
<% elsif not("#{member.attribute}".blank?) %>
<small>[Attribute + Property]</small>
<br>
<code>#{member.attribute}</code>
<% else %>
<small>[Property Only]</small>
<br>
<code>#{member.name}</code>
<% end %>
</td>
<td>
#{member.description.to_s.empty? ? "-" : escape(member.description)}
</td>
<td>
#{member.reflects ? "<sl-icon name='check-lg'></sl-icon>" : "-"}
</td>
<td>
#{type_text.blank? ? "-" : "<code>#{escape(type_text)}</code>"}
</td>
<td>
#{member.default.nil? ? "-" : "<code>#{escape(member.default)}</code>"}
</td>
</tr>
HTML
end.join("\n")
<<~HTML
## Attributes
<div class="table-container">
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Reflects</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
#{tbody}
</tbody>
</table>
</div>
HTML
end
def events_table(events)
return "" if events.nil? || events.empty?
tbody = events.map do |event|
<<~HTML
<tr>
<td>
#{event.name}
</td>
<td>
</td>
</tr>
HTML
end
<<~HTML
## Events
<div class="table-container">
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
#{tbody}
</tbody>
</table>
</div>
HTML
end
def parameter_string(parameter)
rest = parameter.rest ? "..." : ""
# optional = parameter.optional ? "?" : ""
type_text = ""
type_text = ": " + parameter.type.text if parameter.type && parameter.type.text
rest + parameter.name + type_text
end
def functions_table(functions)
return "" if functions.nil? || functions.empty?
tbody = functions.map do |function|
parameters = "-"
if not(function.parameters.to_s.empty?)
parameters = "<code>" + escape(function.parameters.map { |parameter| parameter_string(parameter) }.join(", ")) + "</code>"
end
<<~HTML
<tr>
<td>
<code>#{function.name}()</code>
</td>
<td>
#{function.description.to_s.empty? ? "-" : escape(function.description.to_s)}
</td>
<td>
#{parameters}
</td>
</tr>
HTML
end.join("\n")
<<~HTML
## Functions
<div class="table-container">
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Parameters</th>
</tr>
</thead>
<tbody>
#{tbody}
</tbody>
</table>
</div>
HTML
end
def parts_table(parts)
return "" if parts.nil? || parts.empty?
tbody = parts.map do |part|
<<~HTML
<tr>
<td>
<code>#{part.name}</code>
</td>
<td>
#{part.description.to_s.empty? ? "-" : escape(part.description)}
</td>
</tr>
HTML
end.join("\n")
<<~HTML
## Parts
<div class="table-container">
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
#{tbody}
</tbody>
</table>
</div>
HTML
end
end

View File

@@ -0,0 +1,138 @@
require 'json'
require "nokogiri"
require "asset_mapper"
class Builders::Helpers < SiteBuilder
def build
# Create an AssetMapper::Configuration instance
@asset_mapper = AssetMapper.new.configure do |config|
# Where the manifest files can be found on the host machine
config.manifest_files = [".bridgetown-cache/asset-mapper-manifest.json"]
# The URL or path prefix for the files.
config.asset_host = ENV["BASE_PATH"] + "/bridgetown/static"
# Do not cache the manifest in testing or in development.
config.cache_manifest = false
end
helper "find_asset", :find_asset
helper "find_asset_file_path", :find_asset_file_path
helper "version_number", :version_number
helper "render_svg", :render_svg
# All pages in "_documentation"
helper "doc_collection", :doc_collection
# Categories are top level in "_documentation"
# "_documentation/how_tos", "how_tos" is a category
helper "ordered_categories", :ordered_categories
helper "ordered_categories_hash_map", :ordered_categories_hash_map
helper "docs_without_indexes", :docs_without_indexes
helper "docs_in_category", :docs_in_category
helper "indexed_docs_by_category", :indexed_docs_by_category
helper "on_github", :on_github
helper "next_page_in_category", :next_page_in_category
helper "previous_page_in_category", :previous_page_in_category
helper "current_page_in_category_index", :current_page_in_category_index
end
def find_asset(file_name)
@asset_mapper.manifest.find(file_name)
end
def find_asset_file_path(file_name)
@asset_mapper.manifest.manifest_files[file_name]["file_path"]
end
def render_svg(filename, options = {})
image = site.static_files.find { |f| f.relative_path == "/#{filename}.svg" }
return unless image
file = File.read(image.path)
doc = ::Nokogiri::HTML::DocumentFragment.parse file
svg = doc.at_css 'svg'
options.each { |attr, value| svg[attr.to_s] = value }
doc.to_html.html_safe
end
def doc_collection
site.collections[:documentation].resources
end
def ordered_categories
doc_collection.uniq { |c| c.data[:category] }.sort_by { |c| c.data[:category_order] }
end
def ordered_categories_hash_map
ordered_categories.map do |c|
{
category: c.data[:category],
url: site.config.base_path.to_s + "/" + ::Bridgetown::Utils.slugify(c.data[:category]),
text: c.data[:category].split("_").map(&:capitalize).join(" ")
}
end
end
def docs_without_indexes
doc_collection.reject { |doc| doc.data.layout == "index" }
end
def docs_in_category(category)
docs_without_indexes.select { |doc| doc.data.categories.include?(category) }
end
def indexed_docs_by_category(category)
docs_in_category(category).sort_by { |doc| doc.data.slug }
end
def on_github(resource)
branch = ENV["GITHUB_REF_NAME"] || site.metadata.default_branch
site.metadata.github_url + "/tree/#{branch}/#{site.metadata.doc_location}/#{resource.relative_path}"
end
def version_number
is_ci = ENV["GITHUB_REF_NAME"] != ""
if is_ci && ENV["GITHUB_REF_NAME"] == "main"
return "main"
end
if !is_ci && `git rev-parse --abbrev-ref HEAD`.chomp == "main"
return "main"
end
package_json_file = File.join(File.expand_path("../../../", __dir__), "package.json")
return unless File.exist?(package_json_file)
package_json = File.read(package_json_file)
JSON.parse(package_json)["version"].to_s
end
def next_page_in_category(resource, category = resource.data[:category])
current_page_index = current_page_in_category_index(resource)
return nil if current_page_index.nil?
indexed_docs_by_category(category)[current_page_index + 1]
end
def previous_page_in_category(resource, category = resource.data[:category])
current_page_index = current_page_in_category_index(resource)
return nil if current_page_index.nil? || current_page_index == 0
indexed_docs_by_category(category)[current_page_index - 1]
end
def current_page_in_category_index(resource, category = resource.data[:category])
indexed_docs_by_category(category).index(resource)
end
end

View File

@@ -0,0 +1,93 @@
class Builders::Inspectors < SiteBuilder
def build
inspect_html do |document|
grab_headers(document)
mark_external(document)
syntax_highlight(document)
end
end
def mark_external(document)
document.css("a[href^='http']").each do |anchor|
next unless anchor[:href]&.starts_with?("http") && !anchor[:href]&.include?(site.config.url)
anchor[:target] = "_blank"
anchor[:rel] = "nofollow noopener noreferrer"
next if anchor.css("external-icon")[0]
next if anchor[:"no-external-icon"]
anchor << " <external-icon></external-icon>"
end
end
def syntax_highlight(document)
document.css(":not(.syntax-block) > div.highlighter-rouge").each do |el|
text = el.inner_text
lang = el["class"].scan(/\s?language-.*\s/).last
lang = lang.strip.split("-")[1] if lang
lang = Syntax.full_language(lang)
id = SecureRandom.uuid
el.wrap("<div class='syntax-block'></div>")
actions = <<-HTML
<div class='syntax-block__actions'>
<div class='syntax-block__badge'>
#{lang}
</div>
<clipboard-copy
for='#{id}'
class='button clipboard clipboard--idle syntax-block__clipboard'
aria-label='Copy to clipboard'
data-controller='clipboard'
>
<sl-icon class='clipboard__icon--success' name='clipboard-check'></sl-icon>
<sl-icon class='clipboard__icon--idle' name='clipboard'></sl-icon>
</clipboard-copy>
<textarea id='#{id}' hidden>#{text}</textarea>
</div>
HTML
el.add_previous_sibling(actions)
end
end
def grab_headers(document)
table_of_contents = document.css(".doc #table-of-contents ol")[0]
mobile_menu = document.css(".side-nav--mobile .side-nav__menu")[0]
# This isn't great. but works for my case :shrug:
document.css("main").css("h2[id],h3[id],h4[id],h5[id],h6[id]").each do |heading|
text = heading.inner_text
unless heading.css("a")[0]
heading.content = ""
anchor = %(
<a href='##{heading[:id]}'>#{text}</a>
)
heading << anchor
end
side_anchor = %(
<a href='##{heading[:id]}' class='side-nav__link'>#{text}</a>
)
item = document.create_element("li", "", class: "side-nav__item")
item << side_anchor
table_of_contents << item
# we'll get here.
# list = document.create_element("ul", "", class: "side-nav__category-menu")
# list << item
# mobile_menu.before list
end
end
end

View File

@@ -0,0 +1,4 @@
class SiteBuilder < Bridgetown::Builder
# write builders which subclass SiteBuilder in plugins/builders
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
module.exports = {
plugins: {
'postcss-flexbugs-fixes': {},
'postcss-preset-env': {
autoprefixer: {
flexbox: 'no-2009'
},
stage: 3
}
}
}

View File

@@ -0,0 +1,32 @@
# Roda is a simple Rack-based framework with a flexible architecture based
# on the concept of a routing tree. Bridgetown uses it for its development
# server, but you can also run it in production for fast, dynamic applications.
#
# Learn more at: http://roda.jeremyevans.net
class RodaApp < Roda
# Add additional Roda configuration here if needed
plugin :bridgetown_server
# Uncomment to use Bridgetown SSR:
# plugin :bridgetown_ssr
# Uncomment to use file-based dynamic routing in your project (make sure you
# uncomment the gem dependency in your `Gemfile` as well):
# plugin :bridgetown_routes
# plugin :default_headers,
# {
# 'Content-Type'=>'text/html',
# 'Strict-Transport-Security'=>'max-age=16070400;',
# 'X-Content-Type-Options'=>'nosniff',
# 'X-Frame-Options'=>'deny',
# 'X-XSS-Protection'=>'1; mode=block',
# 'Access-Control-Allow-Origin'=>"*"
# }
#
route do |r|
# Load Roda routes in server/routes (and src/_routes via `bridgetown-routes`)
r.bridgetown
end
end

View File

@@ -0,0 +1,10 @@
# Rename this file to hello.rb to try it out
class Routes::Hello < Bridgetown::Rack::Routes
route do |r|
# route: GET /hello/:name
r.get "hello", String do |name|
{ hello: "friend #{name}" }
end
end
end

View File

@@ -0,0 +1,18 @@
---
permalink: /404.html
layout: default
exclude_from_search: true
---
<main style="margin: 0 auto; display: grid; place-content: center;">
<h1>404 Page Not Found</h1>
<p>
Huh...Strange. That page doesnt appear to exist. Must've gotten lost in the
<br>
Space Time Continuum...
</p>
<p>
<a href="/">There's no place like home...</a>
</p>
</main>

View File

@@ -0,0 +1,11 @@
---
permalink: /500.html
layout: default
exclude_from_search: true
---
<h1>500</h1>
<h2>Internal Server Error :(</h2>
<p>The requested page could not be delivered.</p>

View File

@@ -0,0 +1,5 @@
<sl-alert variant="<%= @type.to_s %>" open>
<sl-icon slot="icon" name="<%= @icon %>"></sl-icon>
<%= markdownify(title) %>
<%= markdownify(content) %>
</sl-alert>

View File

@@ -0,0 +1,24 @@
# @example
# <%= render Alert.new(type: "warning", title:) %>
class Alert < Bridgetown::Component
ICONS = {
primary: "info-circle",
success: "check2-circle",
info: "gear",
warning: "exclamation-triangle",
danger: "exclamation-octagon"
}.freeze
def initialize(type: :info, title: nil, icon: nil)
@type = type.to_sym
@title = title
@icon = icon || ICONS[@type]
end
def title
return if @title.nil?
"<strong>#{@title}</strong><br>".html_safe
end
end

View File

@@ -0,0 +1,3 @@
<%= render Alert.new(type: "warning", icon: "cone-striped") do %>
Coming Soon...
<% end %>

View File

@@ -0,0 +1,2 @@
class ComingSoon < Bridgetown::Component
end

View File

@@ -0,0 +1,5 @@
<% indexed_docs_by_category(@category).each do |doc| %>
<li class="<%= @list_classes %> <%= 'is-active' if doc == @resource %>">
<a class="<%= @link_classes %>" href="<%= doc.relative_url %>"><%= doc.data.title %></a>
</li>
<% end %>

View File

@@ -0,0 +1,12 @@
class DocLinks < Bridgetown::Component
def initialize(category:, resource:, link_classes: nil, list_classes: nil)
@category = category
@list_classes = list_classes
@link_classes = link_classes
@resource = resource
end
def collection
sorted_collection.select { |doc| doc.data[:categories].include?(@category) }
end
end

View File

@@ -0,0 +1,22 @@
<div class="syntax-block">
<div class="syntax-block__actions">
<div class="syntax-block__badge">
<%= filename_or_language %>
</div>
<clipboard-copy
for="<%= id %>"
class="button clipboard clipboard--idle syntax-block__clipboard"
aria-label="Copy to clipboard"
data-controller="clipboard"
>
<sl-icon class="clipboard__icon--success" name="clipboard-check"></sl-icon>
<sl-icon class="clipboard__icon--idle" name="clipboard"></sl-icon>
</clipboard-copy>
<textarea id="<%= id %>" hidden><%= content %></textarea>
</div>
<%= markdownify("```#{language}" + content + "```") %>
</div>

View File

@@ -0,0 +1,30 @@
require "securerandom"
class Syntax < Bridgetown::Component
def self.full_language(language)
return "" if language.nil?
Rouge::Lexer.find(language).title || language.titleize
end
attr_accessor :language, :filename
def initialize(language = "markup", filename = nil)
super()
@language = language
@filename = filename
end
def filename_or_language
filename || full_language
end
def full_language
Syntax.full_language(@language)
end
def id
@id ||= "syntax-#{SecureRandom.uuid}"
end
end

View File

@@ -0,0 +1,15 @@
# Site settings
# These are used to personalize your new site. If you look in the HTML files,
# you will see them accessed via {{ site.metadata.title }}, {{ site.metadata.email }}, and so on.
# You can create any custom variable you would like, and they will be accessible
# in the templates via {{ site.metadata.myvariable }}.
title: Lightweight Components for doing complex things.
logo: "Light Pen"
slogan: Lightweight components for doing complex things.
email: konnor5456@gmail.com
description: >- # this means to ignore newlines until "baseurl:"
A collection of components that don't generally fit a design system, but that I wanted to build.
github_url: https://github.com/konnorrogers/light-pen
default_branch: main
doc_location: docs/src

View File

@@ -0,0 +1,5 @@
---
title: Components
permalink: /components/
layout: index
---

View File

@@ -0,0 +1,48 @@
---
title: <light-pen>
permalink: /components/light-pen/
component: light-pen
---
## Typical Usage
<light-pen style="padding: 8px; height: 100%;" resize-position="30" open-languages="js,html,css">
<template slot="html">
<light-pen>
<template slot="html">
<div>
This is kind of wild huh? We have a <code>&lt;light-pen&gt;</code>
component running inside of a <code>&lt;light-pen&gt;</code> component!!
</div>
</template>
<template slot="css">
code {
padding: 2px 6px;
display: inline-block;
font-size: 0.9em;
background-color: rgba(0,0,0,0.05);
border-radius: 4px;
}
</template>
</light-pen>
</template>
<template slot="css">
html, body {
height: 100%;
min-height: 100%;
width: 100%;
margin: 0;
}
light-pen {
height: 100%;
}
</template>
<template slot="js">
import LightPen from "<%= find_asset "../exports/light-pen.js" %>";
LightPen.define()
</template>
</light-pen>

View File

@@ -0,0 +1,194 @@
---
title: <light-preview>
permalink: /components/light-preview/
---
## Typical Usage
<light-preview>
<template slot="preview-code">
<button>Displaying a super cool button</button>
</template>
<template slot="code">
<style>
light-preview::part(iframe) {
height: 80px;
}
</style>
<light-preview>
<template slot="code">
<button>Displaying a super cool button</button>
</template>
</light-preview>
<script type="module">
import LightPreview from "./exports/light-preview.js";
LightPreview.define()
</script>
</template>
</light-preview>
## Nesting a light-pen inside of a light-pen
<light-preview>
<template slot="code">
<style>
light-preview::part(iframe) {
height: 80px;
}
</style>
<light-preview>
<template slot="code">
<button>Displaying a super cool button</button>
</template>
</light-preview>
<script type="module">
import LightPreview from "light-pen/exports/light-preview.js"
LightPreview.define()
</script>
</template>
<template slot="preview-code">
<style>
light-preview::part(iframe) {
height: 80px;
}
</style>
<light-preview>
<template slot="code">
<button>Displaying a super cool button</button>
</template>
</light-preview>
<script type="module">
import LightPreview from "<%= find_asset "../exports/light-preview.js" %>"
LightPreview.define()
</script>
</template>
</light-preview>
## Loading inline
<light-preview inline-preview>
<template slot="code">
<light-preview inline-preview>
<template slot="code">
<style>
button.button {
border-color: gray;
}
</style>
<button class="button">Displaying a super cool button not inside an iframe</button>
<br>
<p>
Notice how the button has the same styling as the toggle! That's because its not scoped into an iFrame!
</p>
</template>
</light-preview>
<script type="module">
import LightPreview from "<%= find_asset "../exports/light-preview.js" %>"
LightPreview.define()
</script>
</template>
</light-preview>
## Additional buttons
<style>
#additional-buttons button {
appearance: none;
-webkit-appearance: none;
background-color: inherit;
padding: 0.4em 0.6em;
color: inherit;
border: 1px solid lightgray;
border-bottom-color: transparent;
border-right-color: transparent;
height: 100%;
border-radius: 0;
}
#additional-buttons button:focus-visible {
outline: 2px solid hsl(203, 77%, 43%);
}
@supports not selector(:focus-visible) {
#additional-buttons button:focus-within {
outline: 2px solid hsl(203, 77%, 43%);
}
}
#additional-buttons button:hover {
cursor: pointer;
color: hsla(203, 77%, 43%);
background-color: hsla(203, 77%, 43%, 0.1);
}
</style>
<light-preview id="additional-buttons">
<template slot="code">
<style>
#additional-buttons button {
appearance: none;
-webkit-appearance: none;
background-color: inherit;
padding: 0.4em 0.6em;
color: inherit;
border: 1px solid lightgray;
border-bottom-color: transparent;
border-right-color: transparent;
height: 100%;
border-radius: 0;
}
#additional-buttons button:focus-visible {
outline: 2px solid hsl(203, 77%, 43%);
}
@supports not selector(:focus-visible) {
#additional-buttons button:focus-within {
outline: 2px solid hsl(203, 77%, 43%);
}
}
#additional-buttons button:hover {
cursor: pointer;
color: hsla(203, 77%, 43%);
background-color: hsla(203, 77%, 43%, 0.1);
}
light-preview::part(iframe) {
height: 80px;
}
</style>
<light-preview id="additional-buttons">
<template slot="code">
<div>Showing off more buttons</div>
</template>
<button slot="actions">HTML</button>
<button slot="actions">Codepen</button>
</light-preview>
<script type="module">
import LightPreview from "<%= find_asset "../exports/light-preview.js" %>"
LightPreview.define()
</script>
</template>
<button slot="actions">HTML</button>
<button slot="actions">Codepen</button>
</light-preview>

View File

@@ -0,0 +1,5 @@
---
title: Guides
permalink: /guides/
layout: index
---

View File

@@ -0,0 +1,5 @@
---
title: References
permalink: /references/
layout: index
---

View File

@@ -0,0 +1,19 @@
<!doctype html>
<html class="sl-theme-light" lang="<%= site.locale %>">
<head>
<%= render "head", metadata: site.metadata, title: resource.data.title %>
</head>
<body class='<%= "#{resource.data.layout} #{resource.data.page_class}" %>' data-controller="search">
<kr-layout main-id="main">
<%= render "top_nav", metadata: site.metadata %>
<%= yield %>
<%= render "footer", metadata: site.metadata %>
</kr-layout>
<%= render "side_nav_mobile" %>
<bridgetown-ninja-keys endpoint="<%= site.base_path %>/bridgetown_quick_search/index.json"></bridgetown-ninja-keys>
</body>
</html>

View File

@@ -0,0 +1,19 @@
---
layout: default
---
<%= render "side_nav" %>
<main id="main" class="documentation-content">
<% title_id = resource.data.title.downcase.split(" ").join("-").gsub(/[\?!]/, "") %>
<h1 id="<%= title_id %>">
<a href="<%= "##{title_id}" %>">
<%= resource.data.title.titleize %>
</a>
</h1>
<%= yield %>
<%= render "pagination" %>
</main>
<%= render "table_of_contents" %>

Some files were not shown because too many files have changed in this diff Show More