Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions AUDIT_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

This log tracks all significant changes, updates, and versions in the PaperCache project.

## 2026-06-29 (Code Quality Cleanup)
**Change:** refactor: code quality cleanup — dead code, boilerplate, types, constants, AI comments; fix: address PR review findings — listener leak, type contracts, dead ref, stale guard, cfg scope, shortcut loop, timer constant

**Details/Why:**
1. **Dead Code Removal**: Removed `resumeTimer` no-op stub from useTimerStore; removed `onSwipeGesture` (ignored callback) from api.ts and types; removed `themePreset`/`setThemePreset` from useAppStore (duplicated in useSettingsStore, all consumers used the latter); removed `prevNotesRef` from useReminders (assigned but never read).
2. **Boilerplate Consolidation**: `useAppStore.ts` — consolidated 9 setters into `booleanSetter`/`simpleSetter` helpers; `api.ts` — extracted 5×8-line identical listener patterns into shared `onEvent` helper; `KeybindsModal.tsx` — replaced 9 parallel `useState`/`getShortcut` calls with data-driven config array; `useSettingsStore.ts` — removed 11 redundant individual setters (use `setSettings` instead).
3. **Rust Fixes**: Fixed clippy `needless_borrows_for_generic_args` in `notifications.rs`; added doc-commented `[lints.rust] unexpected_cfgs = "allow"` for objc crate macro warnings.
4. **Type Safety**: `any` → typed `GraphControls` interface in GraphView; `Promise<unknown>` → properly typed `openAIChat` response; replaced unsafe `as` casts with wrapper functions; `pauseShortcuts`/`resumeShortcuts` changed to return `Promise<void>`.
5. **Magic Numbers → Named Constants**: Extracted ~25 magic numbers across the codebase (z-indices, timeouts, force params, debounce intervals, canvas dimensions, etc.).
6. **Comment Cleanup**: Removed ~15 pedagogical/AI-generated comments.
7. **PR Review Fixes**: Fixed `onEvent` listener leak (added `disposed` flag); fixed stale-token guard in `useReminders` to gate before backend call; fixed `openAIChat` response validation for missing content; removed dead `searchInputRef`/`useEffect` in App.tsx; narrowed `unexpected_cfgs` suppression; made KeybindsModal global shortcut loop data-driven via config; aligned initial timer tick constant in TimersPage.

**Files changed:** `src/store/useTimerStore.ts`, `src/api.ts`, `src/types.d.ts`, `src/setupTests.ts`, `src/store/useAppStore.ts`, `src/store/useAppStore.test.ts`, `src/store/useSettingsStore.ts`, `src/hooks/useReminders.ts`, `src/components/KeybindsModal.tsx`, `src/components/TimersPage.tsx`, `src/GraphView.tsx`, `src/App.tsx`, `src/lib/editor/extensions.ts`, `src/lib/editor/MathEvaluator.ts`, `src/lib/editor/VariableScope.ts`, `src/components/Editor.tsx`, `src-tauri/src/commands/notifications.rs`, `src-tauri/Cargo.toml`, `src-tauri/src/lib.rs`, `src-tauri/src/macos.rs`, `CHANGELOG.md`, `AUDIT_LOG.md`.

---

## 2026-06-28 (v0.5.6 Release: Keybinds Modal, Shortcut Mappings, Timer Auto-Delete, and Graph Link Refinement)
**Change:** chore(release): bump version to 0.5.6; feat(shortcuts): add dedicated keybinds settings modal and update global hotkeys (`Cmd+R` for tasks, `Cmd+T` for timers); feat(timers): auto-delete expired timers after 5 seconds; feat(graph): support standard markdown links and wikilinks

Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Updated Default Shortcut Mappings**: Shifted the Reminders/Tasks view shortcut to `Cmd+R` and Timers panel shortcut to `Cmd+T`.
- **Timer Auto-Deletion**: Expired countdown timers are now automatically removed from the active list 5 seconds after completing, keeping the UI clean. Timer completion notifications and auto-cleanup now function globally even when the Timers panel is closed.
- **Enhanced Graph View Link Parsing**: Extended 3D Graph View link detection to support standard markdown links (`[Note](Note.md)`) and wikilinks (`[[Note]]`) alongside the existing `/file` syntax, and added z-axis forces for improved 3D layout stability.
- **Code Quality Cleanup**: Consolidated repetitive boilerplate across stores, event listeners, and UI components; extracted ~25 magic numbers into named constants; removed dead code and pedagogical AI-style comments.
- **Improved Type Safety**: Replaced `any` with typed interfaces in GraphView; properly typed `openAIChat` API response; aligned async method return types across bridge API.
- **Rust Lint Cleanup**: Fixed clippy warnings in notifications.rs; documented suppressions for legacy objc crate macro warnings.

