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
112 changes: 107 additions & 5 deletions core/ui/canvas/src/app/atrium/IntelligenceOS.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import {
BrainCircuit,
CircleAlert,
Clock3,
Crosshair,
Layers3,
Network,
Radio,
RefreshCw,
Route,
SearchCheck,
ShieldCheck,
Sparkles,
TimerReset,
} from 'lucide-react'

import type { IntelligenceResourceRecord } from '@/api/intelligenceResourcesApi'
Expand Down Expand Up @@ -49,7 +52,7 @@ const SURFACE_COPY: Record<Surface, { title: string; subtitle: string }> = {
},
opportunities: {
title: 'Opportunities',
subtitle: 'Cases and material shifts worth investigating or acting on.',
subtitle: 'Evidence-backed openings that may warrant a decision, response, or new watch.',
},
agents: {
title: 'Agents',
Expand Down Expand Up @@ -210,7 +213,7 @@ function CoverageStrip({ groups, freshness }: { readonly groups: ResourceGroups;
const entries = [
{ icon: Radio, label: 'Sources', value: `${sources} admitted` },
{ icon: Activity, label: 'Watches', value: `${monitors} active` },
{ icon: Layers3, label: 'Open cases', value: `${openCases} material` },
{ icon: Layers3, label: 'Decision openings', value: `${openCases} ready` },
{ icon: Clock3, label: 'Freshness', value: freshness },
]

Expand Down Expand Up @@ -261,6 +264,107 @@ function AgentsView({ groups }: { readonly groups: ResourceGroups }) {
)
}

function OpportunityStage({
icon: Icon,
title,
detail,
}: {
readonly icon: typeof Crosshair
readonly title: string
readonly detail: string
}) {
return (
<div className="flex gap-3 rounded-lg border bg-background p-4">
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg border border-brand/20 bg-brand/10 text-brand">
<Icon className="size-4" />
</div>
<div>
<div className="text-sm font-semibold">{title}</div>
<p className="mt-1 text-xs leading-relaxed text-muted-foreground">{detail}</p>
</div>
</div>
)
}

function OpportunitySection({
eyebrow,
title,
detail,
items,
empty,
}: {
readonly eyebrow: string
readonly title: string
readonly detail: string
readonly items: readonly IntelligenceResourceRecord[]
readonly empty: string
}) {
return (
<section className="space-y-3">
<div className="flex flex-wrap items-end justify-between gap-3">
<div>
<div className="font-mono text-[9px] font-semibold uppercase tracking-[0.17em] text-brand">{eyebrow}</div>
<h2 className="mt-1 text-lg font-semibold tracking-tight">{title}</h2>
<p className="mt-1 max-w-3xl text-xs leading-relaxed text-muted-foreground">{detail}</p>
</div>
<Badge variant="outline" className="rounded-sm font-mono text-[9px]">{items.length} current</Badge>
</div>
<ResourceGrid items={items} empty={empty} compact />
</section>
)
}

function OpportunitiesView({ groups }: { readonly groups: ResourceGroups }) {
const decisionOpenings = groups.opportunities.filter((item) => item.reference.resource_kind === 'case')
const emergingOpenings = groups.opportunities.filter((item) => item.reference.resource_kind === 'shift')
const earlySignals = groups.opportunities.filter((item) => item.reference.resource_kind === 'signal')

return (
<div className="space-y-7">
<Card className="border-brand/20 bg-brand/[0.04]">
<CardContent className="p-5 md:p-6">
<div className="max-w-3xl">
<Badge variant="secondary" className="mb-3 border border-brand/15 bg-brand/10 text-brand">Decision openings</Badge>
<h2 className="text-xl font-semibold tracking-tight">An Opportunity is intelligence awaiting a decision.</h2>
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
ACE promotes a signal or shift when the evidence suggests a favorable or avoidable window. It is not a lead, a task, or autonomous Work; it remains a proposal until a person investigates, accepts, dismisses, or turns it into Strategy.
</p>
</div>
<div className="mt-5 grid gap-3 md:grid-cols-3">
<OpportunityStage icon={Radio} title="Signal" detail="A relevant observation worth tracking, but not yet a decision opening." />
<OpportunityStage icon={TimerReset} title="Shift" detail="A material delta from the baseline that may create a time-sensitive opening." />
<OpportunityStage icon={SearchCheck} title="Case" detail="A bounded, evidence-backed question ready for investigation or decision." />
</div>
</CardContent>
</Card>

<OpportunitySection
eyebrow="Ready to investigate"
title="Decision openings"
detail="Cases have a bounded question and preserved evidence. Review the record before turning one into Strategy or downstream Work."
items={decisionOpenings}
empty="No evidence-backed decision openings are ready yet."
/>
<OpportunitySection
eyebrow="Developing"
title="Emerging openings"
detail="Material shifts may become Opportunities once their decision window, impact, and evidence are clear."
items={emergingOpenings}
empty="No material shifts are currently developing into Opportunities."
/>
{earlySignals.length > 0 && (
<OpportunitySection
eyebrow="Watchlist"
title="Early signals"
detail="These observations are relevant, but ACE has not yet established a material shift or bounded decision question."
items={earlySignals}
empty="No early signals are being watched."
/>
)}
</div>
)
}

function Metric({ label, value, detail, warning = false }: { readonly label: string; readonly value: number | string; readonly detail: string; readonly warning?: boolean }) {
return (
<Card>
Expand Down Expand Up @@ -297,9 +401,7 @@ function PageContent({ surface, groups, all, onStart }: { readonly surface: Surf
if (surface === 'intelligence') return <BriefingHome groups={groups} all={all} onStart={onStart} />
if (surface === 'connections') return <ConnectionsView groups={groups} />
if (surface === 'agents') return <AgentsView groups={groups} />
if (surface === 'opportunities') {
return <ResourceGrid items={groups.opportunities} empty="No material opportunities have been opened yet." />
}
if (surface === 'opportunities') return <OpportunitiesView groups={groups} />
return <ResourceGrid items={groups.strategy} empty="No decisions or outcomes have entered the strategy loop yet." />
}

Expand Down
2 changes: 1 addition & 1 deletion core/ui/canvas/src/app/journey/SessionsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const RECENT_SESSIONS: readonly RecentSession[] = [
},
]

// Outcome -> dot color. State semantics: in motion = live (brand green),
// Outcome -> dot color. State semantics: in motion = live (spectral cyan),
// decided = success, shelved/archived = neutral (a category, not an alarm).
const OUTCOME_DOT: Record<SessionOutcome, string> = {
'in motion': 'bg-live animate-pulse',
Expand Down
4 changes: 2 additions & 2 deletions core/ui/canvas/src/design/__enforcement__/contrastAA.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,13 @@ const COLORS = {
surfaceCanvas: '#FAFAFA',
surfaceRaised: '#FFFFFF',
surfaceRecessed: '#F4F4F4',
surfaceTint: '#E8F2FF',
surfaceTint: '#E9EEFF',
ink: '#171717',
inkStrong: '#0A0A0A',
inkSoft: '#525252',
inkMuted: '#737373',
inkFaint: '#A3A3A3',
accent: '#0070F3',
accent: '#315DDE',
accentInk: '#FFFFFF',
success: '#16A34A',
warning: '#D97706',
Expand Down
36 changes: 18 additions & 18 deletions core/ui/canvas/src/design/tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
*
* The ACE design system — canonical token sheet.
*
* Visual direction: ENGINEERED LIGHT. Bright neutral canvas, single
* electric-blue accent, hairline-shadow-as-border for elevation, 8px
* engineered buttons (not pills), 12px cards. The vocabulary of Vercel +
* v0 + OpenAI Devs — a working surface that instills trust through
* clarity, not warmth. Whiteboard, not parchment.
* Visual direction: ACE PRECISION. Bright neutral canvas outside the
* command center, a single cognitive-blue accent derived from the ACE mark,
* hairline-shadow-as-border for elevation, 8px engineered buttons (not
* pills), and 12px cards. The vocabulary is ACE's own: calm, exact,
* evidence-forward, and recognizable without borrowing a customer's brand.
*
* See docs/design-system.md for the principles and layer model.
* See docs/design/ace-design-system-v1.md for the principles and layer model.
* Every UI component composes from these tokens. No inline color/font/
* spacing in component code; if a value isn't expressible in tokens, the
* design system gets a new token first.
Expand Down Expand Up @@ -271,15 +271,15 @@
* reference these exclusively. Engineered-light IS the base — there is
* no dark default to override from.
*
* Color values draw from the Vercel/v0/OpenAI-Devs/Amie consensus:
* Color values use ACE's neutral working surfaces:
* canvas #FAFAFA, raised #FFFFFF, line #EAEAEA, ink #171717
* Single chromatic accent: Vercel electric blue #0070F3.
* Single chromatic accent: ACE cognitive blue #315DDE.
*/

--ace-surface-canvas: #FAFAFA; /* page / canvas ground */
--ace-surface-raised: #FFFFFF; /* cards, popovers, the lifted surface */
--ace-surface-recessed: #F4F4F4; /* highlight bands, captured-this-turn chips */
--ace-surface-tint: #E8F2FF; /* accent-tinted background (convergence beat, banner) */
--ace-surface-tint: #E9EEFF; /* accent-tinted background (convergence beat, banner) */

/* Legacy card surface aliases — keep so existing components don't break.
Map them all to the new role tokens. */
Expand Down Expand Up @@ -322,13 +322,13 @@
--ace-focus-ring: 0 0 0 3px var(--ace-accent-soft);
--ace-focus-ring-strong: 0 0 0 1px var(--ace-accent), 0 0 0 4px var(--ace-accent-soft);

/* Single chromatic accent — Vercel electric blue. Used for CTAs,
/* Single chromatic accent — ACE cognitive blue. Used for CTAs,
active states, focus rings, the "next voice" arrow, the
reconciliation banner edge, the convergence beat tint. Nowhere else. */
--ace-accent: #0070F3;
--ace-accent-hover: #0061D5;
--ace-accent-soft: #E8F2FF;
--ace-accent-tint: rgba(0, 112, 243, 0.10);
--ace-accent: #315DDE;
--ace-accent-hover: #274CB8;
--ace-accent-soft: #E9EEFF;
--ace-accent-tint: rgba(49, 93, 222, 0.11);
--ace-accent-ink: #FFFFFF;

/* ============================================================================
Expand All @@ -345,14 +345,14 @@
A branded theme overrides --ace-voice-accent to retint everything in
one move. */
--ace-north-star-bg: var(--ace-accent-soft);
--ace-north-star-line: rgba(0, 112, 243, 0.24);
--ace-north-star-line: rgba(49, 93, 222, 0.24);
--ace-north-star-label: var(--ace-accent);

/* Synthesis surface — the convergence-beat lock-in card. Same accent
tint as the north star to signal "this is the system's verdict
about itself." */
--ace-synthesis-paper: var(--ace-surface-tint);
--ace-synthesis-divider: rgba(0, 112, 243, 0.18);
--ace-synthesis-divider: rgba(49, 93, 222, 0.18);

/* Sketch-chalk — the inked annotation strokes inside cards
(calibration sparkline, contribution arrows). Dark-ink on light
Expand All @@ -363,7 +363,7 @@
--ace-discipline-accent-fallback: var(--ace-ink-muted);

/* Voice accent — the color that names ACE's voice in the room.
Engineered-light base = Vercel electric blue. Components rendering
ACE base = cognitive blue. Components rendering
ACE's chrome (AceCard, ComposingIndicator, VisionAnchor, ShellNav
mark, Briefing tile) consume this token rather than hardcoding the
hex — so a branded extension theme can retint the voice globally
Expand All @@ -374,7 +374,7 @@
--ace-voice-accent: var(--ace-accent);
--ace-voice-accent-hover: var(--ace-accent-hover);
--ace-voice-accent-soft: var(--ace-accent-soft);
--ace-voice-accent-strong: rgba(0, 112, 243, 0.50);
--ace-voice-accent-strong: rgba(49, 93, 222, 0.50);
--ace-voice-accent-ink: var(--ace-accent-ink);

/* Semantic status — used sparingly on sentinel pips, calibration
Expand Down
16 changes: 8 additions & 8 deletions core/ui/canvas/src/design/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
// updates in the same commit. The tokens.css file is the source of
// truth; this file is the JS-side mirror.
//
// VISUAL DIRECTION (current, not permanent): engineered-light. Bright
// neutral canvas, single electric-blue accent (#0070F3), hairline-shadow
// elevation, 8px buttons, 12px cards. See docs/design-system.md.
// VISUAL DIRECTION: ACE precision. Bright neutral canvas outside Atrium,
// single cognitive-blue accent (#315DDE), hairline-shadow elevation,
// 8px buttons, and 12px cards.

// ---------------------------------------------------------------------------
// Layer 0 — Foundations
Expand Down Expand Up @@ -87,15 +87,15 @@ export const SEMANTIC = {
toneMediumSoft: 'rgba(176, 119, 53, 0.10)',
} as const

// Single chromatic accent — Vercel electric blue. The only chromatic
// Single chromatic accent — ACE cognitive blue. The only chromatic
// chrome in the engineered-light direction; everything else is
// neutral. Theme `voice-accent` defaults to this and is the override
// surface for branded extension themes (each retints it to its own accent).
export const ACCENT = {
base: '#0070F3',
hover: '#0061D5',
soft: '#E8F2FF',
tint: 'rgba(0, 112, 243, 0.10)',
base: '#315DDE',
hover: '#274CB8',
soft: '#E9EEFF',
tint: 'rgba(49, 93, 222, 0.11)',
ink: '#FFFFFF',
} as const

Expand Down
Loading