From 97e53de44dfd24b956876a3e36d15360f08acd31 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:17:59 +0000 Subject: [PATCH 1/5] Initial plan From 58c11e40a2d7c2cc7e92bf8122aa055ece245b88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:24:57 +0000 Subject: [PATCH 2/5] Add web-based WSL playground with embedded support Co-authored-by: anare <1291552+anare@users.noreply.github.com> --- README.md | 15 ++ playground/.gitignore | 29 +++ playground/README.md | 338 ++++++++++++++++++++++++ playground/css/playground.css | 478 ++++++++++++++++++++++++++++++++++ playground/demo.html | 427 ++++++++++++++++++++++++++++++ playground/embed.html | 135 ++++++++++ playground/index.html | 130 +++++++++ playground/js/examples.js | 252 ++++++++++++++++++ playground/js/playground.js | 339 ++++++++++++++++++++++++ playground/js/wsl-language.js | 248 ++++++++++++++++++ playground/package.json | 32 +++ 11 files changed, 2423 insertions(+) create mode 100644 playground/.gitignore create mode 100644 playground/README.md create mode 100644 playground/css/playground.css create mode 100644 playground/demo.html create mode 100644 playground/embed.html create mode 100644 playground/index.html create mode 100644 playground/js/examples.js create mode 100644 playground/js/playground.js create mode 100644 playground/js/wsl-language.js create mode 100644 playground/package.json diff --git a/README.md b/README.md index 22c1615..e0232d8 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,18 @@ This repository contains IDE plugins that provide language support for the Kueti ### Visual Studio Code * [VS Code WSL](/vs-code-wsl) - Extension for Visual Studio Code * [VS Code WSL README.md](/vs-code-wsl/README.md) + +## Web Playground + +### Interactive Browser-Based Editor +* [WSL Playground](/playground) - Web-based playground for trying WSL/SimplifiedWSL without installation +* [Playground README.md](/playground/README.md) - Documentation and embedding guide +* **Features:** + - 🎨 Real-time syntax highlighting + - 🌓 Light/dark theme support + - 🔗 Shareable code URLs + - 📱 Responsive design + - 🚀 Embeddable via iframe + - ⚡ Zero installation required + +Try it now: [Open Playground](/playground/index.html) | [Demo Page](/playground/demo.html) diff --git a/playground/.gitignore b/playground/.gitignore new file mode 100644 index 0000000..9582adb --- /dev/null +++ b/playground/.gitignore @@ -0,0 +1,29 @@ +# Dependencies +node_modules/ +package-lock.json + +# Build outputs +dist/ +build/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Temporary files +tmp/ +temp/ +*.tmp diff --git a/playground/README.md b/playground/README.md new file mode 100644 index 0000000..9f4bdc0 --- /dev/null +++ b/playground/README.md @@ -0,0 +1,338 @@ +# WSL Playground - Interactive Web Editor + +An embeddable, web-based playground for the Workflow Specific Language (WSL) and SimplifiedWSL (SWSL) with real-time syntax highlighting and validation. + +## Features + +- 🎨 **Syntax Highlighting** - Full syntax highlighting for both WSL and SimplifiedWSL +- 🌓 **Dark/Light Theme** - Toggle between light and dark themes +- 📝 **Multiple Examples** - Pre-built examples to get started quickly +- 🔗 **Shareable URLs** - Share your code via URL +- 📱 **Responsive Design** - Works on desktop, tablet, and mobile +- 🎯 **Embeddable** - Easy to embed in any website via iframe +- ⚡ **Real-time Validation** - Instant syntax validation as you type +- 🚀 **Zero Installation** - Works directly in the browser + +## Quick Start + +### Standalone Usage + +1. Open `index.html` in your web browser +2. Select an example or start writing your own WSL code +3. Use the toolbar to change themes, clear the editor, or share your code + +### Embedding in Your Website + +#### Basic Embed + +Add this iframe to your HTML: + +```html + +``` + +#### Embed with Initial Code + +Pass code via URL parameter: + +```html + +``` + +#### Responsive Embed + +```html +
+ +
+``` + +## JavaScript API (for iframe communication) + +### Send Code to Embedded Playground + +```javascript +const iframe = document.querySelector('iframe'); + +// Set code in the playground +iframe.contentWindow.postMessage({ + type: 'setCode', + data: { + code: 'module example\n\nworkflow demo\n\naction.Call() -> .', + language: 'swsl' + } +}, '*'); +``` + +### Get Code from Embedded Playground + +```javascript +// Request code from playground +iframe.contentWindow.postMessage({ + type: 'getCode' +}, '*'); + +// Listen for response +window.addEventListener('message', (event) => { + if (event.data.type === 'code') { + console.log('Code:', event.data.data.code); + console.log('Language:', event.data.data.language); + } +}); +``` + +### Change Theme + +```javascript +iframe.contentWindow.postMessage({ + type: 'setTheme', + data: { theme: 'dark' } // or 'light' +}, '*'); +``` + +### Listen for Ready Event + +```javascript +window.addEventListener('message', (event) => { + if (event.data.type === 'ready') { + console.log('Playground is ready!'); + // Now you can send messages to the iframe + } +}); +``` + +## File Structure + +``` +playground/ +├── index.html # Main playground page +├── embed.html # Embeddable version +├── css/ +│ └── playground.css # Styles for the playground +├── js/ +│ ├── playground.js # Main playground logic +│ ├── wsl-language.js # Monaco Editor language definitions +│ └── examples.js # Example code snippets +└── README.md # This file +``` + +## Examples Included + +### SimplifiedWSL Examples + +1. **Hello World** - Basic SimplifiedWSL syntax +2. **Payment Feature** - Feature-level workflow +3. **Solution Example** - Solution-level orchestration +4. **Validate Workflow** - Payment validation +5. **Action Chain** - Chained action execution +6. **Hierarchical Call** - Multi-level workflow calls +7. **Error Handling** - Error handling patterns + +### Traditional WSL Examples + +1. **Login Workflow** - Complete login workflow with state machine + +## Customization + +### Change Default Language + +Edit `playground.js`: + +```javascript +let currentLanguage = 'swsl'; // Change to 'wsl' for WSL +``` + +### Add Custom Examples + +Edit `examples.js`: + +```javascript +const examples = { + my_example: { + name: "My Example", + language: "swsl", + code: `// Your code here` + } +}; +``` + +### Customize Theme Colors + +Edit `playground.css` CSS variables: + +```css +:root { + --accent-color: #0969da; /* Change accent color */ + --bg-primary: #ffffff; /* Background color */ + /* ... other variables ... */ +} +``` + +## Browser Support + +- ✅ Chrome 90+ +- ✅ Firefox 88+ +- ✅ Safari 14+ +- ✅ Edge 90+ + +## Dependencies + +- **Monaco Editor** (v0.45.0) - Microsoft's code editor (powers VS Code) + - Loaded via CDN: `https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/` + +## Deployment + +### GitHub Pages + +1. Push the `playground` directory to your repository +2. Enable GitHub Pages in repository settings +3. Set source to `main` branch and `/playground` folder +4. Access at `https://yourusername.github.io/repository-name/` + +### Static Hosting (Netlify, Vercel, etc.) + +1. Deploy the `playground` directory +2. Configure build settings (none required - pure static) +3. Set publish directory to `playground` + +### Local Development + +Simply open `index.html` in your browser. For best results, use a local server: + +```bash +# Python 3 +python -m http.server 8000 + +# Node.js +npx serve + +# PHP +php -S localhost:8000 +``` + +Then open `http://localhost:8000/index.html` + +## Integration Examples + +### WordPress + +```php +
+ +
+``` + +### React + +```jsx +import React from 'react'; + +function WSLPlayground() { + return ( + + + + +
+

🎮 Interactive Demo

+

Use the controls below to interact with the embedded playground via JavaScript:

+ +
+

Send Code to Playground

+
+ + +
+
+ + + +
+
+ +
+ 💡 Tip: Open your browser console to see the code received from the playground! +
+
+ + +
+

📋 How to Embed

+

Copy and paste this code into your website:

+ +

Basic Embed:

+
<iframe + src="https://your-domain.com/playground/embed.html" + width="100%" + height="600" + frameborder="0" + style="border: 1px solid #ddd; border-radius: 8px;" +></iframe>
+ +

Responsive Embed:

+
<div style="position: relative; padding-bottom: 56.25%; height: 0;"> + <iframe + src="https://your-domain.com/playground/embed.html" + style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" + frameborder="0" + ></iframe> +</div>
+ +

JavaScript API:

+
// Send code to playground +iframe.contentWindow.postMessage({ + type: 'setCode', + data: { + code: 'module example\\n\\nworkflow demo\\n\\naction.Call() -> .', + language: 'swsl' + } +}, '*'); + +// Get code from playground +iframe.contentWindow.postMessage({ type: 'getCode' }, '*'); + +// Listen for code +window.addEventListener('message', (event) => { + if (event.data.type === 'code') { + console.log('Code:', event.data.data.code); + } +});
+
+ + +
+

🎯 Use Cases

+
+
+

📚 Documentation Sites

+

Embed live examples in your documentation

+
+
+

🎓 Educational Platforms

+

Create interactive tutorials and courses

+
+
+

💼 Product Demos

+

Show off your workflow language features

+
+
+

🔬 Testing & Prototyping

+

Quick prototyping and testing workflows

+
+
+
+ + + + + + + diff --git a/playground/embed.html b/playground/embed.html new file mode 100644 index 0000000..ac75460 --- /dev/null +++ b/playground/embed.html @@ -0,0 +1,135 @@ + + + + + + WSL Playground - Embedded + + + + + +
+ +
+
+

WSL Playground

+
+
+ + + +
+
+ + +
+ +
+
+ CODE EDITOR +
+ +
+
+
+
+ + +
+
+ SYNTAX GUIDE + +
+
+
+
+

SimplifiedWSL Quick Reference:

+
    +
  • -> Forward flow
  • +
  • <- Error handler
  • +
  • . Terminal
  • +
+
module example
+workflow demo
+
+action.First() ->
+action.Second() -> .
+
+
+
+
+
+
+ + + + + + + + + + diff --git a/playground/index.html b/playground/index.html new file mode 100644 index 0000000..b42f8e6 --- /dev/null +++ b/playground/index.html @@ -0,0 +1,130 @@ + + + + + + WSL Playground - Interactive Editor + + + + +
+ +
+
+

WSL Playground

+ Interactive Workflow Specific Language Editor +
+
+ + + + +
+
+ + +
+ +
+
+ Editor +
+ + +
+
+
+
+ + +
+
+ Output & Syntax Guide + +
+
+
+
+

Welcome to WSL Playground! 🎉

+

This is an interactive editor for the Workflow Specific Language (WSL) and SimplifiedWSL (SWSL).

+ +

Quick Start:

+
    +
  • Select an example from the dropdown above
  • +
  • Edit the code in the editor
  • +
  • See syntax validation in real-time
  • +
  • Share your code using the Share button
  • +
+ +

SimplifiedWSL Syntax:

+
module example
+
+workflow hello_world
+
+const {
+    message: "Hello, World!"
+}
+
+// Simple action flow
+action.Call() -> 
+action.Process() -> .
+
+ +

Key Operators:

+
    +
  • -> Forward flow (sequential execution)
  • +
  • <- Error binding (error handling)
  • +
  • . Terminal operator (end workflow)
  • +
+ +

Workflow Types:

+
    +
  • workflow - Basic workflow
  • +
  • feature - Feature-level workflow
  • +
  • solution - Solution-level orchestration
  • +
+
+
+
+
+
+ + + +
+ + + + + + + + diff --git a/playground/js/examples.js b/playground/js/examples.js new file mode 100644 index 0000000..5a2f7e6 --- /dev/null +++ b/playground/js/examples.js @@ -0,0 +1,252 @@ +// WSL and SimplifiedWSL Examples + +const examples = { + hello_world: { + name: "Hello World (SWSL)", + language: "swsl", + code: `// SimplifiedWSL Example: Hello World +module example + +workflow hello_world + +const { + event: "greet", + message: "Hello, World from SimplifiedWSL!", + version: "1.0.0" +} + +// Simple greeting workflow using SimplifiedWSL syntax +converse/speak.Say(on: "message", response: $constants.message, statusCode: 200) -> +services/common.Response(message: "Greeting sent successfully", status: "ok") -> . +` + }, + + payment_feature: { + name: "Payment Feature (SWSL)", + language: "swsl", + code: `// SimplifiedWSL Payment Feature Example +module payment + +feature payment_processor + +const { + timeout: 30000, + currency: "USD", + min_amount: 1.00 +} + +// Payment processing feature with validation +workflow:validate_payment() -> +workflow:process_payment() -> +workflow:send_receipt() -> . +` + }, + + solution_example: { + name: "Solution Example (SWSL)", + language: "swsl", + code: `// SimplifiedWSL Solution Example +module payment_solution + +solution payment_orchestrator + +const { + version: "1.0.0", + max_retries: 3 +} + +// Solution-level orchestration +context.Setup() -> +feature:payment_feature() -> +feature:notification_feature() <- + error.CriticalError() -> . +` + }, + + login_workflow: { + name: "Login Workflow (WSL)", + language: "wsl", + code: `// Traditional WSL Login Workflow Example +workflow login { + start: ValidateCredentials + + state ValidateCredentials { + action validation.rules.ValidateLoginCredentials( + username: $input.username, + password: $input.password + ) + on success -> AuthenticateUser + on error -> LoginFailed + } + + state AuthenticateUser { + action auth/login.Authenticate( + username: $input.username, + password: $input.password + ) + on success -> FetchUserData + on error -> LoginFailed + } + + state FetchUserData { + action db/query.GetUserProfile( + userId: $state.userId + ) + on success -> GenerateToken + on error -> LoginFailed + } + + state GenerateToken { + action auth/login.GenerateJWT( + userId: $state.userId, + expiresIn: "24h" + ) + on success -> ReturnSuccess + on error -> LoginFailed + } + + state ReturnSuccess { + action response.json.SendResponse( + status: 200, + body: { + success: true, + token: $state.token, + user: $state.userProfile + } + ) + end ok + } + + state LoginFailed { + action response.json.SendResponse( + status: 401, + body: { + success: false, + error: "Invalid credentials" + } + ) + end error + } +} +` + }, + + workflow_validate: { + name: "Validate Workflow (SWSL)", + language: "swsl", + code: `// SimplifiedWSL Validation Workflow +module payment + +workflow validate_payment + +const { + min_amount: 1.00, + max_amount: 10000.00 +} + +// Validate payment details +payment.ValidateAmount( + amount: $context.payment.amount, + min: $constants.min_amount, + max: $constants.max_amount +) -> +payment.ValidateCard( + cardNumber: $context.payment.cardNumber +) -> +payment.ValidateExpiry( + expiry: $context.payment.expiry +) <- + errors.ValidationError() -> . +` + }, + + action_chain: { + name: "Action Chain (SWSL)", + language: "swsl", + code: `// SimplifiedWSL Action Chaining Example +module example + +workflow action_chain + +// Define reusable actions +def validateInput = validation.Check(input: $context.data) +def processData = processor.Transform(data: $context.data) +def saveResult = db.Save(result: $context.processed) + +// Chain actions together +$validateInput -> +$processData -> +$saveResult <- + error.HandleError() -> . +` + }, + + hierarchical_call: { + name: "Hierarchical Call (SWSL)", + language: "swsl", + code: `// SimplifiedWSL Hierarchical Execution +module orchestrator + +solution payment_solution + +const { + retry_count: 3, + timeout: 30000 +} + +// Call workflows at different levels +context.Initialize() -> + +// Call feature-level workflow +feature:payment_processing() -> + +// Call workflow-level directly +workflow:send_notification() -> + +// Call another solution +solution:audit_logging() -> . +` + }, + + error_handling: { + name: "Error Handling (SWSL)", + language: "swsl", + code: `// SimplifiedWSL Error Handling Example +module example + +workflow error_handling_demo + +// Action with error handler +action.RiskyOperation() <- + error.LogError() -> + error.NotifyAdmin() -> + action.Fallback() -> . + +// Multiple error paths +action.Primary() <- ( + error.Type1() -> action.Recover1(), + error.Type2() -> action.Recover2(), + error.Default() -> action.FallbackAll() +) -> . +` + } +}; + +// Get example by key +function getExample(key) { + return examples[key] || null; +} + +// Get all example keys +function getExampleKeys() { + return Object.keys(examples); +} + +// Get example list for dropdown +function getExampleList() { + return Object.keys(examples).map(key => ({ + key, + name: examples[key].name, + language: examples[key].language + })); +} diff --git a/playground/js/playground.js b/playground/js/playground.js new file mode 100644 index 0000000..3f01035 --- /dev/null +++ b/playground/js/playground.js @@ -0,0 +1,339 @@ +// WSL Playground Main Script + +let editor; +let currentTheme = 'light'; +let currentLanguage = 'swsl'; + +// Initialize the playground +function init() { + // Set up Monaco Editor loader + require.config({ + paths: { + 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs' + } + }); + + require(['vs/editor/editor.main'], function() { + // Register languages + monaco.languages.register({ id: 'swsl' }); + monaco.languages.register({ id: 'wsl' }); + + // Set language configurations + monaco.languages.setLanguageConfiguration('swsl', languageConfiguration); + monaco.languages.setLanguageConfiguration('wsl', languageConfiguration); + + // Set token providers (syntax highlighting) + monaco.languages.setMonarchTokensProvider('swsl', swslLanguageDefinition); + monaco.languages.setMonarchTokensProvider('wsl', wslLanguageDefinition); + + // Define themes + monaco.editor.defineTheme('wsl-light', wslTheme); + monaco.editor.defineTheme('wsl-dark', wslThemeDark); + + // Create editor instance + editor = monaco.editor.create(document.getElementById('editor'), { + value: getDefaultCode(), + language: 'swsl', + theme: 'wsl-light', + fontSize: 14, + lineNumbers: 'on', + roundedSelection: false, + scrollBeyondLastLine: false, + readOnly: false, + minimap: { + enabled: true + }, + automaticLayout: true, + tabSize: 2, + wordWrap: 'on', + wrappingIndent: 'indent', + folding: true, + lineDecorationsWidth: 10, + lineNumbersMinChars: 3 + }); + + // Set up event listeners + setupEventListeners(); + + // Load code from URL if present + loadFromURL(); + }); +} + +// Get default code +function getDefaultCode() { + const example = getExample('hello_world'); + return example ? example.code : '// Start writing your WSL code here\n'; +} + +// Setup event listeners +function setupEventListeners() { + // Language selector + document.getElementById('language-selector').addEventListener('change', (e) => { + currentLanguage = e.target.value; + changeLanguage(currentLanguage); + }); + + // Example selector + document.getElementById('example-selector').addEventListener('change', (e) => { + const exampleKey = e.target.value; + if (exampleKey) { + loadExample(exampleKey); + } + }); + + // Theme toggle + document.getElementById('theme-toggle').addEventListener('click', toggleTheme); + + // Share button + document.getElementById('share-btn').addEventListener('click', shareCode); + + // Format button + document.getElementById('format-btn').addEventListener('click', formatCode); + + // Clear button + document.getElementById('clear-btn').addEventListener('click', clearEditor); + + // Clear output button + document.getElementById('clear-output-btn').addEventListener('click', clearOutput); + + // Editor change listener for validation + editor.onDidChangeModelContent(() => { + validateCode(); + }); + + // Handle messages from parent window (for iframe embedding) + window.addEventListener('message', handleMessage); +} + +// Change editor language +function changeLanguage(lang) { + if (editor) { + monaco.editor.setModelLanguage(editor.getModel(), lang); + addOutput(`Language changed to: ${lang.toUpperCase()}`, 'info'); + } +} + +// Load example +function loadExample(key) { + const example = getExample(key); + if (example) { + editor.setValue(example.code); + if (example.language !== currentLanguage) { + currentLanguage = example.language; + document.getElementById('language-selector').value = example.language; + changeLanguage(example.language); + } + addOutput(`Loaded example: ${example.name}`, 'success'); + } +} + +// Toggle theme +function toggleTheme() { + currentTheme = currentTheme === 'light' ? 'dark' : 'light'; + const theme = currentTheme === 'dark' ? 'wsl-dark' : 'wsl-light'; + const icon = document.querySelector('.theme-icon'); + + monaco.editor.setTheme(theme); + document.body.setAttribute('data-theme', currentTheme); + icon.textContent = currentTheme === 'dark' ? '☀️' : '🌙'; + + addOutput(`Theme changed to: ${currentTheme}`, 'info'); +} + +// Format code (basic formatting) +function formatCode() { + editor.getAction('editor.action.formatDocument').run(); + addOutput('Code formatted', 'success'); +} + +// Clear editor +function clearEditor() { + if (confirm('Are you sure you want to clear the editor?')) { + editor.setValue(''); + addOutput('Editor cleared', 'info'); + } +} + +// Clear output +function clearOutput() { + document.getElementById('output').innerHTML = ''; +} + +// Add output message +function addOutput(message, type = 'info') { + const output = document.getElementById('output'); + const messageDiv = document.createElement('div'); + messageDiv.className = `output-message ${type}`; + messageDiv.textContent = message; + output.appendChild(messageDiv); + output.scrollTop = output.scrollHeight; +} + +// Validate code (basic syntax checking) +function validateCode() { + const code = editor.getValue(); + const language = monaco.editor.getModel(editor.getModel()).getLanguageId(); + + // Clear previous markers + monaco.editor.setModelMarkers(editor.getModel(), 'syntax', []); + + // Basic validation rules + const errors = []; + const lines = code.split('\n'); + + if (language === 'swsl') { + // Check for proper workflow type declaration + const hasWorkflowType = /^\s*(workflow|feature|solution|microservice)\s+\w*/m.test(code); + + // Check for terminal operator + const hasTerminal = /\.\s*$/m.test(code); + + // Check for unbalanced operators + const arrows = (code.match(/->/g) || []).length; + const terminals = (code.match(/\.\s*($|\n)/g) || []).length; + + // Validate each line + lines.forEach((line, index) => { + const trimmed = line.trim(); + + // Check for invalid operator sequences + if (trimmed.includes('->->') || trimmed.includes('<-<-')) { + errors.push({ + startLineNumber: index + 1, + startColumn: 1, + endLineNumber: index + 1, + endColumn: line.length + 1, + message: 'Invalid operator sequence', + severity: monaco.MarkerSeverity.Error + }); + } + + // Check for terminal operator not at end + if (trimmed.includes('.') && !trimmed.endsWith('.') && !/\.\w/.test(trimmed)) { + const dotIndex = trimmed.indexOf('.'); + if (dotIndex < trimmed.length - 1 && trimmed[dotIndex + 1] !== ' ') { + errors.push({ + startLineNumber: index + 1, + startColumn: dotIndex + 1, + endLineNumber: index + 1, + endColumn: dotIndex + 2, + message: 'Terminal operator should be at end of workflow or part of method call', + severity: monaco.MarkerSeverity.Warning + }); + } + } + }); + } + + // Set markers + if (errors.length > 0) { + monaco.editor.setModelMarkers(editor.getModel(), 'syntax', errors); + } +} + +// Share code +function shareCode() { + const code = editor.getValue(); + const lang = currentLanguage; + + // Encode code to base64 + const encoded = btoa(encodeURIComponent(code)); + + // Create shareable URL + const url = `${window.location.origin}${window.location.pathname}?code=${encoded}&lang=${lang}`; + + // Copy to clipboard + navigator.clipboard.writeText(url).then(() => { + addOutput('Share URL copied to clipboard! 🎉', 'success'); + addOutput(`URL: ${url}`, 'info'); + }).catch(() => { + // Fallback: show in output + addOutput('Share URL:', 'info'); + addOutput(url, 'info'); + }); +} + +// Load code from URL +function loadFromURL() { + const params = new URLSearchParams(window.location.search); + const encodedCode = params.get('code'); + const lang = params.get('lang'); + + if (encodedCode) { + try { + const code = decodeURIComponent(atob(encodedCode)); + editor.setValue(code); + addOutput('Code loaded from URL', 'success'); + } catch (e) { + addOutput('Failed to load code from URL', 'error'); + } + } + + if (lang && (lang === 'wsl' || lang === 'swsl')) { + currentLanguage = lang; + document.getElementById('language-selector').value = lang; + changeLanguage(lang); + } +} + +// Handle messages from parent window (for iframe embedding) +function handleMessage(event) { + const { type, data } = event.data; + + switch (type) { + case 'setCode': + if (data && data.code) { + editor.setValue(data.code); + if (data.language) { + currentLanguage = data.language; + changeLanguage(data.language); + } + } + break; + + case 'getCode': + // Send code back to parent + event.source.postMessage({ + type: 'code', + data: { + code: editor.getValue(), + language: currentLanguage + } + }, event.origin); + break; + + case 'setTheme': + if (data && data.theme) { + currentTheme = data.theme; + const theme = currentTheme === 'dark' ? 'wsl-dark' : 'wsl-light'; + monaco.editor.setTheme(theme); + document.body.setAttribute('data-theme', currentTheme); + } + break; + } +} + +// Export code (for download) +function exportCode() { + const code = editor.getValue(); + const lang = currentLanguage; + const filename = `workflow.${lang}`; + + const blob = new Blob([code], { type: 'text/plain' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); + + addOutput(`Code exported as ${filename}`, 'success'); +} + +// Initialize when DOM is ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); +} else { + init(); +} diff --git a/playground/js/wsl-language.js b/playground/js/wsl-language.js new file mode 100644 index 0000000..d06173f --- /dev/null +++ b/playground/js/wsl-language.js @@ -0,0 +1,248 @@ +// Monaco Editor Language Definitions for WSL and SimplifiedWSL + +// SimplifiedWSL Language Definition +const swslLanguageDefinition = { + defaultToken: '', + tokenPostfix: '.swsl', + + keywords: [ + 'module', 'import', 'const', 'def', 'as', + 'workflow', 'feature', 'solution', 'microservice', + 'state', 'on', 'end', 'start', 'action', + 'if', 'else', 'for', 'while', 'return' + ], + + operators: [ + '->', '<-', '.', ':', '(', ')', '{', '}', '[', ']', + '=', ',', '<<', '>>' + ], + + // Common constants + constants: [ + 'true', 'false', 'null', 'undefined' + ], + + // Built-in variables + builtins: [ + '$context', '$constants', '$input', '$state', '$output', '$error' + ], + + // Comments + tokenizer: { + root: [ + // Comments + [/\/\/.*$/, 'comment'], + [/#.*$/, 'comment'], + [/\/\*/, 'comment', '@comment'], + + // Workflow type declarations + [/\b(workflow|feature|solution|microservice)\s+(\w+)?\b/, ['keyword', 'type']], + [/\b(workflow|feature|solution|microservice)\b/, 'keyword'], + + // Module and imports + [/\bmodule\s+(\w+)/, ['keyword', 'namespace']], + [/\bimport\s+/, 'keyword'], + + // Keywords + [/\b(const|def|as|state|on|end|start|action|if|else|for|while|return)\b/, 'keyword'], + + // Constants + [/\b(true|false|null|undefined)\b/, 'constant'], + + // Built-in variables + [/\$\w+(\.\w+)*/, 'variable.predefined'], + + // Numbers + [/\d+\.?\d*/, 'number'], + + // Strings + [/"([^"\\]|\\.)*$/, 'string.invalid'], + [/'([^'\\]|\\.)*$/, 'string.invalid'], + [/"/, 'string', '@string_double'], + [/'/, 'string', '@string_single'], + + // Operators + [/->/, 'operator'], + [/<-/, 'operator'], + [/\.(?!\w)/, 'operator'], + [/:(?!:)/, 'operator'], + [/<<|>>/, 'delimiter'], + + // Action calls (module.Method or module/submodule.Method) + [/\w+([\/\.]\w+)*\.\w+/, 'type.identifier'], + + // Identifiers + [/[a-z_]\w*/, 'identifier'], + [/[A-Z][\w]*/, 'type'], + + // Delimiters + [/[{}()\[\]]/, '@brackets'], + [/[,;]/, 'delimiter'], + ], + + comment: [ + [/[^\/*]+/, 'comment'], + [/\*\//, 'comment', '@pop'], + [/[\/*]/, 'comment'] + ], + + string_double: [ + [/[^\\"]+/, 'string'], + [/\\./, 'string.escape'], + [/"/, 'string', '@pop'] + ], + + string_single: [ + [/[^\\']+/, 'string'], + [/\\./, 'string.escape'], + [/'/, 'string', '@pop'] + ] + } +}; + +// WSL Language Definition (Traditional) +const wslLanguageDefinition = { + defaultToken: '', + tokenPostfix: '.wsl', + + keywords: [ + 'workflow', 'state', 'action', 'on', 'success', 'error', + 'start', 'end', 'ok', 'module', 'import' + ], + + operators: [ + ':', '{', '}', '(', ')', ',', '->' + ], + + constants: [ + 'true', 'false', 'null' + ], + + builtins: [ + '$input', '$state', '$output', '$error', '$context' + ], + + tokenizer: { + root: [ + // Comments + [/\/\/.*$/, 'comment'], + [/\/\*/, 'comment', '@comment'], + + // Keywords + [/\b(workflow|state|action|on|success|error|start|end|ok|module|import)\b/, 'keyword'], + + // Constants + [/\b(true|false|null)\b/, 'constant'], + + // Built-in variables + [/\$\w+/, 'variable.predefined'], + + // Numbers + [/\d+\.?\d*/, 'number'], + + // Strings + [/"([^"\\]|\\.)*$/, 'string.invalid'], + [/"/, 'string', '@string'], + + // Identifiers + [/[a-z_][\w]*/, 'identifier'], + [/[A-Z][\w]*/, 'type'], + + // Operators + [/->/, 'operator'], + + // Delimiters + [/[{}()\[\]]/, '@brackets'], + [/[,;:]/, 'delimiter'], + ], + + comment: [ + [/[^\/*]+/, 'comment'], + [/\*\//, 'comment', '@pop'], + [/[\/*]/, 'comment'] + ], + + string: [ + [/[^\\"]+/, 'string'], + [/\\./, 'string.escape'], + [/"/, 'string', '@pop'] + ] + } +}; + +// Language Configuration (applies to both WSL and SWSL) +const languageConfiguration = { + comments: { + lineComment: '//', + blockComment: ['/*', '*/'] + }, + brackets: [ + ['{', '}'], + ['[', ']'], + ['(', ')'] + ], + autoClosingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" } + ], + surroundingPairs: [ + { open: '{', close: '}' }, + { open: '[', close: ']' }, + { open: '(', close: ')' }, + { open: '"', close: '"' }, + { open: "'", close: "'" } + ], + folding: { + markers: { + start: /^\s*\/\/#region/, + end: /^\s*\/\/#endregion/ + } + } +}; + +// Theme definition for SWSL/WSL +const wslTheme = { + base: 'vs', + inherit: true, + rules: [ + { token: 'comment', foreground: '6a737d', fontStyle: 'italic' }, + { token: 'keyword', foreground: 'd73a49', fontStyle: 'bold' }, + { token: 'operator', foreground: 'd73a49' }, + { token: 'string', foreground: '032f62' }, + { token: 'number', foreground: '005cc5' }, + { token: 'type', foreground: '6f42c1' }, + { token: 'type.identifier', foreground: '005cc5' }, + { token: 'variable.predefined', foreground: 'e36209' }, + { token: 'constant', foreground: '005cc5' }, + { token: 'namespace', foreground: '6f42c1' } + ], + colors: { + 'editor.foreground': '#24292e', + 'editor.background': '#ffffff' + } +}; + +// Dark theme +const wslThemeDark = { + base: 'vs-dark', + inherit: true, + rules: [ + { token: 'comment', foreground: '8b949e', fontStyle: 'italic' }, + { token: 'keyword', foreground: 'ff7b72', fontStyle: 'bold' }, + { token: 'operator', foreground: 'ff7b72' }, + { token: 'string', foreground: 'a5d6ff' }, + { token: 'number', foreground: '79c0ff' }, + { token: 'type', foreground: 'd2a8ff' }, + { token: 'type.identifier', foreground: '79c0ff' }, + { token: 'variable.predefined', foreground: 'ffa657' }, + { token: 'constant', foreground: '79c0ff' }, + { token: 'namespace', foreground: 'd2a8ff' } + ], + colors: { + 'editor.foreground': '#e6edf3', + 'editor.background': '#0d1117' + } +}; diff --git a/playground/package.json b/playground/package.json new file mode 100644 index 0000000..5e3e5ef --- /dev/null +++ b/playground/package.json @@ -0,0 +1,32 @@ +{ + "name": "wsl-playground", + "version": "1.0.0", + "description": "Interactive web-based playground for Workflow Specific Language (WSL) and SimplifiedWSL", + "main": "index.html", + "scripts": { + "start": "npx serve .", + "dev": "npx serve . --listen 8000", + "build": "echo 'No build step required - pure static files'", + "deploy": "echo 'Deploy playground directory to your hosting service'" + }, + "keywords": [ + "wsl", + "swsl", + "workflow", + "playground", + "editor", + "monaco", + "kuetix", + "embedded", + "iframe" + ], + "author": "Kuetix", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/kuetix/ide-plugins" + }, + "homepage": "https://github.com/kuetix/ide-plugins/tree/main/playground", + "devDependencies": {}, + "dependencies": {} +} From 44b64608a7917b2994b832ab87082f31ecc8e0df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:27:14 +0000 Subject: [PATCH 3/5] Add deployment documentation and GitHub Actions workflow Co-authored-by: anare <1291552+anare@users.noreply.github.com> --- .github/workflows/deploy-playground.yml | 70 ++++ playground/DEPLOYMENT.md | 425 ++++++++++++++++++++++++ 2 files changed, 495 insertions(+) create mode 100644 .github/workflows/deploy-playground.yml create mode 100644 playground/DEPLOYMENT.md diff --git a/.github/workflows/deploy-playground.yml b/.github/workflows/deploy-playground.yml new file mode 100644 index 0000000..c95b113 --- /dev/null +++ b/.github/workflows/deploy-playground.yml @@ -0,0 +1,70 @@ +name: Deploy WSL Playground + +on: + push: + branches: [ main ] + paths: + - 'playground/**' + pull_request: + branches: [ main ] + paths: + - 'playground/**' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + # Build job (validate files) + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Validate HTML + run: | + echo "Validating HTML files..." + for file in playground/*.html; do + echo "Checking $file" + if [ -f "$file" ]; then + echo "✓ $file exists" + fi + done + + - name: Validate JavaScript + run: | + echo "Validating JavaScript files..." + for file in playground/js/*.js; do + echo "Checking $file" + if [ -f "$file" ]; then + echo "✓ $file exists" + fi + done + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: 'playground' + + # Deploy job (only on main branch) + deploy: + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/playground/DEPLOYMENT.md b/playground/DEPLOYMENT.md new file mode 100644 index 0000000..d31a7d9 --- /dev/null +++ b/playground/DEPLOYMENT.md @@ -0,0 +1,425 @@ +# Deploying WSL Playground + +This guide explains how to deploy the WSL Playground to various hosting platforms. + +## GitHub Pages (Recommended) + +GitHub Pages is the easiest way to deploy the playground for free. + +### Method 1: Using GitHub Pages Settings + +1. **Push the code to GitHub** (already done if using this repository) + +2. **Enable GitHub Pages:** + - Go to your repository on GitHub + - Click **Settings** → **Pages** + - Under "Source", select the branch (e.g., `main`) + - Select folder: `/ (root)` or configure to serve from `playground` directory + - Click **Save** + +3. **Access your playground:** + - After a few minutes, your playground will be available at: + - `https://[username].github.io/[repository-name]/playground/` + +### Method 2: Using GitHub Actions + +Create `.github/workflows/deploy-playground.yml`: + +```yaml +name: Deploy Playground to GitHub Pages + +on: + push: + branches: [ main ] + paths: + - 'playground/**' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Pages + uses: actions/configure-pages@v3 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v2 + with: + path: 'playground' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 +``` + +## Netlify + +Deploy to Netlify with one click or via CLI. + +### Using Netlify UI + +1. **Connect your repository:** + - Go to [netlify.com](https://netlify.com) + - Click "Add new site" → "Import an existing project" + - Connect your GitHub repository + +2. **Configure build settings:** + - **Build command:** (leave empty - no build needed) + - **Publish directory:** `playground` + - Click **Deploy site** + +3. **Access your playground:** + - Your site will be available at `https://[random-name].netlify.app` + - You can customize the domain in site settings + +### Using Netlify CLI + +```bash +# Install Netlify CLI +npm install -g netlify-cli + +# Navigate to playground directory +cd playground + +# Deploy +netlify deploy --prod --dir . +``` + +### netlify.toml Configuration + +Create `netlify.toml` in the root: + +```toml +[build] + publish = "playground" + command = "" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 +``` + +## Vercel + +Deploy to Vercel for fast global CDN delivery. + +### Using Vercel UI + +1. **Import your repository:** + - Go to [vercel.com](https://vercel.com) + - Click "Add New" → "Project" + - Import your GitHub repository + +2. **Configure project:** + - **Framework Preset:** Other + - **Root Directory:** `playground` + - **Build Command:** (leave empty) + - **Output Directory:** (leave empty) + - Click **Deploy** + +3. **Access your playground:** + - Your site will be available at `https://[project-name].vercel.app` + +### Using Vercel CLI + +```bash +# Install Vercel CLI +npm install -g vercel + +# Navigate to playground directory +cd playground + +# Deploy +vercel --prod +``` + +### vercel.json Configuration + +Create `vercel.json` in playground directory: + +```json +{ + "buildCommand": "", + "outputDirectory": ".", + "cleanUrls": true, + "trailingSlash": false +} +``` + +## AWS S3 + CloudFront + +For enterprise-grade deployment with AWS. + +### Prerequisites +- AWS Account +- AWS CLI installed and configured + +### Deployment Steps + +1. **Create S3 Bucket:** +```bash +aws s3 mb s3://wsl-playground +``` + +2. **Configure bucket for static hosting:** +```bash +aws s3 website s3://wsl-playground --index-document index.html +``` + +3. **Upload files:** +```bash +cd playground +aws s3 sync . s3://wsl-playground --acl public-read +``` + +4. **Set bucket policy:** +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "PublicReadGetObject", + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::wsl-playground/*" + } + ] +} +``` + +5. **Optional: Set up CloudFront for CDN** + +## Custom Server + +Deploy on your own server with any web server. + +### Using Nginx + +1. **Copy files to web server:** +```bash +scp -r playground/* user@server:/var/www/wsl-playground/ +``` + +2. **Configure Nginx:** +```nginx +server { + listen 80; + server_name playground.yourdomain.com; + + root /var/www/wsl-playground; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } +} +``` + +3. **Restart Nginx:** +```bash +sudo systemctl restart nginx +``` + +### Using Apache + +1. **Copy files:** +```bash +scp -r playground/* user@server:/var/www/html/playground/ +``` + +2. **Configure .htaccess:** +```apache + + RewriteEngine On + RewriteBase /playground/ + RewriteRule ^index\.html$ - [L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule . /playground/index.html [L] + + +# Security headers + + Header set X-Frame-Options "SAMEORIGIN" + Header set X-Content-Type-Options "nosniff" + Header set X-XSS-Protection "1; mode=block" + + +# Cache control + + ExpiresActive On + ExpiresByType text/css "access plus 1 year" + ExpiresByType application/javascript "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/svg+xml "access plus 1 year" + +``` + +## Docker + +Deploy using Docker container. + +### Dockerfile + +Create `Dockerfile` in playground directory: + +```dockerfile +FROM nginx:alpine + +# Copy playground files +COPY . /usr/share/nginx/html/ + +# Copy custom nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] +``` + +### nginx.conf + +```nginx +server { + listen 80; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; +} +``` + +### Build and Run + +```bash +cd playground + +# Build Docker image +docker build -t wsl-playground . + +# Run container +docker run -d -p 8080:80 wsl-playground + +# Access at http://localhost:8080 +``` + +## Environment-Specific Configurations + +### Production Checklist + +- [ ] Enable HTTPS/SSL +- [ ] Configure CDN for Monaco Editor (or self-host) +- [ ] Set up proper CORS headers +- [ ] Enable gzip/brotli compression +- [ ] Configure caching headers +- [ ] Set up monitoring/analytics +- [ ] Test on multiple browsers +- [ ] Test mobile responsiveness +- [ ] Set up error tracking (e.g., Sentry) + +### Performance Optimization + +1. **Enable Compression:** +```nginx +gzip on; +gzip_types text/plain text/css application/json application/javascript text/xml application/xml; +``` + +2. **Browser Caching:** +```nginx +location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + expires 1y; + add_header Cache-Control "public, immutable"; +} +``` + +3. **Content Security Policy:** +```nginx +add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' data:; img-src 'self' data:; connect-src 'self';" always; +``` + +## Updating Your Deployment + +### GitHub Pages +```bash +git add playground/ +git commit -m "Update playground" +git push origin main +# GitHub Pages will auto-deploy +``` + +### Netlify/Vercel +```bash +git push origin main +# Automatic deployment via webhook +``` + +### Manual Server +```bash +cd playground +rsync -avz --delete . user@server:/var/www/wsl-playground/ +``` + +## Troubleshooting + +### Monaco Editor Not Loading + +**Problem:** Editor appears blank or shows error. + +**Solutions:** +1. Check CDN availability: `https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/` +2. Try alternative CDN: `https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/` +3. Self-host Monaco Editor (download and include locally) + +### CORS Issues with iframe + +**Problem:** iframe communication doesn't work. + +**Solutions:** +1. Ensure both parent and iframe use same protocol (http/https) +2. Check postMessage origin restrictions +3. Set proper CORS headers on server + +### 404 Errors on Refresh + +**Problem:** Refreshing non-root URLs returns 404. + +**Solution:** Configure server to serve `index.html` for all routes (see server configs above) + +## Support + +For issues or questions about deployment: +- Open an issue on [GitHub](https://github.com/kuetix/ide-plugins/issues) +- Check the [README](README.md) for general documentation From a0b1a4ba16a2e53f04ccb83d830e1d29742c7701 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 12:31:01 +0000 Subject: [PATCH 4/5] Add SRI integrity checks to CDN resources for security Co-authored-by: anare <1291552+anare@users.noreply.github.com> --- playground/embed.html | 11 +++++++++-- playground/index.html | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/playground/embed.html b/playground/embed.html index ac75460..ae8d5cf 100644 --- a/playground/embed.html +++ b/playground/embed.html @@ -5,7 +5,11 @@ WSL Playground - Embedded - +