-
Notifications
You must be signed in to change notification settings - Fork 110
H-5768: Quick Simulation in Edit Mode, and disable Simulate temporarily #8195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
42 commits
Select commit
Hold shift + click to select a range
b521fbf
Everything always available in BottomBar
kube 4c0d697
Extract styles in SimulationControls
kube 07a0d61
Disable SimulateMode in ModeSelector for now
kube 5b3891a
Extract styles in LeftSidebar and make all sections always visible
kube 0d4da59
Extract styles in PlaceNode
kube b273e36
Make PlaceNode token count bigger
kube be34ae1
Make Simulation available in Edit Mode, and disable Simulate tab for now
kube 8e72491
Fix BrokenMachines example
kube 021a4ac
Wording
kube 68dc0c5
Create GlassPanel component
kube e38f2a9
Centralize Resizable in GlassPanel
kube d7bd39f
Update GlassPanel style
kube 3a247cd
Centralize UI Constants
kube bb0cb60
Better resizer handle
kube b59dd4f
Move RESIZE_HANDLE constants in UI constants
kube 815b245
Make tabs a bit cleaner
kube a2501ff
Move activeBottomPanel to EditorStore
kube 3c3e221
Rename DiagnosticsPanel to BottomPanel
kube 866408c
Use Play/Pause/Stop icons
kube 4c509a1
Open Diagnostics when clicking on Run Simulation when errors disable it
kube 068778d
Remove tooltip from Global Parameters panel, just show text
kube 0b60acc
Cleaner BottomPanel
kube 1e0858a
Remove Simulation State from Simulation Settings
kube c4b6619
BottomPanel styles
kube b4ccb94
Add close button to BottomPanel
kube 2dff2de
Diagnostics Indicator opens Diagnostics in BottomPanel, with Tooltip
kube d9aa09a
Add Toggle Panel button in BottomBar
kube 7963943
Hide Place/Transition buttons in BottomBar when Simulation is running
kube 51e084d
SDCPNView readonly when Simulation is running
kube 0fa8f29
Make Token Types, Differential Equations and Global Parameters readonβ¦
kube c466316
Make Diagnostics expanded by default
kube 8947f36
Toolbar refinement + updates from demo review
kube 612615e
Adjust bezelWidth for current BottomBar height
kube b9b4530
Swap Parameters and Computation sections
kube 14eb381
Add tooltip on Time Step
kube f86ebfe
Format
kube 19021ee
Remove dependency of SimulationProvider on EditorContext and reorder β¦
kube 8e08ac6
Add useIsReadOnly hook
kube 18166fc
Review: remove comment
kube 5b09620
Remove globalMode check from TransitionProperties
kube ad258da
Fix format
kube 0cdac41
Add changeset
kube File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@hashintel/petrinaut": patch | ||
| --- | ||
|
|
||
| Quick Simulation in Edit mode, and disable Simulate tab for now. |
271 changes: 271 additions & 0 deletions
271
libs/@hashintel/petrinaut/src/components/glass-panel.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,271 @@ | ||
| import { css, cx } from "@hashintel/ds-helpers/css"; | ||
| import { | ||
| type CSSProperties, | ||
| type ReactNode, | ||
| useCallback, | ||
| useEffect, | ||
| useRef, | ||
| useState, | ||
| } from "react"; | ||
|
|
||
| import { RESIZE_HANDLE_OFFSET, RESIZE_HANDLE_SIZE } from "../constants/ui"; | ||
|
|
||
| const panelContainerStyle = css({ | ||
| position: "relative", | ||
| borderRadius: "[7px]", | ||
| backgroundColor: "[rgba(255, 255, 255, 0.7)]", | ||
| boxShadow: "[0 2px 11px rgba(0, 0, 0, 0.1)]", | ||
| border: "[1px solid rgba(255, 255, 255, 0.8)]", | ||
| }); | ||
|
|
||
| const blurOverlayStyle = css({ | ||
| position: "absolute", | ||
| inset: "[0]", | ||
| borderRadius: "[7px]", | ||
| pointerEvents: "none", | ||
| backdropFilter: "[blur(27px)]", | ||
| }); | ||
|
|
||
| const contentContainerStyle = css({ | ||
| position: "relative", | ||
| height: "[100%]", | ||
| width: "[100%]", | ||
| }); | ||
|
|
||
| type ResizableEdge = "top" | "bottom" | "left" | "right"; | ||
|
|
||
| interface ResizeConfig { | ||
| /** Which edge of the panel is resizable */ | ||
| edge: ResizableEdge; | ||
| /** Callback when the size changes */ | ||
| onResize: (newSize: number) => void; | ||
| /** Current size (width for left/right, height for top/bottom) */ | ||
| size: number; | ||
| /** Minimum size constraint */ | ||
| minSize?: number; | ||
| /** Maximum size constraint */ | ||
| maxSize?: number; | ||
| } | ||
|
|
||
| interface GlassPanelProps { | ||
| /** Content to render inside the panel */ | ||
| children: ReactNode; | ||
| /** Additional CSS class name for the panel container */ | ||
| className?: string; | ||
| /** Inline styles for the panel container */ | ||
| style?: CSSProperties; | ||
| /** Additional CSS class name for the content container */ | ||
| contentClassName?: string; | ||
| /** Inline styles for the content container */ | ||
| contentStyle?: CSSProperties; | ||
| /** Blur amount in pixels (default: 24) */ | ||
| blur?: number; | ||
| /** Configuration for making the panel resizable */ | ||
| resizable?: ResizeConfig; | ||
| } | ||
|
|
||
| const getResizeHandleStyle = (edge: ResizableEdge): CSSProperties => { | ||
| const base: CSSProperties = { | ||
| position: "absolute", | ||
| background: "transparent", | ||
| border: "none", | ||
| padding: 0, | ||
| zIndex: 1001, | ||
| }; | ||
|
|
||
| switch (edge) { | ||
| case "top": | ||
| return { | ||
| ...base, | ||
| top: RESIZE_HANDLE_OFFSET, | ||
| left: 0, | ||
| right: 0, | ||
| height: RESIZE_HANDLE_SIZE, | ||
| cursor: "ns-resize", | ||
| }; | ||
| case "bottom": | ||
| return { | ||
| ...base, | ||
| bottom: RESIZE_HANDLE_OFFSET, | ||
| left: 0, | ||
| right: 0, | ||
| height: RESIZE_HANDLE_SIZE, | ||
| cursor: "ns-resize", | ||
| }; | ||
| case "left": | ||
| return { | ||
| ...base, | ||
| top: 0, | ||
| left: RESIZE_HANDLE_OFFSET, | ||
| bottom: 0, | ||
| width: RESIZE_HANDLE_SIZE, | ||
| cursor: "ew-resize", | ||
| }; | ||
| case "right": | ||
| return { | ||
| ...base, | ||
| top: 0, | ||
| right: RESIZE_HANDLE_OFFSET, | ||
| bottom: 0, | ||
| width: RESIZE_HANDLE_SIZE, | ||
| cursor: "ew-resize", | ||
| }; | ||
| } | ||
| }; | ||
|
|
||
| const getCursorStyle = (edge: ResizableEdge): string => { | ||
| return edge === "top" || edge === "bottom" ? "ns-resize" : "ew-resize"; | ||
| }; | ||
|
|
||
| /** | ||
| * GlassPanel provides a frosted glass-like appearance with backdrop blur. | ||
| * | ||
| * Uses a separate overlay element for the backdrop-filter to avoid | ||
| * interfering with child components that use fixed/absolute positioning | ||
| * (e.g., Monaco Editor hover widgets). | ||
| * | ||
| * Optionally supports resizing from any edge with the `resizable` prop. | ||
| */ | ||
| export const GlassPanel: React.FC<GlassPanelProps> = ({ | ||
| children, | ||
| className, | ||
| style, | ||
| contentClassName, | ||
| contentStyle, | ||
| blur = 24, | ||
| resizable, | ||
| }) => { | ||
| const [isResizing, setIsResizing] = useState(false); | ||
| const resizeStartPosRef = useRef(0); | ||
| const resizeStartSizeRef = useRef(0); | ||
|
|
||
| const handleResizeStart = useCallback( | ||
| (event: React.MouseEvent) => { | ||
| if (!resizable) { | ||
| return; | ||
| } | ||
|
|
||
| event.preventDefault(); | ||
| setIsResizing(true); | ||
|
|
||
| const isVertical = | ||
| resizable.edge === "top" || resizable.edge === "bottom"; | ||
| resizeStartPosRef.current = isVertical ? event.clientY : event.clientX; | ||
| resizeStartSizeRef.current = resizable.size; | ||
| }, | ||
| [resizable], | ||
| ); | ||
|
|
||
| const handleResizeMove = useCallback( | ||
| (event: MouseEvent) => { | ||
| if (!isResizing || !resizable) { | ||
| return; | ||
| } | ||
|
|
||
| const { edge, onResize, minSize = 100, maxSize = 800 } = resizable; | ||
| const isVertical = edge === "top" || edge === "bottom"; | ||
| const currentPos = isVertical ? event.clientY : event.clientX; | ||
|
|
||
| // Calculate delta based on edge direction | ||
| // For top/left: dragging towards origin increases size | ||
| // For bottom/right: dragging away from origin increases size | ||
| let delta: number; | ||
| if (edge === "top" || edge === "left") { | ||
| delta = resizeStartPosRef.current - currentPos; | ||
| } else { | ||
| delta = currentPos - resizeStartPosRef.current; | ||
| } | ||
|
|
||
| const newSize = Math.max( | ||
| minSize, | ||
| Math.min(maxSize, resizeStartSizeRef.current + delta), | ||
| ); | ||
|
|
||
| onResize(newSize); | ||
| }, | ||
| [isResizing, resizable], | ||
| ); | ||
|
|
||
| const handleResizeEnd = useCallback(() => { | ||
| setIsResizing(false); | ||
| }, []); | ||
|
|
||
| // Handle keyboard resize | ||
| const handleKeyDown = useCallback( | ||
| (event: React.KeyboardEvent) => { | ||
| if (!resizable) { | ||
| return; | ||
| } | ||
|
|
||
| const { edge, onResize, size, minSize = 100, maxSize = 800 } = resizable; | ||
| const step = 10; | ||
| let delta = 0; | ||
|
|
||
| if (edge === "top" || edge === "bottom") { | ||
| if (event.key === "ArrowUp") { | ||
| delta = edge === "top" ? step : -step; | ||
| } else if (event.key === "ArrowDown") { | ||
| delta = edge === "top" ? -step : step; | ||
| } | ||
| } else if (event.key === "ArrowLeft") { | ||
| delta = edge === "left" ? step : -step; | ||
| } else if (event.key === "ArrowRight") { | ||
| delta = edge === "left" ? -step : step; | ||
| } | ||
|
|
||
| if (delta !== 0) { | ||
| const newSize = Math.max(minSize, Math.min(maxSize, size + delta)); | ||
| onResize(newSize); | ||
| } | ||
| }, | ||
| [resizable], | ||
| ); | ||
|
|
||
| // Global cursor and event listeners during resize | ||
| useEffect(() => { | ||
| if (!isResizing || !resizable) { | ||
| return; | ||
| } | ||
|
|
||
| document.addEventListener("mousemove", handleResizeMove); | ||
| document.addEventListener("mouseup", handleResizeEnd); | ||
| document.body.style.cursor = getCursorStyle(resizable.edge); | ||
| document.body.style.userSelect = "none"; | ||
CiaranMn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return () => { | ||
| document.removeEventListener("mousemove", handleResizeMove); | ||
| document.removeEventListener("mouseup", handleResizeEnd); | ||
| document.body.style.cursor = ""; | ||
| document.body.style.userSelect = ""; | ||
| }; | ||
| }, [isResizing, resizable, handleResizeMove, handleResizeEnd]); | ||
|
|
||
| return ( | ||
| <div className={cx(panelContainerStyle, className)} style={style}> | ||
| {/* Resize handle */} | ||
| {resizable && ( | ||
| <button | ||
| type="button" | ||
| aria-label={`Resize panel from ${resizable.edge}`} | ||
| onMouseDown={handleResizeStart} | ||
| onKeyDown={handleKeyDown} | ||
| style={getResizeHandleStyle(resizable.edge)} | ||
| /> | ||
| )} | ||
|
|
||
| {/* Blur overlay - separate from content to avoid affecting child positioning */} | ||
| <div | ||
| className={blurOverlayStyle} | ||
| style={blur !== 24 ? { backdropFilter: `blur(${blur}px)` } : undefined} | ||
kube marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /> | ||
|
|
||
| {/* Content container */} | ||
| <div | ||
| className={cx(contentContainerStyle, contentClassName)} | ||
| style={contentStyle} | ||
| > | ||
| {children} | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /** | ||
| * UI-related constants for the Petrinaut editor. | ||
| */ | ||
|
|
||
| // Panel margin (spacing around panels) | ||
| export const PANEL_MARGIN = 10; | ||
|
|
||
| // Resize handle | ||
| export const RESIZE_HANDLE_SIZE = 20; | ||
| export const RESIZE_HANDLE_OFFSET = -Math.floor(RESIZE_HANDLE_SIZE / 2); | ||
|
|
||
| // Left Sidebar | ||
| export const DEFAULT_LEFT_SIDEBAR_WIDTH = 320; | ||
| export const MIN_LEFT_SIDEBAR_WIDTH = 280; | ||
| export const MAX_LEFT_SIDEBAR_WIDTH = 500; | ||
|
|
||
| // Properties Panel (right side) | ||
| export const DEFAULT_PROPERTIES_PANEL_WIDTH = 450; | ||
| export const MIN_PROPERTIES_PANEL_WIDTH = 250; | ||
| export const MAX_PROPERTIES_PANEL_WIDTH = 800; | ||
|
|
||
| // Bottom Panel | ||
| export const DEFAULT_BOTTOM_PANEL_HEIGHT = 180; | ||
| export const MIN_BOTTOM_PANEL_HEIGHT = 100; | ||
| export const MAX_BOTTOM_PANEL_HEIGHT = 600; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,3 @@ | ||
| export const FEATURE_FLAGS = { | ||
| RUNNING_MAN_ICON: true, | ||
| REORDER_TRANSITION_ARCS: false, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.