@lamppost/create-ink-player 1.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/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2026 John Resig
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Create Lamp Post Ink Player
2
+
3
+ This is a utility for helping to create Lamp Post Ink Player games.
4
+
5
+ Please see [https://www.npmjs.com/package/@lamppost/ink-player](@lamppost/ink-player) for more details on how to use this package.
6
+
7
+ This software is released under an MIT license.
package/index.js ADDED
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+ import { execSync } from "node:child_process";
3
+ import {
4
+ cpSync,
5
+ existsSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ rmSync,
9
+ writeFileSync,
10
+ } from "node:fs";
11
+ import { dirname, join, resolve } from "node:path";
12
+ import { stdin as input, stdout as output } from "node:process";
13
+ import readline from "node:readline/promises";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = dirname(__filename);
18
+
19
+ async function main() {
20
+ // Ask for the directory name
21
+ const rl = readline.createInterface({ input, output });
22
+
23
+ let directoryName = "";
24
+ while (true) {
25
+ const answer = await rl.question(
26
+ "What directory name would you like to use? ",
27
+ );
28
+ const trimmed = answer.trim();
29
+
30
+ if (!trimmed || trimmed.length === 0) {
31
+ console.log("Directory name cannot be empty");
32
+ continue;
33
+ }
34
+
35
+ // Check for valid directory name characters
36
+ if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) {
37
+ console.log(
38
+ "Directory name can only contain letters, numbers, hyphens, and underscores",
39
+ );
40
+ continue;
41
+ }
42
+
43
+ directoryName = trimmed;
44
+ break;
45
+ }
46
+
47
+ const targetDir = resolve(process.cwd(), directoryName);
48
+
49
+ // Check if directory already exists
50
+ if (existsSync(targetDir)) {
51
+ console.error(`Error: Directory "${directoryName}" already exists.`);
52
+ process.exit(1);
53
+ }
54
+
55
+ // Ask for the game name
56
+ let gameName = "";
57
+ while (true) {
58
+ const answer = await rl.question("What is the name of your game? ");
59
+ const trimmed = answer.trim();
60
+
61
+ if (!trimmed || trimmed.length === 0) {
62
+ console.log("Game name cannot be empty");
63
+ continue;
64
+ }
65
+
66
+ gameName = trimmed;
67
+ break;
68
+ }
69
+
70
+ rl.close();
71
+
72
+ console.log(`Creating game "${gameName}"...`);
73
+
74
+ // Create the directory
75
+ mkdirSync(targetDir, { recursive: true });
76
+
77
+ // Copy template contents
78
+ const templateDir = join(__dirname, "template");
79
+ console.log("Copying template files...");
80
+ cpSync(templateDir, targetDir, { recursive: true });
81
+
82
+ // Delete the node_modules directory, if it exists
83
+ if (existsSync(join(targetDir, "node_modules"))) {
84
+ rmSync(join(targetDir, "node_modules"), { recursive: true });
85
+ }
86
+
87
+ // Update package.json
88
+ const packageJsonPath = join(targetDir, "package.json");
89
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
90
+ packageJson.name = gameName;
91
+ writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
92
+
93
+ // Update the gameName in the src/index.ts file
94
+ const indexTsPath = join(targetDir, "src/index.ts");
95
+ const indexTs = readFileSync(indexTsPath, "utf-8").replace(
96
+ "Lamp Post Player",
97
+ gameName,
98
+ );
99
+ writeFileSync(indexTsPath, indexTs);
100
+
101
+ // Update the gameName in the README.md file
102
+ const readmeMdPath = join(targetDir, "README.md");
103
+ const readmeMd = readFileSync(readmeMdPath, "utf-8").replace(
104
+ "Lamp Post Player",
105
+ gameName,
106
+ );
107
+ writeFileSync(readmeMdPath, readmeMd);
108
+
109
+ console.log("Installing dependencies...");
110
+
111
+ // Run pnpm install
112
+ try {
113
+ execSync("pnpm install", {
114
+ cwd: targetDir,
115
+ stdio: "inherit",
116
+ });
117
+ } catch (error) {
118
+ console.error("Error installing dependencies:", error.message);
119
+ process.exit(1);
120
+ }
121
+
122
+ console.log(`\n${gameName} setup complete!`);
123
+ console.log(`\nTo get started:`);
124
+ console.log(` cd ${directoryName}`);
125
+ console.log(` pnpm dev`);
126
+ console.log(
127
+ `\nThis will start the development server and open the game in your browser.`,
128
+ );
129
+ }
130
+
131
+ main().catch((error) => {
132
+ console.error("Error:", error.message);
133
+ process.exit(1);
134
+ });
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@lamppost/create-ink-player",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "A utility for helping to create Lamp Post Ink Player games.",
8
+ "author": "John Resig <jeresig@gmail.com>",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/jeresig/lamppost-player.git"
13
+ },
14
+ "homepage": "https://github.com/jeresig/lamppost-player",
15
+ "keywords": [
16
+ "ink",
17
+ "inkjs",
18
+ "interactive fiction",
19
+ "game"
20
+ ],
21
+ "main": "./index.js",
22
+ "bin": {
23
+ "create-ink-player": "./index.js"
24
+ },
25
+ "files": [
26
+ "index.js",
27
+ "template",
28
+ "package.json",
29
+ "README.md",
30
+ "LICENSE"
31
+ ],
32
+ "dependencies": {}
33
+ }
@@ -0,0 +1,16 @@
1
+ # Lamp Post Player
2
+
3
+ This is a web player for running [Ink](https://github.com/inkle/ink) stories created by [Lamp Post Projects](https://lamppostprojects.com/).
4
+
5
+ ## Getting Started
6
+
7
+ * Run `pnpm dev` to see the result in your browser and confirm that things are running correctly.
8
+ * Open up `story/game.ink` in the Inky editor and make modifications, go back to your game in the browser and see your changes live!
9
+ * Update the settings in `src/index.tsx` to use your game name and update any other settings.
10
+ * Update the About page in `src/About.tsx` to include some information about your game (or remove that screen entirely by updating `src/index.ts`).
11
+ * Add any custom CSS styling inside the `src/styles.scss` file.
12
+ * Run `pnpm build` to create the final HTML/JS/CSS/Image files needed to display the game. The files will be output to the `dist/` directory. You can then bundle them or upload them to the location of your choice.
13
+
14
+ ## Next Steps
15
+
16
+ Please see [https://www.npmjs.com/package/@lamppost/ink-player](@lamppost/ink-player) for more details on how to configure the Lamp Post Ink Player
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "lamppost-ink-player-template",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "rsbuild build",
8
+ "dev": "rsbuild dev --open",
9
+ "preview": "rsbuild preview"
10
+ },
11
+ "dependencies": {
12
+ "@lamppost/ink-player": "^1.0.3",
13
+ "preact": "^10.27.1",
14
+ "react-bootstrap": "^2.10.10"
15
+ },
16
+ "devDependencies": {
17
+ "@rsbuild/core": "^1.5.4",
18
+ "@rsbuild/plugin-image-compress": "^1.3.1",
19
+ "@rsbuild/plugin-preact": "^1.5.2",
20
+ "@rsbuild/plugin-sass": "^1.4.0",
21
+ "@rsbuild/plugin-svgr": "^1.2.2",
22
+ "typescript": "^5.9.2"
23
+ },
24
+ "packageManager": "pnpm@10.1.0+sha512.c89847b0667ddab50396bbbd008a2a43cf3b581efd59cf5d9aa8923ea1fb4b8106c041d540d08acb095037594d73ebc51e1ec89ee40c88b30b8a66c0fae0ac1b",
25
+ "pnpm": {
26
+ "patchedDependencies": {
27
+ "@restart/ui": "patches/@restart__ui.patch"
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,26 @@
1
+ diff --git a/esm/useClickOutside.js b/esm/useClickOutside.js
2
+ index 7c8eeb387f4c5a10cfc7114078f81085951f520f..4526af4795862d5afdf6b945e0ba4887117fa656 100644
3
+ --- a/esm/useClickOutside.js
4
+ +++ b/esm/useClickOutside.js
5
+ @@ -66,7 +66,7 @@ function useClickOutside(ref, onClickOutside = noop, {
6
+ // For things rendered in an iframe, the event might originate on the parent window
7
+ // so we should fall back to that global event if the local one doesn't exist
8
+ // https://github.com/facebook/react/issues/20074
9
+ - let currentEvent = (_ownerWindow$event = ownerWindow.event) != null ? _ownerWindow$event : (_ownerWindow$parent = ownerWindow.parent) == null ? void 0 : _ownerWindow$parent.event;
10
+ + let currentEvent = null; try { currentEvent = (_ownerWindow$event = ownerWindow.event) != null ? _ownerWindow$event : (_ownerWindow$parent = ownerWindow.parent) == null ? void 0 : _ownerWindow$parent.event; } catch {}
11
+ let removeInitialTriggerListener = null;
12
+ if (InitialTriggerEvents[clickTrigger]) {
13
+ removeInitialTriggerListener = listen(doc, InitialTriggerEvents[clickTrigger], handleInitialMouse, true);
14
+ diff --git a/esm/useRootClose.js b/esm/useRootClose.js
15
+ index b9e3cfabe1947262004ddf2a210f824458792f03..331843568da66142a77b9aab3b3af91f9809036a 100644
16
+ --- a/esm/useRootClose.js
17
+ +++ b/esm/useRootClose.js
18
+ @@ -37,7 +37,7 @@ function useRootClose(ref, onRootClose, {
19
+
20
+ // Store the current event to avoid triggering handlers immediately
21
+ // https://github.com/facebook/react/issues/20074
22
+ - let currentEvent = (doc.defaultView || window).event;
23
+ + let currentEvent = null; try { currentEvent = (doc.defaultView || window).event; } catch {}
24
+ const removeKeyupListener = listen(doc, 'keyup', e => {
25
+ // skip if this event is the same as the one running when we added the handlers
26
+ if (e === currentEvent) {