import os import yaml from collections import OrderedDict from flask import Flask, jsonify, send_from_directory, request, redirect, url_for, session, render_template from werkzeug.security import check_password_hash import database from flask import Response # --- Path Configurations --- BASE_DIR = os.path.dirname(os.path.abspath(__file__)) FRONTEND_DIR = os.path.join(BASE_DIR, '..', 'frontend') YML_PATH = os.path.join(BASE_DIR, '..', 'mxpic', 'PDKs', 'Silterra', 'directories.yaml') COMPS_ROOT = os.path.join(BASE_DIR, '..', 'mxpic', 'PDKs', 'Silterra') # Define where your new icons folder is located (adjust if it's placed elsewhere) ICONS_DIR = os.path.join(BASE_DIR, 'icons') #build layout save path SAVE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'generated_layouts') app = Flask(__name__, template_folder=FRONTEND_DIR, static_folder=FRONTEND_DIR) app.secret_key = 'super_secret_mxpic_key' app.json.sort_keys = False database.init_db() # ... [Keep countSpaces and buildTree exactly as they are] ... def findComps(baseDir): """Scan component folders, return map of paths -> component info.""" compMap = {} refDir = baseDir for root, dirs, files in os.walk(baseDir): ymlFiles = [f for f in files if f.endswith('.yml')] if ymlFiles: parentDir = os.path.dirname(root) relPath = os.path.relpath(parentDir, refDir) parts = () if relPath == '.' else tuple(relPath.split(os.sep)) compName = os.path.basename(root) # Extract the category (the mother folder's name) category = os.path.basename(parentDir) compMap[parts] = { 'folder': compName, 'yml': ymlFiles[0], 'category': category # Save the category to the map } dirs.clear() return compMap def addCompsToTree(compMap): """Build a completely fresh tree from scratch and insert component nodes.""" fresh_tree = OrderedDict() for pathSeg, compItem in compMap.items(): compName = compItem['folder'] curNode = fresh_tree for seg in pathSeg: if seg not in curNode: curNode[seg] = OrderedDict() curNode = curNode[seg] curNode[compName] = OrderedDict({ "__type__": "component", "__name__": compName, "__yml__": compItem['yml'], "__category__": compItem['category'] # Inject category into the tree }) return fresh_tree # ... [Keep readCompYaml and Page Routes exactly as they are] ... # --- API ROUTES (Library, Components & Icons) --- @app.route('/api/icon/') def getIcon(category): """Serve the icon corresponding to the component category.""" for ext in ('.png', '.svg', '.jpg'): icon_path = os.path.join(ICONS_DIR, f"{category}{ext}") if os.path.exists(icon_path): return send_from_directory(ICONS_DIR, f"{category}{ext}") fallback = os.path.join(ICONS_DIR, "default.png") if os.path.exists(fallback): return send_from_directory(ICONS_DIR, "default.png") # return png if not found transparent_png = ( b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01' b'\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\nIDATx\x9cc\x00\x01' b'\x00\x00\x05\x00\x01\r\n\xf4\xc0\x00\x00\x00\x00IEND\xaeB`\x82' ) return Response(transparent_png, mimetype='image/png') # ... [Keep existing API routes below] ... def readCompYaml(compName): """Load YAML from component folder.""" for root, dirs, files in os.walk(COMPS_ROOT): if os.path.basename(root) == compName: dirs.clear() ymlFiles = [f for f in files if f.endswith('.yml')] if ymlFiles: ymlPath = os.path.join(root, ymlFiles[0]) with open(ymlPath, 'r', encoding='utf-8') as f: return yaml.safe_load(f) return None # --- AUTHENTICATION & PAGE ROUTES --- @app.route('/') def home(): """Route to login page, or bypass to dashboard if already authenticated.""" if 'user_id' in session: return redirect(url_for('dashboard')) return render_template('login.html') @app.route('/login', methods=['POST']) def login(): """Verify credentials against the database.""" username = request.form.get('username') password = request.form.get('password') user = database.get_user(username) # Verify hash from database matches entered password if user and check_password_hash(user[2], password): session['user_id'] = user[0] session['username'] = user[1] return redirect(url_for('dashboard')) else: return render_template('login.html', error="Invalid username or password") @app.route('/dashboard') def dashboard(): """User project list.""" if 'user_id' not in session: return redirect(url_for('home')) return render_template('dashboard.html', username=session['username']) @app.route('/canvas') def canvas(): """The main EDA editor.""" if 'user_id' not in session: return redirect(url_for('home')) # Note: Ensure your old index.html is renamed to canvas.html in the frontend folder return render_template('canvas.html') @app.route('/logout') def logout(): """Clear session and return to login.""" session.clear() return redirect(url_for('home')) @app.route('/api/save-layout', methods=['POST']) def save_layout(): try: data = request.get_json() filename = data.get('filename', 'layout.yaml') content = data.get('content', '') os.makedirs(SAVE_DIR, exist_ok=True) save_path = os.path.join(SAVE_DIR, filename) with open(save_path, 'w', encoding='utf-8') as f: f.write(content) return jsonify({ "message": "successfully saved", "path": save_path }), 200 except Exception as e: return jsonify({"error": str(e)}), 500 # --- API ROUTES (Library & Components) --- @app.route('/api/library') def getLib(): """Get library structure.""" # tree = buildTree(YML_PATH) if os.path.isdir(COMPS_ROOT): compMap = findComps(COMPS_ROOT) fresh_tree = addCompsToTree(compMap) return jsonify(fresh_tree) @app.route('/api/component/') def getComp(component_name): """Return component YAML data.""" data = readCompYaml(component_name) if data is None: return jsonify({"error": "Component not found"}), 404 return jsonify(data) @app.route('/api/component//image') def getCompImg(component_name): """Return first image in component folder.""" for root, dirs, files in os.walk(COMPS_ROOT): if os.path.basename(root) == component_name: dirs.clear() for ext in ('.png', '.jpg', '.jpeg', '.svg'): for f in files: if f.lower().endswith(ext): return send_from_directory(root, f) break return jsonify({"error": "No image found"}), 404 if __name__ == '__main__': print("Starting mxpic EDA Server on http://127.0.0.1:3000") app.run(host='127.0.0.1', port=3000, debug=True)