Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
},
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@6be9e62",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@56957ff",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bfe7ce3",
"@appwrite.io/pink-legacy": "^1.0.3",
Expand Down
13 changes: 13 additions & 0 deletions src/lib/components/git/IconOrigin.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!--
Origin (Cursor's git hosting) has no icon in @appwrite.io/pink-icons-svelte
yet, so this is kept local to the git-connect UI until a proper brand icon
lands in the shared icon package. The Cursor cube mark, taken from
cursor.com's own vector assets; fill is currentcolor to match the
convention of the other Icon* components (e.g. IconGithub) so it drops
into <Icon icon={IconOrigin} /> unchanged.
-->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="63 63 386 386">
<path
fill="currentcolor"
d="m410.344 159.545-146.38-84.5111c-4.7-2.7145-10.5-2.7145-15.2 0l-146.373 84.5111c-3.9515 2.282-6.391 6.501-6.391 11.071v170.418c0 4.569 2.4395 8.789 6.391 11.07l146.379 84.512c4.701 2.714 10.501 2.714 15.201 0l146.38-84.512c3.951-2.281 6.391-6.501 6.391-11.07v-170.418c0-4.57-2.44-8.789-6.391-11.071zm-9.195 17.902-141.308 244.751c-.955 1.65-3.477.976-3.477-.934v-160.261c0-3.203-1.711-6.164-4.487-7.772l-138.786-80.127c-1.65-.956-.976-3.478.934-3.478h282.616c4.013 0 6.522 4.35 4.515 7.828h-.007z" />
</svg>
29 changes: 8 additions & 21 deletions src/lib/components/git/connectGit.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
<script lang="ts">
import { isSelfHosted } from '$lib/system';
import { connectGitHub, connectGitea } from '$lib/stores/git';
import { connectVcsProvider, enabledVcsProviders } from '$lib/stores/git';
import Button from '$lib/elements/forms/button.svelte';
import { IconGithub } from '@appwrite.io/pink-icons-svelte';
import { Alert, Card, Empty, Icon, Layout } from '@appwrite.io/pink-svelte';
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
import IconGitea from './IconGitea.svelte';

export let callbackState: Record<string, string> = 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;
// Gitea is a self-hosted-only feature, not offered on Appwrite Cloud.
let isGiteaEnabled = isSelfHosted && (vcsProviders?.includes('gitea') ?? false);
let providers = enabledVcsProviders($regionalConsoleVariables?._APP_VCS_PROVIDERS);
</script>

<Layout.Stack>
Expand All @@ -41,23 +35,16 @@
title="No installation was added to the project yet"
description="Add an installation to connect repositories">
<svelte:fragment slot="actions">
<Layout.Stack direction="row">
<Button
secondary
href={connectGitHub(callbackState).toString()}
disabled={!isVcsEnabled}>
<Icon slot="start" icon={IconGithub} />
Connect to GitHub
</Button>
{#if isGiteaEnabled}
<Layout.Stack direction="row" justifyContent="center">
{#each providers as provider (provider.id)}
<Button
secondary
href={connectGitea(callbackState).toString()}
href={connectVcsProvider(provider.id, callbackState).toString()}
disabled={!isVcsEnabled}>
<Icon slot="start" icon={IconGitea} />
Connect to Gitea
<Icon slot="start" icon={provider.icon} />
Connect to {provider.label}
</Button>
{/if}
{/each}
</Layout.Stack>
</svelte:fragment>
</Empty>
Expand Down
37 changes: 29 additions & 8 deletions src/lib/components/git/connectRepoModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import { Click, trackEvent } from '$lib/actions/analytics';
import RepositoryBehaviour from '$lib/components/git/repositoryBehaviour.svelte';
import { page } from '$app/state';
import { connectGitHub } from '$lib/stores/git';

import { connectVcsProvider, supportsRepositoryCreation } from '$lib/stores/git';
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';

let {
show = $bindable(false),
Expand All @@ -37,6 +39,11 @@
let selectedInstallationId = $state('');
let selectedRepository = $state('');
let installations = $state({ installations: [], total: 0 });
const canCreateRepository = $derived(
installations.installations.some((entry) =>
supportsRepositoryCreation(entry.provider, $regionalConsoleVariables)
)
);
let error = $state('');

onMount(async () => {
Expand Down Expand Up @@ -96,7 +103,7 @@
</span>
{#if !!installations?.total}
<Layout.Stack gap="xl">
{#if !onlyExisting}
{#if !onlyExisting && canCreateRepository}
<RepositoryBehaviour bind:repositoryBehaviour />
{/if}
{#if repositoryBehaviour === 'new'}
Expand Down Expand Up @@ -143,12 +150,26 @@
<svelte:fragment slot="footer">
{#if repositoryBehaviour === 'existing'}
<Layout.Stack>
<Link variant="quiet" href={connectGitHub(callbackState).toString()}>
<Layout.Stack direction="row" gap="xs">
Missing a repository? check your permissions <Icon
icon={IconArrowSmRight} />
</Layout.Stack>
</Link>
{#if $installation?.provider === 'origin'}
<Link variant="quiet" external href="https://cursor.com/codebase/settings/apps">
<Layout.Stack direction="row" gap="xs">
Missing a repository? check your repository access <Icon
icon={IconArrowSmRight} />
</Layout.Stack>
</Link>
{:else}
<Link
variant="quiet"
href={connectVcsProvider(
$installation?.provider ?? 'github',
callbackState
).toString()}>
<Layout.Stack direction="row" gap="xs">
Missing a repository? check your permissions <Icon
icon={IconArrowSmRight} />
</Layout.Stack>
</Link>
{/if}
</Layout.Stack>
{:else if repositoryBehaviour === 'new'}
<Button text size="s" on:click={() => (show = false)}>Cancel</Button>
Expand Down
21 changes: 17 additions & 4 deletions src/lib/components/git/deploymentSource.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
Typography
} from '@appwrite.io/pink-svelte';
import Button from '$lib/elements/forms/button.svelte';
import IconOrigin from './IconOrigin.svelte';

let {
deployment,
Expand All @@ -35,6 +36,12 @@

let repository = $state<Models.ProviderRepository | null>(null);

// The deployment's server-supplied URLs carry the provider; the model has
// no provider field of its own.
const isOrigin = $derived(
(deployment?.providerRepositoryUrl ?? '').toLowerCase().includes('cursor.com')
);

async function loadRepository() {
if (!resource?.installationId || !resource?.providerRepositoryId || !region || !project) {
return;
Expand Down Expand Up @@ -66,7 +73,11 @@
toggle(e);
}}>
<Layout.Stack direction="row" gap="xs" alignItems="center">
<Icon icon={IconGithub} size="s" /> GitHub
{#if isOrigin}
<Icon icon={IconOrigin} size="s" /> Origin
{:else}
<Icon icon={IconGithub} size="s" /> GitHub
{/if}
</Layout.Stack>
</Link>
{#if repository?.authorized === false}
Expand All @@ -82,8 +93,10 @@
To enable, add the repository to the installation settings on <Link
variant="muted"
external
href={`https://github.com/settings/installations/${repository.providerInstallationId}`}>
GitHub
href={isOrigin
? 'https://cursor.com/codebase/settings/apps'
: `https://github.com/settings/installations/${repository.providerInstallationId}`}>
{isOrigin ? 'Origin' : 'GitHub'}
</Link>.
</Typography.Text>
</svelte:fragment>
Expand All @@ -97,7 +110,7 @@
<ActionMenu.Item.Anchor
href={deployment.providerRepositoryUrl}
external
leadingIcon={IconGithub}>
leadingIcon={isOrigin ? IconOrigin : IconGithub}>
{deployment.providerRepositoryOwner}/{deployment.providerRepositoryName}
</ActionMenu.Item.Anchor>
<ActionMenu.Item.Anchor
Expand Down
67 changes: 55 additions & 12 deletions src/lib/components/git/newRepository.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,55 @@
<script lang="ts">
import { InputCheckbox, InputSelect, InputText } from '$lib/elements/forms';
import { installation } from '$lib/stores/vcs';
import { supportsPublicRepositories, supportsRepositoryCreation } from '$lib/stores/git';
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
import type { Models } from '@appwrite.io/console';
import { Layout } from '@appwrite.io/pink-svelte';

export let selectedInstallationId: string;
export let installations: Models.InstallationList;
export let repositoryName: string;
export let repositoryPrivate = true;
export let disableFields = false;
let {
selectedInstallationId = $bindable(),
installations,
repositoryName = $bindable(),
repositoryPrivate = $bindable(true),
disableFields = false
}: {
selectedInstallationId: string;
installations: Models.InstallationList;
repositoryName: string;
repositoryPrivate?: boolean;
disableFields?: boolean;
} = $props();

// Only offer organizations whose provider can create repositories.
const creatableInstallations = $derived(
installations.installations.filter((entry) =>
supportsRepositoryCreation(entry.provider, $regionalConsoleVariables)
)
);

const supportsPublic = $derived(
supportsPublicRepositories(
creatableInstallations.find((entry) => entry.$id === selectedInstallationId)?.provider,
$regionalConsoleVariables
)
);

$effect(() => {
if (
creatableInstallations.length &&
!creatableInstallations.some((entry) => entry.$id === selectedInstallationId)
) {
selectedInstallationId = creatableInstallations[0].$id;
$installation = creatableInstallations[0];
}
});

$effect(() => {
// Without public repository support every repository is private.
if (!supportsPublic) {
repositoryPrivate = true;
}
});
</script>

<Layout.Stack gap="xl">
Expand All @@ -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
);
}}
Expand All @@ -38,9 +79,11 @@
disabled={disableFields}
bind:value={repositoryName} />
</Layout.Stack>
<InputCheckbox
id="repositoryPrivate"
label="Keep repository private"
disabled={disableFields}
bind:checked={repositoryPrivate} />
{#if supportsPublic}
<InputCheckbox
id="repositoryPrivate"
label="Keep repository private"
disabled={disableFields}
bind:checked={repositoryPrivate} />
{/if}
</Layout.Stack>
Loading