UI UX Pro Max
futureboard/futureboard-studio
Comprehensive design guide for web, mobile, and desktop applications.
How to build a settings page in the Warp desktop client so widgets, page titles and settings search behave correctly, and which common mistakes to avoid.
$ npx skills add warpdotdev/warp --skill gui-settings-ui -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install warpdotdev/warp gui-settings-ui --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/warpdotdev/warp.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/gui-settings-ui .claude/skills/gui-settings-ui && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "gui-settings-ui" agent skill from https://github.com/warpdotdev/warp/tree/master/.agents/skills/gui-settings-ui into .claude/skills/gui-settings-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gui-settings-ui", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/warpdotdev/warp/tree/master/.agents/skills/gui-settings-uiType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add warpdotdev/warp --skill gui-settings-ui -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install warpdotdev/warp gui-settings-ui --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/warpdotdev/warp.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/gui-settings-ui .agents/skills/gui-settings-ui && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "gui-settings-ui" agent skill from https://github.com/warpdotdev/warp/tree/master/.agents/skills/gui-settings-ui into .agents/skills/gui-settings-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gui-settings-ui", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add warpdotdev/warp --skill gui-settings-ui -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install warpdotdev/warp gui-settings-ui --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/warpdotdev/warp.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/gui-settings-ui .cursor/skills/gui-settings-ui && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "gui-settings-ui" agent skill from https://github.com/warpdotdev/warp/tree/master/.agents/skills/gui-settings-ui into .cursor/skills/gui-settings-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gui-settings-ui", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/warpdotdev/warp.git --path .agents/skills/gui-settings-ui--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add warpdotdev/warp --skill gui-settings-ui -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install warpdotdev/warp gui-settings-ui --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/warpdotdev/warp.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/gui-settings-ui .gemini/skills/gui-settings-ui && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "gui-settings-ui" agent skill from https://github.com/warpdotdev/warp/tree/master/.agents/skills/gui-settings-ui into .gemini/skills/gui-settings-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gui-settings-ui", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install warpdotdev/warp gui-settings-uiInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add warpdotdev/warp --skill gui-settings-ui -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/warpdotdev/warp.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/gui-settings-ui .github/skills/gui-settings-ui && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "gui-settings-ui" agent skill from https://github.com/warpdotdev/warp/tree/master/.agents/skills/gui-settings-ui into .github/skills/gui-settings-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gui-settings-ui", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add warpdotdev/warp --skill gui-settings-ui -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install warpdotdev/warp gui-settings-ui --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/warpdotdev/warp.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/gui-settings-ui .opencode/skills/gui-settings-ui && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "gui-settings-ui" agent skill from https://github.com/warpdotdev/warp/tree/master/.agents/skills/gui-settings-ui into .opencode/skills/gui-settings-ui/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gui-settings-ui", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
gui-settings-uiHow to build a settings page in the Warp desktop client so widgets, page titles and settings search behave correctly, and which common mistakes to avoid.
A page is a PageType holding a list of searchable widgets and an optional title, in one of three shapes: uncategorized for a flat widget list, categorized for widgets grouped under subheaders, and monolith for a page that is a single widget because its content cannot be split for search. Each widget implements SettingsWidget with search terms, a render function, a should_render check and an ID used for scroll-to and deeplinks.
Search works per widget: a widget stays visible only if it should render and every word of the query appears, case-insensitively, in its search terms, so a widget is the smallest unit that can disappear. Whether the page title survives a search depends on the page variant, and that is called out as the easiest thing to get wrong. The skill exists because the same two mistakes had already produced five tickets, and it covers choosing a PageType, placing headings, gating widgets and scoping search terms.
The scope is the GUI desktop app's settings modal only. The headless TUI is excluded, and general UI conventions live in the separate gui-ui-guidelines skill.
2 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit f571865. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
cargoFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Warp Settings Page Builder loads about 4.5k tokens when it runs. Until then it costs about 105 tokens; SKILL.md has 2,028 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from warpdotdev/warp at commit f571865, republished under its AGPL-3.0 licence (© warpdotdev). 2,028 words, ~4,548 tokens.
.claude/skills/gui-settings-ui/SKILL.md (or your agent's skills folder).Scope — GUI desktop app only. This skill covers the settings modal in app/src/settings_view/, part of Warp's GUI desktop front-end. It does not apply to the headless TUI (crates/warp_tui). For general UI conventions see gui-ui-guidelines.
Settings pages look simple, so they get written by pattern-matching the nearest neighbor — and the nearest neighbor is often wrong. The same two mistakes have produced five Linear tickets (APP-5060, APP-5058, APP-4910, APP-4922, APP-5059, one of which turned out to be a false positive). Read this before writing a settings page so the sixth doesn't happen.
A settings page is a PageType (app/src/settings_view/settings_page.rs). It holds a list of searchable widgets plus an optional page title:
PageType::new_uncategorized(widgets, Some("Knowledge"))
PageType::new_categorized(categories, None)
PageType::new_monolith(widget, Some("Billing and Usage"), /* is_dual_scrollable */ true)Uncategorized — a flat list of widgets. The common shape.Categorized — widgets grouped under Categorys, each with a subheader (rendered via render_sub_header) and an optional subtitle.Monolith — the whole page is a single widget because its content can't be split for search (Keybindings, Teams, About, Environments).Each widget implements SettingsWidget (settings_page.rs):
search_terms(&self) -> &str — the terms this widget matches on.render(..) — the widget's rows.should_render(&self, app) -> bool — defaults to true. This is one of two ways to conditionally show a setting, and usually not the right one — see "Two ways to gate a widget" below.widget_id() / static_widget_id() — default to std::any::type_name::<Self>(), used for scroll-to and deeplinks.How search filters. PageType::update_filter keeps a widget when
widget.should_render(app) && search_terms_match(widget.search_terms(), query).
search_terms_match requires every whitespace-delimited word of the query to appear (case-insensitively, as a substring) somewhere in the widget's terms; an empty query matches everything. So filtering happens per widget — a widget is the smallest unit that can survive or disappear.
Whether the page title survives a search depends on the variant. PageType::render_page treats the title differently for widget-list pages than for a monolith, and that difference is the easiest thing here to get wrong.
For Uncategorized and Categorized, the title is drawn once, before the loop over the filtered widget list, unconditionally:
let mut page = Flex::column();
if let Some(title) = title {
page.add_child(render_page_title(title, HEADER_FONT_SIZE, appearance));
}
for widget in widgets { /* … only the widgets that matched … */ }The filter cannot reach it. That is what makes the title slot a fix for bug class 1: a title passed through PageType survives filtering on these two variants, while a title rendered inside a widget does not.
For Monolith, the title is gated on the sole widget surviving the filter:
FilteredPageType::Monolith { widget, title, .. } => {
let mut page = Empty::new().finish();
if let Some(widget) = widget // None when the widget didn't match
&& widget.should_render(app)
{
if let Some(title) = title { /* … title, then the widget … */ }
}
page // otherwise nothing renders at all
}get_filtered sets that widget to filter.then_some(..), so a non-matching query makes it None and the page renders empty — title included. On a monolith the title slot buys no search protection; the page is all-or-nothing, title and all.
One more thing worth knowing before you go looking for a title stranded over zero rows: you won't find one. SettingsView::filtered_pages (app/src/settings_view/mod.rs) drops any page whose MatchData is falsy from the sidebar and auto-selects the first page that still matches, so a fully non-matching page is never displayed. The state the title slot actually protects is the partial match — some widgets survive, some don't — and only widget-list pages can be in it.
There are two mechanisms for conditionally showing a setting, and they are not interchangeable.
1. An if at page-build time — never create the widget. This is how most gating in AISettingsPageView::build_page (ai_page.rs) is written:
if FeatureFlag::AIRules.is_enabled() {
widgets.extend(Self::knowledge_widgets());
}
if cfg!(feature = "voice_input")
&& ai_settings.voice_input_enabled_internal.is_supported_on_current_platform()
{
widgets.push(Box::new(VoiceWidget::default()));
}2. should_render(&self, app) -> bool — create the widget and let it opt out per pass.
Which one: can the value change while the app is running?
cfg! feature, a platform-support check. → Use the build-time if. This is the preferred default: the widget never exists, so there is nothing to filter, render, or reason about.should_render.The reason is when each is evaluated. The build-time if runs once, when the page is constructed, and its result is frozen until something rebuilds the page (for AI/Code subpages, only switching subpages does — see below). should_render is re-evaluated on every filter and render pass, so it tracks a value that changes while the settings page is open. Gate a runtime-changing value with a build-time if and the page goes stale; gate a static flag with should_render and you carry a widget around for nothing.
The real should_render users are all the runtime kind: SettingsSyncWidget (main_page.rs) on auth state, WarpDriveToggleWidget (warp_drive_page.rs) on WarpDriveSettings::is_warp_drive_available, and the CLI-agent rich-input widgets (ai_page.rs) on the user-toggleable footer setting, via should_render_cli_agent_rich_input.
Either mechanism keeps search honest: an uncreated widget isn't in the list, and update_filter already skips a widget whose should_render is false. What you must never do is hide rows inside render while search_terms still advertises them — that makes the page match a query and then show nothing for it.
For any heading on a settings page, ask what does it name?
PageType title slot. On Uncategorized / Categorized that is also what keeps it on screen while the page is filtered; on a Monolith it is a structural choice only (see above).Category) that owns that section, so it disappears together with its own rows.Getting this wrong in the first direction is bug class 1 below. Getting it wrong in the second direction leaves a section header stranded above unrelated rows.
Classify the page before you write it:
PageType slot.Categorized, get_filtered drops categories whose widgets all filtered out, so their subheaders vanish automatically — that's the behavior you want.)CodePageWidget, wrapped by CodeIndexingPageWidget) — building it as Uncategorized instead of Monolith made the sidebar show a permanent, misleading "(1)" on any match ([APP-5530]).The two are not mutually exclusive: a page can name itself in the title slot and have per-section subheaders inside its widgets. Privacy does exactly that — PageType::new_uncategorized(widgets, Some("Privacy")) plus render_sub_header calls inside individual widgets. The rule is per heading, not per page.
Worked positive example: Scripting (scripting_page.rs) is a small page done right — two focused widgets (WarpControlCliInstallWidget, LocalControlModeWidget) with their own search_terms, and PageType::new_uncategorized(widgets, Some("Scripting")). A ticket was once filed claiming Scripting needed splitting; it was canceled because the premise was wrong. A small page is not automatically a mega-widget.
Symptom: on a single-topic Uncategorized or Categorized page, typing a search term that matches one row makes the page heading disappear along with the non-matching rows, leaving an unlabeled orphan setting. (A monolith can't reach this state — see the taxonomy above.)
Cause: the only heading was rendered inside a widget — via build_sub_header / render_page_title in that widget's render, or via a header-only widget that exists just to draw a title. Filtering removes the widget, and the heading goes with it.
Canonical fix — commit ddadcee ([APP-5060], #14519), Knowledge. Before, AIFactWidget::render opened with:
let header = build_sub_header(appearance, "Knowledge", …).finish();
let mut column = Flex::column().with_child(header) /* … all the Knowledge rows … */;After, the heading moved to page chrome and the rows became focused widgets:
let title = match subpage {
AISubpage::Knowledge => Some("Knowledge"),
AISubpage::ThirdPartyCLIAgents => Some("Third party CLI agents"),
AISubpage::WarpAgent | AISubpage::Profiles => None,
};
PageType::new_uncategorized(widgets, title)(The ThirdPartyCLIAgents arm and the exhaustive match came from #14524; note the deliberate absence of a _ arm, so a new subpage forces this decision.)
The same commit (#14524) deleted CodeSubpageHeaderWidget from the then-combined Code page — a widget whose entire job was build_sub_header(appearance, self.title, None) — and replaced it with a title passed through PageType. Those two halves are now separate pages, code_indexing_page.rs and code_editor_review_page.rs, each passing its own PAGE_TITLE through the slot. A header-only widget is always this bug. If a widget renders nothing but a title, delete it and use the title slot.
Symptom: searching a term that should isolate one row shows every row on the page, and the sidebar match count is 1 no matter how specific the query is.
Cause: one widget renders many unrelated settings and declares one mega search_terms() blob covering all of them. The widget is the filter unit, so it's all-or-nothing.
Before (CLIAgentWidget, pre-#14524) — one widget, one blob, seven settings:
fn search_terms(&self) -> &str {
"third party cli coding agent claude codex gemini toolbar footer layout chip chips \
rearrange re-arrange bar command regex auto show rich input dismiss ctrl enter submit newline"
}After — one widget per setting, each with terms scoped to just that setting:
fn cli_agent_widgets() -> Vec<Box<dyn SettingsWidget<View = AISettingsPageView>>> {
vec![
Box::new(CLIAgentWidget::default()),
Box::new(CLIAgentAutoToggleRichInputWidget::default()),
Box::new(CLIAgentAutoOpenRichInputWidget::default()),
Box::new(CLIAgentAutoDismissRichInputWidget::default()),
Box::new(CLIAgentSubmitRichInputWidget::default()),
Box::new(CLIAgentCommandsWidget),
Box::new(CLIAgentToolbarLayoutWidget),
]
}Rules of thumb when splitting:
search_terms to that widget only. Keep enough shared context that a page-level query still matches (each CLI-agent widget above keeps "third party cli coding agent"), then add the terms unique to the row.if … { … } inside a mega-render becomes its own widget: if its condition is a static feature flag or platform check, gate it with an if in the page's build function and never create it; if it can change at runtime, give it should_render. The CLI-agent rich-input rows are the runtime case — they hang off the user-toggleable footer setting — so they use should_render and share the predicate through a small free function (should_render_cli_agent_rich_input(app)) rather than duplicating the condition.SwitchStateHandle / MouseStateHandle moves to the widget that owns its control. Never create one inline while rendering (see gui-ui-guidelines and the AGENTS.md note on MouseStateHandle).widget_id() is std::any::type_name::<Self>(), so splitting a widget changes ids. settings_widget_deeplink_target in app/src/settings_view/mod.rs maps stable public slugs (warp://settings?widget=<slug>) onto them. The CLI-agent split deliberately kept CLIAgentWidget as the first widget so cli_agent_settings_widget_id() — the target of the cli_agents deeplink — stayed valid. If you rename or remove a widget that backs a deeplink, re-point the accessor.PageType — reapply the filterAI subpages rebuild their PageType when the active subpage changes (AISettingsPageView::set_active_subpage / build_page). A fresh PageType starts with every widget in its filter, so a live search query is silently dropped unless it's reapplied. SettingsView::reapply_search_filter_to_active_subpage in app/src/settings_view/mod.rs exists for exactly this ([APP-4922], #14116). If you add a code path that rebuilds a subpage's page while search may be active, call it.
The Code umbrella no longer works this way: CodeIndexing and EditorAndCodeReview are separate pages that each own their widgets outright, so nothing rebuilds and there is no filter to reapply. Prefer that shape for new umbrella children — a subpage that owns its own page needs none of this machinery.
Useful to read, not to copy:
warpify_page.rs) — PageType::new_categorized(categories, None) where the first category is Category::new("", vec![Box::new(TitleWidget::default())]) and TitleWidget::render calls render_page_title("Warpify", …). Single-topic page, title inside a widget: bug class 1.warp_drive_page.rs) — PageType::new_uncategorized([WarpDriveHeaderWidget, WarpDriveToggleWidget], None) with no title slot at all. WarpDriveHeaderWidget is a conditional sign-up banner, so a signed-in user sees only a bare toggle row and no page heading.Verification is cheap here; do both.
app/src/settings_view/mod_tests.rs has the harness — StubWidget, stub_widgets_page, and visible_widget_count (which reads PageType::get_filtered). Assert that a term unique to one widget yields exactly one visible widget, and that clearing the query restores all of them. Follow the ${filename}_tests.rs convention from AGENTS.md; run with cargo nextest run -p warp settings_view.Uncategorized / Categorized page; a monolith correctly disappears whole instead,// A widget whose whole job is to draw the page heading. Delete it and pass the
// title through PageType instead.
struct FooSubpageHeaderWidget { title: &'static str }
// A single-topic page with no page title, drawing its heading inside a widget.
// Any non-matching search term erases the heading.
PageType::new_uncategorized(widgets, None) // and build_sub_header("Foo", …) in a widget
// One widget, one mega search_terms blob, many unrelated rows: nothing can be
// filtered or attributed individually.
fn search_terms(&self) -> &str { "foo bar baz qux quux corge grault" }
// Conditional rows hidden inside render() while search_terms still advertises
// them — the page matches a query and then renders nothing for it.
fn render(&self, …) { if flag_enabled { /* the only rows */ } }
// Gate the widget instead: an `if` at page-build time for a static flag, or
// should_render() when the condition can change at runtime.© warpdotdev, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/gui-settings-ui of warpdotdev/warp.
Open the folder on GitHubat commit f571865
We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in warpdotdev/warp, which our catalogue first saw on October 7, 2026.
Warp Settings Page Builder next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Warp Settings Page Builder this skillwarpdotdev/warp | 65k | 1 repos | ~4.5k | Automated safety check: Pass | AGPL-3.0 | |
| UI UX Pro Maxfutureboard/futureboard-studio | 106 | — | ~5.6k | Automated safety check: Pass | MIT | |
| UI PolishJereIDE/JereIDE | 175 | — | ~729 | Automated safety check: Pass | MIT | |
| Engraphdevwhodevs/engraph | 171 | — | ~792 | Automated safety check: Pass | MIT | |
| Worklog Designregisx001/Worklog | 258 | — | ~3.3k | Automated safety check: Pass | MIT | |
| Liteyuki Webui FrontendLiteyukiStudio/LiteyukiBot | 157 | — | ~2.5k | Automated safety check: Pass | Custom licence |
futureboard/futureboard-studio
Comprehensive design guide for web, mobile, and desktop applications.
JereIDE/JereIDE
Suggests 10 very minor UI polish improvements, picks the best one, implements it, verifies it builds, then proceeds to the next until all 10 are done.
devwhodevs/engraph
Index and search document collections using hybrid semantic + graph + full-text search.
regisx001/Worklog
Design and UI skill for the Worklog desktop project manager.
LiteyukiStudio/LiteyukiBot
Build, review, or test LiteyukiBot v7's React/Vite WebUI under webui/ and its packaged static delivery in packages/webui/.
MarchLiu/hypatia
Interact with the Hypatia AI memory system using natural language.
warpdotdev/warp
Builds or updates a design system in Figma from a codebase in ordered phases: discovery, variables and tokens, components, theming and documentation, with checkpoints.
warpdotdev/warp
Required groundwork before any use_figma call: the rules and reference files for running JavaScript in a Figma file through the Plugin API without common failures.
warpdotdev/warp
Authors and edits file-based Warp software factory definitions rooted at factory.yaml, covering agents, automations, scorers and webhooks, and validates them before a pull request.
warpdotdev/warp
Turns a Figma frame or component into production code that matches the design, using the Figma MCP server and the project's own design system.
warpdotdev/warp
Migrates the compatible subset of settings and global file-based MCP servers from the Warp desktop app into Warp Agent CLI without exposing credentials or state.
warpdotdev/warp
Creates project-specific design system rules from your codebase so coding agents implement Figma designs with your components, naming and tokens.
Works with
Categories
How to build a settings page in the Warp desktop client so widgets, page titles and settings search behave correctly, and which common mistakes to avoid. A page is a PageType holding a list of searchable widgets and an optional title, in one of three shapes: uncategorized for a flat widget list, categorized for widgets grouped under subheaders, and monolith for a page that is a single widget because its content cannot be split for search. Each widget implements SettingsWidget with search terms, a render function, a should_render check and an ID used for scroll-to and deeplinks.
Warp Settings Page Builder fits situations like: adding or editing a settings page in the Warp client; writing a new SettingsWidget with correct search terms; deciding whether a heading belongs in the page title or inside a widget; conditionally showing a setting on a settings page.
Run `npx skills add warpdotdev/warp --skill gui-settings-ui -a claude-code`. Or copy the skill folder (.agents/skills/gui-settings-ui in warpdotdev/warp) into .claude/skills/gui-settings-ui in your project. Claude Code loads it when a task matches its description.
Run `npx skills add warpdotdev/warp --skill gui-settings-ui -a codex`. Or copy the skill folder (.agents/skills/gui-settings-ui in warpdotdev/warp) into .agents/skills/gui-settings-ui in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add warpdotdev/warp --skill gui-settings-ui -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/gui-settings-ui, .gemini/skills/gui-settings-ui, .github/skills/gui-settings-ui and .opencode/skills/gui-settings-ui in your project.
Going by SKILL.md and its folder, Warp Settings Page Builder needs the command-line tools its instructions call (cargo). Our summary lists: A checkout of the Warp repository.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Warp Settings Page Builder is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.5k tokens (SKILL.md is roughly 18k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Warp Settings Page Builder: UI UX Pro Max (futureboard/futureboard-studio, 106 stars), UI Polish (JereIDE/JereIDE, 175 stars), Engraph (devwhodevs/engraph, 171 stars) and Worklog Design (regisx001/Worklog, 258 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
warpdotdev (a GitHub organization) maintains it in warpdotdev/warp, which has 65,380 GitHub stars. The repository holds 46 skills in this directory. The repository was last updated on October 7, 2026.
Source: warpdotdev/warp on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.