chore(core/frontend): remove dead codex-era exact-* code (§6)
Removed unused codex/iframe-era modules (confirmed no live imports; public/exact/*.html design baselines kept): - routes/exact-document.tsx, routes/exact-html.ts (~1.6MB generated), routes/exact-dashboard.tsx, routes/exact-pages/ (24 files) - exact-pages.css + its main.tsx import; index.ts ExactDashboardApp re-export App.tsx uses the real Dashboard; nothing referenced the removed cluster. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
099bf0e6aa
commit
579fb7cefa
@ -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; }
|
|
||||||
@ -2,7 +2,6 @@ import { createRoot } from "react-dom/client";
|
|||||||
import { App } from "./App";
|
import { App } from "./App";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
import "./design-restraint.css";
|
import "./design-restraint.css";
|
||||||
import "./exact-pages.css";
|
|
||||||
import "./account-page.css";
|
import "./account-page.css";
|
||||||
import "./product-detail-page.css";
|
import "./product-detail-page.css";
|
||||||
import "./team-page.css";
|
import "./team-page.css";
|
||||||
|
|||||||
@ -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 (
|
|
||||||
<div className="app">
|
|
||||||
<aside className="sidebar">
|
|
||||||
<div className="sidebar-head"><a className="brand" href="/dashboard" onClick={go("dashboard")}><span className="brand-clip"><img className="brand-logo" src="/assets/logo.png" alt="Airshelf" /></span></a></div>
|
|
||||||
<div className="search-box"><IconKitSvg name="search" /><input placeholder="搜索" readOnly /><span className="kbd">Ctrl K</span></div>
|
|
||||||
<div className="nav-section">主要</div>
|
|
||||||
<nav>
|
|
||||||
<a href="/dashboard" className="active" onClick={go("dashboard")}><IconKitSvg name="dashboard" /><span>工作台</span></a>
|
|
||||||
<a href="/products" onClick={go("products")}><IconKitSvg name="package" /><span>商品库</span><span className="pill-mini">7</span></a>
|
|
||||||
<a href="/projects" onClick={go("projects")}><IconKitSvg name="clapperboard" /><span>视频项目</span><span className="pill-mini">8</span></a>
|
|
||||||
<a href="/asset-factory" onClick={go("assetFactory")}><IconKitSvg name="sparkles" /><span>图片生成</span></a>
|
|
||||||
<a href="/library" onClick={go("library")}><IconKitSvg name="library" /><span>资产库</span></a>
|
|
||||||
<a href="/team" onClick={go("team")}><IconKitSvg name="users" /><span>团队</span></a>
|
|
||||||
<a href="/account" onClick={go("account")}><IconKitSvg name="creditCard" /><span>消费</span></a>
|
|
||||||
<a href="/settings" onClick={go("settings")}><IconKitSvg name="settings" /><span>设置</span></a>
|
|
||||||
</nav>
|
|
||||||
<div className="aside-foot"><div className="user"><div className="av">李</div><div className="em">小李的店</div></div></div>
|
|
||||||
</aside>
|
|
||||||
<main>
|
|
||||||
<div className="grid-bg"></div>
|
|
||||||
<header className="topbar">
|
|
||||||
<div className="crumbs"><span className="here">工作台</span></div>
|
|
||||||
<div className="right">
|
|
||||||
<button className="balance-chip" type="button" onClick={() => navigate("account")}><IconKitSvg name="creditCard" />余额 <strong>¥327.40</strong></button>
|
|
||||||
<button className="icon-btn" type="button" onClick={() => navigate("messages")}><IconKitSvg name="bell" /><span className="count-noti">12</span></button>
|
|
||||||
<button className="topbar-avatar" type="button" onDoubleClick={logout}><span>李</span></button>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<div className="content" id="page-content">
|
|
||||||
<CornerMark pos="tl" /><CornerMark pos="tr" /><CornerMark pos="bl" /><CornerMark pos="br" />
|
|
||||||
<div className="page-head">
|
|
||||||
<div><h1>欢迎回来,小李</h1><div className="sub"><span className="mono">// 05.14 · 周三</span><span>·</span><span>你有 <b>3 个项目</b> 正在进行中</span></div></div>
|
|
||||||
<div className="actions"><a className="btn btn-create" href="/products/new" onClick={go("productCreateUpload")}><IconKitSvg name="productPlus" />新建商品</a><a className="btn btn-primary btn-lg btn-create" href="/projects/new" onClick={go("projectWizard")}><IconKitSvg name="clapperboard" />新建项目</a></div>
|
|
||||||
</div>
|
|
||||||
<div className="stats with-corners">
|
|
||||||
<span className="corner-tr" /><span className="corner-bl" />
|
|
||||||
<a className="stat" href="/projects" onClick={go("projects")}><div className="lbl">总项目 <span className="badge">ALL</span></div><div className="v">8</div><div className="delta up"><IconKitSvg name="arrowUp" size={14} /> 本月 +3</div></a>
|
|
||||||
<a className="stat" href="/projects" onClick={go("projects")}><div className="lbl">进行中 <span className="badge">WIP</span></div><div className="v">3</div><div className="delta">2 个待审核</div></a>
|
|
||||||
<a className="stat" href="/projects" onClick={go("projects")}><div className="lbl">本月成片 <span className="badge">DONE</span></div><div className="v">3</div><div className="delta up">较上月 +33%</div></a>
|
|
||||||
<a className="stat" href="/account" onClick={go("account")}><div className="lbl">余额 <span className="badge">¥</span></div><div className="v">¥327<small>.40</small></div><div className="bar"><span style={{ width: "33%" }} /></div><div className="sub">已用 ¥162.60 / ¥500</div></a>
|
|
||||||
</div>
|
|
||||||
<div className="dash-grid">
|
|
||||||
<div><div className="section-h"><h2>最近项目</h2><a className="more" href="/projects" onClick={go("projects")}>[ ALL · 8 ]</a></div><div className="card-hard">{["补水面膜 · 痛点种草 · v3", "透真防晒 · 通勤对比", "蓝牙耳机 · 开箱测评", "春日新品 · 立体口红"].map((title) => <ExactRecentRow key={title} title={title} go={go("pipeline")} />)}</div></div>
|
|
||||||
<div className="dash-side"><div className="section-h"><h2>快捷入口</h2><span className="more">[ /shortcuts ]</span></div><div className="shortcuts">{[["商品库", "products"], ["资产库", "library"], ["充值", "account"], ["所有项目", "projects"]].map(([label, target]) => <a className="shortcut" href={`/${target}`} onClick={go(target as Page)} key={label}><div className="ic"><IconKitSvg name="package" /></div><div><div className="t">{label}</div><div className="d">快速进入</div></div></a>)}</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ExactRecentRow({ title, go }: { title: string; go: (event: { preventDefault: () => void }) => void }) {
|
|
||||||
return (
|
|
||||||
<a className="recent-row" href="/pipeline" onClick={go}>
|
|
||||||
<div className="placeholder thumb"><span className="ph-frame">9:16</span></div>
|
|
||||||
<div className="recent-meta"><div className="name">{title}</div><div className="sub">AI 全生 / 4 段</div></div>
|
|
||||||
<div className="prog"><span className="done" /><span className="done" /><span className="cur" /><span /><span /></div>
|
|
||||||
<span className="pill info"><span className="dot" />进行中</span>
|
|
||||||
<span className="btn btn-sm">继续</span>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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<string, Page | "login" | "register" | null> = {
|
|
||||||
"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<Page, ExactHtmlKey> = {
|
|
||||||
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<Record<ExactHtmlKey, Page | "login" | "register">> = {
|
|
||||||
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<ExactHtmlKey, string> = {
|
|
||||||
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<ExactHtmlKey>([
|
|
||||||
"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<HTMLFormElement>, 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<HTMLIFrameElement | null>(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(
|
|
||||||
"</head>",
|
|
||||||
`<script>window.__AIR_SHELF_EXACT_CONTEXT__=${JSON.stringify(context)};</script></head>`
|
|
||||||
);
|
|
||||||
}, [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<HTMLFormElement>, 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 (
|
|
||||||
<div className="exact-document-route" data-exact-page={pageKey}>
|
|
||||||
<iframe
|
|
||||||
ref={frameRef}
|
|
||||||
title={`Airshelf ${pageKey}`}
|
|
||||||
className="exact-document-frame"
|
|
||||||
srcDoc={html}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactAccountPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="account" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactAccountPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactAssetFactoryPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="assetFactory" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactAssetFactoryPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactDashboardPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="dashboard" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactDashboardPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactImageOptimizePage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="imageOptimize" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactImageOptimizePage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactLibraryPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="library" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactLibraryPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactLoginPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="login" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactLoginPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactMessagesPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="messages" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactMessagesPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactModelPhotoDemoAPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="modelPhotoDemoA" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactModelPhotoDemoAPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactModelPhotoDemoBPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="modelPhotoDemoB" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactModelPhotoDemoBPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactModelPhotoPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="modelPhoto" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactModelPhotoPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactPipelinePage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="pipeline" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactPipelinePage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactPlatformCoverPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="platformCover" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactPlatformCoverPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactProductCreateUploadPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="productCreateUpload" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactProductCreateUploadPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactProductCreatePage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="productCreate" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactProductCreatePage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactProductDetailPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="productDetail" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactProductDetailPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactProductsPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="products" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactProductsPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactProjectWizardPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="projectWizard" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactProjectWizardPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactProjectsPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="projects" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactProjectsPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactRegisterPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="register" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactRegisterPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactSettingsPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="settings" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactSettingsPage;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* Generated by scripts/generate-exact-html.mjs. Do not edit by hand. */
|
|
||||||
import { ExactDocumentPage } from "../exact-document";
|
|
||||||
import type { ExactDocumentPageProps } from "../exact-document";
|
|
||||||
|
|
||||||
export function ExactTeamPage(props: Omit<ExactDocumentPageProps, "pageKey">) {
|
|
||||||
return <ExactDocumentPage {...props} pageKey="team" />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ExactTeamPage;
|
|
||||||
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
|
|
||||||
export { AuthScreen } from "./auth-screen";
|
export { AuthScreen } from "./auth-screen";
|
||||||
export { ExactDashboardApp } from "./exact-dashboard";
|
|
||||||
export { Dashboard } from "./dashboard";
|
export { Dashboard } from "./dashboard";
|
||||||
export { ProductsPage, ProductCreateUploadPage, ProductDetailPage } from "./products";
|
export { ProductsPage, ProductCreateUploadPage, ProductDetailPage } from "./products";
|
||||||
export { ProjectWizardPage, ProjectsPage } from "./projects";
|
export { ProjectWizardPage, ProjectsPage } from "./projects";
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user