@restregular/dsh-notepad 0.5.0 → 0.6.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.
- package/README.md +31 -17
- package/client.js +669 -97
- package/index.js +1 -1
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +5 -3
package/client.js
CHANGED
|
@@ -88,6 +88,10 @@ window.__ModuleLoader__.load({
|
|
|
88
88
|
insert: '插入到输入框',
|
|
89
89
|
insertSegment: '插入此段',
|
|
90
90
|
insertSelected: '插入选中的 {n} 段',
|
|
91
|
+
insertNewSession: '在新会话中插入',
|
|
92
|
+
insertNewSelected: '在新会话中插入选中的 {n} 段',
|
|
93
|
+
insertNewDone: '已在新会话中填入内容',
|
|
94
|
+
insertNewUnavailable: '无法新建会话,内容已复制',
|
|
91
95
|
inserted: '已插入输入框',
|
|
92
96
|
insertEmpty: '没有可插入的内容',
|
|
93
97
|
insertUnavailable: '输入框暂不可用,内容已复制',
|
|
@@ -99,6 +103,10 @@ window.__ModuleLoader__.load({
|
|
|
99
103
|
send: '发送',
|
|
100
104
|
sendSegment: '发送此段',
|
|
101
105
|
sendSelected: '发送选中的 {n} 段',
|
|
106
|
+
sendNewSession: '在新会话中发送',
|
|
107
|
+
sendNewSelected: '在新会话中发送选中的 {n} 段',
|
|
108
|
+
sendNewDone: '已在新会话中发送',
|
|
109
|
+
sendNewUnavailable: '无法新建会话,内容已复制',
|
|
102
110
|
sending: '发送中…',
|
|
103
111
|
sent: '已发送',
|
|
104
112
|
sendFailed: '发送失败',
|
|
@@ -147,6 +155,12 @@ window.__ModuleLoader__.load({
|
|
|
147
155
|
restoredToast: '已恢复',
|
|
148
156
|
restoredCount: '已恢复 {n} 段',
|
|
149
157
|
deleteSegment: '删除此段',
|
|
158
|
+
deleteActiveSegment: '删除此段(不可恢复)',
|
|
159
|
+
deleteConfirmTitle: '删除这个分段?',
|
|
160
|
+
deleteConfirmBody: '删除后无法恢复。分段里的文字和图片引用会一起移除。',
|
|
161
|
+
deleteConfirmHint: '此操作不可撤销',
|
|
162
|
+
deleteConfirmCancel: '取消',
|
|
163
|
+
deleteConfirmOk: '删除',
|
|
150
164
|
deletedToast: '已删除',
|
|
151
165
|
clearArchive: '清空归档',
|
|
152
166
|
clearedArchive: '已清空 {n} 段归档',
|
|
@@ -160,13 +174,24 @@ window.__ModuleLoader__.load({
|
|
|
160
174
|
show: '显示记事本(Ctrl+Shift+N)',
|
|
161
175
|
chars: '{n} 字符',
|
|
162
176
|
segments: '{n} 段',
|
|
163
|
-
|
|
177
|
+
hintNext: '点击查看下一条快捷键',
|
|
178
|
+
hintEnter: 'Enter 新建分段',
|
|
179
|
+
hintNewline: 'Shift+Enter 分段内换行',
|
|
180
|
+
hintInsert: 'Ctrl+Enter 插入当前分段',
|
|
181
|
+
hintToggle: 'Ctrl+Shift+N 显示 / 隐藏浮窗',
|
|
182
|
+
hintReset: 'Ctrl+Shift+0 复位浮窗',
|
|
183
|
+
hintArrow: '↑ / ↓ 切换分段',
|
|
184
|
+
hintPaste: '粘贴图片自动保存到工作区',
|
|
185
|
+
resetShortcut: '复位记事本位置和尺寸',
|
|
164
186
|
workspace: '工作区',
|
|
165
187
|
resize: '拖动调整大小',
|
|
166
188
|
uiReset: '已复位浮窗位置和尺寸',
|
|
167
189
|
addImage: '添加图片',
|
|
168
190
|
removeImage: '移除图片',
|
|
169
191
|
openImage: '打开图片',
|
|
192
|
+
imagePreview: '图片预览',
|
|
193
|
+
closePreview: '关闭预览',
|
|
194
|
+
openOriginal: '打开原图',
|
|
170
195
|
imageAdded: '已添加 {n} 张图片',
|
|
171
196
|
imageFailed: '图片添加失败',
|
|
172
197
|
imageUnsupported: '只支持 PNG / JPEG / WebP / GIF 图片',
|
|
@@ -196,6 +221,10 @@ window.__ModuleLoader__.load({
|
|
|
196
221
|
insert: 'Insert into composer',
|
|
197
222
|
insertSegment: 'Insert this segment',
|
|
198
223
|
insertSelected: 'Insert {n} selected',
|
|
224
|
+
insertNewSession: 'Insert into a new session',
|
|
225
|
+
insertNewSelected: 'Insert {n} selected into a new session',
|
|
226
|
+
insertNewDone: 'Filled into a new session',
|
|
227
|
+
insertNewUnavailable: 'Could not create a session; copied instead',
|
|
199
228
|
inserted: 'Inserted into composer',
|
|
200
229
|
insertEmpty: 'Nothing to insert',
|
|
201
230
|
insertUnavailable: 'Composer unavailable; copied instead',
|
|
@@ -207,6 +236,10 @@ window.__ModuleLoader__.load({
|
|
|
207
236
|
send: 'Send',
|
|
208
237
|
sendSegment: 'Send this segment',
|
|
209
238
|
sendSelected: 'Send {n} selected',
|
|
239
|
+
sendNewSession: 'Send into a new session',
|
|
240
|
+
sendNewSelected: 'Send {n} selected into a new session',
|
|
241
|
+
sendNewDone: 'Sent into a new session',
|
|
242
|
+
sendNewUnavailable: 'Could not create a session; copied instead',
|
|
210
243
|
sending: 'Sending…',
|
|
211
244
|
sent: 'Sent',
|
|
212
245
|
sendFailed: 'Send failed',
|
|
@@ -255,6 +288,12 @@ window.__ModuleLoader__.load({
|
|
|
255
288
|
restoredToast: 'Restored',
|
|
256
289
|
restoredCount: 'Restored {n}',
|
|
257
290
|
deleteSegment: 'Delete this segment',
|
|
291
|
+
deleteActiveSegment: 'Delete this segment (cannot be undone)',
|
|
292
|
+
deleteConfirmTitle: 'Delete this segment?',
|
|
293
|
+
deleteConfirmBody: 'This cannot be undone. The text and image references in the segment will be removed too.',
|
|
294
|
+
deleteConfirmHint: 'This action cannot be undone',
|
|
295
|
+
deleteConfirmCancel: 'Cancel',
|
|
296
|
+
deleteConfirmOk: 'Delete',
|
|
258
297
|
deletedToast: 'Deleted',
|
|
259
298
|
clearArchive: 'Clear archive',
|
|
260
299
|
clearedArchive: 'Cleared {n} archived',
|
|
@@ -268,13 +307,24 @@ window.__ModuleLoader__.load({
|
|
|
268
307
|
show: 'Show notepad (Ctrl+Shift+N)',
|
|
269
308
|
chars: '{n} chars',
|
|
270
309
|
segments: '{n} segments',
|
|
271
|
-
|
|
310
|
+
hintNext: 'Click for the next shortcut',
|
|
311
|
+
hintEnter: 'Enter creates a segment',
|
|
312
|
+
hintNewline: 'Shift+Enter adds a line break',
|
|
313
|
+
hintInsert: 'Ctrl+Enter inserts the current segment',
|
|
314
|
+
hintToggle: 'Ctrl+Shift+N shows / hides the notepad',
|
|
315
|
+
hintReset: 'Ctrl+Shift+0 resets the floating window',
|
|
316
|
+
hintArrow: '↑ / ↓ switch segments',
|
|
317
|
+
hintPaste: 'Pasted images are saved in the workspace',
|
|
318
|
+
resetShortcut: 'Reset the notepad position and size',
|
|
272
319
|
workspace: 'Workspace',
|
|
273
320
|
resize: 'Drag to resize',
|
|
274
321
|
uiReset: 'Window position and size reset',
|
|
275
322
|
addImage: 'Add image',
|
|
276
323
|
removeImage: 'Remove image',
|
|
277
324
|
openImage: 'Open image',
|
|
325
|
+
imagePreview: 'Image preview',
|
|
326
|
+
closePreview: 'Close preview',
|
|
327
|
+
openOriginal: 'Open original',
|
|
278
328
|
imageAdded: 'Added {n} image(s)',
|
|
279
329
|
imageFailed: 'Could not add image',
|
|
280
330
|
imageUnsupported: 'Only PNG / JPEG / WebP / GIF images are supported',
|
|
@@ -287,6 +337,17 @@ window.__ModuleLoader__.load({
|
|
|
287
337
|
},
|
|
288
338
|
};
|
|
289
339
|
|
|
340
|
+
/** Rotating shortcut tips shown one at a time in the footer. */
|
|
341
|
+
const HINT_KEYS = [
|
|
342
|
+
'hintEnter',
|
|
343
|
+
'hintNewline',
|
|
344
|
+
'hintInsert',
|
|
345
|
+
'hintToggle',
|
|
346
|
+
'hintReset',
|
|
347
|
+
'hintArrow',
|
|
348
|
+
'hintPaste',
|
|
349
|
+
];
|
|
350
|
+
|
|
290
351
|
function preferredLanguage() {
|
|
291
352
|
try {
|
|
292
353
|
const tag = String((typeof navigator !== 'undefined' && navigator.language) || 'zh').toLowerCase();
|
|
@@ -576,6 +637,60 @@ window.__ModuleLoader__.load({
|
|
|
576
637
|
);
|
|
577
638
|
}
|
|
578
639
|
|
|
640
|
+
/** Two panels, the second with a plus: start a new session. */
|
|
641
|
+
function NewSessionIcon({ size = 16 }) {
|
|
642
|
+
return h(
|
|
643
|
+
Svg,
|
|
644
|
+
{ size, viewBox: '0 0 16 16' },
|
|
645
|
+
h('rect', {
|
|
646
|
+
x: 1.9, y: 3.2, width: 5.1, height: 9.4, rx: 1.4,
|
|
647
|
+
stroke: 'currentColor', strokeWidth: 1.25,
|
|
648
|
+
}),
|
|
649
|
+
h('rect', {
|
|
650
|
+
x: 8.7, y: 3.2, width: 5.4, height: 9.4, rx: 1.4,
|
|
651
|
+
stroke: 'currentColor', strokeWidth: 1.25,
|
|
652
|
+
}),
|
|
653
|
+
h('path', {
|
|
654
|
+
d: 'M11.4 6.1v3.8M9.5 8h3.8',
|
|
655
|
+
stroke: 'currentColor', strokeWidth: 1.25, strokeLinecap: 'round',
|
|
656
|
+
}),
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/** Circular arrow: reset the floating window geometry. */
|
|
661
|
+
function ResetIcon({ size = 16 }) {
|
|
662
|
+
return h(
|
|
663
|
+
Svg,
|
|
664
|
+
{ size, viewBox: '0 0 16 16' },
|
|
665
|
+
h('path', {
|
|
666
|
+
d: 'M13 8a5 5 0 1 1-1.6-3.7',
|
|
667
|
+
stroke: 'currentColor', strokeWidth: 1.35, strokeLinecap: 'round',
|
|
668
|
+
}),
|
|
669
|
+
h('path', {
|
|
670
|
+
d: 'M13.3 2.7v3.4H9.9',
|
|
671
|
+
stroke: 'currentColor', strokeWidth: 1.35,
|
|
672
|
+
strokeLinecap: 'round', strokeLinejoin: 'round',
|
|
673
|
+
}),
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/** Arrow leaving a frame: open the original image. */
|
|
678
|
+
function OpenIcon({ size = 16 }) {
|
|
679
|
+
return h(
|
|
680
|
+
Svg,
|
|
681
|
+
{ size, viewBox: '0 0 16 16' },
|
|
682
|
+
h('path', {
|
|
683
|
+
d: 'M9.4 3.2h3.4v3.4M12.8 3.2 7.6 8.4',
|
|
684
|
+
stroke: 'currentColor', strokeWidth: 1.3,
|
|
685
|
+
strokeLinecap: 'round', strokeLinejoin: 'round',
|
|
686
|
+
}),
|
|
687
|
+
h('path', {
|
|
688
|
+
d: 'M12.4 9.4v2.3c0 .7-.6 1.3-1.3 1.3H4.6c-.7 0-1.3-.6-1.3-1.3V5.2c0-.7.6-1.3 1.3-1.3h2.3',
|
|
689
|
+
stroke: 'currentColor', strokeWidth: 1.3, strokeLinecap: 'round',
|
|
690
|
+
}),
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
579
694
|
/** Two arrows converging: merge selected segments into one. */
|
|
580
695
|
function MergeIcon({ size = 16 }) {
|
|
581
696
|
return h(
|
|
@@ -636,7 +751,7 @@ window.__ModuleLoader__.load({
|
|
|
636
751
|
.dsh-np-btn[data-accent=true]{background:var(--np-accent);color:var(--np-accent-fg)}
|
|
637
752
|
.dsh-np-btn[data-accent=true]:hover:not(:disabled){background:color-mix(in srgb,var(--np-accent) 88%,#000);color:var(--np-accent-fg)}
|
|
638
753
|
.dsh-np-btn:disabled{opacity:.38;cursor:default;background:transparent;color:var(--dsw-alias-label-tertiary,#6b7280)}
|
|
639
|
-
.dsh-np-btn:focus-visible,.dsh-np-segbtn:focus-visible,.dsh-np-link:focus-visible,.dsh-np-empty-add:focus-visible,.dsh-np-toast-action:focus-visible,.dsh-np-selectall input:focus-visible,.dsh-np-check input:focus-visible,.dsh-np-title-add:focus-visible,.dsh-np-restore:focus-visible,.dsh-np-img-open:focus-visible,.dsh-np-img-remove:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary,#4176e6));outline-offset:-2px}
|
|
754
|
+
.dsh-np-btn:focus-visible,.dsh-np-segbtn:focus-visible,.dsh-np-link:focus-visible,.dsh-np-empty-add:focus-visible,.dsh-np-toast-action:focus-visible,.dsh-np-selectall input:focus-visible,.dsh-np-check input:focus-visible,.dsh-np-title-add:focus-visible,.dsh-np-preview-btn:focus-visible,.dsh-np-confirm-btn:focus-visible,button.dsh-np-hint:focus-visible,.dsh-np-restore:focus-visible,.dsh-np-img-open:focus-visible,.dsh-np-img-remove:focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary,#4176e6));outline-offset:-2px}
|
|
640
755
|
.dsh-np-body{display:flex;flex:1 1 auto;flex-direction:column;min-height:0;padding:8px 10px 0;opacity:1;transition:opacity .16s ease,visibility 0s linear 0s}
|
|
641
756
|
.dsh-np-card[data-minimized=true] .dsh-np-body{opacity:0;visibility:hidden;pointer-events:none;transition:opacity .16s ease,visibility 0s linear .16s}
|
|
642
757
|
.dsh-np-tools{display:flex;align-items:center;flex-wrap:wrap;gap:4px;flex:none;padding:0 2px 7px;border-bottom:1px solid var(--dsw-alias-border-l1,rgb(15 23 42/.08))}
|
|
@@ -669,6 +784,11 @@ window.__ModuleLoader__.load({
|
|
|
669
784
|
.dsh-np-segbtn[data-ai=true]{color:var(--np-accent);opacity:1}
|
|
670
785
|
.dsh-np-segbtn[data-danger=true]:hover:not(:disabled){color:var(--dsw-alias-state-error-primary,#e25555);background:var(--dsw-alias-interactive-bg-hover-danger,rgb(236 19 19/.05))}
|
|
671
786
|
.dsh-np-segbtn:disabled{opacity:.3;cursor:default}
|
|
787
|
+
.dsh-np-actiongroup{display:inline-flex;align-items:center;flex:none;padding:1px;border:1px solid transparent;border-radius:8px;transition:border-color .12s,background .12s}
|
|
788
|
+
.dsh-np-actiongroup:hover,.dsh-np-actiongroup:focus-within{border-color:var(--dsw-alias-border-l2,rgb(15 23 42/.14));background:color-mix(in srgb,var(--np-surface-2) 58%,transparent)}
|
|
789
|
+
.dsh-np-group-extra{width:0!important;min-width:0;padding:0;border:0;opacity:0;visibility:hidden;pointer-events:none;overflow:hidden;transition:width .14s ease,opacity .12s ease,visibility 0s linear .14s}
|
|
790
|
+
.dsh-np-actiongroup:hover .dsh-np-group-extra,.dsh-np-actiongroup:focus-within .dsh-np-group-extra{width:24px!important;opacity:1;visibility:visible;pointer-events:auto;transition:width .14s ease,opacity .12s ease,visibility 0s linear 0s}
|
|
791
|
+
.dsh-np-actiongroup[data-size=bar]:hover .dsh-np-group-extra,.dsh-np-actiongroup[data-size=bar]:focus-within .dsh-np-group-extra{width:27px!important}
|
|
672
792
|
.dsh-np-seg-text{display:block;width:100%;box-sizing:border-box;padding:2px 10px 8px;border:0;outline:0;resize:none;background:transparent;color:inherit;font-family:${FONT_STACK};font-size:13px;line-height:1.6;min-height:26px;max-height:240px;overflow-y:auto;white-space:pre-wrap;overflow-wrap:anywhere;tab-size:2}
|
|
673
793
|
.dsh-np-seg-text::placeholder{color:var(--dsw-alias-label-caption,#9aa3b2)}
|
|
674
794
|
.dsh-np-seg-text:disabled{opacity:.6}
|
|
@@ -706,6 +826,24 @@ window.__ModuleLoader__.load({
|
|
|
706
826
|
.dsh-np-retry{flex:none;padding:0;border:0;background:none;color:var(--dsw-alias-link,#4176e6);font:inherit;font-size:10.5px;cursor:pointer;text-decoration:underline}
|
|
707
827
|
.dsh-np-count{flex:none;font-variant-numeric:tabular-nums}
|
|
708
828
|
.dsh-np-hint{margin-left:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.85;font-size:10px}
|
|
829
|
+
button.dsh-np-hint{padding:0;border:0;background:none;color:inherit;font:inherit;font-size:10px;cursor:pointer;text-align:right}
|
|
830
|
+
button.dsh-np-hint:hover{opacity:1;color:var(--dsw-alias-label-secondary,#4b5563);text-decoration:underline}
|
|
831
|
+
.dsh-np-preview{position:absolute;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;padding:44px 20px 28px;background:rgb(8 12 20/.74);backdrop-filter:blur(3px);animation:dsh-np-fade .14s ease-out}
|
|
832
|
+
.dsh-np-preview-img{display:block;max-width:100%;max-height:100%;object-fit:contain;border-radius:8px;background:#fff;box-shadow:0 16px 40px rgb(0 0 0/.35)}
|
|
833
|
+
.dsh-np-preview-bar{position:absolute;top:8px;right:8px;display:flex;align-items:center;gap:4px}
|
|
834
|
+
.dsh-np-preview-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;border:1px solid rgb(255 255 255/.18);border-radius:7px;background:rgb(15 23 42/.55);color:#f8fafc;cursor:pointer;backdrop-filter:blur(4px)}
|
|
835
|
+
.dsh-np-preview-btn:hover{background:rgb(15 23 42/.82)}
|
|
836
|
+
.dsh-np-preview-name{position:absolute;left:12px;bottom:8px;max-width:calc(100% - 24px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#e5e7eb;font-size:11px}
|
|
837
|
+
.dsh-np-confirm{position:absolute;inset:0;z-index:21;display:flex;align-items:center;justify-content:center;padding:16px;background:rgb(8 12 20/.55);backdrop-filter:blur(2px);animation:dsh-np-fade .14s ease-out}
|
|
838
|
+
.dsh-np-confirm-card{width:min(100%,300px);padding:14px;border:1px solid var(--dsw-alias-border-l2,rgb(15 23 42/.14));border-radius:12px;background:var(--np-surface);box-shadow:var(--dsw-elevation-prominent,0 20px 50px rgb(15 23 42/.22))}
|
|
839
|
+
.dsh-np-confirm-title{font-size:13px;font-weight:650;color:var(--dsw-alias-label-primary,#1f2430)}
|
|
840
|
+
.dsh-np-confirm-body{margin-top:6px;color:var(--dsw-alias-label-secondary,#4b5563);font-size:12px;line-height:1.55}
|
|
841
|
+
.dsh-np-confirm-hint{margin-top:8px;color:var(--dsw-alias-state-error-primary,#e25555);font-size:11px}
|
|
842
|
+
.dsh-np-confirm-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:12px}
|
|
843
|
+
.dsh-np-confirm-btn{height:28px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2,rgb(15 23 42/.14));border-radius:7px;background:var(--np-surface-2);color:var(--dsw-alias-label-secondary,#4b5563);font:inherit;font-size:12px;cursor:pointer}
|
|
844
|
+
.dsh-np-confirm-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgb(38 49 72/.06));color:var(--dsw-alias-label-primary,#1f2430)}
|
|
845
|
+
.dsh-np-confirm-btn[data-danger=true]{border-color:transparent;background:var(--dsw-alias-state-error-primary,#e25555);color:#fff}
|
|
846
|
+
.dsh-np-confirm-btn[data-danger=true]:hover{background:color-mix(in srgb,var(--dsw-alias-state-error-primary,#e25555) 86%,#000);color:#fff}
|
|
709
847
|
.dsh-np-toast{position:absolute;left:50%;bottom:38px;transform:translateX(-50%);z-index:2;display:flex;align-items:center;gap:8px;max-width:calc(100% - 32px);padding:5px 10px;border-radius:8px;background:var(--np-surface-2);border:1px solid var(--dsw-alias-border-l2,rgb(15 23 42/.12));box-shadow:var(--dsw-elevation-lv2,0 8px 24px rgb(15 23 42/.18));color:var(--dsw-alias-label-primary,#1f2430);font-size:11.5px;white-space:nowrap;overflow:hidden;animation:dsh-np-rise .16s ease-out}
|
|
710
848
|
.dsh-np-toast-text{overflow:hidden;text-overflow:ellipsis}
|
|
711
849
|
.dsh-np-toast-action{flex:none;padding:0;border:0;background:none;color:var(--dsw-alias-link,#4176e6);font:inherit;font-size:11.5px;font-weight:600;cursor:pointer;text-decoration:underline}
|
|
@@ -720,6 +858,7 @@ window.__ModuleLoader__.load({
|
|
|
720
858
|
[data-row-key^="workspace:"][data-np-unsent]{position:relative}
|
|
721
859
|
[data-row-key^="workspace:"][data-np-unsent]::after{content:attr(data-np-unsent);position:absolute;top:-4px;left:1px;z-index:2;min-width:15px;height:15px;padding:0 4px;box-sizing:border-box;border-radius:8px;background:var(--dsw-alias-state-business-primary,#4176e6);color:#fff;font-size:9.5px;font-weight:700;line-height:15px;text-align:center;font-variant-numeric:tabular-nums;pointer-events:none;box-shadow:0 0 0 1.5px var(--dsw-specific-sidebar-fill,var(--dsw-alias-bg-layer-1,#fff))}
|
|
722
860
|
@keyframes dsh-np-pulse{0%,100%{opacity:1}50%{opacity:.35}}
|
|
861
|
+
@keyframes dsh-np-fade{from{opacity:0}to{opacity:1}}
|
|
723
862
|
@keyframes dsh-np-rise{from{opacity:0;transform:translate(-50%,4px)}to{opacity:1;transform:translate(-50%,0)}}
|
|
724
863
|
@keyframes dsh-np-item-in{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}
|
|
725
864
|
@keyframes dsh-np-spin{to{transform:rotate(360deg)}}
|
|
@@ -727,7 +866,7 @@ body[data-ds-dark-theme] .dsh-np-card{--np-accent-fg:#0f1115;box-shadow:0 18px 4
|
|
|
727
866
|
body[data-ds-dark-theme] .dsh-np-seg[data-selected=true]{background:color-mix(in srgb,var(--np-accent) 14%,var(--dsw-alias-bg-layer-2,#2c2c2e))}
|
|
728
867
|
body[data-ds-dark-theme] .dsh-np-btn[data-accent=true]:hover:not(:disabled){background:color-mix(in srgb,var(--np-accent) 84%,#fff)}
|
|
729
868
|
body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
730
|
-
@media (prefers-reduced-motion:reduce){.dsh-np-card,.dsh-np-body,.dsh-np-archive,.dsh-np-restore,.dsh-np-seg,.dsh-np-toast{transition:none!important;animation:none!important}.dsh-np-dot[data-state=saving]{animation:none}.dsh-np-img-spinner,.dsh-np-mini-spinner{animation:none}}`;
|
|
869
|
+
@media (prefers-reduced-motion:reduce){.dsh-np-card,.dsh-np-body,.dsh-np-archive,.dsh-np-restore,.dsh-np-seg,.dsh-np-toast,.dsh-np-actiongroup,.dsh-np-group-extra,.dsh-np-preview,.dsh-np-confirm{transition:none!important;animation:none!important}.dsh-np-dot[data-state=saving]{animation:none}.dsh-np-img-spinner,.dsh-np-mini-spinner{animation:none}}`;
|
|
731
870
|
|
|
732
871
|
// ------------------------------------------------------------------
|
|
733
872
|
// Helpers
|
|
@@ -1252,6 +1391,38 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1252
1391
|
}
|
|
1253
1392
|
}
|
|
1254
1393
|
|
|
1394
|
+
/** Mounted notepads register here so a host shortcut can reset them. */
|
|
1395
|
+
const resetHandlers = new Set();
|
|
1396
|
+
|
|
1397
|
+
/** Whether the host shortcut service already owns Ctrl/Cmd+Shift+0. */
|
|
1398
|
+
let resetShortcutHandledByHost = false;
|
|
1399
|
+
|
|
1400
|
+
/** Ask every mounted notepad to reset its position and size. */
|
|
1401
|
+
function requestUiReset() {
|
|
1402
|
+
let handled = false;
|
|
1403
|
+
for (const handler of resetHandlers) {
|
|
1404
|
+
try {
|
|
1405
|
+
handler();
|
|
1406
|
+
handled = true;
|
|
1407
|
+
} catch {
|
|
1408
|
+
/* One failing overlay must not block the others. */
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
return handled;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/** Read the General Settings "send behavior while busy" preference. */
|
|
1415
|
+
function busyEnterPreference(ctx) {
|
|
1416
|
+
try {
|
|
1417
|
+
const forms = ctx.get('configForms') ?? ctx.configForms;
|
|
1418
|
+
const form = typeof forms?.get === 'function' ? forms.get('ui-conversation') : undefined;
|
|
1419
|
+
const value = form?.getSnapshot?.()?.value;
|
|
1420
|
+
return value?.busyEnter === 'steer' ? 'steer' : 'queue';
|
|
1421
|
+
} catch {
|
|
1422
|
+
return 'queue';
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1255
1426
|
// ------------------------------------------------------------------
|
|
1256
1427
|
// Overlay
|
|
1257
1428
|
// ------------------------------------------------------------------
|
|
@@ -1280,6 +1451,9 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1280
1451
|
const [segmentUndo, setSegmentUndo] = useState(() => new Map());
|
|
1281
1452
|
const [batchUndo, setBatchUndo] = useState(null);
|
|
1282
1453
|
const [titleEditing, setTitleEditing] = useState('');
|
|
1454
|
+
const [previewImage, setPreviewImage] = useState(null);
|
|
1455
|
+
const [deleteConfirm, setDeleteConfirm] = useState('');
|
|
1456
|
+
const [hintIndex, setHintIndex] = useState(0);
|
|
1283
1457
|
const [wsCounts, setWsCounts] = useState({});
|
|
1284
1458
|
const [currentWsId, setCurrentWsId] = useState('');
|
|
1285
1459
|
|
|
@@ -1420,6 +1594,24 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1420
1594
|
toastTimerRef.current = setTimeout(() => setToast(null), action === undefined || action === null ? 1800 : 5000);
|
|
1421
1595
|
};
|
|
1422
1596
|
|
|
1597
|
+
const resetGeometry = () => {
|
|
1598
|
+
setUi((previous) => clampUi({
|
|
1599
|
+
...previous,
|
|
1600
|
+
x: null,
|
|
1601
|
+
y: null,
|
|
1602
|
+
width: DEFAULT_W,
|
|
1603
|
+
height: DEFAULT_H,
|
|
1604
|
+
}));
|
|
1605
|
+
showToast(t('uiReset'));
|
|
1606
|
+
};
|
|
1607
|
+
|
|
1608
|
+
useEffect(() => {
|
|
1609
|
+
resetHandlers.add(resetGeometry);
|
|
1610
|
+
return () => {
|
|
1611
|
+
resetHandlers.delete(resetGeometry);
|
|
1612
|
+
};
|
|
1613
|
+
}, []);
|
|
1614
|
+
|
|
1423
1615
|
const scheduleSave = () => {
|
|
1424
1616
|
clearTimer();
|
|
1425
1617
|
timerRef.current = setTimeout(() => {
|
|
@@ -1485,12 +1677,17 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1485
1677
|
setSegmentUndo(new Map());
|
|
1486
1678
|
setBatchUndoState(null);
|
|
1487
1679
|
setTitleEditing('');
|
|
1680
|
+
setPreviewImage(null);
|
|
1681
|
+
setDeleteConfirm('');
|
|
1488
1682
|
pendingScrollRef.current = null;
|
|
1489
1683
|
setStatus('idle');
|
|
1490
1684
|
setError('');
|
|
1491
1685
|
return undefined;
|
|
1492
1686
|
}
|
|
1493
|
-
if (dirtyRef.current && rootRef.current !== ''
|
|
1687
|
+
if (dirtyRef.current && rootRef.current !== '') {
|
|
1688
|
+
// Flush the old workspace/session note before this binding replaces
|
|
1689
|
+
// the in-memory segments; a same-workspace session switch must not
|
|
1690
|
+
// drop a still-debounced edit.
|
|
1494
1691
|
saveNowRef.current?.({ force: true });
|
|
1495
1692
|
}
|
|
1496
1693
|
clearTimer();
|
|
@@ -1505,6 +1702,8 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1505
1702
|
setSegmentUndo(new Map());
|
|
1506
1703
|
setBatchUndoState(null);
|
|
1507
1704
|
setTitleEditing('');
|
|
1705
|
+
setPreviewImage(null);
|
|
1706
|
+
setDeleteConfirm('');
|
|
1508
1707
|
pendingScrollRef.current = null;
|
|
1509
1708
|
setStatus('loading');
|
|
1510
1709
|
setError('');
|
|
@@ -1686,16 +1885,11 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1686
1885
|
setUi((previous) => clampUi({ ...previous, hidden: !previous.hidden }));
|
|
1687
1886
|
return;
|
|
1688
1887
|
}
|
|
1689
|
-
if (
|
|
1888
|
+
if (!resetShortcutHandledByHost
|
|
1889
|
+
&& (event.key === '0' || event.key === ')' || event.code === 'Digit0'
|
|
1890
|
+
|| event.code === 'Numpad0' || event.keyCode === 48)) {
|
|
1690
1891
|
event.preventDefault();
|
|
1691
|
-
|
|
1692
|
-
...previous,
|
|
1693
|
-
x: null,
|
|
1694
|
-
y: null,
|
|
1695
|
-
width: DEFAULT_W,
|
|
1696
|
-
height: DEFAULT_H,
|
|
1697
|
-
}));
|
|
1698
|
-
showToast(t('uiReset'));
|
|
1892
|
+
resetGeometry();
|
|
1699
1893
|
}
|
|
1700
1894
|
};
|
|
1701
1895
|
window.addEventListener('keydown', onKeyDown, true);
|
|
@@ -1708,6 +1902,19 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1708
1902
|
};
|
|
1709
1903
|
}, []);
|
|
1710
1904
|
|
|
1905
|
+
// Close the preview / delete confirmation with Escape ----------------
|
|
1906
|
+
useEffect(() => {
|
|
1907
|
+
if (previewImage === null && deleteConfirm === '') return undefined;
|
|
1908
|
+
const onKeyDown = (event) => {
|
|
1909
|
+
if (event.key !== 'Escape') return;
|
|
1910
|
+
event.preventDefault();
|
|
1911
|
+
setPreviewImage(null);
|
|
1912
|
+
setDeleteConfirm('');
|
|
1913
|
+
};
|
|
1914
|
+
window.addEventListener('keydown', onKeyDown, true);
|
|
1915
|
+
return () => window.removeEventListener('keydown', onKeyDown, true);
|
|
1916
|
+
}, [previewImage, deleteConfirm]);
|
|
1917
|
+
|
|
1711
1918
|
// Flush on page hide / reload -----------------------------------------
|
|
1712
1919
|
useEffect(() => {
|
|
1713
1920
|
const flush = () => {
|
|
@@ -1936,6 +2143,7 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1936
2143
|
const stillReferenced = next.some((segment) =>
|
|
1937
2144
|
(Array.isArray(segment.images) ? segment.images : []).some((image) => image.path === pathValue));
|
|
1938
2145
|
commitSegments(next);
|
|
2146
|
+
if (previewImage !== null && previewImage.path === pathValue) setPreviewImage(null);
|
|
1939
2147
|
if (!stillReferenced) deleteImageFile(pathValue);
|
|
1940
2148
|
};
|
|
1941
2149
|
|
|
@@ -1966,7 +2174,7 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1966
2174
|
const files = clipboardImageFiles(data);
|
|
1967
2175
|
if (files.length === 0) return;
|
|
1968
2176
|
event.preventDefault?.();
|
|
1969
|
-
event
|
|
2177
|
+
event?.stopPropagation?.();
|
|
1970
2178
|
attachImages(id, files, 'paste');
|
|
1971
2179
|
};
|
|
1972
2180
|
|
|
@@ -1975,7 +2183,7 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1975
2183
|
setDragSegment('');
|
|
1976
2184
|
if (files.length === 0) return;
|
|
1977
2185
|
event.preventDefault?.();
|
|
1978
|
-
event
|
|
2186
|
+
event?.stopPropagation?.();
|
|
1979
2187
|
attachImages(id, files, 'pick');
|
|
1980
2188
|
};
|
|
1981
2189
|
|
|
@@ -1995,11 +2203,15 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
1995
2203
|
};
|
|
1996
2204
|
|
|
1997
2205
|
const openImage = (image) => {
|
|
2206
|
+
setPreviewImage({ path: image.path, name: image.name });
|
|
2207
|
+
};
|
|
2208
|
+
|
|
2209
|
+
const openOriginalImage = (image) => {
|
|
1998
2210
|
const url = imageUrl(image.path, rootRef.current);
|
|
1999
2211
|
try {
|
|
2000
2212
|
window.open(url, '_blank', 'noopener,noreferrer');
|
|
2001
2213
|
} catch {
|
|
2002
|
-
/* Popups may be blocked; the
|
|
2214
|
+
/* Popups may be blocked; the in-panel preview still works. */
|
|
2003
2215
|
}
|
|
2004
2216
|
};
|
|
2005
2217
|
|
|
@@ -2102,6 +2314,28 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2102
2314
|
showToast(t('deletedToast'), { label: t('undo'), onClick: undoLast });
|
|
2103
2315
|
};
|
|
2104
2316
|
|
|
2317
|
+
/** Irreversible delete for an active segment, gated by a confirmation. */
|
|
2318
|
+
const deleteActiveSegment = (id) => {
|
|
2319
|
+
setDeleteConfirm('');
|
|
2320
|
+
setPreviewImage(null);
|
|
2321
|
+
const target = segmentsRef.current.find((segment) => segment.id === id && segment.archived !== true);
|
|
2322
|
+
if (target === undefined) return;
|
|
2323
|
+
// This delete cannot be undone: retire any older snackbar undo so it
|
|
2324
|
+
// cannot resurrect the segment through a stale snapshot.
|
|
2325
|
+
undoRef.current = null;
|
|
2326
|
+
setToast(null);
|
|
2327
|
+
setSegmentUndo((previous) => {
|
|
2328
|
+
if (!previous.has(id)) return previous;
|
|
2329
|
+
const updated = new Map(previous);
|
|
2330
|
+
updated.delete(id);
|
|
2331
|
+
return updated;
|
|
2332
|
+
});
|
|
2333
|
+
const next = segmentsRef.current.filter((segment) => segment.id !== id);
|
|
2334
|
+
commitSegments(next);
|
|
2335
|
+
scheduleImageCleanup(target.images);
|
|
2336
|
+
showToast(t('deletedToast'));
|
|
2337
|
+
};
|
|
2338
|
+
|
|
2105
2339
|
const clearArchive = () => {
|
|
2106
2340
|
const archived = segmentsRef.current.filter((segment) => segment.archived === true);
|
|
2107
2341
|
if (archived.length === 0) return;
|
|
@@ -2199,7 +2433,7 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2199
2433
|
const types = event?.dataTransfer?.types;
|
|
2200
2434
|
if (types !== undefined && types !== null && !Array.from(types).includes('application/x-dsh-notepad-segment')) return;
|
|
2201
2435
|
event.preventDefault?.();
|
|
2202
|
-
event
|
|
2436
|
+
event?.stopPropagation?.();
|
|
2203
2437
|
const rect = typeof event.currentTarget?.getBoundingClientRect === 'function'
|
|
2204
2438
|
? event.currentTarget.getBoundingClientRect()
|
|
2205
2439
|
: null;
|
|
@@ -2224,7 +2458,7 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2224
2458
|
setReorderTarget(null);
|
|
2225
2459
|
if (source === '') return;
|
|
2226
2460
|
event.preventDefault?.();
|
|
2227
|
-
event
|
|
2461
|
+
event?.stopPropagation?.();
|
|
2228
2462
|
reorderSegments(source, id, target?.after === true);
|
|
2229
2463
|
};
|
|
2230
2464
|
|
|
@@ -2447,6 +2681,28 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2447
2681
|
showToast(copied ? t('insertUnavailable') : t('copyFailed'));
|
|
2448
2682
|
};
|
|
2449
2683
|
|
|
2684
|
+
/** Start a new session in this workspace with the text already in its composer. */
|
|
2685
|
+
const insertNewSession = async (list) => {
|
|
2686
|
+
const value = mergedText(list);
|
|
2687
|
+
if (value === '') {
|
|
2688
|
+
showToast(t('insertEmpty'));
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
const uiWorkspace = ctx.get('uiWorkspace');
|
|
2692
|
+
if (uiWorkspace !== undefined && uiWorkspace !== null && typeof uiWorkspace.startSession === 'function') {
|
|
2693
|
+
try {
|
|
2694
|
+
if (dirtyRef.current) await saveNowRef.current?.({ force: true });
|
|
2695
|
+
uiWorkspace.startSession(undefined, { prompt: value, clearPreviousDraft: true });
|
|
2696
|
+
showToast(t('insertNewDone'));
|
|
2697
|
+
return;
|
|
2698
|
+
} catch {
|
|
2699
|
+
/* Fall through to copy. */
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
const copied = await copyText(value);
|
|
2703
|
+
showToast(copied ? t('insertNewUnavailable') : t('copyFailed'));
|
|
2704
|
+
};
|
|
2705
|
+
|
|
2450
2706
|
const replaceSegments = async (list) => {
|
|
2451
2707
|
const value = mergedText(list);
|
|
2452
2708
|
if (value === '') {
|
|
@@ -2461,55 +2717,93 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2461
2717
|
showToast(copied ? t('replaceUnavailable') : t('copyFailed'));
|
|
2462
2718
|
};
|
|
2463
2719
|
|
|
2720
|
+
/** Resolve queue vs steer exactly like the composer's Send button. */
|
|
2721
|
+
const resolveSendMode = (sessionId) => {
|
|
2722
|
+
try {
|
|
2723
|
+
const sessions = ctx.get('sessions');
|
|
2724
|
+
const row = sessions?.list?.getSnapshot?.()?.byId?.[sessionId];
|
|
2725
|
+
if (row?.running !== true) return 'queue';
|
|
2726
|
+
} catch {
|
|
2727
|
+
return 'queue';
|
|
2728
|
+
}
|
|
2729
|
+
return busyEnterPreference(ctx);
|
|
2730
|
+
};
|
|
2731
|
+
|
|
2732
|
+
/** Send through the session face with an explicit queue/steer mode. */
|
|
2733
|
+
const promptSession = async (sessionId, value, mode) => {
|
|
2734
|
+
const sessions = ctx.get('sessions');
|
|
2735
|
+
if (sessions === undefined || sessions === null || typeof sessions.binding !== 'function') return false;
|
|
2736
|
+
const liveBinding = sessions.binding(sessionId);
|
|
2737
|
+
const session = liveBinding?.session;
|
|
2738
|
+
if (session === undefined || session === null || typeof session.prompt !== 'function') return false;
|
|
2739
|
+
let echo;
|
|
2740
|
+
try {
|
|
2741
|
+
echo = typeof session.beginSubmission === 'function'
|
|
2742
|
+
? session.beginSubmission({ mode, text: value, attachments: [] })
|
|
2743
|
+
: undefined;
|
|
2744
|
+
} catch {
|
|
2745
|
+
echo = undefined;
|
|
2746
|
+
}
|
|
2747
|
+
try {
|
|
2748
|
+
const result = await session.prompt([{ type: 'text', text: value }], mode, undefined, echo?.requestId);
|
|
2749
|
+
if (result !== undefined && result !== null && result.ok === false) {
|
|
2750
|
+
const failure = new Error(result.error?.message ?? 'prompt failed');
|
|
2751
|
+
failure.code = result.error?.code;
|
|
2752
|
+
throw failure;
|
|
2753
|
+
}
|
|
2754
|
+
return true;
|
|
2755
|
+
} catch (reason) {
|
|
2756
|
+
try {
|
|
2757
|
+
echo?.abandon?.();
|
|
2758
|
+
} catch {
|
|
2759
|
+
/* ignore */
|
|
2760
|
+
}
|
|
2761
|
+
throw reason;
|
|
2762
|
+
}
|
|
2763
|
+
};
|
|
2764
|
+
|
|
2464
2765
|
/**
|
|
2465
|
-
* Send text into
|
|
2466
|
-
*
|
|
2766
|
+
* Send text into a session. Queue prefers the documented scoped conversation
|
|
2767
|
+
* service; steer and its fallback go through the session face. The delivery
|
|
2768
|
+
* mode follows the General Settings "send while busy" preference.
|
|
2467
2769
|
* @returns {Promise<boolean>} whether a prompt was accepted
|
|
2468
2770
|
*/
|
|
2469
|
-
const sendToSession = async (value) => {
|
|
2470
|
-
const sessionId =
|
|
2771
|
+
const sendToSession = async (value, targetSessionId) => {
|
|
2772
|
+
const sessionId = typeof targetSessionId === 'string' && targetSessionId !== ''
|
|
2773
|
+
? targetSessionId
|
|
2774
|
+
: (rootSessionRef.current !== '' ? rootSessionRef.current : bindingRef.current?.id);
|
|
2471
2775
|
if (typeof sessionId !== 'string' || sessionId === '') return false;
|
|
2472
2776
|
const sessions = ctx.get('sessions');
|
|
2473
2777
|
if (sessions === undefined || sessions === null) return false;
|
|
2778
|
+
const mode = resolveSendMode(sessionId);
|
|
2779
|
+
let scoped;
|
|
2474
2780
|
if (typeof sessions.scope === 'function') {
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
if (conversation !== undefined && conversation !== null && typeof conversation.send === 'function') {
|
|
2480
|
-
await conversation.send(value);
|
|
2481
|
-
return true;
|
|
2781
|
+
try {
|
|
2782
|
+
scoped = sessions.scope(sessionId);
|
|
2783
|
+
} catch {
|
|
2784
|
+
scoped = undefined;
|
|
2482
2785
|
}
|
|
2483
2786
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
if (result !== undefined && result !== null && result.ok === false) {
|
|
2499
|
-
throw new Error(result.error?.message ?? 'prompt failed');
|
|
2500
|
-
}
|
|
2787
|
+
const conversation = scoped === undefined || scoped === null
|
|
2788
|
+
? undefined
|
|
2789
|
+
: typeof scoped.get === 'function' ? scoped.get('conversation') : scoped.conversation;
|
|
2790
|
+
if (mode === 'queue' && conversation !== undefined && conversation !== null
|
|
2791
|
+
&& typeof conversation.send === 'function') {
|
|
2792
|
+
await conversation.send(value);
|
|
2793
|
+
return true;
|
|
2794
|
+
}
|
|
2795
|
+
try {
|
|
2796
|
+
return await promptSession(sessionId, value, mode);
|
|
2797
|
+
} catch (reason) {
|
|
2798
|
+
if (mode === 'steer' && reason?.code === 'session/steer-unavailable') {
|
|
2799
|
+
if (conversation !== undefined && conversation !== null && typeof conversation.send === 'function') {
|
|
2800
|
+
await conversation.send(value);
|
|
2501
2801
|
return true;
|
|
2502
|
-
} catch (reason) {
|
|
2503
|
-
try {
|
|
2504
|
-
echo?.abandon?.();
|
|
2505
|
-
} catch {
|
|
2506
|
-
/* ignore */
|
|
2507
|
-
}
|
|
2508
|
-
throw reason;
|
|
2509
2802
|
}
|
|
2803
|
+
return promptSession(sessionId, value, 'queue');
|
|
2510
2804
|
}
|
|
2805
|
+
throw reason;
|
|
2511
2806
|
}
|
|
2512
|
-
return false;
|
|
2513
2807
|
};
|
|
2514
2808
|
|
|
2515
2809
|
const sendSegments = async (list) => {
|
|
@@ -2550,8 +2844,78 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2550
2844
|
}
|
|
2551
2845
|
};
|
|
2552
2846
|
|
|
2847
|
+
/** Create an empty session in a workspace and return its id. */
|
|
2848
|
+
const createWorkspaceSession = async (cwd) => {
|
|
2849
|
+
const sessions = ctx.get('sessions');
|
|
2850
|
+
if (sessions === undefined || sessions === null || typeof sessions.create !== 'function') return null;
|
|
2851
|
+
const result = await sessions.create({ cwd });
|
|
2852
|
+
if (result === undefined || result === null) return null;
|
|
2853
|
+
if (result.ok === false) {
|
|
2854
|
+
const failure = new Error(result.error?.message ?? 'create session failed');
|
|
2855
|
+
failure.code = result.error?.code;
|
|
2856
|
+
throw failure;
|
|
2857
|
+
}
|
|
2858
|
+
const id = result.value?.sessionId ?? result.sessionId
|
|
2859
|
+
?? (typeof result === 'string' ? result : undefined);
|
|
2860
|
+
return typeof id === 'string' && id !== '' ? id : null;
|
|
2861
|
+
};
|
|
2862
|
+
|
|
2863
|
+
/** Start a fresh session, send the text there, then switch to it. */
|
|
2864
|
+
const sendNewSession = async (list) => {
|
|
2865
|
+
if (sendingRef.current) return;
|
|
2866
|
+
const value = mergedText(list);
|
|
2867
|
+
if (value === '') {
|
|
2868
|
+
showToast(t('insertEmpty'));
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
const cwd = bindingRef.current?.cwd ?? rootRef.current;
|
|
2872
|
+
if (cwd === '') {
|
|
2873
|
+
showToast(t('sendNewUnavailable'));
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
sendingRef.current = true;
|
|
2877
|
+
setSending(true);
|
|
2878
|
+
try {
|
|
2879
|
+
const newSessionId = await createWorkspaceSession(cwd);
|
|
2880
|
+
if (newSessionId === null) {
|
|
2881
|
+
if (!insertValue(value)) await copyText(value);
|
|
2882
|
+
showToast(t('sendNewUnavailable'));
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
const accepted = await sendToSession(value, newSessionId);
|
|
2886
|
+
if (accepted !== true) {
|
|
2887
|
+
if (!insertValue(value)) await copyText(value);
|
|
2888
|
+
showToast(t('sendUnavailable'));
|
|
2889
|
+
return;
|
|
2890
|
+
}
|
|
2891
|
+
showToast(t('sendNewDone'));
|
|
2892
|
+
const ids = new Set(
|
|
2893
|
+
list.filter((segment) => segment.archived !== true).map((segment) => segment.id),
|
|
2894
|
+
);
|
|
2895
|
+
if (ids.size > 0 && (rootRef.current === '' || rootRef.current === cwd)) {
|
|
2896
|
+
const archivedAt = new Date().toISOString();
|
|
2897
|
+
const next = segmentsRef.current.map((segment) =>
|
|
2898
|
+
(ids.has(segment.id) ? { ...segment, archived: true, archivedAt } : segment));
|
|
2899
|
+
setSelection(new Set([...selectedRef.current].filter((id) => !ids.has(id))));
|
|
2900
|
+
commitSegments(next);
|
|
2901
|
+
await saveNowRef.current?.({ force: true });
|
|
2902
|
+
}
|
|
2903
|
+
const uiWorkspace = ctx.get('uiWorkspace');
|
|
2904
|
+
if (uiWorkspace !== undefined && uiWorkspace !== null && typeof uiWorkspace.openSession === 'function') {
|
|
2905
|
+
uiWorkspace.openSession(newSessionId);
|
|
2906
|
+
}
|
|
2907
|
+
} catch (reason) {
|
|
2908
|
+
showToast(`${t('sendFailed')}: ${String(reason?.message ?? reason)}`);
|
|
2909
|
+
} finally {
|
|
2910
|
+
sendingRef.current = false;
|
|
2911
|
+
setSending(false);
|
|
2912
|
+
}
|
|
2913
|
+
};
|
|
2914
|
+
|
|
2553
2915
|
const copyAll = async () => {
|
|
2554
|
-
|
|
2916
|
+
// Copy only active work: archived segments were already sent or filed
|
|
2917
|
+
// away and must not leak back into a copy-all.
|
|
2918
|
+
const value = mergedText(segmentsRef.current.filter((segment) => segment.archived !== true));
|
|
2555
2919
|
if (value === '') {
|
|
2556
2920
|
showToast(t('insertEmpty'));
|
|
2557
2921
|
return;
|
|
@@ -2688,7 +3052,7 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2688
3052
|
if (ui.minimized) return;
|
|
2689
3053
|
if (event.button !== undefined && event.button !== 0) return;
|
|
2690
3054
|
event.preventDefault?.();
|
|
2691
|
-
event
|
|
3055
|
+
event?.stopPropagation?.();
|
|
2692
3056
|
const start = { px: event.clientX, py: event.clientY, width: ui.width, height: ui.height };
|
|
2693
3057
|
resizeRef.current = start;
|
|
2694
3058
|
setResizing(true);
|
|
@@ -2734,6 +3098,7 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2734
3098
|
const allSelected = activeSegments.length > 0 && selectedCount === activeSegments.length;
|
|
2735
3099
|
const unsentCount = countUnsent(segments);
|
|
2736
3100
|
const totalChars = segments.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
3101
|
+
const activeChars = activeSegments.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
2737
3102
|
const displayWidth = ui.width;
|
|
2738
3103
|
const displayHeight = ui.minimized ? HEAD_H : ui.height;
|
|
2739
3104
|
const fallbackX = Math.max(EDGE_GAP, ((typeof window !== 'undefined' && window.innerWidth) || 1280) - displayWidth - 24);
|
|
@@ -2761,6 +3126,17 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2761
3126
|
h('span', { className: 'dsh-np-title' }, t('title')),
|
|
2762
3127
|
h('span', { className: 'dsh-np-workspace', title: binding.cwd }, workspaceName(binding.cwd)),
|
|
2763
3128
|
h('span', { className: 'dsh-np-spacer' }),
|
|
3129
|
+
h(
|
|
3130
|
+
'button',
|
|
3131
|
+
{
|
|
3132
|
+
type: 'button',
|
|
3133
|
+
className: 'dsh-np-btn',
|
|
3134
|
+
title: `${t('resetShortcut')} · Ctrl+Shift+0`,
|
|
3135
|
+
'aria-label': t('resetShortcut'),
|
|
3136
|
+
onClick: resetGeometry,
|
|
3137
|
+
},
|
|
3138
|
+
h(ResetIcon, { size: 15 }),
|
|
3139
|
+
),
|
|
2764
3140
|
h(
|
|
2765
3141
|
'button',
|
|
2766
3142
|
{
|
|
@@ -2768,7 +3144,7 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2768
3144
|
className: 'dsh-np-btn',
|
|
2769
3145
|
title: t('copy'),
|
|
2770
3146
|
'aria-label': t('copy'),
|
|
2771
|
-
disabled:
|
|
3147
|
+
disabled: activeChars === 0,
|
|
2772
3148
|
onClick: copyAll,
|
|
2773
3149
|
},
|
|
2774
3150
|
h(CopyIcon, { size: 15 }),
|
|
@@ -2821,6 +3197,37 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2821
3197
|
),
|
|
2822
3198
|
);
|
|
2823
3199
|
|
|
3200
|
+
/** One icon that expands into current-session + new-session actions. */
|
|
3201
|
+
const actionGroup = ({ size, primary, extra, disabled = false }) => {
|
|
3202
|
+
const base = size === 'bar' ? 'dsh-np-btn' : 'dsh-np-segbtn';
|
|
3203
|
+
const primaryProps = {
|
|
3204
|
+
type: 'button',
|
|
3205
|
+
className: base,
|
|
3206
|
+
title: primary.title,
|
|
3207
|
+
'aria-label': primary.title,
|
|
3208
|
+
disabled,
|
|
3209
|
+
onClick: primary.onClick,
|
|
3210
|
+
};
|
|
3211
|
+
if (primary.accent === true) primaryProps['data-accent'] = 'true';
|
|
3212
|
+
return h(
|
|
3213
|
+
'div',
|
|
3214
|
+
{ className: 'dsh-np-actiongroup', 'data-size': size },
|
|
3215
|
+
h('button', primaryProps, primary.icon),
|
|
3216
|
+
h(
|
|
3217
|
+
'button',
|
|
3218
|
+
{
|
|
3219
|
+
type: 'button',
|
|
3220
|
+
className: `${base} dsh-np-group-extra`,
|
|
3221
|
+
title: extra.title,
|
|
3222
|
+
'aria-label': extra.title,
|
|
3223
|
+
disabled,
|
|
3224
|
+
onClick: extra.onClick,
|
|
3225
|
+
},
|
|
3226
|
+
h(NewSessionIcon, { size: 14 }),
|
|
3227
|
+
),
|
|
3228
|
+
);
|
|
3229
|
+
};
|
|
3230
|
+
|
|
2824
3231
|
const tools = h(
|
|
2825
3232
|
'div',
|
|
2826
3233
|
{ className: 'dsh-np-tools' },
|
|
@@ -2838,18 +3245,19 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2838
3245
|
selectedCount > 0 ? t('selected', { n: selectedCount }) : t('selectHint')),
|
|
2839
3246
|
),
|
|
2840
3247
|
h('span', { className: 'dsh-np-spacer' }),
|
|
2841
|
-
|
|
2842
|
-
'
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
className: 'dsh-np-btn',
|
|
3248
|
+
actionGroup({
|
|
3249
|
+
size: 'bar',
|
|
3250
|
+
disabled: selectedCount === 0,
|
|
3251
|
+
primary: {
|
|
2846
3252
|
title: selectedCount > 0 ? t('insertSelected', { n: selectedCount }) : t('insert'),
|
|
2847
|
-
'aria-label': t('insert'),
|
|
2848
|
-
disabled: selectedCount === 0,
|
|
2849
3253
|
onClick: () => insertSegments(selectedActive),
|
|
3254
|
+
icon: h(InsertIcon, { size: 14 }),
|
|
2850
3255
|
},
|
|
2851
|
-
|
|
2852
|
-
|
|
3256
|
+
extra: {
|
|
3257
|
+
title: selectedCount > 0 ? t('insertNewSelected', { n: selectedCount }) : t('insertNewSession'),
|
|
3258
|
+
onClick: () => insertNewSession(selectedActive),
|
|
3259
|
+
},
|
|
3260
|
+
}),
|
|
2853
3261
|
h(
|
|
2854
3262
|
'button',
|
|
2855
3263
|
{
|
|
@@ -2862,21 +3270,22 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
2862
3270
|
},
|
|
2863
3271
|
h(ReplaceIcon, { size: 14 }),
|
|
2864
3272
|
),
|
|
2865
|
-
|
|
2866
|
-
'
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
className: 'dsh-np-btn',
|
|
2870
|
-
'data-accent': 'true',
|
|
3273
|
+
actionGroup({
|
|
3274
|
+
size: 'bar',
|
|
3275
|
+
disabled: selectedCount === 0 || sending,
|
|
3276
|
+
primary: {
|
|
2871
3277
|
title: selectedCount > 0
|
|
2872
3278
|
? (sending ? t('sending') : t('sendSelected', { n: selectedCount }))
|
|
2873
3279
|
: t('send'),
|
|
2874
|
-
'aria-label': t('send'),
|
|
2875
|
-
disabled: selectedCount === 0 || sending,
|
|
2876
3280
|
onClick: () => sendSegments(selectedActive),
|
|
3281
|
+
icon: h(SendIcon, { size: 14 }),
|
|
3282
|
+
accent: true,
|
|
2877
3283
|
},
|
|
2878
|
-
|
|
2879
|
-
|
|
3284
|
+
extra: {
|
|
3285
|
+
title: selectedCount > 0 ? t('sendNewSelected', { n: selectedCount }) : t('sendNewSession'),
|
|
3286
|
+
onClick: () => sendNewSession(selectedActive),
|
|
3287
|
+
},
|
|
3288
|
+
}),
|
|
2880
3289
|
h(
|
|
2881
3290
|
'button',
|
|
2882
3291
|
{
|
|
@@ -3109,7 +3518,6 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
3109
3518
|
'aria-label': t('segmentTitle'),
|
|
3110
3519
|
onClick: () => setTitleEditing(segment.id),
|
|
3111
3520
|
},
|
|
3112
|
-
t('titlePlaceholder'),
|
|
3113
3521
|
),
|
|
3114
3522
|
h(
|
|
3115
3523
|
'button',
|
|
@@ -3122,18 +3530,19 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
3122
3530
|
},
|
|
3123
3531
|
h(ImageIcon, { size: 14 }),
|
|
3124
3532
|
),
|
|
3125
|
-
|
|
3126
|
-
'
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
className: 'dsh-np-segbtn',
|
|
3533
|
+
actionGroup({
|
|
3534
|
+
size: 'card',
|
|
3535
|
+
disabled: empty,
|
|
3536
|
+
primary: {
|
|
3130
3537
|
title: t('insertSegment'),
|
|
3131
|
-
'aria-label': t('insertSegment'),
|
|
3132
|
-
disabled: empty,
|
|
3133
3538
|
onClick: () => insertSegments([segment]),
|
|
3539
|
+
icon: h(InsertIcon, { size: 14 }),
|
|
3134
3540
|
},
|
|
3135
|
-
|
|
3136
|
-
|
|
3541
|
+
extra: {
|
|
3542
|
+
title: t('insertNewSession'),
|
|
3543
|
+
onClick: () => insertNewSession([segment]),
|
|
3544
|
+
},
|
|
3545
|
+
}),
|
|
3137
3546
|
h(
|
|
3138
3547
|
'button',
|
|
3139
3548
|
{
|
|
@@ -3146,18 +3555,19 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
3146
3555
|
},
|
|
3147
3556
|
h(ReplaceIcon, { size: 14 }),
|
|
3148
3557
|
),
|
|
3149
|
-
|
|
3150
|
-
'
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
className: 'dsh-np-segbtn',
|
|
3558
|
+
actionGroup({
|
|
3559
|
+
size: 'card',
|
|
3560
|
+
disabled: empty || sending,
|
|
3561
|
+
primary: {
|
|
3154
3562
|
title: t('sendSegment'),
|
|
3155
|
-
'aria-label': t('sendSegment'),
|
|
3156
|
-
disabled: empty || sending,
|
|
3157
3563
|
onClick: () => sendSegments([segment]),
|
|
3564
|
+
icon: h(SendIcon, { size: 14 }),
|
|
3158
3565
|
},
|
|
3159
|
-
|
|
3160
|
-
|
|
3566
|
+
extra: {
|
|
3567
|
+
title: t('sendNewSession'),
|
|
3568
|
+
onClick: () => sendNewSession([segment]),
|
|
3569
|
+
},
|
|
3570
|
+
}),
|
|
3161
3571
|
h(
|
|
3162
3572
|
'button',
|
|
3163
3573
|
{
|
|
@@ -3186,6 +3596,18 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
3186
3596
|
},
|
|
3187
3597
|
h(ArchiveIcon, { size: 14 }),
|
|
3188
3598
|
),
|
|
3599
|
+
h(
|
|
3600
|
+
'button',
|
|
3601
|
+
{
|
|
3602
|
+
type: 'button',
|
|
3603
|
+
className: 'dsh-np-segbtn',
|
|
3604
|
+
'data-danger': 'true',
|
|
3605
|
+
title: t('deleteActiveSegment'),
|
|
3606
|
+
'aria-label': t('deleteActiveSegment'),
|
|
3607
|
+
onClick: () => setDeleteConfirm(segment.id),
|
|
3608
|
+
},
|
|
3609
|
+
h(TrashIcon, { size: 14 }),
|
|
3610
|
+
),
|
|
3189
3611
|
),
|
|
3190
3612
|
h(
|
|
3191
3613
|
'div',
|
|
@@ -3382,7 +3804,17 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
3382
3804
|
h('span', { className: 'dsh-np-count' },
|
|
3383
3805
|
`${t('chars', { n: totalChars })} · ${t('segments', { n: activeSegments.length })}`
|
|
3384
3806
|
+ (totalImages > 0 ? ` · ${t('images', { n: totalImages })}` : '')),
|
|
3385
|
-
h(
|
|
3807
|
+
h(
|
|
3808
|
+
'button',
|
|
3809
|
+
{
|
|
3810
|
+
type: 'button',
|
|
3811
|
+
className: 'dsh-np-hint',
|
|
3812
|
+
title: t('hintNext'),
|
|
3813
|
+
'aria-label': t('hintNext'),
|
|
3814
|
+
onClick: () => setHintIndex((index) => (index + 1) % HINT_KEYS.length),
|
|
3815
|
+
},
|
|
3816
|
+
t(HINT_KEYS[hintIndex % HINT_KEYS.length]),
|
|
3817
|
+
),
|
|
3386
3818
|
);
|
|
3387
3819
|
|
|
3388
3820
|
const toastNode = toast === null
|
|
@@ -3422,6 +3854,107 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
3422
3854
|
onPointerDown: onResizePointerDown,
|
|
3423
3855
|
});
|
|
3424
3856
|
|
|
3857
|
+
const previewNode = previewImage === null
|
|
3858
|
+
? null
|
|
3859
|
+
: h(
|
|
3860
|
+
'div',
|
|
3861
|
+
{
|
|
3862
|
+
className: 'dsh-np-preview',
|
|
3863
|
+
role: 'dialog',
|
|
3864
|
+
'aria-modal': 'true',
|
|
3865
|
+
'aria-label': t('imagePreview'),
|
|
3866
|
+
onClick: () => setPreviewImage(null),
|
|
3867
|
+
},
|
|
3868
|
+
h('img', {
|
|
3869
|
+
className: 'dsh-np-preview-img',
|
|
3870
|
+
src: imageUrl(previewImage.path, binding.cwd),
|
|
3871
|
+
alt: previewImage.name,
|
|
3872
|
+
onClick: (event) => event?.stopPropagation?.(),
|
|
3873
|
+
}),
|
|
3874
|
+
h(
|
|
3875
|
+
'div',
|
|
3876
|
+
{ className: 'dsh-np-preview-bar' },
|
|
3877
|
+
h(
|
|
3878
|
+
'button',
|
|
3879
|
+
{
|
|
3880
|
+
type: 'button',
|
|
3881
|
+
className: 'dsh-np-preview-btn',
|
|
3882
|
+
title: t('openOriginal'),
|
|
3883
|
+
'aria-label': t('openOriginal'),
|
|
3884
|
+
onClick: (event) => {
|
|
3885
|
+
event?.stopPropagation?.();
|
|
3886
|
+
openOriginalImage(previewImage);
|
|
3887
|
+
},
|
|
3888
|
+
},
|
|
3889
|
+
h(OpenIcon, { size: 15 }),
|
|
3890
|
+
),
|
|
3891
|
+
h(
|
|
3892
|
+
'button',
|
|
3893
|
+
{
|
|
3894
|
+
type: 'button',
|
|
3895
|
+
className: 'dsh-np-preview-btn',
|
|
3896
|
+
title: t('closePreview'),
|
|
3897
|
+
'aria-label': t('closePreview'),
|
|
3898
|
+
onClick: (event) => {
|
|
3899
|
+
event?.stopPropagation?.();
|
|
3900
|
+
setPreviewImage(null);
|
|
3901
|
+
},
|
|
3902
|
+
},
|
|
3903
|
+
h(CloseIcon, { size: 15 }),
|
|
3904
|
+
),
|
|
3905
|
+
),
|
|
3906
|
+
h('div', { className: 'dsh-np-preview-name' }, previewImage.name),
|
|
3907
|
+
);
|
|
3908
|
+
|
|
3909
|
+
const deleteTarget = deleteConfirm === ''
|
|
3910
|
+
? undefined
|
|
3911
|
+
: segments.find((segment) => segment.id === deleteConfirm && segment.archived !== true);
|
|
3912
|
+
const confirmNode = deleteTarget === undefined
|
|
3913
|
+
? null
|
|
3914
|
+
: h(
|
|
3915
|
+
'div',
|
|
3916
|
+
{
|
|
3917
|
+
className: 'dsh-np-confirm',
|
|
3918
|
+
role: 'alertdialog',
|
|
3919
|
+
'aria-modal': 'true',
|
|
3920
|
+
'aria-label': t('deleteConfirmTitle'),
|
|
3921
|
+
onClick: () => setDeleteConfirm(''),
|
|
3922
|
+
},
|
|
3923
|
+
h(
|
|
3924
|
+
'div',
|
|
3925
|
+
{
|
|
3926
|
+
className: 'dsh-np-confirm-card',
|
|
3927
|
+
onClick: (event) => event?.stopPropagation?.(),
|
|
3928
|
+
},
|
|
3929
|
+
h('div', { className: 'dsh-np-confirm-title' }, t('deleteConfirmTitle')),
|
|
3930
|
+
h('div', { className: 'dsh-np-confirm-body' }, t('deleteConfirmBody')),
|
|
3931
|
+
h('div', { className: 'dsh-np-confirm-hint' }, t('deleteConfirmHint')),
|
|
3932
|
+
h(
|
|
3933
|
+
'div',
|
|
3934
|
+
{ className: 'dsh-np-confirm-actions' },
|
|
3935
|
+
h(
|
|
3936
|
+
'button',
|
|
3937
|
+
{
|
|
3938
|
+
type: 'button',
|
|
3939
|
+
className: 'dsh-np-confirm-btn',
|
|
3940
|
+
onClick: () => setDeleteConfirm(''),
|
|
3941
|
+
},
|
|
3942
|
+
t('deleteConfirmCancel'),
|
|
3943
|
+
),
|
|
3944
|
+
h(
|
|
3945
|
+
'button',
|
|
3946
|
+
{
|
|
3947
|
+
type: 'button',
|
|
3948
|
+
className: 'dsh-np-confirm-btn',
|
|
3949
|
+
'data-danger': 'true',
|
|
3950
|
+
onClick: () => deleteActiveSegment(deleteTarget.id),
|
|
3951
|
+
},
|
|
3952
|
+
t('deleteConfirmOk'),
|
|
3953
|
+
),
|
|
3954
|
+
),
|
|
3955
|
+
),
|
|
3956
|
+
);
|
|
3957
|
+
|
|
3425
3958
|
const card = h(
|
|
3426
3959
|
'section',
|
|
3427
3960
|
{
|
|
@@ -3444,6 +3977,8 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
3444
3977
|
},
|
|
3445
3978
|
header,
|
|
3446
3979
|
body,
|
|
3980
|
+
previewNode,
|
|
3981
|
+
confirmNode,
|
|
3447
3982
|
resizeHandle,
|
|
3448
3983
|
);
|
|
3449
3984
|
|
|
@@ -3509,6 +4044,43 @@ body[data-ds-dark-theme] .dsh-np-seg[data-archived=true]{opacity:.7}
|
|
|
3509
4044
|
};
|
|
3510
4045
|
}, 'dsh-notepad: dictionaries');
|
|
3511
4046
|
|
|
4047
|
+
// Register the geometry-reset chord with DSH's shortcut service so it
|
|
4048
|
+
// reaches the page even when the host intercepts native key bindings.
|
|
4049
|
+
const installResetShortcut = (scope) => {
|
|
4050
|
+
const service = typeof scope.get === 'function' ? scope.get('shortcuts') : undefined;
|
|
4051
|
+
if (service === undefined || service === null || typeof service.register !== 'function') return;
|
|
4052
|
+
try {
|
|
4053
|
+
scope.effect(() => service.register({
|
|
4054
|
+
id: 'notepad.resetGeometry',
|
|
4055
|
+
label: () => translate('resetShortcut'),
|
|
4056
|
+
aliases: ['reset notepad', 'reset notepad geometry'],
|
|
4057
|
+
defaults: {
|
|
4058
|
+
'desktop:macos': { code: 'Digit0', modifiers: ['primary', 'shift'] },
|
|
4059
|
+
'desktop:windows': { code: 'Digit0', modifiers: ['primary', 'shift'] },
|
|
4060
|
+
'desktop:linux': { code: 'Digit0', modifiers: ['primary', 'shift'] },
|
|
4061
|
+
'web:macos': { code: 'Digit0', modifiers: ['primary', 'shift'] },
|
|
4062
|
+
'web:windows': { code: 'Digit0', modifiers: ['primary', 'shift'] },
|
|
4063
|
+
'web:linux': { code: 'Digit0', modifiers: ['primary', 'shift'] },
|
|
4064
|
+
},
|
|
4065
|
+
regions: ['page', 'editable'],
|
|
4066
|
+
modals: [],
|
|
4067
|
+
resolve: () => ({ status: 'handled', run: requestUiReset }),
|
|
4068
|
+
}), 'dsh-notepad: reset shortcut');
|
|
4069
|
+
resetShortcutHandledByHost = true;
|
|
4070
|
+
} catch (error) {
|
|
4071
|
+
console.warn('[dsh-notepad] reset shortcut registration failed:', error?.message ?? error);
|
|
4072
|
+
}
|
|
4073
|
+
};
|
|
4074
|
+
if (ctx.get('shortcuts') !== undefined) {
|
|
4075
|
+
installResetShortcut(ctx);
|
|
4076
|
+
} else if (typeof ctx.inject === 'function') {
|
|
4077
|
+
try {
|
|
4078
|
+
ctx.inject(['shortcuts'], (scoped) => installResetShortcut(scoped));
|
|
4079
|
+
} catch {
|
|
4080
|
+
/* The raw key listener and header button remain as fallbacks. */
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
|
|
3512
4084
|
const slots = ctx.get('slots');
|
|
3513
4085
|
if (slots === undefined || typeof slots.register !== 'function' || typeof slots.inject !== 'function') {
|
|
3514
4086
|
console.warn('[dsh-notepad] slots service missing; floating notepad disabled');
|