Skip to content

Commit 5117ddd

Browse files
committed
Adds ID editing and preserves node selection
Enables renaming of activity and step IDs within the editor Automatically reselects updated nodes to maintain continuity
1 parent 41a12cf commit 5117ddd

File tree

2 files changed

+94
-17
lines changed

2 files changed

+94
-17
lines changed

editor/src/components/NodeEditor.tsx

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,35 @@ export const NodeEditor: React.FC<NodeEditorProps> = ({ node, onUpdateActivity,
2222
// Activity editing
2323
if (isActivity(node)) {
2424
return (
25-
<div className="node-editor">
25+
<div className="node-editor">
2626
<div className="tab-bar-wrapper">
27-
<SubTabBar tabs={ACTIVITY_TABS} activeTab={activeTab} onTabChange={setActiveTab} />
27+
<SubTabBar tabs={ACTIVITY_TABS} activeTab={activeTab} onTabChange={setActiveTab} />
2828
</div>
29-
<div className="tab-panel">
29+
<div className="tab-panel">
3030
{activeTab === 'Meta Data' && (
31-
<MessageEditor
32-
title="Intro Message"
33-
message={node.introMessage}
34-
onChange={msg => onUpdateActivity?.(node.id!, { introMessage: msg })}
35-
/>
31+
<>
32+
<div className="edit-field">
33+
<label>
34+
ID
35+
<input
36+
type="text"
37+
defaultValue={node.id || ''}
38+
onBlur={e => {
39+
const newId = e.target.value;
40+
if (newId !== node.id) {
41+
onUpdateActivity?.(node.id!, { id: newId });
42+
}
43+
}}
44+
placeholder="Enter activity ID..."
45+
/>
46+
</label>
47+
</div>
48+
<MessageEditor
49+
title="Intro Message"
50+
message={node.introMessage}
51+
onChange={msg => onUpdateActivity?.(node.id!, { introMessage: msg })}
52+
/>
53+
</>
3654
)}
3755
{activeTab === 'Script' && (
3856
<pre>{JSON.stringify(node, null, 2)}</pre>
@@ -47,11 +65,27 @@ export const NodeEditor: React.FC<NodeEditorProps> = ({ node, onUpdateActivity,
4765
return (
4866
<div className="node-editor" style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
4967
<div className="tab-bar-wrapper">
50-
<SubTabBar tabs={STEP_TABS} activeTab={activeTab} onTabChange={setActiveTab} />
68+
<SubTabBar tabs={STEP_TABS} activeTab={activeTab} onTabChange={setActiveTab} />
5169
</div>
5270
<div className="tab-panel" style={{ flex: 1, overflowY: 'auto' }}>
5371
{activeTab === 'Meta Data' && (
5472
<>
73+
<div className="edit-field">
74+
<label>
75+
ID
76+
<input
77+
type="text"
78+
defaultValue={node.id || ''}
79+
onBlur={e => {
80+
const newId = e.target.value;
81+
if (newId !== node.id) {
82+
onUpdateStep?.(node.id!, { id: newId });
83+
}
84+
}}
85+
placeholder="Enter step ID..."
86+
/>
87+
</label>
88+
</div>
5589
<MessageEditor
5690
title="Intro Message"
5791
message={node.introMessage}

editor/src/components/Sidebar.tsx

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,8 @@ import { TreeView } from './TreeView';
44
import { BaySelector } from './BaySelector';
55
import { LocationSelector } from './LocationSelector';
66
import { CollapsibleSection } from './CollapsibleSection';
7-
import {
8-
LoadScriptButton,
9-
DownloadButton,
10-
CloneSelectedButton,
11-
AddActivityButton,
12-
AddStepButton
13-
} from './buttons';
7+
import { NodeEditor } from './NodeEditor';
8+
import { LoadScriptButton, DownloadButton, CloneSelectedButton, AddActivityButton, AddStepButton } from './buttons';
149

1510
interface Bay {
1611
id: string;
@@ -75,9 +70,35 @@ export const Sidebar: React.FC<SidebarProps> = ({
7570
onDeleteActivity,
7671
onDeleteStep,
7772
parentActivityForAdd,
73+
onUpdateActivity,
74+
onUpdateStep,
7875
}) => {
7976
const [executing, setExecuting] = React.useState(false);
8077
const [execMessage, setExecMessage] = React.useState<string | null>(null);
78+
// Track selection locally to avoid deselection after ID change
79+
const [localSelectedRef, setLocalSelectedRef] = React.useState(selectedRef);
80+
81+
React.useEffect(() => {
82+
setLocalSelectedRef(selectedRef);
83+
}, [selectedRef]);
84+
85+
// Track last selected node ID for reselection after ID change
86+
const lastSelectedRef = React.useRef(selectedRef);
87+
88+
React.useEffect(() => {
89+
lastSelectedRef.current = selectedRef;
90+
}, [selectedRef]);
91+
92+
// Handler to reselect node after ID change
93+
function handleNodeIdChange(oldId: string, newId: string, kind: 'activity' | 'step', parentActivityId?: string) {
94+
if (kind === 'activity') {
95+
setLocalSelectedRef({ kind: 'activity', activityId: newId });
96+
onSelectActivity(newId);
97+
} else if (kind === 'step' && parentActivityId) {
98+
setLocalSelectedRef({ kind: 'step', activityId: parentActivityId, stepId: newId });
99+
onSelectStep(parentActivityId, newId);
100+
}
101+
}
81102

82103
const canExecute = isValid && !!selectedBayObj?.dbId;
83104

@@ -185,15 +206,37 @@ export const Sidebar: React.FC<SidebarProps> = ({
185206
parentActivityForAdd={parentActivityForAdd}
186207
onShowStepDialog={onShowStepDialog}
187208
/>
209+
{/* Pass handleNodeIdChange to NodeEditor below */}
188210
<TreeView
189211
script={script}
190-
selectedRef={selectedRef}
212+
selectedRef={localSelectedRef}
191213
onSelectScript={onSelectScript}
192214
onSelectActivity={onSelectActivity}
193215
onSelectStep={onSelectStep}
194216
onDeleteActivity={onDeleteActivity}
195217
onDeleteStep={onDeleteStep}
196218
/>
219+
{selectedNode && (
220+
<NodeEditor
221+
node={selectedNode}
222+
onUpdateActivity={(activityId: string, patch: Partial<Activity>) => {
223+
if (patch.id && patch.id !== activityId) {
224+
handleNodeIdChange(activityId, patch.id, 'activity');
225+
}
226+
onUpdateActivity(activityId, patch);
227+
}}
228+
onUpdateStep={(stepId: string, patch: Partial<Step>) => {
229+
if (patch.id && patch.id !== stepId) {
230+
let parentId = null;
231+
if (selectedRef && selectedRef.kind === 'step') {
232+
parentId = selectedRef.activityId;
233+
}
234+
handleNodeIdChange(stepId, patch.id, 'step', parentId || undefined);
235+
}
236+
onUpdateStep(stepId, patch);
237+
}}
238+
/>
239+
)}
197240
</CollapsibleSection>
198241
</div>
199242

0 commit comments

Comments
 (0)