diff --git a/README.md b/README.md index bc2e3f2c6..2e900e85e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This repository generates the user and dev documentations of https://github.com/ ## User documentation It is available on: -https://uutils.github.io/coreutils/docs/ +https://uutils.org/coreutils/docs/ Can be generated with: ```bash @@ -17,23 +17,24 @@ mdbook build ## Playground An in-browser playground runs the uutils coreutils WASM build directly in your browser: -https://uutils.github.io/playground/ +https://uutils.org/playground/ -See also how it works: https://uutils.github.io/playground-how-it-works/ +See also how it works: https://uutils.org/playground-how-it-works/ ## Developer documentation: It is available on: -https://uutils.github.io/dev/coreutils/ +https://uutils.org/dev/coreutils/ Can be generated with: ```bash cargo doc --no-deps --all-features --workspace ``` -The pages are committed daily into the gh-pages branch. +The website CI builds this on each `main`/scheduled run and publishes it +under `/dev/coreutils/` (and `/dev/findutils/`) as part of the deploy. ## Build timing Generated by cargo, the build timings can be seen on: -https://uutils.github.io/cargo-timings/cargo-timing.html +https://uutils.org/cargo-timings/cargo-timing.html diff --git a/config.toml b/config.toml index 156734cf3..3ed6d8901 100644 --- a/config.toml +++ b/config.toml @@ -1,5 +1,5 @@ # The URL the site will be built for -base_url = "https://uutils.github.io" +base_url = "https://uutils.org" title = "uutils" diff --git a/content/_index.md b/content/_index.md index e7d9145cf..a349b9b11 100644 --- a/content/_index.md +++ b/content/_index.md @@ -3,12 +3,25 @@ title = "Home" template = "index.html" +++ -
- - - uutils logo - -
uutils
+
+
+ uutils --about + + + +
+
+
+ uutils logo +
user@machine:~$ uutils --about
+
uutils_
+

Cross-platform Rust reimplementations of the command-line tools you use every day, with full GNU compatibility.

