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
11 changes: 6 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,20 @@ jobs:
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: homebrew-tap

- name: Download macOS DMG
- name: Download macOS App
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION=${{ needs.create-tag.outputs.new_version }}
gh release download v$VERSION --pattern "*.dmg" --repo $GITHUB_REPOSITORY
gh release download v$VERSION --pattern "*aarch64.tar.gz" --repo $GITHUB_REPOSITORY

# Calculate SHA256 of the downloaded DMG
DMG_FILE=$(ls *.dmg | head -n 1)
SHA256=$(shasum -a 256 "$DMG_FILE" | awk '{ print $1 }')
# Calculate SHA256 of the downloaded archive
APP_FILE=$(ls *aarch64.tar.gz | head -n 1)
SHA256=$(shasum -a 256 "$APP_FILE" | awk '{ print $1 }')

# Update the rb file
cd homebrew-tap
sed -i '' "s/url .*/url \"https:\/\/github.com\/VariableThe\/PaperCache\/releases\/download\/v$VERSION\/$APP_FILE\"/" Casks/papercache.rb
sed -i '' "s/version \".*\"/version \"$VERSION\"/" Casks/papercache.rb
sed -i '' "s/sha256 arm: * \".*\"/sha256 arm: \"$SHA256\"/" Casks/papercache.rb

Expand Down
58 changes: 28 additions & 30 deletions src-tauri/src/commands/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,38 +46,36 @@ pub fn get_safe_path(id: &str) -> Result<PathBuf, String> {
}
}

fn walk_dir(dir: &Path, notes: &mut Vec<Note>, base_path: &Path) {
if let Ok(entries) = fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
walk_dir(&path, notes, base_path);
} else if path.is_file() {
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
if ext == "md" || ext == "json" {
// Ignore legacy Electron window-state files
if path.file_name().and_then(|n| n.to_str()) == Some("window-state.json") {
continue;
}

if let Ok(content) = fs::read_to_string(&path) {
let metadata = fs::metadata(&path).ok();
let mtime = metadata
.and_then(|m| m.modified().ok())
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
let id = path
.strip_prefix(base_path)
.unwrap_or(&path)
.to_string_lossy()
.to_string();
notes.push(Note { id, content, mtime });
}
}
fn walk_dir(dir: &Path, notes: &mut Vec<Note>, base_path: &Path) -> Result<(), String> {
let entries = fs::read_dir(dir).map_err(|e| e.to_string())?;
for entry in entries {
let entry = entry.map_err(|e| e.to_string())?;
let path = entry.path();
if path.is_dir() {
walk_dir(&path, notes, base_path)?;
} else if path.is_file() {
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
if ext == "md" || ext == "json" {
let content = fs::read_to_string(&path)
.map_err(|e| format!("Failed to read {}: {}", path.display(), e))?;

let metadata = fs::metadata(&path).ok();
let mtime = metadata
.and_then(|m| m.modified().ok())
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_millis() as u64)
.unwrap_or_default();

let id = path
.strip_prefix(base_path)
.unwrap_or(&path)
.to_string_lossy()
.to_string();
notes.push(Note { id, content, mtime });
}
}
}
Ok(())
}

