Compare commits

...
371 Commits
Author SHA1 Message Date
ZacharyZhang-NYandClaude Opus 5.5 09a52a7662 Tasks · G9: pushed, deployed, answered #6 and #7, closed #6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:14:58 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9f879fd8a1 Shell · ColumnShell's story on the Window & Shell page, the PRD line, stories.json and AGENTS.md (#6)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:14:17 -04:00
ZacharyZhang-NYandClaude Opus 5.5 fea012127a Shell · ColumnShell: columns to the window's top, each under a header that drags it; the traffic lights or controls in the first or last (#6)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:04:34 -04:00
ZacharyZhang-NYandClaude Opus 5.5 8c2f7438e0 Forms · An IME composition restarts the caret's blink, as every edit does, and logs its ranges (#7)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:35:46 -04:00
ZacharyZhang-NYandClaude Opus 5.5 4c36a5aa02 Logo: logo.png, 2048px and transparent, rendered from the mark by scripts/favicon.mjs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 22:27:45 -04:00
ZacharyZhang-NYandClaude Opus 5.5 20a8e2b0ee Site · Logo: the letter E in iso blocks, in the header, the favicon, the touch icon and the OG image
One geometry in src/iso/mark.ts, drawn by the engine the miniatures use. The favicon follows the system's mode; scripts/favicon.mjs takes its colors from styles.css. Below 768px the header shows the mark alone, its word kept as the link's name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 22:19:04 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f281d01f35 Site · Showcase: apps built with Ely, from the public Ely-GPUI-Showcases repo at a pinned commit
The build fetches showcase.lock's commit and runs the catalog's own validator. /showcase/ lists featured apps first, then the rest by newest or stars, with categories and search; /showcase/<id>/ shows previews, links and the README. The header drops its sound switch below 414px so the fourth link fits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:39:13 -04:00
ZacharyZhang-NYandClaude Opus 5.5 7e39a9383d Tests · NumberInput's commit test selects with the platform's secondary key; Tabs tests move to navigation/tests/tabs.rs, under 500 lines
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:59:36 -04:00
Amit VermaandClaude Opus 5.5 2fb505ee98 Primitives · Icon::from_path draws an application's SVG with the theme's size and color; a path the asset source lacks fails at render
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:24:39 -04:00
ZacharyZhang-NY 2f02c82c6a Navigation · Tabs on_context: a right click on a tab reports that tab and the pointer, not the chosen one 2026-10-04 15:53:30 -04:00
ZacharyZhang-NY aaaf0d58b2 Menus · ContextMenu manual: one-line doc 2026-10-04 12:01:34 -04:00
ZacharyZhang-NY 967d1e4705 Menus · ContextMenu manual: opens on the owner's numbered request at a point, not on a right click, for rows that come after the press 2026-10-04 11:04:32 -04:00
ZacharyZhang-NY c0f17eeb48 Buttons · ToggleButton disabled: shown, not pressable, no tab stop, says it is unavailable; its tooltip can give the reason 2026-10-04 07:13:14 -04:00
ZacharyZhang-NY b6cb94cad4 Forms · NumberInput on_commit runs on Enter, blur, steps and scrubs, never while typing; number tests move to number/tests.rs 2026-10-04 06:33:06 -04:00
ZacharyZhang-NY beea6caa04 Tasks · T50g: chapter 44 accepted, item by item 2026-10-04 00:52:22 -04:00
ZacharyZhang-NY b85955ae06 Docs · Rendering: what GPUI can show, RenderSurface and its revision, a compiled example; AGENTS.md and README follow 2026-10-04 00:30:12 -04:00
ZacharyZhang-NY 36d5559682 Tasks · T50e: CI green on Linux, Windows and macOS, arm64 and x86_64 2026-10-04 00:20:56 -04:00
ZacharyZhang-NY 717d07ee4e CI · the crate's tests and the gallery on Linux, Windows and macOS, arm64 and x86_64; tests type the platform's secondary key 2026-10-04 00:00:43 -04:00
ZacharyZhang-NY 399e3a6b53 Rendering · web: the RenderSurface story runs live; an e2e test holds that the slider redraws its field 2026-10-03 23:53:30 -04:00
ZacharyZhang-NY 87832c768a Rendering · gallery page: a banded field drawn at device pixels, its slider bumps the revision; the gallery builds on Linux 2026-10-03 23:31:51 -04:00
ZacharyZhang-NY a538092e99 Rendering · RenderSurface: host frames at the box's device pixels, redrawn on a new size or revision, freed from the atlas 2026-10-03 23:22:49 -04:00
ZacharyZhang-NY 61d2378ae2 Tasks · T50a: chapter 44 Rendering, RenderSurface planned, what gpui can and cannot host 2026-10-03 23:14:27 -04:00
ZacharyZhang-NY ba0e0127b4 Scripts · stories.py reads components from the chapter task files alone, not the GitHub round's list 2026-10-03 22:54:14 -04:00
ZacharyZhang-NYandClaude Opus 5.5 2f8b2f687c Tasks · G4: pushed, #2 closed, #3 answered
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 01:36:16 -04:00
ZacharyZhang-NYandClaude Opus 5.5 0dbb8c747c Forms, a11y · Checkbox, Switch, Radio, RadioGroup, Slider and Select name their role, label and state (#3)
A slider's thumb reports its value, the bounds it can reach and its orientation; a select's trigger its value, placeholder and whether its list is open; a disabled control says so in its description.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 01:35:48 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5ff7c219ef Lists · tree guides fall from the parent's chevron and run unbroken across rows (#2)
The chevron takes its indent as a margin, so no gap parts it from its guide column; the cursor's ring is an overlay, so the guides fill the row through its border.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 01:21:26 -04:00
JetSquirrelandClaude Opus 5.5 a6119f1e4e Terminal · build on Windows: set alacritty's Windows-only escape_args
alacritty_terminal 0.26's tty::Options has a field that exists only on
Windows, so the crate failed to compile there with E0063. Each launch
argument is one argv entry, so they are escaped by the C runtime rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 01:01:17 -04:00
ZacharyZhang-NY 7380afb419 Dialogs · a confirm or alert message names itself to assistive technology 2026-10-02 19:28:01 -04:00
ZacharyZhang-NY cf9770b57e Dialogs, a11y · Dialog card names its role, title and detail; prompt error reads as a label; Cancel and Close read i18n; a disabled button says so in its description 2026-10-02 18:52:08 -04:00
ZacharyZhang-NY 290939f82f i18n, a11y, IME · palette copy reads i18n with builtin en/zh; dialog and palette Enter run on the field's key-down action, so an IME-kept Enter never submits; Button, IconButton, TextInput, Tabs, EditorTabs, StatusBarItem and palette rows name their role and label 2026-10-02 12:58:43 -04:00
ZacharyZhang-NY b54500b0e4 Ely 0.1.1 2026-10-01 14:13:54 -04:00
ZacharyZhang-NY 5a5cd636de T48m Docs · E2E passes review; T48 closes 2026-10-01 14:02:00 -04:00
ZacharyZhang-NY 9bdba85727 T48m Docs · E2E: a hashless prose link proves routing in place; the section link keeps its own test 2026-10-01 13:57:16 -04:00
ZacharyZhang-NY ae7614cbf5 T48m Docs · E2E: each guide's heading, rail name and browser title checked; a prose link routes in place 2026-10-01 13:50:45 -04:00
ZacharyZhang-NY d5511238fe T48m Docs · E2E: the Docs link, every guide, the rail, previous and next, section links, the compiled sample and Copy, an unknown guide; the rail marks only the open guide 2026-10-01 13:41:18 -04:00
ZacharyZhang-NY 33c4d651b8 T48l Docs · Testing passes review 2026-10-01 13:40:30 -04:00
ZacharyZhang-NY ae5c2a19f3 T48l Docs · Testing: the feature, an app's limit, writing a test inside Ely, tests to read 2026-10-01 13:34:47 -04:00
ZacharyZhang-NY a84ec17b6a T48k Docs · WebAssembly passes review 2026-10-01 13:33:37 -04:00
ZacharyZhang-NY 2b2f284ed9 T48k Docs · WebAssembly: pictures land in <out>/assets/, the HTTP client is the web's; table words stay whole and long titles hyphenate 2026-10-01 13:30:33 -04:00
ZacharyZhang-NY 16e001ec00 T48k Docs · WebAssembly: the check, an app in the browser, the gallery build, embedding, what differs 2026-10-01 13:22:53 -04:00
ZacharyZhang-NY 02c2d9dfcd T48j Docs · Internationalization passes review 2026-10-01 13:22:35 -04:00
ZacharyZhang-NY a1203af405 T48j Docs · Internationalization: catalogs and messages, locales, right to left; the i18n example compiles 2026-10-01 13:15:07 -04:00
ZacharyZhang-NY 287fe63306 T48i Docs · Overlays passes review 2026-10-01 13:13:59 -04:00
ZacharyZhang-NY bf8ce3f254 T48i Docs · Overlays: only dialogs, popovers and menus take focus; the close button belongs to a fullscreen dialog 2026-10-01 13:12:13 -04:00
ZacharyZhang-NY 4c3b7811f1 T48i Docs · Overlays: drawing over the page, dialogs, popovers, toasts; the overlays example compiles 2026-10-01 13:07:03 -04:00
ZacharyZhang-NY d38430f46a T48h Docs · Icons, fonts and assets passes review 2026-10-01 13:04:09 -04:00
ZacharyZhang-NY c16c6b69d2 T48h Docs · Icons, fonts and assets: icons, adding one, file icons, fonts, an app's own assets; the icons example compiles 2026-10-01 12:57:47 -04:00
ZacharyZhang-NY a9fd0b58a7 T48g Docs · Motion passes review 2026-10-01 12:55:27 -04:00
ZacharyZhang-NY c41ce2055c T48g Docs · Motion: durations and easings, reduced motion, components that move; the motion example compiles 2026-10-01 12:51:23 -04:00
ZacharyZhang-NY dbcedfae88 T48f Docs · Focus and keyboard passes review 2026-10-01 12:48:48 -04:00
ZacharyZhang-NY 73c79bc79a T48f Docs · Focus and keyboard: what init binds, the root scope, making a Tab stop, buttons, overlays; the focus example compiles 2026-10-01 12:38:02 -04:00
ZacharyZhang-NY 85e1fc923a T48e Docs · Theme passes review 2026-10-01 12:36:39 -04:00
ZacharyZhang-NY da39d0a914 T48e Docs · Theme: reading tokens, the calls that change them, an owner's palette, chart hues; the theme example compiles 2026-10-01 12:32:53 -04:00
ZacharyZhang-NY b41a9d3565 T48d Docs · Getting started passes review 2026-10-01 12:30:09 -04:00
ZacharyZhang-NY 1303a407fc T48d Docs · Getting started: four steps, a window that follows the theme, changing state from a press; the start example compiles 2026-10-01 12:26:17 -04:00
ZacharyZhang-NY f90182c638 T48c Docs · Installation passes review 2026-10-01 12:26:00 -04:00
ZacharyZhang-NY 17194c3afd T48c Docs · Installation: GPUI compiles the shaders at run time; the panic quoted without a trailing space 2026-10-01 12:22:19 -04:00
ZacharyZhang-NY 6da6ab00dc T48c Docs · Installation: requirements, the git dependencies and why, shaders, a check, failures and their fixes 2026-10-01 12:17:18 -04:00
ZacharyZhang-NY f326b6dab6 T48b Docs · Introduction passes review 2026-10-01 12:15:07 -04:00
ZacharyZhang-NY 23ccc36668 T48b Docs · Introduction: what Ely gives an app, what is stable, a first window, license 2026-10-01 12:11:29 -04:00
ZacharyZhang-NY 484bec30e1 T48a Docs · Frame passes review; an id-less heading fails the build, protocol-relative links stay native 2026-10-01 12:08:02 -04:00
ZacharyZhang-NY 0b300f1f94 T48a Docs · Frame: guides render at build in a Vite plugin, so a bad one fails the build; focus rings whole in the rail and list; plain heading words, unique ids, captions only when named, code under example= refused 2026-10-01 12:01:42 -04:00
ZacharyZhang-NY 128cc5992f T48a Docs · Frame: titles and the list read plain words, links with a section route, long code wraps, the rail stays in flow on narrow screens, check.sh holds every docs example to Cargo.toml 2026-10-01 11:52:29 -04:00
ZacharyZhang-NY a52241667f T48a Docs · Frame: a Docs tab, /docs/ and /docs/$page/, Markdown guides with a rail, an on-this-page list, previous and next and code blocks; full Rust samples are compiled examples shown as text 2026-10-01 11:40:50 -04:00
ZacharyZhang-NY 6253e4f2d2 CONTRIBUTING.md: setup, where things go, the house rules reviews catch, checks, license 2026-10-01 11:25:34 -04:00
ZacharyZhang-NY 700f402997 Pull request template: what and why, screenshots, the house checklist 2026-10-01 11:14:21 -04:00
ZacharyZhang-NY 9e5850fcb9 CI: checkout v7, setup-node v7 and pnpm/action-setup v6, on Node 24 2026-10-01 11:03:09 -04:00
ZacharyZhang-NY 47a4199a97 Issue forms: Bug Report, Component Request and Other, each titled by its kind; blank issues point to the live components 2026-10-01 10:58:09 -04:00
ZacharyZhang-NY 4f0b3726fe T47 Ship: CI runs the crate's checks on macOS and the site's tests and build 2026-10-01 10:38:19 -04:00
ZacharyZhang-NY 56f0e7b2f7 T47 Ship: the site lives at elygpui.com; its share image and the gallery's pictures load from there 2026-10-01 10:38:18 -04:00
ZacharyZhang-NY cb45f5232f Remove frontend-reference: the folder, its ignore line and its docs 2026-10-01 10:03:27 -04:00
ZacharyZhang-NY 567ab6f36f 更新 README.md 2026-10-01 08:51:27 -04:00
ZacharyZhang-NY fcfebb3db4 T47 Ship: a gallery whose wasm fetch fails retries, then tells the host; the host shows it at once and waits a minute for a slow start 2026-10-01 08:49:10 -04:00
ZacharyZhang-NY 3a0274eeab T47 Ship: wrangler leaves macOS folder files out 2026-10-01 08:44:46 -04:00
ZacharyZhang-NY 0fdd3d5e02 T47 Ship: the OG image lives at the site's own domain 2026-10-01 08:43:43 -04:00
ZacharyZhang-NY bc0fafd57f T46a Site · the hero title fits at 768 too 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 48f344091d T46a Site · Owner's notes pass review; the hero title fits its column 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 8441ee60cd T46a Site · Owner's notes: short story names, aligned icons, a whole city title, tighter story tops, hidden scroll bars, a tablet step at 1024 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 908834465d T46 E2E passes review; T45 closes 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY be56983e15 T46 E2E: typing proven by inked pixels, home frames proven mounted before their claims, full screen measured, window captures decoded 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY faa3dddbfe T46 E2E: the suite follows the new site (hero, features, city, search, rail, keys, captures, live frames, focus, skip, full screen), in Chrome, Firefox and a phone 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 37ea96721a T45e Site · Local passes review 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 52d94a79c9 T45e Site · Local: pnpm local builds the gallery for localhost, builds the site and serves both on 4173 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 557c8dd2cd T45d Site · Components passes review; Slash leaves a hidden field alone; the review log moves to tasks/reviews.md 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 5cc9a302fb T45d Site · Components: the rail reveals the current story with room, Shift arrows stay the page's 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 8444dd3c12 T45d Site · Components: comments one short line 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 97f9811356 T45d Site · Components: one search over chapters, stories and components (Slash, Enter, Escape), the index in hairline rows, the story beside a sticky chapter rail, arrows between stories 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 24687a09fb T45c Site · Home: full screen stops the glide at once, a starting gallery leaves the host's focus alone, a focused frame stays mounted 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY c3f9cd533a T45c Site · Home: full screen locks the page's overflow and Lenis halts on it, ending a glide in flight 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY e2b9670043 T45c Site · Home: a route change ends the glide, full screen holds against the wheel, each skip control focuses its own target 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 23ebaba2a1 T45c Site · Home: a full-screen hero, five live features beside their miniatures, counts on the beat, the chapters as a city, a closing call; smooth scroll, reveals from scroll, a fade between routes; the OG image from the new hero 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY f9c20b251c T45b Site · Iso passes review 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY dbce181588 T45b Site · Iso: diagonal cells in order, the pointer meets only grown cells, motion follows the system live, replay by key, the header fits 280px 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 3aa895c24d T45b Site · Iso: isometric line miniatures on a canvas, hidden lines removed, cells on a marimba beat, playable under the pointer, still under reduced motion 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 8a5ce82119 T45a Site · Stack passes review 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY 5a29fc780a T45a Site · Stack: comments one short line 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY b22e80ac47 T45a Site · Stack: reloads open in the current mode, the first paint keeps the system's, plain route changes 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY f67e5c0839 T45a Site · Stack: Vite 8, React 19 and TanStack Router under pnpm; routes, theme, fonts, the manifest 2026-10-01 08:38:05 -04:00
ZacharyZhang-NY e17e31a689 Update README.md 2026-09-30 00:29:08 -04:00
ZacharyZhang-NY b4ad854508 T45 Website: native captures stand in where a browser cannot run, an OG image, no web notes 2026-09-30 00:03:23 -04:00
ZacharyZhang-NY 5550552e76 T45 Website: an isometric miniature grows to a marimba; finance, charts and chat beside their models; no bars, no boxes 2026-09-29 23:40:04 -04:00
ZacharyZhang-NY 3153d892f4 T46 E2E: Playwright walks the built site, desktop and phone, live stories included 2026-09-29 23:40:04 -04:00
ZacharyZhang-NY 3ec0ae790c T45 Website: a home in motion and every story live, chapters on the left 2026-09-29 23:40:04 -04:00
ZacharyZhang-NY ab3f6eb98f T44d Web · Stories passes review 2026-09-29 23:39:50 -04:00
ZacharyZhang-NY acc39374fa T44d Web · Hidden builders start nothing, fields blink only in focus, grids paste with Control 2026-09-29 23:02:37 -04:00
ZacharyZhang-NY 752e7cba0c T44d Web · Hidden demos keep still, wide ones scroll whole, pastes read the web's clipboard 2026-09-29 22:13:27 -04:00
ZacharyZhang-NY d2018f8510 T44d Web · Stories stand alone: nested ones surface, hidden ones drop their demos, early themes land, pastes arrive 2026-09-29 19:16:41 -04:00
ZacharyZhang-NY 2a132d7d41 T44d Web · The gallery's start takes its defaults from one place 2026-09-29 19:16:41 -04:00
ZacharyZhang-NY 95db323797 T44d Web · What a browser cannot do says why; fonts resolve; the bundle builds
Demos that open a second window, take files, move the window, read the
clipboard or set native menus show a note on the web in the blocked style.
IBM Plex Sans Regular joins the assets, unmodified, where gpui's svg
renderer loads it: gpui_web answers .SystemUIFont with it. The gallery's
web entry registers a Hebrew cut of Noto Sans Hebrew. scripts/web.sh builds
the bundle with wasm-bindgen and wasm-opt -Oz, 20.0 MB, and fails past 25 MiB.
2026-09-29 19:16:41 -04:00
ZacharyZhang-NY 09dd3fc2cb T44d Web · Gallery builds for wasm32: one story per section by URL, themed by the host
The gallery's entry runs natively or, on wasm32, from a wasm_bindgen start
through gpui_web, single-threaded with WebGL2 fallback. ?page, ?story and
?theme choose what shows; an unknown one fails in the page and the log.
set_theme switches the mode later. Pictures load from ELY_GALLERY_ASSETS.
scripts/stories.py writes examples/gallery/stories.json from the pages.
2026-09-29 19:16:41 -04:00
ZacharyZhang-NY 29f1c8f467 web: the library checks for wasm32 (carried) 2026-09-29 19:16:41 -04:00
ZacharyZhang-NY 822a58810a T44c Port · Parity passes review 2026-09-29 19:14:18 -04:00
ZacharyZhang-NY 26629a8b74 Port · Parity: the last long comments are short 2026-09-29 18:43:49 -04:00
ZacharyZhang-NY 007ed30d4a Port · Parity: dark shots judged; the status shot waits out the tooltip; comments one short line 2026-09-29 18:18:05 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d7272e88bd Port · Parity: every page matches gpui 0.2.2's; the rules re-tested
Every page and scripted state was shot with gpui 0.2.2 and with the
port, light and dark, full width and at 280px, judged pair by pair by
agents with skeptics, and every page's log diffed. What differed is
fixed, each with a test that fails without it where the test platform
can show it:

- The gallery's harness takes posted mouse moves (gpui_macos listens
  to a tracking area), posts each drag move twice (it no longer
  replays a held drag), and waits three frames before each shot.
- Rolling digits: a block column, so taffy 0.13 lends the cell no
  baseline, and a line of whole device pixels, since gpui snaps each
  length on its own. DescriptionList's row aligns ends.
- Margins that taffy 0.13 collapses through a plain block: the
  drawer's pull and snap, the stagger and transition rise, the dialog
  and palette rises, the fullscreen dialog's slide.
- An Input keeps room to type in a squeezed row; the field rows drop
  w_full, whose reason was taffy 0.9's.

AGENTS.md's rules that named gpui 0.2.2 or taffy 0.9 were re-tested
against both sources and rewritten, and new ones say what the port
changed. TASKS.md's tags follow: what the new gpui makes possible is
prove. Captions, doc comments and README say what holds now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 10:39:55 -04:00
ZacharyZhang-NYandClaude Opus 5.5 41368430f5 Tasks: the site deploys to Cloudflare through wrangler
The Dokploy line was a misreading; the plan stays as it was.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:28:32 -04:00
ZacharyZhang-NYandClaude Opus 5.5 dc465d7244 T44b Port · Build passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:24:24 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f76c3c1280 Port · Calendar: a real exit clears the preview after a key
The calendar's grid clears its preview on leave, and ignores a leave
that follows a key. gpui records the exit a key makes before the
handler runs, so the guard swallowed it and a real exit afterwards
had no change left to report: the stale day stayed the preview. The
grid now listens independently of input, while its days keep the
default, so a key is no leave and a real exit is. A test leaves the
grid after an arrow, a letter and a month key.

shaped()'s doc goes back to its one line; AGENTS.md keeps the why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 20:54:25 -04:00
ZacharyZhang-NYandClaude Opus 5.5 94deeaed52 Port · Build: Zed's gpui at 1a28cff
gpui and gpui_platform now come from Zed's repo by git at 1a28cff,
the code gpui-pre 0.3.7 republishes, so the gallery can later build
for the browser. gpui takes stacker, as 0.2.2 always did, and
gpui_platform takes font-kit, without which macOS draws no text;
runtime_shaders moves to gpui_platform.

The API moved: focus, blur and Tab steps take cx, Corner is Anchor,
shaped lines paint with an alignment, defer_draw takes a content
mask, the inspector registers a factory, async updates return their
value, and wrapped lines are shared, not cloned. Updates through a
window context go through the window, so a closed window still ends
them as it did.

Where the new gpui behaves differently, Ely keeps its old intent.
Holds and reveals under a resting pointer survive typing through
gpui's input-modality-independent hover; previews the keyboard also
moves keep the default, and the calendar's leave ignores a key press.
Enter reaches a button inside the code editor, since gpui now arms a
key click on the press. The event logger skips a lone modifier. A
video and an image grid keep their shape in a flex column under
taffy 0.13, as a picture does.

AGENTS.md records the new stack and the rules this port changes; the
rest of the old gpui and taffy rules are re-tested in T44c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 20:30:17 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e49c93bb6f Tasks: live components on the site; Zed's gpui; Dokploy
The site shows every component running, as gpui-kit.com does, never
a screenshot. gpui 0.2.2 cannot build for the browser, so T44b moves
gpui to Zed's repo at 1a28cff, T44c re-checks every page and rule on
it, and T44d builds the gallery for wasm32 as one story per
component. The screenshot items give way, and the site deploys to
Dokploy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 20:13:44 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9ef59b01e4 T44a Capture · Roots passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 20:13:05 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e482bac865 Capture · Roots: the roots round 2 missed
A sweep that read every render found 28 roots still on w_full,
reached by 64 components: the field button under Select and every
picker, the checked and mention fields, the slider track,
NumberInput, Steps, the state views, the agent previews, and every
chart and finance frame. All drop it. LivePreview's size_full becomes
h_full, since its card no longer fills.

Every host of those roots was listed and read, 231 in all. Five
tests that mounted a picker as the window root sit in a size_full
box, the index manager gives its method select a flex_1 box, and the
notification center and the gallery's state frames stack their state
views in a column so they stretch.

Each dropped root carries a debug selector, and six chapter tests
mount 43 components in a padded card whose column is measured by
content, through layout::tests::narrow_width: each root must take the
full 240px. With w_full back on the roots, all six fail.

AGENTS.md names the roots that keep w_full and why: picture frames
that keep a ratio, and rows that hand a text field its share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 19:34:01 -04:00
ZacharyZhang-NYandClaude Opus 5.5 ca5c967f17 Capture · Roots: the twelve roots round 1 found
Eleven more roots drop w_full: the docked ActionBar, MessageBubble's
three arms, PromptInput, TitleBar, Tabs, the horizontal Divider with
and without a label, DropZone and Playlist. Their hosts in the crate
are blocks or flex columns. The gallery's docked ActionBar sits in an
items_center column, so the demo gives it the width.

AspectRatio keeps w_full, and AGENTS.md says why: taffy 0.9 follows a
ratio from a set width, not from a stretch, and without one the image
gallery's tiles and the ratio demo collapsed.

Every page and scripted state was shot again at full width and at
280px and compared with the shots from before T44a. Nothing collapsed
or shrank. The mail reader's attachments now wrap side by side, as
their row asks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:30:47 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5779daef82 Capture · Roots: roots drop w_full; hosts give the width
145 component roots drop w_full, the early returns included. Three
field rows keep it, Input, InputGroup and TagInput: stretched without
a width, taffy 0.9 laid the field's flex_1 share at no width in the
template picker at 280px, and the placeholder drew nothing. Container
keeps it, since mx_auto needs a width to fill.

Hosts that relied on a root's width now give it: MessageList boxes
each row at the list's width, the splash gives its bar the column's,
and test stages that mount a component as the window's root sit in a
size_full box. Tests hold a narrow row across the list and the splash
bar across its column; each goes red without its fix.

