[ 'label' => 'Getting Started', 'pages' => [ 'introduction' => 'Introduction', 'quick-start' => 'Quick Start', 'registration' => 'Register a Name', 'dns-management' => 'Manage DNS', ], ], 'chain' => [ 'label' => 'Blockchain', 'pages' => [ 'overview' => 'Overview', 'daemon-rpc' => 'Daemon RPC', 'wallet-rpc' => 'Wallet RPC', 'mining' => 'Mining', 'aliases' => 'Chain Aliases', 'hardforks' => 'Hardfork History', ], ], 'names' => [ 'label' => 'Name System', 'pages' => [ 'overview' => 'Overview', 'registration' => 'Registration', 'dns-records' => 'DNS Records', 'sunrise' => 'Sunrise Period', 'sidechain' => 'ITNS Sidechain', ], ], 'services' => [ 'label' => 'Services', 'pages' => [ 'dns-hosting' => 'DNS Hosting', 'ssl-certificates' => 'SSL Certificates', 'proxy-network' => 'Proxy Network', 'gateway-operators' => 'Gateway Operators', ], ], 'api' => [ 'label' => 'API Reference', 'pages' => [ 'overview' => 'Overview', 'names' => 'Names API', 'explorer' => 'Explorer API', 'proxy' => 'Proxy API', 'gateway' => 'Gateway API', 'authentication' => 'Authentication', ], ], 'governance' => [ 'label' => 'Governance', 'pages' => [ 'cic' => 'Community Interest Company', 'wallet-holders' => 'Wallet Holders', 'economics' => 'Token Economics', ], ], ]; public function __construct() { $this->contentPath = base_path('app/Website/Docs/Content'); } public function index(): \Illuminate\View\View { return view('docs::index', [ 'navigation' => $this->navigation, ]); } public function show(string $section, ?string $page = null): \Illuminate\View\View { $page = $page ?? 'overview'; $filePath = "{$this->contentPath}/{$section}/{$page}.md"; if (! file_exists($filePath)) { abort(404); } $markdown = file_get_contents($filePath); $html = $this->renderMarkdown($markdown); $title = $this->extractTitle($markdown); // Find current position in nav for prev/next $prevNext = $this->findPrevNext($section, $page); return view('docs::show', [ 'navigation' => $this->navigation, 'currentSection' => $section, 'currentPage' => $page, 'content' => $html, 'title' => $title, 'prev' => $prevNext['prev'], 'next' => $prevNext['next'], ]); } public function search(Request $request): \Illuminate\View\View { $query = trim((string) $request->get('q', '')); $results = []; if (strlen($query) >= 2) { $results = $this->searchContent($query); } return view('docs::search', [ 'navigation' => $this->navigation, 'query' => $query, 'results' => $results, ]); } private function renderMarkdown(string $markdown): string { // Strip YAML frontmatter $markdown = preg_replace('/^---\n.*?\n---\n/s', '', $markdown); // Basic markdown to HTML (no external dependency) $html = e($markdown); // Headers $html = preg_replace('/^######\s+(.+)$/m', '
$1
', $html); $html = preg_replace('/^#####\s+(.+)$/m', '
$1
', $html); $html = preg_replace('/^####\s+(.+)$/m', '

$1

', $html); $html = preg_replace('/^###\s+(.+)$/m', '

$1

', $html); $html = preg_replace('/^##\s+(.+)$/m', '

$1

', $html); $html = preg_replace('/^#\s+(.+)$/m', '

$1

', $html); // Code blocks $html = preg_replace('/```(\w*)\n(.*?)\n```/s', '
$2
', $html); // Inline code $html = preg_replace('/`([^`]+)`/', '$1', $html); // Bold and italic $html = preg_replace('/\*\*(.+?)\*\*/', '$1', $html); $html = preg_replace('/\*(.+?)\*/', '$1', $html); // Links $html = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '$1', $html); // Lists $html = preg_replace('/^- (.+)$/m', '
  • $1
  • ', $html); $html = preg_replace('/(
  • .*<\/li>\n?)+/', '', $html); // Paragraphs $html = preg_replace('/\n\n([^<])/', '

    $1', $html); $html = '

    ' . $html . '

    '; // Clean up $html = str_replace('

    <\/p>/', '', $html); $html = str_replace('

    ', '
    ', $html);
            $html = str_replace('

    ', '
    ', $html); $html = str_replace('

    ', $html); $html = str_replace('

    ', '', $html); return $html; } private function extractTitle(string $markdown): string { if (preg_match('/^#\s+(.+)$/m', $markdown, $matches)) { return $matches[1]; } return 'Documentation'; } private function findPrevNext(string $section, string $page): array { $flat = []; foreach ($this->navigation as $sec => $data) { foreach ($data['pages'] as $pg => $label) { $flat[] = ['section' => $sec, 'page' => $pg, 'label' => $label]; } } $current = null; foreach ($flat as $i => $item) { if ($item['section'] === $section && $item['page'] === $page) { $current = $i; break; } } return [ 'prev' => $current !== null && $current > 0 ? $flat[$current - 1] : null, 'next' => $current !== null && $current < count($flat) - 1 ? $flat[$current + 1] : null, ]; } private function searchContent(string $query): array { $results = []; $query = strtolower($query); foreach ($this->navigation as $section => $data) { foreach ($data['pages'] as $page => $label) { $filePath = "{$this->contentPath}/{$section}/{$page}.md"; if (! file_exists($filePath)) { continue; } $content = strtolower(file_get_contents($filePath)); if (str_contains($content, $query)) { // Extract snippet around match $pos = strpos($content, $query); $start = max(0, $pos - 80); $snippet = substr(file_get_contents($filePath), $start, 200); $snippet = trim(preg_replace('/\s+/', ' ', $snippet)); $results[] = [ 'section' => $section, 'page' => $page, 'label' => $label, 'sectionLabel' => $data['label'], 'snippet' => $snippet, ]; } } } return $results; } }