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
4 changes: 4 additions & 0 deletions apps/apollo-vertex/app/guidelines/ai-toolkit/_meta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
index: "Visual Expression",
legal: "Legal",
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,67 @@
"use client";

import { Link2 } from "lucide-react";
import { cn } from "@/lib/utils";
import { Avatar, AvatarFallback } from "@/registry/avatar/avatar";
import { AiIcon } from "./ai-icon";

// Strong gradient for the completed-step marker (decorative, no text on it).
const STRONG = "var(--ai-gradient-strong)";

function SourceLink({
label,
href,
isFirst = true,
}: {
label: string;
href: string;
isFirst?: boolean;
}) {
return (
<>
{!isFirst && " and "}
<a
href={href}
className="text-primary hover:underline"
onClick={(e) => {
if (href === "#") e.preventDefault();
}}
>
{label}
</a>
</>
);
}

function SourceList({
sources,
}: {
sources: { label: string; href: string }[];
}) {
return (
<p className="mt-2 flex items-center gap-1 text-xs text-muted-foreground/70">
<Link2 className="size-3 shrink-0" aria-hidden="true" />
{"From "}
{sources.map((s, i) => (
<SourceLink
key={s.label}
label={s.label}
href={s.href}
isFirst={i === 0}
/>
))}
</p>
);
}

type TimelineEvent = {
title: string;
detail?: string;
time?: string;
// ai-upcoming: queued. ai-progress: running. ai-complete: done. user: a person acted.
marker: "ai-upcoming" | "ai-progress" | "ai-complete" | "user";
initials?: string;
sources?: { label: string; href: string }[];
};

