Skip to content

Commit 062dea2

Browse files
committed
feat: add JSONEditor Arrange dialog to reorder sections and add headings
Add an Arrange toolbar button (and SpeedDial entry) that opens a JSONEditor seeded from the current sections. Editing the outline (drag to reorder, add nested heading blocks) updates _story_outline, which drives the export order.
1 parent b15ff3b commit 062dea2

2 files changed

Lines changed: 49 additions & 0 deletions

File tree

lumen/ai/report.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,11 @@ def _init_view(self):
977977
color="default", margin=0, visible=False,
978978
description="Annotate the report with an AI-written story",
979979
)
980+
self._arrange = IconButton(
981+
icon="reorder", on_click=self._open_arrange_dialog, size="large",
982+
color="default", margin=0, visible=False,
983+
description="Arrange the report (reorder sections and add headings)",
984+
)
980985
self._export = MenuButton(
981986
label="", icon="get_app", variant="text", color="default",
982987
margin=0, size="large", visible=False,
@@ -1011,13 +1016,24 @@ def _init_view(self):
10111016
close_on_click=True,
10121017
title=f"Report Settings: {self.title}",
10131018
)
1019+
self._outline_editor = pn.widgets.JSONEditor(
1020+
mode="tree", sizing_mode="stretch_width", height=400,
1021+
)
1022+
self._outline_editor.param.watch(self._on_outline_change, "value")
1023+
self._outline_dialog = Dialog(
1024+
self._outline_editor,
1025+
show_close_button=True,
1026+
title="Arrange Report",
1027+
width_option="md",
1028+
)
10141029
self._menu = Row(
10151030
self._header_title,
10161031
self._run,
10171032
self._stop,
10181033
self._clear,
10191034
self._collapse,
10201035
self._annotate,
1036+
self._arrange,
10211037
self._export,
10221038
self._settings,
10231039
sizing_mode="stretch_width"
@@ -1028,6 +1044,7 @@ def _init_view(self):
10281044
{"label": "Stop Report", "icon": "stop"},
10291045
{"label": "Clear Report", "icon": "clear"},
10301046
{"label": "Annotate Report", "icon": "auto_stories"},
1047+
{"label": "Arrange Report", "icon": "reorder"},
10311048
{"label": "Export as Notebook", "icon": "description", "format": "ipynb"},
10321049
{"label": "Export as HTML", "icon": "language", "format": "html"},
10331050
{"label": "Configure Report", "icon": "settings"}
@@ -1054,6 +1071,7 @@ def _init_view(self):
10541071
self._container = Column(
10551072
self._view,
10561073
self._dialog,
1074+
self._outline_dialog,
10571075
margin=(0, 0, 0, 5),
10581076
sizing_mode="stretch_both",
10591077
height_policy='fit',
@@ -1074,6 +1092,8 @@ async def _trigger_event(self, item: dict):
10741092
self.reset()
10751093
elif icon == "auto_stories":
10761094
await self._annotate_report()
1095+
elif icon == "reorder":
1096+
self._open_arrange_dialog()
10771097
elif icon in ("description", "language"):
10781098
fmt = item.get("format", "ipynb")
10791099
self._export.value = {"format": fmt}
@@ -1094,6 +1114,7 @@ def _update_icon_visibility(self):
10941114
self._collapse.visible = has_outputs
10951115
self._annotate.visible = has_outputs
10961116
self._annotate.disabled = self.llm is None
1117+
self._arrange.visible = has_outputs
10971118
self._export.visible = has_outputs
10981119
self._settings.visible = has_outputs
10991120
# Only animate play button when no outputs
@@ -1212,6 +1233,14 @@ def _expand_all(self, event=None):
12121233
def _open_settings(self, event=None):
12131234
self._dialog.open = True
12141235

1236+
def _open_arrange_dialog(self, event=None):
1237+
# Seed from the current arrangement, or the default section order.
1238+
self._outline_editor.value = self._story_outline or self._build_default_outline()
1239+
self._outline_dialog.open = True
1240+
1241+
def _on_outline_change(self, event):
1242+
self._story_outline = event.new or []
1243+
12151244
async def _annotate_report(self, event=None):
12161245
"""Write an AI story over the selected sections and insert it into the report."""
12171246
if self.llm is None or not len(self):

lumen/tests/ai/test_story.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,23 @@ async def test_report_outline_inserts_headings():
219219
nb = _nb_text(report)
220220
assert "Introduction" in nb
221221
assert nb.index("Introduction") < nb.index("A done")
222+
223+
224+
async def test_report_arrange_editor_seeds_and_binds_outline():
225+
report = Report(
226+
Section(A(order=[]), title='Section A'),
227+
Section(B(order=[]), title='Section B'),
228+
title='R',
229+
)
230+
await report.execute()
231+
232+
# Opening the arrange dialog seeds the editor from the current sections.
233+
report._open_arrange_dialog()
234+
assert report._outline_editor.value == [{"section": "Section A"}, {"section": "Section B"}]
235+
assert report._outline_dialog.open is True
236+
237+
# Editing the JSONEditor updates the outline, which reorders the export.
238+
report._outline_editor.value = [{"section": "Section B"}, {"section": "Section A"}]
239+
assert report._story_outline == [{"section": "Section B"}, {"section": "Section A"}]
240+
nb = _nb_text(report)
241+
assert nb.index("B done") < nb.index("A done")

0 commit comments

Comments
 (0)