From 920fc30a1272cc5c920414172f360ac50066055e Mon Sep 17 00:00:00 2001 From: TheRealToxicDev Date: Tue, 3 Feb 2026 17:55:06 -0700 Subject: [PATCH 1/2] Add GitHub workflows and fix import organization --- package.json | 3 +- src/components/MarkdownRenderer.tsx | 4 +-- src/components/landing/Header.tsx | 2 +- src/components/landing/Hero.tsx | 2 +- src/components/landing/QuickStart.tsx | 3 +- src/components/layouts/DocsLayout.tsx | 6 ++-- src/lib/useVersion.ts | 2 +- src/routes/docs/$.tsx | 42 ++++++++++++--------------- src/routes/features.tsx | 2 +- 9 files changed, 30 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 0621fae..54a1a64 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "test": "vitest run", "format": "biome format", "format:fix": "biome format --write", - "format:check": "biome format --check", + "format:check": "biome check", + "format:apply": "biome check --fix", "lint": "biome lint", "check": "biome check", "deploy": "npm run build && wrangler deploy" diff --git a/src/components/MarkdownRenderer.tsx b/src/components/MarkdownRenderer.tsx index 6508e69..dec51cb 100644 --- a/src/components/MarkdownRenderer.tsx +++ b/src/components/MarkdownRenderer.tsx @@ -1,5 +1,5 @@ -import { Check, Copy, AlertTriangle, Info, Lightbulb } from "lucide-react"; -import { useState, useCallback } from "react"; +import { AlertTriangle, Check, Copy, Info, Lightbulb } from "lucide-react"; +import { useCallback, useState } from "react"; import ReactMarkdown from "react-markdown"; import rehypeHighlight from "rehype-highlight"; import rehypeRaw from "rehype-raw"; diff --git a/src/components/landing/Header.tsx b/src/components/landing/Header.tsx index b2d295b..96da1a6 100644 --- a/src/components/landing/Header.tsx +++ b/src/components/landing/Header.tsx @@ -1,8 +1,8 @@ import { Github, Menu, Twitter, X } from "lucide-react"; import { useState } from "react"; +import { useVersion } from "../../lib/useVersion"; import { LogoIcon } from "../Logo"; import { Button } from "../ui/Button"; -import { useVersion } from "../../lib/useVersion"; export function Header() { const [mobileMenuOpen, setMobileMenuOpen] = useState(false); diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx index f13a98b..782a053 100644 --- a/src/components/landing/Hero.tsx +++ b/src/components/landing/Hero.tsx @@ -1,6 +1,6 @@ import { AlertTriangle, ChevronRight, Github, Terminal } from "lucide-react"; -import { Button } from "../ui/Button"; import { useVersion } from "../../lib/useVersion"; +import { Button } from "../ui/Button"; export function Hero() { const { version } = useVersion(); diff --git a/src/components/landing/QuickStart.tsx b/src/components/landing/QuickStart.tsx index de29d25..5de5632 100644 --- a/src/components/landing/QuickStart.tsx +++ b/src/components/landing/QuickStart.tsx @@ -1,7 +1,6 @@ import * as Tabs from "@radix-ui/react-tabs"; import { Check, Copy, GitBranch, Globe, Terminal } from "lucide-react"; -import { useId } from "react"; -import { useState } from "react"; +import { useId, useState } from "react"; type TokenType = "command" | "flag" | "path" | "comment" | "string" | "text"; diff --git a/src/components/layouts/DocsLayout.tsx b/src/components/layouts/DocsLayout.tsx index c54dfea..6ff1f4d 100644 --- a/src/components/layouts/DocsLayout.tsx +++ b/src/components/layouts/DocsLayout.tsx @@ -1,8 +1,8 @@ -import { ChevronDown, Home, Menu, X, List, Search } from "lucide-react"; -import { useState, useEffect } from "react"; +import { ChevronDown, Home, List, Menu, Search, X } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useVersion } from "../../lib/useVersion"; import { LogoIcon } from "../Logo"; import { Button } from "../ui/Button"; -import { useVersion } from "../../lib/useVersion"; interface LayoutProps { children: React.ReactNode; diff --git a/src/lib/useVersion.ts b/src/lib/useVersion.ts index 36e923f..87f5c55 100644 --- a/src/lib/useVersion.ts +++ b/src/lib/useVersion.ts @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useEffect, useState } from "react"; interface VersionInfo { version: string; diff --git a/src/routes/docs/$.tsx b/src/routes/docs/$.tsx index 57fd79a..3243c9b 100644 --- a/src/routes/docs/$.tsx +++ b/src/routes/docs/$.tsx @@ -3,40 +3,34 @@ import { createFileRoute } from "@tanstack/react-router"; const SITE_URL = "https://noslop.tech"; const DOCS_IMAGE = `${SITE_URL}/og-docs.png`; +import aiConfigurationMd from "../../../docs/ai/configuration.md?raw"; +import aiExamplesMd from "../../../docs/ai/examples.md?raw"; +import aiIndexMd from "../../../docs/ai/index.md?raw"; +import analysisGitMd from "../../../docs/analysis/git.md?raw"; +import analysisIndexMd from "../../../docs/analysis/index.md?raw"; +import analysisRepositoryMd from "../../../docs/analysis/repository.md?raw"; +import analysisWebMd from "../../../docs/analysis/web.md?raw"; +import cliCommandsMdNew from "../../../docs/cli/commands.md?raw"; +import cliDetectionStrategiesMd from "../../../docs/cli/detection-strategies.md?raw"; +import cliIndexMd from "../../../docs/cli/index.md?raw"; +import communityContributingMd from "../../../docs/community/contributing.md?raw"; +import communityIndexMd from "../../../docs/community/index.md?raw"; +import communitySecurityMd from "../../../docs/community/security.md?raw"; +import gettingStartedConfigurationMd from "../../../docs/getting-started/configuration.md?raw"; // Import all markdown files statically for Cloudflare Workers compatibility // Getting Started section import gettingStartedIndexMd from "../../../docs/getting-started/index.md?raw"; import gettingStartedInstallationMd from "../../../docs/getting-started/installation.md?raw"; -import gettingStartedQuickStartMd from "../../../docs/getting-started/quick-start.md?raw"; import gettingStartedQuickReferenceMd from "../../../docs/getting-started/quick-reference.md?raw"; -import gettingStartedConfigurationMd from "../../../docs/getting-started/configuration.md?raw"; - -import cliIndexMd from "../../../docs/cli/index.md?raw"; -import cliCommandsMdNew from "../../../docs/cli/commands.md?raw"; -import cliDetectionStrategiesMd from "../../../docs/cli/detection-strategies.md?raw"; - -import analysisIndexMd from "../../../docs/analysis/index.md?raw"; -import analysisRepositoryMd from "../../../docs/analysis/repository.md?raw"; -import analysisGitMd from "../../../docs/analysis/git.md?raw"; -import analysisWebMd from "../../../docs/analysis/web.md?raw"; - -import aiIndexMd from "../../../docs/ai/index.md?raw"; -import aiConfigurationMd from "../../../docs/ai/configuration.md?raw"; -import aiExamplesMd from "../../../docs/ai/examples.md?raw"; - -import integrationsIndexMd from "../../../docs/integrations/index.md?raw"; +import gettingStartedQuickStartMd from "../../../docs/getting-started/quick-start.md?raw"; import integrationsAgentSkillsMd from "../../../docs/integrations/agent-skills.md?raw"; +import integrationsIndexMd from "../../../docs/integrations/index.md?raw"; import integrationsWebhooksMd from "../../../docs/integrations/webhooks.md?raw"; - -import referenceIndexMd from "../../../docs/reference/index.md?raw"; import referenceAdvancedConfigMd from "../../../docs/reference/advanced-configuration.md?raw"; import referenceBuildDevMd from "../../../docs/reference/build-development.md?raw"; -import referenceTroubleshootingMd from "../../../docs/reference/troubleshooting.md?raw"; import referenceDisclaimerMd from "../../../docs/reference/disclaimer.md?raw"; - -import communityIndexMd from "../../../docs/community/index.md?raw"; -import communityContributingMd from "../../../docs/community/contributing.md?raw"; -import communitySecurityMd from "../../../docs/community/security.md?raw"; +import referenceIndexMd from "../../../docs/reference/index.md?raw"; +import referenceTroubleshootingMd from "../../../docs/reference/troubleshooting.md?raw"; import { DocsLayout } from "../../components/layouts/DocsLayout"; import { MarkdownRenderer } from "../../components/MarkdownRenderer"; diff --git a/src/routes/features.tsx b/src/routes/features.tsx index 8811fbd..01b8e09 100644 --- a/src/routes/features.tsx +++ b/src/routes/features.tsx @@ -16,10 +16,10 @@ import { Webhook, Zap, } from "lucide-react"; +import { useState } from "react"; import { LogoIcon } from "../components/Logo"; import { Footer } from "../components/landing/Footer"; import { useVersion } from "../lib/useVersion"; -import { useState } from "react"; const coreFeatures = [ { From a509cd0184892283c0091e49ef4160c21784a794 Mon Sep 17 00:00:00 2001 From: TheRealToxicDev Date: Tue, 3 Feb 2026 18:31:29 -0700 Subject: [PATCH 2/2] fix up chagelog page issues --- src/routes/changelog.tsx | 193 ++++++++++++++++++++++++++++++--------- 1 file changed, 150 insertions(+), 43 deletions(-) diff --git a/src/routes/changelog.tsx b/src/routes/changelog.tsx index 73aacec..95b2dcc 100644 --- a/src/routes/changelog.tsx +++ b/src/routes/changelog.tsx @@ -8,6 +8,7 @@ import { Download, ExternalLink, GitCommit, + GithubIcon, Loader2, Plus, Tag, @@ -46,37 +47,92 @@ function parseReleaseBody(body: string): ParsedSection[] { const sections: ParsedSection[] = []; const normalized = body.replace(/\r\n/g, "\n"); - // Parse sections within the release body - const sectionRegex = - /### (Added|Changed|Fixed|Technical Details|Usage Examples|Known Limitations)\n([\s\S]*?)(?=###|$)/g; - const sectionMatches = [...normalized.matchAll(sectionRegex)]; + // Split by section headers (### Header) + const sectionSplit = normalized.split(/^### /m); - for (const sectionMatch of sectionMatches) { - const sectionTitle = sectionMatch[1]; - const sectionContent = sectionMatch[2].trim(); + // First element is before any header, skip it + for (let i = 1; i < sectionSplit.length; i++) { + const part = sectionSplit[i]; + const lines = part.split("\n"); + const firstLine = lines[0]; + + // Extract section title from first line + let sectionTitle = firstLine; + let sectionContent = lines.slice(1).join("\n").trim(); let type: ParsedSection["type"] = "other"; - if (sectionTitle === "Added") type = "added"; - else if (sectionTitle === "Changed") type = "changed"; - else if (sectionTitle === "Fixed") type = "fixed"; - else if (sectionTitle === "Technical Details") type = "technical"; + if (firstLine === "Added") { + type = "added"; + } else if (firstLine === "Changed") { + type = "changed"; + } else if (firstLine === "Fixed") { + type = "fixed"; + } else if (firstLine === "Technical Details") { + type = "technical"; + } - // Parse items - look for bullet points or sub-sections + // Parse items - look for bullet points, numbered items, or sub-sections const items: string[] = []; - const lines = sectionContent.split("\n"); + const contentLines = sectionContent.split("\n"); + let currentItem = ""; + let currentSubsection = ""; + + for (const line of contentLines) { + // Skip completely empty lines + if (!line.trim()) continue; - for (const line of lines) { - if (line.startsWith("- ") || line.startsWith("* ")) { + // Sub-section headers (#### ) + if (line.startsWith("#### ")) { if (currentItem) items.push(currentItem.trim()); - currentItem = line.slice(2); - } else if (line.startsWith("#### ")) { + currentSubsection = line.slice(5); // Remove "#### " + currentItem = ""; // Reset, don't create item yet + continue; + } + + // Top-level bullets (not indented) + if ( + (line.startsWith("- ") || line.startsWith("* ")) && + !line.startsWith(" ") + ) { + if (currentItem) items.push(currentItem.trim()); + const bulletText = line.slice(2); + // If we have a subsection, include it with the first item + if (currentSubsection && currentItem === "") { + currentItem = `**${currentSubsection}**\n${bulletText}`; + currentSubsection = ""; // Consumed + } else { + currentItem = bulletText; + } + } + // Numbered items (1., 2., etc.) not indented + else if (/^\d+\.\s/.test(line) && !line.startsWith(" ")) { if (currentItem) items.push(currentItem.trim()); - currentItem = `**${line.slice(5)}**`; - } else if (line.trim() && currentItem) { - currentItem += ` ${line.trim()}`; + const numberedText = line.replace(/^\d+\.\s/, ""); + // If we have a subsection, include it with the first item + if (currentSubsection && currentItem === "") { + currentItem = `**${currentSubsection}**\n${numberedText}`; + currentSubsection = ""; // Consumed + } else { + currentItem = numberedText; + } + } + // Sub-items (indented bullets or numbered) + else if ( + line.startsWith(" - ") || + line.startsWith(" * ") || + /^ \d+\.\s/.test(line) + ) { + if (currentItem) { + currentItem += `\n${line.trim()}`; + } + } + // Continuation lines (non-empty, not starting with special chars) + else if (line.trim() && currentItem) { + currentItem += `\n${line.trim()}`; } } + if (currentItem) items.push(currentItem.trim()); if (items.length > 0) { @@ -116,7 +172,7 @@ function getSectionColor(type: string) { } function ReleaseSection({ section }: { section: ParsedSection }) { - const [expanded, setExpanded] = useState(section.type === "added"); + const [expanded, setExpanded] = useState(false); return (
@@ -148,20 +204,25 @@ function ReleaseSection({ section }: { section: ParsedSection }) { {section.items.map((item) => (
- $1', - ) - .replace( - /`(.*?)`/g, - '$1', - ), - }} - /> +
+ {item.split("\n").map((line, idx) => ( +
0 ? "ml-2 text-white/60" : ""} + dangerouslySetInnerHTML={{ + __html: line + .replace( + /\*\*(.*?)\*\*/g, + '$1', + ) + .replace( + /`(.*?)`/g, + '$1', + ), + }} + /> + ))} +
))}
@@ -223,7 +284,7 @@ function ReleaseCard({ Latest )} - {release.prerelease && ( + {release.prerelease && ( Pre-release @@ -315,6 +376,13 @@ function ChangelogPage() { const [releases, setReleases] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [page, setPage] = useState(1); + const itemsPerPage = 10; + const totalPages = Math.ceil(releases.length / itemsPerPage); + const paginatedReleases = releases.slice( + (page - 1) * itemsPerPage, + page * itemsPerPage, + ); useEffect(() => { async function fetchReleases() { @@ -379,8 +447,7 @@ function ChangelogPage() { rel="noopener noreferrer" className="text-sm text-white/50 hover:text-white transition-colors flex items-center gap-1" > - GitHub - +
@@ -425,7 +492,7 @@ function ChangelogPage() { releases
-
+
Latest:{" "} {releases[0]?.tag_name} @@ -465,19 +532,59 @@ function ChangelogPage() { )} {!loading && !error && releases.length > 0 && ( + <>
- {releases.map((release, idx) => ( + {paginatedReleases.map((release, idx) => ( ))}
+ + {/* Pagination */} + {totalPages > 1 && ( +
+ + + {Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => ( + + ))} + + +
+ )} + )} - {/* End of timeline */} - {!loading && !error && releases.length > 0 && ( + {/* End of timeline - only show on last page */} + {!loading && !error && releases.length > 0 && page === totalPages && (