mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-25 01:58:39 +02:00
Fix response viewer stream scrolling
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { activeRequestAtom } from '../hooks/useActiveRequest';
|
||||||
import { useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
import { useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||||
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
|
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
|
||||||
import { useHotKey, useSubscribeHotKeys } from '../hooks/useHotKey';
|
import { useHotKey, useSubscribeHotKeys } from '../hooks/useHotKey';
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
}, [allEnvironmentVariables, autocompleteVariables]);
|
}, [allEnvironmentVariables, autocompleteVariables]);
|
||||||
// Track a local key for updates. If the default value is changed when the input is not in focus,
|
// Track a local key for updates. If the default value is changed when the input is not in focus,
|
||||||
// regenerate this to force the field to update.
|
// regenerate this to force the field to update.
|
||||||
const [focusedUpdateKey, regenerateFocusedUpdateKey] = useRandomKey();
|
const [focusedUpdateKey, regenerateFocusedUpdateKey] = useRandomKey('initial');
|
||||||
const forceUpdateKey = `${forceUpdateKeyFromAbove}::${focusedUpdateKey}`;
|
const forceUpdateKey = `${forceUpdateKeyFromAbove}::${focusedUpdateKey}`;
|
||||||
|
|
||||||
if (settings && wrapLines === undefined) {
|
if (settings && wrapLines === undefined) {
|
||||||
@@ -352,17 +352,6 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Force input to update when receiving change and not in focus
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
const currDoc = cm.current?.view.state.doc.toString() || '';
|
|
||||||
const nextDoc = defaultValue || '';
|
|
||||||
const notFocused = !cm.current?.view.hasFocus;
|
|
||||||
const hasChanged = currDoc !== nextDoc;
|
|
||||||
if (notFocused && hasChanged) {
|
|
||||||
regenerateFocusedUpdateKey();
|
|
||||||
}
|
|
||||||
}, [defaultValue, regenerateFocusedUpdateKey]);
|
|
||||||
|
|
||||||
const [, { focusParamValue }] = useRequestEditor();
|
const [, { focusParamValue }] = useRequestEditor();
|
||||||
const onClickPathParameter = useCallback(
|
const onClickPathParameter = useCallback(
|
||||||
async (name: string) => {
|
async (name: string) => {
|
||||||
@@ -487,33 +476,24 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
// For read-only mode, update content when `defaultValue` changes
|
// For read-only mode, update content when `defaultValue` changes
|
||||||
useEffect(
|
useEffect(
|
||||||
function updateReadOnlyEditor() {
|
function updateReadOnlyEditor() {
|
||||||
if (!readOnly || cm.current?.view == null || defaultValue == null) return;
|
if (readOnly && cm.current?.view != null) {
|
||||||
|
updateContents(cm.current.view, defaultValue || '');
|
||||||
// Replace codemirror contents
|
|
||||||
const currentDoc = cm.current.view.state.doc.toString();
|
|
||||||
if (defaultValue.startsWith(currentDoc)) {
|
|
||||||
// If we're just appending, append only the changes. This preserves
|
|
||||||
// things like scroll position.
|
|
||||||
cm.current.view.dispatch({
|
|
||||||
changes: cm.current.view.state.changes({
|
|
||||||
from: currentDoc.length,
|
|
||||||
insert: defaultValue.slice(currentDoc.length),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// If we're replacing everything, reset the entire content
|
|
||||||
cm.current.view.dispatch({
|
|
||||||
changes: cm.current.view.state.changes({
|
|
||||||
from: 0,
|
|
||||||
to: currentDoc.length,
|
|
||||||
insert: defaultValue,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[defaultValue, readOnly],
|
[defaultValue, readOnly],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Force input to update when receiving change and not in focus
|
||||||
|
useLayoutEffect(
|
||||||
|
function updateNonFocusedEditor() {
|
||||||
|
const notFocused = !cm.current?.view.hasFocus;
|
||||||
|
if (notFocused && cm.current != null) {
|
||||||
|
updateContents(cm.current.view, defaultValue || '');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[defaultValue, readOnly, regenerateFocusedUpdateKey],
|
||||||
|
);
|
||||||
|
|
||||||
// Add bg classes to actions, so they appear over the text
|
// Add bg classes to actions, so they appear over the text
|
||||||
const decoratedActions = useMemo(() => {
|
const decoratedActions = useMemo(() => {
|
||||||
const results = [];
|
const results = [];
|
||||||
@@ -720,3 +700,30 @@ function getCachedEditorState(doc: string, stateKey: string | null) {
|
|||||||
function computeFullStateKey(stateKey: string): string {
|
function computeFullStateKey(stateKey: string): string {
|
||||||
return `editor.${stateKey}`;
|
return `editor.${stateKey}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateContents(view: EditorView, text: string) {
|
||||||
|
// Replace codemirror contents
|
||||||
|
const currentDoc = view.state.doc.toString();
|
||||||
|
|
||||||
|
if (currentDoc === text) {
|
||||||
|
return;
|
||||||
|
} else if (text.startsWith(currentDoc)) {
|
||||||
|
// If we're just appending, append only the changes. This preserves
|
||||||
|
// things like scroll position.
|
||||||
|
view.dispatch({
|
||||||
|
changes: view.state.changes({
|
||||||
|
from: currentDoc.length,
|
||||||
|
insert: text.slice(currentDoc.length),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If we're replacing everything, reset the entire content
|
||||||
|
view.dispatch({
|
||||||
|
changes: view.state.changes({
|
||||||
|
from: 0,
|
||||||
|
to: currentDoc.length,
|
||||||
|
insert: text,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export function TextViewer({ language, text, response, requestId, pretty, classN
|
|||||||
language={language}
|
language={language}
|
||||||
actions={actions}
|
actions={actions}
|
||||||
extraExtensions={extraExtensions}
|
extraExtensions={extraExtensions}
|
||||||
stateKey={null}
|
stateKey={'response.body.' + response.id}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { generateId } from '../lib/generateId';
|
import { generateId } from '../lib/generateId';
|
||||||
|
|
||||||
export function useRandomKey() {
|
export function useRandomKey(initialValue?: string) {
|
||||||
const [value, setValue] = useState<string>(generateId());
|
const [value, setValue] = useState<string>(initialValue ?? generateId());
|
||||||
const regenerate = useCallback(() => setValue(generateId()), []);
|
const regenerate = useCallback(() => setValue(generateId()), []);
|
||||||
return [value, regenerate] as const;
|
return [value, regenerate] as const;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export function useResponseBodyText({
|
|||||||
filter: string | null;
|
filter: string | null;
|
||||||
}) {
|
}) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
|
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||||
queryKey: [
|
queryKey: [
|
||||||
'response_body_text',
|
'response_body_text',
|
||||||
response.id,
|
response.id,
|
||||||
|
|||||||
Reference in New Issue
Block a user