draply-dev 1.1.1 → 1.2.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.
package/bin/cli.js CHANGED
@@ -35,12 +35,11 @@ function decode(headers, chunks) {
35
35
  });
36
36
  }
37
37
 
38
- // Храним CSS в скрытой папке .draply/ чтобы не засорять проект юзера
38
+ // Store CSS in hidden .draply/ folder to keep user's project clean
39
39
  const projectRoot = process.cwd();
40
40
  const draplyDir = path.join(projectRoot, '.draply');
41
41
  if (!fs.existsSync(draplyDir)) {
42
42
  fs.mkdirSync(draplyDir, { recursive: true });
43
- // Добавляем .draply в .gitignore если он есть
44
43
  const gitignorePath = path.join(projectRoot, '.gitignore');
45
44
  if (fs.existsSync(gitignorePath)) {
46
45
  const gi = fs.readFileSync(gitignorePath, 'utf8');
@@ -54,7 +53,6 @@ if (!fs.existsSync(overridesPath)) {
54
53
  fs.writeFileSync(overridesPath, '/* draply */\n', 'utf8');
55
54
  }
56
55
 
57
-
58
56
  const server = http.createServer((req, res) => {
59
57
 
60
58
  // CORS preflight
@@ -63,7 +61,7 @@ const server = http.createServer((req, res) => {
63
61
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
64
62
  if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; }
65
63
 
66
- // ── Draply: Save endpoint ──────────────────────────────────────────────────
64
+ // ── Draply: Save changes to CSS ─────────────────────────────────────────────
67
65
  if (req.url === '/draply-save' && req.method === 'POST') {
68
66
  let body = '';
69
67
  req.on('data', c => body += c);
@@ -77,13 +75,11 @@ const server = http.createServer((req, res) => {
77
75
  .map(([k, v]) => ` ${k}: ${v};`)
78
76
  .join('\n');
79
77
  const label = ch.selector.split('>').pop().trim();
80
- lines.push(`/* ${label} */
81
- ${ch.selector} {
82
- ${props}
83
- }`);
78
+ lines.push(`/* ${label} */\n${ch.selector} {\n${props}\n}`);
84
79
  }
85
80
  const css = '/* draply — ' + new Date().toLocaleString('ru-RU') + ' */\n\n' + lines.join('\n\n') + '\n';
86
81
  fs.writeFileSync(overridesPath, css, 'utf8');
82
+ console.log(` \x1b[32m✓\x1b[0m Saved ${changes.length} changes to .draply/overrides.css`);
87
83
  res.writeHead(200, { 'Content-Type': 'application/json' });
88
84
  res.end(JSON.stringify({ ok: true }));
89
85
  } catch (e) {
@@ -118,32 +114,6 @@ ${props}
118
114
  return;
119
115
  }
120
116
 
121
- // ── Draply: Project Info endpoint ──────────────────────────────────────────
122
- if (req.url === '/draply-project-info' && req.method === 'GET') {
123
- const info = detectProject(projectRoot);
124
- res.writeHead(200, { 'Content-Type': 'application/json' });
125
- res.end(JSON.stringify(info));
126
- return;
127
- }
128
-
129
- // ── Draply: Find Source endpoint ──────────────────────────────────────────
130
- if (req.url === '/draply-find-source' && req.method === 'POST') {
131
- let body = '';
132
- req.on('data', c => body += c);
133
- req.on('end', () => {
134
- try {
135
- const { selector, tagName, className } = JSON.parse(body);
136
- const result = findSourceFile(projectRoot, { selector, tagName, className });
137
- res.writeHead(200, { 'Content-Type': 'application/json' });
138
- res.end(JSON.stringify(result));
139
- } catch (e) {
140
- res.writeHead(500, { 'Content-Type': 'application/json' });
141
- res.end(JSON.stringify({ found: false, error: e.message }));
142
- }
143
- });
144
- return;
145
- }
146
-
147
117
  // ── Proxy to dev server ────────────────────────────────────────────────────
148
118
  const opts = {
149
119
  hostname: targetHost,
@@ -181,8 +151,8 @@ ${props}
181
151
  pReq.on('error', () => {
182
152
  res.writeHead(502, { 'Content-Type': 'text/html' });
183
153
  res.end(`<!DOCTYPE html><html><body style="background:#0a0a0f;color:#e8e8f0;font-family:monospace;padding:60px;text-align:center">
184
- <h2 style="color:#ff6b6b">⚠ Не могу достучаться до ${targetHost}:${targetPort}</h2>
185
- <p style="color:#555;margin-top:16px">Убедись что dev сервер запущен, потом обнови страницу</p>
154
+ <h2 style="color:#ff6b6b">⚠ Can't reach ${targetHost}:${targetPort}</h2>
155
+ <p style="color:#555;margin-top:16px">Make sure your dev server is running, then refresh</p>
186
156
  <script>setTimeout(()=>location.reload(), 2000)</script>
187
157
  </body></html>`);
188
158
  });
@@ -191,112 +161,10 @@ ${props}
191
161
  });
192
162
 
193
163
  server.listen(proxyPort, () => {
194
- console.log('\n \x1b[32m●\x1b[0m Draply запущен\n');
195
- console.log(` Твой проект → \x1b[36mhttp://${targetHost}:${targetPort}\x1b[0m`);
196
- console.log(` Открой это → \x1b[33mhttp://localhost:${proxyPort}\x1b[0m \x1b[32m← вот сюда заходи!\x1b[0m\n`);
197
- console.log(` \x1b[90mCtrl+C чтобы остановить\x1b[0m\n`);
164
+ console.log('\n \x1b[32m●\x1b[0m Draply running\n');
165
+ console.log(` Your project → \x1b[36mhttp://${targetHost}:${targetPort}\x1b[0m`);
166
+ console.log(` Open this → \x1b[33mhttp://localhost:${proxyPort}\x1b[0m \x1b[32m← go here!\x1b[0m\n`);
167
+ console.log(` \x1b[90mCtrl+C to stop\x1b[0m\n`);
198
168
  });
199
169
 
200
- process.on('SIGINT', () => { console.log('\n \x1b[90mDraply остановлен\x1b[0m\n'); process.exit(0); });
201
-
202
- // ══════════════════════════════════════════════════════════════════════════
203
- // PROJECT DETECTION
204
- // ══════════════════════════════════════════════════════════════════════════
205
- function detectProject(root) {
206
- const result = { framework: 'unknown', cssStrategy: 'unknown', root };
207
-
208
- try {
209
- const pkgPath = path.join(root, 'package.json');
210
- if (!fs.existsSync(pkgPath)) return result;
211
-
212
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
213
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
214
-
215
- // Detect framework
216
- if (allDeps['next']) result.framework = 'next';
217
- else if (allDeps['react']) result.framework = 'react';
218
- else if (allDeps['nuxt']) result.framework = 'nuxt';
219
- else if (allDeps['vue']) result.framework = 'vue';
220
- else if (allDeps['@angular/core']) result.framework = 'angular';
221
- else if (allDeps['svelte']) result.framework = 'svelte';
222
- else if (allDeps['vite']) result.framework = 'vite';
223
-
224
- // Detect CSS strategy
225
- if (allDeps['tailwindcss']) result.cssStrategy = 'tailwind';
226
- else if (allDeps['styled-components']) result.cssStrategy = 'styled-components';
227
- else if (allDeps['@emotion/react'] || allDeps['@emotion/styled']) result.cssStrategy = 'emotion';
228
- else if (allDeps['sass'] || allDeps['node-sass']) result.cssStrategy = 'sass';
229
- else {
230
- // Check for CSS modules (usually enabled by default in React/Next)
231
- if (['react', 'next'].includes(result.framework)) {
232
- result.cssStrategy = 'css-modules';
233
- } else {
234
- result.cssStrategy = 'external';
235
- }
236
- }
237
- } catch { /* ignore */ }
238
-
239
- return result;
240
- }
241
-
242
- // ══════════════════════════════════════════════════════════════════════════
243
- // SOURCE FILE FINDER
244
- // ══════════════════════════════════════════════════════════════════════════
245
- function findSourceFile(root, { selector, tagName, className }) {
246
- const result = { found: false, file: null, line: null, hint: '' };
247
-
248
- try {
249
- const srcDirs = ['src', 'app', 'pages', 'components', 'lib'];
250
- const extensions = ['.tsx', '.jsx', '.vue', '.svelte', '.js', '.ts'];
251
- const searchTerm = className || tagName || '';
252
-
253
- if (!searchTerm) {
254
- result.hint = 'Select an element with a class name for better results.';
255
- return result;
256
- }
257
-
258
- for (const dir of srcDirs) {
259
- const dirPath = path.join(root, dir);
260
- if (!fs.existsSync(dirPath)) continue;
261
-
262
- const files = walkDir(dirPath, extensions);
263
- for (const file of files) {
264
- try {
265
- const content = fs.readFileSync(file, 'utf8');
266
- const lines = content.split('\n');
267
- for (let i = 0; i < lines.length; i++) {
268
- if (lines[i].includes(searchTerm)) {
269
- result.found = true;
270
- result.file = path.relative(root, file);
271
- result.line = i + 1;
272
- result.hint = `Found "${searchTerm}" at ${result.file}:${result.line}`;
273
- return result;
274
- }
275
- }
276
- } catch { /* skip unreadable files */ }
277
- }
278
- }
279
-
280
- result.hint = `Could not find "${searchTerm}" in source files.`;
281
- } catch (e) {
282
- result.hint = 'Error searching source files: ' + e.message;
283
- }
284
-
285
- return result;
286
- }
287
-
288
- function walkDir(dir, extensions, results = []) {
289
- try {
290
- const entries = fs.readdirSync(dir, { withFileTypes: true });
291
- for (const entry of entries) {
292
- const fullPath = path.join(dir, entry.name);
293
- if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === '.next' || entry.name === 'dist') continue;
294
- if (entry.isDirectory()) {
295
- walkDir(fullPath, extensions, results);
296
- } else if (extensions.some(ext => entry.name.endsWith(ext))) {
297
- results.push(fullPath);
298
- }
299
- }
300
- } catch { /* ignore */ }
301
- return results;
302
- }
170
+ process.on('SIGINT', () => { console.log('\n \x1b[90mDraply stopped\x1b[0m\n'); process.exit(0); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "draply-dev",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "Visual overlay for any frontend project — move, resize, restyle live in the browser, save to CSS",
5
5
  "author": "Arman",
6
6
  "type": "commonjs",