mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 04:15:25 +00:00
enh: text select copy behaviour
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Python CI / Format Backend (push) Waiting to run
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
This commit is contained in:
parent
38f45a38cb
commit
2de854fa02
1 changed files with 130 additions and 78 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher, onDestroy } from 'svelte';
|
||||||
import { onMount, tick, getContext } from 'svelte';
|
import { onMount, tick, getContext } from 'svelte';
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from 'svelte/store';
|
||||||
import type { i18n as i18nType, t } from 'i18next';
|
import type { i18n as i18nType, t } from 'i18next';
|
||||||
|
|
@ -152,6 +152,8 @@
|
||||||
export let topPadding = false;
|
export let topPadding = false;
|
||||||
|
|
||||||
let citationsElement: HTMLDivElement;
|
let citationsElement: HTMLDivElement;
|
||||||
|
|
||||||
|
let contentContainerElement: HTMLDivElement;
|
||||||
let buttonsContainerElement: HTMLDivElement;
|
let buttonsContainerElement: HTMLDivElement;
|
||||||
let showDeleteConfirm = false;
|
let showDeleteConfirm = false;
|
||||||
|
|
||||||
|
|
@ -541,24 +543,70 @@
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buttonsWheelHandler = (event: WheelEvent) => {
|
||||||
|
if (buttonsContainerElement) {
|
||||||
|
if (buttonsContainerElement.scrollWidth <= buttonsContainerElement.clientWidth) {
|
||||||
|
// If the container is not scrollable, horizontal scroll
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (event.deltaY !== 0) {
|
||||||
|
// Adjust horizontal scroll position based on vertical scroll
|
||||||
|
buttonsContainerElement.scrollLeft += event.deltaY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const contentCopyHandler = (e) => {
|
||||||
|
if (contentContainerElement) {
|
||||||
|
e.preventDefault();
|
||||||
|
// Get the selected HTML
|
||||||
|
const selection = window.getSelection();
|
||||||
|
const range = selection.getRangeAt(0);
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
|
||||||
|
// Remove background, color, and font styles
|
||||||
|
tempDiv.appendChild(range.cloneContents());
|
||||||
|
|
||||||
|
tempDiv.querySelectorAll('table').forEach((table) => {
|
||||||
|
table.style.borderCollapse = 'collapse';
|
||||||
|
table.style.width = 'auto';
|
||||||
|
table.style.tableLayout = 'auto';
|
||||||
|
});
|
||||||
|
|
||||||
|
tempDiv.querySelectorAll('th').forEach((th) => {
|
||||||
|
th.style.whiteSpace = 'nowrap';
|
||||||
|
th.style.padding = '4px 8px';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Put cleaned HTML + plain text into clipboard
|
||||||
|
e.clipboardData.setData('text/html', tempDiv.innerHTML);
|
||||||
|
e.clipboardData.setData('text/plain', selection.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// console.log('ResponseMessage mounted');
|
// console.log('ResponseMessage mounted');
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
if (buttonsContainerElement) {
|
if (buttonsContainerElement) {
|
||||||
buttonsContainerElement.addEventListener('wheel', function (event) {
|
buttonsContainerElement.addEventListener('wheel', buttonsWheelHandler);
|
||||||
if (buttonsContainerElement.scrollWidth <= buttonsContainerElement.clientWidth) {
|
}
|
||||||
// If the container is not scrollable, horizontal scroll
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (event.deltaY !== 0) {
|
if (contentContainerElement) {
|
||||||
// Adjust horizontal scroll position based on vertical scroll
|
contentContainerElement.addEventListener('copy', contentCopyHandler);
|
||||||
buttonsContainerElement.scrollLeft += event.deltaY;
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
});
|
onDestroy(() => {
|
||||||
|
if (buttonsContainerElement) {
|
||||||
|
buttonsContainerElement.removeEventListener('wheel', buttonsWheelHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentContainerElement) {
|
||||||
|
contentContainerElement.removeEventListener('copy', contentCopyHandler);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -719,72 +767,76 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
|
||||||
<div class="w-full flex flex-col relative" id="response-content-container">
|
|
||||||
{#if message.content === '' && !message.error && ((model?.info?.meta?.capabilities?.status_updates ?? true) ? (message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length === 0 || (message?.statusHistory?.at(-1)?.hidden ?? false) : true)}
|
|
||||||
<Skeleton />
|
|
||||||
{:else if message.content && message.error !== true}
|
|
||||||
<!-- always show message contents even if there's an error -->
|
|
||||||
<!-- unless message.error === true which is legacy error handling, where the error message is stored in message.content -->
|
|
||||||
<ContentRenderer
|
|
||||||
id={`${chatId}-${message.id}`}
|
|
||||||
messageId={message.id}
|
|
||||||
{history}
|
|
||||||
{selectedModels}
|
|
||||||
content={message.content}
|
|
||||||
sources={message.sources}
|
|
||||||
floatingButtons={message?.done &&
|
|
||||||
!readOnly &&
|
|
||||||
($settings?.showFloatingActionButtons ?? true)}
|
|
||||||
save={!readOnly}
|
|
||||||
preview={!readOnly}
|
|
||||||
{editCodeBlock}
|
|
||||||
{topPadding}
|
|
||||||
done={($settings?.chatFadeStreamingText ?? true)
|
|
||||||
? (message?.done ?? false)
|
|
||||||
: true}
|
|
||||||
{model}
|
|
||||||
onTaskClick={async (e) => {
|
|
||||||
console.log(e);
|
|
||||||
}}
|
|
||||||
onSourceClick={async (id, idx) => {
|
|
||||||
console.log(id, idx);
|
|
||||||
|
|
||||||
if (citationsElement) {
|
|
||||||
citationsElement?.showSourceModal(idx - 1);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onAddMessages={({ modelId, parentId, messages }) => {
|
|
||||||
addMessages({ modelId, parentId, messages });
|
|
||||||
}}
|
|
||||||
onSave={({ raw, oldContent, newContent }) => {
|
|
||||||
history.messages[message.id].content = history.messages[
|
|
||||||
message.id
|
|
||||||
].content.replace(raw, raw.replace(oldContent, newContent));
|
|
||||||
|
|
||||||
updateChat();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if message?.error}
|
|
||||||
<Error content={message?.error?.content ?? message.content} />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if (message?.sources || message?.citations) && (model?.info?.meta?.capabilities?.citations ?? true)}
|
|
||||||
<Citations
|
|
||||||
bind:this={citationsElement}
|
|
||||||
id={message?.id}
|
|
||||||
sources={message?.sources ?? message?.citations}
|
|
||||||
{readOnly}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if message.code_executions}
|
|
||||||
<CodeExecutions codeExecutions={message.code_executions} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={contentContainerElement}
|
||||||
|
class="w-full flex flex-col relative {edit ? 'hidden' : ''}"
|
||||||
|
id="response-content-container"
|
||||||
|
>
|
||||||
|
{#if message.content === '' && !message.error && ((model?.info?.meta?.capabilities?.status_updates ?? true) ? (message?.statusHistory ?? [...(message?.status ? [message?.status] : [])]).length === 0 || (message?.statusHistory?.at(-1)?.hidden ?? false) : true)}
|
||||||
|
<Skeleton />
|
||||||
|
{:else if message.content && message.error !== true}
|
||||||
|
<!-- always show message contents even if there's an error -->
|
||||||
|
<!-- unless message.error === true which is legacy error handling, where the error message is stored in message.content -->
|
||||||
|
<ContentRenderer
|
||||||
|
id={`${chatId}-${message.id}`}
|
||||||
|
messageId={message.id}
|
||||||
|
{history}
|
||||||
|
{selectedModels}
|
||||||
|
content={message.content}
|
||||||
|
sources={message.sources}
|
||||||
|
floatingButtons={message?.done &&
|
||||||
|
!readOnly &&
|
||||||
|
($settings?.showFloatingActionButtons ?? true)}
|
||||||
|
save={!readOnly}
|
||||||
|
preview={!readOnly}
|
||||||
|
{editCodeBlock}
|
||||||
|
{topPadding}
|
||||||
|
done={($settings?.chatFadeStreamingText ?? true)
|
||||||
|
? (message?.done ?? false)
|
||||||
|
: true}
|
||||||
|
{model}
|
||||||
|
onTaskClick={async (e) => {
|
||||||
|
console.log(e);
|
||||||
|
}}
|
||||||
|
onSourceClick={async (id, idx) => {
|
||||||
|
console.log(id, idx);
|
||||||
|
|
||||||
|
if (citationsElement) {
|
||||||
|
citationsElement?.showSourceModal(idx - 1);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onAddMessages={({ modelId, parentId, messages }) => {
|
||||||
|
addMessages({ modelId, parentId, messages });
|
||||||
|
}}
|
||||||
|
onSave={({ raw, oldContent, newContent }) => {
|
||||||
|
history.messages[message.id].content = history.messages[
|
||||||
|
message.id
|
||||||
|
].content.replace(raw, raw.replace(oldContent, newContent));
|
||||||
|
|
||||||
|
updateChat();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if message?.error}
|
||||||
|
<Error content={message?.error?.content ?? message.content} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if (message?.sources || message?.citations) && (model?.info?.meta?.capabilities?.citations ?? true)}
|
||||||
|
<Citations
|
||||||
|
bind:this={citationsElement}
|
||||||
|
id={message?.id}
|
||||||
|
sources={message?.sources ?? message?.citations}
|
||||||
|
{readOnly}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#if message.code_executions}
|
||||||
|
<CodeExecutions codeExecutions={message.code_executions} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue