diff --git a/app/components/AsciinemaPlayer.jsx b/app/components/AsciinemaPlayer.jsx
new file mode 100644
index 00000000..fe9e8fea
--- /dev/null
+++ b/app/components/AsciinemaPlayer.jsx
@@ -0,0 +1,21 @@
+'use client'
+import React, { useEffect, useRef } from 'react';
+import 'asciinema-player/dist/bundle/asciinema-player.css';
+
+const AsciinemaPlayer = ({ src, options }) => {
+ const playerRef = useRef(null);
+ const hasInitialized = useRef(false);
+ useEffect(() => {
+ // 动态导入 asciinema-player 以确保它只在客户端加载
+ import('asciinema-player').then((asciinemaPlayer) => {
+ if (playerRef.current && !hasInitialized.current) {
+ asciinemaPlayer.create(src, playerRef.current, options);
+ hasInitialized.current = true;
+ }
+ });
+ }, []);
+
+ return
;
+};
+
+export default AsciinemaPlayer;
\ No newline at end of file
diff --git a/app/demo/page.jsx b/app/demo/page.jsx
new file mode 100644
index 00000000..feafd48b
--- /dev/null
+++ b/app/demo/page.jsx
@@ -0,0 +1,10 @@
+import AsciinemaPlayer from '@/app/components/AsciinemaPlayer'
+export default function() {
+ return <>
+
+ >
+}
\ No newline at end of file
diff --git a/app/globals.css b/app/globals.css
index e9a4e08c..7baa21c8 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -200,4 +200,9 @@ nav>ol.toc-level-1 {
.toc-wrapper .toc a {
font-weight: 700;
color: var(--w-indigo-light);
+}
+
+.markdown-body .ap-player pre {
+ margin: 0;
+ padding: 0;
}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 41baa621..5645085e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,6 +17,7 @@
"@react-types/shared": "^3.23.1",
"@types/mdx": "^2.0.13",
"antd": "^5.18.0",
+ "asciinema-player": "^3.7.1",
"clsx": "^2.1.1",
"esbuild": "^0.21.4",
"esbuild-plugin-css-modules": "^0.3.0",
@@ -3978,6 +3979,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/asciinema-player": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/asciinema-player/-/asciinema-player-3.7.1.tgz",
+ "integrity": "sha512-zDJteGjBzNQhHEnD0aG7GqV3E53sOyKb1WCxKNRm2PquU70Lq3s4xxb91wyDS0hBJ3J/TB8aY3y8gjGPN+T23A==",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0",
+ "solid-js": "^1.3.0"
+ }
+ },
"node_modules/assert": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
@@ -10881,6 +10891,25 @@
"randombytes": "^2.1.0"
}
},
+ "node_modules/seroval": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.0.7.tgz",
+ "integrity": "sha512-n6ZMQX5q0Vn19Zq7CIKNIo7E75gPkGCFUEqDpa8jgwpYr/vScjqnQ6H09t1uIiZ0ZSK0ypEGvrYK2bhBGWsGdw==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/seroval-plugins": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.0.7.tgz",
+ "integrity": "sha512-GO7TkWvodGp6buMEX9p7tNyIkbwlyuAWbI6G9Ec5bhcm7mQdu3JOK1IXbEUwb3FVzSc363GraG/wLW23NSavIw==",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "seroval": "^1.0"
+ }
+ },
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -10980,6 +11009,16 @@
"node": ">=8"
}
},
+ "node_modules/solid-js": {
+ "version": "1.8.17",
+ "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.17.tgz",
+ "integrity": "sha512-E0FkUgv9sG/gEBWkHr/2XkBluHb1fkrHywUgA6o6XolPDCJ4g1HaLmQufcBBhiF36ee40q+HpG/vCZu7fLpI3Q==",
+ "dependencies": {
+ "csstype": "^3.1.0",
+ "seroval": "^1.0.4",
+ "seroval-plugins": "^1.0.3"
+ }
+ },
"node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
diff --git a/package.json b/package.json
index 1071cd37..0d63afdd 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
"@react-types/shared": "^3.23.1",
"@types/mdx": "^2.0.13",
"antd": "^5.18.0",
+ "asciinema-player": "^3.7.1",
"clsx": "^2.1.1",
"esbuild": "^0.21.4",
"esbuild-plugin-css-modules": "^0.3.0",