Pith - miter
<?php
$folderPath = isset($_GET['pith']) ? $_GET['pith'] : null;
$rawFile = isset($_GET['file']) ? $_GET['file'] : null;
$rootPath = realpath('pith/source'); 

$filePath = ($folderPath && $rawFile) ? $folderPath . DIRECTORY_SEPARATOR . $rawFile : $rawFile;

// "hide" source/.htaccess
if ($filePath && basename($filePath) === '.htaccess') {
	$filePath = null;
	$rawFile = null;
}

// ?pith=
$topFolder = $folderPath ?: basename($rootPath);
if (!$folderPath && $filePath) {
	$partsPath = explode(DIRECTORY_SEPARATOR, ltrim($filePath, DIRECTORY_SEPARATOR));
	$topFolder = count($partsPath) > 1 ? $partsPath[0] : basename($rootPath);
}

// ?pith=[folder]?file=README.md
if ($folderPath && !$rawFile) {
	$readmePath = $folderPath . DIRECTORY_SEPARATOR . 'README.md';
	if (is_file($rootPath . DIRECTORY_SEPARATOR . $readmePath)) {
		$filePath = $readmePath;
	}
}

// download button
if (isset($_GET['download']) && $filePath) {
	$safePath = getSafePath($rootPath . DIRECTORY_SEPARATOR . $filePath, $rootPath);
	if ($safePath && is_file($safePath)) {
		header('Content-Description: File Transfer');
		header('Content-Type: application/octet-stream');
		header('Content-Disposition: attachment; filename="' . basename($safePath) . '"');
		header('Expires: 0');
		header('Cache-Control: must-revalidate');
		header('Pragma: public');
		header('Content-Length: ' . filesize($safePath));
		readfile($safePath);
		exit;
	}
}

function getSafePath($path, $root) {
	$realPath = realpath($path);
	if ($realPath && strpos($realPath . DIRECTORY_SEPARATOR, $root . DIRECTORY_SEPARATOR) === 0) {
		return $realPath;
	}
	return null;
}

// tree build
function renderTree($dir, $root, $filePath = null, $folderPath = null, $depth = 0) {
	$items = scandir($dir);
	usort($items, function ($a, $b) use ($dir) {
		$aDir = is_dir($dir . DIRECTORY_SEPARATOR . $a);
		$bDir = is_dir($dir . DIRECTORY_SEPARATOR . $b);
		return $aDir === $bDir ? strcmp($a, $b) : ($aDir ? 1 : -1);
	});
	echo $depth === 0 ? "<ul class='pith_tree'>" : "<ul>";
	foreach ($items as $item) {
		if ($item == "." || $item == ".." || strpos($item, '.') === 0) continue;

		$fullPath = $dir . DIRECTORY_SEPARATOR . $item;
		$relPath = str_replace($root . DIRECTORY_SEPARATOR, '', $fullPath);

		if ($folderPath) {
			if ($relPath !== $folderPath && strpos($relPath, $folderPath . DIRECTORY_SEPARATOR) !== 0 && strpos($folderPath, $relPath . DIRECTORY_SEPARATOR) !== 0) {
				continue;
			}
		}

		if (is_dir($fullPath)) {
			$isOpen = "";
			if ($folderPath && ($relPath === $folderPath || strpos($relPath, $folderPath . DIRECTORY_SEPARATOR) === 0 || strpos($folderPath, $relPath . DIRECTORY_SEPARATOR) === 0)) {
				$isOpen = "open";
			}

		echo "<li class='folder'><details $isOpen data-path='" . htmlspecialchars($relPath) . "'><summary style='--depth:$depth'>" . htmlspecialchars($item) . "</summary>";

			renderTree($fullPath, $root, $filePath, $folderPath, $depth + 1);
			echo "</details></li>";
		} else {
			$activeClass = ($filePath === $relPath) ? "active-file" : "";
			$parts = explode(DIRECTORY_SEPARATOR, $relPath);
			$fileTopFolder = count($parts) > 1 ? $parts[0] : null;

			if ($fileTopFolder) {
				$urlFile = substr($relPath, strlen($fileTopFolder) + 1);
				$url = "?pith=" . urlencode($fileTopFolder) . "&file=" . urlencode($urlFile);
			} else {
				$url = "?pith=&file=" . urlencode($relPath);
			}
			$fileTime = pith_time_since(filemtime($fullPath));
			$fileSize = pith_file_size(filesize($fullPath));
			echo "<li class='file $activeClass'><a class='file_row' href='$url' style='--depth:$depth'><span class='file_name'>" . htmlspecialchars($item) . "</span><span class='file_time'>" . $fileTime . "</span><span class='file_size'>" . $fileSize . "</span></a></li>";
		}
	}
	echo "</ul>";
}