+ +
+
The uutils project reimplements ubiquitous command line utilities in @@ -29,83 +42,36 @@ C has served us well for decades, but it is time to move on. For new generations This is not about fighting the GNU project. It is not primarily about security (GNU coreutils only had 17 CVEs since 2003) or about license debates. It is about **modernizing foundational software** so it can be maintained and improved by the next generation of contributors. -Ubuntu is already [carefully but purposefully adopting](https://discourse.ubuntu.com/t/carefully-but-purposefully-oxidising-ubuntu/56995) uutils coreutils, and Debian is following the same path. +Ubuntu is already [carefully but purposefully adopting](https://discourse.ubuntu.com/t/carefully-but-purposefully-oxidising-ubuntu/56995) uutils coreutils, Debian is following the same path, and Microsoft offers it as well. # Projects -
- -

coreutils

-

- The commands you use everyday: ls, cp, etc. Production ready! -

-
- -

findutils

-

- Finding what you need: find, locate, updatedb & xargs. -

-
- -

diffutils

-

- Comparing text and files: diff, cmp, diff3, sdiff. -

-
- -

util-linux

-

- Essential system utilities: mount, fdisk, lsblk, dmesg and more. -

-
- -

procps

-

- Process monitoring utilities: ps, top, free, vmstat and more. -

-
- -

sed

-

- Stream editor for filtering and transforming text. -

-
- -

tar

-

- Archiving utility for creating and extracting tar archives. -

-
- -

acl

-

- Access control list utilities: getfacl, setfacl, chacl. -

-
- -

hostname

-

- Show or set the system hostname. -

-
- -

login

-

- Login and user management utilities: login, su, passwd and more. -

-
- -

bsdutils

-

- BSD utility programs: cal, logger, script, wall and more. -

-
- -

shadow-rs

-

- User and group management: useradd, passwd, groupadd, usermod and more. -

-
+ # Crates @@ -119,10 +85,19 @@ which are published on [crates.io](https://crates.io/). - [`platform-info`](https://github.com/uutils/platform-info) - [`uutils-term-grid`](https://github.com/uutils/uutils-term-grid) +# Who we are + +uutils is a community-driven, open-source effort maintained by volunteers around the world. There is no company behind it, just contributors who care about the future of foundational command-line tools. + +Everything happens in the open on [GitHub](https://github.com/uutils), and newcomers are genuinely welcome. Many of our contributors landed their first-ever open-source patch on a uutils project, and we are happy to help you do the same. + +[Meet the team](/team) behind uutils. + # Contributing You can help us out by: +- [Tackling a good first issue](https://github.com/search?q=org%3Auutils+is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22&type=issues&s=created&o=desc) across any of our projects (the easiest way to get started) - Contributing code - Contributing documentation - Reporting bugs (e.g. incompatibilities with GNU utilities) diff --git a/content/awk.md b/content/awk.md new file mode 100644 index 000000000..5f7c0be06 --- /dev/null +++ b/content/awk.md @@ -0,0 +1,25 @@ ++++ + +title = "awk" +template = "project.html" + +[extra] +wip = true + ++++ + +Rust implementation of `awk`, the pattern scanning and text-processing language. + +This project aims to be a drop-in replacement of the original command. + +# Goals + +This project aims to be a drop-in replacement for GNU awk (gawk). Differences with the original are treated as bugs. + +# Contributing + +To contribute to uutils awk, please see [CONTRIBUTING](https://github.com/uutils/awk/blob/main/CONTRIBUTING.md). + +# License + +uutils awk is licensed under the MIT License - see the [LICENSE](https://github.com/uutils/awk/blob/main/LICENSE) file for details. diff --git a/content/coreutils.md b/content/coreutils.md index e77608a62..72480cb0c 100644 --- a/content/coreutils.md +++ b/content/coreutils.md @@ -3,6 +3,9 @@ title = "coreutils" template = "project.html" +[extra] +status = "ready" + +++ uutils coreutils is a cross-platform reimplementation of the GNU coreutils in Rust. While all programs have been implemented, some options might be missing or different behavior might be experienced. diff --git a/content/diffutils.md b/content/diffutils.md index 90a0b88de..58d1434bc 100644 --- a/content/diffutils.md +++ b/content/diffutils.md @@ -3,6 +3,9 @@ title = "diffutils" template = "project.html" +[extra] +status = "beta" + +++ Rust implementation of GNU diffutils: `diff`, `cmp`, `diff3` and `sdiff`. diff --git a/content/findutils.md b/content/findutils.md index d801f9f5c..0760aff58 100644 --- a/content/findutils.md +++ b/content/findutils.md @@ -3,6 +3,9 @@ title = "findutils" template = "project.html" +[extra] +status = "beta" + +++ Rust implementation of GNU findutils: `xargs`, `find`, `locate` and `updatedb`. diff --git a/content/grep.md b/content/grep.md new file mode 100644 index 000000000..603d4e226 --- /dev/null +++ b/content/grep.md @@ -0,0 +1,27 @@ ++++ + +title = "grep" +template = "project.html" + +[extra] +status = "beta" + ++++ + +Rust implementation of GNU grep: `grep`, `egrep` and `fgrep`. + +This project aims to be a drop-in replacement of the original commands. + +# Goals + +This project aims to be a drop-in replacement for GNU grep. Differences with GNU are treated as bugs. + +# Contributing + +To contribute to uutils grep, please see [CONTRIBUTING](https://github.com/uutils/grep/blob/main/CONTRIBUTING.md). + +# License + +uutils grep is licensed under the MIT License - see the [LICENSE](https://github.com/uutils/grep/blob/main/LICENSE) file for details. + +GNU grep is licensed under the GPL 3.0 or later. diff --git a/content/gsoc.md b/content/gsoc.md index f7820cc72..b6f26ff62 100644 --- a/content/gsoc.md +++ b/content/gsoc.md @@ -2,12 +2,18 @@ title = "Uutils at GSOC" +++ -Google summer of code is: - -> Google Summer of Code is a global, online program focused on bringing -> new contributors into open source software development. GSoC -> Contributors work with an open source organization on a 12+ week -> programming project under the guidance of mentors. +
+
+ uutils gsoc --help + + + +
+
+
$ uutils gsoc --help
+

Google Summer of Code is a global, online program focused on bringing new contributors into open source software development. GSoC contributors work with an open source organization on a 12+ week programming project under the guidance of mentors.

+
+
If you want to know more about how it works, check out the links below. @@ -18,7 +24,7 @@ If you want to know more about how it works, check out the links below. # What is it about? -The [uutils project](https://github.com/uutils/) is aiming at rewriting key Linux utilities in Rust, targeting [coreutils](https://github.com/uutils/coreutils), [findutils](https://github.com/uutils/findutils), [diffutils](https://github.com/uutils/diffutils), [procps](https://github.com/uutils/procps), [util-linux](https://github.com/uutils/util-linux), and [bsdutils](https://github.com/uutils/bsdutils). Their goal is to create fully compatible, high-performance drop-in replacements, ensuring reliability through upstream test suites. Significant progress has been made with coreutils, diffutils, and findutils, while the other utilities are in the early stages of development. +The [uutils project](https://github.com/uutils/) is aiming at rewriting key Linux utilities in Rust, targeting [coreutils](https://github.com/uutils/coreutils), [findutils](https://github.com/uutils/findutils), [diffutils](https://github.com/uutils/diffutils), [sed](https://github.com/uutils/sed), [grep](https://github.com/uutils/grep), [awk](https://github.com/uutils/awk), [procps](https://github.com/uutils/procps), [util-linux](https://github.com/uutils/util-linux), and [bsdutils](https://github.com/uutils/bsdutils). Their goal is to create fully compatible, high-performance drop-in replacements, ensuring reliability through upstream test suites. coreutils is already production-ready and shipping in distributions; findutils, diffutils and grep are well advanced; while sed, awk and the system utilities (procps, util-linux, bsdutils) are at earlier stages of development. # How to get started @@ -32,7 +38,7 @@ with uutils. 1. **Reach out to us!** We are happy to discuss potential projects and help you find a meaningful project for uutils. Tell us what interests you about the project and what experience you have and we can find a suitable project together. You can talk to the uutils maintainers on the [Discord server](https://discord.gg/wQVJbvJ). In particular, you can contact: * Sylvestre Ledru (@sylvestre on GitHub and Discord) -2. **Get comfortable with uutils.** To find a good project you need to understand the codebase. We recommend that you take a look at the code, the issue tracker and maybe try to tackle some [good-first-issues](https://github.com/uutils/coreutils/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). Also take a look at our [contributor guidelines](https://github.com/uutils/coreutils/blob/main/CONTRIBUTING.md). +2. **Get comfortable with uutils.** To find a good project you need to understand the codebase. We recommend that you take a look at the code, the issue tracker and maybe try to tackle some [good-first-issues](https://github.com/search?q=org%3Auutils+is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22&type=issues&s=created&o=desc) across any of our projects. Also take a look at our [contributor guidelines](https://github.com/uutils/coreutils/blob/main/CONTRIBUTING.md). 3. **Find a project and a mentor.** We have a [list of potential projects](https://github.com/uutils/coreutils/wiki/GSOC-Project-Ideas) you can adapt or use as inspiration. Make sure discuss your ideas with the maintainers! Some project ideas below have suggested mentors you could contact. 4. **Write the application.** You can do this with your mentor. The application has to go through Google, so make sure to follow all the advice in Google's [Contributor Guide](https://google.github.io/gsocguides/student/writing-a-proposal). Please make sure you include your prior contributions to uutils in your application. @@ -44,448 +50,273 @@ with uutils. # Project Ideas -This page contains project ideas for the Google Summer of Code for -uutils. Feel free to suggest project ideas of your own. - -[Guidelines for the project list](https://google.github.io/gsocguides/mentor/defining-a-project-ideas-list) - -Summarizing that page, each project should include: -- Title -- Description -- Expected outputs -- Skills required/preferred -- Possible mentors -- Size (either ~175 or ~350 hours) -- Difficulty (easy, medium or hard) - -## Performance optimization for coreutils - -While [uutils/coreutils](https://github.com/uutils/coreutils) has achieved strong GNU compatibility, some utilities can still benefit from performance improvements to match or exceed GNU coreutils speed. This project focuses on identifying performance bottlenecks and implementing optimizations across key utilities. - -The goal is to systematically profile, benchmark, and optimize coreutils to ensure they are production-ready for performance-critical environments. - -Key areas of work include: -* Profiling utilities to identify performance bottlenecks (using perf, flamegraph, criterion) -* Creating comprehensive benchmark suite comparing against GNU coreutils -* Optimizing hot paths in frequently-used utilities (cat, cut, sort, uniq, wc, etc.) -* Implementing algorithmic improvements and efficient data structures -* Reducing allocations and improving memory usage patterns -* Leveraging SIMD instructions where applicable for data processing -* Optimizing I/O operations with proper buffering strategies -* Benchmarking across different file sizes and input patterns -* Setting up continuous performance monitoring in CI -* Documenting performance characteristics and optimization techniques - -- **Difficulty**: Medium -- **Size**: ~175 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Performance profiling and optimization techniques - - Understanding of systems programming and I/O optimization - - Benchmarking methodologies - - Knowledge of SIMD and parallel processing is a plus - - Familiarity with coreutils behavior - -## Expand differential fuzzing for coreutils - -The [uutils/coreutils](https://github.com/uutils/coreutils) project has [some fuzzing infrastructure](https://github.com/uutils/coreutils/tree/main/fuzz/fuzz_targets) in place, but many utilities still lack comprehensive fuzz testing. This project focuses on expanding differential fuzzing coverage across coreutils to identify edge cases, improve robustness, and ensure compatibility with GNU coreutils. - -Differential fuzzing compares the behavior of uutils implementations against GNU coreutils to automatically detect discrepancies and bugs that might be missed by traditional testing. - -Key areas of work include: -* Creating new fuzz targets for utilities that currently lack them -* Implementing differential fuzzing harnesses that compare uutils vs GNU outputs -* Expanding existing fuzz targets to cover more code paths and options -* Setting up structured fuzzing campaigns with AFL++ and libFuzzer -* Integrating continuous fuzzing into CI/CD workflows -* Triaging and fixing bugs discovered through fuzzing -* Creating a corpus of interesting test inputs for regression testing -* Documenting fuzzing infrastructure and best practices -* Performance profiling of fuzz targets to maximize coverage - -- **Difficulty**: Medium -- **Size**: ~175 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Experience with fuzzing tools (AFL++, libFuzzer, cargo-fuzz) - - Understanding of differential testing methodologies - - Debugging and bug triage skills - - Familiarity with coreutils behavior - -## Complete `findutils` GNU compatibility - -The [uutils/findutils](https://github.com/uutils/findutils) project has made significant progress with [more than half](https://github.com/uutils/findutils-tracking/) of the GNU findutils and BFS tests passing. This project focuses on completing the remaining work to achieve full GNU compatibility and production readiness. - -The goal is to finish implementing missing features, fix failing test cases, and ensure the utilities (`find`, `xargs`, `locate`, etc.) are fully compatible with their GNU counterparts. - -Key areas of work include: -* Implementing missing command-line options and predicates for `find` -* Fixing edge cases in file system traversal and symlink handling -* Completing `xargs` implementation with proper argument handling -* Improving performance and memory efficiency -* Setting up fuzzing infrastructure for differential testing -* Implementing fuzz targets similar to the [coreutils fuzzing approach](https://github.com/uutils/coreutils/tree/main/fuzz/fuzz_targets) -* Running and passing remaining GNU test suite cases -* Conducting differential fuzzing against GNU findutils and BFS - -- **Difficulty**: Medium -- **Size**: ~175 hours -- **Mentors**: Sylvestre -- **Required skills**: - - Rust - - Understanding of file system operations - - Familiarity with `find` and `xargs` usage - - Experience with fuzzing tools is a plus - -## Complete `diffutils` GNU compatibility - -The [uutils/diffutils](https://github.com/uutils/diffutils) project provides Rust implementations of `diff`, `diff3`, `cmp`, and `sdiff`. Significant progress has been made, but additional work is needed to achieve full GNU compatibility and handle all edge cases. - -This project focuses on completing the remaining features, fixing compatibility issues, and ensuring all utilities pass the GNU test suite. - -Key areas of work include: -* Implementing missing options and output formats for `diff` -* Improving algorithm efficiency for large file comparisons -* Completing `diff3` three-way merge functionality -* Fixing edge cases in binary file detection and handling -* Supporting all unified and context diff formats -* Running and passing the GNU diffutils test suite -* Performance benchmarking and optimization -* Adding fuzzing infrastructure for differential testing against GNU diffutils - -- **Difficulty**: Medium -- **Size**: ~175 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Understanding of diff algorithms (Myers, Patience, etc.) - - Familiarity with `diff` and patch workflows - - Text processing and parsing - -## Complete the Rust implementation of `sed` - -The `sed` (stream editor) utility is a fundamental Unix tool for parsing and transforming text. A Rust implementation has been started but requires significant work to achieve full compatibility with GNU `sed` and POSIX standards. - -This project focuses on completing the existing Rust `sed` implementation to make it production-ready. The work involves implementing missing commands and flags, fixing edge cases, improving regular expression support, and ensuring the implementation passes the GNU test suite. - -Key areas of work include: -* Implementing missing `sed` commands and addressing flags -* Handling complex regular expressions and backreferences -* Supporting multi-line pattern space operations -* Implementing hold space operations correctly -* Adding support for GNU `sed` extensions -* Fixing edge cases and improving error handling -* Running and passing the GNU `sed` test suite -* Performance optimization and benchmarking against GNU `sed` -* Setting up fuzzing infrastructure for differential testing -* Implementing fuzz targets similar to the [coreutils fuzzing approach](https://github.com/uutils/coreutils/tree/main/fuzz/fuzz_targets) -* Conducting differential fuzzing against GNU `sed` to identify edge cases and bugs - -- **Difficulty**: Medium -- **Size**: ~175 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Understanding of regular expressions - - Familiarity with `sed` usage and scripting - - Text processing and parsing concepts - - Experience with fuzzing tools (AFL++, cargo-fuzz) is a plus - -## Rust implementation of `grep` - -The goal of this project is to create a high-performance, feature-complete Rust implementation of `grep` (GNU grep) as part of the uutils ecosystem. While tools like `ripgrep` exist, this project aims to provide a drop-in replacement for GNU `grep` with full compatibility, including all command-line options, output formats, and edge case behaviors. - -The `grep` utility is one of the most widely-used Unix tools for searching text using patterns. A uutils implementation would need to balance GNU compatibility with the performance advantages that Rust can provide. - -Key aspects of the project include: -* Implementing full POSIX and GNU `grep` command-line interface -* Supporting basic regular expressions (BRE), extended regular expressions (ERE), and Perl-compatible regular expressions (PCRE) -* Implementing all output modes (normal, inverted match, count, files-with-matches, etc.) -* Supporting context lines (-A, -B, -C options) and various formatting options -* Handling binary files, compressed files, and recursive directory search -* Optimizing performance for common use cases while maintaining correctness -* Implementing color output and various line-buffering modes -* Running and passing the GNU `grep` test suite -* Setting up fuzzing infrastructure and differential testing against GNU `grep` -* Performance benchmarking against GNU `grep` and other implementations - -- **Difficulty**: Hard -- **Size**: ~350 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Deep understanding of regular expression engines - - Familiarity with `grep` usage and advanced features - - Performance optimization and profiling - - Text processing and I/O optimization techniques - -## Rust implementation of `awk` - -The goal of this project is to create a Rust-based implementation of `awk`, one of the most powerful and widely-used text processing utilities in Unix/Linux systems. The `awk` utility provides a complete programming language for pattern scanning and processing, making it essential for data extraction, report generation, and text transformation tasks. - -This implementation would be a standalone project within the uutils ecosystem, similar to how `findutils` and `diffutils` are organized. The primary objectives are to achieve compatibility with POSIX `awk` specification and GNU `awk` (gawk) extensions, while leveraging Rust's performance and safety guarantees. - -Key aspects of the project include: -* Implementing the `awk` lexer and parser for the AWK programming language -* Building the pattern-action execution engine -* Supporting built-in variables (`NR`, `NF`, `FS`, `RS`, etc.) and functions -* Implementing regular expression matching and field splitting -* Adding support for arrays, user-defined functions, and control flow -* Ensuring compatibility with the POSIX `awk` standard -* Gradually implementing GNU `awk` extensions where feasible -* Setting up GNU test suite execution for validation - -- **Difficulty**: Hard -- **Size**: ~350 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Understanding of lexers, parsers, and interpreters - - Familiarity with `awk` usage and programming - - Knowledge of regular expressions - - Experience with programming language implementation is a plus - -## Complete `procps` implementation and GNU compatibility - -The [uutils/procps](https://github.com/uutils/procps) project aims to reimplement process and system monitoring utilities in Rust. While initial implementations have been started for various tools, this project focuses on completing the core utilities and achieving production readiness with full GNU compatibility. - -This project focuses on completing the most essential procps utilities (`ps`, `top`, `pgrep`, `pkill`, `free`, `uptime`) and ensuring they are ready for real-world usage. - -Key areas of work include: -* Completing core functionality for essential procps utilities -* Implementing missing command-line options and output formats -* Fixing edge cases in /proc filesystem parsing across different kernel versions -* Ensuring accurate process information gathering and display -* Implementing performance optimizations for tools like `top` and `ps` -* Setting up and running GNU procps test suite -* Adding comprehensive error handling and validation -* Creating fuzzing infrastructure for robust /proc parsing -* Performance benchmarking against GNU procps - -- **Difficulty**: Medium -- **Size**: ~175 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Understanding of Linux /proc filesystem - - Familiarity with process management and system monitoring - - Knowledge of procps tools usage - -## Complete `util-linux` implementation and GNU compatibility - -The [uutils/util-linux](https://github.com/uutils/util-linux) project aims to reimplement essential system utilities in Rust. This project focuses on completing the most commonly-used util-linux utilities and achieving production-ready status with full GNU compatibility. - -This project prioritizes completing utilities that are frequently used in scripts and system administration (`dmesg`, `lscpu`, `mount`, `umount`, `kill`, `logger`). - -Key areas of work include: -* Completing implementation of high-priority util-linux utilities -* Implementing missing options and edge case handling -* Ensuring proper interaction with kernel interfaces and system calls -* Supporting various Linux distributions and kernel versions -* Setting up and running GNU util-linux test suite -* Adding comprehensive error handling for system operations -* Performance optimization and resource efficiency -* Creating fuzzing infrastructure for system call interactions -* Documentation and man page compatibility - -- **Difficulty**: Medium -- **Size**: ~175 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Understanding of Linux system calls and kernel interfaces - - Familiarity with util-linux utilities - - System administration knowledge - -## Complete `bsdutils` implementation - -The [uutils/bsdutils](https://github.com/uutils/bsdutils) project focuses on reimplementing BSD-origin utilities commonly found on Linux systems. This project aims to complete the core utilities and achieve compatibility with both BSD and GNU/Linux variants. - -This project focuses on completing essential bsdutils tools like `logger`, `script`, `column`, `hexdump`, and `look`, ensuring they work correctly across different Unix-like systems. - -Key areas of work include: -* Completing implementation of core bsdutils utilities -* Ensuring compatibility with both BSD and GNU/Linux behavior -* Implementing missing command-line options and output formats -* Handling cross-platform differences and portability -* Setting up test suites for both BSD and GNU variants -* Adding comprehensive error handling -* Performance optimization and memory efficiency -* Creating fuzzing infrastructure for robust input handling -* Documentation and compatibility notes - -- **Difficulty**: Medium -- **Size**: ~175 hours -- **Mentors**: TBD -- **Required skills**: - - Rust - - Familiarity with both BSD and Linux environments - - Understanding of bsdutils tools - - Cross-platform development experience - -## Localization -Support for localization for formatting, quoting & sorting in various utilities, like `date`, `ls` and `sort`. For this project, we need to figure out how to deal with locale data. The first option is to use the all-Rust `icu4x` library, which has a different format than what distributions usually provide. In this case a solution _could_ be to write a custom `localedef`-like command. The second option is to use a wrapper around the C `icu` library, which comes with the downside of being a C dependency. - -This is described in detail in [issue #3997](https://github.com/uutils/coreutils/issues/3997). - -And was also discussed in [#1919](https://github.com/uutils/coreutils/issues/1919#issuecomment-846471073), [#3584](https://github.com/uutils/coreutils/issues/3584). - -- Difficulty: Hard -- Size: TBD -- Mentors: TBD -- Required skills: - - Rust - -## `procps`: Development of Process Management and Information Tools in Rust - -This project focuses on creating Rust-based implementations of process management and information tools: `ps`, `pgrep`, `pidwait`, `pkill`, `skill`, and `snice`. The goal is to ensure full compatibility with all options and successful passing of GNU tests, maintaining the functionality and reliability of these essential tools. - -- **Description:** Develop Rust-based versions of key process management and information tools, ensuring compatibility with all options and GNU tests. -- **Expected Outputs:** Efficient, reliable tools with full option compatibility and passing GNU tests. -- **Skills Required/Preferred:** Proficiency in Rust, understanding of Linux process management, experience with GNU testing methodologies. -- **Possible Mentors:** [To be determined] -- **Size:** ~350 hours. -- **Difficulty:** Medium. - -## `procps`: Development of System Monitoring and Statistics Tools in Rust - -This project involves the Rust-based development of system monitoring and statistics tools: `top`, `vmstat`, `tload`, `w`, and `watch`. The objective is to achieve full compatibility with all options and to pass GNU tests, ensuring these tools provide accurate and reliable system insights. - -- **Description:**: Create Rust versions of system monitoring and statistics tools, with a focus on full option compatibility and passing GNU tests. -- **Expected Outputs:**: Robust tools for system monitoring and statistics, fully compatible with existing options and verified by GNU tests. -- **Skills Required/Preferred:**: Rust expertise, knowledge of system performance metrics, familiarity with GNU testing frameworks. -- **Possible Mentors:**: [To be determined] -- **Size:**: ~350 hours. -- **Difficulty:**: Medium. - -## `procps`: Development of Memory and Resource Analysis Tools in Rust - -The aim of this project is to develop Rust-based versions of memory and resource analysis tools: `pmap` and `slabtop`. The project will focus on ensuring full compatibility with all options and passing GNU tests, providing in-depth and reliable analysis of memory usage and kernel resources. - -- **Description:**: Implement Rust versions of memory and resource analysis tools, with emphasis on option compatibility and passing GNU tests. -- **Expected Outputs:**: Advanced tools for memory and resource analysis, fully compatible with existing options and validated by GNU tests. -- **Skills Required/Preferred:**: Proficiency in Rust, deep understanding of memory management and kernel resources, experience with GNU testing methodologies. -- **Possible Mentors:**: [To be determined] -- **Size:**: ~175 hours. -- **Difficulty:**: Medium. - -## `util-linux`: Reimplementation of essential system utilities in Rust - -The objective of this project is to reimplement essential system utilities from the util-linux package in Rust. This initiative will include the development of Rust-based versions of various utilities, such as `dmesg`, `lscpu`, `lsipc`, `lslocks`, `lsmem`, and `lsns`. The primary focus will be on ensuring that these Rust implementations provide full compatibility with existing options and pass GNU tests, delivering reliable and efficient system utilities for Linux users. - -- **Description:**: Reimplement essential system utilities, including `dmesg`, `lscpu`, `lsipc`, `lslocks`, `lsmem`, and `lsns`, using Rust while emphasizing compatibility with existing options and GNU test validation. -- **Expected Outputs:**: Rust-based system utilities that mirror the functionality of their counterparts, ensuring full compatibility and reliability, validated by GNU tests. -- **Skills Required/Preferred:**: Proficiency in Rust programming, knowledge of system utilities and their functionality, experience with GNU testing methodologies. -- **Possible Mentors:**: [To be determined] -- **Size:**: ~175 hours. -- **Difficulty:**: Medium. - -## `util-linux`: Process and Resource Management: Reimplementation in Rust - -This project focuses on the reimplementations of crucial Linux utilities related to process and resource management in the Rust programming language. The target utilities include `runuser`, `sulogin`, `chrt`, `ionice`, `kill`, `renice`, `prlimit`, `taskset`, and `uclampset`. The primary goal is to create Rust-based versions of these utilities, ensuring compatibility with their original counterparts, and validating their functionality with GNU tests. - -- **Description:**: Reimplement key Linux utilities for process and resource management, such as `runuser`, `sulogin`, `chrt`, `ionice`, `kill`, `renice`, `prlimit`, `taskset`, and `uclampset`, in the Rust programming language. The emphasis is on maintaining compatibility with the original utilities and validating their functionality using GNU tests. -- **Expected Outputs:**: Rust-based versions of the specified utilities that seamlessly integrate into Linux systems, providing the same functionality and passing GNU tests for reliability. -- **Skills Required/Preferred:**: Proficiency in Rust programming, understanding of process and resource management on Linux, experience with GNU testing methodologies. -- **Possible Mentors:**: [To be determined] -- **Size:**: ~175 hours. -- **Difficulty:**: Medium. - -## `util-linux`: User and Session Management: Reimplementation in Rust - -This project focuses on the reimplementations of essential Linux utilities related to user and session management in the Rust programming language. The target utilities include `su`, `agetty`, `ctrlaltdel`, `pivot_root`, `switch_root`, `last`, `lslogins`, `mesg`, `setsid`, and `setterm`. The primary goal is to create Rust-based versions of these utilities, ensuring compatibility with their original counterparts, and validating their functionality with GNU tests. - -- **Description:**: Reimplement essential Linux utilities for user and session management, such as `su`, `agetty`, `ctrlaltdel`, `pivot_root`, `switch_root`, `last`, `lslogins`, `mesg`, `setsid`, and `setterm`, in the Rust programming language. The emphasis is on maintaining compatibility with the original utilities and validating their functionality using GNU tests. -- **Expected Outputs:**: Rust-based versions of the specified utilities that seamlessly integrate into Linux systems, providing the same functionality and passing GNU tests for reliability. -- **Skills Required/Preferred:**: Proficiency in Rust programming, understanding of user and session management on Linux, experience with GNU testing methodologies. -- **Possible Mentors:**: [To be determined] -- **Size:**: ~175 hours. -- **Difficulty:**: Medium. - -This project aims to modernize and enhance critical Linux utilities related to user and session management, ensuring they remain efficient, reliable, and fully compatible with existing systems. - -## Code refactoring for `procps`, `util-linux`, and `bsdutils` - -Refactoring the Rust-based versions of procps, util-linux, and bsdutils to reduce code duplication. - -- **Title:**: Code Optimization and Refactoring for procps, util-linux, and bsdutils in Rust -- **Description:**: This project involves optimizing and refactoring the Rust-based versions of procps, util-linux, and bsdutils. The focus will be on eliminating duplicated code across these utilities, particularly in areas like uudoc, the test framework, and support for single/multicall binaries. -- **Expected outputs:**: A streamlined codebase with reduced duplication, improved maintainability for procps, util-linux, and bsdutils. -- **Skills required/preferred:**: Proficiency in Rust programming, understanding of Linux utilities, experience with code optimization and refactoring. -- **Possible mentors:**: Sylvestre -- **Size:**: 175 hours -- **Difficulty:**: Medium - -## A multicall binary and core library for `findutils` - -`findutils` currently exists of a few unconnected binaries. It would be nice to have a multicall binary (like -`coreutils`) and a library of shared functions (like `uucore`). - -This also might require thinking about sharing code between coreutils and findutils. - -- **Difficulty**: Medium -- **Size**: 175 hours -- **Mentors**: TBD -- Required skills: - - Rust - -## Implementation of GNU Test Execution for `procps`, `util-linux`, `diffutils`, and `bsdutils` - -The project aims at integrating the GNU test suite execution using the Rust-based versions of `procps`, `util-linux`, `diffutils`, and `bsdutils`, ensuring compatibility, crucial for seamless drop-in replacement integration. We have been doing such operation successfully for the Coreutils using [GitHub Actions](https://github.com/uutils/coreutils/blob/main/.github/workflows/GnuTests.yml), a [build script](https://github.com/uutils/coreutils/blob/main/util/build-gnu.sh) and a [run script](https://github.com/uutils/coreutils/blob/main/util/run-gnu-test.sh). - -- **Description:**: Run the GNU test suite on the Rust-based versions of procps, util-linux, diffutils, and bsdutils -- **Expected Outputs:**: The GNU test suite execution for each utility, ensuring functionality meets expected standards -- **Skills Required/Preferred:**: GitHub action understanding, Proficiency in Rust, experience with GNU testing methodologies, familiarity with Linux system utilities, and understanding of software testing principles. -- **Possible Mentors:**: Sylvestre -- **Size:**: ~175 hours -- **Difficulty:**: Medium - -## Symbolic/Fuzz Testing and Formal Verification of Tool Grammars - -See [Using Lightweight Formal Methods to Validate a Key Value Storage Node In Amazon S3](https://www.amazon.science/publications/using-lightweight-formal-methods-to-validate-a-key-value-storage-node-in-amazon-s3). - -Most KLEE scaffolding was done for [KLEE 2021](https://project-oak.github.io/rust-verification-tools/2021/07/14/coreutils.html). - -Start with `wc`, formalize the command line grammar. Get it working under AFL++ and Klee. Add several proofs of resource use and correctness - especially proofs about operating system calls and memory/cache usage. Generalize to other tools. Try to unify the seeds for the fuzzer and KLEE so they can help each other find new paths. Use QEMU to test several operating systems and architectures. Automate detection of performance regressions - try to hunt for [accidentally quadratic](https://accidentallyquadratic.tumblr.com) behavior. - -Specific to `wc` - formalize the inner loop over a UTF-8 buffer into a finite state automata with counters that can generalize into SIMD width operations like [simdjson](https://simdjson.org). Further generalize into a monoid so K processors can combine results. - -- Difficulty: Mixed -- Size: Mixed -- Mentors: TBD - informally @chadbrewbaker -- Required skills: - - Rust - - KLEE - - Fuzzers like AFL++ - - Grammar testing frameworks like [LARK](https://github.com/ligurio/lark-grammars/tree/master/lark_grammars/grammars) - - /usr/bin/time -v (and similar tools for Widows/OSX). - - Alloy, TLA+, [P](https://github.com/p-org/P) - - System call tracing with [strace](https://jvns.ca/blog/2014/02/17/spying-on-ssh-with-strace/), [uftrace](https://github.com/namhyung/uftrace) etc. - - SMT solvers like [Z3](https://www.philipzucker.com/programming-and-interactive-proving-with-z3py/) and CVC5 for superoptimization and proofs of automata equivalence. - - [SOUPER](https://github.com/google/souper) and [CompilerExplorer](https://godbolt.org) - - Basic statistics on quantiles (histograms) for outlier detection. The math is simple as generalizing from one to k medians but the formal notation is [complex](https://aakinshin.net/posts/thdqe-hdi/). - - [MPI-IO](https://wgropp.cs.illinois.edu/courses/cs598-s16/lectures/lecture32.pdf), just enough to read a file into k parts and combine "wc" outputs to understand multicore scaling. - -## Development of advanced terminal session recording and replay tools in Rust - -This project involves creating Rust-based implementations of `/usr/bin/script`, `/usr/bin/scriptlive`, and `/usr/bin/scriptreplay`. The `/usr/bin/script` command will record terminal sessions, `/usr/bin/scriptlive` will offer real-time recording features, and `/usr/bin/scriptreplay` will be used to replay recorded sessions. - -The work will happen in https://github.com/uutils/bsdutils. - -- **Description:**: Develop Rust-based versions of `/usr/bin/script`, `/usr/bin/scriptlive`, and `/usr/bin/scriptreplay` for terminal session recording and replaying. -- **Expected Outputs:**: Robust and cross-platform terminal session recording and replay tools, with real-time features in `scriptlive`. -- **Skills Required/Preferred:**: Proficiency in Rust, understanding of terminal emulation, experience with cross-platform development. -- **Possible Mentors:**: [To be determined] -- **Size:**: ~175 hours -- **Difficulty:**: Medium - -## Official Redox support -We want to support the Redox operating system, but are not actively testing against it. Since the last round of fixes in [#2550](https://github.com/uutils/coreutils/pull/2550), many changes have probably been introduced that break Redox support. This project would involve setting up Redox in the CI and fixing any issues that arise and porting features over. - -- Difficulty: Medium -- Size: 175 hours -- Mentors: TBD -- Required skills: - - Rust +These are starting points for a Google Summer of Code project with uutils. Feel free to adapt one or propose your own, and see the [guidelines for the project list](https://google.github.io/gsocguides/mentor/defining-a-project-ideas-list). Each idea lists its difficulty, an estimated size (~175 or ~350 hours) and a suggested mentor where one is available. + +
+ +
+

Performance optimization for coreutils

+
+ Medium + ~175h + Mentor: TBD +
+

uutils/coreutils has strong GNU compatibility, but some utilities can still be made faster. Systematically profile, benchmark and optimize the hot paths so they match or beat GNU coreutils.

+
    +
  • Profile utilities with perf, flamegraph and criterion
  • +
  • Build a benchmark suite comparing against GNU coreutils
  • +
  • Optimize hot paths in cat, cut, sort, uniq, wc, etc.
  • +
  • Reduce allocations, improve buffering, use SIMD where it helps
  • +
+

Skills: Rust, performance profiling, systems/I/O optimization; SIMD a plus.

+
+ +
+

Expand differential fuzzing for coreutils

+
+ Medium + ~175h + Mentor: TBD +
+

coreutils has some fuzzing infrastructure, but many utilities lack coverage. Expand differential fuzzing that compares uutils against GNU to catch discrepancies automatically.

+
    +
  • Add fuzz targets for utilities that currently lack them
  • +
  • Build differential harnesses comparing uutils vs GNU output
  • +
  • Run campaigns with AFL++ and libFuzzer, wire them into CI
  • +
  • Triage and fix the bugs the fuzzers find
  • +
+

Skills: Rust, fuzzing tools (AFL++, libFuzzer, cargo-fuzz), differential testing.

+
+ +
+

Complete findutils GNU compatibility

+
+ Medium + ~175h + Mentor: Sylvestre +
+

uutils/findutils already passes more than half of the GNU findutils and BFS tests. Finish the remaining work to reach full compatibility and production readiness.

+
    +
  • Implement missing options and predicates for find
  • +
  • Fix edge cases in traversal and symlink handling
  • +
  • Complete xargs argument handling
  • +
  • Pass the remaining GNU tests; add differential fuzzing
  • +
+

Skills: Rust, filesystem operations, find/xargs usage; fuzzing a plus.

+
+ +
+

Complete diffutils GNU compatibility

+
+ Medium + ~175h + Mentor: TBD +
+

uutils/diffutils implements diff, diff3, cmp and sdiff. Complete the remaining features and edge cases so it passes the GNU test suite.

+
    +
  • Implement missing options and output formats for diff
  • +
  • Improve algorithm efficiency for large files
  • +
  • Complete diff3 three-way merges
  • +
  • Pass the GNU diffutils tests; add differential fuzzing
  • +
+

Skills: Rust, diff algorithms (Myers, Patience), text processing.

+
+ +
+

Complete the Rust implementation of sed

+
+ Medium + ~175h + Mentor: TBD +
+

uutils/sed has been started but needs significant work to fully match GNU sed and POSIX. Implement the missing commands and edge cases and make it pass the GNU test suite.

+
    +
  • Implement missing commands and addressing flags
  • +
  • Handle complex regex, backreferences and multi-line pattern space
  • +
  • Implement hold-space operations correctly
  • +
  • Pass the GNU sed tests; add differential fuzzing
  • +
+

Skills: Rust, regular expressions, sed scripting, text processing.

+
+ +
+

Rust implementation of grep

+
+ Hard + ~350h + Mentor: TBD +
+

Build a high-performance, feature-complete drop-in replacement for GNU grep - full command-line interface, output modes and edge-case behavior, with the performance Rust can provide.

+
    +
  • Implement the full POSIX/GNU grep CLI
  • +
  • Support BRE, ERE and PCRE patterns
  • +
  • Handle context lines, recursive search, binary and compressed files
  • +
  • Pass the GNU grep tests; benchmark against GNU grep
  • +
+

Skills: Rust, regex engines, performance optimization, I/O.

+
+ +
+

Rust implementation of awk

+
+ Hard + ~350h + Mentor: TBD +
+

Implement awk, a complete programming language for pattern scanning and text processing, targeting POSIX awk and GNU awk (gawk) extensions.

+
    +
  • Build the lexer, parser and pattern-action execution engine
  • +
  • Support built-in variables (NR, NF, FS, RS) and functions
  • +
  • Implement field splitting, arrays, user functions and control flow
  • +
  • Set up GNU test suite execution for validation
  • +
+

Skills: Rust, lexers/parsers/interpreters, regex; language implementation a plus.

+
+ +
+

Complete procps implementation

+
+ Medium + ~350h + Mentor: TBD +
+

uutils/procps reimplements the process and system monitoring tools. Complete the core utilities and reach production readiness with full GNU compatibility. Scope can be focused on one of the groups below.

+
    +
  • Process management & info: ps, pgrep, pidwait, pkill, skill, snice
  • +
  • System monitoring & statistics: top, vmstat, tload, w, watch
  • +
  • Memory & resource analysis: pmap, slabtop, free, uptime
  • +
  • Robust /proc parsing across kernels; run the GNU procps tests
  • +
+

Skills: Rust, Linux /proc filesystem, process management and monitoring.

+
+ +
+

Complete util-linux implementation

+
+ Medium + ~350h + Mentor: TBD +
+

uutils/util-linux reimplements essential system utilities. Complete the most commonly used tools and reach production-ready status with full compatibility. Scope can be focused on one of the groups below.

+
    +
  • Essential system utilities: dmesg, lscpu, lsipc, lslocks, lsmem, lsns, mount, umount
  • +
  • Process & resource management: chrt, ionice, kill, renice, prlimit, taskset, runuser
  • +
  • User & session management: su, agetty, last, lslogins, mesg, setsid, setterm
  • +
  • Run the GNU util-linux tests; man-page compatibility
  • +
+

Skills: Rust, Linux system calls and kernel interfaces, system administration.

+
+ +
+

Complete bsdutils implementation

+
+ Medium + ~175h + Mentor: TBD +
+

uutils/bsdutils reimplements BSD-origin utilities found on Linux. Complete the core tools with compatibility across BSD and GNU/Linux variants.

+
    +
  • Complete logger, column, hexdump, look and friends
  • +
  • Terminal session recording: script, scriptlive, scriptreplay
  • +
  • Handle cross-platform differences and portability
  • +
  • Set up test suites for both BSD and GNU variants
  • +
+

Skills: Rust, BSD and Linux environments, terminal emulation, cross-platform development.

+
+ +
+

Localization

+
+ Hard + Size: TBD + Mentor: TBD +
+

Support localization for formatting, quoting and sorting in utilities like date, ls and sort. The core question is how to deal with locale data: the all-Rust icu4x library (possibly with a custom localedef-like command) versus a wrapper around the C icu library.

+ +

Skills: Rust, Unicode/locale handling.

+
+ +
+

Code refactoring for procps, util-linux & bsdutils

+
+ Medium + ~175h + Mentor: Sylvestre +
+

Refactor the Rust versions of procps, util-linux and bsdutils to reduce duplication, particularly around uudoc, the test framework, and single/multicall binary support.

+
    +
  • Eliminate duplicated code across the three projects
  • +
  • Unify the documentation and test scaffolding
  • +
  • Improve maintainability and shared infrastructure
  • +
+

Skills: Rust, Linux utilities, code optimization and refactoring.

+
+ +
+

A multicall binary and core library for findutils

+
+ Medium + ~175h + Mentor: TBD +
+

findutils currently consists of a few unconnected binaries. Build a multicall binary (like coreutils) and a library of shared functions (like uucore).

+
    +
  • Design a unified multicall entry point
  • +
  • Extract shared functionality into a core library
  • +
  • Consider sharing code between coreutils and findutils
  • +
+

Skills: Rust, library and CLI design.

+
+ +
+

GNU test execution for procps, util-linux, diffutils & bsdutils

+
+ Medium + ~175h + Mentor: Sylvestre +
+

Integrate the upstream test suites against the Rust versions of procps, util-linux, diffutils and bsdutils - crucial for proving drop-in compatibility. We already do this for coreutils via GitHub Actions, a build script and a run script.

+
    +
  • Adapt the coreutils CI approach to each project
  • +
  • Wire the test suites into GitHub Actions
  • +
  • Track and report compatibility over time
  • +
+

Skills: GitHub Actions, Rust, GNU testing methodologies, Linux utilities.

+
+ +
+

Symbolic/fuzz testing and formal verification of tool grammars

+
+ Mixed + Size: Mixed + Mentor: TBD (informally @chadbrewbaker) +
+

Inspired by lightweight formal methods at AWS; most KLEE scaffolding was done for KLEE 2021. Start with wc, formalize its command-line grammar, run it under AFL++ and KLEE, then generalize.

+
    +
  • Add proofs of resource use and correctness, especially around syscalls and memory
  • +
  • Unify fuzzer and KLEE seeds so they help each other find paths
  • +
  • Formalize the wc UTF-8 inner loop into a SIMD-friendly automaton / monoid
  • +
  • Automate detection of accidentally quadratic behavior
  • +
+

Skills: Rust, KLEE, AFL++, SMT solvers (Z3, CVC5), TLA+/Alloy, grammar testing.

+
+ +
+

Official Redox support

+
+ Medium + ~175h + Mentor: TBD +
+

We want to support the Redox operating system but are not actively testing against it. Since the last round of fixes in #2550, regressions have likely crept in.

+
    +
  • Set up Redox in CI
  • +
  • Fix the issues that arise and port missing features
  • +
+

Skills: Rust, cross-platform/OS development.

+
+ +
diff --git a/content/playground.md b/content/playground.md index aa14e87a3..73f9575dc 100644 --- a/content/playground.md +++ b/content/playground.md @@ -17,7 +17,20 @@ template = "page.html" Extra programs:
-
+
+
+ user@uutils: ~ + + + +
+
+
+ +
+ + Run a command, then copy a link that reruns it for anyone you share it with. +

@@ -25,118 +38,7 @@ template = "page.html" - +
- - ## Sharing commands via URL -You can pre-fill the terminal with a command using the `?cmd=` URL parameter. The command runs automatically when the page loads - great for sharing examples or linking from documentation. +After running a command, click **🔗 Copy share link** below the terminal to copy a link that reruns it. You can also build one by hand with the `?cmd=` URL parameter. The command runs automatically when the page loads - great for sharing examples or linking from documentation. **Examples:** diff --git a/content/sed.md b/content/sed.md index c2ba7dafb..9267e4cac 100644 --- a/content/sed.md +++ b/content/sed.md @@ -5,6 +5,7 @@ template = "project.html" [extra] wip = true +status = "alpha" +++ diff --git a/content/shadow-rs.md b/content/shadow-rs.md deleted file mode 100644 index 3f365e34f..000000000 --- a/content/shadow-rs.md +++ /dev/null @@ -1,32 +0,0 @@ -+++ - -title = "shadow-rs" -template = "project.html" - -[extra] -wip = true - -+++ - -Memory-safe Rust reimplementation of Linux shadow-utils: `useradd`, `userdel`, `usermod`, `passwd`, `pwck`, `chpasswd`, `chage`, `groupadd`, `groupdel`, `groupmod`, `grpck`, `chfn`, `chsh`, and `newgrp`. - -This project aims to be a drop-in replacement for the original commands, with the same flags, exit codes, and output format as GNU shadow-utils. - -# Goals - -This project aims to be a drop-in replacement for the GNU shadow-utils, with a focus on: - -- **Drop-in compatibility** — same flags, same exit codes, same output format as GNU shadow-utils. -- **Memory safety** — eliminating buffer overflows and use-after-free vulnerabilities through Rust's type system. -- **Security hardening** — Landlock sandboxing and privilege dropping. -- **Comprehensive testing** — unit tests, property-based tests, integration tests, and fuzz targets. - -# Contributing - -To contribute to uutils shadow-rs, please see [CONTRIBUTING](https://github.com/uutils/shadow-rs/blob/main/CONTRIBUTING.md). - -# License - -uutils shadow-rs is licensed under the MIT License - see the [LICENSE](https://github.com/uutils/shadow-rs/blob/main/LICENSE) file for details. - -GNU shadow-utils is licensed under the BSD 3-Clause License. diff --git a/content/shadow.md b/content/shadow.md new file mode 100644 index 000000000..1235bfc9b --- /dev/null +++ b/content/shadow.md @@ -0,0 +1,33 @@ ++++ + +title = "shadow" +template = "project.html" +aliases = ["/shadow-rs"] + +[extra] +wip = true + ++++ + +Memory-safe Rust reimplementation of Linux shadow-utils: `useradd`, `userdel`, `usermod`, `passwd`, `pwck`, `chpasswd`, `chage`, `groupadd`, `groupdel`, `groupmod`, `grpck`, `chfn`, `chsh`, and `newgrp`. + +This project aims to be a drop-in replacement for the original commands, with the same flags, exit codes, and output format as GNU shadow-utils. + +# Goals + +This project aims to be a drop-in replacement for the GNU shadow-utils, with a focus on: + +- **Drop-in compatibility** - same flags, same exit codes, same output format as GNU shadow-utils. +- **Memory safety** - eliminating buffer overflows and use-after-free vulnerabilities through Rust's type system. +- **Security hardening** - Landlock sandboxing and privilege dropping. +- **Comprehensive testing** - unit tests, property-based tests, integration tests, and fuzz targets. + +# Contributing + +To contribute to uutils shadow, please see [CONTRIBUTING](https://github.com/uutils/shadow/blob/main/CONTRIBUTING.md). + +# License + +uutils shadow is licensed under the MIT License - see the [LICENSE](https://github.com/uutils/shadow/blob/main/LICENSE) file for details. + +GNU shadow-utils is licensed under the BSD 3-Clause License. diff --git a/content/team.md b/content/team.md new file mode 100644 index 000000000..9d0dceaa7 --- /dev/null +++ b/content/team.md @@ -0,0 +1,34 @@ ++++ + +title = "Who we are" +template = "page.html" + ++++ + +uutils is a community-driven, open-source effort maintained by volunteers around the world. There is no company behind it, just contributors who care about the future of foundational command-line tools. + +Everything happens in the open on [GitHub](https://github.com/uutils), and newcomers are genuinely welcome. Many of our contributors landed their first-ever open-source patch on a uutils project, and we are happy to help you do the same. + +
+
+ uutils whoami + + + +
+
+
$ uutils team --list
+ +
+
+ +uutils is built by far more people than this list. See the full roster of contributors on each project's [GitHub repository](https://github.com/uutils). diff --git a/content/users.md b/content/users.md new file mode 100644 index 000000000..f26eddb13 --- /dev/null +++ b/content/users.md @@ -0,0 +1,241 @@ ++++ +title = "Who uses uutils" +template = "page.html" ++++ + +uutils is no longer just a development experiment - it is running in production +at some of the largest software organisations in the world. This page documents +known adopters and how they use our tools. + +--- + +## Canonical / Ubuntu + +Canonical is progressively replacing GNU coreutils with uutils across Ubuntu. +Canonical laid out the roadmap in March 2025 in +[*Carefully But Purposefully Oxidising Ubuntu*](https://discourse.ubuntu.com/t/carefully-but-purposefully-oxidising-ubuntu/56995). + +The rollout: + +| Release | Status | +|---|---| +| Ubuntu 25.10 | uutils coreutils ships as default - real-world testing before the LTS | +| Ubuntu 26.04 LTS | rust-coreutils 0.8.0 included; `cp`, `mv`, `rm` remain GNU pending 8 TOCTOU fixes found in audit | +| Ubuntu 26.10 | Target for 100% rust-coreutils | + +To support this transition, Canonical commissioned a two-phase security audit +with [Zellic](https://zellic.io) (December 2025 – March 2026). The audit found +and resolved 113 issues in total, all reported back to the uutils upstream. +The full report is available at +[github.com/Zellic/publications](https://github.com/Zellic/publications/blob/master/uutils%20coreutils%20-%20Zellic%20Audit%20Report.pdf). + +A Canonical engineer also created +[**oxidizr**](https://github.com/jnsgruk/oxidizr), a tool that lets users +on Ubuntu 24.04+ switch to uutils today - replacing GNU binaries with uutils +symlinks, reversibly, in one command. + +**Links:** +- [Discourse: Carefully But Purposefully Oxidising Ubuntu](https://discourse.ubuntu.com/t/carefully-but-purposefully-oxidising-ubuntu/56995) +- [Discourse: Migration to rust-coreutils in 25.10](https://discourse.ubuntu.com/t/migration-to-rust-coreutils-in-25-10/59708) +- [Discourse: An update on rust-coreutils (April 2026)](https://discourse.ubuntu.com/t/an-update-on-rust-coreutils/80773) +- [github.com/jnsgruk/oxidizr](https://github.com/jnsgruk/oxidizr) +- [LWN.net coverage](https://lwn.net/Articles/1014002/) + +--- + +## Microsoft + +Microsoft ships uutils coreutils as **Coreutils for Windows**, a native +Windows distribution of uutils/coreutils, uutils/findutils, and a Microsoft +fork of uutils/grep. It was announced at **Microsoft Build 2026** (June 2, 2026) +as part of Windows becoming a first-class development platform. + +The distribution is available today via: + +``` +winget install Microsoft.Coreutils +``` + +The stated goal is to make moving between Linux, macOS, WSL, containers, and +Windows frictionless: the same commands, flags, and pipelines work the same +way, so existing scripts carry over without translation. + +Microsoft maintains the downstream packaging at +[github.com/microsoft/coreutils](https://github.com/microsoft/coreutils). +The uutils/coreutils project is the upstream - Microsoft builds from it +directly and does not rewrite the tools. + +**Links:** +- [Windows Developer Blog - Build 2026 announcement](https://blogs.windows.com/windowsdeveloper/2026/06/02/build-2026-furthering-windows-as-the-trusted-platform-for-development/) +- [Microsoft Learn - Coreutils for Windows overview](https://learn.microsoft.com/en-us/windows/core-utils/overview) +- [github.com/microsoft/coreutils](https://github.com/microsoft/coreutils) + +--- + +## Snap Inc. (SPECS AR glasses) + +[Snap Inc.](https://snap.com/) ships **Snap OS** - the proprietary Linux-based +operating system powering the [SPECS augmented reality glasses](https://newsroom.snap.com/introducing-specs-augmented-reality-glasses) +announced at Augmented World Expo 2026 - built on **Yocto/OpenEmbedded**. + +A Snap engineer has been the primary contributor and maintainer +of the `uutils-coreutils` recipe in the **meta-openembedded** (`meta-oe`) layer +since Yocto 4.1 (langdale). The recipe uses `PROVIDES = "coreutils"` so uutils +acts as a transparent drop-in replacement for GNU coreutils in embedded images. + +**Links:** +- [SPECS AR glasses announcement](https://newsroom.snap.com/introducing-specs-augmented-reality-glasses) +- [uutils-coreutils recipe on OpenEmbedded Layer Index](https://layers.openembedded.org/rrs/recipedetail/meta-openembedded/3417/) +- [Patches on the Yocto mailing list](https://lists.yoctoproject.org/g/poky/topic/bitbake_recipe_for/92508454) + +--- + +## Debian + +Debian has packaged uutils coreutils since **Debian 12 (Bookworm)** and is +actively tracking upstream releases. + +Debian is also following Ubuntu's path toward making uutils the default. +A **Google Summer of Code 2024** project - *"Improve support of the Rust +coreutils in Debian"* - was mentored to accelerate the +integration. The package is also inherited by downstream Debian-based +distributions including Raspbian, Kali Linux, Parrot OS, PureOS, and deepin 23. + +**Links:** +- [Debian package tracker: rust-coreutils](https://packages.debian.org/search?keywords=rust-coreutils) +- [GSoC 2024 project](https://wiki.debian.org/SummerOfCode2024/ApprovedProjects) + +--- + +## Alpine Linux + +Alpine Linux packages uutils coreutils in its **community repository** (Alpine +3.19+, now at 0.9.0 in Alpine Edge / 3.24). What makes Alpine particularly +notable is the depth of adoption: **29 downstream Alpine packages already +declare a dependency on uutils-coreutils**, including lvm2, netdata, dracut, +Pi-hole, and openvas-scanner. + +When both `uutils-coreutils` and `coreutils` are installed, Alpine's package +manager automatically purges the GNU binaries and replaces them with uutils +symlinks. + +**Links:** +- [Alpine package: uutils-cutils](https://pkgs.alpinelinux.org/package/edge/community/x86_64/uutils-coreutils) + +--- + +## Redox OS + +[Redox OS](https://www.redox-os.org/), the microkernel operating system written +entirely in Rust, uses uutils as its coreutils layer. The +[Redox Book](https://doc.redox-os.org/book/system-tools.html) states it plainly: +"Redox uses the Rust implementation of the GNU Coreutils, uutils." Redox is also +listed as an officially supported platform in the uutils codebase. + +--- + +## VS Code for the Web + +**Microsoft VS Code for the Web** (vscode.dev) uses uutils coreutils compiled +to **WebAssembly/WASI** to power the shell commands (`ls`, `cat`, `date`, etc.) +available in the browser-based terminal. This was documented in the official +VS Code blog in June 2023 and represents one of the first large-scale +production deployments of uutils in a WASM context. + +**Links:** +- [VS Code blog: WebAssembly shell](https://code.visualstudio.com/blogs/2023/06/05/vscode-wasm-wasi) + +--- + +## Buildroot + +[Buildroot](https://buildroot.org/), the widely-used embedded Linux build +system, ships an official `uutils-coreutils` package since April 2025. + +This brings uutils into the reach of a vast ecosystem of IoT, industrial, and +embedded devices built with Buildroot. + +--- + +## Apertus + +[Apertus](https://www.apertus.org/) builds the **AXIOM Beta**, an open-source +professional cinema camera. Their firmware build system runs on Ubuntu hosts +and was updated in December 2025 to support uutils coreutils as the host +toolchain. + +This makes Apertus one of the first hardware projects to explicitly track and +maintain uutils compatibility in their build system. + +**Links:** +- [apertus.org](https://www.apertus.org/) +- [axiom-firmware on GitHub](https://github.com/apertus-open-source-cinema/axiom-firmware) + +--- + +## Fedora / RHEL / EPEL + +uutils coreutils is packaged in **Fedora** since F39/F40 (as `rust-coreutils`, +renamed to `uutils-coreutils` in Fedora 42). It is available in Fedora +42, 43, 44 and Rawhide at version 0.7.0, and in **EPEL 9 and EPEL 10** - making +it available to Red Hat Enterprise Linux users. Fedora is not planning to make +it the default in the near term but maintains the package actively. + +uutils is also packaged in **OpenMandriva** (all branches) and **Apertis** +(the Debian-based embedded automotive Linux distro, v2023–v2027). + +**Links:** +- [Fedora package: uutils-coreutils](https://packages.fedoraproject.org/pkgs/uutils-coreutils/) +- [EPEL package](https://packages.fedoraproject.org/pkgs/uutils-coreutils/) + +--- + +## NixOS + +uutils coreutils is available in **nixpkgs** as `uutils-coreutils` and +`uutils-coreutils-noprefix` (the latter installs commands without the `uu-` +prefix, as drop-in replacements). + +**Links:** +- [NixOS Wiki: uutils coreutils](https://wiki.nixos.org/wiki/Uutils_coreutils) + +--- + +## macOS + +uutils is available on macOS via two package managers: + +- **Homebrew**: `brew install uutils-coreutils` +- **MacPorts**: `port install coreutils-uutils` + +This makes uutils a practical cross-platform development tool: scripts written +with uutils on Linux run identically on macOS developer machines without +depending on GNU coreutils via Homebrew. + +--- + +## Windows (community) + +Beyond Microsoft's official distribution, uutils is available on Windows via: + +- **MSYS2** (all variants): 0.9.0 +- **Scoop**: `scoop install uutils-coreutils` +- **Chocolatey**: `choco install uutils-coreutils` + +--- + +## Termux (Android) and ChromeOS + +- **[Termux User Repository (TUR)](https://github.com/termux-user-repository/tur/blob/master/tur/uutils-coreutils/build.sh#L2)**: + uutils-coreutils 0.9.0 is available for Android terminals via Termux. +- **[Chromebrew](https://github.com/chromebrew/chromebrew/blob/master/packages/uutils_coreutils.rb#L6)**: ChromeOS users can + install uutils-coreutils via the Chromebrew package manager. + +--- + +## Are you using uutils? + +If your project or organisation uses uutils tools, we would love to hear from +you. Open an issue or pull request on +[github.com/uutils/uutils.github.io](https://github.com/uutils/uutils.github.io) +to add your entry to this page. diff --git a/static/CNAME b/static/CNAME new file mode 100644 index 000000000..fbd5bb9e2 --- /dev/null +++ b/static/CNAME @@ -0,0 +1 @@ +uutils.org diff --git a/static/fonts/JetBrainsMono-400.woff2 b/static/fonts/JetBrainsMono-400.woff2 new file mode 100644 index 000000000..585887339 Binary files /dev/null and b/static/fonts/JetBrainsMono-400.woff2 differ diff --git a/static/fonts/JetBrainsMono-500.woff2 b/static/fonts/JetBrainsMono-500.woff2 new file mode 100644 index 000000000..be878e68f Binary files /dev/null and b/static/fonts/JetBrainsMono-500.woff2 differ diff --git a/static/fonts/JetBrainsMono-700.woff2 b/static/fonts/JetBrainsMono-700.woff2 new file mode 100644 index 000000000..3a4e333fc Binary files /dev/null and b/static/fonts/JetBrainsMono-700.woff2 differ diff --git a/static/js/playground.js b/static/js/playground.js new file mode 100644 index 000000000..77318ea5c --- /dev/null +++ b/static/js/playground.js @@ -0,0 +1,176 @@ +/** + * Page wiring for the playground (/playground). + * + * wasm-terminal.js provides the terminal engine and the window.* helpers + * (initPlayground, runInTerminal, getLastCommand, …); this file connects them + * to the page chrome: the on-demand "Load" buttons, the "Copy share link" + * button, the locale dropdown, the available-commands list, the build-version + * footer and the clickable examples. + */ +document.addEventListener("DOMContentLoaded", function() { + initPlayground("wasm-playground"); + + // Build a "Load" button per optional standalone group (grep, find, + // diffutils, sed). These ship as their own WASM modules and load on demand + // to keep the initial page download light; running a command auto-loads its + // module too (e.g. diff/cmp both come from the diffutils module, and find + // loads find/locate/updatedb together). + var loaderBar = document.getElementById("playground-loaders"); + if (loaderBar && Array.isArray(window.uutilsPrograms)) { + window.uutilsPrograms.forEach(function(prog) { + var btn = document.createElement("button"); + btn.className = "playground-loader"; + var markLoaded = function() { + btn.disabled = true; + btn.classList.add("loaded"); + btn.textContent = "✓ " + prog + " loaded"; + }; + var setIdleLabel = function(size) { + btn.textContent = "Load " + prog + (size ? " (" + (size / 1024 / 1024).toFixed(1) + " MB)" : ""); + }; + setIdleLabel(0); + window.programSize(prog).then(function(size) { + if (!btn.classList.contains("loaded") && !btn.disabled) setIdleLabel(size); + }); + btn.addEventListener("click", function() { + if (btn.disabled) return; + btn.disabled = true; + btn.textContent = "Loading " + prog + "…"; + window.loadProgram(prog).then(function(mod) { + if (mod) { + markLoaded(); + } else { + btn.disabled = false; + btn.textContent = prog + " unavailable"; + } + }); + }); + // Keep the button in sync once every module it covers is loaded by + // running a command (a group like "find" backs find/locate/updatedb). + document.addEventListener("uutils:program-loaded", function(e) { + if (e.detail && window.isProgramLoaded(prog)) markLoaded(); + }); + if (window.isProgramLoaded(prog)) markLoaded(); + loaderBar.appendChild(btn); + }); + } + + // "Copy share link" button: builds a ?cmd= URL to the most recent command + // so it can be shared - the link reruns that command on page load. + var shareBtn = document.getElementById("playground-share-btn"); + var shareHint = document.getElementById("playground-share-hint"); + if (shareBtn) { + var shareResetTimer = null; + var buildShareUrl = function(cmd) { + var url = new URL(window.location.href); + url.search = ""; + url.hash = ""; + url.searchParams.set("cmd", cmd); + return url.toString(); + }; + // Enable the button as soon as a command has been run. + document.addEventListener("uutils:command-run", function(e) { + shareBtn.disabled = false; + if (shareHint && !shareBtn.classList.contains("copied")) { + shareHint.textContent = "Shares: " + e.detail.command; + } + }); + shareBtn.addEventListener("click", function() { + var cmd = window.getLastCommand ? window.getLastCommand() : ""; + if (!cmd) return; + var link = buildShareUrl(cmd); + navigator.clipboard.writeText(link).then(function() { + shareBtn.classList.add("copied"); + shareBtn.textContent = "✓ Link copied!"; + if (shareHint) shareHint.textContent = link; + if (shareResetTimer) clearTimeout(shareResetTimer); + shareResetTimer = setTimeout(function() { + shareBtn.classList.remove("copied"); + shareBtn.textContent = "🔗 Copy share link"; + if (shareHint) shareHint.textContent = "Shares: " + cmd; + }, 2000); + }); + }); + } + + // Populate the locale dropdown from the build-generated list + if (typeof WASM_LOCALES !== "undefined") { + var sel = document.getElementById("locale-select"); + WASM_LOCALES.forEach(function(loc) { + if (loc === "en-US") return; // already the default option + var opt = document.createElement("option"); + opt.value = loc; + opt.textContent = loc; + sel.appendChild(opt); + }); + } + + // Populate the "Available commands" list from the build-generated list + if (typeof WASM_COMMANDS !== "undefined" && Array.isArray(WASM_COMMANDS)) { + var listEl = document.getElementById("wasm-commands-list"); + if (listEl) { + listEl.innerHTML = WASM_COMMANDS.slice().sort().map(function(c) { + return "" + c + ""; + }).join(" "); + } + } + + // Show the uutils commit that was used to build the WASM binary, + // and the uutils.github.io commit the site itself was built from. + var el = document.getElementById("playground-version"); + if (el) { + var parts = []; + if (typeof UUTILS_WASM_VERSION !== "undefined") { + var date = UUTILS_WASM_VERSION.date.split("T")[0]; + var url = "https://github.com/uutils/coreutils/commit/" + UUTILS_WASM_VERSION.commit; + parts.push('Built from uutils/coreutils ' + + UUTILS_WASM_VERSION.short + ' (' + date + ')'); + } + if (typeof UUTILS_GREP_VERSION !== "undefined") { + var grepDate = UUTILS_GREP_VERSION.date.split("T")[0]; + var grepUrl = "https://github.com/uutils/grep/commit/" + UUTILS_GREP_VERSION.commit; + parts.push('grep ' + + UUTILS_GREP_VERSION.short + ' (' + grepDate + ')'); + } + if (typeof UUTILS_FINDUTILS_VERSION !== "undefined") { + var findDate = UUTILS_FINDUTILS_VERSION.date.split("T")[0]; + var findUrl = "https://github.com/uutils/findutils/commit/" + UUTILS_FINDUTILS_VERSION.commit; + parts.push('findutils ' + + UUTILS_FINDUTILS_VERSION.short + ' (' + findDate + ')'); + } + if (typeof UUTILS_DIFFUTILS_VERSION !== "undefined") { + var diffDate = UUTILS_DIFFUTILS_VERSION.date.split("T")[0]; + var diffUrl = "https://github.com/uutils/diffutils/commit/" + UUTILS_DIFFUTILS_VERSION.commit; + parts.push('diffutils ' + + UUTILS_DIFFUTILS_VERSION.short + ' (' + diffDate + ')'); + } + if (typeof UUTILS_SED_VERSION !== "undefined") { + var sedDate = UUTILS_SED_VERSION.date.split("T")[0]; + var sedUrl = "https://github.com/uutils/sed/commit/" + UUTILS_SED_VERSION.commit; + parts.push('sed ' + + UUTILS_SED_VERSION.short + ' (' + sedDate + ')'); + } + if (typeof SITE_VERSION !== "undefined") { + var siteDate = SITE_VERSION.date.split("T")[0]; + var siteUrl = "https://github.com/uutils/uutils.github.io/commit/" + SITE_VERSION.commit; + parts.push('site ' + + SITE_VERSION.short + ' (' + siteDate + ')'); + } + el.innerHTML = parts.join(' · '); + } + + // Clickable examples: run the command and reflect it in the URL so it can + // be shared/bookmarked. + document.querySelectorAll('.playground-example').forEach(function(btn) { + btn.addEventListener('click', function() { + var cmd = btn.textContent; + var url = new URL(window.location.href); + url.searchParams.set('cmd', cmd); + window.history.replaceState(null, '', url); + document.getElementById('wasm-playground').scrollIntoView({ behavior: 'smooth' }); + if (window.runInTerminal) { + window.runInTerminal(cmd); + } + }); + }); +}); diff --git a/static/js/wasm-terminal.js b/static/js/wasm-terminal.js index b073f3b09..d067e3e3e 100644 --- a/static/js/wasm-terminal.js +++ b/static/js/wasm-terminal.js @@ -124,6 +124,7 @@ let wasmSize = 0; // downloaded binary size in bytes let persistentDir = null; let cwd = ""; // virtual current working directory (relative to preopened root) let currentLocale = "en-US"; // current locale for l10n +let lastCommand = ""; // most recent command line run, for the "Share" button function loadScript(src, integrity) { return new Promise((resolve, reject) => { @@ -292,7 +293,7 @@ function getPersistentDir() { */ function resolvePath(p) { if (!p || p.startsWith("-")) return p; - // Absolute paths (starting with /) stay as-is — WASI preopened dir is "." + // Absolute paths (starting with /) stay as-is - WASI preopened dir is "." // so absolute paths won't resolve anyway, but don't mangle them. if (p.startsWith("/")) return p; const base = cwd ? cwd.split("/") : []; @@ -617,7 +618,7 @@ async function executeCommandLine(line) { return ""; } - // Builtin: locale — show or set the current locale + // Builtin: locale - show or set the current locale if (line === "locale" || line.startsWith("locale ")) { const arg = line === "locale" ? "" : line.slice(7).trim(); if (!arg) { @@ -769,6 +770,26 @@ async function executeCommandLine(line) { } +/** + * Record a command line that was run, whether typed at the prompt or triggered + * by clicking an example. Adds it to the up-arrow history (skipping consecutive + * duplicates), remembers it for the page's "Share" button, and notifies any + * listeners. Builtins that only affect the local view (clear) aren't worth + * sharing, so they're skipped. + */ +function recordCommand(line) { + line = (line || "").trim(); + if (!line) return; + if (history[history.length - 1] !== line) history.push(line); + historyIndex = -1; + // clear only wipes the local view, so it isn't worth sharing. + if (line === "clear") return; + lastCommand = line; + if (typeof document !== "undefined") { + document.dispatchEvent(new CustomEvent("uutils:command-run", { detail: { command: line } })); + } +} + function writeToTerminal(text) { if (!terminal) return; const lines = text.split("\n"); @@ -882,8 +903,7 @@ async function handleInput(data) { terminal.write("\r\n"); const line = inputBuffer.trim(); if (line) { - history.push(line); - historyIndex = -1; + recordCommand(line); const output = await executeCommandLine(line); if (output) writeToTerminal(output); } @@ -917,7 +937,7 @@ async function handleInput(data) { continue; } - if (code === 9) { // Tab — completion + if (code === 9) { // Tab - completion const result = tabComplete(inputBuffer, cursorPos); if (result.completed) { inputBuffer = result.buffer; @@ -1012,7 +1032,7 @@ async function initPlayground(containerId) { terminal.writeln(""); terminal.writeln("Type \x1b[1;32mhelp\x1b[0m for available commands."); terminal.writeln("Sample data files: names.txt, numbers.txt, fruits.txt, csv.txt, words.txt"); - terminal.writeln("\x1b[2mgrep, find/locate/updatedb, sed and diff/cmp load on demand — just run them, or use the buttons below.\x1b[0m"); + terminal.writeln("\x1b[2mgrep, find/locate/updatedb, sed and diff/cmp load on demand - just run them, or use the buttons above.\x1b[0m"); } catch (e) { terminal.writeln(" \x1b[1;31mfailed\x1b[0m"); terminal.writeln("Failed to load WASM binary. Commands are not available."); @@ -1038,6 +1058,7 @@ async function runInTerminal(cmd) { // Show the command on the prompt line terminal.write(cmd); terminal.write("\r\n"); + recordCommand(cmd); const output = await executeCommandLine(cmd); if (output) writeToTerminal(output); prompt(); @@ -1059,6 +1080,7 @@ window.initPlayground = initPlayground; window.uutilsExecute = executeCommandLine; window.runInTerminal = runInTerminal; window.setLocale = setLocale; +window.getLastCommand = () => lastCommand; // On-demand loading of the optional standalone modules, used by the "Load" // buttons on the playground page. Buttons operate on groups (see diff --git a/static/style.css b/static/style.css index d7c757b6b..ec4816566 100644 --- a/static/style.css +++ b/static/style.css @@ -1,7 +1,63 @@ +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("/fonts/JetBrainsMono-400.woff2") format("woff2"); +} +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("/fonts/JetBrainsMono-500.woff2") format("woff2"); +} +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("/fonts/JetBrainsMono-700.woff2") format("woff2"); +} + :root { /* Light theme colors (default) */ --accent-color: #c04828; + --accent-hover: #d45530; + + --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace; + --font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, + "Helvetica Neue", Arial, "Noto Sans", sans-serif; + + /* Terminal window palette (kept dark in both themes so it reads as a terminal) */ + --term-head: #2b2b2d; + --term-head-fg: #cfcfd6; + --term-comment: #8a8a93; + --term-blue: #6cb6ff; + --st-ready: #7ee2a0; + --st-beta: #f0c870; + --st-alpha: #f0a868; + --st-wip: #8aa0b8; + + --radius: 0.4rem; + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1); + + --card-bg: #fafafa; + --card-border: #e5e5e5; + --muted-fg-color: #595959; + --terminal-bg-color: #1e1e2e; + --terminal-fg-color: #cdd6f4; + + --badge-ready-bg: #e6f4ea; + --badge-ready-fg: #1e7a3c; + --badge-beta-bg: #fff4e0; + --badge-beta-fg: #9a6400; + --badge-alpha-bg: #fdeadb; + --badge-alpha-fg: #b5500a; + --badge-wip-bg: #eef1f5; + --badge-wip-fg: #4a5568; --dark-fg-color: #fff; --light-fg-color: #141414; @@ -19,7 +75,7 @@ --link-text-color: var(--accent-color); --header-border-color: #ddd; --post-bg-color: #e5e5e5; - --font-face: "Fira Sans", sans-serif; + --font-face: var(--font-sans); --github-icon: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23fff' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E"); --github-icon-black: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='000' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E"); @@ -27,18 +83,30 @@ @media (prefers-color-scheme: dark) { :root { - --fg-color: #e1e1e1; - --bg-color: #222222; + --fg-color: #d7dce5; + --bg-color: #0e0f13; --link-color: var(--accent-color); - --light-highlight-bg-color: #2d2d2d; + --light-highlight-bg-color: #161a22; --light-highlight-fg-color: #ffffff; --dark-highlight-bg-color: #27272a; --dark-highlight-fg-color: #ededed; --highlight-fg-color: var(--light-highlight-fg-color); --highlight-bg-color: var(--light-highlight-bg-color); --link-text-color: var(--accent-color); - --header-border-color: #404040; - --post-bg-color: #2d2d2d; + --header-border-color: #232a36; + --post-bg-color: #151922; + --card-bg: #151922; + --card-border: #232a36; + --muted-fg-color: #9aa0ab; + + --badge-ready-bg: #16321f; + --badge-ready-fg: #7ee2a0; + --badge-beta-bg: #3a2e12; + --badge-beta-fg: #f0c870; + --badge-alpha-bg: #3a2a16; + --badge-alpha-fg: #f0a868; + --badge-wip-bg: #2c313a; + --badge-wip-fg: #b6c0cf; } } @@ -91,6 +159,17 @@ a:focus-visible { border-radius: 2px; } +.btn:focus-visible, +.btn-primary:focus-visible, +.btn-ghost:focus-visible, +.playground-toolbar select:focus-visible, +.wasm-run-btn:focus-visible, +#playground-share-btn:focus-visible, +.playground-loader:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: 2px; +} + /* PAGE LAYOUT */ html, body { @@ -126,7 +205,41 @@ header { padding: 0.5rem 2rem; width: 100%; border-bottom: 1px solid var(--header-border-color); - font-size: 1.2rem; + font-size: 1.05rem; + font-family: var(--font-mono); + position: sticky; + top: 0; + z-index: 100; + background-color: color-mix(in srgb, var(--bg-color) 85%, transparent); + backdrop-filter: saturate(160%) blur(10px); +} + +.brand-name { + margin-left: 0.5rem; + font-family: var(--font-mono); + font-weight: 700; + font-size: 1.4rem; + letter-spacing: -0.04em; +} + +@keyframes cursor-blink { + 50% { + opacity: 0; + } +} + +.brand-cursor, +.hero-cursor { + color: var(--accent-color); + font-weight: 400; + animation: cursor-blink 1.1s steps(1) infinite; +} + +@media (prefers-reduced-motion: reduce) { + .brand-cursor, + .hero-cursor { + animation: none; + } } header .home { @@ -166,16 +279,25 @@ header a:hover:not(.home) { } .dropdown-toggle { - cursor: default; + cursor: pointer; color: var(--fg-color); + background: none; + border: none; border-bottom: 2px solid transparent; - padding-bottom: 0.1em; + padding: 0 0 0.1em; + font: inherit; } -.dropdown:hover .dropdown-toggle { +.dropdown:hover .dropdown-toggle, +.dropdown.open .dropdown-toggle { border-bottom: 2px solid var(--link-text-color); } +.dropdown-toggle:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: 2px; +} + .dropdown-menu::before { content: ""; position: absolute; @@ -201,7 +323,8 @@ header a:hover:not(.home) { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } -.dropdown:hover .dropdown-menu { +.dropdown:hover .dropdown-menu, +.dropdown.open .dropdown-menu { display: flex; } @@ -279,30 +402,117 @@ header .icon { /* HERO */ .hero { - margin: 2rem 0; + margin: 2.5rem 0 3rem; + text-align: center; +} + +/* Hero wrapped in the terminal window: the body is always dark, so the + logo, text and spacing are tuned for the dark terminal background. */ +.term-hero .term-body { + padding: 2.5rem 1.5rem 3rem; +} + +.term-hero .hero { + margin: 0; +} + +.term-hero .hero-title { + color: var(--terminal-fg-color); +} + +.term-hero .hero-tagline { + color: var(--term-head-fg); } .hero img { display: block; - height: 14em; - margin: auto; + height: 11em; + margin: 0 auto 1.2rem; } -.hero div { - font-size: 3.75rem; - line-height: 1; - text-align: center; - padding-bottom: 0.5rem; - font-weight: bold; +.hero-prompt { + font-family: var(--font-mono); + font-size: 0.95rem; + color: var(--muted-fg-color); + margin-bottom: 0.7rem; +} + +.term-hero .hero-prompt { + color: var(--term-comment); +} + +.hero-prompt .pr { + color: var(--accent-color); +} + +.hero-title { + font-family: var(--font-mono); + font-size: 3rem; + line-height: 1.05; + letter-spacing: -0.04em; + font-weight: 700; +} + +.hero-tagline { + max-width: 40rem; + margin: 1rem auto 2rem; + font-size: 1.2rem; + line-height: 1.55; + color: var(--muted-fg-color); +} + +.hero-cta { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 1rem; } @media (min-width: 640px) { - .hero div { - font-size: 6rem; - line-height: 1; + .hero-title { + font-size: 4.5rem; + line-height: 1.02; } } +/* BUTTONS */ +.btn { + display: inline-block; + padding: 0.6em 1.4em; + border-radius: var(--radius); + border: 1px solid var(--accent-color); + font-family: var(--font-mono); + font-size: 1rem; + font-weight: 600; + text-align: center; + cursor: pointer; + transition: background-color 0.2s, color 0.2s, transform 0.2s, box-shadow 0.2s; +} + +.btn-primary { + background-color: var(--accent-color); + color: var(--dark-fg-color); +} + +.btn-primary:hover { + background-color: var(--accent-hover); + border-color: var(--accent-hover); + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + +.btn-ghost { + background-color: transparent; + color: var(--link-text-color); +} + +.btn-ghost:hover { + background-color: var(--accent-color); + color: var(--dark-fg-color); + transform: translateY(-1px); + box-shadow: var(--shadow-md); +} + /* MAIN */ main { margin: 1rem auto; @@ -316,17 +526,22 @@ main { } } +h1, +h2, +h3 { + font-family: var(--font-mono); + letter-spacing: -0.02em; +} + h1 { font-size: 1.5rem; - font-weight: 900; - line-height: 2.25rem; + font-weight: 700; line-height: 1.25; margin-bottom: 2rem; } p { font-size: 1rem; - line-height: 1.5rem; line-height: 1.625; margin-bottom: 2rem; } @@ -346,7 +561,6 @@ p { h2 { font-size: 1.5rem; font-weight: 700; - line-height: 2rem; line-height: 1.25; margin-bottom: 1.5rem; } @@ -378,18 +592,43 @@ ul { /* FOOTER */ footer { align-items: center; - background-color: var(--light-fg-color); - color: var(--light-bg-color); + background-color: var(--bg-color); + color: var(--fg-color); + border-top: 1px solid var(--header-border-color); display: flex; + flex-wrap: wrap; + gap: 0.5rem 1.5rem; flex-grow: 0; flex-shrink: 1; - font-size: 0.75rem; + font-family: var(--font-mono); + font-size: 0.82rem; justify-content: space-between; - line-height: 1rem; - padding: 0.5rem 1rem; + line-height: 1.4; + padding: 1rem 2rem; width: 100%; } +.footer-links { + display: flex; + flex-wrap: wrap; + gap: 1.25rem; +} + +.footer-links a { + color: var(--fg-color); + border-bottom: 2px solid transparent; +} + +.footer-links a:hover { + border-bottom: 2px solid var(--link-text-color); +} + +.footer-brand { + display: flex; + align-items: center; + gap: 0.5rem; +} + .github-icon { background-image: var(--github-icon); height: 1.25rem; @@ -408,50 +647,209 @@ footer { } } +/* TERMINAL WINDOW */ +.term { + background: var(--terminal-bg-color); + border: 1px solid var(--card-border); + border-radius: 0.6em; + overflow: hidden; + box-shadow: var(--shadow-md); + margin: 2rem 0; +} + +.term-bar { + position: relative; + display: flex; + justify-content: flex-end; + align-items: center; + gap: 0.35rem; + padding: 0.4rem 0.5rem; + background: var(--term-head); + border-bottom: 1px solid rgba(255, 255, 255, 0.06); +} + +.term-bar .t { + position: absolute; + left: 0; + right: 0; + text-align: center; + font-family: var(--font-mono); + font-size: 0.8rem; + color: var(--term-head-fg); + pointer-events: none; +} + +.win-btn { + width: 24px; + height: 24px; + border-radius: 50%; + display: grid; + place-items: center; + background: rgba(255, 255, 255, 0.09); + color: var(--term-head-fg); + transition: background 0.12s, color 0.12s; +} + +.win-btn svg { + width: 14px; + height: 14px; +} + +.win-btn:hover { + background: rgba(255, 255, 255, 0.18); +} + +.win-btn.close:hover { + background: #e01b24; + color: #fff; +} + +.term-body { + padding: 1.1rem 1.3rem; + font-family: var(--font-mono); + color: var(--terminal-fg-color); + overflow-x: auto; +} + +.term-prompt { + color: var(--term-comment); + margin-bottom: 0.8rem; + font-size: 0.92rem; +} + +.term-prompt .pr { + color: var(--accent-color); +} + +/* PROJECT LIST (command output) */ .projects { display: flex; flex-direction: column; - flex-wrap: wrap; - justify-content: space-between; - width: 100%; - gap: 2rem; - margin-bottom: 1.5rem; } -.project { - border-left: 0.3em solid gray; - padding: 1rem; +.prow { + display: grid; + grid-template-columns: 9.5rem 1fr auto; + gap: 1rem; + align-items: baseline; + padding: 0.7rem 0.3rem; + border-bottom: 1px dashed var(--card-border); text-decoration: none; - color: var(--fg-color); - transition: text-decoration 0.3s; - transition: border-color 0.3s; + color: var(--terminal-fg-color); + transition: background 0.12s; } -.project h2 { - margin-top: 0; - font-family: monospace; +.prow:last-child { + border-bottom: none; } -.project p { - margin-bottom: 0; +.prow:hover { + background: color-mix(in srgb, var(--accent-color) 12%, transparent); } -.project > span { - color: transparent; - transition: color 0.3s; +.prow .name { + font-family: var(--font-mono); + font-weight: 700; + color: var(--accent-color); } -.project:hover > span { - color: var(--fg-color); +.prow .name::before { + content: "\203A "; + color: var(--term-comment); +} + +.prow .desc { + font-family: var(--font-mono); + font-size: 0.9rem; + color: var(--term-head-fg); +} + +.prow .desc code { + color: var(--term-blue); + background: none; + padding: 0; + font-size: 0.95em; +} + +.st { + font-family: var(--font-mono); + font-weight: 700; + font-size: 0.85rem; + white-space: nowrap; +} + +.st::before { + content: "["; + color: var(--term-comment); +} + +.st::after { + content: "]"; + color: var(--term-comment); +} + +.st-ready { + color: var(--st-ready); +} + +.st-beta { + color: var(--st-beta); +} + +.st-alpha { + color: var(--st-alpha); +} + +.st-wip { + color: var(--st-wip); +} + +@media (max-width: 640px) { + .prow { + grid-template-columns: 1fr auto; + } + + .prow .desc { + grid-column: 1 / -1; + } +} + +/* STATUS BADGES (project detail page titles) */ +.badge { + display: inline-block; + font-size: 0.7rem; + font-weight: 700; + padding: 0.2em 0.6em; + border-radius: 0.3em; + vertical-align: middle; + margin-left: 0.6em; + font-family: var(--font-mono); + white-space: nowrap; +} + +.badge-ready { + background: var(--badge-ready-bg); + color: var(--badge-ready-fg); +} + +.badge-beta { + background: var(--badge-beta-bg); + color: var(--badge-beta-fg); +} + +.badge-alpha { + background: var(--badge-alpha-bg); + color: var(--badge-alpha-fg); } -.project:hover { - border-color: #c04828; +.badge-wip { + background: var(--badge-wip-bg); + color: var(--badge-wip-fg); } .title { font-size: 3em; - font-weight: 900; + font-weight: 700; margin-bottom: 0.5em; text-transform: capitalize; } @@ -463,6 +861,113 @@ footer { } } +/* Terminal prompt line under a project-page title, echoing the hero motif. */ +.page-prompt { + font-family: var(--font-mono); + font-size: 0.95rem; + color: var(--muted-fg-color); + margin: -0.25em 0 1.4em; +} + +.page-prompt .pr { + color: var(--accent-color); +} + +/* GSOC PROJECT IDEAS (card grid) */ +.gsoc-ideas { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)); + gap: 1rem; + margin: 2rem 0; +} + +.gsoc-card { + border: 1px solid var(--card-border); + border-radius: var(--radius); + background: var(--card-bg); + padding: 1.1rem 1.2rem; + transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s; +} + +.gsoc-card:hover { + border-color: var(--accent-color); + box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +.gsoc-card-title { + font-family: var(--font-mono); + font-size: 1.05rem; + font-weight: 700; + color: var(--accent-color); + margin: 0 0 0.6rem; + line-height: 1.3; +} + +.gsoc-card-title::before { + content: "\203A "; + color: var(--muted-fg-color); +} + +.gsoc-meta { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin: 0 0 0.7rem; +} + +.gsoc-badge { + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 700; + padding: 0.18em 0.6em; + border-radius: 0.3em; + white-space: nowrap; + background: var(--badge-wip-bg); + color: var(--badge-wip-fg); +} + +.gsoc-badge.diff-easy { + background: var(--badge-ready-bg); + color: var(--badge-ready-fg); +} + +.gsoc-badge.diff-medium { + background: var(--badge-beta-bg); + color: var(--badge-beta-fg); +} + +.gsoc-badge.diff-hard { + background: #fde8e8; + color: #b42318; +} + +@media (prefers-color-scheme: dark) { + .gsoc-badge.diff-hard { + background: #3a1c1c; + color: #f3a39a; + } +} + +.gsoc-card p { + margin: 0 0 0.6rem; +} + +.gsoc-card ul { + margin: 0 0 0.6rem; + padding-left: 1.1rem; +} + +.gsoc-card li { + margin: 0.15rem 0; +} + +.gsoc-skills { + font-size: 0.85rem; + color: var(--muted-fg-color); + margin: 0; +} + .details { margin-bottom: 1.5em; color: var(--fg-color); @@ -501,23 +1006,28 @@ main img { display: flex; flex-wrap: wrap; justify-content: stretch; - gap: 1em; + gap: 0.8em; margin-bottom: 2em; - font-size: 1.2em; + font-family: var(--font-mono); + font-size: 1rem; } .links > a { - padding: 0.2em 1em; + padding: 0.45em 1em; color: var(--link-text-color); - border: 2px solid #c04828; + border: 1px solid var(--accent-color); + border-radius: var(--radius); flex: 1; text-align: center; + transition: background-color 0.2s, color 0.2s, transform 0.2s, box-shadow 0.2s; } .links > a:hover { color: var(--dark-fg-color); - background-color: #c04828; - border-color: #c04828; + background-color: var(--accent-color); + border-color: var(--accent-color); + transform: translateY(-1px); + box-shadow: var(--shadow-md); } /* WASM PLAYGROUND */ @@ -581,6 +1091,53 @@ main img { opacity: 0.9; } +.playground-share { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.6em; + margin: -0.5em 0 1.2em; + font-size: 0.9em; +} + +#playground-share-btn { + padding: 0.3em 0.7em; + background: var(--highlight-bg-color); + color: var(--highlight-fg-color); + border: 1px solid var(--header-border-color); + border-radius: 0.3em; + font-family: "Fira Code", monospace; + font-size: 0.85em; + cursor: pointer; + white-space: nowrap; + transition: border-color 0.2s, background-color 0.2s, opacity 0.2s; +} + +#playground-share-btn:hover:not(:disabled) { + border-color: var(--accent-color); + background: var(--accent-color); + color: white; +} + +#playground-share-btn:disabled { + cursor: default; + opacity: 0.6; +} + +#playground-share-btn.copied { + border-color: #2ea043; + background: #2ea043; + color: white; +} + +.playground-share-hint { + opacity: 0.75; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} + #wasm-playground { width: 100%; min-height: 480px; @@ -590,10 +1147,22 @@ main img { border: 1px solid var(--header-border-color); } +/* Playground wrapped in the terminal-window chrome: the term frame supplies + the border, rounding and shadow, so the inner element drops its own. */ +.term-playground { + margin: 1.5em 0; +} + +.term-playground #wasm-playground { + margin: 0; + border: none; + border-radius: 0; +} + .playground-version { margin: -0.5em 0 1.5em; font-size: 0.85em; - color: var(--secondary-text-color, inherit); + color: var(--muted-fg-color, inherit); opacity: 0.8; } @@ -606,8 +1175,8 @@ main img { align-items: center; justify-content: center; height: 480px; - background: #1e1e2e; - color: #cdd6f4; + background: var(--terminal-bg-color); + color: var(--terminal-fg-color); font-family: monospace; font-size: 1.1em; } @@ -649,7 +1218,7 @@ main img { } .wasm-run-btn:hover { - background: #d45530; + background: var(--accent-hover); } .wasm-run-btn:disabled { @@ -663,8 +1232,8 @@ main img { .wasm-example-output pre { padding: 0.8em 1em; - background: #1e1e2e; - color: #cdd6f4; + background: var(--terminal-bg-color); + color: var(--terminal-fg-color); border-radius: 0 0 0.3em 0.3em; font-family: "Fira Code", monospace; font-size: 0.9em; @@ -699,3 +1268,95 @@ main img { background: var(--accent-color); color: white; } + +/* COPY-TO-CLIPBOARD ON CODE BLOCKS */ +.code-block-wrapper { + position: relative; +} + +.copy-btn { + position: absolute; + top: 0.5em; + right: 0.5em; + padding: 0.25em 0.6em; + font-size: 0.75rem; + font-family: var(--font-face); + font-weight: 600; + color: var(--highlight-fg-color); + background: var(--highlight-bg-color); + border: 1px solid var(--header-border-color); + border-radius: var(--radius); + cursor: pointer; + opacity: 0; + transition: opacity 0.2s, background-color 0.2s, color 0.2s; +} + +.code-block-wrapper:hover .copy-btn, +.copy-btn:focus-visible { + opacity: 1; +} + +.copy-btn:hover { + color: var(--dark-fg-color); + background: var(--accent-color); + border-color: var(--accent-color); +} + +.copy-btn.copied { + opacity: 1; + color: var(--badge-ready-fg); + background: var(--badge-ready-bg); + border-color: var(--badge-ready-fg); +} + +/* REDUCED MOTION */ +@media (prefers-reduced-motion: reduce) { + html, + body { + scroll-behavior: auto; + } + + *, + ::before, + ::after { + transition: none !important; + animation: none !important; + } +} + +/* HOMEPAGE CONTENT (scoped) */ +.home-content h1 { + font-size: 1.7rem; + margin-top: 3.5rem; + margin-bottom: 1.2rem; +} + +.home-content h1::before { + content: "> "; + color: var(--accent-color); +} + +@media (min-width: 640px) { + .home-content h1 { + font-size: 2.1rem; + } +} + +.home-content ul { + list-style: none; + padding-left: 0; +} + +.home-content li { + position: relative; + padding-left: 1.5rem; +} + +.home-content li::before { + content: "+"; + position: absolute; + left: 0; + color: var(--st-ready); + font-family: var(--font-mono); + font-weight: 700; +} diff --git a/templates/base.html b/templates/base.html index ed4828e23..fe6dfa0c5 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,6 +9,8 @@ + + @@ -18,9 +20,9 @@
- {% include "logo.html" %} + {% include "logo.html" %}uutils_
-
@@ -49,10 +63,15 @@