// A realistic invoice review trail, newest first, mixing agent and human steps.
Expand Down Expand Up @@ -39,6 +89,10 @@ const ACTIVITY: TimelineEvent[] = [
detail: "PO matched, terms verified",
time: "9:10 AM",
marker: "ai-complete",
sources: [
{ label: "PO #8801", href: "#" },
{ label: "vendor terms", href: "#" },
],
},
{
title: "Data extracted",
Expand Down Expand Up @@ -116,8 +170,8 @@ export function ActivityTimeline() {
</defs>
</svg>
<ol className="max-w-md">
{ACTIVITY.map((event, i) => {
const last = i === ACTIVITY.length - 1;
{ACTIVITY.map((event, idx) => {
const last = idx === ACTIVITY.length - 1;
return (
<li key={event.title} className="flex gap-3">
<div className="flex flex-col items-center">
Expand Down Expand Up @@ -147,6 +201,9 @@ export function ActivityTimeline() {
{event.detail}
</p>
)}
{event.sources && event.sources.length > 0 && (
<SourceList sources={event.sources} />
)}
</div>
</li>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { AiIcon } from "./ai-icon";
export function AiInputField() {
return (
<Field>
<FieldLabel htmlFor="ai-input-glow">Job responsibility</FieldLabel>
<FieldLabel htmlFor="ai-input-glow">Cost center</FieldLabel>
<div className="relative overflow-hidden rounded-lg border border-input bg-background transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 dark:bg-input/30">
{/* Gradient definition for the mark. */}
<svg width={0} height={0} aria-hidden="true" className="absolute">
Expand Down Expand Up @@ -46,7 +46,7 @@ export function AiInputField() {
/>
<Input
id="ai-input-glow"
placeholder="Add job responsibility"
placeholder="Add cost center"
className="relative border-0 bg-transparent pr-9 shadow-none focus-visible:ring-0 dark:bg-transparent"
/>
{/* Astroid mark: equal inset from top, bottom, and right; tooltip on hover. */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Input } from "@/components/ui/input";
export function InputField() {
return (
<Field>
<FieldLabel htmlFor="input-default">Job responsibility</FieldLabel>
<Input id="input-default" placeholder="Add job responsibility" />
<FieldLabel htmlFor="input-default">Cost center</FieldLabel>
<Input id="input-default" placeholder="Add cost center" />
</Field>
);
}
120 changes: 120 additions & 0 deletions apps/apollo-vertex/app/guidelines/ai-toolkit/legal/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
export const metadata = { title: 'AI Toolkit: Legal' };

import { Badge } from '@/registry/badge/badge'

<style>{`article h2 { border-bottom: none !important; font-weight: 700; }`}</style>
Comment thread
frankkluijtmans marked this conversation as resolved.

# Legal

How the AI Toolkit meets in-product AI transparency and disclosure requirements.

<div className="mt-4 flex flex-col gap-1 text-sm text-muted-foreground">
<span><Badge status="success" variant="secondary" className="mr-1.5">In place</Badge>Live in the toolkit today.</span>
<span><Badge status="info" variant="secondary" className="mr-1.5">Planned</Badge>Coming in a near-term follow-up.</span>
<span><Badge variant="secondary" className="mr-1.5">Handled elsewhere</Badge>Owned by platform or product teams, outside the design system.</span>
</div>

## Requirement coverage

<table className="w-full text-sm">
<thead>
Comment thread
frankkluijtmans marked this conversation as resolved.
<tr className="border-b border-border">
<th className="py-3 pr-6 text-left text-xs font-medium text-muted-foreground">Requirement</th>
<th className="py-3 pr-6 text-left text-xs font-medium text-muted-foreground">Covered by</th>
<th className="py-3 text-left text-xs font-medium text-muted-foreground">Status</th>
</tr>
</thead>
<tbody className="divide-y divide-border/50">
<tr>
<td className="py-3 pr-6 align-top">Show the user they are working with AI</td>
<td className="py-3 pr-6 align-top"><a href="/guidelines/ai-toolkit#mark-and-wordmark">Mark and wordmark</a></td>
<td className="py-3 align-top"><Badge status="success" variant="secondary">In place</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Mark AI-generated content visibly</td>
<td className="py-3 pr-6 align-top"><a href="/components/badge#ai">AI generated badge</a></td>
<td className="py-3 align-top"><Badge status="success" variant="secondary">In place</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Prompt the user to review AI output</td>
<td className="py-3 pr-6 align-top"><a href="/guidelines/ai-toolkit#surfaces">AiCaveat, on recommendation, action, and AI-content surfaces</a></td>
<td className="py-3 align-top"><Badge status="success" variant="secondary">In place</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Show where the AI's information came from</td>
<td className="py-3 pr-6 align-top"><a href="/guidelines/ai-toolkit#in-components">Source line in the activity timeline</a></td>
<td className="py-3 align-top"><Badge status="success" variant="secondary">In place</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">State what the AI can and cannot do</td>
<td className="py-3 pr-6 align-top">Scope line (AI Chat pattern)</td>
<td className="py-3 align-top"><Badge status="info" variant="secondary">Planned</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Let the AI signal when it is unsure</td>
<td className="py-3 pr-6 align-top">Uncertainty statement (AI Chat pattern)</td>
<td className="py-3 align-top"><Badge status="info" variant="secondary">Planned</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Warn before sensitive data goes to a tool</td>
<td className="py-3 pr-6 align-top">Data-clearance and egress warnings (AI Chat pattern)</td>
<td className="py-3 align-top"><Badge status="info" variant="secondary">Planned</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Confirm before the AI takes an action</td>
<td className="py-3 pr-6 align-top">Action confirmation (AI Chat / actions pattern)</td>
<td className="py-3 align-top"><Badge status="info" variant="secondary">Planned</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Collect feedback with a disclosure</td>
<td className="py-3 pr-6 align-top">Feedback vote widget disclosure</td>
<td className="py-3 align-top"><Badge status="info" variant="secondary">Planned</Badge></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Data masking, access control, audit logs and retention, retrieval grounding, bias testing, human-in-the-loop configuration, approval workflows</td>
<td className="py-3 pr-6 align-top">Owned by platform, ML, and product teams</td>
<td className="py-3 align-top"><Badge variant="secondary">Handled elsewhere</Badge></td>
</tr>
</tbody>
</table>

## Disclosure strings

<table className="w-full text-sm">
<thead>
<tr className="border-b border-border">
<th className="py-3 pr-6 text-left text-xs font-medium text-muted-foreground">String</th>
<th className="py-3 pr-6 text-left text-xs font-medium text-muted-foreground">Basis</th>
<th className="py-3 pr-6 text-left text-xs font-medium text-muted-foreground">Verbatim</th>
<th className="py-3 text-left text-xs font-medium text-muted-foreground">Where</th>
</tr>
</thead>
<tbody className="divide-y divide-border/50">
<tr>
<td className="py-3 pr-6 align-top">"The output is AI generated. Please review."</td>
<td className="py-3 pr-6 align-top">Human-review flag</td>
<td className="py-3 pr-6 align-top">Yes, verbatim</td>
<td className="py-3 align-top"><a href="/guidelines/ai-toolkit#surfaces">AiCaveat default copy</a></td>
Comment thread
frankkluijtmans marked this conversation as resolved.
</tr>
<tr>
<td className="py-3 pr-6 align-top">"AI generated" (badge label)</td>
<td className="py-3 pr-6 align-top">Transparency marking</td>
<td className="py-3 pr-6 align-top">Yes, as the label</td>
<td className="py-3 align-top"><a href="/components/badge#ai">Badge</a></td>
</tr>
<tr>
<td className="py-3 pr-6 align-top">Source line, e.g. "From [sources]"</td>
<td className="py-3 pr-6 align-top">Explainability (a capability, no fixed phrase in the guidance)</td>
<td className="py-3 pr-6 align-top">N/A, our own UI label</td>
<td className="py-3 align-top"><a href="/guidelines/ai-toolkit#in-components">Timeline marker</a></td>
</tr>
</tbody>
</table>

## Source and legal basis

Based on UiPath's internal in-product compliance guidance and the standards below.

Legal basis: EU AI Act (Regulation (EU) 2024/1689), EU GDPR (Regulation 2016/679), the EU Charter of Fundamental Rights, the OECD Principles on AI, the EU HLEG Trustworthy AI Guidelines, and ISO/IEC 42001:2023.

The requirement mapping and status reflect this team's interpretation of that guidance.
70 changes: 63 additions & 7 deletions apps/apollo-vertex/app/guidelines/ai-toolkit/mark-usage.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"use client";

import { Link2 } from "lucide-react";
import type { ReactNode } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -87,17 +90,17 @@ export function MarkUsage() {
<Group title="Badges">
<Tile
label="Solid"
description="A featured, high-confidence AI moment"
description="Highest emphasis. Low-density spots with room, like a page header or a single featured card."
className="w-44"
>
<Badge className="border-0 text-white" style={{ background: FILL }}>
<Mark />
Picked for you
Best match
</Badge>
</Tile>
<Tile
label="Soft"
description="Standard in-context marker"
description="Medium emphasis. The default inside cards and content."
className="w-44"
>
<Badge
Expand All @@ -110,7 +113,7 @@ export function MarkUsage() {
</Tile>
<Tile
label="Outline"
description="Quiet provenance tag"
description="Lowest emphasis. Dense contexts like tables, lists, and toolbars."
className="w-44"
>
<Badge
Expand Down Expand Up @@ -138,8 +141,21 @@ export function MarkUsage() {
</Tile>
</Group>

<p className="max-w-prose text-sm text-muted-foreground">
These are sensible defaults; use them as-is when they fit. The variant
carries the meaning, not the label, so any label can take any variant;
choose by emphasis and how dense the surface is. The caveat that AI can
make mistakes is separate: it is not a badge passenger in any variant.
It belongs to the surface around the content, shown once at the
recommendation or action level.
</p>

<Group title="Buttons">
<Tile label="Solid" description="The marquee action" className="w-44">
<Tile
label="Solid"
description="Highest emphasis. The primary action in a view. Use one at a time."
className="w-44"
>
<Button
size="sm"
className="border-0 text-white hover:opacity-90"
Expand All @@ -149,7 +165,11 @@ export function MarkUsage() {
Ask AI
</Button>
</Tile>
<Tile label="Soft" description="Secondary, in-context" className="w-44">
<Tile
label="Soft"
description="Medium emphasis. Supporting actions inside cards and content."
className="w-44"
>
<Button
size="sm"
variant="outline"
Expand All @@ -160,7 +180,11 @@ export function MarkUsage() {
Summarize
</Button>
</Tile>
<Tile label="Outline" description="Subtle, inline" className="w-44">
<Tile
label="Outline"
description="Lowest emphasis. Inline or dense placements, and tertiary actions."
className="w-44"
>
<Button
size="sm"
variant="outline"
Expand All @@ -187,6 +211,13 @@ export function MarkUsage() {
</Tile>
</Group>

<p className="max-w-prose text-sm text-muted-foreground">
These are sensible defaults; use them as-is when they fit. The variant
carries the meaning, not the label, so any label can take any variant;
choose by how prominent the action should be and how dense the surface
is. Keep one solid action per view so the primary choice stays clear.
</p>

<Group title="Timeline marker">
<Tile label="Upcoming" className="w-20">
<span className="flex size-7 items-center justify-center rounded-full border-2 border-dotted border-insight-300 text-insight-400">
Expand Down Expand Up @@ -239,11 +270,36 @@ export function MarkUsage() {
<p className="text-xs text-muted-foreground">
Compared price, warranty, and lead time
</p>
<p className="mt-2 flex items-center gap-1 text-xs text-muted-foreground/70">
<Link2 className="size-3 shrink-0" aria-hidden="true" />
From{" "}
<a
href="#"
className="text-primary hover:underline"
onClick={(e) => e.preventDefault()}
>
Coupa catalog
</a>{" "}
and{" "}
<a
href="#"
className="text-primary hover:underline"
onClick={(e) => e.preventDefault()}
>
2 supplier quotes
</a>
</p>
</div>
</div>
</Tile>
</Group>

<p className="max-w-prose text-sm text-muted-foreground">
Source line. When an AI step drew on retrieved data, name where it came
from so the result is explainable and the user can trace it back. The
names link to the source.
</p>

<Group title="Thinking">
<Tile label="Idle" className="w-20">
<AstroidThinking isThinking={false} />
Expand Down
Loading
Loading