diff --git a/.woodpecker.yml b/.woodpecker.yml
index e64cebe..7b97531 100644
--- a/.woodpecker.yml
+++ b/.woodpecker.yml
@@ -7,7 +7,7 @@ steps:
commands:
- corepack enable
- pnpm install
- - pnpm run build --no-cache
+ - pnpm run build
- rm -rf /var/woodpecker/thesandwich/*
- mkdir -p /var/woodpecker/thesandwich/
- mv dist/* /var/woodpecker/thesandwich/
diff --git a/assets/FiraCode-Bold.woff2 b/assets/FiraCode-Bold.woff2
new file mode 100644
index 0000000..349dc36
Binary files /dev/null and b/assets/FiraCode-Bold.woff2 differ
diff --git a/assets/FiraCode-Regular.woff2 b/assets/FiraCode-Regular.woff2
new file mode 100644
index 0000000..f8b63fb
Binary files /dev/null and b/assets/FiraCode-Regular.woff2 differ
diff --git a/package.json b/package.json
index f23e68c..3780e86 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
],
"scripts": {
"start": "parcel",
- "build": "parcel build"
+ "build": "pnpm run -r build; parcel build"
},
"devDependencies": {
"@parcel/compressor-brotli": "^2.15.4",
@@ -23,7 +23,8 @@
"posthtml-component": "github:StratusFearMe21/posthtml-components#reorder_processing",
"posthtml-include": "^2.0.1",
"posthtml-markdownit": "^3.1.2",
- "sharp": "^0.33.5"
+ "sharp": "^0.33.5",
+ "thecockpit": "link:./thecockpit/pkg"
},
"dependencies": {
"audiomotion-analyzer": "^4.5.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a3fe4a3..e09309c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -51,6 +51,15 @@ importers:
sharp:
specifier: ^0.33.5
version: 0.33.5
+ thecockpit:
+ specifier: link:./thecockpit/pkg
+ version: link:thecockpit/pkg
+
+ thecockpit:
+ devDependencies:
+ wasm-pack:
+ specifier: ^0.13.1
+ version: 0.13.1
packages:
@@ -704,12 +713,25 @@ packages:
audiomotion-analyzer@4.5.1:
resolution: {integrity: sha512-vEO5HgivsZa9ZhQDItSMjXD5+qsRu6rAEKzAmrg7HbOFeM4tPVrE/wxiRREWxzbGpuH9NZT4Ze+v95G1w/rRAA==}
+ axios@0.26.1:
+ resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
base-x@3.0.11:
resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==}
+ binary-install@1.1.0:
+ resolution: {integrity: sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==}
+ engines: {node: '>=10'}
+
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
@@ -729,6 +751,10 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
+ chownr@2.0.0:
+ resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+ engines: {node: '>=10'}
+
chrome-trace-event@1.0.4:
resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==}
engines: {node: '>=6.0'}
@@ -762,6 +788,9 @@ packages:
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
engines: {node: '>=18'}
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
css-declaration-sorter@7.2.0:
resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==}
engines: {node: ^14 || ^16 || >=18}
@@ -875,10 +904,30 @@ packages:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
+ follow-redirects@1.15.9:
+ resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ fs-minipass@2.1.0:
+ resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+ engines: {node: '>= 8'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
get-port@4.2.0:
resolution: {integrity: sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==}
engines: {node: '>=6'}
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
globals@13.24.0:
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
engines: {node: '>=8'}
@@ -896,6 +945,13 @@ packages:
htmx.org@2.0.6:
resolution: {integrity: sha512-7ythjYneGSk3yCHgtCnQeaoF+D+o7U2LF37WU3O0JYv3gTZSicdEFiI/Ai/NJyC5ZpYJWMpUb11OC5Lr6AfAqA==}
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
inline-style-parser@0.2.4:
resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==}
@@ -1030,6 +1086,26 @@ packages:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
engines: {node: '>=4'}
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minipass@3.3.6:
+ resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+ engines: {node: '>=8'}
+
+ minipass@5.0.0:
+ resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
+ engines: {node: '>=8'}
+
+ minizlib@2.1.2:
+ resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+ engines: {node: '>= 8'}
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
msgpackr-extract@3.0.3:
resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==}
hasBin: true
@@ -1065,6 +1141,9 @@ packages:
nullthrows@1.1.1:
resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
ordered-binary@1.6.0:
resolution: {integrity: sha512-IQh2aMfMIDbPjI/8a3Edr+PiOpcsB7yo8NdW7aHWVaoR/pcDldunMvnnwbk/auPGqmKeAdxtZl7MHX/QmPwhvQ==}
@@ -1073,6 +1152,10 @@ packages:
engines: {node: '>= 16.0.0'}
hasBin: true
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -1310,6 +1393,11 @@ packages:
regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+ rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
+ hasBin: true
+
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -1350,6 +1438,10 @@ packages:
engines: {node: '>=16'}
hasBin: true
+ tar@6.2.1:
+ resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
+ engines: {node: '>=10'}
+
term-size@2.2.1:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
engines: {node: '>=8'}
@@ -1381,9 +1473,19 @@ packages:
resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==}
engines: {node: '>= 4'}
+ wasm-pack@0.13.1:
+ resolution: {integrity: sha512-P9exD4YkjpDbw68xUhF3MDm/CC/3eTmmthyG5bHJ56kalxOTewOunxTke4SyF8MTXV6jUtNjXggPgrGmMtczGg==}
+ hasBin: true
+
weak-lru-cache@1.2.2:
resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==}
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ yallist@4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
snapshots:
'@emnapi/runtime@1.4.4':
@@ -2251,12 +2353,33 @@ snapshots:
audiomotion-analyzer@4.5.1: {}
+ axios@0.26.1:
+ dependencies:
+ follow-redirects: 1.15.9
+ transitivePeerDependencies:
+ - debug
+
+ balanced-match@1.0.2: {}
+
base-x@3.0.11:
dependencies:
safe-buffer: 5.2.1
+ binary-install@1.1.0:
+ dependencies:
+ axios: 0.26.1
+ rimraf: 3.0.2
+ tar: 6.2.1
+ transitivePeerDependencies:
+ - debug
+
boolbase@1.0.0: {}
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
braces@3.0.3:
dependencies:
fill-range: 7.1.1
@@ -2282,6 +2405,8 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
+ chownr@2.0.0: {}
+
chrome-trace-event@1.0.4: {}
clone@2.1.2: {}
@@ -2308,6 +2433,8 @@ snapshots:
commander@12.1.0: {}
+ concat-map@0.0.1: {}
+
css-declaration-sorter@7.2.0(postcss@8.5.6):
dependencies:
postcss: 8.5.6
@@ -2442,8 +2569,25 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
+ follow-redirects@1.15.9: {}
+
+ fs-minipass@2.1.0:
+ dependencies:
+ minipass: 3.3.6
+
+ fs.realpath@1.0.0: {}
+
get-port@4.2.0: {}
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
globals@13.24.0:
dependencies:
type-fest: 0.20.2
@@ -2466,6 +2610,13 @@ snapshots:
htmx.org@2.0.6: {}
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
inline-style-parser@0.2.4: {}
is-arrayish@0.3.2: {}
@@ -2578,6 +2729,23 @@ snapshots:
min-indent@1.0.1: {}
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minipass@3.3.6:
+ dependencies:
+ yallist: 4.0.0
+
+ minipass@5.0.0: {}
+
+ minizlib@2.1.2:
+ dependencies:
+ minipass: 3.3.6
+ yallist: 4.0.0
+
+ mkdirp@1.0.4: {}
+
msgpackr-extract@3.0.3:
dependencies:
node-gyp-build-optional-packages: 5.2.2
@@ -2617,6 +2785,10 @@ snapshots:
nullthrows@1.1.1: {}
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
ordered-binary@1.6.0: {}
parcel@2.15.4(@swc/helpers@0.5.17):
@@ -2640,6 +2812,8 @@ snapshots:
- '@swc/helpers'
- napi-wasm
+ path-is-absolute@1.0.1: {}
+
picocolors@1.1.1: {}
picomatch@2.3.1: {}
@@ -2871,6 +3045,10 @@ snapshots:
regenerator-runtime@0.14.1: {}
+ rimraf@3.0.2:
+ dependencies:
+ glob: 7.2.3
+
safe-buffer@5.2.1: {}
sax@1.4.1: {}
@@ -2933,6 +3111,15 @@ snapshots:
picocolors: 1.1.1
sax: 1.4.1
+ tar@6.2.1:
+ dependencies:
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ minipass: 5.0.0
+ minizlib: 2.1.2
+ mkdirp: 1.0.4
+ yallist: 4.0.0
+
term-size@2.2.1: {}
to-regex-range@5.0.1:
@@ -2955,4 +3142,14 @@ snapshots:
utility-types@3.11.0: {}
+ wasm-pack@0.13.1:
+ dependencies:
+ binary-install: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
weak-lru-cache@1.2.2: {}
+
+ wrappy@1.0.2: {}
+
+ yallist@4.0.0: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..220490e
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,3 @@
+packages:
+ - 'thecockpit'
+ - '.'
diff --git a/src/components/head.html b/src/components/head.html
index 4fb8184..fc4db02 100644
--- a/src/components/head.html
+++ b/src/components/head.html
@@ -8,12 +8,10 @@
The SANDWICH
-
-
-
+
+
diff --git a/src/firacode.css b/src/firacode.css
new file mode 100644
index 0000000..9a41409
--- /dev/null
+++ b/src/firacode.css
@@ -0,0 +1,18 @@
+@font-face {
+ font-family: 'Fira Code';
+ src: url('FiraCode-Regular.woff2') format('woff2'),
+ url('FiraCode-Regular.woff') format('woff');
+ font-weight: normal;
+ font-style: normal;
+ font-display: swap;
+}
+
+@font-face {
+ font-family: 'Fira Code';
+ src: url('FiraCode-Bold.woff2') format('woff2'),
+ url('FiraCode-Bold.woff') format('woff');
+ font-weight: bold;
+ font-style: normal;
+ font-display: swap;
+}
+
diff --git a/src/partials/page_four.html b/src/partials/page_four.html
index 5a93094..cd45f10 100644
--- a/src/partials/page_four.html
+++ b/src/partials/page_four.html
@@ -30,4 +30,19 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/partials/projects.html b/src/partials/projects.html
index 5ac3084..930ac9f 100644
--- a/src/partials/projects.html
+++ b/src/partials/projects.html
@@ -228,4 +228,9 @@
-
\ No newline at end of file
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/style.css b/src/style.css
index ba91927..5a2ed44 100644
--- a/src/style.css
+++ b/src/style.css
@@ -254,6 +254,25 @@ blockquote {
margin: 15px 0;
}
+#cockpit-canvas {
+ margin: 0;
+ width: 100%;
+ height: 70vh;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ align-content: center;
+ background-color: #121212;
+
+ pre {
+ font-family: "Fira Code", monospace;
+ font-size: 16px;
+ margin: 0px;
+ }
+}
+
+
body {
margin: 0;
}
diff --git a/thecockpit/.gitignore b/thecockpit/.gitignore
new file mode 100644
index 0000000..4e30131
--- /dev/null
+++ b/thecockpit/.gitignore
@@ -0,0 +1,6 @@
+/target
+**/*.rs.bk
+Cargo.lock
+bin/
+pkg/
+wasm-pack.log
diff --git a/thecockpit/Cargo.toml b/thecockpit/Cargo.toml
new file mode 100644
index 0000000..b8a3e01
--- /dev/null
+++ b/thecockpit/Cargo.toml
@@ -0,0 +1,46 @@
+[package]
+name = "thecockpit"
+version = "0.1.0"
+authors = ["Isaac Mills "]
+edition = "2018"
+
+[profile.dev.package."*"]
+opt-level = 3
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[features]
+default = ["console_error_panic_hook"]
+crossterm = ["ratatui/crossterm"]
+
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+ratatui = { version = "0.29.0", default-features = false }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+ratatui = { version = "0.29.0", default-features = false }
+ratzilla = "0.1.0"
+wasm-bindgen = "0.2.84"
+
+# The `console_error_panic_hook` crate provides better debugging of panics by
+# logging them with `console.error`. This is great for development, but requires
+# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
+# code size when deploying.
+console_error_panic_hook = { version = "0.1.7", optional = true }
+
+[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
+wasm-bindgen-test = "0.3.34"
+
+[package.metadata.wasm-pack.profile.release]
+wasm-opt = ['--enable-bulk-memory-opt', '--enable-mutable-globals', '--enable-nontrapping-float-to-int']
+# wasm-opt = false
+
+[profile.dev]
+panic = "abort"
+
+[profile.release]
+opt-level = 2
+lto = true
+codegen-units = 1
+strip = true
+panic = "abort"
diff --git a/thecockpit/README.md b/thecockpit/README.md
new file mode 100644
index 0000000..6b68408
--- /dev/null
+++ b/thecockpit/README.md
@@ -0,0 +1,84 @@
+
+
+## About
+
+[**📚 Read this template tutorial! 📚**][template-docs]
+
+This template is designed for compiling Rust libraries into WebAssembly and
+publishing the resulting package to NPM.
+
+Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other
+templates and usages of `wasm-pack`.
+
+[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html
+[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html
+
+## 🚴 Usage
+
+### 🐑 Use `cargo generate` to Clone this Template
+
+[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate)
+
+```
+cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project
+cd my-project
+```
+
+### 🛠️ Build with `wasm-pack build`
+
+```
+wasm-pack build
+```
+
+### 🔬 Test in Headless Browsers with `wasm-pack test`
+
+```
+wasm-pack test --headless --firefox
+```
+
+### 🎁 Publish to NPM with `wasm-pack publish`
+
+```
+wasm-pack publish
+```
+
+## 🔋 Batteries Included
+
+* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating
+ between WebAssembly and JavaScript.
+* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook)
+ for logging panic messages to the developer console.
+* `LICENSE-APACHE` and `LICENSE-MIT`: most Rust projects are licensed this way, so these are included for you
+
+## License
+
+Licensed under either of
+
+* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
+* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally
+submitted for inclusion in the work by you, as defined in the Apache-2.0
+license, shall be dual licensed as above, without any additional terms or
+conditions.
diff --git a/thecockpit/package.json b/thecockpit/package.json
new file mode 100644
index 0000000..47a07be
--- /dev/null
+++ b/thecockpit/package.json
@@ -0,0 +1,8 @@
+{
+ "scripts": {
+ "build": "wasm-pack build --target web --release"
+ },
+ "devDependencies": {
+ "wasm-pack": "^0.13.1"
+ }
+}
diff --git a/thecockpit/pnpm-lock.yaml b/thecockpit/pnpm-lock.yaml
new file mode 100644
index 0000000..55efd94
--- /dev/null
+++ b/thecockpit/pnpm-lock.yaml
@@ -0,0 +1,205 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ devDependencies:
+ wasm-pack:
+ specifier: ^0.13.1
+ version: 0.13.1
+
+packages:
+
+ axios@0.26.1:
+ resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ binary-install@1.1.0:
+ resolution: {integrity: sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==}
+ engines: {node: '>=10'}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ chownr@2.0.0:
+ resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+ engines: {node: '>=10'}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ follow-redirects@1.15.9:
+ resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ fs-minipass@2.1.0:
+ resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+ engines: {node: '>= 8'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ minipass@3.3.6:
+ resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+ engines: {node: '>=8'}
+
+ minipass@5.0.0:
+ resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
+ engines: {node: '>=8'}
+
+ minizlib@2.1.2:
+ resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+ engines: {node: '>= 8'}
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
+ hasBin: true
+
+ tar@6.2.1:
+ resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
+ engines: {node: '>=10'}
+
+ wasm-pack@0.13.1:
+ resolution: {integrity: sha512-P9exD4YkjpDbw68xUhF3MDm/CC/3eTmmthyG5bHJ56kalxOTewOunxTke4SyF8MTXV6jUtNjXggPgrGmMtczGg==}
+ hasBin: true
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ yallist@4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+
+snapshots:
+
+ axios@0.26.1:
+ dependencies:
+ follow-redirects: 1.15.9
+ transitivePeerDependencies:
+ - debug
+
+ balanced-match@1.0.2: {}
+
+ binary-install@1.1.0:
+ dependencies:
+ axios: 0.26.1
+ rimraf: 3.0.2
+ tar: 6.2.1
+ transitivePeerDependencies:
+ - debug
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ chownr@2.0.0: {}
+
+ concat-map@0.0.1: {}
+
+ follow-redirects@1.15.9: {}
+
+ fs-minipass@2.1.0:
+ dependencies:
+ minipass: 3.3.6
+
+ fs.realpath@1.0.0: {}
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minipass@3.3.6:
+ dependencies:
+ yallist: 4.0.0
+
+ minipass@5.0.0: {}
+
+ minizlib@2.1.2:
+ dependencies:
+ minipass: 3.3.6
+ yallist: 4.0.0
+
+ mkdirp@1.0.4: {}
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ path-is-absolute@1.0.1: {}
+
+ rimraf@3.0.2:
+ dependencies:
+ glob: 7.2.3
+
+ tar@6.2.1:
+ dependencies:
+ chownr: 2.0.0
+ fs-minipass: 2.1.0
+ minipass: 5.0.0
+ minizlib: 2.1.2
+ mkdirp: 1.0.4
+ yallist: 4.0.0
+
+ wasm-pack@0.13.1:
+ dependencies:
+ binary-install: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
+ wrappy@1.0.2: {}
+
+ yallist@4.0.0: {}
diff --git a/thecockpit/src/lib.rs b/thecockpit/src/lib.rs
new file mode 100644
index 0000000..01bb747
--- /dev/null
+++ b/thecockpit/src/lib.rs
@@ -0,0 +1,42 @@
+#[cfg(not(target_arch = "wasm32"))]
+pub mod native;
+#[cfg(target_arch = "wasm32")]
+pub mod web;
+
+use std::cell::RefCell;
+
+use ratatui::{
+ layout::Alignment,
+ style::{Color, Stylize},
+ widgets::{Block, BorderType, Paragraph},
+ Frame,
+};
+
+#[derive(Default)]
+pub struct App {
+ counter: RefCell,
+}
+
+impl App {
+ pub fn render(&self, frame: &mut Frame) {
+ let counter = self.counter.borrow();
+ let block = Block::bordered()
+ .title("thecockpit")
+ .title_alignment(Alignment::Center)
+ .border_type(BorderType::Rounded);
+
+ let text = format!(
+ "This is a Ratzilla template.\n\
+ Press left and right to increment and decrement the counter respectively.\n\
+ Counter: {counter}",
+ );
+
+ let paragraph = Paragraph::new(text)
+ .block(block)
+ .fg(Color::White)
+ .bg(Color::Black)
+ .centered();
+
+ frame.render_widget(paragraph, frame.area());
+ }
+}
diff --git a/thecockpit/src/main.rs b/thecockpit/src/main.rs
new file mode 100644
index 0000000..e2123cb
--- /dev/null
+++ b/thecockpit/src/main.rs
@@ -0,0 +1,23 @@
+use ratatui::crossterm::event::{self, KeyCode, KeyEvent};
+use thecockpit::App;
+
+fn main() {
+ let mut terminal = ratatui::init();
+ let app = App::default();
+ loop {
+ terminal
+ .draw(|frame| app.render(frame))
+ .expect("failed to draw frame");
+ match event::read().expect("failed to read event") {
+ event::Event::Key(key) => match key {
+ KeyEvent {
+ code: KeyCode::Char('q'),
+ ..
+ } => break,
+ key => app.handle_events(key),
+ },
+ _ => {}
+ }
+ }
+ ratatui::restore();
+}
diff --git a/thecockpit/src/native/mod.rs b/thecockpit/src/native/mod.rs
new file mode 100644
index 0000000..048085b
--- /dev/null
+++ b/thecockpit/src/native/mod.rs
@@ -0,0 +1,14 @@
+use ratatui::crossterm::event::{KeyCode, KeyEvent};
+
+use crate::App;
+
+impl App {
+ pub fn handle_events(&self, key_event: KeyEvent) {
+ let mut counter = self.counter.borrow_mut();
+ match key_event.code {
+ KeyCode::Left => *counter = counter.saturating_sub(1),
+ KeyCode::Right => *counter = counter.saturating_add(1),
+ _ => {}
+ }
+ }
+}
diff --git a/thecockpit/src/web/mod.rs b/thecockpit/src/web/mod.rs
new file mode 100644
index 0000000..534a1e2
--- /dev/null
+++ b/thecockpit/src/web/mod.rs
@@ -0,0 +1,60 @@
+mod utils;
+
+use std::{cell::RefCell, io, rc::Rc};
+
+use ratatui::{
+ layout::Alignment,
+ style::{Color, Stylize},
+ widgets::{Block, BorderType, Paragraph},
+ Frame, Terminal,
+};
+use ratzilla::{
+ backend::canvas::{CanvasBackend, CanvasBackendOptions},
+ event::{KeyCode, KeyEvent},
+ WebRenderer,
+};
+use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+extern "C" {
+ #[wasm_bindgen(js_namespace = console)]
+ fn log(s: &str);
+}
+
+#[wasm_bindgen]
+pub fn cockpit(id: &str) {
+ utils::set_panic_hook();
+ start_cockpit(id).unwrap();
+}
+
+fn start_cockpit(id: &str) -> io::Result<()> {
+ let backend =
+ CanvasBackend::new_with_options(CanvasBackendOptions::new().grid_id("cockpit-canvas"))?;
+ let terminal = Terminal::new(backend)?;
+
+ let state = Rc::new(crate::App::default());
+
+ let event_state = Rc::clone(&state);
+ terminal.on_key_event(move |key_event| {
+ event_state.handle_events(key_event);
+ });
+
+ let render_state = Rc::clone(&state);
+ terminal.draw_web(move |frame| {
+ render_state.render(frame);
+ });
+
+ Ok(())
+}
+
+impl crate::App {
+ fn handle_events(&self, key_event: KeyEvent) {
+ // log(&format!("{:?}", key_event));
+ let mut counter = self.counter.borrow_mut();
+ match key_event.code {
+ KeyCode::Left => *counter = counter.saturating_sub(1),
+ KeyCode::Right => *counter = counter.saturating_add(1),
+ _ => {}
+ }
+ }
+}
diff --git a/thecockpit/src/web/utils.rs b/thecockpit/src/web/utils.rs
new file mode 100644
index 0000000..b1d7929
--- /dev/null
+++ b/thecockpit/src/web/utils.rs
@@ -0,0 +1,10 @@
+pub fn set_panic_hook() {
+ // When the `console_error_panic_hook` feature is enabled, we can call the
+ // `set_panic_hook` function at least once during initialization, and then
+ // we will get better error messages if our code ever panics.
+ //
+ // For more details see
+ // https://github.com/rustwasm/console_error_panic_hook#readme
+ #[cfg(feature = "console_error_panic_hook")]
+ console_error_panic_hook::set_once();
+}