Merge master into Visual design
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="styles.css" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
|
@ -79,7 +79,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
let activeClass = ['active', 'bg-orange-800', 'text-white'];
|
||||
let activeClass = ['active', 'bg-zinc-700/50', 'text-white'];
|
||||
|
||||
function upMenu() {
|
||||
const menu = document.getElementById('commandMenu');
|
||||
@ -179,17 +179,23 @@
|
||||
{#if showCommand}
|
||||
<div class="relative z-10" role="dialog" aria-modal="true">
|
||||
<div
|
||||
class="fixed inset-0 bg-zinc-500 bg-opacity-25 transition-opacity"
|
||||
class="fixed inset-0 bg-zinc-900 bg-opacity-80 transition-opacity"
|
||||
in:fade={{ duration: 50 }}
|
||||
out:fade={{ duration: 50 }}
|
||||
/>
|
||||
|
||||
<div class="fixed inset-0 z-10 overflow-y-auto p-4 sm:p-6 md:p-20">
|
||||
<div class="command-palette-modal fixed inset-0 z-10 overflow-y-auto p-4 sm:p-6 md:p-20">
|
||||
<div
|
||||
bind:this={palette}
|
||||
in:fade={{ duration: 100 }}
|
||||
out:fade={{ duration: 100 }}
|
||||
class="mx-auto max-w-2xl transform divide-y divide-zinc-500 divide-opacity-20 overflow-hidden rounded-xl bg-zinc-900 shadow-2xl transition-all"
|
||||
class="mx-auto max-w-2xl transform divide-y divide-zinc-500 divide-opacity-20 overflow-hidden rounded-xl bg-zinc-900 shadow-2xl transition-all border border-zinc-700"
|
||||
style="
|
||||
border-width: 0.5px;
|
||||
-webkit-backdrop-filter: blur(20px) saturate(190%) contrast(70%) brightness(80%);
|
||||
backdrop-filter: blur(20px) saturate(190%) contrast(70%) brightness(80%);
|
||||
background-color: rgba(24, 24, 27, 0.60);
|
||||
border: 0.5px solid rgba(63, 63, 70, 0.50);"
|
||||
>
|
||||
<div class="relative">
|
||||
<svg
|
||||
@ -210,7 +216,7 @@
|
||||
id="command"
|
||||
type="text"
|
||||
bind:value={search}
|
||||
class="h-12 w-full border-0 bg-transparent pl-11 pr-4 text-white focus:ring-0 sm:text-sm"
|
||||
class="h-12 w-full border-0 bg-transparent pl-12 pr-4 text-white focus:ring-0 sm:text-sm"
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
@ -230,9 +236,9 @@
|
||||
<svelte:component this={item.icon} />
|
||||
<span class="ml-3 flex-auto truncate">{item.text}</span>
|
||||
{#if item.key}
|
||||
<span class="ml-3 flex-none text-xs font-semibold text-zinc-400"
|
||||
><kbd class="font-sans">⌘</kbd><kbd class="font-sans">{item.key}</kbd></span
|
||||
>
|
||||
<span class="ml-3 flex-none text-xs font-semibold text-zinc-400 px-1 py-1 bg-zinc-800 shadow rounded">
|
||||
<kbd class="font-sans">⌘</kbd><kbd class="font-sans">{item.key}</kbd>
|
||||
</span>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
|
@ -4,7 +4,7 @@
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
|
Before Width: | Height: | Size: 341 B After Width: | Height: | Size: 341 B |
@ -4,7 +4,7 @@
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
|
Before Width: | Height: | Size: 565 B After Width: | Height: | Size: 565 B |
@ -4,7 +4,7 @@
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
|
Before Width: | Height: | Size: 674 B After Width: | Height: | Size: 674 B |
@ -4,7 +4,7 @@
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-6 h-6"
|
||||
class="h-6 w-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
|
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 490 B |
@ -12,7 +12,7 @@
|
||||
<span class="flex-grow" />
|
||||
<span>{formatDistanceToNow(processedResult.timestamp)} ago</span>
|
||||
</p>
|
||||
<div class="rounded-lg border border-[#52525B] bg-zinc-700 text-[#EBDBB2]">
|
||||
<div class="rounded-lg text-[#EBDBB2] bg-[#2F2F33] border border-zinc-700 drop-shadow-lg">
|
||||
{#each processedResult.hunks as hunk, i}
|
||||
{#if i > 0}
|
||||
<div class="border-b border-[#52525B]" />
|
||||
@ -20,7 +20,7 @@
|
||||
<div class="flex flex-col px-6 py-3">
|
||||
{#each hunk.lines as line}
|
||||
{#if !line.hidden}
|
||||
<div class="font-mono mb-px flex leading-4">
|
||||
<div class="mb-px flex font-mono leading-4">
|
||||
<span class="w-6 flex-shrink text-[#928374]"
|
||||
>{line.lineNumber ? line.lineNumber : ''}</span
|
||||
>
|
||||
|
@ -169,15 +169,18 @@
|
||||
<div class="h-full max-h-screen overflow-auto">
|
||||
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{#each $projects as project}
|
||||
<a class="text-lg text-zinc-300 hover:text-zinc-200 " href="/projects/{project.id}/">
|
||||
<a
|
||||
class="text-lg text-zinc-300 hover:text-zinc-200 "
|
||||
href="/projects/{project.id}/"
|
||||
>
|
||||
<div
|
||||
class="flex flex-col justify-between space-y-1 rounded-lg bg-zinc-700 shadow border-zinc-700 border border-t-zinc-600 border-t-[1]"
|
||||
class="flex flex-col justify-between space-y-1 rounded-lg border border-zinc-700 border-t-zinc-600 border-t-[1] bg-zinc-700 shadow"
|
||||
>
|
||||
<div class="flex-grow-0 px-4 py-4">
|
||||
<div class="text-lg text-zinc-300 hover:text-zinc-200">
|
||||
{project.title}
|
||||
</div>
|
||||
<div class="text-base break-words text-zinc-500">
|
||||
<div class="break-words text-base text-zinc-500">
|
||||
{project.path}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -118,7 +118,7 @@
|
||||
|
||||
<footer class="w-full text-sm font-medium">
|
||||
<div
|
||||
class="flex h-6 flex-shrink-0 select-none items-center border-t border-zinc-700 bg-zinc-800 "
|
||||
class="flex h-8 flex-shrink-0 select-none items-center border-t border-zinc-700 bg-zinc-800 "
|
||||
>
|
||||
<div class="mx-4 flex w-full flex-row items-center justify-between space-x-2">
|
||||
{#if $project?.api?.sync}
|
||||
@ -128,8 +128,12 @@
|
||||
<div>Syncing</div>
|
||||
</div>
|
||||
</a>
|
||||
<a target="_blank" rel="noreferrer" href={projectUrl($project)}>Open in GitButler Cloud</a
|
||||
>
|
||||
<a target="_blank" rel="noreferrer" href={projectUrl($project)} class="flex">
|
||||
<div class="leading-5">Open in GitButler Cloud</div>
|
||||
<div class="icon h-5 w-5 ml-1">
|
||||
<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill="#52525B" d="M14 13v1a1 1 0 01-1 1H6c-.575 0-1-.484-1-1V7a1 1 0 011-1h1c1.037 0 1.04 1.5 0 1.5-.178.005-.353 0-.5 0v6h6V13c0-1 1.5-1 1.5 0zm-3.75-7.25A.75.75 0 0111 5h4v4a.75.75 0 01-1.5 0V7.56l-3.22 3.22a.75.75 0 11-1.06-1.06l3.22-3.22H11a.75.75 0 01-.75-.75z"/></svg>
|
||||
</div>
|
||||
</a>
|
||||
{:else}
|
||||
<a href="/projects/{$project?.id}/settings" class="text-zinc-400 hover:text-zinc-300">
|
||||
<div class="flex flex-row items-center space-x-2 ">
|
||||
|
@ -1,12 +1,16 @@
|
||||
<script lang="ts">
|
||||
import type { LayoutData } from './$types';
|
||||
import type { Readable } from 'svelte/store';
|
||||
import type { UISession } from '$lib/uisessions';
|
||||
import type { Activity } from '$lib/sessions';
|
||||
import type { Delta } from '$lib/deltas';
|
||||
|
||||
export let data: LayoutData;
|
||||
$: project = data.project;
|
||||
$: dateSessions = data.dateSessions;
|
||||
$: dateSessions = data.dateSessions as Readable<Record<number, UISession[]>>;
|
||||
$: filesStatus = data.filesStatus;
|
||||
|
||||
function shortPath(path, max = 3) {
|
||||
function shortPath(path: string, max = 3) {
|
||||
if (path.length < 30) {
|
||||
return path;
|
||||
}
|
||||
@ -20,12 +24,12 @@
|
||||
}
|
||||
|
||||
// convert a list of timestamps to a sparkline
|
||||
function timestampsToSpark(tsArray) {
|
||||
function timestampsToSpark(tsArray: number[]) {
|
||||
let range = tsArray[0] - tsArray[tsArray.length - 1];
|
||||
|
||||
let totalBuckets = 18;
|
||||
let bucketSize = range / totalBuckets;
|
||||
let buckets = [];
|
||||
let buckets: number[][] = [];
|
||||
for (let i = 0; i <= totalBuckets; i++) {
|
||||
buckets.push([]);
|
||||
}
|
||||
@ -61,13 +65,13 @@
|
||||
}
|
||||
|
||||
// reduce a group of sessions to a map of filename to timestamps array
|
||||
function sessionFileMap(sessions: any[]) {
|
||||
let sessionsByFile = {};
|
||||
function sessionFileMap(sessions: UISession[]): Record<string, number[]> {
|
||||
let sessionsByFile: Record<string, number[]> = {};
|
||||
sessions.forEach((session) => {
|
||||
if (session.deltas) {
|
||||
Object.entries(session.deltas).forEach((deltas) => {
|
||||
let filename = deltas[0];
|
||||
let timestamps = deltas[1].map((delta: any) => {
|
||||
let timestamps = deltas[1].map((delta: Delta) => {
|
||||
return delta.timestampMs;
|
||||
});
|
||||
if (sessionsByFile[filename]) {
|
||||
@ -83,7 +87,7 @@
|
||||
}
|
||||
|
||||
// order the sessions and summarize the changes by file
|
||||
function orderedSessions(dateSessions: Record<string, any>) {
|
||||
function orderedSessions(dateSessions: Record<number, UISession[]>) {
|
||||
return Object.entries(dateSessions)
|
||||
.sort((a, b) => {
|
||||
return parseInt(b[0]) - parseInt(a[0]);
|
||||
@ -94,11 +98,11 @@
|
||||
.slice(0, 3);
|
||||
}
|
||||
|
||||
function recentActivity(dateSessions: Record<string, any>) {
|
||||
let recentActivity = [];
|
||||
function recentActivity(dateSessions: Record<string, UISession[]>) {
|
||||
let recentActivity: Activity[] = [];
|
||||
if (dateSessions) {
|
||||
Object.entries(dateSessions).forEach(([date, sessions]) => {
|
||||
sessions.forEach((session) => {
|
||||
sessions.forEach((session: UISession) => {
|
||||
if (session.session) {
|
||||
session.session.activity.forEach((activity) => {
|
||||
recentActivity.push(activity);
|
||||
@ -114,32 +118,36 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<div class="project-section-component" style="height: calc(100vh - 110px); overflow: hidden;">
|
||||
<div class="flex">
|
||||
<div class="main-column-containercol-span-2 mt-4" style="width: calc(100% * 0.66); height: calc(-126px + 100vh)">
|
||||
<h1 class="flex text-xl text-zinc-200 px-8">
|
||||
<div class="project-section-component" style="height: calc(100vh - 118px); overflow: hidden;">
|
||||
<div class="flex h-full">
|
||||
<div
|
||||
class="main-column-containercol-span-2 mt-4"
|
||||
style="width: calc(100% * 0.66); height: calc(-126px + 100vh)"
|
||||
>
|
||||
<h1 class="flex py-4 px-8 text-xl text-zinc-300">
|
||||
{$project?.title} <span class="ml-2 text-zinc-600">Project</span>
|
||||
</h1>
|
||||
<div class="mt-4">
|
||||
<div class="recent-file-changes-container w-full" >
|
||||
<h2 class="mb-4 text-lg font-bold text-zinc-300 px-8">Recent File Changes</h2>
|
||||
<div class="recent-file-changes-container w-full h-full">
|
||||
<h2 class="mb-4 px-8 text-lg font-bold text-zinc-300">Recent File Changes</h2>
|
||||
{#if $dateSessions === undefined}
|
||||
<span>Loading...</span>
|
||||
{:else}
|
||||
<div class="flex flex-col space-y-4 px-8 overflow-y-auto pb-16" style="height: calc(100vh - 174px);">
|
||||
<div
|
||||
class="flex flex-col space-y-4 overflow-y-auto px-8 pb-8"
|
||||
style="height: calc(100vh - 253px);"
|
||||
>
|
||||
{#each orderedSessions($dateSessions) as [dateMilliseconds, fileSessions]}
|
||||
<div class="flex flex-col">
|
||||
<div class="mb-1 text-lg text-zinc-400 text-zinc-200">
|
||||
<div class="mb-1 text-zinc-300">
|
||||
{new Date(parseInt(dateMilliseconds)).toLocaleDateString('en-us', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})}
|
||||
|
||||
</div>
|
||||
<div class="rounded bg-zinc-700 p-4">
|
||||
<div class="results-card rounded bg-[#2F2F33] border border-zinc-700 p-4 drop-shadow-lg">
|
||||
{#each Object.entries(fileSessions) as filetime}
|
||||
<div class="flex flex-row justify-between">
|
||||
<div class="font-mono text-zinc-100">{filetime[0]}</div>
|
||||
@ -154,18 +162,23 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="secondary-column-container col-span-1 space-y-6 pt-4 px-4 border-l border-l-zinc-700" style="width: 37%; height: calc(100vh - 110px); overflow-y: auto;">
|
||||
<div>
|
||||
<div
|
||||
class="secondary-column-container col-span-1 flex flex-col border-l border-l-zinc-700"
|
||||
style="width: 37%;"
|
||||
>
|
||||
<div class="work-in-progress-container border-b border-zinc-700 py-4 px-4">
|
||||
<h2 class="mb-2 text-lg font-bold text-zinc-300">Work in Progress</h2>
|
||||
{#if $filesStatus.length == 0}
|
||||
<div class="rounded bg-green-900 p-4 text-green-400 border border-green-700">
|
||||
<div class="flex align-middle rounded border border-green-700 bg-green-900 p-4 text-green-400">
|
||||
<div class="icon h-5 w-5 mr-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="#4ADE80" fill-rule="evenodd" d="M2 10a8 8 0 1 0 16 0 8 8 0 0 0-16 0Zm12.16-1.44a.8.8 0 0 0-1.12-1.12L9.2 11.28 7.36 9.44a.8.8 0 0 0-1.12 1.12l2.4 2.4c.32.32.8.32 1.12 0l4.4-4.4Z" /></svg>
|
||||
</div>
|
||||
Everything is committed
|
||||
</div>
|
||||
{:else}
|
||||
<div class="rounded bg-yellow-500 p-4 text-yellow-900 border border-yellow-600 font-mono">
|
||||
<div class="rounded border border-yellow-600 bg-yellow-500 p-4 font-mono text-yellow-900">
|
||||
<ul class="pl-4">
|
||||
{#each $filesStatus as activity}
|
||||
<li class="list-disc ">
|
||||
@ -177,24 +190,26 @@
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="">
|
||||
|
||||
<div
|
||||
class="recent-activity-container p-4"
|
||||
style="height: calc(100vh - 110px); overflow-y: auto;"
|
||||
>
|
||||
<h2 class="text-lg font-bold text-zinc-300">Recent Activity</h2>
|
||||
{#each recentActivity($dateSessions) as activity}
|
||||
<div class="recent-activity-card mt-4 mb-1 text-zinc-400 border border-zinc-700 rounded">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-row justify-between rounded-t bg-[#2F2F33] p-2">
|
||||
<div class="text-zinc-300">
|
||||
{new Date(parseInt(activity.timestampMs)).toLocaleDateString('en-us', {
|
||||
weekday: 'long',
|
||||
<div class="recent-activity-card mt-4 mb-1 rounded border border-zinc-700 text-zinc-400 drop-shadow-lg">
|
||||
<div class="flex flex-col p-3 rounded bg-[#2F2F33]">
|
||||
<div class="flex flex-row justify-between text-zinc-500 pb-2">
|
||||
<div class="">
|
||||
{new Date(activity.timestampMs).toLocaleDateString('en-us', {
|
||||
weekday: 'short',
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</div>
|
||||
<div class="font-mono text-right">{activity.type}</div>
|
||||
<div class="text-right font-mono ">{activity.type}</div>
|
||||
</div>
|
||||
<div class="rounded-b bg-[#2F2F33] p-2">{activity.message}</div>
|
||||
<div class="rounded-b bg-[#2F2F33] text-zinc-100">{activity.message}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
@ -179,7 +179,6 @@
|
||||
id="description"
|
||||
name="description"
|
||||
rows="3"
|
||||
|
||||
class="w-full rounded-lg border border-zinc-600 bg-zinc-900 p-2 text-zinc-300"
|
||||
value={$project?.api?.description}
|
||||
/>
|
||||
|
@ -154,7 +154,7 @@
|
||||
<!-- Day -->
|
||||
<div
|
||||
id={dateMilliseconds}
|
||||
class="session-day-component flex flex-col rounded-lg border border-zinc-700 bg-zinc-800/50"
|
||||
class="session-day-component flex flex-col rounded-lg bg-[#2F2F33] border border-zinc-700"
|
||||
class:min-w-full={selection.dateMilliseconds == +dateMilliseconds}
|
||||
>
|
||||
<div
|
||||
@ -308,7 +308,7 @@
|
||||
<div class="col-span-1 flex items-center justify-center" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="timeline-file-list flex mb-1 border-b-zinc-700 border-b-2">
|
||||
<div class="timeline-file-list mb-1 flex border-b-2 border-b-zinc-700">
|
||||
<div class="grid flex-auto grid-cols-1 grid-rows-1">
|
||||
<!-- file names list -->
|
||||
<div
|
||||
|
@ -78,8 +78,7 @@
|
||||
name="name"
|
||||
bind:value={userName}
|
||||
type="text"
|
||||
class="px-4 py-2 text-zinc-300 bg-zinc-900 border border-zinc-600 rounded-lg w-full"
|
||||
|
||||
class="w-full rounded-lg border border-zinc-600 bg-zinc-900 px-4 py-2 text-zinc-300"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -92,8 +91,7 @@
|
||||
name="email"
|
||||
bind:value={$user.email}
|
||||
type="text"
|
||||
class="px-4 py-2 text-zinc-300 bg-zinc-900 border border-zinc-600 rounded-lg w-full"
|
||||
|
||||
class="w-full rounded-lg border border-zinc-600 bg-zinc-900 px-4 py-2 text-zinc-300"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -209,8 +207,8 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col mt-8 border-t border-zinc-400 pt-4">
|
||||
<h2 class="text-lg text-zinc-100 font-medium">Get Support</h2>
|
||||
<div class="mt-8 flex flex-col border-t border-zinc-400 pt-4">
|
||||
<h2 class="text-lg font-medium text-zinc-100">Get Support</h2>
|
||||
<div class="text-sm text-zinc-300">
|
||||
If you have an issue or any questions, please email us.
|
||||
</div>
|
||||
|