Skip to content

Commit 8e15423

Browse files
Revamp playground (#69)
* Init generate playground pages * Add more examples * Init sidebar * Simplify sidebar * Fix sidebar * Split client side js * Separate playground assets in its own directory * Remove unused sandbox svg * Fix resizing * Fix active sidebar * Init first page as hello world * Adjust style and improve hello world page * Readd autorun * Set SEO title * Improve styling * Refine style * Add meta tags * Add description * Add client side routing * Handle intro-chatgpt as playground root * Add scroll to active example and adjust style active
1 parent d2da7d7 commit 8e15423

File tree

7 files changed

+1376
-500
lines changed

7 files changed

+1376
-500
lines changed

build.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const { encode } = require('html-entities');
88
const { JSDOM } = require('jsdom');
99
const yaml = require('js-yaml');
1010
const esbuild = require('esbuild');
11+
const { generatePlayground } = require('./src/playground')
1112

1213
const site = "https://docs.puter.com";
1314

@@ -45,8 +46,10 @@ marked.use({
4546
// toolbar
4647
html += '<div class="code-buttons">';
4748
// "Run"
48-
if (exampleID)
49-
html += `<a href="/playground/index.html?example=${exampleID}&autorun=1" class="code-button run-code-button" target="_blank"><span class="run"></span></a>`;
49+
if (exampleID == "intro-chatgpt")
50+
html += `<a href="/playground/?autorun=1" class="code-button run-code-button" target="_blank"><span class="run"></span></a>`;
51+
else
52+
html += `<a href="/playground/${exampleID}/?autorun=1" class="code-button run-code-button" target="_blank"><span class="run"></span></a>`;
5053

5154
// "Copy"
5255
html += '<div class="code-button copy-code-button"><span class="copy"></span></div>';
@@ -689,6 +692,7 @@ generateDocumentation('./src');
689692
generateRedirects();
690693
generateSitemap();
691694
generateLLMs();
695+
generatePlayground();
692696

693697
if (anyErrors) {
694698
process.exit(1);

src/examples.js

Lines changed: 529 additions & 0 deletions
Large diffs are not rendered by default.

src/playground.js

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const examples = require('./examples')
4+
5+
// Function to generate sidebar HTML
6+
const generateSidebarHtml = (sections) => {
7+
let sidebarHtml = '<div class="sidebar-content">';
8+
9+
sections.forEach(section => {
10+
sidebarHtml += `<div class="sidebar-category">`;
11+
sidebarHtml += `<div class="sidebar-category-title">${section.title}</div>`;
12+
section.children.forEach(example => {
13+
sidebarHtml += `<a href="/playground/${example.slug ? example.slug + '/' : ''}" class="sidebar-item">${example.title}</a>`;
14+
});
15+
sidebarHtml += `</div>`;
16+
});
17+
18+
sidebarHtml += '</div>';
19+
return sidebarHtml;
20+
};
21+
22+
const playgroundHtml = `
23+
<html>
24+
25+
<head>
26+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"
27+
integrity="sha512-NhSC1YmyruXifcj/KFRWoC561YpHpc5Jtzgvbuzx5VozKpWvQ+4nXhPdFgmx8xqexRcpAglTj9sIBWINXa8x5w=="
28+
crossorigin="anonymous" referrerpolicy="no-referrer" />
29+
<meta name="viewport" content="width=device-width, initial-scale=1">
30+
<link href="https://fonts.googleapis.com/css?family=Roboto:black,bold,medium,regular,light,thin" rel="stylesheet">
31+
<title>{{TITLE}}</title>
32+
<meta name="title" content="{{TITLE}}" />
33+
<meta name="description" content="{{DESCRIPTION}}" />
34+
35+
<link rel="canonical" href="{{CANONICAL}}">
36+
37+
<meta property="og:title" content="{{TITLE}}">
38+
<meta property="og:description" content="{{DESCRIPTION}}" />
39+
<meta property="og:type" content="website" />
40+
<meta name="og:image" content="https://assets.puter.site/twitter.png">
41+
<meta name="og:url" content="{{CANONICAL}}">
42+
43+
<meta name="twitter:card" content="summary_large_image" />
44+
<meta name="twitter:site" content="@HeyPuter" />
45+
<meta name="twitter:title" content="{{TITLE}}">
46+
<meta name="twitter:description" content="{{DESCRIPTION}}" />
47+
<meta name="twitter:image" content="https://assets.puter.site/twitter.png">
48+
49+
<link rel="apple-touch-icon" sizes="57x57" href="/assets/favicon/apple-icon-57x57.png">
50+
<link rel="apple-touch-icon" sizes="60x60" href="/assets/favicon/apple-icon-60x60.png">
51+
<link rel="apple-touch-icon" sizes="72x72" href="/assets/favicon/apple-icon-72x72.png">
52+
<link rel="apple-touch-icon" sizes="76x76" href="/assets/favicon/apple-icon-76x76.png">
53+
<link rel="apple-touch-icon" sizes="114x114" href="/assets/favicon/apple-icon-114x114.png">
54+
<link rel="apple-touch-icon" sizes="120x120" href="/assets/favicon/apple-icon-120x120.png">
55+
<link rel="apple-touch-icon" sizes="144x144" href="/assets/favicon/apple-icon-144x144.png">
56+
<link rel="apple-touch-icon" sizes="152x152" href="/assets/favicon/apple-icon-152x152.png">
57+
<link rel="apple-touch-icon" sizes="180x180" href="/assets/favicon/apple-icon-180x180.png">
58+
<link rel="icon" type="image/png" sizes="192x192" href="/assets/favicon/android-icon-192x192.png">
59+
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon/favicon-32x32.png">
60+
<link rel="icon" type="image/png" sizes="96x96" href="/assets/favicon/favicon-96x96.png">
61+
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png">
62+
<link rel="manifest" href="/assets/favicon/manifest.json">
63+
<meta name="msapplication-TileColor" content="#ffffff">
64+
<meta name="msapplication-TileImage" content="/assets/favicon/ms-icon-144x144.png">
65+
<meta name="theme-color" content="#ffffff">
66+
<script defer data-domain="docs.puter.com" src="https://plausible.io/js/script.js"></script>
67+
<link rel="stylesheet" href="/playground/assets/css/style.css">
68+
</head>
69+
70+
<body>
71+
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
72+
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
73+
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.min.js"
74+
integrity="sha512-ZG31AN9z/CQD1YDDAK4RUAvogwbJHv6bHrumrnMLzdCrVu4HeAqrUX7Jsal/cbUwXGfaMUNmQU04tQ8XXl5Znw=="
75+
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
76+
<script src="https://js.puter.com/v2/"></script>
77+
78+
<div style="height: 50px; padding: 10px; background-color: #474e5d; display: flex; flex-direction: row;">
79+
<h1 class="logo"><a href="/playground/">Puter.js Playground</a></h1>
80+
<div style="float:right;" class="navbar">
81+
<a href="/" target="_blank" style="margin-right: 35px;">Docs</a>
82+
<a style="display: flex; flex-direction: row; align-items: center;"
83+
href="https://github.com/heyPuter/puter/" target="_blank"><svg role="img"
84+
style="margin-right:4px; margin-bottom: 3px;" width="17" height="17" viewBox="0 0 24 24" fill="#fff"
85+
xmlns="http://www.w3.org/2000/svg">
86+
<title>GitHub</title>
87+
<path
88+
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
89+
</svg><span class="github-stars"></span></a></h1>
90+
</div>
91+
</div>
92+
93+
<div class="main-container">
94+
<!-- Sidebar -->
95+
<div id="sidebar-container">
96+
<div class="sidebar-header">
97+
<button class="sidebar-toggle" id="sidebar-toggle">
98+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-menu-icon lucide-menu"><path d="M4 5h16"/><path d="M4 12h16"/><path d="M4 19h16"/></svg>
99+
</button>
100+
<span class="sidebar-title">Examples</span>
101+
</div>
102+
<div class="sidebar">
103+
{{SIDEBAR}}
104+
</div>
105+
</div>
106+
107+
<div style="display: flex; flex-direction: row; width: 100%;">
108+
<!-- Code Container -->
109+
<div id="code-container">
110+
<div style="overflow: hidden; height: 50px; flex-shrink: 0; display: flex; flex-direction: row; align-items: center; background: #fff; border-bottom: 1px solid #CCC;">
111+
<span style="user-select: none; margin:0; float:left; font-size: 20px; padding: 10px; flex-grow:1;">Code</span>
112+
</div>
113+
<div id="code" style="width: 100%; height: 100%;"></div>
114+
</div>
115+
116+
<!-- Resizer -->
117+
<div class="resizer"></div>
118+
119+
<!-- Output Container -->
120+
<div id="output-container">
121+
<div style="overflow: hidden; height: 50px; flex-shrink: 0; display: flex; flex-direction: row; align-items: center; background: #fff; border-bottom: 1px solid #CCC;">
122+
<span style="user-select: none; margin:0; float:left; font-size: 20px; padding: 10px; flex-grow: 1;">Preview</span>
123+
<button id="run"><span></span>Run</button>
124+
</div>
125+
<div id="output" style="width: 100%; height: 100%;"></div>
126+
</div>
127+
</div>
128+
</div>
129+
<iframe id="initial-code" style="display:none;">{{CODE}}</iframe>
130+
<script src="/playground/assets/js/app.js"></script>
131+
</body>
132+
133+
</html>`
134+
135+
const generatePlayground = () => {
136+
// Generate sidebar HTML once for all examples
137+
const sidebarHtml = generateSidebarHtml(examples);
138+
139+
let totalExamples = 0;
140+
141+
examples.forEach(section => {
142+
section.children.forEach(example => {
143+
// Read source file from src/ directory
144+
const sourcePath = path.join('src', example.source);
145+
const sourceContent = fs.readFileSync(sourcePath, 'utf8');
146+
147+
// Copy playgroundHtml to avoid tainting the original
148+
let htmlTemplate = playgroundHtml.slice();
149+
150+
htmlTemplate = htmlTemplate.replace('{{SIDEBAR}}', sidebarHtml);
151+
const pageTitle = example.slug === '' ? 'Puter.js Playground' : `${example.title} | Puter.js Playground`;
152+
htmlTemplate = htmlTemplate.replaceAll('{{TITLE}}', pageTitle);
153+
const pageDescription = example.description || 'Try Puter.js instantly with interactive examples in your browser. Run, edit, and experiment with code - no installation or setup required.';
154+
htmlTemplate = htmlTemplate.replaceAll('{{DESCRIPTION}}', pageDescription);
155+
const canonicalUrl = `https://docs.puter.com/playground/${example.slug ? example.slug + '/' : ''}`;
156+
htmlTemplate = htmlTemplate.replaceAll('{{CANONICAL}}', canonicalUrl);
157+
const finalHtml = htmlTemplate.replace('{{CODE}}', sourceContent);
158+
159+
// Create output directory
160+
const outputDir = path.join('dist', 'playground', example.slug);
161+
fs.mkdirSync(outputDir, { recursive: true });
162+
163+
// Write the file
164+
const outputPath = path.join(outputDir, 'index.html');
165+
fs.writeFileSync(outputPath, finalHtml, 'utf8');
166+
167+
totalExamples++;
168+
});
169+
});
170+
console.log(`Generated ${totalExamples} playground examples.`);
171+
}
172+
173+
module.exports = { generatePlayground };

0 commit comments

Comments
 (0)