The gallery gains --narrow <px>, which lays each page in a padded
card at that width and shoots it without its script. The page
header wraps: the title takes the room and the theme switcher drops
below, where a long summary used to push it past the edge. A file
panel step waits for the panel to become key instead of a fixed
pause, which lost a race in a capture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:07:22 -04:00
ZacharyZhang-NYandClaude Opus 5.5 486f618ecc Tasks: split T44 into three
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:12:25 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d0fbfd1a4e T43c Tooling · Catalogs passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 09:10:55 -04:00
ZacharyZhang-NYandClaude Opus 5.5 ed1014bc34 Tooling · Catalogs: the audit reads active and code; avatar and container sizes
The audit now holds fg_subtle on active, a selected row's fill, and
each syntax color over the editor's surface, a code block and the
current line. What it found is fixed at the source: fg_subtle is
#696764 light and #94928f dark, syntax.comment follows it, and the
light number, constant and attribute darken to #a3591b, #aa5529 and
#9f5d12. The token viewer adds avatar and container sizes, draws a
container at its share of the widest so it stays in its row, and
names what it shows: the shared colors and scales. Tests pin every
variant, the hue each chart row reads and the code rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:57:52 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d2ac6c7753 Tooling · Catalogs: token viewer, contrast audit; fg_subtle meets AA
DesignTokenViewer shows every token of the theme in the mode shown,
named as code reads it: colors with their hex, syntax, chart and
terminal colors, the type scale, radii, control and icon sizes,
elevations and spacing. A11yChecker audits each text color over the
surfaces it sits on at 4.5:1 and the focus ring and chart hues at
3:1, failures first. The audit found fg_subtle under 4.5:1 on hover
and overlay; it is #6f6d6a light and #8c8a87 dark now, and a test
holds all four Ely palettes to the audit. IconBrowser points to
forms::IconPicker and the ScreenshotTest Harness to the gallery's
capture, each with a section on the tooling page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:23:39 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e5910068e5 T43b Tooling · Inspection passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:05:26 -04:00
ZacharyZhang-NYandClaude Opus 5.5 517d23f78f Tooling · Inspection: stalls count whole, the panel owns Tab, bound keys log
FpsMeter keeps the stamp before the last second, so a frame longer
than the span shows as the worst and a bar. The inspector's panel, a
second root out of the app's scope, sits in a FocusScope of its own,
so Tab goes on from Pick. EventLogger takes key downs through an
interceptor, which runs before bindings, and marks its box with a key
context to know focus is inside, so a field's Backspace and Enter log.
Tests hold the stall, Tab both ways through Pick, bound keys in a
field, a key while focus is outside, newest first and a bare border.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:54:54 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5f951ea581 Tooling · Inspection: inspector, FPS meter, render counter and event logger
install_inspector draws gpui's debug inspector in Ely's look: a panel
with the picked box's source, ids and box model, and its margin,
border, padding and size drawn over the page through raise, since the
panel scrolls. FpsMeter counts frames on the executor's clock and
colors each by the frames it missed. RenderCounter counts the renders
of the view that draws it. EventLogger lists the input that reaches
what it holds, its canvas laid first so a child's stop hides nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 07:13:12 -04:00
ZacharyZhang-NYandClaude Opus 5.5 a0d93eb35f T43a Tooling · Workbench passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:46:24 -04:00
ZacharyZhang-NY f3b328e8f3 Tooling · Workbench: the knobs seed the settings; knobs checked
The knobs join a layout::seeded seed, so a changed starting value,
option list or range starts every setting over, and a renamed knob
no longer fails. new() refuses no knobs, twin names and a number
outside its range. Controls are keyed by knob name. The test presses
keys without a refresh, so only a notify redraws, and walks the
switch twice, the slider twice and a pick the owner takes away.
2026-09-28 06:37:06 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6c21711328 Tooling · Workbench: a playground, and the gallery as the workbench
tooling::Playground lays a preview beside its knobs, each a switch, a
choice or a number that edits one property; the preview redraws from
the settings, which fail on a name or kind the knobs lack. The gallery
app is the ComponentGallery; its page 43 shows both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:08:40 -04:00
ZacharyZhang-NYandClaude Opus 5.5 989eaf29b8 Tasks: split T43 into three
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:03:22 -04:00
ZacharyZhang-NYandClaude Opus 5.5 264b552fd4 T42g Misc · Export and import passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 06:03:05 -04:00
ZacharyZhang-NYandClaude Opus 5.5 cddbd150e6 Misc · Export and import: reasons wrap; every way out tested
feedback::InlineMessage lays its words in a block that wraps, so the
importer's reason for unread text no longer runs past the dialog. The
reader skips a leading byte order mark, names the line an unclosed
quote opened on, and logs once per reading. Tests press each Cancel,
the scrim, Export with the first format, see focus return to the
opener, read new text under an open importer, and see a field with no
column come empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 05:50:28 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9912638fa7 Misc · Export and import: export, field mapping and a CSV importer
misc::ExportDialog offers the owner's formats as cards and hands the
chosen one over for the owner to write. misc::ImportDialog maps the
file's columns to the app's fields, matched by name to start, over a
preview, and asks for each required field's column. misc::CsvImporter
reads CSV as RFC 4180, lets the first row name the columns or not, and
maps it through the ImportDialog into records; text that does not read
says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 05:20:03 -04:00
ZacharyZhang-NYandClaude Opus 5.5 b7c8629ea7 T42f Misc · Web passes review
in_view names its types in place and the non-macOS branch reads its fields, so builds off macOS stay free of warnings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 05:07:35 -04:00
ZacharyZhang-NYandClaude Opus 5.5 1b04dc07a4 Misc · Web: the web view hands the keyboard back
WKWebView takes AppKit's first responder on a press and gpui's view
never asks for it back, so typed text went to the page for good. The
web view now hands it back through wry's focus_parent on every press
gpui gets and when its page drops. The gallery proves it with a native
key after a press on the page. in_view builds only where it is used,
its test covers a box that straddles the view's top, and the embed's
frame takes the card's bottom corners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 04:59:11 -04:00
ZacharyZhang-NYandClaude Opus 5.5 529394e57b Misc · Web: a web view proven with wry, and an iframe embed
misc::WebView lays wry 0.57's child WKWebView over the window at its
box, following it from a canvas in prepaint and hiding it out of view;
it loads an address or the owner's HTML, which makes it the HTML
preview. misc::IframeEmbed frames a page under its address with Open
in browser. EmbedCard points to chat::LinkPreviewCard, ShareSheet to
buttons::ShareButton; PrintButton is blocked, gpui 0.2.2 has no print
API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 04:32:26 -04:00
ZacharyZhang-NYandClaude Opus 5.5 45e128e069 T42e Misc · Terms and notices passes review
A test walks Shift-Tab round the cookie banner past the resting Necessary switch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 04:25:31 -04:00
ZacharyZhang-NYandClaude Opus 5.5 067ef3f122 Misc · Terms and notices: tests for every way out; switch labels wrap
Tests now press Decline and the scrim, see focus return to the opener
after Accept, see necessary cookies stay on and off the Tab path, and
see the license list mark its row and read each version and license.
lists::ListItem marks its description and selected state for tests.
forms::Switch's label wraps in its own block, as Checkbox's does.
Ely's own row in the gallery shows both of its license texts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 04:09:49 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6c589c3115 Misc · Terms and notices: licenses, consent and a cookie banner
misc::LicenseViewer lists what a product ships with each license beside
the chosen one's text. misc::ConsentDialog asks for a box before Accept
and answers each way out once, accept or decline. misc::CookieBanner
takes Accept all, Reject all, or a switch per kind through Choose, which
turns into Save choices on its focus. ChangeLog Viewer points to
data_display::Changelog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 03:36:03 -04:00
ZacharyZhang-NYandClaude Opus 5.5 4d21b15219 T42d Misc · Asking and learning passes review
The poll's mark and its row's selector share one binding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 03:24:15 -04:00
ZacharyZhang-NYandClaude Opus 5.5 8f85e4fe04 Misc · Asking and learning: Submit never rests; radio labels wrap
Survey's Submit asks for any missing answer instead of resting, so no
untick takes its focus handle out of the tree. Flashcards turn and go
round, and a quiz pick clears its ask, through methods the tests reach;
the poll's rows show which one holds the vote. forms::Radio's label
wraps in its own block, as Checkbox's does, so a long one stays in a
narrow box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 03:11:52 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6e02a0cce3 Misc · Asking and learning: a poll, a survey, a quiz and flashcards
misc::Poll turns to results once the owner holds the vote, with Change
vote on Vote's stop. misc::Survey asks on one page and sends when each
question is answered, thanking in Submit's place and focus. misc::Quiz
checks, goes on, scores and starts again on one button. misc::Flashcards
turn on a press and go round the deck, each face fading in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 02:38:33 -04:00
ZacharyZhang-NYandClaude Opus 5.5 19a5295ed8 T42c Misc · Codes and checks passes review
Tests now hold focus in the field after an answer and a passed captcha with no Tab stop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 02:22:34 -04:00
ZacharyZhang-NYandClaude Opus 5.5 fa72f2521d Misc · Codes and checks: the captcha keeps focus through every verdict
Verify never rests, so no verdict or new ask takes its focus handle out
of the tree; with nothing typed it sends focus to the field. Enter hands
nothing over while the owner checks. A pass moves focus from the resting
field to the Verified mark, so the field shows no ring and no caret.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 02:05:44 -04:00
ZacharyZhang-NYandClaude Opus 5.5 8648f5eed1 Tasks: the website's home page, components page and Cloudflare deploy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 01:40:15 -04:00
ZacharyZhang-NYandClaude Opus 5.5 621c8406bb Misc · Codes and checks: a QR code scanner and a captcha
misc::QrCodeScanner reads QR codes with rqrr 0.11 from the frames the
host hands in, one at a time off the main thread, and hands each code
to the owner once. misc::Captcha shows the owner's picture over an
answer field that empties and keeps focus with each answer; the owner
says how it stood. BarcodeDisplay, Signature and Rating Widget point
to their homes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 01:40:15 -04:00
ZacharyZhang-NYandClaude Opus 5.5 854f44ed6c T42b Misc · Numbers passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 01:14:17 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d0bf6ee5e3 Misc · Numbers: a result goes on whole; long readings wrap
A calculator result stays in the sum as `a`, handed to evaluate with
its whole value, so 1 ÷ 3 = × 3 = gives 1; sign turns it and delete
takes it. Equals waits on a dangling operator. The converter's reading
wraps in its box rather than ending in an ellipsis, and tests hold
Control, the = key, a point after a result and the six digits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 01:06:32 -04:00
ZacharyZhang-NYandClaude Opus 5.5 fc10b64303 Misc · Numbers: a calculator and a unit converter
misc::Calculator works a typed sum in forms::evaluate's precedence,
from a pad or the keyboard; Enter is equals while the calculator
itself has focus, and Escape clears or passes on. misc::UnitConverter
reads an amount in another unit across seven kinds, with a swap.
Both show numbers through format::significant, which rounds through
Rust's exponent form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:41:49 -04:00
ZacharyZhang-NYandClaude Opus 5.5 87b0effafb T42a Misc · Time passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:30:00 -04:00
ZacharyZhang-NYandClaude Opus 5.5 977ba4c335 Misc · Time: Lap turns Reset, laps and tickers tested
The stopwatch's second button reads Lap while running and Reset once
paused, on one focus handle, and Reset hands focus to Start, so no
press leaves focus stranded. Lap rows carry selectors; tests hold
their order and times, count the tickers' wakes, and a WorldClock
without places fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:21:23 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6918370b08 Misc · Time: clocks and a stopwatch
misc::Clock shows the time in a zone, the system's unless told, as an
analog face with hour marks and three hands over its digits and a
label; it wakes its view at each whole second of the wall clock, and
only while the owner passes no moment. misc::WorldClock lays places in
rows: a small face, the place, how its day and hour stand against home,
and its time. misc::Stopwatch runs on the executor's clock in tenths,
holds while paused, marks laps while running and clears once paused;
Start and Pause share one button and its focus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:51:58 -04:00
ZacharyZhang-NYandClaude Opus 5.5 7cce4ab3fc Tasks: split T42 into seven
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:44:24 -04:00
ZacharyZhang-NYandClaude Opus 5.5 52a98bf676 T41c Maps · World passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:42:16 -04:00
ZacharyZhang-NYandClaude Opus 5.5 8a3f8d152e Maps · World: marks in their wash, the legend clear of the credit
A choropleth's points and lines now draw in their wash, as its doc
says, while its areas keep the page's color at their edges. The legend
moves to the top left, so the attribution a license asks for stays clear
at any width. A key given twice fails. WorldMap's keying by ISO code,
its Natural Earth credit and its acceptance of the bundle's codes, as
Kosovo's XK, each have a test; so does an endless value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:35:04 -04:00
ZacharyZhang-NYandClaude Opus 5.5 752266c36d Maps · World: choropleths and the world's countries
ChoroplethLayer washes each area by its value, keyed by its id or a
property's text or number, on one hue's scale from its faintest at the
least value to its strongest at the most; an area without a value reads
as no data. ChoroplethMap lays it on the owner's map, with or without
tiles, and puts a legend of the scale and of no data in the corner; a
value that matches no area is logged once. WorldMap washes Natural
Earth's 110m countries, bundled trimmed by scripts/countries.py, by ISO
3166 alpha-2 code, and fails on a key that is no code. A shape now
paints as one fill and one edge path per world in view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:12:27 -04:00
ZacharyZhang-NYandClaude Opus 5.5 811a187a3e T41b Maps · Layers passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:01:43 -04:00
ZacharyZhang-NYandClaude Opus 5.5 11fbb07ff8 Maps · Layers: tests hold what clusters promise
Tests hold that a pan keeps a focused cluster, that a cluster away from
the view takes no Tab, that a zoom out folding a focused pin hands its
focus to the map, and that a press shows members far apart whole. The
heat test pins the quartic kernel's value halfway out; a ring of fewer
than four positions fails with its own reason, and has a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:57:05 -04:00
ZacharyZhang-NYandClaude Opus 5.5 a3115b8126 Maps · Layers: routes, GeoJSON, heat and clusters
MapView takes layers over its tiles and under its pins. RouteLine runs
along places the short way across the date line. read_geojson reads RFC
7946 to features, strictly: a position past the globe, a line of one
position or a ring that does not close fails with its reason, and a
feature placed nowhere keeps no geometry. GeoJsonLayer washes areas as
one path per polygon, so holes stay open, outlines them, strokes lines
and dots points. GeoHeatmap spreads weighted places in a quartic kernel
over small cells, scaled to the densest spot in view. Lines and areas
unwrap across the date line and repeat on every world in view.
cluster_markers gathers pins by squares of the world, so a pan keeps
each group; a cluster shows its count, and its press shows its members
whole, never farther out. A zoom that may regroup them hands a focused
pin's or cluster's focus to the map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:35:40 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f5909f6e43 T41a Maps · View passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:20:08 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e84fbb9a61 Maps · View: a focused pin keeps focus off view
Each pin holds a keyed focus handle of its own, a Tab stop and pressable
while its place is in view, so a focused pin that pans out of view keeps
focus and the arrows go on panning, and a popup that scrolls away hands
focus back to its pin wherever it lies. A popup whose place is off view
takes no focus. Tests hold the tall card's cap and scrolling body, the
map staying under its wheel, and markers refuse a repeated key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:14:42 -04:00
ZacharyZhang-NYandClaude Opus 5.5 bbba5ee04b Maps · View: pins in view take presses, the popup hands focus back
A pin whose place lies outside the view takes no press or Tab, as its
popup would not show. The popup takes focus when it shows and, the
frame it stops showing by its button, Escape, the owner or a pan, hands
focus back to what held it, so focus stays in the map. Its body scrolls
past the room on its side and it paints over the zoom buttons. Tiles
load through gpui's logging loader, so a tile that fails says so. A
drag's owner check has a test, and the per-move viewport logs at debug.
The maps tests split into the map's and the popup's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:57:27 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d6a56f3964 Maps · View: host tiles, pins and a popup, a picker, coordinates
maps::MapView lays the host's tile for each zoom, column and row in Web
Mercator, the nearest cut scaled past the zooms it serves, on whole
pixels. gpui packs pictures in its atlas without padding and samples
them linearly, so each tile draws with a one-pixel rim copied from its
own edge, one of its pixels larger each side and clipped, and no seam
shows. A drag, the wheel or the arrows pan; Command-wheel, + and - or
its buttons zoom about the pointer or the center; the owner keeps the
viewport. MapMarker pins take a press and Enter when the map hears
them; MapPopup opens toward the map's larger half and stays inside it,
closing by its button or Escape. LocationPicker picks the place under a
center crosshair; CoordinateDisplay writes degrees or degrees, minutes
and seconds with tabular figures and a copy button. The gallery's tiles
are Natural Earth's countries, cut z0 to z3 by scripts/tiles.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:22:50 -04:00
ZacharyZhang-NYandClaude Opus 5.5 8da3edbeda Tasks: split T41 into view, layers and world
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:57:30 -04:00
ZacharyZhang-NYandClaude Opus 5.5 7e32d2c294 T40b a11y passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:52:32 -04:00
ZacharyZhang-NYandClaude Opus 5.5 bb59ae845f Gallery: a blocked note wraps beside its dot
blocked() laid its text as a flex item beside the dot, and gpui measures
a text item's least width unwrapped, so a long note ran past its column
and the dot shrank to nothing. The text now sits in a flex_1 block with
no least width beside a dot that never shrinks, which rests on the first
line, as Alert holds its icon. The a11y note and the motion page's
Lottie note wrap inside their columns. A failed hue check names both
charts in hex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:42:19 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6265afbb01 a11y: a skip link, color-blind safe charts
i18n::SkipLink holds a Tab stop in flow at zero size, first where it
sits, and while focused raises a pill over the page; Enter, Space or a
press hands focus to its target. gpui orders stops by tab_index before
paint and registers none for an invisible element, so the stop stays in
flow and only the pill is raised. Theme::set_color_blind_safe swaps Ely's
chart hues for a set fitted to stay apart under Machado's protanopia,
deuteranopia and tritanopia; a test holds every pair 0.10 apart in Oklab
and each hue 3:1 on its page, and red against green shows the measure
bites. KeyboardNavigationHelper points to interaction::RovingFocus. The
announcer, labels and screen-reader text stay blocked: gpui 0.2.2 has no
accessibility tree, and nothing here is trusted to observe VoiceOver.
The i18n tests move under src/i18n/tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:00:26 -04:00
ZacharyZhang-NYandClaude Opus 5.5 ff8f4ba84f T40a i18n passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 19:40:34 -04:00
ZacharyZhang-NYandClaude Opus 5.5 928da801fb i18n: a right-to-left message leads with its mark
gpui sets no paragraph direction, so CoreText took a Hebrew line whose
first strong character was Latin, or the left-to-right mark of a signed
number, as left to right and swapped its parts. I18n::text now leads a
right-to-left message with U+200F; the number keeps CLDR's U+200E, so
its sign stays at the digits' left. The demo adds a change that opens
with a signed number, in every catalog; under Hebrew it lies at the
line's right end. A test holds the marks on a message that starts with
a number and one that starts with a Latin name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 19:36:12 -04:00
ZacharyZhang-NYandClaude Opus 5.5 219e4744c0 i18n: Hebrew on one line, its sign held left, CLDR's short years
gpui 0.2.2 wraps shaped glyphs in the order they are drawn, so a
right-to-left line that wraps comes out garbled. The demo's Hebrew
lines are Ellipses, which cut by characters, and an Ellipsis now sits
by its text's alignment, so a short line under text_right lies at the
right; the editor's completion details take the side they ask for. A
right-to-left locale leads a minus with a left-to-right mark, as CLDR
does, so the sign stays at the digits' left inside Hebrew. en-US and
de-DE write CLDR's two-digit year. The repeated-key and second-catalog
guards have tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 19:20:13 -04:00
ZacharyZhang-NYandClaude Opus 5.5 2a91218fd8 i18n: a locale per app, catalogs of messages, mirrored stacks
i18n::I18n is a gpui global holding the shown locale and one catalog of
messages per locale. text fills each {name} from its arguments; a
missing key, argument or catalog fails, and every catalog holds the
first one's keys. Locale writes numbers with its separators and dates
in CLDR's short numeric form; Hebrew replaces Arabic, whose Egyptian
digits Ely does not write. Direction::row mirrors a stack and
Direction::align sets text against the reading side. The gallery's
chapter 40 shows the messages in seven locales, every locale's number
and date, and a file list that mirrors under Hebrew.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:59:45 -04:00
ZacharyZhang-NYandClaude Opus 5.5 8c3ff2c584 Tasks: split T40 into i18n and a11y
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:26:52 -04:00
ZacharyZhang-NYandClaude Opus 5.5 215b1d49ae T39c Theme · Platform passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:25:40 -04:00
ZacharyZhang-NYandClaude Opus 5.5 ba865710a7 Theme · Platform: a title bar in three parts, buttons at the window's rem
A title bar lays out a start, a title and an end. In the centered styles
the start and end grow alike from nothing, their padding inside them, so
a short title sits at the bar's center and a long one gives way before
whatever stands at either end; on Windows the title, or a spacer when
there is none, takes the free width before the buttons. Caption buttons
take the bar's height at the window's own rem. Tests keep an untitled
Windows bar's buttons at its end, a long Linux title before the round
buttons, a short title centered, and the buttons inside a bar at rem 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:20:20 -04:00
ZacharyZhang-NYandClaude Opus 5.5 815aed304d Theme · Platform: caption buttons of their own height, titles that give way
Windows caption buttons take the bar's height less its hairline, a new
token, instead of their row's, so they keep their height beside caps
that wrap below them. A title bar's title is an Ellipsis: on Windows it
takes the free width before the buttons and gives way; a centered title
sits inset past the lights on both sides. The controls never shrink.
Tests hold a long Windows title's close button inside a 280px bar and
wrapped caps inside their row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:04:05 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f3c8cca4c9 Theme · Platform: one platform for buttons and caps, icon themes, frosted panes
theme::Platform moves out of typography and shell's ControlsStyle folds
into it; the theme's platform starts as the system's. Window buttons Ely
draws, the title bar's layout and every key cap follow it, secondary
included, through typography::keys::keystroke; a title bar whose buttons
the OS draws follows the OS. primitives::IconTheme lays an owner's rules
by file name and extension over Ely's map, for every file icon.
shell::Vibrancy fills a pane with the palette's new glass, translucent
so a window opened Blurred shows its backdrop, solid under high contrast.
gpui 0.2.2 has no Mica; that line stays blocked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 17:41:15 -04:00
ZacharyZhang-NYandClaude Opus 5.5 53578e0525 T39b Theme · Importing passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 17:34:16 -04:00
ZacharyZhang-NYandClaude Opus 5.5 106faca970 Theme · Importing: a long name gives way, four-digit colors read
A read theme's name is an Ellipsis that gives way beside its mode badge,
so a long name keeps the badge inside a 280px box. parse_hex reads #rgba
as VS Code does, each digit doubled, so a theme with a four-digit color
reads. Tests hold the badge inside the box and read #0008 as black at
0x88 alpha.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 17:32:21 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d5cfaeb19c Theme · Importing: VS Code themes read into Ely's palette, code palettes to choose
settings::read_vscode_theme reads a VS Code color theme, comments and
trailing commas and all: its type picks the mode, its colors and token
scopes name what they give, and the rest keep Ely's palette of that mode.
settings::ThemeImporter takes one dropped or browsed for, shows its name,
mode and count, and hands it over on Apply; a file that does not read
says why. theme::syntax_themes offers Ely's code colors, Quiet and Paper,
chosen through settings::SyntaxThemePicker, radio cards with a line of
code in each palette. A card's words now shrink as a plain block, so a
preview wraps inside the card at 280px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 17:11:55 -04:00
ZacharyZhang-NYandClaude Opus 5.5 38ee858b27 T39a Theme · Editing passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 17:02:23 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d49e02ceee Theme · Editing: segments sized by their labels, a thumb that covers its segment
A segmented control's segments now take their labels' widths first and
share the spare room, so a label ends in an ellipsis only when the whole
strip lacks room. The thumb's insets meet the strip's padding again, and
each segment is measured from its borderless wrapper, so the thumb covers
the chosen segment exactly. Tests hold Comfortable whole at 280px and the
thumb to the chosen segment's bounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:58:23 -04:00
ZacharyZhang-NYandClaude Opus 5.5 1860764d95 Theme · Editing: segments that give way, every color named
SegmentedControl's segments now give way when narrow, their labels
ending in an ellipsis, down to twice a control's height each; past
that the strip scrolls as before. The pill's border and padding moved
onto the scrolling strip, and the row inside it takes that floor as its
least width, so the strip scrolls exactly to its last segment. The
palette macro now writes each color's name from its field: Palette and
Syntax gain NAMES and token_mut for every color, the quiet fills too.
Tests press a density segment and each slider, and fit the density
strip in 280px with the text a step and a half larger.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:48:07 -04:00
ZacharyZhang-NYandClaude Opus 5.5 3d2bc0f4d6 Theme · Editing: a theme laid open, and an owner's palette per mode
settings::ThemeEditor lays a theme open: density, corners, text size,
high contrast and reduced motion, then twelve colors of the mode shown,
each a swatch that opens a picker beside its hex; each change hands the
owner a whole draft. Theme::set_palette takes an owner's palette per
mode, the colors fading to it when the mode shows; Theme::palette names
the palette the shown mode fades to; Palette::token_mut and
Syntax::token_mut reach a color by its field's name. The gallery's
theme page applies the editor to the whole gallery live, with a way
back to Ely's theme.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:31:33 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5f2e6a5cda T38b passes review in round 1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:18:23 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9e3e6c109d Interaction · Keys, scroll and focus: roving focus, scroll sync, type-ahead
RovingFocus keeps a row or column of items behind one Tab stop: the
arrows walk focus, Home and End reach the ends, Enter or Space presses,
and a press moves the stop without taking focus. ScrollSync keeps boxes
at one place along an axis: the pane a wheel turned leads, and a canvas
ahead of the panes copies its place before they draw. SelectableList
moves its cursor to the row that starts with the letters typed, the
letters joining within a pause and a Command letter left to the app.
forms::History becomes public as the undo manager. The gallery shows
gpui's own key bindings, a two-stroke chord waiting on its first
stroke, uniform_list, autofocus and the focus ring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:10:32 -04:00
ZacharyZhang-NYandClaude Opus 5.5 ec4c15b539 T38a passes review in round 2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:01:08 -04:00
ZacharyZhang-NYandClaude Opus 5.5 c3e8b114ce Interaction · Pointer: handles inside the box, drags from the press
Resizable's grip sits inside the box's lower right corner and its edge
handles lie inside the edges, their lines on the edge; the edges use
layout::resize_edge, a resize_handle whose line shows only while
hovered, since the box draws its own border. A drag now measures from
the handle's press, kept in keyed state, so the edge follows the
pointer from the first move. Tests drag the right edge downward and
the bottom edge sideways, turn the dial without Shift, and draw a band
with Shift over a selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:58:19 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f513df6aaa Interaction · Pointer: resize, turn, band selection, and gpui's own drag
Resizable sizes a box from its right and bottom edges and a grip at
its corner, within its least and greatest size; the grip is a Tab stop
whose arrows resize a step. Rotatable turns an svg to its angle inside
a ring whose knob a drag moves, Shift snapping to 15 degrees, with the
arrows turning it a notch. SelectionArea selects items with a band
drawn on empty space, a press, Command or Shift to add, and a cursor
the arrows walk with Space to add or drop; Escape clears and
Command-A takes all. The gallery shows gpui's own drag and drop, drag
preview, double press and wheel zoom, with pointers for Sortable,
LongPress and PanZoom; pinch stays blocked, gpui 0.2.2 forwards no
magnify event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:42:03 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9bde90a492 T37b passes review in round 2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:32:18 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e26b2d1407 Onboarding · Help: articles in a list the keyboard walks, a long address cut
HelpPanel lists its articles in lists::SelectableList: the list is one
stop whose cursor scrolls into view, and Enter or a double press opens
an article, focus going to All articles as before. SelectableList now
activates on Enter's release, armed by its own press, as the palette
and menus pick: an activation that moves focus left the release to
press what took it, here the way back. ContactSupport cuts a long
address in the middle and puts a Copy beside it. The lists tests move
into a folder, the release tests on their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:28:49 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5f5e2f73e1 Onboarding · Help: help panel, cheatsheet, what's new, feedback, support, inline help
HelpPanel searches articles and opens one, its body in Markdown, with a
way back; focus follows to All articles and back to the search, a
link to help:<key> opens another, and Contact support and Keyboard
shortcuts wait at its foot. KeyboardShortcutCheatsheet puts
settings::KeyboardShortcutsList in a dialog with its search focused;
WhatsNewDialog puts data_display::Changelog in one with Got it.
FeedbackWidget asks how it is going with four faces and a note, and
thanks. ContactSupport sends a topic and a message, spins while the
owner sends, and empties the message with focus back in it. InlineHelp
is a quiet line with Learn more. HelpTooltip moves to primitives,
where FormLabel's help mark shows it; ToggleGroup keys its toggles by
its own id. EmptyStateWithAction points to feedback::EmptyState.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:05:06 -04:00
ZacharyZhang-NYandClaude Opus 5.5 a066179699 T37a passes review in round 2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:56:30 -04:00
ZacharyZhang-NYandClaude Opus 5.5 973f10a6dc Onboarding · Flows and highlights: focus that holds as the flow turns, a tip that closes
navigation::Wizard keeps one focus handle for Next and Finish, so a
second press on the step before last finishes; Back that reaches the
first step, and a finished step picked by key, hand focus to Next.
Popover::with_opener hands its content a close that gives focus back:
Got it closes the hotspot's tip before the owner hides it, and the
event details close before Edit and Delete run, so focus never lands
on a panel that left the tree. The wizard's count and the setup
checklist's tally name their selectors after their words, and the bar
takes the count's own value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:48:20 -04:00
ZacharyZhang-NYandClaude Opus 5.5 996daed6e4 Onboarding · Flows and highlights: wizard, hotspot, highlight, setup checklist
OnboardingWizard runs a first run on navigation::Wizard without its
numbered steps: "Step 2 of 4" over a bar in as many parts, the step's
title, line and content, Back and Next once ready, Finish last, and
Skip for now. Hotspot pulses on a dot that opens a tip on a press or
Enter, with Got it. FeatureHighlight names something new under a New
badge, with Try it and Not now, and rises in. SetupChecklist counts
first things to do in words and a bar, offers Start on each left, eases
a check in as one gets done, and offers Hide once all are.

navigation::Wizard now keys its buttons, steps and motion by its own id
and takes on_step and on_finish at render, failing loud without them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:20:45 -04:00
ZacharyZhang-NYandClaude Opus 5.5 ebf8955556 T36c passes review in round 2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:14:12 -04:00
ZacharyZhang-NYandClaude Opus 5.5 2027842769 Account · Billing and team: a meter's name gives way, a month past 12 says so
A Meter's name takes the free width and ends in an ellipsis while its
count stays whole, so a long quota name keeps "9 of 12 seats" inside a
280px box. `expiry` says why it refuses a date, and Expires reads
"There is no month 13." apart from "This date has passed.". Tests now
cover an American Express card's fifteen digits and four-digit code,
the address emptying once an invitation goes, a quota past its limit,
and a prompt with benefits standing as a paywall and one without as a
line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:05:34 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6f3d8036da Account · Billing and team: plans, quotas, cards, bills, prompts, members, invitations
SubscriptionCard shows a plan with its state, price and what comes
next, a way to change it, and Cancel after a second press or Keep plan
once canceled. UsageQuota gives each limit a meter and a line of how
much, or how far past. PaymentMethodForm tells a card's network from
its number, checks it with Luhn, and saves once the number checks out,
the month is ahead, the code is whole and a name is given; it rests
while the owner checks. BillingHistory lists bills with their state and
a download; UpgradePrompt stands as one line or as a paywall.
TeamMemberTable changes a member's role through RoleSelector and
removes after a second press, the owner staying; InvitationList
invites by a reading address with a role and resends or revokes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:49:17 -04:00
ZacharyZhang-NYandClaude Opus 5.5 b3ce50ecab TASKS: T36b passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:41:13 -04:00
ZacharyZhang-NYandClaude Opus 5.5 91d939e21e Account · Profile and access: a key's ends stay whole, keys drawn literally
A key's name gives way before its ends, which keep their width. The
ends and a new secret draw through typography::literal. Tests show this
device listing first and the name field emptying after Create.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:38:40 -04:00
ZacharyZhang-NYandClaude Opus 5.5 24e0366e0e Account · Profile and access: profiles, switchers, a user menu, sessions, keys
ProfileCard shows someone at a glance; ProfileEditor edits a profile
seeded by its owner and saves once it changed, has a name and an email
that reads, with a way to put it back. AccountSwitcher and
WorkspaceSwitcher show the one in use in a row that opens a menu of the
others and a way to add one; UserMenu opens from an avatar with who is
signed in, the owner's rows and Sign out, through the new
menus::menu_under. SessionList puts this device first, signs others
out one by one or all after a second press. ApiKeyManager lists keys by
their ends, makes one by name and shows its secret once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:29:35 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5f26c8ee1e TASKS: T36a passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:23:11 -04:00
ZacharyZhang-NYandClaude Opus 5.5 fa167cab38 Account · Sign in: tests for each rest and wait
New tests: Sign in waits for a password and rests while busy; Create
account waits for a name; while one provider is on its way the other
takes no press; Send again rests while a link is on its way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:20:37 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e36458e44e Account · Sign in: log in, sign up, providers, a link by email, a second factor
LoginForm takes an email and a password, keeps one signed in on a box,
and signs in on Enter once the address reads and a password is typed;
a failed try says why above Sign in. SignupForm ticks a new password's
rules as they hold and waits for all of them and the terms.
OAuthButtons offer each provider, one spinning while the rest rest.
MagicLinkForm and ForgotPassword send a link to an address and, once
sent, say where it went and offer it again. TwoFactorInput takes a
six-digit code at once and, after one fails, clears the boxes through
forms::PinInput's new attempt, keeping its focus. The Account chapter
splits into sign-in, profile and access, and billing and team.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:12:49 -04:00
ZacharyZhang-NYandClaude Opus 5.5 89a177d021 TASKS: T35 passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:06:34 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6e5a742148 Settings: an accent palette folds inside a narrow box
The accent picker's palette takes what the custom well leaves and folds
its swatches; a test puts twelve presets in a 280px box and asserts
they fold under each other inside it. The settings tests split their
appearance cases into a file of their own. AGENTS says editing takes
the owner's handler itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:03:55 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9a8dbcb92e Settings: controls give way, notices fold, and every edit needs its owner
A settings row's control slot shrinks to the box, so a control's text
wraps and a palette's row folds; the font size's sample wraps in a plain
block. A notice's boxes carry their channel's name and fold under the
kind's name when narrow. canvas::panel::editing takes the owner's
handler itself: the design panels, the structure editor, the index
manager and the privacy, notice and startup settings take theirs at
render and fail loud without one. Apply fails loud if pressed while the
proxy does not read. Tests cover Apply resting on a bad port, the way
back resting at the default size and a box pressed off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:53:08 -04:00
ZacharyZhang-NYandClaude Opus 5.5 93de2a6bd6 Settings: a settings window, its sections, a search, and what fills them
SettingsLayout lists sections down the side and shows the open one, the
list going above the page in a narrow box; SettingsSection and
SettingsRow title and hold settings, a row's control wrapping under its
name. SettingsSearch finds a setting by its name or words and opens its
section. ThemeSelector, AccentColorPicker, FontSizeControl and
DensitySelector set the look; KeyboardShortcutsList finds shortcuts by
every word; ProxySettings, PrivacySettings, NotificationSettings,
StartupSettings, StorageSettings with its clear, ImportExportSettings,
ResetToDefault, FeatureFlags and DeveloperModeToggle fill the rest.
Every handler a control needs is required and fails loud when missing.
ColorPalette wraps its swatches in a row capped at its columns, so it
keeps their size in a box sized by its content, and its swatches carry
the palette's id. ThemeSwitcher points here: src/theme sits under the
chapters that draw with buttons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:22:58 -04:00
ZacharyZhang-NYandClaude Opus 5.5 4ab478c3c3 TASKS: T34 passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:10:59 -04:00
ZacharyZhang-NYandClaude Opus 5.5 206d89abde Dashboard: a focused tile's Left and Right, and a control's own arrows, tested
The arrows test also moves a focused tile right and back; a new test
focuses a button inside a card and shows its arrows leave the tiles.
A card given for a key with no tile fails loud, in both layouts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 12:08:44 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d1440ef49a Dashboard: a grid of widgets, what fires, what is up, and the stream
DashboardGrid lays widgets on columns: a tile's strip drags it and its
corner resizes it a cell at a time, others move down and rise to fill
gaps, a focused tile steps with the arrows, and under the theme's
stack_below width the tiles stand in one column. DashboardCard frames a
widget. AlertList acknowledges a firing alert from its row and opens
one on Enter; IncidentCard shows an incident's gravity, phase and
updates. UptimeBar, ServiceStatus and HealthCheck show what is up, a
check's detail wrapping under its name when narrow. EventStream keeps
kinds and holds the list while new events count up.
RefreshIntervalSelector and DashboardFilterBar set the window, the
filters and the refresh. StatCard, MetricsChart, ResourceGauge,
LogStream, TraceWaterfall, SystemMonitor, ProcessTable and NetworkGraph
point to their homes; the maps wait for T41.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 11:51:19 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e59239a7bb TASKS: T33d passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 11:38:54 -04:00
ZacharyZhang-NYandClaude Opus 5.5 57a3265a57 DB & Dev Tools · Tools and machines: washes paint, strict hex, tested levels
Text fields paint each run's wash before selections and glyphs, so a
Highlight's background shows: the regex tester's matches, a mention's
link and markdown's code. A tile's value gives way and wraps in a narrow
box. Hex and percent escapes take two hex digits and no sign. Tests
cover each WCAG level at its edge, a clean exit's quiet badge, a
stopped container's line without load, and a reading past its limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 11:15:50 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e3f2d497f1 DB & Dev Tools · Tools and machines: regex, encoders, contrast, containers, pods
RegexTester tries a pattern with its flags against a sample, washes
each match and lists it with its groups, zero-width ones too; a pattern
that does not compile says why. Encoder turns text into Base64, URL or
hex and back, says why a decode fails (Base64 pads end the text, twice
at most) and copies the result, drawn through typography::literal so no
ligature hides a character. ColorContrastChecker measures text on a
background, translucent text as it blends, and names the WCAG levels
met. ContainerList starts, stops, restarts and opens containers, each
button shown when the owner handles it. PodList sorts pods by any
column, ages by length through tables' new Cell::Duration, and narrows
them by namespace. ResourceMonitor shows each reading's latest value
beside its sparkline and, with a limit, how full it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 11:02:35 -04:00
ZacharyZhang-NYandClaude Opus 5.5 0cf33eea33 TASKS: T33c passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 10:53:02 -04:00
ZacharyZhang-NYandClaude Opus 5.5 a8bc4b169c DB & Dev Tools · APIs: a drop inside a folder lands last, tested
The collection's unit test now drops a request into a folder that holds
two, and asserts it lands after them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 10:51:19 -04:00
ZacharyZhang-NYandClaude Opus 5.5 2a87e6cd30 DB & Dev Tools · APIs: a log held at its newest, secrets per environment, moves
WebSocketConsole draws its log through chat::MessageList, so it holds
at the newest message. EnvironmentSelector opens a secret for its own
environment only. CollectionTree takes on_move and devtools::moved
applies a drop; the gallery opens a saved request into the builder and
moves them. Tests cover the builder's refill, Send at rest, Bearer
sign-in, the indented body, the eye, a field's pick path and the log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 10:44:21 -04:00
ZacharyZhang-NYandClaude Opus 5.5 4550c87bf4 DB & Dev Tools · APIs: requests, answers, environments, sockets and GraphQL
ApiRequestBuilder builds a request: its method and address, then tabs
for the query's pairs, headers, body and sign-in, with the address that
goes out under the field, the environment's {{variables}} filled in or
the missing ones named. Send hands the owner the request. The query's
pairs go through forms::KeyValueInput. ResponseViewer shows an answer's
status in its class's tone with its words, its time and size, then its
body, JSON indented, or its headers in a HeadersTable.
EnvironmentSelector picks a set of variables and masks secrets until
their eye opens them. CollectionTree holds saved requests in folders;
Enter opens one. WebSocketConsole shows a socket's messages, each
marked sent or received with its time, and sends on Enter while the
socket is open and the field holds words. GraphQLExplorer browses a
schema beside a query and its variables; Run waits for variables that
read as JSON and the answer shows as a tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 10:24:44 -04:00
ZacharyZhang-NYandClaude Opus 5.5 54eb947b34 DB & Dev Tools · Data formats: every YAML escape, keys split outside quotes
Double-quoted text reads all of YAML's escapes, \x, \u and \U among
them; a code that is no character, or a lone backslash at the end,
fails with its line. A map entry splits at the first colon outside
quotes, so a quoted key or list item may hold ": ". TASKS records the
third round and closes the item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 10:12:20 -04:00
ZacharyZhang-NYandClaude Opus 5.5 479d902643 DB & Dev Tools · Data formats: quoted YAML reads its escapes
A double-quoted scalar reads \" \\ \/ \n \t \r and \0, a single-quoted
one reads '' as a quote, and a key loses either kind of quotes. An
escape YAML does not know fails with its line, and so does block text
after | or >, which the reader does not take.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 10:03:53 -04:00
ZacharyZhang-NYandClaude Opus 5.5 a0984e80a3 DB & Dev Tools · Data formats: YAML lists at their key's indent, XML entities
YAML reads a list written at its key's own indent, as manifests do, and
a list at a map's indent ends at the next key. A `#` inside a quoted
scalar is no comment. XML reads entity references in text and
attributes, failing on one it does not know, and a text's path counts
text nodes from one. A tree's note takes at most half its row, cut with
an ellipsis, so a long value or attribute leaves the key its name.
Format and Minify fail loud if pressed while the JSON does not read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:42:03 -04:00
ZacharyZhang-NYandClaude Opus 5.5 1c01adc4d6 DB & Dev Tools · Data formats: JSON, YAML and XML as trees, a JSON editor
A document reads into one value, Datum: JSON through serde_json, its
keys now kept in their written order, and YAML's block style through a
small reader that says which line it could not read. JsonViewer and
YamlViewer draw it as one tree: each key with its value, maps and lists
with their counts; a search keeps what holds its words and opens the
way; Enter on a value copies its JSONPath. XmlViewer reads elements,
attributes and text and says where tags cross. JsonEditor writes JSON
in the code editor with Format and Minify and says whether it reads.
DocumentViewer is JsonViewer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:22:30 -04:00
ZacharyZhang-NYandClaude Opus 5.5 8c3811aef9 TASKS: T33a passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:19:30 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9340c8b1b3 DB & Dev Tools · Databases: the structure editor stacks in a narrow box
TableStructureEditor measures its width. With room for its least width
it lines the fields up as a table whose cells take fixed shares of that
width, so what they hold is measured at its final width; in a narrower
box each field stacks on its own, its name and remove button, its type
with its flags, then its default, all inside the box. No sideways
scroll remains, so no Tab stop lies past a fold. Tests cover both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:16:56 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d14117849a DB & Dev Tools · Databases: narrow boxes scroll or wrap; the form's rules tested
QueryPlanViewer's step column takes the label width and the table
scrolls sideways past its least width; TableStructureEditor's rows
stretch to a least width that scrolls sideways; RedisKeyBrowser's
columns wrap below each other inside 280px. The form's rules are pure,
parse_port and ready, with unit tests; gpui tests cover saving, a new
connection refilling the form, another engine bringing its port, and a
press on a row's button leaving the row alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:05:35 -04:00
ZacharyZhang-NYandClaude Opus 5.5 4c9727d67c DB & Dev Tools · Databases: connections, schemas, queries, structure, Redis
A new chapter, src/devtools. ConnectionManager lists saved connections
with their engine and address, or why one failed, and connects on Enter;
ConnectionForm fills a connection with a port checked from 1 to 65535,
tests it and saves it. SchemaTree shows schemas, tables, views and
fields with keys marked; ERDiagram draws tables and their foreign keys
on NodeGraph, whose fixed wires show only the sockets that hold one.
QueryHistory finds past runs and runs one again; QueryPlanViewer shows
each step's share of the cost. TableStructureEditor edits fields in
place; IndexManager lists and draws up indexes; RedisKeyBrowser finds
keys by glob and lays out each type. QueryEditor is editor::CodeEditor,
whose lexer now knows SQL's words, and QueryResultGrid is
tables::DataGrid. lists::row_action keeps a row's button its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:51:45 -04:00
ZacharyZhang-NYandClaude Opus 5.5 94ae7e73e7 TASKS: T32d passes review; T33 splits into four
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:45:02 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9170d47f8b Canvas · Graphs and boards: a field hands focus back on Enter and Escape
writing::begin records the owner's focus, the mind map's or the tool
layer's, through forms::Editing::returning. The field keeps its words on
Enter's release and drops them on Escape, and both hand focus back, so
the map's arrows and the board's Backspace work at once after a write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:41:05 -04:00
ZacharyZhang-NYandClaude Opus 5.5 bd0285bd37 Canvas · Graphs and boards: Tab keeps a topic's words; the untested rules tested
Tab in a mind map topic being rewritten keeps the words and hands focus
back to the map; it asks for no child while the field is open, and the
map's keys wait for the field. Tests cover a new wire into a wired input
replacing the old, a connector released on its own shape linking
nothing, and a deleted whiteboard shape taking its links with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:35:08 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f3822a8d98 Canvas · Graphs and boards: node graph, flow chart, mind map, whiteboard
NodeGraph joins Nodes by wires between typed Ports: a title drags a
node, a drag out of an output lands on an input of the same kind, an
input takes one wire, and a press on a wired input lifts its wire.
Links join shapes on the plane and follow them, straight or at right
angles, with an arrow and words; the Connector tool draws them and the
Note tool drops a sticky note. A double press with Select writes on a
shape. FlowChart draws Steps as ellipses, boxes and diamonds with their
words. MindMap lays topics out from a root, half to each side, and asks
the owner to add, remove and rename by key. Whiteboard keeps its own
tool, view and selection over a board it hands back after each change.
Painting moves to canvas::paint; a press on the tool layer goes through
canvas::press, and the layer focuses itself and asks to delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:22:54 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5d58ee4577 TASKS: T32c passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:07:36 -04:00
ZacharyZhang-NYandClaude Opus 5.5 c04c67d39a Canvas · Panels: layers, inspector, alignment, assets, style and layout editors
LayerPanel lists the canvas's layers topmost first on lists::Tree: a box
shows or hides each, F2 renames, a drag restacks into a new paint order.
InspectorPanel edits the one selected shape's frame, fill, visibility and
lock. AlignmentToolbar lines up or spreads the selection through
`aligned`. AssetPanel finds shapes by name in a picks grid, each drawn
to fit its tile. ColorPanel, ShadowEditor, BorderEditor and
TypographyPanel edit a look; GradientPanel is forms::GradientEditor.
ConstraintsEditor pins a shape to its frame, with `Constraints::placed`;
AutoLayoutControls sets flow, gap, padding and a place grid, with
`AutoLayout::laid`; ExportPanel names each file once. The panels share
`canvas::panel`: editing, pair, caption, heading and color_well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 07:58:27 -04:00
ZacharyZhang-NYandClaude Opus 5.5 7d493b669b TASKS: T32b passes review
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 07:50:23 -04:00
ZacharyZhang-NYandClaude Opus 5.5 b55c81d994 Canvas · Tools: single handles, a wrapping palette, the pen ends with its tool
Handles resize one shape, so a selection of several shows its outline
and no handles; a press there no longer falls through to a marquee.
ToggleGroup wraps its faces when narrow, so the ToolPalette stays in a
280px box. A pen path left open ends when the tool changes: two points
or more go to the owner after the frame, a lone point is dropped, and
the open path paints only while the pen is the tool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 07:41:10 -04:00
ZacharyZhang-NYandClaude Opus 5.5 49a205ea35 Canvas · Tools: select, move and snap, resize, draw, pen, text, brush
ToolLayer lies over an InfiniteCanvas and carries its tools. Select
takes the topmost shape a press reaches, Shift adds or drops one, a
drag moves the selection and snaps its nearest edge or middle to
another shape's within reach, a handle resizes it, Shift keeping a
corner's shape, and a drag on empty space draws a SelectionBox that
takes what lies wholly inside. The shape tools draw by a drag toward
any corner, Shift keeping them square; the pen adds a point per press
and a double press ends the path; text drops a line where pressed; the
brush strokes a path its width. A drag may end in the layer or out of
it; each gesture ends once and asks the owner. SelectionBox,
TransformHandles and SnapIndicator draw the marks.

