markui-cli 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/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # MarkUI
2
+
3
+ Annotate your running localhost UI and generate structured prompts for Claude Code.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g markui
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Start your dev server, then in a new terminal:
14
+
15
+ ```bash
16
+ markui
17
+ ```
18
+
19
+ Open http://localhost:3131 in your browser.
20
+
21
+ Click "Annotate", click elements, leave instructions, hit "Copy for Claude".
22
+
23
+ ## Options
24
+
25
+ ```bash
26
+ markui --target 3000 # specify which port your app runs on
27
+ markui --port 3131 # specify which port MarkUI runs on
28
+ ```
29
+
30
+ ## Works with
31
+
32
+ React, Vue, Svelte, Next.js, Vite, plain HTML — anything that runs on localhost.
package/bin/markui.js ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+
3
+ const net = require('net');
4
+ const minimist = require('minimist');
5
+ const { startProxy, startAssetServer } = require('../src/proxy');
6
+ const { inject, eject } = require('../src/injector');
7
+
8
+ const args = minimist(process.argv.slice(2), {
9
+ alias: { t: 'target', p: 'port', h: 'help' },
10
+ default: { port: 3131 }
11
+ });
12
+
13
+ const command = args._[0];
14
+
15
+ if (args.help) {
16
+ console.log(`
17
+ markui — Annotate your localhost UI for Claude Code
18
+
19
+ Usage:
20
+ markui Start markui (auto-injects, open your normal dev URL)
21
+ markui inject Manually inject markui tags into index.html
22
+ markui eject Remove markui tags from index.html
23
+ markui --target 3000 Proxy mode for a specific port (use localhost:3131)
24
+ markui --port 4000 Change MarkUI's asset port (default: 3131)
25
+
26
+ How it works:
27
+ 1. Start your dev server (npm run dev, etc.)
28
+ 2. Run "markui" in your project directory
29
+ 3. Open your normal dev server URL — the overlay is already there
30
+ 4. Ctrl+C to stop (auto-cleans injected tags)
31
+
32
+ Options:
33
+ --target, -t Proxy a specific port (fallback if no index.html found)
34
+ --port, -p Port MarkUI asset server runs on (default: 3131)
35
+ --help, -h Show this help message
36
+ `);
37
+ process.exit(0);
38
+ }
39
+
40
+ if (command === 'inject') {
41
+ inject(args.port);
42
+ process.exit(0);
43
+ }
44
+
45
+ if (command === 'eject') {
46
+ eject();
47
+ process.exit(0);
48
+ }
49
+
50
+ // Common dev server ports: React/Next (3000), Vite (5173), Angular (4200),
51
+ // generic (8080/8000/8888), Flask/Django (5000), Parcel (1234), Astro (4321),
52
+ // Svelte (5000), Nuxt (3000), Remix (3000), Gatsby (8000), Hugo (1313),
53
+ // Rails (3000), Laravel (8000), Spring (8080), Express (3000/4000)
54
+ const SCAN_PORTS = [3000, 5173, 4200, 8080, 8000, 8888, 5000, 4000, 1234, 4321, 1313, 3001, 3002, 5500];
55
+
56
+ function checkPort(port) {
57
+ return new Promise((resolve) => {
58
+ const sock = new net.Socket();
59
+ sock.setTimeout(300);
60
+ sock.once('connect', () => {
61
+ sock.destroy();
62
+ resolve(true);
63
+ });
64
+ sock.once('timeout', () => {
65
+ sock.destroy();
66
+ resolve(false);
67
+ });
68
+ sock.once('error', () => {
69
+ sock.destroy();
70
+ resolve(false);
71
+ });
72
+ sock.connect(port, '127.0.0.1');
73
+ });
74
+ }
75
+
76
+ async function detectTarget() {
77
+ for (const port of SCAN_PORTS) {
78
+ if (await checkPort(port)) return port;
79
+ }
80
+ return null;
81
+ }
82
+
83
+ async function main() {
84
+ let targetPort = args.target;
85
+ const proxyPort = args.port;
86
+
87
+ // Try inject + asset server mode first (user keeps their normal URL)
88
+ const hasIndexHtml = (() => {
89
+ try { return inject(proxyPort, true); } catch(e) { return false; }
90
+ })();
91
+
92
+ if (hasIndexHtml) {
93
+ // Inject mode: tags added to index.html, start asset server
94
+ // User opens their normal dev server URL
95
+ inject(proxyPort);
96
+
97
+ // Clean up injected tags on exit
98
+ const cleanup = () => {
99
+ try { eject(); } catch(e) {}
100
+ process.exit(0);
101
+ };
102
+ process.on('SIGINT', cleanup);
103
+ process.on('SIGTERM', cleanup);
104
+
105
+ if (targetPort) {
106
+ console.log(`\n Your dev server is on http://localhost:${targetPort} — open that URL.\n`);
107
+ } else {
108
+ targetPort = await detectTarget();
109
+ if (targetPort) {
110
+ console.log(`\n Dev server detected on http://localhost:${targetPort} — open that URL.\n`);
111
+ }
112
+ }
113
+
114
+ startAssetServer({ port: proxyPort });
115
+ return;
116
+ }
117
+
118
+ // Fallback: proxy mode (no index.html found to inject into)
119
+ if (targetPort) {
120
+ const alive = await checkPort(targetPort);
121
+ if (!alive) {
122
+ console.error(
123
+ `\nNothing found on port ${targetPort}. Is your dev server running?\n`
124
+ );
125
+ process.exit(1);
126
+ }
127
+ if (targetPort === proxyPort) {
128
+ console.error(
129
+ `\nTarget port and MarkUI port are both ${proxyPort}. Use --port to pick a different MarkUI port.\n`
130
+ );
131
+ process.exit(1);
132
+ }
133
+ startProxy({ targetPort, proxyPort });
134
+ } else {
135
+ console.log('Auto-detecting dev server...');
136
+ targetPort = await detectTarget();
137
+
138
+ if (targetPort && targetPort !== proxyPort) {
139
+ startProxy({ targetPort, proxyPort });
140
+ } else {
141
+ startAssetServer({ port: proxyPort });
142
+ }
143
+ }
144
+ }
145
+
146
+ main();
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "markui-cli",
3
+ "version": "0.1.0",
4
+ "description": "Annotate your localhost UI and generate prompts for Claude Code",
5
+ "bin": {
6
+ "markui": "bin/markui.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "src",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "prepublishOnly": "node -e \"require('./src/proxy'); console.log('Sanity check passed')\""
15
+ },
16
+ "dependencies": {
17
+ "http-proxy": "^1.18.1",
18
+ "minimist": "^1.2.8"
19
+ },
20
+ "keywords": [
21
+ "claude",
22
+ "ai",
23
+ "developer-tools",
24
+ "ui",
25
+ "annotation",
26
+ "llm"
27
+ ],
28
+ "engines": {
29
+ "node": ">=16"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/adebolaali/markui.git"
34
+ },
35
+ "license": "MIT"
36
+ }
@@ -0,0 +1,107 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const SEARCH_PATHS = [
5
+ 'index.html',
6
+ 'public/index.html',
7
+ 'src/index.html',
8
+ 'app/index.html',
9
+ 'dist/index.html',
10
+ 'build/index.html',
11
+ 'static/index.html',
12
+ 'www/index.html',
13
+ 'templates/index.html',
14
+ 'views/index.html'
15
+ ];
16
+
17
+ const MARKER_START = '<!-- markui:start -->';
18
+ const MARKER_END = '<!-- markui:end -->';
19
+
20
+ function buildSnippet(port) {
21
+ return (
22
+ MARKER_START + '\n' +
23
+ `<script src="http://localhost:${port}/__markui__/overlay.js"></script>\n` +
24
+ `<link rel="stylesheet" href="http://localhost:${port}/__markui__/overlay.css">\n` +
25
+ MARKER_END
26
+ );
27
+ }
28
+
29
+ function findIndexHtml() {
30
+ const cwd = process.cwd();
31
+ for (const rel of SEARCH_PATHS) {
32
+ const full = path.join(cwd, rel);
33
+ if (fs.existsSync(full)) return full;
34
+ }
35
+ return null;
36
+ }
37
+
38
+ function inject(port, dryRun) {
39
+ const file = findIndexHtml();
40
+ if (!file) {
41
+ if (dryRun) return false;
42
+ console.error(
43
+ '\nCould not find index.html. Checked:\n' +
44
+ SEARCH_PATHS.map(p => ' ./' + p).join('\n') + '\n'
45
+ );
46
+ process.exit(1);
47
+ }
48
+ if (dryRun) return true;
49
+
50
+ let html = fs.readFileSync(file, 'utf8');
51
+
52
+ // Remove existing injection if present
53
+ const markerRe = new RegExp(
54
+ escapeRegExp(MARKER_START) + '[\\s\\S]*?' + escapeRegExp(MARKER_END) + '\\n?',
55
+ 'g'
56
+ );
57
+ html = html.replace(markerRe, '');
58
+
59
+ const snippet = buildSnippet(port);
60
+
61
+ if (html.includes('</body>')) {
62
+ html = html.replace('</body>', snippet + '\n</body>');
63
+ } else if (html.includes('</html>')) {
64
+ html = html.replace('</html>', snippet + '\n</html>');
65
+ } else {
66
+ html += '\n' + snippet + '\n';
67
+ }
68
+
69
+ fs.writeFileSync(file, html);
70
+
71
+ const rel = path.relative(process.cwd(), file);
72
+ console.log(`Injected into ${rel} — run markui to start the asset server`);
73
+ }
74
+
75
+ function eject() {
76
+ const file = findIndexHtml();
77
+ if (!file) {
78
+ console.error(
79
+ '\nCould not find index.html. Checked:\n' +
80
+ SEARCH_PATHS.map(p => ' ./' + p).join('\n') + '\n'
81
+ );
82
+ process.exit(1);
83
+ }
84
+
85
+ let html = fs.readFileSync(file, 'utf8');
86
+
87
+ const markerRe = new RegExp(
88
+ escapeRegExp(MARKER_START) + '[\\s\\S]*?' + escapeRegExp(MARKER_END) + '\\n?',
89
+ 'g'
90
+ );
91
+
92
+ if (!markerRe.test(html)) {
93
+ console.log('No markui tags found in ' + path.relative(process.cwd(), file));
94
+ return;
95
+ }
96
+
97
+ html = html.replace(markerRe, '');
98
+ fs.writeFileSync(file, html);
99
+
100
+ console.log('Removed markui from ' + path.relative(process.cwd(), file));
101
+ }
102
+
103
+ function escapeRegExp(str) {
104
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
105
+ }
106
+
107
+ module.exports = { inject, eject };