feat(ui): create, update and merge GitLab merge requests from the MR view
This commit is contained in:
@@ -14,6 +14,10 @@ import { openExternalUrl } from '@/lib/url';
|
||||
import { formatDateTimeForPreference } from '@/lib/timeFormat';
|
||||
import type { GitLabMergeRequestContextResult, GitLabMergeRequestSummary } from '@/lib/api/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { toast } from '@/components/ui';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
|
||||
const mrStateColor = (state: string): string => {
|
||||
switch (state) {
|
||||
@@ -199,6 +203,19 @@ export const GitLabMrView: React.FC = () => {
|
||||
setContextError(null);
|
||||
}, [branchMr?.number]);
|
||||
|
||||
// A different branch MR invalidates the update/merge transient state so the
|
||||
// previous MR's edit form, squash flag, and in-flight requests don't leak.
|
||||
React.useEffect(() => {
|
||||
setUpdateOpen(false);
|
||||
setEditTitle('');
|
||||
setEditDescription('');
|
||||
setEditDescriptionKnown(false);
|
||||
setEditDescriptionLoading(false);
|
||||
setUpdating(false);
|
||||
setMergeSquash(false);
|
||||
setMerging(false);
|
||||
}, [branchMr?.number]);
|
||||
|
||||
const toggleContext = React.useCallback(async (mr: GitLabMergeRequestSummary) => {
|
||||
if (!currentDirectory || !gitlab?.mrContext) {
|
||||
return;
|
||||
@@ -226,6 +243,175 @@ export const GitLabMrView: React.FC = () => {
|
||||
}
|
||||
}, [contextOpen, currentDirectory, gitlab, t]);
|
||||
|
||||
// ---- Create / update / merge actions -----------------------------------
|
||||
|
||||
const [createTitle, setCreateTitle] = React.useState('');
|
||||
const [createDescription, setCreateDescription] = React.useState('');
|
||||
const [createTargetBranch, setCreateTargetBranch] = React.useState('main');
|
||||
const [createRemoveSourceBranch, setCreateRemoveSourceBranch] = React.useState(false);
|
||||
const [creating, setCreating] = React.useState(false);
|
||||
const createTargetTouchedRef = React.useRef(false);
|
||||
|
||||
// The default target branch is the target of the repository's previously
|
||||
// listed open MRs when available; otherwise fall back to main.
|
||||
const defaultTargetBranch = React.useMemo(
|
||||
() => openMrs.find((mr) => mr.targetBranch)?.targetBranch ?? 'main',
|
||||
[openMrs],
|
||||
);
|
||||
|
||||
// Adopt the repository's target branch default once the open-MR list
|
||||
// resolves, unless the user has already typed into the field.
|
||||
React.useEffect(() => {
|
||||
if (branchMrLoading || branchMr || createTargetTouchedRef.current) {
|
||||
return;
|
||||
}
|
||||
setCreateTargetBranch(defaultTargetBranch);
|
||||
}, [branchMr, branchMrLoading, defaultTargetBranch]);
|
||||
|
||||
const [updateOpen, setUpdateOpen] = React.useState(false);
|
||||
const [editTitle, setEditTitle] = React.useState('');
|
||||
const [editDescription, setEditDescription] = React.useState('');
|
||||
const [editDescriptionKnown, setEditDescriptionKnown] = React.useState(false);
|
||||
const [editDescriptionLoading, setEditDescriptionLoading] = React.useState(false);
|
||||
const [updating, setUpdating] = React.useState(false);
|
||||
|
||||
const [mergeSquash, setMergeSquash] = React.useState(false);
|
||||
const [merging, setMerging] = React.useState(false);
|
||||
|
||||
const createMr = React.useCallback(async () => {
|
||||
if (!currentDirectory || !currentBranch || !gitlab?.mrCreate) {
|
||||
return;
|
||||
}
|
||||
const targetBranch = createTargetBranch.trim();
|
||||
if (!targetBranch) {
|
||||
return;
|
||||
}
|
||||
setCreating(true);
|
||||
try {
|
||||
const created = await gitlab.mrCreate({
|
||||
directory: currentDirectory,
|
||||
title: createTitle.trim() || currentBranch,
|
||||
sourceBranch: currentBranch,
|
||||
targetBranch,
|
||||
...(createDescription.trim() ? { description: createDescription } : {}),
|
||||
...(createRemoveSourceBranch ? { removeSourceBranch: true } : {}),
|
||||
});
|
||||
toast.success(t('contextPanel.gitlabMr.createMr.toast.created'));
|
||||
// Show the created MR immediately and refresh both the branch MR and
|
||||
// the open list so the card flips to the opened state.
|
||||
setBranchMr(created);
|
||||
setRetryToken((value) => value + 1);
|
||||
// Clear the form.
|
||||
setCreateTitle('');
|
||||
setCreateDescription('');
|
||||
setCreateRemoveSourceBranch(false);
|
||||
createTargetTouchedRef.current = false;
|
||||
setCreateTargetBranch(defaultTargetBranch);
|
||||
} catch (error) {
|
||||
toast.error(t('contextPanel.gitlabMr.createMr.toast.createFailed'), {
|
||||
description: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
} finally {
|
||||
setCreating(false);
|
||||
}
|
||||
}, [createDescription, createRemoveSourceBranch, createTargetBranch, createTitle, currentBranch, currentDirectory, defaultTargetBranch, gitlab, t]);
|
||||
|
||||
const toggleUpdate = React.useCallback(async () => {
|
||||
if (!branchMr) {
|
||||
return;
|
||||
}
|
||||
if (updateOpen) {
|
||||
setUpdateOpen(false);
|
||||
return;
|
||||
}
|
||||
setUpdateOpen(true);
|
||||
setEditTitle(branchMr.title);
|
||||
const knownBody = contextResult?.mr?.body;
|
||||
if (typeof knownBody === 'string') {
|
||||
setEditDescription(knownBody);
|
||||
setEditDescriptionKnown(true);
|
||||
return;
|
||||
}
|
||||
setEditDescription('');
|
||||
setEditDescriptionKnown(false);
|
||||
if (!currentDirectory || !gitlab?.mrContext) {
|
||||
return;
|
||||
}
|
||||
setEditDescriptionLoading(true);
|
||||
try {
|
||||
const result = await gitlab.mrContext(currentDirectory, branchMr.number, { includeDiff: false });
|
||||
if (result.connected === false) {
|
||||
setEditDescription('');
|
||||
return;
|
||||
}
|
||||
setEditDescription(result.mr?.body ?? '');
|
||||
setEditDescriptionKnown(true);
|
||||
} catch {
|
||||
// Leave the description empty; the title can still be edited.
|
||||
} finally {
|
||||
setEditDescriptionLoading(false);
|
||||
}
|
||||
}, [branchMr, contextResult?.mr?.body, currentDirectory, gitlab, updateOpen]);
|
||||
|
||||
const saveMr = React.useCallback(async () => {
|
||||
if (!currentDirectory || !branchMr || !gitlab?.mrUpdate) {
|
||||
return;
|
||||
}
|
||||
const trimmedTitle = editTitle.trim();
|
||||
if (!trimmedTitle) {
|
||||
return;
|
||||
}
|
||||
setUpdating(true);
|
||||
try {
|
||||
await gitlab.mrUpdate({
|
||||
directory: currentDirectory,
|
||||
number: branchMr.number,
|
||||
title: trimmedTitle,
|
||||
// Only send the description when it was actually loaded so an
|
||||
// unresolved description can never be wiped out by a title-only save.
|
||||
...(editDescriptionKnown ? { description: editDescription } : {}),
|
||||
});
|
||||
toast.success(t('contextPanel.gitlabMr.updateMr.toast.updated'));
|
||||
setUpdateOpen(false);
|
||||
setRetryToken((value) => value + 1);
|
||||
} catch (error) {
|
||||
toast.error(t('contextPanel.gitlabMr.updateMr.toast.updateFailed'), {
|
||||
description: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
} finally {
|
||||
setUpdating(false);
|
||||
}
|
||||
}, [branchMr, currentDirectory, editDescription, editDescriptionKnown, editTitle, gitlab, t]);
|
||||
|
||||
const mergeMr = React.useCallback(async () => {
|
||||
if (!currentDirectory || !branchMr || !gitlab?.mrMerge) {
|
||||
return;
|
||||
}
|
||||
setMerging(true);
|
||||
try {
|
||||
const result = await gitlab.mrMerge({
|
||||
directory: currentDirectory,
|
||||
number: branchMr.number,
|
||||
...(mergeSquash ? { squash: true } : {}),
|
||||
});
|
||||
if (result.merged) {
|
||||
toast.success(t('contextPanel.gitlabMr.mergeMr.toast.merged'));
|
||||
} else {
|
||||
toast.error(t('contextPanel.gitlabMr.mergeMr.toast.mergeFailed'), {
|
||||
...(result.message ? { description: result.message } : {}),
|
||||
});
|
||||
}
|
||||
// Refresh the branch MR (flips to the merged state) and the open list.
|
||||
setRetryToken((value) => value + 1);
|
||||
} catch (error) {
|
||||
toast.error(t('contextPanel.gitlabMr.mergeMr.toast.mergeFailed'), {
|
||||
description: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
} finally {
|
||||
setMerging(false);
|
||||
}
|
||||
}, [branchMr, currentDirectory, gitlab, mergeSquash, t]);
|
||||
|
||||
const formatTimestamp = React.useCallback((value?: string) => {
|
||||
if (!value) {
|
||||
return '';
|
||||
@@ -361,8 +547,92 @@ export const GitLabMrView: React.FC = () => {
|
||||
)}
|
||||
{contextOpen ? t('contextPanel.gitlabMr.hideContext') : t('contextPanel.gitlabMr.loadContext')}
|
||||
</Button>
|
||||
{branchMr.state === 'opened' ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 gap-1.5 px-2"
|
||||
onClick={() => void toggleUpdate()}
|
||||
disabled={updating}
|
||||
>
|
||||
<Icon name="edit" className="size-4" />
|
||||
{t('contextPanel.gitlabMr.updateMr.toggle')}
|
||||
</Button>
|
||||
<div
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={mergeSquash}
|
||||
onClick={() => setMergeSquash((value) => !value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
setMergeSquash((value) => !value);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
size="sm"
|
||||
checked={mergeSquash}
|
||||
onChange={(next) => setMergeSquash(next)}
|
||||
ariaLabel={t('contextPanel.gitlabMr.mergeMr.squash')}
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground select-none">{t('contextPanel.gitlabMr.mergeMr.squash')}</span>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-7 gap-1.5 px-2"
|
||||
onClick={() => void mergeMr()}
|
||||
disabled={merging || updating}
|
||||
>
|
||||
{merging ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="git-merge" className="size-4" />}
|
||||
{merging ? t('contextPanel.gitlabMr.mergeMr.merging') : t('contextPanel.gitlabMr.mergeMr.action')}
|
||||
</Button>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{updateOpen && branchMr.state === 'opened' ? (
|
||||
<div className="flex min-w-0 flex-col gap-2 border-t border-border/40 pt-3">
|
||||
<label className="space-y-1">
|
||||
<div className="typography-micro text-muted-foreground">{t('contextPanel.gitlabMr.createMr.titleLabel')}</div>
|
||||
<Input
|
||||
value={editTitle}
|
||||
onChange={(event) => setEditTitle(event.target.value)}
|
||||
placeholder={t('contextPanel.gitlabMr.createMr.titlePlaceholder')}
|
||||
/>
|
||||
</label>
|
||||
<label className="space-y-1">
|
||||
<div className="typography-micro text-muted-foreground">{t('contextPanel.gitlabMr.createMr.descriptionLabel')}</div>
|
||||
{editDescriptionLoading ? (
|
||||
<div className="flex items-center gap-2 typography-micro text-muted-foreground">
|
||||
<Icon name="loader-4" className="size-4 animate-spin" />
|
||||
{t('contextPanel.gitlabMr.loading')}
|
||||
</div>
|
||||
) : (
|
||||
<Textarea
|
||||
value={editDescription}
|
||||
onChange={(event) => setEditDescription(event.target.value)}
|
||||
className="min-h-[80px]"
|
||||
placeholder={t('gitView.pr.placeholder.whatChanged')}
|
||||
/>
|
||||
)}
|
||||
</label>
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-7 gap-1.5 px-2"
|
||||
onClick={() => void saveMr()}
|
||||
disabled={updating || editDescriptionLoading || !editTitle.trim()}
|
||||
>
|
||||
{updating ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="check" className="size-4" />}
|
||||
{updating ? t('contextPanel.gitlabMr.updateMr.saving') : t('contextPanel.gitlabMr.updateMr.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{contextOpen ? (
|
||||
<div className="flex min-w-0 flex-col gap-3 border-t border-border/40 pt-3">
|
||||
{contextLoading ? (
|
||||
@@ -415,6 +685,80 @@ export const GitLabMrView: React.FC = () => {
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : currentBranch ? (
|
||||
<div className="flex min-w-0 flex-col gap-2 rounded-md border border-border/40 p-3">
|
||||
<div className="typography-ui-label font-semibold text-foreground">{t('contextPanel.gitlabMr.createMr.title')}</div>
|
||||
|
||||
<label className="space-y-1">
|
||||
<div className="typography-micro text-muted-foreground">{t('contextPanel.gitlabMr.createMr.sourceBranch')}</div>
|
||||
<Input value={currentBranch} readOnly />
|
||||
</label>
|
||||
|
||||
<label className="space-y-1">
|
||||
<div className="typography-micro text-muted-foreground">{t('contextPanel.gitlabMr.createMr.targetBranch')}</div>
|
||||
<Input
|
||||
value={createTargetBranch}
|
||||
onChange={(event) => {
|
||||
createTargetTouchedRef.current = true;
|
||||
setCreateTargetBranch(event.target.value);
|
||||
}}
|
||||
placeholder="main"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="space-y-1">
|
||||
<div className="typography-micro text-muted-foreground">{t('contextPanel.gitlabMr.createMr.titleLabel')}</div>
|
||||
<Input
|
||||
value={createTitle}
|
||||
onChange={(event) => setCreateTitle(event.target.value)}
|
||||
placeholder={currentBranch}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="space-y-1">
|
||||
<div className="typography-micro text-muted-foreground">{t('contextPanel.gitlabMr.createMr.descriptionLabel')}</div>
|
||||
<Textarea
|
||||
value={createDescription}
|
||||
onChange={(event) => setCreateDescription(event.target.value)}
|
||||
className="min-h-[80px]"
|
||||
placeholder={t('gitView.pr.placeholder.whatChanged')}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={createRemoveSourceBranch}
|
||||
onClick={() => setCreateRemoveSourceBranch((value) => !value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === ' ' || event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
setCreateRemoveSourceBranch((value) => !value);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
size="sm"
|
||||
checked={createRemoveSourceBranch}
|
||||
onChange={(next) => setCreateRemoveSourceBranch(next)}
|
||||
ariaLabel={t('contextPanel.gitlabMr.createMr.removeSourceBranch')}
|
||||
/>
|
||||
<span className="typography-ui-label text-foreground select-none">{t('contextPanel.gitlabMr.createMr.removeSourceBranch')}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-7 gap-1.5 px-2"
|
||||
onClick={() => void createMr()}
|
||||
disabled={creating || !createTargetBranch.trim()}
|
||||
>
|
||||
{creating ? <Icon name="loader-4" className="size-4 animate-spin" /> : <Icon name="git-pull-request" className="size-4" />}
|
||||
{creating ? t('contextPanel.gitlabMr.createMr.submitting') : t('contextPanel.gitlabMr.createMr.submit')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="typography-micro text-muted-foreground">{t('contextPanel.gitlabMr.noMrForBranch')}</div>
|
||||
)}
|
||||
|
||||
@@ -3000,6 +3000,27 @@ export const dict = {
|
||||
'contextPanel.gitlabMr.error.notConnected': 'GitLab ist nicht verbunden',
|
||||
'contextPanel.gitlabMr.empty.noActiveProject': 'Kein aktives Projekt',
|
||||
'contextPanel.gitlabMr.actions.openSettings': 'Einstellungen öffnen',
|
||||
'contextPanel.gitlabMr.createMr.title': 'Neuer Merge-Request',
|
||||
'contextPanel.gitlabMr.createMr.sourceBranch': 'Quellbranch',
|
||||
'contextPanel.gitlabMr.createMr.targetBranch': 'Zielbranch',
|
||||
'contextPanel.gitlabMr.createMr.titleLabel': 'Titel',
|
||||
'contextPanel.gitlabMr.createMr.titlePlaceholder': 'Titel des Merge-Requests',
|
||||
'contextPanel.gitlabMr.createMr.descriptionLabel': 'Beschreibung',
|
||||
'contextPanel.gitlabMr.createMr.removeSourceBranch': 'Quellbranch nach dem Zusammenführen löschen',
|
||||
'contextPanel.gitlabMr.createMr.submit': 'Merge-Request erstellen',
|
||||
'contextPanel.gitlabMr.createMr.submitting': 'Wird erstellt...',
|
||||
'contextPanel.gitlabMr.createMr.toast.created': 'Merge-Request erstellt',
|
||||
'contextPanel.gitlabMr.createMr.toast.createFailed': 'Merge-Request konnte nicht erstellt werden',
|
||||
'contextPanel.gitlabMr.updateMr.toggle': 'Titel und Beschreibung bearbeiten',
|
||||
'contextPanel.gitlabMr.updateMr.save': 'Speichern',
|
||||
'contextPanel.gitlabMr.updateMr.saving': 'Wird gespeichert...',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updated': 'Merge-Request aktualisiert',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updateFailed': 'Merge-Request konnte nicht aktualisiert werden',
|
||||
'contextPanel.gitlabMr.mergeMr.action': 'Zusammenführen',
|
||||
'contextPanel.gitlabMr.mergeMr.squash': 'Commits squashen',
|
||||
'contextPanel.gitlabMr.mergeMr.merging': 'Wird zusammengeführt...',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.merged': 'Merge-Request zusammengeführt',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.mergeFailed': 'Merge-Request konnte nicht zusammengeführt werden',
|
||||
'contextRail.surface.editor.description': 'Bearbeitungskontext',
|
||||
'contextRail.surface.git.description': 'Git-Kontext',
|
||||
'contextRail.surface.git.changesCountAriaSingle': '{label}, {count} geänderte Datei',
|
||||
|
||||
@@ -1138,6 +1138,27 @@ export const dict = {
|
||||
'contextPanel.gitlabMr.error.notConnected': 'GitLab is not connected',
|
||||
'contextPanel.gitlabMr.empty.noActiveProject': 'No active project',
|
||||
'contextPanel.gitlabMr.actions.openSettings': 'Open settings',
|
||||
'contextPanel.gitlabMr.createMr.title': 'New merge request',
|
||||
'contextPanel.gitlabMr.createMr.sourceBranch': 'Source branch',
|
||||
'contextPanel.gitlabMr.createMr.targetBranch': 'Target branch',
|
||||
'contextPanel.gitlabMr.createMr.titleLabel': 'Title',
|
||||
'contextPanel.gitlabMr.createMr.titlePlaceholder': 'Merge request title',
|
||||
'contextPanel.gitlabMr.createMr.descriptionLabel': 'Description',
|
||||
'contextPanel.gitlabMr.createMr.removeSourceBranch': 'Remove source branch on merge',
|
||||
'contextPanel.gitlabMr.createMr.submit': 'Create merge request',
|
||||
'contextPanel.gitlabMr.createMr.submitting': 'Creating...',
|
||||
'contextPanel.gitlabMr.createMr.toast.created': 'Merge request created',
|
||||
'contextPanel.gitlabMr.createMr.toast.createFailed': 'Failed to create merge request',
|
||||
'contextPanel.gitlabMr.updateMr.toggle': 'Edit title & description',
|
||||
'contextPanel.gitlabMr.updateMr.save': 'Save',
|
||||
'contextPanel.gitlabMr.updateMr.saving': 'Saving...',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updated': 'Merge request updated',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updateFailed': 'Failed to update merge request',
|
||||
'contextPanel.gitlabMr.mergeMr.action': 'Merge',
|
||||
'contextPanel.gitlabMr.mergeMr.squash': 'Squash commits',
|
||||
'contextPanel.gitlabMr.mergeMr.merging': 'Merging...',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.merged': 'Merge request merged',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.mergeFailed': 'Failed to merge merge request',
|
||||
'contextRail.surface.editor.description': 'Edit project files',
|
||||
'contextRail.surface.git.description': 'Commits, branches, and pull requests',
|
||||
'contextRail.surface.git.changesCountAriaSingle': '{label}, {count} changed file',
|
||||
|
||||
@@ -1139,6 +1139,27 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextPanel.gitlabMr.error.notConnected": "GitLab no está conectado",
|
||||
"contextPanel.gitlabMr.empty.noActiveProject": "Sin proyecto activo",
|
||||
"contextPanel.gitlabMr.actions.openSettings": "Abrir ajustes",
|
||||
"contextPanel.gitlabMr.createMr.title": "Nueva solicitud de fusión",
|
||||
"contextPanel.gitlabMr.createMr.sourceBranch": "Rama de origen",
|
||||
"contextPanel.gitlabMr.createMr.targetBranch": "Rama de destino",
|
||||
"contextPanel.gitlabMr.createMr.titleLabel": "Título",
|
||||
"contextPanel.gitlabMr.createMr.titlePlaceholder": "Título de la solicitud de fusión",
|
||||
"contextPanel.gitlabMr.createMr.descriptionLabel": "Descripción",
|
||||
"contextPanel.gitlabMr.createMr.removeSourceBranch": "Eliminar la rama de origen al fusionar",
|
||||
"contextPanel.gitlabMr.createMr.submit": "Crear solicitud de fusión",
|
||||
"contextPanel.gitlabMr.createMr.submitting": "Creando...",
|
||||
"contextPanel.gitlabMr.createMr.toast.created": "Solicitud de fusión creada",
|
||||
"contextPanel.gitlabMr.createMr.toast.createFailed": "No se pudo crear la solicitud de fusión",
|
||||
"contextPanel.gitlabMr.updateMr.toggle": "Editar título y descripción",
|
||||
"contextPanel.gitlabMr.updateMr.save": "Guardar",
|
||||
"contextPanel.gitlabMr.updateMr.saving": "Guardando...",
|
||||
"contextPanel.gitlabMr.updateMr.toast.updated": "Solicitud de fusión actualizada",
|
||||
"contextPanel.gitlabMr.updateMr.toast.updateFailed": "No se pudo actualizar la solicitud de fusión",
|
||||
"contextPanel.gitlabMr.mergeMr.action": "Fusionar",
|
||||
"contextPanel.gitlabMr.mergeMr.squash": "Combinar commits",
|
||||
"contextPanel.gitlabMr.mergeMr.merging": "Fusionando...",
|
||||
"contextPanel.gitlabMr.mergeMr.toast.merged": "La solicitud de fusión se fusionó",
|
||||
"contextPanel.gitlabMr.mergeMr.toast.mergeFailed": "No se pudo fusionar la solicitud de fusión",
|
||||
"contextRail.surface.editor.description": "Editar archivos del proyecto",
|
||||
"contextRail.surface.git.description": "Commits, ramas y pull requests",
|
||||
"contextRail.surface.git.changesCountAriaSingle": "{label}, {count} archivo modificado",
|
||||
|
||||
@@ -958,6 +958,27 @@ export const dict = {
|
||||
'contextPanel.gitlabMr.error.notConnected': 'GitLab n\'est pas connecté',
|
||||
'contextPanel.gitlabMr.empty.noActiveProject': 'Aucun projet actif',
|
||||
'contextPanel.gitlabMr.actions.openSettings': 'Ouvrir les paramètres',
|
||||
'contextPanel.gitlabMr.createMr.title': 'Nouvelle demande de fusion',
|
||||
'contextPanel.gitlabMr.createMr.sourceBranch': 'Branche source',
|
||||
'contextPanel.gitlabMr.createMr.targetBranch': 'Branche cible',
|
||||
'contextPanel.gitlabMr.createMr.titleLabel': 'Titre',
|
||||
'contextPanel.gitlabMr.createMr.titlePlaceholder': 'Titre de la demande de fusion',
|
||||
'contextPanel.gitlabMr.createMr.descriptionLabel': 'Description',
|
||||
'contextPanel.gitlabMr.createMr.removeSourceBranch': 'Supprimer la branche source lors de la fusion',
|
||||
'contextPanel.gitlabMr.createMr.submit': 'Créer une demande de fusion',
|
||||
'contextPanel.gitlabMr.createMr.submitting': 'Création...',
|
||||
'contextPanel.gitlabMr.createMr.toast.created': 'Demande de fusion créée',
|
||||
'contextPanel.gitlabMr.createMr.toast.createFailed': 'Échec de la création de la demande de fusion',
|
||||
'contextPanel.gitlabMr.updateMr.toggle': 'Modifier le titre et la description',
|
||||
'contextPanel.gitlabMr.updateMr.save': 'Enregistrer',
|
||||
'contextPanel.gitlabMr.updateMr.saving': 'Enregistrement...',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updated': 'Demande de fusion mise à jour',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updateFailed': 'Échec de la mise à jour de la demande de fusion',
|
||||
'contextPanel.gitlabMr.mergeMr.action': 'Fusionner',
|
||||
'contextPanel.gitlabMr.mergeMr.squash': 'Écraser les commits',
|
||||
'contextPanel.gitlabMr.mergeMr.merging': 'Fusion en cours...',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.merged': 'La demande de fusion a été fusionnée',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.mergeFailed': 'Échec de la fusion de la demande de fusion',
|
||||
'contextRail.surface.editor.description': 'Modifier les fichiers du projet',
|
||||
'contextRail.surface.git.description': 'Commits, branches et pull requests',
|
||||
'contextRail.surface.git.changesCountAriaSingle': '{label}, {count} fichier modifié',
|
||||
|
||||
@@ -1135,6 +1135,27 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.gitlabMr.error.notConnected': 'GitLab に接続されていません',
|
||||
'contextPanel.gitlabMr.empty.noActiveProject': 'アクティブなプロジェクトがありません',
|
||||
'contextPanel.gitlabMr.actions.openSettings': '設定を開く',
|
||||
'contextPanel.gitlabMr.createMr.title': '新しいマージリクエスト',
|
||||
'contextPanel.gitlabMr.createMr.sourceBranch': 'ソースブランチ',
|
||||
'contextPanel.gitlabMr.createMr.targetBranch': 'ターゲットブランチ',
|
||||
'contextPanel.gitlabMr.createMr.titleLabel': 'タイトル',
|
||||
'contextPanel.gitlabMr.createMr.titlePlaceholder': 'マージリクエストのタイトル',
|
||||
'contextPanel.gitlabMr.createMr.descriptionLabel': '説明',
|
||||
'contextPanel.gitlabMr.createMr.removeSourceBranch': 'マージ時にソースブランチを削除する',
|
||||
'contextPanel.gitlabMr.createMr.submit': 'マージリクエストを作成',
|
||||
'contextPanel.gitlabMr.createMr.submitting': '作成中...',
|
||||
'contextPanel.gitlabMr.createMr.toast.created': 'マージリクエストを作成しました',
|
||||
'contextPanel.gitlabMr.createMr.toast.createFailed': 'マージリクエストの作成に失敗しました',
|
||||
'contextPanel.gitlabMr.updateMr.toggle': 'タイトルと説明を編集',
|
||||
'contextPanel.gitlabMr.updateMr.save': '保存',
|
||||
'contextPanel.gitlabMr.updateMr.saving': '保存中...',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updated': 'マージリクエストを更新しました',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updateFailed': 'マージリクエストの更新に失敗しました',
|
||||
'contextPanel.gitlabMr.mergeMr.action': 'マージ',
|
||||
'contextPanel.gitlabMr.mergeMr.squash': 'コミットをスカッシュ',
|
||||
'contextPanel.gitlabMr.mergeMr.merging': 'マージ中...',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.merged': 'マージリクエストをマージしました',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.mergeFailed': 'マージリクエストのマージに失敗しました',
|
||||
'contextRail.surface.editor.description': 'プロジェクトのファイルを編集',
|
||||
'contextRail.surface.git.description': 'コミット・ブランチ・プルリクエスト',
|
||||
'contextRail.surface.git.changesCountAriaSingle': '{label}、変更ファイル{count}件',
|
||||
|
||||
@@ -1139,6 +1139,27 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.gitlabMr.error.notConnected': 'GitLab에 연결되지 않았습니다',
|
||||
'contextPanel.gitlabMr.empty.noActiveProject': '활성 프로젝트가 없습니다',
|
||||
'contextPanel.gitlabMr.actions.openSettings': '설정 열기',
|
||||
'contextPanel.gitlabMr.createMr.title': '새 병합 요청',
|
||||
'contextPanel.gitlabMr.createMr.sourceBranch': '소스 브랜치',
|
||||
'contextPanel.gitlabMr.createMr.targetBranch': '대상 브랜치',
|
||||
'contextPanel.gitlabMr.createMr.titleLabel': '제목',
|
||||
'contextPanel.gitlabMr.createMr.titlePlaceholder': '병합 요청 제목',
|
||||
'contextPanel.gitlabMr.createMr.descriptionLabel': '설명',
|
||||
'contextPanel.gitlabMr.createMr.removeSourceBranch': '병합 시 소스 브랜치 삭제',
|
||||
'contextPanel.gitlabMr.createMr.submit': '병합 요청 만들기',
|
||||
'contextPanel.gitlabMr.createMr.submitting': '만드는 중...',
|
||||
'contextPanel.gitlabMr.createMr.toast.created': '병합 요청이 생성되었습니다',
|
||||
'contextPanel.gitlabMr.createMr.toast.createFailed': '병합 요청 생성에 실패했습니다',
|
||||
'contextPanel.gitlabMr.updateMr.toggle': '제목 및 설명 편집',
|
||||
'contextPanel.gitlabMr.updateMr.save': '저장',
|
||||
'contextPanel.gitlabMr.updateMr.saving': '저장 중...',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updated': '병합 요청이 업데이트되었습니다',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updateFailed': '병합 요청 업데이트에 실패했습니다',
|
||||
'contextPanel.gitlabMr.mergeMr.action': '병합',
|
||||
'contextPanel.gitlabMr.mergeMr.squash': '커밋 스쿼시',
|
||||
'contextPanel.gitlabMr.mergeMr.merging': '병합 중...',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.merged': '병합 요청이 병합되었습니다',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.mergeFailed': '병합 요청 병합에 실패했습니다',
|
||||
'contextRail.surface.editor.description': '프로젝트 파일 편집',
|
||||
'contextRail.surface.git.description': '커밋, 브랜치, 풀 리퀘스트',
|
||||
'contextRail.surface.git.changesCountAriaSingle': '{label}, 변경된 파일 {count}개',
|
||||
|
||||
@@ -1475,6 +1475,27 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.gitlabMr.error.notConnected': 'GitLab nie jest połączony',
|
||||
'contextPanel.gitlabMr.empty.noActiveProject': 'Brak aktywnego projektu',
|
||||
'contextPanel.gitlabMr.actions.openSettings': 'Otwórz ustawienia',
|
||||
'contextPanel.gitlabMr.createMr.title': 'Nowe żądanie scalenia',
|
||||
'contextPanel.gitlabMr.createMr.sourceBranch': 'Gałąź źródłowa',
|
||||
'contextPanel.gitlabMr.createMr.targetBranch': 'Gałąź docelowa',
|
||||
'contextPanel.gitlabMr.createMr.titleLabel': 'Tytuł',
|
||||
'contextPanel.gitlabMr.createMr.titlePlaceholder': 'Tytuł żądania scalenia',
|
||||
'contextPanel.gitlabMr.createMr.descriptionLabel': 'Opis',
|
||||
'contextPanel.gitlabMr.createMr.removeSourceBranch': 'Usuń gałąź źródłową po scaleniu',
|
||||
'contextPanel.gitlabMr.createMr.submit': 'Utwórz żądanie scalenia',
|
||||
'contextPanel.gitlabMr.createMr.submitting': 'Tworzenie...',
|
||||
'contextPanel.gitlabMr.createMr.toast.created': 'Utworzono żądanie scalenia',
|
||||
'contextPanel.gitlabMr.createMr.toast.createFailed': 'Nie udało się utworzyć żądania scalenia',
|
||||
'contextPanel.gitlabMr.updateMr.toggle': 'Edytuj tytuł i opis',
|
||||
'contextPanel.gitlabMr.updateMr.save': 'Zapisz',
|
||||
'contextPanel.gitlabMr.updateMr.saving': 'Zapisywanie...',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updated': 'Zaktualizowano żądanie scalenia',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updateFailed': 'Nie udało się zaktualizować żądania scalenia',
|
||||
'contextPanel.gitlabMr.mergeMr.action': 'Scal',
|
||||
'contextPanel.gitlabMr.mergeMr.squash': 'Składuj commity',
|
||||
'contextPanel.gitlabMr.mergeMr.merging': 'Scalanie...',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.merged': 'Żądanie scalenia scalone',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.mergeFailed': 'Nie udało się scalić żądania scalenia',
|
||||
'contextRail.surface.editor.description': 'Edytuj pliki projektu',
|
||||
'contextRail.surface.git.description': 'Commity, gałęzie i pull requesty',
|
||||
'contextRail.surface.git.changesCountAriaSingle': '{label}, {count} zmieniony plik',
|
||||
|
||||
@@ -1139,6 +1139,27 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextPanel.gitlabMr.error.notConnected": "GitLab não está conectado",
|
||||
"contextPanel.gitlabMr.empty.noActiveProject": "Nenhum projeto ativo",
|
||||
"contextPanel.gitlabMr.actions.openSettings": "Abrir configurações",
|
||||
"contextPanel.gitlabMr.createMr.title": "Nova solicitação de merge",
|
||||
"contextPanel.gitlabMr.createMr.sourceBranch": "Branch de origem",
|
||||
"contextPanel.gitlabMr.createMr.targetBranch": "Branch de destino",
|
||||
"contextPanel.gitlabMr.createMr.titleLabel": "Título",
|
||||
"contextPanel.gitlabMr.createMr.titlePlaceholder": "Título da solicitação de merge",
|
||||
"contextPanel.gitlabMr.createMr.descriptionLabel": "Descrição",
|
||||
"contextPanel.gitlabMr.createMr.removeSourceBranch": "Remover branch de origem ao mesclar",
|
||||
"contextPanel.gitlabMr.createMr.submit": "Criar solicitação de merge",
|
||||
"contextPanel.gitlabMr.createMr.submitting": "Criando...",
|
||||
"contextPanel.gitlabMr.createMr.toast.created": "Solicitação de merge criada",
|
||||
"contextPanel.gitlabMr.createMr.toast.createFailed": "Falha ao criar solicitação de merge",
|
||||
"contextPanel.gitlabMr.updateMr.toggle": "Editar título e descrição",
|
||||
"contextPanel.gitlabMr.updateMr.save": "Salvar",
|
||||
"contextPanel.gitlabMr.updateMr.saving": "Salvando...",
|
||||
"contextPanel.gitlabMr.updateMr.toast.updated": "Solicitação de merge atualizada",
|
||||
"contextPanel.gitlabMr.updateMr.toast.updateFailed": "Falha ao atualizar solicitação de merge",
|
||||
"contextPanel.gitlabMr.mergeMr.action": "Mesclar",
|
||||
"contextPanel.gitlabMr.mergeMr.squash": "Combinar commits",
|
||||
"contextPanel.gitlabMr.mergeMr.merging": "Mesclando...",
|
||||
"contextPanel.gitlabMr.mergeMr.toast.merged": "Solicitação de merge mesclada",
|
||||
"contextPanel.gitlabMr.mergeMr.toast.mergeFailed": "Falha ao mesclar solicitação de merge",
|
||||
"contextRail.surface.editor.description": "Editar arquivos do projeto",
|
||||
"contextRail.surface.git.description": "Commits, branches e pull requests",
|
||||
"contextRail.surface.git.changesCountAriaSingle": "{label}, {count} arquivo modificado",
|
||||
|
||||
@@ -1139,6 +1139,27 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextPanel.gitlabMr.error.notConnected": "GitLab не підключено",
|
||||
"contextPanel.gitlabMr.empty.noActiveProject": "Немає активного проєкту",
|
||||
"contextPanel.gitlabMr.actions.openSettings": "Відкрити налаштування",
|
||||
"contextPanel.gitlabMr.createMr.title": "Новий запит на злиття",
|
||||
"contextPanel.gitlabMr.createMr.sourceBranch": "Вихідна гілка",
|
||||
"contextPanel.gitlabMr.createMr.targetBranch": "Цільова гілка",
|
||||
"contextPanel.gitlabMr.createMr.titleLabel": "Назва",
|
||||
"contextPanel.gitlabMr.createMr.titlePlaceholder": "Назва запиту на злиття",
|
||||
"contextPanel.gitlabMr.createMr.descriptionLabel": "Опис",
|
||||
"contextPanel.gitlabMr.createMr.removeSourceBranch": "Видалити вихідну гілку після злиття",
|
||||
"contextPanel.gitlabMr.createMr.submit": "Створити запит на злиття",
|
||||
"contextPanel.gitlabMr.createMr.submitting": "Створення...",
|
||||
"contextPanel.gitlabMr.createMr.toast.created": "Запит на злиття створено",
|
||||
"contextPanel.gitlabMr.createMr.toast.createFailed": "Не вдалося створити запит на злиття",
|
||||
"contextPanel.gitlabMr.updateMr.toggle": "Редагувати назву та опис",
|
||||
"contextPanel.gitlabMr.updateMr.save": "Зберегти",
|
||||
"contextPanel.gitlabMr.updateMr.saving": "Збереження...",
|
||||
"contextPanel.gitlabMr.updateMr.toast.updated": "Запит на злиття оновлено",
|
||||
"contextPanel.gitlabMr.updateMr.toast.updateFailed": "Не вдалося оновити запит на злиття",
|
||||
"contextPanel.gitlabMr.mergeMr.action": "Злити",
|
||||
"contextPanel.gitlabMr.mergeMr.squash": "Сквошити коміти",
|
||||
"contextPanel.gitlabMr.mergeMr.merging": "Злиття...",
|
||||
"contextPanel.gitlabMr.mergeMr.toast.merged": "Запит на злиття злито",
|
||||
"contextPanel.gitlabMr.mergeMr.toast.mergeFailed": "Не вдалося злити запит на злиття",
|
||||
"contextRail.surface.editor.description": "Редагування файлів проєкту",
|
||||
"contextRail.surface.git.description": "Коміти, гілки та pull request-и",
|
||||
"contextRail.surface.git.changesCountAriaSingle": "{label}, {count} змінений файл",
|
||||
|
||||
@@ -1139,6 +1139,27 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.gitlabMr.error.notConnected': 'GitLab 未连接',
|
||||
'contextPanel.gitlabMr.empty.noActiveProject': '没有活动的项目',
|
||||
'contextPanel.gitlabMr.actions.openSettings': '打开设置',
|
||||
'contextPanel.gitlabMr.createMr.title': '新建合并请求',
|
||||
'contextPanel.gitlabMr.createMr.sourceBranch': '源分支',
|
||||
'contextPanel.gitlabMr.createMr.targetBranch': '目标分支',
|
||||
'contextPanel.gitlabMr.createMr.titleLabel': '标题',
|
||||
'contextPanel.gitlabMr.createMr.titlePlaceholder': '合并请求标题',
|
||||
'contextPanel.gitlabMr.createMr.descriptionLabel': '描述',
|
||||
'contextPanel.gitlabMr.createMr.removeSourceBranch': '合并后删除源分支',
|
||||
'contextPanel.gitlabMr.createMr.submit': '创建合并请求',
|
||||
'contextPanel.gitlabMr.createMr.submitting': '创建中...',
|
||||
'contextPanel.gitlabMr.createMr.toast.created': '合并请求已创建',
|
||||
'contextPanel.gitlabMr.createMr.toast.createFailed': '创建合并请求失败',
|
||||
'contextPanel.gitlabMr.updateMr.toggle': '编辑标题和描述',
|
||||
'contextPanel.gitlabMr.updateMr.save': '保存',
|
||||
'contextPanel.gitlabMr.updateMr.saving': '保存中...',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updated': '合并请求已更新',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updateFailed': '更新合并请求失败',
|
||||
'contextPanel.gitlabMr.mergeMr.action': '合并',
|
||||
'contextPanel.gitlabMr.mergeMr.squash': '压缩提交',
|
||||
'contextPanel.gitlabMr.mergeMr.merging': '合并中...',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.merged': '合并请求已合并',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.mergeFailed': '合并请求合并失败',
|
||||
'contextRail.surface.editor.description': '编辑项目文件',
|
||||
'contextRail.surface.git.description': '提交、分支和拉取请求',
|
||||
'contextRail.surface.git.changesCountAriaSingle': '{label},{count} 个更改的文件',
|
||||
|
||||
@@ -1151,6 +1151,27 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.gitlabMr.error.notConnected': 'GitLab 未連線',
|
||||
'contextPanel.gitlabMr.empty.noActiveProject': '沒有使用中的專案',
|
||||
'contextPanel.gitlabMr.actions.openSettings': '開啟設定',
|
||||
'contextPanel.gitlabMr.createMr.title': '新增合併請求',
|
||||
'contextPanel.gitlabMr.createMr.sourceBranch': '來源分支',
|
||||
'contextPanel.gitlabMr.createMr.targetBranch': '目標分支',
|
||||
'contextPanel.gitlabMr.createMr.titleLabel': '標題',
|
||||
'contextPanel.gitlabMr.createMr.titlePlaceholder': '合併請求標題',
|
||||
'contextPanel.gitlabMr.createMr.descriptionLabel': '描述',
|
||||
'contextPanel.gitlabMr.createMr.removeSourceBranch': '合併後刪除來源分支',
|
||||
'contextPanel.gitlabMr.createMr.submit': '建立合併請求',
|
||||
'contextPanel.gitlabMr.createMr.submitting': '建立中...',
|
||||
'contextPanel.gitlabMr.createMr.toast.created': '合併請求已建立',
|
||||
'contextPanel.gitlabMr.createMr.toast.createFailed': '建立合併請求失敗',
|
||||
'contextPanel.gitlabMr.updateMr.toggle': '編輯標題與描述',
|
||||
'contextPanel.gitlabMr.updateMr.save': '儲存',
|
||||
'contextPanel.gitlabMr.updateMr.saving': '儲存中...',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updated': '合併請求已更新',
|
||||
'contextPanel.gitlabMr.updateMr.toast.updateFailed': '更新合併請求失敗',
|
||||
'contextPanel.gitlabMr.mergeMr.action': '合併',
|
||||
'contextPanel.gitlabMr.mergeMr.squash': '壓縮提交',
|
||||
'contextPanel.gitlabMr.mergeMr.merging': '合併中...',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.merged': '合併請求已合併',
|
||||
'contextPanel.gitlabMr.mergeMr.toast.mergeFailed': '合併請求合併失敗',
|
||||
'contextRail.surface.editor.description': '編輯專案檔案',
|
||||
'contextRail.surface.git.description': '提交、分支與拉取請求',
|
||||
'contextRail.surface.git.changesCountAriaSingle': '{label},{count} 個變更的檔案',
|
||||
|
||||
Reference in New Issue
Block a user