diff --git a/content/treehouse/cmd.tree b/content/treehouse/cmd.tree
index 15211e3..13d587a 100644
--- a/content/treehouse/cmd.tree
+++ b/content/treehouse/cmd.tree
@@ -1,7 +1,7 @@
%% title = "command line"
% id = "01JEK4XKK26T6W603FTPQHQ7C8"
-- press `:`{=html} to open the command line.
+- press `:`{=html} or the little `:|` icon in the footer to open the command line.
% id = "01JEK4XKK27KXP01EK8K890SPK"
- type in your command, then press `Enter`{=html} to run it.
@@ -18,6 +18,3 @@
% id = "01JEK4XKK2EDTVCNZQRV9XDZXJ"
- unknown commands do not do anything.
known commands usually result in immediate feedback.
-
-% id = "01JEK4XKK2S4W0TPT4JY8AH143"
-- the command line is currently not accessible on mobile devices.
diff --git a/crates/treehouse/src/cli/serve.rs b/crates/treehouse/src/cli/serve.rs
index 0355010..5fd0354 100644
--- a/crates/treehouse/src/cli/serve.rs
+++ b/crates/treehouse/src/cli/serve.rs
@@ -5,6 +5,7 @@ mod picture_upload;
use std::fmt::Write;
use std::{net::Ipv4Addr, sync::Arc};
+use axum::http::header::LOCATION;
use axum::{
extract::{Path, Query, RawQuery, State},
http::{
@@ -49,6 +50,7 @@ pub async fn serve(
.route("/", get(index)) // needed explicitly because * does not match empty paths
.route("/*path", get(vfs_entry))
.route("/b", get(branch))
+ .route("/treehouse/quit", get(quit))
.fallback(get(four_oh_four))
.with_state(Arc::new(Server {
sources: sources.clone(),
@@ -196,3 +198,12 @@ async fn branch(RawQuery(named_id): RawQuery, State(state): State>)
system_page(&state.target, system::B_DOCS, StatusCode::OK).await
}
}
+
+async fn quit() -> impl IntoResponse {
+ info!("somebody just quit the treehouse. congration to them!");
+
+ (
+ StatusCode::FOUND,
+ [(LOCATION, "https://www.youtube.com/watch?v=dQw4w9WgXcQ")],
+ )
+}
diff --git a/static/css/icons.css b/static/css/icons.css
index ae2e6e1..3ee6101 100644
--- a/static/css/icons.css
+++ b/static/css/icons.css
@@ -1,24 +1,25 @@
:root {
- --icon-breadcrumb: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTYgMTIgNC00LTQtNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNTU0MjNlIiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4=');
- --icon-expand: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJtNyA1di0zaC0ydjNoLTN2MmgzdjNoMnYtM2gzdi0yeiIgZmlsbD0iIzU1NDIzZSIgZmlsbC1vcGFjaXR5PSIuNSIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+');
- --icon-leaf: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iNiIgY3k9IjYiIGZpbGw9IiM1NTQyM2UiIGZpbGwtb3BhY2l0eT0iLjUiIHI9IjIiLz48L3N2Zz4=');
- --icon-collapse: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTMgNmg2IiBzdHJva2U9IiM1NTQyM2UiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+');
- --icon-more: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTQgNiA0IDQgNC00IiBmaWxsPSJub25lIiBzdHJva2U9IiM1NTQyM2UiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+');
+ --icon-breadcrumb: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTYgMTIgNC00LTQtNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjNTU0MjNlIiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4=");
+ --icon-expand: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJtNyA1di0zaC0ydjNoLTN2MmgzdjNoMnYtM2gzdi0yeiIgZmlsbD0iIzU1NDIzZSIgZmlsbC1vcGFjaXR5PSIuNSIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+");
+ --icon-leaf: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iNiIgY3k9IjYiIGZpbGw9IiM1NTQyM2UiIGZpbGwtb3BhY2l0eT0iLjUiIHI9IjIiLz48L3N2Zz4=");
+ --icon-collapse: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTMgNmg2IiBzdHJva2U9IiM1NTQyM2UiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+");
+ --icon-more: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTQgNiA0IDQgNC00IiBmaWxsPSJub25lIiBzdHJva2U9IiM1NTQyM2UiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+");
--icon-permalink: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTcuNjU2ODYgMiAxLjQxNDIxIDEuNDE0MjJjLjY4MDUxLjY4MDUxIDEuMDY0NTMgMS41NDUyMSAxLjE1MjEzIDIuNDMzNjIuODg4NC4wODc2IDEuNzUzMS40NzE2NSAyLjQzMzcgMS4xNTIxNmwxLjQxNDIgMS40MTQyMmMxLjU2MjEgMS41NjIwOSAxLjU2MjEgNC4wOTQ3OCAwIDUuNjU2ODhzLTQuMDk0NzkgMS41NjIxLTUuNjU2ODggMGwtMS40MTQyMi0xLjQxNDJjLS42ODA1MS0uNjgwNi0xLjA2NDU2LTEuNTQ1My0xLjE1MjE2LTIuNDMzNy0uODg4NDEtLjA4NzYtMS43NTMxMS0uNDcxNjItMi40MzM2Mi0xLjE1MjEzbC0xLjQxNDIyLTEuNDE0MjFjLTEuNTYyMDk0LTEuNTYyMS0xLjU2MjA5NC00LjA5NDc2IDAtNS42NTY4NiAxLjU2MjEtMS41NjIwOTQgNC4wOTQ3Ni0xLjU2MjA5NCA1LjY1Njg2IDB6bS42MTggNy42ODkwN2MtLjE0NDMuMDg1MjItLjI5MjgxLjE2MDYxLS40NDQ1NS4yMjYxNi4wMjA4My40ODI1Ny4yMTU0Ni45NTg5Ny41ODM5MSAxLjMyNzM3bDEuNDE0MjEgMS40MTQzYy43ODEwNy43ODEgMi4wNDczNy43ODEgMi44Mjg0NyAwIC43ODEtLjc4MTEuNzgxLTIuMDQ3NCAwLTIuODI4NDdsLTEuNDE0My0xLjQxNDIxYy0uMzY4NC0uMzY4NDUtLjg0NDgtLjU2MzA4LTEuMzI3MzctLjU4MzkxLS4wNjU1NS4xNTE3My0uMTQwOTMuMzAwMjQtLjIyNjE2LjQ0NDU0bDEuODQ2NDMgMS44NDY0NS0xLjQxNDIgMS40MTQyem0tLjYxOC00Ljg2MDY0Yy4zNjg0NC4zNjg0NS41NjMwOC44NDQ4OC41ODM5MSAxLjMyNzQyLS4xNTE3NC4wNjU1NC0uMzAwMjQuMTQwOTMtLjQ0NDU0LjIyNjE1bC0xLjkxNzU0LTEuOTE3NTMtMS40MTQyMSAxLjQxNDIxIDEuOTE3NTMgMS45MTc1M2MtLjA4NTIzLjE0NDMxLS4xNjA2MS4yOTI4Mi0uMjI2MTYuNDQ0NTYtLjQ4MjU0LS4wMjA4My0uOTU4OTctLjIxNTQ3LTEuMzI3NDItLjU4MzkxbC0xLjQxNDIxLTEuNDE0MjJjLS43ODEwNS0uNzgxMDUtLjc4MTA1LTIuMDQ3MzcgMC0yLjgyODQyczIuMDQ3MzctLjc4MTA1IDIuODI4NDIgMHoiIGZpbGw9IiM1NTQyM2UiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
--icon-go: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTEwLjU4NTggNy0yLjI5Mjg5LTIuMjkyODkgMS40MTQyMS0xLjQxNDIyIDQuNzA3MDggNC43MDcxMS00LjcwNzA4IDQuNzA3MS0xLjQxNDIxLTEuNDE0MiAyLjI5Mjg5LTIuMjkyOWgtNy41ODU4di0yeiIgZmlsbD0iIzU1NDIzZSIvPjwvc3ZnPg==");
- --icon-history: url('data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xMi4yNDI2IDMuNzU3MzZjLTEuMDg1Ny0xLjA4NTc5LTIuNTg1NzUtMS43NTczNi00LjI0MjYtMS43NTczNi0zLjMxMzcxIDAtNiAyLjY4NjI5LTYgNiAwIDMuMzEzNyAyLjY4NjI5IDYgNiA2IDMuMzEzNyAwIDYtMi42ODYzIDYtNmgybC0zLTMtMyAzaDJjMCAyLjIwOTEtMS43OTA5IDQtNCA0LTIuMjA5MTQgMC00LTEuNzkwOS00LTQgMC0yLjIwOTE0IDEuNzkwODYtNCA0LTQgMS4xMDQ1NyAwIDIuMTA0Ni40NDc3MiAyLjgyODQgMS4xNzE1N3ptLTUuMjQyNTkgMS4yNDI2NHYyLjU4NTc5bC0xLjIwNzEgMS4yMDcxIDEuNDE0MjEgMS40MTQyMSAxLjc5Mjg5LTEuNzkyODl2LTMuNDE0MjF6IiBmaWxsPSIjNTU0MjNlIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=');
+ --icon-history: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xMi4yNDI2IDMuNzU3MzZjLTEuMDg1Ny0xLjA4NTc5LTIuNTg1NzUtMS43NTczNi00LjI0MjYtMS43NTczNi0zLjMxMzcxIDAtNiAyLjY4NjI5LTYgNiAwIDMuMzEzNyAyLjY4NjI5IDYgNiA2IDMuMzEzNyAwIDYtMi42ODYzIDYtNmgybC0zLTMtMyAzaDJjMCAyLjIwOTEtMS43OTA5IDQtNCA0LTIuMjA5MTQgMC00LTEuNzkwOS00LTQgMC0yLjIwOTE0IDEuNzkwODYtNCA0LTQgMS4xMDQ1NyAwIDIuMTA0Ni40NDc3MiAyLjgyODQgMS4xNzE1N3ptLTUuMjQyNTkgMS4yNDI2NHYyLjU4NTc5bC0xLjIwNzEgMS4yMDcxIDEuNDE0MjEgMS40MTQyMSAxLjc5Mjg5LTEuNzkyODl2LTMuNDE0MjF6IiBmaWxsPSIjNTU0MjNlIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=");
+ --icon-cmd: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjNTU0MjNlIj48Y2lyY2xlIGN4PSI2IiBjeT0iNiIgcj0iMSIvPjxjaXJjbGUgY3g9IjYiIGN5PSIxMCIgcj0iMSIvPjxwYXRoIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0ibTkgMTR2LTEyaDJ2MTJ6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L2c+PC9zdmc+");
}
@media (prefers-color-scheme: dark) {
:root {
- --icon-breadcrumb: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTYgMTIgNC00LTQtNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDdjZGJmIiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4=');
- --icon-expand: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJtNyA1di0zaC0ydjNoLTN2MmgzdjNoMnYtM2gzdi0yeiIgZmlsbD0iI2Q3Y2RiZiIgZmlsbC1vcGFjaXR5PSIuNSIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+');
- --icon-leaf: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iNiIgY3k9IjYiIGZpbGw9IiNkN2NkYmYiIGZpbGwtb3BhY2l0eT0iLjUiIHI9IjIiLz48L3N2Zz4=');
- --icon-collapse: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTMgNmg2IiBzdHJva2U9IiNkN2NkYmYiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+');
+ --icon-breadcrumb: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTYgMTIgNC00LTQtNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDdjZGJmIiBzdHJva2Utd2lkdGg9IjIiLz48L3N2Zz4=");
+ --icon-expand: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJtNyA1di0zaC0ydjNoLTN2MmgzdjNoMnYtM2gzdi0yeiIgZmlsbD0iI2Q3Y2RiZiIgZmlsbC1vcGFjaXR5PSIuNSIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+");
+ --icon-leaf: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iNiIgY3k9IjYiIGZpbGw9IiNkN2NkYmYiIGZpbGwtb3BhY2l0eT0iLjUiIHI9IjIiLz48L3N2Zz4=");
+ --icon-collapse: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEyIiB3aWR0aD0iMTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTMgNmg2IiBzdHJva2U9IiNkN2NkYmYiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+");
--icon-permalink: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTcuNjU2ODYgMiAxLjQxNDIxIDEuNDE0MjJjLjY4MDUxLjY4MDUxIDEuMDY0NTMgMS41NDUyMSAxLjE1MjEzIDIuNDMzNjIuODg4NC4wODc2IDEuNzUzMS40NzE2NSAyLjQzMzcgMS4xNTIxNmwxLjQxNDIgMS40MTQyMmMxLjU2MjEgMS41NjIwOSAxLjU2MjEgNC4wOTQ3OCAwIDUuNjU2ODhzLTQuMDk0NzkgMS41NjIxLTUuNjU2ODggMGwtMS40MTQyMi0xLjQxNDJjLS42ODA1MS0uNjgwNi0xLjA2NDU2LTEuNTQ1My0xLjE1MjE2LTIuNDMzNy0uODg4NDEtLjA4NzYtMS43NTMxMS0uNDcxNjItMi40MzM2Mi0xLjE1MjEzbC0xLjQxNDIyLTEuNDE0MjFjLTEuNTYyMDk0LTEuNTYyMS0xLjU2MjA5NC00LjA5NDc2IDAtNS42NTY4NiAxLjU2MjEtMS41NjIwOTQgNC4wOTQ3Ni0xLjU2MjA5NCA1LjY1Njg2IDB6bS42MTggNy42ODkwN2MtLjE0NDMuMDg1MjItLjI5MjgxLjE2MDYxLS40NDQ1NS4yMjYxNi4wMjA4My40ODI1Ny4yMTU0Ni45NTg5Ny41ODM5MSAxLjMyNzM3bDEuNDE0MjEgMS40MTQzYy43ODEwNy43ODEgMi4wNDczNy43ODEgMi44Mjg0NyAwIC43ODEtLjc4MTEuNzgxLTIuMDQ3NCAwLTIuODI4NDdsLTEuNDE0My0xLjQxNDIxYy0uMzY4NC0uMzY4NDUtLjg0NDgtLjU2MzA4LTEuMzI3MzctLjU4MzkxLS4wNjU1NS4xNTE3My0uMTQwOTMuMzAwMjQtLjIyNjE2LjQ0NDU0bDEuODQ2NDMgMS44NDY0NS0xLjQxNDIgMS40MTQyem0tLjYxOC00Ljg2MDY0Yy4zNjg0NC4zNjg0NS41NjMwOC44NDQ4OC41ODM5MSAxLjMyNzQyLS4xNTE3NC4wNjU1NC0uMzAwMjQuMTQwOTMtLjQ0NDU0LjIyNjE1bC0xLjkxNzU0LTEuOTE3NTMtMS40MTQyMSAxLjQxNDIxIDEuOTE3NTMgMS45MTc1M2MtLjA4NTIzLjE0NDMxLS4xNjA2MS4yOTI4Mi0uMjI2MTYuNDQ0NTYtLjQ4MjU0LS4wMjA4My0uOTU4OTctLjIxNTQ3LTEuMzI3NDItLjU4MzkxbC0xLjQxNDIxLTEuNDE0MjJjLS43ODEwNS0uNzgxMDUtLjc4MTA1LTIuMDQ3MzcgMC0yLjgyODQyczIuMDQ3MzctLjc4MTA1IDIuODI4NDIgMHoiIGZpbGw9IiNkN2NkYmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
--icon-go: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTEwLjU4NTggNy0yLjI5Mjg5LTIuMjkyODkgMS40MTQyMS0xLjQxNDIyIDQuNzA3MDggNC43MDcxMS00LjcwNzA4IDQuNzA3MS0xLjQxNDIxLTEuNDE0MiAyLjI5Mjg5LTIuMjkyOWgtNy41ODU4di0yeiIgZmlsbD0iI2Q3Y2RiZiIvPjwvc3ZnPg==");
- --icon-more: url('data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTQgNiA0IDQgNC00IiBmaWxsPSJub25lIiBzdHJva2U9IiNkN2NkYmYiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+');
- --icon-history: url('data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xMi4yNDI2IDMuNzU3MzZjLTEuMDg1Ny0xLjA4NTc5LTIuNTg1NzUtMS43NTczNi00LjI0MjYtMS43NTczNi0zLjMxMzcxIDAtNiAyLjY4NjI5LTYgNiAwIDMuMzEzNyAyLjY4NjI5IDYgNiA2IDMuMzEzNyAwIDYtMi42ODYzIDYtNmgybC0zLTMtMyAzaDJjMCAyLjIwOTEtMS43OTA5IDQtNCA0LTIuMjA5MTQgMC00LTEuNzkwOS00LTQgMC0yLjIwOTE0IDEuNzkwODYtNCA0LTQgMS4xMDQ1NyAwIDIuMTA0Ni40NDc3MiAyLjgyODQgMS4xNzE1N3ptLTUuMjQyNTkgMS4yNDI2NHYyLjU4NTc5bC0xLjIwNzEgMS4yMDcxIDEuNDE0MjEgMS40MTQyMSAxLjc5Mjg5LTEuNzkyODl2LTMuNDE0MjF6IiBmaWxsPSIjZDdjZGJmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=');
+ --icon-more: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjE2IiB3aWR0aD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTQgNiA0IDQgNC00IiBmaWxsPSJub25lIiBzdHJva2U9IiNkN2NkYmYiIHN0cm9rZS1vcGFjaXR5PSIuNSIgc3Ryb2tlLXdpZHRoPSIyIi8+PC9zdmc+");
+ --icon-history: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Im0xMi4yNDI2IDMuNzU3MzZjLTEuMDg1Ny0xLjA4NTc5LTIuNTg1NzUtMS43NTczNi00LjI0MjYtMS43NTczNi0zLjMxMzcxIDAtNiAyLjY4NjI5LTYgNiAwIDMuMzEzNyAyLjY4NjI5IDYgNiA2IDMuMzEzNyAwIDYtMi42ODYzIDYtNmgybC0zLTMtMyAzaDJjMCAyLjIwOTEtMS43OTA5IDQtNCA0LTIuMjA5MTQgMC00LTEuNzkwOS00LTQgMC0yLjIwOTE0IDEuNzkwODYtNCA0LTQgMS4xMDQ1NyAwIDIuMTA0Ni40NDc3MiAyLjgyODQgMS4xNzE1N3ptLTUuMjQyNTkgMS4yNDI2NHYyLjU4NTc5bC0xLjIwNzEgMS4yMDcxIDEuNDE0MjEgMS40MTQyMSAxLjc5Mjg5LTEuNzkyODl2LTMuNDE0MjF6IiBmaWxsPSIjZDdjZGJmIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=");
+ --icon-cmd: url("data:image/svg+xml;base64,PHN2ZyBmaWxsPSJub25lIiBoZWlnaHQ9IjE2IiB2aWV3Qm94PSIwIDAgMTYgMTYiIHdpZHRoPSIxNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZyBmaWxsPSIjZDdjZGJmIj48Y2lyY2xlIGN4PSI2IiBjeT0iNiIgcj0iMSIvPjxjaXJjbGUgY3g9IjYiIGN5PSIxMCIgcj0iMSIvPjxwYXRoIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0ibTkgMTR2LTEyaDJ2MTJ6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L2c+PC9zdmc+");
}
}
-
diff --git a/static/css/main.css b/static/css/main.css
index e0d9c23..2700236 100644
--- a/static/css/main.css
+++ b/static/css/main.css
@@ -545,78 +545,45 @@ footer {
display: flex;
flex-direction: row;
- & #version-info {
+ & > .left {
flex-grow: 1;
}
- & #footer-icon {
+ & > .right {
flex-shrink: 0;
}
- & #version-info {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: end;
+ & #footer-icon {
+ display: block;
+
+ & > svg {
+ display: block;
+ color: var(--text-color);
+ opacity: 40%;
+ }
+ }
+
+ & #open-command-line {
+ width: 32px;
+ height: 32px;
+
+ background: none;
+ border: none;
+ padding: 0;
+
+ background-image: var(--icon-cmd);
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
opacity: 50%;
transition: var(--transition-duration) opacity;
- & .icon-history {
- display: inline-block;
- width: 32px;
- height: 32px;
- margin-right: 0.5rem;
- background-image: var(--icon-history);
- background-repeat: no-repeat;
- background-position: 50% 50%;
- }
-
- & > ul {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- justify-content: flex-end;
-
- margin: 0;
- padding-left: 0;
-
- list-style: none;
- opacity: 0%;
- transition: var(--transition-duration) opacity;
- }
-
- & > ul > li {
- flex-shrink: 0;
- }
-
- & > ul > li:not(:first-child)::before {
- content: "ยท";
- text-decoration: none;
- display: inline-block;
- padding-left: 0.75em;
- padding-right: 0.75em;
- }
-
- & a {
- display: inline-block;
- color: var(--text-color);
- }
-
- &:hover > ul {
+ &:hover {
opacity: 100%;
}
- }
- & #footer-icon {
- & > a {
- display: block;
- }
-
- & > a > svg {
- display: block;
- color: var(--text-color);
- opacity: 40%;
+ &:active {
+ opacity: 75%;
}
}
}
@@ -727,8 +694,9 @@ th-command-line {
& > .input-wrapper {
display: flex;
flex-direction: row;
+ align-items: center;
- padding: 2px 4px;
+ padding: 0 4px;
width: 100%;
&::before {
@@ -743,6 +711,8 @@ th-command-line {
border: none;
flex-grow: 1;
+ padding: 2px 0;
+
&:focus {
outline: none;
}
@@ -758,6 +728,9 @@ th-command-line {
margin: 0;
padding: 0;
+ max-height: 25vh;
+ overflow: auto;
+
& > li {
padding: 2px 8px;
@@ -774,6 +747,30 @@ th-command-line {
background-color: var(--liquidex-brand-blue);
color: white;
}
+
+ &.immediate {
+ cursor: pointer;
+ }
+ }
+ }
+}
+
+@media (hover: none) {
+ th-command-line {
+ & > ul.suggestions > li {
+ border-bottom: 1px solid var(--border-1);
+ }
+ }
+}
+
+@media (pointer: coarse) {
+ th-command-line {
+ & > .input-wrapper > input {
+ padding: 8px 0;
+ }
+
+ & > ul.suggestions > li {
+ padding: 8px 8px;
}
}
}
diff --git a/static/js/command-line.js b/static/js/command-line.js
index b509688..e991d43 100644
--- a/static/js/command-line.js
+++ b/static/js/command-line.js
@@ -26,12 +26,10 @@ export class CommandLine extends HTMLElement {
}
});
- window.addEventListener("click", () => {
- this.hide();
- });
-
- this.addEventListener("click", (event) => {
- event.stopPropagation();
+ window.addEventListener("click", (event) => {
+ if (!this.contains(event.target)) {
+ this.hide();
+ }
});
this.input.addEventListener("keydown", (event) => {
@@ -71,6 +69,7 @@ export class CommandLine extends HTMLElement {
tab(current, next) {
current?.classList?.remove("tabbed");
next.classList.add("tabbed");
+ next.scrollIntoView();
this.input.value = next.name;
// NOTE: Do NOT update suggestions here.
@@ -94,13 +93,20 @@ export class CommandLine extends HTMLElement {
updateSuggestions() {
let search = parseCommand(this.input.value)?.command ?? "";
let suggestions = Array.from(CommandLine.commands.entries()).filter(
- ([name, def]) => !def.isAlias && fuzzyMatch(search, name),
+ ([name, def]) =>
+ !def.isAlias &&
+ (def.showInSuggestions?.(this.input.value) ?? true) &&
+ fuzzyMatch(search, name),
);
suggestions.sort();
this.suggestions.replaceChildren();
for (let [name, def] of suggestions) {
let suggestion = this.suggestions.appendChild(document.createElement("li"));
+ if (def.immediate) {
+ suggestion.classList.add("immediate");
+ }
+
let commandName = suggestion.appendChild(document.createElement("dfn"));
commandName.textContent = name;
let commandDescription = suggestion.appendChild(document.createElement("span"));
@@ -110,10 +116,17 @@ export class CommandLine extends HTMLElement {
suggestion.name = name;
suggestion.def = def;
- suggestion.addEventListener("click", () => {
- this.input.value = name;
- this.updateSuggestions();
- this.input.focus();
+ suggestion.addEventListener("click", (event) => {
+ event.stopPropagation();
+
+ if (def.immediate) {
+ this.hide();
+ this.runCommand(name);
+ } else {
+ this.input.value = name + " ";
+ this.updateSuggestions();
+ this.input.focus();
+ }
});
}
}
@@ -128,11 +141,13 @@ export class CommandLine extends HTMLElement {
}
}
- static registerCommand({ aliases, description, run }) {
+ static registerCommand({ aliases, description, immediate, showInSuggestions, run }) {
for (let i = 0; i < aliases.length; ++i) {
CommandLine.commands.set(aliases[i], {
isAlias: i != 0,
description,
+ immediate,
+ showInSuggestions,
run,
});
}
@@ -168,7 +183,43 @@ function fuzzyMatch(pattern, string) {
CommandLine.registerCommand({
aliases: ["help", "h"],
description: '"OwO, what is this?"',
+ immediate: true,
+
run() {
window.location = `${TREEHOUSE_SITE}/treehouse/cmd`;
},
});
+
+CommandLine.registerCommand({
+ aliases: ["new", "n"],
+ description: "go to news feed",
+ immediate: true,
+
+ run() {
+ window.location = `${TREEHOUSE_SITE}/treehouse/new`;
+ },
+});
+
+CommandLine.registerCommand({
+ aliases: ["index", "i", "-w-"],
+ description: "go home",
+ immediate: true,
+
+ run() {
+ window.location = `${TREEHOUSE_SITE}/`;
+ },
+});
+
+CommandLine.registerCommand({
+ aliases: ["quit", "exit", "q", "q!", "wq", "wq!", "wqa", "wqa!", "bc", "bc!", "bca", "bca!"],
+ description: "quit liquidex's treehouse (congration!)",
+ // non-immediate because this is a destructive action
+
+ showInSuggestions(commandLine) {
+ return commandLine.length >= 1;
+ },
+
+ run() {
+ window.location = `${TREEHOUSE_SITE}/treehouse/quit`;
+ },
+});
diff --git a/static/js/dev/picture-upload.js b/static/js/dev/picture-upload.js
index 6791eeb..9712797 100644
--- a/static/js/dev/picture-upload.js
+++ b/static/js/dev/picture-upload.js
@@ -155,8 +155,10 @@ function formatSizeSI(bytes) {
if (TREEHOUSE_DEV) {
CommandLine.registerCommand({
- aliases: ["addpic"],
+ aliases: ["add-pic"],
description: "add a picture interactively and copy its ulid",
+ immediate: true,
+
run() {
let dialog = document.body.appendChild(document.createElement("dialog"));
dialog.addEventListener("keydown", (event) => {
diff --git a/static/svg/dark/cmd.svg b/static/svg/dark/cmd.svg
new file mode 100644
index 0000000..c97eb9a
--- /dev/null
+++ b/static/svg/dark/cmd.svg
@@ -0,0 +1,5 @@
+
diff --git a/static/svg/light/cmd.svg b/static/svg/light/cmd.svg
new file mode 100644
index 0000000..5b66990
--- /dev/null
+++ b/static/svg/light/cmd.svg
@@ -0,0 +1,5 @@
+
diff --git a/template/components/_footer.hbs b/template/components/_footer.hbs
index d8a16de..e216374 100644
--- a/template/components/_footer.hbs
+++ b/template/components/_footer.hbs
@@ -1,20 +1,16 @@