Skip to content

feat(docs): surface external-service homepage links from block metadata#5034

Open
waleedlatif1 wants to merge 2 commits into
stagingfrom
feat/blockmeta-docs-links
Open

feat(docs): surface external-service homepage links from block metadata#5034
waleedlatif1 wants to merge 2 commits into
stagingfrom
feat/blockmeta-docs-links

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Extract the new BlockMeta.url (external-service homepage, e.g. exa.ai) in generate-docs.ts and project it into integrations.json
  • Render an outbound Visit link on every generated integration doc page via BlockInfoCard (design-system tokens, rel="noopener noreferrer")
  • Add a Visit {name} CTA on the integration landing pages and a SoftwareApplication.sameAs entity link in the JSON-LD — strengthens topical authority for SEO
  • Regenerated docs: per-page BlockInfoCard props + icons sync (picked up the newer Quartr logo that the docs copy had drifted from)

Type of Change

  • New feature (non-breaking)

Testing

Tested manually — ran bun run generate-docs, verified 207 integrations carry a url, spot-checked MDX (with + without url), tsc --noEmit, bun run lint, and bun run check:api-validation all pass.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Jun 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jun 13, 2026 9:05pm

Request Review

@cursor

cursor Bot commented Jun 13, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
Docs and presentational UI only; outbound links use standard security attributes with no auth or runtime behavior changes.

Overview
Integration doc pages now pass name and href into BlockInfoCard, which renders an optional Visit {service} outbound link (new tab, noopener noreferrer) under the hero icon when a homepage URL is provided. Internal blocks without a URL (e.g. Deployments, File, Knowledge) stay icon-only.

QuartrIcon is updated to a compact 32×32 mark (logo on white) instead of the wide wordmark SVG.

English integration MDX was regenerated in bulk so each third-party integration supplies the new props; formatting on BlockInfoCard usage is normalized.

Reviewed by Cursor Bugbot for commit 73a538f. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit deb0def. Configure here.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2fa2183. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR surfaces BlockMeta.url (the external-service homepage) across three doc surfaces: a "Visit" outbound link in each generated MDX integration page via BlockInfoCard, a CTA button on the integration landing page, and an about: Organization JSON-LD node for SEO. A new extractBlockMetaBody helper refactors shared meta-scoping logic between the existing extractTagsFromBlockMeta and the new extractUrlFromBlockMeta.

  • generate-docs.ts: Adds extractBlockMetaBody + extractUrlFromBlockMeta; projects url into integrations.json and the MDX BlockInfoCard component attributes; refactors extractTagsFromBlockMeta to use the shared helper.
  • BlockInfoCard: Gains name/href props and renders an outbound Visit link with rel="noopener noreferrer", design-system CSS tokens, and a formatHostname display helper. The surrounding wrapper gains not-prose to prevent Fumadocs prose styles from overriding the link appearance.
  • Integration landing page: Adds a "Visit {name}" animated CTA button and an about: { '@type': 'Organization', … } JSON-LD entity (note: the PR description calls this sameAs, but about is the correct schema.org property for this relationship).

Confidence Score: 5/5

Safe to merge; all changed surfaces are additive doc/metadata features with no mutations to runtime workflow logic.

The doc-generation script changes are isolated to a new extraction path that produces optional metadata — blocks without a BlockMeta.url are unaffected and fall back to the existing rendering path. The BlockInfoCard and landing-page additions are purely presentational and conditionally rendered only when url is present. All 207 generated URL values are well-formed absolute HTTPS URLs verified by the author's manual run. No runtime logic, auth paths, database writes, or API contracts are touched.

No files require special attention beyond the doc-generation script, where a prior review already identified the regex word-boundary and MDX-escaping risks that apply to future block additions.

Important Files Changed

