diff --git a/README.md b/README.md index bf459c8..9a9f05a 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ main 推送通过 CI 验证后自动部署;Pull Request 只验证,不部署 `content/docs/00-endor-concepts-draft.md` 解释全球入口、区域网关与 Runtime、分片、CDN、身份及跨区恢复的概念设计,页面为 `/docs/00-endor-concepts-draft`。 -图形源码位于 `tools/figures/src/endor_figures/architecture.py`,由 `pnpm figures` 生成 `apps/docs/public/figures/endor-architecture.svg`。Python 负责可复现的节点、全连接和复制路径,React 查看器提供放大、滚动与 SVG 下载。无需外部图标、字体或绘图服务。图中的数量是示意,身份服务/CDN 接入属于建议,所有未实现的关系均以草案说明。 +图形源码位于 `tools/figures/src/endor_figures/architecture.py`,由 `pnpm figures` 生成 `apps/docs/public/figures/endor-architecture.svg`。Python 负责可复现的节点、全连接和复制路径,正文直接显示图片,点击可放大;大图支持原尺寸查看、滚动和 Escape/空白处关闭。无需外部图标、字体或绘图服务。图中的数量是示意,身份服务/CDN 接入属于建议,所有未实现的关系均以草案说明。 拓扑检查:`uv run --locked --project tools/figures python -m unittest discover -s tools/figures/tests -v`。容器检查流程会运行同一检查。 diff --git a/apps/docs/src/components/ArchitectureFigure.tsx b/apps/docs/src/components/ArchitectureFigure.tsx index 88dc88c..f717a27 100644 --- a/apps/docs/src/components/ArchitectureFigure.tsx +++ b/apps/docs/src/components/ArchitectureFigure.tsx @@ -1,104 +1,66 @@ import { useRef, useState } from "react"; const source = "/figures/endor-architecture.svg"; -const figureWidth = 2000; export function ArchitectureFigure({ alt }: { alt: string }) { const dialog = useRef(null); const trigger = useRef(null); - const pan = useRef(null); - const [fit, setFit] = useState(true); - const [zoom, setZoom] = useState(1); + const stage = useRef(null); + const [originalSize, setOriginalSize] = useState(false); + function open() { - setFit(true); - setZoom(1); + setOriginalSize(false); dialog.current?.showModal(); } - function changeZoom(delta: number) { - const current = fit - ? ((pan.current?.clientWidth ?? figureWidth + 16) - 16) / figureWidth - : zoom; - setFit(false); - setZoom( - Math.max(0.1, Math.min(2, Math.round((current + delta) * 100) / 100)), - ); - } + return ( -
- - ENDOR / 架构拓扑 - - + <> - - 多区域拓扑 · 独立分片群与统一备份 · 点击放大查看 - { + if ( + event.target === dialog.current || + event.target === stage.current + ) { + dialog.current?.close(); + } + }} onClose={() => trigger.current?.focus()} > -
- Endor 架构拓扑 -
- - - {fit ? "适应宽度" : `${Math.round(zoom * 100)}%`} - - - - - 下载 SVG - - -
-
-
dialog.current?.close()} > - {/* eslint-disable-next-line @next/next/no-img-element */} - {alt} + × + +
+
-

- 实线:请求 / - 依赖 · 紫色:身份 · 蓝色点线:回源 · 金色:统一备份。放大后可横向、纵向滚动,Esc - 关闭。 -

