Skip to content
Development
Skill

/iframe

**CRITICAL**: This mode is for widgets **embedded as iframes in external web pages**. This is **NOT** a native window. This is **NOT** Jupyter. The widget is sandboxed.

From plugin
pywry
9318 skills1 agent2 commands1 MCP
Install
$ npx -y skills add deeleeramone/PyWry --skill iframe --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/iframe

Context preview

The summary Claude sees to decide when to auto-load this skill.

**CRITICAL**: This mode is for widgets **embedded as iframes in external web pages**. This is **NOT** a native window. This is **NOT** Jupyter. The widget is sandboxed.

SKILL.md

iframe.SKILL.md

iFrame Embed Mode

> **CRITICAL**: This mode is for widgets **embedded as iframes in external web pages**. > This is **NOT** a native window. This is **NOT** Jupyter. The widget is sandboxed.

What iFrame Mode IS

You're creating widgets to be **embedded as iframes** in external pages:

  • Widget served at a URL: `http://host:port/widget/{widget_id}`
  • Embedded via `<iframe src="...">` in parent page
  • Sandboxed execution - isolated from parent
  • Cross-origin restrictions apply

What iFrame Mode is NOT

❌ **NOT a native window** - No OS integration, no native dialogs ❌ **NOT Jupyter** - No kernel, no notebook context ❌ **NOT the main page** - You're embedded, constrained by parent

Architecture

┌───────────────────────────────────────────────────────────────────┐
│                   Parent Web Page (any origin)                    │
│                                                                   │
│   <iframe src="http://pywry-server:port/widget/abc123"            │
│           width="100%" height="500">                              │
│     ┌───────────────────────────────────────────────────────┐     │
│     │              PyWry Widget (sandboxed)                 │     │
│     │                                                       │     │
│     │    ┌───────────────────────────────────────────┐      │     │
│     │    │        Your Content (HTML/CSS/JS)         │      │     │
│     │    └───────────────────────────────────────────┘      │     │
│     │                                                       │     │
│     │    [Toolbars, Charts, Forms, etc.]                    │     │
│     │                                                       │     │
│     └───────────────────────────────────────────────────────┘     │
│   </iframe>                                                       │
│                                                                   │
│   Other page content...                                           │
└───────────────────────────────────────────────────────────────────┘
                   ↕ postMessage for communication

Key Constraints

Fixed Dimensions

  • Width and height determined by parent's `<iframe>` tag
  • Widget cannot resize itself
  • Design for specific dimensions or be responsive

Sandboxed Execution

  • Cannot access parent DOM
  • Cannot read parent cookies/storage
  • Limited access to some browser APIs
  • Navigation stays within iframe (unless allowed)

Cross-Origin Restrictions

  • Widget origin ≠ parent origin (usually)
  • Communication via `postMessage` only
  • Some features require `allow` attributes

Best Practices

1. Sizing - Design for Fixed Dimensions

# The parent controls your size
# <iframe src="..." width="600" height="400">

# Design content to fit
content = Div(
    content="...",
    style="width: 100%; height: 100%; overflow: auto;",
)

2. Toolbars - Keep Minimal

# Screen real estate is precious
create_widget(
    html=content.build_html(),
    toolbars=[{
        "position": "top",
        "items": [
            # Compact: icons only or short labels
            {"type": "button", "label": "⟳", "event": "refresh"},
            {"type": "toggle", "label": "Auto", "event": "auto-update"},
        ]
    }]
)

3. Navigation - Stays in Frame

# By default, navigation stays within iframe
navigate(widget_id, url="/other-view")  # Still in iframe

# External links may be blocked by parent
# Use with caution:
navigate(widget_id, url="https://external.com", external=True)

4. Downloads - May Require Gesture

# Some browsers block downloads from iframes
# Ensure download is triggered by user action
# (button click, not automatic)

Security Considerations

Sandboxing Protects Both Sides

  • Widget can't access parent page data
  • Parent can't directly access widget DOM
  • Use this for untrusted contexts

Secret Handling

# Never expose secrets in URL or logs
# Use SecretInput handler - value stays server-side
create_widget(
    toolbars=[{
        "items": [
            {"type": "secret", "label": "API Key", "event": "api-key"},
        ]
    }]
)

Token-Based Access

# Consider adding tokens to widget URLs for access control
# http://host:port/widget/{widget_id}?token={access_token}

Recommended Components

| Component | Why | |-----------|-----| | `Button` | Compact action triggers | | `Toggle` | On/off states (saves space) | | `Select` | Dropdowns (vertical space efficient) | | `Plotly` | Self-contained interactive charts |

Communication with Parent

Widget → Parent (via events)

# Widget emits events that parent can receive
# Parent listens for postMessage events
#
# Parent page JavaScript:
# window.addEventListener('message', (e) => {
#     if (e.data.type === 'pywry:event') {
#         console.log(e.data.event_type, e.data.data);
#     }
# });

Parent → Widget (via send_event)

# Server can send events to widget
send_event(widget_id, event_type="parent:message", data={"action": "refresh"})

Embedding Pattern

Basic Embed

<iframe
  src="http://pywry-server:8001/widget/my-chart"
  width="100%"
  height="500"
  frameborder="0"
  loading="lazy">
</iframe>

With Permissions

<iframe
  src="http://pywry-server:8001/widget/my-chart"
  width="100%"
  height="500"
  frameborder="0"
  allow="clipboard-write; downloads"
  sandbox="allow-scripts allow-same-origin">
</iframe>

Code Example

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

app = PyWry()

# Compact content for iframe embedding
content = Div(
    content="""
        <div style="padding: 12px;">
            <div id="status" style="margin-bottom: 12px;">Ready</div>
            <div id="chart" style="height: 350px;"></div>
        </div>
    """,
    component_id="embed-widget",
)

# Create embeddable widget
widget = app.show(
    html=conten
Read more
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.

Get the whole plugin

Other skills on pywry.