diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreview.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreview.svelte
index 0efe052b1f..8e89491723 100644
--- a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreview.svelte
+++ b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreview.svelte
@@ -5,6 +5,7 @@
ChatAttachmentsPreviewNavButtons,
ChatAttachmentsPreviewThumbnailStrip
} from '$lib/components/app';
+ import { UI_DATA_ATTRS } from '$lib/constants';
import { modelsStore } from '$lib/stores';
import {
createBase64DataUrl,
@@ -90,7 +91,7 @@
const index = currentIndex;
setTimeout(() => {
- const thumbnail = document.querySelector(`[data-thumbnail-index="${index}"]`);
+ const thumbnail = document.querySelector(`[${UI_DATA_ATTRS.THUMBNAIL_INDEX}="${index}"]`);
thumbnail?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
}, 0);
diff --git a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte
index 60b72077d8..366c8372b9 100644
--- a/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte
+++ b/tools/ui/src/lib/components/app/chat/ChatAttachments/ChatAttachmentsPreview/ChatAttachmentsPreviewThumbnailStrip.svelte
@@ -1,7 +1,7 @@
-{#if useContenteditable}
+{#if useRichInput}
- import { CODE_BLOCK } from '$lib/constants';
- import { ColorMode } from '$lib/enums';
+ import { CODE_BLOCK, CODE_TOKEN_ATTR, UI_DATA_ATTRS } from '$lib/constants';
+ import { BooleanString, ChatFormInputRichTokenKind, ColorMode } from '$lib/enums';
import { isMobile } from '$lib/stores';
import type { ChatFormInputRichToken } from '$lib/types';
import type { SourceHistoryEntry } from '$lib/utils';
@@ -53,7 +53,7 @@
// browser's native undo stack.
const history = new SourceHistory();
- // Browsers disagree on what an empty contenteditable contains (`
`,
+ // Browsers disagree on what an empty rich chat form input contains (`
`,
// `
`, or nothing), so emptiness is decided by the
// serialized source, not the DOM shape.
function syncEmptyState(serialized?: string) {
@@ -61,7 +61,7 @@
const source = serialized ?? serializeContent(rootElement);
- rootElement.dataset.empty = source.length === 0 ? 'true' : 'false';
+ rootElement.dataset.empty = source.length === 0 ? BooleanString.TRUE : BooleanString.FALSE;
}
function renderTokens(tokens: ChatFormInputRichToken[]) {
@@ -69,7 +69,7 @@
const caret = rangeToTextOffset(rootElement, safeRange());
- // eslint-disable-next-line svelte/no-dom-manipulating -- the token layer is owned imperatively; Svelte renders only the contenteditable host, never its children
+ // eslint-disable-next-line svelte/no-dom-manipulating -- the token layer is owned imperatively; Svelte renders only the rich chat form input host, never its children
rootElement.replaceChildren(buildFragment(tokens));
syncCodeBlockHatches(rootElement);
@@ -127,7 +127,9 @@
}
function highlightCodeBlocks(root: HTMLElement) {
- for (const el of root.querySelectorAll('code[data-code-token="code_block"]')) {
+ for (const el of root.querySelectorAll(
+ `code[${CODE_TOKEN_ATTR}="${ChatFormInputRichTokenKind.CODE_BLOCK}"]`
+ )) {
highlightCodeBlockElement(el);
}
}
@@ -151,7 +153,10 @@
}
while (node && node !== rootElement) {
- if (node instanceof HTMLElement && node.dataset.codeToken === 'code_block') {
+ if (
+ node instanceof HTMLElement &&
+ node.getAttribute(CODE_TOKEN_ATTR) === ChatFormInputRichTokenKind.CODE_BLOCK
+ ) {
const caret = rangeToTextOffset(rootElement, range);
if (highlightCodeBlockElement(node)) {
@@ -189,11 +194,13 @@
* (deduped via the data attribute) swapped on mode change.
*/
function loadHighlightTheme(isDark: boolean) {
- document.querySelectorAll('style[data-highlight-theme-preview]').forEach((s) => s.remove());
+ document
+ .querySelectorAll(`style[${UI_DATA_ATTRS.HIGHLIGHT_THEME_PREVIEW}]`)
+ .forEach((s) => s.remove());
const style = document.createElement('style');
- style.setAttribute('data-highlight-theme-preview', 'true');
+ style.setAttribute(UI_DATA_ATTRS.HIGHLIGHT_THEME_PREVIEW, BooleanString.TRUE);
style.textContent = isDark ? githubDarkCss : githubLightCss;
document.head.appendChild(style);
@@ -311,7 +318,7 @@
source[source.length - 2] !== '\n' &&
last?.nodeType === Node.TEXT_NODE
) {
- // eslint-disable-next-line svelte/no-dom-manipulating -- the token layer is owned imperatively; Svelte renders only the contenteditable host, never its children
+ // eslint-disable-next-line svelte/no-dom-manipulating -- the token layer is owned imperatively; Svelte renders only the rich chat form input host, never its children
rootElement.appendChild(document.createTextNode('\n'));
restoreCaret(source.length);
resizeHeight();
@@ -404,7 +411,10 @@
let node: Node | null = container.parentNode;
while (node && node !== rootElement) {
- if (node instanceof HTMLElement && node.dataset.codeToken === 'code_block') {
+ if (
+ node instanceof HTMLElement &&
+ node.getAttribute(CODE_TOKEN_ATTR) === ChatFormInputRichTokenKind.CODE_BLOCK
+ ) {
const tail = document.createRange();
tail.setStart(container, offset);
@@ -462,7 +472,11 @@
const first = rootElement.firstChild;
- if (!(first instanceof HTMLElement) || first.dataset.codeToken !== 'code_block') return false;
+ if (
+ !(first instanceof HTMLElement) ||
+ first.getAttribute(CODE_TOKEN_ATTR) !== ChatFormInputRichTokenKind.CODE_BLOCK
+ )
+ return false;
const range = safeRange();
@@ -483,7 +497,7 @@
if (firstLineEnd !== -1 && caret > firstLineEnd) return false;
}
- // eslint-disable-next-line svelte/no-dom-manipulating -- the token layer is owned imperatively; Svelte renders only the contenteditable host, never its children
+ // eslint-disable-next-line svelte/no-dom-manipulating -- the token layer is owned imperatively; Svelte renders only the rich chat form input host, never its children
rootElement.prepend(document.createElement('br'));
restoreCaret(0, extend);
@@ -507,7 +521,11 @@
const second = first.nextSibling;
- if (!(second instanceof HTMLElement) || second.dataset.codeToken !== 'code_block') return;
+ if (
+ !(second instanceof HTMLElement) ||
+ second.getAttribute(CODE_TOKEN_ATTR) !== ChatFormInputRichTokenKind.CODE_BLOCK
+ )
+ return;
const range = safeRange();
const onHatch =
diff --git a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte
index 3ebb29b9b3..160c14ce8e 100644
--- a/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte
+++ b/tools/ui/src/lib/components/app/chat/ChatForm/ChatFormPickers/ChatFormPicker/ChatFormPickerList.svelte
@@ -1,7 +1,7 @@
-