ToolPalette picks a tool from icons named in tips; BrushSettings sets a
brush's width and hue; HistoryPanel lists the steps done, the undone
ones quiet, and takes the drawing to a step on Enter or a double press.

The model changes: lines and arrows end at a named corner, so they run
either way, and paths carry their width. Three Lucide icons: hexagon,
slash and move-up-right.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 07:27:27 -04:00
ZacharyZhang-NYandClaude Opus 5.5 ea99d30e3b Canvas · the wheel scrolls as scroll boxes do; pans hold at the press
A wheel without a modifier now slides the content by its delta, as
gpui's scroll boxes do; it moved the other way. The wheel reads the
live viewport and skips an end with no delta, and a drag pans from the
press by the pointer's whole way with the viewport held at the press,
so events that land in one frame no longer overwrite each other. Tests:
a plain wheel moves the content up with it; three wheels and an empty
end add up, and four moves pan their sum; a small frame fits at a zoom
of one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 07:15:01 -04:00
ZacharyZhang-NYandClaude Opus 5.5 2c099ceffb Canvas · Plane: an endless canvas, its grid, rulers, zoom and map
A new chapter, src/canvas. InfiniteCanvas lays artboards, shapes and
guides on an endless plane over a dot grid whose step, one, two or five
times a power of ten, keeps the dots apart as it zooms out. Rulers run
along the top and left in canvas units. A drag on empty space pans from
the press, a wheel scrolls, and Command or Control with the wheel zooms
about the pointer; the owner keeps the viewport, seeded, so its own
zoom shows at once. Layers lie over the plane in view pixels.

Shapes are rectangles, ellipses, regular polygons, lines, arrows, text,
paths and notes, each in a chart hue, painted to scale; text and notes
scale their type. Artboards carry their names above them.

ZoomControls step out and in through fixed stops, fit on request, and
hold ZoomIndicator, whose press sets 100%. MiniMap draws what lies on
the canvas and the view's box, and centers the view where it is pressed
or dragged. The viewport math, the steps and the map's fit are tested
without gpui.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 06:58:59 -04:00
ZacharyZhang-NYandClaude Opus 5.5 86336ee828 Project · focus that follows: segments, the tracker's action, tests
A segmented control too narrow for its segments now scrolls sideways
in a strip of its own, and a segment that takes focus comes into view:
its focus is a tab_stop handle, and layout::reveal_when_focused, now
shared with the board's cards, measures it from a box that holds its
ring. The DatabaseView's own strip goes.

The time tracker's Start and Stop are one Button that keeps one handle
through the new Button::focus_handle. A start moves focus to it, a stop
hands focus back to the field, and the field starts on Enter's release,
so the release no longer clicks the Stop that took focus.

ProgressRing's percent refuses content inside, as content refuses a
percent. Tests: Enter starts, Enter stops after 90 seconds, and the
field takes the keys again; Skip moves on at once; a focused Timeline
segment at 280px comes into view; the ring's refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 06:32:50 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6377f926c8 Project · Time and databases: a time tracker, a pomodoro, a database
TimeTracker times a task: a field names it, Start or Enter starts the
clock and Stop ends it, the running time counting up by the executor's
clock; the entries sit under it in hours and minutes with their total.
The owner keeps what runs and what was spent, and a blank name starts
nothing.

PomodoroTimer runs focus, a short break, and after the last round a
long one. A ring counts each phase down around the time left, dots
count the rounds, Pause banks the time gone, and a phase that runs out
moves on, waits for Start, and reaches the owner through window.defer.
motion::ProgressRing gains a size and content inside, its stroke kept
at the theme ring's weight, the content outside the per-turn glide.

DatabaseView shows tasks five ways, switched from a strip that scrolls
when narrow: a table, a board by status whose moves hand on the new
status, a gallery of issue cards, a month calendar by due day, and a
timeline from start to due, which counts the records without dates.
Task gains a start day.

Along the way: table rows, groups and details fill with min_w_full, so
they stretch in the table's column and fill a virtual list alike;
before, a table in a measured column drew rows at their words' width.
The gallery script logs each probe it aims at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 06:11:38 -04:00
ZacharyZhang-NYandClaude Opus 5.5 28973fe1d2 Project · a roadmap bar's end and a workload bar's cap, tested
A roadmap bar ran through its last day, but no test looked at its end:
a bar stopping at the last day's start stayed green. The roadmap test
now asserts October's bar reaches the span's end. The workload bar's
share, capped at the whole track, moves into Load::filled with a unit
test, so a load past capacity cannot draw past its track unseen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 05:53:28 -04:00
ZacharyZhang-NYandClaude Opus 5.5 75f00883ac Project · Plans: a roadmap, a burndown, milestones, workload
Roadmap lays initiatives across months in lanes: each a bar from its
first day through its last, filled as far as it has come, under the
months' names and a line at today; hover a bar for its dates. Bars sit
at fractions of the span, so nothing is measured, and a strip too
narrow for its months scrolls sideways at a least width per month.
GanttChart and TimelineView are the charts GanttChart.

BurndownChart lives with the charts, on their scales, labels, tiles
and paint: the ideal line from the scope down to nothing, what was left
at each day's end so far, and a line at today; hover a day to read it.

MilestoneProgress shows a milestone's issues done as a bar and a count,
when it is due, and whether it keeps pace: on track, at risk when the
work trails the time gone by more than a tenth, late past its day, or
done. WorkloadView sets each person's points against what they can
take, the bar turning red past it, in columns that line up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 05:42:03 -04:00
ZacharyZhang-NYandClaude Opus 5.5 fc0c3e564d Project · a focused card scrolls into view
The board scrolled sideways without a handle, so a card that took
focus past the fold stayed out of sight, ring and all, and Option-Right
could send it into a column nobody saw. The board now tracks its
scroll, and a card that takes focus sets the offset through
layout::bring_into_view from its row's painted box, ring included, once
per focus and again when it lands in another column. The stepping
rules gain tests for left and right keeping a card's place and for the
end of a shorter column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 05:28:35 -04:00
ZacharyZhang-NYandClaude Opus 5.5 62382f7e68 Project · Issues and boards: a board, issue cards, a task's panel, labels
KanbanBoard lays columns of cards side by side. A card drags to a
place in any column: it rides raised at the pointer, a slot opens where
it would land, the other cards glide aside on motion::Flip, and the
drop asks the owner through on_move(card, column, index); a drop in its
own place asks nothing. Option with an arrow moves the focused card,
whose focus the board keys by card so it follows it across columns. A
press, Enter or Space opens it. Sprint Board is this board.

IssueCard shows an issue's status, name, assignee, title, priority,
labels and due day. TaskDetailPanel lays a task out in full: its title
edits in place, its status, priority, assignee and due day change where
they stand, and its notes and steps follow; each edit hands the whole
task on, and a blank title is not taken. LabelManager tends a team's
labels: a hue picked from a popover, a name edited in place, a delete,
and a field that makes a new one; names stay unique in any case, and a
refused name says why.

The fill rule changes: min_w_full, taken up in T30d, resolves against
a block's outer width in taffy 0.9, so a field in a padded card ran
past its padding. A component that fills now takes no width on its
root, which blocks and flex columns stretch; FormField and the new
roots do. The chart's hue names move to theme::HUE_NAMES, which the
event editor and the label manager share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 05:12:20 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9b506e5d54 Project · Tasks: priorities, issues, statuses, assignees, task rows
A new chapter, src/project. PriorityIndicator shows how urgent a task
is in three bars, urgent in red, its words in a tip or beside it.
IssueIdBadge names an issue by its team's key and number, as ENG-123.
StatusSelect picks a status from the workflow, each with its mark, and
AssigneePicker picks a person or no one; DueDatePicker is the forms
DatePicker. TaskItem is a task in a row: its box, priority and issue,
its title, then its due day, red when past and still open, and who has
it. TaskList puts them in a SelectableList: arrows move, Enter opens,
and a box's press or keys stay at the box. SubtaskList is the
documents Checklist, which gains a field that adds a step on Enter.

Along the way: forms::Choice::avatar leads a person's row with their
initials, in lists and in a Select's trigger; ListItem::struck strikes
a closed task's title; typography::Ellipsis now shapes with the text
style's run, so a strike or underline on its box shows; two Lucide
icons, circle-dashed and circle-dot-dashed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 04:48:54 -04:00
ZacharyZhang-NYandClaude Opus 5.5 e2eb1cfc41 Calendar · open times start over on new days
AvailabilityPicker kept the day chosen in keyed state whatever days the
owner passed next, and the slots lookup expected it among them: an
owner paging to the next week crashed the app once a day was chosen.
The choice is now seeded on the owner's days and pick, so a new run of
days starts it over at the picked day or the first open one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 04:31:14 -04:00
ZacharyZhang-NYandClaude Opus 5.5 07a577b574 Calendar · Editing: an event's editor, its repeats, reminders and open times
EventEditor puts an event's parts in a form: its title, whole days or
hours, where, its color, how it repeats, its reminders and notes. Save
waits for a title and an end after the start and hands on an
EventDraft; Cancel and Delete show when the owner handles them. A new
draft from the owner starts the editor over.

RecurrenceEditor sets a rule after RFC 5545: daily, weekly on its days,
monthly or yearly, every so many, until a day or a count. A line reads
the rule back with the next days it falls on. ReminderPicker keeps
reminders from a fixed list, soonest first, and Add brings the nearest
one not yet set. AvailabilityPicker counts the open slots of each day
and lists the chosen day's, around what is busy, inside working hours
and after now.

FormField filled its column with a percent width, which taffy 0.9
leaves at content width when a block measures the column first and the
width comes out the same: at 280px the editor's fields shrank to their
labels. It now fills with min_w_full, as new roots do; older roots move
over in T44.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 04:22:02 -04:00
ZacharyZhang-NYandClaude Opus 5.5 a506ddc98c Calendar · Agenda and people: an agenda, an event's details, its guests
AgendaView lists the days ahead under their names, whole days first,
then by the hour, and leaves out days with nothing; it rides on
lists::Sections, so a press or an arrow selects and Enter opens. An
empty stretch says nothing is planned.

EventPopover is an event's chip that opens its details: its title in
its hue, when and where, who is asked, and Edit and Delete when the
owner handles them. It opens through Popover::with_opener, which now
hands any element the toggle and keeps focus, placement and every way
out in the popover. AttendeeList counts the answers and lists each
guest with their answer, the organizer first. A hue worn in a row is
now data_display::color_mark, for mail labels and events alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 03:35:59 -04:00
ZacharyZhang-NYandClaude Opus 5.5 b68f2bf1d5 Calendar · views on the clock keep moving
The week and day views read the clock once and handed that moment down
as given, which turned off the line's ticker: the line at now and the
lit day stood still. The owner's moment now goes down unchanged, and a
time grid, month view or year view on the clock keeps its own ticker,
so the line moves and today turns over at midnight, even on days that
are not today. A given moment holds them still.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 03:23:18 -04:00
ZacharyZhang-NYandClaude Opus 5.5 a41cb2f9c2 Calendar · Time grid: week and day views, drag to make and to resize
TimeGrid sets days side by side under a column of hours. Each event is
a card by its hours, clipped to each day it touches, and events that
overlap share the width in lanes. A drag on empty time makes an event
by quarter hours through forms::pen; a drag on an event's lower edge,
on the day it ends, moves its end, each drag checked against its
owner. The grid opens at half past seven, or an hour and a half
before now when today is shown and that is later.

CurrentTimeIndicator marks the minute now on today and moves with the
clock. TimezoneOverlay adds another zone's hours beside the grid's.
AllDayRow lays whole days across their columns in lanes.
CalendarWeekView and CalendarDayView are one view of seven days or
one: a header that steps and comes back to today, the days' names
stacked so they fit narrow columns, the whole days, then the hours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 03:13:38 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f316ab56ba Calendar · Months: a month view, a year view, event chips and cards
CalendarMonthView lays a month in week rows. An event across days lies
over them as a bar in the first free lane, cut at the week's edge;
the rest sit by the hour as chips, and a crowded day keeps the rest
behind "N more". Arrows move a day, Page keys turn the month, and
Enter or a double press opens the day; the cursor holds through the
owner's echo of a turn. Days sit at sevenths of their row, as the bars
do, so text never widens a column.

YearView shows twelve small months; a day with something on it wears a
dot in its first event's hue. EventChip is an event in a line and
EventCard a block with its hours and place. MiniCalendar points to
forms::Calendar. Palette::hue is now the one home for a chart color by
index, for mail labels and events alike.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 02:49:37 -04:00
ZacharyZhang-NYandClaude Opus 5.5 c1a5603184 Primitives · a raise keeps its state under its owner's id
A raise inside another was namespaced by its place in the layer's
queue, so one that came before it moved its id path and rebuilt its
keyed state and focus: an open date popup would lose its month when a
tip appeared. raise now takes its owner's id and draws under it, and
float and float_height pass the owner's id on from every call site.
A test holds a focused box in a raise while another comes before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 02:23:05 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9abab5f2ba Primitives · raise: what opens inside a dialog lies over it
gpui 0.2.2 asserts on a deferred draw inside another, and every dialog,
sheet and popover draws inside one, so opening a date field in the
snooze or schedule dialog panicked, as would any list, menu or picker
opened in an overlay. primitives::raise replaces each deferred draw:
at the top it defers as before; inside something raised it leaves a
placeholder in the tree, and its layer lays it out on its own, then
draws it last. check.sh refuses gpui's deferred anywhere else.

The time dialog's rule is now times::ahead, tested for a moment
behind, now itself, a moment ahead and another zone; a gpui test picks
yesterday in the snooze dialog, sees that it has passed and that
nothing is sent, then picks two days ahead and hears that moment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 01:51:51 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5b0a8443a7 Mail · Sorting: a label picker and a snooze menu
LabelPicker puts labels on a message: a field finds them by name,
arrows move and a press or Enter turns one on or off, each row with its
check and its hue. A name no label has can be made into one, and the
field starts over. Its rows are the option rows every list shares;
forms::marked_row leads one with a mark. The cursor shows only while
the field has focus.

SnoozePicker offers later today, tomorrow, this weekend and next week,
each with its day and hour, or a time picked in a dialog. It shares
mail::times with ScheduleSend: the names, the dialog, which now waits
for a time ahead of now, and a rule that a moment comes once, so on a
Sunday tomorrow morning no longer repeats as Monday morning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 01:16:24 -04:00
ZacharyZhang-NYandClaude Opus 5.5 154f7f2b46 Data display · a tag stays inside its container
A Tag is capped at its container's width, and its label ends in an
ellipsis beside a fixed remove mark, so a long name in a narrow tag
field stays inside the field instead of running past it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 00:38:30 -04:00
ZacharyZhang-NYandClaude Opus 5.5 84c3dc16de Mail · Writing: the composer, recipients that suggest, a signature, send later
MailComposer writes a message: To, and Cc and Bcc once asked for, as
tag fields that suggest contacts and mark what is not an address; the
subject and the words; the files, each with a way off; the signature
under the words. Send wakes once there is someone to send to and every
address is one; it hands over the draft now or at a time, and Discard
lets it go. A new start from the owner begins the draft over.

forms::TagInput now suggests: the rows that hold the typed words float
under it through the list Combobox uses; Up, Down and Enter pick, and
Escape sets them aside. RecipientInput points to it. ScheduleSend
offers tomorrow morning, tomorrow afternoon, Monday morning, or a day
and hour picked in a dialog. SignatureEditor writes the lines and shows
how they sit under a message; it tells the owner's echo from a new
signature, so the caret stays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 00:27:23 -04:00
ZacharyZhang-NYandClaude Opus 5.5 48118499ae Mail · a folded message keeps focus as it opens, and gives way at 280px
Each message in a thread holds one focus handle, a Tab stop while
folded and tracked on the letter once open, so a keyboard open keeps
focus on the message and Tab goes on to its Reply. A press on a fold
leaves focus alone. A fold's name now gives way with its words, so a
long name beside a long date stays inside the row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 00:07:27 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f946af6216 Mail · Reading: a message reader, a conversation, and quoted text
MailReader opens a message under its subject: the sender's picture,
name and address, who it went to, when it came, and Reply, Reply all
when it went to more than one, and Forward, each only with a handler;
then its words and its files, each downloadable. MailThreadView lays a
conversation oldest first: the newest stands open, the others fold to
a line of the sender, their first words and when, which a press, Enter
or Space opens. QuotedText folds words quoted from before behind a
small mark; open, they sit behind a rule. Long names give way at the
label width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:57:06 -04:00
ZacharyZhang-NYandClaude Opus 5.5 0a281d91ab Mail · a focused star keeps Space and Enter; so does Unpin
A button in a SelectableList row now stops Space and Enter at its box
on key down, so the list no longer toggles its selection before the
button acts. The mail star and the pinned messages' Unpin, which had
the same fault, both do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:45:22 -04:00
ZacharyZhang-NYandClaude Opus 5.5 b6b1224c27 Mail · Boxes and lists: mailboxes, folders and labels, the mail list, inbox zero
MailboxList sets mailboxes, folders and labels in sections: a press, an
arrow or Enter opens one, the open one lit; a box with mail unread
stands out with its count, and a label wears one of the theme's chart
hues. MailList holds messages in a multiple-select list: the sender
strong beside a dot while unread, the subject and first words under
it, the time today, the weekday this week, the date this year or with
its year, a paperclip for files, and a star whose press stays off the
row. InboxZeroState points to feedback::EmptyState.

Sections of selectable lists now have one home, lists::Sections, which
ChannelList and MemberList use too; ListItem gains a third line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:35:04 -04:00
ZacharyZhang-NYandClaude Opus 5.5 6f599803ce Messaging · GIF picker: a GIF focused past the fold scrolls into view
Each GIF holds its own focus handle, and a canvas around it, ring and
all, sets the results box's offset once per focus from its painted
bounds. layout::bring_into_view now works along either axis; the data
table's header and the video timeline pass their sideways one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:11:46 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5ade2bed74 Messaging · Stickers and GIFs: a sticker picker and a GIF picker
StickerPicker shows stickers a pack at a time, chosen above with a
segmented control; a search spans every pack and keeps the stickers
whose names hold it. Arrows move, Enter or a press picks. It reuses the
glyph grid, now forms::picks, which takes its columns and cell size and
titles from the host, so emoji, icons and stickers share one searchable
grid; forms::glyphs keeps the emoji and icon pickers.

GifPicker packs host GIFs in two columns, each moving, under a search
it owns; the host hears the trimmed words only when they change, and
categories fill the search while it is empty. Waiting, it shows tiles
in GIF shapes; finding nothing, it says so. Each GIF is a Tab stop that
a press, Enter or Space picks.

The share toggle now shows its state, as the microphone and camera do.
The gallery ships line-drawn stickers and small GIFs made from its own
pictures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 23:00:15 -04:00
ZacharyZhang-NYandClaude Opus 5.5 48d02c4903 Messaging · Calls: controls, tiles, a grid, a shared screen, a bar, a ringing dialog, a huddle
CallControls holds the microphone, camera and a shared screen as
toggles, then Leave. ParticipantTile shows a camera's frame from the
host or the person's initials, their name with a mark when muted, and a
ring while they speak. VideoCallGrid lays tiles in even rows as far as
they fit across its measured width; a short last row centers at the
full rows' width. ScreenShareView names who presents and offers Stop
presenting for your own share, the call's tiles beside or below.
VoiceCallBar tells where a call is, how long it has run and who is in
it, with the microphone and Leave. IncomingCallDialog rings with the
caller's picture; Accept or Decline answers each ring once, and Escape
declines. HuddleIndicator rings while a huddle is live; a press joins.

The recorder's device toggle and the camera's name chip now have one
home in media. People: member names sort whatever their case, a
status's words save trimmed, and a profile card's local time ticks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 22:33:54 -04:00
ZacharyZhang-NYandClaude Opus 5.5 37e56c7ab7 Messaging · People: members, a profile card, online status and a status setter
MemberList sets members online and offline by name, each group a list
under its count. A press or an arrow chooses one; Enter or a double
press acts on them; offline members go quiet. UserProfileCard shows a
person's picture with presence, name and standing, title, status, the
time in their zone, and Message and Call, each only with a handler.
OnlineStatus draws a presence's dot and word; Presence now names itself
and gives its color, in data_display, its one home.

StatusSetter holds a draft seeded from the owner's status: an emoji
from a picker field, words in its own text field, and when they clear.
Suggestions fill all three; Save hands the status over and Clear status
removes it. The emoji field reuses the date pickers' field, whose
trigger ignores the Enter release after a pick.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 22:08:25 -04:00
ZacharyZhang-NYandClaude Opus 5.5 5bbfdcd766 Messaging · Messages: messages in runs, threads, the unread line and receipts
ChatMessage sets a message under its author's picture, name and clock
time. The next by the same author within five minutes, on the same day,
joins the run: only its words, the time in the gutter on hover.
UnreadDivider marks where unread messages start. MessageThread tells who
replied, how many and when the last came; with a handler it opens on a
press, Enter or Space. ThreadPanel holds the first message, a rule
counting the replies, the replies and a composer, its body scrolling.
ReadReceipt says sending, sent, delivered or read, and who saw it in a
group. MessageReactions and TypingIndicator point to collab and motion.

gpui recolors nothing on hover: text takes its color at layout. The
gutter time rests invisible and turns visible through a group hover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:48:17 -04:00
ZacharyZhang-NYandClaude Opus 5.5 3c8a5a026c Messaging · Channels: the chat list, its rows, a channel's head and its pins
ChannelList sets chats down in sections, each a list: a press, an arrow
or Enter opens one, and the open chat is lit. A title names one section,
and a section holds a chat. ChannelItem shows a hash or a lock, its
unread count, a pin when pinned, and goes quiet when muted.
DirectMessageItem shows a person's initials with their presence. Unread
names stand out through ListItem's new strong; muted channels and
offline people go quiet through its quiet. ChannelHeader holds the name
and topic beside members, pins and a call, wrapping by the header rule.
PinnedMessages lists pins newest first; a press, an arrow or Enter opens
one, and Unpin lets one go. With none it shows a line and takes no Tab
stop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:30:17 -04:00
ZacharyZhang-NYandClaude Opus 5.5 eeeca67e55 Files: a press on a download's button stays with the button
gpui runs the click of every pressed element under the pointer, so a
press on a finished download's Show button also opened the file through
the pressable row. The actions' box stops the press on mouse down, so
the row starts no click of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:12:38 -04:00
ZacharyZhang-NYandClaude Opus 5.5 0fcde7dbbe Files · Transfers: file operations, a transfer queue, downloads and storage
A file on its way is a Transfer with a state: waiting, moving at a rate,
paused, done or failed with a reason. One row draws it for the queue and
the downloads: its icon, its way, a bar while it moves or waits paused, a
status with the time left, and only the actions its state allows.
TransferQueue counts what moves and waits. DownloadManager keeps the
finished below under Clear, opening on a press and showing their folder.
FileOperationProgress tells a copy or move with its bar, time left and
current file, its header wrapping by the header rule; a failure speaks in
the danger tone. StorageUsage points to data_display's UsageBar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:05:59 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f324702c89 Files: recent and found files keep the cursor on the file, not its place
RecentFiles and FileSearch keyed their rows by place in the list, so when
an open moved a file to the top, the cursor stayed at the place and the
next Enter opened another file. Rows are keyed by the file's folder and
name, asserted once a list, so the cursor follows the file. A dotfile's
kind reads by its extension, as its icon does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 20:55:21 -04:00
ZacharyZhang-NYandClaude Opus 5.5 c5c35f90fc Files · Find and look: preview, recent files, search, duplicates and archives
FilePreview shows a file large: its picture in the host's shape, the first
lines of its text, or its kind's icon, over its name, kind, size and
change; the gallery opens it on Space as a quick look. RecentFiles lists
files newest first under a header a day, a list a day. FileSearch sits
over a field the host owns and lists what the host found with its folder.
DuplicateFinder marks copies to remove and keeps the last of each group,
its mark shut, with the bytes the marks free beside Remove. ArchiveViewer
draws an archive's entries as a tree with sizes and savings, from paths
gathered by the folder builder the file tree now shares. A checkbox's
label wraps inside its box instead of running past it. Favorites and the
hex viewer point to documents and debug.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 20:42:06 -04:00
ZacharyZhang-NYandClaude Opus 5.5 78fc3f90c0 Files: a narrow listing keeps its names; a column starts fresh for a new folder
The directory listing's size and date columns are fixed, so at 280px the
name had no room and rows showed no names. The heading and rows now sit
in a sideways scroll box whose child keeps the least width: the name's
least, the fixed columns and each row's icon, gaps and padding. A column
of the explorer was keyed by its depth, so a new folder there inherited
the old list's cursor; columns and rows are keyed by their folder. The
explorer asserts each folder on its path is a folder in its parent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 20:22:12 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f694f0d413 Files · Explorer: a folder as a list, tiles or columns, with one path and one order
FileExplorer shows the folder at the end of a path three ways: the
directory listing, a grid of tiles, or a column per folder along the
path with the next folder lit. Its name, its count and a switch of views
sit above one path bar that climbs. A folder opens in place; a file goes
to the host, which lists each folder it goes to. A pick in an earlier
column closes the columns past it, and a new deepest column scrolls into
view. The directory listing takes its pick from its owner and reports
it, so the pick carries across views; its path bar and its first order
are shared. The gallery walks a small file system and shows the tree,
the context menu, renaming in place and a drop zone in use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 20:07:33 -04:00
ZacharyZhang-NYandClaude Opus 5.5 906e6c8ac1 Files: a tile's name keeps its ends; the ellipses share one line cutter
gpui's line_clamp wraps once and lays the rest of a long name on the
second line, which a centered tile then clipped at both ends. A tile's
name is now one line, cut in the middle so the front and the extension
stay. MiddleEllipsis draws like Ellipsis: in the text style around it,
sized by its text, so a short name centers; the two share one cutter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 19:51:43 -04:00
ZacharyZhang-NYandClaude Opus 5.5 ebad5328ed Files · Items: one icon map, file icon, file row and file grid
The crate had two maps from a file's name to its icon, in chat and in the
file tree, and they disagreed. One map now lives in primitives, over the
Lucide file-* icons, and chat, agent, the file tree and the directory
listing use it. FileIcon draws it for a file or a folder. FileItem is a
file's row, a ListItem with its size and when it changed. FileList is the
directory listing. FileGrid lays tiles in the order given, with a cursor
seeded from the owner's pick: arrows by tile and by row, Home, End, Enter
and a double press to open. The count of what fits a width has one home,
layout's columns_for, and fit builds on it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 19:40:10 -04:00
ZacharyZhang-NYandClaude Opus 5.5 9872adc5e9 Forms: an option list stays inside the window; media ratios are checked in one place
The floating list under a Select, Combobox or MultiSelect was at least its
trigger's width with no ceiling, so a long choice ran it past the window
and its rows never ellipsized. It now stops at the window's width less the
window margin on each side, so gpui snaps it inside. The camera preview
and the model viewer check their ratio through primitives::checked_ratio.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 19:17:52 -04:00
ZacharyZhang-NYandClaude Opus 5.5 7f1cfe8c3d Media · Capture and devices: recorder, camera, mic meter, device choice and 3D model viewer
ScreenRecorderControls holds a recording in one pill: Record, then a live
dot and the time it has run beside pause or resume and stop, with mic and
camera toggles. CameraPreview shows host frames in the camera's shape, or
the camera off. MicLevelMeter lights segments up to the level, amber and
red near the top, with tones shared from data_display. DeviceSelector
picks a microphone, camera or speaker through Select, which may now be
empty while shut. ModelViewer shows the host's frame for an orbit that a
drag, the arrows, Command-scroll, + and - and a reset move; the orbit is
seeded from the host so fast drags add up. The gallery draws a stone
torus per orbit and frees each replaced frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 19:05:23 -04:00
ZacharyZhang-NYandClaude Opus 5.5 235f059c8e Lists: a selectable list's cursor starts on the owner's pick; the waveform's fit is tested
SelectableList keeps the owner's last selection and the one it last
sent. A selection the owner makes, on first paint or later, moves the
cursor to its first row; the echo of the list's own pick leaves the
cursor where the pick put it. The playlist's keys start from the song
under way. The waveform's bar count and inset move into `fit`, tested
beside `resample`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 18:44:38 -04:00
ZacharyZhang-NYandClaude Opus 5.5 4b86283d69 Media · Audio and controls: player, waveform, spectrum, volume, speed, transport and playlist
AudioPlayer sets a song's cover, title and artist over a scrubber with
the time played and left, the transport and the volume. AudioWaveform
draws peaks gathered to the bars that fit, dark as far as it has played,
and seeks by press, drag and keys, with the time tip the scrubber shares.
AudioSpectrum shows levels by pitch. VolumeControl mutes from its speaker,
and moving its slider while muted unmutes. PlaybackSpeedControl picks from
a menu. MediaControls holds shuffle, back, play, forward and repeat.
Playlist builds on SelectableList: the song under way is lit with moving
bars, and Enter or a double press plays another.

The video player's bar now rests while the stage itself holds focus, a
key it takes wakes the bar, and a press on a resting bar only wakes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 18:32:45 -04:00
ZacharyZhang-NYandClaude Opus 5.5 fcb1b2d9df Media: the player's bar fades by elapsed time, so its scrubber and buttons keep their state
The fade restarted an animation id at each rest and wake, and gpui hands
that id to every descendant: the scrubber's drag, hover and focus handle
and the buttons' focus were rebuilt under it. The bar's opacity now reads
the time since the last change through motion::since_change. The stage
tracks focus even without key handlers, so a focused bar button keeps
the bar up. The player's tests move to their own file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 18:14:06 -04:00
ZacharyZhang-NYandClaude Opus 5.5 fbf99ed4f7 Media: the caption outlasts the bar; one Space per press; narrow boxes hold
Only the bar fades now, so the caption stays while the controls rest, and
the caption chip can shrink, so a long line wraps inside a narrow player.
A focused media button keeps Space to itself, since gpui presses it on
the release. The player and the strip let Command and Control letters
pass. The scrubber's tip splits the free width by the pointer's share and
stays over the track at both ends. An inline edit's label gives way with
an ellipsis beside fixed parts.

