diff --git a/apps/calendar/apps/web/src/routes/(app)/+layout.svelte b/apps/calendar/apps/web/src/routes/(app)/+layout.svelte
index 52066a92b..46b63932c 100644
--- a/apps/calendar/apps/web/src/routes/(app)/+layout.svelte
+++ b/apps/calendar/apps/web/src/routes/(app)/+layout.svelte
@@ -3,7 +3,7 @@
import { page } from '$app/stores';
import { onMount } from 'svelte';
import { locale } from 'svelte-i18n';
- import { PillNavigation, QuickInputBar } from '@manacore/shared-ui';
+ import { PillNavigation, QuickInputBar, InputBarHelpModal } from '@manacore/shared-ui';
import {
SplitPaneContainer,
setSplitPanelContext,
@@ -150,6 +150,42 @@
let isCollapsed = $state(false);
let isToolbarCollapsed = $state(true); // Default to collapsed - FAB next to InputBar
+ // InputBar help modal state
+ let helpModalOpen = $state(false);
+ let helpModalMode = $state<'shortcuts' | 'syntax'>('shortcuts');
+
+ function handleShowShortcuts() {
+ helpModalMode = 'shortcuts';
+ helpModalOpen = true;
+ }
+
+ function handleShowSyntaxHelp() {
+ helpModalMode = 'syntax';
+ helpModalOpen = true;
+ }
+
+ function handleCloseHelpModal() {
+ helpModalOpen = false;
+ }
+
+ // Default calendar for InputBar quick create
+ let selectedDefaultCalendarId = $derived(
+ calendarsStore.calendars.find((c) => c.isDefault)?.id || calendarsStore.calendars[0]?.id
+ );
+
+ function handleDefaultCalendarChange(id: string) {
+ // Update the default calendar via API
+ calendarsStore.setAsDefault(id);
+ }
+
+ // Calendar options for InputBar context menu
+ let calendarOptions = $derived(
+ calendarsStore.calendars.map((c) => ({
+ id: c.id,
+ label: c.name,
+ }))
+ );
+
// Use theme store's isDark directly
let isDark = $derived(theme.isDark);
@@ -431,7 +467,6 @@
onParseCreate={handleParseCreate}
createText="Erstellen"
appIcon="calendar"
- autoFocus={true}
bottomOffset={isSidebarMode
? '0px'
: showCalendarToolbar && !isToolbarCollapsed
@@ -439,6 +474,12 @@
: '70px'}
hasFabRight={showCalendarToolbar && !isSidebarMode}
hasFabLeft={showCalendarToolbar && !isSidebarMode && settingsStore.dateStripCollapsed}
+ defaultOptions={calendarOptions}
+ selectedDefaultId={selectedDefaultCalendarId}
+ defaultOptionLabel="Standard-Kalender"
+ onDefaultChange={handleDefaultCalendarChange}
+ onShowShortcuts={handleShowShortcuts}
+ onShowSyntaxHelp={handleShowSyntaxHelp}
/>
@@ -446,6 +487,9 @@
+
+
+
diff --git a/packages/shared-ui/src/quick-input/index.ts b/packages/shared-ui/src/quick-input/index.ts
index 74a6114bb..2d054ccd7 100644
--- a/packages/shared-ui/src/quick-input/index.ts
+++ b/packages/shared-ui/src/quick-input/index.ts
@@ -1,4 +1,28 @@
export { default as InputBar } from './InputBar.svelte';
// Alias for backwards compatibility
export { default as QuickInputBar } from './InputBar.svelte';
+export { default as InputBarContextMenu } from './InputBarContextMenu.svelte';
+export { default as InputBarHelpModal } from './InputBarHelpModal.svelte';
export type { QuickInputItem, QuickAction, CreatePreview } from './types';
+
+// Recent input history (tags, references)
+export {
+ getRecentTags,
+ getRecentReferences,
+ addRecentTag,
+ addRecentReference,
+ extractAndSaveFromInput,
+ clearRecentHistory,
+ createRecentInputHistoryStore,
+} from './recentInputHistory';
+
+// InputBar settings
+export {
+ loadInputBarSettings,
+ saveInputBarSettings,
+ updateInputBarSetting,
+ resetInputBarSettings,
+ createInputBarSettingsStore,
+ getInputBarSettingsStore,
+} from './inputBarSettings.svelte';
+export type { InputBarSettings } from './inputBarSettings.svelte';
diff --git a/packages/shared-ui/src/quick-input/inputBarSettings.svelte.ts b/packages/shared-ui/src/quick-input/inputBarSettings.svelte.ts
new file mode 100644
index 000000000..ae6a85425
--- /dev/null
+++ b/packages/shared-ui/src/quick-input/inputBarSettings.svelte.ts
@@ -0,0 +1,127 @@
+/**
+ * InputBar Settings Store
+ *
+ * Persisted settings for InputBar behavior and appearance.
+ * Stored in localStorage for cross-session retention.
+ */
+
+const STORAGE_KEY = 'inputbar-settings';
+
+export interface InputBarSettings {
+ /** Enable syntax highlighting for #tags, @refs, dates, etc. */
+ syntaxHighlighting: boolean;
+ /** Auto-focus InputBar on page load */
+ autoFocus: boolean;
+}
+
+const DEFAULT_SETTINGS: InputBarSettings = {
+ syntaxHighlighting: true,
+ autoFocus: true,
+};
+
+/**
+ * Load settings from localStorage
+ */
+export function loadInputBarSettings(): InputBarSettings {
+ if (typeof window === 'undefined') return { ...DEFAULT_SETTINGS };
+
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY);
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ return { ...DEFAULT_SETTINGS, ...parsed };
+ }
+ } catch {
+ // Ignore parse errors
+ }
+
+ return { ...DEFAULT_SETTINGS };
+}
+
+/**
+ * Save settings to localStorage
+ */
+export function saveInputBarSettings(settings: InputBarSettings): void {
+ if (typeof window === 'undefined') return;
+
+ try {
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
+ } catch {
+ // Ignore storage errors
+ }
+}
+
+/**
+ * Update a single setting
+ */
+export function updateInputBarSetting(
+ key: K,
+ value: InputBarSettings[K]
+): InputBarSettings {
+ const current = loadInputBarSettings();
+ const updated = { ...current, [key]: value };
+ saveInputBarSettings(updated);
+ return updated;
+}
+
+/**
+ * Reset settings to defaults
+ */
+export function resetInputBarSettings(): InputBarSettings {
+ saveInputBarSettings(DEFAULT_SETTINGS);
+ return { ...DEFAULT_SETTINGS };
+}
+
+/**
+ * Create a reactive Svelte 5 store for InputBar settings
+ */
+export function createInputBarSettingsStore() {
+ let settings = $state(loadInputBarSettings());
+
+ function refresh() {
+ settings = loadInputBarSettings();
+ }
+
+ function set(key: K, value: InputBarSettings[K]) {
+ settings = updateInputBarSetting(key, value);
+ }
+
+ function toggle(key: keyof InputBarSettings) {
+ if (typeof settings[key] === 'boolean') {
+ set(key, !settings[key] as InputBarSettings[typeof key]);
+ }
+ }
+
+ function reset() {
+ settings = resetInputBarSettings();
+ }
+
+ return {
+ get settings() {
+ return settings;
+ },
+ get syntaxHighlighting() {
+ return settings.syntaxHighlighting;
+ },
+ get autoFocus() {
+ return settings.autoFocus;
+ },
+ set,
+ toggle,
+ reset,
+ refresh,
+ };
+}
+
+// Global singleton store instance
+let globalStore: ReturnType | null = null;
+
+/**
+ * Get the global InputBar settings store instance
+ */
+export function getInputBarSettingsStore() {
+ if (!globalStore) {
+ globalStore = createInputBarSettingsStore();
+ }
+ return globalStore;
+}
diff --git a/packages/shared-ui/src/quick-input/recentInputHistory.ts b/packages/shared-ui/src/quick-input/recentInputHistory.ts
new file mode 100644
index 000000000..1805dce33
--- /dev/null
+++ b/packages/shared-ui/src/quick-input/recentInputHistory.ts
@@ -0,0 +1,167 @@
+/**
+ * Recent Input History Store
+ *
+ * Tracks recently used tags (#) and references (@) for quick access in the InputBar context menu.
+ * Persists to localStorage for cross-session retention.
+ */
+
+const STORAGE_KEY_TAGS = 'inputbar-recent-tags';
+const STORAGE_KEY_REFS = 'inputbar-recent-references';
+const MAX_ITEMS = 10;
+
+/**
+ * Get recent tags from localStorage
+ */
+export function getRecentTags(): string[] {
+ if (typeof window === 'undefined') return [];
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY_TAGS);
+ return stored ? JSON.parse(stored) : [];
+ } catch {
+ return [];
+ }
+}
+
+/**
+ * Get recent references from localStorage
+ */
+export function getRecentReferences(): string[] {
+ if (typeof window === 'undefined') return [];
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY_REFS);
+ return stored ? JSON.parse(stored) : [];
+ } catch {
+ return [];
+ }
+}
+
+/**
+ * Add a tag to recent history
+ * @param tag - The tag to add (with or without #)
+ */
+export function addRecentTag(tag: string): void {
+ if (typeof window === 'undefined') return;
+
+ // Normalize tag (ensure it starts with #)
+ const normalizedTag = tag.startsWith('#') ? tag : `#${tag}`;
+
+ try {
+ const current = getRecentTags();
+ // Remove if already exists (will be re-added at front)
+ const filtered = current.filter((t) => t.toLowerCase() !== normalizedTag.toLowerCase());
+ // Add to front, limit to MAX_ITEMS
+ const updated = [normalizedTag, ...filtered].slice(0, MAX_ITEMS);
+ localStorage.setItem(STORAGE_KEY_TAGS, JSON.stringify(updated));
+ } catch {
+ // Ignore storage errors
+ }
+}
+
+/**
+ * Add a reference to recent history
+ * @param reference - The reference to add (with or without @)
+ */
+export function addRecentReference(reference: string): void {
+ if (typeof window === 'undefined') return;
+
+ // Normalize reference (ensure it starts with @)
+ const normalizedRef = reference.startsWith('@') ? reference : `@${reference}`;
+
+ try {
+ const current = getRecentReferences();
+ // Remove if already exists (will be re-added at front)
+ const filtered = current.filter((r) => r.toLowerCase() !== normalizedRef.toLowerCase());
+ // Add to front, limit to MAX_ITEMS
+ const updated = [normalizedRef, ...filtered].slice(0, MAX_ITEMS);
+ localStorage.setItem(STORAGE_KEY_REFS, JSON.stringify(updated));
+ } catch {
+ // Ignore storage errors
+ }
+}
+
+/**
+ * Extract and save tags and references from input text
+ * Call this when user creates an item to track their usage patterns
+ * @param text - The input text to parse
+ */
+export function extractAndSaveFromInput(text: string): void {
+ if (!text) return;
+
+ // Extract tags (#word)
+ const tagMatches = text.match(/#\w+/g);
+ if (tagMatches) {
+ tagMatches.forEach((tag) => addRecentTag(tag));
+ }
+
+ // Extract references (@word)
+ const refMatches = text.match(/@\w+/g);
+ if (refMatches) {
+ refMatches.forEach((ref) => addRecentReference(ref));
+ }
+}
+
+/**
+ * Clear all recent history
+ */
+export function clearRecentHistory(): void {
+ if (typeof window === 'undefined') return;
+ try {
+ localStorage.removeItem(STORAGE_KEY_TAGS);
+ localStorage.removeItem(STORAGE_KEY_REFS);
+ } catch {
+ // Ignore storage errors
+ }
+}
+
+/**
+ * Create a reactive store for use in Svelte 5 components
+ * Returns reactive state that updates when history changes
+ */
+export function createRecentInputHistoryStore() {
+ let tags = $state(getRecentTags());
+ let references = $state(getRecentReferences());
+
+ // Refresh from localStorage
+ function refresh() {
+ tags = getRecentTags();
+ references = getRecentReferences();
+ }
+
+ // Add tag and refresh
+ function addTag(tag: string) {
+ addRecentTag(tag);
+ refresh();
+ }
+
+ // Add reference and refresh
+ function addReference(ref: string) {
+ addRecentReference(ref);
+ refresh();
+ }
+
+ // Extract from text and refresh
+ function extractAndSave(text: string) {
+ extractAndSaveFromInput(text);
+ refresh();
+ }
+
+ // Clear and refresh
+ function clear() {
+ clearRecentHistory();
+ refresh();
+ }
+
+ return {
+ get tags() {
+ return tags;
+ },
+ get references() {
+ return references;
+ },
+ addTag,
+ addReference,
+ extractAndSave,
+ clear,
+ refresh,
+ };
+}