Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
05f0d4c
Add terminal panel to the css
Oluwasetemi Jan 21, 2026
7e14062
add my email to the playground core package.json
Oluwasetemi Jan 21, 2026
6fec437
Update EditorController.ts
Oluwasetemi Jan 21, 2026
f956928
Update EventEmitter.test.ts
Oluwasetemi Jan 21, 2026
566a2d6
add html, css, md to the playground engine
Oluwasetemi Jan 21, 2026
11506a3
add clear to the console features
Oluwasetemi Jan 21, 2026
b73db26
add terminal emit to the output and add clear to the injected console
Oluwasetemi Jan 21, 2026
0e6debc
Update TemplateCache.test.ts
Oluwasetemi Jan 21, 2026
32b9632
ANSI todo for terminal output
Oluwasetemi Jan 21, 2026
8514fde
add type FileSystemNode and the injectedConsoleForward script
Oluwasetemi Jan 21, 2026
50a767a
Update WebContainerManager.ts
Oluwasetemi Jan 21, 2026
54b344a
Update tsconfig.json
Oluwasetemi Jan 21, 2026
79c1e0f
Update PlaygroundPanel.tsx
Oluwasetemi Jan 21, 2026
d38122d
add try catch callback
Oluwasetemi Jan 21, 2026
d09b78d
add Terminal to the Playground Context
Oluwasetemi Jan 21, 2026
89450e9
wip ansi parser
Oluwasetemi Jan 21, 2026
b6fb5ea
update the test file to include missing types
Oluwasetemi Jan 21, 2026
dd242bb
Delete ansiParser.ts
Oluwasetemi Jan 21, 2026
bb04476
Update Terminal to use xterm.js
Oluwasetemi Jan 21, 2026
02f576b
setup hiddenFiles to the core engine and react packages
Oluwasetemi Jan 21, 2026
b328a32
add a support for hiddenFiles
Oluwasetemi Jan 21, 2026
0efe956
add xterm.js
Oluwasetemi Jan 21, 2026
8416261
work new template to test hiddenFiles config
Oluwasetemi Jan 21, 2026
446673e
setup the bug console.clear with the right bind
Oluwasetemi Jan 21, 2026
3b1aeaa
remove unused variable in the demo
Oluwasetemi Jan 21, 2026
29b5b81
fix failing test.
Oluwasetemi Jan 21, 2026
ecbbaa9
setup a middleman btw editor(codemirrow and monaco)
Oluwasetemi Jan 23, 2026
12ed030
feat: react changes
Oluwasetemi Jan 23, 2026
70e14be
text monaco in the demo
Oluwasetemi Jan 23, 2026
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
41 changes: 21 additions & 20 deletions apps/demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import {
PlaygroundHeader,
PlaygroundPanel,
ResizablePanel,
} from '@setemiojo/playground-react'
} from "@setemiojo/playground-react";
import { useState } from "react";
// import { reactTemplate as newTemp } from "./react.ts";
import {
nodeTemplate,
reactTemplate,
vanillaTemplate,
vueTemplate,
} from '@setemiojo/playground-templates'
import { useState } from 'react'
import './playground.css'
import './App.css'
import "./playground.css";
import "./App.css";

const templates: Record<string, Template> = {
vanilla: vanillaTemplate,
Expand All @@ -25,30 +26,30 @@ const templates: Record<string, Template> = {
}

export default function App() {
const [selectedTemplate, setSelectedTemplate] = useState<string>('react')
const [showSidebar, setShowSidebar] = useState(false)
const [selectedTemplate, setSelectedTemplate] = useState<string>("react");
const [showSidebar, setShowSidebar] = useState(false);
const template = templates[selectedTemplate]

const getTitle = () => {
switch (selectedTemplate) {
case 'react':
return 'React Playground'
case 'vue':
return 'Vue Playground'
case 'node':
return 'Node.js Playground'
case "react":
return "React Playground";
case "vue":
return "Vue Playground";
case "node":
return "Node.js Playground";
default:
return 'JavaScript Playground'
return "JavaScript Playground";
}
}
};
Comment on lines +29 to +44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove extra semicolons in component body.

style/semi is erroring on the state declarations, switch returns, and component return.

🛠️ Proposed fix
-  const [selectedTemplate, setSelectedTemplate] = useState<string>("react");
-  const [showSidebar, setShowSidebar] = useState(false);
+  const [selectedTemplate, setSelectedTemplate] = useState<string>("react")
+  const [showSidebar, setShowSidebar] = useState(false)
@@
-      case "react":
-        return "React Playground";
-      case "vue":
-        return "Vue Playground";
-      case "node":
-        return "Node.js Playground";
+      case "react":
+        return "React Playground"
+      case "vue":
+        return "Vue Playground"
+      case "node":
+        return "Node.js Playground"
@@
-      default:
-        return "JavaScript Playground";
+      default:
+        return "JavaScript Playground"
     }
