[MIRROR] tgui cleanup again (#11432)

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-08-15 16:26:48 -07:00
committed by GitHub
parent 77a99a4531
commit 15cbe5bee3
20 changed files with 64 additions and 46 deletions

View File

@@ -56,8 +56,7 @@
"suspicious": {
"noArrayIndexKey": "off",
"noExplicitAny": "off",
"noImplicitAnyLet": "off",
"noAssignInExpressions": "warn"
"noImplicitAnyLet": "off"
}
}
},

View File

@@ -66,6 +66,12 @@
/datum/preference/toggle/tgui_say_light/apply_to_client(client/client, value)
client.tgui_say?.load()
/datum/preference/toggle/tgui_use_spellcheck
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "TGUI_ENABLE_SPELLCHECK"
default_value = TRUE
savefile_identifier = PREFERENCE_PLAYER
/datum/preference/toggle/tgui_say_emotes
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "tgui_say_emotes"

View File

@@ -72,10 +72,11 @@
window.send_message("props", list(
"lightMode" = client?.prefs?.read_preference(/datum/preference/toggle/tgui_say_light),
"scale" = client.prefs?.read_preference(/datum/preference/toggle/ui_scale),
"scale" = client?.prefs?.read_preference(/datum/preference/toggle/ui_scale),
"minimumWidth" = minimum_width,
"minimumHeight" = minimum_height,
"maxLength" = max_length,
"spellcheck" = client?.prefs?.read_preference(/datum/preference/toggle/tgui_use_spellcheck)
))
stop_thinking()

View File

@@ -126,6 +126,7 @@
data["placeholder"] = default // Default is a reserved keyword
data["swapped_buttons"] = !user.read_preference(/datum/preference/toggle/tgui_swapped_buttons)
data["title"] = title
data["spellcheck"] = user.read_preference(/datum/preference/toggle/tgui_use_spellcheck)
return data
/datum/tgui_input_text/tgui_data(mob/user)

View File

