Merge pull request #1 from aleksrutins/phlexite

Start rewriting with Phlexite
This commit is contained in:
Aleks Rūtiņš 2024-12-25 15:18:25 -05:00 committed by GitHub
commit 403691351a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 534 additions and 549 deletions

View file

@ -1,39 +1,53 @@
name: Deploy site to Pages
name: Deploy site to GitHub Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: true
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Setup Ruby
uses: ruby/setup-ruby@v1.180.1
with:
bundler-cache: true
- name: Build Site
run: bundle exec ruby build.rb
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: _build/
# Deployment job
deploy:
environment:
name: github-pages
url: $!
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v3
- uses: DeterminateSystems/nix-installer-action@v9
- uses: DeterminateSystems/magic-nix-cache-action@v2
- uses: cachix/cachix-action@v15
with:
name: aleksrutins
- uses: actions/configure-pages@v2
- name: Build Site
run: 'nix build .'
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: './result'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
uses: actions/deploy-pages@v4

1
.gitignore vendored
View file

@ -1,2 +1 @@
cheetah.toml
_build/

8
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

18
.idea/biocircuits.iml generated Normal file
View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RUBY_MODULE" version="4">
<component name="ModuleRunConfigurationManager">
<shared />
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" scope="PROVIDED" name="bundler (v2.5.22, ruby-3.3.6-p108) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="phlex (v1.11.0, ruby-3.3.6-p108) [gem]" level="application" />
<orderEntry type="library" scope="PROVIDED" name="phlexite (v0.1.3, ruby-3.3.6-p108) [gem]" level="application" />
</component>
</module>

4
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="ruby-3.3.6-p108" project-jdk-type="RUBY_SDK" />
</project>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/biocircuits.iml" filepath="$PROJECT_DIR$/.idea/biocircuits.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

8
Gemfile Normal file
View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
source "https://rubygems.org"
# gem "rails"
gem "phlex", "~> 1.11"
gem "phlexite", "~> 0.1.3"
gem "kramdown", "~> 2.5"

22
Gemfile.lock Normal file
View file

@ -0,0 +1,22 @@
GEM
remote: https://rubygems.org/
specs:
kramdown (2.5.1)
rexml (>= 3.3.9)
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
phlex (1.11.0)
phlexite (0.1.3)
rexml (3.4.0)
PLATFORMS
x64-mingw-ucrt
DEPENDENCIES
kramdown (~> 2.5)
kramdown-parser-gfm (~> 1.1)
phlex (~> 1.11)
phlexite (~> 0.1.3)
BUNDLED WITH
2.5.22

View file

