├── .gitignore
├── screenshot.png
├── styles
├── favicon.ico
├── themes.css
└── styles.css
├── src
├── main.rs
├── base
│ ├── banner.rs
│ ├── prompt
│ │ ├── themes.rs
│ │ ├── general.rs
│ │ └── keyboard.rs
│ └── prompt.rs
├── base.rs
├── commands.rs
└── commands
│ ├── fetch
│ ├── structs.rs
│ └── formats.rs
│ ├── texts.rs
│ └── fetch.rs
├── README.md
├── Cargo.toml
├── configs
├── neofetch.txt
├── config.json
└── lang_icons
│ ├── python.txt
│ ├── rust.txt
│ ├── github.txt
│ └── octocat.txt
├── index.html
├── .github
└── workflows
│ └── gh-pages-deploy.yml
└── Cargo.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /dist
3 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shettysach/Termfolio/HEAD/screenshot.png
--------------------------------------------------------------------------------
/styles/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shettysach/Termfolio/HEAD/styles/favicon.ico
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | use leptos::view;
2 |
3 | mod base;
4 | mod commands;
5 | use base::Base;
6 |
7 | fn main() {
8 | leptos::mount_to_body(|| view! { });
9 | }
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Termfolio
2 | ---
3 | - Portfolio website inspired by shells, terminals and CLI utilities.
4 | - Customizable and configurable through JSON
5 | - Work in Progress, needs refactoring and optimization.
6 | - Built using the Leptos framework for Rust WASM.
7 |
8 | 
9 |
--------------------------------------------------------------------------------
/src/base/banner.rs:
--------------------------------------------------------------------------------
1 | use crate::commands::{banner, get_prompt};
2 | use leptos::{component, view, IntoView};
3 |
4 | #[component]
5 | pub fn Banner() -> impl IntoView {
6 | let banner = banner();
7 |
8 | view! {
9 |
{get_prompt}
10 | "help"
11 |
12 |
13 |
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "termfolio"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | leptos = { version = "0.5.7", features = ["csr", "nightly"] }
10 | leptos-use = "0.9.0"
11 | reqwest = { version = "0.12.9", features = ["json"] }
12 | serde = "1.0.215"
13 | serde_json = "1.0.133"
14 | tokio = { version = "1.41.1", features = ["macros", "sync"] }
15 |
--------------------------------------------------------------------------------
/configs/neofetch.txt:
--------------------------------------------------------------------------------
1 |
2 | .x+=:.
3 | z` ^% .uef^"
4 | . >k :d88E
5 | .@8Ned8" u . `888E
6 | .@^%8888" us888u. .udR88N 888E .z8k
7 | x88: `)8b. .@88 "8888" >888'888k 888E~?888L
8 | 8888N=*8888 9888 9888 9888 'Y" 888E 888E
9 | %8" R88 9888 9888 9888 888E 888E
10 | @8Wou 9% 9888 9888 9888 888E 888E
11 | .888888P` 9888 9888 ?8888u../ 888E 888E
12 | ` ^"F "888*""888" "8888P' m888N= 888<
13 | ^Y" ^Y' "P' `Y" 888
14 | J88"
15 | @%
16 | :"
17 |
--------------------------------------------------------------------------------
/styles/themes.css:
--------------------------------------------------------------------------------
1 | .catppuccin {
2 | --white: #f5e0dc;
3 | --black: #181825;
4 | --green: #cba6f7;
5 | --red: #e78284;
6 | --blue: #89b4fa;
7 | --yellow: #f9e2af;
8 | --orange: #fab387;
9 | --purple: #b4befe;
10 | --dgreen: #94e2d5;
11 | --dblue: #74c7ec;
12 | }
13 |
14 | .nord {
15 | --white: #d8dee9;
16 | --black: #2b303d;
17 | --green: #88c0d0;
18 | --blue: #5e81ac;
19 | --red: #bf616a;
20 | --yellow: #ebcb8b;
21 | --orange: #d08770;
22 | --purple: #b4befe;
23 | --dgreen: #a3be8c;
24 | --dblue: #81a1c1;
25 | }
26 |
27 | .default {
28 | --white: White;
29 | --black: Black;
30 | --green: LimeGreen;
31 | --red: Red;
32 | --blue: DodgerBlue;
33 | --yellow: Gold;
34 | --orange: OrangeRed;
35 | --purple: MediumSlateBlue;
36 | --dgreen: MediumSeaGreen;
37 | --dblue: DeepSkyBlue;
38 | }
39 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Termfolio
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/base.rs:
--------------------------------------------------------------------------------
1 | use leptos::{component, create_signal, view, For, IntoView, SignalGet};
2 | use std::collections::VecDeque;
3 |
4 | mod banner;
5 | use banner::Banner;
6 | mod prompt;
7 | use prompt::Prompt;
8 |
9 | #[component]
10 | pub fn Base() -> impl IntoView {
11 | // Signals for number of prompts and history vector
12 | let (prompts, set_prompts) = create_signal(1);
13 | let (history, set_history) = create_signal(VecDeque::new());
14 |
15 | let prompt_list = move || (0..prompts.get()).collect::>();
16 |
17 | view! {
18 |
19 |
20 | }
25 | }
26 | />
27 |
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/base/prompt/themes.rs:
--------------------------------------------------------------------------------
1 | use leptos::Signal;
2 | use leptos_use::{
3 | use_color_mode_with_options, use_cycle_list_with_options, ColorMode, UseColorModeOptions,
4 | UseColorModeReturn, UseCycleListOptions, UseCycleListReturn,
5 | };
6 |
7 | // Last theme will be default
8 | static THEMES: [&str; 4] = ["catppuccin", "nord", "default", "tokyonight"];
9 |
10 | pub fn theme_changer() -> (Signal, impl Fn() + Clone) {
11 | let UseColorModeReturn { mode, set_mode, .. } = use_color_mode_with_options(
12 | UseColorModeOptions::default()
13 | .custom_modes(THEMES.into_iter().map(String::from).collect())
14 | .initial_value(ColorMode::from(THEMES.last().unwrap().to_string())),
15 | );
16 |
17 | let UseCycleListReturn { state, next, .. } = use_cycle_list_with_options(
18 | THEMES
19 | .iter()
20 | .map(|s| ColorMode::Custom(s.to_string()))
21 | .collect::>(),
22 | UseCycleListOptions::default().initial_value(Some((mode, set_mode).into())),
23 | );
24 |
25 | (state, next)
26 | }
27 |
--------------------------------------------------------------------------------
/configs/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "github": "shettysach",
3 |
4 | "about": {
5 | "name": "Sachith C Shetty",
6 | "intro": "Hi, I'm Sachith, a Master's student in Computer Science at USC, with interests in systems programming, deep learning compilers and programming language compilers. I am also interested in learning about information theory, deep learning and functional programming. I currently use Rust, Python, C++ and C.",
7 |
8 | "langs": ["Rust", "Python", "C++", "C", "Haskell"],
9 |
10 | "experience": [
11 | {
12 | "title": "Intern at Thingularity India",
13 | "description": [ "Created a demo mobile app using Flutter(Dart, Android SDK)", "Helped in testing APIs and preparing test reports using Postman API.", "Learnt about embedded systems and programming, interacting with Bluetooth Low Energy (BLE) devices and Raspberry Pi (Python), and using REST API to get and post data to servers."]
14 | }
15 | ],
16 |
17 | "education": [
18 | {
19 | "institute": "USC, Los Angeles",
20 | "course": "MSCS",
21 | "duration": "2025 - 2027"
22 | },
23 | {
24 | "institute": "VIT, Vellore",
25 | "course": "B.Tech CSE",
26 | "duration": "2021 - 2025"
27 | }
28 | ]
29 | },
30 |
31 | "links": {
32 | "github": "shettysach",
33 | "email": "ShettySachith47@gmail.com",
34 | "linkedin": "in/sachith-shetty-3a165724b",
35 | "twitter": null
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/commands.rs:
--------------------------------------------------------------------------------
1 | mod fetch;
2 | mod texts;
3 | pub use fetch::get_prompt;
4 |
5 | pub async fn command(inp0: &str, inp1: &str) -> String {
6 | let result = match inp0 {
7 | "help" | "termfolio" => texts::HELP,
8 | "about" => &fetch::get_about(),
9 | "github" | "neofetch" | "fastfetch" => &fetch::get_github().await,
10 | "repos" | "onefetch" => &fetch::get_repos().await,
11 | "links" => fetch::get_contacts(),
12 | "credits" => texts::CREDITS,
13 |
14 | "cd" => "Nowhere to go.",
15 | "mkdir" | "touch" => "Nowhere to create.",
16 | "rm" | "rmdir" => "Nothing to destroy.",
17 | "cp" => "Nothing to duplicate.",
18 | "mv" => "Nowhere to move.",
19 | "ls" | "cat" => "Nothing to see.",
20 | "grep" | "which" | "find" => "Nowhere to search.",
21 | "pwd" => "You are here.",
22 | "nano" | "vi" | "vim" | "nvim" | "hx" => "Great editor.",
23 | "emacs" => "Great mail client",
24 | "su" | "sudo" | "chmod" => "With great power comes great responsibility.",
25 | "whoami" => "Despite everything, it's still you.",
26 | "exit" => "Hasta la vista.",
27 | "echo" => inp1.trim(),
28 | "" => "",
29 | _ => &format!("{inp0}: command not found"),
30 | };
31 |
32 | result.to_string()
33 | }
34 |
35 | pub fn autocomplete(inp: &str) -> &str {
36 | let inp = inp.trim();
37 |
38 | let comms = [
39 | "help",
40 | "history",
41 | "about",
42 | "github",
43 | "repos",
44 | "links",
45 | "theme",
46 | "wal",
47 | "credits",
48 | "onefetch",
49 | "neofetch",
50 | "fastfetch",
51 | ];
52 |
53 | if !inp.is_empty() {
54 | for &c in comms.iter() {
55 | if c.starts_with(inp) {
56 | return c;
57 | }
58 | }
59 | }
60 |
61 | inp
62 | }
63 |
64 | pub fn banner() -> String {
65 | String::from(texts::HELP)
66 | }
67 |
--------------------------------------------------------------------------------
/src/base/prompt/general.rs:
--------------------------------------------------------------------------------
1 | use crate::commands::command;
2 | use leptos::{ReadSignal, Signal, SignalGetUntracked, SignalUpdate, WriteSignal};
3 | use leptos_use::ColorMode;
4 | use std::collections::VecDeque;
5 |
6 | pub async fn general_commands(
7 | value: String,
8 | state: Signal,
9 | next: F,
10 | set_out: WriteSignal,
11 | submitter: WriteSignal,
12 | updater: WriteSignal>,
13 | history: ReadSignal>,
14 | ) where
15 | F: Fn(),
16 | {
17 | let value = value.trim().replace("<", "‹").replace(">", "›");
18 | let val = value.split_once(' ').unwrap_or((&value, ""));
19 |
20 | match val.0 {
21 | "clear" => {
22 | submitter.update(|prompts| {
23 | *prompts = 0;
24 | });
25 | }
26 | "history" => {
27 | let hist: Vec = history.get_untracked().into();
28 | let hist: Vec = hist
29 | .iter()
30 | .rev()
31 | .enumerate()
32 | .map(|(i, c)| format!("{} {}", i + 1, c))
33 | .collect();
34 | set_out(hist.join("\n"));
35 | }
36 | "theme" | "t" | "wal" => {
37 | next();
38 | let new_theme = state.get_untracked();
39 | set_out(format!(
40 | r#"Theme changed to: {new_theme}"#
41 | ));
42 | }
43 | _ => set_out(command(val.0, val.1).await),
44 | }
45 |
46 | updater.update(|hist| {
47 | if !value.is_empty() && hist.front() != Some(&value) {
48 | hist.push_front(value);
49 | if hist.len() > 20 {
50 | hist.pop_back();
51 | }
52 | }
53 | });
54 |
55 | // Clears if max limit is reached
56 | submitter.update(|prompts| {
57 | if *prompts == u8::MAX {
58 | *prompts = 0;
59 | }
60 | });
61 |
62 | submitter.update(|prompts| {
63 | *prompts += 1;
64 | });
65 | }
66 |
--------------------------------------------------------------------------------
/src/commands/fetch/structs.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | #[derive(Deserialize, Serialize, Clone)]
4 | pub struct Config {
5 | pub github: String,
6 | pub about: About,
7 | pub links: Links,
8 | }
9 |
10 | #[derive(Deserialize, Serialize, Clone)]
11 | pub struct About {
12 | pub name: String,
13 | pub intro: String,
14 | pub langs: Vec,
15 | pub experience: Vec,
16 | pub education: Vec,
17 | }
18 |
19 | #[derive(Deserialize, Serialize, Clone)]
20 | pub struct Experience {
21 | pub title: String,
22 | pub description: Vec,
23 | }
24 |
25 | #[derive(Deserialize, Serialize, Clone)]
26 | pub struct Education {
27 | pub institute: String,
28 | pub course: String,
29 | pub duration: String,
30 | }
31 |
32 | #[derive(Deserialize, Serialize, Clone)]
33 | pub struct Links {
34 | pub github: String,
35 | pub email: Option,
36 | pub linkedin: Option,
37 | pub twitter: Option,
38 | }
39 |
40 | #[derive(Deserialize, Serialize, Clone)]
41 | pub struct Profile {
42 | pub username: String,
43 | pub langs: Vec,
44 | pub info: UserInfo,
45 | pub stats: UserStats,
46 | }
47 |
48 | #[derive(Deserialize, Serialize, Clone)]
49 | pub struct UserInfo {
50 | pub name: Option,
51 | pub bio: Option,
52 | pub public_repos: u16,
53 | pub company: Option,
54 | pub location: Option,
55 | pub followers: u16,
56 | pub following: u16,
57 | pub created_at: String,
58 | }
59 |
60 | #[derive(Deserialize, Serialize, Clone)]
61 | pub struct UserStats {
62 | pub stars: u16,
63 | pub forks: u16,
64 | }
65 |
66 | #[derive(Deserialize, Serialize)]
67 | pub struct ApiResponse {
68 | pub response: Vec,
69 | }
70 |
71 | #[derive(Deserialize, Serialize)]
72 | pub struct Repos {
73 | pub repos: Vec,
74 | }
75 |
76 | #[derive(Deserialize, Serialize, Clone)]
77 | pub struct Repository {
78 | pub author: String,
79 | pub name: String,
80 | pub description: String,
81 | pub stars: u16,
82 | pub forks: u16,
83 | pub language: String,
84 | }
85 |
--------------------------------------------------------------------------------
/styles/styles.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --white: #c0caf5;
3 | --black: #1a1b26;
4 | --green: #2ac3de;
5 | --red: #bb9af7;
6 | --blue: #7aa2f7;
7 | --yellow: #ffc777;
8 | --orange: #ff757f;
9 | --purple: #bb9af7;
10 | --dgreen: #c3e88d;
11 | --dblue: #7dcfff;
12 | }
13 |
14 | * {
15 | font-family: "JetBrains Mono";
16 | font-size: 17px;
17 | background: var(--black);
18 | color: var(--white);
19 | text-underline-offset: 8px;
20 | text-decoration-thickness: 2px;
21 | /*text-shadow: 0px 3px 8px currentColor;*/
22 | }
23 |
24 | input {
25 | font-size: 100%;
26 | outline: none;
27 | border: none;
28 | width: 460px;
29 | }
30 |
31 | form {
32 | display: inline-block;
33 | margin-bottom: 0px;
34 | caret-shape: block;
35 | caret-color: var(--green);
36 | font-weight: 500;
37 | }
38 |
39 | .semibold {
40 | font-weight: 500;
41 | }
42 |
43 | .rd {
44 | color: var(--red);
45 | }
46 |
47 | .grn {
48 | color: var(--green);
49 | }
50 |
51 | .ylw {
52 | color: var(--yellow);
53 | }
54 |
55 | .blu {
56 | color: var(--blue);
57 | }
58 |
59 | .inline {
60 | display: inline;
61 | font-weight: 500;
62 | color: var(--green);
63 | }
64 |
65 | .output {
66 | white-space: pre-wrap;
67 | word-wrap: break-word;
68 | margin-bottom: 22px;
69 | }
70 |
71 | .about,
72 | .ascii,
73 | .text {
74 | flex-direction: row;
75 | box-sizing: border-box;
76 | float: left;
77 | }
78 |
79 | /* About */
80 |
81 | .about {
82 | flex: 0 0 calc(60%);
83 | outline: 3.5px solid var(--green);
84 | outline-offset: 25px;
85 | }
86 |
87 | /* Github fetch */
88 |
89 | .ascii {
90 | flex: 0 0 calc(50% - 15px);
91 | margin: 20px;
92 | }
93 |
94 | /* Repos fetch */
95 |
96 | .text {
97 | flex: 0 0 calc(50% - 35px);
98 | margin: 30px;
99 | max-width: 30%;
100 | }
101 |
102 | .row:after {
103 | content: "";
104 | display: table;
105 | clear: both;
106 | align-items: baseline;
107 | }
108 |
109 | /* Color blocks */
110 |
111 | .blocks {
112 | font-size: 35px;
113 | }
114 |
115 | #orientation-warning {
116 | display: none;
117 | position: fixed;
118 | width: 100%;
119 | height: 100%;
120 | background-color: rgba(0, 0, 0, 0.75);
121 | color: white;
122 | top: 0;
123 | left: 0;
124 | font-size: 1.2rem;
125 | text-align: center;
126 | padding-top: 30vh;
127 | }
128 |
--------------------------------------------------------------------------------
/src/base/prompt/keyboard.rs:
--------------------------------------------------------------------------------
1 | use crate::commands::autocomplete;
2 | use leptos::{
3 | ev::{keydown, KeyboardEvent},
4 | html::Input,
5 | NodeRef, ReadSignal, SignalGet, SignalUpdate, WriteSignal,
6 | };
7 | use leptos_use::use_event_listener;
8 | use std::{cmp::Ordering, collections::VecDeque};
9 |
10 | pub fn keyboard_commands(
11 | input_element: NodeRef,
12 | history: ReadSignal>,
13 | history_index: ReadSignal,
14 | set_history_index: WriteSignal,
15 | submitter: WriteSignal,
16 | ) {
17 | let _ = use_event_listener(input_element, keydown, move |ev: KeyboardEvent| {
18 | let hist = history.get();
19 | let index = history_index.get().into();
20 | let inp = input_element.get().unwrap();
21 |
22 | match &ev.key()[..] {
23 | //Previous command in history
24 | "ArrowUp" => {
25 | ev.prevent_default();
26 | if index < hist.len() {
27 | inp.set_value(&hist[index]);
28 | set_history_index.update(move |history_index| *history_index += 1);
29 | }
30 | }
31 |
32 | //Next command in history
33 | "ArrowDown" => match index.cmp(&1) {
34 | Ordering::Greater => {
35 | inp.set_value(&hist[index - 2]);
36 | set_history_index.update(move |history_index| *history_index -= 1);
37 | }
38 | Ordering::Equal => {
39 | inp.set_value("");
40 | set_history_index.update(move |history_index| *history_index -= 1);
41 | }
42 | Ordering::Less => (),
43 | },
44 |
45 | //Autocomplete
46 | "Tab" => {
47 | ev.prevent_default();
48 | inp.set_value(autocomplete(&inp.value()));
49 | }
50 | _ => {}
51 | }
52 |
53 | //Ctrl
54 | if ev.ctrl_key() || ev.meta_key() {
55 | // Clear
56 | match &ev.key()[..] {
57 | "l" | "L" => {
58 | ev.prevent_default();
59 | submitter.update(|prompts| {
60 | *prompts = 0;
61 | });
62 | submitter.update(|prompts| {
63 | *prompts += 1;
64 | });
65 | }
66 | // Can add Ctrl + P / N for history,
67 | // but will interfere with new window shortcut
68 | _ => {}
69 | }
70 | }
71 | });
72 | }
73 |
--------------------------------------------------------------------------------
/src/commands/texts.rs:
--------------------------------------------------------------------------------
1 | pub const HELP: &str = r#" _____________ __ ___________ __ ________
2 | /_ __/ __/ _ \/ |/ / __/ __ \/ / / _/ __ \
3 | / / / _// , _/ /|_/ / _// /_/ / /___/ // /_/ /
4 | /_/ /___/_/|_/_/ /_/_/ \____/____/___/\____/
5 |
6 | Hello, welcome to Termfolio [WIP]. Type one of these commands -
7 |
8 | about - View about me
9 | neofetch / fastfetch / github - View about Github profile
10 | onefetch / repos - View about my pinned repos/projects
11 | links - View contact info and links
12 | help - View this help section
13 | theme / wal - Cycle through themes
14 | credits - View credits and repo
15 | history - View command history
16 | clear - Clear screen
17 |
18 | You can use arrow keys to scroll through history,
19 | and also use Ctrl+L to clear the screen
20 | If you prefer a static site, visit shettysach.github.io
21 | "#;
22 |
23 | pub const CREDITS: &str = r#" _____________ __ ___________ __ ________
24 | /_ __/ __/ _ \/ |/ / __/ __ \/ / / _/ __ \
25 | / / / _// , _/ /|_/ / _// /_/ / /___/ // /_/ /
26 | /_/ /___/_/|_/_/ /_/_/ \____/____/___/\____/
27 |
28 | Terminal style portfolio website.
29 |
30 | Github: github.com/shettysach
31 |
32 | Repo: github.com/shettysach/termfolio
33 |
34 | APIs used -
35 |
36 | * Github REST API
40 |
41 | * Pinned repos - berrysauce/pinned
45 |
46 | * Total stars and forks - idealclover/GitHub-Star-Counter
50 |
51 | "#;
52 |
53 | pub const READ_JSON_ERROR: &str = r#"Error reading config.json"#;
54 | pub const FETCH_GITHUB_ERROR: &str =
55 | r#"Error fetching data from Github."#;
56 |
--------------------------------------------------------------------------------
/src/base/prompt.rs:
--------------------------------------------------------------------------------
1 | mod general;
2 | mod keyboard;
3 | mod themes;
4 |
5 | use crate::commands::get_prompt;
6 | use general::general_commands;
7 | use keyboard::keyboard_commands;
8 | use leptos::{
9 | component, create_effect, create_node_ref, create_signal,
10 | ev::SubmitEvent,
11 | html::{Form, Input},
12 | spawn_local, view, IntoView, NodeRef, ReadSignal, WriteSignal,
13 | };
14 | use std::collections::VecDeque;
15 | use themes::theme_changer;
16 |
17 | #[component]
18 | pub fn Prompt(
19 | submitter: WriteSignal,
20 | updater: WriteSignal>,
21 | history: ReadSignal>,
22 | ) -> impl IntoView {
23 | //Output and history index signals
24 | let (out, set_out) = create_signal(String::new());
25 | let (history_index, set_history_index): (ReadSignal, WriteSignal) = create_signal(0);
26 |
27 | //Form and input elements
28 | let form_element: NodeRef
89 |
90 |
91 |
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Release to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | workflow_dispatch:
7 |
8 | permissions:
9 | contents: write
10 | pages: write
11 | id-token: write
12 |
13 | concurrency:
14 | group: "pages"
15 | cancel-in-progress: false
16 |
17 | jobs:
18 | github-pages-release:
19 | timeout-minutes: 10
20 |
21 | environment:
22 | name: github-pages
23 | url: ${{ steps.deployment.outputs.page_url }}
24 |
25 | runs-on: ubuntu-latest
26 |
27 | steps:
28 | - uses: actions/checkout@v4
29 |
30 | - name: Install pinned nightly
31 | uses: dtolnay/rust-toolchain@nightly
32 | with:
33 | toolchain: nightly-2024-08-15
34 | components: clippy, rustfmt
35 |
36 | - name: Add WASM target
37 | run: rustup target add wasm32-unknown-unknown
38 |
39 | - name: Lint (fmt + clippy)
40 | run: |
41 | cargo fmt --all -- --check
42 | cargo clippy --all-targets --all-features -- -D warnings
43 |
44 | # - name: Build Tailwind CSS
45 | # run: |
46 | # npm ci
47 | # npx tailwindcss -i -o