Radish alpha
r
rad:z4D5UCArafTzTQpDZNQRuqswh3ury
Radicle desktop app
Radicle
Git
Start with hotkey navigation for sidebars
Open did:key:z6MkkfM3...sVz5 opened 1 year ago
11 files changed +96 -19 6ca5f296 4a5b277e
modified package-lock.json
@@ -15,7 +15,8 @@
        "@tauri-apps/plugin-dialog": "^2.2.0",
        "@tauri-apps/plugin-log": "^2.2.0",
        "@tauri-apps/plugin-shell": "^2.2.0",
-
        "@tauri-apps/plugin-window-state": "^2.2.0"
+
        "@tauri-apps/plugin-window-state": "^2.2.0",
+
        "keyux": "^0.11.0"
      },
      "devDependencies": {
        "@eslint/js": "^9.17.0",
@@ -3065,6 +3066,20 @@
        "katex": "cli.js"
      }
    },
+
    "node_modules/keyux": {
+
      "version": "0.11.0",
+
      "resolved": "https://registry.npmjs.org/keyux/-/keyux-0.11.0.tgz",
+
      "integrity": "sha512-kO/Oa7U/npKl/RYsG62bKZkdUlw6CoIALLVbMLI9WsQ91UGXO71TEh9OlYsdaoAiszHvN8Q2SdSJBKJrbUfBaA==",
+
      "funding": [
+
        {
+
          "type": "github",
+
          "url": "https://github.com/sponsors/ai"
+
        }
+
      ],
+
      "engines": {
+
        "node": "^18.0.0 || >=20.0.0"
+
      }
+
    },
    "node_modules/keyv": {
      "version": "4.5.4",
      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
modified package.json
@@ -30,7 +30,8 @@
    "@tauri-apps/plugin-dialog": "^2.2.0",
    "@tauri-apps/plugin-log": "^2.2.0",
    "@tauri-apps/plugin-shell": "^2.2.0",
-
    "@tauri-apps/plugin-window-state": "^2.2.0"
+
    "@tauri-apps/plugin-window-state": "^2.2.0",
+
    "keyux": "^0.11.0"
  },
  "devDependencies": {
    "@eslint/js": "^9.17.0",
modified public/typography.css
@@ -132,6 +132,9 @@ p {
.txt-missing {
  color: var(--color-foreground-dim);
}
+
.txt-hotkey::first-letter {
+
  text-decoration: underline;
+
}
.txt-emoji {
  height: 1em;
  width: 1em;
modified src/components/Border.svelte
@@ -6,6 +6,7 @@
    variant: "primary" | "secondary" | "ghost" | "float" | "danger" | "success";
    hoverable?: boolean;
    onclick?: () => void;
+
    keyShortcuts?: string;
    stylePosition?: string;
    stylePadding?: string;
    styleHeight?: string;
@@ -29,6 +30,7 @@
    variant,
    hoverable = false,
    onclick,
+
    keyShortcuts,
    stylePadding,
    styleHeight,
    styleMinHeight,
@@ -226,6 +228,7 @@
  {onclick}
  role="button"
  tabindex={onclick !== undefined ? 0 : -1}
+
  aria-keyshortcuts={keyShortcuts}
  {style}
  style:min-height={styleMinHeight}
  style:height={styleHeight}>
modified src/components/Button.svelte
@@ -4,6 +4,7 @@
  interface Props {
    children: Snippet;
    variant: "primary" | "secondary" | "ghost" | "success" | "danger";
+
    keyShortcuts?: string;
    onclick?: () => void;
    disabled?: boolean;
    active?: boolean;
@@ -15,6 +16,7 @@
  const {
    children,
    variant,
+
    keyShortcuts,
    onclick = undefined,
    disabled = false,
    active = false,
@@ -372,6 +374,7 @@
  onclick={!disabled ? onclick : undefined}
  role="button"
  tabindex="0"
+
  aria-keyshortcuts={keyShortcuts}
  {title}
  {style}>
  <div class="p1-1"></div>
modified src/components/HomeSidebar.svelte
@@ -68,10 +68,14 @@
          <!-- svelte-ignore a11y_click_events_have_key_events -->
          <!-- svelte-ignore a11y_no_static_element_interactions -->
          <div
+
            aria-keyshortcuts="i"
            class="tab"
            class:active={!activeTab.repo}
            onclick={() => router.push({ resource: "inbox" })}>
-
            <div class="global-flex"><Icon name="inbox" />Inbox</div>
+
            <div class="global-flex">
+
              <Icon name="inbox" />
+
              <span class="txt-hotkey">Inbox</span>
+
            </div>
            <div class="global-counter">
              {sum(Array.from(notificationCount.values()).map(c => c.count))}
            </div>
@@ -104,10 +108,14 @@
        <div
          class="tab"
          style:cursor="pointer"
+
          aria-keyshortcuts="i"
          onclick={() => router.push({ resource: "inbox" })}
          style:color="var(--color-foreground-contrast)"
          style:padding-left="12px">
-
          <div class="global-flex"><Icon name="inbox" />Inbox</div>
+
          <div class="global-flex">
+
            <Icon name="inbox" />
+
            <span class="txt-hotkey">Inbox</span>
+
          </div>
          <div class="global-counter">
            {sum(Array.from(notificationCount.values()).map(c => c.count))}
          </div>
@@ -126,9 +134,13 @@
        <!-- svelte-ignore a11y_no_static_element_interactions -->
        <div
          class="tab txt-small"
+
          aria-keyshortcuts="r"
          class:active={!activeTab.filter}
          onclick={() => router.push({ resource: "home" })}>
-
          <div class="global-flex"><Icon name="repo" />Repositories</div>
+
          <div class="global-flex">
+
            <Icon name="repo" />
+
            <span class="txt-hotkey">Repositories</span>
+
          </div>
          <div class="global-counter">
            {repoCount.total}
          </div>
@@ -137,6 +149,7 @@
        <!-- svelte-ignore a11y_no_static_element_interactions -->
        <div
          class="tab"
+
          aria-keyshortcuts="d"
          class:active={activeTab.filter === "delegate"}
          onclick={() =>
            router.push({
@@ -145,7 +158,7 @@
            })}>
          <div class="global-flex">
            <Icon name="delegate" />
-
            <div>Delegate</div>
+
            <span class="txt-hotkey">Delegate</span>
          </div>
          <div class="global-counter">{repoCount.delegate}</div>
        </div>
@@ -153,6 +166,7 @@
        <!-- svelte-ignore a11y_no_static_element_interactions -->
        <div
          class="tab"
+
          aria-keyshortcuts="c"
          class:active={activeTab.filter === "contributor"}
          onclick={() =>
            router.push({
@@ -161,7 +175,7 @@
            })}>
          <div class="global-flex">
            <Icon name="user" />
-
            <div>Contributor</div>
+
            <span class="txt-hotkey">Contributor</span>
          </div>
          <div class="global-counter">{repoCount.contributor}</div>
        </div>
@@ -169,6 +183,7 @@
        <!-- svelte-ignore a11y_no_static_element_interactions -->
        <div
          class="tab"
+
          aria-keyshortcuts="p"
          class:active={activeTab.filter === "private"}
          onclick={() =>
            router.push({
@@ -177,7 +192,7 @@
            })}>
          <div class="global-flex">
            <Icon name="lock" />
-
            <div>Private</div>
+
            <span class="txt-hotkey">Private</span>
          </div>
          <div class="global-counter">{repoCount.private}</div>
        </div>
@@ -188,6 +203,7 @@
        variant="float">
        <Link
          styleWidth="100%"
+
          keyShortcuts="r"
          underline={false}
          route={{
            resource: "home",
@@ -198,7 +214,7 @@
            class="tab">
            <div class="global-flex">
              <Icon name="repo" />
-
              Repositories
+
              <span class="txt-hotkey">Repositories</span>
            </div>
            <div class="global-counter">
              {repoCount.total}
modified src/components/IssuesSecondColumn.svelte
@@ -68,11 +68,15 @@
      styleGap="2px"
      styleBackgroundColor="var(--color-background-float)">
      <Link
+
        keyShortcuts="i"
        styleWidth="100%"
        underline={false}
        route={{ resource: "repo.issues", rid: repo.rid, status: "all" }}>
        <div class="tab active">
-
          <div class="global-flex"><Icon name="issue" />Issues</div>
+
          <div class="global-flex">
+
            <Icon name="issue" />
+
            <span class="txt-hotkey">Issues</span>
+
          </div>
          <div class="global-counter">
            {project.meta.issues.open + project.meta.issues.closed}
          </div>
@@ -82,6 +86,7 @@
      <Link
        styleWidth="100%"
        underline={false}
+
        keyShortcuts="o"
        route={{
          resource: "repo.issues",
          rid: repo.rid,
@@ -91,7 +96,8 @@
          <div
            class="global-flex"
            class:open={["open", "all"].includes(status)}>
-
            <Icon name="issue" />Open
+
            <Icon name="issue" />
+
            <span class="txt-hotkey">Open</span>
          </div>
          <div class="global-counter" class:highlight={status === "all"}>
            {project.meta.issues.open}
@@ -102,6 +108,7 @@
      <Link
        styleWidth="100%"
        underline={false}
+
        keyShortcuts="c"
        route={{
          resource: "repo.issues",
          rid: repo.rid,
@@ -111,7 +118,8 @@
          <div
            class="global-flex"
            class:closed={["closed", "all"].includes(status)}>
-
            <Icon name="issue-closed" />Closed
+
            <Icon name="issue-closed" />
+
            <span class="txt-hotkey">Closed</span>
          </div>
          <div class="global-counter" class:highlight={status === "all"}>
            {project.meta.issues.closed}
@@ -122,6 +130,7 @@

    <div style:margin-top="0.5rem">
      <Link
+
        keyShortcuts="p"
        styleWidth="100%"
        underline={false}
        route={{ resource: "repo.patches", rid: repo.rid, status: "open" }}>
@@ -129,7 +138,10 @@
          class="tab"
          style:color="var(--color-foreground-contrast)"
          style:padding-left="12px">
-
          <div class="global-flex"><Icon name="patch" />Patches</div>
+
          <div class="global-flex">
+
            <Icon name="patch" />
+
            <span class="txt-hotkey">Patches</span>
+
          </div>
          <div class="global-counter">
            {project.meta.patches.draft +
              project.meta.patches.open +
modified src/components/Link.svelte
@@ -7,6 +7,7 @@
  interface Props {
    children: Snippet;
    route: Route;
+
    keyShortcuts?: string;
    disabled?: boolean;
    underline?: boolean;
    styleWidth?: string;
@@ -16,6 +17,7 @@
  const {
    children,
    route,
+
    keyShortcuts,
    disabled = false,
    underline = true,
    styleWidth,
@@ -46,6 +48,7 @@

<a
  onclick={navigateToRoute}
+
  aria-keyshortcuts={keyShortcuts}
  href={routeToPath(route)}
  class:underline
  style:color={styleColor}
modified src/components/NakedButton.svelte
@@ -4,6 +4,7 @@
  interface Props {
    children: Snippet;
    title?: string;
+
    keyShortcuts?: string;
    disabled?: boolean;
    variant: "primary" | "secondary" | "ghost";
    onclick?: (e: MouseEvent) => void;
@@ -16,6 +17,7 @@
    children,
    title,
    disabled,
+
    keyShortcuts,
    variant,
    onclick,
    styleHeight = "2rem",
@@ -238,6 +240,7 @@
  class:disabled
  class:active
  onclick={!disabled ? onclick : undefined}
+
  aria-keyshortcuts={keyShortcuts}
  {title}
  role="button"
  tabindex="0"
modified src/components/PatchesSecondColumn.svelte
@@ -71,12 +71,16 @@
      <Link
        styleWidth="100%"
        underline={false}
+
        keyShortcuts="i"
        route={{ resource: "repo.issues", rid: repo.rid, status: "open" }}>
        <div
          class="tab"
          style:color="var(--color-foreground-contrast)"
          style:padding-left="12px">
-
          <div class="global-flex"><Icon name="issue" />Issues</div>
+
          <div class="global-flex">
+
            <Icon name="issue" />
+
            <span class="txt-hotkey">Issues</span>
+
          </div>
          <div class="global-counter">
            {project.meta.issues.open + project.meta.issues.closed}
          </div>
@@ -92,9 +96,13 @@
      <Link
        styleWidth="100%"
        underline={false}
+
        keyShortcuts="p"
        route={{ resource: "repo.patches", rid: repo.rid, status: undefined }}>
        <div class="tab active">
-
          <div class="global-flex"><Icon name="patch" />Patches</div>
+
          <div class="global-flex">
+
            <Icon name="patch" />
+
            <span class="txt-hotkey">Patches</span>
+
          </div>
          <div class="global-counter">
            {project.meta.patches.draft +
              project.meta.patches.open +
@@ -107,6 +115,7 @@
      <Link
        styleWidth="100%"
        underline={false}
+
        keyShortcuts="o"
        route={{
          resource: "repo.patches",
          rid: repo.rid,
@@ -117,7 +126,7 @@
            class="global-flex"
            class:open={["open", undefined].includes(status)}>
            <Icon name="patch" />
-
            Open
+
            <span class="txt-hotkey">Open</span>
          </div>
          <div class="global-counter" class:highlight={status === undefined}>
            {project.meta.patches.open}
@@ -128,6 +137,7 @@
      <Link
        styleWidth="100%"
        underline={false}
+
        keyShortcuts="m"
        route={{
          resource: "repo.patches",
          rid: repo.rid,
@@ -137,7 +147,8 @@
          <div
            class="global-flex"
            class:merged={["merged", undefined].includes(status)}>
-
            <Icon name="patch-merged" />Merged
+
            <Icon name="patch-merged" />
+
            <span class="txt-hotkey">Merged</span>
          </div>
          <div class="global-counter" class:highlight={status === undefined}>
            {project.meta.patches.merged}
@@ -148,6 +159,7 @@
      <Link
        styleWidth="100%"
        underline={false}
+
        keyShortcuts="a"
        route={{
          resource: "repo.patches",
          rid: repo.rid,
@@ -157,7 +169,8 @@
          <div
            class="global-flex"
            class:archived={["archived", undefined].includes(status)}>
-
            <Icon name="patch-archived" />Archived
+
            <Icon name="patch-archived" />
+
            <span class="txt-hotkey">Archived</span>
          </div>
          <div class="global-counter" class:highlight={status === undefined}>
            {project.meta.patches.archived}
@@ -165,6 +178,7 @@
        </div>
      </Link>
      <Link
+
        keyShortcuts="d"
        styleWidth="100%"
        underline={false}
        route={{
@@ -177,7 +191,7 @@
            class="global-flex"
            class:draft={["draft", undefined].includes(status)}>
            <Icon name="patch-draft" />
-
            Draft
+
            <span class="txt-hotkey">Draft</span>
          </div>
          <div class="global-counter" class:highlight={status === undefined}>
            {project.meta.patches.draft}
modified src/main.ts
@@ -1,6 +1,10 @@
import { mount } from "svelte";
+
import { hotkeyKeyUX, hotkeyMacCompat, startKeyUX } from "keyux";
import App from "./App.svelte";

const app = mount(App, { target: document.body });

+
const mac = hotkeyMacCompat();
+
startKeyUX(window, [hotkeyKeyUX([mac])]);
+

export default app;