Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Implement markdown rendering
Alexis Sellier committed 4 years ago
commit 86269546d5daf8d8e1a57262f9234578af4f1917
parent e5410fed4e4ec4237cfe9e96281990c78c9b6fe0
12 files changed +213 -20
modified package-lock.json
@@ -6,8 +6,10 @@
    "": {
      "dependencies": {
        "@snowpack/plugin-typescript": "^1.2.1",
+
        "@types/marked": "^2.0.3",
        "ethereum-blockies": "^0.1.1",
        "ethers": "^5.0.31",
+
        "marked": "^2.0.7",
        "multibase": "^4.0.4",
        "multihashes": "^4.0.2",
        "svelte": "^3.32.3",
@@ -809,6 +811,11 @@
      "integrity": "sha512-EBrpH2iXXfaf/9z81koiDYkp2mlwW2XzFcAqn6qh7VKyP8zBvHHAQzNhY+W9vH5arAjmGAm5g8ElWq6YmXm3ig==",
      "dev": true
    },
+
    "node_modules/@types/marked": {
+
      "version": "2.0.3",
+
      "resolved": "https://registry.npmjs.org/@types/marked/-/marked-2.0.3.tgz",
+
      "integrity": "sha512-lbhSN1rht/tQ+dSWxawCzGgTfxe9DB31iLgiT1ZVT5lshpam/nyOA1m3tKHRoNPctB2ukSL22JZI5Fr+WI/zYg=="
+
    },
    "node_modules/@types/node": {
      "version": "15.0.2",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
@@ -2117,6 +2124,17 @@
        "node": ">= 10"
      }
    },
+
    "node_modules/marked": {
+
      "version": "2.0.7",
+
      "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.7.tgz",
+
      "integrity": "sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ==",
+
      "bin": {
+
        "marked": "bin/marked"
+
      },
+
      "engines": {
+
        "node": ">= 8.16.2"
+
      }
+
    },
    "node_modules/merge-stream": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -3995,6 +4013,11 @@
      "integrity": "sha512-EBrpH2iXXfaf/9z81koiDYkp2mlwW2XzFcAqn6qh7VKyP8zBvHHAQzNhY+W9vH5arAjmGAm5g8ElWq6YmXm3ig==",
      "dev": true
    },
+
    "@types/marked": {
+
      "version": "2.0.3",
+
      "resolved": "https://registry.npmjs.org/@types/marked/-/marked-2.0.3.tgz",
+
      "integrity": "sha512-lbhSN1rht/tQ+dSWxawCzGgTfxe9DB31iLgiT1ZVT5lshpam/nyOA1m3tKHRoNPctB2ukSL22JZI5Fr+WI/zYg=="
+
    },
    "@types/node": {
      "version": "15.0.2",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-15.0.2.tgz",
@@ -5050,6 +5073,11 @@
        "ssri": "^8.0.0"
      }
    },
+
    "marked": {
+
      "version": "2.0.7",
+
      "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.7.tgz",
+
      "integrity": "sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ=="
+
    },
    "merge-stream": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
modified package.json
@@ -15,8 +15,10 @@
  },
  "dependencies": {
    "@snowpack/plugin-typescript": "^1.2.1",
+
    "@types/marked": "^2.0.3",
    "ethereum-blockies": "^0.1.1",
    "ethers": "^5.0.31",
+
    "marked": "^2.0.7",
    "multibase": "^4.0.4",
    "multihashes": "^4.0.2",
    "svelte": "^3.32.3",
modified public/index.css
@@ -46,6 +46,7 @@
	--color-foreground-faded: #777788;
	--color-foreground-subtle: #444455;
	--color-foreground-background: #77778811;
+
	--color-foreground-background-subtle: #7777880a;
	--color-foreground-1: #242e38;
	--color-foreground-2: #29343d;
	--color-foreground-3: #333e47;
@@ -57,9 +58,9 @@
	--color-glow-error: #ff555522;

	--font-family-sans-serif: Inter, sans-serif;
+
	--font-family-monospace: monospace;
	--font-weight-medium: 600;
	--font-weight-bold: 700;
-
	--font-family-monospace: monospace;
	--border-radius: 50px;
	--box-shadow-color: var(--color-secondary-2);
	--content-max-width: 1920px;
modified public/typography.css
@@ -35,8 +35,3 @@
	font-weight: 700;
	src: url("fonts/SourceCodePro-Bold.otf");
}
-

