Spaces:
Paused
Paused
Disable text streaming if user prefers reduced animation (#1131)
Browse files* Disable text streaming if user prefers reduced animation
* Simplify filtering of stream updates
---------
Co-authored-by: Nathan Sarrazin <[email protected]>
src/lib/components/chat/ChatMessage.svelte
CHANGED
|
@@ -37,6 +37,7 @@
|
|
| 37 |
import type { ToolFront } from "$lib/types/Tool";
|
| 38 |
import { base } from "$app/paths";
|
| 39 |
import { useConvTreeStore } from "$lib/stores/convTree";
|
|
|
|
| 40 |
import Modal from "../Modal.svelte";
|
| 41 |
import { toolHasName } from "$lib/utils/tools";
|
| 42 |
|
|
@@ -87,6 +88,7 @@
|
|
| 87 |
|
| 88 |
let initialized = false;
|
| 89 |
|
|
|
|
| 90 |
const renderer = new marked.Renderer();
|
| 91 |
// For code blocks with simple backticks
|
| 92 |
renderer.codespan = (code) => {
|
|
@@ -123,6 +125,10 @@
|
|
| 123 |
!message.content && (webSearchIsDone || (searchUpdates && searchUpdates.length === 0));
|
| 124 |
|
| 125 |
afterUpdate(() => {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
loadingEl?.$destroy();
|
| 127 |
clearTimeout(pendingTimeout);
|
| 128 |
|
|
@@ -364,6 +370,9 @@
|
|
| 364 |
class="prose max-w-none max-sm:prose-sm dark:prose-invert prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
|
| 365 |
bind:this={contentEl}
|
| 366 |
>
|
|
|
|
|
|
|
|
|
|
| 367 |
{#each tokens as token}
|
| 368 |
{#if token.type === "code"}
|
| 369 |
<CodeBlock lang={token.lang} code={unsanitizeMd(token.text)} />
|
|
|
|
| 37 |
import type { ToolFront } from "$lib/types/Tool";
|
| 38 |
import { base } from "$app/paths";
|
| 39 |
import { useConvTreeStore } from "$lib/stores/convTree";
|
| 40 |
+
import { isReducedMotion } from "$lib/utils/isReduceMotion";
|
| 41 |
import Modal from "../Modal.svelte";
|
| 42 |
import { toolHasName } from "$lib/utils/tools";
|
| 43 |
|
|
|
|
| 88 |
|
| 89 |
let initialized = false;
|
| 90 |
|
| 91 |
+
const reducedMotionMode = isReducedMotion(window);
|
| 92 |
const renderer = new marked.Renderer();
|
| 93 |
// For code blocks with simple backticks
|
| 94 |
renderer.codespan = (code) => {
|
|
|
|
| 125 |
!message.content && (webSearchIsDone || (searchUpdates && searchUpdates.length === 0));
|
| 126 |
|
| 127 |
afterUpdate(() => {
|
| 128 |
+
if (reducedMotionMode) {
|
| 129 |
+
return;
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
loadingEl?.$destroy();
|
| 133 |
clearTimeout(pendingTimeout);
|
| 134 |
|
|
|
|
| 370 |
class="prose max-w-none max-sm:prose-sm dark:prose-invert prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
|
| 371 |
bind:this={contentEl}
|
| 372 |
>
|
| 373 |
+
{#if isLast && loading && reducedMotionMode}
|
| 374 |
+
<IconLoading classNames="loading inline ml-2 first:ml-0" />
|
| 375 |
+
{/if}
|
| 376 |
{#each tokens as token}
|
| 377 |
{#if token.type === "code"}
|
| 378 |
<CodeBlock lang={token.lang} code={unsanitizeMd(token.text)} />
|
src/lib/utils/isReduceMotion.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export function isReducedMotion(window: Window) {
|
| 2 |
+
const { matchMedia } = window;
|
| 3 |
+
|
| 4 |
+
return matchMedia("(prefers-reduced-motion: reduce)").matches;
|
| 5 |
+
}
|
src/routes/conversation/[id]/+page.svelte
CHANGED
|
@@ -23,6 +23,7 @@
|
|
| 23 |
import { fetchMessageUpdates } from "$lib/utils/messageUpdates";
|
| 24 |
import { createConvTreeStore } from "$lib/stores/convTree";
|
| 25 |
import type { v4 } from "uuid";
|
|
|
|
| 26 |
import { useSettingsStore } from "$lib/stores/settings.js";
|
| 27 |
|
| 28 |
export let data;
|
|
@@ -79,6 +80,7 @@
|
|
| 79 |
$isAborted = false;
|
| 80 |
loading = true;
|
| 81 |
pending = true;
|
|
|
|
| 82 |
|
| 83 |
const base64Files = await Promise.all(
|
| 84 |
(files ?? []).map((file) =>
|
|
@@ -215,6 +217,7 @@
|
|
| 215 |
files = [];
|
| 216 |
|
| 217 |
const messageUpdates: MessageUpdate[] = [];
|
|
|
|
| 218 |
for await (const update of messageUpdatesIterator) {
|
| 219 |
if ($isAborted) {
|
| 220 |
messageUpdatesAbortController.abort();
|
|
@@ -234,9 +237,8 @@
|
|
| 234 |
|
| 235 |
messageUpdates.push(update);
|
| 236 |
|
| 237 |
-
if (update.type === MessageUpdateType.Stream) {
|
| 238 |
pending = false;
|
| 239 |
-
messageToWriteTo.content += update.token;
|
| 240 |
messages = [...messages];
|
| 241 |
} else if (
|
| 242 |
update.type === MessageUpdateType.WebSearch ||
|
|
|
|
| 23 |
import { fetchMessageUpdates } from "$lib/utils/messageUpdates";
|
| 24 |
import { createConvTreeStore } from "$lib/stores/convTree";
|
| 25 |
import type { v4 } from "uuid";
|
| 26 |
+
import { isReducedMotion } from "$lib/utils/isReduceMotion.js";
|
| 27 |
import { useSettingsStore } from "$lib/stores/settings.js";
|
| 28 |
|
| 29 |
export let data;
|
|
|
|
| 80 |
$isAborted = false;
|
| 81 |
loading = true;
|
| 82 |
pending = true;
|
| 83 |
+
const reducedMotionMode = isReducedMotion(window);
|
| 84 |
|
| 85 |
const base64Files = await Promise.all(
|
| 86 |
(files ?? []).map((file) =>
|
|
|
|
| 217 |
files = [];
|
| 218 |
|
| 219 |
const messageUpdates: MessageUpdate[] = [];
|
| 220 |
+
|
| 221 |
for await (const update of messageUpdatesIterator) {
|
| 222 |
if ($isAborted) {
|
| 223 |
messageUpdatesAbortController.abort();
|
|
|
|
| 237 |
|
| 238 |
messageUpdates.push(update);
|
| 239 |
|
| 240 |
+
if (update.type === MessageUpdateType.Stream && !reducedMotionMode) {
|
| 241 |
pending = false;
|
|
|
|
| 242 |
messages = [...messages];
|
| 243 |
} else if (
|
| 244 |
update.type === MessageUpdateType.WebSearch ||
|