Skip to content

Commit

Permalink
UX improvements (#243)
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
8 people authored Sep 27, 2024
1 parent 9a1d429 commit 9043f30
Show file tree
Hide file tree
Showing 17 changed files with 1,107 additions and 94 deletions.
59 changes: 53 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,59 @@ A JupyterLab extension for live-editing of LaTeX documents.

## Usage

To use, right-click on an open `.tex` document within JupyterLab, and select `Show LaTeX Preview`:
![preview](images/show_preview.png)
This 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.
### Compilation

To compile and preview a LaTeX document:

1. Open a `.tex` document within JupyterLab.
2. Use one of the following methods to compile and preview the document:
- Right-click on the document and select `Show LaTeX Preview` from the context menu.
- Click the `Preview` button in the toolbar at the top of the document.

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.

### Writing Tools

A toolbar menu at the top of the document provides shortcuts to common LaTeX editing tasks:

#### Text Formatting

- **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.
- **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.
- **Bold**: Highlight the text you want to format in bold and click the `B` button.
- **Italic**: Highlight the text you want to format in italics and click the `I` button.
- **Underline**: Highlight the text you want to underline and click the `U` button.

#### Text Layout

- **Left Align**: Highlight the text you want to align left and click the left alignment button.
- **Center Align**: Highlight the text you want to center align and click the center alignment button.
- **Right Align**: Highlight the text you want to align right and click the right alignment button.

#### Lists

- **Bullet List**: Click the bullet list button to insert a bullet list.
- **Numbered List**: Click the numbered list button to insert a numbered list.

#### Tables and Plots

- **Table Creation GUI**: Click the table button to open a dialog for creating a table with a specified number of rows and columns.
- **Add Plot**: Click the plot button to select a plot type and insert it into your document. Available plot types include:
- Simple function plot
- Plot from file
- Scatter plot
- Bar graphs
- Contour plot
- Parametric plot

### Error Handling

- **Error Log Filtering Options**: Enhanced error log filtering options to help you quickly identify and resolve issues.

### Main Menu Helpers

- **Constant Menu**: Quickly insert common mathematical constants.
- **Symbol Menu**: Easily insert various mathematical symbols.

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

Expand Down
86 changes: 84 additions & 2 deletions jupyterlab_latex/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,89 @@ def bib_condition(self):
"""
return any([re.match(r'.*\.bib', x) for x in set(glob.glob("*"))])

def filter_output(self, latex_output):
"""Filters latex output for "interesting" messages
Parameters
----------
latex_output: string
This is the output of the executed latex command from,
run_command in run_latex.
returns:
A string representing the filtered output.
Notes
-----
- Based on the public domain perl script texfot v 1.43 written by
Karl Berry in 2014. It has no home page beyond the package on
CTAN: <https://ctan.org/pkg/texfot>.
"""
ignore = re.compile(r'''^(
LaTeX\ Warning:\ You\ have\ requested\ package
|LaTeX\ Font\ Warning:\ Some\ font\ shapes
|LaTeX\ Font\ Warning:\ Size\ substitutions
|Package\ auxhook\ Warning:\ Cannot\ patch
|Package\ caption\ Warning:\ Un(supported|known)\ document\ class
|Package\ fixltx2e\ Warning:\ fixltx2e\ is\ not\ required
|Package\ frenchb?\.ldf\ Warning:\ (Figures|The\ definition)
|Package\ layouts\ Warning:\ Layout\ scale
|\*\*\*\ Reloading\ Xunicode\ for\ encoding # spurious ***
|pdfTeX\ warning:.*inclusion:\ fou #nd PDF version ...
|pdfTeX\ warning:.*inclusion:\ mul #tiple pdfs with page group
|libpng\ warning:\ iCCP:\ Not\ recognizing
|!\ $
|This\ is
|No\ pages\ of\ output. # possibly not worth ignoring?
)''', re.VERBOSE)

next_line = re.compile(r'''^(
.*?:[0-9]+: # usual file:lineno: form
|! # usual ! form
|>\ [^<] # from \show..., but not "> <img.whatever"
|.*pdfTeX\ warning # pdftex complaints often cross lines
|LaTeX\ Font\ Warning:\ Font\ shape
|Package\ hyperref\ Warning:\ Token\ not\ allowed
|removed\ on\ input\ line # hyperref
|Runaway\ argument
)''', re.VERBOSE)

show = re.compile(r'''^(
Output\ written
|No\ pages\ of\ output
|\(.*end\ occurred\ inside\ a\ group
|(Und|Ov)erfull
|(LaTeX|Package|Class).*(Error|Warning)
|.*Citation.*undefined
|.*\ Error # as in \Url Error ->...
|Missing\ character: # good to show (need \tracinglostchars=1)
|\\endL.*problem # XeTeX?
|\*\*\*\s # *** from some packages or subprograms
|l\.[0-9]+ # line number marking
|all\ text\ was\ ignored\ after\ line
|.*Fatal\ error
|.*for\ symbol.*on\ input\ line
)''', re.VERBOSE)

print_next = False
filtered_output = []

for line in latex_output.split('\n'):
if print_next:
filtered_output.append(line)
print_next = False
elif ignore.match(line):
continue

elif next_line.match(line):
filtered_output.append(line)
print_next = True

elif show.match(line):
filtered_output.append(line)

return '\n'.join(filtered_output)

@gen.coroutine
def run_latex(self, command_sequence):
Expand Down Expand Up @@ -193,7 +276,7 @@ def run_latex(self, command_sequence):
self.set_status(500)
self.log.error((f'LaTeX command `{" ".join(cmd)}` '
f'errored with code: {code}'))
return output
return json.dumps({'fullMessage':output, 'errorOnlyMessage':self.filter_output(output)})

return "LaTeX compiled"

Expand Down Expand Up @@ -229,4 +312,3 @@ def get(self, path = ''):
cmd_sequence = self.build_tex_cmd_sequence(tex_base_name)
out = yield self.run_latex(cmd_sequence)
self.finish(out)

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"@jupyterlab/fileeditor": "^4.0.0",
"@jupyterlab/launcher": "^4.0.0",
"@jupyterlab/mainmenu": "^4.0.0",
"@jupyterlab/notebook": "^4.0.0",
"@jupyterlab/services": "^7.0.0",
"@jupyterlab/settingregistry": "^4.0.0",
"@jupyterlab/statedb": "^4.0.0",
Expand Down
16 changes: 11 additions & 5 deletions sample.tex
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
\title{JupyterLab \LaTeX}
\date{}
\maketitle

test
$5_{3_{4} + 4_{3} + 5^{4}}$
\section{Introduction}
This is a sample document demonstrating the ability to live-edit
\LaTeX documents in JupyterLab.
\\
\\
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.
\\
Test
\\
We can write equations:
\begin{equation}
Expand All @@ -25,7 +26,6 @@ \section{Introduction}
\label{eq:NavierStokes}
\end{equation}
\\
\\
And we can write tables:
\begin{center}
\begin{tabular}{ | l | c | r| }
Expand All @@ -37,8 +37,16 @@ \section{Introduction}
\end{tabular}
\end{center}


\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.}


We can reference equations by their numbers, i.e. Equation (\ref{eq:NavierStokes}).

Hello
We can reference equations by their numbers, i.e. Equation (\ref{eq:NavierStokes}).


\subsection{We can add new subsections}
And we can include images, such as the Jupyter logo:
\begin{center}
Expand All @@ -50,8 +58,6 @@ \subsection{We can add new subsections}
\pagebreak




You can also use SyncTeX by typing ⌘(CMD)/CTRL+⇧(SHIFT)+X.

\end{document}
63 changes: 58 additions & 5 deletions src/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import { Message } from '@lumino/messaging';

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

import { HTMLSelect } from '@jupyterlab/ui-components';

import * as React from 'react';
import * as ReactDOM from 'react-dom';

const LATEX_ERROR_PANEL = 'jp-LatexErrorPanel';
const LATEX_ERROR_CONTAINER = 'jp-LatexErrorContainer';

const TOOLBAR_CELLTYPE_CLASS = 'jp-Notebook-toolbarCellType';
const TOOLBAR_CELLTYPE_DROPDOWN_CLASS = 'jp-Notebook-toolbarCellTypeDropdown';

/**
* A widget which hosts the error logs from LaTeX
* when document compilation fails.
Expand All @@ -22,10 +27,11 @@ export class ErrorPanel extends Widget {
constructor() {
super();
this.addClass(LATEX_ERROR_PANEL);
this.addClass(TOOLBAR_CELLTYPE_CLASS);
}

set text(value: string) {
ReactDOM.render(<LatexError text={value} />, this.node, () => {
ReactDOM.render(<LatexError text={value} node={this} />, this.node, () => {
this.update();
});
}
Expand All @@ -34,7 +40,7 @@ export class ErrorPanel extends Widget {
* Handle an update request.
*/
protected onUpdateRequest(msg: Message): void {
const el = this.node.children[0];
const el = this.node.children[2].children[0];
el.scrollTop = el.scrollHeight;
}

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

export interface ILatexProps {
text: string;
node: ErrorPanel;
}

export class LatexError extends React.Component<ILatexProps, {}> {
selectedValue: string | undefined;
fullMessage: string;
errorOnlyMessage: string;
displayedMessage: string;

constructor(props: Readonly<ILatexProps>) {
super(props);
let messages = JSON.parse(props.text);
this.fullMessage = messages.fullMessage;
this.errorOnlyMessage = messages.errorOnlyMessage;
this.displayedMessage = this.errorOnlyMessage;
}

handleChange = (event: React.ChangeEvent<HTMLSelectElement>): void => {
this.selectedValue = event.target.value;
if (event.target.value === 'Filtered') {
this.displayedMessage = this.errorOnlyMessage;
} else if (event.target.value === 'Unfiltered') {
this.displayedMessage = this.fullMessage;
} else if (event.target.value === 'JSON') {
this.displayedMessage = this.props.text;
}

/**
* Force ErrorPanel to rerender.
*/
this.setState({});
this.props.node.update();
};

render() {
return (
<pre className={LATEX_ERROR_CONTAINER}>
<code>{this.props.text}</code>
</pre>
<>
<label style={{ marginLeft: '1em', color: 'black' }}>Log Level:</label>

<div style={{ display: 'inline-block', position: 'relative' }}>
<HTMLSelect
className={TOOLBAR_CELLTYPE_DROPDOWN_CLASS}
onChange={this.handleChange}
aria-label="Log level"
value={this.selectedValue}
options={['Filtered', 'Unfiltered', 'JSON']}
/>
</div>

<div style={{ height: 'calc(100% - 3em)' }}>
<pre className={LATEX_ERROR_CONTAINER}>
<code>{this.displayedMessage}</code>
</pre>
</div>
</>
);
}
}
Loading

0 comments on commit 9043f30

Please sign in to comment.