增加bartex的style

This commit is contained in:
ymk
2025-08-11 10:38:30 +08:00
parent a293f985f5
commit 4a8e375ad0
21 changed files with 634 additions and 384 deletions

View File

@@ -6,7 +6,8 @@
"Header": {
"appName": "Fast3DText",
"editorName": "Editor",
"blogName": "Blog"
"blogName": "Blog",
"styleName": "Style Library"
},
"ErrorPage": {
"title": "Some thing went wrong",
@@ -106,5 +107,28 @@
"ctaTitle": "Create Your 3D Background Now",
"ctaSubtitle": "Start generating professional 3D 'Do Not Write On This Page' designs",
"ctaButton": "Generate 3D Text"
},
"StylePage": {
"title": "3D Text Styles Library",
"seoTitle": "3D Text Styles Library",
"seoDescription": "Explore our collection of 3D text styles tutorials, tips and inspiration. Learn how to create stunning 3D text effects for your projects.",
"templateFontLabel": "Font",
"templateBgColorLabel": "Background Color",
"templateTextColorLabel": "Text Color",
"templateTextGradientColorLabel": "Text Gradient Color"
},
"StyleBarbie": {
"title": "Create Stunning Barbie-Pink 3D Text",
"summary": "Use our online 3D text editor to craft eye-catching Barbie-inspired designs.",
"cta": "Start Designing Now",
"simpleTitle": "Barbie Pink 3D Text Generator Online",
"description": "Looking for a way to create Barbie-style 3D text? Our editor lets you apply pink gradients, custom fonts, and high-quality 3D effects all from your browser.",
"templateTitle": "Template Style",
"templateFont": "Bartex",
"templateBgColor": "#ffe3f0 (soft pink)",
"templateTextGradient": "#ff5ac7 → #ff94da",
"tipsTitle": "Tips",
"content1": "The Barbie-pink style is perfect for playful logos, birthday cards, social media graphics, and more. With our easy-to-use editor, you can adjust shadows, lighting, and font styles effortlessly.",
"content2": "Get started in seconds—no downloads required. Just enter your text, choose a style, and export beautiful 3D visuals instantly."
}
}

View File

@@ -6,7 +6,8 @@
"Header": {
"appName": "Fast3DText",
"editorName": "编辑器",
"blogName": "博文记录"
"blogName": "博文记录",
"styleName": "模板库"
},
"ErrorPage": {
"title": " 错误页面",
@@ -106,5 +107,28 @@
"ctaTitle": "立即创建3D'请勿在此页书写'背景",
"ctaSubtitle": "开始生成专业的3D文字设计",
"ctaButton": "生成3D文字"
},
"StylePage": {
"title": "模板库",
"seoTitle": "3D文字模板库",
"seoDescription": "探索我们的3D文字设计不同风格集合。学习如何为您的项目创建惊艳的3D文字效果。",
"templateFontLabel": "字体",
"templateBgColorLabel": "背景颜色",
"templateTextColorLabel": "文字颜色",
"templateTextGradientColorLabel": "文字渐变色"
},
"StyleBarbie": {
"title": "创建梦幻芭比粉3D文字",
"summary": "使用我们的在线3D文字编辑器轻松打造吸睛的3D芭比风格设计。",
"cta": "立即开始设计",
"simpleTitle": "在线芭比粉3D文字生成器",
"description": "想要制作芭比风格的3D文字我们的编辑器支持粉色渐变、自定义字体与高质量的三维效果全部在浏览器中完成。",
"templateTitle": "模板风格",
"templateFont": "Bartex",
"templateBgColor": "#ffe3f0柔和粉色",
"templateTextGradient": "#ff5ac7 → #ff94da",
"tipsTitle": "提示",
"content1": "芭比粉风格非常适合用于可爱风格的Logo、生日卡片、社交媒体图像等。只需几步操作即可轻松调整阴影、光照和字体样式。",
"content2": "无需下载几秒即可上手。输入文字选择风格即刻导出精美的3D视觉效果。"
}
}

View File

