Skip to main content

Native APIs

Krema provides a rich set of native APIs accessible from your frontend via the JavaScript bridge.

JavaScript Bridge

Krema injects a global window.krema object in your frontend:

// Invoke a backend command
const result = await window.krema.invoke('greet', { name: 'World' });

// Listen for events from backend
window.krema.on('file-changed', (data) => {
console.log('File changed:', data.path);
});

// One-time event listener
window.krema.once('app-ready', () => {
console.log('App is ready!');
});

// Remove event listener
const unsubscribe = window.krema.on('update', handler);
unsubscribe();

File Dialogs

Open File

import build.krema.api.dialog.FileDialog;

@KremaCommand
public String openFile() {
return FileDialog.open()
.title("Select a file")
.filters("Text Files", "*.txt", "*.md")
.filters("All Files", "*.*")
.show()
.map(Path::toString)
.orElse(null);
}

Save File

@KremaCommand
public String saveFile() {
return FileDialog.save()
.title("Save file")
.defaultName("document.txt")
.show()
.map(Path::toString)
.orElse(null);
}

Select Multiple Files

@KremaCommand
public List<String> openMultiple() {
return FileDialog.openMultiple()
.title("Select files")
.show()
.stream()
.map(Path::toString)
.toList();
}

Select Folder

@KremaCommand
public String selectFolder() {
return FileDialog.folder()
.title("Select folder")
.show()
.map(Path::toString)
.orElse(null);
}

Clipboard

import build.krema.api.clipboard.Clipboard;

@KremaCommand
public String getClipboardText() {
return Clipboard.readText();
}

@KremaCommand
public void setClipboardText(String text) {
Clipboard.writeText(text);
}

@KremaCommand
public void copyImage(String base64Image) {
byte[] imageData = Base64.getDecoder().decode(base64Image);
Clipboard.writeImage(imageData);
}

Notifications

import build.krema.api.notification.Notification;

@KremaCommand
public void showNotification(String title, String body) {
Notification.show(title, body);
}

System Tray

import build.krema.api.tray.SystemTray;
import build.krema.api.tray.TrayMenu;

@KremaCommand
public void setupTray() {
SystemTray.get()
.setIcon(iconBytes)
.setTooltip("My App")
.setMenu(TrayMenu.builder()
.item("Show", () -> window.show())
.separator()
.item("Quit", () -> System.exit(0))
.build())
.show();
}

Shell Commands

import build.krema.api.shell.Shell;

@KremaCommand
@RequiresPermission(Permission.SHELL_OPEN)
public void openInBrowser(String url) {
Shell.open(url);
}

@KremaCommand
@RequiresPermission(Permission.SHELL_EXECUTE)
public String runCommand(String command) {
return Shell.execute(command).output();
}

Path Utilities

import build.krema.api.path.AppPaths;

@KremaCommand
public Map<String, String> getPaths() {
return Map.of(
"appData", AppPaths.appDataDir().toString(),
"config", AppPaths.appConfigDir().toString(),
"cache", AppPaths.appCacheDir().toString(),
"home", AppPaths.homeDir().toString(),
"desktop", AppPaths.desktopDir().toString(),
"documents", AppPaths.documentsDir().toString(),
"downloads", AppPaths.downloadsDir().toString()
);
}

Screen Information

import build.krema.api.screen.Screen;

@KremaCommand
public List<ScreenInfo> getScreens() {
return Screen.all().stream()
.map(s -> new ScreenInfo(
s.bounds(),
s.scaleFactor(),
s.isPrimary()
))
.toList();
}

@KremaCommand
public Point getCursorPosition() {
return Screen.cursorPosition();
}

Global Shortcuts

Register system-wide keyboard shortcuts:

import build.krema.api.shortcut.GlobalShortcut;

@KremaCommand
public void registerShortcut() {
GlobalShortcut.register("Cmd+Shift+P", () -> {
events.emit("shortcut-triggered", Map.of("key", "Cmd+Shift+P"));
});
}

@KremaCommand
public void unregisterShortcut() {
GlobalShortcut.unregister("Cmd+Shift+P");
}

Native Menus

Application Menu

import build.krema.api.menu.Menu;
import build.krema.api.menu.MenuItem;

Menu appMenu = Menu.builder()
.submenu("File")
.item("New", "Cmd+N", () -> newDocument())
.item("Open...", "Cmd+O", () -> openDocument())
.separator()
.item("Save", "Cmd+S", () -> saveDocument())
.end()
.submenu("Edit")
.item("Undo", "Cmd+Z", () -> undo())
.item("Redo", "Cmd+Shift+Z", () -> redo())
.end()
.build();

window.setMenu(appMenu);

Context Menu

@KremaCommand
public void showContextMenu(int x, int y) {
Menu contextMenu = Menu.builder()
.item("Cut", () -> cut())
.item("Copy", () -> copy())
.item("Paste", () -> paste())
.build();

contextMenu.showAt(x, y);
}

Secure Storage

Store sensitive data securely using the OS keychain:

import build.krema.api.storage.SecureStorage;

@KremaCommand
public void storeSecret(String key, String value) {
SecureStorage.set(key, value);
}

@KremaCommand
public String getSecret(String key) {
return SecureStorage.get(key).orElse(null);
}

@KremaCommand
public void deleteSecret(String key) {
SecureStorage.delete(key);
}

HTTP Client

Make HTTP requests from the backend (bypasses CORS):

import build.krema.api.http.HttpClient;

@KremaCommand
public String fetchData(String url) {
return HttpClient.fetch(url)
.header("Authorization", "Bearer " + token)
.get()
.body();
}

@KremaCommand
public String postJson(String url, Map<String, Object> data) {
return HttpClient.fetch(url)
.header("Content-Type", "application/json")
.body(Json.stringify(data))
.post()
.body();
}

Events from Backend

Emit events to the frontend:

import build.krema.event.EventEmitter;

public class Main {
private static EventEmitter events;

public static void main(String[] args) {
Krema.app()
.events(emitter -> events = emitter)
.commands(new Commands())
.run();
}

public static void notifyProgress(int percent) {
events.emit("progress", Map.of("percent", percent));
}
}

Listen in frontend:

window.krema.on('progress', (data) => {
progressBar.style.width = `${data.percent}%`;
});