@ctrl-ui/react 0.9.5 → 0.9.6

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.
Files changed (64) hide show
  1. package/README.md +2 -2
  2. package/dist/action-bar.js +16 -8
  3. package/dist/activity.d.ts +1 -1
  4. package/dist/activity.js +3 -1
  5. package/dist/audio-visualizer.js +1 -1
  6. package/dist/blocks/agent-run-clock.d.ts +5 -0
  7. package/dist/blocks/agent-run-clock.js +52 -0
  8. package/dist/blocks/agent-run-sidebar.d.ts +13 -0
  9. package/dist/blocks/agent-run-sidebar.js +100 -0
  10. package/dist/blocks/agent-run-status.d.ts +32 -0
  11. package/dist/blocks/agent-run-status.js +27 -0
  12. package/dist/blocks/agent-run.d.ts +80 -0
  13. package/dist/blocks/agent-run.js +254 -0
  14. package/dist/blocks/coding-agent.d.ts +1 -1
  15. package/dist/blocks/design-canvas-data.d.ts +1 -1
  16. package/dist/blocks/design-canvas.d.ts +2 -1
  17. package/dist/blocks/design-canvas.js +15 -8
  18. package/dist/chat-message.js +33 -39
  19. package/dist/knob-contracts/infinite-canvas-knobs.d.ts +1 -1
  20. package/dist/knob-contracts/infinite-canvas-knobs.js +1 -7
  21. package/dist/knob-contracts/swap-knobs.d.ts +4 -0
  22. package/dist/knob-contracts/swap-knobs.js +9 -0
  23. package/dist/knob-contracts/timeline-knobs.d.ts +1 -1
  24. package/dist/knob-contracts/timeline-knobs.js +1 -0
  25. package/dist/knob-contracts/tool-approval-knobs.d.ts +4 -0
  26. package/dist/knob-contracts/tool-approval-knobs.js +32 -0
  27. package/dist/lib/infinite-canvas-camera-store.d.ts +65 -0
  28. package/dist/lib/infinite-canvas-camera-store.js +233 -0
  29. package/dist/lib/infinite-canvas-camera.d.ts +33 -0
  30. package/dist/lib/infinite-canvas-camera.js +98 -0
  31. package/dist/lib/infinite-canvas-navigation.d.ts +19 -0
  32. package/dist/lib/infinite-canvas-navigation.js +395 -0
  33. package/dist/markdown-editor/bubble-menu.d.ts +6 -0
  34. package/dist/markdown-editor/bubble-menu.js +45 -0
  35. package/dist/markdown-editor/toolbar.d.ts +17 -1
  36. package/dist/markdown-editor/toolbar.js +103 -81
  37. package/dist/styles/index.css +10 -8
  38. package/dist/styles/recipes/activity.css +8 -0
  39. package/dist/styles/recipes/button-group.css +12 -6
  40. package/dist/styles/recipes/button.css +12 -2
  41. package/dist/styles/recipes/chat-message-edit.css +6 -21
  42. package/dist/styles/recipes/infinite-canvas.css +17 -57
  43. package/dist/styles/recipes/markdown-editor.css +12 -0
  44. package/dist/styles/recipes/swap.css +143 -0
  45. package/dist/styles/recipes/timeline.css +11 -0
  46. package/dist/styles/recipes/tool-approval.css +267 -0
  47. package/dist/styles/recipes/track-highlight.css +19 -6
  48. package/dist/styles/theme.css +6 -5
  49. package/dist/styles/type-scale.css +1 -1
  50. package/dist/tool-approval.d.ts +49 -0
  51. package/dist/tool-approval.js +260 -0
  52. package/dist/ui/code.js +13 -3
  53. package/dist/ui/command.js +1 -0
  54. package/dist/ui/infinite-canvas.d.ts +19 -22
  55. package/dist/ui/infinite-canvas.js +264 -325
  56. package/dist/ui/rich-tooltip.d.ts +2 -1
  57. package/dist/ui/rich-tooltip.js +32 -29
  58. package/dist/ui/swap.d.ts +14 -0
  59. package/dist/ui/swap.js +69 -0
  60. package/dist/ui/timeline.d.ts +1 -1
  61. package/dist/ui/timeline.js +1 -1
  62. package/dist/user-ask.js +1 -1
  63. package/lint/no-raw-type-classes.grit +2 -2
  64. package/package.json +2 -2
