Overview of Crest
Crest is a powerful, unified search engine and productivity tool designed to streamline your digital workflow. It acts as a central hub for accessing applications, files, web searches, calculations, and more, all through a simple and intuitive interface.
This tutorial will guide you through the core components of Crest, from its global hotkey management to its sophisticated plugin system and state management.
Key Features
- Global Hotkey & Window Management: Instant access to Crest from anywhere in your system.
- Unified Search Engine: A single search bar to find everything you need.
- Data Indexers: Efficiently indexing your local data for lightning-fast retrieval.
- Plugin System: Extend Crest's functionality with custom plugins.
- Tauri IPC Bridge: Secure communication between the frontend and the Rust backend.
- Frontend State Management: A responsive and synchronized user interface powered by Zustand.
Explore the chapters on the left to dive deep into each of these areas and understand how Crest is built and how it works.
Global Hotkey & Window Management
In this chapter, we explore how Crest handles global hotkeys and window management using the Tauri framework. This is the entry point for the user, allowing them to summon Crest instantly with a simple keyboard shortcut.
The Global Hotkey
Crest uses the tauri-plugin-global-shortcut to listen for a specific key combination (e.g., Alt+Space or Cmd+Space). When this shortcut is pressed, the backend receives an event and toggles the visibility of the main window.
Backend Implementation (Rust)
// src-tauri/src/main.rs
// ... imports ...
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.setup(|app| {
let handle = app.handle();
// Register the global shortcut
app.global_shortcut_manager().register("Alt+Space", move || {
let window = handle.get_window("main").unwrap();
if window.is_visible().unwrap() {
window.hide().unwrap();
} else {
window.show().unwrap();
window.set_focus().unwrap();
}
}).unwrap();
Ok(())
})
// ...
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Window Management
Crest's main window is configured to be borderless and transparent, providing a modern "palette" feel. We also handle events like "blur" (when the window loses focus) to automatically hide the application.
Blur Event Handling
// src/App.tsx
import { useEffect } from 'react';
import { appWindow } from '@tauri-apps/api/window';
function App() {
useEffect(() => {
const unlisten = appWindow.onFocusChanged(({ payload: focused }) => {
if (!focused) {
appWindow.hide();
}
});
return () => {
unlisten.then(f => f());
};
}, []);
// ...
}
By combining global shortcuts with smart window event handling, Crest provides a seamless and "always-available" experience for the user.
Unified Search Engine
In our last chapter, we learned how to summon Crest to life with a magical keyboard shortcut, making it pop up and disappear instantly. But what happens after Crest appears? That's where the "Unified Search Engine" comes in – it's the brain that makes Crest intelligent and incredibly useful!
Think of Crest's search engine as your personal, super-efficient assistant. Instead of having to open a separate app for everything – a calculator for math, a file manager for documents, or a browser for web searches – Crest brings it all together into one simple box. It processes whatever you type and instantly pulls up the most relevant results from all corners of your digital world.
The core problem this system solves is information fragmentation. You have apps, files, clipboard history, and the entire internet, all scattered. Crest's Unified Search Engine acts as a master key, unlocking and organizing all this information right at your fingertips.
What is the Unified Search Engine?
The Unified Search Engine is a centralized system that takes your search query and broadcasts it to various "Search Providers." Each provider is an expert in a specific area:
- App Provider: Finds and launches installed applications on your computer.
- File Provider: Searches for documents, folders, and files across your hard drive.
- Calculator: Instantly solves mathematical expressions as you type.
- Web Search: Quickly searches Google, DuckDuckGo, or other engines.
- Clipboard: Retrieves items you've recently copied to your clipboard.
The Search Result Object
Every result found, whether it's an app or a math answer, is converted into a standard "Search Result" format. This allows Crest to display them consistently in the list.
// src/types/ipc.ts
export interface SearchResult {
id: string; // A unique identifier
title: string; // The main text shown (e.g., "Google Chrome")
subtitle?: string; // Extra info (e.g., "/Applications/Google Chrome.app")
icon?: string; // An icon representing the result
category: string; // What kind of result it is (e.g., "App", "File", "Math")
action: SearchAction; // What happens when you click it
}
Data Indexers
In our last chapter, we unveiled the magic of Crest's Unified Search Engine, learning how it intelligently processes your queries to find applications, files, and even web results. But a truly smart search engine needs to know what's on your computer! How does Crest get this information? That's where Data Indexers come into play.
Imagine Crest as a super-fast librarian. The Unified Search Engine is how you ask the librarian for a book. But before you can ask, someone needs to actually go out, find all the books, read their titles, authors, and summaries, and neatly organize them on shelves. These dedicated "book-finders" and "organizers" are Crest's Data Indexers.
What is a Data Indexer?
A Data Indexer is a specialized background process that crawls through a specific type of data on your system, extracts the important bits, and stores them in a highly optimized "Index" (like a giant, searchable catalog).
- App Indexer: Scans system application folders (like
/ApplicationsorC:\Program Files) for app names, icons, and locations. - File Indexer: Scans your home directory and other folders you specify for file names, paths, and metadata.
- Clipboard Indexer: Scans your system clipboard for recently copied text, links, or images.
The App Indexer (A Rust Example)
In Crest's backend (written in Rust), the App Indexer is responsible for finding your installed programs.
// src-tauri/src/indexers/app_indexer.rs (Simplified)
use std::fs;
use std::path::Path;
pub struct AppInfo {
pub name: String,
pub path: String,
}
pub fn index_apps() -> Vec<AppInfo> {
let mut apps = Vec::new();
let app_dir = Path::new("/Applications");
if let Ok(entries) = fs::read_dir(app_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) == Some("app") {
let name = path.file_stem().unwrap().to_str().unwrap().to_string();
apps.push(AppInfo { name, path: path.to_str().unwrap().to_string() });
}
}
}
apps
}
Plugin System
In our journey through Crest so far, we've learned how to summon it with a Global Hotkey, how its Unified Search Engine intelligently finds information, and how Data Indexers keep everything up-to-date. But what if you need Crest to do something really specific? That's where the Plugin System shines!
The main problem the Plugin System solves is customization without complexity. Instead of having to change Crest's core code, you can write small, independent programs (plugins) that Crest can run.
A Simple JavaScript Plugin (Conceptual)
Crest allows you to write plugins in simple JavaScript. Here's what a basic "Hello World" plugin might look like:
// hello-plugin.js
module.exports = {
onSearch: async (query) => {
if (query.startsWith('hello')) {
return [{
id: 'hello-result',
title: 'Say Hello!',
subtitle: 'Crest is happy to see you.',
category: 'Plugin',
action: { type: 'copy', value: 'Hello from Crest!' }
}];
}
return [];
},
onAction: (result) => {
console.log("Plugin action triggered:", result.title);
}
};
Tauri IPC Bridge
The "Tauri IPC Bridge" is exactly this secure communication channel. "IPC" stands for Inter-Process Communication, which is just a fancy way of saying "two separate programs talking to each other." Tauri, the framework Crest is built with, provides an incredibly safe and fast bridge between the frontend and the backend.
The core problem the IPC Bridge solves is secure and efficient coordination.
How to Use the IPC Bridge (A Search Example)
1. Defining a Command in the Backend (Rust)
// src-tauri/src/main.rs (Simplified)
#[tauri::command]
fn search(query: String) -> Vec<SearchResult> {
println!("Backend received search query: {}", query);
let results = core_engine::run_search(query);
results
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![search])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
2. Invoking the Command from the Frontend (TypeScript)
// src/components/SearchInput.tsx (Simplified)
import { invoke } from '@tauri-apps/api/core';
const handleSearch = async (query: string) => {
try {
const results = await invoke<SearchResult[]>('search', { query: query });
console.log("Frontend received results:", results);
} catch (error) {
console.error("IPC call failed:", error);
}
};
Frontend State Management
"Frontend State Management" is the system that organizes all the monitors, data screens, and controls within the control room itself. It ensures that when you type a query, the search box updates, the results list changes, and the preview panel shows details for the correct active item, all in perfect sync.
Zustand State Store
Crest uses Zustand to manage its global state. This store holds the current search results and the active selection.
// src/store/useSearchStore.ts
import { create } from 'zustand';
import { SearchResult } from '../types/ipc';
interface SearchState {
results: SearchResult[];
selectedIndex: number;
setResults: (results: SearchResult[]) => void;
nextResult: () => void;
prevResult: () => void;
}
export const useSearchStore = create<SearchState>((set) => ({
results: [],
selectedIndex: 0,
setResults: (results) => set({ results, selectedIndex: 0 }),
nextResult: () => set((state) => ({
selectedIndex: Math.min(state.selectedIndex + 1, state.results.length - 1)
})),
prevResult: () => set((state) => ({
selectedIndex: Math.max(state.selectedIndex - 1, 0)
})),
}));
By centralizing the state in a Zustand store, any component in Crest can easily access and update the search results or the currently selected item, ensuring a consistent user experience.