index.html
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>🔥 KAMAJI - Specification Archive</title> 7 <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> 8 <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script> 9 <style> 10 * { 11 margin: 0; 12 padding: 0; 13 box-sizing: border-box; 14 } 15 16 :root { 17 /* 2003 Fire Theme - More refined */ 18 --fire-red: #ff3300; 19 --fire-orange: #ff6600; 20 --fire-yellow: #ffcc00; 21 --fire-bright: #ff9933; 22 --digital-black: #000000; 23 --digital-dark: #111111; 24 --digital-gray: #1a1a1a; 25 --chrome-silver: #cccccc; 26 --warm-gray: #666666; 27 28 /* Text */ 29 --text-light: #ffffff; 30 --text-gray: #aaaaaa; 31 --text-dim: #777777; 32 33 --sidebar-width: 280px; 34 } 35 36 @keyframes subtle-glow { 37 0%, 100% { text-shadow: 0 0 8px rgba(255, 102, 0, 0.4); } 38 50% { text-shadow: 0 0 12px rgba(255, 102, 0, 0.6); } 39 } 40 41 @keyframes slide-in { 42 from { transform: translateX(-20px); opacity: 0; } 43 to { transform: translateX(0); opacity: 1; } 44 } 45 46 @keyframes fade-in { 47 from { opacity: 0; } 48 to { opacity: 1; } 49 } 50 51 @keyframes pulse-soft { 52 0%, 100% { transform: scale(1); } 53 50% { transform: scale(1.05); } 54 } 55 56 body { 57 font-family: Verdana, Tahoma, 'Trebuchet MS', sans-serif; 58 background: linear-gradient(135deg, #000000 0%, #0a0a0a 50%, #111111 100%); 59 color: var(--text-light); 60 line-height: 1.6; 61 overflow-x: hidden; 62 } 63 64 /* Subtle ambient background */ 65 body::before { 66 content: ''; 67 position: fixed; 68 top: 0; 69 left: 0; 70 right: 0; 71 bottom: 0; 72 background: 73 radial-gradient(ellipse at 20% 30%, rgba(255, 102, 0, 0.08) 0%, transparent 50%), 74 radial-gradient(ellipse at 80% 70%, rgba(255, 51, 0, 0.05) 0%, transparent 50%); 75 pointer-events: none; 76 z-index: 0; 77 } 78 79 /* Sidebar - 2003 polished style */ 80 nav#sidebar { 81 position: fixed; 82 top: 0; 83 left: 0; 84 width: var(--sidebar-width); 85 height: 100vh; 86 background: linear-gradient(180deg, #0d0d0d 0%, #080808 100%); 87 border-right: 2px solid var(--fire-orange); 88 box-shadow: 2px 0 15px rgba(255, 102, 0, 0.3); 89 overflow-y: auto; 90 z-index: 1001; 91 padding-bottom: 240px; 92 } 93 94 nav::-webkit-scrollbar { 95 width: 10px; 96 } 97 98 nav::-webkit-scrollbar-track { 99 background: #000; 100 } 101 102 nav::-webkit-scrollbar-thumb { 103 background: linear-gradient(180deg, var(--fire-orange), var(--fire-red)); 104 border-radius: 5px; 105 } 106 107 nav::-webkit-scrollbar-thumb:hover { 108 background: var(--fire-bright); 109 } 110 111 /* Sidebar Header */ 112 .sidebar-header { 113 padding: 1.5rem 1rem; 114 text-align: center; 115 background: linear-gradient(180deg, rgba(255, 102, 0, 0.15) 0%, transparent 100%); 116 border-bottom: 2px solid var(--fire-orange); 117 margin-bottom: 1.5rem; 118 } 119 120 #kamaji-illustration { 121 width: 100%; 122 height: auto; 123 margin-bottom: 1rem; 124 cursor: pointer; 125 filter: drop-shadow(0 4px 12px rgba(255, 102, 0, 0.3)); 126 transition: all 0.4s ease; 127 } 128 129 #kamaji-illustration:hover { 130 filter: drop-shadow(0 8px 20px rgba(255, 102, 0, 0.5)); 131 } 132 133 .sidebar-logo { 134 font-size: 1.35rem; 135 font-weight: bold; 136 color: var(--fire-orange); 137 margin-bottom: 0.5rem; 138 animation: subtle-glow 3s ease-in-out infinite; 139 } 140 141 .sidebar-version { 142 font-size: 0.75rem; 143 color: var(--text-gray); 144 text-transform: uppercase; 145 letter-spacing: 0.15em; 146 font-family: 'Courier New', monospace; 147 } 148 149 .nav-section { 150 margin-bottom: 1.5rem; 151 animation: slide-in 0.5s ease-out; 152 } 153 154 .nav-section-title { 155 padding: 0.875rem 1.5rem; 156 font-size: 0.7rem; 157 text-transform: uppercase; 158 letter-spacing: 0.2em; 159 color: var(--fire-bright); 160 background: linear-gradient(90deg, rgba(255, 102, 0, 0.15) 0%, transparent 100%); 161 border-left: 3px solid var(--fire-orange); 162 font-weight: bold; 163 margin-bottom: 0.5rem; 164 } 165 166 .nav-item { 167 padding: 0.75rem 1.5rem; 168 color: var(--text-gray); 169 display: flex; 170 align-items: center; 171 gap: 0.75rem; 172 cursor: pointer; 173 transition: all 0.3s ease; 174 border-left: 3px solid transparent; 175 font-size: 0.85rem; 176 border-radius: 0 4px 4px 0; 177 margin: 2px 0; 178 } 179 180 .nav-item:hover { 181 background: linear-gradient(90deg, rgba(255, 102, 0, 0.2) 0%, rgba(255, 102, 0, 0.05) 100%); 182 color: var(--text-light); 183 border-left-color: var(--fire-orange); 184 transform: translateX(3px); 185 } 186 187 .nav-item.active { 188 background: linear-gradient(90deg, rgba(255, 102, 0, 0.25) 0%, rgba(255, 102, 0, 0.1) 100%); 189 color: var(--fire-orange); 190 border-left-color: var(--fire-bright); 191 font-weight: bold; 192 box-shadow: inset 0 0 10px rgba(255, 102, 0, 0.2); 193 } 194 195 .nav-item-icon { 196 font-size: 1.125rem; 197 min-width: 1.5rem; 198 filter: drop-shadow(0 0 2px rgba(255, 102, 0, 0.3)); 199 } 200 201 /* Stats Panel - Refined 2003 style */ 202 .stats-panel { 203 position: fixed; 204 bottom: 0; 205 left: 0; 206 width: var(--sidebar-width); 207 background: linear-gradient(180deg, #0a0a0a 0%, #000000 100%); 208 border-right: 2px solid var(--fire-orange); 209 border-top: 2px solid var(--fire-orange); 210 padding: 1.5rem; 211 z-index: 1002; 212 box-shadow: 0 -4px 15px rgba(255, 102, 0, 0.2); 213 } 214 215 .stats-title { 216 color: var(--fire-orange); 217 font-size: 0.75rem; 218 font-weight: bold; 219 margin-bottom: 1rem; 220 text-transform: uppercase; 221 letter-spacing: 0.2em; 222 text-align: center; 223 border-bottom: 1px solid rgba(255, 102, 0, 0.3); 224 padding-bottom: 0.75rem; 225 } 226 227 .stat-row { 228 display: flex; 229 justify-content: space-between; 230 margin-bottom: 0.75rem; 231 font-size: 0.8rem; 232 padding: 0.625rem; 233 background: rgba(255, 102, 0, 0.05); 234 border-radius: 4px; 235 border-left: 2px solid var(--fire-orange); 236 transition: all 0.3s ease; 237 } 238 239 .stat-row:hover { 240 background: rgba(255, 102, 0, 0.1); 241 transform: translateX(2px); 242 } 243 244 .stat-label { 245 color: var(--text-gray); 246 font-family: Verdana, sans-serif; 247 font-size: 0.75rem; 248 } 249 250 .stat-value { 251 color: var(--fire-orange); 252 font-weight: bold; 253 font-family: 'Courier New', monospace; 254 } 255 256 /* Header - Polished 2003 */ 257 header { 258 position: fixed; 259 top: 0; 260 left: var(--sidebar-width); 261 right: 0; 262 background: linear-gradient(180deg, #1a1a1a 0%, #0d0d0d 100%); 263 border-bottom: 2px solid var(--fire-orange); 264 z-index: 1000; 265 box-shadow: 0 2px 20px rgba(255, 102, 0, 0.25); 266 } 267 268 .header-main { 269 padding: 1.25rem 2.5rem; 270 display: flex; 271 align-items: center; 272 justify-content: space-between; 273 background: linear-gradient(90deg, rgba(255, 102, 0, 0.08) 0%, transparent 50%); 274 } 275 276 .logo { 277 display: flex; 278 align-items: center; 279 gap: 1.25rem; 280 } 281 282 .logo-icon { 283 font-size: 2.25rem; 284 animation: pulse-soft 4s ease-in-out infinite; 285 filter: drop-shadow(0 0 8px rgba(255, 102, 0, 0.4)); 286 } 287 288 .logo-text { 289 font-size: 1.75rem; 290 font-weight: bold; 291 color: var(--fire-orange); 292 text-transform: uppercase; 293 letter-spacing: 0.3em; 294 text-shadow: 0 0 10px rgba(255, 102, 0, 0.5); 295 } 296 297 .header-nav { 298 display: flex; 299 gap: 2rem; 300 align-items: center; 301 } 302 303 .status-badge { 304 display: inline-block; 305 padding: 0.375rem 1rem; 306 background: linear-gradient(135deg, rgba(255, 102, 0, 0.2), rgba(255, 51, 0, 0.15)); 307 border: 1px solid var(--fire-orange); 308 border-radius: 12px; 309 color: var(--fire-orange); 310 font-size: 0.7rem; 311 text-transform: uppercase; 312 letter-spacing: 0.15em; 313 font-family: Verdana, sans-serif; 314 box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.1); 315 } 316 317 /* Search - 2003 refined input */ 318 .search-box { 319 position: relative; 320 } 321 322 #search-input { 323 padding: 0.625rem 1.25rem; 324 background: #000; 325 border: 1px solid var(--fire-orange); 326 border-radius: 6px; 327 color: var(--text-light); 328 font-family: Verdana, sans-serif; 329 font-size: 0.875rem; 330 width: 280px; 331 transition: all 0.3s ease; 332 box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3); 333 } 334 335 #search-input:focus { 336 outline: none; 337 box-shadow: 0 0 15px rgba(255, 102, 0, 0.4), inset 0 2px 4px rgba(0, 0, 0, 0.3); 338 border-color: var(--fire-bright); 339 background: #0a0a0a; 340 } 341 342 #search-input::placeholder { 343 color: var(--text-dim); 344 } 345 346 /* Main content */ 347 main { 348 margin-left: var(--sidebar-width); 349 margin-top: 90px; 350 padding: 3rem 4rem; 351 animation: fade-in 0.5s ease-out; 352 } 353 354 /* Typography - Refined 2003 */ 355 h1 { 356 font-size: 2.5rem; 357 margin-bottom: 1.5rem; 358 color: var(--fire-orange); 359 text-transform: uppercase; 360 letter-spacing: 0.05em; 361 border-bottom: 2px solid var(--fire-orange); 362 padding-bottom: 1rem; 363 font-weight: 700; 364 text-shadow: 0 2px 4px rgba(255, 102, 0, 0.3); 365 } 366 367 h1::before { 368 content: '▸ '; 369 color: var(--fire-bright); 370 } 371 372 h2 { 373 font-size: 1.85rem; 374 margin-top: 3rem; 375 margin-bottom: 1.25rem; 376 color: var(--fire-bright); 377 text-transform: uppercase; 378 letter-spacing: 0.05em; 379 border-left: 4px solid var(--fire-orange); 380 padding-left: 1.25rem; 381 text-shadow: 0 1px 3px rgba(255, 102, 0, 0.2); 382 } 383 384 h3 { 385 font-size: 1.35rem; 386 margin-top: 2rem; 387 margin-bottom: 1rem; 388 color: var(--text-light); 389 font-weight: 600; 390 } 391 392 h4 { 393 font-size: 1.05rem; 394 margin-top: 1.5rem; 395 margin-bottom: 0.75rem; 396 color: var(--text-gray); 397 text-transform: uppercase; 398 letter-spacing: 0.1em; 399 } 400 401 p { 402 margin-bottom: 1.25rem; 403 color: var(--text-light); 404 line-height: 1.7; 405 } 406 407 /* Code blocks - Polished terminal */ 408 pre { 409 background: #000; 410 border: 1px solid var(--fire-orange); 411 border-radius: 6px; 412 padding: 1.5rem; 413 overflow-x: auto; 414 margin: 1.5rem 0; 415 font-family: 'Courier New', monospace; 416 font-size: 0.875rem; 417 line-height: 1.6; 418 box-shadow: 0 4px 20px rgba(255, 102, 0, 0.15), inset 0 2px 4px rgba(0, 0, 0, 0.3); 419 } 420 421 code { 422 font-family: 'Courier New', monospace; 423 font-size: 0.875rem; 424 background: rgba(255, 102, 0, 0.1); 425 padding: 0.25rem 0.5rem; 426 border-radius: 3px; 427 color: var(--fire-bright); 428 border: 1px solid rgba(255, 102, 0, 0.3); 429 } 430 431 pre code { 432 background: none; 433 padding: 0; 434 color: var(--text-light); 435 border: none; 436 } 437 438 /* Tables - Refined data grid */ 439 table { 440 width: 100%; 441 border-collapse: collapse; 442 margin: 2rem 0; 443 background: #000; 444 border: 1px solid var(--fire-orange); 445 border-radius: 6px; 446 overflow: hidden; 447 box-shadow: 0 4px 15px rgba(255, 102, 0, 0.15); 448 } 449 450 th { 451 background: linear-gradient(135deg, rgba(255, 102, 0, 0.25), rgba(255, 102, 0, 0.15)); 452 padding: 1rem; 453 text-align: left; 454 font-weight: bold; 455 color: var(--fire-orange); 456 border-bottom: 2px solid var(--fire-orange); 457 text-transform: uppercase; 458 font-size: 0.8rem; 459 letter-spacing: 0.15em; 460 } 461 462 td { 463 padding: 0.875rem 1rem; 464 border-bottom: 1px solid rgba(255, 102, 0, 0.15); 465 color: var(--text-light); 466 } 467 468 tr:last-child td { 469 border-bottom: none; 470 } 471 472 tr:hover { 473 background: rgba(255, 102, 0, 0.08); 474 } 475 476 /* Links - 2003 style */ 477 a { 478 color: var(--fire-orange); 479 text-decoration: none; 480 border-bottom: 1px dotted var(--fire-orange); 481 transition: all 0.2s ease; 482 } 483 484 a:hover { 485 color: var(--fire-bright); 486 border-bottom-style: solid; 487 text-shadow: 0 0 8px rgba(255, 102, 0, 0.4); 488 } 489 490 /* Lists */ 491 ul, ol { 492 margin: 1.25rem 0; 493 padding-left: 2rem; 494 } 495 496 li { 497 margin: 0.75rem 0; 498 color: var(--text-light); 499 } 500 501 li::marker { 502 color: var(--fire-orange); 503 } 504 505 /* Blockquotes */ 506 blockquote { 507 border-left: 4px solid var(--fire-orange); 508 padding: 1.25rem 1.5rem; 509 margin: 2rem 0; 510 background: linear-gradient(90deg, rgba(255, 102, 0, 0.08) 0%, transparent 100%); 511 color: var(--text-gray); 512 font-style: italic; 513 border-radius: 0 6px 6px 0; 514 } 515 516 /* Horizontal rules */ 517 hr { 518 border: none; 519 height: 2px; 520 background: linear-gradient(90deg, transparent 0%, var(--fire-orange) 50%, transparent 100%); 521 margin: 3rem 0; 522 } 523 524 /* Loading state - Refined */ 525 .loading { 526 text-align: center; 527 padding: 5rem 0; 528 color: var(--fire-orange); 529 font-size: 1.25rem; 530 text-transform: uppercase; 531 letter-spacing: 0.2em; 532 } 533 534 .loading::before { 535 content: ''; 536 display: block; 537 width: 240px; 538 height: 6px; 539 background: rgba(255, 102, 0, 0.15); 540 border-radius: 3px; 541 margin: 0 auto 2rem; 542 position: relative; 543 overflow: hidden; 544 box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.3); 545 } 546 547 /* Responsive */ 548 @media (max-width: 1024px) { 549 nav#sidebar, .stats-panel { 550 transform: translateX(-100%); 551 transition: transform 0.3s ease; 552 } 553 554 nav#sidebar.open, .stats-panel.open { 555 transform: translateX(0); 556 } 557 558 header { 559 left: 0; 560 } 561 562 main { 563 margin-left: 0; 564 padding: 2rem; 565 } 566 567 #search-input { 568 width: 200px; 569 } 570 } 571 572 @media (max-width: 640px) { 573 .header-main { 574 flex-direction: column; 575 gap: 1rem; 576 } 577 578 .logo-text { 579 font-size: 1.25rem; 580 } 581 582 main { 583 padding: 1.5rem 1rem; 584 } 585 } 586 587 /* Print */ 588 @media print { 589 nav, header, .stats-panel { 590 display: none; 591 } 592 593 main { 594 margin-left: 0; 595 margin-top: 0; 596 } 597 598 body { 599 background: white; 600 color: black; 601 } 602 } 603 </style> 604 </head> 605 <body> 606 <nav id="sidebar"> 607 <div class="sidebar-header"> 608 <canvas id="kamaji-illustration" width="280" height="280"></canvas> 609 <div class="sidebar-logo">🔥 KAMAJI</div> 610 </div> 611 612 <div class="nav-section"> 613 <div class="nav-section-title">▸ OVERVIEW</div> 614 <div class="nav-item" data-file="README.md"> 615 <span class="nav-item-icon">📄</span> 616 <span class="nav-item-text">README</span> 617 </div> 618 <div class="nav-item" data-file="00_INDEX.md"> 619 <span class="nav-item-icon">📑</span> 620 <span class="nav-item-text">Index</span> 621 </div> 622 <div class="nav-item" data-file="ARCHITECTURE.md"> 623 <span class="nav-item-icon">🏗️</span> 624 <span class="nav-item-text">Architecture</span> 625 </div> 626 </div> 627 628 <div class="nav-section"> 629 <div class="nav-section-title">▸ SPECIFICATIONS</div> 630 <div class="nav-item" data-file="01_ui_components.md"> 631 <span class="nav-item-icon">🎨</span> 632 <span class="nav-item-text">UI Components</span> 633 </div> 634 <div class="nav-item" data-file="02_input_interactions.md"> 635 <span class="nav-item-icon">⌨️</span> 636 <span class="nav-item-text">Input System</span> 637 </div> 638 <div class="nav-item" data-file="03_consciousness_system.md"> 639 <span class="nav-item-icon">🧠</span> 640 <span class="nav-item-text">Consciousness</span> 641 </div> 642 <div class="nav-item" data-file="04_agent_system.md"> 643 <span class="nav-item-icon">🤖</span> 644 <span class="nav-item-text">Agent System</span> 645 </div> 646 <div class="nav-item" data-file="05_state_management.md"> 647 <span class="nav-item-icon">🔄</span> 648 <span class="nav-item-text">State Management</span> 649 </div> 650 <div class="nav-item" data-file="06_llm_integration.md"> 651 <span class="nav-item-icon">🔌</span> 652 <span class="nav-item-text">LLM Integration</span> 653 </div> 654 <div class="nav-item" data-file="07_commands_tools.md"> 655 <span class="nav-item-icon">🔧</span> 656 <span class="nav-item-text">Commands & Tools</span> 657 </div> 658 <div class="nav-item" data-file="08_visual_styling.md"> 659 <span class="nav-item-icon">🎭</span> 660 <span class="nav-item-text">Visual Styling</span> 661 </div> 662 </div> 663 664 <div class="nav-section"> 665 <div class="nav-section-title">▸ SYNTHESIS</div> 666 <div class="nav-item" data-file="USER_INTERACTIONS.md"> 667 <span class="nav-item-icon">👤</span> 668 <span class="nav-item-text">User Guide</span> 669 </div> 670 <div class="nav-item" data-file="DATA_FLOW.md"> 671 <span class="nav-item-icon">💧</span> 672 <span class="nav-item-text">Data Flow</span> 673 </div> 674 </div> 675 676 <div class="nav-section"> 677 <div class="nav-section-title">▸ VERIFICATION</div> 678 <div class="nav-item" data-file="VERIFICATION_SUMMARY.md"> 679 <span class="nav-item-icon">📊</span> 680 <span class="nav-item-text">Summary</span> 681 </div> 682 <div class="nav-item" data-file="VERIFICATION_01_UI_COMPONENTS.md"> 683 <span class="nav-item-icon">✓</span> 684 <span class="nav-item-text">UI Check</span> 685 </div> 686 <div class="nav-item" data-file="VERIFICATION_02_STATE.md"> 687 <span class="nav-item-icon">✓</span> 688 <span class="nav-item-text">State Check</span> 689 </div> 690 <div class="nav-item" data-file="VERIFICATION_03_INPUT.md"> 691 <span class="nav-item-icon">✓</span> 692 <span class="nav-item-text">Input Check</span> 693 </div> 694 <div class="nav-item" data-file="VERIFICATION_04_CONSCIOUSNESS.md"> 695 <span class="nav-item-icon">✓</span> 696 <span class="nav-item-text">Consciousness Check</span> 697 </div> 698 <div class="nav-item" data-file="VERIFICATION_05_AGENTS_TOOLS.md"> 699 <span class="nav-item-icon">✓</span> 700 <span class="nav-item-text">Agents Check</span> 701 </div> 702 <div class="nav-item" data-file="VERIFICATION_06_LLM.md"> 703 <span class="nav-item-icon">✓</span> 704 <span class="nav-item-text">LLM Check</span> 705 </div> 706 <div class="nav-item" data-file="VERIFICATION_07_VISUAL.md"> 707 <span class="nav-item-icon">✓</span> 708 <span class="nav-item-text">Visual Check</span> 709 </div> 710 </div> 711 </nav> 712 713 <div class="stats-panel"> 714 <div class="stats-title">SYSTEM STATUS</div> 715 <div class="stat-row"> 716 <span class="stat-label">DOCS LOADED</span> 717 <span class="stat-value" id="specs-loaded">0</span> 718 </div> 719 <div class="stat-row"> 720 <span class="stat-label">CACHE HITS</span> 721 <span class="stat-value" id="cache-hits">0</span> 722 </div> 723 <div class="stat-row"> 724 <span class="stat-label">CURRENT</span> 725 <span class="stat-value" id="current-view">README</span> 726 </div> 727 <div class="stat-row"> 728 <span class="stat-label">COMPLETE</span> 729 <span class="stat-value">99.8%</span> 730 </div> 731 </div> 732 733 <header> 734 <div class="header-main"> 735 <div class="logo"> 736 <div class="logo-icon">🔥</div> 737 <div class="logo-text">KAMAJI</div> 738 <span class="status-badge">SPECIFICATION v1.0.390</span> 739 </div> 740 <nav class="header-nav"> 741 <div class="search-box"> 742 <input type="text" id="search-input" placeholder="Search documentation..."> 743 </div> 744 </nav> 745 </div> 746 </header> 747 748 <main id="content"> 749 <div class="loading"> 750 <div>LOADING SPECIFICATION...</div> 751 </div> 752 </main> 753 754 <script> 755 const SPEC_PATH = './'; 756 const DEFAULT_FILE = 'README.md'; 757 758 let currentFile = DEFAULT_FILE; 759 let specCache = new Map(); 760 let specsLoaded = 0; 761 let cacheHits = 0; 762 let manifest = null; 763 764 document.addEventListener('DOMContentLoaded', async () => { 765 await loadManifest(); 766 initNavigation(); 767 initSearch(); 768 loadSpec(DEFAULT_FILE); 769 updateMetrics(); 770 }); 771 772 async function loadManifest() { 773 try { 774 const response = await fetch(SPEC_PATH + 'manifest.json'); 775 if (response.ok) { 776 manifest = await response.json(); 777 console.log('Manifest loaded:', manifest.files.length, 'files'); 778 } 779 } catch (error) { 780 console.warn('Could not load manifest:', error); 781 } 782 } 783 784 function isValidFile(filename) { 785 if (!manifest) return true; // Fallback if manifest not loaded 786 return manifest.files.includes(filename); 787 } 788 789 function initNavigation() { 790 document.querySelectorAll('.nav-item').forEach(item => { 791 item.addEventListener('click', () => { 792 const file = item.getAttribute('data-file'); 793 loadSpec(file); 794 795 document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active')); 796 item.classList.add('active'); 797 798 window.location.hash = file.replace('.md', ''); 799 }); 800 }); 801 802 window.addEventListener('hashchange', () => { 803 const file = (window.location.hash.slice(1) || 'README') + '.md'; 804 loadSpec(file); 805 }); 806 807 if (window.location.hash) { 808 const file = window.location.hash.slice(1) + '.md'; 809 const navItem = document.querySelector(`[data-file="${file}"]`); 810 if (navItem) navItem.click(); 811 } 812 } 813 814 function initSearch() { 815 const searchInput = document.getElementById('search-input'); 816 let searchTimeout; 817 818 searchInput.addEventListener('input', (e) => { 819 clearTimeout(searchTimeout); 820 searchTimeout = setTimeout(() => { 821 performSearch(e.target.value); 822 }, 300); 823 }); 824 825 document.addEventListener('keydown', (e) => { 826 if (e.key === '/' && !e.ctrlKey && !e.metaKey && e.target.tagName !== 'INPUT') { 827 e.preventDefault(); 828 searchInput.focus(); 829 } 830 if (e.key === 'Escape' && e.target === searchInput) { 831 searchInput.value = ''; 832 performSearch(''); 833 searchInput.blur(); 834 } 835 }); 836 } 837 838 function performSearch(query) { 839 if (!query.trim()) { 840 document.querySelectorAll('.nav-item').forEach(item => { 841 item.style.display = 'flex'; 842 }); 843 document.querySelectorAll('.nav-section').forEach(section => { 844 section.style.display = 'block'; 845 }); 846 return; 847 } 848 849 const lowerQuery = query.toLowerCase(); 850 851 document.querySelectorAll('.nav-section').forEach(section => { 852 let hasVisibleItems = false; 853 section.querySelectorAll('.nav-item').forEach(item => { 854 const text = item.textContent.toLowerCase(); 855 const visible = text.includes(lowerQuery); 856 item.style.display = visible ? 'flex' : 'none'; 857 if (visible) hasVisibleItems = true; 858 }); 859 section.style.display = hasVisibleItems ? 'block' : 'none'; 860 }); 861 } 862 863 async function loadSpec(filename) { 864 const content = document.getElementById('content'); 865 866 // Validate file before attempting to load 867 if (manifest && !isValidFile(filename)) { 868 console.warn(`Invalid file requested: ${filename}, redirecting to fallback`); 869 const fallback = manifest.fallback || manifest.default || 'README.md'; 870 window.location.hash = fallback.replace('.md', ''); 871 return; 872 } 873 874 currentFile = filename; 875 876 const displayName = filename 877 .replace('.md', '') 878 .replace(/_/g, ' ') 879 .replace(/VERIFICATION /g, 'VER ') 880 .replace(/^\d+_?/, '') 881 .trim() 882 .toUpperCase(); 883 document.getElementById('current-view').textContent = displayName; 884 885 // Check cache first 886 if (specCache.has(filename)) { 887 cacheHits++; 888 updateMetrics(); 889 renderMarkdown(specCache.get(filename)); 890 return; 891 } 892 893 content.innerHTML = '<div class="loading"><div>LOADING SPECIFICATION...</div></div>'; 894 895 try { 896 const response = await fetch(SPEC_PATH + filename); 897 898 if (!response.ok) { 899 throw new Error(`HTTP ${response.status}: ${response.statusText}`); 900 } 901 902 const markdown = await response.text(); 903 904 // Validate markdown is not empty 905 if (!markdown || markdown.trim().length === 0) { 906 throw new Error('File is empty'); 907 } 908 909 specCache.set(filename, markdown); 910 specsLoaded++; 911 updateMetrics(); 912 renderMarkdown(markdown); 913 914 } catch (error) { 915 console.error('Failed to load specification:', filename, error); 916 917 // Try fallback file if available 918 if (manifest && filename !== manifest.fallback && filename !== manifest.default) { 919 const fallback = manifest.fallback || manifest.default; 920 content.innerHTML = ` 921 <div style="padding: 3rem; text-align: center;"> 922 <h2 style="color: var(--fire-orange);">⚠️ DOCUMENT NOT FOUND</h2> 923 <p style="color: var(--text-gray); margin-top: 1.5rem; font-size: 1.1rem;"> 924 The requested document <strong>${filename}</strong> could not be loaded. 925 </p> 926 <p style="color: var(--text-light); margin-top: 1.5rem;"> 927 Redirecting to index... 928 </p> 929 </div> 930 `; 931 932 // Redirect after showing message 933 setTimeout(() => { 934 window.location.hash = fallback.replace('.md', ''); 935 }, 2000); 936 } else { 937 // Show error if we can't fallback 938 content.innerHTML = ` 939 <div style="padding: 3rem; text-align: center;"> 940 <h2 style="color: var(--fire-red);">⚠️ LOAD ERROR</h2> 941 <p style="color: var(--text-gray); margin-top: 1.5rem; font-size: 1.1rem;"> 942 Document: <strong>${filename}</strong> 943 </p> 944 <p style="color: var(--fire-orange); margin-top: 1rem;"> 945 ${error.message} 946 </p> 947 <p style="color: var(--text-dim); margin-top: 2rem; font-size: 0.9rem;"> 948 Please check that the file exists and the server is running. 949 </p> 950 </div> 951 `; 952 } 953 } 954 } 955 956 function renderMarkdown(markdown) { 957 const content = document.getElementById('content'); 958 959 marked.setOptions({ 960 breaks: true, 961 gfm: true 962 }); 963 964 const html = marked.parse(markdown); 965 content.innerHTML = html; 966 967 window.scrollTo({ top: 0, behavior: 'smooth' }); 968 969 content.querySelectorAll('h2, h3, h4').forEach(heading => { 970 const id = heading.textContent.toLowerCase() 971 .replace(/[^\w\s-]/g, '') 972 .replace(/\s+/g, '-'); 973 heading.id = id; 974 975 heading.style.cursor = 'pointer'; 976 heading.title = 'Click to copy link'; 977 heading.addEventListener('click', () => { 978 const url = `${window.location.origin}${window.location.pathname}#${currentFile.replace('.md', '')}/${id}`; 979 navigator.clipboard.writeText(url); 980 981 const originalText = heading.textContent; 982 heading.textContent = '✓ LINK COPIED'; 983 setTimeout(() => { 984 heading.textContent = originalText; 985 }, 1500); 986 }); 987 }); 988 } 989 990 function updateMetrics() { 991 document.getElementById('specs-loaded').textContent = specsLoaded; 992 document.getElementById('cache-hits').textContent = cacheHits; 993 } 994 995 // DRAW KAMAJI ON CANVAS 996 document.addEventListener('DOMContentLoaded', () => { 997 const canvas = document.getElementById('kamaji-illustration'); 998 const ctx = canvas.getContext('2d'); 999 1000 function drawKamaji() { 1001 ctx.clearRect(0, 0, canvas.width, canvas.height); 1002 1003 // DRAW BODY FIRST, THEN ARMS ON TOP 1004 1005 // OLIVE GREEN PANTS (cross-legged, low) 1006 ctx.fillStyle = '#6b7c4a'; 1007 ctx.beginPath(); 1008 ctx.ellipse(140, 230, 35, 25, 0, 0, Math.PI * 2); 1009 ctx.fill(); 1010 1011 // DARK BLUE SHIRT (very hunched, compressed) 1012 const shirtGrad = ctx.createRadialGradient(140, 195, 10, 140, 195, 35); 1013 shirtGrad.addColorStop(0, '#3a4f6b'); 1014 shirtGrad.addColorStop(1, '#1f2d3d'); 1015 ctx.fillStyle = shirtGrad; 1016 ctx.beginPath(); 1017 ctx.ellipse(140, 195, 38, 35, 0, 0, Math.PI * 2); 1018 ctx.fill(); 1019 1020 // LARGE SPHERICAL HEAD - peachy tan, positioned lower for hunch 1021 const skinGrad = ctx.createRadialGradient(140, 140, 15, 140, 140, 45); 1022 skinGrad.addColorStop(0, '#f5d5b8'); 1023 skinGrad.addColorStop(1, '#d4a574'); 1024 ctx.fillStyle = skinGrad; 1025 ctx.beginPath(); 1026 ctx.ellipse(140, 140, 42, 45, 0, 0, Math.PI * 2); 1027 ctx.fill(); 1028 1029 // Wispy white hair 1030 ctx.fillStyle = 'rgba(232, 232, 232, 0.75)'; 1031 ctx.beginPath(); 1032 ctx.ellipse(125, 100, 12, 8, -0.3, 0, Math.PI * 2); 1033 ctx.fill(); 1034 ctx.beginPath(); 1035 ctx.ellipse(155, 102, 11, 7, 0.3, 0, Math.PI * 2); 1036 ctx.fill(); 1037 1038 // HUGE ROUND BLACK GLASSES - dominating the face 1039 ctx.fillStyle = '#0a0a0a'; 1040 ctx.strokeStyle = '#2a2a2a'; 1041 ctx.lineWidth = 2.5; 1042 1043 ctx.beginPath(); 1044 ctx.arc(115, 135, 18, 0, Math.PI * 2); 1045 ctx.fill(); 1046 ctx.stroke(); 1047 1048 ctx.beginPath(); 1049 ctx.arc(165, 135, 18, 0, Math.PI * 2); 1050 ctx.fill(); 1051 ctx.stroke(); 1052 1053 // Thick glasses bridge 1054 ctx.lineWidth = 3; 1055 ctx.beginPath(); 1056 ctx.moveTo(133, 135); 1057 ctx.lineTo(147, 135); 1058 ctx.stroke(); 1059 1060 // Lens reflections 1061 ctx.fillStyle = 'rgba(255, 255, 255, 0.25)'; 1062 ctx.beginPath(); 1063 ctx.ellipse(110, 130, 3, 4, 0, 0, Math.PI * 2); 1064 ctx.fill(); 1065 ctx.beginPath(); 1066 ctx.ellipse(160, 130, 3, 4, 0, 0, Math.PI * 2); 1067 ctx.fill(); 1068 1069 // Small nose 1070 ctx.fillStyle = 'rgba(216, 196, 176, 0.5)'; 1071 ctx.beginPath(); 1072 ctx.ellipse(140, 143, 4, 6, 0, 0, Math.PI * 2); 1073 ctx.fill(); 1074 1075 // WILD BUSHY DARKER BROWN BEARD - more distinct 1076 ctx.fillStyle = '#6b5638'; // DARKER BROWN base 1077 1078 // Left side - elongated downward 1079 ctx.beginPath(); 1080 ctx.ellipse(100, 170, 24, 42, -0.2, 0, Math.PI * 2); 1081 ctx.fill(); 1082 1083 // Right side - elongated downward 1084 ctx.beginPath(); 1085 ctx.ellipse(180, 170, 24, 42, 0.2, 0, Math.PI * 2); 1086 ctx.fill(); 1087 1088 // Center mass - elongated not round 1089 ctx.fillStyle = '#7d6644'; // MID BROWN 1090 ctx.beginPath(); 1091 ctx.ellipse(140, 175, 28, 45, 0, 0, Math.PI * 2); 1092 ctx.fill(); 1093 1094 // Lower layers - more elongated and distinct 1095 ctx.fillStyle = '#8f7650'; // LIGHTER BROWN 1096 ctx.beginPath(); 1097 ctx.ellipse(115, 188, 20, 38, -0.1, 0, Math.PI * 2); 1098 ctx.fill(); 1099 1100 ctx.beginPath(); 1101 ctx.ellipse(165, 188, 20, 38, 0.1, 0, Math.PI * 2); 1102 ctx.fill(); 1103 1104 // Inner highlights for volume 1105 ctx.fillStyle = '#9d865a'; 1106 ctx.beginPath(); 1107 ctx.ellipse(140, 182, 20, 35, 0, 0, Math.PI * 2); 1108 ctx.fill(); 1109 1110 // FRAYED STRANDS at the bottom - darker and more visible 1111 ctx.strokeStyle = '#7d6644'; 1112 ctx.lineWidth = 2; 1113 ctx.lineCap = 'round'; 1114 1115 const frayedStrands = [ 1116 [[95, 198], [92, 212], [90, 223]], 1117 [[105, 203], [103, 217], [100, 230]], 1118 [[115, 208], [113, 220], [110, 234]], 1119 [[125, 212], [124, 224], [122, 237]], 1120 [[135, 215], [135, 227], [135, 240]], 1121 [[145, 215], [145, 227], [145, 240]], 1122 [[155, 212], [156, 224], [158, 237]], 1123 [[165, 208], [167, 220], [170, 234]], 1124 [[175, 203], [177, 217], [180, 230]], 1125 [[185, 198], [188, 212], [190, 223]], 1126 [[100, 200], [98, 214], [95, 227]], 1127 [[140, 216], [140, 229], [140, 242]], 1128 [[180, 200], [182, 214], [185, 227]], 1129 [[120, 210], [119, 222], [117, 235]], 1130 [[160, 210], [161, 222], [163, 235]] 1131 ]; 1132 1133 frayedStrands.forEach(points => { 1134 ctx.beginPath(); 1135 ctx.moveTo(points[0][0], points[0][1]); 1136 points.slice(1).forEach(([x, y]) => ctx.lineTo(x, y)); 1137 ctx.stroke(); 1138 }); 1139 1140 // PROMINENT WILD MUSTACHE - DARKER BROWN matching beard 1141 ctx.strokeStyle = '#6b5638'; 1142 ctx.lineWidth = 8; 1143 ctx.lineCap = 'round'; 1144 1145 ctx.beginPath(); 1146 ctx.moveTo(100, 150); 1147 ctx.quadraticCurveTo(80, 160, 60, 153); 1148 ctx.stroke(); 1149 1150 ctx.beginPath(); 1151 ctx.moveTo(180, 150); 1152 ctx.quadraticCurveTo(200, 160, 220, 153); 1153 ctx.stroke(); 1154 1155 ctx.strokeStyle = '#7d6644'; 1156 ctx.lineWidth = 6; 1157 1158 ctx.beginPath(); 1159 ctx.moveTo(103, 152); 1160 ctx.quadraticCurveTo(85, 161, 65, 155); 1161 ctx.stroke(); 1162 1163 ctx.beginPath(); 1164 ctx.moveTo(177, 152); 1165 ctx.quadraticCurveTo(195, 161, 215, 155); 1166 ctx.stroke(); 1167 1168 // DRAW 6 PROPER HUMAN ARMS WITH LONG SLEEVES AND HANDS 1169 function drawHumanArm(x1, y1, x2, y2, x3, y3) { 1170 // DARK BLUE LONG SLEEVE - Upper arm 1171 ctx.strokeStyle = '#3a4f6b'; 1172 ctx.lineWidth = 18; 1173 ctx.lineCap = 'round'; 1174 ctx.beginPath(); 1175 ctx.moveTo(x1, y1); 1176 ctx.lineTo(x2, y2); 1177 ctx.stroke(); 1178 1179 // Sleeve shadow for depth 1180 ctx.strokeStyle = '#2a3f5b'; 1181 ctx.lineWidth = 16; 1182 ctx.beginPath(); 1183 ctx.moveTo(x1 + 1, y1 + 1); 1184 ctx.lineTo(x2 + 1, y2 + 1); 1185 ctx.stroke(); 1186 1187 // DARK BLUE LONG SLEEVE - Forearm 1188 ctx.strokeStyle = '#3a4f6b'; 1189 ctx.lineWidth = 16; 1190 ctx.beginPath(); 1191 ctx.moveTo(x2, y2); 1192 ctx.lineTo(x3, y3); 1193 ctx.stroke(); 1194 1195 // Forearm sleeve shadow 1196 ctx.strokeStyle = '#2a3f5b'; 1197 ctx.lineWidth = 14; 1198 ctx.beginPath(); 1199 ctx.moveTo(x2 + 1, y2 + 1); 1200 ctx.lineTo(x3 + 1, y3 + 1); 1201 ctx.stroke(); 1202 1203 // Elbow joint (covered by sleeve) 1204 ctx.fillStyle = '#344a60'; 1205 ctx.beginPath(); 1206 ctx.arc(x2, y2, 10, 0, Math.PI * 2); 1207 ctx.fill(); 1208 1209 // PROPER HAND STRUCTURE (compact to fit viewport) 1210 const isLeft = x3 < 140; 1211 const direction = isLeft ? -1 : 1; 1212 1213 // Calculate hand angle based on arm direction 1214 const armAngle = Math.atan2(y3 - y2, x3 - x2); 1215 1216 // WRIST 1217 ctx.fillStyle = '#d4a574'; 1218 ctx.beginPath(); 1219 ctx.ellipse(x3, y3, 5, 6, armAngle, 0, Math.PI * 2); 1220 ctx.fill(); 1221 1222 // PALM - rectangular base of hand (smaller) 1223 const palmWidth = 10; 1224 const palmLength = 12; 1225 const palmX = x3 + direction * 6; 1226 const palmY = y3; 1227 1228 ctx.fillStyle = '#d4a574'; 1229 ctx.beginPath(); 1230 ctx.roundRect( 1231 isLeft ? palmX - palmLength : palmX, 1232 palmY - palmWidth/2, 1233 palmLength, 1234 palmWidth, 1235 2 1236 ); 1237 ctx.fill(); 1238 1239 // Palm crease lines 1240 ctx.strokeStyle = '#b8865a'; 1241 ctx.lineWidth = 0.5; 1242 ctx.beginPath(); 1243 ctx.moveTo(palmX + direction * 3, palmY - 3); 1244 ctx.lineTo(palmX + direction * 3, palmY + 3); 1245 ctx.stroke(); 1246 1247 // FIVE FINGERS extending from palm (shorter to fit) 1248 const fingerStartX = palmX + direction * palmLength; 1249 1250 // Finger positions (y offsets from palm center) - COMPACT 1251 const fingers = [ 1252 { y: -5, length: 8, width: 2 }, // pinky 1253 { y: -2, length: 10, width: 2 }, // ring 1254 { y: 1, length: 11, width: 2 }, // middle (longest) 1255 { y: 4, length: 9, width: 2 }, // index 1256 { y: 7, length: 6, width: 2.5 } // thumb (shorter, thicker) 1257 ]; 1258 1259 fingers.forEach((finger, index) => { 1260 const fy = palmY + finger.y; 1261 const isThumb = index === 4; 1262 1263 // Draw finger 1264 ctx.fillStyle = '#c89860'; 1265 ctx.beginPath(); 1266 1267 if (isThumb) { 1268 // Thumb at angle 1269 ctx.ellipse( 1270 fingerStartX + direction * 3, 1271 fy, 1272 finger.width * 1.5, 1273 finger.length / 2, 1274 direction * 0.6, 1275 0, 1276 Math.PI * 2 1277 ); 1278 } else { 1279 // Regular finger 1280 ctx.roundRect( 1281 isLeft ? fingerStartX - finger.length : fingerStartX, 1282 fy - finger.width/2, 1283 finger.length, 1284 finger.width, 1285 finger.width/2 1286 ); 1287 } 1288 ctx.fill(); 1289 1290 // Finger joint (not on thumb) 1291 if (!isThumb) { 1292 ctx.strokeStyle = '#a67850'; 1293 ctx.lineWidth = 0.5; 1294 ctx.beginPath(); 1295 ctx.moveTo(fingerStartX + direction * finger.length * 0.6, fy - finger.width/2); 1296 ctx.lineTo(fingerStartX + direction * finger.length * 0.6, fy + finger.width/2); 1297 ctx.stroke(); 1298 } 1299 1300 // Fingernail 1301 ctx.fillStyle = 'rgba(240, 230, 220, 0.6)'; 1302 ctx.beginPath(); 1303 const nailX = isThumb ? 1304 fingerStartX + direction * 4 : 1305 fingerStartX + direction * finger.length * 0.85; 1306 ctx.ellipse(nailX, fy, 0.8, 1, 0, 0, Math.PI * 2); 1307 ctx.fill(); 1308 }); 1309 } 1310 1311 // LEFT ARM 1 - upper left (hands moved inward to stay on screen) 1312 drawHumanArm(110, 165, 60, 100, 35, 95); 1313 1314 // LEFT ARM 2 - middle left 1315 drawHumanArm(110, 180, 50, 160, 35, 165); 1316 1317 // LEFT ARM 3 - lower left 1318 drawHumanArm(110, 195, 55, 225, 35, 255); 1319 1320 // RIGHT ARM 1 - upper right (hands moved inward to stay on screen) 1321 drawHumanArm(170, 165, 220, 100, 245, 95); 1322 1323 // RIGHT ARM 2 - middle right 1324 drawHumanArm(170, 180, 230, 160, 245, 165); 1325 1326 // RIGHT ARM 3 - lower right 1327 drawHumanArm(170, 195, 225, 225, 245, 255); 1328 1329 // Version indicator 1330 ctx.fillStyle = '#ff6600'; 1331 ctx.font = 'bold 10px Courier New'; 1332 ctx.fillText('v1.0.401', 5, 275); 1333 } 1334 1335 drawKamaji(); 1336 1337 // ANIMATIONS with anime.js 1338 let breatheOffset = 0; 1339 1340 function animate() { 1341 breatheOffset += 0.02; 1342 ctx.save(); 1343 ctx.clearRect(0, 0, canvas.width, canvas.height); 1344 ctx.translate(0, Math.sin(breatheOffset) * 2); 1345 drawKamaji(); 1346 ctx.restore(); 1347 requestAnimationFrame(animate); 1348 } 1349 1350 animate(); 1351 1352 // Hover wobble 1353 canvas.addEventListener('mouseenter', () => { 1354 anime({ 1355 targets: canvas, 1356 rotate: [-2, 2, -1, 1, 0], 1357 duration: 800, 1358 easing: 'easeInOutQuad' 1359 }); 1360 }); 1361 }); 1362 </script> 1363 </body> 1364 </html>