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
13 changes: 13 additions & 0 deletions AGENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ When creating branches or Pull Requests via the `gh` CLI:
2. **Table Selection:** Pass `slot="selection"` on any `<Checkbox>` rendered inside `<Table.Header>` or `<Table.Cell>` (e.g. `<Checkbox slot="selection" ... />`).
3. Replace nested `<button>` elements inside `<Table.Column>` with clickable `<div>` or `<span>` elements (e.g. `<div role="button" tabIndex={0} onClick={...}>`).

### Unified Delete & Delete Icon Buttons (`DeleteIconButton` & `DeleteButton`)
**Rule:** ALWAYS use the unified `@fluxify/components` delete button components for delete and remove actions across the portal UI instead of ad-hoc `<Button variant="ghost">` or solid `<Button variant="danger">`:
1. **Icon-only delete actions (table rows, card actions, form removals):** Use `<DeleteIconButton aria-label="Delete ..." onPress={...} />` (or pass custom `icon`, `size`, `isDisabled`, `iconSize`). It uses `variant="danger-soft"` (translucent danger styling) by default.
2. **Text delete actions (bulk delete buttons, danger zone action buttons):** Use `<DeleteButton onPress={...}>Delete ...</DeleteButton>` (uses `variant="danger-soft"` with leading `TbTrash` icon by default).
3. **Confirmation dialogs:** Use `<ConfirmDialog danger ...>` which defaults destructive action buttons to `variant="danger-soft"` (translucent danger) unless explicitly given `variant="danger"`.
4. **Dropdown menu delete items:** Style with `variant="danger" className="text-danger hover:bg-danger/10 focus:bg-danger/10 focus:text-danger"` and `<TbTrash size={16} className="text-danger" />`.

### Unified Modal Close Button (`CloseButton` / `ModalCloseButton`)
**Rule:** ALWAYS use the unified `@fluxify/components` close button component for modals, dialogs, and clearable surfaces across the portal and components UI instead of default HeroUI `<Modal.CloseTrigger>` (which renders an unthemed solid background and custom SVG) or ad-hoc custom icon buttons:
1. **Modal headers:** Use `<CloseButton />` (or `<ModalCloseButton />`) in `<Modal.Header>` (with flex layout, e.g. `<Modal.Header className="flex flex-row items-center justify-between">`). It uses `TbX` from `react-icons/tb` (default 18px), applies theme tokens (`text-muted hover:text-foreground hover:bg-surface-secondary active:bg-surface-secondary/80 rounded-md transition-colors`), and sets `slot="close"` for automatic modal dismissal with React Aria.
2. **Explicit close triggers:** When a manual dismissal callback is needed (e.g. outside dialog context or controlled state resets), pass `onPress={onClose}` (e.g. `<CloseButton onPress={handleClose} />`).
3. **Clearable input controls:** Use `<CloseButton aria-label="Clear ..." onPress={handleClear} />` for consistent clear actions in search/selector bars.

### Harness Structured Output — "Unrecognized token '\'" / "Unexpected EOF" / silent parse failures
**File:** `apps/ai-gateway/src/harness/models/base.ts` (`fallbackStructuredOutput`, `cleanJsonOutput`, `sliceBalancedJson`, `parseJsonLoose`).

