Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion frontend/src/components/canvas/DiagramCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ const isWhiteboard = computed(() => activeType.value === 'whiteboard')
// strategy, so activeType would read 'block'. On a unified doc the shared block
// substrate and the whiteboard layer compose over one canvas (the auto-layout
// types become frames in a later phase); legacy single-type docs are unchanged.
const isUnified = computed(() => isUnifiedDocument(store.state))
// A unified doc renders the composed canvas — EXCEPT while a frame is focused for
// editing, when the mode strategy is overridden to that sub-model's type and the
// editor renders as its single-type editor (so isUnified goes false here).
const isUnified = computed(() => isUnifiedDocument(store.state) && !editorUi.state.focusedFrame)
const showBlockLayer = computed(() => !rendersOwnLayer.value || isUnified.value)

const mindmapLayout = computed(() =>
Expand Down Expand Up @@ -379,6 +382,14 @@ function openAtActualSize() {

let resizeObserver = null

// Entering/leaving a frame's focus mode reframes the view: on enter, fit the now
// single-type content (the frame); on exit, fit the unified canvas. Without this
// the frame renders at its own coords, off-screen from where the frame sat.
watch(
() => editorUi.state.focusedFrame,
() => nextTick(fitToView),
)

onMounted(() => {
openAtActualSize()
// Route editorUi.fit() (bottom-left control + ⇧1 shortcut) through fitToView so
Expand Down Expand Up @@ -835,6 +846,7 @@ const surfaceCursor = computed(() => {
:stroke-width="selectedFrame === 'mindmap' ? 1.5 : 0"
stroke-dasharray="6 4" style="cursor: move"
@pointerdown.stop="startFrameDrag('mindmap', $event)"
@dblclick.stop="editorUi.setFocusedFrame('mindmap')"
/>
<g class="unified-frame-content">
<MindMapNodeLayer :mindmap="store.state.mindmap" :positions="mindmapLayout.positions" />
Expand All @@ -849,6 +861,7 @@ const surfaceCursor = computed(() => {
:stroke-width="selectedFrame === 'flowchart' ? 1.5 : 0"
stroke-dasharray="6 4" style="cursor: move"
@pointerdown.stop="startFrameDrag('flowchart', $event)"
@dblclick.stop="editorUi.setFocusedFrame('flowchart')"
/>
<g style="pointer-events: none">
<FlowchartLayer :flowchart="store.state.flowchart" />
Expand Down
20 changes: 18 additions & 2 deletions frontend/src/pages/EditorShell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useCollaboration } from '@/composables/useCollaboration.js'
import { useAppSettings } from '@/composables/useAppSettings.js'
import TopToolbar from '@/components/toolbar/TopToolbar.vue'
import DiagramCanvas from '@/components/canvas/DiagramCanvas.vue'
import LucideIcon from '@/icons/LucideIcon.vue'
import Minimap from '@/components/canvas/Minimap.vue'
import WhiteboardMinimap from '@/components/canvas/WhiteboardMinimap.vue'
import MindMapOverlay from '@/components/canvas/MindMapOverlay.vue'
Expand All @@ -41,8 +42,13 @@ const editorUi = createEditorUi()
provideDiagramStore(store)
provideEditorUi(editorUi)

// Active mode module for this diagram's type (spec diagram-types §0/G1).
const modeStrategy = computed(() => getModeStrategy(store.state.diagramType))
// Active mode module for this diagram's type (spec diagram-types §0/G1). On the
// unified canvas, entering a frame (editorUi.state.focusedFrame) overrides the
// strategy to that sub-model's type, so the whole editor becomes its single-type
// editor (full node editing/keyboard/toolbar) until "Back to canvas".
const modeStrategy = computed(() =>
getModeStrategy(editorUi.state.focusedFrame || store.state.diagramType),
)
provideModeStrategy(modeStrategy)

// Surface-interaction delegation seam (spec diagram-types Part G1/G4). The active
Expand Down Expand Up @@ -128,6 +134,16 @@ onMounted(() => {

<div class="flex min-h-0 flex-1">
<main class="relative min-h-0 min-w-0 flex-1">
<!-- Focus-mode bar: shown while editing a frame on the unified canvas. -->
<button
v-if="editorUi.state.focusedFrame"
class="absolute left-1/2 top-3 z-20 flex -translate-x-1/2 items-center gap-1.5 rounded-full border border-outline-gray-2 bg-surface-base px-3 py-1.5 text-[13px] font-medium text-ink-gray-8 shadow-md hover:bg-surface-gray-2"
@click="editorUi.setFocusedFrame(null)"
>
<LucideIcon name="arrow-left" class="h-4 w-4" />
Back to canvas
<span class="text-ink-gray-5">· editing {{ editorUi.state.focusedFrame }}</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The raw focusedFrame key ('mindmap' / 'flowchart') is rendered directly as user-facing copy. Users will see "· editing mindmap" instead of "· editing Mind Map".

Suggested change
<span class="text-ink-gray-5">· editing {{ editorUi.state.focusedFrame }}</span>
<span class="text-ink-gray-5">· editing {{ editorUi.state.focusedFrame === 'mindmap' ? 'Mind Map' : 'Flowchart' }}</span>

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

</button>
<DiagramCanvas />
<Minimap />
<WhiteboardMinimap v-if="modeStrategy.type === 'whiteboard'" />
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/stores/useEditorUi.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export function createEditorUi() {
infiniteCanvas: true,
// The selected section id (chrome — sections aren't part of shape selection).
selectedSectionId: null,
// Unified canvas: the frame being edited in focus mode ('mindmap'|'flowchart'
// |null). While set, the editor behaves as that sub-model's single-type editor
// (full node editing/keyboard/toolbar); "Back to canvas" clears it.
focusedFrame: null,
// True for a short window after a layout op (tidy / flip) so node positions
// tween instead of jumping (spec 17.1). Off during free drag → no lag.
animateLayout: false,
Expand Down Expand Up @@ -48,6 +52,11 @@ function assembleUi(state, viewport) {
// Switching to draw remembers the chosen shape so the tool can be re-armed.
function attachTools(ui, state) {
ui.setTool = (tool) => (state.tool = tool)
// Enter/leave a frame's focused single-type editing mode (unified canvas).
ui.setFocusedFrame = (kind) => {
state.focusedFrame = kind || null
state.tool = 'select'
}
ui.setDrawShape = (type) => {
state.drawShapeType = type
state.lastShapeType = type
Expand Down
Loading