@blocksdiy/react-common 1.52.1 → 1.53.0
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/hooks/agent-conversation-context.d.ts +13 -0
- package/dist/hooks/agent-conversation-context.d.ts.map +1 -1
- package/dist/hooks/agent-conversation-context.js +22 -1
- package/dist/hooks/agent-thread-store.d.ts +62 -0
- package/dist/hooks/agent-thread-store.d.ts.map +1 -0
- package/dist/hooks/agent-thread-store.js +170 -0
- package/dist/hooks/floating-agent-chat.d.ts +77 -0
- package/dist/hooks/floating-agent-chat.d.ts.map +1 -0
- package/dist/hooks/floating-agent-chat.js +89 -0
- package/dist/hooks/use-agent-thread.d.ts +67 -0
- package/dist/hooks/use-agent-thread.d.ts.map +1 -0
- package/dist/hooks/use-agent-thread.js +193 -0
- package/dist/hooks/use-agent-threads.d.ts +33 -1
- package/dist/hooks/use-agent-threads.d.ts.map +1 -1
- package/dist/hooks/use-agent-threads.js +132 -83
- package/dist/hooks/use-media-query.d.ts +2 -0
- package/dist/hooks/use-media-query.d.ts.map +1 -0
- package/dist/hooks/use-media-query.js +11 -0
- package/dist/hooks/use-resizable-panel.d.ts +66 -0
- package/dist/hooks/use-resizable-panel.d.ts.map +1 -0
- package/dist/hooks/use-resizable-panel.js +184 -0
- package/package.json +2 -2
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// A floating panel's size, headless: resized from the edges its corner anchoring leaves free by
|
|
2
|
+
// a pointer drag (with capture) or arrow keys on a focused handle, clamped to a floor and to the
|
|
3
|
+
// viewport, remembered per browser. The hook owns the math and the memory; the component owns
|
|
4
|
+
// the handles' markup and spreads `getHandleProps(axis)` on them.
|
|
5
|
+
import { useCallback, useRef, useState } from "react";
|
|
6
|
+
const clampNumber = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
7
|
+
/**
|
|
8
|
+
* A size the panel can take inside `viewport`: never below the floor, never past the viewport
|
|
9
|
+
* minus the inset on both sides — unless the viewport itself is smaller than the floor, where
|
|
10
|
+
* the floor wins (CSS still caps the box to the viewport).
|
|
11
|
+
*/
|
|
12
|
+
export const clampPanelSize = (size, viewport, { minSize, inset }) => {
|
|
13
|
+
const maxWidth = Math.max(minSize.width, viewport.width - 2 * inset);
|
|
14
|
+
const maxHeight = Math.max(minSize.height, viewport.height - 2 * inset);
|
|
15
|
+
return {
|
|
16
|
+
width: Math.round(clampNumber(size.width, minSize.width, maxWidth)),
|
|
17
|
+
height: Math.round(clampNumber(size.height, minSize.height, maxHeight)),
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
/** A stored size, or null for anything but two finite positive numbers. */
|
|
21
|
+
export const parseStoredPanelSize = (raw) => {
|
|
22
|
+
if (!raw) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(raw);
|
|
27
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const { width, height } = parsed;
|
|
31
|
+
if (typeof width !== "number" ||
|
|
32
|
+
typeof height !== "number" ||
|
|
33
|
+
!Number.isFinite(width) ||
|
|
34
|
+
!Number.isFinite(height) ||
|
|
35
|
+
width <= 0 ||
|
|
36
|
+
height <= 0) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return { width, height };
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The size a drag has reached. The panel is anchored at the viewport's bottom-start corner, so
|
|
47
|
+
* growth is toward the end edge and upward — the horizontal delta flips in RTL, and a drag up
|
|
48
|
+
* counts as more height.
|
|
49
|
+
*/
|
|
50
|
+
export const panelSizeFromDrag = (drag, pointer, bounds) => {
|
|
51
|
+
const dx = (pointer.clientX - drag.startX) * (drag.isRtl ? -1 : 1);
|
|
52
|
+
const dy = drag.startY - pointer.clientY;
|
|
53
|
+
return clampPanelSize({
|
|
54
|
+
width: drag.axis === "height" ? drag.startSize.width : drag.startSize.width + dx,
|
|
55
|
+
height: drag.axis === "width" ? drag.startSize.height : drag.startSize.height + dy,
|
|
56
|
+
}, drag.viewport, bounds);
|
|
57
|
+
};
|
|
58
|
+
/** The key's step for a handle: toward the end edge and up grow; null for a key the axis ignores. */
|
|
59
|
+
export const panelKeyboardDelta = (key, axis, step, isRtl) => {
|
|
60
|
+
const grow = isRtl ? "ArrowLeft" : "ArrowRight";
|
|
61
|
+
const shrink = isRtl ? "ArrowRight" : "ArrowLeft";
|
|
62
|
+
if (axis !== "height" && key === grow) {
|
|
63
|
+
return { dx: step, dy: 0 };
|
|
64
|
+
}
|
|
65
|
+
if (axis !== "height" && key === shrink) {
|
|
66
|
+
return { dx: -step, dy: 0 };
|
|
67
|
+
}
|
|
68
|
+
if (axis !== "width" && key === "ArrowUp") {
|
|
69
|
+
return { dx: 0, dy: step };
|
|
70
|
+
}
|
|
71
|
+
if (axis !== "width" && key === "ArrowDown") {
|
|
72
|
+
return { dx: 0, dy: -step };
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
const KEYBOARD_STEP = 16;
|
|
77
|
+
const KEYBOARD_STEP_LARGE = 64;
|
|
78
|
+
const readStoredSize = (storageKey) => {
|
|
79
|
+
if (!storageKey) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
return parseStoredPanelSize(window.localStorage.getItem(storageKey));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const storeSize = (storageKey, size) => {
|
|
90
|
+
if (!storageKey) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
window.localStorage.setItem(storageKey, JSON.stringify(size));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// Private mode or a full store: the panel still resizes, it just forgets on reload.
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
/** The panel's box, the viewport it floats in, and its direction. */
|
|
101
|
+
const measure = (panel) => {
|
|
102
|
+
const rect = panel.getBoundingClientRect();
|
|
103
|
+
return {
|
|
104
|
+
size: { width: rect.width, height: rect.height },
|
|
105
|
+
viewport: { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight },
|
|
106
|
+
isRtl: getComputedStyle(panel).direction === "rtl",
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
export const useResizablePanel = (panelRef, { defaultSize, minSize, inset, storageKey }) => {
|
|
110
|
+
const [size, setSize] = useState(() => {
|
|
111
|
+
const stored = readStoredSize(storageKey);
|
|
112
|
+
if (!stored) {
|
|
113
|
+
return defaultSize;
|
|
114
|
+
}
|
|
115
|
+
// Remembered under an earlier, smaller floor: never below today's (CSS caps it to the viewport).
|
|
116
|
+
return {
|
|
117
|
+
width: Math.max(stored.width, minSize.width),
|
|
118
|
+
height: Math.max(stored.height, minSize.height),
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
const [isResizing, setResizing] = useState(false);
|
|
122
|
+
const dragRef = useRef(null);
|
|
123
|
+
const bounds = { minSize, inset };
|
|
124
|
+
const commit = useCallback((next) => {
|
|
125
|
+
setSize(next);
|
|
126
|
+
storeSize(storageKey, next);
|
|
127
|
+
}, [storageKey]);
|
|
128
|
+
const getHandleProps = (axis) => ({
|
|
129
|
+
onPointerDown: (event) => {
|
|
130
|
+
const panel = panelRef.current;
|
|
131
|
+
if (!panel || event.button !== 0) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const measured = measure(panel);
|
|
135
|
+
dragRef.current = {
|
|
136
|
+
pointerId: event.pointerId,
|
|
137
|
+
axis,
|
|
138
|
+
startX: event.clientX,
|
|
139
|
+
startY: event.clientY,
|
|
140
|
+
startSize: measured.size,
|
|
141
|
+
viewport: measured.viewport,
|
|
142
|
+
isRtl: measured.isRtl,
|
|
143
|
+
};
|
|
144
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
145
|
+
event.preventDefault(); // no text selection while dragging
|
|
146
|
+
setResizing(true);
|
|
147
|
+
},
|
|
148
|
+
onPointerMove: (event) => {
|
|
149
|
+
const drag = dragRef.current;
|
|
150
|
+
if (!drag || drag.pointerId !== event.pointerId) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
setSize(panelSizeFromDrag(drag, event, bounds));
|
|
154
|
+
},
|
|
155
|
+
onPointerUp: (event) => {
|
|
156
|
+
const drag = dragRef.current;
|
|
157
|
+
if (!drag || drag.pointerId !== event.pointerId) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
dragRef.current = null;
|
|
161
|
+
setResizing(false);
|
|
162
|
+
// The drag's own final value — a DOM measurement here could lag the last move by a frame.
|
|
163
|
+
commit(panelSizeFromDrag(drag, event, bounds));
|
|
164
|
+
},
|
|
165
|
+
onPointerCancel: () => {
|
|
166
|
+
dragRef.current = null;
|
|
167
|
+
setResizing(false);
|
|
168
|
+
},
|
|
169
|
+
onKeyDown: (event) => {
|
|
170
|
+
const panel = panelRef.current;
|
|
171
|
+
if (!panel) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const { size: current, viewport, isRtl } = measure(panel);
|
|
175
|
+
const delta = panelKeyboardDelta(event.key, axis, event.shiftKey ? KEYBOARD_STEP_LARGE : KEYBOARD_STEP, isRtl);
|
|
176
|
+
if (!delta) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
commit(clampPanelSize({ width: current.width + delta.dx, height: current.height + delta.dy }, viewport, bounds));
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
return { size, isResizing, getHandleProps };
|
|
184
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocksdiy/react-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.53.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React common",
|
|
6
6
|
"keywords": [],
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"penpal": "^8.0.0",
|
|
40
40
|
"react": "^19.2.8",
|
|
41
41
|
"use-stick-to-bottom": "1.1.4",
|
|
42
|
-
"@blocksdiy/blocks-client-api": "1.
|
|
42
|
+
"@blocksdiy/blocks-client-api": "1.5.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/react": "^19.2.18",
|