Initial commit of Astro project: drizzle database
@ -0,0 +1,29 @@
|
|||||||
|
# build output
|
||||||
|
dist/
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# vscode
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# local data
|
||||||
|
*.local*
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Drizzle build output
|
||||||
|
out/
|
@ -0,0 +1,47 @@
|
|||||||
|
# Astro Starter Kit: Minimal
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm create astro@latest -- --template minimal
|
||||||
|
```
|
||||||
|
|
||||||
|
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
|
||||||
|
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
|
||||||
|
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json)
|
||||||
|
|
||||||
|
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||||
|
|
||||||
|
## 🚀 Project Structure
|
||||||
|
|
||||||
|
Inside of your Astro project, you'll see the following folders and files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/
|
||||||
|
├── public/
|
||||||
|
├── src/
|
||||||
|
│ └── pages/
|
||||||
|
│ └── index.astro
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||||
|
|
||||||
|
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||||
|
|
||||||
|
Any static assets, like images, can be placed in the `public/` directory.
|
||||||
|
|
||||||
|
## 🧞 Commands
|
||||||
|
|
||||||
|
All commands are run from the root of the project, from a terminal:
|
||||||
|
|
||||||
|
| Command | Action |
|
||||||
|
| :------------------------ | :----------------------------------------------- |
|
||||||
|
| `npm install` | Installs dependencies |
|
||||||
|
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||||
|
| `npm run build` | Build your production site to `./dist/` |
|
||||||
|
| `npm run preview` | Preview your build locally, before deploying |
|
||||||
|
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||||
|
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||||
|
|
||||||
|
## 👀 Want to learn more?
|
||||||
|
|
||||||
|
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import preact from "@astrojs/preact";
|
||||||
|
|
||||||
|
import node from "@astrojs/node";
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
output: "hybrid",
|
||||||
|
outDir: "out/astro",
|
||||||
|
server: {
|
||||||
|
port: 3000
|
||||||
|
},
|
||||||
|
integrations: [preact()],
|
||||||
|
adapter: node({
|
||||||
|
mode: "standalone"
|
||||||
|
})
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
schema: "src/db/schema.ts",
|
||||||
|
driver: "better-sqlite",
|
||||||
|
out: "out/drizzle",
|
||||||
|
});
|
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "website",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "run-s drizzle:* astro:dev",
|
||||||
|
"build": "run-s drizzle:generate astro:build",
|
||||||
|
"astro:dev": "astro dev",
|
||||||
|
"astro:build": "astro check && astro build",
|
||||||
|
"drizzle:generate": "drizzle-kit generate:sqlite",
|
||||||
|
"drizzle:migrate": "tsx src/db/migrate.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/check": "^0.5.6",
|
||||||
|
"@astrojs/node": "^8.2.1",
|
||||||
|
"@astrojs/preact": "^3.1.1",
|
||||||
|
"@fontsource/open-sans": "^5.0.24",
|
||||||
|
"@fontsource/source-code-pro": "^5.0.16",
|
||||||
|
"@fontsource/source-sans-pro": "^5.0.8",
|
||||||
|
"astro": "^4.4.6",
|
||||||
|
"better-sqlite3": "^9.4.3",
|
||||||
|
"drizzle-orm": "^0.29.4",
|
||||||
|
"preact": "^10.19.6",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/better-sqlite3": "^7.6.9",
|
||||||
|
"drizzle-kit": "^0.20.14",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"sass": "^1.71.1",
|
||||||
|
"tsx": "^4.7.1"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||||
|
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||||
|
<style>
|
||||||
|
path { fill: #000; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path { fill: #FFF; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 749 B |
@ -0,0 +1,87 @@
|
|||||||
|
<svg width="1000" height="500" viewBox="0 0 1000 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="80" y="190" width="60" height="120" fill="#1E6733"/>
|
||||||
|
<rect x="160" y="50" width="150" height="60" fill="#1E6733"/>
|
||||||
|
<rect x="140" y="90" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="140" y="200" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="140" y="410" width="10" height="20" fill="#ECC333"/>
|
||||||
|
<rect x="140" y="350" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="240" y="110" width="70" height="10" fill="#ECC333"/>
|
||||||
|
<rect x="250" y="130" width="60" height="130" fill="#1E6733"/>
|
||||||
|
<rect x="340" y="50" width="60" height="120" fill="#1E6733"/>
|
||||||
|
<rect x="340" y="190" width="60" height="120" fill="#1E6733"/>
|
||||||
|
<rect x="590" y="190" width="60" height="120" fill="#1E6733"/>
|
||||||
|
<rect x="690" y="180" width="60" height="120" fill="#1E6733"/>
|
||||||
|
<rect x="690" y="310" width="60" height="140" fill="#1E6733"/>
|
||||||
|
<rect x="690" y="50" width="60" height="120" fill="#1E6733"/>
|
||||||
|
<rect x="590" y="320" width="60" height="130" fill="#1E6733"/>
|
||||||
|
<rect x="590" y="50" width="60" height="130" fill="#1E6733"/>
|
||||||
|
<rect x="420" y="240" width="150" height="60" fill="#1E6733"/>
|
||||||
|
<rect x="340" y="320" width="60" height="130" fill="#1E6733"/>
|
||||||
|
<rect x="240" y="140" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="350" y="170" width="40" height="10" fill="#ECC333"/>
|
||||||
|
<rect x="330" y="330" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="160" y="200" width="80" height="60" fill="#1E6733"/>
|
||||||
|
<rect x="650" y="200" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="750" y="330" width="10" height="60" fill="#ECC333"/>
|
||||||
|
<rect x="800" y="450" width="40" height="10" fill="#ECC333"/>
|
||||||
|
<rect x="850" y="450" width="30" height="10" fill="#ECC333"/>
|
||||||
|
<rect x="750" y="90" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="810" y="110" width="60" height="10" fill="#ECC333"/>
|
||||||
|
<rect x="580" y="330" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="580" y="60" width="10" height="50" fill="#ECC333"/>
|
||||||
|
<rect x="710" y="420" width="20" height="20" fill="#C4C4C4"/>
|
||||||
|
<rect x="710" y="390" width="20" height="20" fill="#C4C4C4"/>
|
||||||
|
<rect x="100" y="280" width="20" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="100" y="260" width="20" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="110" y="210" width="20" height="20" fill="#C4C4C4"/>
|
||||||
|
<rect x="350" y="430" width="40" height="10" fill="#303030"/>
|
||||||
|
<rect x="350" y="410" width="40" height="10" fill="#303030"/>
|
||||||
|
<rect x="350" y="390" width="40" height="10" fill="#303030"/>
|
||||||
|
<rect x="700" y="70" width="20" height="40" fill="#303030"/>
|
||||||
|
<rect x="700" y="120" width="20" height="40" fill="#303030"/>
|
||||||
|
<rect x="610" y="280" width="20" height="20" fill="#C4C4C4"/>
|
||||||
|
<rect x="600" y="240" width="20" height="20" fill="#C4C4C4"/>
|
||||||
|
<rect x="430" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="430" y="265" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="430" y="280" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="445" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="445" y="265" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="460" y="280" width="40" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="475" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="475" y="265" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="505" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="505" y="265" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="520" y="265" width="10" height="15" fill="#C4C4C4"/>
|
||||||
|
<rect x="505" y="280" width="25" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="535" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="535" y="265" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="535" y="280" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="445" y="280" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="460" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="460" y="265" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="490" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="490" y="265" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="520" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="550" y="250" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="550" y="265" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="550" y="280" width="10" height="10" fill="#C4C4C4"/>
|
||||||
|
<rect x="620" y="210" width="20" height="20" fill="#C4C4C4"/>
|
||||||
|
<rect x="370" y="70" width="20" height="30" fill="#303030"/>
|
||||||
|
<rect x="370" y="110" width="20" height="30" fill="#303030"/>
|
||||||
|
<rect x="870" y="440" width="40" height="10" transform="rotate(-90 870 440)" fill="#303030"/>
|
||||||
|
<rect x="890" y="440" width="40" height="10" transform="rotate(-90 890 440)" fill="#303030"/>
|
||||||
|
<rect x="810" y="440" width="40" height="10" transform="rotate(-90 810 440)" fill="#303030"/>
|
||||||
|
<rect x="790" y="440" width="40" height="10" transform="rotate(-90 790 440)" fill="#303030"/>
|
||||||
|
<rect x="270" y="100" width="40" height="10" transform="rotate(-90 270 100)" fill="#303030"/>
|
||||||
|
<rect x="290" y="100" width="40" height="10" transform="rotate(-90 290 100)" fill="#303030"/>
|
||||||
|
<rect x="190" y="100" width="40" height="10" transform="rotate(-90 190 100)" fill="#303030"/>
|
||||||
|
<rect x="170" y="100" width="40" height="10" transform="rotate(-90 170 100)" fill="#303030"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M140 60V170C134.477 170 130 174.477 130 180H90C90 174.477 85.5228 170 80 170V60C85.5228 60 90 55.5228 90 50H130C130 55.5228 134.477 60 140 60Z" fill="#1E6733"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M130 320H90C90 325.523 85.5229 330 80 330V440C85.5229 440 90 444.477 90 450H130C130 444.477 134.477 440 140 440V330C134.477 330 130 325.523 130 320Z" fill="#1E6733"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M770 60C775.523 60 780 55.5228 780 50H910C910 55.5228 914.477 60 920 60V100C914.477 100 910 104.477 910 110H780C780 104.477 775.523 100 770 100V60Z" fill="#1E6733"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M770 400C775.523 400 780 395.523 780 390H910C910 395.523 914.477 400 920 400V440C914.477 440 910 444.477 910 450H780C780 444.477 775.523 440 770 440V400Z" fill="#1E6733"/>
|
||||||
|
<rect x="750" y="190" width="10" height="40" fill="#ECC333"/>
|
||||||
|
<rect x="750" y="240" width="10" height="20" fill="#ECC333"/>
|
||||||
|
<rect x="400" y="200" width="10" height="40" fill="#ECC333"/>
|
||||||
|
<rect x="400" y="250" width="10" height="20" fill="#ECC333"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 765 B |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 1.9 MiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 4.6 MiB |
@ -0,0 +1,8 @@
|
|||||||
|
<svg width="100%" height="2rem" viewBox="0 0 1 1" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<pattern id="zig-zag" x="0" y="0" width="2" height="1">
|
||||||
|
<path fill="#C2A8EB" d="M 0,0 L 1,1 L 2,0 L 0,0"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect fill="url(#zig-zag)" x="0" y="0" width="100%" height="1" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 344 B |
@ -0,0 +1,5 @@
|
|||||||
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||||
|
import Database from "better-sqlite3";
|
||||||
|
|
||||||
|
const sql = new Database("out/website.sqlite");
|
||||||
|
export const db = drizzle(sql);
|
@ -0,0 +1,4 @@
|
|||||||
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
||||||
|
import { db } from "./index";
|
||||||
|
|
||||||
|
migrate(db, { migrationsFolder: "out/drizzle" });
|
@ -0,0 +1,43 @@
|
|||||||
|
import { sql } from "drizzle-orm";
|
||||||
|
import { text, sqliteTable } from "drizzle-orm/sqlite-core";
|
||||||
|
|
||||||
|
export const users = sqliteTable("users", {
|
||||||
|
// id è l'id unico di questo utente, non è modificabile una volta creato l'utente.
|
||||||
|
id: text("id")
|
||||||
|
.primaryKey()
|
||||||
|
.default(sql`(lower(hex(randomblob(16))))`),
|
||||||
|
|
||||||
|
// Username è il nome leggibile di questo utente utilizzato anche per le
|
||||||
|
// route per singolo utente, deve essere unico nel sito.
|
||||||
|
//
|
||||||
|
// NOTE: Quando un utente accede per la prima volta di default gli viene
|
||||||
|
// chiesto se usare quello dell'account che sta usando o se cambiarlo
|
||||||
|
// (in teoria non dovrebbe essere un problema poterlo modificare
|
||||||
|
// successivamente).
|
||||||
|
username: text("username").unique().notNull(),
|
||||||
|
|
||||||
|
// FullName da mostrare in giro per il sito
|
||||||
|
fullName: text("fullname"),
|
||||||
|
|
||||||
|
// Email per eventuale contatto
|
||||||
|
email: text("email"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type User = typeof users.$inferSelect; // return type when queried
|
||||||
|
export type InsertUser = typeof users.$inferInsert; // insert type
|
||||||
|
|
||||||
|
export const accounts = sqliteTable("accounts", {
|
||||||
|
// id è l'id unico di questo account, non è modificabile una volta creato l'account.
|
||||||
|
id: text("id").primaryKey(),
|
||||||
|
|
||||||
|
userId: text("userId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => users.id),
|
||||||
|
|
||||||
|
provider: text("provider").$type<"poisson" | "ateneo">().notNull(),
|
||||||
|
|
||||||
|
token: text("token").notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Account = typeof accounts.$inferSelect; // return type when queried
|
||||||
|
export type InsertAccount = typeof accounts.$inferInsert; // insert type
|
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="astro/client" />
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
import PageLayout from './PageLayout.astro'
|
||||||
|
|
||||||
|
const { frontmatter } = Astro.props
|
||||||
|
---
|
||||||
|
|
||||||
|
<PageLayout {...frontmatter} />
|
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
import '@fontsource/open-sans/latin.css'
|
||||||
|
import '@fontsource/source-sans-pro/latin.css'
|
||||||
|
import '@fontsource/source-code-pro/latin.css'
|
||||||
|
|
||||||
|
import '../styles/main.scss'
|
||||||
|
|
||||||
|
const { title, description, thumbnail, pageName } = Astro.props
|
||||||
|
---
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
|
<meta property="og:title" content={title ?? 'PHC'} />
|
||||||
|
<meta property="og:description" content={description ?? 'Sito web del PHC'} />
|
||||||
|
{thumbnail && <meta property="og:image" content={thumbnail} />}
|
||||||
|
|
||||||
|
<link rel="icon" type="image/png" sizes="512x512" href="/assets/icon.png" />
|
||||||
|
|
||||||
|
<title>{title}</title>
|
||||||
|
</head>
|
||||||
|
<body class:list={['page-' + (pageName ?? 'unknown')]}>
|
||||||
|
<slot />
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from './BaseLayout.astro'
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout {...Astro.props}>
|
||||||
|
<header>
|
||||||
|
<div class="logo">
|
||||||
|
<img src="/images/logo-circuit-board.svg" alt="phc logo" />
|
||||||
|
</div>
|
||||||
|
<div class="links">
|
||||||
|
<a role="button" href="#">Utenti</a>
|
||||||
|
<a role="button" href="#">Notizie</a>
|
||||||
|
<a role="button" href="#">Progetti</a>
|
||||||
|
<a role="button" href="#">About</a>
|
||||||
|
<a class="primary" role="button" href="#">Accedi</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<slot />
|
||||||
|
</main>
|
||||||
|
<footer>© PHC 2023</footer>
|
||||||
|
</BaseLayout>
|
@ -0,0 +1,199 @@
|
|||||||
|
---
|
||||||
|
import PageLayout from '../layouts/PageLayout.astro'
|
||||||
|
---
|
||||||
|
|
||||||
|
<PageLayout pageName="homepage">
|
||||||
|
<section class="principal">
|
||||||
|
<div class="circuit-layer">
|
||||||
|
<canvas id="circuits-art"></canvas>
|
||||||
|
<script src="../scripts/circuits-art.ts"></script>
|
||||||
|
</div>
|
||||||
|
<div class="logo">
|
||||||
|
<img src="/images/logo-circuit-board.svg" alt="phc logo" />
|
||||||
|
</div>
|
||||||
|
<div class="whats-phc">
|
||||||
|
<div class="title">Cos'è il PHC?</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Totam vero deserunt tempore reprehenderit atque, voluptate
|
||||||
|
dolorum excepturi libero pariatur sequi?
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Laboriosam soluta ab a illum mollitia quaerat quia, veniam consequatur expedita dolore autem reiciendis quae rem
|
||||||
|
excepturi optio? Maiores, hic?
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Exercitationem error atque amet. Tempora earum nemo eveniet
|
||||||
|
aspernatur quam quas, doloribus expedita quisquam dignissimos cupiditate inventore a modi optio harum veritatis,
|
||||||
|
adipisci ab ullam distinctio odio quod delectus ipsum, rerum animi.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="news">
|
||||||
|
<div class="zig-zag">
|
||||||
|
<svg width="100%" height="2rem" viewBox="0 0 1 1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
|
||||||
|
<defs>
|
||||||
|
<pattern id="zig-zag-1" x="0" y="0" width="2" height="1" patternUnits="userSpaceOnUse">
|
||||||
|
<path fill="#C2A8EB" d="M 0,1 L 1,0 L 2,1 L 0,1"></path>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect fill="url(#zig-zag-1)" x="0" y="0" width="1000" height="1"></rect>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title">Ultime Notizie</div>
|
||||||
|
|
||||||
|
<div class="news-list">
|
||||||
|
<div class="news">
|
||||||
|
<div class="title">Lorem ipsum dolor sit.</div>
|
||||||
|
<div class="abstract">
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis reprehenderit porro omnis enim deleniti esse quos,
|
||||||
|
architecto adipisci veritatis, iusto perferendis aperiam recusandae exercitationem doloribus, illum commodi
|
||||||
|
voluptatem pariatur eius!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Impedit ut quod aspernatur vitae vero incidunt cupiditate perferendis explicabo sunt possimus rerum expedita dicta
|
||||||
|
nesciunt enim mollitia iure ullam aut, pariatur, at cumque. Nemo obcaecati eaque recusandae fugit sed!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="news">
|
||||||
|
<div class="title">Tempore provident impedit libero?</div>
|
||||||
|
<div class="abstract">
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis reprehenderit porro omnis enim deleniti esse quos,
|
||||||
|
architecto adipisci veritatis, iusto perferendis aperiam recusandae exercitationem doloribus, illum commodi
|
||||||
|
voluptatem pariatur eius!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Impedit ut quod aspernatur vitae vero incidunt cupiditate perferendis explicabo sunt possimus rerum expedita dicta
|
||||||
|
nesciunt enim mollitia iure ullam aut, pariatur, at cumque. Nemo obcaecati eaque recusandae fugit sed!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Impedit ut quod aspernatur vitae vero incidunt cupiditate perferendis explicabo sunt possimus rerum expedita dicta
|
||||||
|
nesciunt enim mollitia iure ullam aut, pariatur, at cumque. Nemo obcaecati eaque recusandae fugit sed!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Impedit ut quod aspernatur vitae vero incidunt cupiditate perferendis explicabo sunt possimus rerum expedita dicta
|
||||||
|
nesciunt enim mollitia iure ullam aut, pariatur, at cumque. Nemo obcaecati eaque recusandae fugit sed!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="news">
|
||||||
|
<div class="title">Alias molestias consectetur quam</div>
|
||||||
|
<div class="abstract">
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis reprehenderit porro omnis enim deleniti esse quos,
|
||||||
|
architecto adipisci veritatis, iusto perferendis aperiam recusandae exercitationem doloribus, illum commodi
|
||||||
|
voluptatem pariatur eius!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="news">
|
||||||
|
<div class="title">Inventore dignissimos sapiente nulla</div>
|
||||||
|
<div class="abstract">
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Debitis reprehenderit porro omnis enim deleniti esse quos,
|
||||||
|
architecto adipisci veritatis, iusto perferendis aperiam recusandae exercitationem doloribus, illum commodi
|
||||||
|
voluptatem pariatur eius!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Impedit ut quod aspernatur vitae vero incidunt cupiditate perferendis explicabo sunt possimus rerum expedita dicta
|
||||||
|
nesciunt enim mollitia iure ullam aut, pariatur, at cumque. Nemo obcaecati eaque recusandae fugit sed!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a class="primary" href="#" role="button">Vai all'Archivio</a>
|
||||||
|
</section>
|
||||||
|
<section class="projects">
|
||||||
|
<div class="zig-zag">
|
||||||
|
<svg width="100%" height="2rem" viewBox="0 0 1 1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
|
||||||
|
<defs>
|
||||||
|
<pattern id="zig-zag-2" x="0" y="0" width="2" height="1" patternUnits="userSpaceOnUse">
|
||||||
|
<path fill="#f5f2cc" d="M 0,1 L 1,0 L 2,1 L 0,1"></path>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect fill="url(#zig-zag-2)" x="0" y="0" width="1000" height="1"></rect>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title">Progetti</div>
|
||||||
|
|
||||||
|
<div class="project-list">
|
||||||
|
<a href="#">
|
||||||
|
<div class="project">
|
||||||
|
<div class="image">
|
||||||
|
<div class="box"></div>
|
||||||
|
</div>
|
||||||
|
<div class="title">Gitea</div>
|
||||||
|
<div class="description">
|
||||||
|
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Voluptatibus quam iure dolor. Excepturi facere, ipsa
|
||||||
|
accusantium labore explicabo quaerat incidunt.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#">
|
||||||
|
<div class="project">
|
||||||
|
<div class="image">
|
||||||
|
<div class="box"></div>
|
||||||
|
</div>
|
||||||
|
<div class="title">Zulip</div>
|
||||||
|
<div class="description">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Repellendus, veritatis.</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#">
|
||||||
|
<div class="project">
|
||||||
|
<div class="image">
|
||||||
|
<div class="box"></div>
|
||||||
|
</div>
|
||||||
|
<div class="title">Orario</div>
|
||||||
|
<div class="description">
|
||||||
|
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Voluptatibus quam iure dolor. Excepturi facere, ipsa
|
||||||
|
accusantium labore explicabo quaerat incidunt.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#">
|
||||||
|
<div class="project">
|
||||||
|
<div class="image">
|
||||||
|
<div class="box"></div>
|
||||||
|
</div>
|
||||||
|
<div class="title">Problemi</div>
|
||||||
|
<div class="description">
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Non, hic libero beatae voluptatem incidunt.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="#">
|
||||||
|
<div class="project">
|
||||||
|
<div class="image">
|
||||||
|
<div class="box"></div>
|
||||||
|
</div>
|
||||||
|
<div class="title">Cluster "Steffè"</div>
|
||||||
|
<div class="description">
|
||||||
|
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Voluptatibus quam iure dolor. Excepturi facere, ipsa
|
||||||
|
accusantium labore explicabo quaerat incidunt.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="wanna-be-macchinista">
|
||||||
|
<div class="zig-zag">
|
||||||
|
<svg width="100%" height="2rem" viewBox="0 0 1 1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMid meet">
|
||||||
|
<defs>
|
||||||
|
<pattern id="zig-zag-3" x="0" y="0" width="2" height="1" patternUnits="userSpaceOnUse">
|
||||||
|
<path fill="#888" d="M 0,1 L 1,0 L 2,1 L 0,1"></path>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect fill="url(#zig-zag-3)" x="0" y="0" width="1000" height="1"></rect>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title">Vuoi diventare macchinista?</div>
|
||||||
|
</section>
|
||||||
|
</PageLayout>
|
@ -0,0 +1,316 @@
|
|||||||
|
const $canvas: HTMLCanvasElement = document.querySelector('#circuits-art')!
|
||||||
|
|
||||||
|
interface Grid<T> extends Iterable<[number, number, T]> {
|
||||||
|
has(point: [number, number]): boolean
|
||||||
|
get(point: [number, number]): T
|
||||||
|
set(point: [number, number], value: T): void
|
||||||
|
}
|
||||||
|
|
||||||
|
function createGrid<T>(): Grid<T> {
|
||||||
|
const cells: Record<string, T> = {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
has([x, y]) {
|
||||||
|
return cells[`${x},${y}`] !== undefined
|
||||||
|
},
|
||||||
|
get([x, y]) {
|
||||||
|
return cells[`${x},${y}`]
|
||||||
|
},
|
||||||
|
set([x, y], value) {
|
||||||
|
cells[`${x},${y}`] = value
|
||||||
|
},
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
for (const [coord, value] of Object.entries(cells)) {
|
||||||
|
const [x, y] = coord.split(',').map(s => parseInt(s))
|
||||||
|
yield [x, y, value]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WireCell = 'down' | 'down-left' | 'down-right' | 'dot'
|
||||||
|
|
||||||
|
type WireDirection = 'down' | 'down-left' | 'down-right'
|
||||||
|
|
||||||
|
type WireSteps = { position: Point2; direction: WireCell }[]
|
||||||
|
|
||||||
|
type Point2 = [number, number]
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
grid: Grid<WireCell>
|
||||||
|
queuedWire: {
|
||||||
|
index: number
|
||||||
|
steps: WireSteps
|
||||||
|
} | null
|
||||||
|
badTries: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Renderer = {
|
||||||
|
timer: number
|
||||||
|
}
|
||||||
|
|
||||||
|
let renderer: Renderer | null = null
|
||||||
|
|
||||||
|
const RENDERER_FPS = 30
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
console.log('Setting up circuits art...')
|
||||||
|
|
||||||
|
$canvas.width = $canvas.clientWidth
|
||||||
|
$canvas.height = $canvas.clientHeight
|
||||||
|
|
||||||
|
const g = $canvas.getContext('2d')!
|
||||||
|
const state: State = {
|
||||||
|
grid: createGrid(),
|
||||||
|
queuedWire: null,
|
||||||
|
badTries: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const startTime = new Date()
|
||||||
|
|
||||||
|
const handle = setInterval(() => {
|
||||||
|
const time = new Date().getTime() - startTime.getTime()
|
||||||
|
|
||||||
|
update(state, g.canvas.width, g.canvas.height, time)
|
||||||
|
render(g, state, time)
|
||||||
|
}, 1000 / RENDERER_FPS)
|
||||||
|
|
||||||
|
renderer = { timer: handle }
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(state: State, width: number, height: number, time: number) {
|
||||||
|
const w = (width / CELL_SIZE) | 0
|
||||||
|
// const h = (height / CELL_SIZE) | 0
|
||||||
|
|
||||||
|
if (state.badTries > 1000) {
|
||||||
|
console.log('finished')
|
||||||
|
clearInterval(renderer!.timer)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.queuedWire) {
|
||||||
|
const sx = randomInt(0, w)
|
||||||
|
if (!state.grid.has([sx, 0])) {
|
||||||
|
const steps = generateWire(state.grid, [sx, 0])
|
||||||
|
if (steps.length < 7) {
|
||||||
|
state.badTries++
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.queuedWire = {
|
||||||
|
index: 0,
|
||||||
|
steps,
|
||||||
|
}
|
||||||
|
|
||||||
|
state.grid.set(state.queuedWire.steps[0].position, 'dot')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
state.badTries++
|
||||||
|
} else {
|
||||||
|
const wire = state.queuedWire
|
||||||
|
|
||||||
|
const step = wire.steps[wire.index]
|
||||||
|
state.grid.set(step.position, step.direction)
|
||||||
|
|
||||||
|
if (wire.index + 1 < wire.steps.length) {
|
||||||
|
state.grid.set(wire.steps[wire.index + 1].position, 'dot')
|
||||||
|
}
|
||||||
|
|
||||||
|
wire.index++
|
||||||
|
|
||||||
|
if (wire.index >= wire.steps.length) {
|
||||||
|
state.queuedWire = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CELL_SIZE = 27
|
||||||
|
|
||||||
|
const BACKGROUND_COLOR = '#ecffe3'
|
||||||
|
const WIRE_COLOR = '#a6ce94'
|
||||||
|
|
||||||
|
const RENDER_CELL: Record<WireCell, (g: CanvasRenderingContext2D) => void> = {
|
||||||
|
'down': g => {
|
||||||
|
g.strokeStyle = WIRE_COLOR
|
||||||
|
g.beginPath()
|
||||||
|
g.moveTo(0, 0)
|
||||||
|
g.lineTo(0, 1)
|
||||||
|
g.stroke()
|
||||||
|
},
|
||||||
|
'down-left': g => {
|
||||||
|
g.strokeStyle = WIRE_COLOR
|
||||||
|
g.beginPath()
|
||||||
|
g.moveTo(0, 0)
|
||||||
|
g.lineTo(-1, 1)
|
||||||
|
g.stroke()
|
||||||
|
},
|
||||||
|
'down-right': g => {
|
||||||
|
g.strokeStyle = WIRE_COLOR
|
||||||
|
g.beginPath()
|
||||||
|
g.moveTo(0, 0)
|
||||||
|
g.lineTo(+1, 1)
|
||||||
|
g.stroke()
|
||||||
|
},
|
||||||
|
'dot': g => {
|
||||||
|
g.fillStyle = BACKGROUND_COLOR
|
||||||
|
g.beginPath()
|
||||||
|
g.ellipse(0, 0, 0.15, 0.15, 0, 0, 2 * Math.PI)
|
||||||
|
g.fill()
|
||||||
|
|
||||||
|
g.strokeStyle = WIRE_COLOR
|
||||||
|
g.beginPath()
|
||||||
|
g.ellipse(0, 0, 0.15, 0.15, 0, 0, 2 * Math.PI)
|
||||||
|
g.stroke()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function render(g: CanvasRenderingContext2D, state: State, time: number) {
|
||||||
|
g.clearRect(0, 0, g.canvas.width, g.canvas.height)
|
||||||
|
g.resetTransform()
|
||||||
|
g.scale(CELL_SIZE, CELL_SIZE)
|
||||||
|
g.lineWidth = 3 / CELL_SIZE
|
||||||
|
|
||||||
|
const w = (g.canvas.width / CELL_SIZE) | 0
|
||||||
|
const h = (g.canvas.height / CELL_SIZE) | 0
|
||||||
|
for (let y = 0; y <= h + 1; y++) {
|
||||||
|
for (let x = 0; x <= w + 1; x++) {
|
||||||
|
if (!state.grid.has([x, y])) continue
|
||||||
|
|
||||||
|
const cell = state.grid.get([x, y])
|
||||||
|
|
||||||
|
g.save()
|
||||||
|
g.translate(x, y)
|
||||||
|
|
||||||
|
// g.fillStyle = '#f008'
|
||||||
|
// g.beginPath()
|
||||||
|
// g.rect(-0.25, -0.25, 0.5, 0.5)
|
||||||
|
// g.fill()
|
||||||
|
|
||||||
|
RENDER_CELL[cell](g)
|
||||||
|
g.restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (state.queuedWire) {
|
||||||
|
// for (const step of state.queuedWire.steps) {
|
||||||
|
// const [x, y] = step.position
|
||||||
|
|
||||||
|
// g.fillStyle = '#00f8'
|
||||||
|
// g.save()
|
||||||
|
// g.translate(x, y)
|
||||||
|
|
||||||
|
// g.beginPath()
|
||||||
|
// g.rect(-0.25, -0.25, 0.5, 0.5)
|
||||||
|
// g.fill()
|
||||||
|
// g.restore()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const [mx, my] = state.mouse
|
||||||
|
// g.save()
|
||||||
|
// g.fillStyle = '#0008'
|
||||||
|
// g.translate(Math.floor(mx / CELL_SIZE), Math.floor(my / CELL_SIZE))
|
||||||
|
// g.beginPath()
|
||||||
|
// g.rect(0, 0, 1, 1)
|
||||||
|
// g.fill()
|
||||||
|
// g.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
setup()
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (renderer) {
|
||||||
|
clearInterval(renderer.timer)
|
||||||
|
}
|
||||||
|
setup()
|
||||||
|
})
|
||||||
|
|
||||||
|
function randomInt(from: number, to: number) {
|
||||||
|
return Math.floor(Math.random() * (to - from + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomChoice<T>(choices: T[]): T {
|
||||||
|
return choices[randomInt(0, choices.length - 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomWeightedChoice<T>(choices: [T, number][]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 + 4 + 5 + 6
|
||||||
|
randomWeightedChoice([
|
||||||
|
['a', 3],
|
||||||
|
['b', 4],
|
||||||
|
['c', 5],
|
||||||
|
['d', 6],
|
||||||
|
])
|
||||||
|
|
||||||
|
const DIR_TO_VEC: Record<WireDirection, Point2> = {
|
||||||
|
['down']: [0, 1],
|
||||||
|
['down-left']: [-1, 1],
|
||||||
|
['down-right']: [+1, 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShortCircuitBoolean = boolean | (() => boolean)
|
||||||
|
|
||||||
|
const callOrBoolean = (v: ShortCircuitBoolean): boolean => (typeof v === 'boolean' ? v : v())
|
||||||
|
const implies = (a: ShortCircuitBoolean, b: ShortCircuitBoolean) => !callOrBoolean(a) || callOrBoolean(b)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells whether a given direction is not blocked by some other cell in a given grid and starting position
|
||||||
|
*/
|
||||||
|
const DIR_AVAILABLE_PREDICATE: Record<WireDirection, (pos: Point2, grid: Grid<WireCell>) => boolean> = {
|
||||||
|
['down']: ([x, y], grid) =>
|
||||||
|
!grid.has([x, y + 1]) &&
|
||||||
|
implies(grid.has([x - 1, y]), () => grid.get([x - 1, y]) !== 'down-right') &&
|
||||||
|
implies(grid.has([x + 1, y]), () => grid.get([x + 1, y]) !== 'down-left'),
|
||||||
|
['down-left']: ([x, y], grid) => !grid.has([x - 1, y + 1]) && implies(grid.has([x - 1, y]), () => grid.get([x - 1, y]) === 'down-left'),
|
||||||
|
['down-right']: ([x, y], grid) =>
|
||||||
|
!grid.has([x + 1, y + 1]) && implies(grid.has([x + 1, y]), () => grid.get([x + 1, y]) === 'down-right'),
|
||||||
|
}
|
||||||
|
|
||||||
|
function pruneDirections(grid: Grid<WireCell>, position: Point2, directions: WireDirection[]): WireDirection[] {
|
||||||
|
return directions.filter(dir => DIR_AVAILABLE_PREDICATE[dir](position, grid))
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateWire(grid: Grid<WireCell>, startingPoint: Point2): { position: Point2; direction: WireCell }[] {
|
||||||
|
const segmentLength = Math.floor(1 - Math.random() ** 2) * 10 + 30
|
||||||
|
let currentPosition = startingPoint
|
||||||
|
let currentDirection: WireDirection = randomChoice(['down', 'down', 'down', 'down-left', 'down-right'])
|
||||||
|
|
||||||
|
const steps: { position: Point2; direction: WireCell }[] = []
|
||||||
|
|
||||||
|
for (let i = 0; i < segmentLength; i++) {
|
||||||
|
const availableDirections = pruneDirections(grid, currentPosition, ['down', 'down-left', 'down-right'])
|
||||||
|
if (availableDirections.length === 0) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
const dir =
|
||||||
|
availableDirections.includes(currentDirection) && Math.random() < 0.25
|
||||||
|
? currentDirection
|
||||||
|
: randomChoice(availableDirections)
|
||||||
|
|
||||||
|
if ((currentDirection === 'down-left' && dir === 'down-right') || (currentDirection === 'down-right' && dir === 'down-left')) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
const [x, y] = currentPosition
|
||||||
|
const [dx, dy] = DIR_TO_VEC[dir]
|
||||||
|
|
||||||
|
steps.push({
|
||||||
|
position: [x, y],
|
||||||
|
direction: dir,
|
||||||
|
})
|
||||||
|
|
||||||
|
currentPosition = [x + dx, y + dy]
|
||||||
|
currentDirection = dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const last = steps.at(-1)
|
||||||
|
if (last) {
|
||||||
|
last.direction = 'dot'
|
||||||
|
}
|
||||||
|
|
||||||
|
return steps
|
||||||
|
}
|
@ -0,0 +1,474 @@
|
|||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font: inherit;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
min-height: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-snap-type: y proximity;
|
||||||
|
scroll-padding-top: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Typography
|
||||||
|
//
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
p + p {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Controls
|
||||||
|
//
|
||||||
|
|
||||||
|
button,
|
||||||
|
.button,
|
||||||
|
[role='button'] {
|
||||||
|
appearance: none;
|
||||||
|
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
border: 3px solid #222;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 4px 4px 0 0 #222;
|
||||||
|
|
||||||
|
transition: all 64ms linear;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translate(2px, 2px);
|
||||||
|
box-shadow: 2px 2px 0 0 #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: 0.25rem 1.5rem;
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
|
color: #222;
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: #1e6733;
|
||||||
|
color: #f4fef7;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #2b8b47;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pages
|
||||||
|
//
|
||||||
|
|
||||||
|
header {
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
height: 6rem;
|
||||||
|
border-bottom: 4px solid #222;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
|
||||||
|
padding: 0 1.5rem;
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
min-height: 6rem;
|
||||||
|
border-top: 4px solid #222;
|
||||||
|
background: #444;
|
||||||
|
color: #fdfdfd;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-homepage {
|
||||||
|
header .logo {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
min-height: calc(100vh - 6rem);
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
min-height: calc(100vh - 10rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 2rem;
|
||||||
|
|
||||||
|
scroll-snap-align: start;
|
||||||
|
|
||||||
|
& > .title {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 60px;
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
|
|
||||||
|
padding-top: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.zig-zag {
|
||||||
|
z-index: 5;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
bottom: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section.principal {
|
||||||
|
z-index: -2;
|
||||||
|
|
||||||
|
min-height: calc(100vh - 2rem);
|
||||||
|
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6rem;
|
||||||
|
|
||||||
|
padding: 6rem 0;
|
||||||
|
|
||||||
|
background: #ecffe3;
|
||||||
|
// circuit color
|
||||||
|
// background: #a6ce94;
|
||||||
|
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.circuit-layer {
|
||||||
|
z-index: -1;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
padding-top: 6rem;
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
max-width: 640px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
filter: drop-shadow(6px 6px 0 #222) drop-shadow(4px 0 0 #222) drop-shadow(0 4px 0 #222) drop-shadow(-4px 0 0 #222)
|
||||||
|
drop-shadow(0 -4px 0 #222);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.whats-phc {
|
||||||
|
background: #e4c5ff;
|
||||||
|
|
||||||
|
border: 4px solid #222;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 6px 6px 0 0 #222;
|
||||||
|
|
||||||
|
padding: 2rem;
|
||||||
|
|
||||||
|
max-width: 37rem;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
@extend .text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section.news {
|
||||||
|
background: #c2a8eb;
|
||||||
|
|
||||||
|
gap: 3rem;
|
||||||
|
|
||||||
|
padding-bottom: 6rem;
|
||||||
|
|
||||||
|
.news-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 3rem;
|
||||||
|
|
||||||
|
padding: 0 3rem;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.news {
|
||||||
|
background: #fffbeb;
|
||||||
|
|
||||||
|
border: 3px solid #222;
|
||||||
|
border-radius: 9px;
|
||||||
|
box-shadow: 9px 9px 0 0 #222;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
width: 22rem;
|
||||||
|
max-height: 27rem;
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #c67e14;
|
||||||
|
border: 2px solid #222;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #e69419;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #c67e14;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline solid 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .title {
|
||||||
|
// border-bottom: 2px solid #222;
|
||||||
|
|
||||||
|
border-top-left-radius: 9px;
|
||||||
|
border-top-right-radius: 9px;
|
||||||
|
|
||||||
|
padding: 1rem;
|
||||||
|
background: #f8e8b1;
|
||||||
|
|
||||||
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
& > .abstract {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
@extend .text;
|
||||||
|
}
|
||||||
|
& > .continue {
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
align-items: end;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[role='button'] {
|
||||||
|
padding: 0.5rem 2rem;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
// background: #824ed4;
|
||||||
|
// color: #f0e6ff;
|
||||||
|
background: #f8e8b1;
|
||||||
|
color: #000d;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
// background: #8e5ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section.projects {
|
||||||
|
background: #f5f2cc;
|
||||||
|
|
||||||
|
padding-bottom: 6rem;
|
||||||
|
|
||||||
|
.project-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
|
||||||
|
gap: 3rem;
|
||||||
|
|
||||||
|
padding: 0 6rem;
|
||||||
|
|
||||||
|
align-items: start;
|
||||||
|
|
||||||
|
.project {
|
||||||
|
// background: #fcddff;
|
||||||
|
// background: #ffa89c;
|
||||||
|
background: #a2d4f3;
|
||||||
|
color: #000e;
|
||||||
|
|
||||||
|
border: 3px solid #222;
|
||||||
|
border-radius: 9px;
|
||||||
|
box-shadow: 9px 9px 0 0 #222;
|
||||||
|
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
|
||||||
|
gap: 0.25rem 1rem;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
grid-row: span 2;
|
||||||
|
// place-self: center;
|
||||||
|
|
||||||
|
.box {
|
||||||
|
background: #0003;
|
||||||
|
border: 3px solid #0006;
|
||||||
|
border-radius: 6px;
|
||||||
|
width: 5rem;
|
||||||
|
height: 5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
transition: all 128ms ease-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translate(0, -8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
section.wanna-be-macchinista {
|
||||||
|
background: #888;
|
||||||
|
color: #fdfdfd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Misc
|
||||||
|
//
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track:vertical {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-left: 2px solid #222;
|
||||||
|
border-top: 2px solid #222;
|
||||||
|
border-bottom: 2px solid #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track:horizontal {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-top: 2px solid #222;
|
||||||
|
border-left: 2px solid #222;
|
||||||
|
border-right: 2px solid #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: #1e6733;
|
||||||
|
border: 2px solid #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: #2b8b47;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-corner {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
// border-left: 2px solid #222;
|
||||||
|
// border-top: 2px solid #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 15px;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict",
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"jsxImportSource": "preact"
|
||||||
|
}
|
||||||
|
}
|