feat(ui): attach GitLab issues and merge requests to sessions
This commit is contained in:
@@ -61,6 +61,9 @@ import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel';
|
||||
import { useThemeSystem } from '@/contexts/useThemeSystem';
|
||||
import { GitHubIssuePickerDialog } from '@/components/session/GitHubIssuePickerDialog';
|
||||
import { GitHubPrPickerDialog } from '@/components/session/GitHubPrPickerDialog';
|
||||
import { GitLabIssuePickerDialog } from '@/components/session/GitLabIssuePickerDialog';
|
||||
import { GitLabMrPickerDialog } from '@/components/session/GitLabMrPickerDialog';
|
||||
import { useGitProvider } from '@/lib/gitProvider';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { DraftPresetChips } from './DraftPresetChips';
|
||||
import { useChatSearchDirectory } from '@/hooks/useChatSearchDirectory';
|
||||
@@ -376,6 +379,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
||||
const { currentTheme } = useThemeSystem();
|
||||
const chatSearchDirectory = useChatSearchDirectory();
|
||||
const isGitRepo = useIsGitRepo(currentDirectory);
|
||||
const gitProvider = useGitProvider(currentDirectory);
|
||||
const currentGitStatus = useGitStore((state) =>
|
||||
currentDirectory ? state.directories.get(currentDirectory)?.status ?? null : null,
|
||||
);
|
||||
@@ -663,6 +667,8 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
||||
// Issue linking state
|
||||
const [issuePickerOpen, setIssuePickerOpen] = React.useState(false);
|
||||
const [prPickerOpen, setPrPickerOpen] = React.useState(false);
|
||||
const [gitlabIssuePickerOpen, setGitlabIssuePickerOpen] = React.useState(false);
|
||||
const [gitlabMrPickerOpen, setGitlabMrPickerOpen] = React.useState(false);
|
||||
const [linkedIssue, setLinkedIssue] = React.useState<{
|
||||
number: number;
|
||||
title: string;
|
||||
@@ -945,12 +951,20 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
||||
}, [isExpandedInput, setExpandedInput]);
|
||||
|
||||
const openIssuePicker = React.useCallback(() => {
|
||||
setIssuePickerOpen(true);
|
||||
}, []);
|
||||
if (gitProvider === 'gitlab') {
|
||||
setGitlabIssuePickerOpen(true);
|
||||
} else {
|
||||
setIssuePickerOpen(true);
|
||||
}
|
||||
}, [gitProvider]);
|
||||
|
||||
const openPrPicker = React.useCallback(() => {
|
||||
setPrPickerOpen(true);
|
||||
}, []);
|
||||
if (gitProvider === 'gitlab') {
|
||||
setGitlabMrPickerOpen(true);
|
||||
} else {
|
||||
setPrPickerOpen(true);
|
||||
}
|
||||
}, [gitProvider]);
|
||||
|
||||
const getSubmitErrorMessage = (error: unknown, fallback: string) => {
|
||||
const message = error instanceof Error ? error.message : '';
|
||||
@@ -2545,7 +2559,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
||||
author={linkedIssue.author}
|
||||
openInBrowserLabel={t('chat.chatInput.linked.issue.openInBrowserAria')}
|
||||
removeLabel={t('chat.chatInput.linked.issue.removeAria')}
|
||||
onReopenPicker={() => setIssuePickerOpen(true)}
|
||||
onReopenPicker={openIssuePicker}
|
||||
onRemove={() => setLinkedIssue(null)}
|
||||
/>
|
||||
) : null}
|
||||
@@ -2558,7 +2572,7 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
||||
branches={{ head: linkedPr.head, base: linkedPr.base }}
|
||||
openInBrowserLabel={t('chat.chatInput.linked.pr.openInBrowserAria')}
|
||||
removeLabel={t('chat.chatInput.linked.pr.removeAria')}
|
||||
onReopenPicker={() => setPrPickerOpen(true)}
|
||||
onReopenPicker={openPrPicker}
|
||||
onRemove={() => setLinkedPr(null)}
|
||||
/>
|
||||
) : null}
|
||||
@@ -2878,6 +2892,23 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
||||
setLinkedIssue(null);
|
||||
}}
|
||||
/>
|
||||
<GitLabIssuePickerDialog
|
||||
open={gitlabIssuePickerOpen}
|
||||
onOpenChange={setGitlabIssuePickerOpen}
|
||||
mode="select"
|
||||
onSelect={(issue) => {
|
||||
setLinkedIssue(issue);
|
||||
setLinkedPr(null);
|
||||
}}
|
||||
/>
|
||||
<GitLabMrPickerDialog
|
||||
open={gitlabMrPickerOpen}
|
||||
onOpenChange={setGitlabMrPickerOpen}
|
||||
onSelect={(pr) => {
|
||||
setLinkedPr(pr);
|
||||
setLinkedIssue(null);
|
||||
}}
|
||||
/>
|
||||
<ReviewFlowDialog
|
||||
open={reviewDialogOpen}
|
||||
onOpenChange={setReviewDialogOpen}
|
||||
@@ -2944,8 +2975,8 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
||||
requestAnimationFrame(openIssuePicker);
|
||||
}}
|
||||
>
|
||||
<Icon name="github" className="h-[18px] w-[18px] flex-shrink-0 text-muted-foreground" />
|
||||
{t('chat.chatInput.actions.linkGithubIssue')}
|
||||
<Icon name={gitProvider === 'gitlab' ? 'git-branch' : 'github'} className="h-[18px] w-[18px] flex-shrink-0 text-muted-foreground" />
|
||||
{gitProvider === 'gitlab' ? t('chat.chatInput.actions.linkGitlabIssue') : t('chat.chatInput.actions.linkGithubIssue')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -2956,8 +2987,8 @@ const ChatInputComponent: React.FC<ChatInputProps> = ({ onOpenSettings, scrollTo
|
||||
requestAnimationFrame(openPrPicker);
|
||||
}}
|
||||
>
|
||||
<Icon name="git-pull-request" className="h-[18px] w-[18px] flex-shrink-0 text-muted-foreground" />
|
||||
{t('chat.chatInput.actions.linkGithubPr')}
|
||||
<Icon name={gitProvider === 'gitlab' ? 'git-merge' : 'git-pull-request'} className="h-[18px] w-[18px] flex-shrink-0 text-muted-foreground" />
|
||||
{gitProvider === 'gitlab' ? t('chat.chatInput.actions.linkGitlabMr') : t('chat.chatInput.actions.linkGithubPr')}
|
||||
</button>
|
||||
</div>
|
||||
</MobileOverlayPanel>
|
||||
|
||||
@@ -27,7 +27,7 @@ type ComposerAttachmentControlsProps = {
|
||||
handlePickLocalFiles: () => void;
|
||||
openIssuePicker: () => void;
|
||||
openPrPicker: () => void;
|
||||
/** Only shows the GitHub issue/PR attach actions when the repo is GitHub. */
|
||||
/** Shows the GitHub issue/PR or GitLab issue/MR attach actions based on the repo provider. */
|
||||
gitProvider?: GitProvider | null;
|
||||
onOpenSettings?: () => void;
|
||||
onMenuOpenChange?: (open: boolean) => void;
|
||||
@@ -121,6 +121,25 @@ export const ComposerAttachmentControls = React.memo(function ComposerAttachment
|
||||
{t('chat.chatInput.actions.linkGithubPr')}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
) : gitProvider === 'gitlab' ? (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
requestAnimationFrame(openIssuePicker);
|
||||
}}
|
||||
>
|
||||
<Icon name="git-branch"/>
|
||||
{t('chat.chatInput.actions.linkGitlabIssue')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
requestAnimationFrame(openPrPicker);
|
||||
}}
|
||||
>
|
||||
<Icon name="git-merge"/>
|
||||
{t('chat.chatInput.actions.linkGitlabMr')}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
) : null}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@@ -0,0 +1,733 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
import { useSessionUIStore } from '@/sync/session-ui-store';
|
||||
import { useSelectionStore } from '@/sync/selection-store';
|
||||
import * as sessionActions from '@/sync/session-actions';
|
||||
import { buildLinkedIssue } from '@/lib/linkedIssues';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useGitLabAuthStore } from '@/stores/useGitLabAuthStore';
|
||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||
import { parseModelIdentifier } from '@/lib/modelIdentifier';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { createWorktreeSessionForNewBranch } from '@/lib/worktreeSessionCreator';
|
||||
import { generateBranchSlug } from '@/lib/git/branchNameGenerator';
|
||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
|
||||
import type { GitLabIssue, GitLabIssueComment, GitLabIssuesListResult, GitLabIssueSummary } from '@/lib/api/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
const parseIssueNumber = (value: string): number | null => {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
const urlMatch = trimmed.match(/\/issues\/(\d+)(?:\b|\/|$)/i);
|
||||
if (urlMatch) {
|
||||
const parsed = Number(urlMatch[1]);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
const hashMatch = trimmed.match(/^#?(\d+)$/);
|
||||
if (hashMatch) {
|
||||
const parsed = Number(hashMatch[1]);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const buildIssueContextText = (args: {
|
||||
repo: GitLabIssuesListResult['repo'] | undefined;
|
||||
issue: GitLabIssue;
|
||||
comments: GitLabIssueComment[];
|
||||
}) => {
|
||||
const payload = {
|
||||
repo: args.repo ?? null,
|
||||
issue: args.issue,
|
||||
comments: args.comments,
|
||||
};
|
||||
return `GitLab issue context (JSON)\n${JSON.stringify(payload, null, 2)}`;
|
||||
};
|
||||
|
||||
export function GitLabIssuePickerDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
mode = 'createSession',
|
||||
onSelect,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
mode?: 'createSession' | 'select';
|
||||
onSelect?: (issue: { number: number; title: string; url: string; contextText: string; author?: { login: string; avatarUrl?: string } }) => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const { gitlab } = useRuntimeAPIs();
|
||||
const gitlabAuthStatus = useGitLabAuthStore((state) => state.status);
|
||||
const gitlabAuthChecked = useGitLabAuthStore((state) => state.hasChecked);
|
||||
const setSettingsDialogOpen = useUIStore((state) => state.setSettingsDialogOpen);
|
||||
const setSettingsPage = useUIStore((state) => state.setSettingsPage);
|
||||
const isMobile = useUIStore((state) => state.isMobile);
|
||||
const { isTablet } = useDeviceInfo();
|
||||
const alwaysShowActions = isMobile || isTablet;
|
||||
const activeProject = useProjectsStore((state) => state.getActiveProject());
|
||||
const currentDirectory = useDirectoryStore((state) => state.currentDirectory);
|
||||
|
||||
const projectDirectory = React.useMemo(() => {
|
||||
return activeProject?.path?.trim() || currentDirectory?.trim() || null;
|
||||
}, [activeProject?.path, currentDirectory]);
|
||||
|
||||
const [query, setQuery] = React.useState('');
|
||||
const [createInWorktree, setCreateInWorktree] = React.useState(false);
|
||||
const [result, setResult] = React.useState<GitLabIssuesListResult | null>(null);
|
||||
const [issues, setIssues] = React.useState<GitLabIssueSummary[]>([]);
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [hasMore, setHasMore] = React.useState(false);
|
||||
const [startingIssueNumber, setStartingIssueNumber] = React.useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [isLoadingMore, setIsLoadingMore] = React.useState(false);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
const directNumber = React.useMemo(() => parseIssueNumber(query), [query]);
|
||||
const debouncedQuery = useDebouncedValue(query, 350);
|
||||
const isTextSearch = debouncedQuery.trim().length > 0 && !directNumber;
|
||||
|
||||
const refresh = React.useCallback(async () => {
|
||||
if (!projectDirectory) {
|
||||
setResult(null);
|
||||
setError(t('session.gitlabIssuePicker.error.noActiveProject'));
|
||||
return;
|
||||
}
|
||||
if (gitlabAuthChecked && gitlabAuthStatus?.connected === false) {
|
||||
setResult({ connected: false, issues: [], page: 1, hasMore: false });
|
||||
setIssues([]);
|
||||
setHasMore(false);
|
||||
setPage(1);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
if (!gitlab?.issuesList) {
|
||||
setResult(null);
|
||||
setError(t('session.gitlabIssuePicker.error.runtimeUnavailable'));
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const next = await gitlab.issuesList(projectDirectory, { page: 1 });
|
||||
setResult(next);
|
||||
setIssues(next.issues ?? []);
|
||||
setPage(next.page ?? 1);
|
||||
setHasMore(Boolean(next.hasMore));
|
||||
if (next.connected === false) {
|
||||
setError(null);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [gitlab, gitlabAuthChecked, gitlabAuthStatus, projectDirectory, t]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open || !projectDirectory) return;
|
||||
if (gitlabAuthChecked && gitlabAuthStatus?.connected === false) return;
|
||||
if (!gitlab?.issuesList) return;
|
||||
if (!debouncedQuery.trim() || directNumber) {
|
||||
void refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
gitlab.issuesList(projectDirectory, { page: 1, query: debouncedQuery.trim() })
|
||||
.then((next) => {
|
||||
if (controller.signal.aborted) return;
|
||||
setResult(next);
|
||||
setIssues(next.issues ?? []);
|
||||
setPage(next.page ?? 1);
|
||||
setHasMore(Boolean(next.hasMore));
|
||||
})
|
||||
.catch((e) => {
|
||||
if (controller.signal.aborted) return;
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
})
|
||||
.finally(() => {
|
||||
if (!controller.signal.aborted) setIsLoading(false);
|
||||
});
|
||||
|
||||
return () => controller.abort();
|
||||
}, [open, projectDirectory, gitlab, gitlabAuthChecked, gitlabAuthStatus, debouncedQuery, directNumber, refresh, t]);
|
||||
|
||||
const loadMore = React.useCallback(async () => {
|
||||
if (!projectDirectory) return;
|
||||
if (!gitlab?.issuesList) return;
|
||||
if (isLoadingMore || isLoading) return;
|
||||
if (!hasMore) return;
|
||||
|
||||
setIsLoadingMore(true);
|
||||
try {
|
||||
const nextPage = page + 1;
|
||||
const next = isTextSearch
|
||||
? await gitlab.issuesList(projectDirectory, { page: nextPage, query: debouncedQuery.trim() })
|
||||
: await gitlab.issuesList(projectDirectory, { page: nextPage });
|
||||
setResult(next);
|
||||
setIssues((prev) => [...prev, ...(next.issues ?? [])]);
|
||||
setPage(next.page ?? nextPage);
|
||||
setHasMore(Boolean(next.hasMore));
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
toast.error(t('session.gitlabIssuePicker.toast.loadMoreFailed'), { description: message });
|
||||
} finally {
|
||||
setIsLoadingMore(false);
|
||||
}
|
||||
}, [gitlab, hasMore, isLoading, isLoadingMore, isTextSearch, debouncedQuery, page, projectDirectory, t]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) {
|
||||
setQuery('');
|
||||
setCreateInWorktree(false);
|
||||
setStartingIssueNumber(null);
|
||||
setError(null);
|
||||
setResult(null);
|
||||
setIssues([]);
|
||||
setPage(1);
|
||||
setHasMore(false);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
void refresh();
|
||||
}, [open, refresh]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) return;
|
||||
if (gitlabAuthChecked && gitlabAuthStatus?.connected === false) {
|
||||
setResult({ connected: false, issues: [], page: 1, hasMore: false });
|
||||
setIssues([]);
|
||||
setHasMore(false);
|
||||
setPage(1);
|
||||
setError(null);
|
||||
}
|
||||
}, [gitlabAuthChecked, gitlabAuthStatus, open]);
|
||||
|
||||
const connected = gitlabAuthChecked ? result?.connected !== false : true;
|
||||
const repoUrl = result?.repo?.url ?? null;
|
||||
|
||||
const openGitLabSettings = React.useCallback(() => {
|
||||
setSettingsPage('git');
|
||||
setSettingsDialogOpen(true);
|
||||
}, [setSettingsDialogOpen, setSettingsPage]);
|
||||
|
||||
const resolveDefaultAgentName = React.useCallback((): string | undefined => {
|
||||
const configState = useConfigStore.getState();
|
||||
const visibleAgents = configState.getVisibleAgents();
|
||||
|
||||
if (configState.settingsDefaultAgent) {
|
||||
const settingsAgent = visibleAgents.find((a) => a.name === configState.settingsDefaultAgent);
|
||||
if (settingsAgent) {
|
||||
return settingsAgent.name;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
visibleAgents.find((agent) => agent.name === 'build')?.name ||
|
||||
visibleAgents[0]?.name
|
||||
);
|
||||
}, []);
|
||||
|
||||
const resolveDefaultModelSelection = React.useCallback((): { providerID: string; modelID: string } | null => {
|
||||
const configState = useConfigStore.getState();
|
||||
const settingsDefaultModel = configState.settingsDefaultModel;
|
||||
if (!settingsDefaultModel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsed = parseModelIdentifier(settingsDefaultModel);
|
||||
if (!parsed) {
|
||||
return null;
|
||||
}
|
||||
const { providerId: providerID, modelId: modelID } = parsed;
|
||||
|
||||
const modelMetadata = configState.getModelMetadata(providerID, modelID);
|
||||
if (!modelMetadata) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { providerID, modelID };
|
||||
}, []);
|
||||
|
||||
const resolveDefaultVariant = React.useCallback((providerID: string, modelID: string): string | undefined => {
|
||||
const configState = useConfigStore.getState();
|
||||
const settingsDefaultVariant = configState.settingsDefaultVariant;
|
||||
const currentVariant = configState.currentProviderId === providerID && configState.currentModelId === modelID
|
||||
? configState.currentVariant
|
||||
: undefined;
|
||||
|
||||
const provider = configState.providers.find((p) => p.id === providerID);
|
||||
const model = provider?.models.find((m: Record<string, unknown>) => (m as { id?: string }).id === modelID) as
|
||||
| { variants?: Record<string, unknown> }
|
||||
| undefined;
|
||||
const variants = model?.variants;
|
||||
if (!variants) {
|
||||
return settingsDefaultVariant || currentVariant || undefined;
|
||||
}
|
||||
if (settingsDefaultVariant && Object.prototype.hasOwnProperty.call(variants, settingsDefaultVariant)) {
|
||||
return settingsDefaultVariant;
|
||||
}
|
||||
if (currentVariant && Object.prototype.hasOwnProperty.call(variants, currentVariant)) {
|
||||
return currentVariant;
|
||||
}
|
||||
return undefined;
|
||||
}, []);
|
||||
|
||||
const startSession = React.useCallback(async (issueNumber: number) => {
|
||||
if (mode === 'select') {
|
||||
// In select mode, fetch full issue details and return via onSelect
|
||||
if (!projectDirectory) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.noActiveProject'));
|
||||
return;
|
||||
}
|
||||
if (!gitlab?.issueGet || !gitlab?.issueComments) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.runtimeUnavailable'));
|
||||
return;
|
||||
}
|
||||
if (startingIssueNumber) return;
|
||||
setStartingIssueNumber(issueNumber);
|
||||
try {
|
||||
const issueRes = await gitlab.issueGet(projectDirectory, issueNumber);
|
||||
if (issueRes.connected === false) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.notConnected'));
|
||||
return;
|
||||
}
|
||||
if (!issueRes.repo) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.repoNotResolvable'), {
|
||||
description: t('session.gitlabIssuePicker.error.repoMustBeGitlab'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const issue = issueRes.issue;
|
||||
if (!issue) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.issueNotFound'));
|
||||
return;
|
||||
}
|
||||
|
||||
const commentsRes = await gitlab.issueComments(projectDirectory, issueNumber);
|
||||
if (commentsRes.connected === false) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.notConnected'));
|
||||
return;
|
||||
}
|
||||
const comments = commentsRes.comments ?? [];
|
||||
|
||||
// Build full context text like in createSession mode
|
||||
const contextText = buildIssueContextText({ repo: issueRes.repo, issue, comments });
|
||||
|
||||
if (onSelect) {
|
||||
onSelect({
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
url: issue.url,
|
||||
contextText,
|
||||
author: issue.author ? {
|
||||
login: issue.author.username,
|
||||
avatarUrl: issue.author.avatarUrl,
|
||||
} : undefined,
|
||||
});
|
||||
}
|
||||
onOpenChange(false);
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
toast.error(t('session.gitlabIssuePicker.toast.loadIssueDetailsFailed'), { description: message });
|
||||
} finally {
|
||||
setStartingIssueNumber(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!projectDirectory) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.noActiveProject'));
|
||||
return;
|
||||
}
|
||||
if (!gitlab?.issueGet || !gitlab?.issueComments) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.runtimeUnavailable'));
|
||||
return;
|
||||
}
|
||||
if (startingIssueNumber) return;
|
||||
setStartingIssueNumber(issueNumber);
|
||||
try {
|
||||
const issueRes = await gitlab.issueGet(projectDirectory, issueNumber);
|
||||
if (issueRes.connected === false) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.notConnected'));
|
||||
return;
|
||||
}
|
||||
if (!issueRes.repo) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.repoNotResolvable'), {
|
||||
description: t('session.gitlabIssuePicker.error.repoMustBeGitlab'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const issue = issueRes.issue;
|
||||
if (!issue) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.issueNotFound'));
|
||||
return;
|
||||
}
|
||||
|
||||
const commentsRes = await gitlab.issueComments(projectDirectory, issueNumber);
|
||||
if (commentsRes.connected === false) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.notConnected'));
|
||||
return;
|
||||
}
|
||||
const comments = commentsRes.comments ?? [];
|
||||
|
||||
const sessionTitle = `#${issue.number} ${issue.title}`.trim();
|
||||
|
||||
const { sessionId, sessionDirectory } = await (async () => {
|
||||
if (createInWorktree) {
|
||||
const preferred = `issue-${issue.number}-${generateBranchSlug()}`;
|
||||
const created = await createWorktreeSessionForNewBranch(
|
||||
projectDirectory,
|
||||
preferred,
|
||||
undefined,
|
||||
{ returnAfterDirectoryCreated: true }
|
||||
);
|
||||
if (!created?.id) {
|
||||
throw new Error('Failed to create worktree session');
|
||||
}
|
||||
return { sessionId: created.id, sessionDirectory: created.path };
|
||||
}
|
||||
|
||||
const session = await sessionActions.createSession(sessionTitle, projectDirectory, null);
|
||||
if (!session?.id) {
|
||||
throw new Error('Failed to create session');
|
||||
}
|
||||
return { sessionId: session.id, sessionDirectory: session.directory ?? projectDirectory };
|
||||
})();
|
||||
|
||||
// Ensure worktree-based sessions also get the issue title.
|
||||
void sessionActions.updateSessionTitle(sessionId, sessionTitle).catch(() => undefined);
|
||||
|
||||
try {
|
||||
useSessionUIStore.getState().initializeNewOpenChamberSession(sessionId, useConfigStore.getState().agents);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Close modal immediately after session exists (don't wait for message send).
|
||||
onOpenChange(false);
|
||||
|
||||
const configState = useConfigStore.getState();
|
||||
const lastUsedProvider = useSelectionStore.getState().lastUsedProvider;
|
||||
|
||||
const defaultModel = resolveDefaultModelSelection();
|
||||
const providerID = defaultModel?.providerID || configState.currentProviderId || lastUsedProvider?.providerID;
|
||||
const modelID = defaultModel?.modelID || configState.currentModelId || lastUsedProvider?.modelID;
|
||||
const agentName = resolveDefaultAgentName() || configState.currentAgentName || undefined;
|
||||
if (!providerID || !modelID) {
|
||||
toast.error(t('session.gitlabIssuePicker.error.noModelSelected'));
|
||||
return;
|
||||
}
|
||||
|
||||
const variant = resolveDefaultVariant(providerID, modelID);
|
||||
|
||||
const visiblePromptText = await renderMagicPrompt('gitlab.issue.review.visible', {
|
||||
issue_number: String(issue.number),
|
||||
});
|
||||
const instructionsText = await renderMagicPrompt('gitlab.issue.review.instructions');
|
||||
const contextText = buildIssueContextText({ repo: issueRes.repo, issue, comments });
|
||||
|
||||
// Record the thread this session was created for, so it stays visible as
|
||||
// a context source once the opening message has scrolled away. A
|
||||
// snapshot, never re-fetched; a failed write must not fail the flow.
|
||||
void sessionActions.setLinkedIssue(
|
||||
sessionId,
|
||||
sessionDirectory,
|
||||
buildLinkedIssue({
|
||||
url: issue.url,
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
kind: 'issue',
|
||||
author: issue.author ? {
|
||||
login: issue.author.username,
|
||||
avatarUrl: issue.author.avatarUrl,
|
||||
} : undefined,
|
||||
linkedAt: Date.now(),
|
||||
}),
|
||||
true,
|
||||
).catch(() => undefined);
|
||||
|
||||
void useSessionUIStore.getState().sendMessage(
|
||||
visiblePromptText,
|
||||
providerID,
|
||||
modelID,
|
||||
agentName,
|
||||
undefined,
|
||||
undefined,
|
||||
[
|
||||
{ text: instructionsText, synthetic: true },
|
||||
{ text: contextText, synthetic: true },
|
||||
],
|
||||
variant,
|
||||
undefined,
|
||||
{ sessionId },
|
||||
).catch((e) => {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
toast.error(t('session.gitlabIssuePicker.toast.sendContextFailed'), {
|
||||
description: message,
|
||||
});
|
||||
});
|
||||
|
||||
toast.success(t('session.gitlabIssuePicker.toast.sessionCreated'));
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
toast.error(t('session.gitlabIssuePicker.toast.startSessionFailed'), { description: message });
|
||||
} finally {
|
||||
setStartingIssueNumber(null);
|
||||
}
|
||||
}, [createInWorktree, gitlab, mode, onOpenChange, onSelect, projectDirectory, resolveDefaultAgentName, resolveDefaultModelSelection, resolveDefaultVariant, startingIssueNumber, t]);
|
||||
|
||||
const title = mode === 'select' ? t('session.gitlabIssuePicker.title.select') : t('session.gitlabIssuePicker.title.createSession');
|
||||
const description = mode === 'select'
|
||||
? t('session.gitlabIssuePicker.description.select')
|
||||
: t('session.gitlabIssuePicker.description.createSession');
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<div className="relative mt-2">
|
||||
<Icon name="search" className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder={t('session.gitlabIssuePicker.searchPlaceholder')}
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className="pl-9 w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={cn(isMobile ? 'min-h-0 mt-2' : 'flex-1 overflow-y-auto mt-2')}>
|
||||
{!projectDirectory ? (
|
||||
<div className="text-center text-muted-foreground py-8">{t('session.gitlabIssuePicker.empty.noActiveProject')}</div>
|
||||
) : null}
|
||||
|
||||
{!gitlab ? (
|
||||
<div className="text-center text-muted-foreground py-8">{t('session.gitlabIssuePicker.empty.runtimeUnavailable')}</div>
|
||||
) : null}
|
||||
|
||||
{isLoading ? (
|
||||
<div className="text-center text-muted-foreground py-8 flex items-center justify-center gap-2">
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('session.gitlabIssuePicker.loading.issues')}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{connected === false ? (
|
||||
<div className="text-center text-muted-foreground py-8 space-y-3">
|
||||
<div>{t('session.gitlabIssuePicker.empty.notConnected')}</div>
|
||||
<div className="flex justify-center">
|
||||
<Button variant="outline" size="sm" onClick={openGitLabSettings}>
|
||||
{t('session.gitlabIssuePicker.actions.openSettings')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{error ? (
|
||||
<div className="text-center text-muted-foreground py-8 break-words">{error}</div>
|
||||
) : null}
|
||||
|
||||
{directNumber && projectDirectory && gitlab && connected ? (
|
||||
<div
|
||||
className={cn(
|
||||
'group flex items-center gap-2 py-1.5 hover:bg-interactive-hover/30 rounded transition-colors cursor-pointer',
|
||||
startingIssueNumber === directNumber && 'bg-interactive-selection/30'
|
||||
)}
|
||||
onClick={() => void startSession(directNumber)}
|
||||
>
|
||||
<span className="typography-meta text-muted-foreground w-5 text-right flex-shrink-0">#</span>
|
||||
<p className="flex-1 min-w-0 typography-small text-foreground truncate ml-0.5">
|
||||
{t('session.gitlabIssuePicker.actions.useIssue', { number: directNumber })}
|
||||
</p>
|
||||
<div className="flex-shrink-0 h-5 flex items-center mr-2">
|
||||
{startingIssueNumber === directNumber ? (
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin text-muted-foreground" />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{issues.length === 0 && !isLoading && connected && gitlab && projectDirectory ? (
|
||||
<div className="text-center text-muted-foreground py-8">{debouncedQuery.trim() ? t('session.gitlabIssuePicker.empty.noIssuesFound') : t('session.gitlabIssuePicker.empty.noOpenIssuesFound')}</div>
|
||||
) : null}
|
||||
|
||||
{issues.map((issue) => (
|
||||
<div
|
||||
key={issue.number}
|
||||
className={cn(
|
||||
'group flex items-center gap-2 py-1.5 hover:bg-interactive-hover/30 rounded transition-colors cursor-pointer',
|
||||
startingIssueNumber === issue.number && 'bg-interactive-selection/30'
|
||||
)}
|
||||
onClick={() => void startSession(issue.number)}
|
||||
>
|
||||
<span className="typography-meta text-muted-foreground w-12 text-right flex-shrink-0">
|
||||
#{issue.number}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0 ml-0.5">
|
||||
<p className="typography-small text-foreground truncate">
|
||||
{issue.title}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0 h-5 flex items-center mr-2">
|
||||
{startingIssueNumber === issue.number ? (
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin text-muted-foreground" />
|
||||
) : (
|
||||
<a
|
||||
href={issue.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(
|
||||
"h-5 w-5 items-center justify-center text-muted-foreground hover:text-foreground transition-colors",
|
||||
alwaysShowActions ? "flex" : "hidden group-hover:flex"
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
aria-label={t('session.gitlabIssuePicker.actions.openInGitLabAria')}
|
||||
>
|
||||
<Icon name="external-link" className="h-4 w-4" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{hasMore && connected && projectDirectory && gitlab ? (
|
||||
<div className="py-2 flex justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void loadMore()}
|
||||
disabled={isLoadingMore || Boolean(startingIssueNumber)}
|
||||
className={cn(
|
||||
'typography-meta text-muted-foreground hover:text-foreground transition-colors underline underline-offset-4',
|
||||
(isLoadingMore || Boolean(startingIssueNumber)) && 'opacity-50 cursor-not-allowed hover:text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{isLoadingMore ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('session.gitlabIssuePicker.loading.more')}
|
||||
</span>
|
||||
) : (
|
||||
t('session.gitlabIssuePicker.actions.loadMore')
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{mode !== 'select' && (
|
||||
<div className="mt-4 p-3 bg-muted/30 rounded-lg">
|
||||
<p className="typography-meta text-muted-foreground font-medium mb-2">{t('session.gitlabIssuePicker.actions.sectionTitle')}</p>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-2">
|
||||
<div
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={createInWorktree}
|
||||
onClick={() => setCreateInWorktree((v) => !v)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === ' ' || e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
setCreateInWorktree((v) => !v);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setCreateInWorktree((v) => !v);
|
||||
}}
|
||||
aria-label={t('session.gitlabIssuePicker.actions.toggleWorktreeAria')}
|
||||
className="flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
||||
>
|
||||
{createInWorktree ? (
|
||||
<Icon name="checkbox" className="h-4 w-4 text-primary" />
|
||||
) : (
|
||||
<Icon name="checkbox-blank" className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
<span className="typography-meta text-muted-foreground">{t('session.gitlabIssuePicker.actions.createInWorktree')}</span>
|
||||
<span className="typography-meta text-muted-foreground/70 hidden sm:inline">(issue-<number>-<slug>)</span>
|
||||
</div>
|
||||
<div className="hidden sm:block sm:flex-1" />
|
||||
<div className="flex items-center gap-2">
|
||||
{repoUrl ? (
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<a href={repoUrl} target="_blank" rel="noopener noreferrer">
|
||||
<Icon name="external-link" className="size-4" />
|
||||
{t('session.gitlabIssuePicker.actions.openRepo')}
|
||||
</a>
|
||||
</Button>
|
||||
) : null}
|
||||
<Button variant="outline" size="sm" onClick={refresh} disabled={isLoading || Boolean(startingIssueNumber)}>
|
||||
{t('session.gitlabIssuePicker.actions.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
if (isMobile) {
|
||||
return (
|
||||
<MobileOverlayPanel
|
||||
open={open}
|
||||
title={title}
|
||||
onClose={() => onOpenChange(false)}
|
||||
renderHeader={(closeButton) => (
|
||||
<div className="flex flex-col gap-1.5 px-3 py-2 border-b border-border/40">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="typography-ui-label font-semibold text-foreground">{title}</h2>
|
||||
{closeButton}
|
||||
</div>
|
||||
<p className="typography-small text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</MobileOverlayPanel>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-2xl max-h-[70vh] flex flex-col">
|
||||
<DialogHeader className="flex-shrink-0">
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Icon name="git-branch" className="h-5 w-5" />
|
||||
{title}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{description}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{content}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { useGitLabAuthStore } from '@/stores/useGitLabAuthStore';
|
||||
import { renderMagicPrompt } from '@/lib/magicPrompts';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
|
||||
import type { GitLabMergeRequestContextResult, GitLabMergeRequestSummary, GitLabMergeRequestsListResult } from '@/lib/api/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
const parsePrNumber = (value: string): number | null => {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
const urlMatch = trimmed.match(/\/merge_requests\/(\d+)(?:\b|\/|$)/i);
|
||||
if (urlMatch) {
|
||||
const parsed = Number(urlMatch[1]);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
const shortMatch = trimmed.match(/^!?(\d+)$/);
|
||||
if (shortMatch) {
|
||||
const parsed = Number(shortMatch[1]);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const buildMergeRequestContextText = (payload: GitLabMergeRequestContextResult) => {
|
||||
return `GitLab merge request context (JSON)\n${JSON.stringify(payload, null, 2)}`;
|
||||
};
|
||||
|
||||
export function GitLabMrPickerDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
onSelect,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onSelect?: (mr: {
|
||||
number: number;
|
||||
title: string;
|
||||
url: string;
|
||||
head: string;
|
||||
base: string;
|
||||
includeDiff: boolean;
|
||||
instructionsText: string;
|
||||
contextText: string;
|
||||
author?: { login: string; avatarUrl?: string };
|
||||
}) => void;
|
||||
}) {
|
||||
const { t } = useI18n();
|
||||
const { gitlab } = useRuntimeAPIs();
|
||||
const gitlabAuthStatus = useGitLabAuthStore((state) => state.status);
|
||||
const gitlabAuthChecked = useGitLabAuthStore((state) => state.hasChecked);
|
||||
const setSettingsDialogOpen = useUIStore((state) => state.setSettingsDialogOpen);
|
||||
const setSettingsPage = useUIStore((state) => state.setSettingsPage);
|
||||
const isMobile = useUIStore((state) => state.isMobile);
|
||||
const { isTablet } = useDeviceInfo();
|
||||
const alwaysShowActions = isMobile || isTablet;
|
||||
const activeProject = useProjectsStore((state) => state.getActiveProject());
|
||||
|
||||
const projectDirectory = activeProject?.path ?? null;
|
||||
|
||||
const [query, setQuery] = React.useState('');
|
||||
const [includeDiff, setIncludeDiff] = React.useState(false);
|
||||
const [result, setResult] = React.useState<GitLabMergeRequestsListResult | null>(null);
|
||||
const [mrs, setMrs] = React.useState<GitLabMergeRequestSummary[]>([]);
|
||||
const [page, setPage] = React.useState(1);
|
||||
const [hasMore, setHasMore] = React.useState(false);
|
||||
const [loadingMrNumber, setLoadingMrNumber] = React.useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [isLoadingMore, setIsLoadingMore] = React.useState(false);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
const directNumber = React.useMemo(() => parsePrNumber(query), [query]);
|
||||
const debouncedQuery = useDebouncedValue(query, 350);
|
||||
const isTextSearch = debouncedQuery.trim().length > 0 && !directNumber;
|
||||
|
||||
const refresh = React.useCallback(async () => {
|
||||
if (!projectDirectory) {
|
||||
setResult(null);
|
||||
setError(t('session.gitlabMrPicker.error.noActiveProject'));
|
||||
return;
|
||||
}
|
||||
if (gitlabAuthChecked && gitlabAuthStatus?.connected === false) {
|
||||
setResult({ connected: false, mrs: [], page: 1, hasMore: false });
|
||||
setMrs([]);
|
||||
setHasMore(false);
|
||||
setPage(1);
|
||||
setError(null);
|
||||
return;
|
||||
}
|
||||
if (!gitlab?.mrsList) {
|
||||
setResult(null);
|
||||
setError(t('session.gitlabMrPicker.error.runtimeUnavailable'));
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const next = await gitlab.mrsList(projectDirectory, { page: 1 });
|
||||
setResult(next);
|
||||
setMrs(next.mrs ?? []);
|
||||
setPage(next.page ?? 1);
|
||||
setHasMore(Boolean(next.hasMore));
|
||||
if (next.connected === false) {
|
||||
setError(null);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [gitlab, gitlabAuthChecked, gitlabAuthStatus, projectDirectory, t]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open || !projectDirectory) return;
|
||||
if (gitlabAuthChecked && gitlabAuthStatus?.connected === false) return;
|
||||
if (!gitlab?.mrsList) return;
|
||||
if (!debouncedQuery.trim() || directNumber) {
|
||||
void refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
gitlab.mrsList(projectDirectory, { page: 1, query: debouncedQuery.trim() })
|
||||
.then((next) => {
|
||||
if (controller.signal.aborted) return;
|
||||
setResult(next);
|
||||
setMrs(next.mrs ?? []);
|
||||
setPage(next.page ?? 1);
|
||||
setHasMore(Boolean(next.hasMore));
|
||||
})
|
||||
.catch((e) => {
|
||||
if (controller.signal.aborted) return;
|
||||
setError(e instanceof Error ? e.message : String(e));
|
||||
})
|
||||
.finally(() => {
|
||||
if (!controller.signal.aborted) setIsLoading(false);
|
||||
});
|
||||
|
||||
return () => controller.abort();
|
||||
}, [open, projectDirectory, gitlab, gitlabAuthChecked, gitlabAuthStatus, debouncedQuery, directNumber, refresh, t]);
|
||||
|
||||
const loadMore = React.useCallback(async () => {
|
||||
if (!projectDirectory) return;
|
||||
if (!gitlab?.mrsList) return;
|
||||
if (isLoadingMore || isLoading) return;
|
||||
if (!hasMore) return;
|
||||
|
||||
setIsLoadingMore(true);
|
||||
try {
|
||||
const nextPage = page + 1;
|
||||
const next = isTextSearch
|
||||
? await gitlab.mrsList(projectDirectory, { page: nextPage, query: debouncedQuery.trim() })
|
||||
: await gitlab.mrsList(projectDirectory, { page: nextPage });
|
||||
setResult(next);
|
||||
setMrs((prev) => [...prev, ...(next.mrs ?? [])]);
|
||||
setPage(next.page ?? nextPage);
|
||||
setHasMore(Boolean(next.hasMore));
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
toast.error(t('session.gitlabMrPicker.toast.loadMoreFailed'), { description: message });
|
||||
} finally {
|
||||
setIsLoadingMore(false);
|
||||
}
|
||||
}, [gitlab, hasMore, isLoading, isLoadingMore, isTextSearch, debouncedQuery, page, projectDirectory, t]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) {
|
||||
setQuery('');
|
||||
setIncludeDiff(false);
|
||||
setLoadingMrNumber(null);
|
||||
setError(null);
|
||||
setResult(null);
|
||||
setMrs([]);
|
||||
setPage(1);
|
||||
setHasMore(false);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
void refresh();
|
||||
}, [open, refresh]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!open) return;
|
||||
if (gitlabAuthChecked && gitlabAuthStatus?.connected === false) {
|
||||
setResult({ connected: false, mrs: [], page: 1, hasMore: false });
|
||||
setMrs([]);
|
||||
setHasMore(false);
|
||||
setPage(1);
|
||||
setError(null);
|
||||
}
|
||||
}, [gitlabAuthChecked, gitlabAuthStatus, open]);
|
||||
|
||||
const connected = gitlabAuthChecked ? result?.connected !== false : true;
|
||||
|
||||
const openGitLabSettings = React.useCallback(() => {
|
||||
setSettingsPage('git');
|
||||
setSettingsDialogOpen(true);
|
||||
}, [setSettingsDialogOpen, setSettingsPage]);
|
||||
|
||||
const attachMr = React.useCallback(async (mrNumber: number) => {
|
||||
if (!projectDirectory) {
|
||||
toast.error(t('session.gitlabMrPicker.error.noActiveProject'));
|
||||
return;
|
||||
}
|
||||
if (!gitlab?.mrContext) {
|
||||
toast.error(t('session.gitlabMrPicker.error.runtimeUnavailable'));
|
||||
return;
|
||||
}
|
||||
if (loadingMrNumber) return;
|
||||
|
||||
setLoadingMrNumber(mrNumber);
|
||||
try {
|
||||
const context = await gitlab.mrContext(projectDirectory, mrNumber, {
|
||||
includeDiff,
|
||||
});
|
||||
|
||||
if (context.connected === false) {
|
||||
toast.error(t('session.gitlabMrPicker.error.notConnected'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.mr) {
|
||||
toast.error(t('session.gitlabMrPicker.error.mrNotFound'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!context.repo) {
|
||||
toast.error(t('session.gitlabMrPicker.error.repoNotResolvable'), {
|
||||
description: t('session.gitlabMrPicker.error.repoMustBeGitlab'),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (onSelect) {
|
||||
const instructionsText = await renderMagicPrompt('gitlab.pr.review.instructions');
|
||||
onSelect({
|
||||
number: context.mr.number,
|
||||
title: context.mr.title,
|
||||
url: context.mr.url,
|
||||
head: context.mr.sourceBranch,
|
||||
base: context.mr.targetBranch,
|
||||
includeDiff,
|
||||
instructionsText,
|
||||
contextText: buildMergeRequestContextText(context),
|
||||
author: context.mr.author
|
||||
? {
|
||||
login: context.mr.author.username,
|
||||
avatarUrl: context.mr.author.avatarUrl,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
onOpenChange(false);
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
toast.error(t('session.gitlabMrPicker.toast.loadDetailsFailed'), { description: message });
|
||||
} finally {
|
||||
setLoadingMrNumber(null);
|
||||
}
|
||||
}, [gitlab, includeDiff, loadingMrNumber, onOpenChange, onSelect, projectDirectory, t]);
|
||||
|
||||
const title = t('session.gitlabMrPicker.title');
|
||||
const description = t('session.gitlabMrPicker.description');
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<div className="mt-2 flex items-center gap-3">
|
||||
<div className="relative flex-1 min-w-0">
|
||||
<Icon name="search" className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder={t('session.gitlabMrPicker.searchPlaceholder')}
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className="pl-9 w-full"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIncludeDiff((prev) => !prev)}
|
||||
className="h-9 shrink-0 flex items-center gap-2 text-left"
|
||||
aria-pressed={includeDiff}
|
||||
aria-label={t('session.gitlabMrPicker.includeDiffAria')}
|
||||
>
|
||||
<span onClick={(e) => e.stopPropagation()}>
|
||||
<Checkbox
|
||||
checked={includeDiff}
|
||||
onChange={(checked) => setIncludeDiff(checked)}
|
||||
ariaLabel={t('session.gitlabMrPicker.includeDiffAria')}
|
||||
/>
|
||||
</span>
|
||||
<span className="typography-small text-muted-foreground whitespace-nowrap">{t('session.gitlabMrPicker.includeDiff')}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={cn(isMobile ? 'min-h-0' : 'flex-1 overflow-y-auto')}>
|
||||
{!projectDirectory ? (
|
||||
<div className="text-center text-muted-foreground py-8">{t('session.gitlabMrPicker.empty.noActiveProject')}</div>
|
||||
) : null}
|
||||
|
||||
{!gitlab ? (
|
||||
<div className="text-center text-muted-foreground py-8">{t('session.gitlabMrPicker.empty.runtimeUnavailable')}</div>
|
||||
) : null}
|
||||
|
||||
{isLoading ? (
|
||||
<div className="text-center text-muted-foreground py-8 flex items-center justify-center gap-2">
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('session.gitlabMrPicker.loading.mergeRequests')}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{connected === false ? (
|
||||
<div className="text-center text-muted-foreground py-8 space-y-3">
|
||||
<div>{t('session.gitlabMrPicker.empty.notConnected')}</div>
|
||||
<div className="flex justify-center">
|
||||
<Button variant="outline" size="sm" onClick={openGitLabSettings}>
|
||||
{t('session.gitlabMrPicker.actions.openSettings')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{error ? (
|
||||
<div className="text-center text-muted-foreground py-8 break-words">{error}</div>
|
||||
) : null}
|
||||
|
||||
{directNumber && projectDirectory && gitlab && connected ? (
|
||||
<div
|
||||
className={cn(
|
||||
'group flex items-center gap-2 py-1.5 hover:bg-interactive-hover/30 rounded transition-colors cursor-pointer',
|
||||
loadingMrNumber === directNumber && 'bg-interactive-selection/30'
|
||||
)}
|
||||
onClick={() => void attachMr(directNumber)}
|
||||
>
|
||||
<span className="typography-meta text-muted-foreground w-5 text-right flex-shrink-0">!</span>
|
||||
<p className="flex-1 min-w-0 typography-small text-foreground truncate ml-0.5">
|
||||
{t('session.gitlabMrPicker.actions.useMergeRequest', { number: directNumber })}
|
||||
</p>
|
||||
<div className="flex-shrink-0 h-5 flex items-center mr-2">
|
||||
{loadingMrNumber === directNumber ? (
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin text-muted-foreground" />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{mrs.length === 0 && !isLoading && connected && gitlab && projectDirectory ? (
|
||||
<div className="text-center text-muted-foreground py-8">{debouncedQuery.trim() ? t('session.gitlabMrPicker.empty.noMergeRequestsFound') : t('session.gitlabMrPicker.empty.noOpenMergeRequestsFound')}</div>
|
||||
) : null}
|
||||
|
||||
{mrs.map((mr) => (
|
||||
<div
|
||||
key={mr.number}
|
||||
className={cn(
|
||||
'group flex items-center gap-2 py-1.5 hover:bg-interactive-hover/30 rounded transition-colors cursor-pointer',
|
||||
loadingMrNumber === mr.number && 'bg-interactive-selection/30'
|
||||
)}
|
||||
onClick={() => void attachMr(mr.number)}
|
||||
>
|
||||
<div className="flex-1 min-w-0 ml-0.5">
|
||||
<p className="typography-small text-foreground truncate">
|
||||
<span className="text-muted-foreground mr-1">!{mr.number}</span>
|
||||
{mr.title}
|
||||
</p>
|
||||
<p className="typography-meta text-muted-foreground truncate">{mr.sourceBranch} → {mr.targetBranch}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0 h-5 flex items-center mr-2">
|
||||
{loadingMrNumber === mr.number ? (
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin text-muted-foreground" />
|
||||
) : (
|
||||
<a
|
||||
href={mr.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(
|
||||
"h-5 w-5 items-center justify-center text-muted-foreground hover:text-foreground transition-colors",
|
||||
alwaysShowActions ? "flex" : "hidden group-hover:flex"
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
aria-label={t('session.gitlabMrPicker.actions.openInGitLabAria')}
|
||||
>
|
||||
<Icon name="external-link" className="h-4 w-4" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{hasMore && connected && projectDirectory && gitlab ? (
|
||||
<div className="py-2 flex justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void loadMore()}
|
||||
disabled={isLoadingMore || Boolean(loadingMrNumber)}
|
||||
className={cn(
|
||||
'typography-meta text-muted-foreground hover:text-foreground transition-colors underline underline-offset-4',
|
||||
(isLoadingMore || Boolean(loadingMrNumber)) && 'opacity-50 cursor-not-allowed hover:text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{isLoadingMore ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Icon name="loader-4" className="h-4 w-4 animate-spin" />
|
||||
{t('session.gitlabMrPicker.loading.more')}
|
||||
</span>
|
||||
) : (
|
||||
t('session.gitlabMrPicker.actions.loadMore')
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
if (isMobile) {
|
||||
return (
|
||||
<MobileOverlayPanel
|
||||
open={open}
|
||||
title={title}
|
||||
onClose={() => onOpenChange(false)}
|
||||
renderHeader={(closeButton) => (
|
||||
<div className="flex flex-col gap-1.5 px-3 py-2 border-b border-border/40">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="typography-ui-label font-semibold text-foreground">{title}</h2>
|
||||
{closeButton}
|
||||
</div>
|
||||
<p className="typography-small text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</MobileOverlayPanel>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-2xl max-h-[70vh] flex flex-col">
|
||||
<DialogHeader className="flex-shrink-0">
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Icon name="git-merge" className="h-5 w-5" />
|
||||
{title}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{description}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{content}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -1683,6 +1683,39 @@ export const dict = {
|
||||
'session.githubIssuePicker.actions.createInWorktree': 'In Worktree erstellen',
|
||||
'session.githubIssuePicker.actions.openRepo': 'Repository öffnen',
|
||||
'session.githubIssuePicker.actions.refresh': 'Aktualisieren',
|
||||
'session.gitlabIssuePicker.error.noActiveProject': 'Kein aktives Projekt',
|
||||
'session.gitlabIssuePicker.error.runtimeUnavailable': 'GitLab-Laufzeit-API nicht verfügbar',
|
||||
'session.gitlabIssuePicker.error.notConnected': 'GitLab nicht verbunden',
|
||||
'session.gitlabIssuePicker.error.repoNotResolvable': 'Repository kann nicht aufgelöst werden',
|
||||
'session.gitlabIssuePicker.error.repoMustBeGitlab': 'Das origin-Remote muss eine GitLab-URL sein',
|
||||
'session.gitlabIssuePicker.error.issueNotFound': 'Problem nicht gefunden',
|
||||
'session.gitlabIssuePicker.error.noModelSelected': 'Kein Modell ausgewählt',
|
||||
'session.gitlabIssuePicker.toast.loadMoreFailed': 'Fehler beim Laden weiterer Probleme',
|
||||
'session.gitlabIssuePicker.toast.loadIssueDetailsFailed': 'Fehler beim Laden der Problemdetails',
|
||||
'session.gitlabIssuePicker.toast.sendContextFailed': 'Fehler beim Senden des Problem-Kontexts',
|
||||
'session.gitlabIssuePicker.toast.sessionCreated': 'Sitzung aus Problem erstellt',
|
||||
'session.gitlabIssuePicker.toast.startSessionFailed': 'Fehler beim Starten der Sitzung',
|
||||
'session.gitlabIssuePicker.title.select': 'GitLab-Problem verknüpfen',
|
||||
'session.gitlabIssuePicker.title.createSession': 'Neue Sitzung aus GitLab-Problem',
|
||||
'session.gitlabIssuePicker.description.select': 'Wählen Sie ein Problem, um es mit dieser Sitzung zu verknüpfen.',
|
||||
'session.gitlabIssuePicker.description.createSession': 'Erstellt eine neue Sitzung mit verstecktem Problem-Kontext (Titel/Inhalt/Labels/Kommentare).',
|
||||
'session.gitlabIssuePicker.searchPlaceholder': 'Suche nach Titel oder fügen Sie eine Problem-URL ein',
|
||||
'session.gitlabIssuePicker.empty.noActiveProject': 'Kein aktives Projekt ausgewählt.',
|
||||
'session.gitlabIssuePicker.empty.runtimeUnavailable': 'GitLab-Laufzeit-API nicht verfügbar.',
|
||||
'session.gitlabIssuePicker.empty.notConnected': 'GitLab nicht verbunden. Verbinden Sie Ihr GitLab-Konto in den Einstellungen.',
|
||||
'session.gitlabIssuePicker.empty.noIssuesFound': 'Keine Probleme gefunden',
|
||||
'session.gitlabIssuePicker.empty.noOpenIssuesFound': 'Keine offenen Probleme gefunden',
|
||||
'session.gitlabIssuePicker.loading.issues': 'Probleme werden geladen...',
|
||||
'session.gitlabIssuePicker.loading.more': 'Wird geladen...',
|
||||
'session.gitlabIssuePicker.actions.openSettings': 'Einstellungen öffnen',
|
||||
'session.gitlabIssuePicker.actions.useIssue': 'Problem #{number} verwenden',
|
||||
'session.gitlabIssuePicker.actions.openInGitLabAria': 'In GitLab öffnen',
|
||||
'session.gitlabIssuePicker.actions.loadMore': 'Mehr laden',
|
||||
'session.gitlabIssuePicker.actions.sectionTitle': 'Aktionen',
|
||||
'session.gitlabIssuePicker.actions.toggleWorktreeAria': 'Worktree umschalten',
|
||||
'session.gitlabIssuePicker.actions.createInWorktree': 'In Worktree erstellen',
|
||||
'session.gitlabIssuePicker.actions.openRepo': 'Repository öffnen',
|
||||
'session.gitlabIssuePicker.actions.refresh': 'Aktualisieren',
|
||||
'session.githubPrPicker.error.noActiveProject': 'Kein aktives Projekt',
|
||||
'session.githubPrPicker.error.runtimeUnavailable': 'GitHub-Laufzeit-API nicht verfügbar',
|
||||
'session.githubPrPicker.error.notConnected': 'GitHub nicht verbunden',
|
||||
@@ -1707,6 +1740,30 @@ export const dict = {
|
||||
'session.githubPrPicker.actions.usePullRequest': 'Pull Request #{number} verwenden',
|
||||
'session.githubPrPicker.actions.openInGitHubAria': 'In GitHub öffnen',
|
||||
'session.githubPrPicker.actions.loadMore': 'Mehr laden',
|
||||
'session.gitlabMrPicker.error.noActiveProject': 'Kein aktives Projekt',
|
||||
'session.gitlabMrPicker.error.runtimeUnavailable': 'GitLab-Laufzeit-API nicht verfügbar',
|
||||
'session.gitlabMrPicker.error.notConnected': 'GitLab nicht verbunden',
|
||||
'session.gitlabMrPicker.error.mrNotFound': 'Merge Request nicht gefunden',
|
||||
'session.gitlabMrPicker.error.repoNotResolvable': 'Repository kann nicht aufgelöst werden',
|
||||
'session.gitlabMrPicker.error.repoMustBeGitlab': 'Das origin-Remote muss eine GitLab-URL sein',
|
||||
'session.gitlabMrPicker.toast.loadMoreFailed': 'Fehler beim Laden weiterer Merge Requests',
|
||||
'session.gitlabMrPicker.toast.loadDetailsFailed': 'Fehler beim Laden der Merge-Request-Details',
|
||||
'session.gitlabMrPicker.title': 'GitLab-Merge Request verknüpfen',
|
||||
'session.gitlabMrPicker.description': 'Wählen Sie einen Merge Request aus, um den Überprüfungs-Kontext zu dieser Nachricht hinzuzufügen.',
|
||||
'session.gitlabMrPicker.searchPlaceholder': 'Suche nach Titel oder fügen Sie eine Merge-Request-URL ein',
|
||||
'session.gitlabMrPicker.includeDiffAria': 'MR-Diff in angehängten Kontext einfügen',
|
||||
'session.gitlabMrPicker.includeDiff': 'MR-Diff einfügen',
|
||||
'session.gitlabMrPicker.empty.noActiveProject': 'Kein aktives Projekt ausgewählt.',
|
||||
'session.gitlabMrPicker.empty.runtimeUnavailable': 'GitLab-Laufzeit-API nicht verfügbar.',
|
||||
'session.gitlabMrPicker.empty.notConnected': 'GitLab nicht verbunden. Verbinden Sie Ihr GitLab-Konto in den Einstellungen.',
|
||||
'session.gitlabMrPicker.empty.noMergeRequestsFound': 'Keine Merge Requests gefunden',
|
||||
'session.gitlabMrPicker.empty.noOpenMergeRequestsFound': 'Keine offenen Merge Requests gefunden',
|
||||
'session.gitlabMrPicker.loading.mergeRequests': 'Merge Requests werden geladen...',
|
||||
'session.gitlabMrPicker.loading.more': 'Wird geladen...',
|
||||
'session.gitlabMrPicker.actions.openSettings': 'Einstellungen öffnen',
|
||||
'session.gitlabMrPicker.actions.useMergeRequest': 'Merge Request !{number} verwenden',
|
||||
'session.gitlabMrPicker.actions.openInGitLabAria': 'In GitLab öffnen',
|
||||
'session.gitlabMrPicker.actions.loadMore': 'Mehr laden',
|
||||
'session.newWorktree.title': 'Neuer Worktree',
|
||||
'session.newWorktree.mode.newBranch': 'Neue Branch',
|
||||
'session.newWorktree.mode.existingBranch': 'Vorhandener Branch',
|
||||
@@ -2010,6 +2067,8 @@ export const dict = {
|
||||
'chat.chatInput.actions.addAttachment': 'Anhang hinzufügen',
|
||||
'chat.chatInput.actions.linkGithubIssue': 'GitHub-Issue verknüpfen',
|
||||
'chat.chatInput.actions.linkGithubPr': 'GitHub-PR verknüpfen',
|
||||
'chat.chatInput.actions.linkGitlabIssue': 'GitLab-Issue verknüpfen',
|
||||
'chat.chatInput.actions.linkGitlabMr': 'GitLab-MR verknüpfen',
|
||||
'chat.chatInput.actions.modelAgentSettings': 'Modell- und Agenteneinstellungen',
|
||||
'chat.chatInput.actions.sendMessageAria': 'Nachricht senden',
|
||||
'chat.chatInput.actions.queueMessageAria': 'Nachricht in die Warteschlange stellen',
|
||||
|
||||
@@ -1840,6 +1840,39 @@ export const dict = {
|
||||
'session.githubIssuePicker.actions.createInWorktree': 'Create in worktree',
|
||||
'session.githubIssuePicker.actions.openRepo': 'Open Repo',
|
||||
'session.githubIssuePicker.actions.refresh': 'Refresh',
|
||||
'session.gitlabIssuePicker.error.noActiveProject': 'No active project',
|
||||
'session.gitlabIssuePicker.error.runtimeUnavailable': 'GitLab runtime API unavailable',
|
||||
'session.gitlabIssuePicker.error.notConnected': 'GitLab not connected',
|
||||
'session.gitlabIssuePicker.error.repoNotResolvable': 'Repo not resolvable',
|
||||
'session.gitlabIssuePicker.error.repoMustBeGitlab': 'origin remote must be a GitLab URL',
|
||||
'session.gitlabIssuePicker.error.issueNotFound': 'Issue not found',
|
||||
'session.gitlabIssuePicker.error.noModelSelected': 'No model selected',
|
||||
'session.gitlabIssuePicker.toast.loadMoreFailed': 'Failed to load more issues',
|
||||
'session.gitlabIssuePicker.toast.loadIssueDetailsFailed': 'Failed to load issue details',
|
||||
'session.gitlabIssuePicker.toast.sendContextFailed': 'Failed to send issue context',
|
||||
'session.gitlabIssuePicker.toast.sessionCreated': 'Session created from issue',
|
||||
'session.gitlabIssuePicker.toast.startSessionFailed': 'Failed to start session',
|
||||
'session.gitlabIssuePicker.title.select': 'Link GitLab Issue',
|
||||
'session.gitlabIssuePicker.title.createSession': 'New Session From GitLab Issue',
|
||||
'session.gitlabIssuePicker.description.select': 'Select an issue to link to this session.',
|
||||
'session.gitlabIssuePicker.description.createSession': 'Seeds a new session with hidden issue context (title/body/labels/comments).',
|
||||
'session.gitlabIssuePicker.searchPlaceholder': 'Search by title or paste an issue URL',
|
||||
'session.gitlabIssuePicker.empty.noActiveProject': 'No active project selected.',
|
||||
'session.gitlabIssuePicker.empty.runtimeUnavailable': 'GitLab runtime API unavailable.',
|
||||
'session.gitlabIssuePicker.empty.notConnected': 'GitLab not connected. Connect your GitLab account in settings.',
|
||||
'session.gitlabIssuePicker.empty.noIssuesFound': 'No issues found',
|
||||
'session.gitlabIssuePicker.empty.noOpenIssuesFound': 'No open issues found',
|
||||
'session.gitlabIssuePicker.loading.issues': 'Loading issues...',
|
||||
'session.gitlabIssuePicker.loading.more': 'Loading...',
|
||||
'session.gitlabIssuePicker.actions.openSettings': 'Open settings',
|
||||
'session.gitlabIssuePicker.actions.useIssue': 'Use issue #{number}',
|
||||
'session.gitlabIssuePicker.actions.openInGitLabAria': 'Open in GitLab',
|
||||
'session.gitlabIssuePicker.actions.loadMore': 'Load more',
|
||||
'session.gitlabIssuePicker.actions.sectionTitle': 'Actions',
|
||||
'session.gitlabIssuePicker.actions.toggleWorktreeAria': 'Toggle worktree',
|
||||
'session.gitlabIssuePicker.actions.createInWorktree': 'Create in worktree',
|
||||
'session.gitlabIssuePicker.actions.openRepo': 'Open Repo',
|
||||
'session.gitlabIssuePicker.actions.refresh': 'Refresh',
|
||||
'session.githubPrPicker.error.noActiveProject': 'No active project',
|
||||
'session.githubPrPicker.error.runtimeUnavailable': 'GitHub runtime API unavailable',
|
||||
'session.githubPrPicker.error.notConnected': 'GitHub not connected',
|
||||
@@ -1864,6 +1897,30 @@ export const dict = {
|
||||
'session.githubPrPicker.actions.usePullRequest': 'Use pull request #{number}',
|
||||
'session.githubPrPicker.actions.openInGitHubAria': 'Open in GitHub',
|
||||
'session.githubPrPicker.actions.loadMore': 'Load more',
|
||||
'session.gitlabMrPicker.error.noActiveProject': 'No active project',
|
||||
'session.gitlabMrPicker.error.runtimeUnavailable': 'GitLab runtime API unavailable',
|
||||
'session.gitlabMrPicker.error.notConnected': 'GitLab not connected',
|
||||
'session.gitlabMrPicker.error.mrNotFound': 'Merge request not found',
|
||||
'session.gitlabMrPicker.error.repoNotResolvable': 'Repo not resolvable',
|
||||
'session.gitlabMrPicker.error.repoMustBeGitlab': 'origin remote must be a GitLab URL',
|
||||
'session.gitlabMrPicker.toast.loadMoreFailed': 'Failed to load more merge requests',
|
||||
'session.gitlabMrPicker.toast.loadDetailsFailed': 'Failed to load merge request details',
|
||||
'session.gitlabMrPicker.title': 'Link GitLab Merge Request',
|
||||
'session.gitlabMrPicker.description': 'Select a merge request to attach review context to this message.',
|
||||
'session.gitlabMrPicker.searchPlaceholder': 'Search by title or paste a merge request URL',
|
||||
'session.gitlabMrPicker.includeDiffAria': 'Include MR diff in attached context',
|
||||
'session.gitlabMrPicker.includeDiff': 'Include MR diff',
|
||||
'session.gitlabMrPicker.empty.noActiveProject': 'No active project selected.',
|
||||
'session.gitlabMrPicker.empty.runtimeUnavailable': 'GitLab runtime API unavailable.',
|
||||
'session.gitlabMrPicker.empty.notConnected': 'GitLab not connected. Connect your GitLab account in settings.',
|
||||
'session.gitlabMrPicker.empty.noMergeRequestsFound': 'No merge requests found',
|
||||
'session.gitlabMrPicker.empty.noOpenMergeRequestsFound': 'No open merge requests found',
|
||||
'session.gitlabMrPicker.loading.mergeRequests': 'Loading merge requests...',
|
||||
'session.gitlabMrPicker.loading.more': 'Loading...',
|
||||
'session.gitlabMrPicker.actions.openSettings': 'Open settings',
|
||||
'session.gitlabMrPicker.actions.useMergeRequest': 'Use merge request !{number}',
|
||||
'session.gitlabMrPicker.actions.openInGitLabAria': 'Open in GitLab',
|
||||
'session.gitlabMrPicker.actions.loadMore': 'Load more',
|
||||
'session.newWorktree.title': 'New Worktree',
|
||||
'session.newWorktree.mode.newBranch': 'New Branch',
|
||||
'session.newWorktree.mode.existingBranch': 'Existing Branch',
|
||||
@@ -2182,6 +2239,8 @@ export const dict = {
|
||||
'chat.chatInput.actions.addAttachment': 'Add attachment',
|
||||
'chat.chatInput.actions.linkGithubIssue': 'Link GitHub Issue',
|
||||
'chat.chatInput.actions.linkGithubPr': 'Link GitHub PR',
|
||||
'chat.chatInput.actions.linkGitlabIssue': 'Link GitLab Issue',
|
||||
'chat.chatInput.actions.linkGitlabMr': 'Link GitLab MR',
|
||||
'chat.chatInput.actions.modelAgentSettings': 'Model and agent settings',
|
||||
'chat.chatInput.actions.sendMessageAria': 'Send message',
|
||||
'chat.chatInput.actions.queueMessageAria': 'Queue message',
|
||||
|
||||
@@ -1818,6 +1818,39 @@ export const dict: Record<I18nKey, string> = {
|
||||
"session.githubIssuePicker.actions.createInWorktree": "Crear en worktree",
|
||||
"session.githubIssuePicker.actions.openRepo": "Abrir repositorio",
|
||||
"session.githubIssuePicker.actions.refresh": "Actualizar",
|
||||
"session.gitlabIssuePicker.error.noActiveProject": "No hay ningún proyecto activo",
|
||||
"session.gitlabIssuePicker.error.runtimeUnavailable": "API de runtime de GitLab no disponible",
|
||||
"session.gitlabIssuePicker.error.notConnected": "GitLab no está conectado",
|
||||
"session.gitlabIssuePicker.error.repoNotResolvable": "No se pudo resolver el repositorio",
|
||||
"session.gitlabIssuePicker.error.repoMustBeGitlab": "El remoto origin debe ser una URL de GitLab",
|
||||
"session.gitlabIssuePicker.error.issueNotFound": "No se encontró el issue",
|
||||
"session.gitlabIssuePicker.error.noModelSelected": "No hay ningún modelo seleccionado",
|
||||
"session.gitlabIssuePicker.toast.loadMoreFailed": "No se pudo cargar más issues",
|
||||
"session.gitlabIssuePicker.toast.loadIssueDetailsFailed": "No se pudieron cargar los detalles del issue",
|
||||
"session.gitlabIssuePicker.toast.sendContextFailed": "No se pudo enviar el contexto del issue",
|
||||
"session.gitlabIssuePicker.toast.sessionCreated": "Sesión creada desde el issue",
|
||||
"session.gitlabIssuePicker.toast.startSessionFailed": "No se pudo iniciar la sesión",
|
||||
"session.gitlabIssuePicker.title.select": "Vincular issue de GitLab",
|
||||
"session.gitlabIssuePicker.title.createSession": "Nueva sesión desde issue de GitLab",
|
||||
"session.gitlabIssuePicker.description.select": "Selecciona un issue para vincularlo a esta sesión.",
|
||||
"session.gitlabIssuePicker.description.createSession": "Inicia una nueva sesión con contexto oculto del issue (título/cuerpo/etiquetas/comentarios).",
|
||||
"session.gitlabIssuePicker.searchPlaceholder": "Buscar por título o pega la URL del issue",
|
||||
"session.gitlabIssuePicker.empty.noActiveProject": "No hay ningún proyecto activo seleccionado.",
|
||||
"session.gitlabIssuePicker.empty.runtimeUnavailable": "API de runtime de GitLab no disponible.",
|
||||
"session.gitlabIssuePicker.empty.notConnected": "GitLab no está conectado. Conecta tu cuenta de GitLab en configuración.",
|
||||
"session.gitlabIssuePicker.empty.noIssuesFound": "No se encontraron issues",
|
||||
"session.gitlabIssuePicker.empty.noOpenIssuesFound": "No se encontraron issues abiertos",
|
||||
"session.gitlabIssuePicker.loading.issues": "Cargando issues...",
|
||||
"session.gitlabIssuePicker.loading.more": "Cargando...",
|
||||
"session.gitlabIssuePicker.actions.openSettings": "Abrir configuración",
|
||||
"session.gitlabIssuePicker.actions.useIssue": "Usar issue #{number}",
|
||||
"session.gitlabIssuePicker.actions.openInGitLabAria": "Abrir en GitLab",
|
||||
"session.gitlabIssuePicker.actions.loadMore": "Cargar más",
|
||||
"session.gitlabIssuePicker.actions.sectionTitle": "Acciones",
|
||||
"session.gitlabIssuePicker.actions.toggleWorktreeAria": "Activar worktree",
|
||||
"session.gitlabIssuePicker.actions.createInWorktree": "Crear en worktree",
|
||||
"session.gitlabIssuePicker.actions.openRepo": "Abrir repositorio",
|
||||
"session.gitlabIssuePicker.actions.refresh": "Actualizar",
|
||||
"session.githubPrPicker.error.noActiveProject": "No hay ningún proyecto activo",
|
||||
"session.githubPrPicker.error.runtimeUnavailable": "API de runtime de GitHub no disponible",
|
||||
"session.githubPrPicker.error.notConnected": "GitHub no está conectado",
|
||||
@@ -1842,6 +1875,30 @@ export const dict: Record<I18nKey, string> = {
|
||||
"session.githubPrPicker.actions.usePullRequest": "Usar PR #{number}",
|
||||
"session.githubPrPicker.actions.openInGitHubAria": "Abrir en GitHub",
|
||||
"session.githubPrPicker.actions.loadMore": "Cargar más",
|
||||
"session.gitlabMrPicker.error.noActiveProject": "No hay ningún proyecto activo",
|
||||
"session.gitlabMrPicker.error.runtimeUnavailable": "API de runtime de GitLab no disponible",
|
||||
"session.gitlabMrPicker.error.notConnected": "GitLab no está conectado",
|
||||
"session.gitlabMrPicker.error.mrNotFound": "No se encontró el MR",
|
||||
"session.gitlabMrPicker.error.repoNotResolvable": "No se pudo resolver el repositorio",
|
||||
"session.gitlabMrPicker.error.repoMustBeGitlab": "El remoto origin debe ser una URL de GitLab",
|
||||
"session.gitlabMrPicker.toast.loadMoreFailed": "No se pudieron cargar más MR",
|
||||
"session.gitlabMrPicker.toast.loadDetailsFailed": "No se pudieron cargar los detalles del MR",
|
||||
"session.gitlabMrPicker.title": "Vincular MR de GitLab",
|
||||
"session.gitlabMrPicker.description": "Selecciona un MR para adjuntar contexto de revisión a este mensaje.",
|
||||
"session.gitlabMrPicker.searchPlaceholder": "Buscar por título o pega la URL del MR",
|
||||
"session.gitlabMrPicker.includeDiffAria": "Incluir diff del MR en el contexto adjunto",
|
||||
"session.gitlabMrPicker.includeDiff": "Incluir diff del MR",
|
||||
"session.gitlabMrPicker.empty.noActiveProject": "No hay ningún proyecto activo seleccionado.",
|
||||
"session.gitlabMrPicker.empty.runtimeUnavailable": "API de runtime de GitLab no disponible.",
|
||||
"session.gitlabMrPicker.empty.notConnected": "GitLab no está conectado. Conecta tu cuenta de GitLab en configuración.",
|
||||
"session.gitlabMrPicker.empty.noMergeRequestsFound": "No se encontraron MR",
|
||||
"session.gitlabMrPicker.empty.noOpenMergeRequestsFound": "No se encontraron MR abiertos",
|
||||
"session.gitlabMrPicker.loading.mergeRequests": "Cargando MR...",
|
||||
"session.gitlabMrPicker.loading.more": "Cargando...",
|
||||
"session.gitlabMrPicker.actions.openSettings": "Abrir configuración",
|
||||
"session.gitlabMrPicker.actions.useMergeRequest": "Usar MR !{number}",
|
||||
"session.gitlabMrPicker.actions.openInGitLabAria": "Abrir en GitLab",
|
||||
"session.gitlabMrPicker.actions.loadMore": "Cargar más",
|
||||
"session.newWorktree.title": "Nuevo worktree",
|
||||
"session.newWorktree.mode.newBranch": "Nueva rama",
|
||||
"session.newWorktree.mode.existingBranch": "Rama existente",
|
||||
@@ -2160,6 +2217,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
"chat.chatInput.actions.addAttachment": "Añadir adjunto",
|
||||
"chat.chatInput.actions.linkGithubIssue": "Vincular issue de GitHub",
|
||||
"chat.chatInput.actions.linkGithubPr": "Vincular PR de GitHub",
|
||||
"chat.chatInput.actions.linkGitlabIssue": "Vincular issue de GitLab",
|
||||
"chat.chatInput.actions.linkGitlabMr": "Vincular MR de GitLab",
|
||||
"chat.chatInput.actions.modelAgentSettings": "Configuración del modelo y agente",
|
||||
"chat.chatInput.actions.sendMessageAria": "Enviar mensaje",
|
||||
"chat.chatInput.actions.queueMessageAria": "Poner mensaje en cola",
|
||||
|
||||
@@ -1598,6 +1598,39 @@ export const dict = {
|
||||
'session.githubIssuePicker.actions.createInWorktree': 'Créer dans le worktree',
|
||||
'session.githubIssuePicker.actions.openRepo': 'Ouvrir le dépôt',
|
||||
'session.githubIssuePicker.actions.refresh': 'Rafraîchir',
|
||||
'session.gitlabIssuePicker.error.noActiveProject': 'Aucun projet actif',
|
||||
'session.gitlabIssuePicker.error.runtimeUnavailable': 'Exécution GitLab API indisponible',
|
||||
'session.gitlabIssuePicker.error.notConnected': 'GitLab non connecté',
|
||||
'session.gitlabIssuePicker.error.repoNotResolvable': 'Repo non résoluble',
|
||||
'session.gitlabIssuePicker.error.repoMustBeGitlab': 'Le dépôt distant origin doit pointer vers une URL GitLab',
|
||||
'session.gitlabIssuePicker.error.issueNotFound': 'Problème introuvable',
|
||||
'session.gitlabIssuePicker.error.noModelSelected': 'Aucun modèle sélectionné',
|
||||
'session.gitlabIssuePicker.toast.loadMoreFailed': 'Échec du chargement d\'autres problèmes',
|
||||
'session.gitlabIssuePicker.toast.loadIssueDetailsFailed': 'Échec du chargement des détails du problème',
|
||||
'session.gitlabIssuePicker.toast.sendContextFailed': 'Échec de l\'envoi du contexte du problème',
|
||||
'session.gitlabIssuePicker.toast.sessionCreated': 'Session créée à partir du problème',
|
||||
'session.gitlabIssuePicker.toast.startSessionFailed': 'Échec du démarrage de la session',
|
||||
'session.gitlabIssuePicker.title.select': 'Lien vers le problème GitLab',
|
||||
'session.gitlabIssuePicker.title.createSession': 'Nouvelle session du problème GitLab',
|
||||
'session.gitlabIssuePicker.description.select': 'Sélectionnez un problème à lier à cette session.',
|
||||
'session.gitlabIssuePicker.description.createSession': 'Lance une nouvelle session avec un contexte de problème masqué (titre/corps/étiquettes/commentaires).',
|
||||
'session.gitlabIssuePicker.searchPlaceholder': 'Recherchez par titre ou collez l\'URL du problème',
|
||||
'session.gitlabIssuePicker.empty.noActiveProject': 'Aucun projet actif sélectionné.',
|
||||
'session.gitlabIssuePicker.empty.runtimeUnavailable': 'L\'environnement d\'exécution GitLab API n\'est pas disponible.',
|
||||
'session.gitlabIssuePicker.empty.notConnected': 'GitLab non connecté. Connectez votre compte GitLab dans les paramètres.',
|
||||
'session.gitlabIssuePicker.empty.noIssuesFound': 'Aucun problème trouvé',
|
||||
'session.gitlabIssuePicker.empty.noOpenIssuesFound': 'Aucun problème ouvert trouvé',
|
||||
'session.gitlabIssuePicker.loading.issues': 'Chargement des problèmes...',
|
||||
'session.gitlabIssuePicker.loading.more': 'Chargement...',
|
||||
'session.gitlabIssuePicker.actions.openSettings': 'Ouvrir les paramètres',
|
||||
'session.gitlabIssuePicker.actions.useIssue': 'Utilisez le problème #{number}',
|
||||
'session.gitlabIssuePicker.actions.openInGitLabAria': 'Ouvrir dans GitLab',
|
||||
'session.gitlabIssuePicker.actions.loadMore': 'Charger plus',
|
||||
'session.gitlabIssuePicker.actions.sectionTitle': 'Actions',
|
||||
'session.gitlabIssuePicker.actions.toggleWorktreeAria': 'Basculer le worktree',
|
||||
'session.gitlabIssuePicker.actions.createInWorktree': 'Créer dans le worktree',
|
||||
'session.gitlabIssuePicker.actions.openRepo': 'Ouvrir le dépôt',
|
||||
'session.gitlabIssuePicker.actions.refresh': 'Rafraîchir',
|
||||
'session.githubPrPicker.error.noActiveProject': 'Aucun projet actif',
|
||||
'session.githubPrPicker.error.runtimeUnavailable': 'Exécution GitHub API indisponible',
|
||||
'session.githubPrPicker.error.notConnected': 'GitHub non connecté',
|
||||
@@ -1622,6 +1655,30 @@ export const dict = {
|
||||
'session.githubPrPicker.actions.usePullRequest': 'Utiliser la demande d\'extraction #{number}',
|
||||
'session.githubPrPicker.actions.openInGitHubAria': 'Ouvrir dans GitHub',
|
||||
'session.githubPrPicker.actions.loadMore': 'Charger plus',
|
||||
'session.gitlabMrPicker.error.noActiveProject': 'Aucun projet actif',
|
||||
'session.gitlabMrPicker.error.runtimeUnavailable': 'Exécution GitLab API indisponible',
|
||||
'session.gitlabMrPicker.error.notConnected': 'GitLab non connecté',
|
||||
'session.gitlabMrPicker.error.mrNotFound': 'Demande de fusion introuvable',
|
||||
'session.gitlabMrPicker.error.repoNotResolvable': 'Repo non résoluble',
|
||||
'session.gitlabMrPicker.error.repoMustBeGitlab': 'Le dépôt distant origin doit pointer vers une URL GitLab',
|
||||
'session.gitlabMrPicker.toast.loadMoreFailed': 'Échec du chargement d\'autres demandes de fusion',
|
||||
'session.gitlabMrPicker.toast.loadDetailsFailed': 'Échec du chargement des détails de la demande de fusion',
|
||||
'session.gitlabMrPicker.title': 'Lier le MR GitLab',
|
||||
'session.gitlabMrPicker.description': 'Sélectionnez un MR pour joindre un contexte de revue à ce message.',
|
||||
'session.gitlabMrPicker.searchPlaceholder': 'Recherchez par titre ou collez l\'URL du MR',
|
||||
'session.gitlabMrPicker.includeDiffAria': 'Inclure le diff du MR dans le contexte ci-joint',
|
||||
'session.gitlabMrPicker.includeDiff': 'Inclure le diff du MR',
|
||||
'session.gitlabMrPicker.empty.noActiveProject': 'Aucun projet actif sélectionné.',
|
||||
'session.gitlabMrPicker.empty.runtimeUnavailable': 'L\'environnement d\'exécution GitLab API n\'est pas disponible.',
|
||||
'session.gitlabMrPicker.empty.notConnected': 'GitLab non connecté. Connectez votre compte GitLab dans les paramètres.',
|
||||
'session.gitlabMrPicker.empty.noMergeRequestsFound': 'Aucune demande de fusion trouvée',
|
||||
'session.gitlabMrPicker.empty.noOpenMergeRequestsFound': 'Aucune demande de fusion ouverte trouvée',
|
||||
'session.gitlabMrPicker.loading.mergeRequests': 'Chargement des demandes de fusion...',
|
||||
'session.gitlabMrPicker.loading.more': 'Chargement...',
|
||||
'session.gitlabMrPicker.actions.openSettings': 'Ouvrir les paramètres',
|
||||
'session.gitlabMrPicker.actions.useMergeRequest': 'Utiliser le MR !{number}',
|
||||
'session.gitlabMrPicker.actions.openInGitLabAria': 'Ouvrir dans GitLab',
|
||||
'session.gitlabMrPicker.actions.loadMore': 'Charger plus',
|
||||
'session.newWorktree.title': 'Nouveau worktree',
|
||||
'session.newWorktree.mode.newBranch': 'Nouvelle branche',
|
||||
'session.newWorktree.mode.existingBranch': 'Branche existante',
|
||||
@@ -1903,6 +1960,8 @@ export const dict = {
|
||||
'chat.chatInput.actions.addAttachment': 'Ajouter une pièce jointe',
|
||||
'chat.chatInput.actions.linkGithubIssue': 'Lien vers le problème GitHub',
|
||||
'chat.chatInput.actions.linkGithubPr': 'Lien GitHub PR',
|
||||
'chat.chatInput.actions.linkGitlabIssue': 'Lien vers le problème GitLab',
|
||||
'chat.chatInput.actions.linkGitlabMr': 'Lien GitLab MR',
|
||||
'chat.chatInput.actions.modelAgentSettings': 'Paramètres du modèle et de l\'agent',
|
||||
'chat.chatInput.actions.sendMessageAria': 'Envoyer un message',
|
||||
'chat.chatInput.actions.queueMessageAria': 'Message de file d\'attente',
|
||||
|
||||
@@ -1836,6 +1836,39 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubIssuePicker.actions.createInWorktree': 'ワークツリーで作成',
|
||||
'session.githubIssuePicker.actions.openRepo': 'リポジトリを開く',
|
||||
'session.githubIssuePicker.actions.refresh': '更新',
|
||||
'session.gitlabIssuePicker.error.noActiveProject': 'アクティブなプロジェクトがありません',
|
||||
'session.gitlabIssuePicker.error.runtimeUnavailable': 'GitLabランタイムAPIは利用できません',
|
||||
'session.gitlabIssuePicker.error.notConnected': 'GitLabに接続されていません',
|
||||
'session.gitlabIssuePicker.error.repoNotResolvable': 'リポジトリを解決できません',
|
||||
'session.gitlabIssuePicker.error.repoMustBeGitlab': 'originリモートはGitLab URLである必要があります',
|
||||
'session.gitlabIssuePicker.error.issueNotFound': 'Issueが見つかりません',
|
||||
'session.gitlabIssuePicker.error.noModelSelected': 'モデルが選択されていません',
|
||||
'session.gitlabIssuePicker.toast.loadMoreFailed': 'さらにIssueを読み込めませんでした',
|
||||
'session.gitlabIssuePicker.toast.loadIssueDetailsFailed': 'Issueの詳細の読み込みに失敗しました',
|
||||
'session.gitlabIssuePicker.toast.sendContextFailed': 'Issueコンテキストの送信に失敗しました',
|
||||
'session.gitlabIssuePicker.toast.sessionCreated': 'Issueからセッションを作成しました',
|
||||
'session.gitlabIssuePicker.toast.startSessionFailed': 'セッションの開始に失敗しました',
|
||||
'session.gitlabIssuePicker.title.select': 'GitLab Issueをリンク',
|
||||
'session.gitlabIssuePicker.title.createSession': 'GitLab Issueから新しいセッション',
|
||||
'session.gitlabIssuePicker.description.select': 'このセッションにリンクするIssueを選択してください。',
|
||||
'session.gitlabIssuePicker.description.createSession': '非表示のIssueコンテキスト(タイトル/本文/ラベル/コメント)を含む新しいセッションを作成します。',
|
||||
'session.gitlabIssuePicker.searchPlaceholder': 'タイトルで検索するか、Issue URLを貼り付けてください',
|
||||
'session.gitlabIssuePicker.empty.noActiveProject': 'アクティブなプロジェクトが選択されていません。',
|
||||
'session.gitlabIssuePicker.empty.runtimeUnavailable': 'GitLabランタイムAPIは利用できません。',
|
||||
'session.gitlabIssuePicker.empty.notConnected': 'GitLabに接続されていません。設定でGitLabアカウントを接続してください。',
|
||||
'session.gitlabIssuePicker.empty.noIssuesFound': 'Issueが見つかりません',
|
||||
'session.gitlabIssuePicker.empty.noOpenIssuesFound': 'オープンなIssueが見つかりません',
|
||||
'session.gitlabIssuePicker.loading.issues': 'Issueを読み込み中...',
|
||||
'session.gitlabIssuePicker.loading.more': '読み込み中...',
|
||||
'session.gitlabIssuePicker.actions.openSettings': '設定を開く',
|
||||
'session.gitlabIssuePicker.actions.useIssue': 'Issue #{number}を使用',
|
||||
'session.gitlabIssuePicker.actions.openInGitLabAria': 'GitLabで開く',
|
||||
'session.gitlabIssuePicker.actions.loadMore': 'さらに読み込む',
|
||||
'session.gitlabIssuePicker.actions.sectionTitle': 'アクション',
|
||||
'session.gitlabIssuePicker.actions.toggleWorktreeAria': 'ワークツリーの切り替え',
|
||||
'session.gitlabIssuePicker.actions.createInWorktree': 'ワークツリーで作成',
|
||||
'session.gitlabIssuePicker.actions.openRepo': 'リポジトリを開く',
|
||||
'session.gitlabIssuePicker.actions.refresh': '更新',
|
||||
'session.githubPrPicker.error.noActiveProject': 'アクティブなプロジェクトがありません',
|
||||
'session.githubPrPicker.error.runtimeUnavailable': 'GitHubランタイムAPIは利用できません',
|
||||
'session.githubPrPicker.error.notConnected': 'GitHubに接続されていません',
|
||||
@@ -1860,6 +1893,30 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubPrPicker.actions.usePullRequest': 'プルリクエスト #{number}を使用',
|
||||
'session.githubPrPicker.actions.openInGitHubAria': 'GitHubで開く',
|
||||
'session.githubPrPicker.actions.loadMore': 'さらに読み込む',
|
||||
'session.gitlabMrPicker.error.noActiveProject': 'アクティブなプロジェクトがありません',
|
||||
'session.gitlabMrPicker.error.runtimeUnavailable': 'GitLabランタイムAPIは利用できません',
|
||||
'session.gitlabMrPicker.error.notConnected': 'GitLabに接続されていません',
|
||||
'session.gitlabMrPicker.error.mrNotFound': 'マージリクエストが見つかりません',
|
||||
'session.gitlabMrPicker.error.repoNotResolvable': 'リポジトリを解決できません',
|
||||
'session.gitlabMrPicker.error.repoMustBeGitlab': 'originリモートはGitLab URLである必要があります',
|
||||
'session.gitlabMrPicker.toast.loadMoreFailed': 'さらにマージリクエストを読み込めませんでした',
|
||||
'session.gitlabMrPicker.toast.loadDetailsFailed': 'マージリクエストの詳細の読み込みに失敗しました',
|
||||
'session.gitlabMrPicker.title': 'GitLabマージリクエストをリンク',
|
||||
'session.gitlabMrPicker.description': 'このメッセージにレビューコンテキストを添付するマージリクエストを選択してください。',
|
||||
'session.gitlabMrPicker.searchPlaceholder': 'タイトルで検索するか、マージリクエストURLを貼り付けてください',
|
||||
'session.gitlabMrPicker.includeDiffAria': '添付コンテキストにMR差分を含める',
|
||||
'session.gitlabMrPicker.includeDiff': 'MR差分を含める',
|
||||
'session.gitlabMrPicker.empty.noActiveProject': 'アクティブなプロジェクトが選択されていません。',
|
||||
'session.gitlabMrPicker.empty.runtimeUnavailable': 'GitLabランタイムAPIは利用できません。',
|
||||
'session.gitlabMrPicker.empty.notConnected': 'GitLabに接続されていません。設定でGitLabアカウントを接続してください。',
|
||||
'session.gitlabMrPicker.empty.noMergeRequestsFound': 'マージリクエストが見つかりません',
|
||||
'session.gitlabMrPicker.empty.noOpenMergeRequestsFound': 'オープンなマージリクエストが見つかりません',
|
||||
'session.gitlabMrPicker.loading.mergeRequests': 'マージリクエストを読み込み中...',
|
||||
'session.gitlabMrPicker.loading.more': '読み込み中...',
|
||||
'session.gitlabMrPicker.actions.openSettings': '設定を開く',
|
||||
'session.gitlabMrPicker.actions.useMergeRequest': 'マージリクエスト !{number}を使用',
|
||||
'session.gitlabMrPicker.actions.openInGitLabAria': 'GitLabで開く',
|
||||
'session.gitlabMrPicker.actions.loadMore': 'さらに読み込む',
|
||||
'session.newWorktree.title': '新しいワークツリー',
|
||||
'session.newWorktree.mode.newBranch': '新しいブランチ',
|
||||
'session.newWorktree.mode.existingBranch': '既存のブランチ',
|
||||
@@ -2178,6 +2235,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'chat.chatInput.actions.addAttachment': '添付ファイルを追加',
|
||||
'chat.chatInput.actions.linkGithubIssue': 'GitHub Issueをリンク',
|
||||
'chat.chatInput.actions.linkGithubPr': 'GitHub PRをリンク',
|
||||
'chat.chatInput.actions.linkGitlabIssue': 'GitLab Issueをリンク',
|
||||
'chat.chatInput.actions.linkGitlabMr': 'GitLab MRをリンク',
|
||||
'chat.chatInput.actions.modelAgentSettings': 'モデルとエージェント設定',
|
||||
'chat.chatInput.actions.sendMessageAria': 'メッセージを送信',
|
||||
'chat.chatInput.actions.queueMessageAria': 'メッセージをキュー',
|
||||
|
||||
@@ -1842,6 +1842,39 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubIssuePicker.actions.createInWorktree': '워크트리에서 생성',
|
||||
'session.githubIssuePicker.actions.openRepo': '레포지토리 열기',
|
||||
'session.githubIssuePicker.actions.refresh': '새로고침',
|
||||
'session.gitlabIssuePicker.error.noActiveProject': '활성 프로젝트가 없습니다',
|
||||
'session.gitlabIssuePicker.error.runtimeUnavailable': 'GitLab 런타임 API를 사용할 수 없습니다',
|
||||
'session.gitlabIssuePicker.error.notConnected': 'GitLab에 연결되어 있지 않습니다',
|
||||
'session.gitlabIssuePicker.error.repoNotResolvable': '저장소를 확인할 수 없습니다',
|
||||
'session.gitlabIssuePicker.error.repoMustBeGitlab': 'origin 리모트는 GitLab URL이어야 합니다',
|
||||
'session.gitlabIssuePicker.error.issueNotFound': '이슈를 찾을 수 없습니다',
|
||||
'session.gitlabIssuePicker.error.noModelSelected': '선택된 모델이 없습니다',
|
||||
'session.gitlabIssuePicker.toast.loadMoreFailed': '이슈를 더 불러오지 못했습니다',
|
||||
'session.gitlabIssuePicker.toast.loadIssueDetailsFailed': '이슈 상세 정보를 불러오지 못했습니다',
|
||||
'session.gitlabIssuePicker.toast.sendContextFailed': '이슈 컨텍스트 전송에 실패했습니다',
|
||||
'session.gitlabIssuePicker.toast.sessionCreated': '이슈에서 세션을 생성했습니다',
|
||||
'session.gitlabIssuePicker.toast.startSessionFailed': '세션 시작에 실패했습니다',
|
||||
'session.gitlabIssuePicker.title.select': 'GitLab 이슈 연결',
|
||||
'session.gitlabIssuePicker.title.createSession': 'GitLab 이슈로 새 세션 만들기',
|
||||
'session.gitlabIssuePicker.description.select': '이 세션에 연결할 이슈를 선택하세요.',
|
||||
'session.gitlabIssuePicker.description.createSession': '제목, 본문, 라벨, 댓글을 숨겨진 이슈 컨텍스트로 새 세션에 추가합니다.',
|
||||
'session.gitlabIssuePicker.searchPlaceholder': '제목으로 검색하거나 이슈 URL을 붙여넣으세요',
|
||||
'session.gitlabIssuePicker.empty.noActiveProject': '선택된 활성 프로젝트가 없습니다',
|
||||
'session.gitlabIssuePicker.empty.runtimeUnavailable': 'GitLab 런타임 API를 사용할 수 없습니다.',
|
||||
'session.gitlabIssuePicker.empty.notConnected': 'GitLab에 연결되지 않았습니다. 설정에서 GitLab 계정을 연결하세요.',
|
||||
'session.gitlabIssuePicker.empty.noIssuesFound': '이슈 없음',
|
||||
'session.gitlabIssuePicker.empty.noOpenIssuesFound': '열린 이슈가 없습니다',
|
||||
'session.gitlabIssuePicker.loading.issues': '이슈 로드 중…',
|
||||
'session.gitlabIssuePicker.loading.more': '로드 중…',
|
||||
'session.gitlabIssuePicker.actions.openSettings': '설정 열기',
|
||||
'session.gitlabIssuePicker.actions.useIssue': '이슈 #{number} 사용',
|
||||
'session.gitlabIssuePicker.actions.openInGitLabAria': 'GitLab에서 열기',
|
||||
'session.gitlabIssuePicker.actions.loadMore': '더 불러오기',
|
||||
'session.gitlabIssuePicker.actions.sectionTitle': '작업',
|
||||
'session.gitlabIssuePicker.actions.toggleWorktreeAria': '토글 워크트리',
|
||||
'session.gitlabIssuePicker.actions.createInWorktree': '워크트리에서 생성',
|
||||
'session.gitlabIssuePicker.actions.openRepo': '레포지토리 열기',
|
||||
'session.gitlabIssuePicker.actions.refresh': '새로고침',
|
||||
'session.githubPrPicker.error.noActiveProject': '활성 프로젝트가 없습니다',
|
||||
'session.githubPrPicker.error.runtimeUnavailable': 'GitHub 런타임 API를 사용할 수 없습니다',
|
||||
'session.githubPrPicker.error.notConnected': 'GitHub에 연결되어 있지 않습니다',
|
||||
@@ -1866,6 +1899,30 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubPrPicker.actions.usePullRequest': 'PR #{number} 사용',
|
||||
'session.githubPrPicker.actions.openInGitHubAria': 'GitHub에서 열기',
|
||||
'session.githubPrPicker.actions.loadMore': '더 불러오기',
|
||||
'session.gitlabMrPicker.error.noActiveProject': '활성 프로젝트가 없습니다',
|
||||
'session.gitlabMrPicker.error.runtimeUnavailable': 'GitLab 런타임 API를 사용할 수 없습니다',
|
||||
'session.gitlabMrPicker.error.notConnected': 'GitLab에 연결되어 있지 않습니다',
|
||||
'session.gitlabMrPicker.error.mrNotFound': 'MR을 찾을 수 없습니다',
|
||||
'session.gitlabMrPicker.error.repoNotResolvable': '저장소를 확인할 수 없습니다',
|
||||
'session.gitlabMrPicker.error.repoMustBeGitlab': 'origin 리모트는 GitLab URL이어야 합니다',
|
||||
'session.gitlabMrPicker.toast.loadMoreFailed': 'MR을 더 불러오지 못했습니다',
|
||||
'session.gitlabMrPicker.toast.loadDetailsFailed': 'MR 상세 정보를 불러오지 못했습니다',
|
||||
'session.gitlabMrPicker.title': 'GitLab MR 연결',
|
||||
'session.gitlabMrPicker.description': '이 메시지에 리뷰 컨텍스트로 첨부할 MR을 선택하세요.',
|
||||
'session.gitlabMrPicker.searchPlaceholder': '제목으로 검색하거나 MR URL을 붙여넣으세요',
|
||||
'session.gitlabMrPicker.includeDiffAria': 'MR 변경사항을 첨부 컨텍스트에 포함',
|
||||
'session.gitlabMrPicker.includeDiff': 'MR 변경사항 포함',
|
||||
'session.gitlabMrPicker.empty.noActiveProject': '선택된 활성 프로젝트가 없습니다',
|
||||
'session.gitlabMrPicker.empty.runtimeUnavailable': 'GitLab 런타임 API를 사용할 수 없습니다.',
|
||||
'session.gitlabMrPicker.empty.notConnected': 'GitLab에 연결되지 않았습니다. 설정에서 GitLab 계정을 연결하세요.',
|
||||
'session.gitlabMrPicker.empty.noMergeRequestsFound': 'MR이 없습니다',
|
||||
'session.gitlabMrPicker.empty.noOpenMergeRequestsFound': '열린 MR이 없습니다',
|
||||
'session.gitlabMrPicker.loading.mergeRequests': 'MR 불러오는 중…',
|
||||
'session.gitlabMrPicker.loading.more': '로드 중…',
|
||||
'session.gitlabMrPicker.actions.openSettings': '설정 열기',
|
||||
'session.gitlabMrPicker.actions.useMergeRequest': 'MR !{number} 사용',
|
||||
'session.gitlabMrPicker.actions.openInGitLabAria': 'GitLab에서 열기',
|
||||
'session.gitlabMrPicker.actions.loadMore': '더 불러오기',
|
||||
'session.newWorktree.title': '새 워크트리',
|
||||
'session.newWorktree.mode.newBranch': '새 브랜치',
|
||||
'session.newWorktree.mode.existingBranch': '기존 브랜치',
|
||||
@@ -2182,6 +2239,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'chat.chatInput.actions.addAttachment': '첨부 파일 추가',
|
||||
'chat.chatInput.actions.linkGithubIssue': 'GitHub 이슈 연결',
|
||||
'chat.chatInput.actions.linkGithubPr': 'GitHub PR 연결',
|
||||
'chat.chatInput.actions.linkGitlabIssue': 'GitLab 이슈 연결',
|
||||
'chat.chatInput.actions.linkGitlabMr': 'GitLab MR 연결',
|
||||
'chat.chatInput.actions.modelAgentSettings': '모델 및 에이전트 설정',
|
||||
'chat.chatInput.actions.sendMessageAria': '보내기 메시지',
|
||||
'chat.chatInput.actions.queueMessageAria': '메시지 대기열에 추가',
|
||||
|
||||
@@ -1202,6 +1202,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'chat.chatInput.actions.commands': 'Commands',
|
||||
'chat.chatInput.actions.linkGithubIssue': 'Link GitHub Issue',
|
||||
'chat.chatInput.actions.linkGithubPr': 'Link GitHub PR',
|
||||
'chat.chatInput.actions.linkGitlabIssue': 'Połącz zgłoszenie GitLab',
|
||||
'chat.chatInput.actions.linkGitlabMr': 'Połącz merge request GitLab',
|
||||
'chat.chatInput.actions.modelAgentSettings': 'Model and agent settings',
|
||||
'chat.chatInput.actions.queueMessageAria': 'Queue message',
|
||||
'chat.chatInput.actions.sendMessageAria': 'Send message',
|
||||
@@ -2694,6 +2696,39 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubIssuePicker.toast.sendContextFailed': 'Nie udało się wysłać kontekstu zgłoszenia',
|
||||
'session.githubIssuePicker.toast.sessionCreated': 'Sesja utworzona ze zgłoszenia',
|
||||
'session.githubIssuePicker.toast.startSessionFailed': 'Nie udało się uruchomić sesji',
|
||||
'session.gitlabIssuePicker.actions.createInWorktree': 'Utwórz w drzewie pracy',
|
||||
'session.gitlabIssuePicker.actions.loadMore': 'Załaduj więcej',
|
||||
'session.gitlabIssuePicker.actions.openInGitLabAria': 'Otwórz w GitLab',
|
||||
'session.gitlabIssuePicker.actions.openRepo': 'Otwórz repozytorium',
|
||||
'session.gitlabIssuePicker.actions.openSettings': 'Otwórz ustawienia',
|
||||
'session.gitlabIssuePicker.actions.refresh': 'Odśwież',
|
||||
'session.gitlabIssuePicker.actions.sectionTitle': 'Akcje',
|
||||
'session.gitlabIssuePicker.actions.toggleWorktreeAria': 'Przełącz drzewo pracy',
|
||||
'session.gitlabIssuePicker.actions.useIssue': 'Użyj zgłoszenia #{number}',
|
||||
'session.gitlabIssuePicker.description.createSession': 'Tworzy nową sesję z ukrytym kontekstem zgłoszenia (tytuł/treść/etykiety/komentarze).',
|
||||
'session.gitlabIssuePicker.description.select': 'Wybierz zgłoszenie, które chcesz powiązać z tą sesją.',
|
||||
'session.gitlabIssuePicker.empty.noActiveProject': 'Nie wybrano aktywnego projektu.',
|
||||
'session.gitlabIssuePicker.empty.noIssuesFound': 'Nie znaleziono zgłoszeń',
|
||||
'session.gitlabIssuePicker.empty.noOpenIssuesFound': 'Nie znaleziono otwartych zgłoszeń',
|
||||
'session.gitlabIssuePicker.empty.notConnected': 'GitLab nie jest połączony. Połącz konto GitLab w ustawieniach.',
|
||||
'session.gitlabIssuePicker.empty.runtimeUnavailable': 'API środowiska GitLab jest niedostępne.',
|
||||
'session.gitlabIssuePicker.error.issueNotFound': 'Nie znaleziono zgłoszenia',
|
||||
'session.gitlabIssuePicker.error.noActiveProject': 'Brak aktywnego projektu',
|
||||
'session.gitlabIssuePicker.error.noModelSelected': 'Nie wybrano modelu',
|
||||
'session.gitlabIssuePicker.error.notConnected': 'GitLab nie jest połączony',
|
||||
'session.gitlabIssuePicker.error.repoMustBeGitlab': 'remote origin musi być adresem URL GitLab',
|
||||
'session.gitlabIssuePicker.error.repoNotResolvable': 'Nie udało się rozpoznać repozytorium',
|
||||
'session.gitlabIssuePicker.error.runtimeUnavailable': 'API środowiska GitLab jest niedostępne',
|
||||
'session.gitlabIssuePicker.loading.issues': 'Ładowanie zgłoszeń...',
|
||||
'session.gitlabIssuePicker.loading.more': 'Ładowanie...',
|
||||
'session.gitlabIssuePicker.searchPlaceholder': 'Szukaj po tytule lub wklej adres URL zgłoszenia',
|
||||
'session.gitlabIssuePicker.title.createSession': 'Nowa sesja ze zgłoszenia GitLab',
|
||||
'session.gitlabIssuePicker.title.select': 'Połącz zgłoszenie GitLab',
|
||||
'session.gitlabIssuePicker.toast.loadIssueDetailsFailed': 'Nie udało się załadować szczegółów zgłoszenia',
|
||||
'session.gitlabIssuePicker.toast.loadMoreFailed': 'Nie udało się załadować kolejnych zgłoszeń',
|
||||
'session.gitlabIssuePicker.toast.sendContextFailed': 'Nie udało się wysłać kontekstu zgłoszenia',
|
||||
'session.gitlabIssuePicker.toast.sessionCreated': 'Sesja utworzona ze zgłoszenia',
|
||||
'session.gitlabIssuePicker.toast.startSessionFailed': 'Nie udało się uruchomić sesji',
|
||||
'session.githubPrPicker.actions.loadMore': 'Załaduj więcej',
|
||||
'session.githubPrPicker.actions.openInGitHubAria': 'Otwórz w GitHub',
|
||||
'session.githubPrPicker.actions.openSettings': 'Otwórz ustawienia',
|
||||
@@ -2718,6 +2753,30 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubPrPicker.title': 'Połącz pull request GitHub',
|
||||
'session.githubPrPicker.toast.loadDetailsFailed': 'Nie udało się załadować szczegółów pull requesta',
|
||||
'session.githubPrPicker.toast.loadMoreFailed': 'Nie udało się załadować kolejnych pull requestów',
|
||||
'session.gitlabMrPicker.actions.loadMore': 'Załaduj więcej',
|
||||
'session.gitlabMrPicker.actions.openInGitLabAria': 'Otwórz w GitLab',
|
||||
'session.gitlabMrPicker.actions.openSettings': 'Otwórz ustawienia',
|
||||
'session.gitlabMrPicker.actions.useMergeRequest': 'Użyj merge requesta !{number}',
|
||||
'session.gitlabMrPicker.description': 'Wybierz merge request, aby dołączyć kontekst recenzji do tej wiadomości.',
|
||||
'session.gitlabMrPicker.empty.noActiveProject': 'Nie wybrano aktywnego projektu.',
|
||||
'session.gitlabMrPicker.empty.noMergeRequestsFound': 'Nie znaleziono merge requestów',
|
||||
'session.gitlabMrPicker.empty.noOpenMergeRequestsFound': 'Nie znaleziono otwartych merge requestów',
|
||||
'session.gitlabMrPicker.empty.notConnected': 'GitLab nie jest połączony. Połącz konto GitLab w ustawieniach.',
|
||||
'session.gitlabMrPicker.empty.runtimeUnavailable': 'API środowiska GitLab jest niedostępne.',
|
||||
'session.gitlabMrPicker.error.mrNotFound': 'Nie znaleziono merge requesta',
|
||||
'session.gitlabMrPicker.error.noActiveProject': 'Brak aktywnego projektu',
|
||||
'session.gitlabMrPicker.error.notConnected': 'GitLab nie jest połączony',
|
||||
'session.gitlabMrPicker.error.repoMustBeGitlab': 'remote origin musi być adresem URL GitLab',
|
||||
'session.gitlabMrPicker.error.repoNotResolvable': 'Nie udało się rozpoznać repozytorium',
|
||||
'session.gitlabMrPicker.error.runtimeUnavailable': 'API środowiska GitLab jest niedostępne',
|
||||
'session.gitlabMrPicker.includeDiff': 'Dołącz diff MR',
|
||||
'session.gitlabMrPicker.includeDiffAria': 'Dołącz diff MR do załączonego kontekstu',
|
||||
'session.gitlabMrPicker.loading.mergeRequests': 'Ładowanie merge requestów...',
|
||||
'session.gitlabMrPicker.loading.more': 'Ładowanie...',
|
||||
'session.gitlabMrPicker.searchPlaceholder': 'Szukaj po tytule lub wklej adres URL merge requesta',
|
||||
'session.gitlabMrPicker.title': 'Połącz merge request GitLab',
|
||||
'session.gitlabMrPicker.toast.loadDetailsFailed': 'Nie udało się załadować szczegółów merge requesta',
|
||||
'session.gitlabMrPicker.toast.loadMoreFailed': 'Nie udało się załadować kolejnych merge requestów',
|
||||
'session.newWorktree.actions.cancel': 'Anuluj',
|
||||
'session.newWorktree.actions.change': 'Zmień',
|
||||
'session.newWorktree.actions.createWorktree': 'Utwórz drzewo pracy',
|
||||
|
||||
@@ -1818,6 +1818,39 @@ export const dict: Record<I18nKey, string> = {
|
||||
"session.githubIssuePicker.actions.createInWorktree": "Criar em worktree",
|
||||
"session.githubIssuePicker.actions.openRepo": "Abrir repositório",
|
||||
"session.githubIssuePicker.actions.refresh": "Atualizar",
|
||||
"session.gitlabIssuePicker.error.noActiveProject": "Não há nenhum projeto ativo",
|
||||
"session.gitlabIssuePicker.error.runtimeUnavailable": "API de runtime de GitLab não disponível",
|
||||
"session.gitlabIssuePicker.error.notConnected": "GitLab não está conectado",
|
||||
"session.gitlabIssuePicker.error.repoNotResolvable": "Não foi possível resolver o repositório",
|
||||
"session.gitlabIssuePicker.error.repoMustBeGitlab": "O remoto origin deve ser uma URL de GitLab",
|
||||
"session.gitlabIssuePicker.error.issueNotFound": "Issue não encontrada",
|
||||
"session.gitlabIssuePicker.error.noModelSelected": "Não há nenhum modelo selecionado",
|
||||
"session.gitlabIssuePicker.toast.loadMoreFailed": "Não foi possível carregar mais issues",
|
||||
"session.gitlabIssuePicker.toast.loadIssueDetailsFailed": "Não foi possível carregar os detalhes da issue",
|
||||
"session.gitlabIssuePicker.toast.sendContextFailed": "Não foi possível enviar o contexto da issue",
|
||||
"session.gitlabIssuePicker.toast.sessionCreated": "Sessão criada a partir da issue",
|
||||
"session.gitlabIssuePicker.toast.startSessionFailed": "Não foi possível iniciar a sessão",
|
||||
"session.gitlabIssuePicker.title.select": "Vincular issue de GitLab",
|
||||
"session.gitlabIssuePicker.title.createSession": "Nova sessão de issue de GitLab",
|
||||
"session.gitlabIssuePicker.description.select": "Selecione uma issue para vinculá-la a esta sessão.",
|
||||
"session.gitlabIssuePicker.description.createSession": "Inicie uma nova sessão com contexto oculto do issue (título/corpo/etiquetas/comentários).",
|
||||
"session.gitlabIssuePicker.searchPlaceholder": "Pesquisar por título ou cole a URL da issue",
|
||||
"session.gitlabIssuePicker.empty.noActiveProject": "Não há nenhum projeto ativo selecionado.",
|
||||
"session.gitlabIssuePicker.empty.runtimeUnavailable": "API de runtime de GitLab não disponível.",
|
||||
"session.gitlabIssuePicker.empty.notConnected": "GitLab não está conectado. Conecte sua conta do GitLab nas configurações.",
|
||||
"session.gitlabIssuePicker.empty.noIssuesFound": "Nenhuma issue encontrada",
|
||||
"session.gitlabIssuePicker.empty.noOpenIssuesFound": "Nenhuma issue aberta encontrada",
|
||||
"session.gitlabIssuePicker.loading.issues": "Carregando issues...",
|
||||
"session.gitlabIssuePicker.loading.more": "Carregando...",
|
||||
"session.gitlabIssuePicker.actions.openSettings": "Abrir configurações",
|
||||
"session.gitlabIssuePicker.actions.useIssue": "Usar issue #{number}",
|
||||
"session.gitlabIssuePicker.actions.openInGitLabAria": "Abrir em GitLab",
|
||||
"session.gitlabIssuePicker.actions.loadMore": "Carregar mais",
|
||||
"session.gitlabIssuePicker.actions.sectionTitle": "Ações",
|
||||
"session.gitlabIssuePicker.actions.toggleWorktreeAria": "Ativar worktree",
|
||||
"session.gitlabIssuePicker.actions.createInWorktree": "Criar em worktree",
|
||||
"session.gitlabIssuePicker.actions.openRepo": "Abrir repositório",
|
||||
"session.gitlabIssuePicker.actions.refresh": "Atualizar",
|
||||
"session.githubPrPicker.error.noActiveProject": "Não há nenhum projeto ativo",
|
||||
"session.githubPrPicker.error.runtimeUnavailable": "API de runtime de GitHub não disponível",
|
||||
"session.githubPrPicker.error.notConnected": "GitHub não está conectado",
|
||||
@@ -1842,6 +1875,30 @@ export const dict: Record<I18nKey, string> = {
|
||||
"session.githubPrPicker.actions.usePullRequest": "Usar PR #{number}",
|
||||
"session.githubPrPicker.actions.openInGitHubAria": "Abrir em GitHub",
|
||||
"session.githubPrPicker.actions.loadMore": "Carregar mais",
|
||||
"session.gitlabMrPicker.error.noActiveProject": "Não há nenhum projeto ativo",
|
||||
"session.gitlabMrPicker.error.runtimeUnavailable": "API de runtime de GitLab não disponível",
|
||||
"session.gitlabMrPicker.error.notConnected": "GitLab não está conectado",
|
||||
"session.gitlabMrPicker.error.mrNotFound": "MR não encontrado",
|
||||
"session.gitlabMrPicker.error.repoNotResolvable": "Não foi possível resolver o repositório",
|
||||
"session.gitlabMrPicker.error.repoMustBeGitlab": "O remoto origin deve ser uma URL de GitLab",
|
||||
"session.gitlabMrPicker.toast.loadMoreFailed": "Não foi possível carregar mais MRs",
|
||||
"session.gitlabMrPicker.toast.loadDetailsFailed": "Não foi possível carregar os detalhes do MR",
|
||||
"session.gitlabMrPicker.title": "Vincular MR de GitLab",
|
||||
"session.gitlabMrPicker.description": "Selecione um MR para anexar contexto de revisão a esta mensagem.",
|
||||
"session.gitlabMrPicker.searchPlaceholder": "Pesquisar por título ou cole a URL do MR",
|
||||
"session.gitlabMrPicker.includeDiffAria": "Incluir diff do MR no contexto adjunto",
|
||||
"session.gitlabMrPicker.includeDiff": "Incluir diff do MR",
|
||||
"session.gitlabMrPicker.empty.noActiveProject": "Não há nenhum projeto ativo selecionado.",
|
||||
"session.gitlabMrPicker.empty.runtimeUnavailable": "API de runtime de GitLab não disponível.",
|
||||
"session.gitlabMrPicker.empty.notConnected": "GitLab não está conectado. Conecte sua conta do GitLab nas configurações.",
|
||||
"session.gitlabMrPicker.empty.noMergeRequestsFound": "Nenhum MR encontrado",
|
||||
"session.gitlabMrPicker.empty.noOpenMergeRequestsFound": "Nenhum MR aberto encontrado",
|
||||
"session.gitlabMrPicker.loading.mergeRequests": "Carregando MRs...",
|
||||
"session.gitlabMrPicker.loading.more": "Carregando...",
|
||||
"session.gitlabMrPicker.actions.openSettings": "Abrir configurações",
|
||||
"session.gitlabMrPicker.actions.useMergeRequest": "Usar MR !{number}",
|
||||
"session.gitlabMrPicker.actions.openInGitLabAria": "Abrir em GitLab",
|
||||
"session.gitlabMrPicker.actions.loadMore": "Carregar mais",
|
||||
"session.newWorktree.title": "Novo worktree",
|
||||
"session.newWorktree.mode.newBranch": "Nova branch",
|
||||
"session.newWorktree.mode.existingBranch": "Branch existente",
|
||||
@@ -2160,6 +2217,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
"chat.chatInput.actions.addAttachment": "Adicionar adjunto",
|
||||
"chat.chatInput.actions.linkGithubIssue": "Vincular issue de GitHub",
|
||||
"chat.chatInput.actions.linkGithubPr": "Vincular PR de GitHub",
|
||||
"chat.chatInput.actions.linkGitlabIssue": "Vincular issue de GitLab",
|
||||
"chat.chatInput.actions.linkGitlabMr": "Vincular MR de GitLab",
|
||||
"chat.chatInput.actions.modelAgentSettings": "Configurações de modelo e agente",
|
||||
"chat.chatInput.actions.sendMessageAria": "Enviar mensagem",
|
||||
"chat.chatInput.actions.queueMessageAria": "Colocar mensagem na fila",
|
||||
|
||||
@@ -1818,6 +1818,39 @@ export const dict: Record<I18nKey, string> = {
|
||||
"session.githubIssuePicker.actions.createInWorktree": "Створити в worktree",
|
||||
"session.githubIssuePicker.actions.openRepo": "Відкрити репозиторій",
|
||||
"session.githubIssuePicker.actions.refresh": "Оновити",
|
||||
"session.gitlabIssuePicker.error.noActiveProject": "Немає активного проєкту",
|
||||
"session.gitlabIssuePicker.error.runtimeUnavailable": "GitLab API недоступний",
|
||||
"session.gitlabIssuePicker.error.notConnected": "GitLab не підключено",
|
||||
"session.gitlabIssuePicker.error.repoNotResolvable": "Не вдалося визначити репозиторій",
|
||||
"session.gitlabIssuePicker.error.repoMustBeGitlab": "remote origin має бути GitLab URL",
|
||||
"session.gitlabIssuePicker.error.issueNotFound": "Issue не знайдено",
|
||||
"session.gitlabIssuePicker.error.noModelSelected": "Модель не вибрано",
|
||||
"session.gitlabIssuePicker.toast.loadMoreFailed": "Не вдалося завантажити додаткові issue",
|
||||
"session.gitlabIssuePicker.toast.loadIssueDetailsFailed": "Не вдалося завантажити деталі issue",
|
||||
"session.gitlabIssuePicker.toast.sendContextFailed": "Не вдалося надіслати контекст issue",
|
||||
"session.gitlabIssuePicker.toast.sessionCreated": "Сесію створено з issue",
|
||||
"session.gitlabIssuePicker.toast.startSessionFailed": "Не вдалося почати сесію",
|
||||
"session.gitlabIssuePicker.title.select": "Пов’язати GitLab issue",
|
||||
"session.gitlabIssuePicker.title.createSession": "Нова сесія з GitLab issue",
|
||||
"session.gitlabIssuePicker.description.select": "Виберіть issue, щоб пов’язати її з цією сесією.",
|
||||
"session.gitlabIssuePicker.description.createSession": "Запускає нову сесію із прихованим контекстом issue: заголовком, текстом, мітками й коментарями.",
|
||||
"session.gitlabIssuePicker.searchPlaceholder": "Пошук за назвою або вставте URL issue",
|
||||
"session.gitlabIssuePicker.empty.noActiveProject": "Не вибрано жодного активного проєкту.",
|
||||
"session.gitlabIssuePicker.empty.runtimeUnavailable": "GitLab API недоступний.",
|
||||
"session.gitlabIssuePicker.empty.notConnected": "GitLab не підключено. Підключіть обліковий запис GitLab у налаштуваннях.",
|
||||
"session.gitlabIssuePicker.empty.noIssuesFound": "Issue не знайдено",
|
||||
"session.gitlabIssuePicker.empty.noOpenIssuesFound": "Відкритих issue не знайдено",
|
||||
"session.gitlabIssuePicker.loading.issues": "Завантаження issue...",
|
||||
"session.gitlabIssuePicker.loading.more": "Завантаження...",
|
||||
"session.gitlabIssuePicker.actions.openSettings": "Відкрити налаштування",
|
||||
"session.gitlabIssuePicker.actions.useIssue": "Використовувати issue №{number}",
|
||||
"session.gitlabIssuePicker.actions.openInGitLabAria": "Відкрити в GitLab",
|
||||
"session.gitlabIssuePicker.actions.loadMore": "Завантажити ще",
|
||||
"session.gitlabIssuePicker.actions.sectionTitle": "Дії",
|
||||
"session.gitlabIssuePicker.actions.toggleWorktreeAria": "Перемкнути worktree",
|
||||
"session.gitlabIssuePicker.actions.createInWorktree": "Створити в worktree",
|
||||
"session.gitlabIssuePicker.actions.openRepo": "Відкрити репозиторій",
|
||||
"session.gitlabIssuePicker.actions.refresh": "Оновити",
|
||||
"session.githubPrPicker.error.noActiveProject": "Немає активного проєкту",
|
||||
"session.githubPrPicker.error.runtimeUnavailable": "GitHub API недоступний",
|
||||
"session.githubPrPicker.error.notConnected": "GitHub не підключено",
|
||||
@@ -1842,6 +1875,30 @@ export const dict: Record<I18nKey, string> = {
|
||||
"session.githubPrPicker.actions.usePullRequest": "Використовувати PR #{number}",
|
||||
"session.githubPrPicker.actions.openInGitHubAria": "Відкрити в GitHub",
|
||||
"session.githubPrPicker.actions.loadMore": "Завантажити ще",
|
||||
"session.gitlabMrPicker.error.noActiveProject": "Немає активного проєкту",
|
||||
"session.gitlabMrPicker.error.runtimeUnavailable": "GitLab API недоступний",
|
||||
"session.gitlabMrPicker.error.notConnected": "GitLab не підключено",
|
||||
"session.gitlabMrPicker.error.mrNotFound": "MR не знайдено",
|
||||
"session.gitlabMrPicker.error.repoNotResolvable": "Не вдалося визначити репозиторій",
|
||||
"session.gitlabMrPicker.error.repoMustBeGitlab": "remote origin має бути GitLab URL",
|
||||
"session.gitlabMrPicker.toast.loadMoreFailed": "Не вдалося завантажити додаткові MR",
|
||||
"session.gitlabMrPicker.toast.loadDetailsFailed": "Не вдалося завантажити деталі MR",
|
||||
"session.gitlabMrPicker.title": "Пов’язати GitLab MR",
|
||||
"session.gitlabMrPicker.description": "Виберіть MR, щоб додати контекст рев’ю до цього повідомлення.",
|
||||
"session.gitlabMrPicker.searchPlaceholder": "Пошук за назвою або вставте URL MR",
|
||||
"session.gitlabMrPicker.includeDiffAria": "Додати diff MR у вкладений контекст",
|
||||
"session.gitlabMrPicker.includeDiff": "Додати diff MR",
|
||||
"session.gitlabMrPicker.empty.noActiveProject": "Не вибрано жодного активного проєкту.",
|
||||
"session.gitlabMrPicker.empty.runtimeUnavailable": "GitLab API недоступний.",
|
||||
"session.gitlabMrPicker.empty.notConnected": "GitLab не підключено. Підключіть обліковий запис GitLab у налаштуваннях.",
|
||||
"session.gitlabMrPicker.empty.noMergeRequestsFound": "MR не знайдено",
|
||||
"session.gitlabMrPicker.empty.noOpenMergeRequestsFound": "Не знайдено відкритих MR",
|
||||
"session.gitlabMrPicker.loading.mergeRequests": "Завантаження MR...",
|
||||
"session.gitlabMrPicker.loading.more": "Завантаження...",
|
||||
"session.gitlabMrPicker.actions.openSettings": "Відкрити налаштування",
|
||||
"session.gitlabMrPicker.actions.useMergeRequest": "Використовувати MR !{number}",
|
||||
"session.gitlabMrPicker.actions.openInGitLabAria": "Відкрити в GitLab",
|
||||
"session.gitlabMrPicker.actions.loadMore": "Завантажити ще",
|
||||
"session.newWorktree.title": "Нове worktree",
|
||||
"session.newWorktree.mode.newBranch": "Нова гілка",
|
||||
"session.newWorktree.mode.existingBranch": "Наявна гілка",
|
||||
@@ -2160,6 +2217,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
"chat.chatInput.actions.addAttachment": "Додати вкладення",
|
||||
"chat.chatInput.actions.linkGithubIssue": "Пов’язати GitHub issue",
|
||||
"chat.chatInput.actions.linkGithubPr": "Пов’язати GitHub PR",
|
||||
"chat.chatInput.actions.linkGitlabIssue": "Пов’язати GitLab issue",
|
||||
"chat.chatInput.actions.linkGitlabMr": "Пов’язати GitLab MR",
|
||||
"chat.chatInput.actions.modelAgentSettings": "Параметри моделі та агента",
|
||||
"chat.chatInput.actions.sendMessageAria": "Надіслати повідомлення",
|
||||
"chat.chatInput.actions.queueMessageAria": "Поставити повідомлення в чергу",
|
||||
|
||||
@@ -1806,6 +1806,39 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubIssuePicker.actions.createInWorktree': '在工作树中创建',
|
||||
'session.githubIssuePicker.actions.openRepo': '打开仓库',
|
||||
'session.githubIssuePicker.actions.refresh': '刷新',
|
||||
'session.gitlabIssuePicker.error.noActiveProject': '没有活动项目',
|
||||
'session.gitlabIssuePicker.error.runtimeUnavailable': 'GitLab 运行时 API 不可用',
|
||||
'session.gitlabIssuePicker.error.notConnected': 'GitLab 未连接',
|
||||
'session.gitlabIssuePicker.error.repoNotResolvable': '无法解析仓库',
|
||||
'session.gitlabIssuePicker.error.repoMustBeGitlab': 'origin 远程仓库必须是 GitLab URL',
|
||||
'session.gitlabIssuePicker.error.issueNotFound': '未找到 Issue',
|
||||
'session.gitlabIssuePicker.error.noModelSelected': '未选择模型',
|
||||
'session.gitlabIssuePicker.toast.loadMoreFailed': '加载更多 Issue 失败',
|
||||
'session.gitlabIssuePicker.toast.loadIssueDetailsFailed': '加载 Issue 详情失败',
|
||||
'session.gitlabIssuePicker.toast.sendContextFailed': '发送 Issue 上下文失败',
|
||||
'session.gitlabIssuePicker.toast.sessionCreated': '已从 Issue 创建会话',
|
||||
'session.gitlabIssuePicker.toast.startSessionFailed': '启动会话失败',
|
||||
'session.gitlabIssuePicker.title.select': '关联 GitLab Issue',
|
||||
'session.gitlabIssuePicker.title.createSession': '从 GitLab Issue 新建会话',
|
||||
'session.gitlabIssuePicker.description.select': '选择一个 Issue 关联到当前会话。',
|
||||
'session.gitlabIssuePicker.description.createSession': '使用隐藏的 Issue 上下文(标题/正文/标签/评论)初始化新会话。',
|
||||
'session.gitlabIssuePicker.searchPlaceholder': '按标题搜索,或粘贴 Issue URL',
|
||||
'session.gitlabIssuePicker.empty.noActiveProject': '未选择活动项目。',
|
||||
'session.gitlabIssuePicker.empty.runtimeUnavailable': 'GitLab 运行时 API 不可用。',
|
||||
'session.gitlabIssuePicker.empty.notConnected': 'GitLab 未连接。请在设置中连接 GitLab 账号。',
|
||||
'session.gitlabIssuePicker.empty.noIssuesFound': '未找到 Issue',
|
||||
'session.gitlabIssuePicker.empty.noOpenIssuesFound': '没有未关闭的 Issue',
|
||||
'session.gitlabIssuePicker.loading.issues': '正在加载 Issue...',
|
||||
'session.gitlabIssuePicker.loading.more': '加载中...',
|
||||
'session.gitlabIssuePicker.actions.openSettings': '打开设置',
|
||||
'session.gitlabIssuePicker.actions.useIssue': '使用 Issue #{number}',
|
||||
'session.gitlabIssuePicker.actions.openInGitLabAria': '在 GitLab 中打开',
|
||||
'session.gitlabIssuePicker.actions.loadMore': '加载更多',
|
||||
'session.gitlabIssuePicker.actions.sectionTitle': '操作',
|
||||
'session.gitlabIssuePicker.actions.toggleWorktreeAria': '切换工作树',
|
||||
'session.gitlabIssuePicker.actions.createInWorktree': '在工作树中创建',
|
||||
'session.gitlabIssuePicker.actions.openRepo': '打开仓库',
|
||||
'session.gitlabIssuePicker.actions.refresh': '刷新',
|
||||
'session.githubPrPicker.error.noActiveProject': '没有活动项目',
|
||||
'session.githubPrPicker.error.runtimeUnavailable': 'GitHub 运行时 API 不可用',
|
||||
'session.githubPrPicker.error.notConnected': 'GitHub 未连接',
|
||||
@@ -1830,6 +1863,30 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubPrPicker.actions.usePullRequest': '使用 Pull Request #{number}',
|
||||
'session.githubPrPicker.actions.openInGitHubAria': '在 GitHub 中打开',
|
||||
'session.githubPrPicker.actions.loadMore': '加载更多',
|
||||
'session.gitlabMrPicker.error.noActiveProject': '没有活动项目',
|
||||
'session.gitlabMrPicker.error.runtimeUnavailable': 'GitLab 运行时 API 不可用',
|
||||
'session.gitlabMrPicker.error.notConnected': 'GitLab 未连接',
|
||||
'session.gitlabMrPicker.error.mrNotFound': '未找到 Merge Request',
|
||||
'session.gitlabMrPicker.error.repoNotResolvable': '无法解析仓库',
|
||||
'session.gitlabMrPicker.error.repoMustBeGitlab': 'origin 远程仓库必须是 GitLab URL',
|
||||
'session.gitlabMrPicker.toast.loadMoreFailed': '加载更多 Merge Request 失败',
|
||||
'session.gitlabMrPicker.toast.loadDetailsFailed': '加载 Merge Request 详情失败',
|
||||
'session.gitlabMrPicker.title': '关联 GitLab Merge Request',
|
||||
'session.gitlabMrPicker.description': '选择一个 Merge Request,将审查上下文附加到此消息。',
|
||||
'session.gitlabMrPicker.searchPlaceholder': '按标题搜索,或粘贴 Merge Request URL',
|
||||
'session.gitlabMrPicker.includeDiffAria': '在附加上下文中包含 MR 差异',
|
||||
'session.gitlabMrPicker.includeDiff': '包含 MR 差异',
|
||||
'session.gitlabMrPicker.empty.noActiveProject': '未选择活动项目。',
|
||||
'session.gitlabMrPicker.empty.runtimeUnavailable': 'GitLab 运行时 API 不可用。',
|
||||
'session.gitlabMrPicker.empty.notConnected': 'GitLab 未连接。请在设置中连接 GitLab 账号。',
|
||||
'session.gitlabMrPicker.empty.noMergeRequestsFound': '未找到 Merge Request',
|
||||
'session.gitlabMrPicker.empty.noOpenMergeRequestsFound': '没有未关闭的 Merge Request',
|
||||
'session.gitlabMrPicker.loading.mergeRequests': '正在加载 Merge Request...',
|
||||
'session.gitlabMrPicker.loading.more': '加载中...',
|
||||
'session.gitlabMrPicker.actions.openSettings': '打开设置',
|
||||
'session.gitlabMrPicker.actions.useMergeRequest': '使用 Merge Request !{number}',
|
||||
'session.gitlabMrPicker.actions.openInGitLabAria': '在 GitLab 中打开',
|
||||
'session.gitlabMrPicker.actions.loadMore': '加载更多',
|
||||
'session.newWorktree.title': '新建工作树',
|
||||
'session.newWorktree.mode.newBranch': '新分支',
|
||||
'session.newWorktree.mode.existingBranch': '现有分支',
|
||||
@@ -2148,6 +2205,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'chat.chatInput.actions.addAttachment': '添加附件',
|
||||
'chat.chatInput.actions.linkGithubIssue': '关联 GitHub Issue',
|
||||
'chat.chatInput.actions.linkGithubPr': '关联 GitHub PR',
|
||||
'chat.chatInput.actions.linkGitlabIssue': '关联 GitLab Issue',
|
||||
'chat.chatInput.actions.linkGitlabMr': '关联 GitLab MR',
|
||||
'chat.chatInput.actions.modelAgentSettings': '模型与智能体设置',
|
||||
'chat.chatInput.actions.sendMessageAria': '发送消息',
|
||||
'chat.chatInput.actions.queueMessageAria': '将消息加入队列',
|
||||
|
||||
@@ -1810,6 +1810,39 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubIssuePicker.actions.createInWorktree': '在 worktree 中建立',
|
||||
'session.githubIssuePicker.actions.openRepo': '開啟儲存庫',
|
||||
'session.githubIssuePicker.actions.refresh': '重新整理',
|
||||
'session.gitlabIssuePicker.error.noActiveProject': '沒有活動專案',
|
||||
'session.gitlabIssuePicker.error.runtimeUnavailable': 'GitLab 執行時 API 無法使用',
|
||||
'session.gitlabIssuePicker.error.notConnected': 'GitLab 未連線',
|
||||
'session.gitlabIssuePicker.error.repoNotResolvable': '無法解析儲存庫',
|
||||
'session.gitlabIssuePicker.error.repoMustBeGitlab': 'origin 遠端儲存庫必須是 GitLab URL',
|
||||
'session.gitlabIssuePicker.error.issueNotFound': '找不到 Issue',
|
||||
'session.gitlabIssuePicker.error.noModelSelected': '未選擇模型',
|
||||
'session.gitlabIssuePicker.toast.loadMoreFailed': '載入更多 Issue 失敗',
|
||||
'session.gitlabIssuePicker.toast.loadIssueDetailsFailed': '載入 Issue 詳情失敗',
|
||||
'session.gitlabIssuePicker.toast.sendContextFailed': '傳送 Issue 上下文失敗',
|
||||
'session.gitlabIssuePicker.toast.sessionCreated': '已從 Issue 建立會話',
|
||||
'session.gitlabIssuePicker.toast.startSessionFailed': '啟動會話失敗',
|
||||
'session.gitlabIssuePicker.title.select': '關聯 GitLab Issue',
|
||||
'session.gitlabIssuePicker.title.createSession': '從 GitLab Issue 新增會話',
|
||||
'session.gitlabIssuePicker.description.select': '選擇一個 Issue 關聯到目前會話。',
|
||||
'session.gitlabIssuePicker.description.createSession': '使用隱藏的 Issue 上下文(標題/內文/標籤/留言)初始化新會話。',
|
||||
'session.gitlabIssuePicker.searchPlaceholder': '按標題搜尋,或貼上 Issue URL',
|
||||
'session.gitlabIssuePicker.empty.noActiveProject': '未選擇活動專案。',
|
||||
'session.gitlabIssuePicker.empty.runtimeUnavailable': 'GitLab 執行時 API 無法使用。',
|
||||
'session.gitlabIssuePicker.empty.notConnected': 'GitLab 未連線。請在設定中連接 GitLab 帳號。',
|
||||
'session.gitlabIssuePicker.empty.noIssuesFound': '找不到 Issue',
|
||||
'session.gitlabIssuePicker.empty.noOpenIssuesFound': '沒有未關閉的 Issue',
|
||||
'session.gitlabIssuePicker.loading.issues': '正在載入 Issue...',
|
||||
'session.gitlabIssuePicker.loading.more': '載入中...',
|
||||
'session.gitlabIssuePicker.actions.openSettings': '開啟設定',
|
||||
'session.gitlabIssuePicker.actions.useIssue': '使用 Issue #{number}',
|
||||
'session.gitlabIssuePicker.actions.openInGitLabAria': '在 GitLab 中開啟',
|
||||
'session.gitlabIssuePicker.actions.loadMore': '載入更多',
|
||||
'session.gitlabIssuePicker.actions.sectionTitle': '操作',
|
||||
'session.gitlabIssuePicker.actions.toggleWorktreeAria': '切換 worktree',
|
||||
'session.gitlabIssuePicker.actions.createInWorktree': '在 worktree 中建立',
|
||||
'session.gitlabIssuePicker.actions.openRepo': '開啟儲存庫',
|
||||
'session.gitlabIssuePicker.actions.refresh': '重新整理',
|
||||
'session.githubPrPicker.error.noActiveProject': '沒有活動專案',
|
||||
'session.githubPrPicker.error.runtimeUnavailable': 'GitHub 執行時 API 無法使用',
|
||||
'session.githubPrPicker.error.notConnected': 'GitHub 未連線',
|
||||
@@ -1834,6 +1867,30 @@ export const dict: Record<I18nKey, string> = {
|
||||
'session.githubPrPicker.actions.usePullRequest': '使用 Pull Request #{number}',
|
||||
'session.githubPrPicker.actions.openInGitHubAria': '在 GitHub 中開啟',
|
||||
'session.githubPrPicker.actions.loadMore': '載入更多',
|
||||
'session.gitlabMrPicker.error.noActiveProject': '沒有活動專案',
|
||||
'session.gitlabMrPicker.error.runtimeUnavailable': 'GitLab 執行時 API 無法使用',
|
||||
'session.gitlabMrPicker.error.notConnected': 'GitLab 未連線',
|
||||
'session.gitlabMrPicker.error.mrNotFound': '找不到 Merge Request',
|
||||
'session.gitlabMrPicker.error.repoNotResolvable': '無法解析儲存庫',
|
||||
'session.gitlabMrPicker.error.repoMustBeGitlab': 'origin 遠端儲存庫必須是 GitLab URL',
|
||||
'session.gitlabMrPicker.toast.loadMoreFailed': '載入更多 Merge Request 失敗',
|
||||
'session.gitlabMrPicker.toast.loadDetailsFailed': '載入 Merge Request 詳情失敗',
|
||||
'session.gitlabMrPicker.title': '關聯 GitLab Merge Request',
|
||||
'session.gitlabMrPicker.description': '選擇一個 Merge Request,將審查上下文附加到此訊息。',
|
||||
'session.gitlabMrPicker.searchPlaceholder': '按標題搜尋,或貼上 Merge Request URL',
|
||||
'session.gitlabMrPicker.includeDiffAria': '在附加上下文中包含 MR diff',
|
||||
'session.gitlabMrPicker.includeDiff': '包含 MR diff',
|
||||
'session.gitlabMrPicker.empty.noActiveProject': '未選擇活動專案。',
|
||||
'session.gitlabMrPicker.empty.runtimeUnavailable': 'GitLab 執行時 API 無法使用。',
|
||||
'session.gitlabMrPicker.empty.notConnected': 'GitLab 未連線。請在設定中連接 GitLab 帳號。',
|
||||
'session.gitlabMrPicker.empty.noMergeRequestsFound': '找不到 Merge Request',
|
||||
'session.gitlabMrPicker.empty.noOpenMergeRequestsFound': '沒有未關閉的 Merge Request',
|
||||
'session.gitlabMrPicker.loading.mergeRequests': '正在載入 Merge Request...',
|
||||
'session.gitlabMrPicker.loading.more': '載入中...',
|
||||
'session.gitlabMrPicker.actions.openSettings': '開啟設定',
|
||||
'session.gitlabMrPicker.actions.useMergeRequest': '使用 Merge Request !{number}',
|
||||
'session.gitlabMrPicker.actions.openInGitLabAria': '在 GitLab 中開啟',
|
||||
'session.gitlabMrPicker.actions.loadMore': '載入更多',
|
||||
'session.newWorktree.title': '新增 Worktree',
|
||||
'session.newWorktree.mode.newBranch': '新分支',
|
||||
'session.newWorktree.mode.existingBranch': '現有分支',
|
||||
@@ -2152,6 +2209,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'chat.chatInput.actions.addAttachment': '加入附件',
|
||||
'chat.chatInput.actions.linkGithubIssue': '關聯 GitHub Issue',
|
||||
'chat.chatInput.actions.linkGithubPr': '關聯 GitHub PR',
|
||||
'chat.chatInput.actions.linkGitlabIssue': '關聯 GitLab Issue',
|
||||
'chat.chatInput.actions.linkGitlabMr': '關聯 GitLab MR',
|
||||
'chat.chatInput.actions.modelAgentSettings': '模型與 Agent 設定',
|
||||
'chat.chatInput.actions.sendMessageAria': '傳送訊息',
|
||||
'chat.chatInput.actions.queueMessageAria': '將訊息加入佇列',
|
||||
|
||||
Reference in New Issue
Block a user