-
-
Notifications
You must be signed in to change notification settings - Fork 658
feat: add agent-friendly 404 page and llms.txt #3500
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| --- | ||
| import Base from './Base.astro'; | ||
| import Header from '../components/Header.astro'; | ||
| import Footer from '../components/Footer.astro'; | ||
| import { SITE_TITLE } from '../consts'; | ||
|
|
||
| interface Props { | ||
| title: string; | ||
| description: string; | ||
| heading: string; | ||
| } | ||
|
|
||
| const { title, description, heading } = Astro.props; | ||
| --- | ||
|
|
||
| <Base overrideTitle={title} overrideDescription={description}> | ||
| <Header title={SITE_TITLE} /> | ||
| <main class="flex justify-center w-full"> | ||
| <div class="max-w-3xl my-12 px-4 w-full"> | ||
| <h1 class="font-heading text-4xl uppercase">{heading}</h1> | ||
| <div | ||
| class="content-page font-body mt-6 space-y-4 text-xl [&_a]:underline [&_a:hover]:text-navy-light [&_h2]:font-heading [&_h2]:mt-8 [&_h2]:text-2xl [&_h2]:uppercase [&_ul]:list-disc [&_ul]:pl-6" | ||
| > | ||
| <slot /> | ||
| </div> | ||
| </div> | ||
| </main> | ||
| <Footer /> | ||
| </Base> |
| 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> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| --- | ||
| import ContentPage from '@layouts/ContentPage.astro'; | ||
| --- | ||
|
|
||
| <ContentPage | ||
| title="About Shepherd.js" | ||
| description="What Shepherd.js is, who maintains it, and how it is licensed." | ||
| heading="About Shepherd" | ||
| > | ||
| <p> | ||
| Shepherd is an open-source JavaScript library for guiding users through your | ||
| app. It lets you build product tours, user onboarding flows, trainings, and | ||
| feature announcements as a sequence of steps — dialogs that attach to | ||
| elements in your interface, highlight them with a modal overlay, and walk | ||
| users through what to do next. Steps are positioned by | ||
| <a href="https://floating-ui.com/">Floating UI</a>, so they never end up off | ||
| screen or cropped by an overflow. | ||
| </p> | ||
|
|
||
| <p> | ||
| Shepherd ships with full keyboard navigation support, focus trapping, and | ||
| a11y compliance via aria attributes, and its minimal default styles make it | ||
| easy to match your product's look and feel. It works with React, Ember, | ||
| Angular, Vue.js, ES Modules, or plain JavaScript, and is used in production | ||
| by companies such as Google, Ally, and CodePen. | ||
| </p> | ||
|
|
||
| <h2>Who maintains Shepherd</h2> | ||
|
|
||
| <p> | ||
| Shepherd is maintained by <a href="https://shipshape.io">Ship Shape</a>, a | ||
| software consultancy specializing in web app development. Development | ||
| happens in the open on | ||
| <a href="https://github.com/shipshapecode/shepherd">GitHub</a>, where you | ||
| can report issues, propose changes, and follow releases. The library is | ||
| published to npm as | ||
| <a href="https://www.npmjs.com/package/shepherd.js">shepherd.js</a>. | ||
| </p> | ||
|
|
||
| <h2>Licensing</h2> | ||
|
|
||
| <p> | ||
| Shepherd is free for open-source, personal, and non-commercial projects | ||
| under the AGPL-3.0 license. Commercial licenses fund the ongoing maintenance | ||
| of the library, documentation, and community — see | ||
| <a href="/pricing">pricing</a> for details, or read the | ||
| <a href="https://docs.shepherdjs.dev/">documentation</a> to get started. | ||
| </p> | ||
| </ContentPage> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| --- | ||
| import ContentPage from '@layouts/ContentPage.astro'; | ||
| --- | ||
|
|
||
| <ContentPage | ||
| title="Contact — Shepherd.js" | ||
| description="How to reach the Shepherd.js team: email, GitHub, and Discord." | ||
| heading="Contact" | ||
| > | ||
| <p> | ||
| Shepherd is maintained by <a href="https://shipshape.io">Ship Shape</a>. | ||
| Whether you have a question about using the library, want to report a bug, | ||
| are interested in a commercial license, or would like help building tours | ||
| for your product, here is the best way to reach us: | ||
| </p> | ||
|
|
||
| <ul> | ||
| <li> | ||
| <strong>Email:</strong> | ||
| <a href="mailto:ahoy@shipshape.io">ahoy@shipshape.io</a> — licensing, consulting, | ||
| and general inquiries. This is the fastest route for commercial questions. | ||
| </li> | ||
| <li> | ||
| <strong>GitHub:</strong> | ||
| <a href="https://github.com/shipshapecode/shepherd/issues"> | ||
| github.com/shipshapecode/shepherd/issues | ||
| </a> — bug reports and feature requests for the library itself. | ||
| </li> | ||
| <li> | ||
| <strong>Discord:</strong> | ||
| <a href="https://discord.gg/EGcDW5NSud">join our Discord server</a> — community | ||
| chat and support from other Shepherd users and maintainers. | ||
| </li> | ||
| <li> | ||
| <strong>LinkedIn:</strong> | ||
| <a href="https://www.linkedin.com/company/ship-shape/">Ship Shape</a> — company | ||
| updates. | ||
| </li> | ||
| </ul> | ||
|
|
||
| <p> | ||
| For documentation, guides, and the full API reference, visit | ||
| <a href="https://docs.shepherdjs.dev/">docs.shepherdjs.dev</a>. For | ||
| licensing tiers and purchasing a commercial license, see | ||
| <a href="/pricing">pricing</a>. | ||
| </p> | ||
| </ContentPage> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| --- | ||
| import ContentPage from '@layouts/ContentPage.astro'; | ||
| --- | ||
|
|
||
| <ContentPage | ||
| title="Privacy Policy — Shepherd.js" | ||
| description="How the shepherdjs.dev website handles analytics, payments, and personal data." | ||
| heading="Privacy Policy" | ||
| > | ||
| <p> | ||
| This policy describes how the shepherdjs.dev website, operated by | ||
| <a href="https://shipshape.io">Ship Shape</a>, handles data. It applies to | ||
| this website only — not to applications that embed the Shepherd library. | ||
| </p> | ||
|
|
||
| <h2>What we collect</h2> | ||
|
|
||
| <p> | ||
| shepherdjs.dev is a static marketing and documentation site. We do not | ||
| require accounts, and we do not collect personal information to browse the | ||
| site. We use <a href="https://posthog.com/">PostHog</a> for product analytics, | ||
| which records anonymous usage data such as pages viewed, referring site, and browser | ||
| and device type. This helps us understand which parts of the site and documentation | ||
| are useful. PostHog may use cookies or local storage to distinguish visitors; | ||
| data is processed on PostHog's US cloud. | ||
|
Comment on lines
+21
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🛡️ Detected with Advanced Tier | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- candidate references ---'
rg -n -i --glob '!node_modules' --glob '!dist' --glob '!build' 'posthog|do.?not.?track|navigator\.doNotTrack|analytics|telemetry|cookie|localStorage' landing/src landing/package.json landing/astro.config.* 2>/dev/null | head -240
printf '%s\n' '--- relevant files ---'
git ls-files 'landing/src/*' 'landing/*' | rg -i 'privacy|layout|posthog|analytics|cookie|consent|script'Repository: shipshapecode/shepherd Length of output: 4742 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- landing/src/components/Posthog.astro ---'
cat -n landing/src/components/Posthog.astro
printf '%s\n' '--- landing/src/pages/privacy.astro (relevant sections) ---'
sed -n '1,80p' landing/src/pages/privacy.astro | cat -n
printf '%s\n' '--- BaseHead inclusion ---'
sed -n '1,18p;155,182p' landing/src/components/BaseHead.astro | cat -nRepository: shipshapecode/shepherd Length of output: 6679 🌐 Web query:
💡 Result: Inspection citation: inspection_e05d8521d97911cc27d9b5f7d73e9d2b <source_evidence> Citations:
Security Misconfiguration Reachability: External Honor Do Not Track and limit PostHog’s default capture.
Limit collection in the initializerdiff --git a/landing/src/components/Posthog.astro b/landing/src/components/Posthog.astro
@@
posthog.init('phc_sl7TroBwU2fA7dJVU70ZV5u0575fQNWYv1GK5enODkX', {
- api_host: 'https://us.i.posthog.com'
+ api_host: 'https://us.i.posthog.com',
+ respect_dnt: true,
+ autocapture: false,
+ capture_pageleave: false,
+ disable_session_recording: true
});🤖 Prompt for AI Agents |
||
| </p> | ||
|
|
||
| <h2>Payments</h2> | ||
|
|
||
| <p> | ||
| Commercial license purchases are processed by | ||
| <a href="https://polar.sh/">Polar</a>, our checkout provider. Your payment | ||
| details are entered on and processed by Polar — they are never sent to or | ||
| stored on shepherdjs.dev. Polar shares with us only the information needed | ||
| to fulfill your license, such as your name, email address, and order | ||
| details. | ||
| </p> | ||
|
|
||
| <h2>The Shepherd library</h2> | ||
|
|
||
| <p> | ||
| The Shepherd JavaScript library itself, installed in your own applications, | ||
| does not collect, transmit, or store any data about your users. It runs | ||
| entirely in the browser of the application that embeds it. | ||
| </p> | ||
|
|
||
| <h2>Your choices and contact</h2> | ||
|
|
||
| <p> | ||
| You can block analytics with standard browser tooling (content blockers, Do | ||
| Not Track) without affecting your ability to use this site. If you have | ||
| questions about this policy, or want data we may hold about you (such as | ||
| order records) accessed or deleted, email | ||
| <a href="mailto:ahoy@shipshape.io">ahoy@shipshape.io</a>. We will update | ||
| this page if our practices change. | ||
| </p> | ||
| </ContentPage> | ||
| 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/'); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { describe, expect, it } from 'vitest'; | ||
|
|
||
| import { visibleText } from './helpers'; | ||
| import { TEST_BASE_URL } from './setup/dev-server'; | ||
|
|
||
| describe('trust anchor pages', () => { | ||
| for (const path of ['/about', '/contact', '/privacy']) { | ||
| it(`serves ${path} with at least 500 characters of content`, async () => { | ||
| const response = await fetch(`${TEST_BASE_URL}${path}`); | ||
| const html = await response.text(); | ||
|
|
||
| expect(response.status).toBe(200); | ||
| expect(html).toMatch(/<h1[\s>]/); | ||
| expect(visibleText(html).length).toBeGreaterThan(500); | ||
| }); | ||
| } | ||
|
|
||
| it('links the trust pages from the footer', async () => { | ||
| const response = await fetch(`${TEST_BASE_URL}/`, { | ||
| headers: { Accept: 'text/html' } | ||
| }); | ||
| const html = await response.text(); | ||
|
|
||
| expect(html).toContain('href="/about"'); | ||
| expect(html).toContain('href="/contact"'); | ||
| expect(html).toContain('href="/privacy"'); | ||
| }); | ||
| }); |
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.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: shipshapecode/shepherd
Length of output: 2774
🏁 Script executed:
Repository: shipshapecode/shepherd
Length of output: 11482
Import the ESM default export before constructing the tour.
Loading
shepherd.mjsas a module does not create a globalShepherdbinding. The browser example must import the default export in the module that creates the tour.Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents