From bb759b294ced3a61bd7b5a01d408c8339214ff14 Mon Sep 17 00:00:00 2001 From: CHOMPStation2StaffMirrorBot <94713762+CHOMPStation2StaffMirrorBot@users.noreply.github.com> Date: Fri, 14 Feb 2025 06:35:23 -0700 Subject: [PATCH] [MIRROR] tgui 516 (#10158) Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> --- code/modules/asset_cache/assets/tgui.dm | 7 - code/modules/client/client procs.dm | 24 +- .../preference_setup/general/03_body.dm | 12 +- .../client/preferences/types/game/ui.dm | 18 +- code/modules/keybindings/bindings_movekeys.dm | 4 +- code/modules/tgui/tgui.dm | 7 +- code/modules/tgui_input/color.dm | 2 +- code/modules/tgui_input/say_modal/modal.dm | 4 +- code/modules/tgui_panel/external.dm | 9 +- code/modules/tooltip/tooltip.dm | 3 +- tgui/package.json | 2 +- tgui/packages/common/redux.test.ts | 68 +++ tgui/packages/common/storage.js | 99 ---- tgui/packages/common/vector.ts | 51 -- tgui/packages/tgui-dev-server/webpack.js | 2 +- tgui/packages/tgui-panel/chat/middleware.ts | 1 + tgui/packages/tgui-panel/chat/renderer.tsx | 2 +- tgui/packages/tgui-panel/panelFocus.ts | 2 +- .../tgui-panel/styles/tgchat/chat-dark.scss | 1 - .../tgui-panel/styles/tgchat/chat-light.scss | 1 - .../styles/tgchat/chat-vchatdark.scss | 1 - .../styles/tgchat/chat-vchatlight.scss | 1 - tgui/packages/tgui-say/TguiSay.tsx | 552 ++++++++---------- tgui/packages/tgui-say/constants.ts | 19 +- .../packages/tgui-say/fonts/VT323-Regular.ttf | Bin 147320 -> 0 bytes tgui/packages/tgui-say/helpers.ts | 47 +- tgui/packages/tgui-say/styles/button.scss | 33 -- tgui/packages/tgui-say/styles/colors.scss | 8 +- tgui/packages/tgui-say/styles/content.scss | 41 -- tgui/packages/tgui-say/styles/dragzone.scss | 49 -- tgui/packages/tgui-say/styles/main.scss | 54 +- tgui/packages/tgui-say/styles/styles.scss | 119 ++++ tgui/packages/tgui-say/styles/textarea.scss | 51 -- tgui/packages/tgui-say/styles/window.scss | 47 -- tgui/packages/tgui-say/timers.ts | 13 +- tgui/packages/tgui/components/NanoMap.tsx | 17 + tgui/packages/tgui/drag.ts | 2 +- tgui/packages/tgui/interfaces/AICard.tsx | 2 +- tgui/packages/tgui/interfaces/APC.tsx | 2 +- .../tgui/interfaces/AccountsTerminal.tsx | 2 +- tgui/packages/tgui/interfaces/AgentCard.tsx | 2 +- tgui/packages/tgui/interfaces/AiAirlock.tsx | 2 +- tgui/packages/tgui/interfaces/AiRestorer.tsx | 2 +- .../tgui/interfaces/AiSupermatter.tsx | 2 +- tgui/packages/tgui/interfaces/AirAlarm.tsx | 4 +- .../AppearanceChangerBody.tsx | 2 +- .../AppearanceChangerBodyRecords.tsx | 2 +- .../AppearanceChangerDetails.tsx | 7 +- .../AppearanceChangerFlavor.tsx | 2 +- .../AppearanceChangerHeader.tsx | 2 +- .../AppearanceChangerParts.tsx | 2 +- .../interfaces/AppearanceChanger/constants.ts | 7 + .../interfaces/AppearanceChanger/index.tsx | 2 +- .../interfaces/AppearanceChanger/types.ts | 7 - .../packages/tgui/interfaces/ArcadeBattle.tsx | 2 +- .../tgui/interfaces/AreaScrubberControl.tsx | 2 +- .../tgui/interfaces/AssemblyInfrared.tsx | 2 +- .../packages/tgui/interfaces/AssemblyProx.tsx | 2 +- tgui/packages/tgui/interfaces/AtmosFilter.tsx | 2 +- tgui/packages/tgui/interfaces/AtmosMixer.tsx | 2 +- tgui/packages/tgui/interfaces/Autolathe.tsx | 2 +- tgui/packages/tgui/interfaces/Batteryrack.tsx | 2 +- .../packages/tgui/interfaces/Biogenerator.tsx | 2 +- .../BodyScanner/BodyScannerMain.tsx | 2 +- .../BodyScannerMainAbnormalities.tsx | 2 +- .../BodyScanner/BodyScannerMainDamage.tsx | 2 +- .../BodyScanner/BodyScannerMainOccupant.tsx | 2 +- .../BodyScannerMainOrgansExternal.tsx | 2 +- .../BodyScannerMainOrgansInternal.tsx | 2 +- .../BodyScanner/BodyScannerMainReagents.tsx | 2 +- .../tgui/interfaces/BodyScanner/functions.tsx | 2 +- .../tgui/interfaces/BodyScanner/index.tsx | 2 +- tgui/packages/tgui/interfaces/BombTester.tsx | 2 +- .../packages/tgui/interfaces/BotanyEditor.tsx | 2 +- .../tgui/interfaces/BotanyIsolator.tsx | 2 +- tgui/packages/tgui/interfaces/BrigTimer.tsx | 2 +- .../tgui/interfaces/CameraConsole.tsx | 2 +- tgui/packages/tgui/interfaces/Canister.tsx | 2 +- tgui/packages/tgui/interfaces/Canvas.tsx | 2 +- .../tgui/interfaces/CharacterDirectory.tsx | 2 +- .../ChemDispenser/ChemDispenserBeaker.tsx | 2 +- .../ChemDispenser/ChemDispenserChemicals.tsx | 2 +- .../ChemDispenser/ChemDispenserRecipes.tsx | 2 +- .../ChemDispenser/ChemDispenserSettings.tsx | 2 +- .../tgui/interfaces/ChemDispenser/index.tsx | 2 +- .../ChemMasterAnalyzeModalBodyOverride.tsx | 2 +- .../ChemMaster/ChemMasterBeaker.tsx | 4 +- .../ChemMaster/ChemMasterBuffer.tsx | 4 +- .../ChemMaster/ChemMasterCustomization.tsx | 2 +- .../ChemMaster/ChemMasterProduction.tsx | 2 +- .../tgui/interfaces/ChemMaster/index.tsx | 2 +- tgui/packages/tgui/interfaces/Cleanbot.tsx | 2 +- .../CloningConsoleBodyOverride.tsx | 2 +- .../CloningConsoleNavigation.tsx | 2 +- .../CloningConsole/CloningConsoleStatus.tsx | 2 +- .../CloningConsole/CloningConsoleTabs.tsx | 2 +- .../tgui/interfaces/CloningConsole/index.tsx | 2 +- .../interfaces/ColorMate/ColorMateColor.tsx | 2 +- .../interfaces/ColorMate/ColorMateMatrix.tsx | 2 +- .../tgui/interfaces/ColorMate/index.tsx | 2 +- .../CommunicationsConsoleAuth.tsx | 2 +- .../CommunicationsConsoleContent.tsx | 2 +- .../CommunicationsConsoleMain.tsx | 2 +- .../CommunicationsConsoleMessage.tsx | 2 +- .../CommunicationsConsoleStatusDisplay.tsx | 2 +- .../Communicator/CommunicatorContactTab.tsx | 2 +- .../Communicator/CommunicatorGeneral.tsx | 2 +- .../Communicator/CommunicatorHomeTab.tsx | 2 +- .../CommunicatorMessageSubTab.tsx | 2 +- .../Communicator/CommunicatorMessageTab.tsx | 2 +- .../Communicator/CommunicatorNewsTab.tsx | 2 +- .../Communicator/CommunicatorNoteTab.tsx | 2 +- .../Communicator/CommunicatorPhoneTab.tsx | 2 +- .../Communicator/CommunicatorSettingsTab.tsx | 2 +- .../Communicator/CommunicatorWeatherTab.tsx | 2 +- .../tgui/interfaces/Communicator/index.tsx | 2 +- .../interfaces/ComputerFabricator/CfStep2.tsx | 2 +- .../interfaces/ComputerFabricator/index.tsx | 2 +- .../tgui/interfaces/CookingAppliance.tsx | 2 +- .../CrewMonitor/CrewMonitorContent.tsx | 46 +- .../CrewMonitor/CrewMonitorCrew.tsx | 301 +++++----- .../CrewMonitor/CrewMonitorMapView.tsx | 2 +- .../tgui/interfaces/CrewMonitor/index.tsx | 2 +- .../tgui/interfaces/Cryo/CryoContent.tsx | 2 +- tgui/packages/tgui/interfaces/CryoStorage.tsx | 2 +- .../packages/tgui/interfaces/DNAForensics.tsx | 2 +- .../DNAModifier/DNAModifierMain.tsx | 2 +- .../tgui/interfaces/DiseaseSplicer.tsx | 2 +- tgui/packages/tgui/interfaces/DisposalBin.tsx | 2 +- .../packages/tgui/interfaces/DroneConsole.tsx | 2 +- .../AirlockConsoleAdvanced.tsx | 2 +- .../AirlockConsoleDocking.tsx | 2 +- .../AirlockConsolePhoron.tsx | 2 +- .../AirlockConsoleSimple.tsx | 2 +- .../DockingConsoleMulti.tsx | 2 +- .../DockingConsoleSimple.tsx | 2 +- .../EmbeddedController/DoorAccessConsole.tsx | 2 +- .../EmbeddedControllerHelpers.tsx | 4 +- .../EscapePodBerthConsole.tsx | 2 +- .../EmbeddedController/EscapePodConsole.tsx | 2 +- .../EmbeddedController/PanelOpen.tsx | 2 +- .../interfaces/EmbeddedController/index.tsx | 2 +- .../tgui/interfaces/EntityNarrate.tsx | 2 +- tgui/packages/tgui/interfaces/ExonetNode.tsx | 2 +- .../interfaces/ExosuitFabricator/Material.tsx | 2 +- .../interfaces/ExosuitFabricator/Parts.tsx | 2 +- .../interfaces/ExosuitFabricator/Queue.tsx | 2 +- .../interfaces/ExosuitFabricator/index.tsx | 2 +- tgui/packages/tgui/interfaces/Farmbot.tsx | 2 +- tgui/packages/tgui/interfaces/Fax.tsx | 2 +- tgui/packages/tgui/interfaces/Floorbot.tsx | 2 +- tgui/packages/tgui/interfaces/GasPump.tsx | 2 +- .../tgui/interfaces/GasTemperatureSystem.tsx | 2 +- .../GeneralAtmoControl/FuelControls.tsx | 2 +- .../interfaces/GeneralAtmoControl/Sensors.tsx | 2 +- .../GeneralAtmoControl/TankControls.tsx | 2 +- .../interfaces/GeneralAtmoControl/index.tsx | 2 +- .../GeneralRecords/GeneralRecordsList.tsx | 2 +- .../GeneralRecords/GeneralRecordsOptions.tsx | 2 +- .../GeneralRecordsViewGeneral.tsx | 2 +- .../tgui/interfaces/GeneralRecords/index.tsx | 2 +- tgui/packages/tgui/interfaces/Gps.tsx | 5 +- .../tgui/interfaces/GravityGenerator.tsx | 2 +- tgui/packages/tgui/interfaces/GuestPass.tsx | 2 +- .../tgui/interfaces/GyrotronControl.tsx | 2 +- tgui/packages/tgui/interfaces/Holodeck.tsx | 2 +- .../ICAssembly/CircuitComponent.tsx | 5 +- .../tgui/interfaces/ICAssembly/Port.tsx | 2 +- .../tgui/interfaces/ICAssembly/index.tsx | 2 +- tgui/packages/tgui/interfaces/ICCircuit.tsx | 2 +- tgui/packages/tgui/interfaces/ICPrinter.tsx | 2 +- .../interfaces/IdentificationComputer.tsx | 2 +- .../tgui/interfaces/InventoryPanel.tsx | 2 +- .../tgui/interfaces/InventoryPanelHuman.tsx | 2 +- .../tgui/interfaces/IsolationCentrifuge.tsx | 2 +- tgui/packages/tgui/interfaces/Jukebox.tsx | 2 +- tgui/packages/tgui/interfaces/LawManager.tsx | 2 +- .../packages/tgui/interfaces/LookingGlass.tsx | 2 +- tgui/packages/tgui/interfaces/Medbot.tsx | 2 +- .../MedicalRecords/MedicalRecordsList.tsx | 2 +- .../MedicalRecords/MedicalRecordsMedbots.tsx | 2 +- .../MedicalRecords/MedicalRecordsOptions.tsx | 2 +- .../MedicalRecordsViewGeneral.tsx | 2 +- .../MedicalRecordsViewMedical.tsx | 2 +- .../MedicalRecords/MedicalRecordsViruses.tsx | 2 +- .../tgui/interfaces/MedicalRecords/index.tsx | 2 +- .../MedicalRecords/virusModalBodyOverride.tsx | 2 +- .../MessageMonitor/MessageMonitorContent.tsx | 2 +- .../MessageMonitor/MessageMonitorHack.tsx | 2 +- .../MessageMonitor/MessageMonitorLogin.tsx | 2 +- .../MessageMonitor/MessageMonitorTabs.tsx | 4 +- .../tgui/interfaces/MessageMonitor/index.tsx | 2 +- tgui/packages/tgui/interfaces/Microwave.tsx | 2 +- .../interfaces/MiningOreProcessingConsole.tsx | 2 +- tgui/packages/tgui/interfaces/MobSpawner.tsx | 2 +- .../ModifyRobot/ModifyRobotNoModule.tsx | 2 +- .../ModifyRobotTabs/ModifyRobotAccess.tsx | 9 +- .../ModifyRobotTabs/ModifyRobotComponent.tsx | 9 +- .../ModifyRobotComponentTabs.tsx | 2 +- .../ModifyRobotTabs/ModifyRobotModules.tsx | 7 +- .../ModifyRobotTabs/ModifyRobotPKA.tsx | 2 +- .../ModifyRobotTabs/ModifyRobotRadio.tsx | 9 +- .../ModifyRobotTabs/ModifyRobotUpgrades.tsx | 3 +- .../tgui/interfaces/ModifyRobot/index.tsx | 2 +- tgui/packages/tgui/interfaces/MuleBot.tsx | 2 +- tgui/packages/tgui/interfaces/NIF/NIFMain.tsx | 2 +- .../tgui/interfaces/NIF/NIFSettings.tsx | 2 +- tgui/packages/tgui/interfaces/NIF/index.tsx | 2 +- tgui/packages/tgui/interfaces/NTNetRelay.tsx | 2 +- .../Newscaster/NewscasterMainMenu.tsx | 2 +- .../Newscaster/NewscasterNewChannel.tsx | 2 +- .../Newscaster/NewscasterNewStory.tsx | 2 +- .../Newscaster/NewscasterNewWanted.tsx | 2 +- .../interfaces/Newscaster/NewscasterPrint.tsx | 2 +- .../Newscaster/NewscasterViewList.tsx | 2 +- .../Newscaster/NewscasterViewSelected.tsx | 2 +- .../Newscaster/NewscasterViewWanted.tsx | 2 +- tgui/packages/tgui/interfaces/NoticeBoard.tsx | 2 +- .../tgui/interfaces/NtosAccessDecrypter.tsx | 2 +- tgui/packages/tgui/interfaces/NtosArcade.tsx | 2 +- .../tgui/interfaces/NtosConfiguration.tsx | 2 +- .../tgui/interfaces/NtosDigitalWarrant.tsx | 2 +- .../interfaces/NtosEmailAdministration.tsx | 2 +- .../tgui/interfaces/NtosEmailClient.tsx | 2 +- .../tgui/interfaces/NtosFileManager.tsx | 2 +- tgui/packages/tgui/interfaces/NtosMain.tsx | 2 +- tgui/packages/tgui/interfaces/NtosNetChat.tsx | 2 +- tgui/packages/tgui/interfaces/NtosNetDos.tsx | 2 +- .../tgui/interfaces/NtosNetDownloader.tsx | 2 +- .../tgui/interfaces/NtosNetMonitor.tsx | 2 +- .../tgui/interfaces/NtosNetTransfer.tsx | 2 +- .../tgui/interfaces/NtosNewsBrowser.tsx | 2 +- .../tgui/interfaces/NtosRevelation.tsx | 2 +- tgui/packages/tgui/interfaces/NtosUAV.tsx | 2 +- .../tgui/interfaces/NtosWordProcessor.tsx | 2 +- tgui/packages/tgui/interfaces/OmniFilter.tsx | 2 +- tgui/packages/tgui/interfaces/OmniMixer.tsx | 2 +- .../OperatingComputerOptions.tsx | 2 +- .../OperatingComputerPatient.tsx | 2 +- .../interfaces/OperatingComputer/index.tsx | 2 +- .../tgui/interfaces/OvermapDisperser.tsx | 2 +- .../tgui/interfaces/OvermapEngines.tsx | 2 +- tgui/packages/tgui/interfaces/OvermapHelm.tsx | 2 +- .../tgui/interfaces/OvermapNavigation.tsx | 2 +- .../interfaces/OvermapShieldGenerator.tsx | 2 +- .../tgui/interfaces/OvermapShipSensors.tsx | 2 +- .../tgui/interfaces/ParticleAccelerator.tsx | 2 +- tgui/packages/tgui/interfaces/PartsLathe.tsx | 4 +- .../tgui/interfaces/PathogenicIsolator.tsx | 4 +- tgui/packages/tgui/interfaces/Pda/index.tsx | 2 +- .../Pda/pda_screens/pda_main_menu.tsx | 2 +- .../Pda/pda_screens/pda_medical.tsx | 2 +- .../Pda/pda_screens/pda_messenger.tsx | 2 +- .../interfaces/Pda/pda_screens/pda_news.tsx | 2 +- .../Pda/pda_screens/pda_security.tsx | 2 +- .../tgui/interfaces/PersonalCrafting.tsx | 3 +- .../interfaces/PetrificationInterface.tsx | 2 +- tgui/packages/tgui/interfaces/Photocopier.tsx | 2 +- .../tgui/interfaces/PipeDispenser.tsx | 2 +- .../tgui/interfaces/PlantAnalyzer.tsx | 2 +- .../tgui/interfaces/PlayerEffects/index.tsx | 2 +- .../tgui/interfaces/PointDefenseControl.tsx | 2 +- .../tgui/interfaces/PortableGenerator.tsx | 2 +- .../packages/tgui/interfaces/PortablePump.tsx | 2 +- .../tgui/interfaces/PortableTurret.tsx | 2 +- .../PowerMonitor/PowerMonitorContent.tsx | 2 +- .../PowerMonitor/PowerMonitorFocus.tsx | 2 +- .../PreferencesMenu/GamePreferenceWindow.tsx | 2 +- .../PreferencesMenu/GamePreferencesPage.tsx | 2 +- .../ServerPreferencesFetcher.tsx | 2 +- .../preferences/features/base.tsx | 4 +- .../preferences/features/dropdowns.tsx | 6 +- .../features/game_preferences/fps.tsx | 2 +- .../features/game_preferences/ui.tsx | 7 + .../tgui/interfaces/PressureRegulator.tsx | 2 +- .../tgui/interfaces/PrisonerManagement.tsx | 2 +- .../tgui/interfaces/RCON/RCONBreakerList.tsx | 2 +- .../tgui/interfaces/RCON/RCONSMESControls.tsx | 4 +- .../tgui/interfaces/RCON/RCONSMESItem.tsx | 2 +- .../tgui/interfaces/RCON/RCONSmesList.tsx | 2 +- .../interfaces/RIGSuit/RIGSuitHardware.tsx | 2 +- .../interfaces/RIGSuit/RIGSuitModules.tsx | 2 +- .../tgui/interfaces/RIGSuit/RIGSuitStatus.tsx | 2 +- .../tgui/interfaces/RIGSuit/index.tsx | 2 +- tgui/packages/tgui/interfaces/Radio.tsx | 2 +- .../RapidPipeDispenser/LayerSection.tsx | 2 +- .../RapidPipeDispenser/PipeTypeSection.tsx | 2 +- .../RapidPipeDispenser/SelectionSection.tsx | 2 +- .../RequestConsole/RequestConsolTypes.tsx | 2 +- .../RequestConsole/RequestConsoleMessage.tsx | 2 +- .../RequestConsole/RequestConsoleSettings.tsx | 2 +- .../tgui/interfaces/RequestConsole/index.tsx | 2 +- .../tgui/interfaces/ResearchConsole/index.tsx | 2 +- .../ResearchConsole/pages/Constructor.tsx | 6 +- .../ResearchConsole/pages/DesignList.tsx | 2 +- .../pages/DestructiveAnalyzer.tsx | 2 +- .../interfaces/ResearchConsole/pages/Misc.tsx | 2 +- .../ResearchConsole/pages/ResearchList.tsx | 2 +- .../interfaces/ResearchServerController.tsx | 2 +- .../ResleevingConsole/BodyRecordModal.tsx | 2 +- .../ResleevingConsole/MindRecordModal.tsx | 2 +- .../ResleevingConsoleElements.tsx | 2 +- .../ResleevingConsolePodGrowers.tsx | 2 +- .../ResleevingConsolePodSleevers.tsx | 2 +- .../ResleevingConsolePodSpods.tsx | 2 +- .../ResleevingConsoleRecords.tsx | 2 +- .../ResleevingConsoleStatus.tsx | 2 +- .../interfaces/ResleevingConsole/index.tsx | 2 +- .../tgui/interfaces/ResleevingPod.tsx | 2 +- .../interfaces/RobotChoose/SpriteSection.tsx | 2 +- .../tgui/interfaces/RobotChoose/index.tsx | 2 +- .../tgui/interfaces/Robotact/Diagnostics.tsx | 2 +- .../tgui/interfaces/Robotact/Modules.tsx | 2 +- .../tgui/interfaces/Robotact/StatusScreen.tsx | 2 +- .../tgui/interfaces/Robotact/index.tsx | 2 +- .../interfaces/RoboticsControlConsole.tsx | 2 +- tgui/packages/tgui/interfaces/RogueZones.tsx | 2 +- .../tgui/interfaces/RustCoreMonitor.tsx | 2 +- .../tgui/interfaces/RustFuelControl.tsx | 2 +- tgui/packages/tgui/interfaces/Secbot.tsx | 2 +- tgui/packages/tgui/interfaces/SecureSafe.tsx | 2 +- .../SecurityRecords/SecurityRecordsList.tsx | 2 +- .../SecurityRecordsOptions.tsx | 2 +- .../SecurityRecordsViewGeneral.tsx | 2 +- .../SecurityRecordsViewSecurity.tsx | 2 +- .../tgui/interfaces/SecurityRecords/index.tsx | 2 +- .../tgui/interfaces/ShieldCapacitor.tsx | 2 +- .../tgui/interfaces/ShieldGenerator.tsx | 2 +- .../tgui/interfaces/ShutoffMonitor.tsx | 2 +- .../ShuttleControlConsoleTypes.tsx | 2 +- .../ShuttleControlConsoleWeb.tsx | 2 +- .../ShuttleControlSharedShuttleControls.tsx | 2 +- .../ShuttleControlSharedShuttleStatus.tsx | 2 +- .../interfaces/ShuttleControl/functions.tsx | 2 +- .../tgui/interfaces/ShuttleControl/index.tsx | 2 +- .../interfaces/Sleeper/SleeperChemicals.tsx | 2 +- .../tgui/interfaces/Sleeper/SleeperDamage.tsx | 2 +- .../Sleeper/SleeperDialysisPump.tsx | 4 +- .../tgui/interfaces/Sleeper/SleeperEmpty.tsx | 2 +- .../tgui/interfaces/Sleeper/SleeperMain.tsx | 2 +- .../interfaces/Sleeper/SleeperOccupant.tsx | 2 +- .../tgui/interfaces/Sleeper/index.tsx | 2 +- tgui/packages/tgui/interfaces/SmartVend.tsx | 2 +- tgui/packages/tgui/interfaces/Smes.tsx | 2 +- .../packages/tgui/interfaces/SolarControl.tsx | 2 +- tgui/packages/tgui/interfaces/SpaceHeater.tsx | 2 +- .../tgui/interfaces/StationAlertConsole.tsx | 2 +- .../tgui/interfaces/StockExchange.tsx | 2 +- tgui/packages/tgui/interfaces/SuitCycler.tsx | 2 +- .../tgui/interfaces/SuitStorageUnit.tsx | 2 +- .../tgui/interfaces/SupermatterMonitor.tsx | 2 +- .../SupplyConsoleMenuHistoryExport.tsx | 2 +- .../SupplyConsole/SupplyConsoleMenuOrder.tsx | 4 +- .../SupplyConsoleMenuOrderList.tsx | 2 +- .../SupplyConsoleShuttleStatus.tsx | 2 +- .../SupplyConsole/viewCrateContents.tsx | 2 +- tgui/packages/tgui/interfaces/Tank.tsx | 2 +- .../tgui/interfaces/TelecommsLogBrowser.tsx | 2 +- .../interfaces/TelecommsMultitoolMenu.tsx | 2 +- tgui/packages/tgui/interfaces/Teleporter.tsx | 2 +- .../tgui/interfaces/TelesciConsole.tsx | 2 +- .../tgui/interfaces/TextInputModal.tsx | 1 + tgui/packages/tgui/interfaces/TimeClock.tsx | 2 +- .../tgui/interfaces/TransferValve.tsx | 2 +- .../tgui/interfaces/TurbineControl.tsx | 2 +- tgui/packages/tgui/interfaces/Turbolift.tsx | 2 +- .../Uplink/ExploitableInformation.tsx | 2 +- .../tgui/interfaces/Uplink/GenericUplink.tsx | 2 +- .../tgui/interfaces/Uplink/ItemList.tsx | 4 +- .../tgui/interfaces/Uplink/UplinkHeader.tsx | 2 +- .../packages/tgui/interfaces/Uplink/index.tsx | 2 +- tgui/packages/tgui/interfaces/Vending.tsx | 2 +- .../VoreBellySelectionAndCustomization.tsx | 4 +- .../VorePanel/VoreContentsPanel.tsx | 4 +- .../interfaces/VorePanel/VoreInsidePanel.tsx | 4 +- .../VorePanel/VoreSelectedBelly.tsx | 4 +- ...VoreSelectedBellyDescriptionsBellymode.tsx | 2 +- .../VoreSelectedBellyDescriptionsEscape.tsx | 4 +- .../VoreSelectedBellyDescriptionsIdle.tsx | 2 +- ...ctedBellyDescriptionsInteractionChance.tsx | 4 +- .../VoreSelectedBellyDescriptionsTransfer.tsx | 4 +- .../VoreSelectedBellyControls.tsx | 2 +- .../VoreSelectedBellyDescriptions.tsx | 2 +- .../VoreSelectedBellyOptions.tsx | 2 +- .../VoreSelectedBellySounds.tsx | 2 +- .../VoreSelectedBellyVisuals.tsx | 2 +- .../VoreSelectedMobTypeBellyButtons.tsx | 2 +- .../VorePanel/VoreUserPreferenceItem.tsx | 2 +- .../VorePanel/VoreUserPreferences.tsx | 4 +- .../VoreUserPreferencesAesthetic .tsx | 2 +- .../VoreUserPreferencesMechanical .tsx | 4 +- .../tgui/interfaces/VorePanel/index.tsx | 2 +- .../VorePanelExportBellyString.tsx | 2 +- .../VorePanelExportDownload.tsx | 2 +- .../tgui/interfaces/VorePanelExport/index.tsx | 2 +- tgui/packages/tgui/interfaces/Wires.tsx | 2 +- .../packages/tgui/interfaces/WiresAirlock.tsx | 2 +- .../interfaces/XenoarchArtifactAnalyzer.tsx | 2 +- .../interfaces/XenoarchArtifactHarvester.tsx | 2 +- .../XenoarchHandheldPowerUtilizer.tsx | 2 +- .../tgui/interfaces/XenoarchReplicator.tsx | 2 +- .../XenoarchReplicator_clothing_vr.tsx | 2 +- .../XenoarchReplicator_voremob_vr.tsx | 2 +- .../tgui/interfaces/XenoarchSpectrometer.tsx | 2 +- .../tgui/interfaces/XenoarchSuspension.tsx | 2 +- .../tgui/interfaces/common/AtmosControls.tsx | 2 +- .../common/InterfaceLockNoticeBox.tsx | 2 +- .../tgui/interfaces/common/LoginScreen.tsx | 2 +- .../tgui/interfaces/common/Overmap.tsx | 2 +- .../tgui/interfaces/common/PortableAtmos.tsx | 2 +- .../interfaces/common/TemporaryNotice.tsx | 2 +- tgui/packages/tgui/interfaces/pAIDoorjack.tsx | 2 +- .../packages/tgui/interfaces/pAIInterface.tsx | 2 +- .../tgui/interfaces/pAIMedrecords.tsx | 4 +- .../tgui/interfaces/pAISecrecords.tsx | 8 +- .../styles/interfaces/IntegratedCircuit.scss | 2 - tgui/packages/tgui/styles/layouts/Layout.scss | 34 -- .../tgui/styles/layouts/NtosWindow.scss | 1 - .../tgui/styles/layouts/TitleBar.scss | 1 - tgui/packages/tgui/styles/themes/paper.scss | 7 - tgui/webpack.config.edge.js | 147 ----- tgui/webpack.config.js | 13 +- 422 files changed, 1193 insertions(+), 1595 deletions(-) create mode 100644 tgui/packages/common/redux.test.ts delete mode 100644 tgui/packages/common/vector.ts delete mode 100644 tgui/packages/tgui-say/fonts/VT323-Regular.ttf delete mode 100644 tgui/packages/tgui-say/styles/button.scss delete mode 100644 tgui/packages/tgui-say/styles/content.scss delete mode 100644 tgui/packages/tgui-say/styles/dragzone.scss create mode 100644 tgui/packages/tgui-say/styles/styles.scss delete mode 100644 tgui/packages/tgui-say/styles/textarea.scss delete mode 100644 tgui/packages/tgui-say/styles/window.scss delete mode 100644 tgui/webpack.config.edge.js diff --git a/code/modules/asset_cache/assets/tgui.dm b/code/modules/asset_cache/assets/tgui.dm index 03e2acd449..9c79925602 100644 --- a/code/modules/asset_cache/assets/tgui.dm +++ b/code/modules/asset_cache/assets/tgui.dm @@ -5,13 +5,6 @@ "tgui.bundle.css" = file("tgui/public/tgui.bundle.css"), ) -/datum/asset/simple/tgui_edge - keep_local_name = TRUE - assets = list( - "tgui.bundle.edge.js" = file("tgui/public/tgui.bundle.edge.js"), - "tgui.bundle.edge.css" = file("tgui/public/tgui.bundle.edge.css"), - ) - /datum/asset/simple/tgui_panel keep_local_name = TRUE assets = list( diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 446ab4deba..c6076ed56c 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -226,9 +226,7 @@ //CONNECT// /////////// /client/New(TopicData) - // TODO: Remove version check with 516 - if(byond_version >= 516) // Enable 516 compat browser storage mechanisms - winset(src, null, "browser-options=[DEFAULT_CLIENT_BROWSER_OPTIONS]") + winset(src, null, "browser-options=[DEFAULT_CLIENT_BROWSER_OPTIONS]") TopicData = null //Prevent calls to client.Topic from connect @@ -360,11 +358,9 @@ fully_created = TRUE attempt_auto_fit_viewport() - // TODO: Remove version check with 516 - if(byond_version >= 516) - // Now that we're fully initialized, use our prefs - if(prefs?.read_preference(/datum/preference/toggle/browser_dev_tools)) - winset(src, null, "browser-options=[DEFAULT_CLIENT_BROWSER_OPTIONS],devtools") + // Now that we're fully initialized, use our prefs + if(prefs?.read_preference(/datum/preference/toggle/browser_dev_tools)) + winset(src, null, "browser-options=[DEFAULT_CLIENT_BROWSER_OPTIONS],devtools") ////////////// //DISCONNECT// @@ -672,18 +668,6 @@ src << browse("[message]","window=dropmessage;size=480x360;can_close=1") qdel(src) -/// Keydown event in a tgui window this client has open. Has keycode passed to it. -/client/verb/TguiKeyDown(keycode as text) - set name = "TguiKeyDown" - set hidden = TRUE - return // stub - -/// Keyup event in a tgui window this client has open. Has keycode passed to it. -/client/verb/TguiKeyUp(keycode as text) // Doesn't seem to currently fire? - set name = "TguiKeyUp" - set hidden = TRUE - return // stub - /client/verb/toggle_fullscreen() set name = "Toggle Fullscreen" set category = "OOC.Client Settings" diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index af656eef26..15a01122a8 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -341,7 +341,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/key = pref.rlimb_data[name] if(!istext(key)) log_debug("Bad rlimb_data for [key_name(pref.client)], [name] was set to [key]") - to_chat(user, span_warning("Error loading robot limb data for `[name]`, clearing pref.")) + to_chat(usr, span_warning("Error loading robot limb data for `[name]`, clearing pref.")) pref.rlimb_data -= name else R = LAZYACCESS(all_robolimbs, key) @@ -552,7 +552,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.set_biological_gender(mob_species.genders[1]) pref.custom_species = null //grab one of the valid hair styles for the newly chosen species - var/list/valid_hairstyles = pref.get_valid_hairstyles(user) + var/list/valid_hairstyles = pref.get_valid_hairstyles() if(valid_hairstyles.len) if(!(pref.h_style in valid_hairstyles)) @@ -604,7 +604,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["hair_style"]) - var/list/valid_hairstyles = pref.get_valid_hairstyles(user) + var/list/valid_hairstyles = pref.get_valid_hairstyles() var/new_h_style = tgui_input_list(user, "Choose your character's hair style:", "Character Preference", valid_hairstyles, pref.h_style) if(new_h_style && CanUseTopic(user)) @@ -629,7 +629,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else if(href_list["hair_style_left"]) var/H = href_list["hair_style_left"] - var/list/valid_hairstyles = pref.get_valid_hairstyles(user) + var/list/valid_hairstyles = pref.get_valid_hairstyles() var/start = valid_hairstyles.Find(H) if(start != 1) //If we're not the beginning of the list, become the previous element. @@ -640,7 +640,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else if(href_list["hair_style_right"]) var/H = href_list["hair_style_right"] - var/list/valid_hairstyles = pref.get_valid_hairstyles(user) + var/list/valid_hairstyles = pref.get_valid_hairstyles() var/start = valid_hairstyles.Find(H) if(start != valid_hairstyles.len) //If we're not the end of the list, become the next element. @@ -1054,7 +1054,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["ear_color2"]) - var/new_earc2 = tgui_color_picker(user, "Choose your character's ear colour:", "Character Preference", + var/new_earc2 = tgui_color_picker(user, "Choose your character's secondary ear colour:", "Character Preference", pref.read_preference(/datum/preference/color/human/ears_color2)) if(new_earc2) pref.update_preference_by_type(/datum/preference/color/human/ears_color2, new_earc2) diff --git a/code/modules/client/preferences/types/game/ui.dm b/code/modules/client/preferences/types/game/ui.dm index 3f110e0680..cf50190be8 100644 --- a/code/modules/client/preferences/types/game/ui.dm +++ b/code/modules/client/preferences/types/game/ui.dm @@ -81,7 +81,7 @@ savefile_identifier = PREFERENCE_PLAYER minimum = 1 - maximum = 5 + maximum = 20 step = 1 /datum/preference/numeric/tgui_say_height/create_default_value() @@ -90,6 +90,22 @@ /datum/preference/numeric/tgui_say_height/apply_to_client(client/client, value) client.tgui_say?.load() + +/datum/preference/numeric/tgui_say_width + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "tgui_say_width" + savefile_identifier = PREFERENCE_PLAYER + + minimum = 360 + maximum = 800 + step = 20 + +/datum/preference/numeric/tgui_say_width/create_default_value() + return 360 + +/datum/preference/numeric/tgui_say_width/apply_to_client(client/client, value) + client.tgui_say?.load() + /datum/preference/text/preset_colors category = PREFERENCE_CATEGORY_MANUALLY_RENDERED savefile_identifier = PREFERENCE_PLAYER diff --git a/code/modules/keybindings/bindings_movekeys.dm b/code/modules/keybindings/bindings_movekeys.dm index 896618084a..bd15f10096 100644 --- a/code/modules/keybindings/bindings_movekeys.dm +++ b/code/modules/keybindings/bindings_movekeys.dm @@ -25,7 +25,7 @@ var/global/list/MOVE_KEY_MAPPINGS = list( // Validate input. Must be one (and only one) of the key codes) if(isnull(movekey) || (movekey & ~0xFFF) || (movekey & (movekey - 1))) - log_debug("Client [ckey] sent an illegal movement key down: [movekeyName] ([movekey])") + // og_debug("Client [ckey] sent an illegal movement key down: [movekeyName] ([movekey])") // We forward tgui keys nowadays return // Record that we are now holding the key! @@ -57,7 +57,7 @@ var/global/list/MOVE_KEY_MAPPINGS = list( // Validate input. Must be one (and only one) of the key codes) if(isnull(movekey) || (movekey & ~0xFFF) || (movekey & (movekey - 1))) - log_debug("Client [ckey] sent an illegal movement key up: [movekeyName] ([movekey])") + // log_debug("Client [ckey] sent an illegal movement key up: [movekeyName] ([movekey])") // We forward tgui keys nowadays return // Clear bit indicating we were holding the key diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index c95ab2ff64..f337b41cdc 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -106,11 +106,8 @@ strict_mode = TRUE, fancy = user.read_preference(/datum/preference/toggle/tgui_fancy), assets = list( - // FIXME: Delete this when 516 is required! - user.client.byond_version >= 516 \ - ? get_asset_datum(/datum/asset/simple/tgui_edge) \ - : get_asset_datum(/datum/asset/simple/tgui), - )) + get_asset_datum(/datum/asset/simple/tgui), + )) else window.send_message("ping") send_assets() diff --git a/code/modules/tgui_input/color.dm b/code/modules/tgui_input/color.dm index 83b22e206c..23c1122a92 100644 --- a/code/modules/tgui_input/color.dm +++ b/code/modules/tgui_input/color.dm @@ -18,7 +18,7 @@ else return // Client does NOT have tgui_input on: Returns regular input - if(!user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_mode) || user.client.byond_version < 516) // Todo, remove once virgo is on 516, but currently the TGUI interface is already programmed for edge + if(!user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_mode)) return input(user, message, title, default) as color|null var/datum/tgui_color_picker/picker = new(user, message, title, default, timeout, autofocus) picker.tgui_interact(user) diff --git a/code/modules/tgui_input/say_modal/modal.dm b/code/modules/tgui_input/say_modal/modal.dm index 40dba424c0..c43554ac12 100644 --- a/code/modules/tgui_input/say_modal/modal.dm +++ b/code/modules/tgui_input/say_modal/modal.dm @@ -65,11 +65,13 @@ window_open = FALSE var/minimumHeight = client?.prefs?.read_preference(/datum/preference/numeric/tgui_say_height) || 1 - winset(client, "tgui_say", "pos=410,400;size=360,[(minimumHeight * 20) + 10];is-visible=0;") + var/minimumWidth = client?.prefs?.read_preference(/datum/preference/numeric/tgui_say_width) || 1 + winset(client, "tgui_say", "pos=410,400;size=360,30;is-visible=0;") window.send_message("props", list( lightMode = client?.prefs?.read_preference(/datum/preference/toggle/tgui_say_light), minimumHeight = minimumHeight, + minimumWidth = minimumWidth, maxLength = max_length, )) diff --git a/code/modules/tgui_panel/external.dm b/code/modules/tgui_panel/external.dm index 8912eb8c12..6574dd6ec9 100644 --- a/code/modules/tgui_panel/external.dm +++ b/code/modules/tgui_panel/external.dm @@ -32,12 +32,9 @@ tgui_panel.initialize(force = TRUE) // Force show the panel to see if there are any errors winset(src, "legacy_output_selector", "left=output_browser") - // TODO: Remove version check with 516 - if(byond_version >= 516) - if(prefs?.read_preference(/datum/preference/toggle/browser_dev_tools)) - winset(src, null, "browser-options=[DEFAULT_CLIENT_BROWSER_OPTIONS],devtools") - else - winset(src, null, "browser-options=[DEFAULT_CLIENT_BROWSER_OPTIONS]") + + if(prefs?.read_preference(/datum/preference/toggle/browser_dev_tools)) + winset(src, null, "browser-options=[DEFAULT_CLIENT_BROWSER_OPTIONS],devtools") /client/verb/refresh_tgui() set name = "Refresh TGUI" diff --git a/code/modules/tooltip/tooltip.dm b/code/modules/tooltip/tooltip.dm index 27c0e79d6f..cd267521e5 100644 --- a/code/modules/tooltip/tooltip.dm +++ b/code/modules/tooltip/tooltip.dm @@ -111,7 +111,8 @@ Notes: last_target = null /datum/tooltip/proc/do_hide() - winshow(owner, control, FALSE) + if(owner) + winshow(owner, control, FALSE) /datum/tooltip/Destroy(force) last_target = null diff --git a/tgui/package.json b/tgui/package.json index 7f0bf06de2..960b6df590 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -9,7 +9,7 @@ "scripts": { "tgui:analyze": "webpack --analyze", "tgui:bench": "webpack --env TGUI_BENCH=1 && node packages/tgui-bench/index.js", - "tgui:build": "BROWSERSLIST_IGNORE_OLD_DATA=true webpack && webpack --config ./webpack.config.edge.js", + "tgui:build": "BROWSERSLIST_IGNORE_OLD_DATA=true webpack", "tgui:dev": "node --experimental-modules packages/tgui-dev-server/index.js", "tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx", "tgui:prettier": "prettier --check .", diff --git a/tgui/packages/common/redux.test.ts b/tgui/packages/common/redux.test.ts new file mode 100644 index 0000000000..d4af99907c --- /dev/null +++ b/tgui/packages/common/redux.test.ts @@ -0,0 +1,68 @@ +import { + Action, + applyMiddleware, + combineReducers, + createAction, + createStore, + Reducer, +} from './redux'; + +// Dummy Reducer +const counterReducer: Reducer> = (state = 0, action) => { + switch (action.type) { + case 'INCREMENT': + return state + 1; + case 'DECREMENT': + return state - 1; + default: + return state; + } +}; + +// Dummy Middleware +const loggingMiddleware = (storeApi) => (next) => (action) => { + console.log('Middleware:', action); + return next(action); +}; + +// Dummy Action Creators +const increment = createAction('INCREMENT'); +const decrement = createAction('DECREMENT'); + +describe('Redux implementation tests', () => { + test('createStore works', () => { + const store = createStore(counterReducer); + expect(store.getState()).toBe(0); + }); + + test('createStore with applyMiddleware works', () => { + const store = createStore( + counterReducer, + applyMiddleware(loggingMiddleware), + ); + expect(store.getState()).toBe(0); + }); + + test('dispatch works', () => { + const store = createStore(counterReducer); + store.dispatch(increment()); + expect(store.getState()).toBe(1); + store.dispatch(decrement()); + expect(store.getState()).toBe(0); + }); + + test('combineReducers works', () => { + const rootReducer = combineReducers({ + counter: counterReducer, + }); + const store = createStore(rootReducer); + expect(store.getState()).toEqual({ counter: 0 }); + }); + + test('createAction works', () => { + const incrementAction = increment(); + expect(incrementAction).toEqual({ type: 'INCREMENT' }); + const decrementAction = decrement(); + expect(decrementAction).toEqual({ type: 'DECREMENT' }); + }); +}); diff --git a/tgui/packages/common/storage.js b/tgui/packages/common/storage.js index 2235199075..fda143c97a 100644 --- a/tgui/packages/common/storage.js +++ b/tgui/packages/common/storage.js @@ -29,36 +29,6 @@ const testHubStorage = testGeneric( () => window.hubStorage && window.hubStorage.getItem, ); -// TODO: Remove with 516 -// prettier-ignore -const testIndexedDb = testGeneric(() => ( - (window.indexedDB || window.msIndexedDB) - && (window.IDBTransaction || window.msIDBTransaction) -)); - -class MemoryBackend { - constructor() { - this.impl = IMPL_MEMORY; - this.store = {}; - } - - async get(key) { - return this.store[key]; - } - - async set(key, value) { - this.store[key] = value; - } - - async remove(key) { - this.store[key] = undefined; - } - - async clear() { - this.store = {}; - } -} - class HubStorageBackend { constructor() { this.impl = IMPL_HUB_STORAGE; @@ -84,63 +54,6 @@ class HubStorageBackend { } } -class IndexedDbBackend { - // TODO: Remove with 516 - constructor() { - this.impl = IMPL_INDEXED_DB; - /** @type {Promise} */ - this.dbPromise = new Promise((resolve, reject) => { - const indexedDB = window.indexedDB || window.msIndexedDB; - const req = indexedDB.open(INDEXED_DB_NAME, INDEXED_DB_VERSION); - req.onupgradeneeded = () => { - try { - req.result.createObjectStore(INDEXED_DB_STORE_NAME); - } catch (err) { - reject(new Error('Failed to upgrade IDB: ' + req.error)); - } - }; - req.onsuccess = () => resolve(req.result); - req.onerror = () => { - reject(new Error('Failed to open IDB: ' + req.error)); - }; - }); - } - - async getStore(mode) { - // prettier-ignore - return this.dbPromise.then((db) => db - .transaction(INDEXED_DB_STORE_NAME, mode) - .objectStore(INDEXED_DB_STORE_NAME)); - } - - async get(key) { - const store = await this.getStore(READ_ONLY); - return new Promise((resolve, reject) => { - const req = store.get(key); - req.onsuccess = () => resolve(req.result); - req.onerror = () => reject(req.error); - }); - } - - async set(key, value) { - // NOTE: We deliberately make this operation transactionless - const store = await this.getStore(READ_WRITE); - store.put(value, key); - } - - async remove(key) { - // NOTE: We deliberately make this operation transactionless - const store = await this.getStore(READ_WRITE); - store.delete(key); - } - - async clear() { - // NOTE: We deliberately make this operation transactionless - const store = await this.getStore(READ_WRITE); - store.clear(); - } -} - /** * Web Storage Proxy object, which selects the best backend available * depending on the environment. @@ -151,18 +64,6 @@ class StorageProxy { if (!Byond.TRIDENT && testHubStorage()) { return new HubStorageBackend(); } - // TODO: Remove with 516 - if (testIndexedDb()) { - try { - const backend = new IndexedDbBackend(); - await backend.dbPromise; - return backend; - } catch {} - } - console.warn( - 'No supported storage backend found. Using in-memory storage.', - ); - return new MemoryBackend(); })(); } diff --git a/tgui/packages/common/vector.ts b/tgui/packages/common/vector.ts deleted file mode 100644 index c91715a8f9..0000000000 --- a/tgui/packages/common/vector.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * N-dimensional vector manipulation functions. - * - * Vectors are plain number arrays, i.e. [x, y, z]. - * - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { map, reduce, zip } from './collections'; - -const ADD = (a: number, b: number): number => a + b; -const SUB = (a: number, b: number): number => a - b; -const MUL = (a: number, b: number): number => a * b; -const DIV = (a: number, b: number): number => a / b; - -export type Vector = number[]; - -export const vecAdd = (...vecs: Vector[]): Vector => { - return map(zip(...vecs), (x) => reduce(x, ADD)); -}; - -export const vecSubtract = (...vecs: Vector[]): Vector => { - return map(zip(...vecs), (x) => reduce(x, SUB)); -}; - -export const vecMultiply = (...vecs: Vector[]): Vector => { - return map(zip(...vecs), (x) => reduce(x, MUL)); -}; - -export const vecDivide = (...vecs: Vector[]): Vector => { - return map(zip(...vecs), (x) => reduce(x, DIV)); -}; - -export const vecScale = (vec: Vector, n: number): Vector => { - return map(vec, (x) => x * n); -}; - -export const vecInverse = (vec: Vector): Vector => { - return map(vec, (x) => -x); -}; - -export const vecLength = (vec: Vector): number => { - return Math.sqrt(reduce(vecMultiply(vec, vec), ADD)); -}; - -export const vecNormalize = (vec: Vector): Vector => { - const length = vecLength(vec); - return map(vec, (c) => c / length); -}; diff --git a/tgui/packages/tgui-dev-server/webpack.js b/tgui/packages/tgui-dev-server/webpack.js index 4addb3486d..e4fbdeb9f1 100644 --- a/tgui/packages/tgui-dev-server/webpack.js +++ b/tgui/packages/tgui-dev-server/webpack.js @@ -31,7 +31,7 @@ class WebpackCompiler { // and retrieve all necessary dependencies. const requireFromRoot = createRequire(dirname(import.meta.url) + '/../..'); const webpack = await requireFromRoot('webpack'); - const createConfig = await requireFromRoot('./webpack.config.edge.js'); + const createConfig = await requireFromRoot('./webpack.config.js'); const config = createConfig({}, options); // Inject the HMR plugin into the config if we're using it if (options.hot) { diff --git a/tgui/packages/tgui-panel/chat/middleware.ts b/tgui/packages/tgui-panel/chat/middleware.ts index ab0fd29f87..fef65dc38d 100644 --- a/tgui/packages/tgui-panel/chat/middleware.ts +++ b/tgui/packages/tgui-panel/chat/middleware.ts @@ -217,6 +217,7 @@ export const chatMiddleware = (store) => { chatRenderer.processBatch([payload_obj.content], { doArchive: true, }); + sequences.push(sequence); if (game.roundId !== settings.lastId) { storedRounds.push(game.roundId); storedLines.push(settings.totalStoredMessages - 1); diff --git a/tgui/packages/tgui-panel/chat/renderer.tsx b/tgui/packages/tgui-panel/chat/renderer.tsx index fd8ff7ebeb..c70ed28c14 100644 --- a/tgui/packages/tgui-panel/chat/renderer.tsx +++ b/tgui/packages/tgui-panel/chat/renderer.tsx @@ -29,7 +29,7 @@ import { typeIsImportant, } from './model'; import { highlightNode, linkifyNode } from './replaceInTextNode'; -import { message } from './types'; +import type { message } from './types'; const logger = createLogger('chatRenderer'); diff --git a/tgui/packages/tgui-panel/panelFocus.ts b/tgui/packages/tgui-panel/panelFocus.ts index 4973e457ed..d47207d209 100644 --- a/tgui/packages/tgui-panel/panelFocus.ts +++ b/tgui/packages/tgui-panel/panelFocus.ts @@ -7,9 +7,9 @@ * @license MIT */ -import { vecLength, vecSubtract } from 'common/vector'; import { focusMap } from 'tgui/focus'; import { canStealFocus, globalEvents } from 'tgui-core/events'; +import { vecLength, vecSubtract } from 'tgui-core/vector'; // Empyrically determined number for the smallest possible // text you can select with the mouse. diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index 1aa5fe24fb..dd7a16499a 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -15,7 +15,6 @@ img { margin: 0; padding: 0; line-height: 1; - -ms-interpolation-mode: nearest-neighbor; // TODO: Remove with 516 image-rendering: pixelated; } diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss index a769314e1b..ff4a35f8e4 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss @@ -33,7 +33,6 @@ img { margin: 0; padding: 0; line-height: 1; - -ms-interpolation-mode: nearest-neighbor; // TODO: Remove with 516 image-rendering: pixelated; } diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss index b660428ad9..3379b949ef 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss @@ -15,7 +15,6 @@ img { margin: 0; padding: 0; line-height: 1; - -ms-interpolation-mode: nearest-neighbor; // TODO: Remove with 516 image-rendering: pixelated; } diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss index 0345879cb0..f5b53a80c2 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss @@ -33,7 +33,6 @@ img { margin: 0; padding: 0; line-height: 1; - -ms-interpolation-mode: nearest-neighbor; // TODO: Remove with 516 image-rendering: pixelated; } diff --git a/tgui/packages/tgui-say/TguiSay.tsx b/tgui/packages/tgui-say/TguiSay.tsx index 040359b7c3..dd75fbf0e7 100644 --- a/tgui/packages/tgui-say/TguiSay.tsx +++ b/tgui/packages/tgui-say/TguiSay.tsx @@ -1,17 +1,15 @@ -import { Component, createRef, RefObject } from 'react'; +import './styles/main.scss'; + +import { FormEvent, KeyboardEvent, useEffect, useRef, useState } from 'react'; import { dragStartHandler } from 'tgui/drag'; -import { - removeAllSkiplines, - sanitizeMultiline, -} from 'tgui/interfaces/TextInputModal'; import { isEscape, KEY } from 'tgui-core/keys'; import { clamp } from 'tgui-core/math'; -import { BooleanLike } from 'tgui-core/react'; +import { type BooleanLike, classes } from 'tgui-core/react'; import { Channel, ChannelIterator } from './ChannelIterator'; import { ChatHistory } from './ChatHistory'; -import { LINE_LENGTHS, RADIO_PREFIXES, WINDOW_SIZES } from './constants'; -import { windowClose, windowOpen, windowSet } from './helpers'; +import { LineLength, RADIO_PREFIXES, WindowSize } from './constants'; +import { getPrefix, windowClose, windowOpen, windowSet } from './helpers'; import { byondMessages } from './timers'; type ByondOpen = { @@ -21,390 +19,330 @@ type ByondOpen = { type ByondProps = { maxLength: number; minimumHeight: number; + minimumWidth: number; lightMode: BooleanLike; }; -type State = { - buttonContent: string | number; - size: number; -}; - -const CHANNEL_REGEX = /^:\w\s|^,b\s/; - -const ROWS: Record = { - small: 1, - medium: 2, - large: 3, - max: 6, - width: 1, // not used +const ROWS: Record = { + Small: 1, + Medium: 2, + Large: 3, + Max: 20, + Width: 360, + MaxWidth: 800, } as const; -export class TguiSay extends Component<{}, State> { - private channelIterator: ChannelIterator; - private chatHistory: ChatHistory; - private currentPrefix: keyof typeof RADIO_PREFIXES | null; - private innerRef: RefObject; - private lightMode: boolean; - private minimumHeight: number; - private maxLength: number; - private messages: typeof byondMessages; - state: State; +export function TguiSay() { + const innerRef = useRef(null); + const channelIterator = useRef(new ChannelIterator()); + const chatHistory = useRef(new ChatHistory()); + const messages = useRef(byondMessages); - constructor(props: never) { - super(props); + // 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. + const [buttonContent, setButtonContent] = useState(''); + const [currentPrefix, setCurrentPrefix] = useState< + keyof typeof RADIO_PREFIXES | null + >(null); + const [size, setSize] = useState(WindowSize.Small); + const [maxLength, setMaxLength] = useState(1024); + const [minimumHeight, setMinimumHeight] = useState(WindowSize.Small); + const [minimumWidth, setMinimumWidth] = useState(WindowSize.Width); + const [lightMode, setLightMode] = useState(false); + const [value, setValue] = useState(''); - this.channelIterator = new ChannelIterator(); - this.chatHistory = new ChatHistory(); - this.currentPrefix = null; - this.innerRef = createRef(); - this.lightMode = false; - this.minimumHeight = 1; - this.maxLength = 1024; - this.messages = byondMessages; - this.state = { - buttonContent: '', - size: WINDOW_SIZES.small, - }; + function handleArrowKeys(direction: KEY.Up | KEY.Down): void { + const chat = chatHistory.current; + const iterator = channelIterator.current; - this.handleArrowKeys = this.handleArrowKeys.bind(this); - this.handleBackspaceDelete = this.handleBackspaceDelete.bind(this); - this.handleClose = this.handleClose.bind(this); - this.handleEnter = this.handleEnter.bind(this); - this.handleForceSay = this.handleForceSay.bind(this); - this.handleIncrementChannel = this.handleIncrementChannel.bind(this); - this.handleInput = this.handleInput.bind(this); - this.handleKeyDown = this.handleKeyDown.bind(this); - this.handleOpen = this.handleOpen.bind(this); - this.handleProps = this.handleProps.bind(this); - this.reset = this.reset.bind(this); - this.setSize = this.setSize.bind(this); - this.setValue = this.setValue.bind(this); - } - - componentDidMount() { - Byond.subscribeTo('props', this.handleProps); - Byond.subscribeTo('force', this.handleForceSay); - Byond.subscribeTo('open', this.handleOpen); - } - - handleArrowKeys(direction: KEY.PageUp | KEY.PageDown) { - const currentValue = this.innerRef.current?.value; - - if (direction === KEY.PageUp) { - if (this.chatHistory.isAtLatest() && currentValue) { + if (direction === KEY.Up) { + if (chat.isAtLatest() && value) { // Save current message to temp history if at the most recent message - this.chatHistory.saveTemp(currentValue); + chat.saveTemp(value); } // Try to get the previous message, fall back to the current value if none - const prevMessage = this.chatHistory.getOlderMessage(); + const prevMessage = chat.getOlderMessage(); if (prevMessage) { - this.setState({ buttonContent: this.chatHistory.getIndex() }); - this.setSize(prevMessage.length); - this.setValue(prevMessage); + setButtonContent(chat.getIndex().toString()); + setValue(prevMessage); } } else { - const nextMessage = - this.chatHistory.getNewerMessage() || this.chatHistory.getTemp() || ''; + const nextMessage = chat.getNewerMessage() || chat.getTemp() || ''; - const buttonContent = this.chatHistory.isAtLatest() - ? this.channelIterator.current() - : this.chatHistory.getIndex(); + const newContent = chat.isAtLatest() + ? iterator.current() + : chat.getIndex().toString(); - this.setState({ buttonContent }); - this.setSize(nextMessage.length); - this.setValue(nextMessage); + setButtonContent(newContent); + setValue(nextMessage); } } - handleBackspaceDelete() { - const typed = this.innerRef.current?.value; + function handleBackspaceDelete(): void { + const chat = chatHistory.current; + const iterator = channelIterator.current; // User is on a chat history message - if (!this.chatHistory.isAtLatest()) { - this.chatHistory.reset(); - this.setState({ - buttonContent: this.currentPrefix ?? this.channelIterator.current(), - }); + if (!chat.isAtLatest()) { + chat.reset(); + setButtonContent(currentPrefix ?? iterator.current()); + // Empty input, resets the channel - } else if ( - !!this.currentPrefix && - this.channelIterator.isSay() && - typed?.length === 0 - ) { - this.currentPrefix = null; - this.setState({ buttonContent: this.channelIterator.current() }); + } else if (currentPrefix && iterator.isSay() && value?.length === 0) { + setCurrentPrefix(null); + setButtonContent(iterator.current()); } - - this.setSize(typed?.length); } - handleClose() { - const current = this.innerRef.current; - - if (current) { - current.blur(); - } - - this.reset(); - this.chatHistory.reset(); - this.channelIterator.reset(); - this.currentPrefix = null; + function handleClose(): void { + innerRef.current?.blur(); windowClose(); + + setTimeout(() => { + chatHistory.current.reset(); + channelIterator.current.reset(); + unloadChat(); + }, 25); } - handleEnter() { - const prefix = this.currentPrefix ?? ''; - const value = this.innerRef.current?.value; - - if (value?.length && value.length < this.maxLength) { - this.chatHistory.add(value); - - // Everything can be multiline, but only emotes get passed that way to the game - const sanitizedValue = this.channelIterator.isMultiline() - ? sanitizeMultiline(value) - : removeAllSkiplines(value); + function handleEnter(): void { + const iterator = channelIterator.current; + const prefix = currentPrefix ?? ''; + if (value?.length && value.length < maxLength) { + chatHistory.current.add(value); Byond.sendMessage('entry', { - channel: this.channelIterator.current(), - entry: this.channelIterator.isSay() - ? prefix + sanitizedValue - : sanitizedValue, + channel: iterator.current(), + entry: iterator.isSay() ? prefix + value : value, }); } - this.handleClose(); + handleClose(); } - handleForceSay() { - const currentValue = this.innerRef.current?.value; + function handleForceSay(): void { + const iterator = channelIterator.current; + // Only force say if we're on a visible channel and have typed something - if (!currentValue || !this.channelIterator.isVisible()) return; + if (!value || iterator.isVisible()) return; - const prefix = this.currentPrefix ?? ''; - const grunt = this.channelIterator.isSay() - ? prefix + currentValue - : currentValue; + const prefix = currentPrefix ?? ''; + const grunt = iterator.isSay() ? prefix + value : value; - this.messages.forceSayMsg(grunt); - this.reset(); + messages.current.forceSayMsg(grunt, iterator.current()); + unloadChat(); } - handleIncrementChannel() { - this.currentPrefix = null; + function handleIncrementChannel(): void { + const iterator = channelIterator.current; - this.channelIterator.next(); - - // If we've looped onto a quiet channel, tell byond to hide thinking indicators - if (!this.channelIterator.isVisible()) { - this.messages.channelIncrementMsg(false, this.channelIterator.current()); - } else { - this.messages.channelIncrementMsg(true, this.channelIterator.current()); - } - - this.setState({ buttonContent: this.channelIterator.current() }); + iterator.next(); + setButtonContent(iterator.current()); + setCurrentPrefix(null); + messages.current.channelIncrementMsg(iterator.isVisible()); } - handleDecrementChannel() { - this.currentPrefix = null; + function handleInput(event: FormEvent): void { + let newValue = event.currentTarget.value; - this.channelIterator.prev(); + let newPrefix = getPrefix(newValue) || currentPrefix; + // Handles switching prefixes + if (newPrefix && newPrefix !== currentPrefix) { + setButtonContent(RADIO_PREFIXES[newPrefix]); + setCurrentPrefix(newPrefix); + newValue = newValue.slice(3); - // If we've looped onto a quiet channel, tell byond to hide thinking indicators - if (!this.channelIterator.isVisible()) { - this.messages.channelIncrementMsg(false, this.channelIterator.current()); - } else { - this.messages.channelIncrementMsg(true, this.channelIterator.current()); + if (newPrefix === ',b ') { + Byond.sendMessage('thinking', { visible: false }); + } } - this.setState({ buttonContent: this.channelIterator.current() }); + // Handles typing indicators + if (channelIterator.current.isVisible() && newPrefix !== ',b ') { + messages.current.typingMsg(); + } + + setValue(newValue); } - handleInput() { - const typed = this.innerRef.current?.value; - - // If we're typing, send the message - if (this.channelIterator.isVisible()) { - this.messages.typingMsg(this.channelIterator.current()); - } - - this.setSize(typed?.length); - - // Is there a value? Is it long enough to be a prefix? - if (!typed || typed.length < 3) { - return; - } - - if (!CHANNEL_REGEX.test(typed)) { - return; - } - - // Is it a valid prefix? - const prefix = typed - .slice(0, 3) - ?.toLowerCase() as keyof typeof RADIO_PREFIXES; - if (!RADIO_PREFIXES[prefix] || prefix === this.currentPrefix) { - return; - } - - this.channelIterator.set('Say'); - this.currentPrefix = prefix; - this.setState({ buttonContent: RADIO_PREFIXES[prefix] }); - this.setValue(typed.slice(3)); + function getMarkupString( + inputText: string, + markupType: string, + startPosition: number, + endPosition: number, + ) { + return `${inputText.substring(0, startPosition)}${markupType}${inputText.substring(startPosition, endPosition)}${markupType}${inputText.substring(endPosition)}`; } - handleKeyDown(event: React.KeyboardEvent) { - const currentValue = this.innerRef.current?.value; + function handleKeyDown(event: KeyboardEvent): void { + if (event.getModifierState('AltGraph')) return; + switch (event.key) { - case KEY.PageUp: - case KEY.PageDown: - // Allow moving between lines if there are newlines - /* if (currentValue?.includes('\n')) { - break; - } */ + case 'u': // replace with tgui core 1.8.x + if (event.ctrlKey || event.metaKey) { + event.preventDefault(); + const { value, selectionStart, selectionEnd } = event.currentTarget; + event.currentTarget.value = getMarkupString( + value, + '_', + selectionStart, + selectionEnd, + ); + event.currentTarget.selectionEnd = selectionEnd + 2; + } + break; + case 'i': // replace with tgui core 1.8.x + if (event.ctrlKey || event.metaKey) { + event.preventDefault(); + const { value, selectionStart, selectionEnd } = event.currentTarget; + event.currentTarget.value = getMarkupString( + value, + '|', + selectionStart, + selectionEnd, + ); + event.currentTarget.selectionEnd = selectionEnd + 2; + } + break; + case 'b': // replace with tgui core 1.8.x + if (event.ctrlKey || event.metaKey) { + event.preventDefault(); + const { value, selectionStart, selectionEnd } = event.currentTarget; + event.currentTarget.value = getMarkupString( + value, + '+', + selectionStart, + selectionEnd, + ); + event.currentTarget.selectionEnd = selectionEnd + 2; + } + break; + case KEY.Up: + case KEY.Down: event.preventDefault(); - this.handleArrowKeys(event.key); + handleArrowKeys(event.key); break; case KEY.Delete: case KEY.Backspace: - this.handleBackspaceDelete(); + handleBackspaceDelete(); break; case KEY.Enter: - // Allow inputting newlines - if (event.shiftKey) { - break; - } event.preventDefault(); - this.handleEnter(); + handleEnter(); break; case KEY.Tab: event.preventDefault(); - if (event.shiftKey) { - this.handleDecrementChannel(); - } else { - this.handleIncrementChannel(); - } + handleIncrementChannel(); break; default: if (isEscape(event.key)) { - this.handleClose(); + handleClose(); } } } - handleOpen = (data: ByondOpen) => { + function handleOpen(data: ByondOpen): void { setTimeout(() => { - this.innerRef.current?.focus(); - }, 0); + innerRef.current?.focus(); + windowSet(WindowSize.Width, WindowSize.Small); + setSize(WindowSize.Width); + }, 1); const { channel } = data; + const iterator = channelIterator.current; // Catches the case where the modal is already open - if (this.channelIterator.isSay()) { - this.channelIterator.set(channel); + if (iterator.isSay()) { + iterator.set(channel); } - this.setState({ buttonContent: this.channelIterator.current() }); - windowOpen(this.channelIterator.current()); - }; - - handleProps = (data: ByondProps) => { - const { maxLength, minimumHeight, lightMode } = data; - this.maxLength = maxLength; - this.minimumHeight = minimumHeight; - this.setSize(); - this.lightMode = !!lightMode; - }; - - reset() { - this.setValue(''); - this.setSize(); - this.setState({ - buttonContent: this.channelIterator.current(), - }); + setButtonContent(iterator.current()); + windowOpen(iterator.current()); } - setSize(length = 0) { - let newSize: number; - - const currentValue = this.innerRef.current?.value; - - if (currentValue?.includes('\n')) { - newSize = WINDOW_SIZES.large; - } else if (length > LINE_LENGTHS.medium) { - newSize = WINDOW_SIZES.large; - } else if (length <= LINE_LENGTHS.medium && length > LINE_LENGTHS.small) { - newSize = WINDOW_SIZES.medium; - } else { - newSize = WINDOW_SIZES.small; - } - - newSize = clamp(newSize, this.minimumHeight * 20 + 10, WINDOW_SIZES.max); - console.log(newSize); - - if (this.state.size !== newSize) { - this.setState({ size: newSize }); - windowSet(newSize); - } - } - - setValue(value: string) { - const textArea = this.innerRef.current; - if (textArea) { - textArea.value = value; - } - } - - render() { - const theme = - (this.lightMode && 'lightMode') || - (this.currentPrefix && RADIO_PREFIXES[this.currentPrefix]) || - this.channelIterator.current(); - - return ( -
- -
- -
- -