dsh-figmark 1.1.1 → 1.1.2

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 CHANGED
@@ -1083,6 +1083,20 @@ window.__ModuleLoader__.load({
1083
1083
  * every colour the palette offers.
1084
1084
  */
1085
1085
  const PIN_NUMBER_COLOR = "#ffffff";
1086
+ /**
1087
+ * Font size of the number inside a pin, in figure pixels.
1088
+ *
1089
+ * Scaled by the mark's weight as well as the figure: the rail that makes a pin bigger
1090
+ * makes its head bigger, and a head that grows around a fixed-size numeral reads as a
1091
+ * circle with a speck in it. The number is the pin's whole payload, so it keeps the
1092
+ * same share of the head at every size.
1093
+ * @param mark - the point mark whose pin is being drawn.
1094
+ * @param unit - the figure's size multiple.
1095
+ * @returns the font size to draw the number at.
1096
+ */
1097
+ function pinFontSizeOf(mark, unit = 1) {
1098
+ return 11 * (mark.weight ?? 1) * unit;
1099
+ }
1086
1100
  /** Normalize two opposite corners into a top-left origin and positive extents. */
1087
1101
  function bounds(first, second) {
1088
1102
  return {
@@ -1867,7 +1881,7 @@ window.__ModuleLoader__.load({
1867
1881
  x: centre[0],
1868
1882
  y: centre[1],
1869
1883
  fontFamily: PIN_FONT_FAMILY,
1870
- fontSize: 11 * unit,
1884
+ fontSize: pinFontSizeOf(mark, unit),
1871
1885
  fontWeight: 700,
1872
1886
  fill: PIN_NUMBER_COLOR,
1873
1887
  textAnchor: "middle",
@@ -2631,7 +2645,7 @@ window.__ModuleLoader__.load({
2631
2645
  const centre = mark.kind === "point" ? pinNumberCentre(mark, unit) : void 0;
2632
2646
  if (pin !== void 0 && centre !== void 0) {
2633
2647
  parts.push(`<path d="${pin}" fill="${mark.color}" stroke="#ffffff" stroke-width="${2 * unit}" stroke-linejoin="round"/>`);
2634
- if (number !== void 0) parts.push(`<text x="${centre[0]}" y="${centre[1]}" font-family="${PIN_FONT_FAMILY}" font-size="${11 * unit}" font-weight="700" fill="${PIN_NUMBER_COLOR}" text-anchor="middle" dominant-baseline="central">${number}</text>`);
2648
+ if (number !== void 0) parts.push(`<text x="${centre[0]}" y="${centre[1]}" font-family="${PIN_FONT_FAMILY}" font-size="${pinFontSizeOf(mark, unit)}" font-weight="700" fill="${PIN_NUMBER_COLOR}" text-anchor="middle" dominant-baseline="central">${number}</text>`);
2635
2649
  }
2636
2650
  return parts.join("");
2637
2651
  }
@@ -3292,33 +3306,25 @@ window.__ModuleLoader__.load({
3292
3306
  cursor:ns-resize;transition:width .16s ease,background .16s ease,box-shadow .16s ease}
3293
3307
  .da-weight:hover,.da-weight:focus-within{width:30px;background:var(--dsw-alias-bg-layer-2,#fff);
3294
3308
  box-shadow:0 5px 18px rgba(20,40,80,.16),0 0 0 0.5px var(--dsw-alias-border-l2,currentColor)}
3295
- /* The wedge: a hairline at the bottom, the heaviest width at the top. Drawn as a
3296
- clipped gradient box so it costs one element and no SVG. */
3297
- .da-weight::before{content:"";position:absolute;left:50%;top:14px;bottom:14px;width:11px;
3298
- transform:translateX(-50%);opacity:0;
3299
- background:linear-gradient(to top,var(--dsw-alias-border-l3,rgba(127,127,127,.34)),
3300
- var(--dsw-alias-border-l4,rgba(127,127,127,.5)));
3301
- clip-path:polygon(calc(50% - 0.5px) 100%,calc(50% + 0.5px) 100%,
3302
- calc(50% + 5.5px) 0,calc(50% - 5.5px) 0);
3303
- transition:opacity .16s ease}
3304
- .da-weight:hover::before,.da-weight:focus-within::before{opacity:1}
3305
- /* The part already chosen, drawn from the bottom up in the brand colour. */
3306
- .da-weight-fill{position:absolute;left:50%;bottom:14px;width:11px;transform:translateX(-50%);
3307
- opacity:0;pointer-events:none;transition:opacity .16s ease;
3308
- background:var(--dsw-alias-brand-primary,#176df2);
3309
- clip-path:polygon(calc(50% - 0.5px) 100%,calc(50% + 0.5px) 100%,
3310
- calc(50% + 5.5px) 0,calc(50% - 5.5px) 0)}
3311
- .da-weight:hover .da-weight-fill,.da-weight:focus-within .da-weight-fill{opacity:1}
3309
+ /* The track is an svg in the body: a hairline at the bottom that opens into a rounded
3310
+ shoulder at the top. It is not a clip-path polygon because a polygon has no rounded
3311
+ 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 12x100 shape over the
3313
+ rail's height, so the shoulders stay smooth at any length. */
3314
+ .da-weight-svg{position:absolute;left:50%;top:12px;bottom:12px;width:18px;
3315
+ transform:translateX(-50%);opacity:0;pointer-events:none;transition:opacity .16s ease}
3316
+ .da-weight:hover .da-weight-svg,.da-weight:focus-within .da-weight-svg{opacity:1}
3312
3317
  /* The knob sits on the value: the only part that shows at rest, so the rail reads as
3313
3318
  a slider waiting to be touched rather than a bare rule. */
3314
- .da-weight-knob{position:absolute;left:50%;width:11px;height:11px;border-radius:50%;
3315
- transform:translateX(-50%);pointer-events:none;
3319
+ .da-weight-knob{position:absolute;left:50%;width:var(--da-knob-size,11px);height:var(--da-knob-size,11px);
3320
+ border-radius:50%;transform:translateX(-50%);pointer-events:none;
3316
3321
  background:var(--dsw-alias-bg-layer-2,#fff);
3317
- box-shadow:0 1px 4px rgba(20,40,80,.32),0 0 0 1px var(--dsw-alias-border-l3,currentColor);
3322
+ box-shadow:0 1px 4px rgba(20,40,80,.28),0 0 0 1px var(--dsw-alias-border-l3,currentColor);
3318
3323
  transition:width .16s ease,height .16s ease,background .16s ease}
3324
+ .da-weight:hover,.da-weight:focus-within{--da-knob-size:15px}
3319
3325
  .da-weight:hover .da-weight-knob,.da-weight:focus-within .da-weight-knob{
3320
- width:15px;height:15px;background:var(--dsw-alias-brand-primary,#176df2);
3321
- box-shadow:0 2px 7px rgba(20,40,80,.34)}
3326
+ background:var(--dsw-alias-brand-primary,#176df2);
3327
+ box-shadow:0 2px 7px rgba(20,40,80,.3)}
3322
3328
  /* The browser's own range, rotated upright and made transparent. It spans the whole
3323
3329
  rail so a press anywhere on it lands on the slider — including on the knob, which
3324
3330
  is why the knob is pointer-transparent. */
@@ -3491,7 +3497,7 @@ window.__ModuleLoader__.load({
3491
3497
  come from the mark it belongs to. It is a pill rather than a card — no head, no
3492
3498
  border between the field and the panel — so writing about a mark costs one line
3493
3499
  over the figure instead of a box that covers it. */
3494
- .da-float-detail{width:300px;gap:0;padding:4px;border-radius:22px;
3500
+ .da-float-detail{width:300px;max-width:calc(100% - 16px);gap:0;padding:4px;border-radius:22px;
3495
3501
  flex-direction:row;align-items:flex-end;
3496
3502
  box-shadow:0 8px 26px rgba(20,40,80,.2),0 0 0 1px rgba(20,40,80,.04)}
3497
3503
  .da-float .da-comment{flex:1 1 auto;min-width:0;box-sizing:border-box;border:0;background:transparent;
@@ -3814,6 +3820,27 @@ window.__ModuleLoader__.load({
3814
3820
  "#2f9e44"
3815
3821
  ];
3816
3822
  /** Tools in toolbar order. */
3823
+ /**
3824
+ * The rail's track, drawn on an 18x100 grid and stretched to whatever height it gets.
3825
+ *
3826
+ * A hairline at the bottom opening into a round shoulder at the top: the control's
3827
+ * subject is thickness, so the track itself says which way is heavier. Every edge is
3828
+ * curved — straight flanks make a paper dart, and a square cap makes a cut-off blade —
3829
+ * so the profile is eased and the top is closed with an arc a little wider than the
3830
+ * shoulders. Stretching it vertically (preserveAspectRatio="none") costs nothing
3831
+ * visually: the only vertical features are straight lines, and the horizontal ones are
3832
+ * what the gradient is for.
3833
+ */
3834
+ const WEIGHT_TRACK = [
3835
+ "M 8.2 100",
3836
+ "C 8.2 78 7.9 58 6.4 38",
3837
+ "C 5.4 25 3.2 16 1.9 11",
3838
+ "A 7.1 7.1 0 0 1 16.1 11",
3839
+ "C 14.8 16 12.6 25 11.6 38",
3840
+ "C 10.1 58 9.8 78 9.8 100",
3841
+ "A 0.8 0.8 0 0 1 8.2 100",
3842
+ "Z"
3843
+ ].join(" ");
3817
3844
  const TOOLS = [
3818
3845
  {
3819
3846
  tool: "select",
@@ -4063,9 +4090,27 @@ window.__ModuleLoader__.load({
4063
4090
  const isVector = mediaType === "image/svg+xml";
4064
4091
  const t = (0, react.useCallback)((key) => resolveTranslate(props, key), [props.t, props.localeId]);
4065
4092
  const sessionId = props.sessionId ?? parsedAddress?.sessionId;
4093
+ /**
4094
+ * A private prefix for the rail's gradient and clip ids.
4095
+ *
4096
+ * Two bodies can be mounted at once (a PDF lays out one per page), and an id is
4097
+ * document-wide: sharing one would make every rail's fill follow the first rail's
4098
+ * selection. Declared here, with the other hooks, because the ids are read far below
4099
+ * the early returns for a figure that failed to load.
4100
+ */
4101
+ const trackId = `da-weight-${(0, react.useId)().replaceAll(":", "")}`;
4066
4102
  const stageRef = (0, react.useRef)(null);
4067
4103
  /** The box a double clicked mark's detail is written in. */
4068
4104
  const detailRef = (0, react.useRef)(null);
4105
+ /**
4106
+ * The comment pill around that box.
4107
+ *
4108
+ * Measured rather than the textarea inside it: the field gives up width to the two
4109
+ * hand-off buttons as soon as there is something to send, so a placement computed
4110
+ * from the field's box would put the buttons past the edge of the panel — which is
4111
+ * exactly what a comment pill against the right edge used to do.
4112
+ */
4113
+ const pillRef = (0, react.useRef)(null);
4069
4114
  const barRef = (0, react.useRef)(null);
4070
4115
  const figureHostRef = (0, react.useRef)(null);
4071
4116
  const surfaceRef = (0, react.useRef)(null);
@@ -4653,7 +4698,7 @@ window.__ModuleLoader__.load({
4653
4698
  }
4654
4699
  const panel = root.getBoundingClientRect();
4655
4700
  const measured = host.getBoundingClientRect();
4656
- const pillSize = measuredSize(detailRef.current, {
4701
+ const pillSize = measuredSize(pillRef.current, {
4657
4702
  width: 300,
4658
4703
  height: 48
4659
4704
  });
@@ -5231,6 +5276,13 @@ window.__ModuleLoader__.load({
5231
5276
  */
5232
5277
  const shownWeight = selected === null ? WEIGHT_TOOLS.has(tool) ? weight : null : selected.weight ?? 1;
5233
5278
  /**
5279
+ * How far up the rail that value sits, from 0 at the lightest to 1 at the heaviest.
5280
+ *
5281
+ * One number for the fill, the knob and the input: computed three times the three
5282
+ * could round differently and the knob would sit a pixel off the fill it belongs to.
5283
+ */
5284
+ const weightFraction = shownWeight === null ? 0 : (shownWeight - WEIGHT_MIN) / (4 - WEIGHT_MIN);
5285
+ /**
5234
5286
  * Change one attribute of the selected mark, or of the next stroke when nothing
5235
5287
  * is selected.
5236
5288
  *
@@ -5518,6 +5570,7 @@ window.__ModuleLoader__.load({
5518
5570
  className: "da-float da-float-detail",
5519
5571
  role: "group",
5520
5572
  "aria-label": t("detail"),
5573
+ ref: pillRef,
5521
5574
  style: {
5522
5575
  left: detailAt.left,
5523
5576
  top: detailAt.top
@@ -5572,15 +5625,49 @@ window.__ModuleLoader__.load({
5572
5625
  chooseWeight(Number(event.target.value));
5573
5626
  }
5574
5627
  }),
5575
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5576
- className: "da-weight-fill",
5577
- "aria-hidden": true,
5578
- style: { height: `${(shownWeight - WEIGHT_MIN) / (4 - WEIGHT_MIN) * 100}%` }
5628
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
5629
+ className: "da-weight-svg",
5630
+ viewBox: "0 0 18 100",
5631
+ preserveAspectRatio: "none",
5632
+ "aria-hidden": "true",
5633
+ children: [
5634
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("defs", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
5635
+ id: `${trackId}-shade`,
5636
+ x1: "0",
5637
+ y1: "1",
5638
+ x2: "0",
5639
+ y2: "0",
5640
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
5641
+ offset: "0",
5642
+ stopColor: "rgba(20,40,80,.05)"
5643
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
5644
+ offset: "1",
5645
+ stopColor: "rgba(20,40,80,.2)"
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
5654
+ })
5655
+ })] }),
5656
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
5657
+ d: WEIGHT_TRACK,
5658
+ fill: `url(#${trackId}-shade)`
5659
+ }),
5660
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
5661
+ d: WEIGHT_TRACK,
5662
+ fill: "var(--dsw-alias-brand-primary,#176df2)",
5663
+ clipPath: `url(#${trackId}-cut)`
5664
+ })
5665
+ ]
5579
5666
  }),
5580
5667
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5581
5668
  className: "da-weight-knob",
5582
5669
  "aria-hidden": true,
5583
- style: { bottom: `calc(${(shownWeight - WEIGHT_MIN) / (4 - WEIGHT_MIN) * 100}% - 5.5px)` }
5670
+ style: { bottom: `calc(12px + ${weightFraction} * (100% - 24px - var(--da-knob-size, 11px)))` }
5584
5671
  })
5585
5672
  ]
5586
5673
  }),
package/market/index.json CHANGED
@@ -93,8 +93,8 @@
93
93
  },
94
94
  "versions": [
95
95
  {
96
- "version": "1.1.1",
97
- "publishTimestamp": 1792729486,
96
+ "version": "1.1.2",
97
+ "publishTimestamp": 1792731605,
98
98
  "source": {
99
99
  "type": "npm",
100
100
  "registry": "https://registry.npmjs.org"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-figmark",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "private": false,
5
5
  "description": "DSH 插件:在右侧栏预览里对科研配图、SVG、PDF、HTML 直接圈画批注,标注写进侧车文件并可一键交给智能体改图源",
6
6
  "license": "MIT",