Skip to content

Commit c65c367

Browse files
committed
Fix panel transition flicker
1 parent f74fa77 commit c65c367

8 files changed

Lines changed: 90 additions & 38 deletions

File tree

‎anycode/App.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,11 @@ const App: React.FC = () => {
187187
const handleOpenFile = useEvent((
188188
path: string, line?: number, column?: number, mode?: DiffMode,
189189
) => {
190+
const keepPreviousEditor = layoutActionsRef.current
191+
?.isEditorPanelVisible(editors.activeEditorPaneId) ?? false;
190192
const paneId = resolveEditorPaneId();
191193
if (!paneId) return;
192-
editors.openFile(path, line, column, paneId, mode);
194+
editors.openFile(path, line, column, paneId, mode, keepPreviousEditor);
193195
});
194196

195197
const handleSelectFile = useEvent((fileId: string) => {

‎anycode/components/Search.css‎

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@
126126
.search-input-wrapper {
127127
display: grid;
128128
position: relative;
129-
margin: 8px 8px 0px 8px;
129+
margin: 8px;
130+
container-type: inline-size;
130131
}
131132

132133
.search-prefix {
@@ -157,10 +158,6 @@
157158
-webkit-touch-callout: default;
158159
}
159160

160-
.search-input-wrapper-compact .search-input {
161-
padding-right: 52px;
162-
}
163-
164161
.search-actions {
165162
display: flex;
166163
align-items: center;
@@ -270,9 +267,27 @@
270267
padding: 0 6px;
271268
}
272269

273-
.search-input-wrapper-compact .search-mode-button {
274-
min-width: 18px;
275-
padding: 0 5px;
270+
.search-mode-label-short {
271+
display: none;
272+
}
273+
274+
@container (max-width: 249px) {
275+
.search-input {
276+
padding-right: 52px;
277+
}
278+
279+
.search-mode-button {
280+
min-width: 18px;
281+
padding: 0 5px;
282+
}
283+
284+
.search-mode-label-long {
285+
display: none;
286+
}
287+
288+
.search-mode-label-short {
289+
display: inline;
290+
}
276291
}
277292

278293
.search-mode-button:hover,

‎anycode/components/Search.tsx‎

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,6 @@ const Search = ({ id, wsRef, isConnected, focusRequestToken, inputValue, onInput
231231
const [fileResults, setFileResults] = useState<FileSearchResult[]>([]);
232232
const [filesSearchEnded, setFilesSearchEnded] = useState(true);
233233
const [filesSearchError, setFilesSearchError] = useState<string | null>(null);
234-
const [isModeToggleCompact, setIsModeToggleCompact] = useState(false);
235-
const inputWrapperRef = useRef<HTMLDivElement | null>(null);
236234
const inputRef = useRef<HTMLTextAreaElement>(null);
237235
const startTimeRef = useRef<number | null>(null);
238236
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
@@ -298,20 +296,6 @@ const Search = ({ id, wsRef, isConnected, focusRequestToken, inputValue, onInput
298296
inputRef.current?.focus();
299297
}, [focusRequestToken]);
300298

301-
useEffect(() => {
302-
const el = inputWrapperRef.current;
303-
if (!el) return;
304-
305-
const updateCompactMode = () => {
306-
setIsModeToggleCompact(el.clientWidth < 250);
307-
};
308-
309-
updateCompactMode();
310-
const resizeObserver = new ResizeObserver(updateCompactMode);
311-
resizeObserver.observe(el);
312-
return () => resizeObserver.disconnect();
313-
}, []);
314-
315299
useEffect(() => {
316300
const el = resultsRef.current;
317301
if (!el) return;
@@ -818,8 +802,7 @@ const Search = ({ id, wsRef, isConnected, focusRequestToken, inputValue, onInput
818802
<div className="search-container">
819803

820804
<div
821-
ref={inputWrapperRef}
822-
className={`search-input-wrapper ${isModeToggleCompact ? "search-input-wrapper-compact" : ""}`}
805+
className="search-input-wrapper"
823806
>
824807
<textarea
825808
className="search-input"
@@ -840,7 +823,8 @@ const Search = ({ id, wsRef, isConnected, focusRequestToken, inputValue, onInput
840823
role="tab"
841824
aria-selected={searchMode === "content"}
842825
>
843-
{isModeToggleCompact ? "C" : "Content"}
826+
<span className="search-mode-label-long">Content</span>
827+
<span className="search-mode-label-short">C</span>
844828
</button>
845829
<button
846830
type="button"
@@ -849,7 +833,8 @@ const Search = ({ id, wsRef, isConnected, focusRequestToken, inputValue, onInput
849833
role="tab"
850834
aria-selected={searchMode === "files"}
851835
>
852-
{isModeToggleCompact ? "F" : "Files"}
836+
<span className="search-mode-label-long">Files</span>
837+
<span className="search-mode-label-short">F</span>
853838
</button>
854839
</div>
855840
</div>

‎anycode/components/layout/Layout.tsx‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ type LayoutProps = {
294294

295295
export type LayoutActions = {
296296
ensureEditorPanel: (preferredPanelId?: string | null) => string | null;
297+
isEditorPanelVisible: (preferredPanelId?: string | null) => boolean;
297298
ensurePanel: (panelId: PanelId) => string | null;
298299
};
299300

@@ -831,7 +832,7 @@ export const Layout: React.FC<LayoutProps> = ({
831832
renderPanelRef.current(panelId, panelKey)
832833
), []);
833834

834-
const ensureEditorPanel = useCallback((preferredPanelId?: string | null): string | null => {
835+
const findEditorPanel = useCallback((preferredPanelId?: string | null): IDockviewPanel | null => {
835836
const api = apiRef.current;
836837
if (!api) {
837838
return null;
@@ -842,7 +843,20 @@ export const Layout: React.FC<LayoutProps> = ({
842843
const activeEditorPanel = activePanel && getPanelBaseId(activePanel.id) === 'editor'
843844
? activePanel
844845
: null;
845-
const existingPanel = preferredPanel ?? activeEditorPanel ?? getPanelsByBaseId(api, 'editor')[0];
846+
return preferredPanel ?? activeEditorPanel ?? getPanelsByBaseId(api, 'editor')[0] ?? null;
847+
}, []);
848+
849+
const isEditorPanelVisible = useCallback((preferredPanelId?: string | null): boolean => (
850+
findEditorPanel(preferredPanelId)?.api.isVisible ?? false
851+
), [findEditorPanel]);
852+
853+
const ensureEditorPanel = useCallback((preferredPanelId?: string | null): string | null => {
854+
const api = apiRef.current;
855+
if (!api) {
856+
return null;
857+
}
858+
859+
const existingPanel = findEditorPanel(preferredPanelId);
846860
if (existingPanel) {
847861
existingPanel.api.setActive();
848862
return existingPanel.id;
@@ -873,7 +887,7 @@ export const Layout: React.FC<LayoutProps> = ({
873887
editorPanel.group.api.setConstraints(PANEL_CONSTRAINTS);
874888
editorPanel.api.setActive();
875889
return editorPanel.id;
876-
}, [resolvePanelContent]);
890+
}, [findEditorPanel, resolvePanelContent]);
877891

878892
const ensurePanel = useCallback((panelId: PanelId): string | null => {
879893
const api = apiRef.current;
@@ -912,13 +926,14 @@ export const Layout: React.FC<LayoutProps> = ({
912926
useEffect(() => {
913927
onActionsReady?.({
914928
ensureEditorPanel,
929+
isEditorPanelVisible,
915930
ensurePanel,
916931
});
917932

918933
return () => {
919934
onActionsReady?.(null);
920935
};
921-
}, [ensureEditorPanel, ensurePanel, onActionsReady]);
936+
}, [ensureEditorPanel, ensurePanel, isEditorPanelVisible, onActionsReady]);
922937

923938
const disposeListeners = useCallback(() => {
924939
for (const listener of listenersRef.current) {

‎anycode/components/terminal/Terminal.tsx‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ const Terminal: React.FC<XTerminalProps> = ({
8282
const saveSnapshotTimerRef = useRef<number | null>(null);
8383
const themeObserverRef = useRef<MutationObserver | null>(null);
8484
const didAutoFocusRef = useRef<boolean>(false);
85+
const wasContainerVisibleRef = useRef(false);
8586
const onDataRef = useRef(onData);
8687
const onResizeRef = useRef(onResize);
8788
const isTerminalClosingRef = useRef(isTerminalClosing);
@@ -299,6 +300,30 @@ const Terminal: React.FC<XTerminalProps> = ({
299300
});
300301

301302
const resizeObserver = new ResizeObserver(() => {
303+
const container = terminalRef.current;
304+
const isContainerVisible = Boolean(
305+
container && container.clientWidth > 0 && container.clientHeight > 0
306+
);
307+
308+
if (!isContainerVisible) {
309+
wasContainerVisibleRef.current = false;
310+
return;
311+
}
312+
313+
if (!wasContainerVisibleRef.current) {
314+
wasContainerVisibleRef.current = true;
315+
if (fitDebounceTimerRef.current !== null) {
316+
clearTimeout(fitDebounceTimerRef.current);
317+
fitDebounceTimerRef.current = null;
318+
}
319+
if (resizeRafRef.current !== null) {
320+
cancelAnimationFrame(resizeRafRef.current);
321+
resizeRafRef.current = null;
322+
}
323+
fitAddon.fit();
324+
return;
325+
}
326+
302327
if (fitDebounceTimerRef.current !== null) {
303328
clearTimeout(fitDebounceTimerRef.current);
304329
}
@@ -367,6 +392,7 @@ const Terminal: React.FC<XTerminalProps> = ({
367392
resizeObserverRef.current.disconnect();
368393
resizeObserverRef.current = null;
369394
}
395+
wasContainerVisibleRef.current = false;
370396
if (themeObserverRef.current) {
371397
themeObserverRef.current.disconnect();
372398
themeObserverRef.current = null;

‎anycode/features/editor/EditorPanel.tsx‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type EditorPanelProps = {
99
editors: {
1010
files: Array<{ id: string }>;
1111
editorStates: ReadonlyMap<string, AnycodeEditor>;
12+
keepPreviousEditorByPane: Readonly<Record<string, boolean>>;
1213
getActiveFileIdForPane: (paneId: string) => string | null;
1314
setActiveEditorPaneId: (paneId: string) => void;
1415
referencesPeekByPane?: Record<string, ReferencesPeekState | null>;
@@ -44,9 +45,12 @@ export const EditorPanel = ({ panelKey, editors }: EditorPanelProps) => {
4445
const editorForCurrentFile = lastReadyEditor?.id === paneFileId
4546
? lastReadyEditor
4647
: null;
48+
const fallbackEditor = editors.keepPreviousEditorByPane[panelKey]
49+
? lastReadyEditor
50+
: editorForCurrentFile;
4751
const displayedEditor = paneFile && editorState
4852
? { id: paneFile.id, state: editorState }
49-
: editorForCurrentFile;
53+
: fallbackEditor;
5054

5155
return (
5256
<div

‎anycode/hooks/useEditors.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export const useEditors = ({ wsRef, isConnected, onFileClosed }: UseEditorsParam
135135
const editorStatesRef = useRef<Map<string, AnycodeEditor>>(new Map());
136136
const editorRefs = useRef<Map<string, AnycodeEditor>>(new Map());
137137
const initializingEditorsRef = useRef<Map<string, Promise<AnycodeEditor>>>(new Map());
138+
const [keepPreviousEditorByPane, setKeepPreviousEditorByPane] = useState<Record<string, boolean>>({});
138139

139140
const savedFileContentsRef = useRef<Map<string, string>>(new Map());
140141
const previewFileContentsRef = useRef<Map<string, string>>(new Map());
@@ -616,6 +617,7 @@ export const useEditors = ({ wsRef, isConnected, onFileClosed }: UseEditorsParam
616617
column?: number,
617618
paneId?: string,
618619
diffMode?: DiffMode,
620+
keepPreviousEditor = true,
619621
) => {
620622
if (!paneId && !hasVisibleEditorPane()) {
621623
return;
@@ -624,6 +626,11 @@ export const useEditors = ({ wsRef, isConnected, onFileClosed }: UseEditorsParam
624626
const existingFile = filesRef.current.find((file) => file.id === path);
625627
const targetPaneId = resolveTargetPaneId(paneId, existingFile?.id);
626628
const mode = diffMode ?? getEditorDiffMode(targetPaneId);
629+
setKeepPreviousEditorByPane((prev) => (
630+
prev[targetPaneId] === keepPreviousEditor
631+
? prev
632+
: { ...prev, [targetPaneId]: keepPreviousEditor }
633+
));
627634

628635
if (existingFile) {
629636
const editor = editorRefs.current.get(existingFile.id);
@@ -1333,6 +1340,7 @@ export const useEditors = ({ wsRef, isConnected, onFileClosed }: UseEditorsParam
13331340
setEditorDiffMode,
13341341
cycleEditorDiffMode,
13351342
editorStates,
1343+
keepPreviousEditorByPane,
13361344
closeFile,
13371345
saveFile,
13381346
openFile,

‎anycode/hooks/usePersistedScroll.ts‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,7 @@ export function usePersistedScroll<T extends HTMLElement>(
6666
if (panel) {
6767
visibilityDisposable = panel.onDidVisibilityChange((event: any) => {
6868
if (event.isVisible) {
69-
// Wait for layout updates before restoring scroll
70-
requestAnimationFrame(() => {
71-
restoreScroll();
72-
});
69+
restoreScroll();
7370
}
7471
});
7572
}

0 commit comments

Comments
 (0)