mirror of
https://github.com/ByteAtATime/raycast-linux.git
synced 2025-08-31 11:17:27 +00:00
feat: add hud API
This commit adds the HUD API, allowing extensions to display temporary, non-interactive messages on the screen. The implementation adds a `show_hud` Tauri command that manages a dedicated, borderless Svelte window. This window is dynamically resized to fit its content and is automatically hidden after two seconds.
This commit is contained in:
parent
1ae8a571cb
commit
28d1605bba
8 changed files with 125 additions and 2 deletions
|
@ -78,6 +78,15 @@ const HideToastPayloadSchema = z.object({
|
|||
id: z.number()
|
||||
});
|
||||
|
||||
const ShowHudPayloadSchema = z.object({
|
||||
title: z.string()
|
||||
});
|
||||
|
||||
const ShowHudMessageSchema = z.object({
|
||||
type: z.literal('SHOW_HUD'),
|
||||
payload: ShowHudPayloadSchema
|
||||
});
|
||||
|
||||
export const CommandSchema = z.discriminatedUnion('type', [
|
||||
z.object({ type: z.literal('CREATE_INSTANCE'), payload: CreateInstancePayloadSchema }),
|
||||
z.object({ type: z.literal('CREATE_TEXT_INSTANCE'), payload: CreateTextInstancePayloadSchema }),
|
||||
|
@ -292,6 +301,7 @@ const OauthRemoveTokensMessageSchema = z.object({
|
|||
export const SidecarMessageWithPluginsSchema = z.union([
|
||||
BatchUpdateSchema,
|
||||
CommandSchema,
|
||||
ShowHudMessageSchema,
|
||||
LogMessageSchema,
|
||||
PluginListSchema,
|
||||
PreferenceValuesSchema,
|
||||
|
|
8
sidecar/src/api/hud.ts
Normal file
8
sidecar/src/api/hud.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { writeOutput } from '../io';
|
||||
|
||||
export async function showHUD(title: string): Promise<void> {
|
||||
writeOutput({
|
||||
type: 'SHOW_HUD',
|
||||
payload: { title }
|
||||
});
|
||||
}
|
|
@ -13,6 +13,7 @@ import { Detail } from './components/detail';
|
|||
import { environment, getSelectedFinderItems, getSelectedText, open } from './environment';
|
||||
import { preferencesStore } from '../preferences';
|
||||
import { showToast } from './toast';
|
||||
import { showHUD } from './hud';
|
||||
import { BrowserExtensionAPI } from './browserExtension';
|
||||
import { Clipboard } from './clipboard';
|
||||
import * as OAuth from './oauth';
|
||||
|
@ -70,6 +71,7 @@ export const getRaycastApi = () => {
|
|||
getSelectedText,
|
||||
open,
|
||||
showToast,
|
||||
showHUD,
|
||||
useNavigation,
|
||||
usePersistentState: <T>(
|
||||
key: string,
|
||||
|
|
14
src-tauri/capabilities/hud.json
Normal file
14
src-tauri/capabilities/hud.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "hud",
|
||||
"description": "Capability for the HUD window",
|
||||
"windows": [
|
||||
"hud"
|
||||
],
|
||||
"permissions": [
|
||||
"core:window:allow-set-size",
|
||||
"core:window:allow-center",
|
||||
"core:window:allow-set-min-size",
|
||||
"core:window:allow-set-max-size"
|
||||
]
|
||||
}
|
|
@ -56,6 +56,42 @@ fn get_selected_text() -> String {
|
|||
get_text()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn show_hud(app: tauri::AppHandle, title: String) -> Result<(), String> {
|
||||
let url = format!("/hud?title={}", title);
|
||||
let hud_window = match app.get_webview_window("hud") {
|
||||
Some(window) => window,
|
||||
None => tauri::WebviewWindowBuilder::new(&app, "hud", tauri::WebviewUrl::App(url.into()))
|
||||
.decorations(false)
|
||||
.transparent(true)
|
||||
.always_on_top(true)
|
||||
.skip_taskbar(true)
|
||||
.center()
|
||||
.min_inner_size(300.0, 80.0)
|
||||
.max_inner_size(300.0, 80.0)
|
||||
.inner_size(300.0, 80.0)
|
||||
.build()
|
||||
.map_err(|e| e.to_string())?,
|
||||
};
|
||||
|
||||
let window_clone = hud_window.clone();
|
||||
window_clone
|
||||
.emit("hud-message", &title)
|
||||
.map_err(|e| e.to_string())?;
|
||||
window_clone
|
||||
.set_ignore_cursor_events(true)
|
||||
.map_err(|e| e.to_string())?;
|
||||
window_clone.show().map_err(|e| e.to_string())?;
|
||||
window_clone.set_focus().map_err(|e| e.to_string())?;
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
let _ = window_clone.hide();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_background_refresh() {
|
||||
thread::spawn(|| {
|
||||
thread::sleep(Duration::from_secs(60));
|
||||
|
@ -107,8 +143,8 @@ pub fn run() {
|
|||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.emit("deep-link", args[1].to_string());
|
||||
window.show().unwrap();
|
||||
window.set_focus().unwrap();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -130,6 +166,7 @@ pub fn run() {
|
|||
get_installed_apps,
|
||||
launch_app,
|
||||
get_selected_text,
|
||||
show_hud,
|
||||
filesystem::get_selected_finder_items,
|
||||
extensions::install_extension,
|
||||
browser_extension::browser_extension_check_connection,
|
||||
|
|
|
@ -23,6 +23,18 @@
|
|||
"width": 600,
|
||||
"height": 80,
|
||||
"center": true
|
||||
},
|
||||
{
|
||||
"label": "hud",
|
||||
"url": "/hud",
|
||||
"visible": false,
|
||||
"decorations": false,
|
||||
"transparent": true,
|
||||
"alwaysOnTop": true,
|
||||
"skipTaskbar": true,
|
||||
"center": true,
|
||||
"width": 300,
|
||||
"height": 80
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
|
@ -55,4 +67,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -152,6 +152,11 @@ class SidecarService {
|
|||
return;
|
||||
}
|
||||
|
||||
if (typedMessage.type === 'SHOW_HUD') {
|
||||
invoke('show_hud', { title: typedMessage.payload.title });
|
||||
return;
|
||||
}
|
||||
|
||||
if (typedMessage.type === 'open') {
|
||||
const { target, application } = typedMessage.payload;
|
||||
shellOpen(target, application).catch((err) => {
|
||||
|
|
35
src/routes/hud/+page.svelte
Normal file
35
src/routes/hud/+page.svelte
Normal file
|
@ -0,0 +1,35 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { getCurrentWindow, LogicalSize } from '@tauri-apps/api/window';
|
||||
|
||||
let hudText = $derived(page.url.searchParams.get('title') ?? '');
|
||||
let hudEl = $state<HTMLDivElement | null>(null);
|
||||
|
||||
$effect(() => {
|
||||
const bounds = hudEl?.getBoundingClientRect();
|
||||
if (bounds) {
|
||||
const window = getCurrentWindow();
|
||||
window.setMinSize(new LogicalSize(bounds.width, bounds.height));
|
||||
window.setMaxSize(new LogicalSize(bounds.width, bounds.height));
|
||||
window.setSize(new LogicalSize(bounds.width, bounds.height));
|
||||
window.center();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex h-screen items-center justify-center bg-transparent">
|
||||
{#if hudText}
|
||||
<div
|
||||
class="rounded-full bg-black/70 px-4 py-2 text-sm font-medium text-white"
|
||||
bind:this={hudEl}
|
||||
>
|
||||
{hudText}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
Loading…
Add table
Add a link
Reference in a new issue