Agent skill

UI Features

by marcus in marcus/sidecar

Implementing UI/UX features in sidecar including modals (internal/modal library), keyboard shortcuts, mouse support, scrolling, pill/tab rendering, and pane resizing.

MITAuto-check passedFrontend & Design

Install UI Features

skills CLI
$ npx skills add marcus/sidecar --skill ui-features -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install marcus/sidecar ui-features --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/marcus/sidecar.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/ui-features .claude/skills/ui-features && rm -rf skills-src

Use ~/.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/

Facts

Skill name
ui-features
GitHub stars
1.1k
Token cost
~4.6k tokens
SKILL.md length
1,855 words
Files
2 (incl. references)
Skills in repo
19
Repo updated
First seen
Licence
MIT

At a glance

Implementing UI/UX features in sidecar including modals (internal/modal library), keyboard shortcuts, mouse support, scrolling, pill/tab rendering, and pane resizing.

  • Works in 3 steps: Returns early if required state is missing → Caches based on width to avoid… → Creates the modal only when needed
  • Implementing UI features
  • SKILL.md covers Quick Checklist, Modals (internal/modal), Pill-Shaped Elements… and Keyboard Shortcuts, plus 5 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

UI Features is an agent skill from marcus/sidecar. Implementing UI/UX features in sidecar including modals (internal/modal library), keyboard shortcuts, mouse support, scrolling, pill/tab rendering, and pane resizing. Use when implementing UI features, handling user input, adding keyboard shortcuts, building modals, or working on UX improvements.

Its SKILL.md is about 4.6k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files, including reference files (for example `references/keyboard-shortcuts-reference.md`).

It sits in Frontend & Design, covering UI design. The repository describes itself as: Use sidecar next to CLI agents for diffs, file trees, conversation history, and task management with td. The licence is MIT.

When your agent uses it

  • Implementing UI features
  • Handling user input
  • Adding keyboard shortcuts
  • Building modals

Example prompts

  • “/ui-features”

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. Returns early if required state is missing
  2. Caches based on width to avoid rebuilding every frame
  3. Creates the modal only when needed

What it can do on your machine

Read from SKILL.md and the folder at commit 3792a4e. It shows what the files ask for, not the result of running them.

  • Tool permissions

    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.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are go).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

UI Features loads about 4.6k tokens when it runs, and up to ~6.5k if it reads all its reference files. Until then it costs about 77 tokens; SKILL.md has 1,855 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~77
When it runs · the whole SKILL.md, loaded when a task matches
~4.6k
With references · SKILL.md plus every file in references/, read only if the agent opens them
~6.5k

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.

Safety

Auto-check passed

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.

SKILL.md

The full file from marcus/sidecar at commit 3792a4e, republished under its MIT licence (© marcus). 1,855 words, ~4,611 tokens.

Download SKILL.mdSave it as .claude/skills/ui-features/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
name
ui-features
description
Implementing UI/UX features in sidecar including modals (internal/modal library), keyboard shortcuts, mouse support, scrolling, pill/tab rendering, and pane resizing. Use when implementing UI features, handling user input, adding keyboard shortcuts, building modals, or working on UX improvements.

UI Feature Implementation

Single entry point for sidecar UI work. All new modals must use internal/modal. For complete keyboard shortcut listings, see references/keyboard-shortcuts-reference.md.

Quick Checklist

  • Modals: use internal/modal, render with ui.OverlayModal, avoid manual hit region math
  • Pills/chips/tabs: use styles.RenderPillWithStyle; auto-fallback when nerdFontsEnabled is false
  • Keyboard: Commands + FocusContext + bindings must match; names short; priorities set
  • Mouse: rebuild hit regions on each render; add general regions first, specific last
  • Rendering: keep output within View width/height to avoid header/footer overlap. Use contentHeight := height - headerLines - footerLines
  • Testing: verify keyboard, mouse, hover, scrolling, and footer hints
  • Plugins must NOT render their own footer -- the app renders a unified footer from Commands()

Modals (internal/modal)

All new modals must use internal/modal. See docs/guides/deprecated/declarative-modal-guide.md for the full API.

Create a modal
go
m := modal.New("Delete Worktree?",
    modal.WithWidth(58),
    modal.WithVariant(modal.VariantDanger),
    modal.WithPrimaryAction("delete"),
).
    AddSection(modal.Text("Name: " + wt.Name)).
    AddSection(modal.Spacer()).
    AddSection(modal.Buttons(
        modal.Btn(" Delete ", "delete", modal.BtnDanger()),
        modal.Btn(" Cancel ", "cancel"),
    ))
