Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Implement sticky file headers
Rūdolfs Ošiņš committed 2 years ago
commit 6f4f7894a38939b8b144cbd64f7be93cde1b660d
parent 77b49dcb601fe3accf1037b6b38b62b01766dff7
11 files changed +227 -253
modified src/components/ExpandButton.svelte
@@ -1,9 +1,15 @@
-
<script lang="ts">
+
<script lang="ts" strictEvents>
+
  import { createEventDispatcher } from "svelte";
+

  import IconButton from "./IconButton.svelte";
  import IconSmall from "./IconSmall.svelte";

-
  export let expanded: boolean = true;
  export let variant: "left-aligned" | "inline" = "left-aligned";
+

+
  let expanded: boolean = true;
+
  const dispatch = createEventDispatcher<{
+
    toggle: { expanded: boolean };
+
  }>();
</script>

<style>
@@ -13,7 +19,12 @@
  }
</style>

-
<IconButton ariaLabel="expand" on:click={() => (expanded = !expanded)}>
+
<IconButton
+
  ariaLabel="expand"
+
  on:click={() => {
+
    expanded = !expanded;
+
    dispatch("toggle", { expanded });
+
  }}>
  <div class="expand">
    {#if expanded}
      <IconSmall name={variant === "inline" ? "ellipsis" : "chevron-down"} />
modified src/components/File.svelte
@@ -1,65 +1,86 @@
+
<script lang="ts">
+
  import { tick } from "svelte";
+
  import ExpandButton from "./ExpandButton.svelte";
+

+
  export let collapsable: boolean = false;
+

+
  let expanded = true;
+
  let header: HTMLDivElement;
+
</script>
+

<style>
  .header {
    display: flex;
    height: 3rem;
    align-items: center;
    padding: 0 0.5rem 0 1rem;
-
    border-width: 1px 1px 0 1px;
-
    border-color: var(--color-border-hint);
-
    border-style: solid;
+
    border: 1px solid var(--color-border-hint);
    border-top-left-radius: var(--border-radius-small);
    border-top-right-radius: var(--border-radius-small);
+
    background-color: var(--color-background-default);
+
    position: sticky;
+
    top: 0;
+
    z-index: 10;
+
  }
+

+
  .collapsed {
+
    border-radius: var(--border-radius-small);
+
    border: 1px solid var(--color-border-hint);
+
  }
+

+
  .left {
+
    display: flex;
+
    gap: 0.5rem;
+
    margin-right: 1rem;
  }

  .right {
    display: flex;
    gap: 0.5rem;
    margin-left: auto;
-
    white-space: nowrap;
    overflow: hidden;
-
    text-overflow: ellipsis;
-
  }
-

-
  .left {
-
    font-weight: var(--font-weight-semibold);
-
    font-size: var(--font-size-small);
-
    flex-shrink: 0;
-
    padding-right: 0.5rem;
  }

  .container {
    position: relative;
-
    display: flex;
    overflow-x: auto;
    border: 1px solid var(--color-border-hint);
-
    border-top-style: solid;
-
    border-bottom-left-radius: var(--border-radius-small);
-
    border-bottom-right-radius: var(--border-radius-small);
+
    border-top: 0;
    background: var(--color-background-float);
-
    width: 100%;
    border-bottom-left-radius: var(--border-radius-small);
    border-bottom-right-radius: var(--border-radius-small);
  }

  @media (max-width: 720px) {
+
    .header,
    .container {
-
      border-left: none;
-
      border-right: none;
-
      border-bottom-left-radius: 0;
-
      border-bottom-right-radius: 0;
+
      border-radius: 0;
    }
  }
</style>

-
<div class="header">
-
  <span class="left">
+
<div bind:this={header} class="header" class:collapsed={!expanded}>
+
  <div class="left">
+
    {#if collapsable}
+
      <ExpandButton
+
        on:toggle={async () => {
+
          expanded = !expanded;
+
          if (!expanded) {
+
            await tick();
+
            header.scrollIntoView({ behavior: "smooth", block: "nearest" });
+
          }
+
        }} />
+
    {/if}
    <slot name="left-header" />
-
  </span>
+
  </div>
+

  <div class="right">
    <slot name="right-header" />
  </div>
</div>

-
<div class="container">
-
  <slot />
-
</div>
+
{#if expanded}
+
  <div class="container">
+
    <slot />
+
  </div>
+
{/if}
modified src/components/FilePath.svelte
@@ -10,8 +10,9 @@
</script>

<style>
-
  .container {
+
  .file-path {
    font-size: var(--font-size-small);
+
    white-space: nowrap;
  }

  .path {
@@ -25,4 +26,4 @@
</style>

<!-- prettier-ignore -->
-
<span class="container"><span class="path">{path}</span><span class="filename">{filename}</span></span>
+
<span class="file-path"><span class="path">{path}</span><span class="filename">{filename}</span></span>
modified src/components/Markdown.svelte
@@ -158,6 +158,9 @@
</script>

<style>
+
  :global(html) {
+
    scroll-padding-top: 4rem;
+
  }
  .front-matter {
    font-size: var(--font-size-tiny);
    font-family: var(--font-family-monospace);
modified src/views/projects/Changeset.svelte
@@ -59,19 +59,24 @@
</script>

<style>
-
  .changeset-summary {
+
  .summary {
    padding-bottom: 1.5rem;
    margin-left: 1rem;
  }
-
  .changeset-summary .additions {
+
  .additions {
    color: var(--color-foreground-success);
  }
-
  .changeset-summary .deletions {
+
  .deletions {
    color: var(--color-foreground-red);
  }
+
  .diff-list {
+
    display: flex;
+
    flex-direction: column;
+
    gap: 1.5rem;
+
  }
</style>

-
<div class="changeset-summary">
+
<div class="summary">
  <span>{diffDescription(diff)}</span>
  with
  <span class:additions={diff.stats.insertions > 0}>
@@ -84,7 +89,8 @@
    {pluralize("deletion", diff.stats.deletions)}
  </span>
</div>
-
<div class="diff-listing">
+

+
<div class="diff-list">
  <Observer let:filesVisibility let:observer>
    {#each diff.added as file}
      <div use:intersection={observer} id={"observer:" + file.path}>
modified src/views/projects/Changeset/FileDiff.svelte
@@ -7,7 +7,7 @@
  import * as Syntax from "@app/lib/syntax";

  import Badge from "@app/components/Badge.svelte";
-
  import ExpandButton from "@app/components/ExpandButton.svelte";
+
  import File from "@app/components/File.svelte";
  import FilePath from "@app/components/FilePath.svelte";
  import IconButton from "@app/components/IconButton.svelte";
  import IconSmall from "@app/components/IconSmall.svelte";
@@ -31,7 +31,6 @@
  export let projectId: string;
  export let visible: boolean = false;

-
  let expanded = true;
  let selection: Selection | undefined = undefined;
  let highlighting: { new?: string[]; old?: string[] } | undefined = undefined;
  let syntaxHighlightingLoading: boolean = false;
@@ -49,7 +48,7 @@
            "-",
          ),
        )
-
        ?.scrollIntoView();
+
        ?.scrollIntoView({ block: "center" });
    }
  });

@@ -239,25 +238,8 @@
</script>

<style>
-
  .wrapper {
-
    border: 1px solid var(--color-border-default);
-
    border-radius: var(--border-radius-small);
-
    margin-bottom: 2rem;
-
    line-height: 1.5rem;
-
  }
-
  .header {
-
    align-items: center;
-
    background: none;
-
    border-radius: 0;
-
    display: flex;
-
    flex-direction: row;
-
    height: 3rem;
-
    padding: 1rem;
-
    gap: 0.5rem;
-
  }
  main {
    font-size: var(--font-size-small);
-
    border-top: 1px solid var(--color-border-default);
    background: var(--color-background-float);
    border-radius: 0 0 var(--border-radius-small) var(--border-radius-small);
    overflow-x: auto;
@@ -392,131 +374,123 @@
    padding-left: 0.5rem;
    color: var(--color-foreground-dim);
  }
-
  .header-right {
-
    margin-left: auto;
-
    display: flex;
-
    align-items: center;
-
    gap: 1rem;
-
  }
</style>

-
<div id={filePath} class="wrapper">
-
  <div class="header">
-
    <ExpandButton bind:expanded />
-
    <div class="actions">
-
      {#if (headerBadgeCaption === "moved" || headerBadgeCaption === "copied") && oldFilePath}
-
        <span>
-
          <FilePath filenameWithPath={oldFilePath} /> → <FilePath
-
            filenameWithPath={filePath} />
-
        </span>
-
      {:else}
-
        <FilePath filenameWithPath={filePath} />
-
      {/if}
-
      {#if headerBadgeCaption === "added"}
-
        <Badge variant="positive">added</Badge>
-
      {:else if headerBadgeCaption === "deleted"}
-
        <Badge variant="negative">deleted</Badge>
-
      {:else if headerBadgeCaption === "moved"}
-
        <Badge variant="foreground">moved</Badge>
-
      {:else if headerBadgeCaption === "copied"}
-
        <Badge variant="foreground">copied</Badge>
-
      {/if}
-
    </div>
+
<File collapsable>
+
  <svelte:fragment slot="left-header">
+
    {#if (headerBadgeCaption === "moved" || headerBadgeCaption === "copied") && oldFilePath}
+
      <span>
+
        <FilePath filenameWithPath={oldFilePath} /> → <FilePath
+
          filenameWithPath={filePath} />
+
      </span>
+
    {:else}
+
      <FilePath filenameWithPath={filePath} />
+
    {/if}
+

+
    {#if headerBadgeCaption === "added"}
+
      <Badge variant="positive">added</Badge>
+
    {:else if headerBadgeCaption === "deleted"}
+
      <Badge variant="negative">deleted</Badge>
+
    {:else if headerBadgeCaption === "moved"}
+
      <Badge variant="foreground">moved</Badge>
+
    {:else if headerBadgeCaption === "copied"}
+
      <Badge variant="foreground">copied</Badge>
+
    {/if}
+
  </svelte:fragment>
+

+
  <svelte:fragment slot="right-header">
    {#if revision}
-
      <div class="header-right">
-
        {#if syntaxHighlightingLoading}
-
          <Loading small />
-
        {/if}
-
        <Link
-
          route={{
-
            resource: "project.source",
-
            project: projectId,
-
            node: baseUrl,
-
            path: filePath,
-
            revision,
-
          }}>
-
          <IconButton title="View file">
-
            <IconSmall name="chevron-left-right" />
-
          </IconButton>
-
        </Link>
-
      </div>
+
      {#if syntaxHighlightingLoading}
+
        <Loading small />
+
      {/if}
+
      <Link
+
        route={{
+
          resource: "project.source",
+
          project: projectId,
+
          node: baseUrl,
+
          path: filePath,
+
          revision,
+
        }}>
+
        <IconButton title="View file">
+
          <IconSmall name="chevron-left-right" />
+
        </IconButton>
+
      </Link>
    {/if}
-
  </div>
-
  {#if expanded}
-
    <main>
-
      {#if fileDiff.type === "plain"}
-
        {#if fileDiff.hunks.length > 0}
-
          <table class="diff" data-file-diff-select>
-
            {#each fileDiff.hunks as hunk, hunkIdx}
+
  </svelte:fragment>
+

+
  <main>
+
    {#if fileDiff.type === "plain"}
+
      {#if fileDiff.hunks.length > 0}
+
        <table class="diff" data-file-diff-select>
+
          {#each fileDiff.hunks as hunk, hunkIdx}
+
            <tr
+
              class="diff-line hunk-header"
+
              class:selected={hunkHeaderSelected(selection, hunkIdx)}>
+
              <td colspan={2} style:position="relative">
+
                <div class="selection-indicator-left" />
+
              </td>
+
              <td
+
                colspan={6}
+
                class="diff-expand-header"
+
                style:position="relative">
+
                {hunk.header}
+
                <div class="selection-indicator-right" />
+
              </td>
+
            </tr>
+
            {#each hunk.lines as line, lineIdx}
              <tr
-
                class="diff-line hunk-header"
-
                class:selected={hunkHeaderSelected(selection, hunkIdx)}>
-
                <td colspan={2} style:position="relative">
+
                style:position="relative"
+
                class={`diff-line type-${line.type}`}
+
                class:selection-start={selection?.startHunk === hunkIdx &&
+
                  selection.startLine === lineIdx}
+
                class:selection-end={(selection?.endHunk === hunkIdx &&
+
                  selection.endLine === lineIdx) ||
+
                  (selection?.startHunk === hunkIdx &&
+
                    selection.startLine === lineIdx &&
+
                    selection?.endHunk === undefined)}
+
                class:selected={isLineSelected(selection, hunkIdx, lineIdx)}>
+
                <td
+
                  id={[filePath, "H" + hunkIdx, "L" + lineIdx].join("-")}
+
                  class="diff-line-number left"
+
                  on:click={e => selectLine(hunkIdx, lineIdx, e)}>
                  <div class="selection-indicator-left" />
+
                  {lineNumberL(line)}
                </td>
                <td
-
                  colspan={6}
-
                  class="diff-expand-header"
-
                  style:position="relative">
-
                  {hunk.header}
-
                  <div class="selection-indicator-right" />
+
                  class="diff-line-number right"
+
                  on:click={e => selectLine(hunkIdx, lineIdx, e)}>
+
                  {lineNumberR(line)}
                </td>
-
              </tr>
-
              {#each hunk.lines as line, lineIdx}
-
                <tr
-
                  style:position="relative"
-
                  class={`diff-line type-${line.type}`}
-
                  class:selection-start={selection?.startHunk === hunkIdx &&
-
                    selection.startLine === lineIdx}
-
                  class:selection-end={(selection?.endHunk === hunkIdx &&
-
                    selection.endLine === lineIdx) ||
-
                    (selection?.startHunk === hunkIdx &&
-
                      selection.startLine === lineIdx &&
-
                      selection?.endHunk === undefined)}
-
                  class:selected={isLineSelected(selection, hunkIdx, lineIdx)}>
-
                  <td
-
                    id={[filePath, "H" + hunkIdx, "L" + lineIdx].join("-")}
-
                    class="diff-line-number left"
-
                    on:click={e => selectLine(hunkIdx, lineIdx, e)}>
-
                    <div class="selection-indicator-left" />
-
                    {lineNumberL(line)}
-
                  </td>
-
                  <td
-
                    class="diff-line-number right"
-
                    on:click={e => selectLine(hunkIdx, lineIdx, e)}>
-
                    {lineNumberR(line)}
-
                  </td>
-
                  <td class="diff-line-type" data-line-type={line.type}>
-
                    {lineSign(line)}
-
                  </td>
-
                  <td class="diff-line-content">
-
                    {#if highlighting}
-
                      {#if line.type === "addition" && highlighting.new}
-
                        {@html highlighting.new[line.lineNo - 1]}
-
                      {:else if line.type === "context" && highlighting.new}
-
                        {@html highlighting.new[line.lineNoNew - 1]}
-
                      {:else if line.type === "deletion" && highlighting.old}
-
                        {@html highlighting.old[line.lineNo - 1]}
-
                      {/if}
-
                    {:else}
-
                      {line.line}
+
                <td class="diff-line-type" data-line-type={line.type}>
+
                  {lineSign(line)}
+
                </td>
+
                <td class="diff-line-content">
+
                  {#if highlighting}
+
                    {#if line.type === "addition" && highlighting.new}
+
                      {@html highlighting.new[line.lineNo - 1]}
+
                    {:else if line.type === "context" && highlighting.new}
+
                      {@html highlighting.new[line.lineNoNew - 1]}
+
                    {:else if line.type === "deletion" && highlighting.old}
+
                      {@html highlighting.old[line.lineNo - 1]}
                    {/if}
-
                  </td>
-
                  <div class="selection-indicator-right" />
-
                </tr>
-
              {/each}
+
                  {:else}
+
                    {line.line}
+
                  {/if}
+
                </td>
+
                <div class="selection-indicator-right" />
+
              </tr>
            {/each}
-
          </table>
-
        {:else}
-
          <div style:margin="1rem 0">
-
            <Placeholder iconName="empty-file" caption="Empty file" inline />
-
          </div>
-
        {/if}
+
          {/each}
+
        </table>
      {:else}
        <div style:margin="1rem 0">
-
          <Placeholder iconName="binary-file" caption="Binary file" inline />
+
          <Placeholder iconName="empty-file" caption="Empty file" inline />
        </div>
      {/if}
-
    </main>
-
  {/if}
-
</div>
+
    {:else}
+
      <div style:margin="1rem 0">
+
        <Placeholder iconName="binary-file" caption="Binary file" inline />
+
      </div>
+
    {/if}
+
  </main>
+
</File>
modified src/views/projects/Cob/CobCommitTeaser.svelte
@@ -71,7 +71,11 @@
        </div>
      </Link>
      {#if commit.description}
-
        <ExpandButton variant="inline" bind:expanded={commitMessageVisible} />
+
        <ExpandButton
+
          variant="inline"
+
          on:toggle={() => {
+
            commitMessageVisible = !commitMessageVisible;
+
          }} />
      {/if}
    </div>
    {#if commitMessageVisible}
modified src/views/projects/Cob/Revision.svelte
@@ -23,7 +23,7 @@
  import Thread from "@app/components/Thread.svelte";

  export let baseUrl: BaseUrl;
-
  export let expanded: boolean = true;
+
  export let expanded: boolean = false;
  export let patchId: string;
  export let projectHead: string;
  export let projectDefaultBranch: string;
@@ -124,7 +124,7 @@
    background: none;
    padding: 0.5rem;
    font-size: var(--font-size-small);
-
    height: 2.5rem;
+
    height: 3rem;
  }
  .revision-name {
    display: flex;
@@ -200,7 +200,10 @@
  <div class="revision-box" class:expanded>
    <div class="revision-header">
      <div class="revision-name">
-
        <ExpandButton bind:expanded />
+
        <ExpandButton
+
          on:toggle={() => {
+
            expanded = !expanded;
+
          }} />
        <span>
          Revision
          <span class="global-hash">{utils.formatObjectId(revisionId)}</span>
modified src/views/projects/Commit/CommitTeaser.svelte
@@ -79,7 +79,11 @@
        </div>
      </Link>
      {#if commit.description}
-
        <ExpandButton variant="inline" bind:expanded={commitMessageVisible} />
+
        <ExpandButton
+
          variant="inline"
+
          on:toggle={() => {
+
            commitMessageVisible = !commitMessageVisible;
+
          }} />
      {/if}
    </div>
    {#if commitMessageVisible}
modified src/views/projects/Source/Blob.svelte
@@ -10,13 +10,14 @@
  import { routeToPath } from "@app/lib/router";

  import Button from "@app/components/Button.svelte";
+
  import File from "@app/components/File.svelte";
  import FilePath from "@app/components/FilePath.svelte";
  import IconSmall from "@app/components/IconSmall.svelte";
+
  import InlineMarkdown from "@app/components/InlineMarkdown.svelte";
  import Link from "@app/components/Link.svelte";
  import Markdown from "@app/components/Markdown.svelte";
  import Placeholder from "@app/components/Placeholder.svelte";
  import Radio from "@app/components/Radio.svelte";
-
  import InlineMarkdown from "@app/components/InlineMarkdown.svelte";

  export let baseUrl: BaseUrl;
  export let projectId: string;
@@ -82,7 +83,7 @@
      const target = document.getElementById(selectedLineId);
      if (target) {
        target.classList.add("highlight");
-
        target.scrollIntoView();
+
        target.scrollIntoView({ block: "center" });
      }
    }
  });
@@ -97,34 +98,6 @@
</script>

<style>
-
  .file-header {
-
    display: flex;
-
    height: 3rem;
-
    align-items: center;
-
    padding: 0 0.5rem 0 1rem;
-
    border-width: 1px 1px 0 1px;
-
    border-color: var(--color-border-hint);
-
    border-style: solid;
-
    border-top-left-radius: var(--border-radius-small);
-
    border-top-right-radius: var(--border-radius-small);
-
  }
-

-
  .right {
-
    display: flex;
-
    gap: 0.5rem;
-
    margin-left: auto;
-
    white-space: nowrap;
-
    overflow: hidden;
-
    text-overflow: ellipsis;
-
  }
-

-
  .file-name {
-
    font-weight: var(--font-weight-semibold);
-
    font-size: var(--font-size-small);
-
    flex-shrink: 0;
-
    padding-right: 0.5rem;
-
  }
-

  .code :global(.line-number) {
    font-family: var(--font-family-monospace);
    color: var(--color-foreground-disabled);
@@ -177,18 +150,6 @@
    margin-bottom: 1.5rem;
  }

-
  .container {
-
    overflow-x: auto;
-
    border: 1px solid var(--color-border-hint);
-
    border-top-style: solid;
-
    border-bottom-left-radius: var(--border-radius-small);
-
    border-bottom-right-radius: var(--border-radius-small);
-
    background: var(--color-background-float);
-
    width: 100%;
-
    border-bottom-left-radius: var(--border-radius-small);
-
    border-bottom-right-radius: var(--border-radius-small);
-
  }
-

  .no-scrollbar {
    scrollbar-width: none;
  }
@@ -211,29 +172,15 @@
    .commit-teaser {
      padding: 0 0.75rem;
    }
-
    .file-header {
-
      border-top-left-radius: 0;
-
      border-top-right-radius: 0;
-
      border-left: none;
-
      border-right: none;
-
    }
    .hash-button {
      display: none;
    }
-
    .container {
-
      border-left: none;
-
      border-right: none;
-
      border-bottom-left-radius: 0;
-
      border-bottom-right-radius: 0;
-
    }
  }
</style>

-
<div class="file-header">
-
  <span class="file-name">
-
    <FilePath filenameWithPath={blob.path} />
-
  </span>
-
  <div class="right">
+
<File>
+
  <FilePath slot="left-header" filenameWithPath={blob.path} />
+
  <svelte:fragment slot="right-header">
    <div class="commit-teaser">
      <div class="hash-button">
        <Link
@@ -252,7 +199,9 @@
          </Button>
        </Link>
      </div>
-
      <InlineMarkdown fontSize="small" content={lastCommit.summary} />
+
      <div style:white-space="nowrap">
+
        <InlineMarkdown fontSize="small" content={lastCommit.summary} />
+
      </div>
    </div>
    <div class="layout-desktop-flex" style:gap="0.5rem">
      {#if isMarkdown}
@@ -283,10 +232,8 @@
        </Button>
      </a>
    </div>
-
  </div>
-
</div>
+
  </svelte:fragment>

-
<div class="container">
  {#if blob.binary}
    <div style:margin="4rem 0" style:width="100%">
      <Placeholder iconName="binary-file" caption="Binary file" />
@@ -304,4 +251,4 @@
      <Placeholder iconName="empty-file" caption="Empty file" />
    </div>
  {/if}
-
</div>
+
</File>
modified tests/e2e/project.spec.ts
@@ -56,7 +56,7 @@ test("navigate to project", async ({ page }) => {
  }

  // Navigate to the project README.md by default.
-
  await expect(page.locator(".file-name")).toContainText("README.md");
+
  await expect(page.locator(".filename")).toContainText("README.md");

  // Show a commit teaser.
  await expect(page.getByText("dd068e9 Add README.md")).toBeVisible();
@@ -170,40 +170,40 @@ test("files with special characters in the filename", async ({ page }) => {
  await sourceTree.getByText("special").click();

  await sourceTree.getByText("+plus+").click();
-
  await expect(page.locator(".file-name")).toContainText("+plus");
+
  await expect(page.locator(".filename")).toContainText("+plus");

  await sourceTree.getByText("-dash-").click();
-
  await expect(page.locator(".file-name")).toContainText("-dash-");
+
  await expect(page.locator(".filename")).toContainText("-dash-");

  await sourceTree.getByText(":colon:").click();
-
  await expect(page.locator(".file-name")).toContainText(":colon:");
+
  await expect(page.locator(".filename")).toContainText(":colon:");

  await sourceTree.getByText(";semicolon;").click();
-
  await expect(page.locator(".file-name")).toContainText(";semicolon;");
+
  await expect(page.locator(".filename")).toContainText(";semicolon;");

  await sourceTree.getByText("@at@").click();
-
  await expect(page.locator(".file-name")).toContainText("@at@");
+
  await expect(page.locator(".filename")).toContainText("@at@");

  await sourceTree.getByText("_underscore_").click();
-
  await expect(page.locator(".file-name")).toContainText("_underscore_");
+
  await expect(page.locator(".filename")).toContainText("_underscore_");

  // TODO: fix these errors in `radicle-httpd` for the following edge cases.
  //
  // await sourceTree.getByText("back\\slash").click();
-
  // await expect(page.locator(".file-name")).toContainText("back\\slash");
+
  // await expect(page.locator(".filename")).toContainText("back\\slash");
  // await sourceTree.getByText("qs?param1=value?param2=value2#hash").click();
-
  // await expect(page.locator(".file-name")).toContainText(
+
  // await expect(page.locator(".filename")).toContainText(
  //   "qs?param1=value?param2=value2#hash",
  // );

  await sourceTree.getByText("spaces are okay").click();
-
  await expect(page.locator(".file-name")).toContainText("spaces are okay");
+
  await expect(page.locator(".filename")).toContainText("spaces are okay");

  await sourceTree.getByText("~tilde~").click();
-
  await expect(page.locator(".file-name")).toContainText("~tilde~");
+
  await expect(page.locator(".filename")).toContainText("~tilde~");

  await sourceTree.getByText("👹👹👹").click();
-
  await expect(page.locator(".file-name")).toContainText("👹👹👹");
+
  await expect(page.locator(".filename")).toContainText("👹👹👹");
});

test("binary files", async ({ page }) => {
@@ -453,7 +453,7 @@ test("internal file markdown link", async ({ page }) => {
  await page.getByRole("link", { name: "Markdown Cheatsheet" }).click();
  await expect(page).toHaveURL(`${markdownUrl}/tree/main/cheatsheet.md`);
  await expect(
-
    page.locator(".file-header", { hasText: "cheatsheet.md" }),
+
    page.locator(".filename", { hasText: "cheatsheet.md" }),
  ).toBeVisible();

  await page.goto(`${markdownUrl}/tree/main/link-files.md`);
@@ -462,7 +462,7 @@ test("internal file markdown link", async ({ page }) => {
    `${markdownUrl}/tree/main/assets/black-square.png`,
  );
  await expect(
-
    page.locator(".file-header", {
+
    page.locator(".file-path", {
      hasText: "assets/black-square.png",
    }),
  ).toBeVisible();