dsh-figmark 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/lib/client.js +403 -298
- package/lib/index.js +20 -5
- package/market/index.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.4 — 2026-11-06
|
|
4
|
+
|
|
5
|
+
本版统一批注卡片的预览与发送方式,修复缩放网页导出错位,并改进本机网页的检索和状态保留。
|
|
6
|
+
|
|
7
|
+
### Improved
|
|
8
|
+
|
|
9
|
+
- 待发送标注进入输入框原生附件区;输入框为空时也可直接点右下角发送,或补充文字后一起发送。
|
|
10
|
+
- 标注卡片的重复附件缩略图合并显示,其他手动附件继续保留;卡片本身只负责预览与移除。
|
|
11
|
+
- 「本机网页」切换侧栏后保留当前页面、代理连接和未发送标注,返回时无需重新检索。
|
|
12
|
+
- 本机网页检索合并首页与 `index.html` 的重复结果;多个结果匹配时要求明确选择。
|
|
13
|
+
- 精简本机网页列表的标题、说明、搜索框和结果布局。
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- 修复缩放网页导出时按可视尺寸重新排版,导致网页标题和截图内容错位的问题。
|
|
18
|
+
- 修复 HTML 标注卡片把任意 URL 当作普通文件打开的问题。
|
|
19
|
+
- 修复 PDF 多页标注附件在原生输入框中的识别和发送状态。
|
|
20
|
+
|
|
3
21
|
## 1.2.3 — 2026-11-02
|
|
4
22
|
|
|
5
23
|
本版增加输入框附图标注,并修复导出 SVG 与画布 Pin 编号的显示问题。
|
package/lib/client.js
CHANGED
|
@@ -73,15 +73,17 @@ window.__ModuleLoader__.load({
|
|
|
73
73
|
reveal_hidden: "元素目前不可见,请先展开或翻到对应页面",
|
|
74
74
|
reveal_wrong_surface: "无法切换到标注所在的页面",
|
|
75
75
|
reveal_timeout: "页面没有响应定位请求",
|
|
76
|
-
serviceKicker: "LOCAL RUNTIME",
|
|
77
76
|
serviceTitle: "本机网页",
|
|
78
|
-
serviceHint: "
|
|
79
|
-
serviceSearch: "
|
|
77
|
+
serviceHint: "打开正在运行的本机网页,直接圈画与批注。",
|
|
78
|
+
serviceSearch: "搜索页面,或输入 localhost:5173",
|
|
80
79
|
serviceOpen: "打开",
|
|
81
80
|
serviceOpening: "正在连接",
|
|
82
81
|
serviceLocal: "正在运行",
|
|
83
82
|
serviceScanning: "检索中",
|
|
84
83
|
serviceRescan: "重新检索",
|
|
84
|
+
serviceNoMatch: "没有匹配的页面",
|
|
85
|
+
serviceChooseMatch: "找到多个页面,请选择下方的一项。",
|
|
86
|
+
serviceSearchHint: "换个关键词,或输入完整的本机地址。",
|
|
85
87
|
serviceEmpty: "没有发现本机服务",
|
|
86
88
|
serviceEmptyHint: "先启动开发服务器,或在上方直接输入地址。",
|
|
87
89
|
serviceBadAddress: "请输入本机 HTTP 地址",
|
|
@@ -169,18 +171,25 @@ window.__ModuleLoader__.load({
|
|
|
169
171
|
detailHint: "当前选中",
|
|
170
172
|
detailUnset: "未选中",
|
|
171
173
|
detailIdle: "在图上点一下、框一块或画一笔,就能为那处写说明。",
|
|
172
|
-
cardOpen: "
|
|
174
|
+
cardOpen: "打开原文件",
|
|
173
175
|
cardOpenLive: "回到本机网页",
|
|
174
176
|
cardCaptureOmitted: "位置标注",
|
|
175
177
|
cardCaptureFailed: "截图失败",
|
|
176
|
-
cardMore: "
|
|
178
|
+
cardMore: "查看其余 {n} 处",
|
|
179
|
+
cardExpand: "查看完整批注",
|
|
180
|
+
cardCollapse: "收起详情",
|
|
181
|
+
cardOpenFailed: "未能打开原内容,请重试。",
|
|
177
182
|
cardGoal: "目标",
|
|
178
183
|
cardPage: "第 {n} 页",
|
|
184
|
+
cardPages: "共 {n} 页",
|
|
179
185
|
cardCount: "{n} 处标注",
|
|
180
186
|
pendingCount: "{n}",
|
|
181
187
|
pendingRemove: "移除附图标注",
|
|
182
|
-
pendingSendHint: "
|
|
183
|
-
|
|
188
|
+
pendingSendHint: "发送当前消息及全部待发送标注",
|
|
189
|
+
pendingSendMessage: "发送消息",
|
|
190
|
+
pendingSending: "发送中…",
|
|
191
|
+
pendingUnavailable: "当前输入框暂时无法发送。",
|
|
192
|
+
pendingSendNow: "发送这份标注",
|
|
184
193
|
pendingFailed: "没能发送:"
|
|
185
194
|
};
|
|
186
195
|
/** English copy. */
|
|
@@ -223,15 +232,17 @@ window.__ModuleLoader__.load({
|
|
|
223
232
|
reveal_hidden: "The element is not visible yet",
|
|
224
233
|
reveal_wrong_surface: "Could not switch to the mark’s page",
|
|
225
234
|
reveal_timeout: "The page did not answer the locate request",
|
|
226
|
-
serviceKicker: "LOCAL RUNTIME",
|
|
227
235
|
serviceTitle: "Local pages",
|
|
228
|
-
serviceHint: "
|
|
229
|
-
serviceSearch: "
|
|
236
|
+
serviceHint: "Open a running local page to draw and comment.",
|
|
237
|
+
serviceSearch: "Find a page or enter localhost:5173",
|
|
230
238
|
serviceOpen: "Open",
|
|
231
239
|
serviceOpening: "Connecting",
|
|
232
240
|
serviceLocal: "Running",
|
|
233
241
|
serviceScanning: "Scanning",
|
|
234
242
|
serviceRescan: "Scan again",
|
|
243
|
+
serviceNoMatch: "No matching pages",
|
|
244
|
+
serviceChooseMatch: "Several pages match. Choose one below.",
|
|
245
|
+
serviceSearchHint: "Try another keyword or enter the full local address.",
|
|
235
246
|
serviceEmpty: "No local services found",
|
|
236
247
|
serviceEmptyHint: "Start a development server, or type its address above.",
|
|
237
248
|
serviceBadAddress: "Enter a local HTTP address",
|
|
@@ -319,18 +330,25 @@ window.__ModuleLoader__.load({
|
|
|
319
330
|
detailHint: "selected",
|
|
320
331
|
detailUnset: "nothing selected",
|
|
321
332
|
detailIdle: "Click the figure, or box a region or draw a stroke, to write about that spot.",
|
|
322
|
-
cardOpen: "Open
|
|
333
|
+
cardOpen: "Open source file",
|
|
323
334
|
cardOpenLive: "Return to the local page",
|
|
324
335
|
cardCaptureOmitted: "location only",
|
|
325
336
|
cardCaptureFailed: "screenshot failed",
|
|
326
|
-
cardMore: "
|
|
337
|
+
cardMore: "Show {n} more",
|
|
338
|
+
cardExpand: "Review all annotations",
|
|
339
|
+
cardCollapse: "Collapse details",
|
|
340
|
+
cardOpenFailed: "Could not open the source. Try again.",
|
|
327
341
|
cardGoal: "Goal",
|
|
328
342
|
cardPage: "page {n}",
|
|
343
|
+
cardPages: "{n} pages",
|
|
329
344
|
cardCount: "{n} annotations",
|
|
330
345
|
pendingCount: "{n}",
|
|
331
346
|
pendingRemove: "Remove the annotation",
|
|
332
|
-
pendingSendHint: "Send
|
|
333
|
-
|
|
347
|
+
pendingSendHint: "Send the message and all staged reviews",
|
|
348
|
+
pendingSendMessage: "Send message",
|
|
349
|
+
pendingSending: "Sending…",
|
|
350
|
+
pendingUnavailable: "This composer cannot send right now.",
|
|
351
|
+
pendingSendNow: "Send this review",
|
|
334
352
|
pendingFailed: "Could not send: "
|
|
335
353
|
};
|
|
336
354
|
/** Locale namespace this dictionary registers under. */
|
|
@@ -4815,14 +4833,14 @@ window.__ModuleLoader__.load({
|
|
|
4815
4833
|
* Upload one file as a draft attachment without putting it in the rail yet.
|
|
4816
4834
|
* @param conversation - the conversation service.
|
|
4817
4835
|
* @param sessionId - session the draft belongs to.
|
|
4818
|
-
* @param
|
|
4836
|
+
* @param files - the rendered review images to hold.
|
|
4819
4837
|
* @returns the ids the send will add, empty when the file could not be made into one.
|
|
4820
4838
|
*/
|
|
4821
|
-
function
|
|
4839
|
+
function holdFiles(conversation, sessionId, files) {
|
|
4822
4840
|
const create = conversation.createDrafts?.bind(conversation);
|
|
4823
|
-
if (
|
|
4841
|
+
if (files.length === 0 || create === void 0) return [];
|
|
4824
4842
|
try {
|
|
4825
|
-
return create(sessionId,
|
|
4843
|
+
return create(sessionId, files);
|
|
4826
4844
|
} catch {
|
|
4827
4845
|
return [];
|
|
4828
4846
|
}
|
|
@@ -4833,9 +4851,10 @@ window.__ModuleLoader__.load({
|
|
|
4833
4851
|
* @param sessionId - session the draft belongs to.
|
|
4834
4852
|
* @param message - the message, verbatim.
|
|
4835
4853
|
* @param file - the review picture, when the surface has one to attach.
|
|
4854
|
+
* @param additionalFiles - marked page images of a multi-page review.
|
|
4836
4855
|
* @returns what was armed, or undefined when this session cannot be armed.
|
|
4837
4856
|
*/
|
|
4838
|
-
function armReview(conversation, sessionId, message, file) {
|
|
4857
|
+
function armReview(conversation, sessionId, message, file, additionalFiles = []) {
|
|
4839
4858
|
let shell;
|
|
4840
4859
|
try {
|
|
4841
4860
|
shell = conversation.input.shell(sessionId);
|
|
@@ -4843,10 +4862,12 @@ window.__ModuleLoader__.load({
|
|
|
4843
4862
|
return;
|
|
4844
4863
|
}
|
|
4845
4864
|
if (typeof shell.submit !== "function") return void 0;
|
|
4865
|
+
const drafts = holdFiles(conversation, sessionId, file === void 0 ? additionalFiles : [file, ...additionalFiles]);
|
|
4846
4866
|
return {
|
|
4847
4867
|
shell,
|
|
4848
4868
|
text: message,
|
|
4849
|
-
attachmentIds:
|
|
4869
|
+
attachmentIds: drafts.map((draft) => draft.id),
|
|
4870
|
+
previewUrls: drafts.flatMap((draft) => draft.previewUrl === void 0 ? [] : [draft.previewUrl])
|
|
4850
4871
|
};
|
|
4851
4872
|
}
|
|
4852
4873
|
/**
|
|
@@ -4909,12 +4930,13 @@ window.__ModuleLoader__.load({
|
|
|
4909
4930
|
for (const claim of claims) {
|
|
4910
4931
|
const { text, attachmentIds } = claim.armed;
|
|
4911
4932
|
if (draft.includes(text)) continue;
|
|
4912
|
-
|
|
4913
|
-
|
|
4933
|
+
const missing = attachmentIds.filter((id) => !live.attachmentIds?.includes(id));
|
|
4934
|
+
if (missing.length > 0) {
|
|
4935
|
+
if (!attach(shell, missing)) {
|
|
4914
4936
|
unwind(shell, typed, added);
|
|
4915
4937
|
return;
|
|
4916
4938
|
}
|
|
4917
|
-
added.push(...
|
|
4939
|
+
added.push(...missing);
|
|
4918
4940
|
}
|
|
4919
4941
|
draft = draft.trim() === "" ? text : `${draft}\n\n${text}`;
|
|
4920
4942
|
}
|
|
@@ -4974,7 +4996,7 @@ window.__ModuleLoader__.load({
|
|
|
4974
4996
|
try {
|
|
4975
4997
|
shell.setDraft(merge.typed);
|
|
4976
4998
|
} catch {}
|
|
4977
|
-
for (const claim of claims) for (const id of claim.armed.attachmentIds) shell.removeAttachment?.(id);
|
|
4999
|
+
for (const claim of claims) if (claim.armed.staged !== true) for (const id of claim.armed.attachmentIds) shell.removeAttachment?.(id);
|
|
4978
5000
|
}
|
|
4979
5001
|
/**
|
|
4980
5002
|
* Take one card off whatever patch its shell is currently carrying.
|
|
@@ -5047,9 +5069,9 @@ window.__ModuleLoader__.load({
|
|
|
5047
5069
|
const send = () => {
|
|
5048
5070
|
try {
|
|
5049
5071
|
original.apply(shell, args);
|
|
5050
|
-
settleClaims(shell, claims, merge);
|
|
5051
5072
|
} finally {
|
|
5052
5073
|
waiting = false;
|
|
5074
|
+
settleClaims(shell, claims, merge);
|
|
5053
5075
|
}
|
|
5054
5076
|
};
|
|
5055
5077
|
if (wroteThrough(shell, merge)) {
|
|
@@ -5102,13 +5124,27 @@ window.__ModuleLoader__.load({
|
|
|
5102
5124
|
*/
|
|
5103
5125
|
function parkInComposer(input) {
|
|
5104
5126
|
const { document: document_, locale, paths, sessionId } = input;
|
|
5105
|
-
|
|
5127
|
+
let armed = input.conversation === void 0 ? void 0 : armReview(input.conversation, sessionId, "", input.file, input.files);
|
|
5128
|
+
const planned = (input.file === void 0 ? 0 : 1) + (input.files?.length ?? 0);
|
|
5129
|
+
const attached = armed?.attachmentIds.length ?? planned;
|
|
5130
|
+
const pageCapture = input.pageCapture === "attached" && attached === 0 ? "failed" : input.pageCapture;
|
|
5131
|
+
const message = buildAnnotationMessage(document_, paths, locale, attached > 0, Math.min(input.pageImages ?? 0, attached), pageCapture);
|
|
5132
|
+
if (armed !== void 0 && input.conversation !== void 0) {
|
|
5133
|
+
const ids = armed.attachmentIds.length > 0 ? armed.attachmentIds : holdFiles(input.conversation, sessionId, [new File([message], "annotation.txt", { type: "text/plain" })]).map((draft) => draft.id);
|
|
5134
|
+
if (ids.length > 0 && attach(armed.shell, ids)) armed = {
|
|
5135
|
+
...armed,
|
|
5136
|
+
attachmentIds: ids,
|
|
5137
|
+
staged: true
|
|
5138
|
+
};
|
|
5139
|
+
else {
|
|
5140
|
+
for (const id of ids) input.conversation.releaseDraftAttachment?.(id);
|
|
5141
|
+
armed = void 0;
|
|
5142
|
+
}
|
|
5143
|
+
}
|
|
5106
5144
|
rememberDelivered(message, input.thumb);
|
|
5107
|
-
const block = matchAnnotation(message, 0);
|
|
5108
|
-
const armed = input.conversation === void 0 ? void 0 : armReview(input.conversation, sessionId, message, input.file);
|
|
5109
5145
|
parkAnnotation({
|
|
5110
5146
|
sessionId,
|
|
5111
|
-
data:
|
|
5147
|
+
data: matchAnnotation(message, 0)?.data ?? {
|
|
5112
5148
|
file: document_.figure.path,
|
|
5113
5149
|
scope: "",
|
|
5114
5150
|
target: "",
|
|
@@ -5119,7 +5155,10 @@ window.__ModuleLoader__.load({
|
|
|
5119
5155
|
},
|
|
5120
5156
|
raw: message,
|
|
5121
5157
|
conversation: input.conversation,
|
|
5122
|
-
armed
|
|
5158
|
+
armed: armed === void 0 ? void 0 : {
|
|
5159
|
+
...armed,
|
|
5160
|
+
text: message
|
|
5161
|
+
},
|
|
5123
5162
|
send: input.send
|
|
5124
5163
|
});
|
|
5125
5164
|
}
|
|
@@ -8792,19 +8831,20 @@ body[data-ds-dark-theme] .da-root{--da-quiet:#9aa0a8}
|
|
|
8792
8831
|
border:0.5px solid var(--dsw-alias-border-l2,rgba(128,128,128,.3));border-radius:12px;
|
|
8793
8832
|
background:var(--dsw-alias-bg-base,rgba(255,255,255,.72));
|
|
8794
8833
|
box-shadow:0 1px 2px rgba(20,40,80,.04);white-space:normal}
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
.da-card-head{display:flex;align-items:center;gap:8px;min-width:0;padding:7px 9px;
|
|
8834
|
+
/* Review, source navigation and delivery are separate native buttons. */
|
|
8835
|
+
.da-card button,.da-staged-send{appearance:none;font:inherit;color:inherit;cursor:pointer}
|
|
8836
|
+
.da-card button:focus-visible,.da-staged-send:focus-visible{outline:2px solid var(--da-accent,#176df2);outline-offset:-2px}
|
|
8837
|
+
.da-card button:hover,.da-staged-send:hover{background:var(--dsw-alias-bg-layer-2,rgba(128,128,128,.08));color:var(--da-accent,#176df2)}
|
|
8838
|
+
.da-card-head{width:100%;box-sizing:border-box;border:0;text-align:left;display:flex;align-items:center;gap:8px;min-width:0;padding:7px 9px;
|
|
8800
8839
|
border-bottom:0.5px solid var(--dsw-alias-border-l2,rgba(128,128,128,.2));
|
|
8801
8840
|
background:var(--dsw-alias-bg-layer-1,rgba(128,128,128,.025))}
|
|
8841
|
+
.da-card-head-static{cursor:default}
|
|
8802
8842
|
.da-card-shot{position:relative;display:flex;flex:0 0 auto;align-items:center;justify-content:center;width:34px;height:34px;overflow:hidden;border-radius:7px;
|
|
8803
8843
|
border:0.5px solid var(--dsw-alias-border-l2,rgba(128,128,128,.18));
|
|
8804
8844
|
background:var(--dsw-alias-bg-layer-3,rgba(128,128,128,.1));color:var(--da-accent,#176df2)}
|
|
8805
8845
|
/* The picture covers the glyph; a picture that fails hides itself, and the glyph
|
|
8806
8846
|
underneath it all along is what is left. A miss is never an empty box. */
|
|
8807
|
-
.da-card-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:
|
|
8847
|
+
.da-card-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}
|
|
8808
8848
|
.da-card-shot img[hidden]{display:none}
|
|
8809
8849
|
.da-card-glyph{width:18px;height:18px}
|
|
8810
8850
|
.da-card-text{display:flex;flex:1 1 auto;flex-direction:column;gap:2px;min-width:0}
|
|
@@ -8827,10 +8867,11 @@ button.da-card:focus-visible{outline:2px solid var(--da-accent,#176df2);outline-
|
|
|
8827
8867
|
border-radius:50%;background:var(--dsw-alias-bg-layer-3,rgba(128,128,128,.1));
|
|
8828
8868
|
color:var(--dsw-alias-label-tertiary,inherit)}
|
|
8829
8869
|
.da-card-go svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
|
|
8830
|
-
|
|
8870
|
+
.da-card-head:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242,.14));color:var(--da-accent,#176df2)}
|
|
8831
8871
|
/* Each mark is a compact record, not a line of debug output. The row head answers
|
|
8832
8872
|
"which mark and where"; the element name and selector follow only when the message
|
|
8833
8873
|
carried them; the reader's sentence is the strongest text. */
|
|
8874
|
+
.da-card[data-expanded="true"] .da-card-go{transform:rotate(90deg)}
|
|
8834
8875
|
.da-card-body{display:flex;flex-direction:column;min-width:0;padding:0 9px 2px}
|
|
8835
8876
|
.da-card-row{display:flex;flex-direction:column;gap:2px;min-width:0;padding:6px 0}
|
|
8836
8877
|
.da-card-row+.da-card-row{border-top:0.5px solid var(--dsw-alias-border-l2,rgba(128,128,128,.14))}
|
|
@@ -8932,21 +8973,23 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
8932
8973
|
background:none;font:inherit;font-size:13px;line-height:1;cursor:pointer;opacity:.65}
|
|
8933
8974
|
.da-staged-result-x:hover{opacity:1;background:var(--dsw-alias-bg-layer-2,rgba(255,255,255,.72))}
|
|
8934
8975
|
.da-staged-result-x:focus-visible{outline:2px solid var(--da-accent,#176df2);outline-offset:1px}
|
|
8935
|
-
/*
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8976
|
+
/* Attachments belong to the message. A review may carry several PDF pages,
|
|
8977
|
+
and a message may also carry unrelated files; its source is not that snapshot. */
|
|
8978
|
+
.da-card-actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:4px;padding:3px 5px;
|
|
8979
|
+
border-top:0.5px solid var(--dsw-alias-border-l2,rgba(128,128,128,.2))}
|
|
8980
|
+
.da-card-toggle,.da-card-source{border:0;border-radius:6px;background:none;padding:6px 4px;min-height:30px;font-size:11px!important}
|
|
8981
|
+
.da-card-source{color:var(--da-accent,#176df2)!important}
|
|
8982
|
+
.da-card-error{padding:6px 9px;color:var(--dsw-alias-label-error,#c0392b);font-size:11px}
|
|
8983
|
+
.da-card-locator{margin-left:22px;overflow-wrap:anywhere;white-space:pre-wrap;color:var(--dsw-alias-label-tertiary,inherit);
|
|
8984
|
+
font:10px/1.5 var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace)}
|
|
8985
|
+
.da-card[data-expanded="true"] .da-card-said{display:block;white-space:pre-wrap;-webkit-line-clamp:unset}
|
|
8986
|
+
.da-card[data-expanded="true"] .da-card-name{white-space:normal;overflow-wrap:anywhere}
|
|
8987
|
+
.da-card-target{overflow-wrap:anywhere}
|
|
8988
|
+
.da-staged-type{font-size:9px;font-weight:650;letter-spacing:.04em;color:var(--dsw-alias-label-tertiary,inherit)}
|
|
8989
|
+
.da-staged-details{display:block;width:min(440px,100%);margin-top:4px}
|
|
8990
|
+
.da-staged-details[hidden]{display:none}
|
|
8991
|
+
.da-staged-send{border:0;border-radius:6px;background:none;min-height:28px;padding:4px 8px;font-size:11px;color:var(--da-accent,#176df2)}
|
|
8992
|
+
.da-staged-send[disabled]{opacity:.6;cursor:default}
|
|
8950
8993
|
|
|
8951
8994
|
/* --- local service browser ------------------------------------------------ */
|
|
8952
8995
|
.da-service-shell{display:flex;flex-direction:column;height:100%;min-height:0}
|
|
@@ -8998,7 +9041,7 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
8998
9041
|
.da-services{--da-service-ink:var(--dsw-alias-label-primary,#1b1d20);
|
|
8999
9042
|
--da-service-muted:var(--dsw-alias-label-tertiary,#787d85);
|
|
9000
9043
|
--da-service-line:color-mix(in srgb,var(--dsw-alias-border-l2,#c9cbd0) 66%,transparent);
|
|
9001
|
-
box-sizing:border-box;height:100%;overflow:auto;padding:
|
|
9044
|
+
box-sizing:border-box;height:100%;overflow:auto;padding:24px 20px 32px;width:100%;
|
|
9002
9045
|
color:var(--da-service-ink);
|
|
9003
9046
|
background:
|
|
9004
9047
|
radial-gradient(circle at 18% 0%,color-mix(in srgb,#4c8a63 8%,transparent),transparent 36%),
|
|
@@ -9006,10 +9049,7 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
9006
9049
|
.da-services-head,.da-service-search,.da-services-toolbar,.da-service-list,.da-services-error{
|
|
9007
9050
|
width:min(480px,100%);margin-left:auto;margin-right:auto}
|
|
9008
9051
|
.da-services-head{padding:0 4px 18px}
|
|
9009
|
-
.da-services-
|
|
9010
|
-
font:9px/1.2 var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);
|
|
9011
|
-
letter-spacing:.16em}
|
|
9012
|
-
.da-services-head h1{margin:0;font-size:clamp(25px,4.5vw,34px);font-weight:680;line-height:1.08;letter-spacing:-.035em}
|
|
9052
|
+
.da-services-head h1{margin:0;font-size:20px;font-weight:680;line-height:1.08;letter-spacing:-.035em}
|
|
9013
9053
|
.da-services-head p{max-width:410px;margin:9px 0 0;color:var(--da-service-muted);font-size:12px;line-height:1.65}
|
|
9014
9054
|
.da-service-search{display:flex;align-items:center;gap:9px;box-sizing:border-box;height:48px;padding:0 7px 0 14px;
|
|
9015
9055
|
border:1px solid var(--da-service-line);border-radius:15px;
|
|
@@ -15559,6 +15599,32 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
15559
15599
|
});
|
|
15560
15600
|
}
|
|
15561
15601
|
//#endregion
|
|
15602
|
+
//#region src/client/annotation-subject.ts
|
|
15603
|
+
/** Shared display identity for a review, before and after sending. */
|
|
15604
|
+
/**
|
|
15605
|
+
* Identify the reviewed source, independently of its mark tools or screenshot.
|
|
15606
|
+
* @param data - the immutable message facts.
|
|
15607
|
+
* @returns a readable name, media badge, and whether navigation belongs to the service tab.
|
|
15608
|
+
*/
|
|
15609
|
+
function annotationSubject(data) {
|
|
15610
|
+
const address = data.html ? loopbackAddressOf(data.file) : void 0;
|
|
15611
|
+
if (address !== void 0) {
|
|
15612
|
+
const url = new URL(address);
|
|
15613
|
+
return {
|
|
15614
|
+
name: `${url.host}${url.pathname === "/" ? "" : url.pathname}`,
|
|
15615
|
+
badge: "WEB",
|
|
15616
|
+
live: true
|
|
15617
|
+
};
|
|
15618
|
+
}
|
|
15619
|
+
const suffix = suffixOf(data.file);
|
|
15620
|
+
const badge = data.html ? "HTML" : /^(?:pdf|svg|png|jpe?g|webp|gif|bmp|ico)$/u.test(suffix) ? suffix.toUpperCase() : "FIG";
|
|
15621
|
+
return {
|
|
15622
|
+
name: baseNameOf(data.file),
|
|
15623
|
+
badge,
|
|
15624
|
+
live: false
|
|
15625
|
+
};
|
|
15626
|
+
}
|
|
15627
|
+
//#endregion
|
|
15562
15628
|
//#region src/client/annotation-card.tsx
|
|
15563
15629
|
/**
|
|
15564
15630
|
* The annotation a reader sent, as a card inside their own bubble.
|
|
@@ -15566,8 +15632,8 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
15566
15632
|
* The message still says everything it said — the model reads the same text,
|
|
15567
15633
|
* copying it copies the same text — but in the transcript the same block is easier
|
|
15568
15634
|
* to scan as a small sheet: which figure, how large, how many marks, and what each
|
|
15569
|
-
* of them asked for. Clicking
|
|
15570
|
-
*
|
|
15635
|
+
* of them asked for. Clicking its header expands the delivered review; a separate action opens the
|
|
15636
|
+
* current source in the Sidebar, which may have changed since this message was sent.
|
|
15571
15637
|
*
|
|
15572
15638
|
* Two rules keep this a view and nothing more. It reads the block it was dispatched
|
|
15573
15639
|
* for and never edits it; and it draws from the parsed data alone, so a block whose
|
|
@@ -15693,9 +15759,10 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
15693
15759
|
* @returns the row.
|
|
15694
15760
|
*/
|
|
15695
15761
|
function CardRow(props) {
|
|
15696
|
-
const { mark, ordinal, page } = props;
|
|
15762
|
+
const { mark, ordinal, page, expanded } = props;
|
|
15697
15763
|
const said = mark.comment === "" ? void 0 : mark.comment;
|
|
15698
15764
|
const parts = readCardMarkLabel(mark.label);
|
|
15765
|
+
const number = /\d+$/u.exec(parts.name)?.[0] ?? String(ordinal);
|
|
15699
15766
|
const kind = parts.name.replace(/\s*\d+$/u, "");
|
|
15700
15767
|
const label = parts.element === "" ? kind : parts.element;
|
|
15701
15768
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -15708,7 +15775,7 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
15708
15775
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15709
15776
|
className: "da-card-kind",
|
|
15710
15777
|
"aria-hidden": "true",
|
|
15711
|
-
children:
|
|
15778
|
+
children: number
|
|
15712
15779
|
}),
|
|
15713
15780
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15714
15781
|
className: "da-card-mark",
|
|
@@ -15737,6 +15804,10 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
15737
15804
|
className: "da-card-said",
|
|
15738
15805
|
children: said
|
|
15739
15806
|
}),
|
|
15807
|
+
expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15808
|
+
className: "da-card-locator",
|
|
15809
|
+
children: mark.label
|
|
15810
|
+
}) : null,
|
|
15740
15811
|
parts.aside === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15741
15812
|
className: "da-card-warning",
|
|
15742
15813
|
children: parts.aside
|
|
@@ -15750,7 +15821,6 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
15750
15821
|
* @returns the card, or the block's own text when its data is not one it can draw.
|
|
15751
15822
|
*/
|
|
15752
15823
|
function AnnotationCard(props) {
|
|
15753
|
-
const [shot, setShot] = (0, react.useState)(true);
|
|
15754
15824
|
(0, react.useEffect)(() => {
|
|
15755
15825
|
ensureStyles(document);
|
|
15756
15826
|
}, []);
|
|
@@ -15760,111 +15830,156 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
15760
15830
|
className: "da-card-fallback",
|
|
15761
15831
|
children: raw
|
|
15762
15832
|
});
|
|
15833
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReviewCard, {
|
|
15834
|
+
...props,
|
|
15835
|
+
data,
|
|
15836
|
+
raw
|
|
15837
|
+
}, raw || JSON.stringify(data));
|
|
15838
|
+
}
|
|
15839
|
+
/** The review snapshot; navigation to the current source is a separate action. */
|
|
15840
|
+
function ReviewCard(props) {
|
|
15841
|
+
const { data, raw, embedded = false } = props;
|
|
15842
|
+
const [failedThumb, setFailedThumb] = (0, react.useState)();
|
|
15843
|
+
const [expanded, setExpanded] = (0, react.useState)(embedded);
|
|
15844
|
+
const [trouble, setTrouble] = (0, react.useState)(false);
|
|
15845
|
+
const bodyId = (0, react.useId)();
|
|
15763
15846
|
const interactive = props.interactive !== false;
|
|
15764
15847
|
const open = props.open;
|
|
15765
|
-
const
|
|
15766
|
-
const
|
|
15848
|
+
const candidate = thumbFor(raw);
|
|
15849
|
+
const thumb = candidate === failedThumb ? void 0 : candidate;
|
|
15850
|
+
const shown = embedded || expanded ? data.marks : data.marks.slice(0, MAX_ROWS);
|
|
15767
15851
|
const rest = data.marks.length - shown.length;
|
|
15768
|
-
const
|
|
15769
|
-
const
|
|
15852
|
+
const subject = annotationSubject(data);
|
|
15853
|
+
const liveFile = subject.live;
|
|
15770
15854
|
const captureNotice = liveFile && thumb === void 0 ? data.pageCapture === "omitted" ? resolveTranslate(props, "cardCaptureOmitted") : data.pageCapture === "failed" ? resolveTranslate(props, "cardCaptureFailed") : "" : "";
|
|
15771
|
-
const
|
|
15772
|
-
const cut = Math.max(displayFile.lastIndexOf("/"), displayFile.lastIndexOf("\\"));
|
|
15773
|
-
const name = displayFile.slice(cut + 1);
|
|
15855
|
+
const sourceOpener = interactive && (props.canOpen?.(data.file, data.html) ?? true) ? open : void 0;
|
|
15774
15856
|
const dimensions = /(\d+)\s*[×x]\s*(\d+)/u.exec(data.scope);
|
|
15775
15857
|
const size = dimensions === null ? "" : `${dimensions[1]}×${dimensions[2]}`;
|
|
15776
|
-
const
|
|
15777
|
-
|
|
15778
|
-
|
|
15779
|
-
|
|
15780
|
-
|
|
15781
|
-
|
|
15782
|
-
|
|
15783
|
-
|
|
15784
|
-
|
|
15785
|
-
|
|
15786
|
-
|
|
15858
|
+
const pages = /^(?:共 (\d+) 页|(\d+) pages)(?:,|,|$)/u.exec(data.scope);
|
|
15859
|
+
const scope = pages === null ? size : resolveTranslate(props, "cardPages", { n: Number(pages[1] ?? pages[2]) });
|
|
15860
|
+
const headContent = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
15861
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15862
|
+
className: "da-card-shot",
|
|
15863
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SheetIcon, {}), thumb === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
15864
|
+
alt: "",
|
|
15865
|
+
src: thumb,
|
|
15866
|
+
onError: () => {
|
|
15867
|
+
setFailedThumb(thumb);
|
|
15868
|
+
}
|
|
15869
|
+
})]
|
|
15870
|
+
}),
|
|
15871
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15872
|
+
className: "da-card-text",
|
|
15873
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15874
|
+
className: "da-card-title-row",
|
|
15875
|
+
children: [
|
|
15876
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15877
|
+
className: "da-card-title",
|
|
15878
|
+
title: data.file,
|
|
15879
|
+
children: subject.name
|
|
15880
|
+
}),
|
|
15881
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15882
|
+
className: "da-card-type",
|
|
15883
|
+
children: subject.badge
|
|
15884
|
+
}),
|
|
15885
|
+
captureNotice === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15886
|
+
className: "da-card-capture",
|
|
15887
|
+
children: captureNotice
|
|
15888
|
+
})
|
|
15889
|
+
]
|
|
15890
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15891
|
+
className: "da-card-scope",
|
|
15892
|
+
children: [scope === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15893
|
+
className: "da-card-meta",
|
|
15894
|
+
title: data.scope,
|
|
15895
|
+
children: scope
|
|
15896
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15897
|
+
className: "da-card-tally",
|
|
15898
|
+
"aria-label": resolveTranslate(props, "cardCount", { n: data.marks.length }),
|
|
15899
|
+
title: data.scope,
|
|
15900
|
+
children: data.marks.length
|
|
15787
15901
|
})]
|
|
15902
|
+
})]
|
|
15903
|
+
}),
|
|
15904
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15905
|
+
className: "da-card-go",
|
|
15906
|
+
"aria-hidden": "true",
|
|
15907
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
15908
|
+
viewBox: "0 0 16 16",
|
|
15909
|
+
focusable: "false",
|
|
15910
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m6 3.5 4.5 4.5L6 12.5" })
|
|
15911
|
+
})
|
|
15912
|
+
})
|
|
15913
|
+
] });
|
|
15914
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15915
|
+
className: "da-card",
|
|
15916
|
+
"data-expanded": expanded,
|
|
15917
|
+
children: [
|
|
15918
|
+
embedded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15919
|
+
className: "da-card-head da-card-head-static",
|
|
15920
|
+
children: headContent
|
|
15921
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15922
|
+
type: "button",
|
|
15923
|
+
className: "da-card-head",
|
|
15924
|
+
"aria-expanded": expanded,
|
|
15925
|
+
"aria-controls": bodyId,
|
|
15926
|
+
title: resolveTranslate(props, expanded ? "cardCollapse" : "cardExpand"),
|
|
15927
|
+
onClick: () => {
|
|
15928
|
+
setExpanded((value) => !value);
|
|
15929
|
+
},
|
|
15930
|
+
children: headContent
|
|
15788
15931
|
}),
|
|
15789
15932
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15790
|
-
className: "da-card-
|
|
15791
|
-
|
|
15792
|
-
|
|
15793
|
-
|
|
15794
|
-
|
|
15795
|
-
|
|
15796
|
-
|
|
15797
|
-
})
|
|
15798
|
-
|
|
15799
|
-
|
|
15800
|
-
|
|
15801
|
-
})
|
|
15802
|
-
|
|
15803
|
-
|
|
15804
|
-
|
|
15805
|
-
|
|
15806
|
-
|
|
15807
|
-
|
|
15808
|
-
|
|
15809
|
-
|
|
15810
|
-
|
|
15811
|
-
|
|
15812
|
-
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15933
|
+
className: "da-card-body",
|
|
15934
|
+
id: bodyId,
|
|
15935
|
+
children: [
|
|
15936
|
+
shown.map((mark, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardRow, {
|
|
15937
|
+
mark,
|
|
15938
|
+
ordinal: index + 1,
|
|
15939
|
+
expanded,
|
|
15940
|
+
page: mark.page === void 0 ? void 0 : resolveTranslate(props, "cardPage", { n: mark.page })
|
|
15941
|
+
}, `${String(mark.page ?? "")}-${String(index)}`)),
|
|
15942
|
+
rest > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15943
|
+
className: "da-card-rest",
|
|
15944
|
+
children: resolveTranslate(props, "cardMore", { n: rest })
|
|
15945
|
+
}) : null,
|
|
15946
|
+
data.target === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15947
|
+
className: "da-card-row da-card-target",
|
|
15948
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15949
|
+
className: "da-card-goal",
|
|
15950
|
+
children: resolveTranslate(props, "cardGoal")
|
|
15951
|
+
}), data.target]
|
|
15952
|
+
})
|
|
15953
|
+
]
|
|
15954
|
+
}),
|
|
15955
|
+
embedded ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15956
|
+
className: "da-card-actions",
|
|
15957
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15958
|
+
type: "button",
|
|
15959
|
+
className: "da-card-toggle",
|
|
15960
|
+
"aria-expanded": expanded,
|
|
15961
|
+
"aria-controls": bodyId,
|
|
15962
|
+
onClick: () => {
|
|
15963
|
+
setExpanded((value) => !value);
|
|
15964
|
+
},
|
|
15965
|
+
children: resolveTranslate(props, expanded ? "cardCollapse" : "cardExpand")
|
|
15966
|
+
}), sourceOpener === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
15967
|
+
type: "button",
|
|
15968
|
+
className: "da-card-source",
|
|
15969
|
+
title: data.file,
|
|
15970
|
+
onClick: () => {
|
|
15971
|
+
setTrouble(sourceOpener(data.file, data.html) === false);
|
|
15972
|
+
},
|
|
15973
|
+
children: resolveTranslate(props, liveFile ? "cardOpenLive" : "cardOpen")
|
|
15819
15974
|
})]
|
|
15820
15975
|
}),
|
|
15821
|
-
|
|
15822
|
-
className: "da-card-
|
|
15823
|
-
|
|
15824
|
-
children:
|
|
15825
|
-
viewBox: "0 0 16 16",
|
|
15826
|
-
focusable: "false",
|
|
15827
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m6 3.5 4.5 4.5L6 12.5" })
|
|
15828
|
-
})
|
|
15976
|
+
trouble ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15977
|
+
className: "da-card-error",
|
|
15978
|
+
role: "status",
|
|
15979
|
+
children: resolveTranslate(props, "cardOpenFailed")
|
|
15829
15980
|
}) : null
|
|
15830
15981
|
]
|
|
15831
15982
|
});
|
|
15832
|
-
const body = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15833
|
-
className: "da-card-body",
|
|
15834
|
-
children: [
|
|
15835
|
-
shown.map((mark, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardRow, {
|
|
15836
|
-
mark,
|
|
15837
|
-
ordinal: index + 1,
|
|
15838
|
-
page: mark.page === void 0 ? void 0 : resolveTranslate(props, "cardPage", { n: mark.page })
|
|
15839
|
-
}, `${String(mark.page ?? "")}-${String(index)}`)),
|
|
15840
|
-
rest > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15841
|
-
className: "da-card-row da-card-rest",
|
|
15842
|
-
children: resolveTranslate(props, "cardMore", { n: rest })
|
|
15843
|
-
}) : null,
|
|
15844
|
-
data.target === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15845
|
-
className: "da-card-row da-card-target",
|
|
15846
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15847
|
-
className: "da-card-goal",
|
|
15848
|
-
children: resolveTranslate(props, "cardGoal")
|
|
15849
|
-
}), data.target]
|
|
15850
|
-
})
|
|
15851
|
-
]
|
|
15852
|
-
});
|
|
15853
|
-
const onClick = canOpen && open !== void 0 ? () => {
|
|
15854
|
-
open(data.file, data.html);
|
|
15855
|
-
} : void 0;
|
|
15856
|
-
if (onClick === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15857
|
-
className: "da-card",
|
|
15858
|
-
"data-static": "",
|
|
15859
|
-
children: [head, body]
|
|
15860
|
-
});
|
|
15861
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
15862
|
-
type: "button",
|
|
15863
|
-
className: "da-card",
|
|
15864
|
-
title: `${resolveTranslate(props, liveFile ? "cardOpenLive" : "cardOpen")} — ${displayFile}`,
|
|
15865
|
-
onClick,
|
|
15866
|
-
children: [head, body]
|
|
15867
|
-
});
|
|
15868
15983
|
}
|
|
15869
15984
|
//#endregion
|
|
15870
15985
|
//#region src/client/document-seat.ts
|
|
@@ -15948,7 +16063,8 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
15948
16063
|
paths: saved,
|
|
15949
16064
|
thumb,
|
|
15950
16065
|
file: void 0,
|
|
15951
|
-
|
|
16066
|
+
files: images.slice(0, 8).map(({ page, image }) => new File([image], `${baseNameOf(document.figure.path)}${pageFileSuffix(page)}.annotated.png`, { type: "image/png" })),
|
|
16067
|
+
pageImages: Math.min(images.length, 8),
|
|
15952
16068
|
locale: options.locale,
|
|
15953
16069
|
send: async () => {
|
|
15954
16070
|
if (sessions === void 0) throw new Error("the sessions service is unavailable");
|
|
@@ -16167,31 +16283,9 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
16167
16283
|
//#endregion
|
|
16168
16284
|
//#region src/client/pending-card.tsx
|
|
16169
16285
|
/**
|
|
16170
|
-
*
|
|
16171
|
-
*
|
|
16172
|
-
*
|
|
16173
|
-
* written into it that stays a card, and the whole annotation block sitting in the
|
|
16174
|
-
* input box is exactly what a reader cannot scan. So the block is armed beside the box
|
|
16175
|
-
* — its message held, its picture uploaded — and the chip stands beside the reader's
|
|
16176
|
-
* own attachments as the face of what that send will carry: the figure's thumbnail, how
|
|
16177
|
-
* many marks it carries, the file's name, and the first mark's note.
|
|
16178
|
-
* It is portalled into the composer card's own flow (see `composer-seat`), so it
|
|
16179
|
-
* sits where an attached picture sits; the dock entry that owns it holds only the
|
|
16180
|
-
* anchor, and falls back to drawing in the dock itself when there is no card.
|
|
16181
|
-
*
|
|
16182
|
-
* What the card shows and what it hides is the point. A dimension or a pair of
|
|
16183
|
-
* coordinates is not something to read at a glance, so it stays in the block the model
|
|
16184
|
-
* reads and in the hover title, and never on the face: the face says the figure and the
|
|
16185
|
-
* count first, the note second, and truncates both rather than wrapping into a wall of
|
|
16186
|
-
* text.
|
|
16187
|
-
*
|
|
16188
|
-
* One press is the point, and the box keeps the reader's own words while it is not
|
|
16189
|
-
* pressed: nothing is written into the draft, so the sentence being typed stays the only
|
|
16190
|
-
* text in it. The box's own send carries the armed message — typing above the card and
|
|
16191
|
-
* pressing Send sends the sentence and the review as one message, with the annotation
|
|
16192
|
-
* behind the sentence — and the card's own action runs that same send for a reader who
|
|
16193
|
-
* wants to fire it from the card. The card goes when that send takes it, or when the
|
|
16194
|
-
* reader drops it, because that is when it stops standing for anything.
|
|
16286
|
+
* Reviews staged beside the composer. The chip previews its immutable payload;
|
|
16287
|
+
* the native composer Send carries armed reviews, with or without typed text.
|
|
16288
|
+
* Older hosts without draft admission retain a direct-delivery fallback.
|
|
16195
16289
|
* @module dsh-figmark/client/pending-card
|
|
16196
16290
|
*/
|
|
16197
16291
|
/**
|
|
@@ -16229,28 +16323,6 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
16229
16323
|
});
|
|
16230
16324
|
}
|
|
16231
16325
|
/**
|
|
16232
|
-
* The small outline that stands for a mark's shape on the card's one line.
|
|
16233
|
-
* @returns the glyph.
|
|
16234
|
-
*/
|
|
16235
|
-
function MarkIcon() {
|
|
16236
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
16237
|
-
className: "da-staged-mark-icon",
|
|
16238
|
-
viewBox: "0 0 12 12",
|
|
16239
|
-
"aria-hidden": "true",
|
|
16240
|
-
focusable: "false",
|
|
16241
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
16242
|
-
x: "1",
|
|
16243
|
-
y: "2",
|
|
16244
|
-
width: "10",
|
|
16245
|
-
height: "8",
|
|
16246
|
-
rx: "1.5",
|
|
16247
|
-
fill: "none",
|
|
16248
|
-
stroke: "currentColor",
|
|
16249
|
-
strokeWidth: "1.1"
|
|
16250
|
-
})
|
|
16251
|
-
});
|
|
16252
|
-
}
|
|
16253
|
-
/**
|
|
16254
16326
|
* A mark's name without its coordinates: the kind and its number, which is what a
|
|
16255
16327
|
* reader recognises, with a space between the two the way the product writes it.
|
|
16256
16328
|
* @param label - the mark's own name, coordinates and all.
|
|
@@ -16269,25 +16341,15 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
16269
16341
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
16270
16342
|
const [trouble, setTrouble] = (0, react.useState)("");
|
|
16271
16343
|
const [shot, setShot] = (0, react.useState)(true);
|
|
16344
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
16345
|
+
const detailId = (0, react.useId)();
|
|
16272
16346
|
const sending = (0, react.useRef)(false);
|
|
16273
16347
|
const armed = entry.armed;
|
|
16274
16348
|
const data = entry.data;
|
|
16275
16349
|
const thumb = shot ? thumbFor(entry.raw) : void 0;
|
|
16276
16350
|
const send = async () => {
|
|
16277
16351
|
if (sending.current) return;
|
|
16278
|
-
|
|
16279
|
-
sending.current = true;
|
|
16280
|
-
setBusy(true);
|
|
16281
|
-
try {
|
|
16282
|
-
armed.shell.submit?.();
|
|
16283
|
-
} finally {
|
|
16284
|
-
queueMicrotask(() => {
|
|
16285
|
-
sending.current = false;
|
|
16286
|
-
setBusy(false);
|
|
16287
|
-
});
|
|
16288
|
-
}
|
|
16289
|
-
return;
|
|
16290
|
-
}
|
|
16352
|
+
setTrouble("");
|
|
16291
16353
|
sending.current = true;
|
|
16292
16354
|
setBusy(true);
|
|
16293
16355
|
try {
|
|
@@ -16311,88 +16373,127 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
16311
16373
|
entry.id,
|
|
16312
16374
|
sessionId
|
|
16313
16375
|
]);
|
|
16314
|
-
const
|
|
16315
|
-
const
|
|
16316
|
-
const name = shownFile.slice(cut + 1);
|
|
16376
|
+
const subject = annotationSubject(data);
|
|
16377
|
+
const name = subject.name;
|
|
16317
16378
|
const first = data.marks[0];
|
|
16318
|
-
const captureNotice =
|
|
16379
|
+
const captureNotice = subject.live && thumb === void 0 ? data.pageCapture === "omitted" ? resolveTranslate(props, "cardCaptureOmitted") : data.pageCapture === "failed" ? resolveTranslate(props, "cardCaptureFailed") : "" : "";
|
|
16319
16380
|
const detail = first === void 0 ? name : first.comment === "" ? first.label : `${first.label} · ${first.comment}`;
|
|
16320
16381
|
const hover = data.scope === "" ? data.file : `${data.file} · ${data.scope}`;
|
|
16321
16382
|
const removeLabel = resolveTranslate(props, "pendingRemove");
|
|
16383
|
+
const hint = resolveTranslate(props, "pendingSendNow");
|
|
16322
16384
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16323
16385
|
className: "da-staged",
|
|
16324
|
-
children: [
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16329
|
-
|
|
16330
|
-
|
|
16331
|
-
|
|
16332
|
-
|
|
16333
|
-
|
|
16334
|
-
|
|
16335
|
-
|
|
16336
|
-
|
|
16337
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FigureIcon, {}), thumb === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
16338
|
-
alt: "",
|
|
16339
|
-
src: thumb,
|
|
16340
|
-
onError: () => {
|
|
16341
|
-
setShot(false);
|
|
16342
|
-
}
|
|
16343
|
-
})]
|
|
16344
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16345
|
-
className: "da-staged-text",
|
|
16386
|
+
children: [
|
|
16387
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16388
|
+
className: "da-staged-card",
|
|
16389
|
+
title: hover,
|
|
16390
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
16391
|
+
type: "button",
|
|
16392
|
+
className: "da-staged-open",
|
|
16393
|
+
"aria-expanded": expanded,
|
|
16394
|
+
"aria-controls": detailId,
|
|
16395
|
+
title: resolveTranslate(props, expanded ? "cardCollapse" : "cardExpand"),
|
|
16396
|
+
onClick: () => {
|
|
16397
|
+
setExpanded((value) => !value);
|
|
16398
|
+
},
|
|
16346
16399
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16347
|
-
className: "da-staged-
|
|
16400
|
+
className: "da-staged-shot",
|
|
16401
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FigureIcon, {}), thumb === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
16402
|
+
alt: "",
|
|
16403
|
+
src: thumb,
|
|
16404
|
+
onError: () => {
|
|
16405
|
+
setShot(false);
|
|
16406
|
+
}
|
|
16407
|
+
})]
|
|
16408
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16409
|
+
className: "da-staged-text",
|
|
16348
16410
|
children: [
|
|
16349
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
16350
|
-
className: "da-staged-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16411
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16412
|
+
className: "da-staged-head",
|
|
16413
|
+
children: [
|
|
16414
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16415
|
+
className: "da-staged-title",
|
|
16416
|
+
title: name,
|
|
16417
|
+
children: name
|
|
16418
|
+
}),
|
|
16419
|
+
captureNotice === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16420
|
+
className: "da-staged-capture",
|
|
16421
|
+
children: captureNotice
|
|
16422
|
+
}),
|
|
16423
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16424
|
+
className: "da-staged-count",
|
|
16425
|
+
"aria-label": resolveTranslate(props, "cardCount", { n: data.marks.length }),
|
|
16426
|
+
children: resolveTranslate(props, "pendingCount", { n: data.marks.length })
|
|
16427
|
+
})
|
|
16428
|
+
]
|
|
16357
16429
|
}),
|
|
16358
16430
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16359
|
-
className: "da-staged-
|
|
16360
|
-
|
|
16361
|
-
children: resolveTranslate(props, "pendingCount", { n: data.marks.length })
|
|
16362
|
-
})
|
|
16363
|
-
]
|
|
16364
|
-
}), first === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16365
|
-
className: "da-staged-line",
|
|
16366
|
-
title: detail,
|
|
16367
|
-
children: [
|
|
16368
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkIcon, {}),
|
|
16369
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16370
|
-
className: "da-staged-shape",
|
|
16371
|
-
children: shortLabel(first.label)
|
|
16431
|
+
className: "da-staged-type",
|
|
16432
|
+
children: subject.badge
|
|
16372
16433
|
}),
|
|
16373
|
-
first
|
|
16374
|
-
className: "da-staged-
|
|
16375
|
-
|
|
16434
|
+
first === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
16435
|
+
className: "da-staged-line",
|
|
16436
|
+
title: detail,
|
|
16437
|
+
children: [
|
|
16438
|
+
first.page === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16439
|
+
className: "da-card-page",
|
|
16440
|
+
children: resolveTranslate(props, "cardPage", { n: first.page })
|
|
16441
|
+
}),
|
|
16442
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16443
|
+
className: "da-staged-shape",
|
|
16444
|
+
children: shortLabel(first.label)
|
|
16445
|
+
}),
|
|
16446
|
+
first.comment === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16447
|
+
className: "da-staged-said",
|
|
16448
|
+
children: first.comment
|
|
16449
|
+
})
|
|
16450
|
+
]
|
|
16376
16451
|
})
|
|
16377
16452
|
]
|
|
16378
16453
|
})]
|
|
16454
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16455
|
+
type: "button",
|
|
16456
|
+
className: "da-staged-x",
|
|
16457
|
+
"aria-label": removeLabel,
|
|
16458
|
+
title: removeLabel,
|
|
16459
|
+
disabled: busy,
|
|
16460
|
+
onClick: () => {
|
|
16461
|
+
if (sending.current) return;
|
|
16462
|
+
if (armed !== void 0) disarmReview(armed, entry.conversation);
|
|
16463
|
+
unparkAnnotation(sessionId, entry.id);
|
|
16464
|
+
},
|
|
16465
|
+
children: "×"
|
|
16379
16466
|
})]
|
|
16380
|
-
}),
|
|
16467
|
+
}),
|
|
16468
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16469
|
+
id: detailId,
|
|
16470
|
+
className: "da-staged-details",
|
|
16471
|
+
hidden: !expanded,
|
|
16472
|
+
children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnnotationCard, {
|
|
16473
|
+
node: { data },
|
|
16474
|
+
raw: entry.raw,
|
|
16475
|
+
interactive: false,
|
|
16476
|
+
embedded: true,
|
|
16477
|
+
t: props.t,
|
|
16478
|
+
localeId: props.localeId
|
|
16479
|
+
}) : null
|
|
16480
|
+
}),
|
|
16481
|
+
armed === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16381
16482
|
type: "button",
|
|
16382
|
-
className: "da-staged-
|
|
16383
|
-
"aria-label": removeLabel,
|
|
16384
|
-
title: removeLabel,
|
|
16483
|
+
className: "da-staged-send",
|
|
16385
16484
|
disabled: busy,
|
|
16485
|
+
title: hint,
|
|
16386
16486
|
onClick: () => {
|
|
16387
|
-
|
|
16388
|
-
unparkAnnotation(sessionId, entry.id);
|
|
16487
|
+
send();
|
|
16389
16488
|
},
|
|
16390
|
-
children: "
|
|
16391
|
-
})
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
|
-
|
|
16489
|
+
children: resolveTranslate(props, busy ? "pendingSending" : "pendingSendNow")
|
|
16490
|
+
}) : null,
|
|
16491
|
+
trouble === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16492
|
+
className: "da-staged-trouble",
|
|
16493
|
+
role: "alert",
|
|
16494
|
+
children: trouble
|
|
16495
|
+
})
|
|
16496
|
+
]
|
|
16396
16497
|
});
|
|
16397
16498
|
}
|
|
16398
16499
|
/**
|
|
@@ -16427,6 +16528,11 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
16427
16528
|
return mountSeat(node, setSeat);
|
|
16428
16529
|
}, [sessionId, showing]);
|
|
16429
16530
|
if (!showing) return null;
|
|
16531
|
+
const owned = entries.flatMap((entry) => entry.armed?.previewUrls ?? []).map((url) => `img[src=${JSON.stringify(url)}]`).join(",");
|
|
16532
|
+
const thumbnailStyle = owned === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: `
|
|
16533
|
+
[data-composer-card] [role="group"] > div:has(${owned}){display:none}
|
|
16534
|
+
[data-composer-card] > div:has(> div > [role="group"]):not(:has([role="group"] > div:not(:has(${owned})))){display:none}
|
|
16535
|
+
` });
|
|
16430
16536
|
const cards = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ParkedChip, {
|
|
16431
16537
|
entry,
|
|
16432
16538
|
sessionId,
|
|
@@ -16453,11 +16559,15 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
16453
16559
|
children: "×"
|
|
16454
16560
|
})]
|
|
16455
16561
|
});
|
|
16456
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
16457
|
-
|
|
16458
|
-
|
|
16459
|
-
|
|
16460
|
-
|
|
16562
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
16563
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16564
|
+
className: "da-staged-anchor",
|
|
16565
|
+
ref: anchor,
|
|
16566
|
+
hidden: true
|
|
16567
|
+
}),
|
|
16568
|
+
thumbnailStyle,
|
|
16569
|
+
seat === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [cards, report] }) : (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [cards, report] }), seat)
|
|
16570
|
+
] });
|
|
16461
16571
|
}
|
|
16462
16572
|
//#endregion
|
|
16463
16573
|
//#region src/client/live-drafts.ts
|
|
@@ -17041,13 +17151,13 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
17041
17151
|
return;
|
|
17042
17152
|
}
|
|
17043
17153
|
const needle = query.trim().toLowerCase();
|
|
17044
|
-
|
|
17045
|
-
|
|
17046
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17154
|
+
if (needle === "") return;
|
|
17155
|
+
const titleMatch = visible.some((service) => service.title?.toLowerCase().includes(needle) === true);
|
|
17156
|
+
if (looksLikeAddress(query) && !titleMatch) openWorkspace(query);
|
|
17157
|
+
else if (visible.length === 1) {
|
|
17158
|
+
const only = visible[0];
|
|
17159
|
+
openWorkspace(only.url, only.title);
|
|
17160
|
+
} else setError(t(visible.length === 0 ? "serviceNoMatch" : "serviceChooseMatch"));
|
|
17051
17161
|
};
|
|
17052
17162
|
const visible = (0, react.useMemo)(() => {
|
|
17053
17163
|
const needle = query.trim().toLowerCase();
|
|
@@ -17146,14 +17256,7 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
17146
17256
|
children: [
|
|
17147
17257
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
17148
17258
|
className: "da-services-head",
|
|
17149
|
-
children: [
|
|
17150
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
17151
|
-
className: "da-services-kicker",
|
|
17152
|
-
children: t("serviceKicker")
|
|
17153
|
-
}),
|
|
17154
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", { children: t("serviceTitle") }),
|
|
17155
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("serviceHint") })
|
|
17156
|
-
]
|
|
17259
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", { children: t("serviceTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("serviceHint") })]
|
|
17157
17260
|
}),
|
|
17158
17261
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
17159
17262
|
className: "da-service-search",
|
|
@@ -17211,7 +17314,7 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
17211
17314
|
children: t("serviceScanning")
|
|
17212
17315
|
}) : visible.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
17213
17316
|
className: "da-services-empty",
|
|
17214
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("serviceEmpty") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("serviceEmptyHint") })]
|
|
17317
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t(query.trim() === "" ? "serviceEmpty" : "serviceNoMatch") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(query.trim() === "" ? "serviceEmptyHint" : "serviceSearchHint") })]
|
|
17215
17318
|
}) : visible.map((service) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
17216
17319
|
type: "button",
|
|
17217
17320
|
className: "da-service-row",
|
|
@@ -18445,6 +18548,7 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
18445
18548
|
*/
|
|
18446
18549
|
function canOpenFigure(ctx, sessionId, path, html) {
|
|
18447
18550
|
if (sessionId === "" || path === "") return false;
|
|
18551
|
+
if (/^[a-z][a-z\d+.-]*:\/\//iu.test(path) && !isLivePage(path, html)) return false;
|
|
18448
18552
|
const sidebar = ctx.get("sidebarRight");
|
|
18449
18553
|
if (sidebar === void 0) return false;
|
|
18450
18554
|
return !isLivePage(path, html) || sidebar.openTab !== void 0;
|
|
@@ -18521,6 +18625,7 @@ button.da-card:hover .da-card-go{background:var(--da-accent-soft,rgba(23,109,242
|
|
|
18521
18625
|
ctx.effect(() => sidebarTabs.register({
|
|
18522
18626
|
id: SERVICE_BODY_ID,
|
|
18523
18627
|
kind: SERVICE_KIND,
|
|
18628
|
+
keepMounted: true,
|
|
18524
18629
|
priority: "extension",
|
|
18525
18630
|
title: () => locale.bind(NAMESPACE)("serviceTitle"),
|
|
18526
18631
|
guide: [{
|
package/lib/index.js
CHANGED
|
@@ -885,9 +885,14 @@ ${readFileSync(createRequire(import.meta.url).resolve("modern-screenshot/dist/in
|
|
|
885
885
|
? [rect.left + scroll[0], rect.top + scroll[1], width, height]
|
|
886
886
|
: [0, 0, width, height]
|
|
887
887
|
var target = surface || root
|
|
888
|
+
// The deck's ancestor may scale a 1920px slide into a 548px viewport. Layout
|
|
889
|
+
// at the original size, then map the bitmap into imageRect. Feeding rect.width
|
|
890
|
+
// back as CSS width reflows/clips the slide while its children stay full-size.
|
|
891
|
+
var layoutWidth = surface ? surface.offsetWidth || width : width
|
|
892
|
+
var layoutHeight = surface ? surface.offsetHeight || height : height
|
|
888
893
|
// The parent composes at no more than 1.5x, so a larger source only costs a DOM
|
|
889
894
|
// clone and PNG encode without adding pixels the delivered picture keeps.
|
|
890
|
-
var scale = Math.min(1.5, 1800 / Math.max(
|
|
895
|
+
var scale = Math.min(1.5, 1800 / Math.max(layoutWidth, layoutHeight))
|
|
891
896
|
var screenshot = window.modernScreenshot
|
|
892
897
|
if (!screenshot || typeof screenshot.domToDataUrl !== 'function') {
|
|
893
898
|
send({
|
|
@@ -900,8 +905,9 @@ ${readFileSync(createRequire(import.meta.url).resolve("modern-screenshot/dist/in
|
|
|
900
905
|
return
|
|
901
906
|
}
|
|
902
907
|
screenshot.domToDataUrl(target, {
|
|
903
|
-
width:
|
|
904
|
-
height:
|
|
908
|
+
width: layoutWidth,
|
|
909
|
+
height: layoutHeight,
|
|
910
|
+
style: surface ? { transform: 'none', zoom: '1' } : null,
|
|
905
911
|
scale: scale,
|
|
906
912
|
backgroundColor: '#ffffff',
|
|
907
913
|
maximumCanvasSize: 4096,
|
|
@@ -1555,7 +1561,7 @@ function probe(candidate) {
|
|
|
1555
1561
|
const finishResponse = () => {
|
|
1556
1562
|
/* v8 ignore next -- Node populates statusCode before emitting `response`. */
|
|
1557
1563
|
const status = res.statusCode ?? 0;
|
|
1558
|
-
if (status < 200 || status >=
|
|
1564
|
+
if (status < 200 || status >= 400) {
|
|
1559
1565
|
finish(void 0);
|
|
1560
1566
|
return;
|
|
1561
1567
|
}
|
|
@@ -1630,7 +1636,16 @@ async function discoverServices(request, sessionCwd) {
|
|
|
1630
1636
|
workspace: 1,
|
|
1631
1637
|
common: 2
|
|
1632
1638
|
};
|
|
1633
|
-
|
|
1639
|
+
const sorted = services.sort((left, right) => rank[left.source] - rank[right.source] || new URL(left.url).port.localeCompare(new URL(right.url).port, void 0, { numeric: true }));
|
|
1640
|
+
const pages = /* @__PURE__ */ new Set();
|
|
1641
|
+
return sorted.filter((service) => {
|
|
1642
|
+
const url = new URL(service.url);
|
|
1643
|
+
const path = service.title === void 0 ? url.pathname : url.pathname.replace(/\/index\.html?$/iu, "/");
|
|
1644
|
+
const key = `${url.origin}${path}${url.search}${url.hash}\n${service.title ?? ""}`;
|
|
1645
|
+
if (pages.has(key)) return false;
|
|
1646
|
+
pages.add(key);
|
|
1647
|
+
return true;
|
|
1648
|
+
});
|
|
1634
1649
|
}
|
|
1635
1650
|
//#endregion
|
|
1636
1651
|
//#region src/host/store-schema.ts
|
package/market/index.json
CHANGED