Add some new commands for the Crawl4ai script transpiler and creating an interactive tutorial that allows users to go through multiple steps and apply the syntax to automate the page. Fixed some issues and add several new commands for setting input values, variables, clearing input fields, and more.
This commit is contained in:
667
docs/examples/c4a_script/tutorial/assets/app.css
Normal file
667
docs/examples/c4a_script/tutorial/assets/app.css
Normal file
@@ -0,0 +1,667 @@
|
||||
/* ================================================================
|
||||
C4A-Script Tutorial - App Layout CSS
|
||||
Terminal theme with Dank Mono font
|
||||
================================================================ */
|
||||
|
||||
/* CSS Variables */
|
||||
:root {
|
||||
--bg-primary: #070708;
|
||||
--bg-secondary: #0e0e10;
|
||||
--bg-tertiary: #1a1a1b;
|
||||
--border-color: #2a2a2c;
|
||||
--border-hover: #3a3a3c;
|
||||
--text-primary: #e0e0e0;
|
||||
--text-secondary: #8b8b8d;
|
||||
--text-muted: #606065;
|
||||
--primary-color: #0fbbaa;
|
||||
--primary-hover: #0da89a;
|
||||
--primary-dim: #0a8577;
|
||||
--error-color: #ff5555;
|
||||
--warning-color: #ffb86c;
|
||||
--success-color: #50fa7b;
|
||||
--info-color: #8be9fd;
|
||||
--code-bg: #1e1e20;
|
||||
--modal-overlay: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* Base Reset */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Fonts */
|
||||
@font-face {
|
||||
font-family: 'Dank Mono';
|
||||
src: url('DankMono-Regular.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Dank Mono';
|
||||
src: url('DankMono-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Dank Mono';
|
||||
src: url('DankMono-Italic.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Body & App Container */
|
||||
body {
|
||||
font-family: 'Dank Mono', 'Monaco', 'Consolas', monospace;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Panels */
|
||||
.editor-panel,
|
||||
.playground-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.editor-panel {
|
||||
flex: 1;
|
||||
background: var(--bg-secondary);
|
||||
border-right: 1px solid var(--border-color);
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.playground-panel {
|
||||
flex: 1;
|
||||
background: var(--bg-primary);
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
/* Panel Headers */
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: var(--bg-tertiary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.panel-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Action Buttons */
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-hover);
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
background: var(--primary-color);
|
||||
color: var(--bg-primary);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.action-btn.primary:hover {
|
||||
background: var(--primary-hover);
|
||||
border-color: var(--primary-hover);
|
||||
}
|
||||
|
||||
.action-btn .icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* Editor Wrapper */
|
||||
.editor-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1; /* Ensure it's above any potential overlays */
|
||||
}
|
||||
|
||||
.editor-wrapper .CodeMirror {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
font-family: 'Dank Mono', monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Ensure CodeMirror is interactive */
|
||||
.CodeMirror {
|
||||
background: var(--bg-primary) !important;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
/* Make cursor more visible */
|
||||
.CodeMirror-cursor {
|
||||
border-left: 2px solid var(--primary-color) !important;
|
||||
border-left-width: 2px !important;
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
/* Ensure cursor is visible when focused */
|
||||
.CodeMirror-focused .CodeMirror-cursor {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
/* Fix for CodeMirror in flex container */
|
||||
.CodeMirror-sizer {
|
||||
min-height: auto !important;
|
||||
}
|
||||
|
||||
/* Remove aggressive pointer-events override */
|
||||
.CodeMirror-code {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.editor-wrapper textarea {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Output Section (Bottom of Editor) */
|
||||
.output-section {
|
||||
height: 250px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.tabs {
|
||||
display: flex;
|
||||
background: var(--bg-tertiary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 8px 20px;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
color: var(--primary-color);
|
||||
border-bottom-color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Tab Content */
|
||||
.tab-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
display: none;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tab-pane.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Console */
|
||||
.console {
|
||||
padding: 12px;
|
||||
background: var(--bg-primary);
|
||||
font-size: 13px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.console-line {
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.console-prompt {
|
||||
color: var(--primary-color);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.console-text {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.console-error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.console-warning {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
|
||||
.console-success {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
/* JavaScript Output */
|
||||
.js-output-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 8px 12px;
|
||||
background: var(--bg-tertiary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.js-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mini-btn {
|
||||
padding: 4px 8px;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.mini-btn:hover {
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.js-output {
|
||||
padding: 12px;
|
||||
background: var(--code-bg);
|
||||
color: var(--text-primary);
|
||||
font-family: 'Dank Mono', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
margin: 0;
|
||||
min-height: calc(100% - 44px);
|
||||
}
|
||||
|
||||
/* Execution Progress */
|
||||
.execution-progress {
|
||||
padding: 12px;
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.progress-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.progress-icon {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.progress-item.active .progress-icon {
|
||||
color: var(--info-color);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
.progress-item.completed .progress-icon {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.progress-item.error .progress-icon {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
/* Playground */
|
||||
.playground-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#playground-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
/* Tutorial Intro Modal */
|
||||
.tutorial-intro-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--modal-overlay);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2000;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.tutorial-intro-modal.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.intro-content {
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 32px;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.intro-content h2 {
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 16px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.intro-content p {
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.intro-content ul {
|
||||
list-style: none;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.intro-content li {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 8px;
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.intro-content li:before {
|
||||
content: "▸";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.intro-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.intro-btn {
|
||||
padding: 10px 24px;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.intro-btn:hover {
|
||||
background: var(--bg-primary);
|
||||
border-color: var(--border-hover);
|
||||
}
|
||||
|
||||
.intro-btn.primary {
|
||||
background: var(--primary-color);
|
||||
color: var(--bg-primary);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.intro-btn.primary:hover {
|
||||
background: var(--primary-hover);
|
||||
border-color: var(--primary-hover);
|
||||
}
|
||||
|
||||
/* Tutorial Navigation Bar */
|
||||
.tutorial-nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--bg-tertiary);
|
||||
border-bottom: 1px solid var(--primary-color);
|
||||
z-index: 1000;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.tutorial-nav.hidden {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.tutorial-nav-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.tutorial-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tutorial-step-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tutorial-step-title span:first-child {
|
||||
color: var(--text-secondary);
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tutorial-step-title span:last-child {
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.tutorial-description {
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.tutorial-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tutorial-progress-bar {
|
||||
height: 3px;
|
||||
background: var(--bg-secondary);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.tutorial-progress-bar .progress-fill {
|
||||
height: 100%;
|
||||
background: var(--primary-color);
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
/* Adjust app container when tutorial is active */
|
||||
.app-container.tutorial-active {
|
||||
padding-top: 80px;
|
||||
}
|
||||
|
||||
.tutorial-controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 8px 16px;
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.nav-btn:hover:not(:disabled) {
|
||||
background: var(--bg-primary);
|
||||
border-color: var(--border-hover);
|
||||
}
|
||||
|
||||
.nav-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.nav-btn.primary {
|
||||
background: var(--primary-color);
|
||||
color: var(--bg-primary);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.nav-btn.primary:hover {
|
||||
background: var(--primary-hover);
|
||||
border-color: var(--primary-hover);
|
||||
}
|
||||
|
||||
.exit-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
border: none;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.exit-btn:hover {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Fullscreen Mode */
|
||||
.playground-panel.fullscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1500;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
/* Scrollbar Styling */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--border-color);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--border-hover);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.app-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.editor-panel,
|
||||
.playground-panel {
|
||||
min-width: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.editor-panel {
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.output-section {
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
625
docs/examples/c4a_script/tutorial/assets/app.js
Normal file
625
docs/examples/c4a_script/tutorial/assets/app.js
Normal file
@@ -0,0 +1,625 @@
|
||||
// C4A-Script Tutorial App Controller
|
||||
class TutorialApp {
|
||||
constructor() {
|
||||
this.editor = null;
|
||||
this.currentScript = '';
|
||||
this.currentJS = [];
|
||||
this.isEditingJS = false;
|
||||
this.tutorialMode = false;
|
||||
this.currentStep = 0;
|
||||
this.tutorialSteps = [];
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.setupEditors();
|
||||
this.setupButtons();
|
||||
this.setupTabs();
|
||||
this.setupTutorial();
|
||||
this.checkFirstVisit();
|
||||
}
|
||||
|
||||
setupEditors() {
|
||||
// C4A Script Editor
|
||||
const c4aTextarea = document.getElementById('c4a-editor');
|
||||
this.editor = CodeMirror.fromTextArea(c4aTextarea, {
|
||||
mode: 'javascript', // Use JS mode for now
|
||||
theme: 'material-darker',
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
autoCloseBrackets: true,
|
||||
matchBrackets: true,
|
||||
readOnly: false,
|
||||
cursorBlinkRate: 530,
|
||||
inputStyle: 'contenteditable' // Changed from 'textarea' to prevent cursor issues
|
||||
});
|
||||
|
||||
// Save script on change
|
||||
this.editor.on('change', () => {
|
||||
this.currentScript = this.editor.getValue();
|
||||
localStorage.setItem('c4a-script', this.currentScript);
|
||||
});
|
||||
|
||||
// Load saved script
|
||||
const saved = localStorage.getItem('c4a-script');
|
||||
if (saved && !this.tutorialMode) {
|
||||
this.editor.setValue(saved);
|
||||
}
|
||||
|
||||
// Ensure editor is properly sized and interactive
|
||||
setTimeout(() => {
|
||||
this.editor.refresh();
|
||||
// Set cursor position instead of just focusing
|
||||
const doc = this.editor.getDoc();
|
||||
doc.setCursor(doc.lineCount() - 1, 0);
|
||||
this.editor.focus();
|
||||
}, 100);
|
||||
|
||||
// Single unified click handler for focus
|
||||
const editorElement = this.editor.getWrapperElement();
|
||||
editorElement.addEventListener('mousedown', (e) => {
|
||||
// Use mousedown instead of click for immediate response
|
||||
e.stopPropagation();
|
||||
// Ensure editor gets focus on next tick
|
||||
setTimeout(() => {
|
||||
if (!this.editor.hasFocus()) {
|
||||
this.editor.focus();
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
setupButtons() {
|
||||
// Run button
|
||||
document.getElementById('run-btn').addEventListener('click', () => {
|
||||
this.runScript();
|
||||
});
|
||||
|
||||
// Clear button
|
||||
document.getElementById('clear-btn').addEventListener('click', () => {
|
||||
this.editor.setValue('');
|
||||
this.clearConsole();
|
||||
});
|
||||
|
||||
// Examples button
|
||||
document.getElementById('examples-btn').addEventListener('click', () => {
|
||||
this.showExamples();
|
||||
});
|
||||
|
||||
// Tutorial button
|
||||
document.getElementById('tutorial-btn').addEventListener('click', () => {
|
||||
this.showIntroModal();
|
||||
});
|
||||
|
||||
// Copy JS button
|
||||
document.getElementById('copy-js-btn').addEventListener('click', () => {
|
||||
this.copyJS();
|
||||
});
|
||||
|
||||
// Edit JS button
|
||||
document.getElementById('edit-js-btn').addEventListener('click', () => {
|
||||
this.toggleJSEdit();
|
||||
});
|
||||
|
||||
// Reset playground
|
||||
document.getElementById('reset-playground').addEventListener('click', () => {
|
||||
this.resetPlayground();
|
||||
});
|
||||
|
||||
// Fullscreen
|
||||
document.getElementById('fullscreen-btn').addEventListener('click', () => {
|
||||
this.toggleFullscreen();
|
||||
});
|
||||
|
||||
// Intro modal buttons
|
||||
document.getElementById('start-tutorial-btn').addEventListener('click', () => {
|
||||
this.hideIntroModal();
|
||||
this.startTutorial();
|
||||
});
|
||||
|
||||
document.getElementById('skip-tutorial-btn').addEventListener('click', () => {
|
||||
this.hideIntroModal();
|
||||
});
|
||||
|
||||
// Tutorial navigation
|
||||
document.getElementById('tutorial-prev').addEventListener('click', () => {
|
||||
this.prevStep();
|
||||
});
|
||||
|
||||
document.getElementById('tutorial-next').addEventListener('click', () => {
|
||||
this.nextStep();
|
||||
});
|
||||
|
||||
document.getElementById('tutorial-exit').addEventListener('click', () => {
|
||||
this.exitTutorial();
|
||||
});
|
||||
}
|
||||
|
||||
setupTabs() {
|
||||
const tabs = document.querySelectorAll('.tab');
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const tabName = tab.getAttribute('data-tab');
|
||||
this.switchTab(tabName);
|
||||
});
|
||||
});
|
||||
|
||||
// Remove execution tab since we're removing it
|
||||
const progressTab = document.querySelector('[data-tab="progress"]');
|
||||
if (progressTab) progressTab.remove();
|
||||
}
|
||||
|
||||
switchTab(tabName) {
|
||||
// Update tab buttons
|
||||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
|
||||
|
||||
// Update tab panes
|
||||
document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
|
||||
document.getElementById(`${tabName}-tab`).classList.add('active');
|
||||
}
|
||||
|
||||
checkFirstVisit() {
|
||||
const hasVisited = localStorage.getItem('c4a-tutorial-visited');
|
||||
if (!hasVisited) {
|
||||
setTimeout(() => this.showIntroModal(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
showIntroModal() {
|
||||
document.getElementById('tutorial-intro').classList.remove('hidden');
|
||||
}
|
||||
|
||||
hideIntroModal() {
|
||||
document.getElementById('tutorial-intro').classList.add('hidden');
|
||||
localStorage.setItem('c4a-tutorial-visited', 'true');
|
||||
}
|
||||
|
||||
setupTutorial() {
|
||||
this.tutorialSteps = [
|
||||
{
|
||||
title: "Welcome to C4A-Script!",
|
||||
description: "C4A-Script is a simple language for web automation. Let's start by handling popups that appear on websites.",
|
||||
script: "# Welcome to C4A-Script Tutorial!\n# Let's start by waiting for the page to load\n\nWAIT `body` 2",
|
||||
validate: () => true
|
||||
},
|
||||
{
|
||||
title: "Handle Cookie Banner",
|
||||
description: "Check if cookie banner exists and accept it.",
|
||||
script: "# Wait for page and handle cookie banner\nWAIT `body` 2\n\n# Check if cookie banner exists, then click accept\nIF (EXISTS `.cookie-banner`) THEN CLICK `.accept`",
|
||||
validate: () => {
|
||||
const iframe = document.getElementById('playground-frame');
|
||||
return !iframe.contentDocument?.querySelector('.cookie-banner');
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Handle Newsletter Popup",
|
||||
description: "Now let's handle the newsletter popup that appears after 3 seconds.",
|
||||
script: "# Wait for newsletter popup to appear\nWAIT 3\n\n# Close the newsletter popup\nIF (EXISTS `#newsletter-popup`) THEN CLICK `.close`",
|
||||
validate: () => {
|
||||
const iframe = document.getElementById('playground-frame');
|
||||
return !iframe.contentDocument?.querySelector('#newsletter-popup');
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Start Interactive Elements",
|
||||
description: "Click the start button to reveal more interactive elements.",
|
||||
script: "# Click the start tutorial button\nCLICK `#start-tutorial`",
|
||||
validate: () => true
|
||||
},
|
||||
{
|
||||
title: "Login Process",
|
||||
description: "Now let's complete the login form with email and password using the new SET command.",
|
||||
script: "# Login process\nCLICK `#login-btn`\nWAIT `.login-form` 2\n\n# Fill form fields using SET\nSET `#email` \"demo@example.com\"\nSET `#password` \"demo123\"\n\n# Submit the form\nCLICK `button[type=\"submit\"]`\nWAIT `.success` 2",
|
||||
validate: () => {
|
||||
const iframe = document.getElementById('playground-frame');
|
||||
return iframe.contentDocument?.querySelector('.user-info')?.style.display === 'flex';
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Navigate and Scroll",
|
||||
description: "Navigate to the catalog and use scrolling to load more products.",
|
||||
script: "# Navigate to catalog\nCLICK `#catalog-link`\nWAIT `.product-grid` 3\n\n# Scroll to load more products\nSCROLL DOWN 500\nWAIT 1\nSCROLL DOWN 500\nWAIT 1\n\n# Apply a filter\nCLICK `.filter-group input[type=\"checkbox\"]`",
|
||||
validate: () => true
|
||||
},
|
||||
{
|
||||
title: "Advanced - Procedures",
|
||||
description: "Create reusable command groups with PROC for common tasks.",
|
||||
script: "# Define a procedure to check login status\nPROC check_login\n IF (NOT EXISTS `.user-info`) THEN CLICK `#login-btn`\n IF (NOT EXISTS `.user-info`) THEN WAIT `.login-form` 2\n IF (NOT EXISTS `.user-info`) THEN SET `#email` \"demo@example.com\"\n IF (NOT EXISTS `.user-info`) THEN SET `#password` \"demo123\"\n IF (NOT EXISTS `.user-info`) THEN CLICK `button[type=\"submit\"]`\nENDPROC\n\n# Example: Navigate between sections\nCLICK `a[href=\"#tabs\"]`\nWAIT 1\nCLICK `a[href=\"#forms\"]`\nWAIT 1\n\n# If we get logged out, use our procedure\ncheck_login",
|
||||
validate: () => true
|
||||
},
|
||||
{
|
||||
title: "More Commands",
|
||||
description: "Explore additional C4A commands with the Forms section.",
|
||||
script: "# First, let's fill the contact form with variables\n\n# Set variables\nSETVAR name = \"Alice Smith\"\nSETVAR msg = \"I'd like to know more about your Premium plan!\"\n\n# Fill contact form\nSET `#contact-name` $name\nSET `#contact-email` \"alice@example.com\"\nSET `#contact-message` $msg\n\n# Select dropdown option\nCLICK `#contact-subject`\nCLICK `option[value=\"support\"]`\nWAIT 0.5\n\n# Submit contact form\nCLICK `.btn-primary`\nWAIT 1\n\n# Now let's do the multi-step survey\nSCROLL DOWN 400\nWAIT 0.5\n\n# Step 1: Personal info\nSET `#full-name` \"Bob Johnson\"\nSET `#survey-email` \"bob@example.com\"\nCLICK `.next-step`\nWAIT 0.5\n\n# Step 2: Select interests (multi-select)\nCLICK `#interests`\nCLICK `option[value=\"technology\"]`\nCLICK `option[value=\"science\"]`\nCLICK `.next-step`\nWAIT 0.5\n\n# Step 3: Submit survey\nCLICK `#submit-survey`\n\n# Check results with JavaScript\nEVAL `console.log('Forms completed successfully!')`",
|
||||
validate: () => true
|
||||
},
|
||||
{
|
||||
title: "Congratulations!",
|
||||
description: "You've mastered C4A-Script basics! You can now automate complex web interactions. Try the examples or create your own scripts.",
|
||||
script: "# 🎉 You've completed the tutorial!\n\n# You learned:\n# ✓ WAIT - Wait for elements or time\n# ✓ IF/THEN - Conditional actions\n# ✓ NOT - Negate conditions\n# ✓ CLICK - Click elements\n# ✓ SET - Set input field values\n# ✓ SETVAR - Create variables\n# ✓ SCROLL - Scroll the page\n# ✓ PROC - Create procedures\n# ✓ And much more!\n\n# Try the Examples button for more scripts\n# Happy automating with C4A-Script!",
|
||||
validate: () => true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
startTutorial() {
|
||||
this.tutorialMode = true;
|
||||
this.currentStep = 0;
|
||||
document.getElementById('tutorial-nav').classList.remove('hidden');
|
||||
document.querySelector('.app-container').classList.add('tutorial-active');
|
||||
this.showStep(0);
|
||||
}
|
||||
|
||||
exitTutorial() {
|
||||
this.tutorialMode = false;
|
||||
document.getElementById('tutorial-nav').classList.add('hidden');
|
||||
document.querySelector('.app-container').classList.remove('tutorial-active');
|
||||
}
|
||||
|
||||
showStep(index) {
|
||||
const step = this.tutorialSteps[index];
|
||||
if (!step) return;
|
||||
|
||||
// Update navigation UI
|
||||
document.getElementById('tutorial-step-info').textContent = `Step ${index + 1} of ${this.tutorialSteps.length}`;
|
||||
document.getElementById('tutorial-title').textContent = step.title;
|
||||
|
||||
// Update progress bar
|
||||
const progress = ((index + 1) / this.tutorialSteps.length) * 100;
|
||||
document.getElementById('tutorial-progress-fill').style.width = `${progress}%`;
|
||||
|
||||
// Update buttons
|
||||
document.getElementById('tutorial-prev').disabled = index === 0;
|
||||
const nextBtn = document.getElementById('tutorial-next');
|
||||
if (index === this.tutorialSteps.length - 1) {
|
||||
nextBtn.textContent = 'Finish';
|
||||
} else {
|
||||
nextBtn.textContent = 'Next →';
|
||||
}
|
||||
|
||||
// Set script
|
||||
this.editor.setValue(step.script);
|
||||
|
||||
// Update description in nav bar
|
||||
document.getElementById('tutorial-description').textContent = step.description;
|
||||
|
||||
// Focus editor after setting content and ensure it's editable
|
||||
// Use requestAnimationFrame for better timing
|
||||
requestAnimationFrame(() => {
|
||||
this.editor.setOption('readOnly', false);
|
||||
this.editor.refresh();
|
||||
// Place cursor at end first, then focus
|
||||
const doc = this.editor.getDoc();
|
||||
const lastLine = doc.lineCount() - 1;
|
||||
const lastCh = doc.getLine(lastLine).length;
|
||||
doc.setCursor(lastLine, lastCh);
|
||||
this.editor.focus();
|
||||
});
|
||||
}
|
||||
|
||||
// Removed showTooltip method - no longer needed
|
||||
|
||||
nextStep() {
|
||||
if (this.currentStep < this.tutorialSteps.length - 1) {
|
||||
this.currentStep++;
|
||||
this.showStep(this.currentStep);
|
||||
} else {
|
||||
this.exitTutorial();
|
||||
}
|
||||
}
|
||||
|
||||
prevStep() {
|
||||
if (this.currentStep > 0) {
|
||||
this.currentStep--;
|
||||
this.showStep(this.currentStep);
|
||||
}
|
||||
}
|
||||
|
||||
async runScript() {
|
||||
const script = this.editor.getValue();
|
||||
if (!script.trim()) {
|
||||
this.addConsoleMessage('No script to run', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearConsole();
|
||||
this.switchTab('console');
|
||||
this.addConsoleMessage('Compiling C4A script...');
|
||||
|
||||
try {
|
||||
// If in JS edit mode, use the edited JS directly
|
||||
if (this.isEditingJS) {
|
||||
const jsCode = document.getElementById('js-output').textContent.split('\n').filter(line => line.trim());
|
||||
await this.executeJS(jsCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile C4A to JS
|
||||
const compiled = await this.compileScript(script);
|
||||
|
||||
if (compiled.success) {
|
||||
this.currentJS = compiled.jsCode;
|
||||
this.displayJS(compiled.jsCode);
|
||||
this.addConsoleMessage(`✓ Compiled successfully (${compiled.jsCode.length} statements)`, 'success');
|
||||
|
||||
// Execute the JS
|
||||
await this.executeJS(compiled.jsCode);
|
||||
} else {
|
||||
// Show compilation error
|
||||
const error = compiled.error;
|
||||
this.addConsoleMessage(`✗ Compilation error at line ${error.line}:${error.column}`, 'error');
|
||||
this.addConsoleMessage(` ${error.message}`, 'error');
|
||||
if (error.suggestion) {
|
||||
this.addConsoleMessage(` 💡 ${error.suggestion}`, 'warning');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.addConsoleMessage(`Error: ${error.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async compileScript(script) {
|
||||
try {
|
||||
const response = await fetch('/api/compile', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ script })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Compilation service unavailable');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
// Return error if compilation service is unavailable
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
line: 1,
|
||||
column: 1,
|
||||
message: "C4A compilation service unavailable. Please ensure the server is running.",
|
||||
suggestion: "Start the C4A server or check your connection"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
displayJS(jsCode) {
|
||||
const formatted = jsCode.map((line, i) =>
|
||||
`${(i + 1).toString().padStart(2, ' ')}. ${line}`
|
||||
).join('\n');
|
||||
|
||||
document.getElementById('js-output').textContent = formatted;
|
||||
}
|
||||
|
||||
async executeJS(jsCode) {
|
||||
this.addConsoleMessage('Executing JavaScript...');
|
||||
|
||||
// Send all code to iframe to execute at once
|
||||
await this.executeInIframe(jsCode);
|
||||
}
|
||||
|
||||
async executeInIframe(jsCode) {
|
||||
const iframe = document.getElementById('playground-frame');
|
||||
const iframeWindow = iframe.contentWindow;
|
||||
|
||||
// Create a unique ID for this execution
|
||||
const executionId = 'exec_' + Date.now();
|
||||
|
||||
// Create the full script to execute in iframe
|
||||
const fullScript = `
|
||||
(async () => {
|
||||
const results = [];
|
||||
try {
|
||||
${jsCode.map((code, i) => `
|
||||
try {
|
||||
${code}
|
||||
results.push({ index: ${i}, success: true, code: ${JSON.stringify(code)} });
|
||||
} catch (error) {
|
||||
results.push({ index: ${i}, success: false, error: error.message, code: ${JSON.stringify(code)} });
|
||||
throw error; // Stop execution on first error
|
||||
}
|
||||
`).join('\n')}
|
||||
|
||||
// Send success message
|
||||
window.parent.postMessage({
|
||||
type: 'c4a-execution-complete',
|
||||
id: '${executionId}',
|
||||
success: true,
|
||||
results: results
|
||||
}, '*');
|
||||
} catch (error) {
|
||||
// Send error message
|
||||
window.parent.postMessage({
|
||||
type: 'c4a-execution-complete',
|
||||
id: '${executionId}',
|
||||
success: false,
|
||||
error: error.message,
|
||||
results: results
|
||||
}, '*');
|
||||
}
|
||||
})();
|
||||
`;
|
||||
|
||||
// Wait for execution result
|
||||
return new Promise((resolve, reject) => {
|
||||
const messageHandler = (event) => {
|
||||
if (event.data.type === 'c4a-execution-complete' && event.data.id === executionId) {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
|
||||
// Log results
|
||||
if (event.data.results) {
|
||||
event.data.results.forEach(result => {
|
||||
if (result.success) {
|
||||
this.addConsoleMessage(`✓ ${result.code}`, 'success');
|
||||
} else {
|
||||
this.addConsoleMessage(`✗ ${result.code}`, 'error');
|
||||
this.addConsoleMessage(` Error: ${result.error}`, 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (event.data.success) {
|
||||
this.addConsoleMessage('Execution completed successfully', 'success');
|
||||
resolve();
|
||||
} else {
|
||||
this.addConsoleMessage('Execution stopped due to error', 'error');
|
||||
resolve(); // Still resolve to not break the flow
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', messageHandler);
|
||||
|
||||
// Inject and execute the script
|
||||
const script = iframe.contentDocument.createElement('script');
|
||||
script.textContent = fullScript;
|
||||
iframe.contentDocument.body.appendChild(script);
|
||||
script.remove();
|
||||
|
||||
// Timeout after 10 seconds
|
||||
setTimeout(() => {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
this.addConsoleMessage('Execution timeout', 'warning');
|
||||
resolve();
|
||||
}, 10000);
|
||||
});
|
||||
}
|
||||
|
||||
highlightElement(element) {
|
||||
const originalBorder = element.style.border;
|
||||
const originalBackground = element.style.backgroundColor;
|
||||
|
||||
element.style.border = '2px solid #0fbbaa';
|
||||
element.style.backgroundColor = 'rgba(15, 187, 170, 0.1)';
|
||||
|
||||
setTimeout(() => {
|
||||
element.style.border = originalBorder;
|
||||
element.style.backgroundColor = originalBackground;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Removed progress methods - everything goes to console now
|
||||
|
||||
addConsoleMessage(message, type = 'text') {
|
||||
const consoleEl = document.getElementById('console-output');
|
||||
const line = document.createElement('div');
|
||||
line.className = 'console-line';
|
||||
line.innerHTML = `
|
||||
<span class="console-prompt">$</span>
|
||||
<span class="console-${type}">${message}</span>
|
||||
`;
|
||||
consoleEl.appendChild(line);
|
||||
|
||||
// Scroll the console container (parent of console-output)
|
||||
const consoleContainer = consoleEl.parentElement;
|
||||
if (consoleContainer) {
|
||||
// Use requestAnimationFrame to ensure DOM has updated
|
||||
requestAnimationFrame(() => {
|
||||
consoleContainer.scrollTop = consoleContainer.scrollHeight;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clearConsole() {
|
||||
document.getElementById('console-output').innerHTML = `
|
||||
<div class="console-line">
|
||||
<span class="console-prompt">$</span>
|
||||
<span class="console-text">Ready to run C4A scripts...</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
copyJS() {
|
||||
const text = this.currentJS.join('\n');
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
this.addConsoleMessage('JavaScript copied to clipboard', 'success');
|
||||
});
|
||||
}
|
||||
|
||||
toggleJSEdit() {
|
||||
this.isEditingJS = !this.isEditingJS;
|
||||
const editBtn = document.getElementById('edit-js-btn');
|
||||
const jsOutput = document.getElementById('js-output');
|
||||
|
||||
if (this.isEditingJS) {
|
||||
editBtn.innerHTML = '<span>💾</span>';
|
||||
jsOutput.contentEditable = true;
|
||||
jsOutput.style.outline = '1px solid #0fbbaa';
|
||||
this.addConsoleMessage('JS edit mode enabled - modify and run', 'warning');
|
||||
} else {
|
||||
editBtn.innerHTML = '<span>✏️</span>';
|
||||
jsOutput.contentEditable = false;
|
||||
jsOutput.style.outline = 'none';
|
||||
this.addConsoleMessage('JS edit mode disabled', 'text');
|
||||
}
|
||||
}
|
||||
|
||||
resetPlayground() {
|
||||
const iframe = document.getElementById('playground-frame');
|
||||
iframe.src = iframe.src;
|
||||
this.addConsoleMessage('Playground reset', 'success');
|
||||
}
|
||||
|
||||
toggleFullscreen() {
|
||||
const playgroundPanel = document.querySelector('.playground-panel');
|
||||
playgroundPanel.classList.toggle('fullscreen');
|
||||
}
|
||||
|
||||
showExamples() {
|
||||
const examples = [
|
||||
{
|
||||
name: 'Quick Start - Handle Popups',
|
||||
script: `# Handle popups quickly\nWAIT \`body\` 2\nIF (EXISTS \`.cookie-banner\`) THEN CLICK \`.accept\`\nWAIT 3\nIF (EXISTS \`#newsletter-popup\`) THEN CLICK \`.close\``
|
||||
},
|
||||
{
|
||||
name: 'Complete Login Flow',
|
||||
script: `# Full login process\nCLICK \`#login-btn\`\nWAIT \`.login-form\` 2\n\n# Set credentials using the new SET command\nSET \`#email\` "demo@example.com"\nSET \`#password\` "demo123"\n\n# Submit\nCLICK \`button[type="submit"]\`\nWAIT \`.success\` 2`
|
||||
},
|
||||
{
|
||||
name: 'Product Browsing',
|
||||
script: `# Browse products with filters\nCLICK \`#catalog-link\`\nWAIT \`.product-grid\` 3\n\n# Apply filters\nCLICK \`.filter-button\`\nWAIT 0.5\nCLICK \`input[value="electronics"]\`\n\n# Load more products\nREPEAT (SCROLL DOWN 800, 3)\nWAIT 1`
|
||||
},
|
||||
{
|
||||
name: 'Advanced Form Wizard',
|
||||
script: `# Multi-step form with validation\nCLICK \`a[href="#forms"]\`\nWAIT \`#survey-form\` 2\n\n# Step 1: Personal Info\nSET \`#full-name\` "John Doe"\nSET \`#survey-email\` "john@example.com"\nCLICK \`.next-step\`\nWAIT 1\n\n# Step 2: Preferences\nCLICK \`#interests\`\nCLICK \`option[value="tech"]\`\nCLICK \`option[value="music"]\`\nCLICK \`.next-step\`\nWAIT 1\n\n# Step 3: Submit\nIF (EXISTS \`#comments\`) THEN SET \`#comments\` "Great experience!"\nCLICK \`#submit-survey\`\nWAIT \`.success-message\` 3`
|
||||
}
|
||||
];
|
||||
|
||||
const currentIndex = parseInt(localStorage.getItem('exampleIndex') || '0');
|
||||
const example = examples[currentIndex % examples.length];
|
||||
|
||||
this.editor.setValue(example.script);
|
||||
this.addConsoleMessage(`Loaded example: ${example.name}`, 'success');
|
||||
|
||||
localStorage.setItem('exampleIndex', ((currentIndex + 1) % examples.length).toString());
|
||||
}
|
||||
|
||||
wait(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
|
||||
// Add animations CSS
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes fadeOut {
|
||||
from { opacity: 1; transform: translateY(0); }
|
||||
to { opacity: 0; transform: translateY(-10px); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.tutorialApp = new TutorialApp();
|
||||
console.log('🎓 C4A-Script Tutorial initialized!');
|
||||
});
|
||||
531
docs/examples/c4a_script/tutorial/assets/styles.css
Normal file
531
docs/examples/c4a_script/tutorial/assets/styles.css
Normal file
@@ -0,0 +1,531 @@
|
||||
/* DankMono Font Faces */
|
||||
@font-face {
|
||||
font-family: 'DankMono';
|
||||
src: url('DankMono-Regular.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DankMono';
|
||||
src: url('DankMono-Bold.woff2') format('woff2');
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'DankMono';
|
||||
src: url('DankMono-Italic.woff2') format('woff2');
|
||||
font-weight: 400;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Root Variables - Matching docs theme */
|
||||
:root {
|
||||
--global-font-size: 14px;
|
||||
--global-code-font-size: 13px;
|
||||
--global-line-height: 1.5em;
|
||||
--global-space: 10px;
|
||||
--font-stack: DankMono, Monaco, Courier New, monospace;
|
||||
--mono-font-stack: DankMono, Monaco, Courier New, monospace;
|
||||
|
||||
--background-color: #070708;
|
||||
--font-color: #e8e9ed;
|
||||
--invert-font-color: #222225;
|
||||
--secondary-color: #d5cec0;
|
||||
--tertiary-color: #a3abba;
|
||||
--primary-color: #0fbbaa;
|
||||
--error-color: #ff3c74;
|
||||
--progress-bar-background: #3f3f44;
|
||||
--progress-bar-fill: #09b5a5;
|
||||
--code-bg-color: #3f3f44;
|
||||
--block-background-color: #202020;
|
||||
|
||||
--header-height: 55px;
|
||||
}
|
||||
|
||||
/* Base Styles */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: var(--font-stack);
|
||||
font-size: var(--global-font-size);
|
||||
line-height: var(--global-line-height);
|
||||
color: var(--font-color);
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
/* Terminal Framework */
|
||||
.terminal {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: var(--header-height);
|
||||
background-color: var(--background-color);
|
||||
border-bottom: 1px solid var(--progress-bar-background);
|
||||
z-index: 1000;
|
||||
padding: 0 calc(var(--global-space) * 2);
|
||||
}
|
||||
|
||||
.terminal-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.terminal-logo h1 {
|
||||
margin: 0;
|
||||
font-size: 1.2em;
|
||||
color: var(--primary-color);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.terminal-menu ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
|
||||
.terminal-menu a {
|
||||
color: var(--secondary-color);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.terminal-menu a:hover,
|
||||
.terminal-menu a.active {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Main Container */
|
||||
.main-container {
|
||||
padding-top: calc(var(--header-height) + 2em);
|
||||
padding-left: 2em;
|
||||
padding-right: 2em;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* Tutorial Grid */
|
||||
.tutorial-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2em;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* Terminal Cards */
|
||||
.terminal-card {
|
||||
background-color: var(--block-background-color);
|
||||
border: 1px solid var(--progress-bar-background);
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.terminal-card header {
|
||||
background-color: var(--progress-bar-background);
|
||||
padding: 0.8em 1em;
|
||||
font-weight: 700;
|
||||
color: var(--font-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.terminal-card > div {
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
/* Editor Section */
|
||||
.editor-controls {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#c4a-editor {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: var(--mono-font-stack);
|
||||
font-size: var(--global-code-font-size);
|
||||
background-color: var(--code-bg-color);
|
||||
color: var(--font-color);
|
||||
border: none;
|
||||
padding: 1em;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
/* JS Output */
|
||||
.js-output-container {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.js-output-container pre {
|
||||
margin: 0;
|
||||
padding: 1em;
|
||||
background-color: var(--code-bg-color);
|
||||
}
|
||||
|
||||
.js-output-container code {
|
||||
font-family: var(--mono-font-stack);
|
||||
font-size: var(--global-code-font-size);
|
||||
color: var(--font-color);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* Console Output */
|
||||
.console-output {
|
||||
font-family: var(--mono-font-stack);
|
||||
font-size: var(--global-code-font-size);
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.console-line {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.console-prompt {
|
||||
color: var(--primary-color);
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.console-text {
|
||||
color: var(--font-color);
|
||||
}
|
||||
|
||||
.console-error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.console-success {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
/* Playground */
|
||||
.playground-container {
|
||||
height: 600px;
|
||||
background-color: #fff;
|
||||
border: 1px solid var(--progress-bar-background);
|
||||
}
|
||||
|
||||
#playground-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Execution Progress */
|
||||
.execution-progress {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.progress-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.8em;
|
||||
margin-bottom: 0.8em;
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.progress-item.active {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.progress-item.completed {
|
||||
color: var(--tertiary-color);
|
||||
}
|
||||
|
||||
.progress-item.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.progress-icon {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
background-color: var(--primary-color);
|
||||
color: var(--background-color);
|
||||
border: none;
|
||||
padding: 0.5em 1em;
|
||||
font-family: var(--font-stack);
|
||||
font-size: 0.9em;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: var(--progress-bar-fill);
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.3em 0.8em;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
background-color: transparent;
|
||||
color: var(--secondary-color);
|
||||
border: 1px solid var(--progress-bar-background);
|
||||
}
|
||||
|
||||
.btn-ghost:hover {
|
||||
background-color: var(--progress-bar-background);
|
||||
color: var(--font-color);
|
||||
}
|
||||
|
||||
/* Scrollbars */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--block-background-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--progress-bar-background);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--secondary-color);
|
||||
}
|
||||
|
||||
/* CodeMirror Theme Override */
|
||||
.CodeMirror {
|
||||
font-family: var(--mono-font-stack) !important;
|
||||
font-size: var(--global-code-font-size) !important;
|
||||
background-color: var(--code-bg-color) !important;
|
||||
color: var(--font-color) !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
background-color: var(--progress-bar-background) !important;
|
||||
border-right: 1px solid var(--progress-bar-background) !important;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 1200px) {
|
||||
.tutorial-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.playground-section {
|
||||
order: -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Links */
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Lists */
|
||||
ul, ol {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
/* Code */
|
||||
code {
|
||||
background-color: var(--code-bg-color);
|
||||
padding: 0.2em 0.4em;
|
||||
font-family: var(--mono-font-stack);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* Headings */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 700;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: var(--primary-color);
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
/* Tutorial Panel */
|
||||
.tutorial-panel {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 20px;
|
||||
width: 380px;
|
||||
background: #1a1a1b;
|
||||
border: 1px solid #2a2a2c;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||
z-index: 1000;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.tutorial-panel.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tutorial-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #2a2a2c;
|
||||
}
|
||||
|
||||
.tutorial-header h3 {
|
||||
margin: 0;
|
||||
color: #0fbbaa;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #8b8b8d;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: #2a2a2c;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.tutorial-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.tutorial-content p {
|
||||
margin: 0 0 16px 0;
|
||||
color: #e0e0e0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.tutorial-progress {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.tutorial-progress span {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
color: #8b8b8d;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 4px;
|
||||
background: #2a2a2c;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: #0fbbaa;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.tutorial-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 0 20px 20px;
|
||||
}
|
||||
|
||||
.tutorial-btn {
|
||||
flex: 1;
|
||||
padding: 10px 16px;
|
||||
background: #2a2a2c;
|
||||
color: #e0e0e0;
|
||||
border: 1px solid #3a3a3c;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tutorial-btn:hover:not(:disabled) {
|
||||
background: #3a3a3c;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.tutorial-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.tutorial-btn.primary {
|
||||
background: #0fbbaa;
|
||||
color: #070708;
|
||||
border-color: #0fbbaa;
|
||||
}
|
||||
|
||||
.tutorial-btn.primary:hover {
|
||||
background: #0da89a;
|
||||
border-color: #0da89a;
|
||||
}
|
||||
|
||||
/* Tutorial Highlights */
|
||||
.tutorial-highlight {
|
||||
position: relative;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(15, 187, 170, 0.4);
|
||||
}
|
||||
50% {
|
||||
box-shadow: 0 0 0 10px rgba(15, 187, 170, 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(15, 187, 170, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.editor-card {
|
||||
position: relative;
|
||||
}
|
||||
Reference in New Issue
Block a user