The player is a Tab stop only when a key does something, logs its
captions, keys its fade by its own id, and logs a failed wake.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 17:52:27 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d0175edb7b T26c Media: video
Scrubber shows what has loaded, what has played and its chapters, names
the time under the pointer, seeks by press and gpui's drag, and steps by
the slider's keys; over_media lights it for a dark layer. VideoPlayer
shows the host's frame in its shape with a caption and a control bar over
it: the scrubber, play, the time, speed, captions, picture in picture and
full screen, each only with its handler; the bar rests after a still
pointer while it plays, on one wake from the executor's timer. Keys:
Space and K, J and L, the arrows, C and F. VideoThumbnailStrip lays frames
along the length under a playhead with a trim whose handles drag and whose
ends I and O set. SubtitleEditor edits cues in place: times as mm:ss.mmm,
kept in order, a time that does not read kept out with the reason shown.

InlineEdit is a Tab stop with the ring; it hands focus back after Enter or
Escape and keeps its text on Enter's release. overlays::media_button is
shared by the lightbox and the player's bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 17:07:26 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f7f20b5146 Reviews: T26b passes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 16:46:40 -04:00
ZacharyZhang-NYandClaude Opus 5.5 cb28c0e305 Media: a shaped corner follows the pointer up and down; the fitted crop is told once
With an aspect, a corner drag sizes the crop by whichever side the pointer
moved further, so a drag straight up or down resizes it too. The upload
tells the host the fitted crop once per file, whether the host keeps it or
not; a host that dropped it had set off a loop of reports. The annotator's
Escape stops only when it lets a chosen mark go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 16:38:22 -04:00
ZacharyZhang-NYandClaude Opus 5.5 7d86c804d7 T26b Media: crop and annotate
ImageCropper crops a picture by a box in shares of it: a press holds a
corner, a side or the body (crop::grip_at), a drag through gpui's drag
moves them (crop::dragged), kept inside the picture with a least side;
with an aspect only the corners size it and the box keeps its shape; the
arrows move it. The picture sits inset by half a handle, the focus border
outside it, so handles on its edge stay whole and it keeps its shape.
ImageUpload (T06e) takes pictures only, from a drop or the dialog
(forms::taken, DropZone::kinds), decodes the file for its shape, and with
no crop yet reports the largest centered box of its aspect once. The
annotator draws boxes, arrows, lines and numbered pins in chart colors
through forms::pen; Select chooses a mark, Delete removes it, and Cmd-Z
and Shift-Cmd-Z undo and redo through forms::History. theme.media() holds
their sizes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 16:16:29 -04:00
ZacharyZhang-NYandClaude Opus 5.5 0cf1d3679c Reviews: T26a passes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 15:58:02 -04:00
ZacharyZhang-NYandClaude Opus 5.5 12f0baab15 Media: the viewer's picture moves; a viewer with no picture takes no Tab
The stage's picture carries an id, so gpui keeps its frame and asks for
the next: an animated picture plays, turned or not. A viewer whose picture
has not loaded or cannot open is no Tab stop. AGENTS.md: img() moves an
animated picture's frames only with an id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 15:51:03 -04:00
ZacharyZhang-NYandClaude Opus 5.5 f8a51a5352 T26a Media: images
A new chapter, src/media. ImageViewer fits a picture to its stage and
zooms by the document steps, by keys, buttons and Cmd-scroll about the
pointer; a drag through gpui's drag, a scroll or the arrows pan it once it
overhangs; R turns it a quarter at a time. gpui paints pictures without a
transform, so media::turn turns the decoded frames off the main thread,
an asset keyed by the picture and the turn; `image` 0.25, which gpui
already decodes with, is now a dependency. ImageThumbnail shows a picture
in its own shape with a corner label and a ring when chosen, and is a Tab
stop only with a handler. Lightbox / Gallery Viewer points to
overlays::Lightbox, which the gallery opens from the thumbnails.

T26 splits into T26a–e. AGENTS.md: the drag rule says drags that pan,
turn or slide go through gpui's drag; pictures turn by their pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 15:34:45 -04:00
ZacharyZhang-NYandClaude Opus 5.5 4678773383 Reviews: T25f passes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 15:22:31 -04:00
ZacharyZhang-NYandClaude Opus 5.5 54e0384582 Generative: the embedding turns by drag past its edge; version diffs are tested
The turned embedding turns through gpui's drag, marked with its view, so a
drag keeps turning past the plot's edge. What a chosen version is set
against, and its heading, move into versions::compared, with a test. A
hidden legend entry dims its key and not its ring, and an embedding refuses
a group named twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 15:17:45 -04:00
ZacharyZhang-NYandClaude Opus 5.5 98b1a6416c T25f Generative: data and prompts
DatasetViewer is tables::DataTable with row detail; the gallery pages
through a dataset and opens a sample whole. EmbeddingVisualizer scatters
points by group, flat or turned in three dimensions: lens.rs holds the
projection, where a turned view keeps the unit ball's scale and fits any
point past it at every turn. A drag or the arrow keys spin it, the
pointer names a point, and the legend hides a group. PromptPlayground
gives each {name} of a template a field, marks the blanks, and runs once
every value is in. PromptDiff shows word changes inline, a space parting
a word taken out from the word put in; PromptVersionHistory lists
versions to choose from and restore.

ChartLegend entries that toggle are Tab stops with the focus ring. Each
capture pass starts its page fresh, so dark runs the script from the same
start as light.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 14:57:55 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d196aa1e26 Reviews: T25e passes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 14:02:23 -04:00
ZacharyZhang-NYandClaude Opus 5.5 85f13b5455 Description rows wrap; the fine-tune share is tested
A DescriptionList row drops its value under its label once the value has
less than half a label's width beside it, so a 280px model card no longer
breaks its facts mid-word. FineTuneJobCard's bar share and its epoch line
move into tuning::progress, with a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 13:57:14 -04:00
ZacharyZhang-NY 164d033ad3 T25e Generative: models
ModelCard sets a model's maker, what it does, its facts and tags, and
Use unless it is in use. ModelDownloadManager lists downloads with how
much of how much, speed and time left, and Pause, Resume, Cancel,
Retry and Delete by state. ModelStatus marks a model idle, loading,
loaded with its GPU memory, or failed. HardwareMonitor sets a meter per
part beside its history. FineTuneJobCard shows a job's phase, its
epochs and its loss on training and held-out data.

AGENTS.md: after each item, cargo clean -p ely-gpui-component too;
old builds of the crate had filled the disk. The reveal rule names
layout::bring_into_view.
2026-09-26 13:45:17 -04:00
ZacharyZhang-NY 092d125eda Reviews: T25d passes
Round 2 of the Claude reviewer found no defects.
2026-09-26 13:37:25 -04:00
ZacharyZhang-NY cb1b1ca90a Generative: the video strip scrolls and reveals; tags wrap in their row
The timeline's scroll box is the flex row itself, so its reach is its
shots; a focused shot brings itself whole into view through
layout::bring_into_view, which table headers now share; Extend sits
with the caption under the strip. A voice's tags cap at the row's
width and wrap inside it.
2026-09-26 13:32:44 -04:00
ZacharyZhang-NY 2266b4e367 T25d Generative: sound and motion
AudioGenerationPlayer shows a generated sound's prompt, model and
length, a bar while it is made, then chat::AudioMessage to play it;
Regenerate and Download wait for it. TTSVoicePicker lists voices with a
sample to play, the chosen one washed with a check, tags dropping below
when narrow. VideoGenerationTimeline lays shots along their time under
a ruler, with posters, placeholders, a playhead and Extend.

AudioMessage's waveform is one Tab stop where Left and Right seek, and
it gives way at 280px. debug::ruler serves the timeline's ruler. A mask
shown to read no longer follows the pointer.
2026-09-26 13:16:42 -04:00
ZacharyZhang-NY 6bfd53baee Reviews: T25c passes
Round 2 of the Claude reviewer found no defects.
2026-09-26 13:09:53 -04:00
ZacharyZhang-NY d5129675c9 Generative: a mask shown to read takes no pen; strokes fail loud
InpaintCanvas is a Tab stop with the focus ring only when on_brush
serves its keys, and takes the pen, the crosshair and the ring only
with on_stroke. new() asserts each stroke's radius within REACH and
its points on the picture. The settled strokes' cells stay cached, so
a drag paints only the stroke under the pen over a copy. The canvas
keeps square corners, so the wash fits the picture. The gallery drags
with moves.
2026-09-26 13:05:34 -04:00
ZacharyZhang-NY 997264439b T25c Generative: canvas
InpaintCanvas lays a mask over a picture: brush strokes in shares of
the picture, rastered into a 320-column grid where a later stroke wins
and an erasing one clears, painted as merged rectangles snapped to
device pixels. A ring follows the pointer at the brush's size; [ and ]
size the brush and X swaps it when focused. MaskBrush sets paint or
erase and the size, with Undo and Clear waiting for a stroke.

Pointer strokes move to forms::pen, which SignaturePad and the canvas
share.
2026-09-26 12:54:18 -04:00
ZacharyZhang-NY 8f20af49c3 Reviews: T25b passes
Round 2 of the Claude reviewer found no defects.
2026-09-26 12:47:12 -04:00
ZacharyZhang-NY 2a992f0399 Generative: a picture with nothing to open takes no focus
A done result tracks its focus only beside its press, so a grid with
no open handler leaves focus where it was. A decided comparison drops
its empty verdict row, so no gap is left under the sides.
2026-09-26 12:44:18 -04:00
ZacharyZhang-NY ac949fb1ce T25b Generative: queue and results
GenerationQueue shows each job's picture or where it stands, its
prompt and its progress, with Cancel, Retry and Remove as its state
allows; new rows fold in. GenerationGrid lays a run's results out in
their shape: breathing while they come, the picture once done, why one
failed with Retry. VariationPicker shows the chosen variation large
over thumbnails and ways on from it. ABCompareView sets two to four
models' answers side by side, blind until a verdict. ImageCompare
points to data_display::BeforeAfter.

primitives::framed and checked_ratio now serve previews and generative
alike. AGENTS.md records that gpui reads a &str picture as a web
address, and that grid rows overgrow tiles that keep a ratio.
2026-09-26 12:35:23 -04:00
ZacharyZhang-NY 406fe4cf0e Reviews: T25a passes
Round 2 of the Claude reviewer found no defects.
2026-09-26 12:26:37 -04:00
ZacharyZhang-NY 8be1e36c53 Forms: Cmd-Enter submits from any control in a form
Form sets the ElyForm key context and binds secondary-enter to Submit
under it, so a focused tile, chip or button submits too. The prompt
enhancer's wash trims anything but letters and digits from its runs,
its spacing sits on its parts so nothing gaps at rest, and its tests
check that the field takes focus back.
2026-09-26 12:24:05 -04:00
ZacharyZhang-NY 59b4673240 T25a Generative: prompt and settings
PromptEnhancer offers a fuller prompt with the words it adds washed,
split at punctuation; Use this takes it into the field, Keep mine
lets it go. StylePresetPicker and AspectRatioPicker are rows of Tab
stops, the chosen one in accent; a ratio chip draws its shape.
SeedInput keeps digits up to u32::MAX and rolls one with a die.
NegativePromptInput and ImageGenerationPanel point to forms::TagInput
and forms::Form. theme.generative() holds the chapter's sizes. The
gallery's preset pictures are the atrium photo in five styles.
2026-09-26 12:13:17 -04:00
ZacharyZhang-NY b651c05b1e Reviews: T24f passes
Round 2 of the Claude reviewer found no defects.
2026-09-26 12:06:06 -04:00
ZacharyZhang-NY 1f1166fef0 Tables: focused headers scroll into view; long tables scroll sideways
A sortable header is a tracked Tab stop; once focused it sets its
table's sideways offset whole from its painted bounds and asks one
frame. The virtualized branch scrolls sideways too, and its list
keeps a plain wheel to its rows. layout::on_axis keeps a scroll box's
wheel to its axes; tables, code blocks, the thumbnail strip and the
gallery's page use it. Footer figures move to tables/foot.rs; the
profiler's ruler takes theme.chart().label. HumanInputRequest's
answered state gets a test.
2026-09-26 12:00:27 -04:00
ZacharyZhang-NY a29cc8bc45 T24f Agent: control and cost
HumanInputRequest asks a question with answers to pick and a line to
write one; CostBreakdown totals a session and splits it by part.
InterruptButton, TokenUsageChart, TraceViewer and EvalResultTable
point to chat::stop_button, a stacked BarChart, TimelineProfiler and
DataTable.

UsageBar shows amounts as its owner writes them. MemoryPanel and
HumanInputRequest share agent::line::send_line; the chapter's bool
callbacks take forms::OnFlag.

DataTable scrolls columns wider than its box sideways, pinned or not,
and leaves a plain wheel to the page; sortable headers are Tab stops.
TimelineProfiler fits its ruler to its lane, its spans are Tab stops,
and a picked span takes accent.
2026-09-26 11:39:24 -04:00
ZacharyZhang-NY 96c4fe01a6 Reviews: T24e passes
Round 2 of the Claude reviewer found no defects.
2026-09-26 11:12:29 -04:00
ZacharyZhang-NY bdb5a1093f Agent: fail loud on cpu past full; group tools in grouped()
SandboxStatus::cpu asserts its share lies in 0..=1 and hands it to
Meter unclamped. ToolRegistry gathers kept tools by source through
grouped(), tested for first-seen order and repeated sources.
2026-09-26 11:09:50 -04:00
ZacharyZhang-NY e864c539b0 T24e: Agent, environment
- SandboxStatus: a dot and a word (a ring while it starts or works), what it
  uses of what it has as meters, and how long it has run; the header wraps
- McpServerList over lists::ListItem: a dot per state, what it runs or why it
  failed, its tools once connected, and a switch
- ToolRegistry: tools grouped by source, found by every word of the owner's
  query, each Allow, Ask or Deny; rows wrap at 280px
- MemoryPanel: a line to keep a memory, Enter or Remember, blank words kept
  out; each memory with its source, when, and Forget
- CheckpointList over data_display::Timeline, newest first, the current point
  marked, Rewind on the rest
- gallery: every control wired to state
2026-09-26 11:00:15 -04:00
ZacharyZhang-NY 85781635ff T24d round 3 fix, after the cap
- BrowserPreview marks the shown frame with accent; the focus color stays
  the focus ring's
- BrowserPreview and ComputerUseViewer take the host's ratio, 16:10 unless
  set, so frames keep their shape and a pointer's share lands on the picture;
  test a_screen_takes_its_pictures_shape
- TASKS: the review moves to a Claude Fable 5.1 max session after codex ran
  out of quota; round 3 fixed after the cap, no fourth review
- AGENTS: focus and choice colors part; host pictures keep their shape
2026-09-26 10:54:39 -04:00
ZacharyZhang-NY c013747b7c T24d round 2 fix
- BrowserPreview keeps a ScrollHandle for its strip and a tracked focus
  handle per thumbnail; the focused thumbnail scrolls into view once, through
  forms::options::reveal and the canvas now shared as revealer
- Popup draws its reveal through the same revealer
- test tab_brings_a_hidden_frame_into_view
2026-09-26 09:16:01 -04:00
ZacharyZhang-NY e99a86bed5 T24d round 1 fix
- BrowserPreview's frame strip scrolls sideways, so every frame stays in
  reach in a narrow card
- ArtifactPanel's header wraps: the title keeps label_width, the controls
  ride in one group that wraps inside the box
- tests: a_narrow_strip_scrolls_to_its_last_frame,
  a_narrow_panel_wraps_its_header
- AGENTS: everything works at 280px
2026-09-26 09:07:14 -04:00
ZacharyZhang-NY 26913df98a T24d: Agent, previews
- BrowserPreview: the address with a lock and a spinner while it loads, the
  host's frame shown, and the frames so far along the foot as Tab stops;
  ScreenshotStream is that strip
- ComputerUseViewer: the host's frame, a ring where the agent points that
  rings again at each new point, and what it does there
- LivePreview: the host's page under its address, with reload
- ArtifactPanel: title, versions through chat::BranchNavigator, Preview and
  Source when it has both, copy and download; it fails loud with neither
- gallery: frames cut from the gallery's own pages; one asset! macro in
  main.rs replaces three copies
2026-09-26 08:59:12 -04:00
ZacharyZhang-NY dbda2fd8c2 Reviews: T24c passes 2026-09-26 08:55:21 -04:00
ZacharyZhang-NY 58a5784124 T24c round 1 fix
- FileChangeCard's header wraps: the path keeps label_width, counts and
  verdict drop below as one group; MultiFileDiffReview's header wraps its
  counts and its all-buttons inside the box, parted by justify_between
- git::DiffViewer's fold rows are Tab stops with a focus ring when the
  owner opens folds; every row keeps a transparent border so heights match;
  an inert fold no longer shows a pointer
- tests: a_narrow_review_wraps_its_rows_inside_its_box,
  git::a_fold_opens_from_the_keyboard; the gallery adds a 280px review
- AGENTS: taffy drops gaps beside an auto margin; wrapping headers
2026-09-26 08:52:38 -04:00
ZacharyZhang-NY 62397cbba1 T24c: Agent, changes
- FileChangeCard: a proposed change to one file, its path and counts,
  Reject and Accept in a slot that keeps presses off the header; opened,
  the diff through a headless git::DiffViewer with its folds opening
- MultiFileDiffReview: every file at once, the total counts, how many are
  reviewed, Reject all and Accept all; verdicts as collab::Decision
- git::DiffViewer gains headless, leaving the file's header to its owner
- CommandExecutionCard is terminal::CommandBlock
- gallery: three proposed files and a command's block
2026-09-26 08:37:45 -04:00
ZacharyZhang-NY 882280cb25 Reviews: T24b passes 2026-09-26 08:34:31 -04:00
ZacharyZhang-NY fab0131168 T24b round 1 fix
- AgentProgress splits its bar only for two steps or more; a task of one
  step draws a plain bar
- test a_task_of_one_step_draws_before_and_after renders 0 of 1 and 1 of 1
2026-09-26 08:31:32 -04:00
ZacharyZhang-NY f4cf1ef3d7 T24b: Agent, progress
- AgentStepList: an agent's steps down a data_display::Timeline rail,
  marked by chat::step_mark, with detail and time
- AgentPlan: items waiting, at work or done, with a count; done items go
  quiet and struck through
- chat::step_mark reads motion::changes: a check or cross that arrives pops
  in, still on first paint
- AgentStatus: a pill for planning, working, waiting, done or failed
- AgentProgress: the step at work, done of total, the time so far and a
  bar split by step; progress past its total fails loud
- SubAgentTree is lists::Tree with each agent's state as its note
- gallery: a plan the button advances, steps, statuses and a tree
2026-09-26 08:25:51 -04:00
ZacharyZhang-NY aaf212c53c Reviews: T24a passes 2026-09-26 08:21:23 -04:00
ZacharyZhang-NY f04bc081eb T24a round 2 fix
- a dialog's body scroll box holds one inner column, so the body keeps
  its height and scrolls instead of shrinking to the capped card
- test a_tall_dialog_body_keeps_its_height_and_scrolls: 2000px stays
  2000px and a 300px wheel moves it 300px
2026-09-26 08:17:18 -04:00
ZacharyZhang-NY a47d0b058b T24a round 1 fix
- overlays::Dialog caps its card at the window, a title bar's height clear
  at each end, and scrolls its body while heading and actions stay; the
  fullscreen body scrolls too; test a_tall_dialog_keeps_its_actions_in_the_window
- ToolCallCard's tool name gives way with an Ellipsis, so time and chevron
  stay in a narrow column; the gallery shows one at 280px
- the Agent script shuts the card it opened, so each theme's shot opens it
- AGENTS: dialogs fit the window
2026-09-26 08:09:46 -04:00
ZacharyZhang-NY ecb2eac4a5 T24a: Agent, tool calls
- ToolCallCard: a step mark, the tool in mono, what it touched and how long
  it took; a press opens its arguments and result as code, an error in red
- ToolCallGroup folds several calls under one line
- ToolApprovalDialog over overlays::Dialog: Deny or Run, one answer; Escape,
  the scrim and the close deny
- PermissionPrompt: deny, always allow or allow once, in place
- chat::StepState gains Failed; chat::step_mark draws a step's mark for
  search steps and tool calls alike
- typography::format::took reads ms, seconds or minutes
- gallery: an Agent page with calls in each state, a group, the dialog and
  the prompt
2026-09-26 08:00:21 -04:00
ZacharyZhang-NY 3d3ceee79d Reviews: T23e passes; T24 splits into six in PRD order 2026-09-26 07:56:18 -04:00
ZacharyZhang-NY b9d73e2b30 T23e: AI chat, welcome
- WelcomeScreen: a greeting for the hour over the owner's composer,
  starters and cards, centered while it fits and scrolled from its top
  when it does not
- SuggestionChips, FollowUpSuggestions and CapabilityCards: each press asks
  the owner; rows and cards are Tab stops
- PromptLibrary is documents::TemplatePicker over saved prompts
- gallery: starters and cards fill the welcome composer, a saved prompt
  fills it too, send clears it with a toast, a follow-up asks its question
2026-09-26 07:50:09 -04:00
ZacharyZhang-NY 5b15a324ae Reviews: T23d passes 2026-09-26 07:46:11 -04:00
ZacharyZhang-NY e46974f055 T23d round 1 fix
- the conversation list reads the zone through format::system_zone, which
  fails loud; the gallery expects the system zone
- Combobox opens with its cursor on its value, MultiSelect on the first
  row ticked; tests for all three lists open r25 of thirty in view
- AGENTS: the zone rule; lists open on their choice
2026-09-26 07:42:45 -04:00
ZacharyZhang-NY 280d49a96c T23d: AI chat, conversations
- ConversationList: pinned first, then by day, newest first; a query keeps
  the titles that hold every word
- ConversationItem: a Tab stop when there is something to do; its menu of
  rename, pin and delete shows on hover, while open, or while the row holds
  focus; a press on the menu leaves the row shut
- new_chat_button, ProjectList, ProjectKnowledgePanel with a capacity meter
- SharedConversationView: who shared it and when, and a way to carry it on
- ConversationExport: format and thinking in a Dialog, Cancel or Export
- icons: square-pen, corner-down-right
- gallery: conversations with rename, pin and delete; projects; a shared
  conversation; export with a toast
2026-09-26 07:32:48 -04:00
ZacharyZhang-NY 6478dc4242 Forms: lists reveal their cursor once laid out
- Select, Combobox and MultiSelect opened a long list at its top, the
  choice out of view: gpui skips a scroll asked before the list's first
  frame. Picker and Suggestions share forms::options::reveal, which Popup
  runs from a canvas inside the list
- test: a_long_list_opens_with_its_choice_in_view
- AGENTS: the scroll rule names the reveal
2026-09-26 07:25:13 -04:00
ZacharyZhang-NY 83e2cdeaa2 Reviews: T23c passes 2026-09-26 07:15:16 -04:00
ZacharyZhang-NY 1a00e19591 T23c round 2 fix
- a trigger opens only its word, so a path keeps its slashes in the query
- suggestions reveal the cursor once the list has its bounds: Popup takes a
  row to reveal, and a canvas inside the list asks for it and one more frame
2026-09-26 07:10:53 -04:00
ZacharyZhang-NY 4fafba4b20 T23c round 1 fix
- suggestions: arrows step past disabled rows and scroll the cursor into view;
  the cursor starts on an enabled row; Enter refuses a disabled one
- active_trigger takes the query's word test: one word for mentions, any
  non-space for the composer, so a file name keeps its dot
- DragDropOverlay takes the drop itself, which gives it the hitbox gpui needs
  to style a drag-over
- ParameterPanel shows the places of the step, value and both ends
- gallery: the prompt menu fills the draft; Reset restores the system prompt
- AGENTS: drag-over styles need a hitbox; debug_bounds never clear
2026-09-26 06:59:37 -04:00
ZacharyZhang-NY b2ce2df428 T23c: AI chat, input
- PromptInput: attachments and context above the field, the owner's tools and
  send below; Enter sends, Shift-Enter breaks the line; / offers commands and
  @ offers context over forms::Suggestions; files dropped on it attach behind a
  drop veil
- AttachmentButton, AttachmentChip with thumbnails and uploads, ContextChips
- VoiceInputButton and VoiceWaveform with a red dot and the time
- ParameterPanel, SystemPromptEditor, TokenCounter, CostEstimator
- forms: a Newline action on Shift-Enter keeps one-line fields to one line;
  replace_trigger moves beside Suggestions; choose and dropped shared
- gallery: composer, pickers and tuning
2026-09-26 06:29:20 -04:00
ZacharyZhang-NY fd9bcf486c Reviews: T23b passes 2026-09-26 06:24:16 -04:00
ZacharyZhang-NY 61085ef7a5 T23b round 2 fix
- the gallery binds its passage's range before the vec, as clippy asks
- AGENTS: anything pressable is a Tab stop; text in a row gives way to fixed parts
2026-09-26 06:22:15 -04:00
ZacharyZhang-NY 4c9695a85f T23b round 1 fixes
- source cards and web results with a handler are Tab stops with focus rings
- the sources row and a passage's header shrink with an ellipsis, keeping the
  count, chevron and score in view
- chat tests move into a folder, citations in their own file
- gallery: a narrow column of citations
2026-09-26 06:16:43 -04:00
ZacharyZhang-NY c3e9f14241 T23b: AI chat, citations
- CitationBadge: a numbered pill after a claim, its source on hover, a press
  opens it; a Tab stop
- SourceCard and SourceList: the sources folded under their count and sites,
  each a card numbered as cited; the fold opens from the keyboard
- SearchProgress: steps waiting, working and done
- WebResultCard and DocumentChunkPreview with matched words washed
- gallery: citation and sources states
2026-09-26 06:07:42 -04:00
ZacharyZhang-NY 8889be4f38 Reviews: T23a passes 2026-09-26 06:02:09 -04:00
ZacharyZhang-NY 32135600f2 T23a round 1 fixes
- your messages shrink within their column and wrap there
- pictures, grids and video take their own shape up to their column's width;
  files, voice notes and link cards stay inside it
- a finished stream shows whole
- avatar pictures load from web addresses too
- the list drops its way down once everything fits again
- the reasoning toggle is a Tab stop with a focus ring
- a link's thumbnail keeps its corners
- gallery: a narrow column of media
2026-09-26 05:56:48 -04:00
ZacharyZhang-NY 6151291443 T23a: AI chat, messages
- messages: yours in a quiet bubble, the assistant's as prose beside its mark,
  system notes, errors; avatars, headers with model and time, footers
- MessageList over gpui's bottom-aligned list: drawn near the view, held at the
  newest, counting arrivals while away; a way back down; day separators
- streaming text and markdown reveal toward the text as it grows, with a caret;
  a blinking cursor before the first words
- CodeBlock with language, copy, run, apply, download and folding;
  MarkdownRenderer::code lets messages draw fences with it
- pictures, grids, files, voice notes, video over host frames, link previews
- quotes, actions, an editor that takes focus, versions, regenerate, stop and
  continue
- errors with retry, a rate limit counting down, feedback, thinking blocks
- gallery: the AI Chat page with scripted states
2026-09-26 05:35:09 -04:00
ZacharyZhang-NY 88a1550c14 Reviews: T22 passes 2026-09-26 05:27:57 -04:00
ZacharyZhang-NY f6bbfe61da T22 round 1 fixes
- a remote cursor shows only while its caret sits inside the field, sideways too
- presence avatars keep a transparent border for the focus ring
- the access list shows each role, as words when it cannot change
- status dots take the theme's size
- peer and member pictures load from files and web addresses alike
2026-09-26 05:22:50 -04:00
ZacharyZhang-NY 153cf95b99 T22: Collaboration
- presence: avatars ringed in each peer's color with where they are, press to
  follow; a breathing live dot; follow mode frames the view and stops on Escape
- remote cursors and selections painted over a field from its own layout
- comment threads with replies, reactions and resolve; a marker that opens a
  thread in a bubble; a sidebar of open and resolved threads
- annotations washed in their authors' colors, notes on hover
- suggestion mode: edits become suggestions word by word; track changes shows
  them struck and underlined, to accept or reject
- sharing: invite by email with checked chips, roles, the access list and who
  the link lets in, in one dialog
- primitives::hold_focus gives a mode its own focus, shared with ZenMode
- AvatarGroup's tuck and count shared; Avatar::ring_color; TagInput::check;
  TextInput::bounds_for_range; Popover::size
- icons: smile-plus, user-plus, message-square-diff
- gallery: the Collaboration page with scripted states
2026-09-26 05:10:37 -04:00
ZacharyZhang-NY 0623855f38 Reviews: T21b and T21c pass 2026-09-26 05:01:35 -04:00
ZacharyZhang-NY 9c9e811534 T21b and T21c round 1 fixes
- a page centers only while it fits the view; a wider one starts in reach
- the reveal runs after the pages' prepaint and keys on the hit itself, so a
  new hit at the same index and a hit current from the first frame come into view
- the chapter list scrolls
- zen takes focus when turned on from outside, so Escape leaves it, and hands
  focus back
- the gallery keeps a page's due date and tags
2026-09-26 04:55:58 -04:00
ZacharyZhang-NY cc82acbe20 T21b and T21c: Documents, reading and knowledge
- DocumentViewer: pages the app drew on a desk, the page under the view's middle
  counted, zoom steps and fit, thumbnails, notes pinned where a press lands, and
  the owner's FindWidget over the pages, hits lit and the current one revealed
- PageThumbnailList, EpubReader (chapters, type size, progress), PrintPreview
- MarkdownRenderer feeds TableOfContents through sections and outline
- Footnote, TooltipTerm, Glossary, ReadingProgress, ZenMode
- Backlinks, Favorites, TrashBin, PageCover, PageIcon, TemplatePicker,
  VersionHistory with PageHistoryDiff
- editor::FindWidget shows the replace fold and option toggles only with handlers
- block editor tests move to documents/blocks
- icons: a-arrow-down, a-arrow-up
- gallery: reading and knowledge sections; scripted find, changes and zen
2026-09-26 04:34:32 -04:00
ZacharyZhang-NY fb993ffceb T21a round 3 fix, after the cap
- TextInput records the selection each edit replaces, where every edit passes;
  the block editor's undo goes back to it, for typing, delete, cut and paste
- TextInput's layout geometry moves to forms/text/geometry.rs
- Reviews: T21a done, fixed after the cap, no fourth review
2026-09-26 04:03:43 -04:00
ZacharyZhang-NY d0c7f98269 T21a round 2 fixes
- a field notes its selection when a key goes down, so undo of typing selects
  what the typing replaced
- reading order skips table cells merged away, so Up and Down across blocks
  land on cells that show
2026-09-26 03:57:27 -04:00
ZacharyZhang-NY 82e227cf60 T21a round 1 fixes
- Enter over a selection drops it and splits at its start, one undo step
- undo and redo keep where the caret was and put it back
- a format unwraps only its own markers, so italic inside bold keeps the bold
- keys into a merged table cell land on the cell that shows
- undo rebuilds a block whose kind needs other fields, so styling comes back
- tests activate their window, as focus events need
- undo bookkeeping moves to blocks/edit.rs
2026-09-26 03:49:27 -04:00
ZacharyZhang-NY 9edd0a8612 T21a: Documents, editing
- markdown read with pulldown-cmark: marks for live styling, blocks, links, words
- forms::Highlight gains weight, slant and strike; measure and paint share runs
- Inter Italic embedded, so emphasis reads slanted
- forms::mention's popup and keys become Suggestions, shared with the editors
- RichTextEditor with FixedFormatToolbar, the floating toolbar, LinkEditor,
  @ and : suggestions, WordCount and ReadingTime
- MarkdownEditor: visual (a pressed block opens as source), source, split
- BlockEditor: prose, lists, to-dos, quotes, callouts, toggles, code, math,
  diagrams the app draws, dividers, tables that grow and merge, pictures to
  resize and align, cards for video and pages, columns, synced text; slash menu,
  markdown shortcuts, handles that drag and open a menu, one undo for all
- TodoItem and Checklist
- icons: heading-1..3, workflow, merge
- gallery: the Documents page with scripted states
2026-09-26 03:32:24 -04:00
ZacharyZhang-NY 05e764304d Reviews: T20 passes 2026-09-26 02:57:34 -04:00
ZacharyZhang-NY 7a5d7e2ea6 T20 round 1 fixes
- the hex demo's selection stops at memory's end
- a disassembly gutter fills its row, so an empty one takes a press
- a breakpoint's box keeps its press from the row
- ruler ticks keep the places their step needs
- the toolbar names each usual key for the platform; the app binds them
- tests: gutter press, box press, tick places
2026-09-26 02:54:23 -04:00
ZacharyZhang-NY 74d6bd1771 T20: Debug
- DebugToolbar, BreakpointList, CallStack with folded library frames, ThreadList
- VariablesPanel and WatchPanel, colored by kind, changes washed
- HexViewer at a base address with the byte under the pointer lit on both sides
- Disassembly with source lines, registers and numbers colored, gutter breakpoints
- Flamegraph that zooms by frame, TimelineProfiler that zooms and pans by wheel
- NetworkInspector with a waterfall, a filter and a request's headers and body
- icons: redo-dot, arrow-down-to-dot, arrow-up-from-dot
- gallery: the Debug page
2026-09-26 02:38:50 -04:00
ZacharyZhang-NY 74dfb5130e Reviews: T19 passes 2026-09-26 02:32:07 -04:00
ZacharyZhang-NY b2c587546e T19 round 2 fixes
- regions read their lines through TextMerge's accessors; conflicts and resolve
  split with similar's tokenize_lines, so a lone return ends a line everywhere
