From b216f05b6f24ef034236b4f2b2b48d4f66462620 Mon Sep 17 00:00:00 2001 From: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Date: Fri, 16 May 2025 11:23:51 -0700 Subject: [PATCH] tgui paper bandaid fix (#91098) ## About The Pull Request For the record, this is a bandaid fix. The inputs are losing focus with each update (aka EVERY backend update). This sets them to only update when their editability changes, letting you type again. Caveat: The UI, or at least how it's done with dangerous HTML, doesn't seem to be smart enough to detect when the UI turns back to being non editable, nor am I smart enough to fix this behavior after several attempts. The result: When you drop the pen, the fields look editable but lose focus when clicked just like the bug originally. It's not great UX. Paperwork needs to just be wholesale redone. Bumped marked.js versions a HUGE amount which should come with better performance, typings, etc. This seemingly resolves a few issues with markdown otherwise (like typing _a) --- .../tgui/interfaces/MarkdownViewer.tsx | 2 +- .../tgui/interfaces/PaperSheet/Preview.tsx | 86 ++++++++++--------- .../interfaces/PaperSheet/PrimaryView.tsx | 5 +- tgui/packages/tgui/package.json | 5 +- tgui/packages/tgui/process.ts | 33 ++++--- tgui/yarn.lock | 50 ++++++++--- 6 files changed, 114 insertions(+), 67 deletions(-) diff --git a/tgui/packages/tgui/interfaces/MarkdownViewer.tsx b/tgui/packages/tgui/interfaces/MarkdownViewer.tsx index 9ee7764f9ed..092c68d8735 100644 --- a/tgui/packages/tgui/interfaces/MarkdownViewer.tsx +++ b/tgui/packages/tgui/interfaces/MarkdownViewer.tsx @@ -29,7 +29,7 @@ type MarkdownRendererProps = { export const MarkdownRenderer = (props: MarkdownRendererProps) => { let { content, sanitize } = props; - content = marked(content); + content = marked(content, { async: false }); if (sanitize) { content = sanitizeText(content, /* advHtml = */ false); } diff --git a/tgui/packages/tgui/interfaces/PaperSheet/Preview.tsx b/tgui/packages/tgui/interfaces/PaperSheet/Preview.tsx index 5f3229a2d08..fdcc3671116 100644 --- a/tgui/packages/tgui/interfaces/PaperSheet/Preview.tsx +++ b/tgui/packages/tgui/interfaces/PaperSheet/Preview.tsx @@ -1,10 +1,12 @@ import { marked } from 'marked'; +import { baseUrl } from 'marked-base-url'; +import { markedSmartypants } from 'marked-smartypants'; import { Component, RefObject } from 'react'; import { Box, Section } from 'tgui-core/components'; import { useBackend, useLocalState } from '../../backend'; import { sanitizeText } from '../../sanitize'; -import { canEdit, tokenizer, walkTokens } from './helpers'; +import { tokenizer, walkTokens } from './helpers'; import { StampView } from './StampView'; import { FieldInput, InteractionType, PaperContext } from './types'; @@ -12,6 +14,7 @@ type PreviewViewProps = { scrollableRef: RefObject; handleOnScroll: (this: GlobalEventHandlers, ev: Event) => any; textArea: string; + canEdit: boolean; }; type FieldCreationReturn = { @@ -78,15 +81,17 @@ export class PreviewView extends Component { walkTokens, }; - marked.use({ - extensions: [inputField], - breaks: true, - gfm: true, - smartypants: true, - walkTokens: walkTokens, + marked.use( + { + extensions: [inputField], + breaks: true, + gfm: true, + walkTokens: walkTokens, + }, + markedSmartypants(), // Once assets are fixed might need to change this for them - baseUrl: 'thisshouldbreakhttp', - }); + baseUrl('thisshouldbreakhttp'), + ); }; // Extracts the paper field "counter" from a full ID. @@ -145,6 +150,12 @@ export class PreviewView extends Component { document.removeEventListener('input', this.onInputHandler); } + shouldComponentUpdate(nextProps: Readonly): boolean { + if (!this.props.canEdit) return true; + + return this.props.canEdit !== nextProps.canEdit; + } + // Creates the partial inline HTML for previewing or reading the paper from // only static_ui_data from DM. createPreviewFromDM = (): { text: string; newFieldCount: number } => { @@ -155,13 +166,12 @@ export class PreviewView extends Component { default_pen_font, default_pen_color, paper_color, - held_item_details, } = data; let output = ''; let fieldCount = 0; - const readOnly = !canEdit(held_item_details); + const readOnly = !this.props.canEdit; // If readonly is the same (input field writiability state hasn't changed) // And the input stats are the same (no new text inputs since last time) @@ -254,7 +264,7 @@ export class PreviewView extends Component { text: string, font: string, color: string, - bold: boolean = false, + bold = false, ): string => { return ` { - const textHtml = { - __html: sanitizeText( - marked(value, { +type ProcessedText = { + __html: string; +}; + +export function processedText(value: string | null): ProcessedText { + if (!value) { + return { __html: '' }; + } + + const parsed = marked + .use( + { breaks: true, - smartypants: true, - smartLists: true, - baseUrl: 'thisshouldbreakhttp', - }), - ), + }, + markedSmartypants(), + baseUrl('thisshouldbreakhttp'), + ) + .parse(value, { async: false }); + + const textHtml = { + __html: sanitizeText(parsed), }; return textHtml; -}; +} diff --git a/tgui/yarn.lock b/tgui/yarn.lock index 5aa9d58c57a..ba48dbcf689 100644 --- a/tgui/yarn.lock +++ b/tgui/yarn.lock @@ -3918,13 +3918,6 @@ __metadata: languageName: node linkType: hard -"@types/marked@npm:4.3.2": - version: 4.3.2 - resolution: "@types/marked@npm:4.3.2" - checksum: 10c0/6f44d28da5c940a719d6c6aca41e33f49c5fe957f7972939cc1cbb47d045951f4d969f382d655345d0463e47db1994635d2862018716fcef2fa85ceeceb116e7 - languageName: node - linkType: hard - "@types/mime@npm:^1": version: 1.3.5 resolution: "@types/mime@npm:1.3.5" @@ -13824,12 +13817,32 @@ __metadata: languageName: node linkType: hard -"marked@npm:^4.3.0": - version: 4.3.0 - resolution: "marked@npm:4.3.0" +"marked-base-url@npm:^1.1.6": + version: 1.1.6 + resolution: "marked-base-url@npm:1.1.6" + peerDependencies: + marked: ">= 4 < 16" + checksum: 10c0/346b8f3a912ab5192caec896d8e923b83113f81312eb4838ec9773aca620088cd3ed4d9fce773f3251d71c72f271c3df8554f025c1146cea3e76845b29e5be42 + languageName: node + linkType: hard + +"marked-smartypants@npm:^1.1.9": + version: 1.1.9 + resolution: "marked-smartypants@npm:1.1.9" + dependencies: + smartypants: "npm:^0.2.2" + peerDependencies: + marked: ">=4 <16" + checksum: 10c0/50a9f9ddeb67045933c26abdd8acf997368d96920990048b6d432c4d2374a513152c639b46c16c74934483b7a1df6093f8f26461f2409b9bf1e58f8ac759391f + languageName: node + linkType: hard + +"marked@npm:^15.0.11": + version: 15.0.11 + resolution: "marked@npm:15.0.11" bin: marked: bin/marked.js - checksum: 10c0/0013463855e31b9c88d8bb2891a611d10ef1dc79f2e3cbff1bf71ba389e04c5971298c886af0be799d7fa9aa4593b086a136062d59f1210b0480b026a8c5dc47 + checksum: 10c0/d532db4955c1f2ac6efc65a644725e9e12e7944cb6af40c7148baecfd3b3c2f3564229b3daf12d2125635466448fb9b367ce52357be3aea0273e3d152efdbdcf languageName: node linkType: hard @@ -18699,6 +18712,16 @@ __metadata: languageName: node linkType: hard +"smartypants@npm:^0.2.2": + version: 0.2.2 + resolution: "smartypants@npm:0.2.2" + bin: + smartypants: bin/smartypants.js + smartypantsu: bin/smartypantsu.js + checksum: 10c0/29b46df6acaed9ad6271624e26e055d6360c9da7f79433b7a83ebd40b50bd6b554d8f3793932f42be07e5badce889f547c89378fda77cb1f935b09a902c4527b + languageName: node + linkType: hard + "snake-case@npm:^3.0.4": version: 3.0.4 resolution: "snake-case@npm:3.0.4" @@ -19885,7 +19908,6 @@ __metadata: version: 0.0.0-use.local resolution: "tgui@workspace:packages/tgui" dependencies: - "@types/marked": "npm:4.3.2" "@types/react": "npm:^19.1.0" "@types/react-dom": "npm:^19.1.2" common: "workspace:*" @@ -19895,7 +19917,9 @@ __metadata: highlight.js: "npm:^11.11.1" jest: "npm:^29.7.0" js-yaml: "npm:^4.1.0" - marked: "npm:^4.3.0" + marked: "npm:^15.0.11" + marked-base-url: "npm:^1.1.6" + marked-smartypants: "npm:^1.1.9" react: "npm:^19.1.0" react-dom: "npm:^19.1.0" tgui-core: "npm:^3.1.4"