at main 17 kB view raw
1<!doctype html> 2<html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <title>KnotView</title> 7 <link rel="icon" type="image/svg+xml" href="favicon.svg" /> 8 <link rel="stylesheet" href="styles.css" /> 9 <link 10 rel="stylesheet" 11 href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css" 12 /> 13 <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script> 14 <script src="https://cdn.jsdelivr.net/npm/marked@11.1.1/marked.min.js"></script> 15 <script 16 defer 17 src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" 18 ></script> 19 <script src="api.js"></script> 20 <script src="app.js"></script> 21 </head> 22 <body> 23 <div class="container" x-data="app" x-init="init()"> 24 <header> 25 <div class="header-top"> 26 <h1>KnotView</h1> 27 <button class="theme-toggle" @click="toggleTheme"> 28 <span x-show="!darkTheme">🌙</span> 29 <span x-show="darkTheme">☀️</span> 30 <span x-text="darkTheme ? 'Light' : 'Dark'"></span> 31 </button> 32 </div> 33 <div class="connection-panel"> 34 <input 35 type="text" 36 x-model="serverUrl" 37 placeholder="https://knot.example.com" 38 @keyup.enter="connectToServer" 39 /> 40 <button 41 @click="connectToServer" 42 :disabled="isConnected" 43 x-text="isConnected ? 'Connected ✓' : 'Connect'" 44 ></button> 45 </div> 46 <div 47 x-show="status.message" 48 class="status" 49 :class="status.type" 50 x-text="status.message" 51 ></div> 52 </header> 53 54 <div class="main-content"> 55 <!-- Sidebar --> 56 <aside class="sidebar" x-show="state.currentRepo"> 57 <div> 58 <h2> 59 Repository 60 <button 61 @click="showUsersList" 62 class="secondary" 63 style="padding: 6px 12px; font-size: 12px" 64 > 65 ← Back 66 </button> 67 </h2> 68 <div class="repo-info"> 69 <template x-if="state.currentRepo"> 70 <div> 71 <div class="label">Repository</div> 72 <div 73 class="value" 74 x-text="state.currentRepo?.name" 75 ></div> 76 77 <div class="label">Owner</div> 78 <div 79 class="value" 80 x-text="state.resolvedHandle || state.currentRepo?.did" 81 ></div> 82 83 <div class="label">Clone URL</div> 84 <div class="clone-url"> 85 <code 86 x-text="`${API.getBaseUrl()}/repo/${state.currentRepo?.fullPath}`" 87 ></code> 88 <button 89 class="copy-btn" 90 @click="copyToClipboard(`${API.getBaseUrl()}/repo/${state.currentRepo?.fullPath}`)" 91 > 92 Copy 93 </button> 94 </div> 95 96 <button 97 @click="window.location.href = `${API.getBaseUrl()}/xrpc/sh.tangled.repo.archive?repo=${encodeURIComponent(state.currentRepo?.fullPath)}&branch=${encodeURIComponent(state.currentBranch)}`" 98 style="width: 100%; margin-top: 8px" 99 > 100 Download Archive 101 </button> 102 </div> 103 </template> 104 </div> 105 </div> 106 107 <div class="branches-section"> 108 <h2>Branches</h2> 109 <div class="branch-list"> 110 <template x-for="branch in branches" :key="branch"> 111 <div 112 class="branch-item" 113 :class="{ active: branch.reference.name === state.currentBranch }" 114 @click="switchBranch(branch.reference.name)" 115 > 116 <span x-text="branch.reference.name"></span> 117 </div> 118 </template> 119 </div> 120 </div> 121 </aside> 122 123 <!-- Main Viewer --> 124 <main class="viewer"> 125 <div x-show="loading" class="loading"> 126 <div class="spinner"></div> 127 <p x-text="loadingMessage"></p> 128 </div> 129 130 <div 131 x-show="error && !loading" 132 class="error-message" 133 x-html="error" 134 ></div> 135 136 <!-- Empty State --> 137 <div 138 x-show="!loading && !error && !state.currentRepo && view === 'empty'" 139 class="welcome-hero" 140 > 141 <svg 142 xmlns="http://www.w3.org/2000/svg" 143 fill="none" 144 viewBox="0 0 24 24" 145 stroke="currentColor" 146 > 147 <path 148 stroke-linecap="round" 149 stroke-linejoin="round" 150 stroke-width="2" 151 d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" 152 /> 153 </svg> 154 <h2>Welcome to KnotView</h2> 155 <p class="subtitle"> 156 A web-based repository browser for Tangled Knots. 157 Browse, explore, and download content from Knot 158 servers. 159 </p> 160 161 <div class="feature-list"> 162 <div class="feature-item"> 163 <h4>🗂️ Browse Repositories</h4> 164 <p> 165 Navigate through files and folders with an 166 intuitive interface 167 </p> 168 </div> 169 <div class="feature-item"> 170 <h4>🌿 Branch Support</h4> 171 <p> 172 Switch between different branches seamlessly 173 </p> 174 </div> 175 <div class="feature-item"> 176 <h4>📄 File Viewer</h4> 177 <p> 178 View files with syntax highlighting and 179 markdown rendering 180 </p> 181 </div> 182 <div class="feature-item"> 183 <h4>📦 Download Archives</h4> 184 <p>Download entire repositories as archives</p> 185 </div> 186 </div> 187 188 <div class="getting-started"> 189 <h3>Getting Started</h3> 190 <ol> 191 <li> 192 Enter your Knot server URL in the input 193 above 194 </li> 195 <li> 196 Click "Connect" to connect to the server 197 </li> 198 <li>Select a repository from the list</li> 199 <li> 200 Browse files, switch branches, and explore 201 even if Tangled AppView is down! 202 </li> 203 </ol> 204 </div> 205 </div> 206 207 <!-- Users/Repos List --> 208 <div 209 x-show="!loading && !error && view === 'repoList'" 210 style="padding: 20px" 211 > 212 <template x-for="user in users" :key="user.did"> 213 <div class="user-item"> 214 <div 215 class="user-header" 216 x-text="user.handle || user.did" 217 ></div> 218 <template 219 x-for="repo in user.repos" 220 :key="repo.fullPath" 221 > 222 <div 223 class="repo-item" 224 @click="selectRepository(repo)" 225 > 226 <strong x-text="repo.name"></strong> 227 <small x-text="repo.fullPath"></small> 228 </div> 229 </template> 230 </div> 231 </template> 232 233 <!-- Manual Entry Fallback --> 234 <div 235 x-show="users.length === 0 && !loading" 236 class="empty-state" 237 > 238 <svg 239 xmlns="http://www.w3.org/2000/svg" 240 fill="none" 241 viewBox="0 0 24 24" 242 stroke="currentColor" 243 > 244 <path 245 stroke-linecap="round" 246 stroke-linejoin="round" 247 stroke-width="2" 248 d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" 249 /> 250 </svg> 251 <h3>Repository List Not Available</h3> 252 <p> 253 This server doesn't support automatic repository 254 listing. 255 </p> 256 <p style="margin-top: 20px"> 257 Please enter repository path manually: 258 </p> 259 <div 260 style=" 261 margin-top: 20px; 262 max-width: 400px; 263 margin-left: auto; 264 margin-right: auto; 265 " 266 > 267 <input 268 type="text" 269 x-model="manualRepoPath" 270 placeholder="did:plc:xxx.../repo-name" 271 style=" 272 width: 100%; 273 margin-bottom: 10px; 274 padding: 10px; 275 border: 1px solid #cbd5e1; 276 border-radius: 6px; 277 " 278 @keyup.enter="loadManualRepo" 279 /> 280 <button 281 @click="loadManualRepo" 282 style="width: 100%" 283 > 284 Load Repository 285 </button> 286 </div> 287 </div> 288 </div> 289 290 <!-- File Browser --> 291 <div x-show="!loading && !error && view === 'tree'"> 292 <div class="breadcrumb" x-html="breadcrumbHtml"></div> 293 <div class="file-list" x-html="fileListHtml"></div> 294 <div 295 x-show="readmeHtml" 296 style=" 297 margin-top: 20px; 298 border: 1px solid #e2e8f0; 299 border-radius: 6px; 300 overflow: hidden; 301 " 302 > 303 <div 304 style=" 305 padding: 12px 20px; 306 background: #f8fafc; 307 border-bottom: 1px solid #e2e8f0; 308 font-weight: 600; 309 " 310 > 311 📖 README.md 312 </div> 313 <div 314 class="markdown-content" 315 x-html="readmeHtml" 316 ></div> 317 </div> 318 </div> 319 320 <!-- File Viewer --> 321 <div x-show="!loading && !error && view === 'file'"> 322 <div class="breadcrumb" x-html="breadcrumbHtml"></div> 323 <div class="file-header"> 324 <h3 x-text="currentFile.name"></h3> 325 <div class="file-actions"> 326 <button @click="downloadFile">Download</button> 327 </div> 328 </div> 329 <div 330 x-show="currentFile.isBinary" 331 style=" 332 padding: 40px; 333 text-align: center; 334 color: #64748b; 335 " 336 > 337 <p>Binary file (cannot be displayed)</p> 338 <button 339 @click="downloadFile" 340 style="margin-top: 16px" 341 > 342 Download File 343 </button> 344 </div> 345 <div 346 x-show="currentFile.isMarkdown && !currentFile.isBinary" 347 class="markdown-content" 348 x-html="currentFile.content" 349 ></div> 350 <div 351 x-show="!currentFile.isMarkdown && !currentFile.isBinary" 352 class="file-content" 353 x-html="currentFile.content" 354 ></div> 355 </div> 356 </main> 357 </div> 358 <footer> 359 <p> 360 <a 361 href="https://tangled.org/julien.rbrt.fr/knotview" 362 target="_blank" 363 rel="noopener noreferrer" 364 > 365 Fork me on Tangled. 366 </a> 367 </p> 368 </footer> 369 </div> 370 </body> 371</html>