Skip to content
HagiCode-orgPublic

About

Shared Astro components and Starlight plugin for HagiCode documentation sites — localized footer, header, locale chooser, copyright, promotion banner, analytics, and AI-disclosure support.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

HagiLight

npm @hagicode/hagilight npm @hagicode/hagilight-core npm @hagicode/hagilight-starlight npm @hagicode/hagilight-docusaurus

CI Publish npm GitHub release Last commit TypeScript Astro npm provenance Core demo Starlight demo Docusaurus demo

Reusable Astro components, integrations, a Starlight plugin, and a Docusaurus plugin for HagiCode sites. This repository publishes four npm packages, all authored in TypeScript and shipped as ESM with generated .d.ts declarations. Astro components, Astro route entry points, styles, JSON, and images are shipped in their native formats.

flowchart LR
  A["@hagicode/hagilight"] --> C["@hagicode/hagilight-core"]
  S["@hagicode/hagilight-starlight"] --> C
  D["@hagicode/hagilight-docusaurus"] --> C
Loading

Every feature package depends on the matching version of the shared core and never on another feature package. Install core directly when your site imports a core entry point.

Package npm Downloads
@hagicode/hagilight-core npm downloads
@hagicode/hagilight npm downloads
@hagicode/hagilight-starlight npm downloads
@hagicode/hagilight-docusaurus npm downloads

@hagicode/hagilight-core

Shared building blocks for plain Astro, Starlight, and Docusaurus sites. Its Astro components require astro >=7; it has no Starlight dependency.

Import Provides
@hagicode/hagilight-core/links resolveSiteLinks() and the localized header/footer link catalog types
@hagicode/hagilight-core/favicon resolveFaviconHeadEntry() and getHagilightFaviconDataUri()
@hagicode/hagilight-core/seo Metadata, canonical head composition, and JSON-LD builders
@hagicode/hagilight-core/seo-schema seoSchema for optional seo frontmatter
@hagicode/hagilight-core/rss generateRssFeed() and resolveRssLocales()
@hagicode/hagilight-core/rss-ownership Coordination that keeps one integration as the RSS route owner
@hagicode/hagilight-core/promotions Typed promotion campaign loader
@hagicode/hagilight-core/promoto-banner definePromotoBannerElement(), the promotion banner custom element used by the Docusaurus package
@hagicode/hagilight-core/analytics-events Google Analytics click-event tags (gaEventAttributes(), siteLinkGaAttributes()) and the tracked-link table
@hagicode/hagilight-core/Footer Localized footer with generated RSS and sitemap links
@hagicode/hagilight-core/Copyright, /PromotoBanner, /GoogleAnalytics, /Analytics51LA Astro components
@hagicode/hagilight-core/logo.png, /favicon.ico Brand assets

@hagicode/hagilight

Plain Astro integrations and components (astro >=7):

Import Provides
@hagicode/hagilight/integration hagilight() (sitemap, robots.txt, and RSS feeds by default), hagilightFavicon(), and their option types
@hagicode/hagilight/SEOHead Canonical, Open Graph, Twitter, and JSON-LD head entries
@hagicode/hagilight/Footer Shared footer with Powered By hagilight@<version> attribution

The plain-Astro Footer reads the exact build-time version from its own @hagicode/hagilight package manifest; it does not report an astro framework version. Existing sites that import @hagicode/hagilight-core/Footer can keep that core-only footer (with no feature-package attribution) or switch to @hagicode/hagilight/Footer to display the Hagilight version.

@hagicode/hagilight-starlight

A Starlight plugin (@astrojs/starlight ^0.42.4, astro ^7.3.5) that adds the localized header, language chooser, and footer links; content-width toggle; custom 404 page; end-of-article HagiCode introduction; floating promotion banner; AI translation or authorship disclosures; SEO metadata and multilingual page discovery; RSS feeds; Google Analytics and 51LA; and the shared favicon. Its Footer appends Powered By hagilight-starlight@<version> using the exact build-time version from the @hagicode/hagilight-starlight manifest, not the Astro or Starlight dependency version.

// astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import hagilight from '@hagicode/hagilight-starlight';
import { locales } from '@hagicode/hagilight-starlight/locales';

export default defineConfig({
  site: 'https://docs.example.test',
  integrations: [starlight({ title: 'Docs', locales, plugins: [hagilight({ rss: { includeDocs: false } })] })],
});
// src/content.config.ts
import { defineCollection } from 'astro:content';
import { docsLoader } from '@astrojs/starlight/loaders';
import { docsSchema } from '@astrojs/starlight/schema';
import { hagilightSchema } from '@hagicode/hagilight-starlight/schema';

export const collections = { docs: defineCollection({ loader: docsLoader(), schema: docsSchema({ extend: hagilightSchema }) }) };

HagilightStarlightOptions types every plugin option, so misspelled or mistyped options fail type-checking in astro.config.ts or JSDoc-checked configs; the plugin still validates options at runtime. Components such as @hagicode/hagilight-starlight/Header and /MarkdownContent remain importable for sites that compose their own Starlight overrides.