function pith_time_since($ts) {
	$diff = max(0, time() - $ts);
	$units = [31536000 => 'year', 2592000 => 'month', 86400 => 'day', 3600 => 'hour', 60 => 'min'];
	foreach ($units as $secs => $unit) {
		if ($diff >= $secs) {
			$n = (int) floor($diff / $secs);
			return $n . ' ' . $unit . ($n > 1 ? 's' : '') . ' ago';
		}
	}
	return 'just now';
}

function pith_file_size($bytes) {
	if ($bytes >= 1048576) return number_format($bytes / 1048576, 1) . ' mb';
	return number_format($bytes / 1024, 1) . ' kb';
}
?>

<div class="tenon_header">
	<div class="tenon_header_td_left">
		<span class="title">Pith <?php
		if ($folderPath) {
			echo "- ";
			$is_mobile = function_exists('isMobile') ? isMobile() : false;
			$is_logged_in = ($login ?? false) === true;

			if ($is_mobile) {
				$pith_title_trim = $is_logged_in ? 20 : 45;
			} else {
				$pith_title_trim = $is_logged_in ? 35 : 45;
			}

			$safe_pith_title = htmlspecialchars($title ?? '', ENT_QUOTES, 'UTF-8');
			echo substr($safe_pith_title, 0, $pith_title_trim) . " ";
		}
	?></span>
</div>
<div class="tenon_header_td_right">
</div>
</div>
<div class="pith_table">
	<div class="pith_td">

		<div class='pith_sidebar'>
			<?php renderTree($rootPath, $rootPath, $filePath, $folderPath); ?>
		</div>

		<div class='main_content'>
			<div class='pith_code'>
				<div class='pith_viewer'>
					<div class='pith_viewer_inner'>
						<?php 
						$showCopy = false;
						if ($filePath) {
							$safePath = getSafePath($rootPath . DIRECTORY_SEPARATOR . $filePath, $rootPath);
							if ($safePath && is_file($safePath)) {
								$ext = strtolower(pathinfo($safePath, PATHINFO_EXTENSION));
								$imageExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico'];
								if (in_array($ext, $imageExts)) {
									echo "<div class='image_viewport'><img src='pith/source/" . htmlspecialchars($filePath) . "' alt=''></div>";
								} else {
									echo "<pre><code id='file_content'>" . htmlspecialchars(file_get_contents($safePath)) . "</code></pre>";
									$showCopy = true;
								}
							} else {
								echo "<div class='empty_state'>Error: File not found or access denied.</div>";
							}
						}
						?>
					</div>
				</div>
				<?php if ($showCopy): ?>
					<button class='copy_btn' id='copyBtn' onclick='copyCode()'>Copy</button>
				<?php endif; ?>
			</div>
	</div>
</div>
</div>

<script src='pith/highlight.min.js'></script>
<script src='pith/highlightjs-line-numbers.js'></script>
<script>
	hljs.highlightAll();
	hljs.initLineNumbersOnLoad();

	function copyCode() {
		const codeElement = document.getElementById('file_content');
		const btn = document.getElementById('copyBtn');

		if (!codeElement) return;
		const codeCells = codeElement.querySelectorAll('.hljs-ln-code');
		let text = "";

		if (codeCells.length > 0) {
			text = Array.from(codeCells).map(cell => cell.textContent).join('\n');
		} else {
			text = codeElement.textContent;
		}

		navigator.clipboard.writeText(text).then(() => {
			const originalText = btn.innerText;
			btn.innerText = "Copied";
			btn.classList.add('success');

			setTimeout(() => {
				btn.innerText = originalText;
				btn.classList.remove('success');
			}, 2000);
		}).catch(err => {
			console.error('Failed to copy: ', err);
			alert("Failed to copy text.");
		});
	}
</script>
<script>
	const detailsElements = document.querySelectorAll('details[data-path]');
	detailsElements.forEach(details => {
		const path = details.getAttribute('data-path');
		if (details.open) {
			sessionStorage.setItem('p-' + path, 'open');
		} 
		else if (sessionStorage.getItem('p-' + path) === 'open') {
			details.open = true;
		}
		details.addEventListener('toggle', (e) => {
			const path = details.getAttribute('data-path');
			if (details.open) {
				sessionStorage.setItem('p-' + path, 'open');
			} else {
				sessionStorage.removeItem('p-' + path);
			}
			fitViewer();
		});
		const summary = details.querySelector('summary');
		if (summary) {
			summary.addEventListener('click', (e) => {
				if (!details.open) {
					e.preventDefault();
					window.location.href = '?pith=' + encodeURIComponent(path);
				}
			});
		}        
	});
	function fitViewer() {
		const viewer = document.querySelector('.pith_viewer');
		if (!viewer) return;
		viewer.style.height = '';
		const extra = window.innerHeight - document.body.offsetHeight;
		if (extra > 0) {
			viewer.style.height = (viewer.offsetHeight + extra) + 'px';
		}
	}
	window.addEventListener('load', () => {
		fitViewer();
		if (new URLSearchParams(location.search).has('file')) {
			document.querySelector('.pith_code')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
		}
	});
	window.addEventListener('resize', fitViewer);
</script>