Skip to content
Merged
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
14 changes: 7 additions & 7 deletions kit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const returnTo = safeReturnTo(url.searchParams.get("return_to"));

// Build absolute login URL (bypasses React Router basename prepend)
const loginUrl = buildLoginRedirect(returnTo, url.origin);
// → "https://ampl.tools/auth/login?return_to=%2Fpalaeography"
// → "https://ampl.tools/auth/login?return_to=%2Fpaleography"
```

### Non-refresh note
Expand Down Expand Up @@ -118,26 +118,26 @@ import { AmplHeader, DEFAULT_TOOLS } from "@ampl/kit/ui";
// Compact signed-in header (standard in-app use)
<AmplHeader
tool="calamus"
toolName="Palaeography"
toolName="Paleography"
localeSwitcher={<LocaleSwitcher variant="on-dark" buildHref={buildHref} current={locale} />}
Comment on lines 118 to 122
account={{ name: user.name, avatarUrl: user.avatar_url }}
nav={[
{ label: "Manuscripts", href: "/palaeography/manuscripts", active: true },
{ label: "Groups", href: "/palaeography/groups" },
{ label: "Manuscripts", href: "/paleography/manuscripts", active: true },
{ label: "Groups", href: "/paleography/groups" },
]}
/>

// Full signed-out front door (size="full" only on the front door)
<AmplHeader
tool="calamus"
toolName="Palaeography"
toolName="Paleography"
size="full"
localeSwitcher={<LocaleSwitcher variant="on-dark" buildHref={buildHref} current={locale} />}
signInHref="/auth/login?return_to=/palaeography"
signInHref="/auth/login?return_to=/paleography"
/>
```