-
:root {
-
	--typeface: "Inter";
-
	--typeface-mono: "Source Code Pro";
-
}
modified snowpack.config.js
@@ -19,6 +19,7 @@ module.exports = {
    /* Enable an SPA Fallback in development: */
    {"match": "routes", "src": ".*", "dest": "/index.html"},
    {"match": "all", "src": "/projects/.*", "dest": "/index.html"},
+
    {"match": "all", "src": "/orgs/.*/projects/.*", "dest": "/index.html"},
  ],
  optimize: {
    /* Setting `bundle: true` breaks .json imports in snowpack 3.5.0 */
added src/Markdown.svelte
@@ -0,0 +1,132 @@
+
<script lang="typescript">
+
  import marked from "marked";
+

+
  export let content: string;
+
</script>
+

+
<style>
+
  .markdown :global(h1, h2, h3, h4, h5, h6) {
+
    color: var(--color-foreground-90);
+
  }
+

+
  .markdown :global(h1) {
+
    font-family: var(--typeface-medium);
+
    font-size: 1.75rem;
+
    font-weight: var(--font-weight-medium);
+
    padding: 1rem 0;
+
    margin: 0 0 0.5rem;
+
  }
+

+
  .markdown :global(h2) {
+
    font-size: 1.5rem;
+
    font-weight: var(--font-weight-medium);
+
    padding: 0.75rem 0;
+
    margin: 1.8rem 0 0.5rem;
+
  }
+

+
  .markdown :global(h3) {
+
    font-weight: var(--font-weight-medium);
+
    font-size: 1.25rem;
+
    padding: 0.65rem 0;
+
    margin: 1.75rem 0 0.25rem;
+
  }
+

+
  .markdown :global(h4) {
+
    font-weight: var(--font-weight-medium);
+
    font-size: 1rem;
+
    padding: 0.5rem 0;
+
    margin: 1.5rem 0 0.125rem;
+
  }
+

+
  .markdown :global(h5) {
+
    font-weight: var(--font-weight-medium);
+
    font-size: 0.875rem;
+
    padding: 0.35rem 0;
+
    margin: 1.35rem 0 0.125rem;
+
  }
+

+
  .markdown :global(h6) {
+
    font-weight: var(--font-weight-medium);
+
    font-size: 0.75rem;
+
    padding: 0.25rem 0;
+
    margin: 1.25rem 0 0.125rem;
+
  }
+

+
  .markdown :global(p) {
+
    margin-top: 0;
+
    margin-bottom: 0.625rem;
+
  }
+

+
  .markdown :global(strong) {
+
    font-weight: var(--font-weight-medium);
+
  }
+

+
  .markdown :global(img) {
+
    border-style: none;
+
    max-width: 100%;
+
  }
+

+
  .markdown :global(code) {
+
    font-family: var(--font-family-monospace);
+
    font-size: 1rem;
+
    color: var(--color-light);
+
    background-color: var(--color-foreground-background);
+
    padding: 1px 0.25rem;
+
    border-radius: 0.25rem;
+
  }
+

+
  .markdown :global(pre code) {
+
    background: none;
+
    padding: 0;
+
  }
+

+
  .markdown :global(pre) {
+
    font-family: var(--font-family-monospace);
+
    font-size: 1rem;
+
    background-color: var(--color-foreground-background);
+
    padding: 1rem;
+
    border-radius: 0.25rem;
+
    margin: 1rem 0;
+
    overflow: scroll;
+
    scrollbar-width: none;
+
  }
+

+
  .markdown :global(pre::-webkit-scrollbar) {
+
    display: none;
+
  }
+

+
  .markdown :global(a), .markdown :global(a > code) {
+
    color: var(--color-tertiary-faded);
+
    text-decoration: none;
+
  }
+
  .markdown :global(a:hover) {
+
    text-decoration: underline;
+
  }
+

+
  .markdown :global(hr) {
+
    height: 0;
+
    margin: 0rem 0;
+
    overflow: hidden;
+
    background: transparent;
+
    border: 0;
+
    border-bottom: 1px solid var(--color-foreground-faded);
+
  }
+

+
  .markdown :global(ol) {
+
    list-style-type: decimal;
+
    margin-bottom: 1rem;
+
    padding-left: 1.5rem;
+
  }
+

+
  .markdown :global(ul) {
+
    list-style-type: inherit;
+
    padding-left: 1.25rem;
+
    margin-bottom: 1rem;
+
  }
+
</style>
+

+
{#if content}
+
  <div class="markdown">
+
    {@html marked(content)}
+
  </div>
+
{/if}
modified src/api.ts
@@ -1,10 +1,21 @@
import type { Config } from '@app/config';

-
export async function get(path: string, config: Config): Promise<any> {
+
export async function get(
+
  path: string,
+
  params: Record<string, any>,
+
  config: Config
+
): Promise<any> {
  if (! config.seed.api)
    throw new Error("Seed HTTP API unavailable");

-
  const url = `${config.seed.api}/v1/${path}`;
+
  const query: Record<string, string> = {};
+
  for (const [key, val] of Object.entries(params)) {
+
    query[key] = val.toString();
+
  }
+

+
  const search = new URLSearchParams(query).toString();
+
  const baseUrl = `${config.seed.api}/v1/${path}`;
+
  const url = search ? `${baseUrl}?${search}` : baseUrl;

  let response = null;
  try {
modified src/base/projects/Blob.svelte
@@ -8,12 +8,6 @@
</script>

<style>
-
  .file-source {
-
    border: 1px solid var(--color-foreground-level-3);
-
    border-radius: 0.5rem;
-
    min-width: var(--content-min-width);
-
  }
-

  header .file-header {
    display: flex;
    height: 3rem;
modified src/base/projects/Browser.svelte
@@ -4,9 +4,11 @@
  import Loading from '@app/Loading.svelte';
  import Address from '@app/Address.svelte';
  import { Org } from '@app/base/orgs/Org';
+
  import * as utils from '@app/utils';

  import Tree from './Tree.svelte';
  import Blob from './Blob.svelte';
+
  import Readme from './Readme.svelte';

  export let urn: string;
  export let commit: string;
@@ -19,9 +21,10 @@
    return proj.getTree(urn, commit, path, config);
  };

+
  $: isMarkdownPath = utils.isMarkdownPath(path);
  $: getBlob = path === "/"
    ? proj.getReadme(urn, commit, config)
-
    : proj.getBlob(urn, commit, path, config);
+
    : proj.getBlob(urn, commit, path, { highlight: !isMarkdownPath }, config);
  $: getAnchor = org ? Org.getAnchor(org, urn, config) : null;
</script>

@@ -139,7 +142,11 @@
          {#await getBlob}
            <Loading small center />
          {:then blob}
-
            <Blob {blob} />
+
            {#if utils.isMarkdownPath(blob.path)}
+
              <Readme content={blob.content} />
+
            {:else}
+
              <Blob {blob} />
+
            {/if}
          {:catch}
            <div class="error error-message file-not-found">
              <header>
added src/base/projects/Readme.svelte
@@ -0,0 +1,16 @@
+
<script lang="typescript">
+
  import Markdown from '@app/Markdown.svelte';
+
  export let content: string;
+
</script>
+

+
<style>
+
  article {
+
    padding: 2rem;
+
    background: var(--color-foreground-background-subtle);
+
    border-radius: 0.5rem;
+
  }
+
</style>
+

+
<article>
+
  <Markdown {content} />
+
</article>
modified src/project.ts
@@ -67,7 +67,7 @@ export interface Tree {
}

export async function getInfo(urn: string, config: Config): Promise<Info> {
-
  return api.get(`projects/${urn}`, config);
+
  return api.get(`projects/${urn}`, {}, config);
}

export async function getTree(
@@ -79,16 +79,17 @@ export async function getTree(
  if (path === "/") {
    path = "";
  }
-
  return api.get(`projects/${urn}/tree/${commit}/${path}`, config);
+
  return api.get(`projects/${urn}/tree/${commit}/${path}`, {}, config);
}

export async function getBlob(
  urn: string,
  commit: string,
  path: string,
+
  options: { highlight: boolean },
  config: Config
): Promise<Blob> {
-
  return api.get(`projects/${urn}/blob/${commit}/${path}`, config);
+
  return api.get(`projects/${urn}/blob/${commit}/${path}`, options, config);
}

export async function getReadme(
@@ -96,7 +97,7 @@ export async function getReadme(
  commit: string,
  config: Config
): Promise<Blob> {
-
  return api.get(`projects/${urn}/readme/${commit}`, config);
+
  return api.get(`projects/${urn}/readme/${commit}`, {}, config);
}

export function path(
modified src/utils.ts
@@ -206,3 +206,8 @@ export async function getTokens(address: string, config: Config):
  // TODO
  return [];
}
+

+
// Check whether the given path has a markdown file extension.
+
export function isMarkdownPath(path: string): boolean {
+
  return /\.(md|mkd|markdown)$/i.test(path);
+
}