# Iframe

> A Claude skill from deeleeramone/PyWry.

- **Type:** Skill
- **Install:** `agentstack add skill-deeleeramone-pywry-iframe`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [deeleeramone](https://agentstack.voostack.com/s/deeleeramone)
- **Installs:** 0
- **Category:** [Data & Analytics](https://agentstack.voostack.com/c/data-and-analytics)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [deeleeramone](https://github.com/deeleeramone)
- **Source:** https://github.com/deeleeramone/PyWry/tree/develop/pywry/pywry/mcp/skills/iframe
- **Website:** https://deeleeramone.github.io/PyWry/

## Install

```sh
agentstack add skill-deeleeramone-pywry-iframe
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 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 `` 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)                    │
│                                                                   │
│                                 │
│     ┌───────────────────────────────────────────────────────┐     │
│     │              PyWry Widget (sandboxed)                 │     │
│     │                                                       │     │
│     │    ┌───────────────────────────────────────────┐      │     │
│     │    │        Your Content (HTML/CSS/JS)         │      │     │
│     │    └───────────────────────────────────────────┘      │     │
│     │                                                       │     │
│     │    [Toolbars, Charts, Forms, etc.]                    │     │
│     │                                                       │     │
│     └───────────────────────────────────────────────────────┘     │
│                                                          │
│                                                                   │
│   Other page content...                                           │
└───────────────────────────────────────────────────────────────────┘
                   ↕ postMessage for communication
```

## Key Constraints

### Fixed Dimensions
- Width and height determined by parent's `` 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
```python
# The parent controls your size
# 

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

### 2. Toolbars - Keep Minimal
```python
# 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
```python
# 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
```python
# 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
```python
# 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
```python
# 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)
```python
# 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)
```python
# Server can send events to widget
send_event(widget_id, event_type="parent:message", data={"action": "refresh"})
```

## Embedding Pattern

### Basic Embed
```html

```

### With Permissions
```html

```

## Code Example

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

app = PyWry()

# Compact content for iframe embedding
content = Div(
    content="""
        
            Ready
            
        
    """,
    component_id="embed-widget",
)

# Create embeddable widget
widget = app.show(
    html=content.build_html(),
    height=450,
    toolbars=[
        Toolbar(
            position="top",
            items=[
                Button(label="⟳ Refresh", event="refresh"),
                Toggle(label="Live", event="live-mode", value=False),
            ]
        )
    ]
)

# Widget is now available at:
# http://localhost:port/widget/{widget.id}
print(f"Embed URL: {widget.url}")

# Handle events server-side
while True:
    events = widget.get_events(clear=True)
    for e in events:
        if e["event_type"] == "refresh":
            new_data = fetch_data()
            widget.set_content(component_id="chart", html=render_chart(new_data))
        elif e["event_type"] == "live-mode":
            if e["data"]["value"]:
                start_live_updates()
            else:
                stop_live_updates()
```

## iFrame-Specific Features

### Responsive Design
```python
# Widget should adapt to container size
inject_css(widget_id, css="""
    @media (max-width: 400px) {
        .toolbar { flex-direction: column; }
        .chart { height: 200px; }
    }
""")
```

### Loading States
```python
# Parent may show iframe slowly - show loading state
content = Div(
    content="Loading...",
    component_id="content",
)
# Then update when data is ready
```

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [deeleeramone](https://github.com/deeleeramone)
- **Source:** [deeleeramone/PyWry](https://github.com/deeleeramone/PyWry)
- **License:** Apache-2.0
- **Homepage:** https://deeleeramone.github.io/PyWry/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-deeleeramone-pywry-iframe
- Seller: https://agentstack.voostack.com/s/deeleeramone
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
