Skip to content

Commit 9043f30

Browse files
ktaletskquintfainCbarr-hubJadenMcElveyZX-80
authored
UX improvements (#243)
Co-authored-by: Quint Fain <[email protected]> Co-authored-by: Cbarr-hub <[email protected]> Co-authored-by: Jaden McElvey <[email protected]> Co-authored-by: Jefferson <[email protected]> Co-authored-by: darklordsep1 <[email protected]> Co-authored-by: Roni Ruadap <[email protected]> Co-authored-by: Akshat Saini <[email protected]>
1 parent 9a1d429 commit 9043f30

17 files changed

+1107
-94
lines changed

README.md

+53-6
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,59 @@ A JupyterLab extension for live-editing of LaTeX documents.
1616

1717
## Usage
1818

19-
To use, right-click on an open `.tex` document within JupyterLab, and select `Show LaTeX Preview`:
20-
![preview](images/show_preview.png)
21-
This will compile the `.tex` file and open the rendered PDF document.
22-
Subsequent saves of the file will automatically update the PDF.
23-
If the PDF fails to compile (possibly due to a syntax error),
24-
an error panel will open detailing the LaTeX error.
19+
### Compilation
20+
21+
To compile and preview a LaTeX document:
22+
23+
1. Open a `.tex` document within JupyterLab.
24+
2. Use one of the following methods to compile and preview the document:
25+
- Right-click on the document and select `Show LaTeX Preview` from the context menu.
26+
- Click the `Preview` button in the toolbar at the top of the document.
27+
28+
Both methods will compile the `.tex` file and open the rendered PDF document. Subsequent saves of the file will automatically update the PDF. If the PDF fails to compile (possibly due to a syntax error), an error panel will open detailing the LaTeX error.
29+
30+
### Writing Tools
31+
32+
A toolbar menu at the top of the document provides shortcuts to common LaTeX editing tasks:
33+
34+
#### Text Formatting
35+
36+
- **Subscript**: Highlight the text you want to subscript and click the `Xᵧ` button. If no text is highlighted, an input dialog will appear for you to enter the subscript.
37+
- **Superscript**: Highlight the text you want to superscript and click the `Xⁿ` button. If no text is highlighted, an input dialog will appear for you to enter the superscript.
38+
- **Bold**: Highlight the text you want to format in bold and click the `B` button.
39+
- **Italic**: Highlight the text you want to format in italics and click the `I` button.
40+
- **Underline**: Highlight the text you want to underline and click the `U` button.
41+
42+
#### Text Layout
43+
44+
- **Left Align**: Highlight the text you want to align left and click the left alignment button.
45+
- **Center Align**: Highlight the text you want to center align and click the center alignment button.
46+
- **Right Align**: Highlight the text you want to align right and click the right alignment button.
47+
48+
#### Lists
49+
50+
- **Bullet List**: Click the bullet list button to insert a bullet list.
51+
- **Numbered List**: Click the numbered list button to insert a numbered list.
52+
53+
#### Tables and Plots
54+
55+
- **Table Creation GUI**: Click the table button to open a dialog for creating a table with a specified number of rows and columns.
56+
- **Add Plot**: Click the plot button to select a plot type and insert it into your document. Available plot types include:
57+
- Simple function plot
58+
- Plot from file
59+
- Scatter plot
60+
- Bar graphs
61+
- Contour plot
62+
- Parametric plot
63+
64+
### Error Handling
65+
66+
- **Error Log Filtering Options**: Enhanced error log filtering options to help you quickly identify and resolve issues.
67+
68+
### Main Menu Helpers
69+
70+
- **Constant Menu**: Quickly insert common mathematical constants.
71+
- **Symbol Menu**: Easily insert various mathematical symbols.
2572

2673
For more advanced usage documentation, see [here](docs/advanced.md).
2774

jupyterlab_latex/build.py

+84-2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,89 @@ def bib_condition(self):
160160
"""
161161
return any([re.match(r'.*\.bib', x) for x in set(glob.glob("*"))])
162162

163+
def filter_output(self, latex_output):
164+
"""Filters latex output for "interesting" messages
165+
166+
Parameters
167+
----------
168+
latex_output: string
169+
This is the output of the executed latex command from,
170+
run_command in run_latex.
171+
172+
returns:
173+
A string representing the filtered output.
174+
175+
Notes
176+
-----
177+
- Based on the public domain perl script texfot v 1.43 written by
178+
Karl Berry in 2014. It has no home page beyond the package on
179+
CTAN: <https://ctan.org/pkg/texfot>.
180+
181+
"""
182+
ignore = re.compile(r'''^(
183+
LaTeX\ Warning:\ You\ have\ requested\ package
184+
|LaTeX\ Font\ Warning:\ Some\ font\ shapes
185+
|LaTeX\ Font\ Warning:\ Size\ substitutions
186+
|Package\ auxhook\ Warning:\ Cannot\ patch
187+
|Package\ caption\ Warning:\ Un(supported|known)\ document\ class
188+
|Package\ fixltx2e\ Warning:\ fixltx2e\ is\ not\ required
189+
|Package\ frenchb?\.ldf\ Warning:\ (Figures|The\ definition)
190+
|Package\ layouts\ Warning:\ Layout\ scale
191+
|\*\*\*\ Reloading\ Xunicode\ for\ encoding # spurious ***
192+
|pdfTeX\ warning:.*inclusion:\ fou #nd PDF version ...
193+
|pdfTeX\ warning:.*inclusion:\ mul #tiple pdfs with page group
194+
|libpng\ warning:\ iCCP:\ Not\ recognizing
195+
|!\ $
196+
|This\ is
197+
|No\ pages\ of\ output. # possibly not worth ignoring?
198+
)''', re.VERBOSE)
199+
200+
next_line = re.compile(r'''^(
201+
.*?:[0-9]+: # usual file:lineno: form
202+
|! # usual ! form
203+
|>\ [^<] # from \show..., but not "> <img.whatever"
204+
|.*pdfTeX\ warning # pdftex complaints often cross lines
205+
|LaTeX\ Font\ Warning:\ Font\ shape
206+
|Package\ hyperref\ Warning:\ Token\ not\ allowed
207+
|removed\ on\ input\ line # hyperref
208+
|Runaway\ argument
209+
)''', re.VERBOSE)
210+
211+
show = re.compile(r'''^(
212+
Output\ written
213+
|No\ pages\ of\ output
214+
|\(.*end\ occurred\ inside\ a\ group
215+
|(Und|Ov)erfull
216+
|(LaTeX|Package|Class).*(Error|Warning)
217+
|.*Citation.*undefined
218+
|.*\ Error # as in \Url Error ->...
219+
|Missing\ character: # good to show (need \tracinglostchars=1)
220+
|\\endL.*problem # XeTeX?
221+
|\*\*\*\s # *** from some packages or subprograms
222+
|l\.[0-9]+ # line number marking
223+
|all\ text\ was\ ignored\ after\ line
224+
|.*Fatal\ error
225+
|.*for\ symbol.*on\ input\ line
226+
)''', re.VERBOSE)
227+
228+
print_next = False
229+
filtered_output = []
230+
231+
for line in latex_output.split('\n'):
232+
if print_next:
233+
filtered_output.append(line)
234+
print_next = False
235+
elif ignore.match(line):
236+
continue
237+
238+
elif next_line.match(line):
239+
filtered_output.append(line)
240+
print_next = True
241+
242+
elif show.match(line):
243+
filtered_output.append(line)
244+
245+
return '\n'.join(filtered_output)
163246

164247
@gen.coroutine
165248
def run_latex(self, command_sequence):
@@ -193,7 +276,7 @@ def run_latex(self, command_sequence):
193276
self.set_status(500)
194277
self.log.error((f'LaTeX command `{" ".join(cmd)}` '
195278
f'errored with code: {code}'))
196-
return output
279+
return json.dumps({'fullMessage':output, 'errorOnlyMessage':self.filter_output(output)})
197280

198281
return "LaTeX compiled"
199282

@@ -229,4 +312,3 @@ def get(self, path = ''):
229312
cmd_sequence = self.build_tex_cmd_sequence(tex_base_name)
230313
out = yield self.run_latex(cmd_sequence)
231314
self.finish(out)
232-

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
"@jupyterlab/fileeditor": "^4.0.0",
7474
"@jupyterlab/launcher": "^4.0.0",
7575
"@jupyterlab/mainmenu": "^4.0.0",
76+
"@jupyterlab/notebook": "^4.0.0",
7677
"@jupyterlab/services": "^7.0.0",
7778
"@jupyterlab/settingregistry": "^4.0.0",
7879
"@jupyterlab/statedb": "^4.0.0",

sample.tex

+11-5
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@
66
\title{JupyterLab \LaTeX}
77
\date{}
88
\maketitle
9-
9+
test
10+
$5_{3_{4} + 4_{3} + 5^{4}}$
1011
\section{Introduction}
1112
This is a sample document demonstrating the ability to live-edit
1213
\LaTeX documents in JupyterLab.
1314
\\
14-
\\
1515
Right-click on this document, and select "Show LaTeX Preview".
1616
A new panel should open up on the right with the PDF that is generated
1717
by this document. If there are any errors in the document, an
1818
error panel should open below.
1919
\\
20+
Test
2021
\\
2122
We can write equations:
2223
\begin{equation}
@@ -25,7 +26,6 @@ \section{Introduction}
2526
\label{eq:NavierStokes}
2627
\end{equation}
2728
\\
28-
\\
2929
And we can write tables:
3030
\begin{center}
3131
\begin{tabular}{ | l | c | r| }
@@ -37,8 +37,16 @@ \section{Introduction}
3737
\end{tabular}
3838
\end{center}
3939

40+
41+
\centering{Right-click on this document, and select "Show LaTeX Preview". A new panel should open up on the right with the PDF that is generated by this document. If there are any errors in the document, an error panel should open below.}
42+
43+
4044
We can reference equations by their numbers, i.e. Equation (\ref{eq:NavierStokes}).
4145

46+
Hello
47+
We can reference equations by their numbers, i.e. Equation (\ref{eq:NavierStokes}).
48+
49+
4250
\subsection{We can add new subsections}
4351
And we can include images, such as the Jupyter logo:
4452
\begin{center}
@@ -50,8 +58,6 @@ \subsection{We can add new subsections}
5058
\pagebreak
5159

5260

53-
54-
5561
You can also use SyncTeX by typing ⌘(CMD)/CTRL+⇧(SHIFT)+X.
5662

5763
\end{document}

src/error.tsx

+58-5
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ import { Message } from '@lumino/messaging';
55

66
import { Widget } from '@lumino/widgets';
77

8+
import { HTMLSelect } from '@jupyterlab/ui-components';
9+
810
import * as React from 'react';
911
import * as ReactDOM from 'react-dom';
1012

1113
const LATEX_ERROR_PANEL = 'jp-LatexErrorPanel';
1214
const LATEX_ERROR_CONTAINER = 'jp-LatexErrorContainer';
1315

16+
const TOOLBAR_CELLTYPE_CLASS = 'jp-Notebook-toolbarCellType';
17+
const TOOLBAR_CELLTYPE_DROPDOWN_CLASS = 'jp-Notebook-toolbarCellTypeDropdown';
18+
1419
/**
1520
* A widget which hosts the error logs from LaTeX
1621
* when document compilation fails.
@@ -22,10 +27,11 @@ export class ErrorPanel extends Widget {
2227
constructor() {
2328
super();
2429
this.addClass(LATEX_ERROR_PANEL);
30+
this.addClass(TOOLBAR_CELLTYPE_CLASS);
2531
}
2632

2733
set text(value: string) {
28-
ReactDOM.render(<LatexError text={value} />, this.node, () => {
34+
ReactDOM.render(<LatexError text={value} node={this} />, this.node, () => {
2935
this.update();
3036
});
3137
}
@@ -34,7 +40,7 @@ export class ErrorPanel extends Widget {
3440
* Handle an update request.
3541
*/
3642
protected onUpdateRequest(msg: Message): void {
37-
const el = this.node.children[0];
43+
const el = this.node.children[2].children[0];
3844
el.scrollTop = el.scrollHeight;
3945
}
4046

@@ -48,14 +54,61 @@ export class ErrorPanel extends Widget {
4854

4955
export interface ILatexProps {
5056
text: string;
57+
node: ErrorPanel;
5158
}
5259

5360
export class LatexError extends React.Component<ILatexProps, {}> {
61+
selectedValue: string | undefined;
62+
fullMessage: string;
63+
errorOnlyMessage: string;
64+
displayedMessage: string;
65+
66+
constructor(props: Readonly<ILatexProps>) {
67+
super(props);
68+
let messages = JSON.parse(props.text);
69+
this.fullMessage = messages.fullMessage;
70+
this.errorOnlyMessage = messages.errorOnlyMessage;
71+
this.displayedMessage = this.errorOnlyMessage;
72+
}
73+
74+
handleChange = (event: React.ChangeEvent<HTMLSelectElement>): void => {
75+
this.selectedValue = event.target.value;
76+
if (event.target.value === 'Filtered') {
77+
this.displayedMessage = this.errorOnlyMessage;
78+
} else if (event.target.value === 'Unfiltered') {
79+
this.displayedMessage = this.fullMessage;
80+
} else if (event.target.value === 'JSON') {
81+
this.displayedMessage = this.props.text;
82+
}
83+
84+
/**
85+
* Force ErrorPanel to rerender.
86+
*/
87+
this.setState({});
88+
this.props.node.update();
89+
};
90+
5491
render() {
5592
return (
56-
<pre className={LATEX_ERROR_CONTAINER}>
57-
<code>{this.props.text}</code>
58-
</pre>
93+
<>
94+
<label style={{ marginLeft: '1em', color: 'black' }}>Log Level:</label>
95+
96+
<div style={{ display: 'inline-block', position: 'relative' }}>
97+
<HTMLSelect
98+
className={TOOLBAR_CELLTYPE_DROPDOWN_CLASS}
99+
onChange={this.handleChange}
100+
aria-label="Log level"
101+
value={this.selectedValue}
102+
options={['Filtered', 'Unfiltered', 'JSON']}
103+
/>
104+
</div>
105+
106+
<div style={{ height: 'calc(100% - 3em)' }}>
107+
<pre className={LATEX_ERROR_CONTAINER}>
108+
<code>{this.displayedMessage}</code>
109+
</pre>
110+
</div>
111+
</>
59112
);
60113
}
61114
}

0 commit comments

Comments
 (0)