# Top bar

The bar across the top of an app: where the person is at the start (usually a ScopeSwitcher), search beside it (usually a CommandTrigger), and buttons at the end (the quick chat's, NotificationCenter, PersonMenu). It has no words of its own. Start, middle and end keep their reading order in right-to-left text.

Give it to AppLayout's `top`: there it is a `div` inside AppLayout's banner (`<header>`), so the page never has two banners, and with `frame="app"` it is the L's top arm, the brand in the cell before it. On its own it is a `<header>`, and draws `brand` at its start. Its height is the control height plus the chrome's padding, the brand cell's too, so the top arm is one line.

When the bar itself is narrower than `compactAt` (640 px by default; its own width, not the window's), `compactMiddle` takes `middle`'s place: usually an icon CommandTrigger that opens the same palette.

- Registry name: `top-bar` (`@candor/top-bar`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/top-bar

## Install

```bash
npx candor-ui add top-bar
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/top-bar`.
It brings in: app-layout, tokens, utils.

## Example

```tsx
"use client"

import * as React from "react"
import { MessageCircle, Search } from "lucide-react"
import { AppLayout, useAppLayout } from "@/components/candor/app-layout"
import { CommandPalette, CommandTrigger } from "@/components/candor/command-palette"
import { NotificationCenter } from "@/components/candor/notification-center"
import { PersonMenu } from "@/components/candor/person-menu"
import { ScopeSwitcher } from "@/components/candor/scope-switcher"
import { TopBar } from "@/components/candor/top-bar"
import { Button } from "@/components/candor/ui/button"
import type { Notice } from "@/lib/candor/team"

const notices: Notice[] = [{ id: "n1", kind: "approval-waiting", title: "A migration waits for your approval", at: new Date(Date.now() - 180_000).toISOString(), read: false }]

/** The quick chat's button: the app's own Button, its state from the frame. */
function ChatButton() {
  const layout = useAppLayout()
  const open = layout?.chat !== "closed"
  return (
    <Button variant="ghost" size="icon" aria-label="Ask Keel" aria-expanded={open} onClick={() => layout?.setChat(open ? "closed" : "panel")}>
      <MessageCircle aria-hidden />
    </Button>
  )
}

export default function Example() {
  const [palette, setPalette] = React.useState(false)
  return (
    <div className="h-[360px] overflow-hidden rounded-lg border">
      <AppLayout
        sidebarAt={480}
        chatAt={560}
        chatWidth={240}
        sidebar={<div className="h-full bg-sidebar p-3 text-xs text-muted-foreground">Places</div>}
        chat={{ label: "Ask Keel", content: <p className="p-3 text-sm text-muted-foreground">Ask about this page.</p> }}
        top={
          <TopBar
            start={
              <ScopeSwitcher
                levels={[{ id: "client", kind: "client", value: { id: "acme", label: "Acme" }, options: [{ id: "acme", label: "Acme" }] }]}
                onChange={() => {}}
                environment={{ id: "prod", name: "Acme", production: true, connection: "connected" }}
                mode={{ label: "Recommends only" }}
              />
            }
            // A field-shaped trigger with ⌘K; when the bar is narrow, an icon that opens the same palette.
            middle={<CommandTrigger onClick={() => setPalette(true)} label="Search or run a command" className="max-w-64" />}
            compactMiddle={
              <Button variant="ghost" size="icon" aria-label="Search" onClick={() => setPalette(true)}>
                <Search aria-hidden />
              </Button>
            }
            end={
              <>
                <ChatButton />
                <NotificationCenter notices={notices} onOpen={() => {}} onMarkAllRead={() => {}} />
                <PersonMenu user={{ name: "Sam Rivera", email: "sam@acme.example" }} onSignOut={() => {}} />
              </>
            }
          />
        }
      >
        <div className="p-4 text-sm text-muted-foreground">The page.</div>
      </AppLayout>
      <CommandPalette open={palette} onOpenChange={setPalette} groups={[]} />
    </div>
  )
}
```

## <TopBar>

Props:

- `start?: React.ReactNode | undefined`: Where the person is, at the start: usually a ScopeSwitcher. It gives way before `end` does, so on a narrow screen a ScopeSwitcher truncates and the end's buttons stay on screen.
- `middle?: React.ReactNode | undefined`: Search, after the start: usually a CommandTrigger.
- `end?: React.ReactNode | undefined`: The actions, at the end: the quick chat's button, NotificationCenter, PersonMenu.
- `compactMiddle?: React.ReactNode | undefined`: What takes `middle`'s place when the bar is narrower than `compactAt`, usually an icon CommandTrigger. Left out, `middle` stays.
- `compactAt?: number | undefined` (default `640`): The bar's own width, in pixels, below which `compactMiddle` is drawn. Defaults to 640.
- `brand?: React.ReactNode | undefined`: The product's mark, at the start, drawn only when the bar isn't in an AppLayout with `frame="app"` (there the brand is the frame's own cell).
- `className?: string | undefined`: Classes for the bar, merged with Candor's.

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