Filename Overview
scripts/generate-docs.ts Adds extractBlockMetaBody + extractUrlFromBlockMeta for URL extraction; refactors extractTagsFromBlockMeta to share the new helper. The regex in extractStringPropertyFromContent has no word-boundary guard (pre-existing risk, already flagged in prior review). MDX template interpolates name/url without escaping double-quotes (also pre-flagged).
apps/docs/components/ui/block-info-card.tsx Adds name/href props and renders an outbound Visit link with correct rel="noopener noreferrer", design-system tokens, and a formatHostname display helper. Logic is straightforward; no issues found.
apps/sim/app/(landing)/integrations/(shell)/[slug]/page.tsx Adds websiteUrl CTA button with animation and an about:Organization JSON-LD node for SEO. Implementation is clean; rel="noopener noreferrer" present. Note: PR description calls this a sameAs link but code uses about — about is semantically more accurate here.
apps/sim/lib/integrations/types.ts Adds optional url field to Integration interface with clear JSDoc. All 207 current integration entries carry properly-formed https:// URLs.
apps/docs/components/icons.tsx QuartrIcon replaced with a 32x32 logo variant embedding hardcoded fill='white' and fill='black' paths; does not inherit text-color / currentColor like every other icon in the file. This was flagged in the prior review.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Block source file\n(*BlockMeta.url)"] --> B["extractBlockMetaBody\nscopes to meta literal"]
    B --> C["extractUrlFromBlockMeta\nreturns url string or null"]
    C --> D["extractBlockConfigFromContent\nattaches url to BlockConfig"]
    D --> E["writeIntegrationsJson\nurl → integrations.json"]
    D --> F["generateMarkdownForBlock\nurl → MDX BlockInfoCard props"]
    E --> G["Integration Landing Page\nVisit CTA + JSON-LD about:Org"]
    F --> H["BlockInfoCard component\noutbound Visit link"]
    style A fill:#f0f4ff,stroke:#6b7280
    style G fill:#f0fff4,stroke:#6b7280
    style H fill:#f0fff4,stroke:#6b7280
Loading

Reviews (4): Last reviewed commit: "fix(docs): style block-info-card visit l..." | Re-trigger Greptile