Render in View
go
func (p *Plugin) renderDeleteView(width, height int) string {
    background := p.renderListView(width, height)
    rendered := p.deleteModal.Render(width, height, p.mouseHandler)
    return ui.OverlayModal(background, rendered, width, height)
}
Handle input in Update
go
case tea.KeyMsg:
    action, cmd := p.deleteModal.HandleKey(msg)
    if action != "" {
        return p.handleModalAction(action)
    }
    return p, cmd

case tea.MouseMsg:
    action := p.deleteModal.HandleMouse(msg, p.mouseHandler)
    if action != "" {
        return p.handleModalAction(action)
    }
    return p, nil
Modal initialization and caching (critical)

Always call ensureModal() in BOTH View and Update handlers. Create an ensure function that:

  1. Returns early if required state is missing
  2. Caches based on width to avoid rebuilding every frame
  3. Creates the modal only when needed
go
func (p *Plugin) ensureMyModal() {
    if p.targetItem == nil {
        return
    }
    modalW := 50
    if modalW > p.width-4 { modalW = p.width - 4 }
    if modalW < 20 { modalW = 20 }
    if p.myModal != nil && p.myModalWidthCache == modalW {
        return
    }
    p.myModalWidthCache = modalW
    p.myModal = modal.New("Title", modal.WithWidth(modalW), ...).
        AddSection(...)
}

The key handler MUST call ensure before checking nil:

go
func (p *Plugin) handleMyModalKeys(msg tea.KeyMsg) tea.Cmd {
    p.ensureMyModal()  // CRITICAL: Initialize before nil check
    if p.myModal == nil { return nil }
    action, cmd := p.myModal.HandleKey(msg)
    return cmd
}
Async content invalidation

When modal content depends on async data, invalidate the cache when data arrives:

go
case MyDataLoadedMsg:
    p.myData = msg.Data
    p.clearMyModal()  // Force rebuild with new content
    return p, nil

Modals need their own focus context and commands for footer hints:

  1. Return a dedicated context from FocusContext()
  2. Add commands for the modal context in Commands()
  3. Add bindings in internal/keymap/bindings.go
  4. Intercept custom keys before modal.HandleKey (Tab/Enter/Esc are handled internally)
go
func (p *Plugin) FocusContext() string {
    switch p.viewMode {
    case ViewModeError:  return "git-error"
    case ViewModePushMenu: return "git-push-menu"
    default: return "git-status"
    }
}
Modal notes
  • HandleKey/HandleMouse handle Tab, Shift+Tab, Enter, Esc internally
  • Backdrop clicks return "cancel"; use WithCloseOnBackdropClick(false) to disable
  • Use built-in sections (Text, Input, Textarea, Buttons, Checkbox, List, When) before custom layouts
  • For bespoke layouts, use modal.Custom and return explicit focusable offsets
  • SetFocus(id) auto-scrolls viewport to focused element
  • Prefer ui.OverlayModal(background, modal, width, height) for dimmed overlays; do not pre-center with lipgloss.Place
Background colors (critical)

Lipgloss Background() does not cascade into child content. ANSI resets clear the parent background. Solution: replace ANSI resets within viewport lines with reset + background re-apply, then pad short lines. See fillBackground in internal/modal/layout.go.

Pill-Shaped Elements (internal/styles)

Controlled by nerdFontsEnabled in ~/.config/sidecar/config.json (ui.nerdFontsEnabled).

go
// With explicit colors
label := styles.RenderPill("Output", styles.TextPrimary, styles.Primary, "")

// With a lipgloss.Style (preferred for tabs/chips)
active := styles.RenderPillWithStyle("Output", styles.BarChipActive, "")
inactive := styles.RenderPillWithStyle("Diff", styles.BarChip, "")

Available styles: styles.BarChip (inactive), styles.BarChipActive (active), or custom lipgloss.Style.

Test with both nerdFontsEnabled: true and false to verify fallback.

Keyboard Shortcuts

For complete per-plugin shortcut listings, see references/keyboard-shortcuts-reference.md.

Three things must match
  1. Command ID in Commands() (e.g., "stage-file")
  2. Binding command in internal/keymap/bindings.go (e.g., "stage-file")
  3. Context string in both places (e.g., "git-status")
go
// 1) Commands()
{ID: "stage-file", Name: "Stage", Context: "git-status", Priority: 1}

