Nasaq

QuickCapture

A capture window opened by a global shortcut to jot a note or clip a page, with tags, a destination and save with Ctrl or Cmd plus Enter.

PreviewOpen ↗

Code

import { QuickCapture } from "@fadymondy/nasaq/web";export function Capture() {  return (    <QuickCapture      shortcut="Mod+Shift+K"      suggestedTags={["idea", "todo"]}      onCapture={async (capture) => {        await api.inbox.add(capture);      }}    />  );}

Productivity · beta

Live examples and controls: QuickCapture in the lab.

Install

npx shadcn@latest add https://docs.nasaqui.com/r/quick-capture.json

Get a thought or a page into the inbox in seconds. A keyboard shortcut opens a small dialog with a focused text box; Ctrl or Cmd plus Enter saves and Escape closes. Pass a page and it becomes the web-clipper popup, showing the page and saving it with your note. Pass presentation="panel" to render only the form, for a floating window, side panel or extension popup.

When to use

  • A notes or task app where capturing must be faster than opening the app and finding the right place.
  • A browser extension popup that saves the current page.
  • A desktop app's floating capture window (render the panel in it).

When not to use

Import

import { QuickCapture } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"

Quick start

import { QuickCapture } from "@fadymondy/nasaq/web";

export function Capture() {
  return (
    <QuickCapture
      shortcut="Mod+Shift+K"
      suggestedTags={["idea", "todo"]}
      onCapture={async (capture) => {
        await api.inbox.add(capture);
      }}
    />
  );
}

Press Ctrl+Shift+K (Cmd+Shift+K on a Mac) anywhere in the page to open it.

Anatomy

QuickCapture              data-slot="quick-capture"  data-presentation="dialog" | "panel"
├─ header                 title and description
├─ page card              data-slot="quick-capture-page"  (when `page` is set)
├─ text box               autofocused, dir="auto"
├─ tags                   #tags found in the text, suggested tag toggles
├─ destinations           radio group (when there are two or more)
├─ status                 "Saved to Inbox" (panel) 
└─ footer                 key hints, Cancel, Save

API

PropTypeDefaultDescription
onCapture(capture: CaptureValue) => void | { error?: string } | Promise<...>requiredSave the capture. Return { error } or throw to keep the text. The dialog closes, or the panel clears, only after it resolves.
open, defaultOpen, onOpenChangeboolean, boolean, (boolean) => voiduncontrolled, closedDialog state. The shortcut toggles it.
presentation"dialog" | "panel""dialog"A dialog over the page, or the bare form. The panel has no shortcut.
shortcutstring | null"Mod+Shift+K"Global toggle. Mod is Cmd on Apple and Ctrl elsewhere. A modifier is required. null turns it off.
shortcutEnabledbooleantrueTurn the shortcut off without unmounting.
page{ title?, url, selection? }noneThe page being clipped. The capture becomes a clip.
destinations, defaultDestinationId{ id, label, icon? }[], stringnoneWhere captures go. A picker appears for two or more.
suggestedTagsstring[]noneTags shown as toggles.
initialText, placeholderstring""Starting text and placeholder.
showHintsbooleantrueThe key hints in the footer.
labelsQuickCaptureLabelsEnglish or ArabicString overrides.

CaptureValue is { kind: "note" \| "link" \| "clip", text, title, url?, pageTitle?, selection?, tags, destinationId?, capturedAt }. Tags come from the toggles and from #tags typed in the text (Latin or Arabic).

Helpers

parseCaptureShortcut, matchesCaptureShortcut, captureShortcutKeys, isCaptureSaveKey, buildCapture, canSaveCapture, extractCaptureTags, extractCaptureUrl. All are pure.

Examples

Web clipper popup:

<QuickCapture
  presentation="panel"
  page={{ title: document.title, url: location.href, selection: String(getSelection()) }}
  destinations={[{ id: "inbox", label: "Inbox" }, { id: "read", label: "Read later" }]}
  onCapture={save}
  onOpenChange={(open) => !open && window.close()}
/>

Controlled, with another trigger:

const [open, setOpen] = useState(false);
<QuickCapture open={open} onOpenChange={setOpen} shortcut={null} onCapture={save} />;

Arabic copy:

<QuickCapture onCapture={save} labels={{ title: "التقاط سريع", placeholder: "ما الذي يدور في ذهنك؟" }} />

Accessibility

KeyAction
Shortcut (default Ctrl or Cmd + Shift + K)Open or close the dialog from anywhere.
Ctrl or Cmd + EnterSave.
EscapeClose the dialog, or call onOpenChange(false) in the panel.
TabMove between the text, tags, destinations and buttons.
  • The dialog is a modal with a title and description; focus starts in the text box and returns to where it was on close.
  • Errors are role="alert" and tied to the text box; the saved message in the panel is a polite status.
  • The shortcut matches the physical key (event.code), so it works on Arabic keyboards. Choose one that does not clash with the browser.

RTL & i18n

  • The text box uses dir="auto". URLs and page addresses are shown left-to-right.
  • Tags accept Arabic letters (#عمل). Key hints stay left-to-right.
  • The dialog and footer mirror with the page direction.

Styling & tokens

Uses the dialog, control and --nq-* selected and focus tokens. Target data-slot="quick-capture" and data-presentation. Pass className to change the width.

Do / Don't

  • Do keep the shortcut listener tied to the mounted component. It is removed on unmount, so it never leaks between pages.
  • Do keep the text when saving fails so nothing typed is lost.
  • Don't take a bare letter as the shortcut. A modifier is required.
  • Don't ask for a title or a folder before saving. Sort later.

Lab

https://docs.nasaqui.com/?path=/docs/components-productivity-quick-capture--docs

On this page