- {loading && (
-
-
+
+
+
- )}
-
- {isEditing && (
-
-
-
-
- Esc
- {' '}
- Discard
-
-
|
-
-
- Ctrl+Enter
- {' '}
- Save
-
+
+ {loading && (
+
-
- )}
-
-
{
- useFlowStore.getState().setViewport(viewport)
- }}
- onNodesChange={onNodesChange}
- onEdgesChange={onEdgesChange}
- onConnect={onConnect}
- onReconnect={onReconnect}
- onNodeClick={handleNodeClick}
- onNodeDoubleClick={handleNodeDoubleClick}
- onNodeDragStop={handleNodeDragStop}
- onEdgeClick={handleEdgeClick}
- onSelectionChange={handleSelectionChange}
- onConnectStart={handleConnectStart}
- onConnectEnd={handleConnectEnd}
- nodeTypes={nodeTypes}
- edgeTypes={edgeTypes}
- onPaneClick={() => {
- setContextMenu(null)
- setSelectedStickyId(null)
- setSelectedGroupId(null)
- if (!isDirty) {
- showNodeContextMenu(false)
- setIsEditing(false)
- setParentId(null)
- setChildParentId(null)
- }
- }}
- deleteKeyCode={null}
- minZoom={0.2}
- >
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Esc
+ {' '}
+ Discard
+
+ |
+
+
+ Ctrl+Enter
+ {' '}
+ Save
+
+
+
+ )}
+
+
{
+ useFlowStore.getState().setViewport(viewport)
+ }}
+ onNodesChange={onNodesChange}
+ onEdgesChange={onEdgesChange}
+ onConnect={onConnect}
+ onReconnect={onReconnect}
+ onNodeClick={handleNodeClick}
+ onNodeDoubleClick={handleNodeDoubleClick}
+ onNodeDragStop={handleNodeDragStop}
+ onEdgeClick={handleEdgeClick}
+ onSelectionChange={handleSelectionChange}
+ onConnectStart={handleConnectStart}
+ onConnectEnd={handleConnectEnd}
+ nodeTypes={nodeTypes}
+ edgeTypes={edgeTypes}
+ onPaneClick={() => {
+ setContextMenu(null)
+ setSelectedStickyId(null)
+ setSelectedGroupId(null)
+ if (!isDirty) {
+ showNodeContextMenu(false)
+ setIsEditing(false)
+ setParentId(null)
+ setChildParentId(null)
+ }
+ }}
+ deleteKeyCode={null}
+ minZoom={0.2}
>
- {saveStatus === 'saving' && 'Saving...'}
- {saveStatus === 'saved' && 'Saved'}
-
+
+
+
+
+
+
+
+
+
setShowModal(false)}
+ addNodeAtPosition={addNodeAtPosition}
+ positions={edgeDropPositions}
+ sourceInfo={sourceInfoReference.current}
+ />
+
+ {contextMenu && (
+ setContextMenu(null)}
+ onAddNote={() => addStickyNote(contextMenu.flowPos)}
+ onGroup={handleGrouping}
+ onUngroup={handleUngroup}
+ onCut={cutSelection}
+ onCopy={copySelection}
+ onPaste={pasteSelection}
+ hasSelection={nodes.some((n) => n.selected)}
+ hasGroupedSelection={
+ nodes.some((node) => node.selected) && allSelectedInSameGroup(nodes.filter((node) => node.selected))
+ }
+ hasClipboard={clipboardRef.current !== null}
+ />
+ )}
-
setShowModal(false)}
- addNodeAtPosition={addNodeAtPosition}
- positions={edgeDropPositions}
- sourceInfo={sourceInfoReference.current}
- />
-
- {contextMenu && (
- setContextMenu(null)}
- onAddNote={() => addStickyNote(contextMenu.flowPos)}
- onGroup={handleGrouping}
- onUngroup={handleUngroup}
- onCut={cutSelection}
- onCopy={copySelection}
- onPaste={pasteSelection}
- hasSelection={nodes.some((n) => n.selected)}
- hasGroupedSelection={nodes.some((n) => n.selected) && allSelectedInSameGroup(nodes.filter((n) => n.selected))}
- hasClipboard={clipboardRef.current !== null}
- />
- )}
+
+
+
)
}
-export default function Flow({ showNodeContextMenu }: Readonly<{ showNodeContextMenu: (b: boolean) => void }>) {
+export default function Flow({
+ showNodeContextMenu,
+ onOpenInEditor,
+}: Readonly<{ showNodeContextMenu: (b: boolean) => void; onOpenInEditor: () => void }>) {
return (
-
+
)
diff --git a/src/main/frontend/app/routes/studio/canvas/nodetypes/frank-node.tsx b/src/main/frontend/app/routes/studio/canvas/nodetypes/frank-node.tsx
index 0eb1731f..35642496 100644
--- a/src/main/frontend/app/routes/studio/canvas/nodetypes/frank-node.tsx
+++ b/src/main/frontend/app/routes/studio/canvas/nodetypes/frank-node.tsx
@@ -173,6 +173,16 @@ export default function FrankNode(properties: NodeProps
) {
}
}, [properties.data.children, properties.data.sourceHandles.length, dragOver])
+ useEffect(() => {
+ const container = containerReference.current
+ if (!container) return
+ const observer = new ResizeObserver(() => {
+ setIsOverflowing(container.scrollHeight > container.offsetHeight + 4)
+ })
+ observer.observe(container)
+ return () => observer.disconnect()
+ }, [])
+
const addHandle = useFlowStore.getState().addHandle
const addChild = useFlowStore((state) => state.addChild)
@@ -447,7 +457,6 @@ export default function FrankNode(properties: NodeProps) {
className={`bg-background border-border relative flex w-full flex-col items-center overflow-x-visible rounded-md border ${isManuallyResized ? 'h-full overflow-y-hidden' : 'overflow-y-visible'}`}
style={{
minWidth: `${minNodeWidth}px`,
- minHeight: `${minNodeHeight}px`,
...(properties.selected && { borderColor: `var(${colorVariable})` }),
}}
ref={containerReference}
@@ -559,14 +568,12 @@ export default function FrankNode(properties: NodeProps) {
{isOverflowing && (
- ▼ more
-
+ />
)}
diff --git a/src/main/frontend/app/routes/studio/studio.tsx b/src/main/frontend/app/routes/studio/studio.tsx
index 6fbae926..b3b81298 100644
--- a/src/main/frontend/app/routes/studio/studio.tsx
+++ b/src/main/frontend/app/routes/studio/studio.tsx
@@ -12,9 +12,6 @@ import { SidebarSide, useSidebarStore } from '~/components/sidebars-layout/sideb
import SidebarLayout from '~/components/sidebars-layout/sidebar-layout'
import useTabStore from '~/stores/tab-store'
import { useShallow } from 'zustand/react/shallow'
-import { useProjectStore } from '~/stores/project-store'
-import { toProjectRelativePath } from '~/utils/path-utils'
-import CodeIcon from '/icons/solar/Code.svg?react'
import { openInEditor } from '~/actions/navigationActions'
import Button from '~/components/inputs/button'
import useFlowStore, { isStickyNote } from '~/stores/flow-store'
@@ -148,7 +145,6 @@ function RightPanelContent({
}
export default function Studio() {
- const project = useProjectStore((state) => state.project)
const setVisibility = useSidebarStore((state) => state.setVisibility)
const [showNodeContext, setShowNodeContext] = useState(false)
const { nodeId, editingSubtype, isMultiSelect, selectedStickyId, selectedGroupId } = useNodeContextStore(
@@ -235,20 +231,7 @@ export default function Studio() {
{activeTab ? (
<>
-
-
- {activeTabPath && project ? toProjectRelativePath(activeTabPath, project) : activeTabPath}
-
-
-
-
+
>
) : (
diff --git a/src/main/frontend/app/stores/save-status-store.ts b/src/main/frontend/app/stores/save-status-store.ts
new file mode 100644
index 00000000..15df0473
--- /dev/null
+++ b/src/main/frontend/app/stores/save-status-store.ts
@@ -0,0 +1,19 @@
+import { create } from 'zustand'
+
+export type SaveStatus = 'idle' | 'saving' | 'saved'
+
+interface SaveStatusState {
+ saveStatus: SaveStatus
+ savedAt: Date | null
+ setSaving: () => void
+ setSaved: () => void
+ setIdle: () => void
+}
+
+export const useSaveStatusStore = create()((set) => ({
+ saveStatus: 'idle',
+ savedAt: null,
+ setSaving: () => set({ saveStatus: 'saving' }),
+ setSaved: () => set({ saveStatus: 'saved', savedAt: new Date() }),
+ setIdle: () => set({ saveStatus: 'idle' }),
+}))
diff --git a/src/main/frontend/app/stores/settings-store.ts b/src/main/frontend/app/stores/settings-store.ts
index 175124f3..73ff3206 100644
--- a/src/main/frontend/app/stores/settings-store.ts
+++ b/src/main/frontend/app/stores/settings-store.ts
@@ -65,7 +65,7 @@ const defaultStudioSettings: StudioSettings = {
previewOnSave: true,
autoRefresh: true,
gradient: false,
- paletteExpandedByDefault: true,
+ paletteExpandedByDefault: false,
}
const defaultProjectSettings: ProjectSettings = {