@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 +83 -0
- package/bin/designx.js +155 -0
- package/dist/assets/AppPreviewPanel-r56nQGPH.js +8 -0
- package/dist/assets/index-Bym9SmTQ.js +24 -0
- package/dist/assets/index-CAWjWFwD.js +231 -0
- package/dist/assets/index-CBUxOHiU.js +406 -0
- package/dist/assets/index-vZi_8zoX.css +10 -0
- package/dist/assets/xlsx-CkFp8p6R.js +105 -0
- package/dist/fonts/README.md +11 -0
- package/dist/fonts/Roobert-Medium.woff2 +0 -0
- package/dist/fonts/Roobert-Regular.woff2 +0 -0
- package/dist/fonts/Roobert-SemiBold.woff2 +0 -0
- package/dist/index.html +13 -0
- package/package.json +74 -0
- package/server.cjs +48114 -0
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};
|