diff --git a/.gitignore b/.gitignore
index 08353997..aeb005d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
backend/.venv/
+node_modules/
frontend/.next/
frontend/.next-dev-log/
frontend/node_modules/
diff --git a/apps/contract-web/.env.example b/apps/contract-web/.env.example
new file mode 100644
index 00000000..d87fd5e8
--- /dev/null
+++ b/apps/contract-web/.env.example
@@ -0,0 +1 @@
+NEXT_PUBLIC_CONTRACT_API_BASE_URL=http://localhost:8100
diff --git a/apps/contract-web/.gitkeep b/apps/contract-web/.gitkeep
deleted file mode 100644
index 8b137891..00000000
--- a/apps/contract-web/.gitkeep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/apps/contract-web/README.md b/apps/contract-web/README.md
new file mode 100644
index 00000000..c08128e9
--- /dev/null
+++ b/apps/contract-web/README.md
@@ -0,0 +1,19 @@
+# Contract Web
+
+Next.js app for the V2 contract platform.
+
+## Local Run
+
+```powershell
+npm install
+npm run web:dev
+```
+
+Default URL: `http://localhost:3000`
+
+## Validate
+
+```powershell
+npm run web:typecheck
+npm run web:build
+```
diff --git a/apps/contract-web/next.config.ts b/apps/contract-web/next.config.ts
new file mode 100644
index 00000000..ce199092
--- /dev/null
+++ b/apps/contract-web/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ transpilePackages: ["@yuqei/design-system"]
+};
+
+export default nextConfig;
diff --git a/apps/contract-web/package.json b/apps/contract-web/package.json
new file mode 100644
index 00000000..434698ae
--- /dev/null
+++ b/apps/contract-web/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "@yuqei/contract-web",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev -p 3000",
+ "build": "next build",
+ "start": "next start -p 3000",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@yuqei/design-system": "0.1.0",
+ "lucide-react": "1.21.0",
+ "next": "16.2.9",
+ "react": "19.2.7",
+ "react-dom": "19.2.7"
+ },
+ "devDependencies": {
+ "@types/node": "26.0.0",
+ "@types/react": "19.2.17",
+ "@types/react-dom": "19.2.3",
+ "typescript": "5.9.3"
+ }
+}
diff --git a/apps/contract-web/src/app/admin/page.tsx b/apps/contract-web/src/app/admin/page.tsx
new file mode 100644
index 00000000..0df88a02
--- /dev/null
+++ b/apps/contract-web/src/app/admin/page.tsx
@@ -0,0 +1,47 @@
+import { Settings, ShieldCheck, SlidersHorizontal } from "lucide-react";
+
+const adminSections = [
+ {
+ title: "企业权限",
+ description: "组织、角色、合同可见范围和操作边界。",
+ icon: ShieldCheck
+ },
+ {
+ title: "AI 参数",
+ description: "模型连接、运行参数、提示词版本和审计策略。",
+ icon: SlidersHorizontal
+ },
+ {
+ title: "系统设置",
+ description: "文件存储、通知、备份、健康检查和部署配置。",
+ icon: Settings
+ }
+];
+
+export default function AdminPage() {
+ return (
+ <>
+
+
+
管理区
+
管理员能力集中管理,业务用户保持简洁主流程。
+
+
+
+
+ {adminSections.map((section) => {
+ const Icon = section.icon;
+ return (
+
+
+
+
{section.title}
+
{section.description}
+
+
+ );
+ })}
+
+ >
+ );
+}
diff --git a/apps/contract-web/src/app/contracts/[id]/page.tsx b/apps/contract-web/src/app/contracts/[id]/page.tsx
new file mode 100644
index 00000000..1bef89fe
--- /dev/null
+++ b/apps/contract-web/src/app/contracts/[id]/page.tsx
@@ -0,0 +1,146 @@
+import Link from "next/link";
+import { Archive, Download, FilePenLine, GitCompare, Send } from "lucide-react";
+import { contracts } from "@/lib/mock-data";
+
+type PageProps = {
+ params: Promise<{
+ id: string;
+ }>;
+};
+
+export default async function ContractDetailPage({ params }: PageProps) {
+ const { id } = await params;
+ const contract = contracts.find((item) => item.id === id) ?? contracts[0];
+
+ return (
+ <>
+
+
+
{contract.title}
+
{contract.code} · {contract.counterparty} · 更新时间 {contract.updatedAt}
+
+
+ 返回合同列表
+
+
+
+
+
+ 下一步操作:{contract.nextAction}
+ 只把当前最应该做的动作放到首位,其余能力继续保留但不抢视线。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
合同概览
+ {contract.status}
+
+
+
+
合同金额
+
{contract.amount}
+
+
+
业务负责人
+
{contract.owner}
+
+
+
相对方
+
{contract.counterparty}
+
+
+
下一步
+
{contract.nextAction}
+
+
+
+
+
+
+
正文预览
+
+
+
+
+ {contract.title}
+ 甲方与乙方基于真实交易背景,经平等协商,形成本合同。双方应按约定履行交付、验收、付款、保密和违约责任。
+ 法律审查意见、审批意见、版本差异和履约证据会在侧栏集中折叠展示,正文区域保持干净可读。
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/contract-web/src/app/contracts/page.tsx b/apps/contract-web/src/app/contracts/page.tsx
new file mode 100644
index 00000000..52a00eec
--- /dev/null
+++ b/apps/contract-web/src/app/contracts/page.tsx
@@ -0,0 +1,64 @@
+import Link from "next/link";
+import { Download, FilePlus2, Search } from "lucide-react";
+import { contracts } from "@/lib/mock-data";
+
+export default function ContractsPage() {
+ return (
+ <>
+
+
+
合同
+
按下一步动作组织合同,而不是把所有流程一次性压给用户。
+
+
+
+ 快速起草
+
+
+
+
+
+
+
+
+
+
+
+ | 合同 |
+ 相对方 |
+ 状态 |
+ 下一步 |
+ 更新时间 |
+ 操作 |
+
+
+
+ {contracts.map((contract) => (
+
+ |
+
+ {contract.title}
+
+ {contract.code}
+ |
+ {contract.counterparty} |
+
+ {contract.status}
+ |
+ {contract.nextAction} |
+ {contract.updatedAt} |
+
+
+ |
+
+ ))}
+
+
+ >
+ );
+}
diff --git a/apps/contract-web/src/app/files/page.tsx b/apps/contract-web/src/app/files/page.tsx
new file mode 100644
index 00000000..756f5570
--- /dev/null
+++ b/apps/contract-web/src/app/files/page.tsx
@@ -0,0 +1,67 @@
+import Link from "next/link";
+import { Download, Eye, Filter } from "lucide-react";
+import { files } from "@/lib/mock-data";
+
+export default function FilesPage() {
+ return (
+ <>
+
+
+
合同仓库
+
按文件、关联合同、类型和上传时间统一管理合同资产。
+
+
+
+
+
+
+
+
+
+
+
+
+ | 文件 |
+ 关联合同 |
+ 类型 |
+ 上传时间 |
+ 操作 |
+
+
+
+ {files.map((file) => (
+
+ |
+ {file.name}
+ {file.size}
+ |
+
+
+ {file.contractTitle}
+
+ |
+ {file.type} |
+ {file.uploadedAt} |
+
+
+
+
+
+ |
+
+ ))}
+
+
+ >
+ );
+}
diff --git a/apps/contract-web/src/app/globals.css b/apps/contract-web/src/app/globals.css
new file mode 100644
index 00000000..d27ab18b
--- /dev/null
+++ b/apps/contract-web/src/app/globals.css
@@ -0,0 +1,504 @@
+:root {
+ color-scheme: light;
+ --ink: #172033;
+ --muted: #667085;
+ --line: #d9e0ea;
+ --canvas: #f6f8fb;
+ --surface: #ffffff;
+ --primary: #2457d6;
+ --primary-soft: #eaf0ff;
+ --success: #16794c;
+ --success-soft: #e7f6ef;
+ --warning: #b35b00;
+ --warning-soft: #fff3df;
+ --danger: #bd2d2d;
+ --danger-soft: #fdecec;
+ --accent: #0b7a75;
+ --accent-soft: #e4f5f3;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body {
+ min-height: 100%;
+ margin: 0;
+}
+
+body {
+ color: var(--ink);
+ background: var(--canvas);
+ font-family: Inter, "Segoe UI", "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+button,
+input,
+select {
+ font: inherit;
+}
+
+.app-shell {
+ display: grid;
+ grid-template-columns: 248px minmax(0, 1fr);
+ min-height: 100vh;
+}
+
+.app-sidebar {
+ position: sticky;
+ top: 0;
+ height: 100vh;
+ padding: 20px 16px;
+ color: #f8fbff;
+ background: #16233f;
+ border-right: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+.brand-block {
+ display: grid;
+ gap: 4px;
+ padding: 4px 8px 18px;
+}
+
+.brand-title {
+ font-size: 18px;
+ font-weight: 750;
+ letter-spacing: 0;
+}
+
+.brand-subtitle {
+ color: #bac6d9;
+ font-size: 12px;
+}
+
+.nav-group {
+ display: grid;
+ gap: 6px;
+ margin-top: 12px;
+}
+
+.nav-group-title {
+ margin: 18px 8px 8px;
+ color: #91a1bc;
+ font-size: 12px;
+}
+
+.nav-link {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ min-height: 40px;
+ padding: 0 10px;
+ border-radius: 8px;
+ color: #d8e2f2;
+}
+
+.nav-link svg {
+ flex: 0 0 auto;
+}
+
+.nav-link:hover,
+.nav-link-active {
+ color: #ffffff;
+ background: rgba(255, 255, 255, 0.12);
+}
+
+.app-main {
+ display: grid;
+ grid-template-rows: 64px minmax(0, 1fr);
+ min-width: 0;
+}
+
+.topbar {
+ position: sticky;
+ z-index: 10;
+ top: 0;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ min-width: 0;
+ height: 64px;
+ padding: 0 28px;
+ background: rgba(246, 248, 251, 0.92);
+ border-bottom: 1px solid var(--line);
+ backdrop-filter: blur(12px);
+}
+
+.topbar-title {
+ overflow: hidden;
+ font-size: 16px;
+ font-weight: 700;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.topbar-actions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.content {
+ width: min(100%, 1440px);
+ margin: 0 auto;
+ padding: 24px 28px 40px;
+}
+
+.page-heading {
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 20px;
+ margin-bottom: 20px;
+}
+
+.page-heading h1 {
+ margin: 0;
+ font-size: 28px;
+ line-height: 1.2;
+ letter-spacing: 0;
+}
+
+.page-heading p {
+ margin: 6px 0 0;
+ color: var(--muted);
+ line-height: 1.6;
+}
+
+.button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ min-height: 36px;
+ padding: 0 14px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ color: var(--ink);
+ background: var(--surface);
+ cursor: pointer;
+}
+
+.button-primary {
+ color: #ffffff;
+ border-color: var(--primary);
+ background: var(--primary);
+}
+
+.button-soft {
+ color: var(--primary);
+ border-color: transparent;
+ background: var(--primary-soft);
+}
+
+.icon-button {
+ width: 36px;
+ min-width: 36px;
+ padding: 0;
+}
+
+.panel {
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--surface);
+}
+
+.panel-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 16px 18px;
+ border-bottom: 1px solid var(--line);
+}
+
+.panel-title {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 750;
+}
+
+.panel-body {
+ padding: 18px;
+}
+
+.split-layout {
+ display: grid;
+ grid-template-columns: minmax(0, 1.4fr) minmax(320px, 0.7fr);
+ gap: 16px;
+}
+
+.metric-row {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 12px;
+ margin-bottom: 16px;
+}
+
+.metric {
+ padding: 16px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--surface);
+}
+
+.metric-value {
+ font-size: 24px;
+ font-weight: 800;
+}
+
+.metric-label {
+ margin-top: 4px;
+ color: var(--muted);
+ font-size: 13px;
+}
+
+.list {
+ display: grid;
+ gap: 10px;
+}
+
+.list-item {
+ display: grid;
+ gap: 8px;
+ padding: 14px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--surface);
+}
+
+.list-item-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.item-title {
+ min-width: 0;
+ font-weight: 700;
+}
+
+.item-title svg {
+ vertical-align: -3px;
+}
+
+.item-meta {
+ color: var(--muted);
+ font-size: 13px;
+}
+
+.toolbar {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 14px;
+}
+
+.search-input {
+ min-width: min(100%, 360px);
+ min-height: 38px;
+ padding: 0 12px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: #ffffff;
+}
+
+.tag {
+ display: inline-flex;
+ align-items: center;
+ width: fit-content;
+ min-height: 24px;
+ padding: 0 8px;
+ border-radius: 999px;
+ color: var(--muted);
+ background: #edf1f7;
+ font-size: 12px;
+ white-space: nowrap;
+}
+
+.tag-success {
+ color: var(--success);
+ background: var(--success-soft);
+}
+
+.tag-warning {
+ color: var(--warning);
+ background: var(--warning-soft);
+}
+
+.tag-primary {
+ color: var(--primary);
+ background: var(--primary-soft);
+}
+
+.tag-danger {
+ color: var(--danger);
+ background: var(--danger-soft);
+}
+
+.flow-strip {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+ gap: 10px;
+}
+
+.flow-step {
+ padding: 14px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: #fbfcfe;
+}
+
+.flow-step-title {
+ font-weight: 750;
+}
+
+.flow-step p {
+ margin: 6px 0 0;
+ color: var(--muted);
+ font-size: 13px;
+ line-height: 1.5;
+}
+
+.next-action {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 14px;
+ padding: 16px 18px;
+ border: 1px solid #bfd0ff;
+ border-radius: 8px;
+ background: #f5f8ff;
+}
+
+.next-action strong {
+ display: block;
+ margin-bottom: 4px;
+}
+
+.muted {
+ color: var(--muted);
+}
+
+.grid-two {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 16px;
+}
+
+.detail-grid {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 320px;
+ gap: 16px;
+ margin-top: 16px;
+}
+
+.document-preview {
+ min-height: 420px;
+ padding: 28px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: #ffffff;
+ line-height: 1.8;
+}
+
+.document-preview h2 {
+ margin: 0 0 16px;
+ font-size: 22px;
+ text-align: center;
+}
+
+.table {
+ width: 100%;
+ border-collapse: collapse;
+ overflow: hidden;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ background: var(--surface);
+}
+
+.table th,
+.table td {
+ padding: 12px 14px;
+ border-bottom: 1px solid var(--line);
+ text-align: left;
+ vertical-align: middle;
+}
+
+.table th {
+ color: var(--muted);
+ font-size: 13px;
+ font-weight: 650;
+ background: #f8fafc;
+}
+
+.table tr:last-child td {
+ border-bottom: 0;
+}
+
+details.panel summary {
+ cursor: pointer;
+ list-style: none;
+}
+
+details.panel summary::-webkit-details-marker {
+ display: none;
+}
+
+@media (max-width: 980px) {
+ .app-shell {
+ grid-template-columns: 1fr;
+ }
+
+ .app-sidebar {
+ position: static;
+ height: auto;
+ }
+
+ .nav-group {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .split-layout,
+ .detail-grid,
+ .grid-two {
+ grid-template-columns: 1fr;
+ }
+
+ .metric-row,
+ .flow-strip {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 640px) {
+ .content,
+ .topbar {
+ padding-left: 16px;
+ padding-right: 16px;
+ }
+
+ .page-heading {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .metric-row,
+ .flow-strip,
+ .nav-group {
+ grid-template-columns: 1fr;
+ }
+
+ .topbar-actions {
+ display: none;
+ }
+}
diff --git a/apps/contract-web/src/app/health/page.tsx b/apps/contract-web/src/app/health/page.tsx
new file mode 100644
index 00000000..0746fc6d
--- /dev/null
+++ b/apps/contract-web/src/app/health/page.tsx
@@ -0,0 +1,15 @@
+import { SystemHealthPanel } from "@/features/system/system-health-panel";
+
+export default function HealthPage() {
+ return (
+ <>
+
+
+
系统健康
+
上线前检查前端、合同 API、AI 平台和后台任务状态。
+
+
+
+ >
+ );
+}
diff --git a/apps/contract-web/src/app/layout.tsx b/apps/contract-web/src/app/layout.tsx
new file mode 100644
index 00000000..54691cfe
--- /dev/null
+++ b/apps/contract-web/src/app/layout.tsx
@@ -0,0 +1,23 @@
+import type { Metadata } from "next";
+import type { ReactNode } from "react";
+import { AppShell } from "@/components/app-shell";
+import "./globals.css";
+
+export const metadata: Metadata = {
+ title: "钰麒合同 V2",
+ description: "企业合同智能起草、审查、协同与归档平台"
+};
+
+export default function RootLayout({
+ children
+}: Readonly<{
+ children: ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/apps/contract-web/src/app/page.tsx b/apps/contract-web/src/app/page.tsx
new file mode 100644
index 00000000..bd1571ec
--- /dev/null
+++ b/apps/contract-web/src/app/page.tsx
@@ -0,0 +1,5 @@
+import { WorkspacePage } from "@/features/workspace/workspace-page";
+
+export default function Home() {
+ return ;
+}
diff --git a/apps/contract-web/src/app/search/page.tsx b/apps/contract-web/src/app/search/page.tsx
new file mode 100644
index 00000000..0ae2542e
--- /dev/null
+++ b/apps/contract-web/src/app/search/page.tsx
@@ -0,0 +1,46 @@
+import Link from "next/link";
+import { Search } from "lucide-react";
+import { searchGroups } from "@/lib/mock-data";
+
+export default function SearchPage() {
+ return (
+ <>
+
+
+
统一搜索
+
标题就是主入口,结果按合同、审查、文件、模板分组展示。
+
+
+
+
+
+
+
+
+
+ {searchGroups.map((group) => (
+
+
+
{group.title}
+ {group.items.length} 条
+
+
+ {group.items.map((item) => (
+
+
+ {item.title}
+ {item.updatedAt}
+
+
{item.summary}
+
+ ))}
+
+
+ ))}
+
+ >
+ );
+}
diff --git a/apps/contract-web/src/app/workspace/page.tsx b/apps/contract-web/src/app/workspace/page.tsx
new file mode 100644
index 00000000..76beca9b
--- /dev/null
+++ b/apps/contract-web/src/app/workspace/page.tsx
@@ -0,0 +1,5 @@
+import { WorkspacePage } from "@/features/workspace/workspace-page";
+
+export default function Page() {
+ return ;
+}
diff --git a/apps/contract-web/src/components/app-shell.tsx b/apps/contract-web/src/components/app-shell.tsx
new file mode 100644
index 00000000..7bad7b9d
--- /dev/null
+++ b/apps/contract-web/src/components/app-shell.tsx
@@ -0,0 +1,97 @@
+"use client";
+
+import type { ReactNode } from "react";
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import {
+ Activity,
+ Archive,
+ FileSearch,
+ Files,
+ Home,
+ LayoutDashboard,
+ Search,
+ Settings
+} from "lucide-react";
+
+const mainNav = [
+ { href: "/workspace", label: "工作台", icon: Home },
+ { href: "/contracts", label: "合同", icon: FileSearch },
+ { href: "/search", label: "统一搜索", icon: Search },
+ { href: "/files", label: "合同仓库", icon: Files }
+];
+
+const adminNav = [
+ { href: "/admin", label: "管理区", icon: Settings },
+ { href: "/health", label: "系统健康", icon: Activity }
+];
+
+export function AppShell({ children }: { children: ReactNode }) {
+ const pathname = usePathname();
+
+ return (
+
+
+
+
+
+ {resolveTitle(pathname)}
+
+
+
+ 待我处理
+
+
+
+ 新建合同
+
+
+
+ {children}
+
+
+ );
+}
+
+function resolveTitle(pathname: string): string {
+ if (pathname.startsWith("/contracts/")) return "合同详情";
+ if (pathname.startsWith("/contracts")) return "合同主流程";
+ if (pathname.startsWith("/search")) return "统一搜索";
+ if (pathname.startsWith("/files")) return "合同仓库";
+ if (pathname.startsWith("/admin")) return "管理区";
+ if (pathname.startsWith("/health")) return "系统健康";
+ return "工作台";
+}
diff --git a/apps/contract-web/src/features/system/system-health-panel.tsx b/apps/contract-web/src/features/system/system-health-panel.tsx
new file mode 100644
index 00000000..79eb66ae
--- /dev/null
+++ b/apps/contract-web/src/features/system/system-health-panel.tsx
@@ -0,0 +1,70 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { Activity, RefreshCcw } from "lucide-react";
+
+type HealthState =
+ | { status: "checking" }
+ | { status: "ok"; service: string; version: string; environment: string }
+ | { status: "error"; message: string };
+
+const apiBaseUrl = process.env.NEXT_PUBLIC_CONTRACT_API_BASE_URL ?? "http://localhost:8100";
+
+export function SystemHealthPanel() {
+ const [health, setHealth] = useState({ status: "checking" });
+
+ async function checkHealth() {
+ setHealth({ status: "checking" });
+ try {
+ const response = await fetch(`${apiBaseUrl}/health`, { cache: "no-store" });
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}`);
+ }
+ const data = await response.json() as { service: string; version: string; environment: string };
+ setHealth({ status: "ok", ...data });
+ } catch (error) {
+ setHealth({
+ status: "error",
+ message: error instanceof Error ? error.message : "unknown error"
+ });
+ }
+ }
+
+ useEffect(() => {
+ void checkHealth();
+ }, []);
+
+ return (
+
+
+
合同 API
+
+
+
+
+
+
+ {apiBaseUrl}
+
+ {health.status === "ok" &&
正常}
+ {health.status === "checking" &&
检查中}
+ {health.status === "error" &&
未连接}
+
+ {health.status === "ok" && (
+
+ {health.service} · {health.version} · {health.environment}
+
+ )}
+ {health.status === "error" && (
+
+ {health.message}
+
+ )}
+
+
+
+ );
+}
diff --git a/apps/contract-web/src/features/workspace/workspace-page.tsx b/apps/contract-web/src/features/workspace/workspace-page.tsx
new file mode 100644
index 00000000..df56376f
--- /dev/null
+++ b/apps/contract-web/src/features/workspace/workspace-page.tsx
@@ -0,0 +1,103 @@
+import Link from "next/link";
+import { FilePlus2, Gavel, Inbox, Search, UploadCloud } from "lucide-react";
+import { mainFlow } from "@yuqei/design-system";
+import { tasks } from "@/lib/mock-data";
+
+export function WorkspacePage() {
+ return (
+ <>
+
+
+
工作台
+
只突出待我处理和快速发起,复杂能力收进对应页面和管理区。
+
+
+
+
+ 起草合同
+
+
+
+ 搜索
+
+
+
+
+
+
+
+
+
+
待我处理
+ 优先
+
+
+ {tasks.map((task) => (
+
+
+ {task.title}
+ {task.type}
+
+
{task.summary}
+
+ ))}
+
+
+
+
+
+ >
+ );
+}
diff --git a/apps/contract-web/src/lib/mock-data.ts b/apps/contract-web/src/lib/mock-data.ts
new file mode 100644
index 00000000..a2a92806
--- /dev/null
+++ b/apps/contract-web/src/lib/mock-data.ts
@@ -0,0 +1,120 @@
+export const contracts = [
+ {
+ id: "contract-001",
+ title: "技术服务合同",
+ code: "HT-2026-0001",
+ counterparty: "北京辰星科技有限公司",
+ status: "审查中",
+ statusTone: "tag-warning",
+ nextAction: "法务确认付款和验收条款",
+ updatedAt: "今天 10:18",
+ amount: "¥ 280,000",
+ owner: "张敏"
+ },
+ {
+ id: "contract-002",
+ title: "房屋租赁合同",
+ code: "HT-2026-0002",
+ counterparty: "内蒙古青禾置业",
+ status: "待提交",
+ statusTone: "tag-primary",
+ nextAction: "补充租赁期限后提交",
+ updatedAt: "昨天 18:42",
+ amount: "¥ 96,000",
+ owner: "王磊"
+ },
+ {
+ id: "contract-003",
+ title: "采购框架协议",
+ code: "HT-2026-0003",
+ counterparty: "上海景云供应链",
+ status: "已归档",
+ statusTone: "tag-success",
+ nextAction: "跟踪履约节点",
+ updatedAt: "2026-06-20",
+ amount: "¥ 1,200,000",
+ owner: "李娜"
+ }
+] as const;
+
+export const tasks = [
+ {
+ id: "task-001",
+ title: "技术服务合同需要法务审查",
+ type: "审查",
+ tone: "tag-warning",
+ summary: "付款节点、验收标准和违约责任需要形成处理意见。",
+ href: "/contracts/contract-001"
+ },
+ {
+ id: "task-002",
+ title: "房屋租赁合同待补充期限",
+ type: "起草",
+ tone: "tag-primary",
+ summary: "补齐租赁期限、保证金和交付条件后即可提交。",
+ href: "/contracts/contract-002"
+ },
+ {
+ id: "task-003",
+ title: "采购框架协议履约节点到期",
+ type: "履约",
+ tone: "tag-success",
+ summary: "本周需要上传首批交付验收证据。",
+ href: "/contracts/contract-003"
+ }
+] as const;
+
+export const searchGroups = [
+ {
+ title: "合同",
+ items: contracts.map((contract) => ({
+ title: contract.title,
+ summary: `${contract.counterparty} · ${contract.nextAction}`,
+ updatedAt: contract.updatedAt,
+ href: `/contracts/${contract.id}`
+ }))
+ },
+ {
+ title: "审查意见",
+ items: [
+ {
+ title: "付款条款风险意见",
+ summary: "建议明确验收通过后的付款期限和逾期责任。",
+ updatedAt: "今天 09:50",
+ href: "/contracts/contract-001"
+ }
+ ]
+ },
+ {
+ title: "文件",
+ items: [
+ {
+ title: "技术服务合同-审查版.docx",
+ summary: "来自合同仓库,已关联技术服务合同。",
+ updatedAt: "今天 10:21",
+ href: "/files"
+ }
+ ]
+ }
+] as const;
+
+export const files = [
+ {
+ id: "file-001",
+ name: "技术服务合同-审查版.docx",
+ contractId: "contract-001",
+ contractTitle: "技术服务合同",
+ type: "审查版",
+ size: "128 KB",
+ uploadedAt: "今天 10:21"
+ },
+ {
+ id: "file-002",
+ name: "房屋租赁合同-草稿.pdf",
+ contractId: "contract-002",
+ contractTitle: "房屋租赁合同",
+ type: "草稿",
+ size: "364 KB",
+ uploadedAt: "昨天 19:03"
+ }
+] as const;
diff --git a/apps/contract-web/tsconfig.json b/apps/contract-web/tsconfig.json
new file mode 100644
index 00000000..4b9fd76c
--- /dev/null
+++ b/apps/contract-web/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@/*": [
+ "./src/*"
+ ]
+ },
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
+ },
+ "include": [
+ "next-env.d.ts",
+ ".next/types/**/*.ts",
+ "**/*.ts",
+ "**/*.tsx"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..f70032ba
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1058 @@
+{
+ "name": "yuqei-ai-contract-platform-v2",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "yuqei-ai-contract-platform-v2",
+ "version": "0.1.0",
+ "workspaces": [
+ "apps/*",
+ "packages/*",
+ "sdks/*"
+ ],
+ "devDependencies": {
+ "playwright": "^1.61.0"
+ }
+ },
+ "apps/contract-web": {
+ "name": "@yuqei/contract-web",
+ "version": "0.1.0",
+ "dependencies": {
+ "@yuqei/design-system": "0.1.0",
+ "lucide-react": "1.21.0",
+ "next": "16.2.9",
+ "react": "19.2.7",
+ "react-dom": "19.2.7"
+ },
+ "devDependencies": {
+ "@types/node": "26.0.0",
+ "@types/react": "19.2.17",
+ "@types/react-dom": "19.2.3",
+ "typescript": "5.9.3"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.9.tgz",
+ "integrity": "sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.9.tgz",
+ "integrity": "sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.9.tgz",
+ "integrity": "sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.9.tgz",
+ "integrity": "sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.9.tgz",
+ "integrity": "sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.9.tgz",
+ "integrity": "sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.9.tgz",
+ "integrity": "sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.9.tgz",
+ "integrity": "sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.9.tgz",
+ "integrity": "sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "26.0.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz",
+ "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~8.3.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
+ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@yuqei/contract-web": {
+ "resolved": "apps/contract-web",
+ "link": true
+ },
+ "node_modules/@yuqei/design-system": {
+ "resolved": "packages/design-system",
+ "link": true
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.38",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
+ "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001799",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+ "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.21.0.tgz",
+ "integrity": "sha512-reEZMXq8Qdd5jg5XYkQ5TR1fB/GiQ7ih4vcrthYDtgjSDwh0i6/YLiGjsWsIwgN49gpAnd4J2elSNzncMEEUUQ==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.15",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
+ "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "16.2.9",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.2.9.tgz",
+ "integrity": "sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "16.2.9",
+ "@swc/helpers": "0.5.15",
+ "baseline-browser-mapping": "^2.9.19",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "16.2.9",
+ "@next/swc-darwin-x64": "16.2.9",
+ "@next/swc-linux-arm64-gnu": "16.2.9",
+ "@next/swc-linux-arm64-musl": "16.2.9",
+ "@next/swc-linux-x64-gnu": "16.2.9",
+ "@next/swc-linux-x64-musl": "16.2.9",
+ "@next/swc-win32-arm64-msvc": "16.2.9",
+ "@next/swc-win32-x64-msvc": "16.2.9",
+ "sharp": "^0.34.5"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/playwright": {
+ "version": "1.61.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz",
+ "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.61.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.61.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz",
+ "integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
+ "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.7",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
+ "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.7"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
+ "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "packages/design-system": {
+ "name": "@yuqei/design-system",
+ "version": "0.1.0"
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..81665f3b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "yuqei-ai-contract-platform-v2",
+ "version": "0.1.0",
+ "private": true,
+ "packageManager": "npm@10.9.7",
+ "workspaces": [
+ "apps/*",
+ "packages/*",
+ "sdks/*"
+ ],
+ "scripts": {
+ "test": "python -m pytest",
+ "web:dev": "npm --workspace @yuqei/contract-web run dev",
+ "web:build": "npm --workspace @yuqei/contract-web run build",
+ "web:typecheck": "npm --workspace @yuqei/contract-web run typecheck"
+ },
+ "overrides": {
+ "postcss": "8.5.15"
+ },
+ "devDependencies": {
+ "playwright": "^1.61.0"
+ }
+}
diff --git a/packages/design-system/.gitkeep b/packages/design-system/.gitkeep
deleted file mode 100644
index 8b137891..00000000
--- a/packages/design-system/.gitkeep
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/packages/design-system/README.md b/packages/design-system/README.md
new file mode 100644
index 00000000..0fe17af2
--- /dev/null
+++ b/packages/design-system/README.md
@@ -0,0 +1,5 @@
+# Yuqei Design System
+
+Shared V2 design tokens and workflow primitives.
+
+This package starts intentionally small. It holds product language, layout constants, tokens, and workflow metadata before it grows into a component library.
diff --git a/packages/design-system/package.json b/packages/design-system/package.json
new file mode 100644
index 00000000..abd0bc21
--- /dev/null
+++ b/packages/design-system/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "@yuqei/design-system",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "types": "./src/index.ts",
+ "exports": {
+ ".": "./src/index.ts"
+ },
+ "files": [
+ "src"
+ ]
+}
diff --git a/packages/design-system/src/index.ts b/packages/design-system/src/index.ts
new file mode 100644
index 00000000..3b16516e
--- /dev/null
+++ b/packages/design-system/src/index.ts
@@ -0,0 +1,2 @@
+export * from "./tokens";
+export * from "./workflow";
diff --git a/packages/design-system/src/tokens.ts b/packages/design-system/src/tokens.ts
new file mode 100644
index 00000000..7df263ea
--- /dev/null
+++ b/packages/design-system/src/tokens.ts
@@ -0,0 +1,33 @@
+export const brandTokens = {
+ color: {
+ ink: "#172033",
+ muted: "#667085",
+ line: "#d9e0ea",
+ canvas: "#f6f8fb",
+ surface: "#ffffff",
+ primary: "#2457d6",
+ primarySoft: "#eaf0ff",
+ success: "#16794c",
+ successSoft: "#e7f6ef",
+ warning: "#b35b00",
+ warningSoft: "#fff3df",
+ danger: "#bd2d2d",
+ dangerSoft: "#fdecec",
+ accent: "#0b7a75",
+ accentSoft: "#e4f5f3"
+ },
+ radius: {
+ sm: "6px",
+ md: "8px"
+ },
+ shadow: {
+ panel: "0 10px 30px rgba(16, 24, 40, 0.08)"
+ },
+ layout: {
+ sidebarWidth: "248px",
+ topbarHeight: "64px",
+ contentMaxWidth: "1440px"
+ }
+} as const;
+
+export type BrandTokens = typeof brandTokens;
diff --git a/packages/design-system/src/workflow.ts b/packages/design-system/src/workflow.ts
new file mode 100644
index 00000000..c0a2a288
--- /dev/null
+++ b/packages/design-system/src/workflow.ts
@@ -0,0 +1,24 @@
+export const mainFlow = [
+ {
+ key: "draft",
+ title: "合同起草",
+ description: "从模板或智能问答快速生成合同初稿。"
+ },
+ {
+ key: "review",
+ title: "法律审查",
+ description: "审查风险、补齐依据、形成处理意见。"
+ },
+ {
+ key: "approve",
+ title: "协同审批",
+ description: "按条件触发审批,不默认压迫所有用户。"
+ },
+ {
+ key: "archive",
+ title: "归档履约",
+ description: "沉淀文件、审计、版本和履约证据。"
+ }
+] as const;
+
+export type MainFlowStep = (typeof mainFlow)[number];
diff --git a/project-docs/V2前端基础工程与轻量化体验底座记录-2026-06-22.md b/project-docs/V2前端基础工程与轻量化体验底座记录-2026-06-22.md
new file mode 100644
index 00000000..dc5594f0
--- /dev/null
+++ b/project-docs/V2前端基础工程与轻量化体验底座记录-2026-06-22.md
@@ -0,0 +1,118 @@
+# V2 前端基础工程与轻量化体验底座记录
+
+日期:2026-06-22
+
+## 1. 本轮目标
+
+承接 T0-5,建立 V2 前端基础工程和设计系统底座,同时把企业主线的轻量化体验原则落到第一版页面结构里。
+
+## 2. 已完成内容
+
+### Workspace
+
+已建立根 npm workspace:
+
+- `package.json`
+- `package-lock.json`
+- `tsconfig.base.json`
+
+根命令:
+
+```powershell
+npm run web:dev
+npm run web:typecheck
+npm run web:build
+```
+
+### Design System
+
+目录:`packages/design-system`
+
+已建立:
+
+- 品牌颜色、布局、圆角、阴影 tokens。
+- 企业主流程 `mainFlow` 元数据。
+- package export 边界。
+
+### Contract Web
+
+目录:`apps/contract-web`
+
+已建立:
+
+- Next.js App Router 工程。
+- 全局布局和左侧主导航。
+- 工作台。
+- 合同列表。
+- 合同详情。
+- 统一搜索。
+- 合同仓库。
+- 管理区。
+- 系统健康页。
+
+## 3. 体验原则落地
+
+本轮页面遵循以下原则:
+
+- 工作台只突出“待我处理”和“快速发起”。
+- 主流程导航只保留:工作台、合同、统一搜索、合同仓库。
+- 管理员能力集中到“管理区”。
+- 合同详情首屏突出“下一步操作”。
+- 审批、版本、履约、审计在合同详情侧栏折叠呈现。
+- 统一搜索结果标题就是主入口,减少重复“打开”按钮。
+- 合同仓库保留文件预览、关联合同和批量下载入口。
+
+## 4. 验证结果
+
+已通过:
+
+```powershell
+npm audit --audit-level=moderate
+npm run web:typecheck
+npm run web:build
+python -m pytest
+```
+
+结果:
+
+- npm audit:0 vulnerabilities。
+- TypeScript:通过。
+- Next.js production build:通过。
+- 后端 pytest:6 passed。
+
+页面验收:
+
+- `/`
+- `/workspace`
+- `/contracts`
+- `/contracts/contract-001`
+- `/search`
+- `/files`
+- `/admin`
+- `/health`
+
+截图输出:
+
+```text
+output/playwright/v2-front-foundation/
+```
+
+## 5. 当前边界
+
+本轮仍使用 mock 数据,不包含:
+
+- 登录认证。
+- 真实合同 API 对接。
+- 真实文件上传/下载。
+- 真实搜索查询。
+- 真实审批提交。
+- AI 问答和审查调用。
+
+## 6. 下一步建议
+
+继续进入 S2/S3 的最小闭环:
+
+1. 建立 AI Platform MVP 的数据模型和 `/api/v1/legal/qa` 假实现。
+2. 建立 Python SDK MVP,使 `contract-api` 只能通过 SDK 调用 `ai-platform-api`。
+3. 建立 Contract API 的工作台和合同列表真实接口。
+4. 前端从 mock 数据切换到 Contract API。
diff --git a/tsconfig.base.json b/tsconfig.base.json
new file mode 100644
index 00000000..54c2b0f0
--- /dev/null
+++ b/tsconfig.base.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "es2022"
+ ],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true
+ }
+}