penseat 0.1.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/index.mjs ADDED
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, mkdirSync, copyFileSync, readFileSync } from "fs";
4
+ import { join, dirname } from "path";
5
+ import { execSync } from "child_process";
6
+ import { fileURLToPath } from "url";
7
+
8
+ const __dirname = dirname(fileURLToPath(import.meta.url));
9
+ const TEMPLATES = join(__dirname, "templates");
10
+ const cwd = process.cwd();
11
+
12
+ // ── Colors ──
13
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
14
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
15
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
16
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
17
+ const red = (s) => `\x1b[31m${s}\x1b[0m`;
18
+ const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
19
+
20
+ // ── Banner ──
21
+ console.log();
22
+ console.log(` ${bold("penseat")} ${dim("— draw on your screen, copy to clipboard")}`);
23
+ console.log();
24
+
25
+ // ── Detect project ──
26
+ const pkgPath = join(cwd, "package.json");
27
+ if (!existsSync(pkgPath)) {
28
+ console.log(` ${red("!")} No package.json found. Run this inside a React project.`);
29
+ process.exit(1);
30
+ }
31
+
32
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
33
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
34
+
35
+ if (!deps.react) {
36
+ console.log(` ${red("!")} No React dependency found. Penseat requires a React project.`);
37
+ process.exit(1);
38
+ }
39
+
40
+ // ── Detect src/ directory ──
41
+ const hasSrc = existsSync(join(cwd, "src"));
42
+ const base = hasSrc ? join(cwd, "src") : cwd;
43
+
44
+ const componentsDir = join(base, "components", "penseat");
45
+ const uiDir = join(componentsDir, "ui");
46
+ const libDir = join(base, "lib");
47
+
48
+ // ── Check if already installed ──
49
+ if (existsSync(join(componentsDir, "penseat.tsx"))) {
50
+ console.log(` ${yellow("!")} Penseat is already installed at ${dim(componentsDir)}`);
51
+ console.log(` ${dim(" Delete the folder and re-run to reinstall.")}`);
52
+ process.exit(0);
53
+ }
54
+
55
+ // ── Copy files ──
56
+ console.log(` ${dim("Installing to")} ${cyan(componentsDir.replace(cwd + "/", ""))}`);
57
+ console.log();
58
+
59
+ mkdirSync(uiDir, { recursive: true });
60
+ mkdirSync(libDir, { recursive: true });
61
+
62
+ const files = [
63
+ { from: "components/penseat.tsx", to: join(componentsDir, "penseat.tsx") },
64
+ { from: "components/penseat-bar.tsx", to: join(componentsDir, "penseat-bar.tsx") },
65
+ { from: "components/drawing-canvas.tsx", to: join(componentsDir, "drawing-canvas.tsx") },
66
+ { from: "components/ui/button.tsx", to: join(componentsDir, "ui", "button.tsx") },
67
+ { from: "lib/capture.ts", to: join(libDir, "capture.ts") },
68
+ ];
69
+
70
+ // Only copy utils.ts if it doesn't exist (shadcn projects already have it)
71
+ const utilsTarget = join(libDir, "utils.ts");
72
+ if (!existsSync(utilsTarget)) {
73
+ files.push({ from: "lib/utils.ts", to: utilsTarget });
74
+ }
75
+
76
+ for (const f of files) {
77
+ copyFileSync(join(TEMPLATES, f.from), f.to);
78
+ }
79
+
80
+ console.log(` ${green("+")} Copied ${files.length} files`);
81
+
82
+ // ── Install dependencies ──
83
+ const needed = [
84
+ "html2canvas-pro",
85
+ "lucide-react",
86
+ "@base-ui/react",
87
+ "class-variance-authority",
88
+ "clsx",
89
+ "tailwind-merge",
90
+ ];
91
+
92
+ const missing = needed.filter((d) => !deps[d]);
93
+
94
+ if (missing.length > 0) {
95
+ console.log(` ${dim("Installing dependencies...")}`);
96
+
97
+ // Detect package manager
98
+ let pm = "npm";
99
+ if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) pm = "bun";
100
+ else if (existsSync(join(cwd, "pnpm-lock.yaml"))) pm = "pnpm";
101
+ else if (existsSync(join(cwd, "yarn.lock"))) pm = "yarn";
102
+
103
+ const installCmd = pm === "npm"
104
+ ? `npm install ${missing.join(" ")}`
105
+ : pm === "yarn"
106
+ ? `yarn add ${missing.join(" ")}`
107
+ : `${pm} add ${missing.join(" ")}`;
108
+
109
+ try {
110
+ execSync(installCmd, { cwd, stdio: "pipe" });
111
+ console.log(` ${green("+")} Installed ${missing.length} dependencies ${dim(`(${pm})`)}`);
112
+ } catch {
113
+ console.log(` ${yellow("!")} Auto-install failed. Run manually:`);
114
+ console.log(` ${dim(` ${installCmd}`)}`);
115
+ }
116
+ } else {
117
+ console.log(` ${green("+")} All dependencies already installed`);
118
+ }
119
+
120
+ // ── Usage instructions ──
121
+ const importPath = hasSrc ? "@/components/penseat/penseat" : "./components/penseat/penseat";
122
+
123
+ console.log();
124
+ console.log(` ${bold("Usage:")} Add to your root layout:`);
125
+ console.log();
126
+ console.log(dim(` import Penseat from "${importPath}"`));
127
+ console.log();
128
+ console.log(dim(` export default function Layout({ children }) {`));
129
+ console.log(dim(` return (`));
130
+ console.log(dim(` <html>`));
131
+ console.log(dim(` <body>`));
132
+ console.log(dim(` {children}`));
133
+ console.log(dim(` <Penseat />`));
134
+ console.log(dim(` </body>`));
135
+ console.log(dim(` </html>`));
136
+ console.log(dim(` )`));
137
+ console.log(dim(` }`));
138
+ console.log();
139
+ console.log(` ${bold("Shortcuts:")} ${dim("Cmd+Shift+D")} toggle ${dim("1-4")} colors ${dim("E")} eraser ${dim("Cmd+C")} copy`);
140
+ console.log();
141
+ console.log(` ${green("Done!")} Draw away.`);
142
+ console.log();
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "penseat",
3
+ "version": "0.1.0",
4
+ "description": "Draw on your screen, copy to clipboard. A devtool for annotating and sharing.",
5
+ "bin": {
6
+ "penseat": "./index.mjs"
7
+ },
8
+ "files": [
9
+ "index.mjs",
10
+ "templates/"
11
+ ],
12
+ "type": "module",
13
+ "license": "MIT",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/nicepenseat/penseat"
17
+ },
18
+ "keywords": [
19
+ "devtool",
20
+ "drawing",
21
+ "annotation",
22
+ "screenshot",
23
+ "clipboard",
24
+ "react",
25
+ "nextjs"
26
+ ]
27
+ }