dishui 0.0.56 → 0.0.57

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 (29) hide show
  1. package/dist/components/Select/index.js +90 -72
  2. package/dist/components/Select/index.js.map +1 -1
  3. package/dist/components/pro/Flow/Flow.js +647 -564
  4. package/dist/components/pro/Flow/Flow.js.map +1 -1
  5. package/dist/components/pro/Flow/FlowCanvas.js +1027 -970
  6. package/dist/components/pro/Flow/FlowCanvas.js.map +1 -1
  7. package/dist/components/pro/Flow/canvas/ViewportContainer.d.ts +6 -0
  8. package/dist/components/pro/Flow/canvas/ViewportContainer.js +20 -15
  9. package/dist/components/pro/Flow/canvas/ViewportContainer.js.map +1 -1
  10. package/dist/components/pro/Flow/context/FlowUIContext.d.ts +6 -2
  11. package/dist/components/pro/Flow/context/FlowUIContext.js +51 -51
  12. package/dist/components/pro/Flow/context/FlowUIContext.js.map +1 -1
  13. package/dist/components/pro/Flow/hooks/useCompactMode.d.ts +13 -0
  14. package/dist/components/pro/Flow/hooks/useCompactMode.js +20 -0
  15. package/dist/components/pro/Flow/hooks/useCompactMode.js.map +1 -0
  16. package/dist/components/pro/Flow/hooks/useDraggablePanel.d.ts +19 -4
  17. package/dist/components/pro/Flow/hooks/useDraggablePanel.js +31 -24
  18. package/dist/components/pro/Flow/hooks/useDraggablePanel.js.map +1 -1
  19. package/dist/components/pro/Flow/hooks/useMindMap.js +16 -22
  20. package/dist/components/pro/Flow/hooks/useMindMap.js.map +1 -1
  21. package/dist/components/pro/Flow/hooks/usePixiApp.js +16 -16
  22. package/dist/components/pro/Flow/hooks/usePixiApp.js.map +1 -1
  23. package/dist/components/pro/Flow/panels/FlowPanelGroup.d.ts +10 -5
  24. package/dist/components/pro/Flow/panels/FlowPanelGroup.js +95 -67
  25. package/dist/components/pro/Flow/panels/FlowPanelGroup.js.map +1 -1
  26. package/dist/dishui.css +1 -1
  27. package/dist/utils/i18n.js +10 -0
  28. package/dist/utils/i18n.js.map +1 -1
  29. package/package.json +1 -1
