dsh-select-quote 0.3.5 → 0.3.6
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 +11 -2
- package/icon.svg +9 -0
- package/lib/client.js +219 -221
- package/locale/en.json +6 -0
- package/locale/ru.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -48,10 +48,19 @@ dsh web --patch "$PWD/dev.patch.yml"
|
|
|
48
48
|
|
|
49
49
|
界面语言由 dsh 决定,插件不再自己判断浏览器语言。在 **设置 → 通用 → 语言** 里切换。
|
|
50
50
|
|
|
51
|
+
## 插件页展示
|
|
52
|
+
|
|
53
|
+
插件管理页与设置项里的标题、描述、图标来自包自带的资源,宿主读取它们时**不会激活插件**:
|
|
54
|
+
|
|
55
|
+
- `locale/*.json` 的 `meta.title` / `meta.description`(`en.json` 是发现入口,其余语言文件与它同目录同名)
|
|
56
|
+
- `icon.svg`(SVG / PNG / JPEG / WebP,≤256 KiB,必须在包内)
|
|
57
|
+
|
|
58
|
+
标题按当前语言取,取不到回退 `package.json` 的 `name` / `description`;图标缺失时用面板的默认插画。要加一种语言,往 `locale/` 放一个同名 JSON 即可,`pnpm run check` 会按宿主的规则逐条校验(语言 id 文件名、字段非空、图标类型与体积、`exports` 与 `files` 覆盖、`npm pack` 实际清单)。
|
|
59
|
+
|
|
51
60
|
|
|
52
61
|
## 已知限制
|
|
53
62
|
|
|
54
|
-
-
|
|
63
|
+
- **待发批注按会话持久化**:刷新页面、重启客户端后回到原会话,未发送的批注会原样回来(与草稿同在 localStorage,键各自独立);保留最近 20 个会话、7 天。发送即清除,切换会话跟着切,hero 里换工作区等于换会话,批注不跟着搬。
|
|
55
64
|
- **占用了用户气泡的渲染**:本插件以最低优先级替换内置 `user` 节点。其它插件若替换同一个键(例如 `dsh-easyrewrite` 用 `-1`),会被本插件屏蔽。
|
|
56
65
|
- **卡片宽度有限**:输入框内每条摘要最宽约为输入框的 1/4,超长标题走省略号。
|
|
57
66
|
- **划词只在对话区生效**:不介入编辑器、终端等面板。
|
|
@@ -80,7 +89,7 @@ pnpm run watch # 增量重建
|
|
|
80
89
|
|
|
81
90
|
依赖用 pnpm 管理(锁文件 `pnpm-lock.yaml`);构建产物 `lib/` 随源码一起提交,运行时直接读它。
|
|
82
91
|
|
|
83
|
-
`pnpm run verify` 无头执行产物,断言 8 项贡献都注册上,并检查三份词典的键与 `{placeholder}` 在每种语言里都齐备(复数消息必须有 `.other`
|
|
92
|
+
`pnpm run verify` 无头执行产物,断言 8 项贡献都注册上,并检查三份词典的键与 `{placeholder}` 在每种语言里都齐备(复数消息必须有 `.other` 兜底),外加插件页展示元信息(locale + icon)按宿主的读取规则解析、并核对 `npm pack` 的实际发货清单。
|
|
84
93
|
|
|
85
94
|
### 热更新到桌面客户端
|
|
86
95
|
|
package/icon.svg
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M25 25H23C22.3705 25 21.7777 25.2964 21.4 25.8L20.5 27C20.5 27 19.2503 29 18 29C16.7497 29 15.5 27 15.5 27L14.6 25.8C14.2223 25.2964 13.6295 25 13 25H11C9.96813 24.9974 8.86434 24.4622 8.13596 23.7313C7.40758 23.0004 6.99902 22.0103 7 20.9784V11C6.99935 9.96834 7.40805 8.87567 8.13639 8.14504C8.86474 7.4144 9.96834 7.00259 11 7H25C26.0317 7.0026 27.1353 7.41441 27.8636 8.14504C28.592 8.87568 29.0007 9.96835 29 11V20.9784C29.001 22.0103 28.5924 23.0004 27.864 23.7313C27.1357 24.4622 26.0319 24.9974 25 25ZM21.3317 15.0265H19.7047C19.4286 15.0265 19.2047 14.8026 19.2047 14.5265V13.018C19.2047 12.3909 18.6681 11.8824 18.0043 11.8824C17.3405 11.8824 16.8026 12.3909 16.8026 13.018V14.5265C16.8026 14.8026 16.5788 15.0265 16.3026 15.0265H14.6756C14.0131 15.0265 13.474 15.5338 13.474 16.1609C13.474 16.788 14.0118 17.2965 14.6756 17.2965H16.3026C16.5788 17.2965 16.8026 17.5204 16.8026 17.7965V19.3049C16.8026 19.9308 17.3405 20.4394 18.0043 20.4394C18.6681 20.4394 19.2059 19.9308 19.2059 19.3049V17.7965C19.2059 17.5204 19.4298 17.2965 19.7059 17.2965H21.3329C21.9967 17.2965 22.5346 16.788 22.5346 16.1609C22.5346 15.5338 21.9967 15.0265 21.3317 15.0265Z" fill="url(#paint0_linear_7_8)"/>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="paint0_linear_7_8" x1="29" y1="3.5" x2="14" y2="32" gradientUnits="userSpaceOnUse">
|
|
5
|
+
<stop stop-color="#B2FFDA"/>
|
|
6
|
+
<stop offset="1" stop-color="#0099FF"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
</svg>
|
package/lib/client.js
CHANGED
|
@@ -95,7 +95,6 @@ body {
|
|
|
95
95
|
--dsq-font-size: var(--dsh-content-font-size, 14px);
|
|
96
96
|
--dsq-font-delta: var(--dsh-content-font-delta, 0px);
|
|
97
97
|
--dsq-bg-raised: var(--dsw-alias-bg-layer-1, #fff);
|
|
98
|
-
--dsq-bg-panel: var(--dsw-alias-bg-layer-2, #fff);
|
|
99
98
|
--dsq-bg-toast: var(--dsw-alias-bg-layer-3, rgba(0, 0, 0, 0.78));
|
|
100
99
|
--dsq-line: var(--dsw-alias-border-l2, rgba(0, 0, 0, 0.1));
|
|
101
100
|
--dsq-line-divider: var(--dsw-alias-border-l4, rgba(0, 0, 0, 0.12));
|
|
@@ -106,10 +105,9 @@ body {
|
|
|
106
105
|
--dsq-label-2: var(--dsw-alias-label-secondary, rgba(0, 0, 0, 0.65));
|
|
107
106
|
--dsq-label-3: var(--dsw-alias-label-tertiary, rgba(0, 0, 0, 0.45));
|
|
108
107
|
--dsq-link: var(--dsw-alias-link, rgba(0, 0, 0, 0.65));
|
|
109
|
-
--dsq-shadow-pop: var(--dsw-elevation-prominent, 0 3px 8px rgba(0, 0, 0, 0.04), 0 0 20px rgba(0, 0, 0, 0.05));
|
|
110
|
-
--dsq-shadow-soft: var(--dsw-elevation-soft, 0 1px 2px rgba(0, 0, 0, 0.03), 0 6px 20px rgba(0, 0, 0, 0.06));
|
|
111
108
|
--dsq-bubble: var(--dsw-specific-bubble, rgba(0, 0, 0, 0.05));
|
|
112
|
-
--dsq-menu: var(--dsw-
|
|
109
|
+
--dsq-menu: var(--dsw-menu-surface-fill, rgba(248, 249, 250, .58));
|
|
110
|
+
--dsq-menu-backdrop: var(--dsw-menu-backdrop-filter, blur(40px) saturate(150%));
|
|
113
111
|
--dsq-accent-400: var(--dsw-static-blue-400, #1677ff);
|
|
114
112
|
--dsq-accent: var(--dsw-static-blue-500, #3b82f6);
|
|
115
113
|
--dsq-accent-tint: var(--dsw-static-blue-50p, #eaf3ff);
|
|
@@ -126,23 +124,91 @@ body {
|
|
|
126
124
|
${atomRules}
|
|
127
125
|
|
|
128
126
|
/* Layer 3 — components. Floating selection toolbar. */
|
|
127
|
+
|
|
128
|
+
/* 入场动画位置(2026-10-19 定稿):scale + Y 位移都放在 .dsq_toolbar 容器上。
|
|
129
|
+
规范层面 transform 不形成 backdrop root(opacity 才会),磨砂不受影响;用户已在
|
|
130
|
+
真实客户端验证过容器 scale 没问题。若日后换环境再出问题,回退路径:去掉容器上的
|
|
131
|
+
animation,给 .dsq_toolbarInner 加回 dsq-rise-in(关键帧还在)。 */
|
|
132
|
+
@keyframes dsq-toolbar-in {
|
|
133
|
+
from {
|
|
134
|
+
transform: translate(-50%, -100%) translateY(4px) scale(0.96);
|
|
135
|
+
}
|
|
136
|
+
to {
|
|
137
|
+
transform: translate(-50%, -100%) translateY(0) scale(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* 内容层的 rise:悬停面板的列表在用;工具条改用容器 scale 后这条在这里待命。 */
|
|
142
|
+
@keyframes dsq-rise-in {
|
|
143
|
+
from {
|
|
144
|
+
transform: translateY(4px);
|
|
145
|
+
}
|
|
146
|
+
to {
|
|
147
|
+
transform: translateY(0);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* 宿主菜单材质(官方 material 图层同款):填充走 --dsw-menu-surface-fill,模糊走
|
|
152
|
+
--dsw-menu-backdrop-filter(恒为 blur(40px) saturate(150%)),亮暗自动翻。
|
|
153
|
+
画在 z-index:-1 的独立图层上,内容在它之上:模糊只取身后的页面,不会把
|
|
154
|
+
面板里的文字一起糊掉。工具条、评论气泡、悬停面板三个浮层共用这一条。
|
|
155
|
+
注意:macOS 上宿主另有一个 --dsw-specific-menu(覆写成 94% 不透明)给它的
|
|
156
|
+
菜单用;这里按需求用 surface-fill,所以我们的浮层会比宿主自己的菜单更透。 */
|
|
157
|
+
.dsq_toolbar::before,
|
|
158
|
+
.dsq_commentPop::before,
|
|
159
|
+
.dsq_summaryHoverInner::before {
|
|
160
|
+
content: "";
|
|
161
|
+
position: absolute;
|
|
162
|
+
inset: 0;
|
|
163
|
+
z-index: -1;
|
|
164
|
+
border-radius: inherit;
|
|
165
|
+
background: var(--dsq-menu);
|
|
166
|
+
backdrop-filter: var(--dsq-menu-backdrop);
|
|
167
|
+
-webkit-backdrop-filter: var(--dsq-menu-backdrop);
|
|
168
|
+
pointer-events: none;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* 覆层不描边:elevation-prominent 自带的那圈 0 0 0 .5px(--dsw-elevation-stroke-color,
|
|
172
|
+
默认 border-l4)对磨砂覆层是多余的——边缘自带材质分界,再勾一道反而抢视觉,覆写成
|
|
173
|
+
更淡的 border-l1。
|
|
174
|
+
两个坑,改之前先读:① 这两行必须写在三个浮层自己的选择器上。写在 body 上会随继承
|
|
175
|
+
影响页面里其它依赖默认值的面板(宿主带 [data-menu-material] 的菜单有自己的覆写,
|
|
176
|
+
不受影响)。② --dsq-shadow 也必须在这儿声明,不能留在 body:自定义属性是在**声明处**
|
|
177
|
+
展开的,--dsq-shadow 留在 body 上时 var(--dsw-elevation-prominent) 会在 body 就被
|
|
178
|
+
替换成 border-l4 那一版并继承下来,浮层上的描边覆写根本够不着;跟着声明挪到这里,
|
|
179
|
+
整条链才在浮层元素上取值。 */
|
|
180
|
+
.dsq_toolbar,
|
|
181
|
+
.dsq_commentPop,
|
|
182
|
+
.dsq_summaryHoverInner {
|
|
183
|
+
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
184
|
+
--dsq-shadow: var(--dsw-elevation-prominent, 0 0 0 .5px rgba(0, 0, 0, 0.04), 0 3px 8px rgba(0, 0, 0, 0.04), 0 0 20px rgba(0, 0, 0, 0.05));
|
|
185
|
+
}
|
|
186
|
+
|
|
129
187
|
.dsq_toolbar {
|
|
130
188
|
position: fixed;
|
|
131
189
|
z-index: 10060;
|
|
132
190
|
transform: translate(-50%, -100%);
|
|
133
191
|
gap: 2px;
|
|
134
192
|
padding: 4px;
|
|
135
|
-
/* 无边框:--dsq-shadow-
|
|
136
|
-
|
|
193
|
+
/* 无边框:--dsq-shadow(宿主的 elevation-prominent)自带 0 0 0 .5px 的描边环,
|
|
194
|
+
由它勾勒轮廓,所以这里不要再画 border,否则描边会和环叠成两条线。 */
|
|
137
195
|
border: 0;
|
|
138
196
|
border-radius: 12px;
|
|
139
|
-
|
|
140
|
-
box-shadow: var(--dsq-shadow
|
|
197
|
+
/* 底色与模糊都在上面的材质图层里,这里不再画背景 */
|
|
198
|
+
box-shadow: var(--dsq-shadow);
|
|
141
199
|
pointer-events: auto;
|
|
142
|
-
/*
|
|
200
|
+
/* A/B:scale 入场放在容器上(材质图层归它所有)。关键帧自带 translate(-50%,-100%),
|
|
201
|
+
所以缩放不会破坏居中。若真实客户端里磨砂在放大期间消失,改回内容层方案。 */
|
|
143
202
|
animation: dsq-toolbar-in 0.16s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
144
203
|
}
|
|
145
204
|
|
|
205
|
+
/* 按钮行:只负责布局;入场动画在容器上(见 .dsq_toolbar 的 A/B 说明)。 */
|
|
206
|
+
.dsq_toolbarInner {
|
|
207
|
+
display: inline-flex;
|
|
208
|
+
align-items: center;
|
|
209
|
+
gap: 2px;
|
|
210
|
+
}
|
|
211
|
+
|
|
146
212
|
.dsq_button {
|
|
147
213
|
color: var(--dsq-label);
|
|
148
214
|
font: inherit;
|
|
@@ -397,38 +463,44 @@ ${atomRules}
|
|
|
397
463
|
}
|
|
398
464
|
|
|
399
465
|
/* Must come after .dsq_summaryHover (same class weight otherwise loses to display:none). */
|
|
466
|
+
/* 容器上不做 opacity 过渡、也不做 scale 入场。opacity 那条是规范实锤:祖先一带上
|
|
467
|
+
opacity<1 就成了 backdrop root,::before 采样不到身后的页面,磨砂在整段淡入里
|
|
468
|
+
消失、结束才糊上——所以 opacity 过渡删掉,只留 place() 里那次 0→1 瞬时翻转
|
|
469
|
+
(「定位前不可见」的守卫)。scale 一并去掉是保守起见:容器彻底静止,采样区域
|
|
470
|
+
不会逐帧移动;入场感交给内层列表的 rise(内容不是材质的祖先)。 */
|
|
400
471
|
.dsq_summaryHover.dsq_summaryHoverOpen {
|
|
401
472
|
display: flex;
|
|
402
|
-
transition: opacity 0.12s ease;
|
|
403
|
-
animation: dsq-panel-in 0.16s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
.dsq_summaryHover[data-side='bottom'] {
|
|
407
|
-
transform-origin: top center;
|
|
408
473
|
}
|
|
409
474
|
|
|
410
|
-
.
|
|
411
|
-
|
|
475
|
+
.dsq_summaryHoverInner {
|
|
476
|
+
position: relative;
|
|
477
|
+
gap: 4px;
|
|
478
|
+
padding: 4px;
|
|
479
|
+
border-radius: 12px;
|
|
480
|
+
/* 不画 border:--dsq-shadow(宿主的 elevation-prominent)自带 0 0 0 .5px 的
|
|
481
|
+
描边环,再画一遍会叠成两条线。底色与模糊都在材质图层里。 */
|
|
482
|
+
box-shadow: var(--dsq-shadow);
|
|
412
483
|
}
|
|
413
484
|
|
|
414
|
-
|
|
485
|
+
/* 滚动只放在列表上:材质图层是绝对定位的,留在会滚动的容器里会跟着内容跑掉,
|
|
486
|
+
滚到底部时面板顶部就露空。入场 rise 也在这层——内容不是材质图层的祖先,
|
|
487
|
+
动它不会切断磨砂的采样源。 */
|
|
488
|
+
.dsq_summaryList {
|
|
415
489
|
gap: 4px;
|
|
416
|
-
max-height: min(420px, calc(100vh - 120px));
|
|
417
490
|
overflow-y: auto;
|
|
418
491
|
overscroll-behavior: contain;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
border: 0.5px solid var(--dsq-line);
|
|
422
|
-
/* Frosted panel: the menu token at partial alpha over a blur. */
|
|
423
|
-
background: color-mix(in srgb, var(--dsq-bg-raised) 50%, transparent);
|
|
424
|
-
backdrop-filter: blur(14px) saturate(1.4);
|
|
425
|
-
-webkit-backdrop-filter: blur(14px) saturate(1.4);
|
|
426
|
-
box-shadow: var(--dsq-shadow-pop);
|
|
492
|
+
max-height: min(412px, calc(100vh - 128px));
|
|
493
|
+
animation: dsq-rise-in 0.16s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
427
494
|
}
|
|
428
495
|
|
|
496
|
+
/* 老浏览器没有 backdrop-filter:图层退成不透明底色,否则半透明直接糊在文字上。 */
|
|
429
497
|
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
430
|
-
.
|
|
431
|
-
|
|
498
|
+
.dsq_toolbar::before,
|
|
499
|
+
.dsq_commentPop::before,
|
|
500
|
+
.dsq_summaryHoverInner::before {
|
|
501
|
+
background: var(--dsq-bg-raised);
|
|
502
|
+
backdrop-filter: none;
|
|
503
|
+
-webkit-backdrop-filter: none;
|
|
432
504
|
}
|
|
433
505
|
}
|
|
434
506
|
|
|
@@ -606,10 +678,8 @@ ${atomRules}
|
|
|
606
678
|
gap: 8px;
|
|
607
679
|
padding: 8px;
|
|
608
680
|
border-radius: 16px;
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
-webkit-backdrop-filter: blur(14px) saturate(1.4);
|
|
612
|
-
box-shadow: var(--dsq-shadow-pop);
|
|
681
|
+
/* 底色与模糊都在材质图层里 */
|
|
682
|
+
box-shadow: var(--dsq-shadow);
|
|
613
683
|
}
|
|
614
684
|
|
|
615
685
|
.dsq_commentInput {
|
|
@@ -821,26 +891,6 @@ ${atomRules}
|
|
|
821
891
|
/* Layer 4 — host surface: keyframes, theme flips, and the two rules that reach
|
|
822
892
|
into product-owned DOM. */
|
|
823
893
|
|
|
824
|
-
@keyframes dsq-toolbar-in {
|
|
825
|
-
from {
|
|
826
|
-
opacity: 0;
|
|
827
|
-
transform: translate(-50%, -100%) translateY(4px);
|
|
828
|
-
}
|
|
829
|
-
to {
|
|
830
|
-
opacity: 1;
|
|
831
|
-
transform: translate(-50%, -100%);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
@keyframes dsq-panel-in {
|
|
836
|
-
from {
|
|
837
|
-
transform: scale(0.96);
|
|
838
|
-
}
|
|
839
|
-
to {
|
|
840
|
-
transform: scale(1);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
894
|
@keyframes dsq-ann-flash {
|
|
845
895
|
0% {
|
|
846
896
|
background: var(--dsq-mark-color);
|
|
@@ -882,6 +932,7 @@ function ensureToolbarStyles() {
|
|
|
882
932
|
}
|
|
883
933
|
const styles = {
|
|
884
934
|
toolbar: "dsq_toolbar",
|
|
935
|
+
toolbarInner: "dsq_toolbarInner",
|
|
885
936
|
button: "dsq_button",
|
|
886
937
|
buttonAddComment: "dsq_buttonAddComment",
|
|
887
938
|
divider: "dsq_divider",
|
|
@@ -914,6 +965,7 @@ const styles = {
|
|
|
914
965
|
annEditCancel: "dsq_annEditCancel",
|
|
915
966
|
annEditSave: "dsq_annEditSave",
|
|
916
967
|
summaryHoverInner: "dsq_summaryHoverInner",
|
|
968
|
+
summaryList: "dsq_summaryList",
|
|
917
969
|
summaryItemButton: "dsq_summaryItemButton",
|
|
918
970
|
summaryHoverOpen: "dsq_summaryHoverOpen",
|
|
919
971
|
summaryItemActions: "dsq_summaryItemActions",
|
|
@@ -2052,7 +2104,7 @@ function AnnotationSummary({ count, items, onRemoveAll, onItemSelect, onItemRemo
|
|
|
2052
2104
|
const { top, left, side } = placePopover(root.getBoundingClientRect(), {
|
|
2053
2105
|
width: pop.offsetWidth || 300,
|
|
2054
2106
|
height: pop.offsetHeight || 220
|
|
2055
|
-
}, "
|
|
2107
|
+
}, "top", 0);
|
|
2056
2108
|
pop.style.top = `${top}px`;
|
|
2057
2109
|
pop.style.left = `${left}px`;
|
|
2058
2110
|
pop.style.paddingTop = side === "bottom" ? "12px" : "12px";
|
|
@@ -2196,109 +2248,112 @@ function AnnotationSummary({ count, items, onRemoveAll, onItemSelect, onItemRemo
|
|
|
2196
2248
|
onPointerLeave,
|
|
2197
2249
|
children: (0, react_jsx_runtime.jsx)("div", {
|
|
2198
2250
|
className: cx(A.col, styles.summaryHoverInner),
|
|
2199
|
-
children:
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
className:
|
|
2205
|
-
children: [
|
|
2206
|
-
(
|
|
2207
|
-
className: cx(A.keep, A.gridMid, styles.summaryItemBadge),
|
|
2208
|
-
"aria-hidden": true,
|
|
2209
|
-
children: String(i + 1)
|
|
2210
|
-
}),
|
|
2211
|
-
(0, react_jsx_runtime.jsx)("div", {
|
|
2212
|
-
className: styles.summaryItemLabel,
|
|
2213
|
-
children: t("common.selectedText")
|
|
2214
|
-
}),
|
|
2215
|
-
!isEditing && (onItemRemove || onItemEdit) ? (0, react_jsx_runtime.jsxs)("div", {
|
|
2216
|
-
className: cx(A.row, styles.summaryItemActions),
|
|
2217
|
-
children: [onItemEdit ? (0, react_jsx_runtime.jsx)("button", {
|
|
2218
|
-
type: "button",
|
|
2219
|
-
className: cx(A.plain, A.gridMid, styles.summaryItemAction),
|
|
2220
|
-
"aria-label": t("summary.edit", { index: i + 1 }),
|
|
2221
|
-
onClick: () => startEdit(i),
|
|
2222
|
-
children: (0, react_jsx_runtime.jsx)(LucideIcon, { name: "pencil" })
|
|
2223
|
-
}) : null, onItemRemove ? (0, react_jsx_runtime.jsx)("button", {
|
|
2224
|
-
type: "button",
|
|
2225
|
-
className: cx(A.plain, A.gridMid, styles.summaryItemAction),
|
|
2226
|
-
"aria-label": t("summary.remove", { index: i + 1 }),
|
|
2227
|
-
onClick: () => onItemRemove(i, item),
|
|
2228
|
-
children: (0, react_jsx_runtime.jsx)(LucideIcon, { name: "trash-2" })
|
|
2229
|
-
}) : null]
|
|
2230
|
-
}) : null
|
|
2231
|
-
]
|
|
2232
|
-
}), isEditing ? (0, react_jsx_runtime.jsxs)("div", {
|
|
2233
|
-
className: styles.summaryItemMain,
|
|
2234
|
-
children: [
|
|
2235
|
-
(0, react_jsx_runtime.jsx)("div", {
|
|
2236
|
-
className: styles.annEditText,
|
|
2237
|
-
children: item.text
|
|
2238
|
-
}),
|
|
2239
|
-
(0, react_jsx_runtime.jsx)("div", {
|
|
2240
|
-
className: styles.summaryItemLabel,
|
|
2241
|
-
children: t("summary.userComment")
|
|
2242
|
-
}),
|
|
2243
|
-
(0, react_jsx_runtime.jsx)("textarea", {
|
|
2244
|
-
ref: editInputRef,
|
|
2245
|
-
className: cx(A.box, styles.annEditInput),
|
|
2246
|
-
placeholder: t("common.commentPlaceholder"),
|
|
2247
|
-
value: editDraft,
|
|
2248
|
-
onChange: (e) => setEditDraft(e.target.value),
|
|
2249
|
-
onKeyDown: (e) => {
|
|
2250
|
-
if (e.key === "Escape") {
|
|
2251
|
-
e.preventDefault();
|
|
2252
|
-
setEditing(null);
|
|
2253
|
-
return;
|
|
2254
|
-
}
|
|
2255
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
2256
|
-
e.preventDefault();
|
|
2257
|
-
commitEdit();
|
|
2258
|
-
}
|
|
2259
|
-
}
|
|
2260
|
-
}),
|
|
2261
|
-
(0, react_jsx_runtime.jsxs)("div", {
|
|
2262
|
-
className: cx(A.row, styles.annEditActions),
|
|
2263
|
-
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
2264
|
-
type: "button",
|
|
2265
|
-
className: cx(A.plain, styles.annEditCancel),
|
|
2266
|
-
onClick: () => setEditing(null),
|
|
2267
|
-
children: t("common.cancel")
|
|
2268
|
-
}), (0, react_jsx_runtime.jsx)("button", {
|
|
2269
|
-
type: "button",
|
|
2270
|
-
className: styles.annEditSave,
|
|
2271
|
-
onClick: commitEdit,
|
|
2272
|
-
children: t("common.save")
|
|
2273
|
-
})]
|
|
2274
|
-
})
|
|
2275
|
-
]
|
|
2276
|
-
}) : (0, react_jsx_runtime.jsx)("div", {
|
|
2277
|
-
className: styles.summaryItemMain,
|
|
2278
|
-
children: (0, react_jsx_runtime.jsxs)("button", {
|
|
2279
|
-
type: "button",
|
|
2280
|
-
className: cx(A.plain, styles.summaryItemButton),
|
|
2281
|
-
onClick: () => {
|
|
2282
|
-
onItemSelect?.(i, item);
|
|
2283
|
-
closePop();
|
|
2284
|
-
},
|
|
2251
|
+
children: (0, react_jsx_runtime.jsx)("div", {
|
|
2252
|
+
className: cx(A.col, styles.summaryList),
|
|
2253
|
+
children: items.map((item, i) => {
|
|
2254
|
+
const isEditing = editing === i;
|
|
2255
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
2256
|
+
className: styles.summaryItem,
|
|
2257
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
2258
|
+
className: cx(A.row, styles.summaryItemHead),
|
|
2285
2259
|
children: [
|
|
2286
2260
|
(0, react_jsx_runtime.jsx)("div", {
|
|
2287
|
-
className: styles.
|
|
2288
|
-
|
|
2261
|
+
className: cx(A.keep, A.gridMid, styles.summaryItemBadge),
|
|
2262
|
+
"aria-hidden": true,
|
|
2263
|
+
children: String(i + 1)
|
|
2289
2264
|
}),
|
|
2290
2265
|
(0, react_jsx_runtime.jsx)("div", {
|
|
2291
2266
|
className: styles.summaryItemLabel,
|
|
2292
|
-
children: t("
|
|
2267
|
+
children: t("common.selectedText")
|
|
2268
|
+
}),
|
|
2269
|
+
!isEditing && (onItemRemove || onItemEdit) ? (0, react_jsx_runtime.jsxs)("div", {
|
|
2270
|
+
className: cx(A.row, styles.summaryItemActions),
|
|
2271
|
+
children: [onItemEdit ? (0, react_jsx_runtime.jsx)("button", {
|
|
2272
|
+
type: "button",
|
|
2273
|
+
className: cx(A.plain, A.gridMid, styles.summaryItemAction),
|
|
2274
|
+
"aria-label": t("summary.edit", { index: i + 1 }),
|
|
2275
|
+
onClick: () => startEdit(i),
|
|
2276
|
+
children: (0, react_jsx_runtime.jsx)(LucideIcon, { name: "pencil" })
|
|
2277
|
+
}) : null, onItemRemove ? (0, react_jsx_runtime.jsx)("button", {
|
|
2278
|
+
type: "button",
|
|
2279
|
+
className: cx(A.plain, A.gridMid, styles.summaryItemAction),
|
|
2280
|
+
"aria-label": t("summary.remove", { index: i + 1 }),
|
|
2281
|
+
onClick: () => onItemRemove(i, item),
|
|
2282
|
+
children: (0, react_jsx_runtime.jsx)(LucideIcon, { name: "trash-2" })
|
|
2283
|
+
}) : null]
|
|
2284
|
+
}) : null
|
|
2285
|
+
]
|
|
2286
|
+
}), isEditing ? (0, react_jsx_runtime.jsxs)("div", {
|
|
2287
|
+
className: styles.summaryItemMain,
|
|
2288
|
+
children: [
|
|
2289
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
2290
|
+
className: styles.annEditText,
|
|
2291
|
+
children: item.text
|
|
2293
2292
|
}),
|
|
2294
2293
|
(0, react_jsx_runtime.jsx)("div", {
|
|
2295
|
-
className:
|
|
2296
|
-
children:
|
|
2294
|
+
className: styles.summaryItemLabel,
|
|
2295
|
+
children: t("summary.userComment")
|
|
2296
|
+
}),
|
|
2297
|
+
(0, react_jsx_runtime.jsx)("textarea", {
|
|
2298
|
+
ref: editInputRef,
|
|
2299
|
+
className: cx(A.box, styles.annEditInput),
|
|
2300
|
+
placeholder: t("common.commentPlaceholder"),
|
|
2301
|
+
value: editDraft,
|
|
2302
|
+
onChange: (e) => setEditDraft(e.target.value),
|
|
2303
|
+
onKeyDown: (e) => {
|
|
2304
|
+
if (e.key === "Escape") {
|
|
2305
|
+
e.preventDefault();
|
|
2306
|
+
setEditing(null);
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
2310
|
+
e.preventDefault();
|
|
2311
|
+
commitEdit();
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
}),
|
|
2315
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
2316
|
+
className: cx(A.row, styles.annEditActions),
|
|
2317
|
+
children: [(0, react_jsx_runtime.jsx)("button", {
|
|
2318
|
+
type: "button",
|
|
2319
|
+
className: cx(A.plain, styles.annEditCancel),
|
|
2320
|
+
onClick: () => setEditing(null),
|
|
2321
|
+
children: t("common.cancel")
|
|
2322
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
2323
|
+
type: "button",
|
|
2324
|
+
className: styles.annEditSave,
|
|
2325
|
+
onClick: commitEdit,
|
|
2326
|
+
children: t("common.save")
|
|
2327
|
+
})]
|
|
2297
2328
|
})
|
|
2298
2329
|
]
|
|
2299
|
-
})
|
|
2300
|
-
|
|
2301
|
-
|
|
2330
|
+
}) : (0, react_jsx_runtime.jsx)("div", {
|
|
2331
|
+
className: styles.summaryItemMain,
|
|
2332
|
+
children: (0, react_jsx_runtime.jsxs)("button", {
|
|
2333
|
+
type: "button",
|
|
2334
|
+
className: cx(A.plain, styles.summaryItemButton),
|
|
2335
|
+
onClick: () => {
|
|
2336
|
+
onItemSelect?.(i, item);
|
|
2337
|
+
closePop();
|
|
2338
|
+
},
|
|
2339
|
+
children: [
|
|
2340
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
2341
|
+
className: styles.summaryItemText,
|
|
2342
|
+
children: item.title
|
|
2343
|
+
}),
|
|
2344
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
2345
|
+
className: styles.summaryItemLabel,
|
|
2346
|
+
children: t("summary.userComment")
|
|
2347
|
+
}),
|
|
2348
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
2349
|
+
className: item.comment ? styles.summaryItemText : `${styles.summaryItemText} ${styles.summaryItemTextEmpty}`,
|
|
2350
|
+
children: item.comment || t("summary.noComment")
|
|
2351
|
+
})
|
|
2352
|
+
]
|
|
2353
|
+
})
|
|
2354
|
+
})]
|
|
2355
|
+
}, item.id ?? `${i}`);
|
|
2356
|
+
})
|
|
2302
2357
|
})
|
|
2303
2358
|
})
|
|
2304
2359
|
}) : null;
|
|
@@ -2915,11 +2970,7 @@ function SelectionToolbar({ sessionId }) {
|
|
|
2915
2970
|
"data-dsq-toolbar": "true",
|
|
2916
2971
|
onPointerDown: handleToolbarPointerDown,
|
|
2917
2972
|
children: phase === "comment" ? (0, react_jsx_runtime.jsx)("div", {
|
|
2918
|
-
|
|
2919
|
-
display: "inline-flex",
|
|
2920
|
-
alignItems: "center",
|
|
2921
|
-
gap: 2
|
|
2922
|
-
},
|
|
2973
|
+
className: cx(A.inlineRow, styles.toolbarInner),
|
|
2923
2974
|
children: (0, react_jsx_runtime.jsx)("span", {
|
|
2924
2975
|
style: {
|
|
2925
2976
|
padding: "0 8px",
|
|
@@ -2929,11 +2980,7 @@ function SelectionToolbar({ sessionId }) {
|
|
|
2929
2980
|
children: t("toolbar.commentHint")
|
|
2930
2981
|
})
|
|
2931
2982
|
}) : (0, react_jsx_runtime.jsxs)("div", {
|
|
2932
|
-
|
|
2933
|
-
display: "inline-flex",
|
|
2934
|
-
alignItems: "center",
|
|
2935
|
-
gap: 2
|
|
2936
|
-
},
|
|
2983
|
+
className: cx(A.inlineRow, styles.toolbarInner),
|
|
2937
2984
|
children: [
|
|
2938
2985
|
link ? (0, react_jsx_runtime.jsx)("button", {
|
|
2939
2986
|
type: "button",
|
|
@@ -3281,8 +3328,8 @@ function warnQuota() {
|
|
|
3281
3328
|
console.warn("dsh-select-quote: annotation persistence disabled (storage write failed)");
|
|
3282
3329
|
}
|
|
3283
3330
|
/**
|
|
3284
|
-
* Direct localStorage backend
|
|
3285
|
-
*
|
|
3331
|
+
* Direct localStorage backend. Writes are wrapped so a quota failure degrades
|
|
3332
|
+
* to memory-only instead of breaking the composer.
|
|
3286
3333
|
*/
|
|
3287
3334
|
function createStoragePersistence(storage) {
|
|
3288
3335
|
let degraded = false;
|
|
@@ -3305,45 +3352,6 @@ function createStoragePersistence(storage) {
|
|
|
3305
3352
|
prune: (now) => pruneSessions(storage, now)
|
|
3306
3353
|
};
|
|
3307
3354
|
}
|
|
3308
|
-
/**
|
|
3309
|
-
* Host store backend: the same `defineStore` the composer draft uses, so the
|
|
3310
|
-
* write path, per-session keying and failure semantics are the host's.
|
|
3311
|
-
*/
|
|
3312
|
-
function createHostStorePersistence(defineStore, storage) {
|
|
3313
|
-
const handle = defineStore({
|
|
3314
|
-
init: () => ({
|
|
3315
|
-
v: PAYLOAD_VERSION,
|
|
3316
|
-
updatedAt: 0,
|
|
3317
|
-
items: []
|
|
3318
|
-
}),
|
|
3319
|
-
persist: ANNOTATION_KEY_PREFIX,
|
|
3320
|
-
actions: { replace(draft, items, now) {
|
|
3321
|
-
draft.items = [...items];
|
|
3322
|
-
draft.updatedAt = now;
|
|
3323
|
-
} }
|
|
3324
|
-
});
|
|
3325
|
-
const instances = /* @__PURE__ */ new Map();
|
|
3326
|
-
const instanceOf = (sessionId) => {
|
|
3327
|
-
const cached = instances.get(sessionId);
|
|
3328
|
-
if (cached) return cached;
|
|
3329
|
-
const instance = handle.create(sessionId);
|
|
3330
|
-
instances.set(sessionId, instance);
|
|
3331
|
-
return instance;
|
|
3332
|
-
};
|
|
3333
|
-
return {
|
|
3334
|
-
load: (sessionId) => itemsOf(instanceOf(sessionId).getSnapshot()),
|
|
3335
|
-
save: (sessionId, items) => instanceOf(sessionId).actions.replace(items, Date.now()),
|
|
3336
|
-
clear: (sessionId) => {
|
|
3337
|
-
instanceOf(sessionId).clearPersisted();
|
|
3338
|
-
instances.delete(sessionId);
|
|
3339
|
-
},
|
|
3340
|
-
prune: (now) => {
|
|
3341
|
-
const removed = pruneSessions(storage, now);
|
|
3342
|
-
for (const sessionId of removed) instances.delete(sessionId);
|
|
3343
|
-
return removed;
|
|
3344
|
-
}
|
|
3345
|
-
};
|
|
3346
|
-
}
|
|
3347
3355
|
/** Retention: newest `maxSessions` within `maxAgeMs`, by payload timestamp. */
|
|
3348
3356
|
function pruneSessions(storage, now = Date.now(), options = {}) {
|
|
3349
3357
|
const maxSessions = options.maxSessions ?? 20;
|
|
@@ -3383,27 +3391,20 @@ function createNoopPersistence() {
|
|
|
3383
3391
|
//#endregion
|
|
3384
3392
|
//#region src/client/state/annotation-storage.ts
|
|
3385
3393
|
/**
|
|
3386
|
-
*
|
|
3394
|
+
* Environment probe for annotation persistence.
|
|
3387
3395
|
*
|
|
3388
|
-
* Kept apart from `annotation-persistence.ts` because
|
|
3389
|
-
*
|
|
3390
|
-
*
|
|
3391
|
-
* where a `declare` statement is not valid JavaScript.
|
|
3396
|
+
* Kept apart from `annotation-persistence.ts` because this is the only module
|
|
3397
|
+
* that reads `globalThis`: the pure layer stays importable in Node, where
|
|
3398
|
+
* verify-bundle replays the whole cycle against a Map-backed storage.
|
|
3392
3399
|
*
|
|
3393
|
-
*
|
|
3394
|
-
* draft
|
|
3395
|
-
*
|
|
3400
|
+
* The backend is plain localStorage — the same per-origin store the composer
|
|
3401
|
+
* draft lives in, under this plugin's own key prefix. Requiring the host's
|
|
3402
|
+
* `@deepseek-ai/dsh-client-store` instead would buy nothing: its persistence
|
|
3403
|
+
* backend is localStorage with the same key shape and the same whole-value
|
|
3404
|
+
* payload, while depending on a Harness Client package that the plugin spec
|
|
3405
|
+
* forbids loading as a module (it changes without notice, and a throwing
|
|
3406
|
+
* backend selection would take the whole slot entry down with it).
|
|
3396
3407
|
*/
|
|
3397
|
-
/** The host package is a platform seed word; older hosts may not carry it. */
|
|
3398
|
-
const HOST_STORE_MODULE = "@deepseek-ai/dsh-client-store";
|
|
3399
|
-
function hostDefineStore() {
|
|
3400
|
-
try {
|
|
3401
|
-
const loaded = require(HOST_STORE_MODULE);
|
|
3402
|
-
return typeof loaded?.defineStore === "function" ? loaded.defineStore : null;
|
|
3403
|
-
} catch {
|
|
3404
|
-
return null;
|
|
3405
|
-
}
|
|
3406
|
-
}
|
|
3407
3408
|
function browserStorage() {
|
|
3408
3409
|
try {
|
|
3409
3410
|
const storage = globalThis.localStorage;
|
|
@@ -3427,10 +3428,7 @@ function browserStorage() {
|
|
|
3427
3428
|
}
|
|
3428
3429
|
function createAnnotationPersistence() {
|
|
3429
3430
|
const storage = browserStorage();
|
|
3430
|
-
|
|
3431
|
-
if (storage && defineStore) return createHostStorePersistence(defineStore, storage);
|
|
3432
|
-
if (storage) return createStoragePersistence(storage);
|
|
3433
|
-
return createNoopPersistence();
|
|
3431
|
+
return storage ? createStoragePersistence(storage) : createNoopPersistence();
|
|
3434
3432
|
}
|
|
3435
3433
|
//#endregion
|
|
3436
3434
|
//#region src/client/index.tsx
|
package/locale/en.json
ADDED
package/locale/ru.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-select-quote",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "DeepSeek Harness web plugin: annotate text selected in the conversation — the selection rides along with your next message, is marked in the source text, and the model points back at it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek",
|
|
@@ -26,12 +26,16 @@
|
|
|
26
26
|
"exports": {
|
|
27
27
|
".": "./lib/index.js",
|
|
28
28
|
"./client": "./lib/client.js",
|
|
29
|
-
"./package.json": "./package.json"
|
|
29
|
+
"./package.json": "./package.json",
|
|
30
|
+
"./locale/*.json": "./locale/*.json",
|
|
31
|
+
"./icon": "./icon.svg"
|
|
30
32
|
},
|
|
31
33
|
"files": [
|
|
32
34
|
"lib/index.js",
|
|
33
35
|
"lib/client.js",
|
|
34
36
|
"cordis.patch.yml",
|
|
37
|
+
"locale/*.json",
|
|
38
|
+
"icon.svg",
|
|
35
39
|
"LICENSE"
|
|
36
40
|
],
|
|
37
41
|
"scripts": {
|