sliftutils 0.6.6 → 0.7.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.
Files changed (62) hide show
  1. package/builders/electronBuild.ts +101 -0
  2. package/builders/electronBuildRun.js +4 -0
  3. package/builders/extensionBuild.ts +15 -9
  4. package/builders/generateIndexDts.js +46 -0
  5. package/builders/hotReload.ts +96 -0
  6. package/builders/nodeJSBuild.ts +1 -1
  7. package/builders/setup.ts +182 -0
  8. package/builders/setupRun.js +4 -0
  9. package/builders/watch.ts +182 -0
  10. package/builders/watchRun.js +4 -0
  11. package/builders/webBuild.ts +2 -2
  12. package/builders/webRun.js +4 -0
  13. package/bundler/bundleEntry.ts +17 -9
  14. package/bundler/bundleEntryCaller.ts +1 -1
  15. package/bundler/bundleRequire.ts +21 -3
  16. package/electron/electronIndex.html +12 -0
  17. package/electron/electronMain.ts +29 -0
  18. package/electron/electronRenderer.tsx +33 -0
  19. package/extension/extBackground.ts +22 -0
  20. package/extension/extContentScript.ts +19 -0
  21. package/extension/manifest.json +29 -0
  22. package/index.d.ts +332 -0
  23. package/misc/environment.ts +28 -0
  24. package/nodejs/server.ts +16 -0
  25. package/package.json +28 -3
  26. package/render-utils/DropdownCustom.d.ts +23 -0
  27. package/render-utils/FullscreenModal.d.ts +12 -0
  28. package/render-utils/Input.d.ts +39 -0
  29. package/render-utils/InputLabel.d.ts +35 -0
  30. package/render-utils/InputPicker.d.ts +35 -0
  31. package/render-utils/LocalStorageParam.d.ts +12 -0
  32. package/render-utils/SyncedController.d.ts +38 -0
  33. package/render-utils/SyncedLoadingIndicator.d.ts +8 -0
  34. package/render-utils/Table.d.ts +32 -0
  35. package/render-utils/URLParam.d.ts +13 -0
  36. package/render-utils/asyncObservable.d.ts +3 -0
  37. package/render-utils/index.html +12 -0
  38. package/render-utils/mobxTyped.d.ts +1 -0
  39. package/render-utils/modal.d.ts +6 -0
  40. package/render-utils/observer.d.ts +17 -0
  41. package/spec.txt +5 -64
  42. package/storage/FileFolderAPI.tsx +1 -1
  43. package/storage/PendingManager.tsx +1 -1
  44. package/tsconfig.declarations.json +18 -0
  45. package/web/browser.tsx +37 -0
  46. package/web/index.html +32 -0
  47. /package/{web → render-utils}/DropdownCustom.tsx +0 -0
  48. /package/{web → render-utils}/FullscreenModal.tsx +0 -0
  49. /package/{web → render-utils}/GenericFormat.tsx +0 -0
  50. /package/{web → render-utils}/Input.tsx +0 -0
  51. /package/{web → render-utils}/InputLabel.tsx +0 -0
  52. /package/{web → render-utils}/InputPicker.tsx +0 -0
  53. /package/{web → render-utils}/LocalStorageParam.ts +0 -0
  54. /package/{web → render-utils}/SyncedController.ts +0 -0
  55. /package/{web → render-utils}/SyncedLoadingIndicator.tsx +0 -0
  56. /package/{web → render-utils}/Table.tsx +0 -0
  57. /package/{web → render-utils}/URLParam.ts +0 -0
  58. /package/{web → render-utils}/asyncObservable.ts +0 -0
  59. /package/{web → render-utils}/colors.tsx +0 -0
  60. /package/{web → render-utils}/mobxTyped.ts +0 -0
  61. /package/{web → render-utils}/modal.tsx +0 -0
  62. /package/{web → render-utils}/observer.tsx +0 -0
