diff --git a/README.md b/README.md index d6b2032..e8c0b1d 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,21 @@ # YMB Site -YMB 公开站点响应式首页原型,参考 YouTube 的内容发现信息架构,支持桌面端与移动端。 +YMB 公开站点 React + Vite 项目,参考 YouTube 的内容发现信息架构,支持桌面端与移动端。 ## 运行 -直接用浏览器打开 `index.html`,或在当前目录启动任意静态文件服务器。 +```bash +pnpm install +pnpm dev +pnpm build +``` ## 包含 - 桌面端:顶部搜索、侧边导航、推荐网格、Shorts 横滑区、频道订阅。 - 移动端:顶部紧凑栏、双列内容、横滑 Shorts、底部导航。 -- 原生 JS:搜索、题材筛选、播放提示、关注切换。 +- React:搜索、题材筛选、播放提示、关注切换、移动端菜单。 - 多语言:印尼语 `?lang=id`、English `?lang=en`、简体中文 `?lang=zh`。 - SEO:动态 `lang/title/description`、canonical、hreflang、Open Graph、Twitter Card、JSON-LD、robots.txt、sitemap.xml。 diff --git a/index.html b/index.html index 4bdb678..c04af66 100644 --- a/index.html +++ b/index.html @@ -3,46 +3,23 @@ - + - - + - + - - - - YMB · Cerita pendek, emosi besar - - + + + + + + + + YMB · Short stories, big emotions - -
-
- - ▶ymb - -
-
- -
-
YMB ORIGINAL

Cerita pendek,
emosi besar.

Temukan drama yang bikin kamu terus menonton. Episode baru setiap hari.

Drama pendek YMB
-
REKOMENDASI UNTUKMU

Drama pilihan minggu ini

Lihat semua →
-
SHORTS

Cerita dalam satu tarikan napas

Jelajahi →
-
CHANNEL UNTUKMU

Ikuti cerita favoritmu