@ -1,3 +1,18 @@
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
],
// • rendering keys, e.g.:
throwOnError : false
});
});
function defineVars(vars) {
const data = new Map(vars);

View file

@ -173,3 +173,142 @@ i.cite {
border-top: 1px solid;
opacity: 0.8;
}
/* <editor-fold desc="component: NavLinks"> */
nav.nav-links {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.nav-links a {
display: block;
color: var(--color-theme-1);
text-decoration: none;
border-bottom: 1px solid transparent;
transition-property: border-color;
transition-duration: .5s;
}
.nav-links a:hover {
border-color: var(--color-theme-1);
}
/* </editor-fold> */
/* <editor-fold desc="component: SectionLink"> */
a.section-link {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10px;
margin-top: 10px;
margin-bottom: 10px;
text-decoration: none !important;
text-transform: uppercase;
color: inherit;
--bg-hover: black;
background-image: linear-gradient(135deg,
var(--bg-hover) 0%,
var(--bg-hover) 50%,
transparent 50%,
transparent 100%
);
background-size: 250%;
background-position: 90% 90%;
border: 2px solid;
transition-property: color, border-color, background-position, background-color, background-image;
transition-duration: .75s;
}
a.section-link span, a.section-link svg {
display: block;
}
a.section-link:hover {
color: white;
background-position: 0% 0%;
border-color: black;
}
a.section-link:active {
--bg-hover: var(--color-theme-1);
border-color: var(--color-theme-1);
color: var(--color-theme-1);
background-position: -75% -75%;
}
/* </editor-fold> */
/* <editor-fold desc="page: Index"> */
@keyframes fade {
0% {
opacity: 0;
pointer-events: none;
}
100% {
opacity: 1;
pointer-events: all;
}
}
.fade {
opacity: 0;
pointer-events: none;
animation: fade .5s linear 0s 1 forwards normal;
}
.fade2 {
animation-delay: 2s;
}
.fade3 {
animation-delay: 4s;
}
.fade4 {
animation-delay: 4.25s;
}
.fade5 {
animation-delay: 4.5s;
}
.fade.no-delay {
animation-delay: 0s !important;
}
h1 {
width: 100%;
}
.welcome {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
position: relative;
width: 100%;
}
.welcome img {
display: block;
opacity: 0.7;
}
.tagline {
align-self: center;
font-style: italic;
margin-top: -10px;
}
.under-construction {
padding: 10px;
margin-top: 10px;
margin-bottom: 10px;
text-transform: uppercase;
color: inherit;
border: 2px solid;
opacity: 0.9;
text-align: center;
background: repeating-linear-gradient(
45deg,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0) 10px,
rgba(0, 0, 0, 0.1) 10px,
rgba(0, 0, 0, 0.1) 20px
);
}
/* </editor-fold> */

34
build.rb Normal file
View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
require 'phlex'
require 'phlexite'
require 'kramdown'
require_relative 'views/base_layout'
require_relative 'views/nav_links'
require_relative 'views/section_link'
require_relative 'views/page_layout'
require_relative 'views/pages/index'
require_relative 'views/pages/markdown_page'
def pages = [
['concepts', 'Concepts of Biocircuits'],
['simplest-circuit', "The Simplest Circuit"],
['repressors', 'Repressors & Leaks'],
['activators', 'Activators'],
['hill-functions', 'Ultrasensitivity & the Hill Function'],
['activators-vs-repressors', 'Choosing Between Activators & Repressors'],
]
Phlexite::Site.new do |s|
s.mount 'assets', on: '/assets'
s.page 'index.html', BC::Views::Pages::Index.new
pages.each_index { |idx|
page = pages[idx]
prev = idx <= 0 ? nil : "/#{pages[idx - 1][0]}.html"
fwd = idx >= pages.length - 1 ? nil : "/#{pages[idx + 1][0]}.html"
s.page "#{page[0]}.html", BC::Views::Pages::MarkdownPage.new(File.read("./views/pages/#{page[0]}.md"), page[1], prev, fwd)
}
end

View file

@ -1,29 +0,0 @@
<nav>
<a [href]="back">BACK</a>
<a href="/">HOME</a>
<a [href]="next">NEXT</a>
</nav>
<style>
nav {
display: flex;
flex-direction: row;
justify-content: space-between;
}
nav a {
display: block;
}
a {
color: var(--color-theme-1);
text-decoration: none;
border-bottom: 1px solid transparent;
transition-property: border-color;
transition-duration: .5s;
}
a:hover {
border-color: var(--color-theme-1);
}
</style>

View file

@ -1,48 +0,0 @@
<a [href]="href">
<span><slot></slot></span>
<svg xmlns="http://www.w3.org/2000/svg" height="24" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path font-weight="bold" stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
</svg>
</a>
<style>
a {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 10px;
margin-top: 10px;
margin-bottom: 10px;
text-decoration: none !important;
text-transform: uppercase;
color: inherit;
--bg-hover: black;
background-image: linear-gradient(135deg,
var(--bg-hover) 0%,
var(--bg-hover) 50%,
transparent 50%,
transparent 100%
);
background-size: 250%;
background-position: 90% 90%;
border: 2px solid;
transition-property: color, border-color, background-position, background-color, background-image;
transition-duration: .75s;
}
a span, a svg {
display: block;
}
a:hover {
color: white;
background-position: 0% 0%;
border-color: black;
}
a:active {
--bg-hover: var(--color-theme-1);
border-color: var(--color-theme-1);
color: var(--color-theme-1);
background-position: -75% -75%;
}
</style>

245
flake.lock generated
View file

@ -1,245 +0,0 @@
{
"nodes": {
"cheetah": {
"inputs": {
"naersk": "naersk",
"nixpkgs": "nixpkgs_2",
"packsnap": "packsnap",
"utils": "utils"
},
"locked": {
"lastModified": 1717535613,
"narHash": "sha256-nyppnrB7J1RaeqCaH6O22QsC/c+N0LTNXw3XgsPzxFg=",
"rev": "1b42da203e960f6cad517ad80e0e2dcf5625e884",
"revCount": 67,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/aleksrutins/cheetah/0.2.3/018fe51b-41d1-7c39-af34-e6ff00a655b2/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/aleksrutins/cheetah/0.2.3.tar.gz"
}
},
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1698420672,
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
"owner": "nix-community",
"repo": "naersk",
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "master",
"repo": "naersk",
"type": "github"
}
},
"naersk_2": {
"inputs": {
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1698420672,
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
"owner": "nix-community",
"repo": "naersk",
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1703499205,
"narHash": "sha256-lF9rK5mSUfIZJgZxC3ge40tp1gmyyOXZ+lRY3P8bfbg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1703499205,
"narHash": "sha256-lF9rK5mSUfIZJgZxC3ge40tp1gmyyOXZ+lRY3P8bfbg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1703499205,
"narHash": "sha256-lF9rK5mSUfIZJgZxC3ge40tp1gmyyOXZ+lRY3P8bfbg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e1fa12d4f6c6fe19ccb59cac54b5b3f25e160870",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1671548329,
"narHash": "sha256-OrC6R6zihRjFqdKFF3/vD3bkz44poONSII8ncre1Wh0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ba6ba2b90096dc49f448aa4d4d783b5081b1cc87",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"npmlock2nix": {
"flake": false,
"locked": {
"lastModified": 1673447413,
"narHash": "sha256-sJM82Sj8yfQYs9axEmGZ9Evzdv/kDcI9sddqJ45frrU=",
"owner": "nix-community",
"repo": "npmlock2nix",
"rev": "9197bbf397d76059a76310523d45df10d2e4ca81",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "npmlock2nix",
"type": "github"
}
},
"packsnap": {
"inputs": {
"flake-utils": "flake-utils",
"naersk": "naersk_2",
"nixpkgs": "nixpkgs_4",
"npmlock2nix": "npmlock2nix"
},
"locked": {
"lastModified": 1703773328,
"narHash": "sha256-0sbdKBuPr5UAl71jEPocp0OPhp6vuU4lqdJ8c+vPMqo=",
"owner": "aleksrutins",
"repo": "packsnap",
"rev": "08f30585d31d06f55656b1392c5f7d509900770e",
"type": "github"
},
"original": {
"owner": "aleksrutins",
"repo": "packsnap",
"type": "github"
}
},
"root": {
"inputs": {
"cheetah": "cheetah",
"utils": "utils_2"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,19 +0,0 @@
{
inputs = {
cheetah.url = "https://flakehub.com/f/aleksrutins/cheetah/0.2.3.tar.gz";
utils.url = "github:numtide/flake-utils";
};
outputs = { self, utils, cheetah }:
let config = {
always_hydrate = true;
};
in utils.lib.eachDefaultSystem (system: {
packages.default = (cheetah.buildSite.${system} ./. {
name = "site";
inherit config;
});
devShells.default = (cheetah.createDevShell.${system} { inherit config; });
});
}

View file

@ -1,49 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}} | Biocircuits for Mere Mortals</title>
<link rel="stylesheet" href="/assets/site.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css" integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js" integrity="sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/mhchem.min.js" integrity="sha384-ifpG+NlgMq0kvOSGqGQxW1mJKpjjMDmZdpKGq3tbvD3WPhyshCEEYClriK/wRVU0" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
// customised options
// • auto-render specific keys, e.g.:
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\(', right: '\\)', display: false},
{left: '\\[', right: '\\]', display: true}
],
// • rendering keys, e.g.:
throwOnError : false
});
});
</script>
<script src="/assets/math.js"></script>
<script src="https://unpkg.com/function-plot/dist/function-plot.js"></script>
</head>
<body>
<div class="app">
<main>
<slot></slot>
<footer>
<p>&copy; 2024 <a href="https://farthergate.com">Aleks Rūtiņš</a></p>
<p>Built with <a href="https://cheetah.farthergate.com">Cheetah</a>, <a href="https://mauriciopoppe.github.io/function-plot/">Function Plot</a>, and <a href="https://katex.org/">$\KaTeX$</a></p>
<p>
With the exception of pasted graphics, where the source is noted, this work is licensed under a <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution License CC BY-NC-SA 4.0</a>. All code contained herein is licensed under an <a href="https://opensource.org/license/mit">MIT license</a>.
</p>
</footer>
</main>
</div>
</body>
</html>