// 2) FocusContext()
func (p *Plugin) FocusContext() string { return "git-status" }

// 3) bindings.go
{Key: "s", Command: "stage-file", Context: "git-status"}
Multiple contexts (view modes)

Return different context strings from FocusContext() for different modes. Each context gets its own footer hints and key bindings.

Priority guidelines
  • 1: Primary actions (Stage, Commit, Open)
  • 2: Secondary actions (Diff, Search, Push)
  • 3: Tertiary actions (History, Refresh)
  • 4+: Palette only
Root contexts (q behavior)

In root contexts, q shows quit confirmation. In non-root, q navigates back. Root contexts: global, conversations, conversations-sidebar, git-status, git-status-commits, git-status-diff, file-browser-tree, workspace-list, td-monitor.

Update isRootContext() in internal/app/update.go when adding new contexts.

Text input contexts

When a view has text input, implement plugin.TextInputConsumer and return true while active. This prevents app-level shortcuts from intercepting typed characters.

go
func (p *Plugin) ConsumesTextInput() bool {
    return p.showMyModal
}
footerHints()
    +-- pluginFooterHints() -> Commands() filtered by FocusContext(), sorted by Priority
    +-- globalFooterHints() -> App-level hints
renderHintLineTruncated(hints, availableWidth)
    -> Renders left-to-right until width exceeded
Keyboard checklist
  • Command in Commands() with ID, Name, Context, Priority
  • FocusContext() returns matching context
  • Binding in internal/keymap/bindings.go
  • Key handled in Update() if app does not intercept
  • No conflicting keys in same context
  • Short footer hint names, primary actions Priority 1-2
  • Verify q behavior with isRootContext()
Core files
FilePurpose
internal/plugin/plugin.goCommand struct, Commands(), FocusContext(), TextInputConsumer
internal/keymap/bindings.goDefault key-to-command mappings
internal/keymap/registry.goRuntime binding lookup
internal/app/update.goKey routing, isRootContext()
internal/app/view.goFooter rendering

Scrollbar (internal/ui)

go
rendered, geom := ui.RenderScrollbarWithState(ui.ScrollbarParams{
    TotalItems:   len(items),
    ScrollOffset: p.scrollOffset,
    VisibleItems: visibleCount,
    TrackHeight:  height,
}, style) // ui.ScrollbarStyle{Thumb: state, Track: state}; zero value renders byte-identically to RenderScrollbar

Every scrollbar backed by real scroll state is interactive under the mouse (macOS-style), including the terminal panes' bars over captured scrollback. Terminal surfaces map presses and drags through tty.WindowScrollbarFor onto the shared window model: the gesture freezes the window at an absolute start, release thaws (offset zero follows again), and history loads defer to release so a mid-gesture renumber cannot shift the mapping. Adoption ledger: docs/plans/active/mouse-draggable-scrollbars.md ("Adoption outcome").

Pattern: reduce content width by 1, render content, render scrollbar, join horizontally with lipgloss.JoinHorizontal(lipgloss.Top, content, scrollbar).

For multi-line items, set TrackHeight to actual terminal rows: visibleCount * linesPerItem.

Mouse contract per surface:

  1. Register ui.RegionScrollbarTrack, then ui.RegionScrollbarThumb, after content regions in the same render pass that builds the HitMap (reverse scan gives the bar its column). Register nothing when geom.HasThumb is false — that column is a spacer.
  2. Thumb press = grab at that row; track press = jump-to-spot anchored at the grabbed row; both end in StartDrag so releasing anywhere settles cleanly. Drag maps through ui.OffsetAtRow against the press-time params snapshot; past-end clamps never end the gesture.
  3. A rapid second press arrives as ActionDoubleClick: answer it like ActionClick (re-grab), never swallow it.
  4. Emphasis derives via ui.HandleStateFrom(hovering, dragging) — intensity modulation on the existing theme keys, no new keys.

Wheel boundaries (required for every new scrollable surface)

Trackpad and Magic Mouse flicks emit hundreds of inertial wheel events. Bubble Tea repaints all of Sidecar after every accepted one, so clamping an offset during Update is too late — the freeze happens before the clamp helps. tea.WithFilter(app.FilterInput) asks one read-only question before Update and View:

Would this exact wheel event change the surface currently under the pointer?

Rule: every new scrollable surface or modal must provide exact pre-update bounds, or explicitly declare why its answer is unknown. "Unknown" is a valid, safe answer — guessing is not. Return true only when the event is a certain no-op.