Expand Down
7 changes: 4 additions & 3 deletions apps/portal/src/components/ai/AiHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function AiHome() {
if (isBlocked) {
return (
<div className="mx-auto flex h-full w-full max-w-md flex-col items-center justify-center gap-6 px-4 text-center">
<div className="flex size-16 items-center justify-center rounded-full bg-red-500/10 text-red-500">
<div className="flex size-16 items-center justify-center rounded-full bg-danger/10 text-danger">
<TbAlertTriangle size={32} />
</div>
<div className="flex flex-col gap-2">
Expand All @@ -66,7 +66,7 @@ export function AiHome() {
</div>
<a
href={`/_/admin/ui/${projectId}/integrations?group=ai`}
className="inline-flex items-center gap-2 rounded-xl bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
className="inline-flex items-center gap-2 rounded-xl bg-accent px-4 py-2 text-sm font-medium text-accent-foreground hover:bg-accent/90 transition-colors"
>
<TbPlugConnected size={18} />
Configure Integrations
Expand Down Expand Up @@ -108,7 +108,8 @@ export function AiHome() {
<Button
key={s.label}
size="sm"
className="rounded-full border border-white/10 bg-white/5 backdrop-blur-md px-4 py-2 text-white/70 hover:bg-white/10 hover:text-white"
variant="outline"
className="rounded-full border-border bg-surface px-4 py-2 text-muted hover:bg-surface-secondary hover:text-foreground"
onPress={() => setQuery(s.prompt)}
>
{s.label}
Expand Down
8 changes: 4 additions & 4 deletions apps/portal/src/components/ai/ArtifactsSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ export function ArtifactsSidebar() {

return (
<div
className={`h-full border-l border-white/10 bg-surface transition-all duration-300 ease-in-out shrink-0 overflow-hidden ${
className={`h-full border-l border-border bg-surface transition-all duration-300 ease-in-out shrink-0 overflow-hidden ${
selectedArtifact ? 'w-[500px] opacity-100' : 'w-0 opacity-0 border-none'
}`}
>
<div className="w-[500px] h-full flex flex-col relative">
<div className="h-14 flex items-center justify-between px-4 border-b border-white/10 shrink-0">
<div className="h-14 flex items-center justify-between px-4 border-b border-border shrink-0">
<h3 className="font-semibold text-foreground">Artifacts</h3>
<button
onClick={() => setSelectedArtifact(null)}
className="p-1 hover:bg-white/10 rounded-md text-muted hover:text-foreground transition-colors"
className="p-1 hover:bg-surface-secondary rounded-md text-muted hover:text-foreground transition-colors"
>
<TbChevronsRight size={20} />
</button>
Expand Down Expand Up @@ -49,7 +49,7 @@ export function ArtifactsSidebar() {

<div>
<span className="text-[10px] text-muted uppercase font-bold tracking-wider">Props</span>
<pre className="text-xs text-muted bg-black/20 p-3 rounded-lg overflow-x-auto mt-1 border border-white/5 custom-scrollbar">
<pre className="text-xs text-muted bg-surface-secondary p-3 rounded-lg overflow-x-auto mt-1 border border-border custom-scrollbar">
{JSON.stringify(selectedArtifact.props, null, 2)}
</pre>
</div>
Expand Down
11 changes: 8 additions & 3 deletions apps/portal/src/components/ai/ConversationItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,14 @@ export function ConversationItem({
<TbEdit size={16} />
<Label>Rename</Label>
</Dropdown.Item>
<Dropdown.Item id="delete" variant="danger" textValue="Delete">
<TbTrash size={16} />
<Label>Delete</Label>
<Dropdown.Item
id="delete"
variant="danger"
textValue="Delete"
className="text-danger hover:bg-danger/10 focus:bg-danger/10 focus:text-danger"
>
<TbTrash size={16} className="text-danger" />
<Label className="text-danger">Delete</Label>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown.Popover>
Expand Down
18 changes: 9 additions & 9 deletions apps/portal/src/components/ai/ConversationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,14 +192,14 @@ export function ConversationPage() {
{isLoading ? (
<div className="flex w-full flex-col gap-8 animate-pulse pt-8 opacity-60">
<div className="flex w-full justify-end">
<div className="h-12 w-64 rounded-2xl bg-white/5"></div>
<div className="h-12 w-64 rounded-2xl bg-surface-secondary"></div>
</div>
<div className="flex w-full flex-col gap-2">
<div className="h-3 w-24 rounded-full bg-white/5 mb-1"></div>
<div className="h-20 w-full rounded-xl bg-white/5"></div>
<div className="h-3 w-24 rounded-full bg-surface-secondary mb-1"></div>
<div className="h-20 w-full rounded-xl bg-surface-secondary"></div>
</div>
<div className="flex w-full justify-end">
<div className="h-12 w-48 rounded-2xl bg-white/5"></div>
<div className="h-12 w-48 rounded-2xl bg-surface-secondary"></div>
</div>
</div>
) : messages.length === 0 && !optimisticQuery ? (
Expand Down Expand Up @@ -277,9 +277,9 @@ export function ConversationPage() {
) : isHistoricalPlan ? (
<div className="flex w-full flex-col gap-2">
{isRunActive && <HarnessStatusAccordion conversationId={conversationId} />}
<div className="w-full rounded-2xl border border-white/10 bg-white/5 p-4 flex flex-col gap-3 opacity-70">
<div className="w-full rounded-2xl border border-border bg-surface-secondary p-4 flex flex-col gap-3 opacity-70">
<div className="flex items-center gap-4">
<div className="bg-white/10 p-2.5 rounded-xl text-foreground shrink-0">
<div className="bg-surface p-2.5 rounded-xl text-foreground shrink-0 border border-border">
<TbListSearch size={20} />
</div>
<div className="flex-1">
Expand All @@ -305,11 +305,11 @@ export function ConversationPage() {
{isFetchingNextPage && (
<div className="flex w-full flex-col gap-8 animate-pulse py-8 opacity-60">
<div className="flex w-full justify-end">
<div className="h-12 w-64 rounded-2xl bg-white/5"></div>
<div className="h-12 w-64 rounded-2xl bg-surface-secondary"></div>
</div>
<div className="flex w-full flex-col gap-2">
<div className="h-3 w-24 rounded-full bg-white/5 mb-1"></div>
<div className="h-20 w-full rounded-xl bg-white/5"></div>
<div className="h-3 w-24 rounded-full bg-surface-secondary mb-1"></div>
<div className="h-20 w-full rounded-xl bg-surface-secondary"></div>
</div>
</div>
)}
Expand Down
2 changes: 1 addition & 1 deletion apps/portal/src/components/ai/HarnessStatusAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function HarnessStatusAccordion({ conversationId }: { conversationId: str
</div>
</button>
<div className={`overflow-hidden transition-all duration-300 ease-in-out ${isExpanded ? "max-h-[500px] opacity-100 mt-1" : "max-h-0 opacity-0"}`}>
<div className="flex flex-col gap-3 p-4 bg-white/5 rounded-xl border border-white/10 mx-2">
<div className="flex flex-col gap-3 p-4 bg-surface-secondary rounded-xl border border-border mx-2">
{harnessSteps.map(step => {
const isCompleted = step.nodeStatus === "ended";
const isRunning = step.nodeStatus === "started" || step.nodeStatus === "running";
Expand Down
4 changes: 2 additions & 2 deletions apps/portal/src/components/ai/MarkdownViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ export function MarkdownViewer({ content }: MarkdownViewerProps) {
ol: ({ children }: any) => <ol className="list-decimal ml-6 mb-3 text-sm text-foreground/80">{children}</ol>,
li: ({ children }: any) => <li className="mb-1 text-sm">{children}</li>,
blockquote: ({ children }: any) => (
<blockquote className="border-l-4 border-white/20 pl-4 italic mb-3">
<blockquote className="border-l-4 border-border pl-4 italic mb-3 text-muted">
{children}
</blockquote>
),
pre: ({ children }: any) => (
<pre className="bg-black/20 p-4 rounded-xl mb-3 overflow-x-auto text-sm">
<pre className="bg-surface-secondary border border-border p-4 rounded-xl mb-3 overflow-x-auto text-sm">
{children}
</pre>
),
Expand Down
6 changes: 3 additions & 3 deletions apps/portal/src/components/ai/ModelSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ export function ModelSelect({ projectId, value, models, onChange }: Props) {
onChange={(v) => v && onChange(String(v as Key))}
className="w-[240px]"
>
<Select.Trigger className="flex w-full h-9 items-center justify-between gap-2 rounded-2xl border border-white/10 bg-transparent px-3 font-medium text-muted-foreground shadow-none hover:bg-white/5 data-[open=true]:bg-white/5 data-[focus-visible=true]:ring-0 transition-colors relative">
<Select.Trigger className="flex w-full h-9 items-center justify-between gap-2 rounded-2xl border border-border bg-transparent px-3 font-medium text-muted shadow-none hover:bg-surface-secondary data-[open=true]:bg-surface-secondary data-[focus-visible=true]:ring-0 transition-colors relative">
{!value && (
<span className="absolute -top-1 -right-1 flex h-2.5 w-2.5">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-500 opacity-75"></span>
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-red-500"></span>
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-danger opacity-75"></span>
<span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-danger"></span>
</span>
)}
<Select.Value>
Expand Down
Loading
Loading