Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions examples/embed_demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Preswald Component Embedding Demo</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
color: #2c3e50;
}
.example-container {
margin: 20px 0;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
.component-container {
margin-top: 20px;
border: 1px solid #eee;
padding: 10px;
background-color: #f9f9f9;
}
code {
background-color: #f5f5f5;
padding: 2px 4px;
border-radius: 3px;
font-family: monospace;
}
pre {
background-color: #f5f5f5;
padding: 10px;
border-radius: 5px;
overflow-x: auto;
}
</style>
</head>
<body>
<h1>Preswald Component Embedding Demo</h1>

<p>This page demonstrates how to embed Preswald components in external web pages.</p>

<div class="example-container">
<h2>Embedding a Specific Component</h2>
<p>Below is an embedded card component with ID <code>embeddable_card</code>:</p>

<div class="component-container">
<!-- Replace the URL with your actual server URL -->
<iframe
src="http://localhost:8000/embed?component_id=embeddable_card"
width="100%"
height="200"
frameborder="0">
</iframe>
</div>

<p>HTML code:</p>
<pre><code>&lt;iframe
src="http://localhost:8000/embed?component_id=embeddable_card"
width="100%"
height="200"
frameborder="0"&gt;
&lt;/iframe&gt;</code></pre>
</div>

<div class="example-container">
<h2>Embedding a Plot Component</h2>
<p>Below is an embedded plot component with ID <code>sample_plot</code>:</p>

<div class="component-container">
<!-- Replace the URL with your actual server URL -->
<iframe
src="http://localhost:8000/embed?component_id=sample_plot"
width="100%"
height="350"
frameborder="0">
</iframe>
</div>

<p>HTML code:</p>
<pre><code>&lt;iframe
src="http://localhost:8000/embed?component_id=sample_plot"
width="100%"
height="350"
frameborder="0"&gt;
&lt;/iframe&gt;</code></pre>
</div>

<div class="example-container">
<h2>Embedding the Entire App</h2>
<p>You can also embed the entire application:</p>

<div class="component-container">
<!-- Replace the URL with your actual server URL -->
<iframe
src="http://localhost:8000/embed"
width="100%"
height="500"
frameborder="0">
</iframe>
</div>

<p>HTML code:</p>
<pre><code>&lt;iframe
src="http://localhost:8000/embed"
width="100%"
height="500"
frameborder="0"&gt;
&lt;/iframe&gt;</code></pre>
</div>

<p><strong>Note:</strong> Make sure the Preswald server is running with the <code>embed_example.py</code> script before viewing this page.</p>
</body>
</html>
37 changes: 37 additions & 0 deletions examples/embed_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Example script demonstrating component embedding
"""
import preswald as pw

# Create some components with unique IDs
pw.header("Embed Example", level=1, id="main_header")
pw.text("This is a sample application with multiple components.", id="intro_text")

# Create a component specifically for embedding
with pw.card(id="embeddable_card", title="Embeddable Component"):
pw.text("This component can be embedded on its own.", id="card_text")
pw.button("Click Me", id="embed_button")

# Create another component
pw.plotly({
"data": [{"y": [1, 2, 3, 4], "type": "scatter"}],
"layout": {"title": "Sample Plot"}
}, id="sample_plot")

# Add instructions for embedding
pw.markdown("""
## How to Embed

To embed the card component, use the following HTML code:

```html
<iframe
src="http://localhost:8000/embed?component_id=embeddable_card"
width="100%"
height="300"
frameborder="0">
</iframe>
```

This will only show the card component, not the rest of the application.
""", id="embed_instructions")
15 changes: 13 additions & 2 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BrowserRouter as Router } from 'react-router-dom';
import Layout from './components/Layout';
import LoadingState from './components/LoadingState';
import Dashboard from './components/pages/Dashboard';
import EmbedView from './components/pages/EmbedView';
import { comm } from './utils/websocket';

const App = () => {
Expand All @@ -13,8 +14,14 @@ const App = () => {
const [config, setConfig] = useState(null);
const [isConnected, setIsConnected] = useState(false);
const [areComponentsLoading, setAreComponentsLoading] = useState(true);
const [isEmbedMode, setIsEmbedMode] = useState(false);

useEffect(() => {
// Check if in embed mode (either from URL or from window.EMBED_CONFIG)
const isEmbed = window.location.pathname.startsWith('/embed') ||
(window.EMBED_CONFIG && window.EMBED_CONFIG.embed_mode);
setIsEmbedMode(isEmbed);

comm.connect();

const unsubscribe = comm.subscribe(handleMessage);
Expand Down Expand Up @@ -147,8 +154,12 @@ const App = () => {
setError(message.connected ? null : 'Lost connection. Attempting to reconnect...');
};

console.log('[App] Rendering with:', { components, isConnected, error });
console.log(window.location.pathname);
console.log('[App] Rendering with:', { components, isConnected, error, isEmbedMode });

// If in embed mode, render the EmbedView directly without Layout
if (isEmbedMode) {
return <EmbedView />;
}

return (
<Router>
Expand Down
54 changes: 54 additions & 0 deletions frontend/src/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -848,3 +848,57 @@
background-color: #4b5563;
}
}

/* Embeddable Components Styles */
.embed-container {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
width: 100%;
height: 100%;
min-height: 50px;
margin: 0;
padding: 0;
overflow: auto;
background-color: transparent;
}

.embed-loading {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100px;
color: #333;
font-size: 1rem;
}

.embed-error {
background-color: #fff0f0;
border: 1px solid #ffcccc;
border-radius: 6px;
padding: 12px;
margin: 12px 0;
color: #cc0000;
}

.embed-error h3 {
margin-top: 0;
margin-bottom: 8px;
font-size: 1rem;
font-weight: 600;
}

.embed-error p {
margin: 0;
font-size: 0.9rem;
}

/* When in embed mode, apply tighter spacing */
.embed-container .preswald-component {
margin-bottom: 12px;
}

/* Hide certain UI elements in embed mode */
.embed-container .preswald-debug-info,
.embed-container .preswald-toolbar {
display: none;
}
Loading