## [v0.5.5] - 2026-06-27

Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,17 @@ Built with Tauri, Rust, React, TypeScript, and Vite.

## Shortcuts

All shortcuts are fully customizable via Settings → Keybinds (`Cmd+Shift+S`). Defaults:

| Shortcut | Action |
| ------------- | ---------------------------------------- |
| `Cmd+Shift+C` | Toggle visibility (global, configurable) |
| `Cmd+Shift+N` | New note (global, configurable) |
| `Cmd+Shift+S` | Open settings panel |
| `Cmd+Shift+S` | Open keybinds settings modal |
| `Cmd+N` | New note (in-app) |
| `Cmd+/` | Open shortcuts reference |
| `Cmd+T` | Open Tasks page |
| `Cmd+R` | Open Tasks & Reminders page |
| `Cmd+T` | Open Timers page |
| `Cmd+K` | Main action menu |
| `Cmd+P` | Search notes |
| `Cmd+G` | Graph view |
Expand Down
5 changes: 3 additions & 2 deletions features.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This document outlines every feature available in the PaperCache codebase, organ
- **Date & Time Formats**: Highlights standard date (`DD-MM-YYYY` or `YYYY-MM-DD`) and time (`HH:MM` or `HH:MM:SS`) formats into clean, distinct pills.
- **Slash Command Autosuggest**: Type `/` to trigger an inline ghost-text autosuggest for commands like `/check`, `/task`, `/ai`, or `/ctx`. Press `Tab` to instantly complete the command without breaking your typing flow.
- **Interactive Checkboxes**: Type `/check` to create an interactive checkbox widget. Clicking it changes it to `/checked` and visually strikes through the text on that line!
- **Tasks & Reminders**: Type `/task` to create a task widget. Add a space followed by `@` and a time (like `1d2h`, `tmrw`, or a specific date `YYYY-MM-DD HH:MM`) to set a due date. Press `Cmd+T` (or `Ctrl+T`) to open the Tasks Page, which tracks all tasks, calculates due times, and highlights overdue tasks in red.
- **Tasks & Reminders**: Type `/task` to create a task widget. Add a space followed by `@` and a time (like `1d2h`, `tmrw`, or a specific date `YYYY-MM-DD HH:MM`) to set a due date. Press `Cmd+T` (or `Ctrl+T`) to open the Tasks Page, which tracks all tasks, calculates due times, and highlights overdue tasks in red. Expired timers are automatically removed from the list after 5 seconds to keep the UI clean.
- **Customizable Theming & Fonts**: Customize fonts, text colors, background colors, background images, and individual highlight colors for variables, AI, and math. Supports full dark mode (`grid-dark`, `blueprint`).

