diff --git a/Makefile b/Makefile index 6c194fea..25159251 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ install: - pip install -e .[dev,docs,devsim,femwell,gmsh,klayout,meow,meshwell,ray,sax,schematic,tidy3d,web,vlsir] + pip install -e .[dev,docs,devsim,femwell,gmsh,klayout,meow,meshwell,ray,sax,schematic,tidy3d,vlsir] pre-commit install dev: test-data gmsh elmer install diff --git a/README.md b/README.md index 8f9d67e5..aa5cde8f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ gdsfactory plugins: - `mpb` for MPB mode solver. - `elmer` for electrostatic (capacitive) simulations. - `palace` for full-wave driven (S parameter) and electrostatic (capacitive) simulations. -- `web` for gdsfactory webapp. - `vlsir` for parsing GDS-extracted circuit netlists into Spice, Spectre and Xyce Schematic File formats. ## Installation diff --git a/gplugins/common/config.py b/gplugins/common/config.py index a529eb29..aa4d4b24 100644 --- a/gplugins/common/config.py +++ b/gplugins/common/config.py @@ -18,7 +18,6 @@ class Path: module = module_path repo = repo_path - web = module / "web" results_tidy3d = home / ".tidy3d" test_data = repo / "test-data" sparameters_repo = test_data / "sp" diff --git a/gplugins/web/Makefile b/gplugins/web/Makefile deleted file mode 100644 index 914b8d10..00000000 --- a/gplugins/web/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -run: - uvicorn main:app --reload diff --git a/gplugins/web/__init__.py b/gplugins/web/__init__.py deleted file mode 100644 index b2f01558..00000000 --- a/gplugins/web/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "0.0.11" diff --git a/gplugins/web/gds_files/C.gds b/gplugins/web/gds_files/C.gds deleted file mode 100644 index a16a30ab..00000000 Binary files a/gplugins/web/gds_files/C.gds and /dev/null differ diff --git a/gplugins/web/gds_files/C_width20.gds b/gplugins/web/gds_files/C_width20.gds deleted file mode 100644 index ffd9e397..00000000 Binary files a/gplugins/web/gds_files/C_width20.gds and /dev/null differ diff --git a/gplugins/web/gds_files/C_width5.gds b/gplugins/web/gds_files/C_width5.gds deleted file mode 100644 index 6959e224..00000000 Binary files a/gplugins/web/gds_files/C_width5.gds and /dev/null differ diff --git a/gplugins/web/gds_files/coh_rx_dual_pol.gds b/gplugins/web/gds_files/coh_rx_dual_pol.gds deleted file mode 100644 index 8c75e5d2..00000000 Binary files a/gplugins/web/gds_files/coh_rx_dual_pol.gds and /dev/null differ diff --git a/gplugins/web/gds_files/coh_rx_single_pol.gds b/gplugins/web/gds_files/coh_rx_single_pol.gds deleted file mode 100644 index 7e8c25a4..00000000 Binary files a/gplugins/web/gds_files/coh_rx_single_pol.gds and /dev/null differ diff --git a/gplugins/web/gds_files/coh_rx_single_pol_pad_d_19b6c499.gds b/gplugins/web/gds_files/coh_rx_single_pol_pad_d_19b6c499.gds deleted file mode 100644 index e57ed3aa..00000000 Binary files a/gplugins/web/gds_files/coh_rx_single_pol_pad_d_19b6c499.gds and /dev/null differ diff --git a/gplugins/web/gds_files/coh_rx_single_pol_pad_d_d4526895.gds b/gplugins/web/gds_files/coh_rx_single_pol_pad_d_d4526895.gds deleted file mode 100644 index f33c89df..00000000 Binary files a/gplugins/web/gds_files/coh_rx_single_pol_pad_d_d4526895.gds and /dev/null differ diff --git a/gplugins/web/gds_files/coh_tx_dual_pol.gds b/gplugins/web/gds_files/coh_tx_dual_pol.gds deleted file mode 100644 index 005ee4ef..00000000 Binary files a/gplugins/web/gds_files/coh_tx_dual_pol.gds and /dev/null differ diff --git a/gplugins/web/gds_files/crossing_arm.gds b/gplugins/web/gds_files/crossing_arm.gds deleted file mode 100644 index 3ba4be79..00000000 Binary files a/gplugins/web/gds_files/crossing_arm.gds and /dev/null differ diff --git a/gplugins/web/gds_files/dbr_cavity.gds b/gplugins/web/gds_files/dbr_cavity.gds deleted file mode 100644 index a386a12e..00000000 Binary files a/gplugins/web/gds_files/dbr_cavity.gds and /dev/null differ diff --git a/gplugins/web/gds_files/die_bbox_frame.gds b/gplugins/web/gds_files/die_bbox_frame.gds deleted file mode 100644 index 490f6571..00000000 Binary files a/gplugins/web/gds_files/die_bbox_frame.gds and /dev/null differ diff --git a/gplugins/web/gds_files/loss_deembedding_ch14_23.gds b/gplugins/web/gds_files/loss_deembedding_ch14_23.gds deleted file mode 100644 index 6566a5df..00000000 Binary files a/gplugins/web/gds_files/loss_deembedding_ch14_23.gds and /dev/null differ diff --git a/gplugins/web/gds_files/pad_array_add_fiducials.gds b/gplugins/web/gds_files/pad_array_add_fiducials.gds deleted file mode 100644 index d6e98f01..00000000 Binary files a/gplugins/web/gds_files/pad_array_add_fiducials.gds and /dev/null differ diff --git a/gplugins/web/gds_files/spiral_inner_io_add_gra_f2760628.gds b/gplugins/web/gds_files/spiral_inner_io_add_gra_f2760628.gds deleted file mode 100644 index b0b67e46..00000000 Binary files a/gplugins/web/gds_files/spiral_inner_io_add_gra_f2760628.gds and /dev/null differ diff --git a/gplugins/web/gds_files/wg.gds b/gplugins/web/gds_files/wg.gds deleted file mode 100644 index 16321dab..00000000 Binary files a/gplugins/web/gds_files/wg.gds and /dev/null differ diff --git a/gplugins/web/main.py b/gplugins/web/main.py deleted file mode 100644 index cb691d39..00000000 --- a/gplugins/web/main.py +++ /dev/null @@ -1,300 +0,0 @@ -import base64 -import importlib -import os -import pathlib -from glob import glob -from pathlib import Path - -import gdsfactory as gf -from fastapi import FastAPI, Form, HTTPException, Request, status -from fastapi.responses import HTMLResponse, RedirectResponse -from fastapi.staticfiles import StaticFiles -from fastapi.templating import Jinja2Templates -from gdsfactory.cell import Settings -from gdsfactory.config import CONF, GDSDIR_TEMP, pdks -from gdsfactory.watch import FileWatcher -from loguru import logger -from pydantic import BaseModel -from starlette.routing import WebSocketRoute - -from gplugins.common.config import PATH -from gplugins.web.middleware import ProxiedHeadersMiddleware -from gplugins.web.server import LayoutViewServerEndpoint, get_layout_view - -module_path = Path(__file__).parent.absolute() - -app = FastAPI( - routes=[WebSocketRoute("/view/{cell_name}/ws", endpoint=LayoutViewServerEndpoint)] -) -app.add_middleware(ProxiedHeadersMiddleware) -# app = FastAPI() -app.mount("/static", StaticFiles(directory=PATH.web / "static"), name="static") - -# gdsfiles = StaticFiles(directory=home_path) -# app.mount("/gds_files", gdsfiles, name="gds_files") -templates = Jinja2Templates(directory=PATH.web / "templates") - - -def load_pdk(pdk: str | None = None) -> gf.Pdk: - pdk = pdk or os.environ.get("PDK", "generic") - - if pdk == "generic": - active_pdk = gf.get_active_pdk() - else: - active_module = importlib.import_module(pdk) - active_pdk = active_module.PDK - active_pdk.activate() - return active_pdk - - -def get_url(request: Request) -> str: - port_mod = "" - if request.url.port is not None and len(str(request.url).split(".")) < 3: - port_mod = f":{str(request.url.port)}" - - hostname = request.url.hostname - - if "github" in hostname: - port_mod = "" - - url = str( - request.url.scheme - + "://" - + (hostname or "localhost") - + port_mod - + request.url.path - ) - return url - - -@app.get("/", response_class=HTMLResponse) -async def root(request: Request): - return templates.TemplateResponse("index.html.j2", {"request": request}) - - -@app.get("/pdk-list", response_model=list[str]) -async def get_pdk_list() -> list[str]: - pdks_installed = [] - for pdk in pdks: - try: - m = importlib.import_module(pdk) - m.PDK - pdks_installed.append(pdk) - except Exception as e: - logger.error(f"Could not import {pdk} {e}") - return sorted(pdks_installed) - - -class PDKItem(BaseModel): - pdk: str - - -@app.post("/pdk-set") -async def set_pdk(pdk_item: PDKItem): - pdk = pdk_item.pdk - load_pdk(pdk) - return {"message": f"PDK {pdk} set successfully!"} - - -@app.get("/gds_list", response_class=HTMLResponse) -async def gds_list(request: Request): - """List all saved GDS files.""" - files_root = GDSDIR_TEMP - paths_list = glob(str(files_root / "*.gds")) - files_list = sorted(Path(gdsfile).stem for gdsfile in paths_list) - files_metadata = [ - {"name": file_name, "url": f"view/{file_name}"} for file_name in files_list - ] - return templates.TemplateResponse( - "file_browser.html.j2", - { - "request": request, - "message": f"GDS files in {str(files_root)!r}", - "files_root": files_root, - "files_metadata": files_metadata, - }, - ) - - -@app.get("/gds_current", response_class=HTMLResponse) -async def gds_current() -> RedirectResponse: - """List all saved GDS files.""" - if CONF.last_saved_files: - return RedirectResponse(f"/view/{CONF.last_saved_files[-1].stem}") - else: - return RedirectResponse( - "/", - status_code=status.HTTP_302_FOUND, - ) - - -@app.get("/pdk", response_class=HTMLResponse) -async def pdk(request: Request): - if "preview.app.github" in str(request.url): - return RedirectResponse(str(request.url).replace(".preview", "")) - active_pdk = load_pdk() - pdk_name = active_pdk.name - components = list(active_pdk.cells.keys()) - return templates.TemplateResponse( - "pdk.html.j2", - { - "request": request, - "title": "Main", - "pdk_name": pdk_name, - "components": sorted(components), - }, - ) - - -LOADED_COMPONENTS = {} - - -@app.get("/view/{cell_name}", response_class=HTMLResponse) -async def view_cell(request: Request, cell_name: str, variant: str | None = None): - gds_files = GDSDIR_TEMP.glob("*.gds") - gds_names = [gdspath.stem for gdspath in gds_files] - - if "preview.app.github" in str(request.url): - return RedirectResponse(str(request.url).replace(".preview", "")) - - if variant in LOADED_COMPONENTS: - component = LOADED_COMPONENTS[variant] - else: - try: - component = gf.get_component(cell_name) - except Exception as e: - if cell_name not in gds_names: - raise HTTPException( - status_code=400, detail=f"Component not found. {e}" - ) from e - - gdspath = GDSDIR_TEMP / cell_name - component = gf.import_gds(gdspath=gdspath.with_suffix(".gds")) - component.settings = Settings(name=component.name) - layout_view = get_layout_view(component) - pixel_data = layout_view.get_pixels_with_options(800, 400).to_png_data() - b64_data = base64.b64encode(pixel_data).decode("utf-8") - return templates.TemplateResponse( - "viewer.html.j2", - { - "request": request, - "cell_name": cell_name, - "variant": variant, - "title": "Viewer", - "initial_view": b64_data, - "component": component, - "url": get_url(request), - }, - ) - - -@app.post("/update/{cell_name}") -async def update_cell(request: Request, cell_name: str): - """Cell name is the name of the PCell function.""" - data = await request.form() - settings = {k: v for k, v in data.items() if v != ""} - - if not settings: - return RedirectResponse( - f"/view/{cell_name}", - status_code=status.HTTP_302_FOUND, - ) - try: - component = gf.get_component({"component": cell_name, "settings": settings}) - variant = component.name - except Exception as e: - raise HTTPException(status_code=400, detail=f"Component not found. {e}") from e - - LOADED_COMPONENTS[component.name] = component - return RedirectResponse( - f"/view/{cell_name}?variant={variant}", - status_code=status.HTTP_302_FOUND, - ) - - -@app.post("/search", response_class=RedirectResponse) -async def search(name: str = Form(...)): - logger.info(f"Searching for {name}...") - try: - gf.get_component(name) - except ValueError: - return RedirectResponse("/", status_code=status.HTTP_404_NOT_FOUND) - logger.info(f"Successfully found {name}! Redirecting...") - return RedirectResponse(f"/view/{name}", status_code=status.HTTP_302_FOUND) - - -######################### -# filewatcher -####################### - -watched_folder = None -watcher = None -output = "" -component = None - - -@app.get("/filewatcher", response_class=HTMLResponse) -async def filewatcher(request: Request): - global component - - if CONF.last_saved_files: - component = gf.import_gds(gf.CONF.last_saved_files[-1]) - component.settings = Settings(name=component.name) - else: - component = gf.components.straight() - - layout_view = get_layout_view(component) - pixel_data = layout_view.get_pixels_with_options(800, 400).to_png_data() - b64_data = base64.b64encode(pixel_data).decode("utf-8") - - return templates.TemplateResponse( - "filewatcher.html.j2", - { - "request": request, - "output": output, - "cell_name": str(component.name), - "variant": None, - "title": "Viewer", - "initial_view": b64_data, - "component": component, - "url": get_url(request), - }, - ) - - -@app.post("/filewatcher_start") -async def watch_folder(request: Request, folder_path: str = Form(...)): - global component - global output - global watched_folder - global watcher - - if folder_path is None or not folder_path.strip(): - raise HTTPException(status_code=400, detail="Folder path is required.") - if not os.path.exists(folder_path) or not os.path.isdir(folder_path): - raise HTTPException(status_code=400, detail="Folder does not exist.") - - watched_folder = folder_path - watched_folder = pathlib.Path(folder_path) - watcher = FileWatcher(path=folder_path) - watcher.start() - output += f"watching {watched_folder}\n" - return RedirectResponse( - "/filewatcher", - status_code=status.HTTP_302_FOUND, - ) - - -@app.get("/filewatcher_stop") -def stop_watcher(request: Request) -> str: - """Stops filewacher.""" - global watcher - global watched_folder - global output - - if watcher: - watcher.stop() - - message = f"stopped watching {watched_folder}\n" - output += message - return message diff --git a/gplugins/web/middleware.py b/gplugins/web/middleware.py deleted file mode 100644 index 4b9202dd..00000000 --- a/gplugins/web/middleware.py +++ /dev/null @@ -1,55 +0,0 @@ -from starlette.types import ASGIApp, Receive, Scope, Send - -Headers = list[tuple[bytes, bytes]] - -# original developed by researchers at university of cambridge: -# https://pypi.org/project/fastapi-proxiedheadersmiddleware/ - -# modification to resolve Connection: keep-alive vs. upgrade conflict -# conflict described here: https://github.com/orgs/community/discussions/57596 -# doesn't seem to work as intended, as the upgrade decision is made -# before the request goes through the middleware - - -class ProxiedHeadersMiddleware: - """ - A middleware that modifies the request to ensure that FastAPI uses the - X-Forwarded-* headers when creating URLs used to reference this application. - - We are very permissive in allowing all X-Forwarded-* headers to be used, as - we know that this API will be published behind the API Gateway, and is - therefore not prone to redirect hijacking. - - """ - - def __init__(self, app: ASGIApp): - self.app = app - - async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: - scope["headers"] = self.remap_headers(scope.get("headers", {})) - - await self.app(scope, receive, send) - return - - def remap_headers(self, source: Headers) -> Headers: - """ - Map X-Forwarded-Host to host and X-Forwarded-Prefix to prefix. - - """ - upgrade = len( - [q for p, q in source if "connection" in str(p) and "upgrade" in str(q)] - ) - - source = dict(source) - if upgrade: # resolve conflict with priority of upgrade - source[b"connection"] = b"upgrade" - - if b"x-forwarded-host" in source: - source[b"host"] = source[b"x-forwarded-host"] - source.pop(b"x-forwarded-host") - - if b"x-forwarded-prefix" in source: - source[b"host"] = source[b"host"] + source[b"x-forwarded-prefix"] - source.pop(b"x-forwarded-prefix") - - return list(source.items()) diff --git a/gplugins/web/server.py b/gplugins/web/server.py deleted file mode 100755 index 90f83485..00000000 --- a/gplugins/web/server.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/env python3 -# type: ignore - -import asyncio -import json - -import gdsfactory as gf -import klayout.db as db -import klayout.lay as lay -from fastapi import WebSocket -from gdsfactory.component import GDSDIR_TEMP -from loguru import logger -from starlette.endpoints import WebSocketEndpoint - -host = "localhost" -port = 8765 - - -class LayoutViewServerEndpoint(WebSocketEndpoint): - encoding = "text" - - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - logger.info("Initialized websocket") - _params = self.scope["query_string"].decode("utf-8") - _params_splitted = _params.split("&") - params = {} - for _param in _params_splitted: - key, value = _param.split("=") - params[key] = value - - # print("args:", args) - # print("kwargs:", kwargs) - # self.url = params["gds_file"].replace('/', '\\') - # self.layer_props = params.get("layer_props", None) - lyp_path = GDSDIR_TEMP / "layer_props.lyp" - active_pdk = gf.get_active_pdk() - if active_pdk.layer_views: - active_pdk.layer_views.to_lyp(lyp_path) - self.layer_props = lyp_path - # path_params = args[0]['path_params'] - # cell_name = path_params["cell_name"] - cell_name = params["variant"] - # c = gf.get_component(cell_name) - gds_path = GDSDIR_TEMP / f"{cell_name}.gds" - # c.write_gds(gds_path) - self.url = self.gds_path = str(gds_path) - - async def on_connect(self, websocket) -> None: - await websocket.accept() - await self.connection(websocket) - - async def on_receive(self, websocket, data) -> None: - await self.reader(websocket, data) - - async def on_disconnect(self, websocket, close_code) -> None: - pass - - async def send_image(self, websocket, data) -> None: - await websocket.send_text(data) - - def image_updated(self, websocket) -> None: - pixel_buffer = self.layout_view.get_screenshot_pixels() - asyncio.create_task(self.send_image(websocket, pixel_buffer.to_png_data())) - - def mode_dump(self): - return self.layout_view.mode_names() - - def annotation_dump(self): - return [d[1] for d in self.layout_view.annotation_templates()] - - def layer_dump(self): - return [ - { - "dp": layer.eff_dither_pattern(), - "ls": layer.eff_line_style(), - "c": layer.eff_fill_color(), - "fc": layer.eff_frame_color(), - "m": layer.marked, - "s": layer.source, - "t": layer.transparent, - "va": layer.valid, - "v": layer.visible, - "w": layer.width, - "x": layer.xfill, - "name": layer.name, - "id": layer.id(), - } - for layer in self.layout_view.each_layer() - ] - - async def connection(self, websocket: WebSocket, path: str | None = None) -> None: - self.layout_view = lay.LayoutView() - url = self.url - self.layout_view.load_layout(url) - if self.layer_props is not None: - self.layout_view.load_layer_props(str(self.layer_props)) - self.layout_view.max_hier() - - await websocket.send_text( - json.dumps( - { - "msg": "loaded", - "modes": self.mode_dump(), - "annotations": self.annotation_dump(), - "layers": self.layer_dump(), - } - ) - ) - - asyncio.create_task(self.timer(websocket)) - - async def timer(self, websocket) -> None: - self.layout_view.on_image_updated_event = lambda: self.image_updated(websocket) - while True: - self.layout_view.timer() - await asyncio.sleep(0.01) - - def buttons_from_js(self, js): - buttons = 0 - k = js["k"] - b = js["b"] - if (k & 1) != 0: - buttons |= lay.ButtonState.ShiftKey - if (k & 2) != 0: - buttons |= lay.ButtonState.ControlKey - if (k & 4) != 0: - buttons |= lay.ButtonState.AltKey - if (b & 1) != 0: - buttons |= lay.ButtonState.LeftButton - if (b & 2) != 0: - buttons |= lay.ButtonState.RightButton - if (b & 4) != 0: - buttons |= lay.ButtonState.MidButton - return buttons - - def wheel_event(self, function, js) -> None: - delta = 0 - dx = js["dx"] - dy = js["dy"] - if dx != 0: - delta = -dx - horizontal = True - elif dy != 0: - delta = -dy - horizontal = False - if delta != 0: - function( - delta, horizontal, db.Point(js["x"], js["y"]), self.buttons_from_js(js) - ) - - def mouse_event(self, function, js) -> None: - function(db.Point(js["x"], js["y"]), self.buttons_from_js(js)) - - async def reader(self, websocket, data: str) -> None: - js = json.loads(data) - msg = js["msg"] - if msg == "clear-annotations": - self.layout_view.clear_annotations() - elif msg == "initialize": - self.layout_view.resize(js["width"], js["height"]) - await websocket.send_text(json.dumps({"msg": "initialized"})) - elif msg == "layer-v": - layer_id = js["id"] - vis = js["value"] - for layer in self.layout_view.each_layer(): - if layer.id() == layer_id: - layer.visible = vis - elif msg == "layer-v-all": - vis = js["value"] - for layer in self.layout_view.each_layer(): - layer.visible = vis - elif msg == "mode_select": - self.layout_view.switch_mode(js["mode"]) - elif msg == "mouse_dblclick": - self.mouse_event(self.layout_view.send_mouse_double_clicked_event, js) - elif msg == "mouse_enter": - self.layout_view.send_enter_event() - elif msg == "mouse_leave": - self.layout_view.send_leave_event() - elif msg == "mouse_move": - self.mouse_event(self.layout_view.send_mouse_move_event, js) - elif msg == "mouse_pressed": - self.mouse_event(self.layout_view.send_mouse_press_event, js) - elif msg == "mouse_released": - self.mouse_event(self.layout_view.send_mouse_release_event, js) - elif msg == "quit": - return - elif msg == "resize": - self.layout_view.resize(js["width"], js["height"]) - elif msg == "select-mode": - mode = js["value"] - self.layout_view.switch_mode(mode) - elif msg == "select-ruler": - ruler = js["value"] - self.layout_view.set_config("current-ruler-template", str(ruler)) - elif msg == "wheel": - self.wheel_event(self.layout_view.send_wheel_event, js) - - -def get_layer_properties() -> str | None: - lyp_path = GDSDIR_TEMP / "layers.lyp" - active_pdk = gf.get_active_pdk() - if active_pdk.layer_views: - lyp_path = active_pdk.layer_views.to_lyp(lyp_path) - return str(lyp_path) - - -def get_layout_view(component: gf.Component) -> lay.LayoutView: - """Returns klayout layout view for a gdsfactory Component.""" - gds_path = GDSDIR_TEMP / f"{component.name}.gds" - component.write_gds(gdspath=str(gds_path)) - layout_view = lay.LayoutView() - layout_view.load_layout(str(gds_path)) - lyp_path = get_layer_properties() - if lyp_path: - layout_view.load_layer_props(str(lyp_path)) - layout_view.max_hier() - return layout_view diff --git a/gplugins/web/server_jupyter.py b/gplugins/web/server_jupyter.py deleted file mode 100755 index 48346213..00000000 --- a/gplugins/web/server_jupyter.py +++ /dev/null @@ -1,28 +0,0 @@ -import asyncio - -import uvicorn - -from gplugins.web.main import app - -global jupyter_server -jupyter_server = None - - -def _run(port: int = 8000) -> None: - global jupyter_server - - config = uvicorn.Config(app, port=port) - jupyter_server = uvicorn.Server(config) - loop = asyncio.get_event_loop() - loop.create_task(jupyter_server.serve()) - - -def _server_is_running() -> bool: - global jupyter_server - return False if jupyter_server is None else jupyter_server.started - - -def start(port: int = 8000) -> None: - """Start a jupyter_server if it's not already started.""" - if not _server_is_running(): - _run(port=port) diff --git a/gplugins/web/static/client.css b/gplugins/web/static/client.css deleted file mode 100644 index 34f89931..00000000 --- a/gplugins/web/static/client.css +++ /dev/null @@ -1,109 +0,0 @@ -body { - font-family: Helvetica, Arial; - font-size: 12pt; - background-color: #F5F5F5; /* Soft Gray background for body */ - color: #333; /* Dark Gray for default text */ -} - -#layout_canvas { - width: 100%; - height: 100%; -} - -input, select { - background-color: #FAFAFA; /* Slightly off-white for input fields */ - padding: 4pt; - font-size: 12pt; - border: 1px solid #CCC; /* A subtle border */ -} - -.checked { - background-color: #2E86C1; /* Changing from Gray to Blue for a fresh look */ - color: white; -} - -.unchecked { - background-color: #FAFAFA; /* Consistency with other elements */ -} - -div .menu-left-frame, div .menu-right-frame { - background-color: #FAFAFA; /* Blue background for menus */ - padding: 0; - border-radius: 8px; /* A little rounding for modern feel */ -} - -div .menu-left-frame { - position: absolute; - left: 4pt; - top: 4pt; -} - -div .menu-right-frame { - position: absolute; - right: 4pt; - top: 4pt; -} - -.viewer-panel { - overflow: auto; - display: table; - border-spacing: 0.5rem; - padding: 0; -} - -.viewer-panel-sub { - display: table-row; -} - -.viewer-frame-cell, .viewer-layers-cell { - position: relative; - display: table-cell; - vertical-align: top; - border: 1px solid #DDD; /* Softer border color */ - overflow: hidden; -} - -.viewer-frame { - width: 800px; - height: 500px; - resize: both; - overflow: hidden; - padding: 10px; - padding-top: 40pt; - background-color: white; /* Clear background for frames */ - border-radius: 8px; /* Consistency with other rounded elements */ -} - -.viewer-layers { - min-width: 20%; - white-space: nowrap; - overflow-y: scroll; - padding: 0.5rem; - height: 500px; - background-color: white; /* Clear background for layers */ - border-radius: 8px; /* Consistency */ -} - -.layer-name-cell { - padding-left: 0.5rem; -} - -.layer-visible-cell { - padding-right: 0.5rem; -} - -a { - padding: 5px; - margin: 10px; - display: inline-block; - border: 2px solid #2E86C1; /* Matching Blue for link borders */ - color: #2E86C1; /* Matching Blue for link color */ - text-decoration: none; /* Remove default underline */ - border-radius: 8px; /* Consistency with other rounded elements */ - transition: background-color 0.3s, color 0.3s; /* Smooth hover transition */ -} - -a:hover { - background-color: #2E86C1; /* Blue background on hover */ - color: white; -} diff --git a/gplugins/web/static/client.js b/gplugins/web/static/client.js deleted file mode 100644 index ef39ecc2..00000000 --- a/gplugins/web/static/client.js +++ /dev/null @@ -1,410 +0,0 @@ - -// TODO: shouldn't be explicit here .. -// let url = 'ws://localhost:8765/ws'; - -let ws_url = current_url.replace("http://", "ws://"); -ws_url = ws_url.replace("https://", "wss://"); -ws_url += '/ws?' + "variant=" + cell_variant; -let url = ws_url; -console.log(url); - - -var canvas = document.getElementById("layout_canvas"); -var context = canvas.getContext("2d"); - -var message = document.getElementById("message"); - -// HTML5 does not have a resize event, so we need to poll -// to generate events for the canvas resize - -var lastCanvasWidth = 0; -var lastCanvasHeight = 0; - -setInterval(function () { - - var view = document.getElementById('layout-view'); - var w = view.clientWidth; - var h = view.clientHeight; - - if (lastCanvasWidth !== w || lastCanvasHeight !== h) { - - // this avoids flicker: - - var tempCanvas = document.createElement('canvas'); - tempCanvas.width = canvas.width; - tempCanvas.height = canvas.height; - var tempContext = tempCanvas.getContext("2d"); - tempContext.drawImage(context.canvas, 0, 0); - - lastCanvasWidth = w; - lastCanvasHeight = h; - canvas.width = canvas.clientWidth; - canvas.height = canvas.clientHeight; - - context.drawImage(tempContext.canvas, 0, 0); - if (socket.connected) { - socket.send(JSON.stringify({ msg: "resize", width: canvas.width, height: canvas.height })); - } - else { - console.log("Socket is not connected. Loading static image data...") - const img = new Image(); - img.src = initial_image_data; - img.onload = () => { - context.drawImage(img, 0, 0, img.width, img.height, // source rectangle - 0, 0, canvas.width, canvas.height); // destination rectangle - } - - // resizes the layer list: - - let layers = document.getElementById("layers"); - - var padding = 10; // padding in pixels - layers.style.height = (h - 2 * padding) + "px"; - - }; - } - -}, 10) - -let socket = new WebSocket(url); -socket.binaryType = "blob"; -var initialized = false; - -function showHierarchy(hierarchy) { - var hierarchyElement = document.getElementById("hierarchy"); - hierarchyElement.childNodes = new Array(); - - var hierarchyUL = document.createElement("ul"); - hierarchyUL.className = "hierarchy-ul"; - hierarchyElement.appendChild(hierarchyUL) - - function addToUL(hierarchy, ul){ - for (const [key, value] of Object.entries(hierarchy)) { - console.log(`${key}: ${value}`); - var li = document.createElement("li"); - ul.appendChild(li); - if(typeof value === 'object'){ //further hiera - caret = document.createElement("span"); - caret.className = "caret"; - caret.innerText = key; - sub_ul = document.createElement("ul"); - sub_ul.className = "nested" - li.appendChild(caret) - li.appendChild(sub_ul) - addToUL(value, sub_ul) - } else { - li.innerText = key; - } - } - } - - addToUL(hierarchy, hierarchyUL) - - var toggler = document.getElementsByClassName("caret"); - var i; - - for (i = 0; i < toggler.length; i++) { - toggler[i].addEventListener("click", function() { - this.parentElement.querySelector(".nested").classList.toggle("active"); - this.classList.toggle("caret-down"); - }); - } - -} - - -// Installs a handler called when the connection is established -socket.onopen = function (evt) { - - var ev = { msg: "initialize", width: canvas.width, height: canvas.height }; - socket.send(JSON.stringify(ev)); - - // Prevents the context menu to show up over the canvas area - canvas.addEventListener('contextmenu', function (evt) { - evt.preventDefault(); - }); - - canvas.addEventListener('mousemove', function (evt) { - sendMouseEvent(canvas, "mouse_move", evt); - evt.preventDefault(); - }, false); - - canvas.addEventListener('click', function (evt) { - sendMouseEvent(canvas, "mouse_click", evt); - evt.preventDefault(); - }, false); - - canvas.addEventListener('dblclick', function (evt) { - sendMouseEvent(canvas, "mouse_dblclick", evt); - evt.preventDefault(); - }, false); - - canvas.addEventListener('mousedown', function (evt) { - sendMouseEvent(canvas, "mouse_pressed", evt); - evt.preventDefault(); - }, false); - - canvas.addEventListener('mouseup', function (evt) { - sendMouseEvent(canvas, "mouse_released", evt); - evt.preventDefault(); - }, false); - - canvas.addEventListener('mouseenter', function (evt) { - sendMouseEvent(canvas, "mouse_enter", evt); - evt.preventDefault(); - }, false); - - canvas.addEventListener('mouseout', function (evt) { - sendMouseEvent(canvas, "mouse_leave", evt); - evt.preventDefault(); - }, false); - - canvas.addEventListener('wheel', function (evt) { - sendWheelEvent(canvas, "wheel", evt); - evt.preventDefault(); - }, false); -} - -// Installs a handler for the messages delivered by the web socket -socket.onmessage = function (evt) { - - let data = evt.data; - if (typeof (data) === "string") { - - // For debugging: - // message.textContent = data; - - // incoming messages are JSON objects - js = JSON.parse(data); - if (js.msg == "initialized") { - initialized = true; - } else if (js.msg == "loaded") { - showLayers(js.layers); - showMenu(js.modes, js.annotations); - } - - } else if (initialized) { - - // incoming blob messages are paint events - createImageBitmap(data).then(function (image) { - context.drawImage(image, 0, 0) - }); - - } - -}; - -socket.onclose = evt => console.log(`Closed ${evt.code}`); - -function mouseEventToJSON(canvas, type, evt) { - - var rect = canvas.getBoundingClientRect(); - var x = evt.clientX - rect.left; - var y = evt.clientY - rect.top; - var keys = 0; - if (evt.shiftKey) { - keys += 1; - } - if (evt.ctrlKey) { - keys += 2; - } - if (evt.altKey) { - keys += 4; - } - return { msg: type, x: x, y: y, b: evt.buttons, k: keys }; - -} - -function sendMouseEvent(canvas, type, evt) { - - if (socket.readyState == 1 /*OPEN*/) { - var ev = mouseEventToJSON(canvas, type, evt); - socket.send(JSON.stringify(ev)); - } - -} - -function sendWheelEvent(canvas, type, evt) { - - if (socket.readyState == 1 /*OPEN*/) { - var ev = mouseEventToJSON(canvas, type, evt); - ev.dx = evt.deltaX; - ev.dy = evt.deltaY; - ev.dm = evt.deltaMode; - socket.send(JSON.stringify(ev)); - } - -} - - - -// Updates the layer list -function showMenu(modes, annotations) { - - var modeElement = document.getElementById("modes"); - modeElement.childNodes = new Array(); - - var modeTable = document.createElement("table"); - modeTable.className = "modes-table"; - modeElement.appendChild(modeTable) - - var modeRow = document.createElement("tr"); - modeRow.className = "mode-row-header"; - modeRow.id = "mode-row"; - modeTable.appendChild(modeRow) - - var cell; - var inner; - - modes.forEach(function (m) { - - cell = document.createElement("td"); - cell.className = "mode-cell"; - - var inner = document.createElement("input"); - inner.value = m; - inner.type = "button"; - inner.className = "unchecked"; - inner.onclick = function () { - var modeRow = document.getElementById("mode-row"); - modeRow.childNodes.forEach(function (e) { - e.firstChild.className = "unchecked"; - }); - inner.className = "checked"; - socket.send(JSON.stringify({ msg: "select-mode", value: m })); - }; - - cell.appendChild(inner); - modeRow.appendChild(cell); - - }); - - var menuElement = document.getElementById("menu"); - - var menuTable = document.createElement("table"); - menuTable.className = "menu-table"; - menuElement.appendChild(menuTable) - - var menuRow = document.createElement("tr"); - menuRow.className = "menu-row-header"; - menuTable.appendChild(menuRow) - - cell = document.createElement("td"); - cell.className = "menu-cell"; - menuRow.appendChild(cell); - - var rulersSelect = document.createElement("select"); - rulersSelect.onchange = function () { - socket.send(JSON.stringify({ msg: "select-ruler", value: rulersSelect.selectedIndex })); - }; - cell.appendChild(rulersSelect); - - cell = document.createElement("td"); - cell.className = "menu-cell"; - menuRow.appendChild(cell); - - var clearRulers = document.createElement("input"); - clearRulers.value = "Clear Rulers"; - clearRulers.type = "button"; - clearRulers.onclick = function () { - socket.send(JSON.stringify({ msg: "clear-annotations" })); - }; - cell.appendChild(clearRulers); - - var index = 0; - - annotations.forEach(function (a) { - - var option = document.createElement("option"); - option.value = index; - option.text = a; - - rulersSelect.appendChild(option); - - index += 1; - - }); -} - -// Updates the layer list -function showLayers(layers) { - - var layerElement = document.getElementById("layers"); - layerElement.childNodes = new Array(); - - var layerTable = document.createElement("table"); - layerTable.className = "layer-table"; - layerElement.appendChild(layerTable) - - var cell; - var inner; - var s; - var visibilityCheckboxes = []; - - var layerRow = document.createElement("tr"); - layerRow.className = "layer-row-header"; - - // create a top level entry for resetting/setting all visible flags - - cell = document.createElement("td"); - cell.className = "layer-visible-cell"; - - inner = document.createElement("input"); - inner.type = "checkbox"; - inner.checked = true; - inner.onclick = function () { - var checked = this.checked; - visibilityCheckboxes.forEach(function (cb) { - cb.checked = checked; - }); - socket.send(JSON.stringify({ msg: "layer-v-all", value: checked })); - }; - cell.appendChild(inner); - - layerRow.appendChild(cell); - layerTable.appendChild(layerRow); - - // create table rows for each layer - - layers.forEach(function (l) { - - var layerRow = document.createElement("tr"); - layerRow.className = "layer-row"; - - cell = document.createElement("td"); - cell.className = "layer-visible-cell"; - - inner = document.createElement("input"); - visibilityCheckboxes.push(inner); - inner.type = "checkbox"; - inner.checked = l.v; - inner.onclick = function () { - socket.send(JSON.stringify({ msg: "layer-v", id: l.id, value: this.checked })); - }; - cell.appendChild(inner); - - layerRow.appendChild(cell); - - cell = document.createElement("td"); - cell.className = "layer-color-cell"; - s = "border-style: solid; border-width: " + (l.w < 0 ? 1 : l.w) + "px; border-color: #" + (l.fc & 0xffffff).toString(16) + ";"; - cell.style = s; - layerRow.appendChild(cell); - - inner = document.createElement("div"); - s = "width: 2rem; height: 1em;"; - s += "margin: 1px;"; - s += "background: #" + (l.c & 0xffffff).toString(16) + ";"; - inner.style = s; - cell.appendChild(inner); - - cell = document.createElement("td"); - cell.className = "layer-name-cell"; - cell.textContent = (l.name != 0 ? l.name : l.s); - layerRow.appendChild(cell); - - layerTable.appendChild(layerRow); - - }); - -} diff --git a/gplugins/web/templates/client.html.j2 b/gplugins/web/templates/client.html.j2 deleted file mode 100644 index 4ad10d6e..00000000 --- a/gplugins/web/templates/client.html.j2 +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - -
-
-
- - -
- -
-
-
-
-
-
-
- -
-
- - - - - - - diff --git a/gplugins/web/templates/file_browser.html.j2 b/gplugins/web/templates/file_browser.html.j2 deleted file mode 100644 index 3bf1757b..00000000 --- a/gplugins/web/templates/file_browser.html.j2 +++ /dev/null @@ -1,27 +0,0 @@ -{% include 'header.html.j2' %} -{% include 'navbar.html.j2' %} - - - KWeb GDS File Browser - - - - -
-

