mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:41:09 +02:00
feat(shared-ui): add syntax highlighting to CommandBar input
Keywords are now visually highlighted as you type: - Priority (wichtig, dringend, später, normal): orange/warning - Tags (#tag): primary color - References (@project, @calendar): green/success - Dates (heute, morgen, montag, etc.): purple - Times (14:00, um 14 Uhr): purple Uses overlay technique with transparent input over highlighted backdrop. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
993b8c129a
commit
d98efc455b
1 changed files with 113 additions and 11 deletions
|
|
@ -1,6 +1,47 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
// Syntax highlighting patterns for command keywords
|
||||
interface HighlightPattern {
|
||||
pattern: RegExp;
|
||||
className: string;
|
||||
}
|
||||
|
||||
const HIGHLIGHT_PATTERNS: HighlightPattern[] = [
|
||||
// Priority keywords (Todo)
|
||||
{
|
||||
pattern: /(!{1,3}|!?dringend|!?wichtig|!?normal|!?später|!?späer)\b/gi,
|
||||
className: 'hl-priority',
|
||||
},
|
||||
// Tags
|
||||
{ pattern: /#\w+/g, className: 'hl-tag' },
|
||||
// Projects/Calendars/Companies (@reference)
|
||||
{ pattern: /@\w+/g, className: 'hl-reference' },
|
||||
// Date keywords
|
||||
{
|
||||
pattern:
|
||||
/\b(heute|morgen|übermorgen|montag|dienstag|mittwoch|donnerstag|freitag|samstag|sonntag|nächsten?\s+\w+|in\s+\d+\s+tagen?)\b/gi,
|
||||
className: 'hl-date',
|
||||
},
|
||||
// Time patterns
|
||||
{ pattern: /\b(\d{1,2}:\d{2}|um\s+\d{1,2}(\s*uhr)?|\d{1,2}\s*uhr)\b/gi, className: 'hl-time' },
|
||||
];
|
||||
|
||||
function highlightText(text: string): string {
|
||||
if (!text) return '';
|
||||
|
||||
let result = text;
|
||||
// Escape HTML first
|
||||
result = result.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
// Apply highlights (process in order, avoiding double-highlighting)
|
||||
for (const { pattern, className } of HIGHLIGHT_PATTERNS) {
|
||||
result = result.replace(pattern, (match) => `<span class="${className}">${match}</span>`);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export interface CommandBarItem {
|
||||
id: string;
|
||||
title: string;
|
||||
|
|
@ -68,6 +109,9 @@
|
|||
searchQuery.trim() && onParseCreate ? onParseCreate(searchQuery) : null
|
||||
);
|
||||
|
||||
// Highlighted text for overlay
|
||||
let highlightedQuery = $derived(highlightText(searchQuery));
|
||||
|
||||
// Check if create option is selected (it's always first when available)
|
||||
let isCreateSelected = $derived(selectedIndex === 0 && createPreview !== null);
|
||||
|
||||
|
|
@ -220,7 +264,7 @@
|
|||
onkeydown={handleKeydown}
|
||||
>
|
||||
<div class="command-modal">
|
||||
<!-- Search Input -->
|
||||
<!-- Search Input with Syntax Highlighting -->
|
||||
<div class="command-input-wrapper">
|
||||
<svg class="command-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
|
|
@ -230,14 +274,21 @@
|
|||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
<input
|
||||
bind:this={inputElement}
|
||||
type="text"
|
||||
{placeholder}
|
||||
bind:value={searchQuery}
|
||||
oninput={handleSearch}
|
||||
class="command-input"
|
||||
/>
|
||||
<div class="input-highlight-container">
|
||||
<!-- Highlight backdrop (shows colored keywords) -->
|
||||
<div class="input-highlight-backdrop" aria-hidden="true">
|
||||
{@html highlightedQuery}
|
||||
</div>
|
||||
<!-- Actual input (transparent text, visible caret) -->
|
||||
<input
|
||||
bind:this={inputElement}
|
||||
type="text"
|
||||
{placeholder}
|
||||
bind:value={searchQuery}
|
||||
oninput={handleSearch}
|
||||
class="command-input"
|
||||
/>
|
||||
</div>
|
||||
<kbd class="command-shortcut">ESC</kbd>
|
||||
</div>
|
||||
|
||||
|
|
@ -504,19 +555,70 @@
|
|||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.command-input {
|
||||
/* Input with syntax highlighting overlay */
|
||||
.input-highlight-container {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.input-highlight-backdrop {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
font-size: 1rem;
|
||||
font-family: inherit;
|
||||
white-space: pre;
|
||||
pointer-events: none;
|
||||
color: hsl(var(--color-foreground));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.command-input {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 1rem;
|
||||
color: hsl(var(--color-foreground));
|
||||
font-family: inherit;
|
||||
color: transparent;
|
||||
caret-color: hsl(var(--color-foreground));
|
||||
outline: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.command-input::placeholder {
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
/* Syntax highlighting colors */
|
||||
.input-highlight-backdrop :global(.hl-priority) {
|
||||
color: hsl(var(--color-warning, 38 92% 50%));
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.input-highlight-backdrop :global(.hl-tag) {
|
||||
color: hsl(var(--color-primary));
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input-highlight-backdrop :global(.hl-reference) {
|
||||
color: hsl(var(--color-success));
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input-highlight-backdrop :global(.hl-date) {
|
||||
color: hsl(262 83% 58%);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input-highlight-backdrop :global(.hl-time) {
|
||||
color: hsl(262 83% 58%);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.command-shortcut {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue