mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13.git
synced 2026-07-21 23:02:29 +01:00
lol
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Render a HTML string with the given header and body.
|
||||
*/
|
||||
#define HTML_SKELETON_HEADER_BODY(head, body) \
|
||||
"<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><meta http-equiv='X-UA-Compatible' content='IE=edge'>[head]</head><body>[body]</body></html>"
|
||||
|
||||
/**
|
||||
* Render a HTML string with the given title and body, without additional headers.
|
||||
*/
|
||||
#define HTML_SKELETON_TITLE(title, body) HTML_SKELETON_HEADER_BODY("<title>[title]</title>", body)
|
||||
|
||||
/**
|
||||
* Render a HTML string with the given body, without a title or additional headers.
|
||||
*/
|
||||
#define HTML_SKELETON(body) HTML_SKELETON_HEADER_BODY("", body)
|
||||
@@ -137,7 +137,7 @@ SUBSYSTEM_DEF(pai)
|
||||
dat += "<a href='byond://?src=[REF(src)];option=save;new=1;candidate=[REF(candidate)]'>Save Personality</a><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];option=load;new=1;candidate=[REF(candidate)]'>Load Personality</a><br>"
|
||||
|
||||
M << browse(dat, "window=paiRecruit")
|
||||
M << HTML_SKELETON(dat), "window=paiRecruit")
|
||||
|
||||
/datum/controller/subsystem/pai/proc/spam_again()
|
||||
ghost_spam = FALSE
|
||||
@@ -198,7 +198,7 @@ SUBSYSTEM_DEF(pai)
|
||||
|
||||
dat += "</table>"
|
||||
|
||||
user << browse(dat, "window=findPai")
|
||||
user << HTML_SKELETON(dat), "window=findPai")
|
||||
|
||||
/datum/paiCandidate
|
||||
var/name
|
||||
|
||||
@@ -39,7 +39,7 @@ SUBSYSTEM_DEF(tgui)
|
||||
ntos_error = "<style type='text/css'>\n[ntos_error]\n</style>"
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:ntos-error -->", ntos_error)
|
||||
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "Nanotrasen (c) 2525-[GLOB.startup_year + 544]")
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "Nanotrasen (c) 2525-[text2num(time2text(world.realtime, "YYYY")) + 544]")
|
||||
|
||||
/datum/controller/subsystem/tgui/Shutdown()
|
||||
close_all_uis()
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
|
||||
/// Deactivates the functionality defines by the element on the given datum
|
||||
/datum/element/proc/Detach(datum/source, force)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
UnregisterSignal(source, COMSIG_PARENT_QDELETING)
|
||||
|
||||
/datum/element/Destroy(force)
|
||||
|
||||
@@ -155,7 +155,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
var/shown_threat
|
||||
|
||||
/datum/game_mode/dynamic/admin_panel()
|
||||
var/list/dat = list("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Game Mode Panel</title></head><body><h1><B>Game Mode Panel</B></h1>")
|
||||
var/list/dat = list("<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Game Mode Panel</title></head><body><h1><B>Game Mode Panel</B></h1>")
|
||||
dat += "Dynamic Mode <a href='?_src_=vars;[HrefToken()];Vars=[REF(src)]'>\[VV\]</a> <a href='?src=\ref[src];[HrefToken()]'>\[Refresh\]</a><BR>"
|
||||
dat += "Threat Level: <b>[threat_level]</b><br/>"
|
||||
dat += "Budgets (Roundstart/Midrounds): <b>[initial_round_start_budget]/[threat_level - initial_round_start_budget]</b><br/>"
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
dat += "</td></tr>"
|
||||
|
||||
dat += "</table><hr><a href='?src=[REF(src)];done'>Finalize Airlock Construction</a> | <a href='?src=[REF(src)];cancel'>Cancel and Destroy Airlock</a>"
|
||||
usr << browse(dat,"window=airlockmaker")
|
||||
usr << HTML_SKELETON(dat),"window=airlockmaker")
|
||||
|
||||
/datum/airlock_maker/Topic(var/href,var/list/href_list)
|
||||
if(!usr)
|
||||
|
||||
@@ -259,7 +259,7 @@
|
||||
dat += "Moving: <a href='?src=[REF(src)];operation=togglemoving'>[moving ? "Enabled":"Disabled"]</a>"
|
||||
|
||||
|
||||
user << browse(dat, "window=magnet;size=400x500")
|
||||
user << HTML_SKELETON(dat), "window=magnet;size=400x500")
|
||||
onclose(user, "magnet")
|
||||
|
||||
/obj/machinery/magnetic_controller/Topic(href, href_list)
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
dat += "No personality installed.<br>"
|
||||
dat += "Searching for a personality... Press view available personalities to notify potential candidates."
|
||||
dat += "<A href='byond://?src=[REF(src)];request=1'>\[View available personalities\]</a><br>"
|
||||
user << browse(dat, "window=paicard")
|
||||
user << HTML_SKELETON(dat), "window=paicard")
|
||||
onclose(user, "paicard")
|
||||
return
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
dat += "The implant casing is empty."
|
||||
else
|
||||
dat += "Please insert an implant casing!"
|
||||
user << browse(dat, "window=implantpad")
|
||||
user << HTML_SKELETON(dat), "window=implantpad")
|
||||
onclose(user, "implantpad")
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
dat += "<B>Four uses, use them wisely:</B><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];spell_teleport=1'>Teleport</A><BR>"
|
||||
dat += "Kind regards,<br>Wizards Federation<br><br>P.S. Don't forget to bring your gear, you'll need it to cast most spells.<HR>"
|
||||
user << browse(dat, "window=scroll")
|
||||
user << HTML_SKELETON(dat), "window=scroll")
|
||||
onclose(user, "scroll")
|
||||
return
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
if (!locked)
|
||||
message = "*****"
|
||||
dat += text("<HR>\n>[]<BR>\n<A href='?src=[REF(src)];type=1'>1</A>-<A href='?src=[REF(src)];type=2'>2</A>-<A href='?src=[REF(src)];type=3'>3</A><BR>\n<A href='?src=[REF(src)];type=4'>4</A>-<A href='?src=[REF(src)];type=5'>5</A>-<A href='?src=[REF(src)];type=6'>6</A><BR>\n<A href='?src=[REF(src)];type=7'>7</A>-<A href='?src=[REF(src)];type=8'>8</A>-<A href='?src=[REF(src)];type=9'>9</A><BR>\n<A href='?src=[REF(src)];type=R'>R</A>-<A href='?src=[REF(src)];type=0'>0</A>-<A href='?src=[REF(src)];type=E'>E</A><BR>\n</TT>", message)
|
||||
user << browse(dat, "window=caselock;size=300x280")
|
||||
user << HTML_SKELETON(dat), "window=caselock;size=300x280")
|
||||
|
||||
/obj/item/storage/secure/Topic(href, href_list)
|
||||
..()
|
||||
|
||||
@@ -63,7 +63,7 @@ GLOBAL_PROTECT(Banlist)
|
||||
/proc/LoadBans()
|
||||
if(!CONFIG_GET(flag/ban_legacy_system))
|
||||
return
|
||||
|
||||
|
||||
GLOB.Banlist = new("data/banlist.bdb")
|
||||
log_admin("Loading Banlist")
|
||||
|
||||
@@ -199,7 +199,7 @@ GLOBAL_PROTECT(Banlist)
|
||||
|
||||
dat += "</table>"
|
||||
dat = "<HR><B>Bans:</B> <FONT COLOR=blue>(U) = Unban , (E) = Edit Ban</FONT> - <FONT COLOR=green>([count] Bans)</FONT><HR><table border=1 rules=all frame=void cellspacing=0 cellpadding=3 >[dat]"
|
||||
usr << browse(dat, "window=unbanp;size=875x400")
|
||||
usr << HTML_SKELETON(dat), "window=unbanp;size=875x400")
|
||||
|
||||
//////////////////////////////////// DEBUG ////////////////////////////////////
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
to_chat(usr, "Error: you are not an admin!", confidential = TRUE)
|
||||
return
|
||||
var/dat
|
||||
dat = text("<HEAD><TITLE>Admin Newscaster</TITLE></HEAD><H3>Admin Newscaster Unit</H3>")
|
||||
dat = text("<!DOCTYPE html><html><HEAD><TITLE>Admin Newscaster</TITLE></HEAD><body><H3>Admin Newscaster Unit</H3>")
|
||||
|
||||
switch(admincaster_screen)
|
||||
if(0)
|
||||
@@ -424,6 +424,7 @@
|
||||
else
|
||||
dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com"
|
||||
|
||||
dat += "</body></html>"
|
||||
usr << browse(dat, "window=admincaster_main;size=400x600")
|
||||
onclose(usr, "admincaster_main")
|
||||
|
||||
@@ -460,7 +461,7 @@
|
||||
if(marked_datum && istype(marked_datum, /atom))
|
||||
dat += "<A href='?src=[REF(src)];[HrefToken()];dupe_marked_datum=1'>Duplicate Marked Datum</A><br>"
|
||||
|
||||
usr << browse(dat, "window=admin2;size=240x280")
|
||||
usr << HTML_SKELETON(dat), "window=admin2;size=240x280")
|
||||
return
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge
|
||||
@@ -934,7 +935,7 @@
|
||||
<br/>The threshold at which "round-ender" rulesets will stack. A value higher than 100 ensure this never happens. <br/>
|
||||
"}
|
||||
|
||||
user << browse(dat, "window=dyn_mode_options;size=900x650")
|
||||
user << HTML_SKELETON(dat), "window=dyn_mode_options;size=900x650")
|
||||
|
||||
/datum/admins/proc/create_or_modify_area()
|
||||
set category = "Debug"
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
if(!SSticker.HasRoundStarted())
|
||||
alert("The game hasn't started yet!")
|
||||
return
|
||||
var/list/dat = list("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Round Status</title></head><body><h1><B>Round Status</B></h1>")
|
||||
var/list/dat = list("<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Round Status</title></head><body><h1><B>Round Status</B></h1>")
|
||||
if(SSticker.mode.replacementmode)
|
||||
dat += "Former Game Mode: <B>[SSticker.mode.name]</B><BR>"
|
||||
dat += "Replacement Game Mode: <B>[SSticker.mode.replacementmode.name]</B><BR>"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
if(!check_rights())
|
||||
return
|
||||
log_admin("[key_name(usr)] checked the player panel.")
|
||||
var/dat = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Player Panel</title></head>"
|
||||
var/dat = "<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Player Panel</title></head>"
|
||||
|
||||
//javascript, the part that does most of the work~
|
||||
dat += {"
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
dat += "No datums tagged :("
|
||||
|
||||
dat = dat.Join("<br>")
|
||||
usr << browse(dat, "window=tag;size=800x480")
|
||||
usr << HTML_SKELETON(dat), "window=tag;size=800x480")
|
||||
|
||||
/**
|
||||
* Clears tagged datums
|
||||
|
||||
@@ -612,7 +612,7 @@
|
||||
to_chat(usr, "This mob has no ckey.")
|
||||
return
|
||||
|
||||
var/dat = "<head><title>Job-Ban Panel: [key_name(M)]</title></head>"
|
||||
var/dat = "<!DOCTYPE html><html><head><title>Job-Ban Panel: [key_name(M)]</title></head><body>"
|
||||
|
||||
/***********************************WARNING!************************************
|
||||
The jobban stuff looks mangled and disgusting
|
||||
@@ -917,7 +917,7 @@
|
||||
else
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=[ROLE_RESPAWN];jobban4=[REF(M)]'>Respawns</a></td>"
|
||||
|
||||
dat += "</tr></table>"
|
||||
dat += "</tr></table></body></html>"
|
||||
usr << browse(dat, "window=jobban2;size=800x450")
|
||||
return
|
||||
|
||||
@@ -2616,7 +2616,7 @@
|
||||
var/list/dat = list("Related accounts by [uppertext(href_list["showrelatedacc"])]:")
|
||||
dat += thing_to_check
|
||||
|
||||
usr << browse(dat.Join("<br>"), "window=related_[C];size=420x300")
|
||||
usr << HTML_SKELETON(dat).Join("<br>"), "window=related_[C];size=420x300")
|
||||
|
||||
else if(href_list["centcomlookup"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
@@ -2742,7 +2742,7 @@
|
||||
dat += {"<A href='?src=[REF(src)];[HrefToken()];c_mode2=secret'>Secret</A><br>"}
|
||||
dat += {"<A href='?src=[REF(src)];[HrefToken()];c_mode2=random'>Random</A><br>"}
|
||||
dat += {"Now: [GLOB.master_mode]"}
|
||||
usr << browse(dat, "window=c_mode")
|
||||
usr << HTML_SKELETON(dat), "window=c_mode")
|
||||
|
||||
/datum/admins/proc/HandleFSecret()
|
||||
if(!check_rights(R_ADMIN))
|
||||
@@ -2757,7 +2757,7 @@
|
||||
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_secret2=[mode]'>[config.mode_names[mode]]</A><br>"}
|
||||
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_secret2=secret'>Random (default)</A><br>"}
|
||||
dat += {"Now: [GLOB.secret_force_mode]"}
|
||||
usr << browse(dat, "window=f_secret")
|
||||
usr << HTML_SKELETON(dat), "window=f_secret")
|
||||
|
||||
/datum/admins/proc/makeMentor(ckey)
|
||||
if(!usr.client)
|
||||
|
||||
@@ -79,7 +79,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
title = "Resolved Tickets"
|
||||
if(!l2b)
|
||||
return
|
||||
var/list/dat = list("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>[title]</title></head>")
|
||||
var/list/dat = list("<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>[title]</title></head>")
|
||||
dat += "<A href='?_src_=holder;[HrefToken()];ahelp_tickets=[state]'>Refresh</A><br><br>"
|
||||
for(var/I in l2b)
|
||||
var/datum/admin_help/AH = I
|
||||
@@ -413,7 +413,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
|
||||
|
||||
//Show the ticket panel
|
||||
/datum/admin_help/proc/TicketPanel()
|
||||
var/list/dat = list("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Ticket #[id]</title></head>")
|
||||
var/list/dat = list("<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Ticket #[id]</title></head>")
|
||||
var/ref_src = "[REF(src)]"
|
||||
dat += "<h4>Admin Help Ticket #[id]: [LinkedReplyName(ref_src)]</h4>"
|
||||
dat += "<b>State: "
|
||||
|
||||
@@ -176,7 +176,7 @@ GLOBAL_LIST_EMPTY(dirty_vars)
|
||||
dat += "[ADMIN_VERBOSEJMP(T)]\n"
|
||||
dat += "<br>"
|
||||
|
||||
usr << browse(dat, "window=at_list")
|
||||
usr << HTML_SKELETON(dat), "window=at_list")
|
||||
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turfs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
|
||||
@@ -1072,7 +1072,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
<input type="submit" value="Save">
|
||||
</form></body></html>
|
||||
"}
|
||||
usr << browse(dat, "window=dressup;size=550x600")
|
||||
usr << HTML_SKELETON(dat), "window=dressup;size=550x600")
|
||||
|
||||
/client/proc/toggle_combo_hud()
|
||||
set category = "Admin.Game"
|
||||
@@ -1245,7 +1245,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
for(var/datum/station_goal/S in SSticker.mode.station_goals)
|
||||
dat += "[S.name] - <a href='?src=[REF(S)];[HrefToken()];announce=1'>Announce</a> | <a href='?src=[REF(S)];[HrefToken()];remove=1'>Remove</a><br>"
|
||||
dat += "<br><a href='?src=[REF(src)];[HrefToken()];add_station_goal=1'>Add New Goal</a>"
|
||||
usr << browse(dat, "window=goals;size=400x400")
|
||||
usr << HTML_SKELETON(dat), "window=goals;size=400x400")
|
||||
|
||||
|
||||
/client/proc/toggle_hub()
|
||||
|
||||
@@ -58,14 +58,14 @@
|
||||
dat += "<li>[l]</li>"
|
||||
if(!GLOB.admin_log.len)
|
||||
dat += "No-one has done anything this round!"
|
||||
holder << browse(dat, "window=admin_log")
|
||||
holder << HTML_SKELETON(dat), "window=admin_log")
|
||||
if("show_admins")
|
||||
var/dat = "<B>Current admins:</B><HR>"
|
||||
if(GLOB.admin_datums)
|
||||
for(var/ckey in GLOB.admin_datums)
|
||||
var/datum/admins/D = GLOB.admin_datums[ckey]
|
||||
dat += "[ckey] - [D.rank.name]<br>"
|
||||
holder << browse(dat, "window=showadmins;size=600x500")
|
||||
holder << HTML_SKELETON(dat), "window=showadmins;size=600x500")
|
||||
if("mentor_log")
|
||||
var/dat = "<B>Mentor Log<HR></B>"
|
||||
for(var/l in GLOB.mentorlog)
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
if(!GLOB.mentorlog.len)
|
||||
dat += "No mentors have done anything this round!"
|
||||
usr << browse(dat, "window=mentor_log")
|
||||
usr << HTML_SKELETON(dat), "window=mentor_log")
|
||||
|
||||
//Buttons for debug.
|
||||
if("maint_access_engiebrig")
|
||||
@@ -114,18 +114,18 @@
|
||||
var/dat = "<B>Bombing List</B><HR>"
|
||||
for(var/l in GLOB.bombers)
|
||||
dat += text("[l]<BR>")
|
||||
holder << browse(dat, "window=bombers")
|
||||
holder << HTML_SKELETON(dat), "window=bombers")
|
||||
|
||||
if("list_signalers")
|
||||
var/dat = "<B>Showing last [length(GLOB.lastsignalers)] signalers.</B><HR>"
|
||||
for(var/sig in GLOB.lastsignalers)
|
||||
dat += "[sig]<BR>"
|
||||
holder << browse(dat, "window=lastsignalers;size=800x500")
|
||||
holder << HTML_SKELETON(dat), "window=lastsignalers;size=800x500")
|
||||
if("list_lawchanges")
|
||||
var/dat = "<B>Showing last [length(GLOB.lawchanges)] law changes.</B><HR>"
|
||||
for(var/sig in GLOB.lawchanges)
|
||||
dat += "[sig]<BR>"
|
||||
holder << browse(dat, "window=lawchanges;size=800x500")
|
||||
holder << HTML_SKELETON(dat), "window=lawchanges;size=800x500")
|
||||
if("showailaws")
|
||||
holder.holder.output_ai_laws()//huh, inconvenient var naming, huh?
|
||||
if("showgm")
|
||||
@@ -141,7 +141,7 @@
|
||||
for(var/datum/data/record/t in GLOB.data_core.general)
|
||||
dat += "<tr><td>[t.fields["name"]]</td><td>[t.fields["rank"]]</td></tr>"
|
||||
dat += "</table>"
|
||||
holder << browse(dat, "window=manifest;size=440x410")
|
||||
holder << HTML_SKELETON(dat), "window=manifest;size=440x410")
|
||||
if("dna")
|
||||
var/dat = "<B>Showing DNA from blood.</B><HR>"
|
||||
dat += "<table cellspacing=5><tr><th>Name</th><th>DNA</th><th>Blood Type</th></tr>"
|
||||
@@ -150,7 +150,7 @@
|
||||
if(H.ckey)
|
||||
dat += "<tr><td>[H]</td><td>[H.dna.unique_enzymes]</td><td>[H.dna.blood_type]</td></tr>"
|
||||
dat += "</table>"
|
||||
holder << browse(dat, "window=DNA;size=440x410")
|
||||
holder << HTML_SKELETON(dat), "window=DNA;size=440x410")
|
||||
if("fingerprints")
|
||||
var/dat = "<B>Showing Fingerprints.</B><HR>"
|
||||
dat += "<table cellspacing=5><tr><th>Name</th><th>Fingerprints</th></tr>"
|
||||
@@ -159,7 +159,7 @@
|
||||
if(H.ckey)
|
||||
dat += "<tr><td>[H]</td><td>[md5(H.dna.uni_identity)]</td></tr>"
|
||||
dat += "</table>"
|
||||
holder << browse(dat, "window=fingerprints;size=440x410")
|
||||
holder << HTML_SKELETON(dat), "window=fingerprints;size=440x410")
|
||||
if("ctfbutton")
|
||||
toggle_all_ctf(holder)
|
||||
if("tdomereset")
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
dat += "<I>Your apprentice is training to cast spells without their robes. They know Knock and Mindswap.</I><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];school=[APPRENTICE_MARTIAL]'>Martial Artist</a><BR>"
|
||||
dat += "<I>Your apprentice is training in ancient martial arts. They know an Inner Mantra and the Nuclear Fist technique.</I><BR>"
|
||||
user << browse(dat, "window=radio")
|
||||
user << HTML_SKELETON(dat), "window=radio")
|
||||
onclose(user, "radio")
|
||||
return
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
dat += "<B>Charge progress: [reload]/[reload_cooldown]:</B><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];fire=1'>Open Fire</A><BR>"
|
||||
dat += "Deployment of weapon authorized by <br>Nanotrasen Naval Command<br><br>Remember, friendly fire is grounds for termination of your contract and life.<HR>"
|
||||
user << browse(dat, "window=scroll")
|
||||
user << HTML_SKELETON(dat), "window=scroll")
|
||||
onclose(user, "scroll")
|
||||
|
||||
/obj/machinery/artillerycontrol/Topic(href, href_list)
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
dat+="<BR><I>There is a small scribble near the end of this page... It reads: \"[scribble]\"</I>"
|
||||
dat+= "<HR><DIV STYLE='float:left;'><A href='?src=[REF(src)];prev_page=1'>Previous Page</A></DIV>"
|
||||
dat+="<BR><HR><div align='center'>[curr_page+1]</div>"
|
||||
human_user << browse(dat, "window=newspaper_main;size=300x400")
|
||||
human_user << HTML_SKELETON(dat), "window=newspaper_main;size=300x400")
|
||||
onclose(human_user, "newspaper_main")
|
||||
else
|
||||
to_chat(user, "The paper is full of unintelligible symbols!")
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
for(var/obj/item/I in src)
|
||||
dat += "<A href='?src=[REF(src)];remove=[REF(I)]'>Remove</A> - <A href='?src=[REF(src)];read=[REF(I)]'>[I.name]</A><BR>"
|
||||
user << browse(dat, "window=folder")
|
||||
user << HTML_SKELETON(dat), "window=folder")
|
||||
onclose(user, "folder")
|
||||
add_fingerprint(usr)
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@
|
||||
dat += "- <A href='?src=[REF(src)];strengthdown=1'>--</A>|<A href='?src=[REF(src)];strengthup=1'>++</A><BR><BR>"
|
||||
|
||||
|
||||
user << browse(dat, "window=AMcontrol;size=420x500")
|
||||
user << HTML_SKELETON(dat), "window=AMcontrol;size=420x500")
|
||||
onclose(user, "AMcontrol")
|
||||
return
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@
|
||||
|
||||
dat += "</tr></table><br>Current Selection: [currTag ? GLOB.TAGGERLOCATIONS[currTag] : "None"]</tt>"
|
||||
|
||||
user << browse(dat, "window=destTagScreen;size=450x350")
|
||||
user << HTML_SKELETON(dat), "window=destTagScreen;size=450x350")
|
||||
onclose(user, "destTagScreen")
|
||||
|
||||
/obj/item/dest_tagger/attack_self(mob/user)
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
if(!window.is_ready())
|
||||
window.initialize(
|
||||
fancy = user.client.prefs.tgui_fancy,
|
||||
inline_assets = list(
|
||||
assets = list(
|
||||
get_asset_datum(/datum/asset/simple/tgui),
|
||||
))
|
||||
else
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
/**
|
||||
* sent asset packs (citrp asset backend)
|
||||
*/
|
||||
var/list/datum/asset_pack/sent_assets = list()
|
||||
var/list/datum/asset/sent_assets = list()
|
||||
// Vars passed to initialize proc (and saved for later)
|
||||
var/initial_strict_mode
|
||||
var/initial_fancy
|
||||
@@ -106,8 +106,8 @@
|
||||
html = replacetextEx(html, "\[tgui:strictMode]", strict_mode)
|
||||
// Inject assets
|
||||
var/inline_assets_str = ""
|
||||
for(var/datum/asset_pack/asset as anything in assets)
|
||||
asset = SSassets.ready_asset_pack(asset)
|
||||
for(var/datum/asset/asset as anything in assets)
|
||||
asset.send(client)
|
||||
var/mappings = asset.get_url_mappings()
|
||||
for(var/name in mappings)
|
||||
var/url = mappings[name]
|
||||
@@ -116,11 +116,10 @@
|
||||
inline_assets_str += "Byond.loadCss('[url]', true);\n"
|
||||
else if(copytext(name, -3) == ".js")
|
||||
inline_assets_str += "Byond.loadJs('[url]', true);\n"
|
||||
SSassets.send_asset_pack(client, asset)
|
||||
// incase they logged out
|
||||
if(!client)
|
||||
return
|
||||
client.asset_cache_flush_browse_queue() // flush their assets
|
||||
client.browse_queue_flush()
|
||||
// incase they logged out
|
||||
if(!client)
|
||||
return
|
||||
@@ -321,7 +320,6 @@
|
||||
/datum/tgui_window/proc/send_asset(datum/asset/asset)
|
||||
asset.send(client)
|
||||
sent_assets |= asset
|
||||
. = SSassets.send_asset_pack(client, asset)
|
||||
if(istype(asset, /datum/asset/spritesheet))
|
||||
var/datum/asset/spritesheet/spritesheet = asset
|
||||
send_message("asset/stylesheet", spritesheet.css_filename())
|
||||
@@ -399,12 +397,13 @@
|
||||
client << link(href_list["url"])
|
||||
if("cacheReloaded")
|
||||
reinitialize()
|
||||
if("chat/resend")
|
||||
SSchat.handle_resend(client, payload)
|
||||
// if("chat/resend")
|
||||
// SSchat.handle_resend(client, payload)
|
||||
if("oversizedPayloadRequest")
|
||||
var/payload_id = payload["id"]
|
||||
var/chunk_count = payload["chunkCount"]
|
||||
var/permit_payload = chunk_count <= CONFIG_GET(number/tgui_max_chunk_count)
|
||||
// var/permit_payload = chunk_count <= CONFIG_GET(number/tgui_max_chunk_count)
|
||||
var/permit_payload = chunk_count <= 10
|
||||
if(permit_payload)
|
||||
create_oversized_payload(payload_id, payload["type"], chunk_count)
|
||||
send_message("oversizePayloadResponse", list("allow" = permit_payload, "id" = payload_id))
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
sleep(1)
|
||||
initialized_at = world.time
|
||||
// Perform a clean initialization
|
||||
window.initialize(inline_assets = list(
|
||||
window.initialize(assets = list(
|
||||
get_asset_datum(/datum/asset/simple/tgui_panel),
|
||||
))
|
||||
window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome))
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
|
||||
if(!GLOB.mentorlog.len)
|
||||
dat += "No mentors have done anything this round!"
|
||||
usr << browse(dat, "window=mentor_log")
|
||||
usr << browse(HTML_SKELETON(dat), "window=mentor_log")
|
||||
|
||||
Vendored
+197
-142
@@ -4,147 +4,202 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
declare global {
|
||||
// Webpack asset modules.
|
||||
// Should match extensions used in webpack config.
|
||||
declare module '*.png' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
type ByondType = {
|
||||
/**
|
||||
* True if javascript is running in BYOND.
|
||||
*/
|
||||
IS_BYOND: boolean;
|
||||
|
||||
/**
|
||||
* Makes a BYOND call.
|
||||
*
|
||||
* If path is empty, this will trigger a Topic call.
|
||||
* You can reference a specific object by setting the "src" parameter.
|
||||
*
|
||||
* See: https://secure.byond.com/docs/ref/skinparams.html
|
||||
*/
|
||||
call(path: string, params: object): void;
|
||||
|
||||
/**
|
||||
* Makes an asynchronous BYOND call. Returns a promise.
|
||||
*/
|
||||
callAsync(path: string, params: object): Promise<any>;
|
||||
|
||||
/**
|
||||
* Makes a Topic call.
|
||||
*
|
||||
* You can reference a specific object by setting the "src" parameter.
|
||||
*/
|
||||
topic(params: object): void;
|
||||
|
||||
/**
|
||||
* Runs a command or a verb.
|
||||
*/
|
||||
command(command: string): void;
|
||||
|
||||
/**
|
||||
* Retrieves all properties of the BYOND skin element.
|
||||
*
|
||||
* Returns a promise with a key-value object containing all properties.
|
||||
*/
|
||||
winget(id: string): Promise<object>;
|
||||
|
||||
/**
|
||||
* Retrieves all properties of the BYOND skin element.
|
||||
*
|
||||
* Returns a promise with a key-value object containing all properties.
|
||||
*/
|
||||
winget(id: string, propName: '*'): Promise<object>;
|
||||
|
||||
/**
|
||||
* Retrieves an exactly one property of the BYOND skin element,
|
||||
* as defined in `propName`.
|
||||
*
|
||||
* Returns a promise with the value of that property.
|
||||
*/
|
||||
winget(id: string, propName: string): Promise<any>;
|
||||
|
||||
/**
|
||||
* Retrieves multiple properties of the BYOND skin element,
|
||||
* as defined in the `propNames` array.
|
||||
*
|
||||
* Returns a promise with a key-value object containing listed properties.
|
||||
*/
|
||||
winget(id: string, propNames: string[]): Promise<object>;
|
||||
|
||||
/**
|
||||
* Assigns properties to BYOND skin elements.
|
||||
*/
|
||||
winset(props: object): void;
|
||||
|
||||
/**
|
||||
* Assigns properties to the BYOND skin element.
|
||||
*/
|
||||
winset(id: string, props: object): void;
|
||||
|
||||
/**
|
||||
* Sets a property on the BYOND skin element to a certain value.
|
||||
*/
|
||||
winset(id: string, propName: string, propValue: any): void;
|
||||
|
||||
/**
|
||||
* Parses BYOND JSON.
|
||||
*
|
||||
* Uses a special encoding to preverse Infinity and NaN.
|
||||
*/
|
||||
parseJson(text: string): any;
|
||||
|
||||
/**
|
||||
* Loads a stylesheet into the document.
|
||||
*/
|
||||
loadCss(url: string): void;
|
||||
|
||||
/**
|
||||
* Loads a script into the document.
|
||||
*/
|
||||
loadJs(url: string): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Object that provides access to Byond Skin API and is available in
|
||||
* any tgui application.
|
||||
*/
|
||||
const Byond: ByondType;
|
||||
|
||||
interface Window {
|
||||
/**
|
||||
* ID of the Byond window this script is running on.
|
||||
* Should be used as a parameter to winget/winset.
|
||||
*/
|
||||
__windowId__: string;
|
||||
Byond: ByondType;
|
||||
|
||||
__store__: Store<unknown, AnyAction>;
|
||||
__augmentStack__: (store: Store) => StackAugmentor;
|
||||
|
||||
// IE IndexedDB stuff.
|
||||
msIndexedDB: IDBFactory;
|
||||
msIDBTransaction: IDBTransaction;
|
||||
|
||||
// 516 byondstorage API.
|
||||
hubStorage: Storage;
|
||||
domainStorage: Storage;
|
||||
serverStorage: Storage;
|
||||
}
|
||||
|
||||
// Webpack asset modules.
|
||||
// Should match extensions used in webpack config.
|
||||
declare module '*.png' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
export {};
|
||||
declare module '*.jpg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
marquee: any;
|
||||
blink: any;
|
||||
}
|
||||
}
|
||||
|
||||
type TguiMessage = {
|
||||
type: string;
|
||||
payload?: any;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
type ByondType = {
|
||||
/**
|
||||
* ID of the Byond window this script is running on.
|
||||
* Can be used as a parameter to winget/winset.
|
||||
*/
|
||||
windowId: string;
|
||||
|
||||
/**
|
||||
* True if javascript is running in BYOND.
|
||||
*/
|
||||
IS_BYOND: boolean;
|
||||
|
||||
/**
|
||||
* Version of Blink engine of WebView2. Null if N/A.
|
||||
*/
|
||||
BLINK: number | null;
|
||||
|
||||
/**
|
||||
* If `true`, unhandled errors and common mistakes result in a blue screen
|
||||
* of death, which stops this window from handling incoming messages and
|
||||
* closes the active instance of tgui datum if there was one.
|
||||
*
|
||||
* It can be defined in window.initialize() in DM, or changed in runtime
|
||||
* here via this property to `true` or `false`.
|
||||
*
|
||||
* It is recommended that you keep this ON to detect hard to find bugs.
|
||||
*/
|
||||
strictMode: boolean;
|
||||
|
||||
/**
|
||||
* Makes a BYOND call.
|
||||
*
|
||||
* If path is empty, this will trigger a Topic call.
|
||||
* You can reference a specific object by setting the "src" parameter.
|
||||
*
|
||||
* See: https://secure.byond.com/docs/ref/skinparams.html
|
||||
*/
|
||||
call(path: string, params: object): void;
|
||||
|
||||
/**
|
||||
* Makes an asynchronous BYOND call. Returns a promise.
|
||||
*/
|
||||
callAsync(path: string, params: object): Promise<any>;
|
||||
|
||||
/**
|
||||
* Makes a Topic call.
|
||||
*
|
||||
* You can reference a specific object by setting the "src" parameter.
|
||||
*/
|
||||
topic(params: object): void;
|
||||
|
||||
/**
|
||||
* Runs a command or a verb.
|
||||
*/
|
||||
command(command: string): void;
|
||||
|
||||
/**
|
||||
* Retrieves all properties of the BYOND skin element.
|
||||
*
|
||||
* Returns a promise with a key-value object containing all properties.
|
||||
*/
|
||||
winget(id: string | null): Promise<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Retrieves all properties of the BYOND skin element.
|
||||
*
|
||||
* Returns a promise with a key-value object containing all properties.
|
||||
*/
|
||||
winget(id: string | null, propName: '*'): Promise<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Retrieves an exactly one property of the BYOND skin element,
|
||||
* as defined in `propName`.
|
||||
*
|
||||
* Returns a promise with the value of that property.
|
||||
*/
|
||||
winget(id: string | null, propName: string): Promise<any>;
|
||||
|
||||
/**
|
||||
* Retrieves multiple properties of the BYOND skin element,
|
||||
* as defined in the `propNames` array.
|
||||
*
|
||||
* Returns a promise with a key-value object containing listed properties.
|
||||
*/
|
||||
winget(id: string | null, propNames: string[]): Promise<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Assigns properties to BYOND skin elements in bulk.
|
||||
*/
|
||||
winset(props: object): void;
|
||||
|
||||
/**
|
||||
* Assigns properties to the BYOND skin element.
|
||||
*/
|
||||
winset(id: string | null, props: object): void;
|
||||
|
||||
/**
|
||||
* Sets a property on the BYOND skin element to a certain value.
|
||||
*/
|
||||
winset(id: string | null, propName: string, propValue: any): void;
|
||||
|
||||
/**
|
||||
* Parses BYOND JSON.
|
||||
*
|
||||
* Uses a special encoding to preserve `Infinity` and `NaN`.
|
||||
*/
|
||||
parseJson(text: string): any;
|
||||
|
||||
/**
|
||||
* Sends a message to `/datum/tgui_window` which hosts this window instance.
|
||||
*/
|
||||
sendMessage(type: string, payload?: any): void;
|
||||
sendMessage(message: TguiMessage): void;
|
||||
|
||||
/**
|
||||
* Subscribe to incoming messages that were sent from `/datum/tgui_window`.
|
||||
*/
|
||||
subscribe(listener: (type: string, payload: any) => void): void;
|
||||
|
||||
/**
|
||||
* Subscribe to incoming messages *of some specific type*
|
||||
* that were sent from `/datum/tgui_window`.
|
||||
*/
|
||||
subscribeTo(type: string, listener: (payload: any) => void): void;
|
||||
|
||||
/**
|
||||
* Loads a stylesheet into the document.
|
||||
*/
|
||||
loadCss(url: string): void;
|
||||
|
||||
/**
|
||||
* Loads a script into the document.
|
||||
*/
|
||||
loadJs(url: string): void;
|
||||
|
||||
/**
|
||||
* Maps icons to their ref
|
||||
*/
|
||||
iconRefMap: Record<string, string>;
|
||||
|
||||
/**
|
||||
* Downloads a blob, platform-agnostic
|
||||
*/
|
||||
saveBlob(blob: Blob, filename: string, ext: string): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Object that provides access to Byond Skin API and is available in
|
||||
* any tgui application.
|
||||
*/
|
||||
const Byond: ByondType;
|
||||
|
||||
interface Window {
|
||||
Byond: ByondType;
|
||||
__store__: Store<unknown, AnyAction>;
|
||||
__augmentStack__: (store: Store) => StackAugmentor;
|
||||
|
||||
// IE IndexedDB stuff.
|
||||
msIndexedDB: IDBFactory;
|
||||
msIDBTransaction: IDBTransaction;
|
||||
|
||||
// 516 byondstorage API.
|
||||
hubStorage: Storage;
|
||||
domainStorage: Storage;
|
||||
serverStorage: Storage;
|
||||
|
||||
// TGUI stuff
|
||||
__chatRenderer__: ChatRenderer;
|
||||
}
|
||||
|
||||
+31
-26
@@ -5,42 +5,47 @@
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"tgui:analyze": "rspack --analyze",
|
||||
"tgui:bench": "rspack --env TGUI_BENCH=1 && node packages/tgui-bench/index.js",
|
||||
"tgui:build": "rspack build",
|
||||
"tgui:dev": "node --experimental-modules packages/tgui-dev-server/index.js",
|
||||
"tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx",
|
||||
"tgui:prettier": "prettier --check .",
|
||||
"tgui:sonar": "eslint packages -c .eslintrc-sonar.yml",
|
||||
"tgui:test": "vitest",
|
||||
"tgui:test-simple": "CI=true vitest",
|
||||
"tgui:test-ci": "CI=true vitest",
|
||||
"tgui:tsc": "tsc",
|
||||
"tgui:prettier-fix": "prettier --write .",
|
||||
"tgui:eslint-fix": "eslint --fix packages --ext .js,.cjs,.ts,.jsx,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.28.5",
|
||||
"@babel/eslint-parser": "^7.28.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-transform-jscript": "^7.27.1",
|
||||
"@babel/preset-env": "^7.28.5",
|
||||
"@babel/preset-typescript": "^7.28.5",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/jsdom": "^16.2.15",
|
||||
"@types/node": "^14.18.63",
|
||||
"@typescript-eslint/parser": "^4.33.0",
|
||||
"babel-jest": "^27.5.1",
|
||||
"babel-loader": "^8.4.1",
|
||||
"babel-plugin-inferno": "^6.8.4",
|
||||
"babel-plugin-transform-remove-console": "^6.9.4",
|
||||
"@rspack/cli": "^1.6.8",
|
||||
"@rspack/core": "^1.6.8",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/jsdom": "^27.0.0",
|
||||
"@types/node": "^25.0.3",
|
||||
"@typescript-eslint/parser": "^8.50.0",
|
||||
"common": "workspace:*",
|
||||
"css-loader": "^5.2.7",
|
||||
"eslint": "^7.32.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-radar": "^0.2.1",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"file-loader": "^6.2.0",
|
||||
"inferno": "^7.4.11",
|
||||
"jest": "^27.5.1",
|
||||
"jest-circus": "^27.5.1",
|
||||
"inferno": "^9.0.8",
|
||||
"jest": "^30.2.0",
|
||||
"jest-circus": "^30.2.0",
|
||||
"jsdom": "^27.3.0",
|
||||
"katex": "^0.15.6",
|
||||
"mini-css-extract-plugin": "^1.6.2",
|
||||
"katex": "^0.16.27",
|
||||
"mini-css-extract-plugin": "^2.9.4",
|
||||
"sass": "^1.97.1",
|
||||
"sass-loader": "^16.0.6",
|
||||
"style-loader": "^2.0.0",
|
||||
"style-loader": "^4.0.0",
|
||||
"terser-webpack-plugin": "^5.3.16",
|
||||
"tgui-core": "^5.6.0",
|
||||
"typescript": "^5.9.3",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.104.1",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-cli": "^4.10.0"
|
||||
"url-loader": "^4.1.1"
|
||||
},
|
||||
"packageManager": "yarn@3.4.1"
|
||||
}
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { compose } from './fp';
|
||||
|
||||
/**
|
||||
* Creates a Redux store.
|
||||
*/
|
||||
export const createStore = (reducer, enhancer) => {
|
||||
// Apply a store enhancer (applyMiddleware is one of them).
|
||||
if (enhancer) {
|
||||
return enhancer(createStore)(reducer);
|
||||
}
|
||||
|
||||
let currentState;
|
||||
let listeners = [];
|
||||
|
||||
const getState = () => currentState;
|
||||
|
||||
const subscribe = listener => {
|
||||
listeners.push(listener);
|
||||
};
|
||||
|
||||
const dispatch = action => {
|
||||
currentState = reducer(currentState, action);
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
listeners[i]();
|
||||
}
|
||||
};
|
||||
|
||||
// This creates the initial store by causing each reducer to be called
|
||||
// with an undefined state
|
||||
dispatch({
|
||||
type: '@@INIT',
|
||||
});
|
||||
|
||||
return {
|
||||
dispatch,
|
||||
subscribe,
|
||||
getState,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a store enhancer which applies middleware to all dispatched
|
||||
* actions.
|
||||
*/
|
||||
export const applyMiddleware = (...middlewares) => {
|
||||
return createStore => (reducer, ...args) => {
|
||||
const store = createStore(reducer, ...args);
|
||||
|
||||
let dispatch = () => {
|
||||
throw new Error(
|
||||
'Dispatching while constructing your middleware is not allowed.');
|
||||
};
|
||||
|
||||
const storeApi = {
|
||||
getState: store.getState,
|
||||
dispatch: (action, ...args) => dispatch(action, ...args),
|
||||
};
|
||||
|
||||
const chain = middlewares.map(middleware => middleware(storeApi));
|
||||
dispatch = compose(...chain)(store.dispatch);
|
||||
|
||||
return {
|
||||
...store,
|
||||
dispatch,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Combines reducers by running them in their own object namespaces as
|
||||
* defined in reducersObj paramter.
|
||||
*
|
||||
* Main difference from redux/combineReducers is that it preserves keys
|
||||
* in the state that are not present in the reducers object. This function
|
||||
* is also more flexible than the redux counterpart.
|
||||
*/
|
||||
export const combineReducers = reducersObj => {
|
||||
const keys = Object.keys(reducersObj);
|
||||
let hasChanged = false;
|
||||
return (prevState = {}, action) => {
|
||||
const nextState = { ...prevState };
|
||||
for (let key of keys) {
|
||||
const reducer = reducersObj[key];
|
||||
const prevDomainState = prevState[key];
|
||||
const nextDomainState = reducer(prevDomainState, action);
|
||||
if (prevDomainState !== nextDomainState) {
|
||||
hasChanged = true;
|
||||
nextState[key] = nextDomainState;
|
||||
}
|
||||
}
|
||||
return hasChanged
|
||||
? nextState
|
||||
: prevState;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility function to create an action creator for the given action
|
||||
* type string. The action creator accepts a single argument, which will
|
||||
* be included in the action object as a field called payload. The action
|
||||
* creator function will also have its toString() overriden so that it
|
||||
* returns the action type, allowing it to be used in reducer logic that
|
||||
* is looking for that action type.
|
||||
*
|
||||
* @param {string} type The action type to use for created actions.
|
||||
* @param {any} prepare (optional) a method that takes any number of arguments
|
||||
* and returns { payload } or { payload, meta }. If this is given, the
|
||||
* resulting action creator will pass it's arguments to this method to
|
||||
* calculate payload & meta.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const createAction = (type, prepare = null) => {
|
||||
const actionCreator = (...args) => {
|
||||
if (!prepare) {
|
||||
return { type, payload: args[0] };
|
||||
}
|
||||
const prepared = prepare(...args);
|
||||
if (!prepared) {
|
||||
throw new Error('prepare function did not return an object');
|
||||
}
|
||||
const action = { type };
|
||||
if ('payload' in prepared) {
|
||||
action.payload = prepared.payload;
|
||||
}
|
||||
if ('meta' in prepared) {
|
||||
action.meta = prepared.meta;
|
||||
}
|
||||
return action;
|
||||
};
|
||||
actionCreator.toString = () => '' + type;
|
||||
actionCreator.type = type;
|
||||
actionCreator.match = action => action.type === type;
|
||||
return actionCreator;
|
||||
};
|
||||
|
||||
|
||||
// Implementation specific
|
||||
// --------------------------------------------------------
|
||||
|
||||
export const useDispatch = context => {
|
||||
return context.store.dispatch;
|
||||
};
|
||||
|
||||
export const useSelector = (context, selector) => {
|
||||
return selector(context.store.getState());
|
||||
};
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
export type Reducer<State = any, ActionType extends Action = AnyAction> = (
|
||||
state: State | undefined,
|
||||
action: ActionType,
|
||||
) => State;
|
||||
|
||||
export type Store<State = any, ActionType extends Action = AnyAction> = {
|
||||
dispatch: Dispatch<ActionType>;
|
||||
subscribe: (listener: () => void) => void;
|
||||
getState: () => State;
|
||||
};
|
||||
|
||||
type MiddlewareAPI<State = any, ActionType extends Action = AnyAction> = {
|
||||
getState: () => State;
|
||||
dispatch: Dispatch<ActionType>;
|
||||
};
|
||||
|
||||
export type Middleware = <State = any, ActionType extends Action = AnyAction>(
|
||||
storeApi: MiddlewareAPI<State, ActionType>,
|
||||
) => (next: Dispatch<ActionType>) => Dispatch<ActionType>;
|
||||
|
||||
export type Action<TType = any> = {
|
||||
type: TType;
|
||||
};
|
||||
|
||||
export type AnyAction = Action & {
|
||||
[extraProps: string]: any;
|
||||
};
|
||||
|
||||
export type Dispatch<ActionType extends Action = AnyAction> = (
|
||||
action: ActionType,
|
||||
) => void;
|
||||
|
||||
type StoreEnhancer = (createStoreFunction: Function) => Function;
|
||||
|
||||
type PreparedAction = {
|
||||
payload?: any;
|
||||
meta?: any;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a Redux store.
|
||||
*/
|
||||
export const createStore = <State, ActionType extends Action = AnyAction>(
|
||||
reducer: Reducer<State, ActionType>,
|
||||
enhancer?: StoreEnhancer,
|
||||
): Store<State, ActionType> => {
|
||||
// Apply a store enhancer (applyMiddleware is one of them).
|
||||
if (enhancer) {
|
||||
return enhancer(createStore)(reducer);
|
||||
}
|
||||
|
||||
let currentState: State;
|
||||
let listeners: Array<() => void> = [];
|
||||
|
||||
const getState = (): State => currentState;
|
||||
|
||||
const subscribe = (listener: () => void): void => {
|
||||
listeners.push(listener);
|
||||
};
|
||||
|
||||
const dispatch = (action: ActionType): void => {
|
||||
currentState = reducer(currentState, action);
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
listeners[i]();
|
||||
}
|
||||
};
|
||||
|
||||
// This creates the initial store by causing each reducer to be called
|
||||
// with an undefined state
|
||||
dispatch({ type: '@@INIT' } as ActionType);
|
||||
|
||||
return {
|
||||
dispatch,
|
||||
subscribe,
|
||||
getState,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a store enhancer which applies middleware to all dispatched
|
||||
* actions.
|
||||
*/
|
||||
export const applyMiddleware = (
|
||||
...middlewares: Middleware[]
|
||||
): StoreEnhancer => {
|
||||
return (
|
||||
createStoreFunction: (reducer: Reducer, enhancer?: StoreEnhancer) => Store,
|
||||
) => {
|
||||
return (reducer, ...args): Store => {
|
||||
const store = createStoreFunction(reducer, ...args);
|
||||
|
||||
let dispatch: Dispatch = (action, ...args) => {
|
||||
throw new Error(
|
||||
'Dispatching while constructing your middleware is not allowed.',
|
||||
);
|
||||
};
|
||||
|
||||
const storeApi: MiddlewareAPI = {
|
||||
getState: store.getState,
|
||||
dispatch: (action, ...args) => dispatch(action, ...args),
|
||||
};
|
||||
|
||||
const chain = middlewares.map((middleware) => middleware(storeApi));
|
||||
dispatch = chain.reduceRight(
|
||||
(next, middleware) => middleware(next),
|
||||
store.dispatch,
|
||||
);
|
||||
|
||||
return {
|
||||
...store,
|
||||
dispatch,
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Combines reducers by running them in their own object namespaces as
|
||||
* defined in reducersObj paramter.
|
||||
*
|
||||
* Main difference from redux/combineReducers is that it preserves keys
|
||||
* in the state that are not present in the reducers object. This function
|
||||
* is also more flexible than the redux counterpart.
|
||||
*/
|
||||
export const combineReducers = (
|
||||
reducersObj: Record<string, Reducer>,
|
||||
): Reducer => {
|
||||
const keys = Object.keys(reducersObj);
|
||||
|
||||
return (prevState = {}, action) => {
|
||||
const nextState = { ...prevState };
|
||||
let hasChanged = false;
|
||||
|
||||
for (const key of keys) {
|
||||
const reducer = reducersObj[key];
|
||||
const prevDomainState = prevState[key];
|
||||
const nextDomainState = reducer(prevDomainState, action);
|
||||
|
||||
if (prevDomainState !== nextDomainState) {
|
||||
hasChanged = true;
|
||||
nextState[key] = nextDomainState;
|
||||
}
|
||||
}
|
||||
|
||||
return hasChanged ? nextState : prevState;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* A utility function to create an action creator for the given action
|
||||
* type string. The action creator accepts a single argument, which will
|
||||
* be included in the action object as a field called payload. The action
|
||||
* creator function will also have its toString() overriden so that it
|
||||
* returns the action type, allowing it to be used in reducer logic that
|
||||
* is looking for that action type.
|
||||
*
|
||||
* @param {string} type The action type to use for created actions.
|
||||
* @param {any} prepare (optional) a method that takes any number of arguments
|
||||
* and returns { payload } or { payload, meta }. If this is given, the
|
||||
* resulting action creator will pass its arguments to this method to
|
||||
* calculate payload & meta.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const createAction = <TAction extends string>(
|
||||
type: TAction,
|
||||
prepare?: (...args: any[]) => PreparedAction,
|
||||
) => {
|
||||
const actionCreator = (...args: any[]) => {
|
||||
let action: Action<TAction> & PreparedAction = { type };
|
||||
|
||||
if (prepare) {
|
||||
const prepared = prepare(...args);
|
||||
if (!prepared) {
|
||||
throw new Error('prepare function did not return an object');
|
||||
}
|
||||
action = { ...action, ...prepared };
|
||||
} else {
|
||||
action.payload = args[0];
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
||||
|
||||
actionCreator.toString = () => type;
|
||||
actionCreator.type = type;
|
||||
actionCreator.match = (action) => action.type === type;
|
||||
|
||||
return actionCreator;
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "tgfont",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"fantasticon": "^1.2.3"
|
||||
"fantasticon": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node mkdist.cjs && fantasticon --config config.cjs"
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"axios": "^1.13.2",
|
||||
"glob": "^7.2.3",
|
||||
"glob": "^13.0.0",
|
||||
"source-map": "^0.7.6",
|
||||
"stacktrace-parser": "^0.1.11",
|
||||
"ws": "^7.5.10"
|
||||
"ws": "^8.18.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"version": "4.3.0",
|
||||
"dependencies": {
|
||||
"common": "workspace:*",
|
||||
"dompurify": "^2.5.8",
|
||||
"inferno": "^7.4.11",
|
||||
"dompurify": "^3.3.1",
|
||||
"inferno": "^9.0.8",
|
||||
"tgui": "workspace:*",
|
||||
"tgui-dev-server": "workspace:*"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"clean-css-cli": "^5.6.3",
|
||||
"terser": "^5.39.0"
|
||||
"terser": "^5.44.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"common": "workspace:*",
|
||||
"dateformat": "^4.6.3",
|
||||
"dompurify": "^2.5.8",
|
||||
"inferno": "^7.4.11",
|
||||
"inferno-vnode-flags": "^7.4.11",
|
||||
"dateformat": "^5.0.3",
|
||||
"dompurify": "^3.3.1",
|
||||
"inferno": "^9.0.8",
|
||||
"inferno-vnode-flags": "^9.0.8",
|
||||
"js-yaml": "^4.1.1",
|
||||
"katex": "^0.15.6",
|
||||
"marked": "^4.3.0",
|
||||
"katex": "^0.16.27",
|
||||
"marked": "^17.0.1",
|
||||
"tgui-dev-server": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { flow } from 'common/fp';
|
||||
import { applyMiddleware, combineReducers, createStore } from 'common/redux';
|
||||
import { Component } from 'inferno';
|
||||
import { assetMiddleware } from './assets';
|
||||
import { backendMiddleware, backendReducer } from './backend';
|
||||
import { debugMiddleware, debugReducer, relayMiddleware } from './debug';
|
||||
import { createLogger } from './logging';
|
||||
|
||||
const logger = createLogger('store');
|
||||
|
||||
export const configureStore = (options = {}) => {
|
||||
const reducer = flow([
|
||||
combineReducers({
|
||||
debug: debugReducer,
|
||||
backend: backendReducer,
|
||||
}),
|
||||
options.reducer,
|
||||
]);
|
||||
const middleware = [
|
||||
...(options.middleware?.pre || []),
|
||||
assetMiddleware,
|
||||
backendMiddleware,
|
||||
...(options.middleware?.post || []),
|
||||
];
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
middleware.unshift(
|
||||
loggingMiddleware,
|
||||
debugMiddleware,
|
||||
relayMiddleware);
|
||||
}
|
||||
const enhancer = applyMiddleware(...middleware);
|
||||
const store = createStore(reducer, enhancer);
|
||||
// Globals
|
||||
window.__store__ = store;
|
||||
window.__augmentStack__ = createStackAugmentor(store);
|
||||
return store;
|
||||
};
|
||||
|
||||
const loggingMiddleware = store => next => action => {
|
||||
const { type, payload } = action;
|
||||
if (type === 'update' || type === 'backend/update') {
|
||||
logger.debug('action', { type });
|
||||
}
|
||||
else {
|
||||
logger.debug('action', action);
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a function, which can be assigned to window.__augmentStack__
|
||||
* to augment reported stack traces with useful data for debugging.
|
||||
*/
|
||||
const createStackAugmentor = store => (stack, error) => {
|
||||
if (!error) {
|
||||
error = new Error(stack.split('\n')[0]);
|
||||
error.stack = stack;
|
||||
}
|
||||
else if (typeof error === 'object' && !error.stack) {
|
||||
error.stack = stack;
|
||||
}
|
||||
logger.log('FatalError:', error);
|
||||
const state = store.getState();
|
||||
const config = state?.backend?.config;
|
||||
let augmentedStack = stack;
|
||||
augmentedStack += '\nUser Agent: ' + navigator.userAgent;
|
||||
augmentedStack += '\nState: ' + JSON.stringify({
|
||||
ckey: config?.client?.ckey,
|
||||
interface: config?.interface,
|
||||
window: config?.window,
|
||||
});
|
||||
return augmentedStack;
|
||||
};
|
||||
|
||||
/**
|
||||
* Store provider for Inferno apps.
|
||||
*/
|
||||
export class StoreProvider extends Component {
|
||||
getChildContext() {
|
||||
const { store } = this.props;
|
||||
return { store };
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import {
|
||||
applyMiddleware,
|
||||
combineReducers,
|
||||
createStore,
|
||||
Middleware,
|
||||
Reducer,
|
||||
Store,
|
||||
} from 'common/redux';
|
||||
import { flow } from 'tgui-core/fp';
|
||||
|
||||
import { assetMiddleware } from './assets';
|
||||
import { backendMiddleware, backendReducer } from './backend';
|
||||
import { debugMiddleware, debugReducer, relayMiddleware } from './debug';
|
||||
import { createLogger } from './logging';
|
||||
|
||||
type ConfigureStoreOptions = {
|
||||
sideEffects?: boolean;
|
||||
reducer?: Reducer;
|
||||
middleware?: {
|
||||
pre?: Middleware[];
|
||||
post?: Middleware[];
|
||||
};
|
||||
};
|
||||
|
||||
type StackAugmentor = (stack: string, error?: Error) => string;
|
||||
|
||||
const logger = createLogger('store');
|
||||
|
||||
export const configureStore = (options: ConfigureStoreOptions = {}): Store => {
|
||||
const { sideEffects = true, reducer, middleware } = options;
|
||||
const rootReducer: Reducer = flow([
|
||||
combineReducers({
|
||||
debug: debugReducer,
|
||||
backend: backendReducer,
|
||||
}),
|
||||
reducer as any,
|
||||
]);
|
||||
|
||||
const middlewares: Middleware[] = !sideEffects
|
||||
? []
|
||||
: [
|
||||
...(middleware?.pre || []),
|
||||
assetMiddleware,
|
||||
backendMiddleware,
|
||||
...(middleware?.post || []),
|
||||
];
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// We are using two if statements because Webpack is capable of
|
||||
// removing this specific block as dead code.
|
||||
if (sideEffects) {
|
||||
middlewares.unshift(loggingMiddleware, debugMiddleware, relayMiddleware);
|
||||
}
|
||||
}
|
||||
|
||||
const enhancer = applyMiddleware(...middlewares);
|
||||
const store = createStore(rootReducer, enhancer);
|
||||
|
||||
// Globals
|
||||
window.__store__ = store;
|
||||
window.__augmentStack__ = createStackAugmentor(store);
|
||||
|
||||
return store;
|
||||
};
|
||||
|
||||
const loggingMiddleware: Middleware = (store) => (next) => (action) => {
|
||||
const { type } = action;
|
||||
logger.debug(
|
||||
'action',
|
||||
type === 'update' || type === 'backend/update' ? { type } : action,
|
||||
);
|
||||
return next(action);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a function, which can be assigned to window.__augmentStack__
|
||||
* to augment reported stack traces with useful data for debugging.
|
||||
*/
|
||||
const createStackAugmentor
|
||||
= (store: Store): StackAugmentor =>
|
||||
(stack, error) => {
|
||||
error = error || new Error(stack.split('\n')[0]);
|
||||
error.stack = error.stack || stack;
|
||||
|
||||
logger.log('FatalError:', error);
|
||||
const state = store.getState();
|
||||
const config = state?.backend?.config;
|
||||
|
||||
return (
|
||||
stack
|
||||
+ '\nUser Agent: '
|
||||
+ navigator.userAgent
|
||||
+ '\nState: '
|
||||
+ JSON.stringify({
|
||||
ckey: config?.client?.ckey,
|
||||
interface: config?.interface,
|
||||
window: config?.window,
|
||||
})
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
const { defineConfig } = require('@rspack/cli');
|
||||
const { rspack } = require('@rspack/core');
|
||||
|
||||
const createStats = (verbose) => ({
|
||||
assets: verbose,
|
||||
builtAt: verbose,
|
||||
cached: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
colors: true,
|
||||
entrypoints: true,
|
||||
hash: false,
|
||||
modules: false,
|
||||
performance: false,
|
||||
timings: verbose,
|
||||
version: verbose,
|
||||
});
|
||||
|
||||
/**
|
||||
* 04/25/2025
|
||||
* There is a bug in rspack, possibly only ours, with the experimental css
|
||||
* feature that throws an error in tgui-dev. This prevents hot reloading from
|
||||
* working properly and there doesn't seem to be any way to fix it.
|
||||
*
|
||||
* This config exists to switch to the old css loader during development.
|
||||
*
|
||||
* `TypeError: Cannot read properties of null (reading 'removeChild')`
|
||||
*/
|
||||
module.exports = (env = {}, argv) => {
|
||||
/** @type {import('@rspack/core').Configuration} */
|
||||
const config = defineConfig({
|
||||
cache: false,
|
||||
experiments: undefined,
|
||||
mode: 'development',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.([tj]s(x)?|cjs)$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'builtin:swc-loader',
|
||||
options: {
|
||||
isModule: 'unknown',
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'javascript/auto',
|
||||
},
|
||||
{
|
||||
test: /\.(s)?css$/,
|
||||
use: [
|
||||
{
|
||||
loader: rspack.CssExtractRspackPlugin.loader,
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
},
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
options: {
|
||||
api: 'modern-compiler',
|
||||
implementation: 'sass-embedded',
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'javascript/auto',
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg)$/,
|
||||
type: 'asset/resource',
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
oneOf: [
|
||||
{
|
||||
issuer: /\.(s)?css$/,
|
||||
type: 'asset/inline',
|
||||
},
|
||||
{
|
||||
type: 'asset/resource',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new rspack.EnvironmentPlugin({
|
||||
NODE_ENV: 'development',
|
||||
WEBPACK_HMR_ENABLED: env.WEBPACK_HMR_ENABLED || argv.hot || false,
|
||||
DEV_SERVER_IP: env.DEV_SERVER_IP || null,
|
||||
}),
|
||||
new rspack.CssExtractRspackPlugin({
|
||||
filename: '[name].bundle.css',
|
||||
chunkFilename: '[name].bundle.css',
|
||||
}),
|
||||
],
|
||||
});
|
||||
config.devtool = 'cheap-module-source-map';
|
||||
config.devServer = {
|
||||
progress: false,
|
||||
quiet: false,
|
||||
noInfo: false,
|
||||
clientLogLevel: 'silent',
|
||||
stats: createStats(false),
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const { defineConfig } = require('@rspack/cli');
|
||||
const { rspack } = require('@rspack/core');
|
||||
|
||||
const createStats = (verbose) => ({
|
||||
assets: verbose,
|
||||
builtAt: verbose,
|
||||
cached: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
colors: true,
|
||||
entrypoints: true,
|
||||
hash: false,
|
||||
modules: false,
|
||||
performance: false,
|
||||
timings: verbose,
|
||||
version: verbose,
|
||||
});
|
||||
|
||||
module.exports = (env = {}, argv) => {
|
||||
const mode = argv.mode || 'production';
|
||||
const bench = env.TGUI_BENCH;
|
||||
|
||||
/** @type {import('@rspack/core').Configuration} */
|
||||
const config = defineConfig({
|
||||
cache: true,
|
||||
experiments: {
|
||||
cache: {
|
||||
type: 'persistent',
|
||||
storage: {
|
||||
type: 'filesystem',
|
||||
directory: path.resolve(__dirname, '.yarn/rspack'),
|
||||
},
|
||||
},
|
||||
css: true,
|
||||
},
|
||||
mode: mode === 'production' ? 'production' : 'development',
|
||||
context: path.resolve(__dirname),
|
||||
target: ['web', 'browserslist:edge >= 123'],
|
||||
entry: {
|
||||
tgui: ['./packages/tgui'],
|
||||
'tgui-panel': ['./packages/tgui-panel'],
|
||||
},
|
||||
output: {
|
||||
path: argv.useTmpFolder
|
||||
? path.resolve(__dirname, './public/.tmp')
|
||||
: path.resolve(__dirname, './public'),
|
||||
filename: '[name].bundle.js',
|
||||
chunkFilename: '[name].bundle.js',
|
||||
chunkLoadTimeout: 15000,
|
||||
publicPath: '/',
|
||||
assetModuleFilename: '[name][ext]',
|
||||
},
|
||||
resolve: {
|
||||
pnp: true,
|
||||
extensions: ['.tsx', '.ts', '.js', '.jsx'],
|
||||
alias: {
|
||||
tgui: path.resolve(__dirname, './packages/tgui'),
|
||||
'tgui-panel': path.resolve(__dirname, './packages/tgui-panel'),
|
||||
'tgui-dev-server': path.resolve(
|
||||
__dirname,
|
||||
'./packages/tgui-dev-server',
|
||||
),
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.([tj]s(x)?|cjs)$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'builtin:swc-loader',
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'javascript/auto',
|
||||
},
|
||||
{
|
||||
test: /\.(s)?css$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
options: {
|
||||
api: 'modern-compiler',
|
||||
implementation: 'sass-embedded',
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'css',
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg)$/,
|
||||
type: 'asset/resource',
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
oneOf: [
|
||||
{
|
||||
issuer: /\.(s)?css$/,
|
||||
type: 'asset/inline',
|
||||
},
|
||||
{
|
||||
type: 'asset/resource',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
emitOnErrors: false,
|
||||
},
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
devtool: false,
|
||||
|
||||
stats: createStats(true),
|
||||
plugins: [
|
||||
new rspack.EnvironmentPlugin({
|
||||
NODE_ENV: env.NODE_ENV || mode,
|
||||
WEBPACK_HMR_ENABLED: env.WEBPACK_HMR_ENABLED || argv.hot || false,
|
||||
DEV_SERVER_IP: env.DEV_SERVER_IP || null,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
if (bench) {
|
||||
config.entry = {
|
||||
'tgui-bench': ['./packages/tgui-bench/entrypoint'],
|
||||
};
|
||||
}
|
||||
|
||||
// Development build specific options
|
||||
if (mode !== 'production') {
|
||||
config.devtool = 'cheap-module-source-map';
|
||||
}
|
||||
|
||||
// Development server specific options
|
||||
if (argv.devServer) {
|
||||
config.devServer = {
|
||||
progress: false,
|
||||
quiet: false,
|
||||
noInfo: false,
|
||||
clientLogLevel: 'silent',
|
||||
stats: createStats(false),
|
||||
};
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
+19
-26
@@ -1,28 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"checkJs": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": false,
|
||||
"jsx": "preserve",
|
||||
"lib": [
|
||||
"DOM",
|
||||
"DOM.Iterable",
|
||||
"ESNext",
|
||||
"ScriptHost"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"target": "ES5"
|
||||
},
|
||||
"include": [
|
||||
"./*.d.ts",
|
||||
"./packages"
|
||||
]
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"checkJs": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "ScriptHost"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"strictNullChecks": true,
|
||||
"target": "ESNext"
|
||||
},
|
||||
"include": ["./*.d.ts", "./packages"]
|
||||
}
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const ExtractCssPlugin = require('mini-css-extract-plugin');
|
||||
const { createBabelConfig } = require('./babel.config.js');
|
||||
|
||||
const createStats = verbose => ({
|
||||
assets: verbose,
|
||||
builtAt: verbose,
|
||||
cached: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
colors: true,
|
||||
entrypoints: true,
|
||||
hash: false,
|
||||
modules: false,
|
||||
performance: false,
|
||||
timings: verbose,
|
||||
version: verbose,
|
||||
});
|
||||
|
||||
module.exports = (env = {}, argv) => {
|
||||
const mode = argv.mode === 'production' ? 'production' : 'development';
|
||||
const config = {
|
||||
mode,
|
||||
context: path.resolve(__dirname),
|
||||
target: ['web', 'es5', 'browserslist:ie 11'],
|
||||
entry: {
|
||||
'tgui': [
|
||||
'./packages/tgui',
|
||||
],
|
||||
'tgui-panel': [
|
||||
'./packages/tgui-panel',
|
||||
],
|
||||
},
|
||||
output: {
|
||||
path: argv.useTmpFolder
|
||||
? path.resolve(__dirname, './public/.tmp')
|
||||
: path.resolve(__dirname, './public'),
|
||||
filename: '[name].bundle.js',
|
||||
chunkFilename: '[name].bundle.js',
|
||||
chunkLoadTimeout: 15000,
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
alias: {},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|cjs|ts|tsx)$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: createBabelConfig({ mode }),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
{
|
||||
loader: ExtractCssPlugin.loader,
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg|svg)$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
emitOnErrors: false,
|
||||
},
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
devtool: false,
|
||||
cache: {
|
||||
type: 'filesystem',
|
||||
cacheLocation: path.resolve(__dirname, `.yarn/webpack/${mode}`),
|
||||
buildDependencies: {
|
||||
config: [__filename],
|
||||
},
|
||||
},
|
||||
stats: createStats(true),
|
||||
plugins: [
|
||||
new webpack.EnvironmentPlugin({
|
||||
NODE_ENV: env.NODE_ENV || argv.mode || 'development',
|
||||
WEBPACK_HMR_ENABLED: env.WEBPACK_HMR_ENABLED || argv.hot || false,
|
||||
DEV_SERVER_IP: env.DEV_SERVER_IP || null,
|
||||
}),
|
||||
new ExtractCssPlugin({
|
||||
filename: '[name].bundle.css',
|
||||
chunkFilename: '[name].bundle.css',
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
// Add a bundle analyzer to the plugins array
|
||||
if (argv.analyze) {
|
||||
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
||||
config.plugins = [
|
||||
...config.plugins,
|
||||
new BundleAnalyzerPlugin(),
|
||||
];
|
||||
}
|
||||
|
||||
// Production build specific options
|
||||
if (argv.mode === 'production') {
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
config.optimization.minimizer = [
|
||||
new TerserPlugin({
|
||||
extractComments: false,
|
||||
terserOptions: {
|
||||
ie8: true,
|
||||
output: {
|
||||
ascii_only: true,
|
||||
comments: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
// Development build specific options
|
||||
if (argv.mode !== 'production') {
|
||||
config.devtool = 'cheap-module-source-map';
|
||||
}
|
||||
|
||||
// Development server specific options
|
||||
if (argv.devServer) {
|
||||
config.devServer = {
|
||||
progress: false,
|
||||
quiet: false,
|
||||
noInfo: false,
|
||||
clientLogLevel: 'silent',
|
||||
stats: createStats(false),
|
||||
};
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
+3823
-4379
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user