feat: initialize drainage network frontend
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { NextRequest } from "next/server";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
export const runtime = "nodejs";
|
||||
|
||||
const AGENT_BACKEND_BASE_URL =
|
||||
process.env.AGENT_API_INTERNAL_BASE_URL ??
|
||||
process.env.NEXT_PUBLIC_AGENT_API_BASE_URL ??
|
||||
"http://127.0.0.1:8787";
|
||||
|
||||
type RouteContext = {
|
||||
params: Promise<{ path?: string[] }>;
|
||||
};
|
||||
|
||||
export async function GET(request: NextRequest, context: RouteContext) {
|
||||
return proxyAgentRequest(request, context);
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest, context: RouteContext) {
|
||||
return proxyAgentRequest(request, context);
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, context: RouteContext) {
|
||||
return proxyAgentRequest(request, context);
|
||||
}
|
||||
|
||||
export async function DELETE(request: NextRequest, context: RouteContext) {
|
||||
return proxyAgentRequest(request, context);
|
||||
}
|
||||
|
||||
async function proxyAgentRequest(request: NextRequest, context: RouteContext) {
|
||||
const { path = [] } = await context.params;
|
||||
const upstreamUrl = new URL(`/api/v1/agent/chat/${path.map(encodeURIComponent).join("/")}`, AGENT_BACKEND_BASE_URL);
|
||||
upstreamUrl.search = request.nextUrl.search;
|
||||
|
||||
const headers = new Headers(request.headers);
|
||||
headers.delete("host");
|
||||
headers.delete("content-length");
|
||||
|
||||
const body = request.method === "GET" || request.method === "HEAD" ? undefined : request.body;
|
||||
const upstreamResponse = await fetch(upstreamUrl, {
|
||||
method: request.method,
|
||||
headers,
|
||||
body,
|
||||
cache: "no-store",
|
||||
duplex: body ? "half" : undefined
|
||||
} as RequestInit & { duplex?: "half" });
|
||||
|
||||
const responseHeaders = new Headers(upstreamResponse.headers);
|
||||
responseHeaders.delete("content-encoding");
|
||||
responseHeaders.delete("content-length");
|
||||
|
||||
return new Response(upstreamResponse.body, {
|
||||
status: upstreamResponse.status,
|
||||
statusText: upstreamResponse.statusText,
|
||||
headers: responseHeaders
|
||||
});
|
||||
}
|
||||
+487
@@ -0,0 +1,487 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
color-scheme: light;
|
||||
background: #eaf1f8;
|
||||
--canvas-map: #eaf1f8;
|
||||
--canvas-map-grid: rgba(37, 99, 235, 0.06);
|
||||
--canvas-panel: rgba(255, 255, 255, 0.76);
|
||||
--canvas-panel-strong: rgba(255, 255, 255, 0.92);
|
||||
--action-blue: #2563eb;
|
||||
--action-blue-hover: #1d4ed8;
|
||||
--action-blue-soft: #dbeafe;
|
||||
--ink: #0f172a;
|
||||
--ink-secondary: #334155;
|
||||
--ink-muted: #64748b;
|
||||
--border-soft: rgba(255, 255, 255, 0.62);
|
||||
--border-strong: rgba(148, 163, 184, 0.34);
|
||||
--shadow-control: 0 14px 34px rgba(15, 23, 42, 0.18);
|
||||
--shadow-panel: 0 24px 70px rgba(30, 64, 115, 0.2);
|
||||
--shadow-focus: 0 30px 90px rgba(15, 23, 42, 0.26);
|
||||
--background: 210 40% 98%;
|
||||
--foreground: 222 47% 11%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222 47% 11%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
--primary: 221 83% 53%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 214 32% 91%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
--muted: 210 40% 96%;
|
||||
--muted-foreground: 215 16% 47%;
|
||||
--accent: 210 40% 96%;
|
||||
--accent-foreground: 222 47% 11%;
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214 32% 91%;
|
||||
--input: 214 32% 91%;
|
||||
--ring: 221 83% 53%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:
|
||||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
"PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
button:focus-visible,
|
||||
input:focus-visible {
|
||||
outline: 2px solid #0f172a;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.map-grid {
|
||||
background:
|
||||
linear-gradient(rgba(255, 255, 255, 0.42), rgba(255, 255, 255, 0.08)),
|
||||
linear-gradient(90deg, var(--canvas-map-grid) 1px, transparent 1px),
|
||||
linear-gradient(var(--canvas-map-grid) 1px, transparent 1px),
|
||||
var(--canvas-map);
|
||||
background-size:
|
||||
auto,
|
||||
68px 68px,
|
||||
68px 68px,
|
||||
auto;
|
||||
}
|
||||
|
||||
.command-panel {
|
||||
border: 1px solid var(--border-soft);
|
||||
background: var(--canvas-panel);
|
||||
box-shadow: var(--shadow-panel);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
.agent-panel-band {
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
}
|
||||
|
||||
.agent-panel-conversation {
|
||||
background: rgba(255, 255, 255, 0.34);
|
||||
}
|
||||
|
||||
.agent-panel-message {
|
||||
border: 1px solid rgba(226, 232, 240, 0.72);
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
}
|
||||
|
||||
.agent-streaming-response {
|
||||
display: block;
|
||||
--sd-animation: agent-stream-token-in;
|
||||
--sd-duration: 260ms;
|
||||
--sd-easing: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.agent-chart-line {
|
||||
animation: agent-chart-line-in 220ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
transform-origin: center;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
.agent-chart-point,
|
||||
.agent-chart-bar {
|
||||
animation: agent-chart-mark-in 180ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
transform-box: fill-box;
|
||||
transform-origin: center bottom;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
.agent-panel-nested {
|
||||
border: 1px solid rgba(203, 213, 225, 0.72);
|
||||
background: rgba(248, 250, 252, 0.9);
|
||||
}
|
||||
|
||||
.agent-panel-control {
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
}
|
||||
|
||||
.agent-panel-icon-button {
|
||||
border: 1px solid rgba(203, 213, 225, 0.72);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.agent-panel-icon-button:hover {
|
||||
border-color: rgba(37, 99, 235, 0.26);
|
||||
background: rgba(239, 246, 255, 0.94);
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.agent-panel-primary-icon,
|
||||
.agent-panel-primary-action {
|
||||
border: 1px solid rgba(37, 99, 235, 0.24);
|
||||
background: #2563eb;
|
||||
color: #ffffff;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.agent-panel-primary-icon:hover,
|
||||
.agent-panel-primary-action:hover {
|
||||
border-color: rgba(29, 78, 216, 0.34);
|
||||
background: #1d4ed8;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.agent-panel-primary-icon:disabled,
|
||||
.agent-panel-primary-action:disabled {
|
||||
background: #93c5fd;
|
||||
color: rgba(255, 255, 255, 0.86);
|
||||
}
|
||||
|
||||
.scheduled-feed-scroll,
|
||||
.scheduled-feed-detail-scroll,
|
||||
.agent-conversation-scroll {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(100, 116, 139, 0.26) transparent;
|
||||
scrollbar-gutter: stable;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.scheduled-feed-scroll::-webkit-scrollbar,
|
||||
.scheduled-feed-detail-scroll::-webkit-scrollbar,
|
||||
.agent-conversation-scroll::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.scheduled-feed-scroll::-webkit-scrollbar-track,
|
||||
.scheduled-feed-detail-scroll::-webkit-scrollbar-track,
|
||||
.agent-conversation-scroll::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.scheduled-feed-scroll::-webkit-scrollbar-thumb,
|
||||
.scheduled-feed-detail-scroll::-webkit-scrollbar-thumb,
|
||||
.agent-conversation-scroll::-webkit-scrollbar-thumb {
|
||||
background: rgba(100, 116, 139, 0.24);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.scheduled-feed-scroll:hover::-webkit-scrollbar-thumb,
|
||||
.scheduled-feed-detail-scroll:hover::-webkit-scrollbar-thumb,
|
||||
.agent-conversation-scroll:hover::-webkit-scrollbar-thumb {
|
||||
background: rgba(100, 116, 139, 0.36);
|
||||
}
|
||||
|
||||
.scheduled-feed-panel-shell {
|
||||
transition:
|
||||
width 180ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
max-height 180ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
opacity 150ms ease-out;
|
||||
}
|
||||
|
||||
.scheduled-feed-detail-enter {
|
||||
animation: scheduled-feed-detail-show 140ms ease-out 180ms both;
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
.scheduled-feed-layout {
|
||||
grid-template-columns: 0 minmax(0, 1fr);
|
||||
column-gap: 0;
|
||||
transition:
|
||||
grid-template-columns 180ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
column-gap 180ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
will-change: grid-template-columns;
|
||||
}
|
||||
|
||||
.scheduled-feed-layout-expanded {
|
||||
grid-template-columns: minmax(0, 1fr) 432px;
|
||||
column-gap: 0.75rem;
|
||||
}
|
||||
|
||||
.scheduled-feed-layout-collapsed {
|
||||
grid-template-columns: 0 minmax(0, 1fr);
|
||||
column-gap: 0;
|
||||
}
|
||||
|
||||
.scheduled-feed-shell-enter {
|
||||
animation:
|
||||
scheduled-feed-shell-show 170ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
agent-panel-fade 140ms ease-out;
|
||||
transform-origin: top right;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
.scheduled-feed-shell-exit {
|
||||
animation:
|
||||
scheduled-feed-shell-hide 170ms cubic-bezier(0.5, 0, 0.2, 1) forwards,
|
||||
agent-panel-dim 140ms ease-in forwards;
|
||||
pointer-events: none;
|
||||
transform-origin: top right;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
.agent-panel-confirmation {
|
||||
border: 1px solid rgba(254, 215, 170, 0.92);
|
||||
background: rgba(255, 247, 237, 0.96);
|
||||
}
|
||||
|
||||
.agent-panel-enter {
|
||||
animation:
|
||||
agent-panel-reveal 170ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
agent-panel-fade 150ms ease-out;
|
||||
transform-origin: left top;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
.agent-panel-collapse {
|
||||
animation:
|
||||
agent-panel-dismiss 170ms cubic-bezier(0.5, 0, 0.2, 1) forwards,
|
||||
agent-panel-dim 150ms ease-in forwards;
|
||||
pointer-events: none;
|
||||
transform-origin: left top;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
.agent-rail-enter {
|
||||
animation:
|
||||
agent-rail-shell 170ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
agent-panel-fade 130ms ease-out;
|
||||
transform-origin: left top;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
.agent-rail-item {
|
||||
animation: agent-rail-item 180ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
}
|
||||
|
||||
.agent-rail-item:nth-child(2) {
|
||||
animation-delay: 24ms;
|
||||
}
|
||||
|
||||
.agent-rail-item:nth-child(3) {
|
||||
animation-delay: 42ms;
|
||||
}
|
||||
|
||||
.agent-rail-item:nth-child(4) {
|
||||
animation-delay: 58ms;
|
||||
}
|
||||
|
||||
@keyframes agent-panel-reveal {
|
||||
from {
|
||||
transform: translateX(-8px) scale(0.985);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes agent-panel-fade {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes agent-panel-dim {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes agent-panel-dismiss {
|
||||
from {
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(-8px) scale(0.985);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes agent-rail-shell {
|
||||
from {
|
||||
transform: translateX(-5px) scale(0.94);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes agent-rail-item {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-3px) scale(0.92);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes agent-stream-token-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
filter: blur(0.75px);
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
60% {
|
||||
opacity: 0.88;
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
filter: blur(0);
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes agent-chart-line-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(3px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes agent-chart-mark-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(4px) scaleY(0.75);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scaleY(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scheduled-feed-detail-show {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scheduled-feed-shell-show {
|
||||
from {
|
||||
transform: translateX(10px) translateY(-4px) scale(0.985);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0) translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scheduled-feed-shell-hide {
|
||||
from {
|
||||
transform: translateX(0) translateY(0) scale(1);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(10px) translateY(-4px) scale(0.985);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.agent-panel-enter,
|
||||
.agent-panel-collapse,
|
||||
.agent-rail-enter,
|
||||
.agent-rail-item,
|
||||
.scheduled-feed-shell-enter,
|
||||
.scheduled-feed-shell-exit,
|
||||
.scheduled-feed-detail-enter {
|
||||
animation: none;
|
||||
clip-path: none;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.scheduled-feed-layout {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.scheduled-feed-panel-shell {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.agent-streaming-response [data-sd-animate] {
|
||||
animation: none;
|
||||
filter: none;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.agent-chart-line,
|
||||
.agent-chart-point,
|
||||
.agent-chart-bar {
|
||||
animation: none;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-group {
|
||||
border: 1px solid rgba(255, 255, 255, 0.7) !important;
|
||||
border-radius: 14px !important;
|
||||
box-shadow: 0 14px 36px rgba(30, 64, 115, 0.14) !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.maplibregl-ctrl button {
|
||||
width: 34px !important;
|
||||
height: 34px !important;
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-attrib {
|
||||
border-radius: 8px 0 0 0 !important;
|
||||
color: #475569 !important;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { Metadata } from "next";
|
||||
import { MapToaster } from "@/features/map/core/components/notice";
|
||||
import "katex/dist/katex.min.css";
|
||||
import "streamdown/styles.css";
|
||||
import "./globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "排水管网",
|
||||
description: "Agent 编排型沉浸式排水管网业务工作台"
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="zh-CN" suppressHydrationWarning>
|
||||
<body>
|
||||
{children}
|
||||
<MapToaster />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { MapWorkbenchPage } from "@/features/workbench";
|
||||
|
||||
export default function Home() {
|
||||
return <MapWorkbenchPage />;
|
||||
}
|
||||
Reference in New Issue
Block a user