contactsheet 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.
@@ -0,0 +1,59 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>contactsheet</title>
7
+ <link rel="icon" type="image/png" href="/__cs/ui/favicon.png" />
8
+ <link rel="stylesheet" href="/__cs/ui/style.css" />
9
+ <link rel="stylesheet" href="/__cs/ui/style-wall.css" />
10
+ <link rel="stylesheet" href="/__cs/ui/style-pins.css" />
11
+ <link rel="stylesheet" href="/__cs/ui/style-sidebar.css" />
12
+ <link rel="stylesheet" href="/__cs/ui/style-select.css" />
13
+ </head>
14
+ <body data-mode="browse" data-pin="off">
15
+ <!-- 画布视口:世界容器在里面做 translate+scale -->
16
+ <div id="cs-viewport"><div id="cs-world"></div></div>
17
+
18
+ <!-- 左侧图层列表:上=页面(带路由),下=组件 -->
19
+ <aside id="cs-sidebar"></aside>
20
+
21
+ <!-- 屏幕坐标层:hover/选中描边框、pin 输入框 -->
22
+ <div id="cs-fx"></div>
23
+
24
+ <!-- 顶部信息条 -->
25
+ <div id="cs-topbar">
26
+ <button id="cs-sb-toggle" title="收起列表">☰</button>
27
+ <img class="cs-logo" src="/__cs/ui/logo.png" alt="" />
28
+ <span class="cs-brand">contactsheet</span>
29
+ <span id="cs-target">…</span>
30
+ <button id="cs-push" title="一键推送:把 open 批注 + 选中直接注入本项目正在运行的 Claude Code 会话(按工作目录自动绑定)。快捷键 p">推送 ⏎</button>
31
+ <button id="cs-copy" title="把所有 open 批注 + 当前选中打包复制(与 hook 注入终端的格式一致),粘贴到任何 Claude Code 窗口即可。快捷键 y">复制批注</button>
32
+ <span class="cs-zoom" id="cs-zoom">100%</span>
33
+ </div>
34
+
35
+ <!-- 左下角 HUD:模式 + 快捷键 + 反查信息 -->
36
+ <div id="cs-hud">
37
+ <div class="cs-hud-row">
38
+ <span id="cs-mode">浏览</span>
39
+ <span class="cs-hud-hint">
40
+ <b>1</b> 全景 · <b>2</b> 聚焦 · <b>0</b> 100% · <b>双击</b> 交互 · <b>Enter</b> 走查 ·
41
+ <b>c</b> 批注 · <b>p</b> 推送 · <b>Esc</b> 返回
42
+ </span>
43
+ </div>
44
+ <div id="cs-probe">移到画板上反查元素</div>
45
+ </div>
46
+
47
+ <!-- 右侧 args 面板 -->
48
+ <aside id="cs-args" hidden></aside>
49
+
50
+ <!-- 右下角参考图 + 大图浮层 -->
51
+ <div id="cs-refs"></div>
52
+ <div id="cs-lightbox" hidden></div>
53
+
54
+ <!-- 启动失败提示卡 -->
55
+ <div id="cs-boot" hidden></div>
56
+
57
+ <script src="/__cs/ui/app.js"></script>
58
+ </body>
59
+ </html>
Binary file
@@ -0,0 +1,104 @@
1
+ /* pins 模块样式 —— 由对应 agent 维护,避免并行编辑 style.css 冲突 */
2
+
3
+ /* ============================================================
4
+ 1. pin → 气泡的透明桥接
5
+ 气泡是 bottom:22px,底边与 18px 圆点之间隔着一条缝。鼠标穿过这条缝时,
6
+ 事件落到底下的画板 overlay 上 —— 反查高亮会跟着乱闪,点一下还会选中气泡下方的元素
7
+ (用户截图里被高亮的就是它)。250ms 关闭宽限只挡住了"气泡消失",挡不住这件事。
8
+ 办法:把缝补成 pin 自己的命中区,且**只在气泡打开时存在**,平时一像素都不挡画板。
9
+ ============================================================ */
10
+
11
+ /* 一块矩形就够:横向铺满气泡宽度、纵向从圆点下沿一直盖到气泡底边(实测缝宽 8px)。
12
+ 只做"上方一条窄带"是不够的 —— 气泡里的按钮在右下角,从圆点走过去是条斜线,
13
+ 会从圆点**侧面**擦出去(实测 60 步里第 8~11 步落到画板上)。
14
+ 定位基准是 pin 的 padding box(14×14,圆点 18px 减去 2px 边框)。 */
15
+ .cs-pin::after {
16
+ content: "";
17
+ position: absolute;
18
+ left: 50%;
19
+ bottom: -2px; /* 盖住圆点下边框,四角(border-radius 之外)一并收进来 */
20
+ width: 288px; /* 比气泡(268px)略宽,斜着走向最右那个按钮也不掉出去 */
21
+ height: 26px; /* 到 padding box 上沿再往上 10px:压过 8px 的缝,咬进气泡 2px */
22
+ transform: translateX(-50%);
23
+ display: none; /* 平时不存在 = 一像素都不挡画板 */
24
+ }
25
+
26
+ /* 打开的 pin:桥接生效,并抬到同层其它 pin 之上(同锚点的批注只横向错开 20px,挨得很近) */
27
+ .cs-pin.is-open::after { display: block; }
28
+ .cs-pin.is-open { z-index: 6; }
29
+
30
+ /* 纯 :hover 那条只给真鼠标:触屏 tap 会留下假 hover,把这块命中区永久粘在画板上。
31
+ 命中区只会变大、不改 pin 自己的盒子 —— 指针不会被甩出去,没有 hover 自激振荡。
32
+ (JS 的 .is-open 才是主路径,见 pins.ts 的 attachHoverGrace) */
33
+ @media (hover: hover) and (pointer: fine) {
34
+ .cs-pin:hover::after { display: block; }
35
+ .cs-pin:hover { z-index: 6; }
36
+ }
37
+
38
+ /* ============================================================
39
+ 2. 批注挂的参考图(气泡里 / 批注输入框里)
40
+ ============================================================ */
41
+
42
+ .cs-ref-strip {
43
+ display: flex;
44
+ flex-wrap: wrap;
45
+ gap: var(--cs-sp-2);
46
+ margin-top: var(--cs-sp-3);
47
+ }
48
+ .cs-ref-strip:empty { display: none; } /* 没图就当它不存在,别留一条空隙 */
49
+
50
+ .cs-ref-thumb {
51
+ width: 56px;
52
+ padding: 2px;
53
+ border: 1px solid var(--cs-line-2);
54
+ border-radius: var(--cs-r-1);
55
+ background: var(--cs-s2);
56
+ cursor: zoom-in;
57
+ }
58
+ .cs-ref-thumb:hover { border-color: var(--cs-accent); }
59
+ /* 固定比例 + 等比裁切:贴进来的截图长宽不可控,只给宽高会把竖图压扁;
60
+ 槽位尺寸先定死,图到位时也不会把气泡撑得跳一下 */
61
+ .cs-ref-thumb img {
62
+ display: block;
63
+ width: 100%;
64
+ aspect-ratio: 4 / 3;
65
+ object-fit: cover;
66
+ border-radius: 2px;
67
+ background: var(--cs-s3);
68
+ }
69
+ .cs-ref-thumb .cs-ref-name {
70
+ display: block;
71
+ margin-top: 2px;
72
+ color: var(--cs-fg-3);
73
+ font: var(--cs-fs-micro)/1.2 var(--cs-ff-mono);
74
+ white-space: nowrap;
75
+ overflow: hidden;
76
+ text-overflow: ellipsis;
77
+ }
78
+ /* 取不到图(外壳还没有取图路由 / 文件被删)→ 退化成文件名 chip,不留破图标 */
79
+ .cs-ref-thumb.is-broken {
80
+ width: auto;
81
+ max-width: 100%;
82
+ padding: 2px var(--cs-sp-3);
83
+ cursor: default;
84
+ }
85
+ /* 必须显式 none:上面那条 `display:block` 的优先级高于 [hidden] 的 UA 样式,
86
+ 只置 img.hidden 的话破图会以 alt 文本的形态继续占着 4:3 的槽位(实测过) */
87
+ .cs-ref-thumb.is-broken img { display: none; }
88
+ .cs-ref-thumb.is-broken .cs-ref-name { margin-top: 0; color: var(--cs-fg-4); }
89
+
90
+ /* ============================================================
91
+ 3. 右下角全局参考图坞的标题
92
+ 说清楚这一列是"没有归属"的:归属到批注的图在 pin 气泡里,不在这
93
+ ============================================================ */
94
+
95
+ #cs-refs .cs-refs-head {
96
+ order: 1; /* 坞是 column-reverse,标题压在最下面(离屏幕角最近) */
97
+ width: 84px;
98
+ padding: 2px 4px;
99
+ color: var(--cs-fg-3);
100
+ font: var(--cs-fw-bold) var(--cs-fs-micro)/1.3 var(--cs-ff-sans);
101
+ letter-spacing: var(--cs-track-wide);
102
+ text-align: center;
103
+ cursor: help;
104
+ }
@@ -0,0 +1,10 @@
1
+ /* select 模块样式 —— 由对应 agent 维护,避免并行编辑 style.css 冲突 */
2
+
3
+ /* 高亮框的标签:left/top/max-width 由 select.ts 的 placeTag() 每次绘制时按画板可视区
4
+ 内联算出(它是屏幕坐标层的东西,不吃画板的 overflow:hidden,不管就会飘到画板外面)。
5
+ 这里只兜"宽度放不下怎么收尾":长 selector 用省略号截断,绝不靠 nowrap 一路顶出画板。 */
6
+ .cs-box .cs-box-tag {
7
+ overflow: hidden;
8
+ text-overflow: ellipsis;
9
+ pointer-events: none;
10
+ }
@@ -0,0 +1,132 @@
1
+ /* sidebar 模块样式 —— 由对应 agent 维护,避免并行编辑 style.css 冲突 */
2
+
3
+ /* 导航容器的滚动契约由本文件兜底(不依赖 style.css 的那份):
4
+ 两条隐藏滚动条的写法都要写 —— 标准属性管 Firefox,伪元素管 WebKit/Blink,
5
+ 缺哪条就在哪个引擎里露出滚动条并吃掉宽度;contain 保证滚到底不把画布一起带走。 */
6
+ .cs-sb-list {
7
+ overscroll-behavior: contain;
8
+ scrollbar-width: none;
9
+ }
10
+ .cs-sb-list::-webkit-scrollbar {
11
+ display: none;
12
+ }
13
+ /* 滚动条藏了,"下面还有"就得有别的说法:两层跟着内容走的遮罩(local)+ 两层钉在
14
+ 容器边缘的渐隐(scroll)。滚到顶/底时遮罩正好盖住渐隐,只有中间态才现出来 ——
15
+ 纯 CSS,不用监听滚动。颜色全取中性变量。 */
16
+ .cs-sb-list {
17
+ background:
18
+ linear-gradient(var(--cs-s1) 40%, transparent) top / 100% 20px no-repeat local,
19
+ linear-gradient(transparent, var(--cs-s1) 60%) bottom / 100% 20px no-repeat local,
20
+ linear-gradient(var(--cs-line-2), transparent) top / 100% 8px no-repeat scroll,
21
+ linear-gradient(transparent, var(--cs-line-2)) bottom / 100% 8px no-repeat scroll;
22
+ }
23
+
24
+ /* ---------- 可折叠分组 ---------- */
25
+
26
+ .cs-sb-items[hidden] {
27
+ display: none;
28
+ }
29
+
30
+ /* 分区标题行 / 文件标题行:左边折叠按钮撑满,右边批量显隐按钮 —— 两个独立目标 */
31
+ .cs-sb-section,
32
+ .cs-sb-file {
33
+ display: flex;
34
+ align-items: center;
35
+ gap: var(--cs-sp-1);
36
+ margin: 0;
37
+ }
38
+ .cs-sb-section {
39
+ margin-top: var(--cs-sp-5);
40
+ }
41
+ .cs-sb-list > .cs-sb-group:first-child .cs-sb-section {
42
+ margin-top: 0;
43
+ }
44
+ .cs-sb-file {
45
+ margin-top: var(--cs-sp-3);
46
+ }
47
+
48
+ /* 文件分组的内容缩进一档,并用一条极淡的引导线标出层级(纯中性,不上色) */
49
+ .cs-sb-group.is-file > .cs-sb-items {
50
+ margin-left: var(--cs-sp-4);
51
+ padding-left: var(--cs-sp-3);
52
+ border-left: 1px solid var(--cs-line);
53
+ }
54
+
55
+ /* ---------- 折叠按钮 ---------- */
56
+
57
+ .cs-sb-caret {
58
+ flex: 1 1 auto;
59
+ min-width: 0;
60
+ display: flex;
61
+ align-items: center;
62
+ gap: var(--cs-sp-3);
63
+ padding: var(--cs-sp-2);
64
+ border: 0;
65
+ border-radius: var(--cs-r-2);
66
+ background: none;
67
+ color: inherit; /* 字号/字重跟随各自的标题行,折叠功能不改视觉语言 */
68
+ font: inherit;
69
+ letter-spacing: inherit;
70
+ text-align: left;
71
+ cursor: pointer;
72
+ transition: background var(--cs-dur-1) var(--cs-ease), color var(--cs-dur-1) var(--cs-ease);
73
+ }
74
+ .cs-sb-caret:hover {
75
+ background: var(--cs-s2);
76
+ color: var(--cs-fg);
77
+ }
78
+
79
+ .cs-sb-arrow {
80
+ flex: none;
81
+ width: 9px;
82
+ color: var(--cs-fg-3);
83
+ font-size: var(--cs-fs-micro);
84
+ line-height: 1;
85
+ transition: transform var(--cs-dur-1) var(--cs-ease);
86
+ }
87
+ .cs-sb-caret[aria-expanded="true"] .cs-sb-arrow {
88
+ transform: rotate(90deg);
89
+ }
90
+
91
+ .cs-sb-label {
92
+ flex: 0 1 auto;
93
+ min-width: 0;
94
+ overflow: hidden;
95
+ text-overflow: ellipsis;
96
+ white-space: nowrap;
97
+ }
98
+
99
+ /* 文件行的计数安静一点:一行里已经有两处徽记了,别再叠一个胶囊 */
100
+ .cs-sb-file .cs-sb-count {
101
+ padding: 0;
102
+ background: none;
103
+ }
104
+
105
+ /* 单条显隐平时靠 hover 才现身(style.css),键盘走过来时也得现身 ——
106
+ 焦点环画在一个 opacity:0 的按钮上等于没画 */
107
+ .cs-sb-row:focus-within .cs-sb-eye {
108
+ opacity: 1;
109
+ }
110
+
111
+ /* ---------- 批量显隐:三态 ---------- */
112
+
113
+ .cs-sb-bulk {
114
+ flex: none;
115
+ width: 26px;
116
+ padding: var(--cs-sp-2) 0;
117
+ border: 0;
118
+ border-radius: var(--cs-r-1);
119
+ background: none;
120
+ color: var(--cs-fg-3);
121
+ font: var(--cs-fs-xs)/1 var(--cs-ff-sans);
122
+ cursor: pointer;
123
+ transition: background var(--cs-dur-1) var(--cs-ease), color var(--cs-dur-1) var(--cs-ease);
124
+ }
125
+ /* 三态靠字形区分(● 全显 / ◐ 部分 / ◌ 全隐),明度再补一档,不引入任何彩色 */
126
+ .cs-sb-bulk[data-state="all"] { color: var(--cs-fg-2); }
127
+ .cs-sb-bulk[data-state="some"] { color: var(--cs-fg-2); }
128
+ .cs-sb-bulk[data-state="none"] { color: var(--cs-fg-3); }
129
+ .cs-sb-bulk:hover {
130
+ background: var(--cs-s3);
131
+ color: var(--cs-fg);
132
+ }
@@ -0,0 +1,110 @@
1
+ /* wall 模块样式 —— 由对应 agent 维护,避免并行编辑 style.css 冲突 */
2
+
3
+ /* ---------- 组件画板的底色档位 ----------
4
+ iframe 的画布底在浏览器里必然不透明(把嵌入文档的 html/body 设成 transparent,
5
+ 计算值是透明、渲染出来仍是白),所以"融入画布"不是让它透明,而是把底刷成画布同色。
6
+ iframe 内部由 wall.ts 的 applyBgPref 注入同一个色,这里收掉外层的白底与卡片描边,
7
+ 两层一起消失,那块标本才真的读作"墙的一部分"。 */
8
+
9
+ .cs-board[data-kind="component"][data-bg="blend"] .cs-frame {
10
+ background: var(--cs-bg);
11
+ outline-color: transparent;
12
+ box-shadow: none;
13
+ }
14
+ /* 边界不能彻底消失:hover 时浮出一条细线,提示这里仍是一块可拖可点的画板 */
15
+ .cs-board[data-kind="component"][data-bg="blend"]:hover .cs-frame {
16
+ outline-color: var(--cs-line-2);
17
+ }
18
+ /* 真实底色档不用在这里写:iframe 自己刷项目的 --background,
19
+ 加载完成前的兜底白底由 style.css 的 .cs-frame 基础规则提供 */
20
+
21
+ /* 跟随全局的底色按钮比"本板单独覆盖过"的更淡 —— 一眼看出哪几块被特殊对待了 */
22
+ .cs-bgbtn.is-auto {
23
+ opacity: 0.45;
24
+ }
25
+ .cs-board:hover .cs-bgbtn.is-auto,
26
+ .cs-bgbtn.is-auto:hover {
27
+ opacity: 1;
28
+ }
29
+
30
+ /* ---------- 墙工具条(右上角,JS 建元素,不进 index.html) ----------
31
+ 全局底色默认档 + 自动排列:两件事都是整面墙的,塞进每块画板的标题条既放不下、
32
+ 也会让人以为"只影响这一块"。 */
33
+
34
+ #cs-wall-tools {
35
+ position: fixed;
36
+ right: 14px;
37
+ top: 12px;
38
+ z-index: var(--cs-z-chrome);
39
+ display: flex;
40
+ align-items: center;
41
+ gap: var(--cs-sp-4);
42
+ padding: var(--cs-sp-2) var(--cs-sp-4);
43
+ border: 1px solid var(--cs-line);
44
+ border-radius: var(--cs-radius);
45
+ background: color-mix(in srgb, var(--cs-surface) 88%, transparent);
46
+ backdrop-filter: blur(6px);
47
+ color: var(--cs-fg-3);
48
+ font: var(--cs-fs-xs) / 1 var(--cs-ff-sans);
49
+ user-select: none;
50
+ box-shadow: var(--cs-e1);
51
+ }
52
+ /* args 面板开着时让开,别被压在下面(与 #cs-refs 同一套规则) */
53
+ body[data-args="open"] #cs-wall-tools {
54
+ right: 314px;
55
+ }
56
+ /* 走查模式看的是一块画板本身,工具条让开 */
57
+ body[data-mode="review"] #cs-wall-tools {
58
+ display: none;
59
+ }
60
+
61
+ #cs-wall-tools .cs-wt-label {
62
+ color: var(--cs-fg-3);
63
+ letter-spacing: var(--cs-track-tight);
64
+ white-space: nowrap;
65
+ }
66
+
67
+ #cs-wall-tools .cs-seg {
68
+ display: flex;
69
+ border: 1px solid var(--cs-line-2);
70
+ border-radius: var(--cs-r-1);
71
+ overflow: hidden;
72
+ }
73
+ #cs-wall-tools .cs-seg-btn {
74
+ padding: 3px var(--cs-sp-4);
75
+ border: 0;
76
+ background: transparent;
77
+ color: var(--cs-fg-3);
78
+ font: var(--cs-fs-xs) / 1.2 var(--cs-ff-sans);
79
+ white-space: nowrap;
80
+ cursor: pointer;
81
+ transition: background var(--cs-dur-1) var(--cs-ease), color var(--cs-dur-1) var(--cs-ease);
82
+ }
83
+ #cs-wall-tools .cs-seg-btn + .cs-seg-btn {
84
+ border-left: 1px solid var(--cs-line-2);
85
+ }
86
+ #cs-wall-tools .cs-seg-btn:hover {
87
+ background: var(--cs-s2);
88
+ color: var(--cs-fg);
89
+ }
90
+ #cs-wall-tools .cs-seg-btn.is-on {
91
+ background: var(--cs-s4);
92
+ color: var(--cs-fg);
93
+ }
94
+
95
+ #cs-wall-tools .cs-wt-btn {
96
+ padding: 3px var(--cs-sp-4);
97
+ border: 1px solid var(--cs-line-2);
98
+ border-radius: var(--cs-r-1);
99
+ background: var(--cs-s2);
100
+ color: var(--cs-fg-2);
101
+ font: var(--cs-fw-medium) var(--cs-fs-xs) / 1.2 var(--cs-ff-sans);
102
+ white-space: nowrap;
103
+ cursor: pointer;
104
+ transition: background var(--cs-dur-1) var(--cs-ease), color var(--cs-dur-1) var(--cs-ease);
105
+ }
106
+ #cs-wall-tools .cs-wt-btn:hover {
107
+ background: var(--cs-s3);
108
+ border-color: var(--cs-line-3);
109
+ color: var(--cs-fg);
110
+ }