feat: add QuickInputBar to 6 more apps (mukke, matrix, manadeck, planta, photos, presi)

- Mukke: search songs by title/artist/album via libraryStore
- Matrix: search rooms/contacts, select navigates to chat
- ManaDeck: search decks by title/description
- Planta: search plants by name/species via plantsApi
- Photos: search albums and tags
- Presi: search presentation decks

All with locale-aware syntax highlighting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-23 21:25:51 +01:00
parent 49457c354a
commit ff419f069a
6 changed files with 200 additions and 13 deletions

View file

@ -7,8 +7,8 @@
import { userSettings } from '$lib/stores/user-settings.svelte'; import { userSettings } from '$lib/stores/user-settings.svelte';
import { theme } from '$lib/stores/theme'; import { theme } from '$lib/stores/theme';
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation'; import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation';
import { PillNavigation } from '@manacore/shared-ui'; import { PillNavigation, QuickInputBar } from '@manacore/shared-ui';
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui'; import type { PillNavItem, PillDropdownItem, QuickInputItem } from '@manacore/shared-ui';
import { import {
THEME_DEFINITIONS, THEME_DEFINITIONS,
DEFAULT_THEME_VARIANTS, DEFAULT_THEME_VARIANTS,
@ -19,6 +19,7 @@
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n'; import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
import { getPillAppItems } from '@manacore/shared-branding'; import { getPillAppItems } from '@manacore/shared-branding';
import { setLocale, supportedLocales } from '$lib/i18n'; import { setLocale, supportedLocales } from '$lib/i18n';
import { deckStore } from '$lib/stores/deckStore.svelte';
import { manadeckOnboarding } from '$lib/stores/app-onboarding.svelte'; import { manadeckOnboarding } from '$lib/stores/app-onboarding.svelte';
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding'; import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
@ -139,6 +140,23 @@
goto('/login'); goto('/login');
} }
// QuickInputBar handlers
async function handleInputSearch(query: string): Promise<QuickInputItem[]> {
const q = query.toLowerCase();
return deckStore.decks
.filter((d) => d.title.toLowerCase().includes(q) || d.description?.toLowerCase().includes(q))
.slice(0, 10)
.map((deck) => ({
id: deck.id,
title: deck.title,
subtitle: deck.description || undefined,
}));
}
function handleInputSelect(item: QuickInputItem) {
goto(`/decks/${item.id}`);
}
onMount(async () => { onMount(async () => {
await authStore.initialize(); await authStore.initialize();
@ -209,6 +227,18 @@
allAppsHref="/apps" allAppsHref="/apps"
/> />
<!-- Quick Input Bar -->
<QuickInputBar
onSearch={handleInputSearch}
onSelect={handleInputSelect}
placeholder="Deck suchen..."
emptyText="Keine Decks gefunden"
searchingText="Suche..."
locale={$locale || 'de'}
appIcon="search"
bottomOffset="70px"
/>
<!-- Main content --> <!-- Main content -->
<main class="pb-24"> <main class="pb-24">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">

View file

@ -20,8 +20,8 @@
} from '@manacore/shared-theme'; } from '@manacore/shared-theme';
import type { ThemeVariant } from '@manacore/shared-theme'; import type { ThemeVariant } from '@manacore/shared-theme';
import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation.svelte'; import { isNavCollapsed as collapsedStore } from '$lib/stores/navigation.svelte';
import { PillNavigation } from '@manacore/shared-ui'; import { PillNavigation, QuickInputBar } from '@manacore/shared-ui';
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui'; import type { PillNavItem, PillDropdownItem, QuickInputItem } from '@manacore/shared-ui';
import { getPillAppItems } from '@manacore/shared-branding'; import { getPillAppItems } from '@manacore/shared-branding';
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n'; import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
import { setLocale, supportedLocales } from '$lib/i18n'; import { setLocale, supportedLocales } from '$lib/i18n';
@ -164,6 +164,22 @@
goto('/login'); goto('/login');
} }
// QuickInputBar handlers
async function handleInputSearch(query: string): Promise<QuickInputItem[]> {
const q = query.toLowerCase();
const rooms = matrixStore.rooms.filter((r) => r.name?.toLowerCase().includes(q));
return rooms.slice(0, 10).map((room) => ({
id: room.roomId,
title: room.name || room.roomId,
subtitle: room.isDirect ? 'Direktnachricht' : 'Gruppe',
}));
}
function handleInputSelect(item: QuickInputItem) {
matrixStore.selectRoom(item.id);
goto('/chat');
}
onMount(async () => { onMount(async () => {
// Initialize collapsed state from localStorage // Initialize collapsed state from localStorage
const savedCollapsed = localStorage.getItem('matrix-nav-collapsed'); const savedCollapsed = localStorage.getItem('matrix-nav-collapsed');
@ -332,6 +348,18 @@
allAppsHref="https://mana.how" allAppsHref="https://mana.how"
/> />
<!-- Quick Input Bar -->
<QuickInputBar
onSearch={handleInputSearch}
onSelect={handleInputSelect}
placeholder="Raum oder Kontakt suchen..."
emptyText="Keine Räume gefunden"
searchingText="Suche..."
locale={$locale || 'de'}
appIcon="search"
bottomOffset="70px"
/>
<!-- Main Content --> <!-- Main Content -->
<main class="main-content bg-background"> <main class="main-content bg-background">
{@render children()} {@render children()}

View file

@ -2,8 +2,8 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { PillNavigation, DevBuildBadge } from '@manacore/shared-ui'; import { PillNavigation, QuickInputBar, DevBuildBadge } from '@manacore/shared-ui';
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui'; import type { PillNavItem, PillDropdownItem, QuickInputItem } from '@manacore/shared-ui';
import { import {
SplitPaneContainer, SplitPaneContainer,
setSplitPanelContext, setSplitPanelContext,
@ -18,6 +18,7 @@
import type { ThemeVariant } from '@manacore/shared-theme'; import type { ThemeVariant } from '@manacore/shared-theme';
import { theme } from '$lib/stores/theme.svelte'; import { theme } from '$lib/stores/theme.svelte';
import { authStore } from '$lib/stores/auth.svelte'; import { authStore } from '$lib/stores/auth.svelte';
import { libraryStore } from '$lib/stores/library.svelte';
import MiniPlayer from '$lib/components/MiniPlayer.svelte'; import MiniPlayer from '$lib/components/MiniPlayer.svelte';
import FullPlayer from '$lib/components/FullPlayer.svelte'; import FullPlayer from '$lib/components/FullPlayer.svelte';
import QueuePanel from '$lib/components/QueuePanel.svelte'; import QueuePanel from '$lib/components/QueuePanel.svelte';
@ -102,6 +103,21 @@
goto('/login'); goto('/login');
} }
// QuickInputBar handlers
async function handleInputSearch(query: string): Promise<QuickInputItem[]> {
const songs = await libraryStore.searchSongs(query);
return songs.slice(0, 10).map((song) => ({
id: song.id,
title: song.title || 'Untitled',
subtitle: [song.artist, song.album].filter(Boolean).join(' — '),
isFavorite: song.favorite,
}));
}
function handleInputSelect(item: QuickInputItem) {
goto(`/library?song=${item.id}`);
}
onMount(async () => { onMount(async () => {
await authStore.initialize(); await authStore.initialize();
if (!authStore.isAuthenticated) { if (!authStore.isAuthenticated) {
@ -155,6 +171,18 @@
ariaLabel="Main navigation" ariaLabel="Main navigation"
/> />
<!-- Quick Input Bar -->
<QuickInputBar
onSearch={handleInputSearch}
onSelect={handleInputSelect}
placeholder="Song suchen..."
emptyText="Keine Songs gefunden"
searchingText="Suche..."
locale="de"
appIcon="search"
bottomOffset="140px"
/>
<!-- Main Content --> <!-- Main Content -->
<main id="main-content" class="main-content bg-background"> <main id="main-content" class="main-content bg-background">
<div class="content-wrapper"> <div class="content-wrapper">

View file

@ -2,9 +2,9 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { _ } from 'svelte-i18n'; import { _, locale } from 'svelte-i18n';
import { PillNavigation } from '@manacore/shared-ui'; import { PillNavigation, QuickInputBar } from '@manacore/shared-ui';
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui'; import type { PillNavItem, PillDropdownItem, QuickInputItem } from '@manacore/shared-ui';
import { theme } from '$lib/stores/theme'; import { theme } from '$lib/stores/theme';
import { authStore } from '$lib/stores/auth.svelte'; import { authStore } from '$lib/stores/auth.svelte';
import { photoStore } from '$lib/stores/photos.svelte'; import { photoStore } from '$lib/stores/photos.svelte';
@ -56,6 +56,29 @@
goto('/login'); goto('/login');
} }
// QuickInputBar handlers
async function handleInputSearch(query: string): Promise<QuickInputItem[]> {
const q = query.toLowerCase();
const albums = albumStore.albums.filter((a) => a.name?.toLowerCase().includes(q));
const tags = tagStore.tags.filter((t) => t.name?.toLowerCase().includes(q));
const results: QuickInputItem[] = [];
for (const album of albums.slice(0, 5)) {
results.push({ id: `album-${album.id}`, title: album.name, subtitle: 'Album' });
}
for (const tag of tags.slice(0, 5)) {
results.push({ id: `tag-${tag.id}`, title: tag.name, subtitle: 'Tag' });
}
return results;
}
function handleInputSelect(item: QuickInputItem) {
if (item.id.startsWith('album-')) {
goto(`/albums/${item.id.replace('album-', '')}`);
} else if (item.id.startsWith('tag-')) {
goto(`/tags/${item.id.replace('tag-', '')}`);
}
}
onMount(async () => { onMount(async () => {
await authStore.initialize(); await authStore.initialize();
if (!authStore.isAuthenticated) { if (!authStore.isAuthenticated) {
@ -91,6 +114,18 @@
settingsHref="/settings" settingsHref="/settings"
/> />
<!-- Quick Input Bar -->
<QuickInputBar
onSearch={handleInputSearch}
onSelect={handleInputSelect}
placeholder="Album oder Tag suchen..."
emptyText="Nichts gefunden"
searchingText="Suche..."
locale={$locale || 'de'}
appIcon="search"
bottomOffset="70px"
/>
<main class="main-content bg-background"> <main class="main-content bg-background">
<div class="content-wrapper"> <div class="content-wrapper">
{@render children()} {@render children()}

View file

@ -2,10 +2,11 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { PillNavigation } from '@manacore/shared-ui'; import { PillNavigation, QuickInputBar } from '@manacore/shared-ui';
import type { PillNavItem } from '@manacore/shared-ui'; import type { PillNavItem, QuickInputItem } from '@manacore/shared-ui';
import { theme } from '$lib/stores/theme'; import { theme } from '$lib/stores/theme';
import { authStore } from '$lib/stores/auth.svelte'; import { authStore } from '$lib/stores/auth.svelte';
import { plantsApi } from '$lib/api/plants';
let { children } = $props(); let { children } = $props();
@ -27,6 +28,29 @@
goto('/login'); goto('/login');
} }
// QuickInputBar handlers
async function handleInputSearch(query: string): Promise<QuickInputItem[]> {
const plants = await plantsApi.getAll();
const q = query.toLowerCase();
return plants
.filter(
(p) =>
p.name?.toLowerCase().includes(q) ||
p.commonName?.toLowerCase().includes(q) ||
p.scientificName?.toLowerCase().includes(q)
)
.slice(0, 10)
.map((plant) => ({
id: plant.id,
title: plant.name || plant.commonName || 'Unbenannt',
subtitle: plant.scientificName || undefined,
}));
}
function handleInputSelect(item: QuickInputItem) {
goto(`/plant/${item.id}`);
}
onMount(() => { onMount(() => {
if (!authStore.isAuthenticated) { if (!authStore.isAuthenticated) {
goto('/login'); goto('/login');
@ -49,6 +73,18 @@
primaryColor="#10b981" primaryColor="#10b981"
/> />
<!-- Quick Input Bar -->
<QuickInputBar
onSearch={handleInputSearch}
onSelect={handleInputSelect}
placeholder="Pflanze suchen..."
emptyText="Keine Pflanzen gefunden"
searchingText="Suche..."
locale="de"
appIcon="search"
bottomOffset="70px"
/>
<main class="main-content pt-24"> <main class="main-content pt-24">
<div class="content-wrapper"> <div class="content-wrapper">
{@render children()} {@render children()}

View file

@ -3,8 +3,8 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { locale } from 'svelte-i18n'; import { locale } from 'svelte-i18n';
import { PillNavigation } from '@manacore/shared-ui'; import { PillNavigation, QuickInputBar } from '@manacore/shared-ui';
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui'; import type { PillNavItem, PillDropdownItem, QuickInputItem } from '@manacore/shared-ui';
import { auth } from '$lib/stores/auth.svelte'; import { auth } from '$lib/stores/auth.svelte';
import { userSettings } from '$lib/stores/user-settings.svelte'; import { userSettings } from '$lib/stores/user-settings.svelte';
import { theme } from '$lib/stores/theme'; import { theme } from '$lib/stores/theme';
@ -13,6 +13,7 @@
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n'; import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
import { getPillAppItems } from '@manacore/shared-branding'; import { getPillAppItems } from '@manacore/shared-branding';
import { setLocale, supportedLocales } from '$lib/i18n'; import { setLocale, supportedLocales } from '$lib/i18n';
import { decksStore } from '$lib/stores/decks.svelte';
import { presiOnboarding } from '$lib/stores/app-onboarding.svelte'; import { presiOnboarding } from '$lib/stores/app-onboarding.svelte';
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding'; import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
@ -103,6 +104,23 @@
} }
} }
// QuickInputBar handlers
async function handleInputSearch(query: string): Promise<QuickInputItem[]> {
const q = query.toLowerCase();
return decksStore.decks
.filter((d) => d.title.toLowerCase().includes(q) || d.description?.toLowerCase().includes(q))
.slice(0, 10)
.map((deck) => ({
id: deck.id,
title: deck.title,
subtitle: deck.description || undefined,
}));
}
function handleInputSelect(item: QuickInputItem) {
goto(`/deck/${item.id}`);
}
onMount(async () => { onMount(async () => {
// Redirect to login if not authenticated // Redirect to login if not authenticated
if (!auth.isAuthenticated) { if (!auth.isAuthenticated) {
@ -169,6 +187,18 @@
allAppsHref="/apps" allAppsHref="/apps"
/> />
<!-- Quick Input Bar -->
<QuickInputBar
onSearch={handleInputSearch}
onSelect={handleInputSelect}
placeholder="Präsentation suchen..."
emptyText="Keine Decks gefunden"
searchingText="Suche..."
locale={$locale || 'de'}
appIcon="search"
bottomOffset="70px"
/>
<!-- Main Content --> <!-- Main Content -->
<main class="main-content"> <main class="main-content">
<div class="content-wrapper"> <div class="content-wrapper">