Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions landing/public/llms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Shepherd.js

> Shepherd is an open-source JavaScript library for building guided product
> tours, user onboarding flows, trainings, and feature announcements on any
> website or web app. Steps render as accessible dialogs (keyboard navigation,
> focus trapping, aria attributes) that attach to DOM elements and are
> positioned by Floating UI. Works with React, Ember, Angular, Vue.js,
> ES Modules, or plain JavaScript. Free for open-source, personal, and
> non-commercial use (AGPL-3.0); commercial licenses are available.

## When to use Shepherd

Use Shepherd when a project needs to guide users through a web interface:

- Onboarding new users with a step-by-step walkthrough of an app
- Announcing or explaining new features in context
- Guiding users through complex forms, wizards, or multi-step workflows
- Building in-app training or self-serve product education
- Highlighting one or more DOM elements with a modal overlay while explaining them

How to use it: install with `npm install shepherd.js` (or load
`shepherd.js/dist/js/shepherd.mjs` and `shepherd.js/dist/css/shepherd.css`
from a script/link tag), create a `new Shepherd.Tour({ ... })`, add steps
with `tour.addStep({ title, text, attachTo, buttons })`, then call
`tour.start()`. It runs entirely in the browser; no backend service is
required.

Shepherd is not a native mobile (iOS/Android) tour library and is not an
analytics product — it renders and orchestrates the tour UI itself.

## Docs

- [Documentation](https://docs.shepherdjs.dev/): installation, guides, API reference
- [Homepage in markdown](https://www.shepherdjs.dev/index.md): overview, install, quick example
- [Pricing and licensing](https://www.shepherdjs.dev/pricing): free plan, commercial licenses

## Code

- [GitHub repository](https://github.com/shipshapecode/shepherd): source, issues, releases
- [npm package](https://www.npmjs.com/package/shepherd.js): shepherd.js

## Company

- [About](https://www.shepherdjs.dev/about): who maintains Shepherd
- [Contact](https://www.shepherdjs.dev/contact): email, GitHub, Discord
- [Privacy](https://www.shepherdjs.dev/privacy): privacy policy
- [Ship Shape](https://shipshape.io/): the consultancy that maintains Shepherd

## Optional

- [Blog](https://www.shepherdjs.dev/blog): announcements and articles
- [Sitemap](https://www.shepherdjs.dev/sitemap-index.xml)
56 changes: 56 additions & 0 deletions landing/src/pages/404.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
import Base from '@layouts/Base.astro';
import Header from '@components/Header.astro';
import Footer from '@components/Footer.astro';
import { SITE_DESCRIPTION } from '../consts';
---

<Base
overrideTitle="Page not found — Shepherd.js"
overrideDescription={SITE_DESCRIPTION}
>
<Header title="Page not found" />
<main class="flex justify-center w-full">
<div class="max-w-3xl my-12 px-4 text-center w-full">
<h1 class="font-heading text-4xl uppercase">404 — Page not found</h1>

<p class="font-body mt-6 text-xl">
The page you requested does not exist. Here is where to look instead:
</p>

<ul class="font-body mt-6 space-y-2 text-xl">
<li>
<a class="underline hover:text-navy-light" href="/">Homepage</a>
</li>
<li>
<a
class="underline hover:text-navy-light"
href="https://docs.shepherdjs.dev"
>
Documentation
</a>
</li>
<li>
<a class="underline hover:text-navy-light" href="/pricing">Pricing</a>
</li>
<li>
<a class="underline hover:text-navy-light" href="/blog">Blog</a>
</li>
<li>
<a class="underline hover:text-navy-light" href="/llms.txt">
llms.txt (guidance for AI agents)
</a>
</li>
<li>
<a class="underline hover:text-navy-light" href="/sitemap-index.xml">
Sitemap
</a>
</li>
<li>
<a class="underline hover:text-navy-light" href="/contact">Contact</a>
</li>
</ul>
</div>
</main>
<Footer />
</Base>
29 changes: 29 additions & 0 deletions landing/test/agent-recovery.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, expect, it } from 'vitest';

import { TEST_BASE_URL } from './setup/dev-server';

describe('404 handling', () => {
it('returns HTTP 404 with recovery links for nonexistent paths', async () => {
const response = await fetch(
`${TEST_BASE_URL}/some-path-that-does-not-exist`
);
const html = await response.text();

expect(response.status).toBe(404);
expect(html).toContain('/llms.txt');
expect(html).toContain('/sitemap-index.xml');
expect(html).toContain('https://docs.shepherdjs.dev');
});
});

describe('llms.txt', () => {
it('serves /llms.txt with when-to-use guidance', async () => {
const response = await fetch(`${TEST_BASE_URL}/llms.txt`);
const body = await response.text();

expect(response.status).toBe(200);
expect(body).toMatch(/^# Shepherd\.js/);
expect(body).toContain('## When to use Shepherd');
expect(body).toContain('https://docs.shepherdjs.dev/');
});
});
11 changes: 11 additions & 0 deletions landing/test/dist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,15 @@ describe.skipIf(!staticDir)('build output', () => {
expect(sitemap).toContain('<loc>https://www.shepherdjs.dev/contact/</loc>');
expect(sitemap).toContain('<loc>https://www.shepherdjs.dev/privacy/</loc>');
});

it('emits a 404 page with recovery links', () => {
const notFound = readFileSync(join(staticDir!, '404.html'), 'utf-8');

expect(notFound).toContain('/llms.txt');
expect(notFound).toContain('/sitemap-index.xml');
});

it('emits llms.txt', () => {
expect(existsSync(join(staticDir!, 'llms.txt'))).toBe(true);
});
});
Loading