Refactor a couple of things for clarity

This commit is contained in:
Mattias Granlund 2024-03-14 14:29:06 +01:00
parent f2856ea899
commit 263ef78ed6
6 changed files with 99 additions and 183 deletions

View File

@ -2,7 +2,7 @@
import BranchFiles from './BranchFiles.svelte';
import Tag from '$lib/components/Tag.svelte';
import TimeAgo from '$lib/components/TimeAgo.svelte';
import { projectCurrentCommitMessage } from '$lib/config/config';
import { persistedCommitMessage } from '$lib/config/config';
import { draggable } from '$lib/dragging/draggable';
import { draggableCommit, nonDraggable } from '$lib/dragging/draggables';
import { openExternalUrl } from '$lib/utils/url';
@ -34,7 +34,7 @@
export let branchId: string | undefined = undefined;
const selectedOwnership = writable(Ownership.default());
const currentCommitMessage = projectCurrentCommitMessage(projectId, branchId || '');
const currentCommitMessage = persistedCommitMessage(projectId, branchId || '');
let showFiles = false;

View File

@ -13,14 +13,13 @@
projectCommitGenerationExtraConcise,
projectCommitGenerationUseEmojis,
projectRunCommitHooks,
projectCurrentCommitMessage
persistedCommitMessage
} from '$lib/config/config';
import { persisted } from '$lib/persisted/persisted';
import * as toasts from '$lib/utils/toasts';
import { tooltip } from '$lib/utils/tooltip';
import { setAutoHeight, useAutoHeight } from '$lib/utils/useAutoHeight';
import { setAutoHeight } from '$lib/utils/useAutoHeight';
import { useResize } from '$lib/utils/useResize';
import { invoke } from '@tauri-apps/api/tauri';
import { createEventDispatcher } from 'svelte';
import { quintOut } from 'svelte/easing';
import { fly, slide } from 'svelte/transition';
@ -44,68 +43,62 @@
const aiGenEnabled = projectAiGenEnabled(projectId);
const runCommitHooks = projectRunCommitHooks(projectId);
const currentCommitMessage = projectCurrentCommitMessage(projectId, branch.id);
const commitMessage = persistedCommitMessage(projectId, branch.id);
const commitGenerationExtraConcise = projectCommitGenerationExtraConcise(projectId);
const commitGenerationUseEmojis = projectCommitGenerationUseEmojis(projectId);
function getCommitMessageTitleAndDescription(commitMessage: string) {
// Split the commit message into title and description
// get the first line as title and the rest as description
const [summary, description] = commitMessage.trim().split(/\n+(.*)/s);
return {
summary: summary || '',
description: description || ''
};
}
function concatCommitMessage(message: { summary: string; description: string }) {
return `${message.summary}\n${message.description}`;
}
let commitMessageSet = getCommitMessageTitleAndDescription($currentCommitMessage);
let isCommitting = false;
let aiLoading = false;
let summaryTextareaElement: HTMLTextAreaElement;
let descriptionTextareaElement: HTMLTextAreaElement;
let contextMenu: ContextMenu;
let summarizer: Summarizer | undefined;
// need to detect if the textareas wrapper is focused
let isTextareaFocused = false;
let isSecondTextareaFocused = false;
let titleTextArea: HTMLTextAreaElement;
let descriptionTextArea: HTMLTextAreaElement;
$: [title, description] = splitMessage($commitMessage);
$: if ($commitMessage) {
setAutoHeight(titleTextArea);
setAutoHeight(descriptionTextArea);
}
$: if (user) {
const aiProvider = new ButlerAIProvider(cloud, user);
summarizer = new Summarizer(aiProvider);
}
function splitMessage(message: string) {
const parts = message.trim().split(/\n+(.*)/s);
return [parts[0] || '', parts[1] || ''];
}
function concatMessage(title: string, description: string) {
const message = `${title}\n\n${description}`;
return message.trim();
}
function focusTextareaOnMount(el: HTMLTextAreaElement) {
if (el) el.focus();
}
async function commit() {
if (!commitMessageSet.summary) return;
const message = concatMessage(title, description);
if (!message) return;
isCommitting = true;
branchController
.commitBranch(
try {
await branchController.commitBranch(
branch.id,
concatCommitMessage(commitMessageSet),
message,
$selectedOwnership.toString(),
$runCommitHooks
)
.then(() => {
commitMessageSet.summary = '';
commitMessageSet.description = '';
currentCommitMessage.set('');
})
.finally(() => (isCommitting = false));
);
$commitMessage = '';
} finally {
isCommitting = false;
}
}
export function git_get_config(params: { key: string }) {
return invoke<string>('git_get_global_config', params);
}
let summarizer: Summarizer | undefined;
$: if (user) {
const aiProvider = new ButlerAIProvider(cloud, user);
summarizer = new Summarizer(aiProvider);
}
let isGeneratingCommitMessage = false;
async function generateCommitMessage(files: LocalFile[]) {
if (!user || !summarizer) return;
const diff = files
.map((f) => f.hunks.filter((h) => $selectedOwnership.containsHunk(f.id, h.id)))
.flat()
@ -114,9 +107,6 @@
.join('\n')
.slice(0, 5000);
if (!user) return;
if (!summarizer) return;
// Branches get their names generated only if there are at least 4 lines of code
// If the change is a 'one-liner', the branch name is either left as "virtual branch"
// or the user has to manually trigger the name generation from the meatball menu
@ -125,129 +115,79 @@
dispatch('action', 'generate-branch-name');
}
isGeneratingCommitMessage = true;
summarizer
.commit(diff, $commitGenerationUseEmojis, $commitGenerationExtraConcise)
.then((message) => {
commitMessageSet = getCommitMessageTitleAndDescription(message);
currentCommitMessage.set(message);
setTimeout(() => {
summaryTextareaElement.focus();
setAutoHeight(summaryTextareaElement);
setAutoHeight(descriptionTextareaElement);
}, 0);
})
.catch(() => {
toasts.error('Failed to generate commit message');
})
.finally(() => {
isGeneratingCommitMessage = false;
});
}
const commitGenerationExtraConcise = projectCommitGenerationExtraConcise(projectId);
const commitGenerationUseEmojis = projectCommitGenerationUseEmojis(projectId);
let contextMenu: ContextMenu;
// concat commit message set into a single commit message
$: if (commitMessageSet.summary.length > 0) {
currentCommitMessage.set(concatCommitMessage(commitMessageSet));
}
// move description to title if title is empty
$: if (commitMessageSet.summary.length == 0 && commitMessageSet.description.length > 0) {
const messageSet = getCommitMessageTitleAndDescription(commitMessageSet.description);
commitMessageSet.summary = messageSet.summary;
commitMessageSet.description = messageSet.description;
}
// set auto height on textareas on each message change
$: if (commitMessageSet) {
setAutoHeight(summaryTextareaElement);
setAutoHeight(descriptionTextareaElement);
aiLoading = true;
try {
$commitMessage = await summarizer.commit(
diff,
$commitGenerationUseEmojis,
$commitGenerationExtraConcise
);
} catch {
toasts.error('Failed to generate commit message');
} finally {
aiLoading = false;
}
setTimeout(() => titleTextArea.focus(), 0);
}
</script>
<div class="commit-box" class:commit-box__expanded={$expanded}>
{#if $expanded}
<div class="commit-box__expander" transition:slide={{ duration: 150, easing: quintOut }}>
<div
class="commit-box__textarea-wrapper text-input"
class:text-input__focused={$expanded && (isTextareaFocused || isSecondTextareaFocused)}
>
<div class="commit-box__textarea-wrapper text-input">
<textarea
bind:this={summaryTextareaElement}
bind:value={commitMessageSet.summary}
value={title}
placeholder="Commit summary"
disabled={aiLoading}
class="text-base-body-13 text-semibold commit-box__textarea commit-box__textarea__title"
class:commit-box__textarea_bottom-padding={title.length == 0 && description.length == 0}
spellcheck="false"
rows="1"
bind:this={titleTextArea}
use:focusTextareaOnMount
use:useResize={() => {
setAutoHeight(summaryTextareaElement);
setAutoHeight(descriptionTextareaElement);
}}
on:input={useAutoHeight}
on:focus={(e) => {
useAutoHeight(e);
isTextareaFocused = true;
}}
on:blur={() => {
isTextareaFocused = false;
setAutoHeight(titleTextArea);
setAutoHeight(descriptionTextArea);
}}
on:focus={(e) => setAutoHeight(e.currentTarget)}
on:input={() => ($commitMessage = concatMessage(titleTextArea.value, description))}
on:keydown={(e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
commit();
}
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') commit();
if (e.key === 'Tab' || e.key === 'Enter') {
e.preventDefault();
descriptionTextareaElement.focus();
descriptionTextArea.focus();
}
}}
spellcheck={false}
class="text-base-body-13 text-semibold commit-box__textarea commit-box__textarea__title"
class:commit-box__textarea_bottom-padding={commitMessageSet.description.length == 0 &&
commitMessageSet.summary.length == 0}
rows="1"
disabled={isGeneratingCommitMessage}
placeholder="Commit summary"
/>
{#if commitMessageSet.summary.length > 0}
{#if title.length > 0}
<textarea
bind:this={descriptionTextareaElement}
bind:value={commitMessageSet.description}
on:focus={(e) => {
useAutoHeight(e);
isSecondTextareaFocused = true;
}}
on:blur={() => {
isSecondTextareaFocused = false;
}}
use:setAutoHeight
spellcheck={false}
class="text-base-body-13 commit-box__textarea commit-box__textarea__description"
class:commit-box__textarea_bottom-padding={commitMessageSet.summary.length > 0}
rows="1"
disabled={isGeneratingCommitMessage}
value={description}
disabled={aiLoading}
placeholder="Commit description (optional)"
class="text-base-body-13 commit-box__textarea commit-box__textarea__description"
class:commit-box__textarea_bottom-padding={description.length > 0}
spellcheck="false"
rows="1"
bind:this={descriptionTextArea}
on:focus={(e) => setAutoHeight(e.currentTarget)}
on:input={() => ($commitMessage = concatMessage(title, descriptionTextArea.value))}
on:keydown={(e) => {
if (commitMessageSet.description.length == 0 && e.key === 'Backspace') {
const value = e.currentTarget.value;
if (e.key == 'Backspace' && value.length == 0) {
e.preventDefault();
summaryTextareaElement.focus();
}
// select previous textarea on cmd+a if this textarea is empty
if (
e.key === 'a' &&
(e.metaKey || e.ctrlKey) &&
commitMessageSet.description.length == 0
) {
titleTextArea.focus();
setAutoHeight(e.currentTarget);
} else if (e.key == 'a' && (e.metaKey || e.ctrlKey) && value.length == 0) {
// select previous textarea on cmd+a if this textarea is empty
e.preventDefault();
summaryTextareaElement.focus();
summaryTextareaElement.setSelectionRange(0, summaryTextareaElement.value.length);
titleTextArea.select();
}
}}
/>
{/if}
{#if commitMessageSet.summary.length > 50}
{#if title.length > 50}
<div
transition:fly={{ y: 2, duration: 150 }}
class="commit-box__textarea-tooltip"
@ -271,7 +211,7 @@
icon="ai-small"
color="neutral"
disabled={!$aiGenEnabled || !user}
loading={isGeneratingCommitMessage}
loading={aiLoading}
on:click={() => generateCommitMessage(branch.files)}
>
Generate message
@ -315,8 +255,7 @@
color="primary"
kind="filled"
loading={isCommitting}
disabled={(isCommitting || !commitMessageSet.summary || $selectedOwnership.isEmpty()) &&
$expanded}
disabled={(isCommitting || !title || $selectedOwnership.isEmpty()) && $expanded}
id="commit-to-branch"
on:click={() => {
if ($expanded) {
@ -341,19 +280,21 @@
transition: background-color var(--transition-medium);
border-radius: 0 0 var(--radius-m) var(--radius-m);
}
.commit-box__expander {
display: flex;
flex-direction: column;
margin-bottom: var(--space-12);
}
.commit-box__textarea-wrapper {
position: relative;
display: flex;
flex-direction: column;
gap: var(--space-4);
/* padding: var(--space-12) var(--space-12) var(--space-48) var(--space-12); */
padding: 0;
}
.commit-box__textarea {
overflow: hidden;
display: flex;
@ -361,9 +302,7 @@
align-items: flex-end;
gap: var(--space-16);
background: none;
resize: none;
&:focus {
outline: none;
}
@ -405,7 +344,6 @@
gap: var(--space-6);
}
/* modifiers */
.commit-box__expanded {
background-color: var(--clr-theme-container-pale);
}

View File

@ -51,10 +51,6 @@ export function projectLaneCollapsed(projectId: string, laneId: string): Persist
return persisted(false, key + projectId + '_' + laneId);
}
export function projectCurrentCommitMessage(
projectId: string,
branchId: string
): Persisted<string> {
const key = 'projectCurrentCommitMessage_';
return persisted('', key + projectId + '_' + branchId);
export function persistedCommitMessage(projectId: string, branchId: string): Persisted<string> {
return persisted('', 'projectCurrentCommitMessage_' + projectId + '_' + branchId);
}

View File

@ -1,12 +1,5 @@
export function setAutoHeight(element: HTMLTextAreaElement) {
if (!element) return;
element.style.height = 'auto';
element.style.height = `${element.scrollHeight + 2}px`;
}
export function useAutoHeight(event: Event) {
const textarea = event.target as HTMLTextAreaElement;
setAutoHeight(textarea);
}

View File

@ -64,6 +64,7 @@ export class BranchController {
} catch (err: any) {
toasts.error('Failed to commit changes');
posthog.capture('Commit Failed', err);
throw err;
}
}

View File

@ -18,7 +18,8 @@
);
}
&:focus {
&:focus,
&:focus-within {
background: color-mix(
in srgb,
var(--clr-theme-container-light),
@ -42,16 +43,3 @@
background-color: var(--clr-theme-container-pale);
}
}
.text-input__focused {
border-color: color-mix(
in srgb,
var(--clr-theme-container-outline-light),
var(--darken-tint-extradark)
);
background: color-mix(
in srgb,
var(--clr-theme-container-light),
var(--clr-theme-container-pale) 20%
);
}