@sansavision/create-pulse 0.1.0-alpha.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 (38) hide show
  1. package/README.md +58 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +115 -0
  4. package/package.json +32 -0
  5. package/src/index.ts +114 -0
  6. package/templates/react-all-features/index.html +12 -0
  7. package/templates/react-all-features/package-lock.json +2683 -0
  8. package/templates/react-all-features/package.json +27 -0
  9. package/templates/react-all-features/src/App.tsx +201 -0
  10. package/templates/react-all-features/src/components/VideoPlayer.tsx +113 -0
  11. package/templates/react-all-features/src/hooks/usePulse.ts +113 -0
  12. package/templates/react-all-features/src/index.css +38 -0
  13. package/templates/react-all-features/src/main.tsx +10 -0
  14. package/templates/react-all-features/tailwind.config.js +55 -0
  15. package/templates/react-all-features/tsconfig.json +25 -0
  16. package/templates/react-all-features/tsconfig.node.json +11 -0
  17. package/templates/react-all-features/tsconfig.node.tsbuildinfo +1 -0
  18. package/templates/react-all-features/tsconfig.tsbuildinfo +1 -0
  19. package/templates/react-all-features/vite.config.d.ts +2 -0
  20. package/templates/react-all-features/vite.config.js +6 -0
  21. package/templates/react-all-features/vite.config.ts +7 -0
  22. package/templates/react-watch-together/index.html +12 -0
  23. package/templates/react-watch-together/package-lock.json +2683 -0
  24. package/templates/react-watch-together/package.json +27 -0
  25. package/templates/react-watch-together/src/App.tsx +165 -0
  26. package/templates/react-watch-together/src/components/VideoPlayer.tsx +113 -0
  27. package/templates/react-watch-together/src/hooks/usePulse.ts +113 -0
  28. package/templates/react-watch-together/src/index.css +38 -0
  29. package/templates/react-watch-together/src/main.tsx +10 -0
  30. package/templates/react-watch-together/tailwind.config.js +55 -0
  31. package/templates/react-watch-together/tsconfig.json +25 -0
  32. package/templates/react-watch-together/tsconfig.node.json +11 -0
  33. package/templates/react-watch-together/tsconfig.node.tsbuildinfo +1 -0
  34. package/templates/react-watch-together/tsconfig.tsbuildinfo +1 -0
  35. package/templates/react-watch-together/vite.config.d.ts +2 -0
  36. package/templates/react-watch-together/vite.config.js +6 -0
  37. package/templates/react-watch-together/vite.config.ts +7 -0
  38. package/tsconfig.json +13 -0