Kelola →
-
-
- -
- - +
diff --git a/package.json b/package.json new file mode 100644 index 0000000..7cddcb0 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "ymb-site", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": {"dev":"vite","build":"vite build","preview":"vite preview"}, + "dependencies": {"@vitejs/plugin-react":"latest","vite":"latest","react":"latest","react-dom":"latest","lucide-react":"latest"}, + "devDependencies": {} +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..dc8e313 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,497 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@vitejs/plugin-react': + specifier: latest + version: 6.3.0(vite@8.3.7) + lucide-react: + specifier: latest + version: 1.71.0(react@19.3.1) + react: + specifier: latest + version: 19.3.1 + react-dom: + specifier: latest + version: 19.3.1(react@19.3.1) + vite: + specifier: latest + version: 8.3.7 + +packages: + + '@oxc-project/types@0.156.0': + resolution: {integrity: sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw==} + + '@rolldown/binding-android-arm-eabi@1.2.18': + resolution: {integrity: sha512-ubTG+4KlidvioxjOFdsdEa2F6JVGZb8m2ByJUqQ7fYzUeT1cEn194POcsQ10PKH4p//q0YQn4SB3YpSuzcA1Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.18': + resolution: {integrity: sha512-2Xa1t/JeFGTZn5TbzgPppcCftF8sCVnTNvrIvoSdcZhoFzAuVy1L3xxQUQ4sfWDtwVzg1u6Yi1R3SlJugRNV2Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.18': + resolution: {integrity: sha512-iUzw9HshVvrWci9gVXjxWS7/M/IcLvNo67vu2c/w9zKBz4hIvPRixdUpIW/iCfhldLaq9ER2hecsI9UY5z0/yA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.18': + resolution: {integrity: sha512-XuDHFzbFEtZl0QGJi92+fmG67tfnp1iZ9VOeDkwasGyTq2VG3/5PnJnvAr+MTzFh/rNScJ3VhN+XpZyyGsN7Jw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.18': + resolution: {integrity: sha512-MM8opLfsLK15oEXDz6iBc0t3ahu+ew9jsAmha+ovZ33hSwYgqo7YmpRITqFTfW7xA71BLEQ3jJdTsaCmhB1c9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.18': + resolution: {integrity: sha512-J6EoCWiy5RnFQzHrKyHXXevZ9lukQfGpnchX/zG/k/WC2nc9O9XyS1JgA8kI9VxMFggMbCPjpv7LcikriXfehw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.18': + resolution: {integrity: sha512-HIkFuLF7CdDeki+1aCZLLbLq0hk/PHntb+z4/YCTo47JbksTD6jJEELIz92tYROmDmvMrNqUMqBi71BkJLDR+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.2.18': + resolution: {integrity: sha512-4oqWItLLYt7IceOnElCct8T2ek5p7PmD7LN04TRJRXGo5f3HtKTgGH6PhYRFK4C2ICkq/LMUU0BVPvvlAKhm8g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.2.18': + resolution: {integrity: sha512-+AkRly5SrCEgnPhGRfub7pucASYEC2fTsxZg7VCcnozpA2+Wn/gCYy1t7LuJS67/xyJu/7noYBJncDUkbwgw2A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.2.18': + resolution: {integrity: sha512-Ttg1r9vVykMnQrE/GwYO9icKtXiuiMfRe/w74xJkeoVuvhRG+WtT6LEMOf58Yl63yd77lhHzO8Y55R8CC3XALA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.2.18': + resolution: {integrity: sha512-xX1Tt4o5jUMma0vJsXp5eRZA9nOuyigb2dKJMMzZRmzMxLYGE9PaKKSqHPzPvSQgM8ZOEacSTQwDQcwWfh0xmQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.2.18': + resolution: {integrity: sha512-4VVlGI5K2kociZlW3lSN8VjG3TW5yQIlAcfCdBXMgGjJxZll15Fa4ocRHXHvIVVnwlibCw/NQajJrNDiqqpiiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.2.18': + resolution: {integrity: sha512-PApTsgH1jLYuyy3tSCz9De82Gq5qXwi7vImVZhEzTgX1C4150Cp5M2ZfL3pLvbCvbEHL0oJeC6pd6podtv8Y5w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.18': + resolution: {integrity: sha512-J2XjWjx/SP+4x0H4r6dz3Vzb5WVkg7re40Do9K7HJ3sK2/xcmZrWwakXlSwaMvOkvruLJCD4CL/3wqsIR/thgg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.18': + resolution: {integrity: sha512-XwRJxRQvkuEtS0hIIt29QrqPJ+JzMJSNYHJw4ae/mc6uEarfRG1ceyg4HmmWiBdL/YCb6g7N5BLbSvmRJRYZHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.1.0': + resolution: {integrity: sha512-K+NU2HDCEem4sClgHH4ZZXwdJeNFkJo+hRk479DIwdIviAPN2hFJH1KHHwg8CxHQXvi0M5XY4deTXINfIiSG6w==} + + '@vitejs/plugin-react@6.3.0': + resolution: {integrity: sha512-H9E955aVvD2F4zNHCw4eIZbIfF6xKldlh7MNu+hSU7xwQzh65lI/N+CJWqmfhi0PEjDXIt/H7T7J/4wU63KJiw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 || ^0.3.0 + babel-plugin-react-compiler: ^1.0.0 + oxc-transform-react: '>=0.152.0 <0.157.0' + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + oxc-transform-react: + optional: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lucide-react@1.71.0: + resolution: {integrity: sha512-8YtvF+bqT/EMqAXU7kWvYdRu+TV9j4kS1eIS7IbN1j1Z1DgupbrTL0nDIygdqgBDTD7AJPP607VdVgzDTar4zA==} + peerDependencies: + '@types/react': '*' + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + nanoid@3.3.20: + resolution: {integrity: sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + postcss@8.5.32: + resolution: {integrity: sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==} + engines: {node: ^10 || ^12 || >=14} + + react-dom@19.3.1: + resolution: {integrity: sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==} + peerDependencies: + react: ^19.3.1 + + react@19.3.1: + resolution: {integrity: sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==} + engines: {node: '>=0.10.0'} + + rolldown@1.2.18: + resolution: {integrity: sha512-C1uuT3UR60SShuCaPQFAAZyefJCWZlAKB02BV3BuPE2Rs4fevRQltEKoFO2tV5W9/eJhU5RieV6vJj3euulfNQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + vite@8.3.7: + resolution: {integrity: sha512-oxh0dMxrayBr33ScJvvBJ2HBQ2jc1TBrXKR2NyL/MJ9WPuX19mN2DXWUmywKdyw5RFVQBo8hmtb2FMC0LHrnng==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 || ^0.8.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + +snapshots: + + '@oxc-project/types@0.156.0': {} + + '@rolldown/binding-android-arm-eabi@1.2.18': + optional: true + + '@rolldown/binding-android-arm64@1.2.18': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.18': + optional: true + + '@rolldown/binding-darwin-x64@1.2.18': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.18': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.18': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.18': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.18': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.18': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.18': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.18': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.18': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.18': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.18': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.18': + optional: true + + '@rolldown/pluginutils@1.1.0': {} + + '@vitejs/plugin-react@6.3.0(vite@8.3.7)': + dependencies: + '@rolldown/pluginutils': 1.1.0 + vite: 8.3.7 + + detect-libc@2.1.2: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fsevents@2.3.3: + optional: true + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lucide-react@1.71.0(react@19.3.1): + dependencies: + react: 19.3.1 + + nanoid@3.3.20: {} + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + postcss@8.5.32: + dependencies: + nanoid: 3.3.20 + picocolors: 1.1.1 + source-map-js: 1.2.2 + + react-dom@19.3.1(react@19.3.1): + dependencies: + react: 19.3.1 + scheduler: 0.28.0 + + react@19.3.1: {} + + rolldown@1.2.18: + dependencies: + '@oxc-project/types': 0.156.0 + '@rolldown/pluginutils': 1.1.0 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.18 + '@rolldown/binding-android-arm64': 1.2.18 + '@rolldown/binding-darwin-arm64': 1.2.18 + '@rolldown/binding-darwin-x64': 1.2.18 + '@rolldown/binding-freebsd-x64': 1.2.18 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.18 + '@rolldown/binding-linux-arm64-gnu': 1.2.18 + '@rolldown/binding-linux-arm64-musl': 1.2.18 + '@rolldown/binding-linux-ppc64-gnu': 1.2.18 + '@rolldown/binding-linux-s390x-gnu': 1.2.18 + '@rolldown/binding-linux-x64-gnu': 1.2.18 + '@rolldown/binding-linux-x64-musl': 1.2.18 + '@rolldown/binding-openharmony-arm64': 1.2.18 + '@rolldown/binding-win32-arm64-msvc': 1.2.18 + '@rolldown/binding-win32-x64-msvc': 1.2.18 + + scheduler@0.28.0: {} + + source-map-js@1.2.2: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + vite@8.3.7: + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.32 + rolldown: 1.2.18 + tinyglobby: 0.2.17 + optionalDependencies: + fsevents: 2.3.3 diff --git a/public/assets/Outfit-Bold.ttf b/public/assets/Outfit-Bold.ttf new file mode 100644 index 0000000..0389ff2 Binary files /dev/null and b/public/assets/Outfit-Bold.ttf differ diff --git a/public/assets/PlusJakartaSans-Regular.ttf b/public/assets/PlusJakartaSans-Regular.ttf new file mode 100644 index 0000000..b655eb4 Binary files /dev/null and b/public/assets/PlusJakartaSans-Regular.ttf differ diff --git a/public/assets/PlusJakartaSans-SemiBold.ttf b/public/assets/PlusJakartaSans-SemiBold.ttf new file mode 100644 index 0000000..609def9 Binary files /dev/null and b/public/assets/PlusJakartaSans-SemiBold.ttf differ diff --git a/public/assets/friends.jpg b/public/assets/friends.jpg new file mode 100644 index 0000000..c8a06d1 Binary files /dev/null and b/public/assets/friends.jpg differ diff --git a/public/assets/heir.jpg b/public/assets/heir.jpg new file mode 100644 index 0000000..eb9208c Binary files /dev/null and b/public/assets/heir.jpg differ diff --git a/public/assets/hero.jpg b/public/assets/hero.jpg new file mode 100644 index 0000000..d397b1f Binary files /dev/null and b/public/assets/hero.jpg differ diff --git a/public/assets/revenge.jpg b/public/assets/revenge.jpg new file mode 100644 index 0000000..e82cb29 Binary files /dev/null and b/public/assets/revenge.jpg differ diff --git a/public/assets/romance.jpg b/public/assets/romance.jpg new file mode 100644 index 0000000..cc39851 Binary files /dev/null and b/public/assets/romance.jpg differ diff --git a/public/assets/second-chance.jpg b/public/assets/second-chance.jpg new file mode 100644 index 0000000..55683a8 Binary files /dev/null and b/public/assets/second-chance.jpg differ diff --git a/public/assets/secret.jpg b/public/assets/secret.jpg new file mode 100644 index 0000000..ce53b5a Binary files /dev/null and b/public/assets/secret.jpg differ diff --git a/public/assets/wedding.jpg b/public/assets/wedding.jpg new file mode 100644 index 0000000..67da900 Binary files /dev/null and b/public/assets/wedding.jpg differ diff --git a/src/main.jsx b/src/main.jsx new file mode 100644 index 0000000..52c5599 --- /dev/null +++ b/src/main.jsx @@ -0,0 +1,24 @@ +// Author: fruit.com.cn +import React, { useMemo, useState } from 'react'; +import { createRoot } from 'react-dom/client'; +import { Bell, Bookmark, Compass, History, Home, Menu, Play, Search, UserRound, X } from 'lucide-react'; +import './styles.css'; + +const ASSET = '/assets/'; +const copy = { + id: { title: 'YMB · Cerita pendek, emosi besar', description: 'Temukan drama pendek terbaik dan episode baru setiap hari.', search: 'Cari drama, aktor, atau genre', hero: 'Cerita pendek,', accent: 'emosi besar.', watch: 'Mulai menonton', picks: 'Drama pilihan minggu ini', shorts: 'Cerita dalam satu tarikan napas', channels: 'Ikuti cerita favoritmu', follow: 'Ikuti', following: 'Mengikuti', genres: ['Semua', 'Romansa', 'Balas dendam', 'Keluarga', 'Rahasia'] }, + en: { title: 'YMB · Short stories, big emotions', description: 'Discover the best short dramas and new episodes every day.', search: 'Search dramas, actors, or genres', hero: 'Short stories,', accent: 'big emotions.', watch: 'Watch now', picks: 'Drama picks this week', shorts: 'Stories in one breath', channels: 'Follow your favorite stories', follow: 'Follow', following: 'Following', genres: ['All', 'Romance', 'Revenge', 'Family', 'Secrets'] }, + zh: { title: 'YMB · 短故事,大情绪', description: '发现精彩短剧,每天更新新剧集。', search: '搜索短剧、演员或题材', hero: '短故事,', accent: '大情绪。', watch: '立即观看', picks: '本周精选短剧', shorts: '一口气看完的故事', channels: '关注你喜欢的故事', follow: '关注', following: '已关注', genres: ['全部', '爱情', '复仇', '家庭', '秘密'] } +}; +const videos = [['Pernikahan yang Tak Terduga','YMB Originals','Romansa','wedding.jpg','12:48'],['Rahasia di Balik Senyuman','Cerita Senja','Rahasia','secret.jpg','09:32'],['Pewaris yang Kembali','Drama Pilihan','Keluarga','heir.jpg','15:20'],['Kesempatan Kedua untuk Kita','YMB Originals','Kisah kedua','second-chance.jpg','11:04'],['Cinta Setelah Balas Dendam','Cerita Senja','Balas dendam','revenge.jpg','13:40'],['Sahabat yang Selalu Ada','Drama Pilihan','Keluarga','friends.jpg','08:16'],['Kota Kecil, Rahasia Besar','YMB Originals','Rahasia','hero.jpg','10:12'],['Satu Janji Terakhir','Drama Pilihan','Romansa','romance.jpg','07:55']]; +const shorts = [['Dia kembali setelah 5 tahun','second-chance.jpg'],['Ketika rahasia terbongkar','secret.jpg'],['Satu pesan yang mengubah semuanya','romance.jpg'],['Jangan percaya senyumnya','revenge.jpg'],['Pewaris tanpa nama','heir.jpg'],['Hari pernikahan itu','wedding.jpg']]; +const channels = [['YMB Originals','2,4 jt pengikut','Y','#ca4d43'],['Cerita Senja','986 rb pengikut','C','#5c4aa1'],['Drama Pilihan','742 rb pengikut','D','#2e7d71'],['Kisah Hari Ini','428 rb pengikut','K','#a06a39']]; +function useLanguage() { const initial = new URLSearchParams(location.search).get('lang'); return useState(copy[initial] ? initial : 'id'); } +function App() { + const [lang, setLang] = useLanguage(); const text = copy[lang]; const [genre, setGenre] = useState(text.genres[0]); const [query, setQuery] = useState(''); const [menu, setMenu] = useState(false); const [toast, setToast] = useState(''); + const filtered = useMemo(() => videos.filter((item) => (!query || item.join(' ').toLowerCase().includes(query.toLowerCase())) && (genre === text.genres[0] || item[2] === genre)), [genre, query, text]); + const notify = (message) => { setToast(message); window.setTimeout(() => setToast(''), 1600); }; + const changeLanguage = (value) => { setLang(value); setGenre(copy[value].genres[0]); const url = new URL(window.location.href); url.searchParams.set('lang', value); window.history.replaceState(null, '', url); document.title = copy[value].title; document.documentElement.lang = value === 'zh' ? 'zh-CN' : value; }; + return <>
ymb
event.preventDefault()}> setQuery(event.target.value)} placeholder={text.search} aria-label={text.search} />
N
YMB ORIGINAL

