diff --git a/package.json b/package.json index 886f3424..e44bcf62 100644 --- a/package.json +++ b/package.json @@ -52,11 +52,12 @@ }, "dependencies": { "@ant-design/icons": "^4.7.0", - "@huolala-tech/page-spy-browser": "^2.1.2", - "@huolala-tech/page-spy-plugin-data-harbor": "^2.1.1", - "@huolala-tech/page-spy-plugin-rrweb": "^2.1.1", - "@huolala-tech/page-spy-plugin-whole-bundle": "^2.1.3", - "@huolala-tech/page-spy-types": "^2.1.1", + "@huolala-tech/page-spy-browser": "^2.1.3", + "@huolala-tech/page-spy-plugin-data-harbor": "^2.1.2", + "@huolala-tech/page-spy-plugin-ospy": "^2.1.7", + "@huolala-tech/page-spy-plugin-rrweb": "^2.1.2", + "@huolala-tech/page-spy-plugin-whole-bundle": "^2.1.5", + "@huolala-tech/page-spy-types": "^2.1.2", "@huolala-tech/react-json-view": "^1.2.5", "@huolala-tech/request": "^1.1.2", "@mdx-js/mdx": "^3.0.1", @@ -112,5 +113,6 @@ ], "optionalDependencies": { "@huolala-tech/page-spy-api": "^2.0.0" - } + }, + "packageManager": "yarn@1.22.19" } diff --git a/scripts/public-files.sh b/scripts/public-files.sh index 21851970..b918f7ee 100644 --- a/scripts/public-files.sh +++ b/scripts/public-files.sh @@ -18,6 +18,9 @@ cp "${root}/node_modules/@huolala-tech/page-spy-plugin-data-harbor/dist/iife/ind # @huolala-tech/page-spy-plugin-whole-bundle mkdir -p "${target_plugin}/whole-bundle" cp "${root}/node_modules/@huolala-tech/page-spy-plugin-whole-bundle/dist/iife/index.min.js" "${root}/public/plugin/whole-bundle/index.min.js" +# @huolala-tech/page-spy-plugin-ospy +mkdir -p "${target_plugin}/ospy" +cp "${root}/node_modules/@huolala-tech/page-spy-plugin-ospy/dist/iife/index.min.js" "${root}/public/plugin/ospy/index.min.js" # source-map target_sourcemap="${root}/public/source-map" diff --git a/src/assets/image/logo-pure.svg b/src/assets/image/logo-pure.svg index 3a8f4ca2..1ee2bf0f 100644 --- a/src/assets/image/logo-pure.svg +++ b/src/assets/image/logo-pure.svg @@ -1,4 +1,3 @@ - - + + diff --git a/src/assets/image/logo.svg b/src/assets/image/logo.svg index 054c516b..f388032f 100644 --- a/src/assets/image/logo.svg +++ b/src/assets/image/logo.svg @@ -1,11 +1,10 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/assets/image/o-spy.svg b/src/assets/image/o-spy.svg new file mode 100644 index 00000000..19994e4c --- /dev/null +++ b/src/assets/image/o-spy.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/image/screenshot/ospy-workflow.en.png b/src/assets/image/screenshot/ospy-workflow.en.png new file mode 100644 index 00000000..80d5deb3 Binary files /dev/null and b/src/assets/image/screenshot/ospy-workflow.en.png differ diff --git a/src/assets/image/screenshot/ospy-workflow.zh.png b/src/assets/image/screenshot/ospy-workflow.zh.png new file mode 100644 index 00000000..e71896d9 Binary files /dev/null and b/src/assets/image/screenshot/ospy-workflow.zh.png differ diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index b536bbc7..01bbb47f 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -47,7 +47,7 @@ "copied": "Copied", "prev": "Previous", "next": "Next", - "replay-lab": "Replay Lab", + "o-spy": "oSpy", "toc": "Table of Contents", "filter": "Filter", "back": "Back" @@ -90,8 +90,8 @@ "banner": { "title": "All-In-One
Remote Debugging Tool", "desc": "Debug remotely and easily with a range of features like chrome devtool.", - "get-start": "Quick Start", - "take-try": "Try Now" + "get-start": "Get Started", + "badge": "No Deploy" }, "intro": { "does": "What PageSpy does", @@ -100,7 +100,7 @@ "providesTitle": "Out-of-box <1>SDK and
<4>debugger client", "load-script": "First, load the file", "init-instance": "Then, configure (optional) and initialize", - "welcome": "Welcome to use PageSpy" + "community-comments": "<0>Loved by the community<1>Don't just take our word for it
Hear from real users in the PageSpy community" }, "selConn": { "title": "Select the connection", @@ -248,25 +248,30 @@ "remark": "Remark" } }, - "lab": { - "welcome": "Welcome", - "welcome-title": "Welcome to
Replay Lab", - "welcome-desc": "With just a few lines of code, empower your system with robust 'feedback capabilities'.
All data stays local, with no network transmission, ensuring complete privacy.", - "take-try": "Try Demo", + "oSpy": { + "slogan": "Offline Recording
Plug and Play", + "desc": "Record and replay with a few lines of code.
All data stays local, no network transfer, no privacy risk.", + "import-use": "Integrate O-Spy", "select-log": "Select Log", - "import-use": "Integrate", + "take-try": "Try Demo", "install": "Install", "install-title": "Choose Preferred Integration", "install-desc": "Framework agnostic. Choose any method to integrate into your project.", "comment-title": "Title displayed on floating ball and popup", "comment-logo": "Path to the logo image", "comment-primaryColor": "Customize theme color", - "comment-autoRender": "Call $feedback.open() to open the popup if set to false", + "comment-autoRender": "Call $oSpy.open() to open the popup if set to false", + "comment-exportButtonText": "Custom text on the button inside the popup", + "comment-onExportButtonClick": "Custom callback after clicking the button inside the popup", + "comment-default-params": "All parameters are optional, use them as needed.", "install-1st": "First, install the dependency:", "install-2nd": "Next, create an instance:", "install-result": "After installation, a widget will appear in the bottom right corner. Click to open the popup, enter a note, and export the log.", "replay": "Replay", "guide": "Back to Guide", - "only-pc": "Please visit on PC :)" + "only-pc": "Please visit on PC :)", + "console-output": "Console Output", + "network-request": "Network Request", + "modify-storage": "Modify Storage" } } diff --git a/src/assets/locales/ja.json b/src/assets/locales/ja.json index f972da24..6bca441f 100644 --- a/src/assets/locales/ja.json +++ b/src/assets/locales/ja.json @@ -47,7 +47,7 @@ "copied": "コピーされました", "prev": "前のページ", "next": "次のページ", - "replay-lab": "リプレイラボ", + "o-spy": "oSpy", "toc": "目次", "filter": "フィルター", "back": "戻る" @@ -89,8 +89,8 @@ "banner": { "title": "多機能
リモートデバッグツール", "desc": "Google Chromeのコンソールのように簡単にリモートデバッグを開始します。", - "get-start": "クイックスタート", - "take-try": "今すぐ体験" + "get-start": "使い始める", + "badge": "デプロイ不要" }, "intro": { "does": "PageSpyでできること", @@ -99,7 +99,7 @@ "providesTitle": "使用準備の整った <1>SDK および
<4>クライアントデバッグ", "load-script": "まず、ファイルを読み込む", "init-instance": "次に、設定(任意)して初期化する", - "welcome": "PageSpyへようこそ" + "community-comments": "<0>コミュニティで高い評価<1>私たちの言葉だけでなく
PageSpy コミュニティのリアルな声を聞いてみましょう" }, "selConn": { "title": "接続を選択", @@ -247,25 +247,30 @@ "remark": "備考" } }, - "lab": { - "welcome": "ようこそ", - "welcome-title": "リプレイラボへ
ようこそ", - "welcome-desc": "数行のコードでシステムに強力な「フィードバック機能」を追加できます。
データはすべてローカルに保存され、ネットワーク送信は不要で、プライバシーが完全に保護されます。", - "take-try": "デモ体験", + "oSpy": { + "slogan": "オフライン記録
プラグアンドプレイ", + "desc": "数行のコードで記録&リプレイ。
データはローカルのみ、ネット転送なし、プライバシー安心。", + "import-use": "O-Spy 接続", "select-log": "ログ選択", - "import-use": "導入", + "take-try": "デモ体験", "install": "インストール", "install-title": "お好みの統合方法を選択してください", "install-desc": "フレームワークに依存しません。プロジェクトに最適な方法を自由に選べます。", "comment-title": "フローティングボタンとポップアップに表示されるタイトル", "comment-logo": "ロゴ画像のパス", "comment-primaryColor": "テーマカラーをカスタマイズ", - "comment-autoRender": "false に設定した場合、$feedback.open() を呼び出してポップアップを開けます。", + "comment-autoRender": "false に設定した場合、$oSpy.open() を呼び出してポップアップを開けます。", + "comment-exportButtonText": "ポップアップ内ボタンのカスタムテキスト", + "comment-onExportButtonClick": "ポップアップ内ボタンをクリックした後のカスタムコールバック", + "comment-default-params": "すべてのパラメーターはオプションです。必要に応じて使用してください。", "install-1st": "最初に依存関係をインストールしてください:", "install-2nd": "次にインスタンスを作成してください:", "install-result": "インストール後、右下にウィジェットが表示されます。クリックしてポップアップを開き、メモを入力してログをエクスポートしてください。", "replay": "リプレイ", "guide": "ガイドへ戻る", - "only-pc": "PCでご利用ください :)" + "only-pc": "PCでご利用ください :)", + "console-output": "コンソール出力", + "network-request": "ネットワークリクエスト", + "modify-storage": "Storage を変更" } } diff --git a/src/assets/locales/ko.json b/src/assets/locales/ko.json index 886772ed..f4c630da 100644 --- a/src/assets/locales/ko.json +++ b/src/assets/locales/ko.json @@ -47,7 +47,7 @@ "copied": "복사되었습니다", "prev": "이전 페이지", "next": "다음 페이지", - "replay-lab": "리플레이 연구소", + "o-spy": "oSpy", "toc": "목차", "filter": "필터", "back": "돌아가기" @@ -89,8 +89,8 @@ "banner": { "title": "다기능
원격 디버깅 도구", "desc": "Google Chrome 콘솔을 사용하는 것처럼 간단히 원격 디버깅을 시작하세요.", - "get-start": "빠른 시작", - "take-try": "지금 체험" + "get-start": "시작하기", + "badge": "배포불필요" }, "intro": { "does": "PageSpy가 할 수 있는 일", @@ -99,7 +99,7 @@ "providesTitle": "사용 준비가 된 <1>SDK
및 <4>클라이언트 디버그", "load-script": "먼저, 파일을 로드합니다", "init-instance": "그런 다음, 설정(선택 사항)하고 초기화합니다", - "welcome": "PageSpy에 오신 것을 환영합니다." + "community-comments": "<0>커뮤니티에서 사랑받는 서비스<1>우리의 말만 믿지 마세요
PageSpy 커뮤니티의 실제 사용자들의 의견을 들어보세요" }, "selConn": { "title": "연결 선택", @@ -247,25 +247,30 @@ "remark": "비고" } }, - "lab": { - "welcome": "환영합니다", - "welcome-title": "리플레이 랩에
오신 것을 환영합니다", - "welcome-desc": "몇 줄의 코드로 시스템에 강력한 '피드백 기능'을 추가하세요.
모든 데이터는 로컬에 저장되며, 네트워크 전송 없이 완벽한 개인정보 보호가 가능합니다.", - "take-try": "데모 체험", + "oSpy": { + "slogan": "오프라인 기록
플러그 앤 플레이", + "desc": "몇 줄의 코드로 기록 및 재생.
데이터는 로컬에만 저장, 네트워크 전송 없음, 개인정보 걱정 없음.", + "import-use": "O-Spy 연결", "select-log": "로그 선택", - "import-use": "연동", + "take-try": "데모 체험", "install": "설치", "install-title": "선호하는 통합 방식을 선택하세요", "install-desc": "프레임워크에 구애받지 않습니다. 프로젝트에 맞는 방식을 자유롭게 선택하세요.", "comment-title": "플로팅 버튼과 팝업에 표시되는 제목", "comment-logo": "로고 이미지 경로", "comment-primaryColor": "테마 색상 사용자 정의", - "comment-autoRender": "false로 설정 시 $feedback.open()을 호출하여 팝업을 열 수 있습니다.", + "comment-autoRender": "false로 설정 시 $oSpy.open()을 호출하여 팝업을 열 수 있습니다.", + "comment-exportButtonText": "팝업 내 버튼의 사용자 지정 텍스트", + "comment-onExportButtonClick": "팝업 내 버튼 클릭 후 사용자 지정 콜백", + "comment-default-params": "모든 매개변수는 선택 사항이며, 필요에 따라 사용하세요.", "install-1st": "먼저 의존성을 설치하세요:", "install-2nd": "다음으로 인스턴스를 생성하세요:", "install-result": "설치 후 오른쪽 하단에 위젯이 나타납니다. 클릭하여 팝업을 열고 메모를 입력한 후 로그를 내보내세요.", "replay": "재생", "guide": "가이드로 돌아가기", - "only-pc": "PC에서 방문해주세요 :)" + "only-pc": "PC에서 방문해주세요 :)", + "console-output": "콘솔 출력", + "network-request": "네트워크 요청", + "modify-storage": "Storage 수정" } } diff --git a/src/assets/locales/zh.json b/src/assets/locales/zh.json index 42fdeb7e..3b50a139 100644 --- a/src/assets/locales/zh.json +++ b/src/assets/locales/zh.json @@ -47,7 +47,7 @@ "copied": "已复制", "prev": "上一页", "next": "下一页", - "replay-lab": "回放实验室", + "o-spy": "oSpy", "toc": "本页目录", "filter": "筛选", "back": "返回" @@ -88,9 +88,9 @@ }, "banner": { "title": "多功能
远程调试工具", - "desc": "像使用谷歌浏览器的控制台一样简单地开始远程调试。", - "get-start": "快速上手", - "take-try": "立刻体验" + "desc": "像使用谷歌浏览器的控制台一样简单地开始远程调试", + "get-start": "开始使用", + "badge": "免部署版" }, "intro": { "does": "PageSpy 可以做到", @@ -99,7 +99,7 @@ "providesTitle": "开箱即用的 <1>SDK
<4>调试的客户端", "load-script": "首先,引入文件", "init-instance": "然后,配置(可选的)并初始化", - "welcome": "欢迎使用 PageSpy" + "community-comments": "<0>深受社区青睐<1>不要只看我们说
来听听 PageSpy 社区中真实用户的声音" }, "selConn": { "title": "选择连接", @@ -247,25 +247,30 @@ "remark": "备注" } }, - "lab": { - "welcome": "欢迎", - "welcome-title": "欢迎来到
回放实验室", - "welcome-desc": "几行代码,让系统拥有强大的「问题反馈」能力
数据都在本地,不经过网络传输,无需担心隐私泄露", - "take-try": "体验 Demo", + "oSpy": { + "slogan": "离线记录
即插即用", + "desc": "几行代码,记录程序现场用于回放
数据都在本地,不经过网络传输,无需担心隐私泄露", + "import-use": "接入 O-Spy", "select-log": "选择日志回放", - "import-use": "接入使用", + "take-try": "体验 Demo", "install": "安装", "install-title": "随心选择引入方式", "install-desc": "与框架无关,你可以任选一种方式在项目中引入", "comment-title": "悬浮球和弹窗上显示的标题", "comment-logo": "Logo 的图片路径", "comment-primaryColor": "定制主题色", - "comment-autoRender": "默认渲染悬浮球,设置为 false 后可调用 $feedback.open() 打开弹窗", + "comment-autoRender": "默认渲染悬浮球,设置为 false 后可调用 $oSpy.open() 打开弹窗", + "comment-exportButtonText": "自定义弹窗内按钮上的文案", + "comment-onExportButtonClick": "自定义弹窗内按钮点击后的回调", + "comment-default-params": "所有参数都是可选的,你可按需使用", "install-1st": "首先安装依赖:", "install-2nd": "接着实例化:", "install-result": "引入后会出现右下角所示控件,点开弹窗输入备注信息、并导出日志。", "replay": "回放", "guide": "返回指引", - "only-pc": "请前往 PC 端体验 :)" + "only-pc": "请前往 PC 端体验 :)", + "console-output": "输出日志", + "network-request": "网络请求", + "modify-storage": "修改 Storage" } } diff --git a/src/pages/Docs/components/DeployNext/index.tsx b/src/components/Docs/components/DeployNext/index.tsx similarity index 100% rename from src/pages/Docs/components/DeployNext/index.tsx rename to src/components/Docs/components/DeployNext/index.tsx diff --git a/src/pages/Docs/components/DocContent/index.less b/src/components/Docs/components/DocContent/index.less similarity index 97% rename from src/pages/Docs/components/DocContent/index.less rename to src/components/Docs/components/DocContent/index.less index 9cc1f9be..9bdf790c 100644 --- a/src/pages/Docs/components/DocContent/index.less +++ b/src/components/Docs/components/DocContent/index.less @@ -111,14 +111,16 @@ border-radius: 5px; } table { + min-width: 80%; margin-top: 12px; border-collapse: collapse; - border: 1px solid #333; + border: 2px solid #333; border-radius: 4px; + text-align: center; } th, td { - border: 1px solid #333; + border: 2px solid #333; border-radius: 4px; padding: 6px 14px; diff --git a/src/pages/Docs/components/DocContent/index.tsx b/src/components/Docs/components/DocContent/index.tsx similarity index 82% rename from src/pages/Docs/components/DocContent/index.tsx rename to src/components/Docs/components/DocContent/index.tsx index 2949d0a1..b097651a 100644 --- a/src/pages/Docs/components/DocContent/index.tsx +++ b/src/components/Docs/components/DocContent/index.tsx @@ -8,7 +8,6 @@ import React, { } from 'react'; import './index.less'; import { useLocation, useNavigate, useParams } from 'react-router-dom'; -import { DOC_MENUS, ORDER_DOC_MENUS, OrderDocMenus } from '../DocMenus'; import { useTranslation } from 'react-i18next'; import PrevSvg from '@/assets/image/prev.svg?react'; import NextSvg from '@/assets/image/next.svg?react'; @@ -20,14 +19,14 @@ import { useSidebarStore } from '@/store/doc-sidebar'; import { useShallow } from 'zustand/react/shallow'; import { TransitionContext } from '@/components/Transition'; import components from './mdx-mapping'; -import { getDocContent, LOAD_DOC_EVENT } from './content'; import { ToC } from './toc'; +import { LOAD_DOC_EVENT, SidebarItem, useDocContext } from '../../context'; const FooterLink = ({ menu, flag, }: { - menu: OrderDocMenus[number] | null; + menu: SidebarItem | null; flag: 'prev' | 'next'; }) => { const [lang] = useLanguage(); @@ -61,31 +60,32 @@ const FooterLink = ({ }; export const DocContent = () => { - const { t } = useTranslation(); const [lang] = useLanguage(); + const { orderDocMenus, getContent } = useDocContext(); const params = useParams(); - // route match rule "/docs/*" - const doc = params['*'] || DOC_MENUS[0].children[0].doc; + const currentDoc = useMemo(() => { + return params['*'] || orderDocMenus[0].doc; + }, [orderDocMenus, params]); const { prev, current, next } = useMemo(() => { - const navigation: Record = { + const navigation: Record = { prev: null, current: null, next: null, }; - const index = ORDER_DOC_MENUS.findIndex((o) => o.doc === doc); + const index = orderDocMenus.findIndex((o) => o.doc === currentDoc); if (index < 0) return navigation; if (index === 0) { - navigation.next = ORDER_DOC_MENUS[1]; - } else if (index === ORDER_DOC_MENUS.length - 1) { - navigation.prev = ORDER_DOC_MENUS[index - 1]; + navigation.next = orderDocMenus[1]; + } else if (index === orderDocMenus.length - 1) { + navigation.prev = orderDocMenus[index - 1]; } else { - navigation.prev = ORDER_DOC_MENUS[index - 1]; - navigation.next = ORDER_DOC_MENUS[index + 1]; + navigation.prev = orderDocMenus[index - 1]; + navigation.next = orderDocMenus[index + 1]; } - navigation.current = ORDER_DOC_MENUS[index]; + navigation.current = orderDocMenus[index]; return navigation; - }, [doc]); + }, [currentDoc, orderDocMenus]); const rootRef = useRef(null); // 侧边/底部导航:切换文档,处理状态过渡 @@ -117,6 +117,7 @@ export const DocContent = () => { }, [hash]); useEventListener(LOAD_DOC_EVENT, (e) => { const { detail } = e as CustomEvent; + if (detail.loading === false) { scrollIntoAnchor(); } @@ -145,8 +146,8 @@ export const DocContent = () => { )} - {React.createElement(getDocContent(doc, lang), { - key: doc, + {React.createElement(getContent(currentDoc, lang), { + key: currentDoc, components, })} @@ -167,7 +168,7 @@ export const DocContent = () => { - + ); }; diff --git a/src/pages/Docs/components/DocContent/mdx-mapping/A.tsx b/src/components/Docs/components/DocContent/mdx-mapping/A.tsx similarity index 100% rename from src/pages/Docs/components/DocContent/mdx-mapping/A.tsx rename to src/components/Docs/components/DocContent/mdx-mapping/A.tsx diff --git a/src/pages/Docs/components/DocContent/mdx-mapping/Heading.tsx b/src/components/Docs/components/DocContent/mdx-mapping/Heading.tsx similarity index 100% rename from src/pages/Docs/components/DocContent/mdx-mapping/Heading.tsx rename to src/components/Docs/components/DocContent/mdx-mapping/Heading.tsx diff --git a/src/pages/Docs/components/DocContent/mdx-mapping/Pre.tsx b/src/components/Docs/components/DocContent/mdx-mapping/Pre.tsx similarity index 100% rename from src/pages/Docs/components/DocContent/mdx-mapping/Pre.tsx rename to src/components/Docs/components/DocContent/mdx-mapping/Pre.tsx diff --git a/src/pages/Docs/components/DocContent/mdx-mapping/index.tsx b/src/components/Docs/components/DocContent/mdx-mapping/index.tsx similarity index 100% rename from src/pages/Docs/components/DocContent/mdx-mapping/index.tsx rename to src/components/Docs/components/DocContent/mdx-mapping/index.tsx diff --git a/src/pages/Docs/components/DocContent/toc/index.tsx b/src/components/Docs/components/DocContent/toc/index.tsx similarity index 77% rename from src/pages/Docs/components/DocContent/toc/index.tsx rename to src/components/Docs/components/DocContent/toc/index.tsx index 7ba58d3e..7292316d 100644 --- a/src/pages/Docs/components/DocContent/toc/index.tsx +++ b/src/components/Docs/components/DocContent/toc/index.tsx @@ -4,16 +4,12 @@ import remarkParse from 'remark-parse'; import { visit } from 'unist-util-visit'; import { langType } from '@/utils/useLanguage'; import { useTranslation } from 'react-i18next'; -import { Link, useLocation, useParams } from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import clsx from 'clsx'; import { formatSlug } from '@/utils/docs'; +import { useDocContext } from '@/components/Docs/context'; -const modulesRawText = import.meta.glob('@/pages/Docs/md/*.mdx', { - query: '?raw', - import: 'default', -}) as Record Promise>; - -interface NavItem { +export interface NavItem { text: string; link: string; } @@ -21,7 +17,7 @@ interface NavItem { const processor = unified().use(remarkParse); // 目录生成规则参照 ../mdx-mapping/Heading.tsx -const computeTocs = async (raw: () => Promise) => { +export const computeTocs = async (raw: () => Promise) => { const navs: NavItem[] = []; try { const content = await raw(); @@ -64,21 +60,8 @@ const computeTocs = async (raw: () => Promise) => { return navs; }; -export const mdTocs = Object.entries(modulesRawText).reduce((acc, cur) => { - const [key, value] = cur; - const result = key.match(/md\/(.+)\.(zh|en|ja|ko)\.mdx$/); - if (!result) return acc; - const [, doc, lang] = result; - const tocs = computeTocs(value); - if (!acc[doc]) { - acc[doc] = { [lang]: tocs }; - } else { - acc[doc][lang] = tocs; - } - return acc; -}, {} as Record>>); - export const ToC = ({ doc, lang }: { doc: string; lang: langType }) => { + const { tocs } = useDocContext(); const [navs, setNavs] = useState([]); const { t } = useTranslation(); const { hash } = useLocation(); @@ -86,7 +69,7 @@ export const ToC = ({ doc, lang }: { doc: string; lang: langType }) => { useEffect(() => { const fn = async () => { try { - const p = mdTocs[doc][lang]; + const p = tocs[doc][lang]; if (!p) return; const data = await p; @@ -96,7 +79,7 @@ export const ToC = ({ doc, lang }: { doc: string; lang: langType }) => { } }; fn(); - }, [doc, lang]); + }, [doc, lang, tocs]); return (
diff --git a/src/pages/Docs/components/DocNotFound/index.less b/src/components/Docs/components/DocNotFound/index.less similarity index 100% rename from src/pages/Docs/components/DocNotFound/index.less rename to src/components/Docs/components/DocNotFound/index.less diff --git a/src/pages/Docs/components/DocNotFound/index.tsx b/src/components/Docs/components/DocNotFound/index.tsx similarity index 100% rename from src/pages/Docs/components/DocNotFound/index.tsx rename to src/components/Docs/components/DocNotFound/index.tsx diff --git a/src/pages/Docs/components/EmbedVideo/index.tsx b/src/components/Docs/components/EmbedVideo/index.tsx similarity index 100% rename from src/pages/Docs/components/EmbedVideo/index.tsx rename to src/components/Docs/components/EmbedVideo/index.tsx diff --git a/src/pages/Docs/components/HeaderLink/index.less b/src/components/Docs/components/HeaderLink/index.less similarity index 95% rename from src/pages/Docs/components/HeaderLink/index.less rename to src/components/Docs/components/HeaderLink/index.less index 7e101f51..c748b943 100644 --- a/src/pages/Docs/components/HeaderLink/index.less +++ b/src/components/Docs/components/HeaderLink/index.less @@ -3,7 +3,7 @@ align-items: center; gap: 8px; position: relative; - margin-left: -1rem; + margin-left: -24px; &:is(h1) { margin-bottom: 20px; } diff --git a/src/pages/Docs/components/HeaderLink/index.tsx b/src/components/Docs/components/HeaderLink/index.tsx similarity index 100% rename from src/pages/Docs/components/HeaderLink/index.tsx rename to src/components/Docs/components/HeaderLink/index.tsx diff --git a/src/pages/Docs/components/PlatformTag/index.tsx b/src/components/Docs/components/PlatformTag/index.tsx similarity index 100% rename from src/pages/Docs/components/PlatformTag/index.tsx rename to src/components/Docs/components/PlatformTag/index.tsx diff --git a/src/pages/Docs/components/DocMenus/index.less b/src/components/Docs/components/Sidebar/DocMenus/index.less similarity index 100% rename from src/pages/Docs/components/DocMenus/index.less rename to src/components/Docs/components/Sidebar/DocMenus/index.less diff --git a/src/components/Docs/components/Sidebar/DocMenus/index.tsx b/src/components/Docs/components/Sidebar/DocMenus/index.tsx new file mode 100644 index 00000000..9389d148 --- /dev/null +++ b/src/components/Docs/components/Sidebar/DocMenus/index.tsx @@ -0,0 +1,41 @@ +import { useLanguage } from '@/utils/useLanguage'; +import { useParams } from 'react-router-dom'; +import './index.less'; +import clsx from 'clsx'; +import { memo } from 'react'; +import { TransitionLink } from '@/components/Transition'; +import { useDocContext } from '@/components/Docs/context'; +import { isString } from '@huolala-tech/page-spy-base'; + +export const DocMenus = memo(() => { + const { sidebar } = useDocContext(); + const [lang] = useLanguage(); + const params = useParams(); + // route match rule "/docs/*" + const activeDoc = params['*'] || sidebar[0].children[0].doc; + + return ( +
+ {sidebar.map(({ group, children }, index) => { + return ( +
+

{isString(group) ? group : group[lang]}

+ {children.map(({ label, doc }) => { + return ( + + {typeof label === 'string' ? label : label[lang]} + + ); + })} +
+ ); + })} +
+ ); +}); diff --git a/src/pages/Docs/components/Sidebar/index.less b/src/components/Docs/components/Sidebar/index.less similarity index 100% rename from src/pages/Docs/components/Sidebar/index.less rename to src/components/Docs/components/Sidebar/index.less diff --git a/src/pages/Docs/components/Sidebar/index.tsx b/src/components/Docs/components/Sidebar/index.tsx similarity index 93% rename from src/pages/Docs/components/Sidebar/index.tsx rename to src/components/Docs/components/Sidebar/index.tsx index 3b88d9c2..1656a3db 100644 --- a/src/pages/Docs/components/Sidebar/index.tsx +++ b/src/components/Docs/components/Sidebar/index.tsx @@ -1,4 +1,4 @@ -import { DocMenus } from '../DocMenus'; +import { DocMenus } from './DocMenus'; import './index.less'; import { useRef } from 'react'; import { useClickAway } from 'ahooks'; diff --git a/src/components/Docs/context.tsx b/src/components/Docs/context.tsx new file mode 100644 index 00000000..77215f16 --- /dev/null +++ b/src/components/Docs/context.tsx @@ -0,0 +1,136 @@ +import { langType } from '@/utils/useLanguage'; +import React, { createContext, useContext, useMemo } from 'react'; +import { PropsWithChildren } from 'react'; +import { DocNotFound } from './components/DocNotFound'; +import { computeTocs, NavItem } from './components/DocContent/toc'; + +export const LOAD_DOC_EVENT = 'load-doc'; + +const lazyDocWithNotification = + (doc: string, value: () => Promise) => async () => { + window.dispatchEvent( + new CustomEvent(LOAD_DOC_EVENT, { + detail: { + doc, + loading: true, + }, + }), + ); + const result = await value(); + setTimeout(() => { + window.dispatchEvent( + new CustomEvent(LOAD_DOC_EVENT, { + detail: { + doc, + loading: false, + }, + }), + ); + }, 100); + return result; + }; + +export interface SidebarItem { + label: string | Record; + doc: string; +} + +interface SidebarGroup { + group: string | Record; + children: SidebarItem[]; +} + +interface DocContextInfo { + sidebar: SidebarGroup[]; + orderDocMenus: { + doc: string; + label: string | Record; + }[]; + tocs: Record>>; + getContent: ( + doc: string, + lang: langType, + ) => React.LazyExoticComponent | (() => JSX.Element); +} + +const defaultContext: DocContextInfo = { + sidebar: [], + orderDocMenus: [], + tocs: {}, + getContent() { + return DocNotFound; + }, +}; +const Context = createContext(defaultContext); + +export interface DocContextProps { + sidebar: SidebarGroup[]; + mdxComponents: Record Promise>; + mdRawContents: Record Promise>; +} + +export const DocContext = ({ + children, + sidebar, + mdxComponents, + mdRawContents, +}: PropsWithChildren) => { + const orderDocMenus = useMemo(() => { + return sidebar.reduce((acc, cur) => { + const { children, group } = cur; + const menus = children.map((item) => ({ ...item, group })); + acc.push(...menus); + return acc; + }, [] as SidebarItem[]); + }, [sidebar]); + + const value = useMemo(() => { + const components = Object.entries(mdxComponents).reduce((acc, cur) => { + const [key, value] = cur; + // 文档必须在 "md/" 目录下 + const result = key.match(/md\/(.+)\.(zh|en|ja|ko)\.mdx$/); + if (!result) return acc; + const [, doc, lang] = result; + const lazyDoc = lazyDocWithNotification(doc, value); + if (!acc[doc]) { + acc[doc] = { [lang]: React.lazy(lazyDoc) }; + } else { + acc[doc][lang] = React.lazy(lazyDoc); + } + return acc; + }, {} as Record | (() => JSX.Element)>>); + + const tocs = Object.entries(mdRawContents).reduce((acc, cur) => { + const [key, value] = cur; + const result = key.match(/md\/(.+)\.(zh|en|ja|ko)\.mdx$/); + if (!result) return acc; + const [, doc, lang] = result; + const tocs = computeTocs(value); + if (!acc[doc]) { + acc[doc] = { [lang]: tocs }; + } else { + acc[doc][lang] = tocs; + } + return acc; + }, {} as Record>>); + + return { + sidebar, + orderDocMenus, + tocs, + getContent(doc, lang) { + const docData = components[doc][lang]; + if (!docData) { + return DocNotFound; + } + return docData; + }, + }; + }, [mdRawContents, mdxComponents, orderDocMenus, sidebar]); + + return {children}; +}; + +export const useDocContext = () => { + return useContext(Context); +}; diff --git a/src/pages/Docs/index.less b/src/components/Docs/index.less similarity index 100% rename from src/pages/Docs/index.less rename to src/components/Docs/index.less diff --git a/src/components/Docs/index.tsx b/src/components/Docs/index.tsx new file mode 100644 index 00000000..3ff91585 --- /dev/null +++ b/src/components/Docs/index.tsx @@ -0,0 +1,24 @@ +import './index.less'; +import { DocContent } from './components/DocContent'; +import { Sidebar } from './components/Sidebar'; +import { Suspense } from 'react'; +import { LoadingFallback } from '@/components/LoadingFallback'; +import { TransitionContextWrapper } from '@/components/Transition'; +import { DocContext, DocContextProps } from './context'; + +const Docs = (props: DocContextProps) => { + return ( + +
+ }> + + + + + +
+
+ ); +}; + +export default Docs; diff --git a/src/components/LogReplayer/InvalidObjectURL/index.less b/src/components/LogReplayer/InvalidObjectURL/index.less deleted file mode 100644 index bc114260..00000000 --- a/src/components/LogReplayer/InvalidObjectURL/index.less +++ /dev/null @@ -1,14 +0,0 @@ -.invalid-object-url { - display: flex; - flex-direction: column; - align-items: center; - gap: 24px; - height: 100%; - margin-top: 80px; - font-size: 16px; - .blob-url { - padding: 6px 12px; - border-radius: 4px; - background-color: fade(@primary-color, 15%); - } -} diff --git a/src/components/LogReplayer/InvalidObjectURL/index.tsx b/src/components/LogReplayer/InvalidObjectURL/index.tsx deleted file mode 100644 index 10b20531..00000000 --- a/src/components/LogReplayer/InvalidObjectURL/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import useSearch from '@/utils/useSearch'; -import { Button, Empty, Space } from 'antd'; -import { Trans, useTranslation } from 'react-i18next'; -import './index.less'; -import { Link } from 'react-router-dom'; -import { ArrowLeftOutlined, ReloadOutlined } from '@ant-design/icons'; - -export const InvalidObjectURL = ({ url }: { url: string }) => { - const { t } = useTranslation(); - - return ( -
- - {!url ? ( -

{t('replay.invalid-params')}

- ) : ( - -

- The parameter reading from the URL failed, the object maybe cleared: -

- {url} -
- )} - - - - -
- ); -}; diff --git a/src/components/LogReplayer/index.tsx b/src/components/LogReplayer/index.tsx index d1c74811..b25dd617 100644 --- a/src/components/LogReplayer/index.tsx +++ b/src/components/LogReplayer/index.tsx @@ -1,4 +1,4 @@ -import { message, Row, Col, Space, Empty, Flex } from 'antd'; +import { message, Row, Col, Empty, Flex } from 'antd'; import './index.less'; import { useRequest } from 'ahooks'; import { LoadingFallback } from '@/components/LoadingFallback'; @@ -8,10 +8,6 @@ import { HarborDataItem, useReplayStore } from '@/store/replay'; import { RRWebPlayer } from './RRWebPlayer'; import { PluginPanel } from './PluginPanel'; import '@huolala-tech/react-json-view/dist/style.css'; -import { useTranslation } from 'react-i18next'; -import { InvalidObjectURL } from './InvalidObjectURL'; -import { Link } from 'react-router-dom'; -import LeftArrowSvg from '@/assets/image/left-arrow.svg?react'; import { ReactNode, RefCallback, @@ -34,7 +30,6 @@ interface Props { } export const LogReplayer = ({ url, backSlot = null }: Props) => { - const { t } = useTranslation(); const [allRRwebEvent, setAllData, setIsExpand] = useReplayStore( useShallow((state) => [ state.allRRwebEvent, @@ -42,11 +37,7 @@ export const LogReplayer = ({ url, backSlot = null }: Props) => { state.setIsExpand, ]), ); - const { - loading, - error, - run: requestLog, - } = useRequest( + const { loading, run: requestLog } = useRequest( async () => { const res = await (await fetch(url)).json(); const result = res.map((i: any) => { @@ -68,6 +59,9 @@ export const LogReplayer = ({ url, backSlot = null }: Props) => { }, { manual: true, + onError(e) { + message.error(e.message); + }, }, ); useEffect(() => { @@ -134,24 +128,10 @@ export const LogReplayer = ({ url, backSlot = null }: Props) => { return ; } - if (error) { - message.error(error.message); - return ; - } - return (
- - {backSlot || ( - - - - - {t('replay.title')} - - )} - + {backSlot} diff --git a/src/components/SelectLogButton/index.tsx b/src/components/SelectLogButton/index.tsx new file mode 100644 index 00000000..759ba325 --- /dev/null +++ b/src/components/SelectLogButton/index.tsx @@ -0,0 +1,41 @@ +import Icon from '@ant-design/icons'; +import { UploadProps, Upload, Button, ButtonProps } from 'antd'; +import { useTranslation } from 'react-i18next'; +import PaperClipSvg from '@/assets/image/paper-clip.svg?react'; + +interface Props { + onSelect: (url: string) => void; + uploadProps?: UploadProps; + buttonProps?: ButtonProps; +} + +export const SelectLogButton = ({ + onSelect, + uploadProps = {}, + buttonProps = {}, +}: Props) => { + const { t } = useTranslation(); + + const uploadCustomRequest: UploadProps['customRequest'] = (file) => { + const url = URL.createObjectURL(file.file as File); + onSelect(url); + return null; + }; + return ( + null} + {...uploadProps} + > + + + ); +}; diff --git a/src/pages/Docs/components/DocContent/content/index.ts b/src/pages/Docs/components/DocContent/content/index.ts deleted file mode 100644 index 4caa9658..00000000 --- a/src/pages/Docs/components/DocContent/content/index.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { langType } from '@/utils/useLanguage'; -import React from 'react'; -import { DocNotFound } from '../../DocNotFound'; - -const modules = import.meta.glob('@/pages/Docs/md/*.mdx') as Record< - string, - () => Promise ->; - -export const LOAD_DOC_EVENT = 'load-doc'; - -const lazyDocWithNotification = - (doc: string, value: () => Promise) => async () => { - window.dispatchEvent( - new CustomEvent(LOAD_DOC_EVENT, { - detail: { - doc, - loading: true, - }, - }), - ); - const result = await value(); - setTimeout(() => { - window.dispatchEvent( - new CustomEvent(LOAD_DOC_EVENT, { - detail: { - doc, - loading: false, - }, - }), - ); - }, 100); - return result; - }; - -const mdComponents = Object.entries(modules).reduce((acc, cur) => { - const [key, value] = cur; - const result = key.match(/md\/(.+)\.(zh|en|ja|ko)\.mdx$/); - if (!result) return acc; - const [, doc, lang] = result; - const lazyDoc = lazyDocWithNotification(doc, value); - if (!acc[doc]) { - acc[doc] = { [lang]: React.lazy(lazyDoc) }; - } else { - acc[doc][lang] = React.lazy(lazyDoc); - } - return acc; -}, {} as Record>>); - -export const getDocContent = (doc: string, lang: langType) => { - const docData = mdComponents[doc][lang]; - if (!docData) { - return DocNotFound; - } - return docData; -}; diff --git a/src/pages/Docs/index.tsx b/src/pages/Docs/index.tsx deleted file mode 100644 index aa4c2436..00000000 --- a/src/pages/Docs/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import './index.less'; -import { DocContent } from './components/DocContent'; -import { Sidebar } from './components/Sidebar'; -import { Suspense } from 'react'; -import { LoadingFallback } from '@/components/LoadingFallback'; -import { TransitionContextWrapper } from '@/components/Transition'; - -const Docs = () => { - return ( -
- }> - - - - - -
- ); -}; - -export default Docs; diff --git a/src/pages/Home/components/Footer/index.less b/src/pages/Home/components/Footer/index.less deleted file mode 100644 index 7e9511cb..00000000 --- a/src/pages/Home/components/Footer/index.less +++ /dev/null @@ -1,6 +0,0 @@ -.footer { - height: 120px; - background-color: #fff; - color: @text-secondary; - line-height: 1.6; -} diff --git a/src/pages/Home/components/Introduction/components/Block-3/index.less b/src/pages/Home/components/Introduction/components/Block-3/index.less deleted file mode 100644 index 7335bef7..00000000 --- a/src/pages/Home/components/Introduction/components/Block-3/index.less +++ /dev/null @@ -1,7 +0,0 @@ -.welcome-use { - display: flex; - gap: 12px; - flex-direction: column; - align-items: center; - justify-content: center; -} diff --git a/src/pages/Home/components/Introduction/components/Block-3/index.tsx b/src/pages/Home/components/Introduction/components/Block-3/index.tsx deleted file mode 100644 index aac15b2e..00000000 --- a/src/pages/Home/components/Introduction/components/Block-3/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Button, Typography } from 'antd'; -import logoImg from '@/assets/image/logo.svg'; -import './index.less'; -import { useTranslation } from 'react-i18next'; -import { Link } from 'react-router-dom'; - -export const IntroBlock3 = () => { - const { t } = useTranslation(); - - return ( -
-
- LOGO - {t('intro.welcome')} - - - -
-
- ); -}; diff --git a/src/pages/Layouts/Logo/index.less b/src/pages/Layouts/Logo/index.less new file mode 100644 index 00000000..f44952ba --- /dev/null +++ b/src/pages/Layouts/Logo/index.less @@ -0,0 +1,30 @@ +.logo { + height: var(--header-height); + display: flex; + align-items: center; + gap: 20px; + > a { + height: 100%; + display: inline-flex; + place-items: center; + color: #000; + } + .logo-icon { + font-size: 48px; + } + .logo-name { + margin-left: 12px; + margin-bottom: 0; + } + .page-spy-version { + display: inline-block; + transform: translate(2px, -10px); + font-size: 12px; + } + .producthunt-brand { + height: 36px; + @media screen and (max-width: 420px) { + display: none; + } + } +} diff --git a/src/pages/Layouts/Logo/index.tsx b/src/pages/Layouts/Logo/index.tsx new file mode 100644 index 00000000..8eb0c14f --- /dev/null +++ b/src/pages/Layouts/Logo/index.tsx @@ -0,0 +1,40 @@ +import { isClient } from '@/utils/constants'; +import { version } from '../../../../package.json'; +import { Link } from 'react-router-dom'; +import { useMemo } from 'react'; +import LogoSvg from '@/assets/image/logo.svg?react'; +import OSpySvg from '@/assets/image/o-spy.svg?react'; +import Icon from '@ant-design/icons'; +import Title from 'antd/es/typography/Title'; +import './index.less'; +import { useWhere } from '@/utils/useWhere'; + +export const Logo = () => { + const where = useWhere(); + + const config = useMemo(() => { + const { isOSpy } = where; + if (isOSpy) { + return { + image: OSpySvg, + name: 'O-Spy', + link: '/o-spy', + }; + } + return { + image: LogoSvg, + name: 'PageSpy', + link: '/', + }; + }, [where]); + + return ( + + + + {config.name} + {isClient && <span className="page-spy-version">v{version}</span>} + + + ); +}; diff --git a/src/pages/Layouts/NavMenu/index.tsx b/src/pages/Layouts/NavMenu/index.tsx index 0daf87b7..8aa9244e 100644 --- a/src/pages/Layouts/NavMenu/index.tsx +++ b/src/pages/Layouts/NavMenu/index.tsx @@ -16,6 +16,7 @@ import './index.less'; import { useDarkTheme } from '@/utils/useDarkTheme'; import { CSSTransition } from 'react-transition-group'; import { createPortal } from 'react-dom'; +import { useWhere } from '@/utils/useWhere'; const ALL_LANGS: MenuProps['items'] = [ { @@ -48,6 +49,7 @@ const navDropdownConfig = { export const NavMenuOnPc = () => { const isDark = useDarkTheme(); const [lang, setLang] = useLanguage(); + const { isOSpy } = useWhere(); const { t } = useTranslation(); const langMenus = useMemo(() => { return ALL_LANGS.filter((i) => i?.key !== lang); @@ -59,27 +61,15 @@ export const NavMenuOnPc = () => { 'is-dark': isDark, })} > - {/* Replay labs */} - {isDoc && ( - <> - - - - {t('common.replay-lab')} - - - - - )} {/* Docs */} - + {t('common.doc')} - {isClient && ( + {isClient && !isOSpy && (
{ target="_blank" className="menu-item" > - - - GitHub - +
); }; export const NavMenuOnMobile = () => { + const { isOSpy } = useWhere(); const [lang, setLang] = useLanguage(); const { t } = useTranslation(); const isDark = useDarkTheme(); @@ -207,7 +198,7 @@ export const NavMenuOnMobile = () => { > {/* Docs */} { setExpand(false); @@ -218,11 +209,11 @@ export const NavMenuOnMobile = () => { {t('common.doc')} - {isClient && ( + {isClient && !isOSpy && ( <> {/* Connections */} { setExpand(false); @@ -253,10 +244,10 @@ export const NavMenuOnMobile = () => { {/* GitHub */}
diff --git a/src/pages/Layouts/index.less b/src/pages/Layouts/index.less index c1a448ee..cde7e097 100644 --- a/src/pages/Layouts/index.less +++ b/src/pages/Layouts/index.less @@ -20,42 +20,13 @@ &.is-dark { box-shadow: @dark-box-shadow; background-color: var(--dark-background); + a, .logo-name { - color: #ddd; + color: #fff; word-break: keep-all; } } } - .logo { - height: var(--header-height); - display: flex; - align-items: center; - gap: 32px; - > a { - height: 100%; - display: inline-flex; - place-items: center; - } - &-icon { - height: 46px; - } - &-name { - margin-left: 12px; - margin-bottom: 0; - color: #000; - } - .page-spy-version { - display: inline-block; - transform: translate(2px, -10px); - font-size: 12px; - } - .producthunt-brand { - height: 36px; - @media screen and (max-width: 420px) { - display: none; - } - } - } } @media screen and (max-width: 768px) { diff --git a/src/pages/Layouts/index.tsx b/src/pages/Layouts/index.tsx index 21644bf5..6ef7bc7a 100644 --- a/src/pages/Layouts/index.tsx +++ b/src/pages/Layouts/index.tsx @@ -1,18 +1,15 @@ -import { Col, Layout, Row, Typography } from 'antd'; -import { Link, Outlet } from 'react-router-dom'; -import LogoSvg from '@/assets/image/logo.svg?react'; +import { Col, Layout, Row } from 'antd'; +import { Outlet } from 'react-router-dom'; import './index.less'; import clsx from 'clsx'; import { Suspense } from 'react'; import { LoadingFallback } from '@/components/LoadingFallback'; import { NavMenuOnPc, NavMenuOnMobile } from './NavMenu'; import { useDarkTheme } from '@/utils/useDarkTheme'; -import { version } from '../../../package.json'; -import { isClient, isDoc } from '@/utils/constants'; -import phSvg from '@/assets/image/producthunt.svg'; +import { isDoc } from '@/utils/constants'; +import { Logo } from './Logo'; const { Header, Content } = Layout; -const { Title } = Typography; export const Layouts = () => { const isDark = useDarkTheme(); @@ -27,27 +24,16 @@ export const Layouts = () => { >
- - - - PageSpy - {isClient && ( - <span className="page-spy-version">v{version}</span> - )} - - + {isDoc && ( { - (e.target as HTMLImageElement).src = phSvg; - }} - alt="PageSpy | Product Hunt" + src="https://trendshift.io/api/badge/repositories/5407" + alt="HuolalaTech/page-spy-web | Trendshift" height="36" /> diff --git a/src/pages/LogList/SelectLogButton/index.tsx b/src/pages/LogList/ExtraLogButton/index.tsx similarity index 81% rename from src/pages/LogList/SelectLogButton/index.tsx rename to src/pages/LogList/ExtraLogButton/index.tsx index 0ebb4fa9..cf24c171 100644 --- a/src/pages/LogList/SelectLogButton/index.tsx +++ b/src/pages/LogList/ExtraLogButton/index.tsx @@ -1,15 +1,7 @@ +import { SelectLogButton } from '@/components/SelectLogButton'; import { usePopupRef, withPopup } from '@/utils/withPopup'; -import { PaperClipOutlined, UploadOutlined } from '@ant-design/icons'; -import { - Button, - Form, - Input, - Modal, - Radio, - Space, - Upload, - message, -} from 'antd'; +import { PaperClipOutlined } from '@ant-design/icons'; +import { Button, Form, Input, Modal, Radio, Space, message } from 'antd'; import { t } from 'i18next'; import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -86,23 +78,17 @@ const SelectResource = withPopup(({ visible, resolve }) => { ); } return ( - { - const url = URL.createObjectURL(file.file as File); - const replayURL = getReplayUrl(url); + { + const replayURL = new URL( + `#/replay?url=${url}`, + window.location.href, + ); setTimeout(() => { window.open(replayURL); }, 50); - return null; }} - itemRender={() => null} - > - - + /> ); }} @@ -111,7 +97,7 @@ const SelectResource = withPopup(({ visible, resolve }) => { ); }); -export const SelectLogButton = () => { +export const ExtraLogButton = () => { const popRef = usePopupRef(); return ( diff --git a/src/pages/LogList/index.tsx b/src/pages/LogList/index.tsx index c635d04e..c1c3dda0 100644 --- a/src/pages/LogList/index.tsx +++ b/src/pages/LogList/index.tsx @@ -20,7 +20,7 @@ import { import { Trans, useTranslation } from 'react-i18next'; import './index.less'; import { Link } from 'react-router-dom'; -import { SelectLogButton } from './SelectLogButton'; +import { ExtraLogButton } from './ExtraLogButton'; import dayjs, { type Dayjs } from 'dayjs'; import request from '@/apis/request'; import { ComponentType, useCallback, useRef, useState } from 'react'; @@ -225,7 +225,7 @@ const LogList = () => { > {t('common.reset')} - + diff --git a/src/pages/Home/components/Banner/index.less b/src/pages/Main/components/Banner/index.less similarity index 98% rename from src/pages/Home/components/Banner/index.less rename to src/pages/Main/components/Banner/index.less index 41294f27..fb210fcb 100644 --- a/src/pages/Home/components/Banner/index.less +++ b/src/pages/Main/components/Banner/index.less @@ -83,7 +83,7 @@ @media screen and (max-width: 768px) { .banner { .slogan > p:nth-child(1) { - font-size: 48px; + font-size: 56px; } } } diff --git a/src/pages/Home/components/Banner/index.tsx b/src/pages/Main/components/Banner/index.tsx similarity index 84% rename from src/pages/Home/components/Banner/index.tsx rename to src/pages/Main/components/Banner/index.tsx index 279a997b..6645eb27 100644 --- a/src/pages/Home/components/Banner/index.tsx +++ b/src/pages/Main/components/Banner/index.tsx @@ -1,9 +1,9 @@ -import { Row, Col, Button } from 'antd'; +import { Row, Col, Button, Badge } from 'antd'; import './index.less'; import { Trans, useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import QuickStartSvg from '@/assets/image/quick-start.svg?react'; -import TakeTrySvg from '@/assets/image/take-try.svg?react'; +import OSpySvg from '@/assets/image/o-spy.svg?react'; import Icon from '@ant-design/icons'; const Waves = () => { @@ -87,7 +87,7 @@ export const Banner = () => {

{t('banner.desc')}

- + - - - + + + + +
diff --git a/src/pages/Main/components/Footer/index.less b/src/pages/Main/components/Footer/index.less new file mode 100644 index 00000000..7d81190a --- /dev/null +++ b/src/pages/Main/components/Footer/index.less @@ -0,0 +1,13 @@ +.footer { + height: 120px; + background-color: #111; + border-top: 1px solid rgba(255, 255, 255, 0.1); + color: @text-secondary; + line-height: 1.6; + a { + color: #ccc; + text-decoration: underline; + text-underline-offset: 4px; + font-weight: 500; + } +} diff --git a/src/pages/Home/components/Footer/index.tsx b/src/pages/Main/components/Footer/index.tsx similarity index 100% rename from src/pages/Home/components/Footer/index.tsx rename to src/pages/Main/components/Footer/index.tsx diff --git a/src/pages/Home/components/Introduction/components/Block-1/index.tsx b/src/pages/Main/components/Introduction/components/Block-1/index.tsx similarity index 100% rename from src/pages/Home/components/Introduction/components/Block-1/index.tsx rename to src/pages/Main/components/Introduction/components/Block-1/index.tsx diff --git a/src/pages/Home/components/Introduction/components/Block-2/index.less b/src/pages/Main/components/Introduction/components/Block-2/index.less similarity index 65% rename from src/pages/Home/components/Introduction/components/Block-2/index.less rename to src/pages/Main/components/Introduction/components/Block-2/index.less index a687fba0..b031b797 100644 --- a/src/pages/Home/components/Introduction/components/Block-2/index.less +++ b/src/pages/Main/components/Introduction/components/Block-2/index.less @@ -49,23 +49,25 @@ } } -.fade-enter-active, -.fade-exit-active { - transition: all ease-out 300ms; -} -.fade-enter { - opacity: 0; - transform: translate3d(0, 100px, 0); -} -.fade-enter-active { - opacity: 1; - transform: translate3d(0px, 0, 0); -} -.fade-exit { - opacity: 1; - transform: translate3d(0px, 0, 0); -} -.fade-exit-active { - opacity: 0; - transform: translate3d(0, -50px, 0); +.block-2 { + .fade-enter-active, + .fade-exit-active { + transition: all ease-out 300ms; + } + .fade-enter { + opacity: 0; + transform: translate3d(0, 100px, 0); + } + .fade-enter-active { + opacity: 1; + transform: translate3d(0px, 0, 0); + } + .fade-exit { + opacity: 1; + transform: translate3d(0px, 0, 0); + } + .fade-exit-active { + opacity: 0; + transform: translate3d(0, -50px, 0); + } } diff --git a/src/pages/Home/components/Introduction/components/Block-2/index.tsx b/src/pages/Main/components/Introduction/components/Block-2/index.tsx similarity index 100% rename from src/pages/Home/components/Introduction/components/Block-2/index.tsx rename to src/pages/Main/components/Introduction/components/Block-2/index.tsx diff --git a/src/pages/Main/components/Introduction/components/Block-3/comments.json b/src/pages/Main/components/Introduction/components/Block-3/comments.json new file mode 100644 index 00000000..45f260f9 --- /dev/null +++ b/src/pages/Main/components/Introduction/components/Block-3/comments.json @@ -0,0 +1,74 @@ +[ + { + "html_url": "https://github.com/HuolalaTech/page-spy-web/issues/65#issuecomment-2065824476", + "user": { + "login": "HyperClockUp", + "avatar_url": "https://avatars.githubusercontent.com/u/36909196?v=4", + "html_url": "https://github.com/HyperClockUp" + }, + "body": "我们有很多页面嵌在 pad 端或者硬件内部,调试和复现现场一直是个难题,但使用了 PageSpy 之后,省去了抓包流程,也不需要到设备现场就能轻松定位问题。而且开发团队更新和支持响应速度都非常快,也给我们使用者减少了维护的顾虑,大力支持 PageSpy,希望项目能越来越好,成为前端必备的调试工具!" + }, + { + "html_url": "https://github.com/HuolalaTech/page-spy-web/issues/65#issuecomment-2065822351", + "user": { + "login": "SamWeichangyue", + "avatar_url": "https://avatars.githubusercontent.com/u/30724369?v=4", + "html_url": "https://github.com/SamWeichangyue" + }, + "body": "希望 PageSpy 越来越好,版本迭代清晰,也很迅速,刚要二开,结果发现功能上线嘞,这个项目带给我很多灵感" + }, + { + "html_url": "https://github.com/HuolalaTech/page-spy-web/issues/65#issuecomment-2065828569", + "user": { + "login": "Jumping000", + "avatar_url": "https://avatars.githubusercontent.com/u/34015788?v=4", + "html_url": "https://github.com/Jumping000" + }, + "body": "目前用在个人外包项目,使用方便,支持的技术栈多,项目问题解决及时,快速定位问题,更新速度。很大程度优化开发扯皮环节,带来了便利,减少重复的工作,YYDS!" + }, + { + "html_url": "https://github.com/HuolalaTech/page-spy-web/issues/65#issuecomment-2024627757", + "user": { + "login": "wuchaoxin", + "avatar_url": "https://avatars.githubusercontent.com/u/52433086?v=4", + "html_url": "https://github.com/wuchaoxin" + }, + "body": "使用 PageSpy 工具后,我们团队的前端开发人员和测试人员都对其赞不绝口。它极大地简化了我们的调试流程,不再需要在每一台手机上安装不同的证书来进行代理调试。这一工具的出现,为我们节省了大量宝贵的时间,让调试变得更加高效。从此,我们可以专注于开发和测试工作,而不必为繁琐的调试步骤而烦恼。这确实是一个非常棒的工具,为我们的团队带来了巨大的便利和效率提升。" + }, + { + "html_url": "https://github.com/HuolalaTech/page-spy-web/issues/65#issuecomment-2065842345", + "user": { + "login": "zy1281539626", + "avatar_url": "https://avatars.githubusercontent.com/u/24648935?v=4", + "html_url": "https://github.com/zy1281539626" + }, + "body": "PageSpy 确实能很大的方便开发调试,PC 端看日志是真的爽,直接截图给后端,再也不用拍照手机报错再上传了。" + }, + { + "html_url": "https://github.com/HuolalaTech/page-spy-web/issues/65#issuecomment-2230426027", + "user": { + "login": "Niannianyouxinxin", + "avatar_url": "https://avatars.githubusercontent.com/u/64892018?v=4", + "html_url": "https://github.com/Niannianyouxinxin" + }, + "body": "没用过这么方便的,太方便了,多种部署方式部署非常方便,非常好用可以复现当时的场景和日志,人非常 nice,群里问题知无不言言无不尽,人帅技术好" + }, + { + "html_url": "https://github.com/HuolalaTech/page-spy-web/issues/65#issuecomment-2065849082", + "user": { + "login": "wencai123", + "avatar_url": "https://avatars.githubusercontent.com/u/82213523?v=4", + "html_url": "https://github.com/wencai123" + }, + "body": "PageSpy 的出现,解决了 H5 调试的数据查看、数据 copy、异地调试等痛点问题,支持离线上传日志、PC 端调试 H5,这在传统的 H5 调试工具库里是无法做到的,功能非常强大,极大的提高了调试效率,希望 PageSpy 越来越好,成为引领新一代 H5 调试的工具!" + }, + { + "html_url": "https://github.com/HuolalaTech/page-spy-web/issues/65#issuecomment-2227990210", + "user": { + "login": "tiantangmowang", + "avatar_url": "https://avatars.githubusercontent.com/u/11189379?v=4", + "html_url": "https://github.com/tiantangmowang" + }, + "body": "近期真正有需求价值的开源项目,解决了公司在排查复杂客户环境问题上的一大痛点。这个工具大大节省了我们的时间,让调试变得更加高效。我们衷心希望 PageSpy 能不断改进,成为引领新一代 H5 调试的标杆工具!" + } +] diff --git a/src/pages/Main/components/Introduction/components/Block-3/index.less b/src/pages/Main/components/Introduction/components/Block-3/index.less new file mode 100644 index 00000000..ced64f43 --- /dev/null +++ b/src/pages/Main/components/Introduction/components/Block-3/index.less @@ -0,0 +1,48 @@ +.block-3 { + background-color: #111; +} +.community-comments { + color: #dfdfdf; + h1 { + font-size: 50px; + } + h3 { + font-size: 18px; + margin-block: 12px 48px; + } + .comments-container { + width: 95%; + margin: 0 auto; + + > div { + break-inside: avoid; + padding: 24px; + border-radius: 12px; + background-color: #fff; + margin-bottom: 24px; + background-color: #161616; + border: 1px solid rgba(252, 252, 252, 0.1); + color: #bfbfbf; + .username { + color: #eee; + font-size: 16px; + } + .comments-item { + text-align: left; + font-size: 15px; + letter-spacing: 0.5px; + line-height: 1.5; + } + } + + @media screen and (min-width: 768px) { + column-count: 2; + column-gap: 24px; + } + + @media screen and (min-width: 992px) { + width: 85%; + column-count: 3; + } + } +} diff --git a/src/pages/Main/components/Introduction/components/Block-3/index.tsx b/src/pages/Main/components/Introduction/components/Block-3/index.tsx new file mode 100644 index 00000000..f5e2943a --- /dev/null +++ b/src/pages/Main/components/Introduction/components/Block-3/index.tsx @@ -0,0 +1,42 @@ +import './index.less'; +import { Trans, useTranslation } from 'react-i18next'; +import data from './comments.json'; +import { Avatar, Flex, theme } from 'antd'; + +const { useToken } = theme; + +export const IntroBlock3 = () => { + const { t } = useTranslation(); + + return ( +
+
+ +

深受社区青睐

+

+ 不要只看我们说 +
+ 来听听 PageSpy 社区中真实用户的声音 +

+
+
+ {data.map((i) => ( + + + + {i.user.login.slice(0, 1)} + + {i.user.login} + +

{i.body}

+
+ ))} +
+
+
+ ); +}; diff --git a/src/pages/Home/components/Introduction/index.less b/src/pages/Main/components/Introduction/index.less similarity index 88% rename from src/pages/Home/components/Introduction/index.less rename to src/pages/Main/components/Introduction/index.less index 9adb7385..e1c89150 100644 --- a/src/pages/Home/components/Introduction/index.less +++ b/src/pages/Main/components/Introduction/index.less @@ -7,10 +7,6 @@ align-items: flex-start; gap: 80px; padding-block: 80px; - &.block-3 { - background-color: rgba(@primary-color, 0.06); - align-items: center; - } } .small-title { margin-top: 100px; diff --git a/src/pages/Home/components/Introduction/index.tsx b/src/pages/Main/components/Introduction/index.tsx similarity index 100% rename from src/pages/Home/components/Introduction/index.tsx rename to src/pages/Main/components/Introduction/index.tsx diff --git a/src/pages/Home/index.tsx b/src/pages/Main/index.tsx similarity index 90% rename from src/pages/Home/index.tsx rename to src/pages/Main/index.tsx index d841b5dd..e95448fe 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Main/index.tsx @@ -2,7 +2,7 @@ import { Banner } from './components/Banner'; import { Introduction } from './components/Introduction'; import { Footer } from './components/Footer'; -export const Home = () => { +export const Main = () => { return (
diff --git a/src/pages/Docs/components/DocMenus/index.tsx b/src/pages/MainDocs/index.tsx similarity index 69% rename from src/pages/Docs/components/DocMenus/index.tsx rename to src/pages/MainDocs/index.tsx index 2dea5b6b..90c0250b 100644 --- a/src/pages/Docs/components/DocMenus/index.tsx +++ b/src/pages/MainDocs/index.tsx @@ -1,11 +1,6 @@ -import { langType, useLanguage } from '@/utils/useLanguage'; -import { useParams } from 'react-router-dom'; -import './index.less'; -import clsx from 'clsx'; -import { memo } from 'react'; -import { TransitionLink } from '@/components/Transition'; +import Docs from '@/components/Docs'; -export const DOC_MENUS = [ +const sidebar = [ { group: { zh: '指引', @@ -173,47 +168,14 @@ export const DOC_MENUS = [ }, ]; -export type OrderDocMenus = { - doc: string; - label: string | Record; - group: Record; -}[]; +const mdxComponents = import.meta.glob('./md/*.mdx'); +const mdRawContents = import.meta.glob('./md/*.mdx', { + import: 'default', + query: '?raw', +}) as Record Promise>; -export const ORDER_DOC_MENUS = DOC_MENUS.reduce((acc, cur) => { - const { children, group } = cur; - const menus = children.map((item) => ({ ...item, group })); - acc.push(...menus); - return acc; -}, [] as OrderDocMenus); +const MainDocs = () => { + return ; +}; -export const DocMenus = memo(() => { - const [lang] = useLanguage(); - const params = useParams(); - // route match rule "/docs/*" - const docInUrl = params['*'] || DOC_MENUS[0].children[0].doc; - - return ( -
- {DOC_MENUS.map(({ group, children }, index) => { - return ( -
-

{group[lang]}

- {children.map(({ label, doc }) => { - return ( - - {typeof label === 'string' ? label : label[lang]} - - ); - })} -
- ); - })} -
- ); -}); +export default MainDocs; diff --git a/src/pages/Docs/md/api.en.mdx b/src/pages/MainDocs/md/api.en.mdx similarity index 95% rename from src/pages/Docs/md/api.en.mdx rename to src/pages/MainDocs/md/api.en.mdx index 5d15eecf..341f33f9 100644 --- a/src/pages/Docs/md/api.en.mdx +++ b/src/pages/MainDocs/md/api.en.mdx @@ -1,4 +1,4 @@ -import PlatformTag from '../components/PlatformTag'; +import PlatformTag from '@/components/Docs/components/PlatformTag'; > Some features are only available on specific platforms. We will differentiate them using the following tags: > diff --git a/src/pages/Docs/md/api.zh.mdx b/src/pages/MainDocs/md/api.zh.mdx similarity index 95% rename from src/pages/Docs/md/api.zh.mdx rename to src/pages/MainDocs/md/api.zh.mdx index 5a3e2f6a..c6550e23 100644 --- a/src/pages/Docs/md/api.zh.mdx +++ b/src/pages/MainDocs/md/api.zh.mdx @@ -1,4 +1,4 @@ -import PlatformTag from '../components/PlatformTag'; +import PlatformTag from '@/components/Docs/components/PlatformTag'; > 某些特性仅支持在特定平台中可用,对此我们将通过下方标识以区分: > diff --git a/src/pages/Docs/md/browser.en.mdx b/src/pages/MainDocs/md/browser.en.mdx similarity index 100% rename from src/pages/Docs/md/browser.en.mdx rename to src/pages/MainDocs/md/browser.en.mdx diff --git a/src/pages/Docs/md/browser.zh.mdx b/src/pages/MainDocs/md/browser.zh.mdx similarity index 100% rename from src/pages/Docs/md/browser.zh.mdx rename to src/pages/MainDocs/md/browser.zh.mdx diff --git a/src/pages/Docs/md/changelog.en.mdx b/src/pages/MainDocs/md/changelog.en.mdx similarity index 100% rename from src/pages/Docs/md/changelog.en.mdx rename to src/pages/MainDocs/md/changelog.en.mdx diff --git a/src/pages/Docs/md/changelog.zh.mdx b/src/pages/MainDocs/md/changelog.zh.mdx similarity index 100% rename from src/pages/Docs/md/changelog.zh.mdx rename to src/pages/MainDocs/md/changelog.zh.mdx diff --git a/src/pages/Docs/md/data-harbor.en.mdx b/src/pages/MainDocs/md/data-harbor.en.mdx similarity index 100% rename from src/pages/Docs/md/data-harbor.en.mdx rename to src/pages/MainDocs/md/data-harbor.en.mdx diff --git a/src/pages/Docs/md/data-harbor.zh.mdx b/src/pages/MainDocs/md/data-harbor.zh.mdx similarity index 100% rename from src/pages/Docs/md/data-harbor.zh.mdx rename to src/pages/MainDocs/md/data-harbor.zh.mdx diff --git a/src/pages/Docs/md/deploy-guide.en.mdx b/src/pages/MainDocs/md/deploy-guide.en.mdx similarity index 100% rename from src/pages/Docs/md/deploy-guide.en.mdx rename to src/pages/MainDocs/md/deploy-guide.en.mdx diff --git a/src/pages/Docs/md/deploy-guide.zh.mdx b/src/pages/MainDocs/md/deploy-guide.zh.mdx similarity index 100% rename from src/pages/Docs/md/deploy-guide.zh.mdx rename to src/pages/MainDocs/md/deploy-guide.zh.mdx diff --git a/src/pages/Docs/md/deploy-with-baota.en.mdx b/src/pages/MainDocs/md/deploy-with-baota.en.mdx similarity index 91% rename from src/pages/Docs/md/deploy-with-baota.en.mdx rename to src/pages/MainDocs/md/deploy-with-baota.en.mdx index 47434f10..5568ca83 100644 --- a/src/pages/Docs/md/deploy-with-baota.en.mdx +++ b/src/pages/MainDocs/md/deploy-with-baota.en.mdx @@ -1,6 +1,6 @@ import BaotaPageSpy from '@/assets/image/screenshot/baota-pagespy.png'; import BaotaInstall from '@/assets/image/screenshot/baota-install.png'; -import { DeployNext } from '../components/DeployNext'; +import { DeployNext } from '@/components/Docs/components/DeployNext'; PageSpy supports one-click deployment in the Baota Panel's Docker appstore. diff --git a/src/pages/Docs/md/deploy-with-baota.zh.mdx b/src/pages/MainDocs/md/deploy-with-baota.zh.mdx similarity index 91% rename from src/pages/Docs/md/deploy-with-baota.zh.mdx rename to src/pages/MainDocs/md/deploy-with-baota.zh.mdx index e5dd76ed..837899de 100644 --- a/src/pages/Docs/md/deploy-with-baota.zh.mdx +++ b/src/pages/MainDocs/md/deploy-with-baota.zh.mdx @@ -1,6 +1,6 @@ import BaotaPageSpy from '@/assets/image/screenshot/baota-pagespy.png'; import BaotaInstall from '@/assets/image/screenshot/baota-install.png'; -import { DeployNext } from '../components/DeployNext'; +import { DeployNext } from '@/components/Docs/components/DeployNext'; PageSpy 支持在宝塔面板的 Docker 应用商店一键部署。 diff --git a/src/pages/Docs/md/deploy-with-docker.en.mdx b/src/pages/MainDocs/md/deploy-with-docker.en.mdx similarity index 72% rename from src/pages/Docs/md/deploy-with-docker.en.mdx rename to src/pages/MainDocs/md/deploy-with-docker.en.mdx index 7a5e9cce..51d1f311 100644 --- a/src/pages/Docs/md/deploy-with-docker.en.mdx +++ b/src/pages/MainDocs/md/deploy-with-docker.en.mdx @@ -1,5 +1,5 @@ -import { EmbedVideo } from '../components/EmbedVideo'; -import { DeployNext } from '../components/DeployNext'; +import { EmbedVideo } from '@/components/Docs/components/EmbedVideo'; +import { DeployNext } from '@/components/Docs/components/DeployNext'; ### Install#install diff --git a/src/pages/Docs/md/deploy-with-docker.zh.mdx b/src/pages/MainDocs/md/deploy-with-docker.zh.mdx similarity index 72% rename from src/pages/Docs/md/deploy-with-docker.zh.mdx rename to src/pages/MainDocs/md/deploy-with-docker.zh.mdx index b189688a..baf24573 100644 --- a/src/pages/Docs/md/deploy-with-docker.zh.mdx +++ b/src/pages/MainDocs/md/deploy-with-docker.zh.mdx @@ -1,5 +1,5 @@ -import { EmbedVideo } from '../components/EmbedVideo'; -import { DeployNext } from '../components/DeployNext'; +import { EmbedVideo } from '@/components/Docs/components/EmbedVideo'; +import { DeployNext } from '@/components/Docs/components/DeployNext'; ### 安装#install diff --git a/src/pages/Docs/md/deploy-with-node.en.mdx b/src/pages/MainDocs/md/deploy-with-node.en.mdx similarity index 72% rename from src/pages/Docs/md/deploy-with-node.en.mdx rename to src/pages/MainDocs/md/deploy-with-node.en.mdx index 85c3560e..38876e7f 100644 --- a/src/pages/Docs/md/deploy-with-node.en.mdx +++ b/src/pages/MainDocs/md/deploy-with-node.en.mdx @@ -1,5 +1,5 @@ -import { EmbedVideo } from '../components/EmbedVideo'; -import { DeployNext } from '../components/DeployNext'; +import { EmbedVideo } from '@/components/Docs/components/EmbedVideo'; +import { DeployNext } from '@/components/Docs/components/DeployNext'; ### Install#install diff --git a/src/pages/Docs/md/deploy-with-node.zh.mdx b/src/pages/MainDocs/md/deploy-with-node.zh.mdx similarity index 72% rename from src/pages/Docs/md/deploy-with-node.zh.mdx rename to src/pages/MainDocs/md/deploy-with-node.zh.mdx index be2a9058..a83fdea6 100644 --- a/src/pages/Docs/md/deploy-with-node.zh.mdx +++ b/src/pages/MainDocs/md/deploy-with-node.zh.mdx @@ -1,5 +1,5 @@ -import { EmbedVideo } from '../components/EmbedVideo'; -import { DeployNext } from '../components/DeployNext'; +import { EmbedVideo } from '@/components/Docs/components/EmbedVideo'; +import { DeployNext } from '@/components/Docs/components/DeployNext'; ### 安装#install diff --git a/src/pages/Docs/md/faq.en.mdx b/src/pages/MainDocs/md/faq.en.mdx similarity index 100% rename from src/pages/Docs/md/faq.en.mdx rename to src/pages/MainDocs/md/faq.en.mdx diff --git a/src/pages/Docs/md/faq.zh.mdx b/src/pages/MainDocs/md/faq.zh.mdx similarity index 100% rename from src/pages/Docs/md/faq.zh.mdx rename to src/pages/MainDocs/md/faq.zh.mdx diff --git a/src/pages/Docs/md/harmony.en.mdx b/src/pages/MainDocs/md/harmony.en.mdx similarity index 100% rename from src/pages/Docs/md/harmony.en.mdx rename to src/pages/MainDocs/md/harmony.en.mdx diff --git a/src/pages/Docs/md/harmony.zh.mdx b/src/pages/MainDocs/md/harmony.zh.mdx similarity index 100% rename from src/pages/Docs/md/harmony.zh.mdx rename to src/pages/MainDocs/md/harmony.zh.mdx diff --git a/src/pages/Docs/md/introduction.en.mdx b/src/pages/MainDocs/md/introduction.en.mdx similarity index 100% rename from src/pages/Docs/md/introduction.en.mdx rename to src/pages/MainDocs/md/introduction.en.mdx diff --git a/src/pages/Docs/md/introduction.zh.mdx b/src/pages/MainDocs/md/introduction.zh.mdx similarity index 100% rename from src/pages/Docs/md/introduction.zh.mdx rename to src/pages/MainDocs/md/introduction.zh.mdx diff --git a/src/pages/Docs/md/miniprogram.en.mdx b/src/pages/MainDocs/md/miniprogram.en.mdx similarity index 100% rename from src/pages/Docs/md/miniprogram.en.mdx rename to src/pages/MainDocs/md/miniprogram.en.mdx diff --git a/src/pages/Docs/md/miniprogram.zh.mdx b/src/pages/MainDocs/md/miniprogram.zh.mdx similarity index 100% rename from src/pages/Docs/md/miniprogram.zh.mdx rename to src/pages/MainDocs/md/miniprogram.zh.mdx diff --git a/src/pages/Docs/md/offline-log.en.mdx b/src/pages/MainDocs/md/offline-log.en.mdx similarity index 93% rename from src/pages/Docs/md/offline-log.en.mdx rename to src/pages/MainDocs/md/offline-log.en.mdx index 45954096..dd910830 100644 --- a/src/pages/Docs/md/offline-log.en.mdx +++ b/src/pages/MainDocs/md/offline-log.en.mdx @@ -37,7 +37,7 @@ It internally listens for the `"public-data"` event (see [what is the "public-da ### Used in browser#browser -> If you only need the offline log replay functionality of PageSpy, we recommend using [Replay Lab](#replay-lab) for a simpler integration. +> If you only need the offline log replay functionality of PageSpy, we recommend using [O-Spy](#o-spy) for a simpler integration. #### Step 1: Client-side SDK and plugin integration#step-1 @@ -84,13 +84,13 @@ DataHarborPlugin mainly collects and handles data. Additionally, PageSpy provide - [RRWebPlugin]({VITE_PLUGIN_RRWEB}): Uses `rrweb` to record DOM mutations. In the "Log Replay" panel on the left side of the debugger, devepler can see the user interaction traces. -### Replay Lab#replay-lab +### Using O-Spy#o-spy -The [Replay Lab](/replay-lab) SDK packages PageSpy, DataHarborPlugin, and RRWebPlugin. It includes the best practice configurations for using PageSpy in offline mode and supports custom themes. It is very easy to use. +The [O-Spy](/o-spy) SDK is plug and play, no deployment required. It packages PageSpy, DataHarborPlugin, and RRWebPlugin. It includes the best practice configurations for using PageSpy in offline mode and supports custom themes. It is framework-agnostic, so you can choose any method to integrate it into your project. -import { ImportGuide } from '@/pages/ReplayLab/components/ImportGuide'; +import { ImportGuide } from '@/pages/OSpy/components/ImportGuide'; @@ -98,7 +98,7 @@ Once successfully integrated, you will see a draggable "Issue Feedback" componen #### Custom Theme Example#customize-example -import { CustomizeExample } from '@/pages/ReplayLab/components/Customize'; +import { CustomizeExample } from '@/pages/OSpy/components/Customize'; diff --git a/src/pages/Docs/md/offline-log.zh.mdx b/src/pages/MainDocs/md/offline-log.zh.mdx similarity index 92% rename from src/pages/Docs/md/offline-log.zh.mdx rename to src/pages/MainDocs/md/offline-log.zh.mdx index bbcfa653..53853fbe 100644 --- a/src/pages/Docs/md/offline-log.zh.mdx +++ b/src/pages/MainDocs/md/offline-log.zh.mdx @@ -37,7 +37,7 @@ import mpDataHarborImg from '@/assets/image/screenshot/mp-data-harbor.png'; ### 在浏览器中使用#browser -> 如果仅需要 PageSpy 的离线日志回放功能,我们推荐 [回放实验室](#replay-lab) 的使用方式,集成更简单。 +> 如果仅需要 PageSpy 的离线日志回放功能,我们推荐 [O-Spy](#o-spy) 的使用方式,集成更简单。 #### 第一步:客户端引入 SDK 和插件#step-1 @@ -82,13 +82,13 @@ DataHarborPlugin 本身只收集数据、提供数据处理的功能,PageSpy - [RRWebPlugin]({VITE_PLUGIN_RRWEB}): 使用 `rrweb` 记录 DOM 更新,在调试端的「日志回放」面板左侧用户可以看到页面的操作轨迹; -### 回放实验室#replay-lab +### 使用 O-Spy#o-spy -[回放实验室](/replay-lab) 的 SDK 打包了 PageSpy / DataHarborPlugin / RRWebPlugin,内置 PageSpy 在离线状态下的最佳实践配置、同时支持自定义主题,使用非常简单。 +[O-Spy](/o-spy) 的 SDK 即插即用、无需部署。它打包了 PageSpy / DataHarborPlugin / RRWebPlugin,内置 PageSpy 在离线状态下的最佳实践配置、同时支持自定义主题,使用非常简单。 与框架无关,你可以任选一种方式在项目中引入。 -import { ImportGuide } from '@/pages/ReplayLab/components/ImportGuide'; +import { ImportGuide } from '@/pages/OSpy/components/ImportGuide'; @@ -96,7 +96,7 @@ import { ImportGuide } from '@/pages/ReplayLab/components/ImportGuide'; #### 自定义主题示例#customize-example -import { CustomizeExample } from '@/pages/ReplayLab/components/Customize'; +import { CustomizeExample } from '@/pages/OSpy/components/Customize'; diff --git a/src/pages/MainDocs/md/ospy.en.mdx b/src/pages/MainDocs/md/ospy.en.mdx new file mode 100644 index 00000000..c65c045d --- /dev/null +++ b/src/pages/MainDocs/md/ospy.en.mdx @@ -0,0 +1,2 @@ + +### Offline-Spy \ No newline at end of file diff --git a/src/pages/MainDocs/md/ospy.zh.mdx b/src/pages/MainDocs/md/ospy.zh.mdx new file mode 100644 index 00000000..c65c045d --- /dev/null +++ b/src/pages/MainDocs/md/ospy.zh.mdx @@ -0,0 +1,2 @@ + +### Offline-Spy \ No newline at end of file diff --git a/src/pages/Docs/md/pagespy.en.mdx b/src/pages/MainDocs/md/pagespy.en.mdx similarity index 99% rename from src/pages/Docs/md/pagespy.en.mdx rename to src/pages/MainDocs/md/pagespy.en.mdx index cf91563c..3f334b96 100644 --- a/src/pages/Docs/md/pagespy.en.mdx +++ b/src/pages/MainDocs/md/pagespy.en.mdx @@ -1,6 +1,6 @@ import mpPanelImg from '@/assets/image/screenshot/mp-panel.png'; import mpDataHarborImg from '@/assets/image/screenshot/mp-data-harbor.png'; -import PlatformTag from '../components/PlatformTag' +import PlatformTag from '@/components/Docs/components/PlatformTag' # PageSpy API#api diff --git a/src/pages/Docs/md/pagespy.zh.mdx b/src/pages/MainDocs/md/pagespy.zh.mdx similarity index 99% rename from src/pages/Docs/md/pagespy.zh.mdx rename to src/pages/MainDocs/md/pagespy.zh.mdx index 16465c48..0ef55d5a 100644 --- a/src/pages/Docs/md/pagespy.zh.mdx +++ b/src/pages/MainDocs/md/pagespy.zh.mdx @@ -1,6 +1,6 @@ import mpPanelImg from '@/assets/image/screenshot/mp-panel.png'; import mpDataHarborImg from '@/assets/image/screenshot/mp-data-harbor.png'; -import PlatformTag from '../components/PlatformTag' +import PlatformTag from '@/components/Docs/components/PlatformTag' # PageSpy API diff --git a/src/pages/Docs/md/plugins.en.mdx b/src/pages/MainDocs/md/plugins.en.mdx similarity index 100% rename from src/pages/Docs/md/plugins.en.mdx rename to src/pages/MainDocs/md/plugins.en.mdx diff --git a/src/pages/Docs/md/plugins.zh.mdx b/src/pages/MainDocs/md/plugins.zh.mdx similarity index 100% rename from src/pages/Docs/md/plugins.zh.mdx rename to src/pages/MainDocs/md/plugins.zh.mdx diff --git a/src/pages/Docs/md/react-native.en.mdx b/src/pages/MainDocs/md/react-native.en.mdx similarity index 100% rename from src/pages/Docs/md/react-native.en.mdx rename to src/pages/MainDocs/md/react-native.en.mdx diff --git a/src/pages/Docs/md/react-native.zh.mdx b/src/pages/MainDocs/md/react-native.zh.mdx similarity index 100% rename from src/pages/Docs/md/react-native.zh.mdx rename to src/pages/MainDocs/md/react-native.zh.mdx diff --git a/src/pages/Docs/md/rrweb.en.mdx b/src/pages/MainDocs/md/rrweb.en.mdx similarity index 100% rename from src/pages/Docs/md/rrweb.en.mdx rename to src/pages/MainDocs/md/rrweb.en.mdx diff --git a/src/pages/Docs/md/rrweb.zh.mdx b/src/pages/MainDocs/md/rrweb.zh.mdx similarity index 100% rename from src/pages/Docs/md/rrweb.zh.mdx rename to src/pages/MainDocs/md/rrweb.zh.mdx diff --git a/src/pages/ReplayLab/components/Customize/index.less b/src/pages/OSpy/components/Customize/index.less similarity index 100% rename from src/pages/ReplayLab/components/Customize/index.less rename to src/pages/OSpy/components/Customize/index.less diff --git a/src/pages/ReplayLab/components/Customize/index.tsx b/src/pages/OSpy/components/Customize/index.tsx similarity index 67% rename from src/pages/ReplayLab/components/Customize/index.tsx rename to src/pages/OSpy/components/Customize/index.tsx index ff0ee493..9d78df1b 100644 --- a/src/pages/ReplayLab/components/Customize/index.tsx +++ b/src/pages/OSpy/components/Customize/index.tsx @@ -1,51 +1,43 @@ -import WholeBundle, { - Config, -} from '@huolala-tech/page-spy-plugin-whole-bundle'; -import '@huolala-tech/page-spy-plugin-whole-bundle/dist/index.css'; -import { useEffect, useRef, useState } from 'react'; +import OSpy from '@huolala-tech/page-spy-plugin-ospy'; +import '@huolala-tech/page-spy-plugin-ospy/dist/index.css'; +import { useEffect, useMemo, useRef, useState } from 'react'; import './index.less'; import { CodeBlock } from '@/components/CodeBlock'; import { useInViewport } from 'ahooks'; +import { Config } from '@huolala-tech/page-spy-plugin-ospy/dist/types/config'; +import { useTranslation } from 'react-i18next'; -type Theme = Omit; +type Theme = Pick; const THEMES: Theme[] = [ { - primaryColor: 'red', - title: '问题反馈', - logo: 'https://static.huolala.cn/image/783a566204f23cebb56cd3759954c9590f478aaa.png', + title: 'O-Spy', + logo: 'https://static.huolala.cn/image/21f7b74da110ab5c05d5366763c2efc30e3174fb.png', + primaryColor: '#8434E9', }, { - primaryColor: '#f60', - title: '货拉拉', + title: 'Huolala', logo: 'https://static.huolala.cn/image/b9d3a2facbd188056c2a3e5cb3ebc749023fbfbe.png', + primaryColor: '#f60', }, { - primaryColor: '#328e17', title: 'BugZap', logo: 'https://static.huolala.cn/image/6d7315a89a653f8b4d6c29d2c759491d21854788.png', + primaryColor: '#328e17', }, { - primaryColor: '#d732d2', title: 'DebugX', logo: 'https://static.huolala.cn/image/2ae2e7034b6f7775882953e708fb2351d2cbc690.png', - }, - { - primaryColor: '#468bed', - title: '強力殺虫', - logo: 'https://static.huolala.cn/image/d76e7d734ecb1b21b7e68f907b43cbf2ea06d3e4.png', - }, - { - primaryColor: '#FB9D22', - title: '故障排查', - logo: 'https://static.huolala.cn/image/4b469ad461600b0ed519809947dc2d8edf092000.png', + primaryColor: '#d732d2', }, ]; export const CustomizeExample = () => { + const { t } = useTranslation(); + const $oSpy = useRef(null); useEffect(() => { - const $feedback = new WholeBundle(); + $oSpy.current = new OSpy(); return () => { - $feedback.abort(); + $oSpy.current?.abort(); }; }, []); @@ -53,11 +45,20 @@ export const CustomizeExample = () => { const [inView] = useInViewport(ref); const [themeIndex, setThemeIndex] = useState(0); + const theme = useMemo(() => THEMES[themeIndex], [themeIndex]); + const code = useMemo(() => { + if (themeIndex === 0) + return `// ${t('oSpy.comment-default-params')} +new OSpy();`; + + return `new OSpy(${JSON.stringify( + { ...theme, autoRender: true }, + null, + 2, + )})`; + }, [t, theme, themeIndex]); useEffect(() => { - const theme = THEMES[themeIndex]; - const root = document.querySelector( - '#page-spy-feedback-root', - ) as HTMLDivElement; + const { root } = $oSpy.current || {}; if (!root) return; if (!inView) { @@ -79,7 +80,7 @@ export const CustomizeExample = () => { '[class^="_headerLeft"] [class^="_title"] b', ) as HTMLElement; - if (root && theme) { + if (theme) { const { primaryColor, logo, title } = theme; root.style.setProperty('--primary-color', primaryColor); if (floatLogo && floatTitle) { @@ -91,7 +92,7 @@ export const CustomizeExample = () => { modalTitle.textContent = title; } } - }, [inView, themeIndex]); + }, [inView, theme]); return (
@@ -114,14 +115,7 @@ export const CustomizeExample = () => { ); })}
- +
); }; diff --git a/src/pages/ReplayLab/components/ImportGuide/index.less b/src/pages/OSpy/components/ImportGuide/index.less similarity index 85% rename from src/pages/ReplayLab/components/ImportGuide/index.less rename to src/pages/OSpy/components/ImportGuide/index.less index 62d80d62..57aaf59a 100644 --- a/src/pages/ReplayLab/components/ImportGuide/index.less +++ b/src/pages/OSpy/components/ImportGuide/index.less @@ -10,6 +10,9 @@ border-radius: 4px; transition: all 100ms ease-in-out; cursor: pointer; + &:hover { + background-color: rgba(142, 142, 142, 0.15); + } &.active { color: #fff; background-color: @primary-color; diff --git a/src/pages/ReplayLab/components/ImportGuide/index.tsx b/src/pages/OSpy/components/ImportGuide/index.tsx similarity index 69% rename from src/pages/ReplayLab/components/ImportGuide/index.tsx rename to src/pages/OSpy/components/ImportGuide/index.tsx index 0b12d562..2414dec8 100644 --- a/src/pages/ReplayLab/components/ImportGuide/index.tsx +++ b/src/pages/OSpy/components/ImportGuide/index.tsx @@ -8,15 +8,25 @@ import JsDelivrSvg from '@/assets/image/jsdelivr.svg?react'; import UnpkgSvg from '@/assets/image/unpkg.svg?react'; import './index.less'; -export const ImportGuide = () => { - const { t } = useTranslation('translation', { keyPrefix: 'lab' }); +interface Props { + showConfig?: boolean; +} + +export const ImportGuide = ({ showConfig = true }: Props) => { + const { t } = useTranslation('translation', { keyPrefix: 'oSpy' }); const methods = useMemo(() => { - const INIT_CODE = `const $feedback = new WholeBundle({ - title?: string; // ${t('comment-title')} - logo?: string; // ${t('comment-logo')} - primaryColor?: string; // ${t('comment-primaryColor')} - autoRender?: boolean; // ${t('comment-autoRender')} -})`; + const INIT_CODE = showConfig + ? `const $oSpy = new OSpy({ + title?: string; // ${t('comment-title')} + logo?: string; // ${t('comment-logo')} + primaryColor?: string; // ${t('comment-primaryColor')} + autoRender?: boolean; // ${t('comment-autoRender')} + exportButtonText?: string; // ${t('comment-exportButtonText')} + onExportButtonClick?: (data: CacheMessageItem[]) => void; // ${t( + 'comment-onExportButtonClick', + )} + });` + : `const $oSpy = new OSpy();`; return [ { @@ -32,9 +42,11 @@ export const ImportGuide = () => { ), lang: 'javascript', - code: ` + code: ` -${INIT_CODE}`, +`, }, { title: ( @@ -44,9 +56,11 @@ ${INIT_CODE}`, ), lang: 'javascript', - code: ` + code: ` -${INIT_CODE}`, +`, }, ]} /> @@ -59,13 +73,13 @@ ${INIT_CODE}`,

{t('install-1st')}

{t('install-2nd')}

diff --git a/src/pages/ReplayLab/components/Welcome/demo.json b/src/pages/OSpy/components/Replayer/demo.json similarity index 100% rename from src/pages/ReplayLab/components/Welcome/demo.json rename to src/pages/OSpy/components/Replayer/demo.json diff --git a/src/pages/OSpy/components/Replayer/index.less b/src/pages/OSpy/components/Replayer/index.less new file mode 100644 index 00000000..55bca7d4 --- /dev/null +++ b/src/pages/OSpy/components/Replayer/index.less @@ -0,0 +1,4 @@ +.replayer-container { + position: relative; + height: 100%; +} diff --git a/src/pages/OSpy/components/Replayer/index.tsx b/src/pages/OSpy/components/Replayer/index.tsx new file mode 100644 index 00000000..ee261e81 --- /dev/null +++ b/src/pages/OSpy/components/Replayer/index.tsx @@ -0,0 +1,74 @@ +import { LogReplayer } from '@/components/LogReplayer'; +import { useEffect, useMemo } from 'react'; +import './index.less'; +import { Button, Flex, Space } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { ArrowLeftOutlined } from '@ant-design/icons'; +import { useSize } from 'ahooks'; +import { Link, useNavigate } from 'react-router-dom'; +import useSearch from '@/utils/useSearch'; +import demo from './demo.json?url'; +import { SelectLogButton } from '@/components/SelectLogButton'; + +export const Replayer = () => { + const { t } = useTranslation(); + const size = useSize(document.body); + const navigate = useNavigate(); + + const { url } = useSearch(); + const replayUrl = useMemo(() => { + if (url === 'demo') return demo; + if (!url) return ''; + return url; + }, [url]); + useEffect( + () => () => { + if (url.startsWith('blob://')) { + URL.revokeObjectURL(url); + } + }, + [url], + ); + + const backSlot = useMemo(() => { + return ( + + + + + { + navigate(`?url=${url}`); + }} + /> + + ); + }, [navigate, t]); + + if (Number(size?.width) <= 768) { + return ( + +

{t('oSpy.only-pc')}

+ +
+ ); + } + return ( +
+ +
+ ); +}; diff --git a/src/pages/OSpy/components/Welcome/index.less b/src/pages/OSpy/components/Welcome/index.less new file mode 100644 index 00000000..9407430a --- /dev/null +++ b/src/pages/OSpy/components/Welcome/index.less @@ -0,0 +1,19 @@ +.welcome { + height: 100%; + .slogan { + font-size: 76px; + font-weight: 700; + text-align: center; + &-desc { + font-size: 24px; + text-align: center; + } + } +} +@media screen and (max-width: 768px) { + .welcome { + .slogan { + font-size: 56px; + } + } +} diff --git a/src/pages/OSpy/components/Welcome/index.tsx b/src/pages/OSpy/components/Welcome/index.tsx new file mode 100644 index 00000000..6b1d1f60 --- /dev/null +++ b/src/pages/OSpy/components/Welcome/index.tsx @@ -0,0 +1,131 @@ +import { Button, Flex, Modal, Popover } from 'antd'; +import { Trans, useTranslation } from 'react-i18next'; +import { Link, useNavigate } from 'react-router-dom'; +import LinkSvg from '@/assets/image/link.svg?react'; +import CodeBlockSvg from '@/assets/image/code-block.svg?react'; +import Icon from '@ant-design/icons'; +import { ImportGuide } from '../ImportGuide'; +import { useState } from 'react'; +import './index.less'; +import { useSize } from 'ahooks'; +import { SelectLogButton } from '@/components/SelectLogButton'; + +const InstallSdkButton = () => { + const { t } = useTranslation(); + const size = useSize(document.body); + const [open, setOpen] = useState(false); + + if (!size) return null; + + if (size.height <= 850) { + return ( + <> + {t('oSpy.import-use')}} + width="95%" + style={{ maxWidth: 800 }} + onCancel={() => setOpen(false)} + footer={null} + maskClosable + > + + + + + ); + } + return ( + {t('oSpy.import-use')}} + content={} + trigger="click" + overlayInnerStyle={{ maxWidth: 800 }} + > + + + ); +}; + +export const Welcome = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + const size = useSize(document.body); + + return ( + + +

+ + 离线记录 +
+ 即插即用 +
+

+

+ + 几行代码,回看程序运行现场 +
+ 数据都在本地,不经过网络传输,无需担心隐私泄露 +
+

+ + + + { + navigate(`?url=${url}`); + }} + /> + + + + {t('oSpy.take-try')} + + + + +
+
+ ); +}; diff --git a/src/pages/OSpy/index.less b/src/pages/OSpy/index.less new file mode 100644 index 00000000..6e2e15ef --- /dev/null +++ b/src/pages/OSpy/index.less @@ -0,0 +1,53 @@ +.o-spy { + position: relative; + height: 100%; + color: white; + background: linear-gradient(-45deg, #fe815a, #ff4b90, #9a5cff, #23d5ab); + background-size: 100% 100%; + // animation: gradient 45s ease infinite; + overflow: hidden; + ::selection { + background-color: #fff; + color: @primary-color; + } + h1 { + font-size: 58px; + } + h2 { + font-size: 42px; + } + h3 { + font-size: 32px; + } + h4 { + font-size: 20px; + } + h5 { + font-size: 16px; + } + @media screen and (max-width: 768px) { + h1 { + font-size: 46px; + } + .statement { + font-size: 12px; + } + } + .code-block { + code * { + font-size: 16px; + } + } +} + +@keyframes gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} diff --git a/src/pages/OSpy/index.tsx b/src/pages/OSpy/index.tsx new file mode 100644 index 00000000..ccaf148b --- /dev/null +++ b/src/pages/OSpy/index.tsx @@ -0,0 +1,15 @@ +import './index.less'; +import { Welcome } from './components/Welcome'; +import { Replayer } from './components/Replayer'; +import useSearch from '@/utils/useSearch'; +import { has } from 'lodash-es'; + +export const OSpy = () => { + const search = useSearch(); + + return ( +
+ {has(search, 'url') ? : } +
+ ); +}; diff --git a/src/pages/OSpyDocs/index.tsx b/src/pages/OSpyDocs/index.tsx new file mode 100644 index 00000000..1acc3f36 --- /dev/null +++ b/src/pages/OSpyDocs/index.tsx @@ -0,0 +1,53 @@ +import Docs from '@/components/Docs'; + +const sidebar = [ + { + group: { + zh: '指引', + en: 'Guide', + ja: 'ガイド', + ko: '가이드', + }, + children: [ + { + label: { + zh: '简介', + en: 'Introduction', + ja: '紹介', + ko: '소개', + }, + doc: 'introduction', + }, + { + label: { + zh: '自定义主题', + en: 'Customize Theme', + ja: 'カスタムテーマ', + ko: '사용자 지정 테마', + }, + doc: 'theme', + }, + { + label: { + zh: '常见问题解答', + en: 'FAQ', + ja: 'よくある質問', + ko: '자주 묻는 질문', + }, + doc: 'faq', + }, + ], + }, +]; + +const mdxComponents = import.meta.glob('./md/*.mdx'); +const mdRawContents = import.meta.glob('./md/*.mdx', { + import: 'default', + query: '?raw', +}) as Record Promise>; + +const OSpyDocs = () => { + return ; +}; + +export default OSpyDocs; diff --git a/src/pages/OSpyDocs/md-components/CallOSPy.tsx b/src/pages/OSpyDocs/md-components/CallOSPy.tsx new file mode 100644 index 00000000..5a0de6be --- /dev/null +++ b/src/pages/OSpyDocs/md-components/CallOSPy.tsx @@ -0,0 +1,13 @@ +import OSpy from '@huolala-tech/page-spy-plugin-ospy'; +import '@huolala-tech/page-spy-plugin-ospy/dist/index.css'; +import { useEffect } from 'react'; + +export const CallOSpy = () => { + useEffect(() => { + const $oSpy = new OSpy(); + return () => { + $oSpy.abort(); + }; + }); + return null; +}; diff --git a/src/pages/OSpyDocs/md-components/MakeData.tsx b/src/pages/OSpyDocs/md-components/MakeData.tsx new file mode 100644 index 00000000..9181bb76 --- /dev/null +++ b/src/pages/OSpyDocs/md-components/MakeData.tsx @@ -0,0 +1,85 @@ +import { Button, Flex } from 'antd'; +import { useRef } from 'react'; +import { useTranslation } from 'react-i18next'; + +const asciiCode = [ + ` + _ _ _ _ _ + | | | | | | | | | + | |__| | ___| | | ___ | | + | __ |/ _ \\ | |/ _ \\ | | + | | | | __/ | | (_) | |_| + |_| |_|\\___|_|_|\\___/ (_) + +`, + ` + _____ _ ____ _____ + |_ _( ) / __ \ / ____| + | | |/ _ __ ___ | | | |_____| (___ _ __ _ _ + | | | '_ \` _ \\ | | | |______\\___ \\| '_ \\| | | | + _| |_ | | | | | | | |__| | ____) | |_) | |_| | + |_____| |_| |_| |_| \____/ |_____/| .__/ \__, | + | | __/ | + |_| |___/ +`, + ` + ____ __ __ _ _ _____ _ + / __ \\ / _|/ _| (_) | __ \\ | | + | | | | |_| |_| |_ _ __ ___ | |__) |___ ___ ___ _ __ __| | + | | | | _| _| | | '_ \\ / _ \\ | _ // _ \\/ __/ _ \\| '__/ _\` | + | |__| | | | | | | | | | | __/ | | \\ \\ __/ (_| (_) | | | (_| | + \\____/|_| |_| |_|_|_| |_|\\___| |_| \\_\\___|\\___\\___/|_| \\__,_| +`, + + ` + _____ _ _ _____ _ + | __ \\| | | | | __ \\| | + | |__) | |_ _ __ _ __ _ _ __ __| | | |__) | | __ _ _ _ + | ___/| | | | |/ _\` | / _\` | '_ \\ / _\` | | ___/| |/ _\` | | | | + | | | | |_| | (_| | | (_| | | | | (_| | | | | | (_| | |_| | + |_| |_|\\__,_|\\__, | \\__,_|_| |_|\\__,_| |_| |_|\\__,_|\\__, | + __/ | __/ | + |___/ |___/ +`, + ` + __ __ _ _ + \\ \\ / / | | | | + \\ \\ /\\ / /__| | ___ ___ _ __ ___ ___ | |_ ___ _ _ ___ ___ + \\ \\/ \\/ / _ \\ |/ __/ _ \\| '_ \` _ \\ / _ \\ | __/ _ \\ | | | / __|/ _ \\ + \\ /\\ / __/ | (_| (_) | | | | | | __/ | || (_) | | |_| \\__ \\ __/ + \\/ \\/ \\___|_|\\___\\___/|_| |_| |_|\\___| \\__\\___/ \\__,_|___/\\___| + +`, +]; + +export const MakeData = () => { + const { t } = useTranslation('translation', { keyPrefix: 'oSpy' }); + const index = useRef(0); + return ( + + + + + + ); +}; diff --git a/src/pages/OSpyDocs/md/faq.en.mdx b/src/pages/OSpyDocs/md/faq.en.mdx new file mode 100644 index 00000000..62e95302 --- /dev/null +++ b/src/pages/OSpyDocs/md/faq.en.mdx @@ -0,0 +1,17 @@ +### Relation of the O-Spy and PageSpy? #difference + +O-Spy is built on top of the capabilities of [PageSpy](/) and its plugin ecosystem. Specifically, it packages the following three dependencies: + +- `@huolala-tech/page-spy-browser`: PageSpy's SDK for the Web environment, which enables offline mode in O-Spy; +- `@huolala-tech/page-spy-plugin-rrweb`: Records user interaction traces on the page; +- `@huolala-tech/page-spy-plugin-data-harbor`: Processes the data captured by PageSpy and provides export and other operations. + +The key differences between O-Spy and PageSpy are as follows: + +| | O-Spy | PageSpy | +| -------------------------- | -------------------------- | ------- | +| Requires deployment | No | Yes | +| Online real-time debugging | ❌ | ✅ | +| Offline replay debugging | ✅ | ✅ | +| Data upload | 🟡 Handled manually | ✅ | +| Data download | ✅ | ✅ | diff --git a/src/pages/OSpyDocs/md/faq.zh.mdx b/src/pages/OSpyDocs/md/faq.zh.mdx new file mode 100644 index 00000000..70d1a014 --- /dev/null +++ b/src/pages/OSpyDocs/md/faq.zh.mdx @@ -0,0 +1,17 @@ +### O-Spy 和 PageSpy 是什么关系?#difference + +O-Spy 是基于 [PageSpy](/) 及其插件生态的能力构建而来,具体而言,它打包了以下三个依赖: + +- `@huolala-tech/page-spy-browser`:PageSpy 在 Web 环境中的 SDK,O-Spy 引入后开启离线模式; +- `@huolala-tech/page-spy-plugin-rrweb`:记录用户在页面上的操作轨迹; +- `@huolala-tech/page-spy-plugin-data-harbor`:对 PageSpy 捕获的数据进行处理,对外提供导出等操作; + +两者的区别主要体现在: + +| | O-Spy | PageSpy | +| ------------ | ------------ | ------- | +| 是否需要部署 | 否 | 是 | +| 在线实时调试 | ❌ | ✅ | +| 离线回放调试 | ✅ | ✅ | +| 上传数据 | 🟡 需自行处理 | ✅ | +| 下载数据 | ✅ | ✅ | diff --git a/src/pages/OSpyDocs/md/introduction.en.mdx b/src/pages/OSpyDocs/md/introduction.en.mdx new file mode 100644 index 00000000..c75999d4 --- /dev/null +++ b/src/pages/OSpyDocs/md/introduction.en.mdx @@ -0,0 +1,61 @@ + +[license-img]: https://img.shields.io/github/license/HuolalaTech/page-spy-web?label=License +[license-url]: https://github.com/HuolalaTech/page-spy-web/blob/main/LICENSE +[sdk-ver-img]: https://img.shields.io/npm/v/@huolala-tech/page-spy-plugin-ospy?label=OSpy&color=white +[sdk-ver-url]: https://npmjs.com/package/@huolala-tech/page-spy-plugin-ospy + +import Icon from '@ant-design/icons'; +import Logo from '@/assets/image/o-spy.svg?react'; +import workflow from '@/assets/image/screenshot/ospy-workflow.en.png'; +import { Link } from 'react-router-dom'; + +
+ +

O-Spy

+
Offline recording, plug and play.
+
+ +
+ [![SDK version][sdk-ver-img]][sdk-ver-url] + [![license][license-img]][license-url] +
+ +## What is O-Spy? #what-is-ospy + +O-Spy (pronounced /əʊ spaɪ/, similar to **"Oh-Spy"**) is a debugging tool for **offline recording program runtime data** in web projects. The recorded data can be replayed using the O-Spy platform. [Click to see the demo](/o-spy?url=demo). + +The workflow is shown in the image below: + + + + + +## Quick Start + +#### Step 1: Import O-Spy #step-1 + +O-Spy is framework-agnostic, and you can integrate it into your project in any way you prefer. + +import { ImportGuide } from '@/pages/OSpy/components/ImportGuide'; + + + +Once successfully imported, you will see a draggable "O-Spy" widget at the bottom right of the screen. Try opening it :) + +import { CallOSpy } from '../md-components/CallOSPy'; + + + +#### Step 2: Export Data #step-2 + +This page already integrates O-Spy. Imagine it as your web page and let’s generate some data. Feel free to click the button below: + +import { MakeData } from '../md-components/MakeData' + + + +Then click the floating O-Spy button to open the popup and click "Export Log" to download a JSON file. + +#### Step 3: Replay #step-3 + +Go to the replay page, click the "Select Log" button at the top left, choose the JSON file we just exported, and start the replay! diff --git a/src/pages/OSpyDocs/md/introduction.zh.mdx b/src/pages/OSpyDocs/md/introduction.zh.mdx new file mode 100644 index 00000000..38e416ff --- /dev/null +++ b/src/pages/OSpyDocs/md/introduction.zh.mdx @@ -0,0 +1,63 @@ + +[license-img]: https://img.shields.io/github/license/HuolalaTech/page-spy-web?label=License +[license-url]: https://github.com/HuolalaTech/page-spy-web/blob/main/LICENSE +[sdk-ver-img]: https://img.shields.io/npm/v/@huolala-tech/page-spy-plugin-ospy?label=OSpy&color=white +[sdk-ver-url]: https://npmjs.com/package/@huolala-tech/page-spy-plugin-ospy + +import Icon from '@ant-design/icons'; +import Logo from '@/assets/image/o-spy.svg?react'; +import workflow from '@/assets/image/screenshot/ospy-workflow.zh.png'; +import { Link } from 'react-router-dom'; + +
+ +

O-Spy

+
离线记录,即插即用。
+
+ +
+ [![SDK version][sdk-ver-img]][sdk-ver-url] + [![license][license-img]][license-url] +
+ + +## 什么是 O-Spy?#what-is-ospy + +O-Spy(发音 /əʊ spaɪ/,类似 **"Oh-Spy"**)是一个在 Web 项目中「离线记录程序运行数据」的调试工具,记录的数据可通过 O-Spy 平台回放,[点击查看演示](/o-spy?url=demo)。 + +工作流程如下图: + + + + + +## 快速上手 + +#### 第一步:引入 O-Spy#step-1 + +与框架无关,你可以按你喜欢的方式在项目中引入。 + +import { ImportGuide } from '@/pages/OSpy/components/ImportGuide'; + + + +项目引入成功后就会看到屏幕右下角所示的 "O-Spy" 可拖拽的组件,试着点开看看 :) + +import { CallOSpy } from '../md-components/CallOSPy'; + + + +#### 第二步:导出数据#step-2 + +当前页面已经集成了 O-Spy,将它设想成你的项目页面,我们让程序产生一些数据。随意点击下方按钮: + +import { MakeData } from '../md-components/MakeData' + + + +随后点击 O-Spy 悬浮按钮打开弹窗、点击 “导出日志”,这将会下载一个 json 文件。 + +#### 第三步:回放#step-3 + +前往 回放页面,点击左上角的 “选择日志”、选择我们刚刚导出的 json 文件后即可开始回放! + diff --git a/src/pages/OSpyDocs/md/theme.en.mdx b/src/pages/OSpyDocs/md/theme.en.mdx new file mode 100644 index 00000000..e147fb8a --- /dev/null +++ b/src/pages/OSpyDocs/md/theme.en.mdx @@ -0,0 +1,11 @@ +O-Spy supports customize theme color and brand logo. + +import { ImportGuide } from '@/pages/OSpy/components/ImportGuide'; + + + +## Example#customize-example + +import { CustomizeExample } from '@/pages/OSpy/components/Customize'; + + \ No newline at end of file diff --git a/src/pages/OSpyDocs/md/theme.zh.mdx b/src/pages/OSpyDocs/md/theme.zh.mdx new file mode 100644 index 00000000..b3bbcdaa --- /dev/null +++ b/src/pages/OSpyDocs/md/theme.zh.mdx @@ -0,0 +1,11 @@ +O-Spy 支持定制主题色和品牌 Logo。 + +import { ImportGuide } from '@/pages/OSpy/components/ImportGuide'; + + + +## 案例演示#customize-example + +import { CustomizeExample } from '@/pages/OSpy/components/Customize'; + + \ No newline at end of file diff --git a/src/pages/Replay/index.tsx b/src/pages/Replay/index.tsx index 798e1613..60b46f4e 100644 --- a/src/pages/Replay/index.tsx +++ b/src/pages/Replay/index.tsx @@ -1,10 +1,42 @@ import useSearch from '@/utils/useSearch'; import { LogReplayer } from '@/components/LogReplayer'; +import { SelectLogButton } from '@/components/SelectLogButton'; +import { ArrowLeftOutlined } from '@ant-design/icons'; +import { Space, Button } from 'antd'; +import { useMemo } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; const Replay = () => { const { url } = useSearch(); + const { t } = useTranslation(); - return ; + const navigate = useNavigate(); + const backSlot = useMemo(() => { + return ( + + + + + { + navigate(`?url=${url}`); + }} + /> + + ); + }, [navigate, t, url]); + + return ; }; export default Replay; diff --git a/src/pages/ReplayLab/components/ReplayInLab/index.less b/src/pages/ReplayLab/components/ReplayInLab/index.less deleted file mode 100644 index 3419a096..00000000 --- a/src/pages/ReplayLab/components/ReplayInLab/index.less +++ /dev/null @@ -1,7 +0,0 @@ -.replay-in-lab { - position: relative; - height: 100%; - .replay-container { - height: 100%; - } -} diff --git a/src/pages/ReplayLab/components/ReplayInLab/index.tsx b/src/pages/ReplayLab/components/ReplayInLab/index.tsx deleted file mode 100644 index 6d022483..00000000 --- a/src/pages/ReplayLab/components/ReplayInLab/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { LogReplayer } from '@/components/LogReplayer'; -import { useMemo } from 'react'; -import './index.less'; -import { Button, Flex, Space, Upload, UploadProps } from 'antd'; -import { useTranslation } from 'react-i18next'; -import { ArrowLeftOutlined } from '@ant-design/icons'; -import { useStepStore } from '../store'; -import { useThreshold } from '@/utils/useThreshold'; -import { useShallow } from 'zustand/react/shallow'; -import PaperClipSvg from '@/assets/image/paper-clip.svg?react'; -import Icon from '@ant-design/icons'; - -export const ReplayInLab = () => { - const { t } = useTranslation(); - const isMobile = useThreshold(); - const [prev, replayUrl, setReplayUrl] = useStepStore( - useShallow((state) => [state.prev, state.replayUrl, state.setReplayUrl]), - ); - const uploadCustomRequest: UploadProps['customRequest'] = (file) => { - const blob = URL.createObjectURL(file.file as File); - setReplayUrl(blob); - return null; - }; - const reusableButtons = useMemo(() => { - return ( - - - null} - > - - - - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [prev, t]); - if (isMobile) { - return ( - -

{t('lab.only-pc')}

- -
- ); - } - return ( -
-
- -
-
- ); -}; diff --git a/src/pages/ReplayLab/components/Welcome/index.tsx b/src/pages/ReplayLab/components/Welcome/index.tsx deleted file mode 100644 index 3367ac9f..00000000 --- a/src/pages/ReplayLab/components/Welcome/index.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { Button, Flex, Popover, Upload } from 'antd'; -import { useStepStore } from '../store'; -import { Trans, useTranslation } from 'react-i18next'; -import { Link, useLocation } from 'react-router-dom'; -import LinkSvg from '@/assets/image/link.svg?react'; -import CodeBlockSvg from '@/assets/image/code-block.svg?react'; -import PaperClipSvg from '@/assets/image/paper-clip.svg?react'; -import Icon from '@ant-design/icons'; -import { ImportGuide } from '../ImportGuide'; -import { useEffect } from 'react'; -import demo from './demo.json?url'; -import { useThreshold } from '@/utils/useThreshold'; - -export const Welcome = () => { - const { t } = useTranslation(); - const [next, setReplayUrl] = useStepStore((state) => [ - state.next, - state.setReplayUrl, - ]); - - const gotoReplay = (blob: string) => { - setReplayUrl(blob); - next(); - }; - - const { search } = useLocation(); - useEffect(() => { - if (search.includes('?demo')) { - gotoReplay(demo); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const isMobile = useThreshold(414); - - return ( - -

- - 欢迎来到 -
- 回放实验室 -
-

-

- - 几行代码,让系统拥有强大的「问题反馈」能力 -
- 数据都在本地,不经过网络传输,无需担心隐私泄露 -
-

- - - - - - - { - const blob = URL.createObjectURL(file.file as File); - gotoReplay(blob); - }} - itemRender={() => null} - > - - - - - - {t('lab.take-try')} - - - - -
- ); -}; diff --git a/src/pages/ReplayLab/components/store.tsx b/src/pages/ReplayLab/components/store.tsx deleted file mode 100644 index c82d51ee..00000000 --- a/src/pages/ReplayLab/components/store.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { create } from 'zustand'; -import { debounce } from 'lodash-es'; - -interface StepStore { - current: number; - goto: (c: number) => void; - prev: () => void; - next: () => void; - - replayUrl: string; - setReplayUrl: (replayUrl: string) => void; -} - -export const useStepStore = create((set, get) => { - return { - current: 0, - goto: debounce((current: number) => set({ current }), 300, { - leading: true, - trailing: false, - }), - prev: () => get().goto(Math.max(0, get().current - 1)), - next: () => get().goto(Math.min(1, get().current + 1)), - - replayUrl: '', - setReplayUrl(replayUrl) { - const prev = get().replayUrl; - if (prev?.startsWith('blob:')) { - URL.revokeObjectURL(prev); - } - if (replayUrl) { - set({ replayUrl }); - } - }, - }; -}); diff --git a/src/pages/ReplayLab/index.less b/src/pages/ReplayLab/index.less deleted file mode 100644 index 8ea2b708..00000000 --- a/src/pages/ReplayLab/index.less +++ /dev/null @@ -1,96 +0,0 @@ -.replay-lab { - position: relative; - height: 100%; - color: white; - background: linear-gradient(-45deg, #fe815a, #ff4b90, #9a5cff, #23d5ab); - background-size: 100% 100%; - // animation: gradient 45s ease infinite; - overflow: hidden; - ::selection { - background-color: #fff; - color: @primary-color; - } - h1 { - font-size: 58px; - } - h2 { - font-size: 42px; - } - h3 { - font-size: 32px; - } - h4 { - font-size: 20px; - } - h5 { - font-size: 16px; - } - @media screen and (max-width: 768px) { - h1 { - font-size: 46px; - } - .statement { - font-size: 12px; - } - } - .code-block { - code * { - font-size: 16px; - } - } - .step-actions { - position: fixed; - left: 50%; - bottom: 30px; - transform: translateX(-50%); - font-size: 24px; - user-select: none; - opacity: 0.85; - transition: opacity 0.1s linear; - white-space: nowrap; - &:hover { - opacity: 1; - } - &__item { - font-size: 32px; - padding: 12px; - &[disabled] { - color: #bbb; - cursor: not-allowed; - } - } - } - - .fade-enter-active, - .fade-exit-active { - transition: all ease-out 300ms; - } - .fade-enter { - opacity: 0; - transform: translate3d(40px, 0, 0); - } - .fade-enter-active { - opacity: 1; - transform: translate3d(0px, 0, 0); - } - .fade-exit { - opacity: 1; - transform: translate3d(0px, 0, 0); - } - .fade-exit-active { - opacity: 0; - transform: translate3d(-40px, 0, 0); - } -} - -@keyframes gradient { - 0% { - background-position: 0% 50%; - } - 50% { - background-position: 100% 50%; - } - 100% { - background-position: 0% 50%; - } -} diff --git a/src/pages/ReplayLab/index.tsx b/src/pages/ReplayLab/index.tsx deleted file mode 100644 index ed818fd7..00000000 --- a/src/pages/ReplayLab/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import './index.less'; -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Welcome } from './components/Welcome'; -import { useStepStore } from './components/store'; -import { ReplayInLab } from './components/ReplayInLab'; -import '@huolala-tech/page-spy-plugin-whole-bundle/dist/index.css'; - -const ReplayLab = () => { - const { t } = useTranslation('translation', { keyPrefix: 'lab' }); - const { current } = useStepStore(); - - const contents = useMemo(() => { - return [ - { - title: t('welcome'), - content: , - }, - { - title: t('replay'), - content: , - }, - ]; - }, [t]); - - return ( -
-
{contents[current].content}
-
- ); -}; - -export default ReplayLab; diff --git a/src/routes/config.tsx b/src/routes/config.tsx index 64aa14da..92a3760b 100644 --- a/src/routes/config.tsx +++ b/src/routes/config.tsx @@ -1,17 +1,18 @@ +import React from 'react'; import { RouteObject } from 'react-router-dom'; import { useRoutes } from 'react-router-dom'; import { Page404, To404 } from '@/404'; import { Layouts } from '@/pages/Layouts'; -import { Home } from '@/pages/Home'; -import React from 'react'; +import { Main } from '@/pages/Main'; +import { OSpy } from '@/pages/OSpy'; const Devtools = React.lazy(() => import('@/pages/Devtools')); const RoomList = React.lazy(() => import('@/pages/RoomList')); -const Docs = React.lazy(() => import('@/pages/Docs')); +const MainDocs = React.lazy(() => import('@/pages/MainDocs')); const Replay = React.lazy(() => import('@/pages/Replay')); const LogList = React.lazy(() => import('@/pages/LogList')); -const ReplayLab = React.lazy(() => import('@/pages/ReplayLab')); +const OSpyDocs = React.lazy(() => import('@/pages/OSpyDocs')); export interface RouteInfo { icon?: any; @@ -28,31 +29,41 @@ const routes: RouteObject[] = [ children: [ { index: true, - element: , + element:
, }, { - path: '/devtools', + path: 'devtools', element: , }, { - path: '/room-list', + path: 'room-list', element: , }, { - path: '/log-list', + path: 'log-list', element: , }, { - path: '/docs/*', - element: , + path: 'docs/*', + element: , }, { - path: '/replay', + path: 'replay', element: , }, + ], + }, + { + path: '/o-spy', + element: , + children: [ + { + index: true, + element: , + }, { - path: '/replay-lab', - element: , + path: 'docs/*', + element: , }, ], }, diff --git a/src/utils/useDarkTheme.ts b/src/utils/useDarkTheme.ts index cdfeb847..479a4b9c 100644 --- a/src/utils/useDarkTheme.ts +++ b/src/utils/useDarkTheme.ts @@ -1,16 +1,7 @@ -import { useMemo } from 'react'; -import { useLocation } from 'react-router-dom'; +import { useWhere } from './useWhere'; export const useDarkTheme = () => { - const { pathname } = useLocation(); - const isDark = useMemo(() => { - if ( - pathname === '/' || - ['/docs', '/replay-lab'].some((i) => pathname.startsWith(i)) - ) - return true; - return false; - }, [pathname]); + const { isHome, isDocs, isOSpy } = useWhere(); - return isDark; + return isHome || isDocs || isOSpy; }; diff --git a/src/utils/useThreshold.ts b/src/utils/useThreshold.ts deleted file mode 100644 index d29a0320..00000000 --- a/src/utils/useThreshold.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useState } from 'react'; -import { useEventListener } from './useEventListener'; - -export const useThreshold = (value = 768) => { - const [threshold, setThreshold] = useState(window.innerWidth <= value); - useEventListener('resize', () => { - const result = window.innerWidth <= value; - if (result !== threshold) { - setThreshold(result); - } - }); - - return threshold; -}; diff --git a/src/utils/useWhere.ts b/src/utils/useWhere.ts new file mode 100644 index 00000000..1896aa6a --- /dev/null +++ b/src/utils/useWhere.ts @@ -0,0 +1,11 @@ +import { useLocation } from 'react-router-dom'; + +export const useWhere = () => { + const { pathname } = useLocation(); + + return { + isHome: pathname === '/', + isDocs: pathname.startsWith('/docs'), + isOSpy: pathname.startsWith('/o-spy'), + }; +}; diff --git a/yarn.lock b/yarn.lock index 1f619910..b397a052 100644 --- a/yarn.lock +++ b/yarn.lock @@ -721,61 +721,72 @@ "@huolala-tech/page-spy-api-win32-arm" "2.0.0" "@huolala-tech/page-spy-api-win32-arm64" "2.0.0" -"@huolala-tech/page-spy-base@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-base/-/page-spy-base-2.1.1.tgz#0d97032b4c5e2f045113b49722947e756058202c" - integrity sha512-vkAXgo1a7kMsR47UrXAZ0h5/r6AEtjycD6fEv3SCLBcZz0GsolmPnCEXceKpcec+DD518+wSAQEfyanOkrEV5w== +"@huolala-tech/page-spy-base@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-base/-/page-spy-base-2.1.2.tgz#3d13338f3c8fa0f13b037b45b8f3ac5a397d4986" + integrity sha512-cLE7LUUlZhFo7bIOl9qoJ+NV5jWjnfJG7QBlKr2CEeVmApLMtjre/ADrZgrNbL1ber+FH4r+KiT1uGyAPZ6tLw== dependencies: - "@huolala-tech/page-spy-types" "^2.1.1" + "@huolala-tech/page-spy-types" "^2.1.2" -"@huolala-tech/page-spy-browser@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-browser/-/page-spy-browser-2.1.2.tgz#b92bd147c09e316d148ea9db6230f4992fa7d20c" - integrity sha512-KbimibhbZaAtmuk9Ttvs5b9NQNFIjmX0guhLNpzn6+n9AqKp+h4fhFKLl0o6nVPR+Beeg4WvT1xXzv0Y3zC8wA== +"@huolala-tech/page-spy-browser@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-browser/-/page-spy-browser-2.1.3.tgz#2539bc03614f363dd284b75cb3db84a65f051257" + integrity sha512-B8ByAkQ+PN9hF/qjo5t4A1f4Fpppb6xl2++fhJp5lKmtoDEKDaU5S6WaaH/NAnjcKweJFKga+JvGve2ejwpk8A== dependencies: "@babel/runtime" "^7.13.0" - "@huolala-tech/page-spy-base" "^2.1.1" - "@huolala-tech/page-spy-types" "^2.1.1" + "@huolala-tech/page-spy-base" "^2.1.2" + "@huolala-tech/page-spy-types" "^2.1.2" copy-to-clipboard "^3.3.1" iseedeadpeople "^1.0.0" -"@huolala-tech/page-spy-plugin-data-harbor@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-plugin-data-harbor/-/page-spy-plugin-data-harbor-2.1.1.tgz#1f02c4cb4e3db064b8a9e780b36dd2e6a9cb2e68" - integrity sha512-yBR22E9hBojwmuXhNQFWXJYjSONdePzRYmj6th1QD5ZH8xgEFxRSuB6bdPPPuUG8uRtT0YiGejJn1XTC3tO4sw== +"@huolala-tech/page-spy-plugin-data-harbor@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-plugin-data-harbor/-/page-spy-plugin-data-harbor-2.1.2.tgz#2a9de77107ee8041604c5acf1f6e4d37ed10918b" + integrity sha512-C4FEVsSqwqP1At2LDJIAL0Mp27KYrFAe2vwZVr/+UQwzaqV4tvGU98UubTUuxuZIc0gYMoH43+KsDhP3ycVqXw== dependencies: "@babel/runtime" "^7.13.0" - "@huolala-tech/page-spy-base" "^2.1.1" + "@huolala-tech/page-spy-base" "^2.1.2" copy-to-clipboard "^3.3.3" fflate "^0.8.1" -"@huolala-tech/page-spy-plugin-rrweb@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-plugin-rrweb/-/page-spy-plugin-rrweb-2.1.1.tgz#19dbcca2ebb03794ecf82c809bde62f3f72d573a" - integrity sha512-SAABdH1zIUePVpsTOFY+EbroY/DJgWKCvOtFB0hLY8F0Fc3cJG9bVyXu4ImkAcwVgcYZbqiBQIMno+a5jfRhjg== +"@huolala-tech/page-spy-plugin-ospy@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-plugin-ospy/-/page-spy-plugin-ospy-2.1.7.tgz#6d1a08227594e52398f2794f96eff24cbe9fb82b" + integrity sha512-jRm+bdH0KgB+soYkMwfuMpXcJzVQ3hXl9gekIjlUxquiWeooL0Xn6NbE9JVdpXeiO+bhQ2katzgDHCK1LUmlmQ== + dependencies: + "@huolala-tech/page-spy-base" "^2.1.2" + "@huolala-tech/page-spy-browser" "^2.1.3" + "@huolala-tech/page-spy-plugin-data-harbor" "^2.1.2" + "@huolala-tech/page-spy-plugin-rrweb" "^2.1.2" + "@huolala-tech/page-spy-types" "^2.1.2" + +"@huolala-tech/page-spy-plugin-rrweb@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-plugin-rrweb/-/page-spy-plugin-rrweb-2.1.2.tgz#37c74d3e98a2d244299ff41e74f284e35c11b84f" + integrity sha512-W44GnyNn8rdhRUb5+R0TjgKNh4VlQAlPN96YWfR869rTiP+2rnKA8eQOQQXT/ih0aYVxlCqh7dnI/DaVdDot0Q== dependencies: "@babel/runtime" "^7.13.0" - "@huolala-tech/page-spy-base" "^2.1.1" - "@huolala-tech/page-spy-types" "^2.1.1" + "@huolala-tech/page-spy-base" "^2.1.2" + "@huolala-tech/page-spy-types" "^2.1.2" rrweb "^2.0.0-alpha.4" -"@huolala-tech/page-spy-plugin-whole-bundle@^2.1.3": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-plugin-whole-bundle/-/page-spy-plugin-whole-bundle-2.1.3.tgz#7eb6474f94f15b42eb48e33cc836141d87aa6e74" - integrity sha512-shhaHEPfWgZeJTrA59m5n8mfxg7LUhmMSq55yC/nkb8E1AzbSLS72+nm9aZyCKm8aL/4zyOsdEc351aFW3QIQQ== +"@huolala-tech/page-spy-plugin-whole-bundle@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-plugin-whole-bundle/-/page-spy-plugin-whole-bundle-2.1.5.tgz#5e2b1eea030785b0ba6142bdb88f65a4f255a896" + integrity sha512-xLITfQ85wRk9GscGI3SbzYjEVH7vSmGxDEWDBaX7dbP4rnxJM6cYGTC2rg+y0Dvt67R9eU+3oXo9mwkPI1hcGQ== dependencies: - "@huolala-tech/page-spy-base" "^2.1.1" - "@huolala-tech/page-spy-browser" "^2.1.2" - "@huolala-tech/page-spy-plugin-data-harbor" "^2.1.1" - "@huolala-tech/page-spy-plugin-rrweb" "^2.1.1" - "@huolala-tech/page-spy-types" "^2.1.1" + "@huolala-tech/page-spy-base" "^2.1.2" + "@huolala-tech/page-spy-browser" "^2.1.3" + "@huolala-tech/page-spy-plugin-data-harbor" "^2.1.2" + "@huolala-tech/page-spy-plugin-rrweb" "^2.1.2" + "@huolala-tech/page-spy-types" "^2.1.2" -"@huolala-tech/page-spy-types@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-types/-/page-spy-types-2.1.1.tgz#7429d3f012f30cffd1c458c84ba771337d0ed4b5" - integrity sha512-kbQ5kvNuMF1bAIHuxZE08pjPR+cHKgsul6tBwTs0I6x4e1FOGXbRcPPKZ6OvsH9rOTI1g1vDKtylpCyi1TydFg== +"@huolala-tech/page-spy-types@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@huolala-tech/page-spy-types/-/page-spy-types-2.1.2.tgz#261f44391a9715613eaaf0353a080ef1c6e7e2f6" + integrity sha512-Zh9MPcwGkHvrXXPHXFq26CAjLUd2GO0FfOfAUTJbonsvvNV0kl+jljUlsdgy2HPSqApqXqxBOOAGwULUAqFZng== dependencies: - "@huolala-tech/page-spy-base" "^2.1.1" + "@huolala-tech/page-spy-base" "^2.1.2" "@huolala-tech/react-json-view@^1.2.5": version "1.2.5"