@@ -18,11 +18,17 @@ export declare class ViewportManager {
18
18
  private _rightPanning;
19
19
  /** Whether the current right-button pan actually moved (used to suppress the context menu). */
20
20
  private _rightPanMoved;
21
+ /** True while a multi-touch gesture (pinch zoom / two-finger pan) is active.
22
+ * Set by FlowCanvas's native touch handlers; while set, pointer-driven
23
+ * interactions (marquee, node drag, tap-to-place) must be suppressed. */
24
+ multiTouchActive: boolean;
21
25
  constructor(world: Container, stage: Container, onChange?: ViewportChangeCallback);
22
26
  get state(): ViewportState;
23
27
  setState(s: Partial<ViewportState>): void;
24
28
  /** Start a pan from an external trigger (e.g. pan tool mode) */
25
29
  startPan(sx: number, sy: number): void;
30
+ /** Abort any in-progress pan (used when a multi-touch gesture takes over). */
31
+ cancelPan(): void;
26
32
  /** Smoothly animate to a target state */
27
33
  animateTo(target: ViewportState, duration?: number): void;
28
34
  /** Convert screen coords to world coords */
@@ -20,15 +20,16 @@ var d = class {
20
20
  _spaceHeld = !1;
21
21
  _rightPanning = !1;
22
22
  _rightPanMoved = !1;
23
- constructor(t, s, e) {
24
- this.world = t, this.stage = s, this.onChange = e, this._bindStageEvents(), this._bindKeyEvents();
23
+ multiTouchActive = !1;
24
+ constructor(t, s, i) {
25
+ this.world = t, this.stage = s, this.onChange = i, this._bindStageEvents(), this._bindKeyEvents();
25
26
  }
26
27
  get state() {
27
28
  return { ...this._state };
28
29
  }
29
30
  setState(t) {
30
- const s = t.x !== void 0 ? t.x : this._state.x, e = t.y !== void 0 ? t.y : this._state.y, i = t.zoom !== void 0 ? Math.max(h, Math.min(4, t.zoom)) : this._state.zoom;
31
- s === this._state.x && e === this._state.y && i === this._state.zoom || (this._state.x = s, this._state.y = e, this._state.zoom = i, this._applyTransform());
31
+ const s = t.x !== void 0 ? t.x : this._state.x, i = t.y !== void 0 ? t.y : this._state.y, e = t.zoom !== void 0 ? Math.max(h, Math.min(4, t.zoom)) : this._state.zoom;
32
+ s === this._state.x && i === this._state.y && e === this._state.zoom || (this._state.x = s, this._state.y = i, this._state.zoom = e, this._applyTransform());
32
33
  }
33
34
  startPan(t, s) {
34
35
  this._panning = !0, this._panStart = {
@@ -39,10 +40,13 @@ var d = class {
39
40
  y: this._state.y
40
41
  };
41
42
  }
43
+ cancelPan() {
44
+ this._panning = !1, this._rightPanning = !1;
45
+ }
42
46
  animateTo(t, s = 300) {
43
- const e = { ...this._state }, i = performance.now(), n = () => {
44
- const r = performance.now() - i, o = Math.min(1, r / s), a = o < 0.5 ? 2 * o * o : 1 - (-2 * o + 2) ** 2 / 2;
45
- this._state.x = e.x + (t.x - e.x) * a, this._state.y = e.y + (t.y - e.y) * a, this._state.zoom = e.zoom + (t.zoom - e.zoom) * a, this._applyTransform(), o < 1 && requestAnimationFrame(n);
47
+ const i = { ...this._state }, e = performance.now(), n = () => {
48
+ const r = performance.now() - e, o = Math.min(1, r / s), a = o < 0.5 ? 2 * o * o : 1 - (-2 * o + 2) ** 2 / 2;
49
+ this._state.x = i.x + (t.x - i.x) * a, this._state.y = i.y + (t.y - i.y) * a, this._state.zoom = i.zoom + (t.zoom - i.zoom) * a, this._applyTransform(), o < 1 && requestAnimationFrame(n);
46
50
  };
47
51
  requestAnimationFrame(n);
48
52
  }
@@ -73,13 +77,14 @@ var d = class {
73
77
  _handleKeyUp(t) {
74
78
  t.code === "Space" && (this._spaceHeld = !1, this._panning = !1);
75
79
  }
76
- zoomAt(t, s, e) {
77
- const i = this._state.zoom, n = Math.max(h, Math.min(4, i * e));
78
- n !== i && (this._state.x = t - (t - this._state.x) * (n / i), this._state.y = s - (s - this._state.y) * (n / i), this._state.zoom = n, this._applyTransform());
80
+ zoomAt(t, s, i) {
81
+ const e = this._state.zoom, n = Math.max(h, Math.min(4, e * i));
82
+ n !== e && (this._state.x = t - (t - this._state.x) * (n / e), this._state.y = s - (s - this._state.y) * (n / e), this._state.zoom = n, this._applyTransform());
79
83
  }
80
84
  _onPointerDown = (t) => {
81
- const s = t.button === 1, e = t.button === 2;
82
- (s || e || this._spaceHeld) && (this._panning = !0, this._rightPanning = e, this._rightPanMoved = !1, this._panStart = {
85
+ if (this.multiTouchActive) return;
86
+ const s = t.button === 1, i = t.button === 2;
87
+ (s || i || this._spaceHeld) && (this._panning = !0, this._rightPanning = i, this._rightPanMoved = !1, this._panStart = {
83
88
  x: t.global.x,
84
89
  y: t.global.y
85
90
  }, this._worldStart = {
@@ -88,9 +93,9 @@ var d = class {
88
93
  });
89
94
  };
90
95
  _onPointerMove = (t) => {
91
- if (!this._panning) return;
92
- const s = t.global.x - this._panStart.x, e = t.global.y - this._panStart.y;
93
- this._rightPanning && (Math.abs(s) > 3 || Math.abs(e) > 3) && (this._rightPanMoved = !0), this._state.x = this._worldStart.x + s, this._state.y = this._worldStart.y + e, this._applyTransform();
96
+ if (!this._panning || this.multiTouchActive) return;
97
+ const s = t.global.x - this._panStart.x, i = t.global.y - this._panStart.y;
98
+ this._rightPanning && (Math.abs(s) > 3 || Math.abs(i) > 3) && (this._rightPanMoved = !0), this._state.x = this._worldStart.x + s, this._state.y = this._worldStart.y + i, this._applyTransform();
94
99
  };
95
100
  _onPointerUp = () => {
96
101
  this._panning = !1, this._rightPanning = !1;
@@ -1 +1 @@
1
- {"version":3,"file":"ViewportContainer.js","names":[],"sources":["../../../../../src/components/pro/Flow/canvas/ViewportContainer.ts"],"sourcesContent":["import { Container, FederatedPointerEvent } from 'pixi.js';\nimport { MIN_ZOOM, MAX_ZOOM } from '../constants';\nimport type { ViewportState } from '../types';\n\nexport type ViewportChangeCallback = (state: ViewportState) => void;\n\n/**\n * Manages pan/zoom on the world container.\n * Listens for wheel (zoom) and middle-click / space+drag (pan).\n */\nexport class ViewportManager {\n private world: Container;\n private stage: Container;\n private _state: ViewportState = { x: 0, y: 0, zoom: 1 };\n private onChange?: ViewportChangeCallback;\n\n private _panning = false;\n private _panStart = { x: 0, y: 0 };\n private _worldStart = { x: 0, y: 0 };\n private _spaceHeld = false;\n /** True while a pan was triggered by the right mouse button. */\n private _rightPanning = false;\n /** Whether the current right-button pan actually moved (used to suppress the context menu). */\n private _rightPanMoved = false;\n\n constructor(world: Container, stage: Container, onChange?: ViewportChangeCallback) {\n this.world = world;\n this.stage = stage;\n this.onChange = onChange;\n this._bindStageEvents();\n this._bindKeyEvents();\n }\n\n get state(): ViewportState {\n return { ...this._state };\n }\n\n setState(s: Partial<ViewportState>): void {\n const nx = s.x !== undefined ? s.x : this._state.x;\n const ny = s.y !== undefined ? s.y : this._state.y;\n const nz = s.zoom !== undefined\n ? Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, s.zoom))\n : this._state.zoom;\n // Skip if nothing actually changed — avoids a needless onChange emit that\n // could feed back into React state and cause viewport jitter.\n if (nx === this._state.x && ny === this._state.y && nz === this._state.zoom) return;\n this._state.x = nx;\n this._state.y = ny;\n this._state.zoom = nz;\n this._applyTransform();\n }\n\n /** Start a pan from an external trigger (e.g. pan tool mode) */\n startPan(sx: number, sy: number): void {\n this._panning = true;\n this._panStart = { x: sx, y: sy };\n this._worldStart = { x: this._state.x, y: this._state.y };\n }\n\n /** Smoothly animate to a target state */\n animateTo(target: ViewportState, duration = 300): void {\n const start = { ...this._state };\n const startTime = performance.now();\n const tick = () => {\n const elapsed = performance.now() - startTime;\n const t = Math.min(1, elapsed / duration);\n const ease = t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;\n this._state.x = start.x + (target.x - start.x) * ease;\n this._state.y = start.y + (target.y - start.y) * ease;\n this._state.zoom = start.zoom + (target.zoom - start.zoom) * ease;\n this._applyTransform();\n if (t < 1) requestAnimationFrame(tick);\n };\n requestAnimationFrame(tick);\n }\n\n /** Convert screen coords to world coords */\n screenToWorld(sx: number, sy: number): { x: number; y: number } {\n return {\n x: (sx - this._state.x) / this._state.zoom,\n y: (sy - this._state.y) / this._state.zoom,\n };\n }\n\n /** Convert world coords to screen coords */\n worldToScreen(wx: number, wy: number): { x: number; y: number } {\n return {\n x: wx * this._state.zoom + this._state.x,\n y: wy * this._state.zoom + this._state.y,\n };\n }\n\n private _applyTransform(): void {\n this.world.position.set(this._state.x, this._state.y);\n this.world.scale.set(this._state.zoom);\n this.onChange?.(this.state);\n }\n\n private _bindStageEvents(): void {\n // NOTE: wheel/zoom is handled by a native, non-passive DOM listener in\n // FlowCanvas (see `zoomAt`) so it can call preventDefault() and stop the\n // browser's Ctrl/Cmd+wheel page zoom. Pixi's federated wheel event cannot\n // reliably prevent that native gesture.\n this.stage.on('pointerdown', this._onPointerDown, this);\n this.stage.on('pointermove', this._onPointerMove, this);\n this.stage.on('pointerup', this._onPointerUp, this);\n this.stage.on('pointerupoutside', this._onPointerUp, this);\n }\n\n private _bindKeyEvents(): void {\n this._handleKeyDown = this._handleKeyDown.bind(this);\n this._handleKeyUp = this._handleKeyUp.bind(this);\n window.addEventListener('keydown', this._handleKeyDown);\n window.addEventListener('keyup', this._handleKeyUp);\n }\n\n private _handleKeyDown(e: KeyboardEvent): void {\n if (e.code === 'Space') this._spaceHeld = true;\n }\n\n private _handleKeyUp(e: KeyboardEvent): void {\n if (e.code === 'Space') {\n this._spaceHeld = false;\n this._panning = false;\n }\n }\n\n /**\n * Zoom by a multiplicative factor around a screen-space anchor point.\n * Driven by a native wheel listener so the browser page zoom can be blocked.\n */\n zoomAt(sx: number, sy: number, factor: number): void {\n const oldZoom = this._state.zoom;\n const newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, oldZoom * factor));\n if (newZoom === oldZoom) return;\n this._state.x = sx - (sx - this._state.x) * (newZoom / oldZoom);\n this._state.y = sy - (sy - this._state.y) * (newZoom / oldZoom);\n this._state.zoom = newZoom;\n this._applyTransform();\n }\n\n private _onPointerDown = (e: FederatedPointerEvent): void => {\n const isMiddle = e.button === 1;\n const isRight = e.button === 2;\n if (isMiddle || isRight || this._spaceHeld) {\n this._panning = true;\n this._rightPanning = isRight;\n this._rightPanMoved = false;\n this._panStart = { x: e.global.x, y: e.global.y };\n this._worldStart = { x: this._state.x, y: this._state.y };\n }\n };\n\n private _onPointerMove = (e: FederatedPointerEvent): void => {\n if (!this._panning) return;\n const dx = e.global.x - this._panStart.x;\n const dy = e.global.y - this._panStart.y;\n // Mark a right-button pan as \"moved\" once it passes a small threshold so\n // that a plain right-click (no drag) still opens the context menu.\n if (this._rightPanning && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {\n this._rightPanMoved = true;\n }\n this._state.x = this._worldStart.x + dx;\n this._state.y = this._worldStart.y + dy;\n this._applyTransform();\n };\n\n private _onPointerUp = (): void => {\n this._panning = false;\n this._rightPanning = false;\n };\n\n get isPanning(): boolean {\n return this._panning;\n }\n\n get isSpaceHeld(): boolean {\n return this._spaceHeld;\n }\n\n /**\n * Whether the most recent right-button interaction was a pan drag (moved past\n * the threshold). Consumers use this to suppress the context menu after a\n * right-drag pan. The flag resets on the next pointer down.\n */\n get didRightPan(): boolean {\n return this._rightPanMoved;\n }\n\n destroy(): void {\n window.removeEventListener('keydown', this._handleKeyDown);\n window.removeEventListener('keyup', this._handleKeyUp);\n this.stage.off('pointerdown', this._onPointerDown, this);\n this.stage.off('pointermove', this._onPointerMove, this);\n this.stage.off('pointerup', this._onPointerUp, this);\n this.stage.off('pointerupoutside', this._onPointerUp, this);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ViewportContainer.js","names":[],"sources":["../../../../../src/components/pro/Flow/canvas/ViewportContainer.ts"],"sourcesContent":["import { Container, FederatedPointerEvent } from 'pixi.js';\nimport { MIN_ZOOM, MAX_ZOOM } from '../constants';\nimport type { ViewportState } from '../types';\n\nexport type ViewportChangeCallback = (state: ViewportState) => void;\n\n/**\n * Manages pan/zoom on the world container.\n * Listens for wheel (zoom) and middle-click / space+drag (pan).\n */\nexport class ViewportManager {\n private world: Container;\n private stage: Container;\n private _state: ViewportState = { x: 0, y: 0, zoom: 1 };\n private onChange?: ViewportChangeCallback;\n\n private _panning = false;\n private _panStart = { x: 0, y: 0 };\n private _worldStart = { x: 0, y: 0 };\n private _spaceHeld = false;\n /** True while a pan was triggered by the right mouse button. */\n private _rightPanning = false;\n /** Whether the current right-button pan actually moved (used to suppress the context menu). */\n private _rightPanMoved = false;\n /** True while a multi-touch gesture (pinch zoom / two-finger pan) is active.\n * Set by FlowCanvas's native touch handlers; while set, pointer-driven\n * interactions (marquee, node drag, tap-to-place) must be suppressed. */\n multiTouchActive = false;\n\n constructor(world: Container, stage: Container, onChange?: ViewportChangeCallback) {\n this.world = world;\n this.stage = stage;\n this.onChange = onChange;\n this._bindStageEvents();\n this._bindKeyEvents();\n }\n\n get state(): ViewportState {\n return { ...this._state };\n }\n\n setState(s: Partial<ViewportState>): void {\n const nx = s.x !== undefined ? s.x : this._state.x;\n const ny = s.y !== undefined ? s.y : this._state.y;\n const nz = s.zoom !== undefined\n ? Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, s.zoom))\n : this._state.zoom;\n // Skip if nothing actually changed — avoids a needless onChange emit that\n // could feed back into React state and cause viewport jitter.\n if (nx === this._state.x && ny === this._state.y && nz === this._state.zoom) return;\n this._state.x = nx;\n this._state.y = ny;\n this._state.zoom = nz;\n this._applyTransform();\n }\n\n /** Start a pan from an external trigger (e.g. pan tool mode) */\n startPan(sx: number, sy: number): void {\n this._panning = true;\n this._panStart = { x: sx, y: sy };\n this._worldStart = { x: this._state.x, y: this._state.y };\n }\n\n /** Abort any in-progress pan (used when a multi-touch gesture takes over). */\n cancelPan(): void {\n this._panning = false;\n this._rightPanning = false;\n }\n\n /** Smoothly animate to a target state */\n animateTo(target: ViewportState, duration = 300): void {\n const start = { ...this._state };\n const startTime = performance.now();\n const tick = () => {\n const elapsed = performance.now() - startTime;\n const t = Math.min(1, elapsed / duration);\n const ease = t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;\n this._state.x = start.x + (target.x - start.x) * ease;\n this._state.y = start.y + (target.y - start.y) * ease;\n this._state.zoom = start.zoom + (target.zoom - start.zoom) * ease;\n this._applyTransform();\n if (t < 1) requestAnimationFrame(tick);\n };\n requestAnimationFrame(tick);\n }\n\n /** Convert screen coords to world coords */\n screenToWorld(sx: number, sy: number): { x: number; y: number } {\n return {\n x: (sx - this._state.x) / this._state.zoom,\n y: (sy - this._state.y) / this._state.zoom,\n };\n }\n\n /** Convert world coords to screen coords */\n worldToScreen(wx: number, wy: number): { x: number; y: number } {\n return {\n x: wx * this._state.zoom + this._state.x,\n y: wy * this._state.zoom + this._state.y,\n };\n }\n\n private _applyTransform(): void {\n this.world.position.set(this._state.x, this._state.y);\n this.world.scale.set(this._state.zoom);\n this.onChange?.(this.state);\n }\n\n private _bindStageEvents(): void {\n // NOTE: wheel/zoom is handled by a native, non-passive DOM listener in\n // FlowCanvas (see `zoomAt`) so it can call preventDefault() and stop the\n // browser's Ctrl/Cmd+wheel page zoom. Pixi's federated wheel event cannot\n // reliably prevent that native gesture.\n this.stage.on('pointerdown', this._onPointerDown, this);\n this.stage.on('pointermove', this._onPointerMove, this);\n this.stage.on('pointerup', this._onPointerUp, this);\n this.stage.on('pointerupoutside', this._onPointerUp, this);\n }\n\n private _bindKeyEvents(): void {\n this._handleKeyDown = this._handleKeyDown.bind(this);\n this._handleKeyUp = this._handleKeyUp.bind(this);\n window.addEventListener('keydown', this._handleKeyDown);\n window.addEventListener('keyup', this._handleKeyUp);\n }\n\n private _handleKeyDown(e: KeyboardEvent): void {\n if (e.code === 'Space') this._spaceHeld = true;\n }\n\n private _handleKeyUp(e: KeyboardEvent): void {\n if (e.code === 'Space') {\n this._spaceHeld = false;\n this._panning = false;\n }\n }\n\n /**\n * Zoom by a multiplicative factor around a screen-space anchor point.\n * Driven by a native wheel listener so the browser page zoom can be blocked.\n */\n zoomAt(sx: number, sy: number, factor: number): void {\n const oldZoom = this._state.zoom;\n const newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, oldZoom * factor));\n if (newZoom === oldZoom) return;\n this._state.x = sx - (sx - this._state.x) * (newZoom / oldZoom);\n this._state.y = sy - (sy - this._state.y) * (newZoom / oldZoom);\n this._state.zoom = newZoom;\n this._applyTransform();\n }\n\n private _onPointerDown = (e: FederatedPointerEvent): void => {\n if (this.multiTouchActive) return;\n const isMiddle = e.button === 1;\n const isRight = e.button === 2;\n if (isMiddle || isRight || this._spaceHeld) {\n this._panning = true;\n this._rightPanning = isRight;\n this._rightPanMoved = false;\n this._panStart = { x: e.global.x, y: e.global.y };\n this._worldStart = { x: this._state.x, y: this._state.y };\n }\n };\n\n private _onPointerMove = (e: FederatedPointerEvent): void => {\n if (!this._panning || this.multiTouchActive) return;\n const dx = e.global.x - this._panStart.x;\n const dy = e.global.y - this._panStart.y;\n // Mark a right-button pan as \"moved\" once it passes a small threshold so\n // that a plain right-click (no drag) still opens the context menu.\n if (this._rightPanning && (Math.abs(dx) > 3 || Math.abs(dy) > 3)) {\n this._rightPanMoved = true;\n }\n this._state.x = this._worldStart.x + dx;\n this._state.y = this._worldStart.y + dy;\n this._applyTransform();\n };\n\n private _onPointerUp = (): void => {\n this._panning = false;\n this._rightPanning = false;\n };\n\n get isPanning(): boolean {\n return this._panning;\n }\n\n get isSpaceHeld(): boolean {\n return this._spaceHeld;\n }\n\n /**\n * Whether the most recent right-button interaction was a pan drag (moved past\n * the threshold). Consumers use this to suppress the context menu after a\n * right-drag pan. The flag resets on the next pointer down.\n */\n get didRightPan(): boolean {\n return this._rightPanMoved;\n }\n\n destroy(): void {\n window.removeEventListener('keydown', this._handleKeyDown);\n window.removeEventListener('keyup', this._handleKeyUp);\n this.stage.off('pointerdown', this._onPointerDown, this);\n this.stage.off('pointermove', this._onPointerMove, this);\n this.stage.off('pointerup', this._onPointerUp, this);\n this.stage.off('pointerupoutside', this._onPointerUp, this);\n }\n}\n"],"mappings":"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"}
@@ -5,6 +5,8 @@ export interface ContextMenuState {
5
5
  y: number;
6
6
  nodeId: string | null;
7
7
  }
8
+ /** Where an editing panel lives: left dock, right dock or a floating overlay. */
9
+ export type FlowPanelDock = 'left' | 'right' | 'float';
8
10
  export interface FocusOnRectOptions {
9
11
  /** Screen-space padding around the focused rectangle, in pixels. */
10
12
  padding?: number;
@@ -60,8 +62,10 @@ export interface FlowUIContextValue {
60
62
  setPendingPlacement: (p: PendingPlacement | null) => void;
61
63
  contextMenu: ContextMenuState | null;
62
64
  setContextMenu: (m: ContextMenuState | null) => void;
63
- detachedTabs: Set<string>;
64
- toggleDetach: (tabId: string) => void;
65
+ /** Dock assignment per panel id. Panels not listed default to the left
66
+ * dock; users can move them to the right dock or let them float. */
67
+ panelDocks: Record<string, FlowPanelDock>;
68
+ setPanelDock: (tabId: string, dock: FlowPanelDock) => void;
65
69
  /** PPT-style presentation mode. When set, the overlay animates the viewport
66
70
  * between animation anchors, navigable via arrow keys. */
67
71
  presentation: PresentationState | null;
@@ -1,118 +1,118 @@
1
- import { createContext as lt, useCallback as o, useContext as gt, useMemo as ft, useRef as w, useState as t } from "react";
1
+ import { createContext as dt, useCallback as o, useContext as ft, useMemo as gt, useRef as w, useState as t } from "react";
2
2
  import { jsx as pt } from "react/jsx-runtime";
3
- var b = lt(null);
4
- function mt() {
5
- const c = gt(b);
3
+ var T = dt(null);
4
+ function St() {
5
+ const c = ft(T);
6
6
  if (!c) throw new Error("useFlowUI must be used within FlowUIProvider");
7
7
  return c;
8
8
  }
9
- function xt({ showMiniMap: c = !0, showGrid: V = !0, gridSize: J = 20, showAI: j = !0, readOnly: s = !1, children: k }) {
10
- const [a, q] = t("select"), [d, B] = t("smoothstep"), [h, l] = t({
9
+ function mt({ showMiniMap: c = !0, showGrid: V = !0, gridSize: b = 20, showAI: J = !0, readOnly: s = !1, children: j }) {
10
+ const [a, q] = t("select"), [l, B] = t("smoothstep"), [h, d] = t({
11
11
  x: 0,
12
12
  y: 0,
13
13
  zoom: 1
14
- }), g = w(0), H = o((e) => {
15
- g.current += 1, l(e);
14
+ }), f = w(0), H = o((e) => {
15
+ f.current += 1, d(e);
16
16
  }, []), K = o((e) => {
17
- l(e);
18
- }, []), [f, L] = t(!1), [p, Q] = t(null), W = o((e) => {
17
+ d(e);
18
+ }, []), [g, L] = t(!1), [p, Q] = t(null), W = o((e) => {
19
19
  s || Q(e);
20
- }, [s]), r = w(0), [i, S] = t(!1), I = o(() => {
21
- r.current += 1, i || S(!0);
22
- }, [i]), M = o(() => {
23
- r.current = Math.max(0, r.current - 1), r.current === 0 && S(!1);
24
- }, []), [m, X] = t(!0), [x, Y] = t(!0), [R, Z] = t(!0), [P, _] = t(!0), [F, $] = t(j), [v, O] = t(c), [C, y] = t(V), [N, tt] = t(!0), [A, et] = t(J), [D, ot] = t(null), [T, st] = t(null), nt = o((e) => {
20
+ }, [s]), r = w(0), [i, I] = t(!1), M = o(() => {
21
+ r.current += 1, i || I(!0);
22
+ }, [i]), P = o(() => {
23
+ r.current = Math.max(0, r.current - 1), r.current === 0 && I(!1);
24
+ }, []), [S, X] = t(!0), [m, Y] = t(!0), [R, Z] = t(!0), [x, _] = t(!0), [F, $] = t(J), [C, O] = t(c), [D, y] = t(V), [N, tt] = t(!0), [A, et] = t(b), [v, ot] = t(null), [U, st] = t(null), nt = o((e) => {
25
25
  s || ot(e);
26
26
  }, [s]), ct = o((e) => {
27
27
  s || st(e);
28
- }, [s]), [U, rt] = t(null), [z, it] = t(/* @__PURE__ */ new Set()), E = o((e) => {
29
- it((u) => {
30
- const n = new Set(u);
31
- return n.has(e) ? n.delete(e) : n.add(e), n;
28
+ }, [s]), [z, rt] = t(null), [E, it] = t({}), G = o((e, u) => {
29
+ it((n) => n[e] === u ? n : {
30
+ ...n,
31
+ [e]: u
32
32
  });
33
- }, []), G = w(() => {
34
- }), ut = o((e, u, n, dt, ht) => {
35
- G.current(e, u, n, dt, ht);
33
+ }, []), k = w(() => {
34
+ }), ut = o((e, u, n, lt, ht) => {
35
+ k.current(e, u, n, lt, ht);
36
36
  }, []), wt = o((e) => {
37
- G.current = e;
38
- }, []), at = ft(() => ({
37
+ k.current = e;
38
+ }, []), at = gt(() => ({
39
39
  toolMode: a,
40
40
  setToolMode: q,
41
- edgeType: d,
41
+ edgeType: l,
42
42
  setEdgeType: B,
43
43
  viewport: h,
44
44
  setViewport: H,
45
45
  setViewportInternal: K,
46
- viewportReqRef: g,
46
+ viewportReqRef: f,
47
47
  focusOnRect: ut,
48
48
  setFocusOnRect: wt,
49
- autoFocus: f,
49
+ autoFocus: g,
50
50
  setAutoFocus: L,
51
51
  editingNodeId: p,
52
52
  setEditingNodeId: W,
53
53
  isNodeDragging: i,
54
- beginNodeDrag: I,
55
- endNodeDrag: M,
56
- showPalette: m,
54
+ beginNodeDrag: M,
55
+ endNodeDrag: P,
56
+ showPalette: S,
57
57
  setShowPalette: X,
58
- showInspector: x,
58
+ showInspector: m,
59
59
  setShowInspector: Y,
60
60
  showOutline: R,
61
61
  setShowOutline: Z,
62
- showJson: P,
62
+ showJson: x,
63
63
  setShowJson: _,
64
64
  showAI: F,
65
65
  setShowAI: $,
66
- showMiniMap: v,
66
+ showMiniMap: C,
67
67
  setShowMiniMap: O,
68
- showGrid: C,
68
+ showGrid: D,
69
69
  setShowGrid: y,
70
70
  showAnchors: N,
71
71
  setShowAnchors: tt,
72
72
  gridSize: A,
73
73
  setGridSize: et,
74
- pendingPlacement: D,
74
+ pendingPlacement: v,
75
75
  setPendingPlacement: nt,
76
- contextMenu: T,
76
+ contextMenu: U,
77
77
  setContextMenu: ct,
78
- presentation: U,
78
+ presentation: z,
79
79
  setPresentation: rt,
80
- detachedTabs: z,
81
- toggleDetach: E,
80
+ panelDocks: E,
81
+ setPanelDock: G,
82
82
  readOnly: s
83
83
  }), [
84
84
  a,
85
- d,
85
+ l,
86
86
  h,
87
- f,
87
+ g,
88
88
  p,
89
89
  i,
90
- I,
91
90
  M,
91
+ P,
92
+ S,
92
93
  m,
93
- x,
94
94
  R,
95
- P,
95
+ x,
96
96
  F,
97
- v,
98
97
  C,
98
+ D,
99
99
  N,
100
100
  A,
101
- D,
102
- T,
101
+ v,
103
102
  U,
104
103
  z,
105
104
  E,
105
+ G,
106
106
  s
107
107
  ]);
108
- return /* @__PURE__ */ pt(b.Provider, {
108
+ return /* @__PURE__ */ pt(T.Provider, {
109
109
  value: at,
110
- children: k
110
+ children: j
111
111
  });
112
112
  }
113
113
  export {
114
- xt as FlowUIProvider,
115
- mt as useFlowUI
114
+ mt as FlowUIProvider,
115
+ St as useFlowUI
116
116
  };
117
117
 
118
118
  //# sourceMappingURL=FlowUIContext.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FlowUIContext.js","names":[],"sources":["../../../../../src/components/pro/Flow/context/FlowUIContext.tsx"],"sourcesContent":["import React, { createContext, useContext, useMemo, useState, useRef, useCallback } from 'react';\nimport type { FlowToolMode, FlowEdgeTypeName, FlowShapeType, FlowNodeData, ViewportState } from '../types';\n\nexport interface ContextMenuState {\n x: number;\n y: number;\n nodeId: string | null;\n}\n\nexport interface FocusOnRectOptions {\n /** Screen-space padding around the focused rectangle, in pixels. */\n padding?: number;\n /** Maximum zoom allowed for this focus operation. */\n maxZoom?: number;\n /** Viewport transition duration in milliseconds. */\n duration?: number;\n /** Keep the current zoom and only animate the focused rectangle to center. */\n preserveZoom?: boolean;\n}\n\nexport interface FlowUIContextValue {\n toolMode: FlowToolMode;\n setToolMode: (mode: FlowToolMode) => void;\n edgeType: FlowEdgeTypeName;\n setEdgeType: (type: FlowEdgeTypeName) => void;\n viewport: ViewportState;\n setViewport: (v: ViewportState) => void;\n /** Internal VM→React bridge setter (does not request a VM apply). */\n setViewportInternal: (v: ViewportState) => void;\n /** Monotonic counter of external viewport commands. */\n viewportReqRef: React.MutableRefObject<number>;\n focusOnRect: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void;\n setFocusOnRect: (fn: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void) => void;\n autoFocus: boolean;\n setAutoFocus: (v: boolean) => void;\n editingNodeId: string | null;\n setEditingNodeId: (id: string | null) => void;\n /** True while any node/line is being dragged. Used to suppress interactive\n * DOM overlays (e.g. HTML iframes) that would otherwise swallow pointerup. */\n isNodeDragging: boolean;\n beginNodeDrag: () => void;\n endNodeDrag: () => void;\n showPalette: boolean;\n setShowPalette: (v: boolean) => void;\n showInspector: boolean;\n setShowInspector: (v: boolean) => void;\n showOutline: boolean;\n setShowOutline: (v: boolean) => void;\n showJson: boolean;\n setShowJson: (v: boolean) => void;\n showAI: boolean;\n setShowAI: (v: boolean) => void;\n showMiniMap: boolean;\n setShowMiniMap: (v: boolean) => void;\n showGrid: boolean;\n setShowGrid: (v: boolean) => void;\n /** Global editor-only visibility for animation anchor objects. */\n showAnchors: boolean;\n setShowAnchors: (v: boolean) => void;\n gridSize: number;\n setGridSize: (s: number) => void;\n pendingPlacement: PendingPlacement | null;\n setPendingPlacement: (p: PendingPlacement | null) => void;\n contextMenu: ContextMenuState | null;\n setContextMenu: (m: ContextMenuState | null) => void;\n detachedTabs: Set<string>;\n toggleDetach: (tabId: string) => void;\n /** PPT-style presentation mode. When set, the overlay animates the viewport\n * between animation anchors, navigable via arrow keys. */\n presentation: PresentationState | null;\n setPresentation: (p: PresentationState | null) => void;\n /** When true, all editing affordances are suppressed (read-only preview). */\n readOnly: boolean;\n}\n\nexport interface PresentationState {\n /** Index into the anchor sequence (0-based). */\n current: number;\n /** Whether to show the active anchor frame and sequence badge in preview. */\n showAnchor?: boolean;\n}\n\nexport interface PendingPlacement {\n type: FlowShapeType;\n width: number;\n height: number;\n data: FlowNodeData;\n}\n\nconst FlowUIContext = createContext<FlowUIContextValue | null>(null);\n\nexport function useFlowUI(): FlowUIContextValue {\n const ctx = useContext(FlowUIContext);\n if (!ctx) throw new Error('useFlowUI must be used within FlowUIProvider');\n return ctx;\n}\n\nexport interface FlowUIProviderProps {\n showMiniMap?: boolean;\n showGrid?: boolean;\n gridSize?: number;\n showAI?: boolean;\n /** Suppresses context menu, inline text editing and palette placement.\n * Document mutations are additionally blocked in `Flow` (single choke point). */\n readOnly?: boolean;\n children: React.ReactNode;\n}\n\nexport function FlowUIProvider({ showMiniMap: initMiniMap = true, showGrid: initGrid = true, gridSize: initGridSize = 20, showAI: initShowAI = true, readOnly = false, children }: FlowUIProviderProps) {\n const [toolMode, setToolMode] = useState<FlowToolMode>('select');\n const [edgeType, setEdgeType] = useState<FlowEdgeTypeName>('smoothstep');\n const [viewport, setViewportState] = useState<ViewportState>({ x: 0, y: 0, zoom: 1 });\n // Distinguishes EXTERNAL viewport commands (toolbar, minimap, outline) from\n // internal echoes emitted by the ViewportManager. Only external commands\n // should be pushed back into the VM; echoing VM changes causes jitter.\n const viewportReqRef = useRef(0);\n const setViewport = useCallback((v: ViewportState) => {\n viewportReqRef.current += 1;\n setViewportState(v);\n }, []);\n // Internal setter used by the VM→React bridge; does NOT bump the request\n // counter, so the sync effect will not echo it back to the VM.\n const setViewportInternal = useCallback((v: ViewportState) => {\n setViewportState(v);\n }, []);\n const [autoFocus, setAutoFocus] = useState(false);\n const [editingNodeId, setEditingNodeIdRaw] = useState<string | null>(null);\n // In read-only mode the inline node/edge text editor must never open.\n const setEditingNodeId = useCallback((id: string | null) => {\n if (readOnly) return;\n setEditingNodeIdRaw(id);\n }, [readOnly]);\n // Counter so concurrent drags (multi-node) don't flip the flag early.\n const dragCountRef = useRef(0);\n const [isNodeDragging, setIsNodeDragging] = useState(false);\n const beginNodeDrag = useCallback(() => {\n dragCountRef.current += 1;\n if (!isNodeDragging) setIsNodeDragging(true);\n }, [isNodeDragging]);\n const endNodeDrag = useCallback(() => {\n dragCountRef.current = Math.max(0, dragCountRef.current - 1);\n if (dragCountRef.current === 0) setIsNodeDragging(false);\n }, []);\n const [showPalette, setShowPalette] = useState(true);\n const [showInspector, setShowInspector] = useState(true);\n const [showOutline, setShowOutline] = useState(true);\n const [showJson, setShowJson] = useState(true);\n const [showAI, setShowAI] = useState(initShowAI);\n const [showMiniMap, setShowMiniMap] = useState(initMiniMap);\n const [showGrid, setShowGrid] = useState(initGrid);\n const [showAnchors, setShowAnchors] = useState(true);\n const [gridSize, setGridSize] = useState(initGridSize);\n const [pendingPlacement, setPendingPlacementRaw] = useState<PendingPlacement | null>(null);\n const [contextMenu, setContextMenuRaw] = useState<ContextMenuState | null>(null);\n // Read-only: palette drops and the right-click menu are editing affordances.\n const setPendingPlacement = useCallback((p: PendingPlacement | null) => {\n if (readOnly) return;\n setPendingPlacementRaw(p);\n }, [readOnly]);\n const setContextMenu = useCallback((m: ContextMenuState | null) => {\n if (readOnly) return;\n setContextMenuRaw(m);\n }, [readOnly]);\n const [presentation, setPresentation] = useState<PresentationState | null>(null);\n const [detachedTabs, setDetachedTabs] = useState<Set<string>>(new Set());\n const toggleDetach = useCallback((tabId: string) => {\n setDetachedTabs(prev => {\n const next = new Set(prev);\n if (next.has(tabId)) next.delete(tabId); else next.add(tabId);\n return next;\n });\n }, []);\n\n const focusOnRectRef = useRef<(wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void>(() => {});\n const focusOnRect = useCallback((wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => {\n focusOnRectRef.current(wx, wy, ww, wh, options);\n }, []);\n const setFocusOnRect = useCallback((fn: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void) => {\n focusOnRectRef.current = fn;\n }, []);\n\n const value = useMemo<FlowUIContextValue>(() => ({\n toolMode, setToolMode,\n edgeType, setEdgeType,\n viewport, setViewport,\n setViewportInternal,\n viewportReqRef,\n focusOnRect, setFocusOnRect,\n autoFocus, setAutoFocus,\n editingNodeId, setEditingNodeId,\n isNodeDragging, beginNodeDrag, endNodeDrag,\n showPalette, setShowPalette,\n showInspector, setShowInspector,\n showOutline, setShowOutline,\n showJson, setShowJson,\n showAI, setShowAI,\n showMiniMap, setShowMiniMap,\n showGrid, setShowGrid,\n showAnchors, setShowAnchors,\n gridSize, setGridSize,\n pendingPlacement, setPendingPlacement,\n contextMenu, setContextMenu,\n presentation, setPresentation,\n detachedTabs, toggleDetach,\n readOnly,\n }), [toolMode, edgeType, viewport, autoFocus, editingNodeId, isNodeDragging, beginNodeDrag, endNodeDrag, showPalette, showInspector, showOutline, showJson, showAI, showMiniMap, showGrid, showAnchors, gridSize, pendingPlacement, contextMenu, presentation, detachedTabs, toggleDetach, readOnly]);\n\n return <FlowUIContext.Provider value={value}>{children}</FlowUIContext.Provider>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FlowUIContext.js","names":[],"sources":["../../../../../src/components/pro/Flow/context/FlowUIContext.tsx"],"sourcesContent":["import React, { createContext, useContext, useMemo, useState, useRef, useCallback } from 'react';\nimport type { FlowToolMode, FlowEdgeTypeName, FlowShapeType, FlowNodeData, ViewportState } from '../types';\n\nexport interface ContextMenuState {\n x: number;\n y: number;\n nodeId: string | null;\n}\n\n/** Where an editing panel lives: left dock, right dock or a floating overlay. */\nexport type FlowPanelDock = 'left' | 'right' | 'float';\n\nexport interface FocusOnRectOptions {\n /** Screen-space padding around the focused rectangle, in pixels. */\n padding?: number;\n /** Maximum zoom allowed for this focus operation. */\n maxZoom?: number;\n /** Viewport transition duration in milliseconds. */\n duration?: number;\n /** Keep the current zoom and only animate the focused rectangle to center. */\n preserveZoom?: boolean;\n}\n\nexport interface FlowUIContextValue {\n toolMode: FlowToolMode;\n setToolMode: (mode: FlowToolMode) => void;\n edgeType: FlowEdgeTypeName;\n setEdgeType: (type: FlowEdgeTypeName) => void;\n viewport: ViewportState;\n setViewport: (v: ViewportState) => void;\n /** Internal VM→React bridge setter (does not request a VM apply). */\n setViewportInternal: (v: ViewportState) => void;\n /** Monotonic counter of external viewport commands. */\n viewportReqRef: React.MutableRefObject<number>;\n focusOnRect: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void;\n setFocusOnRect: (fn: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void) => void;\n autoFocus: boolean;\n setAutoFocus: (v: boolean) => void;\n editingNodeId: string | null;\n setEditingNodeId: (id: string | null) => void;\n /** True while any node/line is being dragged. Used to suppress interactive\n * DOM overlays (e.g. HTML iframes) that would otherwise swallow pointerup. */\n isNodeDragging: boolean;\n beginNodeDrag: () => void;\n endNodeDrag: () => void;\n showPalette: boolean;\n setShowPalette: (v: boolean) => void;\n showInspector: boolean;\n setShowInspector: (v: boolean) => void;\n showOutline: boolean;\n setShowOutline: (v: boolean) => void;\n showJson: boolean;\n setShowJson: (v: boolean) => void;\n showAI: boolean;\n setShowAI: (v: boolean) => void;\n showMiniMap: boolean;\n setShowMiniMap: (v: boolean) => void;\n showGrid: boolean;\n setShowGrid: (v: boolean) => void;\n /** Global editor-only visibility for animation anchor objects. */\n showAnchors: boolean;\n setShowAnchors: (v: boolean) => void;\n gridSize: number;\n setGridSize: (s: number) => void;\n pendingPlacement: PendingPlacement | null;\n setPendingPlacement: (p: PendingPlacement | null) => void;\n contextMenu: ContextMenuState | null;\n setContextMenu: (m: ContextMenuState | null) => void;\n /** Dock assignment per panel id. Panels not listed default to the left\n * dock; users can move them to the right dock or let them float. */\n panelDocks: Record<string, FlowPanelDock>;\n setPanelDock: (tabId: string, dock: FlowPanelDock) => void;\n /** PPT-style presentation mode. When set, the overlay animates the viewport\n * between animation anchors, navigable via arrow keys. */\n presentation: PresentationState | null;\n setPresentation: (p: PresentationState | null) => void;\n /** When true, all editing affordances are suppressed (read-only preview). */\n readOnly: boolean;\n}\n\nexport interface PresentationState {\n /** Index into the anchor sequence (0-based). */\n current: number;\n /** Whether to show the active anchor frame and sequence badge in preview. */\n showAnchor?: boolean;\n}\n\nexport interface PendingPlacement {\n type: FlowShapeType;\n width: number;\n height: number;\n data: FlowNodeData;\n}\n\nconst FlowUIContext = createContext<FlowUIContextValue | null>(null);\n\nexport function useFlowUI(): FlowUIContextValue {\n const ctx = useContext(FlowUIContext);\n if (!ctx) throw new Error('useFlowUI must be used within FlowUIProvider');\n return ctx;\n}\n\nexport interface FlowUIProviderProps {\n showMiniMap?: boolean;\n showGrid?: boolean;\n gridSize?: number;\n showAI?: boolean;\n /** Suppresses context menu, inline text editing and palette placement.\n * Document mutations are additionally blocked in `Flow` (single choke point). */\n readOnly?: boolean;\n children: React.ReactNode;\n}\n\nexport function FlowUIProvider({ showMiniMap: initMiniMap = true, showGrid: initGrid = true, gridSize: initGridSize = 20, showAI: initShowAI = true, readOnly = false, children }: FlowUIProviderProps) {\n const [toolMode, setToolMode] = useState<FlowToolMode>('select');\n const [edgeType, setEdgeType] = useState<FlowEdgeTypeName>('smoothstep');\n const [viewport, setViewportState] = useState<ViewportState>({ x: 0, y: 0, zoom: 1 });\n // Distinguishes EXTERNAL viewport commands (toolbar, minimap, outline) from\n // internal echoes emitted by the ViewportManager. Only external commands\n // should be pushed back into the VM; echoing VM changes causes jitter.\n const viewportReqRef = useRef(0);\n const setViewport = useCallback((v: ViewportState) => {\n viewportReqRef.current += 1;\n setViewportState(v);\n }, []);\n // Internal setter used by the VM→React bridge; does NOT bump the request\n // counter, so the sync effect will not echo it back to the VM.\n const setViewportInternal = useCallback((v: ViewportState) => {\n setViewportState(v);\n }, []);\n const [autoFocus, setAutoFocus] = useState(false);\n const [editingNodeId, setEditingNodeIdRaw] = useState<string | null>(null);\n // In read-only mode the inline node/edge text editor must never open.\n const setEditingNodeId = useCallback((id: string | null) => {\n if (readOnly) return;\n setEditingNodeIdRaw(id);\n }, [readOnly]);\n // Counter so concurrent drags (multi-node) don't flip the flag early.\n const dragCountRef = useRef(0);\n const [isNodeDragging, setIsNodeDragging] = useState(false);\n const beginNodeDrag = useCallback(() => {\n dragCountRef.current += 1;\n if (!isNodeDragging) setIsNodeDragging(true);\n }, [isNodeDragging]);\n const endNodeDrag = useCallback(() => {\n dragCountRef.current = Math.max(0, dragCountRef.current - 1);\n if (dragCountRef.current === 0) setIsNodeDragging(false);\n }, []);\n const [showPalette, setShowPalette] = useState(true);\n const [showInspector, setShowInspector] = useState(true);\n const [showOutline, setShowOutline] = useState(true);\n const [showJson, setShowJson] = useState(true);\n const [showAI, setShowAI] = useState(initShowAI);\n const [showMiniMap, setShowMiniMap] = useState(initMiniMap);\n const [showGrid, setShowGrid] = useState(initGrid);\n const [showAnchors, setShowAnchors] = useState(true);\n const [gridSize, setGridSize] = useState(initGridSize);\n const [pendingPlacement, setPendingPlacementRaw] = useState<PendingPlacement | null>(null);\n const [contextMenu, setContextMenuRaw] = useState<ContextMenuState | null>(null);\n // Read-only: palette drops and the right-click menu are editing affordances.\n const setPendingPlacement = useCallback((p: PendingPlacement | null) => {\n if (readOnly) return;\n setPendingPlacementRaw(p);\n }, [readOnly]);\n const setContextMenu = useCallback((m: ContextMenuState | null) => {\n if (readOnly) return;\n setContextMenuRaw(m);\n }, [readOnly]);\n const [presentation, setPresentation] = useState<PresentationState | null>(null);\n const [panelDocks, setPanelDocks] = useState<Record<string, FlowPanelDock>>({});\n const setPanelDock = useCallback((tabId: string, dock: FlowPanelDock) => {\n setPanelDocks(prev => (prev[tabId] === dock ? prev : { ...prev, [tabId]: dock }));\n }, []);\n\n const focusOnRectRef = useRef<(wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void>(() => {});\n const focusOnRect = useCallback((wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => {\n focusOnRectRef.current(wx, wy, ww, wh, options);\n }, []);\n const setFocusOnRect = useCallback((fn: (wx: number, wy: number, ww: number, wh: number, options?: FocusOnRectOptions) => void) => {\n focusOnRectRef.current = fn;\n }, []);\n\n const value = useMemo<FlowUIContextValue>(() => ({\n toolMode, setToolMode,\n edgeType, setEdgeType,\n viewport, setViewport,\n setViewportInternal,\n viewportReqRef,\n focusOnRect, setFocusOnRect,\n autoFocus, setAutoFocus,\n editingNodeId, setEditingNodeId,\n isNodeDragging, beginNodeDrag, endNodeDrag,\n showPalette, setShowPalette,\n showInspector, setShowInspector,\n showOutline, setShowOutline,\n showJson, setShowJson,\n showAI, setShowAI,\n showMiniMap, setShowMiniMap,\n showGrid, setShowGrid,\n showAnchors, setShowAnchors,\n gridSize, setGridSize,\n pendingPlacement, setPendingPlacement,\n contextMenu, setContextMenu,\n presentation, setPresentation,\n panelDocks, setPanelDock,\n readOnly,\n }), [toolMode, edgeType, viewport, autoFocus, editingNodeId, isNodeDragging, beginNodeDrag, endNodeDrag, showPalette, showInspector, showOutline, showJson, showAI, showMiniMap, showGrid, showAnchors, gridSize, pendingPlacement, contextMenu, presentation, panelDocks, setPanelDock, readOnly]);\n\n return <FlowUIContext.Provider value={value}>{children}</FlowUIContext.Provider>;\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { type RefObject } from 'react';
2
+ /**
3
+ * Container-width based small-screen ("compact") detection.
4
+ *
5
+ * Uses a ResizeObserver on the Flow root element rather than
6
+ * `window.matchMedia`, so the mode follows the space the component actually
7
+ * gets: a phone (narrow container) switches to the compact layout, while the
8
+ * same component embedded in a narrow column on a desktop page does too.
9
+ *
10
+ * In compact mode the Flow editor hides the left/right panel docks and instead
11
+ * offers a single panel switcher at the top with full-screen panels.
12
+ */
13
+ export declare function useCompactMode(ref: RefObject<HTMLElement | null>, breakpoint?: number): boolean;
@@ -0,0 +1,20 @@
1
+ import { useEffect as i, useState as f } from "react";
2
+ function m(t, c = 768) {
3
+ const [r, u] = f(!1);
4
+ return i(() => {
5
+ const e = t.current;
6
+ if (!e) return;
7
+ const o = () => {
8
+ const s = e.clientWidth;
9
+ s > 0 && u(s < c);
10
+ };
11
+ o();
12
+ const n = new ResizeObserver(o);
13
+ return n.observe(e), () => n.disconnect();
14
+ }, [t, c]), r;
15
+ }
16
+ export {
17
+ m as useCompactMode
18
+ };
19
+
20
+ //# sourceMappingURL=useCompactMode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCompactMode.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useCompactMode.ts"],"sourcesContent":["import { useEffect, useState, type RefObject } from 'react';\n\n/**\n * Container-width based small-screen (\"compact\") detection.\n *\n * Uses a ResizeObserver on the Flow root element rather than\n * `window.matchMedia`, so the mode follows the space the component actually\n * gets: a phone (narrow container) switches to the compact layout, while the\n * same component embedded in a narrow column on a desktop page does too.\n *\n * In compact mode the Flow editor hides the left/right panel docks and instead\n * offers a single panel switcher at the top with full-screen panels.\n */\nexport function useCompactMode(ref: RefObject<HTMLElement | null>, breakpoint = 768): boolean {\n const [compact, setCompact] = useState(false);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const update = () => {\n const w = el.clientWidth;\n if (w > 0) setCompact(w < breakpoint);\n };\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n return () => ro.disconnect();\n }, [ref, breakpoint]);\n\n return compact;\n}\n"],"mappings":";AAaA,SAAgB,EAAe,GAAoC,IAAa,KAAc;AAC5F,QAAM,CAAC,GAAS,CAAA,IAAc,EAAS,EAAK;AAE5C,SAAA,EAAA,MAAgB;AACd,UAAM,IAAK,EAAI;AACf,QAAI,CAAC,EAAI;AACT,UAAM,IAAA,MAAe;AACnB,YAAM,IAAI,EAAG;AACb,MAAI,IAAI,KAAG,EAAW,IAAI,CAAU;AAAA,IACtC;AACA,IAAA,EAAO;AACP,UAAM,IAAK,IAAI,eAAe,CAAM;AACpC,WAAA,EAAG,QAAQ,CAAE,GACb,MAAa,EAAG,WAAW;AAAA,EAC7B,GAAG,CAAC,GAAK,CAAU,CAAC,GAEb;AACT"}
@@ -3,12 +3,27 @@ export interface PanelPosition {
3
3
  x: number;
4
4
  y: number;
5
5
  }
6
+ export type PanelDropSide = 'left' | 'right';
7
+ export interface DraggablePanelOptions {
8
+ /** Called on pointer-up when the floating panel was dropped over a dock edge. */
9
+ onDropDock?: (side: PanelDropSide) => void;
10
+ /** Notified while dragging so the workspace can highlight the hovered dock. */
11
+ onDragHintChange?: (side: PanelDropSide | null) => void;
12
+ /** Width (px) of the drop zone along each edge of the workspace. */
13
+ edgeZone?: number;
14
+ }
6
15
  /**
7
- * Enables free-position dragging for overlay panels.
8
- * Returns a position override (null = use default CSS) and
9
- * a set of props to spread onto the panel's drag handle element.
16
+ * Enables free-position dragging for overlay panels. Returns a position
17
+ * override (null = use default CSS) and a set of props to spread onto the
18
+ * panel's drag handle element.
19
+ *
20
+ * When `onDropDock` is provided, dropping the panel within `edgeZone` of the
21
+ * workspace's left/right edge attaches it to that dock: the hovered edge is
22
+ * reported via `onDragHintChange` during the drag (so the workspace can
23
+ * highlight a drop zone) and the position resets on drop so the panel snaps
24
+ * into its dock slot.
10
25
  */
11
- export declare function useDraggablePanel(): {
26
+ export declare function useDraggablePanel({ onDropDock, onDragHintChange, edgeZone }?: DraggablePanelOptions): {
12
27
  panelStyle: import("react").CSSProperties | undefined;
13
28
  handleProps: {
14
29
  onPointerDown: (e: ReactPointerEvent<HTMLElement>) => void;
@@ -1,47 +1,54 @@
1
- import { useCallback as i, useRef as p, useState as g } from "react";
2
- function P() {
3
- const [r, l] = g(null), n = p(null), c = i((t) => {
4
- const e = t.currentTarget.closest("[data-flow-panel]");
5
- if (!e) return;
1
+ import { useCallback as i, useRef as h, useState as Y } from "react";
2
+ function R({ onDropDock: p, onDragHintChange: c, edgeZone: u = 72 } = {}) {
3
+ const [s, a] = Y(null), n = h(null), l = h(null), d = i(() => {
4
+ l.current && (l.current = null, c?.(null));
5
+ }, [c]), g = i((t) => {
6
+ const r = t.currentTarget.closest("[data-flow-panel]");
7
+ if (!r) return;
6
8
  t.preventDefault(), t.stopPropagation(), t.target.setPointerCapture(t.pointerId);
7
- const o = e.getBoundingClientRect(), s = e.offsetParent?.getBoundingClientRect() ?? {
9
+ const e = r.getBoundingClientRect(), o = r.offsetParent?.getBoundingClientRect() ?? {
8
10
  left: 0,
9
11
  top: 0
10
12
  };
11
13
  n.current = {
12
14
  startX: t.clientX,
13
15
  startY: t.clientY,
14
- originX: o.left - s.left,
15
- originY: o.top - s.top
16
+ originX: e.left - o.left,
17
+ originY: e.top - o.top,
18
+ width: e.width,
19
+ parentWidth: r.offsetParent?.clientWidth ?? e.left - o.left + e.width
16
20
  };
17
- }, []), a = i((t) => {
21
+ }, []), X = i((t) => {
18
22
  if (!n.current) return;
19
- const e = t.clientX - n.current.startX, o = t.clientY - n.current.startY;
20
- l({
21
- x: n.current.originX + e,
22
- y: n.current.originY + o
23
+ const r = t.clientX - n.current.startX, e = t.clientY - n.current.startY, o = n.current.originX + r;
24
+ a({
25
+ x: o,
26
+ y: n.current.originY + e
23
27
  });
24
- }, []), u = i(() => {
25
- n.current = null;
26
- }, []);
28
+ const P = o + n.current.width / 2, f = P < u ? "left" : P > n.current.parentWidth - u ? "right" : null;
29
+ f !== l.current && (l.current = f, c?.(f));
30
+ }, [u, c]), w = i(() => {
31
+ const t = l.current;
32
+ d(), n.current = null, t && (p?.(t), a(null));
33
+ }, [d, p]);
27
34
  return {
28
- panelStyle: r ? {
35
+ panelStyle: s ? {
29
36
  position: "absolute",
30
- left: r.x,
31
- top: r.y,
37
+ left: s.x,
38
+ top: s.y,
32
39
  right: "auto",
33
40
  bottom: "auto"
34
41
  } : void 0,
35
42
  handleProps: {
36
- onPointerDown: c,
37
- onPointerMove: a,
38
- onPointerUp: u
43
+ onPointerDown: g,
44
+ onPointerMove: X,
45
+ onPointerUp: w
39
46
  },
40
- resetPosition: () => l(null)
47
+ resetPosition: () => a(null)
41
48
  };
42
49
  }
43
50
  export {
44
- P as useDraggablePanel
51
+ R as useDraggablePanel
45
52
  };
46
53
 
47
54
  //# sourceMappingURL=useDraggablePanel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDraggablePanel.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useDraggablePanel.ts"],"sourcesContent":["import { useState, useRef, useCallback, type PointerEvent as ReactPointerEvent } from 'react';\n\nexport interface PanelPosition {\n x: number;\n y: number;\n}\n\n/**\n * Enables free-position dragging for overlay panels.\n * Returns a position override (null = use default CSS) and\n * a set of props to spread onto the panel's drag handle element.\n */\nexport function useDraggablePanel() {\n const [pos, setPos] = useState<PanelPosition | null>(null);\n const dragState = useRef<{ startX: number; startY: number; originX: number; originY: number } | null>(null);\n\n const onPointerDown = useCallback((e: ReactPointerEvent<HTMLElement>) => {\n const el = (e.currentTarget as HTMLElement).closest('[data-flow-panel]') as HTMLElement | null;\n if (!el) return;\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n const rect = el.getBoundingClientRect();\n const parentRect = el.offsetParent?.getBoundingClientRect() ?? { left: 0, top: 0 };\n dragState.current = {\n startX: e.clientX,\n startY: e.clientY,\n originX: rect.left - parentRect.left,\n originY: rect.top - parentRect.top,\n };\n }, []);\n\n const onPointerMove = useCallback((e: ReactPointerEvent<HTMLElement>) => {\n if (!dragState.current) return;\n const dx = e.clientX - dragState.current.startX;\n const dy = e.clientY - dragState.current.startY;\n setPos({\n x: dragState.current.originX + dx,\n y: dragState.current.originY + dy,\n });\n }, []);\n\n const onPointerUp = useCallback(() => {\n dragState.current = null;\n }, []);\n\n const style: React.CSSProperties | undefined = pos\n ? { position: 'absolute', left: pos.x, top: pos.y, right: 'auto', bottom: 'auto' }\n : undefined;\n\n return {\n panelStyle: style,\n handleProps: { onPointerDown, onPointerMove, onPointerUp },\n resetPosition: () => setPos(null),\n };\n}\n"],"mappings":";AAYA,SAAgB,IAAoB;AAClC,QAAM,CAAC,GAAK,CAAA,IAAU,EAA+B,IAAI,GACnD,IAAY,EAAoF,IAAI,GAEpG,IAAgB,EAAA,CAAa,MAAsC;AACvE,UAAM,IAAM,EAAE,cAA8B,QAAQ,mBAAmB;AACvE,QAAI,CAAC,EAAI;AACT,IAAA,EAAE,eAAe,GACjB,EAAE,gBAAgB,GAClB,EAAG,OAAuB,kBAAkB,EAAE,SAAS;AACvD,UAAM,IAAO,EAAG,sBAAsB,GAChC,IAAa,EAAG,cAAc,sBAAsB,KAAK;AAAA,MAAE,MAAM;AAAA,MAAG,KAAK;AAAA,IAAE;AACjF,IAAA,EAAU,UAAU;AAAA,MAClB,QAAQ,EAAE;AAAA,MACV,QAAQ,EAAE;AAAA,MACV,SAAS,EAAK,OAAO,EAAW;AAAA,MAChC,SAAS,EAAK,MAAM,EAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,CAAC,GAEC,IAAgB,EAAA,CAAa,MAAsC;AACvE,QAAI,CAAC,EAAU,QAAS;AACxB,UAAM,IAAK,EAAE,UAAU,EAAU,QAAQ,QACnC,IAAK,EAAE,UAAU,EAAU,QAAQ;AACzC,IAAA,EAAO;AAAA,MACL,GAAG,EAAU,QAAQ,UAAU;AAAA,MAC/B,GAAG,EAAU,QAAQ,UAAU;AAAA,IACjC,CAAC;AAAA,EACH,GAAG,CAAC,CAAC,GAEC,IAAc,EAAA,MAAkB;AACpC,IAAA,EAAU,UAAU;AAAA,EACtB,GAAG,CAAC,CAAC;AAML,SAAO;AAAA,IACL,YAL6C,IAC3C;AAAA,MAAE,UAAU;AAAA,MAAY,MAAM,EAAI;AAAA,MAAG,KAAK,EAAI;AAAA,MAAG,OAAO;AAAA,MAAQ,QAAQ;AAAA,IAAO,IAC/E;AAAA,IAIF,aAAa;AAAA,MAAE,eAAA;AAAA,MAAe,eAAA;AAAA,MAAe,aAAA;AAAA,IAAY;AAAA,IACzD,eAAA,MAAqB,EAAO,IAAI;AAAA,EAClC;AACF"}
1
+ {"version":3,"file":"useDraggablePanel.js","names":[],"sources":["../../../../../src/components/pro/Flow/hooks/useDraggablePanel.ts"],"sourcesContent":["import { useState, useRef, useCallback, type PointerEvent as ReactPointerEvent } from 'react';\n\nexport interface PanelPosition {\n x: number;\n y: number;\n}\n\nexport type PanelDropSide = 'left' | 'right';\n\nexport interface DraggablePanelOptions {\n /** Called on pointer-up when the floating panel was dropped over a dock edge. */\n onDropDock?: (side: PanelDropSide) => void;\n /** Notified while dragging so the workspace can highlight the hovered dock. */\n onDragHintChange?: (side: PanelDropSide | null) => void;\n /** Width (px) of the drop zone along each edge of the workspace. */\n edgeZone?: number;\n}\n\ninterface DragState {\n startX: number;\n startY: number;\n originX: number;\n originY: number;\n width: number;\n parentWidth: number;\n}\n\n/**\n * Enables free-position dragging for overlay panels. Returns a position\n * override (null = use default CSS) and a set of props to spread onto the\n * panel's drag handle element.\n *\n * When `onDropDock` is provided, dropping the panel within `edgeZone` of the\n * workspace's left/right edge attaches it to that dock: the hovered edge is\n * reported via `onDragHintChange` during the drag (so the workspace can\n * highlight a drop zone) and the position resets on drop so the panel snaps\n * into its dock slot.\n */\nexport function useDraggablePanel({ onDropDock, onDragHintChange, edgeZone = 72 }: DraggablePanelOptions = {}) {\n const [pos, setPos] = useState<PanelPosition | null>(null);\n const dragState = useRef<DragState | null>(null);\n const hintRef = useRef<PanelDropSide | null>(null);\n\n const clearHint = useCallback(() => {\n if (hintRef.current) {\n hintRef.current = null;\n onDragHintChange?.(null);\n }\n }, [onDragHintChange]);\n\n const onPointerDown = useCallback((e: ReactPointerEvent<HTMLElement>) => {\n const el = (e.currentTarget as HTMLElement).closest('[data-flow-panel]') as HTMLElement | null;\n if (!el) return;\n e.preventDefault();\n e.stopPropagation();\n (e.target as HTMLElement).setPointerCapture(e.pointerId);\n const rect = el.getBoundingClientRect();\n const parentRect = el.offsetParent?.getBoundingClientRect() ?? { left: 0, top: 0 };\n dragState.current = {\n startX: e.clientX,\n startY: e.clientY,\n originX: rect.left - parentRect.left,\n originY: rect.top - parentRect.top,\n width: rect.width,\n parentWidth: el.offsetParent?.clientWidth ?? rect.left - parentRect.left + rect.width,\n };\n }, []);\n\n const onPointerMove = useCallback((e: ReactPointerEvent<HTMLElement>) => {\n if (!dragState.current) return;\n const dx = e.clientX - dragState.current.startX;\n const dy = e.clientY - dragState.current.startY;\n const x = dragState.current.originX + dx;\n setPos({ x, y: dragState.current.originY + dy });\n // Dock affinity: the panel's center hovering a workspace edge highlights\n // that dock as the drop target.\n const centerX = x + dragState.current.width / 2;\n const next: PanelDropSide | null =\n centerX < edgeZone ? 'left'\n : centerX > dragState.current.parentWidth - edgeZone ? 'right'\n : null;\n if (next !== hintRef.current) {\n hintRef.current = next;\n onDragHintChange?.(next);\n }\n }, [edgeZone, onDragHintChange]);\n\n const onPointerUp = useCallback(() => {\n const hint = hintRef.current;\n clearHint();\n dragState.current = null;\n if (hint) {\n onDropDock?.(hint);\n setPos(null); // snap back into the dock slot\n }\n }, [clearHint, onDropDock]);\n\n const style: React.CSSProperties | undefined = pos\n ? { position: 'absolute', left: pos.x, top: pos.y, right: 'auto', bottom: 'auto' }\n : undefined;\n\n return {\n panelStyle: style,\n handleProps: { onPointerDown, onPointerMove, onPointerUp },\n resetPosition: () => setPos(null),\n };\n}\n"],"mappings":";AAsCA,SAAgB,EAAkB,EAAE,YAAA,GAAY,kBAAA,GAAkB,UAAA,IAAW,GAAA,IAA8B,CAAC,GAAG;AAC7G,QAAM,CAAC,GAAK,CAAA,IAAU,EAA+B,IAAI,GACnD,IAAY,EAAyB,IAAI,GACzC,IAAU,EAA6B,IAAI,GAE3C,IAAY,EAAA,MAAkB;AAClC,IAAI,EAAQ,YACV,EAAQ,UAAU,MAClB,IAAmB,IAAI;AAAA,EAE3B,GAAG,CAAC,CAAgB,CAAC,GAEf,IAAgB,EAAA,CAAa,MAAsC;AACvE,UAAM,IAAM,EAAE,cAA8B,QAAQ,mBAAmB;AACvE,QAAI,CAAC,EAAI;AACT,IAAA,EAAE,eAAe,GACjB,EAAE,gBAAgB,GAClB,EAAG,OAAuB,kBAAkB,EAAE,SAAS;AACvD,UAAM,IAAO,EAAG,sBAAsB,GAChC,IAAa,EAAG,cAAc,sBAAsB,KAAK;AAAA,MAAE,MAAM;AAAA,MAAG,KAAK;AAAA,IAAE;AACjF,IAAA,EAAU,UAAU;AAAA,MAClB,QAAQ,EAAE;AAAA,MACV,QAAQ,EAAE;AAAA,MACV,SAAS,EAAK,OAAO,EAAW;AAAA,MAChC,SAAS,EAAK,MAAM,EAAW;AAAA,MAC/B,OAAO,EAAK;AAAA,MACZ,aAAa,EAAG,cAAc,eAAe,EAAK,OAAO,EAAW,OAAO,EAAK;AAAA,IAClF;AAAA,EACF,GAAG,CAAC,CAAC,GAEC,IAAgB,EAAA,CAAa,MAAsC;AACvE,QAAI,CAAC,EAAU,QAAS;AACxB,UAAM,IAAK,EAAE,UAAU,EAAU,QAAQ,QACnC,IAAK,EAAE,UAAU,EAAU,QAAQ,QACnC,IAAI,EAAU,QAAQ,UAAU;AACtC,IAAA,EAAO;AAAA,MAAE,GAAA;AAAA,MAAG,GAAG,EAAU,QAAQ,UAAU;AAAA,IAAG,CAAC;AAG/C,UAAM,IAAU,IAAI,EAAU,QAAQ,QAAQ,GACxC,IACJ,IAAU,IAAW,SACnB,IAAU,EAAU,QAAQ,cAAc,IAAW,UACrD;AACJ,IAAI,MAAS,EAAQ,YACnB,EAAQ,UAAU,GAClB,IAAmB,CAAI;AAAA,EAE3B,GAAG,CAAC,GAAU,CAAgB,CAAC,GAEzB,IAAc,EAAA,MAAkB;AACpC,UAAM,IAAO,EAAQ;AACrB,IAAA,EAAU,GACV,EAAU,UAAU,MAChB,MACF,IAAa,CAAI,GACjB,EAAO,IAAI;AAAA,EAEf,GAAG,CAAC,GAAW,CAAU,CAAC;AAM1B,SAAO;AAAA,IACL,YAL6C,IAC3C;AAAA,MAAE,UAAU;AAAA,MAAY,MAAM,EAAI;AAAA,MAAG,KAAK,EAAI;AAAA,MAAG,OAAO;AAAA,MAAQ,QAAQ;AAAA,IAAO,IAC/E;AAAA,IAIF,aAAa;AAAA,MAAE,eAAA;AAAA,MAAe,eAAA;AAAA,MAAe,aAAA;AAAA,IAAY;AAAA,IACzD,eAAA,MAAqB,EAAO,IAAI;AAAA,EAClC;AACF"}