wind 11 months ago
parent
commit
ffd41453d7

+ 422 - 3
package-lock.json

@@ -8,19 +8,27 @@
       "name": "mindweb-next",
       "version": "0.1.0",
       "dependencies": {
+        "@headlessui/react": "^2.2.0",
+        "clsx": "^2.1.1",
         "next": "15.1.3",
+        "next-themes": "^0.4.4",
         "react": "^19.0.0",
-        "react-dom": "^19.0.0"
+        "react-animate-height": "^3.2.3",
+        "react-dom": "^19.0.0",
+        "swiper": "^11.1.15",
+        "tailwind-merge": "^2.6.0"
       },
       "devDependencies": {
         "@eslint/eslintrc": "^3",
+        "@tailwindcss/typography": "^0.5.15",
         "@types/node": "^20",
         "@types/react": "^19",
         "@types/react-dom": "^19",
+        "autoprefixer": "^10.4.20",
         "eslint": "^9",
         "eslint-config-next": "15.1.3",
-        "postcss": "^8",
-        "tailwindcss": "^3.4.1",
+        "postcss": "^8.4.49",
+        "tailwindcss": "^3.4.17",
         "typescript": "^5"
       }
     },
@@ -163,6 +171,72 @@
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
+    "node_modules/@floating-ui/core": {
+      "version": "1.6.8",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.6.8.tgz",
+      "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==",
+      "dependencies": {
+        "@floating-ui/utils": "^0.2.8"
+      }
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.6.12",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.6.12.tgz",
+      "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==",
+      "dependencies": {
+        "@floating-ui/core": "^1.6.0",
+        "@floating-ui/utils": "^0.2.8"
+      }
+    },
+    "node_modules/@floating-ui/react": {
+      "version": "0.26.28",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/react/-/react-0.26.28.tgz",
+      "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==",
+      "dependencies": {
+        "@floating-ui/react-dom": "^2.1.2",
+        "@floating-ui/utils": "^0.2.8",
+        "tabbable": "^6.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0",
+        "react-dom": ">=16.8.0"
+      }
+    },
+    "node_modules/@floating-ui/react-dom": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz",
+      "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==",
+      "dependencies": {
+        "@floating-ui/dom": "^1.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0",
+        "react-dom": ">=16.8.0"
+      }
+    },
+    "node_modules/@floating-ui/utils": {
+      "version": "0.2.8",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.8.tgz",
+      "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="
+    },
+    "node_modules/@headlessui/react": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/@headlessui/react/-/react-2.2.0.tgz",
+      "integrity": "sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==",
+      "dependencies": {
+        "@floating-ui/react": "^0.26.16",
+        "@react-aria/focus": "^3.17.1",
+        "@react-aria/interactions": "^3.21.3",
+        "@tanstack/react-virtual": "^3.8.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "react": "^18 || ^19 || ^19.0.0-rc",
+        "react-dom": "^18 || ^19 || ^19.0.0-rc"
+      }
+    },
     "node_modules/@humanfs/core": {
       "version": "0.19.1",
       "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz",
@@ -819,6 +893,83 @@
         "node": ">=14"
       }
     },
+    "node_modules/@react-aria/focus": {
+      "version": "3.19.0",
+      "resolved": "https://registry.npmmirror.com/@react-aria/focus/-/focus-3.19.0.tgz",
+      "integrity": "sha512-hPF9EXoUQeQl1Y21/rbV2H4FdUR2v+4/I0/vB+8U3bT1CJ+1AFj1hc/rqx2DqEwDlEwOHN+E4+mRahQmlybq0A==",
+      "dependencies": {
+        "@react-aria/interactions": "^3.22.5",
+        "@react-aria/utils": "^3.26.0",
+        "@react-types/shared": "^3.26.0",
+        "@swc/helpers": "^0.5.0",
+        "clsx": "^2.0.0"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+      }
+    },
+    "node_modules/@react-aria/interactions": {
+      "version": "3.22.5",
+      "resolved": "https://registry.npmmirror.com/@react-aria/interactions/-/interactions-3.22.5.tgz",
+      "integrity": "sha512-kMwiAD9E0TQp+XNnOs13yVJghiy8ET8L0cbkeuTgNI96sOAp/63EJ1FSrDf17iD8sdjt41LafwX/dKXW9nCcLQ==",
+      "dependencies": {
+        "@react-aria/ssr": "^3.9.7",
+        "@react-aria/utils": "^3.26.0",
+        "@react-types/shared": "^3.26.0",
+        "@swc/helpers": "^0.5.0"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+      }
+    },
+    "node_modules/@react-aria/ssr": {
+      "version": "3.9.7",
+      "resolved": "https://registry.npmmirror.com/@react-aria/ssr/-/ssr-3.9.7.tgz",
+      "integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==",
+      "dependencies": {
+        "@swc/helpers": "^0.5.0"
+      },
+      "engines": {
+        "node": ">= 12"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+      }
+    },
+    "node_modules/@react-aria/utils": {
+      "version": "3.26.0",
+      "resolved": "https://registry.npmmirror.com/@react-aria/utils/-/utils-3.26.0.tgz",
+      "integrity": "sha512-LkZouGSjjQ0rEqo4XJosS4L3YC/zzQkfRM3KoqK6fUOmUJ9t0jQ09WjiF+uOoG9u+p30AVg3TrZRUWmoTS+koQ==",
+      "dependencies": {
+        "@react-aria/ssr": "^3.9.7",
+        "@react-stately/utils": "^3.10.5",
+        "@react-types/shared": "^3.26.0",
+        "@swc/helpers": "^0.5.0",
+        "clsx": "^2.0.0"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+      }
+    },
+    "node_modules/@react-stately/utils": {
+      "version": "3.10.5",
+      "resolved": "https://registry.npmmirror.com/@react-stately/utils/-/utils-3.10.5.tgz",
+      "integrity": "sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==",
+      "dependencies": {
+        "@swc/helpers": "^0.5.0"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+      }
+    },
+    "node_modules/@react-types/shared": {
+      "version": "3.26.0",
+      "resolved": "https://registry.npmmirror.com/@react-types/shared/-/shared-3.26.0.tgz",
+      "integrity": "sha512-6FuPqvhmjjlpEDLTiYx29IJCbCNWPlsyO+ZUmCUXzhUv2ttShOXfw8CmeHWHftT/b2KweAWuzqSlfeXPR76jpw==",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
+      }
+    },
     "node_modules/@rtsao/scc": {
       "version": "1.1.0",
       "resolved": "https://registry.npmmirror.com/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -844,6 +995,59 @@
         "tslib": "^2.8.0"
       }
     },
+    "node_modules/@tailwindcss/typography": {
+      "version": "0.5.15",
+      "resolved": "https://registry.npmmirror.com/@tailwindcss/typography/-/typography-0.5.15.tgz",
+      "integrity": "sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==",
+      "dev": true,
+      "dependencies": {
+        "lodash.castarray": "^4.4.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.merge": "^4.6.2",
+        "postcss-selector-parser": "6.0.10"
+      },
+      "peerDependencies": {
+        "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20"
+      }
+    },
+    "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": {
+      "version": "6.0.10",
+      "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+      "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+      "dev": true,
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@tanstack/react-virtual": {
+      "version": "3.11.2",
+      "resolved": "https://registry.npmmirror.com/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz",
+      "integrity": "sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ==",
+      "dependencies": {
+        "@tanstack/virtual-core": "3.11.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
+    "node_modules/@tanstack/virtual-core": {
+      "version": "3.11.2",
+      "resolved": "https://registry.npmmirror.com/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz",
+      "integrity": "sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/tannerlinsley"
+      }
+    },
     "node_modules/@types/estree": {
       "version": "1.0.6",
       "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz",
@@ -1372,6 +1576,43 @@
       "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
       "dev": true
     },
+    "node_modules/autoprefixer": {
+      "version": "10.4.20",
+      "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.20.tgz",
+      "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "browserslist": "^4.23.3",
+        "caniuse-lite": "^1.0.30001646",
+        "fraction.js": "^4.3.7",
+        "normalize-range": "^0.1.2",
+        "picocolors": "^1.0.1",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "bin": {
+        "autoprefixer": "bin/autoprefixer"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
     "node_modules/available-typed-arrays": {
       "version": "1.0.7",
       "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -1445,6 +1686,38 @@
         "node": ">=8"
       }
     },
+    "node_modules/browserslist": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.3.tgz",
+      "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "caniuse-lite": "^1.0.30001688",
+        "electron-to-chromium": "^1.5.73",
+        "node-releases": "^2.0.19",
+        "update-browserslist-db": "^1.1.1"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
     "node_modules/busboy": {
       "version": "1.6.0",
       "resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz",
@@ -1597,6 +1870,14 @@
       "resolved": "https://registry.npmmirror.com/client-only/-/client-only-0.0.1.tgz",
       "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
     },
+    "node_modules/clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/color": {
       "version": "4.2.3",
       "resolved": "https://registry.npmmirror.com/color/-/color-4.2.3.tgz",
@@ -1852,6 +2133,12 @@
       "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
       "dev": true
     },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.76",
+      "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz",
+      "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==",
+      "dev": true
+    },
     "node_modules/emoji-regex": {
       "version": "9.2.2",
       "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -2032,6 +2319,15 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/escape-string-regexp": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -2615,6 +2911,19 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/fraction.js": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz",
+      "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "type": "patreon",
+        "url": "https://github.com/sponsors/rawify"
+      }
+    },
     "node_modules/fsevents": {
       "version": "2.3.3",
       "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
@@ -3548,6 +3857,18 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/lodash.castarray": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmmirror.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
+      "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
+      "dev": true
+    },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "dev": true
+    },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -3726,6 +4047,15 @@
         }
       }
     },
+    "node_modules/next-themes": {
+      "version": "0.4.4",
+      "resolved": "https://registry.npmmirror.com/next-themes/-/next-themes-0.4.4.tgz",
+      "integrity": "sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==",
+      "peerDependencies": {
+        "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
+        "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
+      }
+    },
     "node_modules/next/node_modules/postcss": {
       "version": "8.4.31",
       "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz",
@@ -3753,6 +4083,12 @@
         "node": "^10 || ^12 || >=14"
       }
     },
+    "node_modules/node-releases": {
+      "version": "2.0.19",
+      "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz",
+      "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+      "dev": true
+    },
     "node_modules/normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -3762,6 +4098,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/normalize-range": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz",
+      "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
@@ -4251,6 +4596,18 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/react-animate-height": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/react-animate-height/-/react-animate-height-3.2.3.tgz",
+      "integrity": "sha512-R6DSvr7ud07oeCixScyvXWEMJY/Mt2+GyOWC1KMaRc69gOBw+SsCg4TJmrp4rKUM1hyd6p+YKw90brjPH93Y2A==",
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "peerDependencies": {
+        "react": ">=16.8.0",
+        "react-dom": ">=16.8.0"
+      }
+    },
     "node_modules/react-dom": {
       "version": "19.0.0",
       "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.0.0.tgz",
@@ -4968,6 +5325,38 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/swiper": {
+      "version": "11.1.15",
+      "resolved": "https://registry.npmmirror.com/swiper/-/swiper-11.1.15.tgz",
+      "integrity": "sha512-IzWeU34WwC7gbhjKsjkImTuCRf+lRbO6cnxMGs88iVNKDwV+xQpBCJxZ4bNH6gSrIbbyVJ1kuGzo3JTtz//CBw==",
+      "funding": [
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/swiperjs"
+        },
+        {
+          "type": "open_collective",
+          "url": "http://opencollective.com/swiper"
+        }
+      ],
+      "engines": {
+        "node": ">= 4.7.0"
+      }
+    },
+    "node_modules/tabbable": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/tabbable/-/tabbable-6.2.0.tgz",
+      "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="
+    },
+    "node_modules/tailwind-merge": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
+      "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/dcastil"
+      }
+    },
     "node_modules/tailwindcss": {
       "version": "3.4.17",
       "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.17.tgz",
@@ -5233,6 +5622,36 @@
       "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
       "dev": true
     },
+    "node_modules/update-browserslist-db": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
+      "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.0"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
     "node_modules/uri-js": {
       "version": "4.4.1",
       "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",

+ 13 - 5
package.json

@@ -9,19 +9,27 @@
     "lint": "next lint"
   },
   "dependencies": {
+    "@headlessui/react": "^2.2.0",
+    "clsx": "^2.1.1",
+    "next": "15.1.3",
+    "next-themes": "^0.4.4",
     "react": "^19.0.0",
+    "react-animate-height": "^3.2.3",
     "react-dom": "^19.0.0",
-    "next": "15.1.3"
+    "swiper": "^11.1.15",
+    "tailwind-merge": "^2.6.0"
   },
   "devDependencies": {
-    "typescript": "^5",
+    "@eslint/eslintrc": "^3",
+    "@tailwindcss/typography": "^0.5.15",
     "@types/node": "^20",
     "@types/react": "^19",
     "@types/react-dom": "^19",
-    "postcss": "^8",
-    "tailwindcss": "^3.4.1",
+    "autoprefixer": "^10.4.20",
     "eslint": "^9",
     "eslint-config-next": "15.1.3",
-    "@eslint/eslintrc": "^3"
+    "postcss": "^8.4.49",
+    "tailwindcss": "^3.4.17",
+    "typescript": "^5"
   }
 }

+ 6 - 0
postcss.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  },
+}

File diff suppressed because it is too large
+ 6 - 0
public/404.svg


BIN
public/QRCode.jpg


BIN
public/demo-illustration.jpg


BIN
public/demo-illustration1.jpg


BIN
public/demo-illustration2.jpg


+ 0 - 1
public/file.svg

@@ -1 +0,0 @@
-<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

+ 0 - 1
public/globe.svg

@@ -1 +0,0 @@
-<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

BIN
public/hero.jpg


File diff suppressed because it is too large
+ 16 - 0
public/mind-journey-logo.svg


+ 0 - 1
public/next.svg

@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

+ 0 - 1
public/vercel.svg

@@ -1 +0,0 @@
-<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

+ 0 - 1
public/window.svg

@@ -1 +0,0 @@
-<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

+ 18 - 0
src/app/about/page.tsx

@@ -0,0 +1,18 @@
+import { Metadata } from "next";
+import Page from "@/components/Page";
+import InformationSection from "@/components/InformationSection";
+
+export const metadata: Metadata = {
+  title: "关于我们 | 心境奇旅",
+  description: "心境奇旅是一家专注于心理健康领域的科技创新型企业。",
+};
+
+export default function AboutPage() {
+  return (
+    <Page title="关于我们">
+      <div className="flex flex-col lg:flex-row">
+        <InformationSection />
+      </div>
+    </Page>
+  );
+}

BIN
src/app/favicon.ico


+ 6 - 7
src/app/globals.css

@@ -3,19 +3,18 @@
 @tailwind utilities;
 
 :root {
-  --background: #ffffff;
-  --foreground: #171717;
+  --foreground-rgb: 0, 0, 0;
+  --background-rgb: 255, 255, 255;
 }
 
 @media (prefers-color-scheme: dark) {
   :root {
-    --background: #0a0a0a;
-    --foreground: #ededed;
+    --foreground-rgb: 255, 255, 255;
+    --background-rgb: 10, 10, 10;
   }
 }
 
 body {
-  color: var(--foreground);
-  background: var(--background);
-  font-family: Arial, Helvetica, sans-serif;
+  color: rgb(var(--foreground-rgb));
+  background: rgb(var(--background-rgb));
 }

+ 21 - 26
src/app/layout.tsx

@@ -1,34 +1,29 @@
-import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
-import "./globals.css";
+import './globals.css'
+import Navigation from '@/components/Navigation'
+import Footer from '@/components/Footer'
+import { ThemeProvider } from '@/components/ThemeProvider'
 
-const geistSans = Geist({
-  variable: "--font-geist-sans",
-  subsets: ["latin"],
-});
-
-const geistMono = Geist_Mono({
-  variable: "--font-geist-mono",
-  subsets: ["latin"],
-});
-
-export const metadata: Metadata = {
-  title: "Create Next App",
-  description: "Generated by create next app",
-};
+export const metadata = {
+  title: '心境奇旅 APP - 专注心理健康',
+  description: '心境奇旅是一款专注于心理健康的app,通过心理练习、心情记录和心理测评帮助你提升心理健康水平。',
+}
 
 export default function RootLayout({
   children,
-}: Readonly<{
-  children: React.ReactNode;
-}>) {
+}: {
+  children: React.ReactNode
+}) {
   return (
-    <html lang="en">
-      <body
-        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
-      >
-        {children}
+    <html lang="zh" suppressHydrationWarning>
+      <body className="min-h-screen flex flex-col">
+        <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
+          <Navigation />
+          <main className="flex-grow">
+            {children}
+          </main>
+          <Footer />
+        </ThemeProvider>
       </body>
     </html>
-  );
+  )
 }

+ 7 - 94
src/app/page.tsx

@@ -1,101 +1,14 @@
 import Image from "next/image";
+import Hero from "@/components/Hero";
+import FeaturesGallery from "@/components/FeaturesGallery";
+import Testimonials from "@/components/Testimonials";
 
 export default function Home() {
   return (
-    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
-      <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
-        <Image
-          className="dark:invert"
-          src="/next.svg"
-          alt="Next.js logo"
-          width={180}
-          height={38}
-          priority
-        />
-        <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
-          <li className="mb-2">
-            Get started by editing{" "}
-            <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
-              src/app/page.tsx
-            </code>
-            .
-          </li>
-          <li>Save and see your changes instantly.</li>
-        </ol>
-
-        <div className="flex gap-4 items-center flex-col sm:flex-row">
-          <a
-            className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
-            href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-            target="_blank"
-            rel="noopener noreferrer"
-          >
-            <Image
-              className="dark:invert"
-              src="/vercel.svg"
-              alt="Vercel logomark"
-              width={20}
-              height={20}
-            />
-            Deploy now
-          </a>
-          <a
-            className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
-            href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-            target="_blank"
-            rel="noopener noreferrer"
-          >
-            Read our docs
-          </a>
-        </div>
-      </main>
-      <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
-        <a
-          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
-          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          <Image
-            aria-hidden
-            src="/file.svg"
-            alt="File icon"
-            width={16}
-            height={16}
-          />
-          Learn
-        </a>
-        <a
-          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
-          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          <Image
-            aria-hidden
-            src="/window.svg"
-            alt="Window icon"
-            width={16}
-            height={16}
-          />
-          Examples
-        </a>
-        <a
-          className="flex items-center gap-2 hover:underline hover:underline-offset-4"
-          href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
-          target="_blank"
-          rel="noopener noreferrer"
-        >
-          <Image
-            aria-hidden
-            src="/globe.svg"
-            alt="Globe icon"
-            width={16}
-            height={16}
-          />
-          Go to nextjs.org →
-        </a>
-      </footer>
+    <div className="space-y-8 pb-5 pt-20 sm:pb-12 sm:pt-20 lg:pb-12 lg:pt-20">
+      <Hero />
+      <FeaturesGallery />
+      <Testimonials />
     </div>
   );
 }

+ 234 - 0
src/app/privacy-policy/page.tsx

@@ -0,0 +1,234 @@
+import { Metadata } from "next";
+import Page from "@/components/Page";
+import RichText from "@/components/RichText";
+
+export const metadata: Metadata = {
+  title: "隐私协议 | 心境奇旅",
+  description: "心境奇旅App的隐私政策说明",
+};
+
+export default function PrivacyPolicyPage() {
+  return (
+    <Page title="隐私协议">
+      <div className="max-w-4xl mx-auto">
+        <RichText>
+          <h2>导言</h2>
+          <p>
+            <i>心境奇旅</i> 是一款由{" "}
+            <strong>
+              <i>北京心境奇旅科技有限公司</i>
+            </strong>{" "}
+            (以下简称”我们“)提供的产品。
+            您在使用我们的服务时,我们可能会收集和使用您的相关信息。我们希望通过本《隐私政策》向您说明,在使用我们的服务时,我们如何收集、使用、储存和分享这些信息,以及我们为您提供的访问、更新、控制和保护这些信息的方式。
+            本《隐私政策》与您所使用的 <i>心境奇旅</i>
+            服务息息相关,希望您仔细阅读,在需要时,按照本《隐私政策》的指引,作出您认为适当的选择。本《隐私政策》中涉及的相关技术词汇,我们尽量以简明扼要的表述,并提供进一步说明的链接,以便您的理解。
+          </p>
+          <p>
+            <strong>
+              您使用或继续使用我们的服务,即意味着同意我们按照本《隐私政策》收集、使用、储存和分享您的相关信息。
+            </strong>
+          </p>
+          <p>
+            如对本《隐私政策》或相关事宜有任何问题,请通过 <strong>service@mindjourneytech.com</strong> 与我们联系。
+          </p>
+
+          <h2>1. 我们收集的信息</h2>
+          <p>
+            我们或我们的第三方合作伙伴提供服务时,可能会收集、储存和使用下列与您有关的信息。如果您不提供相关信息,可能无法注册成为我们的用户或无法享受我们提供的某些服务,或者无法达到相关服务拟达到的效果。
+          </p>
+
+          <ul>
+            <li>
+              <strong>位置信息</strong>
+              ,指您开启设备定位功能并使用我们基于位置提供的相关服务时,收集的有关您位置的信息,包括:
+              <ul>
+                <li>您通过具有定位功能的移动设备使用我们的服务时,通过GPS或WiFi等方式收集的您的地理位置信息;</li>
+                <li>您可以通过关闭定位功能,停止对您的地理位置信息的收集。</li>
+              </ul>
+            </li>
+            <li>
+              <strong>获取存储权限个人信息</strong>
+              ,你需授权存储权限,用于您将应用内上传头像,点击分享生成的图片保存到手机相册中,收集您的个人信息,如昵称、头像,用于图片中信息展示.
+            </li>
+          </ul>
+
+          <p>个性化内容推荐:</p>
+          <ul>
+            <li>
+              我们的应用包含定向推送的功能,旨在为您提供更贴近个性化需求的推荐内容。我们会根据您的兴趣、偏好和行为习惯进行推荐,以提升您的使用体验
+            </li>
+            <li>
+              我们承诺严格遵守相关法律法规,保护您的个人信息安全。我们不会未经您的同意向第三方透露您的个人信息,也不会将您的个人信息用于非法用途
+            </li>
+            <li>
+              您可以在应用中随时查看和修改个人信息的收集和使用权限,也可以选择在我的-设置-隐私设置中关闭定向推送的功能
+            </li>
+          </ul>
+
+          <p>我们接入的第三方SDK:</p>
+          <h3>友盟+统计SDK</h3>
+          <ul>
+            <li>使用场景:因需要统计用户数量及应用崩溃信息,本应用集成了友盟+统计SDK</li>
+            <li>
+              涉及个人信息: 友盟+SDK需要“获取设备序列号”、设备Mac地址、唯一设备识别码(IMEI/android
+              ID/IDFA/OPENUDID/GUID、SIM 卡 IMSI
+              信息)以提供统计分析服务,并通过地理位置校准报表数据准确性,提供基础反作弊能力
+            </li>
+            <li>合作方主体:友盟同欣(北京)科技有限公司</li>
+            <li>共享方式:SDK采集</li>
+            <li>官网链接: https://www.umeng.com/</li>
+            <li>隐私权政策链接: https://www.umeng.com/page/policy</li>
+          </ul>
+          <h3>小米推送SDK</h3>
+          <ul>
+            <li>使用场景:向用户发送推送通知,包括但不限于消息提醒、应用提示等功能</li>
+            <li>
+              涉及个人信息: 小米推送SDK需要“获取设备序列号”、设备Mac地址、唯一设备识别码(IMEI/android
+              ID/IDFA/OPENUDID/GUID、SIM 卡 IMSI 信息)以提供推送服务
+            </li>
+            <li>合作方主体:小米科技有限公司</li>
+            <li>共享方式:SDK采集</li>
+            <li>官网链接: https://www.mi.com/</li>
+          </ul>
+          <h3>OPPOIOPush SDK</h3>
+          <ul>
+            <li>使用场景:向用户发送推送通知,包括但不限于消息提醒、应用提示等功能</li>
+            <li>
+              涉及个人信息: OPPOIOPush SDK需要“获取设备序列号”、设备Mac地址、唯一设备识别码(IMEI/android
+              ID/IDFA/OPENUDID/GUID、SIM 卡 IMSI 信息)以提供推送服务
+            </li>
+            <li>合作方主体:OPPO科技有限公司</li>
+            <li>共享方式:SDK采集</li>
+            <li>官网链接: https://www.oppo.com/</li>
+          </ul>
+
+          <h2>2. 信息的存储</h2>
+          <strong>2.1 信息存储的方式和期限</strong>
+          <ul>
+            <li>我们会通过安全的方式存储您的信息,包括本地存储(例如利用APP进行数据缓存)、数据库和服务器日志。</li>
+            <li>一般情况下,我们只会在为实现服务目的所必需的时间内或法律法规规定的条件下存储您的个人信息。</li>
+          </ul>
+
+          <strong>2.2 信息存储的地域</strong>
+          <ul>
+            <li>我们会按照法律法规规定,将境内收集的用户个人信息存储于中国境内。</li>
+            <li>
+              目前我们不会跨境传输或存储您的个人信息。将来如需跨境传输或存储的,我们会向您告知信息出境的目的、接收方、安全保证措施和安全风险,并征得您的同意。
+            </li>
+          </ul>
+
+          <strong>2.3 产品或服务停止运营时的通知</strong>
+          <ul>
+            <li>
+              当我们的产品或服务发生停止运营的情况时,我们将以推送通知、公告等形式通知您,并在合理期限内删除您的个人信息或进行匿名化处理,法律法规另有规定的除外。
+            </li>
+          </ul>
+
+          <h2>3. 信息安全</h2>
+          <p>
+            我们使用各种安全技术和程序,以防信息的丢失、不当使用、未经授权阅览或披露。例如,在某些服务中,我们将利用加密技术(例如SSL)来保护您提供的个人信息。但请您理解,由于技术的限制以及可能存在的各种恶意手段,在互联网行业,即便竭尽所能加强安全措施,也不可能始终保证信息百分之百的安全。您需要了解,您接入我们的服务所用的系统和通讯网络,有可能因我们可控范围外的因素而出现问题。
+          </p>
+
+          <h2>4. 我们如何使用信息</h2>
+          <p>我们可能将在向您提供服务的过程之中所收集的信息用作下列用途:</p>
+          <ul>
+            <li>向您提供服务;</li>
+            <li>
+              在我们提供服务时,用于身份验证、客户服务、安全防范、诈骗监测、存档和备份用途,确保我们向您提供的产品和服务的安全性;
+            </li>
+            <li>帮助我们设计新服务,改善我们现有服务;</li>
+            <li>
+              使我们更加了解您如何接入和使用我们的服务,从而针对性地回应您的个性化需求,例如语言设定、位置设定、个性化的帮助服务和指示,或对您和其他用户作出其他方面的回应;
+            </li>
+            <li>向您提供与您更加相关的广告以替代普遍投放的广告;</li>
+            <li>评估我们服务中的广告和其他促销及推广活动的效果,并加以改善;</li>
+            <li>软件认证或管理软件升级;</li>
+            <li>让您参与有关我们产品和服务的调查。</li>
+          </ul>
+
+          <h2>5. 信息共享</h2>
+          <p>
+            目前,我们不会主动共享或转让您的个人信息至第三方,如存在其他共享或转让您的个人信息或您需要我们将您的个人信息共享或转让至第三方情形时,我们会直接或确认第三方征得您对上述行为的明示同意。
+          </p>
+          <p>
+            为了投放广告,评估、优化广告投放效果等目的,我们需要向广告主及其代理商等第三方合作伙伴共享您的部分数据,要求其严格遵守我们关于数据隐私保护的措施与要求,包括但不限于根据数据保护协议、承诺书及相关数据处理政策进行处理,避免识别出个人身份,保障隐私安全。
+          </p>
+          <p>我们不会向合作伙伴分享可用于识别您个人身份的信息(例如您的姓名或电子邮件地址),除非您明确授权。</p>
+          <p>
+            我们不会对外公开披露所收集的个人信息,如必须公开披露时,我们会向您告知此次公开披露的目的、披露信息的类型及可能涉及的敏感信息,并征得您的明示同意。
+          </p>
+          <p>
+            随着我们业务的持续发展,我们有可能进行合并、收购、资产转让等交易,我们将告知您相关情形,按照法律法规及不低于本《隐私政策》所要求的标准继续保护或要求新的控制者继续保护您的个人信息。
+          </p>
+          <p>
+            另外,根据相关法律法规及国家标准,以下情形中,我们可能会共享、转让、公开披露个人信息无需事先征得您的授权同意:
+          </p>
+          <ul>
+            <li>与国家安全、国防安全直接相关的;</li>
+            <li>与公共安全、公共卫生、重大公共利益直接相关的;</li>
+            <li>犯罪侦查、起诉、审判和判决执行等直接相关的;</li>
+            <li>出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;</li>
+            <li>个人信息主体自行向社会公众公开个人信息的;</li>
+            <li>从合法公开披露的信息中收集个人信息的,如合法的新闻报道、政府信息公开等渠道。</li>
+          </ul>
+
+          <h2>6. 您的权利</h2>
+          <p>
+            在您使用我们的服务期间,我们可能会视产品具体情况为您提供相应的操作设置,以便您可以查询、删除、更正或撤回您的相关个人信息,您可参考相应的具体指引进行操作。此外,我们还设置了投诉举报渠道,您的意见将会得到及时的处理。如果您无法通过上述途径和方式行使您的个人信息主体权利,您可以通过本《隐私政策》中提供的联系方式提出您的请求,我们会按照法律法规的规定予以反馈。
+          </p>
+
+          <h2>7. 变更</h2>
+          <p>
+            我们可能适时修订本《隐私政策》的条款。当变更发生时,我们会在版本更新时向您提示新的《隐私政策》,并向您说明生效日期。请您仔细阅读变更后的《隐私政策》内容,
+            <strong>若您继续使用我们的服务,即表示您同意我们按照更新后的《隐私政策》处理您的个人信息。</strong>
+          </p>
+
+          <h2>8. 未成年人保护</h2>
+          <p>
+            我们鼓励父母或监护人指导未满十八岁的未成年人使用我们的服务。我们建议未成年人鼓励他们的父母或监护人阅读本《隐私政策》,并建议未成年人在提交的个人信息之前寻求父母或监护人的同意和指导。
+          </p>
+          <h2>9. 注销账户</h2>
+          <p>
+            您有权注销账号,具体流程如下:在主页面点击我的,点击设置,点击注销账号。您注销成功后,我们将根据法律法规的要求删除您的个人信息。
+          </p>
+          <h2>10. 自启动和关联启动</h2>
+          <h3>自启动</h3>
+          <p>
+            1.
+            场景:本应用包含每日一句小组件功能。当您将此小组件添加到设备主屏幕时,为了能够及时更新每日一句的内容,应用可能会自启动。例如,每天清晨,应用自启动以获取新的每日一句语句,从而为您在小组件上展示最新的内容。
+          </p>
+          <p>
+            2.
+            目的:自启动的目的在于确保小组件能够为您持续提供新鲜、有价值的每日一句内容。这有助于您每天在不打开应用的情况下,直接从小组件获取积极向上或富有哲理的语句,丰富您的日常体验。
+          </p>
+          <p>
+            3.
+            规则:自启动规则如下:仅在您已添加每日一句小组件且设备处于联网状态时,应用会在特定的更新时间(如每天凌晨0点 -
+            3点之间)尝试自启动以更新内容。自启动频率为每天最多一次,且每次自启动时只会执行更新小组件内容的操作,不会进行其他不必要的后台操作。
+          </p>
+          <p>
+            4.
+            必要性:自启动对于每日一句小组件功能是必要的。如果没有自启动功能,小组件内容将无法及时更新,您可能会持续看到陈旧的语句,影响您对该功能的正常使用体验。这一功能旨在为您提供便捷的、每日更新的内容服务,自启动是实现这一目标的关键环节。
+          </p>
+          <h3>关联启动</h3>
+          <p>
+            1.
+            场景:当您在本应用内进行支付操作并且选择微信支付方式时,本应用将关联启动微信支付应用程序,以便您完成支付流程。另外,当您使用本应用的分享功能,并且选择分享到微信(如分享文章、图片等内容)时,本应用会关联启动微信应用。
+          </p>
+          <p>
+            2.
+            目的:关联启动微信支付的目的是为了给您提供安全、便捷的支付渠道。借助微信支付广泛的支付网络和安全保障,让您能够快速、放心地完成在本应用内的购买或消费行为。而关联启动微信进行分享的目的是利用微信庞大的社交网络,方便您将本应用内的有趣内容分享给您的微信好友或分享到微信朋友圈等。
+          </p>
+          <p>
+            3.
+            规则:对于微信支付关联启动:当您在本应用的支付页面明确选择微信支付并且您的设备已安装微信支付应用时,将立即关联启动微信支付。在支付完成或取消支付操作后,关联启动的微信支付应用将回到之前的状态(如后台运行或关闭,取决于您的设备设置)。对于微信分享关联启动:当您在本应用内点击分享到微信的按钮并且您的设备已安装微信时,将关联启动微信,引导您至微信的分享界面。
+          </p>
+          <p>
+            4.
+            必要性:关联启动微信支付是必要的,因为它是本应用实现支付功能的重要组成部分。没有这种关联启动,您将无法使用微信支付来完成在本应用中的交易。关联启动微信进行分享也是必要的,它为您提供了一种便捷、高效的分享途径,让您可以轻松地将本应用的内容传播到您的微信社交圈中,增加内容的传播性和应用的用户互动性。
+          </p>
+        </RichText>
+      </div>
+    </Page>
+  );
+}

+ 63 - 0
src/app/user-policy/page.tsx

@@ -0,0 +1,63 @@
+import { Metadata } from 'next';
+import Page from '@/components/Page';
+import RichText from '@/components/RichText';
+
+export const metadata: Metadata = {
+  title: '用户服务协议 | 心境奇旅',
+  description: '心境奇旅App的用户服务协议',
+};
+
+export default function UserPolicyPage() {
+  return (
+    <Page title="用户服务协议">
+      <div className="max-w-4xl mx-auto">
+        <RichText>
+          <p>
+            欢迎使用北京心境奇旅科技有限公司(以下简称"公司")所提供的服务。为使用公司提供的服务,您应当阅读并遵守以下用户协议(以下简称"协议")。
+          </p>
+
+          <h4>一、定义</h4>
+          <p>1.1 本协议中,除非另有定义,下列词语具有如下含义:</p>
+          <p>(1)公司提供的服务:指公司通过其所有的产品和/或服务向用户提供的各项功能和服务。</p>
+          <p>(2)用户:指通过公司的产品和/或服务使用公司提供的服务的个人或组织。</p>
+
+          <h4>二、使用规则</h4>
+          <p>2.1 您在使用公司提供的服务时,应遵守中华人民共和国相关法律法规的规定,不得利用公司的服务从事任何违法活动,包括但不限于:</p>
+          <p>(1)发布、传送、传播、储存侵害他人知识产权、商业秘密等合法权利的内容;</p>
+          <p>(2)发布、传送、传播、储存淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的内容;</p>
+          <p>(3)以任何方式危害国家安全、泄露国家秘密,颠覆国家政权,破坏国家统一的;</p>
+          <p>(4)以任何方式妨害他人合法权益的;</p>
+          <p>(5)发布、传送、传播广告信息及垃圾信息;</p>
+          <p>(6)其他违反中国法律、法规、规章、规范性文件的行为。</p>
+          <p>2.2 您理解并同意,公司有权对您使用公司提供的服务的情况进行审查和监督,如您违反上述规定,公司有权依据协议约定进行处理。</p>
+
+          <h4>三、服务内容</h4>
+          <p>3.1 公司提供的具体服务内容由公司根据实际情况提供,并随时变更、中断或终止部分或全部服务。</p>
+          <p>
+            3.2
+            您理解并同意,公司并不保证提供的服务一定能满足您的要求,也不保证服务不会中断,对于因系统维护或升级的需要而需暂停服务的情况,公司将尽可能事先进行通告。
+          </p>
+
+          <h4>四、隐私保护</h4>
+          <p>4.1 您同意并认可,公司将按照《隐私政策》收集、使用、储存和分享您的个人信息。</p>
+
+          <h4>五、知识产权</h4>
+          <p>5.1 公司提供的服务中包含的所有内容(包括但不限于文字、图片、音频、视频、图表、标识、版面设计、电子文档等)的知识产权均归公司所有。</p>
+
+          <h4>六、争议解决</h4>
+          <p>
+            6.2 如双方就本协议内容或其执行发生任何争议,双方应尽力友好协商解决;协商不成时,任何一方均可向公司所在地的人民法院提起诉讼。
+          </p>
+
+          <h4>七、其他</h4>
+          <p>7.1 本协议构成双方对本协议之约定事项及其他有关事宜的完整协议,除本协议规定的之外,未赋予本协议各方其他权利。</p>
+          <p>7.2 如本协议中的任何条款无论因何种原因完全或部分无效或不具有执行力,本协议的其余条款仍应有效并且有约束力。</p>
+          <p>7.3 本协议中的标题仅为方便而设,不具法律或契约效果。</p>
+          <p>7.4 本协议未尽事宜,由双方协商解决。</p>
+          <p>7.5 本协议中的任何条款无论因何种原因变更,变更后的协议条款对双方均有约束力。</p>
+          <p>该用户协议一旦发布即代表您与北京心境奇旅科技有限公司已经达成协议,并同意接受本协议的所有内容。</p>
+        </RichText>
+      </div>
+    </Page>
+  );
+}

+ 26 - 0
src/components/Button.tsx

@@ -0,0 +1,26 @@
+interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
+  children: React.ReactNode;
+  variant?: 'primary' | 'secondary';
+}
+
+export default function Button({ 
+  children, 
+  variant = 'primary',
+  className = '',
+  ...props 
+}: ButtonProps) {
+  const baseClasses = 'px-6 py-3 rounded-lg font-medium transition-colors duration-200';
+  const variantClasses = {
+    primary: 'bg-blue-500 hover:bg-blue-600 text-white',
+    secondary: 'bg-gray-100 hover:bg-gray-200 text-gray-900 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-white'
+  };
+
+  return (
+    <button
+      className={`${baseClasses} ${variantClasses[variant]} ${className}`}
+      {...props}
+    >
+      {children}
+    </button>
+  );
+}

+ 51 - 0
src/components/Collapse.tsx

@@ -0,0 +1,51 @@
+import { forwardRef, PropsWithChildren } from 'react';
+import AnimateHeight from 'react-animate-height';
+
+export interface CollapseProps {
+  isOpen?: boolean;
+  animateOpacity?: boolean;
+  onAnimationStart?: () => void;
+  onAnimationEnd?: () => void;
+  duration?: number;
+  easing?: string;
+  startingHeight?: number | string;
+  endingHeight?: number | string;
+}
+
+const Collapse = forwardRef<HTMLDivElement, PropsWithChildren<CollapseProps>>(
+  (
+    {
+      isOpen,
+      animateOpacity = true,
+      onAnimationStart,
+      onAnimationEnd,
+      duration = 300,
+      easing = 'ease',
+      startingHeight = 0,
+      endingHeight = 'auto',
+      children,
+      ...rest
+    },
+    ref,
+  ) => {
+    return (
+      <AnimateHeight
+        duration={duration}
+        easing={easing}
+        animateOpacity={animateOpacity}
+        height={isOpen ? endingHeight : startingHeight}
+        applyInlineTransitions={false}
+        {...{ onAnimationStart, onAnimationEnd }}
+        className="transition-[height,opacity,transform] duration-300 ease-in-out backface-hidden"
+      >
+        <div ref={ref} {...rest}>
+          {children}
+        </div>
+      </AnimateHeight>
+    );
+  },
+);
+
+Collapse.displayName = 'Collapse';
+
+export default Collapse;

+ 11 - 0
src/components/Container.tsx

@@ -0,0 +1,11 @@
+import { PropsWithChildren } from "react";
+import { cn } from "@/utils/cn";
+
+interface ContainerProps extends PropsWithChildren {
+  className?: string;
+  children: React.ReactNode;
+}
+
+export default function Container({ children, className }: ContainerProps) {
+  return <div className={cn("mx-auto max-w-screen-xl px-6", className)}>{children}</div>;
+}

+ 112 - 0
src/components/FeaturesGallery.tsx

@@ -0,0 +1,112 @@
+"use client";
+
+import Image from "next/image";
+import { useState } from "react";
+import Container from "./Container";
+import SectionTitle from "./SectionTitle";
+import ThreeLayersCircle from "./ThreeLayersCircle";
+import Collapse from "./Collapse";
+import { cn } from "@/utils/cn";
+
+const TABS = [
+  {
+    title: "心情记录:提升认知与情绪管理能力",
+    description:
+      "<p>心情记录是旅途中的驿站,将旅途中的见闻与自己的想法和心情记录下来吧。心情统计与分析功能可以帮助我们直观地了解我们的情绪变化,发现心情规律。</p>",
+    imageUrl: "/demo-illustration.jpg",
+    baseColor: "249,82,120",
+    secondColor: "221,9,57",
+  },
+  {
+    title: '心理练习:做自己的"咨询师',
+    description:
+      "<p>每天三件开心事、感恩日记等练习帮助你提升幸福感;同时还有《总为没发生的事情担忧》《不敢拒绝别人》等几十个专题练习帮你解决情绪、亲密关系、职场,校园点等具体问题。</p>",
+    imageUrl: "/demo-illustration1.jpg",
+    baseColor: "57,148,224",
+    secondColor: "99,172,232",
+  },
+  {
+    title: "心理测评:探索自我",
+    description:
+      "<p>专业研发的心理测评可以帮助我们了解自己的焦虑水平、抑郁水平,还有更多探索自我的主题测评等你来体验。</p>",
+    imageUrl: "/demo-illustration2.jpg",
+    baseColor: "88,193,132",
+    secondColor: "124,207,158",
+  },
+];
+
+export default function FeaturesGallery() {
+  const [currentTab, setCurrentTab] = useState(TABS[0]);
+
+  const handleTabClick = (idx: number) => {
+    setCurrentTab(TABS[idx]);
+  };
+
+  return (
+    <div className="bg-gray-50 dark:bg-gray-900/40">
+      <Container className="flex flex-col items-center py-10 lg:py-20">
+        <div className="max-w-3xl mx-auto">
+          <SectionTitle>开启一段奇妙的心境旅程</SectionTitle>
+        </div>
+
+        <div className="w-full mt-8 lg:mt-16 flex flex-col lg:flex-row items-start">
+          <div className="w-full lg:w-1/3 space-y-2 lg:space-y-4">
+            {TABS.map((tab, idx) => {
+              const isActive = tab.title === currentTab.title;
+
+              return (
+                <div
+                  key={tab.title}
+                  onClick={() => handleTabClick(idx)}
+                  className={cn(
+                    "cursor-pointer rounded-lg p-4 transition-colors duration-200",
+                    isActive ? "bg-white dark:bg-gray-800 shadow-sm" : "hover:bg-white/80 dark:hover:bg-gray-900"
+                  )}
+                >
+                  <div className="flex items-center gap-6">
+                    <div className="flex-shrink-0">
+                      <ThreeLayersCircle
+                        baseColor={isActive ? tab.baseColor : "transparent"}
+                        secondColor={tab.secondColor}
+                      />
+                    </div>
+                    <h4 className="text-lg font-semibold">{tab.title}</h4>
+                  </div>
+
+                  <Collapse isOpen={isActive} duration={300}>
+                    <div
+                      className="mt-4 text-gray-600 dark:text-gray-300 prose dark:prose-invert"
+                      dangerouslySetInnerHTML={{ __html: tab.description }}
+                    />
+                  </Collapse>
+                </div>
+              );
+            })}
+          </div>
+
+          <div className="w-full lg:w-2/3 relative h-[200px] lg:h-[400px] mt-4 lg:mt-0 lg:pl-16">
+            {TABS.map((tab, idx) => {
+              const isActive = tab.title === currentTab.title;
+              const isFirst = idx === 0;
+
+              return (
+                <Image
+                  key={tab.title}
+                  src={tab.imageUrl}
+                  alt={tab.title}
+                  width={768}
+                  height={400}
+                  className={cn(
+                    "absolute top-0 left-0 rounded-2xl transition-all duration-300 pl-4 object-contain w-full h-full",
+                    isActive ? "opacity-100 translate-x-0 translate-y-0" : "opacity-0 -translate-x-0 translate-y-0"
+                  )}
+                  priority={isFirst}
+                />
+              );
+            })}
+          </div>
+        </div>
+      </Container>
+    </div>
+  );
+}

+ 57 - 0
src/components/Footer.tsx

@@ -0,0 +1,57 @@
+import Link from "next/link";
+
+const FOOTER_SECTIONS = [
+  {
+    title: "法律条款",
+    links: [
+      { href: "/privacy-policy", label: "隐私协议" },
+      { href: "/user-policy", label: "用户服务协议" },
+    ],
+  },
+  {
+    title: "备案信息",
+    links: [{ href: "https://beian.miit.gov.cn", label: "京ICP备2023016274号-1" }],
+  },
+  {
+    title: "公司信息",
+    links: [{ href: "/about", label: "关于我们" }],
+  },
+  {
+    title: "联系我们",
+    links: [
+      { href: "#", label: "lumosjoy (微信)" },
+      { href: "mailto:service@mindjourneytech.com", label: "service@mindjourneytech.com" },
+    ],
+  },
+];
+
+export default function Footer() {
+  return (
+    <footer className="bg-[#0F172A] text-gray-400">
+      <div className="max-w-screen-xl mx-auto px-6 py-16">
+        <div className="grid grid-cols-2 md:grid-cols-4 gap-8">
+          {FOOTER_SECTIONS.map((section) => (
+            <div key={section.title}>
+              <h3 className="text-white text-lg font-medium mb-4">{section.title}</h3>
+              <ul className="space-y-2">
+                {section.links.map((link) => (
+                  <li key={link.label}>
+                    <Link 
+                      href={link.href} 
+                      className="hover:text-white transition-colors duration-200 break-all text-sm md:text-base"
+                    >
+                      {link.label}
+                    </Link>
+                  </li>
+                ))}
+              </ul>
+            </div>
+          ))}
+        </div>
+        <div className="mt-16 pt-8 border-t border-gray-800">
+          <p className="text-center text-sm">Copyright {new Date().getFullYear()} 心境奇旅,All rights reserved.</p>
+        </div>
+      </div>
+    </footer>
+  );
+}

+ 58 - 0
src/components/Hero.tsx

@@ -0,0 +1,58 @@
+"use client";
+
+import Button from "./Button";
+import Container from "./Container";
+import Image from "next/image";
+import Modal from "./Modal";
+import { useState } from "react";
+
+export default function Hero() {
+  const [isModalOpen, setIsModalOpen] = useState(false);
+
+  const handleDownload = () => {
+    // Check if it's mobile
+    const isMobile = /iPhone|iPad|iPod|Android/i.test(typeof window !== "undefined" ? window.navigator.userAgent : "");
+
+    if (isMobile) {
+      // Direct to app store or your app's download page
+      window.location.href = "https://h5.mindjourneytech.com/app.html?isLink=1";
+    } else {
+      // Show QR code modal on desktop
+      setIsModalOpen(true);
+    }
+  };
+
+  return (
+    <Container>
+      <div className="py-10 lg:py-10">
+        <div className="max-w-6xl mx-auto">
+          <h1 className="text-6xl font-bold mb-8 tracking-tighter">心境奇旅 APP</h1>
+          <div className="space-y-2 text-lg text-gray-600 dark:text-gray-300 max-w-5xl">
+            <p>心境奇旅是一款专注于心理健康的app。</p>
+            <p>心理或情绪问题的解决不仅依赖专业知识的输入,更依赖我们自我觉察和主动练习。</p>
+            <p>我们将心理学、认知神经科学、心理咨询诸多流派中的理论与技巧产品化,让专业、好用的内容与工具触手可得。</p>
+            <p>
+              在这里,人们通过自助练习或书写等方式与自己相处,舒缓焦虑、抑郁等情绪,解决情绪问题,建立积极心理习惯。
+            </p>
+            <p>建立强大平静的内心如同一段旅程,我们是旅途中的主角,心境奇旅中,让我们共同探索与成长!</p>
+          </div>
+          <div className="mt-8">
+            <Button
+              onClick={handleDownload}
+              className="px-8 py-3 bg-[#F4A261] hover:bg-[#E76F51] text-white rounded-lg text-lg font-medium transition-colors"
+            >
+              下载 APP
+            </Button>
+          </div>
+        </div>
+      </div>
+
+      <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} title="欢迎下载APP">
+        <div className="flex flex-col items-center">
+          <Image src="/QRCode.jpg" alt="Download QR Code" width={200} height={200} className="mx-auto" />
+          <p className="mt-4 text-sm text-gray-500 dark:text-gray-400 text-center">请使用手机扫描二维码下载APP</p>
+        </div>
+      </Modal>
+    </Container>
+  );
+}

+ 17 - 0
src/components/InformationSection.tsx

@@ -0,0 +1,17 @@
+export default function InformationSection() {
+  return (
+    <div className="w-full max-w-4xl mx-auto px-6 py-16">
+      <div className="prose prose-lg dark:prose-invert mx-auto">
+        <h2 className="text-3xl font-bold mb-8">公司介绍</h2>
+        <div className="space-y-6 text-lg text-gray-600 dark:text-gray-300">
+          <p>心境奇旅是一家专注于心理健康领域的科技创新型企业。</p>
+          <p>
+            作为一家年轻且充满活力的公司,心境奇旅科技拥有一支富有创造力和专业知识的团队。团队成员来自多个不同领域,包括计算机科学、心理学、社会学等。
+          </p>
+          <p>我们致力于利用先进的技术手段,为用户提供全面的心理健康服务,以帮助他们通过自助练习改善生活质量、提升心理健康水平。</p>
+          <p>我们以用户为中心,致力于提供高质量的咨询服务。我们相信,通过我们的努力和创新,可以为用户带来更健康、更幸福的生活。</p>
+        </div>
+      </div>
+    </div>
+  );
+}

File diff suppressed because it is too large
+ 49 - 0
src/components/Logo.tsx


+ 57 - 0
src/components/Modal.tsx

@@ -0,0 +1,57 @@
+"use client";
+
+import { Dialog, Transition } from "@headlessui/react";
+import { Fragment } from "react";
+
+interface ModalProps {
+  isOpen: boolean;
+  onClose: () => void;
+  title: string;
+  children: React.ReactNode;
+}
+
+export default function Modal({ isOpen, onClose, title, children }: ModalProps) {
+  return (
+    <Transition appear show={isOpen} as={Fragment}>
+      <Dialog as="div" className="relative z-50" onClose={onClose} open={isOpen}>
+        <Transition
+          show={isOpen}
+          as={Fragment}
+          enter="ease-out duration-300"
+          enterFrom="opacity-0"
+          enterTo="opacity-100"
+          leave="ease-in duration-200"
+          leaveFrom="opacity-100"
+          leaveTo="opacity-0"
+        >
+          <div className="fixed inset-0 bg-black/25 backdrop-blur-sm" />
+        </Transition>
+
+        <div className="fixed inset-0 overflow-y-auto">
+          <div className="flex min-h-full items-center justify-center p-4 text-center">
+            <Transition
+              show={isOpen}
+              as={Fragment}
+              enter="ease-out duration-300"
+              enterFrom="opacity-0 scale-95"
+              enterTo="opacity-100 scale-100"
+              leave="ease-in duration-200"
+              leaveFrom="opacity-100 scale-100"
+              leaveTo="opacity-0 scale-95"
+            >
+              <Dialog.Panel className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white dark:bg-gray-800 p-6 text-left align-middle shadow-xl transition-all">
+                <Dialog.Title
+                  as="h3"
+                  className="text-lg font-medium leading-6 text-gray-900 dark:text-white text-center"
+                >
+                  {title}
+                </Dialog.Title>
+                <div className="mt-4">{children}</div>
+              </Dialog.Panel>
+            </Transition>
+          </div>
+        </div>
+      </Dialog>
+    </Transition>
+  );
+}

+ 130 - 0
src/components/Navigation.tsx

@@ -0,0 +1,130 @@
+'use client';
+
+import Link from 'next/link';
+import { usePathname } from 'next/navigation';
+import { cn } from '@/utils/cn';
+import { useTheme } from 'next-themes';
+import Logo from './Logo';
+import React from 'react';
+
+const MENU_ITEMS = [
+  { href: '/about', label: '关于我们' },
+];
+
+export default function Navigation() {
+  const pathname = usePathname();
+  const { theme, setTheme } = useTheme();
+  const [mounted, setMounted] = React.useState(false);
+
+  React.useEffect(() => {
+    setMounted(true);
+  }, []);
+
+  if (!mounted) {
+    return (
+      <nav className="fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-sm dark:bg-gray-900/80">
+        <div className="max-w-screen-xl mx-auto px-6">
+          <div className="flex items-center justify-between h-20">
+            <Link href="/" className="flex items-center">
+              <Logo />
+            </Link>
+            <div className="flex items-center space-x-6">
+              {MENU_ITEMS.map((item) => (
+                <Link
+                  key={item.href}
+                  href={item.href}
+                  className="text-base font-medium transition-colors text-gray-600 dark:text-gray-300"
+                >
+                  {item.label}
+                </Link>
+              ))}
+              <button
+                className="p-2 text-gray-600 dark:text-gray-300"
+                aria-label="Toggle dark mode"
+              >
+                <svg
+                  viewBox="0 0 24 24"
+                  fill="none"
+                  strokeWidth="2"
+                  strokeLinecap="round"
+                  strokeLinejoin="round"
+                  className="h-6 w-6"
+                >
+                  <path d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707" />
+                </svg>
+              </button>
+            </div>
+          </div>
+        </div>
+      </nav>
+    );
+  }
+
+  return (
+    <nav className="fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-sm dark:bg-gray-900/80">
+      <div className="max-w-screen-xl mx-auto px-6">
+        <div className="flex items-center justify-between h-20">
+          <Link href="/" className="flex items-center">
+            <Logo />
+          </Link>
+          
+          <div className="flex items-center space-x-6">
+            {MENU_ITEMS.map((item) => (
+              <Link
+                key={item.href}
+                href={item.href}
+                className={cn(
+                  'text-base font-medium transition-colors',
+                  pathname === item.href
+                    ? 'text-primary'
+                    : 'text-gray-600 hover:text-primary dark:text-gray-300 dark:hover:text-primary'
+                )}
+              >
+                {item.label}
+              </Link>
+            ))}
+            <button
+              onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
+              className="p-2 text-gray-600 hover:text-primary dark:text-gray-300 dark:hover:text-primary"
+              aria-label="Toggle dark mode"
+            >
+              {theme === 'dark' ? (
+                <svg
+                  viewBox="0 0 24 24"
+                  fill="none"
+                  strokeWidth="2"
+                  strokeLinecap="round"
+                  strokeLinejoin="round"
+                  className="w-6 h-6"
+                >
+                  <path
+                    d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707"
+                    className="stroke-current"
+                  />
+                  <path
+                    d="M12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"
+                    className="fill-current"
+                  />
+                </svg>
+              ) : (
+                <svg
+                  viewBox="0 0 24 24"
+                  fill="none"
+                  strokeWidth="2"
+                  strokeLinecap="round"
+                  strokeLinejoin="round"
+                  className="w-6 h-6"
+                >
+                  <path
+                    d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
+                    className="stroke-current"
+                  />
+                </svg>
+              )}
+            </button>
+          </div>
+        </div>
+      </div>
+    </nav>
+  );
+}

+ 42 - 0
src/components/Page.tsx

@@ -0,0 +1,42 @@
+"use client";
+
+import { PropsWithChildren, useEffect, useState } from "react";
+import Container from "./Container";
+import SectionTitle from "./SectionTitle";
+
+export interface PageProps {
+  title: string;
+  description?: string;
+}
+
+export default function Page({ title, description, children }: PropsWithChildren<PageProps>) {
+  const [mounted, setMounted] = useState(false);
+
+  useEffect(() => {
+    setMounted(true);
+  }, []);
+
+  if (!mounted) {
+    return null;
+  }
+
+  return (
+    <div className="pt-20">
+      <div className="flex items-center justify-center h-[30vh] bg-gray-100 dark:bg-gray-800">
+        <Container>
+          <div className="flex flex-col items-center justify-center">
+            <SectionTitle className="text-gray-900 dark:text-white">{title}</SectionTitle>
+            {description && (
+              <p className="mt-4 text-center text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
+                {description}
+              </p>
+            )}
+          </div>
+        </Container>
+      </div>
+      <Container>
+        <div className="py-12">{children}</div>
+      </Container>
+    </div>
+  );
+}

+ 29 - 0
src/components/RichText.tsx

@@ -0,0 +1,29 @@
+import { PropsWithChildren } from 'react';
+import { cn } from '@/utils/cn';
+
+interface RichTextProps extends PropsWithChildren {
+  className?: string;
+}
+
+export default function RichText({ children, className }: RichTextProps) {
+  return (
+    <div 
+      className={cn(
+        'prose prose-lg dark:prose-invert max-w-none',
+        'prose-headings:font-bold prose-headings:text-gray-900 dark:prose-headings:text-white',
+        'prose-p:text-gray-600 dark:prose-p:text-gray-300',
+        'prose-strong:text-gray-900 dark:prose-strong:text-white',
+        'prose-ul:list-none prose-ul:pl-0 prose-li:pl-8 prose-li:relative',
+        'prose-li:before:content-["L"] prose-li:before:absolute prose-li:before:left-0 prose-li:before:top-0',
+        'prose-li:before:text-primary prose-li:before:font-arial prose-li:before:scale-x-[-1] prose-li:before:rotate-[-35deg]',
+        'prose-table:border-collapse prose-table:w-full',
+        'prose-th:bg-gray-100 dark:prose-th:bg-gray-800',
+        'prose-th:border prose-th:border-gray-300 dark:prose-th:border-gray-700 prose-th:p-4',
+        'prose-td:border prose-td:border-gray-300 dark:prose-td:border-gray-700 prose-td:p-4',
+        className
+      )}
+    >
+      {children}
+    </div>
+  );
+}

+ 14 - 0
src/components/SectionTitle.tsx

@@ -0,0 +1,14 @@
+import { PropsWithChildren } from "react";
+import { cn } from "@/utils/cn";
+
+interface SectionTitleProps extends PropsWithChildren {
+  className?: string;
+}
+
+export default function SectionTitle({ children, className }: SectionTitleProps) {
+  return (
+    <h2 className={cn("text-3xl md:text-4xl font-bold leading-tight tracking-tight text-center", className)}>
+      {children}
+    </h2>
+  );
+}

+ 72 - 0
src/components/Testimonials.tsx

@@ -0,0 +1,72 @@
+"use client";
+
+import { A11y, Autoplay, Navigation } from "swiper/modules";
+import { Swiper, SwiperSlide } from "swiper/react";
+import Container from "./Container";
+import SectionTitle from "./SectionTitle";
+
+// Import Swiper styles
+import "swiper/css";
+import "swiper/css/navigation";
+import { cn } from "@/utils/cn";
+
+const TESTIMONIALS = [
+  {
+    content: `我喜欢这个软件的第一点是,可以知道自己哪天心情不好,是因为什么类型的事,在统计里面都直接能看到.喜欢的第二点是,可以每天做不同的练习,比如"亲密关系中缺乏安全感"、"不敢拒绝他人"等,通过类似的练习可以慢慢去学会怎么让自己变得内心更强大,感觉有点像在做心理咨询一样。`,
+    author: {
+      name: "Clyde Edwards",
+      title: "Very Serious Man",
+    },
+  },
+  {
+    content: `在找了n多个情绪记录app之后,我发现我最需要的(每天记录三件开心事)功能在里面!我真的很喜欢这个!然后有一些正能量的练习我也很需要!`,
+    author: {
+      name: "Jimmy Hunter",
+      title: "Sigma Male University Graduate",
+    },
+  },
+  {
+    content: `很棒的一款心理自助日记本,非常喜欢!里面的情绪联系非常详尽,按照练习上提供的步骤去梳理自己的情绪,真的会跳脱内耗,以一种新的视角看待自己的问题~有效缓解情绪紧张,超级喜欢!!推荐推荐!!`,
+    author: {
+      name: "Marjorie Morgan",
+      title: "Chief Chad Officer",
+    },
+  },
+];
+
+export default function Testimonials() {
+  return (
+    <div className="py-10">
+      <Container>
+        <SectionTitle>用户反馈</SectionTitle>
+        <div className="relative mt-8">
+          <Swiper
+            modules={[Navigation, Autoplay, A11y]}
+            slidesPerView={1}
+            autoplay={{ delay: 8000 }}
+            centeredSlides
+            navigation
+            loop
+            className="testimonials-swiper"
+          >
+            {TESTIMONIALS.map((testimonial, idx) => (
+              <SwiperSlide key={idx}>
+                <div className="flex flex-col items-center px-4">
+                  <blockquote className="mt-10 text-center text-xl italic text-gray-700 dark:text-gray-300 max-w-[90%] md:max-w-[70%] lg:max-w-[55%]">
+                    &quot;{testimonial.content}&quot;
+                  </blockquote>
+                  {/* <div className="mt-8 flex items-center space-x-4">
+                    <div className="flex flex-col text-sm">
+                      <p className="font-bold text-gray-900 dark:text-white">{testimonial.author.name}</p>
+                      <p className="text-gray-600 dark:text-gray-400">{testimonial.author.title}</p>
+                    </div>
+                  </div> */}
+                </div>
+              </SwiperSlide>
+            ))}
+          </Swiper>
+        </div>
+      </Container>
+    </div>
+  );
+}

+ 9 - 0
src/components/ThemeProvider.tsx

@@ -0,0 +1,9 @@
+'use client'
+
+import * as React from 'react'
+import { ThemeProvider as NextThemesProvider } from 'next-themes'
+import type { ThemeProviderProps } from 'next-themes/dist/types'
+
+export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
+  return <NextThemesProvider {...props}>{children}</NextThemesProvider>
+}

+ 22 - 0
src/components/ThreeLayersCircle.tsx

@@ -0,0 +1,22 @@
+interface ThreeLayersCircleProps {
+  baseColor: string;
+  secondColor: string;
+}
+
+export default function ThreeLayersCircle({ baseColor, secondColor }: ThreeLayersCircleProps) {
+  return (
+    <div 
+      className="relative inline-block opacity-90 w-16 h-16 md:w-14 md:h-14 rounded-full transition-colors duration-200 z-0 shadow-sm"
+      style={{ background: `rgb(${baseColor})` }}
+    >
+      <div 
+        className="absolute w-12 h-12 md:w-11 md:h-11 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full -z-10 shadow-inner"
+        style={{ background: `rgb(${secondColor})` }}
+      />
+      <div 
+        className="absolute w-6 h-6 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full -z-20 shadow-sm"
+        style={{ background: `rgb(${baseColor})` }}
+      />
+    </div>
+  );
+}

+ 6 - 0
src/utils/cn.ts

@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs: ClassValue[]) {
+  return twMerge(clsx(inputs));
+}

+ 15 - 0
tailwind.config.js

@@ -0,0 +1,15 @@
+/** @type {import('tailwindcss').Config} */
+import typography from "@tailwindcss/typography";
+
+module.exports = {
+  content: [
+    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
+    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
+    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
+  ],
+  theme: {
+    extend: {},
+  },
+  plugins: [typography],
+  darkMode: "class",
+};

+ 0 - 18
tailwind.config.ts

@@ -1,18 +0,0 @@
-import type { Config } from "tailwindcss";
-
-export default {
-  content: [
-    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
-    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
-    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
-  ],
-  theme: {
-    extend: {
-      colors: {
-        background: "var(--background)",
-        foreground: "var(--foreground)",
-      },
-    },
-  },
-  plugins: [],
-} satisfies Config;

Some files were not shown because too many files changed in this diff