fn clean_empty_parents(file_path: &Path, base: &Path) {
Expand All @@ -104,7 +102,7 @@ fn clean_empty_parents(file_path: &Path, base: &Path) {
pub fn get_notes() -> Result<Vec<Note>, String> {
let base = get_papercache_dir()?;
let mut notes = Vec::new();
walk_dir(&base, &mut notes, &base);
walk_dir(&base, &mut notes, &base)?;
Ok(notes)
}

Expand Down
4 changes: 4 additions & 0 deletions src-tauri/src/commands/keychain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const SERVICE_NAME: &str = "com.variablethe.papercache";
pub fn set_api_key(key: String) -> Result<bool, String> {
let entry = Entry::new(SERVICE_NAME, "openai_api_key")
.map_err(|e| format!("Failed to access keyring: {}", e))?;
if key.is_empty() {
entry.delete_credential().ok();
return Ok(true);
}
entry
.set_password(&key)
.map_err(|e| format!("Failed to set API key: {}", e))?;
Expand Down
24 changes: 2 additions & 22 deletions src-tauri/src/commands/shortcuts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,7 @@ pub fn update_global_shortcut(
app.global_shortcut()
.on_shortcut(shortcut, move |app, _shortcut, event| {
if event.state() == ShortcutState::Pressed {
if let Some(window) = app.get_webview_window("main") {
let is_visible = window.is_visible().unwrap_or(false);
if is_visible {
let _ = window.hide();
} else {
let _ = window.show();
let _ = window.set_focus();
#[cfg(target_os = "macos")]
crate::macos::force_focus();
}
}
crate::commands::system::toggle_window(app);
let _ = app.emit(&format!("trigger-{}", action_clone), ());
}
})
Expand Down Expand Up @@ -83,17 +73,7 @@ pub fn resume_shortcuts(app: AppHandle) -> Result<(), String> {
.global_shortcut()
.on_shortcut(shortcut, move |app, _, event| {
if event.state() == ShortcutState::Pressed {
if let Some(window) = app.get_webview_window("main") {
let is_visible = window.is_visible().unwrap_or(false);
if is_visible {
let _ = window.hide();
} else {
let _ = window.show();
let _ = window.set_focus();
#[cfg(target_os = "macos")]
crate::macos::force_focus();
}
}
crate::commands::system::toggle_window(app);
let _ = app.emit(&format!("trigger-{}", action_clone), ());
}
});
Expand Down
16 changes: 15 additions & 1 deletion src-tauri/src/commands/system.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use tauri::{AppHandle, WebviewWindow};
use tauri::{AppHandle, Manager, WebviewWindow};
use tauri_plugin_opener::OpenerExt;

#[tauri::command]
Expand All @@ -14,6 +14,20 @@ pub fn close_window(window: WebviewWindow) -> Result<(), String> {
Ok(())
}

pub fn toggle_window(app: &AppHandle) {
if let Some(window) = app.get_webview_window("main") {
let is_visible = window.is_visible().unwrap_or(false);
if is_visible {
let _ = window.hide();
} else {
let _ = window.show();
let _ = window.set_focus();
#[cfg(target_os = "macos")]
crate::macos::force_focus();
}
}
}

#[tauri::command]
pub fn quit_app(app: AppHandle) {
app.exit(0);
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod commands;
#[cfg(target_os = "macos")]
mod macos;
mod tray;
mod window_utils;

use commands::shortcuts::GlobalShortcutState;
use std::sync::atomic::{AtomicBool, Ordering};
Expand Down
34 changes: 10 additions & 24 deletions src-tauri/src/tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ pub fn create_tray(app: &App) -> Result<(), Box<dyn std::error::Error>> {
let quit = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&show_hide, &quit])?;

let icon = tauri::image::Image::from_bytes(include_bytes!("../icons/tray.png"))
.expect("Failed to load tray icon");
let icon_result = tauri::image::Image::from_bytes(include_bytes!("../icons/tray.png"));
let icon = match icon_result {
Ok(icon) => icon,
Err(e) => {
eprintln!("Failed to load tray icon: {}", e);
return Ok(());
}
};

TrayIconBuilder::new()
.icon(icon)
Expand All @@ -20,17 +26,7 @@ pub fn create_tray(app: &App) -> Result<(), Box<dyn std::error::Error>> {
.show_menu_on_left_click(false)
.on_menu_event(|app, event| {
if event.id == "show_hide" {
if let Some(window) = app.get_webview_window("main") {
let is_visible = window.is_visible().unwrap_or(false);
if is_visible {
let _ = window.hide();
} else {
let _ = window.show();
let _ = window.set_focus();
#[cfg(target_os = "macos")]
crate::macos::force_focus();
}
}
crate::commands::system::toggle_window(app);
} else if event.id == "quit" {
app.exit(0);
}
Expand All @@ -43,17 +39,7 @@ pub fn create_tray(app: &App) -> Result<(), Box<dyn std::error::Error>> {
} = event
{
let app = tray.app_handle();
if let Some(window) = app.get_webview_window("main") {
let is_visible = window.is_visible().unwrap_or(false);
if is_visible {
let _ = window.hide();
} else {
let _ = window.show();
let _ = window.set_focus();
#[cfg(target_os = "macos")]
crate::macos::force_focus();
}
}
crate::commands::system::toggle_window(&app);
}
})
.build(app)?;
Expand Down
11 changes: 11 additions & 0 deletions src-tauri/src/window_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use tauri::AppHandle;
use tauri::Manager;

pub fn show_and_focus_window(app: &AppHandle) {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.set_focus();
#[cfg(target_os = "macos")]
crate::macos::force_focus();
}
}
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
},
"bundle": {
"active": true,
"targets": ["nsis", "msi", "appimage", "deb", "dmg", "app"],
"targets": ["nsis", "msi", "appimage", "deb", "app"],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
Expand Down
26 changes: 15 additions & 11 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ body {
width: 150px;
}

.settings-btn-container {
}

.open-settings-btn {
background: transparent;
border: none;
Expand All @@ -73,9 +70,6 @@ body {
opacity: 1;
}

.theme-selector {
}

.theme-selector button {
margin-left: 8px;
font-family: var(--font-family);
Expand Down Expand Up @@ -178,7 +172,9 @@ body {
border-radius: 4px;
outline: none;
font-size: 11px;
transition: all 0.2s ease;
transition:
background-color 0.2s,
opacity 0.2s;
width: 120px;
}

Expand All @@ -198,7 +194,9 @@ body {
text-transform: capitalize;
font-size: 11px;
font-weight: 500;
transition: all 0.2s ease;
transition:
background-color 0.2s,
opacity 0.2s;
}

.cm-panel.cm-search button:hover {
Expand Down Expand Up @@ -234,7 +232,9 @@ body {
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
transition:
background-color 0.2s,
opacity 0.2s;
margin-left: 2px;
}

Expand Down Expand Up @@ -705,7 +705,9 @@ body {
vertical-align: middle;
color: transparent;
background-color: transparent;
transition: all 0.2s ease;
transition:
background-color 0.2s,
opacity 0.2s;
user-select: none;
margin-top: -2px;
}
Expand Down Expand Up @@ -746,7 +748,9 @@ body {
vertical-align: middle;
color: transparent;
background-color: transparent;
transition: all 0.2s ease;
transition:
background-color 0.2s,
opacity 0.2s;
user-select: none;
margin-top: -2px;
}
Expand Down
9 changes: 8 additions & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@

export const tauriApi: ElectronAPI = {
// Implemented Phase 2 Commands
getNotes: () => invoke('get_notes'),
getNotes: async () => {
try {
return await invoke('get_notes')
} catch (e) {
console.error('Failed to get notes', e)

Check warning on line 11 in src/api.ts

View workflow job for this annotation

GitHub Actions / ci (windows-latest)

Unexpected console statement

Check warning on line 11 in src/api.ts

View workflow job for this annotation

GitHub Actions / ci (ubuntu-latest)

Unexpected console statement

Check warning on line 11 in src/api.ts

View workflow job for this annotation

GitHub Actions / ci (macos-latest)

Unexpected console statement
return []
}
},
saveNote: (id, content) => invoke('save_note', { id, content }),
readNote: (id) => invoke('read_note', { id }),
deleteNote: (id) => invoke('delete_note', { id }),
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useGlobalHotkey.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect } from 'react'
import { useAppStore } from '../store/useAppStore'
import { getCurrentWindow } from '@tauri-apps/api/window'

export function useGlobalHotkey() {
const setShowMainActionMenu = useAppStore((state) => state.setShowMainActionMenu)
Expand All @@ -24,8 +25,7 @@ export function useGlobalHotkey() {

// Close the app if nothing else was open
if (!state.showNoteSearch && !isRenaming && actionMenuIndex === 0) {
// @ts-expect-error - appWindow is globally injected by Tauri in older setups but we ignore it here
window.appWindow?.hide()
await getCurrentWindow().hide()
}
if (state.showMainActionMenu) {
e.preventDefault()
Expand Down
3 changes: 1 addition & 2 deletions src/hooks/useNoteStorage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useEffect } from 'react'
import { useAppStore } from '../store/useAppStore'
import type { Note } from '../store/useAppStore'

export function useNoteStorage() {
const notes = useAppStore((state) => state.notes)
Expand All @@ -16,7 +15,7 @@ export function useNoteStorage() {
setNotes(loaded)
const lastOpenNoteId = localStorage.getItem('papercache-last-open-note')
if (lastOpenNoteId) {
const idx = loaded.findIndex((n: Note) => n.id === lastOpenNoteId)
const idx = loaded.findIndex((n) => n.id === lastOpenNoteId)
if (idx !== -1) {
setCurrentNoteIndex(idx)
}
Expand Down
Loading
Loading