generated from nhcarrigan/template
feat: batch of fixes and features (#56)
## Summary This PR includes a batch of bug fixes and new features: ### Bug Fixes - **Links in chat history now open in default browser** instead of navigating within the app - Closes #54 - **Allow spaces in tab names** - space key no longer acts like enter when renaming tabs - Closes #52 ### New Features - **`/cd` command** - Change the working directory of an active tab with context preservation - Closes #55 - **`/search` command** - Search and highlight matches within the conversation - Closes #32 ## Test Plan - [ ] Click a link in chat history and verify it opens in the default browser - [ ] Rename a tab and verify spaces can be typed - [ ] Use `/cd <path>` and verify the directory changes while preserving conversation context - [ ] Use `/search <query>` and verify matches are highlighted in yellow - [ ] Use `/search` with no args to clear the search highlighting ✨ This PR was created with help from Hikari~ 🌸 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Reviewed-on: #56 Co-authored-by: Hikari <hikari@nhcarrigan.com> Co-committed-by: Hikari <hikari@nhcarrigan.com>
This commit was merged in pull request #56.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
export let content: string;
|
||||
export let searchQuery: string;
|
||||
|
||||
interface TextPart {
|
||||
text: string;
|
||||
isMatch: boolean;
|
||||
}
|
||||
|
||||
function getHighlightedParts(text: string, query: string): TextPart[] {
|
||||
if (!query) {
|
||||
return [{ text, isMatch: false }];
|
||||
}
|
||||
|
||||
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const regex = new RegExp(`(${escapedQuery})`, "gi");
|
||||
const parts: TextPart[] = [];
|
||||
let lastIndex = 0;
|
||||
let match: RegExpExecArray | null;
|
||||
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
// Add non-matching text before the match
|
||||
if (match.index > lastIndex) {
|
||||
parts.push({
|
||||
text: text.slice(lastIndex, match.index),
|
||||
isMatch: false,
|
||||
});
|
||||
}
|
||||
|
||||
// Add the matching text
|
||||
parts.push({
|
||||
text: match[1],
|
||||
isMatch: true,
|
||||
});
|
||||
|
||||
lastIndex = regex.lastIndex;
|
||||
}
|
||||
|
||||
// Add any remaining text after the last match
|
||||
if (lastIndex < text.length) {
|
||||
parts.push({
|
||||
text: text.slice(lastIndex),
|
||||
isMatch: false,
|
||||
});
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
$: parts = getHighlightedParts(content, searchQuery);
|
||||
</script>
|
||||
|
||||
<span class="whitespace-pre-wrap">
|
||||
{#each parts as part, index (index)}
|
||||
{#if part.isMatch}
|
||||
<mark class="search-highlight">{part.text}</mark>
|
||||
{:else}
|
||||
{part.text}
|
||||
{/if}
|
||||
{/each}
|
||||
</span>
|
||||
Reference in New Issue
Block a user