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
15 changes: 5 additions & 10 deletions web-ui/app/_components/ChoiceCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import type React from 'react';
import { useTranslations } from 'next-intl';

import { Button } from '@/app/_components/ui/Button';
import type { PendingUserChoice } from '../_lib/chatSessions';

export type { PendingUserChoice };
Expand Down Expand Up @@ -42,23 +43,17 @@ export function ChoiceCard({
)}
<div className="flex flex-wrap gap-2">
{choice.options.map((opt, idx) => (
<button
<Button
key={`${opt.value}-${String(idx)}`}
type="button"
variant={idx === 0 ? 'primary' : 'secondary'}
size="sm"
onClick={() => {
onChoose(opt.value);
}}
disabled={disabled}
className={[
'rounded border px-3 py-2 text-xs font-medium transition',
idx === 0
? 'border-[color:var(--accent)] bg-[color:var(--accent)] text-[color:var(--fg-on-dark)] hover:bg-[color:var(--accent)]'
: 'border-[color:var(--border)] bg-[color:var(--bg-elevated)] text-[color:var(--fg)] hover:border-[color:var(--border-strong)]',
'disabled:cursor-not-allowed disabled:opacity-40',
].join(' ')}
>
{opt.label}
</button>
</Button>
))}
</div>
</div>
Expand Down
27 changes: 8 additions & 19 deletions web-ui/app/_components/ConfirmDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { useEffect, useRef } from 'react';

import { Button } from './ui/Button';

/**
* Minimal modal-confirm. No portal, no library — just a fixed-position
* overlay with focus-trap basics. We keep it generic (title/body/labels)
Expand Down Expand Up @@ -56,14 +58,6 @@ export function ConfirmDialog({

if (!open) return null;

// §4.2 button variants: danger is transparent + error edge + error text
// (the error is the signal — no fill, no glow); neutral confirm is the
// Lume primary (accent fill → gradient + glow via the material layer).
const confirmCls =
tone === 'danger'
? 'border-[color:var(--danger-edge)] bg-transparent text-[color:var(--danger)] hover:bg-[color:var(--danger)]/8'
: 'border-transparent bg-[color:var(--accent)]';

return (
<div
role="dialog"
Expand All @@ -87,21 +81,16 @@ export function ConfirmDialog({
</p>
)}
<div className="mt-4 flex justify-end gap-2">
<button
ref={cancelRef}
type="button"
onClick={onCancel}
className="rounded border border-[color:var(--border)] bg-[color:var(--bg-elevated)] px-4 py-2 text-sm font-medium text-[color:var(--fg)] transition hover:border-[color:var(--border-strong)]"
>
{/* §7.5: focus opens on Cancel (deliberate friction). */}
<Button ref={cancelRef} variant="secondary" onClick={onCancel}>
{cancelLabel}
</button>
<button
type="button"
</Button>
<Button
variant={tone === 'danger' ? 'danger' : 'primary'}
onClick={onConfirm}
className={`rounded border px-4 py-2 text-sm font-medium transition ${confirmCls}`}
>
{confirmLabel}
</button>
</Button>
</div>
</div>
</div>
Expand Down
10 changes: 3 additions & 7 deletions web-ui/app/_components/IssueReportCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type React from 'react';
import { useState } from 'react';
import { useTranslations } from 'next-intl';

import { Button } from '@/app/_components/ui/Button';
import {
ApiError,
confirmBuilderIssue,
Expand Down Expand Up @@ -247,13 +248,8 @@ function SecondaryButton({
onClick: () => void;
}): React.ReactElement {
return (
<button
type="button"
onClick={onClick}
disabled={disabled}
className="rounded border border-[color:var(--border)] bg-[color:var(--bg-elevated)] px-3 py-2 text-xs font-medium text-[color:var(--fg)] transition hover:border-[color:var(--border-strong)] disabled:cursor-not-allowed disabled:opacity-40"
>
<Button variant="secondary" size="sm" onClick={onClick} disabled={disabled}>
{children}
</button>
</Button>
);
}
9 changes: 5 additions & 4 deletions web-ui/app/_components/StreamToasts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useTranslations } from 'next-intl';
import { AnimatePresence, motion } from 'framer-motion';
import { Ban, X } from 'lucide-react';

