From 824512a22406d63e30da19f9c764b8742e9bfbe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=86=95?= Date: Thu, 9 Jul 2026 18:34:31 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=AD=E4=BB=A3=20ONE-OS=20=E5=8E=9F?= =?UTF-8?q?=E5=9E=8B=E5=AF=BC=E8=88=AA=EF=BC=9AVoltAgent=20=E8=A7=86?= =?UTF-8?q?=E8=A7=89=E6=94=B9=E7=89=88=E3=80=81=E5=AF=86=E7=A0=81=E9=97=A8?= =?UTF-8?q?=E7=A6=81=E4=B8=8E=E4=BE=A7=E6=A0=8F=E6=8C=89=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E7=BA=BF=E5=88=87=E6=8D=A2=EF=BC=9B=E8=BD=A6=E8=BE=86=E6=B0=A2?= =?UTF-8?q?=E8=B4=B9=E6=98=8E=E7=BB=86=E6=A0=87=E6=B3=A8=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=87=B3=20v1.5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- scripts/sync-prototype-nav-registry.mjs | 2 +- .../annotation-source.json | 35 +- src/prototypes/oneos-prototype-nav/index.tsx | 487 +++++-- .../oneos-prototype-nav/nav-data.ts | 2 +- .../prototype-registry.json | 138 +- src/prototypes/oneos-prototype-nav/style.css | 1240 ++++++++++++----- .../annotation-source.json | 115 +- 7 files changed, 1445 insertions(+), 574 deletions(-) diff --git a/scripts/sync-prototype-nav-registry.mjs b/scripts/sync-prototype-nav-registry.mjs index 133ca26..584b917 100644 --- a/scripts/sync-prototype-nav-registry.mjs +++ b/scripts/sync-prototype-nav-registry.mjs @@ -295,7 +295,7 @@ export function syncPrototypeNavRegistry(options = {}) { let bumped = 0; for (const prototypeId of targetIds) { - if (prototypeId === NAV_PROTOTYPE_ID) continue; + if (prototypeId === NAV_PROTOTYPE_ID && options.prototype !== NAV_PROTOTYPE_ID) continue; const title = titleMap.get(prototypeId) || prototypeId; const prototypeDir = path.join(projectRoot, 'src/prototypes', prototypeId); if (!fs.existsSync(prototypeDir)) { diff --git a/src/prototypes/oneos-prototype-nav/annotation-source.json b/src/prototypes/oneos-prototype-nav/annotation-source.json index 6970e42..847cc0b 100644 --- a/src/prototypes/oneos-prototype-nav/annotation-source.json +++ b/src/prototypes/oneos-prototype-nav/annotation-source.json @@ -5,7 +5,7 @@ "version": 2, "prototypeName": "oneos-prototype-nav", "pageId": "nav", - "updatedAt": 1783589987167, + "updatedAt": 1783600000000, "nodes": [ { "id": "opn-hero", @@ -51,37 +51,6 @@ }, "markdownMap": {}, "directory": { - "nodes": [ - { - "type": "folder", - "id": "oneos-project-nav", - "title": "ONE-OS 原型导航", - "defaultExpanded": true, - "children": [ - { - "type": "link", - "id": "nav-link-oneos-prototype-nav", - "title": "原型导航(当前)", - "href": "/prototypes/oneos-prototype-nav", - "target": "self" - }, - { - "type": "folder", - "id": "nav-folder-1782874576229-r8atbu", - "title": "OneOS", - "defaultExpanded": true, - "children": [ - { - "type": "link", - "id": "nav-link-oneos-web-workbench", - "title": "工作台", - "href": "/prototypes/oneos-web-workbench", - "target": "self" - } - ] - } - ] - } - ] + "nodes": [] } } diff --git a/src/prototypes/oneos-prototype-nav/index.tsx b/src/prototypes/oneos-prototype-nav/index.tsx index 2a4ad77..578b1fa 100644 --- a/src/prototypes/oneos-prototype-nav/index.tsx +++ b/src/prototypes/oneos-prototype-nav/index.tsx @@ -3,15 +3,24 @@ * ONE-OS 原型目录导航:按菜单分组快速进入各功能原型 */ import './style.css'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { - ArrowRight, - Compass, + ArrowUpRight, + Clock3, ExternalLink, + FileCode2, History, - Layers, + LayoutGrid, + Lock, Search, X, + Zap, } from 'lucide-react'; import { countNavLinks, @@ -29,13 +38,49 @@ import { type RecentUpdateEntry, } from './nav-data'; -function linkMeta(link: NavLinkItem, record: PrototypeRegistryRecord | null): string | undefined { - const parts: string[] = []; - if (link.version || record?.version) parts.push(link.version || record?.version || ''); - if (link.lastUpdatedLabel) parts.push(`${link.lastUpdatedLabel}更新`); +function formatRecordUpdatedAt(record: PrototypeRegistryRecord | null): string | undefined { + if (record?.lastUpdated) { + const date = new Date(record.lastUpdated); + if (!Number.isNaN(date.getTime())) { + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: 'numeric', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + }); + } + } const latest = record?.changelog?.[0]; - if (latest?.summary && parts.length < 2) parts.push(latest.summary); - return parts.filter(Boolean).join(' · ') || undefined; + if (latest) { + const time = latest.time.length === 5 ? `${latest.time}:00` : latest.time; + return `${latest.date.replace(/-/g, '/')} ${time}`; + } + return undefined; +} + +function linkMeta(record: PrototypeRegistryRecord | null): string | undefined { + const updatedAt = formatRecordUpdatedAt(record); + if (updatedAt) return `${updatedAt} 更新`; + return undefined; +} + +function sidebarSectionLabel(section: NavSection): string { + if (section.title === '小羚羚') return '小羚羚「小程序」'; + return section.title; +} + +function sectionIdFromHash(): string | null { + const match = window.location.hash.match(/^#opn-section-(.+)$/u); + return match ? match[1] : null; +} + +function resolveInitialSectionId(sections: NavSection[]): string { + const fromHash = sectionIdFromHash(); + if (fromHash && sections.some((section) => section.id === fromHash)) return fromHash; + return sections[0]?.id || ''; } const NEW_TAB_LINK_PROPS = { @@ -43,6 +88,99 @@ const NEW_TAB_LINK_PROPS = { rel: 'noopener noreferrer', } as const; +const AUTH_STORAGE_KEY = 'oneos-prototype-nav-auth'; +const NAV_ACCESS_PASSWORD = 'lingniu'; + +function readAuthSession(): boolean { + try { + return sessionStorage.getItem(AUTH_STORAGE_KEY) === 'ok'; + } catch { + return false; + } +} + +function writeAuthSession(): void { + try { + sessionStorage.setItem(AUTH_STORAGE_KEY, 'ok'); + } catch { + /* ignore quota / private mode */ + } +} + +function PasswordGate({ onUnlock }: { onUnlock: () => void }) { + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); + const [submitting, setSubmitting] = useState(false); + const inputRef = useRef(null); + + useEffect(() => { + inputRef.current?.focus(); + }, []); + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + setSubmitting(true); + setError(''); + + window.setTimeout(() => { + if (password === NAV_ACCESS_PASSWORD) { + writeAuthSession(); + onUnlock(); + return; + } + setError('密码不正确,请重试'); + setSubmitting(false); + inputRef.current?.focus(); + inputRef.current?.select(); + }, 120); + }; + + return ( +
+
+ + + +

Restricted Access

+

原型导航

+

仅供「数智部」开发团队使用,无法访问请联系「王冕」

+ +
+
+ + { + setPassword(event.target.value); + if (error) setError(''); + }} + placeholder="请输入密码" + autoComplete="current-password" + aria-invalid={Boolean(error)} + aria-describedby={error ? 'opn-gate-error' : undefined} + disabled={submitting} + /> +
+ {error && ( + + )} + +
+
+
+ ); +} + function NavLinkCard({ link, meta, @@ -54,7 +192,9 @@ function NavLinkCard({ }) { const Icon = iconForLink(link.title); const record = getPrototypeRegistryRecord(link.prototypeId); - const metaText = meta ?? linkMeta(link, record); + const metaText = meta ?? linkMeta(record); + const version = link.version || record?.version; + return ( ); } @@ -87,10 +230,10 @@ function SubGroupPanel({
- +

{group.title}

- {group.links.length} 个原型 + 共{group.links.length}个原型
{group.links.map((link) => ( @@ -109,6 +252,9 @@ function SectionBlock({ onSelectLink: (link: NavLinkItem) => void; }) { const Icon = section.icon; + const linkCount = section.links.length + + section.subGroups.reduce((sum, group) => sum + group.links.length, 0); + return (
- +
-

{section.title}

-

{section.description}

+
+

{section.title}

+ {linkCount}个原型页 +
+ {section.description &&

{section.description}

}
@@ -146,32 +295,43 @@ function SectionBlock({ ); } -function ChangelogEntryRow({ entry, isLatest }: { entry: PrototypeChangelogEntry; isLatest?: boolean }) { +function ChangelogEntryRow({ + entry, + isLatest, +}: { + entry: PrototypeChangelogEntry; + isLatest?: boolean; +}) { return (
  • -
    - {entry.version} - +
    + +
    +
    +
    + {entry.version} + +
    +

    {entry.summary}

    + {entry.files.length > 0 && ( +
    + + + 变更文件 + {entry.files.length} + +
      + {entry.files.map((file) => ( +
    • {file}
    • + ))} +
    +
    + )}
    -

    {entry.summary}

    - {entry.files.length > 0 && ( -
    - - 变更文件( - {entry.files.length} - ) - -
      - {entry.files.map((file) => ( -
    • {file}
    • - ))} -
    -
    - )}
  • ); } @@ -186,6 +346,7 @@ function ChangelogDetailPanel({ const record = getPrototypeRegistryRecord(link.prototypeId); const changelog = record?.changelog || []; const Icon = iconForLink(link.title); + const panelRef = useRef(null); useEffect(() => { const onKeyDown = (event: KeyboardEvent) => { @@ -194,6 +355,7 @@ function ChangelogDetailPanel({ document.addEventListener('keydown', onKeyDown); const previousOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; + window.requestAnimationFrame(() => panelRef.current?.focus()); return () => { document.removeEventListener('keydown', onKeyDown); document.body.style.overflow = previousOverflow; @@ -203,27 +365,29 @@ function ChangelogDetailPanel({ return (
    event.stopPropagation()} data-annotation-id={`opn-detail-${link.prototypeId}`} >
    - +
    +

    Prototype Changelog

    {link.title}

    - {record?.version || link.version || 'v1.0'} + {record?.version || link.version || 'v1.0'} {record?.lastUpdated && ( <> - {' '} - · 最近更新 - {' '} + · + {new Date(record.lastUpdated).toLocaleString('zh-CN', { month: 'numeric', day: 'numeric', @@ -235,7 +399,7 @@ function ChangelogDetailPanel({

    -
    @@ -259,17 +423,23 @@ function ChangelogDetailPanel({
    - +

    变更日志与历史记录

    {changelog.length > 0 ? (
      {changelog.map((entry, index) => ( - + ))}
    ) : ( -

    暂无自动记录的变更日志。原型更新并保存后,将在此展示版本说明与历史记录。

    +

    + 暂无自动记录的变更日志。原型更新并保存后,将在此展示版本说明与历史记录。 +

    )}
    @@ -289,34 +459,39 @@ function RecentUpdatesPanel({ return (
    -

    最近更新

    -

    点击条目查看变更说明与历史记录;右上角可在新标签页打开原型

    +
    +

    Activity

    +

    最近更新

    +
    +

    点击条目查看完整变更说明;右上角可在新标签页打开原型

      {updates.map((item) => (
    1. -
      - - {item.version} -
      -

      {item.summary}

      -

      - {item.date} - {' '} - {item.time} -

      +
    2. ))}
    @@ -325,6 +500,7 @@ function RecentUpdatesPanel({ } export default function OneosPrototypeNavApp() { + const [isAuthed, setIsAuthed] = useState(readAuthSession); const sections = useMemo(() => loadNavSections(), []); const recentUpdates = useMemo(() => loadRecentUpdates(), []); const registryUpdatedAt = useMemo(() => getRegistryUpdatedAt(), []); @@ -332,6 +508,8 @@ export default function OneosPrototypeNavApp() { const totalCount = useMemo(() => countNavLinks(sections), [sections]); const [query, setQuery] = useState(''); const [selectedLink, setSelectedLink] = useState(null); + const [activeSectionId, setActiveSectionId] = useState(() => resolveInitialSectionId(loadNavSections())); + const searchRef = useRef(null); const handleSelectLink = useCallback((link: NavLinkItem) => { setSelectedLink(link); @@ -341,6 +519,37 @@ export default function OneosPrototypeNavApp() { setSelectedLink(null); }, []); + const handleSelectSection = useCallback((sectionId: string) => { + setActiveSectionId(sectionId); + window.history.replaceState(null, '', `#opn-section-${sectionId}`); + document.getElementById('opn-main')?.scrollTo({ top: 0 }); + }, []); + + useEffect(() => { + const onHashChange = () => { + const fromHash = sectionIdFromHash(); + if (fromHash && sections.some((section) => section.id === fromHash)) { + setActiveSectionId(fromHash); + } + }; + window.addEventListener('hashchange', onHashChange); + return () => window.removeEventListener('hashchange', onHashChange); + }, [sections]); + + useEffect(() => { + const onKeyDown = (event: KeyboardEvent) => { + const tag = (event.target as HTMLElement | null)?.tagName; + const isTyping = tag === 'INPUT' || tag === 'TEXTAREA' || (event.target as HTMLElement)?.isContentEditable; + if (isTyping || selectedLink) return; + if (event.key === '/') { + event.preventDefault(); + searchRef.current?.focus(); + } + }; + document.addEventListener('keydown', onKeyDown); + return () => document.removeEventListener('keydown', onKeyDown); + }, [selectedLink]); + const normalizedQuery = query.trim().toLowerCase(); const filteredLinks = useMemo(() => { if (!normalizedQuery) return []; @@ -353,53 +562,57 @@ export default function OneosPrototypeNavApp() { }, [allLinks, normalizedQuery]); const visibleSections = useMemo(() => { - if (!normalizedQuery) return sections; - const matchedIds = new Set(filteredLinks.map((link) => link.prototypeId)); - return sections - .map((section) => { - const links = section.links.filter((link) => matchedIds.has(link.prototypeId)); - const subGroups = section.subGroups - .map((group) => ({ - ...group, - links: group.links.filter((link) => matchedIds.has(link.prototypeId)), - })) - .filter((group) => group.links.length > 0); - if (!links.length && !subGroups.length) return null; - return { ...section, links, subGroups }; - }) - .filter((section): section is NavSection => Boolean(section)); - }, [sections, normalizedQuery, filteredLinks]); + if (normalizedQuery) { + const matchedIds = new Set(filteredLinks.map((link) => link.prototypeId)); + return sections + .map((section) => { + const links = section.links.filter((link) => matchedIds.has(link.prototypeId)); + const subGroups = section.subGroups + .map((group) => ({ + ...group, + links: group.links.filter((link) => matchedIds.has(link.prototypeId)), + })) + .filter((group) => group.links.length > 0); + if (!links.length && !subGroups.length) return null; + return { ...section, links, subGroups }; + }) + .filter((section): section is NavSection => Boolean(section)); + } + + const activeSection = sections.find((section) => section.id === activeSectionId) || sections[0]; + return activeSection ? [activeSection] : []; + }, [sections, normalizedQuery, filteredLinks, activeSectionId]); + + if (!isAuthed) { + return ( +
    + setIsAuthed(true)} /> +
    + ); + } return (
    跳到导航内容 -
    -
    -

    - - ONE-OS · 原型中心 -

    -

    原型导航

    -

    - 按系统菜单分组浏览全部可交互原型;点击卡片先查看变更说明,再决定是否打开原型页面。 -

    - -
    - - +
    +
    +
    + + + + ONE-OS · Prototype Hub +
    +
    + + {totalCount} {' '} - 个原型入口 - - - {sections.length} - {' '} - 个一级分组 + 原型 {registryUpdatedAt && ( - - 注册表 + + 同步 {' '} {new Date(registryUpdatedAt).toLocaleString('zh-CN', { month: 'numeric', @@ -410,16 +623,27 @@ export default function OneosPrototypeNavApp() { )}
    +
    +
    + +
    +
    +

    Prototype Directory

    +

    + 「OneOS」全环节产品原型管理 +

    -
    +
    -
    +
    {!normalizedQuery && (
    ) : ( visibleSections.map((section) => ( - + )) )} diff --git a/src/prototypes/oneos-prototype-nav/nav-data.ts b/src/prototypes/oneos-prototype-nav/nav-data.ts index 6d1dc89..c9febf2 100644 --- a/src/prototypes/oneos-prototype-nav/nav-data.ts +++ b/src/prototypes/oneos-prototype-nav/nav-data.ts @@ -95,7 +95,7 @@ const GROUP_ICONS: Record = { const SECTION_META: Record = { OneOS: { icon: Layers, - description: '按业务菜单分组,进入各功能模块的可交互原型。', + description: '', }, 小羚羚: { icon: Smartphone, diff --git a/src/prototypes/oneos-prototype-nav/prototype-registry.json b/src/prototypes/oneos-prototype-nav/prototype-registry.json index 87fff73..10ba74f 100644 --- a/src/prototypes/oneos-prototype-nav/prototype-registry.json +++ b/src/prototypes/oneos-prototype-nav/prototype-registry.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-07-09T09:39:48.227Z", + "updatedAt": "2026-07-09T10:33:56.938Z", "prototypes": { "oneos-web-workbench": { "title": "工作台", @@ -975,10 +975,10 @@ }, "vehicle-h2-fee-ledger": { "title": "车辆氢费明细", - "version": "v1.4", - "revision": 4, - "lastUpdated": "2026-07-09T09:39:46.953Z", - "contentHash": "1515eef3f81e6a77708c09e77ee238e52fd5f0a33fd2c8301050cafe089d4881", + "version": "v1.5", + "revision": 5, + "lastUpdated": "2026-07-09T10:01:49.887Z", + "contentHash": "ffe27c79a960ac86f3ecfe0ebe9d976c0a51a30c51fe2773439297e02b86dee3", "trackedFiles": [ ".spec/PRD.md", ".spec/prototype-comments.json", @@ -987,6 +987,19 @@ "index.tsx" ], "changelog": [ + { + "version": "v1.5", + "date": "2026-07-09", + "time": "18:01", + "summary": "更新需求说明与标注", + "files": [ + ".spec/PRD.md", + ".spec/prototype-comments.json", + "globals.ts", + "H2LedgerPage.jsx", + "index.tsx" + ] + }, { "version": "v1.4", "date": "2026-07-09", @@ -1221,9 +1234,80 @@ "vrsV2Data.js" ], "changelog": [] + }, + "oneos-prototype-nav": { + "title": "原型导航", + "version": "v1.1", + "revision": 1, + "lastUpdated": "2026-07-09T10:33:56.745Z", + "contentHash": "564fe84bcf20fddeb53e953a70dc3eb8fe07806d767fecb8c1597c6d1ebd4b21", + "trackedFiles": [ + ".spec/prototype-comments.json", + "index.tsx", + "nav-data.ts", + "style.css", + "xll-nav-menu.json", + "xll-nav-source.json" + ], + "changelog": [ + { + "version": "v1.1", + "date": "2026-07-09", + "time": "18:31", + "summary": "VoltAgent 视觉改版、密码门禁与侧栏按业务线切换、批注体验优化", + "files": [ + "index.tsx", + "style.css", + "nav-data.ts", + ".spec/prototype-comments.json" + ] + }, + { + "version": "v1.0", + "date": "2026-07-09", + "time": "17:52", + "summary": "原型导航与版本追踪上线", + "files": [ + "index.tsx", + "style.css", + "nav-data.ts", + "nav-menu.json", + "prototype-registry.json" + ] + } + ] } }, "recentUpdates": [ + { + "prototypeId": "oneos-prototype-nav", + "title": "原型导航", + "version": "v1.1", + "date": "2026-07-09", + "time": "18:31", + "summary": "VoltAgent 视觉改版、密码门禁与侧栏按业务线切换、批注体验优化", + "files": [ + "index.tsx", + "style.css", + "nav-data.ts", + ".spec/prototype-comments.json" + ] + }, + { + "prototypeId": "vehicle-h2-fee-ledger", + "title": "车辆氢费明细", + "version": "v1.5", + "date": "2026-07-09", + "time": "18:01", + "summary": "更新需求说明与标注", + "files": [ + ".spec/PRD.md", + ".spec/prototype-comments.json", + "globals.ts", + "H2LedgerPage.jsx", + "index.tsx" + ] + }, { "prototypeId": "payment-records", "title": "收款记录", @@ -1988,50 +2072,6 @@ "components/RiskTagDrawer.tsx", "components/RiskTagForm.tsx" ] - }, - { - "prototypeId": "vehicle-management", - "title": "车辆资产", - "version": "v1.3", - "date": "2026-07-09", - "time": "17:39", - "summary": "更新需求说明与标注", - "files": [ - ".spec/prototype-comments.json", - ".spec/prototype-review.md", - ".spec/requirements-prd-detail.md", - ".spec/requirements-prd-list.md", - ".spec/ui-review.md", - "annotation-source.json", - "components/DateRangeCalendarPanel.tsx", - "components/DateRangeFilterField.tsx", - "components/DetailAccidentRecordsTab.tsx", - "components/DetailAnnualReviewRecordsTab.tsx", - "components/DetailBasicInfoTab.tsx", - "components/DetailExpireField.tsx" - ] - }, - { - "prototypeId": "vehicle-management", - "title": "车辆资产", - "version": "v1.2", - "date": "2026-07-09", - "time": "17:38", - "summary": "更新需求说明与标注", - "files": [ - ".spec/prototype-comments.json", - ".spec/prototype-review.md", - ".spec/requirements-prd-detail.md", - ".spec/requirements-prd-list.md", - ".spec/ui-review.md", - "annotation-source.json", - "components/DateRangeCalendarPanel.tsx", - "components/DateRangeFilterField.tsx", - "components/DetailAccidentRecordsTab.tsx", - "components/DetailAnnualReviewRecordsTab.tsx", - "components/DetailBasicInfoTab.tsx", - "components/DetailExpireField.tsx" - ] } ] } diff --git a/src/prototypes/oneos-prototype-nav/style.css b/src/prototypes/oneos-prototype-nav/style.css index 9f2d142..b4af957 100644 --- a/src/prototypes/oneos-prototype-nav/style.css +++ b/src/prototypes/oneos-prototype-nav/style.css @@ -1,29 +1,45 @@ -@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,400;0,14..32,500;0,14..32,600;0,14..32,700;1,14..32,400&display=swap'); .opn-page { - --opn-bg: #f8fafc; - --opn-surface: #ffffff; - --opn-surface-muted: #f1f5f9; - --opn-ink: #0f172a; - --opn-body: #334155; - --opn-muted: #64748b; - --opn-line: #e2e8f0; - --opn-primary: #2563eb; - --opn-primary-soft: #dbeafe; - --opn-primary-hover: #1d4ed8; - --opn-radius: 16px; - --opn-radius-sm: 12px; - --opn-shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 8px 24px rgba(15, 23, 42, 0.06); - --opn-shadow-hover: 0 4px 12px rgba(37, 99, 235, 0.12), 0 16px 32px rgba(15, 23, 42, 0.08); + /* VoltAgent design tokens */ + --opn-canvas: #101010; + --opn-surface-1: #101010; + --opn-surface-2: #1a1a1a; + --opn-surface-3: #1a1a1a; + --opn-hairline: #3d3a39; + --opn-hairline-strong: #3d3a39; + --opn-hairline-tertiary: #565656; + --opn-ink: #f2f2f2; + --opn-ink-muted: #bdbdbd; + --opn-ink-subtle: #8b949e; + --opn-ink-tertiary: #8b949e; + --opn-primary: #00d992; + --opn-primary-hover: #2fd6a1; + --opn-primary-focus: #10b981; + --opn-on-primary: #101010; + --opn-success: #00d992; + + --opn-radius-xs: 4px; + --opn-radius-sm: 6px; + --opn-radius-md: 8px; + --opn-radius-lg: 8px; + --opn-radius-xl: 8px; + --opn-radius-pill: 9999px; + + --opn-font-display: Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --opn-font-mono: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace; min-height: 100vh; - background: - radial-gradient(ellipse 80% 50% at 50% -10%, rgba(37, 99, 235, 0.08), transparent), - var(--opn-bg); + background-color: var(--opn-canvas); + background-image: + radial-gradient(ellipse 90% 50% at 50% -20%, rgba(0, 217, 146, 0.08), transparent 60%); color: var(--opn-ink); - font-family: 'Plus Jakarta Sans', Inter, system-ui, -apple-system, sans-serif; + font-family: var(--opn-font-display); font-size: 16px; - line-height: 1.6; + line-height: 1.625; + letter-spacing: 0; + font-feature-settings: 'calt' 1, 'rlig' 1; + -webkit-font-smoothing: antialiased; } .opn-skip { @@ -33,8 +49,8 @@ z-index: 100; padding: 12px 16px; background: var(--opn-primary); - color: #fff; - border-radius: var(--opn-radius-sm); + color: var(--opn-on-primary); + border-radius: var(--opn-radius-md); } .opn-skip:focus { @@ -42,85 +58,311 @@ top: 16px; } +.opn-page--gate { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 24px 16px; +} + +.opn-gate { + width: min(420px, 100%); +} + +.opn-gate-card { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + padding: 32px 28px 28px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-md); + background: var(--opn-canvas); + text-align: center; + box-sizing: border-box; +} + +.opn-gate-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 48px; + height: 48px; + margin-bottom: 16px; + border-radius: var(--opn-radius-lg); + border: 1px solid var(--opn-hairline); + background: var(--opn-surface-2); + color: var(--opn-primary-hover); +} + +.opn-gate-eyebrow { + margin: 0 0 8px; + color: var(--opn-primary); + font-size: 12px; + font-weight: 600; + letter-spacing: 0.18em; + text-transform: uppercase; +} + +.opn-gate-title { + margin: 0 0 8px; + font-size: 28px; + font-weight: 400; + letter-spacing: -0.02em; + color: #ffffff; +} + +.opn-gate-lead { + margin: 0 0 24px; + color: var(--opn-ink-muted); + font-size: 14px; + line-height: 1.6; +} + +.opn-gate-form { + display: flex; + flex-direction: column; + gap: 12px; + width: 100%; + margin: 0; + text-align: left; + align-items: stretch; +} + +.opn-gate-field { + display: flex; + flex-direction: column; + gap: 8px; + width: 100%; +} + +.opn-gate-label { + display: block; + margin: 0; + color: var(--opn-ink-muted); + font-size: 13px; + font-weight: 500; + line-height: 1.4; +} + +.opn-gate-input { + display: block; + width: 100%; + min-height: 48px; + margin: 0; + padding: 12px 14px; + border: 1px solid var(--opn-hairline-strong); + border-radius: var(--opn-radius-md); + background: var(--opn-surface-2); + color: var(--opn-ink); + font: inherit; + font-size: 16px; + line-height: 1.4; + outline: none; + box-sizing: border-box; + transition: border-color 150ms ease, box-shadow 150ms ease; +} + +.opn-gate-input::placeholder { + color: var(--opn-ink-subtle); +} + +.opn-gate-input:focus { + border-color: var(--opn-primary); + box-shadow: 0 0 0 2px rgba(0, 217, 146, 0.25); +} + +.opn-gate-input--error { + border-color: rgba(239, 68, 68, 0.55); +} + +.opn-gate-input--error:focus { + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.18); +} + +.opn-gate-error { + margin: 0; + color: #f87171; + font-size: 13px; +} + +.opn-gate-submit { + width: 100%; + margin-top: 4px; + min-height: 44px; + padding: 12px 16px; + border: 0; + border-radius: var(--opn-radius-sm); + background: var(--opn-primary); + color: var(--opn-on-primary); + font: inherit; + font-size: 16px; + font-weight: 600; + cursor: pointer; + box-sizing: border-box; + transition: background 150ms ease, opacity 150ms ease; +} + +.opn-gate-submit:hover:not(:disabled) { + background: var(--opn-primary-hover); +} + +.opn-gate-submit:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +.opn-gate-submit:focus-visible { + outline: 2px solid var(--opn-primary-focus); + outline-offset: 2px; +} + +/* ── Top bar ── */ +.opn-topbar { + position: sticky; + top: 0; + z-index: 50; + border-bottom: 1px solid var(--opn-hairline); + background: rgba(16, 16, 16, 0.92); + backdrop-filter: blur(12px); +} + +.opn-topbar-inner { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + max-width: 1200px; + margin: 0 auto; + padding: 0 24px; + min-height: 56px; +} + +.opn-brand { + display: inline-flex; + align-items: center; + gap: 10px; +} + +.opn-brand-mark { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: var(--opn-radius-sm); + border: 1px solid var(--opn-hairline); + background: var(--opn-surface-2); + color: var(--opn-primary); +} + +.opn-brand-text { + font-size: 14px; + font-weight: 600; + color: var(--opn-ink); + letter-spacing: -0.01em; +} + +.opn-topbar-stats { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.opn-topbar-stat { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-pill); + background: var(--opn-surface-1); + color: var(--opn-ink-muted); + font-size: 12px; + font-weight: 500; + font-variant-numeric: tabular-nums; +} + +.opn-topbar-stat--muted { + color: var(--opn-ink-subtle); + border-color: transparent; + background: transparent; +} + +/* ── Hero ── */ .opn-hero { - border-bottom: 1px solid var(--opn-line); - background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%); + border-bottom: 1px dashed rgba(79, 93, 117, 0.4); } .opn-hero-inner { - max-width: 1120px; + max-width: 1200px; margin: 0 auto; - padding: 40px 24px 32px; + padding: 48px 24px 36px; } .opn-eyebrow { - display: inline-flex; - align-items: center; - gap: 8px; - margin: 0 0 12px; + margin: 0 0 14px; color: var(--opn-primary); - font-size: 14px; + font-size: 12px; font-weight: 600; - letter-spacing: 0.02em; + letter-spacing: 0.18em; + text-transform: uppercase; } .opn-hero h1 { - margin: 0 0 12px; - font-size: clamp(28px, 4vw, 40px); - font-weight: 800; - line-height: 1.15; + display: flex; + flex-direction: column; + gap: 8px; + margin: 0 0 14px; +} + +.opn-hero-accent { + font-size: clamp(32px, 5vw, 48px); + font-weight: 400; + line-height: 1.1; letter-spacing: -0.02em; + color: #ffffff; +} + +.opn-hero-sub { + font-size: clamp(18px, 2.5vw, 22px); + font-weight: 500; + line-height: 1.3; + letter-spacing: -0.03em; + color: var(--opn-ink-muted); } .opn-lead { margin: 0; - max-width: 640px; - color: var(--opn-body); - font-size: 17px; -} - -.opn-hero-meta { - display: flex; - flex-wrap: wrap; - gap: 12px; - margin-top: 20px; -} - -.opn-stat { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 8px 14px; - border: 1px solid var(--opn-line); - border-radius: 999px; - background: var(--opn-surface); - color: var(--opn-body); - font-size: 14px; - font-weight: 600; + max-width: 620px; + color: var(--opn-ink-muted); + font-size: 16px; + line-height: 1.6; } .opn-search { display: flex; align-items: center; gap: 10px; - margin-top: 24px; - max-width: 480px; - padding: 12px 16px; - border: 1px solid var(--opn-line); - border-radius: var(--opn-radius); - background: var(--opn-surface); - box-shadow: var(--opn-shadow); + margin-top: 28px; + max-width: 520px; + padding: 0 14px; + min-height: 48px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-sm); + background: var(--opn-surface-2); transition: border-color 180ms ease, box-shadow 180ms ease; } .opn-search:focus-within { - border-color: rgba(37, 99, 235, 0.45); - box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12); + border-color: var(--opn-primary); + box-shadow: 0 0 0 2px rgba(0, 217, 146, 0.2); } .opn-search svg { flex-shrink: 0; - color: var(--opn-muted); + color: var(--opn-ink-subtle); } .opn-search input { @@ -134,7 +376,23 @@ } .opn-search input::placeholder { - color: var(--opn-muted); + color: var(--opn-ink-subtle); +} + +.opn-search-kbd { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 24px; + height: 22px; + padding: 0 6px; + border: 1px solid var(--opn-hairline-strong); + border-radius: var(--opn-radius-xs); + background: var(--opn-surface-2); + color: var(--opn-ink-subtle); + font-family: var(--opn-font-mono); + font-size: 11px; + line-height: 1; } .opn-search-clear { @@ -144,118 +402,135 @@ width: 32px; height: 32px; border: 0; - border-radius: 8px; + border-radius: var(--opn-radius-sm); background: transparent; - color: var(--opn-muted); + color: var(--opn-ink-subtle); cursor: pointer; transition: background 150ms ease, color 150ms ease; } .opn-search-clear:hover { - background: var(--opn-surface-muted); + background: var(--opn-surface-2); color: var(--opn-ink); } .opn-search-clear:focus-visible { - outline: 2px solid var(--opn-primary); + outline: 2px solid var(--opn-primary-focus); outline-offset: 2px; } +/* ── Layout ── */ .opn-layout { display: grid; - grid-template-columns: 240px minmax(0, 1fr); - gap: 32px; - max-width: 1120px; + grid-template-columns: 220px minmax(0, 1fr); + gap: 40px; + max-width: 1200px; margin: 0 auto; - padding: 28px 24px 56px; + padding: 32px 24px 72px; +} + +.opn-layout--search { + grid-template-columns: minmax(0, 1fr); + max-width: none; + width: 100%; +} + +.opn-layout--search .opn-main { + width: 100%; + min-width: 0; } .opn-sidebar { position: sticky; - top: 24px; + top: 80px; align-self: start; } .opn-sidebar-title { - margin: 0 0 12px; - color: var(--opn-muted); - font-size: 12px; - font-weight: 700; - letter-spacing: 0.08em; + margin: 0 0 10px; + padding: 0 12px; + color: var(--opn-primary); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.18em; text-transform: uppercase; } .opn-sidebar-nav { display: flex; flex-direction: column; - gap: 4px; + gap: 2px; } .opn-sidebar-link { display: flex; align-items: center; gap: 10px; - min-height: 44px; - padding: 10px 12px; + width: 100%; + min-height: 40px; + padding: 8px 12px; border-radius: var(--opn-radius-sm); - color: var(--opn-body); - font-size: 15px; - font-weight: 600; + border: 1px solid transparent; + border-left: 3px solid transparent; + background: transparent; + color: var(--opn-ink-muted); + font: inherit; + font-size: 14px; + font-weight: 500; + text-align: left; text-decoration: none; cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: background 150ms ease, color 150ms ease, border-color 150ms ease; } .opn-sidebar-link:hover { - background: var(--opn-primary-soft); - color: var(--opn-primary-hover); + background: var(--opn-surface-2); + color: var(--opn-ink); +} + +.opn-sidebar-link--active { + background: var(--opn-surface-2); + border-left-color: var(--opn-primary); + color: var(--opn-ink); +} + +.opn-sidebar-link--active svg { + color: var(--opn-primary); } .opn-sidebar-link:focus-visible { - outline: 2px solid var(--opn-primary); + outline: 2px solid var(--opn-primary-focus); outline-offset: 2px; } -.opn-sidebar-extra { - display: inline-flex; - align-items: center; - gap: 8px; - margin-top: 16px; - padding: 10px 12px; - color: var(--opn-muted); - font-size: 14px; - font-weight: 600; - text-decoration: none; - cursor: pointer; -} - -.opn-sidebar-extra:hover { - color: var(--opn-primary); -} - .opn-main { display: flex; flex-direction: column; - gap: 32px; + gap: 28px; min-width: 0; } .opn-search-result { margin: 0; - color: var(--opn-muted); + color: var(--opn-ink-subtle); font-size: 14px; } +.opn-search-result strong { + color: var(--opn-ink); + font-weight: 600; +} + +/* ── Sections ── */ .opn-section { padding: 24px; - border: 1px solid var(--opn-line); - border-radius: calc(var(--opn-radius) + 4px); - background: var(--opn-surface); - box-shadow: var(--opn-shadow); + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-md); + background: var(--opn-canvas); } .opn-section-header { - margin-bottom: 20px; + margin-bottom: 18px; } .opn-section-heading { @@ -268,109 +543,136 @@ display: inline-flex; align-items: center; justify-content: center; - width: 44px; - height: 44px; - border-radius: 12px; - background: var(--opn-primary-soft); + width: 40px; + height: 40px; + border-radius: var(--opn-radius-sm); + border: 1px solid var(--opn-hairline); + background: var(--opn-surface-2); color: var(--opn-primary); flex-shrink: 0; } +.opn-section-title-row { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 4px; +} + .opn-section-header h2 { - margin: 0 0 4px; - font-size: 22px; - font-weight: 800; - letter-spacing: -0.02em; + margin: 0; + font-size: 20px; + font-weight: 600; + letter-spacing: -0.03em; +} + +.opn-section-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 22px; + height: 22px; + padding: 0 8px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-pill); + background: var(--opn-canvas); + color: var(--opn-primary); + font-family: var(--opn-font-mono); + font-size: 11px; + font-weight: 550; + font-variant-numeric: tabular-nums; } .opn-section-header p { margin: 0; - color: var(--opn-muted); - font-size: 15px; + color: var(--opn-ink-muted); + font-size: 14px; } .opn-subgroup-stack { display: flex; flex-direction: column; - gap: 20px; + gap: 16px; } .opn-subgroup { - padding: 18px; - border: 1px solid var(--opn-line); - border-radius: var(--opn-radius); - background: var(--opn-surface-muted); + padding: 16px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-md); + background: var(--opn-surface-2); } .opn-subgroup-header { display: flex; align-items: center; gap: 10px; - margin-bottom: 14px; + margin-bottom: 12px; } .opn-subgroup-icon { display: inline-flex; align-items: center; justify-content: center; - width: 36px; - height: 36px; - border-radius: 10px; - background: var(--opn-surface); - color: var(--opn-primary); + width: 32px; + height: 32px; + border-radius: var(--opn-radius-md); + border: 1px solid var(--opn-hairline); + background: var(--opn-surface-1); + color: var(--opn-ink-muted); } .opn-subgroup-header h3 { margin: 0; flex: 1; - font-size: 17px; - font-weight: 700; + font-size: 15px; + font-weight: 600; + letter-spacing: -0.02em; } .opn-subgroup-count { - color: var(--opn-muted); - font-size: 13px; - font-weight: 600; + color: var(--opn-ink-tertiary); + font-size: 12px; + font-weight: 500; + font-variant-numeric: tabular-nums; } +/* ── Link cards ── */ .opn-link-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); - gap: 12px; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 10px; } .opn-link-grid--featured { - margin-bottom: 20px; + margin-bottom: 16px; } .opn-link-card { display: flex; - align-items: center; + align-items: flex-start; gap: 12px; width: 100%; - min-height: 64px; - padding: 14px 16px; - border: 1px solid var(--opn-line); - border-radius: var(--opn-radius-sm); - background: var(--opn-surface); + min-height: 72px; + padding: 14px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-md); + background: var(--opn-canvas); color: inherit; text-align: left; font: inherit; cursor: pointer; transition: border-color 180ms ease, - box-shadow 180ms ease, - transform 180ms ease; + background 180ms ease; } .opn-link-card:hover { - border-color: rgba(37, 99, 235, 0.35); - box-shadow: var(--opn-shadow-hover); - transform: translateY(-1px); + border-color: var(--opn-primary); + background: var(--opn-surface-2); } .opn-link-card:focus-visible { - outline: 2px solid var(--opn-primary); + outline: 2px solid var(--opn-primary-focus); outline-offset: 2px; } @@ -378,160 +680,227 @@ display: inline-flex; align-items: center; justify-content: center; - width: 40px; - height: 40px; - border-radius: 10px; - background: var(--opn-primary-soft); - color: var(--opn-primary); + width: 36px; + height: 36px; + border-radius: var(--opn-radius-md); + border: 1px solid var(--opn-hairline-strong); + background: var(--opn-surface-2); + color: var(--opn-ink-muted); flex-shrink: 0; + transition: color 180ms ease, border-color 180ms ease; +} + +.opn-link-card:hover .opn-link-card-icon { + color: var(--opn-primary); + border-color: rgba(0, 217, 146, 0.45); } .opn-link-card-body { display: flex; flex-direction: column; - gap: 2px; + gap: 4px; min-width: 0; flex: 1; } +.opn-link-card-title-row { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; +} + .opn-link-card-title { - font-size: 15px; - font-weight: 700; + font-size: 14px; + font-weight: 600; color: var(--opn-ink); + letter-spacing: -0.02em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.opn-link-card-version { + flex-shrink: 0; + padding: 2px 8px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-pill); + background: var(--opn-surface-2); + color: var(--opn-primary); + font-family: var(--opn-font-mono); + font-size: 10px; + font-weight: 550; + font-variant-numeric: tabular-nums; } .opn-link-card-meta { - color: var(--opn-muted); - font-size: 12px; - font-weight: 500; + color: var(--opn-ink-subtle); + font-family: var(--opn-font-mono); + font-size: 10px; + line-height: 1.35; + font-variant-numeric: tabular-nums; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .opn-link-card-arrow { flex-shrink: 0; - color: var(--opn-muted); + margin-top: 2px; + color: var(--opn-ink-subtle); transition: transform 180ms ease, color 180ms ease; } .opn-link-card:hover .opn-link-card-arrow { color: var(--opn-primary); - transform: translateX(2px); -} - -.opn-stat--muted { - color: var(--opn-muted); - font-weight: 500; } +/* ── Recent updates ── */ .opn-updates { padding: 24px; - border: 1px solid var(--opn-line); - border-radius: calc(var(--opn-radius) + 4px); - background: var(--opn-surface); - box-shadow: var(--opn-shadow); + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-md); + background: var(--opn-canvas); +} + +.opn-updates-header { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + justify-content: space-between; + gap: 12px; + margin-bottom: 14px; +} + +.opn-updates-eyebrow { + margin: 0 0 4px; + color: var(--opn-primary); + font-size: 12px; + font-weight: 600; + letter-spacing: 0.18em; + text-transform: uppercase; } .opn-updates-header h2 { - margin: 0 0 4px; + margin: 0; font-size: 20px; - font-weight: 800; + font-weight: 600; + letter-spacing: -0.03em; } -.opn-updates-header p { - margin: 0 0 16px; - color: var(--opn-muted); - font-size: 14px; +.opn-updates-header > p { + margin: 0; + max-width: 360px; + color: var(--opn-ink-muted); + font-size: 13px; + text-align: right; } .opn-updates-list { list-style: none; margin: 0; padding: 0; - display: flex; - flex-direction: column; - gap: 12px; + border-top: 1px solid var(--opn-hairline); } .opn-updates-item { - padding: 14px 16px; - border: 1px solid var(--opn-line); - border-radius: var(--opn-radius-sm); - background: var(--opn-surface-muted); -} - -.opn-updates-main { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - margin-bottom: 6px; + border-bottom: 1px solid var(--opn-hairline); } .opn-updates-link { - padding: 0; + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; + width: 100%; + padding: 16px 4px; border: 0; background: transparent; - color: var(--opn-ink); + color: inherit; font: inherit; - font-size: 15px; - font-weight: 700; text-align: left; cursor: pointer; + transition: background 150ms ease; } .opn-updates-link:hover { - color: var(--opn-primary-hover); + background: rgba(255, 255, 255, 0.02); } .opn-updates-link:focus-visible { - outline: 2px solid var(--opn-primary); - outline-offset: 2px; + outline: 2px solid var(--opn-primary-focus); + outline-offset: -2px; } -.opn-updates-version { - flex-shrink: 0; - padding: 4px 10px; - border-radius: 999px; - background: var(--opn-primary-soft); - color: var(--opn-primary-hover); - font-size: 12px; - font-weight: 700; - font-variant-numeric: tabular-nums; +.opn-updates-link-main { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; +} + +.opn-updates-link-title { + font-size: 15px; + font-weight: 600; + color: var(--opn-ink); + letter-spacing: -0.02em; } .opn-updates-summary { - margin: 0 0 4px; - color: var(--opn-body); - font-size: 14px; + color: var(--opn-ink-muted); + font-size: 13px; + line-height: 1.45; } -.opn-updates-meta { - margin: 0; - color: var(--opn-muted); - font-size: 12px; +.opn-updates-side { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 6px; + flex-shrink: 0; +} + +.opn-updates-version { + padding: 3px 8px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-pill); + background: var(--opn-surface-2); + color: var(--opn-primary); + font-family: var(--opn-font-mono); + font-size: 11px; + font-weight: 550; font-variant-numeric: tabular-nums; } +.opn-updates-meta { + color: var(--opn-ink-tertiary); + font-size: 11px; + font-variant-numeric: tabular-nums; +} + +/* ── Empty ── */ .opn-empty { - padding: 48px 24px; - border: 1px dashed var(--opn-line); - border-radius: var(--opn-radius); - background: var(--opn-surface); + padding: 56px 24px; + border: 1px dashed rgba(79, 93, 117, 0.4); + border-radius: var(--opn-radius-md); + background: var(--opn-canvas); text-align: center; } .opn-empty p { margin: 0 0 16px; - color: var(--opn-muted); + color: var(--opn-ink-subtle); } .opn-empty-btn { min-height: 44px; - padding: 10px 18px; + padding: 12px 16px; border: 0; - border-radius: 10px; + border-radius: var(--opn-radius-sm); background: var(--opn-primary); - color: #fff; + color: var(--opn-on-primary); font: inherit; + font-size: 16px; font-weight: 600; cursor: pointer; transition: background 150ms ease; @@ -542,55 +911,11 @@ } .opn-empty-btn:focus-visible { - outline: 2px solid var(--opn-primary); + outline: 2px solid var(--opn-primary-focus); outline-offset: 2px; } -@media (max-width: 900px) { - .opn-layout { - grid-template-columns: 1fr; - gap: 20px; - } - - .opn-sidebar { - position: static; - display: flex; - flex-wrap: wrap; - gap: 8px; - align-items: center; - } - - .opn-sidebar-title { - width: 100%; - } - - .opn-sidebar-nav { - flex-direction: row; - flex-wrap: wrap; - width: 100%; - } - - .opn-sidebar-extra { - margin-top: 0; - } -} - -@media (max-width: 560px) { - .opn-hero-inner, - .opn-layout { - padding-left: 16px; - padding-right: 16px; - } - - .opn-section { - padding: 18px; - } - - .opn-link-grid { - grid-template-columns: 1fr; - } -} - +/* ── Detail modal ── */ .opn-detail-overlay { position: fixed; inset: 0; @@ -598,18 +923,25 @@ display: flex; align-items: flex-start; justify-content: center; - padding: 24px 16px; - background: rgba(15, 23, 42, 0.45); + padding: 32px 16px; + background: rgba(0, 0, 0, 0.72); + backdrop-filter: blur(6px); overflow-y: auto; + animation: opn-fade-in 200ms ease; } .opn-detail-panel { - width: min(720px, 100%); + position: relative; + display: flex; + flex-direction: column; + width: min(680px, 100%); + max-height: min(88vh, 760px); margin: auto 0; - border: 1px solid var(--opn-line); - border-radius: calc(var(--opn-radius) + 4px); - background: var(--opn-surface); - box-shadow: 0 24px 48px rgba(15, 23, 42, 0.18); + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-md); + background: var(--opn-canvas); + animation: opn-slide-up 220ms cubic-bezier(0.22, 1, 0.36, 1); + overflow: hidden; } .opn-detail-header { @@ -617,8 +949,17 @@ align-items: flex-start; justify-content: space-between; gap: 16px; + flex-shrink: 0; padding: 20px 20px 16px; - border-bottom: 1px solid var(--opn-line); + border-bottom: 1px solid var(--opn-hairline); +} + +.opn-detail-toolbar { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; + padding-top: 2px; } .opn-detail-heading { @@ -632,46 +973,78 @@ display: inline-flex; align-items: center; justify-content: center; - width: 44px; - height: 44px; - border-radius: 12px; - background: var(--opn-primary-soft); + width: 40px; + height: 40px; + border-radius: var(--opn-radius-sm); + border: 1px solid var(--opn-hairline); + background: var(--opn-surface-2); color: var(--opn-primary); flex-shrink: 0; } -.opn-detail-header h2 { +.opn-detail-eyebrow { margin: 0 0 4px; + color: var(--opn-primary); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.18em; + text-transform: uppercase; +} + +.opn-detail-header h2 { + margin: 0 0 6px; font-size: 20px; - font-weight: 800; + font-weight: 600; + letter-spacing: -0.03em; } .opn-detail-subtitle { + display: inline-flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; margin: 0; - color: var(--opn-muted); - font-size: 14px; + color: var(--opn-ink-subtle); + font-size: 13px; font-variant-numeric: tabular-nums; } -.opn-detail-actions { - display: flex; - align-items: center; - gap: 8px; - flex-shrink: 0; +.opn-detail-version { + padding: 2px 8px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-pill); + background: var(--opn-surface-2); + color: var(--opn-primary); + font-family: var(--opn-font-mono); + font-size: 11px; + font-weight: 550; +} + +.opn-detail-dot { + color: var(--opn-ink-tertiary); +} + +.opn-detail-body { + flex: 1; + min-height: 0; + padding: 16px 20px 20px; + overflow-y: auto; } .opn-detail-open-btn { display: inline-flex; align-items: center; - gap: 8px; - min-height: 44px; - padding: 10px 16px; - border-radius: 10px; + gap: 6px; + min-height: 40px; + padding: 8px 12px; + border: 0; + border-radius: var(--opn-radius-sm); background: var(--opn-primary); - color: #fff; - font-size: 14px; - font-weight: 700; + color: var(--opn-on-primary); + font-size: 13px; + font-weight: 600; text-decoration: none; + white-space: nowrap; cursor: pointer; transition: background 150ms ease; } @@ -681,7 +1054,7 @@ } .opn-detail-open-btn:focus-visible { - outline: 2px solid var(--opn-primary); + outline: 2px solid var(--opn-primary-focus); outline-offset: 2px; } @@ -689,42 +1062,39 @@ display: inline-flex; align-items: center; justify-content: center; - width: 44px; - height: 44px; - border: 1px solid var(--opn-line); - border-radius: 10px; - background: var(--opn-surface); - color: var(--opn-muted); + width: 40px; + height: 40px; + border: 1px solid var(--opn-hairline); + border-radius: var(--opn-radius-md); + background: var(--opn-surface-1); + color: var(--opn-ink-subtle); cursor: pointer; - transition: background 150ms ease, color 150ms ease; + transition: background 150ms ease, color 150ms ease, border-color 150ms ease; } .opn-detail-close:hover { - background: var(--opn-surface-muted); + background: var(--opn-surface-3); + border-color: var(--opn-hairline-strong); color: var(--opn-ink); } .opn-detail-close:focus-visible { - outline: 2px solid var(--opn-primary); + outline: 2px solid var(--opn-primary-focus); outline-offset: 2px; } -.opn-detail-body { - padding: 16px 20px 24px; -} - .opn-detail-section-header { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; - color: var(--opn-body); + color: var(--opn-ink-muted); } .opn-detail-section-header h3 { margin: 0; - font-size: 16px; - font-weight: 700; + font-size: 14px; + font-weight: 600; } .opn-changelog-list { @@ -733,19 +1103,54 @@ padding: 0; display: flex; flex-direction: column; - gap: 12px; + gap: 0; } .opn-changelog-item { - padding: 14px 16px; - border: 1px solid var(--opn-line); - border-radius: var(--opn-radius-sm); - background: var(--opn-surface-muted); + display: grid; + grid-template-columns: 20px minmax(0, 1fr); + gap: 12px; + padding: 14px 0; + border-bottom: 1px solid var(--opn-hairline); } -.opn-changelog-item--latest { - border-color: rgba(37, 99, 235, 0.35); - background: linear-gradient(180deg, #eff6ff 0%, #f8fafc 100%); +.opn-changelog-item:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +.opn-changelog-item--latest .opn-changelog-dot { + background: var(--opn-primary); + box-shadow: 0 0 0 4px rgba(0, 217, 146, 0.2); +} + +.opn-changelog-rail { + position: relative; + display: flex; + justify-content: center; + padding-top: 6px; +} + +.opn-changelog-rail::before { + content: ''; + position: absolute; + top: 14px; + bottom: -14px; + width: 1px; + background: var(--opn-hairline); +} + +.opn-changelog-item:last-child .opn-changelog-rail::before { + display: none; +} + +.opn-changelog-dot { + position: relative; + z-index: 1; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--opn-hairline-strong); } .opn-changelog-item-head { @@ -753,61 +1158,180 @@ align-items: center; justify-content: space-between; gap: 12px; - margin-bottom: 8px; + margin-bottom: 6px; } .opn-changelog-version { - padding: 4px 10px; - border-radius: 999px; - background: var(--opn-primary-soft); - color: var(--opn-primary-hover); - font-size: 12px; - font-weight: 700; + padding: 3px 8px; + border-radius: var(--opn-radius-pill); + background: var(--opn-surface-3); + color: var(--opn-ink-muted); + font-family: var(--opn-font-mono); + font-size: 11px; + font-weight: 500; font-variant-numeric: tabular-nums; } +.opn-changelog-item--latest .opn-changelog-version { + border-color: rgba(0, 217, 146, 0.35); + color: var(--opn-primary); +} + .opn-changelog-time { - color: var(--opn-muted); - font-size: 12px; + color: var(--opn-ink-tertiary); + font-size: 11px; font-variant-numeric: tabular-nums; } .opn-changelog-summary { margin: 0; - color: var(--opn-body); - font-size: 15px; - font-weight: 600; + color: var(--opn-ink-muted); + font-size: 14px; + font-weight: 500; + line-height: 1.5; } .opn-changelog-files { margin-top: 10px; - color: var(--opn-muted); - font-size: 13px; + color: var(--opn-ink-subtle); + font-size: 12px; } .opn-changelog-files summary { + display: inline-flex; + align-items: center; + gap: 6px; cursor: pointer; - font-weight: 600; + font-weight: 500; + color: var(--opn-ink-subtle); + list-style: none; +} + +.opn-changelog-files summary::-webkit-details-marker { + display: none; +} + +.opn-changelog-files-count { + padding: 1px 6px; + border-radius: var(--opn-radius-pill); + background: var(--opn-surface-3); + font-family: var(--opn-font-mono); + font-size: 10px; } .opn-changelog-files ul { margin: 8px 0 0; - padding-left: 18px; + padding-left: 0; + list-style: none; } .opn-changelog-files li { - margin: 2px 0; + margin: 4px 0; + padding: 4px 8px; + border-radius: var(--opn-radius-xs); + background: var(--opn-surface-1); + font-family: var(--opn-font-mono); + font-size: 11px; word-break: break-all; } .opn-detail-empty { margin: 0; - padding: 20px; - border: 1px dashed var(--opn-line); - border-radius: var(--opn-radius-sm); - color: var(--opn-muted); + padding: 24px; + border: 1px dashed var(--opn-hairline-strong); + border-radius: var(--opn-radius-lg); + color: var(--opn-ink-subtle); font-size: 14px; text-align: center; + line-height: 1.6; +} + +@keyframes opn-fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes opn-slide-up { + from { + opacity: 0; + transform: translateY(12px) scale(0.98); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +/* ── Responsive ── */ +@media (max-width: 960px) { + .opn-layout { + grid-template-columns: 1fr; + gap: 20px; + padding-top: 24px; + } + + .opn-sidebar { + position: static; + padding-bottom: 4px; + border-bottom: 1px solid var(--opn-hairline); + } + + .opn-sidebar-nav { + flex-direction: row; + flex-wrap: wrap; + gap: 6px; + padding-bottom: 12px; + } + + .opn-updates-header > p { + text-align: left; + max-width: none; + } +} + +@media (max-width: 640px) { + .opn-topbar-inner, + .opn-hero-inner, + .opn-layout { + padding-left: 16px; + padding-right: 16px; + } + + .opn-hero-inner { + padding-top: 36px; + } + + .opn-section, + .opn-updates { + padding: 16px; + } + + .opn-link-grid { + grid-template-columns: 1fr; + } + + .opn-detail-header { + flex-direction: column; + align-items: stretch; + } + + .opn-detail-toolbar { + width: 100%; + justify-content: flex-end; + padding-top: 0; + } + + .opn-updates-link { + flex-direction: column; + gap: 10px; + } + + .opn-updates-side { + flex-direction: row; + align-items: center; + width: 100%; + justify-content: space-between; + } } @media (prefers-reduced-motion: reduce) { @@ -816,11 +1340,19 @@ .opn-search, .opn-sidebar-link, .opn-search-clear, - .opn-empty-btn { + .opn-empty-btn, + .opn-detail-overlay, + .opn-detail-panel, + .opn-updates-link { transition: none; + animation: none; } .opn-link-card:hover { transform: none; } + + .opn-link-card:hover .opn-link-card-arrow { + transform: none; + } } diff --git a/src/prototypes/vehicle-h2-fee-ledger/annotation-source.json b/src/prototypes/vehicle-h2-fee-ledger/annotation-source.json index cb337c4..bd83655 100644 --- a/src/prototypes/vehicle-h2-fee-ledger/annotation-source.json +++ b/src/prototypes/vehicle-h2-fee-ledger/annotation-source.json @@ -5,7 +5,7 @@ "version": 2, "prototypeName": "vehicle-h2-fee-ledger", "pageId": "list", - "updatedAt": 1783589987168, + "updatedAt": 1783588824941, "nodes": [ { "id": "vh2-header", @@ -300,13 +300,6 @@ "title": "ONE-OS 原型导航", "defaultExpanded": true, "children": [ - { - "type": "link", - "id": "nav-link-oneos-prototype-nav", - "title": "原型导航", - "href": "/prototypes/oneos-prototype-nav", - "target": "self" - }, { "type": "folder", "id": "nav-folder-1782874576229-r8atbu", @@ -321,6 +314,112 @@ "target": "self" } ] + }, + { + "type": "link", + "id": "nav-link-lease-business-line-overview", + "title": "业务条线说明", + "href": "/prototypes/lease-business-line-overview", + "target": "self" + }, + { + "type": "folder", + "id": "nav-folder-prototypes-oneos-web", + "title": "ONE-OS Web 端", + "defaultExpanded": true, + "children": [ + { + "type": "link", + "id": "nav-link-oneos-web-login", + "title": "登录", + "href": "/prototypes/oneos-web-login", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-vehicle-asset", + "title": "车辆管理", + "href": "/prototypes/oneos-web-vehicle-asset", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-contract-template", + "title": "合同模板管理", + "href": "/prototypes/oneos-web-contract-template", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-help-center", + "title": "帮助中心", + "href": "/prototypes/oneos-web-help-center", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-finance", + "title": "财务管理", + "href": "/prototypes/oneos-web-finance", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-procurement", + "title": "采购管理", + "href": "/prototypes/oneos-web-procurement", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-lease-contract", + "title": "车辆租赁合同", + "href": "/prototypes/oneos-web-lease-contract", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-h2-station", + "title": "加氢站管理", + "href": "/prototypes/oneos-web-h2-station", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-data-analysis", + "title": "数据分析", + "href": "/prototypes/oneos-web-data-analysis", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-ledger-data", + "title": "台账数据", + "href": "/prototypes/oneos-web-ledger-data", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-business", + "title": "业务管理", + "href": "/prototypes/oneos-web-business", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-ops", + "title": "运维管理", + "href": "/prototypes/oneos-web-ops", + "target": "self" + }, + { + "type": "link", + "id": "nav-link-oneos-web-requirements", + "title": "需求说明", + "href": "/prototypes/oneos-web-requirements", + "target": "self" + } + ] } ] },