Repository navigation
UI Library
Nibtree has its own UI library and design system in ui/. The Svelte components in ui/web/ copy the app's look exactly, and the design tokens in ui/design-tokens/ describe colors, sizes and each component's measurements once, for every platform.
- One look everywhere. The app is moving to native macOS, Windows and Linux apps later. When every platform builds its components from the same tokens, a change of a size or a color reaches all of them.
- Faster work. New screens are put together from ready components instead of new markup and CSS.
- Usable by people and AI agents. Every component has one doc. People read it in the showcase; agents read the same text as Markdown.
flowchart LR
T["ui/design-tokens/*.json"] -->|"bun run ui:tokens"| CSS["web tokens.css and tokens.ts"]
T --> Swift["generated/macos/*.swift"]
T --> XAML["generated/windows/*.xaml"]
T --> GTK["generated/linux/*.css"]
CSS --> C["ui/web components"]
D["ui/web/src/lib/docs"] -->|"bun run ui:docs"| MD["COMPONENTS.md and docs/components"]
D --> S["showcase pages"]
C --> S
-
Tokens.
colors.jsonholds the built-in Light and Dark colors (written tostyles/colors.css).typography.jsonholds the font families, sizes, weights, line heights, letter spacing and named text styles (body, label, hint, caption, sectionLabel, title, heading, code...).tokens.jsonholds size, radius, space, motion, the breakpoints (panel and window widths) and the grid.components.jsonandcomponents-2.jsonhold each component's measurements, such as"button": {"height": "{size.control}"}.editor.jsonholds the code editor's sizes (CSS variables, or@nibtree/ui/tokensin script).themes/holds the color themes. A string in braces refers to another token. -
Generators.
design-tokens/build/turns the files into CSS variables (--button-height), a TypeScript object for code that measures in script, Swift (GMColor.bg,GMButtonTokens.height), XAML resource dictionaries and GTK 4 CSS. Swift output type-checks withswiftc. -
Components.
ui/web/src/lib/components/has one component per file. Each takes sizes from tokens and colors from the theme, and noclassorstyle: tone, size and layout are props.Stacklays things out with spacing steps andTextwrites words in a tone and size, so screens need no CSS of their own. A list is aList(keys, selection, a filter field that drives it) ofRows;Spacerpushes the rest of a row to its end. AresponsiveToolbar hides less needed words when narrow. -
Named sizes. Design props are never free numbers or text. Sizes, widths, heights and spacing are named presets from
tokens.json(logic/presets.ts), so every platform has the same few choices. Numbers are only for data, such as a count or a size the user dragged. -
Editor surfaces.
CodeEditor,DiffView,MergeView,MarkdownView,MarkdownPreview,MarkdownEditor,FormatToolbar,MediaViewandTerminalViewwrap CodeMirror, markdown-it, TipTap and xterm.js in the app's look, with colors from the theme tokens. The app adds its own features (blame, staging, a shell) through props such asextensionsandonready.AppShellis the window layout: activity bars, resizable side and bottom panels, and the status bar. -
Logic. Keyboard movement, tree rows, virtual windows, tab dragging, menu placement, pick filtering and resizing live in
ui/web/src/lib/logic/as pure TypeScript with tests. -
Docs.
ui/web/src/lib/docs/has one doc per component: summary, when to use it, every prop, rules, examples and the app markup it replaces.bun run ui:docswritesui/web/COMPONENTS.md(an index),ui/web/docs/groups/<Group>.md(a line per component) andui/web/docs/components/<Name>.md. -
Showcase.
bun run uiserves http://localhost:1430: a sidebar of components by group, a live preview, the props table, the rules, and code tabs for the example, the component, its logic and the Markdown doc. Foundations shows every color, text style, size, the grid and the breakpoints. A switch tries Light and Dark.bun run ui:themesopens every story in light and dark and fails on a page error or on text that does not stand out from its background.
ui/mcp/ lets AI agents ask the design system for components, tokens, icons and rules. See UI-Library-MCP.md.
- New UI is a library component first: component, doc, story and tokens, then the app uses it.
- Only reusable building blocks go in the library: primitives and variants or props on them. A screen-specific piece (a "Commit to" row), or one that only arranges other components with no behavior of its own (a copy field, a confirm dialog), is composed from them, not added as a component.
- No
classorstyleprops, no literal colors (the white switch knob is the only listed exception). - No free numbers or text for design: sizes, widths, heights, gaps and paddings are named presets; there is no margin prop.
- Sizes come from token variables, never a pixel value copied into a component.
- Stories use only library components, no CSS, which proves a screen needs none.
- Every file stays within 120 columns and 300 lines; generated files are split by kind.
-
design-tokens/build/tokens.test.ts:colors.csshas every color, the popup shadow and the color scheme in each block, every generated file is up to date and within the line limits, references resolve, and colors and shadows parse. -
web/src/lib/docs/docs.test.ts: every component has one doc and one story, each doc lists exactly the props in the component's$props()with the same defaults, bindables and required ones, no component takes a class or style, no literal colors, stories have no CSS, and the generated Markdown is fresh and within 120 columns. -
web/src/lib/logic/*.test.ts: the pure logic. -
ui/lines.test.ts: every file inui/stays within 120 columns and 300 lines. -
web/src/lib/styles/markdown.test.ts: the Markdown styles stay equal to the app's preview. -
bun run checktype-checksui/webwith its own tsconfig.
The app imports the library as the local package @nibtree/ui; how it moves onto it is in UI-Library-Migration.md.
bun run ui:parity proves each component looks exactly like the app's own UI. Each case in ui/web/src/parity/cases/ holds the app's markup, styled by the CSS read at test time from the app's real .svelte files and the library's styles. Svelte's scoping weight is kept, so rules compete as they do in the app. Next to it sits the library component in the same state. Elements that a child component draws, such as an icon, are marked so the app's scoped rules skip them, as Svelte's scoping does. A browser compares 36 computed properties (stroke width included) of each pair at rest, on hover and on focus, pseudo-elements included, in light and dark. Any difference fails. Known app bugs, where the library is right, are listed with their reason instead, such as the dialog's red confirm button turning blue on hover. Side-by-side screenshots land in ui/web/.parity/, which is not committed.
| Path | What it holds |
|---|---|
ui/design-tokens/*.json |
The design system's values |
ui/design-tokens/build/ |
The model and one writer per platform |
ui/design-tokens/scripts/tokens.ts |
bun run ui:tokens (--check fails on stale files) |
ui/web/src/lib/components/ |
The Svelte components |
ui/web/src/lib/docs/ |
One doc per component and the Markdown writer |
ui/web/src/lib/editor/ |
Editor looks |
ui/web/src/showcase/ |
The local site, stories and foundation pages |
ui/web/scripts/docs.ts |
bun run ui:docs (--check fails on stale pages) |
The library's fixed bugs, each with what you saw, why and the fix, are in UI-Library-Bugs.md.
- Update it when the token files, the generators, the rules or the scripts change.
- Every app screen is on the library now, so the parity list is empty; add a case (
ui/web/src/parity/cases.tsand a page) only when a new piece must match app markup that is not on the library yet.
These pages are generated from docs/wiki. To correct something, open a pull request against the source file; edits made here are overwritten.
Using Nibtree
- Getting Started
- Welcome Screen
- Workspaces
- Files Panel
- File Operations
- File Icons
- Rounded Panels
- Editor and Tabs
- Pin Reorder and Wrap Tabs
- Single Tab Title
- Split Editor
- New File and Unsaved Changes
- Unload Hidden Tabs
- Editing Code
- Code Appearance
- Markdown Editor
- Image and PDF Preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and Replace
- Changes and Commits
- Sign In Prompts
- Commit Box Layout
- Repository Actions
- Commit Options
- Ignoring Files
- Diffs
- Inline Diffs
- Staging View
- Revert a Change
- Resolving Conflicts
- Merge Tool
- Blame
- History and Log
- Merge Commits
- Branches and Tags
- Branches Popup
- Remotes
- Stashes
- Shelf
- Git Menu
- Git Dialogs
- Interactive Rebase
- Worktrees
- AI Agents
- AI Tasks
- AI Usage
- Keep Computer Awake
- Submodules
- Git LFS
- GitHub
- Git Console
- Terminal
- Terminal Features
- Scripts
- Navigation
- Menus
- Settings
- Settings Search
- Color Themes
- Settings Terminal and Automation
- Settings Files
- Updates
- Status Bar and Help
- Notifications
- Memory Use
- Clear Cache
- MCP and CLI
- Git Mergetool
- Keyboard Shortcuts
- Keyboard Shortcuts Editor
- Troubleshooting
- Troubleshooting Git
Developers
- Developer Guide
- Architecture
- Project Layout
- Backend
- How Folder Watching Works
- Folder Watching Bugs We Fixed
- How Workspace Files Work
- How the Preview Scheme Works
- Backend Services
- How Terminal Features Work
- Terminal Bugs We Fixed
- Frontend
- How the Rich Markdown Editor Works
- Rich Markdown Bugs We Fixed
- Git Menu Links
- Menu Keys and Routing
- Frontend Modules
- Blame Bugs We Fixed
- Project Color And Title Bar
- One Diff View
- Diff Bugs We Fixed
- Merge Tool Layouts
- Merge Tool Bugs We Fixed
- Color Theme Bugs We Fixed
- How the Connected Diff Works
- Rounded Panels Bugs We Fixed
- Update Bugs We Fixed
- Status Bar Bugs We Fixed
- MCP and CLI Bugs We Fixed
- Repository Actions Bugs We Fixed
- How the Changes Tab Works
- CodeMirror Patterns
- How the Cursor Works
- How Editor Features Work
- How the Path Bar Works
- Commands and Events
- Settings Reference
- Commands Workspace and Changes
- Commands Branches and Remotes
- Commands History and Extras
- Commands App and Tools
- Testing
- Test Suites
- Debugging
- Debugging with MCP
- Measuring Setting Memory
- How Terminal Memory Is Measured
- How Markdown Diagrams Work
- Releases and CI
- Publishing the Wiki
- Versioning and Changelog
- Browser Version Packages
- Browser Release Checks
- Docker Image
- Platforms and Signing
- Windows Support
- Windows Bugs We Fixed
- Windows MCP and CLI
- Docs and Screenshots
- UI Library
- UI Library MCP
- UI Library Bugs
- UI Library Migration
- Contributing
How features work
- Welcome screen
- Workspaces and folders
- Files panel
- File operations
- File icons
- Editor and tabs
- Pin, reorder and wrap tabs
- Single tab title
- Split editor
- New File and unsaved changes
- Unload hidden tabs
- Editing code
- Code appearance
- Markdown editor
- Image and PDF preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and replace
- Changes and commits
- Sign-in prompts
- Commit box layout
- Repository actions
- Commit options
- Ignoring files
- Diffs
- Inline diffs
- Staging view
- Revert a change
- Resolving conflicts
- Merge tool
- Blame
- History and log
- Merge commits
- Commit tabs
- Branches and tags
- Branches popup
- Remotes
- Stashes
- Shelf
- Git menu
- Git dialogs
- Interactive rebase
- Worktrees
- AI agents
- AI usage
- Keep computer awake
- AI tasks
- Submodules
- Git LFS
- GitHub account
- Git Console
- Back and Forward
- Integrated terminal
- Scripts tool window
- Menu bar
- Settings
- Search in Settings
- Color themes
- Rounded panels
- Updates and release channels
- Status bar and help links
- Notifications
- Memory readout and memory log
- Clear Cache
- MCP server and command line tool
- git mergetool mode