Files
Innei 3d751686af ✨ feat(acceptance): restore in-app workspace and assemble the public page from atoms (#18501)
* ✨ feat(acceptance): restore in-app workspace and assemble the public page from atoms

Keep the full owner workspace in the main SPA and compose the workbench share page from read-only capability atoms so owner actions stay out of the public bundle.

* 📝 docs(compose-atoms): generalize the skill around grain and sink-state

Make the kit apply to any heavy domain feature, and keep focus review filling the in-app frame.

* ⚡ perf(workbench): slim the public page and stop shipping shiki in the bundle

Keep the share surface to identity only, shim the browser lambda client to cookies, stub shiki on SSR, and load grammars from esm.sh on the client.

* ✨ feat(acceptance): show goal and check inventory on workbench detail

* 🐛 fix(acceptance): handle null acceptanceId in useAcceptanceBundle

Signed-off-by: Innei <tukon479@gmail.com>

* ✨ feat(workbench): put a go-to-app CTA on the public header

* ✅ test(workbench): fail the build when the graph uses stub-missing APIs

Keep workbench stubs as an explicit surface and scan the module graph on
build:rr so a later call to a simplified-away method is a build failure.

* 🐛 fix(acceptance): type verdict helpers with TFunction

Database CI lint failed because useTranslation('verify') is not assignable
to the hand-written (key: string) => string helper signatures.

---------

Signed-off-by: Innei <tukon479@gmail.com>
2026-08-20 16:45:21 +08:00

133 lines
3.4 KiB
TypeScript

import { useTheme } from 'next-themes';
import type { ComponentType, CSSProperties, ReactElement } from 'react';
import { lazy, Suspense, useEffect } from 'react';
import type { RouteObject } from 'react-router';
import { Outlet, useRouteError } from 'react-router';
import { isChunkLoadError, notifyChunkError } from '@/utils/chunkError';
import WorkbenchShell, { WorkbenchNamespace } from './shell';
import WorkbenchLoading from './shell/WorkbenchLoading';
const lazyElement = (importFn: () => Promise<{ default: ComponentType }>): ReactElement => {
const LazyComponent = lazy(importFn);
return <LazyComponent />;
};
const buttonStyle: CSSProperties = {
background: 'transparent',
border: '1px solid currentcolor',
borderRadius: 6,
color: 'inherit',
cursor: 'pointer',
font: 'inherit',
padding: '6px 16px',
};
const WorkbenchErrorBoundary = () => {
const error = useRouteError() as Error;
const { resolvedTheme } = useTheme();
if (typeof window !== 'undefined' && isChunkLoadError(error)) notifyChunkError();
const isDark = resolvedTheme === 'dark';
return (
<div
style={{
alignItems: 'center',
background: isDark ? '#000' : '#f8f8f8',
color: isDark ? '#e6e6e6' : '#1a1a1a',
display: 'flex',
flexDirection: 'column',
fontFamily: 'sans-serif',
gap: 16,
justifyContent: 'center',
minHeight: '100dvh',
padding: 16,
}}
>
<h2 style={{ margin: 0 }}>Something went wrong</h2>
<div style={{ display: 'flex', gap: 12 }}>
<button style={buttonStyle} type={'button'} onClick={() => window.location.reload()}>
Retry
</button>
<button style={buttonStyle} type={'button'} onClick={() => window.location.assign('/')}>
Back
</button>
</div>
</div>
);
};
const ExitWorkbench = () => {
useEffect(() => {
window.location.replace('/');
}, []);
return <WorkbenchLoading />;
};
const VerifyNamespace = () => (
<WorkbenchNamespace namespace="verify">
<Outlet />
</WorkbenchNamespace>
);
export const workbenchRoutes: RouteObject[] = [
{
children: [
{
element: lazyElement(() => import('./routes/agent/docs/[docId]')),
path: 'agent/:aid/docs/:docId',
},
{
children: [
{
element: <ExitWorkbench />,
index: true,
},
{
element: lazyElement(() => import('./routes/acceptance/[acceptanceId]')),
path: ':acceptanceId',
},
{
element: lazyElement(() => import('./routes/acceptance/[acceptanceId]')),
path: ':acceptanceId/check/:checkId',
},
],
element: <VerifyNamespace />,
path: 'acceptance',
},
{
children: [
{
element: lazyElement(() => import('./routes/verify')),
index: true,
},
{
element: lazyElement(() => import('./routes/verify/[runId]')),
path: ':runId',
},
],
element: <VerifyNamespace />,
path: 'verify',
},
{
element: <ExitWorkbench />,
path: '*',
},
],
element: (
<WorkbenchShell>
<Suspense fallback={<WorkbenchLoading />}>
<Outlet />
</Suspense>
</WorkbenchShell>
),
errorElement: <WorkbenchErrorBoundary />,
path: '/',
},
];