From e19cad20da6c90020fc699612550ee9f8f132d94 Mon Sep 17 00:00:00 2001 From: Bryan Wade Date: Fri, 4 Sep 2026 13:35:43 -0700 Subject: [PATCH] Make demo apps easier to deploy Add a Demo Apps entry to the Developers sidebar and a wide visual card directory for the current Comfy examples. Each card offers Comfy Cloud or Deploy Your Own, then rotates horizontally to the selected path's sample-specific steps. Constraint: Existing example source and workflow downloads retain their GitHub access controls Constraint: Demo card interaction must remain compatible with Mint's MDX hydration model Rejected: Direct external deployment links only | Users need the API key, workflow, Build, and endpoint handoff before leaving the page Rejected: Page-defined MDX components | Mint removes them from its client no-JS rendering pass Confidence: medium Scope-risk: narrow Reversibility: clean Directive: Keep the Comfy Cloud and Deploy Your Own paths aligned with the provider templates and Developer Platform Builds flow Tested: Mint local preview; actual browser card flip checks; MDX parse; SVG asset HTTP checks; verified Discord Render repository; git diff --check Not-tested: External Vercel, Render, and Developer Platform authenticated handoffs; zoomed or translated reverse-card layouts --- development/samples/overview.mdx | 133 +++++++++++++++++++++++++++ docs.json | 8 +- images/samples/discord-image-bot.svg | 21 +++++ images/samples/img2img-web-app.svg | 20 ++++ images/samples/sketch-to-image.svg | 19 ++++ samples-cards.css | 31 +++++++ samples-cards.js | 34 +++++++ 7 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 development/samples/overview.mdx create mode 100644 images/samples/discord-image-bot.svg create mode 100644 images/samples/img2img-web-app.svg create mode 100644 images/samples/sketch-to-image.svg create mode 100644 samples-cards.css create mode 100644 samples-cards.js 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"); + }); + } +});