Skip to content
Open
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
20 changes: 18 additions & 2 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
## Source of truth

- Status: Active
- Last refreshed: 2026-08-08
- Primary product surfaces: Administrator Activity Configuration offer validity, minimum purchase amount, module-wide email throughput, email authoring, localization review, generation, activation preflight, Continuous Activity lifecycle automation, lifecycle operations/metrics, and customer Wallet Recall-offer pricing.
- Last refreshed: 2026-08-11
- Primary product surfaces: Authenticated Console application header and official CLI discovery entry; Administrator Activity Configuration offer validity, minimum purchase amount, module-wide email throughput, email authoring, localization review, generation, activation preflight, Continuous Activity lifecycle automation, lifecycle operations/metrics, and customer Wallet Recall-offer pricing.
- Evidence reviewed:
- `web/default/src/components/layout/components/app-header.tsx`
- `web/default/src/components/layout/components/header.tsx`
- `web/default/src/components/layout/components/top-nav.tsx`
- `web/default/src/components/ui/button.tsx`
- `web/default/src/lib/origins.ts`
- `website/src/lib/cli-landing.ts`
- `web/default/src/features/recall-campaigns/components/campaign-editor.tsx`
- `web/default/src/features/recall-campaigns/components/campaign-email-html-editor.tsx`
- `web/default/src/features/recall-campaigns/index.tsx`
Expand Down Expand Up @@ -180,3 +186,13 @@
## Open questions

- None. The source language, trigger, activation gate, optional review rule, regeneration overwrite behavior, both promotion-validity modes, optional operator-entered USD/INR/BRL/JPY minimums, and one Activity-module-wide hourly email limit are approved.

## Authenticated Console header / CLI CTA addendum

- Goal: Make Flatkey CLI immediately discoverable from every authenticated Console screen without restoring Home, Rankings, or other removed navigation items.
- Placement: The CTA lives in the default `AppHeader` action group after desktop website navigation and before notifications. It is intentionally separate from `TopNav` so it remains visible below the `lg` breakpoint and does not appear in public navigation consumers.
- Destination: Open the official website `/cli` landing page through the Console origin helper. Do not point ordinary navigation at `/cli/authorize`, which requires a device authorization code.
- Visual treatment: Reuse the existing compact button shape and brand-violet palette. Use a terminal icon plus a visible `CLI` label, a restrained violet-to-fuchsia treatment, and a modest shadow. Do not pulse, flash, or animate continuously.
- Responsive behavior: Show `Flatkey CLI` from `sm` upward and the shorter `CLI` label on narrower screens. The control remains visible at 360px and does not depend on hover.
- Accessibility: Render a real link with a visible keyboard focus ring, sufficient text contrast, and decorative icons hidden from assistive technology. External navigation opens in a new tab with `noopener noreferrer`.
- Verification: Cover destination, external-link safety, visible desktop/mobile labels, and icon semantics with a component test; run targeted tests, lint, formatting, typecheck, production build, and 1440px/390px browser checks.
42 changes: 42 additions & 0 deletions docs/superpowers/specs/2026-08-11-console-cli-header-cta-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Console CLI Header CTA Design

## Goal

Add a prominent Flatkey CLI entry to the authenticated Console header so users can discover the product from any Console screen. Home and Rankings remain absent from the Console top navigation.

## Selected approach

Add a dedicated `ConsoleCliCta` component to the default `AppHeader` action group, immediately after desktop website navigation and before notifications. The CTA is not represented as a `TopNavLink`: `useTopNavLinks()` is also consumed by public navigation, while the Console CTA must remain authenticated-shell-only and visible on mobile.

The CTA opens the official website `/cli` landing page using `officialWebsiteUrl('/cli')`. It does not link to `/cli/authorize`, because that route is a device-flow callback that requires `user_code`.

## Alternatives considered

1. A normal `CLI` text link inside `TopNav`. Rejected because it is not visually prominent and the current `AppHeader` hides the `TopNav` wrapper below `lg`.
2. A static non-clickable CLI badge. Rejected because it creates emphasis without giving users a useful next action.
3. A direct GitHub or npm link. Rejected as the primary header destination because the official `/cli` landing page already provides localized product context, install commands, and downstream links.

## Visual and responsive behavior