How to comply:

  1. Implement plugin.WheelBoundaryConsumer on the plugin (WheelAtBoundary(tea.MouseWheelMsg) bool) and add var _ plugin.WheelBoundaryConsumer = (*Plugin)(nil).
  2. Mirror handleMouseScroll's routing exactly — same hit map, same modal precedence — but load nothing, move nothing, render nothing.
  3. Derive the maximum from the same helper the renderer clamps with (internal/scroll.Bounds), never a second copy of the arithmetic.
  4. Declarative modals answer for themselves via modal.WheelAtBoundary(msg, handler); the host only owns precedence between a modal, a nested overlay, and a custom scrolling child. Call Invalidate() when content or geometry changes so a stale layout answers unknown instead of wrong.
  5. Return false (unknown) for: embedded models you do not own, tmux panes with mouse reporting, scrollback with unloaded history, lazy lists that can load more, and anything before its first trustworthy render.
  6. Declare the surface's policy in assembly.WheelBoundaryRegistry (covered / externally-owned / deprecated-exclusion). A new plugin without a row fails the assembly tests; a new ModalKind without a row fails TestEveryModalKindHasALedgerRow in internal/app.
  7. Prove it with the shared stress fixture internal/scroll/scrolltest: scrolltest.Run(t, scrolltest.Tail{...}) feeds hundreds of same-direction events and one reverse event, with no sleeps.

Background: docs/plans/active/scroll-inertia-complete-coverage.md.

Show full SKILL.md (778 more words)Show less

Wheel burst coalescing (required for every new scrollable surface)

Boundary drop alone is half the protection. A flick that never reaches a boundary — mid-range inertia over a long card — still delivers one event per notch, and each accepted one is an Update + View repaint. tty.WheelBurst / tty.WheelBursts (internal/tty/wheel.go) coalesce a flick into whole flushes: "how much of this burst has the surface earned" lives in that one place, so a flick travels the same distance on every surface.

Rule: every new scrollable content area applies wheel deltas through the shared burst guard and drops at boundaries — both, not either.

How to comply:

  1. Hold one tty.WheelBurst per scroll surface. A host with one surface embeds the value in whatever struct owns it (internal/overview/preview.go, workspace issuePane); a host with several uses tty.WheelBursts keyed per leaf/region (Files tree + preview, app content deck).
  2. Apply through Add(delta, now); when ok is false the event was held, change nothing, and return early. The held delta rides the next flush — never apply delta directly.
  3. Inject the clock for tests: a wheelNow func() time.Time field (Files), the plugin clock (p.now()), or a Model hook (notificationCentreNow). Tests drive a whole flick without sleeping — see internal/plugins/filebrowser/scroll_burst_test.go and the issue-viewer equivalents beside each host.
  4. Reset the burst wherever the boundary answer is true, so inertia dropped at the top or bottom cannot spend itself into the next gesture. Crossing surfaces with WheelBursts.For resets automatically.
  5. Keyboard scrolling does not coalesce; do not route keys through the burst.

Reference adoptions: embedded terminals (internal/tty/tty.go), Files tree and preview (internal/plugins/filebrowser), workspace terminal + issue panes, overview terminal + issue preview, app content deck + notification centre + issue preview modal.

Mouse Support

Setup
go
type Plugin struct {
    mouseHandler *mouse.Handler
}
func New() *Plugin {
    return &Plugin{mouseHandler: mouse.NewHandler()}
}
Register hit regions during render
go
func (p *Plugin) View(width, height int) string {
    p.mouseHandler.Clear()
    p.mouseHandler.HitMap.AddRect("pane", 0, 0, width, height, nil)
    p.mouseHandler.HitMap.AddRect("item", 2, 5, width-4, 1, 0)
    return content
}
Region ordering (critical)

Regions tested in reverse order. Add general regions first, specific regions last.

Pane header controls (internal/ui + internal/panereposition)

A pane header's right edge carries two controls: the layout button ⊞ (U+229E) and the close ×. Both are drawn through ui.ResolveButtonStyle with the same one-cell padding, so a pane header never invents a third button look, and both are gated on the pane_move feature for the layout button's half.

Reserve, do not measure. ui.ReserveHeaderControls(width, controls ...HeaderControl) returns the tab strip's usable width and each control's column in one call; panereposition.ReserveMovableHeader(width, movable, hasClose) is the pane-side wrapper. Each host binds it once per frame in its own reserveHeader/composeHeader pair (workspace.Plugin, overview.Model, app.appContentDeck) so its header renderer, its tab strips and its region sink all measure from one answer — a strip laid out for a different reserve than the header composes is how a tab click lands on the wrong tab.

