diff --git a/apps/web/e2e/data/admin.mjs b/apps/web/e2e/data/admin.mjs index 058d35875..3d4265b08 100644 --- a/apps/web/e2e/data/admin.mjs +++ b/apps/web/e2e/data/admin.mjs @@ -44,7 +44,7 @@ export function buildAdmin(now, base, resources) { vaults: resources.vaults.filter(of("vault", projectId)), }); - const keyRef = (project, k) => ({ id: k.id, name: k.name, prefix: k.prefix, kind: "issued", revoked_at: k.revoked_at ? iso(k.revoked_at) : null }); + const keyRef = (k) => ({ id: k.id, name: k.name, prefix: k.prefix, kind: "issued", revoked_at: k.revoked_at ? iso(k.revoked_at) : null }); // Deterministic creator per resource: rotate through the project's keys; one in seven is unknown. const creators = new Map(); let turn = 0; @@ -52,7 +52,7 @@ export function buildAdmin(now, base, resources) { const cacheKey = `${type}:${id}`; if (!creators.has(cacheKey)) { turn += 1; - creators.set(cacheKey, turn % 7 === 0 ? null : keyRef(project, project.keys[turn % project.keys.length])); + creators.set(cacheKey, turn % 7 === 0 ? null : keyRef(project.keys[turn % project.keys.length])); } return creators.get(cacheKey); }; @@ -61,7 +61,7 @@ export function buildAdmin(now, base, resources) { const own = collections(project.id); const list = []; const push = (at, action, type, id, parent = "", creatorType = type) => { - const creator = action === "create" ? creatorFor(project, creatorType, id) : keyRef(project, project.keys[list.length % project.keys.length]); + const creator = action === "create" ? creatorFor(project, creatorType, id) : keyRef(project.keys[list.length % project.keys.length]); list.push({ id: `op_${project.id.slice(5)}_${list.length}`, created_at: iso(at), api_key: creator, action, resource_type: type, resource_id: id, parent_id: parent, request_id: `req_${list.length}`, trace_id: `${list.length}`.padStart(32, "0") }); }; for (const agent of own.agents) { push(agent.created_at, "create", "agent", agent.id); if (agent.updated_at > agent.created_at) push(agent.updated_at, "update", "agent", agent.id); } diff --git a/apps/web/e2e/data/routes.mjs b/apps/web/e2e/data/routes.mjs index 535af7a62..c0806d2f8 100644 --- a/apps/web/e2e/data/routes.mjs +++ b/apps/web/e2e/data/routes.mjs @@ -15,7 +15,6 @@ function daemonUrl(publicUrl) { export function buildDemo(now = Math.floor(Date.now() / 1000), publicUrl = "https://core.example.com") { seed = 42; - const models = ["gpt-5.1-codex", "claude-sonnet-5", "MiniMax-M2", "gpt-5.1-mini"]; const agents = agentDefinitions.map((definition, index) => ({ id: `agent_${index + 1}${uuid().slice(0, 6)}`, object: "agent", model: definition.model, name: definition.name, instructions: definition.instructions, metadata: definition.metadata, multi_agent: { enabled: false, max_concurrent_subagents: null }, reasoning: definition.reasoning, service_tier: "auto", diff --git a/apps/web/e2e/fixture-console.mjs b/apps/web/e2e/fixture-console.mjs index 1f2513802..c5a67bea7 100644 --- a/apps/web/e2e/fixture-console.mjs +++ b/apps/web/e2e/fixture-console.mjs @@ -533,7 +533,7 @@ const tokenLimit = (value) => value === undefined || (Number.isInteger(value) && const jsonKind = (value) => Array.isArray(value) ? "an array" : typeof value === "string" ? "a string" : typeof value === "boolean" ? "a boolean" : Number.isInteger(value) ? "an integer" : "a number"; /** The first rule a provider body breaks, in Core's order and words; null when valid. */ -function providerProblem(harness, input) { +function providerProblem(input) { if (Object.keys(input).some((key) => !PROVIDER_FIELDS.has(key)) || ["protocol", "base_url", "api_key"].some((key) => typeof input[key] !== "string") || !tokenLimit(input.context_window) || !tokenLimit(input.max_output_tokens)) return PROVIDER_SHAPE; if (!httpsBase(input.base_url)) return "model provider requires an HTTPS base_url without credentials, query or fragment"; @@ -581,7 +581,7 @@ async function harnessRoute(request, response, path) { if (typeof input.model !== "string" || !input.model.trim()) return error(response, 400, "A model ID is required.", "model_configuration_model_invalid"); if (input.harness_config !== undefined && (!input.harness_config || typeof input.harness_config !== "object" || Array.isArray(input.harness_config))) return error(response, 400, "Harness configuration must be an object.", "harness_config_invalid"); if (!support(harness).protocols.includes(input.model_provider?.protocol)) return error(response, 400, "This harness does not support this protocol.", "model_provider_protocol_unsupported"); - const problem = providerProblem(harness, input.model_provider ?? {}); + const problem = providerProblem(input.model_provider ?? {}); if (problem) return error(response, 400, problem, "invalid_request_error"); entry.provider = { object: "core.model_configuration", harness, model: input.model, harness_config: input.harness_config ?? {}, diff --git a/apps/web/package.json b/apps/web/package.json index 10e47110a..a2257a27c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,7 +17,6 @@ "@number-flow/react": "^0.6.2", "@oac/agents-client": "workspace:*", "@tanstack/react-query": "^5.103.2", - "@tanstack/react-table": "^8.21.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "i18next": "^26.4.2", diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index 934f1cf02..d7b40b9b9 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -26,9 +26,6 @@ importers: '@tanstack/react-query': specifier: ^5.103.2 version: 5.103.2(react@19.3.0) - '@tanstack/react-table': - specifier: ^8.21.3 - version: 8.21.3(react-dom@19.3.0(react@19.3.0))(react@19.3.0) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -541,17 +538,6 @@ packages: peerDependencies: react: ^18 || ^19 - '@tanstack/react-table@8.21.3': - resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} - engines: {node: '>=12'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' - - '@tanstack/table-core@8.21.3': - resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} - engines: {node: '>=12'} - '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1694,14 +1680,6 @@ snapshots: '@tanstack/query-core': 5.103.2 react: 19.3.0 - '@tanstack/react-table@8.21.3(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': - dependencies: - '@tanstack/table-core': 8.21.3 - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) - - '@tanstack/table-core@8.21.3': {} - '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 diff --git a/apps/web/src/ConsoleApp.tsx b/apps/web/src/ConsoleApp.tsx index b9ce5881e..8e55c738e 100644 --- a/apps/web/src/ConsoleApp.tsx +++ b/apps/web/src/ConsoleApp.tsx @@ -19,7 +19,7 @@ import { SessionPage } from "./features/sessions/SessionPage"; import { SkillsPage } from "./features/skills/SkillsPage"; import { SettingsPage } from "./features/settings/SettingsPage"; import { VaultsPage } from "./features/vaults/VaultsPage"; -import { consoleDepth, ConsoleNavigationContext, useConsoleNavigation, hashWithParams, routeParamsFromHash, type ConsoleIntent, type RouteParams } from "./lib/console-navigation"; +import { consoleDepth, ConsoleNavigationContext, hashWithParams, routeParamsFromHash, type ConsoleIntent, type RouteParams } from "./lib/console-navigation"; import { consoleHashForView, consoleNavParent, consoleViewFromHash, type ConsoleView } from "./lib/console-routes"; import { ProjectsProvider, useProjects } from "./lib/projects"; import { collectionQuery, collections, filesCollection, queryClient, type CollectionSpec } from "./lib/queries"; diff --git a/apps/web/src/app/beautifui/foundation.css b/apps/web/src/app/beautifui/foundation.css index eb9a3b892..8d2a26ce0 100644 --- a/apps/web/src/app/beautifui/foundation.css +++ b/apps/web/src/app/beautifui/foundation.css @@ -211,30 +211,6 @@ a { text-decoration-skip-ink: auto; } -/* theme switches swap every token at once — freeze transitions so the flip - * is one clean repaint instead of hundreds of mismatched color fades */ -.theme-switching *, -.theme-switching *::before, -.theme-switching *::after { - transition: none !important; -} - -/* touch: :hover sticks after a tap, so neutralize row/header hovers there */ -@media (hover: none) { - .records-row:hover > .records-cell { - background: transparent; - } - - .records-row:hover > td.is-colsel { - background: color-mix(in srgb, var(--accent) 4%, var(--surface)); - } - - .records-header-button:hover { - background: transparent; - color: var(--ink-2); - } -} - /* Tailwind v4 resets buttons to cursor:default — anything * clickable must invite the click */ button:not(:disabled), @@ -258,73 +234,6 @@ select:focus-visible, outline: none; } -.animated-underline { - position: relative; - display: inline-block; -} - -.animated-underline::after { - position: absolute; - right: 0; - bottom: -1px; - left: 0; - height: 1px; - content: ""; - background: currentColor; - transform: scaleX(0); - transform-origin: left center; - transition: transform 280ms var(--ease-link); -} - -a:hover .animated-underline::after, -a:focus-visible .animated-underline::after { - transform: scaleX(1); -} - -/* ══════════════════════════════════════════════════════════ - * COMPONENT-SPECIFIC — StreamingText / ContextCards - * Favicon source avatars. - * Copy this block only alongside StreamingText / ContextCards; it is not part of the - * generic foundation above. - * ══════════════════════════════════════════════════════════ */ -.source-avatar { - box-shadow: 0 0 0 1px oklch(0.21 0.034 263.436 / 0.1); -} - -.dark .source-avatar { - box-shadow: 0 0 0 1px oklch(1 0 0 / 0.08); -} - -/* ── primitive spacing system ───────────────────────────── - * Shared optical offsets for small demo cards. Use these - * instead of hand-tuning 10/12/14px per component. - */ - -.primitive-card-pad { - padding: 12px; -} - -.primitive-card-bar { - padding: 10px 12px; -} - -.primitive-card-footer { - padding: 10px; -} - -.primitive-table-cell { - padding: 10px 12px; -} - -.primitive-icon-button { - display: inline-flex; - width: 28px; - height: 28px; - align-items: center; - justify-content: center; - border-radius: var(--radius-control); -} - /* ── shared keyframes ─────────────────────────────────── */ @keyframes shimmer-text { @@ -347,114 +256,6 @@ a:focus-visible .animated-underline::after { } } -.home-reveal { - will-change: opacity, transform, filter; -} - - -/* ── filter table electric status colors ─────────────────── */ - -/* ══════════════════════════════════════════════════════════ - * COMPONENT-SPECIFIC — FilterTable - * Electric status-pill colors. - * Copy this block only alongside FilterTable; it is not part of the - * generic foundation above. - * ══════════════════════════════════════════════════════════ */ -.filter-status-todo, -.filter-status-progress, -.filter-status-done { - border: 1px solid transparent; -} - -/* ══════════════════════════════════════════════════════════ - * COMPONENT-SPECIFIC — InsightCards - * Liveline chart tooltip + cursor. - * Copy this block only alongside InsightCards; it is not part of the - * generic foundation above. - * ══════════════════════════════════════════════════════════ */ -.insight-chart-stage { - overflow: hidden; -} - -.insight-chart-cursor { - position: absolute; - z-index: 4; - top: 0; - bottom: 0; - width: 1px; - background: var(--ink); - opacity: 0.26; - pointer-events: none; -} - -.insight-chart-tooltip-anchor { - position: absolute; - z-index: 5; - top: 8px; - transform: translateX(-50%); - pointer-events: none; -} - -.insight-chart-tooltip { - display: flex; - align-items: center; - gap: 12px; - padding: 5px 9px; - border: 1px solid var(--line-strong); - border-radius: 8px; - color: var(--tooltip-fg); - background: var(--tooltip-bg); - box-shadow: var(--shadow-overlay); - font-size: 11.5px; - font-variant-numeric: tabular-nums; - white-space: nowrap; -} - -.insight-chart-tooltip-item { - display: inline-flex; - align-items: center; - gap: 6px; -} - -.insight-chart-tooltip-dot { - width: 8px; - height: 8px; - flex: 0 0 8px; - border-radius: 50%; -} - -/* Status pills share the same base-hue → color-mix derivation as .records-tag, - * so both components stay consistent across light and dark. */ -.filter-status-todo { - --tag-base: oklch(0.757 0.153 66.401); -} -.filter-status-progress { - --tag-base: oklch(0.671 0.118 219.351); -} -.filter-status-done { - --tag-base: oklch(0.652 0.131 162.865); -} - -.filter-status-todo, -.filter-status-progress, -.filter-status-done { - color: color-mix(in srgb, var(--tag-base) 92%, var(--ink)); - background: color-mix(in srgb, var(--tag-base) 20%, var(--surface)); - border-color: color-mix(in srgb, var(--tag-base) 34%, var(--surface)); -} - -/* Dark mode — a richer, saturated same-hue fill - * with bright same-hue text and no visible border (light mode keeps the soft - * tinted pastels above). Applies to both the records grid and status pills. */ -.dark .records-tag, -.dark .filter-status-todo, -.dark .filter-status-progress, -.dark .filter-status-done { - color: color-mix(in srgb, var(--tag-base) 86%, white); - background: color-mix(in srgb, var(--tag-base) 34%, var(--surface)); - border-color: color-mix(in srgb, var(--tag-base) 34%, var(--surface)); -} - @keyframes fade-in { from { opacity: 0; @@ -474,39 +275,6 @@ a:focus-visible .animated-underline::after { } } -/* ── StreamText primitive ───────────────────────────────── - * Fast character reveal: the newest few characters carry a - * soft, masked blur so the leading edge resolves smoothly as - * text flows in. The caret holds solid while streaming, then - * blinks once the string settles. */ -/* ══════════════════════════════════════════════════════════ - * COMPONENT-SPECIFIC — StreamText - * The streaming caret + tail. - * Copy this block only alongside StreamText; it is not part of the - * generic foundation above. - * ══════════════════════════════════════════════════════════ */ -.stream-tail { - filter: blur(1.6px); - -webkit-mask-image: linear-gradient(to right, oklch(0 0 0) 20%, oklch(0 0 0 / 0.2)); - mask-image: linear-gradient(to right, oklch(0 0 0) 20%, oklch(0 0 0 / 0.2)); -} - -.stream-caret { - display: inline-block; - width: 2px; - height: 1.05em; - margin-left: 1.5px; - translate: 0 -0.5px; - border-radius: 1px; - background: var(--ink); - vertical-align: text-bottom; - animation: caret-blink 1s step-end infinite; -} - -.stream-caret.is-streaming { - animation: none; -} - @keyframes caret-blink { 0%, 100% { @@ -517,17 +285,6 @@ a:focus-visible .animated-underline::after { } } -@media (prefers-reduced-motion: reduce) { - .stream-caret { - animation: none; - } - .stream-tail { - filter: none; - -webkit-mask-image: none; - mask-image: none; - } -} - @keyframes pop-in { from { opacity: 0; diff --git a/apps/web/src/components/atoms/Button.tsx b/apps/web/src/components/atoms/Button.tsx deleted file mode 100644 index af0bfca1b..000000000 --- a/apps/web/src/components/atoms/Button.tsx +++ /dev/null @@ -1,47 +0,0 @@ -"use client"; - -import { ButtonHTMLAttributes } from "react"; -import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils"; - -const filledShadow = "shadow-[inset_0_1px_0_rgba(255,255,255,0.14)]"; - -/* Pill-shaped by default — the app's core button style. Explicit symmetric - * padding (not a fixed height) so the top/bottom spacing is always equal. */ -export const buttonVariants = cva( - `inline-flex items-center justify-center font-medium select-none - transition-[transform,background-color,opacity] duration-150 ease-out - active:scale-[0.96] disabled:opacity-50 disabled:pointer-events-none`, - { - variants: { - variant: { - primary: `bg-ink text-canvas hover:opacity-90 dark:bg-ink dark:text-canvas ${filledShadow}`, - secondary: "bg-surface text-ink shadow-btn hover:bg-inset aria-expanded:bg-hover", - ghost: "bg-hover-2 text-ink hover:bg-line-strong", - accent: `bg-accent text-white hover:bg-accent-ink ${filledShadow}`, - success: `bg-green text-white hover:brightness-95 ${filledShadow}`, - /* transparent until hovered — for dense toolbars/action rows */ - quiet: "text-ink hover:bg-hover", - }, - size: { - /* compact toolbar pill — fixed height, lighter weight */ - xs: "h-7 rounded-full px-2.5 text-[12px] font-normal leading-none gap-1", - /* canonical action pill — 27px tall, roomy sides */ - sm: "h-[27px] px-3 text-[13px] leading-none rounded-full gap-1.5", - md: "px-4 py-[9px] text-sm leading-none rounded-full gap-2", - }, - }, - defaultVariants: { variant: "secondary", size: "md" }, - }, -); - -export type ButtonVariant = NonNullable["variant"]>; - -export function Button({ - variant, - size, - className, - ...props -}: ButtonHTMLAttributes & VariantProps) { - return