diff --git a/development/samples/overview.mdx b/development/samples/overview.mdx new file mode 100644 index 000000000..c8b24e8ab --- /dev/null +++ b/development/samples/overview.mdx @@ -0,0 +1,133 @@ +--- +title: "Demo Apps" +description: "Start from a working demo app built on the Comfy API." +mode: "wide" +hideFooterPagination: true +--- + +Start from a working demo app, then adapt the workflow and interface for your own project. Choose Comfy Cloud for the fastest path, or Deploy Your Own when you need your own models or nodes. + +
+
+
+
+ Preview of the image-to-image web app +
+

Image-to-image web app

+

Upload an image, run a Comfy workflow, and show the result in a Next.js app.

+
+ + +
+ +
+
+ + +
+
+ +
+
+
+ Preview of the sketch-to-image app +
+

Sketch to image

Draw or import a guide, then generate an image from the guide and a prompt.

+
+ +
+
+ + +
+
+ +
+
+
+ Preview of the Discord image bot +
+

Discord image bot

Add an `/imagine` command to a Discord server and run the bundled Comfy workflow.

+
+ +
+
+ + +
+
+
+ + +Both paths need a [Comfy API key](https://platform.comfy.org/profile/api-keys). For Deploy Your Own, download the workflow, use Builds to paste it and configure the serverless endpoint, wait for its URL, then paste that URL and the API key into your Vercel or Render deployment. + + +Want to add a demo app? See the [examples repository](https://github.com/Comfy-Org/comfy-examples). diff --git a/docs.json b/docs.json index 79f6017a8..a8f05c869 100644 --- a/docs.json +++ b/docs.json @@ -3028,6 +3028,12 @@ } ] }, + { + "group": "Demo Apps", + "pages": [ + "development/samples/overview" + ] + }, { "group": "Comfy Router", "pages": [ @@ -13897,4 +13903,4 @@ "destination": "/tutorials/partner-nodes/wan/wan3-0" } ] -} \ No newline at end of file +} diff --git a/images/samples/discord-image-bot.svg b/images/samples/discord-image-bot.svg new file mode 100644 index 000000000..4a60c6991 --- /dev/null +++ b/images/samples/discord-image-bot.svg @@ -0,0 +1,21 @@ + + + + + + + # imagine + + + C + /imagine + a lavender cat in sunlight + + C + Comfy bot + Your image is ready + + + + + diff --git a/images/samples/img2img-web-app.svg b/images/samples/img2img-web-app.svg new file mode 100644 index 000000000..445abb678 --- /dev/null +++ b/images/samples/img2img-web-app.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + Upload an image + + + + + + + Generated image + + diff --git a/images/samples/sketch-to-image.svg b/images/samples/sketch-to-image.svg new file mode 100644 index 000000000..681fd26b0 --- /dev/null +++ b/images/samples/sketch-to-image.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + Draw a guide + + + + + + Generate from sketch + + diff --git a/samples-cards.css b/samples-cards.css new file mode 100644 index 000000000..b857b168b --- /dev/null +++ b/samples-cards.css @@ -0,0 +1,31 @@ +.sample-card-grid { display: grid; gap: 20px; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 28px; } +.sample-card { min-width: 0; perspective: 1200px; } +.sample-card-inner { height: 565px; position: relative; transform-style: preserve-3d; transition: transform 560ms cubic-bezier(.2, .75, .3, 1); } +.sample-card[data-flipped="true"] .sample-card-inner { transform: rotateY(180deg); } +.sample-card-face { backface-visibility: hidden; background: #fff; border: 1px solid #e5e1eb; border-radius: 12px; box-sizing: border-box; height: 100%; inset: 0; overflow: hidden; position: absolute; width: 100%; } +.sample-card-front { display: flex; flex-direction: column; } +.sample-card-back { opacity: 0; pointer-events: none; transform: rotateY(180deg); visibility: hidden; } +.sample-card[data-flipped="true"] .sample-card-front { pointer-events: none; transition: visibility 0s linear 280ms; visibility: hidden; } +.sample-card[data-flipped="true"][data-active-path="api"] .sample-card-back[data-sample-path="api"], .sample-card[data-flipped="true"][data-active-path="endpoint"] .sample-card-back[data-sample-path="endpoint"] { opacity: 1; pointer-events: auto; visibility: visible; } +.sample-card-preview { border-bottom: 1px solid #e5e1eb; display: block; width: 100%; } +.sample-card-body { display: flex; flex: 1; flex-direction: column; padding: 20px; } +.sample-card-body h2, .sample-path-body h2 { color: #49378b; font-size: 18px; font-weight: 700; letter-spacing: -.02em; margin: 0 0 8px; } +.sample-path-body h2 { text-align: center; } +.sample-card-body > p { color: #5f5966; font-size: 14px; line-height: 1.55; margin: 0; min-height: 66px; } +.sample-card-actions { display: flex; gap: 8px; margin-top: 20px; } +.sample-card-actions button { background: #fff; border: 1px solid #cfc8de; border-radius: 6px; color: #49378b; cursor: pointer; flex: 1; font-size: 13px; font-weight: 700; padding: 10px 12px; } +.sample-card-links { border-top: 1px solid #eeeaf1; display: flex; gap: 8px; margin-top: 16px; padding-top: 12px; } +.sample-card-links a { color: #625b6c; font-size: 12px; font-weight: 650; text-decoration: none; } +.sample-card-links span { color: #b3adba; } +.sample-path-body { padding: 20px; } +.sample-path-body > button { background: transparent; border: 0; color: #625b6c; cursor: pointer; font-size: 12px; font-weight: 700; padding: 0; } +.sample-path-eyebrow { color: #857a9e; font-size: 10px; font-weight: 700; letter-spacing: .08em; margin: 19px 0 5px; text-align: center; text-transform: uppercase; } +.sample-path-body > p:not(.sample-path-eyebrow) { color: #5f5966; font-size: 12px; line-height: 1.5; margin: 0 0 15px; } +.sample-path-steps { display: grid; gap: 8px; } +.sample-path-step { align-items: center; background: #faf9fc; border: 1px solid #e5e1ea; border-radius: 8px; display: flex; gap: 10px; padding: 10px 11px; } +.sample-path-number { align-items: center; background: #49378b; border-radius: 999px; color: #fff; display: inline-flex; flex: 0 0 21px; font-size: 11px; font-weight: 700; height: 21px; justify-content: center; } +.sample-path-copy { flex: 1; min-width: 0; } +.sample-path-copy strong { color: #312c36; display: block; font-size: 12px; } +.sample-path-copy span { color: #706a75; display: block; font-size: 11px; line-height: 1.35; margin-top: 2px; } +.sample-path-step a { color: #49378b; flex: 0 0 auto; font-size: 11px; font-weight: 700; text-decoration: none; } +@media (max-width: 768px) { .sample-card-grid { grid-template-columns: 1fr; } } diff --git a/samples-cards.js b/samples-cards.js new file mode 100644 index 000000000..650e97f62 --- /dev/null +++ b/samples-cards.js @@ -0,0 +1,34 @@ +document.addEventListener("click", (event) => { + const openButton = event.target.closest("[data-sample-open]"); + const backButton = event.target.closest("[data-sample-back]"); + + if (openButton) { + const card = openButton.closest("[data-sample-card]"); + const path = openButton.dataset.sampleOpen; + const front = card.querySelector("[data-sample-front]"); + const selectedPath = card.querySelector(`[data-sample-path="${path}"]`); + event.preventDefault(); + card.dataset.activePath = path; + card.dataset.flipped = "true"; + front.inert = true; + front.setAttribute("aria-hidden", "true"); + card.querySelectorAll("[data-sample-path]").forEach((panel) => { + panel.inert = panel !== selectedPath; + panel.setAttribute("aria-hidden", panel === selectedPath ? "false" : "true"); + }); + } + + if (backButton) { + const card = backButton.closest("[data-sample-card]"); + const front = card.querySelector("[data-sample-front]"); + event.preventDefault(); + card.removeAttribute("data-flipped"); + card.removeAttribute("data-active-path"); + front.inert = false; + front.setAttribute("aria-hidden", "false"); + card.querySelectorAll("[data-sample-path]").forEach((panel) => { + panel.inert = true; + panel.setAttribute("aria-hidden", "true"); + }); + } +});