@tutti-os/workbench-surface 0.0.109 → 0.0.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +84 -64
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -43,6 +43,12 @@ When a node body or header needs host-owned business state, pass an
|
|
|
43
43
|
`externalNodeState` and `externalWorkspaceState`. If the source exposes
|
|
44
44
|
`subscribe(...)`, the host re-renders when that subscription notifies.
|
|
45
45
|
|
|
46
|
+
Node body context also exposes `isDragging` and `isResizing`. The Workbench
|
|
47
|
+
shell continues applying live frame geometry during direct manipulation, while
|
|
48
|
+
an expensive body adapter may use these flags to suppress frame-only renders
|
|
49
|
+
until the interaction settles. The adapter must still observe both interaction
|
|
50
|
+
transitions so the final committed frame reaches responsive body layout.
|
|
51
|
+
|
|
46
52
|
Host-owned business instances are represented with `projectedNodes`. A
|
|
47
53
|
projected node tells the workbench that a shell should currently exist for a
|
|
48
54
|
host-owned instance; the workbench reconciles that presence with snapshot
|
package/dist/index.d.ts
CHANGED
|
@@ -274,6 +274,8 @@ interface WorkbenchRenderNodeContext<TData = unknown> {
|
|
|
274
274
|
node: WorkbenchNode<TData>;
|
|
275
275
|
layout: WorkbenchNodeRenderFrame;
|
|
276
276
|
controller: WorkbenchController<TData>;
|
|
277
|
+
isDragging: boolean;
|
|
278
|
+
isResizing: boolean;
|
|
277
279
|
}
|
|
278
280
|
interface WorkbenchDockContext<TData = unknown> {
|
|
279
281
|
focusedNodeId: string | null;
|
|
@@ -661,7 +663,11 @@ interface WorkbenchHostNodeBodyContext<TExternalNodeState = unknown, TExternalWo
|
|
|
661
663
|
host: WorkbenchHostHandle;
|
|
662
664
|
instanceId: string;
|
|
663
665
|
instanceKey?: string | null;
|
|
666
|
+
/** True while the host is interactively moving this node. */
|
|
667
|
+
isDragging: boolean;
|
|
664
668
|
isFocused: boolean;
|
|
669
|
+
/** True while the host is interactively resizing this node. */
|
|
670
|
+
isResizing: boolean;
|
|
665
671
|
/** Current host presentation mode; null for the normal window layout. */
|
|
666
672
|
presentationMode?: WorkbenchSurfacePresentation["mode"] | null;
|
|
667
673
|
node: WorkbenchNode<WorkbenchHostNodeData>;
|
package/dist/index.js
CHANGED
|
@@ -3267,6 +3267,12 @@ function WorkbenchNodeLayerItem({
|
|
|
3267
3267
|
const isFocused = useWorkbenchSelector(
|
|
3268
3268
|
(state) => selectFocusedWorkbenchNode(state)?.id === nodeID
|
|
3269
3269
|
);
|
|
3270
|
+
const isDragging = useWorkbenchSelector(
|
|
3271
|
+
(state) => state.activeDragNodeId === nodeID
|
|
3272
|
+
);
|
|
3273
|
+
const isResizing = useWorkbenchSelector(
|
|
3274
|
+
(state) => state.activeResizeNodeId === nodeID
|
|
3275
|
+
);
|
|
3270
3276
|
const zIndex = useWorkbenchSelector(
|
|
3271
3277
|
(state) => selectWorkbenchNodeZIndex(state, nodeID)
|
|
3272
3278
|
);
|
|
@@ -3297,6 +3303,8 @@ function WorkbenchNodeLayerItem({
|
|
|
3297
3303
|
}),
|
|
3298
3304
|
children: renderNode({
|
|
3299
3305
|
node,
|
|
3306
|
+
isDragging,
|
|
3307
|
+
isResizing,
|
|
3300
3308
|
layout: {
|
|
3301
3309
|
frame: node.frame,
|
|
3302
3310
|
presentation,
|
|
@@ -8659,6 +8667,77 @@ function resolveDockLabelTooltipAnchorRect(slotElement) {
|
|
|
8659
8667
|
};
|
|
8660
8668
|
}
|
|
8661
8669
|
|
|
8670
|
+
// src/host/dockWallpaperSampling.ts
|
|
8671
|
+
var dockWallpaperSampleMaxSizePx = 192;
|
|
8672
|
+
var dockWallpaperToneSampleCount = 7;
|
|
8673
|
+
var sampleByImage = /* @__PURE__ */ new WeakMap();
|
|
8674
|
+
function getDockWallpaperImageSample(image) {
|
|
8675
|
+
if (sampleByImage.has(image)) {
|
|
8676
|
+
return sampleByImage.get(image) ?? null;
|
|
8677
|
+
}
|
|
8678
|
+
const sample = createDockWallpaperImageSample(image);
|
|
8679
|
+
sampleByImage.set(image, sample);
|
|
8680
|
+
return sample;
|
|
8681
|
+
}
|
|
8682
|
+
function createDockWallpaperImageSample(image) {
|
|
8683
|
+
if (image.naturalWidth <= 0 || image.naturalHeight <= 0) {
|
|
8684
|
+
return null;
|
|
8685
|
+
}
|
|
8686
|
+
const canvas = document.createElement("canvas");
|
|
8687
|
+
const scale = dockWallpaperSampleMaxSizePx / Math.max(image.naturalWidth, image.naturalHeight);
|
|
8688
|
+
canvas.width = Math.max(1, Math.round(image.naturalWidth * scale));
|
|
8689
|
+
canvas.height = Math.max(1, Math.round(image.naturalHeight * scale));
|
|
8690
|
+
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
8691
|
+
if (!context) {
|
|
8692
|
+
return null;
|
|
8693
|
+
}
|
|
8694
|
+
try {
|
|
8695
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
8696
|
+
return {
|
|
8697
|
+
data: context.getImageData(0, 0, canvas.width, canvas.height).data,
|
|
8698
|
+
height: canvas.height,
|
|
8699
|
+
width: canvas.width
|
|
8700
|
+
};
|
|
8701
|
+
} catch {
|
|
8702
|
+
return null;
|
|
8703
|
+
}
|
|
8704
|
+
}
|
|
8705
|
+
function sampleDockWallpaperLuminanceAtElement({
|
|
8706
|
+
elementRect,
|
|
8707
|
+
renderedImageRect,
|
|
8708
|
+
sample,
|
|
8709
|
+
wallpaperRect
|
|
8710
|
+
}) {
|
|
8711
|
+
const isVerticalElement = elementRect.height >= elementRect.width;
|
|
8712
|
+
let luminanceSum = 0;
|
|
8713
|
+
let samples = 0;
|
|
8714
|
+
for (let index = 0; index < dockWallpaperToneSampleCount; index += 1) {
|
|
8715
|
+
const ratio = index / (dockWallpaperToneSampleCount - 1);
|
|
8716
|
+
const clientX = isVerticalElement ? elementRect.left + elementRect.width / 2 : elementRect.left + elementRect.width * ratio;
|
|
8717
|
+
const clientY = isVerticalElement ? elementRect.top + elementRect.height * ratio : elementRect.top + elementRect.height / 2;
|
|
8718
|
+
const imageX = (clientX - wallpaperRect.left - renderedImageRect.left) / renderedImageRect.width;
|
|
8719
|
+
const imageY = (clientY - wallpaperRect.top - renderedImageRect.top) / renderedImageRect.height;
|
|
8720
|
+
if (imageX < 0 || imageX > 1 || imageY < 0 || imageY > 1) {
|
|
8721
|
+
continue;
|
|
8722
|
+
}
|
|
8723
|
+
const sampleX = Math.min(
|
|
8724
|
+
sample.width - 1,
|
|
8725
|
+
Math.max(0, Math.round(imageX * (sample.width - 1)))
|
|
8726
|
+
);
|
|
8727
|
+
const sampleY = Math.min(
|
|
8728
|
+
sample.height - 1,
|
|
8729
|
+
Math.max(0, Math.round(imageY * (sample.height - 1)))
|
|
8730
|
+
);
|
|
8731
|
+
const pixelOffset = (sampleY * sample.width + sampleX) * 4;
|
|
8732
|
+
const red = sample.data[pixelOffset] ?? 0;
|
|
8733
|
+
const green = sample.data[pixelOffset + 1] ?? 0;
|
|
8734
|
+
const blue = sample.data[pixelOffset + 2] ?? 0;
|
|
8735
|
+
luminanceSum += 0.2126 * red + 0.7152 * green + 0.0722 * blue;
|
|
8736
|
+
samples += 1;
|
|
8737
|
+
}
|
|
8738
|
+
return samples > 0 ? luminanceSum / samples : null;
|
|
8739
|
+
}
|
|
8740
|
+
|
|
8662
8741
|
// src/host/dockScrollState.ts
|
|
8663
8742
|
var dockScrollOverflowEpsilonPx = 1;
|
|
8664
8743
|
function resolveWorkbenchHostDockScrollState({
|
|
@@ -12858,8 +12937,8 @@ async function resolveDockWallpaperTones({
|
|
|
12858
12937
|
if (!wallpaperImage) {
|
|
12859
12938
|
return /* @__PURE__ */ new Map();
|
|
12860
12939
|
}
|
|
12861
|
-
const
|
|
12862
|
-
if (!
|
|
12940
|
+
const wallpaperSample = getDockWallpaperImageSample(wallpaperImage);
|
|
12941
|
+
if (!wallpaperSample) {
|
|
12863
12942
|
return /* @__PURE__ */ new Map();
|
|
12864
12943
|
}
|
|
12865
12944
|
const wallpaperRect = wallpaperElement.getBoundingClientRect();
|
|
@@ -12877,7 +12956,7 @@ async function resolveDockWallpaperTones({
|
|
|
12877
12956
|
const luminance = sampleDockWallpaperLuminanceAtElement({
|
|
12878
12957
|
elementRect: element.getBoundingClientRect(),
|
|
12879
12958
|
renderedImageRect,
|
|
12880
|
-
|
|
12959
|
+
sample: wallpaperSample,
|
|
12881
12960
|
wallpaperRect
|
|
12882
12961
|
});
|
|
12883
12962
|
if (luminance === null) {
|
|
@@ -12910,26 +12989,6 @@ function loadDockWallpaperImage(url) {
|
|
|
12910
12989
|
dockWallpaperImageCache.set(url, promise);
|
|
12911
12990
|
return promise;
|
|
12912
12991
|
}
|
|
12913
|
-
function createDockWallpaperSampleCanvas(image) {
|
|
12914
|
-
if (image.naturalWidth <= 0 || image.naturalHeight <= 0) {
|
|
12915
|
-
return null;
|
|
12916
|
-
}
|
|
12917
|
-
const canvas = document.createElement("canvas");
|
|
12918
|
-
const scale = dockWallpaperSampleCanvasMaxSizePx / Math.max(image.naturalWidth, image.naturalHeight);
|
|
12919
|
-
canvas.width = Math.max(1, Math.round(image.naturalWidth * scale));
|
|
12920
|
-
canvas.height = Math.max(1, Math.round(image.naturalHeight * scale));
|
|
12921
|
-
const context = canvas.getContext("2d", { willReadFrequently: true });
|
|
12922
|
-
if (!context) {
|
|
12923
|
-
return null;
|
|
12924
|
-
}
|
|
12925
|
-
try {
|
|
12926
|
-
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
12927
|
-
context.getImageData(0, 0, 1, 1);
|
|
12928
|
-
} catch {
|
|
12929
|
-
return null;
|
|
12930
|
-
}
|
|
12931
|
-
return canvas;
|
|
12932
|
-
}
|
|
12933
12992
|
function resolveDockWallpaperRenderedImageRect({
|
|
12934
12993
|
containerHeight,
|
|
12935
12994
|
containerWidth,
|
|
@@ -12987,45 +13046,6 @@ function resolveDockWallpaperPositionOffset(value, availableSpace) {
|
|
|
12987
13046
|
}
|
|
12988
13047
|
return availableSpace / 2;
|
|
12989
13048
|
}
|
|
12990
|
-
function sampleDockWallpaperLuminanceAtElement({
|
|
12991
|
-
elementRect,
|
|
12992
|
-
renderedImageRect,
|
|
12993
|
-
sampleCanvas,
|
|
12994
|
-
wallpaperRect
|
|
12995
|
-
}) {
|
|
12996
|
-
const context = sampleCanvas.getContext("2d", { willReadFrequently: true });
|
|
12997
|
-
if (!context) {
|
|
12998
|
-
return null;
|
|
12999
|
-
}
|
|
13000
|
-
const isVerticalElement = elementRect.height >= elementRect.width;
|
|
13001
|
-
let luminanceSum = 0;
|
|
13002
|
-
let samples = 0;
|
|
13003
|
-
for (let index = 0; index < dockWallpaperToneSampleCount; index += 1) {
|
|
13004
|
-
const ratio = index / (dockWallpaperToneSampleCount - 1);
|
|
13005
|
-
const clientX = isVerticalElement ? elementRect.left + elementRect.width / 2 : elementRect.left + elementRect.width * ratio;
|
|
13006
|
-
const clientY = isVerticalElement ? elementRect.top + elementRect.height * ratio : elementRect.top + elementRect.height / 2;
|
|
13007
|
-
const imageX = (clientX - wallpaperRect.left - renderedImageRect.left) / renderedImageRect.width;
|
|
13008
|
-
const imageY = (clientY - wallpaperRect.top - renderedImageRect.top) / renderedImageRect.height;
|
|
13009
|
-
if (imageX < 0 || imageX > 1 || imageY < 0 || imageY > 1) {
|
|
13010
|
-
continue;
|
|
13011
|
-
}
|
|
13012
|
-
const canvasX = Math.min(
|
|
13013
|
-
sampleCanvas.width - 1,
|
|
13014
|
-
Math.max(0, Math.round(imageX * (sampleCanvas.width - 1)))
|
|
13015
|
-
);
|
|
13016
|
-
const canvasY = Math.min(
|
|
13017
|
-
sampleCanvas.height - 1,
|
|
13018
|
-
Math.max(0, Math.round(imageY * (sampleCanvas.height - 1)))
|
|
13019
|
-
);
|
|
13020
|
-
const pixel = context.getImageData(canvasX, canvasY, 1, 1).data;
|
|
13021
|
-
const red = pixel[0] ?? 0;
|
|
13022
|
-
const green = pixel[1] ?? 0;
|
|
13023
|
-
const blue = pixel[2] ?? 0;
|
|
13024
|
-
luminanceSum += 0.2126 * red + 0.7152 * green + 0.0722 * blue;
|
|
13025
|
-
samples += 1;
|
|
13026
|
-
}
|
|
13027
|
-
return samples > 0 ? luminanceSum / samples : null;
|
|
13028
|
-
}
|
|
13029
13049
|
function dockWallpaperToneMapsEqual(left, right) {
|
|
13030
13050
|
if (left.size !== right.size) {
|
|
13031
13051
|
return false;
|
|
@@ -13102,8 +13122,6 @@ var dockHoverPanelCloseDelayMs = 160;
|
|
|
13102
13122
|
var dockHoverPanelHitSlopPx = 12;
|
|
13103
13123
|
var dockHoverPanelBridgeSlopPx = 6;
|
|
13104
13124
|
var dockHoverPanelPointerRestTolerancePx = 4;
|
|
13105
|
-
var dockWallpaperSampleCanvasMaxSizePx = 192;
|
|
13106
|
-
var dockWallpaperToneSampleCount = 7;
|
|
13107
13125
|
var dockWallpaperDarkLuminanceThreshold = 132;
|
|
13108
13126
|
function rectContainsPoint(rect, clientX, clientY, slopPx = 0) {
|
|
13109
13127
|
return clientX >= rect.left - slopPx && clientX <= rect.right + slopPx && clientY >= rect.top - slopPx && clientY <= rect.bottom + slopPx;
|
|
@@ -13188,7 +13206,9 @@ function createWorkbenchHostNodeBodyContext({
|
|
|
13188
13206
|
host,
|
|
13189
13207
|
instanceId: context.node.data.instanceId,
|
|
13190
13208
|
instanceKey: context.node.data.instanceKey ?? null,
|
|
13209
|
+
isDragging: context.isDragging,
|
|
13191
13210
|
isFocused: selectFocusedWorkbenchNode(host.getSnapshot())?.id === context.node.id,
|
|
13211
|
+
isResizing: context.isResizing,
|
|
13192
13212
|
presentationMode: context.layout.presentation?.mode ?? null,
|
|
13193
13213
|
node: context.node,
|
|
13194
13214
|
setNodeRuntimeState(state) {
|