@@ -17,7 +17,7 @@
"crypto-js": "^4.2.0",
"lucide-react": "^0.536.0",
"lz-string": "^1.5.0",
"motion": "^12.23.11",
"motion": "^12.23.12",
"next": "15.2.4",
"next-intl": "^4.3.4",
"next-themes": "^0.4.6",
@@ -40,7 +40,7 @@
"eslint": "^9.32.0",
"eslint-config-next": "15.2.3",
"tailwindcss": "^4.1.11",
"typescript": "^5.8.3"
"typescript": "^5.9.2"
},
"overrides": {
"@types/react": "19.0.12",

252
pnpm-lock.yaml generated
View File

@@ -33,14 +33,14 @@ importers:
specifier: ^1.5.0
version: 1.5.0
motion:
specifier: ^12.23.11
version: 12.23.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
specifier: ^12.23.12
version: 12.23.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
next:
specifier: 15.2.4
version: 15.2.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
next-intl:
specifier: ^4.3.4
version: 4.3.4(next@15.2.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.8.3)
version: 4.3.4(next@15.2.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -52,7 +52,7 @@ importers:
version: 19.0.0(react@19.0.0)
react-schemaorg:
specifier: ^2.0.0
version: 2.0.0(react@19.0.0)(schema-dts@1.1.5)(typescript@5.8.3)
version: 2.0.0(react@19.0.0)(schema-dts@1.1.5)(typescript@5.9.2)
schema-dts:
specifier: ^1.1.5
version: 1.1.5
@@ -92,13 +92,13 @@ importers:
version: 9.32.0(jiti@2.5.1)
eslint-config-next:
specifier: 15.2.3
version: 15.2.3(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
version: 15.2.3(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
tailwindcss:
specifier: ^4.1.11
version: 4.1.11
typescript:
specifier: ^5.8.3
version: 5.8.3
specifier: ^5.9.2
version: 5.9.2
packages:
@@ -160,14 +160,14 @@ packages:
resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@floating-ui/core@1.7.2':
resolution: {integrity: sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==}
'@floating-ui/core@1.7.3':
resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
'@floating-ui/dom@1.7.2':
resolution: {integrity: sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==}
'@floating-ui/dom@1.7.3':
resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==}
'@floating-ui/react-dom@2.1.4':
resolution: {integrity: sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==}
'@floating-ui/react-dom@2.1.5':
resolution: {integrity: sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
@@ -1255,63 +1255,63 @@ packages:
'@types/webxr@0.5.22':
resolution: {integrity: sha512-Vr6Stjv5jPRqH690f5I5GLjVk8GSsoQSYJ2FVd/3jJF7KaqfwPi3ehfBS96mlQ2kPCwZaX6U0rG2+NGHBKkA/A==}
'@typescript-eslint/eslint-plugin@8.38.0':
resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==}
'@typescript-eslint/eslint-plugin@8.39.0':
resolution: {integrity: sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.38.0
'@typescript-eslint/parser': ^8.39.0
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/parser@8.38.0':
resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==}
'@typescript-eslint/parser@8.39.0':
resolution: {integrity: sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/project-service@8.38.0':
resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==}
'@typescript-eslint/project-service@8.39.0':
resolution: {integrity: sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/scope-manager@8.38.0':
resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
'@typescript-eslint/scope-manager@8.39.0':
resolution: {integrity: sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.38.0':
resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==}
'@typescript-eslint/tsconfig-utils@8.39.0':
resolution: {integrity: sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/type-utils@8.38.0':
resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==}
'@typescript-eslint/type-utils@8.39.0':
resolution: {integrity: sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/types@8.38.0':
resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
'@typescript-eslint/types@8.39.0':
resolution: {integrity: sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.38.0':
resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
'@typescript-eslint/typescript-estree@8.39.0':
resolution: {integrity: sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <5.9.0'
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/utils@8.38.0':
resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==}
'@typescript-eslint/utils@8.39.0':
resolution: {integrity: sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/visitor-keys@8.38.0':
resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
'@typescript-eslint/visitor-keys@8.39.0':
resolution: {integrity: sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
@@ -1688,8 +1688,8 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
enhanced-resolve@5.18.2:
resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==}
enhanced-resolve@5.18.3:
resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
engines: {node: '>=10.13.0'}
es-abstract@1.24.0:
@@ -1898,8 +1898,8 @@ packages:
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
engines: {node: '>= 0.4'}
framer-motion@12.23.11:
resolution: {integrity: sha512-VzNi+exyI3bn7Pzvz1Fjap1VO9gQu8mxrsSsNamMidsZ8AA8W2kQsR+YQOciEUbMtkKAWIbPHPttfn5e9jqqJQ==}
framer-motion@12.23.12:
resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
@@ -2300,14 +2300,14 @@ packages:
engines: {node: '>=10'}
hasBin: true
motion-dom@12.23.9:
resolution: {integrity: sha512-6Sv++iWS8XMFCgU1qwKj9l4xuC47Hp4+2jvPfyTXkqDg2tTzSgX6nWKD4kNFXk0k7llO59LZTPuJigza4A2K1A==}
motion-dom@12.23.12:
resolution: {integrity: sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==}
motion-utils@12.23.6:
resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==}
motion@12.23.11:
resolution: {integrity: sha512-AHv/2SivIz9fjvND8wwN2LldDTuwkPyTSWecAY/xzB1/2eF7zxvh9JRkf8aF4eGoGsy1e2YKp+CQC5yxcssnEw==}
motion@12.23.12:
resolution: {integrity: sha512-8jCD8uW5GD1csOoqh1WhH1A6j5APHVE15nuBkFeRiMzYBdRwyAHmSP/oXSuW0WJPZRXTFdBoG4hY9TFWNhhwng==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
@@ -2770,8 +2770,8 @@ packages:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
typescript@5.8.3:
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
typescript@5.9.2:
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
engines: {node: '>=14.17'}
hasBin: true
@@ -2922,18 +2922,18 @@ snapshots:
'@eslint/core': 0.15.1
levn: 0.4.1
'@floating-ui/core@1.7.2':
'@floating-ui/core@1.7.3':
dependencies:
'@floating-ui/utils': 0.2.10
'@floating-ui/dom@1.7.2':
'@floating-ui/dom@1.7.3':
dependencies:
'@floating-ui/core': 1.7.2
'@floating-ui/core': 1.7.3
'@floating-ui/utils': 0.2.10
'@floating-ui/react-dom@2.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
'@floating-ui/react-dom@2.1.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@floating-ui/dom': 1.7.2
'@floating-ui/dom': 1.7.3
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
@@ -3520,7 +3520,7 @@ snapshots:
'@radix-ui/react-popper@1.2.7(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
dependencies:
'@floating-ui/react-dom': 2.1.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@floating-ui/react-dom': 2.1.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.0.4(@types/react@19.0.12))(@types/react@19.0.12)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.12)(react@19.0.0)
'@radix-ui/react-context': 1.1.2(@types/react@19.0.12)(react@19.0.0)
@@ -3902,7 +3902,7 @@ snapshots:
'@tailwindcss/node@4.1.11':
dependencies:
'@ampproject/remapping': 2.3.0
enhanced-resolve: 5.18.2
enhanced-resolve: 5.18.3
jiti: 2.5.1
lightningcss: 1.30.1
magic-string: 0.30.17
@@ -4012,97 +4012,97 @@ snapshots:
'@types/webxr@0.5.22': {}
'@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
'@typescript-eslint/eslint-plugin@8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/type-utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/visitor-keys': 8.38.0
'@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/scope-manager': 8.39.0
'@typescript-eslint/type-utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/visitor-keys': 8.39.0
eslint: 9.32.0(jiti@2.5.1)
graphemer: 1.4.0
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.1.0(typescript@5.8.3)
typescript: 5.8.3
ts-api-utils: 2.1.0(typescript@5.9.2)
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
'@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
'@typescript-eslint/visitor-keys': 8.38.0
'@typescript-eslint/scope-manager': 8.39.0
'@typescript-eslint/types': 8.39.0
'@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
'@typescript-eslint/visitor-keys': 8.39.0
debug: 4.4.1
eslint: 9.32.0(jiti@2.5.1)
typescript: 5.8.3
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.38.0(typescript@5.8.3)':
'@typescript-eslint/project-service@8.39.0(typescript@5.9.2)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3)
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2)
'@typescript-eslint/types': 8.39.0
debug: 4.4.1
typescript: 5.8.3
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/scope-manager@8.38.0':
'@typescript-eslint/scope-manager@8.39.0':
dependencies:
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/visitor-keys': 8.38.0
'@typescript-eslint/types': 8.39.0
'@typescript-eslint/visitor-keys': 8.39.0
'@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.8.3)':
'@typescript-eslint/tsconfig-utils@8.39.0(typescript@5.9.2)':
dependencies:
typescript: 5.8.3
typescript: 5.9.2
'@typescript-eslint/type-utils@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
'@typescript-eslint/type-utils@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
'@typescript-eslint/utils': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/types': 8.39.0
'@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
'@typescript-eslint/utils': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
debug: 4.4.1
eslint: 9.32.0(jiti@2.5.1)
ts-api-utils: 2.1.0(typescript@5.8.3)
typescript: 5.8.3
ts-api-utils: 2.1.0(typescript@5.9.2)
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/types@8.38.0': {}
'@typescript-eslint/types@8.39.0': {}
'@typescript-eslint/typescript-estree@8.38.0(typescript@5.8.3)':
'@typescript-eslint/typescript-estree@8.39.0(typescript@5.9.2)':
dependencies:
'@typescript-eslint/project-service': 8.38.0(typescript@5.8.3)
'@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3)
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/visitor-keys': 8.38.0
'@typescript-eslint/project-service': 8.39.0(typescript@5.9.2)
'@typescript-eslint/tsconfig-utils': 8.39.0(typescript@5.9.2)
'@typescript-eslint/types': 8.39.0
'@typescript-eslint/visitor-keys': 8.39.0
debug: 4.4.1
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
semver: 7.7.2
ts-api-utils: 2.1.0(typescript@5.8.3)
typescript: 5.8.3
ts-api-utils: 2.1.0(typescript@5.9.2)
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)':
'@typescript-eslint/utils@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)':
dependencies:
'@eslint-community/eslint-utils': 4.7.0(eslint@9.32.0(jiti@2.5.1))
'@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
'@typescript-eslint/scope-manager': 8.39.0
'@typescript-eslint/types': 8.39.0
'@typescript-eslint/typescript-estree': 8.39.0(typescript@5.9.2)
eslint: 9.32.0(jiti@2.5.1)
typescript: 5.8.3
typescript: 5.9.2
transitivePeerDependencies:
- supports-color
'@typescript-eslint/visitor-keys@8.38.0':
'@typescript-eslint/visitor-keys@8.39.0':
dependencies:
'@typescript-eslint/types': 8.38.0
'@typescript-eslint/types': 8.39.0
eslint-visitor-keys: 4.2.1
'@unrs/resolver-binding-android-arm-eabi@1.11.1':
@@ -4427,7 +4427,7 @@ snapshots:
emoji-regex@9.2.2: {}
enhanced-resolve@5.18.2:
enhanced-resolve@5.18.3:
dependencies:
graceful-fs: 4.2.11
tapable: 2.2.2
@@ -4535,21 +4535,21 @@ snapshots:
escape-string-regexp@4.0.0: {}
eslint-config-next@15.2.3(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3):
eslint-config-next@15.2.3(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2):
dependencies:
'@next/eslint-plugin-next': 15.2.3
'@rushstack/eslint-patch': 1.12.0
'@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/eslint-plugin': 8.39.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
'@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-react: 7.37.5(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-react-hooks: 5.2.0(eslint@9.32.0(jiti@2.5.1))
optionalDependencies:
typescript: 5.8.3
typescript: 5.9.2
transitivePeerDependencies:
- eslint-import-resolver-webpack
- eslint-plugin-import-x
@@ -4574,22 +4574,22 @@ snapshots:
tinyglobby: 0.2.14
unrs-resolver: 1.11.1
optionalDependencies:
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)):
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.32.0(jiti@2.5.1))
transitivePeerDependencies:
- supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)):
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1)):
dependencies:
'@rtsao/scc': 1.1.0
array-includes: 3.1.9
@@ -4600,7 +4600,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.32.0(jiti@2.5.1)
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.32.0(jiti@2.5.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -4612,7 +4612,7 @@ snapshots:
string.prototype.trimend: 1.0.9
tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.38.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.8.3)
'@typescript-eslint/parser': 8.39.0(eslint@9.32.0(jiti@2.5.1))(typescript@5.9.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
@@ -4788,9 +4788,9 @@ snapshots:
dependencies:
is-callable: 1.2.7
framer-motion@12.23.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
framer-motion@12.23.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
motion-dom: 12.23.9
motion-dom: 12.23.12
motion-utils: 12.23.6
tslib: 2.8.1
optionalDependencies:
@@ -5168,15 +5168,15 @@ snapshots:
mkdirp@3.0.1: {}
motion-dom@12.23.9:
motion-dom@12.23.12:
dependencies:
motion-utils: 12.23.6
motion-utils@12.23.6: {}
motion@12.23.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
motion@12.23.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
framer-motion: 12.23.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
framer-motion: 12.23.12(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
tslib: 2.8.1
optionalDependencies:
react: 19.0.0
@@ -5192,7 +5192,7 @@ snapshots:
negotiator@1.0.0: {}
next-intl@4.3.4(next@15.2.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.8.3):
next-intl@4.3.4(next@15.2.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.9.2):
dependencies:
'@formatjs/intl-localematcher': 0.5.10
negotiator: 1.0.0
@@ -5200,7 +5200,7 @@ snapshots:
react: 19.0.0
use-intl: 4.3.4(react@19.0.0)
optionalDependencies:
typescript: 5.8.3
typescript: 5.9.2
next-themes@0.4.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
@@ -5428,11 +5428,11 @@ snapshots:
optionalDependencies:
'@types/react': 19.0.12
react-schemaorg@2.0.0(react@19.0.0)(schema-dts@1.1.5)(typescript@5.8.3):
react-schemaorg@2.0.0(react@19.0.0)(schema-dts@1.1.5)(typescript@5.9.2):
dependencies:
react: 19.0.0
schema-dts: 1.1.5
typescript: 5.8.3
typescript: 5.9.2
react-style-singleton@2.2.3(@types/react@19.0.12)(react@19.0.0):
dependencies:
@@ -5707,9 +5707,9 @@ snapshots:
dependencies:
is-number: 7.0.0
ts-api-utils@2.1.0(typescript@5.8.3):
ts-api-utils@2.1.0(typescript@5.9.2):
dependencies:
typescript: 5.8.3
typescript: 5.9.2
tsconfig-paths@3.15.0:
dependencies:
@@ -5757,7 +5757,7 @@ snapshots:
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
typescript@5.8.3: {}
typescript@5.9.2: {}
unbox-primitive@1.1.0:
dependencies:

View File

@@ -1,7 +1,7 @@
import { getTranslations } from "next-intl/server";
import Footer from "@/components/Footer";
import Header from "@/components/Header";
import Editor from "@/components/SimpleEditor";
import Editor from "@/components/FullEditor";
import { useTranslations } from "next-intl";
import { Locales } from "@/i18n/config";
import { Metadata } from "next";

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View File

@@ -0,0 +1,148 @@
import Image from 'next/image';
import { useLocale, useTranslations } from 'next-intl';
import { StyleItem } from '../list';
import { Locales } from '@/i18n/config';
import { Metadata } from 'next';
import { Box, Flex, Container, Heading, Text, Button, Strong } from '@radix-ui/themes';
import Footer from '@/components/Footer';
import Header from '@/components/Header';
import highCover from "./1024_576.png";
import cover from "./512_288.png";
const host = process.env.NEXT_PUBLIC_HOST;
export const styleContent = {
id: "barbie-pink",
cover: cover,
date: "2025-08-07",
en: {
title: "Barbie-Pink 3D Text",
summary: "Use our online 3D text editor to craft eye-catching Barbie-inspired designs.",
},
zh: {
title: "芭比粉3D文本",
summary: "使用我们的在线3D文本编辑器为项目创建引人注目的芭比粉3D文本。",
},
} satisfies StyleItem;
export default function BarbiePinkLandingPage() {
const t = useTranslations('StyleBarbie');
const common = useTranslations('StylePage');
const locale = useLocale();
return (
<Flex direction={"column"} >
<Header />
<Flex justify={"center"} width={"full"} p={"4"} style={{ backgroundColor: 'var(--pink-3)' }}>
<Flex className="min-h-screen" direction={"column"} gap={"3"}>
<Flex justify="center" p="4">
<Image
src={highCover}
alt="3D Barbie Text Example"
width={300}
height={169}
style={{ borderRadius: 'var(--radius-3)', boxShadow: 'var(--shadow-4)' }}
/>
</Flex>
<Container>
<Flex direction="column" align="center" gap="4">
<Heading as='h1' size="8" align="center">{t('title')}</Heading>
<Text size="5" align="center">{t('description')}</Text>
</Flex>
</Container>
<Container className='text-center'>
<Heading as='h2' size="5" mb="4" >{t('templateTitle')}</Heading>
<Flex gap={"2"} direction={"column"} >
<Flex gap={"2"} justify={"center"}>
<Text className='font-bold'>{common("templateFontLabel")}:</Text>
<Text size="3">{t('templateFont')}</Text>
</Flex>
<Flex gap={"2"} justify={"center"}>
<Text className='font-bold'>{common("templateBgColorLabel")}:</Text>
<Text size="3">{t('templateBgColor')}</Text>
</Flex>
<Flex gap={"2"} justify={"center"}>
<Text className='font-bold'>{common("templateTextGradientColorLabel")}:</Text>
<Text size="3">{t('templateTextGradient')}</Text>
</Flex>
</Flex>
</Container>
<Container className='text-center'>
<Heading as='h2' size="6" mb="4">{t('tipsTitle')}</Heading>
<Text as="p" mb="4">{t('content1')}</Text>
<Text as="p" mb="4">{t('content2')}</Text>
</Container>
<Flex direction={"column"} gap={"2"} justify={"center"} align={"center"}>
<Button size="3" asChild >
<a href={`/${locale}/editor/U2FsdGVkX19rP8xCyBPFUL%2FO0fDre3wZBjUP%2FxsyN60rkJvZFHgAhV1OIq3LX7XhLRacG0NzByrl7Xt1t9tAt2PO8UkFCRk7fABsu7HlfxSyIYeE%2Bp6ikdiqfIO%2BNEzNxx3GzasHxdw1FxEaOtZcspT8hIWpxb59WXsJ%2BvheZpiZV8N%2FqYZTUMSWD0GYX1AOi6bWV%2BmTHp8hRJzko1SfrWWX5%2F9NxCrxYeAhFpXxH%2FFKtt3EAlg4XDrvjsqIvX%2FDbESOv7reY3HYydZnBFKbGdALPqNiIuHxcMpChrIqxSebNhKbBVDOy24yAR7aYBNuZYN1BHZCV9tpa3tfzy2B6dVaW80zNBSBpypi7foPYVQDJ8K9QggnWHXqED4V65LSApKoCcm56W1A%2BP%2BMWKmMJw%3D%3D`}>{t('cta')}</a>
</Button>
<Button size="3" variant="soft" asChild>
<a href={`/${locale}/editor/U2FsdGVkX1%2BitOSouauvY7pzI7LQf%2BxXMLoslpg1yqvA1SCE53KtvcsLe5lHk9HUQ7TDbae9MMFc%2F%2FiNYT7sUyftCN2UgjelVSqfUYI9gk%2FlFCnTkuzd4iPPWqaOR5fwoxHkebMnqBTOfi%2F6vJBc2i0ujKQDgGB%2Byny1ygk60%2F0P65eR6wTMPWCU5mTVa7ZDGYKl7uFUMipmu3c2nirDn%2BWzHQBQoGH9xvxWNhqlbM0oZLY8fMSJT%2FpZ1RjVZ785pAUP5wFMUc5yJhvBLs6C8uLYaGjilqTfJn5NflecZZ3vgG%2FZv0TWgrRoScM1OmvQhIDiAnXCGjFQ3Ek2s6otpXf%2FG0OPTPMqx964F4iqgvfrOuZGY5q4DwWr48tVKjrORmgOPqC846MvaQxWCvpnVQ%3D%3D`}>you can also try <Strong>Barbie_Doll</Strong></a>
</Button>
<Button size="3" variant="soft" asChild>
<a href={`/${locale}/editor/U2FsdGVkX1%2FIkxznkprehHz%2FtkJbwnqS3IzlVZhN9HOq%2Fv2IB9gBj2ZPWnR1879R1bYzi5iy77X%2B9tGWnZO0p%2BxtjCwQcYLij%2BFc8VcJafJLvErwByaUlZCKqEjnZ1rKqxO1EsiJdBKJFjRE0PsgUqk5E2kM6LaO03jpXk7D1zey3pYfWsj5%2FjZxLdtD5w146bmMwI7ygRik9wFxlizZV%2BrYlzCoB7lqSDb9%2FkvH2ZqAV72TFCwSLqLGm%2B%2FnGs7VlOro%2FPU99jgxVKnNQpZz7lIaCt30qgZC3i6UCftQt00TrxzlGwSIA0iG8w2GnV2%2BAzrjje1JOgp%2Bg8onEuuMzMIxpxjzjrdhDSHzxyErt3Ag2Sk2jKvkFy0R%2FDT2H5GHitpPSTBWJVFi0lc5zAdoaDfeIKX%2BULZUXYzErDuOB3U%3D`}>you can also try <Strong>Barbie_Princess</Strong></a>
</Button>
</Flex>
</Flex>
</Flex>
<Footer />
</Flex>
);
}
const locales = Locales;
export function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const name = styleContent.id;
const title = styleContent[locale as "en" | "zh"].title;
const description = styleContent[locale as "en" | "zh"].summary;
return {
title,
description,
keywords: [],
openGraph: {
title,
description,
url: `${host}/${locale}/styles/${name}`,
images: [
{
url: `${process.env.NEXT_PUBLIC_HOST}/og-image.png`,
width: 1200,
height: 630,
alt: title,
},
],
locale: locale,
type: "website",
},
twitter: {
card: "summary_large_image",
title,
description,
images: [`${process.env.NEXT_PUBLIC_HOST}/og-image.png`],
},
alternates: {
canonical: `${host}/styles/${name}`,
languages: {
en: `${host}/en/styles/${name}`,
zh: `${host}/zh/styles/${name}`,
},
},
};
}

View File

@@ -0,0 +1,17 @@
import { StaticImageData } from "next/image";
import { styleContent as barbieStyle } from "./barbie-pink/page";
export interface StyleItem {
id: string;
date: string;
cover: StaticImageData;
en: {
title: string;
summary: string;
};
zh: {
title: string;
summary: string;
};
}
export const styles: StyleItem[] = [barbieStyle];

View File

@@ -0,0 +1,94 @@
import Footer from '@/components/Footer'
import Header from '@/components/Header'
import { Card, Flex, Text, Heading, Box, Link } from '@radix-ui/themes'
import { styles } from './list'
import { useLocale, useTranslations } from 'next-intl';
import { Locales } from '@/i18n/config';
import { Metadata } from 'next';
import { getTranslations } from 'next-intl/server';
import Image from 'next/image';
const host = process.env.NEXT_PUBLIC_HOST;
export default function StyleListPage() {
const locale = useLocale() as "zh" | "en";
const t = useTranslations("StylePage");
return (
<Flex direction={"column"} gap={"4"}>
<Header />
<Box p="4" className="text-center">
<Heading as='h1' size="7" mb="4">{t("title")}</Heading>
<Flex justify={"center"} gap={"4"} wrap={"wrap"}>
{styles.map(styleItem => (
<Card key={styleItem.id} size="2" style={{ maxWidth: 300, boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)' }} mx="4" my="4">
<Link href={`/${locale}/styles/${styleItem.id}`} color='iris'>
<Flex direction="column" gap="4">
<Box style={{ overflow: 'hidden' }}>
<Image src={styleItem.cover} alt={styleItem[locale].title} width={512} height={288} />
</Box>
<Flex direction={"column"} gap={"1"}>
<Heading as='h2' size="5" weight="bold" className='text-black dark:text-white'>{styleItem[locale].title}</Heading>
<Text color="gray" >{styleItem.date}</Text>
<Text truncate={true} >{styleItem[locale].summary}</Text>
</Flex>
</Flex>
</Link>
</Card>
))}
</Flex>
</Box>
<Footer />
</Flex>
)
}
const locales = Locales;
export function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}
export async function generateMetadata({
params,
}: {
params: Promise<{ locale: string }>;
}): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "StylePage" });
const name = "styles";
return {
title: t("seoTitle"),
description: t("seoDescription"),
openGraph: {
title: t("seoTitle"),
description: t("seoDescription"),
url: `${host}/${locale}/${name}`,
images: [
{
url: `${process.env.NEXT_PUBLIC_HOST}/og-image.png`,
width: 1200,
height: 630,
alt: t("seoTitle"),
},
],
locale: locale,
type: "website",
},
twitter: {
card: "summary_large_image",
title: t("seoTitle"),
description: t("seoDescription"),
images: [`${process.env.NEXT_PUBLIC_HOST}/og-image.png`],
},
alternates: {
canonical: `${host}/${name}`,
languages: {
en: `${host}/en/${name}`,
zh: `${host}/zh/${name}`,
},
},
};
}

