-
Notifications
You must be signed in to change notification settings - Fork 186
feat: add Appwrite MCP install cards to onboarding and project settings #2335
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
ConsoleProject ID: Sites (2)
Note You can use Avatars API to generate QR code for any text or URLs. |
WalkthroughReplaced Users images with AuthPreview/AuthPreviewDark and adjusted background positioning and responsive image heights. Changed the auth layout to a fixed row orientation with explicit flex sizing. Added a responsive two-column block containing an MCP server card (one‑click install, docs, four action anchors using Cursor/Claude/VSCode/Windsurf icons) and a Discord link card with a horizontal header and right‑aligned arrow. Introduced four new icon components (CursorIcon, ClaudeIcon, VSCodeIcon, WindsurfIcon). Added breakpoint-specific height rules for double-width/full-height cards and minor formatting cleanup around the Discord click handler. ✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (5)
src/routes/(console)/project-[region]-[project]/overview/onboard.svelte (5)
482-487
: Inline layout styles: consider moving to a CSS class.Functionally fine; moving the flex/min-width logic into a named class improves readability and keeps markup cleaner.
551-553
: Add analytics + rel to docs link.Track the MCP docs click and add rel for security on the external target.
Apply this diff:
- <Typography.Text + <Typography.Text color="--fgcolor-neutral-secondary"> - Deploy the Appwrite MCP server with a single - click, or view the <Link.Anchor - href="https://appwrite.io/docs" - target="_blank">docs</Link.Anchor> for instructions. + Deploy the Appwrite MCP server with a single + click, or view the <Link.Anchor + href="https://appwrite.io/docs" + target="_blank" + rel="noopener noreferrer" + on:click={() => trackEvent(Click.OnboardingMcpDocsClick)} + >docs</Link.Anchor> for instructions. </Typography.Text>
586-587
: Open Discord in a new tab and add rel.Keep users in Console and follow external-link best practices.
Apply this diff:
- <Card.Link - href="https://appwrite.io/discord" + <Card.Link + href="https://appwrite.io/discord" + target="_blank" + rel="noopener noreferrer"
704-707
: Avoid fixed heights to prevent content clipping; prefer min-height/clamp.Fixed heights risk overflow with larger fonts/translations. Use min-height and scale down at breakpoints.
Apply this diff:
- .double-width-card .full-height-card { - height: 240px; - width: 100%; - } + .double-width-card .full-height-card { + min-height: 240px; + width: 100%; + } @@ - .double-width-card .full-height-card { - height: 220px; - } + .double-width-card .full-height-card { + min-height: 220px; + } @@ - .double-width-card .full-height-card { - height: auto; - } + .double-width-card .full-height-card { + min-height: unset; + }Also applies to: 710-717, 720-727
539-547
: Copy: consider clarifying value prop.“MCP server” might be unfamiliar. A short “Connect your IDE to Appwrite via MCP” subheading can improve comprehension.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
src/routes/(console)/project-[region]-[project]/overview/assets/auth-preview-dark.svg
is excluded by!**/*.svg
src/routes/(console)/project-[region]-[project]/overview/assets/auth-preview.svg
is excluded by!**/*.svg
📒 Files selected for processing (1)
src/routes/(console)/project-[region]-[project]/overview/onboard.svelte
(5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: e2e
- GitHub Check: build
🔇 Additional comments (4)
src/routes/(console)/project-[region]-[project]/overview/onboard.svelte (4)
15-16
: AuthPreview asset swap looks good.Imports align with usage below and correctly support theme variants.
524-529
: Auth image: theme-aware background is correct.The flex grow and background-image swap per theme are sound.
591-595
: Discord header layout looks good.The horizontal header with icon/title improves scanability.
692-692
: Auth image positioning change is appropriate.Right-bottom works better with the new asset.
href="https://appwrite.io/docs" | ||
target="_blank">docs</Link.Anchor> for instructions. | ||
</Typography.Text> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify analytics enums and deep links exist.
Ensure Click.OnboardingMcpDocsClick and Click.OnboardingMcpInstallClick (or equivalents) are defined and captured; confirm deep-link destinations before merge.
Run:
Also applies to: 563-575
🏁 Script executed:
#!/bin/bash
# Verify analytics event enums and any MCP deep-link utilities
rg -nP 'OnboardingMcp(Docs|Install)Click|McpInstall|MCP_INSTALLS|deep.?link' -C2
Length of output: 0
🏁 Script executed:
#!/bin/bash
# Locate analytics definitions and deep-link utilities
echo "=== Files and paths containing 'analytics' ==="
fd -t f analytics || echo "(no analytics-named files found)"
echo
echo "=== Searching for OnboardingMcp event enums ==="
rg -nP 'OnboardingMcp(Docs|Install)Click' -C2 || echo "(no matches)"
echo
echo "=== Searching for deep link utilities (deepLink, deep-link) ==="
rg -nP 'deepLink|deep-?link' -C2 || echo "(no matches)"
Length of output: 215
Define missing analytics events and deep-link utilities
- Add
Click.OnboardingMcpDocsClick
andClick.OnboardingMcpInstallClick
to the analytics enum insrc/lib/actions/analytics.ts
- Implement or import the deep-link helpers for the MCP docs and install pages referenced in this component
<Typography.Text | ||
color="--fgcolor-neutral-tertiary" | ||
size="s">One-click install</Typography.Text> | ||
<Layout.Stack | ||
direction="row" | ||
gap="s" | ||
wrap="wrap"> | ||
<Button.Button size="s" variant="secondary"> | ||
Cursor | ||
</Button.Button> | ||
<Button.Button size="s" variant="secondary"> | ||
Claude Desktop | ||
</Button.Button> | ||
<Button.Button size="s" variant="secondary"> | ||
VS code | ||
</Button.Button> | ||
<Button.Button size="s" variant="secondary"> | ||
Windsurf editor | ||
</Button.Button> | ||
</Layout.Stack> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MCP “One‑click install” buttons are inert; wire them up + fix casing.
Currently these buttons have no href/on:click, so they don’t perform installs and provide no telemetry. Also, “VS code” and “Windsurf editor” casing is off.
Apply this diff to render actionable links with tracking and corrected labels:
- <Layout.Stack
- direction="row"
- gap="s"
- wrap="wrap">
- <Button.Button size="s" variant="secondary">
- Cursor
- </Button.Button>
- <Button.Button size="s" variant="secondary">
- Claude Desktop
- </Button.Button>
- <Button.Button size="s" variant="secondary">
- VS code
- </Button.Button>
- <Button.Button size="s" variant="secondary">
- Windsurf editor
- </Button.Button>
- </Layout.Stack>
+ <Layout.Stack direction="row" gap="s" wrap="wrap">
+ {#each MCP_INSTALLS as item}
+ <Link.Button
+ size="s"
+ variant="secondary"
+ href={item.href}
+ target="_blank"
+ rel="noopener noreferrer"
+ title={`Install for ${item.label}`}
+ on:click={() => trackEvent(Click.OnboardingMcpInstallClick, { client: item.id })}
+ >
+ {item.label}
+ </Link.Button>
+ {/each}
+ </Layout.Stack>
Add this helper in the <script>
block (near other constants):
const MCP_INSTALLS = [
// TODO: replace hrefs with real deep links before merge
{ id: 'cursor', label: 'Cursor', href: 'https://appwrite.io/mcp/install/cursor' },
{ id: 'claude-desktop', label: 'Claude Desktop', href: 'https://appwrite.io/mcp/install/claude-desktop' },
{ id: 'vscode', label: 'VS Code', href: 'https://appwrite.io/mcp/install/vscode' },
{ id: 'windsurf', label: 'Windsurf Editor', href: 'https://appwrite.io/mcp/install/windsurf' }
];
If deep links differ per environment/region, derive href
at runtime from config and include region/project
params.
I can wire this to actual deep links once you share the target URIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (10)
src/routes/(console)/project-[region]-[project]/overview/components/WindsurfIcon.svelte (2)
7-7
: Make size and a11y configurable (alt/aria), add non-blocking decodeExpose props for size and accessibility; keep default decorative behavior. Also add decoding="async" and draggable="false".
<script lang="ts"> import { app } from '$lib/stores/app'; import Light from '../assets/windsurf.svg'; import Dark from '../assets/dark/windsurf.svg'; + export let size: number = 16; + export let altText: string = 'Windsurf'; + export let decorative: boolean = true; </script> -<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" /> +<img + src={$app.themeInUse === 'dark' ? Dark : Light} + width={size} + height={size} + alt={decorative ? '' : altText} + aria-hidden={decorative ? 'true' : 'false'} + decoding="async" + draggable="false" +/>
1-7
: Avoid duplication across icon componentsIf Cursor/Claude/VSCode icons follow the same pattern, consider a tiny generic ThemedIcon component to reduce repetition.
Example (new file ThemedIcon.svelte):
<script lang="ts"> import { app } from '$lib/stores/app'; export let light: string; export let dark: string; export let size: number = 16; export let altText: string = ''; export let decorative: boolean = true; </script> <img src={$app.themeInUse === 'dark' ? dark : light} width={size} height={size} alt={decorative ? '' : altText} aria-hidden={decorative ? 'true' : 'false'} decoding="async" draggable="false" />Then each icon becomes a 3–5 line wrapper passing assets/alt.
src/routes/(console)/project-[region]-[project]/overview/components/CursorIcon.svelte (2)
7-7
: Exposesize
andalt
props for reuse.
Keeps default 16px while allowing overrides and better a11y.<script lang="ts"> import { app } from '$lib/stores/app'; import Light from '../assets/cursor-ai.svg'; import Dark from '../assets/dark/cursor-ai.svg'; + export let size: number = 16; + export let alt = ''; </script> -<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" /> +<img + src={$app.themeInUse === 'dark' ? Dark : Light} + width={size} + height={size} + alt={alt} + decoding="async" +/>
7-7
: Optional: avoid SSR theme flicker without store.
If you support “system”/SSR, consider prefers-color-scheme via. -<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" /> +<picture> + <source srcset={Dark} media="(prefers-color-scheme: dark)" /> + <img src={Light} width={16} height={16} alt="" decoding="async" /> +</picture>src/routes/(console)/project-[region]-[project]/overview/components/VSCodeIcon.svelte (3)
7-7
: Make the icon accessible and customizable (alt/size + rest props).If this icon is the only content inside a link/button,
alt=""
will make it invisible to screen readers. Exposealt
(default empty for decorative use),size
, and forward$$restProps
so callers can setaria-label
,class
, etc.<script lang="ts"> import { app } from '$lib/stores/app'; import Light from '../assets/vscode.svg'; import Dark from '../assets/dark/vscode.svg'; + export let alt: string = ''; + export let size: number | string = 16; </script> -<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" /> +<img + {...$$restProps} + src={$app.themeInUse === 'dark' ? Dark : Light} + width={size} + height={size} + alt={alt} + aria-hidden={alt === ''} + decoding="async" +/>Action: If this icon appears inside an anchor without visible text, ensure the anchor has an accessible name (e.g.,
aria-label="Open in VS Code"
), or pass a non-emptyalt
. Please confirm.
1-5
: Verify SSR/hydration behavior for theme-based asset swap.Using
$app.themeInUse
at render-time can cause a server–client mismatch if the initial theme differs between SSR and the browser. If you’ve seen hydration warnings or a brief light/dark swap, consider one of:
- Client-only switch (render light by default; update
src
inonMount
).- OS-driven
<picture>
fallback (avoids store coupling but won’t track in-app toggles).Alternative (OS-driven) for reference:
<picture> <source srcset={Dark} media="(prefers-color-scheme: dark)" /> <img {...$$restProps} src={Light} width={size} height={size} alt={alt} aria-hidden={alt === ''} /> </picture>
1-7
: Deduplicate: extract a reusable ThemedIcon base.You’ve added four similar icon components. A single
ThemedIcon
that acceptslight
,dark
,alt
, andsize
would reduce duplication and ensure consistent a11y and SSR handling across all icons.Happy to sketch
ThemedIcon.svelte
and update the four call sites—want me to do that?src/routes/(console)/project-[region]-[project]/overview/components/ClaudeIcon.svelte (3)
7-7
: Expose alt/size props and mark decorative usage for a11y.Empty alt makes this decorative; offer props so callers can provide accessible text when the icon stands alone.
<script lang="ts"> import { app } from '$lib/stores/app'; import Light from '../assets/claude.svg'; import Dark from '../assets/dark/claude.svg'; + export let alt: string = ''; + export let size: number = 16; </script> -<img src={$app.themeInUse === 'dark' ? Dark : Light} width="16" height="16" alt="" /> +<img + src={$app.themeInUse === 'dark' ? Dark : Light} + width={size} + height={size} + alt={alt} + aria-hidden={!alt} + decoding="async" +/>
1-7
: Consider a tiny shared ThemeIcon to de-duplicate Cursor/Claude/VSCode/Windsurf.All four components share the same logic. A single base Icon reduces churn and ensures consistent a11y.
Example base:
<!-- ThemeIcon.svelte --> <script lang="ts"> import { app } from '$lib/stores/app'; export let light: string; export let dark: string; export let alt: string = ''; export let size: number = 16; </script> <img src={$app.themeInUse === 'dark' ? dark : light} width={size} height={size} alt={alt} aria-hidden={!alt} decoding="async" />Usage in ClaudeIcon:
<script lang="ts"> import ThemeIcon from './ThemeIcon.svelte'; import Light from '../assets/claude.svg'; import Dark from '../assets/dark/claude.svg'; export let alt = ''; export let size = 16; </script> <ThemeIcon light={Light} dark={Dark} {alt} {size} />
2-5
: Optional: avoid store dependency if OS theme is sufficient.If syncing to OS theme is acceptable for these tiny assets,
removes the store subscription and avoids potential SSR/hydration flips. <picture> <source srcset={Dark} media="(prefers-color-scheme: dark)" /> <img src={Light} width={16} height={16} alt="" aria-hidden="true" decoding="async" /> </picture>Also applies to: 7-7
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (8)
src/routes/(console)/project-[region]-[project]/overview/assets/claude.svg
is excluded by!**/*.svg
src/routes/(console)/project-[region]-[project]/overview/assets/cursor-ai.svg
is excluded by!**/*.svg
src/routes/(console)/project-[region]-[project]/overview/assets/dark/claude.svg
is excluded by!**/*.svg
src/routes/(console)/project-[region]-[project]/overview/assets/dark/cursor-ai.svg
is excluded by!**/*.svg
src/routes/(console)/project-[region]-[project]/overview/assets/dark/vscode.svg
is excluded by!**/*.svg
src/routes/(console)/project-[region]-[project]/overview/assets/dark/windsurf.svg
is excluded by!**/*.svg
src/routes/(console)/project-[region]-[project]/overview/assets/vscode.svg
is excluded by!**/*.svg
src/routes/(console)/project-[region]-[project]/overview/assets/windsurf.svg
is excluded by!**/*.svg
📒 Files selected for processing (5)
src/routes/(console)/project-[region]-[project]/overview/components/ClaudeIcon.svelte
(1 hunks)src/routes/(console)/project-[region]-[project]/overview/components/CursorIcon.svelte
(1 hunks)src/routes/(console)/project-[region]-[project]/overview/components/VSCodeIcon.svelte
(1 hunks)src/routes/(console)/project-[region]-[project]/overview/components/WindsurfIcon.svelte
(1 hunks)src/routes/(console)/project-[region]-[project]/overview/onboard.svelte
(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/routes/(console)/project-[region]-[project]/overview/onboard.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: e2e
- GitHub Check: build
🔇 Additional comments (6)
src/routes/(console)/project-[region]-[project]/overview/components/WindsurfIcon.svelte (2)
1-5
: LGTM: theme-aware asset switch is correctUsing the store to flip Light/Dark assets is consistent and straightforward.
7-7
: No usages of WindsurfIcon found in any .svelte files—no risk of unlabeled links.src/routes/(console)/project-[region]-[project]/overview/components/CursorIcon.svelte (2)
1-5
: LGTM: simple theme-based icon.
Pattern matches other icons; safe import usage.
2-4
: Assets and theme contract verified. Both SVGs are present undersrc/routes/.../overview/assets
, andthemeInUse
only ever yields'light'
or'dark'
(no'system'
/'auto'
), so no changes needed.src/routes/(console)/project-[region]-[project]/overview/components/ClaudeIcon.svelte (2)
2-5
: LGTM on theme-aware asset import.Imports and store usage look fine and consistent with other components.
3-4
: Assets verified: both SVGs present and non-empty
Bothsrc/routes/(console)/project-[region]-[project]/overview/assets/claude.svg
and its dark variant in…/assets/dark/claude.svg
exist and are non-zero byte files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (4)
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte (4)
33-36
: Avoid cross-route coupling for iconsThese icons live under overview/components; consider moving to a shared location (e.g., $lib/icons) to prevent route-to-route imports and ease reuse.
190-194
: Inconsistent OAuth link behavior“Connect to GitHub” uses external (new tab) while “Add installation” does not. Pick one approach for the OAuth flow to reduce confusion and potential popup blockers.
219-256
: DRY up the four action anchorsConsider a data-driven loop to cut duplication and keep labels/links/icons consistent.
- <Layout.Stack direction="row" gap="s" wrap="wrap" justifyContent="center"> - <Button.Anchor ... - </Button.Anchor> - <Button.Anchor ... - </Button.Anchor> - <Button.Anchor ... - </Button.Anchor> - <Button.Anchor ... - </Button.Anchor> - </Layout.Stack> + <Layout.Stack direction="row" gap="s" wrap="wrap" justifyContent="center"> + {#each [ + { label: 'Cursor', href: 'https://apwr.dev/docs-mcp-cursor?ref=docs', icon: CursorIcon, key: 'cursor' }, + { label: 'Claude Desktop', href: 'https://appwrite.io/docs/tooling/mcp/claude', icon: ClaudeIcon, key: 'claude' }, + { label: 'VS Code', href: 'https://apwr.dev/docs-mcp-vscode?ref=docs', icon: VSCodeIcon, key: 'vscode' }, + { label: 'Windsurf Editor', href: 'https://appwrite.io/docs/tooling/mcp/windsurf', icon: WindsurfIcon, key: 'windsurf' } + ] as a} + <Button.Anchor + href={a.href} + target="_blank" + rel="noopener noreferrer" + size="s" + variant="secondary" + on:click={() => trackEvent(Click.SettingsMcpActionClick, { target: a.key })}> + <Icon slot="start" icon={a.icon} size="xs" /> + {a.label} + </Button.Anchor> + {/each} + </Layout.Stack>
219-256
: Standardize domains/UTM across MCP links (optional)Links mix appwrite.io and apwr.dev and inconsistent ref params. Standardize domains and tracking parameters for reliable analytics and SEO.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte
(7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: e2e
- GitHub Check: build
🔇 Additional comments (4)
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte (4)
18-21
: Imports look goodImporting Table and Button (for Button.Anchor) and aliasing Link as PinkLink is consistent with usage below.
92-101
: LGTM: swapped to FormButton for “Add installation”Matches page styling and keeps analytics hook. No issues spotted.
205-209
: MCP block copy is clearNice, concise value prop. No change requested.
5-5
: Ensure FormButton forwards target/rel for anchor links
FormButton exportshref
/external
but doesn’t exposetarget
orrel
; verify it applies correct link attributes (e.g._blank
/noopener noreferrer
) to avoid a11y regressions, or useButton.Anchor
for all anchors.
<FormButton compact href="#" external>Learn more</FormButton> | ||
</svelte:fragment> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Broken “Learn more” link
href="#" will mislead users. Replace with the correct docs URL and include rel="noopener noreferrer" if it opens in a new tab.
Happy to wire the correct link if you share the target doc.
🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte
around lines 180-181, the FormButton uses href="#" which is a broken/misleading
link; replace href="#" with the correct documentation URL (e.g., the intended
docs page you provide), and if the link should open in a new tab add
target="_blank" plus rel="noopener noreferrer" to the element to prevent
tabnabbing and preserve security; ensure the button still renders as a link
(keep external prop if intended).
Deploy the Appwrite MCP server with a single click, or view the <PinkLink.Anchor | ||
href="https://appwrite.io/docs" | ||
target="_blank" | ||
rel="noreferrer">docs</PinkLink.Anchor> for instructions. | ||
</svelte:fragment> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add noopener to external docs link
Prevent tabnabbing; align with best practices.
- Deploy the Appwrite MCP server with a single click, or view the <PinkLink.Anchor
- href="https://appwrite.io/docs"
- target="_blank"
- rel="noreferrer">docs</PinkLink.Anchor> for instructions.
+ Deploy the Appwrite MCP server with a single click, or view the <PinkLink.Anchor
+ href="https://appwrite.io/docs"
+ target="_blank"
+ rel="noopener noreferrer">docs</PinkLink.Anchor> for instructions.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
Deploy the Appwrite MCP server with a single click, or view the <PinkLink.Anchor | |
href="https://appwrite.io/docs" | |
target="_blank" | |
rel="noreferrer">docs</PinkLink.Anchor> for instructions. | |
</svelte:fragment> | |
Deploy the Appwrite MCP server with a single click, or view the <PinkLink.Anchor | |
href="https://appwrite.io/docs" | |
target="_blank" | |
rel="noopener noreferrer">docs</PinkLink.Anchor> for instructions. |
🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte
around lines 213 to 217, the external docs link uses target="_blank" with
rel="noreferrer" only; add noopener to the rel attribute to prevent tabnabbing
by changing rel to "noreferrer noopener" (or "noopener noreferrer") while
keeping the existing target and other attributes intact.
<Layout.Stack direction="row" gap="s" wrap="wrap" justifyContent="center"> | ||
<Button.Anchor | ||
href="https://apwr.dev/docs-mcp-cursor?ref=docs" | ||
target="_blank" | ||
rel="noreferrer" | ||
size="s" | ||
variant="secondary"> | ||
<Icon slot="start" icon={CursorIcon} size="xs" /> | ||
Cursor | ||
</Button.Anchor> | ||
<Button.Anchor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Harden external link (Cursor) and add analytics (optional)
Add noopener; consider tracking clicks for adoption metrics.
- <Button.Anchor
+ <Button.Anchor
href="https://apwr.dev/docs-mcp-cursor?ref=docs"
target="_blank"
- rel="noreferrer"
+ rel="noopener noreferrer"
size="s"
variant="secondary">
<Icon slot="start" icon={CursorIcon} size="xs" />
Cursor
</Button.Anchor>
Optionally:
<Button.Anchor ... on:click={() => trackEvent(Click.SettingsMcpActionClick, { target: 'cursor' })}>
href="https://appwrite.io/docs/tooling/mcp/claude" | ||
target="_blank" | ||
rel="noreferrer" | ||
size="s" | ||
variant="secondary"> | ||
<Icon slot="start" icon={ClaudeIcon} size="xs" /> | ||
Claude Desktop | ||
</Button.Anchor> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Harden external link (Claude) and consider tracking
- <Button.Anchor
+ <Button.Anchor
href="https://appwrite.io/docs/tooling/mcp/claude"
target="_blank"
- rel="noreferrer"
+ rel="noopener noreferrer"
size="s"
variant="secondary">
<Icon slot="start" icon={ClaudeIcon} size="xs" />
Claude Desktop
</Button.Anchor>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
href="https://appwrite.io/docs/tooling/mcp/claude" | |
target="_blank" | |
rel="noreferrer" | |
size="s" | |
variant="secondary"> | |
<Icon slot="start" icon={ClaudeIcon} size="xs" /> | |
Claude Desktop | |
</Button.Anchor> | |
<Button.Anchor | |
href="https://appwrite.io/docs/tooling/mcp/claude" | |
target="_blank" | |
rel="noopener noreferrer" | |
size="s" | |
variant="secondary"> | |
<Icon slot="start" icon={ClaudeIcon} size="xs" /> | |
Claude Desktop | |
</Button.Anchor> |
🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte
around lines 230–237, the external Claude link should be hardened and
instrumented for tracking: add rel="noopener noreferrer" (not just noreferrer)
to prevent window.opener attacks, ensure target="_blank" remains, and either
append safe tracking query params (e.g.,
utm_source=app&utm_medium=console&utm_campaign=installations) or add an on:click
handler that calls your analytics tracker with a clear event name (e.g.,
"external_link_click" with label "claude_desktop") before navigating; ensure the
handler still opens the link in a new tab if you use programmatic navigation,
and keep accessibility attributes intact.
href="https://apwr.dev/docs-mcp-vscode?ref=docs" | ||
target="_blank" | ||
rel="noreferrer" | ||
size="s" | ||
variant="secondary"> | ||
<Icon slot="start" icon={VSCodeIcon} size="xs" /> | ||
VS code | ||
</Button.Anchor> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix product casing and harden link (VS Code)
- <Button.Anchor
+ <Button.Anchor
href="https://apwr.dev/docs-mcp-vscode?ref=docs"
target="_blank"
- rel="noreferrer"
+ rel="noopener noreferrer"
size="s"
variant="secondary">
<Icon slot="start" icon={VSCodeIcon} size="xs" />
- VS code
+ VS Code
</Button.Anchor>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
href="https://apwr.dev/docs-mcp-vscode?ref=docs" | |
target="_blank" | |
rel="noreferrer" | |
size="s" | |
variant="secondary"> | |
<Icon slot="start" icon={VSCodeIcon} size="xs" /> | |
VS code | |
</Button.Anchor> | |
<Button.Anchor | |
href="https://apwr.dev/docs-mcp-vscode?ref=docs" | |
target="_blank" | |
rel="noopener noreferrer" | |
size="s" | |
variant="secondary"> | |
<Icon slot="start" icon={VSCodeIcon} size="xs" /> | |
VS Code | |
</Button.Anchor> |
🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte
around lines 239 to 246, fix the product casing and harden the external link:
change the visible label from "VS code" to "VS Code" (capitalize "Code"), and
update the anchor rel attribute to include "noopener" in addition to
"noreferrer" (rel="noopener noreferrer") so the external link is hardened
against window.opener attacks while preserving existing attributes.
href="https://appwrite.io/docs/tooling/mcp/windsurf" | ||
target="_blank" | ||
rel="noreferrer" | ||
size="s" | ||
variant="secondary"> | ||
<Icon slot="start" icon={WindsurfIcon} size="xs" /> | ||
Windsurf editor | ||
</Button.Anchor> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix product casing and harden link (Windsurf)
- <Button.Anchor
+ <Button.Anchor
href="https://appwrite.io/docs/tooling/mcp/windsurf"
target="_blank"
- rel="noreferrer"
+ rel="noopener noreferrer"
size="s"
variant="secondary">
<Icon slot="start" icon={WindsurfIcon} size="xs" />
- Windsurf editor
+ Windsurf Editor
</Button.Anchor>
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
href="https://appwrite.io/docs/tooling/mcp/windsurf" | |
target="_blank" | |
rel="noreferrer" | |
size="s" | |
variant="secondary"> | |
<Icon slot="start" icon={WindsurfIcon} size="xs" /> | |
Windsurf editor | |
</Button.Anchor> | |
<Button.Anchor | |
href="https://appwrite.io/docs/tooling/mcp/windsurf" | |
target="_blank" | |
rel="noopener noreferrer" | |
size="s" | |
variant="secondary"> | |
<Icon slot="start" icon={WindsurfIcon} size="xs" /> | |
Windsurf Editor | |
</Button.Anchor> |
🤖 Prompt for AI Agents
In
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte
around lines 248 to 255, the button text uses incorrect product casing and the
external link rel is too weak; update the visible label to use the correct
product casing "Windsurf Editor" (capitalized) and harden the anchor by changing
rel to "noopener noreferrer" (keep target="_blank") and ensure the href is a
fully-qualified, https URL without interpolated user data; optionally add an
accessible aria-label matching the visible text.
<Button.Anchor | ||
href="https://apwr.dev/docs-mcp-cursor?ref=docs" | ||
target="_blank" | ||
rel="noreferrer" | ||
size="s" | ||
variant="secondary"> | ||
<Icon | ||
slot="start" | ||
icon={CursorIcon} | ||
size="xs" /> | ||
Cursor | ||
</Button.Anchor> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
component structure for all mcp items are same, lets make an array of objects and then use {#each ...}
to reduce the boilerplate 👍.
<Button.Anchor | ||
href="https://apwr.dev/docs-mcp-cursor?ref=docs" | ||
target="_blank" | ||
rel="noreferrer" | ||
size="s" | ||
variant="secondary"> | ||
<Icon slot="start" icon={CursorIcon} size="xs" /> | ||
Cursor | ||
</Button.Anchor> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here as above. the array object can be a const val in project level store.ts
.
What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
yes
Summary by CodeRabbit