From e5c90fdd40006d2c6aaedf07d242d9ef42265c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 1 Oct 2026 12:57:47 -0400 Subject: [PATCH] =?UTF-8?q?T48h=20Docs=20=C2=B7=20Icons,=20fonts=20and=20a?= =?UTF-8?q?ssets:=20icons,=20adding=20one,=20file=20icons,=20fonts,=20an?= =?UTF-8?q?=20app's=20own=20assets;=20the=20icons=20example=20compiles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 4 ++ examples/docs/icons.rs | 78 +++++++++++++++++++++++++++++++++++++ frontend/src/docs/guides.ts | 2 +- frontend/src/docs/icons.md | 49 +++++++++++++++++++++++ 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 examples/docs/icons.rs create mode 100644 frontend/src/docs/icons.md diff --git a/Cargo.toml b/Cargo.toml index ae92a1e..5423cbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,3 +107,7 @@ path = "examples/docs/focus.rs" [[example]] name = "docs_motion" path = "examples/docs/motion.rs" + +[[example]] +name = "docs_icons" +path = "examples/docs/icons.rs" diff --git a/examples/docs/icons.rs b/examples/docs/icons.rs new file mode 100644 index 0000000..7c59cd0 --- /dev/null +++ b/examples/docs/icons.rs @@ -0,0 +1,78 @@ +use std::f32::consts::FRAC_PI_4; + +use ely_gpui_component::{ + Assets, + files::FileIcon, + primitives::{FocusScope, Icon, IconName, IconTheme}, + theme::{ActiveTheme, IconSize}, +}; +use gpui::{ + App, AppContext, Context, FocusHandle, IntoElement, ParentElement, Radians, Render, Styled, + Window, WindowOptions, div, +}; + +struct Icons { + focus: FocusHandle, +} + +impl Render for Icons { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { + let theme = cx.theme(); + let file = |name: &'static str| { + div() + .flex() + .items_center() + .gap_2() + .child(FileIcon::file(name)) + .child(name) + }; + FocusScope::new(&self.focus) + .root() + .size_full() + .flex() + .flex_col() + .gap_6() + .p_8() + .bg(theme.colors.bg) + .text_color(theme.colors.fg) + .child( + div() + .flex() + .items_center() + .gap_4() + .child(Icon::new(IconName::Check)) + .child(Icon::new(IconName::Rocket).size(IconSize::Xl)) + .child(Icon::new(IconName::CircleCheck).color(theme.colors.success)) + .child(Icon::new(IconName::Package).rotate(Radians(FRAC_PI_4))), + ) + .child( + div() + .flex() + .flex_col() + .gap_2() + .child(file("main.rs")) + .child(file("Cargo.toml")) + .child(file("notes.txt")), + ) + } +} + +fn main() { + gpui_platform::application() + .with_assets(Assets) + .run(|cx: &mut App| { + ely_gpui_component::init(cx); + IconTheme::default() + .name("Cargo.toml", IconName::Package) + .apply(cx); + cx.open_window(WindowOptions::default(), |window, cx| { + cx.new(|cx| { + let focus = cx.focus_handle(); + window.focus(&focus, cx); + Icons { focus } + }) + }) + .expect("window failed to open"); + cx.activate(true); + }); +} diff --git a/frontend/src/docs/guides.ts b/frontend/src/docs/guides.ts index e53b539..3cff0f8 100644 --- a/frontend/src/docs/guides.ts +++ b/frontend/src/docs/guides.ts @@ -6,7 +6,7 @@ import { Marked, type Tokens } from "marked"; const at = (relative: string) => fileURLToPath(new URL(relative, import.meta.url)); /** The guides in reading order, by file name. */ -const ORDER = ["introduction", "installation", "getting-started", "theme", "focus", "motion"]; +const ORDER = ["introduction", "installation", "getting-started", "theme", "focus", "motion", "icons"]; export interface Guide { /** The path segment; the introduction has none. */ diff --git a/frontend/src/docs/icons.md b/frontend/src/docs/icons.md new file mode 100644 index 0000000..b566227 --- /dev/null +++ b/frontend/src/docs/icons.md @@ -0,0 +1,49 @@ +# Icons, fonts and assets + +`Assets` embeds Ely's icons and fonts in the binary. Pass it to `with_assets`; `init` registers the fonts and checks that the icons load. + +## Icons + +Ely bundles 306 Lucide 1.48.0 icons, each an `IconName` variant: `IconName::Check` is `icons/check.svg`, and `name()` returns the Lucide name, `check`. `IconName::ALL` lists them all. + +| Call | Does | +| --- | --- | +| `Icon::new(name)` | Draws the icon at `IconSize::Md` in the theme's `fg`. | +| `.size(IconSize::Xl)` | `Xs` to `Xxl`, from the theme's icon sizes. | +| `.color(color)` | Any color; components pass theme colors. | +| `.rotate(Radians(..))` | Turns the glyph inside its box. | +| `.scale(factor)` | Draws the glyph larger or smaller in the same box. | + +GPUI's `svg()` paints with its own text color alone, so `Icon` always sets one. + +## Adding an icon + +From a clone of the repository: + +1. `scripts/icons.sh ...` fetches Lucide 1.48.0 with npm and copies each SVG into `assets/icons/`. +2. Add a line to the `icons!` list in `src/primitives/icon.rs`, such as `Rocket => "rocket",`. + +## File icons + +`FileIcon::file(name)` shows the icon a file's name suggests, and `FileIcon::folder(open)` a folder's. Ely maps extensions to icons: `rs` and `ts` to `FileCode`, `toml` to `FileCog`, `json` to `FileJson`, images, audio, video, archives and more. An app lays its own rules over that map: + +| Call | Does | +| --- | --- | +| `IconTheme::default().name("Cargo.toml", IconName::Package)` | A file of that exact name, in any case. | +| `.extension("md", IconName::BookOpen)` | Files with that extension, given without its dot. | +| `.apply(cx)` | Serves every window from then on, and repaints. | + +A name rule beats an extension rule, and both beat Ely's map. + +## Fonts + +`init` registers seven fonts from `Assets`: Inter Regular, Medium, SemiBold and Italic; JetBrains Mono Regular and Medium; IBM Plex Sans Regular. The theme draws text in Inter and code in JetBrains Mono, set by its `font_family` and `mono_family` fields. On the web, GPUI answers the system UI font with IBM Plex Sans, and text inside SVGs falls back to it. + +## An app's own assets + +GPUI takes one asset source. An app with files of its own writes an `AssetSource` that answers its own paths and hands every other path to `Assets`. `init` panics unless `icons/check.svg` loads through it. + +## A window of icons + +```rust example=icons +```