Comment on lines 3705 to 3724
export function QuartrIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} viewBox='0 0 151 40' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M11.5483 29.9995C11.5483 35.5222 19.8031 39.9995 26.1809 39.9995C29.574 39.9995 32.6254 38.7323 34.7384 36.7144L26.1809 29.9995H11.5483Z'
fill='currentColor'
/>
<path
d='M0 29.9996C0 35.5222 5.17047 39.9995 11.5483 39.9995V19.9998C5.17047 19.9998 0 24.4768 0 29.9996Z'
fill='currentColor'
/>
<path
d='M26.1808 9.99989V27.5075H37.7292V10.0249C37.7173 4.46985 32.552 -0.0135841 26.1808 2.98331e-05H11.4362V0.00114226C5.11012 0.0536532 0 4.50986 0 9.99989V19.9998H11.5483V9.99989H26.1808'
fill='currentColor'
/>
<path d='M28.1898 29.3275L40.7827 38.9295V29.3275H28.1898Z' fill='currentColor' />
<path
d='M88.7938 25.1089H88.7527C87.927 29.0309 85.4089 30.806 82.0238 30.806C77.0289 30.806 75.2949 27.5033 75.2949 23.3752V11.0324H78.8865V22.4671C78.8865 25.1089 80.0423 27.5033 83.5102 27.5033C86.8123 27.5033 88.7938 25.6872 88.7938 22.4671V11.0324H92.3854V30.3106H88.7938V25.1089Z'
fill='currentColor'
/>
<path
d='M107.351 23.1277V22.2193H101.613C99.1774 22.2193 97.8563 23.1277 97.8563 24.82C97.8563 26.3063 98.8062 27.7095 101.613 27.7095C105.163 27.7095 107.351 25.7697 107.351 23.1277ZM107.351 25.3982H107.31C106.443 28.948 103.842 30.7235 100.499 30.7235C96.7419 30.7235 94.3067 28.907 94.3067 25.2742C94.3067 21.8892 96.4529 19.371 101.118 19.371H107.351V18.3389C107.351 15.3254 106.196 13.6742 103.14 13.6742C100.375 13.6742 98.8473 14.9538 98.3108 18.1324L94.8843 17.6371C95.5447 13.1375 98.1869 10.6193 103.264 10.6193C108.631 10.6193 110.943 13.3438 110.943 18.2976V30.3106H107.351V25.3982'
fill='currentColor'
/>
<path
d='M112.909 30.3106V11.0324H116.5V16.9767H116.583C117.367 12.477 119.184 11.0324 122.28 11.0324H123.477V14.995H121.743C118.111 14.995 116.5 16.3988 116.5 20.1141V30.3106H112.909'
fill='currentColor'
/>
<path
d='M128.24 14.1166L125.391 15.0444V11.0205H128.24V5.69549H131.832V11.0205H137.982V15.0146L131.832 14.1166V24.0654C131.832 26.2122 132.657 27.3266 134.804 27.3266C135.836 27.3266 136.703 27.1203 137.735 26.8313L138.189 30.1339C136.868 30.5465 135.836 30.7944 134.06 30.7944C130.015 30.7944 128.24 28.1524 128.24 24.5609V14.1166'
fill='currentColor'
/>
<path
d='M140.129 30.3106V11.0324H143.72V16.9767H143.803C144.587 12.477 146.403 11.0324 149.499 11.0324H150.697V14.995H148.963C145.33 14.995 143.72 16.3988 143.72 20.1141V30.3106H140.129'
fill='currentColor'
/>
<path
d='M70.4206 19.1212C70.4206 14.0209 66.7718 10.045 61.8913 10.045C57.0108 10.045 53.0603 14.0209 53.0603 19.1212C53.0603 24.5028 56.9397 27.7175 61.8913 27.7175C66.8429 27.7175 70.4206 24.5028 70.4206 19.1212V19.1212ZM49.7827 19.1615C49.7827 11.9726 55.2655 6.67151 61.8913 6.67151C68.553 6.67151 73.6979 11.9726 73.6979 19.1615C73.6979 23.4184 71.6333 26.3923 68.9972 28.6411L72.0609 34.2237V34.304H68.4629L66.3614 30.2878C65.1145 30.7696 63.423 31.051 61.8913 31.051C55.1227 31.051 49.7827 26.3098 49.7827 19.1615'
fill='currentColor'
/>
<svg {...props} viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect width='32' height='32' rx='6' fill='white' />
<g transform='translate(6 6.19) scale(0.16624)'>
<path
d='M34.0674 88.4993C34.0674 104.791 58.4191 117.999 77.2336 117.999C87.2434 117.999 96.2453 114.261 102.478 108.308L77.2336 88.4993H34.0674Z'
fill='black'
/>
<path
d='M0 88.4993C0 104.791 15.253 117.999 34.0677 117.999V58.9997C15.253 58.9997 0 72.2071 0 88.4993Z'
fill='black'
/>
<path
d='M77.2339 29.4999V81.1477H111.302V29.5736C111.267 13.1861 96.0292 -0.0400894 77.2339 7.20269e-05H33.737V0.00335371C15.075 0.158262 0 13.3042 0 29.4999V58.9997H34.0677V29.4999H77.2339'
fill='black'
/>
<path d='M83.1602 86.5171L120.31 114.843V86.5171H83.1602Z' fill='black' />
</g>
</svg>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 QuartrIcon hardcodes fill='black' instead of fill='currentColor'

Every other icon in this file renders its paths with fill='currentColor' so they inherit the parent's text color — in BlockInfoCard that's text-white, which makes them appear as white silhouettes on the colored card background. The new QuartrIcon instead embeds a self-contained white rounded rectangle with black paths. On Quartr's color="#000000" card the icon will render as a white boxed app-icon floating inside a black panel rather than a clean white monochrome glyph like every other integration icon. If the card background ever changes (or the icon is used in a different listing context), the hardcoded colors will not adapt.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread scripts/generate-docs.ts
color="${bgColor || '#F5F5F5'}"
color="${bgColor || '#F5F5F5'}"${url ? `\n name="${name}"\n href="${url}"` : ''}
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 MDX JSX attributes inject name and url without escaping

If a block's name or url value ever contains a double-quote ("), the generated MDX attribute will be syntactically broken — e.g. name="Google \"Foo\" Search" or href="https://example.com/?q=\"bar\"" would both corrupt the JSX attribute boundary and prevent the page from rendering. The values are developer-controlled today so the risk is low, but adding simple escaping (value.replace(/"/g, '&quot;')) would make the template resilient to future additions.

- Extract `BlockMeta.url` in generate-docs and project it into integrations.json
- Render an outbound 'Visit' link on generated integration doc pages via BlockInfoCard
- Add a 'Visit' CTA and a vendor `about` entity reference in the landing JSON-LD for SEO
- Regenerate docs (icons sync, per-page BlockInfoCard props)
@waleedlatif1 waleedlatif1 force-pushed the feat/blockmeta-docs-links branch from 2fa2183 to 0d54af8 Compare June 13, 2026 20:53
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 0d54af8. Configure here.

@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR surfaces BlockMeta.url (the external-service homepage) through the docs pipeline: the generation script extracts it from block source files, writes it into integrations.json, and renders it as an outbound "Visit" link in both the MDX BlockInfoCard component and the landing-page integration hero. A SoftwareApplication.about entity is also injected into the JSON-LD for SEO.

  • generate-docs.ts gains extractBlockMetaBody (shared by tags and URL extraction) and extractUrlFromBlockMeta; the URL propagates into integrations.json and into the MDX <BlockInfoCard> template.
  • block-info-card.tsx gains optional name/href props and renders a styled outbound anchor below the icon card.
  • The integration landing page [slug]/page.tsx adds a conditional "Visit {name}" CTA button and wires websiteUrl into the JSON-LD about field.

Confidence Score: 4/5

Safe to merge. The Visit links and JSON-LD additions are isolated, opt-in (only shown when url is present), and the docs generation script is not run at runtime.

The core rendering changes in block-info-card.tsx and page.tsx are clean and correct. The generate-docs.ts script has two related fragility points: the string-extraction regex lacks a word boundary (a docsUrl field in any future BlockMeta literal would silently be picked up as the service homepage), and name/url values are interpolated into JSX attribute strings without escaping quotes. Neither issue breaks any currently generated file, but they are easy to hit as new blocks are added.

scripts/generate-docs.ts — the extractStringPropertyFromContent regex and the MDX attribute template string warrant a quick fix before new block metadata containing URL-suffixed properties is added.

Important Files Changed

Filename Overview
scripts/generate-docs.ts Adds url extraction from BlockMeta; refactors extractTagsFromBlockMeta to share extractBlockMetaBody; injects url into integrations.json and MDX template. The regex used for url extraction lacks a word boundary, so a property named docsUrl in a BlockMeta literal would satisfy the pattern for url.
apps/docs/components/ui/block-info-card.tsx Adds optional name/href props and renders an outbound Visit link below the icon card. Logic is clean; uses rel="noopener noreferrer" and a defensive formatHostname fallback.
apps/sim/app/(landing)/integrations/(shell)/[slug]/page.tsx Adds conditional Visit CTA button and wires websiteUrl into JSON-LD as an about Organization entity. Clean conditional rendering, correct rel attributes.
apps/sim/lib/integrations/types.ts Adds optional url field to the Integration interface with clear JSDoc. Non-breaking additive change.
apps/docs/components/icons.tsx Replaces the old path-based QuartrIcon with a new one using a white rounded rect background and hardcoded fill='black' paths. The icon won't adapt to dark mode or colored parent contexts the way other icons do.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Block source file\n(*BlockMeta.url)"] --> B["generate-docs.ts\nextractBlockMetaBody\nextractUrlFromBlockMeta"]
    B --> C["integrations.json\n{url: 'https://...'}"]
    B --> D["MDX file\n&lt;BlockInfoCard href='...' name='...' /&gt;"]
    C --> E["Integration type\n(types.ts url?: string)"]
    E --> F["[slug]/page.tsx\nwebsiteUrl"]
    F --> G["JSON-LD about\n{Organization, url}"]
    F --> H["Visit CTA button\n(outbound anchor)"]
    D --> I["BlockInfoCard\nVisit link (docs site)"]
