Atlas / Skills / marcus / Create Modal

Create ModalSAFE

skills/marcus/create-modal

Use sidecar next to CLI agents for diffs, file trees, conversation history, and task management with td

Verdict
SAFE
Grade
B
Trust score
89 /100
Version
—
Hosts
1 documented
License
MIT
Stars
1,086
01

Overview

Use sidecar next to CLI agents for diffs, file trees, conversation history, and task management with td

Read from source at commit a69114b893d1OBSERVED · 2026-10-10
02

Host compatibility

What the documentation claims. We have not run a compatibility test.

HostStatusNotes
cursormentioned
03

What it tells the agent

The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.

---
name: create-modal
description: Create declarative modals using the modal library API. Covers modal types (confirm, input, select, form), sections (Text, Buttons, Input, Textarea, Checkbox, Select, List, Combo, When, Custom), rendering with OverlayModal, and keyboard/mouse handling. Use when adding modals or dialogs to the application.
---

# Creating Declarative Modals

Use the `internal/modal` package. The library handles keyboard navigation, mouse hit regions, hover states, and scrolling automatically.

## Quick Start

```go
import "github.com/marcus/sidecar/internal/modal"

// 1. Create the modal
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"),
    ))

// 2. Render in View
func (p *Plugin) View(width, height int) string {
    background := p.renderListView(width, height)
    rendered := p.myModal.Render(width, height, p.mouseHandler)
    return ui.OverlayModal(background, rendered, width, height)
}

// 3. Handle input in Update
case tea.KeyMsg:
    action, cmd := p.myModal.HandleKey(msg)
    if action != "" {
        return p.handleAction(action) // "delete", "cancel", etc.
    }
    return p, cmd

case tea.MouseMsg:
    action := p.myModal.HandleMouse(msg, p.mouseHandler)
    if action != "" {
        return p.handleAction(action)
    }
    return p, nil
```

## Critical: Modal Initialization Pattern

The modal must exist before input handling. Create an `ensure` function called in **both** View and Update:

```go
func (p *Plugin) ensureMyModal() {
    if p.targetItem == nil {
        return // Required state missing
    }

    modalW := 50
    if modalW > p.width-4 {
        modalW = p.width - 4
    }
    if modalW < 20 {
        modalW = 20
    }

    // Only rebuild if needed
    if p.myModal != nil && p.myModalWidthCache == modalW {
        return
    }
    p.myModalWidthCache = modalW

    p.myModal = modal.New("Title", modal.WithWidth(modalW), ...).
        AddSection(...)
}
```

**Call `ensureModal()` before the nil check in key handlers:**

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

Without this, the first keypress after opening drops because View runs after Update in bubbletea.

## Constructor and Options

```go
m := modal.New(title string, opts ...Option)
```

| Option | Description | Default |
|--------|-------------|---------|
| `WithWidth(int)` | Modal width in characters | 50 |
| `WithVariant(Variant)` | Visual style | `VariantDefault` |
| `WithPrimaryAction(string)` | Action ID for Enter on inputs | "" |
| `WithHints(bool)` | Show "Tab to switch..." hint | true |
| `WithCloseOnBackdropClick(bool)` | Backdrop click returns "cancel" | true |

**Variants:** `VariantDefault`, `VariantDanger` (red), `VariantWarning` (yellow), `VariantInfo` (blue)

## Built-in Sections

### Text and Spacer
```go
modal.Text("Static text with auto line wrapping")
modal.Spacer()  // Single blank line
```

### Buttons
```go
modal.Buttons(
    modal.Btn(" Save ", "save"),              // Standard button
    modal.Btn(" Delete ", "delete", modal.BtnDanger()),  // Red
    modal.Btn(" Submit ", "submit", modal.BtnPrimary()), // Primary
    modal.Btn(" Cancel ", "cancel"),
)
```
- Include padding in labels: `" Save "` not `"Save"`
- Button IDs are returned as actions
- Tab/Shift+Tab cycles focus

### Input
```go
var nameInput textinput.Model
modal.Input("name-input", &nameInput)
modal.InputWithLabel("name-input", "Name:", &nameInput)
modal.Input("name-input", &nameInput,
    modal.WithSubmitOnEnter(true),       // Default: true
    modal.WithSubmitAction("submit"),    // Override primary action
)
```

### Textarea
```go
var msgArea textarea.Model
modal.Textarea("message", &msgArea, 5)          // height in lines
modal.TextareaWithLabel("message", "Label:", &msgArea, 5)
```
- Enter inserts newlines (never submits)

### Combo (floating dropdown)
```go
items := []modal.DropdownItem{
    {ID: "main", Label: "main", Value: "main"},
    {ID: "dev", Label: "dev", Value: "dev"},
}
var selectedIdx int
modal.Combo("branch", &branchInput, items, &selectedIdx)
```
- Single-line input; filtered results float over later sections (modal height does not change)
- `selected` is an **items** index (same as List)
- Typing filters and selects the top match; up/down move the highlight
- Enter commits the highlight and, by default, returns the modal primary action
- Tab commits and moves focus; Esc closes the overlay without cancelling the modal
- Click an overlay row to commit without submitting

### Checkbox
```go
var includeFiles bool
modal.Checkbox("include-files", "Include untracked files", &includeFiles)
```
- Space toggles
- Enter does **not** toggle; it submits the modal primary action (if any)

### Select (one choice out of a set)
```go
items := []modal.SelectItem{
    {ID: "shell", Label: "Shell", Description: "new agent/shell session"},
    {ID: "worktree", Label: "Worktree", Description: "shell in a new worktree"},
}
var selectedIdx int
modal.Select("kind", items, &selectedIdx,
    modal.WithMaxVisible(6),
    modal.WithDisabled(func(i int) string { return reasons[i] }),
    modal.WithOnSelect(func(i int) { rebuildAround(i) }),
)
```
- The default control for a single choice: sort, filter, kind. `modal.List` is the low-level column of rows for lists that are not a single choice.
- Two shapes, chosen by count: a segmented `[ A | B | C ]` under five choices, a `❯`-cursor full-width list with an aligned description column at five or more (and the list whenever the segments would not fit the width). `WithShape(modal.Shap
04

Trust audit

SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.

LayerWhat it checksResult
L0Provenance & inventoryWARN
L1Static analysis of the codeNA
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
none-observed
Network
none-observed
Shell
none-observed
Dependencies
pinned
Secrets in source
none-found

Findings (2)

MEDIUMInventory / provenance · inv.symlink · CWE-1104
.codex/skills
.codex/skills
Why it matters. link not followed
LOWInventory / provenance · inv.symlink · CWE-1104
GEMINI.md
GEMINI.md
Why it matters. link not followed

Gates applied: no_behavioural_pass.

Audited 2026-10-10 · audit v0.4.1 · source sha a69114b893d1full audit observations/trust-audit/skill/marcus__create-modal.json · Report an issue / request a re-scan
05

Audit history

Every audit this skill has had.

DateSourceVerdictGradeScoreChange
2026-10-10a69114b893d1SAFEB89first audit
06

Questions

What does the Create Modal skill do?

Use sidecar next to CLI agents for diffs, file trees, conversation history, and task management with td

Is Create Modal safe to install?

The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean skill reads B.

What can Create Modal access on my machine?

The audit observed no filesystem, network or shell use at all in its source.

Which assistants does Create Modal work with?

Its documentation mentions cursor. That is what the text claims, not a compatibility test we ran.

How current is this page?

The grade is for one exact copy of the source (a69114b893d1), read on 2026-10-10. The repository is watched, and a new audit runs when it changes — this is the first audit.

Advertisement