Skip to content
Development
Skill

/native

**CRITICAL**: This is a **native desktop application** using PyWry/WRY (Rust WebView). This is **NOT** a browser. This is **NOT** Jupyter. This is **NOT** an iframe.

From plugin
pywry
9918 skills1 agent2 commands1 MCP

Claude Code

$ npx -y skills add deeleeramone/PyWry --skill native --agent claude-code

SKILL.md

native.SKILL.md

Native Window Mode

> **CRITICAL**: This is a **native desktop application** using PyWry/WRY (Rust WebView). > This is **NOT** a browser. This is **NOT** Jupyter. This is **NOT** an iframe.

What Native Mode IS

You're creating widgets for a **native desktop window** powered by:

  • **PyWry** (Python bindings)
  • **WRY** (Rust WebView library)
  • **Tauri** ecosystem (system-level access)

The widget runs in a **standalone system window** with:

  • Native title bar, minimize, maximize, close buttons
  • Direct OS integration (file dialogs, notifications, clipboard)
  • No browser chrome, no URL bar, no tabs
  • Full screen real estate for your content

What Native Mode is NOT

❌ **NOT a web browser** - No DevTools (unless explicitly enabled), no extensions, no URL bar ❌ **NOT Jupyter** - No cell output area, no kernel, no notebook context ❌ **NOT an iframe** - No parent page, no sandboxing, no cross-origin restrictions

Architecture

┌───────────────────────────────────────────────────────────┐
│                      Native Window                        │
│  ┌─────────────────────────────────────────────────────┐  │
│  │               Title Bar (OS-native)                 │  │
│  ├─────────────────────────────────────────────────────┤  │
│  │                                                     │  │
│  │                 WebView (WRY/Tauri)                 │  │
│  │                                                     │  │
│  │    ┌─────────────────────────────────────────┐      │  │
│  │    │          Your Widget Content            │      │  │
│  │    │      (HTML/CSS/JS via PyWry API)        │      │  │
│  │    └─────────────────────────────────────────┘      │  │
│  │                                                     │  │
│  └─────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────┘
              ↕ Python ↔ Rust ↔ OS integration

Key Capabilities

Window Control

  • Resize, minimize, maximize, close via native controls
  • Set window title dynamically
  • Window always stays on top (optional)
  • Multiple windows possible

System Integration

  • **Downloads**: `download` tool triggers native OS save dialog
  • **Navigation**: `navigate` can open external URLs in system browser
  • **Clipboard**: Full clipboard access
  • **File System**: Can reference local files

Persistent State

  • Widget state persists until window is closed
  • Events accumulate until `get_events(clear=True)`
  • Long-running applications are the norm

Best Practices

1. Layout - Use Full Viewport

# You have the entire window - use it
content = Div(
    content="<h1>Dashboard</h1>",
    component_id="main",
    style="height: 100vh; width: 100vw; padding: 20px;",
)

2. Toolbars - Position Top/Bottom

# Top toolbar for primary navigation
create_widget(
    html=content.build_html(),
    toolbars=[{
        "position": "top",
        "items": [
            {"type": "button", "label": "File", "event": "menu:file"},
            {"type": "button", "label": "Edit", "event": "menu:edit"},
            {"type": "button", "label": "View", "event": "menu:view"},
        ]
    }]
)

3. Theme - Respect System Preference

# Let OS dark/light mode control the theme
update_theme(widget_id, theme="system")

4. Interactive Buttons - The Pattern

**CRITICAL**: Buttons work automatically when events follow `elementId:action` pattern.

# Counter widget - buttons update content automatically
create_widget(
    html='<div id="counter" style="font-size:48px;text-align:center">0</div>',
    toolbars=[{
        "position": "top",
        "items": [
            {"type": "button", "label": "+1", "event": "counter:increment"},
            {"type": "button", "label": "-1", "event": "counter:decrement"},
            {"type": "button", "label": "Reset", "event": "counter:reset"}
        ]
    }]
)

The magic: `event="counter:increment"` automatically: 1. Finds element with `id="counter"` 2. Increments its text content by 1 3. Updates the display

Supported actions:

  • `increment` - add 1
  • `decrement` - subtract 1
  • `reset` - set to 0
  • `toggle` - switch true/false

5. Window Events

# Detect window close
events = get_events(widget_id, clear=True)
for e in events:
    if e["event_type"] == "pywry:disconnect":
        # User closed the window - cleanup
        cleanup_resources()

Recommended Components

| Component | Why | |-----------|-----| | `TabGroup` | Multi-view navigation (like app tabs) | | `SecretInput` | Secure API key storage | | `Marquee` | Live data tickers (full width available) | | `Plotly` | Full interactive charts (zoom, pan, export) |

Code Example

from pywry import PyWry
from pywry.toolbar import Button, Div, TabGroup, Toolbar

app = PyWry()

# Create main content area
content = Div(
    content="""
        <h1 style="margin: 0;">Financial Dashboard</h1>
        <p>Real-time market data</p>
    """,
    component_id="dashboard",
    style="padding: 24px; height: calc(100vh - 100px);",
)

# Create window with toolbars
widget = app.show(
    html=content.build_html(),
    title="Market Dashboard",
    height=800,
    toolbars=[
        Toolbar(
            position="top",
            items=[
                Button(label="Refresh", event="action:refresh", variant="primary"),
                Button(label="Settings", event="action:settings", variant="ghost"),
            ]
        )
    ]
)

# Event loop
while True:
    events = widget.get_events(clear=True)
    for e in events:
        if e["event_type"] == "pywry:disconnect":
            exit()
        elif e["event_type"] == "action:refresh":
            # Refresh data...
            pass

Native-Specific Features

File Downloads

# Triggers native OS save dialog
download(widget_id,
         url="data:text/csv;base64,...",
         filename="export.csv")

Open in System Browser

# Ope
Read more
@deeleeramone
Ships withpywry

PyWry is a cross-platform app factory, rendering engine and UI toolkit for Python that produces native desktop, web, and notebook experiences from a single API.

9918 skills1 agent1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
99
Star history of deeleeramone/PyWry on GitHub, a chart by star-history.com

Other skills on pywry.

pywry-orientation

pywry-orientation

When to reach for PyWry MCP tools (native webview rendering, Plotly, TradingView, AgGrid, chat) instead of writing…

@deeleeramone@deeleeramoneInstall Skill
chat_agent

chat_agent

How an agent operates inside a PyWry chat widget — reading user messages, attachments, @-context, tool-call result cards,…

@deeleeramone@deeleeramoneInstall Skill
component_reference

component_reference

**STOP. THIS IS THE AUTHORITATIVE REFERENCE FOR ALL COMPONENTS.** **YOU MUST USE THE EXACT EVENT SIGNATURES DOCUMENTED HERE.**…

@deeleeramone@deeleeramoneInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about native.

Which coding agents can use native?
native installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.