contactsheet 0.1.5 → 0.1.7
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 +7 -5
- package/dist/canvas/app.js +39 -10
- package/dist/canvas/app.js.map +2 -2
- package/dist/canvas/style-pins.css +54 -0
- package/dist/cli.js +41 -24
- package/dist/cli.js.map +3 -3
- package/package.json +1 -1
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
.cs-ref-strip:empty { display: none; } /* 没图就当它不存在,别留一条空隙 */
|
|
49
49
|
|
|
50
50
|
.cs-ref-thumb {
|
|
51
|
+
position: relative; /* 删除钮的锚 */
|
|
51
52
|
width: 56px;
|
|
52
53
|
padding: 2px;
|
|
53
54
|
border: 1px solid var(--cs-line-2);
|
|
@@ -56,6 +57,26 @@
|
|
|
56
57
|
cursor: zoom-in;
|
|
57
58
|
}
|
|
58
59
|
.cs-ref-thumb:hover { border-color: var(--cs-accent); }
|
|
60
|
+
/* 删除钮:悬停才现身,压在缩略图右上角。命中区 18px,比字形大一圈 */
|
|
61
|
+
.cs-ref-rm {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: -6px;
|
|
64
|
+
right: -6px;
|
|
65
|
+
width: 18px;
|
|
66
|
+
height: 18px;
|
|
67
|
+
padding: 0;
|
|
68
|
+
border: 1px solid var(--cs-line-2);
|
|
69
|
+
border-radius: var(--cs-r-full);
|
|
70
|
+
background: var(--cs-s1);
|
|
71
|
+
color: var(--cs-fg-2);
|
|
72
|
+
font: var(--cs-fs-micro)/1 var(--cs-ff-sans);
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
opacity: 0;
|
|
75
|
+
transition: opacity var(--cs-dur-1) var(--cs-ease), color var(--cs-dur-1) var(--cs-ease);
|
|
76
|
+
}
|
|
77
|
+
.cs-ref-thumb:hover .cs-ref-rm,
|
|
78
|
+
.cs-ref-rm:focus-visible { opacity: 1; }
|
|
79
|
+
.cs-ref-rm:hover { color: var(--cs-danger); border-color: var(--cs-danger); }
|
|
59
80
|
/* 固定比例 + 等比裁切:贴进来的截图长宽不可控,只给宽高会把竖图压扁;
|
|
60
81
|
槽位尺寸先定死,图到位时也不会把气泡撑得跳一下 */
|
|
61
82
|
.cs-ref-thumb img {
|
|
@@ -102,3 +123,36 @@
|
|
|
102
123
|
text-align: center;
|
|
103
124
|
cursor: help;
|
|
104
125
|
}
|
|
126
|
+
|
|
127
|
+
/* ============================================================
|
|
128
|
+
气泡编辑框的滚动卫生(RESP-015:滚动条定制;滚轮事件由 view.ts 放行)
|
|
129
|
+
============================================================ */
|
|
130
|
+
.cs-pin-edit {
|
|
131
|
+
resize: none; /* UA 的拉伸角在暗色气泡上是块白斑,气泡宽度也不该被拉走 */
|
|
132
|
+
overscroll-behavior: contain; /* 滚到头别把手势漏给画布 */
|
|
133
|
+
scrollbar-width: thin;
|
|
134
|
+
scrollbar-color: var(--cs-s4) transparent;
|
|
135
|
+
}
|
|
136
|
+
.cs-pin-edit::-webkit-scrollbar { width: 8px; }
|
|
137
|
+
.cs-pin-edit::-webkit-scrollbar-thumb {
|
|
138
|
+
border-radius: var(--cs-r-full);
|
|
139
|
+
background: var(--cs-s4);
|
|
140
|
+
border: 2px solid transparent;
|
|
141
|
+
background-clip: content-box;
|
|
142
|
+
}
|
|
143
|
+
.cs-pin-edit::-webkit-scrollbar-track { background: transparent; }
|
|
144
|
+
/* 气泡正文长文同样能滚(展示态) */
|
|
145
|
+
.cs-pin-text {
|
|
146
|
+
max-height: 220px;
|
|
147
|
+
overflow-y: auto;
|
|
148
|
+
overscroll-behavior: contain;
|
|
149
|
+
scrollbar-width: thin;
|
|
150
|
+
scrollbar-color: var(--cs-s4) transparent;
|
|
151
|
+
}
|
|
152
|
+
.cs-pin-text::-webkit-scrollbar { width: 8px; }
|
|
153
|
+
.cs-pin-text::-webkit-scrollbar-thumb {
|
|
154
|
+
border-radius: var(--cs-r-full);
|
|
155
|
+
background: var(--cs-s4);
|
|
156
|
+
border: 2px solid transparent;
|
|
157
|
+
background-clip: content-box;
|
|
158
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
|
|
4
10
|
// src/cli.ts
|
|
5
11
|
import { parseArgs } from "node:util";
|
|
@@ -29,7 +35,6 @@ function loadConfig(cwd, flags) {
|
|
|
29
35
|
// 末尾斜杠去掉,后面一律靠 `${target}/xxx` 拼
|
|
30
36
|
target: String(pick("target") ?? DEFAULT_TARGET).replace(/\/+$/, ""),
|
|
31
37
|
port,
|
|
32
|
-
portExplicit: flags.port !== void 0,
|
|
33
38
|
appDir: pick("appDir") ?? detectAppDir(projectRoot),
|
|
34
39
|
designDir: pick("designDir") ?? DEFAULT_DESIGN_DIR
|
|
35
40
|
};
|
|
@@ -1734,15 +1739,27 @@ function downPage(target) {
|
|
|
1734
1739
|
function escapeHtml(s) {
|
|
1735
1740
|
return s.replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
1736
1741
|
}
|
|
1737
|
-
async function
|
|
1742
|
+
async function probeSameProject(cfg, port) {
|
|
1743
|
+
for (let i = 0; i < 2; i++) {
|
|
1744
|
+
try {
|
|
1745
|
+
const r = await fetch(`http://127.0.0.1:${port}/__cs/api/state`, {
|
|
1746
|
+
signal: AbortSignal.timeout(2500)
|
|
1747
|
+
});
|
|
1748
|
+
if (!r.ok) return null;
|
|
1749
|
+
const data = await r.json();
|
|
1750
|
+
return data?.projectRoot === cfg.projectRoot ? { version: data.version } : null;
|
|
1751
|
+
} catch {
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
return null;
|
|
1755
|
+
}
|
|
1756
|
+
function occupierPid(port) {
|
|
1738
1757
|
try {
|
|
1739
|
-
const
|
|
1740
|
-
const
|
|
1741
|
-
|
|
1742
|
-
const data = await r.json();
|
|
1743
|
-
return data?.projectRoot === cfg.projectRoot;
|
|
1758
|
+
const { execSync } = __require("node:child_process");
|
|
1759
|
+
const out = execSync(`lsof -t -nP -iTCP:${port} -sTCP:LISTEN`, { timeout: 3e3 }).toString().trim();
|
|
1760
|
+
return out.split("\n")[0] || null;
|
|
1744
1761
|
} catch {
|
|
1745
|
-
return
|
|
1762
|
+
return null;
|
|
1746
1763
|
}
|
|
1747
1764
|
}
|
|
1748
1765
|
async function listenWithFallback(server, cfg, host, onTwin) {
|
|
@@ -1774,27 +1791,27 @@ async function listenWithFallback(server, cfg, host, onTwin) {
|
|
|
1774
1791
|
return false;
|
|
1775
1792
|
};
|
|
1776
1793
|
if (await tryListen(cfg.port)) return cfg.port;
|
|
1777
|
-
|
|
1794
|
+
const running = await probeSameProject(cfg, cfg.port);
|
|
1795
|
+
if (running) {
|
|
1796
|
+
if (running.version && running.version !== pkgVersion()) {
|
|
1797
|
+
const pid2 = occupierPid(cfg.port);
|
|
1798
|
+
console.error(
|
|
1799
|
+
warn(`\u672C\u9879\u76EE\u5DF2\u6709\u4E00\u4E2A contactsheet \u5728 ${cfg.port} \u4E0A,\u4F46\u5B83\u662F v${running.version},\u5F53\u524D\u662F v${pkgVersion()}\u3002`) + `
|
|
1800
|
+
\u65E7\u5B9E\u4F8B\u4E0D\u4F1A\u81EA\u52A8\u5347\u7EA7 \u2014\u2014 \u5148\u505C\u6389\u5B83\u518D\u8D77:kill ${pid2 ?? "<lsof -t -iTCP:" + cfg.port + ">"}`
|
|
1801
|
+
);
|
|
1802
|
+
process.exit(1);
|
|
1803
|
+
}
|
|
1778
1804
|
console.log(
|
|
1779
1805
|
`${cyan("\u25CF")} \u672C\u9879\u76EE\u5DF2\u7ECF\u6709\u4E00\u4E2A contactsheet \u5728\u8DD1\u4E86,\u76F4\u63A5\u7528\u5B83:${link(`http://localhost:${cfg.port}/__cs`)}`
|
|
1780
1806
|
);
|
|
1781
1807
|
process.exit(0);
|
|
1782
1808
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
if (await tryListen(p)) {
|
|
1790
|
-
console.warn(
|
|
1791
|
-
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;
|
|
1792
|
-
`) + 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`)
|
|
1793
|
-
);
|
|
1794
|
-
return p;
|
|
1795
|
-
}
|
|
1796
|
-
}
|
|
1797
|
-
throw new Error(`\u4ECE ${cfg.port} \u5F80\u4E0A\u8FDE\u8BD5 20 \u4E2A\u7AEF\u53E3\u90FD\u88AB\u5360,\u7528 --port \u6307\u5B9A\u4E00\u4E2A\u7A7A\u95F2\u7AEF\u53E3`);
|
|
1809
|
+
const pid = occupierPid(cfg.port);
|
|
1810
|
+
throw new Error(
|
|
1811
|
+
`\u7AEF\u53E3 ${cfg.port} \u88AB\u5176\u4ED6\u8FDB\u7A0B\u5360\u7528${pid ? `(PID ${pid})` : ""}\u3002.mcp.json/hook/config \u90FD\u6307\u5411 ${cfg.port},\u6362\u7AEF\u53E3\u5B83\u4EEC\u4F1A\u5168\u90E8\u5931\u8054,\u6240\u4EE5\u4E0D\u81EA\u52A8\u987A\u5EF6 \u2014\u2014
|
|
1812
|
+
\u91CA\u653E\u5B83:kill ${pid ?? `$(lsof -t -nP -iTCP:${cfg.port} -sTCP:LISTEN)`}
|
|
1813
|
+
\u6216\u6362\u7AEF\u53E3:\u6539 contactsheet.config.json \u7684 port \u540E\u91CD\u8DD1 npx contactsheet init(MCP/hook \u4F1A\u8DDF\u7740\u6362)`
|
|
1814
|
+
);
|
|
1798
1815
|
}
|
|
1799
1816
|
|
|
1800
1817
|
// src/watch/index.ts
|