@steambrew/ttc 2.8.7 → 3.0.1

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/src/plugin-api.ts CHANGED
@@ -1,263 +1,77 @@
1
- declare global {
2
- interface Window {
3
- /**
4
- * @description The plugin list is a global object that contains all the plugins loaded in the client.
5
- * It is used to store the pluginSelf object
6
- */
7
- PLUGIN_LIST: any;
8
- /**
9
- * @description The plugin settings store is a global object that contains all the plugins settings.
10
- * It is used to store the plugin settings and the plugin settings parser.
11
- */
12
- MILLENNIUM_PLUGIN_SETTINGS_STORE: any;
13
- /**
14
- * @description Used to store the settings for the plugin.
15
- * Millennium then renders these in the settings panel.
16
- */
17
- MILLENNIUM_SIDEBAR_NAVIGATION_PANELS: any;
18
- }
19
- }
20
-
21
- interface SettingsProps {
22
- options?: any[];
23
- range?: number[];
24
- type: string;
25
- value: any;
26
- }
27
-
28
- /**
29
- * the pluginName is the name of the plugin.
30
- * It is used to identify the plugin in the plugin list and settings store.
31
- */
32
- declare const pluginName: string;
33
- /**
34
- * PluginEntryPointMain is the default function returned by rolling up the plugin.
35
- * It is the main entry point for the plugin. It's IIFE has been removed, therefore it only runs once its manually called.
36
- * This is done to prevent the plugin from running before the settings have been parsed.
37
- */
38
- declare const PluginEntryPointMain: any;
39
- /**
40
- * The underlying IPC object used to communicate with the backend.
41
- * Its defined within the plugin utils package under the client module.
42
- */
43
- declare const MILLENNIUM_BACKEND_IPC: any;
44
- /**
45
- * Since ExecutePluginModule is called from both the webkit and the client module,
46
- * this flag is used to determine if the plugin is running in the client module or not.
47
- */
48
- declare const MILLENNIUM_IS_CLIENT_MODULE: boolean;
49
- /**
50
- * A reference to the main Web Browser within the client.
51
- * This is used to send messages to the main window browser manager.
52
- */
53
- declare const MainWindowBrowserManager: any;
54
- /**
55
- * Steam Client API object
56
- */
57
- declare const SteamClient: any;
58
-
59
- /**
60
- * @description Append the active plugin to the global plugin
61
- * list and notify that the frontend Loaded.
62
- */
63
- function ExecutePluginModule() {
64
- let MillenniumStore = window.MILLENNIUM_PLUGIN_SETTINGS_STORE[pluginName];
65
-
66
- function OnPluginConfigChange(key: any, __: string, value: any) {
67
- if (key in MillenniumStore.settingsStore) {
68
- MillenniumStore.ignoreProxyFlag = true;
69
- MillenniumStore.settingsStore[key] = value;
70
- MillenniumStore.ignoreProxyFlag = false;
71
- }
72
- }
73
-
74
- /** Expose the OnPluginConfigChange so it can be called externally */
75
- MillenniumStore.OnPluginConfigChange = OnPluginConfigChange;
76
-
77
- MILLENNIUM_BACKEND_IPC.postMessage(0, { pluginName: pluginName, methodName: '__builtins__.__millennium_plugin_settings_parser__' }).then(async (response: any) => {
78
- /**
79
- * __millennium_plugin_settings_parser__ will return false if the plugin has no settings.
80
- * If the plugin has settings, it will return a base64 encoded string.
81
- * The string is then decoded and parsed into an object.
82
- */
83
- if (typeof response.returnValue === 'string') {
84
- MillenniumStore.ignoreProxyFlag = true;
85
-
86
- /** Initialize the settings store from the settings returned from the backend. */
87
- MillenniumStore.settingsStore = MillenniumStore.DefinePluginSetting(
88
- Object.fromEntries(JSON.parse(atob(response.returnValue)).map((item: any) => [item.functionName, item])),
89
- );
90
-
91
- MillenniumStore.ignoreProxyFlag = false;
92
- }
93
-
94
- /** @ts-ignore: call the plugin main after the settings have been parsed. This prevent plugin settings from being undefined at top level. */
95
- let PluginModule = PluginEntryPointMain();
96
-
97
- /** Assign the plugin on plugin list. */
98
- Object.assign(window.PLUGIN_LIST[pluginName], {
99
- ...PluginModule,
100
- __millennium_internal_plugin_name_do_not_use_or_change__: pluginName,
101
- });
102
-
103
- /** Run the rolled up plugins default exported function */
104
- let pluginProps = await PluginModule.default();
105
-
106
- function isValidSidebarNavComponent(obj: any) {
107
- return obj && obj.title !== undefined && obj.icon !== undefined && obj.content !== undefined;
108
- }
109
-
110
- if (pluginProps && isValidSidebarNavComponent(pluginProps)) {
111
- window.MILLENNIUM_SIDEBAR_NAVIGATION_PANELS[pluginName] = pluginProps;
112
- } else {
113
- console.warn(
114
- `Plugin ${pluginName} does not contain proper SidebarNavigation props and therefor can't be mounted by Millennium. Please ensure it has a title, icon, and content.`,
115
- );
116
- return;
117
- }
118
-
119
- /** If the current module is a client module, post message id=1 which calls the front_end_loaded method on the backend. */
120
- if (MILLENNIUM_IS_CLIENT_MODULE) {
121
- MILLENNIUM_BACKEND_IPC.postMessage(1, { pluginName: pluginName });
122
- }
123
- });
124
- }
125
-
126
- /**
127
- * @description Initialize the plugins settings store and the plugin list.
128
- * This function is called once per plugin and is used to store the plugin settings and the plugin list.
129
- */
130
- function InitializePlugins() {
131
- /**
132
- * This function is called n times depending on n plugin count,
133
- * Create the plugin list if it wasn't already created
134
- */
135
- (window.PLUGIN_LIST ||= {})[pluginName] ||= {};
136
- (window.MILLENNIUM_PLUGIN_SETTINGS_STORE ||= {})[pluginName] ||= {};
137
- window.MILLENNIUM_SIDEBAR_NAVIGATION_PANELS ||= {};
138
-
139
- /**
140
- * Accepted IPC message types from Millennium backend.
141
- */
142
- enum IPCType {
143
- CallServerMethod,
144
- }
145
-
146
- let MillenniumStore = window.MILLENNIUM_PLUGIN_SETTINGS_STORE[pluginName];
147
- let IPCMessageId = `Millennium.Internal.IPC.[${pluginName}]`;
148
- let isClientModule = MILLENNIUM_IS_CLIENT_MODULE;
149
-
150
- const ComponentTypeMap = {
151
- DropDown: ['string', 'number', 'boolean'],
152
- NumberTextInput: ['number'],
153
- StringTextInput: ['string'],
154
- FloatTextInput: ['number'],
155
- CheckBox: ['boolean'],
156
- NumberSlider: ['number'],
157
- FloatSlider: ['number'],
158
- };
159
-
160
- MillenniumStore.ignoreProxyFlag = false;
161
-
162
- function DelegateToBackend(pluginName: string, name: string, value: any) {
163
- return MILLENNIUM_BACKEND_IPC.postMessage(IPCType.CallServerMethod, {
164
- pluginName,
165
- methodName: '__builtins__.__update_settings_value__',
166
- argumentList: { name, value },
167
- });
168
- }
169
-
170
- async function ClientInitializeIPC() {
171
- /** Wait for the MainWindowBrowser to not be undefined */
172
- while (typeof MainWindowBrowserManager === 'undefined') {
173
- await new Promise((resolve) => setTimeout(resolve, 0));
174
- }
175
-
176
- MainWindowBrowserManager?.m_browser?.on('message', (messageId: string, data: string) => {
177
- if (messageId !== IPCMessageId) {
178
- return;
179
- }
180
-
181
- const { name, value } = JSON.parse(data);
182
-
183
- MillenniumStore.ignoreProxyFlag = true;
184
- MillenniumStore.settingsStore[name] = value;
185
-
186
- DelegateToBackend(pluginName, name, value);
187
- MillenniumStore.ignoreProxyFlag = false;
188
- });
189
- }
190
-
191
- if (isClientModule) {
192
- ClientInitializeIPC();
193
- }
194
-
195
- const StartSettingPropagation = (name: string, value: any) => {
196
- if (MillenniumStore.ignoreProxyFlag) {
197
- return;
198
- }
199
-
200
- if (isClientModule) {
201
- DelegateToBackend(pluginName, name, value);
202
-
203
- /** If the browser doesn't exist yet, no use sending anything to it. */
204
- if (typeof MainWindowBrowserManager !== 'undefined') {
205
- MainWindowBrowserManager?.m_browser?.PostMessage(IPCMessageId, JSON.stringify({ name, value }));
206
- }
207
- } else {
208
- /** Send the message to the SharedJSContext */
209
- SteamClient.BrowserView.PostMessageToParent(IPCMessageId, JSON.stringify({ name, value }));
210
- }
211
- };
212
-
213
- function clamp(value: number, min: number, max: number) {
214
- return Math.max(min, Math.min(max, value));
215
- }
216
-
217
- const DefinePluginSetting = <T extends Record<string, SettingsProps>>(obj: T) => {
218
- return new Proxy(obj, {
219
- set(target, property, value) {
220
- if (!(property in target)) {
221
- throw new TypeError(`Property ${String(property)} does not exist on plugin settings`);
222
- }
223
-
224
- const settingType = ComponentTypeMap[target[property as keyof T].type as keyof typeof ComponentTypeMap];
225
- const range = target[property as keyof T]?.range;
226
-
227
- /** Clamp the value between the given range */
228
- if (settingType.includes('number') && typeof value === 'number') {
229
- if (range) {
230
- value = clamp(value, range[0], range[1]);
231
- }
232
-
233
- value ||= 0; // Fallback to 0 if the value is undefined or null
234
- }
235
-
236
- /** Check if the value is of the proper type */
237
- if (!settingType.includes(typeof value)) {
238
- throw new TypeError(`Expected ${settingType.join(' or ')}, got ${typeof value}`);
239
- }
240
-
241
- target[property as keyof T].value = value;
242
- StartSettingPropagation(String(property), value);
243
- return true;
244
- },
245
- get(target, property) {
246
- if (property === '__raw_get_internals__') {
247
- return target;
248
- }
249
-
250
- if (property in target) {
251
- return target[property as keyof T].value;
252
- }
253
- return undefined;
254
- },
255
- });
256
- };
257
-
258
- MillenniumStore.DefinePluginSetting = DefinePluginSetting;
259
- MillenniumStore.settingsStore = DefinePluginSetting({});
260
- }
261
-
262
- export { ExecutePluginModule, InitializePlugins };
263
-
1
+ declare global {
2
+ interface Window {
3
+ /**
4
+ * @description The plugin list is a global object that contains all the plugins loaded in the client.
5
+ * It is used to store the pluginSelf object
6
+ */
7
+ PLUGIN_LIST: any;
8
+ /**
9
+ * @description Used to store the settings for the plugin.
10
+ * Millennium then renders these in the settings panel.
11
+ */
12
+ MILLENNIUM_SIDEBAR_NAVIGATION_PANELS: any;
13
+ }
14
+ }
15
+
16
+ /**
17
+ * the pluginName is the name of the plugin.
18
+ * It is used to identify the plugin in the plugin list and settings store.
19
+ */
20
+ declare const pluginName: string;
21
+ /**
22
+ * PluginEntryPointMain is the default function returned by rolling up the plugin.
23
+ * It is the main entry point for the plugin. It's IIFE has been removed, therefore it only runs once its manually called.
24
+ * This is done to prevent the plugin from running before the settings have been parsed.
25
+ */
26
+ declare const PluginEntryPointMain: any;
27
+ /**
28
+ * The underlying IPC object used to communicate with the backend.
29
+ * Its defined within the plugin utils package under the client module.
30
+ */
31
+ declare const MILLENNIUM_BACKEND_IPC: any;
32
+ /**
33
+ * Since ExecutePluginModule is called from both the webkit and the client module,
34
+ * this flag is used to determine if the plugin is running in the client module or not.
35
+ */
36
+ declare const MILLENNIUM_IS_CLIENT_MODULE: boolean;
37
+
38
+ /**
39
+ * @description Append the active plugin to the global plugin
40
+ * list and notify that the frontend Loaded.
41
+ */
42
+ async function ExecutePluginModule() {
43
+ let PluginModule = PluginEntryPointMain();
44
+
45
+ /** Assign the plugin on plugin list. */
46
+ Object.assign(window.PLUGIN_LIST[pluginName], {
47
+ ...PluginModule,
48
+ __millennium_internal_plugin_name_do_not_use_or_change__: pluginName,
49
+ });
50
+
51
+ /** Run the rolled up plugins default exported function */
52
+ let pluginProps = await PluginModule.default();
53
+
54
+ function isValidSidebarNavComponent(obj: any) {
55
+ return obj && obj.title !== undefined && obj.icon !== undefined && obj.content !== undefined;
56
+ }
57
+
58
+ if (pluginProps && isValidSidebarNavComponent(pluginProps)) {
59
+ window.MILLENNIUM_SIDEBAR_NAVIGATION_PANELS[pluginName] = pluginProps;
60
+ }
61
+
62
+ /** If the current module is a client module, post message id=1 which calls the front_end_loaded method on the backend. */
63
+ if (MILLENNIUM_IS_CLIENT_MODULE) {
64
+ MILLENNIUM_BACKEND_IPC.postMessage(1, { pluginName: pluginName });
65
+ }
66
+ }
67
+
68
+ /**
69
+ * @description Initialize the plugins settings store and the plugin list.
70
+ * This function is called once per plugin and is used to store the plugin settings and the plugin list.
71
+ */
72
+ function InitializePlugins() {
73
+ (window.PLUGIN_LIST ||= {})[pluginName] ||= {};
74
+ window.MILLENNIUM_SIDEBAR_NAVIGATION_PANELS ||= {};
75
+ }
76
+
77
+ export { ExecutePluginModule, InitializePlugins };
@@ -1,89 +1,83 @@
1
- import chalk from 'chalk';
2
- import { Logger } from './logger';
3
-
4
- /***
5
- * @brief print the parameter list to the stdout
6
- */
7
- export const PrintParamHelp = () => {
8
- console.log(
9
- 'millennium-ttc parameter list:' +
10
- '\n\t' +
11
- chalk.magenta('--help') +
12
- ': display parameter list' +
13
- '\n\t' +
14
- chalk.bold.red('--build') +
15
- ': ' +
16
- chalk.bold.red('(required)') +
17
- ': build type [dev, prod] (prod minifies code)' +
18
- '\n\t' +
19
- chalk.magenta('--target') +
20
- ': path to plugin, default to cwd',
21
- );
22
- };
23
-
24
- export enum BuildType {
25
- DevBuild,
26
- ProdBuild,
27
- }
28
-
29
- export interface ParameterProps {
30
- type: BuildType;
31
- targetPlugin: string; // path
32
- isMillennium?: boolean;
33
- }
34
-
35
- export const ValidateParameters = (args: Array<string>): ParameterProps => {
36
- let typeProp: BuildType = BuildType.DevBuild,
37
- targetProp: string = process.cwd(),
38
- isMillennium: boolean = false;
39
-
40
- if (args.includes('--help')) {
41
- PrintParamHelp();
42
- process.exit();
43
- }
44
-
45
- // startup args are invalid
46
- if (!args.includes('--build')) {
47
- Logger.Error('Received invalid arguments...');
48
- PrintParamHelp();
49
- process.exit();
50
- }
51
-
52
- for (let i = 0; i < args.length; i++) {
53
- if (args[i] === '--build') {
54
- const BuildMode: string = args[i + 1];
55
-
56
- switch (BuildMode) {
57
- case 'dev':
58
- typeProp = BuildType.DevBuild;
59
- break;
60
- case 'prod':
61
- typeProp = BuildType.ProdBuild;
62
- break;
63
- default: {
64
- Logger.Error('--build parameter must be preceded by build type [dev, prod]');
65
- process.exit();
66
- }
67
- }
68
- }
69
-
70
- if (args[i] == '--target') {
71
- if (args[i + 1] === undefined) {
72
- Logger.Error('--target parameter must be preceded by system path');
73
- process.exit();
74
- }
75
-
76
- targetProp = args[i + 1];
77
- }
78
-
79
- if (args[i] == '--millennium-internal') {
80
- isMillennium = true;
81
- }
82
- }
83
-
84
- return {
85
- type: typeProp,
86
- targetPlugin: targetProp,
87
- isMillennium: isMillennium,
88
- };
89
- };
1
+ import { Logger } from './logger';
2
+
3
+ export const PrintParamHelp = () => {
4
+ console.log(
5
+ [
6
+ '',
7
+ 'usage: millennium-ttc --build <dev|prod> [options]',
8
+ '',
9
+ 'options:',
10
+ ' --build <dev|prod> build type (prod enables minification)',
11
+ ' --target <path> plugin directory (default: current directory)',
12
+ ' --no-update skip update check',
13
+ ' --help show this message',
14
+ '',
15
+ ].join('\n'),
16
+ );
17
+ };
18
+
19
+ export enum BuildType {
20
+ DevBuild,
21
+ ProdBuild,
22
+ }
23
+
24
+ export interface ParameterProps {
25
+ type: BuildType;
26
+ targetPlugin: string; // path
27
+ isMillennium?: boolean;
28
+ }
29
+
30
+ export const ValidateParameters = (args: Array<string>): ParameterProps => {
31
+ let typeProp: BuildType = BuildType.DevBuild,
32
+ targetProp: string = process.cwd(),
33
+ isMillennium: boolean = false;
34
+
35
+ if (args.includes('--help')) {
36
+ PrintParamHelp();
37
+ process.exit();
38
+ }
39
+
40
+ if (!args.includes('--build')) {
41
+ Logger.error('missing required argument: --build');
42
+ PrintParamHelp();
43
+ process.exit();
44
+ }
45
+
46
+ for (let i = 0; i < args.length; i++) {
47
+ if (args[i] === '--build') {
48
+ const BuildMode: string = args[i + 1];
49
+
50
+ switch (BuildMode) {
51
+ case 'dev':
52
+ typeProp = BuildType.DevBuild;
53
+ break;
54
+ case 'prod':
55
+ typeProp = BuildType.ProdBuild;
56
+ break;
57
+ default: {
58
+ Logger.error('--build must be one of: dev, prod');
59
+ process.exit();
60
+ }
61
+ }
62
+ }
63
+
64
+ if (args[i] == '--target') {
65
+ if (args[i + 1] === undefined) {
66
+ Logger.error('--target requires a path argument');
67
+ process.exit();
68
+ }
69
+
70
+ targetProp = args[i + 1];
71
+ }
72
+
73
+ if (args[i] == '--millennium-internal') {
74
+ isMillennium = true;
75
+ }
76
+ }
77
+
78
+ return {
79
+ type: typeProp,
80
+ targetPlugin: targetProp,
81
+ isMillennium: isMillennium,
82
+ };
83
+ };