View File

@@ -1,73 +1,39 @@
import { Locales } from "@/i18n/config";
import { MetadataRoute } from "next";
import { blogs } from "./[locale]/blogs/list";
import { styles } from "./[locale]/styles/list";
const host = process.env.NEXT_PUBLIC_HOST;
export default function sitemap(): MetadataRoute.Sitemap {
const baseUrl = host!;
const locales = Locales;
const urls = ["/", "/blogs", "/editor", "/do-not-write-on-this-page"];
blogs.forEach((blog) => {
urls.push(`/blogs/${blog.id}`);
});
styles.forEach((style) => {
urls.push(`/styles/${style.id}`);
});
const multiLocaleUrls = locales
.map((locale) =>
urls.map((url) => ({
url: `${baseUrl}/${locale}${url}`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: 0.8,
}))
)
.flat();
return [
{
url: baseUrl,
...urls.map((url) => ({
url: `${baseUrl}${url}`,
lastModified: new Date(),
changeFrequency: "yearly",
changeFrequency: "daily" as const,
priority: 1,
},
...locales.map((locale) => ({
url: `${baseUrl}/${locale}`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: 0.8,
})),
{
url: baseUrl + "/editor",
lastModified: new Date(),
changeFrequency: "yearly",
priority: 1,
},
...locales.map((locale) => ({
url: `${baseUrl}/${locale}/editor`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: 0.8,
})),
{
url: baseUrl + "/do-not-write-on-this-page",
lastModified: new Date(),
changeFrequency: "yearly",
priority: 1,
},
...locales.map((locale) => ({
url: `${baseUrl}/${locale}/do-not-write-on-this-page`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: 0.8,
})),
{
url: baseUrl + "/blogs",
lastModified: new Date(),
changeFrequency: "yearly",
priority: 1,
},
...locales.map((locale) => ({
url: `${baseUrl}/${locale}/blogs`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: 0.8,
})),
{
url: baseUrl + "/blogs/Create-3D-Text-with-the-Barbie-Font",
lastModified: new Date(),
changeFrequency: "yearly",
priority: 1,
},
...locales.map((locale) => ({
url: `${baseUrl}/${locale}/blogs/Create-3D-Text-with-the-Barbie-Font`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: 0.8,
})),
...multiLocaleUrls,
];
}

View File

@@ -20,12 +20,19 @@ export default function Footer() {
>
{t("editorName")}
</Link>
<Link
href={`/${locale}/styles`}
className="text-sm text-muted-foreground hover:text-primary"
>
{t("styleName")}
</Link>
<Link
href={`/${locale}/blogs`}
className="text-sm text-muted-foreground hover:text-primary"
>
{t("blogName")}
</Link>
<Link
href="/features-form"
className="text-sm text-muted-foreground hover:text-primary"

View File

@@ -1,5 +1,5 @@
"use client";
import { Flex, Box, Link } from "@radix-ui/themes";
import { Flex } from "@radix-ui/themes";
import BackgroundSelector, {
BackgroundProp,
} from "./common/BackgroundSelector";
@@ -7,8 +7,6 @@ import PreviewToolbar from "./common/PreviewToolbar";
import { useState } from "react";
import { useTranslations } from "next-intl";
import TextSetting, { TextProp } from "./common/TextSetting";
import { useSearchParams } from "next/navigation";
import { decodeText } from "@/lib/utils";
/**
* 全特性工具栏

View File

@@ -24,18 +24,20 @@ export default function Header() {
{t("editorName")}
</Link>
<Link
href={`/${locale}/styles`}
className="text-sm text-muted-foreground hover:text-primary"
>
{t("styleName")}
</Link>
<Link
href={`/${locale}/blogs`}
className="text-sm text-muted-foreground hover:text-primary"
>
{t("blogName")}
</Link>
<Link
href="/features-form"
className="text-sm text-muted-foreground hover:text-primary"
>
Features Wanted
</Link>
</Flex>
<Flex align="center" gap="4" className="w-1/4">

View File

@@ -1,81 +0,0 @@
"use client";
import { Flex, Box, Link } from "@radix-ui/themes";
import BackgroundSelector, {
BackgroundProp,
} from "./common/BackgroundSelector";
import PreviewToolbar from "./common/PreviewToolbar";
import SimpleTextSetting from "./common/SimpleTextSetting";
import { useEffect, useState } from "react";
import { useLocale, useTranslations } from "next-intl";
import { TextProp } from "./common/TextSetting";
import { useRouter } from "@/i18n/navigation";
/**
* 简易工具
* @returns
*/
export default function SimpleEditor({ textProp, backgroundProp }: { textProp: TextProp | undefined, backgroundProp: BackgroundProp | undefined }) {
const t = useTranslations("TextEditor");
const tIndex = useTranslations("HomePage");
const router = useRouter();
backgroundProp = {
type: "color",
color: "#c4b1b1",
image: null,
} satisfies BackgroundProp;
textProp = textProp || TextProp.default(t("defaultText"));
const [background, setBackground] = useState<BackgroundProp>(backgroundProp);
const [text, setText] = useState<TextProp>(textProp);
// useEffect(() => {
// let bg = sessionStorage.getItem("background");
// if (bg) {
// console.log("初始化设置 bg", bg);
// setBackground(JSON.parse(bg));
// }
// let txt = sessionStorage.getItem("text");
// if (txt) {
// console.log("初始化设置 txt", txt);
// setText(JSON.parse(txt));
// }
// }, []);
useEffect(() => {
sessionStorage.setItem("background", JSON.stringify(background));
}, [background]);
useEffect(() => {
sessionStorage.setItem("text", JSON.stringify(text));
}, [text]);
const locale = useLocale();
return (
<Flex gap={"2"}>
<Flex gap={"2"} direction={"column"} className="w-1/3">
<BackgroundSelector
background={background}
setBackground={setBackground}
/>
<SimpleTextSetting text={text} setText={setText} />
<Box className="text-center"> <Link href={`/${locale}/editor`} >{tIndex("toolMore")} ?</Link> </Box>
</Flex>
<Flex className="w-2/3" direction={"column"} justify={"between"}>
<PreviewToolbar background={background} text={text} />
</Flex>
</Flex>
);
}

View File

@@ -40,7 +40,7 @@ export default function BackgroundSelector({
};
return (
<Box className="space-y-4 p-4 border rounded-lg min-w-64">
<Box className="p-4 border rounded-lg min-w-64">
<Heading as="h2" size="4" className="font-medium text-lg">{t("title")}</Heading>
<Flex gap={"2"} p="2">
@@ -53,13 +53,24 @@ export default function BackgroundSelector({
<Box className="w-full">
{background.type === "color" && (
<input
type="color"
id="color-picker"
value={background.color}
onChange={handleColorChange}
className="w-full h-10 rounded-md cursor-pointer"
/>
<Flex gap={"6"} p="2">
<input
type="color"
id="color-picker"
value={background.color}
onChange={handleColorChange}
className="w-1/3 h-10 rounded-md cursor-pointer"
/>
<input
type="text"
value={background.color}
onChange={handleColorChange}
className="w-1/3 h-10 rounded-md cursor-pointer pl-4"
/>
</Flex>
)}
{background.type === "image" && (

View File

@@ -32,8 +32,9 @@ export default function PreviewToolbar({
}) {
let host = process.env.NEXT_PUBLIC_HOST?.substring("https://".length);
const t = useTranslations("PreviewBar");
const [aspectRadio, setAspectRadio] = useState<number>(0);
const split = Sizes[0].split("x").map(Number);
const initAspectRadio = 0;
const [aspectRadio, setAspectRadio] = useState<number>(initAspectRadio);
const split = Sizes[initAspectRadio].split("x").map(Number);
const [size, setSize] = useState<Size>({ width: split[0], height: split[1] });
const container = useRef<HTMLCanvasElement>(null);
const fullscreenElement = useRef<HTMLImageElement>(null);

View File

@@ -1,60 +0,0 @@
import { Flex, Heading, Select } from "@radix-ui/themes";
import { useTranslations } from "next-intl";
import { FontNames, FontWeights, TextProp } from "./TextSetting";
export default function TextSetting({
text,
setText,
}: {
text: TextProp;
setText: (text: TextProp) => void;
}) {
const t = useTranslations("TextEditor");
return (
<Flex className="p-4 border rounded-lg " gap={"3"} wrap={"wrap"}>
<Heading size={"3"} className="font-medium text-lg" >{t("title")}</Heading>
<textarea
value={text.text}
onChange={e => setText({ ...text, text: e.target.value })}
className="w-full p-3 border rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
rows={4}
/>
<div className="space-y-1 w-full">
<label className="block text-sm text-muted-foreground">
{t("textColor")}
</label>
<input
type="color"
value={text.color}
onChange={e => setText({ ...text, color: e.target.value })}
className="w-full h-10 rounded-md cursor-pointer"
/>
</div>
<div className="space-y-1 w-1/3">
<label className="block text-sm text-muted-foreground">
{t("fontFamily")}
</label>
<Select.Root defaultValue={`${text.font}`} onValueChange={(e) => setText({ ...text, font: e })}>
<Select.Trigger />
<Select.Content>
{FontNames.map((name) => <Select.Item key={name} value={name}>{name}</Select.Item>)}
</Select.Content>
</Select.Root>
</div>
<div className="space-y-2 w-1/3">
<label className="block text-sm text-muted-foreground">
{t("fontWeight")}
</label>
<Select.Root defaultValue={`${text.weight}`} onValueChange={(e) => setText({ ...text, weight: e })}>
<Select.Trigger />
<Select.Content>
{FontWeights.map((name) => <Select.Item key={name} value={name}>{name}</Select.Item>)}
</Select.Content>
</Select.Root>
</div>
</Flex>
);
}

View File

@@ -1,14 +1,15 @@
'use client'
import { containsChinese, DefaultFontChinese, FontLang, Fonts, FontWeight, getFontWeight, getOnlineFontPath } from "@/lib/fonts";
import { Flex, Heading, Select, Tooltip, IconButton, Link, Box, Tabs, RadioGroup } from "@radix-ui/themes";
import { PlusIcon, CircleQuestionMarkIcon } from "lucide-react";
import { useLocale, useTranslations } from "next-intl";
import { useEffect, useRef, useState } from "react";
export const FontWeights = ["Regular", "Bold"];
export const FontNames = ["Gentilis", "Helvetiker", "Optimer", "Noto_Sans_SC_zh", "Alibaba_PuHuiTi_3.0_zh"];
export type ColorGradientDir = "l2r" | "t2b";
export type FontFrom = "online" | "upload";
export enum FontFrom {
online,
upload,
}
export class TextProp {
text: string
color: string | string[]
@@ -16,13 +17,13 @@ export class TextProp {
fontFrom: FontFrom
font: string
fontUrl: string
weight: string
weight: FontWeight
constructor(
text: string,
color: string,
fontFrom: FontFrom,
font: string,
weight: string) {
weight: FontWeight) {
this.text = text;
this.color = color;
@@ -35,35 +36,22 @@ export class TextProp {
static default(text: string): TextProp {
let font = FontNames[0];
let font = Fonts[0].name;
if (containsChinese(text)) {
font = "Alibaba_PuHuiTi_3.0_zh";
font = DefaultFontChinese;
}
return {
text,
color: "#8e86fe",
colorGradientDir: "l2r",
font,
fontUrl: getOnlineFontPath(font, FontWeights[0]),
weight: FontWeights[0],
fontFrom: "online",
fontUrl: getOnlineFontPath(font, FontWeight.Regular),
weight: FontWeight.Regular,
fontFrom: FontFrom.online,
}
}
}
function getOnlineFontPath(fontName: string, fontWeight: String) {
let font = fontName;
if (fontName.endsWith("zh")) {
font = fontName.slice(0, -3);
}
return `https://fast3dtest.mysoul.fun/${font}_${fontWeight}.json`;
}
function containsChinese(str: string) {
return /[\u4e00-\u9fa5]/.test(str);
}
export interface UploadFont {
name: string;
@@ -72,6 +60,17 @@ export interface UploadFont {
type TextMode = "color" | "gradient";
const getFontWeightEnabled = (font: string) => {
let f = Fonts.find(item => item.name == font);
const map = new Map<string, boolean>()
if (f) {
f.weight.forEach(w => map.set(w, true))
}
return map;
};
export default function TextSetting({
text,
setText,
@@ -81,15 +80,16 @@ export default function TextSetting({
}) {
const locale = useLocale();
const t = useTranslations("TextEditor");
const t = useTranslations("TextEditor");
const [uploadFonts, setUploadFonts] = useState<UploadFont[]>([]);
const isPureColor = !Array.isArray(text.color);
const [textColorMode, setTextColorMode] = useState<TextMode>(isPureColor ? "color" : "gradient");
const [textColor, setTextColor] = useState<string>(isPureColor ? text.color as string : "#000000");
const [textGradientColor, setTextGradientColor] = useState<string[]>(!isPureColor ? text.color as string[] : ["#ce6464", "#63635a"]);
const [colorGradientDir, setColorGradientDir] = useState<ColorGradientDir>(text.colorGradientDir as ColorGradientDir);
const [fontWeightEnbled, setFontWeightEnabled] = useState<Map<string, boolean>>(getFontWeightEnabled(text.font));
let inited = useRef(false);
useEffect(() => {
@@ -102,7 +102,7 @@ export default function TextSetting({
if (uploadFonts.length > 0) {
handleSelectFont(uploadFonts[uploadFonts.length - 1].name)
} else {
handleSelectFont(FontNames[0])
handleSelectFont(Fonts[0].name);
}
}, [uploadFonts]);
@@ -150,14 +150,25 @@ export default function TextSetting({
}, [colorGradientDir]);
const handleSelectFont = (font: string) => {
if (FontNames.indexOf(font) !== -1) {
setText({ ...text, font: font, fontFrom: "online", fontUrl: getOnlineFontPath(font, text.weight) });
const f = Fonts.find(item => item.name == font);
if (f) {
if (f.weight.includes(text.weight)) {
setText({ ...text, font: font, fontFrom: FontFrom.online, fontUrl: getOnlineFontPath(font, text.weight) });
} else {
const w = f.weight[0];
setText({ ...text, font: font, fontFrom: FontFrom.online, fontUrl: getOnlineFontPath(font, w), weight: w });
}
} else {
let f = uploadFonts.find((item) => item.name === font)!;
setText({ ...text, font: font, fontFrom: "upload", fontUrl: f.url });
setText({ ...text, font: font, fontFrom: FontFrom.upload, fontUrl: f.url });
}
const map = getFontWeightEnabled(font);
setFontWeightEnabled(map);
};
return (
<Flex className="p-4 border rounded-lg " gap={"3"} direction={"column"}>
<Heading as="h2" size="4" className="font-medium text-lg" >{t("title")}</Heading>
@@ -271,7 +282,7 @@ export default function TextSetting({
<Select.Group>
<Select.Label>Online</Select.Label>
{FontNames.map((name) => <Select.Item key={name} value={name}>{name}</Select.Item>)}
{Fonts.map(({ name }) => <Select.Item key={name} value={name}>{name}</Select.Item>)}
</Select.Group>
</Select.Content>
@@ -311,13 +322,13 @@ export default function TextSetting({
{t("fontWeight")}
</Heading>
<Select.Root defaultValue={`${text.weight}`} onValueChange={(e) => setText({ ...text, weight: e })}>
<Select.Root value={text.weight} onValueChange={(e) => setText({ ...text, weight: getFontWeight(e) })}>
<Select.Trigger />
<Select.Content>
{FontWeights.map((name) => <Select.Item disabled={text.fontFrom == "upload"} key={name} value={name}>{name}</Select.Item>)}
{Object.entries(FontWeight).map(([name, value]) =>
<Select.Item disabled={!fontWeightEnbled.get(name)} key={name} value={name}>{value}</Select.Item>)}
</Select.Content>
</Select.Root>
</div>
</Flex>
);

View File

@@ -97,6 +97,8 @@ export function resize(
console.log("resize to width = " + width + " height = " + height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
// camera = new THREE.OrthographicCamera(
// clientWidth / -2,
// clientWidth / 2,
@@ -105,6 +107,7 @@ export function resize(
// 0.1,
// 1000
// );
renderer.setSize(width, height, false);
}

85
src/lib/fonts.ts Normal file
View File

@@ -0,0 +1,85 @@
export enum FontWeight {
Regular = "Regular",
Bold = "Bold",
}
export enum FontLang {
EN = "en",
ZH = "zh",
}
export function getFontWeight(s: string) {
switch (s) {
case "Regular":
return FontWeight.Regular;
case "Bold":
return FontWeight.Bold;
default:
return FontWeight.Regular;
}
}
export class FontDefine {
name: string;
weight: FontWeight[];
lang: FontLang[];
constructor(name: string, weight: FontWeight[], lang: FontLang[]) {
this.name = name;
this.weight = weight;
this.lang = lang;
}
}
export function getOnlineFontPath(fontName: string, w: FontWeight) {
return `https://fast3dtest.mysoul.fun/${fontName}_${w}.json`;
}
export function containsChinese(str: string) {
return /[\u4e00-\u9fa5]/.test(str);
}
export const DefaultFontChinese: string = "Alibaba_PuHuiTi_3.0";
export const Fonts: FontDefine[] = [
{
name: "Gentilis",
weight: [FontWeight.Regular, FontWeight.Bold],
lang: [FontLang.EN],
},
{
name: "Helvetiker",
weight: [FontWeight.Regular, FontWeight.Bold],
lang: [FontLang.EN],
},
{
name: "Optimer",
weight: [FontWeight.Regular, FontWeight.Bold],
lang: [FontLang.EN],
},
{
name: "Alibaba_PuHuiTi_3.0",
weight: [FontWeight.Regular, FontWeight.Bold],
lang: [FontLang.EN, FontLang.ZH],
},
{
name: "Noto_Sans_SC",
weight: [FontWeight.Regular, FontWeight.Bold],
lang: [FontLang.EN, FontLang.ZH],
},
{
name: "Barbie_Doll",
weight: [FontWeight.Regular],
lang: [FontLang.EN],
},
{
name: "Barbie_Princess",
weight: [FontWeight.Regular],
lang: [FontLang.EN],
},
{
name: "Bartex",
weight: [FontWeight.Regular],
lang: [FontLang.EN],
},
];