- Use the existing compact button geometry with a terminal icon, `Flatkey CLI` on `sm` and wider screens, and `CLI` below `sm`.
- Use the existing Flatkey violet visual language with a restrained violet-to-fuchsia background, white text, and modest shadow.
- Keep the control inside the 48px header and visible down to 360px.
- Use color and the persistent text label together; do not add pulsing, flashing, or continuous animation.

## Accessibility and link behavior

- Render a semantic anchor with `target="_blank"` and `rel="noopener noreferrer"`.
- Keep the visible label as the accessible name and mark terminal/external-link icons decorative.
- Preserve an obvious `focus-visible` ring and AA-readable text contrast in light and dark themes.

## Implementation boundary

- Create `web/default/src/components/layout/components/console-cli-cta.tsx`.
- Render it only from the default action group in `app-header.tsx`, before notifications.
- Do not extend `TopNavLink`, modify `useTopNavLinks()`, change public navigation, add dependencies, or alter CLI authorization behavior.

## Verification

- A server-rendered component test proves the `/cli` destination, safe new-tab attributes, both responsive labels, and hidden decorative icons.
- Targeted test, ESLint, Prettier, TypeScript, production build, and `git diff --check` must pass.
- Browser checks at 1440px and 390px verify placement, emphasis, no header overflow, focus visibility, and destination behavior.
2 changes: 2 additions & 0 deletions web/default/src/components/layout/components/app-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { NotificationPopover } from '@/components/notification-popover'
import { ProfileDropdown } from '@/components/profile-dropdown'
import { defaultTopNavLinks } from '../config/top-nav.config'
import { type TopNavLink } from '../types'
import { ConsoleCliCta } from './console-cli-cta'
import { Header } from './header'
import { SystemBrand } from './system-brand'
import { TopNav } from './top-nav'
Expand Down Expand Up @@ -107,6 +108,7 @@ export function AppHeader({
<TopNav links={links} />
</div>
)}
<ConsoleCliCta />
{showNotifications && (
<NotificationPopover
open={notifications.popoverOpen}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Copyright (C) 2023-2026 QuantumNous

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

For commercial licensing, please contact support@quantumnous.com
*/
import { describe, expect, test } from 'bun:test'
import { renderToStaticMarkup } from 'react-dom/server'
import { ConsoleCliCta } from './console-cli-cta'

describe('ConsoleCliCta', () => {
test('opens the official CLI landing page with responsive labels', () => {
const html = renderToStaticMarkup(<ConsoleCliCta />)

expect(html).toContain('href="/cli"')
expect(html).toContain('target="_blank"')
expect(html).toContain('noopener')
expect(html).toContain('noreferrer')
expect(html).toContain('Flatkey CLI')
expect(html).toContain('sm:hidden')
expect(html).toContain('aria-hidden="true"')
})

test('keeps the brand gradient high contrast through hover', () => {
const html = renderToStaticMarkup(<ConsoleCliCta />)

expect(html).toContain('from-violet-700')
expect(html).toContain('to-fuchsia-700')
expect(html).toContain('hover:from-violet-600')
expect(html).toContain('hover:to-fuchsia-600')
})
})
42 changes: 42 additions & 0 deletions web/default/src/components/layout/components/console-cli-cta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Copyright (C) 2023-2026 QuantumNous

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

For commercial licensing, please contact support@quantumnous.com
*/
import { ExternalLink, Terminal } from 'lucide-react'
import { officialWebsiteUrl } from '@/lib/origins'
import { Button } from '@/components/ui/button'

export function ConsoleCliCta() {
return (
<Button
size='sm'
className='h-9 gap-1.5 border-violet-400/30 bg-gradient-to-r from-violet-700 to-fuchsia-700 px-2 text-xs font-semibold text-white shadow-[0_10px_24px_-14px_rgba(124,58,237,0.85)] hover:from-violet-600 hover:to-fuchsia-600 focus-visible:ring-violet-500/40 dark:border-violet-300/30 dark:text-white'
render={
<a
href={officialWebsiteUrl('/cli')}
target='_blank'
rel='noopener noreferrer'
/>
}
>
<Terminal aria-hidden='true' />
<span className='hidden sm:inline'>Flatkey CLI</span>
<span className='sm:hidden'>CLI</span>
<ExternalLink className='hidden sm:block' aria-hidden='true' />
</Button>
)
}