From 84893662ca9390b834cf28c4eec1a9de6638eee6 Mon Sep 17 00:00:00 2001 From: ivan Date: Sat, 15 Aug 2026 23:05:57 -0600 Subject: [PATCH] big kahuna --- README.pod | 66 + bin/compiled.world.psgi | 33 +- cpanfile | 2 + lib/CW.pm | 5 +- lib/CW/Controller.pm | 2 +- lib/CW/Controller/Index.pm | 16 +- lib/CW/Layout.pm | 18 + lib/CW/Layout/Default.pm | 134 ++ lib/CW/Migration.pm | 72 + lib/CW/Migration/01.pm | 21 + lib/CW/Migration/02.pm | 21 + lib/CW/Migration/03.pm | 60 + lib/CW/Migration/04.pm | 25 + lib/CW/Plack/Request.pm | 9 +- lib/CW/Plack/Response.pm | 43 +- lib/CW/Template.pm | 20 + lib/CW/Template/Boards.pm | 11 + lib/CW/Template/Index.pm | 27 + public/robots.txt | 164 ++ public/static/css/cw.css | 4 + public/static/css/shared.css | 58 + public/static/html/demo.html | 33 + public/static/images/badges/admin.png | Bin 0 -> 420 bytes public/static/images/cw.svg | 663 +++++++ public/static/images/t.svg | 2406 +++++++++++++++++++++++++ 25 files changed, 3886 insertions(+), 27 deletions(-) create mode 100644 lib/CW/Layout.pm create mode 100644 lib/CW/Layout/Default.pm create mode 100644 lib/CW/Migration.pm create mode 100644 lib/CW/Migration/01.pm create mode 100644 lib/CW/Migration/02.pm create mode 100644 lib/CW/Migration/03.pm create mode 100644 lib/CW/Migration/04.pm create mode 100644 lib/CW/Template.pm create mode 100644 lib/CW/Template/Boards.pm create mode 100644 lib/CW/Template/Index.pm create mode 100644 public/robots.txt create mode 100644 public/static/css/cw.css create mode 100644 public/static/css/shared.css create mode 100644 public/static/html/demo.html create mode 100644 public/static/images/badges/admin.png create mode 100644 public/static/images/cw.svg create mode 100644 public/static/images/t.svg diff --git a/README.pod b/README.pod index e69de29..c6b80d6 100644 --- a/README.pod +++ b/README.pod @@ -0,0 +1,66 @@ + +=pod + +=head1 L (CW) + +A human-first maker-space social platform. AI has destroyed what was left of the "old" internet, compiled.world +aims to take some of it back. + +=head2 Human First + +All AI/LLM/Slop discussion and submission is entirely banned. This is a space for humans by humans. + +=head2 Content + +All content must be human made. Discourse is rarely moderated outside of that. + +Anything illegal, including images, links, etc. Will result in a permanent ban, for both your account and your IP. + +=head2 Membership + +Users can L anonymously, however admission is entirely by admin discretion. +Admins democratically vote in users based on their sign-up forms. + +=head2 Badges + +compiled.world has ~100 badges for users to earn. They're entirely cosmetic, however we find it useful for differentiating users skillsets. + +=head2 Boards + +=over + +=item * + +/t/ - Programming, Technology, Computing + +=item * + +/b/ - Money, Finance, Business + +=item * + +/a/ - Art of any form + +=item * + +/s/ - Maths & Sciences + +=item * + +/g/ - Games (video, tabletops, etc) + +=item * + +/m/ - Feedback and complaints + +=item * + +More boards will be added over time + +=back + +=head2 LICENSE + +compiled.world uses a AGPLv3.0 license. See the L file at the root of this project. + +=cut diff --git a/bin/compiled.world.psgi b/bin/compiled.world.psgi index 9308755..06b2f4b 100644 --- a/bin/compiled.world.psgi +++ b/bin/compiled.world.psgi @@ -5,16 +5,19 @@ use 5.036; use strict; use warnings; -use FindBin; use Plack::Builder; +use Plack::Session::Store::DBI; + use DBIx::Connector; use HTML::Composer; - +use FindBin; +use utf8::all; use lib "$FindBin::Bin/../lib"; use CW; use CW::Config; use CW::Router; +use CW::Migration; my $h = HTML::Composer->new(); my $conf = CW::Config->new; @@ -26,16 +29,24 @@ my $conn = DBIx::Connector->new( ); my $router = CW::Router->new(); - $router->get('/')->action('Index#index'); +CW::Migration->migrate( $conn->dbh ); + builder { - sub { - CW->new( - dbh => $conn->dbh, - environment => $conf->get('environment'), - h => $h, - router => $router - )->app->(@_); - } + enable 'Session', store => Plack::Session::Store::DBI->new( + get_dbh => sub { + return $conn->dbh; + } + ); + enable 'Static', path => sub { + /^\/robots\.txt/ or /^\/static\//; + }, + root => "./public"; + CW->new( + dbh => $conn->dbh, + environment => $conf->get('environment'), + h => $h, + router => $router + )->app; }; diff --git a/cpanfile b/cpanfile index c33b5d6..1b2987a 100644 --- a/cpanfile +++ b/cpanfile @@ -1,5 +1,7 @@ requires "HTML::Composer"; requires "Plack"; +requires "Plack::Middleware::Session"; requires "Moo"; requires "Starman"; requires "Router::Simple"; +requires "utf8::all"; diff --git a/lib/CW.pm b/lib/CW.pm index 14ca9d0..9a0a1fe 100644 --- a/lib/CW.pm +++ b/lib/CW.pm @@ -1,7 +1,7 @@ - # This is the main class for compiled.world, it is instantiated every request # Routes, database connections, and other data around the request lifecycle are passed # as attributes, and should not be instantiated per request. + package CW; use Moo; @@ -9,6 +9,7 @@ use Plack::Request; use CW::Plack::Request; use CW::Plack::Response; +use CW::Migration; has h => ( is => 'ro', @@ -46,7 +47,7 @@ sub app { my $route = $match->{route}; my $pkg = $route->pkg; my $action = $route->action; - $pkg->new->$action( $request, $response ); + $pkg->new( dbh => $self->dbh )->$action( $request, $response ); } else { $response->status(404); diff --git a/lib/CW/Controller.pm b/lib/CW/Controller.pm index 835da81..6466b11 100644 --- a/lib/CW/Controller.pm +++ b/lib/CW/Controller.pm @@ -2,6 +2,6 @@ package CW::Controller; use Moo; -has dbh => ( is => 'ro' ); +has dbh => ( is => 'ro', required => 1 ); 1; diff --git a/lib/CW/Controller/Index.pm b/lib/CW/Controller/Index.pm index 403a8bc..0903852 100644 --- a/lib/CW/Controller/Index.pm +++ b/lib/CW/Controller/Index.pm @@ -2,19 +2,17 @@ package CW::Controller::Index; use Moo; -with 'CW::Controller'; +extends 'CW::Controller'; sub index { my ( $self, $request, $response ) = @_; - $response->template( - [ - head => [], - body => [ - h1 => ["Foo Bar!"] - ] - ] - ); + my $boards = + $self->dbh->selectall_arrayref( q[SELECT * FROM boards ORDER BY id], + { Slice => {} } ); + + $response->template( 'Index', + { title => 'compiled.world | home', boards => $boards } ); } 1; diff --git a/lib/CW/Layout.pm b/lib/CW/Layout.pm new file mode 100644 index 0000000..5db8fed --- /dev/null +++ b/lib/CW/Layout.pm @@ -0,0 +1,18 @@ +package CW::Layout; + +use Moo; +use Types::Standard qw(HashRef InstanceOf); + +has template => ( + is => 'ro', + required => 1, + isa => InstanceOf ['CW::Template'] +); + +has ctx => ( + is => 'ro', + required => 1, + isa => HashRef +); + +1; diff --git a/lib/CW/Layout/Default.pm b/lib/CW/Layout/Default.pm new file mode 100644 index 0000000..fda0601 --- /dev/null +++ b/lib/CW/Layout/Default.pm @@ -0,0 +1,134 @@ +package CW::Layout::Default; + +use Moo; + +extends 'CW::Layout'; + +my $HACKERSPACE_LINK = "https://en.wikipedia.org/wiki/Hackerspace"; + +sub render { + my ($self) = @_; + + my $ctx = $self->ctx // {}; + + if ( !$ctx->{nav} ) { + $ctx->{nav} = []; + my @path = split '/', $ctx->{env}->{PATH_INFO}; + + if (@path) { + my $nav = []; + for ( 0 .. $#path ) { + my $text = $path[$_]; + my @prev = @path[ 0 .. $_ ]; + push @$nav, + { + text => $text, + path => '/' . join( '/', ( @prev, $text ) ) + }; + } + } + } + + my @additional_links; + if ( !$ctx->{user} ) { + push @additional_links, $self->_make_nav_link("login"); + } + else { + if ( $ctx->{user}->is_admin ) { + push @additional_links, $self->_make_nav_link("admin"); + } + + push @additional_links, $self->_make_nav_link("logout"); + } + + my $board = $ctx->{board} // 'cw'; + + return [ + head => [ + title => [ $self->ctx->{title} ], + meta => { charset => 'UTF-8' }, + meta => { + name => 'viewport', + content => 'width=device-width, initial-scale=1.0' + }, + meta => { + name => 'description', + content => ( + $self->ctx->{description} + // 'compiled.world, a gated hackerspace ' + . 'for humans to interact with humans' + ) + }, + meta => { + name => 'robots', + content => 'index, follow' + }, + link => { + href => '/static/css/shared.css', + rel => 'stylesheet' + }, + link => { + href => '/static/css/' . $board . '.css', + rel => 'stylesheet' + } + ], + body => [ + header => [ + div => [ + div => { + class => "header-wrapper" + } => [ + img => { + src => "/static/images/$board.svg", + class => "logo" + }, + div => [ + a => { + href => "/", + style => "text-decoration: none" + } => [ + h1 => { style => "margin: 0" } => + ["compiled.world"] + ], + p => { style => "margin-top: 0" } => [ + "A digital ", + a => { + href => $HACKERSPACE_LINK + } => ["hackerspace"], + " for human beings" + ], + nav => { class => "navbar" } => [ + ( + map { $self->_make_nav_link($_) } + qw(recent popular badges about) + ), + @additional_links, + ] + ] + ], + ] + ], + main => [ + nav => { class => "quicknav" } => [ + map { + ( + li => [ + "/", + a => { href => $_->{path} } => [ $_->{text} ] + ] + ) + } @{ $ctx->{nav} } + ], + div => $self->template->render + ], + footer => [] + ] + ]; +} + +sub _make_nav_link { + my ( $self, $text ) = @_; + return a => { class => "navbar-link", href => "/$text" } => [$text]; +} + +1; diff --git a/lib/CW/Migration.pm b/lib/CW/Migration.pm new file mode 100644 index 0000000..8fe89c0 --- /dev/null +++ b/lib/CW/Migration.pm @@ -0,0 +1,72 @@ +package CW::Migration; + +use Moo; +use feature qw(state say); +use File::Find; + +has dbh => ( + is => 'ro', + required => 1 +); + +sub migrate { + my ($self) = @_; + + return $self->_perform_migrations(pop) + if !ref($self) && $self eq __PACKAGE__; + + $self->dbh->do(<<'SQL'); +CREATE TABLE IF NOT EXISTS migrations ( + id VARCHAR(4), + migration_sql TEXT NOT NULL, + PRIMARY KEY (id) +); +SQL + + my $id = $self->id; + my $already_migrated = $self->already_migrated; + + if ($already_migrated) { + say "[Migration] $id already migrated"; + return; + } + + $self->dbh->do( $self->sql ); + $self->dbh->do( "INSERT INTO migrations (id, migration_sql) VALUES (?, ?)", + undef, $id, $self->sql ); + say "[Migration] $id migrated successfully"; +} + +sub already_migrated { + my ($self) = @_; + + my ($exists_check) = + $self->dbh->selectrow_array( + "SELECT 1 FROM migrations WHERE id = ? LIMIT 1", + undef, $self->id ); + + return $exists_check; +} + +# static + +sub _perform_migrations { + my ( $class, $dbh ) = @_; + + find( + { + wanted => sub { + return unless /\.pm$/; + my @parts = split( /\/+/, $File::Find::name ); + my $file = $parts[-1]; + my $class = "CW::Migration::" . ( $file =~ s/\.pm$//r ); + eval "use $class"; + my $migration = $class->new( dbh => $dbh ); + $migration->migrate; + } + }, + 'lib/CW/Migration' + ); +} + +1; diff --git a/lib/CW/Migration/01.pm b/lib/CW/Migration/01.pm new file mode 100644 index 0000000..9020d6f --- /dev/null +++ b/lib/CW/Migration/01.pm @@ -0,0 +1,21 @@ +package CW::Migration::01; +use Moo; + +use FindBin; + +extends 'CW::Migration'; + +sub id { + return 1; +} + +sub sql { + return <<'SQL'; +CREATE TABLE sessions ( + id CHAR(72) PRIMARY KEY, + session_data TEXT +) +SQL +} + +1; diff --git a/lib/CW/Migration/02.pm b/lib/CW/Migration/02.pm new file mode 100644 index 0000000..cb05bb9 --- /dev/null +++ b/lib/CW/Migration/02.pm @@ -0,0 +1,21 @@ +package CW::Migration::02; + +use Moo; + +extends 'CW::Migration'; + +sub id { + return 2; +} + +sub sql { + return <<'SQL'; +CREATE TABLE boards ( + id VARCHAR(3) PRIMARY KEY, + name VARCHAR(100), + description TEXT +) +SQL +} + +1; diff --git a/lib/CW/Migration/03.pm b/lib/CW/Migration/03.pm new file mode 100644 index 0000000..7720f82 --- /dev/null +++ b/lib/CW/Migration/03.pm @@ -0,0 +1,60 @@ +package CW::Migration::03; + +use Moo; + +extends 'CW::Migration'; + +sub id { + return 3; +} + +sub sql { + my $boards = [ + { + id => 't', + name => 'Technology', + description => 'Programming, computing, hardware discussion' + }, + { + id => 'b', + name => 'Money', + description => 'Finance, money, and business discussion' + }, + { + id => 'a', + name => 'Art', + description => 'Human made art (all forms welcome) discussion' + }, + { + id => 's', + name => 'Math & Science', + description => 'Math and science discussion' + }, + { + id => 'g', + name => 'Games', + description => 'Video, Tabletop, TCG, gaming discussion' + }, + { + id => 'm', + name => 'Meta', + description => 'Discussion about compiled.world' + } + ]; + + my $values = join( + ",", + map { + "('" + . $_->{id} . "', '" + . $_->{name} . "', '" + . $_->{description} . "')" + } @$boards + ); + my $sql = + 'INSERT INTO boards (id, name, description) VALUES ' . $values . ';'; + + return $sql; +} + +1; diff --git a/lib/CW/Migration/04.pm b/lib/CW/Migration/04.pm new file mode 100644 index 0000000..371b47e --- /dev/null +++ b/lib/CW/Migration/04.pm @@ -0,0 +1,25 @@ +package CW::Migration::04; + +use Moo; + +extends 'CW::Migration'; + +sub id { + return 4; +} + +sub sql { + return q[CREATE TABLE users ( +id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, +name VARCHAR(24), +password VARCHAR(62), +admin BOOLEAN DEFAULT false, +active BOOLEAN DEFAULT false, +denied BOOLEAN DEFAULT false, +denied_reason TEXT, +join_request JSON NOT NULL +) +] +} + +1; diff --git a/lib/CW/Plack/Request.pm b/lib/CW/Plack/Request.pm index 0a19850..b4a316d 100644 --- a/lib/CW/Plack/Request.pm +++ b/lib/CW/Plack/Request.pm @@ -10,13 +10,18 @@ has h => ( has _request => ( is => 'ro', init_arg => 'request', - required => 1 + required => 1, + handles => [qw(env)] ); sub new_response { my ( $self, @args ) = @_; my $response = $self->_request->new_response(@args); - return CW::Plack::Response->new( response => $response, h => $self->h ); + return CW::Plack::Response->new( + request => $self, + response => $response, + h => $self->h + ); } 1; diff --git a/lib/CW/Plack/Response.pm b/lib/CW/Plack/Response.pm index ae19173..0d15f4e 100644 --- a/lib/CW/Plack/Response.pm +++ b/lib/CW/Plack/Response.pm @@ -1,6 +1,7 @@ package CW::Plack::Response; use Moo; +use Carp (); has _response => ( is => 'ro', @@ -9,15 +10,53 @@ has _response => ( handles => [qw(finalize status body headers header)] ); +has request => ( + is => 'ro', + required => 1, + handles => [qw(env)] +); + has h => ( is => 'ro', required => 1 ); sub template { - my ( $self, $template ) = @_; - my $html = $self->h->html($template); + my ( $self, $template, $ctx ) = @_; + + my $html; + + # If rendering inline + if ( ref($template) && ref($template) eq 'ARRAY' ) { + $html = $self->h->html($template); + } + elsif ( !ref($template) ) { + $ctx //= {}; + $ctx->{env} = $self->env; + + ## no strict [BuiltinFunctions::ProhibitStringyEval] + my $template_pkg = "CW::Template::$template"; + eval "use $template_pkg"; + + my $layout_pkg = + "CW::Layout::" . ( delete( $ctx->{layout} ) // 'Default' ); + ## no strict [BuiltinFunctions::ProhibitStringyEval] + eval "use $layout_pkg"; + + my $layout = $layout_pkg->new( + template => $template_pkg->new( ctx => $ctx ), + ctx => $ctx + ); + + $html = $self->h->html( $layout->render ); + } + else { + Carp::croak("Invalid value passed to template call: $template"); + } + + $self->_response->header( 'Content-Type' => 'text/html; encoding=utf-8' ); $self->_response->body($html); + return $self; } diff --git a/lib/CW/Template.pm b/lib/CW/Template.pm new file mode 100644 index 0000000..faf0f51 --- /dev/null +++ b/lib/CW/Template.pm @@ -0,0 +1,20 @@ +package CW::Template; + +use Moo; +use Types::Standard qw(HashRef); + +has ctx => ( + is => 'ro', + required => 1, + isa => HashRef +); + +sub render { + my ($self) = @_; + return [ + h1 => ["Undefined Template"], + p => [ "You should probably implement this template! " . ref($self) ] + ]; +} + +1; diff --git a/lib/CW/Template/Boards.pm b/lib/CW/Template/Boards.pm new file mode 100644 index 0000000..0e88677 --- /dev/null +++ b/lib/CW/Template/Boards.pm @@ -0,0 +1,11 @@ +package CW::Template::Boards; + +use Moo; + +extends 'CW::Template'; + +sub render { + my ($self) = @_; +} + +1; diff --git a/lib/CW/Template/Index.pm b/lib/CW/Template/Index.pm new file mode 100644 index 0000000..66639a8 --- /dev/null +++ b/lib/CW/Template/Index.pm @@ -0,0 +1,27 @@ +package CW::Template::Index; + +use Moo; + +extends 'CW::Template'; + +sub render { + my ($self) = @_; + + my @boards = @{ $self->ctx->{boards} }; + return [ + map { + ( + li => [ + a => { + href => '/' . $_->{id} . '/', + class => 'index-board-link' + } => [ '/' . $_->{id} . '/ - ' . $_->{name} ], + p => { class => 'index-board-description' } => + [ $_->{description} ] + ] + ) + } @boards + ]; +} + +1; diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..7d735aa --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,164 @@ +User-agent: AddSearchBot +User-agent: AgentTimes +User-agent: AI2Bot +User-agent: AI2Bot-DeepResearchEval +User-agent: Ai2Bot-Dolma +User-agent: aiHitBot +User-agent: AIWebIndex +User-agent: amazon-kendra +User-agent: amazon-QBusiness +User-agent: Amazonbot +User-agent: AmazonBuyForMe +User-agent: Amzn-SearchBot +User-agent: Amzn-User +User-agent: Andibot +User-agent: Anomura +User-agent: anthropic-ai +User-agent: ApifyBot +User-agent: ApifyWebsiteContentCrawler +User-agent: Applebot +User-agent: Applebot-Extended +User-agent: Aranet-SearchBot +User-agent: atlassian-bot +User-agent: Awario +User-agent: AzureAI-SearchBot +User-agent: bedrockbot +User-agent: bigsur.ai +User-agent: Bravebot +User-agent: Brightbot +User-agent: Brightbot 1.0 +User-agent: BuddyBot +User-agent: Bytespider +User-agent: CCBot +User-agent: Channel3Bot +User-agent: ChatGLM-Spider +User-agent: ChatGPT Agent +User-agent: ChatGPT-User +User-agent: Claude-Code +User-agent: Claude-SearchBot +User-agent: Claude-User +User-agent: Claude-Web +User-agent: ClaudeBot +User-agent: Cloudflare-AutoRAG +User-agent: CloudVertexBot +User-agent: Code +User-agent: cohere-ai +User-agent: cohere-training-data-crawler +User-agent: Cotoyogi +User-agent: CragCrawler +User-agent: Crawl4AI +User-agent: Crawlspace +User-agent: Cursor +User-agent: Datenbank Crawler +User-agent: DeepSeekBot +User-agent: Devin +User-agent: Diffbot +User-agent: DuckAssistBot +User-agent: Echobot Bot +User-agent: EchoboxBot +User-agent: ExaBot +User-agent: FacebookBot +User-agent: facebookexternalhit +User-agent: Factset_spyderbot +User-agent: FirecrawlAgent +User-agent: FriendlyCrawler +User-agent: GeistHaus-PageFetcher +User-agent: Gemini-Deep-Research +User-agent: Google-Agent +User-agent: Google-CloudVertexBot +User-agent: Google-Extended +User-agent: Google-Firebase +User-agent: Google-Gemini-CLI +User-agent: Google-NotebookLM +User-agent: GoogleAgent-Mariner +User-agent: GoogleAgent-URLContext +User-agent: GoogleOther +User-agent: GoogleOther-Image +User-agent: GoogleOther-Video +User-agent: GPTBot +User-agent: HenkBot +User-agent: iAskBot +User-agent: iaskspider +User-agent: iaskspider/2.0 +User-agent: ICC-Crawler +User-agent: ImagesiftBot +User-agent: imageSpider +User-agent: img2dataset +User-agent: ISSCyberRiskCrawler +User-agent: kagi-fetcher +User-agent: Kangaroo Bot +User-agent: Kimi-User +User-agent: KlaviyoAIBot +User-agent: KunatoCrawler +User-agent: laion-huggingface-processor +User-agent: LAIONDownloader +User-agent: LCC +User-agent: LinerBot +User-agent: Linguee Bot +User-agent: LinkupBot +User-agent: Manus-User +User-agent: meta-externalagent +User-agent: Meta-ExternalAgent +User-agent: meta-externalfetcher +User-agent: Meta-ExternalFetcher +User-agent: meta-webindexer +User-agent: MistralAI-User +User-agent: MistralAI-User/1.0 +User-agent: Mozilla-Tabstack +User-agent: MyCentralAIScraperBot +User-agent: NagetBot +User-agent: netEstate Imprint Crawler +User-agent: newsai +User-agent: NotebookLM +User-agent: NovaAct +User-agent: OAI-SearchBot +User-agent: omgili +User-agent: omgilibot +User-agent: OpenAI +User-agent: opencode +User-agent: Operator +User-agent: PanguBot +User-agent: Panscient +User-agent: panscient.com +User-agent: Perplexity-User +User-agent: PerplexityBot +User-agent: PetalBot +User-agent: PhindBot +User-agent: Poggio-Citations +User-agent: Poseidon Research Crawler +User-agent: QualifiedBot +User-agent: Querit-SearchBot +User-agent: QueritBot +User-agent: QuillBot +User-agent: quillbot.com +User-agent: SBIntuitionsBot +User-agent: Scrapy +User-agent: SemrushBot-OCOB +User-agent: SemrushBot-SWA +User-agent: Shap-User +User-agent: ShapBot +User-agent: Sidetrade indexer bot +User-agent: Spider +User-agent: TavilyBot +User-agent: Terra Cotta +User-agent: TerraCotta +User-agent: Thinkbot +User-agent: TikTokSpider +User-agent: Timpibot +User-agent: TongyiBot +User-agent: Trae +User-agent: TwinAgent +User-agent: UseAI +User-agent: VelenPublicWebCrawler +User-agent: WARDBot +User-agent: Webzio-Extended +User-agent: webzio-extended +User-agent: wpbot +User-agent: WRTNBot +User-agent: YaK +User-agent: YandexAdditional +User-agent: YandexAdditionalBot +User-agent: YiyanBot +User-agent: YouBot +User-agent: ZanistaBot +Disallow: / diff --git a/public/static/css/cw.css b/public/static/css/cw.css new file mode 100644 index 0000000..ec6378a --- /dev/null +++ b/public/static/css/cw.css @@ -0,0 +1,4 @@ +h1, h2, h3, h4, h5, p, a { + color: darkgreen; + text-decoration-color: darkgreen; +} diff --git a/public/static/css/shared.css b/public/static/css/shared.css new file mode 100644 index 0000000..99c2f5a --- /dev/null +++ b/public/static/css/shared.css @@ -0,0 +1,58 @@ +/* Background */ +body { + background-color: #FBFFFF; +} + +/* Typography */ +* { + font-size: 1rem; +} + +h1, h2, h3, h4, a, p { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +h1 { + font-size: 2.5rem; +} + +p, a { + font-size: calc(1.33rem + 0.01vh); +} + +/* Header */ +.logo { + width: 17.33rem; + height: 11.99rem; +} + +.header-wrapper { + display: flex; + flex-direction: row; +} + +/* Navigation */ +.navbar-link { + font-size: 1.2rem; +} + +nav.navbar { + display: flex; + flex-direction: row; + align-content: left; + align-items: left; + justify-content: space-between; +} + +/* Mobile */ +@media screen and (max-width: 480px) { + .header-wrapper { + display: flex; + flex-direction: column; + align-content: center; + align-items: center; + text-align: center; + } +} + diff --git a/public/static/html/demo.html b/public/static/html/demo.html new file mode 100644 index 0000000..11b13cf --- /dev/null +++ b/public/static/html/demo.html @@ -0,0 +1,33 @@ + + + + compiled.world | demo + + + +

