Radish alpha
r
rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5
Radicle web interface
Radicle
Git
radicle-explorer src views nodes View.svelte
<script lang="ts">
  import type { BaseUrl, Node, NodeStats } from "@http-client";

  import dompurify from "dompurify";
  import { markdown } from "@app/lib/markdown";

  import Command from "@app/components/Command.svelte";
  import Icon from "@app/components/Icon.svelte";
  import IconButton from "@app/components/IconButton.svelte";
  import Layout from "@app/components/Layout.svelte";
  import Popover from "@app/components/Popover.svelte";
  import ReposView from "./ReposView.svelte";
  import UserAvatar from "@app/components/UserAvatar.svelte";

  import PolicyExplainer from "./PolicyExplainer.svelte";
  import SeedSelector from "./SeedSelector.svelte";
  import Seeding from "./Seeding.svelte";
  import UserAgent from "./UserAgent.svelte";
  import NodeAddress from "./NodeAddress.svelte";

  export let baseUrl: BaseUrl;
  export let stats: NodeStats;
  export let node: Node;

  function render(content: string): string {
    return dompurify.sanitize(
      markdown({ linkify: true, emojis: true }).parse(content) as string,
    );
  }
</script>

<style>
  .sidebar-wrapper {
    display: flex;
    flex-direction: column;
    min-height: 100%;
  }

  .sidebar {
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    flex: 1;
  }

  .sidebar-content {
    gap: 1rem;
    display: flex;
    flex-direction: column;
  }

  .footer {
    margin-top: auto;
    padding-top: 1rem;
    font: var(--txt-body-m-regular);
    color: var(--color-text-tertiary);
  }

  .footer a {
    color: var(--color-text-tertiary);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
  }

  .footer a:hover {
    text-decoration: underline;
  }

  .description {
    word-break: break-word;
  }

  .sidebar-item {
    display: flex;
    align-items: center;
    height: 2rem;
  }

  .box {
    font: var(--txt-body-m-regular);
    line-height: 1.625rem;
    width: 17rem;
  }

  code {
    font: var(--txt-code-regular);
    background-color: var(--color-surface-mid);
    border-radius: var(--border-radius-sm);
    padding: 0.125rem 0.25rem;
  }
</style>

<Layout>
  <div slot="sidebar" class="sidebar-wrapper">
    {#if node.bannerUrl}
      <img style:width="100%" alt="Node banner" src={node.bannerUrl} />
    {/if}

    <div class="sidebar">
      <div class="sidebar-content">
        <div style:display="flex" style:align-items="center" style:gap="1rem">
          {#if node.avatarUrl}
            <img
              style:border-radius="var(--border-radius-md)"
              style:min-width="64px"
              width="64"
              height="64"
              class="avatar"
              alt="Seed avatar"
              src={node.avatarUrl} />
          {:else}
            <UserAvatar nodeId={node.id} styleWidth="4rem" />
          {/if}
          <div style:width="100%">
            <div class="global-flex-item">
              <SeedSelector {baseUrl} />
            </div>
            <NodeAddress {node} />
          </div>
        </div>

        {#if node.description}
          <div class="description txt-body-m-regular">
            {@html render(node.description)}
          </div>
        {:else}
          <div
            class="global-flex-item txt-body-m-regular"
            style:align-items="center"
            style:justify-content="space-between"
            style:color="var(--color-text-tertiary)"
            style:gap="0.25rem">
            No description configured.
            <Popover popoverPositionTop="0" popoverPositionLeft="2.25rem">
              <IconButton slot="toggle" let:toggle on:click={toggle}>
                <Icon name="guide" />
              </IconButton>

              <div slot="popover" class="box">
                If you're the owner of this node, you can customize this page by
                setting the
                <code>avatarUrl</code>
                ,
                <code>bannerUrl</code>
                and
                <code>description</code>
                fields under the
                <code>web</code>
                object in your node config.
                <div style:margin-top="1rem">
                  <Command command="rad config edit" fullWidth />
                </div>
              </div>
            </Popover>
          </div>
        {/if}

        <div style:display="flex" style:flex-direction="column">
          <PolicyExplainer seedingPolicy={node.config?.seedingPolicy} />
          <div class="sidebar-item">
            <Seeding count={stats.repos.total}>
              <div style:width="2rem"></div>
            </Seeding>
          </div>
          <div class="sidebar-item">
            <UserAgent agent={node.agent} />
          </div>
        </div>
      </div>

      <div class="footer">
        <a href="https://radicle.dev" target="_blank" rel="noreferrer">
          Learn more about Radicle
          <Icon name="open-external" />
        </a>
      </div>
    </div>
  </div>

  <div slot="center">
    <ReposView {baseUrl} {stats} />
  </div>
</Layout>