-{{ message }} -

-
- -
-

Files available:

- -

File root: {{ files_root }}

-
- - - diff --git a/gplugins/web/templates/filewatcher.html.j2 b/gplugins/web/templates/filewatcher.html.j2 deleted file mode 100644 index a4c699dd..00000000 --- a/gplugins/web/templates/filewatcher.html.j2 +++ /dev/null @@ -1,66 +0,0 @@ -{% include 'header.html.j2' %} -{% include 'navbar.html.j2' %} - - - Folder Watcher - - - -

Folder to watch for changing files

-
- - - -
- -
-
{{ output }}
-
- - -
-

{{ cell_name }}

- {% if variant %} -

({{ variant }})

- {% endif %} - -
-
-
- - -
- -
-
-
-
-
-
-
-
- -
-
- - - - - -{% include 'footer.html.j2' %} diff --git a/gplugins/web/templates/footer.html.j2 b/gplugins/web/templates/footer.html.j2 deleted file mode 100644 index fc1b9604..00000000 --- a/gplugins/web/templates/footer.html.j2 +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/gplugins/web/templates/gds_history.html.j2 b/gplugins/web/templates/gds_history.html.j2 deleted file mode 100644 index 6b3731aa..00000000 --- a/gplugins/web/templates/gds_history.html.j2 +++ /dev/null @@ -1,10 +0,0 @@ -{% include 'header.html.j2' %} -{% include 'navbar.html.j2' %} -