View file

@ -1,11 +0,0 @@
<extends template="layouts/base.html" [title]="title"></extends>
<p></p>
<p>
<nav-links [back]="back" [next]="next"></nav-links>
</p>
<slot></slot>
<p>
<nav-links [back]="back" [next]="next"></nav-links>
</p>

View file

@ -1,121 +0,0 @@
<extends template="layouts/base.html" title="Home"></extends>
<section>
<h1 class="welcome">
<img src="/assets/biocircuitslogo.svg" alt="Biological Circuits" />
</h1>
<p class="tagline">for mere mortals</p>
<p class="tagline">(albeit ones who know a bit of differential calculus)</p>
<p><strong>DISCLAIMER: This is a school project. I would like to think that the CalTech course I've drawn from has made me somewhat knowledgeable on this topic, but please do not use this for medical advice, etc., etc. If you happen to know what you are doing, and you find a mistake on this website, please <a href="https://github.com/aleksrutins/biocircuits/issues/new">tell me about it</a>. With that said:</a></strong></p>
<p class="fade">
Biological circuit design is the science of abstracting <em>natural</em> biological processes by defining them
as you would any synthetic circuit. It also allows us to create our own circuits out of natural components.
As <a href="https://biocircuits.github.io">CalTech's open-source Biocircuits course</a> states:
</p>
<blockquote class="fade fade2">
Indeed, the marvelous progression of electronic circuit capabilities [...] could well describe biological circuits decades from now. Like electronics, we may will soon be able to program cellular “miracle devices” to create “little gadgets” that address serious environmental and medical applications.
</blockquote>
<p class="fade fade3">That CalTech course will be the main source for this presentation. All diagrams are from it.</p>
<p class="fade fade4">Let's dive in!</p>
<div class="fade fade5">
<section-link href="/concepts.html">1. Concepts of Biocircuits</section-link>
<section-link href="/simplest-circuit.html">2. The Simplest Circuit</section-link>
<section-link href="/repressors.html">3. Repressors & Leaks</section-link>
<section-link href="/activators.html">4. Activators</section-link>
<section-link href="/hill-functions.html">5. Ultrasensitivity & the Hill Function</section-link>
<section-link href="/activators-vs-repressors.html">6. Choosing Between Activators & Repressors</section-link>
<div class="under-construction">
More Sections To Come
</div>
</div>
</section>
<script>
addEventListener('DOMContentLoaded', () => {
if(localStorage.getItem('faded'))
document.querySelectorAll('.fade').forEach(it =>
it.classList.add('no-delay'));
localStorage.setItem('faded', true);
});
</script>
<style>
@keyframes fade {
0% {
opacity: 0;
pointer-events: none;
}
100% {
opacity: 1;
pointer-events: all;
}
}
.fade {
opacity: 0;
pointer-events: none;
animation: fade .5s linear 0s 1 forwards normal;
}
.fade2 {
animation-delay: 2s;
}
.fade3 {
animation-delay: 4s;
}
.fade4 {
animation-delay: 4.25s;
}
.fade5 {
animation-delay: 4.5s;
}
.fade.no-delay {
animation-delay: 0s !important;
}
h1 {
width: 100%;
}
.welcome {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
position: relative;
width: 100%;
}
.welcome img {
display: block;
opacity: 0.7;
}
.tagline {
align-self: center;
font-style: italic;
margin-top: -10px;
}
.under-construction {
padding: 10px;
margin-top: 10px;
margin-bottom: 10px;
text-transform: uppercase;
color: inherit;
border: 2px solid;
opacity: 0.9;
text-align: center;
background: repeating-linear-gradient(
45deg,
rgba(0, 0, 0, 0),
rgba(0, 0, 0, 0) 10px,
rgba(0, 0, 0, 0.1) 10px,
rgba(0, 0, 0, 0.1) 20px
);
}
</style>