## Math, Variables, and Calculations
Expand All @@ -28,6 +28,7 @@ This document outlines every feature available in the PaperCache codebase, organ
- **Interactive Graph View** (`Cmd+G`): An interactive 2D knowledge graph rendered with Three.js WebGL. Nodes are clean flat circles with always-visible labels, edges are colored by opacity. Features:
- **Folder Clustering**: Notes in the same folder are gently attracted toward a shared centroid, creating subtle visual groupings.
- **Cmd+F Fuzzy Search**: Press `Cmd+F` inside graph view to fuzzy-search note names. Navigate with arrow keys, press Enter to fly the camera directly to the matched node.
- **Multi-Format Link Detection**: Automatically detects connections via standard markdown links (`[Note](Note.md)`), wikilinks (`[[Note]]`), and `/file Note` syntax.
- **Drag to Rearrange**: Nodes can be dragged freely; positions are cached and restored across graph sessions.
- **Smooth Fade-in**: The graph overlay animates in with a 250ms fade.
- **Lazy-Loaded**: The Three.js bundle (~1.3 MB) loads only when the graph is first opened, keeping startup fast.
Expand All @@ -49,7 +50,7 @@ This document outlines every feature available in the PaperCache codebase, organ
- **Stealth / Background Mode**: Click away or lose focus, and the app instantly hides itself (macOS) or after a brief debounce (Windows/Linux — prevents accidental hide when dragging the title bar). On macOS, it runs as an "accessory" and hides its dock icon completely, acting like a true floating utility.
- **Intelligent Multi-Monitor Support**: When summoning the app via its global hotkey, it detects the active screen your mouse is currently on and brings the window instantly to that specific screen's workspace.
- **System Tray Icon**: A minimal system tray icon for toggling visibility or quitting the app cleanly, adapting to the user's OS theme (light/dark).
- **Global Hotkeys**:
- **Global Hotkeys**: All shortcuts are fully customizable via Settings → Keybinds (`Cmd+Shift+S`). The keybinds modal lets you remap every action with live recording. Defaults include:
- `Cmd+Shift+N` (configurable): Spawn a new note from anywhere. If the app is already open, creates the note without hiding.
- `Cmd+Shift+C` (configurable): Toggle PaperCache visibility from anywhere on your OS.
- **State Memory**: Memorizes precise window coordinates, dimensions, and zoom levels across launches to persist workspace state.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,12 @@ tauri-plugin-notification = "2.0.0-rc.5"
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25"
objc = "0.2"

# The objc v0.2 crate macros use cfg(cargo-clippy) which is no longer recognized,
# producing unexpected_cfgs warnings from external macro expansions that cannot
# be suppressed per-function or per-module (the span originates in objc crate code).
[lints.rust]
unexpected_cfgs = "allow"



2 changes: 1 addition & 1 deletion src-tauri/src/commands/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub async fn schedule_timer(
.notification()
.builder()
.title("PaperCache Timer")
.body(&format!("⏱ Timer finished: {}", label))
.body(format!("⏱ Timer finished: {}", label))
.show();
let _ = app_clone.emit("timer-complete", &id_clone);
});
Expand Down
9 changes: 7 additions & 2 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ mod commands;
mod macos;
mod tray;

#[allow(dead_code)]
const FOCUS_LOSS_DEBOUNCE_MS: u64 = 200;
#[allow(dead_code)]
const WINDOW_STATE_RESTORE_DELAY_MS: u64 = 300;