@@ -9,8 +9,8 @@ import { getAllFiles } from "../misc/fs";
9
9
  async function main() {
10
10
  let time = Date.now();
11
11
  let yargObj = yargs(process.argv)
12
- .option("entryPoint", { type: "string", default: "./browser.tsx", desc: `Path to the entry point file` })
13
- .option("indexHtml", { type: "string", default: "./index.html", desc: `Path to the index.html file` })
12
+ .option("entryPoint", { type: "string", default: "./web/browser.tsx", desc: `Path to the entry point file` })
13
+ .option("indexHtml", { type: "string", default: "./web/index.html", desc: `Path to the index.html file` })
14
14
  .option("assetsFolder", { type: "string", default: "./assets", desc: `Path to the assets folder` })
15
15
  .option("outputFolder", { type: "string", default: "./build-web", desc: `Output folder` })
16
16
  .argv || {}
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ const open = require("open");
3
+ void open("./build-web/web/index.html");
4
+
@@ -1,22 +1,19 @@
1
1
  import path from "path";
2
2
  import { bundle } from "./bundler";
3
3
  import fs from "fs";
4
- import yargs from "yargs";
5
4
 
6
5
  async function main() {
7
- let yargObj = yargs(process.argv)
8
- .option("entryPoint", { type: "string", desc: `Path to the entry point file` })
9
- .option("outputFolder", { type: "string", desc: `Path to the output folder` })
10
- .argv || {}
11
- ;
12
- let entryPoint = yargObj.entryPoint;
13
- let outputFolder = yargObj.outputFolder;
6
+ // NOTE: Using yargs added ~0.5s to the time to run this, and considering we run in ~1s... that's just too much
7
+ let entryPoint = process.argv[2];
8
+ let outputFolder = process.argv[3];
14
9
  if (!entryPoint) {
15
10
  throw new Error("No entry point provided. Please use the --entryPoint option.");
16
11
  }
17
12
  if (!outputFolder) {
18
13
  throw new Error("No output folder provided. Please use the --outputFolder option.");
19
14
  }
15
+ // We prefer production, as this is what the bundler uses internally. This ensures that in the build and when run, we will have the same environment, which will result in the same requires being called.
16
+ process.env.NODE_ENV = process.env.NODE_ENV || "production";
20
17
  require(entryPoint);
21
18
 
22
19
  let name = path.basename(entryPoint);
@@ -32,6 +29,17 @@ async function main() {
32
29
  rootPath: path.resolve("."),
33
30
  entryPoints: [entryPoint],
34
31
  });
35
- await fs.promises.writeFile(`${yargObj.outputFolder}/${name}`, bundled.bundle);
32
+
33
+ let finalPath = `${outputFolder}/${name}`;
34
+ let tempPath = `${finalPath}.tmp`;
35
+
36
+ try {
37
+ await fs.promises.writeFile(tempPath, bundled.bundle);
38
+ await fs.promises.rename(tempPath, finalPath);
39
+ } finally {
40
+ try {
41
+ await fs.promises.unlink(tempPath);
42
+ } catch { }
43
+ }
36
44
  }
37
45
  main().catch(console.error).finally(() => process.exit());