56
views/base_layout.rb Normal file
View file

@ -0,0 +1,56 @@
# frozen_string_literal: true
module BC
module Views
class BaseLayout < ::Phlex::HTML
def initialize(title)
@title = title
end
def view_template
doctype
html do
head do
meta charset: 'utf-8'
meta name: 'viewport', content: 'width=device-width, initial-scale=1.0'
title { @title + " | Biocircuits for Mere Mortals" }
link rel: 'stylesheet', href: '/assets/site.css'
link rel: "stylesheet", href: "https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css", integrity: "sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww", crossorigin: "anonymous"
script defer: true, src: "https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.js", integrity: "sha384-hIoBPJpTUs74ddyc4bFZSM1TVlQDA60VBbJS0oA934VSz82sBx1X7kSx2ATBDIyd", crossorigin: "anonymous"
script defer: true, src: "https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/mhchem.min.js", integrity: "sha384-ifpG+NlgMq0kvOSGqGQxW1mJKpjjMDmZdpKGq3tbvD3WPhyshCEEYClriK/wRVU0", crossorigin: "anonymous"
script defer: true, src: "https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/contrib/auto-render.min.js", integrity: "sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk", crossorigin: "anonymous"
script src: "/assets/math.js"
script src: "https://unpkg.com/function-plot/dist/function-plot.js"
end
body do
div class: "app" do
main do
yield
footer do
p do
plain "© 2024 "
a(href: "https://farthergate.com") { "Aleks Rūtiņš" }
end
unsafe_raw {
<<EOF
<p>Built with <a href="https://cheetah.farthergate.com">Cheetah</a>, <a href="https://mauriciopoppe.github.io/function-plot/">Function Plot</a>, and <a href="https://katex.org/">$\KaTeX$</a></p>
<p>
With the exception of pasted graphics, where the source is noted, this work is licensed under a <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution License CC BY-NC-SA 4.0</a>. All code contained herein is licensed under an <a href="https://opensource.org/license/mit">MIT license</a>.
</p>
EOF
}
end
end
end
end
end
end
end
end
end