gdspaths

-

-{% for component in components %} -{{ component }} -{% endfor %} -

- -{% include 'footer.html.j2' %} diff --git a/gplugins/web/templates/header.html.j2 b/gplugins/web/templates/header.html.j2 deleted file mode 100644 index ad233f9f..00000000 --- a/gplugins/web/templates/header.html.j2 +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - Gdsfactory - {{ title | default("Page") }} - - - - - - - - - - - - - - - diff --git a/gplugins/web/templates/index.html.j2 b/gplugins/web/templates/index.html.j2 deleted file mode 100644 index d2f34f29..00000000 --- a/gplugins/web/templates/index.html.j2 +++ /dev/null @@ -1,14 +0,0 @@ -{% include 'header.html.j2' %} -{% include 'navbar.html.j2' %} - - - Homepage - - -

gdsfactory webapp

-

Pdk explorer

-

Last Component.show()

-

Component.show() list

-

filewatcher

- - diff --git a/gplugins/web/templates/navbar.html.j2 b/gplugins/web/templates/navbar.html.j2 deleted file mode 100644 index 1bdedc42..00000000 --- a/gplugins/web/templates/navbar.html.j2 +++ /dev/null @@ -1,19 +0,0 @@ - diff --git a/gplugins/web/templates/pdk.html.j2 b/gplugins/web/templates/pdk.html.j2 deleted file mode 100644 index 938ccc7d..00000000 --- a/gplugins/web/templates/pdk.html.j2 +++ /dev/null @@ -1,10 +0,0 @@ -{% include 'header.html.j2' %} -{% include 'navbar.html.j2' %} -

