Skip to content

Commit 27abf34

Browse files
committed
Add rewrap toggle for commit message formatting
Introduce a "rewrap" option when viewing commit messages, making it easier to read if you often resize branches/stacks.
1 parent 4cd68a5 commit 27abf34

File tree

5 files changed

+70
-4
lines changed

5 files changed

+70
-4
lines changed

apps/desktop/src/components/CommitContextMenu.svelte

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<script lang="ts">
3131
import ContextMenu from '$components/ContextMenu.svelte';
3232
import { writeClipboard } from '$lib/backend/clipboard';
33+
import { rewrapCommitMessage } from '$lib/config/uiFeatureFlags';
3334
import { StackService } from '$lib/stacks/stackService.svelte';
3435
import { TestId } from '$lib/testing/testIds';
3536
import { openExternalUrl } from '$lib/utils/url';
@@ -157,5 +158,16 @@
157158
/>
158159
</ContextMenuSection>
159160
{/if}
161+
<ContextMenuSection>
162+
<ContextMenuItem
163+
icon="text-wrap"
164+
label={$rewrapCommitMessage ? 'Original' : 'Re-wrap'}
165+
disabled={commitInsertion.current.isLoading}
166+
onclick={() => {
167+
rewrapCommitMessage.set(!$rewrapCommitMessage);
168+
close();
169+
}}
170+
/>
171+
</ContextMenuSection>
160172
</ContextMenu>
161173
{/if}

apps/desktop/src/components/CommitDetails.svelte

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { TestId } from '$lib/testing/testIds';
77
import { UserService } from '$lib/user/userService';
88
import { splitMessage } from '$lib/utils/commitMessage';
9-
import { truncate } from '$lib/utils/string';
9+
import { rejoinParagraphs, truncate } from '$lib/utils/string';
1010
import { getContextStoreBySymbol, inject } from '@gitbutler/shared/context';
1111
import Icon from '@gitbutler/ui/Icon.svelte';
1212
import Tooltip from '@gitbutler/ui/Tooltip.svelte';
@@ -17,9 +17,10 @@
1717
1818
type Props = {
1919
commit: UpstreamCommit | Commit;
20+
rewrap?: boolean;
2021
};
2122
22-
const { commit }: Props = $props();
23+
const { commit, rewrap }: Props = $props();
2324
2425
const [userService] = inject(UserService, UiState);
2526
const userSettings = getContextStoreBySymbol<Settings, Writable<Settings>>(SETTINGS);
@@ -36,7 +37,8 @@
3637
const maxLength = $derived((messageWidthRem - 2) * 2 - 1 * (Math.pow(zoom, 2) - 1));
3738
3839
const message = $derived(commit.message);
39-
const description = $derived(splitMessage(message).description);
40+
const raw = $derived(splitMessage(message).description);
41+
const description = $derived(rewrap ? rejoinParagraphs(raw) : raw);
4042
const abbreviated = $derived(truncate(description, maxLength, 3));
4143
const isAbbrev = $derived(abbreviated !== description);
4244

apps/desktop/src/components/CommitView.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import { isLocalAndRemoteCommit } from '$components/lib';
1111
import { isCommit } from '$lib/branches/v3';
1212
import { type CommitKey } from '$lib/commits/commit';
13+
import { rewrapCommitMessage } from '$lib/config/uiFeatureFlags';
1314
import { DefaultForgeFactory } from '$lib/forge/forgeFactory.svelte';
1415
import { ModeService } from '$lib/mode/modeService';
1516
import { showToast } from '$lib/notifications/toasts';
@@ -238,7 +239,7 @@
238239
/>
239240
</div>
240241
{:else}
241-
<CommitDetails {commit} />
242+
<CommitDetails {commit} rewrap={$rewrapCommitMessage} />
242243
{/if}
243244
</div>
244245
</Drawer>

apps/desktop/src/lib/config/uiFeatureFlags.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ export const workspaceSwapPanels = persisted<
1313

1414
export const ircEnabled = persistWithExpiration(false, 'feature-irc', 1440 * 30);
1515
export const ircServer = persistWithExpiration('', 'feature-irc-server', 1440 * 30);
16+
export const rewrapCommitMessage = persistWithExpiration(true, 'rewrap-commit-msg', 1440 * 30);

apps/desktop/src/lib/utils/string.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,53 @@ export function truncate(text: string, maxChars: number, maxLines: number): stri
2828
return text.substring(0, maxChars) + '…';
2929
}
3030
}
31+
32+
export function rejoinParagraphs(text: string): string {
33+
const lines = text.split('\n');
34+
const result: string[] = [];
35+
let currentParagraph = '';
36+
37+
for (let i = 0; i < lines.length; i++) {
38+
const line = lines[i]!;
39+
const trimmedLine = line.trim();
40+
41+
// Check if this line should start a new block (headers, lists, code blocks, etc.)
42+
const isBlockElement = /^(#{1,6}\s|```|~~~|\*\s|-\s|\d+\.\s|>\s|\|\s|$)/.test(trimmedLine);
43+
44+
// Check if previous line was a block element
45+
const prevLine = i > 0 ? lines[i - 1]!.trim() : '';
46+
const prevWasBlock = /^(#{1,6}\s|```|~~~|\*\s|-\s|\d+\.\s|>\s|\|\s)/.test(prevLine);
47+
48+
// Empty line - signals paragraph break
49+
if (trimmedLine === '') {
50+
if (currentParagraph.trim()) {
51+
result.push(currentParagraph.trim());
52+
currentParagraph = '';
53+
}
54+
continue;
55+
}
56+
57+
// Start new paragraph for block elements or after block elements
58+
if (isBlockElement || prevWasBlock) {
59+
if (currentParagraph.trim()) {
60+
result.push(currentParagraph.trim());
61+
currentParagraph = '';
62+
}
63+
result.push(line);
64+
} else {
65+
// Continue current paragraph
66+
if (currentParagraph) {
67+
currentParagraph += ' ' + trimmedLine;
68+
} else {
69+
currentParagraph = trimmedLine;
70+
}
71+
}
72+
}
73+
74+
// Add any remaining paragraph
75+
if (currentParagraph.trim()) {
76+
result.push(currentParagraph.trim());
77+
}
78+
79+
return result.join('\n\n');
80+
}

0 commit comments

Comments
 (0)