Fix history snapshot corruption, missing dir crash, stale batch delete

- Deep-copy node data on restore to prevent edits from mutating
  stored history snapshots
- Guard glob calls against non-existent current_dir
- Read current selection at delete time instead of using stale
  render-time capture

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 18:11:11 +01:00
parent d3dbd4645a
commit 39a1b98924
2 changed files with 10 additions and 3 deletions

View File

@@ -224,6 +224,9 @@ def index():
'text-caption q-pa-md') 'text-caption q-pa-md')
def _render_pane_file_selector(pane_state: AppState): def _render_pane_file_selector(pane_state: AppState):
if not pane_state.current_dir.exists():
ui.label('Directory not found.').classes('text-warning')
return
json_files = sorted(pane_state.current_dir.glob('*.json')) json_files = sorted(pane_state.current_dir.glob('*.json'))
json_files = [f for f in json_files if f.name not in ( json_files = [f for f in json_files if f.name not in (
'.editor_config.json', '.editor_snippets.json')] '.editor_config.json', '.editor_snippets.json')]
@@ -422,6 +425,9 @@ def render_sidebar(state: AppState, dual_pane: dict):
with ui.card().classes('w-full q-pa-md q-mb-md'): with ui.card().classes('w-full q-pa-md q-mb-md'):
@ui.refreshable @ui.refreshable
def render_file_list(): def render_file_list():
if not state.current_dir.exists():
ui.label('Directory not found.').classes('text-warning')
return
json_files = sorted(state.current_dir.glob('*.json')) json_files = sorted(state.current_dir.glob('*.json'))
json_files = [f for f in json_files if f.name not in ('.editor_config.json', '.editor_snippets.json')] json_files = [f for f in json_files if f.name not in ('.editor_config.json', '.editor_snippets.json')]

View File

@@ -130,10 +130,11 @@ def _render_batch_delete(htree, data, file_path, state, refresh_fn):
).classes('text-warning q-mt-md') ).classes('text-warning q-mt-md')
def do_batch_delete(): def do_batch_delete():
_delete_nodes(htree, data, file_path, valid) current_valid = state.timeline_selected_nodes & set(htree.nodes.keys())
_delete_nodes(htree, data, file_path, current_valid)
state.timeline_selected_nodes = set() state.timeline_selected_nodes = set()
ui.notify( ui.notify(
f'Deleted {count} node{"s" if count != 1 else ""}!', f'Deleted {len(current_valid)} node{"s" if len(current_valid) != 1 else ""}!',
type='positive') type='positive')
refresh_fn() refresh_fn()
@@ -288,7 +289,7 @@ def _render_graphviz(dot_source: str):
def _restore_node(data, node, htree, file_path, state: AppState): def _restore_node(data, node, htree, file_path, state: AppState):
"""Restore a history node as the current version.""" """Restore a history node as the current version."""
node_data = node['data'] node_data = copy.deepcopy(node['data'])
if KEY_BATCH_DATA not in node_data and KEY_BATCH_DATA in data: if KEY_BATCH_DATA not in node_data and KEY_BATCH_DATA in data:
del data[KEY_BATCH_DATA] del data[KEY_BATCH_DATA]
data.update(node_data) data.update(node_data)