Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
Add overlay scrollbars with custom styling
Sebastian Martinez committed 10 months ago
commit e246a48b90778f391fa423606f6b9e502c8d7d33
parent 499a098
8 files changed +119 -88
modified index.html
@@ -65,6 +65,10 @@
    <link rel="stylesheet" type="text/css" href="/prettylights.css" />
    <link rel="stylesheet" type="text/css" href="/colors.css" />
    <link rel="stylesheet" type="text/css" href="/syntax.css" />
+
    <link
+
      rel="stylesheet"
+
      type="text/css"
+
      href="/node_modules/overlayscrollbars/styles/overlayscrollbars.min.css" />
    <script type="module">
      // Make global 'Buffer' available to legacy modules.
      import { Buffer } from "buffer";
modified package-lock.json
@@ -16,6 +16,8 @@
        "@tauri-apps/plugin-log": "^2.4.0",
        "@tauri-apps/plugin-shell": "^2.2.1",
        "@tauri-apps/plugin-window-state": "^2.2.2",
+
        "overlayscrollbars": "^2.11.4",
+
        "overlayscrollbars-svelte": "^0.5.5",
        "zod": "^3.24.4"
      },
      "devDependencies": {
@@ -78,7 +80,6 @@
      "version": "2.3.0",
      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
-
      "dev": true,
      "dependencies": {
        "@jridgewell/gen-mapping": "^0.3.5",
        "@jridgewell/trace-mapping": "^0.3.24"
@@ -708,7 +709,6 @@
      "version": "0.3.8",
      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
      "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
-
      "dev": true,
      "dependencies": {
        "@jridgewell/set-array": "^1.2.1",
        "@jridgewell/sourcemap-codec": "^1.4.10",
@@ -722,7 +722,6 @@
      "version": "3.1.2",
      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
-
      "dev": true,
      "engines": {
        "node": ">=6.0.0"
      }
@@ -731,7 +730,6 @@
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
-
      "dev": true,
      "engines": {
        "node": ">=6.0.0"
      }
@@ -739,14 +737,12 @@
    "node_modules/@jridgewell/sourcemap-codec": {
      "version": "1.5.0",
      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
-
      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
-
      "dev": true
+
      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
    },
    "node_modules/@jridgewell/trace-mapping": {
      "version": "0.3.25",
      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
-
      "dev": true,
      "dependencies": {
        "@jridgewell/resolve-uri": "^3.1.0",
        "@jridgewell/sourcemap-codec": "^1.4.14"
@@ -1142,7 +1138,6 @@
      "version": "1.0.5",
      "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
      "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
-
      "dev": true,
      "peerDependencies": {
        "acorn": "^8.9.0"
      }
@@ -1448,8 +1443,7 @@
    "node_modules/@types/estree": {
      "version": "1.0.7",
      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
-
      "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
-
      "dev": true
+
      "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
    },
    "node_modules/@types/hast": {
      "version": "3.0.4",
@@ -1850,7 +1844,6 @@
      "version": "8.14.1",
      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
      "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
-
      "dev": true,
      "bin": {
        "acorn": "bin/acorn"
      },
@@ -1908,7 +1901,6 @@
      "version": "5.3.2",
      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
      "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
-
      "dev": true,
      "engines": {
        "node": ">= 0.4"
      }
@@ -1943,7 +1935,6 @@
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
      "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
-
      "dev": true,
      "engines": {
        "node": ">= 0.4"
      }
@@ -2182,7 +2173,6 @@
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
-
      "dev": true,
      "engines": {
        "node": ">=6"
      }
@@ -2681,8 +2671,7 @@
    "node_modules/esm-env": {
      "version": "1.2.2",
      "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
-
      "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
-
      "dev": true
+
      "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="
    },
    "node_modules/espree": {
      "version": "10.3.0",
@@ -2717,7 +2706,6 @@
      "version": "1.4.6",
      "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.6.tgz",
      "integrity": "sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==",
-
      "dev": true,
      "dependencies": {
        "@jridgewell/sourcemap-codec": "^1.4.15"
      }
@@ -3554,7 +3542,6 @@
      "version": "3.0.3",
      "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
      "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
-
      "dev": true,
      "dependencies": {
        "@types/estree": "^1.0.6"
      }
@@ -3742,8 +3729,7 @@
    "node_modules/locate-character": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
-
      "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
-
      "dev": true
+
      "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="
    },
    "node_modules/locate-path": {
      "version": "6.0.0",
@@ -3791,7 +3777,6 @@
      "version": "0.30.17",
      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
-
      "dev": true,
      "dependencies": {
        "@jridgewell/sourcemap-codec": "^1.5.0"
      }
@@ -4101,6 +4086,22 @@
        "node": ">= 0.8.0"
      }
    },
+
    "node_modules/overlayscrollbars": {
+
      "version": "2.11.4",
+
      "resolved": "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-2.11.4.tgz",
+
      "integrity": "sha512-GKYQo3OZ1QWnppNjQVv5hfpn+glYUxc6+ufW+ivdXUyLWFNc01XoH2Z36KGM4I8e5pXYeA3ElNItcXiLvmUhnQ==",
+
      "license": "MIT"
+
    },
+
    "node_modules/overlayscrollbars-svelte": {
+
      "version": "0.5.5",
+
      "resolved": "https://registry.npmjs.org/overlayscrollbars-svelte/-/overlayscrollbars-svelte-0.5.5.tgz",
+
      "integrity": "sha512-+dRW3YZSvFbKi5vDCpnUOHuoPLLSdu0BUVVMYZdmfVghu7XkafDRebG2y91/ImPqj6YDAUsz1rcWVYhCJSS/pQ==",
+
      "license": "MIT",
+
      "peerDependencies": {
+
        "overlayscrollbars": "^2.0.0",
+
        "svelte": "^5.0.0"
+
      }
+
    },
    "node_modules/p-limit": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -4957,7 +4958,6 @@
      "version": "5.28.2",
      "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.28.2.tgz",
      "integrity": "sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==",
-
      "dev": true,
      "dependencies": {
        "@ampproject/remapping": "^2.3.0",
        "@jridgewell/sourcemap-codec": "^1.5.0",
@@ -5608,8 +5608,7 @@
    "node_modules/zimmerframe": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
-
      "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
-
      "dev": true
+
      "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="
    },
    "node_modules/zod": {
      "version": "3.24.4",
modified package.json
@@ -31,6 +31,8 @@
    "@tauri-apps/plugin-log": "^2.4.0",
    "@tauri-apps/plugin-shell": "^2.2.1",
    "@tauri-apps/plugin-window-state": "^2.2.2",
+
    "overlayscrollbars": "^2.11.4",
+
    "overlayscrollbars-svelte": "^0.5.5",
    "zod": "^3.24.4"
  },
  "devDependencies": {
modified public/index.css
@@ -22,14 +22,17 @@ body {
  background-color: var(--color-background-default);
}

-
::-webkit-scrollbar {
-
  width: 6px;
-
  height: 6px;
-
  background: transparent;
-
}
-

-
::-webkit-scrollbar-thumb {
-
  background: rgba(128, 128, 128, 0.5);
+
:root {
+
  .os-theme-radicle {
+
    --os-size: 16px;
+
    --os-handle-border-radius: 4px;
+
    --os-padding-perpendicular: 4px;
+
    --os-padding-axis: 2px;
+
    --os-track-bg: var(--color-background-default);
+
    --os-handle-bg: var(--color-fill-ghost);
+
    --os-handle-bg-hover: var(--color-fill-ghost);
+
    --os-handle-bg-active: var(--color-fill-ghost);
+
  }
}

::selection {
modified src/components/Border.svelte
@@ -11,6 +11,7 @@
      | "danger"
      | "success"
      | "outline";
+
    innerElement?: HTMLElement;
    hoverable?: boolean;
    onclick?: (e: MouseEvent) => void;
    stylePosition?: string;
@@ -33,10 +34,12 @@
    styleJustifyContent?: string;
  }

-
  const {
+
  /* eslint-disable prefer-const */
+
  let {
    children,
    variant,
    hoverable = false,
+
    innerElement = $bindable(),
    onclick,
    stylePadding,
    styleHeight,
@@ -257,6 +260,7 @@
  <div class="p3-2"></div>
  <div
    class="p3-3"
+
    bind:this={innerElement}
    style:max-height={styleMaxHeight}
    style:min-width={styleMinWidth}
    style:display={styleDisplay}
modified src/components/InboxButton.svelte
@@ -4,6 +4,7 @@
  import { getCurrentWindow } from "@tauri-apps/api/window";

  import { onMount } from "svelte";
+
  import { useOverlayScrollbars } from "overlayscrollbars-svelte";

  import { dynamicInterval } from "@app/lib/interval";
  import { invoke } from "@app/lib/invoke";
@@ -20,6 +21,7 @@

  let { notificationCount }: Props = $props();

+
  let borderComponent: HTMLElement | undefined = $state();
  let notificationPopoverExpaneded: boolean = $state(false);
  let buttonActive: boolean = $state(false);

@@ -29,6 +31,19 @@
    }
  });

+
  $effect(() => {
+
    if (borderComponent) {
+
      const [initialize] = useOverlayScrollbars({
+
        options: () => ({
+
          scrollbars: { theme: "os-theme-radicle", autoHide: "scroll" },
+
        }),
+
        defer: true,
+
      });
+

+
      initialize({ target: borderComponent });
+
    }
+
  });
+

  onMount(async () => {
    await loadCounter();
  });
@@ -128,6 +143,7 @@

  {#snippet popover()}
    <Border
+
      bind:innerElement={borderComponent}
      variant="ghost"
      styleWidth="40rem"
      stylePadding="1rem"
modified src/components/Sidebar.svelte
@@ -111,7 +111,7 @@
  {/if}
</div>

-
<div>
+
<div style:z-index="20">
  <NakedButton
    styleHeight="2.5rem"
    variant="ghost"
modified src/views/repo/Layout.svelte
@@ -27,7 +27,7 @@
  import type { Snippet } from "svelte";
  import type { Config } from "@bindings/config/Config";

-
  import { onMount } from "svelte";
+
  import { OverlayScrollbarsComponent } from "overlayscrollbars-svelte";

  import Header from "@app/components/Header.svelte";

@@ -57,43 +57,8 @@
    breadcrumbs,
  }: Props = $props();

-
  let contentContainer: HTMLElement | undefined = $state();
-
  let secondColumnContainer: HTMLElement | undefined = $state();
  let loadingContent = false;
  let loadingSecondColumn = false;
-

-
  onMount(() => {
-
    if (contentContainer && loadMoreContent) {
-
      contentContainer.addEventListener("scroll", async () => {
-
        if (
-
          contentContainer &&
-
          contentContainer.scrollTop + contentContainer.clientHeight >=
-
            contentContainer.scrollHeight / 2 &&
-
          loadingContent === false
-
        ) {
-
          loadingContent = true;
-
          void loadMoreContent().finally(() => (loadingContent = false));
-
        }
-
      });
-
    }
-

-
    if (secondColumnContainer && loadMoreSecondColumn) {
-
      secondColumnContainer.addEventListener("scroll", async () => {
-
        if (
-
          secondColumnContainer &&
-
          secondColumnContainer.scrollTop +
-
            secondColumnContainer.clientHeight >=
-
            secondColumnContainer.scrollHeight / 2 &&
-
          loadingSecondColumn === false
-
        ) {
-
          loadingSecondColumn = true;
-
          void loadMoreSecondColumn().finally(
-
            () => (loadingSecondColumn = false),
-
          );
-
        }
-
      });
-
    }
-
  });
</script>

<style>
@@ -119,19 +84,13 @@
    justify-content: space-between;
  }

-
  .secondColumn {
+
  :global(.secondColumn) {
+
    z-index: 10;
    grid-column: 2 / 3;
    max-width: 29rem;
    min-width: 14rem;
    padding: 1rem 1rem 1rem 0;
  }
-

-
  .content {
-
    grid-column: 3 / 4;
-
    width: 100%;
-
    overflow: scroll;
-
    overscroll-behavior: none;
-
  }
</style>

<div class="layout">
@@ -148,18 +107,62 @@
    </div>
  {/if}

-
  <div
+
  <OverlayScrollbarsComponent
+
    element="div"
    class="secondColumn"
-
    style:padding-left={hideSidebar ? "1rem" : "0"}
-
    bind:this={secondColumnContainer}
-
    style:display={oneColumnLayout && !hideSidebar ? "none" : undefined}
-
    style:overflow={styleSecondColumnOverflow}>
+
    style={`padding-left: ${hideSidebar ? "1rem" : "0"}; ${oneColumnLayout && !hideSidebar ? "display: none;" : ""}; overflow: ${styleSecondColumnOverflow}`}
+
    events={{
+
      scroll: instance => {
+
        const secondColumnContainer = instance.elements().target;
+
        if (
+
          loadMoreSecondColumn &&
+
          secondColumnContainer.scrollTop +
+
            secondColumnContainer.clientHeight >=
+
            secondColumnContainer.scrollHeight / 2 &&
+
          loadingSecondColumn === false
+
        ) {
+
          loadingSecondColumn = true;
+
          void loadMoreSecondColumn().finally(
+
            () => (loadingSecondColumn = false),
+
          );
+
        }
+
      },
+
    }}
+
    options={{
+
      overflow: { x: "visible" },
+
      scrollbars: {
+
        theme: "os-theme-radicle",
+
        autoHide: "scroll",
+
      },
+
    }}
+
    defer>
    {@render secondColumn()}
-
  </div>
+
  </OverlayScrollbarsComponent>

-
  <div
-
    class="content global-reset-scroll-after-navigate"
-
    bind:this={contentContainer}>
+
  <OverlayScrollbarsComponent
+
    element="div"
+
    events={{
+
      scroll: instance => {
+
        const contentContainer = instance.elements().target;
+
        if (
+
          loadMoreContent &&
+
          contentContainer.scrollTop + contentContainer.clientHeight >=
+
            contentContainer.scrollHeight / 2 &&
+
          loadingContent === false
+
        ) {
+
          loadingContent = true;
+
          void loadMoreContent().finally(() => (loadingContent = false));
+
        }
+
      },
+
    }}
+
    style="grid-column: 3/4; width: 100%;"
+
    options={{
+
      scrollbars: {
+
        theme: "os-theme-radicle",
+
        autoHide: "scroll",
+
      },
+
    }}
+
    defer>
    {@render children()}
-
  </div>
+
  </OverlayScrollbarsComponent>
</div>