All files / src/platform Desktop.ts

100% Statements 37/37
100% Branches 12/12
100% Functions 13/13
100% Lines 29/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87            8x 8x 3x     8x     2x 2x   1x       8x       8x               8x 8x         8x 8x 8x 8x   8x       2x             4x       1x       8x       1x 1x   1x               1x 1x 3x 2x 2x   1x    
import { invoke } from '@tauri-apps/api/core';
import { getCurrentWindow } from '@tauri-apps/api/window';
import { watchEditorGlass } from '../desktop/EditorGlass';
import { applyTheme, onMac } from '../desktop/Theme';
import type { CaptureMode } from '../lib/Db';
 
applyTheme();
addEventListener('storage', (event) => {
  if (event.key === 'theme' || event.key === null) applyTheme();
});
// Editor windows follow the main window's Window design on macOS.
if (onMac() && isEditorWindow()) watchEditorGlass();
 
function isEditorWindow() {
  try {
    return getCurrentWindow().label.startsWith('editor-');
  } catch {
    return false;
  }
}
 
export const CAPTURE_MODES: CaptureMode[] = ['selection', 'screen', 'window'];
 
export type Action = 'selection' | 'screen' | 'window' | 'copy-selection';
// Alt is Option on macOS.
export const DEFAULT_SHORTCUTS: Record<Action, string> = {
  selection: 'Alt+Shift+S',
  window: 'Alt+Shift+W',
  screen: 'Alt+Shift+V',
  'copy-selection': 'Alt+Shift+C',
};
 
export const CAPTURE_HINT =
  'Use Capture, the Daguerre icon in the menu bar or system tray, or a shortcut.';
export const SHORTCUT_HINTS: [keys: string, label: string][] = [
  [DEFAULT_SHORTCUTS.selection, 'Select area'],
  [DEFAULT_SHORTCUTS.window, 'Window'],
  [DEFAULT_SHORTCUTS.screen, 'Screen'],
];
export const NO_URL_LABEL = '';
export const SAVE_PNG_LABEL = 'Save PNG…';
export const SAVED_TOAST = 'Saved';
export const SEARCH_PLACEHOLDER = 'Search by title';
 
const bytes = async (blob: Blob) => new Uint8Array(await blob.arrayBuffer());
 
/** Asks where to save, then writes the file; resolves false if the dialog is cancelled. */
export async function downloadBlob(blob: Blob, name: string) {
  return invoke<boolean>('save_file', await bytes(blob), {
    // Header values must be ASCII.
    headers: { 'x-file-name': name.replace(/[^\x20-\x7e]/g, '_') },
  });
}
 
export async function copyPng(png: Blob | Promise<Blob>) {
  await invoke('copy_png', await bytes(await png));
}
 
export async function copyText(text: Promise<string>) {
  await invoke('copy_text', { text: await text });
}
 
// WKWebView ignores window.print(), so the native webview prints itself.
export const printPage = () => invoke('print');
 
/** History lives in the main window: bring it forward and close this editor (through its close guard). */
export function leaveToHistory() {
  void invoke('show_main', { page: 'history' }).then(() =>
    getCurrentWindow().close(),
  );
  return true;
}
 
/** Asks before closing a window with unsaved work; returns a cleanup function. */
export function guardClose(
  isDirty: () => boolean,
  ask: () => Promise<boolean>,
) {
  const win = getCurrentWindow();
  const off = win.onCloseRequested(async (e) => {
    if (!isDirty()) return;
    e.preventDefault();
    if (await ask()) await win.destroy();
  });
  return () => void off.then((unlisten) => unlisten());
}