Improve general UX

- Hide issue title edit button when not logged in
- Show Blob placeholder when empty file
- Allow selecting blob code without the lines
- Add Clipboard component to object id in CobHeader
- Add blockies to Patch and Issueteasers
- Reduce formatted object ids to 7 instead of 11 chars
- Remove line breaking on soft wraps from Markdown
- Reduce font size of code blocks in Markdown
- Improve margins in IssueTeaser
- Reduce font size in FileDiffs
- Improve teasers, assignees and tags highlighting
- Reduce font size of code
- Change commit teaser subtitle
- Show Clipboard in commit sha1
- Add blockies to PeerSelector
- Adjust commit view CSS

Signed-off-by: Sebastian Martinez <me@sebastinez.dev>
This commit is contained in:
Sebastian Martinez
2023-04-14 16:15:51 +02:00
parent 94632d7ea0
commit e5ec6e33fb
19 changed files with 124 additions and 114 deletions
+9 -1
View File
@@ -9,7 +9,8 @@
}
.layout-mobile,
.layout-mobile-inline {
.layout-mobile-inline,
.layout-mobile-flex {
display: none !important;
}
.layout-desktop {
@@ -18,6 +19,9 @@
.layout-desktop-inline {
display: inline !important;
}
.layout-desktop-flex {
display: flex !important;
}
@media (max-width: 720px) {
.layout-mobile {
@@ -26,7 +30,11 @@
.layout-mobile-inline {
display: inline !important;
}
.layout-mobile-flex {
display: flex !important;
}
.layout-desktop,
.layout-desktop-flex,
.layout-desktop-inline {
display: none !important;
}
+4 -15
View File
@@ -4,7 +4,6 @@
export let authorId: string;
export let caption: string | undefined = undefined;
export let highlight: boolean = false;
export let noAvatar: boolean = false;
export let timestamp: number | undefined = undefined;
@@ -16,7 +15,7 @@
.authorship {
display: inline-flex;
align-items: center;
color: var(--color-foreground-5);
color: var(--color-foreground-6);
padding: 0.125rem 0;
}
.id {
@@ -26,26 +25,18 @@
}
.body {
margin: 0 0.4rem;
color: var(--color-foreground-5);
white-space: nowrap;
}
.highlight {
color: var(--color-foreground-6);
font-weight: var(--font-weight-bold);
}
.date {
color: var(--color-foreground-6);
}
</style>
<span class="authorship txt-tiny" title={relativeTimestamp(timestamp)}>
{#if !noAvatar}
<Avatar inline nodeId={authorId} />
{/if}
<span class:highlight class="id highlight layout-desktop">
<span class="id layout-desktop">
{formatNodeId(authorId)}
</span>
<span class:highlight class="id layout-mobile">
<span class="id layout-mobile">
{formatNodeId(authorId).replace("did:key:", "")}
</span>
<span class="body">
@@ -56,8 +47,6 @@
{/if}
</span>
{#if timestamp}
<span class:date={highlight}>
{formatTimestamp(timestamp)}
</span>
{formatTimestamp(timestamp)}
{/if}
</span>
+1 -1
View File
@@ -83,7 +83,7 @@
{:else if body.trim() === ""}
<span class="txt-missing">No description.</span>
{:else}
<Markdown {rawPath} breaks content={body} />
<Markdown {rawPath} content={body} />
{/if}
</div>
</div>
+11 -12
View File
@@ -1,6 +1,5 @@
<script lang="ts" context="module">
export interface Item<T> {
key: string;
title: string;
value: T;
badge: string | null;
@@ -50,19 +49,19 @@
<div class="dropdown">
{#each items as item}
{#if item.key}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="dropdown-item"
class:selected={item.value === selected}
use:twemoji
on:click={() => onSelect(item)}
title={item.title}>
{@html item.key}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="dropdown-item"
class:selected={item.value === selected}
use:twemoji
on:click={() => onSelect(item)}
title={item.title}>
<slot name="item" {item}>
{item.value}
{#if item.badge}
<Badge variant="primary">{item.badge}</Badge>
{/if}
</div>
{/if}
</slot>
</div>
{/each}
</div>
+2 -3
View File
@@ -16,14 +16,13 @@
export let content: string;
export let hash: string | null = null;
export let path: string = "/";
export let breaks = false;
export let rawPath: string;
$: doc = matter(content);
$: frontMatter = Object.entries(doc.data).filter(
([, val]) => typeof val === "string" || typeof val === "number",
);
marked.use({ extensions, renderer, breaks });
marked.use({ extensions, renderer });
let container: HTMLElement;
@@ -187,7 +186,7 @@
.markdown :global(code) {
font-family: var(--font-family-monospace);
font-size: var(--font-size-regular);
font-size: var(--font-size-small);
color: var(--color-foreground-6);
background-color: var(--color-foreground-2);
border-radius: 0.5rem;
+1 -1
View File
@@ -304,7 +304,7 @@ export function createAddRemoveArrays(
// Formats COBs Object Ids
export function formatObjectId(id: string): string {
return id.substring(0, 11);
return id.substring(0, 7);
}
export function stripDidPrefix(array: string[]): string[] {
+7 -1
View File
@@ -127,6 +127,7 @@
text-align: right;
padding-right: 1rem;
padding-left: 1rem;
user-select: none;
}
.code :global(.line-number:hover) {
cursor: pointer;
@@ -158,7 +159,7 @@
width: 100%;
border-spacing: 0;
overflow-x: auto;
font-size: var(--font-size-regular);
font-size: var(--font-size-small);
padding-top: 1rem;
margin-bottom: 1.5rem;
}
@@ -254,6 +255,11 @@
<table class="code no-scrollbar">
{@html toHtml(content)}
</table>
{:else}
<div class="binary">
<div use:twemoji>🍂</div>
<span class="txt-tiny">Empty file</span>
</div>
{/if}
</div>
</div>
+2 -2
View File
@@ -54,7 +54,7 @@
align-items: center;
font-size: var(--font-size-small);
margin-bottom: 0.75rem;
color: var(--color-foreground-5);
color: var(--color-foreground-6);
}
.metadata-section-body {
display: flex;
@@ -64,7 +64,7 @@
margin-bottom: 1.25rem;
}
.metadata-section-empty {
color: var(--color-foreground-6);
color: var(--color-foreground-5);
}
</style>
+12 -4
View File
@@ -4,6 +4,7 @@
import { formatObjectId } from "@app/lib/utils";
import Button from "@app/components/Button.svelte";
import Clipboard from "@app/components/Clipboard.svelte";
import TextInput from "@app/components/TextInput.svelte";
export let action: "create" | "edit" | "view" = "view";
@@ -39,8 +40,12 @@
width: 90%;
}
.id {
display: flex;
flex-direction: row;
align-items: center;
font-size: var(--font-size-tiny);
color: var(--color-foreground-5);
font-family: var(--font-family-monospace);
color: var(--color-foreground-6);
}
.title {
overflow: hidden;
@@ -68,9 +73,12 @@
{/if}
<slot name="revision" />
{#if id}
<div class="txt-monospace id layout-desktop">{id}</div>
<div class="txt-monospace id layout-mobile">
{formatObjectId(id)}
<div class="id">
<div class="layout-desktop">{id}</div>
<div class="layout-mobile">
{formatObjectId(id)}
</div>
<Clipboard small text={id} />
</div>
{/if}
{/if}
+1 -1
View File
@@ -60,7 +60,7 @@
size="small"
on:click={() => dispatch("saveStatus", selectedItem.value)}
style={attachableStyle}>
{selectedItem.key}
{selectedItem.title}
</Button>
<Floating>
<svelte:fragment slot="toggle">
+2 -2
View File
@@ -51,7 +51,7 @@
align-items: center;
font-size: var(--font-size-small);
margin-bottom: 0.75rem;
color: var(--color-foreground-5);
color: var(--color-foreground-6);
}
.metadata-section-body {
display: flex;
@@ -61,7 +61,7 @@
margin-bottom: 1.25rem;
}
.metadata-section-empty {
color: var(--color-foreground-6);
color: var(--color-foreground-5);
}
.tag {
overflow: hidden;
+8 -4
View File
@@ -7,6 +7,7 @@
import Changeset from "@app/views/projects/SourceBrowser/Changeset.svelte";
import CommitAuthorship from "@app/views/projects/Commit/CommitAuthorship.svelte";
import Clipboard from "@app/components/Clipboard.svelte";
export let commit: Commit;
@@ -36,10 +37,11 @@
align-items: center;
}
.description {
margin: 0.5rem 0 1rem 0;
margin: 1rem 0;
white-space: pre-wrap;
}
.sha1 {
align-items: center;
color: var(--color-foreground-5);
font-size: var(--font-size-small);
}
@@ -54,12 +56,14 @@
<div class="commit">
<header>
<div class="summary">
<div class="txt-medium" use:twemoji>{header.summary}</div>
<div class="layout-desktop txt-monospace sha1">
<div class="txt-medium txt-bold" use:twemoji>{header.summary}</div>
<div class="layout-desktop-flex txt-monospace sha1">
<span>{header.id}</span>
<Clipboard small text={header.id} />
</div>
<div class="layout-mobile txt-monospace sha1 txt-small">
<div class="layout-mobile-flex txt-monospace sha1 txt-small">
{formatCommit(header.id)}
<Clipboard small text={header.id} />
</div>
</div>
<pre class="description txt-small">{header.description}</pre>
@@ -13,12 +13,11 @@
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--color-foreground-5);
color: var(--color-foreground-6);
padding: 0.125rem 0;
}
.authorship .author,
.authorship .committer {
color: var(--color-foreground);
white-space: nowrap;
}
.authorship .avatar {
+1 -3
View File
@@ -40,7 +40,6 @@
state: { status: "closed", reason: "other" } as const,
},
].map(item => ({
key: item.title,
title: item.title,
value: item.state,
badge: null,
@@ -198,7 +197,7 @@
<div class="issue">
<div>
<CobHeader
action="edit"
{action}
id={issue.id}
title={issue.title}
on:editTitle={editTitle}>
@@ -214,7 +213,6 @@
</Badge>
{/if}
<Authorship
highlight
timestamp={issue.discussion[0].timestamp}
authorId={issue.author.id}
caption="opened this issue" />
+5 -16
View File
@@ -35,14 +35,11 @@
cursor: pointer;
}
.subtitle {
color: var(--color-foreground-5);
color: var(--color-foreground-6);
font-size: var(--font-size-tiny);
font-family: var(--font-family-monospace);
margin-right: 0.4rem;
}
.id {
margin-right: 0.5rem;
}
.summary {
display: flex;
flex-direction: row;
@@ -82,9 +79,6 @@
justify-self: center;
align-self: center;
}
.highlight {
color: var(--color-foreground-6);
}
.state-icon {
width: 0.5rem;
height: 0.5rem;
@@ -128,15 +122,10 @@
</span>
</div>
<div class="summary subtitle">
<span class="id">
<span class="highlight">{formatObjectId(issue.id)}</span>
opened
<span class="highlight">
{formatTimestamp(issue.discussion[0].timestamp)}
</span>
by
</span>
<Authorship highlight noAvatar authorId={issue.author.id} />
{formatObjectId(issue.id)} opened {formatTimestamp(
issue.discussion[0].timestamp,
)} by
<Authorship authorId={issue.author.id} />
</div>
</div>
{#if commentCount > 0}
+9 -14
View File
@@ -193,9 +193,6 @@
margin: 1rem;
color: var(--color-foreground-5);
}
.highlight {
color: var(--color-foreground-6);
}
@media (max-width: 1092px) {
.patch {
@@ -216,7 +213,7 @@
<div class="patch">
<div>
<CobHeader id={patch.id} title={patch.title}>
<span slot="revision" class="revision txt-monospace txt-tiny">
<span slot="revision" class="txt-monospace txt-tiny">
<Floating>
<svelte:fragment slot="toggle">
<HeaderToggleLabel
@@ -230,7 +227,6 @@
<Dropdown
items={enumeratedRevisions.map(([r, i]) => {
return {
key: `Revision ${i} (${utils.formatObjectId(r.id)})`,
title: `Revision ${i} (${utils.formatObjectId(r.id)})`,
value: r.id,
badge: null,
@@ -244,7 +240,11 @@
params: { patch: patch.id, revision: item.value },
},
});
}} />
}}>
<span slot="item" let:item>
{item.title}
</span>
</Dropdown>
</svelte:fragment>
</Floating>
</span>
@@ -254,13 +254,12 @@
</Badge>
<div class="layout-desktop">
<Authorship
highlight
timestamp={patch.revisions[0].timestamp}
authorId={patch.author.id}
caption="opened this patch" />
</div>
<div class="layout-mobile">
<Authorship highlight authorId={patch.author.id} />
<Authorship authorId={patch.author.id} />
</div>
</svelte:fragment>
</CobHeader>
@@ -298,17 +297,13 @@
authorId={element.inner.node}
timestamp={element.timestamp}>
merged
<span class="highlight">
{utils.formatCommit(element.inner.commit)}
</span>
{utils.formatCommit(element.inner.commit)}
</Authorship>
</div>
<div class="action layout-mobile txt-tiny">
<Authorship authorId={element.inner.node}>
merged
<span class="highlight">
{utils.formatCommit(element.inner.commit)}
</span>
{utils.formatCommit(element.inner.commit)}
</Authorship>
</div>
{:else if element.type === "review"}
+9 -15
View File
@@ -37,7 +37,11 @@
cursor: pointer;
}
.meta {
color: var(--color-foreground-5);
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
color: var(--color-foreground-6);
font-size: var(--font-size-tiny);
font-family: var(--font-family-monospace);
margin: 0 0.5rem;
@@ -46,10 +50,6 @@
margin: 0;
}
.summary {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
padding-right: 2rem;
}
.patch-title {
@@ -89,9 +89,6 @@
height: 0.5rem;
border-radius: var(--border-radius-small);
}
.highlight {
color: var(--color-foreground-6);
}
.draft {
background-color: var(--color-foreground-4);
}
@@ -138,13 +135,10 @@
</div>
<div class="summary">
<span class="meta id">
<span class="highlight">{formatObjectId(patch.id)}</span>
opened
<span class="highlight">
{formatTimestamp(latestRevision.timestamp)}
</span>
by
<Authorship highlight noAvatar authorId={patch.author.id} />
{formatObjectId(patch.id)} opened {formatTimestamp(
latestRevision.timestamp,
)} by
<Authorship authorId={patch.author.id} />
</span>
</div>
</div>
+38 -17
View File
@@ -6,6 +6,7 @@
import { formatNodeId, truncateId } from "@app/lib/utils";
import Avatar from "@app/components/Avatar.svelte";
import Badge from "@app/components/Badge.svelte";
import Dropdown from "@app/components/Dropdown.svelte";
import Floating from "@app/components/Floating.svelte";
@@ -29,7 +30,6 @@
meta = peers.find(p => p.id === peer);
items = peers.map(p => {
return {
key: `<span style="gap: 0;"><span style="color: var(--color-foreground-6);display: inline;">did:key:</span>${p.id}</span>`,
value: p.id,
title: createTitle(p),
badge: p.delegate ? "delegate" : null,
@@ -56,15 +56,25 @@
background-color: var(--color-secondary-2);
border-radius: var(--border-radius-small);
}
.selected {
padding: 0.5rem 0.75rem !important;
}
.selector .peer.not-allowed {
cursor: not-allowed;
}
.peer-id {
margin: 0 0.5rem;
}
.peer:hover {
background-color: var(--color-foreground-2);
}
.peer-item {
display: flex;
flex-direction: row;
align-items: center;
gap: 0;
}
.prefix {
display: inline-block;
color: var(--color-secondary-6);
}
.stat {
display: flex;
align-items: center;
@@ -78,24 +88,24 @@
<Floating>
<div slot="toggle" class="selector" title="Change peer">
<div class="stat peer" class:not-allowed={!peers}>
<Icon name="fork" />
<div class="stat peer" class:selected={peer} class:not-allowed={!peers}>
{#if !peer}
<Icon name="fork" />
{/if}
{#if meta}
<span class="peer-id">
<span style="display: flex;">
<span style="color: var(--color-secondary-5);">did:key:</span>
{truncateId(meta.id)}
</span>
<span style:display="flex">
<Avatar nodeId={meta.id} inline />
<span style:color="var(--color-secondary-5)">did:key:</span>
{truncateId(meta.id)}
</span>
{#if meta.delegate}
<Badge variant="primary">delegate</Badge>
{/if}
{:else if peer}
<span class="peer-id">
<span style="display: flex;">
<span style="color: var(--color-secondary-5);">did:key:</span>
{truncateId(peer)}
</span>
<span style:display="flex">
<Avatar nodeId={peer} inline />
<span style:color="var(--color-secondary-5)">did:key:</span>
{truncateId(peer)}
</span>
{/if}
</div>
@@ -105,6 +115,17 @@
<Dropdown
{items}
selected={peer}
on:select={e => switchPeer(e.detail.value)} />
on:select={e => switchPeer(e.detail.value)}>
<div class="peer-item" slot="item" let:item>
<Avatar nodeId={item.value} inline />
<!-- We ignore prettier here for the following line
to avoid getting a whitespace between did:key: and the nid due to a newline -->
<!-- prettier-ignore -->
<span><span class="prefix">did:key:</span>{item.value}</span>
{#if item.badge}
<Badge variant="primary">{item.badge}</Badge>
{/if}
</div>
</Dropdown>
</svelte:fragment>
</Floating>
@@ -80,6 +80,7 @@
padding: 1rem;
}
main {
font-size: var(--font-size-small);
border-top: 1px dashed var(--color-foreground-4);
background: var(--color-background-1);
border-radius: 0 0 var(--border-radius-small) var(--border-radius-small);