package/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @sansavision/create-pulse
2
+
3
+ The official scaffolding tool for creating Pulse applications.
4
+
5
+ `create-pulse` generates a battle-tested, TypeScript-ready React application pre-configured to connect to a Pulse Relay. It eliminates the boilerplate of setting up WebSocket/WebTransport connections and provides robust React hooks.
6
+
7
+ ## Usage
8
+
9
+ You can create a new Pulse project interactively by running:
10
+
11
+ ```bash
12
+ npm create pulse@latest
13
+ # or
14
+ npx @sansavision/create-pulse
15
+ # or
16
+ bunx @sansavision/create-pulse
17
+ ```
18
+
19
+ You can optionally specify the project name directly:
20
+
21
+ ```bash
22
+ npx @sansavision/create-pulse my-pulse-app
23
+ ```
24
+
25
+ ## Available Templates
26
+
27
+ When you run `create-pulse`, you'll be prompted to select a template.
28
+
29
+ ### 1. Watch Together (React + TS)
30
+ A clean implementation of the "Watch Together" synchronized video player.
31
+ - Features a custom `usePulse` hook for lifecycle management.
32
+ - Handles syncing `play`, `pause`, and `seek` events across clients natively via the Pulse Relay.
33
+ - Built with React, Vite, Tailwind CSS, and TypeScript.
34
+
35
+ ### 2. All Features (React + TS)
36
+ A kitchen-sink boilerplate that integrates multiple features.
37
+ - Perfect for exploring the SDK limits.
38
+ - Contains stubs and route setups for Chat, Metrics, and Video sync.
39
+ - Built with React, Vite, Tailwind CSS, and TypeScript.
40
+
41
+ ## Running Your Scaffolded App
42
+
43
+ After your app is generated:
44
+
45
+ 1. Navigate to the project directory:
46
+ ```bash
47
+ cd my-pulse-app
48
+ ```
49
+ 2. Install dependencies:
50
+ ```bash
51
+ npm install
52
+ ```
53
+ 3. Start the dev server:
54
+ ```bash
55
+ npm run dev
56
+ ```
57
+
58
+ **Note:** The templates are configured by default to connect to a local Pulse Relay running at `ws://localhost:4001`. Ensure your local Rust relay is running, or update `src/App.tsx` to point to a production relay URL.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/index.ts
27
+ var p = __toESM(require("@clack/prompts"));
28
+ var import_picocolors = __toESM(require("picocolors"));
29
+ var import_mri = __toESM(require("mri"));
30
+ var import_fs = __toESM(require("fs"));
31
+ var import_path = __toESM(require("path"));
32
+ var argv = (0, import_mri.default)(process.argv.slice(2));
33
+ var TEMPLATES_DIR = import_path.default.join(__dirname, "../templates");
34
+ async function main() {
35
+ p.intro(import_picocolors.default.bgMagenta(import_picocolors.default.white(" Create Pulse App ")));
36
+ let targetDir = argv._[0];
37
+ const project = await p.group(
38
+ {
39
+ path: () => {
40
+ if (targetDir) return Promise.resolve(targetDir);
41
+ return p.text({
42
+ message: "Project name:",
43
+ initialValue: "pulse-app",
44
+ validate: (val) => {
45
+ if (!val || val.trim() === "") return "Project name is required";
46
+ return;
47
+ }
48
+ });
49
+ },
50
+ template: () => {
51
+ return p.select({
52
+ message: "Pick a template:",
53
+ options: [
54
+ { value: "react-watch-together", label: "Watch Together (React + TS)", hint: "Synchronized video playback" },
55
+ { value: "react-all-features", label: "All Features (React + TS)", hint: "Chat, Video, Audio, RPC" },
56
+ { value: "vanilla-basic", label: "Vanilla JS (Basic)", hint: "Minimal setup" }
57
+ ]
58
+ });
59
+ }
60
+ },
61
+ {
62
+ onCancel: () => {
63
+ p.cancel("Operation cancelled.");
64
+ process.exit(0);
65
+ }
66
+ }
67
+ );
68
+ const destDir = import_path.default.resolve(process.cwd(), project.path);
69
+ if (!import_fs.default.existsSync(destDir)) {
70
+ import_fs.default.mkdirSync(destDir, { recursive: true });
71
+ }
72
+ const s = p.spinner();
73
+ s.start(`Scaffolding project in ${import_picocolors.default.cyan(project.path)}...`);
74
+ const templateDir = import_path.default.join(TEMPLATES_DIR, project.template);
75
+ if (!import_fs.default.existsSync(templateDir)) {
76
+ s.stop(import_picocolors.default.red(`Template ${project.template} not found at ${templateDir}`));
77
+ process.exit(1);
78
+ }
79
+ copyDir(templateDir, destDir);
80
+ const gitignorePath = import_path.default.join(destDir, "_gitignore");
81
+ if (import_fs.default.existsSync(gitignorePath)) {
82
+ import_fs.default.renameSync(gitignorePath, import_path.default.join(destDir, ".gitignore"));
83
+ }
84
+ const pkgPath = import_path.default.join(destDir, "package.json");
85
+ if (import_fs.default.existsSync(pkgPath)) {
86
+ const pkg = JSON.parse(import_fs.default.readFileSync(pkgPath, "utf-8"));
87
+ pkg.name = import_path.default.basename(destDir);
88
+ import_fs.default.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
89
+ }
90
+ s.stop(`Scaffolded ${import_picocolors.default.cyan(project.template)} template in ${import_picocolors.default.cyan(project.path)}`);
91
+ p.note(
92
+ `cd ${project.path}
93
+ npm install
94
+ npm run dev`,
95
+ "Next steps"
96
+ );
97
+ p.outro(import_picocolors.default.magenta("Pulse is ready! \u{1F680}"));
98
+ }
99
+ function copyDir(src, dest) {
100
+ const entries = import_fs.default.readdirSync(src, { withFileTypes: true });
101
+ for (const entry of entries) {
102
+ const srcPath = import_path.default.join(src, entry.name);
103
+ if (entry.name === "node_modules" || entry.name === "dist") continue;
104
+ const destPath = import_path.default.join(dest, entry.name);
105
+ if (entry.isDirectory()) {
106
+ if (!import_fs.default.existsSync(destPath)) {
107
+ import_fs.default.mkdirSync(destPath);
108
+ }
109
+ copyDir(srcPath, destPath);
110
+ } else {
111
+ import_fs.default.copyFileSync(srcPath, destPath);
112
+ }
113
+ }
114
+ }
115
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@sansavision/create-pulse",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Scaffold a new Pulse application",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "create-pulse": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsup src/index.ts --format cjs --dts",
11
+ "dev": "tsup src/index.ts --format cjs --watch"
12
+ },
13
+ "keywords": [
14
+ "pulse",
15
+ "create-pulse",
16
+ "scaffold",
17
+ "cli"
18
+ ],
19
+ "author": "Sansa",
20
+ "license": "MIT",
21
+ "dependencies": {
22
+ "@clack/prompts": "^0.7.0",
23
+ "mri": "^1.2.0",
24
+ "picocolors": "^1.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/mri": "^1.1.5",
28
+ "@types/node": "^20.0.0",
29
+ "tsup": "^8.0.2",
30
+ "typescript": "^5.0.0"
31
+ }
32
+ }
package/src/index.ts ADDED
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import * as p from '@clack/prompts';
3
+ import pc from 'picocolors';
4
+ import mri from 'mri';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const argv = mri(process.argv.slice(2));
10
+
11
+ // In CommonJS, __dirname is available, but since we compile with tsup to CJS, we can just use __dirname.
12
+ const TEMPLATES_DIR = path.join(__dirname, '../templates');
13
+
14
+ async function main() {
15
+ p.intro(pc.bgMagenta(pc.white(' Create Pulse App ')));
16
+
17
+ let targetDir = argv._[0];
18
+
19
+ const project = await p.group(
20
+ {
21
+ path: () => {
22
+ if (targetDir) return Promise.resolve(targetDir);
23
+ return p.text({
24
+ message: 'Project name:',
25
+ initialValue: 'pulse-app',
26
+ validate: (val) => {
27
+ if (!val || val.trim() === '') return 'Project name is required';
28
+ return;
29
+ }
30
+ });
31
+ },
32
+ template: () => {
33
+ return p.select({
34
+ message: 'Pick a template:',
35
+ options: [
36
+ { value: 'react-watch-together', label: 'Watch Together (React + TS)', hint: 'Synchronized video playback' },
37
+ { value: 'react-all-features', label: 'All Features (React + TS)', hint: 'Chat, Video, Audio, RPC' },
38
+ { value: 'vanilla-basic', label: 'Vanilla JS (Basic)', hint: 'Minimal setup' }
39
+ ]
40
+ });
41
+ }
42
+ },
43
+ {
44
+ onCancel: () => {
45
+ p.cancel('Operation cancelled.');
46
+ process.exit(0);
47
+ }
48
+ }
49
+ );
50
+
51
+ const destDir = path.resolve(process.cwd(), project.path);
52
+ if (!fs.existsSync(destDir)) {
53
+ fs.mkdirSync(destDir, { recursive: true });
54
+ }
55
+
56
+ const s = p.spinner();
57
+ s.start(`Scaffolding project in ${pc.cyan(project.path)}...`);
58
+
59
+ const templateDir = path.join(TEMPLATES_DIR, project.template as string);
60
+
61
+ if (!fs.existsSync(templateDir)) {
62
+ s.stop(pc.red(`Template ${project.template} not found at ${templateDir}`));
63
+ process.exit(1);
64
+ }
65
+
66
+ copyDir(templateDir, destDir);
67
+
68
+ // Rename _gitignore to .gitignore
69
+ const gitignorePath = path.join(destDir, '_gitignore');
70
+ if (fs.existsSync(gitignorePath)) {
71
+ fs.renameSync(gitignorePath, path.join(destDir, '.gitignore'));
72
+ }
73
+
74
+ // Update package.json name to match the project path
75
+ const pkgPath = path.join(destDir, 'package.json');
76
+ if (fs.existsSync(pkgPath)) {
77
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
78
+ pkg.name = path.basename(destDir);
79
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
80
+ }
81
+
82
+ s.stop(`Scaffolded ${pc.cyan(project.template)} template in ${pc.cyan(project.path)}`);
83
+
84
+ p.note(
85
+ `cd ${project.path}\n` +
86
+ `npm install\n` +
87
+ `npm run dev`,
88
+ 'Next steps'
89
+ );
90
+
91
+ p.outro(pc.magenta('Pulse is ready! 🚀'));
92
+ }
93
+
94
+ function copyDir(src: string, dest: string) {
95
+ const entries = fs.readdirSync(src, { withFileTypes: true });
96
+
97
+ for (const entry of entries) {
98
+ const srcPath = path.join(src, entry.name);
99
+ if (entry.name === 'node_modules' || entry.name === 'dist') continue;
100
+
101
+ const destPath = path.join(dest, entry.name);
102
+
103
+ if (entry.isDirectory()) {
104
+ if (!fs.existsSync(destPath)) {
105
+ fs.mkdirSync(destPath);
106
+ }
107
+ copyDir(srcPath, destPath);
108
+ } else {
109
+ fs.copyFileSync(srcPath, destPath);
110
+ }
111
+ }
112
+ }
113
+
114
+ main().catch(console.error);
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Watch Together - Pulse Demo</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>