@studioindia/designx 1.0.0

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 ADDED
@@ -0,0 +1,83 @@
1
+ # DesignX
2
+
3
+ AI-powered product design pipeline — from brief to working app.
4
+
5
+ DesignX is a local-first design tool that runs entirely on your machine. It uses your GitHub Copilot subscription to guide you through a structured design process: research, personas, journey maps, feature trees, data models, and generated code — all in one place.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js 18 or later
10
+ - An active [GitHub Copilot](https://github.com/features/copilot) subscription
11
+
12
+ ## Getting started
13
+
14
+ ```bash
15
+ npx @studioindia/designx
16
+ ```
17
+
18
+ That's it. DesignX starts a local server on port 3000 and opens the UI in your default browser. All your data is stored in `~/.designx/` on your own machine — nothing is sent to any external server.
19
+
20
+ On first launch, click **Connect GitHub Copilot** and follow the Device Flow prompt to authenticate with your GitHub account.
21
+
22
+ ## Options
23
+
24
+ ```
25
+ designx [options]
26
+
27
+ --port <port> Port to listen on (default: 3000)
28
+ --data-dir <path> Where to store project data (default: ~/.designx)
29
+ --no-open Do not open browser automatically
30
+ --version, -v Print version
31
+ --help, -h Print help
32
+ ```
33
+
34
+ ## Examples
35
+
36
+ ```bash
37
+ # Default
38
+ npx @studioindia/designx
39
+
40
+ # Custom port
41
+ npx @studioindia/designx --port 8080
42
+
43
+ # Custom data directory
44
+ npx @studioindia/designx --data-dir ~/Documents/my-designx
45
+
46
+ # Headless (no browser auto-open)
47
+ npx @studioindia/designx --no-open
48
+ ```
49
+
50
+ ## Data directory layout
51
+
52
+ ```
53
+ ~/.designx/
54
+ ├── data/
55
+ │ ├── kb-jobs.json
56
+ │ └── agent-runs.json
57
+ └── projects/
58
+ └── {project-slug}/
59
+ ├── project.json
60
+ ├── conversation.json
61
+ ├── artifacts.json
62
+ ├── session.json
63
+ ├── uploads/ ← knowledge base files
64
+ └── built-app/ ← exported generated code
65
+ ```
66
+
67
+ ## How authentication works
68
+
69
+ DesignX uses [GitHub Device Flow](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#device-flow) — the same flow used by the GitHub CLI. You are redirected to `github.com/login/device`, enter a short code, and authorize the app with your own GitHub account. No passwords are stored by DesignX. Your access token lives only in your local `~/.designx/` directory.
70
+
71
+ ## Updates
72
+
73
+ DesignX checks for new versions on startup. When an update is available, a banner appears in the UI with a **Relaunch to update** button — click it and DesignX restarts with the latest version automatically.
74
+
75
+ To update manually:
76
+
77
+ ```bash
78
+ npx @studioindia/designx@latest
79
+ ```
80
+
81
+ ## License
82
+
83
+ MIT
package/bin/designx.js ADDED
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ // DesignX CLI entry point
3
+ // Usage: npx designx [options]
4
+ // designx [options]
5
+
6
+ import { spawn, exec } from 'node:child_process'
7
+ import { mkdirSync, existsSync, writeFileSync, unlinkSync } from 'node:fs'
8
+ import { join } from 'node:path'
9
+ import { homedir } from 'node:os'
10
+ import { createRequire } from 'node:module'
11
+ import { fileURLToPath } from 'node:url'
12
+
13
+ const __dirname = fileURLToPath(new URL('.', import.meta.url))
14
+ const require = createRequire(import.meta.url)
15
+ const { version } = require('../package.json')
16
+
17
+ // ── Argument parsing ──────────────────────────────────────────────────────────
18
+
19
+ const args = process.argv.slice(2)
20
+
21
+ function getArg(flag) {
22
+ const eqIdx = args.findIndex((a) => a.startsWith(`${flag}=`))
23
+ if (eqIdx !== -1) return args[eqIdx].slice(flag.length + 1)
24
+ const spaceIdx = args.indexOf(flag)
25
+ if (spaceIdx !== -1 && args[spaceIdx + 1] && !args[spaceIdx + 1].startsWith('-')) {
26
+ return args[spaceIdx + 1]
27
+ }
28
+ return null
29
+ }
30
+
31
+ const PORT = getArg('--port') ?? process.env.DESIGNX_PORT ?? '3000'
32
+ const DATA_DIR = getArg('--data-dir') ?? process.env.DESIGNX_DATA_DIR ?? join(homedir(), '.designx')
33
+ const NO_OPEN = args.includes('--no-open')
34
+
35
+ // ── Help / version ────────────────────────────────────────────────────────────
36
+
37
+ if (args.includes('--version') || args.includes('-v')) {
38
+ console.log(`designx v${version}`)
39
+ process.exit(0)
40
+ }
41
+
42
+ if (args.includes('--help') || args.includes('-h')) {
43
+ console.log(`
44
+ designx v${version}
45
+
46
+ Usage: designx [options]
47
+
48
+ Options:
49
+ --port <port> Port to listen on (default: 3000)
50
+ --data-dir <path> Where to store all data (default: ~/.designx)
51
+ --no-open Do not open browser automatically
52
+ --version, -v Print version
53
+ --help, -h Print this help
54
+ `)
55
+ process.exit(0)
56
+ }
57
+
58
+ // ── Data directory bootstrap ──────────────────────────────────────────────────
59
+
60
+ for (const dir of [
61
+ DATA_DIR,
62
+ join(DATA_DIR, 'projects'),
63
+ join(DATA_DIR, 'data'),
64
+ ]) {
65
+ mkdirSync(dir, { recursive: true })
66
+ }
67
+
68
+ // ── Start server ──────────────────────────────────────────────────────────────
69
+
70
+ const serverPath = join(__dirname, '../server.cjs')
71
+
72
+ if (!existsSync(serverPath)) {
73
+ console.error(
74
+ '\n [designx] server.cjs not found.\n' +
75
+ ' Run `npm run package` inside the app directory to build it first.\n'
76
+ )
77
+ process.exit(1)
78
+ }
79
+
80
+ const child = spawn('node', [serverPath], {
81
+ env: {
82
+ ...process.env,
83
+ PORT,
84
+ NODE_ENV: 'production',
85
+ DESIGNX_DATA_DIR: DATA_DIR,
86
+ DESIGNX_UPDATE_FLAG: join(DATA_DIR, 'update-available.json'),
87
+ },
88
+ stdio: 'inherit',
89
+ })
90
+
91
+ child.on('error', (err) => {
92
+ console.error('[designx] Failed to start server:', err.message)
93
+ process.exit(1)
94
+ })
95
+
96
+ child.on('exit', (code) => {
97
+ process.exit(code ?? 0)
98
+ })
99
+
100
+ process.on('SIGINT', () => { child.kill('SIGINT'); process.exit(0) })
101
+ process.on('SIGTERM', () => { child.kill('SIGTERM'); process.exit(0) })
102
+
103
+ // ── Open browser ──────────────────────────────────────────────────────────────
104
+
105
+ const url = `http://localhost:${PORT}`
106
+
107
+ console.log(`\n DesignX v${version}`)
108
+ console.log(` Running at: ${url}`)
109
+ console.log(` Data dir: ${DATA_DIR}`)
110
+ console.log(` Stop with: Ctrl+C\n`)
111
+
112
+ if (!NO_OPEN) {
113
+ setTimeout(() => {
114
+ const cmd =
115
+ process.platform === 'darwin' ? `open "${url}"` :
116
+ process.platform === 'win32' ? `start "" "${url}"` :
117
+ `xdg-open "${url}"`
118
+ exec(cmd, (err) => {
119
+ if (err) console.warn(` [designx] Could not open browser: ${err.message}`)
120
+ })
121
+ }, 1800)
122
+ }
123
+
124
+ // ── Background update check ───────────────────────────────────────────────────
125
+ // Checks npm registry for the latest version. Non-blocking — runs after server
126
+ // starts. Writes ~/.designx/update-available.json when a newer version exists,
127
+ // which the server reads and exposes via GET /api/update-available.
128
+
129
+ const UPDATE_FLAG = join(DATA_DIR, 'update-available.json')
130
+
131
+ // Clear stale flag from previous run
132
+ try { unlinkSync(UPDATE_FLAG) } catch { /* doesn't exist — fine */ }
133
+
134
+ setTimeout(() => {
135
+ exec('npm view @studioindia/designx version --json 2>/dev/null', { timeout: 10000 }, (err, stdout) => {
136
+ if (err || !stdout) return
137
+ try {
138
+ const latest = JSON.parse(stdout.trim()).replace(/^"|"$/g, '')
139
+ if (latest && latest !== version && isNewer(latest, version)) {
140
+ writeFileSync(UPDATE_FLAG, JSON.stringify({ latest, current: version }), 'utf8')
141
+ console.log(` [designx] Update available: v${version} → v${latest} (Relaunch to update)`)
142
+ }
143
+ } catch { /* malformed response */ }
144
+ })
145
+ }, 3000) // wait 3 s to not slow perceived startup
146
+
147
+ function isNewer(a, b) {
148
+ const pa = a.split('.').map(Number)
149
+ const pb = b.split('.').map(Number)
150
+ for (let i = 0; i < 3; i++) {
151
+ if ((pa[i] ?? 0) > (pb[i] ?? 0)) return true
152
+ if ((pa[i] ?? 0) < (pb[i] ?? 0)) return false
153
+ }
154
+ return false
155
+ }
@@ -0,0 +1,8 @@
1
+ import{r as s,p as R,a as O,j as o,n as N,b as U,s as Z,c as H}from"./index-CBUxOHiU.js";const k=new Map([["bold",s.createElement(s.Fragment,null,s.createElement("path",{d:"M71.68,97.22,34.74,128l36.94,30.78a12,12,0,1,1-15.36,18.44l-48-40a12,12,0,0,1,0-18.44l48-40A12,12,0,0,1,71.68,97.22Zm176,21.56-48-40a12,12,0,1,0-15.36,18.44L221.26,128l-36.94,30.78a12,12,0,1,0,15.36,18.44l48-40a12,12,0,0,0,0-18.44ZM164.1,28.72a12,12,0,0,0-15.38,7.18l-64,176a12,12,0,0,0,7.18,15.37A11.79,11.79,0,0,0,96,228a12,12,0,0,0,11.28-7.9l64-176A12,12,0,0,0,164.1,28.72Z"}))],["duotone",s.createElement(s.Fragment,null,s.createElement("path",{d:"M240,128l-48,40H64L16,128,64,88H192Z",opacity:"0.2"}),s.createElement("path",{d:"M69.12,94.15,28.5,128l40.62,33.85a8,8,0,1,1-10.24,12.29l-48-40a8,8,0,0,1,0-12.29l48-40a8,8,0,0,1,10.24,12.3Zm176,27.7-48-40a8,8,0,1,0-10.24,12.3L227.5,128l-40.62,33.85a8,8,0,1,0,10.24,12.29l48-40a8,8,0,0,0,0-12.29ZM162.73,32.48a8,8,0,0,0-10.25,4.79l-64,176a8,8,0,0,0,4.79,10.26A8.14,8.14,0,0,0,96,224a8,8,0,0,0,7.52-5.27l64-176A8,8,0,0,0,162.73,32.48Z"}))],["fill",s.createElement(s.Fragment,null,s.createElement("path",{d:"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM92.8,145.6a8,8,0,1,1-9.6,12.8l-32-24a8,8,0,0,1,0-12.8l32-24a8,8,0,0,1,9.6,12.8L69.33,128Zm58.89-71.4-32,112a8,8,0,1,1-15.38-4.4l32-112a8,8,0,0,1,15.38,4.4Zm53.11,60.2-32,24a8,8,0,0,1-9.6-12.8L186.67,128,163.2,110.4a8,8,0,1,1,9.6-12.8l32,24a8,8,0,0,1,0,12.8Z"}))],["light",s.createElement(s.Fragment,null,s.createElement("path",{d:"M67.84,92.61,25.37,128l42.47,35.39a6,6,0,1,1-7.68,9.22l-48-40a6,6,0,0,1,0-9.22l48-40a6,6,0,0,1,7.68,9.22Zm176,30.78-48-40a6,6,0,1,0-7.68,9.22L230.63,128l-42.47,35.39a6,6,0,1,0,7.68,9.22l48-40a6,6,0,0,0,0-9.22Zm-81.79-89A6,6,0,0,0,154.36,38l-64,176A6,6,0,0,0,94,221.64a6.15,6.15,0,0,0,2,.36,6,6,0,0,0,5.64-3.95l64-176A6,6,0,0,0,162.05,34.36Z"}))],["regular",s.createElement(s.Fragment,null,s.createElement("path",{d:"M69.12,94.15,28.5,128l40.62,33.85a8,8,0,1,1-10.24,12.29l-48-40a8,8,0,0,1,0-12.29l48-40a8,8,0,0,1,10.24,12.3Zm176,27.7-48-40a8,8,0,1,0-10.24,12.3L227.5,128l-40.62,33.85a8,8,0,1,0,10.24,12.29l48-40a8,8,0,0,0,0-12.29ZM162.73,32.48a8,8,0,0,0-10.25,4.79l-64,176a8,8,0,0,0,4.79,10.26A8.14,8.14,0,0,0,96,224a8,8,0,0,0,7.52-5.27l64-176A8,8,0,0,0,162.73,32.48Z"}))],["thin",s.createElement(s.Fragment,null,s.createElement("path",{d:"M66.56,91.07,22.25,128l44.31,36.93A4,4,0,0,1,64,172a3.94,3.94,0,0,1-2.56-.93l-48-40a4,4,0,0,1,0-6.14l48-40a4,4,0,0,1,5.12,6.14Zm176,33.86-48-40a4,4,0,1,0-5.12,6.14L233.75,128l-44.31,36.93a4,4,0,1,0,5.12,6.14l48-40a4,4,0,0,0,0-6.14ZM161.37,36.24a4,4,0,0,0-5.13,2.39l-64,176a4,4,0,0,0,2.39,5.13A4.12,4.12,0,0,0,96,220a4,4,0,0,0,3.76-2.63l64-176A4,4,0,0,0,161.37,36.24Z"}))]]),M=s.forwardRef((e,t)=>s.createElement(R,{ref:t,...e,weights:k}));M.displayName="CodeIcon";const z=M,q=500,K=20,V=300,B="https://stackblitz.com",A=["angular-cli","create-react-app","html","javascript","node","polymer","typescript","vue"],W=["project","search","ports","settings"],G=["light","dark"],J=["editor","preview"],I={clickToLoad:e=>m("ctl",e),devToolsHeight:e=>$("devtoolsheight",e),forceEmbedLayout:e=>m("embed",e),hideDevTools:e=>m("hidedevtools",e),hideExplorer:e=>m("hideExplorer",e),hideNavigation:e=>m("hideNavigation",e),openFile:e=>g("file",e),showSidebar:e=>X("showSidebar",e),sidebarView:e=>x("sidebarView",e,W),startScript:e=>g("startScript",e),terminalHeight:e=>$("terminalHeight",e),theme:e=>x("theme",e,G),view:e=>x("view",e,J),zenMode:e=>m("zenMode",e),organization:e=>`${g("orgName",e==null?void 0:e.name)}&${g("orgProvider",e==null?void 0:e.provider)}`,crossOriginIsolated:e=>m("corp",e)};function F(e={}){const t=Object.entries(e).map(([n,r])=>r!=null&&I.hasOwnProperty(n)?I[n](r):"").filter(Boolean);return t.length?`?${t.join("&")}`:""}function m(e,t){return t===!0?`${e}=1`:""}function X(e,t){return typeof t=="boolean"?`${e}=${t?"1":"0"}`:""}function $(e,t){if(typeof t=="number"&&!Number.isNaN(t)){const n=Math.min(100,Math.max(0,t));return`${e}=${encodeURIComponent(Math.round(n))}`}return""}function x(e,t="",n=[]){return n.includes(t)?`${e}=${encodeURIComponent(t)}`:""}function g(e,t){return(Array.isArray(t)?t:[t]).filter(r=>typeof r=="string"&&r.trim()!=="").map(r=>`${e}=${encodeURIComponent(r)}`).join("&")}function L(){return Math.random().toString(36).slice(2,6)+Math.random().toString(36).slice(2,6)}function w(e,t){return`${D(t)}${e}${F(t)}`}function E(e,t){const n={forceEmbedLayout:!0};return t&&typeof t=="object"&&Object.assign(n,t),`${D(n)}${e}${F(n)}`}function D(e={}){return(typeof e.origin=="string"?e.origin:B).replace(/\/$/,"")}function _(e,t,n){if(!t||!e||!e.parentNode)throw new Error("Invalid Element");e.id&&(t.id=e.id),e.className&&(t.className=e.className),Y(t,n),Q(e,t,n),e.replaceWith(t)}function j(e){if(typeof e=="string"){const t=document.getElementById(e);if(!t)throw new Error(`Could not find element with id '${e}'`);return t}else if(e instanceof HTMLElement)return e;throw new Error(`Invalid element: ${e}`)}function v(e){return e&&e.newWindow===!1?"_self":"_blank"}function Y(e,t={}){const n=Object.hasOwnProperty.call(t,"height")?`${t.height}`:`${V}`,r=Object.hasOwnProperty.call(t,"width")?`${t.width}`:void 0;e.setAttribute("height",n),r?e.setAttribute("width",r):e.setAttribute("style","width:100%;")}function Q(e,t,n={}){var i,a;const r=((a=(i=e.allow)==null?void 0:i.split(";"))==null?void 0:a.map(l=>l.trim()))??[];n.crossOriginIsolated&&!r.includes("cross-origin-isolated")&&r.push("cross-origin-isolated"),r.length>0&&(t.allow=r.join("; "))}class ee{constructor(t){this.pending={},this.port=t,this.port.onmessage=this.messageListener.bind(this)}request({type:t,payload:n}){return new Promise((r,i)=>{const a=L();this.pending[a]={resolve:r,reject:i},this.port.postMessage({type:t,payload:{...n,__reqid:a}})})}messageListener(t){var d;if(typeof((d=t.data.payload)==null?void 0:d.__reqid)!="string")return;const{type:n,payload:r}=t.data,{__reqid:i,__success:a,__error:l}=r;this.pending[i]&&(a?this.pending[i].resolve(this.cleanResult(r)):this.pending[i].reject(l?`${n}: ${l}`:n),delete this.pending[i])}cleanResult(t){const n={...t};return delete n.__reqid,delete n.__success,delete n.__error,Object.keys(n).length?n:null}}class te{constructor(t,n){this.editor={openFile:r=>this._rdc.request({type:"SDK_OPEN_FILE",payload:{path:r}}),setCurrentFile:r=>this._rdc.request({type:"SDK_SET_CURRENT_FILE",payload:{path:r}}),setTheme:r=>this._rdc.request({type:"SDK_SET_UI_THEME",payload:{theme:r}}),setView:r=>this._rdc.request({type:"SDK_SET_UI_VIEW",payload:{view:r}}),showSidebar:(r=!0)=>this._rdc.request({type:"SDK_TOGGLE_SIDEBAR",payload:{visible:r}})},this.preview={origin:"",getUrl:()=>this._rdc.request({type:"SDK_GET_PREVIEW_URL",payload:{}}).then(r=>(r==null?void 0:r.url)??null),setUrl:(r="/")=>{if(typeof r!="string"||!r.startsWith("/"))throw new Error(`Invalid argument: expected a path starting with '/', got '${r}'`);return this._rdc.request({type:"SDK_SET_PREVIEW_URL",payload:{path:r}})}},this._rdc=new ee(t),Object.defineProperty(this.preview,"origin",{value:typeof n.previewOrigin=="string"?n.previewOrigin:null,writable:!1})}applyFsDiff(t){const n=r=>r!==null&&typeof r=="object";if(!n(t)||!n(t.create))throw new Error("Invalid diff object: expected diff.create to be an object.");if(!Array.isArray(t.destroy))throw new Error("Invalid diff object: expected diff.destroy to be an array.");return this._rdc.request({type:"SDK_APPLY_FS_DIFF",payload:t})}getDependencies(){return this._rdc.request({type:"SDK_GET_DEPS_SNAPSHOT",payload:{}})}getFsSnapshot(){return this._rdc.request({type:"SDK_GET_FS_SNAPSHOT",payload:{}})}}const y=[];class ne{constructor(t){this.id=L(),this.element=t,this.pending=new Promise((n,r)=>{const i=({data:c,ports:u})=>{(c==null?void 0:c.action)==="SDK_INIT_SUCCESS"&&c.id===this.id&&(this.vm=new te(u[0],c.payload),n(this.vm),l())},a=()=>{var c;(c=this.element.contentWindow)==null||c.postMessage({action:"SDK_INIT",id:this.id},"*")};function l(){window.clearInterval(p),window.removeEventListener("message",i)}window.addEventListener("message",i),a();let d=0;const p=window.setInterval(()=>{if(this.vm){l();return}if(d>=K){l(),r("Timeout: Unable to establish a connection with the StackBlitz VM"),y.forEach((c,u)=>{c.id===this.id&&y.splice(u,1)});return}d++,a()},q)}),y.push(this)}}const re=e=>{const t=e instanceof Element?"element":"id";return y.find(n=>n[t]===e)??null};function ie(e,t){const n=document.createElement("input");return n.type="hidden",n.name=e,n.value=t,n}function se(e){return e.replace(/\[/g,"%5B").replace(/\]/g,"%5D")}function C({template:e,title:t,description:n,dependencies:r,files:i,settings:a}){if(!A.includes(e)){const c=A.map(u=>`'${u}'`).join(", ");console.warn(`Unsupported project.template: must be one of ${c}`)}const l=[],d=(c,u,h="")=>{l.push(ie(c,typeof u=="string"?u:h))};d("project[title]",t),typeof n=="string"&&n.length>0&&d("project[description]",n),d("project[template]",e,"javascript"),r&&(e==="node"?console.warn("Invalid project.dependencies: dependencies must be provided as a 'package.json' file when using the 'node' template."):d("project[dependencies]",JSON.stringify(r))),a&&d("project[settings]",JSON.stringify(a)),Object.entries(i).forEach(([c,u])=>{d(`project[files][${se(c)}]`,u)});const p=document.createElement("form");return p.method="POST",p.setAttribute("style","display:none!important;"),p.append(...l),p}function oe(e,t){const n=C(e);return n.action=E("/run",t),n.id="sb_run",`<!doctype html>
2
+ <html>
3
+ <head><title></title></head>
4
+ <body>
5
+ ${n.outerHTML}
6
+ <script>document.getElementById('${n.id}').submit();<\/script>
7
+ </body>
8
+ </html>`}function ae(e,t){const n=C(e);n.action=w("/run",t),n.target=v(t),document.body.appendChild(n),n.submit(),document.body.removeChild(n)}function b(e){return e!=null&&e.contentWindow?(re(e)??new ne(e)).pending:Promise.reject("Provided element is not an iframe.")}function ce(e,t){ae(e,t)}function le(e,t){const n=w(`/edit/${e}`,t),r=v(t);window.open(n,r)}function de(e,t){const n=w(`/github/${e}`,t),r=v(t);window.open(n,r)}function ue(e,t,n){var l;const r=j(e),i=oe(t,n),a=document.createElement("iframe");return _(r,a,n),(l=a.contentDocument)==null||l.write(i),b(a)}function pe(e,t,n){const r=j(e),i=document.createElement("iframe");return i.src=E(`/edit/${t}`,n),_(r,i,n),b(i)}function me(e,t,n){const r=j(e),i=document.createElement("iframe");return i.src=E(`/github/${t}`,n),_(r,i,n),b(i)}const T={connect:b,embedGithubProject:me,embedProject:ue,embedProjectId:pe,openGithubProject:de,openProject:ce,openProjectId:le};function fe({artifacts:e,projectName:t,onClose:n}){const r=s.useRef(null),[i,a]=s.useState("idle"),[l,d]=s.useState(null),p=s.useRef(null),u=e.filter(f=>f.phase===6).length,h=O(e,t);function S(){T.openProject({title:t,description:`Preview of ${t} — generated by Enigma v2 Deliver phase`,template:"node",files:h},{openFile:"src/App.tsx,src/main.tsx"})}async function P(){if(!(!r.current||i==="loading"||i==="ready")){a("loading"),d(null);try{p.current=await T.embedProject(r.current,{title:t,description:`Preview of ${t} — generated by Enigma v2 Deliver phase`,template:"node",files:h},{height:"100%",hideNavigation:!1,hideExplorer:!1,openFile:"src/App.tsx",terminalHeight:30,view:"preview"}),a("ready")}catch(f){d(f instanceof Error?f.message:"Failed to load preview"),a("error")}}}return s.useEffect(()=>(P(),()=>{p.current=null}),[]),o.jsxs("div",{className:"flex flex-col h-full bg-canvas border-l border-border",children:[o.jsxs("div",{className:"flex items-center gap-2 px-4 py-3 border-b border-border flex-shrink-0",children:[o.jsx(z,{size:15,className:"text-text-tertiary flex-shrink-0","aria-hidden":"true"}),o.jsxs("div",{className:"flex-1 min-w-0",children:[o.jsxs("p",{className:"text-small font-medium text-text-primary truncate",children:["App Preview — ",t]}),o.jsxs("p",{className:"text-caption text-text-tertiary",children:[u," artifact",u!==1?"s":""," · powered by StackBlitz"]})]}),o.jsxs("button",{type:"button",onClick:S,title:"Open in new tab",className:"flex items-center gap-1 rounded-card border border-border px-2 py-1 text-caption text-text-secondary hover:text-text-primary hover:shadow-hover-subtle transition-all",children:[o.jsx(N,{size:13,"aria-hidden":"true"}),"New tab"]}),o.jsx("button",{type:"button",onClick:n,title:"Close preview",className:"flex items-center justify-center w-6 h-6 rounded-card text-text-tertiary hover:text-text-primary hover:bg-border-secondary transition-colors",children:o.jsx(U,{size:14,"aria-hidden":"true"})})]}),o.jsxs("div",{className:"flex-1 relative overflow-hidden",children:[o.jsx("div",{ref:r,className:"absolute inset-0"}),i==="loading"&&o.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center bg-canvas z-10 gap-3",children:[o.jsx(Z,{size:24,className:"text-text-tertiary animate-spin","aria-hidden":"true"}),o.jsx("p",{className:"text-small text-text-secondary",children:"Starting preview…"}),o.jsx("p",{className:"text-caption text-text-tertiary",children:"Installing dependencies, this may take ~30s"})]}),i==="error"&&o.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center bg-canvas z-10 gap-4 px-6 text-center",children:[o.jsx("p",{className:"text-body-medium text-text-primary",children:"Preview unavailable"}),o.jsx("p",{className:"text-small text-text-secondary",children:l??"StackBlitz failed to load"}),o.jsxs("div",{className:"flex gap-2",children:[o.jsxs("button",{type:"button",onClick:()=>{a("idle"),P()},className:"flex items-center gap-1.5 rounded-card border border-border px-3 py-1.5 text-small text-text-secondary hover:text-text-primary",children:[o.jsx(H,{size:12,"aria-hidden":"true"}),"Retry"]}),o.jsxs("button",{type:"button",onClick:S,className:"flex items-center gap-1.5 rounded-card border border-border px-3 py-1.5 text-small text-text-secondary hover:text-text-primary",children:[o.jsx(N,{size:12,"aria-hidden":"true"}),"Open in new tab instead"]})]})]})]})]})}export{fe as default};