copper3d 3.4.5 → 3.4.7
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/dist/Utils/segmentation/DrawToolCore.d.ts +19 -2
- package/dist/Utils/segmentation/DrawToolCore.js +58 -13
- package/dist/Utils/segmentation/DrawToolCore.js.map +1 -1
- package/dist/Utils/segmentation/NrrdTools.d.ts +0 -1
- package/dist/Utils/segmentation/NrrdTools.js +2 -7
- package/dist/Utils/segmentation/NrrdTools.js.map +1 -1
- package/dist/Utils/segmentation/core/types.d.ts +0 -1
- package/dist/Utils/segmentation/core/types.js.map +1 -1
- package/dist/Utils/segmentation/coreTools/gui.d.ts +0 -1
- package/dist/Utils/segmentation/coreTools/gui.js +21 -29
- package/dist/Utils/segmentation/coreTools/gui.js.map +1 -1
- package/dist/Utils/segmentation/tools/DrawingTool.d.ts +18 -2
- package/dist/Utils/segmentation/tools/DrawingTool.js +60 -21
- package/dist/Utils/segmentation/tools/DrawingTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/ToolHost.d.ts +2 -3
- package/dist/Utils/utils.d.ts +0 -1
- package/dist/Utils/utils.js +0 -80
- package/dist/Utils/utils.js.map +1 -1
- package/dist/bundle.esm.js +141 -149
- package/dist/bundle.umd.js +141 -149
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types/Utils/segmentation/DrawToolCore.d.ts +19 -2
- package/dist/types/Utils/segmentation/NrrdTools.d.ts +0 -1
- package/dist/types/Utils/segmentation/core/types.d.ts +0 -1
- package/dist/types/Utils/segmentation/coreTools/gui.d.ts +0 -1
- package/dist/types/Utils/segmentation/tools/DrawingTool.d.ts +18 -2
- package/dist/types/Utils/segmentation/tools/ToolHost.d.ts +2 -3
- package/dist/types/Utils/utils.d.ts +0 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/bundle.umd.js
CHANGED
|
@@ -40614,86 +40614,6 @@ void main() {
|
|
|
40614
40614
|
}
|
|
40615
40615
|
return url;
|
|
40616
40616
|
}
|
|
40617
|
-
function switchEraserSize(size, urls) {
|
|
40618
|
-
let url = "";
|
|
40619
|
-
if (!!urls && urls.length > 0) {
|
|
40620
|
-
if (size <= 3) {
|
|
40621
|
-
url = `url(${urls[0]}) 3 3, crosshair`;
|
|
40622
|
-
}
|
|
40623
|
-
else if (3 < size && size <= 8) {
|
|
40624
|
-
url = `url(${urls[1]}) 8 8, crosshair`;
|
|
40625
|
-
}
|
|
40626
|
-
else if (8 < size && size <= 13) {
|
|
40627
|
-
url = `url(${urls[2]}) 13 13, crosshair`;
|
|
40628
|
-
}
|
|
40629
|
-
else if (13 < size && size <= 18) {
|
|
40630
|
-
url = `url(${urls[3]}) 18 18, crosshair`;
|
|
40631
|
-
}
|
|
40632
|
-
else if (18 < size && size <= 23) {
|
|
40633
|
-
url = `url(${urls[4]}) 23 23, crosshair`;
|
|
40634
|
-
}
|
|
40635
|
-
else if (23 < size && size <= 28) {
|
|
40636
|
-
url = `url(${urls[5]}) 28 28, crosshair`;
|
|
40637
|
-
}
|
|
40638
|
-
else if (28 < size && size <= 33) {
|
|
40639
|
-
url = `url(${urls[6]}) 33 33, crosshair`;
|
|
40640
|
-
}
|
|
40641
|
-
else if (33 < size && size <= 38) {
|
|
40642
|
-
url = `url(${urls[7]}) 38 38, crosshair`;
|
|
40643
|
-
}
|
|
40644
|
-
else if (38 < size && size <= 43) {
|
|
40645
|
-
url = `url(${urls[8]}) 43 43, crosshair`;
|
|
40646
|
-
}
|
|
40647
|
-
else if (43 < size && size <= 48) {
|
|
40648
|
-
url = `url(${urls[9]}) 48 48, crosshair`;
|
|
40649
|
-
}
|
|
40650
|
-
else if (48 < size && size <= 53) {
|
|
40651
|
-
url = `url(${urls[10]}) 52 52, crosshair`;
|
|
40652
|
-
}
|
|
40653
|
-
else {
|
|
40654
|
-
url = `url(${urls[11]}) 52 52, crosshair`;
|
|
40655
|
-
}
|
|
40656
|
-
}
|
|
40657
|
-
else {
|
|
40658
|
-
if (size <= 3) {
|
|
40659
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_3.png) 3 3, crosshair`;
|
|
40660
|
-
}
|
|
40661
|
-
else if (3 < size && size <= 8) {
|
|
40662
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_8.png) 8 8, crosshair`;
|
|
40663
|
-
}
|
|
40664
|
-
else if (8 < size && size <= 13) {
|
|
40665
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_13.png) 13 13, crosshair`;
|
|
40666
|
-
}
|
|
40667
|
-
else if (13 < size && size <= 18) {
|
|
40668
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_18.png) 18 18, crosshair`;
|
|
40669
|
-
}
|
|
40670
|
-
else if (18 < size && size <= 23) {
|
|
40671
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_23.png) 23 23, crosshair`;
|
|
40672
|
-
}
|
|
40673
|
-
else if (23 < size && size <= 28) {
|
|
40674
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_28.png) 28 28, crosshair`;
|
|
40675
|
-
}
|
|
40676
|
-
else if (28 < size && size <= 33) {
|
|
40677
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_33.png) 33 33, crosshair`;
|
|
40678
|
-
}
|
|
40679
|
-
else if (33 < size && size <= 38) {
|
|
40680
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_38.png) 38 38, crosshair`;
|
|
40681
|
-
}
|
|
40682
|
-
else if (38 < size && size <= 43) {
|
|
40683
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_43.png) 43 43, crosshair`;
|
|
40684
|
-
}
|
|
40685
|
-
else if (43 < size && size <= 48) {
|
|
40686
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_48.png) 48 48, crosshair`;
|
|
40687
|
-
}
|
|
40688
|
-
else if (48 < size && size <= 53) {
|
|
40689
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_52.png) 52 52, crosshair`;
|
|
40690
|
-
}
|
|
40691
|
-
else {
|
|
40692
|
-
url = `url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_52.png) 52 52, crosshair`;
|
|
40693
|
-
}
|
|
40694
|
-
}
|
|
40695
|
-
return url;
|
|
40696
|
-
}
|
|
40697
40617
|
function throttle(callback, wait) {
|
|
40698
40618
|
let start = 0;
|
|
40699
40619
|
return function (event) {
|
|
@@ -73010,10 +72930,7 @@ void main() {
|
|
|
73010
72930
|
.name("BrushAndEraserSize")
|
|
73011
72931
|
.min(5)
|
|
73012
72932
|
.max(50)
|
|
73013
|
-
.step(1)
|
|
73014
|
-
.onChange(() => {
|
|
73015
|
-
updateGuiBrushAndEraserSize();
|
|
73016
|
-
});
|
|
72933
|
+
.step(1);
|
|
73017
72934
|
actionsFolder.add(configs.gui_states.mode, "eraser").onChange((value) => {
|
|
73018
72935
|
updateGuiEraserState();
|
|
73019
72936
|
});
|
|
@@ -73141,37 +73058,33 @@ void main() {
|
|
|
73141
73058
|
configs.gui_states.viewConfig.readyToUpdate = true;
|
|
73142
73059
|
});
|
|
73143
73060
|
actionsFolder.open();
|
|
73144
|
-
|
|
73145
|
-
|
|
73146
|
-
|
|
73147
|
-
|
|
73148
|
-
|
|
73149
|
-
|
|
73150
|
-
|
|
73151
|
-
const updatePencilState = () => {
|
|
73152
|
-
if (configs.gui_states.mode.pencil) {
|
|
73153
|
-
// add canvas brush circle move event listeners
|
|
73154
|
-
configs.drawingCanvas.removeEventListener("mouseover", configs.drawingPrameters.handleOnDrawingBrushCricleMove);
|
|
73155
|
-
configs.drawingCanvas.removeEventListener("mouseout", configs.drawingPrameters.handleOnDrawingBrushCricleMove);
|
|
73061
|
+
// Add/remove the hover-tracking listeners that feed the brush/eraser preview
|
|
73062
|
+
// ring (mouseOver position). Present for brush & eraser, removed for pencil.
|
|
73063
|
+
const setBrushHoverTracking = (enabled) => {
|
|
73064
|
+
const handler = configs.drawingPrameters.handleOnDrawingBrushCricleMove;
|
|
73065
|
+
if (enabled) {
|
|
73066
|
+
configs.drawingCanvas.addEventListener("mouseover", handler);
|
|
73067
|
+
configs.drawingCanvas.addEventListener("mouseout", handler);
|
|
73156
73068
|
}
|
|
73157
73069
|
else {
|
|
73158
|
-
|
|
73159
|
-
configs.drawingCanvas.
|
|
73160
|
-
configs.drawingCanvas.addEventListener("mouseout", configs.drawingPrameters.handleOnDrawingBrushCricleMove);
|
|
73070
|
+
configs.drawingCanvas.removeEventListener("mouseover", handler);
|
|
73071
|
+
configs.drawingCanvas.removeEventListener("mouseout", handler);
|
|
73161
73072
|
}
|
|
73073
|
+
};
|
|
73074
|
+
const updatePencilState = () => {
|
|
73075
|
+
// Pencil has no preview ring; brush keeps the hover-tracking listeners.
|
|
73076
|
+
setBrushHoverTracking(!configs.gui_states.mode.pencil);
|
|
73162
73077
|
configs.drawingCanvas.style.cursor = configs.gui_states.viewConfig.defaultPaintCursor;
|
|
73163
73078
|
};
|
|
73164
73079
|
const updateGuiEraserState = () => {
|
|
73165
|
-
//
|
|
73080
|
+
// The eraser shows a dashed preview ring (rendered in DrawToolCore.start),
|
|
73081
|
+
// so it needs the same hover-tracking as the brush. The canvas keeps the
|
|
73082
|
+
// default paint cursor; the ring conveys size (resize via Shift+wheel).
|
|
73166
73083
|
if (configs.gui_states.mode.eraser) {
|
|
73167
|
-
|
|
73168
|
-
? (configs.drawingCanvas.style.cursor = switchEraserSize(configs.gui_states.drawing.brushAndEraserSize, configs.eraserUrls))
|
|
73169
|
-
: (configs.drawingCanvas.style.cursor = switchEraserSize(configs.gui_states.drawing.brushAndEraserSize));
|
|
73170
|
-
}
|
|
73171
|
-
else {
|
|
73172
|
-
configs.drawingCanvas.style.cursor =
|
|
73173
|
-
configs.gui_states.viewConfig.defaultPaintCursor;
|
|
73084
|
+
setBrushHoverTracking(true);
|
|
73174
73085
|
}
|
|
73086
|
+
configs.drawingCanvas.style.cursor =
|
|
73087
|
+
configs.gui_states.viewConfig.defaultPaintCursor;
|
|
73175
73088
|
};
|
|
73176
73089
|
const updateGuiSphereState = () => {
|
|
73177
73090
|
var _a, _b, _c, _d;
|
|
@@ -73227,7 +73140,6 @@ void main() {
|
|
|
73227
73140
|
min: 5,
|
|
73228
73141
|
max: 50,
|
|
73229
73142
|
step: 1,
|
|
73230
|
-
onChange: updateGuiBrushAndEraserSize,
|
|
73231
73143
|
},
|
|
73232
73144
|
eraser: {
|
|
73233
73145
|
name: "Eraser",
|
|
@@ -74956,18 +74868,11 @@ void main() {
|
|
|
74956
74868
|
this.drawingLines = [];
|
|
74957
74869
|
this.isPainting = true;
|
|
74958
74870
|
this.ctx.protectedData.isDrawing = true;
|
|
74959
|
-
//
|
|
74960
|
-
|
|
74961
|
-
|
|
74962
|
-
|
|
74963
|
-
|
|
74964
|
-
? switchEraserSize(this.ctx.gui_states.drawing.brushAndEraserSize, urls)
|
|
74965
|
-
: switchEraserSize(this.ctx.gui_states.drawing.brushAndEraserSize);
|
|
74966
|
-
}
|
|
74967
|
-
else {
|
|
74968
|
-
this.ctx.protectedData.canvases.drawingCanvas.style.cursor =
|
|
74969
|
-
this.ctx.gui_states.viewConfig.defaultPaintCursor;
|
|
74970
|
-
}
|
|
74871
|
+
// Brush and eraser both show their size via the dashed/solid preview ring
|
|
74872
|
+
// rendered in DrawToolCore.start(); the canvas keeps the default paint
|
|
74873
|
+
// cursor underneath. (The eraser previously swapped in a sized PNG cursor.)
|
|
74874
|
+
this.ctx.protectedData.canvases.drawingCanvas.style.cursor =
|
|
74875
|
+
this.ctx.gui_states.viewConfig.defaultPaintCursor;
|
|
74971
74876
|
// Record draw start position
|
|
74972
74877
|
this.ctx.nrrd_states.interaction.drawStartPos.x = e.offsetX;
|
|
74973
74878
|
this.ctx.nrrd_states.interaction.drawStartPos.y = e.offsetY;
|
|
@@ -75043,9 +74948,15 @@ void main() {
|
|
|
75043
74948
|
this.refreshLayerFromVolume();
|
|
75044
74949
|
}
|
|
75045
74950
|
else {
|
|
75046
|
-
// Brush mode: voxels already written
|
|
75047
|
-
//
|
|
74951
|
+
// Brush mode: voxels already written directly into the MaskVolume
|
|
74952
|
+
// during mousemove, so no canvas→voxel bake (syncLayerSliceData) is
|
|
74953
|
+
// needed. But we MUST still notify the backend — unlike pencil/eraser
|
|
74954
|
+
// (which go through syncLayerSliceData) the brush path never fires
|
|
74955
|
+
// onMaskChanged, so brush strokes were silently never persisted via
|
|
74956
|
+
// useReplaceMask. Re-render the final state, then fire onMaskChanged
|
|
74957
|
+
// for the current slice (same pattern as SphereBrushTool.refreshDisplay).
|
|
75048
74958
|
this.refreshLayerFromVolume();
|
|
74959
|
+
this.notifyMaskChanged();
|
|
75049
74960
|
}
|
|
75050
74961
|
}
|
|
75051
74962
|
else {
|
|
@@ -75099,20 +75010,37 @@ void main() {
|
|
|
75099
75010
|
return handler;
|
|
75100
75011
|
}
|
|
75101
75012
|
/**
|
|
75102
|
-
* Render brush
|
|
75013
|
+
* Render the size-preview ring for the brush / eraser on the drawing context.
|
|
75014
|
+
*
|
|
75015
|
+
* - Brush: solid outline in the current brush colour.
|
|
75016
|
+
* - Eraser: red dashed outline (matches the sphereEraser preview), so the
|
|
75017
|
+
* eraser no longer needs a sized PNG cursor — the ring tracks the live
|
|
75018
|
+
* `brushAndEraserSize` and is what the user resizes with Shift+wheel.
|
|
75019
|
+
*
|
|
75020
|
+
* Only the pencil has no preview ring (its stroke width is fixed).
|
|
75103
75021
|
*/
|
|
75104
|
-
renderBrushPreview(ctx,
|
|
75022
|
+
renderBrushPreview(ctx, _width, _height) {
|
|
75105
75023
|
if (this.ctx.gui_states.mode.pencil ||
|
|
75106
|
-
this.ctx.gui_states.mode.eraser ||
|
|
75107
75024
|
!this.ctx.nrrd_states.interaction.mouseOver) {
|
|
75108
75025
|
return;
|
|
75109
75026
|
}
|
|
75110
|
-
|
|
75111
|
-
|
|
75027
|
+
const isEraser = this.ctx.gui_states.mode.eraser;
|
|
75028
|
+
// No clearRect here: DrawToolCore.start() clears drawingCtx once per frame
|
|
75029
|
+
// before compositing, and this ring is now drawn on top of the mask so it
|
|
75030
|
+
// stays visible during an active stroke (e.g. while Shift+wheel resizing).
|
|
75031
|
+
ctx.save();
|
|
75112
75032
|
ctx.beginPath();
|
|
75113
75033
|
ctx.arc(this.ctx.nrrd_states.interaction.mouseOverX, this.ctx.nrrd_states.interaction.mouseOverY, this.ctx.gui_states.drawing.brushAndEraserSize / 2 + 1, 0, Math.PI * 2);
|
|
75114
|
-
|
|
75034
|
+
if (isEraser) {
|
|
75035
|
+
ctx.strokeStyle = "#ff4444";
|
|
75036
|
+
ctx.lineWidth = 2;
|
|
75037
|
+
ctx.setLineDash([4, 4]);
|
|
75038
|
+
}
|
|
75039
|
+
else {
|
|
75040
|
+
ctx.strokeStyle = this.ctx.gui_states.drawing.brushColor;
|
|
75041
|
+
}
|
|
75115
75042
|
ctx.stroke();
|
|
75043
|
+
ctx.restore();
|
|
75116
75044
|
}
|
|
75117
75045
|
// ── Brush mode: direct voxel write ────────────────────────────
|
|
75118
75046
|
/**
|
|
@@ -75303,6 +75231,30 @@ void main() {
|
|
|
75303
75231
|
this.callbacks.compositeAllLayers();
|
|
75304
75232
|
this.ctx.protectedData.mainPreSlices.mesh.material.map.needsUpdate = true;
|
|
75305
75233
|
}
|
|
75234
|
+
/**
|
|
75235
|
+
* Notify the backend of the brush stroke by firing onMaskChanged for the
|
|
75236
|
+
* current slice. Brush writes voxels straight into the MaskVolume (no canvas
|
|
75237
|
+
* bake), so we read the authoritative slice back from the volume — mirrors
|
|
75238
|
+
* SphereBrushTool.refreshDisplay's notify path. Brush only ever touches the
|
|
75239
|
+
* current view slice (canvasToVoxel3D fixes the slice axis), so a single
|
|
75240
|
+
* slice notification is sufficient.
|
|
75241
|
+
*/
|
|
75242
|
+
notifyMaskChanged() {
|
|
75243
|
+
if (this.ctx.nrrd_states.flags.loadingMaskData)
|
|
75244
|
+
return;
|
|
75245
|
+
try {
|
|
75246
|
+
const layer = this.ctx.gui_states.layerChannel.layer;
|
|
75247
|
+
const vol = this.callbacks.getVolumeForLayer(layer);
|
|
75248
|
+
const channel = this.ctx.gui_states.layerChannel.activeChannel || 1;
|
|
75249
|
+
const axis = this.ctx.protectedData.axis;
|
|
75250
|
+
const sliceIndex = this.ctx.nrrd_states.view.currentSliceIndex;
|
|
75251
|
+
const { data: sliceData, width, height } = vol.getSliceUint8(sliceIndex, axis);
|
|
75252
|
+
this.ctx.callbacks.onMaskChanged(sliceData, layer, channel, sliceIndex, axis, width, height, false);
|
|
75253
|
+
}
|
|
75254
|
+
catch (_a) {
|
|
75255
|
+
// Volume not ready — skip notification
|
|
75256
|
+
}
|
|
75257
|
+
}
|
|
75306
75258
|
// ── Private helpers ────────────────────────────────────────
|
|
75307
75259
|
/** Capture pre-draw slice snapshot for undo */
|
|
75308
75260
|
capturePreDrawSnapshot() {
|
|
@@ -76229,7 +76181,6 @@ void main() {
|
|
|
76229
76181
|
handleOnContrastMouseUp: (ev) => { },
|
|
76230
76182
|
handleOnContrastMouseLeave: (ev) => { },
|
|
76231
76183
|
};
|
|
76232
|
-
this.eraserUrls = [];
|
|
76233
76184
|
this.pencilUrls = [];
|
|
76234
76185
|
this.undoManager = new UndoManager();
|
|
76235
76186
|
/** Slice index recorded when paintOnCanvas() starts, guards stale-click */
|
|
@@ -76304,7 +76255,6 @@ void main() {
|
|
|
76304
76255
|
filterDrawedImage: (axis, index) => this.renderer.filterDrawedImage(axis, index),
|
|
76305
76256
|
getVolumeForLayer: (layer) => this.renderer.getVolumeForLayer(layer),
|
|
76306
76257
|
pushUndoDelta: (delta) => this.undoManager.push(delta),
|
|
76307
|
-
getEraserUrls: () => this.eraserUrls,
|
|
76308
76258
|
renderSliceToCanvas: (layer, axis, sliceIndex, buffer, ctx, w, h) => this.renderer.renderSliceToCanvas(layer, axis, sliceIndex, buffer, ctx, w, h),
|
|
76309
76259
|
getOrCreateSliceBuffer: (axis) => this.renderer.getOrCreateSliceBuffer(axis),
|
|
76310
76260
|
});
|
|
@@ -76316,7 +76266,6 @@ void main() {
|
|
|
76316
76266
|
getOrCreateSliceBuffer: (axis) => this.renderer.getOrCreateSliceBuffer(axis),
|
|
76317
76267
|
setEmptyCanvasSize: (axis) => this.setEmptyCanvasSize(axis),
|
|
76318
76268
|
reloadMasksFromVolume: () => this.reloadMasksFromVolume(),
|
|
76319
|
-
getEraserUrls: () => this.eraserUrls,
|
|
76320
76269
|
});
|
|
76321
76270
|
}
|
|
76322
76271
|
initDrawToolCore() {
|
|
@@ -76471,6 +76420,12 @@ void main() {
|
|
|
76471
76420
|
});
|
|
76472
76421
|
// Register wheel handler with EventRouter
|
|
76473
76422
|
this.eventRouter.setWheelHandler((e) => {
|
|
76423
|
+
// Shift + wheel resizes the brush/eraser (like sphereBrush/sphereEraser
|
|
76424
|
+
// radius), independent of whether the left button is held. Consumes the
|
|
76425
|
+
// event so it never falls through to zoom/slice. Pencil is excluded even
|
|
76426
|
+
// though it also uses Shift — see tryBrushEraserResizeWheel.
|
|
76427
|
+
if (this.tryBrushEraserResizeWheel(e))
|
|
76428
|
+
return;
|
|
76474
76429
|
if (this.activeWheelMode === 'zoom') {
|
|
76475
76430
|
this.drawingPrameters.handleMouseZoomSliceWheel(e);
|
|
76476
76431
|
}
|
|
@@ -76484,8 +76439,47 @@ void main() {
|
|
|
76484
76439
|
// Bind all event listeners
|
|
76485
76440
|
this.eventRouter.bindAll();
|
|
76486
76441
|
}
|
|
76487
|
-
|
|
76488
|
-
|
|
76442
|
+
/**
|
|
76443
|
+
* Shift + mouse-wheel resize for the brush and eraser tools.
|
|
76444
|
+
*
|
|
76445
|
+
* Mirrors the sphereBrush/sphereEraser "scroll to change radius" UX so the
|
|
76446
|
+
* user no longer has to reach for the B&E Size slider mid-annotation. Active
|
|
76447
|
+
* whenever the EventRouter is in `draw` mode (i.e. Shift is held — with or
|
|
76448
|
+
* without the left button down) and the current tool is brush or eraser.
|
|
76449
|
+
*
|
|
76450
|
+
* Pencil is deliberately excluded: it also draws with Shift, but its stroke
|
|
76451
|
+
* width is fixed and unrelated to `brushAndEraserSize`. Sphere-family tools
|
|
76452
|
+
* keep their own wheel-radius handler (`activeWheelMode === 'sphereBrush'`).
|
|
76453
|
+
*
|
|
76454
|
+
* `gui_states.mode` is the source of truth for the active tool here, not
|
|
76455
|
+
* `eventRouter.getGuiTool()` — the latter is only updated for sphere tools.
|
|
76456
|
+
*
|
|
76457
|
+
* @returns true if the event was handled (and should not fall through to
|
|
76458
|
+
* the zoom/slice/sphere wheel handlers).
|
|
76459
|
+
*/
|
|
76460
|
+
tryBrushEraserResizeWheel(e) {
|
|
76461
|
+
// `draw` mode means Shift is held and neither crosshair nor contrast is
|
|
76462
|
+
// active (EventRouter only enters it for drawing tools under those rules),
|
|
76463
|
+
// so it doubles as the "Shift held, not blocked" gate.
|
|
76464
|
+
if (this.eventRouter.getMode() !== 'draw')
|
|
76465
|
+
return false;
|
|
76466
|
+
const mode = this.state.gui_states.mode;
|
|
76467
|
+
// Brush = no flag set; eraser = mode.eraser. Exclude pencil and all
|
|
76468
|
+
// sphere-family tools.
|
|
76469
|
+
if (mode.pencil || mode.sphere || mode.sphereBrush || mode.sphereEraser) {
|
|
76470
|
+
return false;
|
|
76471
|
+
}
|
|
76472
|
+
e.preventDefault();
|
|
76473
|
+
const drawing = this.state.gui_states.drawing;
|
|
76474
|
+
// Step/bounds match the B&E Size slider config (min 5, max 50, step 1).
|
|
76475
|
+
const next = Math.max(5, Math.min(50, drawing.brushAndEraserSize + (e.deltaY < 0 ? 1 : -1)));
|
|
76476
|
+
if (next === drawing.brushAndEraserSize)
|
|
76477
|
+
return true;
|
|
76478
|
+
drawing.brushAndEraserSize = next;
|
|
76479
|
+
// Both brush and eraser draw a size-preview ring in start() that reads
|
|
76480
|
+
// brushAndEraserSize live, so the new size shows up on the next frame with
|
|
76481
|
+
// no extra work here.
|
|
76482
|
+
return true;
|
|
76489
76483
|
}
|
|
76490
76484
|
setPencilIconUrls(urls) {
|
|
76491
76485
|
this.pencilUrls = urls;
|
|
@@ -76654,6 +76648,7 @@ void main() {
|
|
|
76654
76648
|
this.state.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
76655
76649
|
this.state.protectedData.ctxes.drawingCtx.globalAlpha =
|
|
76656
76650
|
this.state.gui_states.drawing.globalAlpha;
|
|
76651
|
+
const currentMode = this.eventRouter.getMode();
|
|
76657
76652
|
if (this.state.protectedData.isDrawing) {
|
|
76658
76653
|
this.state.protectedData.ctxes.drawingLayerMasterCtx.lineCap = "round";
|
|
76659
76654
|
this.state.protectedData.ctxes.drawingLayerMasterCtx.globalAlpha = 1;
|
|
@@ -76662,16 +76657,18 @@ void main() {
|
|
|
76662
76657
|
target.ctx.globalAlpha = 1;
|
|
76663
76658
|
}
|
|
76664
76659
|
}
|
|
76665
|
-
else {
|
|
76666
|
-
|
|
76667
|
-
if (currentMode === 'draw') {
|
|
76668
|
-
this.drawingTool.renderBrushPreview(this.state.protectedData.ctxes.drawingCtx, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
76669
|
-
}
|
|
76670
|
-
else if (currentMode === 'crosshair' || this.eventRouter.isCrosshairEnabled()) {
|
|
76671
|
-
this.crosshairTool.renderCrosshair(this.state.protectedData.ctxes.drawingCtx, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
76672
|
-
}
|
|
76660
|
+
else if (currentMode === 'crosshair' || this.eventRouter.isCrosshairEnabled()) {
|
|
76661
|
+
this.crosshairTool.renderCrosshair(this.state.protectedData.ctxes.drawingCtx, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
76673
76662
|
}
|
|
76674
76663
|
this.state.protectedData.ctxes.drawingCtx.drawImage(this.state.protectedData.canvases.drawingCanvasLayerMaster, 0, 0);
|
|
76664
|
+
// Brush/eraser size ring is drawn AFTER the mask composite so it stays
|
|
76665
|
+
// visible during an active stroke (not just on hover) — e.g. while the
|
|
76666
|
+
// user resizes with Shift+wheel mid-paint. Gated on draw mode so it only
|
|
76667
|
+
// shows for brush/eraser with Shift held (renderBrushPreview itself
|
|
76668
|
+
// skips pencil and the no-hover case).
|
|
76669
|
+
if (currentMode === 'draw') {
|
|
76670
|
+
this.drawingTool.renderBrushPreview(this.state.protectedData.ctxes.drawingCtx);
|
|
76671
|
+
}
|
|
76675
76672
|
if (this.state.gui_states.mode.sphere
|
|
76676
76673
|
|| this.state.gui_states.mode.sphereBrush
|
|
76677
76674
|
|| this.state.gui_states.mode.sphereEraser) {
|
|
@@ -78383,7 +78380,6 @@ void main() {
|
|
|
78383
78380
|
nrrd_states: this.state.nrrd_states,
|
|
78384
78381
|
drawingCanvas: this.state.protectedData.canvases.drawingCanvas,
|
|
78385
78382
|
drawingPrameters: this.drawCore.drawingPrameters,
|
|
78386
|
-
eraserUrls: this.drawCore.eraserUrls,
|
|
78387
78383
|
pencilUrls: this.drawCore.pencilUrls,
|
|
78388
78384
|
mainPreSlices: this.state.protectedData.mainPreSlices,
|
|
78389
78385
|
protectedData: this.state.protectedData,
|
|
@@ -78416,7 +78412,6 @@ void main() {
|
|
|
78416
78412
|
this.guiCallbacks = {
|
|
78417
78413
|
updatePencilState: this.guiParameterSettings.pencil.onChange,
|
|
78418
78414
|
updateEraserState: this.guiParameterSettings.eraser.onChange,
|
|
78419
|
-
updateBrushAndEraserSize: this.guiParameterSettings.brushAndEraserSize.onChange,
|
|
78420
78415
|
updateSphereState: this.guiParameterSettings.sphere.onChange,
|
|
78421
78416
|
updateCalDistance: this.guiParameterSettings.activeSphereType.onChange,
|
|
78422
78417
|
updateWindowHigh: this.guiParameterSettings.windowHigh.onChange,
|
|
@@ -78529,9 +78524,9 @@ void main() {
|
|
|
78529
78524
|
return this.state.gui_states.drawing.globalAlpha;
|
|
78530
78525
|
}
|
|
78531
78526
|
setBrushSize(size) {
|
|
78532
|
-
|
|
78527
|
+
// No cursor side-effect needed: the brush/eraser preview ring reads this
|
|
78528
|
+
// value live each frame (see DrawingTool.renderBrushPreview).
|
|
78533
78529
|
this.state.gui_states.drawing.brushAndEraserSize = Math.max(5, Math.min(50, size));
|
|
78534
|
-
(_a = this.guiCallbacks) === null || _a === void 0 ? void 0 : _a.updateBrushAndEraserSize();
|
|
78535
78530
|
}
|
|
78536
78531
|
getBrushSize() {
|
|
78537
78532
|
return this.state.gui_states.drawing.brushAndEraserSize;
|
|
@@ -78766,9 +78761,6 @@ void main() {
|
|
|
78766
78761
|
enableContrastDragEvents(callback) {
|
|
78767
78762
|
this.drawCore.setupConrastEvents(callback);
|
|
78768
78763
|
}
|
|
78769
|
-
setEraserUrls(urls) {
|
|
78770
|
-
this.drawCore.setEraserUrls(urls);
|
|
78771
|
-
}
|
|
78772
78764
|
setPencilIconUrls(urls) {
|
|
78773
78765
|
this.drawCore.setPencilIconUrls(urls);
|
|
78774
78766
|
}
|
|
@@ -79458,7 +79450,7 @@ void main() {
|
|
|
79458
79450
|
}
|
|
79459
79451
|
|
|
79460
79452
|
// import * as kiwrious from "copper3d_plugin_heart_k";
|
|
79461
|
-
const REVISION = "v3.4.
|
|
79453
|
+
const REVISION = "v3.4.7-beta";
|
|
79462
79454
|
console.log(`%cCopper3D Visualisation %cBeta:${REVISION}`, "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25");
|
|
79463
79455
|
|
|
79464
79456
|
exports.CHANNEL_COLORS = CHANNEL_COLORS;
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,6 @@ import type { ToolMode, IAnnotationCallbacks } from "./Utils/segmentation/core/t
|
|
|
25
25
|
import { CHANNEL_COLORS, CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss } from "./Utils/segmentation/core/index";
|
|
26
26
|
import type { LayerId, ChannelValue } from "./Utils/segmentation/core/index";
|
|
27
27
|
import "./css/style.css";
|
|
28
|
-
export declare const REVISION = "v3.4.
|
|
28
|
+
export declare const REVISION = "v3.4.7-beta";
|
|
29
29
|
export { copperRenderer, copperRendererOnDemond, copperMSceneRenderer, setHDRFilePath, addLabelToScene, convert3DPostoScreenPos, convertScreenPosto3DPos, addBoxHelper, fullScreenListenner, configKiwriousHeart, copperScene, copperSceneOnDemond, copperMScene, CameraViewPoint, kiwrious, NrrdTools, loading, Copper3dTrackballControls, createTexture2D_NRRD, MeshNodeTool, throttle, removeGuiFolderChilden, CHANNEL_COLORS, CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss, GaussianSmoother, };
|
|
30
30
|
export type { positionType, screenPosType, optsType, nrrdMeshesType, nrrdSliceType, SensorDecodedValue_kiwrious, SensorReadResult_kiwrious, HeartRateResult_kiwrious, loadingBarType, IPaintImage, exportPaintImageType, IOptVTKLoader, ICommXYZ, IGUIStates, IGuiParameterSettings, INrrdStates, NrrdState, GuiState, IGuiMeta, ToolMode, IAnnotationCallbacks, LayerId, ChannelValue, };
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import { MeshNodeTool } from "./Utils/MeshNodeTool";
|
|
|
21
21
|
import { removeGuiFolderChilden } from "./Utils/segmentation/coreTools/gui";
|
|
22
22
|
import { CHANNEL_COLORS, CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss } from "./Utils/segmentation/core/index";
|
|
23
23
|
import "./css/style.css";
|
|
24
|
-
export const REVISION = "v3.4.
|
|
24
|
+
export const REVISION = "v3.4.7-beta";
|
|
25
25
|
console.log(`%cCopper3D Visualisation %cBeta:${REVISION}`, "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25");
|
|
26
26
|
export { copperRenderer, copperRendererOnDemond, copperMSceneRenderer, setHDRFilePath, addLabelToScene, convert3DPostoScreenPos, convertScreenPosto3DPos, addBoxHelper, fullScreenListenner, configKiwriousHeart, copperScene, copperSceneOnDemond, copperMScene, CameraViewPoint, kiwrious, NrrdTools, loading, Copper3dTrackballControls, createTexture2D_NRRD, MeshNodeTool, throttle, removeGuiFolderChilden, CHANNEL_COLORS, CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss, GaussianSmoother, };
|
|
27
27
|
//# sourceMappingURL=index.js.map
|
|
@@ -30,7 +30,6 @@ export declare class DrawToolCore {
|
|
|
30
30
|
readonly renderer: RenderingUtils;
|
|
31
31
|
drawingPrameters: IDrawingEvents;
|
|
32
32
|
contrastEventPrameters: IContrastEvents;
|
|
33
|
-
eraserUrls: string[];
|
|
34
33
|
pencilUrls: string[];
|
|
35
34
|
undoManager: UndoManager;
|
|
36
35
|
eventRouter: EventRouter;
|
|
@@ -59,7 +58,25 @@ export declare class DrawToolCore {
|
|
|
59
58
|
constructor(container: HTMLElement, state: CanvasState);
|
|
60
59
|
private initTools;
|
|
61
60
|
private initDrawToolCore;
|
|
62
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Shift + mouse-wheel resize for the brush and eraser tools.
|
|
63
|
+
*
|
|
64
|
+
* Mirrors the sphereBrush/sphereEraser "scroll to change radius" UX so the
|
|
65
|
+
* user no longer has to reach for the B&E Size slider mid-annotation. Active
|
|
66
|
+
* whenever the EventRouter is in `draw` mode (i.e. Shift is held — with or
|
|
67
|
+
* without the left button down) and the current tool is brush or eraser.
|
|
68
|
+
*
|
|
69
|
+
* Pencil is deliberately excluded: it also draws with Shift, but its stroke
|
|
70
|
+
* width is fixed and unrelated to `brushAndEraserSize`. Sphere-family tools
|
|
71
|
+
* keep their own wheel-radius handler (`activeWheelMode === 'sphereBrush'`).
|
|
72
|
+
*
|
|
73
|
+
* `gui_states.mode` is the source of truth for the active tool here, not
|
|
74
|
+
* `eventRouter.getGuiTool()` — the latter is only updated for sphere tools.
|
|
75
|
+
*
|
|
76
|
+
* @returns true if the event was handled (and should not fall through to
|
|
77
|
+
* the zoom/slice/sphere wheel handlers).
|
|
78
|
+
*/
|
|
79
|
+
private tryBrushEraserResizeWheel;
|
|
63
80
|
setPencilIconUrls(urls: string[]): void;
|
|
64
81
|
private setCurrentLayer;
|
|
65
82
|
draw(opts?: IDrawOpts): void;
|
|
@@ -110,7 +110,6 @@ export declare class NrrdTools {
|
|
|
110
110
|
setBaseDrawDisplayCanvasesSize(size: number): void;
|
|
111
111
|
setDisplaySliceIndexPanel(panel: HTMLDivElement): void;
|
|
112
112
|
enableContrastDragEvents(callback: (step: number, towards: "horizental" | "vertical") => void): void;
|
|
113
|
-
setEraserUrls(urls: string[]): void;
|
|
114
113
|
setPencilIconUrls(urls: string[]): void;
|
|
115
114
|
/**
|
|
116
115
|
* Switch all contrast slices' orientation
|
|
@@ -65,9 +65,16 @@ export declare class DrawingTool extends BaseTool {
|
|
|
65
65
|
*/
|
|
66
66
|
createBrushTrackingHandler(): (e: MouseEvent) => void;
|
|
67
67
|
/**
|
|
68
|
-
* Render brush
|
|
68
|
+
* Render the size-preview ring for the brush / eraser on the drawing context.
|
|
69
|
+
*
|
|
70
|
+
* - Brush: solid outline in the current brush colour.
|
|
71
|
+
* - Eraser: red dashed outline (matches the sphereEraser preview), so the
|
|
72
|
+
* eraser no longer needs a sized PNG cursor — the ring tracks the live
|
|
73
|
+
* `brushAndEraserSize` and is what the user resizes with Shift+wheel.
|
|
74
|
+
*
|
|
75
|
+
* Only the pencil has no preview ring (its stroke width is fixed).
|
|
69
76
|
*/
|
|
70
|
-
renderBrushPreview(ctx: CanvasRenderingContext2D,
|
|
77
|
+
renderBrushPreview(ctx: CanvasRenderingContext2D, _width?: number, _height?: number): void;
|
|
71
78
|
/**
|
|
72
79
|
* Convert display (zoomed) coordinates to 3D voxel coordinates.
|
|
73
80
|
* Same logic as SphereBrushTool.canvasToVoxelCenter.
|
|
@@ -94,6 +101,15 @@ export declare class DrawingTool extends BaseTool {
|
|
|
94
101
|
* then composite all layers to master.
|
|
95
102
|
*/
|
|
96
103
|
private refreshLayerFromVolume;
|
|
104
|
+
/**
|
|
105
|
+
* Notify the backend of the brush stroke by firing onMaskChanged for the
|
|
106
|
+
* current slice. Brush writes voxels straight into the MaskVolume (no canvas
|
|
107
|
+
* bake), so we read the authoritative slice back from the volume — mirrors
|
|
108
|
+
* SphereBrushTool.refreshDisplay's notify path. Brush only ever touches the
|
|
109
|
+
* current view slice (canvasToVoxel3D fixes the slice axis), so a single
|
|
110
|
+
* slice notification is sufficient.
|
|
111
|
+
*/
|
|
112
|
+
private notifyMaskChanged;
|
|
97
113
|
/** Capture pre-draw slice snapshot for undo */
|
|
98
114
|
private capturePreDrawSnapshot;
|
|
99
115
|
/** Push delta to UndoManager after drawing completes */
|
|
@@ -41,7 +41,6 @@ export interface ToolHost {
|
|
|
41
41
|
} | undefined;
|
|
42
42
|
pushUndoDelta(delta: MaskDelta): void;
|
|
43
43
|
pushUndoGroup(deltas: MaskDelta[]): void;
|
|
44
|
-
getEraserUrls(): string[];
|
|
45
44
|
enableCrosshair(): void;
|
|
46
45
|
setUpSphereOrigins(mouseX: number, mouseY: number, sliceIndex: number): void;
|
|
47
46
|
zoomActionAfterDrawSphere(): void;
|
|
@@ -64,7 +63,7 @@ export type ZoomHostDeps = Pick<ToolHost, 'resetPaintAreaUIPosition' | 'resizePa
|
|
|
64
63
|
/** SphereTool host dependencies */
|
|
65
64
|
export type SphereHostDeps = Pick<ToolHost, 'setEmptyCanvasSize' | 'drawImageOnEmptyImage' | 'enableCrosshair' | 'setUpSphereOrigins'>;
|
|
66
65
|
/** DrawingTool host dependencies */
|
|
67
|
-
export type DrawingHostDeps = Pick<ToolHost, 'setCurrentLayer' | 'compositeAllLayers' | 'syncLayerSliceData' | 'filterDrawedImage' | 'getVolumeForLayer' | 'pushUndoDelta' | '
|
|
66
|
+
export type DrawingHostDeps = Pick<ToolHost, 'setCurrentLayer' | 'compositeAllLayers' | 'syncLayerSliceData' | 'filterDrawedImage' | 'getVolumeForLayer' | 'pushUndoDelta' | 'renderSliceToCanvas' | 'getOrCreateSliceBuffer'>;
|
|
68
67
|
/** DragSliceTool host dependencies */
|
|
69
68
|
export type DragSliceHostDeps = Pick<ToolHost, 'setSyncsliceNum' | 'setIsDrawFalse' | 'flipDisplayImageByAxis' | 'setEmptyCanvasSize' | 'getOrCreateSliceBuffer' | 'renderSliceToCanvas' | 'refreshSphereOverlay' | 'compositeAllLayers'>;
|
|
70
69
|
/** LayerChannelManager host dependencies */
|
|
@@ -74,4 +73,4 @@ export type SliceRenderHostDeps = Pick<ToolHost, 'compositeAllLayers' | 'getOrCr
|
|
|
74
73
|
/** DataLoader host dependencies */
|
|
75
74
|
export type DataLoaderHostDeps = Pick<ToolHost, 'invalidateSliceBuffer' | 'setDisplaySlicesBaseOnAxis' | 'afterLoadSlice' | 'setEmptyCanvasSize' | 'syncLayerSliceData' | 'reloadMasksFromVolume' | 'resetZoom'>;
|
|
76
75
|
/** SphereBrushTool host dependencies */
|
|
77
|
-
export type SphereBrushHostDeps = Pick<ToolHost, 'getVolumeForLayer' | 'compositeAllLayers' | 'pushUndoGroup' | 'renderSliceToCanvas' | 'getOrCreateSliceBuffer' | 'setEmptyCanvasSize' | 'reloadMasksFromVolume'
|
|
76
|
+
export type SphereBrushHostDeps = Pick<ToolHost, 'getVolumeForLayer' | 'compositeAllLayers' | 'pushUndoGroup' | 'renderSliceToCanvas' | 'getOrCreateSliceBuffer' | 'setEmptyCanvasSize' | 'reloadMasksFromVolume'>;
|
|
@@ -7,7 +7,6 @@ export declare function loading(loadingGif?: string): {
|
|
|
7
7
|
progress: HTMLDivElement;
|
|
8
8
|
};
|
|
9
9
|
export declare function switchPencilIcon(icon: string, urls?: string[]): string;
|
|
10
|
-
export declare function switchEraserSize(size: number, urls?: string[]): string;
|
|
11
10
|
export declare function throttle(callback: (event: MouseEvent) => void, wait: number): (event: MouseEvent) => void;
|
|
12
11
|
/**
|
|
13
12
|
* Cubic-Lagrange basis function
|
package/dist/types/index.d.ts
CHANGED
|
@@ -25,6 +25,6 @@ import type { ToolMode, IAnnotationCallbacks } from "./Utils/segmentation/core/t
|
|
|
25
25
|
import { CHANNEL_COLORS, CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss } from "./Utils/segmentation/core/index";
|
|
26
26
|
import type { LayerId, ChannelValue } from "./Utils/segmentation/core/index";
|
|
27
27
|
import "./css/style.css";
|
|
28
|
-
export declare const REVISION = "v3.4.
|
|
28
|
+
export declare const REVISION = "v3.4.7-beta";
|
|
29
29
|
export { copperRenderer, copperRendererOnDemond, copperMSceneRenderer, setHDRFilePath, addLabelToScene, convert3DPostoScreenPos, convertScreenPosto3DPos, addBoxHelper, fullScreenListenner, configKiwriousHeart, copperScene, copperSceneOnDemond, copperMScene, CameraViewPoint, kiwrious, NrrdTools, loading, Copper3dTrackballControls, createTexture2D_NRRD, MeshNodeTool, throttle, removeGuiFolderChilden, CHANNEL_COLORS, CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss, GaussianSmoother, };
|
|
30
30
|
export type { positionType, screenPosType, optsType, nrrdMeshesType, nrrdSliceType, SensorDecodedValue_kiwrious, SensorReadResult_kiwrious, HeartRateResult_kiwrious, loadingBarType, IPaintImage, exportPaintImageType, IOptVTKLoader, ICommXYZ, IGUIStates, IGuiParameterSettings, INrrdStates, NrrdState, GuiState, IGuiMeta, ToolMode, IAnnotationCallbacks, LayerId, ChannelValue, };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "copper3d",
|
|
3
3
|
"description": "A 3d visualisation package base on threejs provides multiple scenes and Nrrd image load funtion.",
|
|
4
|
-
"version": "3.4.
|
|
4
|
+
"version": "3.4.7",
|
|
5
5
|
"main": "dist/bundle.umd.js",
|
|
6
6
|
"moudle": "dist/bundle.esm.js",
|
|
7
7
|
"types": "dist/types/index.d.ts",
|