Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 85 additions & 123 deletions docs/public/editor/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,50 +15,22 @@
overflow: hidden;
}

/* Editor textarea sizing & behavior */
.editor-textarea {
flex: 1;
width: 100%;
/* CodeMirror editor sizing */
.cm-editor {
height: 100%;
padding: 12px 16px 12px 60px;
font-size: 13px;
line-height: 1.6;
border: none;
resize: none;
outline: none;
tab-size: 2;
-moz-tab-size: 2;
overflow: auto;
background: var(--bgColor-default, var(--color-canvas-default));
color: var(--fgColor-default, var(--color-fg-default));
}
.editor-textarea::placeholder {
color: var(--fgColor-muted, var(--color-fg-muted));
}

/* Line numbers gutter */
.line-numbers {
position: absolute;
top: 0;
left: 0;
width: 48px;
height: 100%;
overflow: hidden;
z-index: 2;
background: var(--bgColor-muted, var(--color-canvas-subtle));
border-right: 1px solid var(--borderColor-default, var(--color-border-default));
.cm-editor .cm-scroller {
overflow: auto;
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
line-height: 1.6;
}
.line-numbers-inner {
padding: 12px 0;
.cm-editor .cm-gutters {
font-size: 13px;
line-height: 1.6;
text-align: right;
user-select: none;
color: var(--fgColor-muted, var(--color-fg-muted));
}
.line-numbers-inner div {
padding-right: 12px;
height: 20.8px;
.cm-editor.cm-focused {
outline: none;
}

/* Draggable divider */
Expand Down Expand Up @@ -101,16 +73,6 @@
background: var(--fgColor-accent, var(--color-accent-fg));
}

/* Output pre sizing */
.output-pre {
margin: 0;
padding: 12px 16px;
font-size: 13px;
line-height: 1.6;
white-space: pre;
min-height: 100%;
}

/* Loading overlay & spinner */
.loading-overlay {
position: fixed;
Expand Down Expand Up @@ -278,20 +240,7 @@
<span class="kbd-hint-other">Ctrl+Enter</span>
</kbd>
</div>
<div class="flex-1 d-flex position-relative" style="min-height: 0; overflow: hidden;">
<div class="line-numbers" id="lineNumbers">
<div class="line-numbers-inner text-mono" id="lineNumbersInner"></div>
</div>
<textarea
class="editor-textarea text-mono"
id="editor"
spellcheck="false"
autocapitalize="off"
autocomplete="off"
autocorrect="off"
placeholder="Write your agentic workflow here..."
></textarea>
</div>
<div class="flex-1" style="min-height: 0; overflow: hidden;" id="editorMount"></div>
</div>

<div class="divider" id="divider"></div>
Expand All @@ -306,11 +255,11 @@
Compiled Output (.lock.yml)
</span>
</div>
<div class="flex-1 overflow-auto color-bg-subtle" style="min-height: 0;" id="outputContainer">
<div class="flex-1" style="min-height: 0; overflow: hidden;" id="outputContainer">
<div class="d-flex flex-items-center flex-justify-center color-fg-muted f5 p-4 text-center" style="height: 100%;" id="outputPlaceholder">
Compiled YAML will appear here
</div>
<pre class="output-pre text-mono color-fg-default d-none" id="outputPre"></pre>
<div id="outputMount" style="height: 100%; display: none;"></div>
</div>
</div>
</div>
Expand Down Expand Up @@ -342,6 +291,13 @@
// gh-aw Playground - Application Logic
// ================================================================

import { EditorView, basicSetup } from 'https://esm.sh/codemirror@6.0.2';
import { EditorState, Compartment } from 'https://esm.sh/@codemirror/state@6.5.4';
import { keymap } from 'https://esm.sh/@codemirror/view@6.39.14';
import { yaml } from 'https://esm.sh/@codemirror/lang-yaml@6.1.2';
import { markdown } from 'https://esm.sh/@codemirror/lang-markdown@6.5.0';
import { indentUnit } from 'https://esm.sh/@codemirror/language@6.12.1';
import { oneDark } from 'https://esm.sh/@codemirror/theme-one-dark@6.1.3';
import { createWorkerCompiler } from '/gh-aw/wasm/compiler-loader.js';

// ---------------------------------------------------------------
Expand All @@ -367,9 +323,9 @@
// ---------------------------------------------------------------
const $ = (id) => document.getElementById(id);

const editor = $('editor');
const outputPre = $('outputPre');
const editorMount = $('editorMount');
const outputPlaceholder = $('outputPlaceholder');
const outputMount = $('outputMount');
Comment on lines 327 to +328
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

The editorMount element is referenced directly using $('editorMount') on line 408, but unlike other DOM elements (lines 325-342), it's not stored in a constant. Consider adding const editorMount = $('editorMount'); to the DOM Elements section for consistency with the established pattern.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done. Added const editorMount = $('editorMount') to the DOM Elements section.

const outputContainer = $('outputContainer');
const compileBtn = $('compileBtn');
const statusBadge = $('statusBadge');
Expand All @@ -380,7 +336,6 @@
const errorText = $('errorText');
const warningBanner = $('warningBanner');
const warningText = $('warningText');
const lineNumbersInner = $('lineNumbersInner');
const themeToggle = $('themeToggle');
const toggleTrack = $('toggleTrack');
const divider = $('divider');
Expand All @@ -401,12 +356,19 @@
// ---------------------------------------------------------------
// Theme (uses Primer's data-color-mode)
// ---------------------------------------------------------------
const editorThemeConfig = new Compartment();
const outputThemeConfig = new Compartment();

function getPreferredTheme() {
const saved = localStorage.getItem('gh-aw-playground-theme');
if (saved) return saved;
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}

function cmThemeFor(theme) {
return theme === 'dark' ? oneDark : [];
}

function setTheme(theme) {
document.documentElement.setAttribute('data-color-mode', theme);
localStorage.setItem('gh-aw-playground-theme', theme);
Expand All @@ -419,8 +381,55 @@
sunIcon.style.display = 'none';
moonIcon.style.display = 'block';
}

// Update CodeMirror themes
const cmTheme = cmThemeFor(theme);
editorView.dispatch({ effects: editorThemeConfig.reconfigure(cmTheme) });
outputView.dispatch({ effects: outputThemeConfig.reconfigure(cmTheme) });
}

// ---------------------------------------------------------------
// CodeMirror: Input Editor (Markdown with YAML frontmatter)
// ---------------------------------------------------------------
const editorView = new EditorView({
doc: DEFAULT_CONTENT,
extensions: [
basicSetup,
markdown(),
EditorState.tabSize.of(2),
indentUnit.of(' '),
editorThemeConfig.of(cmThemeFor(getPreferredTheme())),
keymap.of([{
key: 'Mod-Enter',
run: () => { doCompile(); return true; }
}]),
EditorView.updateListener.of(update => {
if (update.docChanged && autoCompile && isReady) {
scheduleCompile();
}
}),
],
parent: editorMount,
});

// ---------------------------------------------------------------
// CodeMirror: Output View (YAML, read-only)
// ---------------------------------------------------------------
const outputView = new EditorView({
doc: '',
extensions: [
basicSetup,
yaml(),
EditorState.readOnly.of(true),
EditorView.editable.of(false),
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

Both EditorState.readOnly and EditorView.editable are set for the output view. These settings are redundant - using EditorState.readOnly.of(true) alone is sufficient to make the editor read-only. Consider removing EditorView.editable.of(false) for cleaner code.

Suggested change
EditorView.editable.of(false),

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Intentionally keeping both. readOnly prevents state modifications but still shows a cursor and accepts focus. editable.of(false) removes the contenteditable attribute from the DOM entirely, so the output panel has no cursor, no text selection caret, and doesn't accept keyboard focus — which is the right UX for a display-only output panel.

outputThemeConfig.of(cmThemeFor(getPreferredTheme())),
],
parent: outputMount,
});

// ---------------------------------------------------------------
// Apply initial theme + listen for changes
// ---------------------------------------------------------------
setTheme(getPreferredTheme());

themeToggle.addEventListener('click', () => {
Expand Down Expand Up @@ -467,57 +476,6 @@
}
}

// ---------------------------------------------------------------
// Line numbers
// ---------------------------------------------------------------
function updateLineNumbers() {
const lines = editor.value.split('\n').length;
let html = '';
for (let i = 1; i <= lines; i++) {
html += '<div>' + i + '</div>';
}
lineNumbersInner.innerHTML = html;
}

function syncLineNumberScroll() {
const lineNumbers = $('lineNumbers');
lineNumbers.scrollTop = editor.scrollTop;
}

// ---------------------------------------------------------------
// Editor setup
// ---------------------------------------------------------------
editor.value = DEFAULT_CONTENT;
updateLineNumbers();

editor.addEventListener('input', () => {
updateLineNumbers();
if (autoCompile && isReady) {
scheduleCompile();
}
});

editor.addEventListener('scroll', syncLineNumberScroll);

// Tab key inserts 2 spaces
editor.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
e.preventDefault();
const start = editor.selectionStart;
const end = editor.selectionEnd;
const val = editor.value;
editor.value = val.substring(0, start) + ' ' + val.substring(end);
editor.selectionStart = editor.selectionEnd = start + 2;
editor.dispatchEvent(new Event('input'));
}

// Ctrl+Enter or Cmd+Enter to compile
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
doCompile();
}
});

// ---------------------------------------------------------------
// Auto-compile toggle
// ---------------------------------------------------------------
Expand Down Expand Up @@ -545,10 +503,10 @@
compileTimer = null;
}

const md = editor.value;
const md = editorView.state.doc.toString();
if (!md.trim()) {
outputPre.classList.add('d-none');
outputPlaceholder.classList.remove('d-none');
outputMount.style.display = 'none';
outputPlaceholder.style.display = 'flex';
outputPlaceholder.textContent = 'Compiled YAML will appear here';
currentYaml = '';
return;
Expand All @@ -572,9 +530,13 @@
} else {
setStatus('ready', 'Ready');
currentYaml = result.yaml;
outputPre.textContent = result.yaml;
outputPre.classList.remove('d-none');
outputPlaceholder.classList.add('d-none');

// Update output CodeMirror view
outputView.dispatch({
changes: { from: 0, to: outputView.state.doc.length, insert: result.yaml }
});
outputMount.style.display = 'block';
outputPlaceholder.style.display = 'none';

if (result.warnings && result.warnings.length > 0) {
warningText.textContent = result.warnings.join('\n');
Expand Down