20
views/nav_links.rb Normal file
View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
module BC
module Views
class NavLinks < ::Phlex::HTML
def initialize(back, fwd)
@back = back
@fwd = fwd
end
def view_template
nav class: 'nav-links' do
@back ? a(href: @back) { "BACK" } : div(style: "visibility: hidden") { "BACK" }
a(href: "/") { "HOME" }
@fwd ? a(href: @fwd) { "NEXT" } : div(style: "visibility: hidden") { "NEXT" }
end
end
end
end
end

28
views/page_layout.rb Normal file
View file

@ -0,0 +1,28 @@
# frozen_string_literal: true
module BC
module Views
class PageLayout < ::Phlex::HTML
def initialize(title, back, fwd)
@title = title
@back = back
@fwd = fwd
end
def view_template
render(BaseLayout.new(@title)) do
p
p do
render(NavLinks.new(@back, @fwd))
end
yield
p do
render(NavLinks.new(@back, @fwd))
end
end
end
end
end
end

View file

@ -23,12 +23,9 @@ At this point, we're pretty sure that, in the majority of cases, regulators are
You've reached the end, for now. I hope to expand this site in the future. If you have any questions, comments, or suggestions, please feel free to [tell me about them](https://github.com/aleksrutins/biocircuits/issues/new).
<section class="footnotes">
[^1] Savageau, M. A. (1977). Design of molecular control mechanisms and the demand for gene expression. Proceedings of the National Academy of Sciences, 74(12), 56475651. <https://doi.org/10.1073/pnas.74.12.5647>
[^1]: Savageau, M. A. (1977). Design of molecular control mechanisms and the demand for gene expression. Proceedings of the National Academy of Sciences, 74(12), 56475651. <https://doi.org/10.1073/pnas.74.12.5647>
[^2] Shinar, G., Dekel, E., Tlusty, T., & Alon, U. (2006). Rules for biological regulation based on error minimization. Proceedings of the National Academy of Sciences, 103(11), 39994004. <https://doi.org/10.1073/pnas.0506610103>
[^2]: Shinar, G., Dekel, E., Tlusty, T., & Alon, U. (2006). Rules for biological regulation based on error minimization. Proceedings of the National Academy of Sciences, 103(11), 39994004. <https://doi.org/10.1073/pnas.0506610103>
[^3] Gerland, U., & Hwa, T. (2009). Evolutionary selection between alternative modes of gene regulation. Proceedings of the National Academy of Sciences, 106(22), 88418846. <https://doi.org/10.1073/pnas.0808500106>
</section>
[^3]: Gerland, U., & Hwa, T. (2009). Evolutionary selection between alternative modes of gene regulation. Proceedings of the National Academy of Sciences, 106(22), 88418846. <https://doi.org/10.1073/pnas.0808500106>