movable comes from the tree, via panereposition.Movable: it is false with no tree and false for a tree of one leaf. PlanMove refuses every destination on a single leaf, so a control there is a target the user aims at for nothing, and a header with no leaf at all used to compare hover against leaf 0 — which every un-hovered header matches — and paint a permanently hovered button. ui.ReserveHeaderClose remains a one-line compatibility wrapper for headers that carry only the ×. The arithmetic assumes a one-column glyph and the test suite pins ansi.StringWidth of the rendered label; a wider glyph silently shifts every hit region on the row.

Drop order as the row narrows: the layout button goes first, the close × last. A control is dropped all-or-nothing. A half-drawn control is a target whose meaning cannot be recovered, and the close button is the one a user cannot work around.

The Layout region rung is after Title and before Close. paneframe.RegionSink registers Layout(node *panelayout.Node, hit Box) at that rung for the same reason RegisterRegions documents for the close button, one step earlier: regions are tested in reverse order, so the later-registered Close wins the cell it actually occupies while Layout still outranks the title strip underneath it. All three pane hosts implement it beside their close-region binding, with the same hover tracking.

The button opens panereposition.Controller — the same modal M opens. See .claude/skills/keyboard-shortcuts/SKILL.md for the key and its target resolution, and .claude/skills/drag-pane/SKILL.md for where the region sits in the windowing model.

Coordinate system

App offsets Y by headerHeight (the single painted header row) before forwarding to plugins. Plugins operate in local coords where Y=0 is plugin content top.

Common patterns
  • Click to select/focus, scroll wheel to move, double-click to open
  • Drag regions for pane resizing
  • Hover for visual feedback (focus takes precedence)
Mouse troubleshooting
SymptomFix
Clicks don't registerCheck region order (pane first)
Y offsets wrongAccount for borders, padding, headers
Scroll over items brokenInclude item regions in scroll routing
Double-click failsEnsure consistent region ID/bounds
Drag brokenCall StartDrag on click, check DragRegion during drag

© marcus, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 1 other file (references) in .claude/skills/ui-features of marcus/sidecar.

  • SKILL.md
  • references/keyboard-shortcuts-reference.md

Open the folder on GitHubat commit 3792a4e

Compare with similar skills

UI Features 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.

UI Features compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
UI Features this skillmarcus/sidecar1.1k—~4.6kAutomated safety check: PassMIT
UI StylingOhh-889/skyroc79513 repos~2.5kAutomated safety check: PassMIT
LobeHub Interactive Prototypelobehub/lobehub83k—~1.6kAutomated safety check: PassCustom licence
Make Interfaces Feel Bettersamuelclay/NewsBlur7.6k10 repos~1.5kAutomated safety check: PassMIT
UI UX Pro MaxZxBing0066/pixel-converter18113 repos~2.6kAutomated safety check: NotesBSD-2-Clause
Stitch Prompt Enhancergoogle-labs-code/stitch-skills8.4k6 repos~1.7kAutomated safety check: PassApache-2.0

