-
Notifications
You must be signed in to change notification settings - Fork 284
feat(website): add VCS landing pages and sitemap alias #970
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
fd030cd
fix(website): publish sitemap.xml
benvinegar 8522f65
feat(website): add VCS landing pages
benvinegar c1ce3a6
refactor(website): simplify marketing links
benvinegar e564178
docs(website): link VCS workflow guides
benvinegar 3f9dd06
test(website): match simplified homepage links
benvinegar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --- | ||
| --- |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { copyFile } from "node:fs/promises"; | ||
| import { join } from "node:path"; | ||
|
|
||
| const outputDirectory = join(import.meta.dirname, "..", "dist"); | ||
|
|
||
| /** Publishes the generated URL set at the conventional sitemap.xml path. */ | ||
| async function publishSitemapAlias(): Promise<void> { | ||
| await copyFile(join(outputDirectory, "sitemap-0.xml"), join(outputDirectory, "sitemap.xml")); | ||
| } | ||
|
|
||
| await publishSitemapAlias(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| export interface VcsLandingPage { | ||
| slug: "git" | "jujutsu" | "sapling"; | ||
| name: string; | ||
| command: string; | ||
| title: string; | ||
| description: string; | ||
| keywords: string[]; | ||
| answer: string; | ||
| quickStart: string[]; | ||
| setup?: { heading: string; lines: string[] }; | ||
| workflows: { heading: string; body: string }[]; | ||
| limitations: string; | ||
| docsHref: string; | ||
| } | ||
|
|
||
| /** Search-focused VCS guides rendered as both HTML and Markdown. */ | ||
| export const VCS_LANDING_PAGES: VcsLandingPage[] = [ | ||
| { | ||
| slug: "git", | ||
| name: "Git", | ||
| command: "git", | ||
| title: "Terminal Git diff viewer for reviewing changesets | Hunk", | ||
| description: | ||
| "Use Hunk as a terminal Git diff viewer for working trees, staged changes, commits, stashes, pager output, and difftool workflows.", | ||
| keywords: [ | ||
| "git diff viewer", | ||
| "terminal git diff", | ||
| "git difftool", | ||
| "git pager", | ||
| "review git changes", | ||
| ], | ||
| answer: | ||
| "Hunk turns Git changes into one review-first terminal UI: a multi-file stream, file navigation, split or stack layouts, expandable context, watch mode, and inline agent notes. Run Hunk directly for a complete changeset, or connect it to Git as a pager or difftool.", | ||
| quickStart: ["hunk diff", "hunk diff --staged", "hunk show HEAD~1", "hunk stash show"], | ||
| setup: { | ||
| heading: "Use Hunk from Git commands", | ||
| lines: [ | ||
| 'git config --global core.pager "hunk pager"', | ||
| 'git config --global difftool.hunk.cmd \'hunk difftool "$LOCAL" "$REMOTE" "$MERGED"\'', | ||
| "git config --global difftool.prompt false", | ||
| ], | ||
| }, | ||
| workflows: [ | ||
| { | ||
| heading: "Review the whole working tree", | ||
| body: "`hunk diff` includes tracked changes and untracked files in one ordered review stream. Add `--exclude-untracked` when you only want files Git already knows about.", | ||
| }, | ||
| { | ||
| heading: "Review staged work, commits, and stashes", | ||
| body: "Use `hunk diff --staged`, `hunk show <ref>`, or `hunk stash show [stash]`. Pathspecs after `--` narrow the input without changing Hunk's review navigation.", | ||
| }, | ||
| { | ||
| heading: "Pager or difftool", | ||
| body: "Pager mode lets `git diff` and `git show` open in Hunk. Git difftool integration is pair-oriented because Git invokes it once per file; use `hunk diff` for Hunk's native full-changeset stream.", | ||
| }, | ||
| ], | ||
| limitations: | ||
| "Git controls the input in pager mode, so untracked files are not synthesized there. Use `hunk diff` when you want Hunk to assemble the complete working-tree review.", | ||
| docsHref: "/docs/workflows/git-pager-and-difftool/", | ||
| }, | ||
| { | ||
| slug: "jujutsu", | ||
| name: "Jujutsu", | ||
| command: "jj", | ||
| title: "Terminal diff viewer for Jujutsu (jj) | Hunk", | ||
| description: | ||
| "Review Jujutsu working copies and changes in Hunk with native jj revsets, pager integration, multi-file navigation, and agent annotations.", | ||
| keywords: [ | ||
| "jujutsu diff viewer", | ||
| "jj diff tui", | ||
| "jj pager", | ||
| "terminal diff viewer", | ||
| "review jj changes", | ||
| ], | ||
| answer: | ||
| "Hunk detects Jujutsu repositories and passes native jj revsets to its Jujutsu backend. Review a working copy or change in the same multi-file terminal UI Hunk provides for Git, without translating your workflow into Git refs.", | ||
| quickStart: ["hunk diff", "hunk diff @-", "hunk show @"], | ||
| setup: { | ||
| heading: "Use Hunk as the jj pager", | ||
| lines: [ | ||
| "# jj config edit --user", | ||
| "[ui]", | ||
| 'pager = ["hunk", "pager"]', | ||
| 'diff-formatter = ":git"', | ||
| ], | ||
| }, | ||
| workflows: [ | ||
| { | ||
| heading: "Review the working copy", | ||
| body: "Run `hunk diff` inside a Jujutsu workspace. Hunk detects jj and builds one navigable review stream from the current working-copy changes.", | ||
| }, | ||
| { | ||
| heading: "Use native revsets", | ||
| body: "Targets such as `@`, `@-`, and larger revset expressions stay in Jujutsu syntax. Use `hunk diff <target>` to compare and `hunk show <target>` to inspect one change.", | ||
| }, | ||
| { | ||
| heading: "Keep Hunk in the jj workflow", | ||
| body: "Configure jj to emit Git-format diffs through `hunk pager`, so pager-driven commands can open the same review UI while ordinary text still falls back to a text pager.", | ||
| }, | ||
| ], | ||
| limitations: | ||
| "Jujutsu has no Git staging area or stash workflow. Hunk's jj watch mode currently polls rather than observing repository files directly.", | ||
| docsHref: "/docs/workflows/jujutsu-and-sapling/", | ||
| }, | ||
| { | ||
| slug: "sapling", | ||
| name: "Sapling", | ||
| command: "sl", | ||
| title: "Terminal diff viewer for Sapling SCM | Hunk", | ||
| description: | ||
| "Review Sapling working copies and commits in Hunk with native revsets, pager integration, multi-file navigation, and untracked-file support.", | ||
| keywords: [ | ||
| "sapling diff viewer", | ||
| "sl diff tui", | ||
| "sapling pager", | ||
| "terminal diff viewer", | ||
| "review sapling changes", | ||
| ], | ||
| answer: | ||
| "Hunk detects Sapling repositories and accepts Sapling's own revision syntax. It turns working-copy and committed changes into a multi-file terminal review with fast navigation, split or stack layouts, expandable context, and inline agent notes.", | ||
| quickStart: ["hunk diff", "hunk diff .^", "hunk show ."], | ||
| setup: { | ||
| heading: "Use Hunk as the Sapling pager", | ||
| lines: ["# sl config -u", "[pager]", "pager = hunk pager"], | ||
| }, | ||
| workflows: [ | ||
| { | ||
| heading: "Review tracked and unknown files", | ||
| body: "`hunk diff` includes Sapling working-copy changes and unknown files by default. Add `--exclude-untracked` when the review should contain tracked files only.", | ||
| }, | ||
| { | ||
| heading: "Use Sapling revisions", | ||
| body: "Pass native targets such as `.^` and `.` directly to `hunk diff` or `hunk show`. Hunk sends them to the detected Sapling backend rather than treating them as Git refs.", | ||
| }, | ||
| { | ||
| heading: "Open pager output in Hunk", | ||
| body: "Set Sapling's pager to `hunk pager` to route patch-like command output into the review UI. Non-diff output continues through Hunk's plain-text fallback.", | ||
| }, | ||
| ], | ||
| limitations: | ||
| "Sapling has no Git staging-area or stash commands in Hunk. Its watch mode currently polls rather than observing repository files directly.", | ||
| docsHref: "/docs/workflows/jujutsu-and-sapling/", | ||
| }, | ||
| ]; | ||
|
|
||
| /** Returns the canonical public URL for one VCS guide. */ | ||
| export function vcsLandingUrl(page: VcsLandingPage): string { | ||
| return `https://hunk.dev/${page.slug}/`; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import type { VcsLandingPage } from "../data/vcsLandingPages"; | ||
|
|
||
| /** Renders the agent-readable counterpart of one VCS landing page. */ | ||
| export function renderVcsLandingMarkdown(page: VcsLandingPage): string { | ||
| const sections = page.workflows.flatMap((workflow) => [ | ||
| `## ${workflow.heading}`, | ||
| "", | ||
| workflow.body, | ||
| "", | ||
| ]); | ||
| const setup = page.setup | ||
| ? [`## ${page.setup.heading}`, "", "```text", ...page.setup.lines, "```", ""] | ||
| : []; | ||
|
|
||
| return [ | ||
| `# Hunk for ${page.name}`, | ||
| "", | ||
| `> ${page.description}`, | ||
| "", | ||
| page.answer, | ||
| "", | ||
| "## Start reviewing", | ||
| "", | ||
| "```bash", | ||
| ...page.quickStart, | ||
| "```", | ||
| "", | ||
| ...sections, | ||
| ...setup, | ||
| "## What to know", | ||
| "", | ||
| page.limitations, | ||
| "", | ||
| `[Read the complete ${page.name} workflow documentation](https://hunk.dev${page.docsHref})`, | ||
| "", | ||
| "[Install Hunk](https://hunk.dev/#install)", | ||
| "", | ||
| ].join("\n"); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| --- | ||
| import BrandFooter from "../components/BrandFooter.astro"; | ||
| import BrandHeader from "../components/BrandHeader.astro"; | ||
| import PageHead from "../components/PageHead.astro"; | ||
| import { VCS_LANDING_PAGES, vcsLandingUrl, type VcsLandingPage } from "../data/vcsLandingPages"; | ||
| import { withInlineCode, withoutInlineCode } from "../lib/inlineCode"; | ||
| import { SITE_ORIGIN } from "../lib/site"; | ||
| import "../styles/marketing.css"; | ||
| import "../styles/compare.css"; | ||
|
|
||
| /** Builds one canonical workflow guide for every VCS Hunk supports. */ | ||
| export function getStaticPaths() { | ||
| return VCS_LANDING_PAGES.map((page) => ({ params: { vcs: page.slug }, props: { page } })); | ||
| } | ||
|
|
||
| interface Props { | ||
| page: VcsLandingPage; | ||
| } | ||
|
|
||
| const { page } = Astro.props; | ||
| const path = `/${page.slug}/`; | ||
| const url = vcsLandingUrl(page); | ||
| const siblings = VCS_LANDING_PAGES.filter((entry) => entry.slug !== page.slug); | ||
| const schema = [ | ||
| { | ||
| "@context": "https://schema.org", | ||
| "@type": "TechArticle", | ||
| headline: page.title, | ||
| description: page.description, | ||
| url, | ||
| mainEntityOfPage: url, | ||
| inLanguage: "en", | ||
| author: { "@type": "Organization", name: "Modem", url: "https://modem.dev" }, | ||
| publisher: { "@type": "Organization", name: "Modem", url: "https://modem.dev" }, | ||
| about: { | ||
| "@type": "SoftwareApplication", | ||
| name: "Hunk", | ||
| url: `${SITE_ORIGIN}/`, | ||
| applicationCategory: "DeveloperApplication", | ||
| operatingSystem: "macOS, Linux, Windows", | ||
| }, | ||
| abstract: withoutInlineCode(page.answer), | ||
| }, | ||
| { | ||
| "@context": "https://schema.org", | ||
| "@type": "BreadcrumbList", | ||
| itemListElement: [ | ||
| { "@type": "ListItem", position: 1, name: "Hunk", item: `${SITE_ORIGIN}/` }, | ||
| { "@type": "ListItem", position: 2, name: `Hunk for ${page.name}`, item: url }, | ||
| ], | ||
| }, | ||
| ]; | ||
| --- | ||
|
|
||
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <PageHead | ||
| title={page.title} | ||
| description={page.description} | ||
| keywords={page.keywords.join(", ")} | ||
| path={path} | ||
| ogImageAlt={`Hunk terminal diff viewer for ${page.name}`} | ||
| schema={schema} | ||
| /> | ||
| <link rel="alternate" type="text/markdown" href={`${SITE_ORIGIN}/${page.slug}.md`} /> | ||
| </head> | ||
| <body> | ||
| <div class="marketing"> | ||
| <header class="top"><BrandHeader /></header> | ||
| <main> | ||
| <section class="cmp cmp-intro"> | ||
| <nav class="crumbs" aria-label="Breadcrumb"><a href="/">Hunk</a> / {page.name}</nav> | ||
| <p class="kicker"># hunk for {page.command}</p> | ||
| <h1>Review {page.name} changes<br />without leaving the terminal.</h1> | ||
| <p class="cmp-answer">{page.answer}</p> | ||
| <p class="cmp-meta"> | ||
| <span>Native {page.command} repository support</span> | ||
| <a href={`/${page.slug}.md`}>Read as Markdown</a> | ||
| </p> | ||
| </section> | ||
|
|
||
| <section class="cmp cmp-section" aria-labelledby="quick-start"> | ||
| <h2 id="quick-start">Start reviewing</h2> | ||
| <p class="cmp-prose">Run these commands inside a {page.name} workspace:</p> | ||
| <figure class="cmp-code"> | ||
| <div class="paper-bar"><span class="dot"></span><span class="dot"></span><span class="dot"></span><span class="pt">terminal</span></div> | ||
| <pre tabindex="0"><code>{page.quickStart.join("\n")}</code></pre> | ||
| </figure> | ||
| </section> | ||
|
|
||
| {page.workflows.map((workflow) => ( | ||
| <section class="cmp cmp-section"> | ||
| <h2>{workflow.heading}</h2> | ||
| <p class="cmp-prose" set:html={withInlineCode(workflow.body)} /> | ||
| </section> | ||
| ))} | ||
|
|
||
| {page.setup && ( | ||
| <section class="cmp cmp-section"> | ||
| <h2>{page.setup.heading}</h2> | ||
| <figure class="cmp-code"> | ||
| <div class="paper-bar"><span class="dot"></span><span class="dot"></span><span class="dot"></span><span class="pt">config</span></div> | ||
| <pre tabindex="0"><code>{page.setup.lines.join("\n")}</code></pre> | ||
| </figure> | ||
| </section> | ||
| )} | ||
|
|
||
| <section class="cmp cmp-section"> | ||
| <h2>What to know</h2> | ||
| <p class="cmp-prose">{page.limitations}</p> | ||
| <p class="cmp-prose"><a href={page.docsHref}>Read the complete {page.name} workflow documentation →</a></p> | ||
| </section> | ||
|
|
||
| <section class="cmp cmp-section" aria-labelledby="other-vcs"> | ||
| <h2 id="other-vcs">Hunk for other version-control systems</h2> | ||
| <div class="cmp-cards"> | ||
| {siblings.map((entry) => ( | ||
| <a class="cmp-card" href={`/${entry.slug}/`}> | ||
| <b>Hunk for {entry.name}</b> | ||
| <span>{entry.description}</span> | ||
| </a> | ||
| ))} | ||
| </div> | ||
| </section> | ||
|
|
||
| <section class="cmp cmp-tail"> | ||
| <h2>Try Hunk on your next {page.name} changeset.</h2> | ||
| <div class="cta"> | ||
| <a class="ghost" href="/#install">Install hunk</a> | ||
| <a class="plain" href={page.docsHref}>Read the workflow docs</a> | ||
| </div> | ||
| </section> | ||
| </main> | ||
| <BrandFooter /> | ||
| </div> | ||
| </body> | ||
| </html> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The production build now relies on fixed sitemap output paths and filenames without an automated test covering the generated alias. A regression in the Astro output or copy behavior can fail deployment or publish an incorrect sitemap without being caught by the test suite.
Context Used: guidelines.mdc Cursor rule (source)
Prompt To Fix With AI
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!