feat(web,ui): built-in codeberg.org gitea host and provider detectUrls
This commit is contained in:
@@ -167,26 +167,30 @@ export const GitPage: React.FC<GitPageProps> = (props) => {
|
||||
title={t('settings.page.git.title')}
|
||||
showSaveStatus
|
||||
>
|
||||
<div className="flex h-8 min-w-0">
|
||||
<SortableTabsStrip
|
||||
items={providerTabs}
|
||||
activeId={activeProviderTab}
|
||||
onSelect={(tabId) => setActiveProviderTab(tabId as GitProviderTabId)}
|
||||
layoutMode="fit"
|
||||
variant="active-pill"
|
||||
activePillButtonClassName="h-7"
|
||||
/>
|
||||
</div>
|
||||
<section className="overflow-hidden rounded-lg border border-[var(--interactive-border)] bg-[var(--surface-muted)]">
|
||||
<div className="border-b border-[var(--surface-subtle)] px-3 py-2">
|
||||
<div className="flex h-8 min-w-0">
|
||||
<SortableTabsStrip
|
||||
items={providerTabs}
|
||||
activeId={activeProviderTab}
|
||||
onSelect={(tabId) => setActiveProviderTab(tabId as GitProviderTabId)}
|
||||
layoutMode="fit"
|
||||
variant="active-pill"
|
||||
activePillButtonClassName="h-7"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" aria-label={t('settings.git.tabs.github')} hidden={activeProviderTab !== 'github'}>
|
||||
<GitHubSettings />
|
||||
</div>
|
||||
<div role="tabpanel" aria-label={t('settings.git.tabs.gitlab')} hidden={activeProviderTab !== 'gitlab'}>
|
||||
<GitLabSettings />
|
||||
</div>
|
||||
<div role="tabpanel" aria-label={t('settings.git.tabs.gitea')} hidden={activeProviderTab !== 'gitea'}>
|
||||
<GiteaSettings />
|
||||
</div>
|
||||
<div role="tabpanel" aria-label={t('settings.git.tabs.github')} hidden={activeProviderTab !== 'github'}>
|
||||
<GitHubSettings embedded />
|
||||
</div>
|
||||
<div role="tabpanel" aria-label={t('settings.git.tabs.gitlab')} hidden={activeProviderTab !== 'gitlab'}>
|
||||
<GitLabSettings embedded />
|
||||
</div>
|
||||
<div role="tabpanel" aria-label={t('settings.git.tabs.gitea')} hidden={activeProviderTab !== 'gitea'}>
|
||||
<GiteaSettings embedded />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<SettingsSection
|
||||
title={t('settings.gitIdentities.page.section.title')}
|
||||
|
||||
@@ -36,7 +36,7 @@ type DeviceFlowCompleteResponse =
|
||||
| { connected: true; user: GitHubUser; scope?: string }
|
||||
| { connected: false; status?: string; error?: string };
|
||||
|
||||
export const GitHubSettings: React.FC = () => {
|
||||
export const GitHubSettings: React.FC<{ embedded?: boolean }> = ({ embedded = false }) => {
|
||||
const { t } = useI18n();
|
||||
const { isMobile } = useDeviceInfo();
|
||||
const runtimeGitHub = getRegisteredRuntimeAPIs()?.github;
|
||||
@@ -271,14 +271,8 @@ export const GitHubSettings: React.FC = () => {
|
||||
? t('settings.github.page.accountSource.cli')
|
||||
: t('settings.github.page.accountSource.oauth');
|
||||
|
||||
return (
|
||||
const accountContent = (
|
||||
<>
|
||||
<SettingsSection
|
||||
title={t('settings.github.page.oauth.title')}
|
||||
divider={false}
|
||||
settingsItem="git.github-account"
|
||||
info={t('settings.github.page.tooltip.connectAccount')}
|
||||
>
|
||||
<div className="rounded-lg bg-[var(--surface-elevated)]/70 overflow-hidden flex flex-col">
|
||||
{connected ? (
|
||||
<div className={cn("px-4 py-3", isMobile ? "flex flex-col gap-3" : "flex items-center justify-between gap-4")}>
|
||||
@@ -447,65 +441,95 @@ export const GitHubSettings: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-4 pt-4">
|
||||
<div className={cn('flex flex-col gap-4', embedded ? 'border-t border-[var(--surface-subtle)] pt-4' : 'pt-4')}>
|
||||
<ProviderApiBaseUrlInput provider="github" />
|
||||
<ProviderDetectUrlsInput provider="github" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
</SettingsSection>
|
||||
|
||||
{ghCli?.available && !ghCli?.active && (!ghCli.user || ghCli.disabled) && (
|
||||
<SettingsSection title={t('settings.github.page.ghCli.title')}>
|
||||
<div className="rounded-lg bg-[var(--surface-elevated)]/70 overflow-hidden">
|
||||
<div className={cn("px-4 py-3", isMobile ? "flex flex-col gap-3" : "flex items-center justify-between gap-4")}>
|
||||
<div className={cn("flex min-w-0 items-center gap-4", isMobile ? "w-full" : undefined)}>
|
||||
{ghCli.user?.avatarUrl ? (
|
||||
<img
|
||||
src={ghCli.user.avatarUrl}
|
||||
alt={ghCli.user.login ? t('settings.github.page.avatarAlt.withLogin', { login: ghCli.user.login }) : t('settings.github.page.avatarAlt.fallback')}
|
||||
className="h-10 w-10 shrink-0 rounded-full border border-[var(--interactive-border)] bg-[var(--surface-muted)] object-cover"
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-[var(--interactive-border)] bg-[var(--surface-muted)]">
|
||||
<Icon name="github-fill" className="h-4 w-4 text-muted-foreground" />
|
||||
</div>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
{!ghCli.disabled && ghCli.user && (
|
||||
<div className="typography-ui-label text-foreground truncate">
|
||||
{ghCli.user.name?.trim() || ghCli.user.login || 'GitHub'}
|
||||
</div>
|
||||
)}
|
||||
{!ghCli.disabled && ghCli.user?.login && (
|
||||
<div className={cn("flex items-center gap-2 typography-meta text-muted-foreground mt-0.5", isMobile ? "flex-wrap" : "truncate")}>
|
||||
<Icon name="github-fill" className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="font-mono">{ghCli.user.login}</span>
|
||||
{ghCli.user.email && <span className="opacity-50">•</span>}
|
||||
{ghCli.user.email && <span>{ghCli.user.email}</span>}
|
||||
</div>
|
||||
)}
|
||||
<div className={cn("typography-meta text-muted-foreground", ghCli.disabled ? "opacity-60" : undefined)}>
|
||||
{ghCli.disabled
|
||||
? t('settings.github.page.ghCli.disabledDescription')
|
||||
: t('settings.github.page.ghCli.fallbackDescription')}
|
||||
</div>
|
||||
</div>
|
||||
const ghCliContent = ghCli?.available && !ghCli?.active && (!ghCli.user || ghCli.disabled) ? (
|
||||
<div className="rounded-lg bg-[var(--surface-elevated)]/70 overflow-hidden">
|
||||
<div className={cn("px-4 py-3", isMobile ? "flex flex-col gap-3" : "flex items-center justify-between gap-4")}>
|
||||
<div className={cn("flex min-w-0 items-center gap-4", isMobile ? "w-full" : undefined)}>
|
||||
{ghCli.user?.avatarUrl ? (
|
||||
<img
|
||||
src={ghCli.user.avatarUrl}
|
||||
alt={ghCli.user.login ? t('settings.github.page.avatarAlt.withLogin', { login: ghCli.user.login }) : t('settings.github.page.avatarAlt.fallback')}
|
||||
className="h-10 w-10 shrink-0 rounded-full border border-[var(--interactive-border)] bg-[var(--surface-muted)] object-cover"
|
||||
loading="lazy"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-[var(--interactive-border)] bg-[var(--surface-muted)]">
|
||||
<Icon name="github-fill" className="h-4 w-4 text-muted-foreground" />
|
||||
</div>
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
{!ghCli.disabled && ghCli.user && (
|
||||
<div className="typography-ui-label text-foreground truncate">
|
||||
{ghCli.user.name?.trim() || ghCli.user.login || 'GitHub'}
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => toggleGhCli(!ghCli.disabled)}
|
||||
disabled={isBusy}
|
||||
className={cn(isMobile ? "w-full" : undefined)}
|
||||
>
|
||||
{ghCli.disabled
|
||||
? t('settings.github.page.ghCli.actions.enable')
|
||||
: t('settings.github.page.ghCli.actions.disable')}
|
||||
</Button>
|
||||
)}
|
||||
{!ghCli.disabled && ghCli.user?.login && (
|
||||
<div className={cn("flex items-center gap-2 typography-meta text-muted-foreground mt-0.5", isMobile ? "flex-wrap" : "truncate")}>
|
||||
<Icon name="github-fill" className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="font-mono">{ghCli.user.login}</span>
|
||||
{ghCli.user.email && <span className="opacity-50">•</span>}
|
||||
{ghCli.user.email && <span>{ghCli.user.email}</span>}
|
||||
</div>
|
||||
)}
|
||||
<div className={cn("typography-meta text-muted-foreground", ghCli.disabled ? "opacity-60" : undefined)}>
|
||||
{ghCli.disabled
|
||||
? t('settings.github.page.ghCli.disabledDescription')
|
||||
: t('settings.github.page.ghCli.fallbackDescription')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => toggleGhCli(!ghCli.disabled)}
|
||||
disabled={isBusy}
|
||||
className={cn(isMobile ? "w-full" : undefined)}
|
||||
>
|
||||
{ghCli.disabled
|
||||
? t('settings.github.page.ghCli.actions.enable')
|
||||
: t('settings.github.page.ghCli.actions.disable')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
if (embedded) {
|
||||
return (
|
||||
<>
|
||||
<div data-settings-item="git.github-account" className="p-4">
|
||||
{accountContent}
|
||||
</div>
|
||||
{ghCliContent && (
|
||||
<div className="border-t border-[var(--surface-subtle)] px-4 py-4">
|
||||
{ghCliContent}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsSection
|
||||
title={t('settings.github.page.oauth.title')}
|
||||
divider={false}
|
||||
settingsItem="git.github-account"
|
||||
info={t('settings.github.page.tooltip.connectAccount')}
|
||||
>
|
||||
{accountContent}
|
||||
</SettingsSection>
|
||||
|
||||
{ghCliContent && (
|
||||
<SettingsSection title={t('settings.github.page.ghCli.title')}>
|
||||
{ghCliContent}
|
||||
</SettingsSection>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -24,7 +24,7 @@ const getBaseUrlHost = (baseUrl?: string | null): string => {
|
||||
}
|
||||
};
|
||||
|
||||
export const GitLabSettings: React.FC = () => {
|
||||
export const GitLabSettings: React.FC<{ embedded?: boolean }> = ({ embedded = false }) => {
|
||||
const { t } = useI18n();
|
||||
const { isMobile } = useDeviceInfo();
|
||||
const runtimeGitLab = getRegisteredRuntimeAPIs()?.gitlab;
|
||||
@@ -154,13 +154,8 @@ export const GitLabSettings: React.FC = () => {
|
||||
const currentAccount = accounts.find((account) => account.current) ?? (accounts.length > 0 ? accounts[0] : null);
|
||||
const currentBaseUrlHost = getBaseUrlHost(currentAccount?.baseUrl ?? status?.defaultBaseUrl);
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
title={t('settings.gitlab.page.title')}
|
||||
description={t('settings.gitlab.page.description')}
|
||||
info={t('settings.gitlab.page.tooltip.connectAccount')}
|
||||
settingsItem="git.gitlab-account"
|
||||
>
|
||||
const sectionContent = (
|
||||
<>
|
||||
<div className="rounded-lg bg-[var(--surface-elevated)]/70 overflow-hidden flex flex-col">
|
||||
{connected ? (
|
||||
<div className={cn('px-4 py-3', isMobile ? 'flex flex-col gap-3' : 'flex items-center justify-between gap-4')}>
|
||||
@@ -296,10 +291,29 @@ export const GitLabSettings: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4 pt-4">
|
||||
<div className={cn('flex flex-col gap-4', embedded ? 'border-t border-[var(--surface-subtle)] pt-4' : 'pt-4')}>
|
||||
<ProviderApiBaseUrlInput provider="gitlab" />
|
||||
<ProviderDetectUrlsInput provider="gitlab" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
if (embedded) {
|
||||
return (
|
||||
<div data-settings-item="git.gitlab-account" className="p-4">
|
||||
{sectionContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
title={t('settings.gitlab.page.title')}
|
||||
description={t('settings.gitlab.page.description')}
|
||||
info={t('settings.gitlab.page.tooltip.connectAccount')}
|
||||
settingsItem="git.gitlab-account"
|
||||
>
|
||||
{sectionContent}
|
||||
</SettingsSection>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ const getBaseUrlHost = (baseUrl?: string | null): string => {
|
||||
}
|
||||
};
|
||||
|
||||
export const GiteaSettings: React.FC = () => {
|
||||
export const GiteaSettings: React.FC<{ embedded?: boolean }> = ({ embedded = false }) => {
|
||||
const { t } = useI18n();
|
||||
const { isMobile } = useDeviceInfo();
|
||||
const runtimeGitea = getRegisteredRuntimeAPIs()?.gitea;
|
||||
@@ -159,13 +159,8 @@ export const GiteaSettings: React.FC = () => {
|
||||
const currentAccount = accounts.find((account) => account.current) ?? (accounts.length > 0 ? accounts[0] : null);
|
||||
const currentBaseUrlHost = getBaseUrlHost(currentAccount?.baseUrl);
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
title={t('settings.gitea.page.title')}
|
||||
description={t('settings.gitea.page.description')}
|
||||
info={t('settings.gitea.page.tooltip.connectAccount')}
|
||||
settingsItem="git.gitea-account"
|
||||
>
|
||||
const sectionContent = (
|
||||
<>
|
||||
<div className="rounded-lg bg-[var(--surface-elevated)]/70 overflow-hidden flex flex-col">
|
||||
{connected ? (
|
||||
<div className={cn('px-4 py-3', isMobile ? 'flex flex-col gap-3' : 'flex items-center justify-between gap-4')}>
|
||||
@@ -306,10 +301,29 @@ export const GiteaSettings: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4 pt-4">
|
||||
<div className={cn('flex flex-col gap-4', embedded ? 'border-t border-[var(--surface-subtle)] pt-4' : 'pt-4')}>
|
||||
<ProviderApiBaseUrlInput provider="gitea" />
|
||||
<ProviderDetectUrlsInput provider="gitea" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
if (embedded) {
|
||||
return (
|
||||
<div data-settings-item="git.gitea-account" className="p-4">
|
||||
{sectionContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
title={t('settings.gitea.page.title')}
|
||||
description={t('settings.gitea.page.description')}
|
||||
info={t('settings.gitea.page.tooltip.connectAccount')}
|
||||
settingsItem="git.gitea-account"
|
||||
>
|
||||
{sectionContent}
|
||||
</SettingsSection>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user