Fix project reopen persistence

This commit is contained in:
=
2026-06-10 19:10:59 +08:00
parent 7195dea7cd
commit d577edf348
7 changed files with 262 additions and 53 deletions
+36 -24
View File
@@ -18,6 +18,13 @@ from werkzeug.security import check_password_hash
import database
from flask import Response
from gds_builder import build_project_gds
from layout_files import (
LayoutFileError,
is_layout_cell_filename,
load_layout_cell_files,
parse_layout_cell_content,
read_layout_cell_file,
)
from pdk_access import (
cleanup_expired_exports,
create_export_path,
@@ -145,13 +152,17 @@ def cell_routes_path(project_name, cell_name):
def write_route_points_sidecar(yaml_content, output_path):
"""Extract route points from layout YAML and save them beside the cell."""
layout = yaml.safe_load(yaml_content) or {}
layout = yaml_content if isinstance(yaml_content, dict) else parse_layout_cell_content(yaml_content)
routes = {}
# The sidecar preserves manually edited route control points separately from
# the main YAML file for tooling that wants route-only metadata.
for bundle_name, bundle in (layout.get("bundles") or {}).items():
if not isinstance(bundle, dict):
continue
saved_links = []
for link in bundle.get("links") or []:
if not isinstance(link, dict):
continue
points = link.get("points") or []
if not points:
continue
@@ -587,22 +598,24 @@ def list_projects():
os.makedirs(root, exist_ok=True)
projects = []
# Each project is a folder and each YAML file inside that folder is treated
# as one saved cell/canvas.
# Each project is a folder and each valid layout YAML file inside that
# folder is treated as one saved cell/canvas. Route sidecars and malformed
# files are ignored so reopen stays resilient to stale runtime artifacts.
for name in sorted(os.listdir(root)):
path = os.path.join(root, name)
if not os.path.isdir(path):
continue
cells = []
for filename in sorted(os.listdir(path)):
if not filename.lower().endswith(('.yml', '.yaml')):
if not is_layout_cell_filename(filename):
continue
cell_name = os.path.splitext(filename)[0]
yml_path = os.path.join(path, filename)
cells.append({
"name": cell_name,
"has_layout": os.path.exists(yml_path)
})
try:
read_layout_cell_file(yml_path)
except LayoutFileError:
continue
cells.append({"name": cell_name, "has_layout": True})
meta = read_project_meta(name)
projects.append({
"name": name,
@@ -648,24 +661,20 @@ def get_project(project_name):
if not os.path.isdir(root):
return jsonify({"error": "Project not found"}), 404
cells = []
for filename in sorted(os.listdir(root)):
if not filename.lower().endswith(('.yml', '.yaml')):
continue
cell_name = os.path.splitext(filename)[0]
yml_path = os.path.join(root, filename)
if not os.path.exists(yml_path):
continue
with open(yml_path, 'r', encoding='utf-8') as f:
cells.append({
"name": cell_name,
"content": f.read()
})
loaded_cells, warnings = load_layout_cell_files(root)
cells = [
{
"name": os.path.splitext(cell["filename"])[0],
"content": cell["content"]
}
for cell in loaded_cells
]
return jsonify({
"name": safe_name(project_name, 'project_1'),
"cells": cells,
"technology": read_project_meta(project_name).get("technology")
"technology": read_project_meta(project_name).get("technology"),
"warnings": warnings
})
@@ -728,18 +737,19 @@ def rename_cell(project_name, cell_name):
def save_layout():
"""Persist a canvas layout YAML document and refresh its preview assets."""
try:
data = request.get_json()
data = request.get_json(silent=True) or {}
project = safe_name(data.get('project'), 'project_1')
cell = safe_name(data.get('cell'), 'canvas_1')
content = data.get('content', '')
create_preview = bool(data.get('preview', True))
layout_doc = parse_layout_cell_content(content, f"{project}/{cell}.yml")
save_path = cell_file_path(project, cell)
os.makedirs(os.path.dirname(save_path), exist_ok=True)
with open(save_path, 'w', encoding='utf-8') as f:
f.write(content)
write_route_points_sidecar(content, cell_routes_path(project, cell))
write_route_points_sidecar(layout_doc, cell_routes_path(project, cell))
svg_path = None
svg_version = None
@@ -782,6 +792,8 @@ def save_layout():
"preview_error": preview_error
}), 200
except LayoutFileError as e:
return jsonify({"error": str(e)}), 400
except Exception as e:
return jsonify({"error": str(e)}), 500