yonagush Claude Sonnet 4.6 commited on
Commit
012f7ff
Β·
1 Parent(s): ef3da7b

fix: CTA links to article URL not homepage; 'READ FULL STORY' button with domain underneath

Browse files
app.py CHANGED
@@ -76,14 +76,16 @@ async def fetch_article_endpoint(url: str = Form(...)):
76
  async def carousel_generate(
77
  url: str = Form(...),
78
  brand_name: str = Form("ChainStreet"),
79
- cta_url: str = Form("chainstreet.io"),
80
  groq_api_key: str = Form(""),
81
  logo_file: Optional[UploadFile] = File(None),
82
  ):
83
  """URL β†’ fetch article β†’ AI 5-slide carousel β†’ PIL images."""
84
  logo_bytes = (await logo_file.read()) if (logo_file and logo_file.filename) else None
 
 
85
  jid = _new_job()
86
- asyncio.create_task(_carousel_job(jid, url, brand_name, cta_url, groq_api_key, logo_bytes))
87
  return {"job_id": jid, "status": "queued"}
88
 
89
  async def _carousel_job(jid, url, brand_name, cta_url, api_key, logo_bytes):
 
76
  async def carousel_generate(
77
  url: str = Form(...),
78
  brand_name: str = Form("ChainStreet"),
79
+ cta_url: str = Form(""),
80
  groq_api_key: str = Form(""),
81
  logo_file: Optional[UploadFile] = File(None),
82
  ):
83
  """URL β†’ fetch article β†’ AI 5-slide carousel β†’ PIL images."""
84
  logo_bytes = (await logo_file.read()) if (logo_file and logo_file.filename) else None
85
+ # Default CTA to the article URL itself β€” drives traffic back to the exact piece
86
+ effective_cta = cta_url.strip() or url.strip()
87
  jid = _new_job()
88
+ asyncio.create_task(_carousel_job(jid, url, brand_name, effective_cta, groq_api_key, logo_bytes))
89
  return {"job_id": jid, "status": "queued"}
90
 
91
  async def _carousel_job(jid, url, brand_name, cta_url, api_key, logo_bytes):
frontend/src/components/SocialStudio.jsx CHANGED
@@ -151,7 +151,7 @@ export default function SocialStudio({ brand, setBrand }) {
151
 
152
  // Carousel fields
153
  const [url, setUrl] = useState('')
154
- const [ctaUrl, setCtaUrl] = useState('chainstreet.io')
155
 
156
  // Single-card fields
157
  const [topic, setTopic] = useState('')
@@ -307,12 +307,12 @@ export default function SocialStudio({ brand, setBrand }) {
307
  <input
308
  className="input"
309
  type="text"
310
- placeholder="chainstreet.io"
311
  value={ctaUrl}
312
  onChange={e => setCtaUrl(e.target.value)}
313
  disabled={loading}
314
  />
315
- <p className="text-xs text-white/30">Shown on the final slide as a branded button</p>
316
  </div>
317
  </>
318
  )}
 
151
 
152
  // Carousel fields
153
  const [url, setUrl] = useState('')
154
+ const [ctaUrl, setCtaUrl] = useState('')
155
 
156
  // Single-card fields
157
  const [topic, setTopic] = useState('')
 
307
  <input
308
  className="input"
309
  type="text"
310
+ placeholder={url.trim() || "Leave blank to use article URL"}
311
  value={ctaUrl}
312
  onChange={e => setCtaUrl(e.target.value)}
313
  disabled={loading}
314
  />
315
+ <p className="text-xs text-white/30">Leave blank to link back to the article automatically</p>
316
  </div>
317
  </>
318
  )}
services/carousel_service.py CHANGED
@@ -322,20 +322,32 @@ def _slide_cta(slide, bg_bytes, brand, cta_url, idx, total):
322
  y2 += 44
323
 
324
  # CTA button β€” gold, centred
325
- btn_label = f"β†’ {(cta_url or 'chainstreet.io').upper()}"
326
- btnf = _f(30, bold=True)
327
- btw = d.textlength(btn_label, font=btnf)
328
- pad_x, pad_y = 48, 18
 
 
 
 
 
 
 
 
329
  bx0 = (W - btw - pad_x*2) // 2
330
- by0 = max(y2 + 44, H - 200)
331
- bx1, by1 = bx0 + btw + pad_x*2, by0 + 64
332
  # Outer glow
333
  for expand in [10, 6, 3]:
334
  d.rounded_rectangle([bx0-expand, by0-expand, bx1+expand, by1+expand],
335
- radius=36+expand, fill=(*GOLD, 28))
336
- d.rounded_rectangle([bx0, by0, bx1, by1], radius=34, fill=GOLD)
337
  d.text((bx0 + pad_x, by0 + pad_y), btn_label, font=btnf, fill=BLACK)
338
 
 
 
 
 
339
  _num(d, idx, total)
340
  return _jpeg(img)
341
 
 
322
  y2 += 44
323
 
324
  # CTA button β€” gold, centred
325
+ # Extract display domain from cta_url (show "chainstreet.io" even if full URL passed)
326
+ raw_cta = cta_url or "chainstreet.io"
327
+ import re as _re
328
+ domain_match = _re.search(r'(?:https?://)?([^/]+)', raw_cta)
329
+ display_domain = domain_match.group(1) if domain_match else raw_cta
330
+ btn_label = f"β†’ READ FULL STORY"
331
+ domain_label = display_domain.lower()
332
+
333
+ btnf = _f(30, bold=True)
334
+ domf = _f(22, bold=False)
335
+ btw = d.textlength(btn_label, font=btnf)
336
+ pad_x, pad_y = 48, 16
337
  bx0 = (W - btw - pad_x*2) // 2
338
+ by0 = max(y2 + 36, H - 215)
339
+ bx1, by1 = bx0 + btw + pad_x*2, by0 + 60
340
  # Outer glow
341
  for expand in [10, 6, 3]:
342
  d.rounded_rectangle([bx0-expand, by0-expand, bx1+expand, by1+expand],
343
+ radius=34+expand, fill=(*GOLD, 28))
344
+ d.rounded_rectangle([bx0, by0, bx1, by1], radius=32, fill=GOLD)
345
  d.text((bx0 + pad_x, by0 + pad_y), btn_label, font=btnf, fill=BLACK)
346
 
347
+ # Domain URL underneath button
348
+ dtw = d.textlength(domain_label, font=domf)
349
+ d.text(((W - dtw)//2, by1 + 14), domain_label, font=domf, fill=(*GRAY,))
350
+
351
  _num(d, idx, total)
352
  return _jpeg(img)
353