Merge branch 'master' into ian/prettify-scroll-bar

This commit is contained in:
Ian Donahue 2023-03-28 10:57:34 +02:00
commit dbc04ed0fe
17 changed files with 289 additions and 209 deletions

View File

@ -9,6 +9,7 @@
-webkit-perspective: 1000;
}
/* SCROLL BAR STYLING */
/* width */
@ -42,11 +43,10 @@
}
/* COMMAND PALETTE */
.result-section-header {
@apply mb-2 mt-2 mx-2 cursor-default select-none py-2 text-sm font-semibold text-zinc-300;
@apply mx-2 mb-2 mt-2 cursor-default select-none py-2 text-sm font-semibold text-zinc-300;
}
.quick-command-item {
@ -56,7 +56,3 @@
.quick-command-key {
@apply rounded-sm border border-[#3A393F] bg-[#343338] px-[3px] font-mono text-[11px] shadow;
}

View File

@ -2,15 +2,17 @@
import { afterNavigate, goto } from '$app/navigation';
import { page } from '$app/stores';
const getUri = (url: URL) => url.pathname + url.search + url.hash;
let position = 0;
const history = [$page.url.pathname];
const history = [getUri($page.url)];
$: canGoBack = history.length > 1 && position > 0;
$: canGoForward = history.length > 1 && position < history.length - 1;
afterNavigate((nav) => {
if (nav.to === null) return;
const to = nav.to.url.pathname;
const to = getUri(nav.to.url);
if (to === history[position]) {
return;
} else if (to === history[position + 1]) {

View File

@ -13,6 +13,8 @@
import Commit from './Commit.svelte';
import { invoke } from '@tauri-apps/api';
import { createEventDispatcher } from 'svelte';
import { RewindIcon } from '$lib/components/icons';
import { GitCommitIcon } from '$lib/components/icons';
const dispatch = createEventDispatcher();
@ -84,12 +86,23 @@
visible: scopeToProject,
commands: [
{
title: 'Commit',
title: 'Quick commit',
description: 'C',
selected: false,
action: {
component: Commit
},
icon: GitCommitIcon,
visible: 'commit'.includes(userInput?.toLowerCase())
},
{
title: 'Commit',
description: 'Shift C',
selected: false,
action: {
component: Commit
},
icon: GitCommitIcon,
visible: 'commit'.includes(userInput?.toLowerCase())
},
{
@ -99,6 +112,7 @@
action: {
component: Replay
},
icon: RewindIcon,
visible: 'replay history'.includes(userInput?.toLowerCase())
}
]
@ -170,9 +184,12 @@
<Modal on:close>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="flex h-[640px] w-[640px] flex-col rounded text-zinc-400" on:click|stopPropagation>
<div
class="commnand-palette flex max-h-[360px] w-[640px] flex-col rounded text-zinc-400"
on:click|stopPropagation
>
<!-- Search input area -->
<div class="flex items-center border-b border-zinc-400/20 py-2">
<div class="search-input flex items-center border-b border-zinc-400/20 py-2">
<div class="ml-4 mr-2 flex flex-grow items-center">
<!-- Project scope -->
{#if scopeToProject}
@ -198,17 +215,17 @@
</div>
</div>
<!-- Main part -->
<div class="flex-auto overflow-y-auto">
<div class="search-results flex-auto overflow-y-auto">
{#each commandGroups as group, groupIdx}
{#if group.visible}
<div class="mx-2 cursor-default select-none">
<p class="mx-2 cursor-default select-none py-2 text-sm font-semibold text-zinc-300/80">
<p class="result-section-header">
<span>{group.name}</span>
{#if group.description}
<span class="ml-2 font-light italic text-zinc-300/60">({group.description})</span>
<span class="ml-2 font-light italic text-zinc-300/70">({group.description})</span>
{/if}
</p>
<ul class="">
<ul class="quick-command-list text-zinc-300">
{#each group.commands as command, commandIdx}
{#if command.visible}
{#if Action.isLink(command.action)}
@ -217,12 +234,13 @@
on:focus={() => (selection = [groupIdx, commandIdx])}
id={`${groupIdx}-${commandIdx}`}
href={command.action.href}
class="{selection[0] === groupIdx && selection[1] === commandIdx
class="quick-command-item {selection[0] === groupIdx &&
selection[1] === commandIdx
? 'bg-zinc-700/70'
: ''} flex cursor-default items-center rounded-lg p-2 px-2 outline-none"
>
<span class="flex-grow">{command.title}</span>
<span>{command.description}</span>
<span class="quick-command flex-grow">{command.title}</span>
<span class="quick-command-key">{command.description}</span>
</a>
{:else if Action.isActionInPalette(command.action)}
<div
@ -230,11 +248,16 @@
on:focus={() => (selection = [groupIdx, commandIdx])}
on:click={triggerCommand}
class="{selection[0] === groupIdx && selection[1] === commandIdx
? 'bg-zinc-700/70'
: ''} flex cursor-default items-center rounded-lg p-2 px-2 outline-none"
? 'bg-zinc-50/10'
: ''} quick-command-item flex cursor-default items-center "
>
<span class="flex-grow">{command.title}</span>
<span>{command.description}</span>
<span class="quick-command-icon">
<svelte:component this={command.icon} />
</span>
<span class="quick-command flex-grow">{command.title}</span>
{#each command.description.split(' ') as token}
<span class="quick-command-key">{token}</span>
{/each}
</div>
{/if}
{/if}

View File

@ -8,6 +8,7 @@
import Replay from './Replay.svelte';
import Branch from './Branch.svelte';
import { currentProject } from '$lib/current_project';
import { goto } from '$app/navigation';
let dialog: ComponentType | undefined;
@ -54,6 +55,11 @@
dialog === Commit ? (dialog = undefined) : (dialog = Commit);
}
},
'Shift+c': () => {
if ($currentProject) {
goto(`/projects/${$currentProject?.id}/commit`);
}
},
r: () => {
if ($currentProject) {
dialog === Replay ? (dialog = undefined) : (dialog = Replay);

View File

@ -1,6 +1,6 @@
<script lang="ts">
import Modal from '../Modal.svelte';
import { shortPath } from '$lib/paths';
import { collapsable } from '$lib/paths';
import { invoke } from '@tauri-apps/api';
import { currentProject } from '$lib/current_project';
import { onMount } from 'svelte';
@ -100,9 +100,7 @@
<div>
{file[1]}
</div>
<div class="font-mono">
{shortPath(file[0])}
</div>
<span class="font-mono" use:collapsable={{ value: file[0], separator: '/' }} />
</div>
{/each}
</div>

View File

@ -80,23 +80,23 @@
<Modal on:close>
<div class="mx-2 cursor-default select-none">
<p class="mx-2 cursor-default select-none py-2 text-sm font-semibold text-zinc-300/80">
<p class="mx-2 cursor-default select-none py-2 text-sm font-semibold text-zinc-300">
Replay working history from...
</p>
<ul class="">
<ul class="quick-command-list">
{#each listOptions as listItem, idx}
<a
on:mouseover={() => (selectionIdx = idx)}
on:focus={() => (selectionIdx = idx)}
on:click={gotoDestination}
class="{selectionIdx === idx
? 'bg-zinc-700/70'
: ''} flex cursor-default items-center rounded-lg p-2 px-2 outline-none"
? 'bg-zinc-50/10'
: ''} quick-command-item flex cursor-default items-center"
href="/"
>
<span class="flex-grow">{listItem.label}</span>
<span>{idx + 1}</span>
<span class="quick-command flex-grow">{listItem.label}</span>
<span class="quick-command-key">{idx + 1}</span>
</a>
{/each}
</ul>

View File

@ -16,6 +16,7 @@ export type Command = {
action: Action;
selected: boolean;
visible: boolean;
icon: ComponentType;
};
export type CommandGroup = {
name: string;

View File

@ -10,9 +10,11 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<dialog
class="rounded-lg
border border-zinc-400/40
bg-zinc-900/70 p-0 shadow-lg
class="w-[640px]
overflow-hidden rounded-lg
border-[0.5px] border-[#3F3F3F] bg-zinc-900/70
p-0
shadow-lg
backdrop-blur-xl
"
in:scale={{ duration: 150 }}

View File

@ -0,0 +1,6 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M9.88822 7.00178C9.16279 7.02506 8.46269 7.2735 7.88503 7.71283C7.30733 8.15203 6.88067 8.76019 6.66431 9.45298H3.03247C2.66239 9.45651 2.32189 9.65594 2.13796 9.97703C1.95401 10.2981 1.95401 10.6927 2.13796 11.0138C2.32191 11.3349 2.66239 11.5343 3.03247 11.5379H6.66431C6.96248 12.4919 7.655 13.2725 8.56658 13.6825C9.47816 14.0925 10.5217 14.0925 11.4333 13.6825C12.3449 13.2725 13.0374 12.4919 13.3356 11.5379H16.9674C17.3375 11.5343 17.678 11.3349 17.8619 11.0138C18.046 10.6927 18.046 10.2981 17.8619 9.97703C17.678 9.65594 17.3375 9.4565 16.9674 9.45298H13.3356C13.1079 8.72377 12.6475 8.08927 12.0249 7.64651C11.4022 7.20388 10.6517 6.97741 9.88822 7.00178ZM9.96575 9.08105C10.3461 9.07179 10.714 9.21698 10.9855 9.48347C11.2571 9.74994 11.4092 10.115 11.4071 10.4954C11.4136 10.8728 11.2682 11.2369 11.0036 11.5061C10.739 11.7752 10.3774 11.9267 10 11.9267C9.62248 11.9267 9.26091 11.7752 8.99641 11.5061C8.73176 11.2369 8.58632 10.8728 8.59278 10.4954C8.59083 10.1269 8.73357 9.77225 8.99029 9.50774C9.24701 9.24334 9.59723 9.0901 9.96575 9.08109V9.08105Z"
fill="#71717A"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,14 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.35744 9.90001L8.9 13.6467L8.9 6.15336L4.35744 9.90001ZM2.93534 9.12855C2.45039 9.52854 2.45039 10.2715 2.93534 10.6715L8.76372 15.4786C9.41596 16.0166 10.4 15.5526 10.4 14.7072L10.4 5.09284C10.4 4.24738 9.41595 3.78343 8.76371 4.32139L2.93534 9.12855Z"
fill="#71717A"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12.1633 9.89999L15.7 13.3032L15.7 6.49683L12.1633 9.89999ZM10.7488 9.17942C10.34 9.57282 10.34 10.2272 10.7488 10.6206L15.5066 15.1987C16.1419 15.8101 17.2 15.3598 17.2 14.4782L17.2 5.32182C17.2 4.44016 16.1419 3.98992 15.5066 4.60124L10.7488 9.17942Z"
fill="#71717A"
/>
</svg>

After

Width:  |  Height:  |  Size: 767 B

View File

@ -5,7 +5,9 @@ export { default as ContactIcon } from './ContactIcon.svelte';
export { default as FileIcon } from './FileIcon.svelte';
export { default as FolderIcon } from './FolderIcon.svelte';
export { default as LabelIcon } from './LabelIcon.svelte';
export { default as GitCommitIcon } from './GitCommitIcon.svelte';
export { default as ProjectIcon } from './ProjectIcon.svelte';
export { default as RewindIcon } from './RewindIcon.svelte';
export { default as IconRotateClockwise } from './IconRotateClockwise.svelte';
export { default as IconPlayerPauseFilled } from './IconPlayerPauseFilled.svelte';
export { default as IconPlayerPlayFilled } from './IconPlayerPlayFilled.svelte';

View File

@ -1,12 +1,30 @@
export function shortPath(path: string, max = 3, maxLen = 30) {
if (path.length < maxLen) {
return path;
type Params = { separator: string; value: string };
export const collapsable = (e: HTMLElement, params: Params) => {
if (e.textContent === null) return;
e.dataset['value'] = e.textContent;
const collapse = (e: HTMLElement, { separator, value }: Params) => {
e.textContent = value;
while (e.offsetWidth < e.scrollWidth) {
const parts: string[] = e.textContent.split(separator);
const firstLongPartIndex = parts.findIndex((p) => p.length > 1);
if (firstLongPartIndex === -1) return;
e.textContent = [
...parts.slice(0, firstLongPartIndex),
parts[firstLongPartIndex][0],
...parts.slice(firstLongPartIndex + 1)
].join(separator);
}
const pathParts = path.split('/');
const file = pathParts.pop();
if (pathParts.length > 0) {
const pp = pathParts.map((p) => p.slice(0, max)).join('/');
return `${pp}/${file}`;
}
return file;
}
};
collapse(e, params);
const onResize = () => collapse(e, params);
window.addEventListener('resize', onResize);
return {
update: (params: Params) => collapse(e, params),
destroy: () => window.removeEventListener('resize', onResize)
};
};

View File

@ -3,7 +3,7 @@
import type { Session } from '$lib/sessions';
import { format, startOfDay } from 'date-fns';
import type { Delta } from '$lib/deltas';
import { shortPath } from '$lib/paths';
import { collapsable } from '$lib/paths';
import { invoke } from '@tauri-apps/api';
import { toHumanBranchName } from '$lib/branch';
import { list as listDeltas } from '$lib/deltas';
@ -198,7 +198,7 @@
<div class="flex flex-row justify-between">
<div class="w-96 truncate font-mono text-zinc-300">
<a class="cursor-pointer" href={playerURL(dateMilliseconds, filetime[0])}>
{shortPath(filetime[0], 3, 70)}
<span use:collapsable={{ value: filetime[0], separator: '/' }} />
</a>
</div>
<div class="font-mono text-zinc-400">
@ -287,7 +287,7 @@
{#each $filesStatus as activity}
<li class="list-disc">
{activity.status.slice(0, 1)}
{shortPath(activity.path)}
<span use:collapsable={{ value: activity.path, separator: '/' }} />
</li>
{/each}
</ul>

View File

@ -2,7 +2,7 @@
import { invoke } from '@tauri-apps/api';
import type { PageData } from './$types';
import Api from '$lib/api';
import { shortPath } from '$lib/paths';
import { collapsable } from '$lib/paths';
import toast from 'svelte-french-toast';
import { slide } from 'svelte/transition';
import { toHumanBranchName } from '$lib/branch';
@ -215,9 +215,8 @@
<div
class="cursor-pointer {currentPath == activity.path ? 'text-white' : ''}"
on:click={selectPath(activity.path)}
>
{shortPath(activity.path)}
</div>
use:collapsable={{ value: activity.path, separator: '/' }}
/>
</div>
</li>
{/each}

View File

@ -17,6 +17,8 @@
const currentDate = derived(page, (page) => page.params.date);
const today = format(new Date(), 'yyyy-MM-dd');
const fileFilter = derived(page, (page) => page.url.searchParams.get('file'));
</script>
@ -35,20 +37,28 @@
</a>
{/if}
<div class="flex h-full w-full flex-auto flex-row gap-2 overflow-auto p-2">
<ul id="days" class="flex h-full flex-shrink-0 flex-col gap-2 overflow-y-scroll">
<div class="flex h-full w-full flex-row gap-2 px-2">
<ul
id="days"
class="scrollbar-hidden grid h-full flex-shrink-0 auto-rows-min gap-2 overflow-y-scroll py-2"
>
{#each $dates as date}
<li class="w-full">
{@const isToday = format(new Date(date), 'yyyy-MM-dd') === today}
<li>
<a
href="/projects/{projectId}/player/{date}{$page.url.search}"
class:bg-gb-800={date === $currentDate}
class:text-white={date === $currentDate}
class:border-gb-700={date !== $currentDate}
class:bg-gb-900={date !== $currentDate}
class="flex w-full flex-col items-center rounded border border-[0.5px] p-2 text-zinc-300 shadow transition duration-150 ease-out hover:bg-gb-800 hover:ease-in"
class="max-h-content flex w-full flex-col items-center justify-around rounded border border-[0.5px] p-2 text-zinc-300 shadow transition duration-150 ease-out hover:bg-gb-800 hover:ease-in"
>
{#if isToday}
<div class="py-2 text-lg leading-5">Today</div>
{:else}
<div class="text-xl leading-5">{new Date(date).getDate()}</div>
<div class="leading-4">{format(new Date(date), 'MMM')}</div>
{/if}
</a>
</li>
{/each}

View File

@ -23,7 +23,7 @@
import type { PageData } from './$types';
import { derived, writable } from 'svelte/store';
import { IconPlayerPauseFilled, IconPlayerPlayFilled } from '$lib/components/icons';
import { shortPath } from '$lib/paths';
import { collapsable } from '$lib/paths';
import { page } from '$app/stores';
import { CodeViewer } from '$lib/components';
import { asyncDerived } from '@square/svelte-store';
@ -202,7 +202,7 @@
<article
id="activities"
class="flex h-full h-full w-80 flex-shrink-0 flex-col rounded border-[0.5px] border-gb-700 bg-gb-900 xl:w-96"
class="my-2 flex w-80 flex-shrink-0 flex-grow-0 flex-col rounded border-[0.5px] border-gb-700 bg-gb-900 xl:w-96"
>
{#await richSessions.load()}
<div class="flex h-full flex-col items-center justify-center">
@ -249,15 +249,14 @@
</span>
{#if isCurrent}
<ul class="rounded-b bg-zinc-800 p-2">
<ul class="list-disk bg-zinc-800 p-2" style:list-style="disc">
{#each Object.keys(session.files) as filename}
<li
class:text-zinc-100={$frame?.filepath === filename}
class:font-bold={$frame?.filepath === filename}
class="truncate text-left text-zinc-500"
>
{shortPath(filename)}
</li>
class="ml-5 text-zinc-500"
use:collapsable={{ value: filename, separator: '/' }}
/>
{/each}
</ul>
{/if}
@ -270,9 +269,11 @@
{/await}
</article>
<div id="player" class="flex-auto overflow-auto rounded border border-zinc-700 bg-gb-900 ">
<div
id="player"
class="relative my-2 flex flex-auto overflow-auto rounded border border-zinc-700 bg-gb-900"
>
{#if $frame}
<div class="relative flex h-full w-full flex-col gap-2 ">
<div id="code" class="h-full w-full flex-auto overflow-auto px-2 pb-[120px]">
<CodeViewer
doc={$frame.doc}
@ -282,13 +283,17 @@
/>
</div>
<div id="info" class="absolute bottom-[86px] left-4 rounded-lg bg-zinc-800 p-2">
<div class="flex flex-row justify-between space-x-2">
<div class="font-mono font-bold text-white">{shortPath($frame.filepath)}</div>
<div>
<div
id="info"
class="w-content absolute bottom-[86px] ml-4 flex max-w-full gap-2 rounded-lg bg-zinc-800 p-2"
>
<span
class="flex-auto overflow-auto font-mono font-bold text-white"
use:collapsable={{ value: $frame.filepath, separator: '/' }}
/>
<span class="whitespace-nowrap">
{new Date($frame.deltas[$frame.deltas.length - 1].timestampMs).toLocaleString('en-US')}
</div>
</div>
</span>
</div>
<div
@ -426,7 +431,6 @@
</div>
</div>
</div>
</div>
{:else}
<div class="mt-8 text-center">Select a playlist</div>
{/if}

View File

@ -26,7 +26,6 @@ const config = {
}
}
},
plugins: []
};