The tool switcher defaults to `DEFAULT_TOOLS` (Palaeography + Scheduling); pass
The tool switcher defaults to `DEFAULT_TOOLS` (Paleography + Scheduling); pass
a custom `tools` array to override. `SiteHeader` is deprecated as of v0.3.0 in
favour of `AmplHeader`. See `CONSUMING.md` for the full prop reference and CSP
requirements.
10 changes: 5 additions & 5 deletions kit/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export async function validateSession(
* - backslash (handles `\\evil` and `/path\\evil`)
* - embedded schemes (javascript:, http://, etc.) via URL parser
*
* NOTE: returns the full apex pathname (e.g. /palaeography/x)
* NOTE: returns the full apex pathname (e.g. /paleography/x)
* — NOT basename-stripped. The callback's absolute-URL redirect handles
* routing without double-prefix.
*/
Expand All @@ -158,8 +158,8 @@ export function safeReturnTo(value: string | null): string {
* prepend. Consumer tools call this to redirect unauthenticated users.
*
* Example:
* buildLoginRedirect("/palaeography", "https://ampl.tools")
* // → "https://ampl.tools/auth/login?return_to=%2Fpalaeography"
* buildLoginRedirect("/paleography", "https://ampl.tools")
* // → "https://ampl.tools/auth/login?return_to=%2Fpaleography"
*
* The `returnTo` value should already be validated by `safeReturnTo`.
*/
Expand All @@ -184,8 +184,8 @@ export function buildLoginRedirect(
* basename, same absolute URL output via `new URL(...)`.
*
* Example:
* buildLogoutHref("/palaeography", "https://ampl.tools")
* // → "https://ampl.tools/auth/logout?return_to=%2Fpalaeography"
* buildLogoutHref("/paleography", "https://ampl.tools")
* // → "https://ampl.tools/auth/logout?return_to=%2Fpaleography"
*
* The `returnTo` value should already be validated by `safeReturnTo`.
*/
Expand Down
14 changes: 10 additions & 4 deletions kit/ui/ampl-header/AmplHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
* sheet; holds the single mobile-sheet open/close state. All tool variation
* comes through props. The public entry point for the component.
*
* @version v0.3.0
* v0.3.2: the cross-tool switcher moved from the WORKSHOP band into the
* institutional band; the mobile sheet gains a "switch tool" section.
*
* @version v0.3.2
Comment on lines +8 to +11
*/
import { useState } from "react";
import { InstitutionalBand } from "./InstitutionalBand";
Expand Down Expand Up @@ -43,17 +46,17 @@ export function AmplHeader({
labHome={labHome}
menuOpen={menuOpen}
onMenuToggle={() => setMenuOpen((v) => !v)}
/>
<WorkshopBand
tool={tool}
toolName={toolName}
tools={tools}
/>
<WorkshopBand
nav={nav}
context={context}
localeSwitcher={localeSwitcher}
account={account}
signInHref={signInHref}
signInLabel={signInLabel}
tools={tools}
/>
<MobileSheet
open={menuOpen}
Expand All @@ -63,6 +66,9 @@ export function AmplHeader({
account={account}
signInHref={signInHref}
signInLabel={signInLabel}
tool={tool}
toolName={toolName}
tools={tools}
/>
</header>
);
Expand Down
26 changes: 20 additions & 6 deletions kit/ui/ampl-header/InstitutionalBand.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
/**
* Institutional band — the white top band: AMPL logo lockup (links to the lab
* home) + lab-site nav, scaling between full and compact; plus the mobile
* hamburger that toggles the sheet. Owned by the kit (single AMPL identity).
* home) + the cross-tool WORKSHOP switcher + lab-site nav, scaling between full
* and compact; plus the mobile hamburger that toggles the sheet. Owned by the
* kit (single AMPL identity).
*
* @version v0.3.0
* v0.3.2: the WORKSHOP tool switcher moved here (first nav item) from the plum
* band, so its dropdown opens at page-top with nothing below to clip it.
*
* @version v0.3.2
*/
import { useTranslation } from "react-i18next";
import amplLogo from "../../assets/ampl-logo.svg";
import { LAB_NAV } from "./lab-nav";
import type { HeaderSize } from "./types";
import { ToolSwitcher } from "./ToolSwitcher";
import type { HeaderSize, ToolId, ToolLink } from "./types";

export function InstitutionalBand({
size,
labHome,
menuOpen,
onMenuToggle,
tool,
toolName,
tools,
}: {
size: HeaderSize;
labHome: string;
menuOpen: boolean;
onMenuToggle: () => void;
/** v0.3.2: the cross-tool switcher now lives here, as the first nav item. */
tool: ToolId;
toolName: string;
tools: ToolLink[];
}) {
const { t } = useTranslation("kit");
const full = size === "full";
Expand All @@ -39,13 +51,15 @@ export function InstitutionalBand({
<img src={amplLogo} alt="" className={`block h-auto ${full ? "w-[220px]" : "w-[132px]"}`} />
</a>

{/* Lab nav — desktop only; mobile uses the sheet. */}
{/* Switcher + lab nav — desktop only; mobile uses the sheet. The
WORKSHOP switcher is first (left of the lab links). */}
<nav
aria-label={t("nav.ariaLabel")}
className={`hidden flex-wrap items-center justify-end md:flex ${
full ? "max-w-[480px] gap-x-8 gap-y-1.5" : "max-w-[420px] gap-x-[26px] gap-y-1"
full ? "max-w-[620px] gap-x-8 gap-y-1.5" : "max-w-[540px] gap-x-[26px] gap-y-1"
}`}
>
<ToolSwitcher tool={tool} toolName={toolName} tools={tools} size={size} />
{LAB_NAV.map((item) => (
<a
key={item.key}
Expand Down
44 changes: 39 additions & 5 deletions kit/ui/ampl-header/MobileSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
/**
* Mobile sheet — the full-width hamburger-toggled panel for narrow screens:
* lab nav + tool nav + EN/ES + account/sign-in. Always in the DOM; visibility
* via the `hidden`/`aria-hidden` toggle so it is SSR-present and tab-order-correct.
* workshop switcher + lab nav + tool nav + EN/ES + account/sign-in. Always in
* the DOM; visibility via the `hidden`/`aria-hidden` toggle so it is SSR-present
* and tab-order-correct.
*
* @version v0.3.0
* v0.3.2: gains a "switch tool" section (current + other tools), since the
* desktop switcher now lives in the institutional band which collapses here.
*
* @version v0.3.2
*/
import type { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { buildSignOutAction } from "../lib/sign-out";
import { LAB_NAV } from "./lab-nav";
import type { AccountInfo, NavItem } from "./types";
import type { AccountInfo, NavItem, ToolId, ToolLink } from "./types";

export function MobileSheet({
open,
Expand All @@ -19,6 +23,9 @@ export function MobileSheet({
account,
signInHref,
signInLabel,
tool,
toolName,
tools,
}: {
open: boolean;
nav?: NavItem[];
Expand All @@ -27,11 +34,16 @@ export function MobileSheet({
account?: AccountInfo | null;
signInHref?: string;
signInLabel?: ReactNode;
tool: ToolId;
toolName: string;
tools: ToolLink[];
}) {
const { t } = useTranslation("kit");
const signOutAction = account
? buildSignOutAction(account.signOutHref ?? "/auth/logout", account.returnTo)
: "";
const currentTool = tools.find((tl) => tl.id === tool);
const otherTools = tools.filter((tl) => tl.id !== tool);

return (
<div
Expand All @@ -40,8 +52,30 @@ export function MobileSheet({
aria-hidden={!open}
className="border-t border-white/20 bg-accent-deep px-[30px] py-5 md:hidden"
>
{/* Workshop switcher — current tool + switch-to links */}
<div className="flex flex-col gap-1.5 pb-5">
<span className="font-display text-[8px] uppercase tracking-[1px] text-accent-pale">
{t("switcher.current")}
</span>
<span className="font-title text-[18px] font-medium text-white">
{currentTool?.name ?? toolName}
</span>
{otherTools.length > 0 && (
<>
<span className="mt-2 font-display text-[8px] uppercase tracking-[1px] text-white/60">
{t("switcher.switchTo")}
</span>
{otherTools.map((tl) => (
<a key={tl.id} href={tl.href} className="font-title text-[16px] font-medium text-white no-underline">
{tl.name}
</a>
))}
</>
)}
</div>

{/* Lab nav */}
<nav aria-label={t("nav.ariaLabel")} className="flex flex-col gap-3">
<nav aria-label={t("nav.ariaLabel")} className="flex flex-col gap-3 border-t border-white/20 pt-5">
{LAB_NAV.map((item) => (
<a key={item.key} href={item.href} className="font-title text-[18px] uppercase tracking-[0.5px] text-white no-underline">
{t(`nav.${item.key}`)}
Expand Down
30 changes: 19 additions & 11 deletions kit/ui/ampl-header/ToolSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,47 @@
/**
* Tool switcher — the `Workshop · {tool} ▾` cluster on the WORKSHOP band, a
* native <details> disclosure. The panel surfaces the current tool in a banner,
* then lists the other AMPL tools you can switch to (current-banner + others).
* Tool switcher — the `WORKSHOP ▾` cluster, a native <details> disclosure. Lives
* in the white INSTITUTIONAL band as the first nav item (v0.3.2 relocation), so
* the trigger is styled like the lab-nav links (dark, uppercase title) and scales
* with the band `size`. The panel itself is the dark plum popover: the current
* tool in a banner, then the other AMPL tools you can switch to.
*
* @version v0.3.1
* @version v0.3.2
*/
import { useTranslation } from "react-i18next";
import type { ToolId, ToolLink } from "./types";
import type { HeaderSize, ToolId, ToolLink } from "./types";

export function ToolSwitcher({
tool,
toolName,
tools,
size = "compact",
}: {
tool: ToolId;
toolName: string;
tools: ToolLink[];
/** Institutional-band scale — matches the lab-nav link sizing. */
size?: HeaderSize;
}) {
const { t } = useTranslation("kit");
const current = tools.find((tl) => tl.id === tool);
const others = tools.filter((tl) => tl.id !== tool);
const full = size === "full";

return (
<details className="relative [&_summary::-webkit-details-marker]:hidden">
<summary
aria-haspopup="true"
className="flex cursor-pointer list-none items-center gap-[11px] whitespace-nowrap"
className={`flex cursor-pointer list-none items-center gap-1.5 whitespace-nowrap font-title font-medium uppercase leading-none tracking-[0.5px] text-fg-1 no-underline hover:text-accent ${
full ? "text-[22px]" : "text-[15px]"
}`}
>
<span className="font-display text-[12px] uppercase tracking-[1.5px] text-accent-pale">Workshop</span>
<span className="text-white/20" aria-hidden>·</span>
<span className="font-title text-[20px] font-medium tracking-[-0.2px] text-white">{toolName}</span>
<span className="ml-0.5 text-[11px] text-white/60" aria-hidden>▾</span>
{/* Literal "Workshop" (as the pre-v0.3.2 trigger was) — ES branding of
this label is a pending decision, deliberately not invented here. */}
Workshop
<span className="text-[0.7em] text-fg-3" aria-hidden>▾</span>
</summary>

<div className="absolute left-0 top-full z-20 w-64 border border-white/20 bg-accent-deepest">
<div className="absolute left-0 top-full z-30 mt-2 w-64 border border-white/20 bg-accent-deepest">
{/* Current tool — where you are now */}
<div aria-current="true" className="border-b border-white/20 bg-black/20 px-3.5 py-3">
<span className="mb-[3px] block font-display text-[8px] uppercase tracking-[1px] text-accent-pale">
Expand Down
20 changes: 7 additions & 13 deletions kit/ui/ampl-header/WorkshopBand.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,38 @@
/**
* WORKSHOP band — the deep-plum band: tool switcher + contextual in-app nav
* (active/disabled states) + the EN/ES switcher and account-chip-or-sign-in cluster.
* WORKSHOP band — the deep-plum band: contextual in-app nav (active/disabled
* states) + the EN/ES switcher and account-chip-or-sign-in cluster.
*
* @version v0.3.0
* v0.3.2: the cross-tool WORKSHOP switcher moved UP into the institutional band;
* this band no longer owns it.
*
* @version v0.3.2
*/
import type { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { ToolSwitcher } from "./ToolSwitcher";
import { AccountMenu } from "./AccountMenu";
import type { AccountInfo, NavItem, ToolId, ToolLink } from "./types";
import type { AccountInfo, NavItem } from "./types";

export function WorkshopBand({
tool,
toolName,
nav,
context,
localeSwitcher,
account,
signInHref,
signInLabel,
tools,
}: {
tool: ToolId;
toolName: string;
nav?: NavItem[];
context?: ReactNode;
localeSwitcher: ReactNode;
account?: AccountInfo | null;
signInHref?: string;
signInLabel?: ReactNode;
tools: ToolLink[];
}) {
const { t } = useTranslation("kit");
const hasNav = (nav && nav.length > 0) || context;

return (
<div className="bg-accent-deep">
<div className="mx-auto flex h-14 max-w-[1200px] items-center gap-7 px-[30px]">
<ToolSwitcher tool={tool} toolName={toolName} tools={tools} />

{/* Contextual tool nav — desktop only; mobile uses the sheet. */}
{hasNav && (
<nav className="hidden flex-1 items-center gap-[22px] md:flex">
Expand Down
6 changes: 3 additions & 3 deletions kit/ui/ampl-header/tools.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { ToolLink } from "./types";

/**
* The live AMPL Workshop tool registry. Display names are public ("Palaeography"
* The live AMPL Workshop tool registry. Display names are public ("Paleography"
* for the calamus codebase); descriptors are English fallbacks localised at
* render via `switcher.tagline.<id>`. Data-driven so future tools slot in.
*
* @version v0.3.0
* @version v0.3.2
*/
export const DEFAULT_TOOLS: ToolLink[] = [
{ id: "calamus", name: "Palaeography", descriptor: "Practice reading manuscripts", href: "https://ampl.tools/palaeography" },
{ id: "calamus", name: "Paleography", descriptor: "Practice reading manuscripts", href: "https://ampl.tools/paleography" },
{ id: "scheduling", name: "Scheduling", descriptor: "booking & polls", href: "https://ampl.tools/scheduling" },
];
2 changes: 1 addition & 1 deletion kit/ui/ampl-header/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export type HeaderSize = "full" | "compact";
export interface AmplHeaderProps {
/** Internal tool id — drives the switcher "current" highlight. */
tool: ToolId;
/** Public display name shown in the WORKSHOP band (e.g. "Palaeography"). */
/** Public display name shown in the WORKSHOP band (e.g. "Paleography"). */
toolName: string;
/** Institutional-band scale. Default "compact"; "full" only on the signed-out front door. */
size?: HeaderSize;
Expand Down
Loading
Loading