View file

@ -1,5 +1,3 @@
<extends template="layouts/page.html" next="/simplest-circuit.html" title="Concepts of Biocircuits"></extends>
# Concepts of Biocircuits
Before we start designing circuits, we need to talk about **natural** (evolved) circuits and **synthetic** circuits.

77
views/pages/index.rb Normal file
View file

@ -0,0 +1,77 @@
# frozen_string_literal: true
module BC
module Views
module Pages
class Index < ::Phlex::HTML
def sections = {
"1. Concepts of Biocircuits" => "/concepts.html",
"2. The Simplest Circuit" => "/simplest-circuit.html",
"3. Repressors & Leaks" => "/repressors.html",
"4. Activators" => "/activators.html",
"5. Ultrasensitivity & the Hill Function" => "/hill-functions.html",
"6. Choosing Between Activators & Repressors" => "/activators-vs-repressors.html"
}
def view_template
render(::BC::Views::BaseLayout.new("Home")) do
section do
h1 class: "welcome" do
img src: "/assets/biocircuitslogo.svg", alt: "Biological Circuits"
end
p(class: "tagline") { "for mere mortals" }
p(class: "tagline") { "(albeit ones who know a bit of differential calculus)" }
p do
strong do
span { "DISCLAIMER: This is a school project. I would like to think that the CalTech course I've drawn from has made me somewhat knowledgeable on this topic, but please do not use this for medical advice, etc., etc. If you happen to know what you are doing, and you find a mistake on this website, please " }
a(href: "https://github.com/aleksrutins/biocircuits/issues/new") { "tell me about it." }
span { " With that said:" }
end
end
unsafe_raw(
<<EOF
<p class="fade">
Biological circuit design is the science of abstracting <em>natural</em> biological processes by defining them
as you would any synthetic circuit. It also allows us to create our own circuits out of natural components.
As <a href="https://biocircuits.github.io">CalTech's open-source Biocircuits course</a> states:
</p>
<blockquote class="fade fade2">
Indeed, the marvelous progression of electronic circuit capabilities [...] could well describe biological circuits decades from now. Like electronics, we may will soon be able to program cellular miracle devices to create little gadgets that address serious environmental and medical applications.
</blockquote>
<p class="fade fade3">That CalTech course will be the main source for this presentation. All diagrams are from it.</p>
<p class="fade fade4">Let's dive in!</p>
EOF
)
div class: "fade fade5" do
sections.each { |title, link|
render(SectionLink.new(link)) { title }
}
div class: "under-construction" do
"More Sections To Come"
end
end
script {
unsafe_raw("
addEventListener('DOMContentLoaded', () => {
if(localStorage.getItem('faded'))
document.querySelectorAll('.fade').forEach(it =>
it.classList.add('no-delay'));
localStorage.setItem('faded', true);
});
")
}
end
end
end
end
end
end
end

View file

@ -0,0 +1,20 @@
module BC
module Views
module Pages
class MarkdownPage < ::Phlex::HTML
def initialize(content, title, prev, fwd)
@content = content
@title = title
@prev = prev
@fwd = fwd
end
def view_template
render PageLayout.new(@title, @prev, @fwd) do
unsafe_raw Kramdown::Document.new(@content).to_html
end
end
end
end
end
end

View file

@ -21,8 +21,10 @@ However, in real life, proteins aren't just made forever; they're also reduced,
There's a differential equation for this:
$$ \frac{dx}{dt} = \text{production} - (\text{degradation} + \text{dilution}) $$
Or:
$$ \frac{dx}{dt} = \beta - \gamma x $$
Since \\(\gamma\\) counts both degradation and dilution, we can say that:
$$ \gamma = \gamma_\text{degradation} + \gamma_\text{dilution} $$
@ -31,8 +33,11 @@ Since we're getting into the math, a quick tip: if you (like me) forget what a v
To find the net production of the protein under steady state conditions, set the derivative to zero and solve for \\(x\\):
$$0 = \beta - \gamma x$$
$$-\beta = -\gamma x$$
$$\frac{-\beta}{-\gamma} = x$$
$$\frac\beta\gamma = x$$
And we find that **steady-state protein concentration is proportional to the ratio of production and removal rates**. This is another core concept that should be built as intuition.
@ -65,11 +70,13 @@ Right now, we have protein synthesis as one process &mdash; no intermediate step
The reaction can now be described by two coupled differential equations:
$$\frac{dm}{dt} = \beta_m - \gamma_mm$$
$$\frac{dx}{dt} = \beta_pm - \gamma_px$$
Now, to find steady-state mRNA and protein concentrations, we set both derivatives to zero and solve, giving us:
$$m_{ss}=\frac{\beta_m}{\gamma_m}$$
$$x_{ss}=\frac{\beta_pm_{ss}}{\gamma_p}=\frac{\beta_p\beta_m}{\gamma_p\gamma_m}$$
This tells us that steady-state protein concentration, when we consider transcription and translation as separate steps, is proportional to the product of the two synthesis rates and inversely proportional to the product of the two degradation rates. Again, if you think about it, that's pretty intuitive.

29
views/section_link.rb Normal file
View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
module BC
module Views
class ChevronRightIcon < Phlex::SVG
def view_template
svg xmlns: 'http://www.w3.org/2000/svg', height: 24, width: 24, fill: "none", viewBox: "0 0 24 24", stroke_width: 1.5, stroke: "currentColor", class: "size-6" do
path font_weight: "bold", stroke_linecap: "round", stroke_linejoin: "round", d: "m8.25 4.5 7.5 7.5-7.5 7.5"
end
end
end
class SectionLink < Phlex::HTML
def initialize(href)
@href = href
end
def view_template
a href: @href, class: "section-link" do
span do
yield
end
render ChevronRightIcon.new
end
end
end
end
end