@@ -0,0 +1,98 @@
1
+ //#region src/lib/infinite-canvas-camera.ts
2
+ const MAX_CAMERA_OFFSET_PX = 1e7;
3
+ const WHEEL_LINE_HEIGHT_PX = 16;
4
+ const DOM_DELTA_LINE = 1;
5
+ const DOM_DELTA_PAGE = 2;
6
+ function clampScale(scale, minScale, maxScale) {
7
+ return Math.min(Math.max(scale, minScale), maxScale);
8
+ }
9
+ function sameTransform(left, right) {
10
+ return left.x === right.x && left.y === right.y && left.scale === right.scale;
11
+ }
12
+ function boundedTransform(next, minScale, maxScale) {
13
+ if (!(Number.isFinite(next.x) && Number.isFinite(next.y) && Number.isFinite(next.scale)) || next.scale <= 0) return null;
14
+ return {
15
+ x: Math.min(Math.max(next.x, -1e7), MAX_CAMERA_OFFSET_PX),
16
+ y: Math.min(Math.max(next.y, -1e7), MAX_CAMERA_OFFSET_PX),
17
+ scale: clampScale(next.scale, minScale, maxScale)
18
+ };
19
+ }
20
+ function toWorldPoint(transform, point) {
21
+ return {
22
+ x: (point.x - transform.x) / transform.scale,
23
+ y: (point.y - transform.y) / transform.scale
24
+ };
25
+ }
26
+ function zoomAroundPoint(transform, nextScale, point) {
27
+ const world = toWorldPoint(transform, point);
28
+ return {
29
+ x: point.x - world.x * nextScale,
30
+ y: point.y - world.y * nextScale,
31
+ scale: nextScale
32
+ };
33
+ }
34
+ function pinchTransform(start, startMidpoint, midpoint, nextScale) {
35
+ const zoomed = zoomAroundPoint(start, nextScale, startMidpoint);
36
+ return {
37
+ x: zoomed.x + midpoint.x - startMidpoint.x,
38
+ y: zoomed.y + midpoint.y - startMidpoint.y,
39
+ scale: nextScale
40
+ };
41
+ }
42
+ function wheelDeltaMultiplier(deltaMode, pageHeight) {
43
+ if (deltaMode === DOM_DELTA_LINE) return WHEEL_LINE_HEIGHT_PX;
44
+ if (deltaMode === DOM_DELTA_PAGE) return pageHeight;
45
+ return 1;
46
+ }
47
+ function wheelDeltaPixels(event, pageHeight) {
48
+ const multiplier = wheelDeltaMultiplier(event.deltaMode, pageHeight);
49
+ const verticalAsHorizontal = event.shiftKey && event.deltaX === 0;
50
+ return {
51
+ x: (verticalAsHorizontal ? event.deltaY : event.deltaX) * multiplier,
52
+ y: (verticalAsHorizontal ? 0 : event.deltaY) * multiplier
53
+ };
54
+ }
55
+ function unionBounds(bounds) {
56
+ if (bounds.length === 0) return null;
57
+ let left = Number.POSITIVE_INFINITY;
58
+ let top = Number.POSITIVE_INFINITY;
59
+ let right = Number.NEGATIVE_INFINITY;
60
+ let bottom = Number.NEGATIVE_INFINITY;
61
+ for (const entry of bounds) {
62
+ left = Math.min(left, entry.x);
63
+ top = Math.min(top, entry.y);
64
+ right = Math.max(right, entry.x + entry.width);
65
+ bottom = Math.max(bottom, entry.y + entry.height);
66
+ }
67
+ return {
68
+ x: left,
69
+ y: top,
70
+ width: right - left,
71
+ height: bottom - top
72
+ };
73
+ }
74
+ function fitTransform(bounds, area, limits) {
75
+ const availableWidth = Math.max(1, area.width - limits.padding * 2);
76
+ const availableHeight = Math.max(1, area.height - limits.padding * 2);
77
+ const scale = clampScale(Math.min(availableWidth / Math.max(bounds.width, 1), availableHeight / Math.max(bounds.height, 1)), limits.minScale, limits.maxScale);
78
+ return {
79
+ x: area.x + area.width / 2 - (bounds.x + bounds.width / 2) * scale,
80
+ y: area.y + area.height / 2 - (bounds.y + bounds.height / 2) * scale,
81
+ scale
82
+ };
83
+ }
84
+ function revealAxisOffset(start, size, areaStart, areaSize, padding) {
85
+ const visibleStart = areaStart + padding;
86
+ const visibleEnd = areaStart + areaSize - padding;
87
+ if (size > visibleEnd - visibleStart || start < visibleStart) return visibleStart - start;
88
+ if (start + size > visibleEnd) return visibleEnd - (start + size);
89
+ return 0;
90
+ }
91
+ function revealOffset(screenBounds, area, padding) {
92
+ return {
93
+ x: revealAxisOffset(screenBounds.x, screenBounds.width, area.x, area.width, padding),
94
+ y: revealAxisOffset(screenBounds.y, screenBounds.height, area.y, area.height, padding)
95
+ };
96
+ }
97
+ //#endregion
98
+ export { boundedTransform, clampScale, fitTransform, pinchTransform, revealOffset, sameTransform, toWorldPoint, unionBounds, wheelDeltaMultiplier, wheelDeltaPixels, zoomAroundPoint };
@@ -0,0 +1,19 @@
1
+ import { InfiniteCanvasPoint } from "./infinite-canvas-camera.js";
2
+ import { InfiniteCanvasCameraStore } from "./infinite-canvas-camera-store.js";
3
+ //#region src/lib/infinite-canvas-navigation.d.ts
4
+ export type InfiniteCanvasPanOnDrag = "background" | "anywhere" | "never";
5
+ export type InfiniteCanvasNavigationOptions = {
6
+ panOnDrag: InfiniteCanvasPanOnDrag;
7
+ onWheel?: (event: WheelEvent) => void;
8
+ };
9
+ export type InfiniteCanvasNavigation = {
10
+ beginBackgroundPan: (event: PointerEvent) => void;
11
+ syncPanReady: () => void;
12
+ detach: () => void;
13
+ };
14
+ export declare const INFINITE_CANVAS_KEY_SHORTCUTS = "ArrowLeft ArrowRight ArrowUp ArrowDown + - 0 1";
15
+ export declare const ARROW_KEY_DIRECTIONS: Partial<Record<string, InfiniteCanvasPoint>>;
16
+ export declare function keyboardStep(shiftKey: boolean): number;
17
+ export declare function runInfiniteCanvasKeyCommand(camera: InfiniteCanvasCameraStore, key: string, shiftKey: boolean): boolean;
18
+ export declare function attachInfiniteCanvasNavigation(root: HTMLElement, camera: InfiniteCanvasCameraStore, readOptions: () => InfiniteCanvasNavigationOptions): InfiniteCanvasNavigation;
19
+ //#endregion
@@ -0,0 +1,395 @@
1
+ import { pinchTransform, wheelDeltaMultiplier, wheelDeltaPixels, zoomAroundPoint } from "./infinite-canvas-camera.js";
2
+ //#region src/lib/infinite-canvas-navigation.ts
3
+ const INFINITE_CANVAS_KEY_SHORTCUTS = "ArrowLeft ArrowRight ArrowUp ArrowDown + - 0 1";
4
+ const ARROW_KEY_DIRECTIONS = {
5
+ ArrowLeft: {
6
+ x: -1,
7
+ y: 0
8
+ },
9
+ ArrowRight: {
10
+ x: 1,
11
+ y: 0
12
+ },
13
+ ArrowUp: {
14
+ x: 0,
15
+ y: -1
16
+ },
17
+ ArrowDown: {
18
+ x: 0,
19
+ y: 1
20
+ }
21
+ };
22
+ const KEYBOARD_ZOOM_FACTORS = {
23
+ "+": 1.15,
24
+ "=": 1.15,
25
+ "-": 1 / 1.15
26
+ };
27
+ const KEYBOARD_STEP_PX = 32;
28
+ const WHEEL_ZOOM_SPEED = .004;
29
+ const MAX_WHEEL_ZOOM_DELTA = 50;
30
+ const NOTCHED_WHEEL_DELTA = 40;
31
+ const CLICK_SLOP_PX = 3;
32
+ const MIDDLE_BUTTON = 1;
33
+ const SCROLLABLE_OVERFLOW = /auto|scroll|overlay/;
34
+ const SPACE_CONSUMING_TARGET = [
35
+ "input",
36
+ "textarea",
37
+ "select",
38
+ "button",
39
+ "a[href]",
40
+ "[contenteditable]:not([contenteditable='false'])",
41
+ ...[
42
+ "button",
43
+ "checkbox",
44
+ "combobox",
45
+ "menuitem",
46
+ "option",
47
+ "radio",
48
+ "slider",
49
+ "spinbutton",
50
+ "switch",
51
+ "tab",
52
+ "textbox"
53
+ ].map((role) => `[role='${role}']`)
54
+ ].join(", ");
55
+ function keyboardStep(shiftKey) {
56
+ return shiftKey ? 96 : KEYBOARD_STEP_PX;
57
+ }
58
+ function runInfiniteCanvasKeyCommand(camera, key, shiftKey) {
59
+ const direction = ARROW_KEY_DIRECTIONS[key];
60
+ if (direction) {
61
+ const current = camera.getTransform();
62
+ const step = keyboardStep(shiftKey);
63
+ camera.move({
64
+ ...current,
65
+ x: current.x - direction.x * step,
66
+ y: current.y - direction.y * step
67
+ }, "keyboard");
68
+ return true;
69
+ }
70
+ const zoomFactor = KEYBOARD_ZOOM_FACTORS[key];
71
+ if (zoomFactor) camera.zoomBy(zoomFactor, { reason: "keyboard" });
72
+ else if (key === "0") camera.reset({ reason: "keyboard" });
73
+ else if (key === "1") camera.fit({ reason: "keyboard" });
74
+ else return false;
75
+ return true;
76
+ }
77
+ function canScrollFurther(element, delta) {
78
+ if (element.scrollHeight <= element.clientHeight && element.scrollWidth <= element.clientWidth) return false;
79
+ const { overflowX, overflowY } = getComputedStyle(element);
80
+ const verticalRoom = delta.y < 0 ? element.scrollTop > 0 : Math.ceil(element.scrollTop + element.clientHeight) < element.scrollHeight;
81
+ const horizontalRoom = delta.x < 0 ? element.scrollLeft > 0 : Math.ceil(element.scrollLeft + element.clientWidth) < element.scrollWidth;
82
+ return delta.y !== 0 && SCROLLABLE_OVERFLOW.test(overflowY) && verticalRoom || delta.x !== 0 && SCROLLABLE_OVERFLOW.test(overflowX) && horizontalRoom;
83
+ }
84
+ function nestedScrollerConsumes(target, root, delta) {
85
+ for (let element = target instanceof Element ? target : null; element && element !== root; element = element.parentElement) if (canScrollFurther(element, delta)) return true;
86
+ return false;
87
+ }
88
+ function gesturePointerIds(active) {
89
+ return active.kind === "pan" ? [active.pointerId] : active.pointerIds;
90
+ }
91
+ function clickAfterGesture(ended) {
92
+ if (ended.kind === "pinch") return "click";
93
+ if (!(ended.explicit || ended.moved)) return null;
94
+ return ended.button === MIDDLE_BUTTON ? "auxclick" : "click";
95
+ }
96
+ function gestureScale(event) {
97
+ return "scale" in event && typeof event.scale === "number" ? event.scale : null;
98
+ }
99
+ function gestureClientPoint(event) {
100
+ if (!("clientX" in event && "clientY" in event)) return null;
101
+ const { clientX, clientY } = event;
102
+ return typeof clientX === "number" && typeof clientY === "number" ? {
103
+ clientX,
104
+ clientY
105
+ } : null;
106
+ }
107
+ function attachInfiniteCanvasNavigation(root, camera, readOptions) {
108
+ const listeners = new AbortController();
109
+ const { signal } = listeners;
110
+ const touches = /* @__PURE__ */ new Map();
111
+ let gesture = null;
112
+ let spaceHeld = false;
113
+ let safariPinchStartScale = null;
114
+ let suppressedClick = null;
115
+ let suppressedClickTimer;
116
+ let pointerFocus = false;
117
+ let pointerFocusTimer;
118
+ function syncPanReady() {
119
+ root.toggleAttribute("data-pan-ready", spaceHeld || readOptions().panOnDrag === "anywhere");
120
+ }
121
+ function suppressNextClick(type) {
122
+ suppressedClick = type;
123
+ clearTimeout(suppressedClickTimer);
124
+ suppressedClickTimer = setTimeout(() => {
125
+ suppressedClick = null;
126
+ });
127
+ }
128
+ function startPan(event, explicit) {
129
+ root.setPointerCapture(event.pointerId);
130
+ root.toggleAttribute("data-panning", true);
131
+ gesture = {
132
+ kind: "pan",
133
+ pointerId: event.pointerId,
134
+ button: event.button,
135
+ explicit,
136
+ moved: false,
137
+ startPointer: {
138
+ x: event.clientX,
139
+ y: event.clientY
140
+ },
141
+ startTransform: camera.getTransform()
142
+ };
143
+ }
144
+ function startPinch() {
145
+ const [first, second] = [...touches.entries()];
146
+ if (!(first && second)) return;
147
+ for (const [pointerId] of [first, second]) if (!root.hasPointerCapture(pointerId)) root.setPointerCapture(pointerId);
148
+ root.toggleAttribute("data-panning", true);
149
+ gesture = {
150
+ kind: "pinch",
151
+ pointerIds: [first[0], second[0]],
152
+ startDistance: Math.max(1, Math.hypot(first[1].x - second[1].x, first[1].y - second[1].y)),
153
+ startMidpoint: camera.toLocalPoint({
154
+ clientX: (first[1].x + second[1].x) / 2,
155
+ clientY: (first[1].y + second[1].y) / 2
156
+ }),
157
+ startTransform: camera.getTransform()
158
+ };
159
+ }
160
+ function involvesPointer(pointerId) {
161
+ return gesture !== null && gesturePointerIds(gesture).includes(pointerId);
162
+ }
163
+ function finishGesture() {
164
+ const ended = gesture;
165
+ if (!ended) return;
166
+ gesture = null;
167
+ root.removeAttribute("data-panning");
168
+ for (const pointerId of gesturePointerIds(ended)) if (root.hasPointerCapture(pointerId)) root.releasePointerCapture(pointerId);
169
+ const click = clickAfterGesture(ended);
170
+ if (click) suppressNextClick(click);
171
+ }
172
+ function claim(event) {
173
+ event.preventDefault();
174
+ event.stopPropagation();
175
+ }
176
+ function isNavigableTarget(target) {
177
+ return target === root || target instanceof Node && camera.contentElement()?.contains(target) === true;
178
+ }
179
+ function markPointerFocus() {
180
+ pointerFocus = true;
181
+ clearTimeout(pointerFocusTimer);
182
+ pointerFocusTimer = setTimeout(() => {
183
+ pointerFocus = false;
184
+ });
185
+ }
186
+ function claimsTouchPinch(event) {
187
+ if (event.pointerType !== "touch") return false;
188
+ if (event.isPrimary) touches.clear();
189
+ touches.set(event.pointerId, {
190
+ x: event.clientX,
191
+ y: event.clientY
192
+ });
193
+ if (touches.size < 2) return false;
194
+ claim(event);
195
+ if (touches.size === 2) startPinch();
196
+ return true;
197
+ }
198
+ function isExplicitPan(event) {
199
+ if (!isNavigableTarget(event.target)) return false;
200
+ if (event.button === MIDDLE_BUTTON) return true;
201
+ return event.button === 0 && (spaceHeld || readOptions().panOnDrag === "anywhere");
202
+ }
203
+ function claimNavigationPointer(event) {
204
+ markPointerFocus();
205
+ if (claimsTouchPinch(event) || gesture || !event.isPrimary || !isExplicitPan(event)) return;
206
+ claim(event);
207
+ startPan(event, true);
208
+ }
209
+ function followPointer(event) {
210
+ if (touches.has(event.pointerId)) touches.set(event.pointerId, {
211
+ x: event.clientX,
212
+ y: event.clientY
213
+ });
214
+ if (!(gesture && involvesPointer(event.pointerId))) return;
215
+ if (gesture.kind === "pan") {
216
+ const deltaX = event.clientX - gesture.startPointer.x;
217
+ const deltaY = event.clientY - gesture.startPointer.y;
218
+ if (Math.hypot(deltaX, deltaY) > CLICK_SLOP_PX) gesture.moved = true;
219
+ const { startTransform } = gesture;
220
+ camera.move({
221
+ ...startTransform,
222
+ x: startTransform.x + deltaX,
223
+ y: startTransform.y + deltaY
224
+ }, "pointer");
225
+ return;
226
+ }
227
+ const [first, second] = gesture.pointerIds.map((pointerId) => touches.get(pointerId));
228
+ if (!(first && second)) return;
229
+ const midpoint = camera.toLocalPoint({
230
+ clientX: (first.x + second.x) / 2,
231
+ clientY: (first.y + second.y) / 2
232
+ });
233
+ const ratio = Math.hypot(first.x - second.x, first.y - second.y) / gesture.startDistance;
234
+ const nextScale = camera.limitScale(gesture.startTransform.scale * ratio);
235
+ camera.move(pinchTransform(gesture.startTransform, gesture.startMidpoint, midpoint, nextScale), "pinch");
236
+ }
237
+ function releasePointer(event) {
238
+ touches.delete(event.pointerId);
239
+ if (involvesPointer(event.pointerId)) finishGesture();
240
+ }
241
+ function zoomWithWheel(event) {
242
+ event.preventDefault();
243
+ if (safariPinchStartScale !== null) return;
244
+ const current = camera.getTransform();
245
+ const zoomDelta = event.deltaY * wheelDeltaMultiplier(event.deltaMode, root.clientHeight);
246
+ const boundedDelta = Math.min(Math.max(zoomDelta, -50), MAX_WHEEL_ZOOM_DELTA);
247
+ const nextScale = camera.limitScale(current.scale * Math.exp(-boundedDelta * WHEEL_ZOOM_SPEED));
248
+ camera.move(zoomAroundPoint(current, nextScale, camera.toLocalPoint(event)), "wheel", Math.abs(zoomDelta) >= NOTCHED_WHEEL_DELTA);
249
+ }
250
+ function handleWheel(event) {
251
+ readOptions().onWheel?.(event);
252
+ if (event.defaultPrevented) return;
253
+ if (event.ctrlKey || event.metaKey) {
254
+ zoomWithWheel(event);
255
+ return;
256
+ }
257
+ const delta = wheelDeltaPixels(event, root.clientHeight);
258
+ if (nestedScrollerConsumes(event.target, root, delta)) return;
259
+ event.preventDefault();
260
+ const current = camera.getTransform();
261
+ camera.move({
262
+ ...current,
263
+ x: current.x - delta.x,
264
+ y: current.y - delta.y
265
+ }, "wheel");
266
+ }
267
+ function startSafariPinch(event) {
268
+ event.preventDefault();
269
+ safariPinchStartScale = camera.getTransform().scale;
270
+ }
271
+ function followSafariPinch(event) {
272
+ event.preventDefault();
273
+ const scale = gestureScale(event);
274
+ if (safariPinchStartScale === null || scale === null || gesture?.kind === "pinch") return;
275
+ const clientPoint = gestureClientPoint(event);
276
+ const origin = clientPoint ? camera.toLocalPoint(clientPoint) : {
277
+ x: root.clientWidth / 2,
278
+ y: root.clientHeight / 2
279
+ };
280
+ camera.move(zoomAroundPoint(camera.getTransform(), camera.limitScale(safariPinchStartScale * scale), origin), "pinch");
281
+ }
282
+ function endSafariPinch(event) {
283
+ event.preventDefault();
284
+ safariPinchStartScale = null;
285
+ }
286
+ function armSpacePan(event) {
287
+ if (event.code !== "Space") return;
288
+ if (spaceHeld) {
289
+ event.preventDefault();
290
+ return;
291
+ }
292
+ if (event.defaultPrevented || event.repeat || event.isComposing || event.ctrlKey || event.metaKey || event.altKey) return;
293
+ const { target } = event;
294
+ if (target instanceof HTMLElement && (target.isContentEditable || target.matches(SPACE_CONSUMING_TARGET))) return;
295
+ if (!(root.contains(document.activeElement) || root.matches(":hover"))) return;
296
+ event.preventDefault();
297
+ spaceHeld = true;
298
+ syncPanReady();
299
+ }
300
+ function disarmSpacePan(event) {
301
+ if (event.code !== "Space" || !spaceHeld) return;
302
+ spaceHeld = false;
303
+ syncPanReady();
304
+ }
305
+ function releaseHeldInput() {
306
+ spaceHeld = false;
307
+ safariPinchStartScale = null;
308
+ touches.clear();
309
+ finishGesture();
310
+ syncPanReady();
311
+ }
312
+ function swallowClickAfterPan(event) {
313
+ if (event.type !== suppressedClick) return;
314
+ suppressedClick = null;
315
+ event.preventDefault();
316
+ event.stopPropagation();
317
+ }
318
+ function revealKeyboardFocus(event) {
319
+ if (pointerFocus || !(event.target instanceof Element)) return;
320
+ if (camera.contentElement()?.contains(event.target)) camera.revealElement(event.target);
321
+ }
322
+ function absorbNativeScroll() {
323
+ const { scrollLeft, scrollTop } = root;
324
+ if (scrollLeft === 0 && scrollTop === 0) return;
325
+ root.scrollTo(0, 0);
326
+ const current = camera.getTransform();
327
+ camera.moveNow({
328
+ ...current,
329
+ x: current.x - scrollLeft,
330
+ y: current.y - scrollTop
331
+ }, "reveal");
332
+ }
333
+ root.addEventListener("pointerdown", claimNavigationPointer, {
334
+ capture: true,
335
+ signal
336
+ });
337
+ root.addEventListener("pointermove", followPointer, {
338
+ capture: true,
339
+ signal
340
+ });
341
+ root.addEventListener("pointerup", releasePointer, {
342
+ capture: true,
343
+ signal
344
+ });
345
+ root.addEventListener("pointercancel", releasePointer, {
346
+ capture: true,
347
+ signal
348
+ });
349
+ root.addEventListener("lostpointercapture", (event) => {
350
+ if (event.target === root && involvesPointer(event.pointerId)) finishGesture();
351
+ }, { signal });
352
+ root.addEventListener("click", swallowClickAfterPan, {
353
+ capture: true,
354
+ signal
355
+ });
356
+ root.addEventListener("auxclick", swallowClickAfterPan, {
357
+ capture: true,
358
+ signal
359
+ });
360
+ root.addEventListener("mousedown", (event) => {
361
+ if (event.button === MIDDLE_BUTTON && isNavigableTarget(event.target)) event.preventDefault();
362
+ }, { signal });
363
+ root.addEventListener("wheel", handleWheel, {
364
+ passive: false,
365
+ signal
366
+ });
367
+ root.addEventListener("gesturestart", startSafariPinch, { signal });
368
+ root.addEventListener("gesturechange", followSafariPinch, { signal });
369
+ root.addEventListener("gestureend", endSafariPinch, { signal });
370
+ root.addEventListener("focusin", revealKeyboardFocus, { signal });
371
+ root.addEventListener("scroll", absorbNativeScroll, { signal });
372
+ window.addEventListener("keydown", armSpacePan, { signal });
373
+ window.addEventListener("keyup", disarmSpacePan, { signal });
374
+ window.addEventListener("blur", releaseHeldInput, { signal });
375
+ document.addEventListener("visibilitychange", () => {
376
+ if (document.visibilityState === "hidden") releaseHeldInput();
377
+ }, { signal });
378
+ syncPanReady();
379
+ return {
380
+ beginBackgroundPan(event) {
381
+ if (gesture || event.defaultPrevented || event.target !== root || !event.isPrimary || event.button !== 0) return;
382
+ if (readOptions().panOnDrag === "background") startPan(event, false);
383
+ },
384
+ syncPanReady,
385
+ detach() {
386
+ listeners.abort();
387
+ clearTimeout(suppressedClickTimer);
388
+ clearTimeout(pointerFocusTimer);
389
+ releaseHeldInput();
390
+ root.removeAttribute("data-pan-ready");
391
+ }
392
+ };
393
+ }
394
+ //#endregion
395
+ export { ARROW_KEY_DIRECTIONS, INFINITE_CANVAS_KEY_SHORTCUTS, attachInfiniteCanvasNavigation, keyboardStep, runInfiniteCanvasKeyCommand };
@@ -0,0 +1,6 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/markdown-editor/bubble-menu.d.ts
3
+ export declare function MarkdownEditorBubbleMenu({ children }: {
4
+ children?: ReactNode;
5
+ }): import("react").JSX.Element | null;
6
+ //#endregion
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import { TooltipProvider } from "../ui/tooltip.js";
3
+ import { useMarkdownEditor } from "./context.js";
4
+ import { Toolbar, ToolbarSeparator } from "../ui/toolbar.js";
5
+ import { MarkdownEditorFormatButton, MarkdownEditorHeading, MarkdownEditorLink, useMarkdownEditorFormatActions } from "./toolbar.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { NodeSelection } from "@tiptap/pm/state";
8
+ import { BubbleMenu } from "@tiptap/react/menus";
9
+ //#region src/markdown-editor/bubble-menu.tsx
10
+ const showForFormattableText = ({ editor, view, state, element }) => {
11
+ const { selection } = state;
12
+ return !(selection instanceof NodeSelection) && !selection.empty && state.doc.textBetween(selection.from, selection.to).trim() !== "" && !editor.isActive("codeBlock") && editor.isEditable && (view.hasFocus() || element.contains(document.activeElement));
13
+ };
14
+ const floatingOptions = {
15
+ placement: "top",
16
+ offset: 8,
17
+ flip: { padding: 8 },
18
+ shift: { padding: 8 }
19
+ };
20
+ function MarkdownEditorBubbleMenu({ children }) {
21
+ const { editor, disabled, source } = useMarkdownEditor();
22
+ const { marks } = useMarkdownEditorFormatActions();
23
+ if (!editor || disabled || source) return null;
24
+ return /* @__PURE__ */ jsx(BubbleMenu, {
25
+ editor,
26
+ shouldShow: showForFormattableText,
27
+ options: floatingOptions,
28
+ "data-control-ui": "markdown-editor",
29
+ "data-control-family": "markdown-editor",
30
+ "data-slot": "bubble-menu",
31
+ children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Toolbar, {
32
+ "aria-label": "Selection formatting",
33
+ variant: "floating",
34
+ children: [
35
+ /* @__PURE__ */ jsx(MarkdownEditorHeading, {}),
36
+ /* @__PURE__ */ jsx(ToolbarSeparator, {}),
37
+ marks.map((action) => /* @__PURE__ */ jsx(MarkdownEditorFormatButton, { action }, action.label)),
38
+ /* @__PURE__ */ jsx(MarkdownEditorLink, {}),
39
+ children
40
+ ]
41
+ }) })
42
+ });
43
+ }
44
+ //#endregion
45
+ export { MarkdownEditorBubbleMenu };
@@ -1,8 +1,24 @@
1
- import { ReactNode } from "react";
1
+ import { MouseEventHandler, ReactNode } from "react";
2
+ import { BoldIcon } from "lucide-react";
2
3
  //#region src/markdown-editor/toolbar.d.ts
4
+ type MarkdownEditorFormatAction = {
5
+ label: string;
6
+ Icon: typeof BoldIcon;
7
+ active: boolean;
8
+ run: () => void;
9
+ };
10
+ export declare function useMarkdownEditorFormatActions(): {
11
+ marks: MarkdownEditorFormatAction[];
12
+ blocks: MarkdownEditorFormatAction[];
13
+ };
14
+ export declare function MarkdownEditorFormatButton({ action, onMouseDown }: {
15
+ action: MarkdownEditorFormatAction;
16
+ onMouseDown?: MouseEventHandler<HTMLButtonElement>;
17
+ }): import("react").JSX.Element;
3
18
  export declare function MarkdownEditorToolbar({ children }: {
4
19
  children?: ReactNode;
5
20
  }): import("react").JSX.Element;
21
+ export declare function MarkdownEditorHeading(): import("react").JSX.Element;
6
22
  export declare function MarkdownEditorLink(): import("react").JSX.Element;
7
23
  export declare function MarkdownEditorImage(): import("react").JSX.Element;
8
24
  export declare function MarkdownEditorUploads(): import("react").JSX.Element;