@@ -10,5 +10,5 @@ export async function bundleEntryCaller(config: {
10
10
  entryPoint = path.resolve(entryPoint).replace(/\\/g, "/");
11
11
  outputFolder = path.resolve(outputFolder).replace(/\\/g, "/");
12
12
  let bundleEntryPath = path.resolve(__dirname, "bundleEntry.ts").replace(/\\/g, "/");
13
- await runPromise(`yarn typenode ${JSON.stringify(bundleEntryPath)} --entryPoint ${JSON.stringify(entryPoint)} --outputFolder ${JSON.stringify(outputFolder)}`);
13
+ await runPromise(`node -r ./node_modules/typenode/index.js ${JSON.stringify(bundleEntryPath)} ${JSON.stringify(entryPoint)} ${JSON.stringify(outputFolder)}`);
14
14
  }
@@ -16,11 +16,9 @@ export function bundleRequire(config: BundleRequireConfig) {
16
16
  (globalThis as any).global = (globalThis as any).global || globalThis;
17
17
  (globalThis as any).setImmediate = (globalThis as any).setImmediate || globalThis.setTimeout;
18
18
 
19
- // Set document, so isNode checks return false.
20
- (globalThis as any).document = (globalThis as any).document || {};
21
19
  (globalThis as any).BOOTED_EDGE_NODE = undefined;
22
20
 
23
- const builtInModuleExports = {
21
+ let builtInModuleExports: { [key: string]: unknown } = {
24
22
  worker_threads: {
25
23
  isMainThread: true,
26
24
  },
@@ -47,6 +45,26 @@ export function bundleRequire(config: BundleRequireConfig) {
47
45
  child_process: {},
48
46
  events: class EventEmitter { },
49
47
  };
48
+ // If is nodeJs
49
+ let allBuiltInModules = new Set<string>();
50
+ // Electron
51
+ allBuiltInModules.add("electron");
52
+ allBuiltInModules.add("original-fs");
53
+ allBuiltInModules.add("vscode");
54
+ if (typeof document === "undefined" && typeof require !== "undefined") {
55
+ // Change the builts ins to use the actual built ins!
56
+ let { builtinModules } = require("node:module");
57
+ for (let key of builtinModules) {
58
+ allBuiltInModules.add(key);
59
+ }
60
+ }
61
+ for (let key of allBuiltInModules) {
62
+ Object.defineProperty(builtInModuleExports, key, {
63
+ get() {
64
+ return require(key);
65
+ },
66
+ });
67
+ }
50
68
 
51
69
  // Just path.resolve (but needs to be reimplemented, because we can't use imports)
52
70
  function pathResolve(...paths: string[]): string {
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Electron Test App</title>
6
+ </head>
7
+ <body>
8
+ <div id="app"></div>
9
+ <script src="./electronRenderer.js"></script>
10
+ </body>
11
+ </html>
12
+
@@ -0,0 +1,29 @@
1
+ import { app, BrowserWindow } from "electron";
2
+ import { isInElectron } from "../misc/environment";
3
+
4
+ function main() {
5
+ if (!isInElectron()) {
6
+ return;
7
+ }
8
+
9
+ app.whenReady().then(() => {
10
+ const mainWindow = new BrowserWindow({
11
+ width: 800,
12
+ height: 600,
13
+ webPreferences: {
14
+ nodeIntegration: false,
15
+ contextIsolation: true,
16
+ },
17
+ });
18
+
19
+ mainWindow.loadFile("./electronIndex.html");
20
+ mainWindow.webContents.openDevTools();
21
+ });
22
+
23
+ app.on("window-all-closed", () => {
24
+ app.quit();
25
+ });
26
+ }
27
+
28
+ main();
29
+
@@ -0,0 +1,33 @@
1
+ import * as preact from "preact";
2
+ import { observable } from "mobx";
3
+ import { observer } from "../render-utils/observer";
4
+ import { isNode } from "typesafecss";
5
+ import { enableHotReloading } from "../builders/hotReload";
6
+
7
+ @observer
8
+ class App extends preact.Component {
9
+ synced = observable({
10
+ count: 0,
11
+ });
12
+
13
+ render() {
14
+ return (
15
+ <div>
16
+ <h1>Hello from Electron!</h1>
17
+ <p>Count: {this.synced.count}</p>
18
+ <button onClick={() => this.synced.count++}>
19
+ Increment
20
+ </button>
21
+ </div>
22
+ );
23
+ }
24
+ }
25
+
26
+ async function main() {
27
+ if (isNode()) return;
28
+ await enableHotReloading({ port: 9879 });
29
+ preact.render(<App />, document.getElementById("app")!);
30
+ }
31
+
32
+ main().catch(console.error);
33
+
@@ -0,0 +1,22 @@
1
+ import { enableHotReloading } from "../builders/hotReload";
2
+ import { isInChromeExtension } from "../misc/environment";
3
+
4
+ async function main() {
5
+ if (!isInChromeExtension()) {
6
+ return;
7
+ }
8
+
9
+ await enableHotReloading({ port: 9878 });
10
+
11
+ chrome.runtime.onInstalled.addListener(() => {
12
+ console.log("Extension installed!");
13
+ });
14
+
15
+ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
16
+ console.log("Message received:", message, sender);
17
+ sendResponse({ status: "ok 26" });
18
+ });
19
+ }
20
+
21
+ main().catch(console.error);
22
+
@@ -0,0 +1,19 @@
1
+ import { enableHotReloading } from "../builders/hotReload";
2
+ import { isInChromeExtension } from "../misc/environment";
3
+
4
+ async function main() {
5
+ if (!isInChromeExtension()) {
6
+ return;
7
+ }
8
+
9
+ await enableHotReloading({ port: 9878 });
10
+
11
+ console.log("Content script loaded! new");
12
+
13
+ chrome.runtime.sendMessage({ type: "contentScriptLoaded" }, (response) => {
14
+ console.log("Response from background:", response);
15
+ });
16
+ }
17
+
18
+ main().catch(console.error);
19
+
@@ -0,0 +1,29 @@
1
+ {
2
+ "manifest_version": 3,
3
+ "name": "Example Extension",
4
+ "version": "1.0.0",
5
+ "description": "Example Chrome extension",
6
+ "background": {
7
+ "service_worker": "extBackground.js"
8
+ },
9
+ "content_scripts": [
10
+ {
11
+ "matches": [
12
+ "*://*.wikipedia.org/*"
13
+ ],
14
+ "js": [
15
+ "extContentScript.js"
16
+ ]
17
+ }
18
+ ],
19
+ "permissions": [
20
+ "tabs",
21
+ "storage"
22
+ ],
23
+ "host_permissions": [
24
+ "*://*.wikipedia.org/*"
25
+ ],
26
+ "icons": {
27
+ "16": "assets/icon16.png"
28
+ }
29
+ }
package/index.d.ts ADDED
@@ -0,0 +1,332 @@
1
+ // Auto-generated file. Do not edit manually.
2
+ // Generated by: yarn generate-index-dts
3
+
4
+ declare module "sliftutils/render-utils/DropdownCustom" {
5
+ import preact from "preact";
6
+ import { LengthOrPercentage } from "typesafecss/cssTypes";
7
+ export declare class DropdownCustom<T> extends preact.Component<{
8
+ class?: string;
9
+ optionClass?: string;
10
+ title?: string;
11
+ value: T;
12
+ onChange: (value: T, index: number) => void;
13
+ maxWidth?: LengthOrPercentage;
14
+ options: {
15
+ value: T;
16
+ label: (isOpen: boolean) => preact.ComponentChild;
17
+ }[];
18
+ }> {
19
+ synced: {
20
+ isOpen: boolean;
21
+ tempIndexSelected: number | null;
22
+ };
23
+ onUnmount: (() => void)[];
24
+ componentDidMount(): void;
25
+ componentDidUnmount(): void;
26
+ render(): preact.JSX.Element;
27
+ }
28
+
29
+ }
30
+
31
+ declare module "sliftutils/render-utils/FullscreenModal" {
32
+ import preact from "preact";
33
+ export declare function showFullscreenModal(contents: preact.ComponentChildren): void;
34
+ export declare class FullscreenModal extends preact.Component<{
35
+ parentState?: {
36
+ open: boolean;
37
+ };
38
+ onCancel?: () => void;
39
+ style?: preact.JSX.CSSProperties;
40
+ outerStyle?: preact.JSX.CSSProperties;
41
+ }> {
42
+ render(): preact.JSX.Element;
43
+ }
44
+
45
+ }
46
+
47
+ declare module "sliftutils/render-utils/Input" {
48
+ import preact from "preact";
49
+ export type InputProps = (preact.JSX.HTMLAttributes<HTMLInputElement> & {
50
+ /** ONLY throttles onChangeValue */
51
+ throttle?: number;
52
+ flavor?: "large" | "small" | "none";
53
+ focusOnMount?: boolean;
54
+ textarea?: boolean;
55
+ /** Update on key stroke, not on blur (just does onInput = onChange, as onInput already does this) */
56
+ hot?: boolean;
57
+ /** Updates arrow keys with modifier behavior to use larger numbers, instead of decimals. */
58
+ integer?: boolean;
59
+ /** Only works with number/integer */
60
+ reverseArrowKeyDirection?: boolean;
61
+ inputRef?: (x: HTMLInputElement | null) => void;
62
+ /** Don't blur on enter key */
63
+ noEnterKeyBlur?: boolean;
64
+ noFocusSelect?: boolean;
65
+ inputKey?: string;
66
+ fillWidth?: boolean;
67
+ autocompleteValues?: string[];
68
+ /** Forces the input to update when focused. Usually we hold updates, to prevent the user's
69
+ * typing to be interrupted by background updates.
70
+ * NOTE: "hot" is usually required when using this.
71
+ */
72
+ forceInputValueUpdatesWhenFocused?: boolean;
73
+ onChangeValue?: (value: string) => void;
74
+ });
75
+ export declare class Input extends preact.Component<InputProps> {
76
+ onFocusText: string;
77
+ firstFocus: boolean;
78
+ elem: HTMLInputElement | null;
79
+ lastValue: unknown;
80
+ lastChecked: unknown;
81
+ onChangeThrottle: undefined | {
82
+ throttle: number;
83
+ run: (newValue: string) => void;
84
+ };
85
+ render(): preact.JSX.Element;
86
+ }
87
+
88
+ }
89
+
90
+ declare module "sliftutils/render-utils/InputLabel" {
91
+ import preact from "preact";
92
+ import { InputProps } from "./Input";
93
+ import { URLParamStr } from "./URLParam";
94
+ export type InputLabelProps = Omit<InputProps, "label" | "title"> & {
95
+ label?: preact.ComponentChild;
96
+ number?: boolean;
97
+ /** A number, AND, an integer. Changes behavior arrow arrow keys as well */
98
+ integer?: boolean;
99
+ checkbox?: boolean;
100
+ edit?: boolean;
101
+ alwaysShowPencil?: boolean;
102
+ outerClass?: string;
103
+ maxDecimals?: number;
104
+ percent?: boolean;
105
+ editClass?: string;
106
+ fontSize?: number;
107
+ tooltip?: string;
108
+ fillWidth?: boolean;
109
+ useDateUI?: boolean;
110
+ };
111
+ export declare const startGuessDateRange: number;
112
+ export declare const endGuessDateRange: number;
113
+ export declare class InputLabel extends preact.Component<InputLabelProps> {
114
+ synced: {
115
+ editting: boolean;
116
+ editInputValue: string;
117
+ editUpdateSeqNum: number;
118
+ };
119
+ render(): preact.JSX.Element;
120
+ }
121
+ export declare class InputLabelURL extends preact.Component<InputLabelProps & {
122
+ persisted: URLParamStr;
123
+ }> {
124
+ render(): preact.JSX.Element;
125
+ }
126
+
127
+ }
128
+
129
+ declare module "sliftutils/render-utils/InputPicker" {
130
+ import preact from "preact";
131
+ export type InputOption<T> = {
132
+ value: T;
133
+ label?: preact.ComponentChild;
134
+ matchText?: string;
135
+ };
136
+ export type FullInputOption<T> = {
137
+ value: T;
138
+ label: preact.ComponentChild;
139
+ matchText: string;
140
+ };
141
+ export declare class InputPickerURL extends preact.Component<{
142
+ label?: preact.ComponentChild;
143
+ options: (string | InputOption<string>)[];
144
+ allowNonOptions?: boolean;
145
+ value: {
146
+ value: string;
147
+ };
148
+ }> {
149
+ render(): preact.JSX.Element;
150
+ }
151
+ export declare class InputPicker<T> extends preact.Component<{
152
+ label?: preact.ComponentChild;
153
+ picked: T[];
154
+ options: InputOption<T>[];
155
+ addPicked: (value: T) => void;
156
+ removePicked: (value: T) => void;
157
+ allowNonOptions?: boolean;
158
+ }> {
159
+ synced: {
160
+ pendingText: string;
161
+ focused: boolean;
162
+ };
163
+ render(): preact.JSX.Element;
164
+ }
165
+
166
+ }
167
+
168
+ declare module "sliftutils/render-utils/LocalStorageParam" {
169
+ export declare class LocalStorageParamStr {
170
+ readonly storageKey: string;
171
+ private defaultValue;
172
+ private state;
173
+ lastSetValue: string;
174
+ constructor(storageKey: string, defaultValue?: string);
175
+ forceUpdate(): void;
176
+ get(): string;
177
+ set(value: string): void;
178
+ get value(): string;
179
+ set value(value: string);
180
+ }
181
+
182
+ }
183
+
184
+ declare module "sliftutils/render-utils/SyncedController" {
185
+ import { SocketRegistered } from "socket-function/SocketFunctionTypes";
186
+ type RemapFunction<T> = T extends (...args: infer Args) => Promise<infer Return> ? {
187
+ (...args: Args): Return | undefined;
188
+ promise(...args: Args): Promise<Return>;
189
+ refresh(...args: Args): void;
190
+ refreshAll(): void;
191
+ reset(...args: Args): void;
192
+ resetAll(): void;
193
+ isLoading(...args: Args): boolean;
194
+ setCache(cache: {
195
+ args: Args;
196
+ result: Return;
197
+ }): void;
198
+ } : T;
199
+ export declare function getSyncedController<T extends SocketRegistered>(controller: T, config?: {
200
+ /** When a controller call for a write finishes, we refresh all readers.
201
+ * - Invalidation is global, across all controllers.
202
+ */
203
+ reads?: {
204
+ [key in keyof T["nodes"][""]]?: string[];
205
+ };
206
+ writes?: {
207
+ [key in keyof T["nodes"][""]]?: string[];
208
+ };
209
+ }): {
210
+ (nodeId: string): {
211
+ [fnc in keyof T["nodes"][""]]: RemapFunction<T["nodes"][""][fnc]>;
212
+ } & {
213
+ resetAll(): void;
214
+ refreshAll(): void;
215
+ anyPending(): boolean;
216
+ };
217
+ resetAll(): void;
218
+ refreshAll(): void;
219
+ anyPending(): boolean;
220
+ rerenderAll(): void;
221
+ };
222
+ export {};
223
+
224
+ }
225
+
226
+ declare module "sliftutils/render-utils/SyncedLoadingIndicator" {
227
+ import * as preact from "preact";
228
+ export declare class SyncedLoadingIndicator extends preact.Component<{
229
+ controller: {
230
+ anyPending: () => boolean;
231
+ };
232
+ }> {
233
+ render(): preact.JSX.Element | null;
234
+ }
235
+
236
+ }
237
+
238
+ declare module "sliftutils/render-utils/Table" {
239
+ import preact from "preact";
240
+ import { JSXFormatter } from "./GenericFormat";
241
+ export type ColumnType<T = unknown, Row extends RowType = RowType> = undefined | null | {
242
+ center?: boolean;
243
+ title?: preact.ComponentChild;
244
+ formatter?: JSXFormatter<T, Row>;
245
+ };
246
+ export type RowType = {
247
+ [columnName: string]: unknown;
248
+ };
249
+ export type ColumnsType = {
250
+ [columnName: string]: ColumnType;
251
+ };
252
+ export type TableType<RowT extends RowType = RowType> = {
253
+ columns: {
254
+ [columnName in keyof RowT]?: ColumnType<RowT[columnName], RowT>;
255
+ };
256
+ rows: RowT[];
257
+ };
258
+ export declare class Table<RowT extends RowType> extends preact.Component<TableType<RowT> & {
259
+ class?: string;
260
+ cellClass?: string;
261
+ initialLimit?: number;
262
+ lineLimit?: number;
263
+ characterLimit?: number;
264
+ excludeEmptyColumns?: boolean;
265
+ }> {
266
+ state: {
267
+ limit: number;
268
+ };
269
+ render(): preact.JSX.Element;
270
+ }
271
+
272
+ }
273
+
274
+ declare module "sliftutils/render-utils/URLParam" {
275
+ export declare class URLParamStr {
276
+ readonly urlKey: string;
277
+ private state;
278
+ lastSetValue: string;
279
+ constructor(urlKey: string);
280
+ forceUpdate(): void;
281
+ get(): string;
282
+ set(value: string): void;
283
+ get value(): string;
284
+ set value(value: string);
285
+ }
286
+ export declare function batchUrlUpdate<T>(code: () => T): T;
287
+ export declare function createLink(params: [URLParamStr, string][]): string;
288
+
289
+ }
290
+
291
+ declare module "sliftutils/render-utils/asyncObservable" {
292
+ export declare function asyncCache<Args, T>(getValue: (args: Args) => Promise<T>): {
293
+ (args: Args): T | undefined;
294
+ };
295
+
296
+ }
297
+
298
+ declare module "sliftutils/render-utils/mobxTyped" {
299
+ export declare function configureMobxNextFrameScheduler(): void;
300
+
301
+ }
302
+
303
+ declare module "sliftutils/render-utils/modal" {
304
+ import preact from "preact";
305
+ export declare function showModal(config: {
306
+ contents: preact.ComponentChildren;
307
+ }): {
308
+ close: () => void;
309
+ };
310
+
311
+ }
312
+
313
+ declare module "sliftutils/render-utils/observer" {
314
+ import * as preact from "preact";
315
+ import { Reaction } from "mobx";
316
+ export declare function observer<T extends {
317
+ new (...args: any[]): {
318
+ render(): preact.ComponentChild;
319
+ forceUpdate(callback?: () => void): void;
320
+ componentWillUnmount?(): void;
321
+ };
322
+ }>(Constructor: T): {
323
+ new (...args: any[]): {
324
+ reaction: Reaction;
325
+ componentWillUnmount(): void;
326
+ render(...args: any[]): preact.ComponentChild;
327
+ forceUpdate(callback?: () => void): void;
328
+ };
329
+ readonly name: string;
330
+ } & T;
331
+
332
+ }
@@ -2,10 +2,38 @@
2
2
  export function isInChromeExtension() {
3
3
  return typeof chrome !== "undefined" && chrome.runtime && chrome.runtime.id;
4
4
  }
5
+ export function isInChromeExtensionBackground() {
6
+ if (!isInChromeExtension()) return false;
7
+
8
+ // Manifest V3: Service Worker context (has importScripts but no document)
9
+ if (typeof (globalThis as any).importScripts === "function" && typeof document === "undefined") {
10
+ return true;
11
+ }
12
+
13
+ // Manifest V2: Background page
14
+ if (typeof chrome.extension !== "undefined" && typeof chrome.extension.getBackgroundPage === "function") {
15
+ try {
16
+ return chrome.extension.getBackgroundPage() === window;
17
+ } catch (e) {
18
+ return false;
19
+ }
20
+ }
21
+
22
+ return false;
23
+ }
24
+ export function isInChromeExtensionContentScript() {
25
+ return isInChromeExtension() && !isInChromeExtensionBackground();
26
+ }
27
+ export function isInElectron() {
28
+ return typeof process !== "undefined" && process.versions && process.versions.electron;
29
+ }
5
30
  let isInBuildFlag = false;
6
31
  export function triggerIsInBuild() {
7
32
  isInBuildFlag = true;
8
33
  }
9
34
  export function isInBuild() {
10
35
  return isInBuildFlag;
36
+ }
37
+ export function isInBrowser() {
38
+ return typeof document !== "undefined";
11
39
  }
@@ -0,0 +1,16 @@
1
+ import http from "http";
2
+
3
+ async function main() {
4
+ const server = http.createServer((req, res) => {
5
+ res.writeHead(200, { "Content-Type": "text/plain" });
6
+ res.end("Hello from Node.js!\n");
7
+ });
8
+
9
+ const port = 3000;
10
+ server.listen(port, () => {
11
+ console.log(`Server running at http://localhost:${port}/`);
12
+ });
13
+ }
14
+
15
+ main().catch(console.error);
16
+