@@ -48,7 +48,7 @@
"marked-smartypants": "^1.1.9",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui-core": "^5.0.0",
"tgui-core": "^5.1.0",
"tgui-dev-server": "workspace:*",
},
},
@@ -71,7 +71,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui": "workspace:*",
"tgui-core": "^5.0.0",
"tgui-core": "^5.1.0",
"tgui-dev-server": "workspace:*",
},
},
@@ -83,7 +83,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui": "workspace:*",
"tgui-core": "^5.0.0",
"tgui-core": "^5.1.0",
},
},
"packages/tgui-setup": {
@@ -1108,7 +1108,7 @@
"tgui": ["tgui@workspace:packages/tgui"],
"tgui-core": ["tgui-core@5.0.0", "", { "dependencies": { "@floating-ui/react": "^0.27.13", "@nozbe/microfuzz": "^1.0.0" }, "peerDependencies": { "react": "^19.1.0", "react-dom": "^19.1.0" } }, "sha512-v03Sds9QjKSLv1WTK3NB1HwZ3Mp+P7FJ0MPdB2QTYnf2UgZG8cinf+SBqo78hOXuLj9iXRMSg5GO5LzaWHBTYQ=="],
"tgui-core": ["tgui-core@5.1.0", "", { "dependencies": { "@floating-ui/react": "^0.27.13", "@nozbe/microfuzz": "^1.0.0" }, "peerDependencies": { "react": "^19.1.0", "react-dom": "^19.1.0" } }, "sha512-I6uokZ4hC2UaufOZ9sub2t7ls5F8HtTl2HV/slA4ZqL5hzi2N+lrARoHtPMhi6ZoRZ0ZPJQscqTMdE+Iptcz1A=="],
"tgui-dev-server": ["tgui-dev-server@workspace:packages/tgui-dev-server"],

View File

@@ -191,11 +191,11 @@ class ChatRenderer {
ensureScrollTracking: () => void;
highlightParsers:
| {
highlightWords: string;
highlightWords: string[];
highlightRegex: RegExp;
highlightColor: string;
highlightWholeMessage: boolean;
highlightBlacklist: string;
highlightBlacklist: boolean;
blacklistregex: RegExp;
}[]
| null;

View File

@@ -12,33 +12,36 @@ const regexParseNode = (params: {
regex: RegExp;
createNode: (text: string) => Node;
captureAdjust?: (str: string) => string;
}): { nodes?: HTMLElement; n?: number } => {
}): { nodes?: Node[]; n?: number } => {
const { node, regex, createNode, captureAdjust } = params;
const text = node.textContent || '';
const text = node.textContent;
if (!text || !regex) {
return { nodes: [], n: 0 };
}
const textLength = text.length;
let nodes;
let new_node;
let match = regex.exec(text);
const nodes: Node[] = [];
let fragment: Node | undefined;
let new_node: Node;
let match: RegExpExecArray | null;
let lastIndex = 0;
let fragment;
let n = 0;
let count = 0;
// eslint-disable-next-line no-cond-assign
while (match !== null) {
while (true) {
match = regex.exec(text);
if (!match) break;
n += 1;
// Safety check to prevent permanent
// client crashing
if (++count > 9999) {
return {};
return { nodes: [], n: 0 };
}
// Lazy init fragment
if (!fragment) {
fragment = document.createDocumentFragment();
}
// Lazy init nodes
if (!nodes) {
nodes = [];
}
const matchText = captureAdjust ? captureAdjust(match[0]) : match[0];
const matchLength = matchText.length;
// If matchText is set to be a substring nested within the original
@@ -55,7 +58,6 @@ const regexParseNode = (params: {
new_node = createNode(matchText);
nodes.push(new_node);
fragment.appendChild(new_node);
match = regex.exec(text);
}
if (fragment) {
// Insert the remaining unmatched chunk
@@ -83,7 +85,7 @@ const regexParseNode = (params: {
export const replaceInTextNode =
(
regex: RegExp,
words: string | null,
words: string[] | null,
createNode: (text: string) => Node,
): ((node: Node) => number) =>
(node: Node) => {
@@ -162,7 +164,7 @@ const createHighlightNode = (text: string): HTMLSpanElement => {
export const highlightNode = (
node: Node,
regex: RegExp,
words: string,
words: string[],
createNode: (text: string) => Node = createHighlightNode,
) => {
if (!createNode) {

View File

@@ -8,7 +8,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui": "workspace:*",
"tgui-core": "^5.0.0",
"tgui-core": "^5.1.0",
"tgui-dev-server": "workspace:*"
},
"private": true

View File

@@ -28,6 +28,7 @@ type ByondProps = {
minimumWidth: number;
lightMode: BooleanLike;
scale: BooleanLike;
spellcheck: BooleanLike;
};
export function TguiSay() {
@@ -38,6 +39,7 @@ export function TguiSay() {
const scale = useRef(true);
const minimumHeight = useRef(WindowSize.Small);
const minimumWidth = useRef(WindowSize.Width);
const spellcheck = useRef(true);
// I initially wanted to make these an object or a reducer, but it's not really worth it.
// You lose the granulatity and add a lot of boilerplate.
@@ -320,7 +322,7 @@ export function TguiSay() {
);
minimumHeight.current = data.minimumHeight;
minimumWidth.current = minWidth;
setLightMode(!!data.lightMode);
spellcheck.current = !!data.spellcheck;
scale.current = !!data.scale;
}
@@ -388,7 +390,7 @@ export function TguiSay() {
{buttonContent}
</button>
<textarea
spellCheck
spellCheck={spellcheck.current}
autoCorrect="off"
className={classes([
'textarea',

View File

@@ -6,7 +6,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui": "workspace:*",
"tgui-core": "^5.0.0"
"tgui-core": "^5.1.0"
},
"private": true
}

View File

@@ -1,7 +1,6 @@
import { useBackend } from 'tgui/backend';
import { Button, LabeledList, Section, Stack } from 'tgui-core/components';
import { useBackend } from '../../backend';
import { type ControllerData } from './types';
import type { ControllerData } from './types';
export function OverviewSection(props) {
const { act, data } = useBackend<ControllerData>();

View File

@@ -7,7 +7,7 @@ import {
Stack,
} from 'tgui-core/components';
import { type SubsystemData } from './types';
import type { SubsystemData } from './types';
type Props = {
subsystem: SubsystemData;

View File

@@ -1,4 +1,4 @@
import { type Dispatch } from 'react';
import type { Dispatch } from 'react';
import {
Button,
Icon,
@@ -49,13 +49,13 @@ export function SubsystemRow(props: Props) {
let rangeDisplay = {};
if (showBars) {
if (sortType === SortType.Cost) {
valueDisplay = value.toFixed(2) + 'ms';
valueDisplay = `${value.toFixed(2)} ms`;
rangeDisplay = {
average: [75, 124.99],
bad: [125, Infinity],
};
} else {
valueDisplay = value.toFixed(2) + '%';
valueDisplay = `${value.toFixed(2)} %`;
rangeDisplay = {
average: [10, 24.99],
bad: [25, Infinity],

View File

@@ -1,11 +1,10 @@
import { type Dispatch, useEffect, useState } from 'react';
import { useBackend } from 'tgui/backend';
import { Button, Section, Stack, Table } from 'tgui-core/components';
import { useBackend } from '../../backend';
import { SORTING_TYPES } from './constants';
import { type FilterState } from './filters';
import type { FilterState } from './filters';
import { SubsystemRow } from './SubsystemRow';
import { type ControllerData, type SubsystemData } from './types';
import type { ControllerData, SubsystemData } from './types';
type Props = {
filterOpts: FilterState;

View File

@@ -1,4 +1,4 @@
import { type SortType } from './types';
import type { SortType } from './types';
export type FilterState = {
ascending: boolean;

View File

@@ -3,7 +3,7 @@ import { Button, Dropdown, Input, Section, Stack } from 'tgui-core/components';
import { Window } from '../../layouts';
import { SORTING_TYPES } from './constants';
import { FilterAction, filterReducer, type FilterState } from './filters';
import { FilterAction, type FilterState, filterReducer } from './filters';
import { OverviewSection } from './OverviewSection';
import { SubsystemDialog } from './SubsystemDialog';
import { SubsystemViews } from './SubsystemViews';

View File

@@ -1,4 +1,4 @@
import { type BooleanLike } from 'tgui-core/react';
import type { BooleanLike } from 'tgui-core/react';
export type SubsystemData = {
can_fire: BooleanLike;

View File

@@ -83,6 +83,13 @@ export const tgui_say_emotes: FeatureToggle = {
component: CheckboxInput,
};
export const TGUI_ENABLE_SPELLCHECK: FeatureToggle = {
name: 'TGUI: Spellcheck',
category: 'UI',
description: 'Enables spellchecking on TGUI text areas and TGUI Say.',
component: CheckboxInput,
};
export const tgui_say_height: FeatureNumeric = {
name: 'Say: TGUI Height (Lines)',
category: 'UI',

View File

@@ -2,9 +2,8 @@ import { useState } from 'react';
import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts';
import { Box, Section, Stack, TextArea } from 'tgui-core/components';
import { isEscape } from 'tgui-core/keys';
import { KEY } from 'tgui-core/keys';
import { isEscape, KEY } from 'tgui-core/keys';
import type { BooleanLike } from 'tgui-core/react';
import { InputButtons } from './common/InputButtons';
import { Loader } from './common/Loader';
@@ -16,6 +15,7 @@ type TextInputData = {
placeholder: string;
timeout: number;
title: string;
spellcheck: BooleanLike;
};
export const sanitizeMultiline = (toSanitize: string) => {
@@ -36,6 +36,7 @@ export const TextInputModal = (props) => {
placeholder = '',
timeout,
title,
spellcheck,
} = data;
const [input, setInput] = useState(placeholder || '');
@@ -80,6 +81,7 @@ export const TextInputModal = (props) => {
autoFocus
autoSelect
fluid
spellcheck={!!spellcheck}
userMarkup={{ u: '_', i: '|', b: '+' }}
height={multiline || input.length >= 30 ? '100%' : '1.8rem'}
maxLength={max_length}

View File

@@ -13,7 +13,7 @@
"marked-smartypants": "^1.1.9",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui-core": "^5.0.0",
"tgui-core": "^5.1.0",
"tgui-dev-server": "workspace:*"
},
"private": true