- the merged ending and line breaks know a lone return
- taking both sides separates them only when both hold text
- merge views split rows the same way
2026-09-26 02:28:13 -04:00
ZacharyZhang-NY 3abcd5cc87 T19 round 1 fixes
- merges keep each line's ending and the last newline; resolve keeps every other byte
- the split view numbers each side from its own file
- hunk headers come from similar's UnifiedHunkHeader: an empty side starts before its range
- the commit subject counter sits in the input's suffix
- clipped code rows in diffs, merges, blame and review set their line height
- gallery: scripted split view and a long subject
2026-09-26 02:19:11 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d083e87493 T19 Git: diffs unified and split with word marks and folds, three-way merge and conflict resolver, changes list and commit input, commit graph and list, branch selector, branch, tag and stash lists, blame with annotation, file history, pull request card, review thread, diff stat and status badge
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:59:27 -04:00
ZacharyZhang-NYandClaude Opus 5.5 de1c03746e Reviews: T18 passes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:53:52 -04:00
ZacharyZhang-NYandClaude Opus 5.5 99d9bf45d4 T18 round 2 fixes: a folder the terminal cannot enter stops the start, and links read combining clusters whole
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:50:58 -04:00
ZacharyZhang-NYandClaude Opus 5.5 47f3c7d505 T18 review fixes: a missing folder stops the start, combining marks paint on their cell, the terminal is a Tab stop and keeps its wheel, clear drops find matches; the gallery starts a fresh shell per pick and picks the shell tab before typing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:44:13 -04:00
ZacharyZhang-NYandClaude Opus 5.5 8374b77906 T18 Terminal: a real shell on a pseudo-terminal through alacritty_terminal 0.26, drawn cell by cell in the theme's ANSI colors, with keys, text input, selection, scrollback, links and regex find; replayed output; tabs, splits and toolbar with a shell selector; command blocks and history; AnsiText, LogViewer and ProcessList
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:27:44 -04:00
ZacharyZhang-NYandClaude Opus 5.5 601a29a374 Reviews: T17a–e pass
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:20:19 -04:00
ZacharyZhang-NYandClaude Opus 5.5 30ffe7a0b2 T17 round 2 fixes: merge keeps the newest caret in the lead by where it was given, and a composition cleared to nothing ends
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:16:08 -04:00
ZacharyZhang-NYandClaude Opus 5.5 a42f8e44a8 T17 review fixes: hidden numbers draw, empty edits and read-only commands change nothing, hints stay in a ghost-cut row, the newest cursor leads, a composition undoes in one step, call keys carry their place, references count matches, trailing-space hits and **/ globs, name-only switcher marks, pin and remove stay on their button, settings show their places; decimals moves to typography::format
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:07:37 -04:00
ZacharyZhang-NYandClaude Opus 5.5 d05754ac19 T17 Editor: CodeEditor with gutter, folds, minimap, rulers, guides, brackets, whitespace, cursors, multi-cursor, sticky scroll, inlay hints, lenses, decorations, ghost text, inline chat and diff (T17a); completions, signatures, hover, code actions, rename, references, outline, go to symbol, call and type hierarchy (T17b); find and replace, search panel and filters (T17c); problems, output, debug console, extensions, settings with JSON, keybindings, theme previews, welcome, recent projects, switcher, task runner (T17d); status items (T17e)
Shared: generic undo History, OverflowMenu icon and tooltip, StatusBarItem leading, six Lucide icons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 00:43:41 -04:00
ZacharyZhang-NYandClaude Opus 5.5 c229a096ca Reviews: T16a–e done. After the cap: a lagging synced chart leaves the shared window alone, and a price step keeps its places by magnitude
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 00:20:08 -04:00
1256 changed files with 162562 additions and 5399 deletions

No files matched your search

+59
View File
@@ -0,0 +1,59 @@
name: Bug Report
description: Something draws, behaves or builds wrong.
title: "[Bug Report] "
labels: [bug]
body:
- type: input
id: component
attributes:
label: Component
description: The component's name, as the components page shows it.
placeholder: Dialog
validations:
required: true
- type: dropdown
id: where
attributes:
label: Where
options:
- Native gallery, macOS
- Native gallery, Windows
- Native gallery, Linux
- Live example on elygpui.com
- My own app
validations:
required: true
- type: textarea
id: what
attributes:
label: What happened
description: A screenshot or a short recording helps most.
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
placeholder: |
1. Open the Overlays page
2. Press Open dialog
3. Press Tab twice
validations:
required: true
- type: textarea
id: expected
attributes:
label: What you expected
validations:
required: true
- type: input
id: version
attributes:
label: Commit or version
placeholder: 4f0b372
- type: textarea
id: logs
attributes:
label: Logs
description: Terminal output, or the browser console for the website.
render: shell
@@ -0,0 +1,36 @@
name: Component Request
description: A component Ely lacks, or one that should do more.
title: "[Component Request] "
labels: [enhancement]
body:
- type: input
id: name
attributes:
label: Component
placeholder: ColorWheel
validations:
required: true
- type: textarea
id: use
attributes:
label: What it is for
description: The app and the moment you need it in.
validations:
required: true
- type: textarea
id: behavior
attributes:
label: How it should behave
description: Mouse, keys, states, light and dark.
- type: textarea
id: references
attributes:
label: References
description: Links or screenshots of it elsewhere.
- type: checkboxes
id: searched
attributes:
label: Checked
options:
- label: I searched the components page at elygpui.com and the open issues.
required: true
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Components, live
url: https://elygpui.com/components/
about: Every component runs in the browser; try it before you file.
+11
View File
@@ -0,0 +1,11 @@
name: Other
description: Questions, docs, ideas, anything else.
title: "[Other] "
labels: [question]
body:
- type: textarea
id: what
attributes:
label: What's on your mind
validations:
required: true
+16
View File
@@ -0,0 +1,16 @@
## What and why
<!-- One or two sentences. Link the issue: Closes #123 -->
## Screenshots
<!-- For anything that draws: light and dark, and 280px wide if it holds a row or header. -->
## Checklist
- [ ] `scripts/check.sh` passes
- [ ] The gallery shows it: a story on its chapter's page, and `python3 scripts/stories.py` rerun
- [ ] Keyboard works: Tab reaches it, focus ring shows, Enter and Space press it
- [ ] Reduced motion holds its motion still
- [ ] `AGENTS.md` names any new rule or dependency
- [ ] For `frontend/`: `pnpm test`, `pnpm build` and `pnpm e2e` pass
+53
View File
@@ -0,0 +1,53 @@
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
crate:
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.95"
components: clippy, rustfmt
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
- run: scripts/check.sh
platforms:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.95"
- uses: Swatinem/rust-cache@v2
- run: cargo test --lib --features test-support --quiet
- run: cargo build --example gallery
site:
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
with:
version: 11
- uses: actions/setup-node@v7
with:
node-version: 24
cache: pnpm
cache-dependency-path: frontend/pnpm-lock.yaml
- run: pnpm install --frozen-lockfile
- run: pnpm test
- run: pnpm build
+172 -32
View File
@@ -5,27 +5,40 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
## Stack
- Rust 1.95, edition 2024. One crate: `ely-gpui-component`.
- gpui 0.2.2 from crates.io. Feature `runtime_shaders` is on by default: machines without Xcode lack the Metal compiler.
- Assets, embedded with `rust-embed`: Lucide 1.48.0 icons (ISC), Inter 4.1 and JetBrains Mono 2.304 (OFL).
- gpui and gpui_platform from Zed's repo by git at 1a28cff, the code gpui-pre 0.3.7 republishes. gpui takes `stacker`, which keeps deep layouts off the stack's end; gpui_platform takes `font-kit`, without which macOS draws no text. Feature `runtime_shaders`, on by default, turns on gpui_platform's: machines without Xcode lack the Metal compiler.
- Assets, embedded with `rust-embed`: Lucide 1.48.0 icons (ISC), Inter 4.1 (regular, medium, semibold, italic), JetBrains Mono 2.304 and IBM Plex Sans Regular (OFL). Plex sits unmodified, its name reserved, where gpui's svg renderer loads it: gpui_web answers `.SystemUIFont` with it, and svg text falls back to it on the web.
- Catalog data: `emojis` (Unicode emoji), `isolang` (ISO 639, native names), `isocountry` (ISO 3166), `iso_currency` (ISO 4217, also minor units for money).
- Codes: `qrcode` (QR) and `barcoders` (Code 128, EAN-13), both MIT OR Apache-2.0, default features off.
- Codes: `qrcode` (QR) and `barcoders` (Code 128, EAN-13), both MIT OR Apache-2.0, default features off. `rqrr` 0.11 ((MIT OR Apache-2.0) AND ISC, default features off) reads QR codes from the host's frames.
- macOS extras (tray icon, Dock badge) call AppKit through `cocoa` 0.26 and `objc` 0.2, the crates gpui already links.
- Gallery: `examples/gallery`. Website: `frontend/` (Vite 8, pnpm), built after the components.
- Web views: `wry` 0.57 (Apache-2.0 OR MIT, default features off, macOS only) lays a child WKWebView over the window.
- Terminal: `alacritty_terminal` 0.26 (Apache-2.0, default features off, native only) for the grid, its parser and the pseudo-terminal; `futures` carries its events. `vte` 0.15 (Apache-2.0 OR MIT), the parser it re-exports, reads `AnsiText`'s codes alone, so colored output builds for wasm32.
- Diffs: `similar` 3.2 (Apache-2.0) for line and word diffs and three-way merges; its `unicode` feature splits words at punctuation.
- Markdown: `pulldown-cmark` 0.13 (MIT, default features off) for CommonMark with tables, tasks, strikethrough, footnotes and math.
- Pictures: `image` 0.25 (MIT OR Apache-2.0), the crate gpui decodes with, default features off; it turns decoded frames and rims map tiles.
- Maps: Natural Earth's 110m countries (public domain). `assets/maps/countries.geojson`, trimmed by `scripts/countries.py` to an ISO code, a name and positions to 0.01°, backs `maps::WorldMap`; the gallery's tiles are cut from the same file, z0 to z3, by `scripts/tiles.py` with Pillow.
- Web: on wasm32, `web-time` 1.1 (MIT OR Apache-2.0) gives the `Instant` gpui's executor returns, std's own natively; jiff takes `js`, rust-embed `debug-embed`, similar `wasm32_web_time`. The gallery's web entry takes `gpui_web`, `wasm-bindgen`, `js-sys` and `web-sys` as wasm-only dev-dependencies, and a Hebrew cut of Noto Sans Hebrew 2.003 (OFL, `examples/gallery/fonts`): gpui_web asks the browser for emoji and CJK alone. The `wasm-bindgen` CLI at Cargo.lock's version and binaryen's `wasm-opt` finish the bundle.
- Gallery: `examples/gallery`. Website: `frontend/` (Vite 8.3, pnpm 11, TypeScript, React 19.3, TanStack Router 1.170 in code-based routes, Lenis 1.3), in serro.ai's language: heavy titles over thin faint text, hairlines, one accent, full-screen sections, isometric line miniatures on a canvas. Pages name a story by its title's first part (`src/data.ts`'s `name`); scroll bars stay hidden; layouts step at 1024 and 768. Each story runs live from the gallery built for wasm32 through gpui_web, in a same-origin iframe, or, where a browser cannot run it, as a native capture. The Docs tab is Markdown in `frontend/src/docs/`, read in its `ORDER` and rendered by `marked` 18 (MIT) inside `vite.config.ts`'s `docs` plugin, so a bad guide fails `pnpm build` and the browser gets HTML alone; a fenced block tagged `example=NAME` shows `examples/docs/NAME.rs`, an example `scripts/check.sh` compiles as `docs_NAME`, so every full Rust sample on the site builds. The Showcase tab lists apps built with Ely from the public repo `ZacharyZhang-NY/Ely-GPUI-Showcases`: manifests, PNG previews and Markdown READMEs, checked by its `scripts/catalog.mjs` (`marked`, `pngjs`, `twitter-text`), its stars refreshed by its CI. The mark is the letter E in iso blocks, its middle arm in the accent: one geometry in `src/iso/mark.ts`, drawn by the engine the miniatures use, worn by the header in the page's iso colors and by the favicon. Below 768px the header shows the mark without its word, which stays the home link's name; below 414px it drops the sound switch. Geist and Geist Mono (OFL, `@fontsource-variable`), Reicon icons (MIT, based on Solar Icons, CC BY 4.0, credited in the footer), simple-icons' GitHub mark (CC0). It deploys to Cloudflare Workers static assets through `npx wrangler` (4.143).
## Commands
- `cargo run --example gallery` opens the gallery. `-- --page <slug>` starts on a page.
- `cargo run --example gallery` opens the gallery. `-- --page <slug>` starts on a page; `--story <title or slug>` with it draws that section alone, and `--capture` then shoots it without the page's script.
- `cargo run --example gallery -- --capture <dir>` writes PNGs of every page, light and dark, top to bottom, then each page's scripted states, including windows the demos open. macOS only.
- `cargo test --lib --features test-support`, `cargo clippy --all-targets --features test-support -- -D warnings`, `cargo fmt --check`. `scripts/check.sh` runs them with the house rules.
- `rm -rf target/debug/incremental` after each task item keeps the disk lean.
- `cargo run --example gallery -- --narrow 280 --capture <dir>` lays each page, header and body, at that width in a padded card and shoots it without its script, whose aims are measured at full width. Demos that set their own width run past the card.
- `RUSTC_BOOTSTRAP=1 cargo check --lib --target wasm32-unknown-unknown` checks the web build; gpui_web's `wasm_thread` needs the bootstrap, as Zed's web examples do.
- `ELY_GALLERY_ASSETS=<address of out/assets/> scripts/web.sh <out>` builds the gallery for the browser, profile `web`, and fails past Cloudflare's 25 MiB. It opens at `?page=<slug>&story=<title or slug>&theme=light|dark`; the host page calls `gallery.setTheme(mode)` or posts `{ ely: "theme", theme }`, before the wasm runs or after, and the page posts `{ ely: "ready" }` to its parent once it runs.
- In `frontend/`: `ELY_GALLERY_ASSETS=<site>/gallery/assets/ pnpm gallery` builds the gallery into `public/gallery`, `pnpm dev` serves the site, `pnpm build` checks types and writes `dist`, `pnpm deploy` runs `wrangler deploy`, `pnpm showcase` writes the showcase catalog into `src/showcase.json` and `public/showcase` (both ignored): it downloads the commit `showcase.lock` pins, installs that commit's packages and runs its own validator, so a bad catalog fails the build; `dev` and `build` run it first, and `ELY_SHOWCASE=<checkout>` reads a local catalog instead. `pnpm test` runs the iso engine's, the mark's and the showcase list's tests, and `pnpm local` builds the gallery for localhost, builds the site and serves both on http://localhost:4173/ (wasm-bindgen and wasm-opt on PATH, as `scripts/web.sh` says). `node scripts/og.mjs`, with `pnpm preview` up, renders `public/og.png` from the hero and the header's mark. `node scripts/favicon.mjs` writes `public/favicon.svg`, which follows the system's mode, `public/apple-touch-icon.png` and the repo's `logo.png` (2048px, transparent) from the mark, in the colors `styles.css` sets. The site reads `examples/gallery/stories.json` for its chapters, stories and search.
- `python3 scripts/stories.py` writes `examples/gallery/stories.json`, every page's sections in drawing order with their components, for the site: the names in a title, and in its `SHOWN` table what a story draws beyond them. It fails on a section opened inside another and on a ticked entry with a home of its own that no story names. `scripts/check.sh` refuses a stale one.
- `cargo test --lib --features test-support`, `cargo clippy --all-targets --features test-support -- -D warnings`, `cargo fmt --check`. `scripts/check.sh` runs them with the house rules and lints the gallery for wasm32.
- `rm -rf target/debug/incremental` and `cargo clean -p ely-gpui-component` after each task item keep the disk lean. Cargo keeps every old build of the crate and its tests under a new hash; by T25 they had filled the disk.
- `scripts/icons.sh <lucide-name>...` adds icons. Then add a line to `IconName` in `src/primitives/icon.rs`.
- `python3 scripts/countries.py <ne_110m_admin_0_countries.geojson>` writes the bundled world; `python3 scripts/tiles.py <same file>` writes the gallery's map tiles.
## Layout
- `src/<chapter>/`: one folder per chapter of `gpui-components.md`.
- `src/theme`: palettes, tokens, `ActiveTheme`. `src/motion`: durations, easings, spring.
- `examples/gallery/pages/<chapter>.rs`: one page per chapter, in PRD order.
- `TASKS.md` and `tasks/`: progress. Every PRD entry has a line and a tag.
- `TASKS.md` and `tasks/`: progress. Every PRD entry has a line and a tag; `tasks/reviews.md` keeps every review round.
## Rules
@@ -35,18 +48,27 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- One component, one home. Duplicates point to it.
- Fail fast. No silent fallbacks. Log state changes with `log`.
- Files stay at or under 500 lines. Comments are one short line, and rare.
- A repeating animation (spinner, breath, sweep) holds still under reduced motion instead of shortening, or it would spin at 1 ms a turn. An effect set off by a change (flash, shake) reads elapsed time rather than restarting an animation id, so what it holds keeps its state.
- `motion::duration()` honors reduced motion. Springs overshoot: use them inside animators, never as gpui easings, because gpui asserts eased values stay in `0..=1`.
- A repeating animation (spinner, breath, sweep) holds still under reduced motion instead of shortening, or it would spin at 1 ms a turn. An effect set off by a change (flash, shake, a bar that fades at rest) reads elapsed time through `motion::since_change` rather than restarting an animation id, so what it holds keeps its state: an animation's id passes to every descendant, and a new one rebuilds their focus, drags and hover.
- `motion::duration()` honors reduced motion. Springs overshoot. gpui hands an easing's value to the whole animator unclamped, so `motion::spring` goes inside the animator, on only what may overshoot (`forms::Switch` springs its thumb's travel, not its colors).
- `svg()` paints only with its own `text_color`. `Icon` always sets one.
- Buttons stay out of focus on click (`prevent_default` on mouse down). Tab still reaches them.
- The focus ring is a 1px focus-colored border (`FocusRing`). gpui paints shadows under the element, so a shadow ring floods transparent elements. Focusable elements keep a 1px border, transparent at rest.
- `ely_gpui_component::init` binds Tab and Shift-Tab to `FocusNext` and `FocusPrev`. Wrap the root in `FocusScope` with `.root()` and focus its handle. A root scope takes focus when the focused element leaves the tree, as a toast's or an alert's button does.
- gpui's `Window::dispatch_event` returns a private type, so the gallery scripts mouse input by posting `NSEvent`s to its own queue.
- The gallery scripts mouse input by posting `NSEvent`s to its own queue, so a press takes AppKit's path and reaches a native view over the window (`misc::WebView`). `Window::dispatch_event` reaches only gpui's listeners. gpui's normal window takes mouse moves from a tracking area, which only a real cursor feeds, so the harness sets the window's `acceptsMouseMovedEvents` for posted moves.
- gpui starts a drag on the first move past its threshold, and only the moves after it reach `on_drag_move`. gpui 0.2.2's mac window replayed a held drag every 16 ms; gpui_macos replays only while `pressedMouseButtons` shows a real button, which posted events never set. So `Step::DragTo` posts each move twice, as the replay did: the second post of the first step reaches `on_drag_move`, where a component that anchors on its first drag move sets its anchor (Scrollbar, Knob, Split).
- Every shot of a gpui window, a page's or a script's, waits three of its frames through `on_next_frame`: gpui presents without waiting on the GPU, and a heavy page's draw outlasts a fixed pause, so a shot could show the frame before the last step. A page's pauses stay, for work off the main thread such as pictures decoding. A covered window draws no frames, so its capture fails after 5 s instead of shooting a stale frame.
- `hover()` needs an element id.
- gpui takes hover at layout only on an element with an id, from a state its mouse moves keep, so `.id(..).hover(..)` recolors text. Without an id, `hover` applies at paint, after the text took its color, and a mouse move alone redraws nothing. After a key press paint drops the hover and layout keeps it until the mouse moves, so a hover that sets text and background shows only the text. What shows on hover rests `invisible()` and turns `visible()` through `group_hover` (`chat::ConversationItem`, `messaging::ChatMessage`).
- Whatever draws over the page goes through `primitives::raise`: at the top it defers; inside something raised it lays out on its own and draws last there, so a list opened in a dialog lies over the dialog. gpui takes a deferred draw inside another but paints them all by priority alone, so a tooltip (0) deferred inside a fullscreen dialog (1) would draw under it. A raise takes its owner's id and draws under it, so its keyed state and focus hold while other raises come and go. `scripts/check.sh` refuses gpui's `deferred` anywhere else.
- gpui hides hover after a key press until the mouse moves. A hover that holds or shows something (the toast stack, a carousel, tooltips, hover cards, charts, scroll activity) sets `HoverListenerMode::InputModalityIndependent`, so typing under a resting pointer keeps it. A hover preview the keyboard also moves keeps the default on its items; the box that clears it on leave listens independently, so a real exit still reports, and ignores a leave that follows a key through `last_input_was_keyboard` (`forms::Calendar`).
- `occlude()` blocks the pointer for everything painted before it, its own ancestors too. Put it on the outermost box that should stop the pointer, as the toast stack does, or the ancestors' hover never fires.
- gpui sends a drag's moves to every listener of its type. Each drag payload carries its owner's `EntityId`, and handlers check it.
- A component that takes a starting value from its owner keeps it in `layout::seeded`: a new value from the owner replaces local drags.
- The capture harness turns on reduced motion, so shots are still: repeating motion rests on one frame, and a shimmer shows no band.
- A drag that pans, turns or slides goes through gpui's drag, whose moves reach the owner in or out of its box; `on_mouse_move` stops at the box's edge. gpui sends a drag's moves to every listener of its type, so each payload carries its owner's `EntityId` and handlers check it.
- gpui paints pictures without a transform, so a picture turns by its pixels: `media::turn` turns decoded frames by quarter turns off the main thread, an asset keyed by the picture and the turn. gpui's loader turns a photo upright by its EXIF orientation first, so `media::ImageViewer` turns from the upright frame and `media::ImageUpload` measures that frame. An svg turns to any angle through its `Transformation`, so a control that turns shows one (`interaction::Rotatable`).
- A box that keeps a cursor over its items rings the item the cursor is on, not the box, as SelectableList marks its row (`interaction::SelectionArea`).
- Boxes that scroll together go through `interaction::ScrollSync`: the pane a wheel turned leads, and a canvas laid before the panes copies its place in prepaint, before they draw, so no frame shows them apart.
- A component that takes a starting value from its owner keeps it in `layout::seeded`: a new value from the owner replaces local drags. A field that reports its words as they change keeps what it sent, so the owner's echo leaves the caret alone (`mail::SignatureEditor`). A cursor inside a seeded view stays while it lies in what is shown, so the owner's echo of a turn leaves it (`calendar::CalendarMonthView`, `YearView`).
- The capture harness turns on reduced motion, so shots are still: repeating motion rests on one frame, and a shimmer shows no band. gpui's mac window stops its display link while occluded, so a capture advances a moving picture only on the draws the harness forces.
- Each capture pass starts its page fresh (`Gallery::fresh`): page state lives under the pass, so the dark pass runs its script from the same start as the light one.
- The capture harness finds gpui windows by handle through `raw-window-handle`. AppKit popups, which gpui does not own, come from the window list by process and level.
- Posted events cannot move a macOS window, so window drags are not scripted. `drag_region` follows Zed's title bar.
- An overlay takes focus with `primitives::take_focus` and returns it with `give_back` on every close path. While open, it takes focus back whenever the focused element leaves the tree (gpui's `on_focus_lost`).
@@ -57,56 +79,161 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- `TextInput::bounds_for` reads the last layout. Text changed this frame clamps to the laid-out end until the next paint.
- The harness types with `dispatch_keystroke` through `AnyWindowHandle`, which leaves the root view free to redraw.
- The macOS open panel runs out of process too. Escape does not reach it; the harness sends it `cancel:`.
- gpui's `test-support` swaps its executor: `block` fails on a real dispatcher. It stays behind this crate's `test-support` feature, never on for the gallery.
- gpui's `test-support` stays behind this crate's `test-support` feature and is never on for the gallery: it tracks every entity handle for leaks and records every `debug_selector`'s bounds each frame.
- A tracked `FocusHandle` is a Tab stop only when built with `.tab_stop(true)`. `tab_index` on the div reaches only handles gpui makes itself. `primitives::tab_stop` keeps one per id.
- Floating lists go below their anchor, or above when only above has room (`forms::options::float`). gpui's own switch keeps the anchor point, so a flipped list would cover its trigger.
- A trigger that opens a list goes through `forms::select::listing`: toggle, keys, blur and popup live there, for Select, TabOverflowMenu and Breadcrumb alike.
- Floating lists go below their anchor, or above when only above has room (`forms::options::float`). gpui's own switch keeps the anchor point, so a flipped list would cover its trigger. An option list is at least its trigger's width and at most the window's less `window_margin` each side, so gpui can snap it inside and its rows end in an ellipsis; wider, gpui pins it left and the rest runs off.
- A trigger that opens a list goes through `forms::select::listing`: toggle, keys, blur and popup live there, for Select, TabOverflowMenu and Breadcrumb alike. A field that suggests as you type floats its rows through `forms::select::Picker` and `forms::options::Popup`, as Combobox and TagInput do.
- A palette goes through `navigation::palette::Palette`: query field, grouped rows, cursor, keys and focus live there. CommandPalette, QuickOpen, QuickSwitcher, SearchPalette and QuickLauncher only build rows.
- A menu goes through `menus::menu`: rows, submenus, keys, focus and presses outside live there, for DropdownMenu, OverflowMenu, SplitButton, ContextMenu, SearchableMenu and MenuBar. A press on the host's own box is the host's to handle, so a trigger toggles and a right click moves a context menu without a close and reopen in one event.
- gpui focuses the nearest focusable ancestor on mouse down, and apps wrap everything in a `FocusScope`. A press that must leave focus alone calls `prevent_default`, as buttons and context regions do.
- gpui runs the click of every pressed element under the pointer, a row's with its button's. A pressable row that holds buttons stops their press at their box on mouse down (`files::transfer`), as `overlays::media_button` stops its own.
- gpui draws a window's first frame inside `open_window`. State the first frame reads, such as `cx.set_menus`, is set before it opens.
- gpui clicks a focused element when Enter or Space is released. An overlay that hands focus back picks on release too; a pick on press returns focus first, and the release clicks the opener again.
- A dialog goes through `overlays::Dialog`: scrim, focus trap, Escape and focus return live there. AlertDialog, ConfirmDialog and PromptDialog build on it; a fullscreen dialog fills the window below its title bar.
- gpui clicks a focused element when Enter or Space is released, and only if the press reached it and focus stayed there. An overlay that hands focus back picks on release too. An edit in place that hands focus back keeps its text on Enter's release (`forms::InlineEdit`). The press itself bubbles, so a container that takes Space leaves a focused button its own: `overlays::media_button` stops it, and a button in a `SelectableList` row stops Space and Enter at its box (`mail::MailItem`'s star, `messaging::PinnedMessages`' Unpin). A key context that binds a key its children use acts only while its own handle is focused and propagates otherwise, or a focused child never arms (`editor::CodeEditor`'s Newline).
- A focus handle a component hands back lives across all its modes: take it with `tab_stop` before any early return, or keyed state drops it while the other mode draws and the handed-back focus lands nowhere. A thread's message keeps one handle folded and open, a Tab stop only while folded (`mail::MailThreadView`).
- A button whose words and action change with a mode keeps one handle through `Button::focus_handle`, and the component moves focus to it or back to its field as the mode turns (`project::TimeTracker`).
- A button that waits on the owner shows it with `loading`, which takes no press and stays a Tab stop, so focus holds through the wait; `disabled` is for what is not ready, and a disabled button is no stop (`onboarding::ContactSupport`).
- A control names its role, label and state on the element that takes focus, for gpui's AccessKit tree (a slider's thumb, a select's trigger). gpui sets no disabled flag, so a disabled control says `state.unavailable` in its description. A thumb bounded by another reports the bounds it can reach. The test platform builds no tree; a live window read through UI Automation, or `Window::debug_a11y_tree_json` while a client is connected, proves it.
- A help mark, ?, is `primitives::HelpTooltip`; FormLabel's help shows it.
- A panel that opens from something other than a button goes through `overlays::Popover::with_opener`, which hands the opener its toggle; focus, placement and every way out stay in the popover (`calendar::EventPopover`'s chip). Its content gets a close that hands focus back: a press that ends the panel's work closes it before the owner acts, or a panel whose host leaves the tree strands focus (`onboarding::Hotspot`'s Got it, EventPopover's Edit and Delete).
- A flow in steps goes through `navigation::Wizard`: Back, Next, Finish and the step's motion live there. Next and Finish share one focus handle, and focus moves to it when Back reaches the first step or a finished step is picked. `onboarding::OnboardingWizard` leaves out its numbered steps (`headless`) and shows a count over a bar in as many parts.
- A dialog goes through `overlays::Dialog`: scrim, focus trap, Escape and focus return live there. Its close runs on every way out, so a dialog with two answers marks the chosen one before it closes (`messaging::IncomingCallDialog`). AlertDialog, ConfirmDialog and PromptDialog build on it; a fullscreen dialog fills the window below its title bar. A dialog's card fits the window, a title bar's height clear at each end; its body scrolls and its actions stay.
- Controls on a dark layer over pictures are `overlays::media_button`, light icons that dim when they have nowhere to go, for the lightbox and a video player's bar alike.
- A component's letter keys step aside while Command or Control is held, so the app's shortcuts reach the app (`media::ImageAnnotator`, `VideoPlayer`, `VideoThumbnailStrip`).
- A tip over a track splits the free width around it by the pointer's share: it stays inside the track and under the pointer, and nothing is measured (`media::scrubber::time_tip`, for the scrubber and the waveform).
- `primitives::Severity` names info, success, warning and danger with their colors and icons, for alerts and feedback alike.
- Rows that move are keyed. `motion::Flip` places them absolutely from last frame's heights, so no frame shows a row at its new place before it glides there, and drives the glide from elapsed time: an animation id that changes per move would rebuild the row's keyed state. `Reorder` holds a row by key, drags with an empty ghost and moves the held row itself.
- A marker that slides between items (segment thumb, tab line) measures them with `motion::slide` and eases with `glide`.
- A picker hands focus to its popup on open and back to its trigger on pick or Escape; it closes once focus leaves both (`forms::date::picker`).
- A picker hands focus to its popup on open and back to its trigger on pick or Escape; it closes once focus leaves both (`forms::date::picker`). `messaging::StatusSetter`'s emoji field reuses it.
- Motion for a value change keys its animation on `motion::changes`, so it replays per change and stays still on first paint. Key only the part that moves: a keyed ancestor gives every descendant a new id, and focused buttons lose focus.
- A spotlight or tour takes its target's box from `primitives::Measure`; the lit box glides between targets.
- Thumbs stay inside their component's box: the track is padded by half a thumb (`Slider`, `ColorPicker`, `GradientEditor`).
- Thumbs stay inside their component's box: the track is padded by half a thumb (`Slider`, `ColorPicker`, `GradientEditor`, `media::ImageCropper`).
- gpui's `Hsla::opacity` scales alpha; `alpha` sets it.
- A rounded box does not clip its children. Each layer inside takes the radius itself; `checker` takes one.
- A window keeps one `feedback::Toaster` and renders one `ToastViewport`; anything holding the entity pushes to it. Messages share `primitives::Severity`.
- Only gpui can build `ExternalPaths` with paths, so drop rules live in `forms::files::dropped`, where tests reach them.
- Drop rules live in `forms::files::dropped`. `taken` adds file kinds by extension, for drops and the dialog alike (`DropZone::kinds`).
- What a dropped file leads to is drawn by a function of what was read (`settings::importer::read_row`). gpui's test platform builds `ExternalPaths` with paths and answers a file dialog through `simulate_path_prompt_response`.
- A stroke drawn with the pointer goes through `forms::pen`: a press starts it, a drag extends it, and the lift hands it over, in or out of the pad (`SignaturePad`, `generative::InpaintCanvas`, `calendar::TimeGrid`'s drag on empty time).
- gpui runs key bindings before key listeners. A container takes a child's bound key through the action with `capture_action`, as `Form` takes `Submit`.
- `ScrollHandle::scroll_to_item` runs in the container's prepaint before its bounds are stored, so a first-frame call misfires. Call it from a child's prepaint once the handle has bounds, then request one frame (`navigation::editor_tabs`). Never poll layout with `request_animation_frame`.
- gpui animations run on the wall clock; timers run on the executor's clock, which tests advance by hand. A test that needs an animation settled turns on the theme's reduced motion. Clocks read `background_executor().now()` and wake only their own view.
- `ScrollHandle::scroll_to_item` runs in the container's prepaint against last frame's bounds and overflow, so a call before a list's first frame does nothing. Call it from a child's prepaint once the handle has bounds, then request one frame (`navigation::editor_tabs`). A floating list reveals its cursor, and a strip its focused item, through `forms::options::reveal` and `revealer`. A focused Tab stop deeper in a scroll box sets the box's offset whole from its own painted bounds in prepaint, once per focus, through `layout::bring_into_view` along the box's axis; the canvas measures the stop with its ring (`messaging::GifPicker`). `layout::reveal_when_focused` is that canvas; it lies over a box that holds the stop's ring (`buttons::SegmentedControl`, `project::KanbanBoard`). Never poll layout with `request_animation_frame`.
- gpui animations run on the wall clock, except `Animation::repeat_synced` loops, which read the executor's clock; timers run on the executor's clock, which tests advance by hand. A test that needs an animation settled turns on the theme's reduced motion. Clocks read `background_executor().now()` and wake only their own view.
- Machine-read marks, QR codes and barcodes, paint `ink` on `paper`: dark on light in both themes, so cameras read them.
- A layer over pressable content that must still pass scrolls to its ancestors uses `block_mouse_except_scroll`; `occlude` hides them from scrolls too.
- Trackpad momentum arrives as `TouchPhase::Moved` after `Ended`, so a gesture acts only between `Started` and `Ended` (`lists::SwipeableListItem`).
- Quartz scroll events carry no window. The harness hands them to the window itself, placed in window points (`Step::Swipe`).
- A chevron that opens and closes is `primitives::Disclosure`.
- `uniform_list` does not stretch its rows; each row sets `w_full`.
- `uniform_list` does not stretch its rows; each row sets `w_full`, or `min_w_full` when the same row also sits in a flex column, where it stretches (`tables::DataTable`). gpui's `list` lays rows at their content's width too; `chat::MessageList` boxes each row at the list's width.
- Chapters import only earlier chapters, so a select of tree nodes lives in `lists` (`TreeSelect`) and reaches `forms::listing`.
- Script clicks aim at a probe's corner, so a scripted target must fit in the window; a tall probe puts its top off screen.
- Script clicks aim at a probe's corner, so a scripted target must fit in the window; a tall probe puts its top off screen. The log names each probe's bounds as the script aims, and a probe around content with no width of its own measures smaller than what it draws, so a scripted demo sets its width.
- `scripts/check.sh` fails by its exit status; a pipe into `tail` hides it.
- Reduced motion shortens animations to 1ms on the wall clock. Tests sleep past it between refreshes to see where one ends.
- The test platform never runs next-frame callbacks, and its text metrics are simplified: each character takes a fixed advance. Tests refresh the window in place of the display link, and click where layout does not hang on text, such as padding and slots.
- The test platform runs next-frame callbacks only when a test calls `Window::simulate_next_frame`. Its text metrics are simplified: each character takes a fixed advance, unless the test builds its context with `TestAppContext::build_with_text_system`. Tests refresh the window in place of the display link, and click where layout does not hang on text, such as padding and slots.
- An absolute element with no insets sits where it would flow: after the content in a block, inside the padding in a flex box. A canvas that measures its parent pins itself with `.top_0().left_0()`.
- Keyed state lives while its element renders in consecutive frames. An overlay rendered only while open starts fresh each time.
- Long grids scroll in a `uniform_list`. gpui has no nearest scroll, so a key move up scrolls with `Top` and down with `Bottom` (`forms::glyphs`).
- Long grids scroll in a `uniform_list`; a key move up scrolls with `Top` and down with `Bottom` (`forms::picks`). A searchable grid of picks, emoji, icons or stickers, goes through `forms::picks`'s `Grid`; `uniform_list` gives every row one height, so a grid of large cells shows one group at a time instead of titles (`messaging::StickerPicker`).
- Masks reshape edits inside `TextInput::set_fit`, which sees the replaced range and the typed text. A diff after the fact cannot tell typed characters from kept ones.
- Ids inside a reusable component carry its owner's id or `EntityId`, animation ids too. Twin ids share focus, click and animation state.
- Ids inside a reusable component carry its owner's id or `EntityId`, animation ids too. Twin ids share focus, click and animation state. A child keyed by its place inherits the state of what stood there before; key it by what it shows (`files::FileExplorer` keys a column by its folder).
- Text wraps at the width its box had when measured. A cross-axis `max_w` comes too late, and a flex column counts wrapped text as one line. Put prose in a plain block inside a flex row with `flex_1().max_w(..)` (`feedback::states`).
- gpui 0.2.2 keeps a no-wrap line's first measure, taken at full width inside flex and scroll boxes, so `.truncate()` clips there instead of ending in an ellipsis. A line that may overflow is a `typography::Ellipsis`.
- A line that may overflow is a `typography::Ellipsis`, which shapes its cut at the width its box painted with.
- gpui's text answers a min-content probe with the width it last shaped at, often one line, so a set width around prose does not give way in a narrow flex row; a host that lets such a column shrink gives it `min_w_0` (the gallery's stacked DescriptionList).
- Taffy 0.13 no longer floors a flex item's least width at its row's padding and border, and a cached content measure hides a `flex_1` item's own width, so a text field's least width held no room to type. `forms::Input`'s text slot keeps an empty strut under the text, its padding and transparent hairlines at no height, so the field's least width holds the frame's inset.
- gpui's `line_clamp` lays the rest of the text on its last line and cuts it hard; centered, it loses both ends. A name whose ends matter is a `typography::MiddleEllipsis` on one line (`files::FileGrid`).
- Set an explicit line height on any box that clips text. gpui's default leading is taller than a tight box, and the clip eats descenders.
- gpui's `img` takes its picture's pixel size for any `Auto` side, and its aspect ratio beats percent heights in flow. `Image` pins it absolute at full size, so an `Image` needs a sized box.
- Badges, tags and avatars live in `data_display`; forms and shell draw theirs from there, and `data_display` imports neither. Avatars take square pictures.
- `img()` keeps loading state only with an id. Content masks are rectangles, so rounded corners survive only when the image fills its box without cropping.
- A file on its way is a `files::Transfer` drawn by one row in `files::transfer`: TransferQueue and DownloadManager share it, and its actions follow its state.
- Paths with `/` between folders become a tree through `lists::Folder::of`, for `FileTree` and `files::ArchiveViewer` alike.
- A folder's path over its entries is `lists::path_bar`, and its first order is `lists::listed`, folders first by name: DirectoryListing and FileExplorer share both, so every view shows one path and one order.
- A file's icon comes from `primitives::file_icon`, one map from name to icon for every chapter, under the owner's `primitives::IconTheme`, whose rules by name and extension come first; `files::FileIcon` draws it. What fits across a width comes from `layout::columns_for`, and `layout::fit` adds the inset that centers it.
- Badges, tags and avatars live in `data_display`; forms and shell draw theirs from there, and `data_display` imports neither. A hue worn in a row, a label's or an event's, is `data_display::color_mark`, and `Palette::hue` gives the chart color by index. A tag is never wider than its container; a long label ends in an ellipsis beside its remove mark.
- A picture drawn in code reaches gpui as a `RenderImage`. A host that replaces one frees the old through `App::drop_image`, or every frame stays in the sprite atlas (the gallery's model).
- Pixels a host draws at a box's size go through `rendering::RenderSurface`: it asks for one BGRA frame at the box's device pixels in prepaint, draws again only for a new size or revision, and frees the old frame and, on leave, the last one. The revision covers all the frame reads, theme colors too.
- Tests type a shortcut as it is bound: a `secondary-` binding is Command on macOS and Control elsewhere, so its test types `secondary-`; `cmd-` is the platform key on every OS and reaches only handlers that read that modifier (`lists::SelectableList`). A Tab count runs through `forms::EyeDropper` only on macOS, where it is enabled.
- `img()` keeps loading state, and moves an animated picture's frames, only with an id; the frames hold still while its window is inactive or gpui's own `reduce_motion` is on, which the theme's reduced motion does not set. Content masks are rectangles, but `img()` draws only the part of its picture inside its box and rounds that part with its own radius, so a `Cover` crop keeps its corners.
- gpui reads a `&str` picture source with a scheme as a web address, and any other as an embedded asset. A file goes through `Path` (`documents::source` in the crate, `ui::picture` in the gallery).
- Taffy sizes a flex column by the height a ratio box has at its set width, before its cap, so chat's `shaped` pictures sit in a block inside such a column (`chat::ImageMessage`, `VideoMessage`, `ImageGrid`).
- Taffy 0.13 collapses a child's vertical margin through a block parent with no padding or border on that side, and its size cache keeps only that block's size, so a box that measures it again leaves the margin out. A component moves a box with a relative offset, or with padding where what follows moves too, not with such a margin: `layout::Drawer` pulls and snaps its panel, and a fullscreen `overlays::Dialog` slides its card, with `top`; `motion::Stagger`, `Transition`, a centered Dialog and the palette's card rise with padding, so the box a scrim centers grows with them; the gallery's inspector sample is its probe's own child. A margin that stops at its parent is safe: a flex or grid item's own, or a child's of a flex item or of a box with padding or a border on that side. A flex wrapper stops the collapse but gives its items a definite height, so percent heights inside start to resolve (the Drawer's `max_h(relative(0.85))`).
- Tiles in a picture's shape sit in flex rows of `flex_1` cells (`generative::GenerationGrid`). A cell's padding counts in its flex base, so a row of fewer cells gives each more width; cells that match across rows keep their inset on a box inside (`messaging::VideoCallGrid`).
- Rise reads green and fall red unless a component's `red_up` swaps them, as markets in East Asia read. `finance::quotes::moves` picks the pair.
- Charts lay out in `f32` pixels of their own box, tested without gpui, and paint through `charts::paint::at`: a canvas's paint gets window coordinates, and `with_element_offset` works only in prepaint. Their sizes come from `theme.chart()`.
- A label pinned to a point sits in a zero-size absolute box that centers it with flex (`charts::axes`), so no offset guesses at text size.
- Tokens live in `src/theme`; the raw `px()` scan skips that folder only.
- An owner's palette, edited or imported, reaches the theme per mode through `Theme::set_palette`, and `Theme::palette` names the one the shown mode fades to; high contrast strengthens Ely's own palettes alone. A color goes by its field's name through `token` and `token_mut`, on `Palette` and `Syntax` alike. Ely's palettes pass `tooling::A11yChecker`'s audit in both modes, with high contrast and without; a test holds them there.
- `ScrollHandle` keeps its children's bounds unscrolled: a child paints at its bounds plus the offset. A press on a scrolled child subtracts both, and an offset that brings a child to a place is set whole, not added (`documents::find::reveal`).
- A find box is `editor::FindWidget`, over code, a terminal or a document's pages. It shows the replace fold and the option toggles only when the owner handles them.
- Taffy hands negative free space to cross-axis auto margins and to centering alike, and gpui has no safe alignment, so a centered child wider than its scroll box starts past the scroll's reach. Center only what fits the measured box (`documents::DocumentViewer`).
- A mode that leaves on Escape holds its own focus through `primitives::hold_focus`: turned on while focus is elsewhere, it takes focus so Escape reaches it, and `hand_back` returns it (`documents::ZenMode`, `collab::FollowMode`).
- A chat holds at its newest message through gpui's `list` with `ListAlignment::Bottom`: it sticks while scrolled to the end, and `scroll_to` one past the last item sticks it again; that call sends no scroll event, so the owner clears its own away state (`chat::MessageList`).
- Anything pressable is a Tab stop: a row, card or toggle takes `tab_index(0)` and `focus_ring`, with a 1px border that stays transparent where it shows none; gpui presses it on the release of Enter or Space.
- A list of pressable rows goes through `lists::SelectableList`, whose cursor reaches them by key: Up and Down move, Enter or a double press activates (`media::Playlist`). Enter activates on its release, armed by its own press, so an activation that moves focus leaves the release nothing to press (`onboarding::HelpPanel`). Typing a row's first letters moves the cursor there, the letters joining within a pause on the executor's clock; a letter held with Command, Control or Alt stays the app's. A bare `ListItem` press is the pointer's only. Titled sections of such lists go through `lists::Sections`, for ChannelList, MemberList and MailboxList; each owner builds its rows and keeps its own checks.
- Columns that must line up across rows sit absolutely at fractions of their row, as the bars over them do (`calendar::CalendarMonthView`): with real text, gpui lets a `flex_1` cell's text widen it, and the test platform's fixed advances hide it.
- A row that holds text beside fixed parts lets the text give way: the text takes `flex_1().min_w_0()` and an `Ellipsis`; counts, icons, dates and chevrons take `flex_none`. A label that must stay whole wraps in that block instead, as `forms::Checkbox`'s does.
- gpui reports no text baselines, so under `items_baseline` a leaf aligns by its bottom. A block takes its first in-flow child's baseline, so a box led by a rolling column or a padded badge aligns oddly: the digit column stays a block, and a row whose value may be a badge aligns `items_end` (`typography::AnimatedNumber`, `data_display::DescriptionList`).
- gpui snaps every authored length to device pixels before layout, each on its own, so a stack moved by a multiple of a row keeps the row a whole number of device pixels, or the error grows with the multiple (`typography::AnimatedNumber`).
- A mark over a text field reads the field at paint time, from a canvas laid over it (`collab::RemoteCursor`): by then `TextInput::bounds_for` holds this frame's layout, and a still page never renders again to catch up.
- A callback field holds a named `type` alias; clippy's type_complexity rejects `Option<Rc<dyn Fn(..)>>`. A chapter shares one alias per shape.
- A component that fills its container takes no width on its root: a block or a flex column stretches it, in a flex row the host gives it `flex_1`, and as a window's root it fills the window, since gpui sets a window root's auto sides to the window's size. A percent width ignores the root's own margins, so they push it past its container, and in a flex row it takes the whole row as its basis and leaves a `flex_1().min_w_0()` neighbor nothing. A root centered under a cap keeps `w_full`, since a flex column stretches no child with an auto side margin, and `mx_auto` needs a width to fill (`layout::Container`). So does a box that keeps a ratio: a block follows its ratio from a stretch, but a flex or grid box only from a set width, and callers may make one flex (`layout::AspectRatio`, and `primitives::framed` under VideoPlayer, CameraPreview, ModelViewer, ParticipantTile and InpaintCanvas). `Button::full_width` is its host asking, since a button takes no style. Each chapter's test holds its filling roots at the full width of a flex column in a padded block through `layout::tests::narrow_width`, which reads a root by its `debug_selector`.
- A field's `Highlight` sets color, wash, weight, slant and strike per span; measuring and painting share one set of runs, so wrapped heights match.
- A block editor keeps one undo for the document: text and structure together, as snapshots through `forms::History`; it takes Undo and Redo before its fields do.
- gpui calls focus listeners only while the window is active. A test that needs focus events activates its window with `window.activate_window()`.
- A terminal takes Tab and Shift-Tab through bindings under `ElyTerminal`, ahead of focus moves; other keys go through `terminal::keys` in a key listener, and typed text through its input handler.
- A shell in the gallery starts without startup files and with a fixed prompt, so captures show no user or host.
- gpui has no `(ElementId, usize)` id. A child id names its index: `(self.id.clone(), format!("row-{ix}"))`.
- Clippy's `single_range_in_vec_init` rejects `[a..b]` and `vec![a..b]`; bind the range first.
- A component built on `lists::Tree` fills its box; the host gives it a height. A fixed-width column beside a `flex_1` one takes `flex_none`, or it shrinks.
- Editor rows read their text through `CodeEditor::row_text`: a multi-line ghost cuts its line, and the rest follows the ghost's last line.
- gpui clears `debug_bounds` each frame: a test sees an element leave as well as appear.
- Time reads `web_time::Instant`: std's compiles for wasm32 and panics in the browser. `scripts/check.sh` refuses std's in `src` and the gallery.
- What cannot build for wasm32 is absent there through `cfg(not(target_family = "wasm"))`, never faked: the live terminal, `Terminal` and `Launch` with their grid, keys and search, and the gallery's capture harness.
- What a browser cannot run draws natively alone: `ui::native_only` leaves a window's opener out on the web, and the web's menu bar keeps View alone. The site shows native captures instead, light and dark: a story whose demo needs the desktop (files, the terminal's shell, the tray, the Dock, window moves) as its capture, and a live story that opens windows with those windows below. `python3 scripts/shots.py` takes them with the native gallery, window in front, into `frontend/public/shots` and `frontend/src/shots.json`.
- A story is a gallery section: its title is a literal, one per page. Under `ui::Story` the one asked for draws without its title; the rest drop their demos (`ui::Section`), so nothing of theirs lays out, loads or moves. Their builders still run, so a demo that starts work on its own, a clock, a load or a shell, starts it through `ui::live`, which a story grants its own section alone and fails on a title no section has. A story the page lacks fails in the page.
- Embedded in a page, the web gallery keeps gpui_web's hidden text field from taking focus, and hands focus back to the host, until a press or a key reaches it (`examples/gallery/index.html`); gpui_web focuses that field whenever its window is active. It retries a failed start twice, since frames that fetch the wasm at once can fail one, then posts `{ ely: "failed", error }`. The host shows its own state until `{ ely: "ready" }` or that failure, sends the mode on ready and on every turn, and puts a skip link before the frame, since Tab inside gpui cycles within it.
- The gallery's pictures go through `asset!` and `ui::picture` or `ui::resource`: a file natively, on the web an address under `ELY_GALLERY_ASSETS`, since gpui reads no files there.
- On the web a paste reaches the focused field as the browser's paste event, through `EntityInputHandler::paste`, so Paste stays unbound there (`TextInput`, `CodeEditor`). Code that reads the clipboard itself awaits `read_from_clipboard_async`: gpui_web's `read_from_clipboard` answers at once and finds nothing (`tables::DataGrid`'s Cmd-V).
- The first frame wears the host's mode through `Theme::set_mode_now`, a mode sent before the window opens included; `set_mode` fades from the colors on screen.
- The system time zone comes from `typography::format::system_zone`, which fails loud; jiff's `TimeZone::system()` falls back to UTC in silence.
- A Select with nothing to pick stays shut: disabled, it shows its placeholder; open, it fails (`media::DeviceSelector` says no device was found).
- A list opens with its cursor on the current choice: Select on the chosen row, Combobox on its value, MultiSelect on the first one ticked, SelectableList on its first selected row. A selection the owner makes, not the echo of the list's own pick, moves SelectableList's cursor there.
- An event repeats as RFC 5545 has it: weeks count from the start's own week, and a month or a year without the start's date is passed over (`calendar::Recurrence`).
- A search step, a tool call or an agent's step says where it stands with `chat::StepState` and draws it with `chat::step_mark`.
- A row that parts left and right groups its sides and uses `justify_between`. An auto margin along a line keeps the line's gaps, but taffy also hands the space it took to the line's justify. A line with an auto margin keeps the default justify and runs forward (`forms::MultiSelect`'s chevron); centered, ended, spaced, or reversed as `i18n::Direction::row` is under a right-to-left locale, its items run out of its box.
- A header that holds a name beside counts and actions wraps: `flex_wrap`, the name `flex_1` with `min_w(label_width)`, the rest one `flex_none` group that drops below.
- Every header, row and strip works at 280px: a name keeps its minimum width, the rest wraps below or scrolls, and nothing draws past its box.
- gpui takes a scroll box's reach from its direct children's bounds, and a block child stretches to the box. Content that scrolls sideways sets its least width on that child (`tables::body::least`).
- gpui turns a wheel along the other axis onto a box that scrolls one way, and the page around it scrolls too. A box inside a scrolling page keeps its wheel to its axis with `layout::on_axis`, and so does the page.
- The focus color belongs to the focus ring. A chosen or shown item takes accent, as `forms::picks` and `lists::ListItem` do.
- A box that shows a host's picture takes the picture's shape from the host (`ratio`), so nothing is cropped and a point given as a share lands on the picture. A focusable one keeps its focus border on an outer box, so the picture keeps that shape exactly (`media::ImageCropper`).
- A default only the component can work out, such as a crop fitted to a picture it decodes, reaches the owner once through `window.defer` after render (`media::ImageUpload`).
- A ScrubInput shows its number in about 140px beside a short label. In a narrower column a pair hides it, so a long label takes a row of its own (`canvas::TypographyPanel`).
- Design panels edit a whole value and hand the owner a changed copy through `canvas::panel::editing`, which takes the owner's handler itself: a component without one fails at render. A swatch that opens a picker is `canvas::panel::color_well`.
- A press that stops propagation also stops the ancestors' focus on press. A layer that takes keys focuses itself in its own press handler (`canvas::ToolLayer`, `canvas::MindMap`), and a field over it stops its own presses.
- A button inside a pressable row goes through `lists::row_action`, which keeps its press and its Space or Enter from the row (`devtools::ConnectionManager`).
- A field opened over a component hands focus back to that component on Enter and Escape, through `forms::Editing`'s `returning` and `give_back` (`canvas::writing`); a blur keeps the words and leaves focus where it went.
- A focus ring on a box whose children measure themselves goes on an overlay child: a border insets the children and shifts what they measure (`canvas::ToolLayer`).
- A menu of times goes through `mail::times`: each row names its day and hour, a moment comes once (tomorrow is next Monday on a Sunday), and "Pick a time…" opens one dialog that waits for a time ahead of now (`ScheduleSend`, `SnoozePicker`).
- A list whose cursor a field drives marks the cursor only while the field has focus; at rest its first row would look hovered (`mail::LabelPicker`). Its rows are option rows, and `forms::marked_row` leads one with a mark such as a label's hue.
- A choice that names a person leads with their initials through `forms::Choice::avatar`, in the list and in a Select's trigger alike (`project::AssigneePicker`).
- `typography::Ellipsis` shapes its line with the text style's own run, so a strike or an underline set on its box shows (`lists::ListItem::struck`, a closed task's title).
- Focus that must follow an item to another parent is taken by the owner and keyed by the item: a handle gpui makes for an element dies with its path (`project::KanbanBoard`, whose cards keep focus across columns).
- The theme's `platform`, the system's to start, decides the window buttons Ely draws, the title bar's layout and key caps, `secondary` included (`typography::keys::keystroke`). A title bar whose buttons the OS draws follows the OS.
- A window whose sidebar runs to its top is `shell::ColumnShell`: each column's header drags the window; the first leaves the traffic lights room, and drawn controls lead the first header in the macOS manner or end the last. A set width yields before a header's own parts; the host's header items keep one control's width and scroll when short.
- Taffy counts a flex item's text in its least width whatever width the item sets. A box that must add none lays its content absolutely inside a filling box (`shell::ColumnShell`'s title and body).
- A centered title sits between a start and an end that grow alike from a zero basis, their padding on a box inside them, since taffy counts padding in the flex base; their content is their least width, so the title gives way before either end (`shell::TitleBar`).
- A pane the window's blur shows through is `shell::Vibrancy`, filled with the palette's `glass`, in a window opened `WindowBackgroundAppearance::Blurred`: vibrancy on macOS, acrylic on Windows. gpui's `MicaBackdrop` and `MicaAltBackdrop` are DWM backdrops from Windows 11 build 22621; older builds get none, in silence, and macOS plain transparency.
- The chart's hues go by `theme::HUE_NAMES`, for an event's color and a label's alike.
- Text whose every character counts, encoded data and keys, goes through `typography::literal`: JetBrains Mono draws `==` and `->` as ligatures (`devtools::Encoder`).
- Messages go through `i18n::I18n`, a global the app sets: `cx.global::<I18n>().text(key, args)` fills each `{name}`. Every catalog holds the first one's keys; a missing key, argument or catalog fails. A right-to-left locale mirrors a stack with `Direction::row` and sets its text with `Direction::align`; CoreText orders mixed Hebrew and Latin runs on macOS, but gpui sets no paragraph direction, so a line takes its first strong character's: `text` leads a right-to-left message with U+200F, so its words suit labels, not a field's value, and `Locale::number` a right-to-left minus with U+200E, as CLDR does. gpui wraps shaped glyphs in the order they are drawn, on the web as on macOS, so a right-to-left line that wraps comes out garbled: right-to-left text stays on one line, an `Ellipsis`, which cuts by characters and sits by its text's alignment.
- gpui orders Tab stops by `tab_index` before paint order, and registers none for an `invisible()` element. A stop that must come first where it sits, yet draw over what paints after it, stays in flow at zero size and raises what it shows (`i18n::SkipLink`): a lower `tab_index` would sort it before the root scope's own handle, so Tab from the root would pass it.
- Color-blind safe charts swap Ely's chart hues for a set fitted to stay apart under Machado's protanopia, deuteranopia and tritanopia; a test holds every pair 0.10 apart in Oklab and each hue 3:1 on its page (`Theme::set_color_blind_safe`, in Ely's own palettes alone).
- gpui packs pictures in its atlas without padding and samples them with a linear filter, on the web as on macOS, so a scaled picture's edge takes color from its atlas neighbor, and pictures laid edge to edge show seams. A map tile carries a one-pixel rim copied from its own edge and draws one of its pixels larger each side, clipped to its box (`maps::tile`).
- A wall clock reads `Timestamp::now` and wakes its view at each whole second through a keyed task, only while the owner passes no moment; a stopwatch reads the executor's clock, so tests advance it (`misc::Clock`, `misc::Stopwatch`).
- A number shown to so many significant digits goes through `typography::format::significant`, which rounds through Rust's exponent form, so no binary noise shows at any size (`misc::Calculator`, `misc::UnitConverter`).
- A component that reads the host's frames reads one at a time off the main thread and passes over frames that come meanwhile; when a read ends it redraws, so the newest frame is read next (`misc::QrCodeScanner`).
- Embedded, the web gallery ignores its own `focus()` calls until a press or key reaches it, so a frame that starts never takes the host page's focus; the site keeps a focused frame mounted (`frontend/src/pages/Home.tsx`'s `Near`).
- A native view laid over the window takes AppKit's first responder on a press, and gpui's view never asks for it back, so typed text would go to the native view for good. The component hands the keyboard back on every press gpui gets and when the view leaves (`misc::WebView`, through wry's `focus_parent`); `Step::NativeKey` proves it, since gpui's own dispatch skips AppKit.
- A map keeps places in `f64`: at zoom 19 the world is 134 million pixels across. Its wheel pans and Command or Control with the wheel zooms about the pointer, as the canvas's does. Its tiles stay in gpui's asset cache, as every `img()` picture does. Its popup is an overlay that is not modal: it takes focus when it shows, and the frame it stops showing, by any way out, hands focus back. Its layers, lines and areas unwrap across the date line and repeat on every world in view. Clustered pins gather by squares of the world, not of the view, so a pan never regroups them; a zoom may, so it hands a focused pin's or cluster's focus to the map.
- gpui's inspector (debug builds) runs no mouse listener while picking and holds a pick on a press without a redraw, so Ely's panel requests a frame each render while picking (`tooling::install_inspector`). Its panel scrolls, so the box model it draws over the page goes through `primitives::raise`, which paints unclipped. The panel is a second root, out of the app's scope, so it owns Tab through a `FocusScope` of its own.
- A tool that measures redraws cannot move: any animation redraws the view it counts (`tooling::RenderCounter`). `AnyView::cached` renders again only when it or a view inside it notifies, its bounds, clip or text style change, or the window refreshes; gpui refreshes the window whenever input turns between the keyboard and the pointer. A meter that counts frames keeps its view drawing while shown (`tooling::FpsMeter`).
- A logger of input lays its canvas before what it holds, so its capture listeners run first and see what a child stops. Key downs come through `App::intercept_keystrokes`, which runs before bindings, and a key context on its box tells it focus is inside (`tooling::EventLogger`). It also hears a lone modifier's release as a keystroke, which the logger skips.
## Decisions
@@ -119,3 +246,16 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
- 2026-09-25: an entry built from a later chapter's parts lands with that chapter; its line points there. MenuBar goes to Menus, QuickLauncher to Navigation.
- 2026-09-25: SystemNotification is blocked. An unbundled app has no notification center (probed on macOS 27.2), and gpui 0.2.2 has no API.
- 2026-09-25: `unexpected_cfgs` declares `feature = "cargo-clippy"`, which the objc 0.2 macros test.
- 2026-09-26: Terminal proven with `alacritty_terminal` 0.26: its grid, parser and pseudo-terminal, drawn by Ely. A replayed grid serves recorded output and tests.
- 2026-09-27: ThemeSwitcher is `settings::ThemeSelector`: `src/theme` sits under every chapter and cannot draw with buttons.
- 2026-09-28: QRCodeScanner proven with `rqrr` 0.11: the host hands in frames as `RenderImage`s, read off the main thread; gpui has no camera.
- 2026-09-28: WebView proven with `wry` 0.57: a child WKWebView over the window, placed from a canvas in prepaint at its box each frame and hidden once the box leaves the view. It draws above everything gpui paints and gpui's clipping does not reach it; gpui's test windows have no native handle, so the capture proves it.
- 2026-09-28: InspectorOverlay is gpui's inspector drawn by Ely, `tooling::install_inspector`, in debug builds only, as gpui has it.
- 2026-09-28: every text pair the contrast audit holds meets 4.5:1: `fg_subtle` is #696764 light and #94928f dark, `syntax.comment` with it, and the light syntax's number, constant and attribute are #a3591b, #aa5529 and #9f5d12; the contrast audit found them under.
- 2026-09-28: the site shows components live, as gpui-kit.com does, not as screenshots. gpui 0.2.2 cannot build for the browser: Zed added gpui_web on 2026-02-26 and has published no gpui since 0.2.2. So gpui moves to Zed's repo by git at 1a28cff (T44b), the code gpui-pre 0.3.7 republishes; the source stays first-party, and this replaces the 2026-09-24 choice. The terminal stays native: alacritty_terminal's polling does not build for wasm32.
- 2026-09-28: gpui builds an AccessKit tree on macOS, Windows and Linux, not the web, from elements with an id and a `role`, so the accessibility entries turn from `blocked` to `prove`. It still has no tray or badge API, so TrayIcon and DockBadge call AppKit.
- 2026-09-28: SystemNotification is `prove`: gpui's `show_system_notification` posts only from an app bundle; unbundled, as `cargo run` starts the gallery, it logs and drops the call, and the web posts nothing.
- 2026-09-29: the ported debug gallery draws its heaviest page about 1.6 times slower than on gpui 0.2.2 (tooling: 9 fps, worst frame 117 ms, against 14 fps and 72 ms), with gpui's accessibility tree on or off (`Application::new_inaccessible` measured the same). Ely's code did not change there; the frame cost is gpui's.
- 2026-09-29: the web gallery runs on one thread, since a static site sends no cross-origin isolation, and draws through WebGL2 where WebGPU is missing. Its pictures are served beside the wasm, not embedded: the wasm stays under Cloudflare's 25 MiB, and a story fetches only its own. `wasm-opt -Oz` takes the wasm from 23.0 to 20.0 MB; gzip and brotli stay near 7.9 and 5.5 MB.
- 2026-10-04: the Showcase follows gpui-kit.com/apps: apps live in their own public repo and arrive by pull request. The site pins a commit of it in `frontend/showcase.lock` and moves only when someone bumps the pin, so stars refresh on the site with each bump. READMEs take Markdown and PNG pictures alone, no HTML or video, so the site needs no sanitizer. The catalog started empty.
- 2026-10-04: chapter 44, Rendering, holds `RenderSurface` alone. gpui at 1a28cff paints `RenderImage`s everywhere, `CVPixelBuffer`s (NV12) on macOS's Metal renderer alone, and shares no device or shader hook, so shader, camera and GPU views are the host's, feeding the surface, and zero-copy or Metal, OpenGL, Vulkan and Direct3D surfaces are blocked (`tasks/ch44.md`). CI tests the crate and builds the gallery on Linux, Windows and macOS, arm64 and x86_64.
+49
View File
@@ -0,0 +1,49 @@
# Contributing
Thanks for helping. Bugs, component requests and pull requests are all welcome.
## Before you start
- Try the component live at https://elygpui.com/components/ first; it may already do what you need.
- Open an issue from a template (Bug Report, Component Request or Other) before a large change, so we agree on the shape first.
## Setup
- Rust 1.95 with `rustup target add wasm32-unknown-unknown`. No full Xcode needed: gpui compiles its shaders at runtime.
- For the website: Node 24 and pnpm 11.
- Development happens on macOS; the capture tool needs it.
```sh
cargo run --example gallery # every chapter, light and dark
cargo run --example gallery -- --page forms
```
## Where things go
- A component lives in `src/<chapter>/`, one folder per chapter. One component, one home; anything that duplicates another points to it.
- Its demo is a story on the chapter's page, `examples/gallery/pages/<chapter>.rs`. After adding or renaming a story, run `python3 scripts/stories.py`.
- `TASKS.md` and `tasks/` track every component; tick its line when it lands.
- The website is `frontend/`; its README section has the commands.
## House rules
`AGENTS.md` holds them all. The ones reviews catch most:
- Colors, sizes, radii and shadows come from `cx.theme()`; no raw `px()` in components. Spacing uses gpui's rem helpers.
- Every component works in light and dark, at 280px wide, by keyboard (Tab reaches it, a 1px focus ring shows, Enter and Space press it), and holds still under reduced motion.
- Fail fast and log state changes; no silent fallbacks.
- Files stay at or under 500 lines. Comments are one short line, and rare.
- A new rule or dependency gets a line in `AGENTS.md`.
## Checks
```sh
scripts/check.sh # fmt, clippy, tests, the web lint, house rules
cd frontend && pnpm test && pnpm build && pnpm e2e # when you touch the website
```
CI runs the same on every pull request. Fill in the pull request template: what and why, light and dark screenshots for anything that draws.
## License
Ely is MIT or Apache-2.0, at your option. Unless you say otherwise, what you contribute is licensed the same way.
Generated
+2286 -1827
View File
File diff suppressed because it is too large. Load diff
+73 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "ely-gpui-component"
version = "0.1.0"
version = "0.1.1"
edition = "2024"
rust-version = "1.95"
license = "MIT OR Apache-2.0"
@@ -10,36 +10,62 @@ publish = false
[features]
default = ["runtime_shaders"]
runtime_shaders = ["gpui/runtime_shaders"]
runtime_shaders = ["gpui_platform/runtime_shaders"]
test-support = ["gpui/test-support"]
[dependencies]
anyhow = "1"
barcoders = { version = "2", default-features = false, features = ["std"] }
emojis = "0.9"
gpui = "0.2.2"
futures = "0.3"
gpui = { git = "https://github.com/zed-industries/zed", rev = "1a28cff4b409169bac058bca40dfbfeb7621d19b", default-features = false, features = ["stacker"] }
gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "1a28cff4b409169bac058bca40dfbfeb7621d19b", features = ["font-kit"] }
image = { version = "0.25", default-features = false }
iso_currency = { version = "0.7", features = ["iterator"] }
isocountry = "0.3"
isolang = { version = "2.4", features = ["local_names", "list_languages"] }
jiff = "0.2"
log = "0.4"
pulldown-cmark = { version = "0.13", default-features = false }
qrcode = { version = "0.14", default-features = false }
regex = "1"
rqrr = { version = "0.11", default-features = false }
rust-embed = "8.12"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_json = { version = "1", features = ["preserve_order"] }
similar = { version = "3.2", default-features = false, features = ["std", "text", "inline", "unicode"] }
smallvec = "1"
unicode-segmentation = "1"
vte = { version = "0.15", default-features = false, features = ["std", "ansi"] }
web-time = "1.1"
[target.'cfg(not(target_family = "wasm"))'.dependencies]
alacritty_terminal = { version = "0.26", default-features = false }
[target.'cfg(target_family = "wasm")'.dependencies]
jiff = { version = "0.2", features = ["js"] }
rust-embed = { version = "8.12", features = ["debug-embed"] }
similar = { version = "3.2", default-features = false, features = ["wasm32_web_time"] }
[target.'cfg(target_os = "macos")'.dependencies]
block = "0.1"
cocoa = "0.26"
objc = "0.2"
raw-window-handle = "0.6"
wry = { version = "0.57", default-features = false }
[target.'cfg(unix)'.dependencies]
rustix = { version = "1", default-features = false, features = ["std", "fs"] }
[dev-dependencies]
env_logger = "0.11"
[target.'cfg(target_family = "wasm")'.dev-dependencies]
gpui_web = { git = "https://github.com/zed-industries/zed", rev = "1a28cff4b409169bac058bca40dfbfeb7621d19b" }
js-sys = "0.3"
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["Location", "UrlSearchParams", "Window"] }
[target.'cfg(target_os = "macos")'.dev-dependencies]
core-foundation = "0.10"
core-graphics = "0.24"
@@ -51,6 +77,49 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("cargo-cli
[lints.clippy]
all = { level = "warn", priority = -1 }
# The web gallery: small over fast, one unit so LTO sees everything.
[profile.web]
inherits = "release"
opt-level = "z"
lto = true
codegen-units = 1
[[example]]
name = "gallery"
path = "examples/gallery/main.rs"
[[example]]
name = "docs_hello"
path = "examples/docs/hello.rs"
[[example]]
name = "docs_start"
path = "examples/docs/start.rs"
[[example]]
name = "docs_theme"
path = "examples/docs/theme.rs"
[[example]]
name = "docs_focus"
path = "examples/docs/focus.rs"
[[example]]
name = "docs_motion"
path = "examples/docs/motion.rs"
[[example]]
name = "docs_icons"
path = "examples/docs/icons.rs"
[[example]]
name = "docs_overlays"
path = "examples/docs/overlays.rs"
[[example]]
name = "docs_i18n"
path = "examples/docs/i18n.rs"
[[example]]
name = "docs_render"
path = "examples/docs/render.rs"
+26 -7
View File
@@ -3,22 +3,25 @@
A component library for [GPUI](https://www.gpui.rs), the Rust UI framework behind Zed.
Every component ships in light and dark. The palette is warm and quiet. Color carries meaning, not decoration.
Status: early. `TASKS.md` tracks every component, chapter by chapter.
Every component runs live at https://elygpui.com, compiled to WebAssembly.
Status: early. `TASKS.md` tracks every component, chapter by chapter. To help, see `CONTRIBUTING.md`.
## Use
```toml
[dependencies]
ely-gpui-component = { git = "https://github.com/ZacharyZhang-NY/Ely-GPUI-Components" }
gpui = "0.2.2"
gpui = { git = "https://github.com/zed-industries/zed", rev = "1a28cff4b409169bac058bca40dfbfeb7621d19b" }
gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "1a28cff4b409169bac058bca40dfbfeb7621d19b", features = ["font-kit"] }
```
```rust
use ely_gpui_component::{Assets, theme::{Mode, Theme}};
use gpui::{App, Application};
use gpui::App;
fn main() {
Application::new().with_assets(Assets).run(|cx: &mut App| {
gpui_platform::application().with_assets(Assets).run(|cx: &mut App| {
ely_gpui_component::init(cx);
Theme::set_mode(Mode::Dark, cx);
});
@@ -33,14 +36,30 @@ fn main() {
cargo run --example gallery
cargo run --example gallery -- --page buttons
cargo run --example gallery -- --capture shots # macOS: PNG of every page, light and dark
ELY_GALLERY_ASSETS=https://example.com/gallery/assets/ scripts/web.sh dist # for the browser
```
In a browser, `?page=buttons&story=icon-button&theme=dark` draws one section alone; `examples/gallery/stories.json` lists them all.
## Website
```sh
cd frontend
pnpm install
ELY_GALLERY_ASSETS=https://<site>/gallery/assets/ pnpm gallery # the wasm gallery, into public/gallery
pnpm dev # or: pnpm build && pnpm preview; both fetch the showcase at showcase.lock's commit
ELY_SHOWCASE=../../Ely-GPUI-Showcases pnpm dev # the showcase from a local checkout
node scripts/og.mjs # with pnpm preview up: public/og.png from the hero
node scripts/favicon.mjs # public/favicon.svg, apple-touch-icon.png and ../logo.png from the mark
pnpm deploy # Cloudflare, through wrangler
```
## Build notes
- gpui 0.2.2 compiles its Metal shaders at runtime here (`runtime_shaders`, on by default), so a full Xcode install is not required.
- Tested on macOS only. The capture tool needs macOS.
- gpui compiles its Metal shaders at runtime here (`runtime_shaders`, on by default), so a full Xcode install is not required. gpui_platform needs `font-kit`, or macOS draws no text.
- CI tests the crate and builds the gallery on macOS, Windows and Linux, arm64 and x86_64. The capture tool needs macOS.
## License
MIT or Apache-2.0, at your option. Lucide icons: ISC. Inter and JetBrains Mono: SIL Open Font License 1.1.
MIT or Apache-2.0, at your option. Lucide icons: ISC. Inter, JetBrains Mono, IBM Plex Sans and the gallery's Noto Sans Hebrew: SIL Open Font License 1.1.
The gallery photos in `examples/gallery/assets` were generated for this project with gpt-image-2.
+137 -156
View File
@@ -1,17 +1,17 @@
# Tasks
Progress truth. Work runs top to bottom. No item is skipped.
Each `T` item ends with a codex review (`gpt-6-astra`, effort `max`, herdr split pane). Three rounds at most.
Each `T` item ends with a review in a herdr split pane, three rounds at most: codex (`gpt-6-astra`, effort `max`) through T24d round 2; from T24d round 3, after codex ran out of quota on 2026-09-26, a Claude session on Fable 5.1 at max effort, at the user's word; from T44a round 2, codex again, as the user asked; from T45d round 2, Claude Code on Fable 5.1 at high effort, at the user's word; from T50a, codex `gpt-6.1-sol` at `max`, at the user's word.
Per-component lines live in `tasks/`. Tags there:
| Tag | Meaning |
|-----|---------|
| `→ path` | Alias or duplicate. One home, at `path`. |
| `native` | gpui 0.2.2 ships it. The gallery shows the native call. |
| `native` | gpui ships it. The gallery shows the native call. |
| `host` | Ely draws the UI. The host app supplies the engine data. |
| `prove` | Needs a crate or platform API not yet proven with gpui 0.2.2. |
| `blocked` | Cannot be real on gpui 0.2.2. Reason given. |
| `prove` | Needs a crate or platform API not yet proven with gpui. |
| `blocked` | Cannot be real on gpui. Reason given. |
| `(Txx)` | Built early in task `Txx` as a dependency. |
## Items
@@ -55,53 +55,138 @@ Per-component lines live in `tasks/`. Tags there:
- [x] T15a Charts · Cartesian
- [x] T15b Charts · Parts and spreads
- [x] T15c Charts · Flows and relations
- [ ] T16a Finance · Market charts
- [ ] T16b Finance · Technical analysis
- [ ] T16c Finance · Quotes & book
- [ ] T16d Finance · Trading
- [ ] T16e Finance · Markets & assets
- [ ] T17a Editor · Core
- [ ] T17b Editor · Intelligence
- [ ] T17c Editor · Search
- [ ] T17d Editor · Panels
- [ ] T17e Editor · Status items
- [ ] T18 Terminal
- [ ] T19 Git
- [ ] T20 Debug
- [ ] T21a Documents · Editing — `tasks/ch21-30.md`
- [ ] T21b Documents · Reading
- [ ] T21c Documents · Knowledge
- [ ] T22 Collaboration
- [ ] T23a AI Chat · Messages
- [ ] T23b AI Chat · Citations
- [ ] T23c AI Chat · Input
- [ ] T23d AI Chat · Conversations
- [ ] T23e AI Chat · Welcome
- [ ] T24 Agent
- [ ] T25 Generative
- [ ] T26 Media
- [ ] T27 Files
- [ ] T28 Messaging
- [ ] T29 Mail
- [ ] T30 Calendar
- [ ] T31 Project — `tasks/ch31-43.md`
- [ ] T32 Canvas & Design
- [ ] T33 DB & Dev Tools
- [ ] T34 Dashboard
- [ ] T35 Settings
- [ ] T36 Account
- [ ] T37 Onboarding & Help
- [ ] T38 Interaction
- [ ] T39 Theme
- [ ] T40 i18n & a11y
- [ ] T41 Maps
- [ ] T42 Misc
- [ ] T43 Library Tooling
- [ ] T44 Capture: every component, light and dark, plus motion clips
- [ ] T45 Website: `frontend/`, Vite 8, pnpm, light and dark, motion
- [ ] T46 E2E: Playwright against the built site
- [ ] T47 Ship: GitHub repo (public), CI, Pages deploy
- [ ] T48 Acceptance: live URL and MVP checklist
- [x] T16a Finance · Market charts
- [x] T16b Finance · Technical analysis
- [x] T16c Finance · Quotes & book
- [x] T16d Finance · Trading
- [x] T16e Finance · Markets & assets
- [x] T17a Editor · Core
- [x] T17b Editor · Intelligence
- [x] T17c Editor · Search
- [x] T17d Editor · Panels
- [x] T17e Editor · Status items
- [x] T18 Terminal
- [x] T19 Git
- [x] T20 Debug
- [x] T21a Documents · Editing — `tasks/ch21-30.md`
- [x] T21b Documents · Reading
- [x] T21c Documents · Knowledge
- [x] T22 Collaboration
- [x] T23a AI Chat · Messages
- [x] T23b AI Chat · Citations
- [x] T23c AI Chat · Input
- [x] T23d AI Chat · Conversations
- [x] T23e AI Chat · Welcome
- [x] T24a Agent · Tool calls
- [x] T24b Agent · Progress
- [x] T24c Agent · Changes
- [x] T24d Agent · Previews
- [x] T24e Agent · Environment
- [x] T24f Agent · Control and cost
- [x] T25a Generative · Prompt and settings
- [x] T25b Generative · Queue and results
- [x] T25c Generative · Canvas
- [x] T25d Generative · Sound and motion
- [x] T25e Generative · Models
- [x] T25f Generative · Data and prompts
- [x] T26a Media · Images
- [x] T26b Media · Crop and annotate
- [x] T26c Media · Video
- [x] T26d Media · Audio and controls
- [x] T26e Media · Capture and devices
- [x] T27a Files · Items
- [x] T27b Files · Explorer
- [x] T27c Files · Find and look
- [x] T27d Files · Transfers
- [x] T28a Messaging · Channels
- [x] T28b Messaging · Messages
- [x] T28c Messaging · People
- [x] T28d Messaging · Calls
- [x] T28e Messaging · Stickers and GIFs
- [x] T29a Mail · Boxes and lists
- [x] T29b Mail · Reading
- [x] T29c Mail · Writing
- [x] T29d Mail · Sorting
- [x] T30a Calendar · Months
- [x] T30b Calendar · Time grid
- [x] T30c Calendar · Agenda and people
- [x] T30d Calendar · Editing
- [x] T31a Project · Tasks — `tasks/ch31-43.md`
- [x] T31b Project · Issues and boards
- [x] T31c Project · Plans
- [x] T31d Project · Time and databases
- [x] T32a Canvas · Plane — `tasks/ch31-43.md`
- [x] T32b Canvas · Tools — `tasks/ch31-43.md`
- [x] T32c Canvas · Panels — `tasks/ch31-43.md`
- [x] T32d Canvas · Graphs and boards — `tasks/ch31-43.md`
- [x] T33a DB & Dev Tools · Databases — `tasks/ch31-43.md`
- [x] T33b DB & Dev Tools · Data formats — `tasks/ch31-43.md`
- [x] T33c DB & Dev Tools · APIs — `tasks/ch31-43.md`
- [x] T33d DB & Dev Tools · Tools and machines — `tasks/ch31-43.md`
- [x] T34 Dashboard — `tasks/ch31-43.md` (its two maps lines wait for T41)
- [x] T35 Settings — `tasks/ch31-43.md`
- [x] T36a Account · Sign in — `tasks/ch31-43.md`
- [x] T36b Account · Profile and access — `tasks/ch31-43.md`
- [x] T36c Account · Billing and team — `tasks/ch31-43.md`
- [x] T37a Onboarding · Flows and highlights — `tasks/ch31-43.md`
- [x] T37b Onboarding · Help — `tasks/ch31-43.md`
- [x] T38a Interaction · Pointer — `tasks/ch31-43.md`
- [x] T38b Interaction · Keys, scroll and focus — `tasks/ch31-43.md`
- [x] T39a Theme · Editing — `tasks/ch31-43.md`
- [x] T39b Theme · Importing — `tasks/ch31-43.md`
- [x] T39c Theme · Platform — `tasks/ch31-43.md`
- [x] T40a i18n — `tasks/ch31-43.md`
- [x] T40b a11y — `tasks/ch31-43.md`
- [x] T41a Maps · View — `tasks/ch31-43.md`
- [x] T41b Maps · Layers — `tasks/ch31-43.md`
- [x] T41c Maps · World — `tasks/ch31-43.md`
- [x] T42a Misc · Time — `tasks/ch31-43.md`
- [x] T42b Misc · Numbers — `tasks/ch31-43.md`
- [x] T42c Misc · Codes and checks — `tasks/ch31-43.md`
- [x] T42d Misc · Asking and learning — `tasks/ch31-43.md`
- [x] T42e Misc · Terms and notices — `tasks/ch31-43.md`
- [x] T42f Misc · Web — `tasks/ch31-43.md`
- [x] T42g Misc · Export and import — `tasks/ch31-43.md`
- [x] T43a Tooling · Workbench — `tasks/ch31-43.md`
- [x] T43b Tooling · Inspection — `tasks/ch31-43.md`
- [x] T43c Tooling · Catalogs — `tasks/ch31-43.md`
- [x] T44a Capture · Roots: roots still on `w_full` drop it, checked at 280px and in padded cards
- [x] T44b Port · Build: gpui and gpui_platform from Zed's repo by git at 1a28cff; the crate, its tests and the gallery build, and check.sh passes
- [x] T44c Port · Parity: every page and scripted state captured light and dark and matched against T44a's shots; AGENTS.md's gpui 0.2.2 and taffy 0.9 rules re-tested at 1a28cff and rewritten
- [x] T44d Web · Stories: the gallery builds for wasm32 through gpui_web; one story per component, chosen by URL, themed by the host page; what cannot run in a browser says why
- [x] T45 Website: `frontend/` rebuilt after `frontend-reference/` (serro.ai's language: heavy titles, thin faint text, hairlines, one accent), Vite 8, pnpm, React 19, TanStack Router, light and dark; isometric line miniatures that grow cell by cell on a marimba beat; every story live, a native capture where a browser cannot run it; no box inside a box, no subheadings; run locally, not published
- [x] T45a Site · Stack: the old `frontend/` replaced by Vite 8, React 19 and TanStack Router under pnpm; routes `/`, `/components/`, `/components/$page/$story`; theme, fonts, the stories manifest
- [x] T45b Site · Iso: isometric line miniatures on a canvas, no perspective, hidden lines removed, cells growing on a sixteenth-note marimba beat, playable under the pointer, light and dark, still under reduced motion
- [x] T45c Site · Home: a full-screen hero and full-screen sections, each a miniature beside its live component, counts, the chapters as a city of plinths, a closing call; motion from scroll and route changes
- [x] T45d Site · Components: the chapter index and the story page redesigned; live stories without frames, native captures for what a browser cannot run, search, keys
- [x] T45e Site · Local: the gallery bundle built for localhost and the site served for the owner to see; not published
- [x] T46 E2E: Playwright against the built site
- [x] T46a Site · Owner's notes: the city title whole, header icons on one line, less space above a story, no visible scroll bars, each story named by its first component, every page laid out at phone, tablet and desktop widths
- [x] T47 Ship: GitHub repo (public), CI, Cloudflare deploy through `npx wrangler`
- [x] T48 Docs: a Docs tab after gpui-kit.com/docs, a rail of guides, an on-this-page list, previous and next; every claim checked against the code, every full Rust sample an example `scripts/check.sh` compiles and the page imports as text
- [x] T48a Docs · Frame: the Docs tab, `/docs/` and `/docs/$page/`, Markdown pages, the rail, the on-this-page list, previous and next, code blocks, light and dark, phone to desktop
- [x] T48b Docs · Introduction: what Ely is, the order to read, what is stable, a first example, license
- [x] T48c Docs · Installation: Rust, the git dependencies, gpui_platform's fonts, runtime shaders, the gallery as a check, what `init` refuses
- [x] T48d Docs · Getting started: an app from `init` to a window under a root `FocusScope`, with a Button
- [x] T48e Docs · Theme: modes, tokens through `cx.theme()`, high contrast, color-blind safe charts, an owner's palette
- [x] T48f Docs · Focus and keyboard: Tab and Shift-Tab, the root scope, tab stops, the focus ring, overlays that take and give back focus
- [x] T48g Docs · Motion: durations, reduced motion, springs, motion from a change
- [x] T48h Docs · Icons, fonts and assets: `Assets`, `IconName`, adding icons, the embedded fonts, file icons
- [x] T48i Docs · Overlays: what draws over the page, dialogs, popovers, toasts
- [x] T48j Docs · Internationalization: the `I18n` global, catalogs, right-to-left
- [x] T48k Docs · WebAssembly: the web gallery build, its URL, the host protocol, what stays native
- [x] T48l Docs · Testing: the `test-support` feature, frames, clocks and reduced motion in tests
- [x] T48m Docs · E2E: the Docs routes, rail, list, previous and next, and code blocks under Playwright
- [ ] T49 Acceptance: live URL and MVP checklist
- [x] T50a Rendering · Plan: chapter 44 in the PRD, `tasks/ch44.md`, these lines
- [x] T50b Rendering · Surface: `rendering::RenderSurface`, tested — `tasks/ch44.md`
- [x] T50c Rendering · Gallery: a Rendering page, its story, `stories.json`; the gallery runs on Linux
- [x] T50d Rendering · Web: the story runs live on the site from the wasm gallery, checked in a browser
- [x] T50e Rendering · Platforms: CI builds and tests the crate on Linux, macOS and Windows, arm64 and x86_64
- [x] T50f Rendering · Docs: a guide with an example `scripts/check.sh` compiles; AGENTS.md
- [x] T50g Rendering · Acceptance: the checklist, item by item
- [x] T51 Showcase · an apps page fed by a public repo — `tasks/showcase.md`
- [x] T52 Logo · an isometric mark for the header, favicon and OG image — `tasks/logo.md`
## T00 detail
@@ -116,108 +201,4 @@ Per-component lines live in `tasks/`. Tags there:
## Review log
| Item | Round | Verdict | Notes |
|------|-------|---------|-------|
| T00 | 1 | FAIL | capture swallowed errors; root not focused; raw px sizes; gallery copy; tag conflict |
| T00 | 2 | FAIL | AGENTS.md called `border_1` a rem helper |
| T00 | 3 | PASS | |
| T01 | 1 | FAIL | img loading needs an id; radii lost; focus loop cap; tooltip geometry literals |
| T01 | 2 | FAIL | Cover painted outside the box once the clip was removed |
| T01 | 3 | FAIL | two-line doc comment; fixed after the cap, verified by scan, no fourth review |
| T02 | 1 | FAIL | stale selection; strftime and zone fallbacks; \\text flattening; probes missed targets; ellipsis overflow; u64 precision; caret sizes; LineClamp copy; 500-line wording |
| T02 | 2 | PASS | |
| T03 | 1 | FAIL | restore kept cached split and float state; drags lacked an owner; weights unchecked; pane id overflow broke atomic restore; sticky copy escaped the clip; wheel propagated past the viewport; drawer pull lost the pointer; no top dock; grip sizes raw |
| T03 | 2 | FAIL | closing a pane made NaN or infinite weights; drag ghost text black in dark |
| T03 | 3 | FAIL | even split of a denormal weight underflowed; fixed after the cap by normalizing on close and split, verified by tests and capture, no fourth review |
| T04 | 1 | FAIL | system close skipped TitleBar::on_close; hosted windows had no Tab scope; switcher lost Tab to an ancestor, dropped focus on close, kept a stale index |
| T04 | 2 | FAIL | a removed on_close kept blocking the system close; switcher keys bubbled past it |
| T04 | 3 | PASS | |
| T05 | 1 | FAIL | hold task kept its state alive; a third click wedged the done state; border_1 undid the group seam |
| T05 | 2 | FAIL | AGENTS.md said no window is found by scanning; popups are |
| T05 | 3 | PASS | |
| T06a | 1 | FAIL | twin ids in clear and eye buttons; text fields not Tab stops; IME undo; stepping from a stale value; mask literals re-read as input; undo while disabled; Down past the last line; clamp before rounding; stuck mention dismissal; NaN swallowed by min and max |
| T06a | 2 | FAIL | float error in rounded bounds; composition commit skipped filter and length; a typed leading literal was eaten; PIN select-all collapsed; stepper state read the old value |
| T06a | 3 | FAIL | a mask diff misread a select-all replacement; grid tolerance too wide for large numbers; fixed after the cap by fitting masks inside `TextInput` with the exact edit, and a float-error tolerance; verified by tests and capture, no fourth review |
| T06b | 1 | FAIL | kept highlight and cascade trail went out of bounds; keyboard click reclosed MultiSelect and Cascader; disabled rows committed by keyboard; Combobox hid its owner's value; a disabled chosen radio left no Tab stop; a disabled Rating took arrows |
| T06b | 2 | FAIL | a free Combobox cleared the text it mounted with |
| T06b | 3 | PASS | |
| T06c | 1 | FAIL | range preview reseeded the month; disabled pickers still committed; month buttons left the cursor; hour column scrolled before layout; `*/1` day fields were not wildcards; next(0) ran on; clock fell back to UTC; zone offsets went stale; "never runs" overclaimed |
| T06c | 2 | FAIL | the description of an unstarred full day field ignored the OR rule |
| T06c | 3 | PASS | |
| T06d | 1 | FAIL | a zero-size checker hung the paint loop; a dragged stop lost its grip after passing another; `opacity(1.0)` kept alpha, so opaque pickers stayed see-through; inset thumbs drifted from the drawn gradients; swatch fills and checkers crossed rounded corners; the thumb ring was a literal white |
| T06d | 2 | PASS | |
| T06e | 1 | PASS | |
| T06f | 1 | FAIL | a stroke lost the move that started the drag and its release point; language codes were not searchable though the gallery said so; emoji search compared a lowercase query with mixed-case names |
| T06f | 2 | PASS | |
| T06g | 1 | FAIL | form errors shared one animation id, so a later error skipped its entrance; the demo left links out of its saved snapshot; the demo let a lone @ pass as an email |
| T06g | 2 | FAIL | a field's error still keyed its entrance by the label, so twin labels in one form shared it |
| T06g | 3 | PASS | |
| T07a | 1 | FAIL | editor tabs keyed their scroll on the count of changes: a far tab chosen at mount stayed off screen, and after one change every redraw pulled a manual scroll back |
| T07a | 2 | FAIL | in a hidden or zero-width strip the follow asked for a frame on every render, 126 redraws in 2 s |
| T07a | 3 | FAIL | AGENTS.md said the test platform gives text no width; it gives each character a fixed advance (fixed after the cap) |
| T07b | 1 | FAIL | an open navigation menu kept entry and link indexes past lists that shrank, and disabled links still fired by click or Enter |
| T07b | 2 | PASS | |
| T07c | 1 | FAIL | SearchPalette let a disabled result run on Enter, and the palette tests compiled without test-support |
| T07c | 2 | PASS | |
| T08a | 1 | FAIL | a marked row the owner removed panicked on Enter, the trigger's second click skipped give_back, Enter and Space reached parents on press, the harness never released keys so scripted picks failed, and the menus copy said key hints run rows |
| T08a | 2 | FAIL | Ctrl-Enter and Shift-Space reached keys() on press and picked at once |
| T08a | 3 | PASS | |
| T08b | 1 | FAIL | a shrinking pie kept a mark past its end, a hub press lost focus, pie confirm keys bubbled and modified releases picked, an open dropdown stayed put when its host moved, clearing the filter kept the old mark, and the ring sat half a slice off its hub |
| T08b | 2 | FAIL | the pie's opening right click let a root FocusScope take focus before take_focus recorded it |
| T08b | 3 | PASS | |
| T09a | 1 | FAIL | dialog buttons closed through the owner and skipped give_back, popover and hover panels let presses through, and the popover guessed its height |
| T09a | 2 | FAIL | the prompt's Enter release ran from anywhere in the dialog, so Enter on Cancel submitted and on Submit sent twice |
| T09a | 3 | PASS | |
| T09b | 1 | FAIL | the lightbox photo was cropped, the toolbar kept last frame's anchor, Escape missed a spotlight whose target held focus, a disabled Next dropped focus, and a dead arrow's press closed the lightbox |
| T09b | 2 | FAIL | Enter on a focused Back that returned the tour to its first step removed the button and lost focus, so the arrows stopped |
| T09b | 3 | FAIL | Left from a focused Back also removed it and lost focus; take_focus now takes focus back whenever the focused element leaves the tree (fixed after the cap) |
| T10a | 1 | FAIL | a second press on a leaving toast's Undo ran it again, EmptyState's prose width was a literal, and two gallery callouts described behavior the code lacks |
| T10a | 2 | FAIL | a gallery callout said reduced motion settles every animation, but the undo line keeps its real time |
| T10a | 3 | PASS | |
| T10b | 1 | FAIL | the state view's prose and detail widths were literals again, and pad put zeros ahead of a minus sign |
| T10b | 2 | FAIL | the error chain did not wrap inside its narrowed box, and ConnectionStatus's doc said OfflineIndicator shows only while offline |
| T10b | 3 | FAIL | a wrapped body did not push the actions down; the prose is now a plain block, and a test compares a short and a long body's height (fixed after the cap) |
| T11a | 1 | FAIL | the shimmer band crossed the container's rounded corners, and the skeleton card's picture height was a literal |
| T11a | 2 | FAIL | a rounded_full shimmer used the raw 9999px radius, so the band's path left the box |
| T11a | 3 | PASS | |
| T11b | 1 | FAIL | the sweeping bar grew in a tall flex column, the overlay's veil ignored rounded corners, the fail demo could not start a load, and two UploadList docs said more than the rows show |
| T11b | 2 | PASS | |
| T11c | 1 | FAIL | a drag kept a stale row index and panicked when the list shrank, a move changed each row's animation id and rebuilt its state, and repeated demo tags shared a key |
| T11c | 2 | FAIL | a row first seen hidden drew open and asked for no frame, so it lingered until something else redrew |
| T11c | 3 | PASS | |
| T11d | 1 | FAIL | the ripple drew inverted columns in a cut corner, motes crossed rounded corners, a narrow marquee jumped, confetti burst on a falling count, and the Lottie note misread velato |
| T11d | 2 | FAIL | Marquee's doc still said its content is drawn twice |
| T11d | 3 | PASS | |
| T12a | 1 | FAIL | the avatar upload took HEIC, which gpui cannot decode, an accent badge's dot matched its fill, and a square avatar's upload veil stayed round |
| T12a | 2 | PASS | |
| T12b | 1 | FAIL | the gpui test module compiled without test-support, and a long description value overflowed a narrow column |
| T12b | 2 | PASS | |
| T12c | 1 | FAIL | the gallery kept an open index past a shrunk list, and the lightbox asserted on it |
| T12c | 2 | FAIL | emptying the list removed the open lightbox without handing focus back |
| T12c | 3 | PASS | |
| T12d | 1 | FAIL | Code 128 took a set-switch character, a usage bar's float sum tripped its assert, and a dense QR floored its modules to nothing |
| T12d | 2 | PASS | |
| T13a | 1 | FAIL | Down and Cmd-A picked disabled rows, and Enter opened them |
| T13a | 2 | PASS | |
| T13b | 1 | FAIL | Down stuck on a loading row and never reached the nodes past it |
| T13b | 2 | PASS | |
| T13c | 1 | FAIL | the cursor kept its index through a re-sort, so Enter opened another file |
| T13c | 2 | PASS | |
| T14a | 1 | FAIL | numbers and words compared out of order in a mixed column, and the sort panicked |
| T14a | 2 | PASS | |
| T14b | 1 | FAIL | a removed filter rule left its typing to the next rule, and two sort rows could take one column |
| T14b | 2 | PASS | |
| T14c | 1 | FAIL | a shrunk grid kept its cursor outside, long column letters overflowed, presses landed in merged cells, Tab left the grid, and ranges counted text and blanks |
| T14c | 2 | FAIL | Tab stepped into a merge's hidden cells |
| T14c | 3 | PASS | |
| T15a | 1 | FAIL | an empty chart's SVG export panicked, and a huge flat domain looped forever making ticks |
| T15a | 2 | PASS | |
| T15b | 1 | FAIL | a pointed part outlived shrinking data, an empty heatmap made up a cell, a negative total flipped its bar, and a sunburst child filled its parent's arc |
| T15b | 2 | PASS | |
| T15c | 1 | FAIL | eight charts kept a pointed part past their data, and the Gantt today line ran past the timeline |
| T15c | 2 | PASS | |
| T16a | 1 | — | pending review |
| T16b | 1 | — | pending review |
| T16c | 1 | — | pending review |
| T16d | 1 | — | pending review |
| T16e | 1 | — | pending review |
Every round's verdict lives in `tasks/reviews.md`.
Binary file not shown.
@@ -0,0 +1,92 @@
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file not shown.
+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-a-arrow-down"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m14 12 4 4 4-4" />
<path d="M18 16V7" />
<path d="m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16" />
<path d="M3.304 13h6.392" />
</svg>

After

Width:  |  Height:  |  Size: 432 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-a-arrow-up"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m14 11 4-4 4 4" />
<path d="M18 16V7" />
<path d="m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16" />
<path d="M3.304 13h6.392" />
</svg>

After

Width:  |  Height:  |  Size: 430 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-align-center-horizontal"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M2 12h20" />
<path d="M10 16v4a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-4" />
<path d="M10 8V4a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v4" />
<path d="M20 16v1a2 2 0 0 1-2 2h-2a2 2 0 0 1-2-2v-1" />
<path d="M14 8V7c0-1.1.9-2 2-2h2a2 2 0 0 1 2 2v1" />
</svg>

After

Width:  |  Height:  |  Size: 551 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-align-center-vertical"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 2v20" />
<path d="M8 10H4a2 2 0 0 1-2-2V6c0-1.1.9-2 2-2h4" />
<path d="M16 10h4a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-4" />
<path d="M8 20H7a2 2 0 0 1-2-2v-2c0-1.1.9-2 2-2h1" />
<path d="M16 14h1a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2h-1" />
</svg>

After

Width:  |  Height:  |  Size: 549 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-align-end-horizontal"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="6" height="16" x="4" y="2" rx="2" />
<rect width="6" height="9" x="14" y="9" rx="2" />
<path d="M22 22H2" />
</svg>

After

Width:  |  Height:  |  Size: 427 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-align-end-vertical"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="16" height="6" x="2" y="4" rx="2" />
<rect width="9" height="6" x="9" y="14" rx="2" />
<path d="M22 22V2" />
</svg>

After

Width:  |  Height:  |  Size: 425 B

@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-align-horizontal-distribute-center"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="6" height="14" x="4" y="5" rx="2" />
<rect width="6" height="10" x="14" y="7" rx="2" />
<path d="M17 22v-5" />
<path d="M17 7V2" />
<path d="M7 22v-3" />
<path d="M7 5V2" />
</svg>

After

Width:  |  Height:  |  Size: 512 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-align-start-horizontal"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="6" height="16" x="4" y="6" rx="2" />
<rect width="6" height="9" x="14" y="6" rx="2" />
<path d="M22 2H2" />
</svg>

After

Width:  |  Height:  |  Size: 428 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-align-start-vertical"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="9" height="6" x="6" y="14" rx="2" />
<rect width="16" height="6" x="6" y="4" rx="2" />
<path d="M2 2v20" />
</svg>

After

Width:  |  Height:  |  Size: 426 B

@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-align-vertical-distribute-center"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M22 17h-3" />
<path d="M22 7h-5" />
<path d="M5 17H2" />
<path d="M7 7H2" />
<rect x="5" y="14" width="14" height="6" rx="2" />
<rect x="7" y="4" width="10" height="6" rx="2" />
</svg>

After

Width:  |  Height:  |  Size: 510 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-arrow-down-to-dot"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 2v14" />
<path d="m19 9-7 7-7-7" />
<circle cx="12" cy="21" r="1" />
</svg>

After

Width:  |  Height:  |  Size: 384 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-arrow-down-to-line"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 17V3" />
<path d="m6 11 6 6 6-6" />
<path d="M19 21H5" />
</svg>

After

Width:  |  Height:  |  Size: 374 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-arrow-down-up"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m3 16 4 4 4-4" />
<path d="M7 20V4" />
<path d="m21 8-4-4-4 4" />
<path d="M17 4v16" />
</svg>

After

Width:  |  Height:  |  Size: 397 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-arrow-up-from-dot"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m5 9 7-7 7 7" />
<path d="M12 16V2" />
<circle cx="12" cy="21" r="1" />
</svg>

After

Width:  |  Height:  |  Size: 383 B

+20
View File
@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-audio-lines"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M2 10v3" />
<path d="M6 6v11" />
<path d="M10 3v18" />
<path d="M14 8v7" />
<path d="M18 5v13" />
<path d="M22 10v3" />
</svg>

After

Width:  |  Height:  |  Size: 431 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-ban"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="12" r="10" />
<path d="M4.929 4.929 19.07 19.071" />
</svg>

After

Width:  |  Height:  |  Size: 359 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-braces"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1" />
<path d="M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1" />
</svg>

After

Width:  |  Height:  |  Size: 457 B

+20
View File
@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-captions-off"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10.5 5H19a2 2 0 0 1 2 2v8.5" />
<path d="M17 11h-.5" />
<path d="M19 19H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2" />
<path d="m2 2 20 20" />
<path d="M7 11h4" />
<path d="M7 15h2.5" />
</svg>

After

Width:  |  Height:  |  Size: 489 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-captions"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="18" height="14" x="3" y="5" rx="2" ry="2" />
<path d="M7 15h4M15 15h2M7 11h2M13 11h4" />
</svg>

After

Width:  |  Height:  |  Size: 393 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-case-sensitive"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16" />
<path d="M22 9v7" />
<path d="M3.304 13h6.392" />
<circle cx="18.5" cy="12.5" r="3.5" />
</svg>

After

Width:  |  Height:  |  Size: 444 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-chart-gantt"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 6h8" />
<path d="M12 16h6" />
<path d="M3 3v16a2 2 0 0 0 2 2h16" />
<path d="M8 11h7" />
</svg>

After

Width:  |  Height:  |  Size: 400 B

+22
View File
@@ -0,0 +1,22 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-circle-dashed"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10.1 2.182a10 10 0 0 1 3.8 0" />
<path d="M13.9 21.818a10 10 0 0 1-3.8 0" />
<path d="M17.609 3.721a10 10 0 0 1 2.69 2.7" />
<path d="M2.182 13.9a10 10 0 0 1 0-3.8" />
<path d="M20.279 17.609a10 10 0 0 1-2.7 2.69" />
<path d="M21.818 10.1a10 10 0 0 1 0 3.8" />
<path d="M3.721 6.391a10 10 0 0 1 2.7-2.69" />
<path d="M6.391 20.279a10 10 0 0 1-2.69-2.7" />
</svg>

After

Width:  |  Height:  |  Size: 674 B

+23
View File
@@ -0,0 +1,23 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-circle-dot-dashed"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10.1 2.18a9.93 9.93 0 0 1 3.8 0" />
<path d="M17.6 3.71a9.95 9.95 0 0 1 2.69 2.7" />
<path d="M21.82 10.1a9.93 9.93 0 0 1 0 3.8" />
<path d="M20.29 17.6a9.95 9.95 0 0 1-2.7 2.69" />
<path d="M13.9 21.82a9.94 9.94 0 0 1-3.8 0" />
<path d="M6.4 20.29a9.95 9.95 0 0 1-2.69-2.7" />
<path d="M2.18 13.9a9.93 9.93 0 0 1 0-3.8" />
<path d="M3.71 6.4a9.95 9.95 0 0 1 2.7-2.69" />
<circle cx="12" cy="12" r="1" />
</svg>

After

Width:  |  Height:  |  Size: 729 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-columns-3"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="18" height="18" x="3" y="3" rx="2" />
<path d="M9 3v18" />
<path d="M15 3v18" />
</svg>

After

Width:  |  Height:  |  Size: 388 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-corner-down-right"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m15 10 5 5-5 5" />
<path d="M4 4v7a4 4 0 0 0 4 4h12" />
</svg>

After

Width:  |  Height:  |  Size: 365 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-delete"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M10 5a2 2 0 0 0-1.344.519l-6.328 5.74a1 1 0 0 0 0 1.481l6.328 5.741A2 2 0 0 0 10 19h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2z" />
<path d="m12 9 6 6" />
<path d="m18 9-6 6" />
</svg>

After

Width:  |  Height:  |  Size: 468 B

+20
View File
@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-dices"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="12" height="12" x="2" y="10" rx="2" ry="2" />
<path d="m17.92 14 3.5-3.5a2.24 2.24 0 0 0 0-3l-5-4.92a2.24 2.24 0 0 0-3 0L10 6" />
<path d="M6 18h.01" />
<path d="M10 14h.01" />
<path d="M15 6h.01" />
<path d="M18 9h.01" />
</svg>

After

Width:  |  Height:  |  Size: 532 B

+20
View File
@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-archive"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v11.5" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="M8 12v-1" />
<path d="M8 18v-2" />
<path d="M8 7V6" />
<circle cx="8" cy="20" r="2" />
</svg>

After

Width:  |  Height:  |  Size: 557 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-audio"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 6.835V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-.343" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="M2 19a2 2 0 0 1 4 0v1a2 2 0 0 1-4 0v-4a6 6 0 0 1 12 0v4a2 2 0 0 1-4 0v-1a2 2 0 0 1 4 0" />
</svg>

After

Width:  |  Height:  |  Size: 550 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-code"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="M10 12.5 8 15l2 2.5" />
<path d="m14 12.5 2 2.5-2 2.5" />
</svg>

After

Width:  |  Height:  |  Size: 524 B

+26
View File
@@ -0,0 +1,26 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-cog"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M15 8a1 1 0 0 1-1-1V2a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8z" />
<path d="M20 8v12a2 2 0 0 1-2 2h-4.182" />
<path d="m3.305 19.53.923-.382" />
<path d="M4 10.592V4a2 2 0 0 1 2-2h8" />
<path d="m4.228 16.852-.924-.383" />
<path d="m5.852 15.228-.383-.923" />
<path d="m5.852 20.772-.383.924" />
<path d="m8.148 15.228.383-.923" />
<path d="m8.53 21.696-.382-.924" />
<path d="m9.773 16.852.922-.383" />
<path d="m9.773 19.148.922.383" />
<circle cx="7" cy="18" r="3" />
</svg>

After

Width:  |  Height:  |  Size: 806 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-image"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<circle cx="10" cy="12" r="2" />
<path d="m20 17-1.296-1.296a2.41 2.41 0 0 0-3.408 0L9 22" />
</svg>

After

Width:  |  Height:  |  Size: 552 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-json"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1" />
<path d="M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1" />
</svg>

After

Width:  |  Height:  |  Size: 619 B

+20
View File
@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-spreadsheet"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="M8 13h2" />
<path d="M14 13h2" />
<path d="M8 17h2" />
<path d="M14 17h2" />
</svg>

After

Width:  |  Height:  |  Size: 554 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-terminal"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="m8 16 2-2-2-2" />
<path d="M12 18h4" />
</svg>

After

Width:  |  Height:  |  Size: 510 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-type"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="M11 18h2" />
<path d="M12 12v6" />
<path d="M9 13v-.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v.5" />
</svg>

After

Width:  |  Height:  |  Size: 564 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-file-video-camera"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 12V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2" />
<path d="M14 2v5a1 1 0 0 0 1 1h5" />
<path d="m10 17.843 3.033-1.755a.64.64 0 0 1 .967.56v4.704a.65.65 0 0 1-.967.56L10 20.157" />
<rect width="7" height="6" x="3" y="16" rx="1" />
</svg>

After

Width:  |  Height:  |  Size: 594 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-frown"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M15 10V9" />
<path d="M9 10V9" />
<path d="M9 16a5 5 0 016 0" />
<circle cx="12" cy="12" r="10" />
</svg>

After

Width:  |  Height:  |  Size: 400 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-fullscreen"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M3 7V5a2 2 0 0 1 2-2h2" />
<path d="M17 3h2a2 2 0 0 1 2 2v2" />
<path d="M21 17v2a2 2 0 0 1-2 2h-2" />
<path d="M7 21H5a2 2 0 0 1-2-2v-2" />
<rect width="10" height="8" x="7" y="8" rx="1" />
</svg>

After

Width:  |  Height:  |  Size: 499 B

+20
View File
@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-git-pull-request-closed"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m15.5 3.5 5 5" />
<path d="m15.5 8.5 5-5" />
<path d="M18 11.62V15" />
<path d="M6 9v12" />
<circle cx="18" cy="18" r="3" />
<circle cx="6" cy="6" r="3" />
</svg>

After

Width:  |  Height:  |  Size: 479 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-git-pull-request-draft"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="18" cy="18" r="3" />
<circle cx="6" cy="6" r="3" />
<path d="M18 6V5" />
<path d="M18 11v-1" />
<line x1="6" x2="6" y1="9" y2="21" />
</svg>

After

Width:  |  Height:  |  Size: 457 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-heading-1"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 12h8" />
<path d="M4 18V6" />
<path d="M12 18V6" />
<path d="m17 12 3-2v8" />
</svg>

After

Width:  |  Height:  |  Size: 386 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-heading-2"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 12h8" />
<path d="M4 18V6" />
<path d="M12 18V6" />
<path d="M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1" />
</svg>

After

Width:  |  Height:  |  Size: 411 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-heading-3"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M4 12h8" />
<path d="M4 18V6" />
<path d="M12 18V6" />
<path d="M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2" />
<path d="M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2" />
</svg>

After

Width:  |  Height:  |  Size: 473 B

+15
View File
@@ -0,0 +1,15 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-headphones"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 18 0v7a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3" />
</svg>

After

Width:  |  Height:  |  Size: 428 B

+15
View File
@@ -0,0 +1,15 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-hexagon"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
</svg>

After

Width:  |  Height:  |  Size: 423 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-laugh"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M15 10V9" />
<path d="M7.084 14.302a5.12 5.12 0 009.833 0 .24.24 0 00-.235-.302H7.32a.24.24 0 00-.235.302" />
<path d="M9 10V9" />
<circle cx="12" cy="12" r="10" />
</svg>

After

Width:  |  Height:  |  Size: 466 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-meh"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M15 10V9" />
<path d="M8 16h8" />
<path d="M9 10V9" />
<circle cx="12" cy="12" r="10" />
</svg>

After

Width:  |  Height:  |  Size: 388 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-merge"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m8 6 4-4 4 4" />
<path d="M12 2v10.3a4 4 0 0 1-1.172 2.872L4 22" />
<path d="m20 22-5-5" />
</svg>

After

Width:  |  Height:  |  Size: 391 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-message-square-diff"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z" />
<path d="M10 15h4" />
<path d="M10 9h4" />
<path d="M12 7v4" />
</svg>

After

Width:  |  Height:  |  Size: 499 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-move-up-right"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13 5H19V11" />
<path d="M19 5L5 19" />
</svg>

After

Width:  |  Height:  |  Size: 345 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-music"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M9 18V5l12-2v13" />
<circle cx="6" cy="18" r="3" />
<circle cx="18" cy="16" r="3" />
</svg>

After

Width:  |  Height:  |  Size: 384 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-octagon-alert"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 16h.01" />
<path d="M12 8v4" />
<path d="M15.312 2a2 2 0 0 1 1.414.586l4.688 4.688A2 2 0 0 1 22 8.688v6.624a2 2 0 0 1-.586 1.414l-4.688 4.688a2 2 0 0 1-1.414.586H8.688a2 2 0 0 1-1.414-.586l-4.688-4.688A2 2 0 0 1 2 15.312V8.688a2 2 0 0 1 .586-1.414l4.688-4.688A2 2 0 0 1 8.688 2z" />
</svg>

After

Width:  |  Height:  |  Size: 594 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-pen-line"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13 21h8" />
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
</svg>

After

Width:  |  Height:  |  Size: 455 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-redo-dot"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="12" cy="17" r="1" />
<path d="M21 7v6h-6" />
<path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7" />
</svg>

After

Width:  |  Height:  |  Size: 405 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-regex"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M17 3v10" />
<path d="m12.67 5.5 8.66 5" />
<path d="m12.67 10.5 8.66-5" />
<path d="M9 17a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2v-2z" />
</svg>

After

Width:  |  Height:  |  Size: 462 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-repeat-1"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m17 2 4 4-4 4" />
<path d="M3 11v-1a4 4 0 0 1 4-4h14" />
<path d="m7 22-4-4 4-4" />
<path d="M21 13v1a4 4 0 0 1-4 4H3" />
<path d="M11 10h1v4" />
</svg>

After

Width:  |  Height:  |  Size: 452 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-repeat"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m17 2 4 4-4 4" />
<path d="M3 11v-1a4 4 0 0 1 4-4h14" />
<path d="m7 22-4-4 4-4" />
<path d="M21 13v1a4 4 0 0 1-4 4H3" />
</svg>

After

Width:  |  Height:  |  Size: 424 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-reply-all"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m12 17-5-5 5-5" />
<path d="M22 18v-2a4 4 0 0 0-4-4H7" />
<path d="m7 17-5-5 5-5" />
</svg>

After

Width:  |  Height:  |  Size: 388 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-screen-share-off"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13 3H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-3" />
<path d="M8 21h8" />
<path d="M12 17v4" />
<path d="m22 3-5 5" />
<path d="m17 3 5 5" />
</svg>

After

Width:  |  Height:  |  Size: 466 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-screen-share"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13 3H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-3" />
<path d="M8 21h8" />
<path d="M12 17v4" />
<path d="m17 8 5-5" />
<path d="M17 3h5v5" />
</svg>

After

Width:  |  Height:  |  Size: 462 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-shuffle"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m18 14 4 4-4 4" />
<path d="m18 2 4 4-4 4" />
<path d="M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22" />
<path d="M2 6h1.972a4 4 0 0 1 3.6 2.2" />
<path d="M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45" />
</svg>

After

Width:  |  Height:  |  Size: 522 B

+15
View File
@@ -0,0 +1,15 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-slash"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M22 2 2 22" />
</svg>

After

Width:  |  Height:  |  Size: 310 B

+20
View File
@@ -0,0 +1,20 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-smile-plus"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M13.267 2.08a10 10 0 108.653 8.653" />
<path d="M15 10V9" />
<path d="M16 5h6" />
<path d="M16.472 15a6 6 0 01-8.943 0" />
<path d="M19 2v6" />
<path d="M9 10V9" />
</svg>

After

Width:  |  Height:  |  Size: 475 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-square-function"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="18" height="18" x="3" y="3" rx="2" ry="2" />
<path d="M9 17c2 0 2.8-1 2.8-2.8V10c0-2 1-3.3 3.2-3" />
<path d="M9 11.2h5.7" />
</svg>

After

Width:  |  Height:  |  Size: 439 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-square-pen"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
<path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z" />
</svg>

After

Width:  |  Height:  |  Size: 498 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-square-round-corner"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 11a8 8 0 0 0-8-8" />
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
</svg>

After

Width:  |  Height:  |  Size: 406 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-sticky-note"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M21 9a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 15 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2z" />
<path d="M15 3v5a1 1 0 0 0 1 1h5" />
</svg>

After

Width:  |  Height:  |  Size: 458 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-user-plus"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<line x1="19" x2="19" y1="8" y2="14" />
<line x1="22" x2="16" y1="11" y2="11" />
</svg>

After

Width:  |  Height:  |  Size: 463 B

+18
View File
@@ -0,0 +1,18 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-variable"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M8 21s-4-3-4-9 4-9 4-9" />
<path d="M16 3s4 3 4 9-4 9-4 9" />
<line x1="15" x2="9" y1="9" y2="15" />
<line x1="9" x2="15" y1="9" y2="15" />
</svg>

After

Width:  |  Height:  |  Size: 444 B

+16
View File
@@ -0,0 +1,16 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-volume-1"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z" />
<path d="M16 9a5 5 0 0 1 0 6" />
</svg>

After

Width:  |  Height:  |  Size: 506 B

+15
View File
@@ -0,0 +1,15 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-volume"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z" />
</svg>

After

Width:  |  Height:  |  Size: 469 B

+19
View File
@@ -0,0 +1,19 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-whole-word"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle cx="7" cy="12" r="3" />
<path d="M10 9v6" />
<circle cx="17" cy="12" r="3" />
<path d="M14 7v8" />
<path d="M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1" />
</svg>

After

Width:  |  Height:  |  Size: 462 B

+17
View File
@@ -0,0 +1,17 @@
<!-- @license lucide-static v1.48.0 - ISC -->
<svg
class="lucide lucide-workflow"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="8" height="8" x="3" y="3" rx="2" />
<path d="M7 11v4a2 2 0 0 0 2 2h4" />
<rect width="8" height="8" x="13" y="13" rx="2" />
</svg>

After

Width:  |  Height:  |  Size: 430 B

+1
View File
@@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"name":"Fiji","iso":"FJ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[180,-16.07],[180,-16.56],[179.36,-16.8],[178.73,-17.01],[178.6,-16.64],[179.1,-16.43],[179.41,-16.38],[180,-16.07]]],[[[178.13,-17.5],[178.37,-17.34],[178.72,-17.63],[178.55,-18.15],[177.93,-18.29],[177.38,-18.16],[177.29,-17.72],[177.67,-17.38],[178.13,-17.5]]],[[[-179.79,-16.02],[-179.92,-16.5],[-180,-16.56],[-180,-16.07],[-179.79,-16.02]]]]}},{"type":"Feature","properties":{"name":"Tanzania","iso":"TZ"},"geometry":{"type":"MultiPolygon","coordinates":[[[[33.9,-0.95],[34.07,-1.06],[37.7,-3.1],[37.77,-3.68],[39.2,-4.68],[38.74,-5.91],[38.8,-6.48],[39.44,-6.84],[39.47,-7.1],[39.19,-7.7],[39.25,-8.01],[39.19,-8.49],[39.54,-9.11],[39.95,-10.1],[40.32,-10.32],[39.52,-10.9],[38.43,-11.29],[37.83,-11.27],[37.47,-11.57],[36.78,-11.59],[36.51,-11.72],[35.31,-11.44],[34.56,-11.52],[34.28,-10.16],[33.94,-9.69],[33.74,-9.42],[32.76,-9.23],[32.19,-8.93],[31.56,-8.76],[31.16,-8.59],[30.74,-8.34],[30.2,-7.08],[29.62,-6.52],[29.42,-5.94],[29.52,-5.42],[29.34,-4.5],[29.75,-4.45],[30.12,-4.09],[30.51,-3.57],[30.75,-3.36],[30.74,-3.03],[30.53,-2.81],[30.47,-2.41],[30.76,-2.29],[30.82,-1.7],[30.42,-1.13],[30.77,-1.01],[31.87,-1.03],[33.9,-0.95]]]]}},{"type":"Feature","properties":{"name":"W. Sahara","iso":"EH"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-8.67,27.66],[-8.67,27.59],[-8.68,27.4],[-8.69,25.88],[-11.97,25.93],[-11.94,23.37],[-12.87,23.28],[-13.12,22.77],[-12.93,21.33],[-16.85,21.33],[-17.06,21.0],[-17.02,21.42],[-17.0,21.42],[-14.75,21.5],[-14.63,21.86],[-14.22,22.31],[-13.89,23.69],[-12.5,24.77],[-12.03,26.03],[-11.72,26.1],[-11.39,26.88],[-10.55,26.99],[-10.19,26.86],[-9.74,26.86],[-9.41,27.09],[-8.79,27.12],[-8.82,27.66],[-8.67,27.66]]]]}},{"type":"Feature","properties":{"name":"Canada","iso":"CA"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-122.84,49],[-122.97,49.0],[-124.91,49.98],[-125.62,50.42],[-127.44,50.83],[-127.99,51.72],[-127.85,52.33],[-129.13,52.76],[-129.31,53.56],[-130.51,54.29],[-130.54,54.8],[-129.98,55.28],[-130.01,55.92],[-131.71,56.55],[-132.73,57.69],[-133.36,58.41],[-134.27,58.86],[-134.94,59.27],[-135.48,59.79],[-136.48,59.46],[-137.45,58.91],[-138.34,59.56],[-139.04,60],[-140.01,60.28],[-141.0,60.31],[-140.99,66.0],[-140.99,69.71],[-139.12,69.47],[-137.55,68.99],[-136.5,68.9],[-135.63,69.32],[-134.41,69.63],[-132.93,69.51],[-131.43,69.94],[-129.79,70.19],[-129.11,69.78],[-128.36,70.01],[-128.14,70.48],[-127.45,70.38],[-125.76,69.48],[-124.42,70.16],[-124.29,69.4],[-123.06,69.56],[-122.68,69.86],[-121.47,69.8],[-119.94,69.38],[-117.6,69.01],[-116.23,68.84],[-115.25,68.91],[-113.9,68.4],[-115.3,67.9],[-113.5,67.69],[-110.8,67.81],[-109.95,67.98],[-108.88,67.38],[-107.79,67.89],[-108.81,68.31],[-108.17,68.65],[-106.95,68.7],[-106.15,68.8],[-105.34,68.56],[-104.34,68.02],[-103.22,68.1],[-101.45,67.65],[-99.9,67.81],[-98.44,67.78],[-98.56,68.4],[-97.67,68.58],[-96.12,68.24],[-96.13,67.29],[-95.49,68.09],[-94.69,68.06],[-94.23,69.07],[-95.3,69.69],[-96.47,70.09],[-96.39,71.19],[-95.21,71.92],[-93.89,71.76],[-92.88,71.32],[-91.52,70.19],[-92.41,69.7],[-90.55,69.5],[-90.55,68.47],[-89.22,69.26],[-88.02,68.62],[-88.32,67.87],[-87.35,67.2],[-86.31,67.92],[-85.58,68.78],[-85.52,69.88],[-84.1,69.81],[-82.62,69.66],[-81.28,69.16],[-81.22,68.67],[-81.96,68.13],[-81.26,67.6],[-81.39,67.11],[-83.34,66.41],[-84.74,66.26],[-85.77,66.56],[-86.07,66.06],[-87.03,65.21],[-87.32,64.78],[-88.48,64.1],[-89.91,64.03],[-90.7,63.61],[-90.77,62.96],[-91.93,62.84],[-93.16,62.02],[-94.24,60.9],[-94.63,60.11],[-94.68,58.95],[-93.22,58.78],[-92.76,57.85],[-92.3,57.09],[-90.9,57.28],[-89.04,56.85],[-88.04,56.47],[-87.32,56.0],[-86.07,55.72],[-85.01,55.3],[-83.36,55.24],[-82.27,55.15],[-82.44,54.28],[-82.13,53.28],[-81.4,52.16],[-79.91,51.21],[-79.14,51.53],[-78.6,52.56],[-79.12,54.14],[-79.83,54.67],[-78.23,55.14],[-77.1,55.84],[-76.54,56.53],[-76.62,57.2],[-77.3,58.05],[-78.52,58.8],[-77.34,59.85],[-77.77,60.76],[-78.11,62.32],[-77.41,62.55],[-75.7,62.28],[-74.67,62.18],[-73.84,62.44],[-72.91,62.11],[-71.68,61.53],[-71.37,61.14],[-69.59,61.06],[-69.62,60.22],[-69.29,58.96],[-68.37,58.8],[-67.65,58.21],[-66.2,58.77],[-65.25,59.87],[-64.58,60.34],[-63.8,59.44],[-62.5,58.17],[-61.4,56.97],[-61.8,56.34],[-60.47,55.78],[-59.57,55.2],[-57.98,54.95],[-57.33,54.63],[-56.94,53.78],[-56.16,53.65],[-55.76,53.27],[-55.68,52.15],[-56.41,51.77],[-57.13,51.42],[-58.77,51.06],[-60.03,50.24],[-61.72,50.08],[-63.86,50.29],[-65.36,50.3],[-66.4,50.23],[-67.24,49.51],[-68.51,49.07],[-69.95,47.74],[-71.1,46.82],[-70.26,46.99],[-68.65,48.3],[-66.55,49.13],[-65.06,49.23],[-64.17,48.74],[-65.12,48.07],[-64.8,46.99],[-64.47,46.24],[-63.17,45.74],[-61.52,45.88],[-60.52,47.01],[-60.45,46.28],[-59.8,45.92],[-61.04,45.27],[-63.25,44.67],[-64.25,44.27],[-65.36,43.55],[-66.12,43.62],[-66.16,44.47],[-64.43,45.29],[-66.03,45.26],[-67.14,45.14],[-67.79,45.7],[-67.79,47.07],[-68.23,47.35],Line truncated
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2 22 12 12 22 2 12Z"/>
<path d="M8 12h8M12 8v8"/>
</svg>

After

Width:  |  Height:  |  Size: 256 B

+72
View File
@@ -0,0 +1,72 @@
use ely_gpui_component::{
Assets,
buttons::Button,
primitives::{FocusRing, FocusScope},
theme::{ActiveTheme, Radius},
};
use gpui::{
App, AppContext, Context, FocusHandle, InteractiveElement, IntoElement, ParentElement, Render,
StatefulInteractiveElement, Styled, Window, WindowOptions, div, transparent_black,
};
struct Panel {
root: FocusHandle,
tile: FocusHandle,
on: bool,
}
impl Render for Panel {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme();
let this = cx.entity();
let tile = div()
.id("tile")
.track_focus(&self.tile)
.p_4()
.border_1()
.border_color(transparent_black())
.rounded(theme.radius(Radius::Md))
.bg(theme.colors.surface)
.focus_ring(cx)
.child(if self.on { "On" } else { "Off" })
.on_click(move |_, _, cx| {
this.update(cx, |panel, cx| {
panel.on = !panel.on;
cx.notify();
})
});
FocusScope::new(&self.root)
.root()
.size_full()
.flex()
.flex_col()
.gap_4()
.p_8()
.bg(theme.colors.bg)
.text_color(theme.colors.fg)
.child(Button::new("first", "First"))
.child(tile)
.child(Button::new("last", "Last"))
}
}
fn main() {
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx);
cx.open_window(WindowOptions::default(), |window, cx| {
cx.new(|cx| {
let root = cx.focus_handle();
window.focus(&root, cx);
Panel {
root,
tile: cx.focus_handle().tab_stop(true),
on: false,
}
})
})
.expect("window failed to open");
cx.activate(true);
});
}
+55
View File
@@ -0,0 +1,55 @@
use ely_gpui_component::{Assets, buttons::Button, primitives::FocusScope, theme::ActiveTheme};
use gpui::{
App, AppContext, Context, FocusHandle, IntoElement, ParentElement, Render, Styled, Window,
WindowOptions,
};
struct Hello {
focus: FocusHandle,
presses: usize,
}
impl Render for Hello {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let colors = &cx.theme().colors;
let this = cx.entity();
FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.flex_col()
.items_center()
.justify_center()
.gap_4()
.bg(colors.bg)
.text_color(colors.fg)
.child(format!("Pressed {} times", self.presses))
.child(
Button::new("press", "Press")
.primary()
.on_click(move |_, _, cx| {
this.update(cx, |hello, cx| {
hello.presses += 1;
cx.notify();
});
}),
)
}
}
fn main() {
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx);
cx.open_window(WindowOptions::default(), |window, cx| {
cx.new(|cx| {
let focus = cx.focus_handle();
window.focus(&focus, cx);
Hello { focus, presses: 0 }
})
})
.expect("window failed to open");
cx.activate(true);
});
}
+86
View File
@@ -0,0 +1,86 @@
use ely_gpui_component::{
Assets,
buttons::Button,
i18n::{Direction, I18n},
primitives::FocusScope,
theme::ActiveTheme,
};
use gpui::{
App, AppContext, Context, FocusHandle, IntoElement, ParentElement, Render, Styled, Window,
WindowOptions, div,
};
struct Words {
focus: FocusHandle,
}
impl Render for Words {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme();
let i18n = cx.global::<I18n>();
let locale = i18n.locale();
let greeting = i18n.text("greeting", &[("name", "Ada")]);
let files = i18n.text("files", &[("count", &locale.number(1234.0, 0))]);
let next = if locale.direction == Direction::Rtl {
"en-US"
} else {
"he-IL"
};
let row = locale
.direction
.row(div())
.gap_4()
.child(greeting)
.child(files);
FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.flex_col()
.gap_4()
.p_8()
.bg(theme.colors.bg)
.text_color(theme.colors.fg)
.child(row)
.child(
Button::new("locale", i18n.text("switch", &[]))
.on_click(move |_, _, cx| I18n::set_locale(next, cx)),
)
}
}
fn main() {
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx);
cx.set_global(
I18n::new("en-US")
.catalog(
"en-US",
&[
("greeting", "Hello, {name}"),
("files", "{count} files"),
("switch", "עברית"),
],
)
.catalog(
"he-IL",
&[
("greeting", "שלום, {name}"),
("files", "{count} קבצים"),
("switch", "English"),
],
),
);
cx.open_window(WindowOptions::default(), |window, cx| {
cx.new(|cx| {
let focus = cx.focus_handle();
window.focus(&focus, cx);
Words { focus }
})
})
.expect("window failed to open");
cx.activate(true);
});
}
+104
View File
@@ -0,0 +1,104 @@
use std::{borrow::Cow, f32::consts::FRAC_PI_4};
use ely_gpui_component::{
Assets,
files::FileIcon,
primitives::{FocusScope, Icon, IconName, IconTheme},
theme::{ActiveTheme, IconSize},
};
use gpui::{
App, AppContext, AssetSource, Context, FocusHandle, IntoElement, ParentElement, Radians,
Render, SharedString, Styled, Window, WindowOptions, div,
};
const CUSTOM_ICON: &str = "app-icons/mark.svg";
struct AppAssets;
impl AssetSource for AppAssets {
fn load(&self, path: &str) -> gpui::Result<Option<Cow<'static, [u8]>>> {
if path == CUSTOM_ICON {
return Ok(Some(Cow::Borrowed(include_bytes!("../assets/mark.svg"))));
}
Assets.load(path)
}
fn list(&self, path: &str) -> gpui::Result<Vec<SharedString>> {
let mut assets = Assets.list(path)?;
if CUSTOM_ICON.starts_with(path) {
assets.push(CUSTOM_ICON.into());
}
Ok(assets)
}
}
struct Icons {
focus: FocusHandle,
}
impl Render for Icons {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> 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(
Icon::from_path(CUSTOM_ICON)
.size(IconSize::Xl)
.color(theme.colors.accent),
),
)
.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(AppAssets)
.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);
});
}
+122
View File
@@ -0,0 +1,122 @@
use ely_gpui_component::{
Assets,
buttons::Button,
motion::{self, Entrance, Flash, Spinner, Transition},
primitives::FocusScope,
theme::{ActiveTheme, Radius, Theme},
};
use gpui::{
Animation, AnimationExt, App, AppContext, Context, FocusHandle, IntoElement, ParentElement,
Render, Styled, Window, WindowOptions, div,
};
struct Moves {
focus: FocusHandle,
shown: bool,
count: usize,
}
impl Render for Moves {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme();
let still = theme.reduced_motion;
let (toggle, add) = (cx.entity(), cx.entity());
let dot = div()
.size_4()
.rounded(theme.radius(Radius::Xl))
.bg(theme.colors.accent);
let dot = if still {
dot.into_any_element()
} else {
dot.with_animation(
"breath",
Animation::new(motion::duration(motion::SLOW * 4, cx))
.repeat()
.with_easing(motion::ease_in_out_cubic),
|dot, t| dot.opacity(0.4 + 0.6 * (1.0 - (2.0 * t - 1.0).abs())),
)
.into_any_element()
};
FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.flex_col()
.gap_4()
.p_8()
.bg(theme.colors.bg)
.text_color(theme.colors.fg)
.child(
div()
.flex()
.gap_2()
.child(
Button::new("show", if self.shown { "Hide" } else { "Show" }).on_click(
move |_, _, cx| {
toggle.update(cx, |moves, cx| {
moves.shown = !moves.shown;
cx.notify();
})
},
),
)
.child(Button::new("add", "Add one").on_click(move |_, _, cx| {
add.update(cx, |moves, cx| {
moves.count += 1;
cx.notify();
})
}))
.child(
Button::new(
"still",
if still {
"Allow motion"
} else {
"Reduce motion"
},
)
.on_click(move |_, _, cx| {
Theme::update(cx, |theme| theme.reduced_motion = !still)
}),
),
)
.child(
Transition::new("panel", self.shown)
.entrance(Entrance::Rise)
.child("A panel that rises in and fades out."),
)
.child(
Flash::new("count", self.count)
.p_2()
.child(format!("Count: {}", self.count)),
)
.child(
div()
.flex()
.gap_4()
.child(Spinner::new("spinner"))
.child(dot),
)
}
}
fn main() {
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx);
cx.open_window(WindowOptions::default(), |window, cx| {
cx.new(|cx| {
let focus = cx.focus_handle();
window.focus(&focus, cx);
Moves {
focus,
shown: true,
count: 0,
}
})
})
.expect("window failed to open");
cx.activate(true);
});
}
+96
View File
@@ -0,0 +1,96 @@
use ely_gpui_component::{
Assets,
buttons::Button,
feedback::{Toast, ToastViewport, Toaster},
overlays::{Dialog, Popover},
primitives::{FocusScope, Severity},
theme::ActiveTheme,
};
use gpui::{
App, AppContext, Context, Entity, FocusHandle, IntoElement, ParentElement, Render, Styled,
Window, WindowOptions, div,
};
struct Layers {
focus: FocusHandle,
toaster: Entity<Toaster>,
open: bool,
}
impl Render for Layers {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme();
let (opener, closer) = (cx.entity(), cx.entity());
let toaster = self.toaster.clone();
let dialog = self.open.then(|| {
Dialog::new("delete", "Delete notes.txt?", move |_, cx| {
closer.update(cx, |layers, cx| {
layers.open = false;
cx.notify();
})
})
.detail("It leaves the trash after 30 days.")
.action(|close| {
Button::new("cancel", "Cancel").on_click(move |_, window, cx| close(window, cx))
})
.action(move |close| {
Button::new("confirm", "Delete")
.primary()
.on_click(move |_, window, cx| {
close(window, cx);
toaster.update(cx, |toaster, cx| {
toaster.push(Toast::new("Deleted").severity(Severity::Success), cx);
});
})
})
});
FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.flex_col()
.gap_4()
.p_8()
.bg(theme.colors.bg)
.text_color(theme.colors.fg)
.child(
div()
.flex()
.gap_2()
.child(Popover::new("details", "Details", |_, _| {
div().p_3().child("Size 4 KB · Edited today")
}))
.child(
Button::new("delete-open", "Delete…").on_click(move |_, _, cx| {
opener.update(cx, |layers, cx| {
layers.open = true;
cx.notify();
})
}),
),
)
.children(dialog)
.child(ToastViewport::new("toasts", &self.toaster))
}
}
fn main() {
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx);
cx.open_window(WindowOptions::default(), |window, cx| {
cx.new(|cx| {
let focus = cx.focus_handle();
window.focus(&focus, cx);
Layers {
focus,
toaster: cx.new(|_| Toaster::default()),
open: false,
}
})
})
.expect("window failed to open");
cx.activate(true);
});
}
+70
View File
@@ -0,0 +1,70 @@
use ely_gpui_component::{
Assets, buttons::Button, primitives::FocusScope, rendering::RenderSurface, theme::ActiveTheme,
};
use gpui::{
App, AppContext, Context, DevicePixels, FocusHandle, IntoElement, ParentElement, Render,
RenderImage, Size, Styled, Window, WindowOptions,
};
use image::{Frame, RgbaImage};
struct Stripes {
focus: FocusHandle,
count: u32,
}
/// `count` vertical stripes across `size`, in BGRA as gpui holds frames.
fn stripes(size: Size<DevicePixels>, count: u32) -> RenderImage {
let (width, height) = (size.width.0 as u32, size.height.0 as u32);
let pixels = RgbaImage::from_fn(width, height, |x, _| {
let lit = (x * count / width).is_multiple_of(2);
let shade = if lit { 220 } else { 40 };
image::Rgba([shade, shade, shade, 255])
});
RenderImage::new(vec![Frame::new(pixels)])
}
impl Render for Stripes {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let colors = &cx.theme().colors;
let count = self.count;
let this = cx.entity();
FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.flex_col()
.gap_4()
.p_6()
.bg(colors.bg)
.text_color(colors.fg)
.child(
RenderSurface::new("stripes", 2.0, move |size, _| stripes(size, count))
.revision(u64::from(count)),
)
.child(
Button::new("more", "More stripes").on_click(move |_, _, cx| {
this.update(cx, |stripes, cx| {
stripes.count += 2;
cx.notify();
});
}),
)
}
}
fn main() {
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx);
cx.open_window(WindowOptions::default(), |window, cx| {
cx.new(|cx| {
let focus = cx.focus_handle();
window.focus(&focus, cx);
Stripes { focus, count: 8 }
})
})
.expect("window failed to open");
cx.activate(true);
});
}
+69
View File
@@ -0,0 +1,69 @@
use ely_gpui_component::{
Assets,
buttons::Button,
primitives::FocusScope,
theme::{ActiveTheme, Mode, Theme},
typography::{Heading, Paragraph},
};
use gpui::{
App, AppContext, Bounds, Context, FocusHandle, IntoElement, ParentElement, Render, Styled,
TitlebarOptions, Window, WindowBounds, WindowOptions, px, size,
};
struct Start {
focus: FocusHandle,
}
impl Render for Start {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme();
let next = if theme.is_dark() {
Mode::Light
} else {
Mode::Dark
};
FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.flex_col()
.gap_4()
.p_8()
.bg(theme.colors.bg)
.child(Heading::h1("Start"))
.child(Paragraph::new(
"Every Ely component reads the theme, so one call turns them all.",
))
.child(
Button::new("mode", format!("{next:?} mode"))
.on_click(move |_, _, cx| Theme::set_mode(next, cx)),
)
}
}
fn main() {
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx);
Theme::set_mode_now(Mode::Dark, cx);
let bounds = Bounds::centered(None, size(px(640.0), px(400.0)), cx);
let options = WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
titlebar: Some(TitlebarOptions {
title: Some("Start".into()),
..Default::default()
}),
..Default::default()
};
cx.open_window(options, |window, cx| {
cx.new(|cx| {
let focus = cx.focus_handle();
window.focus(&focus, cx);
Start { focus }
})
})
.expect("window failed to open");
cx.activate(true);
});
}
+114
View File
@@ -0,0 +1,114 @@
use ely_gpui_component::{
Assets,
buttons::Button,
primitives::FocusScope,
theme::{ActiveTheme, Density, Elevation, Mode, Radius, TextSize, Theme},
typography::Paragraph,
};
use gpui::{
App, AppContext, Context, FocusHandle, IntoElement, ParentElement, Render, Styled, Window,
WindowOptions, div, rgb,
};
struct Tokens {
focus: FocusHandle,
}
impl Render for Tokens {
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let theme = cx.theme();
let dark = theme.is_dark();
let contrast = theme.high_contrast();
let compact = theme.density == Density::Compact;
let card = div()
.flex()
.flex_col()
.gap_2()
.p_4()
.bg(theme.colors.surface)
.border_1()
.border_color(theme.colors.border)
.rounded(theme.radius(Radius::Lg))
.shadow(theme.elevation(Elevation::Floating))
.text_size(theme.text_size(TextSize::Lg))
.text_color(theme.colors.fg)
.child("A card made of tokens")
.child(Paragraph::new(
"Its colors, radius, shadow and text size come from the theme.",
));
let controls = div()
.flex()
.flex_wrap()
.gap_2()
.child(
Button::new("mode", if dark { "Light mode" } else { "Dark mode" }).on_click(
move |_, _, cx| {
Theme::set_mode(if dark { Mode::Light } else { Mode::Dark }, cx)
},
),
)
.child(
Button::new(
"contrast",
if contrast {
"Usual contrast"
} else {
"High contrast"
},
)
.on_click(move |_, _, cx| Theme::set_high_contrast(!contrast, cx)),
)
.child(
Button::new(
"density",
if compact {
"Standard density"
} else {
"Compact"
},
)
.on_click(move |_, _, cx| {
Theme::update(cx, |theme| {
theme.density = if compact {
Density::Standard
} else {
Density::Compact
};
})
}),
)
.child(Button::new("accent", "Green accent").on_click(|_, _, cx| {
let mode = cx.theme().mode();
let mut palette = cx.theme().palette();
*palette.token_mut("accent") = rgb(0x2f7d4f).into();
Theme::set_palette(mode, Some(palette), cx);
}));
FocusScope::new(&self.focus)
.root()
.size_full()
.flex()
.flex_col()
.gap_6()
.p_8()
.bg(theme.colors.bg)
.child(card)
.child(controls)
}
}
fn main() {
gpui_platform::application()
.with_assets(Assets)
.run(|cx: &mut App| {
ely_gpui_component::init(cx);
cx.open_window(WindowOptions::default(), |window, cx| {
cx.new(|cx| {
let focus = cx.focus_handle();
window.focus(&focus, cx);
Tokens { focus }
})
})
.expect("window failed to open");
cx.activate(true);
});
}
Loaded 100 of 1256 files, more files were not shown because too many files have changed in this diff. Show more