From 1f0dca4646e2adaf108a44f1798dabcff0b10fbc Mon Sep 17 00:00:00 2001 From: ndom91 Date: Tue, 15 Oct 2024 17:33:27 +0200 Subject: [PATCH] feat: add 'generate branch name' to stack series header --- apps/desktop/src/lib/ai/service.ts | 3 +- .../lib/branch/BranchLaneContextMenu.svelte | 20 ++++---- .../lib/branch/StackingBranchHeader.svelte | 39 ++++++++++++++- .../StackingBranchHeaderContextMenu.svelte | 27 ++++++++++- apps/desktop/src/lib/stack/Stack.svelte | 48 +------------------ apps/desktop/src/lib/stack/StackHeader.svelte | 9 +--- 6 files changed, 80 insertions(+), 66 deletions(-) diff --git a/apps/desktop/src/lib/ai/service.ts b/apps/desktop/src/lib/ai/service.ts index 23df5685e..6fe256f94 100644 --- a/apps/desktop/src/lib/ai/service.ts +++ b/apps/desktop/src/lib/ai/service.ts @@ -335,9 +335,10 @@ export class AIService { const messageResult = await aiClient.evaluate(prompt, { onToken }); if (isFailure(messageResult)) return messageResult; + const message = messageResult.value; - return ok(message.replaceAll(' ', '-').replaceAll('\n', '-')); + return ok(message?.replaceAll(' ', '-').replaceAll('\n', '-') ?? ''); } async describePR({ diff --git a/apps/desktop/src/lib/branch/BranchLaneContextMenu.svelte b/apps/desktop/src/lib/branch/BranchLaneContextMenu.svelte index 45cbd4bed..e63633f9e 100644 --- a/apps/desktop/src/lib/branch/BranchLaneContextMenu.svelte +++ b/apps/desktop/src/lib/branch/BranchLaneContextMenu.svelte @@ -20,7 +20,7 @@ contextMenuEl?: ReturnType; target?: HTMLElement; onCollapse: () => void; - onGenerateBranchName: () => void; + onGenerateBranchName?: () => void; } let { contextMenuEl = $bindable(), target, onCollapse, onGenerateBranchName }: Props = $props(); @@ -113,14 +113,16 @@ }} /> - { - onGenerateBranchName(); - contextMenuEl?.close(); - }} - disabled={!($aiGenEnabled && aiConfigurationValid) || branch.files?.length === 0} - /> + {#if !$stackingFeature} + { + onGenerateBranchName?.(); + contextMenuEl?.close(); + }} + disabled={!($aiGenEnabled && aiConfigurationValid) || branch.files?.length === 0} + /> + {/if} {#if !$stackingFeature} diff --git a/apps/desktop/src/lib/branch/StackingBranchHeader.svelte b/apps/desktop/src/lib/branch/StackingBranchHeader.svelte index 82b6bcf3c..e247103b5 100644 --- a/apps/desktop/src/lib/branch/StackingBranchHeader.svelte +++ b/apps/desktop/src/lib/branch/StackingBranchHeader.svelte @@ -2,14 +2,20 @@ import BranchLabel from './BranchLabel.svelte'; import StackingStatusIcon from './StackingStatusIcon.svelte'; import { getColorFromBranchType } from './stackingUtils'; + import { PromptService } from '$lib/ai/promptService'; + import { AIService } from '$lib/ai/service'; + import { Project } from '$lib/backend/projects'; import { BaseBranch } from '$lib/baseBranch/baseBranch'; import StackingBranchHeaderContextMenu from '$lib/branch/StackingBranchHeaderContextMenu.svelte'; import ContextMenu from '$lib/components/contextmenu/ContextMenu.svelte'; + import { projectAiGenEnabled } from '$lib/config/config'; import { getGitHost } from '$lib/gitHost/interface/gitHost'; import { getGitHostListingService } from '$lib/gitHost/interface/gitHostListingService'; import { getGitHostPrService } from '$lib/gitHost/interface/gitHostPrService'; + import { showError } from '$lib/notifications/toasts'; import PrDetailsModal from '$lib/pr/PrDetailsModal.svelte'; import StackingPullRequestCard from '$lib/pr/StackingPullRequestCard.svelte'; + import { isFailure } from '$lib/result'; import { slugify } from '$lib/utils/string'; import { openExternalUrl } from '$lib/utils/url'; import { BranchController } from '$lib/vbranches/branchController'; @@ -28,14 +34,19 @@ let descriptionVisible = $state(false); + const project = getContext(Project); + const aiService = getContext(AIService); + const promptService = getContext(PromptService); const branchStore = getContextStore(VirtualBranch); - const branch = $derived($branchStore); + const aiGenEnabled = projectAiGenEnabled(project.id); const branchController = getContext(BranchController); const baseBranch = getContextStore(BaseBranch); const prService = getGitHostPrService(); const gitHost = getGitHost(); + const gitHostBranch = $derived(upstreamName ? $gitHost?.branch(upstreamName) : undefined); + const branch = $derived($branchStore); let contextMenu = $state>(); let prDetailsModal = $state>(); @@ -83,6 +94,31 @@ function addDescription() { descriptionVisible = true; } + + async function generateBranchName() { + if (!aiGenEnabled || !currentSeries) return; + + const hunks = currentSeries.patches.flatMap((p) => p.files.flatMap((f) => f.hunks)); + + const prompt = promptService.selectedBranchPrompt(project.id); + const messageResult = await aiService.summarizeBranch({ + hunks, + branchTemplate: prompt + }); + + if (isFailure(messageResult)) { + console.error(messageResult.failure); + showError('Failed to generate branch name', messageResult.failure); + + return; + } + + const message = messageResult.value; + + if (message && message !== currentSeries.name) { + branchController.updateSeriesName(branch.id, currentSeries.name, slugify(message)); + } + }
@@ -127,6 +163,7 @@ headName={name} seriesCount={branch.series?.length ?? 0} {addDescription} + onGenerateBranchName={generateBranchName} />
diff --git a/apps/desktop/src/lib/branch/StackingBranchHeaderContextMenu.svelte b/apps/desktop/src/lib/branch/StackingBranchHeaderContextMenu.svelte index 2400bdbf3..0b4875065 100644 --- a/apps/desktop/src/lib/branch/StackingBranchHeaderContextMenu.svelte +++ b/apps/desktop/src/lib/branch/StackingBranchHeaderContextMenu.svelte @@ -1,7 +1,10 @@ @@ -46,6 +63,14 @@ contextMenuEl?.close(); }} /> + { + onGenerateBranchName(); + contextMenuEl?.close(); + }} + disabled={!($aiGenEnabled && aiConfigurationValid)} + /> { diff --git a/apps/desktop/src/lib/stack/Stack.svelte b/apps/desktop/src/lib/stack/Stack.svelte index fb88aa1fb..612270875 100644 --- a/apps/desktop/src/lib/stack/Stack.svelte +++ b/apps/desktop/src/lib/stack/Stack.svelte @@ -2,22 +2,17 @@ import StackHeader from './StackHeader.svelte'; import StackSeries from './StackSeries.svelte'; import InfoMessage from '../shared/InfoMessage.svelte'; - import { PromptService } from '$lib/ai/promptService'; - import { AIService } from '$lib/ai/service'; import laneNewSvg from '$lib/assets/empty-state/lane-new.svg?raw'; import noChangesSvg from '$lib/assets/empty-state/lane-no-changes.svg?raw'; import { Project } from '$lib/backend/projects'; import Dropzones from '$lib/branch/Dropzones.svelte'; import StackingNewStackCard from '$lib/branch/StackingNewStackCard.svelte'; import CommitDialog from '$lib/commit/CommitDialog.svelte'; - import { projectAiGenEnabled } from '$lib/config/config'; import { stackingFeatureMultipleSeries } from '$lib/config/uiFeatureFlags'; import BranchFiles from '$lib/file/BranchFiles.svelte'; import { getGitHostChecksMonitor } from '$lib/gitHost/interface/gitHostChecksMonitor'; import { getGitHostListingService } from '$lib/gitHost/interface/gitHostListingService'; import { getGitHostPrMonitor } from '$lib/gitHost/interface/gitHostPrMonitor'; - import { showError } from '$lib/notifications/toasts'; - import { isFailure } from '$lib/result'; import ScrollableContainer from '$lib/scroll/ScrollableContainer.svelte'; import { SETTINGS, type Settings } from '$lib/settings/userSettings'; import Resizer from '$lib/shared/Resizer.svelte'; @@ -46,11 +41,6 @@ const branch = $derived($branchStore); - const aiGenEnabled = projectAiGenEnabled(project.id); - - const aiService = getContext(AIService); - const promptService = getContext(PromptService); - const userSettings = getContextStoreBySymbol(SETTINGS); const defaultBranchWidthRem = persisted(24, 'defaulBranchWidth' + project.id); const laneWidthKey = 'laneWidth_'; @@ -66,32 +56,6 @@ } }); - async function generateBranchName() { - if (!aiGenEnabled) return; - - const hunks = branch.files.flatMap((f) => f.hunks); - - const prompt = promptService.selectedBranchPrompt(project.id); - const messageResult = await aiService.summarizeBranch({ - hunks, - branchTemplate: prompt - }); - - if (isFailure(messageResult)) { - console.error(messageResult.failure); - showError('Failed to generate branch name', messageResult.failure); - - return; - } - - const message = messageResult.value; - - if (message && message !== branch.name) { - branch.name = message; - branchController.updateBranchName(branch.id, branch.name); - } - } - onMount(() => { laneWidth = lscache.get(laneWidthKey + branch.id); }); @@ -141,11 +105,7 @@ {#if $isLaneCollapsed}
- +
{:else} @@ -165,11 +125,7 @@ class="branch-card__contents" data-tauri-drag-region > - +
{#if branch.files?.length > 0}
diff --git a/apps/desktop/src/lib/stack/StackHeader.svelte b/apps/desktop/src/lib/stack/StackHeader.svelte index f17f00ceb..c6b5d474e 100644 --- a/apps/desktop/src/lib/stack/StackHeader.svelte +++ b/apps/desktop/src/lib/stack/StackHeader.svelte @@ -14,16 +14,10 @@ interface Props { uncommittedChanges?: number; isLaneCollapsed: Persisted; - onGenerateBranchName: () => void; stackPrs?: number; } - const { - uncommittedChanges = 0, - isLaneCollapsed, - onGenerateBranchName, - stackPrs = 0 - }: Props = $props(); + const { uncommittedChanges = 0, isLaneCollapsed, stackPrs = 0 }: Props = $props(); const branchController = getContext(BranchController); const branchStore = getContextStore(VirtualBranch); @@ -130,7 +124,6 @@ bind:contextMenuEl={contextMenu} target={meatballButtonEl} onCollapse={collapseLane} - {onGenerateBranchName} />