@bilalba/fig-mcp 1.1.2 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -13
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +42 -5
- package/dist/mcp/server.js.map +1 -1
- package/dist/parser/index.d.ts +5 -0
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +21 -0
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/instance-resolver.d.ts +68 -0
- package/dist/parser/instance-resolver.d.ts.map +1 -0
- package/dist/parser/instance-resolver.js +580 -0
- package/dist/parser/instance-resolver.js.map +1 -0
- package/dist/parser/kiwi-parser.d.ts.map +1 -1
- package/dist/parser/kiwi-parser.js +9 -0
- package/dist/parser/kiwi-parser.js.map +1 -1
- package/dist/parser/types.d.ts +4 -0
- package/dist/parser/types.d.ts.map +1 -1
- package/dist/render-single.js +6 -2
- package/dist/render-single.js.map +1 -1
- package/dist/renderer/render-screen.d.ts.map +1 -1
- package/dist/renderer/render-screen.js +122 -2
- package/dist/renderer/render-screen.js.map +1 -1
- package/dist/renderer/render-types.d.ts +6 -1
- package/dist/renderer/render-types.d.ts.map +1 -1
- package/dist/renderer/render-types.js +2 -0
- package/dist/renderer/render-types.js.map +1 -1
- package/dist/web-viewer/build-client.d.ts +1 -0
- package/dist/web-viewer/build-client.d.ts.map +1 -1
- package/dist/web-viewer/build-client.js +32 -5
- package/dist/web-viewer/build-client.js.map +1 -1
- package/dist/web-viewer/client/dist/viewer.js +10 -0
- package/dist/web-viewer/client/index.html +138 -0
- package/dist/web-viewer/client/styles.css +561 -0
- package/dist/web-viewer/server.d.ts.map +1 -1
- package/dist/web-viewer/server.js +2 -0
- package/dist/web-viewer/server.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";(()=>{var _=null,E=[],L=null,y=null,u=1,C="",V=null,f=[],T=null,g=null,r=e=>document.getElementById(e),i={fileName:r("file-name"),openBtn:r("open-btn"),search:r("search"),pagesList:r("pages-list"),tree:r("tree"),canvas:r("canvas"),canvasPlaceholder:r("canvas-placeholder"),zoomIn:r("zoom-in"),zoomOut:r("zoom-out"),zoomLevel:r("zoom-level"),zoomFit:r("zoom-fit"),noSelection:r("no-selection"),nodeDetails:r("node-details"),nodeId:r("node-id"),copyId:r("copy-id"),nodeType:r("node-type"),nodeName:r("node-name"),nodeX:r("node-x"),nodeY:r("node-y"),nodeWidth:r("node-width"),nodeHeight:r("node-height"),textSection:r("text-section"),nodeText:r("node-text"),nodeJson:r("node-json"),fileDialog:r("file-dialog"),filePathInput:r("file-path-input"),cancelOpen:r("cancel-open"),confirmOpen:r("confirm-open")};async function b(e,n){let t=await fetch(e,n);if(!t.ok){let o=await t.json().catch(()=>({error:t.statusText}));throw new Error(o.error||"Request failed")}return t.json()}async function j(){return b("/api/tree")}async function J(e){return b(`/api/node/${encodeURIComponent(e)}`)}async function Z(e){return b(`/api/node-raw/${encodeURIComponent(e)}`)}async function K(e){let n=await fetch(`/api/render/${encodeURIComponent(e)}`);if(!n.ok)throw new Error("Failed to render");return n.text()}async function Q(e){return(await b(`/api/flat-nodes/${encodeURIComponent(e)}`)).nodes}async function ee(e){await b("/api/open",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({filePath:e})})}function te(e){return{DOCUMENT:"D",CANVAS:"P",FRAME:"F",GROUP:"G",TEXT:"T",RECTANGLE:"R",ELLIPSE:"O",VECTOR:"V",LINE:"L",STAR:"*",REGULAR_POLYGON:"P",COMPONENT:"C",COMPONENT_SET:"S",INSTANCE:"I",BOOLEAN_OPERATION:"B",SLICE:"S",STICKY:"N",SHAPE_WITH_TEXT:"ST",CONNECTOR:"CN",SECTION:"SE"}[e]||"?"}function ne(){i.pagesList.innerHTML="";for(let e of E){let n=document.createElement("div");n.className="page-item",e.id===L&&n.classList.add("selected"),n.dataset.pageId=e.id;let t=document.createElement("span");t.className="page-icon",t.textContent="P",n.appendChild(t);let o=document.createElement("span");o.className="page-name",o.textContent=e.name||"(unnamed)",n.appendChild(o),n.addEventListener("click",()=>ie(e.id)),i.pagesList.appendChild(n)}}function ie(e){L=e,y=null,i.pagesList.querySelectorAll(".page-item").forEach(n=>{n.classList.toggle("selected",n.getAttribute("data-page-id")===e)}),I(),i.canvas.innerHTML='<div id="canvas-placeholder">Select a node to preview</div>',i.noSelection.classList.remove("hidden"),i.nodeDetails.classList.add("hidden")}function oe(e,n){if(!n)return!0;let t=n.toLowerCase();return e.name.toLowerCase().includes(t)||e.type.toLowerCase().includes(t)||e.id.toLowerCase().includes(t)}function G(e,n){return oe(e,n)?!0:e.children?e.children.some(t=>G(t,n)):!1}function N(e,n=0){let t=document.createElement("div");t.className="tree-node",t.dataset.nodeId=e.id;let o=e.children&&e.children.length>0,s=n<2;s&&o&&t.classList.add("expanded");let a=document.createElement("div");a.className="tree-node-row",a.style.paddingLeft=`${n*16+8}px`,e.id===y&&a.classList.add("selected");let l=document.createElement("span");l.className=`tree-toggle ${o?s?"expanded":"collapsed":""}`,o&&l.addEventListener("click",v=>{v.stopPropagation(),t.classList.toggle("expanded"),l.classList.toggle("collapsed"),l.classList.toggle("expanded")}),a.appendChild(l);let c=document.createElement("span");c.className=`tree-icon type-${e.type}`,c.textContent=te(e.type),a.appendChild(c);let d=document.createElement("span");d.className="tree-name",d.textContent=e.name||"(unnamed)",a.appendChild(d);let p=document.createElement("span");if(p.className="tree-type",p.textContent=e.type,a.appendChild(p),a.addEventListener("click",()=>O(e.id)),t.appendChild(a),o){let v=document.createElement("div");v.className="tree-children";for(let w of e.children)C&&!G(w,C)||v.appendChild(N(w,n+1));t.appendChild(v)}return t}function I(){i.tree.innerHTML="";let e=E.find(n=>n.id===L);if(!e){i.tree.innerHTML='<div style="padding: 20px; color: #999;">Select a page to view its contents</div>';return}if(C){let n=o=>{o.classList.add("expanded");let s=o.querySelector(".tree-toggle");s&&(s.classList.remove("collapsed"),s.classList.add("expanded"))},t=document.createDocumentFragment();t.appendChild(N(e)),i.tree.appendChild(t),i.tree.querySelectorAll(".tree-node").forEach(o=>{n(o)})}else i.tree.appendChild(N(e))}async function O(e,n){y=e,i.tree.querySelectorAll(".tree-node-row.selected").forEach(o=>{o.classList.remove("selected")});let t=i.tree.querySelector(`[data-node-id="${e}"] > .tree-node-row`);t&&t.classList.add("selected"),i.noSelection.classList.add("hidden"),i.nodeDetails.classList.remove("hidden"),i.nodeId.textContent=e;try{let[o,s]=await Promise.allSettled([J(e),Z(e)]);if(o.status!=="fulfilled")throw o.reason;let{node:a}=o.value,l=s.status==="fulfilled"?s.value.node:a;i.nodeType.textContent=a.type,i.nodeName.textContent=a.name||"(unnamed)",i.nodeX.textContent=a.x?.toFixed(1)??"-",i.nodeY.textContent=a.y?.toFixed(1)??"-",i.nodeWidth.textContent=a.width?.toFixed(1)??"-",i.nodeHeight.textContent=a.height?.toFixed(1)??"-",a.characters?(i.textSection.classList.remove("hidden"),i.nodeText.textContent=a.characters):i.textSection.classList.add("hidden"),i.nodeJson.textContent=JSON.stringify(l,null,2),n?.skipRender?M(e):await ae(e)}catch(o){console.error("Failed to load node details:",o)}}async function ae(e){let n=e!==V;try{let[t,o]=await Promise.all([K(e),Q(e)]);if(i.canvas.innerHTML=t,i.canvasPlaceholder?.remove(),V=e,f=o,o.length>0){let s=1/0,a=1/0,l=-1/0,c=-1/0;for(let d of o)s=Math.min(s,d.absX),a=Math.min(a,d.absY),l=Math.max(l,d.absX+d.width),c=Math.max(c,d.absY+d.height);g={minX:s,minY:a,width:l-s,height:c-a}}else g=null;if(re(),n){x=0,H=0,u=1;let s=F(),a=i.canvas.parentElement;s?(a.scrollLeft=0,a.scrollTop=0):A()}}catch(t){console.error("Failed to render preview:",t),i.canvas.innerHTML=`<div id="canvas-placeholder">Failed to render: ${t}</div>`,i.canvas.style.width="",i.canvas.style.height="",f=[],g=null}}var x=0,H=0;function F(){let n=i.canvas.parentElement.getBoundingClientRect();i.zoomLevel.textContent=`${Math.round(u*100)}%`,i.canvas.style.transform="";let t=i.canvas.querySelector("svg:not(#hover-overlay)");if(t){x===0&&(x=t.width.baseVal.value||parseFloat(t.getAttribute("width")||"100"),H=t.height.baseVal.value||parseFloat(t.getAttribute("height")||"100"));let o=x*u,s=H*u;t.setAttribute("width",String(o)),t.setAttribute("height",String(s));let a=i.canvas.querySelector("#hover-overlay");a&&(a.setAttribute("width",String(o)),a.setAttribute("height",String(s)));let l=80,c=o+l,d=s+l,p=c<=n.width&&d<=n.height;return p?(i.canvas.style.width="",i.canvas.style.height=""):(i.canvas.style.width=`${c}px`,i.canvas.style.height=`${d}px`),p}return!0}function P(e,n){let t=i.canvas.parentElement,o=t.getBoundingClientRect(),s=u,a=n||{x:o.width/2,y:o.height/2},l=40,c=(t.scrollLeft+a.x-l)/s,d=(t.scrollTop+a.y-l)/s;if(u=e,F())t.scrollLeft=0,t.scrollTop=0;else{let v=c*e+l-a.x,w=d*e+l-a.y,q=Math.max(0,t.scrollWidth-t.clientWidth),U=Math.max(0,t.scrollHeight-t.clientHeight);t.scrollLeft=Math.max(0,Math.min(q,v)),t.scrollTop=Math.max(0,Math.min(U,w))}}function Y(){let e=Math.min(u*1.15,10);P(e)}function X(){let e=Math.max(u/1.15,.1);P(e)}function se(e,n){let t=Math.max(-100,Math.min(100,e)),o=1+Math.abs(t)*.003,s;t<0?s=Math.min(u*o,10):s=Math.max(u/o,.1),P(s,n)}function A(){let e=i.canvas.querySelector("svg:not(#hover-overlay)");if(!e)return;let n=i.canvas.parentElement,t=n.getBoundingClientRect(),o=e.width.baseVal.value||parseFloat(e.getAttribute("width")||"100"),s=e.height.baseVal.value||parseFloat(e.getAttribute("height")||"100"),a=80,l=(t.width-a)/o,c=(t.height-a)/s;u=Math.min(l,c,1);let d=F();n.scrollLeft=0,n.scrollTop=0}var h=null,m=null;function re(){m&&m.remove();let e=i.canvas.querySelector("svg");e&&(m=document.createElement("div"),m.id="svg-wrapper",m.style.cssText=`
|
|
2
|
+
position: relative;
|
|
3
|
+
display: inline-block;
|
|
4
|
+
`,e.parentNode?.insertBefore(m,e),m.appendChild(e),h=document.createElementNS("http://www.w3.org/2000/svg","svg"),h.id="hover-overlay",h.style.cssText=`
|
|
5
|
+
position: absolute;
|
|
6
|
+
top: 0;
|
|
7
|
+
left: 0;
|
|
8
|
+
pointer-events: none;
|
|
9
|
+
overflow: visible;
|
|
10
|
+
`,m.appendChild(h))}function z(){return m?m.querySelector("svg:not(#hover-overlay)"):i.canvas.querySelector("svg:not(#hover-overlay)")}function M(e){if(!h||!g||(h.innerHTML="",!e))return;let n=f.find(d=>d.id===e);if(!n)return;let t=z();if(!t)return;let o=t.width.baseVal.value||parseFloat(t.getAttribute("width")||"0"),s=t.height.baseVal.value||parseFloat(t.getAttribute("height")||"0");h.setAttribute("viewBox",`0 0 ${o} ${s}`),h.setAttribute("width",String(o)),h.setAttribute("height",String(s));let a=n.absX-g.minX,l=n.absY-g.minY,c=document.createElementNS("http://www.w3.org/2000/svg","rect");c.setAttribute("x",String(a)),c.setAttribute("y",String(l)),c.setAttribute("width",String(n.width)),c.setAttribute("height",String(n.height)),c.setAttribute("fill","rgba(0, 120, 212, 0.1)"),c.setAttribute("stroke","#0078d4"),c.setAttribute("stroke-width","2"),h.appendChild(c)}function k(e,n){let t=z();if(!t||!g)return null;let o=t.getBoundingClientRect(),s=t.width.baseVal.value||parseFloat(t.getAttribute("width")||"0"),a=t.height.baseVal.value||parseFloat(t.getAttribute("height")||"0"),l=(e-o.left)/u,c=(n-o.top)/u,d=l+g.minX,p=c+g.minY;return{x:d,y:p}}function R(e,n){return f.filter(t=>t.visible&&e>=t.absX&&e<=t.absX+t.width&&n>=t.absY&&n<=t.absY+t.height).sort((t,o)=>o.depth-t.depth)}function D(e){return e.length===0?null:e[0]}function $(e){let n=i.tree.querySelector(`[data-node-id="${e}"]`);if(!n)return;let t=n.parentElement;for(;t;){if(t.classList?.contains("tree-node")){t.classList.add("expanded");let s=t.querySelector(":scope > .tree-node-row > .tree-toggle");s&&(s.classList.remove("collapsed"),s.classList.add("expanded"))}if(t.id==="tree")break;t=t.parentElement}let o=n.querySelector(":scope > .tree-node-row");o&&o.scrollIntoView({behavior:"smooth",block:"center"})}function le(e){if(f.length===0)return;let n=k(e.clientX,e.clientY);if(!n){T=null,M(null);return}let t=R(n.x,n.y),o=D(t);o?.id!==T&&(T=o?.id??null,M(T))}function ce(e){if(f.length===0)return;let n=k(e.clientX,e.clientY);if(!n)return;let t=R(n.x,n.y),o=D(t);if(o){$(o.id);let s=f.some(a=>a.id===o.id);O(o.id,{skipRender:s})}}function de(e){if(f.length===0)return;let n=k(e.clientX,e.clientY);if(!n)return;let t=R(n.x,n.y),o=D(t);o&&($(o.id),O(o.id))}function ue(){T=null,M(null)}function me(){i.fileDialog.classList.remove("hidden"),i.filePathInput.focus()}function S(){i.fileDialog.classList.add("hidden")}async function B(){let e=i.filePathInput.value.trim();if(e)try{i.confirmOpen.disabled=!0,i.confirmOpen.textContent="Loading...",await ee(e),S(),L=null,y=null,await W(),i.fileName.textContent=e.split("/").pop()||e}catch(n){alert(`Failed to open file: ${n}`)}finally{i.confirmOpen.disabled=!1,i.confirmOpen.textContent="Open"}}async function W(){try{let{tree:e,meta:n}=await j();_=e,E=[],e.children&&(E=e.children.filter(t=>t.type==="CANVAS")),E.length>0&&!L&&(L=E[0].id),ne(),I(),n?.name&&(i.fileName.textContent=n.name)}catch(e){console.error("Failed to load tree:",e),E=[],i.pagesList.innerHTML="",i.tree.innerHTML='<div style="padding: 20px; color: #999;">No file loaded. Click "Open File" to start.</div>'}}async function he(){if(y)try{await navigator.clipboard.writeText(y);let e=i.copyId.textContent;i.copyId.textContent="Copied!",setTimeout(()=>{i.copyId.textContent=e},1e3)}catch{let e=document.createElement("textarea");e.value=y,document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e)}}function pe(){i.zoomIn.addEventListener("click",Y),i.zoomOut.addEventListener("click",X),i.zoomFit.addEventListener("click",A),document.addEventListener("keydown",e=>{e.target instanceof HTMLInputElement||(e.key==="="||e.key==="+"?(e.preventDefault(),Y()):e.key==="-"?(e.preventDefault(),X()):e.key==="0"&&(e.preventDefault(),A()))}),i.canvas.parentElement?.addEventListener("wheel",e=>{if(e.ctrlKey||e.metaKey){e.preventDefault();let t=i.canvas.parentElement.getBoundingClientRect(),o={x:e.clientX-t.left,y:e.clientY-t.top};se(e.deltaY,o)}},{passive:!1}),i.canvas.addEventListener("mousemove",le),i.canvas.addEventListener("click",ce),i.canvas.addEventListener("dblclick",de),i.canvas.addEventListener("mouseleave",ue),i.search.addEventListener("input",e=>{C=e.target.value,I()}),i.openBtn.addEventListener("click",me),i.cancelOpen.addEventListener("click",S),i.confirmOpen.addEventListener("click",B),i.filePathInput.addEventListener("keydown",e=>{e.key==="Enter"?B():e.key==="Escape"&&S()}),i.fileDialog.addEventListener("click",e=>{e.target===i.fileDialog&&S()}),i.copyId.addEventListener("click",he),W()}document.addEventListener("DOMContentLoaded",pe);})();
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Fig Viewer</title>
|
|
7
|
+
<link rel="stylesheet" href="/styles.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app">
|
|
11
|
+
<!-- Header -->
|
|
12
|
+
<header id="header">
|
|
13
|
+
<h1>Fig Viewer</h1>
|
|
14
|
+
<div id="file-info">
|
|
15
|
+
<span id="file-name">No file loaded</span>
|
|
16
|
+
<button id="open-btn" title="Open .fig file">Open File</button>
|
|
17
|
+
</div>
|
|
18
|
+
</header>
|
|
19
|
+
|
|
20
|
+
<!-- Main layout -->
|
|
21
|
+
<div id="main">
|
|
22
|
+
<!-- Left panel: Tree view -->
|
|
23
|
+
<aside id="tree-panel">
|
|
24
|
+
<!-- Pages list -->
|
|
25
|
+
<div id="pages-section">
|
|
26
|
+
<div id="pages-header">
|
|
27
|
+
<span>Pages</span>
|
|
28
|
+
</div>
|
|
29
|
+
<div id="pages-list"></div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- Tree with search -->
|
|
33
|
+
<div id="tree-section">
|
|
34
|
+
<div id="tree-header">
|
|
35
|
+
<input type="text" id="search" placeholder="Search nodes..." />
|
|
36
|
+
</div>
|
|
37
|
+
<div id="tree-container">
|
|
38
|
+
<div id="tree"></div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</aside>
|
|
42
|
+
|
|
43
|
+
<!-- Center: Canvas/Preview -->
|
|
44
|
+
<main id="canvas-panel">
|
|
45
|
+
<div id="canvas-toolbar">
|
|
46
|
+
<button id="zoom-in" title="Zoom in">+</button>
|
|
47
|
+
<span id="zoom-level">100%</span>
|
|
48
|
+
<button id="zoom-out" title="Zoom out">-</button>
|
|
49
|
+
<button id="zoom-fit" title="Fit to view">Fit</button>
|
|
50
|
+
</div>
|
|
51
|
+
<div id="canvas-container">
|
|
52
|
+
<div id="canvas">
|
|
53
|
+
<div id="canvas-placeholder">
|
|
54
|
+
Select a node to preview
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</main>
|
|
59
|
+
|
|
60
|
+
<!-- Right panel: Node details -->
|
|
61
|
+
<aside id="details-panel">
|
|
62
|
+
<div id="details-header">
|
|
63
|
+
<h2>Node Details</h2>
|
|
64
|
+
</div>
|
|
65
|
+
<div id="details-content">
|
|
66
|
+
<div id="no-selection">Select a node to see details</div>
|
|
67
|
+
<div id="node-details" class="hidden">
|
|
68
|
+
<div class="detail-section">
|
|
69
|
+
<div class="detail-row">
|
|
70
|
+
<label>ID</label>
|
|
71
|
+
<div class="detail-value">
|
|
72
|
+
<code id="node-id"></code>
|
|
73
|
+
<button id="copy-id" title="Copy ID">Copy</button>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="detail-row">
|
|
77
|
+
<label>Type</label>
|
|
78
|
+
<span id="node-type"></span>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="detail-row">
|
|
81
|
+
<label>Name</label>
|
|
82
|
+
<span id="node-name"></span>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="detail-section">
|
|
87
|
+
<h3>Bounds</h3>
|
|
88
|
+
<div class="detail-grid">
|
|
89
|
+
<div class="detail-cell">
|
|
90
|
+
<label>X</label>
|
|
91
|
+
<span id="node-x">-</span>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="detail-cell">
|
|
94
|
+
<label>Y</label>
|
|
95
|
+
<span id="node-y">-</span>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="detail-cell">
|
|
98
|
+
<label>W</label>
|
|
99
|
+
<span id="node-width">-</span>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="detail-cell">
|
|
102
|
+
<label>H</label>
|
|
103
|
+
<span id="node-height">-</span>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div class="detail-section" id="text-section">
|
|
109
|
+
<h3>Text</h3>
|
|
110
|
+
<div id="node-text"></div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="detail-section">
|
|
114
|
+
<h3>Raw JSON</h3>
|
|
115
|
+
<pre id="node-json"></pre>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</aside>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<!-- File picker dialog (hidden by default) -->
|
|
124
|
+
<div id="file-dialog" class="dialog hidden">
|
|
125
|
+
<div class="dialog-content">
|
|
126
|
+
<h2>Open .fig File</h2>
|
|
127
|
+
<p>Enter the path to a .fig file on your local machine:</p>
|
|
128
|
+
<input type="text" id="file-path-input" placeholder="/path/to/design.fig" />
|
|
129
|
+
<div class="dialog-buttons">
|
|
130
|
+
<button id="cancel-open">Cancel</button>
|
|
131
|
+
<button id="confirm-open" class="primary">Open</button>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<script src="/viewer.js"></script>
|
|
137
|
+
</body>
|
|
138
|
+
</html>
|