@hagicode/hagilight-docusaurus

A Docusaurus 3 plugin that adds the Hagilight footer, promotion banner, analytics, SEO metadata, and discovery files (sitemap.xml, robots.txt, rss.xml) to a Docusaurus site. It peers on @docusaurus/core and @docusaurus/theme-classic ^3.9.2, depends only on the shared core, and exports the locale catalog from @hagicode/hagilight-docusaurus/locales.

// docusaurus.config.mjs
import { hagilightDocusaurus } from '@hagicode/hagilight-docusaurus';
import { i18n } from '@hagicode/hagilight-docusaurus/locales';

export default {
  title: 'My docs',
  url: 'https://docs.example.com',
  baseUrl: '/',
  i18n,
  plugins: [
    hagilightDocusaurus({
      analytics: {
        googleAnalytics: { measurementId: 'G-XXXXXXXXXX' },
        fiftyOneLa: { enabled: false },
      },
    }),
  ],
  themeConfig: {
    navbar: { items: [{ type: 'localeDropdown' }] },
  },
};

The built-in analytics IDs belong to HagiCode, so set your own IDs or disable each provider. Every option, its default, and the sitemap, robots.txt, and RSS rules are in the package README. The demo in examples/demo-docusaurus-web is published at https://docusaurus.hagicode.com/.

First-party links and campaign tags

Shared links classify each destination as same-origin, first-party (hagicode.com and its subdomains, plus github.com/HagiCode-org), or third-party. First-party and same-origin new-tab links use rel="noopener"; third-party links keep rel="noopener noreferrer". Setting links.siteId adds utm_source=<siteId>&utm_medium=footer (or promoto for the banner) to first-party hagicode.com footer and banner destinations, after deduplication and never on GitHub, third-party, or same-origin links. The Google Analytics cross-domain linker is not configured, because every first-party host that runs the shared gtag shares the hagicode.com cookie domain. The full rules, options and the linker decision are in the core package README.

Google Analytics click events

