Start rewriting with Phlexite
This commit is contained in:
parent
e063e315ed
commit
f464d585cc
30 changed files with 486 additions and 921 deletions
48
.github/workflows/deploy.yml
vendored
48
.github/workflows/deploy.yml
vendored
|
@ -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
1
.gitignore
vendored
|
@ -1,2 +1 @@
|
|||
cheetah.toml
|
||||
_build/
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal 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
|
20
.idea/biocircuits.iml
generated
Normal file
20
.idea/biocircuits.iml
generated
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?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="markly (v0.12.1, 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="phlex-markdown (v0.3.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
4
.idea/misc.xml
generated
Normal 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
8
.idea/modules.xml
generated
Normal 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
6
.idea/vcs.xml
generated
Normal 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
8
Gemfile
Normal 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 "phlex-markdown", "~> 0.3.0"
|
20
Gemfile.lock
Normal file
20
Gemfile.lock
Normal file
|
@ -0,0 +1,20 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
markly (0.12.1)
|
||||
phlex (1.11.0)
|
||||
phlex-markdown (0.3.0)
|
||||
markly (~> 0.7)
|
||||
phlex (>= 0.5)
|
||||
phlexite (0.1.3)
|
||||
|
||||
PLATFORMS
|
||||
x64-mingw-ucrt
|
||||
|
||||
DEPENDENCIES
|
||||
phlex (~> 1.11)
|
||||
phlex-markdown (~> 0.3.0)
|
||||
phlexite (~> 0.1.3)
|
||||
|
||||
BUNDLED WITH
|
||||
2.5.22
|
|
@ -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);
|
||||
|
||||
|
|
141
assets/site.css
141
assets/site.css
|
@ -172,4 +172,143 @@ i.cite {
|
|||
margin: 10px;
|
||||
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> */
|
16
build.rb
Normal file
16
build.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'phlex'
|
||||
require 'phlexite'
|
||||
|
||||
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'
|
||||
|
||||
Phlexite::Site.new do |s|
|
||||
s.mount 'assets', on: '/assets'
|
||||
|
||||
s.page 'index.html', BC::Views::Pages::Index.new
|
||||
end
|
|
@ -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>
|
|
@ -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
245
flake.lock
generated
|
@ -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
|
||||
}
|
19
flake.nix
19
flake.nix
|
@ -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; });
|
||||
});
|
||||
}
|
|
@ -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>© 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>
|
|
@ -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>
|
|
@ -1,34 +0,0 @@
|
|||
<extends template="layouts/page.html" back="/hill-functions.html" title="Choosing Between Activators & Repressors"></extends>
|
||||
|
||||
# Choosing Between Activators & Repressors
|
||||
|
||||
We now have two ways of regulating gene expression, and they seem to be able to do the same things. Which should we use?
|
||||
|
||||

|
||||
<i class="cite">Credit: CalTech</i>
|
||||
|
||||
Michael Savageau researched this in the context of bacterial metabolic genes and digestive enzyme production in 1977.[^1] He found that genes in high demand were usually regulated by activators, while those in low demand were generally regulated by repressors.
|
||||
|
||||

|
||||
<i class="cite">Credit: CalTech</i>
|
||||
|
||||
He hypothesized the "use it or lose it" rule: essentially, the somewhat counterintuitive behavior was due to selection pressure that would eliminate the regulators if they were not used enough.
|
||||
|
||||
There have been other explanations, too:
|
||||
|
||||
- Shinar, et al. found the same result in 2006, but suggested a slightly more intuitive explanation.[^2] They suggested that proteins do not only bind to one receptor, but can bind to a range of similar receptors, even if a protein could cause problems in some of them. Since unoccupied receptors are more susceptible to binding errors, it's preferable to keep them occupied with the right protein than to take the risk of the wrong protein accidentally binding.
|
||||
- Gerland and Hwa found the same results as Savageau in 2009, but restricted the scope of his reasoning.[^3] They suggested that, for small populations with long timescales (slow evolution), Savageau's reasoning was correct. In large populations with short timescales (fast evolution), though, they suggested that the opposite, more intuitive, option could occur: regulators could be used for the higher-demand proteins and activators for the lower-demand proteins, in the interest of reducing "wear and tear".
|
||||
|
||||
At this point, we're pretty sure that, in the majority of cases, regulators are used for lower-demand proteins, and activators are used for higher-demand proteins, as Savageau suggested. As shown by the varying ideas above, though, we still don't know why.
|
||||
|
||||
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), 5647–5651. <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), 3999–4004. <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), 8841–8846. <https://doi.org/10.1073/pnas.0808500106>
|
||||
|
||||
</section>
|
|
@ -1,44 +0,0 @@
|
|||
<extends template="layouts/page.html" back="/repressors.html" next="/hill-functions.html" title="Activators"></extends>
|
||||
|
||||
# Activators
|
||||
|
||||
Just like repressors can inhibit a gene, **activators** can enable it. Just like repressors, activators' behavior can also be changed by small molecule inputs. Another example from our bacterial friends is the LuxR activator, which only acts as an activator in the presence of the compound ligand.
|
||||
|
||||
Here's a diagram, with the activator labeled A:
|
||||
|
||||

|
||||
<i class="cite">Credit: CalTech</i>
|
||||
|
||||
For activators, we have a different curve (seen below in red, contrasted to the repressor curve in blue):
|
||||
|
||||
$$\beta(a)=\beta_0\frac{p_\text{bound}}{p_\text{tot}}=\beta_0\frac{a/K_d}{1 + a/K_d}$$
|
||||
|
||||
<div class="graph">
|
||||
<div id="binding-curve"></div>
|
||||
<div>
|
||||
<label for="kd">K<sub>d</sub</label>
|
||||
<input type="range" id="kd" value=1>
|
||||
</div>
|
||||
<div>
|
||||
<label for="beta0">β<sub>0</sub></label>
|
||||
<input type="range" id="beta0" value=1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
As you can see, an activator has exactly the opposite effect as a repressor.
|
||||
|
||||
That was a short section. Onward!
|
||||
|
||||
<script>
|
||||
plot('#binding-curve', (kd, beta0) => [`${beta0}/(1 + x/(${kd}))`, `${beta0} * ((x/${kd})/(1 + (x/${kd})))`], ['#kd', '#beta0'], [[0, 10], [0, 10]])
|
||||
|
||||
defineVars([
|
||||
['P', 'The promoter of the DNA.'],
|
||||
['A', 'The activator.'],
|
||||
['p', 'A concentration of promoters, either unbound, bound (pbound), or total (ptot).'],
|
||||
['x', 'The gene in question.'],
|
||||
['K', 'Kd is the dissociation constant, a measure of the likelihood that the activator will unbind.'],
|
||||
['β', 'β(x) is the simple binding curve. β0 is the maximum expression level.'],
|
||||
['a', 'The concentration of activator.']
|
||||
])
|
||||
</script>
|
|
@ -1,26 +0,0 @@
|
|||
<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.
|
||||
|
||||
- We often don't understand natural circuits, and even synthetic biocircuits often use components (such as certain proteins) that we don't fully understand.
|
||||
- Synthetic and natural circuits often use different design principles. In electronic circuits, for instance, interference is generally avoided, and a circuit is often designed to follow a relatively simple and traceable path; in natural circuits, by contrast, "crosstalk" and complex networks between components are common, and are used to do things that electronic circuits can't.
|
||||
- There's a lot of noise and variation in biological processes. As mentioned above, some natural circuits take great advantage of this.
|
||||
- Electrical systems use positive and negative voltages and currents, which allow for positive and negative effects. Biocircuits are built out of molecules whose concentrations cannot be negative, so they have to use other ways of inverting effects.
|
||||
- From a practical standpoint, even with technologies like CRISPR, we have a very limited ability to construct, test, and compare designs of biological circuits. This is improving rapidly, though!
|
||||
|
||||
Because of these differences and challenges, biological circuit design is done using **phenomenological modeling**: modeling relationships at a high level, independent of underlying molecular details.
|
||||
|
||||
At a high level, biological circuit design can be approached the same way as electronic circuit design, but with a different set of components: instead of wires, resistors, transistors, and the like, biocircuits use DNA, RNA, and proteins.
|
||||
|
||||
Again, though, even though we know a lot about biocircuit design, there are still a lot of fundamental things that we don't know, such as:
|
||||
|
||||
- What does each circuit do for the cell? (functions, design principles)
|
||||
- What parts of the circuit do what? (mechanism)
|
||||
- How can we control cells in predictable ways using these circuits? (biomedical applications)
|
||||
- How can we design circuits for predictable behaviors in living cells? (synthetic biology & bioengineering)
|
||||
|
||||
In theory, natural and synthetic circuits _should_ share a common set of design principles. These principles are generally expressed as a statement: _Circuit feature X enables function Y_. We know a few already, but new ones are still being discovered.
|
||||
|
||||
That's the introduction. Onward - let's design a circuit!
|
|
@ -1,70 +0,0 @@
|
|||
<extends template="layouts/page.html" back="/activators.html" next="/activators-vs-repressors.html" title="Ultrasensitivity & the Hill Function"></extends>
|
||||
|
||||
# Ultrasensitivity & the Hill Function
|
||||
|
||||
The models we've been using are all well and good to capture the general idea of the effects of activators and repressors, but in the real world, many responses respond in a more switch-like, or **ultrasensitive**, way. This can come from many different things; for instance, binding a protein at one site might increase the affinity for that protein at an adjacent site, or you could have a protein with an alternative shape that could be stabilized by binding agonist effector molecules (basically, molecules that change its shape) into a shape that has a higher affinity for those same molecules. What you generally see in an ultrasensitive response is that an increasing concentration has a little effect for a while, and then suddenly a large effect.
|
||||
|
||||
The **Hill function** is a phenomenological way of analyzing ultrasensitive systems. There are two versions of it, the activating Hill function (for analyzing activators, shown in blue):
|
||||
|
||||
$$f_\text{act}(x)=\frac{x^n}{k^n + x^n}=\frac{(x/k)^n}{1 + (x/k)^n}$$
|
||||
|
||||
And the repressive Hill function (for analyzing repressors, shown in red):
|
||||
|
||||
$$f_\text{rep}(x)=\frac{k^n}{k^n + x^n}=\frac{1}{1 + (x/k)^n}$$
|
||||
|
||||
This function has two parameters:
|
||||
|
||||
- $k$ is the concentration at which the function attains half of its maximum value. This is called the **Hill activation constant**.
|
||||
- $n$ is the **Hill coefficient**. This is a way of parameterizing how ultrasensitive the response is. When $n=1$, the Hill function is identical to the simple binding curves. As $n$ increases, the function becomes sharper and more ultrasensitive. At the limit when $n=\infty$, the Hill function is a perfect step function.
|
||||
|
||||
<div class="graph">
|
||||
<div id="hill-graph"></div>
|
||||
<div>
|
||||
<label for="k">k</label>
|
||||
<input type="range" id="k" value=1>
|
||||
</div>
|
||||
<div>
|
||||
<label for="n">n</label>
|
||||
<input type="range" id="n" value=1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
To find production rates with the Hill function, just multiply by $\beta_0$:
|
||||
|
||||
- Activator (shown in blue): $\beta(a)=\beta_0f_\text{act}(a)=\beta_0\frac{(a/k)^n}{1 + (a/k)^n}$
|
||||
- Repressor (shown in red): $\beta(r)=\beta_0f_\text{rep}(r)=\beta_0\frac{1}{1 + (r/k)^n}$
|
||||
|
||||
<div class="graph">
|
||||
<div id="hill-graph-prod"></div>
|
||||
<div>
|
||||
<label for="k-prod">k</label>
|
||||
<input type="range" id="k-prod" value=1>
|
||||
</div>
|
||||
<div>
|
||||
<label for="n-prod">n</label>
|
||||
<input type="range" id="n-prod" value=1>
|
||||
</div>
|
||||
<div>
|
||||
<label for="b0">β<sub>0</sub></label>
|
||||
<input type="range" id="b0" value=1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Onwards!
|
||||
|
||||
<script>
|
||||
plot('#hill-graph', (k, n) => [`((x/${k})^(${n}))/(1 + ((x/${k})^(${n})))`, `1/(1 + ((x/${k})^(${n})))`], ['#k', '#n'], [[0, 10], [0, 1]])
|
||||
plot('#hill-graph-prod', (k, n, b0) => [`(${b0}) * ((x/${k})^(${n}))/(1 + ((x/${k})^(${n})))`, `(${b0})/(1 + ((x/${k})^(${n})))`], ['#k-prod', '#n-prod', '#b0'], [[0, 10], [0, 10]])
|
||||
|
||||
defineVars([
|
||||
['f', 'The Hill function.'],
|
||||
['P', 'The promoter of the DNA.'],
|
||||
['A', 'The activator.'],
|
||||
['p', 'A concentration of promoters, either unbound, bound (pbound), or total (ptot).'],
|
||||
['k', 'The concentration at which the function attains half of its maximum value.'],
|
||||
['n', 'The Hill coefficient, a measurement of how ultrasensitive the response is.'],
|
||||
['β', 'β(x) is the simple binding curve. β0 is the maximum expression level.'],
|
||||
['r', 'The concentration of repressor.'],
|
||||
['a', 'The concentration of activator.']
|
||||
])
|
||||
</script>
|
121
pages/index.html
121
pages/index.html
|
@ -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>
|
|
@ -1,98 +0,0 @@
|
|||
<extends template="layouts/page.html" back="/simplest-circuit.html" next="/activators.html" title="Repressors & Leaks"></extends>
|
||||
|
||||
# Repressors & Leaks
|
||||
|
||||
Theoretically, a single gene could keep producing a single protein indefinitely, if it had the energy. From a circuit design perspective, though, that's not very interesting. In order to do something more interesting, we need some way to regulate production.
|
||||
|
||||
**Repressors** enable gene regulation. They bind to specific binding sites at or near the promoter of a gene (remember, that's where transcription starts) and inhibit transcription initiation. An example of this in bacteria is the Lacl repressor in _E. coli_. Normally, it inhibits the gene for lactase production. When lactose is present, though, a modified form of lactose (allolactose) binds to Lacl, and prevents it from inhibiting lactase production. Lactase is then produced, and the lactose is digested. Lacl is then free to bind to the gene again, inhibiting lactase production until the next time lactose appears.
|
||||
|
||||
Here's a diagram, with the repressor labeled R:
|
||||
|
||||

|
||||
<i class="cite">Credit: CalTech</i>
|
||||
|
||||
Binding and unbinding a repressor can be modeled through this chemical equation:
|
||||
|
||||
$$\ce{P + R <=>[k_+][k_-] P_\mathrm{bound}}$$
|
||||
|
||||
We can model the dynamics of this reaction using mass action kinetics: essentially, the rate of a reaction is proportional to the product of the concentrations of its products. Within a single cell, an individual site is either bound or unbound. Averaged over a population of cells, though, we can talk about the mean occupancy of a site. If \\(r\\) is the concentration of repressor, \\(p\\) is the concentration of unbound promoter, and \\(p_\text{bound}\\) is the concentration of bound promoter, we can say that:
|
||||
|
||||
$$\frac{dp}{dt}=-k_+pr+k_-p_\text{bound}$$
|
||||
|
||||
For this situation, we can assume a separation of timescales, because the rate at which the repressor binds to and unbinds from the DNA is generally very fast compared to the rates at which mRNA and protein concentrations vary. This is generally a reasonable assumption in bacteria, but be aware that in some situations, as in some genes in mammals, it might not work. Here, though, we can handily assume that the binding and unbinding is happening quickly enough that \\(\frac{dp}{dt}\approx0\\). To find the concentrations, then, we can set the above equal to zero and solve for whatever we want.
|
||||
|
||||
Specifically, if \\(p_\text{tot}\\) is the total concentration of promoters, both bound and unbound, then we can set \\(p_\text{bound}=p_\text{tot}-p\\) and rearrange the equation to give the fraction of free promoters:
|
||||
|
||||
$$\frac{p}{p_\text{tot}}=\frac1{(1+r/K_d)}$$
|
||||
|
||||
Where \\(K_d\\) is the **dissociation constant** — a measurement of the likelyhood that a repressor will unbind from its binding site — calculated as \\(\frac{k_-}{k_+}\\). Because we have a separation of timescales and can simplify things, the rate of production of gene product should be proportional to the probability of the promoter being unbound:
|
||||
|
||||
$$\beta(r)=\beta_0\frac{p}{p_\text{tot}}=\frac{\beta_0}{1 + r/K_d}$$
|
||||
|
||||
This is the **simple binding curve**. We'll be using and building on it over the course of the next few sections. It has three parameters:
|
||||
|
||||
- \\(K_d\\) is the concentration of repressor at which the production is reduced to half its maximum value.
|
||||
- \\(\beta_0\\) is the maximum expression level, acting as a coefficient for the rest of the function.
|
||||
- \\(r\\) is the concentration of repressor.
|
||||
|
||||
For small values of \\(r\\), note that the slope is \\(-\frac{\beta_0}{K_d}\\). Here's a graph to play around with (the line represents the initial slope):
|
||||
|
||||
<div class="graph">
|
||||
<div id="binding-curve"></div>
|
||||
<div>
|
||||
<label for="kd">K<sub>d</sub</label>
|
||||
<input type="range" id="kd" value=1>
|
||||
</div>
|
||||
<div>
|
||||
<label for="beta0">β<sub>0</sub></label>
|
||||
<input type="range" id="beta0" value=1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Leaky expression
|
||||
|
||||
Unfortunately, repressors aren't perfect. Many genes can never be entirely repressed, and have a baseline, or "basal", level or expression. We can represent this by adding another term, $\alpha_0$, to $\beta(r)$:
|
||||
|
||||
$$\beta(r)=\frac{\beta_0}{1 + r/K_d}+\alpha_0$$
|
||||
|
||||
<div class="graph">
|
||||
<div id="binding-curve-leaky"></div>
|
||||
<div>
|
||||
<label for="leaky-kd">K<sub>d</sub</label>
|
||||
<input type="range" id="leaky-kd" value=1>
|
||||
</div>
|
||||
<div>
|
||||
<label for="leaky-beta0">β<sub>0</sub></label>
|
||||
<input type="range" id="leaky-beta0" value=1>
|
||||
</div>
|
||||
<div>
|
||||
<label for="leaky-alpha0">α<sub>0</sub></label>
|
||||
<input type="range" id="leaky-alpha0" value=1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Why does this happen, you ask? There are a couple of reasons:
|
||||
|
||||
1. Molecular interactions inside a cell are always probabilistic, and repressors are constantly binding and unbinding.
|
||||
2. Even if there are more repressors than genes to repress, there is typically a small, but finite, amount of time between the unbinding and re-binding of a repressor. During this time, transcription initiation can still happen. Remember: repressors only bind to the promoter, so once transcription is initiated, protein synthesis can still happen, even if a repressor binds during the process.
|
||||
|
||||
Because leaks are so common, it's important to make sure that important parts of your circuits don't depend on the complete absence of leaky expression.
|
||||
|
||||
Onwards!
|
||||
|
||||
<script>
|
||||
plot('#binding-curve', (kd, beta0) => [`${beta0}/(1 + x/(${kd}))`, `-(${beta0})x/(${kd}) + ${beta0}`], ['#kd', '#beta0'], [[0, 10], [0, 10]])
|
||||
|
||||
plot('#binding-curve-leaky', (kd, beta0, alpha0) => [`${beta0}/(1 + x/(${kd})) + ${alpha0}`, `-(${beta0})x/(${kd}) + ${beta0} + ${alpha0}`], ['#leaky-kd', '#leaky-beta0', '#leaky-alpha0'], [[0, 10], [0, 10]])
|
||||
|
||||
defineVars([
|
||||
['P', 'The promoter of the DNA.'],
|
||||
['R', 'The repressor.'],
|
||||
['p', 'A concentration of promoters, either unbound, bound (pbound), or total (ptot).'],
|
||||
['x', 'The gene in question.'],
|
||||
['K', 'Kd is the dissociation constant, a measure of the likelihood that the repressor will unbind.'],
|
||||
['β', 'β(x) is the simple binding curve. β0 is the maximum expression level.'],
|
||||
['r', 'The concentration of repressor.'],
|
||||
['α', 'α0 is the baseline, or "basal", level of expression of the gene.']
|
||||
])
|
||||
</script>
|
|
@ -1,108 +0,0 @@
|
|||
<extends template="layouts/page.html" back="/concepts.html" next="/repressors.html" title="The Simplest Circuit"></extends>
|
||||
|
||||
# The Simplest Circuit
|
||||
|
||||
The key idea for this section is that **steady-state expression levels depend on protein production and removal rates**.
|
||||
|
||||
Let's break that down.
|
||||
|
||||
First, steady-state conditions mean that all inputs to the system are constant forever. That simplifies things quite a bit.
|
||||
|
||||
Expression levels are just the net amount of proteins produced.
|
||||
|
||||
So, how many proteins there are, with no variables, depends on how many proteins are being created and how many are being removed. Simple, right?
|
||||
|
||||
Now, the circuit. This circuit is the simplest possible circuit: a single gene — let's call it \\(x\\) — coding for a single protein \\(p\\) at a rate of \\(\beta\\) molecules per unit time.
|
||||
|
||||

|
||||
<i class="cite">Credit: CalTech</i>
|
||||
|
||||
However, in real life, proteins aren't just made forever; they're also reduced, through both _active degradation_ (being broken down) and _dilution_ (the cell getting bigger, which reduces the protein's _concentration_). That's represented above by the dashed circle. For simplicity, let's say that it's being reduced at a rate constant \\(\gamma\\) (that letter is a gamma, for anyone who wanted to know). Note that this is not just a rate — it's a _rate constant_, meaning that the actual rate is proportional to the number of molecules. More molecules, more reduction.
|
||||
|
||||
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} $$
|
||||
|
||||
Since we're getting into the math, a quick tip: if you (like me) forget what a variable does halfway through the page, just hover over it and it'll tell you what it does. Because of the way it's rendered, it might not work in some cases, but feel free to try it. Anyway, back to the show.
|
||||
|
||||
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.
|
||||
|
||||
Since I haven't used the interactive graphing functions I took the bandwidth to import yet, let's graph the general shape of the protein concentration function under the simplest possible conditions (play with the sliders!):
|
||||
|
||||
$$f(t)=xt=\frac{\beta t}{\gamma}$$
|
||||
|
||||
<div class="graph">
|
||||
<div id="concentration-graph"></div>
|
||||
<div>
|
||||
<label for="beta">β</label>
|
||||
<input type="range" id="beta">
|
||||
</div>
|
||||
<div>
|
||||
<label for="gamma">γ</label>
|
||||
<input type="range" id="gamma">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
It's a line — for now.
|
||||
|
||||
## Considering Transcription
|
||||
|
||||
Right now, we have protein synthesis as one process — no intermediate steps. In reality, it has two: transcription and translation. The mRNA made in transcription can _also_ be degraded and diluted, just like the proteins made in translation. Let's add another variable to represent mRNA — call it \\(m\\). This can be shown in a diagram:
|
||||
|
||||

|
||||
<i class="cite">Credit: CalTech</i>
|
||||
|
||||
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.
|
||||
|
||||
Here's a graph to play with:
|
||||
|
||||
<div class="graph">
|
||||
<div id="concentration-graph-2step"></div>
|
||||
<div>
|
||||
<label for="betap">β<sub>p</sub></label>
|
||||
<input type="range" id="betap">
|
||||
<label for="gammap">γ<sub>p</sub></label>
|
||||
<input type="range" id="gammap">
|
||||
</div>
|
||||
<div>
|
||||
<label for="betam">β<sub>m</sub></label>
|
||||
<input type="range" id="betam">
|
||||
<label for="gammam">γ<sub>m</sub></label>
|
||||
<input type="range" id="gammam">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
It's still a line. Onwards!
|
||||
|
||||
<script>
|
||||
plot('#concentration-graph', (beta, gamma) => [`(${beta}x)/${gamma}`], ['#beta', '#gamma'])
|
||||
plot('#concentration-graph-2step', (betap, gammap, betam, gammam) => [`((${betap})(${betam})x)/((${gammap})(${gammam}))`], ['#betap', '#gammap', '#betam', '#gammam'])
|
||||
defineVars([
|
||||
['γ', 'The rate constant for reduction of concentration.'],
|
||||
['β', 'The rate of production, in molecules per unit time.'],
|
||||
['x', 'The gene in question.'],
|
||||
['m', 'The mRNA in question.'],
|
||||
['p', 'The protein in question.'],
|
||||
['ss', 'steady-state']
|
||||
])
|
||||
</script>
|
56
views/base_layout.rb
Normal file
56
views/base_layout.rb
Normal 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
|
||||
span { "© 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
20
views/nav_links.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module BC
|
||||
module Views
|
||||
class NavLinks
|
||||
def initialize(back, fwd)
|
||||
@back = back
|
||||
@fwd = fwd
|
||||
end
|
||||
|
||||
def view_template
|
||||
nav class: 'nav-links' do
|
||||
@back ? a(href: @back) { "BACK" } : div
|
||||
a(href: "/") { "HOME" }
|
||||
@fwd ? a(href: @fwd) { "NEXT" } : div
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
28
views/page_layout.rb
Normal file
28
views/page_layout.rb
Normal 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
|
77
views/pages/index.rb
Normal file
77
views/pages/index.rb
Normal 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",
|
||||
"2. The Simplest Circuit" => "/simplest-circuit",
|
||||
"3. Repressors & Leaks" => "/repressors",
|
||||
"4. Activators" => "/activators",
|
||||
"5. Ultrasensitivity & the Hill Function" => "/hill-functions",
|
||||
"6. Choosing Between Activators & Repressors" => "/activators-vs-repressors"
|
||||
}
|
||||
|
||||
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 {
|
||||
"
|
||||
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
|
29
views/section_link.rb
Normal file
29
views/section_link.rb
Normal 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 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
|
Loading…
Add table
Add a link
Reference in a new issue