fix: tree editor authoring blockers - scroll trap, form density, branching hint

- Replace fixed viewport height with flex layout in NodeEditorPanel
- Make footer sticky so Save/Cancel always reachable
- Compact root node banner to single-line with InfoTip tooltip
- Reduce resolution note from callout box to inline text
- Add answer-first branching hint below options label

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-19 14:19:51 -05:00
parent 9462d8b15a
commit 47051b3ed8
3 changed files with 11 additions and 22 deletions

View File

@@ -162,7 +162,7 @@ export function NodeEditorPanel({ nodeId, onClose, onSelectType }: NodeEditorPan
const isRoot = treeStructure?.id === nodeId
return (
<div ref={panelRef} className="flex h-[calc(100vh-105px)] w-[400px] shrink-0 flex-col border-l border-border bg-card">
<div ref={panelRef} className="flex h-full min-h-0 w-[400px] shrink-0 flex-col border-l border-border bg-card">
{/* Header */}
<div className="flex items-center gap-2 border-b border-border px-4 py-3 shrink-0">
<span className={cn('flex h-5 w-5 shrink-0 items-center justify-center rounded', config.badgeClass)}>
@@ -175,14 +175,14 @@ export function NodeEditorPanel({ nodeId, onClose, onSelectType }: NodeEditorPan
</div>
{/* Body — scrollable form area */}
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-3">
<div className="min-h-0 flex-1 overflow-y-auto px-4 py-3 scroll-pb-24">
{draft.type === 'decision' && <NodeFormDecision node={draft} onUpdate={handleDraftUpdate} />}
{draft.type === 'action' && <NodeFormAction node={draft} onUpdate={handleDraftUpdate} />}
{draft.type === 'solution' && <NodeFormResolution node={draft} onUpdate={handleDraftUpdate} />}
</div>
{/* Footer */}
<div className="flex items-center gap-2 border-t border-border px-4 py-3 shrink-0">
<div className="sticky bottom-0 flex items-center gap-2 border-t border-border bg-card px-4 py-3 shrink-0">
<button
onClick={handleSave}
disabled={!isDirty}

View File

@@ -82,21 +82,10 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
<div className="space-y-4">
{/* Root node banner */}
{isRootNode && (
<div className="rounded-lg border-2 border-blue-500/30 bg-blue-500/10 p-4">
<div className="flex items-start gap-3">
<div className="rounded-full bg-blue-500/20 p-2">
<Play className="h-5 w-5 text-blue-500" />
</div>
<div>
<h3 className="font-semibold text-blue-400">
Starting Question
</h3>
<p className="mt-1 text-sm text-muted-foreground">
This is the first question users will see when they start this troubleshooting tree.
Each option below creates a different troubleshooting path.
</p>
</div>
</div>
<div className="flex items-center gap-2 rounded-lg border border-blue-500/30 bg-blue-500/10 px-3 py-2">
<Play className="h-4 w-4 text-blue-500 shrink-0" />
<span className="text-sm font-medium text-blue-400">Starting Question</span>
<InfoTip text="This is the first question users will see. Each option creates a different troubleshooting path." />
</div>
)}
@@ -150,6 +139,7 @@ export function NodeFormDecision({ node, onUpdate }: NodeFormDecisionProps) {
? "Add as many options as needed (A, B, C, D...). Each option leads to a different troubleshooting path."
: "Each option can branch to a different next step."} />
</label>
<p className="text-xs text-muted-foreground mt-1">Options become answer placeholders you can fill in later.</p>
{optionsError && (
<p className="mt-1 text-xs text-red-400">{optionsError.message}</p>
)}

View File

@@ -143,10 +143,9 @@ Document what was done and the outcome.
</div>
{/* Note about terminal node */}
<div className="rounded-md bg-emerald-400/10 p-3 text-sm text-emerald-400">
<strong>Note:</strong> Solution nodes are terminal - they end the troubleshooting flow.
The session will be marked complete when the user reaches this node.
</div>
<p className="text-xs text-emerald-400/70">
Solution nodes are terminal the session completes when users reach this node.
</p>
</div>
)
}