Merge pull request #15400 from Arturlang/LaTeX-in-paper

LaTeX-in-paper
This commit is contained in:
Lin
2021-12-05 00:23:55 +00:00
committed by GitHub
24 changed files with 1484 additions and 60 deletions
Binary file not shown.
+119 -60
View File
@@ -4,11 +4,11 @@
* @author Original WarlockD (https://github.com/warlockd)
* @author Changes stylemistake
* @author Changes ThePotato97
* @author Changes Ghommie
* @license MIT
*/
import { classes } from 'common/react';
import { Fragment } from 'inferno';
import { Component } from 'inferno';
import marked from 'marked';
import { useBackend } from '../backend';
@@ -16,8 +16,27 @@ import { Box, Flex, Tabs, TextArea } from '../components';
import { Window } from '../layouts';
import { clamp } from 'common/math';
import { sanitizeText } from '../sanitize';
import katex from 'katex';
const MAX_PAPER_LENGTH = 5000; // Question, should we send this with ui_data?
// Find where people put in equations inside two $ symbols
// and convert them to proper LaTeX
const equationRegex = (text) => {
const find_equation_formatting = /\$.*\$/igm;
const find_regex = find_equation_formatting.exec(text);
if (find_regex) {
for (let i = 0; i < find_regex.length; i++) {
const removeDollarSigns = find_regex[i].replace(/\$/g, '');
const convertToLatex = katex.renderToString(removeDollarSigns, {
throwOnError: false,
});
text = text.replace(find_regex[i], convertToLatex);
}
}
return text;
};
// Hacky, yes, works?...yes
const textWidth = (text, font, fontsize) => {
// default font height is 12 in tgui
@@ -31,7 +50,7 @@ const textWidth = (text, font, fontsize) => {
const setFontinText = (text, font, color, bold=false) => {
return "<span style=\""
+ "color:'" + color + "';"
+ "color:" + color + ";"
+ "font-family:'" + font + "';"
+ ((bold)
? "font-weight: bold;"
@@ -55,7 +74,7 @@ const createInputField = (length, width, font,
+ "type=\"text\" "
+ "style=\""
+ "font:'" + fontsize + "x " + font + "';"
+ "color:'" + color + "';"
+ "color:" + color + ";"
+ "min-width:" + width + ";"
+ "max-width:" + width + ";"
+ "\" "
@@ -236,7 +255,6 @@ const PaperSheetView = (props, context) => {
height="100%" >
<Box
className="Paper__Page"
color="black"
fillPositionedParent
width="100%"
height="100%"
@@ -345,7 +363,7 @@ class PaperSheetStamper extends Component {
rotate: this.state.rotate,
};
return (
<Fragment>
<>
<PaperSheetView
readOnly
value={value}
@@ -353,11 +371,58 @@ class PaperSheetStamper extends Component {
<Stamp
active_stamp
opacity={0.5} image={current_pos} />
</Fragment>
</>
);
}
}
// This creates the html from marked text as well as the form fields
const createPreview = (
value,
text,
do_fields = false,
field_counter,
color,
font,
user_name,
is_crayon = false,
) => {
const out = { text: text };
// check if we are adding to paper, if not
// we still have to check if someone entered something
// into the fields
value = value.trim();
if (value.length > 0) {
// First lets make sure it ends in a new line
value += value[value.length] === "\n" ? " \n" : "\n \n";
// Second, we sanitize the text of html
const sanitized_text = sanitizeText(value);
const signed_text = signDocument(sanitized_text, color, user_name);
// Third we replace the [__] with fields as markedjs fucks them up
const fielded_text = createFields(
signed_text, font, 12, color, field_counter);
// Fourth, parse the text using markup
const formatted_text = run_marked_default(fielded_text.text);
const replacedEquations = equationRegex(formatted_text);
// Fifth, we wrap the created text in the pin color, and font.
// crayon is bold (<b> tags), maybe make fountain pin italic?
const fonted_text = setFontinText(
replacedEquations, font, color, is_crayon);
out.text += fonted_text;
out.field_counter = fielded_text.counter;
}
if (do_fields) {
// finally we check all the form fields to see
// if any data was entered by the user and
// if it was return the data and modify the text
const final_processing = checkAllFields(
out.text, font, color, user_name, is_crayon);
out.text = final_processing.text;
out.form_fields = final_processing.fields;
}
return out;
};
// ugh. So have to turn this into a full
// component too if I want to keep updates
// low and keep the weird flashing down
@@ -367,58 +432,24 @@ class PaperSheetEdit extends Component {
this.state = {
previewSelected: "Preview",
old_text: props.value || "",
counter: props.counter || 0,
textarea_text: "",
combined_text: props.value || "",
};
}
// This is the main rendering part, this creates the html from marked text
// as well as the form fields
createPreview(value, do_fields = false) {
createPreviewFromData(value, do_fields = false) {
const { data } = useBackend(this.context);
const {
text,
pen_color,
pen_font,
is_crayon,
field_counter,
edit_usr,
} = data;
const out = { text: text };
// check if we are adding to paper, if not
// we still have to check if someone entered something
// into the fields
value = value.trim();
if (value.length > 0) {
// First lets make sure it ends in a new line
value += value[value.length] === "\n" ? " \n" : "\n \n";
// Second, we sanitize the text of html
const sanitized_text = sanitizeText(value);
const signed_text = signDocument(sanitized_text, pen_color, edit_usr);
// Third we replace the [__] with fields as markedjs fucks them up
const fielded_text = createFields(
signed_text, pen_font, 12, pen_color, field_counter);
// Fourth, parse the text using markup
const formatted_text = run_marked_default(fielded_text.text);
// Fifth, we wrap the created text in the pin color, and font.
// crayon is bold (<b> tags), maybe make fountain pin italic?
const fonted_text = setFontinText(
formatted_text, pen_font, pen_color, is_crayon);
out.text += fonted_text;
out.field_counter = fielded_text.counter;
}
if (do_fields) {
// finally we check all the form fields to see
// if any data was entered by the user and
// if it was return the data and modify the text
const final_processing = checkAllFields(
out.text, pen_font, pen_color, edit_usr, is_crayon);
out.text = final_processing.text;
out.form_fields = final_processing.fields;
}
return out;
return createPreview(value,
this.state.old_text,
do_fields,
this.state.counter,
data.pen_color,
data.pen_font,
data.edit_usr,
data.is_crayon,
);
}
onInputHandler(e, value) {
if (value !== this.state.textarea_text) {
const combined_length = this.state.old_text.length
@@ -439,19 +470,21 @@ class PaperSheetEdit extends Component {
}
this.setState(() => ({
textarea_text: value,
combined_text: this.createPreview(value),
combined_text: this.createPreviewFromData(value),
}));
}
}
// the final update send to byond, final upkeep
finalUpdate(new_text) {
const { act } = useBackend(this.context);
const final_processing = this.createPreview(new_text, true);
const final_processing = this.createPreviewFromData(new_text, true);
act('save', final_processing);
this.setState(() => { return {
textarea_text: "",
previewSelected: "save",
combined_text: final_processing.text,
old_text: final_processing.text,
counter: final_processing.field_counter,
}; });
// byond should switch us to readonly mode from here
}
@@ -490,7 +523,7 @@ class PaperSheetEdit extends Component {
const new_state = {
previewSelected: "Preview",
textarea_text: this.state.textarea_text,
combined_text: this.createPreview(
combined_text: this.createPreviewFromData(
this.state.textarea_text).text,
};
return new_state;
@@ -516,7 +549,7 @@ class PaperSheetEdit extends Component {
const new_state = {
previewSelected: "confirm",
textarea_text: this.state.textarea_text,
combined_text: this.createPreview(
combined_text: this.createPreviewFromData(
this.state.textarea_text).text,
};
return new_state;
@@ -567,7 +600,33 @@ export const PaperSheet = (props, context) => {
sizeX,
sizeY,
name,
add_text,
add_font,
add_color,
add_sign,
field_counter,
} = data;
// some features can add text to a paper sheet outside of this ui
// we need to parse, sanitize and add any of it to the text value.
const values = { text: text, field_counter: field_counter };
if (add_text) {
for (let index = 0; index < add_text.length; index++) {
const used_color = add_color[index];
const used_font = add_font[index];
const used_sign = add_sign[index];
const processing = createPreview(
add_text[index],
values.text,
false,
values.field_counter,
used_color,
used_font,
used_sign
);
values.text = processing.text;
values.field_counter = processing.field_counter;
}
}
const stamp_list = !stamps
? []
: stamps;
@@ -576,14 +635,15 @@ export const PaperSheet = (props, context) => {
case 0:
return (
<PaperSheetView
value={text}
value={values.text}
stamps={stamp_list}
readOnly />
);
case 1:
return (
<PaperSheetEdit
value={text}
value={values.text}
counter={values.field_counter}
textColor={pen_color}
fontFamily={pen_font}
stamps={stamp_list}
@@ -592,7 +652,7 @@ export const PaperSheet = (props, context) => {
case 2:
return (
<PaperSheetStamper
value={text}
value={values.text}
stamps={stamp_list}
stamp_class={stamp_class} />
);
@@ -605,8 +665,7 @@ export const PaperSheet = (props, context) => {
title={name}
theme="paper"
width={sizeX || 400}
height={sizeY || 500}
resizable>
height={sizeY || 500}>
<Window.Content
backgroundColor={paper_color}
scrollable>
+1
View File
@@ -7,6 +7,7 @@
"dompurify": "^2.2.7",
"inferno": "^7.4.8",
"inferno-vnode-flags": "^7.4.8",
"katex": "^0.15.1",
"marked": "^2.0.3",
"tgui-dev-server": "workspace:*",
"tgui-polyfill": "workspace:*"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,28 @@
.katex {
font-size: 1.5em !important
}
eq {
display: inline-block
}
eqn {
display: block
}
section.eqno {
display: flex;
flex-direction: row;
align-content: space-between;
align-items: center
}
section.eqno>eqn {
width: 100%;
margin-left: 3em
}
section.eqno>span {
width: 3em;
text-align: right
}
+2
View File
@@ -15,6 +15,8 @@
@include meta.load-css('./atomic/debug-layout.scss');
@include meta.load-css('./atomic/outline.scss');
@include meta.load-css('./atomic/text.scss');
@include meta.load-css('./atomic/TexMath.scss');
@include meta.load-css('./atomic/KaTeX.scss');
// Components
@include meta.load-css('./components/BlockQuote.scss');
+19
View File
@@ -3036,6 +3036,13 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^8.0.0":
version: 8.3.0
resolution: "commander@npm:8.3.0"
checksum: 0b818d97ca9c8ad461ff05f873082af0c1895e49984a2abf1090e4a85c3537c3c0f9921e38314a9de7e71941f00f710c204d3560e3a7bc0b1023eef92d6bbfc0
languageName: node
linkType: hard
"common@workspace:*, common@workspace:packages/common":
version: 0.0.0-use.local
resolution: "common@workspace:packages/common"
@@ -5826,6 +5833,17 @@ __metadata:
languageName: node
linkType: hard
"katex@npm:^0.15.1":
version: 0.15.1
resolution: "katex@npm:0.15.1"
dependencies:
commander: ^8.0.0
bin:
katex: cli.js
checksum: d1db044d8b2f37c70adb4c331dfe55b453f2fc4a0cc72e050e76ff372a7f74201a3ac96998bda3f63cafac3622b2e2e89aecc4b2ed960c5fdd2dda2d8bfe5757
languageName: node
linkType: hard
"kind-of@npm:^3.0.2, kind-of@npm:^3.0.3, kind-of@npm:^3.2.0":
version: 3.2.2
resolution: "kind-of@npm:3.2.2"
@@ -8369,6 +8387,7 @@ resolve@^2.0.0-next.3:
dompurify: ^2.2.7
inferno: ^7.4.8
inferno-vnode-flags: ^7.4.8
katex: ^0.15.1
marked: ^2.0.3
tgui-dev-server: "workspace:*"
tgui-polyfill: "workspace:*"