Editor mode model: mobile Fixtures/Plants/Journal/Assistant bar (#99)
On a phone the editor stacked a control column (title, share, season, a 7-chip palette wrapping to 3 rows, journal/history/assistant) ABOVE the canvas, crushing the garden — the point of the app — into a short strip. There was no single "mode" switch: fixtures lived in the palette, plants in a floating focus toolbar, and journal/assistant in the rail. Mobile-first now (#99): the canvas is the whole screen, and a bottom mode bar switches the tools docked beneath it — - Fixtures → the object palette (arm → tap to place) - Plants → the seed tray, once a bed is focused; focusing a bed enters this mode. No bed focused → a "tap a bed, then Plant here" hint. Done planting / Clear ride along. - Journal → opens the journal rail sheet - Assistant → opens the chat rail sheet (hidden with no model configured) A slim mobile top strip keeps the garden name / season / share that lived in the desktop column. Closing a panel rail returns to a canvas mode so the bar reappears; History stays a rail sub-tab (not a fifth mode). Desktop is untouched: the mode bar is md:hidden and the side-column layout stands; `mode` is an inert hint there. Store gains `mode`/`setMode` (reset with the rest of transient state on garden switch). Verified live at 390px (each mode + the select-bed → Plant here → seed-tray flow, canvas now dominant) and 1280px (desktop unchanged). tsc + vitest (92) + build green. DESIGN.md updated. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
This commit is contained in:
@@ -11,10 +11,22 @@ import type { EditorObject } from './types'
|
||||
export const MIN_SCALE = 0.05 // px per cm — fully zoomed out
|
||||
export const MAX_SCALE = 20 // px per cm — fully zoomed in
|
||||
|
||||
// The editor's one primary activity (#99). On mobile this is the segmented
|
||||
// control at the bottom of the screen; it decides which tools dock there —
|
||||
// placing fixtures, placing plants, the journal, or the assistant — so the four
|
||||
// activities stop competing for the same cramped strip. Desktop keeps its
|
||||
// side-column layout and treats this as a lighter hint.
|
||||
export type EditorMode = 'fixtures' | 'plants' | 'journal' | 'assistant'
|
||||
|
||||
interface EditorState {
|
||||
viewport: Viewport
|
||||
setViewport: (next: Viewport | ((prev: Viewport) => Viewport)) => void
|
||||
|
||||
// The primary editor mode (mobile mode bar). Ephemeral — which tool you were
|
||||
// last using is not a property of the garden.
|
||||
mode: EditorMode
|
||||
setMode: (mode: EditorMode) => void
|
||||
|
||||
// The selected object OR plop (mutually exclusive; selecting one clears the
|
||||
// other). selectedId is a garden object; selectedPlantingId is a plop.
|
||||
selectedId: number | null
|
||||
@@ -82,6 +94,9 @@ export const useEditorStore = create<EditorState>((set) => ({
|
||||
viewport: { tx: 0, ty: 0, scale: 1 },
|
||||
setViewport: (next) => set((s) => ({ viewport: typeof next === 'function' ? next(s.viewport) : next })),
|
||||
|
||||
mode: 'fixtures',
|
||||
setMode: (mode) => set({ mode }),
|
||||
|
||||
selectedId: null,
|
||||
select: (id) => set({ selectedId: id, selectedPlantingId: null }),
|
||||
|
||||
@@ -129,5 +144,6 @@ export const useEditorStore = create<EditorState>((set) => ({
|
||||
railTab: null,
|
||||
seasonYear: null,
|
||||
journalObjectId: null,
|
||||
mode: 'fixtures',
|
||||
}),
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user