contactsheet 0.1.2 → 0.1.4

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/dist/cli.js CHANGED
@@ -496,6 +496,22 @@ async function regenerateRegistry(cfg) {
496
496
  // src/init/index.ts
497
497
  import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
498
498
  import { dirname, join, relative, resolve } from "node:path";
499
+
500
+ // src/term.ts
501
+ var on = process.stdout.isTTY === true && !process.env.NO_COLOR && process.env.TERM !== "dumb";
502
+ var wrap = (open, close) => (s) => on ? `\x1B[${open}m${s}\x1B[${close}m` : s;
503
+ var bold = wrap(1, 22);
504
+ var dim = wrap(2, 22);
505
+ var red = wrap(31, 39);
506
+ var green = wrap(32, 39);
507
+ var yellow = wrap(33, 39);
508
+ var cyan = wrap(36, 39);
509
+ var magenta = wrap(35, 39);
510
+ var link = (s) => on ? `\x1B[36;4m${s}\x1B[39;24m` : s;
511
+ var ok = (s) => `${green("\u2713")} ${s}`;
512
+ var warn = (s) => `${yellow("\u26A0")} ${yellow(s)}`;
513
+
514
+ // src/init/index.ts
499
515
  var DEFAULT_TARGET2 = "http://localhost:3000";
500
516
  var DEFAULT_PORT2 = 5199;
501
517
  var DEFAULT_DESIGN_DIR2 = "design";
@@ -505,21 +521,21 @@ async function runInit(cwd, flags) {
505
521
  assertNextProject(root);
506
522
  const appDir = detectAppDir2(root);
507
523
  const cfg = writeConfigFile(root, appDir, flags);
508
- console.log(`contactsheet init \u2014\u2014 ${root}`);
509
- console.log(` app \u76EE\u5F55:${cfg.appDir}`);
510
- console.log(` \u914D\u7F6E:contactsheet.config.json(target ${cfg.target},port ${cfg.port},\u753B\u677F\u76EE\u5F55 ${cfg.designDir})`);
524
+ console.log(`${bold(cyan("\u25C6"))} ${bold("contactsheet init")} ${dim("\u2014\u2014")} ${root}`);
525
+ console.log(ok(`app \u76EE\u5F55:${cfg.appDir}`));
526
+ console.log(ok(`\u914D\u7F6E:contactsheet.config.json(target ${cfg.target},port ${cfg.port},\u753B\u677F\u76EE\u5F55 ${cfg.designDir})`));
511
527
  seedArtboards(root, cfg.designDir);
512
528
  writeMcpJson(root, cfg.port);
513
529
  writeClaudeHook(root, cfg.port);
514
530
  console.log("");
515
- console.log("\u4E0B\u4E00\u6B65:");
516
- console.log(" 1. \u7167\u5E38\u8D77\u4F60\u81EA\u5DF1\u7684 dev server(next dev),\u786E\u8BA4\u5B83\u5728 " + cfg.target);
517
- console.log(" 2. \u53E6\u5F00\u4E00\u4E2A\u7EC8\u7AEF:npx contactsheet");
518
- console.log(` 3. \u6253\u5F00 http://localhost:${cfg.port}/__cs`);
531
+ console.log(bold("\u4E0B\u4E00\u6B65:"));
532
+ console.log(` 1. \u7167\u5E38\u8D77\u4F60\u81EA\u5DF1\u7684 dev server(next dev),\u786E\u8BA4\u5B83\u5728 ${cyan(cfg.target)}`);
533
+ console.log(` 2. \u53E6\u5F00\u4E00\u4E2A\u7EC8\u7AEF:${cyan("npx contactsheet")}`);
534
+ console.log(` 3. \u6253\u5F00 ${link(`http://localhost:${cfg.port}/__cs`)}`);
519
535
  console.log("");
520
- console.log("\u7AEF\u53E3\u90FD\u53EF\u4EE5\u6362:dev server \u4E0D\u5728 " + cfg.target + " \u5C31 --target <url>,");
521
- console.log(` ${cfg.port} \u88AB\u5360\u5C31 --port <n>(\u6216\u6539 contactsheet.config.json \u540E\u91CD\u8DD1 init,MCP/hook \u4F1A\u8DDF\u7740\u6362)\u3002`);
522
- console.log(` ${cfg.port} \u88AB\u522B\u7684\u8FDB\u7A0B\u5360\u7740\u65F6,contactsheet \u4F1A\u81EA\u52A8\u987A\u5EF6\u5230\u4E0B\u4E00\u4E2A\u7A7A\u95F2\u7AEF\u53E3\u5E76\u63D0\u793A\u3002`);
536
+ console.log(dim("\u7AEF\u53E3\u90FD\u53EF\u4EE5\u6362:dev server \u4E0D\u5728 ") + cfg.target + dim(" \u5C31 --target <url>,"));
537
+ console.log(dim(` ${cfg.port} \u88AB\u5360\u5C31 --port <n>(\u6216\u6539 contactsheet.config.json \u540E\u91CD\u8DD1 init,MCP/hook \u4F1A\u8DDF\u7740\u6362)\u3002`));
538
+ console.log(dim(` ${cfg.port} \u88AB\u522B\u7684\u8FDB\u7A0B\u5360\u7740\u65F6,contactsheet \u4F1A\u81EA\u52A8\u987A\u5EF6\u5230\u4E0B\u4E00\u4E2A\u7A7A\u95F2\u7AEF\u53E3\u5E76\u63D0\u793A\u3002`));
523
539
  }
524
540
  function assertNextProject(root) {
525
541
  const pkgPath = join(root, "package.json");
@@ -597,7 +613,7 @@ function seedArtboards(root, designDir) {
597
613
  writeFileSync(artboard, artboardTemplate(name, importPathFor(root, designDir, componentFile)), "utf8");
598
614
  created++;
599
615
  }
600
- console.log(` \u753B\u677F:\u94FA\u4E86 ${created} \u5757\u753B\u677F,\u8DF3\u8FC7 ${skipped} \u4E2A\u5DF2\u5B58\u5728` + (unreadable ? `,${unreadable} \u4E2A\u6587\u4EF6\u6CA1\u8BA4\u51FA\u7EC4\u4EF6\u5BFC\u51FA` : ""));
616
+ console.log(ok(`\u753B\u677F:\u94FA\u4E86 ${green(String(created))} \u5757,\u8DF3\u8FC7 ${skipped} \u4E2A\u5DF2\u5B58\u5728`) + (unreadable ? `,${unreadable} \u4E2A\u6587\u4EF6\u6CA1\u8BA4\u51FA\u7EC4\u4EF6\u5BFC\u51FA` : ""));
601
617
  }
602
618
  function firstComponentExport(source) {
603
619
  const hits = [];
@@ -1044,9 +1060,9 @@ import path5 from "node:path";
1044
1060
  function isLive(sock) {
1045
1061
  return new Promise((resolve2) => {
1046
1062
  const s = new net.Socket();
1047
- const done = (ok) => {
1063
+ const done = (ok2) => {
1048
1064
  s.destroy();
1049
- resolve2(ok);
1065
+ resolve2(ok2);
1050
1066
  };
1051
1067
  s.on("connect", () => done(true));
1052
1068
  s.on("error", () => done(false));
@@ -1317,12 +1333,12 @@ function pushToken() {
1317
1333
  function originAllowed(cfg, req) {
1318
1334
  const origin = req.headers.origin;
1319
1335
  if (!origin) return true;
1320
- const ok = /* @__PURE__ */ new Set([
1336
+ const ok2 = /* @__PURE__ */ new Set([
1321
1337
  `http://localhost:${cfg.port}`,
1322
1338
  `http://127.0.0.1:${cfg.port}`,
1323
1339
  `http://[::1]:${cfg.port}`
1324
1340
  ]);
1325
- return ok.has(origin);
1341
+ return ok2.has(origin);
1326
1342
  }
1327
1343
  async function handleApi(cfg, req, res, pathname) {
1328
1344
  const method = req.method ?? "GET";
@@ -1489,7 +1505,7 @@ function createProxyLog(target) {
1489
1505
  }
1490
1506
  down = true;
1491
1507
  console.error(
1492
- `[contactsheet] \u26A0\uFE0F \u76EE\u6807 ${target} \u8FDE\u4E0D\u4E0A\u4E86 \u2014\u2014 \u8D77 next dev \u540E\u4F1A\u81EA\u52A8\u6062\u590D(\u753B\u5E03\u4F1A\u81EA\u5DF1\u91CD\u8BD5)\u3002\u671F\u95F4\u7684\u4EE3\u7406\u5931\u8D25\u4E0D\u518D\u9010\u6761\u6253\u5370\u3002`
1508
+ `${yellow("\u26A0")} ${yellow(`\u76EE\u6807 ${target} \u8FDE\u4E0D\u4E0A\u4E86`)} \u2014\u2014 \u8D77 next dev \u540E\u4F1A\u81EA\u52A8\u6062\u590D(\u753B\u5E03\u4F1A\u81EA\u5DF1\u91CD\u8BD5)\u3002` + dim("\u671F\u95F4\u7684\u4EE3\u7406\u5931\u8D25\u4E0D\u518D\u9010\u6761\u6253\u5370\u3002")
1493
1509
  );
1494
1510
  return;
1495
1511
  }
@@ -1499,12 +1515,12 @@ function createProxyLog(target) {
1499
1515
  win.extra++;
1500
1516
  return;
1501
1517
  }
1502
- console.error(`[contactsheet] \u4EE3\u7406\u5931\u8D25 ${pathOf(url)} ${code}`);
1518
+ console.error(`${red("\u2716")} \u4EE3\u7406\u5931\u8D25 ${pathOf(url)} ${red(String(code))}`);
1503
1519
  const timer = setTimeout(() => {
1504
1520
  const extra = windows.get(key)?.extra ?? 0;
1505
1521
  windows.delete(key);
1506
1522
  if (extra > 0) {
1507
- console.error(`[contactsheet] (\u8FC7\u53BB ${WINDOW_MS / 1e3} \u79D2\u8FD8\u6709 ${extra} \u6B21\u540C\u7C7B\u5931\u8D25:${key})`);
1523
+ console.error(dim(` (\u8FC7\u53BB ${WINDOW_MS / 1e3} \u79D2\u8FD8\u6709 ${extra} \u6B21\u540C\u7C7B\u5931\u8D25:${key})`));
1508
1524
  }
1509
1525
  }, WINDOW_MS);
1510
1526
  timer.unref();
@@ -1515,7 +1531,7 @@ function createProxyLog(target) {
1515
1531
  down = false;
1516
1532
  const n = silenced;
1517
1533
  silenced = 0;
1518
- console.log(`[contactsheet] \u76EE\u6807 ${target} \u5DF2\u6062\u590D${n > 0 ? `(\u4E0D\u53EF\u8FBE\u671F\u95F4\u538B\u6389 ${n} \u6761\u4EE3\u7406\u5931\u8D25)` : ""}`);
1534
+ console.log(`${green("\u2713")} ${green(`\u76EE\u6807 ${target} \u5DF2\u6062\u590D`)}${n > 0 ? dim(`(\u4E0D\u53EF\u8FBE\u671F\u95F4\u538B\u6389 ${n} \u6761\u4EE3\u7406\u5931\u8D25)`) : ""}`);
1519
1535
  },
1520
1536
  close() {
1521
1537
  for (const win of windows.values()) clearTimeout(win.timer);
@@ -1565,10 +1581,17 @@ async function startServer(cfg) {
1565
1581
  s.on("close", () => sockets.delete(s));
1566
1582
  });
1567
1583
  const host = cfg.host ?? "127.0.0.1";
1568
- const port = await listenWithFallback(server, cfg, host);
1584
+ let twin = null;
1585
+ const port = await listenWithFallback(server, cfg, host, (t) => {
1586
+ twin = t;
1587
+ t.on("connection", (sock) => {
1588
+ sockets.add(sock);
1589
+ sock.on("close", () => sockets.delete(sock));
1590
+ });
1591
+ });
1569
1592
  if (host !== "127.0.0.1" && host !== "localhost") {
1570
1593
  console.warn(
1571
- `[contactsheet] \u26A0\uFE0F \u76D1\u542C\u5728 ${host}:${port} \u2014\u2014 \u753B\u5E03\u65E0\u9274\u6743,\u540C\u7F51\u7EDC\u7684\u4EFB\u4F55\u4EBA\u90FD\u80FD\u8BFB\u4F60\u7684\u6279\u6CE8\u3001\u5E76\u5411\u4F60\u7684 Claude Code \u4F1A\u8BDD\u63A8\u9001\u6D88\u606F\u3002\u53EA\u5728\u53EF\u4FE1\u7F51\u7EDC\u8FD9\u4E48\u505A\u3002`
1594
+ warn(`\u76D1\u542C\u5728 ${host}:${port} \u2014\u2014 \u753B\u5E03\u65E0\u9274\u6743,\u540C\u7F51\u7EDC\u7684\u4EFB\u4F55\u4EBA\u90FD\u80FD\u8BFB\u4F60\u7684\u6279\u6CE8\u3001\u5E76\u5411\u4F60\u7684 Claude Code \u4F1A\u8BDD\u63A8\u9001\u6D88\u606F\u3002\u53EA\u5728\u53EF\u4FE1\u7F51\u7EDC\u8FD9\u4E48\u505A\u3002`)
1572
1595
  );
1573
1596
  }
1574
1597
  return {
@@ -1577,6 +1600,9 @@ async function startServer(cfg) {
1577
1600
  closeAll();
1578
1601
  proxyLog.close();
1579
1602
  proxy.close();
1603
+ if (twin) {
1604
+ await new Promise((r) => twin.close(() => r()));
1605
+ }
1580
1606
  await new Promise((resolve2) => {
1581
1607
  const fallback = setTimeout(resolve2, 2e3);
1582
1608
  for (const s of sockets) s.destroy();
@@ -1658,22 +1684,38 @@ async function occupiedBySameProject(cfg, port) {
1658
1684
  return false;
1659
1685
  }
1660
1686
  }
1661
- async function listenWithFallback(server, cfg, host) {
1662
- const tryListen = (port) => new Promise((resolve2, reject) => {
1687
+ async function listenWithFallback(server, cfg, host, onTwin) {
1688
+ const listenOnce = (srv, port, h) => new Promise((resolve2, reject) => {
1663
1689
  const onErr = (err) => {
1664
- if (err.code === "EADDRINUSE") resolve2(false);
1690
+ if (err.code === "EADDRINUSE") resolve2("busy");
1691
+ else if (err.code === "EADDRNOTAVAIL" || err.code === "EAFNOSUPPORT") resolve2("nofam");
1665
1692
  else reject(err);
1666
1693
  };
1667
- server.once("error", onErr);
1668
- server.listen(port, host, () => {
1669
- server.off("error", onErr);
1670
- resolve2(true);
1694
+ srv.once("error", onErr);
1695
+ srv.listen(port, h, () => {
1696
+ srv.off("error", onErr);
1697
+ resolve2("ok");
1671
1698
  });
1672
1699
  });
1700
+ const tryListen = async (port) => {
1701
+ const main2 = await listenOnce(server, port, host);
1702
+ if (main2 !== "ok") return false;
1703
+ if (host !== "127.0.0.1") return true;
1704
+ const t = http.createServer(server.listeners("request")[0]);
1705
+ for (const l of server.listeners("upgrade")) t.on("upgrade", l);
1706
+ const r = await listenOnce(t, port, "::1");
1707
+ if (r === "ok") {
1708
+ onTwin(t);
1709
+ return true;
1710
+ }
1711
+ if (r === "nofam") return true;
1712
+ await new Promise((res) => server.close(() => res()));
1713
+ return false;
1714
+ };
1673
1715
  if (await tryListen(cfg.port)) return cfg.port;
1674
1716
  if (await occupiedBySameProject(cfg, cfg.port)) {
1675
1717
  console.log(
1676
- `[contactsheet] \u672C\u9879\u76EE\u5DF2\u7ECF\u6709\u4E00\u4E2A contactsheet \u5728\u8DD1\u4E86,\u76F4\u63A5\u7528\u5B83:http://localhost:${cfg.port}/__cs`
1718
+ `${cyan("\u25CF")} \u672C\u9879\u76EE\u5DF2\u7ECF\u6709\u4E00\u4E2A contactsheet \u5728\u8DD1\u4E86,\u76F4\u63A5\u7528\u5B83:${link(`http://localhost:${cfg.port}/__cs`)}`
1677
1719
  );
1678
1720
  process.exit(0);
1679
1721
  }
@@ -1685,9 +1727,8 @@ async function listenWithFallback(server, cfg, host) {
1685
1727
  for (let p = cfg.port + 1; p <= cfg.port + 20; p++) {
1686
1728
  if (await tryListen(p)) {
1687
1729
  console.warn(
1688
- `[contactsheet] \u26A0\uFE0F \u7AEF\u53E3 ${cfg.port} \u88AB\u5360,\u5DF2\u987A\u5EF6\u5230 ${p} \u2192 http://localhost:${p}/__cs
1689
- \u6CE8\u610F:\u6362\u7AEF\u53E3 = \u6362\u6D4F\u89C8\u5668\u6E90\u3002\u753B\u677F\u4F4D\u7F6E/\u6298\u53E0/\u5E95\u8272\u8FD9\u4E9B\u672C\u673A\u8BB0\u5FC6\u6309\u6E90\u9694\u79BB,\u8FD9\u4E2A\u7AEF\u53E3\u4E0B\u662F\u5168\u65B0\u7684;
1690
- .mcp.json \u4E0E hook \u4ECD\u6307\u5411 ${cfg.port}\u3002\u60F3\u8981\u7A33\u5B9A,\u8BF7\u91CA\u653E ${cfg.port},\u6216\u6539 config \u7684 port \u540E\u91CD\u8DD1 npx contactsheet init\u3002`
1730
+ warn(`\u7AEF\u53E3 ${cfg.port} \u88AB\u5360,\u5DF2\u987A\u5EF6\u5230 ${p} \u2192 `) + link(`http://localhost:${p}/__cs`) + "\n" + dim(` \u6CE8\u610F:\u6362\u7AEF\u53E3 = \u6362\u6D4F\u89C8\u5668\u6E90\u3002\u753B\u677F\u4F4D\u7F6E/\u6298\u53E0/\u5E95\u8272\u8FD9\u4E9B\u672C\u673A\u8BB0\u5FC6\u6309\u6E90\u9694\u79BB,\u8FD9\u4E2A\u7AEF\u53E3\u4E0B\u662F\u5168\u65B0\u7684;
1731
+ `) + dim(` .mcp.json \u4E0E hook \u4ECD\u6307\u5411 ${cfg.port}\u3002\u60F3\u8981\u7A33\u5B9A,\u8BF7\u91CA\u653E ${cfg.port},\u6216\u6539 config \u7684 port \u540E\u91CD\u8DD1 npx contactsheet init\u3002`)
1691
1732
  );
1692
1733
  return p;
1693
1734
  }
@@ -1746,6 +1787,7 @@ async function startWatcher(cfg, onChange) {
1746
1787
  }
1747
1788
 
1748
1789
  // src/cli.ts
1790
+ process.noDeprecation = true;
1749
1791
  var HELP = `contactsheet \u2014\u2014 \u628A UI \u7684\u5404\u79CD\u72B6\u6001\u644A\u5728\u4E00\u9762\u53EF\u7F29\u653E\u7684\u5899\u4E0A
1750
1792
 
1751
1793
  contactsheet [up] \u9644\u7740\u5230\u5F53\u524D Next.js repo:\u6CE8\u5165\u753B\u677F\u8DEF\u7531 + \u8D77\u5916\u58F3(\u9ED8\u8BA4\u547D\u4EE4)
@@ -1794,19 +1836,21 @@ ${HELP}`);
1794
1836
  }
1795
1837
  async function up(flags) {
1796
1838
  const cfg = loadConfig(process.cwd(), flags);
1797
- await tryStep("\u6CE8\u5165\u753B\u677F\u8DEF\u7531", () => ensureInjected(cfg));
1839
+ const injected = await tryStep("\u6CE8\u5165\u753B\u677F\u8DEF\u7531", () => ensureInjected(cfg));
1840
+ if (injected !== null) console.log(ok(dim("\u753B\u677F\u8DEF\u7531\u5DF2\u6CE8\u5165")));
1798
1841
  const watcher = await tryStep(
1799
1842
  "\u542F\u52A8\u6587\u4EF6\u76D1\u542C",
1800
1843
  () => startWatcher(cfg, (entries) => broadcast({ type: "registry", entries }))
1801
1844
  );
1845
+ if (watcher) console.log(ok(dim(`\u76D1\u542C ${cfg.designDir}/**/*.artboard.{tsx,ts}`)));
1802
1846
  const server = await startServer(cfg);
1803
1847
  banner(cfg, server.port);
1804
1848
  let closing = false;
1805
1849
  const shutdown = async (sig) => {
1806
1850
  if (closing) return;
1807
1851
  closing = true;
1808
- console.log(`
1809
- [contactsheet] \u6536\u5230 ${sig},\u6B63\u5728\u9000\u51FA\u2026`);
1852
+ console.log(dim(`
1853
+ [contactsheet] \u6536\u5230 ${sig},\u6B63\u5728\u9000\u51FA\u2026`));
1810
1854
  if (watcher) await quiet(() => watcher.close());
1811
1855
  await quiet(() => server.close());
1812
1856
  await quiet(() => closeBrowser());
@@ -1819,7 +1863,7 @@ async function tryStep(label, fn) {
1819
1863
  try {
1820
1864
  return await fn();
1821
1865
  } catch (err) {
1822
- console.warn(`[contactsheet] ${label}\u5931\u8D25,\u5DF2\u8DF3\u8FC7:${err instanceof Error ? err.message : String(err)}`);
1866
+ console.warn(warn(`${label}\u5931\u8D25,\u5DF2\u8DF3\u8FC7:${err instanceof Error ? err.message : String(err)}`));
1823
1867
  return null;
1824
1868
  }
1825
1869
  }
@@ -1831,10 +1875,10 @@ async function quiet(fn) {
1831
1875
  }
1832
1876
  function banner(cfg, port) {
1833
1877
  console.log(`
1834
- contactsheet http://localhost:${port}/__cs
1835
- \u4EE3\u7406\u76EE\u6807 ${cfg.target}
1836
- \u753B\u677F\u76EE\u5F55 ${cfg.designDir}/ \xB7 app \u76EE\u5F55 ${cfg.appDir}/
1837
- Ctrl-C \u9000\u51FA
1878
+ ${bold(cyan("\u25C6"))} ${bold("contactsheet")} ${link(`http://localhost:${port}/__cs`)}
1879
+ ${dim("\u4EE3\u7406\u76EE\u6807")} ${cfg.target}
1880
+ ${dim("\u753B\u677F\u76EE\u5F55")} ${cfg.designDir}/ ${dim("\xB7")} app \u76EE\u5F55 ${cfg.appDir}/
1881
+ ${dim("Ctrl-C \u9000\u51FA")}
1838
1882
  `);
1839
1883
  }
1840
1884
  //# sourceMappingURL=cli.js.map