Similar skills

  • UI Styling

    Ohh-889/skyroc

    Create beautiful, accessible user interfaces with shadcn/ui components (built on Radix UI + Tailwind), Tailwind CSS utility-first styling, and canvas-based visual designs.

    795 GitHub starsUsed in 13 repos~2.5k tokens
    Frontend & DesignAuto-check passed
  • Builds single-file interactive HTML prototypes rendered with the real LobeHub UI components and written as production-style React, so they can later be split into files.

    83k GitHub stars~1.6k tokensUpdated today
    Frontend & DesignAuto-check passed
  • Make Interfaces Feel Better

    samuelclay/NewsBlur

    Design engineering principles for making interfaces feel polished.

    7.6k GitHub starsUsed in 10 repos~1.5k tokens
    Frontend & DesignAuto-check passed
  • UI UX Pro Max

    ZxBing0066/pixel-converter

    UI/UX design intelligence with searchable database. An agent skill from ZxBing0066/pixel-converter.

    181 GitHub starsUsed in 13 repos~2.6k tokens
    Frontend & DesignAuto-check: notes
  • Stitch Prompt Enhancer

    google-labs-code/stitch-skills

    Official

    Rewrites a vague UI generation idea into a structured, keyword-rich prompt for Stitch, pulling in an existing DESIGN.md design system when the project has one.

    8.4k GitHub starsUsed in 6 repos~1.7k tokens
    Frontend & DesignAuto-check passed
  • Design Dna

    zanwei/design-dna

    Extract, define, and apply design DNA across three dimensions: design system (tokens), design style (qualitative feel), and visual effects (Canvas, WebGL, 3D, particles, shaders, scroll effects…

    1.9k GitHub starsUsed in 1 repo~2.1k tokens
    Frontend & DesignAuto-check passed

More from marcus/sidecar

All 19 skills in this repo
  • Create Adapter

    marcus/sidecar

    Create conversation adapters for importing AI chat history from different tools (Claude Code, Cursor, Warp, Codex, etc.).

    1.1k GitHub stars~4.8k tokensUpdated 2 days ago
    Auto-check passed
  • Create Modal

    marcus/sidecar

    Create declarative modals using the modal library API. An agent skill from marcus/sidecar.

    1.1k GitHub stars~2.6k tokensUpdated 2 days ago
    Auto-check passed
  • Create Plugin

    marcus/sidecar

    Create new sidecar plugins implementing the plugin.Plugin interface, rendering views with Bubble Tea, handling keyboard input via keymap contexts, and integrating with the app shell (footer hints…

    1.1k GitHub stars~2.3k tokensUpdated 2 days ago
    Auto-check passed
  • Project Switching

    marcus/sidecar

    Project switching implementation in sidecar: project discovery, state management, UI flow, modal rendering, filtering, theme preview, and plugin reinitialization.

    1.1k GitHub stars~1.8k tokensUpdated 2 days ago
    Auto-check passed
  • Shell Integration

    marcus/sidecar

    Interactive shell/TTY integration with tmux session management, shell command execution, control-mode output capture with polling fallback, native cursor rendering, lazy scrollback, selection, paste…

    1.1k GitHub stars~3.6k tokensUpdated 2 days ago
    Auto-check passed
  • Sidecar Website

    marcus/sidecar

    Writing and maintaining the Sidecar Docusaurus documentation site, including page structure, doc authoring, blog posts, styling, images, and deployment workflow.

    1.1k GitHub stars~1.7k tokensUpdated 2 days ago
    Auto-check passed

Questions about UI Features

What does UI Features do?

Implementing UI/UX features in sidecar including modals (internal/modal library), keyboard shortcuts, mouse support, scrolling, pill/tab rendering, and pane resizing. UI Features is an agent skill from marcus/sidecar. Implementing UI/UX features in sidecar including modals (internal/modal library), keyboard shortcuts, mouse support, scrolling, pill/tab rendering, and pane resizing.

When should I use UI Features?

UI Features fits situations like: implementing UI features; handling user input; adding keyboard shortcuts; building modals.

How do I install UI Features in Claude Code?

Run `npx skills add marcus/sidecar --skill ui-features -a claude-code`. Or copy the skill folder (.claude/skills/ui-features in marcus/sidecar) into .claude/skills/ui-features in your project. Claude Code loads it when a task matches its description.

How do I install UI Features in Codex?

Run `npx skills add marcus/sidecar --skill ui-features -a codex`. Or copy the skill folder (.claude/skills/ui-features in marcus/sidecar) into .agents/skills/ui-features in your project. Codex loads it when a task matches its description.

Can I use UI Features in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add marcus/sidecar --skill ui-features -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/ui-features, .gemini/skills/ui-features, .github/skills/ui-features and .opencode/skills/ui-features in your project.

What does UI Features need to run?

SKILL.md names no scripts, command-line tools or credentials: UI Features is instructions for the agent only.

Does UI Features access the network?

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.

Is UI Features safe to install?

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.

What licence does UI Features use?

UI Features is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does UI Features use?

About 4.6k 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. Its references folder adds about 1.9k tokens, read only when the agent opens those files.

What are the alternatives to UI Features?

Skills that share tags, products or a category with UI Features: UI Styling (Ohh-889/skyroc, 795 stars), LobeHub Interactive Prototype (lobehub/lobehub, 83k stars), Make Interfaces Feel Better (samuelclay/NewsBlur, 7.6k stars) and UI UX Pro Max (ZxBing0066/pixel-converter, 181 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains UI Features?

marcus (a GitHub user) maintains it in marcus/sidecar, which has 1,085 GitHub stars. The repository holds 19 skills in this directory. The repository was last updated on October 5, 2026.

Source: marcus/sidecar on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.