h1

+

h2

+

h3

+

h4

+ +

+ This is a paragraph. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

+ + + + + + diff --git a/public/static/images/badges/admin.png b/public/static/images/badges/admin.png new file mode 100644 index 0000000000000000000000000000000000000000..bd9b2c9765365bfab7506c27c7de15cf01fdc936 GIT binary patch literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG z?e4QcO_l1FXiEWwBuiW) zN}Tg^b5rw57@Uhz6H8K46v{J8G8EiBeFMT9`NV;W4tu&dhFA!;PCU(f$Uwj)cm4{+ zISU!I9>}a(;9=nw;=riTw{Z$zk)vaB=Gh}}^ro%3aj)|H{FVn>u3PHA@KbSF&GEsu z_U7Nx)oE9cicRx1IoW@pN%y4Jg+sf&!*@K_# z+PJ4cM8c6jO>n;2$Muhxm~Ec<$Ss@j!_neLKF<#e`J`Hxox%>UwKnZOa3YLRP3K@t zHqYXHLJ0>aZ`q)=K}9NuE2@)K`T4fn&-N{-UdVq)tnNv(yZdvXcNjcf{an^LB{Ts5 DVbPNZ literal 0 HcmV?d00001 diff --git a/public/static/images/cw.svg b/public/static/images/cw.svg new file mode 100644 index 0000000..323e842 --- /dev/null +++ b/public/static/images/cw.svg @@ -0,0 +1,663 @@ + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + diff --git a/public/static/images/t.svg b/public/static/images/t.svg new file mode 100644 index 0000000..bdc5577 --- /dev/null +++ b/public/static/images/t.svg @@ -0,0 +1,2406 @@ + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +