mirror of
https://github.com/open-webui/open-webui
synced 2025-03-28 11:22:31 +00:00
refac: tab text variable select
This commit is contained in:
parent
0107a70343
commit
6c0d3ce736
@ -53,8 +53,9 @@
|
|||||||
|
|
||||||
let recording = false;
|
let recording = false;
|
||||||
|
|
||||||
let chatTextAreaElement: HTMLTextAreaElement;
|
|
||||||
let chatInputContainerElement;
|
let chatInputContainerElement;
|
||||||
|
let chatInputElement;
|
||||||
|
|
||||||
let filesInputElement;
|
let filesInputElement;
|
||||||
let commandsElement;
|
let commandsElement;
|
||||||
|
|
||||||
@ -70,9 +71,10 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
$: if (prompt) {
|
$: if (prompt) {
|
||||||
if (chatTextAreaElement) {
|
if (chatInputContainerElement) {
|
||||||
chatTextAreaElement.style.height = '';
|
chatInputContainerElement.style.height = '';
|
||||||
chatTextAreaElement.style.height = Math.min(chatTextAreaElement.scrollHeight, 200) + 'px';
|
chatInputContainerElement.style.height =
|
||||||
|
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +322,8 @@
|
|||||||
atSelectedModel = data.data;
|
atSelectedModel = data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
chatTextAreaElement?.focus();
|
const chatInputElement = document.getElementById('chat-input');
|
||||||
|
chatInputElement?.focus();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -482,7 +485,9 @@
|
|||||||
}}
|
}}
|
||||||
onClose={async () => {
|
onClose={async () => {
|
||||||
await tick();
|
await tick();
|
||||||
chatTextAreaElement?.focus();
|
|
||||||
|
const chatInput = document.getElementById('chat-input');
|
||||||
|
chatInput?.focus();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@ -506,9 +511,11 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={chatInputContainerElement}
|
bind:this={chatInputContainerElement}
|
||||||
|
id="chat-input-container"
|
||||||
class="scrollbar-hidden text-left bg-gray-50 dark:bg-gray-850 dark:text-gray-100 outline-none w-full py-3 px-1 rounded-xl resize-none h-[48px] overflow-auto"
|
class="scrollbar-hidden text-left bg-gray-50 dark:bg-gray-850 dark:text-gray-100 outline-none w-full py-3 px-1 rounded-xl resize-none h-[48px] overflow-auto"
|
||||||
>
|
>
|
||||||
<RichTextInput
|
<RichTextInput
|
||||||
|
bind:this={chatInputElement}
|
||||||
id="chat-input"
|
id="chat-input"
|
||||||
placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
|
placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
|
||||||
bind:value={prompt}
|
bind:value={prompt}
|
||||||
@ -634,26 +641,6 @@
|
|||||||
]?.at(-1);
|
]?.at(-1);
|
||||||
|
|
||||||
commandOptionButton?.click();
|
commandOptionButton?.click();
|
||||||
} else if (e.key === 'Tab') {
|
|
||||||
const words = findWordIndices(prompt);
|
|
||||||
|
|
||||||
if (words.length > 0) {
|
|
||||||
const word = words.at(0);
|
|
||||||
const fullPrompt = prompt;
|
|
||||||
|
|
||||||
prompt = prompt.substring(0, word?.endIndex + 1);
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
e.target.scrollTop = e.target.scrollHeight;
|
|
||||||
prompt = fullPrompt;
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
e.target.setSelectionRange(word?.startIndex, word.endIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.target.style.height = '';
|
|
||||||
e.target.style.height = Math.min(e.target.scrollHeight, 200) + 'px';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
|
@ -110,21 +110,17 @@
|
|||||||
|
|
||||||
prompt = text;
|
prompt = text;
|
||||||
|
|
||||||
|
const chatInputContainerElement = document.getElementById('chat-input-container');
|
||||||
const chatInputElement = document.getElementById('chat-input');
|
const chatInputElement = document.getElementById('chat-input');
|
||||||
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
chatInputElement.style.height = '';
|
if (chatInputContainerElement) {
|
||||||
chatInputElement.style.height = Math.min(chatInputElement.scrollHeight, 200) + 'px';
|
chatInputContainerElement.style.height = '';
|
||||||
|
chatInputContainerElement.style.height =
|
||||||
|
Math.min(chatInputContainerElement.scrollHeight, 200) + 'px';
|
||||||
|
|
||||||
chatInputElement?.focus();
|
chatInputElement?.focus();
|
||||||
|
|
||||||
await tick();
|
|
||||||
|
|
||||||
const words = findWordIndices(prompt);
|
|
||||||
if (words.length > 0) {
|
|
||||||
const word = words.at(0);
|
|
||||||
chatInputElement.setSelectionRange(word?.startIndex, word.endIndex + 1);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -164,6 +164,60 @@
|
|||||||
return liftListItem(schema.nodes.list_item)(state, dispatch);
|
return liftListItem(schema.nodes.list_item)(state, dispatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findNextTemplate(doc, from = 0) {
|
||||||
|
const patterns = [
|
||||||
|
{ start: '[', end: ']' },
|
||||||
|
{ start: '{{', end: '}}' }
|
||||||
|
];
|
||||||
|
|
||||||
|
let result = null;
|
||||||
|
|
||||||
|
doc.nodesBetween(from, doc.content.size, (node, pos) => {
|
||||||
|
if (result) return false; // Stop if we've found a match
|
||||||
|
if (node.isText) {
|
||||||
|
const text = node.text;
|
||||||
|
let index = Math.max(0, from - pos);
|
||||||
|
while (index < text.length) {
|
||||||
|
for (const pattern of patterns) {
|
||||||
|
if (text.startsWith(pattern.start, index)) {
|
||||||
|
const endIndex = text.indexOf(pattern.end, index + pattern.start.length);
|
||||||
|
if (endIndex !== -1) {
|
||||||
|
result = {
|
||||||
|
from: pos + index,
|
||||||
|
to: pos + endIndex + pattern.end.length
|
||||||
|
};
|
||||||
|
return false; // Stop searching
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectNextTemplate(state, dispatch) {
|
||||||
|
const { doc, selection } = state;
|
||||||
|
const from = selection.to;
|
||||||
|
let template = findNextTemplate(doc, from);
|
||||||
|
|
||||||
|
if (!template) {
|
||||||
|
// If not found, search from the beginning
|
||||||
|
template = findNextTemplate(doc, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (template) {
|
||||||
|
if (dispatch) {
|
||||||
|
const tr = state.tr.setSelection(TextSelection.create(doc, template.from, template.to));
|
||||||
|
dispatch(tr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const initialDoc = markdownToProseMirrorDoc(value || ''); // Convert the initial content
|
const initialDoc = markdownToProseMirrorDoc(value || ''); // Convert the initial content
|
||||||
|
|
||||||
@ -234,14 +288,16 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Prevent default tab navigation and provide indent/outdent behavior inside lists:
|
// Prevent default tab navigation and provide indent/outdent behavior inside lists:
|
||||||
Tab: (state, dispatch, view) => {
|
Tab: chainCommands((state, dispatch, view) => {
|
||||||
const { $from } = state.selection;
|
const { $from } = state.selection;
|
||||||
console.log('Tab key pressed', $from.parent, $from.parent.type);
|
console.log('Tab key pressed', $from.parent, $from.parent.type);
|
||||||
if (isInList(state)) {
|
if (isInList(state)) {
|
||||||
return sinkListItem(schema.nodes.list_item)(state, dispatch);
|
return sinkListItem(schema.nodes.list_item)(state, dispatch);
|
||||||
|
} else {
|
||||||
|
return selectNextTemplate(state, dispatch);
|
||||||
}
|
}
|
||||||
return true; // Prevent Tab from moving the focus
|
return true; // Prevent Tab from moving the focus
|
||||||
},
|
}),
|
||||||
'Shift-Tab': (state, dispatch, view) => {
|
'Shift-Tab': (state, dispatch, view) => {
|
||||||
const { $from } = state.selection;
|
const { $from } = state.selection;
|
||||||
console.log('Shift-Tab key pressed', $from.parent, $from.parent.type);
|
console.log('Shift-Tab key pressed', $from.parent, $from.parent.type);
|
||||||
@ -327,6 +383,16 @@
|
|||||||
selection: TextSelection.atEnd(newDoc) // This sets the cursor at the end
|
selection: TextSelection.atEnd(newDoc) // This sets the cursor at the end
|
||||||
});
|
});
|
||||||
view.updateState(newState);
|
view.updateState(newState);
|
||||||
|
|
||||||
|
// After updating the state, try to find and select the next template
|
||||||
|
setTimeout(() => {
|
||||||
|
const templateFound = selectNextTemplate(view.state, view.dispatch);
|
||||||
|
if (!templateFound) {
|
||||||
|
// If no template found, set cursor at the end
|
||||||
|
const endPos = view.state.doc.content.size;
|
||||||
|
view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, endPos)));
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy ProseMirror instance on unmount
|
// Destroy ProseMirror instance on unmount
|
||||||
|
Loading…
Reference in New Issue
Block a user