Loading

Comments Outside Diff (1)

  1. scripts/generate-docs.ts, line 1207-1213 (link)

    P2 The regex built from propName has no word boundary, so searching for 'url' will match inside a longer property name. If a *BlockMeta literal ever contains a field whose name ends in url (e.g. docsUrl, thumbnailUrl, baseUrl) before the actual url field, extractUrlFromBlockMeta will silently return the wrong value and every generated doc page + integrations.json entry for that block will carry the wrong link.

Reviews (2): Last reviewed commit: "feat(docs): surface external-service hom..." | Re-trigger Greptile

Comment thread scripts/generate-docs.ts
<BlockInfoCard
type="${type}"
color="${bgColor || '#F5F5F5'}"
color="${bgColor || '#F5F5F5'}"${url ? `\n name="${name}"\n href="${url}"` : ''}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The name and url values are interpolated directly into a JSX attribute string without escaping double-quote characters. A block whose name contains " or a URL with an embedded " would produce malformed JSX in the generated MDX, breaking the doc page for that integration.

Suggested change
color="${bgColor || '#F5F5F5'}"${url ? `\n name="${name}"\n href="${url}"` : ''}
color="${bgColor || '#F5F5F5'}"${url ? `\n name="${name.replace(/"/g, '&quot;')}"\n href="${url.replace(/"/g, '%22')}"` : ''}

Comment on lines +3707 to 3726
<svg {...props} viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect width='32' height='32' rx='6' fill='white' />
<g transform='translate(6 6.19) scale(0.16624)'>
<path
d='M34.0674 88.4993C34.0674 104.791 58.4191 117.999 77.2336 117.999C87.2434 117.999 96.2453 114.261 102.478 108.308L77.2336 88.4993H34.0674Z'
fill='black'
/>
<path
d='M0 88.4993C0 104.791 15.253 117.999 34.0677 117.999V58.9997C15.253 58.9997 0 72.2071 0 88.4993Z'
fill='black'
/>
<path
d='M77.2339 29.4999V81.1477H111.302V29.5736C111.267 13.1861 96.0292 -0.0400894 77.2339 7.20269e-05H33.737V0.00335371C15.075 0.158262 0 13.3042 0 29.4999V58.9997H34.0677V29.4999H77.2339'
fill='black'
/>
<path d='M83.1602 86.5171L120.31 114.843V86.5171H83.1602Z' fill='black' />
</g>
</svg>
)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 QuartrIcon ignores color context

The new icon embeds a hard-coded fill='white' background rect and fill='black' paths, so it no longer responds to text-white or any CSS color applied by the parent. Every other icon in the file uses fill='currentColor', which lets the caller (e.g. BlockInfoCard's size-10 text-white) control the fill. On a dark-mode docs site or any non-white background the icon will render as a white square with a black logo regardless of the surrounding theme.

Mark the card not-prose and render the visit link as a bordered pill (no
underline, normal weight, muted) so it reads as an intentional button instead
of inheriting fumadocs' bold underlined prose-link styling.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile review

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 73a538f. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant