Merge pull request #148 from alibaba/feat/drop-hash-router
feat(website): drop hash route; upgrade styling
This commit is contained in:
@@ -11,7 +11,7 @@ The GUI Agent Living in Your Webpage. Control web interfaces with natural langua
|
|||||||
|
|
||||||
🌐 **English** | [中文](./docs/README-zh.md)
|
🌐 **English** | [中文](./docs/README-zh.md)
|
||||||
|
|
||||||
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/#/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
||||||
|
|
||||||
<video id="demo-video" src="https://github.com/user-attachments/assets/34d8444d-cbfb-44a3-a24e-fd5c167bb0bf" controls crossorigin muted></video>
|
<video id="demo-video" src="https://github.com/user-attachments/assets/34d8444d-cbfb-44a3-a24e-fd5c167bb0bf" controls crossorigin muted></video>
|
||||||
|
|
||||||
@@ -28,14 +28,14 @@ The GUI Agent Living in Your Webpage. Control web interfaces with natural langua
|
|||||||
- No special permissions required.
|
- No special permissions required.
|
||||||
- **🧠 Bring your own LLMs**
|
- **🧠 Bring your own LLMs**
|
||||||
- **🎨 Pretty UI with human-in-the-loop**
|
- **🎨 Pretty UI with human-in-the-loop**
|
||||||
- **🐙 Optional [chrome extension](https://alibaba.github.io/page-agent/#/docs/features/chrome-extension) for multi-page tasks.**
|
- **🐙 Optional [chrome extension](https://alibaba.github.io/page-agent/docs/features/chrome-extension) for multi-page tasks.**
|
||||||
|
|
||||||
## 💡 Use Cases
|
## 💡 Use Cases
|
||||||
|
|
||||||
- **SaaS AI Copilot** — Ship an AI copilot in your product in lines of code. No backend rewrite needed.
|
- **SaaS AI Copilot** — Ship an AI copilot in your product in lines of code. No backend rewrite needed.
|
||||||
- **Smart Form Filling** — Turn 20-click workflows into one sentence. Perfect for ERP, CRM, and admin systems.
|
- **Smart Form Filling** — Turn 20-click workflows into one sentence. Perfect for ERP, CRM, and admin systems.
|
||||||
- **Accessibility** — Make any web app accessible through natural language. Voice commands, screen readers, zero barrier.
|
- **Accessibility** — Make any web app accessible through natural language. Voice commands, screen readers, zero barrier.
|
||||||
- **Multi-page Agent** — Extend your agent's reach across browser tabs with the optional [chrome extension](https://alibaba.github.io/page-agent/#/docs/features/chrome-extension).
|
- **Multi-page Agent** — Extend your agent's reach across browser tabs with the optional [chrome extension](https://alibaba.github.io/page-agent/docs/features/chrome-extension).
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ const agent = new PageAgent({
|
|||||||
await agent.execute('Click the login button')
|
await agent.execute('Click the login button')
|
||||||
```
|
```
|
||||||
|
|
||||||
For more programmatic usage, see [📖 Documentations](https://alibaba.github.io/page-agent/#/docs/introduction/overview).
|
For more programmatic usage, see [📖 Documentations](https://alibaba.github.io/page-agent/docs/introduction/overview).
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ interface PageAgentConfig {
|
|||||||
- Single-page application only (cannot navigate across pages)
|
- Single-page application only (cannot navigate across pages)
|
||||||
- No visual recognition (relies on DOM structure)
|
- No visual recognition (relies on DOM structure)
|
||||||
- Limited interaction support (no hover, drag-drop, canvas operations)
|
- Limited interaction support (no hover, drag-drop, canvas operations)
|
||||||
- See [Limitations](https://alibaba.github.io/page-agent/#/docs/introduction/limitations) for details
|
- See [Limitations](https://alibaba.github.io/page-agent/docs/introduction/limitations) for details
|
||||||
|
|
||||||
### Acknowledgments
|
### Acknowledgments
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
🌐 [English](../README.md) | **中文**
|
🌐 [English](../README.md) | **中文**
|
||||||
|
|
||||||
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/#/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
👉 <a href="https://alibaba.github.io/page-agent/" target="_blank"><b>🚀 Demo</b></a> | <a href="https://alibaba.github.io/page-agent/docs/introduction/overview" target="_blank"><b>📖 Documentation</b></a>
|
||||||
|
|
||||||
<video id="demo-video" src="https://github.com/user-attachments/assets/34d8444d-cbfb-44a3-a24e-fd5c167bb0bf" controls crossorigin muted></video>
|
<video id="demo-video" src="https://github.com/user-attachments/assets/34d8444d-cbfb-44a3-a24e-fd5c167bb0bf" controls crossorigin muted></video>
|
||||||
|
|
||||||
@@ -28,14 +28,14 @@
|
|||||||
- 无需特殊权限。
|
- 无需特殊权限。
|
||||||
- **🧠 用你自己的 LLM**
|
- **🧠 用你自己的 LLM**
|
||||||
- **🎨 精美 UI,支持人机协同**
|
- **🎨 精美 UI,支持人机协同**
|
||||||
- **🐙 可选的 [Chrome 扩展](https://alibaba.github.io/page-agent/#/docs/features/chrome-extension),支持跨页面任务。**
|
- **🐙 可选的 [Chrome 扩展](https://alibaba.github.io/page-agent/docs/features/chrome-extension),支持跨页面任务。**
|
||||||
|
|
||||||
## 💡 应用场景
|
## 💡 应用场景
|
||||||
|
|
||||||
- **SaaS AI 副驾驶** — 几行代码为你的产品加上 AI 副驾驶,不需要重写后端。
|
- **SaaS AI 副驾驶** — 几行代码为你的产品加上 AI 副驾驶,不需要重写后端。
|
||||||
- **智能表单填写** — 把 20 次点击变成一句话。ERP、CRM、管理后台的最佳拍档。
|
- **智能表单填写** — 把 20 次点击变成一句话。ERP、CRM、管理后台的最佳拍档。
|
||||||
- **无障碍增强** — 用自然语言让任何网页无障碍。语音指令、屏幕阅读器,零门槛。
|
- **无障碍增强** — 用自然语言让任何网页无障碍。语音指令、屏幕阅读器,零门槛。
|
||||||
- **跨页面 Agent** — 通过可选的 [Chrome 扩展](https://alibaba.github.io/page-agent/#/docs/features/chrome-extension),让你的 Agent 跨标签页工作。
|
- **跨页面 Agent** — 通过可选的 [Chrome 扩展](https://alibaba.github.io/page-agent/docs/features/chrome-extension),让你的 Agent 跨标签页工作。
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 🚀 快速开始
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ const agent = new PageAgent({
|
|||||||
await agent.execute('点击登录按钮')
|
await agent.execute('点击登录按钮')
|
||||||
```
|
```
|
||||||
|
|
||||||
更多编程用法,请参阅 [📖 文档](https://alibaba.github.io/page-agent/#/docs/introduction/overview)。
|
更多编程用法,请参阅 [📖 文档](https://alibaba.github.io/page-agent/docs/introduction/overview)。
|
||||||
|
|
||||||
## 🤝 贡献
|
## 🤝 贡献
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export function EmptyState() {
|
|||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://alibaba.github.io/page-agent/#/docs/features/chrome-extension"
|
href="https://alibaba.github.io/page-agent/docs/features/chrome-extension"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="hover:text-foreground transition-colors"
|
className="hover:text-foreground transition-colors"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function parseLLMConfig(config: LLMConfig): Required<LLMConfig> {
|
|||||||
if (!config.baseURL || !config.apiKey || !config.model) {
|
if (!config.baseURL || !config.apiKey || !config.model) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'[PageAgent] LLM configuration required. Please provide: baseURL, apiKey, model. ' +
|
'[PageAgent] LLM configuration required. Please provide: baseURL, apiKey, model. ' +
|
||||||
'See: https://alibaba.github.io/page-agent/#/docs/features/models'
|
'See: https://alibaba.github.io/page-agent/docs/features/models'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
- **React** with TypeScript
|
- **React** with TypeScript
|
||||||
- **Vite** for dev server and build
|
- **Vite** for dev server and build
|
||||||
- **Tailwind CSS** for styling
|
- **Tailwind CSS** for styling
|
||||||
- **shadcn/ui** (new-york style) for UI components
|
- **shadcn/ui** (new-york style) for UI components — **do NOT hand-edit `src/components/ui/` files**
|
||||||
- **Magic UI** for animations and effects
|
- **Magic UI** for animations and effects
|
||||||
- **wouter** with hash routing for static hosting
|
- **wouter** with browser routing (`base: "/page-agent"`)
|
||||||
- **lucide-react** for icons
|
- **lucide-react** for icons
|
||||||
|
|
||||||
## Component Guidelines
|
## Component Guidelines
|
||||||
@@ -53,58 +53,74 @@ Located in `src/components/ui/`:
|
|||||||
### Styling Rules
|
### Styling Rules
|
||||||
|
|
||||||
1. **Prefer Tailwind classes** over custom CSS
|
1. **Prefer Tailwind classes** over custom CSS
|
||||||
2. Use CSS modules only for complex component-specific styles
|
2. Support dark mode via `dark:` classes
|
||||||
3. Support dark mode via `dark:` classes
|
3. Use CSS variables from `src/index.css` for theme colors
|
||||||
4. Use CSS variables from `src/index.css` for theme colors
|
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
├── pages/
|
├── pages/
|
||||||
│ ├── Home.tsx # Homepage
|
│ ├── home/
|
||||||
|
│ │ ├── HomeContent.tsx # Homepage sections
|
||||||
|
│ │ └── ...Section.tsx
|
||||||
│ └── docs/
|
│ └── docs/
|
||||||
│ ├── Layout.tsx # Documentation sidebar
|
│ ├── index.tsx # Docs route switch
|
||||||
|
│ ├── Layout.tsx # Sidebar navigation
|
||||||
│ └── [section]/[topic]/page.tsx
|
│ └── [section]/[topic]/page.tsx
|
||||||
├── components/
|
├── components/
|
||||||
│ ├── ui/ # shadcn/ui + Magic UI components
|
│ ├── ui/ # shadcn/ui + Magic UI (DO NOT hand-edit)
|
||||||
|
│ ├── Heading.tsx # Anchor heading for doc pages
|
||||||
│ ├── Header.tsx # Site header
|
│ ├── Header.tsx # Site header
|
||||||
│ └── Footer.tsx # Site footer
|
│ └── Footer.tsx # Site footer
|
||||||
├── i18n/ # Internationalization
|
├── i18n/ # Internationalization
|
||||||
├── router.tsx # Central routing
|
├── router.tsx # Root layout + routing
|
||||||
└── main.tsx # App entry
|
└── main.tsx # App entry
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Routing
|
||||||
|
|
||||||
|
Uses wouter browser routing with base path for GitHub Pages deployment at `https://alibaba.github.io/page-agent/`.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// main.tsx
|
||||||
|
<Router base="/page-agent">
|
||||||
|
<PagesRouter />
|
||||||
|
</Router>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key rules:**
|
||||||
|
|
||||||
|
- Header and Footer live in `router.tsx` **outside** `<Switch>`, so they always see the root router context (`base="/page-agent"`)
|
||||||
|
- Docs pages are nested via `<Route path="/docs" nest>`, which creates a child context (`base="/page-agent/docs"`)
|
||||||
|
- Inside the docs nest, Link hrefs are relative to `/docs` (e.g. `href="/features/models"`, NOT `href="/docs/features/models"`)
|
||||||
|
- **Never use `~` prefix** in Link hrefs — it bypasses the base path entirely
|
||||||
|
- Doc page headings use `<Heading id="slug" level={2}>` for anchor links
|
||||||
|
|
||||||
|
### SPA on GitHub Pages
|
||||||
|
|
||||||
|
Instead of `404.html` redirects, the build copies `index.html` into every route directory. Add new routes to the `SPA_ROUTES` array in `vite.config.js`.
|
||||||
|
|
||||||
## Adding New Pages
|
## Adding New Pages
|
||||||
|
|
||||||
### Documentation Page
|
### Documentation Page
|
||||||
|
|
||||||
1. Create `src/pages/docs/<section>/<slug>/page.tsx`
|
1. Create `src/pages/docs/<section>/<slug>/page.tsx`
|
||||||
2. Add route to `src/router.tsx` with `<Header /> + <DocsLayout>` wrapper
|
2. Add route in `src/pages/docs/index.tsx`
|
||||||
3. Add navigation item to `pages/docs/Layout.tsx`
|
3. Add navigation item in `src/pages/docs/Layout.tsx`
|
||||||
|
4. Add path to `SPA_ROUTES` in `vite.config.js`
|
||||||
## Routing
|
|
||||||
|
|
||||||
Uses hash-based routing for static hosting:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { Router } from 'wouter'
|
|
||||||
import { useHashLocation } from 'wouter/use-hash-location'
|
|
||||||
|
|
||||||
;<Router hook={useHashLocation}>{/* routes */}</Router>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Files
|
## Configuration Files
|
||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
| ----------------- | ----------------------- |
|
| ----------------- | ----------------------- |
|
||||||
| `components.json` | shadcn/ui configuration |
|
| `components.json` | shadcn/ui configuration |
|
||||||
| `vite.config.js` | Vite build settings |
|
| `vite.config.js` | Vite build + SPA routes |
|
||||||
| `tsconfig.json` | TypeScript config |
|
| `tsconfig.json` | TypeScript config |
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm start # Dev server (from root)
|
npm start # Dev server (from root)
|
||||||
npm run build:website # Build website (from root)
|
npm run build:website # Build website (from root)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function Header() {
|
|||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link
|
<Link
|
||||||
href="~/"
|
href="/"
|
||||||
className="flex items-center gap-2 sm:gap-3 group shrink-0"
|
className="flex items-center gap-2 sm:gap-3 group shrink-0"
|
||||||
aria-label={isZh ? 'page-agent 首页' : 'page-agent home'}
|
aria-label={isZh ? 'page-agent 首页' : 'page-agent home'}
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
@@ -56,7 +56,7 @@ export default function Header() {
|
|||||||
aria-label="Mobile navigation"
|
aria-label="Mobile navigation"
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href="~/docs/introduction/overview"
|
href="/docs/introduction/overview"
|
||||||
className="p-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200 shrink-0"
|
className="p-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200 shrink-0"
|
||||||
aria-label={isZh ? '文档' : 'Docs'}
|
aria-label={isZh ? '文档' : 'Docs'}
|
||||||
>
|
>
|
||||||
@@ -90,7 +90,7 @@ export default function Header() {
|
|||||||
{import.meta.env.VERSION}
|
{import.meta.env.VERSION}
|
||||||
</span>
|
</span>
|
||||||
<Link
|
<Link
|
||||||
href="~/docs/introduction/overview"
|
href="/docs/introduction/overview"
|
||||||
className="flex items-center gap-1.5 text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200"
|
className="flex items-center gap-1.5 text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200"
|
||||||
>
|
>
|
||||||
<BookOpen className="w-4 h-4" />
|
<BookOpen className="w-4 h-4" />
|
||||||
@@ -138,7 +138,7 @@ export default function Header() {
|
|||||||
role="navigation"
|
role="navigation"
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href="~/docs/introduction/overview"
|
href="/docs/introduction/overview"
|
||||||
className="flex items-center gap-2 px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200"
|
className="flex items-center gap-2 px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-blue-600 dark:hover:text-blue-400 transition-colors duration-200"
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
>
|
>
|
||||||
|
|||||||
45
packages/website/src/components/Heading.tsx
Normal file
45
packages/website/src/components/Heading.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { ComponentPropsWithoutRef, useEffect, useRef } from 'react'
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
|
type Level = 2 | 3
|
||||||
|
|
||||||
|
interface HeadingProps extends Omit<ComponentPropsWithoutRef<'h2'>, 'children'> {
|
||||||
|
id: string
|
||||||
|
level?: Level
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const levelStyles = {
|
||||||
|
2: { tag: 'h2', className: 'text-2xl font-semibold mb-4' },
|
||||||
|
3: { tag: 'h3', className: 'text-xl font-semibold mb-3' },
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export function Heading({ id, level = 2, className, children, ...props }: HeadingProps) {
|
||||||
|
const ref = useRef<HTMLHeadingElement>(null)
|
||||||
|
const { tag: Tag, className: defaultClassName } = levelStyles[level]
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (window.location.hash === `#${id}`) {
|
||||||
|
ref.current?.scrollIntoView({ behavior: 'smooth' })
|
||||||
|
}
|
||||||
|
}, [id])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
ref={ref}
|
||||||
|
id={id}
|
||||||
|
className={cn('group relative scroll-mt-20', defaultClassName, className)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={`#${id}`}
|
||||||
|
className="absolute -left-5 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 text-gray-400 hover:text-blue-500 transition-opacity no-underline"
|
||||||
|
aria-label={`Link to ${id}`}
|
||||||
|
>
|
||||||
|
#
|
||||||
|
</a>
|
||||||
|
{children}
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -85,7 +85,7 @@ export function MagicCard({
|
|||||||
`,
|
`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="bg-background absolute inset-px rounded-[inherit]" />
|
<div className="absolute inset-px rounded-[inherit] bg-white dark:bg-neutral-900" />
|
||||||
<motion.div
|
<motion.div
|
||||||
className="pointer-events-none absolute inset-px rounded-[inherit] opacity-0 transition-opacity duration-300 group-hover:opacity-100"
|
className="pointer-events-none absolute inset-px rounded-[inherit] opacity-0 transition-opacity duration-300 group-hover:opacity-100"
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import { Router } from 'wouter'
|
import { Router } from 'wouter'
|
||||||
import { useHashLocation } from 'wouter/use-hash-location'
|
|
||||||
|
|
||||||
import { LanguageProvider } from './i18n/context'
|
import { LanguageProvider } from './i18n/context'
|
||||||
import { default as PagesRouter } from './router'
|
import { default as PagesRouter } from './router'
|
||||||
|
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
|
||||||
|
// Redirect legacy hash routes (e.g. /#/docs/foo) to clean paths
|
||||||
|
const { hash } = window.location
|
||||||
|
if (hash.length > 1 && hash.includes('/')) {
|
||||||
|
const path = hash.replace(/^#\/?/, '/')
|
||||||
|
history.replaceState(null, '', '/page-agent' + path)
|
||||||
|
}
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<LanguageProvider>
|
<LanguageProvider>
|
||||||
<Router hook={useHashLocation}>
|
<Router base="/page-agent">
|
||||||
<PagesRouter />
|
<PagesRouter />
|
||||||
</Router>
|
</Router>
|
||||||
</LanguageProvider>
|
</LanguageProvider>
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import Footer from '../components/Footer'
|
|
||||||
import Header from '../components/Header'
|
|
||||||
import FeaturesSection from './home/FeaturesSection'
|
import FeaturesSection from './home/FeaturesSection'
|
||||||
import HeroSection from './home/HeroSection'
|
import HeroSection from './home/HeroSection'
|
||||||
import OneMoreThingSection from './home/OneMoreThingSection'
|
import OneMoreThingSection from './home/OneMoreThingSection'
|
||||||
@@ -7,17 +5,11 @@ import ScenariosSection from './home/ScenariosSection'
|
|||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-linear-to-br from-blue-50 to-purple-50 dark:from-gray-900 dark:to-gray-800">
|
<>
|
||||||
<Header />
|
<HeroSection />
|
||||||
|
<FeaturesSection />
|
||||||
<main id="main-content">
|
<ScenariosSection />
|
||||||
<HeroSection />
|
<OneMoreThingSection />
|
||||||
<FeaturesSection />
|
</>
|
||||||
<ScenariosSection />
|
|
||||||
<OneMoreThingSection />
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { APIDivider, APIReference } from '@/components/ui/api-reference'
|
import { APIDivider, APIReference } from '@/components/ui/api-reference'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ export default function CustomUIDocs() {
|
|||||||
|
|
||||||
{/* Architecture */}
|
{/* Architecture */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '架构' : 'Architecture'}</h2>
|
<Heading id="architecture">{isZh ? '架构' : 'Architecture'}</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? 'PageAgent 由三个独立模块组成,可自由组合:'
|
? 'PageAgent 由三个独立模块组成,可自由组合:'
|
||||||
@@ -45,7 +46,7 @@ export default function CustomUIDocs() {
|
|||||||
|
|
||||||
{/* Two Event Streams */}
|
{/* Two Event Streams */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '两个事件流' : 'Two Event Streams'}</h2>
|
<Heading id="two-event-streams">{isZh ? '两个事件流' : 'Two Event Streams'}</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? 'PageAgentCore 提供两种不同性质的事件流,方便 UI 渲染:'
|
? 'PageAgentCore 提供两种不同性质的事件流,方便 UI 渲染:'
|
||||||
@@ -120,7 +121,7 @@ export default function CustomUIDocs() {
|
|||||||
|
|
||||||
{/* All Events */}
|
{/* All Events */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '所有事件' : 'All Events'}</h2>
|
<Heading id="all-events">{isZh ? '所有事件' : 'All Events'}</Heading>
|
||||||
<APIReference
|
<APIReference
|
||||||
properties={[
|
properties={[
|
||||||
{
|
{
|
||||||
@@ -155,7 +156,7 @@ export default function CustomUIDocs() {
|
|||||||
|
|
||||||
{/* HistoricalEvent Types */}
|
{/* HistoricalEvent Types */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">HistoricalEvent</h2>
|
<Heading id="historicalevent">HistoricalEvent</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh ? 'agent.history 数组中的事件类型:' : 'Event types in agent.history array:'}
|
{isZh ? 'agent.history 数组中的事件类型:' : 'Event types in agent.history array:'}
|
||||||
</p>
|
</p>
|
||||||
@@ -172,7 +173,7 @@ export default function CustomUIDocs() {
|
|||||||
|
|
||||||
{/* AgentActivity Types */}
|
{/* AgentActivity Types */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">AgentActivity</h2>
|
<Heading id="agentactivity">AgentActivity</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh ? 'activity 事件的 detail 类型:' : 'The detail type of activity events:'}
|
{isZh ? 'activity 事件的 detail 类型:' : 'The detail type of activity events:'}
|
||||||
</p>
|
</p>
|
||||||
@@ -191,9 +192,7 @@ export default function CustomUIDocs() {
|
|||||||
|
|
||||||
{/* React Hooks Example */}
|
{/* React Hooks Example */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">
|
<Heading id="using-react-hooks">{isZh ? '使用 React Hooks' : 'Using React Hooks'}</Heading>
|
||||||
{isZh ? '使用 React Hooks' : 'Using React Hooks'}
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh ? '监听事件并更新 React 状态:' : 'Listen to events and update React state:'}
|
{isZh ? '监听事件并更新 React 状态:' : 'Listen to events and update React state:'}
|
||||||
</p>
|
</p>
|
||||||
@@ -229,9 +228,9 @@ export default function CustomUIDocs() {
|
|||||||
|
|
||||||
{/* Assembly Example */}
|
{/* Assembly Example */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">
|
<Heading id="assembling-core-controller-custom-ui">
|
||||||
{isZh ? '组装 Core + Controller + 自定义 UI' : 'Assembling Core + Controller + Custom UI'}
|
{isZh ? '组装 Core + Controller + 自定义 UI' : 'Assembling Core + Controller + Custom UI'}
|
||||||
</h2>
|
</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '参考内置 PageAgent 的实现方式,用自定义 UI 替换 Panel:'
|
? '参考内置 PageAgent 的实现方式,用自定义 UI 替换 Panel:'
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { APIDivider, APIReference, TypeRef } from '@/components/ui/api-reference'
|
import { APIDivider, APIReference, TypeRef } from '@/components/ui/api-reference'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
@@ -17,9 +18,9 @@ export default function PageAgentCoreDocs() {
|
|||||||
|
|
||||||
{/* When to use */}
|
{/* When to use */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">
|
<Heading id="when-to-use-pageagentcore">
|
||||||
{isZh ? '何时使用 PageAgentCore' : 'When to Use PageAgentCore'}
|
{isZh ? '何时使用 PageAgentCore' : 'When to Use PageAgentCore'}
|
||||||
</h2>
|
</Heading>
|
||||||
<ul className="list-disc list-inside text-gray-600 dark:text-gray-400 space-y-2">
|
<ul className="list-disc list-inside text-gray-600 dark:text-gray-400 space-y-2">
|
||||||
<li>{isZh ? '需要自定义 UI 界面' : 'Need a custom UI interface'}</li>
|
<li>{isZh ? '需要自定义 UI 界面' : 'Need a custom UI interface'}</li>
|
||||||
<li>{isZh ? '在自动化测试中无头运行' : 'Running headless in automated tests'}</li>
|
<li>{isZh ? '在自动化测试中无头运行' : 'Running headless in automated tests'}</li>
|
||||||
@@ -38,7 +39,7 @@ export default function PageAgentCoreDocs() {
|
|||||||
|
|
||||||
{/* Basic Usage */}
|
{/* Basic Usage */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '基本用法' : 'Basic Usage'}</h2>
|
<Heading id="basic-usage">{isZh ? '基本用法' : 'Basic Usage'}</Heading>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`import { PageAgentCore } from '@page-agent/core'
|
code={`import { PageAgentCore } from '@page-agent/core'
|
||||||
@@ -76,7 +77,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* LLM Configuration */}
|
{/* LLM Configuration */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">LLMConfig</h2>
|
<Heading id="llmconfig">LLMConfig</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '配置与大语言模型的连接参数。支持 OpenAI 兼容的 API。'
|
? '配置与大语言模型的连接参数。支持 OpenAI 兼容的 API。'
|
||||||
@@ -133,7 +134,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* Agent Configuration */}
|
{/* Agent Configuration */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">AgentConfig</h2>
|
<Heading id="agentconfig">AgentConfig</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '配置 Agent 的行为、生命周期钩子和扩展能力。'
|
? '配置 Agent 的行为、生命周期钩子和扩展能力。'
|
||||||
@@ -210,7 +211,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* Lifecycle Hooks */}
|
{/* Lifecycle Hooks */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '生命周期钩子' : 'Lifecycle Hooks'}</h2>
|
<Heading id="lifecycle-hooks">{isZh ? '生命周期钩子' : 'Lifecycle Hooks'}</Heading>
|
||||||
<div className="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg p-4 mb-4">
|
<div className="bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg p-4 mb-4">
|
||||||
<p className="text-amber-800 dark:text-amber-200 text-sm">
|
<p className="text-amber-800 dark:text-amber-200 text-sm">
|
||||||
<strong>⚠️ {isZh ? '警告' : 'Warning'}:</strong>{' '}
|
<strong>⚠️ {isZh ? '警告' : 'Warning'}:</strong>{' '}
|
||||||
@@ -262,7 +263,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* PageController Configuration */}
|
{/* PageController Configuration */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">PageControllerConfig</h2>
|
<Heading id="pagecontrollerconfig">PageControllerConfig</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '配置 DOM 提取、元素交互和视觉反馈。'
|
? '配置 DOM 提取、元素交互和视觉反馈。'
|
||||||
@@ -321,7 +322,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* Properties */}
|
{/* Properties */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '属性' : 'Properties'}</h2>
|
<Heading id="properties">{isZh ? '属性' : 'Properties'}</Heading>
|
||||||
<APIReference
|
<APIReference
|
||||||
properties={[
|
properties={[
|
||||||
{
|
{
|
||||||
@@ -366,7 +367,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* Methods */}
|
{/* Methods */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '方法' : 'Methods'}</h2>
|
<Heading id="methods">{isZh ? '方法' : 'Methods'}</Heading>
|
||||||
<APIReference
|
<APIReference
|
||||||
properties={[
|
properties={[
|
||||||
{
|
{
|
||||||
@@ -389,7 +390,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* Events */}
|
{/* Events */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '事件' : 'Events'}</h2>
|
<Heading id="events">{isZh ? '事件' : 'Events'}</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh ? (
|
{isZh ? (
|
||||||
<>
|
<>
|
||||||
@@ -438,7 +439,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* ExecutionResult */}
|
{/* ExecutionResult */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">ExecutionResult</h2>
|
<Heading id="executionresult">ExecutionResult</Heading>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`interface ExecutionResult {
|
code={`interface ExecutionResult {
|
||||||
@@ -454,7 +455,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* AgentActivity */}
|
{/* AgentActivity */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">AgentActivity</h2>
|
<Heading id="agentactivity">AgentActivity</Heading>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`type AgentActivity =
|
code={`type AgentActivity =
|
||||||
@@ -470,7 +471,7 @@ const result = await agent.execute('Fill in the form with test data')`}
|
|||||||
|
|
||||||
{/* Headless Usage */}
|
{/* Headless Usage */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '无头模式' : 'Headless Mode'}</h2>
|
<Heading id="headless-mode">{isZh ? '无头模式' : 'Headless Mode'}</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '在非 DOM 环境中,你必须实现自定义的 PageController(例如远程操作页面或 Puppeteer)。'
|
? '在非 DOM 环境中,你必须实现自定义的 PageController(例如远程操作页面或 Puppeteer)。'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Link } from 'wouter'
|
import { Link } from 'wouter'
|
||||||
|
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { APIReference, TypeRef } from '@/components/ui/api-reference'
|
import { APIReference, TypeRef } from '@/components/ui/api-reference'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
@@ -19,9 +20,9 @@ export default function PageAgentDocs() {
|
|||||||
|
|
||||||
{/* When to use */}
|
{/* When to use */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">
|
<Heading id="when-to-use-pageagent">
|
||||||
{isZh ? '何时使用 PageAgent' : 'When to Use PageAgent'}
|
{isZh ? '何时使用 PageAgent' : 'When to Use PageAgent'}
|
||||||
</h2>
|
</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '在大多数场景下,你应该使用 PageAgent。它提供了开箱即用的完整体验:'
|
? '在大多数场景下,你应该使用 PageAgent。它提供了开箱即用的完整体验:'
|
||||||
@@ -43,7 +44,7 @@ export default function PageAgentDocs() {
|
|||||||
|
|
||||||
{/* Basic Usage */}
|
{/* Basic Usage */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '基本用法' : 'Basic Usage'}</h2>
|
<Heading id="basic-usage">{isZh ? '基本用法' : 'Basic Usage'}</Heading>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`import { PageAgent } from 'page-agent'
|
code={`import { PageAgent } from 'page-agent'
|
||||||
@@ -69,7 +70,7 @@ console.log(result.history) // Full execution history`}
|
|||||||
|
|
||||||
{/* Class Definition */}
|
{/* Class Definition */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '类定义' : 'Class Definition'}</h2>
|
<Heading id="class-definition">{isZh ? '类定义' : 'Class Definition'}</Heading>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
language="typescript"
|
language="typescript"
|
||||||
code={`class PageAgent extends PageAgentCore {
|
code={`class PageAgent extends PageAgentCore {
|
||||||
@@ -109,7 +110,7 @@ console.log(result.history) // Full execution history`}
|
|||||||
|
|
||||||
{/* Configuration */}
|
{/* Configuration */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '配置' : 'Configuration'}</h2>
|
<Heading id="configuration">{isZh ? '配置' : 'Configuration'}</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? 'PageAgent 使用与 PageAgentCore 相同的配置接口。'
|
? 'PageAgent 使用与 PageAgentCore 相同的配置接口。'
|
||||||
@@ -144,7 +145,7 @@ console.log(result.history) // Full execution history`}
|
|||||||
|
|
||||||
{/* Panel Property */}
|
{/* Panel Property */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? 'Panel 属性' : 'Panel Property'}</h2>
|
<Heading id="panel-property">{isZh ? 'Panel 属性' : 'Panel Property'}</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? 'PageAgent 自动创建一个 Panel 实例。你可以通过 panel 属性访问它来控制 UI:'
|
? 'PageAgent 自动创建一个 Panel 实例。你可以通过 panel 属性访问它来控制 UI:'
|
||||||
@@ -185,9 +186,9 @@ agent.panel.dispose()`}
|
|||||||
|
|
||||||
{/* Comparison with PageAgentCore */}
|
{/* Comparison with PageAgentCore */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">
|
<Heading id="pageagent-vs-pageagentcore">
|
||||||
{isZh ? 'PageAgent vs PageAgentCore' : 'PageAgent vs PageAgentCore'}
|
{isZh ? 'PageAgent vs PageAgentCore' : 'PageAgent vs PageAgentCore'}
|
||||||
</h2>
|
</Heading>
|
||||||
<div className="overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700">
|
<div className="overflow-hidden rounded-lg border border-gray-200 dark:border-gray-700">
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { siChromewebstore, siGithub } from 'simple-icons'
|
import { siChromewebstore, siGithub } from 'simple-icons'
|
||||||
|
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function ChromeExtension() {
|
export default function ChromeExtension() {
|
||||||
@@ -22,7 +23,9 @@ export default function ChromeExtension() {
|
|||||||
<div className="space-y-8 mt-8">
|
<div className="space-y-8 mt-8">
|
||||||
{/* Features */}
|
{/* Features */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '核心特性' : 'Key Features'}</h2>
|
<Heading id="key-features" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? '核心特性' : 'Key Features'}
|
||||||
|
</Heading>
|
||||||
<div className="grid md:grid-cols-3 gap-4">
|
<div className="grid md:grid-cols-3 gap-4">
|
||||||
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
<div className="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||||
<h3 className="font-semibold mb-2">🔓 {isZh ? '多页任务' : 'Multi-Page Tasks'}</h3>
|
<h3 className="font-semibold mb-2">🔓 {isZh ? '多页任务' : 'Multi-Page Tasks'}</h3>
|
||||||
@@ -57,7 +60,9 @@ export default function ChromeExtension() {
|
|||||||
|
|
||||||
{/* Install */}
|
{/* Install */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '获取扩展' : 'Get the Extension'}</h2>
|
<Heading id="get-the-extension" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? '获取扩展' : 'Get the Extension'}
|
||||||
|
</Heading>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
<a
|
<a
|
||||||
href={chromeWebStoreUrl}
|
href={chromeWebStoreUrl}
|
||||||
@@ -86,9 +91,9 @@ export default function ChromeExtension() {
|
|||||||
|
|
||||||
{/* Relationship with PageAgent.js */}
|
{/* Relationship with PageAgent.js */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">
|
<Heading id="how-it-relates-to-page-agent-js" className="text-2xl font-bold mb-4">
|
||||||
{isZh ? '与 PageAgent.js 的关系' : 'How It Relates to PageAgent.js'}
|
{isZh ? '与 PageAgent.js 的关系' : 'How It Relates to PageAgent.js'}
|
||||||
</h2>
|
</Heading>
|
||||||
<div className="p-5 bg-gray-50 dark:bg-gray-800 rounded-lg space-y-3 text-gray-600 dark:text-gray-300">
|
<div className="p-5 bg-gray-50 dark:bg-gray-800 rounded-lg space-y-3 text-gray-600 dark:text-gray-300">
|
||||||
<p>
|
<p>
|
||||||
{isZh
|
{isZh
|
||||||
@@ -105,9 +110,9 @@ export default function ChromeExtension() {
|
|||||||
|
|
||||||
{/* Third-party Integration */}
|
{/* Third-party Integration */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">
|
<Heading id="third-party-integration" className="text-2xl font-bold mb-4">
|
||||||
{isZh ? '第三方接入' : 'Third-Party Integration'}
|
{isZh ? '第三方接入' : 'Third-Party Integration'}
|
||||||
</h2>
|
</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '通过页面 JavaScript 调用 `window.PAGE_AGENT_EXT`,你的应用可以发起跨页面任务并控制浏览器行为。'
|
? '通过页面 JavaScript 调用 `window.PAGE_AGENT_EXT`,你的应用可以发起跨页面任务并控制浏览器行为。'
|
||||||
@@ -145,7 +150,9 @@ localStorage.setItem('PageAgentExtUserAuthToken', '<your-token-from-extension>')
|
|||||||
|
|
||||||
{/* API Reference */}
|
{/* API Reference */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? 'API 参考' : 'API Reference'}</h2>
|
<Heading id="api-reference" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? 'API 参考' : 'API Reference'}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
{/* AI Assistant Instructions */}
|
{/* AI Assistant Instructions */}
|
||||||
<section className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
|
<section className="p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
|
||||||
@@ -170,9 +177,9 @@ localStorage.setItem('PageAgentExtUserAuthToken', '<your-token-from-extension>')
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* TypeScript Declaration */}
|
{/* TypeScript Declaration */}
|
||||||
<h2 className="text-2xl font-bold mb-4">
|
<Heading id="typescript-declaration" className="text-2xl font-bold mb-4">
|
||||||
{isZh ? 'TypeScript 类型声明' : 'TypeScript Declaration'}
|
{isZh ? 'TypeScript 类型声明' : 'TypeScript Declaration'}
|
||||||
</h2>
|
</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '推荐把 `execute` 的类型声明加入你的项目,获得完整类型提示。'
|
? '推荐把 `execute` 的类型声明加入你的项目,获得完整类型提示。'
|
||||||
@@ -304,11 +311,14 @@ window.PAGE_AGENT_EXT.stop()`
|
|||||||
|
|
||||||
{/* Integration Guide */}
|
{/* Integration Guide */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">
|
<Heading
|
||||||
|
id="integrate-multipageagent-into-your-extension"
|
||||||
|
className="text-2xl font-bold mb-4"
|
||||||
|
>
|
||||||
{isZh
|
{isZh
|
||||||
? '将 MultiPageAgent 集成你自己的插件'
|
? '将 MultiPageAgent 集成你自己的插件'
|
||||||
: 'Integrate MultiPageAgent into Your Extension'}
|
: 'Integrate MultiPageAgent into Your Extension'}
|
||||||
</h2>
|
</Heading>
|
||||||
<p>@TODO</p>
|
<p>@TODO</p>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function Instructions() {
|
export default function Instructions() {
|
||||||
@@ -16,9 +17,9 @@ export default function Instructions() {
|
|||||||
|
|
||||||
{/* System Instructions */}
|
{/* System Instructions */}
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-3xl font-bold mb-6">
|
<Heading id="system-instructions" className="text-3xl font-bold mb-6">
|
||||||
{isZh ? '系统级指导 (System Instructions)' : 'System Instructions'}
|
{isZh ? '系统级指导 (System Instructions)' : 'System Instructions'}
|
||||||
</h2>
|
</Heading>
|
||||||
|
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||||
{isZh
|
{isZh
|
||||||
@@ -46,9 +47,9 @@ Guidelines:
|
|||||||
|
|
||||||
{/* Page Instructions */}
|
{/* Page Instructions */}
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-3xl font-bold mb-6">
|
<Heading id="page-instructions" className="text-3xl font-bold mb-6">
|
||||||
{isZh ? '页面级指导 (Page Instructions)' : 'Page Instructions'}
|
{isZh ? '页面级指导 (Page Instructions)' : 'Page Instructions'}
|
||||||
</h2>
|
</Heading>
|
||||||
|
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||||
{isZh
|
{isZh
|
||||||
@@ -90,7 +91,9 @@ This is the product listing page.
|
|||||||
|
|
||||||
{/* How It Works */}
|
{/* How It Works */}
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-3xl font-bold mb-6">{isZh ? '工作原理' : 'How It Works'}</h2>
|
<Heading id="how-it-works" className="text-3xl font-bold mb-6">
|
||||||
|
{isZh ? '工作原理' : 'How It Works'}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import BetaNotice from '@/components/BetaNotice'
|
import BetaNotice from '@/components/BetaNotice'
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function CustomTools() {
|
export default function CustomTools() {
|
||||||
@@ -17,7 +18,9 @@ export default function CustomTools() {
|
|||||||
|
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '工具注册' : 'Tool Registration'}</h2>
|
<Heading id="tool-registration" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? '工具注册' : 'Tool Registration'}
|
||||||
|
</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '每个自定义工具需要定义四个核心属性:name、description、input schema 和 execute 函数。'
|
? '每个自定义工具需要定义四个核心属性:name、description、input schema 和 execute 函数。'
|
||||||
@@ -55,7 +58,9 @@ const pageAgent = new PageAgent({customTools})
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '页面过滤器' : 'Page Filter'}</h2>
|
<Heading id="page-filter" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? '页面过滤器' : 'Page Filter'}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
<BetaNotice />
|
<BetaNotice />
|
||||||
|
|
||||||
@@ -89,7 +94,9 @@ const pageAgent = new PageAgent({customTools})
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '最佳实践' : 'Best Practices'}</h2>
|
<Heading id="best-practices" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? '最佳实践' : 'Best Practices'}
|
||||||
|
</Heading>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
||||||
<h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-300 mb-2">
|
<h3 className="text-lg font-semibold text-yellow-900 dark:text-yellow-300 mb-2">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function DataMasking() {
|
export default function DataMasking() {
|
||||||
@@ -15,7 +16,9 @@ export default function DataMasking() {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-3xl font-bold mb-6">{isZh ? '接口定义' : 'API Definition'}</h2>
|
<Heading id="api-definition" className="text-3xl font-bold mb-6">
|
||||||
|
{isZh ? '接口定义' : 'API Definition'}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
className="mb-6"
|
className="mb-6"
|
||||||
@@ -30,9 +33,9 @@ export default function DataMasking() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="mb-12">
|
<section className="mb-12">
|
||||||
<h2 className="text-3xl font-bold mb-6">
|
<Heading id="common-masking-patterns" className="text-3xl font-bold mb-6">
|
||||||
{isZh ? '常用脱敏规则' : 'Common Masking Patterns'}
|
{isZh ? '常用脱敏规则' : 'Common Masking Patterns'}
|
||||||
</h2>
|
</Heading>
|
||||||
|
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
<p className="text-gray-600 dark:text-gray-300 mb-6">
|
||||||
{isZh
|
{isZh
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Fragment } from 'react'
|
import { Fragment } from 'react'
|
||||||
|
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
const BASELINE = new Set([
|
const BASELINE = new Set([
|
||||||
@@ -63,7 +64,9 @@ export default function Models() {
|
|||||||
|
|
||||||
{/* Models Section */}
|
{/* Models Section */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-3">{isZh ? '已测试模型' : 'Tested Models'}</h2>
|
<Heading id="tested-models" className="text-2xl font-semibold mb-3">
|
||||||
|
{isZh ? '已测试模型' : 'Tested Models'}
|
||||||
|
</Heading>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '推荐使用 ToolCall 能力强的轻量级模型。'
|
? '推荐使用 ToolCall 能力强的轻量级模型。'
|
||||||
@@ -141,7 +144,7 @@ export default function Models() {
|
|||||||
|
|
||||||
{/* Configuration Section */}
|
{/* Configuration Section */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">{isZh ? '配置方式' : 'Configuration'}</h2>
|
<Heading id="configuration">{isZh ? '配置方式' : 'Configuration'}</Heading>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
code={`// OpenAI-compatible services (e.g., Alibaba Bailian)
|
code={`// OpenAI-compatible services (e.g., Alibaba Bailian)
|
||||||
const pageAgent = new PageAgent({
|
const pageAgent = new PageAgent({
|
||||||
@@ -163,9 +166,7 @@ const pageAgent = new PageAgent({
|
|||||||
|
|
||||||
{/* Free Testing API Section */}
|
{/* Free Testing API Section */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">
|
<Heading id="free-testing-api">{isZh ? '免费测试接口' : 'Free Testing API'}</Heading>
|
||||||
{isZh ? '免费测试接口' : 'Free Testing API'}
|
|
||||||
</h2>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '以下免费测试接口仅供 PageAgent.js 和 PageAgent Extension 的技术评估使用。有速率限制,可能随时变更。请勿用于生产环境。'
|
? '以下免费测试接口仅供 PageAgent.js 和 PageAgent Extension 的技术评估使用。有速率限制,可能随时变更。请勿用于生产环境。'
|
||||||
@@ -215,7 +216,7 @@ LLM_API_KEY="NA"`}
|
|||||||
|
|
||||||
{/* Ollama Section */}
|
{/* Ollama Section */}
|
||||||
<section className="mb-10">
|
<section className="mb-10">
|
||||||
<h2 className="text-2xl font-semibold mb-4">Ollama</h2>
|
<Heading id="ollama">Ollama</Heading>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? '已在 Ollama 0.15 + qwen3:14b (RTX3090 24GB) 上测试通过。'
|
? '已在 Ollama 0.15 + qwen3:14b (RTX3090 24GB) 上测试通过。'
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { Suspense } from 'react'
|
import { Suspense } from 'react'
|
||||||
import { Route, Switch } from 'wouter'
|
import { Route, Switch } from 'wouter'
|
||||||
|
|
||||||
import Footer from '../../components/Footer'
|
|
||||||
import Header from '../../components/Header'
|
|
||||||
import DocsLayout from './Layout'
|
import DocsLayout from './Layout'
|
||||||
import CustomUIDocs from './advanced/custom-ui/page'
|
import CustomUIDocs from './advanced/custom-ui/page'
|
||||||
import PageAgentCoreDocs from './advanced/page-agent-core/page'
|
import PageAgentCoreDocs from './advanced/page-agent-core/page'
|
||||||
@@ -25,15 +23,9 @@ import Troubleshooting from './introduction/troubleshooting/page'
|
|||||||
|
|
||||||
function DocsPage({ children }: { children: React.ReactNode }) {
|
function DocsPage({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900 flex flex-col">
|
<DocsLayout>
|
||||||
<Header />
|
<Suspense>{children}</Suspense>
|
||||||
<div className="flex-1">
|
</DocsLayout>
|
||||||
<DocsLayout>
|
|
||||||
<Suspense>{children}</Suspense>
|
|
||||||
</DocsLayout>
|
|
||||||
</div>
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import BetaNotice from '@/components/BetaNotice'
|
import BetaNotice from '@/components/BetaNotice'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function SecurityPermissions() {
|
export default function SecurityPermissions() {
|
||||||
@@ -18,9 +19,9 @@ export default function SecurityPermissions() {
|
|||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-3">
|
<Heading id="element-interaction-allowlist-blocklist" className="text-2xl font-bold mb-3">
|
||||||
{isZh ? '元素操作黑白名单' : 'Element Interaction Allowlist/Blocklist'}
|
{isZh ? '元素操作黑白名单' : 'Element Interaction Allowlist/Blocklist'}
|
||||||
</h2>
|
</Heading>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg">
|
<div className="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg">
|
||||||
<h3 className="text-lg font-semibold text-red-900 dark:text-red-300">
|
<h3 className="text-lg font-semibold text-red-900 dark:text-red-300">
|
||||||
@@ -46,9 +47,9 @@ export default function SecurityPermissions() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-3">
|
<Heading id="instruction-safety-constraints" className="text-2xl font-bold mb-3">
|
||||||
{isZh ? 'Instruction 安全约束' : 'Instruction Safety Constraints'}
|
{isZh ? 'Instruction 安全约束' : 'Instruction Safety Constraints'}
|
||||||
</h2>
|
</Heading>
|
||||||
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg">
|
||||||
<h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300">
|
<h3 className="text-lg font-semibold mb-2 text-yellow-900 dark:text-yellow-300">
|
||||||
⚠️ {isZh ? '高危操作控制' : 'High-Risk Operation Control'}
|
⚠️ {isZh ? '高危操作控制' : 'High-Risk Operation Control'}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function ThirdPartyAgentPage() {
|
export default function ThirdPartyAgentPage() {
|
||||||
@@ -26,7 +27,9 @@ export default function ThirdPartyAgentPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '集成方式' : 'Integration Method'}</h2>
|
<Heading id="integration-method" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? '集成方式' : 'Integration Method'}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
<div className="space-y-4 mb-6">
|
<div className="space-y-4 mb-6">
|
||||||
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
|
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
|
||||||
@@ -57,7 +60,9 @@ const pageAgentTool = {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '应用场景' : 'Use Cases'}</h2>
|
<Heading id="use-cases" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? '应用场景' : 'Use Cases'}
|
||||||
|
</Heading>
|
||||||
<div className="grid md:grid-cols-2 gap-4 mb-6">
|
<div className="grid md:grid-cols-2 gap-4 mb-6">
|
||||||
<div className="bg-linear-to-br from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg">
|
<div className="bg-linear-to-br from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-700 p-4 rounded-lg">
|
||||||
<h4 className="font-semibold mb-2 text-gray-900 dark:text-white">
|
<h4 className="font-semibold mb-2 text-gray-900 dark:text-white">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Link } from 'wouter'
|
import { Link } from 'wouter'
|
||||||
|
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function LimitationsPage() {
|
export default function LimitationsPage() {
|
||||||
@@ -20,9 +21,9 @@ export default function LimitationsPage() {
|
|||||||
|
|
||||||
<div className="prose prose-lg dark:prose-invert max-w-none">
|
<div className="prose prose-lg dark:prose-invert max-w-none">
|
||||||
{/* PageAgent.js vs PageAgentExt */}
|
{/* PageAgent.js vs PageAgentExt */}
|
||||||
<h2 className="text-2xl font-bold mb-3">
|
<Heading id="pageagent-js-vs-pageagentext" className="text-2xl font-bold mb-3">
|
||||||
{isZh ? 'PageAgent.js vs PageAgentExt' : 'PageAgent.js vs PageAgentExt'}
|
{isZh ? 'PageAgent.js vs PageAgentExt' : 'PageAgent.js vs PageAgentExt'}
|
||||||
</h2>
|
</Heading>
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||||
{isZh
|
{isZh
|
||||||
? 'PageAgent.js 是核心库,运行在页面内。PageAgentExt 是可选的浏览器扩展,提供额外的浏览器级控制能力。'
|
? 'PageAgent.js 是核心库,运行在页面内。PageAgentExt 是可选的浏览器扩展,提供额外的浏览器级控制能力。'
|
||||||
@@ -37,7 +38,7 @@ export default function LimitationsPage() {
|
|||||||
<th className="text-left py-3 pl-4 font-semibold">
|
<th className="text-left py-3 pl-4 font-semibold">
|
||||||
PageAgentExt{' '}
|
PageAgentExt{' '}
|
||||||
<Link
|
<Link
|
||||||
href="/docs/features/chrome-extension"
|
href="/features/chrome-extension"
|
||||||
className="text-xs font-normal text-blue-600 dark:text-blue-400 hover:underline"
|
className="text-xs font-normal text-blue-600 dark:text-blue-400 hover:underline"
|
||||||
>
|
>
|
||||||
{isZh ? '了解更多' : 'learn more'}
|
{isZh ? '了解更多' : 'learn more'}
|
||||||
@@ -82,9 +83,9 @@ export default function LimitationsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Interaction Limitations */}
|
{/* Interaction Limitations */}
|
||||||
<h2 className="text-2xl font-bold mb-3">
|
<Heading id="interaction-capabilities" className="text-2xl font-bold mb-3">
|
||||||
{isZh ? '交互能力' : 'Interaction Capabilities'}
|
{isZh ? '交互能力' : 'Interaction Capabilities'}
|
||||||
</h2>
|
</Heading>
|
||||||
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6">
|
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6 mb-6">
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
<div>
|
<div>
|
||||||
@@ -127,7 +128,9 @@ export default function LimitationsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Understanding Limitations */}
|
{/* Understanding Limitations */}
|
||||||
<h2 className="text-2xl font-bold mb-3">{isZh ? '理解能力' : 'Understanding'}</h2>
|
<Heading id="understanding" className="text-2xl font-bold mb-3">
|
||||||
|
{isZh ? '理解能力' : 'Understanding'}
|
||||||
|
</Heading>
|
||||||
<div className="bg-amber-50 dark:bg-amber-900/20 border-l-4 border-amber-400 p-4 mb-6">
|
<div className="bg-amber-50 dark:bg-amber-900/20 border-l-4 border-amber-400 p-4 mb-6">
|
||||||
<p className="text-amber-800 dark:text-amber-200 mb-2 font-medium">
|
<p className="text-amber-800 dark:text-amber-200 mb-2 font-medium">
|
||||||
{isZh
|
{isZh
|
||||||
@@ -142,9 +145,9 @@ export default function LimitationsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Website Quality */}
|
{/* Website Quality */}
|
||||||
<h2 className="text-2xl font-bold mb-3">
|
<Heading id="page-quality-matters" className="text-2xl font-bold mb-3">
|
||||||
{isZh ? '网页质量影响' : 'Page Quality Matters'}
|
{isZh ? '网页质量影响' : 'Page Quality Matters'}
|
||||||
</h2>
|
</Heading>
|
||||||
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6">
|
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-6">
|
||||||
<p className="text-gray-600 dark:text-gray-300 text-sm">
|
<p className="text-gray-600 dark:text-gray-300 text-sm">
|
||||||
{isZh
|
{isZh
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
export default function Overview() {
|
export default function Overview() {
|
||||||
@@ -48,9 +49,9 @@ export default function Overview() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-4">
|
<Heading id="what-is-page-agent" className="text-2xl font-bold mb-4">
|
||||||
{isZh ? '什么是 page-agent?' : 'What is page-agent?'}
|
{isZh ? '什么是 page-agent?' : 'What is page-agent?'}
|
||||||
</h2>
|
</Heading>
|
||||||
|
|
||||||
<p className="text-gray-600 dark:text-gray-300 mb-8 leading-relaxed ">
|
<p className="text-gray-600 dark:text-gray-300 mb-8 leading-relaxed ">
|
||||||
{isZh
|
{isZh
|
||||||
@@ -60,7 +61,9 @@ export default function Overview() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-2xl font-bold mb-3">{isZh ? '核心特性' : 'Core Features'}</h2>
|
<Heading id="core-features" className="text-2xl font-bold mb-3">
|
||||||
|
{isZh ? '核心特性' : 'Core Features'}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-4 mb-8" role="list">
|
<div className="grid md:grid-cols-2 gap-4 mb-8" role="list">
|
||||||
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
|
||||||
@@ -108,9 +111,9 @@ export default function Overview() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="text-2xl font-bold mb-4">
|
<Heading id="vs-browser-use" className="text-2xl font-bold mb-4">
|
||||||
{isZh ? '与 browser-use 的区别' : 'vs. browser-use'}
|
{isZh ? '与 browser-use 的区别' : 'vs. browser-use'}
|
||||||
</h2>
|
</Heading>
|
||||||
|
|
||||||
<div className="overflow-x-auto mb-8">
|
<div className="overflow-x-auto mb-8">
|
||||||
<table className="w-full border-collapse border border-gray-300 dark:border-gray-600">
|
<table className="w-full border-collapse border border-gray-300 dark:border-gray-600">
|
||||||
@@ -174,7 +177,9 @@ export default function Overview() {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="text-2xl font-bold mb-4">{isZh ? '应用场景' : 'Use Cases'}</h2>
|
<Heading id="use-cases" className="text-2xl font-bold mb-4">
|
||||||
|
{isZh ? '应用场景' : 'Use Cases'}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
<ul className="space-y-4 mb-8">
|
<ul className="space-y-4 mb-8">
|
||||||
<li className="flex items-start space-x-3">
|
<li className="flex items-start space-x-3">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { CDN_DEMO_CN_URL, CDN_DEMO_URL } from '@/constants'
|
import { CDN_DEMO_CN_URL, CDN_DEMO_URL } from '@/constants'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
@@ -13,7 +14,9 @@ export default function QuickStart() {
|
|||||||
{isZh ? '几分钟内完成 page-agent 的集成。' : 'Integrate page-agent in minutes.'}
|
{isZh ? '几分钟内完成 page-agent 的集成。' : 'Integrate page-agent in minutes.'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 className="text-2xl font-bold mb-3">{isZh ? '安装步骤' : 'Installation Steps'}</h2>
|
<Heading id="installation-steps" className="text-2xl font-bold mb-3">
|
||||||
|
{isZh ? '安装步骤' : 'Installation Steps'}
|
||||||
|
</Heading>
|
||||||
|
|
||||||
<div className="space-y-4 mb-6">
|
<div className="space-y-4 mb-6">
|
||||||
{/* Demo CDN - One Line */}
|
{/* Demo CDN - One Line */}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { Link } from 'wouter'
|
import { Link } from 'wouter'
|
||||||
|
|
||||||
import CodeEditor from '@/components/CodeEditor'
|
import CodeEditor from '@/components/CodeEditor'
|
||||||
|
import { Heading } from '@/components/Heading'
|
||||||
import { useLanguage } from '@/i18n/context'
|
import { useLanguage } from '@/i18n/context'
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -369,10 +370,13 @@ export default function TroubleshootingPage() {
|
|||||||
{/* Main content */}
|
{/* Main content */}
|
||||||
<div className="flex-1 min-w-0 space-y-12">
|
<div className="flex-1 min-w-0 space-y-12">
|
||||||
{SECTIONS.map((section) => (
|
{SECTIONS.map((section) => (
|
||||||
<section key={section.id} id={section.id} className="scroll-mt-24">
|
<section key={section.id} className="scroll-mt-24">
|
||||||
<h2 className="text-2xl font-bold mb-4 text-gray-900 dark:text-white">
|
<Heading
|
||||||
|
id={section.id}
|
||||||
|
className="text-2xl font-bold mb-4 text-gray-900 dark:text-white"
|
||||||
|
>
|
||||||
{isZh ? section.title.zh : section.title.en}
|
{isZh ? section.title.zh : section.title.en}
|
||||||
</h2>
|
</Heading>
|
||||||
|
|
||||||
{/* Symptom callout */}
|
{/* Symptom callout */}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,163 +1,229 @@
|
|||||||
import { Bot, Box, MessageSquare, Shield, Sparkles, Users } from 'lucide-react'
|
import { Bot, Box, MessageSquare, Shield, Sparkles, Users } from 'lucide-react'
|
||||||
|
|
||||||
import { BlurFade } from '../../components/ui/blur-fade'
|
import { BlurFade } from '../../components/ui/blur-fade'
|
||||||
import { Marquee } from '../../components/ui/marquee'
|
import { Highlighter } from '../../components/ui/highlighter'
|
||||||
|
import { MagicCard } from '../../components/ui/magic-card'
|
||||||
import { Particles } from '../../components/ui/particles'
|
import { Particles } from '../../components/ui/particles'
|
||||||
import { useLanguage } from '../../i18n/context'
|
import { useLanguage } from '../../i18n/context'
|
||||||
|
|
||||||
|
// Word-cloud style: each item has a position (%), size, opacity, and color for a scattered look
|
||||||
|
const LLM_CLOUD: {
|
||||||
|
name: string
|
||||||
|
color: string
|
||||||
|
x: number
|
||||||
|
y: number
|
||||||
|
size: number
|
||||||
|
opacity: number
|
||||||
|
}[] = [
|
||||||
|
{ name: 'OpenAI', color: '#10b981', x: 18, y: 22, size: 1.5, opacity: 1 },
|
||||||
|
{ name: 'Claude', color: '#f97316', x: 62, y: 15, size: 1.35, opacity: 0.95 },
|
||||||
|
{ name: 'Qwen', color: '#8b5cf6', x: 38, y: 50, size: 1.8, opacity: 0.9 },
|
||||||
|
{ name: 'Gemini', color: '#3b82f6', x: 68, y: 48, size: 1.2, opacity: 0.85 },
|
||||||
|
{ name: 'DeepSeek', color: '#06b6d4', x: 10, y: 65, size: 1.1, opacity: 0.8 },
|
||||||
|
{ name: 'Grok', color: '#f43f5e', x: 52, y: 78, size: 1.0, opacity: 0.75 },
|
||||||
|
{ name: 'Ollama', color: '#9ca3af', x: 82, y: 25, size: 1.1, opacity: 0.8 },
|
||||||
|
{ name: 'Kimi', color: '#14b8a6', x: 30, y: 82, size: 0.85, opacity: 0.6 },
|
||||||
|
{ name: 'GLM', color: '#f59e0b', x: 70, y: 72, size: 0.85, opacity: 0.55 },
|
||||||
|
{ name: 'LLaMA', color: '#60a5fa', x: 88, y: 70, size: 0.8, opacity: 0.45 },
|
||||||
|
]
|
||||||
|
|
||||||
|
const CARD_HEIGHT = 'h-72'
|
||||||
|
|
||||||
export default function FeaturesSection() {
|
export default function FeaturesSection() {
|
||||||
const { isZh } = useLanguage()
|
const { isZh } = useLanguage()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="px-6 py-14" aria-labelledby="features-heading">
|
<section className="px-6 py-14" aria-labelledby="features-heading">
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 auto-rows-[22rem]">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 auto-rows-[18rem]">
|
||||||
{/* Zero Infrastructure (2-col) */}
|
{/* Row 1: Zero Infrastructure (2col) + Privacy (1col) */}
|
||||||
<BlurFade inView className="col-span-1 md:col-span-2">
|
<BlurFade inView className="col-span-1 md:col-span-2">
|
||||||
<div className="group relative h-full overflow-hidden rounded-2xl bg-white/80 dark:bg-gray-900/80 backdrop-blur-xl border border-gray-200/60 dark:border-white/8 [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] hover:shadow-xl transition-all duration-500 flex flex-col">
|
<MagicCard
|
||||||
<div className="flex-1 p-8 flex flex-col justify-center">
|
className="h-full rounded-2xl"
|
||||||
<div className="space-y-3 mb-6">
|
gradientFrom="#3b82f6"
|
||||||
{[
|
gradientTo="#06b6d4"
|
||||||
'pip install browser-use playwright',
|
gradientColor="#3b82f6"
|
||||||
'docker run -p 3000:3000 playwright-mcp',
|
gradientOpacity={0.15}
|
||||||
'const browser = await chromium.launch()',
|
>
|
||||||
].map((cmd) => (
|
<div className={`flex ${CARD_HEIGHT} flex-col`}>
|
||||||
<div
|
<div className="flex-1 p-7 flex flex-col justify-center">
|
||||||
key={cmd}
|
<div className="space-y-2.5 mb-5">
|
||||||
className="flex items-center gap-2.5 font-mono text-sm text-gray-400 dark:text-gray-600 line-through decoration-red-400/40 truncate"
|
{[
|
||||||
>
|
'pip install browser-use playwright',
|
||||||
<span className="text-red-400/60 text-xs shrink-0">✗</span>
|
'docker run -p 3000:3000 playwright-mcp',
|
||||||
{cmd}
|
'const browser = await chromium.launch()',
|
||||||
|
].map((cmd) => (
|
||||||
|
<div
|
||||||
|
key={cmd}
|
||||||
|
className="font-mono text-sm text-white-400 dark:text-gray-300 truncate"
|
||||||
|
>
|
||||||
|
<Highlighter
|
||||||
|
action="strike-through"
|
||||||
|
color="#ef4444aa"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
// multiline={false}
|
||||||
|
isView
|
||||||
|
>
|
||||||
|
{cmd}
|
||||||
|
</Highlighter>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="bg-emerald-50 dark:bg-emerald-950/30 border border-emerald-200/60 dark:border-emerald-700/40 rounded-xl px-5 py-3 font-mono text-sm text-emerald-700 dark:text-emerald-400 flex items-center gap-2.5">
|
||||||
|
<span className="text-emerald-500 text-xs shrink-0">✓</span>
|
||||||
|
{'<script src="page-agent.js"></script>'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="px-7 pb-5">
|
||||||
|
<div className="flex items-center gap-2.5 mb-1">
|
||||||
|
<Box className="w-5 h-5 text-blue-500" />
|
||||||
|
<h3 className="font-semibold text-lg text-gray-900 dark:text-white">
|
||||||
|
{isZh ? '零基建集成' : 'Zero Infrastructure'}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-300 leading-relaxed">
|
||||||
|
{isZh
|
||||||
|
? '无需 Python、无头浏览器、服务端部署。一行 script 标签搞定。'
|
||||||
|
: "No Python. No headless browser. No server. One script tag — that's it."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MagicCard>
|
||||||
|
</BlurFade>
|
||||||
|
|
||||||
|
<BlurFade inView delay={0.1} className="col-span-1">
|
||||||
|
<MagicCard
|
||||||
|
className="h-full rounded-2xl"
|
||||||
|
gradientFrom="#8b5cf6"
|
||||||
|
gradientTo="#a855f7"
|
||||||
|
gradientColor="#8b5cf6"
|
||||||
|
gradientOpacity={0.12}
|
||||||
|
>
|
||||||
|
<div className={`flex ${CARD_HEIGHT} flex-col`}>
|
||||||
|
<div className="flex-1 relative overflow-hidden">
|
||||||
|
<Particles
|
||||||
|
className="absolute inset-0"
|
||||||
|
quantity={40}
|
||||||
|
staticity={50}
|
||||||
|
ease={80}
|
||||||
|
color="#8b5cf6"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
|
<div className="w-16 h-16 rounded-2xl bg-purple-500/10 dark:bg-purple-500/20 backdrop-blur-sm flex items-center justify-center ring-1 ring-purple-500/20">
|
||||||
|
<Shield className="w-8 h-8 text-purple-500" strokeWidth={1.5} />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="px-6 pb-5">
|
||||||
|
<h3 className="font-semibold text-lg text-gray-900 dark:text-white mb-1">
|
||||||
|
{isZh ? '隐私优先' : 'Privacy by Default'}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-300 leading-relaxed">
|
||||||
|
{isZh
|
||||||
|
? '浏览器内运行,数据完全由你掌控。'
|
||||||
|
: 'Runs in the browser. You control your data, always.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MagicCard>
|
||||||
|
</BlurFade>
|
||||||
|
|
||||||
|
{/* Row 2: Human-in-the-Loop (1col) + LLM (2col) */}
|
||||||
|
<BlurFade inView delay={0.15} className="col-span-1">
|
||||||
|
<MagicCard
|
||||||
|
className="h-full rounded-2xl"
|
||||||
|
gradientFrom="#3b82f6"
|
||||||
|
gradientTo="#8b5cf6"
|
||||||
|
gradientColor="#6366f1"
|
||||||
|
gradientOpacity={0.12}
|
||||||
|
>
|
||||||
|
<div className={`flex ${CARD_HEIGHT} flex-col`}>
|
||||||
|
<div className="flex-1 p-5 flex flex-col justify-center max-w-xs mx-auto w-full">
|
||||||
|
<div className="flex gap-2 mb-2.5">
|
||||||
|
<div className="shrink-0 w-6 h-6 rounded-full bg-purple-100 dark:bg-purple-900/50 flex items-center justify-center">
|
||||||
|
<Bot className="w-3.5 h-3.5 text-purple-600 dark:text-purple-400" />
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-100 dark:bg-white/10 rounded-2xl rounded-tl-md px-3.5 py-2 text-sm text-gray-700 dark:text-gray-200">
|
||||||
|
{isZh ? '找到 3 条匹配记录。选择哪一条?' : 'Found 3 matches. Which one?'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 justify-end mb-2.5">
|
||||||
|
<div className="bg-blue-500 rounded-2xl rounded-tr-md px-3.5 py-2 text-sm text-white">
|
||||||
|
{isZh ? '第二条' : 'The second one.'}
|
||||||
|
</div>
|
||||||
|
<div className="shrink-0 w-6 h-6 rounded-full bg-blue-100 dark:bg-blue-900/50 flex items-center justify-center">
|
||||||
|
<Users className="w-3.5 h-3.5 text-blue-600 dark:text-blue-400" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<div className="shrink-0 w-6 h-6 rounded-full bg-emerald-100 dark:bg-emerald-900/50 flex items-center justify-center text-emerald-600 dark:text-emerald-400 text-xs font-bold">
|
||||||
|
✓
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-100 dark:bg-white/10 rounded-2xl rounded-tl-md px-3.5 py-2 text-sm text-gray-700 dark:text-gray-200">
|
||||||
|
{isZh ? '已选择并提交!' : 'Done! Selected and submitted.'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="px-5 pb-5">
|
||||||
|
<div className="flex items-center gap-2.5 mb-1">
|
||||||
|
<MessageSquare className="w-5 h-5 text-blue-500" />
|
||||||
|
<h3 className="font-semibold text-lg text-gray-900 dark:text-white">
|
||||||
|
{isZh ? '人机协同' : 'Human-in-the-Loop'}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-300 leading-relaxed">
|
||||||
|
{isZh
|
||||||
|
? '内置协作面板,AI 操作前先确认——不是盲目自动化。'
|
||||||
|
: 'Built-in collaborative panel. Agent asks before acting — not blind automation.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MagicCard>
|
||||||
|
</BlurFade>
|
||||||
|
|
||||||
|
<BlurFade inView delay={0.2} className="col-span-1 md:col-span-2">
|
||||||
|
<MagicCard
|
||||||
|
className="h-full rounded-2xl"
|
||||||
|
gradientFrom="#f59e0b"
|
||||||
|
gradientTo="#f97316"
|
||||||
|
gradientColor="#f59e0b"
|
||||||
|
gradientOpacity={0.12}
|
||||||
|
>
|
||||||
|
<div className={`flex ${CARD_HEIGHT} flex-col`}>
|
||||||
|
<div className="flex-1 overflow-hidden relative">
|
||||||
|
{LLM_CLOUD.map((item) => (
|
||||||
|
<span
|
||||||
|
key={item.name}
|
||||||
|
className="absolute font-semibold whitespace-nowrap select-none"
|
||||||
|
style={{
|
||||||
|
left: `${item.x}%`,
|
||||||
|
top: `${item.y}%`,
|
||||||
|
fontSize: `${item.size}rem`,
|
||||||
|
color: item.color,
|
||||||
|
opacity: item.opacity,
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
textShadow: `0 0 80px ${item.color}99`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-emerald-50 dark:bg-emerald-950/20 border border-emerald-200/60 dark:border-emerald-800/30 rounded-xl px-5 py-3.5 font-mono text-sm text-emerald-700 dark:text-emerald-400 flex items-center gap-2.5">
|
<div className="px-7 pb-5">
|
||||||
<span className="text-emerald-500 text-xs shrink-0">✓</span>
|
<div className="flex items-center gap-2.5 mb-1">
|
||||||
{'<script src="page-agent.js"></script>'}
|
<Sparkles className="w-5 h-5 text-amber-500" />
|
||||||
</div>
|
<h3 className="font-semibold text-lg text-gray-900 dark:text-white">
|
||||||
</div>
|
{isZh ? '兼容多种 LLM' : 'Bring Your Own LLMs'}
|
||||||
<div className="px-8 pb-6">
|
</h3>
|
||||||
<div className="flex items-center gap-2.5 mb-1.5">
|
|
||||||
<Box className="w-5 h-5 text-blue-500" />
|
|
||||||
<h3 className="font-semibold text-lg text-gray-900 dark:text-white">
|
|
||||||
{isZh ? '零基建集成' : 'Zero Infrastructure'}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 leading-relaxed">
|
|
||||||
{isZh
|
|
||||||
? '无需 Python、无头浏览器、服务端部署。一行 script 标签搞定。'
|
|
||||||
: "No Python. No headless browser. No server. One script tag — that's it."}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BlurFade>
|
|
||||||
|
|
||||||
{/* Privacy by Default (1-col) */}
|
|
||||||
<BlurFade inView delay={0.1} className="col-span-1">
|
|
||||||
<div className="group relative h-full overflow-hidden rounded-2xl bg-white/80 dark:bg-gray-900/80 backdrop-blur-xl border border-gray-200/60 dark:border-white/8 [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] hover:shadow-xl transition-all duration-500 flex flex-col">
|
|
||||||
<div className="flex-1 relative overflow-hidden">
|
|
||||||
<Particles
|
|
||||||
className="absolute inset-0"
|
|
||||||
quantity={40}
|
|
||||||
staticity={50}
|
|
||||||
ease={80}
|
|
||||||
color="#8b5cf6"
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
|
||||||
<div className="w-20 h-20 rounded-2xl bg-purple-500/10 dark:bg-purple-500/20 backdrop-blur-sm flex items-center justify-center ring-1 ring-purple-500/20">
|
|
||||||
<Shield className="w-10 h-10 text-purple-500" strokeWidth={1.5} />
|
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-sm text-gray-500 dark:text-gray-300 leading-relaxed">
|
||||||
|
{isZh
|
||||||
|
? 'OpenAI、Claude、DeepSeek、Qwen 等,或通过 Ollama 完全离线。'
|
||||||
|
: 'OpenAI, Claude, DeepSeek, Qwen, and more — or fully offline via Ollama.'}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-6 pb-6">
|
</MagicCard>
|
||||||
<h3 className="font-semibold text-lg text-gray-900 dark:text-white mb-1.5">
|
|
||||||
{isZh ? '隐私优先' : 'Privacy by Default'}
|
|
||||||
</h3>
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 leading-relaxed">
|
|
||||||
{isZh
|
|
||||||
? '浏览器内运行,数据完全由你掌控。'
|
|
||||||
: 'Runs in the browser. You control your data, always.'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BlurFade>
|
|
||||||
|
|
||||||
{/* Any LLM (1-col) */}
|
|
||||||
<BlurFade inView delay={0.15} className="col-span-1">
|
|
||||||
<div className="group relative h-full overflow-hidden rounded-2xl bg-white/80 dark:bg-gray-900/80 backdrop-blur-xl border border-gray-200/60 dark:border-white/8 [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] hover:shadow-xl transition-all duration-500 flex flex-col">
|
|
||||||
<div className="flex-1 relative overflow-hidden">
|
|
||||||
<Marquee vertical pauseOnHover className="h-full [--duration:25s] [--gap:0.75rem]">
|
|
||||||
{['OpenAI', 'Claude', 'DeepSeek', 'Qwen', 'Gemini', 'GLM', 'Ollama', 'Groq'].map(
|
|
||||||
(name) => (
|
|
||||||
<div
|
|
||||||
key={name}
|
|
||||||
className="mx-auto rounded-full bg-gray-100 dark:bg-gray-800 px-4 py-2 text-sm font-medium text-gray-600 dark:text-gray-300 ring-1 ring-gray-200/50 dark:ring-white/5"
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Marquee>
|
|
||||||
</div>
|
|
||||||
<div className="px-6 pb-6">
|
|
||||||
<div className="flex items-center gap-2.5 mb-1.5">
|
|
||||||
<Sparkles className="w-5 h-5 text-amber-500" />
|
|
||||||
<h3 className="font-semibold text-lg text-gray-900 dark:text-white">
|
|
||||||
{isZh ? '兼容多种 LLM' : 'Bring Your Own LLMs'}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 leading-relaxed">
|
|
||||||
{isZh
|
|
||||||
? 'OpenAI、Claude、DeepSeek、Qwen 等,或通过 Ollama 完全离线。'
|
|
||||||
: 'OpenAI, Claude, DeepSeek, Qwen, and more — or fully offline via Ollama.'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BlurFade>
|
|
||||||
|
|
||||||
{/* Human-in-the-Loop (2-col) */}
|
|
||||||
<BlurFade inView delay={0.2} className="col-span-1 md:col-span-2">
|
|
||||||
<div className="group relative h-full overflow-hidden rounded-2xl bg-white/80 dark:bg-gray-900/80 backdrop-blur-xl border border-gray-200/60 dark:border-white/8 [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] hover:shadow-xl transition-all duration-500 flex flex-col">
|
|
||||||
<div className="flex-1 p-8 flex flex-col justify-center max-w-md mx-auto w-full">
|
|
||||||
<div className="flex gap-2.5 mb-3">
|
|
||||||
<div className="shrink-0 w-7 h-7 rounded-full bg-purple-100 dark:bg-purple-900/50 flex items-center justify-center">
|
|
||||||
<Bot className="w-4 h-4 text-purple-600 dark:text-purple-400" />
|
|
||||||
</div>
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-800 rounded-2xl rounded-tl-md px-4 py-2.5 text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
{isZh ? '找到 3 条匹配记录。选择哪一条?' : 'Found 3 matches. Which one?'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2.5 justify-end mb-3">
|
|
||||||
<div className="bg-blue-500 rounded-2xl rounded-tr-md px-4 py-2.5 text-sm text-white">
|
|
||||||
{isZh ? '第二条' : 'The second one.'}
|
|
||||||
</div>
|
|
||||||
<div className="shrink-0 w-7 h-7 rounded-full bg-blue-100 dark:bg-blue-900/50 flex items-center justify-center">
|
|
||||||
<Users className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2.5">
|
|
||||||
<div className="shrink-0 w-7 h-7 rounded-full bg-emerald-100 dark:bg-emerald-900/50 flex items-center justify-center text-emerald-600 dark:text-emerald-400 text-xs font-bold">
|
|
||||||
✓
|
|
||||||
</div>
|
|
||||||
<div className="bg-gray-100 dark:bg-gray-800 rounded-2xl rounded-tl-md px-4 py-2.5 text-sm text-gray-700 dark:text-gray-300">
|
|
||||||
{isZh ? '已选择并提交!' : 'Done! Selected and submitted.'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="px-8 pb-6">
|
|
||||||
<div className="flex items-center gap-2.5 mb-1.5">
|
|
||||||
<MessageSquare className="w-5 h-5 text-blue-500" />
|
|
||||||
<h3 className="font-semibold text-lg text-gray-900 dark:text-white">
|
|
||||||
{isZh ? '人机协同' : 'Human-in-the-Loop'}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400 leading-relaxed">
|
|
||||||
{isZh
|
|
||||||
? '内置协作面板,AI 操作前先确认——不是盲目自动化。'
|
|
||||||
: 'Built-in collaborative panel. Agent asks before acting — not blind automation.'}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BlurFade>
|
</BlurFade>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ export default function HeroSection() {
|
|||||||
<div className="flex border-b border-gray-200 dark:border-gray-700">
|
<div className="flex border-b border-gray-200 dark:border-gray-700">
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('try')}
|
onClick={() => setActiveTab('try')}
|
||||||
className={`flex-1 px-4 py-4 text-lg font-medium transition-colors duration-200 rounded-tl-2xl ${
|
className={`cursor-pointer flex-1 px-4 py-4 text-lg font-medium transition-colors duration-200 rounded-tl-2xl ${
|
||||||
activeTab === 'try'
|
activeTab === 'try'
|
||||||
? 'bg-linear-to-r from-blue-50 to-purple-50 dark:from-blue-900/30 dark:to-purple-900/30 text-blue-700 dark:text-blue-300 border-b-2 border-blue-500'
|
? 'bg-linear-to-r from-blue-50 to-purple-50 dark:from-blue-900/30 dark:to-purple-900/30 text-blue-700 dark:text-blue-300 border-b-2 border-blue-500'
|
||||||
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700'
|
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||||
@@ -180,7 +180,7 @@ export default function HeroSection() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setActiveTab('other')}
|
onClick={() => setActiveTab('other')}
|
||||||
className={`flex-1 px-4 py-4 text-lg font-medium transition-colors duration-200 rounded-tr-2xl ${
|
className={`cursor-pointer flex-1 px-4 py-4 text-lg font-medium transition-colors duration-200 rounded-tr-2xl ${
|
||||||
activeTab === 'other'
|
activeTab === 'other'
|
||||||
? 'bg-linear-to-r from-green-50 to-blue-50 dark:from-green-900/30 dark:to-blue-900/30 text-green-700 dark:text-green-300 border-b-2 border-green-500'
|
? 'bg-linear-to-r from-green-50 to-blue-50 dark:from-green-900/30 dark:to-blue-900/30 text-green-700 dark:text-green-300 border-b-2 border-green-500'
|
||||||
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700'
|
: 'text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-gray-700'
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ export default function ScenariosSection() {
|
|||||||
const { isZh } = useLanguage()
|
const { isZh } = useLanguage()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section className="px-6 py-16" aria-labelledby="scenarios-heading">
|
||||||
className="px-6 py-16 bg-white/50 dark:bg-gray-800/50 backdrop-blur-sm"
|
|
||||||
aria-labelledby="scenarios-heading"
|
|
||||||
>
|
|
||||||
<div className="max-w-6xl mx-auto">
|
<div className="max-w-6xl mx-auto">
|
||||||
<BlurFade inView>
|
<BlurFade inView>
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
@@ -25,9 +22,9 @@ export default function ScenariosSection() {
|
|||||||
</BlurFade>
|
</BlurFade>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{/* Scenario 1: SaaS AI Copilot */}
|
{/* SaaS AI Copilot */}
|
||||||
<BlurFade inView delay={0.05}>
|
<BlurFade inView delay={0.05}>
|
||||||
<div className="group relative overflow-hidden rounded-2xl bg-linear-to-b from-blue-50 to-white dark:from-blue-950/20 dark:to-gray-900 border border-blue-100/80 dark:border-blue-900/30 hover:shadow-xl hover:-translate-y-1 transition-all duration-500">
|
<div className="group relative overflow-hidden rounded-2xl bg-linear-to-b from-blue-50 to-white dark:from-blue-950/40 dark:to-gray-800 border border-blue-200/80 dark:border-blue-800/50 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-500">
|
||||||
<div className="p-6 pb-4">
|
<div className="p-6 pb-4">
|
||||||
<div className="rounded-xl bg-gray-950 p-4 font-mono text-xs leading-6 text-gray-300 overflow-hidden shadow-inner">
|
<div className="rounded-xl bg-gray-950 p-4 font-mono text-xs leading-6 text-gray-300 overflow-hidden shadow-inner">
|
||||||
<div>
|
<div>
|
||||||
@@ -60,7 +57,7 @@ export default function ScenariosSection() {
|
|||||||
{isZh ? 'SaaS AI 副驾驶' : 'SaaS AI Copilot'}
|
{isZh ? 'SaaS AI 副驾驶' : 'SaaS AI Copilot'}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed">
|
<p className="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||||
{isZh
|
{isZh
|
||||||
? '几小时内为你的产品加上 AI 副驾驶,不需要重写后端。'
|
? '几小时内为你的产品加上 AI 副驾驶,不需要重写后端。'
|
||||||
: 'Ship an AI copilot in your product in hours, not months. No backend rewrite needed.'}
|
: 'Ship an AI copilot in your product in hours, not months. No backend rewrite needed.'}
|
||||||
@@ -69,12 +66,12 @@ export default function ScenariosSection() {
|
|||||||
</div>
|
</div>
|
||||||
</BlurFade>
|
</BlurFade>
|
||||||
|
|
||||||
{/* Scenario 2: Smart Form Filling */}
|
{/* Smart Form Filling */}
|
||||||
<BlurFade inView delay={0.1}>
|
<BlurFade inView delay={0.1}>
|
||||||
<div className="group relative overflow-hidden rounded-2xl bg-linear-to-b from-amber-50 to-white dark:from-amber-950/20 dark:to-gray-900 border border-amber-100/80 dark:border-amber-900/30 hover:shadow-xl hover:-translate-y-1 transition-all duration-500">
|
<div className="group relative overflow-hidden rounded-2xl bg-linear-to-b from-amber-50 to-white dark:from-amber-950/40 dark:to-gray-800 border border-amber-200/80 dark:border-amber-800/50 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-500">
|
||||||
<div className="p-6 pb-4">
|
<div className="p-6 pb-4">
|
||||||
<div className="rounded-xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 p-4 shadow-inner space-y-2.5">
|
<div className="rounded-xl bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-700 p-4 shadow-inner space-y-2.5">
|
||||||
<div className="flex items-center gap-2 text-xs text-gray-500 bg-amber-50 dark:bg-amber-900/20 rounded-lg px-3 py-2 border border-amber-200/50 dark:border-amber-800/30">
|
<div className="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 bg-amber-50 dark:bg-amber-900/30 rounded-lg px-3 py-2 border border-amber-200/50 dark:border-amber-700/40">
|
||||||
<span>🪄</span>
|
<span>🪄</span>
|
||||||
<span className="italic">
|
<span className="italic">
|
||||||
{isZh
|
{isZh
|
||||||
@@ -83,22 +80,15 @@ export default function ScenariosSection() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{[
|
{[
|
||||||
{
|
{ label: isZh ? '姓名' : 'Name', value: 'John Smith' },
|
||||||
label: isZh ? '姓名' : 'Name',
|
{ label: isZh ? '金额' : 'Amount', value: '$342.50' },
|
||||||
value: 'John Smith',
|
{ label: isZh ? '类目' : 'Category', value: 'Travel' },
|
||||||
},
|
|
||||||
{
|
|
||||||
label: isZh ? '金额' : 'Amount',
|
|
||||||
value: '$342.50',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: isZh ? '类目' : 'Category',
|
|
||||||
value: 'Travel',
|
|
||||||
},
|
|
||||||
].map((field) => (
|
].map((field) => (
|
||||||
<div key={field.label} className="flex items-center gap-2">
|
<div key={field.label} className="flex items-center gap-2">
|
||||||
<span className="text-xs text-gray-400 w-12 shrink-0">{field.label}</span>
|
<span className="text-xs text-gray-400 dark:text-gray-500 w-12 shrink-0">
|
||||||
<div className="flex-1 h-7 bg-gray-50 dark:bg-gray-700 rounded border border-gray-200 dark:border-gray-600 px-2 flex items-center text-xs text-gray-600 dark:text-gray-300">
|
{field.label}
|
||||||
|
</span>
|
||||||
|
<div className="flex-1 h-7 bg-gray-50 dark:bg-gray-800 rounded border border-gray-200 dark:border-gray-600 px-2 flex items-center text-xs text-gray-600 dark:text-gray-300">
|
||||||
{field.value}
|
{field.value}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-emerald-500 text-xs">✓</span>
|
<span className="text-emerald-500 text-xs">✓</span>
|
||||||
@@ -113,7 +103,7 @@ export default function ScenariosSection() {
|
|||||||
{isZh ? '智能表单填写' : 'Smart Form Filling'}
|
{isZh ? '智能表单填写' : 'Smart Form Filling'}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed">
|
<p className="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||||
{isZh
|
{isZh
|
||||||
? '把 20 次点击变成一句话。ERP、CRM、管理后台的最佳拍档。'
|
? '把 20 次点击变成一句话。ERP、CRM、管理后台的最佳拍档。'
|
||||||
: 'Turn 20-click workflows into one sentence. Perfect for ERP, CRM, and admin systems.'}
|
: 'Turn 20-click workflows into one sentence. Perfect for ERP, CRM, and admin systems.'}
|
||||||
@@ -122,13 +112,13 @@ export default function ScenariosSection() {
|
|||||||
</div>
|
</div>
|
||||||
</BlurFade>
|
</BlurFade>
|
||||||
|
|
||||||
{/* Scenario 3: Accessibility */}
|
{/* Accessibility */}
|
||||||
<BlurFade inView delay={0.15}>
|
<BlurFade inView delay={0.15}>
|
||||||
<div className="group relative overflow-hidden rounded-2xl bg-linear-to-b from-purple-50 to-white dark:from-purple-950/20 dark:to-gray-900 border border-purple-100/80 dark:border-purple-900/30 hover:shadow-xl hover:-translate-y-1 transition-all duration-500">
|
<div className="group relative overflow-hidden rounded-2xl bg-linear-to-b from-purple-50 to-white dark:from-purple-950/40 dark:to-gray-800 border border-purple-200/80 dark:border-purple-800/50 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-500">
|
||||||
<div className="p-6 pb-4 flex flex-col items-center justify-center">
|
<div className="p-6 pb-4 flex flex-col items-center justify-center">
|
||||||
<div className="w-full rounded-xl bg-purple-50 dark:bg-purple-900/20 p-5 space-y-3">
|
<div className="w-full rounded-xl bg-purple-50 dark:bg-purple-900/30 p-5 space-y-3">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="w-8 h-8 rounded-full bg-purple-500/10 flex items-center justify-center text-base">
|
<div className="w-8 h-8 rounded-full bg-purple-500/10 dark:bg-purple-500/20 flex items-center justify-center text-base">
|
||||||
🎤
|
🎤
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm text-purple-700 dark:text-purple-300 italic">
|
<div className="text-sm text-purple-700 dark:text-purple-300 italic">
|
||||||
@@ -141,7 +131,7 @@ export default function ScenariosSection() {
|
|||||||
<div className="w-1.5 h-1.5 bg-purple-400 rounded-full animate-pulse [animation-delay:0.2s]"></div>
|
<div className="w-1.5 h-1.5 bg-purple-400 rounded-full animate-pulse [animation-delay:0.2s]"></div>
|
||||||
<div className="w-1.5 h-1.5 bg-purple-400 rounded-full animate-pulse [animation-delay:0.4s]"></div>
|
<div className="w-1.5 h-1.5 bg-purple-400 rounded-full animate-pulse [animation-delay:0.4s]"></div>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-purple-500">
|
<span className="text-xs text-purple-500 dark:text-purple-400">
|
||||||
{isZh ? 'AI 正在执行...' : 'AI executing...'}
|
{isZh ? 'AI 正在执行...' : 'AI executing...'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -157,7 +147,7 @@ export default function ScenariosSection() {
|
|||||||
{isZh ? '无障碍增强' : 'Accessibility'}
|
{isZh ? '无障碍增强' : 'Accessibility'}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 leading-relaxed">
|
<p className="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||||
{isZh
|
{isZh
|
||||||
? '用自然语言让任何网页无障碍。语音指令、屏幕阅读器,零门槛。'
|
? '用自然语言让任何网页无障碍。语音指令、屏幕阅读器,零门槛。'
|
||||||
: 'Make any web app accessible through natural language. Voice, screen readers, zero barrier.'}
|
: 'Make any web app accessible through natural language. Voice, screen readers, zero barrier.'}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Suspense, lazy, useLayoutEffect } from 'react'
|
import { Suspense, useLayoutEffect } from 'react'
|
||||||
import { Route, Switch, useLocation } from 'wouter'
|
import { Route, Switch, useLocation } from 'wouter'
|
||||||
|
|
||||||
|
import Footer from './components/Footer'
|
||||||
|
import Header from './components/Header'
|
||||||
import HomePage from './pages/Home'
|
import HomePage from './pages/Home'
|
||||||
import DocsPages from './pages/docs/index'
|
import DocsPages from './pages/docs/index'
|
||||||
|
|
||||||
// const DocsPages = lazy(() => import('./pages/docs/index'))
|
|
||||||
|
|
||||||
function ScrollToTop() {
|
function ScrollToTop() {
|
||||||
const [pathname] = useLocation()
|
const [pathname] = useLocation()
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
@@ -16,29 +16,37 @@ function ScrollToTop() {
|
|||||||
|
|
||||||
export default function Router() {
|
export default function Router() {
|
||||||
return (
|
return (
|
||||||
<Suspense>
|
<div className="flex min-h-screen flex-col">
|
||||||
<ScrollToTop />
|
<Header />
|
||||||
<Switch>
|
<Suspense>
|
||||||
{/* Home page */}
|
<ScrollToTop />
|
||||||
<Route path="/">
|
<Switch>
|
||||||
<HomePage />
|
<Route path="/">
|
||||||
</Route>
|
<main
|
||||||
|
id="main-content"
|
||||||
|
className="flex-1 bg-linear-to-br from-blue-50 to-purple-50 dark:from-gray-900 dark:to-gray-800"
|
||||||
|
>
|
||||||
|
<HomePage />
|
||||||
|
</main>
|
||||||
|
</Route>
|
||||||
|
|
||||||
{/* All docs pages */}
|
<Route path="/docs" nest>
|
||||||
<Route path="/docs" nest>
|
<div className="flex-1 bg-white dark:bg-gray-900">
|
||||||
<DocsPages />
|
<DocsPages />
|
||||||
</Route>
|
|
||||||
|
|
||||||
{/* 404 */}
|
|
||||||
<Route>
|
|
||||||
<div className="min-h-screen bg-white dark:bg-gray-900 flex items-center justify-center">
|
|
||||||
<div className="text-center">
|
|
||||||
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white">404</h1>
|
|
||||||
<p className="text-xl text-gray-600 dark:text-gray-300">页面未找到</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Route>
|
||||||
</Route>
|
|
||||||
</Switch>
|
<Route>
|
||||||
</Suspense>
|
<div className="flex-1 bg-white dark:bg-gray-900 flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-4xl font-bold mb-4 text-gray-900 dark:text-white">404</h1>
|
||||||
|
<p className="text-xl text-gray-600 dark:text-gray-300">Page not found</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
</Suspense>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
import react from '@vitejs/plugin-react-swc'
|
import react from '@vitejs/plugin-react-swc'
|
||||||
import { config as dotenvConfig } from 'dotenv'
|
import { config as dotenvConfig } from 'dotenv'
|
||||||
import { readFileSync } from 'node:fs'
|
import { copyFileSync, mkdirSync, readFileSync } from 'node:fs'
|
||||||
import process from 'node:process'
|
import process from 'node:process'
|
||||||
import { dirname, resolve } from 'path'
|
import { dirname, join, resolve } from 'path'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
@@ -15,11 +15,47 @@ const pageAgentPkg = JSON.parse(
|
|||||||
// Load .env from repo root
|
// Load .env from repo root
|
||||||
dotenvConfig({ path: resolve(__dirname, '../../.env') })
|
dotenvConfig({ path: resolve(__dirname, '../../.env') })
|
||||||
|
|
||||||
|
// All SPA routes that need index.html copies for direct access on static hosts
|
||||||
|
const SPA_ROUTES = [
|
||||||
|
'docs',
|
||||||
|
'docs/introduction/overview',
|
||||||
|
'docs/introduction/quick-start',
|
||||||
|
'docs/introduction/limitations',
|
||||||
|
'docs/introduction/troubleshooting',
|
||||||
|
'docs/features/custom-tools',
|
||||||
|
'docs/features/data-masking',
|
||||||
|
'docs/features/custom-instructions',
|
||||||
|
'docs/features/models',
|
||||||
|
'docs/features/chrome-extension',
|
||||||
|
'docs/advanced/page-agent',
|
||||||
|
'docs/advanced/page-agent-core',
|
||||||
|
'docs/advanced/custom-ui',
|
||||||
|
'docs/integration/security-permissions',
|
||||||
|
'docs/integration/best-practices',
|
||||||
|
'docs/integration/third-party-agent',
|
||||||
|
]
|
||||||
|
|
||||||
|
function spaRoutes() {
|
||||||
|
return {
|
||||||
|
name: 'spa-routes',
|
||||||
|
closeBundle() {
|
||||||
|
const dist = resolve(__dirname, 'dist')
|
||||||
|
const src = join(dist, 'index.html')
|
||||||
|
for (const route of SPA_ROUTES) {
|
||||||
|
const dir = join(dist, route)
|
||||||
|
mkdirSync(dir, { recursive: true })
|
||||||
|
copyFileSync(src, join(dir, 'index.html'))
|
||||||
|
}
|
||||||
|
console.log(` ✓ Copied index.html to ${SPA_ROUTES.length} SPA routes`)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Website Config (React Documentation Site)
|
// Website Config (React Documentation Site)
|
||||||
export default defineConfig(({ mode }) => ({
|
export default defineConfig(({ mode }) => ({
|
||||||
base: './',
|
base: '/page-agent/',
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
plugins: [react(), tailwindcss()],
|
plugins: [react(), tailwindcss(), spaRoutes()],
|
||||||
build: {
|
build: {
|
||||||
chunkSizeWarningLimit: 2000,
|
chunkSizeWarningLimit: 2000,
|
||||||
cssCodeSplit: true,
|
cssCodeSplit: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user