- This faction is for IC administrative jobs used by staff.
-
"}
-
- departments = {"Command"}
+ description = "This faction is for IC administrative jobs used by staff."
+ departments = list(DEPARTMENT_COMMAND)
title_suffix = "SCC"
+ ui_priority = -INFINITY // top of the list
+
allowed_role_types = ADMIN_ROLES
allowed_species_types = list(
diff --git a/code/game/jobs/faction/faction.dm b/code/game/jobs/faction/faction.dm
index 75493c3f379..ddf7ee41ef2 100644
--- a/code/game/jobs/faction/faction.dm
+++ b/code/game/jobs/faction/faction.dm
@@ -2,7 +2,11 @@
var/name
var/description
var/title_suffix
- var/departments
+ var/list/departments
+ var/wiki_page
+
+ /// Number indicating the list index of this faction in the 'faction select' interface.
+ var/ui_priority
var/list/allowed_role_types
var/list/allowed_species_types
@@ -60,33 +64,31 @@
role.blacklisted_species = J.blacklisted_species
. += role
-/datum/faction/proc/get_selection_error(datum/preferences/prefs, var/mob/user)
+/datum/faction/proc/get_selection_error(datum/preferences/prefs, mob/user)
if(length(allowed_species_types))
-
var/datum/species/S = prefs.get_species_datum()
if(!S)
- return "No valid species selected."
+ return "No valid species"
if(!is_type_in_typecache(S, allowed_species_types))
- return "Invalid species selected."
+ return "Invalid species"
if(length(blacklisted_citizenship_types))
-
var/datum/citizenship/C = SSrecords.citizenships[prefs.citizenship]
if(!C)
- return "No valid nation selected."
+ return "No valid nation"
if(is_type_in_typecache(C, blacklisted_citizenship_types))
- return "Invalid nation selected."
+ return "Invalid nation"
- if (!is_visible(user))
- return "This faction is not available to you."
+ if(!is_visible(user))
+ return "This faction is not available to you"
return null
-/datum/faction/proc/can_select(datum/preferences/prefs, var/mob/user)
+/datum/faction/proc/can_select(datum/preferences/prefs, mob/user)
return !get_selection_error(prefs, user)
/datum/faction/proc/get_logo_name()
diff --git a/code/game/jobs/faction/hephaestus.dm b/code/game/jobs/faction/hephaestus.dm
index e0ec679eb6d..674b2948e74 100644
--- a/code/game/jobs/faction/hephaestus.dm
+++ b/code/game/jobs/faction/hephaestus.dm
@@ -1,17 +1,18 @@
/datum/faction/hephaestus_industries
name = "Hephaestus Industries"
- description = {"
+ description = {"
Hephaestus Industries, a sprawling and diverse mega-corporation
focused on engineering and manufacturing on a massive scale, found their start
- as a conglomerate of several aerospace companies in the 22nd century. Initially
+ as a conglomerate of several aerospace companies in the 22nd century.
Initially
funded by sales of new designs for warp technology, the company fell on hard times
during the Second Great Depression in the late 23rd century. Receiving bailouts
from the Sol Alliance and securing several crucial production contracts, they have slowly
worked their way to become the dominant manufacturing mega-corporation in the
Sol Alliance, pioneering interstellar logistics and construction on an awe-inspiring scale.
-
+ description = {"
The Orion Spur's largest interstellar banking conglomerate, Idris Incorporated
is operated by the mysterious Idris family. Idris Incorporated's influence
can be found in nearly every corner of human space with their financing of
- nearly every type of business and enterprise. Their higher risk ventures have
+ nearly every type of business and enterprise. Their higher risk ventures have
payment enforced by the infamous Idris Reclamation Units, shell IPCs sent to
claim payment from negligent loan takers. In recent years, they have begun
diversifying into more service-based industries.
-
- Considered the largest megacorporation within the Orion Spur, many will find themselves doing the biddings of NanoTrasen.
+ description = {"
+ Considered the largest megacorporation within the Orion Spur, many will find themselves doing the biddings of NanoTrasen.
Initially a biotechnical research company, it rapidly grew in size after the discovery of phoron.
- NanoTrasen's continued monopoly on the resource catapulted it into the limelight, where it has remained for the last forty-odd years.
+ NanoTrasen's continued monopoly on the resource catapulted it into the limelight, where it has remained for the last forty-odd years.
During the Phoron Scarcity, NanoTrasen's power has since begun to waver, resulting in their profit margins diminishing considerably.
Nonetheless, NanoTrasen has managed to secure itself as a crucial member of the newly-founded Stellar Corporate Conglomerate
allowing themselves to remain as a dominant corporate presence within the Orion Spur.
-
+ description = {"
Founded in 2464, the Orion Express is a corporation designed to handle logistics for the
Stellar Corporate Conglomerate in the wake of the Phoron Scarcity and the sudden issues
- the Conglomeration of the megacorporations presented. It consists of its main branch, dedicated
+ the Conglomeration of the megacorporations presented. It consists of its main branch, dedicated
to cargo services and transport, but also features a fledgling robotics division, mainly focused
- on industrial synthetics to aid in its logistics missions. The Orion Express is expected to become an
+ on industrial synthetics to aid in its logistics missions. The Orion Express is expected to become an
integral part of the Stellar Corporate Conglomerate's future through delivering supplies and merchandise throughout the Orion Spur.
-
+ description = {"
The Private Military Contracting Group is a coalition of security and medical contractors;
- borne from the necessity of protecting the Stellar Corporate Conglomerate and its holdings.
+ borne from the necessity of protecting the Stellar Corporate Conglomerate and its holdings.
Following the ever-growing corporate empire, mercenaries and contractors from all across the spur are deployed accordingly;
- from mere office buildings to outposts in the Corporate Reconstruction Zone.
+ from mere office buildings to outposts in the Corporate Reconstruction Zone.
Unlike the other members of the Corporate Conglomerate, the Private Military Contracting Group has few employees of its own.
Only some liaisons and bureaucrats work behind the scenes to hire and manage the contractors;
the rest of its members are part of several organizations contracted to supply the PMCG.
-
+ description = {"
The Stellar Corporate Conglomerate (SCC) was formed at the height of corporate power in the galaxy,
originally to secure Tau Ceti assets during the Second Solarian Incursion.
Chainlink now exercises undisputed economic dominance over the Orion Spur,
- and is now willing and eager to supersede any and all higher authority placed before it.
+ and is now willing and eager to supersede any and all higher authority placed before it.
Cooperation has been deemed essential ever since Einstein Engines resurfaced due to the emerging prominence of warp travel.
They've remained in a shaky peace, puppeteered to carry out the engimatic whims of the Trasen family.
Of course, this doesn't stop their greed for power and glory - they're just corporations, after all.
-
+ description = {"
For individuals not necessarily tied to any of the large mega corporations lording
over human space, the mark of "independent" can open as many doors as it is likely to
- close. You would often see people lacking a concrete affiliation popping up as
+ close. You would often see people lacking a concrete affiliation popping up as
reporters, journalists, individual traders. Their presence, though often an inconvenience
for the mega corporations, is often necessary to forward the facade of economic freedom
that Tau Ceti reports to have.
-
+ description = {"
The largest weapons producer in human space, Zavodskoi Interstellar initially
- found its place with the invention of a militarized voidsuit for use in the Interstellar War.
+ found its place with the invention of a militarized voidsuit for use in the Interstellar War.
With many lucrative weapon contracts thanks to the Sol Alliance, as well as acquisitions of
other major armaments companies, Zavodskoi weapons can be found in the hands of nearly every
- military force across the Orion Spur. They are the main corporation found in the Empire of
+ military force across the Orion Spur. They are the main corporation found in the Empire of
Dominia, and are at the forefront of weapons development technology.
-
+ description = {"
Zeng-Hu Pharmaceuticals, born of a merger of two major biotech companies on
Earth in 2032, was the first to successfully develop cryogenics in the 21st
century for the purposes of space travel. This development, crucial to
interstellar colonization, helped propel them to their current position as the
- largest pharmaceutical and medical corporation in the Orion Spur. In more recent
+ largest pharmaceutical and medical corporation in the Orion Spur. In more recent
years, they were also the first mega-corporation to partner with the newly-discovered
Skrell, working closely with this alien species to pioneer cloning, a once
controversial field that is now more accepted today.
-
"}
-
- departments = {"Medical Research"}
+ "}
+ departments = list(DEPARTMENT_MEDICAL, DEPARTMENT_SCIENCE)
title_suffix = "Zeng"
+ wiki_page = "Zeng-Hu_Pharmaceuticals"
allowed_role_types = ZENG_ROLES
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index 7acc3d13966..7ef392dc41d 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -490,6 +490,11 @@ GLOBAL_LIST_INIT(valid_bloodtypes, list(
pref.citizenship = OO.possible_citizenships[1]
pref.religion = OO.possible_religions[1]
+ var/datum/faction/current_faction = SSjobs.name_factions[pref.faction]
+ if(!current_faction?.can_select(pref, user))
+ to_chat(user, SPAN_WARNING("[pref.faction] does not employ [mob_species.name_plural]. Resetting to [SSjobs.default_faction.name]!"))
+ pref.faction = SSjobs.default_faction.name
+
// Follows roughly the same way hair does above, but for gradient styles
var/global/list/valid_gradients = list()
diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm
index 8279494808f..bcbd86fcc69 100644
--- a/code/modules/client/preference_setup/occupation/occupation.dm
+++ b/code/modules/client/preference_setup/occupation/occupation.dm
@@ -1,6 +1,8 @@
/datum/category_item/player_setup_item/occupation
name = "Occupation"
sort_order = 1
+ /// The module datum for the faction select interface, if it's currently open.
+ var/datum/tgui_module/faction_select/faction_ui
/datum/category_item/player_setup_item/occupation/load_character(var/savefile/S)
S["alternate_option"] >> pref.alternate_option
@@ -163,7 +165,7 @@
"",
"
Character Faction ",
"This will influence the jobs you can select from, and the starting equipment. ",
- "[pref.faction]
"
+ "[pref.faction] "
)
dat += list(
@@ -292,17 +294,17 @@
if(SetJob(user, href_list["set_job"]))
return TOPIC_REFRESH_UPDATE_PREVIEW
- else if(href_list["faction_preview"])
- show_faction_menu(user, html_decode(href_list["faction_preview"]))
+ else if(href_list["faction_select"])
+ if(!faction_ui)
+ faction_ui = new(src)
+ faction_ui.ui_interact(user)
return TOPIC_NOACTION
- else if(href_list["faction_select"])
- validate_and_set_faction(html_decode(href_list["faction_select"]))
- show_faction_menu(user, html_decode(href_list["faction_select"]))
- return TOPIC_REFRESH_UPDATE_PREVIEW
-
return ..()
+/datum/category_item/player_setup_item/occupation/proc/on_ui_close()
+ faction_ui = null
+
/datum/category_item/player_setup_item/occupation/proc/sanitize_faction()
if (!SSjobs.name_factions[pref.faction])
pref.faction = SSjobs.default_faction.name
@@ -448,67 +450,10 @@
pref.player_alt_titles.Cut()
-/datum/category_item/player_setup_item/occupation/proc/show_faction_menu(mob/user, selected_faction) //note : selected faction is what you choose to see, pref.faction is the actual chosen faction
- simple_asset_ensure_is_sent(user, /datum/asset/simple/faction_icons)
-
- var/list/dat = list("
"
- dat += {""}
-
- dat += " Departments:"
- dat += "[faction.departments]"
- dat += "
"
- dat += "
"
- dat += "
"
-
- dat += "You can learn more about this faction on the wiki."
-
- if (selected_faction == pref.faction)
- dat += " \[Faction selected\]"
- else if (faction.can_select(pref,user))
- dat += " \[Select faction\]"
- else
- dat += " [faction.get_selection_error(pref, user)]"
- dat += "
"
-
- user << browse(HTML_SKELETON(dat.Join()), "window=factionpreview;size=750x450")
-
-/datum/category_item/player_setup_item/occupation/proc/validate_and_set_faction(selected_faction)
- var/datum/faction/faction = SSjobs.name_factions[selected_faction]
-
- if (!faction)
- to_client_chat(SPAN_DANGER("Invalid faction chosen. Resetting to default."))
- selected_faction = SSjobs.default_faction.name
-
+/datum/category_item/player_setup_item/occupation/proc/validate_and_set_faction(datum/faction/faction)
ResetJobs() // How to be horribly lazy.
-
- pref.faction = selected_faction
-
- to_client_chat(SPAN_NOTICE("New faction chosen. Job preferences reset."))
+ pref.faction = faction.name
+ to_client_chat(SPAN_NOTICE("New faction chosen! Job preferences reset."))
/datum/preferences/proc/GetPlayerAltTitle(datum/job/job)
return player_alt_titles[job.title] || job.title
diff --git a/code/modules/tgui/modules/faction_select.dm b/code/modules/tgui/modules/faction_select.dm
new file mode 100644
index 00000000000..bcf85fab657
--- /dev/null
+++ b/code/modules/tgui/modules/faction_select.dm
@@ -0,0 +1,95 @@
+/datum/tgui_module/faction_select
+ /// The 'occupation' datum of the user viewing the interface.
+ var/datum/category_item/player_setup_item/occupation/occupation
+ /// The name of the currently viewed faction.
+ var/viewed_faction
+
+/datum/tgui_module/faction_select/New(datum/category_item/player_setup_item/occupation/occupation)
+ . = ..()
+ src.occupation = occupation
+ viewed_faction = occupation.pref.faction
+
+/datum/tgui_module/faction_select/ui_interact(mob/user, datum/tgui/ui)
+ . = ..()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "FactionSelect")
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/datum/tgui_module/faction_select/ui_close(mob/user)
+ . = ..()
+ occupation.on_ui_close()
+
+/datum/tgui_module/faction_select/ui_data(mob/user)
+ var/list/data = alist()
+ data["chosen_faction"] = occupation.pref.faction
+
+ var/datum/faction/faction_datum = get_faction(viewed_faction, user)
+ // Set `viewed_faction` to the `get_faction()` result from it, since that double checks that it's actually valid.
+ data["viewed_faction"] = faction_datum.name
+ data["viewed_selection_error"] = faction_datum.get_selection_error(occupation.pref, user)
+
+ return data
+
+/datum/tgui_module/faction_select/ui_static_data(mob/user)
+ var/list/data = alist()
+
+ var/list/factions = list()
+ for(var/datum/faction/faction as anything in sort_list(SSjobs.factions, GLOBAL_PROC_REF(cmp_faction)))
+ if(!faction.is_visible(user))
+ continue
+
+ factions.Add(list(alist(
+ "name" = faction.name,
+ "suffix" = faction.title_suffix,
+ "desc" = faction.description,
+ "logo" = faction.get_logo_name(),
+ "departments" = faction.departments,
+ "wiki_page" = faction.wiki_page
+ )))
+ data["factions"] = factions
+ data["wiki_url"] = GLOB.config.wikiurl
+ return data
+
+/datum/tgui_module/faction_select/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ var/datum/faction/faction = get_faction(params["faction"], usr, FALSE)
+
+ if(action == "view_faction")
+ viewed_faction = faction.name
+ return TRUE
+
+ if(action == "choose_faction")
+ var/faction_error = faction.get_selection_error(occupation.pref, usr)
+ if(faction_error)
+ to_chat(usr, SPAN_DANGER("Error selecting faction!: [faction_error]"))
+ return FALSE
+
+ occupation.validate_and_set_faction(faction)
+ usr.client.prefs.ShowChoices(usr)
+ usr.client.prefs.update_preview_icon()
+ return TRUE
+
+ if(action == "open_wiki")
+ var/client/client = usr.get_client()
+ client?.wiki(faction.wiki_page)
+ return FALSE
+
+/datum/tgui_module/faction_select/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/simple/faction_icons)
+ )
+
+/datum/tgui_module/faction_select/proc/get_faction(faction_name, mob/user, quiet = TRUE)
+ var/datum/faction/faction = SSjobs.name_factions[faction_name]
+ if(!istype(faction) || !faction.is_visible(user))
+ if(!quiet)
+ to_chat(usr, SPAN_WARNING("Invalid faction chosen. Resetting to [SSjobs.default_faction.name]."))
+ update_static_data(user) // Update the faction list to hopefully remove anything invalid.
+ return SSjobs.default_faction
+
+ return faction
diff --git a/html/changelogs/SabreML-PR-faction-select-tgui.yml b/html/changelogs/SabreML-PR-faction-select-tgui.yml
new file mode 100644
index 00000000000..7a9564b09c1
--- /dev/null
+++ b/html/changelogs/SabreML-PR-faction-select-tgui.yml
@@ -0,0 +1,14 @@
+# Your name.
+author: SabreML
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit.
+# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Revamped the 'Faction Select' UI in the occupation menu of character setup."
+ - bugfix: "Fixed changing species in character setup not resetting your faction if it became invalid."
diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json
index 6cf786961b4..aba7aae098b 100644
--- a/tgui/packages/tgui-dev-server/package.json
+++ b/tgui/packages/tgui-dev-server/package.json
@@ -6,7 +6,7 @@
"dependencies": {
"axios": "^1.12.0",
"glob": "^7.1.7",
- "source-map": "^0.7.3",
+ "source-map": "^0.7.4",
"stacktrace-parser": "^0.1.10",
"ws": "^7.5.10"
}
diff --git a/tgui/packages/tgui/interfaces/FactionSelect.tsx b/tgui/packages/tgui/interfaces/FactionSelect.tsx
new file mode 100644
index 00000000000..69970334f4e
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/FactionSelect.tsx
@@ -0,0 +1,232 @@
+// code/modules/tgui/modules/faction_select.dm
+import { classes } from 'common/react';
+import { resolveAsset } from '../assets';
+import { useBackend } from '../backend';
+import { Box, Button, Divider, Flex, Icon, LabeledList, Section, Stack } from '../components';
+import { Window } from '../layouts';
+
+export type FactionSelectData = {
+ chosen_faction: string;
+ viewed_faction: string;
+ viewed_selection_error: string;
+ factions: Faction[];
+ wiki_url: string;
+};
+
+export type Faction = {
+ name: string;
+ suffix: string;
+ desc: string;
+ logo: string;
+ departments: string[];
+ wiki_page: string;
+};
+
+export const FactionSelect = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const FactionList = (props, context) => {
+ const { act, data } = useBackend(context);
+ return (
+
+
+ {data.factions.map((faction) => (
+
+
+
+ ))}
+
+
+ );
+};
+
+const FactionInfo = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const currentFaction = data.factions.find(
+ (faction) => faction.name === data.viewed_faction
+ )!; // Non-null assertion for `currentFaction` since if it is null, that shouldn't be handled here.
+
+ const FormatDescription = (text: string) => {
+ return text
+ .replace(/\n\t/g, ' ')
+ .replace(/ \s*/g, '\n')
+ .trim();
+ };
+
+ return (
+
+
+
+
+ {currentFaction.name}
+
+
+
+
+
+
+
+
+
+
+ {FormatDescription(currentFaction.desc)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ━━ You can learn more about this faction on the wiki:
+
+
+
+ );
+};
+
+const FactionPanel = (props: { currentFaction: Faction }, context) => {
+ const { currentFaction } = props;
+ const { act, data } = useBackend(context);
+
+ const currentIsChosen = currentFaction.name === data.chosen_faction;
+ const CanSelect = () =>
+ currentFaction.name === data.chosen_faction ||
+ !!data.viewed_selection_error;
+
+ const Checkmark = (input: boolean) => {
+ if (input) {
+ return ;
+ } else {
+ return ;
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ Departments:
+
+ {currentFaction.departments.map((department) => (
+
+ {department}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {Checkmark(true)}
+ {/* NYI (/datum/faction/var/blacklisted_citizenship_types) */}
+
+
+ {Checkmark(!data.viewed_selection_error)}
+
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/styles/interfaces/FactionSelect.scss b/tgui/packages/tgui/styles/interfaces/FactionSelect.scss
new file mode 100644
index 00000000000..ff40dba683e
--- /dev/null
+++ b/tgui/packages/tgui/styles/interfaces/FactionSelect.scss
@@ -0,0 +1,36 @@
+@use '../components/Divider.scss';
+
+// Colours based on the companies' 'Official Colours' sections on the wiki.
+$company-colors: (
+ 'SCC': #1966b3,
+ 'NT': #14498f,
+ 'Hepht': #aa6d18,
+ 'Idris': #369fa1,
+ 'Orion': #aa6818,
+ 'PMCG': #1a1a1a,
+ 'Zavod': #8a1414,
+ 'Zeng': #b56cea,
+ // 'Independent' doesn't get a colour :(
+);
+
+.theme-faction_select {
+ .Divider {
+ &--vertical--dashed {
+ border-left: Divider.$thickness dashed Divider.$color;
+ }
+ }
+
+ .LabeledList__content {
+ text-align: right;
+ }
+
+ .Layout__content {
+ background-image: none;
+ }
+
+ @each $name, $color in $company-colors {
+ .faction_entry--#{$name}:not(.Button--selected) {
+ background-image: linear-gradient(to right, transparent 65%, $color);
+ }
+ }
+}
diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss
index 5c5b9e2777e..65226174596 100644
--- a/tgui/packages/tgui/styles/main.scss
+++ b/tgui/packages/tgui/styles/main.scss
@@ -23,6 +23,7 @@
@include meta.load-css('./components/BlockQuote.scss');
@include meta.load-css('./components/Button.scss');
@include meta.load-css('./components/ColorBox.scss');
+@include meta.load-css('./components/Dialog.scss');
@include meta.load-css('./components/Dimmer.scss');
@include meta.load-css('./components/Divider.scss');
@include meta.load-css('./components/Dropdown.scss');
@@ -44,12 +45,12 @@
@include meta.load-css('./components/Tabs.scss');
@include meta.load-css('./components/TextArea.scss');
@include meta.load-css('./components/Tooltip.scss');
-@include meta.load-css('./components/Dialog.scss');
// Interfaces
-@include meta.load-css('./interfaces/ListInput.scss');
@include meta.load-css('./interfaces/AlertModal.scss');
@include meta.load-css('./interfaces/CrewManifest.scss');
+@include meta.load-css('./interfaces/FactionSelect.scss');
+@include meta.load-css('./interfaces/ListInput.scss');
@include meta.load-css('./interfaces/PersonalCrafting.scss');
@include meta.load-css('./interfaces/RequestManager.scss');
diff --git a/tgui/yarn.lock b/tgui/yarn.lock
index c2b2af65f88..4529cc68842 100644
--- a/tgui/yarn.lock
+++ b/tgui/yarn.lock
@@ -9462,6 +9462,13 @@ resolve@^2.0.0-next.3:
languageName: node
linkType: hard
+"source-map@npm:^0.7.4":
+ version: 0.7.6
+ resolution: "source-map@npm:0.7.6"
+ checksum: 932f4a2390aa7100e91357d88cc272de984ad29139ac09eedfde8cc78d46da35f389065d0c5343c5d71d054a6ebd4939a8c0f2c98d5df64fe97bb8a730596c2d
+ languageName: node
+ linkType: hard
+
"sprintf-js@npm:~1.0.2":
version: 1.0.3
resolution: "sprintf-js@npm:1.0.3"
@@ -9967,7 +9974,7 @@ resolve@^2.0.0-next.3:
dependencies:
axios: ^1.12.0
glob: ^7.1.7
- source-map: ^0.7.3
+ source-map: ^0.7.4
stacktrace-parser: ^0.1.10
ws: ^7.5.10
languageName: unknown