Editor: the toolkit is a rail tab on desktop, and the rail is wider
The handoff drew the toolkit as a 216px card left of the plan. That width was better spent on the plan and the rail, so the toolkit is now the rail's first tab — Toolkit / Plot / Journal / History / Assistant — rendered `embedded` (no card chrome, no heading; the tab is the heading), the grid is two columns, and the rail grows from 336 to 400px. Focusing a bed (double-click) switches the rail to Toolkit, because that is where the plant palette now lives; a single click still selects into Plot. The phone chrome is untouched: it never used the card. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -177,7 +177,10 @@ export const Canvas = forwardRef<
|
||||
st.setSel(m ? null : { type: 'object', id: o.id })
|
||||
st.setArmedKind(null)
|
||||
st.setGhost(null)
|
||||
st.setTab('plot')
|
||||
// Focusing a bed is planting intent: the rail shows the plant palette
|
||||
// (the toolkit's focused mode), not the inspector. A single click still
|
||||
// selects into the Plot tab.
|
||||
st.setTab('toolkit')
|
||||
if (!el) return
|
||||
const bw = o.rotationDeg % 180 ? o.heightCm : o.widthCm
|
||||
const bh = o.rotationDeg % 180 ? o.widthCm : o.heightCm
|
||||
|
||||
@@ -14,12 +14,18 @@ import { useEditorStore } from './store'
|
||||
import type { EditorObject } from './types'
|
||||
|
||||
/**
|
||||
* The desktop editor's left card. Out of focus: the seven object kinds as pill
|
||||
* The desktop editor's toolkit. Out of focus: the seven object kinds as pill
|
||||
* rows — click to arm (then click the plan), or drag one straight onto it. In a
|
||||
* focused bed it swaps to the plant list with a search, same arm/drag behavior,
|
||||
* plus the bed's bulk tools (fill, clear, scan a packet).
|
||||
*
|
||||
* It started life as the workspace's left card (the handoff's layout) and now
|
||||
* lives as the first tab of the rail, so the plan and the rail get the width
|
||||
* the card took. `embedded` is that mode: no card chrome of its own, and no
|
||||
* "Toolkit" heading — the tab is the heading.
|
||||
*/
|
||||
export function Toolkit({
|
||||
embedded = false,
|
||||
unit,
|
||||
plants,
|
||||
plantings,
|
||||
@@ -34,6 +40,7 @@ export function Toolkit({
|
||||
onClear,
|
||||
onScan,
|
||||
}: {
|
||||
embedded?: boolean
|
||||
unit: UnitPref
|
||||
plants: Plant[]
|
||||
plantings: EditorPlanting[]
|
||||
@@ -55,10 +62,10 @@ export function Toolkit({
|
||||
const [query, setQuery] = useState('')
|
||||
|
||||
return (
|
||||
<div className="panel flex min-h-0 flex-col gap-2 overflow-y-auto overflow-x-hidden p-3.5">
|
||||
<div className={cn('flex min-h-0 flex-col gap-2 overflow-x-hidden', embedded ? 'flex-1' : 'panel overflow-y-auto p-3.5')}>
|
||||
{!focused ? (
|
||||
<>
|
||||
<h6 className="mx-1 mb-1.5 mt-1">Toolkit</h6>
|
||||
{!embedded && <h6 className="mx-1 mb-1.5 mt-1">Toolkit</h6>}
|
||||
{OBJECT_KINDS.map((k) => {
|
||||
const armed = armedKind === k.kind
|
||||
return (
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface Viewport {
|
||||
export type Selection = { type: 'object'; id: number } | { type: 'plop'; id: number }
|
||||
|
||||
/** The right-hand rail's tabs (desktop). */
|
||||
export type RailTab = 'plot' | 'journal' | 'history' | 'chat'
|
||||
export type RailTab = 'toolkit' | 'plot' | 'journal' | 'history' | 'chat'
|
||||
|
||||
/** The phone's primary mode — which tools dock under the canvas. */
|
||||
export type PhoneMode = 'build' | 'plants' | 'journal' | 'chat'
|
||||
|
||||
@@ -116,6 +116,7 @@ const MODES: { id: PhoneMode; label: string; icon: IconName }[] = [
|
||||
]
|
||||
|
||||
const TABS: { id: RailTab; label: string }[] = [
|
||||
{ id: 'toolkit', label: 'Toolkit' },
|
||||
{ id: 'plot', label: 'Plot' },
|
||||
{ id: 'journal', label: 'Journal' },
|
||||
{ id: 'history', label: 'History' },
|
||||
@@ -124,9 +125,11 @@ const TABS: { id: RailTab; label: string }[] = [
|
||||
|
||||
/**
|
||||
* The editor: one canvas, two chromes. Above 760px of container width it is the
|
||||
* three-card workspace — toolkit, plan, rail; below, a phone layout with the
|
||||
* canvas as the whole screen, a peek panel that docks between canvas and mode
|
||||
* bar, and a tool strip for the current mode. Same state, same components.
|
||||
* two-card workspace — the plan, and a rail whose first tab is the toolkit
|
||||
* (the handoff drew it as a third card on the left; folding it into the rail
|
||||
* gave the plan and the rail its width); below, a phone layout with the canvas
|
||||
* as the whole screen, a peek panel that docks between canvas and mode bar,
|
||||
* and a tool strip for the current mode. Same state, same components.
|
||||
*/
|
||||
function Editor({
|
||||
gid,
|
||||
@@ -580,27 +583,29 @@ function Editor({
|
||||
// ── desktop ─────────────────────────────────────────────────────────────
|
||||
const tabs = TABS.filter((t) => t.id !== 'chat' || hasAssistant)
|
||||
const plot = inspector ?? <GardenSummary objects={objects} plantings={plantings} plantsById={plantsById} canEdit={canEdit} />
|
||||
const toolkit = (
|
||||
<Toolkit
|
||||
embedded
|
||||
unit={unit}
|
||||
plants={plants}
|
||||
plantings={plantings}
|
||||
lotsByPlant={lotsByPlantId}
|
||||
canEdit={canEdit}
|
||||
focused={focused}
|
||||
focusedPlopCount={focusedPlops.length}
|
||||
canScan={canScan}
|
||||
filling={fillObject.isPending}
|
||||
onBack={exitFocus}
|
||||
onFill={(layout) => focused && armedPlant && fillObject.mutate({ objectId: focused.id, plantId: armedPlant.id, layout })}
|
||||
onClear={() => setClearing(true)}
|
||||
onScan={() => setScanning(true)}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<div ref={rootRef} className="flex h-dvh flex-col bg-bg">
|
||||
<Nav active="gardens" />
|
||||
<div className="grid min-h-0 flex-1 gap-3.5 p-3.5 pt-0 [grid-template-columns:216px_minmax(0,1fr)_336px]">
|
||||
<Toolkit
|
||||
unit={unit}
|
||||
plants={plants}
|
||||
plantings={plantings}
|
||||
lotsByPlant={lotsByPlantId}
|
||||
canEdit={canEdit}
|
||||
focused={focused}
|
||||
focusedPlopCount={focusedPlops.length}
|
||||
canScan={canScan}
|
||||
filling={fillObject.isPending}
|
||||
onBack={exitFocus}
|
||||
onFill={(layout) => focused && armedPlant && fillObject.mutate({ objectId: focused.id, plantId: armedPlant.id, layout })}
|
||||
onClear={() => setClearing(true)}
|
||||
onScan={() => setScanning(true)}
|
||||
/>
|
||||
|
||||
<div className="grid min-h-0 flex-1 gap-3.5 p-3.5 pt-0 [grid-template-columns:minmax(0,1fr)_400px]">
|
||||
<div className="panel flex min-h-0 flex-col overflow-hidden">
|
||||
<div className="flex flex-wrap items-center gap-3 border-b border-divider px-[18px] py-3">
|
||||
<h4 className="text-[19px]">{g.name}</h4>
|
||||
@@ -645,6 +650,7 @@ function Editor({
|
||||
))}
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto p-4">
|
||||
{tab === 'toolkit' && toolkit}
|
||||
{tab === 'plot' && plot}
|
||||
{tab === 'journal' && journal}
|
||||
{tab === 'history' && <HistoryTab canEdit={canEdit} undoLast={undoLast} />}
|
||||
|
||||
Reference in New Issue
Block a user