GitHub → DeepWiki Button

Adds an "Open in DeepWiki" button on GitHub repo pages, search results, topics, and explore pages

Per 03-04-2026. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name         GitHub → DeepWiki Button
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Adds an "Open in DeepWiki" button on GitHub repo pages, search results, topics, and explore pages
// @author       Michael Farah
// @license      MIT
// @match        https://github.com/*
// @grant        none
// ==/UserScript==


(function () {
  "use strict";

  const BUTTON_STYLE = `
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 10px;
    font-size: 12px;
    font-weight: 500;
    color: #fff;
    background-color: #1a6ae4;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    vertical-align: middle;
    margin-left: 6px;
    line-height: 20px;
  `;

  const RESERVED_OWNERS = new Set([
    "topics",
    "search",
    "settings",
    "orgs",
    "users",
    "explore",
    "trending",
    "marketplace",
    "sponsors",
    "notifications",
    "about",
    "pricing",
    "enterprise",
    "blog",
    "readme",
    "security",
    "contact",
    "features",
    "join",
    "login",
    "pulls",
    "issues",
    "stars",
  ]);

  function getRepoPath(urlPath) {
    const match = urlPath.match(/^\/([^/]+)\/([^/]+)\/?$/);
    if (!match) return null;
    const owner = match[1];
    const repo = match[2];
    if (RESERVED_OWNERS.has(owner.toLowerCase())) return null;
    return `${owner}/${repo}`;
  }

  function makeButton(repoPath) {
    const url = `https://deepwiki.com/${repoPath}`;
    const btn = document.createElement("a");
    btn.href = url;
    btn.target = "_blank";
    btn.rel = "noopener noreferrer";
    btn.setAttribute("style", BUTTON_STYLE);
    btn.className = "deepwiki-btn-injected";
    btn.innerHTML = `
      <svg width="12" height="12" viewBox="0 0 16 16" fill="currentColor">
        <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38
          0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13
          -.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66
          .07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15
          -.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82a7.65 7.65 0 0 1 2-.27
          c.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12
          .51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48
          0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
      </svg>
      DeepWiki
    `;
    btn.title = `Open on DeepWiki: ${url}`;
    return btn;
  }

  // ── Repo page ──────────────────────────────────────────────────────────────
  function addButtonToRepoPage() {
    const repoPath = getRepoPath(location.pathname);
    if (!repoPath) return;
    if (document.getElementById("deepwiki-repo-btn")) return;

    const heading = document.querySelector('[itemprop="name"] a, h1.d-flex a');
    if (heading) {
      const btn = makeButton(repoPath);
      btn.id = "deepwiki-repo-btn";
      heading.parentElement.appendChild(btn);
    }
  }

  // ── Shared helper: inject buttons next to a set of anchor elements ─────────
  function injectButtonsForLinks(links) {
    links.forEach((link) => {
      const href = link.getAttribute("href") || "";
      const repoPath = getRepoPath(href);
      if (!repoPath) return;
      if (link.parentElement.querySelector(".deepwiki-btn-injected")) return;
      link.parentElement.appendChild(makeButton(repoPath));
    });
  }

  // ── Search results page ────────────────────────────────────────────────────
  function addButtonsToSearchResults() {
    const links = document.querySelectorAll(
      'div[data-testid="results-list"] a[href^="/"], ' +
        "li.repo-list-item a.v-align-middle, " +
        "div.search-title a"
    );
    injectButtonsForLinks(links);
  }

  // ── Topics page  (github.com/topics/<name>) ────────────────────────────────
  function addButtonsToTopicsPage() {
    // Each repo card is an <article>; find all of them
    document.querySelectorAll("article.border").forEach((card) => {
      // Already injected
      if (card.querySelector(".deepwiki-btn-injected")) return;

      const links = card.querySelectorAll('h3 a[href^="/"]');
      if (links.length < 2) return;

      // The second link contains the full repository path (e.g., "/rust-lang/rust")
      const repoPath = getRepoPath(links[1].getAttribute("href"));

      // If the path is invalid or belongs to a reserved owner, skip it
      if (!repoPath) return;

      const btn = makeButton(repoPath);

      // Append button after the h3 heading, inside the card
      const heading = card.querySelector("h3");
      if (heading) heading.appendChild(btn);
    });
  }

  // ── Explore page  (github.com/explore) ────────────────────────────────────
  function addButtonsToExplorePage() {
    const links = document.querySelectorAll(
      'article a[href^="/"][data-ga-click], ' +
        'div.explore-content a.text-bold[href^="/"]'
    );
    injectButtonsForLinks(links);
  }

  // ── Router ─────────────────────────────────────────────────────────────────
  function run() {
    const path = location.pathname;
    const search = location.search;

    if (path.startsWith("/search") || search.includes("type=repositories")) {
      addButtonsToSearchResults();
    } else if (path.startsWith("/topics/") || path === "/topics") {
      // /topics/<name> — list of repos tagged with that topic
      // Note: /topics itself is just a browse page with no repo cards, but
      // running the injector is harmless; it simply won't find matching links.
      addButtonsToTopicsPage();
    } else if (path.startsWith("/explore")) {
      addButtonsToExplorePage();
    } else {
      addButtonToRepoPage();
    }
  }

  run();

  // Debounced observer — avoids hammering run() on every tiny DOM mutation
  let debounceTimer;
  const observer = new MutationObserver(() => {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(run, 300);
  });
  observer.observe(document.body, { childList: true, subtree: true });
})();