forked from mirror/crumbs-original
fix: decode HTML entities in unlinkifyHtml and fix indentation
- Add entity decoding (& < > ") to unlinkifyHtml to prevent double-encoding on contenteditable edit cycles - Fix indentation of LinkPopover import and state declaration - Move LinkPopover inside root div - Add unit test for entity decoding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Fred Cabassut
co-authored by
Claude Opus 4.6
parent
377ce4b72c
commit
beb4209baf
@@ -7,7 +7,7 @@
|
||||
import { dragHandleZone, dragHandle, type DndEvent } from 'svelte-dnd-action';
|
||||
import { flip } from 'svelte/animate';
|
||||
import { type ChecklistItem, generateId, parseChecklist, serializeChecklist, toggleItemWithCascade, indentItem, outdentItem, linkifyText, unlinkifyHtml } from '$lib/utils/checklist.js';
|
||||
import LinkPopover from './LinkPopover.svelte';
|
||||
import LinkPopover from './LinkPopover.svelte';
|
||||
|
||||
interface Props {
|
||||
content: string;
|
||||
@@ -20,7 +20,7 @@ import LinkPopover from './LinkPopover.svelte';
|
||||
let items = $state<ChecklistItem[]>(parseChecklist(content));
|
||||
let doneExpanded = $state(true);
|
||||
const flipDurationMs = 150;
|
||||
let linkPopover = $state<{ url: string; anchor: DOMRect } | null>(null);
|
||||
let linkPopover = $state<{ url: string; anchor: DOMRect } | null>(null);
|
||||
|
||||
// Track the last content we emitted so we can distinguish self-originated
|
||||
// changes from external ones (sync, history restore, etc.)
|
||||
@@ -314,7 +314,7 @@ let linkPopover = $state<{ url: string; anchor: DOMRect } | null>(null);
|
||||
data-testid="checklist-input"
|
||||
data-item-id={item.id}
|
||||
role="textbox"
|
||||
tabindex="0"
|
||||
tabindex="0"
|
||||
>{@html linkifyText(item.text)}</div>
|
||||
<button
|
||||
onclick={() => removeItem(item.id)}
|
||||
@@ -382,9 +382,9 @@ let linkPopover = $state<{ url: string; anchor: DOMRect } | null>(null);
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if linkPopover}
|
||||
<LinkPopover url={linkPopover.url} anchor={linkPopover.anchor} onClose={() => (linkPopover = null)} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if linkPopover}
|
||||
<LinkPopover url={linkPopover.url} anchor={linkPopover.anchor} onClose={() => (linkPopover = null)} />
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -312,4 +312,10 @@ describe('unlinkifyHtml', () => {
|
||||
it('handles div and br tags from contenteditable', () => {
|
||||
expect(unlinkifyHtml('line1<br>line2')).toBe('line1line2');
|
||||
});
|
||||
|
||||
it('decodes HTML entities after stripping tags', () => {
|
||||
expect(unlinkifyHtml('Tom & Jerry')).toBe('Tom & Jerry');
|
||||
expect(unlinkifyHtml('<b>bold</b>')).toBe('<b>bold</b>');
|
||||
expect(unlinkifyHtml('say "hello"')).toBe('say "hello"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -189,5 +189,10 @@ export function linkifyText(text: string): string {
|
||||
|
||||
export function unlinkifyHtml(html: string): string {
|
||||
if (!html) return '';
|
||||
return html.replace(/<[^>]*>/g, '');
|
||||
return html
|
||||
.replace(/<[^>]*>/g, '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user