-  };
+  }
@@
-  );
+  )

Also applies to: 107-107

🧰 Tools
🪛 ESLint

[error] 29-29: Extra semicolon.

(style/semi)


[error] 30-30: Extra semicolon.

(style/semi)


[error] 36-36: Extra semicolon.

(style/semi)


[error] 38-38: Extra semicolon.

(style/semi)


[error] 40-40: Extra semicolon.

(style/semi)


[error] 42-42: Extra semicolon.

(style/semi)


[error] 44-44: Extra semicolon.

(style/semi)

🤖 Prompt for AI Agents
In `@apps/demo/src/App.tsx` around lines 29 - 44, Remove the stray semicolons in
the component body: delete the unnecessary semicolons after the useState
declarations (selectedTemplate/setSelectedTemplate and
showSidebar/setShowSidebar), remove the extra semicolons after each return in
the getTitle switch cases and the trailing semicolon after the getTitle function
(so getTitle ends with } not };), and remove any extra semicolon following the
component return. Target the identifiers selectedTemplate, setSelectedTemplate,
showSidebar, setShowSidebar, template, and getTitle when making these edits.


return (
<div className="app">
<div className="template-bar">
<select
disabled
value={selectedTemplate}
onChange={e => setSelectedTemplate(e.target.value)}
onChange={(e) => setSelectedTemplate(e.target.value)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Satisfy arrow-parens and jsx-wrap-multilines rules.

Lint requires removing unnecessary parens for single-arg arrows and wrapping multiline JSX expressions.

🛠️ Proposed fix
-          onChange={(e) => setSelectedTemplate(e.target.value)}
+          onChange={e => setSelectedTemplate(e.target.value)}
@@
-              <ResizablePanel
-                firstPanel={
-                  <div className="playground-editor-section">
-                    <PlaygroundEditor />
-                  </div>
-                }
-                secondPanel={
-                  <div className="playground-preview-section">
-                    <PlaygroundPanel />
-                  </div>
-                }
+              <ResizablePanel
+                firstPanel={(
+                  <div className="playground-editor-section">
+                    <PlaygroundEditor />
+                  </div>
+                )}
+                secondPanel={(
+                  <div className="playground-preview-section">
+                    <PlaygroundPanel />
+                  </div>
+                )}

Also applies to: 82-91

🧰 Tools
🪛 ESLint

[error] 52-52: Unexpected parentheses around single function argument having a body with no curly braces.

(style/arrow-parens)

🤖 Prompt for AI Agents
In `@apps/demo/src/App.tsx` at line 52, Remove unnecessary parens from single-arg
arrow functions and wrap multiline JSX in parentheses: change handlers like the
onChange arrow that calls setSelectedTemplate from "(e) =>
setSelectedTemplate(e.target.value)" to use the single-arg form "e =>
setSelectedTemplate(e.target.value)", and ensure any multiline JSX blocks (the
JSX expression around the component/fragment referenced in the render returned
JSX, specifically the block covering lines 82-91) are wrapped in parentheses so
the JSX-wrap-multilines rule is satisfied.

>
<option value="vanilla">Vanilla JS</option>
<option value="react">React</option>
Expand Down Expand Up @@ -78,16 +79,16 @@ export default function App() {
</aside>
)}
<ResizablePanel
firstPanel={(
firstPanel={
<div className="playground-editor-section">
<PlaygroundEditor />
</div>
)}
secondPanel={(
}
secondPanel={
<div className="playground-preview-section">
<PlaygroundPanel />
</div>
)}
}
direction="horizontal"
responsive
responsiveBreakpoint={768}
Expand All @@ -103,5 +104,5 @@ export default function App() {
</Playground>
</main>
</div>
)
);
}
84 changes: 81 additions & 3 deletions apps/demo/src/playground.css
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@

.editor-container {
flex: 1;
overflow: auto;
position: relative;
overflow: hidden;
}

/* CodeMirror Editor Styles */
.editor-container .cm-editor {
height: 100%;
}
Expand All @@ -105,6 +107,56 @@
line-height: 1.6;
}

/* Monaco Editor Styles */
.editor-container .monaco-editor {
width: 100% !important;
height: 100% !important;
position: absolute !important;
top: 0;
left: 0;
--monaco-monospace-font: 'JetBrains Mono', 'Fira Code', Menlo, Monaco, 'Courier New', monospace;
--vscode-editor-background: var(--editor-bg);
--vscode-editorGutter-background: var(--editor-bg);
}

.editor-container .monaco-editor .overflow-guard {
width: 100% !important;
height: 100% !important;
}

/* Fix Monaco textarea/input area */
.editor-container .monaco-editor .inputarea {
min-width: 0 !important;
width: auto !important;
}

.editor-container .monaco-editor .lines-content {
min-width: 100% !important;
}

.editor-container .monaco-editor .view-lines {
min-width: 100% !important;
}

/* Ensure editor scrollable element works correctly */
.editor-container .monaco-editor .monaco-scrollable-element.editor-scrollable {
width: 100% !important;
}

/* Monaco Editor Links */
.editor-container .monaco-editor a {
border-bottom: none;
}

.editor-container .monaco-editor a:hover {
border-bottom: none;
}

/* Monaco fixed widgets */
.editor-container .monaco-editor .overflowingContentWidgets {
z-index: 100;
}

/* Preview Panel */
.playground-panel {
flex: 1;
Expand Down Expand Up @@ -199,15 +251,17 @@
}

.playground-panel-result,
.playground-panel-console {
.playground-panel-console,
.playground-panel-terminal {
position: absolute;
inset: 0;
display: none;
overflow: auto;
}

.playground-panel-result.active,
.playground-panel-console.active {
.playground-panel-console.active,
.playground-panel-terminal.active {
display: block;
}

Expand Down Expand Up @@ -300,6 +354,30 @@
color: #dcdcaa;
}

/* Terminal (xterm.js) */
.playground-panel-terminal {
background: var(--terminal-bg);
overflow: hidden;
}

.playground-panel-terminal .playground-terminal-container {
width: 100%;
height: 100%;
}

/* xterm.js overrides for better integration */
.playground-panel-terminal .xterm {
padding: 8px;
}

.playground-panel-terminal .xterm-viewport {
overflow-y: auto !important;
}

.playground-panel-terminal .xterm-screen {
width: 100% !important;
}

.playground-console-message.info {
color: #75beff;
}
Comment on lines 381 to 383
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate CSS rule for .playground-console-message.info.

This rule (lines 329-331) duplicates the one at lines 286-289. This appears to be an artifact from inserting the terminal styles. Consider removing the duplicate.

Proposed fix
 .playground-panel-terminal .xterm-screen {
   width: 100% !important;
 }
-
-.playground-console-message.info {
-  color: `#75beff`;
-}

 /* Legacy Preview (keep for compatibility) */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.playground-console-message.info {
color: #75beff;
}
🤖 Prompt for AI Agents
In `@apps/demo/src/playground.css` around lines 329 - 331, The CSS rule for
`.playground-console-message.info` with `color: `#75beff`;` at lines 329-331 is a
duplicate of the same rule that already exists at lines 286-289. Remove the
duplicate rule block entirely from the playground.css file to eliminate the
redundancy.

Expand Down
161 changes: 161 additions & 0 deletions apps/demo/src/react.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import type { Template } from '@setemiojo/playground-core'

export const reactTemplate: Template = {
id: 'react',
name: 'React',
description: 'React 19 with Vite',
files: {
'package.json': {
file: {
contents: JSON.stringify(
{
name: 'react-playground',
type: 'module',
scripts: {
dev: 'vite',
build: 'vite build',
},
dependencies: {
'react': '^19.2.3',
'react-dom': '^19.2.3',
},
devDependencies: {
'@types/react': '^19.2.8',
'@types/react-dom': '^19.2.3',
'@vitejs/plugin-react': '^5.1.2',
'vite': '^7.3.1',
},
},
null,
2,
),
},
},
'vite.config.js': {
file: {
contents: `import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
plugins: [react()],
});`,
},
},
'index.html': {
file: {
contents: `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>React Playground</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>`,
},
},
'src': {
directory: {
'main.jsx': {
file: {
contents: `import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);`,
},
},
'App.jsx': {
file: {
contents: `import { useState } from 'react';
import './App.css';

function App() {
const [count, setCount] = useState(0);

return (
<div className="App">
<h1>React Playground</h1>
<p>Edit src/App.jsx to see changes instantly.</p>

<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
Count is {count}
</button>
</div>
</div>
);
}

export default App;`,
},
},
'App.css': {
file: {
contents: `.App {
text-align: center;
padding: 40px;
}

h1 {
color: #333;
margin-bottom: 20px;
}

.card {
padding: 20px;
}

button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}

button:hover {
background: #0056b3;
}`,
},
},
'index.css': {
file: {
contents: `body {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}`,
},
},
},
},
},
dependencies: {
'react': '^19.2.3',
'react-dom': '^19.2.3',
},
devDependencies: {
'@types/react': '^19.2.8',
'@types/react-dom': '^19.2.3',
'@vitejs/plugin-react': '^5.1.2',
'vite': '^7.3.1',
},
commands: {
dev: 'npm run dev',
},
entryFile: '/src/App.jsx',
// Example: hide config files from the file tree
hiddenFiles: ['/vite.config.js', '/package.json'],
}
Loading