|
54 | 54 | caret-color: transparent; |
55 | 55 | color: transparent; |
56 | 56 | } |
| 57 | + /* Memastikan background tetap penuh saat fullscreen */ |
| 58 | + :fullscreen { |
| 59 | + background-color: transparent; |
| 60 | + } |
57 | 61 | </style> |
58 | 62 | <script> |
59 | 63 | // Konfigurasi Tailwind CSS untuk mode gelap |
|
93 | 97 | <div class="w-3.5 h-3.5 bg-red-500 rounded-full"></div> |
94 | 98 | <div class="w-3.5 h-3.5 bg-yellow-500 rounded-full"></div> |
95 | 99 | <div class="w-3.5 h-3.5 bg-green-500 rounded-full"></div> |
96 | | - <div class="flex-grow text-center text-sm text-gray-600 dark:text-gray-400"> |
| 100 | + <div class="flex-grow text-center text-sm text-gray-600 dark:text-gray-400 overflow-hidden whitespace-nowrap text-ellipsis px-2"> |
97 | 101 | nmwafa@portfolio — bash |
98 | 102 | </div> |
99 | | - <!-- Tombol Light/Dark Mode --> |
100 | | - <button id="theme-toggle" class="text-gray-600 dark:text-gray-400 hover:text-black dark:hover:text-white"> |
101 | | - <svg id="theme-icon-dark" class="w-5 h-5 hidden" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg> |
102 | | - <svg id="theme-icon-light" class="w-5 h-5 hidden" fill="currentColor" viewBox="0 0 20 20"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm-.707 8.486a1 1 0 011.414 0l.707.707a1 1 0 01-1.414 1.414l-.707-.707a1 1 0 010-1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"></path></svg> |
103 | | - </button> |
| 103 | + |
| 104 | + <div class="flex items-center gap-3"> |
| 105 | + <!-- Tombol Fullscreen --> |
| 106 | + <button id="fullscreen-toggle" class="text-gray-600 dark:text-gray-400 hover:text-black dark:hover:text-white transition-colors"> |
| 107 | + <!-- Icon Expand --> |
| 108 | + <svg id="fs-icon-expand" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 109 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"></path> |
| 110 | + </svg> |
| 111 | + <!-- Icon Compress (Hidden by default) --> |
| 112 | + <svg id="fs-icon-compress" class="w-5 h-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
| 113 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4l5 5m11-5l-5 5m-11 11l5-5m11 5l-5-5M9 4v5H4m11-5v5h5M9 20v-5H4m11 5v-5h5"></path> |
| 114 | + </svg> |
| 115 | + </button> |
| 116 | + |
| 117 | + <!-- Tombol Light/Dark Mode --> |
| 118 | + <button id="theme-toggle" class="text-gray-600 dark:text-gray-400 hover:text-black dark:hover:text-white transition-colors"> |
| 119 | + <svg id="theme-icon-dark" class="w-5 h-5 hidden" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg> |
| 120 | + <svg id="theme-icon-light" class="w-5 h-5 hidden" fill="currentColor" viewBox="0 0 20 20"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm-.707 8.486a1 1 0 011.414 0l.707.707a1 1 0 01-1.414 1.414l-.707-.707a1 1 0 010-1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"></path></svg> |
| 121 | + </button> |
| 122 | + </div> |
104 | 123 | </div> |
105 | 124 |
|
106 | 125 | <!-- Konten Terminal --> |
|
113 | 132 | <!-- Container untuk input dan overlay visualnya --> |
114 | 133 | <div class="relative flex-grow" onclick="commandInput.focus()"> |
115 | 134 | <!-- Input field yang sebenarnya, teks dan kursornya transparan --> |
116 | | - <input type="text" id="command-input" class="w-full bg-transparent border-none outline-none" autofocus> |
| 135 | + <input type="text" id="command-input" class="w-full bg-transparent border-none outline-none" autofocus autocomplete="off"> |
117 | 136 | <!-- Overlay visual untuk menampilkan teks dan kursor kustom --> |
118 | 137 | <div class="absolute inset-0 flex items-center pointer-events-none"> |
119 | 138 | <span id="input-text-display" class="whitespace-pre"></span> |
|
133 | 152 | const themeIconLight = document.getElementById('theme-icon-light'); |
134 | 153 | const fakeCursor = document.getElementById('fake-cursor'); |
135 | 154 | const inputTextDisplay = document.getElementById('input-text-display'); |
| 155 | + |
| 156 | + // Unsur baru untuk Fullscreen |
| 157 | + const fullscreenToggle = document.getElementById('fullscreen-toggle'); |
| 158 | + const fsIconExpand = document.getElementById('fs-icon-expand'); |
| 159 | + const fsIconCompress = document.getElementById('fs-icon-compress'); |
136 | 160 |
|
137 | 161 | // --- Data Mock untuk Blog dan Proyek --- |
138 | 162 | const blogPosts = { |
|
234 | 258 | const fullCommand = commandInput.value.trim(); |
235 | 259 | if (fullCommand) { |
236 | 260 | const [command, ...args] = fullCommand.split(' '); |
237 | | - |
| 261 | + |
238 | 262 | const promptLine = document.createElement('div'); |
239 | 263 | promptLine.innerHTML = ` |
240 | 264 | <div class="flex"> |
241 | 265 | <div class="flex-shrink-0 mr-2"> |
242 | 266 | <span class="text-light-green dark:text-dark-green">nmwafa@portfolio</span><span class="text-light-fg dark:text-dark-fg">:</span><span class="text-light-blue dark:text-dark-blue">~</span><span class="text-light-fg dark:text-dark-fg">$</span> |
243 | 267 | </div> |
244 | | - <div class="flex-grow">${fullCommand}</div> |
| 268 | + <div class="flex-grow break-all">${fullCommand}</div> |
245 | 269 | </div>`; |
246 | 270 | output.appendChild(promptLine); |
247 | 271 | commandInput.value = ''; |
|
256 | 280 | } else { |
257 | 281 | resultDiv.innerHTML = `<p class="text-light-red dark:text-dark-red">Error: Perintah tidak ditemukan: ${command}. Ketik 'help' untuk daftar perintah.</p>`; |
258 | 282 | } |
259 | | - |
| 283 | + |
260 | 284 | terminalBody.scrollTop = terminalBody.scrollHeight; |
261 | 285 | } |
262 | 286 | commandInput.disabled = false; |
|
291 | 315 | setTheme(currentTheme === 'dark' ? 'light' : 'dark'); |
292 | 316 | }); |
293 | 317 |
|
| 318 | + // --- Logika Fullscreen (Cross-Browser) --- |
| 319 | + function toggleFullscreen() { |
| 320 | + if (!document.fullscreenElement && !document.webkitFullscreenElement && !document.mozFullScreenElement && !document.msFullscreenElement) { |
| 321 | + // Masuk Fullscreen |
| 322 | + const elem = document.documentElement; |
| 323 | + if (elem.requestFullscreen) { |
| 324 | + elem.requestFullscreen(); |
| 325 | + } else if (elem.webkitRequestFullscreen) { |
| 326 | + elem.webkitRequestFullscreen(); |
| 327 | + } else if (elem.msRequestFullscreen) { |
| 328 | + elem.msRequestFullscreen(); |
| 329 | + } else if (elem.mozRequestFullScreen) { |
| 330 | + elem.mozRequestFullScreen(); |
| 331 | + } |
| 332 | + } else { |
| 333 | + // Keluar Fullscreen |
| 334 | + if (document.exitFullscreen) { |
| 335 | + document.exitFullscreen(); |
| 336 | + } else if (document.webkitExitFullscreen) { |
| 337 | + document.webkitExitFullscreen(); |
| 338 | + } else if (document.msExitFullscreen) { |
| 339 | + document.msExitFullscreen(); |
| 340 | + } else if (document.mozCancelFullScreen) { |
| 341 | + document.mozCancelFullScreen(); |
| 342 | + } |
| 343 | + } |
| 344 | + } |
| 345 | + |
| 346 | + fullscreenToggle.addEventListener('click', toggleFullscreen); |
| 347 | + |
| 348 | + // Update icon saat status fullscreen berubah (termasuk jika user tekan ESC) |
| 349 | + function updateFullscreenIcons() { |
| 350 | + const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement; |
| 351 | + if (isFullscreen) { |
| 352 | + fsIconExpand.classList.add('hidden'); |
| 353 | + fsIconCompress.classList.remove('hidden'); |
| 354 | + } else { |
| 355 | + fsIconExpand.classList.remove('hidden'); |
| 356 | + fsIconCompress.classList.add('hidden'); |
| 357 | + } |
| 358 | + } |
| 359 | + |
| 360 | + document.addEventListener('fullscreenchange', updateFullscreenIcons); |
| 361 | + document.addEventListener('webkitfullscreenchange', updateFullscreenIcons); |
| 362 | + document.addEventListener('mozfullscreenchange', updateFullscreenIcons); |
| 363 | + document.addEventListener('MSFullscreenChange', updateFullscreenIcons); |
| 364 | + |
294 | 365 | // --- Inisialisasi --- |
295 | 366 | function getWelcomeMessage() { |
296 | 367 | return ` |
|
300 | 371 | <br> |
301 | 372 | `; |
302 | 373 | } |
303 | | - |
| 374 | + |
304 | 375 | function initialize() { |
305 | 376 | const preferredTheme = localStorage.getItem('theme'); |
306 | 377 | setTheme(preferredTheme || 'dark'); |
|
317 | 388 | initialize(); |
318 | 389 | </script> |
319 | 390 |
|
320 | | - <!-- block --> |
| 391 | + <!-- Block Inspeksi (Ctrl+U, F12, Right Click) --> |
321 | 392 | <script> |
322 | 393 | document.addEventListener('keydown', function(e) { |
323 | 394 | const isControlOrCommand = e.ctrlKey || e.metaKey; |
|
326 | 397 | e.preventDefault(); |
327 | 398 | } |
328 | 399 | }); |
329 | | - document.addEventListener('contextmenu', e => e.preventDefault()); |
330 | 400 | </script> |
331 | 401 | </body> |
332 | 402 | </html> |
333 | | - |
334 | | - |
335 | | - |
336 | | - |
337 | | - |
338 | | - |
339 | | - |
340 | | - |
341 | | - |
342 | | - |
343 | | - |
344 | | - |
345 | | - |
346 | | - |
347 | | - |
348 | | - |
349 | | - |
350 | | - |
351 | | - |
352 | | - |
353 | | - |
354 | | - |
355 | | - |
356 | | - |
|
0 commit comments