feat(website): implement cloud page and extract shared ReasonSection/FAQSection
3
apps/website/public/icons/ai-models/gemini.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M31.0589 13.0316H16.014V19.1831H24.6589C24.5199 20.0537 24.2078 20.9101 23.7509 21.691C23.2273 22.5858 22.5801 23.2669 21.9167 23.7857C19.9294 25.3395 17.6126 25.6572 16.0035 25.6572C11.9389 25.6572 8.46594 22.9766 7.12148 19.3341C7.06723 19.2019 7.0312 19.0654 6.98733 18.9304C6.69023 18.0034 6.5279 17.0215 6.5279 16.001C6.5279 14.939 6.70369 13.9223 7.0242 12.9621C8.28844 9.17522 11.8397 6.34675 16.0064 6.34675C16.8445 6.34675 17.6516 6.44854 18.417 6.65159C20.1661 7.11561 21.4034 8.0295 22.1615 8.75237L26.7361 4.18101C23.9534 1.57756 20.3259 3.9369e-09 15.9988 3.9369e-09C12.5396 -7.59723e-05 9.34593 1.09971 6.72881 2.95838C4.60641 4.46571 2.86573 6.48384 1.69099 8.82767C0.598311 11.0009 0 13.4092 0 15.9986C0 18.5881 0.599225 21.0215 1.69191 23.1746V23.1891C2.84605 25.4749 4.5338 27.4431 6.58508 28.9435C8.3771 30.2543 11.5904 32 15.9988 32C18.534 32 20.7809 31.5336 22.7625 30.6595C24.192 30.029 25.4585 29.2066 26.6052 28.1496C28.1203 26.753 29.3069 25.0255 30.1168 23.038C30.9268 21.0505 31.36 18.8029 31.36 16.3662C31.36 15.2314 31.2483 14.0791 31.0589 13.0316Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
4
apps/website/public/icons/ai-models/grok.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="31" viewBox="0 0 32 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.3587 19.8988L22.9972 11.9646C23.5187 11.5757 24.2642 11.7274 24.5127 12.3316C25.8207 15.5179 25.2363 19.3471 22.634 21.9762C20.0318 24.6052 16.411 25.1818 13.1015 23.8686L9.48617 25.5598C14.6716 29.1406 20.9684 28.255 24.9032 24.277C28.0243 21.1237 28.9909 16.8255 28.0871 12.9496L28.0952 12.9578C26.7845 7.26377 28.4175 4.98781 31.7625 0.333808C31.8416 0.223459 31.9208 0.113108 32 0L27.5982 4.44709V4.4333L12.356 19.9016" fill="white"/>
|
||||
<path d="M10.1634 21.8272C6.44151 18.2353 7.0832 12.6764 10.2589 9.47079C12.6072 7.09824 16.4546 6.12993 19.8133 7.55344L23.4204 5.87061C22.7706 5.3961 21.9377 4.88574 20.982 4.52709C16.6622 2.73116 11.4904 3.62499 7.97884 7.16997C4.60108 10.5825 3.53887 15.8297 5.36292 20.3071C6.72549 23.6535 4.49185 26.0204 2.24183 28.4096C1.44449 29.2564 0.644421 30.1034 0 31L10.1606 21.8299" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 952 B |
5
apps/website/public/icons/ai-models/ltx.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="38" height="16" viewBox="0 0 38 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M25.6714 2.88536C26.1493 2.82951 26.7876 2.87631 27.2746 2.87552C29.4061 2.87217 29.7407 4.85107 31.0086 6.17285C31.3057 5.47951 31.7391 4.76441 32.1866 4.149C32.6673 3.48769 33.0911 3.10066 33.953 2.95519C34.7328 2.82353 36.8597 2.74599 37.4893 3.17876C37.6223 3.27022 37.7198 3.40009 37.7342 3.55864C37.7879 4.1504 34.524 8.38331 33.8977 9.14783C34.6224 10.5615 37.9453 14.0003 37.9993 15.2938C38.0058 15.4508 37.9689 15.5748 37.8512 15.6902C37.4841 16.0502 36.5552 15.9679 36.0583 15.9783C35.4029 15.9928 34.8918 16.0117 34.2446 15.9184C32.7906 15.7089 32.4029 14.8649 31.7106 13.8079C31.4542 13.4166 31.1547 13.018 30.868 12.641C29.8939 13.549 29.4433 14.7027 28.431 15.5215C27.7186 16.0978 24.0748 16.1659 23.6489 15.5402C23.3554 14.4588 26.2687 11.0941 26.96 10.1276C27.1912 9.80439 27.3923 9.66031 27.5531 9.23699L27.5618 9.21374C27.0682 8.21459 23.6269 4.06408 23.8836 3.31986C24.2686 2.90918 25.1029 2.92321 25.6714 2.88536Z" fill="white"/>
|
||||
<path d="M13.5798 0.0217006C14.0245 0.0102399 17.731 -0.0733401 17.8671 0.188355C18.0783 0.594232 18.0745 2.39664 17.9571 2.91885C18.7829 2.90052 20.9815 2.73358 21.576 3.24255C21.9254 3.54165 21.8894 6.31329 21.4914 6.7223C20.9809 6.93681 18.6254 6.86654 17.9992 6.8141C18.2005 8.24188 17.6814 9.92179 18.1662 11.2688C18.2188 11.4149 18.4418 11.6515 18.5582 11.7758L18.5851 11.7807C19.4248 11.9253 20.4783 11.7921 21.4193 11.8541C21.5894 11.9423 21.7529 12.0279 21.8509 12.193C22.2597 12.8822 22.1381 14.3802 21.9594 15.1114C21.9191 15.307 21.8419 15.6435 21.6653 15.748C20.9367 16.1792 19.0596 15.9477 18.2447 15.8484C16.5346 15.6399 15.0424 15.373 13.9488 14.018C12.762 12.5472 12.9954 10.9199 12.9709 9.17573C12.9618 8.52672 12.9588 7.41995 13.0502 6.80867C12.4361 6.84166 10.8569 7.01497 10.4917 6.5846C10.1043 6.12824 10.3072 3.9303 10.3518 3.3296C10.9517 2.70004 12.1816 2.90197 13.0395 2.9117C13.0534 2.19219 12.7947 0.87298 13.1333 0.195009C13.203 0.055524 13.4145 0.0516102 13.5798 0.0217006Z" fill="white"/>
|
||||
<path d="M1.66628 2.88823C2.58555 2.81214 4.10841 2.81874 4.945 3.20141C5.33248 5.15667 4.99029 7.60389 5.06565 9.60325C5.07579 9.8735 4.9874 11.6351 5.25399 11.7357C6.28618 12.1249 11.1291 11.506 11.8399 12.0455C12.163 12.56 12.0235 15.1134 11.8775 15.7598C11.3062 15.9103 11.0189 15.8876 10.4264 15.8899L5.78094 15.8919C3.59892 15.9082 2.36972 16.0953 0.208639 15.5943C0.116058 15.3247 0.0349184 14.7783 0.0303189 14.4959C-0.0272935 10.99 0.0150458 7.4781 0.0142792 3.97146C0.0140433 2.80644 0.604555 2.93016 1.66628 2.88823Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
4
apps/website/public/icons/ai-models/qwen.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.1935 32L24.9858 15.5745L27.22 19.5198H31.4952L27.22 12.0998L29.4765 8.3063H12.2615L14.5178 4.36093L12.2615 0.332235L7.41455 8.3063H2.39172L11.6896 23.9422H7.41455L4.85911 27.9711H13.194L15.6878 32L15.6431 31.9985C15.4212 31.9838 15.2231 31.8597 15.1159 31.668L13.1938 28.3031H4.85911C4.6194 28.3031 4.40156 28.1756 4.2872 27.9711L2.03092 24.1776C1.85061 23.8609 1.8506 23.4694 2.03092 23.1527L3.95303 19.7881L0.135229 12.9026C-0.0450889 12.586 -0.0450637 12.1944 0.135229 11.8778L2.39151 8.08424C2.50586 7.87972 2.72371 7.75222 2.96341 7.75222H7.41455L11.6896 0.332235C11.8039 0.127714 12.0218 0 12.2615 0H16.7124C16.9521 0 17.17 0.127714 17.2843 0.332235L19.2064 3.69689H27.5411C27.7808 3.69689 27.9986 3.82439 28.113 4.02891L30.3693 7.82245C30.5496 8.13912 30.5496 8.53061 30.3693 8.84729L28.113 12.6408L31.6316 19.0976C31.8119 19.4143 31.8119 19.8058 31.6316 20.1224L29.3751 23.916C29.2608 24.1204 29.0431 24.248 28.8034 24.248H24.9856L20.7106 31.668C20.5962 31.8725 20.3784 32 20.1387 32H16.1935Z" fill="white"/>
|
||||
<path d="M22.0609 13.703H10.6869L16.1935 23.001L22.0609 13.703Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
4
apps/website/public/icons/ai-models/wan.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.1935 32L24.9858 15.5745L27.22 19.5198H31.4952L27.22 12.0998L29.4765 8.3063H12.2615L14.5178 4.36093L12.2615 0.332235L7.41455 8.3063H2.39172L11.6896 23.9422H7.41455L4.85911 27.9711H13.194L15.6878 32L15.6431 31.9985C15.4212 31.9838 15.2231 31.8597 15.1159 31.668L13.1938 28.3031H4.85911C4.6194 28.3031 4.40156 28.1756 4.2872 27.9711L2.03092 24.1776C1.85061 23.8609 1.8506 23.4694 2.03092 23.1527L3.95303 19.7881L0.135229 12.9026C-0.0450889 12.586 -0.0450637 12.1944 0.135229 11.8778L2.39151 8.08424C2.50586 7.87972 2.72371 7.75222 2.96341 7.75222H7.41455L11.6896 0.332235C11.8039 0.127714 12.0218 0 12.2615 0H16.7124C16.9521 0 17.17 0.127714 17.2843 0.332235L19.2064 3.69689H27.5411C27.7808 3.69689 27.9986 3.82439 28.113 4.02891L30.3693 7.82245C30.5496 8.13912 30.5496 8.53061 30.3693 8.84729L28.113 12.6408L31.6316 19.0976C31.8119 19.4143 31.8119 19.8058 31.6316 20.1224L29.3751 23.916C29.2608 24.1204 29.0431 24.248 28.8034 24.248H24.9856L20.7106 31.668C20.5962 31.8725 20.3784 32 20.1387 32H16.1935Z" fill="white"/>
|
||||
<path d="M22.0609 13.703H10.6869L16.1935 23.001L22.0609 13.703Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
BIN
apps/website/public/images/cloud/ai-models/grok-imagine.webp
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
apps/website/public/images/cloud/ai-models/ltx-23.webp
Normal file
|
After Width: | Height: | Size: 640 KiB |
BIN
apps/website/public/images/cloud/ai-models/nano-banana-pro.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
apps/website/public/images/cloud/ai-models/qwen-image-edit.webp
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
apps/website/public/images/cloud/ai-models/wan-22.webp
Normal file
|
After Width: | Height: | Size: 978 KiB |
110
apps/website/public/images/cloud/hero-cube.svg
Normal file
@@ -0,0 +1,110 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 900" fill="none">
|
||||
<!-- Background geometric lines -->
|
||||
<g stroke="#49378B" stroke-width="1.5" fill="none" opacity="0.4">
|
||||
<!-- Outer hexagonal frame layers -->
|
||||
<path d="M400 80 L600 200 L600 440 L400 560 L200 440 L200 200 Z" />
|
||||
<path d="M400 120 L570 220 L570 420 L400 520 L230 420 L230 220 Z" />
|
||||
<!-- Connector lines going up -->
|
||||
<line x1="300" y1="160" x2="300" y2="60" />
|
||||
<line x1="400" y1="120" x2="400" y2="20" />
|
||||
<line x1="500" y1="160" x2="500" y2="60" />
|
||||
<!-- Bottom platform layers -->
|
||||
<path d="M250 520 L550 520 L600 560 L600 600 L400 700 L200 600 L200 560 Z" opacity="0.3" />
|
||||
<path d="M280 620 L520 620 L560 650 L560 680 L400 760 L240 680 L240 650 Z" opacity="0.2" />
|
||||
<path d="M320 700 L480 700 L510 720 L510 740 L400 800 L290 740 L290 720 Z" opacity="0.15" />
|
||||
</g>
|
||||
|
||||
<!-- 3D Isometric cube cluster -->
|
||||
<g transform="translate(400, 380)">
|
||||
<!-- Back layer cubes (purple/dark) -->
|
||||
<!-- Top back -->
|
||||
<g transform="translate(0, -100)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#49378B" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#5a45a0" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#3d2d75" />
|
||||
</g>
|
||||
|
||||
<!-- Middle row - left back -->
|
||||
<g transform="translate(-70, -55)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#49378B" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#5a45a0" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#3d2d75" />
|
||||
</g>
|
||||
<!-- Middle row - right back -->
|
||||
<g transform="translate(70, -55)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#49378B" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#5a45a0" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#3d2d75" />
|
||||
</g>
|
||||
|
||||
<!-- Yellow accent cubes - front facing -->
|
||||
<!-- Top -->
|
||||
<g transform="translate(0, -65)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#f2ff59" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#f2ff59" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#d4e04e" />
|
||||
<polygon points="0,-5 -35,-20 -35,20 0,40" fill="#e0ec50" />
|
||||
</g>
|
||||
|
||||
<!-- Middle left yellow -->
|
||||
<g transform="translate(-70, -20)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#f2ff59" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#f2ff59" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#d4e04e" />
|
||||
<polygon points="0,-5 -35,-20 -35,20 0,40" fill="#e0ec50" />
|
||||
</g>
|
||||
<!-- Middle right yellow -->
|
||||
<g transform="translate(70, -20)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#f2ff59" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#f2ff59" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#d4e04e" />
|
||||
<polygon points="0,-5 -35,-20 -35,20 0,40" fill="#e0ec50" />
|
||||
</g>
|
||||
|
||||
<!-- Center purple -->
|
||||
<g transform="translate(0, -20)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#49378B" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#5a45a0" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#3d2d75" />
|
||||
</g>
|
||||
|
||||
<!-- Bottom row -->
|
||||
<g transform="translate(-70, 25)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#49378B" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#5a45a0" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#3d2d75" />
|
||||
</g>
|
||||
<g transform="translate(70, 25)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#49378B" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#5a45a0" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#3d2d75" />
|
||||
</g>
|
||||
|
||||
<!-- Front bottom yellow -->
|
||||
<g transform="translate(0, 25)">
|
||||
<polygon points="0,-40 35,-20 35,20 0,40 -35,20 -35,-20" fill="#f2ff59" />
|
||||
<polygon points="0,-40 35,-20 0,-5 -35,-20" fill="#f2ff59" />
|
||||
<polygon points="0,-5 35,-20 35,20 0,40" fill="#d4e04e" />
|
||||
<polygon points="0,-5 -35,-20 -35,20 0,40" fill="#e0ec50" />
|
||||
</g>
|
||||
|
||||
<!-- Outer corner yellow accents -->
|
||||
<g transform="translate(-105, 5)">
|
||||
<polygon points="0,-25 20,-12 20,12 0,25 -20,12 -20,-12" fill="#f2ff59" />
|
||||
<polygon points="0,-25 20,-12 0,-2 -20,-12" fill="#f2ff59" />
|
||||
<polygon points="0,-2 20,-12 20,12 0,25" fill="#d4e04e" />
|
||||
</g>
|
||||
<g transform="translate(105, 5)">
|
||||
<polygon points="0,-25 20,-12 20,12 0,25 -20,12 -20,-12" fill="#f2ff59" />
|
||||
<polygon points="0,-25 20,-12 0,-2 -20,-12" fill="#f2ff59" />
|
||||
<polygon points="0,-2 -20,-12 -20,12 0,25" fill="#e0ec50" />
|
||||
</g>
|
||||
<g transform="translate(0, -135)">
|
||||
<polygon points="0,-25 20,-12 20,12 0,25 -20,12 -20,-12" fill="#f2ff59" />
|
||||
<polygon points="0,-25 20,-12 0,-2 -20,-12" fill="#f2ff59" />
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Bottom arrow/chevron shape -->
|
||||
<path d="M340 780 L400 820 L460 780 L460 850 L400 890 L340 850 Z" fill="#211927" stroke="#49378B" stroke-width="1" opacity="0.5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -12,24 +12,9 @@ const meta: Meta<typeof FAQSection> = {
|
||||
})
|
||||
],
|
||||
args: {
|
||||
heading: 'FAQ',
|
||||
items: [
|
||||
{
|
||||
question: 'What hardware do I need to run ComfyUI?',
|
||||
answer:
|
||||
'A dedicated GPU is strongly recommended. NVIDIA GPUs with at least 4GB VRAM work best, but AMD and Apple Silicon are also supported.'
|
||||
},
|
||||
{
|
||||
question: 'Is ComfyUI free?',
|
||||
answer:
|
||||
'Yes, ComfyUI is completely free and open source. You can run it on your own hardware at no cost.'
|
||||
},
|
||||
{
|
||||
question: 'Can I use ComfyUI commercially?',
|
||||
answer:
|
||||
'Yes. ComfyUI is released under the GPL license, so you are free to use it for commercial purposes.'
|
||||
}
|
||||
]
|
||||
headingKey: 'download.faq.heading',
|
||||
faqPrefix: 'download.faq',
|
||||
faqCount: 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,24 +23,10 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const SingleItem: Story = {
|
||||
args: {
|
||||
heading: 'Questions',
|
||||
items: [
|
||||
{
|
||||
question: 'How do I get started?',
|
||||
answer: 'Download ComfyUI and follow the setup guide.'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const ManyItems: Story = {
|
||||
args: {
|
||||
heading: 'Frequently Asked Questions',
|
||||
items: Array.from({ length: 8 }, (_, i) => ({
|
||||
question: `Question ${i + 1}: What about feature ${i + 1}?`,
|
||||
answer: `This is the detailed answer for question ${i + 1}. It explains everything you need to know about this particular topic.`
|
||||
}))
|
||||
headingKey: 'download.faq.heading',
|
||||
faqPrefix: 'download.faq',
|
||||
faqCount: 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,36 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
interface FAQItem {
|
||||
question: string
|
||||
answer: string
|
||||
}
|
||||
import type { Locale, TranslationKey } from '../../i18n/translations'
|
||||
|
||||
const { heading, items } = defineProps<{
|
||||
heading: string
|
||||
items: FAQItem[]
|
||||
import { t } from '../../i18n/translations'
|
||||
|
||||
const {
|
||||
locale = 'en',
|
||||
headingKey,
|
||||
faqPrefix,
|
||||
faqCount
|
||||
} = defineProps<{
|
||||
locale?: Locale
|
||||
headingKey: TranslationKey
|
||||
faqPrefix: string
|
||||
faqCount: number
|
||||
}>()
|
||||
|
||||
const expanded = reactive(items.map(() => true))
|
||||
const faqKeys: Array<{ q: TranslationKey; a: TranslationKey }> = Array.from(
|
||||
{ length: faqCount },
|
||||
(_, i) => ({
|
||||
q: `${faqPrefix}.${i + 1}.q` as TranslationKey,
|
||||
a: `${faqPrefix}.${i + 1}.a` as TranslationKey
|
||||
})
|
||||
)
|
||||
|
||||
const faqs = faqKeys.map(({ q, a }) => ({
|
||||
question: t(q, locale),
|
||||
answer: t(a, locale)
|
||||
}))
|
||||
|
||||
const expanded = reactive(faqs.map(() => true))
|
||||
|
||||
function toggle(index: number) {
|
||||
expanded[index] = !expanded[index]
|
||||
@@ -26,14 +45,14 @@ function toggle(index: number) {
|
||||
class="bg-primary-comfy-ink sticky top-20 z-10 w-full shrink-0 self-start py-4 md:top-28 md:w-80 md:py-0"
|
||||
>
|
||||
<h2 class="text-primary-comfy-canvas text-4xl font-light md:text-5xl">
|
||||
{{ heading }}
|
||||
{{ t(headingKey, locale) }}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- Right FAQ list -->
|
||||
<div class="flex-1">
|
||||
<div
|
||||
v-for="(faq, index) in items"
|
||||
v-for="(faq, index) in faqs"
|
||||
:key="index"
|
||||
class="border-primary-comfy-canvas/20 border-b"
|
||||
>
|
||||
@@ -67,7 +86,7 @@ function toggle(index: number) {
|
||||
role="region"
|
||||
class="pb-6"
|
||||
>
|
||||
<p class="text-primary-comfy-canvas/70 text-sm">
|
||||
<p class="text-primary-comfy-canvas/70 text-sm whitespace-pre-line">
|
||||
{{ faq.answer }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@ useFrameScrub(canvasRef, {
|
||||
|
||||
<template>
|
||||
<section
|
||||
class="flex flex-col items-center px-4 pt-36 pb-16 lg:px-20 lg:pt-50 lg:pb-8"
|
||||
class="flex flex-col items-center px-4 py-16 lg:px-20 lg:pt-18 lg:pb-8"
|
||||
>
|
||||
<h1
|
||||
class="text-primary-comfy-canvas text-center text-5xl font-light whitespace-pre-line lg:text-8xl"
|
||||
|
||||
151
apps/website/src/components/product/cloud/AIModelsSection.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import { externalLinks } from '../../../config/routes'
|
||||
import { t } from '../../../i18n/translations'
|
||||
import BrandButton from '../../common/BrandButton.vue'
|
||||
|
||||
type ModelCard = {
|
||||
titleKey:
|
||||
| 'cloud.aiModels.card.grokImagine'
|
||||
| 'cloud.aiModels.card.nanoBananaPro'
|
||||
| 'cloud.aiModels.card.ltx23'
|
||||
| 'cloud.aiModels.card.qwenImageEdit'
|
||||
| 'cloud.aiModels.card.wan22TextToVideo'
|
||||
imageSrc: string
|
||||
badgeIcon: string
|
||||
badgeClass: string
|
||||
layoutClass: string
|
||||
}
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
|
||||
const modelCards: ModelCard[] = [
|
||||
{
|
||||
titleKey: 'cloud.aiModels.card.grokImagine',
|
||||
imageSrc: '/images/cloud/ai-models/grok-imagine.webp',
|
||||
badgeIcon: '/icons/ai-models/grok.svg',
|
||||
badgeClass:
|
||||
'bg-white/20 text-white rounded-2xl backdrop-blur-sm group-hover:bg-primary-comfy-yellow group-hover:text-primary-comfy-ink',
|
||||
layoutClass: 'lg:col-span-6 lg:aspect-[16/7]'
|
||||
},
|
||||
{
|
||||
titleKey: 'cloud.aiModels.card.nanoBananaPro',
|
||||
imageSrc: '/images/cloud/ai-models/nano-banana-pro.webp',
|
||||
badgeIcon: '/icons/ai-models/gemini.svg',
|
||||
badgeClass:
|
||||
'bg-white/20 text-white rounded-full backdrop-blur-sm group-hover:bg-primary-comfy-yellow group-hover:text-primary-comfy-ink',
|
||||
layoutClass: 'lg:col-span-6 lg:aspect-[16/7]'
|
||||
},
|
||||
{
|
||||
titleKey: 'cloud.aiModels.card.ltx23',
|
||||
imageSrc: '/images/cloud/ai-models/ltx-23.webp',
|
||||
badgeIcon: '/icons/ai-models/ltx.svg',
|
||||
badgeClass:
|
||||
'bg-white/20 text-white rounded-full backdrop-blur-sm group-hover:bg-primary-comfy-yellow group-hover:text-primary-comfy-ink',
|
||||
layoutClass: 'lg:col-span-4 lg:aspect-[4/3]'
|
||||
},
|
||||
{
|
||||
titleKey: 'cloud.aiModels.card.qwenImageEdit',
|
||||
imageSrc: '/images/cloud/ai-models/qwen-image-edit.webp',
|
||||
badgeIcon: '/icons/ai-models/qwen.svg',
|
||||
badgeClass:
|
||||
'bg-white/20 text-white rounded-full backdrop-blur-sm group-hover:bg-primary-comfy-yellow group-hover:text-primary-comfy-ink',
|
||||
layoutClass: 'lg:col-span-4 lg:aspect-[4/3]'
|
||||
},
|
||||
{
|
||||
titleKey: 'cloud.aiModels.card.wan22TextToVideo',
|
||||
imageSrc: '/images/cloud/ai-models/wan-22.webp',
|
||||
badgeIcon: '/icons/ai-models/wan.svg',
|
||||
badgeClass:
|
||||
'bg-white/20 text-white rounded-full backdrop-blur-sm group-hover:bg-primary-comfy-yellow group-hover:text-primary-comfy-ink',
|
||||
layoutClass: 'lg:col-span-4 lg:aspect-[4/3]'
|
||||
}
|
||||
]
|
||||
|
||||
function getCardClass(layoutClass: string): string {
|
||||
return `${layoutClass} group relative h-72 overflow-hidden rounded-4xl bg-black/40 lg:h-auto cursor-pointer`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="bg-primary-comfy-ink px-4 py-24 lg:px-20 lg:py-40">
|
||||
<div class="mx-auto flex w-full max-w-7xl flex-col items-center">
|
||||
<p
|
||||
class="text-primary-comfy-yellow text-center text-sm font-bold tracking-widest uppercase"
|
||||
>
|
||||
{{ t('cloud.aiModels.label', locale) }}
|
||||
</p>
|
||||
|
||||
<h2
|
||||
class="text-primary-comfy-canvas lg:text-6.5xl mt-6 max-w-4xl text-center text-5xl/tight font-light lg:mt-8"
|
||||
>
|
||||
{{ t('cloud.aiModels.heading', locale) }}
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="text-primary-comfy-canvas/70 mt-6 max-w-xl text-center text-2xl/snug font-light lg:mt-8"
|
||||
>
|
||||
{{ t('cloud.aiModels.subtitle', locale) }}
|
||||
</p>
|
||||
|
||||
<div class="mt-12 w-full lg:mt-20">
|
||||
<div class="rounded-4xl border border-white/12 p-2 lg:p-1.5">
|
||||
<div class="grid grid-cols-1 gap-2 lg:grid-cols-12">
|
||||
<a
|
||||
v-for="card in modelCards"
|
||||
:key="card.titleKey"
|
||||
:class="getCardClass(card.layoutClass)"
|
||||
>
|
||||
<img
|
||||
:src="card.imageSrc"
|
||||
alt=""
|
||||
class="size-full object-cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="absolute inset-0 bg-linear-to-t from-black/50 via-transparent to-black/15"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="absolute top-5 right-5 flex h-12 min-w-12 items-center justify-center px-3 lg:top-6 lg:right-6"
|
||||
:class="card.badgeClass"
|
||||
>
|
||||
<span
|
||||
class="inline-block size-6 bg-current"
|
||||
:style="{
|
||||
maskImage: `url(${card.badgeIcon})`,
|
||||
maskSize: 'contain',
|
||||
maskRepeat: 'no-repeat',
|
||||
maskPosition: 'center'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p
|
||||
class="text-primary-warm-white absolute inset-x-6 bottom-6 text-5xl/tight font-light whitespace-pre-line lg:top-6 lg:right-auto lg:bottom-auto lg:text-4xl"
|
||||
>
|
||||
{{ t(card.titleKey, locale) }}
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BrandButton
|
||||
:href="externalLinks.workflows"
|
||||
:label="t('cloud.aiModels.ctaMobile', locale)"
|
||||
variant="outline"
|
||||
class-name="mt-8 w-full max-w-md text-center text-sm lg:hidden"
|
||||
/>
|
||||
<BrandButton
|
||||
:href="externalLinks.workflows"
|
||||
:label="t('cloud.aiModels.ctaDesktop', locale)"
|
||||
variant="outline"
|
||||
class-name="mt-8 hidden text-sm lg:inline-flex"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import { t } from '../../../i18n/translations'
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
|
||||
const cards = [
|
||||
{
|
||||
labelKey: 'cloud.audience.creators.label' as const,
|
||||
titleKey: 'cloud.audience.creators.title' as const,
|
||||
descriptionKey: 'cloud.audience.creators.description' as const,
|
||||
placeholderClass: 'bg-linear-to-b from-emerald-300 to-amber-400'
|
||||
},
|
||||
{
|
||||
labelKey: 'cloud.audience.teams.label' as const,
|
||||
titleKey: 'cloud.audience.teams.title' as const,
|
||||
descriptionKey: 'cloud.audience.teams.description' as const,
|
||||
placeholderClass:
|
||||
'bg-linear-to-br from-purple-900 via-pink-600 to-amber-500'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="px-4 py-24 lg:px-20 lg:py-40">
|
||||
<div class="mx-auto max-w-7xl">
|
||||
<h2
|
||||
class="text-primary-comfy-ink mx-auto max-w-3xl text-center text-4xl/tight font-light lg:text-6xl/tight"
|
||||
>
|
||||
{{ t('cloud.audience.heading', locale).split('{creators}')[0]
|
||||
}}<span class="text-primary-comfy-yellow">{{
|
||||
t('cloud.audience.headingHighlight', locale)
|
||||
}}</span
|
||||
>{{ t('cloud.audience.heading', locale).split('{creators}')[1] }}
|
||||
</h2>
|
||||
|
||||
<div class="mt-12 grid grid-cols-1 gap-6 lg:mt-20 lg:grid-cols-2">
|
||||
<div
|
||||
v-for="card in cards"
|
||||
:key="card.labelKey"
|
||||
class="bg-primary-comfy-ink overflow-hidden rounded-4xl"
|
||||
>
|
||||
<div
|
||||
class="m-3 aspect-4/3 rounded-3xl"
|
||||
:class="card.placeholderClass"
|
||||
/>
|
||||
|
||||
<div class="p-8 lg:p-10">
|
||||
<p
|
||||
class="text-primary-comfy-yellow text-sm font-bold tracking-widest uppercase"
|
||||
>
|
||||
{{ t(card.labelKey, locale) }}
|
||||
</p>
|
||||
|
||||
<h3
|
||||
class="text-primary-comfy-canvas mt-4 text-3xl/tight font-light whitespace-pre-line lg:text-4xl/tight"
|
||||
>
|
||||
{{ t(card.titleKey, locale) }}
|
||||
</h3>
|
||||
|
||||
<p class="text-primary-comfy-canvas/70 mt-4 text-sm">
|
||||
{{ t(card.descriptionKey, locale) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
16
apps/website/src/components/product/cloud/FAQSection.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import FAQSection from '../../common/FAQSection.vue'
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FAQSection
|
||||
:locale="locale"
|
||||
heading-key="cloud.faq.heading"
|
||||
faq-prefix="cloud.faq"
|
||||
:faq-count="15"
|
||||
/>
|
||||
</template>
|
||||
41
apps/website/src/components/product/cloud/HeroSection.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import { t } from '../../../i18n/translations'
|
||||
import BrandButton from '../../common/BrandButton.vue'
|
||||
import ProductHeroBadge from '../../common/ProductHeroBadge.vue'
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="flex flex-col items-center px-4 pt-24 pb-0 lg:px-20 lg:pt-40">
|
||||
<ProductHeroBadge text="CLOUD" />
|
||||
|
||||
<h1
|
||||
class="text-primary-comfy-canvas lg:text-6.5xl mt-6 text-center text-4xl font-light whitespace-pre-line lg:mt-8"
|
||||
>
|
||||
{{ t('cloud.hero.heading', locale) }}
|
||||
</h1>
|
||||
|
||||
<p
|
||||
class="mt-6 max-w-md text-center text-sm/relaxed text-white/60 lg:mt-8 lg:max-w-lg lg:text-base/relaxed"
|
||||
>
|
||||
{{ t('cloud.hero.subtitle', locale) }}
|
||||
</p>
|
||||
|
||||
<BrandButton
|
||||
href="https://app.comfy.org"
|
||||
:label="t('cloud.hero.cta', locale)"
|
||||
class="mt-8 w-full text-center lg:mt-10 lg:w-auto"
|
||||
/>
|
||||
|
||||
<div class="mt-12 w-full max-w-2xl lg:mt-16">
|
||||
<img
|
||||
src="/images/cloud/hero-cube.svg"
|
||||
alt="ComfyUI Cloud — 3D node cube illustration"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
41
apps/website/src/components/product/cloud/PricingSection.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import { getRoutes } from '../../../config/routes'
|
||||
import { t } from '../../../i18n/translations'
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="px-4 pb-24 lg:px-20 lg:pb-40">
|
||||
<div class="mx-auto max-w-7xl">
|
||||
<div
|
||||
class="bg-primary-comfy-yellow flex flex-col gap-8 rounded-4xl p-8 lg:flex-row lg:items-center lg:justify-between lg:p-12"
|
||||
>
|
||||
<div class="max-w-2xl">
|
||||
<h2
|
||||
class="text-primary-comfy-ink text-3xl/tight font-semibold lg:text-4xl/tight"
|
||||
>
|
||||
{{ t('cloud.pricing.title', locale) }}
|
||||
</h2>
|
||||
|
||||
<p class="text-primary-comfy-ink/70 mt-4 text-sm">
|
||||
{{ t('cloud.pricing.description', locale) }}
|
||||
</p>
|
||||
|
||||
<p class="text-primary-comfy-ink mt-4 text-sm font-semibold">
|
||||
{{ t('cloud.pricing.tagline', locale) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a
|
||||
:href="getRoutes(locale).cloudPricing"
|
||||
class="bg-primary-comfy-ink text-primary-comfy-yellow shrink-0 rounded-2xl px-6 py-3 text-center text-sm font-semibold transition-opacity hover:opacity-90"
|
||||
>
|
||||
{{ t('cloud.pricing.cta', locale) }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import { getRoutes } from '../../../config/routes'
|
||||
import { t } from '../../../i18n/translations'
|
||||
import ProductCard from '../../common/ProductCard.vue'
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
const routes = getRoutes(locale)
|
||||
|
||||
const cards = [
|
||||
{
|
||||
title: t('products.local.title', locale),
|
||||
description: t('products.local.description', locale),
|
||||
cta: t('products.local.cta', locale),
|
||||
href: routes.download,
|
||||
bg: 'bg-primary-warm-gray'
|
||||
},
|
||||
{
|
||||
title: t('products.api.title', locale),
|
||||
description: t('products.api.description', locale),
|
||||
cta: t('products.api.cta', locale),
|
||||
href: routes.api,
|
||||
bg: 'bg-primary-comfy-plum'
|
||||
},
|
||||
{
|
||||
title: t('products.enterprise.title', locale),
|
||||
description: t('products.enterprise.description', locale),
|
||||
cta: t('products.enterprise.cta', locale),
|
||||
href: routes.cloudEnterprise,
|
||||
bg: 'bg-illustration-forest'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="bg-primary-comfy-ink px-4 py-24 md:px-20 md:py-40">
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<p
|
||||
class="text-primary-comfy-yellow text-xs font-bold tracking-widest uppercase"
|
||||
>
|
||||
{{ t('products.label', locale) }}
|
||||
</p>
|
||||
<h2
|
||||
class="text-primary-comfy-canvas mt-4 text-4xl font-light whitespace-pre-line md:text-5xl"
|
||||
>
|
||||
{{ t('products.heading', locale) }}
|
||||
</h2>
|
||||
<p class="text-primary-comfy-canvas/70 mt-4 text-sm">
|
||||
{{ t('products.subheading', locale) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-16 grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
<ProductCard v-for="card in cards" :key="card.title" v-bind="card" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
54
apps/website/src/components/product/cloud/ReasonSection.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import type { Reason } from '../shared/ReasonSection.vue'
|
||||
|
||||
import ReasonSection from '../shared/ReasonSection.vue'
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
|
||||
interface CloudReason extends Reason {
|
||||
badge?: boolean
|
||||
}
|
||||
|
||||
const reasons: CloudReason[] = [
|
||||
{
|
||||
titleKey: 'cloud.reason.1.title',
|
||||
descriptionKey: 'cloud.reason.1.description'
|
||||
},
|
||||
{
|
||||
titleKey: 'cloud.reason.2.title',
|
||||
descriptionKey: 'cloud.reason.2.description',
|
||||
badge: true
|
||||
},
|
||||
{
|
||||
titleKey: 'cloud.reason.3.title',
|
||||
descriptionKey: 'cloud.reason.3.description'
|
||||
},
|
||||
{
|
||||
titleKey: 'cloud.reason.4.title',
|
||||
descriptionKey: 'cloud.reason.4.description'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ReasonSection
|
||||
:locale="locale"
|
||||
heading-key="cloud.reason.heading"
|
||||
heading-highlight-key="cloud.reason.headingHighlight"
|
||||
:reasons="reasons"
|
||||
>
|
||||
<template #reason-extra="{ reason }">
|
||||
<div v-if="(reason as CloudReason).badge" class="mt-3">
|
||||
<span
|
||||
class="font-formula-condensed text-primary-comfy-yellow text-lg font-bold tracking-wide"
|
||||
>
|
||||
ONLY ON
|
||||
<img src="/icons/logo.svg" alt="Comfy" class="inline-block h-5" />
|
||||
CLOUD
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</ReasonSection>
|
||||
</template>
|
||||
@@ -8,9 +8,7 @@ const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
class="bg-transparency-white-t4 px-4 pt-28 pb-4 text-center lg:px-20 lg:pt-36 lg:pb-8"
|
||||
>
|
||||
<section class="bg-transparency-white-t4 p-4 text-center lg:px-20 lg:py-8">
|
||||
<p
|
||||
class="text-primary-comfy-canvas text-lg font-semibold lg:text-sm lg:font-normal"
|
||||
>
|
||||
|
||||
@@ -1,28 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale, TranslationKey } from '../../../i18n/translations'
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import { t } from '../../../i18n/translations'
|
||||
import SharedFAQSection from '../../common/FAQSection.vue'
|
||||
import FAQSection from '../../common/FAQSection.vue'
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
|
||||
const faqKeys: Array<{ q: TranslationKey; a: TranslationKey }> = Array.from(
|
||||
{ length: 8 },
|
||||
(_, i) => ({
|
||||
q: `download.faq.${i + 1}.q` as TranslationKey,
|
||||
a: `download.faq.${i + 1}.a` as TranslationKey
|
||||
})
|
||||
)
|
||||
|
||||
const items = faqKeys.map(({ q, a }) => ({
|
||||
question: t(q, locale),
|
||||
answer: t(a, locale)
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SharedFAQSection
|
||||
:heading="t('download.faq.heading', locale)"
|
||||
:items="items"
|
||||
<FAQSection
|
||||
:locale="locale"
|
||||
heading-key="download.faq.heading"
|
||||
faq-prefix="download.faq"
|
||||
:faq-count="8"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import type { Locale } from '../../../i18n/translations'
|
||||
|
||||
import { t } from '../../../i18n/translations'
|
||||
import SharedReasonSection from '../shared/ReasonSection.vue'
|
||||
import type { Reason } from '../shared/ReasonSection.vue'
|
||||
|
||||
import ReasonSection from '../shared/ReasonSection.vue'
|
||||
|
||||
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
|
||||
|
||||
const reasons = [
|
||||
const reasons: Reason[] = [
|
||||
{
|
||||
title: t('download.reason.1.title', locale),
|
||||
description: t('download.reason.1.description', locale)
|
||||
titleKey: 'download.reason.1.title',
|
||||
descriptionKey: 'download.reason.1.description'
|
||||
},
|
||||
{
|
||||
title: t('download.reason.2.title', locale),
|
||||
description: t('download.reason.2.description', locale)
|
||||
titleKey: 'download.reason.2.title',
|
||||
descriptionKey: 'download.reason.2.description'
|
||||
},
|
||||
{
|
||||
title: t('download.reason.3.title', locale),
|
||||
description: t('download.reason.3.description', locale)
|
||||
titleKey: 'download.reason.3.title',
|
||||
descriptionKey: 'download.reason.3.description'
|
||||
},
|
||||
{
|
||||
title: t('download.reason.4.title', locale),
|
||||
description: t('download.reason.4.description', locale)
|
||||
titleKey: 'download.reason.4.title',
|
||||
descriptionKey: 'download.reason.4.description'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SharedReasonSection
|
||||
:heading="t('download.reason.heading', locale)"
|
||||
:heading-highlight="t('download.reason.headingHighlight', locale)"
|
||||
<ReasonSection
|
||||
:locale="locale"
|
||||
heading-key="download.reason.heading"
|
||||
heading-highlight-key="download.reason.headingHighlight"
|
||||
highlight-class="text-primary-warm-white"
|
||||
:reasons="reasons"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,28 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import type { Reason } from './ReasonSection.vue'
|
||||
|
||||
import ReasonSection from './ReasonSection.vue'
|
||||
|
||||
const defaultReasons: Reason[] = [
|
||||
{
|
||||
titleKey: 'download.reason.1.title',
|
||||
descriptionKey: 'download.reason.1.description'
|
||||
},
|
||||
{
|
||||
titleKey: 'download.reason.2.title',
|
||||
descriptionKey: 'download.reason.2.description'
|
||||
},
|
||||
{
|
||||
titleKey: 'download.reason.3.title',
|
||||
descriptionKey: 'download.reason.3.description'
|
||||
},
|
||||
{
|
||||
titleKey: 'download.reason.4.title',
|
||||
descriptionKey: 'download.reason.4.description'
|
||||
}
|
||||
]
|
||||
|
||||
const meta: Meta<typeof ReasonSection> = {
|
||||
title: 'Website/Product/ReasonSection',
|
||||
component: ReasonSection,
|
||||
@@ -12,30 +33,10 @@ const meta: Meta<typeof ReasonSection> = {
|
||||
})
|
||||
],
|
||||
args: {
|
||||
heading: 'Why professionals\nchoose ',
|
||||
headingHighlight: 'Comfy Local',
|
||||
reasons: [
|
||||
{
|
||||
title: 'Unlimited\ncreative power',
|
||||
description:
|
||||
'Run any workflow without limits. No queues, no credits, no restrictions on what you can create.'
|
||||
},
|
||||
{
|
||||
title: 'Any model,\nany time',
|
||||
description:
|
||||
'Use any open-source model instantly. Switch between Stable Diffusion, Flux, and more with a single click.'
|
||||
},
|
||||
{
|
||||
title: 'Your machine,\nyour rules',
|
||||
description:
|
||||
'Your data never leaves your computer. Full privacy and complete control over your creative environment.'
|
||||
},
|
||||
{
|
||||
title: 'Free.\nOpen Source.',
|
||||
description:
|
||||
'No subscriptions, no hidden fees. ComfyUI is and always will be free and open source.'
|
||||
}
|
||||
]
|
||||
headingKey: 'download.reason.heading',
|
||||
headingHighlightKey: 'download.reason.headingHighlight',
|
||||
highlightClass: 'text-primary-warm-white',
|
||||
reasons: defaultReasons
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,19 +45,8 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {}
|
||||
|
||||
export const WithoutHighlight: Story = {
|
||||
export const FewReasons: Story = {
|
||||
args: {
|
||||
heading: 'Why choose Comfy',
|
||||
headingHighlight: '',
|
||||
reasons: [
|
||||
{
|
||||
title: 'Fast',
|
||||
description: 'Optimized for speed and efficiency.'
|
||||
},
|
||||
{
|
||||
title: 'Flexible',
|
||||
description: 'Adapt to any workflow with ease.'
|
||||
}
|
||||
]
|
||||
reasons: defaultReasons.slice(0, 2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
interface Reason {
|
||||
title: string
|
||||
description: string
|
||||
import type { Locale, TranslationKey } from '../../../i18n/translations'
|
||||
|
||||
import { t } from '../../../i18n/translations'
|
||||
|
||||
export interface Reason {
|
||||
titleKey: TranslationKey
|
||||
descriptionKey: TranslationKey
|
||||
}
|
||||
|
||||
const {
|
||||
heading,
|
||||
headingHighlight = '',
|
||||
locale = 'en',
|
||||
headingKey,
|
||||
headingHighlightKey,
|
||||
highlightClass = 'text-white',
|
||||
reasons
|
||||
} = defineProps<{
|
||||
heading: string
|
||||
headingHighlight?: string
|
||||
locale?: Locale
|
||||
headingKey: TranslationKey
|
||||
headingHighlightKey: TranslationKey
|
||||
highlightClass?: string
|
||||
reasons: Reason[]
|
||||
}>()
|
||||
</script>
|
||||
@@ -26,9 +34,9 @@ const {
|
||||
<h2
|
||||
class="text-primary-comfy-canvas text-4xl font-light whitespace-pre-line md:text-5xl"
|
||||
>
|
||||
{{ heading
|
||||
}}<span v-if="headingHighlight" class="text-primary-warm-white">{{
|
||||
headingHighlight
|
||||
{{ t(headingKey, locale)
|
||||
}}<span :class="highlightClass">{{
|
||||
t(headingHighlightKey, locale)
|
||||
}}</span>
|
||||
</h2>
|
||||
</div>
|
||||
@@ -37,16 +45,19 @@ const {
|
||||
<div class="flex-1">
|
||||
<div
|
||||
v-for="reason in reasons"
|
||||
:key="reason.title"
|
||||
:key="reason.titleKey"
|
||||
class="border-primary-comfy-canvas/20 flex flex-col gap-4 border-b py-10 first:pt-0 md:flex-row md:gap-12"
|
||||
>
|
||||
<h3
|
||||
class="text-primary-comfy-canvas shrink-0 text-2xl font-light whitespace-pre-line md:w-52"
|
||||
>
|
||||
{{ reason.title }}
|
||||
</h3>
|
||||
<div class="shrink-0 md:w-52">
|
||||
<h3
|
||||
class="text-primary-comfy-canvas text-2xl font-light whitespace-pre-line"
|
||||
>
|
||||
{{ t(reason.titleKey, locale) }}
|
||||
</h3>
|
||||
<slot name="reason-extra" :reason="reason" />
|
||||
</div>
|
||||
<p class="text-primary-comfy-canvas/70 flex-1 text-sm">
|
||||
{{ reason.description }}
|
||||
{{ t(reason.descriptionKey, locale) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -383,6 +383,303 @@ const translations = {
|
||||
en: 'Powerful GPUs, same workflow, same results, from anywhere.',
|
||||
'zh-CN': '强大 GPU,同样的工作流,同样的结果,随时随地。'
|
||||
},
|
||||
// Cloud – HeroSection
|
||||
'cloud.hero.heading': {
|
||||
en: 'The full power of\nComfyUI — from\nanywhere.',
|
||||
'zh-CN': 'ComfyUI 的全部能力\n随时随地。'
|
||||
},
|
||||
'cloud.hero.subtitle': {
|
||||
en: 'The easiest way to start with ComfyUI. Pre-loaded models. Pre-installed custom nodes. Concurrent jobs. The full power of ComfyUI on Blackwell RTX 6000 Pros. Open a tab and start creating.',
|
||||
'zh-CN':
|
||||
'最简单的 ComfyUI 入门方式。预加载模型。预安装自定义节点。并发任务。在 Blackwell RTX 6000 Pro 上体验 ComfyUI 的全部能力。打开标签页,开始创作。'
|
||||
},
|
||||
'cloud.hero.cta': {
|
||||
en: 'TRY COMFY CLOUD FOR FREE',
|
||||
'zh-CN': '免费试用 COMFY CLOUD'
|
||||
},
|
||||
|
||||
// Cloud – ReasonSection
|
||||
'cloud.reason.heading': {
|
||||
en: 'Why\nprofessionals\nchoose ',
|
||||
'zh-CN': '专业人士为何\n选择'
|
||||
},
|
||||
'cloud.reason.headingHighlight': {
|
||||
en: 'Cloud',
|
||||
'zh-CN': 'Cloud'
|
||||
},
|
||||
'cloud.reason.1.title': {
|
||||
en: 'Powerful GPUs with end-\nto-end security built-in',
|
||||
'zh-CN': '强大 GPU\n端到端安全内置'
|
||||
},
|
||||
'cloud.reason.1.description': {
|
||||
en: 'Comfy Cloud works on any device. Pay only for running workflows, not idle time. With Comfy Cloud, you get security and infrastructure built-in with access to the most popular custom nodes.',
|
||||
'zh-CN':
|
||||
'Comfy Cloud 可在任何设备上使用。只需为运行工作流付费,无需为闲置时间付费。使用 Comfy Cloud,您可获得内置的安全性和基础设施,并访问最流行的自定义节点。'
|
||||
},
|
||||
'cloud.reason.2.title': {
|
||||
en: 'All models. Commercial\nlicense guaranteed.',
|
||||
'zh-CN': '所有模型。\n商业许可保证。'
|
||||
},
|
||||
'cloud.reason.2.description': {
|
||||
en: 'Run open-source models like Wan 2.2, Flux, LTX and Qwen alongside partner models like Nano Banana, Seedance, Seedream, Grok, Kling, Hunyuan 3D and more. Every model on Comfy Cloud is cleared for commercial use. No license ambiguity. All through one credit balance.',
|
||||
'zh-CN':
|
||||
'运行 Wan 2.2、Flux、LTX 和 Qwen 等开源模型,以及 Nano Banana、Seedance、Seedream、Grok、Kling、Hunyuan 3D 等合作伙伴模型。Comfy Cloud 上的每个模型都已获得商业使用许可。无许可证歧义。通过统一的积分余额使用。'
|
||||
},
|
||||
'cloud.reason.3.title': {
|
||||
en: 'More control than any\nother visual AI tool',
|
||||
'zh-CN': '比任何其他\n视觉 AI 工具更强的控制力'
|
||||
},
|
||||
'cloud.reason.3.description': {
|
||||
en: 'Every node exposed. Every setting adjustable. ComfyUI gives you the full inference pipeline. Choose your sampler, your scheduler, your model chain. The cloud simplifies the setup and supercharges the hardware underneath it.',
|
||||
'zh-CN':
|
||||
'每个节点都可见。每个设置都可调。ComfyUI 为您提供完整的推理管线。选择您的采样器、调度器、模型链。云端简化了设置并增强了底层硬件。'
|
||||
},
|
||||
'cloud.reason.4.title': {
|
||||
en: 'Community workflows,\nunlimited customization\nthrough pre-installed\ncustom nodes',
|
||||
'zh-CN': '社区工作流,\n通过预安装自定义节点\n实现无限自定义'
|
||||
},
|
||||
'cloud.reason.4.description': {
|
||||
en: 'Browse, run, and remix workflows built by thousands of creators. Start from proven templates instead of blank canvases. Upload custom LoRAs or finetuned foundational models from CivitAI and Hugging Face. The nodes powering ~90% of local ComfyUI workflows are now in the cloud.',
|
||||
'zh-CN':
|
||||
'浏览、运行和混搭由数千名创作者构建的工作流。从经过验证的模板开始,而非空白画布。上传自定义 LoRA 或来自 CivitAI 和 Hugging Face 的微调基础模型。驱动约 90% 本地 ComfyUI 工作流的节点现已上云。'
|
||||
},
|
||||
|
||||
// Cloud – AIModelsSection
|
||||
'cloud.aiModels.label': {
|
||||
en: 'AI MODELS',
|
||||
'zh-CN': 'AI 模型'
|
||||
},
|
||||
'cloud.aiModels.heading': {
|
||||
en: 'Run the world’s\nleading AI models',
|
||||
'zh-CN': '运行全球领先的\nAI 模型'
|
||||
},
|
||||
'cloud.aiModels.subtitle': {
|
||||
en: 'New models are added as they launch.',
|
||||
'zh-CN': '新模型发布后会第一时间上线。'
|
||||
},
|
||||
'cloud.aiModels.card.grokImagine': {
|
||||
en: 'Grok Imagine',
|
||||
'zh-CN': 'Grok Imagine'
|
||||
},
|
||||
'cloud.aiModels.card.nanoBananaPro': {
|
||||
en: 'Nano Banana Pro',
|
||||
'zh-CN': 'Nano Banana Pro'
|
||||
},
|
||||
'cloud.aiModels.card.ltx23': {
|
||||
en: 'LTX 2.3',
|
||||
'zh-CN': 'LTX 2.3'
|
||||
},
|
||||
'cloud.aiModels.card.qwenImageEdit': {
|
||||
en: 'Advanced image\nediting with Qwen',
|
||||
'zh-CN': 'Qwen 高级\n图像编辑'
|
||||
},
|
||||
'cloud.aiModels.card.wan22TextToVideo': {
|
||||
en: 'Wan 2.2\ntext to video',
|
||||
'zh-CN': 'Wan 2.2\n文生视频'
|
||||
},
|
||||
'cloud.aiModels.ctaDesktop': {
|
||||
en: 'EXPLORE WORKFLOWS WITH THE LATEST MODELS',
|
||||
'zh-CN': '探索最新模型工作流'
|
||||
},
|
||||
'cloud.aiModels.ctaMobile': {
|
||||
en: 'EXPLORE WORKFLOWS',
|
||||
'zh-CN': '探索工作流'
|
||||
},
|
||||
|
||||
// Cloud – AudienceSection
|
||||
'cloud.audience.heading': {
|
||||
en: 'Built for {creators} who need quality, control, and simplicity.',
|
||||
'zh-CN': '为追求质量、控制与简约的{creators}而生。'
|
||||
},
|
||||
'cloud.audience.headingHighlight': {
|
||||
en: 'creators',
|
||||
'zh-CN': '创作者'
|
||||
},
|
||||
'cloud.audience.creators.label': {
|
||||
en: 'CREATORS',
|
||||
'zh-CN': '创作者'
|
||||
},
|
||||
'cloud.audience.creators.title': {
|
||||
en: 'From idea to output\nin minutes.',
|
||||
'zh-CN': '从创意到成品,\n只需几分钟。'
|
||||
},
|
||||
'cloud.audience.creators.description': {
|
||||
en: 'For those who want to generate images, video, and 3D with more control than prompt-based tools offer — without spending a weekend configuring your machine.',
|
||||
'zh-CN':
|
||||
'适合那些想要生成图像、视频和 3D 内容,且需要比基于提示词的工具更精细控制的人——无需花一整个周末配置机器。'
|
||||
},
|
||||
'cloud.audience.teams.label': {
|
||||
en: 'TEAMS & STUDIOS',
|
||||
'zh-CN': '团队与工作室'
|
||||
},
|
||||
'cloud.audience.teams.title': {
|
||||
en: 'Onboard your\nteam today.',
|
||||
'zh-CN': '立即开始\n团队协作。'
|
||||
},
|
||||
'cloud.audience.teams.description': {
|
||||
en: 'No IT setup, no GPU procurement, no environment headaches. Everyone works from the same up-to-date platform. Share workflows via App Mode links — your team runs them instantly, no training required.',
|
||||
'zh-CN':
|
||||
'无需 IT 部署、GPU 采购,也没有环境配置的烦恼。所有人在同一个最新平台上工作。通过 App Mode 链接分享工作流——团队立即运行,无需培训。'
|
||||
},
|
||||
|
||||
// Cloud – PricingSection
|
||||
'cloud.pricing.title': {
|
||||
en: 'Simple, credit-based pricing',
|
||||
'zh-CN': '简单的按积分计费'
|
||||
},
|
||||
'cloud.pricing.description': {
|
||||
en: 'One balance for Cloud GPU time and Partner Node API models. Build and edit workflows for free — credits are consumed only when the GPU runs.',
|
||||
'zh-CN':
|
||||
'一个余额即可使用云端 GPU 算力和合作伙伴节点 API 模型。免费构建和编辑工作流——仅在 GPU 运行时消耗积分。'
|
||||
},
|
||||
'cloud.pricing.tagline': {
|
||||
en: "Start free. Upgrade when you're ready.",
|
||||
'zh-CN': '免费开始,随时升级。'
|
||||
},
|
||||
'cloud.pricing.cta': {
|
||||
en: 'SEE PRICING PLANS',
|
||||
'zh-CN': '查看定价方案'
|
||||
},
|
||||
|
||||
// Cloud – FAQSection
|
||||
'cloud.faq.heading': {
|
||||
en: "FAQ's",
|
||||
'zh-CN': '常见问题'
|
||||
},
|
||||
'cloud.faq.1.q': {
|
||||
en: 'What is Comfy Cloud / ComfyUI Cloud?',
|
||||
'zh-CN': '什么是 Comfy Cloud / ComfyUI Cloud?'
|
||||
},
|
||||
'cloud.faq.1.a': {
|
||||
en: 'Comfy Cloud is a version of ComfyUI that we officially host — no setup, no GPU required. Run your workflows instantly on high-performance cloud GPUs.',
|
||||
'zh-CN':
|
||||
'Comfy Cloud 是我们官方托管的 ComfyUI 版本——无需设置,无需 GPU。在高性能云端 GPU 上即时运行您的工作流。'
|
||||
},
|
||||
'cloud.faq.2.q': {
|
||||
en: 'How is Cloud different from running ComfyUI locally?',
|
||||
'zh-CN': 'Cloud 与本地运行 ComfyUI 有什么区别?'
|
||||
},
|
||||
'cloud.faq.2.a': {
|
||||
en: 'Cloud runs on powerful remote GPUs and is accessible from any device. Local runs entirely on your computer, giving you full control and offline use.',
|
||||
'zh-CN':
|
||||
'Cloud 在强大的远程 GPU 上运行,可从任何设备访问。本地版完全在您的电脑上运行,提供完全控制和离线使用。'
|
||||
},
|
||||
'cloud.faq.3.q': {
|
||||
en: 'Which version should I choose, Comfy Cloud or local ComfyUI (self-hosted)?',
|
||||
'zh-CN': '我应该选择 Comfy Cloud 还是本地 ComfyUI(自托管)?'
|
||||
},
|
||||
'cloud.faq.3.a': {
|
||||
en: "Comfy Cloud (beta) has zero setup, is easy to share with your team, and is faster than most GPUs you can run on a desktop workstation. You can immediately run the best models and workflows from the community on Comfy Cloud.\nLocal ComfyUI is infinitely customizable, works offline, and you don't need to worry about queue times. However, depending on what you want to create, you might need to have a good GPU and some amount of technical knowledge to install community-created custom nodes.",
|
||||
'zh-CN':
|
||||
'Comfy Cloud(测试版)无需任何设置,方便与团队共享,比大多数桌面工作站 GPU 更快。您可以立即在 Comfy Cloud 上运行社区中最好的模型和工作流。\n本地 ComfyUI 可以无限定制,支持离线工作,无需担心排队时间。但根据您的创作需求,可能需要一块好的 GPU 以及一定的技术知识来安装社区创建的自定义节点。'
|
||||
},
|
||||
'cloud.faq.4.q': {
|
||||
en: 'Do I need a GPU or a strong computer to use Comfy Cloud?',
|
||||
'zh-CN': '使用 Comfy Cloud 需要 GPU 或高性能电脑吗?'
|
||||
},
|
||||
'cloud.faq.4.a': {
|
||||
en: 'No, you can start creating instantly from your browser, no matter what computer you use.',
|
||||
'zh-CN': '不需要,无论使用什么电脑,您都可以从浏览器即时开始创作。'
|
||||
},
|
||||
'cloud.faq.5.q': {
|
||||
en: 'What machine or GPU does Comfy Cloud run on?',
|
||||
'zh-CN': 'Comfy Cloud 使用什么机器或 GPU?'
|
||||
},
|
||||
'cloud.faq.5.a': {
|
||||
en: 'Comfy Cloud runs on Blackwell RTX 6000 Pros — 96GB VRAM.',
|
||||
'zh-CN': 'Comfy Cloud 运行在 Blackwell RTX 6000 Pro 上——96GB 显存。'
|
||||
},
|
||||
'cloud.faq.6.q': {
|
||||
en: 'Can I use my existing workflows with Comfy Cloud?',
|
||||
'zh-CN': '我可以在 Comfy Cloud 上使用现有的工作流吗?'
|
||||
},
|
||||
'cloud.faq.6.a': {
|
||||
en: 'Yes, your workflows work across Local and Cloud. Just note that only the most popular custom nodes are supported for now, but more will be added soon.',
|
||||
'zh-CN':
|
||||
'可以,您的工作流在本地和云端都能使用。请注意,目前仅支持最热门的自定义节点,但很快会添加更多。'
|
||||
},
|
||||
'cloud.faq.7.q': {
|
||||
en: 'Are all ComfyUI extensions and custom nodes supported?',
|
||||
'zh-CN': '所有 ComfyUI 扩展和自定义节点都支持吗?'
|
||||
},
|
||||
'cloud.faq.7.a': {
|
||||
en: 'You can always check Cloud to see the list of extensions and models that we support, for free.\nMost popular ones are available, and new ones are added over time.',
|
||||
'zh-CN':
|
||||
'您可以随时在 Cloud 上免费查看我们支持的扩展和模型列表。\n大多数热门扩展已可用,新的扩展会持续添加。'
|
||||
},
|
||||
'cloud.faq.8.q': {
|
||||
en: 'Can I use my own models or checkpoints?',
|
||||
'zh-CN': '我可以使用自己的模型或检查点吗?'
|
||||
},
|
||||
'cloud.faq.8.a': {
|
||||
en: 'You can always check Cloud to see the list of extensions and models that we support, for free.\nCurrently, we support a wide variety of preinstalled models.\nFor those on the Creator or Pro plans, you can bring in your own fine-tuned LoRAs from CivitAI to perfect your own style.\nImporting from HuggingFace and direct file upload for larger models is on our roadmap.',
|
||||
'zh-CN':
|
||||
'您可以随时在 Cloud 上免费查看我们支持的扩展和模型列表。\n目前我们支持大量预装模型。\n对于 Creator 或 Pro 计划用户,您可以导入自己从 CivitAI 微调的 LoRA 来打造专属风格。\n从 HuggingFace 导入和大型模型的直接上传功能已在我们的路线图中。'
|
||||
},
|
||||
'cloud.faq.9.q': {
|
||||
en: 'Can I run long or multiple workflows?',
|
||||
'zh-CN': '我可以运行长时间或多个工作流吗?'
|
||||
},
|
||||
'cloud.faq.9.a': {
|
||||
en: "Each workflow can run for up to 30 minutes, with one active job at a time. We're adding higher tiers and parallel runs soon for even more flexibility.",
|
||||
'zh-CN':
|
||||
'每个工作流最长可运行 30 分钟,同时运行一个活跃任务。我们即将推出更高层级和并行运行,提供更大灵活性。'
|
||||
},
|
||||
'cloud.faq.10.q': {
|
||||
en: 'How is my user data stored and secured in Comfy Cloud?',
|
||||
'zh-CN': '我的用户数据在 Comfy Cloud 中如何存储和保护?'
|
||||
},
|
||||
'cloud.faq.10.a': {
|
||||
en: 'By default, all your inputs, outputs, and workflows are private to your account.\nFor enhanced security features or enterprise-level options, please contact our team via support@comfy.org for more details.',
|
||||
'zh-CN':
|
||||
'默认情况下,您的所有输入、输出和工作流都是您账户的私有数据。\n如需增强安全功能或企业级选项,请通过 support@comfy.org 联系我们的团队了解更多详情。'
|
||||
},
|
||||
'cloud.faq.11.q': {
|
||||
en: 'How does pricing for Comfy Cloud work?',
|
||||
'zh-CN': 'Comfy Cloud 的定价是怎样的?'
|
||||
},
|
||||
'cloud.faq.11.a': {
|
||||
en: 'Your monthly plan grants you a single credit balance that you can spend anywhere. Partner Nodes (formerly API nodes) will have set credit prices per usage. For Cloud workflows, you will be charged credits based on the exact duration of your workflow run — longer runs consume more credits.',
|
||||
'zh-CN':
|
||||
'您的月度计划会授予一个可在任何地方使用的积分余额。合作伙伴节点(原 API 节点)按使用量设定积分价格。对于云端工作流,将根据工作流运行的确切时长收取积分——运行时间越长消耗的积分越多。'
|
||||
},
|
||||
'cloud.faq.12.q': {
|
||||
en: "What's the difference between Partner Node credits and my Cloud subscription?",
|
||||
'zh-CN': '合作伙伴节点积分和我的 Cloud 订阅有什么区别?'
|
||||
},
|
||||
'cloud.faq.12.a': {
|
||||
en: 'Comfy Cloud has a credit system that is used for both Partner nodes (formerly API nodes) and running workflows on cloud.\n1. Partner Nodes (Pay-as-you-go): These nodes (formerly called API nodes) run third-party models via API calls and can be used on both Comfy Cloud and Local/Self-Hosted ComfyUI. Each node has its own usage cost, determined by the API provider, and we directly match their pricing.\n2. Running workflows on cloud: Exclusive to Comfy Cloud, you get a set amount of credits per month, with the amount differing based on your plan. More credits can be topped up anytime. Credits are only used up for GPU time while workflows are running — not while editing or building them. No idle costs, no setup, and no infrastructure to manage.',
|
||||
'zh-CN':
|
||||
'Comfy Cloud 有一个积分系统,用于合作伙伴节点(原 API 节点)和在云端运行工作流。\n1. 合作伙伴节点(按需付费):这些节点(原称 API 节点)通过 API 调用运行第三方模型,可在 Comfy Cloud 和本地/自托管 ComfyUI 上使用。每个节点有其自身的使用成本,由 API 提供商决定,我们直接匹配他们的定价。\n2. 在云端运行工作流:Comfy Cloud 专属,您每月获得一定数量的积分,数量根据您的计划而不同。积分可随时充值。积分仅在工作流运行时用于 GPU 时间——编辑或构建时不消耗。无闲置成本,无需设置,无需管理基础设施。'
|
||||
},
|
||||
'cloud.faq.13.q': {
|
||||
en: 'Can I cancel my subscription?',
|
||||
'zh-CN': '我可以取消订阅吗?'
|
||||
},
|
||||
'cloud.faq.13.a': {
|
||||
en: "Yes. You can cancel your subscription anytime through your account's billing settings, powered by Stripe. Your plan will remain active until the end of your current billing period.",
|
||||
'zh-CN':
|
||||
'可以。您可以随时通过账户的账单设置取消订阅(由 Stripe 提供支持)。您的计划将在当前计费周期结束前保持有效。'
|
||||
},
|
||||
'cloud.faq.14.q': {
|
||||
en: "Where can I find my invoices or add my company's tax ID?",
|
||||
'zh-CN': '我在哪里可以找到发票或添加公司税号?'
|
||||
},
|
||||
'cloud.faq.14.a': {
|
||||
en: "You can manage all billing details directly through your Stripe portal.\nGo to Settings → Plans & Credits → Invoice History to open the Stripe portal. From there, you can view and download invoices, update your billing information, and add your company's tax ID.",
|
||||
'zh-CN':
|
||||
'您可以通过 Stripe 门户直接管理所有账单详情。\n前往设置 → 计划与积分 → 发票历史以打开 Stripe 门户。在那里,您可以查看和下载发票、更新账单信息并添加公司税号。'
|
||||
},
|
||||
'cloud.faq.15.q': {
|
||||
en: 'Will ComfyUI always be free to run locally?',
|
||||
'zh-CN': 'ComfyUI 本地运行会一直免费吗?'
|
||||
},
|
||||
'cloud.faq.15.a': {
|
||||
en: "Yes, absolutely. ComfyUI will always be free and open source. You can deploy it however you want, such as downloading it from GitHub, using Docker, custom setups, etc.\n\nComfy Cloud is an optional hosted service for those who prefer convenience, accessibility, or don't have powerful GPUs.",
|
||||
'zh-CN':
|
||||
'是的,绝对如此。ComfyUI 将始终免费且开源。您可以按任何方式部署它,例如从 GitHub 下载、使用 Docker、自定义设置等。\n\nComfy Cloud 是一项可选的托管服务,适合偏好便捷性、可访问性或没有强大 GPU 的用户。'
|
||||
},
|
||||
|
||||
'buildWhat.row1': { en: 'BUILD WHAT', 'zh-CN': '构建' },
|
||||
'buildWhat.row2a': { en: "DOESN'T EXIST", 'zh-CN': '尚不存在的' },
|
||||
'buildWhat.row2b': { en: 'YET', 'zh-CN': '事物' },
|
||||
|
||||
@@ -117,7 +117,7 @@ const websiteJsonLd = {
|
||||
)}
|
||||
|
||||
<SiteNav locale={locale} client:load />
|
||||
<main>
|
||||
<main class="mt-20 lg:mt-32">
|
||||
<slot />
|
||||
</main>
|
||||
<SiteFooter locale={locale} client:load />
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro'
|
||||
import HeroSection from '../../components/product/cloud/HeroSection.vue'
|
||||
import ReasonSection from '../../components/product/cloud/ReasonSection.vue'
|
||||
import AIModelsSection from '../../components/product/cloud/AIModelsSection.vue'
|
||||
import AudienceSection from '../../components/product/cloud/AudienceSection.vue'
|
||||
import PricingSection from '../../components/product/cloud/PricingSection.vue'
|
||||
import ProductCardsSection from '../../components/product/cloud/ProductCardsSection.vue'
|
||||
import FAQSection from '../../components/product/cloud/FAQSection.vue'
|
||||
---
|
||||
|
||||
<BaseLayout title="Comfy Cloud — AI in the Cloud">
|
||||
<!-- TODO: Add page content -->
|
||||
<HeroSection />
|
||||
<ReasonSection />
|
||||
<AIModelsSection />
|
||||
<AudienceSection />
|
||||
<PricingSection />
|
||||
<ProductCardsSection />
|
||||
<FAQSection client:visible />
|
||||
</BaseLayout>
|
||||
|
||||