Skip to content

Commit 39c0c2b

Browse files
committed
frontend/latex/output: split auto sync into forward/inverse buttons to control both directions
1 parent 38ead0c commit 39c0c2b

24 files changed

+185
-81
lines changed

src/packages/frontend/components/icon.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ import {
192192
StopFilled,
193193
StopOutlined,
194194
StrikethroughOutlined,
195+
SwapLeftOutlined,
195196
SwapOutlined,
197+
SwapRightOutlined,
196198
SyncOutlined,
197199
TableOutlined,
198200
TagFilled,
@@ -599,6 +601,8 @@ const IconSpec = {
599601
ungroup: { IconFont: "ungroup" },
600602
"signature-outlined": SignatureOutlined,
601603
swap: SwapOutlined,
604+
"sync-left": SwapLeftOutlined,
605+
"sync-right": SwapRightOutlined,
602606
unlink: { IconFont: "unlink" },
603607
upload: UploadOutlined,
604608
user: UserOutlined,

src/packages/frontend/frame-editors/latex-editor/actions.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,20 +1413,20 @@ export class Actions extends BaseActions<LatexEditorState> {
14131413
}
14141414
}
14151415

1416-
// Check if auto-sync is enabled for any output panel
1417-
private is_auto_sync_enabled(): boolean {
1416+
// Check if forward auto-sync (CM → PDF) is enabled for any output panel
1417+
private is_auto_sync_forward_enabled(): boolean {
14181418
const local_view_state = this.store.get("local_view_state");
14191419
if (!local_view_state) return false;
14201420

1421-
// Check all output panels for auto-sync enabled
1421+
// Check all output panels for forward auto-sync enabled
14221422
for (const [key, value] of local_view_state.entrySeq()) {
14231423
// Only check output panels
14241424
if (this._is_output_panel(key) && value) {
1425-
const autoSyncEnabled =
1425+
const autoSyncForward =
14261426
typeof value.get === "function"
1427-
? value.get("autoSyncEnabled")
1428-
: value.autoSyncEnabled;
1429-
if (autoSyncEnabled) {
1427+
? value.get("autoSyncForward")
1428+
: value.autoSyncForward;
1429+
if (autoSyncForward) {
14301430
return true;
14311431
}
14321432
}
@@ -1446,7 +1446,7 @@ export class Actions extends BaseActions<LatexEditorState> {
14461446

14471447
// Handle cursor movement - called by BaseActions.set_cursor_locs
14481448
public handle_cursor_move(locs: any[]): void {
1449-
if (!this.is_auto_sync_enabled() || locs.length === 0) return;
1449+
if (!this.is_auto_sync_forward_enabled() || locs.length === 0) return;
14501450

14511451
// Prevent duplicate sync operations
14521452
if (this.is_auto_sync_in_progress()) return;

src/packages/frontend/frame-editors/latex-editor/output-control-pages.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import { COLORS } from "@cocalc/util/theme";
1515

1616
import { Actions } from "./actions";
1717

18+
// Tweak it in such a way, that it looks consistent with ./*-sync.tsx left/right arrows
19+
const CONTROL_BUTTON_PADDING = "0 14px";
20+
1821
const CONTROL_PAGE_STYLE = {
1922
display: "flex",
2023
alignItems: "center",
@@ -100,6 +103,7 @@ export function PageNavigationControls({
100103
icon={<Icon name="arrow-up" />}
101104
onClick={() => flipPage(-1)}
102105
disabled={currentPage <= 1}
106+
style={{ padding: CONTROL_BUTTON_PADDING }}
103107
/>
104108
</Tip>
105109

@@ -109,6 +113,7 @@ export function PageNavigationControls({
109113
icon={<Icon name="arrow-down" />}
110114
onClick={() => flipPage(1)}
111115
disabled={currentPage >= totalPages}
116+
style={{ padding: CONTROL_BUTTON_PADDING }}
112117
/>
113118
</Tip>
114119
</Space.Compact>

src/packages/frontend/frame-editors/latex-editor/output-control-sync.tsx

Lines changed: 94 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,44 @@
55

66
/*
77
Sync Controls Component for LaTeX Editor Output Panel
8-
Provides auto-sync toggle and manual sync functionality between source and PDF
8+
Provides auto-sync toggle buttons for bidirectional sync between source and PDF
99
*/
1010

11-
import { Button, Switch } from "antd";
11+
import { Space } from "antd";
1212
import { useCallback, useEffect, useState } from "react";
1313
import { defineMessage, useIntl } from "react-intl";
1414

15+
import { Button as BSButton } from "@cocalc/frontend/antd-bootstrap";
1516
import { useRedux } from "@cocalc/frontend/app-framework";
1617
import { HelpIcon, Icon, Tip } from "@cocalc/frontend/components";
17-
import { labels } from "@cocalc/frontend/i18n";
1818

1919
import { Actions } from "./actions";
2020

21-
export const AUTO_SYNC_TOOLTIP_MSG = defineMessage({
22-
id: "editor.latex.pdf_controls.auto_sync.tooltip",
21+
// Tweak it in such a way, that it looks consistent with ./*-pages.tsx up/down arrows
22+
const CONTROL_BUTTON_PADDING = "0 8px";
23+
24+
const FORWARD_SYNC_TOOLTIP_MSG = defineMessage({
25+
id: "editor.latex.pdf_controls.forward_sync.tooltip",
26+
defaultMessage: "Auto-sync from source to PDF: cursor moves scroll the PDF",
27+
description:
28+
"Tooltip explaining forward auto-sync (CM → PDF) in LaTeX PDF controls",
29+
});
30+
31+
const INVERSE_SYNC_TOOLTIP_MSG = defineMessage({
32+
id: "editor.latex.pdf_controls.inverse_sync.tooltip",
2333
defaultMessage:
24-
"Auto-sync between source and PDF: cursor moves follow PDF scrolling, PDF scrolls to cursor position",
34+
"Auto-sync from PDF to source: PDF scrolling moves the cursor",
2535
description:
26-
"Tooltip explaining bidirectional auto-sync functionality in LaTeX PDF controls",
36+
"Tooltip explaining inverse auto-sync (PDF → CM) in LaTeX PDF controls",
2737
});
2838

29-
export const SYNC_HELP_MSG = {
39+
const SYNC_BUTTON_TOOLTIP_MSG = defineMessage({
40+
id: "editor.latex.pdf_controls.sync_button.tooltip",
41+
defaultMessage: "One-time inverse sync to the source editor",
42+
description: "Tooltip for manual sync button in LaTeX PDF controls",
43+
});
44+
45+
const SYNC_HELP_MSG = {
3046
title: defineMessage({
3147
id: "editor.latex.pdf_controls.sync_help.title",
3248
defaultMessage: "LaTeX Sync Help",
@@ -37,12 +53,12 @@ export const SYNC_HELP_MSG = {
3753
defaultMessage: `<p><strong>Manual Mode:</strong></p>
3854
<ul>
3955
<li>Use ALT+Return in source document to jump to corresponding PDF location</li>
40-
<li>Double-click in PDF for inverse search to source</li>
56+
<li>Double-click in PDF or the "Sync" button for inverse search to source</li>
4157
</ul>
4258
<p><strong>Automatic Mode:</strong></p>
4359
<ul>
44-
<li>Syncs automatically from cursor changes in source to PDF</li>
45-
<li>Moving the PDF viewport moves the cursor in source</li>
60+
<li>Forward Sync (→): Syncs automatically from cursor changes in source to PDF</li>
61+
<li>Inverse Sync (←): Moving the PDF viewport moves the cursor in source</li>
4662
</ul>
4763
<p>This functionality uses SyncTeX to coordinate between LaTeX source and PDF output.</p>`,
4864
description:
@@ -76,13 +92,17 @@ export function SyncControls({
7692
const intl = useIntl();
7793

7894
// Auto-sync state (stored in local view state)
79-
const storedAutoSyncEnabled =
80-
useRedux([actions.name, "local_view_state", id, "autoSyncEnabled"]) ??
81-
false; // Default to false
95+
const storedAutoSyncForward =
96+
useRedux([actions.name, "local_view_state", id, "autoSyncForward"]) ??
97+
false;
8298

83-
const [localAutoSyncEnabled, setLocalAutoSyncEnabled] = useState(
84-
storedAutoSyncEnabled,
85-
);
99+
const storedAutoSyncInverse =
100+
useRedux([actions.name, "local_view_state", id, "autoSyncInverse"]) ??
101+
false;
102+
103+
const [autoSyncForward, setAutoSyncForward] = useState(storedAutoSyncForward);
104+
105+
const [autoSyncInverse, setAutoSyncInverse] = useState(storedAutoSyncInverse);
86106

87107
// Check if auto sync is in progress
88108
const autoSyncInProgress =
@@ -126,53 +146,73 @@ export function SyncControls({
126146

127147
// Sync state with stored values when they change
128148
useEffect(() => {
129-
setLocalAutoSyncEnabled(storedAutoSyncEnabled);
130-
}, [storedAutoSyncEnabled]);
149+
setAutoSyncForward(storedAutoSyncForward);
150+
}, [storedAutoSyncForward]);
151+
152+
useEffect(() => {
153+
setAutoSyncInverse(storedAutoSyncInverse);
154+
}, [storedAutoSyncInverse]);
131155

132-
// Auto-sync effect when viewport changes and auto-sync is enabled
156+
// Auto-sync effect when viewport changes and inverse auto-sync is enabled
133157
useEffect(() => {
134-
if (localAutoSyncEnabled && viewportInfo && !autoSyncInProgress) {
158+
if (autoSyncInverse && viewportInfo && !autoSyncInProgress) {
135159
handleViewportSync(viewportInfo.page, viewportInfo.x, viewportInfo.y);
136160
}
137-
}, [
138-
localAutoSyncEnabled,
139-
viewportInfo,
140-
autoSyncInProgress,
141-
handleViewportSync,
142-
]);
143-
144-
const handleAutoSyncChange = (enabled: boolean) => {
145-
setLocalAutoSyncEnabled(enabled);
146-
// Save to local view state for persistence
161+
}, [autoSyncInverse, viewportInfo, autoSyncInProgress, handleViewportSync]);
162+
163+
function handleAutoSyncChange(type: "autoSyncForward" | "autoSyncInverse") {
164+
const forward = type === "autoSyncForward";
165+
const enabled = !(forward ? autoSyncForward : autoSyncInverse);
166+
(forward ? setAutoSyncForward : setAutoSyncInverse)(enabled);
147167
const local_view_state = actions.store.get("local_view_state");
148168
actions.setState({
149-
local_view_state: local_view_state.setIn(
150-
[id, "autoSyncEnabled"],
151-
enabled,
152-
),
169+
local_view_state: local_view_state.setIn([id, type], enabled),
153170
});
154-
};
171+
}
155172

156173
return (
157174
<div style={{ display: "flex", alignItems: "center", gap: "5px" }}>
158-
{!narrow && <Icon name="exchange" />}
159-
<Tip title={intl.formatMessage(AUTO_SYNC_TOOLTIP_MSG)} placement="top">
160-
<Switch
161-
checked={localAutoSyncEnabled}
162-
onChange={handleAutoSyncChange}
163-
checkedChildren={intl.formatMessage(labels.on)}
164-
unCheckedChildren={intl.formatMessage(labels.off)}
165-
/>
166-
</Tip>
167-
<Button
168-
type="text"
169-
size="small"
170-
style={{ fontSize: "13px", padding: "0 4px", height: "auto" }}
171-
onClick={handleManualSync}
172-
disabled={pageDimensions.length === 0}
173-
>
174-
Sync
175-
</Button>
175+
<Space.Compact>
176+
<Tip
177+
title={intl.formatMessage(INVERSE_SYNC_TOOLTIP_MSG)}
178+
placement="top"
179+
>
180+
<BSButton
181+
active={autoSyncInverse}
182+
bsSize="xsmall"
183+
onClick={() => handleAutoSyncChange("autoSyncInverse")}
184+
style={{ padding: CONTROL_BUTTON_PADDING }}
185+
>
186+
<Icon name="sync-left" />
187+
</BSButton>
188+
</Tip>
189+
<Tip
190+
title={intl.formatMessage(FORWARD_SYNC_TOOLTIP_MSG)}
191+
placement="top"
192+
>
193+
<BSButton
194+
active={autoSyncForward}
195+
bsSize="xsmall"
196+
onClick={() => handleAutoSyncChange("autoSyncForward")}
197+
style={{ padding: CONTROL_BUTTON_PADDING }}
198+
>
199+
<Icon name="sync-left" rotate="180" />
200+
</BSButton>
201+
</Tip>
202+
<Tip
203+
title={intl.formatMessage(SYNC_BUTTON_TOOLTIP_MSG)}
204+
placement="top"
205+
>
206+
<BSButton
207+
bsSize="xsmall"
208+
style={{ padding: CONTROL_BUTTON_PADDING }}
209+
onClick={handleManualSync}
210+
disabled={pageDimensions.length === 0}
211+
>
212+
Sync
213+
</BSButton>
214+
</Tip>
215+
</Space.Compact>
176216
{!narrow && (
177217
<HelpIcon
178218
title={intl.formatMessage(SYNC_HELP_MSG.title)}

src/packages/frontend/frame-editors/latex-editor/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import { webapp_client } from "@cocalc/frontend/webapp-client";
1010
import { ExecOptsBlocking } from "@cocalc/util/db-schema/projects";
1111
import { separate_file_extension } from "@cocalc/util/misc";
1212
import { ExecuteCodeOutputAsync } from "@cocalc/util/types/execute-code";
13-
import { TIMEOUT_LATEX_JOB_S } from "./constants";
1413
import { TITLE_BAR_BORDER } from "../frame-tree/style";
14+
import { TIMEOUT_LATEX_JOB_S } from "./constants";
1515

1616
export const OUTPUT_HEADER_STYLE = {
1717
display: "flex",

src/packages/frontend/i18n/trans/ar_EG.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,10 @@
592592
"editor.latex.command.print.label": "طباعة مصدر LaTeX",
593593
"editor.latex.command.print.tooltip": "اطبع الشيفرة المصدرية لهذا المستند. استخدم الطباعة من إطار معاينة PDF لطباعة المستند المُعرض.",
594594
"editor.latex.pdf_controls.auto_sync.tooltip": "المزامنة التلقائية بين المصدر وPDF: يتحرك المؤشر مع تمرير PDF، يتم تمرير PDF إلى موضع المؤشر",
595-
"editor.latex.pdf_controls.sync_help.content": "<p><strong>الوضع اليدوي:</strong></p> <ul> <li>استخدم ALT+Return في المستند المصدر للقفز إلى الموقع المقابل في PDF</li> <li>انقر نقرًا مزدوجًا في PDF للبحث العكسي إلى المصدر</li> </ul> <p><strong>الوضع التلقائي:</strong></p> <ul> <li>يقوم بالمزامنة تلقائيًا من تغييرات المؤشر في المصدر إلى PDF</li> <li>تحريك نافذة عرض PDF يحرك المؤشر في المصدر</li> </ul> <p>تستخدم هذه الوظيفة SyncTeX للتنسيق بين مصدر LaTeX وإخراج PDF.</p>",
595+
"editor.latex.pdf_controls.forward_sync.tooltip": "المزامنة التلقائية من المصدر إلى PDF: تحريك المؤشر يحرك ملف PDF",
596+
"editor.latex.pdf_controls.inverse_sync.tooltip": "المزامنة التلقائية من PDF إلى المصدر: تحريك التمرير في PDF يحرك المؤشر",
597+
"editor.latex.pdf_controls.sync_button.tooltip": "مزامنة عكسية لمرة واحدة إلى محرر المصدر",
598+
"editor.latex.pdf_controls.sync_help.content": "<p><strong>الوضع اليدوي:</strong></p> <ul> <li>استخدم ALT+Return في المستند المصدر للقفز إلى الموقع المقابل في PDF</li> <li>انقر نقرًا مزدوجًا في PDF أو على زر \"المزامنة\" للبحث العكسي في المصدر</li> </ul> <p><strong>الوضع التلقائي:</strong></p> <ul> <li>المزامنة الأمامية (→): تقوم بالمزامنة تلقائيًا من تغييرات المؤشر في المصدر إلى PDF</li> <li>المزامنة العكسية (←): تحريك عرض PDF يحرك المؤشر في المصدر</li> </ul> <p>تستخدم هذه الوظيفة SyncTeX للتنسيق بين مصدر LaTeX ومخرجات PDF.</p>",
596599
"editor.latex.pdf_controls.sync_help.title": "مساعدة مزامنة LaTeX",
597600
"editor.latex.pdf_embed.title": "PDF - أصلي",
598601
"editor.latex.pdf_embed.title.short": "PDF (native)",

src/packages/frontend/i18n/trans/de_DE.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,10 @@
592592
"editor.latex.command.print.label": "LaTeX-Code drucken",
593593
"editor.latex.command.print.tooltip": "Drucken Sie den Quellcode dieses Dokuments. Verwenden Sie Drucken aus dem PDF-Vorschau-Fenster, um das gerenderte Dokument zu drucken.",
594594
"editor.latex.pdf_controls.auto_sync.tooltip": "Automatische Synchronisation zwischen Quelle und PDF: Der Cursor bewegt sich beim Scrollen im PDF, das PDF scrollt zur Cursorposition",
595-
"editor.latex.pdf_controls.sync_help.content": "<p><strong>Manueller Modus:</strong></p> <ul> <li>Verwenden Sie ALT+Return im Quelldokument, um zur entsprechenden PDF-Position zu springen</li> <li>Doppelklicken Sie im PDF für die inverse Suche zur Quelle</li> </ul> <p><strong>Automatischer Modus:</strong></p> <ul> <li>Synchronisiert automatisch von Cursoränderungen in der Quelle zum PDF</li> <li>Das Verschieben des PDF-Viewports bewegt den Cursor in der Quelle</li> </ul> <p>Diese Funktionalität verwendet SyncTeX, um zwischen LaTeX-Quelle und PDF-Ausgabe zu koordinieren.</p>",
595+
"editor.latex.pdf_controls.forward_sync.tooltip": "Automatische Synchronisierung vom Quelltext zum PDF: Cursorbewegungen scrollen das PDF",
596+
"editor.latex.pdf_controls.inverse_sync.tooltip": "Automatische Synchronisierung von PDF zur Quelle: PDF-Scrollen bewegt den Cursor",
597+
"editor.latex.pdf_controls.sync_button.tooltip": "Einmalige inverse Synchronisation mit dem Quell-Editor",
598+
"editor.latex.pdf_controls.sync_help.content": "<p><strong>Manueller Modus:</strong></p> <ul> <li>Verwenden Sie ALT+Return im Quelldokument, um zur entsprechenden PDF-Position zu springen</li> <li>Doppelklicken Sie im PDF oder auf die Schaltfläche \"Sync\" für die inverse Suche zur Quelle</li> </ul> <p><strong>Automatischer Modus:</strong></p> <ul> <li>Vorwärtssynchronisierung (→): Synchronisiert automatisch von Cursoränderungen in der Quelle zum PDF</li> <li>Inverse Synchronisierung (←): Verschieben des PDF-Ansichtsbereichs verschiebt den Cursor in der Quelle</li> </ul> <p>Diese Funktionalität verwendet SyncTeX, um zwischen LaTeX-Quelle und PDF-Ausgabe zu koordinieren.</p>",
596599
"editor.latex.pdf_controls.sync_help.title": "LaTeX-Synchronisierungshilfe",
597600
"editor.latex.pdf_embed.title": "PDF - Native",
598601
"editor.latex.pdf_embed.title.short": "PDF (nativ)",

src/packages/frontend/i18n/trans/es_ES.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,10 @@
592592
"editor.latex.command.print.label": "Imprimir fuente LaTeX",
593593
"editor.latex.command.print.tooltip": "Imprime el código fuente de este documento. Usa Imprimir desde el marco de Vista previa de PDF para imprimir el documento renderizado.",
594594
"editor.latex.pdf_controls.auto_sync.tooltip": "Auto-sincronización entre la fuente y el PDF: el movimiento del cursor sigue el desplazamiento del PDF, el PDF se desplaza a la posición del cursor",
595-
"editor.latex.pdf_controls.sync_help.content": "<p><strong>Modo Manual:</strong></p> <ul> <li>Usa ALT+Return en el documento fuente para saltar a la ubicación correspondiente en el PDF</li> <li>Haz doble clic en el PDF para realizar una búsqueda inversa hacia la fuente</li> </ul> <p><strong>Modo Automático:</strong></p> <ul> <li>Se sincroniza automáticamente desde los cambios de cursor en la fuente al PDF</li> <li>Mover la vista del PDF mueve el cursor en la fuente</li> </ul> <p>Esta funcionalidad utiliza SyncTeX para coordinar entre la fuente LaTeX y la salida en PDF.</p>",
595+
"editor.latex.pdf_controls.forward_sync.tooltip": "Auto-sincronización de fuente a PDF: el cursor mueve el desplazamiento del PDF",
596+
"editor.latex.pdf_controls.inverse_sync.tooltip": "Sincronización automática de PDF a fuente: el desplazamiento del PDF mueve el cursor",
597+
"editor.latex.pdf_controls.sync_button.tooltip": "Sincronización inversa única con el editor de origen",
598+
"editor.latex.pdf_controls.sync_help.content": "<p><strong>Modo Manual:</strong></p> <ul> <li>Usa ALT+Return en el documento fuente para saltar a la ubicación correspondiente en el PDF</li> <li>Haz doble clic en el PDF o en el botón \"Sincronizar\" para buscar inversamente en la fuente</li> </ul> <p><strong>Modo Automático:</strong></p> <ul> <li>Sincronización directa (→): Sincroniza automáticamente los cambios de cursor en la fuente al PDF</li> <li>Sincronización inversa (←): Mover la vista del PDF mueve el cursor en la fuente</li> </ul> <p>Esta funcionalidad utiliza SyncTeX para coordinar entre la fuente LaTeX y la salida PDF.</p>",
596599
"editor.latex.pdf_controls.sync_help.title": "Ayuda de sincronización de LaTeX",
597600
"editor.latex.pdf_embed.title": "PDF - Nativo",
598601
"editor.latex.pdf_embed.title.short": "PDF (nativo)",

0 commit comments

Comments
 (0)