mirror of
https://github.com/open-webui/open-webui
synced 2025-06-22 18:07:17 +00:00
Merge 6555cf9d7a
into aef0ad2d10
This commit is contained in:
commit
15692553ba
@ -16,6 +16,7 @@
|
|||||||
const eventDispatch = createEventDispatcher();
|
const eventDispatch = createEventDispatcher();
|
||||||
|
|
||||||
import { EditorState, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
import { EditorState, Plugin, PluginKey, TextSelection } from 'prosemirror-state';
|
||||||
|
import { Fragment } from 'prosemirror-model';
|
||||||
import { Decoration, DecorationSet } from 'prosemirror-view';
|
import { Decoration, DecorationSet } from 'prosemirror-view';
|
||||||
import { Editor } from '@tiptap/core';
|
import { Editor } from '@tiptap/core';
|
||||||
|
|
||||||
@ -351,46 +352,72 @@
|
|||||||
},
|
},
|
||||||
paste: (view, event) => {
|
paste: (view, event) => {
|
||||||
if (event.clipboardData) {
|
if (event.clipboardData) {
|
||||||
// Extract plain text from clipboard and paste it without formatting
|
|
||||||
const plainText = event.clipboardData.getData('text/plain');
|
const plainText = event.clipboardData.getData('text/plain');
|
||||||
if (plainText) {
|
if (plainText) {
|
||||||
if (largeTextAsFile) {
|
if (largeTextAsFile && plainText.length > PASTED_TEXT_CHARACTER_LIMIT) {
|
||||||
if (plainText.length > PASTED_TEXT_CHARACTER_LIMIT) {
|
// Delegate handling of large text pastes to the parent component.
|
||||||
// Dispatch paste event to parent component
|
eventDispatch('paste', { event });
|
||||||
eventDispatch('paste', { event });
|
event.preventDefault();
|
||||||
event.preventDefault();
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for mobile WebViews that strip line breaks when pasting from
|
||||||
|
// clipboard suggestions (e.g., Gboard clipboard history).
|
||||||
|
const isMobile = /Android|iPhone|iPad|iPod|Windows Phone/i.test(
|
||||||
|
navigator.userAgent
|
||||||
|
);
|
||||||
|
const isWebView =
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
(/wv/i.test(navigator.userAgent) || // Standard Android WebView flag
|
||||||
|
(navigator.userAgent.includes('Android') &&
|
||||||
|
!navigator.userAgent.includes('Chrome')) || // Other generic Android WebViews
|
||||||
|
(navigator.userAgent.includes('Safari') &&
|
||||||
|
!navigator.userAgent.includes('Version'))); // iOS WebView (in-app browsers)
|
||||||
|
|
||||||
|
if (isMobile && isWebView && plainText.includes('\n')) {
|
||||||
|
// Manually deconstruct the pasted text and insert it with hard breaks
|
||||||
|
// to preserve the multi-line formatting.
|
||||||
|
const { state, dispatch } = view;
|
||||||
|
const { from, to } = state.selection;
|
||||||
|
|
||||||
|
const lines = plainText.split('\n');
|
||||||
|
const nodes = [];
|
||||||
|
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
if (index > 0) {
|
||||||
|
nodes.push(state.schema.nodes.hardBreak.create());
|
||||||
|
}
|
||||||
|
if (line.length > 0) {
|
||||||
|
nodes.push(state.schema.text(line));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fragment = Fragment.fromArray(nodes);
|
||||||
|
const tr = state.tr.replaceWith(from, to, fragment);
|
||||||
|
dispatch(tr.scrollIntoView());
|
||||||
|
event.preventDefault();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Let ProseMirror handle normal text paste in non-problematic environments.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the pasted content contains image files
|
// Delegate image paste handling to the parent component.
|
||||||
const hasImageFile = Array.from(event.clipboardData.files).some((file) =>
|
const hasImageFile = Array.from(event.clipboardData.files).some((file) =>
|
||||||
file.type.startsWith('image/')
|
file.type.startsWith('image/')
|
||||||
);
|
);
|
||||||
|
// Fallback for cases where an image is in dataTransfer.items but not clipboardData.files.
|
||||||
// Check for image in dataTransfer items (for cases where files are not available)
|
|
||||||
const hasImageItem = Array.from(event.clipboardData.items).some((item) =>
|
const hasImageItem = Array.from(event.clipboardData.items).some((item) =>
|
||||||
item.type.startsWith('image/')
|
item.type.startsWith('image/')
|
||||||
);
|
);
|
||||||
if (hasImageFile) {
|
if (hasImageFile || hasImageItem) {
|
||||||
// If there's an image, dispatch the event to the parent
|
|
||||||
eventDispatch('paste', { event });
|
|
||||||
event.preventDefault();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasImageItem) {
|
|
||||||
// If there's an image item, dispatch the event to the parent
|
|
||||||
eventDispatch('paste', { event });
|
eventDispatch('paste', { event });
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// For all other cases, let ProseMirror perform its default paste behavior.
|
||||||
// For all other cases (text, formatted text, etc.), let ProseMirror handle it
|
view.dispatch(view.state.tr.scrollIntoView());
|
||||||
view.dispatch(view.state.tr.scrollIntoView()); // Move viewport to the cursor after pasting
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user