<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SkyCode Editor</title>
<!-- Tailwind CSS & DaisyUI -->
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<!-- xterm.js -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
<!-- Monaco Editor -->
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.52.0/min/vs/loader.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
overflow: hidden;
}
.file-tree {
max-height: calc(100vh - 8rem);
overflow-y: auto;
}
#editor-container {
flex: 1 1 auto;
min-height: 100px;
overflow: hidden;
}
#terminal-container {
flex: 0 0 auto;
height: 200px;
min-height: 100px;
}
.xterm {
padding: 8px;
}
/* Context menu */
.context-menu {
position: fixed;
z-index: 1000;
}
/* Resizer - larger hit area for easier targeting */
.resizer {
height: 6px;
background: oklch(var(--b3));
cursor: ns-resize;
position: relative;
}
.resizer::before {
content: '';
position: absolute;
top: -4px;
left: 0;
right: 0;
height: 14px;
cursor: ns-resize;
}
.resizer:hover {
background: oklch(var(--p));
}
</style>
</head>
<body class="bg-base-200">
<!-- Navbar -->
<div class="navbar bg-base-300 border-b border-base-100 h-12 min-h-12">
<div class="flex-1">
<a href="/" class="btn btn-ghost btn-sm text-lg font-bold">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" />
</svg>
SkyCode
</a>
</div>
<div class="flex-none gap-2">
<span id="status" class="text-xs text-base-content/50"></span>
<button class="btn btn-ghost btn-sm btn-circle" onclick="toggleSearchPanel()" title="Search (Ctrl+Shift+F)">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
</svg>
</button>
<button class="btn btn-ghost btn-sm btn-circle" onclick="openTerminalPanel()" title="Terminal (Ctrl+`)">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z" />
</svg>
</button>
<button class="btn btn-ghost btn-sm btn-circle" onclick="showSettingsModal()" title="Settings">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 010 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 010-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</button>
{{with CurrentUser}}
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm gap-1">
<div class="w-6 rounded-full overflow-hidden">
<img alt="{{.Name}}" src="{{.Avatar}}" class="w-full h-full object-cover" />
</div>
<span class="text-xs text-base-content/70">@{{.Handle}}</span>
</div>
<ul tabindex="0" class="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
<li><a href="https://theskyscape.com/user/{{.Handle}}" target="_blank">View Profile</a></li>
{{if eq .Handle "connor"}}<li><a href="/admin">Admin</a></li>{{end}}
<li><a href="/auth/logout">Logout</a></li>
</ul>
</div>
{{end}}
</div>
</div>
<!-- Main Layout -->
<div class="flex h-[calc(100vh-3rem)]">
<!-- Sidebar - Projects & Files -->
<div class="w-56 bg-base-300 border-r border-base-100 flex flex-col">
<!-- Sidebar Header -->
<div class="p-2 flex justify-between items-center border-b border-base-100">
<span class="text-xs font-semibold text-base-content/70">Projects</span>
<div class="flex gap-1">
<button class="btn btn-ghost btn-xs" onclick="syncWorkspace()" title="Sync workspace">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
</svg>
</button>
<button class="btn btn-ghost btn-xs" onclick="showNewFileModal()" title="New File">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
</button>
</div>
</div>
<!-- Projects List -->
<div class="flex-1 overflow-y-auto file-tree" id="projects-list">
<!-- Projects loaded dynamically -->
</div>
<!-- Git Panel (collapsible at bottom) -->
<div class="border-t border-base-100 flex flex-col" id="git-panel">
<button onclick="toggleGitPanel()" class="w-full p-2 flex items-center justify-between text-xs font-semibold text-base-content/70 hover:bg-base-200">
<span>Git</span>
<svg id="git-panel-chevron" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 transition-transform">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
</button>
<div id="git-content" class="hidden p-2 overflow-y-auto flex-1" style="max-height: 300px;">
<!-- Git status loaded dynamically -->
</div>
</div>
</div>
<!-- Editor + Terminal -->
<div class="flex-1 flex flex-col">
<!-- Tab Bar -->
<div class="bg-base-300 border-b border-base-100 px-2 py-1 flex items-center gap-2">
<div id="tabs" class="flex gap-1 flex-1 overflow-x-auto">
<!-- Tabs loaded dynamically -->
</div>
<button class="btn btn-ghost btn-xs" onclick="saveCurrentFile()" title="Save (Ctrl+S)">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M5 3h11l5 5v11a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M7 3v5h8V3M7 14h10v7H7z" />
</svg>
</button>
</div>
<!-- Editor -->
<div id="editor-container"></div>
<!-- Resizer -->
<div class="resizer" id="resizer"></div>
<!-- Terminal -->
<div id="terminal-container" class="bg-base-300 border-t border-base-100">
<div class="flex items-center justify-between px-2 py-1 border-b border-base-100">
<!-- Terminal Tabs -->
<div class="flex items-center gap-1 overflow-x-auto" id="terminal-tabs">
<!-- Tabs populated dynamically -->
</div>
<div class="flex gap-1">
<button class="btn btn-ghost btn-xs" onclick="createNewTerminal()" title="New Terminal">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
</button>
<button class="btn btn-ghost btn-xs" onclick="clearTerminal()" title="Clear">Clear</button>
<button class="btn btn-ghost btn-xs" onclick="maximizeTerminal()" title="Maximize">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" />
</svg>
</button>
<button class="btn btn-ghost btn-xs" onclick="closeTerminalPanel()" title="Close (Ctrl+`)">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</button>
</div>
</div>
<div id="terminals-wrapper" style="height: calc(100% - 28px); position: relative;">
<!-- Terminal instances populated dynamically -->
</div>
</div>
</div>
</div>
<!-- Context Menu -->
<div id="context-menu" class="context-menu hidden">
<ul class="menu bg-base-100 rounded-box shadow-lg w-48 p-2">
<li><a onclick="renameContextFile()">Rename</a></li>
<li><a onclick="deleteContextFile()" class="text-error">Delete</a></li>
</ul>
</div>
<!-- Search Panel -->
<div id="search-panel" class="hidden fixed top-14 right-4 w-96 bg-base-300 rounded-lg shadow-xl border border-base-100 z-50">
<div class="p-3 border-b border-base-100 flex items-center gap-2">
<input type="text" id="search-input" placeholder="Search in files..."
class="input input-sm input-bordered flex-1"
onkeydown="if(event.key==='Enter') performSearch()">
<label class="flex items-center gap-1 text-xs cursor-pointer">
<input type="checkbox" id="regex-toggle" class="checkbox checkbox-xs">
<span>Regex</span>
</label>
<button class="btn btn-ghost btn-xs" onclick="closeSearchPanel()">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div id="search-results" class="max-h-80 overflow-y-auto">
<div class="p-3 text-sm text-base-content/50">Enter a search term and press Enter</div>
</div>
</div>
<!-- New File Modal -->
<dialog id="new-file-modal" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">New File</h3>
<div class="py-4">
<input type="text" id="new-file-path" placeholder="filename.go or folder/filename.go"
class="input input-bordered w-full" onkeydown="if(event.key==='Enter') createNewFile()">
</div>
<div class="modal-action">
<button class="btn btn-ghost" onclick="document.getElementById('new-file-modal').close()">Cancel</button>
<button class="btn btn-primary" onclick="createNewFile()">Create</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- Rename Modal -->
<dialog id="rename-modal" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Rename File</h3>
<div class="py-4">
<input type="text" id="rename-path" placeholder="new-filename.go"
class="input input-bordered w-full" onkeydown="if(event.key==='Enter') confirmRename()">
</div>
<div class="modal-action">
<button class="btn btn-ghost" onclick="document.getElementById('rename-modal').close()">Cancel</button>
<button class="btn btn-primary" onclick="confirmRename()">Rename</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- New Project Modal -->
<dialog id="new-project-modal" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">New Project</h3>
<div class="py-4 space-y-4">
<div class="form-control">
<label class="label"><span class="label-text">Project Name</span></label>
<input type="text" id="new-project-name" placeholder="my-project"
class="input input-bordered w-full" onkeydown="if(event.key==='Enter') createProject()">
</div>
<div class="form-control">
<label class="label"><span class="label-text">Clone from URL (optional)</span></label>
<input type="text" id="new-project-url" placeholder="https://github.com/user/repo.git"
class="input input-bordered w-full">
<p class="text-xs text-base-content/50 mt-1">Leave empty to create an empty project</p>
</div>
</div>
<div class="modal-action">
<button class="btn btn-ghost" onclick="document.getElementById('new-project-modal').close()">Cancel</button>
<button class="btn btn-primary" onclick="createProject()">Create</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- New Branch Modal -->
<dialog id="new-branch-modal" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Create Branch</h3>
<div class="py-4">
<input type="text" id="new-branch-name" placeholder="feature/my-feature"
class="input input-bordered w-full" onkeydown="if(event.key==='Enter') createBranch()">
<p class="text-xs text-base-content/50 mt-2">Enter the name for the new branch</p>
</div>
<div class="modal-action">
<button class="btn btn-ghost" onclick="document.getElementById('new-branch-modal').close()">Cancel</button>
<button class="btn btn-primary" onclick="createBranch()">Create</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- Clone Repository Modal -->
<dialog id="clone-modal" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Clone Repository</h3>
<div class="py-4">
<input type="text" id="clone-url" placeholder="https://github.com/user/repo.git"
class="input input-bordered w-full" onkeydown="if(event.key==='Enter') cloneRepository()">
<p class="text-xs text-base-content/50 mt-2">Enter the URL of the repository to clone</p>
</div>
<div class="modal-action">
<button class="btn btn-ghost" onclick="document.getElementById('clone-modal').close()">Cancel</button>
<button class="btn btn-primary" onclick="cloneRepository()">Clone</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- Git Credentials Modal -->
<dialog id="git-credentials-modal" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg">Authentication Required</h3>
<p class="text-sm text-base-content/70 mt-2">Enter your credentials to push to this repository. For GitHub, use a Personal Access Token as the password.</p>
<div class="py-4 space-y-3">
<input type="text" id="git-username" placeholder="Username"
class="input input-bordered w-full" autocomplete="username">
<input type="password" id="git-password" placeholder="Password or Token"
class="input input-bordered w-full" autocomplete="current-password"
onkeydown="if(event.key==='Enter') gitPushWithCredentials()">
<p class="text-xs text-base-content/50">Credentials are used once and not saved.</p>
</div>
<div class="modal-action">
<button class="btn btn-ghost" onclick="document.getElementById('git-credentials-modal').close()">Cancel</button>
<button class="btn btn-primary" onclick="gitPushWithCredentials()">Push</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- Settings Modal -->
<dialog id="settings-modal" class="modal">
<div class="modal-box">
<h3 class="font-bold text-lg mb-4">Settings</h3>
<div class="space-y-4">
<!-- Theme -->
<div class="form-control">
<label class="label">
<span class="label-text">Theme</span>
</label>
<select id="setting-theme" class="select select-bordered w-full">
<option value="dark">Dark</option>
<option value="light">Light</option>
<option value="dracula">Dracula</option>
<option value="nord">Nord</option>
<option value="sunset">Sunset</option>
</select>
</div>
<!-- Font Size -->
<div class="form-control">
<label class="label">
<span class="label-text">Font Size</span>
<span class="label-text-alt" id="font-size-value">14px</span>
</label>
<input type="range" id="setting-font-size" min="10" max="24" value="14"
class="range range-primary" oninput="updateFontSizeLabel()">
</div>
<!-- Tab Size -->
<div class="form-control">
<label class="label">
<span class="label-text">Tab Size</span>
</label>
<select id="setting-tab-size" class="select select-bordered w-full">
<option value="2">2 spaces</option>
<option value="4">4 spaces</option>
<option value="8">8 spaces</option>
</select>
</div>
<!-- Word Wrap -->
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Word Wrap</span>
<input type="checkbox" id="setting-word-wrap" class="toggle toggle-primary" checked>
</label>
</div>
<!-- Minimap -->
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Minimap</span>
<input type="checkbox" id="setting-minimap" class="toggle toggle-primary">
</label>
</div>
<!-- Persist Workspace -->
<div class="form-control">
<label class="label cursor-pointer">
<span class="label-text">Persist Workspace</span>
<input type="checkbox" id="setting-persist-workspace" class="toggle toggle-primary" checked>
</label>
<label class="label">
<span class="label-text-alt text-base-content/50">Save files to database (disable for ephemeral sessions)</span>
</label>
</div>
</div>
<div class="modal-action">
<button class="btn btn-ghost" onclick="document.getElementById('settings-modal').close()">Cancel</button>
<button class="btn btn-primary" onclick="saveSettings()">Save</button>
</div>
</div>
<form method="dialog" class="modal-backdrop"><button>close</button></form>
</dialog>
<!-- Editor Scripts -->
<script src="/public/scripts/main.js"></script>
<script src="/public/scripts/terminal.js"></script>
<script src="/public/scripts/files.js"></script>
<script src="/public/scripts/git.js"></script>
<script src="/public/scripts/search.js"></script>
<!-- Legacy inline script for any remaining code -->
<script>
</script>
</body>
</html>