site-config.ts3.3 KBView on GitHub
const TITLE = 'Cedar — The AI-native workspace for mid-funnel sales';
const DESCRIPTION =
  'Cedar analyses your sales calls, email and CRM to map out a winning playbook — then drafts the context-aware follow-ups that keep every deal moving.';

/**
 * Origin the marketing pages present as canonical to search engines.
 *
 * Defaults to the app origin so nothing changes until the marketing site actually
 * answers on its own domain — pointing `rel=canonical` at a host that 301s straight
 * back here would be a canonical loop. Set VITE_PUBLIC_MARKETING_URL to
 * https://cedarcopilot.com in the frontend build once apex DNS is cut over.
 */
export const CANONICAL_ORIGIN: string = (import.meta.env.VITE_PUBLIC_MARKETING_URL ||
  import.meta.env.VITE_PUBLIC_APP_URL) as string;

/**
 * Absolute canonical URL for a path on the marketing origin.
 *
 * Root's `meta` is what every route without its own `meta` export inherits — React Router
 * does not merge meta up the tree — so a canonical fixed at the bare origin would tell
 * crawlers that `/pricing`, `/legal/*`, `/roadmap` and `/hr` are all duplicates of the
 * homepage. Build it from the path the visitor is actually on.
 */
export function canonicalUrl(pathname: string): string {
  try {
    return new URL(pathname, CANONICAL_ORIGIN).toString();
  } catch {
    return CANONICAL_ORIGIN;
  }
}

export const siteConfig = {
  title: TITLE,
  description: DESCRIPTION,
  applicationName: 'Cedar',
  openGraph: {
    title: TITLE,
    description: DESCRIPTION,
    images: [
      {
        url: `${import.meta.env.VITE_PUBLIC_APP_URL}/og.png`,
        width: 1200,
        height: 630,
        alt: TITLE,
      },
    ],
  },
  alternates: {
    canonical: CANONICAL_ORIGIN,
  },
};

type FaviconLink = {
  tagName: 'link';
  rel: string;
  href: string;
  type?: string;
  sizes?: string;
};

/**
 * The site-wide tab/bookmark icons.
 *
 * These are meta descriptors rather than static `<link>` tags in root's `<head>` so that a route
 * with its own `meta` export still gets them — React Router replaces a route's meta wholesale
 * instead of merging it, so anything a route needs has to be in its own return value.
 *
 * `apple-touch-icon` is what iOS uses when the page is saved to the home screen.
 */
export function faviconLinks(): FaviconLink[] {
  return [
    {
      tagName: 'link',
      rel: 'icon',
      type: 'image/png',
      sizes: '32x32',
      href: '/icons/favicon-32x32.png',
    },
    {
      tagName: 'link',
      rel: 'icon',
      type: 'image/png',
      sizes: '16x16',
      href: '/icons/favicon-16x16.png',
    },
    { tagName: 'link', rel: 'icon', href: '/favicon.ico', sizes: 'any' },
    { tagName: 'link', rel: 'apple-touch-icon', href: '/icons/apple-touch-icon.png' },
  ];
}

/** Days the calendar icons cover — one file per day of the month, as Google Calendar does. */
export const CALENDAR_ICON_DAYS = 31;

/**
 * The calendar tab icon showing `day`, generated by scripts/generate-calendar-favicons.mjs.
 *
 * Out-of-range days clamp rather than 404, so a bad clock or a caller doing its own date maths
 * still gets an icon.
 */
export function calendarDayIconHref(day: number): string {
  const clamped = Math.min(Math.max(Math.trunc(day) || 1, 1), CALENDAR_ICON_DAYS);
  return `/icons/calendar/day-${clamped}.svg`;
}