{text.hero}
{text.accent}

{text.description}

YMB drama

{text.picks}

Lihat semua →
{text.genres.map((item) => )}
{filtered.map((item) =>
notify(`▶ ${item[0]}`)}>
{item[0]}{item[4]}
{item[1][0]}
{item[0]}{item[1]}
{(Math.random() * 900 + 100).toFixed(0)} rb tayangan · kemarin
)}

{text.shorts}

Explore →
{shorts.map((item) =>
notify(`▶ ${item[0]}`)}>{item[0]}{item[0]}
)}

{text.channels}

{channels.map((item) =>
{item[2]}
{item[0]}{item[1]}
)}
{toast &&
{toast}
}{menu && }; +} +createRoot(document.getElementById('root')).render(); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..8c71331 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,2 @@ +/* Author: fruit.com.cn */ +@font-face{font-family:Jakarta;src:url('/assets/PlusJakartaSans-Regular.ttf')}@font-face{font-family:Jakarta;src:url('/assets/PlusJakartaSans-SemiBold.ttf');font-weight:600}@font-face{font-family:Outfit;src:url('/assets/Outfit-Bold.ttf');font-weight:700}:root{--bg:#0f0f0f;--surface:#181818;--line:#303030;--muted:#aaa;--accent:#ff4b3e;--sidebar:230px}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:#f1f1f1;font:14px Jakarta,Arial,sans-serif}a{color:inherit;text-decoration:none}button,input,select{font:inherit}.topbar{height:64px;position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:24px;padding:0 28px;background:#0f0f0ff2;border-bottom:1px solid #222;backdrop-filter:blur(15px)}.brand{display:flex;align-items:center;gap:8px;font:700 26px Outfit;min-width:150px}.brand-symbol{display:grid;place-items:center;width:28px;height:28px;border-radius:8px;background:var(--accent)}.search-form{display:flex;max-width:620px;flex:1;margin:auto}.search-form input{width:100%;height:40px;padding:0 16px;border:1px solid #3b3b3b;border-radius:22px 0 0 22px;background:#121212;color:#fff}.search-form button{width:54px;border:1px solid #3b3b3b;border-left:0;border-radius:0 22px 22px 0;background:#282828;color:#fff;display:grid;place-items:center}.top-actions{display:flex;align-items:center;gap:14px}.top-actions select{border:1px solid #363636;border-radius:16px;background:#1d1d1d;color:#ddd;padding:6px 9px;font-size:11px}.profile-chip{display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:#d45a68}.icon-button{border:0;background:transparent;color:#fff;display:grid;place-items:center}.sidebar{position:fixed;top:64px;bottom:0;width:var(--sidebar);padding:20px 12px;background:var(--bg);border-right:1px solid #222;z-index:4}.sidebar nav{display:grid;gap:4px}.sidebar nav a,.genre-link{display:flex;align-items:center;gap:16px;width:100%;padding:11px 14px;border:0;border-radius:10px;background:transparent;color:#ccc;text-align:left;cursor:pointer}.sidebar nav a:hover,.genre-link:hover{background:#272727;color:#fff}.sidebar small{display:block;padding:14px;color:#888;font-size:10px;letter-spacing:.12em}.nav-rule{height:1px;background:var(--line);margin:18px 10px}.content{max-width:1500px;padding:28px 34px 80px calc(var(--sidebar) + 34px);margin:auto}.hero-card{position:relative;min-height:350px;overflow:hidden;border-radius:18px;background:#27191a;margin-bottom:44px}.hero-copy{position:relative;z-index:1;width:52%;padding:54px}.hero-copy small,.section-heading span{color:#ff786f;font-size:11px;letter-spacing:.12em;font-weight:600}.hero-copy h1{margin:12px 0;font:700 56px/1 Outfit;letter-spacing:-.04em}.hero-copy h1 em{color:#ff786f;font-style:normal}.hero-copy p{max-width:390px;color:#ccc;line-height:1.7}.primary-button,.chip,.channel-card button{border:0;border-radius:22px;padding:11px 17px;cursor:pointer;font-weight:600}.primary-button{background:#fff;color:#111}.hero-art{position:absolute;inset:0 0 0 40%}.hero-art img{width:100%;height:100%;object-fit:cover}.hero-art:after{content:'';position:absolute;inset:0;background:linear-gradient(90deg,#27191a,rgba(39,25,26,.72) 28%,transparent 80%)}section{margin-bottom:46px}.section-heading{display:flex;justify-content:space-between;align-items:end;margin-bottom:17px}.section-heading h2{margin:0;font:700 24px Outfit}.chip-row{display:flex;gap:8px;overflow:auto;margin-bottom:18px}.chip{background:#2b2b2b;color:#fff;white-space:nowrap}.chip.active,.chip:hover{background:#fff;color:#111}.video-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:24px 16px}.video-card{cursor:pointer;min-width:0}.thumb{position:relative;aspect-ratio:16/9;overflow:hidden;border-radius:10px;background:#222}.thumb img{width:100%;height:100%;object-fit:cover}.thumb b{position:absolute;right:7px;bottom:7px;padding:3px 6px;background:#000c;border-radius:4px;font-size:11px}.video-meta{display:flex;gap:9px;padding-top:9px}.channel-avatar{display:grid;place-items:center;flex:none;width:32px;height:32px;border-radius:50%;font-weight:700}.video-meta strong,.channel-card strong{display:block;line-height:1.4}.video-meta small,.channel-card small{display:block;color:#999;font-size:11px;line-height:1.45;margin-top:4px}.shorts-row{display:grid;grid-template-columns:repeat(6,1fr);gap:14px}.short-card{position:relative;aspect-ratio:9/14;overflow:hidden;border-radius:11px;cursor:pointer}.short-card img{width:100%;height:100%;object-fit:cover}.short-card:after{content:'';position:absolute;inset:45% 0 0;background:linear-gradient(transparent,#000c)}.short-card strong{position:absolute;z-index:1;bottom:10px;left:9px;right:9px;font-size:12px}.channel-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}.channel-card{display:flex;align-items:center;gap:10px;padding:14px;border:1px solid var(--line);border-radius:11px}.channel-card button{margin-left:auto;padding:8px 11px;background:#fff;color:#111}.bottom-nav,.mobile-only{display:none}.toast{position:fixed;left:50%;bottom:24px;z-index:8;transform:translateX(-50%);background:#fff;color:#111;padding:11px 16px;border-radius:7px}.scrim{position:fixed;inset:0;z-index:3;background:#0008;border:0}@media(max-width:1100px){.video-grid{grid-template-columns:repeat(3,1fr)}.shorts-row{grid-template-columns:repeat(5,1fr)}.channel-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:760px){.topbar{height:58px;padding:0 14px;gap:12px}.mobile-only{display:grid}.sidebar{display:none;top:58px;width:245px}.sidebar.open{display:block}.desktop-only{display:none}.brand{min-width:auto;font-size:23px}.content{padding:20px 14px 90px}.hero-card{min-height:420px;margin:0 -14px 36px;border-radius:0;display:flex;align-items:flex-end}.hero-copy{width:100%;padding:24px}.hero-copy h1{font-size:42px}.hero-art{inset:0}.hero-art:after{background:linear-gradient(0deg,#27191a,rgba(39,25,26,.35) 55%,transparent)}.video-grid{grid-template-columns:repeat(2,1fr);gap:22px 12px}.shorts-row{display:flex;overflow:auto;margin-right:-14px}.short-card{flex:0 0 130px}.channel-grid{grid-template-columns:1fr}.bottom-nav{position:fixed;display:flex;justify-content:space-around;align-items:center;left:0;right:0;bottom:0;height:66px;z-index:5;background:#151515;border-top:1px solid #292929}.bottom-nav a{display:grid;place-items:center;gap:4px;color:#aaa;font-size:10px}.bottom-nav a:hover{color:#fff}} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..144d4e2 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,5 @@ +// Author: fruit.com.cn +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ plugins: [react()] });