use commands::shortcuts::GlobalShortcutState;
use commands::notifications::NotificationState;
Expand Down Expand Up @@ -87,7 +92,7 @@ pub fn run() {
let dialog_open = is_dialog_open.clone();
std::thread::spawn(move || {
std::thread::sleep(
std::time::Duration::from_millis(200),
std::time::Duration::from_millis(FOCUS_LOSS_DEBOUNCE_MS),
);
if g2.load(Ordering::SeqCst) == gen_at_spawn
&& !dialog_open.load(Ordering::SeqCst)
Expand All @@ -109,7 +114,7 @@ pub fn run() {
// Plugin's on_window_ready fires too early for available_monitors() on macOS.
let win = window.clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(300));
std::thread::sleep(std::time::Duration::from_millis(WINDOW_STATE_RESTORE_DELAY_MS));
let _ = win.clone().run_on_main_thread(move || {
let _ = win.restore_state(StateFlags::POSITION | StateFlags::SIZE);
if let Ok(app_dir) = win.app_handle().path().app_config_dir() {
Expand Down
2 changes: 0 additions & 2 deletions src-tauri/src/macos.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#![allow(unexpected_cfgs)]

#[cfg(target_os = "macos")]
use tauri::{AppHandle, Emitter};

Expand Down
31 changes: 9 additions & 22 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import { NoteTitleBar } from './components/NoteTitleBar'
import { Editor, type EditorRef } from './components/Editor'
import Settings from './Settings'

const TOAST_TIMEOUT_MS = 5000
const MODAL_Z_INDEX = 9999
const KEYBINDS_Z_INDEX = 10000
const TOAST_Z_INDEX = 99999

function App() {
const notes = useAppStore((state) => state.notes)
const setNotes = useAppStore((state) => state.setNotes)
Expand All @@ -36,7 +41,6 @@ function App() {
const setShowTimersView = useAppStore((state) => state.setShowTimersView)
const toasts = useAppStore((state) => state.toasts)
const removeToast = useAppStore((state) => state.removeToast)
const showNoteSearch = useAppStore((state) => state.showNoteSearch)
const setShowMainActionMenu = useAppStore((state) => state.setShowMainActionMenu)
const showSettingsModal = useAppStore((state) => state.showSettingsModal)
const setShowSettingsModal = useAppStore((state) => state.setShowSettingsModal)
Expand All @@ -48,9 +52,6 @@ function App() {

const editorRef = useRef<EditorRef>(null)

const searchInputRef = useRef<HTMLInputElement>(null)

// Custom Hooks
useNoteStorage()
useVariables()
useReminders()
Expand All @@ -63,7 +64,6 @@ function App() {
useAppStore.getState().setIsHyprland(isHyp)
})

// Show a toast before the app auto-restarts for an update
const disposeUpdateReady = window.electronAPI.onUpdateReady(() => {
useAppStore.getState().addToast({
message: '✨ PaperCache updated — restarting in 3 seconds…',
Expand Down Expand Up @@ -94,39 +94,28 @@ function App() {
}
}, [])

// Auto-dismiss toasts after 5 seconds
const toastTimersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(new Map())
useEffect(() => {
const timers = toastTimersRef.current
const currentIds = new Set(toasts.map((t) => t.id))

// Clear timers for removed toasts
for (const [id, timer] of timers) {
if (!currentIds.has(id)) {
clearTimeout(timer)
timers.delete(id)
}
}

// Set timers for new toasts
for (const toast of toasts) {
if (!timers.has(toast.id)) {
timers.set(
toast.id,
setTimeout(() => removeToast(toast.id), 5000)
setTimeout(() => removeToast(toast.id), TOAST_TIMEOUT_MS)
)
}
}
}, [toasts, removeToast])

useEffect(() => {
if (showNoteSearch && searchInputRef.current) {
setTimeout(() => {
searchInputRef.current?.focus()
}, 50)
}
}, [showNoteSearch])

useEffect(() => {
async function checkVersion() {
if (notes.length === 0) return
Expand Down Expand Up @@ -205,13 +194,11 @@ function App() {
const note = currentNotes[idx]
const newContent = note.content.slice(0, from) + insert + note.content.slice(to)

// Side-effects outside of state updater
window.electronAPI.saveNote(note.id, newContent)
if (idx === currentNoteIndex) {
editorRef.current?.dispatch({ changes: { from, to, insert } })
}

// Pure state update
setNotes((prevNotes) =>
prevNotes.map((n) => (n.id === noteId ? { ...n, content: newContent } : n))
)
Expand Down Expand Up @@ -257,7 +244,7 @@ function App() {
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.75)',
backdropFilter: 'blur(5px)',
zIndex: 9999,
zIndex: MODAL_Z_INDEX,
overflow: 'auto',
}}
>
Expand All @@ -276,7 +263,7 @@ function App() {
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.75)',
backdropFilter: 'blur(5px)',
zIndex: 10000,
zIndex: KEYBINDS_Z_INDEX,
overflow: 'auto',
}}
>
Expand All @@ -294,7 +281,7 @@ function App() {
display: 'flex',
flexDirection: 'column',
gap: 8,
zIndex: 99999,
zIndex: TOAST_Z_INDEX,
}}
>
{toasts.map((toast) => (
Expand Down
Loading
Loading