diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index 93a2041..a129ca7 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -1546,7 +1546,7 @@ mod tests { let settings = store.global_capture_get().unwrap(); assert_eq!(settings, GlobalCaptureSettings::default()); - assert!(!settings.enabled); + assert!(settings.enabled); assert_eq!(settings.shortcut, "CommandOrControl+Shift+Space"); assert!(!settings.paused); assert_eq!(settings.default_mode, CaptureMode::QuickNote); diff --git a/crates/store/src/settings.rs b/crates/store/src/settings.rs index 33b994f..f677033 100644 --- a/crates/store/src/settings.rs +++ b/crates/store/src/settings.rs @@ -136,7 +136,7 @@ pub struct CaptureDocRef { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct GlobalCaptureSettings { - #[serde(default)] + #[serde(default = "default_true")] pub enabled: bool, #[serde(default = "default_global_capture_shortcut")] pub shortcut: String, @@ -161,7 +161,7 @@ pub struct GlobalCaptureSettings { impl Default for GlobalCaptureSettings { fn default() -> Self { Self { - enabled: false, + enabled: true, shortcut: default_global_capture_shortcut(), paused: false, default_mode: CaptureMode::default(), diff --git a/docs/roadmap.md b/docs/roadmap.md index 0318582..0545610 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ --- title: "Roadmap" -last_updated: 2026-02-23 +last_updated: 2026-02-24 --- ## Content blocks (transclusion) @@ -62,3 +62,24 @@ Improve file organization 2. **Recovery** - Corrupt settings/workspace → app resets safely - Missing location root → UI prompts to relink/remove + +## Responsiveness adjustments + +Improve responsiveness across desktop widths and small-window usage while keeping editor workflows stable. + +### Tasks + +- **Baseline and guardrails** + - Confirm hotspot map: top bars, fixed overlays/dialogs, status/tabs density, and split/sidebar constraints. + - Define supported width tiers and expected behavior per tier. + - Add a manual viewport checklist for core flows (open doc, edit, search, export, quick capture). +- **Core shell responsiveness** + - Add narrow-width compaction for `AppHeaderBar` and `Toolbar` (wrapping/collapsing with clear priority rules). + - Tune sidebar and split-pane constraints with graceful fallback when split is not feasible. + - Use react motion for smooth transitions and animations. + - Introduce predictable truncation and information priority for tabs and status bar. +- Create Dialog primitive that can be used for all overlays and dialogs & modals + - Convert layout/search overlays from fixed desktop assumptions to responsive sheet/panel behavior. + - Rework backend alert anchoring/stacking to avoid clipping at narrow widths. + - Improve PDF export dialog sizing/scroll behavior for constrained viewports. +- Document responsiveness rules so future UI changes follow the same standards. diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 6cb3880..dbb7e9d 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -1,8 +1,8 @@ { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", - "description": "Capability for the main window", - "windows": ["main"], + "description": "Capability for main and quick capture windows", + "windows": ["main", "quick_capture"], "permissions": [ "core:default", "log:default", diff --git a/src-tauri/src/capture.rs b/src-tauri/src/capture.rs index 8b7e1a8..b01a9d5 100644 --- a/src-tauri/src/capture.rs +++ b/src-tauri/src/capture.rs @@ -27,8 +27,9 @@ pub fn ensure_quick_capture_window(app: &AppHandle) -> Result void; ); +const DEFAULT_GLOBAL_CAPTURE_SETTINGS: GlobalCaptureSettings = { + enabled: true, + shortcut: "CommandOrControl+Shift+Space", + paused: false, + defaultMode: "QuickNote", + targetLocationId: null, + inboxRelativeDir: "inbox", + appendTarget: null, + closeAfterSave: true, + showTrayIcon: true, + lastCaptureTarget: null, +}; + function App() { const { model: editorModel, dispatch: editorDispatch, openDoc } = useEditor(); const { model: previewModel, render: renderPreview, syncLine: syncPreviewLine, setDoc: setPreviewDoc } = usePreview(); @@ -54,6 +76,9 @@ function App() { const [isLayoutSettingsOpen, setIsLayoutSettingsOpen] = useState(false); const [isPdfExportDialogOpen, setIsPdfExportDialogOpen] = useState(false); const [layoutSettingsHydrated, setLayoutSettingsHydrated] = useState(false); + const [globalCaptureSettings, setGlobalCaptureSettings] = useState( + DEFAULT_GLOBAL_CAPTURE_SETTINGS, + ); useWorkspaceSync(); useLayoutHotkeys(); @@ -272,6 +297,21 @@ function App() { setIsLayoutSettingsOpen(false); }, []); + const handleQuickCaptureEnabledChange = useCallback((enabled: boolean) => { + setGlobalCaptureSettings((prev) => { + if (prev.enabled === enabled) { + return prev; + } + + const next = { ...prev, enabled }; + void runCmd(globalCaptureSet(next, () => {}, (error) => { + logger.error("Failed to persist quick capture enabled state", error); + setGlobalCaptureSettings(prev); + })); + return next; + }); + }, []); + const focusModePanelProps = useMemo(() => { const pos = pick(editorModel, ["cursorLine", "cursorColumn"]); const stats = { ...pos, wordCount, charCount, selectionCount }; @@ -374,6 +414,16 @@ function App() { }); }, () => {})); + void runCmd(globalCaptureGet((settings) => { + if (isCancelled) { + return; + } + + setGlobalCaptureSettings(settings); + }, (error) => { + logger.error("Failed to load global capture settings", error); + })); + return () => { isCancelled = true; }; @@ -389,6 +439,7 @@ function App() { setTopBarsCollapsed, setCalmUiSettings, setFocusModeSettings, + setGlobalCaptureSettings, ]); useEffect(() => { @@ -464,7 +515,11 @@ function App() { - + { expect(onErr).not.toHaveBeenCalled(); }); + it("defaults global capture to enabled when backend omits the field", async () => { + const onOk = vi.fn(); + const onErr = vi.fn(); + + vi.mocked(invoke).mockResolvedValueOnce({ + type: "ok", + value: { + shortcut: "CommandOrControl+Shift+Space", + paused: false, + default_mode: "QuickNote", + target_location_id: null, + inbox_relative_dir: "inbox", + append_target: null, + close_after_save: true, + show_tray_icon: true, + last_capture_target: null, + }, + }); + + await runCmd(globalCaptureGet(onOk, onErr)); + + expect(onOk).toHaveBeenCalledWith(expect.objectContaining({ enabled: true })); + expect(onErr).not.toHaveBeenCalled(); + }); + it("normalizes global capture submit results from backend snake_case", async () => { const onOk = vi.fn(); const onErr = vi.fn(); diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 6bf2547..b0dae53 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,8 +1,6 @@ -import { type ClassValue, clsx } from "clsx"; +import { cn } from "$utils/tw"; +import { type ClassValue } from "clsx"; import { type ButtonHTMLAttributes, forwardRef } from "react"; -import { twMerge } from "tailwind-merge"; - -const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs)); export type ButtonVariant = | "unstyled" diff --git a/src/components/capture/QuickCaptureApp.tsx b/src/components/capture/QuickCaptureApp.tsx index cdf106a..0115fb3 100644 --- a/src/components/capture/QuickCaptureApp.tsx +++ b/src/components/capture/QuickCaptureApp.tsx @@ -68,7 +68,7 @@ export function QuickCaptureApp() { if (isLoading) { return ( -
+
); diff --git a/src/components/capture/QuickCaptureForm.tsx b/src/components/capture/QuickCaptureForm.tsx index e4f9ffe..9b261ed 100644 --- a/src/components/capture/QuickCaptureForm.tsx +++ b/src/components/capture/QuickCaptureForm.tsx @@ -1,6 +1,7 @@ import { Button } from "$components/Button"; import { XIcon } from "$icons"; import type { CaptureMode } from "$types"; +import { cn } from "$utils/tw"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; type QuickCaptureFormProps = { @@ -35,22 +36,22 @@ const ModeButton = ({ currentMode, setMode, isSubmitting, mode }: ModeButtonProp const modeLabel = useMemo(() => getModeLabel(mode), [mode]); - return ( - + : "bg-field-02 text-text-secondary border-border-subtle hover:bg-field-hover-02 hover:text-text-primary", + ), + [mode, currentMode], ); + + return ; }; const ModeButtons = ({ currentMode, setMode, isSubmitting }: ModeButtonsProps) => ( -
+
{(["QuickNote", "WritingSession", "Append"] as CaptureMode[]).map((m) => ( ))} @@ -58,7 +59,7 @@ const ModeButtons = ({ currentMode, setMode, isSubmitting }: ModeButtonsProps) = ); const ErrorMessage = ({ error }: { error: string | null }) => { - return
{error && {error}}
; + return
{error && {error}}
; }; const FooterActions = ( @@ -69,15 +70,15 @@ const FooterActions = ( text: string; }, ) => ( -
- +
+ {mode === "WritingSession" ? "Cmd+Enter to save, Esc to close" : "Enter to save, Shift+Enter for newline, Esc to close"}