diff --git a/core/frontend/src/exact-pages.css b/core/frontend/src/exact-pages.css deleted file mode 100644 index 5216cea..0000000 --- a/core/frontend/src/exact-pages.css +++ /dev/null @@ -1,33 +0,0 @@ -/* Exact page-level CSS copied from 电商AI平台/index.html. */ -.exact-document-route { - width: 100%; - min-height: 100vh; - background: var(--background-base); -} -.exact-document-frame { - display: block; - width: 100%; - min-height: 100vh; - border: 0; - background: var(--background-base); -} -.app .brand { gap: 0; padding: 0; } -.app .search-box .kbd { background: transparent; border: 0; border-radius: 0; padding: 0; } -.dash-grid { display: grid; grid-template-columns: 1.7fr 1fr; gap: 24px; align-items: start; } -.recent-row { display: grid; grid-template-columns: 54px 1fr 110px 130px 60px; align-items: center; gap: 16px; padding: 14px 18px; border-bottom: 1px solid var(--border-faint); cursor: pointer; width: auto; text-align: inherit; background: transparent; } -.recent-row .prog, .recent-row .pill, .recent-row .btn { justify-self: start; } -.recent-row:last-child { border-bottom: 0; } -.recent-row:hover { background: var(--background-lighter); } -.recent-row .thumb { width: 54px; height: 70px; border-radius: var(--r-md); } -.recent-meta .name { font-weight: 600; font-size: 13.5px; color: var(--accent-black); } -.recent-meta .sub { font-size: 12px; color: var(--black-alpha-48); margin-top: 3px; font-family: var(--font-mono); letter-spacing: .01em; } -.shortcuts { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } -.shortcut { background: var(--surface); border: 1px solid var(--border-faint); border-radius: var(--r-md); padding: 16px; display: flex; align-items: flex-start; gap: 12px; cursor: pointer; transition: background var(--t-base); } -.shortcut:hover { background: var(--black-alpha-4); } -.shortcut .ic { width: 32px; height: 32px; background: var(--heat-12); color: var(--heat); display: grid; place-items: center; border: 0; border-radius: var(--r-md); flex-shrink: 0; } -.shortcut .ic svg { width: 16px; height: 16px; } -.shortcut .t { font-size: 13px; font-weight: 600; } -.shortcut .d { font-size: 11.5px; color: var(--black-alpha-48); margin-top: 3px; font-family: var(--font-mono); letter-spacing: .01em; } -.tip { background: var(--surface); border: 1px dashed var(--border-faint); padding: 14px 16px; font-size: 12.5px; color: var(--black-alpha-56); line-height: 1.6; border-radius: var(--r-md); } -.tip strong { color: var(--accent-black); font-weight: 600; display: block; margin-bottom: 4px; } -.tip .mono { font-family: var(--font-mono); color: var(--heat); background: var(--heat-12); padding: 1px 5px; border-radius: var(--r-sm); font-size: 11.5px; } diff --git a/core/frontend/src/main.tsx b/core/frontend/src/main.tsx index 8acd0f1..d89a9f7 100644 --- a/core/frontend/src/main.tsx +++ b/core/frontend/src/main.tsx @@ -2,7 +2,6 @@ import { createRoot } from "react-dom/client"; import { App } from "./App"; import "./styles.css"; import "./design-restraint.css"; -import "./exact-pages.css"; import "./account-page.css"; import "./product-detail-page.css"; import "./team-page.css"; diff --git a/core/frontend/src/routes/exact-dashboard.tsx b/core/frontend/src/routes/exact-dashboard.tsx deleted file mode 100644 index 09fab36..0000000 --- a/core/frontend/src/routes/exact-dashboard.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { IconKitSvg } from "../components/IconKitSvg"; -import { CornerMark } from "../components/app-shell"; -import type { NavigateFn, Page } from "./route-config"; - -export function ExactDashboardApp({ navigate, logout }: { navigate: NavigateFn; logout: () => void }) { - const go = (next: Page) => (event: { preventDefault: () => void }) => { - event.preventDefault(); - navigate(next); - }; - - return ( -
- -
-
-
-
工作台
-
- - - -
-
-
- -
-

欢迎回来,小李

// 05.14 · 周三·你有 3 个项目 正在进行中
- -
- -
-

最近项目

[ ALL · 8 ]
{["补水面膜 · 痛点种草 · v3", "透真防晒 · 通勤对比", "蓝牙耳机 · 开箱测评", "春日新品 · 立体口红"].map((title) => )}
-

快捷入口

[ /shortcuts ]
{[["商品库", "products"], ["资产库", "library"], ["充值", "account"], ["所有项目", "projects"]].map(([label, target]) =>
{label}
快速进入
)}
-
-
-
-
- ); -} - -export function ExactRecentRow({ title, go }: { title: string; go: (event: { preventDefault: () => void }) => void }) { - return ( - -
9:16
-
{title}
AI 全生 / 4 段
-
- 进行中 - 继续 -
- ); -} diff --git a/core/frontend/src/routes/exact-document.tsx b/core/frontend/src/routes/exact-document.tsx deleted file mode 100644 index 531a95a..0000000 --- a/core/frontend/src/routes/exact-document.tsx +++ /dev/null @@ -1,349 +0,0 @@ -import { useEffect, useMemo, useRef } from "react"; -import type { FormEvent } from "react"; -import { exactHtmlDocuments } from "./exact-html"; -import type { ExactHtmlKey } from "./exact-html"; -import type { AuthMode, NavigateFn, Page } from "./route-config"; - -const fileToPage: Record = { - "account.html": "account", - "asset-factory.html": "assetFactory", - "image-optimize.html": "imageOptimize", - "index.html": "dashboard", - "library.html": "library", - "login.html": "login", - "messages.html": "messages", - "model-photo.html": "modelPhoto", - "model-photo-demo-a.html": "modelPhotoDemoA", - "model-photo-demo-b.html": "modelPhotoDemoB", - "pipeline.html": "pipeline", - "platform-cover.html": "platformCover", - "product-create.html": "productCreateUpload", - "product-create-upload.html": "productCreateUpload", - "product-detail.html": "productDetail", - "products.html": "products", - "projects-new.html": "projectWizard", - "projects.html": "projects", - "register.html": "register", - "settings.html": "settings", - "team.html": "team" -}; - -const pageToExactKey: Record = { - dashboard: "dashboard", - products: "products", - productDetail: "productDetail", - productCreateUpload: "productCreateUpload", - projects: "projects", - projectWizard: "projectWizard", - pipeline: "pipeline", - library: "library", - account: "account", - team: "team", - messages: "messages", - assetFactory: "assetFactory", - imageOptimize: "imageOptimize", - modelPhoto: "modelPhoto", - modelPhotoDemoA: "modelPhotoDemoA", - modelPhotoDemoB: "modelPhotoDemoB", - platformCover: "platformCover", - settings: "settings", - settingsNotify: "settings" -}; - -const exactKeyToPage: Partial> = { - account: "account", - assetFactory: "assetFactory", - dashboard: "dashboard", - imageOptimize: "imageOptimize", - library: "library", - login: "login", - messages: "messages", - modelPhoto: "modelPhoto", - modelPhotoDemoA: "modelPhotoDemoA", - modelPhotoDemoB: "modelPhotoDemoB", - pipeline: "pipeline", - platformCover: "platformCover", - productCreate: "productCreateUpload", - productCreateUpload: "productCreateUpload", - productDetail: "productDetail", - products: "products", - projectWizard: "projectWizard", - projects: "projects", - register: "register", - settings: "settings", - team: "team" -}; - -const exactKeyToFile: Record = { - account: "account.html", - assetFactory: "asset-factory.html", - dashboard: "index.html", - imageOptimize: "image-optimize.html", - library: "library.html", - login: "login.html", - messages: "messages.html", - modelPhoto: "model-photo.html", - modelPhotoDemoA: "model-photo-demo-a.html", - modelPhotoDemoB: "model-photo-demo-b.html", - pipeline: "pipeline.html", - platformCover: "platform-cover.html", - productCreate: "product-create.html", - productCreateUpload: "product-create-upload.html", - productDetail: "product-detail.html", - products: "products.html", - projectWizard: "projects-new.html", - projects: "projects.html", - register: "register.html", - settings: "settings.html", - team: "team.html" -}; - -const liveHydratePages = new Set([ - "dashboard", - "products", - "productDetail", - "projectWizard", - "projects", - "pipeline", - "library", - "account", - "settings", - "team" -]); - -function routeFromHref(rawHref: string | null) { - if (!rawHref || rawHref === "#" || rawHref.startsWith("javascript:")) return null; - const url = new URL(rawHref, "https://airshelf.local/exact/"); - const fileName = url.pathname.split("/").filter(Boolean).pop() || "index.html"; - const page = fileToPage[fileName]; - if (!page) return null; - const params = new URLSearchParams(url.search); - return { - page, - hash: url.hash || "", - search: url.search || "", - productId: params.get("product_id") || undefined, - projectId: params.get("project_id") || undefined - }; -} - -function routeFromInlineAction(action: string | null) { - if (!action) return null; - const hrefMatch = action.match(/location\.href\s*=\s*['"]([^'"]+)/); - if (hrefMatch) return routeFromHref(hrefMatch[1]); - const hashMatch = action.match(/location\.hash\s*=\s*['"]([^'"]+)/); - if (hashMatch) return { page: null, hash: hashMatch[1], search: "" }; - return null; -} - -function setFrameHeight(frame: HTMLIFrameElement) { - frame.style.height = `${Math.max(window.innerHeight, 720)}px`; -} - -export type ExactDocumentPageProps = { - pageKey: ExactHtmlKey; - hash?: string; - productId?: string; - projectId?: string; - navigate?: NavigateFn; - onAuthModeChange?: (mode: AuthMode) => void; - onAuthSubmit?: (mode: AuthMode, event: FormEvent, form: HTMLFormElement) => void; -}; - -export function exactKeyForPage(page: Page): ExactHtmlKey { - return pageToExactKey[page] || "dashboard"; -} - -function contextSearch(pageKey: ExactHtmlKey, productId?: string, projectId?: string) { - const params = new URLSearchParams(); - if (pageKey === "productDetail" && productId) params.set("product_id", productId); - if (pageKey === "pipeline" && projectId) params.set("project_id", projectId); - const text = params.toString(); - return text ? `?${text}` : ""; -} - -export function ExactDocumentPage({ - pageKey, - hash, - productId, - projectId, - navigate, - onAuthModeChange, - onAuthSubmit -}: ExactDocumentPageProps) { - const frameRef = useRef(null); - const html = useMemo(() => { - const context = { - page: exactKeyToFile[pageKey], - search: contextSearch(pageKey, productId, projectId), - hash: hash ? `#${hash.replace(/^#/, "")}` : "", - liveHydrate: liveHydratePages.has(pageKey) - }; - return exactHtmlDocuments[pageKey].replace( - "", - `` - ); - }, [hash, pageKey, productId, projectId]); - - useEffect(() => { - const frame = frameRef.current; - if (!frame) return; - const currentFrame: HTMLIFrameElement = frame; - - function onLoad() { - const doc = currentFrame.contentDocument; - const win = currentFrame.contentWindow; - if (!doc || !win) return; - - (win as Window & { __AIR_SHELF_HOST_NAVIGATE__?: (href: string) => void }).__AIR_SHELF_HOST_NAVIGATE__ = ( - href: string - ) => { - const hostRoute = routeFromHref(href); - if (!hostRoute?.page) return; - if (hostRoute.page === "login" || hostRoute.page === "register") { - onAuthModeChange?.(hostRoute.page); - return; - } - navigate?.(hostRoute.page, { - hash: hostRoute.hash || undefined, - productId: hostRoute.productId || (hostRoute.page === "productDetail" ? productId : undefined), - projectId: hostRoute.projectId || (hostRoute.page === "pipeline" ? projectId : undefined) - }); - }; - - const applyFrameHash = (nextHash: string) => { - const cleanHash = nextHash.replace(/^#/, ""); - const stageMatch = cleanHash.match(/^stage-(\d+)$/); - const pipelineWindow = win as Window & { activateStage?: (stage: number) => void }; - if (pageKey === "pipeline" && stageMatch && typeof pipelineWindow.activateStage === "function") { - pipelineWindow.activateStage(Number(stageMatch[1])); - return; - } - const settingsWindow = win as Window & { showSection?: (sectionId: string) => void }; - if (pageKey === "settings" && cleanHash.startsWith("sec-") && typeof settingsWindow.showSection === "function") { - settingsWindow.showSection(cleanHash); - return; - } - doc.getElementById(cleanHash)?.scrollIntoView({ behavior: "smooth", block: "start" }); - }; - - if (hash) { - setTimeout(() => { - applyFrameHash(hash); - }, 0); - } - - const clickHandler = (event: MouseEvent) => { - const target = event.target as Element | null; - if (!target) return; - - const syncHashOnlyRoute = (nextHash: string) => { - const cleanHash = nextHash.replace(/^#/, ""); - applyFrameHash(cleanHash); - if (exactKeyToPage[pageKey]) { - const nextUrl = `${window.location.pathname}${window.location.search}#${cleanHash}`; - window.history.replaceState(null, "", nextUrl); - } - }; - - const actionNode = target.closest("[onclick]") as HTMLElement | null; - const actionRoute = routeFromInlineAction(actionNode?.getAttribute("onclick") || null); - if (actionRoute?.hash && actionRoute.page === null) { - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - syncHashOnlyRoute(actionRoute.hash); - return; - } - if (actionRoute?.page) { - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - if (actionRoute.page === "login" || actionRoute.page === "register") { - onAuthModeChange?.(actionRoute.page); - return; - } - navigate?.(actionRoute.page, { - hash: actionRoute.hash || undefined, - productId: actionRoute.productId || (actionRoute.page === "productDetail" ? productId || "exact" : undefined), - projectId: actionRoute.projectId - }); - return; - } - - const anchor = target.closest("a[href]") as HTMLAnchorElement | null; - const rawAnchorHref = anchor?.getAttribute("href") || null; - if (rawAnchorHref?.startsWith("#")) { - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - syncHashOnlyRoute(rawAnchorHref); - return; - } - const anchorRoute = routeFromHref(rawAnchorHref); - if (!anchorRoute) return; - event.preventDefault(); - event.stopPropagation(); - if (anchorRoute.page === "login" || anchorRoute.page === "register") { - onAuthModeChange?.(anchorRoute.page); - return; - } - navigate?.(anchorRoute.page, { - hash: anchorRoute.hash || undefined, - productId: anchorRoute.productId || (anchorRoute.page === "productDetail" ? productId || "exact" : undefined), - projectId: anchorRoute.projectId - }); - }; - - const submitHandler = (event: SubmitEvent) => { - const form = event.target as HTMLFormElement | null; - if (!form) return; - const isLogin = pageKey === "login" && form.id === "login-form"; - const isRegister = pageKey === "register" && form.id === "register-form"; - if (!isLogin && !isRegister) return; - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - onAuthSubmit?.(isLogin ? "login" : "register", event as unknown as FormEvent, form); - }; - - doc.addEventListener("click", clickHandler, true); - doc.addEventListener("submit", submitHandler, true); - setFrameHeight(currentFrame); - const resizeFrame = () => setFrameHeight(currentFrame); - window.addEventListener("resize", resizeFrame); - const observer = new ResizeObserver(() => setFrameHeight(currentFrame)); - observer.observe(doc.documentElement); - observer.observe(doc.body); - - const cleanup = () => { - doc.removeEventListener("click", clickHandler, true); - doc.removeEventListener("submit", submitHandler, true); - window.removeEventListener("resize", resizeFrame); - observer.disconnect(); - }; - currentFrame.dataset.cleanupKey = String(Date.now()); - (currentFrame as HTMLIFrameElement & { __airshelfCleanup?: () => void }).__airshelfCleanup?.(); - (currentFrame as HTMLIFrameElement & { __airshelfCleanup?: () => void }).__airshelfCleanup = cleanup; - } - - currentFrame.addEventListener("load", onLoad); - if (currentFrame.contentDocument?.readyState !== "loading") onLoad(); - return () => { - currentFrame.removeEventListener("load", onLoad); - (currentFrame as HTMLIFrameElement & { __airshelfCleanup?: () => void }).__airshelfCleanup?.(); - (currentFrame as HTMLIFrameElement & { __airshelfCleanup?: () => void }).__airshelfCleanup = undefined; - }; - }, [hash, navigate, onAuthModeChange, onAuthSubmit, pageKey]); - - return ( -
-