Connor McCutcheon
/ SkyCode
editor.html
html
<!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>
No comments yet.