Move branch lane to branch card

This commit is contained in:
Mattias Granlund 2023-11-27 19:36:43 +01:00
parent 7350fc59d0
commit 9471e8567a
4 changed files with 494 additions and 452 deletions

View File

@ -37,8 +37,7 @@
<div class="p-4">Loading...</div>
{:else}
<div
id="branch-lanes"
class="flex h-full flex-shrink flex-grow items-start px-1"
class="board"
role="group"
bind:this={dropZone}
on:dragover={(e) => {
@ -171,3 +170,14 @@
{/if}
</div>
{/if}
<style lang="postcss">
.board {
display: flex;
flex-grow: 1;
flex-shrink: 1;
align-items: start;
height: 100%;
padding: var(--space-16);
}
</style>

View File

@ -0,0 +1,460 @@
<script lang="ts">
import type { BaseBranch, Branch, Commit } from '$lib/vbranches/types';
import { getContext, onMount } from 'svelte';
import { dropzone } from '$lib/utils/draggable';
import {
isDraggableHunk,
isDraggableFile,
isDraggableCommit,
type DraggableCommit,
type DraggableFile,
type DraggableHunk
} from '$lib/draggables';
import { Ownership } from '$lib/vbranches/ownership';
import { getExpandedWithCacheFallback, setExpandedWithCache } from './cache';
import type { BranchController } from '$lib/vbranches/branchController';
import { quintOut } from 'svelte/easing';
import { crossfade } from 'svelte/transition';
import type { User, getCloudApiClient } from '$lib/backend/cloud';
import Resizer from '$lib/components/Resizer.svelte';
import { SETTINGS_CONTEXT, type SettingsStore } from '$lib/settings/userSettings';
import lscache from 'lscache';
import CommitDialog from './CommitDialog.svelte';
import { writable } from 'svelte/store';
import { computedAddedRemoved } from '$lib/vbranches/fileStatus';
import { getPullRequestByBranch, createPullRequest } from '$lib/github/pullrequest';
import type { GitHubIntegrationContext } from '$lib/github/types';
import { isDraggableRemoteCommit, type DraggableRemoteCommit } from '$lib/draggables';
import BranchHeader from './BranchHeader.svelte';
import UpstreamCommits from './UpstreamCommits.svelte';
import BranchFiles from './BranchFiles.svelte';
import LocalCommits from './LocalCommits.svelte';
import RemoteCommits from './RemoteCommits.svelte';
import IntegratedCommits from './IntegratedCommits.svelte';
const [send, receive] = crossfade({
duration: (d) => Math.sqrt(d * 200),
fallback(node) {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
return {
duration: 600,
easing: quintOut,
css: (t) => `
transform: ${transform} scale(${t});
opacity: ${t}
`
};
}
});
export let branch: Branch;
export let readonly = false;
export let projectId: string;
export let base: BaseBranch | undefined | null;
export let cloudEnabled: boolean;
export let cloud: ReturnType<typeof getCloudApiClient>;
export let branchController: BranchController;
export let maximized = false;
export let branchCount = 1;
export let githubContext: GitHubIntegrationContext | undefined;
export let user: User | undefined;
const userSettings = getContext<SettingsStore>(SETTINGS_CONTEXT);
const allExpanded = writable(false);
const allCollapsed = writable(false);
let rsViewport: HTMLElement;
let viewport: HTMLElement;
let contents: HTMLElement;
const laneWidthKey = 'laneWidth:';
let laneWidth: number;
$: prPromise =
githubContext && branch.upstream
? getPullRequestByBranch(githubContext, branch.upstream?.name.split('/').slice(-1)[0])
: undefined;
$: branchName = branch.upstream?.name.split('/').slice(-1)[0];
function createPr() {
if (githubContext && base?.branchName && branchName) {
createPullRequest(
githubContext,
branchName,
base.branchName.split('/').slice(-1)[0],
branch.name,
branch.notes
).then((pr) => {
prPromise = Promise.resolve(pr);
});
}
}
$: {
// On refresh we need to check expansion status from localStorage
branch.files && expandFromCache();
}
function expandFromCache() {
// Exercise cache lookup for all files.
$allExpanded = branch.files.every((f) => getExpandedWithCacheFallback(f));
$allCollapsed = branch.files.every((f) => getExpandedWithCacheFallback(f) == false);
}
function handleCollapseAll() {
branch.files.forEach((f) => setExpandedWithCache(f, false));
$allExpanded = false;
branch.files = branch.files;
}
function handleExpandAll() {
branch.files.forEach((f) => setExpandedWithCache(f, true));
$allExpanded = true;
branch.files = branch.files;
}
let commitDialogShown = false;
$: if (commitDialogShown && branch.files.length === 0) {
commitDialogShown = false;
}
function generateBranchName() {
const diff = branch.files
.map((f) => f.hunks)
.flat()
.map((h) => h.diff)
.flat()
.join('\n')
.slice(0, 5000);
if (user) {
cloud.summarize.branch(user.access_token, { diff }).then((result) => {
if (result.message && result.message !== branch.name) {
branch.name = result.message;
branchController.updateBranchName(branch.id, branch.name);
}
});
}
}
$: linesTouched = computedAddedRemoved(...branch.files);
$: if (
branch.name.toLowerCase().includes('virtual branch') &&
linesTouched.added + linesTouched.removed > 4
) {
generateBranchName();
}
function resetHeadCommit() {
if (branch.commits.length > 1) {
branchController.resetBranch(branch.id, branch.commits[1].id);
} else if (branch.commits.length === 1 && base) {
branchController.resetBranch(branch.id, base.baseSha);
}
}
onMount(() => {
expandFromCache();
laneWidth = lscache.get(laneWidthKey + branch.id) ?? $userSettings.defaultLaneWidth;
});
const selectedOwnership = writable(Ownership.fromBranch(branch));
$: if (commitDialogShown) selectedOwnership.set(Ownership.fromBranch(branch));
function acceptCherrypick(data: any) {
return isDraggableRemoteCommit(data) && data.branchId == branch.id;
}
function onCherrypicked(data: DraggableRemoteCommit) {
branchController.cherryPick(branch.id, data.remoteCommit.id);
}
function acceptBranchDrop(data: any) {
if (isDraggableHunk(data) && data.branchId != branch.id) {
return true;
} else if (isDraggableFile(data) && data.branchId != branch.id) {
return true;
} else {
return false;
}
}
function onBranchDrop(data: DraggableHunk | DraggableFile) {
if (isDraggableHunk(data)) {
const newOwnership = `${data.hunk.filePath}:${data.hunk.id}`;
branchController.updateBranchOwnership(
branch.id,
(newOwnership + '\n' + branch.ownership).trim()
);
} else if (isDraggableFile(data)) {
const newOwnership = `${data.file.path}:${data.file.hunks.map(({ id }) => id).join(',')}`;
branchController.updateBranchOwnership(
branch.id,
(newOwnership + '\n' + branch.ownership).trim()
);
}
}
function acceptAmend(commit: Commit) {
return (data: any) => {
if (
isDraggableHunk(data) &&
data.branchId == branch.id &&
commit.id == branch.commits.at(0)?.id
) {
return true;
} else if (
isDraggableFile(data) &&
data.branchId == branch.id &&
commit.id == branch.commits.at(0)?.id
) {
return true;
} else {
return false;
}
};
}
function onAmend(data: DraggableFile | DraggableHunk) {
if (isDraggableHunk(data)) {
const newOwnership = `${data.hunk.filePath}:${data.hunk.id}`;
branchController.amendBranch(branch.id, newOwnership);
} else if (isDraggableFile(data)) {
const newOwnership = `${data.file.path}:${data.file.hunks.map(({ id }) => id).join(',')}`;
branchController.amendBranch(branch.id, newOwnership);
}
}
function acceptSquash(commit: Commit) {
return (data: any) => {
return (
isDraggableCommit(data) &&
data.branchId == branch.id &&
(commit.parentIds.includes(data.commit.id) || data.commit.parentIds.includes(commit.id))
);
};
}
function onSquash(commit: Commit) {
function isParentOf(commit: Commit, other: Commit) {
return commit.parentIds.includes(other.id);
}
return (data: DraggableCommit) => {
if (isParentOf(commit, data.commit)) {
branchController.squashBranchCommit(data.branchId, commit.id);
} else if (isParentOf(data.commit, commit)) {
branchController.squashBranchCommit(data.branchId, data.commit.id);
}
};
}
</script>
<div class="branch-card" style:width={maximized ? '100%' : `${laneWidth}px`}>
<div class="flex">
<div class="border-color-4 flex flex-grow flex-col">
<BranchHeader
{branchController}
{branch}
{allCollapsed}
{allExpanded}
on:action={(e) => {
if (e.detail == 'expand') {
handleExpandAll();
} else if (e.detail == 'collapse') {
handleCollapseAll();
} else if (e.detail == 'generate-branch-name') {
generateBranchName();
}
}}
/>
{#if branch.upstream?.commits.length && branch.upstream?.commits.length > 0 && !branch.conflicted}
<UpstreamCommits
upstream={branch.upstream}
branchId={branch.id}
{branchController}
{branchCount}
{projectId}
{base}
/>
{/if}
</div>
</div>
<div
class="relative flex flex-grow overflow-y-hidden"
use:dropzone={{
hover: 'cherrypick-dz-hover',
active: 'cherrypick-dz-active',
accepts: acceptCherrypick,
onDrop: onCherrypicked
}}
use:dropzone={{
hover: 'lane-dz-hover',
active: 'lane-dz-active',
accepts: acceptBranchDrop,
onDrop: onBranchDrop
}}
>
<!-- TODO: Figure out why z-10 is necessary for expand up/down to not come out on top -->
<div
class="cherrypick-dz-marker absolute z-10 hidden h-full w-full items-center justify-center rounded bg-blue-100/70 outline-dashed outline-2 -outline-offset-8 outline-light-600 dark:bg-blue-900/60 dark:outline-dark-300"
>
<div class="hover-text invisible font-semibold">Apply here</div>
</div>
<!-- TODO: Figure out why z-10 is necessary for expand up/down to not come out on top -->
<div
class="lane-dz-marker absolute z-10 hidden h-full w-full items-center justify-center rounded bg-blue-100/70 outline-dashed outline-2 -outline-offset-8 outline-light-600 dark:bg-blue-900/60 dark:outline-dark-300"
>
<div class="hover-text invisible font-semibold">Move here</div>
</div>
<div bind:this={viewport} class="scroll-container hide-native-scrollbar">
<div bind:this={contents} class="flex min-h-full flex-col">
{#if branch.files?.length > 0}
<BranchFiles {branch} {readonly} {selectedOwnership} />
{#if branch.active}
<CommitDialog
{projectId}
{branchController}
{branch}
{cloudEnabled}
{cloud}
{selectedOwnership}
{user}
/>
{/if}
{:else if branch.commits.length == 0}
<div class="new-branch" data-dnd-ignore>
<h1 class="text-base-16 text-semibold">Nothing on this branch yet</h1>
<p class="px-12">Get some work done, then throw some files my way!</p>
</div>
{:else}
<!-- attention: these markers have custom css at the bottom of thise file -->
<div class="no-changes" data-dnd-ignore>
<h1 class="text-base-16 text-semibold">No uncommitted changes on this branch</h1>
</div>
{/if}
{#if branch.commits.length > 0}
<LocalCommits
{branch}
{base}
{send}
{receive}
{prPromise}
{githubContext}
{projectId}
{branchController}
{acceptAmend}
{acceptSquash}
{onAmend}
{onSquash}
{resetHeadCommit}
{createPr}
/>
<RemoteCommits
{branch}
{base}
{send}
{receive}
{prPromise}
{githubContext}
{projectId}
{acceptAmend}
{acceptSquash}
{onAmend}
{onSquash}
{resetHeadCommit}
{createPr}
/>
<IntegratedCommits {branch} {base} {send} {receive} {projectId} />
{/if}
</div>
</div>
</div>
</div>
{#if !maximized}
<Resizer
minWidth={330}
viewport={rsViewport}
direction="horizontal"
class="z-30"
on:width={(e) => {
laneWidth = e.detail;
lscache.set(laneWidthKey + branch.id, e.detail, 7 * 1440); // 7 day ttl
userSettings.update((s) => ({
...s,
defaultLaneWidth: e.detail
}));
}}
/>
{/if}
<style lang="postcss">
.branch-card {
display: flex;
flex-grow: 1;
flex-direction: column;
cursor: default;
overflow-x: hidden;
background: var(--clr-theme-container-light);
border-radius: var(--radius-m);
}
.scroll-container {
max-height: 100%;
flex-grow: 1;
flex-direction: column;
display: flex;
overflow-y: scroll;
overscroll-behavior: none;
}
.new-branch,
.no-changes {
display: flex;
flex-grow: 1;
flex-direction: column;
background: var(--clr-theme-container-light);
justify-content: center;
gap: var(--space-8);
& h1 {
color: var(--clr-theme-scale-ntrl-40);
text-align: center;
}
}
.new-branch p {
color: var(--clr-theme-scale-ntrl-50);
text-align: center;
}
/* hunks drop zone */
:global(.lane-dz-active .lane-dz-marker) {
@apply flex;
}
:global(.lane-dz-hover .hover-text) {
@apply visible;
}
/* cherry pick drop zone */
:global(.cherrypick-dz-active .cherrypick-dz-marker) {
@apply flex;
}
:global(.cherrypick-dz-hover .hover-text) {
@apply visible;
}
/* squash drop zone */
:global(.squash-dz-active .squash-dz-marker) {
@apply flex;
}
:global(.squash-dz-hover .hover-text) {
@apply visible;
}
</style>

View File

@ -1,56 +1,9 @@
<script lang="ts">
import type { BaseBranch, Branch, Commit } from '$lib/vbranches/types';
import { getContext, onMount } from 'svelte';
import { dropzone } from '$lib/utils/draggable';
import {
isDraggableHunk,
isDraggableFile,
isDraggableCommit,
type DraggableCommit,
type DraggableFile,
type DraggableHunk
} from '$lib/draggables';
import { Ownership } from '$lib/vbranches/ownership';
import { getExpandedWithCacheFallback, setExpandedWithCache } from './cache';
import type { BaseBranch, Branch } from '$lib/vbranches/types';
import type { BranchController } from '$lib/vbranches/branchController';
import { quintOut } from 'svelte/easing';
import { crossfade } from 'svelte/transition';
import type { User, getCloudApiClient } from '$lib/backend/cloud';
import Scrollbar from '$lib/components/Scrollbar.svelte';
import Resizer from '$lib/components/Resizer.svelte';
import { SETTINGS_CONTEXT, type SettingsStore } from '$lib/settings/userSettings';
import lscache from 'lscache';
import CommitDialog from './CommitDialog.svelte';
import { writable } from 'svelte/store';
import { computedAddedRemoved } from '$lib/vbranches/fileStatus';
import { getPullRequestByBranch, createPullRequest } from '$lib/github/pullrequest';
import type { GitHubIntegrationContext } from '$lib/github/types';
import Tooltip from '$lib/components/Tooltip.svelte';
import { isDraggableRemoteCommit, type DraggableRemoteCommit } from '$lib/draggables';
import BranchHeader from './BranchHeader.svelte';
import UpstreamCommits from './UpstreamCommits.svelte';
import BranchFiles from './BranchFiles.svelte';
import LocalCommits from './LocalCommits.svelte';
import RemoteCommits from './RemoteCommits.svelte';
import IntegratedCommits from './IntegratedCommits.svelte';
const [send, receive] = crossfade({
duration: (d) => Math.sqrt(d * 200),
fallback(node) {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
return {
duration: 600,
easing: quintOut,
css: (t) => `
transform: ${transform} scale(${t});
opacity: ${t}
`
};
}
});
import BranchCard from './BranchCard.svelte';
export let branch: Branch;
export let readonly = false;
@ -63,412 +16,31 @@
export let branchCount = 1;
export let githubContext: GitHubIntegrationContext | undefined;
export let user: User | undefined;
const userSettings = getContext<SettingsStore>(SETTINGS_CONTEXT);
const allExpanded = writable(false);
const allCollapsed = writable(false);
let viewport: Element;
let contents: Element;
let rsViewport: HTMLElement;
let laneWidth: number;
const laneWidthKey = 'laneWidth:';
$: prPromise =
githubContext && branch.upstream
? getPullRequestByBranch(githubContext, branch.upstream?.name.split('/').slice(-1)[0])
: undefined;
$: branchName = branch.upstream?.name.split('/').slice(-1)[0];
function createPr() {
if (githubContext && base?.branchName && branchName) {
createPullRequest(
githubContext,
branchName,
base.branchName.split('/').slice(-1)[0],
branch.name,
branch.notes
).then((pr) => {
prPromise = Promise.resolve(pr);
});
}
}
$: {
// On refresh we need to check expansion status from localStorage
branch.files && expandFromCache();
}
function expandFromCache() {
// Exercise cache lookup for all files.
$allExpanded = branch.files.every((f) => getExpandedWithCacheFallback(f));
$allCollapsed = branch.files.every((f) => getExpandedWithCacheFallback(f) == false);
}
function handleCollapseAll() {
branch.files.forEach((f) => setExpandedWithCache(f, false));
$allExpanded = false;
branch.files = branch.files;
}
function handleExpandAll() {
branch.files.forEach((f) => setExpandedWithCache(f, true));
$allExpanded = true;
branch.files = branch.files;
}
let commitDialogShown = false;
$: if (commitDialogShown && branch.files.length === 0) {
commitDialogShown = false;
}
function generateBranchName() {
const diff = branch.files
.map((f) => f.hunks)
.flat()
.map((h) => h.diff)
.flat()
.join('\n')
.slice(0, 5000);
if (user) {
cloud.summarize.branch(user.access_token, { diff }).then((result) => {
if (result.message && result.message !== branch.name) {
branch.name = result.message;
branchController.updateBranchName(branch.id, branch.name);
}
});
}
}
$: linesTouched = computedAddedRemoved(...branch.files);
$: if (
branch.name.toLowerCase().includes('virtual branch') &&
linesTouched.added + linesTouched.removed > 4
) {
generateBranchName();
}
function resetHeadCommit() {
if (branch.commits.length > 1) {
branchController.resetBranch(branch.id, branch.commits[1].id);
} else if (branch.commits.length === 1 && base) {
branchController.resetBranch(branch.id, base.baseSha);
}
}
onMount(() => {
expandFromCache();
laneWidth = lscache.get(laneWidthKey + branch.id) ?? $userSettings.defaultLaneWidth;
});
const selectedOwnership = writable(Ownership.fromBranch(branch));
$: if (commitDialogShown) selectedOwnership.set(Ownership.fromBranch(branch));
function acceptCherrypick(data: any) {
return isDraggableRemoteCommit(data) && data.branchId == branch.id;
}
function onCherrypicked(data: DraggableRemoteCommit) {
branchController.cherryPick(branch.id, data.remoteCommit.id);
}
function acceptBranchDrop(data: any) {
if (isDraggableHunk(data) && data.branchId != branch.id) {
return true;
} else if (isDraggableFile(data) && data.branchId != branch.id) {
return true;
} else {
return false;
}
}
function onBranchDrop(data: DraggableHunk | DraggableFile) {
if (isDraggableHunk(data)) {
const newOwnership = `${data.hunk.filePath}:${data.hunk.id}`;
branchController.updateBranchOwnership(
branch.id,
(newOwnership + '\n' + branch.ownership).trim()
);
} else if (isDraggableFile(data)) {
const newOwnership = `${data.file.path}:${data.file.hunks.map(({ id }) => id).join(',')}`;
branchController.updateBranchOwnership(
branch.id,
(newOwnership + '\n' + branch.ownership).trim()
);
}
}
function acceptAmend(commit: Commit) {
return (data: any) => {
if (
isDraggableHunk(data) &&
data.branchId == branch.id &&
commit.id == branch.commits.at(0)?.id
) {
return true;
} else if (
isDraggableFile(data) &&
data.branchId == branch.id &&
commit.id == branch.commits.at(0)?.id
) {
return true;
} else {
return false;
}
};
}
function onAmend(data: DraggableFile | DraggableHunk) {
if (isDraggableHunk(data)) {
const newOwnership = `${data.hunk.filePath}:${data.hunk.id}`;
branchController.amendBranch(branch.id, newOwnership);
} else if (isDraggableFile(data)) {
const newOwnership = `${data.file.path}:${data.file.hunks.map(({ id }) => id).join(',')}`;
branchController.amendBranch(branch.id, newOwnership);
}
}
function acceptSquash(commit: Commit) {
return (data: any) => {
return (
isDraggableCommit(data) &&
data.branchId == branch.id &&
(commit.parentIds.includes(data.commit.id) || data.commit.parentIds.includes(commit.id))
);
};
}
function onSquash(commit: Commit) {
function isParentOf(commit: Commit, other: Commit) {
return commit.parentIds.includes(other.id);
}
return (data: DraggableCommit) => {
if (isParentOf(commit, data.commit)) {
branchController.squashBranchCommit(data.branchId, commit.id);
} else if (isParentOf(data.commit, commit)) {
branchController.squashBranchCommit(data.branchId, data.commit.id);
}
};
}
</script>
<div
class="relative flex h-full shrink-0 snap-center"
style:width={maximized ? '100%' : `${laneWidth}px`}
>
<div class="wrapper">
<div class="absolute h-3 w-full" data-tauri-drag-region></div>
<div bind:this={rsViewport} class="branch-card">
<div
class="flex h-full flex-col overflow-hidden rounded-lg border"
style:background-color="var(--bg-surface)"
style:border-color="var(--border-surface)"
>
<div class="flex">
<div class="border-color-4 flex flex-grow flex-col">
<BranchHeader
{branchController}
{branch}
{allCollapsed}
{allExpanded}
on:action={(e) => {
if (e.detail == 'expand') {
handleExpandAll();
} else if (e.detail == 'collapse') {
handleCollapseAll();
} else if (e.detail == 'generate-branch-name') {
generateBranchName();
}
}}
/>
{#if branch.upstream?.commits.length && branch.upstream?.commits.length > 0 && !branch.conflicted}
<UpstreamCommits
upstream={branch.upstream}
branchId={branch.id}
{branchController}
{branchCount}
{projectId}
{base}
/>
{/if}
</div>
</div>
<div
class="relative flex flex-grow overflow-y-hidden"
use:dropzone={{
hover: 'cherrypick-dz-hover',
active: 'cherrypick-dz-active',
accepts: acceptCherrypick,
onDrop: onCherrypicked
}}
use:dropzone={{
hover: 'lane-dz-hover',
active: 'lane-dz-active',
accepts: acceptBranchDrop,
onDrop: onBranchDrop
}}
>
<!-- TODO: Figure out why z-10 is necessary for expand up/down to not come out on top -->
<div
class="cherrypick-dz-marker absolute z-10 hidden h-full w-full items-center justify-center rounded bg-blue-100/70 outline-dashed outline-2 -outline-offset-8 outline-light-600 dark:bg-blue-900/60 dark:outline-dark-300"
>
<div class="hover-text invisible font-semibold">Apply here</div>
</div>
<!-- TODO: Figure out why z-10 is necessary for expand up/down to not come out on top -->
<div
class="lane-dz-marker absolute z-10 hidden h-full w-full items-center justify-center rounded bg-blue-100/70 outline-dashed outline-2 -outline-offset-8 outline-light-600 dark:bg-blue-900/60 dark:outline-dark-300"
>
<div class="hover-text invisible font-semibold">Move here</div>
</div>
<div bind:this={viewport} class="scroll-container hide-native-scrollbar">
<div bind:this={contents} class="flex min-h-full flex-col">
{#if branch.files?.length > 0}
<BranchFiles {branch} {readonly} {selectedOwnership} />
{#if branch.active}
<CommitDialog
{projectId}
{branchController}
{branch}
{cloudEnabled}
{cloud}
{selectedOwnership}
{user}
/>
{/if}
{:else if branch.commits.length == 0}
<div class="new-branch" data-dnd-ignore>
<h1 class="text-base-16 text-semibold">Nothing on this branch yet</h1>
<p class="px-12">Get some work done, then throw some files my way!</p>
</div>
{:else}
<!-- attention: these markers have custom css at the bottom of thise file -->
<div class="no-changes" data-dnd-ignore>
<h1 class="text-base-16 text-semibold">No uncommitted changes on this branch</h1>
</div>
{/if}
{#if branch.commits.length > 0}
<LocalCommits
{branch}
{base}
{send}
{receive}
{prPromise}
{githubContext}
{projectId}
{branchController}
{acceptAmend}
{acceptSquash}
{onAmend}
{onSquash}
{resetHeadCommit}
{createPr}
/>
<RemoteCommits
{branch}
{base}
{send}
{receive}
{prPromise}
{githubContext}
{projectId}
{acceptAmend}
{acceptSquash}
{onAmend}
{onSquash}
{resetHeadCommit}
{createPr}
/>
<IntegratedCommits {branch} {base} {send} {receive} {projectId} />
{/if}
</div>
</div>
<Scrollbar {viewport} {contents} width="0.4rem" />
</div>
</div>
</div>
{#if !maximized}
<Resizer
minWidth={330}
viewport={rsViewport}
direction="horizontal"
class="z-30"
on:width={(e) => {
laneWidth = e.detail;
lscache.set(laneWidthKey + branch.id, e.detail, 7 * 1440); // 7 day ttl
userSettings.update((s) => ({
...s,
defaultLaneWidth: e.detail
}));
}}
/>
{/if}
<BranchCard
{branch}
{readonly}
{projectId}
{base}
{cloudEnabled}
{cloud}
{branchController}
{maximized}
{branchCount}
{githubContext}
{user}
/>
</div>
<style lang="postcss">
/* hunks drop zone */
:global(.lane-dz-active .lane-dz-marker) {
@apply flex;
}
:global(.lane-dz-hover .hover-text) {
@apply visible;
}
/* cherry pick drop zone */
:global(.cherrypick-dz-active .cherrypick-dz-marker) {
@apply flex;
}
:global(.cherrypick-dz-hover .hover-text) {
@apply visible;
}
/* squash drop zone */
:global(.squash-dz-active .squash-dz-marker) {
@apply flex;
}
:global(.squash-dz-hover .hover-text) {
@apply visible;
}
.branch-card {
.wrapper {
border: 1px solid var(--clr-theme-container-outline-light);
display: flex;
flex-grow: 1;
flex-direction: column;
cursor: default;
overflow-x: hidden;
padding: var(--space-12) var(--space-4);
}
.scroll-container {
max-height: 100%;
flex-grow: 1;
flex-direction: column;
display: flex;
overflow-y: scroll;
overscroll-behavior: none;
}
.new-branch,
.no-changes {
display: flex;
flex-grow: 1;
flex-direction: column;
background: var(--clr-theme-container-light);
justify-content: center;
gap: var(--space-8);
& h1 {
color: var(--clr-theme-scale-ntrl-40);
text-align: center;
}
}
.new-branch p {
color: var(--clr-theme-scale-ntrl-50);
text-align: center;
height: 100%;
flex-shrink: 0;
border-radius: var(--radius-m);
}
</style>

View File

@ -147,7 +147,7 @@
.commit-box {
display: flex;
flex-direction: column;
border-top: 1px solid var(--color-theme-container-outline-light);
border-top: 1px solid var(--clr-theme-container-outline-light);
background: var(--clr-theme-container-pale);
padding: var(--space-16);
gap: var(--space-8);