-
+ ); } diff --git a/apps/docs/src/styles/globals.css b/apps/docs/src/styles/globals.css index ded8101..745adfa 100644 --- a/apps/docs/src/styles/globals.css +++ b/apps/docs/src/styles/globals.css @@ -1341,166 +1341,109 @@ button { border-radius: 0; } -/* The topology stays a reproducible SVG; React only supplies the viewer. */ -.architecture-figure { - display: block; - border: 1px solid var(--line); - border-radius: 10px; - overflow: hidden; - margin: 28px 0; -} -.figure-toolbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 12px 16px; - background: #f5f8f3; - font: - 11px ui-monospace, - monospace; - color: #6c806f; -} -.figure-toolbar button { - color: var(--green); - border: 1px solid #d7e3d4; - padding: 5px 10px; - border-radius: 5px; - background: white; -} -.figure-preview { +/* Inline diagrams behave like ordinary article images with click-to-enlarge. */ +.architecture-preview { display: block; width: 100%; - background: #fcfdfb; + margin: 28px 0; + padding: 0; + border: 0; + background: transparent; cursor: zoom-in; } -.prose .figure-preview img { +.prose .architecture-preview img { display: block; width: 100%; - padding: 0; - margin: 0; - border: 0; - border-radius: 0; -} -.figure-caption { - display: block; - padding: 10px 16px; - font-size: 11px; - color: #758576; - border-top: 1px solid var(--line); -} -.figure-dialog { - padding: 0; - width: 96vw; - max-width: 1900px; - height: 94dvh; - max-height: 94dvh; - border: 1px solid #dce5d9; - border-radius: 12px; - margin: auto; - overflow: hidden; - background: #fcfdfb; - box-shadow: 0 20px 100px #18312530; -} -.figure-dialog::backdrop { - background: #1e322e66; - backdrop-filter: blur(4px); -} -.figure-dialog[open] { - display: flex; - flex-direction: column; -} -.figure-viewer-toolbar { - display: flex; - justify-content: space-between; - gap: 16px; - flex-wrap: wrap; - padding: 12px 18px; - background: #fff; - border-bottom: 1px solid var(--line); - align-items: center; -} -.figure-viewer-toolbar strong { - font-size: 14px; - font-weight: 600; - color: var(--ink); -} -.figure-viewer-toolbar small { - color: #a68b52; - font-size: 11px; - margin-left: 5px; -} -.figure-controls { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 8px; - font-size: 11px; - color: #647c67; -} -.figure-controls button, -.figure-controls a { - min-height: 32px; - min-width: 32px; - padding: 4px 9px; - border: 1px solid #dae4d6; - border-radius: 5px; - background: #fff; - font-weight: 400; - text-decoration: none; -} -.figure-controls button:disabled { - opacity: 0.35; - cursor: default; -} -.figure-controls > span { - min-width: 62px; - text-align: center; -} -.figure-pan { - overflow: auto; - flex: 1; - min-height: 0; - overscroll-behavior: contain; - padding: 8px; -} -.prose .figure-pan img { - display: block; - max-width: none; height: auto; - margin: 0 auto; + margin: 0; padding: 0; border: 0; border-radius: 0; - background: #fcfdfb; + background: transparent; } -.figure-viewer-hint { - margin: 0 !important; - padding: 10px 18px; - border-top: 1px solid var(--line); - background: #fff; - color: #798d79; - font-size: 10px !important; - line-height: 1.7 !important; +.image-lightbox { + width: 100vw; + height: 100dvh; + max-width: none; + max-height: none; + margin: 0; + padding: 0; + border: 0; + background: transparent; + overflow: hidden; +} +.image-lightbox::backdrop { + background: rgba(245, 247, 244, 0.97); +} +.image-lightbox-stage { + width: 100%; + height: 100%; + display: flex; + overflow: auto; + overscroll-behavior: contain; + padding: 32px; +} +.image-lightbox-picture { + display: block; + flex-shrink: 0; + margin: auto; + padding: 0; + border: 0; + background: transparent; + cursor: zoom-in; +} +.prose .image-lightbox-picture img { + display: block; + width: auto; + height: auto; + max-width: calc(100vw - 64px); + max-height: calc(100dvh - 64px); + margin: 0; + padding: 0; + border: 0; + border-radius: 0; + background: transparent; +} +.image-lightbox-picture.original-size { + cursor: zoom-out; +} +.prose .image-lightbox-picture.original-size img { + width: 2000px; + max-width: none; + max-height: none; +} +.image-lightbox-close { + position: fixed; + z-index: 1; + top: 12px; + right: 12px; + display: grid; + place-items: center; + width: 40px; + height: 40px; + padding: 0; + border: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.9); + color: #52655a; + font: + 28px/1 ui-sans-serif, + system-ui, + sans-serif; +} +.image-lightbox-close:hover { + background: #edf2ec; } @media (max-width: 560px) { - .figure-viewer-toolbar { - padding: 10px; - gap: 8px; + .image-lightbox-stage { + padding: 16px; } - .figure-controls { - gap: 5px; + .prose .image-lightbox-picture img { + max-width: calc(100vw - 32px); + max-height: calc(100dvh - 32px); } - .figure-controls button, - .figure-controls a { - min-height: 36px; - } - .figure-toolbar { - padding: 10px; - font-size: 10px; - } - .figure-caption { - font-size: 10px; - padding: 10px; + .prose .image-lightbox-picture.original-size img { + max-width: none; + max-height: none; } } diff --git a/design/verification/plain-image/README.md b/design/verification/plain-image/README.md new file mode 100644 index 0000000..30f371a --- /dev/null +++ b/design/verification/plain-image/README.md @@ -0,0 +1,12 @@ +# 普通图片交互验收 · 2026-10-27 + +按用户的两处页面批注,删除架构图上下工具栏、说明条、边框和卡片包装。正文只显示可点击的图片。放大后只显示图片和关闭按钮,支持再次点击图片切换原尺寸、点击空白关闭、Escape关闭并恢复焦点。 + +- `pnpm lint`、`pnpm typecheck`、`pnpm --filter @endor/docs run build --webpack` 通过。 +- 浏览器检查旧 `.figure-toolbar`、`.figure-caption`、`.figure-controls` 数量为0;正文图片及点击区域计算边框均为0px。 +- 实际验证点击图片打开、点击空白关闭、焦点回到正文图片、再次打开及Escape关闭。 +- 390px手机宽度下正文无横向溢出;原尺寸图片宽2000px,仅在大图滚动区域内滚动,页面仍宽390px。 +- 浏览器error/warn日志为空。 +- `inline.png` 为无框正文图片;`enlarged.png` 为简洁放大效果。 + +图片数据与Python制图源码未变;README同步移除旧工具栏/下载控件的功能描述。 diff --git a/design/verification/plain-image/enlarged.png b/design/verification/plain-image/enlarged.png new file mode 100644 index 0000000..86aaef8 Binary files /dev/null and b/design/verification/plain-image/enlarged.png differ diff --git a/design/verification/plain-image/inline.png b/design/verification/plain-image/inline.png new file mode 100644 index 0000000..748b10e Binary files /dev/null and b/design/verification/plain-image/inline.png differ