54 lines · 2.2 KB
import { useNavigate, useRouterState } from "@tanstack/react-router";
import { useEffect } from "react";

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}

const REF_ROUTES = new Set(["/$owner/$repo/tree/$", "/$owner/$repo/blob/$", "/$owner/$repo/commits/$"]);
const EDIT_ROUTE = "/$owner/$repo/edit/$";

function loaderRef(data: unknown): string | null {
  if (!isRecord(data)) return null;
  const ref = data.ref;
  if (typeof ref === "string") return ref;
  return isRecord(ref) && typeof ref.name === "string" ? ref.name : null;
}

function loaderPath(data: unknown): string | null {
  return isRecord(data) && typeof data.path === "string" && data.path ? data.path : null;
}

function isTyping(target: EventTarget | null): boolean {
  if (!(target instanceof HTMLElement)) return false;
  return target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName);
}

/** Pressing `.` anywhere in a repository opens the workspace editor on the ref being viewed, like github.dev. */
export function useEditorHotkey(owner: string, repo: string, defaultBranch: string, enabled = true) {
  const navigate = useNavigate();
  const matches = useRouterState({ select: (state) => state.matches });
  const editing = matches.some((match) => match.routeId === EDIT_ROUTE);
  const path =
    matches
      .filter((match) => match.routeId === "/$owner/$repo/blob/$")
      .map((match) => loaderPath(match.loaderData))
      .findLast((value) => value !== null) ?? null;
  const ref =
    matches
      .filter((match) => REF_ROUTES.has(match.routeId))
      .map((match) => loaderRef(match.loaderData))