steam-theming-utils 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -9
- package/cdp/class_modules.js +4 -4
- package/cdp/class_modules_webpack.js +2 -1
- package/cdp/db/accountpreferences.js +11 -0
- package/cdp/db/apppage.js +12 -0
- package/cdp/{class_modules_db.js → db/client.js} +38 -35
- package/cdp/db/gameslist.js +10 -0
- package/cdp/db/notificationspage.js +12 -0
- package/cdp/db/profileedit.js +21 -0
- package/cdp/db/shoppingcart.js +9 -0
- package/cdp/make_readable_classes.js +26 -7
- package/cdp/preload/profileedit.js +6 -0
- package/lib/build_class_modules.js +113 -14
- package/lib/make_readable_classes.js +12 -5
- package/lib/replace_old_classes.js +19 -19
- package/package.json +6 -5
- package/src/api.d.ts +36 -3
- package/src/api.js +30 -24
- package/src/constants.js +9 -8
- package/src/index.js +1 -10
- package/src/postcss_plugin.js +77 -0
- package/src/shared.js +92 -0
- package/lib/build_theme.js +0 -69
package/README.md
CHANGED
|
@@ -1,23 +1,51 @@
|
|
|
1
1
|
# steam-theming-utils
|
|
2
2
|
|
|
3
|
-
A collection of scripts for easier (and
|
|
3
|
+
A collection of scripts for easier (and future-proof) Steam theming.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
$ npm i steam-theming-utils
|
|
9
|
-
$ npx steam-theming-utils <script>
|
|
9
|
+
$ npx steam-theming-utils <script> <page>
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
Note that running any script requires Steam running with `-cef-enable-debugging`.
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Scripts
|
|
15
15
|
|
|
16
|
-
| Name | Description
|
|
17
|
-
| --------------------- |
|
|
18
|
-
| build_class_modules | Generates a
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
| replace_old_classes | Replaces old classes with new ones for themes not using the [template][template]. |
|
|
16
|
+
| Name | Description |
|
|
17
|
+
| --------------------- | ----------------------------------------------------------------------------------------- |
|
|
18
|
+
| build_class_modules | Generates a class map file for usage with other scripts. |
|
|
19
|
+
| make_readable_classes | Adds readable versions of classes to the focused window/page. ![Preview][classes-preview] |
|
|
20
|
+
| replace_old_classes | Replaces old classes with new ones for themes not using the [template][template]. |
|
|
22
21
|
|
|
22
|
+
## Pages
|
|
23
|
+
|
|
24
|
+
| Name | Description |
|
|
25
|
+
| ------------------ | ---------------------------------------------------------------------------- |
|
|
26
|
+
| apppage | Related items & controller info in https://store.steampowered.com/app/666220 |
|
|
27
|
+
| accountpreferences | https://store.steampowered.com/account |
|
|
28
|
+
| client | The Steam client. The default. |
|
|
29
|
+
| gameslist | https://steamcommunity.com/my/games |
|
|
30
|
+
| notificationspage | https://steamcommunity.com/my/notifications |
|
|
31
|
+
| profileedit | https://steamcommunity.com/my/edit |
|
|
32
|
+
| shoppingcart | https://store.steampowered.com/cart |
|
|
33
|
+
|
|
34
|
+
## Errors
|
|
35
|
+
|
|
36
|
+
| Name | Description |
|
|
37
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
38
|
+
| #ClassName is undefined | Typo or that class either got renamed or removed, and so you will have to update it yourself. |
|
|
39
|
+
| [mod_name] no such module | Typo or that module either got renamed or removed, see diffs [here][diffs] depending on the page. |
|
|
40
|
+
| [map_name] no such map | Use `npx steam-theming-utils build_class_modules map_name` to create it. |
|
|
41
|
+
|
|
42
|
+
## Config
|
|
43
|
+
|
|
44
|
+
Configured through a `steam-theming-utils.config.js` (or the one from [here][config-files]) file that must `export default` an [object][config-docs]. It's optional and has defaults listed [here][config-defaults].
|
|
45
|
+
|
|
46
|
+
[classes-preview]: ./img/readable_classes.png
|
|
47
|
+
[config-defaults]: https://github.com/ricewind012/steam-theming-utils/blob/master/src/constants.js#L7-L11
|
|
48
|
+
[config-docs]: https://github.com/ricewind012/steam-theming-utils/blob/master/src/api.d.ts#L4-L16
|
|
49
|
+
[config-files]: https://github.com/cosmiconfig/cosmiconfig#usage-for-end-users
|
|
50
|
+
[diffs]: https://github.com/ricewind012/steam-theming-utils/tree/master/cdp/db
|
|
23
51
|
[template]: https://github.com/ricewind012/more-advanced-theme-template
|
package/cdp/class_modules.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
classModules = {
|
|
2
|
-
...specialModules,
|
|
3
|
-
...parsedModules
|
|
4
|
-
|
|
5
|
-
.reduce((a, b) => Object.assign(a, b)),
|
|
2
|
+
...(window.specialModules || {}),
|
|
3
|
+
...(window.parsedModules
|
|
4
|
+
?.map((e) => ({ [e[0]]: e[1] }))
|
|
5
|
+
.reduce((a, b) => Object.assign(a, b)) || {}),
|
|
6
6
|
...exportedModules
|
|
7
7
|
.flatMap((a) => {
|
|
8
8
|
const mod = findFirstModule(a[1], a[0]);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Note that every module is only available on the "family management" page.
|
|
2
|
+
exportedModules = [
|
|
3
|
+
["authorizeddevices", (e) => e.AuthorizedDeviceGroup],
|
|
4
|
+
["cookies", (e) => e.CookieSection],
|
|
5
|
+
["familymanagement", (e) => e.FamilySettingsContainer],
|
|
6
|
+
["familymanagementtabs", (e) => e.GraphicalAssetsTabs],
|
|
7
|
+
// TODO: shared with steam settings notif page
|
|
8
|
+
["notifications", (e) => e.NotificationSection],
|
|
9
|
+
// TODO: gamepaddialog
|
|
10
|
+
["toggle", (e) => e.Field],
|
|
11
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
exportedModules = [
|
|
2
|
+
["storeitemscarousel", (e) => e.StoreItemsCarousel],
|
|
3
|
+
["capsule", (e) => e.MainCapsuleImageContainer],
|
|
4
|
+
["capsulecontainer", (e) => e.ItemCount1],
|
|
5
|
+
["carousel", (e) => e.pipScrollerContainer],
|
|
6
|
+
["creatorhome", (e) => e.FollowBtnText],
|
|
7
|
+
["creatorhomecarousel", (e) => e.CreatorHomeWithItems],
|
|
8
|
+
["focusring", (e) => e.FocusRingRoot],
|
|
9
|
+
["gamehover", (e) => e.GameHoverCapsuleCtn],
|
|
10
|
+
["scrollsnapcarousel", (e) => e.ScrollSnapCarousel],
|
|
11
|
+
["sidebarcontrollerinfo", (e) => e.StoreSidebarContainer],
|
|
12
|
+
];
|
|
@@ -44,14 +44,9 @@ specialModules = {
|
|
|
44
44
|
window.parsedModules = [
|
|
45
45
|
...(
|
|
46
46
|
await Promise.all(
|
|
47
|
-
[
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"chunk~1a96cdf59", // Also broadcast
|
|
51
|
-
"gamenotes",
|
|
52
|
-
"gamerecording",
|
|
53
|
-
].map(async (e) =>
|
|
54
|
-
(await fetch(`https://steamloopback.host/${e}.js`)).text(),
|
|
47
|
+
["awardicon", "broadcast", "gamenotes", "gamerecording"].map(
|
|
48
|
+
async (e) =>
|
|
49
|
+
(await fetch(`https://steamloopback.host/${e}.js`)).text(),
|
|
55
50
|
),
|
|
56
51
|
)
|
|
57
52
|
)
|
|
@@ -67,8 +62,6 @@ specialModules = {
|
|
|
67
62
|
return "awardicon";
|
|
68
63
|
case exists("PopOutVideoTitleBar"):
|
|
69
64
|
return "broadcastembeddable";
|
|
70
|
-
case exists("BroadcastPlayerLite"):
|
|
71
|
-
return "broadcastplayer";
|
|
72
65
|
case exists("StoreSaleImage_mini"):
|
|
73
66
|
return "broadcastwidgets";
|
|
74
67
|
case exists("ClipUploadStatus"):
|
|
@@ -78,7 +71,7 @@ specialModules = {
|
|
|
78
71
|
case exists("GameNotesPopup"):
|
|
79
72
|
return "gamenotespopups";
|
|
80
73
|
case exists("ClipSavedHint"):
|
|
81
|
-
return "
|
|
74
|
+
return "gamerecordingclip";
|
|
82
75
|
case exists("LinkHelp"):
|
|
83
76
|
return "pmhover";
|
|
84
77
|
case exists("CommandButton"):
|
|
@@ -124,7 +117,8 @@ exportedModules = [
|
|
|
124
117
|
["appdetailsfriendssection", (e) => e.FriendsOverflow],
|
|
125
118
|
["appdetailsgameinfocontainer", (e) => e.GameInfoShadow],
|
|
126
119
|
["appdetailsgameinfopanel", (e) => e.GameDescription],
|
|
127
|
-
["appdetailsheader", (e) => e.
|
|
120
|
+
["appdetailsheader", (e) => e.AppDetailsOverviewPanel],
|
|
121
|
+
["appdetailsheaderinfo", (e) => e.HeaderFriendsInGameBadge],
|
|
128
122
|
["appdetailshover", (e) => e.AppDetailsHover],
|
|
129
123
|
["appdetailsinvalidostype", (e) => e.InvalidOSTypeBody],
|
|
130
124
|
["appdetailsmastersubincluded", (e) => e.IncludedBanner],
|
|
@@ -175,10 +169,13 @@ exportedModules = [
|
|
|
175
169
|
["bluetoothsettings", (e) => e.NotConnectedLabel && e.Header],
|
|
176
170
|
["borrowgamedialog", (e) => e.BorrowGameDialog],
|
|
177
171
|
["bottombar", (e) => e.BottomBarContainer],
|
|
172
|
+
["bottombarprogressbar", (e) => e.loadingBarAnim],
|
|
178
173
|
["boxcarousel", (e) => e.BoxCarousel],
|
|
174
|
+
["bpmfriendsuicontainer", (e) => e.FriendsChatsContainer],
|
|
179
175
|
["broadcastchat", (e) => e.BroadcastChat],
|
|
180
176
|
["broadcastchatannouncement", (e) => e.GiveawayWinnerBox],
|
|
181
177
|
["broadcastfirsttime", (e) => e.BroadcastFirstTimeDialog],
|
|
178
|
+
["broadcastplayer", (e) => e.BroadcastPlayerLite],
|
|
182
179
|
["broadcastsettings", (e) => e.ConfigureMic],
|
|
183
180
|
["broadcaststatus", (e) => e.BroadcastStatusBody],
|
|
184
181
|
["browserviewfindinpage", (e) => e.ControlButton],
|
|
@@ -199,39 +196,39 @@ exportedModules = [
|
|
|
199
196
|
["clanimagechooser", (e) => e.ImagesOuterContainer],
|
|
200
197
|
["clanimagepickandresize", (e) => e.Image && Object.keys(e).length === 1],
|
|
201
198
|
["clipdecorator", (e) => e.ClipDecorator],
|
|
202
|
-
["clipgolive", (e) => e.GoLiveButton],
|
|
203
199
|
["clipmanager", (e) => e.ClipActions],
|
|
204
200
|
["clipplayback", (e) => e.PlaybackControlsCtn],
|
|
205
|
-
["
|
|
201
|
+
["clipplaybackscrollbar", (e) => e.ScrollBarCtn],
|
|
202
|
+
["clipplayheadseekscrubber", (e) => e.GhostPlayheadCtn],
|
|
206
203
|
["clipplaybackskip", (e) => e.SkipperCtn],
|
|
207
|
-
["clipplaybacktimeline", (e) => e.ScrollBarCtn],
|
|
208
204
|
["clipplayhead", (e) => e.PlayHeadContainer && !e.FullMask],
|
|
209
|
-
["clippostgame", (e) => e.
|
|
205
|
+
["clippostgame", (e) => e.PostGameSummaryHighlightGroup],
|
|
210
206
|
["clipsbutton", (e) => e.ClipsButtonContainer],
|
|
211
207
|
["cliptimeline", (e) => e.GamepadTimelineContainer],
|
|
212
|
-
["
|
|
208
|
+
["cliptimelinebackgroundticks", (e) => e.TimeTick],
|
|
213
209
|
["cliptimelinecontextmenu", (e) => e.TimelineContextMenuItem],
|
|
214
|
-
["
|
|
210
|
+
["cliptimelinedatedecorator", (e) => e.TimelineRelativeDate],
|
|
211
|
+
["cliptimelinegamemodes", (e) => e.GameModeMarker],
|
|
215
212
|
["cliptimelinemarker", (e) => e.MarkerBacking],
|
|
216
213
|
["cliptimelinemarkercontainer", (e) => e.TimelineMarkerCtn],
|
|
217
|
-
["
|
|
218
|
-
["
|
|
214
|
+
["cliptimelinerangeselector", (e) => e.TrackRangeControls && !e.ActiveCtn],
|
|
215
|
+
["cliptimelinerecordingdecorator", (e) => e.LiveRecordingBuffer],
|
|
219
216
|
["cloudconflict", (e) => e.ConflictChoiceText],
|
|
220
217
|
["cloudfileuploadprogress", (e) => e.UploadPreviewContainer],
|
|
221
218
|
["collapseicon", (e) => e.CollapseIconParent],
|
|
222
219
|
["collectionbanner", (e) => e.CollectionShelfBanner],
|
|
223
220
|
["collectionview", (e) => e.DynamicCollectionLabelAndButton],
|
|
224
221
|
["colorsettings", (e) => e.FloatingControls],
|
|
225
|
-
["
|
|
222
|
+
["commentthread", (e) => e.ActivityCommentThread],
|
|
226
223
|
["console", (e) => e.Console],
|
|
227
224
|
["contentmanagement", (e) => e.ContentManagement],
|
|
228
|
-
["contextmenu", (e) => e.
|
|
225
|
+
["contextmenu", (e) => e.ContextMenuMouseOverlay],
|
|
229
226
|
["controllerconfigurator", (e) => e.ControllerConfiguratorActionSetSelector],
|
|
230
227
|
[
|
|
231
|
-
"
|
|
228
|
+
"controllerconfiguratorinterstitial",
|
|
232
229
|
(e) => e.ConfiguratorInterstitialContainer,
|
|
233
230
|
],
|
|
234
|
-
["
|
|
231
|
+
["controllerconfiguratormouseposition", (e) => e.BackgroundScreenshot],
|
|
235
232
|
[
|
|
236
233
|
"controllerconfiguratoractionsetselector",
|
|
237
234
|
(e) => e.ActionSetNameOverIndicators,
|
|
@@ -243,11 +240,11 @@ exportedModules = [
|
|
|
243
240
|
["controllerconfiguratorsummary", (e) => e.StandardControl],
|
|
244
241
|
["controllerconfiguratorvirtualmenus", (e) => e.VirtualMenus],
|
|
245
242
|
[
|
|
246
|
-
"
|
|
243
|
+
"controllerconfiguratorvisualizerdeadzones",
|
|
247
244
|
(e) => e.VisualizerCenterXOffset,
|
|
248
245
|
],
|
|
249
246
|
["controllersettings", (e) => e.ControllerName],
|
|
250
|
-
["
|
|
247
|
+
["controllersettingsdevicecalibration", (e) => e.CalibrationButton],
|
|
251
248
|
["creatorhomeembed", (e) => e.DevSummaryCtn],
|
|
252
249
|
["cropmodal", (e) => e.CropImage],
|
|
253
250
|
["cssgrid", (e) => e.CSSGrid],
|
|
@@ -263,12 +260,12 @@ exportedModules = [
|
|
|
263
260
|
["desktopsecuritysettings", (e) => e.SteamGuardIcon],
|
|
264
261
|
["desktoptoasts", (e) => e.DesktopToastPopup],
|
|
265
262
|
["dialogs", (e) => e.DialogTitle && e.DialogContent],
|
|
266
|
-
["discoveryqueuewidget", (e) => e.
|
|
263
|
+
["discoveryqueuewidget", (e) => e.DiscoveryQueueApp],
|
|
267
264
|
["discoveryqueuewizard", (e) => e.DeckVerifiedLogo],
|
|
268
265
|
["discussionwidget", (e) => e.DiscussContainer],
|
|
269
266
|
["displayscaledialog", (e) => e.YouCanChangeThisLater],
|
|
270
267
|
["displaysettings", (e) => e.TimeRangeControls && !e.BandwidthInput],
|
|
271
|
-
["downloadgraph", (e) => e.
|
|
268
|
+
["downloadgraph", (e) => e.DownloadGraphStats],
|
|
272
269
|
["downloads", (e) => e.InProgress],
|
|
273
270
|
["downloadsettings", (e) => e.BandwidthLimit],
|
|
274
271
|
["draganddrop", (e) => e.GhostContainer],
|
|
@@ -294,7 +291,7 @@ exportedModules = [
|
|
|
294
291
|
["familysettings", (e) => e.AuthorizeUserField],
|
|
295
292
|
["familysharedcomponents", (e) => e.FamilyMemberRowTop],
|
|
296
293
|
["fastscrolloverlay", (e) => e.FastScrollOverlay],
|
|
297
|
-
["focusring", (e) => e.
|
|
294
|
+
["focusring", (e) => e.FocusRingRoot],
|
|
298
295
|
["footer", (e) => e.BasicFooter],
|
|
299
296
|
["footericons", (e) => e.Knockout],
|
|
300
297
|
["friendinvites", (e) => e.IncomingInvites],
|
|
@@ -332,17 +329,19 @@ exportedModules = [
|
|
|
332
329
|
["gamepadtabbedpage", (e) => e.CanBeHeaderBackground],
|
|
333
330
|
["gamepadtoasts", (e) => e.GamepadToastPlaceholder],
|
|
334
331
|
["gamepadui", (e) => e.GamepadUIPopupWindowBody],
|
|
335
|
-
["
|
|
332
|
+
["gamepaduisvglibrary", (e) => e.WifiBar1],
|
|
336
333
|
["gamepaduiappoverlay", (e) => e.OverlayPosition],
|
|
337
334
|
["gamepaduiappoverlayvirtualmenucontainer", (e) => e.VirtualMenuContainer],
|
|
338
335
|
["gamerecordingaudiosessionfield", (e) => e.VU],
|
|
336
|
+
["gamerecordingbitrate", (e) => e.BitrateButton],
|
|
339
337
|
["gamerecordingdesktopdialog", (e) => e.GameRecordingDesktopDialog],
|
|
340
338
|
["gamerecordingplayer", (e) => e.GameRecordingPlayer],
|
|
341
|
-
["gamerecordingplayermouselistener", (e) => e.MouseListenerContainer],
|
|
342
339
|
["gamerecordingquickaccess", (e) => e.BackgroundRecordingQuickSettingRow],
|
|
343
340
|
["gamerecordingsettings", (e) => e.RecordingMode],
|
|
344
341
|
["gamerecordingshare", (e) => e.ExportProgressContainer],
|
|
345
342
|
["gamerecordingtour", (e) => e.TourBox],
|
|
343
|
+
["gamerecordingwarning", (e) => e.WarningBox && !e.VU],
|
|
344
|
+
["golivebutton", (e) => e.GoLiveButton],
|
|
346
345
|
["guidedtour", (e) => e.PageIndicator],
|
|
347
346
|
["gyroscopenoisebar", (e) => e.RotateChilden],
|
|
348
347
|
["hardwaresurveydialog", (e) => e.HardwareSurveySections],
|
|
@@ -397,7 +396,9 @@ exportedModules = [
|
|
|
397
396
|
["messages", (e) => e.MsgWithAddons],
|
|
398
397
|
["miniprofile", (e) => e.playerContent],
|
|
399
398
|
["modals", (e) => e.BodyNoScroll],
|
|
399
|
+
["mouselistenercontainer", (e) => e.MouseListenerContainer],
|
|
400
400
|
["moveappsdialog", (e) => e.MoveAppsDialog],
|
|
401
|
+
["multiselectactions", (e) => e.MultiSelectActionDialogContainer],
|
|
401
402
|
["mustupdateclientdialog", (e) => e.MustUpdateClientModalContent],
|
|
402
403
|
["networkconnectiondialog", (e) => e.ConnectionStatus],
|
|
403
404
|
["networkdiagnosticsdialog", (e) => e.ColumnDisplayName],
|
|
@@ -419,7 +420,8 @@ exportedModules = [
|
|
|
419
420
|
["overlaydialogs", (e) => e.Invited],
|
|
420
421
|
["overlayguides", (e) => e.GuidesHomeHeaderDesc],
|
|
421
422
|
["overlaytimeline", (e) => e.KeyboardCapture],
|
|
422
|
-
["overlaytimelinecontainer", (e) => e.OverlayPopup &&
|
|
423
|
+
["overlaytimelinecontainer", (e) => e.OverlayPopup && e.BackgroundRecording],
|
|
424
|
+
["overlaytimelinepreview", (e) => e.ThumbnailHitBoxPadding],
|
|
423
425
|
["overlaytimer", (e) => e.OverlayClock],
|
|
424
426
|
["pageablecontainer", (e) => e.HeaderPageControls],
|
|
425
427
|
["pagedcontent", (e) => e.NavTitle],
|
|
@@ -462,11 +464,11 @@ exportedModules = [
|
|
|
462
464
|
["reactions", (e) => e.ReactorName],
|
|
463
465
|
["recentchatssteamdeck", (e) => e.RecentChatsList],
|
|
464
466
|
["recentlycompleted", (e) => e.RecentlyCompleted],
|
|
465
|
-
["recordingicon", (e) => e.RecordingIconContainer],
|
|
466
467
|
["reloadingdialog", (e) => e.Popup && Object.keys(e).length === 1],
|
|
467
468
|
["remainderlist", (e) => e.ItemWrapper],
|
|
468
469
|
["remoteplay", (e) => e.ContentForm],
|
|
469
470
|
["remoteplaydialog", (e) => e.SegmentedInput && Object.keys(e).length === 3],
|
|
471
|
+
["remoteplayexplainerdialog", (e) => e.AppStoreContainer],
|
|
470
472
|
["remoteplaysettings", (e) => e.SubSetting],
|
|
471
473
|
["removefreeappdialog", (e) => e.RemovingText],
|
|
472
474
|
["removegamehover", (e) => e.RemoveBoxTransition],
|
|
@@ -493,8 +495,9 @@ exportedModules = [
|
|
|
493
495
|
["segmentedinputs", (e) => e.SegmentedCharacterInput && !e.Text],
|
|
494
496
|
["serverbrowserdialog", (e) => e.ServerBrowserDialog],
|
|
495
497
|
["settings", (e) => e.SettingsDialogSubHeader],
|
|
496
|
-
["
|
|
497
|
-
["
|
|
498
|
+
["sharedcommon", (e) => e.v6 && e.AvatarImage],
|
|
499
|
+
["sharedsvggamerecordings", (e) => e.RecordingIconContainer],
|
|
500
|
+
["sharedsvglibrary", (e) => e.SteamDeckCompatLogo],
|
|
498
501
|
["sharedappdetailsheader", (e) => e.BoxSizerDelete],
|
|
499
502
|
["sharedialog", (e) => e.ShareButton && e.ShareIcon],
|
|
500
503
|
["sharescreenshotupload", (e) => e.ShareScreenshotDialog],
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
exportedModules = [
|
|
2
|
+
// shared
|
|
3
|
+
["gamehover", (e) => e.ItemHoverSource],
|
|
4
|
+
["gameslist", (e) => e.Gameslistapp],
|
|
5
|
+
["entry", (e) => e.GamesListItemContainer],
|
|
6
|
+
["search", (e) => e.SearchIcon],
|
|
7
|
+
["rareachievementglow", (e) => e.RareAchievementIconGlowContainerRoot],
|
|
8
|
+
["remote", (e) => e.RemoteDownloadHeader],
|
|
9
|
+
["tabs", (e) => e.TabBaseline],
|
|
10
|
+
];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
exportedModules = [
|
|
2
|
+
["notificationspage", (e) => e.NotificationPageCtn],
|
|
3
|
+
[
|
|
4
|
+
"notification",
|
|
5
|
+
(e) =>
|
|
6
|
+
e.StandardTemplateContainer &&
|
|
7
|
+
!e.AllNotificationsCommentPlus &&
|
|
8
|
+
!e.ShortTemplate,
|
|
9
|
+
],
|
|
10
|
+
// TODO: has DesktopToastTemplate, BottomBar, etc., wtf?
|
|
11
|
+
["notificationcontainer", (e) => e.SteamNotificationWrapper],
|
|
12
|
+
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
exportedModules = [
|
|
2
|
+
["avatar", (e) => e.AvatarDialogUploadArea],
|
|
3
|
+
["avatarcollection", (e) => e.AvatarCollection],
|
|
4
|
+
["avatarcrop", (e) => e.AvatarCrop],
|
|
5
|
+
["body", (e) => e.ProfileEditRoot],
|
|
6
|
+
["favoritebadge", (e) => e.Badge],
|
|
7
|
+
["favoritegroup", (e) => e.FavoriteGroup],
|
|
8
|
+
["friendsnooze", (e) => e.SnoozeContainer],
|
|
9
|
+
["miniprofile", (e) => e.miniProfile],
|
|
10
|
+
["miniprofilepreview", (e) => e.MiniProfilePreview],
|
|
11
|
+
["personastatusicons", (e) => e.PersonaStatusIcon],
|
|
12
|
+
["profilebackground", (e) => e.ProfileBackgroundEquipOption],
|
|
13
|
+
["profileedit", (e) => e.ProfileEditRoot],
|
|
14
|
+
["profilemodifier", (e) => e.ProfileModifierPreview],
|
|
15
|
+
["profilepreview", (e) => e.ProfilePreviewCtn],
|
|
16
|
+
["profiletheme", (e) => e.ProfileThemePicker],
|
|
17
|
+
["profilethemecolors", (e) => e.SteamDeckTheme],
|
|
18
|
+
["shell", (e) => e.ProfileEditStoreLink],
|
|
19
|
+
["steamavatar", (e) => e.avatarFrame],
|
|
20
|
+
["summary", (e) => e.summaryTextArea],
|
|
21
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
exportedModules = [
|
|
2
|
+
["shoppingcart", (e) => e.ShoppingCartModal],
|
|
3
|
+
["shoppingcartcount", (e) => e.ShoppingCartCountCtn],
|
|
4
|
+
["shoppingcartitem", (e) => e.DropDownThin],
|
|
5
|
+
["giftrecipient", (e) => e.GiftRecipientPickerFormCtn],
|
|
6
|
+
["storesalewidget", (e) => e.StoreSalePriceWidget],
|
|
7
|
+
["tradingcart", (e) => e.TradingCardContainer],
|
|
8
|
+
["upsell", (e) => e.CartUpsellArea],
|
|
9
|
+
];
|
|
@@ -9,6 +9,8 @@ function getNormalClass(className) {
|
|
|
9
9
|
|
|
10
10
|
return [key, name].join("_");
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
notInDb.push(className);
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
function normalizeElement(el) {
|
|
@@ -21,21 +23,38 @@ function normalizeElement(el) {
|
|
|
21
23
|
return;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
el.
|
|
26
|
+
el.dataset.readableClass = `\n${readableClasses}\n`;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
function
|
|
29
|
+
function main({ target }) {
|
|
28
30
|
const elements = target.document.querySelectorAll("[class]");
|
|
29
31
|
for (const el of elements) {
|
|
30
32
|
normalizeElement(el);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
if (inClient) {
|
|
36
|
+
for (const popup of popups) {
|
|
37
|
+
popup.removeEventListener("focus", main);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (notInDb.length > 0) {
|
|
42
|
+
const classes = [...new Set(notInDb)];
|
|
43
|
+
console.error(
|
|
44
|
+
"%s classes are not in the classes db: %o",
|
|
45
|
+
classes.length,
|
|
46
|
+
classes.sort(),
|
|
47
|
+
);
|
|
35
48
|
}
|
|
36
49
|
}
|
|
37
50
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
inClient = !!SteamClient.User;
|
|
52
|
+
notInDb = [];
|
|
53
|
+
if (inClient) {
|
|
54
|
+
window.popups = [...g_PopupManager.GetPopups()].map((e) => e.m_popup);
|
|
55
|
+
for (const popup of popups) {
|
|
56
|
+
popup.addEventListener("focus", main);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
main({ target: window });
|
|
41
60
|
}
|
|
@@ -1,26 +1,125 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import cp from "node:child_process";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
3
|
+
import prettier from "prettier";
|
|
4
|
+
import {
|
|
5
|
+
connection,
|
|
6
|
+
config,
|
|
7
|
+
run,
|
|
8
|
+
runCdpFile,
|
|
9
|
+
runWithResult,
|
|
10
|
+
sleep,
|
|
11
|
+
} from "../src/api.js";
|
|
12
|
+
import { createWebConnection, getPageUrl } from "../src/shared.js";
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
/**
|
|
15
|
+
* BrowserView event to listen for on page load.
|
|
16
|
+
*/
|
|
17
|
+
const BROWSER_EVENT = "finished-request";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @type {Record<import("../src/api").Page, string>}
|
|
21
|
+
*/
|
|
22
|
+
const SELECTORS = {
|
|
23
|
+
accountpreferences: "[data-featuretarget]",
|
|
24
|
+
gameslist: "[data-featuretarget='gameslist-root']",
|
|
25
|
+
notificationspage: "#react_root",
|
|
26
|
+
profileedit: "#react_root",
|
|
27
|
+
shoppingcart: "[data-featuretarget='react-root']",
|
|
28
|
+
storeitemscarousel: "[data-featuretarget$='-carousel']",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
async function sleepUntilResult(...args) {
|
|
32
|
+
while (!(await runWithResult(...args))) {
|
|
33
|
+
await sleep(10);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Gets a CDP web connection, accounting for the needed React part to load.
|
|
39
|
+
* @param {import("../src/api").Page | "client"} page
|
|
40
|
+
*/
|
|
41
|
+
async function getWebConn(page) {
|
|
42
|
+
if (page === "client") {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const openedConn = await createWebConnection(page).catch(() => {});
|
|
47
|
+
if (openedConn) {
|
|
48
|
+
return openedConn;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const { url } = await getPageUrl(page);
|
|
52
|
+
await run(`
|
|
53
|
+
function onFinishedRequest() {
|
|
54
|
+
window._finished = true;
|
|
55
|
+
browser.off("${BROWSER_EVENT}", onFinishedRequest);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
browser = SteamClient.BrowserView.Create();
|
|
59
|
+
browser.LoadURL("${url}");
|
|
60
|
+
browser.on("${BROWSER_EVENT}", onFinishedRequest);
|
|
61
|
+
`);
|
|
62
|
+
|
|
63
|
+
console.log("Waiting for page load...");
|
|
64
|
+
await sleepUntilResult("window._finished");
|
|
65
|
+
const conn = await createWebConnection(page).catch((e) => {
|
|
66
|
+
console.log("%s\nNo page whose URL is %o has been found.", e.message, url);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const selector = `${SELECTORS[page]}:not(:empty)`;
|
|
71
|
+
const expression = `!!document.querySelector("${selector}")`;
|
|
72
|
+
console.log("Waiting for %o selector...", selector);
|
|
73
|
+
await sleepUntilResult(expression, conn);
|
|
74
|
+
|
|
75
|
+
return conn;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {import("../src/api").Page} page
|
|
80
|
+
* @param {import("chrome-remote-interface").Client} conn
|
|
81
|
+
*/
|
|
82
|
+
async function doTheThing(page, conn) {
|
|
83
|
+
const webpackRan = await runWithResult("!!webpackCache", conn);
|
|
84
|
+
const forceWebpackRerun = await runWithResult("forceWebpackRerun", conn);
|
|
85
|
+
const preloadFile = path.join("preload", `${page}.js`);
|
|
10
86
|
if (!webpackRan || forceWebpackRerun) {
|
|
11
|
-
await runCdpFile("class_modules_webpack.js");
|
|
87
|
+
await runCdpFile("class_modules_webpack.js", conn);
|
|
88
|
+
}
|
|
89
|
+
if (fs.existsSync(preloadFile)) {
|
|
90
|
+
await runCdpFile(preloadFile, conn);
|
|
12
91
|
}
|
|
13
|
-
await runCdpFile("
|
|
92
|
+
await runCdpFile(path.join("db", `${page}.js`), conn);
|
|
93
|
+
|
|
94
|
+
const dirPath = path.join(process.cwd(), config.classMaps);
|
|
95
|
+
const filePath = path.join(dirPath, `${page}.json`);
|
|
14
96
|
|
|
15
|
-
const
|
|
16
|
-
// Sleep for a bit to not error on first launch.
|
|
17
|
-
await sleep(10);
|
|
18
|
-
const output = await runCdpFile("class_modules.js");
|
|
97
|
+
const output = await runCdpFile("class_modules.js", conn);
|
|
19
98
|
const [classModules, allModules] = await runWithResult(
|
|
20
99
|
"[Object.keys(classModules).length, allModules.length]",
|
|
100
|
+
conn,
|
|
21
101
|
);
|
|
22
102
|
|
|
23
|
-
|
|
24
|
-
|
|
103
|
+
const content = await prettier.format(JSON.stringify(output), {
|
|
104
|
+
parser: "json-stringify",
|
|
105
|
+
});
|
|
106
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
107
|
+
fs.writeFileSync(filePath, content);
|
|
25
108
|
console.log("Wrote %s/%s modules to %o", classModules, allModules, filePath);
|
|
26
109
|
}
|
|
110
|
+
|
|
111
|
+
export async function execute(page = "client") {
|
|
112
|
+
const isClient = page === "client";
|
|
113
|
+
const webConn = await getWebConn(page);
|
|
114
|
+
|
|
115
|
+
const conn = isClient ? connection : webConn;
|
|
116
|
+
await doTheThing(page, conn);
|
|
117
|
+
|
|
118
|
+
webConn?.close();
|
|
119
|
+
if (!isClient) {
|
|
120
|
+
await run(`
|
|
121
|
+
window._finished = false;
|
|
122
|
+
SteamClient.BrowserView.Destroy(browser);
|
|
123
|
+
`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import { readScript, runCdpFile } from "../src/api.js";
|
|
1
|
+
import { connection, readScript, runCdpFile } from "../src/api.js";
|
|
2
|
+
import { createWebConnection } from "../src/shared.js";
|
|
3
|
+
|
|
4
|
+
export async function execute(page = "client") {
|
|
5
|
+
const isClient = page === "client";
|
|
6
|
+
const webConn = isClient ? null : await createWebConnection(page);
|
|
7
|
+
const conn = isClient ? connection : webConn;
|
|
2
8
|
|
|
3
|
-
export async function execute() {
|
|
4
9
|
const script = await readScript("build_class_modules");
|
|
5
|
-
await script.execute();
|
|
10
|
+
await script.execute(page);
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
if (isClient) {
|
|
13
|
+
console.log("Waiting for focus...");
|
|
14
|
+
}
|
|
15
|
+
await runCdpFile("make_readable_classes.js", conn);
|
|
9
16
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import postcss from "postcss";
|
|
3
|
-
import {
|
|
3
|
+
import { config } from "../src/api.js";
|
|
4
4
|
import { readFile, selectorReplacerPlugin } from "../src/shared.js";
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const CLASS_MAP_FILE = path.join(config.classMaps, "client.json");
|
|
7
|
+
const OLD_CLASS_MAP_FILE = path.join(config.classMaps, "client_old.json");
|
|
8
|
+
const SELECTOR = /\.([\w-]+)/g;
|
|
7
9
|
|
|
8
10
|
if ([CLASS_MAP_FILE, OLD_CLASS_MAP_FILE].some((e) => !fs.existsSync(e))) {
|
|
9
11
|
console.log("Usage:");
|
|
@@ -43,24 +45,22 @@ function findNewClassFromOld(oldName) {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export async function execute() {
|
|
46
|
-
const
|
|
48
|
+
const files = fs
|
|
47
49
|
.readdirSync(cwd, { recursive: true })
|
|
48
|
-
.filter((e) => e.endsWith(".css"))
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
replace: (_, s) => findNewClassFromOld(s),
|
|
55
|
-
}),
|
|
56
|
-
]).process(readFile(e), {
|
|
57
|
-
from: e,
|
|
50
|
+
.filter((e) => e.endsWith(".css"));
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
postcss([
|
|
53
|
+
selectorReplacerPlugin({
|
|
54
|
+
match: SELECTOR,
|
|
55
|
+
replace: (_, s) => findNewClassFromOld(s),
|
|
58
56
|
}),
|
|
59
|
-
])
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
])
|
|
58
|
+
.process(readFile(file), {
|
|
59
|
+
from: file,
|
|
60
|
+
})
|
|
61
|
+
.then(({ css }) => {
|
|
62
|
+
fs.writeFileSync(file, css);
|
|
63
|
+
console.log("[%s] done", file);
|
|
64
|
+
});
|
|
65
65
|
}
|
|
66
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "steam-theming-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "A collection of scripts for easier Steam theming",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
},
|
|
13
13
|
"types": "./src/api.d.ts",
|
|
14
14
|
"exports": {
|
|
15
|
-
".": "./src/api.js"
|
|
15
|
+
".": "./src/api.js",
|
|
16
|
+
"./postcss-plugin": "./src/postcss_plugin.js"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
18
|
-
"@biomejs/biome": "^1.9.0",
|
|
19
19
|
"chrome-remote-interface": "^0.33.2",
|
|
20
|
-
"
|
|
21
|
-
"postcss
|
|
20
|
+
"lilconfig": "^3.1.2",
|
|
21
|
+
"postcss": "^8.5.1",
|
|
22
|
+
"prettier": "^3.4.1"
|
|
22
23
|
}
|
|
23
24
|
}
|
package/src/api.d.ts
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import type CDP from "chrome-remote-interface";
|
|
1
2
|
import type Protocol from "devtools-protocol";
|
|
2
3
|
|
|
4
|
+
export interface Config {
|
|
5
|
+
/**
|
|
6
|
+
* Path of built class maps.
|
|
7
|
+
*/
|
|
8
|
+
classMaps: string;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Directories for the postcss plugin to ignore.
|
|
12
|
+
*
|
|
13
|
+
* For example: `["client/shared", "web/vars"]` will ignore
|
|
14
|
+
* `src/client/shared` and `src/web/vars`, assuming the base dir is `src`.
|
|
15
|
+
*/
|
|
16
|
+
ignore: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
3
19
|
interface Script {
|
|
4
|
-
execute(): Promise<void>;
|
|
20
|
+
execute(arg?: string): Promise<void>;
|
|
5
21
|
}
|
|
6
22
|
|
|
7
23
|
/**
|
|
@@ -9,10 +25,22 @@ interface Script {
|
|
|
9
25
|
*/
|
|
10
26
|
type ScriptFile =
|
|
11
27
|
| "build_class_modules"
|
|
12
|
-
| "build_theme"
|
|
13
28
|
| "make_readable_classes"
|
|
14
29
|
| "replace_old_classes";
|
|
15
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Pages that have an existing class map, excluding `client`.
|
|
33
|
+
*/
|
|
34
|
+
export type Page =
|
|
35
|
+
| "accountpreferences"
|
|
36
|
+
| "apppage"
|
|
37
|
+
| "gameslist"
|
|
38
|
+
| "notificationspage"
|
|
39
|
+
| "profileedit"
|
|
40
|
+
| "shoppingcart";
|
|
41
|
+
|
|
42
|
+
export declare const config: Config;
|
|
43
|
+
|
|
16
44
|
/**
|
|
17
45
|
* @param file The script to read.
|
|
18
46
|
*/
|
|
@@ -23,6 +51,7 @@ export function readScript(name: ScriptFile): Promise<Script>;
|
|
|
23
51
|
*/
|
|
24
52
|
export function run(
|
|
25
53
|
expression: string,
|
|
54
|
+
conn?: CDP.Client,
|
|
26
55
|
): Promise<Protocol.Runtime.EvaluateResponse>;
|
|
27
56
|
|
|
28
57
|
/**
|
|
@@ -30,6 +59,7 @@ export function run(
|
|
|
30
59
|
*/
|
|
31
60
|
export function runCdpFile(
|
|
32
61
|
file: ScriptFile,
|
|
62
|
+
conn?: CDP.Client,
|
|
33
63
|
): Promise<Protocol.Runtime.EvaluateResponse>;
|
|
34
64
|
|
|
35
65
|
/**
|
|
@@ -38,6 +68,9 @@ export function runCdpFile(
|
|
|
38
68
|
*
|
|
39
69
|
* @param expression JS to run.
|
|
40
70
|
*/
|
|
41
|
-
export function runWithResult(
|
|
71
|
+
export function runWithResult(
|
|
72
|
+
expression: string,
|
|
73
|
+
conn?: CDP.Client,
|
|
74
|
+
): Promise<any>;
|
|
42
75
|
|
|
43
76
|
export function sleep(ms: number): Promise<void>;
|
package/src/api.js
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
import cdp from "chrome-remote-interface";
|
|
2
1
|
import path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { lilconfig } from "lilconfig";
|
|
3
|
+
import { CDP_FILES_PATH, DEFAULT_CONFIG, SCRIPT_PATH } from "./constants.js";
|
|
4
|
+
import { createConnection, readFile } from "./shared.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
});
|
|
6
|
+
// postcss-cli hangs because of cdp
|
|
7
|
+
export const connection =
|
|
8
|
+
path.basename(process.argv[1]) !== "postcss" &&
|
|
9
|
+
(await createConnection((e) =>
|
|
10
|
+
e.find((e) => e.title === "SharedJSContext"),
|
|
11
|
+
).catch((e) => {
|
|
12
|
+
console.log(
|
|
13
|
+
"%s\nTry running Steam with %o",
|
|
14
|
+
e.message,
|
|
15
|
+
"-cef-enable-debugging",
|
|
16
|
+
);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}));
|
|
19
19
|
|
|
20
|
-
export const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
export const config = Object.assign(
|
|
21
|
+
DEFAULT_CONFIG,
|
|
22
|
+
(await lilconfig("steam-theming-utils").search())?.config || {},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const readScript = (name) =>
|
|
26
|
+
import(`file://${path.join(SCRIPT_PATH, `${name}.js`)}`);
|
|
27
|
+
|
|
28
|
+
export const run = (expression, conn = connection) =>
|
|
29
|
+
conn.Runtime.evaluate({
|
|
24
30
|
expression,
|
|
25
31
|
awaitPromise: true,
|
|
26
32
|
returnByValue: true,
|
|
27
33
|
});
|
|
28
|
-
export const runCdpFile =
|
|
29
|
-
|
|
30
|
-
export const runWithResult = async (expression) =>
|
|
31
|
-
(await run(expression)).result.value;
|
|
34
|
+
export const runCdpFile = (file, conn = connection) =>
|
|
35
|
+
runWithResult(readFile(path.join(CDP_FILES_PATH, file)), conn);
|
|
36
|
+
export const runWithResult = async (expression, conn = connection) =>
|
|
37
|
+
(await run(expression, conn)).result.value;
|
|
32
38
|
|
|
33
39
|
export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
package/src/constants.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
const packagePath = path
|
|
5
|
-
.dirname(fileURLToPath(import.meta.url))
|
|
6
|
-
.split(path.sep)
|
|
7
|
-
.slice(0, -1)
|
|
8
|
-
.join(path.sep);
|
|
2
|
+
import { packagePath } from "./shared.js";
|
|
9
3
|
|
|
10
4
|
export const CDP_FILES_PATH = path.join(packagePath, "cdp");
|
|
11
|
-
export const CLASS_MAP_FILE = "class_map.json";
|
|
12
5
|
export const SCRIPT_PATH = path.join(packagePath, "lib");
|
|
6
|
+
|
|
7
|
+
/** @type {import("./api").Config} */
|
|
8
|
+
export const DEFAULT_CONFIG = {
|
|
9
|
+
classMaps: "class_maps",
|
|
10
|
+
ignore: [],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const STORE_BASE_URL = "https://store.steampowered.com";
|
package/src/index.js
CHANGED
|
@@ -13,15 +13,6 @@ if (!files.some((e) => process.argv[2] === e)) {
|
|
|
13
13
|
process.exit(2);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
await connection.Runtime.enable();
|
|
17
|
-
connection.Runtime.on("consoleAPICalled", (ev) => {
|
|
18
|
-
if (ev.type !== "error") {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
console.error(...ev.args.map((e) => e.description || e.value));
|
|
23
|
-
});
|
|
24
|
-
|
|
25
16
|
const script = await readScript(process.argv[2]);
|
|
26
|
-
await script.execute();
|
|
17
|
+
await script.execute(process.argv[3]);
|
|
27
18
|
connection.close();
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
// Not a dependency because postcss already depends on it.
|
|
4
|
+
import yargs from "yargs";
|
|
5
|
+
import { config } from "./api.js";
|
|
6
|
+
|
|
7
|
+
const PAGES = [
|
|
8
|
+
"accountpreferences",
|
|
9
|
+
"apppage",
|
|
10
|
+
"client",
|
|
11
|
+
"gameslist",
|
|
12
|
+
"notificationspage",
|
|
13
|
+
"profileedit",
|
|
14
|
+
"shoppingcart",
|
|
15
|
+
];
|
|
16
|
+
const SELECTOR = /#(\w+)/g;
|
|
17
|
+
|
|
18
|
+
const { argv } = yargs(process.argv);
|
|
19
|
+
const cwd = process.cwd();
|
|
20
|
+
const classMap = {};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Gets a class map on demand rather than reading all files at once.
|
|
24
|
+
* @param {string} page
|
|
25
|
+
*/
|
|
26
|
+
function getClassMap(page) {
|
|
27
|
+
if (classMap[page]) {
|
|
28
|
+
return classMap[page];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const pagePath = path.join(config.classMaps, `${page}.json`);
|
|
32
|
+
if (!fs.existsSync(pagePath)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
classMap[page] = JSON.parse(fs.readFileSync(pagePath));
|
|
37
|
+
return classMap[page];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const selectorReplacerPlugin = () => (css) => {
|
|
41
|
+
const { file } = css.source.input;
|
|
42
|
+
const name = path.basename(file, ".scss");
|
|
43
|
+
|
|
44
|
+
const splitPath = file.split(path.sep);
|
|
45
|
+
const page = PAGES.find((e) => splitPath.includes(e));
|
|
46
|
+
if (!page) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const map = getClassMap(page);
|
|
51
|
+
if (!map) {
|
|
52
|
+
console.error("[%s] no such map", page);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const mod = map?.[name];
|
|
57
|
+
const ignoredPaths = config.ignore || [];
|
|
58
|
+
const src = path.join(cwd, argv.base);
|
|
59
|
+
const skipFile = ignoredPaths.some((e) => file.startsWith(path.join(src, e)));
|
|
60
|
+
if (!mod && !skipFile) {
|
|
61
|
+
console.error("[%s] no such module", name);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
css.walkRules((rule) => {
|
|
66
|
+
rule.selector = rule.selector.replace(SELECTOR, (_, s) => {
|
|
67
|
+
const id = mod[s];
|
|
68
|
+
if (!id) {
|
|
69
|
+
console.error("[%s] %o is undefined", name, `#${s}`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return `.${id}`;
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
selectorReplacerPlugin.postcss = true;
|
package/src/shared.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import cdp from "chrome-remote-interface";
|
|
5
|
+
import { runWithResult } from "./api.js";
|
|
6
|
+
import { STORE_BASE_URL } from "./constants.js";
|
|
7
|
+
|
|
8
|
+
export const packagePath = path
|
|
9
|
+
.dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
.split(path.sep)
|
|
11
|
+
.slice(0, -1)
|
|
12
|
+
.join(path.sep);
|
|
2
13
|
|
|
3
14
|
export const readFile = (file) => fs.readFileSync(file).toString();
|
|
4
15
|
|
|
@@ -8,3 +19,84 @@ export const selectorReplacerPlugin = (opts) => (css) => {
|
|
|
8
19
|
});
|
|
9
20
|
};
|
|
10
21
|
selectorReplacerPlugin.postcss = true;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {object} SteamPage
|
|
25
|
+
*
|
|
26
|
+
* @property {string} url
|
|
27
|
+
* The URL to open if not open already.
|
|
28
|
+
* @property {RegExp} match
|
|
29
|
+
* URL regex to match if there is an open page.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Gets a page URL for a given page name.
|
|
34
|
+
* @param {import("./api").Page} page
|
|
35
|
+
* @returns {Promise<SteamPage>}
|
|
36
|
+
*/
|
|
37
|
+
export async function getPageUrl(page) {
|
|
38
|
+
const resolve = (name) => runWithResult(`urlStore.ResolveURL("${name}")`);
|
|
39
|
+
/** @returns {SteamPage} */
|
|
40
|
+
const pageObj = (url) => ({
|
|
41
|
+
url,
|
|
42
|
+
match: new RegExp(`^${url.replace(/\/+$/, "")}`),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const profileUrl = await resolve("SteamIDMyProfile");
|
|
46
|
+
switch (page) {
|
|
47
|
+
case "accountpreferences":
|
|
48
|
+
return {
|
|
49
|
+
url: await resolve("FamilyManagement"),
|
|
50
|
+
match: new RegExp(`^${STORE_BASE_URL}/account`),
|
|
51
|
+
};
|
|
52
|
+
case "apppage":
|
|
53
|
+
return {
|
|
54
|
+
url: `${STORE_BASE_URL}/app/666220`,
|
|
55
|
+
match: new RegExp(`^${STORE_BASE_URL}/app/\\d+`),
|
|
56
|
+
};
|
|
57
|
+
case "gameslist":
|
|
58
|
+
return pageObj(`${profileUrl}games`);
|
|
59
|
+
case "notificationspage":
|
|
60
|
+
return pageObj(`${profileUrl}notifications`);
|
|
61
|
+
case "profileedit":
|
|
62
|
+
return pageObj(await resolve("SteamIDEditPage"));
|
|
63
|
+
case "shoppingcart":
|
|
64
|
+
return pageObj(await resolve("StoreCart"));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Creates a CDP connection for a given target.
|
|
70
|
+
* @param {(targets: cdp.Target[]) => cdp.Target} target
|
|
71
|
+
*/
|
|
72
|
+
export async function createConnection(target) {
|
|
73
|
+
const connection = await cdp({
|
|
74
|
+
host: "127.0.0.1",
|
|
75
|
+
port: 8080,
|
|
76
|
+
target,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await connection.Runtime.enable();
|
|
80
|
+
connection.Runtime.on("consoleAPICalled", (ev) => {
|
|
81
|
+
if (ev.type !== "error") {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.error(...ev.args.map((e) => e.description || e.value));
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return connection;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Creates a CDP connection for a given page name.
|
|
93
|
+
* @param {import("./api").Page} page
|
|
94
|
+
*/
|
|
95
|
+
export async function createWebConnection(page) {
|
|
96
|
+
const { match } = await getPageUrl(page);
|
|
97
|
+
const connection = await createConnection((e) =>
|
|
98
|
+
e.find((e) => e.url.match(match)),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return connection;
|
|
102
|
+
}
|
package/lib/build_theme.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import postcss from "postcss";
|
|
4
|
-
import postcssrc from "postcss-load-config";
|
|
5
|
-
import { readScript } from "../src/api.js";
|
|
6
|
-
import { CLASS_MAP_FILE } from "../src/constants.js";
|
|
7
|
-
import { readFile, selectorReplacerPlugin } from "../src/shared.js";
|
|
8
|
-
|
|
9
|
-
const cwd = process.cwd();
|
|
10
|
-
const DIST_DIR = path.join(cwd, "dist");
|
|
11
|
-
const EXTENSION = ".css";
|
|
12
|
-
const SRC_DIR = path.join(cwd, "src");
|
|
13
|
-
|
|
14
|
-
export async function execute() {
|
|
15
|
-
if (!fs.existsSync(path.join(cwd, CLASS_MAP_FILE))) {
|
|
16
|
-
const script = await readScript("build_class_modules");
|
|
17
|
-
await script.execute();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const classes = JSON.parse(fs.readFileSync(CLASS_MAP_FILE));
|
|
21
|
-
// TODO:
|
|
22
|
-
// Can't use SASS here - screams at me with "Please check the validity
|
|
23
|
-
// of the block starting from line #1" with a completely valid SCSS syntax.
|
|
24
|
-
const { plugins, options } = await postcssrc();
|
|
25
|
-
const parsedCss = fs
|
|
26
|
-
.readdirSync(SRC_DIR, { recursive: true })
|
|
27
|
-
.filter((e) => e.endsWith(EXTENSION))
|
|
28
|
-
.map((e) => [e, path.basename(e, EXTENSION)])
|
|
29
|
-
.map(([e, name]) => [
|
|
30
|
-
e,
|
|
31
|
-
postcss([
|
|
32
|
-
selectorReplacerPlugin({
|
|
33
|
-
match: /#(\w+)/g,
|
|
34
|
-
replace: (_, s) => {
|
|
35
|
-
const mod = classes[name];
|
|
36
|
-
if (!mod) {
|
|
37
|
-
console.log("[%s] no such module", name);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const id = mod[s];
|
|
42
|
-
if (!id) {
|
|
43
|
-
console.log("[%s] %o is undefined", name, `#${s}`);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return `.${id} /* ${s} */`;
|
|
48
|
-
},
|
|
49
|
-
}),
|
|
50
|
-
...plugins,
|
|
51
|
-
]).process(readFile(path.join(SRC_DIR, e)), {
|
|
52
|
-
...options,
|
|
53
|
-
from: path.join(SRC_DIR, e),
|
|
54
|
-
to: path.join(DIST_DIR, e),
|
|
55
|
-
}),
|
|
56
|
-
]);
|
|
57
|
-
|
|
58
|
-
fs.rmSync(DIST_DIR, { force: true, recursive: true });
|
|
59
|
-
for (const [file, result] of parsedCss) {
|
|
60
|
-
const { css, map } = await result;
|
|
61
|
-
const distFile = path.join(DIST_DIR, file);
|
|
62
|
-
|
|
63
|
-
fs.mkdirSync(path.dirname(distFile), { recursive: true });
|
|
64
|
-
fs.writeFileSync(distFile, css);
|
|
65
|
-
if (map) {
|
|
66
|
-
fs.writeFileSync(`${distFile}.map`, map.toString());
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|