{{ pdk_name }} PDK explorer

-

-{% for component in components %} -

  • {{ component }}
  • -{% endfor %} -

    - -{% include 'footer.html.j2' %} diff --git a/gplugins/web/templates/viewer.html.j2 b/gplugins/web/templates/viewer.html.j2 deleted file mode 100644 index 645e637c..00000000 --- a/gplugins/web/templates/viewer.html.j2 +++ /dev/null @@ -1,44 +0,0 @@ -{% include 'header.html.j2' %} -{% include 'navbar.html.j2' %} -
    -

    {{ cell_name }}

    - {% if variant %} -

    ({{ variant }})

    - {% endif %} - -
    -
    -
    - - -
    - -
    -
    -
    -
    -
    -
    -
    -

    Parameters

    -
    - - {% for setting_name, setting_value in component.settings.default.items() %} -
    - - {% if setting_name in component.settings.changed %} - - {% else %} - - {% endif %} -
    - {% endfor %} -
    -
    - - -{% include 'footer.html.j2' %} diff --git a/pyproject.toml b/pyproject.toml index f7c4fb4b..7caa4c2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,12 +96,6 @@ vlsir = [ "vlsir>=4.0.0,<6.0.0", "vlsirtools>=4.0.0,<6.0.0" ] -web = [ - "jinja2", - "python-multipart", - "fastapi>=0.102.0,<1", - "uvicorn[standard]" -] [tool.black] exclude = ''' # Specify the files/dirs that should be ignored by the black formatter