GoogleAnalytics (included by the Starlight plugin) reports clicks on key links and download buttons as GA4 events, with no configuration. Events are sent only on production pages where Google Analytics is enabled, never on the 404 page, and are skipped when gtag is missing.

  • Event name (action): download_click for the download category, store_visit for store, and link_click for navigation, community, and promotion.
  • Categories: use download for installers and download-hub entry points, and store for store listing pages (Microsoft Store, Steam). A single click reports under one category only.
  • Parameters: event_category, event_label (the link's stable id, not its localized text), link_location (header, footer, article_promotion, promoto_banner, or your own value), link_url, and transport_type: 'beacon'. No link text, form values, or visitor identifiers are sent.
  • Tracked links: header home, blog, support; footer downloadClient (download), microsoftStore (store), dockerCompose, productDocs, blogPosts (navigation), github, discord, issueFeedback (community); the HagiCode showcase calls to action, Microsoft Store badge (store, and its fallback link), and all-downloads link (download); and the floating promotion banner call to action, labelled with the promotion id. TRACKED_SITE_LINKS in packages/core/src/analytics-events.ts is authoritative; renaming a link id changes its GA label. Related sites, RSS, sitemap, about, contact, filings, and extraLinks are not tracked.
  • Your own links: spread gaEventAttributes({ category, label, location }) from @hagicode/hagilight-core/analytics-events onto an anchor. Use category: 'store' for a store listing and category: 'download' for an installer. Unknown categories (including action names such as store_visit) throw at build time and are ignored at click time.
  • Reports: register event_category, event_label, link_location, and link_url as GA4 custom dimensions to see them in standard reports.

See the core package README for the full contract.

TypeScript usage

Every JavaScript entry point has a types export condition, so TypeScript resolves declarations with moduleResolution NodeNext, Node16, or Bundler without application-level module declarations. For example, type the module passed to hagilight({ rss: { getFeed } }):

// src/rss-feed.ts
import type { RssFeedCallback } from '@hagicode/hagilight/integration';

const getFeed: RssFeedCallback = async ({ lang }) => ({
  title: 'Updates',
  description: `Updates in ${lang}`,
  items: [{ title: 'Release notes', link: '/blog/release/', date: new Date() }],
});
export default getFeed;

Local development

Run commands from the repository root:

npm install
npm run build              # tsc -b: core, then the Docusaurus, plain Astro, and Starlight packages
npm run typecheck          # build, check Astro route entry points, and check test/types fixtures
npm test                   # builds first, then runs node --test
npm run build:example      # builds packages, the core-footer demo, and the Starlight demo variants, then verifies their output (the Docusaurus demo is built and checked by npm test)
npm run build:starlight-example  # builds only the default Starlight demo, without the verification variants
npm run pack:check         # verifies all four tarballs, exports, and version alignment
npm run integration:installed  # installs tarballs into isolated core, Docusaurus, Astro, and Starlight consumers

Sources live in packages/*/src/*.ts and compile to the ignored packages/*/dist/ directories; .astro components and route files import the built dist/*.js modules, so build before running examples from a clean checkout. The release workflow stamps one version into all four packages and the core dependency of every feature package (scripts/release.mjs stamp), then scripts/publish.mjs publishes core first, followed by the Docusaurus, plain-Astro, and Starlight packages.

Sitemap and robots.txt for plain Astro

Register the plain-Astro integration in astro.config.mjs (importing a component alone cannot register an Astro integration):

import { defineConfig } from 'astro/config';
import { hagilight } from '@hagicode/hagilight/integration';

export default defineConfig({
  site: 'https://example.test',
  integrations: [hagilight()],
});

With an absolute HTTP(S) site, Astro's sitemap integration generates sitemap-index.xml and Hagilight generates robots.txt with an absolute Sitemap: URL to that index. For base: '/manual/', verify https://example.test/manual/sitemap-index.xml and the sitemap entries under https://example.test/manual/; the localized Footer sitemap link uses that same base-aware path. Set hagilight({ enabled: false }) to disable both generated outputs. An existing @astrojs/sitemap integration or Starlight owns sitemap generation instead; a consumer-owned public/robots.txt or src/pages/robots.txt.* remains authoritative.

If you need a site-specific crawl policy, own public/robots.txt and include the absolute URL of the deployed sitemap index:

User-agent: *
Allow: /
Disallow: /private/
Sitemap: https://example.test/manual/sitemap-index.xml

Hagilight does not generate llms.txt. To publish AI discovery links, add your own public/llms.txt with only the localized pages you want to expose:

# Example documentation

- [English](https://example.test/manual/)
- [简体中文](https://example.test/manual/zh-CN/)

If you do not want AI discovery, do not add an llms.txt file. Deployments under a base path must still serve /robots.txt at the origin root, because crawlers look for that exact URL.

Localized RSS for plain Astro

hagilight() from @hagicode/hagilight/integration generates localized RSS feeds by default, so integrating it is enough to publish /rss.xml and /rss.<language>.xml. Sites can keep owning RSS routes and call generateRssFeed from @hagicode/hagilight-core/rss, or customize the generated feeds with the rss option:

import { defineConfig } from 'astro/config';
import { hagilight } from '@hagicode/hagilight/integration';

export default defineConfig({
  site: 'https://example.test',
  integrations: [
    hagilight({
      rss: { getFeed: './src/rss-feed.ts' },
    }),
  ],
});

When rss is omitted (the default) or rss: true, the integration derives the feed locales from the Astro i18n config and serves a built-in empty feed. Pass rss: false to disable RSS entirely. The getFeed path is resolved relative to the Astro project root. Its module must default-export an RssFeedCallback that receives { route, lang } for each configured locale and returns { title, description, items }. Items use the same title, link, optional description, and optional date/pubDate shape accepted by generateRssFeed. The integration requires an absolute HTTP(S) site URL and a valid callback module and result when getFeed is supplied.

The integration prerenders /rss.xml, /rss.en.xml, and /rss.<language>.xml for each configured non-English language. /rss.xml and /rss.en.xml use the configured English callback result. If no English locale is configured, both are valid empty English feeds; Hagilight does not invoke a different language's callback or borrow its content. A generated filename conflicting with a page or public file fails the build rather than replacing consumer-owned output. Setting rss: false leaves route and Footer behavior unchanged.

Generated route and item URLs honor Astro's base, for example base: '/manual/' generates /manual/rss.xml and resolves relative item links under https://example.test/manual/. The core Footer gets the default feed link and, on configured non-English pages, a current-language link from request-local integration context. Explicit links.rssFeedUrl, links.rssLocaleFeedUrl, link overrides, and existing removal options remain authoritative.

When both Hagilight integrations are active, Starlight remains the sole RSS owner if its RSS generation is enabled; the plain-Astro hagilight() then adds neither routes nor Footer URLs. Both packages coordinate through @hagicode/hagilight-core/rss-ownership. Having both packages installed does not enable either integration. If the Starlight integration is present with RSS explicitly disabled, the plain-Astro hagilight() owns the RSS routes instead.

Desktop viewport regression baseline

Install the Chromium browser once with npx playwright install chromium, then run npm run test:viewport from the Hagilight repository root. The command builds and serves both example sites locally before checking these routes at 1536 x 864, 1920 x 1080, and 2560 x 1440 CSS viewport pixels:

Example Routes Layout states
Core Astro /, /zh-CN/ Header actions, bounded feature cards, internal code-block scrolling, and document overflow
Starlight /zh-CN/, /zh-Hant/ Header, sidebar, populated page outline, narrow and wide reading widths, and the open language chooser

The suite blocks external requests, disables optional analytics only for its test build, waits for fonts, and disables animation. On failure it reports the route, viewport, and affected region, then saves a screenshot; CI retains these under .ci-artifacts/viewport/.

These dimensions are Chromium CSS viewport pixels at device scale factor 1. They do not emulate macOS display scaling, physical device pixels, Safari, or other browser typography; a real-device/browser pass remains complementary.

About

Shared Astro components and Starlight plugin for HagiCode documentation sites — localized footer, header, locale chooser, copyright, promotion banner, analytics, and AI-disclosure support.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages