dsh-figmark 1.1.2 → 1.1.3
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/lib/client.js +422 -205
- package/lib/index.js +3 -3
- package/market/index.json +2 -2
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -268,7 +268,7 @@ window.__ModuleLoader__.load({
|
|
|
268
268
|
* @returns the value, pulled to the nearest end of the range.
|
|
269
269
|
*/
|
|
270
270
|
function clampWeight(value) {
|
|
271
|
-
return Math.min(
|
|
271
|
+
return Math.min(8, Math.max(WEIGHT_MIN, value));
|
|
272
272
|
}
|
|
273
273
|
/**
|
|
274
274
|
* Whether a value is a stroke weight this plugin would write.
|
|
@@ -277,7 +277,7 @@ window.__ModuleLoader__.load({
|
|
|
277
277
|
* @returns true when it is one.
|
|
278
278
|
*/
|
|
279
279
|
function isWeight(value) {
|
|
280
|
-
return typeof value === "number" && Number.isFinite(value) && value >= .5 && value <=
|
|
280
|
+
return typeof value === "number" && Number.isFinite(value) && value >= .5 && value <= 8;
|
|
281
281
|
}
|
|
282
282
|
/**
|
|
283
283
|
* Suffix naming one page's files inside a paged document's name.
|
|
@@ -326,6 +326,86 @@ window.__ModuleLoader__.load({
|
|
|
326
326
|
function round(value) {
|
|
327
327
|
return Math.round(value);
|
|
328
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Write a freehand stroke's extent as the box it was drawn in.
|
|
331
|
+
*
|
|
332
|
+
* The stroke itself is dozens of coordinates, most of which say nothing an agent
|
|
333
|
+
* can act on; the box says where on the figure to look, which is what the reading
|
|
334
|
+
* of the mark needs. `strokeExtents` names the area, and the phrase above it names
|
|
335
|
+
* the mark, so the pair still reads as one line.
|
|
336
|
+
* @param points - the stroke's sampled points.
|
|
337
|
+
* @returns the box as ` (x,y)-(x,y)`, or nothing for an empty stroke.
|
|
338
|
+
*/
|
|
339
|
+
function strokeExtents(points) {
|
|
340
|
+
let left = Number.POSITIVE_INFINITY;
|
|
341
|
+
let top = Number.POSITIVE_INFINITY;
|
|
342
|
+
let right = Number.NEGATIVE_INFINITY;
|
|
343
|
+
let bottom = Number.NEGATIVE_INFINITY;
|
|
344
|
+
for (const [x, y] of points) {
|
|
345
|
+
left = Math.min(left, x);
|
|
346
|
+
top = Math.min(top, y);
|
|
347
|
+
right = Math.max(right, x);
|
|
348
|
+
bottom = Math.max(bottom, y);
|
|
349
|
+
}
|
|
350
|
+
if (!Number.isFinite(left)) return "";
|
|
351
|
+
return ` (${round(left)},${round(top)})-(${round(right)},${round(bottom)})`;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Write two dragged corners as the rectangle they make.
|
|
355
|
+
*
|
|
356
|
+
* A box dragged up and to the left arrives as (bottom-right, top-left), and the
|
|
357
|
+
* agent reading `(187,138)-(42,88)` has to take a min and a max before it knows
|
|
358
|
+
* which area is meant. Both corners are therefore written left-to-right and
|
|
359
|
+
* top-to-bottom, so the same box always reads the same way however it was drawn.
|
|
360
|
+
* @param first - where the drag started.
|
|
361
|
+
* @param second - where it ended.
|
|
362
|
+
* @returns the corner pair as `(x,y)-(x,y)`, or nothing when the mark is unplaced.
|
|
363
|
+
*/
|
|
364
|
+
function extents(first, second) {
|
|
365
|
+
if (first === void 0 || second === void 0) return "";
|
|
366
|
+
return ` (${round(Math.min(first[0], second[0]))},${round(Math.min(first[1], second[1]))})-(${round(Math.max(first[0], second[0]))},${round(Math.max(first[1], second[1]))})`;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Say how far a mark reaches outside the figure, when it does.
|
|
370
|
+
*
|
|
371
|
+
* A negative coordinate is an accurate description — the origin is the figure's
|
|
372
|
+
* top-left corner, so -57 is 57 pixels above it — but a reader has to notice the
|
|
373
|
+
* sign and then reason about what it implies. Naming the overhang outright costs
|
|
374
|
+
* about a dozen characters and removes that step: the agent is told the mark is
|
|
375
|
+
* off the picture and by how much, rather than being left to infer it. A mark that
|
|
376
|
+
* sits inside the figure says nothing at all, which is the common case.
|
|
377
|
+
* @param mark - the mark to measure.
|
|
378
|
+
* @param figure - the figure's size, when the document knows it.
|
|
379
|
+
* @param locale - language of the surrounding message.
|
|
380
|
+
* @returns the sentence fragment to append, or nothing when the mark is inside.
|
|
381
|
+
*/
|
|
382
|
+
function overhang(mark, figure, locale) {
|
|
383
|
+
const { width, height } = figure;
|
|
384
|
+
if (width === void 0 || height === void 0) return "";
|
|
385
|
+
let left = Number.POSITIVE_INFINITY;
|
|
386
|
+
let top = Number.POSITIVE_INFINITY;
|
|
387
|
+
let right = Number.NEGATIVE_INFINITY;
|
|
388
|
+
let bottom = Number.NEGATIVE_INFINITY;
|
|
389
|
+
for (const [x, y] of mark.points) {
|
|
390
|
+
left = Math.min(left, x);
|
|
391
|
+
top = Math.min(top, y);
|
|
392
|
+
right = Math.max(right, x);
|
|
393
|
+
bottom = Math.max(bottom, y);
|
|
394
|
+
}
|
|
395
|
+
const escape = (amount, zhWord, enWord) => {
|
|
396
|
+
const pixels = round(amount);
|
|
397
|
+
if (pixels <= 0) return "";
|
|
398
|
+
return locale === "zh" ? `${zhWord} ${pixels}` : `${enWord} ${pixels}`;
|
|
399
|
+
};
|
|
400
|
+
const escapes = [
|
|
401
|
+
escape(-left, "左", "left"),
|
|
402
|
+
escape(-top, "上", "up"),
|
|
403
|
+
escape(right - width, "右", "right"),
|
|
404
|
+
escape(bottom - height, "下", "down")
|
|
405
|
+
].filter((part) => part !== "");
|
|
406
|
+
if (escapes.length === 0) return "";
|
|
407
|
+
return locale === "zh" ? `(超出图面:${escapes.join("、")} 像素)` : ` (off the figure: ${escapes.join(", ")} px)`;
|
|
408
|
+
}
|
|
329
409
|
/** Describe one mark's shape in words, for the model-facing summary. */
|
|
330
410
|
function describeShape(mark, locale) {
|
|
331
411
|
const [first, second] = mark.points;
|
|
@@ -333,9 +413,12 @@ window.__ModuleLoader__.load({
|
|
|
333
413
|
const turned = mark.rotation === void 0 || mark.rotation === 0 ? "" : zh ? `(旋转 ${Math.round(mark.rotation)}°)` : ` (rotated ${Math.round(mark.rotation)}°)`;
|
|
334
414
|
switch (mark.kind) {
|
|
335
415
|
case "arrow": return first !== void 0 && second !== void 0 ? zh ? `箭头 (${round(first[0])},${round(first[1])}) → (${round(second[0])},${round(second[1])})${turned}` : `arrow (${round(first[0])},${round(first[1])}) → (${round(second[0])},${round(second[1])})${turned}` : zh ? "箭头" : "arrow";
|
|
336
|
-
case "rect": return
|
|
337
|
-
case "ellipse": return
|
|
338
|
-
case "pen":
|
|
416
|
+
case "rect": return (zh ? "框选" : "box") + extents(first, second) + turned;
|
|
417
|
+
case "ellipse": return (zh ? "圈选" : "ellipse") + extents(first, second) + turned;
|
|
418
|
+
case "pen": {
|
|
419
|
+
const box = strokeExtents(mark.points);
|
|
420
|
+
return `${zh ? "手绘线" : "freehand stroke"}${box}${turned}`;
|
|
421
|
+
}
|
|
339
422
|
case "point": return first !== void 0 ? zh ? `单点位于 (${round(first[0])},${round(first[1])})` : `at (${round(first[0])},${round(first[1])})` : zh ? "单点" : "a point";
|
|
340
423
|
}
|
|
341
424
|
}
|
|
@@ -350,7 +433,7 @@ window.__ModuleLoader__.load({
|
|
|
350
433
|
if (anchor.text !== void 0 && anchor.text !== "") parts.push(zh ? `元素文字「${anchor.text}」` : `element text "${anchor.text}"`);
|
|
351
434
|
if (anchor.selector !== void 0 && anchor.selector !== "") parts.push(zh ? `选择器 ${anchor.selector}` : `selector ${anchor.selector}`);
|
|
352
435
|
if (parts.length === 0) parts.push(`<${anchor.tag}>`);
|
|
353
|
-
return `(${parts.join(",")}
|
|
436
|
+
return zh ? `(${parts.join(",")})` : ` (${parts.join(", ")})`;
|
|
354
437
|
}
|
|
355
438
|
/**
|
|
356
439
|
* Render one mark as a numbered line the model reads.
|
|
@@ -362,9 +445,11 @@ window.__ModuleLoader__.load({
|
|
|
362
445
|
* @param mark - the mark to describe.
|
|
363
446
|
* @param index - zero-based position in the document, rendered as a circled number.
|
|
364
447
|
* @param locale - language of the surrounding message.
|
|
448
|
+
* @param figure - the figure these marks belong to, when its size is known; a mark
|
|
449
|
+
* that reaches past it then says so rather than leaving the sign to be noticed.
|
|
365
450
|
* @returns one line of the annotation summary.
|
|
366
451
|
*/
|
|
367
|
-
function describeMark(mark, index, locale) {
|
|
452
|
+
function describeMark(mark, index, locale, figure) {
|
|
368
453
|
const zh = locale === "zh";
|
|
369
454
|
const marker = [
|
|
370
455
|
"①",
|
|
@@ -380,8 +465,9 @@ window.__ModuleLoader__.load({
|
|
|
380
465
|
][index] ?? `${index + 1}.`;
|
|
381
466
|
const page = mark.page === void 0 ? "" : zh ? `第 ${mark.page} 页 ` : `page ${mark.page} `;
|
|
382
467
|
const detail = mark.detail !== void 0 && mark.detail.trim() !== "" ? mark.detail.trim() : "";
|
|
383
|
-
const
|
|
384
|
-
|
|
468
|
+
const escape = figure === void 0 ? "" : overhang(mark, figure, locale);
|
|
469
|
+
const parts = [`${marker} ${page}${describeShape(mark, locale)}${describeAnchor(mark, locale)}${escape}`];
|
|
470
|
+
if (detail !== "") parts.push(zh ? `批注:${detail}` : `note: ${detail}`);
|
|
385
471
|
if (detail === "") parts.push(zh ? "(未写说明)" : "(no detail)");
|
|
386
472
|
return parts.join(zh ? "," : ", ");
|
|
387
473
|
}
|
|
@@ -392,7 +478,7 @@ window.__ModuleLoader__.load({
|
|
|
392
478
|
* @returns one line per mark.
|
|
393
479
|
*/
|
|
394
480
|
function describeMarks(document, locale) {
|
|
395
|
-
return document.marks.map((mark, index) => describeMark(mark, index, locale));
|
|
481
|
+
return document.marks.map((mark, index) => describeMark(mark, index, locale, document.figure));
|
|
396
482
|
}
|
|
397
483
|
//#endregion
|
|
398
484
|
//#region src/shared/figure-kind.ts
|
|
@@ -867,7 +953,7 @@ window.__ModuleLoader__.load({
|
|
|
867
953
|
* @param rotation - the mark's rotation in degrees.
|
|
868
954
|
* @returns the handles.
|
|
869
955
|
*/
|
|
870
|
-
function handlesFor(box, rotation) {
|
|
956
|
+
function handlesFor(box, rotation, size = 7) {
|
|
871
957
|
const centre = boxCentre(box);
|
|
872
958
|
const radians = rotation * Math.PI / 180;
|
|
873
959
|
const at = (x, y) => rotateAround([x, y], centre, radians);
|
|
@@ -884,11 +970,23 @@ window.__ModuleLoader__.load({
|
|
|
884
970
|
});
|
|
885
971
|
placed.push({
|
|
886
972
|
id: "rotate",
|
|
887
|
-
point: at(centre[0], box.y -
|
|
973
|
+
point: at(centre[0], box.y - armFor(size))
|
|
888
974
|
});
|
|
889
975
|
return placed;
|
|
890
976
|
}
|
|
891
977
|
/**
|
|
978
|
+
* How far the turn handle stands off the top edge, for a handle of a given size.
|
|
979
|
+
*
|
|
980
|
+
* Proportional to the handle: a bigger grip on a short arm would have the circle
|
|
981
|
+
* overlapping the corner squares, and a smaller one on a long arm would look
|
|
982
|
+
* detached from the frame it turns.
|
|
983
|
+
* @param size - the handle's side, in figure pixels.
|
|
984
|
+
* @returns the distance from the top edge, in figure pixels.
|
|
985
|
+
*/
|
|
986
|
+
function armFor(size) {
|
|
987
|
+
return size * (22 / 7);
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
892
990
|
* The line that ties the rotation handle to the box it turns.
|
|
893
991
|
*
|
|
894
992
|
* It reads as attached to the mark only because it is anchored on the top edge
|
|
@@ -897,11 +995,11 @@ window.__ModuleLoader__.load({
|
|
|
897
995
|
* @param rotation - the mark's rotation in degrees.
|
|
898
996
|
* @returns the two ends of the tether, from the turn handle to the top edge.
|
|
899
997
|
*/
|
|
900
|
-
function tetherFor(box, rotation) {
|
|
998
|
+
function tetherFor(box, rotation, size = 7) {
|
|
901
999
|
const centre = boxCentre(box);
|
|
902
1000
|
const radians = rotation * Math.PI / 180;
|
|
903
1001
|
return {
|
|
904
|
-
from: rotateAround([centre[0], box.y -
|
|
1002
|
+
from: rotateAround([centre[0], box.y - armFor(size)], centre, radians),
|
|
905
1003
|
to: rotateAround([centre[0], box.y], centre, radians)
|
|
906
1004
|
};
|
|
907
1005
|
}
|
|
@@ -913,10 +1011,10 @@ window.__ModuleLoader__.load({
|
|
|
913
1011
|
* @param reach - how far from a handle's centre still counts, in figure pixels.
|
|
914
1012
|
* @returns the handle grabbed, or undefined for a press on the mark itself.
|
|
915
1013
|
*/
|
|
916
|
-
function handleAt(box, rotation, at, reach = 7) {
|
|
1014
|
+
function handleAt(box, rotation, at, reach = 7, size = 7) {
|
|
917
1015
|
let best;
|
|
918
1016
|
let bestDistance = reach;
|
|
919
|
-
for (const handle of handlesFor(box, rotation)) {
|
|
1017
|
+
for (const handle of handlesFor(box, rotation, size)) {
|
|
920
1018
|
const distance = Math.hypot(handle.point[0] - at[0], handle.point[1] - at[1]);
|
|
921
1019
|
if (distance <= bestDistance) {
|
|
922
1020
|
best = handle.id;
|
|
@@ -993,7 +1091,6 @@ window.__ModuleLoader__.load({
|
|
|
993
1091
|
const degrees = normalizeDegrees((Math.atan2(at[1] - centre[1], at[0] - centre[0]) + Math.PI / 2) * 180 / Math.PI);
|
|
994
1092
|
return snap ? normalizeDegrees(Math.round(degrees / 15) * 15) : degrees;
|
|
995
1093
|
}
|
|
996
|
-
/** The cursor for one handle: what dragging it will do. */
|
|
997
1094
|
const CURSORS = {
|
|
998
1095
|
nw: "nwse-resize",
|
|
999
1096
|
n: "ns-resize",
|
|
@@ -1003,7 +1100,7 @@ window.__ModuleLoader__.load({
|
|
|
1003
1100
|
s: "ns-resize",
|
|
1004
1101
|
sw: "nesw-resize",
|
|
1005
1102
|
w: "ew-resize",
|
|
1006
|
-
rotate: "
|
|
1103
|
+
rotate: `url("data:image/svg+xml,${encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><g fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M17.5 7.5A7 7 0 1 1 8.6 6.1\" stroke=\"#ffffff\" stroke-width=\"4.4\"/><path d=\"M17.5 7.5A7 7 0 1 1 8.6 6.1\" stroke=\"#2d2d2d\" stroke-width=\"2\"/><path d=\"M15.2 3.1L18.6 8.2L12.9 8.9\" stroke=\"#ffffff\" stroke-width=\"4.4\"/><path d=\"M15.2 3.1L18.6 8.2L12.9 8.9\" stroke=\"#2d2d2d\" stroke-width=\"2\"/></g></svg>")}") 12 12, crosshair`
|
|
1007
1104
|
};
|
|
1008
1105
|
/**
|
|
1009
1106
|
* The mouse cursor for one handle.
|
|
@@ -1358,27 +1455,15 @@ window.__ModuleLoader__.load({
|
|
|
1358
1455
|
const cy = box.y + box.height / 2;
|
|
1359
1456
|
return `rotate(${n(radians * 180 / Math.PI)} ${n(cx)} ${n(cy)})`;
|
|
1360
1457
|
}
|
|
1361
|
-
//#endregion
|
|
1362
|
-
//#region src/client/scale.ts
|
|
1363
1458
|
/**
|
|
1364
|
-
*
|
|
1365
|
-
*
|
|
1366
|
-
* A stroke is not a physical thing: 2.5 pixels is a bold line on a 300px thumbnail
|
|
1367
|
-
* and a hairline on a 4000px scan. Marks are comparisons the reader makes against
|
|
1368
|
-
* the line work underneath them, so they have to be sized in the same currency as
|
|
1369
|
-
* the figure — a fraction of it — rather than in the figure's pixels.
|
|
1459
|
+
* Smallest multiple of the base sizes, for figures far below the reference.
|
|
1370
1460
|
*
|
|
1371
|
-
*
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1374
|
-
*
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
*/
|
|
1378
|
-
/** The figure edge, in pixels, that gets the unit it always had. */
|
|
1379
|
-
const REFERENCE_EDGE = 1200;
|
|
1380
|
-
/** Smallest multiple of the base sizes, for figures far below the reference. */
|
|
1381
|
-
const MIN_UNIT = .75;
|
|
1461
|
+
* Low enough that a matplotlib SVG — which measures itself in points, ~283 wide for
|
|
1462
|
+
* the same figure a PNG exports at 1179px — is still scaled down rather than propped
|
|
1463
|
+
* up to the old floor. The floor is a guard against division, not a design choice:
|
|
1464
|
+
* a figure below ~240px is one nothing sensible is drawn on.
|
|
1465
|
+
*/
|
|
1466
|
+
const MIN_UNIT = .2;
|
|
1382
1467
|
/**
|
|
1383
1468
|
* The size everything drawn on a figure is multiplied by.
|
|
1384
1469
|
*
|
|
@@ -1392,7 +1477,7 @@ window.__ModuleLoader__.load({
|
|
|
1392
1477
|
function figureUnit(width, height) {
|
|
1393
1478
|
const edge = Math.max(width, height);
|
|
1394
1479
|
if (!Number.isFinite(edge) || edge <= 0) return 1;
|
|
1395
|
-
return Math.min(
|
|
1480
|
+
return Math.min(10, Math.max(MIN_UNIT, edge / 800));
|
|
1396
1481
|
}
|
|
1397
1482
|
//#endregion
|
|
1398
1483
|
//#region src/client/Canvas.tsx
|
|
@@ -1423,7 +1508,8 @@ window.__ModuleLoader__.load({
|
|
|
1423
1508
|
/** Minimum distance between recorded freehand samples, in figure pixels. */
|
|
1424
1509
|
const PEN_SAMPLE_STEP = 3;
|
|
1425
1510
|
/** Reach of a handle's hit area, in figure pixels: generous enough to grab. */
|
|
1426
|
-
|
|
1511
|
+
/** How far from a handle's centre a press still counts, as a multiple of its side. */
|
|
1512
|
+
const HANDLE_REACH = 2.2;
|
|
1427
1513
|
/** Monotonic id source for marks created in this session. */
|
|
1428
1514
|
let markSeq = 0;
|
|
1429
1515
|
/**
|
|
@@ -1580,10 +1666,20 @@ window.__ModuleLoader__.load({
|
|
|
1580
1666
|
* @param event - the pointer event.
|
|
1581
1667
|
* @returns true when a handle took the press.
|
|
1582
1668
|
*/
|
|
1669
|
+
/**
|
|
1670
|
+
* The control frame's handle size in figure pixels.
|
|
1671
|
+
*
|
|
1672
|
+
* The frame is drawn in the same svg as the marks, which is in figure pixels, and
|
|
1673
|
+
* the whole paper is then scaled by CSS. A handle has to be a fixed number of screen
|
|
1674
|
+
* pixels to be hittable, so its figure size is that target divided by the scale.
|
|
1675
|
+
*/
|
|
1676
|
+
const grip = 7 / Math.max(.05, props.scale ?? 1);
|
|
1677
|
+
/** A hairline that stays a hairline on screen, whatever the paper is scaled to. */
|
|
1678
|
+
const hairline = 1 / Math.max(.05, props.scale ?? 1);
|
|
1583
1679
|
const beginHandle = (mark, event) => {
|
|
1584
1680
|
if (props.selectedId !== mark.id) return false;
|
|
1585
1681
|
if (mark.kind === "point") return false;
|
|
1586
|
-
const grabbed = handleAt(controlBox(mark), mark.rotation ?? 0, pressedAt.current.value, HANDLE_REACH);
|
|
1682
|
+
const grabbed = handleAt(controlBox(mark), mark.rotation ?? 0, pressedAt.current.value, HANDLE_REACH * grip, grip);
|
|
1587
1683
|
if (grabbed === void 0) return false;
|
|
1588
1684
|
beginGesture(mark, event, grabbed);
|
|
1589
1685
|
return true;
|
|
@@ -1814,26 +1910,26 @@ window.__ModuleLoader__.load({
|
|
|
1814
1910
|
/** The resize and rotation handles of the selected mark. */
|
|
1815
1911
|
const renderHandles = (mark) => {
|
|
1816
1912
|
const frame = controlBox(mark);
|
|
1817
|
-
const half =
|
|
1818
|
-
const handles = handlesFor(frame, mark.rotation ?? 0);
|
|
1819
|
-
const tether = tetherFor(frame, mark.rotation ?? 0);
|
|
1913
|
+
const half = grip / 2;
|
|
1914
|
+
const handles = handlesFor(frame, mark.rotation ?? 0, grip);
|
|
1915
|
+
const tether = tetherFor(frame, mark.rotation ?? 0, grip);
|
|
1820
1916
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("line", {
|
|
1821
1917
|
x1: tether.from[0],
|
|
1822
1918
|
y1: tether.from[1],
|
|
1823
1919
|
x2: tether.to[0],
|
|
1824
1920
|
y2: tether.to[1],
|
|
1825
1921
|
stroke: "#4c8dff",
|
|
1826
|
-
strokeWidth:
|
|
1922
|
+
strokeWidth: hairline
|
|
1827
1923
|
}), handles.map((handle) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
1828
1924
|
"data-da-handle": handle.id,
|
|
1829
1925
|
x: handle.point[0] - half,
|
|
1830
1926
|
y: handle.point[1] - half,
|
|
1831
|
-
width:
|
|
1832
|
-
height:
|
|
1833
|
-
rx: handle.id === "rotate" ?
|
|
1927
|
+
width: grip,
|
|
1928
|
+
height: grip,
|
|
1929
|
+
rx: handle.id === "rotate" ? grip / 2 : grip / 7,
|
|
1834
1930
|
fill: "#ffffff",
|
|
1835
1931
|
stroke: "#4c8dff",
|
|
1836
|
-
strokeWidth: 1.5,
|
|
1932
|
+
strokeWidth: 1.5 * hairline,
|
|
1837
1933
|
style: {
|
|
1838
1934
|
pointerEvents: "all",
|
|
1839
1935
|
cursor: cursorFor(handle.id)
|
|
@@ -1902,16 +1998,17 @@ window.__ModuleLoader__.load({
|
|
|
1902
1998
|
const box = pointMarkBox(shown, unit);
|
|
1903
1999
|
/* v8 ignore next -- a point mark carries exactly the one coordinate it was placed at. */
|
|
1904
2000
|
if (box === void 0) return null;
|
|
2001
|
+
const inset = 3 * hairline;
|
|
1905
2002
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
1906
|
-
x: box.x -
|
|
1907
|
-
y: box.y -
|
|
1908
|
-
width: box.width +
|
|
1909
|
-
height: box.height +
|
|
1910
|
-
rx:
|
|
2003
|
+
x: box.x - inset,
|
|
2004
|
+
y: box.y - inset,
|
|
2005
|
+
width: box.width + 2 * inset,
|
|
2006
|
+
height: box.height + 2 * inset,
|
|
2007
|
+
rx: inset,
|
|
1911
2008
|
fill: "none",
|
|
1912
2009
|
stroke: "#4c8dff",
|
|
1913
|
-
strokeWidth:
|
|
1914
|
-
strokeDasharray: `${3 *
|
|
2010
|
+
strokeWidth: hairline,
|
|
2011
|
+
strokeDasharray: `${3 * hairline} ${2 * hairline}`,
|
|
1915
2012
|
style: { pointerEvents: "none" }
|
|
1916
2013
|
});
|
|
1917
2014
|
};
|
|
@@ -1998,10 +2095,6 @@ window.__ModuleLoader__.load({
|
|
|
1998
2095
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1999
2096
|
className: "da-overlay",
|
|
2000
2097
|
"data-da-overlay": "",
|
|
2001
|
-
style: {
|
|
2002
|
-
left: originX,
|
|
2003
|
-
top: originY
|
|
2004
|
-
},
|
|
2005
2098
|
width,
|
|
2006
2099
|
height,
|
|
2007
2100
|
viewBox: `0 0 ${width} ${height}`,
|
|
@@ -2281,14 +2374,13 @@ window.__ModuleLoader__.load({
|
|
|
2281
2374
|
const pages = annotatedPages(document);
|
|
2282
2375
|
const marked = pages.length === 0 ? "" : zh ? `其中 ${pages.length} 页有标注,` : `${pages.length} pages marked, `;
|
|
2283
2376
|
const kind = rendered ? zh ? "【HTML 标注】" : "[HTML annotations]" : zh ? "【附图标注】" : "[figure annotations]";
|
|
2284
|
-
const
|
|
2285
|
-
const header = zh ? `${kind}${figure.path}(${scope === "" ? "" : `${scope},`}${size}${marked}标注 ${marks.length} 处,${hash})` : `${kind} ${figure.path} (${scope === "" ? "" : `${scope}, `}${size}${marked}${marks.length} marks, ${hash})`;
|
|
2377
|
+
const header = zh ? `${kind}${figure.path}(${scope === "" ? "" : `${scope},`}${size}${marked}标注 ${marks.length} 处)` : `${kind} ${figure.path} (${scope === "" ? "" : `${scope}, `}${size}${marked}${marks.length} marks)`;
|
|
2286
2378
|
const paged = figure.pageCount !== void 0;
|
|
2287
2379
|
const pictured = paths.reviewPath !== null;
|
|
2288
2380
|
const attachment = !pictured ? [] : [paged ? zh ? "带批注 PDF 已作为本条消息的附件发送,可直接在阅读器里核对。" : "The annotated PDF is attached to this message; you can check it in any reader." : zh ? "标注图已作为本条消息的附件发送。" : "The annotated image is attached to this message."];
|
|
2289
2381
|
const noPicture = !pictured && !rendered && marks.length > 0 && !needsReviewImage(document) ? [zh ? "本条标注只有位置点,没有标注图:按上面逐条的坐标与说明处理原图即可。" : "These marks are points only, so no annotated image was made: work from the coordinates and notes above against the original figure."] : [];
|
|
2290
2382
|
const summary = document.summary !== void 0 && document.summary.trim() !== "" ? [zh ? `这张图是:${document.summary.trim()}` : `the figure shows: ${document.summary.trim()}`, ""] : [];
|
|
2291
|
-
const
|
|
2383
|
+
const closer = rendered ? zh ? "逐条改 HTML 生成源。" : "Edit the HTML source, mark by mark." : paged ? zh ? "逐条处理该 PDF。" : "Work through the PDF, mark by mark." : zh ? "逐条改生成源。" : "Edit the generating source, mark by mark.";
|
|
2292
2384
|
return [
|
|
2293
2385
|
header,
|
|
2294
2386
|
...attachment,
|
|
@@ -2297,7 +2389,7 @@ window.__ModuleLoader__.load({
|
|
|
2297
2389
|
...summary,
|
|
2298
2390
|
...describeMarks(document, locale),
|
|
2299
2391
|
"",
|
|
2300
|
-
|
|
2392
|
+
closer
|
|
2301
2393
|
].join("\n");
|
|
2302
2394
|
}
|
|
2303
2395
|
/**
|
|
@@ -2684,7 +2776,25 @@ window.__ModuleLoader__.load({
|
|
|
2684
2776
|
const unit = figureUnit(layer.width, layer.height);
|
|
2685
2777
|
const paper = plotBounds(layer.width, layer.height, marks, unit);
|
|
2686
2778
|
const shift = `translate(${-paper.x} ${-paper.y})`;
|
|
2687
|
-
|
|
2779
|
+
const edge = paper.x !== 0 || paper.y !== 0 || paper.width !== layer.width || paper.height !== layer.height ? figureEdge(layer.width, layer.height, unit) : "";
|
|
2780
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${paper.width}" height="${paper.height}" viewBox="0 0 ${paper.width} ${paper.height}"><rect x="0" y="0" width="${paper.width}" height="${paper.height}" fill="#ffffff"/><g transform="${shift}">` + figureSvg(layer) + edge + marks.map((mark) => markSvg(mark, numbers.get(mark.id), unit)).join("") + "</g></svg>";
|
|
2781
|
+
}
|
|
2782
|
+
/** Round a stroke measurement to a tenth, as the path data does. */
|
|
2783
|
+
function hair9(value) {
|
|
2784
|
+
return (Math.round(value * 10) / 10).toString();
|
|
2785
|
+
}
|
|
2786
|
+
/**
|
|
2787
|
+
* The rule that marks where the figure ends and the grown paper begins.
|
|
2788
|
+
*
|
|
2789
|
+
* Drawn only when the paper grew, and drawn grey and dashed: it is a note about the
|
|
2790
|
+
* picture, not part of it, so it must not read as a mark the reader made.
|
|
2791
|
+
* @param width - the figure's width, in figure pixels.
|
|
2792
|
+
* @param height - the figure's height, in figure pixels.
|
|
2793
|
+
* @param unit - the figure's size multiple, so the rule is sized like the marks.
|
|
2794
|
+
* @returns an SVG rect, or an empty string when there is nothing to outline.
|
|
2795
|
+
*/
|
|
2796
|
+
function figureEdge(width, height, unit) {
|
|
2797
|
+
return `<rect x="0" y="0" width="${width}" height="${height}" fill="none" stroke="#9aa3b2" stroke-width="${hair9(Math.max(1, 1.5 * unit))}" stroke-dasharray="${hair9(6 * unit)} ${hair9(4 * unit)}"/>`;
|
|
2688
2798
|
}
|
|
2689
2799
|
/**
|
|
2690
2800
|
* The size of the picture {@link composeReviewSvg} builds.
|
|
@@ -3253,10 +3363,27 @@ window.__ModuleLoader__.load({
|
|
|
3253
3363
|
const STYLE_MARKER = "data-dsh-figmark";
|
|
3254
3364
|
/** The stylesheet text. */
|
|
3255
3365
|
const STYLES = `
|
|
3366
|
+
/* An accent of the plugin's own.
|
|
3367
|
+
The shell's --dsw-alias-brand-primary is not a brand colour in this theme — it
|
|
3368
|
+
resolves to the *ink* (near-black on light, near-white on dark), because the shell
|
|
3369
|
+
uses it for filled buttons in monochrome. Reading it as "the blue" turned this
|
|
3370
|
+
panel's sliders, filled buttons and selection rings into ink: a black knob on a
|
|
3371
|
+
white rail, and a wedge that goes solid black at the heaviest setting. The alias
|
|
3372
|
+
stays as the fallback so a shell that really does define a brand colour still wins,
|
|
3373
|
+
but the accent is named here so every rule reads one variable instead of quietly
|
|
3374
|
+
inheriting whatever "primary" means that week.
|
|
3375
|
+
The dark block re-states it, because a blue tuned for white paper is too dark on a
|
|
3376
|
+
near-black panel. */
|
|
3256
3377
|
/* The panel follows the shell's palette rather than carrying one of its own: every
|
|
3257
3378
|
colour below is an alias the theme owner resolves, with the inherited value as the
|
|
3258
3379
|
fallback so a shell that defines nothing still renders something legible. */
|
|
3259
|
-
.da-root{
|
|
3380
|
+
.da-root{--da-accent:#176df2;--da-accent-soft:rgba(23,109,242,.14);
|
|
3381
|
+
position:relative;display:flex;flex-direction:column;height:100%;min-height:0;overflow:hidden;color:var(--dsw-alias-label-primary,inherit);background:var(--dsw-alias-bg-document-preview,transparent)}
|
|
3382
|
+
/* The shell marks its dark palette on <body> rather than through prefers-color-scheme:
|
|
3383
|
+
a reader can pick either palette whatever the OS is set to, so following the shell's
|
|
3384
|
+
own switch is what keeps the accent readable in both. A blue tuned for white paper
|
|
3385
|
+
is too dark on a near-black panel. */
|
|
3386
|
+
body[data-ds-dark-theme] .da-root{--da-accent:#4c8dff;--da-accent-soft:rgba(76,141,255,.22)}
|
|
3260
3387
|
|
|
3261
3388
|
/* Every small picture in the panel is one stroke set, declared once: without this
|
|
3262
3389
|
an svg with no fill of its own falls back to a solid black shape, which is what
|
|
@@ -3274,12 +3401,20 @@ window.__ModuleLoader__.load({
|
|
|
3274
3401
|
box-shadow:0 8px 26px rgba(10,14,24,.3),0 0 0 1px rgba(255,255,255,.08)}
|
|
3275
3402
|
/* While it is being dragged the pointer is the grip, whatever it passes over. */
|
|
3276
3403
|
.da-palette[data-dragging="true"]{opacity:.94;box-shadow:0 12px 32px rgba(10,14,24,.4),0 0 0 1px rgba(255,255,255,.14)}
|
|
3277
|
-
.da-palette[data-dragging="true"] *{cursor:
|
|
3278
|
-
/* The handle: a
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
.
|
|
3282
|
-
.da-palette-grip
|
|
3404
|
+
.da-palette[data-dragging="true"] *{cursor:move !important}
|
|
3405
|
+
/* The handle: a block of six dots, the one part of a strip that only drags. The block
|
|
3406
|
+
is turned to suit the strip — wider than tall on the bar, taller than wide on the
|
|
3407
|
+
palette — because a grip whose proportions fight its strip looks borrowed from the
|
|
3408
|
+
other one. */
|
|
3409
|
+
.da-palette-grip{display:flex;align-items:center;justify-content:center;width:26px;height:17px;
|
|
3410
|
+
border-radius:5px;cursor:move;color:rgba(255,255,255,.55);touch-action:none;flex:0 0 auto}
|
|
3411
|
+
.da-palette-grip:hover{color:rgba(255,255,255,.92);background:rgba(255,255,255,.1)}
|
|
3412
|
+
/* The dots' box follows the block: three across by two down needs width, two across
|
|
3413
|
+
by three down needs height. Sized per block rather than once, so the svg is not
|
|
3414
|
+
letterboxed inside its own handle. */
|
|
3415
|
+
.da-grip-dots{width:12px;height:8px;fill:currentColor;display:block}
|
|
3416
|
+
.da-bar .da-palette-grip{width:16px;height:18px}
|
|
3417
|
+
.da-bar .da-grip-dots{width:8px;height:12px}
|
|
3283
3418
|
/* A tile is a round button holding one white glyph. No word under it: six glyphs in
|
|
3284
3419
|
a column are read at a glance, and the word is still there as the tooltip and as
|
|
3285
3420
|
the accessible name. */
|
|
@@ -3301,6 +3436,7 @@ window.__ModuleLoader__.load({
|
|
|
3301
3436
|
drawing. The drawn parts never take the pointer, or grabbing the knob would grab
|
|
3302
3437
|
the knob instead of the slider. */
|
|
3303
3438
|
.da-weight{position:absolute;z-index:7;left:6px;top:50%;transform:translateY(-50%);
|
|
3439
|
+
--da-track-inset:10px;--da-knob-size:11px;
|
|
3304
3440
|
display:flex;align-items:center;justify-content:center;width:7px;height:200px;
|
|
3305
3441
|
border-radius:999px;background:var(--dsw-alias-border-l2,rgba(127,127,127,.22));
|
|
3306
3442
|
cursor:ns-resize;transition:width .16s ease,background .16s ease,box-shadow .16s ease}
|
|
@@ -3309,13 +3445,24 @@ window.__ModuleLoader__.load({
|
|
|
3309
3445
|
/* The track is an svg in the body: a hairline at the bottom that opens into a rounded
|
|
3310
3446
|
shoulder at the top. It is not a clip-path polygon because a polygon has no rounded
|
|
3311
3447
|
ends — the corners were what made the wedge read as a cut-out blade rather than a
|
|
3312
|
-
part of the panel. preserveAspectRatio="none" stretches one
|
|
3313
|
-
rail's height, so the shoulders stay smooth at any length.
|
|
3314
|
-
|
|
3315
|
-
|
|
3448
|
+
part of the panel. preserveAspectRatio="none" stretches one 18x100 shape over the
|
|
3449
|
+
rail's height, so the shoulders stay smooth at any length.
|
|
3450
|
+
The height is written out rather than expressed as top+bottom: an svg is a replaced
|
|
3451
|
+
element: with a viewBox it takes its intrinsic height for its width and ignores the
|
|
3452
|
+
bottom offset entirely. The track was therefore a fixed 100px tall while the knob
|
|
3453
|
+
travelled the whole 200px rail, which is why the two could never line up. */
|
|
3454
|
+
.da-weight-svg{position:absolute;left:50%;top:var(--da-track-inset);height:calc(100% - 2 * var(--da-track-inset));
|
|
3455
|
+
width:18px;transform:translateX(-50%);opacity:0;pointer-events:none;
|
|
3456
|
+
transition:opacity .16s ease}
|
|
3316
3457
|
.da-weight:hover .da-weight-svg,.da-weight:focus-within .da-weight-svg{opacity:1}
|
|
3317
3458
|
/* The knob sits on the value: the only part that shows at rest, so the rail reads as
|
|
3318
3459
|
a slider waiting to be touched rather than a bare rule. */
|
|
3460
|
+
/* The blue: a bar of the same width as the groove, its height the one number the
|
|
3461
|
+
knob also uses, so its top edge is the knob's centre at every weight. */
|
|
3462
|
+
.da-weight-fill{position:absolute;left:50%;bottom:var(--da-track-inset);width:5px;
|
|
3463
|
+
transform:translateX(-50%);border-radius:999px;background:var(--da-accent,#176df2);
|
|
3464
|
+
opacity:0;pointer-events:none;transition:opacity .16s ease}
|
|
3465
|
+
.da-weight:hover .da-weight-fill,.da-weight:focus-within .da-weight-fill{opacity:1}
|
|
3319
3466
|
.da-weight-knob{position:absolute;left:50%;width:var(--da-knob-size,11px);height:var(--da-knob-size,11px);
|
|
3320
3467
|
border-radius:50%;transform:translateX(-50%);pointer-events:none;
|
|
3321
3468
|
background:var(--dsw-alias-bg-layer-2,#fff);
|
|
@@ -3323,7 +3470,7 @@ window.__ModuleLoader__.load({
|
|
|
3323
3470
|
transition:width .16s ease,height .16s ease,background .16s ease}
|
|
3324
3471
|
.da-weight:hover,.da-weight:focus-within{--da-knob-size:15px}
|
|
3325
3472
|
.da-weight:hover .da-weight-knob,.da-weight:focus-within .da-weight-knob{
|
|
3326
|
-
background:var(--
|
|
3473
|
+
background:var(--da-accent,#176df2);
|
|
3327
3474
|
box-shadow:0 2px 7px rgba(20,40,80,.3)}
|
|
3328
3475
|
/* The browser's own range, rotated upright and made transparent. It spans the whole
|
|
3329
3476
|
rail so a press anywhere on it lands on the slider — including on the knob, which
|
|
@@ -3345,7 +3492,7 @@ window.__ModuleLoader__.load({
|
|
|
3345
3492
|
it centred while untouched must not also apply — with both set it stretches. */
|
|
3346
3493
|
.da-bar[data-parked="true"]{bottom:auto}
|
|
3347
3494
|
.da-bar[data-dragging="true"]{opacity:.94;box-shadow:0 12px 32px rgba(10,14,24,.4),0 0 0 1px rgba(255,255,255,.14)}
|
|
3348
|
-
.da-bar[data-dragging="true"] *{cursor:
|
|
3495
|
+
.da-bar[data-dragging="true"] *{cursor:move !important}
|
|
3349
3496
|
/* The bar's buttons are white glyphs on the dark strip. The one filled control is
|
|
3350
3497
|
still the way out. */
|
|
3351
3498
|
.da-bar .da-dock-btn{color:#fff}
|
|
@@ -3386,8 +3533,8 @@ window.__ModuleLoader__.load({
|
|
|
3386
3533
|
.da-tool .da-tool-label{font-size:10px;line-height:1;white-space:nowrap;color:var(--dsw-alias-label-secondary,inherit)}
|
|
3387
3534
|
/* The armed tool is the one state in this row the reader must never hunt for: it is
|
|
3388
3535
|
the only tile with a fill, and the only label in the brand colour. */
|
|
3389
|
-
.da-tool[aria-pressed="true"]{border-color:transparent;background:var(--
|
|
3390
|
-
.da-tool[aria-pressed="true"] .da-tool-label{color:var(--
|
|
3536
|
+
.da-tool[aria-pressed="true"]{border-color:transparent;background:var(--da-accent-soft,rgba(23,109,242,.1));color:var(--da-accent,currentColor)}
|
|
3537
|
+
.da-tool[aria-pressed="true"] .da-tool-label{color:var(--da-accent,currentColor);font-weight:600}
|
|
3391
3538
|
.da-tool[aria-pressed="true"] svg{stroke-width:2.1}
|
|
3392
3539
|
.da-tool:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.08))}
|
|
3393
3540
|
.da-btn-icon{width:30px;padding:0}
|
|
@@ -3398,6 +3545,10 @@ window.__ModuleLoader__.load({
|
|
|
3398
3545
|
.da-stylewrap{position:relative;flex:0 0 auto}
|
|
3399
3546
|
/* The style fold floats above the bar rather than taking a row of it: opened over
|
|
3400
3547
|
the figure, it costs the picture nothing and lands where the eye already is. */
|
|
3548
|
+
/* Opens upwards by default, because the bar lives at the bottom. Parked at the top
|
|
3549
|
+
there is nothing above it to open into, so the fold turns over and hangs below its
|
|
3550
|
+
own button instead — decided by the same measurement the records use. */
|
|
3551
|
+
.da-style-row[data-side="below"]{bottom:auto;top:calc(100% + 6px);box-shadow:0 -6px 22px rgba(20,40,80,.12),0 6px 22px rgba(20,40,80,.16)}
|
|
3401
3552
|
.da-style-row{position:absolute;left:8px;bottom:calc(100% + 6px);z-index:9;
|
|
3402
3553
|
display:flex;align-items:center;gap:8px;flex-wrap:wrap;max-width:calc(100% - 16px);
|
|
3403
3554
|
padding:8px 10px;border:0.5px solid var(--dsw-alias-border-l2,currentColor);border-radius:11px;
|
|
@@ -3412,17 +3563,24 @@ window.__ModuleLoader__.load({
|
|
|
3412
3563
|
/* Anywhere else is "done here": the panel closes on the click that lands outside it
|
|
3413
3564
|
rather than trapping the reader in a popup with one way out. */
|
|
3414
3565
|
.da-swatch{width:20px;height:20px;border-radius:50%;corner-shape:round;border:2px solid var(--dsw-alias-bg-base,transparent);box-shadow:0 0 0 1px var(--dsw-alias-border-l4,rgba(127,127,127,.5));cursor:pointer;padding:0}
|
|
3415
|
-
.da-swatch[aria-pressed="true"]{box-shadow:0 0 0 2px var(--
|
|
3566
|
+
.da-swatch[aria-pressed="true"]{box-shadow:0 0 0 2px var(--da-accent,currentColor)}
|
|
3416
3567
|
/* --- the figure on its table -------------------------------------------- */
|
|
3417
3568
|
/* A figure is a sheet on a table, not a picture floating in a void: the white paper
|
|
3418
3569
|
is what gives the panel a middle, and the gap around it is the margin the eye
|
|
3419
3570
|
needs to see where the drawing stops. */
|
|
3420
3571
|
.da-stage{position:relative;display:flex;align-items:safe center;justify-content:safe center;flex:1 1 auto;overflow:auto;min-height:0;padding:14px;background:var(--dsw-alias-bg-document-preview,transparent)}
|
|
3421
|
-
.da-surface{position:relative;flex:0 0 auto;margin:0;transform-origin:top left
|
|
3422
|
-
/* The paper: the figure plus the margin a mark may have needed. It is
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
.
|
|
3572
|
+
.da-surface{position:relative;flex:0 0 auto;margin:0;transform-origin:top left}
|
|
3573
|
+
/* The paper: the figure plus the margin a mark may have needed. It is deliberately
|
|
3574
|
+
left transparent — the white sheet belongs to the figure, so a margin that only
|
|
3575
|
+
exists because a mark reached past the edge reads as board round a card rather than
|
|
3576
|
+
as more of the figure. The exported picture still gets its white fill: that one is
|
|
3577
|
+
looked at by someone who has no board behind it. */
|
|
3578
|
+
.da-paper{background:transparent}
|
|
3579
|
+
/* The figure is the sheet: white, rounded, lifted off the panel. This is the element
|
|
3580
|
+
that carries the paper look, which is why the margin can be see-through without the
|
|
3581
|
+
drawing losing its edge. */
|
|
3582
|
+
.da-figure-frame{background:var(--dsw-alias-bg-base,#fff);border-radius:4px;overflow:hidden;
|
|
3583
|
+
box-shadow:var(--dsw-elevation-prominent,0 1px 3px rgba(0,0,0,.14),0 8px 24px rgba(0,0,0,.08))}
|
|
3426
3584
|
.da-figure{display:block;user-select:none;-webkit-user-drag:none}
|
|
3427
3585
|
/* The table is a thing you can push. The hand only shows under the tools that
|
|
3428
3586
|
leave a drag free to move the view: under a drawing tool the same drag draws. */
|
|
@@ -3455,22 +3613,22 @@ window.__ModuleLoader__.load({
|
|
|
3455
3613
|
width:28px;height:28px;padding:0;border:0;border-radius:50%;background:transparent;
|
|
3456
3614
|
color:inherit;cursor:pointer;flex:0 0 auto}
|
|
3457
3615
|
.da-dock-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.12))}
|
|
3458
|
-
.da-dock-btn[aria-pressed="true"]{background:var(--
|
|
3616
|
+
.da-dock-btn[aria-pressed="true"]{background:var(--da-accent-soft,rgba(23,109,242,.12));color:var(--da-accent,#176df2)}
|
|
3459
3617
|
.da-dock-btn svg{width:15px;height:15px}
|
|
3460
3618
|
.da-dock-btn:disabled{opacity:.4;cursor:default}
|
|
3461
3619
|
.da-dock-btn:disabled:hover{background:transparent}
|
|
3462
3620
|
/* The one filled control in the panel is the way out of it. */
|
|
3463
|
-
.da-dock-btn-primary{border-color:var(--
|
|
3464
|
-
.da-dock-btn-primary:hover{opacity:.92;background:var(--
|
|
3621
|
+
.da-dock-btn-primary{border-color:var(--da-accent,#176df2);background:var(--da-accent,#176df2);color:#fff;box-shadow:0 2px 8px rgba(23,109,242,.32)}
|
|
3622
|
+
.da-dock-btn-primary:hover{opacity:.92;background:var(--da-accent,#176df2)}
|
|
3465
3623
|
/* A dot, not a count: "there is a goal written" is all the bar needs to say about
|
|
3466
3624
|
a field it is not a list of. */
|
|
3467
3625
|
.da-dock-dot{position:absolute;top:3px;right:4px;width:5px;height:5px;border-radius:50%;
|
|
3468
|
-
background:var(--
|
|
3626
|
+
background:var(--da-accent,#176df2)}
|
|
3469
3627
|
.da-dock-sep{width:1px;height:16px;margin:0 3px;background:var(--dsw-alias-border-l3,currentColor);opacity:.6;flex:0 0 auto}
|
|
3470
3628
|
/* The count rides on the list's own button: it is the one thing about the list a
|
|
3471
3629
|
reader wants without opening it. */
|
|
3472
3630
|
.da-dock-count{position:absolute;top:-1px;right:-2px;min-width:14px;height:14px;padding:0 3px;
|
|
3473
|
-
border-radius:7px;background:var(--
|
|
3631
|
+
border-radius:7px;background:var(--da-accent,#176df2);color:#fff;
|
|
3474
3632
|
font-size:9px;font-weight:700;line-height:14px;text-align:center;font-variant-numeric:tabular-nums}
|
|
3475
3633
|
|
|
3476
3634
|
/* --- the floating records ------------------------------------------------ */
|
|
@@ -3482,7 +3640,7 @@ window.__ModuleLoader__.load({
|
|
|
3482
3640
|
background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 6px 22px rgba(20,40,80,.14),0 0 0 1px rgba(20,40,80,.03)}
|
|
3483
3641
|
.da-card-title{display:flex;align-items:center;gap:5px;min-width:0;font-size:12px;font-weight:650;
|
|
3484
3642
|
color:var(--dsw-alias-label-primary,inherit)}
|
|
3485
|
-
.da-card-title svg{width:14px;height:14px;flex:0 0 auto;color:var(--
|
|
3643
|
+
.da-card-title svg{width:14px;height:14px;flex:0 0 auto;color:var(--da-accent,#176df2)}
|
|
3486
3644
|
|
|
3487
3645
|
.da-float-head{display:flex;align-items:center;gap:7px}
|
|
3488
3646
|
.da-float-head .da-card-title{flex:1 1 auto;min-width:0}
|
|
@@ -3521,10 +3679,10 @@ window.__ModuleLoader__.load({
|
|
|
3521
3679
|
color:var(--dsw-alias-label-primary,currentColor)}
|
|
3522
3680
|
.da-comment-send-btn:disabled{opacity:.45;cursor:default}
|
|
3523
3681
|
.da-comment-send-btn svg{width:15px;height:15px}
|
|
3524
|
-
.da-comment-send-primary{background:var(--
|
|
3525
|
-
.da-comment-send-primary:hover{background:var(--
|
|
3682
|
+
.da-comment-send-primary{background:var(--da-accent,#176df2);color:#fff}
|
|
3683
|
+
.da-comment-send-primary:hover{background:var(--da-accent,#176df2);color:#fff;filter:brightness(1.08)}
|
|
3526
3684
|
.da-float textarea{width:100%;box-sizing:border-box;min-height:74px;max-height:200px;resize:vertical;border:0.5px solid var(--dsw-alias-border-l3,currentColor);border-radius:9px;background:var(--dsw-alias-bg-layer-2,transparent);color:inherit;font:inherit;font-size:12px;padding:8px 10px;line-height:1.65}
|
|
3527
|
-
.da-float textarea:focus{outline:none;border-color:var(--
|
|
3685
|
+
.da-float textarea:focus{outline:none;border-color:var(--da-accent,#176df2)}
|
|
3528
3686
|
.da-btn-count{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;
|
|
3529
3687
|
padding:0 4px;border-radius:8px;background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.14));
|
|
3530
3688
|
font-size:10px;font-weight:600;font-variant-numeric:tabular-nums}
|
|
@@ -3537,8 +3695,8 @@ window.__ModuleLoader__.load({
|
|
|
3537
3695
|
/* What the selected mark is, in one word: the detail field is disabled until there
|
|
3538
3696
|
is something to describe, and this says what it would describe. */
|
|
3539
3697
|
.da-chip{display:inline-flex;align-items:center;height:16px;padding:0 6px;border-radius:5px;
|
|
3540
|
-
background:var(--
|
|
3541
|
-
color:var(--
|
|
3698
|
+
background:var(--da-accent-soft,rgba(23,109,242,.12));
|
|
3699
|
+
color:var(--da-accent,#176df2);font-size:10px;font-weight:600}
|
|
3542
3700
|
/* The "?" beside a card's name: the long form of what that field is for. */
|
|
3543
3701
|
.da-hintdot{display:inline-flex;align-items:center;justify-content:center;width:13px;height:13px;
|
|
3544
3702
|
border-radius:50%;border:0.5px solid var(--dsw-alias-border-l3,currentColor);
|
|
@@ -3551,7 +3709,7 @@ window.__ModuleLoader__.load({
|
|
|
3551
3709
|
.da-mark{display:flex;align-items:center;gap:7px;padding:5px 6px;border-radius:8px;
|
|
3552
3710
|
border:0.5px solid transparent;min-width:0}
|
|
3553
3711
|
.da-mark:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.08))}
|
|
3554
|
-
.da-mark[data-selected="true"]{border-color:var(--
|
|
3712
|
+
.da-mark[data-selected="true"]{border-color:var(--da-accent,#176df2);
|
|
3555
3713
|
background:var(--dsw-alias-interactive-bg-hover,rgba(23,109,242,.08))}
|
|
3556
3714
|
.da-mark-index{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
|
|
3557
3715
|
width:20px;height:20px;border-radius:6px;color:#fff;font-size:11px;font-weight:700;
|
|
@@ -3823,24 +3981,36 @@ window.__ModuleLoader__.load({
|
|
|
3823
3981
|
/**
|
|
3824
3982
|
* The rail's track, drawn on an 18x100 grid and stretched to whatever height it gets.
|
|
3825
3983
|
*
|
|
3826
|
-
*
|
|
3827
|
-
*
|
|
3828
|
-
*
|
|
3829
|
-
*
|
|
3830
|
-
*
|
|
3831
|
-
*
|
|
3832
|
-
*
|
|
3984
|
+
* The control's subject is thickness, so the track says which way is heavier: a little
|
|
3985
|
+
* narrower at the bottom, about one part in six. "A little" is the whole note — a
|
|
3986
|
+
* water-drop taper reads as decoration rather than as a gauge, and the reader said so
|
|
3987
|
+
* twice. One cubic per side and no shoulders, so each flank is a single continuous
|
|
3988
|
+
* curve from end to end with nothing for the eye to catch on; the two ends close with
|
|
3989
|
+
* faint arcs so the track looks drawn rather than cut off.
|
|
3990
|
+
*
|
|
3991
|
+
* Stretching it vertically (preserveAspectRatio="none") is what makes the caps need
|
|
3992
|
+
* care: the svg is 18x100 over a box about 180 tall, so one y unit is 1.8 of an x unit.
|
|
3993
|
+
* An `r` that reads as a circle in the viewBox would be a tall ellipse on screen — the
|
|
3994
|
+
* cap's `ry` is therefore set to `rx / 1.8`, which is what puts a round end on it.
|
|
3833
3995
|
*/
|
|
3834
3996
|
const WEIGHT_TRACK = [
|
|
3835
|
-
"M
|
|
3836
|
-
"
|
|
3837
|
-
"C
|
|
3838
|
-
"A
|
|
3839
|
-
"C
|
|
3840
|
-
"C 10.1 58 9.8 78 9.8 100",
|
|
3841
|
-
"A 0.8 0.8 0 0 1 8.2 100",
|
|
3997
|
+
"M 6.9 98.9",
|
|
3998
|
+
"A 2.1 1.15 0 0 0 11.1 98.9",
|
|
3999
|
+
"C 11.4 66 11.9 33 12.2 1.1",
|
|
4000
|
+
"A 3.2 1.15 0 0 0 5.8 1.1",
|
|
4001
|
+
"C 6.1 33 6.6 66 6.9 98.9",
|
|
3842
4002
|
"Z"
|
|
3843
4003
|
].join(" ");
|
|
4004
|
+
/**
|
|
4005
|
+
* How far the blue reaches, as a `height` in the rail.
|
|
4006
|
+
*
|
|
4007
|
+
* The same expression as the knob's `bottom`, because the blue has to end exactly where
|
|
4008
|
+
* the knob's centre is: written once as a function so a change to the travel cannot
|
|
4009
|
+
* leave the fill behind. `inset` keeps both clear of the rail's rounded caps; the extra
|
|
4010
|
+
* half knob at each end keeps the knob's whole circle between the two ends of the bar
|
|
4011
|
+
* rather than hanging half of itself past the top when the pen is at its heaviest.
|
|
4012
|
+
*/
|
|
4013
|
+
const travelHeight = (fraction) => `calc(var(--da-track-inset) + var(--da-knob-size) / 2 + (100% - 2 * var(--da-track-inset) - var(--da-knob-size)) * ${fraction})`;
|
|
3844
4014
|
const TOOLS = [
|
|
3845
4015
|
{
|
|
3846
4016
|
tool: "select",
|
|
@@ -3897,21 +4067,61 @@ window.__ModuleLoader__.load({
|
|
|
3897
4067
|
* with the mouse rather than a mark you leave behind. Drawn rather than written so
|
|
3898
4068
|
* the toolbar stays one row of controls at any locale or font size.
|
|
3899
4069
|
*/
|
|
4070
|
+
/**
|
|
4071
|
+
* The handle's dots: six of them, three by two, turned as the reader asked.
|
|
4072
|
+
*
|
|
4073
|
+
* The palette stands up and takes a grip three columns wide by two rows tall; the bar
|
|
4074
|
+
* lies down and takes one two columns wide by three rows tall. That is the pair the
|
|
4075
|
+
* reader called for, and it is the opposite of what the strips' own proportions would
|
|
4076
|
+
* suggest — noted here so it is not "corrected" back by the next pair of eyes.
|
|
4077
|
+
*
|
|
4078
|
+
* Drawn instead of typed — the braille character this replaces was one glyph at one
|
|
4079
|
+
* size, so the dots could not be sized or spaced, and at 9px they came out a smudge.
|
|
4080
|
+
*/
|
|
4081
|
+
function GripDots({ wide }) {
|
|
4082
|
+
const xs = wide ? [
|
|
4083
|
+
2,
|
|
4084
|
+
6,
|
|
4085
|
+
10
|
|
4086
|
+
] : [2, 6];
|
|
4087
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4088
|
+
className: "da-grip-dots",
|
|
4089
|
+
viewBox: wide ? "0 0 12 8" : "0 0 8 12",
|
|
4090
|
+
"aria-hidden": "true",
|
|
4091
|
+
focusable: "false",
|
|
4092
|
+
children: (wide ? [2, 6] : [
|
|
4093
|
+
2,
|
|
4094
|
+
6,
|
|
4095
|
+
10
|
|
4096
|
+
]).map((y) => xs.map((x) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
4097
|
+
cx: x,
|
|
4098
|
+
cy: y,
|
|
4099
|
+
r: 1.4
|
|
4100
|
+
}, `${x}-${y}`)))
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
/**
|
|
4104
|
+
* The select tool: a cursor, outlined rather than filled.
|
|
4105
|
+
*
|
|
4106
|
+
* Filled, it read as a black blob against the dark chip — the silhouette was there but
|
|
4107
|
+
* the direction it points was not, which is the only thing a cursor has to say. As an
|
|
4108
|
+
* outline it belongs to the same stroke set as the other five glyphs.
|
|
4109
|
+
*
|
|
4110
|
+
* The shape is Tabler's `cursor-text`-sibling `pointer` glyph, taken whole at its own
|
|
4111
|
+
* 24 grid: its body is one closed curve with round joins rather than the seven hard
|
|
4112
|
+
* corners a hand-traced outline has, and at 16px that softness is the whole difference
|
|
4113
|
+
* between a cursor and a shard. Scaled by hand it lost exactly that.
|
|
4114
|
+
*/
|
|
3900
4115
|
function PointerIcon() {
|
|
3901
4116
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
viewBox: "0 0 13 15",
|
|
4117
|
+
className: "da-ico",
|
|
4118
|
+
viewBox: "0 0 24 24",
|
|
3905
4119
|
"aria-hidden": "true",
|
|
3906
4120
|
focusable: "false",
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
stroke: "currentColor",
|
|
3912
|
-
strokeWidth: "0.9",
|
|
3913
|
-
strokeLinejoin: "round"
|
|
3914
|
-
})
|
|
4121
|
+
strokeWidth: "2",
|
|
4122
|
+
strokeLinecap: "round",
|
|
4123
|
+
strokeLinejoin: "round",
|
|
4124
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M14.185 13.14l5.644 -2.202c1.625 -.634 1.538 -2.962 -.13 -3.473l-14.319 -4.382c-1.41 -.431 -2.73 .888 -2.298 2.298l4.382 14.318c.51 1.668 2.84 1.755 3.473 .13l2.202 -5.644a1.84 1.84 0 0 1 1.045 -1.045" })
|
|
3915
4125
|
});
|
|
3916
4126
|
}
|
|
3917
4127
|
/**
|
|
@@ -4114,6 +4324,7 @@ window.__ModuleLoader__.load({
|
|
|
4114
4324
|
const barRef = (0, react.useRef)(null);
|
|
4115
4325
|
const figureHostRef = (0, react.useRef)(null);
|
|
4116
4326
|
const surfaceRef = (0, react.useRef)(null);
|
|
4327
|
+
const paperRef = (0, react.useRef)(null);
|
|
4117
4328
|
const figureRef = (0, react.useRef)(null);
|
|
4118
4329
|
/**
|
|
4119
4330
|
* Hand the stage to the owner as its scrollport, and keep it here too.
|
|
@@ -4655,6 +4866,17 @@ window.__ModuleLoader__.load({
|
|
|
4655
4866
|
htmlHeight
|
|
4656
4867
|
]);
|
|
4657
4868
|
/**
|
|
4869
|
+
* Whether an upward fold would come out of the top of the panel.
|
|
4870
|
+
*
|
|
4871
|
+
* The style fold is anchored to its own button, so it climbs — which is right while
|
|
4872
|
+
* the bar sits at the bottom and wrong the moment the reader parks the bar along the
|
|
4873
|
+
* top, where climbing puts the swatches out of sight above the panel. The record
|
|
4874
|
+
* placement already answers this question for the records; the fold reads the same
|
|
4875
|
+
* answer rather than measuring a second time, so the two can never disagree about
|
|
4876
|
+
* where there is room.
|
|
4877
|
+
*/
|
|
4878
|
+
const foldOpensDown = floatsAt !== null && floatsAt.bottom === void 0;
|
|
4879
|
+
/**
|
|
4658
4880
|
* Put a record away when the reader clicks anywhere else.
|
|
4659
4881
|
*
|
|
4660
4882
|
* A record is a fold, not a mode: it has done its job the moment the reader looks
|
|
@@ -4752,9 +4974,9 @@ window.__ModuleLoader__.load({
|
|
|
4752
4974
|
const zoomAt = (0, react.useCallback)((next, clientX, clientY) => {
|
|
4753
4975
|
const stage = stageRef.current;
|
|
4754
4976
|
const surface = surfaceRef.current;
|
|
4755
|
-
const
|
|
4977
|
+
const sheet = paperRef.current;
|
|
4756
4978
|
/* v8 ignore next -- a wheel event only reaches a handler on a mounted stage, and size is what allows one. */
|
|
4757
|
-
if (stage === null || surface === null ||
|
|
4979
|
+
if (stage === null || surface === null || sheet === null || paper === null) return;
|
|
4758
4980
|
const before = liveScale.current;
|
|
4759
4981
|
const reached = clampZoom(next);
|
|
4760
4982
|
if (reached === before) return;
|
|
@@ -4768,19 +4990,22 @@ window.__ModuleLoader__.load({
|
|
|
4768
4990
|
}, {
|
|
4769
4991
|
clientX,
|
|
4770
4992
|
clientY
|
|
4771
|
-
}, before, reached,
|
|
4993
|
+
}, before, reached, paper);
|
|
4772
4994
|
liveScale.current = reached;
|
|
4773
|
-
surface.style.width = `${
|
|
4774
|
-
surface.style.height = `${
|
|
4775
|
-
|
|
4995
|
+
surface.style.width = `${paper.width * reached}px`;
|
|
4996
|
+
surface.style.height = `${paper.height * reached}px`;
|
|
4997
|
+
sheet.style.transform = `scale(${reached})`;
|
|
4776
4998
|
stage.scrollLeft = target.scrollLeft;
|
|
4777
4999
|
stage.scrollTop = target.scrollTop;
|
|
4778
5000
|
clearTimeout(settleTimer.current);
|
|
4779
5001
|
settleTimer.current = setTimeout(() => {
|
|
4780
5002
|
settleTimer.current = void 0;
|
|
5003
|
+
const settled = paperRef.current;
|
|
5004
|
+
/* v8 ignore next -- the sheet is the element the pinch was measured on. */
|
|
5005
|
+
if (settled !== null) settled.style.transform = "";
|
|
4781
5006
|
setZoom(reached);
|
|
4782
5007
|
}, 120);
|
|
4783
|
-
}, [
|
|
5008
|
+
}, [paper]);
|
|
4784
5009
|
(0, react.useEffect)(() => () => {
|
|
4785
5010
|
clearTimeout(settleTimer.current);
|
|
4786
5011
|
}, []);
|
|
@@ -5281,7 +5506,7 @@ window.__ModuleLoader__.load({
|
|
|
5281
5506
|
* One number for the fill, the knob and the input: computed three times the three
|
|
5282
5507
|
* could round differently and the knob would sit a pixel off the fill it belongs to.
|
|
5283
5508
|
*/
|
|
5284
|
-
const weightFraction = shownWeight === null ? 0 : (shownWeight - WEIGHT_MIN) / (
|
|
5509
|
+
const weightFraction = shownWeight === null ? 0 : (shownWeight - WEIGHT_MIN) / (8 - WEIGHT_MIN);
|
|
5285
5510
|
/**
|
|
5286
5511
|
* Change one attribute of the selected mark, or of the next stroke when nothing
|
|
5287
5512
|
* is selected.
|
|
@@ -5366,13 +5591,15 @@ window.__ModuleLoader__.load({
|
|
|
5366
5591
|
className: "da-surface",
|
|
5367
5592
|
ref: surfaceRef,
|
|
5368
5593
|
style: surfaceStyle,
|
|
5369
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5594
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5370
5595
|
className: "da-paper",
|
|
5596
|
+
ref: paperRef,
|
|
5371
5597
|
style: paperStyle,
|
|
5372
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5598
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5599
|
+
className: "da-figure-frame",
|
|
5373
5600
|
ref: figureRef,
|
|
5374
5601
|
style: figureStyle,
|
|
5375
|
-
children:
|
|
5602
|
+
children: figure?.kind === "svg" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5376
5603
|
ref: figureHostRef,
|
|
5377
5604
|
dangerouslySetInnerHTML: { __html: figure.markup }
|
|
5378
5605
|
}) : figure?.kind === "raster" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
@@ -5397,36 +5624,37 @@ window.__ModuleLoader__.load({
|
|
|
5397
5624
|
width: figure.width,
|
|
5398
5625
|
height: htmlHeight
|
|
5399
5626
|
}
|
|
5400
|
-
}) : null
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5627
|
+
}) : null
|
|
5628
|
+
}), paper === null || size === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Canvas, {
|
|
5629
|
+
width: paper.width,
|
|
5630
|
+
height: paper.height,
|
|
5631
|
+
origin: [-paper.x, -paper.y],
|
|
5632
|
+
marks,
|
|
5633
|
+
tool,
|
|
5634
|
+
color,
|
|
5635
|
+
weight,
|
|
5636
|
+
scale,
|
|
5637
|
+
containerRef: figureHostRef,
|
|
5638
|
+
anchoring: isVector,
|
|
5639
|
+
anchorAtFigurePoint: figure?.kind === "html" ? anchorAtFigurePoint : void 0,
|
|
5640
|
+
selectedId,
|
|
5641
|
+
onSelect: setSelectedId,
|
|
5642
|
+
onAdd: addMark,
|
|
5643
|
+
onMove: moveMark,
|
|
5644
|
+
onUpdate: updateMark,
|
|
5645
|
+
onRemove: removeMark,
|
|
5646
|
+
onPan: panBy,
|
|
5647
|
+
onPanStart: () => {
|
|
5648
|
+
setPanning(true);
|
|
5649
|
+
},
|
|
5650
|
+
onPanEnd: () => {
|
|
5651
|
+
setPanning(false);
|
|
5652
|
+
},
|
|
5653
|
+
onEdit: editMark,
|
|
5654
|
+
onDrawn: finishDrawing,
|
|
5655
|
+
onLiveEdit: setLiveMark,
|
|
5656
|
+
numbering: props.numbering
|
|
5657
|
+
})]
|
|
5430
5658
|
})
|
|
5431
5659
|
})
|
|
5432
5660
|
}),
|
|
@@ -5618,7 +5846,7 @@ window.__ModuleLoader__.load({
|
|
|
5618
5846
|
className: "da-weight-input",
|
|
5619
5847
|
"aria-label": t(selected?.kind === "point" || tool === "point" ? "pinSize" : "weight"),
|
|
5620
5848
|
min: WEIGHT_MIN,
|
|
5621
|
-
max:
|
|
5849
|
+
max: 8,
|
|
5622
5850
|
step: .1,
|
|
5623
5851
|
value: shownWeight,
|
|
5624
5852
|
onChange: (event) => {
|
|
@@ -5630,44 +5858,40 @@ window.__ModuleLoader__.load({
|
|
|
5630
5858
|
viewBox: "0 0 18 100",
|
|
5631
5859
|
preserveAspectRatio: "none",
|
|
5632
5860
|
"aria-hidden": "true",
|
|
5633
|
-
children: [
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5861
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
|
|
5862
|
+
id: `${trackId}-shade`,
|
|
5863
|
+
x1: "0",
|
|
5864
|
+
y1: "1",
|
|
5865
|
+
x2: "0",
|
|
5866
|
+
y2: "0",
|
|
5867
|
+
children: [
|
|
5868
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
5641
5869
|
offset: "0",
|
|
5642
|
-
stopColor: "rgba(20,40,80,.
|
|
5643
|
-
}),
|
|
5870
|
+
stopColor: "rgba(20,40,80,.06)"
|
|
5871
|
+
}),
|
|
5872
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
5873
|
+
offset: ".55",
|
|
5874
|
+
stopColor: "rgba(20,40,80,.13)"
|
|
5875
|
+
}),
|
|
5876
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
5644
5877
|
offset: "1",
|
|
5645
|
-
stopColor: "rgba(20,40,80,.
|
|
5646
|
-
})]
|
|
5647
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("clipPath", {
|
|
5648
|
-
id: `${trackId}-cut`,
|
|
5649
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
5650
|
-
x: "0",
|
|
5651
|
-
y: 100 - weightFraction * 100,
|
|
5652
|
-
width: "18",
|
|
5653
|
-
height: weightFraction * 100
|
|
5878
|
+
stopColor: "rgba(20,40,80,.24)"
|
|
5654
5879
|
})
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
]
|
|
5880
|
+
]
|
|
5881
|
+
}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
5882
|
+
d: WEIGHT_TRACK,
|
|
5883
|
+
fill: `url(#${trackId}-shade)`
|
|
5884
|
+
})]
|
|
5885
|
+
}),
|
|
5886
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5887
|
+
className: "da-weight-fill",
|
|
5888
|
+
"aria-hidden": true,
|
|
5889
|
+
style: { height: travelHeight(weightFraction) }
|
|
5666
5890
|
}),
|
|
5667
5891
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5668
5892
|
className: "da-weight-knob",
|
|
5669
5893
|
"aria-hidden": true,
|
|
5670
|
-
style: { bottom: `calc(
|
|
5894
|
+
style: { bottom: `calc(${travelHeight(weightFraction)} - var(--da-knob-size) / 2)` }
|
|
5671
5895
|
})
|
|
5672
5896
|
]
|
|
5673
5897
|
}),
|
|
@@ -5707,11 +5931,7 @@ window.__ModuleLoader__.load({
|
|
|
5707
5931
|
dragFrom.current = null;
|
|
5708
5932
|
setDragging(false);
|
|
5709
5933
|
},
|
|
5710
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
5711
|
-
className: "da-palette-grip-dots",
|
|
5712
|
-
"aria-hidden": true,
|
|
5713
|
-
children: "⠿"
|
|
5714
|
-
})
|
|
5934
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GripDots, { wide: true })
|
|
5715
5935
|
}), TOOLS.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5716
5936
|
type: "button",
|
|
5717
5937
|
className: "da-btn da-tool",
|
|
@@ -5765,11 +5985,7 @@ window.__ModuleLoader__.load({
|
|
|
5765
5985
|
barFrom.current = null;
|
|
5766
5986
|
setBarDragging(false);
|
|
5767
5987
|
},
|
|
5768
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
5769
|
-
className: "da-palette-grip-dots",
|
|
5770
|
-
"aria-hidden": true,
|
|
5771
|
-
children: "⠿"
|
|
5772
|
-
})
|
|
5988
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GripDots, { wide: false })
|
|
5773
5989
|
}),
|
|
5774
5990
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5775
5991
|
className: "da-bar-tools",
|
|
@@ -5796,6 +6012,7 @@ window.__ModuleLoader__.load({
|
|
|
5796
6012
|
className: "da-style-row",
|
|
5797
6013
|
role: "group",
|
|
5798
6014
|
"aria-label": t("style"),
|
|
6015
|
+
"data-side": foldOpensDown ? "below" : "above",
|
|
5799
6016
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5800
6017
|
className: "da-style-label",
|
|
5801
6018
|
children: t("styleColor")
|
package/lib/index.js
CHANGED
|
@@ -21,9 +21,9 @@ const GUIDANCE = [
|
|
|
21
21
|
"",
|
|
22
22
|
"用户可能在文档预览里直接标注(箭头/框选/椭圆/手绘/单点),发送给你的消息以【附图标注】开头,正文就是**逐条标注清单**,不需要再去读任何附加文件(标注文件是投递用的临时件,发出即删)。收到时按下列纪律处理:",
|
|
23
23
|
"",
|
|
24
|
-
"1. 逐条读消息正文的清单:每条形如 `① 箭头 (10,10) → (40,30)
|
|
24
|
+
"1. 逐条读消息正文的清单:每条形如 `① 箭头 (10,10) → (40,30),批注:标号指错了`。括号里是图面像素坐标,原点在左上角;框选与圈选给的是**左上角-右下角**,手绘线给的是它画在哪个框里。`批注:` 之后是用户写的**详细说明**——那是这条标注唯一的内容,按它判断意图。有的条目在形状后面紧跟一个圆括号,写着该处落在哪个元素上(SVG 给元素标题/`id`/元素文字,HTML 给 `selector`/元素文字),定位时优先用它。",
|
|
25
25
|
"2. **PDF 是整份文档一份标注**:每条标注都带 `第 N 页`,坐标是那一页自己的(原点在该页左上角)。看每条标注前先确认它的页码。",
|
|
26
|
-
"3.
|
|
26
|
+
"3. 若标注超出原图边界,标注图会把越界的那圈纸也画出来,并用**灰色虚线**框住原图真正的边缘:虚线**里面**是原图,**外面**是你画的标注伸出去的部分——那块不属于图,不要把它当成新增的画布内容。有形状的标注(箭头/框选/椭圆/手绘)会附一张标注图(`<图名>.annotated.png`,PDF 则是一份**带批注的 PDF**,其批注是原生 PDF 注释对象:`/Ink` 手绘、`/Square` 框选、`/Circle` 圈选、`/Line` 箭头),可直接打开核对图面位置。**若消息说「本条标注只有位置点,没有标注图」,那就是全部内容:只有坐标加一句说明,直接按原图定位处理,不要去找不存在的附件。**",
|
|
27
27
|
"4. 逐条处理标注:每条标注都要落到具体的图面改动,并在答复里逐条回应(编号与标注一致)。无法实现的标注要说明原因,不要静默跳过。",
|
|
28
28
|
"5. 修改附图以**标注所指的生成源**为准(绘制脚本、SVG 源或 `generate_patent_figure` 的入参),不要直接涂改导出的位图;PDF 同理,改的是生成该 PDF 的源头,不是那份带批注的副本。未标注的部分保持不动。",
|
|
29
29
|
"6. 改完图后重新渲染并核对:图面尺寸/坐标若变化,旧标注的坐标会失准,此时以 `anchor` 与标注文字为准复核一遍,再回报结果。",
|
|
@@ -270,7 +270,7 @@ const WEIGHT_MIN = .5;
|
|
|
270
270
|
* @returns the value, pulled to the nearest end of the range.
|
|
271
271
|
*/
|
|
272
272
|
function clampWeight(value) {
|
|
273
|
-
return Math.min(
|
|
273
|
+
return Math.min(8, Math.max(WEIGHT_MIN, value));
|
|
274
274
|
}
|
|
275
275
|
//#endregion
|
|
276
276
|
//#region src/host/store.ts
|
package/market/index.json
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-figmark",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "DSH
|
|
5
|
+
"description": "DSH \u63d2\u4ef6\uff1a\u5728\u53f3\u4fa7\u680f\u9884\u89c8\u91cc\u5bf9\u79d1\u7814\u914d\u56fe\u3001SVG\u3001PDF\u3001HTML \u76f4\u63a5\u5708\u753b\u6279\u6ce8\uff0c\u6807\u6ce8\u5199\u8fdb\u4fa7\u8f66\u6587\u4ef6\u5e76\u53ef\u4e00\u952e\u4ea4\u7ed9\u667a\u80fd\u4f53\u6539\u56fe\u6e90",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"icon": "./icon.svg",
|
|
8
8
|
"keywords": [
|