import { Button } from '@/app/_components/ui/Button';
import { useChatSessionsCtx } from '../_lib/chatSessionsContext';
import {
type StreamPhase,
Expand Down Expand Up @@ -325,16 +326,16 @@ function AbortConfirmModal({
>
{t('abortConfirmKeep')}
</button>
<button
type="button"
<Button
variant="danger"
size="sm"
onClick={(e) => {
e.stopPropagation();
onConfirm();
}}
className="rounded-md border border-[color:var(--danger-edge)] bg-transparent px-3 py-2 text-xs font-medium text-[color:var(--danger)] transition hover:bg-[color:var(--danger)]/8"
>
{t('abortConfirmStop')}
</button>
</Button>
</div>
</motion.div>
</motion.div>,
Expand Down
16 changes: 7 additions & 9 deletions web-ui/app/_components/store/CredentialsEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
patchInstalledSecrets,
} from '../../_lib/api';
import type { PluginSetupField } from '../../_lib/storeTypes';
import { Button } from '@/app/_components/ui/Button';

interface CredentialsEditorProps {
pluginId: string;
Expand Down Expand Up @@ -354,19 +355,16 @@ export function CredentialsEditor({
</ul>

<div className="flex items-center gap-3">
<button
type="button"
<Button
variant="primary"
onClick={() => void onSave()}
disabled={saving || dirtyCount === 0}
className="inline-flex items-center gap-2 rounded-md bg-[color:var(--accent)] px-3 py-2 text-[12px] font-semibold text-[color:var(--fg-on-dark)] shadow-sm transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40"
busy={saving}
busyLabel={`Speichern${dirtyCount > 0 ? ` (${String(dirtyCount)})` : ''}`}
>
{saving ? (
<span className="lume-busy-dots" aria-hidden />
) : (
<CheckCircle2 className="size-3.5" aria-hidden />
)}
<CheckCircle2 className="size-3.5" aria-hidden />
Speichern{dirtyCount > 0 ? ` (${String(dirtyCount)})` : ''}
</button>
</Button>
{savedAt !== null ? (
<span className="text-[11px] text-[color:var(--muted-ink)]">
✓ Gespeichert
Expand Down
32 changes: 10 additions & 22 deletions web-ui/app/_components/store/EditFromStoreButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { useRouter } from 'next/navigation';
import { AlertTriangle, Pencil } from 'lucide-react';

import { cloneBuilderDraftFromInstalled } from '../../_lib/api';
import { cn } from '../../_lib/cn';
import type { CloneFromInstalledResponse } from '../../_lib/builderTypes';
import { Button } from '@/app/_components/ui/Button';

/**
* Edit-from-Store action (B.6-3). Surfaces on the plugin detail page when
Expand Down Expand Up @@ -71,30 +71,18 @@ export function EditFromStoreButton({

return (
<div className="space-y-2">
<button
type="button"
<Button
variant="secondary"
fullWidth
onClick={() => void handleClick()}
disabled={busy}
className={cn(
'inline-flex w-full items-center justify-center gap-2 rounded-md px-4 py-2',
'text-[12px] font-semibold uppercase tracking-[0.18em]',
'border border-[color:var(--rule-strong)] bg-[color:var(--paper)] text-[color:var(--ink)]',
'transition-colors hover:bg-[color:var(--bg-soft)]',
'disabled:cursor-not-allowed disabled:opacity-60',
)}
busy={busy}
busyLabel="Klone Draft …"
className="text-[12px] uppercase tracking-[0.18em]"
>
{busy ? (
<>
<span className="lume-busy-dots" aria-hidden />
Klone Draft …
</>
) : (
<>
<Pencil className="size-3.5" aria-hidden />
Im Builder bearbeiten
</>
)}
</button>
<Pencil className="size-3.5" aria-hidden />
Im Builder bearbeiten
</Button>
{phase.kind === 'failed' ? (
<div className="rounded-md border border-[color:var(--danger)]/40 bg-[color:var(--danger)]/6 px-3 py-2">
<div className="flex items-start gap-2">
Expand Down
84 changes: 36 additions & 48 deletions web-ui/app/_components/store/InstallButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import type {
InstallChainResolution,
InstallJob,
InstallSetupField,

Check warning on line 29 in web-ui/app/_components/store/InstallButton.tsx

View workflow job for this annotation

GitHub Actions / web-ui (lint + typecheck + vitest)

'InstallSetupField' is defined but never used
InstallValidationError,
LocalizedMarkdown,
} from '../../_lib/storeTypes';
Expand All @@ -34,6 +34,7 @@
import { RequiresWizard } from './RequiresWizard';
import { FieldRow, extractValues } from './setupForm';
import { Markdown } from '../Markdown';
import { Button } from '@/app/_components/ui/Button';

interface InstallButtonProps {
pluginId: string;
Expand Down Expand Up @@ -108,14 +109,12 @@
if (!enabled) {
return (
<div className="flex flex-col gap-3">
<button
type="button"
<Button
variant="secondary"
pill
fullWidth
disabled
className={cn(
'group flex w-full items-center justify-between gap-3 rounded-full px-6 py-3',
'bg-[color:var(--bg-soft)] ring-1 ring-inset ring-[color:var(--border)]',
'cursor-not-allowed text-[color:var(--fg-subtle)]',
)}
className="justify-between gap-3 px-6 py-3 text-[color:var(--fg-subtle)]"
aria-describedby={`install-${pluginId}-reason`}
>
<span className="flex items-center gap-3">
Expand All @@ -124,7 +123,7 @@
Installation blockiert
</span>
</span>
</button>
</Button>
{blockingReasons?.length ? (
<ul
id={`install-${pluginId}-reason`}
Expand All @@ -148,18 +147,13 @@

return (
<>
<button
type="button"
<Button
variant="primary"
pill
fullWidth
onClick={handleOpen}
aria-label={`${pluginName} installieren`}
className={cn(
'group flex w-full items-center justify-between gap-3 rounded-full px-6 py-3',
'bg-[color:var(--accent)] text-[color:var(--accent-fg)] shadow-[var(--shadow-cta)]',
'transition-[background,transform] duration-[var(--dur-fast)] ease-[var(--ease-out)]',
'hover:bg-[color:var(--accent-hover)] active:translate-y-px active:bg-[color:var(--accent-press)]',
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--accent)]',
'disabled:cursor-not-allowed disabled:opacity-70',
)}
className="group justify-between gap-3 px-6 py-3"
disabled={phase.kind === 'creating' || phase.kind === 'success'}
>
<span className="text-[15px] font-semibold">
Expand All @@ -173,7 +167,7 @@
aria-hidden
/>
)}
</button>
</Button>

{drawerOpen ? (
<InstallDrawer
Expand Down Expand Up @@ -426,15 +420,16 @@
{update.from ? `${update.from} → ${update.to}` : `→ ${update.to}`}
</span>
</span>
<button
type="button"
<Button
variant="primary"
pill
onClick={() => void doUpdate()}
disabled={updating}
className="inline-flex items-center gap-2 rounded-full bg-[color:var(--accent)] px-4 py-2 text-[12px] font-semibold text-[color:var(--accent-fg)] disabled:opacity-60"
busy={updating}
busyLabel="Aktualisiere …"
>
{updating ? <span className="lume-busy-dots" aria-hidden /> : null}
{updating ? 'Aktualisiere …' : 'Aktualisieren'}
</button>
Aktualisieren
</Button>
</div>
{updateError ? (
<p className="font-mono-num mt-2 text-[11px] text-[color:var(--danger,#b03030)]">
Expand Down Expand Up @@ -507,22 +502,20 @@
</span>
</label>
<div className="mt-3 flex gap-2">
<button
type="button"
onClick={doUninstall}
className="rounded-full bg-[color:var(--danger,#b03030)] px-4 py-2 text-[12px] font-semibold text-[color:var(--fg-on-dark)]"
>
<Button variant="danger" pill onClick={doUninstall}>
{alsoDeletePackage
? 'Ja, deinstallieren + löschen'
: 'Ja, deinstallieren'}
</button>
<button
type="button"
</Button>
<Button
variant="secondary"
pill
size="sm"
onClick={() => setState({ kind: 'idle' })}
className="rounded-full bg-[color:var(--bg)] px-4 py-2 text-[12px] font-semibold text-[color:var(--fg-muted)] ring-1 ring-inset ring-[color:var(--border)]"
className="font-semibold text-[color:var(--fg-muted)]"
>
Abbrechen
</button>
</Button>
</div>
</div>
)}
Expand Down Expand Up @@ -693,24 +686,19 @@
<br />
Andere Werte landen in der Instanz-Konfiguration.
</p>
<button
<Button
type="submit"
variant="primary"
pill
size="lg"
disabled={submitting}
className={cn(
'inline-flex items-center gap-2 rounded-full px-6 py-3',
'bg-[color:var(--accent)] text-[color:var(--accent-fg)] shadow-[var(--shadow-cta)]',
'transition-[background,transform] duration-[var(--dur-fast)] ease-[var(--ease-out)]',
'hover:bg-[color:var(--accent-hover)] active:translate-y-px',
'disabled:cursor-wait disabled:opacity-70',
)}
busy={submitting}
busyLabel="Wird installiert …"
>
{submitting ? (
<span className="lume-busy-dots" aria-hidden />
) : null}
<span className="text-[15px] font-semibold">
{submitting ? 'Wird installiert …' : 'Installation bestätigen'}
Installation bestätigen
</span>
</button>
</Button>
</footer>
</form>
)}
Expand Down
Loading
Loading