From 39c9cbc858704f7652be0fef05aa9fee277b115d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 18 Aug 2026 16:48:21 +0200 Subject: [PATCH 1/5] feat: add Origin (Cursor) as a connectable git provider Adds Origin, Cursor's git hosting, alongside GitHub and Gitea: - IconOrigin.svelte with the Cursor cube mark, currentcolor like the other provider icons - connectOrigin() plus provider URL helpers in the git store, and a Connect to Origin button (second, after GitHub) in the shared connect empty state, gated on _APP_VCS_PROVIDERS containing 'origin' - Provider-aware settings page: Origin icon and cursor.com/codebase links for installations, per-row Configure follows the row's provider instead of always GitHub, unknown providers fall back to a git icon - Repository cards, git-deployment modals and the deployment source popover no longer assume GitHub for icons and repository links Co-Authored-By: Claude Fable 5 --- src/lib/components/git/IconOrigin.svelte | 13 ++++++++++ src/lib/components/git/connectGit.svelte | 14 +++++++++- .../components/git/deploymentSource.svelte | 21 ++++++++++++--- src/lib/components/git/repositoryCard.svelte | 12 +++++++-- src/lib/stores/git.ts | 26 ++++++++++++++++++- .../(modals)/createGit.svelte | 11 ++++++-- .../settings/updateInstallations.svelte | 18 +++++++++---- .../createGitDeploymentModal.svelte | 11 ++++++-- 8 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 src/lib/components/git/IconOrigin.svelte diff --git a/src/lib/components/git/IconOrigin.svelte b/src/lib/components/git/IconOrigin.svelte new file mode 100644 index 0000000000..184073d564 --- /dev/null +++ b/src/lib/components/git/IconOrigin.svelte @@ -0,0 +1,13 @@ + + + + diff --git a/src/lib/components/git/connectGit.svelte b/src/lib/components/git/connectGit.svelte index 6473f5f6d1..6f856561a1 100644 --- a/src/lib/components/git/connectGit.svelte +++ b/src/lib/components/git/connectGit.svelte @@ -1,11 +1,12 @@ @@ -49,6 +52,15 @@ Connect to GitHub + {#if isOriginEnabled} + + {/if} {#if isGiteaEnabled} (showInstallationDropdown[i] = false)}> Configure @@ -192,7 +200,7 @@ Connect to GitHub diff --git a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createGitDeploymentModal.svelte b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createGitDeploymentModal.svelte index 6e98419d04..55af1b71b0 100644 --- a/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createGitDeploymentModal.svelte +++ b/src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/createGitDeploymentModal.svelte @@ -10,6 +10,8 @@ import { addNotification } from '$lib/stores/notifications'; import { sdk } from '$lib/stores/sdk'; import { installation, repository } from '$lib/stores/vcs'; + import { getProviderRepositoryUrl } from '$lib/stores/git'; + import IconOrigin from '$lib/components/git/IconOrigin.svelte'; import { Adapter, BuildRuntime, @@ -160,10 +162,15 @@ alignItems="center" gap="xs"> - + + href={getProviderRepositoryUrl( + $installation?.provider ?? 'github', + $repository?.organization, + $repository?.name + )}> {$repository?.organization}/{$repository?.name} From a80f2f7860a0cc941ff4d808cfd04e824a9482e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 18 Aug 2026 17:58:28 +0200 Subject: [PATCH 2/5] feat: choose between git providers across the console Builds on the Origin groundwork with a central provider registry in the git store (icons, labels, provider URLs, installation-settings URLs and enablement gates), the same shape the new console uses. Everything that assumed GitHub now reads from it: - Connect empty states (shared connectGit, settings, both template wizards) render one button per enabled provider - Settings' Add installation becomes a provider chooser when more than one provider is enabled, and each row's Configure follows the row's provider - The repository picker labels installations with their provider icon and offers per-provider 'Add installation' entries - 'Missing a repository?' permission links follow the installation's provider (GitHub installation settings vs Cursor's app settings) - Wizard asides and repository cards show the connected provider's icon - The create-repository modal explains that Origin apps cannot create repositories and points at cursor.com instead Co-Authored-By: Claude Fable 5 --- src/lib/components/git/connectGit.svelte | 36 ++------ .../components/git/connectRepoModal.svelte | 54 +++++++++-- src/lib/components/git/repositories.svelte | 31 +++++-- src/lib/stores/git.ts | 84 +++++++++++++++-- .../create-function/(components)/aside.svelte | 9 +- .../functions/create-function/+page.svelte | 7 +- .../repository-[repository]/repoCard.svelte | 7 +- .../template-[template]/+page.svelte | 18 ++-- .../template-[template]/repoCard.svelte | 6 +- .../settings/updateInstallations.svelte | 92 +++++++++++-------- .../sites/create-site/aside.svelte | 9 +- .../create-site/repositories/+page.svelte | 16 ++-- .../repository-[repository]/+page.svelte | 6 +- .../template-[template]/+page.svelte | 17 +++- 14 files changed, 274 insertions(+), 118 deletions(-) diff --git a/src/lib/components/git/connectGit.svelte b/src/lib/components/git/connectGit.svelte index 6f856561a1..823b3929e7 100644 --- a/src/lib/components/git/connectGit.svelte +++ b/src/lib/components/git/connectGit.svelte @@ -1,12 +1,9 @@ @@ -45,31 +39,15 @@ description="Add an installation to connect repositories"> - - {#if isOriginEnabled} - - {/if} - {#if isGiteaEnabled} + {#each providers as provider (provider.id)} - {/if} + {/each} diff --git a/src/lib/components/git/connectRepoModal.svelte b/src/lib/components/git/connectRepoModal.svelte index faca63d917..6ccc1d204f 100644 --- a/src/lib/components/git/connectRepoModal.svelte +++ b/src/lib/components/git/connectRepoModal.svelte @@ -12,8 +12,10 @@ import { addNotification } from '$lib/stores/notifications'; import { Click, trackEvent } from '$lib/actions/analytics'; import RepositoryBehaviour from '$lib/components/git/repositoryBehaviour.svelte'; + import { Alert } from '@appwrite.io/pink-svelte'; import { page } from '$app/state'; - import { connectGitHub } from '$lib/stores/git'; + + import { connectVcsProvider, getVcsProvider } from '$lib/stores/git'; let { show = $bindable(false), @@ -37,6 +39,17 @@ let selectedInstallationId = $state(''); let selectedRepository = $state(''); let installations = $state({ installations: [], total: 0 }); + const selectedProvider = $derived( + getVcsProvider( + installations?.installations?.find((entry) => entry.$id === selectedInstallationId) + ?.provider ?? + $installation?.provider ?? + 'github' + ) + ); + // Origin's partner API has no repository creation for apps; repositories + // made on cursor.com appear in the picker automatically. + const creationUnsupported = $derived(selectedProvider.id === 'origin'); let error = $state(''); onMount(async () => { @@ -105,6 +118,14 @@ bind:repositoryPrivate bind:selectedInstallationId {installations} /> + {#if creationUnsupported} + + Origin does not allow apps to create repositories. Create it in the + Codebase section on cursor.com + and it will appear in the repository list here. + + {/if} {:else} {#if repositoryBehaviour === 'existing'} - - - Missing a repository? check your permissions - - + {#if $installation?.provider === 'origin'} + + + Missing a repository? check your repository access + + + {:else} + + + Missing a repository? check your permissions + + + {/if} {:else if repositoryBehaviour === 'new'} - {/if} diff --git a/src/lib/components/git/repositories.svelte b/src/lib/components/git/repositories.svelte index df2c800501..6e6181dadd 100644 --- a/src/lib/components/git/repositories.svelte +++ b/src/lib/components/git/repositories.svelte @@ -19,7 +19,8 @@ import SvgIcon from '../svgIcon.svelte'; import { Query, VCSDetectionType, type Models } from '@appwrite.io/console'; import { getFrameworkIcon } from '$lib/stores/sites'; - import { connectGitHub } from '$lib/stores/git'; + import { connectVcsProvider, enabledVcsProviders, getVcsProvider } from '$lib/stores/git'; + import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store'; import { addNotification } from '$lib/stores/notifications'; import { page } from '$app/state'; import Card from '../card.svelte'; @@ -27,6 +28,11 @@ import { onMount, untrack, onDestroy } from 'svelte'; import { debounce } from '$lib/helpers/debounce'; + // Not in the SDK's generated types yet -- server already returns it. + const vcsProviders = enabledVcsProviders( + ($regionalConsoleVariables as { _APP_VCS_PROVIDERS?: string[] })?._APP_VCS_PROVIDERS + ); + let { action = $bindable('select'), selectedRepository = $bindable(undefined), @@ -176,18 +182,27 @@ ...installationsMap.map((entry) => { return { label: entry.organization, + leadingIcon: getVcsProvider(entry.provider).icon, value: entry.$id }; }), - { - label: 'Add installation', - leadingIcon: IconPlus, - value: 'new' - } + ...vcsProviders.map((provider) => { + return { + label: + vcsProviders.length > 1 + ? `Add ${provider.label} installation` + : 'Add installation', + leadingIcon: IconPlus, + value: `new-${provider.id}` + }; + }) ]} on:change={() => { - if (selectedInstallation === 'new') { - window.location.href = connectGitHub(callbackState).toString(); + if (selectedInstallation?.startsWith('new-')) { + window.location.href = connectVcsProvider( + selectedInstallation.slice('new-'.length), + callbackState + ).toString(); } search = ''; installation.set( diff --git a/src/lib/stores/git.ts b/src/lib/stores/git.ts index 580c181a41..00785a6501 100644 --- a/src/lib/stores/git.ts +++ b/src/lib/stores/git.ts @@ -1,6 +1,79 @@ import { page } from '$app/state'; +import type { ComponentType } from 'svelte'; +import { IconGithub } from '@appwrite.io/pink-icons-svelte'; +import IconGitea from '$lib/components/git/IconGitea.svelte'; +import IconOrigin from '$lib/components/git/IconOrigin.svelte'; +import { isSelfHosted } from '$lib/system'; import { getApiEndpoint } from './sdk'; +export type VcsProviderId = 'github' | 'origin' | 'gitea'; + +export interface VcsProviderMeta { + id: VcsProviderId; + label: string; + icon: ComponentType; + /** Web URL of an owner/organization on the provider, or '' when the host is not knowable client-side. */ + organizationUrl: (organization: string) => string; + /** Where the user manages the installation's repository access, or null when the provider has no such page. */ + installationSettingsUrl: (providerInstallationId: string) => string | null; + /** GitHub predates the _APP_VCS_PROVIDERS variable, so it stays on unconditionally. */ + alwaysEnabled?: boolean; + /** Gitea is a self-hosted-only feature, not offered on Appwrite Cloud. */ + selfHostedOnly?: boolean; +} + +/** + * Single source of truth for the console's git providers - icons, labels, + * provider URLs and the authorize redirects. Key order is display order: + * GitHub first, Origin second, then the rest. + */ +export const VCS_PROVIDERS: Record = { + github: { + id: 'github', + label: 'GitHub', + icon: IconGithub, + organizationUrl: (organization) => `https://github.com/${organization}`, + installationSettingsUrl: (providerInstallationId) => + `https://github.com/settings/installations/${providerInstallationId}`, + alwaysEnabled: true + }, + origin: { + id: 'origin', + label: 'Origin', + icon: IconOrigin as unknown as ComponentType, + // Origin repositories are browsed in the Codebase section of cursor.com + organizationUrl: (organization) => `https://cursor.com/codebase/${organization}`, + installationSettingsUrl: () => 'https://cursor.com/codebase/settings/apps' + }, + gitea: { + id: 'gitea', + label: 'Gitea', + icon: IconGitea as unknown as ComponentType, + // The Gitea host is per-deployment server configuration the client does not know + organizationUrl: () => '', + installationSettingsUrl: () => null, + selfHostedOnly: true + } +}; + +/** Provider metadata with a GitHub fallback for unknown ids (cosmetic use only). */ +export function getVcsProvider(provider?: string): VcsProviderMeta { + return VCS_PROVIDERS[provider as VcsProviderId] ?? VCS_PROVIDERS.github; +} + +/** + * Providers the connect UI should offer, honoring the server's + * _APP_VCS_PROVIDERS list and each provider's own gates. + */ +export function enabledVcsProviders(vcsProviders: string[] | undefined): VcsProviderMeta[] { + return Object.values(VCS_PROVIDERS).filter( + (provider) => + provider.alwaysEnabled || + ((vcsProviders?.includes(provider.id) ?? false) && + (!provider.selfHostedOnly || isSelfHosted)) + ); +} + export function connectVcsProvider(provider: string, callbackState: Record = null) { const redirect = new URL(page.url); if (callbackState) { @@ -29,16 +102,11 @@ export function connectOrigin(callbackState: Record = null) { } /** - * Browser URL of an owner/organization on the provider. Origin repositories - * are browsed in the Codebase section of cursor.com. + * Browser URL of an owner/organization on the provider. Unknown providers + * fall back to GitHub, matching the console's historical behavior. */ export function getProviderOrganizationUrl(provider: string, organization: string): string { - switch (provider) { - case 'origin': - return `https://cursor.com/codebase/${organization}`; - default: - return `https://github.com/${organization}`; - } + return getVcsProvider(provider).organizationUrl(organization); } /** diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/(components)/aside.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/(components)/aside.svelte index 4acf3d41a7..2b473c61a7 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/create-function/(components)/aside.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/(components)/aside.svelte @@ -1,7 +1,9 @@ diff --git a/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte b/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte index 34331de126..c4b3b645d1 100644 --- a/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte +++ b/src/routes/(console)/organization-[organization]/settings/Soc2Modal.svelte @@ -11,7 +11,7 @@ import type { Models } from '@appwrite.io/console'; export let show = false; - export let locale: Models.CloudLocale; + export let locale: Models.Locale; export let countryList: Models.CountryList; let email = ''; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/helpers/terminology.ts b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/helpers/terminology.ts index 02fb339a2a..8ca9c32a90 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/helpers/terminology.ts +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/helpers/terminology.ts @@ -1,15 +1,19 @@ import type { Page } from '@sveltejs/kit'; import { capitalize, plural } from '$lib/helpers/string'; -import { AppwriteException, type TablesDBIndexType, type Models } from '@appwrite.io/console'; +import { + AppwriteException, + type DatabaseType as ConsoleDatabaseType, + type TablesDBIndexType, + type Models +} from '@appwrite.io/console'; import type { Attributes, Collection, Columns, Table } from '$database/store'; import type { Term, TerminologyResult, TerminologyShape } from '$database/(entity)/helpers/types'; type BaseTerminology = typeof baseTerminology; type ImplementedDBTypes = Omit; -/* manual type for the time being because vectorsdb is pending */ -export type DatabaseType = 'legacy' | 'tablesdb' | 'documentsdb' | 'vectorsdb'; +export type DatabaseType = `${ConsoleDatabaseType}`; export type CollectionDatabaseType = Extract; export const DEFAULT_VECTOR_DIMENSION = 768; diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/embeddingModal.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/embeddingModal.svelte index 51d427291b..5269113176 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/embeddingModal.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/embeddingModal.svelte @@ -27,7 +27,7 @@ try { const response = await sdk .forProject(page.params.region, page.params.project) - .vectorsDB.createTextEmbeddings({ texts: [content.trim()] }); + .embeddings.createTextEmbeddings({ texts: [content.trim()] }); const embedding = response?.embeddings?.[0]?.embedding; if (embedding?.length) { diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte index 017e1a03a0..6186c43475 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte @@ -105,7 +105,8 @@ .migrations.createJSONImport({ bucketId: pendingFile.bucketId, fileId: pendingFile.$id, - resourceId: `${page.params.database}:${page.params.collection}`, + databaseId: page.params.database, + collectionId: page.params.collection, internalFile: pendingLocalFile, onDuplicate: importOnDuplicate }); diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/export/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/export/+page.svelte index a4a2b7e9aa..9d19f13b9c 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/export/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/export/+page.svelte @@ -56,7 +56,8 @@ await sdk .forProject(page.params.region, page.params.project) .migrations.createJSONExport({ - resourceId: `${page.params.database}:${page.params.collection}`, + databaseId: page.params.database, + collectionId: page.params.collection, filename: filename, columns: [], queries: exportWithFilters ? Array.from(localQueries.values()) : [], diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte index f1fe008679..89444b447a 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/+page.svelte @@ -139,7 +139,8 @@ .migrations.createCSVImport({ bucketId: pendingFile.bucketId, fileId: pendingFile.$id, - resourceId: `${page.params.database}:${page.params.table}`, + databaseId: page.params.database, + collectionId: page.params.table, internalFile: pendingLocalFile, onDuplicate: importOnDuplicate }); diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte index 9ed600090f..8291162dbb 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/export/+page.svelte @@ -101,7 +101,8 @@ await sdk .forProject(page.params.region, page.params.project) .migrations.createCSVExport({ - resourceId: `${page.params.database}:${page.params.table}`, + databaseId: page.params.database, + collectionId: page.params.table, filename: filename, columns: selectedCols, queries: exportWithFilters ? Array.from(localQueries.values()) : [], diff --git a/src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte b/src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte index 50e94d3ff0..658ceb6608 100644 --- a/src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte @@ -13,17 +13,14 @@ import { BarChart, Legend } from '$lib/charts'; import { formatNum } from '$lib/helpers/string'; import { total } from '$lib/layout/usageHelpers'; - import { base } from '$app/paths'; import { formatCurrency, formatNumberWithCommas, clampMin } from '$lib/helpers/numbers'; import { getCountryName } from '$lib/helpers/diallingCodes.js'; import { Accordion, Icon, Layout, Link, Table, Typography } from '@appwrite.io/pink-svelte'; import { IconChartSquareBar } from '@appwrite.io/pink-icons-svelte'; - import { page } from '$app/state'; import { BillingPlanGroup } from '@appwrite.io/console'; export let data; - $: baseRoute = `${base}/project-${page.params.region}-${page.params.project}`; $: network = data.usage.network; $: users = data.usage.users; $: usersTotal = data.usage.usersTotal; @@ -321,26 +318,6 @@ data: [...executions.map((e) => [e.date, e.value])] } ]} /> - {#if data.usage.executionsBreakdown?.length > 0} - - - Function - Usage - - {#each data.usage.executionsBreakdown as func} - - - {func.name ?? func.resourceId} - - - {formatNum(func.value)} executions - - - {/each} - - {/if} {:else} From 424f585c3f599a6e3d6919de07fb174c2397f741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 19 Aug 2026 17:34:37 +0200 Subject: [PATCH 4/5] fix(git): center provider connect buttons in empty states The button row in the git empty states spans the full footer width, so it needs its own centering like the MCP card below it. Also drops the now redundant _APP_VCS_PROVIDERS casts in the touched files. Co-Authored-By: Claude Fable 5 --- src/lib/components/git/connectGit.svelte | 7 ++----- .../settings/updateInstallations.svelte | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/lib/components/git/connectGit.svelte b/src/lib/components/git/connectGit.svelte index 823b3929e7..f9b0abe917 100644 --- a/src/lib/components/git/connectGit.svelte +++ b/src/lib/components/git/connectGit.svelte @@ -8,10 +8,7 @@ export let callbackState: Record = null; let isVcsEnabled = $regionalConsoleVariables?._APP_VCS_ENABLED === true; - // Not in the SDK's generated types yet -- server already returns it. - let vcsProviders = ($regionalConsoleVariables as { _APP_VCS_PROVIDERS?: string[] }) - ?._APP_VCS_PROVIDERS; - let providers = enabledVcsProviders(vcsProviders); + let providers = enabledVcsProviders($regionalConsoleVariables?._APP_VCS_PROVIDERS); @@ -38,7 +35,7 @@ title="No installation was added to the project yet" description="Add an installation to connect repositories"> - + {#each providers as provider (provider.id)} - {/if} diff --git a/src/lib/components/git/newRepository.svelte b/src/lib/components/git/newRepository.svelte index ffbb7cf5ce..ea4c0acbb2 100644 --- a/src/lib/components/git/newRepository.svelte +++ b/src/lib/components/git/newRepository.svelte @@ -1,14 +1,55 @@ @@ -18,14 +59,14 @@ id="installation" label="Git organization" disabled={disableFields} - options={installations.installations.map((entry) => { + options={creatableInstallations.map((entry) => { return { label: entry.organization, value: entry.$id }; })} on:change={() => { - $installation = installations.installations.find( + $installation = creatableInstallations.find( (entry) => entry.$id === selectedInstallationId ); }} @@ -38,9 +79,11 @@ disabled={disableFields} bind:value={repositoryName} /> - + {#if supportsPublic} + + {/if} diff --git a/src/lib/stores/git.ts b/src/lib/stores/git.ts index 00785a6501..30851d99e0 100644 --- a/src/lib/stores/git.ts +++ b/src/lib/stores/git.ts @@ -1,4 +1,5 @@ import { page } from '$app/state'; +import type { Models } from '@appwrite.io/console'; import type { ComponentType } from 'svelte'; import { IconGithub } from '@appwrite.io/pink-icons-svelte'; import IconGitea from '$lib/components/git/IconGitea.svelte'; @@ -42,7 +43,7 @@ export const VCS_PROVIDERS: Record = { label: 'Origin', icon: IconOrigin as unknown as ComponentType, // Origin repositories are browsed in the Codebase section of cursor.com - organizationUrl: (organization) => `https://cursor.com/codebase/${organization}`, + organizationUrl: () => 'https://cursor.com/codebase', installationSettingsUrl: () => 'https://cursor.com/codebase/settings/apps' }, gitea: { @@ -74,6 +75,29 @@ export function enabledVcsProviders(vcsProviders: string[] | undefined): VcsProv ); } +/** + * Whether the console can create repositories on the provider. Servers that + * predate _APP_VCS_PROVIDERS_WITH_REPOSITORY_CREATION omit the list; keep the + * console's historical behavior (creation offered) for them. + */ +export function supportsRepositoryCreation( + provider: string, + variables: Models.ConsoleVariables +): boolean { + return variables?._APP_VCS_PROVIDERS_WITH_REPOSITORY_CREATION?.includes(provider) ?? true; +} + +/** + * Whether the provider can host public repositories. When it cannot, every + * repository is private and the public/private choice is meaningless. + */ +export function supportsPublicRepositories( + provider: string, + variables: Models.ConsoleVariables +): boolean { + return variables?._APP_VCS_PROVIDERS_WITH_PUBLIC_REPOSITORIES?.includes(provider) ?? true; +} + export function connectVcsProvider(provider: string, callbackState: Record = null) { const redirect = new URL(page.url); if (callbackState) { diff --git a/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte b/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte index c552a19f89..47119c60c3 100644 --- a/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/functions/create-function/template-[template]/+page.svelte @@ -31,7 +31,11 @@ import Aside from '../(components)/aside.svelte'; import { iconPath } from '$lib/stores/app'; import Permissions from './permissions.svelte'; - import { connectVcsProvider, enabledVcsProviders } from '$lib/stores/git'; + import { + connectVcsProvider, + enabledVcsProviders, + supportsRepositoryCreation + } from '$lib/stores/git'; import RepoCard from './repoCard.svelte'; import { Dependencies } from '$lib/constants'; import { getIconFromRuntime } from '$lib/stores/runtimes'; @@ -40,6 +44,12 @@ let { data }: { data: PageData } = $props(); + const canCreateRepository = $derived( + data.installations.installations.some((entry) => + supportsRepositoryCreation(entry.provider, $regionalConsoleVariables) + ) + ); + const specificationOptions = $derived( (data.specificationsList?.specifications ?? []).map((size) => ({ label: @@ -229,6 +239,12 @@ } } + $effect(() => { + if (!canCreateRepository) { + repositoryBehaviour = 'existing'; + } + }); + $effect(() => { if (repositoryBehaviour === 'new') { selectedInstallationId ??= $installation?.$id; @@ -302,7 +318,9 @@ {#if !!data?.installations?.total}
- + {#if canCreateRepository} + + {/if} {#if repositoryBehaviour === 'new'} - - {#each enabledVcsProviders(($regionalConsoleVariables as { _APP_VCS_PROVIDERS?: string[] })?._APP_VCS_PROVIDERS) as provider (provider.id)} + + {#each enabledVcsProviders($regionalConsoleVariables?._APP_VCS_PROVIDERS) as provider (provider.id)}