velloo 0.3.0 → 0.4.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/canvas/assets/index-CSSyvzzo.css +2 -0
- package/canvas/assets/index-MXiDRs_Q.js +29 -0
- package/canvas/assets/lucide-all-BSrW0hTo.js +1 -0
- package/canvas/index.html +2 -2
- package/{chunk-bpeezw7w.js → chunk-09385g5r.js} +1 -1
- package/{chunk-52hxv18g.js → chunk-0r80246c.js} +1 -1
- package/{chunk-k9219kmj.js → chunk-1tdh4h14.js} +1 -1
- package/{chunk-as08hzp3.js → chunk-25j8vgpa.js} +1 -1
- package/{chunk-3tmsrvaj.js → chunk-33r6266k.js} +1 -1
- package/{chunk-d9p6s05n.js → chunk-38ev3cp3.js} +1 -1
- package/chunk-3gbmq4jz.js +2 -0
- package/{chunk-9nhw49vv.js → chunk-3kqtny5t.js} +1 -1
- package/{chunk-td5z2sm4.js → chunk-46xykc71.js} +1 -1
- package/chunk-4rvmpsda.js +3 -0
- package/{chunk-7bvvbsqy.js → chunk-5qd2ykvw.js} +1 -1
- package/{chunk-49cgk5s2.js → chunk-6qfyem23.js} +1 -1
- package/{chunk-y5r2w2z3.js → chunk-6z0kpe65.js} +1 -1
- package/{chunk-nycs0z7h.js → chunk-7cktk5qr.js} +1 -1
- package/chunk-8g5ghy46.js +5 -0
- package/chunk-8krqgzny.js +2 -0
- package/{chunk-jx11jvq8.js → chunk-8sfx5x8k.js} +1 -1
- package/chunk-8yadb6xz.js +5 -0
- package/{chunk-k6n249vb.js → chunk-8yz604tw.js} +1 -1
- package/{chunk-dsks52m7.js → chunk-c5zjen06.js} +1 -1
- package/{chunk-bvmtq0vn.js → chunk-cdxy4xzs.js} +1 -1
- package/chunk-cgzhcvsj.js +2 -0
- package/{chunk-k7sehkm8.js → chunk-e56eb38j.js} +1 -1
- package/{chunk-5ezzc1h2.js → chunk-egccxmed.js} +5 -5
- package/chunk-gsne0ap4.js +2 -0
- package/chunk-hzjzmkp6.js +6 -0
- package/{chunk-wa6qamwh.js → chunk-jtsn41cs.js} +1 -1
- package/{chunk-4nqr089t.js → chunk-k10n8na7.js} +1 -1
- package/{chunk-1jzhw3p4.js → chunk-n5acf0s3.js} +1 -1
- package/chunk-nc5k1ez4.js +2 -0
- package/chunk-njhdn9j0.js +4 -0
- package/{chunk-kz4423th.js → chunk-q4d60wr3.js} +1 -1
- package/{chunk-9g4tr0r6.js → chunk-rsy246re.js} +1 -1
- package/{chunk-wzngjjeb.js → chunk-syg6k8m0.js} +1 -1
- package/{chunk-wcqqx6tz.js → chunk-t4zss1tk.js} +1 -1
- package/{chunk-n9kcxaj7.js → chunk-t8xk4thc.js} +1 -1
- package/{chunk-7k7dpj5y.js → chunk-tkvb3wb8.js} +1 -1
- package/{chunk-cxzkkn12.js → chunk-tqwe5tc3.js} +1 -1
- package/{chunk-7njj1qmq.js → chunk-v0syg2hx.js} +1 -1
- package/chunk-va9v9y5m.js +2 -0
- package/{chunk-3g3dqej3.js → chunk-vbe2r4kv.js} +1 -1
- package/{chunk-qgwhd4n6.js → chunk-vh4144az.js} +1 -1
- package/{chunk-m3czg1tt.js → chunk-y0rwf1ze.js} +1 -1
- package/{chunk-maz6z7tz.js → chunk-yjmawrv5.js} +1 -1
- package/{chunk-yeh9gb5w.js → chunk-ys3s5gaa.js} +1 -1
- package/{chunk-qdnb2t6a.js → chunk-z1r0sc3k.js} +1 -1
- package/{chunk-1n5kmsxb.js → chunk-zkn1m8tw.js} +1 -1
- package/cli.js +1 -1
- package/package.json +1 -1
- package/canvas/assets/index-CqXW6xA_.js +0 -29
- package/canvas/assets/index-gB0StCrx.css +0 -2
- package/canvas/assets/lucide-all-C_CNpsMs.js +0 -1
- package/chunk-5wv5a52y.js +0 -2
- package/chunk-6nzqhfqq.js +0 -5
- package/chunk-6zz9kfbd.js +0 -3
- package/chunk-788t8y5x.js +0 -2
- package/chunk-9f22qnge.js +0 -2
- package/chunk-hfzg98c8.js +0 -4
- package/chunk-n9t36h0q.js +0 -2
- package/chunk-nr3ej3qf.js +0 -5
- package/chunk-t5bb30zy.js +0 -3
- package/chunk-tbmkq7wy.js +0 -6
- package/chunk-tc4mzzfw.js +0 -2
- package/chunk-xyfed1j0.js +0 -2
package/canvas/index.html
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
<link rel="manifest" href="/site.webmanifest" />
|
|
10
10
|
<meta name="theme-color" content="#0d0c0a" />
|
|
11
11
|
<title>velloo</title>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-MXiDRs_Q.js"></script>
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
|
|
14
14
|
<link rel="modulepreload" crossorigin href="/assets/react-D0Ec-185.js">
|
|
15
15
|
<link rel="modulepreload" crossorigin href="/assets/vendor-CKmQ-Zst.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/radix-gmpYzWRH.js">
|
|
17
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
17
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CSSyvzzo.css">
|
|
18
18
|
</head>
|
|
19
19
|
<body>
|
|
20
20
|
<div id="root"></div>
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{recordedDesignName}from"./chunk-
|
|
2
|
+
import{recordedDesignName}from"./chunk-egccxmed.js";import{displayPath}from"./chunk-v0syg2hx.js";import{basename,relative,resolve}from"path";function designWithFolder(folder,name,opts={}){let shown=name??recordedDesignName(folder)??basename(folder),rel=relative(resolve("."),folder),where=rel!==""&&!rel.startsWith("..")&&!opts.absolute?`./${rel}`:displayPath(folder);return`"${shown}" (${where})`}
|
|
3
3
|
export{designWithFolder};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{isCancel,confirm,select}from"./chunk-80fjfyha.js";import{capturesDir,sessionStatePath,captureDir,listCaptures,deleteCapture,isSafeCaptureId,HeadedBrowserMissingError,startCaptureSession}from"./chunk-c4fym47a.js";import"./chunk-nte0t7v3.js";import{loadDesignFolder}from"./chunk-
|
|
2
|
+
import{isCancel,confirm,select}from"./chunk-80fjfyha.js";import{capturesDir,sessionStatePath,captureDir,listCaptures,deleteCapture,isSafeCaptureId,HeadedBrowserMissingError,startCaptureSession}from"./chunk-c4fym47a.js";import"./chunk-nte0t7v3.js";import{loadDesignFolder}from"./chunk-egccxmed.js";import"./chunk-gsne0ap4.js";import{fail}from"./chunk-s23gtejf.js";import{installChromiumInteractive}from"./chunk-2fgfex9b.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign}from"./chunk-46xykc71.js";import{openUrl}from"./chunk-hggap8fq.js";import{withSubcommands}from"./chunk-94vx1yb0.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{join}from"path";var import_picocolors=__toESM(require_picocolors(),1);var CTRL_C=3,LOOPBACK=/^(localhost|127\.0\.0\.1|\[::1\]|::1)(:\d+)?$/i;function normalizeCaptureUrl(input){let raw=input.trim();if(raw==="")return null;let hasScheme=/^[a-z][a-z0-9+.-]*:\/\//i.test(raw),host=raw.split(/[/?#]/,1)[0]??"",candidate=hasScheme?raw:`${LOOPBACK.test(host)?"http":"https"}://${raw}`;try{let url=new URL(candidate);if(url.protocol!=="http:"&&url.protocol!=="https:")return null;if(url.hostname==="")return null;return url.toString()}catch{return null}}function bindKeys(handlers){let stdin=process.stdin;if(!stdin.isTTY)return()=>{};stdin.setRawMode(!0),stdin.resume(),stdin.setEncoding("utf8");let onData=(key)=>{if(key==="c")handlers.page();else if(key==="t")handlers.theme();else if(key==="l")handlers.list();else if(key==="q"||key.charCodeAt(0)===CTRL_C)handlers.quit()};return stdin.on("data",onData),()=>{if(stdin.off("data",onData),stdin.isTTY)stdin.setRawMode(!1);stdin.pause()}}function when(iso){let d=new Date(iso);if(Number.isNaN(d.getTime()))return iso;return d.toLocaleString(void 0,{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function describe(m){let what=m.themeOnly?"theme only":`${m.nodeCount} nodes, ${m.assetCount} assets`;return`${m.title||m.finalUrl||m.url} ${import_picocolors.default.dim(`\u2014 ${what}, ${when(m.capturedAt)}`)}`}function printCaptures(captures,dir){if(console.log(""),captures.length===0){console.log(import_picocolors.default.dim(" No captures yet. Run `velloo capture <url>` to make one.")),console.log("");return}console.log(` ${import_picocolors.default.bold(`${captures.length} capture${captures.length===1?"":"s"}`)}`),console.log(import_picocolors.default.dim(` ${dir}`)),console.log("");for(let m of captures)console.log(` ${import_picocolors.default.cyan(m.id)} ${describe(m)}`),console.log(import_picocolors.default.dim(` ${m.finalUrl||m.url}`));console.log("")}async function browseCaptures(root,folderId){let dir=capturesDir(root,folderId),captures=listCaptures(root,folderId);if(!process.stdin.isTTY||captures.length===0){printCaptures(captures,dir);return}for(;;){if(captures=listCaptures(root,folderId),captures.length===0){console.log(import_picocolors.default.dim(`
|
|
3
3
|
No captures left.
|
|
4
4
|
`));return}let picked=await select({message:`${captures.length} capture${captures.length===1?"":"s"} for this folder`,options:[...captures.map((m)=>({value:m.id,label:describe(m),hint:m.finalUrl||m.url})),{value:"__done",label:import_picocolors.default.dim("Done")}]});if(isCancel(picked)||picked==="__done")return;let manifest=captures.find((m)=>m.id===picked);if(!manifest)continue;let action=await select({message:manifest.title||manifest.id,options:[...manifest.files.includes("page.png")?[{value:"open",label:"Open the screenshot"}]:[],{value:"details",label:"Show what it contains"},{value:"delete",label:import_picocolors.default.red("Delete this capture")},{value:"back",label:import_picocolors.default.dim("Back")}]});if(isCancel(action)||action==="back")continue;if(action==="open"){await openUrl(`file://${join(captureDir(root,manifest.id,folderId),"page.png")}`);continue}if(action==="details"){console.log(""),console.log(` ${import_picocolors.default.bold(manifest.id)}`),console.log(` url ${manifest.finalUrl||manifest.url}`),console.log(` captured ${when(manifest.capturedAt)}`),console.log(` viewport ${manifest.viewport.w}\xD7${manifest.viewport.h}`),console.log(` nodes ${manifest.nodeCount}`),console.log(` assets ${manifest.assetCount}`),console.log(` files ${manifest.files.join(", ")}`),console.log(import_picocolors.default.dim(` ${captureDir(root,manifest.id,folderId)}`)),console.log(""),console.log(import_picocolors.default.dim(` verify a screen against it: compare_to_url { captureId: "${manifest.id}" }`)),console.log("");continue}if(action==="delete"){let sure=await confirm({message:`Delete ${manifest.title||manifest.id}? This can't be undone.`,initialValue:!1});if(isCancel(sure)||!sure)continue;deleteCapture(root,manifest.id,folderId),console.log(import_picocolors.default.dim(` deleted ${manifest.id}`))}}}var DESIGN_FLAG={type:"string",description:DESIGN_ARG_DESCRIPTION};async function captureDesign(arg,command){let design=await loadDesignFolder(await resolveDesign(arg,command,{designFlag:"--design"}));return{root:design.root,folderId:design.config.folderId}}var list=defineCommand({meta:{name:"list",description:"Browse stored captures (interactive on a TTY)"},args:{design:DESIGN_FLAG},async run({args}){let{root,folderId}=await captureDesign(args.design,"capture list");await browseCaptures(root,folderId)}}),remove=defineCommand({meta:{name:"delete",description:"Delete a stored capture, or every capture for the design"},args:{id:{type:"positional",required:!0,description:"The capture id, or `all`"},design:DESIGN_FLAG},async run({args}){let{root,folderId}=await captureDesign(args.design,"capture delete"),id=args.id.trim();if(id==="all"){let all=listCaptures(root,folderId);if(all.length===0){console.log(import_picocolors.default.dim("velloo capture: no captures to delete."));return}if(process.stdin.isTTY){let sure=await confirm({message:`Delete all ${all.length} captures for this design? This can't be undone.`,initialValue:!1});if(isCancel(sure)||!sure){console.log(import_picocolors.default.dim(" nothing deleted."));return}}for(let m of all)deleteCapture(root,m.id,folderId);console.log(`velloo capture: deleted ${all.length} captures.`);return}if(!isSafeCaptureId(id))fail("capture delete",`not a capture id: ${args.id}`);if(!deleteCapture(root,id,folderId))fail("capture delete",`no capture "${id}" for this design \u2014 run \`velloo capture list\`.`);console.log(`velloo capture: deleted ${id}.`)}}),capture=defineCommand({meta:{name:"capture",description:"open a browser, log in, and capture pages as evidence for the agent"},args:{url:{type:"positional",required:!1,description:"Page to open the browser on, e.g. http://localhost:3000/dashboard"},design:DESIGN_FLAG},async run({args}){let{root,folderId}=await captureDesign(args.design,"capture"),url;if(args.url!==void 0){let normalized=normalizeCaptureUrl(args.url);if(normalized===null)fail("capture",`not a page that can be captured: ${args.url} (expected an http(s) URL)`);url=normalized}let statePath=url?sessionStatePath(root,new URL(url).origin,folderId):void 0;console.log(""),console.log(` ${import_picocolors.default.bold("velloo capture")}`),console.log(import_picocolors.default.dim(` A browser window opens. Log in and navigate wherever you need \u2014 then use the
|
|
5
5
|
`+" velloo toolbar in the page (or the keys below) to capture what the agent should see.")),console.log(""),console.log(import_picocolors.default.dim(` ${import_picocolors.default.cyan("c")} capture page ${import_picocolors.default.cyan("t")} capture theme ${import_picocolors.default.cyan("l")} list ${import_picocolors.default.cyan("q")} done`)),console.log("");let made=[],onCapture=(m)=>{made.push(m);let what=m.themeOnly?"theme":`${m.nodeCount} nodes, ${m.assetCount} assets`;console.log(` ${import_picocolors.default.green("\u2713")} ${m.title||m.url} ${import_picocolors.default.dim(`(${what}) \u2192 ${m.id}`)}`)},start=async()=>startCaptureSession({folderRoot:root,...folderId?{folderId}:{},...url?{url}:{},...statePath?{sessionStatePath:statePath}:{},onCapture,onStatus:(message)=>console.log(import_picocolors.default.dim(` ${message}`))}),session;try{session=await start()}catch(err){if(!(err instanceof HeadedBrowserMissingError))throw err;if(!process.stdin.isTTY)fail("capture",err.message);console.log(""),console.log(` ${import_picocolors.default.yellow("\u26A0")} ${err.message}`);let proceed=await confirm({message:"Download the full browser now? (~300MB, one-time)",initialValue:!0});if(isCancel(proceed)||!proceed)fail("capture","no browser available for a capture session.");if(!await installChromiumInteractive())fail("capture","the browser isn't usable yet \u2014 see the output above.");session=await start()}let report=(e)=>{console.log(import_picocolors.default.red(` capture failed: ${e instanceof Error?e.message:String(e)}`))},unbind=bindKeys({page:()=>{session.capturePage().catch(report)},theme:()=>{session.captureTheme().catch(report)},list:()=>{if(console.log(import_picocolors.default.dim(` on: ${session.currentUrl()||"(no page)"}`)),made.length===0)console.log(import_picocolors.default.dim(" nothing captured yet"));for(let m of made)console.log(import_picocolors.default.dim(` ${m.id} ${m.title||m.url}`))},quit:()=>{session.close()}});if(await session.finished,unbind(),console.log(""),made.length===0)console.log(` ${import_picocolors.default.dim("No captures made.")}`);else console.log(` ${import_picocolors.default.green("\u2713")} ${made.length} capture${made.length===1?"":"s"} saved`),console.log(import_picocolors.default.dim(` ${capturesDir(root,folderId)}`)),console.log(""),console.log(import_picocolors.default.dim(" The agent can read these with list_captures / get_capture,")),console.log(import_picocolors.default.dim(" and verify a screen against one with compare_to_url { captureId }."));if(statePath)console.log(""),console.log(import_picocolors.default.dim(" Session cookies for this app were saved to:")),console.log(import_picocolors.default.dim(` ${statePath}`)),console.log(import_picocolors.default.dim(` Scoped to that site only (third-party sign-in cookies are dropped), outside your
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{string,number,object,CURRENT_SCHEMA_VERSION,schemaVersionOf}from"./chunk-nte0t7v3.js";import{writeJsonAtomic}from"./chunk-
|
|
2
|
+
import{string,number,object,CURRENT_SCHEMA_VERSION,schemaVersionOf}from"./chunk-nte0t7v3.js";import{writeJsonAtomic}from"./chunk-egccxmed.js";import{TOOL_VERSION}from"./chunk-q4d60wr3.js";import{defaultCloudUrl}from"./chunk-25j8vgpa.js";function isLoopbackHost(host){let normalized=host.trim().toLowerCase().replace(/^\[|\]$/g,"").replace(/\.$/,"");if(normalized==="localhost"||normalized==="::1")return!0;let octets=normalized.split(".");return octets.length===4&&octets[0]==="127"&&octets.every((part)=>/^\d+$/.test(part))}function assertLoopbackHost(host){if(isLoopbackHost(host))return;throw Error(`refusing to bind ${JSON.stringify(host)}: the canvas and MCP endpoints have no authentication and only serve loopback (use 127.0.0.1, localhost or ::1)`)}import{closeSync,existsSync,mkdirSync,openSync,readFileSync,realpathSync,statSync,unlinkSync,writeSync}from"fs";import{mkdir,readFile}from"fs/promises";import{homedir}from"os";import{dirname,join,resolve}from"path";var DaemonRecordSchema=object({root:string().min(1),pid:number().int().positive(),canvasUrl:string().min(1),canvasPort:number().int().positive(),mcpUrl:string().min(1),mcpPort:number().int().positive(),cloudUrl:string().min(1),version:string().min(1),startedAt:string().min(1)});function daemonMatchesRuntime(rec,cloudUrl=defaultCloudUrl()){return rec.version===TOOL_VERSION&&rec.cloudUrl===cloudUrl}var MUTEX_STALE_MS=15000,HEALTHY_TIMEOUT_MS=15000,cacheDir=(root)=>join(root,".design","cache"),lockPath=(root)=>join(cacheDir(root),"runtime.json"),mutexPath=(root)=>join(cacheDir(root),"runtime.lock"),logPath=(root)=>join(cacheDir(root),"daemon.log"),portMemoPath=(root)=>join(cacheDir(root),"port.json"),registryPath=()=>process.env.VELLOO_DAEMONS_PATH??join(homedir(),".velloo","daemons.json");function daemonRoot(folder){let abs=resolve(folder);return existsSync(abs)?realpathSync(abs):abs}class DesignFolderFormatError extends Error{root;found;current;constructor(message,root,found,current){super(message);this.root=root;this.found=found;this.current=current;this.name="DesignFolderFormatError"}}function assertFolderFormatCurrent(root){let raw;try{raw=JSON.parse(readFileSync(join(root,".design","config.json"),"utf8"))}catch{return}let found=schemaVersionOf(raw);if(found===CURRENT_SCHEMA_VERSION)return;let message=found>CURRENT_SCHEMA_VERSION?`velloo: ${root} uses design-folder schema version ${found}, but this velloo only knows version ${CURRENT_SCHEMA_VERSION}. Upgrade velloo to open it.`:`velloo: ${root} uses design-folder schema version ${found} (current: ${CURRENT_SCHEMA_VERSION}). Run \`velloo upgrade ${root}\` to migrate it.`;throw new DesignFolderFormatError(message,root,found,CURRENT_SCHEMA_VERSION)}async function readLock(root){try{let parsed=DaemonRecordSchema.safeParse(JSON.parse(await readFile(lockPath(root),"utf8")));return parsed.success?parsed.data:null}catch{return null}}async function writeLock(rec){await writeJsonAtomic(lockPath(rec.root),rec)}function removeLock(root){try{unlinkSync(lockPath(root))}catch{}}var DEFAULT_CANVAS_PORT=7300,DERIVED_BAND={start:7310,size:90},PortMemoSchema=object({canvasPort:number().int().min(1).max(65535)});async function readRememberedPort(root){try{let parsed=PortMemoSchema.safeParse(JSON.parse(await readFile(portMemoPath(root),"utf8")));return parsed.success?parsed.data.canvasPort:null}catch{return null}}async function rememberPort(root,canvasPort){await writeJsonAtomic(portMemoPath(root),{canvasPort})}function derivedPort(root){let digest=Bun.CryptoHasher.hash("sha256",root,"hex");return DERIVED_BAND.start+Number.parseInt(digest.slice(0,8),16)%DERIVED_BAND.size}function portCandidates(opts){if(opts.explicit!==void 0)return[opts.explicit];let wanted=[opts.remembered,DEFAULT_CANVAS_PORT,derivedPort(opts.root)];return[...new Set(wanted.filter((p)=>typeof p==="number"))]}var HealthSchema=object({app:string().optional(),root:string().optional()});async function isLive(rec){try{let res=await fetch(`http://127.0.0.1:${rec.canvasPort}/api/health`,{signal:AbortSignal.timeout(1000)});if(!res.ok)return!1;let body=await res.json(),health=HealthSchema.safeParse(body);return health.success&&health.data.app==="velloo"&&health.data.root===rec.root}catch{return!1}}async function registryRead(){try{let raw=JSON.parse(await readFile(registryPath(),"utf8"));if(!Array.isArray(raw))return[];return raw.flatMap((entry)=>{let parsed=DaemonRecordSchema.safeParse(entry);return parsed.success?[parsed.data]:[]})}catch{return[]}}async function registryWrite(records){let p=registryPath();await mkdir(dirname(p),{recursive:!0}),await writeJsonAtomic(p,records)}async function registryUpsert(rec){let others=(await registryRead()).filter((r)=>r.root!==rec.root);await registryWrite([...others,rec])}async function registryRemove(root){await registryWrite((await registryRead()).filter((r)=>r.root!==root))}async function listDaemons(){let all=await registryRead(),live=[];for(let rec of all)if(await isLive(rec))live.push(rec);if(live.length!==all.length)await registryWrite(live);return live}function daemonSpawnCmd(root,preferredPort,host){let args=["__daemon",root,"--host",host];if(preferredPort!==void 0)args.push("--port",String(preferredPort));let entry=Bun.main;return existsSync(entry)?[process.execPath,entry,...args]:[process.execPath,...args]}function spawnDetached(root,preferredPort,host){mkdirSync(cacheDir(root),{recursive:!0});let logStart=0;try{logStart=statSync(logPath(root)).size}catch{}let log=openSync(logPath(root),"a"),proc=Bun.spawn(daemonSpawnCmd(root,preferredPort,host),{stdin:"ignore",stdout:log,stderr:log,detached:process.platform==="win32"});return proc.unref(),{exited:proc.exited,logStart}}function claimMutex(root){let path=mutexPath(root);try{let fd=openSync(path,"wx");return writeSync(fd,String(process.pid)),closeSync(fd),!0}catch{try{if(Date.now()-statSync(path).mtimeMs>MUTEX_STALE_MS)return unlinkSync(path),claimMutex(root)}catch{}return!1}}function releaseMutex(root){try{unlinkSync(mutexPath(root))}catch{}}function startupFailure(root,logStart){let appended="";try{appended=readFileSync(logPath(root),"utf8").slice(logStart)}catch{}let lines=appended.split(`
|
|
3
3
|
`).map((l)=>l.replace(/^velloo __daemon: /,"").trimEnd()).filter((l)=>l.trim().length>0).slice(-8);if(lines.length===0)return`velloo: the canvas daemon for ${root} exited during startup with no output \u2014 see ${logPath(root)}`;return`velloo: the canvas daemon for ${root} exited during startup:
|
|
4
4
|
${lines.join(`
|
|
5
5
|
`)}`}async function waitForHealthy(root,spawned){let deadline=Date.now()+HEALTHY_TIMEOUT_MS,died=!1;if(spawned)spawned.exited.then(()=>died=!0);while(Date.now()<deadline){let rec=await readLock(root);if(rec&&await isLive(rec))return rec;if(died)throw Error(startupFailure(root,spawned?.logStart??0));await new Promise((r)=>setTimeout(r,150))}throw Error(`velloo: canvas daemon for ${root} didn't come up \u2014 see ${logPath(root)}`)}async function ensureDaemon(folder,opts={}){let root=daemonRoot(folder);assertFolderFormatCurrent(root);let host=opts.host??"127.0.0.1";assertLoopbackHost(host),mkdirSync(cacheDir(root),{recursive:!0});let deadline=Date.now()+HEALTHY_TIMEOUT_MS+5000;while(Date.now()<deadline){let existing=await readLock(root);if(existing&&await isLive(existing)){if(daemonMatchesRuntime(existing))return existing;await stopDaemon(root)}if(claimMutex(root))try{let again=await readLock(root);if(again&&await isLive(again)){if(daemonMatchesRuntime(again))return again;await stopDaemon(root)}let spawned=spawnDetached(root,opts.preferredPort,host);return opts.onSpawn?.(),await waitForHealthy(root,spawned)}finally{releaseMutex(root)}await new Promise((r)=>setTimeout(r,200))}throw Error(`velloo: timed out ensuring a canvas daemon for ${root}`)}async function stopDaemon(root){let rec=await readLock(root);if(!rec)return!1;try{process.kill(rec.pid,"SIGTERM")}catch{}for(let i=0;i<30;i++){if(!await isLive(rec))break;await new Promise((r)=>setTimeout(r,100))}return removeLock(root),await registryRemove(root),!0}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{AuthConfigResponseSchema,HealthResponseSchema,cloudJson}from"./chunk-
|
|
2
|
+
import{AuthConfigResponseSchema,HealthResponseSchema,cloudJson}from"./chunk-gsne0ap4.js";var BUILT_DEFAULT="https://api.velloo.ai";function defaultCloudUrl(){return(process.env.VELLOO_CLOUD_URL??BUILT_DEFAULT).replace(/\/+$/,"")}var CONFIG_TIMEOUT_MS=5000;function cloudOrigin(baseUrl){return new URL(baseUrl).origin}async function fetchCloudAppUrl(baseUrl,timeoutMs=CONFIG_TIMEOUT_MS){let fallback=cloudOrigin(baseUrl),res;try{res=await fetch(`${fallback}/v1/auth/config`,{signal:AbortSignal.timeout(timeoutMs)})}catch{return fallback}if(!res.ok)return fallback;let config=await cloudJson(res,AuthConfigResponseSchema,"reading the cloud's config");if(!config.ok||config.value.appUrl===void 0)return fallback;try{let advertised=new URL(config.value.appUrl).origin;return isSecureCloudUrl(advertised)?advertised:fallback}catch{return fallback}}async function publishedBoardsUrl(baseUrl){return`${await fetchCloudAppUrl(baseUrl)}/boards`}async function billingPageUrl(baseUrl){return`${await fetchCloudAppUrl(baseUrl)}/billing`}var LOOPBACK=new Set(["localhost","127.0.0.1","::1","[::1]"]);function isSecureCloudUrl(cloudUrl){try{let u=new URL(cloudUrl);if(u.protocol==="https:")return!0;return u.protocol==="http:"&&LOOPBACK.has(u.hostname)}catch{return!1}}function assertSecureCloudUrl(cloudUrl){if(!isSecureCloudUrl(cloudUrl))throw Error(`refusing to send credentials to a non-HTTPS cloud URL: ${cloudUrl} (use https:// or a loopback host)`)}async function checkCloudHealth(baseUrl){let res;try{res=await fetch(`${baseUrl}/health`,{signal:AbortSignal.timeout(8000)})}catch(error){return{status:"unreachable",reason:error instanceof Error?error.message:String(error)}}let health=await cloudJson(res,HealthResponseSchema,"reading the cloud's health");if(!health.ok||health.value.ok!==!1)return{status:"ok"};let down=[health.value.db?.ok===!1?"database":null,health.value.blob?.ok===!1?"storage backend":null].filter((part)=>part!==null);return{status:"unhealthy",detail:`the cloud's ${down.length>0?down.join(" and "):"a dependency"} is unavailable \u2014 publishes and share links are down; try again later.`}}
|
|
3
3
|
export{defaultCloudUrl,fetchCloudAppUrl,publishedBoardsUrl,billingPageUrl,isSecureCloudUrl,assertSecureCloudUrl,checkCloudHealth};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import"./chunk-nte0t7v3.js";import{recordedDesignName}from"./chunk-
|
|
2
|
+
import"./chunk-nte0t7v3.js";import{recordedDesignName}from"./chunk-egccxmed.js";import"./chunk-gsne0ap4.js";import"./chunk-c4fym47a.js";import"./chunk-q4d60wr3.js";import{assertLoopbackHost,daemonRoot,isLive,ensureDaemon,stopDaemon}from"./chunk-1tdh4h14.js";import"./chunk-80fjfyha.js";import{fail}from"./chunk-s23gtejf.js";import"./chunk-2fgfex9b.js";import"./chunk-z1r0sc3k.js";import"./chunk-cvn689d5.js";import"./chunk-v0syg2hx.js";import{createProgress}from"./chunk-pjtg3wnm.js";import{designWithFolder}from"./chunk-09385g5r.js";import{findDesigns,DESIGN_ARG_DESCRIPTION,resolveDesign}from"./chunk-46xykc71.js";import{openUrl}from"./chunk-hggap8fq.js";import"./chunk-vh4144az.js";import"./chunk-25j8vgpa.js";import{traceEnabled}from"./chunk-6tx68d90.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{basename,join}from"path";var import_picocolors=__toESM(require_picocolors(),1);function actionForRunKey(key,targetCount=1){if(key==="b")return{kind:"background"};if(key==="q"||key==="\x03")return{kind:"stop"};if(targetCount<=1)return key==="o"?{kind:"open"}:null;if(key==="a"||key==="o")return{kind:"open"};if(/^[1-9]$/.test(key)){let index=Number(key);return index<=targetCount?{kind:"open",index}:null}return null}function shouldStayForeground(opts){return!opts.background&&opts.stdinIsTTY}function bindRunKeys(onKey){let stdin=process.stdin;if(!stdin.isTTY)return()=>{};stdin.setRawMode(!0),stdin.resume(),stdin.setEncoding("utf8");let onData=(chunk)=>{onKey(chunk)};return stdin.on("data",onData),()=>{if(stdin.off("data",onData),stdin.isTTY)stdin.setRawMode(!1);stdin.pause()}}var LIVE_POLL_MS=2000;async function waitInForeground(opts){return await new Promise((resolve)=>{let settled=!1,unbind=()=>{},timer,finish=(outcome)=>{if(settled)return;if(settled=!0,unbind(),timer!==void 0)clearInterval(timer);process.off("SIGINT",onStop),process.off("SIGTERM",onStop),resolve(outcome)},onStop=()=>finish("stop");unbind=bindRunKeys((key)=>{let action=actionForRunKey(key,opts.targetCount??1);if(!action)return;if(action.kind==="open")opts.onOpen(action.index);else finish(action.kind)}),process.on("SIGINT",onStop),process.on("SIGTERM",onStop),timer=setInterval(()=>{opts.isLive().then((live)=>{if(!live)finish("died")})},LIVE_POLL_MS)})}import{resolve}from"path";async function resolveRunTargets(arg,opts={}){let cwd=resolve(opts.cwd??"."),single=async()=>{let folder=await resolveDesign(arg,"run",{interactive:!0,requireConfig:!0,cwd});return[{name:recordedDesignName(folder)??void 0,folder}]};if(arg)return single();let found;try{found=await findDesigns(cwd)}catch{return single()}if(!found||found.designs.length<2)return single();let targets=[];for(let design of found.designs)if(design.exists&&!design.outsideRepo)targets.push({name:design.name,folder:design.root});if(targets.length===0)return single();if(targets.length===1)return targets;let defaultDesign=found.defaultDesign;if(defaultDesign)targets.sort((a,b)=>a.name===defaultDesign?-1:b.name===defaultDesign?1:0);return targets}function label(target){return recordedDesignName(target.folder)??target.name??basename(target.folder)}function printKeys(attached){if(console.log(""),attached.length>1)attached.forEach(({target},i)=>{console.log(` ${import_picocolors.default.cyan(String(i+1))} open ${label(target)}`)}),console.log(` ${import_picocolors.default.cyan("a")} open all`);else console.log(` ${import_picocolors.default.cyan("o")} open the browser`);console.log(` ${import_picocolors.default.cyan("b")} run in the background`),console.log(` ${import_picocolors.default.cyan("q")} stop`),console.log("")}function printBackgroundStay(designArg){console.log(`velloo: it keeps running in the background \u2014 awake while the canvas is open or an agent is connected. Stop it anytime with \`velloo stop${designArg}\`.`)}var run_default=defineCommand({meta:{name:"run",description:"Open the canvas for a design folder and stay in the foreground (starts a persistent canvas if none is running)"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},port:{type:"string",description:"Preferred canvas port when starting fresh (default: the port this folder ran on last, else 7300)"},host:{type:"string",description:"Loopback bind hostname: 127.0.0.1 (default), localhost or ::1"},open:{type:"boolean",default:!1,description:"Open the canvas in your browser"},background:{type:"boolean",default:!1,description:"Leave the canvas running and return to the shell (the default when stdin is not a TTY)"}},async run({args}){assertLoopbackHost(args.host??"127.0.0.1");let targets=await resolveRunTargets(args.design,{}),preferredPort=args.port?Number(args.port):void 0;if(preferredPort!==void 0&&(!Number.isFinite(preferredPort)||preferredPort<0))fail("run",`invalid --port ${JSON.stringify(args.port)}`);let progress=createProgress();progress.start(targets.length>1?"starting canvases":"starting canvas");let spawned=0,running=[];try{for(let[index,target]of targets.entries()){if(targets.length>1)progress.step(`starting ${label(target)}`);let rec=await ensureDaemon(target.folder,{...preferredPort!==void 0&&index===0?{preferredPort}:{},host:args.host,onSpawn:()=>{spawned++,progress.step(`waiting for ${label(target)}`)}});running.push({target,rec})}progress.succeed(spawned===0?targets.length>1?"connected to running canvases":"connected to running canvas":targets.length>1?`started ${targets.length} canvases`:"started canvas")}catch(error){throw progress.fail("could not start canvas"),error}let designArg=args.design?` ${args.design}`:"",first=running[0];if(running.length===1)console.log(`velloo: design ${designWithFolder(first.target.folder,first.target.name)}`),console.log(`velloo: canvas at ${first.rec.canvasUrl}`);else{console.log("velloo: canvases");let rows=running.map(({target,rec})=>({design:designWithFolder(target.folder,target.name),url:rec.canvasUrl})),width=Math.max(...rows.map((row)=>row.design.length));for(let row of rows)console.log(` ${import_picocolors.default.bold(row.design.padEnd(width))} ${row.url}`)}if(traceEnabled())if(spawned>0)console.log(`velloo: \u29BF trace recording ON \u2014 agent MCP calls tape to ${join(first.target.folder,".velloo","trace")} (view with \`velloo trace\`)`);else console.log(`velloo: VELLOO_TRACE is set, but a canvas was already running \u2014 recording is NOT active on it. Run \`velloo stop${designArg}\`, then re-run to record. The agent must also connect through this same (trace-enabled) velloo.`);let foreground=shouldStayForeground({background:Boolean(args.background),stdinIsTTY:Boolean(process.stdin.isTTY)});if(foreground)printKeys(running);else printBackgroundStay(designArg);let attached=running,openTarget=(index)=>{let picked=index===void 0?attached:[attached[index-1]];for(let entry of picked)if(entry)openUrl(entry.rec.canvasUrl)};if(args.open)openTarget(running.length>1?void 0:1);if(!foreground)return;for(;;){let outcome=await waitInForeground({isLive:async()=>{for(let{rec}of attached)if(!await isLive(rec))return!1;return!0},onOpen:openTarget,targetCount:attached.length});if(outcome==="background"){printBackgroundStay(designArg);return}if(outcome==="died"){let alive=[];for(let entry of attached)if(await isLive(entry.rec))alive.push(entry);else console.log(`velloo: the canvas for "${label(entry.target)}" stopped.`);if(attached=alive,attached.length===0){console.log(`velloo: no canvases left. Start again with \`velloo run${designArg}\`.`);return}console.log(import_picocolors.default.dim(` Still running: ${attached.map(({target})=>label(target)).join(", ")}.`)),printKeys(attached);continue}for(let{target}of attached){let stopped=await stopDaemon(daemonRoot(target.folder));console.log(stopped?`velloo: stopped ${designWithFolder(target.folder)}`:`velloo: no canvas running for ${designWithFolder(target.folder)}`)}return}}});export{run_default as default};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{detectTailwindMajor}from"./chunk-
|
|
2
|
+
import{detectTailwindMajor}from"./chunk-egccxmed.js";import{existsSync,readFileSync}from"fs";import{join}from"path";function readJson(path){try{return JSON.parse(readFileSync(path,"utf8"))}catch{return null}}function depRange(deps,name){let v=deps[name];return typeof v==="string"?v:void 0}function detectHost(appRoot){let pkg=readJson(join(appRoot,"package.json"))??{},deps={...pkg.dependencies??{},...pkg.devDependencies??{}},tailwindMajor=detectTailwindMajor(appRoot),componentsJson=readJson(join(appRoot,"components.json")),shadcn=componentsJson!==null,shadcnStyle=typeof componentsJson?.style==="string"?componentsJson.style:void 0,uiLibrary=depRange(deps,"@mui/material")?"mui":depRange(deps,"antd")?"antd":depRange(deps,"@chakra-ui/react")?"chakra":shadcn?"shadcn":void 0,unsupportedUi=uiLibrary?void 0:detectUnsupportedUi(deps);return{shadcn,shadcnStyle,tailwindMajor,globalsCssPath:findGlobalsCss(appRoot,componentsJson),...uiLibrary?{uiLibrary}:{},...unsupportedUi?{unsupportedUi}:{}}}var COMPONENT_DIR_CANDIDATES=["src/components/ui","components/ui","app/components/ui","src/app/components/ui","src/components","components"];function findComponentsDir(appRoot){return COMPONENT_DIR_CANDIDATES.find((rel)=>existsSync(join(appRoot,rel)))}function detectUnsupportedUi(deps){let known=[["@mantine/core","Mantine"],["@nextui-org/react","NextUI"],["@heroui/react","HeroUI"],["react-bootstrap","React Bootstrap"],["@fluentui/react-components","Fluent UI"],["@untitledui/icons","Untitled UI"],["@untitledui/file-icons","Untitled UI"],["react-aria-components","React Aria"]];for(let[pkg,label]of known)if(depRange(deps,pkg))return label;return}function findGlobalsCss(appRoot,componentsJson){let candidates=[componentsJson&&typeof componentsJson.tailwind?.css==="string"?componentsJson.tailwind.css:void 0,"src/app/globals.css","app/globals.css","src/styles/globals.css","styles/globals.css","src/index.css","src/globals.css","src/app.css"].filter((c)=>Boolean(c));for(let rel of candidates){let abs=join(appRoot,rel);if(existsSync(abs))return abs}return}
|
|
3
3
|
export{detectHost,findComponentsDir};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import"./chunk-gsne0ap4.js";import"./chunk-nte0t7v3.js";import{loadCredential,verifyCredential}from"./chunk-njhdn9j0.js";import"./chunk-egccxmed.js";import"./chunk-c4fym47a.js";import"./chunk-q4d60wr3.js";import{listDaemons}from"./chunk-1tdh4h14.js";import"./chunk-80fjfyha.js";import"./chunk-s23gtejf.js";import"./chunk-2fgfex9b.js";import"./chunk-z1r0sc3k.js";import"./chunk-cvn689d5.js";import"./chunk-v0syg2hx.js";import{designWithFolder}from"./chunk-09385g5r.js";import"./chunk-vh4144az.js";import{defaultCloudUrl}from"./chunk-25j8vgpa.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";var import_picocolors=__toESM(require_picocolors(),1);var status_default=defineCommand({meta:{name:"status",description:"List the velloo canvas daemons running on this machine + cloud sign-in"},args:{},async run(){let daemons=await listDaemons();if(daemons.length===0)console.log("velloo: no canvas daemons running.");else for(let d of daemons)console.log(`${d.canvasUrl} ${designWithFolder(d.root,void 0,{absolute:!0})} (pid ${d.pid}, since ${d.startedAt})`);let cloudUrl=defaultCloudUrl(),cred=await loadCredential(cloudUrl);if(!cred){console.log(import_picocolors.default.dim(`cloud: not signed in (${cloudUrl}) \u2014 run \`velloo login\` to share.`));return}let email=await verifyCredential(cloudUrl,cred.token,3000);console.log(email?`cloud: signed in as ${email} (${cloudUrl})`:import_picocolors.default.dim(`cloud: stored credential for ${cred.email} (${cloudUrl}) \u2014 expired or cloud unreachable; \`velloo login\` to refresh.`))}});export{status_default as default};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{localDesignOf}from"./chunk-
|
|
2
|
+
import{localDesignOf}from"./chunk-egccxmed.js";import{TOOL_VERSION}from"./chunk-q4d60wr3.js";import{isCancel,multiselect,log,select}from"./chunk-80fjfyha.js";import{manifestListing,findDesigns}from"./chunk-46xykc71.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM}from"./chunk-2smsg2ct.js";import{existsSync}from"fs";import{homedir}from"os";import{dirname,join}from"path";var claudeEntry=(conn)=>conn.transport==="http"?{type:"http",url:conn.url}:{command:conn.command,args:conn.args},cursorEntry=(conn)=>conn.transport==="http"?{url:conn.url}:{command:conn.command,args:conn.args},codexEntry=cursorEntry,continueEntry=(conn)=>conn.transport==="http"?{name:"velloo",type:"streamable-http",url:conn.url}:{name:"velloo",type:"stdio",command:conn.command,args:conn.args},droidEntry=(conn)=>conn.transport==="http"?{type:"http",url:conn.url}:{type:"stdio",command:conn.command,args:conn.args,disabled:!1},geminiEntry=(conn)=>conn.transport==="http"?{httpUrl:conn.url}:{command:conn.command,args:conn.args},opencodeEntry=(conn)=>conn.transport==="http"?{type:"remote",url:conn.url,enabled:!0}:{type:"local",command:[conn.command,...conn.args],enabled:!0},vscodeEntry=(conn)=>conn.transport==="http"?{type:"http",url:conn.url}:{type:"stdio",command:conn.command,args:conn.args};function claudeDesktopConfigPath(homeDir){if(process.platform==="win32")return join(homeDir,"AppData","Roaming","Claude","claude_desktop_config.json");if(process.platform==="darwin")return join(homeDir,"Library","Application Support","Claude","claude_desktop_config.json");return join(homeDir,".config","Claude","claude_desktop_config.json")}var AGENTS={"claude-code":{id:"claude-code",label:"Claude Code (project)",scope:"project",format:"json",family:"claude-code",path:(root)=>join(root,".mcp.json"),entry:claudeEntry},cursor:{id:"cursor",label:"Cursor (project)",scope:"project",format:"json",family:"cursor",path:(root)=>join(root,".cursor","mcp.json"),entry:cursorEntry},codex:{id:"codex",label:"Codex CLI (project)",scope:"project",format:"codex-toml",path:(root)=>join(root,".codex","config.toml"),entry:codexEntry},continue:{id:"continue",label:"Continue (project)",scope:"project",format:"continue-block",path:(root)=>join(root,".continue","mcpServers","velloo.yaml"),entry:continueEntry},"claude-code-global":{id:"claude-code-global",label:"Claude Code (global)",scope:"global",format:"json",family:"claude-code",path:(_root,home)=>join(home,".claude.json"),entry:claudeEntry},"cursor-global":{id:"cursor-global",label:"Cursor (global)",scope:"global",format:"json",family:"cursor",path:(_root,home)=>join(home,".cursor","mcp.json"),entry:cursorEntry},"codex-global":{id:"codex-global",label:"Codex CLI (global)",scope:"global",format:"codex-toml",path:(_root,home)=>join(home,".codex","config.toml"),entry:codexEntry},"continue-global":{id:"continue-global",label:"Continue (global)",scope:"global",format:"continue-config",path:(_root,home)=>join(home,".continue","config.yaml"),entry:continueEntry},"claude-desktop":{id:"claude-desktop",label:"Claude Desktop",scope:"global",format:"json",gui:!0,path:(_root,home)=>claudeDesktopConfigPath(home),entry:claudeEntry},opencode:{id:"opencode",label:"opencode (project)",scope:"project",format:"opencode-json",path:(root)=>join(root,"opencode.json"),entry:opencodeEntry},"opencode-global":{id:"opencode-global",label:"opencode (global)",scope:"global",format:"opencode-json",path:(_root,home)=>join(home,".config","opencode","opencode.json"),entry:opencodeEntry},"droid-global":{id:"droid-global",label:"Droid (global)",scope:"global",format:"json",path:(_root,home)=>join(home,".factory","mcp.json"),entry:droidEntry},"cline-global":{id:"cline-global",label:"Cline (global)",scope:"global",format:"json",path:(_root,home)=>join(home,".cline","data","settings","cline_mcp_settings.json"),entry:cursorEntry},gemini:{id:"gemini",label:"Gemini CLI (project)",scope:"project",format:"json",path:(root)=>join(root,".gemini","settings.json"),entry:geminiEntry},"gemini-global":{id:"gemini-global",label:"Gemini CLI (global)",scope:"global",format:"json",path:(_root,home)=>join(home,".gemini","settings.json"),entry:geminiEntry},"windsurf-global":{id:"windsurf-global",label:"Windsurf (global)",scope:"global",format:"json",path:(_root,home)=>join(home,".codeium","windsurf","mcp_config.json"),entry:cursorEntry},vscode:{id:"vscode",label:"VS Code / Copilot (project)",scope:"project",format:"vscode-json",path:(root)=>join(root,".vscode","mcp.json"),entry:vscodeEntry}},AGENT_IDS=Object.keys(AGENTS),PROJECT_AGENT_IDS=["claude-code","cursor"],GLOBAL_AGENT_IDS=["claude-code-global","cursor-global"];function detectInstalledAgents(homeDir=homedir()){let has=(rel)=>existsSync(join(homeDir,rel)),found=[];if(Bun.which("claude")||has(".claude.json")||has(".claude"))found.push("claude-code-global");if(Bun.which("cursor-agent")||Bun.which("cursor")||has(".cursor"))found.push("cursor-global");if(Bun.which("codex")||has(".codex"))found.push("codex-global");if(has(".continue"))found.push("continue-global");if(existsSync(dirname(claudeDesktopConfigPath(homeDir))))found.push("claude-desktop");if(Bun.which("opencode")||has(join(".config","opencode")))found.push("opencode-global");if(Bun.which("droid")||has(".factory"))found.push("droid-global");if(Bun.which("cline")||has(".cline"))found.push("cline-global");if(Bun.which("gemini")||has(join(".gemini","settings.json")))found.push("gemini-global");if(has(join(".codeium","windsurf")))found.push("windsurf-global");return found}import{existsSync as existsSync2,realpathSync}from"fs";import{access}from"fs/promises";import{dirname as dirname2,join as join2,parse,relative,resolve,sep}from"path";async function resolveProjectRoot(designFolder,override){if(override)return override;let local=localDesignOf(designFolder);if(local)return local.root;let registered=await registeringManifest(designFolder);if(registered)return registered.dir;return nearestPackageRoot(designFolder)}async function designArgumentFor(projectRoot,designFolder){let set=await findDesigns(projectRoot).catch(()=>null),target=realOr(designFolder);if(set?.designs.some((d)=>!d.outsideRepo&&realOr(d.root)===target))return null;return relative(projectRoot,designFolder).split(sep).join("/")||"."}function realOr(path){return existsSync2(path)?realpathSync(path):resolve(path)}async function agentRootCandidates(designFolder){if(localDesignOf(designFolder))return[];let roots=[await resolveProjectRoot(designFolder)];return roots.push(await nearestPackageRoot(designFolder)),[...new Set(roots)]}async function registeringManifest(designFolder){let abs=resolve(designFolder),found;try{found=await manifestListing(abs)}catch{return null}if(!found)return null;let real=existsSync2(abs)?realpathSync(abs):abs;return found.folders.some((folder)=>folder===abs||existsSync2(folder)&&realpathSync(folder)===real)?found:null}async function nearestPackageRoot(designFolder){let fallback=dirname2(designFolder),dir=fallback,fsRoot=parse(dir).root;for(let i=0;i<8;i++){try{return await access(join2(dir,"package.json")),dir}catch{}if(dir===fsRoot)break;dir=dirname2(dir)}return fallback}import{existsSync as existsSync5}from"fs";import{homedir as homedir3}from"os";import{join as join6}from"path";import{mkdir,writeFile}from"fs/promises";import{dirname as dirname3,join as join3}from"path";async function installCursorRules(projectRoot,designFolder){let pinned=await designArgumentFor(projectRoot,designFolder),arg=pinned?` ${pinned}`:"",body=`---
|
|
3
3
|
description: Drive UI design through the Velloo MCP server (a local design canvas backed by this project's shadcn components).
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
---
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{BoardSchema,isArchived,designNameIssue,toDesignName,ConfigSchema,ScreenSchema}from"./chunk-nte0t7v3.js";import{writeJsonAtomic,listLocalDesigns,writeLocalDesign,readRepoManifestAt,findOwningManifest,recordedDesignName,findDesigns2,pickDesign}from"./chunk-5ezzc1h2.js";import{isCancel,multiselect,select}from"./chunk-80fjfyha.js";import{fail}from"./chunk-s23gtejf.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,rm,stat}from"fs/promises";import{homedir}from"os";import{basename,dirname,isAbsolute,join,relative,resolve,sep}from"path";var MANIFEST_FILE="velloo.json",DEFAULT_FOLDER_NAME="velloo";function findManifest(dir){return readRepoManifestAt(dir,{strict:!0})}function manifestListing(folder){return findOwningManifest(folder,{strict:!0})}function findDesigns(cwd){return findDesigns2(cwd,{strict:!0})}function sameFolder(a,b){return a===b||existsSync(a)&&existsSync(b)&&realpathSync(a)===realpathSync(b)}function assertCurrent(found){if(found.legacy)throw Error(`${found.path} is in the pre-designs format (\`projects\`) \u2014 run \`velloo upgrade\` to migrate it first.`)}async function unregisterDesign(folder,projectDir){let abs=resolve(folder),here=projectDir?await findManifest(projectDir):null,found=here?.folders.some((path)=>sameFolder(path,abs))?here:await manifestListing(abs);if(!found)return{name:null,path:null,removedManifest:!1};let index=found.folders.findIndex((path)=>sameFolder(path,abs));if(index===-1)return{name:null,path:found.path,removedManifest:!1};assertCurrent(found);let name=recordedDesignName(abs)??basename(abs),manifest=withoutDesign(found.manifest,index,name);if(manifest===null)return await rm(found.path,{force:!0}),{name,path:found.path,removedManifest:!0};return await writeJsonAtomic(found.path,manifest),{name,path:found.path,removedManifest:!1}}function withoutDesign(manifest,index,name){let designs=manifest.designs.filter((_,i)=>i!==index),{defaultDesign,...rest}=manifest;if(designs.length===0&&!rest.feedback)return null;return{...rest,designs,...defaultDesign&&defaultDesign!==name?{defaultDesign}:{}}}function isWithin(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!isAbsolute(rel)}async function findGitRoot(start){let home=homedir(),dir=resolve(start);while(dir!==home){try{return await stat(join(dir,".git")),dir}catch{}let parent=dirname(dir);if(parent===dir)return null;dir=parent}return null}var SCAN_SKIP=new Set(["node_modules",".git","dist","build",".next",".turbo","coverage",".velloo"]);async function discoverDesignFolders(root,maxDepth=3){let out=[],walk=async(dir,depth)=>{let entries;try{entries=await readdir(dir,{withFileTypes:!0})}catch{return}for(let entry of entries){if(!entry.isDirectory()||entry.name.startsWith(".")||SCAN_SKIP.has(entry.name))continue;let child=join(dir,entry.name);try{await stat(join(child,".design","config.json")),out.push(child);continue}catch{}if(depth<maxDepth)await walk(child,depth+1)}};return await walk(resolve(root),1),out.sort()}function deriveName(folder,projectDir){let base=basename(folder),nested=base===DEFAULT_FOLDER_NAME&&dirname(folder)!==projectDir;return toDesignName(nested?basename(dirname(folder)):base)??"app"}async function chooseDesignName(request){let folder=resolve(request.folder),issue=request.requested===void 0?null:designNameIssue(request.requested);if(issue)throw Error(`invalid --name ${JSON.stringify(request.requested)} \u2014 ${issue}`);let taken=await takenNames(request,folder);if(request.requested){let holder=taken.get(request.requested);if(holder)throw Error(`a design named "${request.requested}" already exists at ${holder} \u2014 pick another --name.`);return request.requested}let base;if(request.storage==="repository")base=deriveName(folder,resolve(request.checkout));else{let chosen=request.storage==="chosen"&&basename(folder)!==DEFAULT_FOLDER_NAME;base=deriveName(chosen?folder:request.appRoot)}let name=base;for(let n=2;taken.has(name);n++)name=`${base}-${n}`;return name}async function takenNames(request,folder){let from=request.checkout,taken=new Map,set=await findDesigns(from).catch(()=>null);for(let design of set?.designs??[])if(!sameFolder(design.root,folder))taken.set(design.name,design.root);if(!set?.repo)for(let sibling of await discoverDesignFolders(request.checkout,1)){let name=recordedDesignName(sibling);if(name&&!sameFolder(sibling,folder))taken.set(name,sibling)}return taken}async function writeDesignName(folder,name){let path=join(folder,".design","config.json"),config=ConfigSchema.parse(JSON.parse(await readFile(path,"utf8")));await writeJsonAtomic(path,{...config,name})}async function registerDesign(folder,projectDir){let abs=resolve(folder),name=recordedDesignName(abs);if(!name)throw Error(`${abs} has no design name \u2014 run \`velloo design upgrade ${abs}\` first.`);let dir=resolve(projectDir),found=await findManifest(dir),path=found?found.path:join(dir,MANIFEST_FILE);if(found?.folders.some((target)=>sameFolder(target,abs)))return{name,path,created:!1};if(found)assertCurrent(found);let clash=(await findDesigns(dir))?.designs.find((d)=>d.name===name&&!sameFolder(d.root,abs));if(clash)throw Error(`a design named "${name}" already exists at ${clash.root} \u2014 rename one with \`velloo design rename\`.`);let designs=[...found?.manifest.designs??[]],rel=(target)=>relative(dir,target).split("\\").join("/")||".",defaultDesign=found?.manifest.defaultDesign;if(!found)for(let sibling of await discoverDesignFolders(dir,1)){if(sibling===abs)continue;if(designs.push(rel(sibling)),sibling===join(dir,DEFAULT_FOLDER_NAME))defaultDesign=recordedDesignName(sibling)??void 0}designs.push(rel(abs));let manifest={...found?.manifest??{},designs,...defaultDesign?{defaultDesign}:{}};return await writeJsonAtomic(path,manifest),{name,path,created:!0}}async function registerLocalDesign(opts){await writeLocalDesign(opts.id,{root:opts.root,appRoot:opts.appRoot,...opts.designPath?{designPath:opts.designPath}:{}})}import{existsSync as existsSync2,readdirSync}from"fs";import{readdir as readdir2,readFile as readFile2}from"fs/promises";import{homedir as homedir2}from"os";import{basename as basename2,dirname as dirname2,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";var DEFAULT_FOLDER="velloo",DESIGN_ARG_DESCRIPTION="A design name from ./velloo.json, a design folder, or a velloo.json file or its directory (default: the design folder you're in, else this directory's velloo.json: its only design or defaultDesign)";async function existingDesignFolder(appRoot){try{let found=await findDesigns(appRoot);for(let design of found?.designs??[])if(design.exists)return design.root}catch{}let conventional=join2(resolve2(appRoot),DEFAULT_FOLDER);if(await hasDesignConfig(conventional))return conventional;let[first]=await discoverDesignFolders(resolve2(appRoot));return first??null}function isDesignFolderSync(dir){return existsSync2(join2(dir,".design","config.json"))}function isEmptyOrMissingSync(dir){try{return readdirSync(dir).length===0}catch(err){if(err.code==="ENOENT")return!0;throw err}}async function hasDesignConfig(dir){try{return await readFile2(join2(dir,".design","config.json"),"utf8"),!0}catch{return!1}}var DESIGN_ID_ARG={type:"string",description:"The design's number in `velloo design list`, instead of its name or path"};async function designFolder(set,design,abort){let repo=set.repo;if(repo&&design.outsideRepo){let entry=relative2(repo.dir,design.root);abort(`design ${JSON.stringify(design.name)} in ${repo.path} points outside the repository (${entry}), which velloo.json cannot authorize. Pass the design path explicitly, or run \`velloo design bind ${design.root}\` here to keep it as a local design on this machine.`)}if(!design.exists)abort(`design ${JSON.stringify(design.name)} ${design.local?"(a local design)":`in ${repo?.path}`} points at ${design.root}, which is not a velloo design folder (no .design/config.json).`);return design.root}function quoteArg(value){return/^[\w@%+=:,./-]+$/.test(value)?value:`'${value.replace(/'/g,"'\\''")}'`}function designsLabel(set){return set.repo?.path??"this checkout's local designs"}function byName(set,name,abort){let shared=set.conflicts.get(name);if(shared)abort(`${shared.length} designs are named ${JSON.stringify(name)}: ${shared.map((d)=>d.root).join(", ")}. Pass a path, and rename one with \`velloo design rename <path> <new-name>\`.`);return set.byName.get(name)}async function resolveDesign(arg,cmd,opts={}){let abort=opts.onFail??((m)=>fail(cmd,m)),cwd=resolve2(opts.cwd??"."),set=null;try{set=await findDesigns(cwd)}catch(err){abort(err.message)}if(opts.id!==void 0){if(arg)abort("pass a design or --id, not both.");let n=Number(opts.id);if(!Number.isInteger(n)||n<1)abort("--id takes a design's number from `velloo design list`.");if(!set||set.designs.length===0){if(n===1)return resolveDesign(void 0,cmd,{...opts,id:void 0});abort(`there is no design #${n} here \u2014 \`velloo design list\` numbers them.`)}let entry=set.designs[n-1];if(!entry)abort(`there is no design #${n} \u2014 ${designsLabel(set)} has ${set.designs.length}. \`velloo design list\` numbers them.`);return designFolder(set,entry,abort)}if(arg){let named=set&&!arg.includes(sep2)?byName(set,arg,abort):void 0;if(set&&named)return designFolder(set,named,abort);let explicit=resolve2(cwd,arg);if(await hasDesignConfig(explicit))return explicit;let project=basename2(explicit)==="velloo.json"&&existsSync2(explicit)?dirname2(explicit):explicit;if((await findDesigns(project).catch((err)=>abort(err.message)))?.designs.length)return resolveDesign(void 0,cmd,{...opts,cwd:project});let nested=join2(explicit,DEFAULT_FOLDER);if(await hasDesignConfig(nested))return nested;if(!set&&!arg.includes(sep2)){let above=await enclosingProject(cwd),match=above?.designs.find((d)=>d.name===arg);if(above&&match)abort(`design ${JSON.stringify(arg)} is listed in \`${shortPath(cwd,above.label)}\`, and commands only read the directory they run in. Run from \`${shortPath(cwd,above.dir)}\`, or pass its folder (\`${quoteArg(shortPath(cwd,match.root))}\`) instead of its name.`)}if(set&&!arg.includes(sep2))abort(`unknown design ${JSON.stringify(arg)} \u2014 ${designsLabel(set)} lists: ${set.designs.map((d)=>d.name).join(", ")}. Pass a design name or a folder path.`);if(opts.requireConfig)abort(`${explicit} is not a velloo design folder (no .design/config.json).`);return explicit}if(set&&set.designs.length>0){if(await hasDesignConfig(cwd))return cwd;let picked=pickDesign(set,cwd);if(picked&&picked.reason!=="arbitrary")return designFolder(set,picked.design,abort);let names=set.designs.map((d)=>d.name);if(opts.interactive&&process.stdin.isTTY){let chosen=await select({message:"Which design?",options:set.designs.map((d)=>({value:d.root,label:d.name,hint:relative2(cwd,d.root)}))});if(isCancel(chosen))abort("cancelled.");let design=set.designs.find((d)=>d.root===chosen);return designFolder(set,design,abort)}let example=cmd.startsWith("design ")?`velloo ${cmd} --id 1\` for ${quoteArg(names[0]??"")}, as numbered by \`velloo design list`:`velloo ${cmd} ${designExample(opts,names[0]??"")}`;abort(`${designsLabel(set)} lists several designs: ${names.join(", ")}. Pass one (e.g. \`${example}\`)${set.repo?' or set "defaultDesign"':""}.`)}if(await hasDesignConfig(cwd))return cwd;let here=join2(cwd,DEFAULT_FOLDER);if(await hasDesignConfig(here))return here;let takesDesign=opts.takesDesign!==!1,above=await enclosingProject(cwd);if(above){let nearest=[...above.designs].sort((a,b)=>relative2(cwd,a.root).length-relative2(cwd,b.root).length)[0],example=takesDesign&&nearest?`, or pass a design folder (e.g. \`velloo ${cmd} ${designExample(opts,relative2(cwd,nearest.root).split(sep2).join("/"))}\`)`:"";abort(`no velloo.json or design folder here. \`${shortPath(cwd,above.label)}\` lists ${above.designs.map((d)=>JSON.stringify(d.name)).join(", ")}, and commands only read the directory they run in: run from \`${shortPath(cwd,above.dir)}\`${example}.`)}let example=takesDesign?`, pass a design folder or a velloo.json path (e.g. \`velloo ${cmd} ${designExample(opts,"apps/web")}\`)`:"";abort(`no velloo.json or design folder in ${cwd}. Run from the directory that has your velloo.json${example}, or \`velloo init\` here.${movedCheckoutHint(cwd)}`)}function shortPath(cwd,path){let rel=relative2(cwd,path);return rel!==""&&rel.length<path.length?rel.split(sep2).join("/"):path}function designExample(opts,design){return opts.designFlag?`${opts.designFlag}=${quoteArg(design)}`:quoteArg(design)}async function enclosingProject(cwd){let here=resolve2(cwd);for(let dir=dirname2(here);dir!==here;){let set=await findDesigns(dir).catch(()=>null),designs=set?.designs.filter((d)=>d.exists&&!d.outsideRepo)??[];if(set&&designs.length>0)return{dir,label:set.repo?.path??`${dir} (local designs)`,designs};let parent=dirname2(dir);if(parent===dir)return null;dir=parent}return null}async function checkoutDesigns(cwd){let set=await findDesigns(cwd).catch(()=>null);if(set&&set.designs.length>0)return set.designs.filter((d)=>d.exists&&!d.outsideRepo).map((d)=>d.root);try{return[await resolveDesign(void 0,"upgrade",{requireConfig:!0,cwd,onFail:()=>{throw Error("no design here")}})]}catch{return[]}}async function resolveDesignForSession(arg,cmd,cwd=resolve2(".")){if(!arg&&!await hasDesignConfig(cwd)){let set=await findDesigns(cwd).catch(()=>null),picked=set?pickDesign(set,cwd):null;if(set&&picked?.reason==="arbitrary")return{folder:await designFolder(set,picked.design,(m)=>fail(cmd,m)),pick:"arbitrary"}}return{folder:await resolveDesign(arg,cmd,{cwd}),pick:void 0}}function movedCheckoutHint(cwd){if(!insideProject(cwd))return"";let orphans=listLocalDesigns().filter((design)=>!existsSync2(design.root)&&existsSync2(design.designRoot));if(orphans.length===0)return"";return`
|
|
2
|
+
import{BoardSchema,isArchived,designNameIssue,toDesignName,ConfigSchema,ScreenSchema}from"./chunk-nte0t7v3.js";import{writeJsonAtomic,listLocalDesigns,writeLocalDesign,readRepoManifestAt,findOwningManifest,recordedDesignName,findDesigns2,pickDesign}from"./chunk-egccxmed.js";import{isCancel,multiselect,select}from"./chunk-80fjfyha.js";import{fail}from"./chunk-s23gtejf.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,rm,stat}from"fs/promises";import{homedir}from"os";import{basename,dirname,isAbsolute,join,relative,resolve,sep}from"path";var MANIFEST_FILE="velloo.json",DEFAULT_FOLDER_NAME="velloo";function findManifest(dir){return readRepoManifestAt(dir,{strict:!0})}function manifestListing(folder){return findOwningManifest(folder,{strict:!0})}function findDesigns(cwd){return findDesigns2(cwd,{strict:!0})}function sameFolder(a,b){return a===b||existsSync(a)&&existsSync(b)&&realpathSync(a)===realpathSync(b)}function assertCurrent(found){if(found.legacy)throw Error(`${found.path} is in the pre-designs format (\`projects\`) \u2014 run \`velloo upgrade\` to migrate it first.`)}async function unregisterDesign(folder,projectDir){let abs=resolve(folder),here=projectDir?await findManifest(projectDir):null,found=here?.folders.some((path)=>sameFolder(path,abs))?here:await manifestListing(abs);if(!found)return{name:null,path:null,removedManifest:!1};let index=found.folders.findIndex((path)=>sameFolder(path,abs));if(index===-1)return{name:null,path:found.path,removedManifest:!1};assertCurrent(found);let name=recordedDesignName(abs)??basename(abs),manifest=withoutDesign(found.manifest,index,name);if(manifest===null)return await rm(found.path,{force:!0}),{name,path:found.path,removedManifest:!0};return await writeJsonAtomic(found.path,manifest),{name,path:found.path,removedManifest:!1}}function withoutDesign(manifest,index,name){let designs=manifest.designs.filter((_,i)=>i!==index),{defaultDesign,...rest}=manifest;if(designs.length===0&&!rest.feedback)return null;return{...rest,designs,...defaultDesign&&defaultDesign!==name?{defaultDesign}:{}}}function isWithin(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!isAbsolute(rel)}async function findGitRoot(start){let home=homedir(),dir=resolve(start);while(dir!==home){try{return await stat(join(dir,".git")),dir}catch{}let parent=dirname(dir);if(parent===dir)return null;dir=parent}return null}var SCAN_SKIP=new Set(["node_modules",".git","dist","build",".next",".turbo","coverage",".velloo"]);async function discoverDesignFolders(root,maxDepth=3){let out=[],walk=async(dir,depth)=>{let entries;try{entries=await readdir(dir,{withFileTypes:!0})}catch{return}for(let entry of entries){if(!entry.isDirectory()||entry.name.startsWith(".")||SCAN_SKIP.has(entry.name))continue;let child=join(dir,entry.name);try{await stat(join(child,".design","config.json")),out.push(child);continue}catch{}if(depth<maxDepth)await walk(child,depth+1)}};return await walk(resolve(root),1),out.sort()}function deriveName(folder,projectDir){let base=basename(folder),nested=base===DEFAULT_FOLDER_NAME&&dirname(folder)!==projectDir;return toDesignName(nested?basename(dirname(folder)):base)??"app"}async function chooseDesignName(request){let folder=resolve(request.folder),issue=request.requested===void 0?null:designNameIssue(request.requested);if(issue)throw Error(`invalid --name ${JSON.stringify(request.requested)} \u2014 ${issue}`);let taken=await takenNames(request,folder);if(request.requested){let holder=taken.get(request.requested);if(holder)throw Error(`a design named "${request.requested}" already exists at ${holder} \u2014 pick another --name.`);return request.requested}let base;if(request.storage==="repository")base=deriveName(folder,resolve(request.checkout));else{let chosen=request.storage==="chosen"&&basename(folder)!==DEFAULT_FOLDER_NAME;base=deriveName(chosen?folder:request.appRoot)}let name=base;for(let n=2;taken.has(name);n++)name=`${base}-${n}`;return name}async function takenNames(request,folder){let from=request.checkout,taken=new Map,set=await findDesigns(from).catch(()=>null);for(let design of set?.designs??[])if(!sameFolder(design.root,folder))taken.set(design.name,design.root);if(!set?.repo)for(let sibling of await discoverDesignFolders(request.checkout,1)){let name=recordedDesignName(sibling);if(name&&!sameFolder(sibling,folder))taken.set(name,sibling)}return taken}async function writeDesignName(folder,name){let path=join(folder,".design","config.json"),config=ConfigSchema.parse(JSON.parse(await readFile(path,"utf8")));await writeJsonAtomic(path,{...config,name})}async function registerDesign(folder,projectDir){let abs=resolve(folder),name=recordedDesignName(abs);if(!name)throw Error(`${abs} has no design name \u2014 run \`velloo design upgrade ${abs}\` first.`);let dir=resolve(projectDir),found=await findManifest(dir),path=found?found.path:join(dir,MANIFEST_FILE);if(found?.folders.some((target)=>sameFolder(target,abs)))return{name,path,created:!1};if(found)assertCurrent(found);let clash=(await findDesigns(dir))?.designs.find((d)=>d.name===name&&!sameFolder(d.root,abs));if(clash)throw Error(`a design named "${name}" already exists at ${clash.root} \u2014 rename one with \`velloo design rename\`.`);let designs=[...found?.manifest.designs??[]],rel=(target)=>relative(dir,target).split("\\").join("/")||".",defaultDesign=found?.manifest.defaultDesign;if(!found)for(let sibling of await discoverDesignFolders(dir,1)){if(sibling===abs)continue;if(designs.push(rel(sibling)),sibling===join(dir,DEFAULT_FOLDER_NAME))defaultDesign=recordedDesignName(sibling)??void 0}designs.push(rel(abs));let manifest={...found?.manifest??{},designs,...defaultDesign?{defaultDesign}:{}};return await writeJsonAtomic(path,manifest),{name,path,created:!0}}async function registerLocalDesign(opts){await writeLocalDesign(opts.id,{root:opts.root,appRoot:opts.appRoot,...opts.designPath?{designPath:opts.designPath}:{}})}import{existsSync as existsSync2,readdirSync}from"fs";import{readdir as readdir2,readFile as readFile2}from"fs/promises";import{homedir as homedir2}from"os";import{basename as basename2,dirname as dirname2,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";var DEFAULT_FOLDER="velloo",DESIGN_ARG_DESCRIPTION="A design name from ./velloo.json, a design folder, or a velloo.json file or its directory (default: the design folder you're in, else this directory's velloo.json: its only design or defaultDesign)";async function existingDesignFolder(appRoot){try{let found=await findDesigns(appRoot);for(let design of found?.designs??[])if(design.exists)return design.root}catch{}let conventional=join2(resolve2(appRoot),DEFAULT_FOLDER);if(await hasDesignConfig(conventional))return conventional;let[first]=await discoverDesignFolders(resolve2(appRoot));return first??null}function isDesignFolderSync(dir){return existsSync2(join2(dir,".design","config.json"))}function isEmptyOrMissingSync(dir){try{return readdirSync(dir).length===0}catch(err){if(err.code==="ENOENT")return!0;throw err}}async function hasDesignConfig(dir){try{return await readFile2(join2(dir,".design","config.json"),"utf8"),!0}catch{return!1}}var DESIGN_ID_ARG={type:"string",description:"The design's number in `velloo design list`, instead of its name or path"};async function designFolder(set,design,abort){let repo=set.repo;if(repo&&design.outsideRepo){let entry=relative2(repo.dir,design.root);abort(`design ${JSON.stringify(design.name)} in ${repo.path} points outside the repository (${entry}), which velloo.json cannot authorize. Pass the design path explicitly, or run \`velloo design bind ${design.root}\` here to keep it as a local design on this machine.`)}if(!design.exists)abort(`design ${JSON.stringify(design.name)} ${design.local?"(a local design)":`in ${repo?.path}`} points at ${design.root}, which is not a velloo design folder (no .design/config.json).`);return design.root}function quoteArg(value){return/^[\w@%+=:,./-]+$/.test(value)?value:`'${value.replace(/'/g,"'\\''")}'`}function designsLabel(set){return set.repo?.path??"this checkout's local designs"}function byName(set,name,abort){let shared=set.conflicts.get(name);if(shared)abort(`${shared.length} designs are named ${JSON.stringify(name)}: ${shared.map((d)=>d.root).join(", ")}. Pass a path, and rename one with \`velloo design rename <path> <new-name>\`.`);return set.byName.get(name)}async function resolveDesign(arg,cmd,opts={}){let abort=opts.onFail??((m)=>fail(cmd,m)),cwd=resolve2(opts.cwd??"."),set=null;try{set=await findDesigns(cwd)}catch(err){abort(err.message)}if(opts.id!==void 0){if(arg)abort("pass a design or --id, not both.");let n=Number(opts.id);if(!Number.isInteger(n)||n<1)abort("--id takes a design's number from `velloo design list`.");if(!set||set.designs.length===0){if(n===1)return resolveDesign(void 0,cmd,{...opts,id:void 0});abort(`there is no design #${n} here \u2014 \`velloo design list\` numbers them.`)}let entry=set.designs[n-1];if(!entry)abort(`there is no design #${n} \u2014 ${designsLabel(set)} has ${set.designs.length}. \`velloo design list\` numbers them.`);return designFolder(set,entry,abort)}if(arg){let named=set&&!arg.includes(sep2)?byName(set,arg,abort):void 0;if(set&&named)return designFolder(set,named,abort);let explicit=resolve2(cwd,arg);if(await hasDesignConfig(explicit))return explicit;let project=basename2(explicit)==="velloo.json"&&existsSync2(explicit)?dirname2(explicit):explicit;if((await findDesigns(project).catch((err)=>abort(err.message)))?.designs.length)return resolveDesign(void 0,cmd,{...opts,cwd:project});let nested=join2(explicit,DEFAULT_FOLDER);if(await hasDesignConfig(nested))return nested;if(!set&&!arg.includes(sep2)){let above=await enclosingProject(cwd),match=above?.designs.find((d)=>d.name===arg);if(above&&match)abort(`design ${JSON.stringify(arg)} is listed in \`${shortPath(cwd,above.label)}\`, and commands only read the directory they run in. Run from \`${shortPath(cwd,above.dir)}\`, or pass its folder (\`${quoteArg(shortPath(cwd,match.root))}\`) instead of its name.`)}if(set&&!arg.includes(sep2))abort(`unknown design ${JSON.stringify(arg)} \u2014 ${designsLabel(set)} lists: ${set.designs.map((d)=>d.name).join(", ")}. Pass a design name or a folder path.`);if(opts.requireConfig)abort(`${explicit} is not a velloo design folder (no .design/config.json).`);return explicit}if(set&&set.designs.length>0){if(await hasDesignConfig(cwd))return cwd;let picked=pickDesign(set,cwd);if(picked&&picked.reason!=="arbitrary")return designFolder(set,picked.design,abort);let names=set.designs.map((d)=>d.name);if(opts.interactive&&process.stdin.isTTY){let chosen=await select({message:"Which design?",options:set.designs.map((d)=>({value:d.root,label:d.name,hint:relative2(cwd,d.root)}))});if(isCancel(chosen))abort("cancelled.");let design=set.designs.find((d)=>d.root===chosen);return designFolder(set,design,abort)}let example=cmd.startsWith("design ")?`velloo ${cmd} --id 1\` for ${quoteArg(names[0]??"")}, as numbered by \`velloo design list`:`velloo ${cmd} ${designExample(opts,names[0]??"")}`;abort(`${designsLabel(set)} lists several designs: ${names.join(", ")}. Pass one (e.g. \`${example}\`)${set.repo?' or set "defaultDesign"':""}.`)}if(await hasDesignConfig(cwd))return cwd;let here=join2(cwd,DEFAULT_FOLDER);if(await hasDesignConfig(here))return here;let takesDesign=opts.takesDesign!==!1,above=await enclosingProject(cwd);if(above){let nearest=[...above.designs].sort((a,b)=>relative2(cwd,a.root).length-relative2(cwd,b.root).length)[0],example=takesDesign&&nearest?`, or pass a design folder (e.g. \`velloo ${cmd} ${designExample(opts,relative2(cwd,nearest.root).split(sep2).join("/"))}\`)`:"";abort(`no velloo.json or design folder here. \`${shortPath(cwd,above.label)}\` lists ${above.designs.map((d)=>JSON.stringify(d.name)).join(", ")}, and commands only read the directory they run in: run from \`${shortPath(cwd,above.dir)}\`${example}.`)}let example=takesDesign?`, pass a design folder or a velloo.json path (e.g. \`velloo ${cmd} ${designExample(opts,"apps/web")}\`)`:"";abort(`no velloo.json or design folder in ${cwd}. Run from the directory that has your velloo.json${example}, or \`velloo init\` here.${movedCheckoutHint(cwd)}`)}function shortPath(cwd,path){let rel=relative2(cwd,path);return rel!==""&&rel.length<path.length?rel.split(sep2).join("/"):path}function designExample(opts,design){return opts.designFlag?`${opts.designFlag}=${quoteArg(design)}`:quoteArg(design)}async function enclosingProject(cwd){let here=resolve2(cwd);for(let dir=dirname2(here);dir!==here;){let set=await findDesigns(dir).catch(()=>null),designs=set?.designs.filter((d)=>d.exists&&!d.outsideRepo)??[];if(set&&designs.length>0)return{dir,label:set.repo?.path??`${dir} (local designs)`,designs};let parent=dirname2(dir);if(parent===dir)return null;dir=parent}return null}async function checkoutDesigns(cwd){let set=await findDesigns(cwd).catch(()=>null);if(set&&set.designs.length>0)return set.designs.filter((d)=>d.exists&&!d.outsideRepo).map((d)=>d.root);try{return[await resolveDesign(void 0,"upgrade",{requireConfig:!0,cwd,onFail:()=>{throw Error("no design here")}})]}catch{return[]}}async function resolveDesignForSession(arg,cmd,cwd=resolve2(".")){if(!arg&&!await hasDesignConfig(cwd)){let set=await findDesigns(cwd).catch(()=>null),picked=set?pickDesign(set,cwd):null;if(set&&picked?.reason==="arbitrary")return{folder:await designFolder(set,picked.design,(m)=>fail(cmd,m)),pick:"arbitrary"}}return{folder:await resolveDesign(arg,cmd,{cwd}),pick:void 0}}function movedCheckoutHint(cwd){if(!insideProject(cwd))return"";let orphans=listLocalDesigns().filter((design)=>!existsSync2(design.root)&&existsSync2(design.designRoot));if(orphans.length===0)return"";return`
|
|
3
3
|
This machine has designs for checkouts that no longer exist at their old paths:${orphans.map((design)=>`
|
|
4
4
|
"${recordedDesignName(design.designRoot)??design.legacyName??design.id}" at ${design.designRoot}, made for ${design.root}`).join("")}
|
|
5
5
|
If this is one of those checkouts, moved, attach its design with \`velloo design bind <design folder>\`. Otherwise forget it with \`velloo design remove <design folder>\`.`}function insideProject(cwd){let home=homedir2();for(let dir=cwd;dir!==home&&dirname2(dir)!==dir;dir=dirname2(dir))if(existsSync2(join2(dir,".git"))||existsSync2(join2(dir,"package.json")))return!0;return!1}async function listScreens(folder){let files;try{files=(await readdir2(join2(folder,"screens"))).filter((f)=>f.endsWith(".json"))}catch{return[]}let out=[];for(let f of files.sort()){let path=join2(folder,"screens",f);try{let s=ScreenSchema.parse(JSON.parse(await readFile2(path,"utf8")));out.push({id:s.id,name:s.name||s.id,path})}catch{}}return out}async function listBoards(folder){let files;try{files=(await readdir2(join2(folder,"boards"))).filter((f)=>f.endsWith(".json"))}catch{return[]}let out=[];for(let f of files.sort())try{let b=BoardSchema.parse(JSON.parse(await readFile2(join2(folder,"boards",f),"utf8")));out.push({id:b.id,name:b.name||b.id,screens:b.frames.map((fr)=>fr.screen),archived:isArchived(b)})}catch{}return out}function boardsByScreen(boards){let map=new Map;for(let b of boards)for(let sid of b.screens){let names=map.get(sid)??[];names.push(b.name),map.set(sid,names)}return map}async function pickBoards(folder,arg,interactive,cmd){let all=await listBoards(folder);if(all.length===0)return[];if(arg){let ids=new Set(arg.split(",").map((s)=>s.trim()).filter(Boolean)),chosen=all.filter((b)=>ids.has(b.id));if(chosen.length===0)fail(cmd,`no boards matched "${arg}". Available: ${all.map((b)=>b.id).join(", ")}`);return chosen}let boards=all.filter((b)=>!b.archived);if(boards.length===0)return[];if(!interactive)return boards;let chosen=await multiselect(boardSelectionPrompt(boards));if(isCancel(chosen))fail(cmd,"cancelled.");return resolveBoardSelection(boards,chosen)}function boardSelectionPrompt(boards){return{message:"Which boards to publish? (a: select all / clear all)",options:boards.map((board)=>({value:board.id,label:board.name,hint:`${board.screens.length} screen${board.screens.length===1?"":"s"}`})),initialValues:[],required:!1}}function resolveBoardSelection(boards,selectedIds){let selected=new Set(selectedIds),chosen=boards.filter((board)=>selected.has(board.id));return chosen.length>0?chosen:null}async function pickScreen(folder,arg,interactive,cmd){let screens=await listScreens(folder);if(arg){let byId=screens.find((s)=>s.id===arg);if(byId)return byId;return{id:arg,name:arg,path:resolve2(arg)}}if(screens.length===0)fail(cmd,`no screens found in ${folder}/screens.`);if(screens.length===1)return screens[0];if(!interactive)fail(cmd,`pick a screen: ${screens.map((s)=>s.id).join(", ")}`);let onBoard=boardsByScreen(await listBoards(folder)),chosen=await select({message:"Which screen?",options:screens.map((s)=>({value:s.id,label:s.name,hint:onBoard.get(s.id)?.join(", ")??`${s.id}.json`}))});if(isCancel(chosen))fail(cmd,"cancelled.");return screens.find((s)=>s.id===chosen)}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import"./chunk-nte0t7v3.js";import{topUpTokens}from"./chunk-egccxmed.js";import"./chunk-gsne0ap4.js";import"./chunk-c4fym47a.js";import{normalizeCloudUrl,loadCredential,saveCredential,verifyCredential,performDeviceLogin}from"./chunk-njhdn9j0.js";import{fail}from"./chunk-s23gtejf.js";import{defaultCloudUrl}from"./chunk-25j8vgpa.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{createInterface}from"readline/promises";async function waitForEnter(message){if(!process.stdin.isTTY)return;let rl=createInterface({input:process.stdin,output:process.stdout});try{await rl.question(message)}finally{rl.close()}}var login_default=defineCommand({meta:{name:"login",description:"Log in to velloo-cloud (opens the browser, saves a CLI token)"},args:{url:{type:"string",description:"velloo-cloud base URL (default: $VELLOO_CLOUD_URL or the built-in default)"},force:{type:"boolean",description:"Re-authenticate even if already logged in",default:!1}},async run({args}){let cloudUrl=args.url?normalizeCloudUrl(args.url):defaultCloudUrl();if(!args.force){let existing=await loadCredential(cloudUrl),email=existing&&await verifyCredential(cloudUrl,existing.token);if(email){console.log(`velloo login: already logged in to ${cloudUrl} as ${email}.`),console.log(" Use `velloo login --force` to switch accounts, or `velloo logout` to sign out.");return}}let result;try{result=await performDeviceLogin(cloudUrl,async({verificationUrl,userCode})=>{console.log(`
|
|
3
|
+
velloo login will open your browser to approve this device (code ${userCode}).`),await waitForEnter("Press Enter to open the browser\u2026 "),console.log(`Opening ${verificationUrl}`),console.log(`If the browser doesn't open, visit that URL and enter: ${userCode}`)})}catch(err){fail("login",err instanceof Error?err.message:String(err))}let path=await saveCredential(cloudUrl,result);console.log(`velloo login: logged in to ${cloudUrl} as ${result.email}`),console.log(` credentials saved to ${path}`);try{await topUpTokens({url:cloudUrl,token:result.token},10)}catch{}}});export{login_default as default};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{isCancel,confirm,log,cancel,note,spinner,select,text}from"./chunk-80fjfyha.js";import{designNameIssue,CURRENT_SCHEMA_VERSION,schemaVersionOf,APP_PATH_PREFIX,planMigration,ConfigSchema}from"./chunk-nte0t7v3.js";import{writeJsonAtomic,managedDesignPath,managedDesignId,localDesignOf,writeLocalDesign,removeLocalDesign,resolveAppPath,recordedDesignName,pickDesign,readRepoFeedback,writeRepoFeedback,writeFeedbackContactOk,loadDesignFolder,hostAppRootFrom,topUpTokens}from"./chunk-5ezzc1h2.js";import{CHROMIUM_INSTALL_CMD,chromiumExecutable}from"./chunk-c4fym47a.js";import{PROJECT_AGENT_IDS,resolveProjectRoot,manualSetupText,askAgentWiring,globallyWiredAgents,connect}from"./chunk-9nhw49vv.js";import{TOOL_VERSION}from"./chunk-kz4423th.js";import{fail}from"./chunk-s23gtejf.js";import{daemonRoot,ensureDaemon,stopDaemon}from"./chunk-k9219kmj.js";import{installedVellooBin,updateStatus,upgradeInstalledVelloo}from"./chunk-7k7dpj5y.js";import{dirExists,discoverScanRoots,scanApps,primaryApp,buildScreensFromScan,buildBoardsFromScan,DEFAULT_THEME_PRESET,isValidPreset,WIZARD_PROVIDERS,LIBRARY_IDS,DEFAULT_LIBRARY_ID,interactiveLibraryChoices,planInstall,scanAdoption,DEFAULT_STACK_ID,stackForFramework,isValidStack,importThemeFromMui,findMuiTheme,importThemeFromGlobals,isEmptyOrMissing,resolveTheme,buildScaffold,writeScaffold,displayPath,printSummary,printExitInstructions,printScreenshotReadiness,promptShellCompletions}from"./chunk-7njj1qmq.js";import{loadCredential,saveCredential}from"./chunk-hfzg98c8.js";import{installChromiumInteractive}from"./chunk-2fgfex9b.js";import{detectHost,findComponentsDir}from"./chunk-d9p6s05n.js";import{copyToClipboard}from"./chunk-vbk1qhdz.js";import{upgradeFolder}from"./chunk-jx11jvq8.js";import{findManifest,manifestListing,findDesigns,withoutDesign,isWithin,findGitRoot,chooseDesignName,registerDesign,registerLocalDesign,existingDesignFolder,isDesignFolderSync,isEmptyOrMissingSync,hasDesignConfig,resolveDesign}from"./chunk-td5z2sm4.js";import{verifyCredential,performDeviceLogin}from"./chunk-6zz9kfbd.js";import{openUrl}from"./chunk-hggap8fq.js";import{defaultCloudUrl}from"./chunk-as08hzp3.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{randomUUID as randomUUID2}from"crypto";import{dirname as dirname2,relative as relative5,resolve as resolve7}from"path";var import_picocolors11=__toESM(require_picocolors(),1);import{existsSync}from"fs";import{readFile}from"fs/promises";import{join,relative,resolve,sep}from"path";function isSentinel(path){return path==="binary"||path.startsWith("~/")}function toPosix(path){return path.split(sep).join("/")||"."}function isUnder(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!/^(?:[\\/]|[A-Za-z]:)/.test(rel)}async function readConfig(folder){return ConfigSchema.parse(JSON.parse(await readFile(join(folder,".design","config.json"),"utf8")))}async function recordedAppRoot(designFolder){let folder=resolve(designFolder),local=localDesignOf(folder);if(local){let path=resolve(local.appRoot);return{path,kind:"local",localId:local.id,checkout:local.root,...appShape(path)}}let recorded=(await readConfig(folder)).hostApp?.root,path=recorded&&!isSentinel(recorded)?resolveAppPath(folder,recorded):resolve(folder,".."),found=await manifestListing(folder).catch(()=>null),designName=recordedDesignName(folder);return{path,kind:"in-repo",...designName?{designName}:{},...found?{manifestPath:found.path}:{},...appShape(path)}}function appShape(path){let exists=existsSync(path);return{exists,looksLikeApp:exists&&existsSync(join(path,"package.json"))}}async function planAppRootChange(designFolder,newRoot){let folder=resolve(designFolder),current=await recordedAppRoot(folder),to=resolve(newRoot),change={from:current.path,to,rewritten:[]};if(current.kind==="local"&¤t.localId&¤t.checkout){if(!isUnder(current.checkout,to))throw Error(`An application root must live inside the design's checkout (${current.checkout}). ${to} is outside it.`);return change.local={id:current.localId,root:current.checkout},change}let config=await readConfig(folder),reanchor=(field,stored)=>{if(isSentinel(stored))return;let abs=resolveAppPath(folder,stored);if(!isUnder(current.path,abs))return;let next=toPosix(relative(folder,join(to,relative(current.path,abs))));if(next===stored)return;return change.rewritten.push({field,from:stored,to:next}),next};if(config.hostApp)reanchor("hostApp.root",config.hostApp.root);else change.rewritten.push({field:"hostApp.root",from:"(unset)",to:toPosix(relative(folder,to))});for(let[name,app]of Object.entries(config.hostApps??{}))reanchor(`hostApps.${name}.root`,app.root);for(let[name,library]of Object.entries(config.libraries))if(library.source==="in-repo")reanchor(`libraries.${name}.componentsPath`,library.componentsPath);return change}async function applyAppRootChange(designFolder,change){let folder=resolve(designFolder);if(change.local){await writeLocalDesign(change.local.id,{root:change.local.root,appRoot:change.to,...managedDesignId(folder)?{}:{designPath:folder}});return}let config=await readConfig(folder),patch=new Map(change.rewritten.map((r)=>[r.field,r.to])),hostRoot=patch.get("hostApp.root");if(hostRoot!==void 0)config.hostApp=config.hostApp?{...config.hostApp,root:hostRoot}:{root:hostRoot};for(let[name,app]of Object.entries(config.hostApps??{})){let next=patch.get(`hostApps.${name}.root`);if(next!==void 0)app.root=next}for(let[name,library]of Object.entries(config.libraries)){let next=patch.get(`libraries.${name}.componentsPath`);if(next!==void 0)library.componentsPath=next}await writeJsonAtomic(join(folder,".design","config.json"),ConfigSchema.parse(config))}async function appRootCandidates(near){let start=resolve(near),from=(await findManifest(start).catch(()=>null))?.dir??await findGitRoot(start)??start;return(await discoverScanRoots(from)).map((app)=>({dir:app.dir,rel:relative(from,app.dir)||"."}))}async function appRootIsNotAnApp(appRoot){let abs=resolve(appRoot);if(existsSync(join(abs,"package.json"))&&(await discoverScanRoots(abs))[0]?.dir===abs)return null;let candidates=(await appRootCandidates(abs)).filter((c)=>c.dir!==abs);return candidates.length>0?candidates:null}async function promptAppRootChoice(current,opts={}){let message=opts.message??"Which application is this design for?",currentHint=opts.currentHint??"recorded now",abs=resolve(current),candidates=await appRootCandidates(abs),options=candidates.map((c)=>({value:c.dir,label:c.rel,...c.dir===abs?{hint:currentHint}:{}}));if(!candidates.some((c)=>c.dir===abs))options.push({value:abs,label:current,hint:currentHint});if(options.length<=1)return null;let chosen=await select({message,options});if(isCancel(chosen))return null;return chosen}import{createHash,randomUUID}from"crypto";import{existsSync as existsSync2,lstatSync,realpathSync}from"fs";import{cp,lstat,mkdir,readdir,readFile as readFile2,rm}from"fs/promises";import{basename,dirname,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";function physicalDestination(path){if(lstatSync(path,{throwIfNoEntry:!1}))return realpathSync(path);let parent=dirname(path);return join2(physicalDestination(parent),relative2(parent,path))}var RUNTIME=[".design/cache",".velloo","node_modules"];async function rebaseDesignConfig(source,destination,appRoot,external){let config=ConfigSchema.parse(JSON.parse(await readFile2(join2(destination,".design/config.json"),"utf8"))),rebase=(path)=>{if(path==="binary"||path.startsWith("~/"))return path;let abs=resolveAppPath(source,path),withinDesign=relative2(source,abs);if(withinDesign===""||withinDesign!==".."&&!withinDesign.startsWith(`..${sep2}`)&&!/^(?:[\\/]|[A-Za-z]:)/.test(withinDesign))return withinDesign.split(sep2).join("/")||".";return external?`${APP_PATH_PREFIX}${relative2(appRoot,abs).split(sep2).join("/")||"."}`:relative2(destination,abs).split(sep2).join("/")||"."};config.hostApp=config.hostApp?{...config.hostApp,root:rebase(config.hostApp.root)}:{root:rebase("..")};for(let app of Object.values(config.hostApps??{}))app.root=rebase(app.root);for(let library of Object.values(config.libraries))if(library.componentsPath)library.componentsPath=rebase(library.componentsPath);await writeJsonAtomic(join2(destination,".design/config.json"),config)}async function fingerprint(folder){let hash=createHash("sha256");async function walk(rel){if(rel===".git"||RUNTIME.some((p)=>rel===p||rel.startsWith(`${p}/`)))return;let path=join2(folder,rel),stat=await lstat(path);if(stat.isSymbolicLink())throw Error(`Cannot relocate symbolic link ${path}. Replace it with its actual design content first.`);if(stat.isDirectory())for(let name of(await readdir(path)).sort())await walk(rel?`${rel}/${name}`:name);else if(stat.isFile())hash.update(rel).update("\x00").update(await readFile2(path));else throw Error(`Unsupported filesystem entry: ${path}`)}return await walk(""),hash.digest("hex")}async function planRelocation(source,cwd,to,external){if(Boolean(to)===external)throw Error("Choose exactly one destination: --external or --to <path>.");let designs=await findDesigns(cwd),physicalSource=realpathSync(source),local=designs?.local.find((design)=>realOr(design.designRoot)===physicalSource),repo=designs?.repo??null,index=repo?.folders.findIndex((path)=>existsSync2(path)&&realpathSync(path)===physicalSource)??-1;if(!local&&index===-1)throw Error("Run relocation from the checkout the design belongs to; the source must be one of its designs.");let root=local?.root??repo?.dir,name=recordedDesignName(source);if(!name)throw Error(`${source} has no design name \u2014 run \`velloo upgrade\` first.`);let physicalRoot=realpathSync(root);if(physicalSource===physicalRoot)throw Error("Cannot relocate the application repository itself. Use a separate design directory.");if(external&&managedDesignId(source))throw Error("This design already lives in managed storage.");let destination=external?managedDesignPath(local?.id??randomUUID()):resolve2(cwd,to??""),physicalTarget=physicalDestination(destination),inRepo=!external&&isWithin(physicalRoot,physicalTarget);if(physicalTarget===physicalSource||physicalTarget.startsWith(physicalSource+sep2)||physicalSource.startsWith(physicalTarget+sep2))throw Error("Source and destination cannot contain each other.");if(existsSync2(destination))throw Error(`Destination is occupied: ${destination}. Choose a new path; nothing was changed.`);let manifestPath=repo?.path??join2(root,"velloo.json"),before=repo?await readFile2(repo.path,"utf8"):null,manifest,destinationManifestPath;if(repo?.legacy)throw Error(`${repo.path} is in the pre-designs format \u2014 run \`velloo upgrade\` first.`);if(inRepo){destinationManifestPath=relative2(physicalRoot,physicalTarget).split(sep2).join("/")||".";let designs=[...repo?.manifest.designs??[]];if(index===-1)designs.push(destinationManifestPath);else designs[index]=destinationManifestPath;manifest={...repo?.manifest??{},designs}}else if(repo&&index!==-1)manifest=withoutDesign(repo.manifest,index,name);let config=ConfigSchema.parse(JSON.parse(await readFile2(join2(source,".design/config.json"),"utf8")));return{source,destination,root,appRoot:local?.appRoot??hostAppRootFrom(source,config.hostApp),designName:name,sourceLocalId:local?.id,sourceManifestPath:index===-1?void 0:repo?.manifest.designs[index],destinationManifestPath,destinationLocal:inRepo?void 0:{id:local?.id??(external?basename(destination):randomUUID()),...external?{}:{designPath:destination}},manifestPath,before,manifest}}function realOr(path){return existsSync2(path)?realpathSync(path):resolve2(path)}async function relocateDesign(plan){await stopDaemon(daemonRoot(plan.source));let original=await fingerprint(plan.source);await mkdir(dirname(plan.destination),{recursive:!0}),await mkdir(plan.destination);let committed=!1;try{for(let entry of await readdir(plan.source))await cp(join2(plan.source,entry),join2(plan.destination,entry),{recursive:!0,errorOnExist:!0,force:!1,filter:(path)=>!RUNTIME.some((p)=>{let rel=relative2(plan.source,path).split(sep2).join("/");return rel===p||rel.startsWith(`${p}/`)})});if(await fingerprint(plan.destination)!==original)throw Error("Copied content did not match the source.");if(await rebaseDesignConfig(plan.source,plan.destination,plan.appRoot,plan.destinationLocal!==void 0),await loadDesignFolder(plan.destination,{preferences:!1}),await fingerprint(plan.source)!==original)throw Error("The source changed during relocation. Close editors and retry.");if(await readFile2(plan.manifestPath,"utf8").catch(()=>null)!==plan.before)throw Error("The manifest changed during relocation. Review it and retry.");if(plan.destinationLocal){if(plan.sourceLocalId&&plan.sourceLocalId!==plan.destinationLocal.id)await removeLocalDesign(plan.sourceLocalId);await writeLocalDesign(plan.destinationLocal.id,{root:plan.root,appRoot:plan.appRoot,...plan.destinationLocal.designPath?{designPath:plan.destinationLocal.designPath}:{}})}else if(plan.sourceLocalId)await removeLocalDesign(plan.sourceLocalId);if(plan.manifest===null)await rm(plan.manifestPath,{force:!0});else if(plan.manifest)await writeJsonAtomic(plan.manifestPath,plan.manifest);committed=!0}finally{if(!committed)await rm(plan.destination,{recursive:!0,force:!0})}try{await rm(plan.source,{recursive:!0})}catch(error){throw Error(`Relocation completed to ${plan.destination}, but the old copy remains at ${plan.source}. Remove it after checking the destination. ${error}`)}}var import_picocolors=__toESM(require_picocolors(),1);async function offerUpgradeBeforeInit(argv){if(process.env.VELLOO_DISABLE_UPDATE_CHECK==="1"||!process.stdin.isTTY)return!1;let status;try{status=await updateStatus({refresh:!0})}catch{return!1}if(!status.available||!status.upgradable||!status.latest)return!1;console.log(""),console.log(`${import_picocolors.default.yellow("A newer velloo is available:")} ${status.current} \u2192 ${import_picocolors.default.bold(status.latest)}`);let yes=await confirm({message:"Upgrade before setting up this design folder?",initialValue:!0});if(isCancel(yes)||!yes)return console.log(import_picocolors.default.dim(" Continuing on the current version.")),!1;try{if(!(await upgradeInstalledVelloo()).upgraded)return!1;let bin=installedVellooBin();if(!bin)return console.log(import_picocolors.default.dim(" Upgraded. Run `velloo init` again to continue on the new version.")),!0;let code=await Bun.spawn([bin,...argv],{stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,VELLOO_DISABLE_UPDATE_CHECK:"1"}}).exited;process.exit(code)}catch(error){return console.log(import_picocolors.default.yellow(` Could not upgrade (${error instanceof Error?error.message:String(error)}) \u2014 continuing on the current version.`)),!1}}import{resolve as resolve3}from"path";var START_TO_CONTENT={"redesign-screen":"redesign-screen","redesign-component":"component",custom:"custom",sample:"sample",blank:"blank",scratch:"sample",scan:"scan"};function isValidLibraryId(v){return LIBRARY_IDS.includes(v)}function isValidContent(v){return v==="sample"||v==="blank"||v==="scan"||v==="redesign-screen"||v==="component"||v==="custom"}function isValidStart(v){return v in START_TO_CONTENT}function shouldRunWizard(args,stdinIsTTY){if(args.nonInteractive)return!1;return stdinIsTTY}function answersFromArgs(args){if(args.library&&!isValidLibraryId(args.library))throw Error(`unknown --library ${JSON.stringify(args.library)}. Valid: ${LIBRARY_IDS.join(" | ")}.`);if(args.start&&!isValidStart(args.start))throw Error(`unknown --start ${JSON.stringify(args.start)}. Valid: redesign-screen | redesign-component | custom | sample | blank | scan.`);if(args.initialContent&&!isValidContent(args.initialContent))throw Error(`unknown --initial-content ${JSON.stringify(args.initialContent)}. Valid: sample | blank | scan | redesign-screen | component | custom.`);let themePreset=args.themePreset?.trim()||void 0;if(themePreset&&!isValidPreset(themePreset))throw Error(`unknown --theme-preset ${JSON.stringify(args.themePreset)}.`);let stack=args.stack?.trim()||void 0;if(stack&&!isValidStack(stack))throw Error(`unknown --stack ${JSON.stringify(args.stack)}. Valid: nextjs | vite | astro | remix.`);let appRoot=resolve3(args.folder??"."),folder=resolve3(appRoot,args.designFolder??"velloo"),library=args.library??DEFAULT_LIBRARY_ID,initialContent=args.initialContent??"sample";if(args.start)initialContent=START_TO_CONTENT[args.start]??initialContent;let screenName=args.screenName?.trim()||void 0,componentDescription=args.component?.trim()||void 0,customRequest=args.request?.trim()||void 0;if(initialContent==="component"&&!componentDescription)throw Error("--component is required for --start=redesign-component.");if(initialContent==="custom"&&!customRequest)throw Error("--request is required for --start=custom.");if(initialContent==="redesign-screen"&&!screenName&&args.start==="redesign-screen");let source=WIZARD_PROVIDERS[library].defaultSource;return{appRoot,scanRoot:appRoot,folder,library,source,componentsRelative:args.componentsDir??"src/components/ui",initialContent,themePreset,...stack?{stack}:{},...screenName?{screenName}:{},...componentDescription?{componentDescription}:{},...customRequest?{customRequest}:{}}}import{readFile as readFile3,rm as rm2,writeFile}from"fs/promises";import{tmpdir}from"os";import{join as join3}from"path";var import_picocolors2=__toESM(require_picocolors(),1);import{relative as relative3,sep as sep3}from"path";var SCREENS_PLACEHOLDER="{{screens}}";function libraryLabel(answers){let unsupported=answers.detected?.unsupportedUi;if(answers.library==="none"&&unsupported)return`own ${unsupported}`;return WIZARD_PROVIDERS[answers.library].handoffComponentsLabel}function appContextLines(answers){let lines=[],appRels=[...new Set((answers.selectedRoutes??[]).map((r)=>r.appRel).filter(Boolean))];if(appRels.length>1)lines.push(`This is a monorepo with ${appRels.length} apps (${appRels.map((r)=>`\`${r}\``).join(", ")}); each app's screens sit on their own board. Run the matching app's dev server when comparing screens.`);else{let uiRel=relative3(answers.appRoot,answers.scanRoot).split(sep3).join("/");if(uiRel&&!uiRel.startsWith(".."))lines.push(`This app's UI lives in \`${uiRel}/\` \u2014 run its dev server from there.`)}return lines}function setupFirstGuidance(){return"**Calibrate before you design** \u2014 run the **velloo-setup** skill first. My app's own components render on the canvas once their preview entry is set up (`preview_status`, then `set_preview_entry`); everything else is drawn with Velloo's components themed by this folder's tokens, so an uncalibrated board won't look like my app. Set up the preview entry, import my stylesheet, set the real fonts, and get the app running so there's something real to check against. Use my components from list_components' Repo shelves rather than rebuilding them from primitives. Tell me in a line what you matched and what stayed unverified."}function compareGuidance(){return"Compare against the real app, not memory: iterate with `screenshot` + `compare_to_url` at the same viewport and fix the `topMismatches` in the order they're ranked. If a capture comes back `unverified`, the similarity number means nothing \u2014 and if the reason is a login wall, call `start_capture_session` so I can sign in and capture the page for you, then verify against it with `compare_to_url { captureId }`."}function captureSiteGuidance(url){return[`**Start by capturing the site.** Call \`start_capture_session { url: "${url}" }\` \u2014 it opens a browser window I drive. I'll log in if needed and hit "Capture page" on each page worth designing from; the call returns straight away, so poll \`list_captures\` until my captures appear.`,"Then read each one with `get_capture`: it gives you a structural outline (repeated blocks are marked \u2014 those are your component candidates), the site's CSS custom properties as `import_theme`-ready CSS, and its images. Run `import_theme` with that CSS before composing so the real tokens resolve.","Re-express the pages with real components against the theme \u2014 do not transcribe the DOM node-for-node. Verify each screen with `compare_to_url { captureId }`."].join(`
|
|
2
|
+
import{isCancel,confirm,log,cancel,note,spinner,select,text}from"./chunk-80fjfyha.js";import{designNameIssue,CURRENT_SCHEMA_VERSION,schemaVersionOf,APP_PATH_PREFIX,planMigration,ConfigSchema}from"./chunk-nte0t7v3.js";import{writeJsonAtomic,managedDesignPath,managedDesignId,localDesignOf,writeLocalDesign,removeLocalDesign,resolveAppPath,recordedDesignName,pickDesign,readRepoFeedback,writeRepoFeedback,writeFeedbackContactOk,loadDesignFolder,hostAppRootFrom,topUpTokens}from"./chunk-egccxmed.js";import{CHROMIUM_INSTALL_CMD,chromiumExecutable}from"./chunk-c4fym47a.js";import{PROJECT_AGENT_IDS,resolveProjectRoot,manualSetupText,askAgentWiring,globallyWiredAgents,connect}from"./chunk-3kqtny5t.js";import{TOOL_VERSION}from"./chunk-q4d60wr3.js";import{fail}from"./chunk-s23gtejf.js";import{daemonRoot,ensureDaemon,stopDaemon}from"./chunk-1tdh4h14.js";import{installedVellooBin,updateStatus,upgradeInstalledVelloo}from"./chunk-tkvb3wb8.js";import{dirExists,discoverScanRoots,scanApps,primaryApp,buildScreensFromScan,buildBoardsFromScan,DEFAULT_THEME_PRESET,isValidPreset,WIZARD_PROVIDERS,LIBRARY_IDS,DEFAULT_LIBRARY_ID,interactiveLibraryChoices,planInstall,scanAdoption,DEFAULT_STACK_ID,stackForFramework,isValidStack,importThemeFromMui,findMuiTheme,importThemeFromGlobals,isEmptyOrMissing,resolveTheme,buildScaffold,writeScaffold,displayPath,printSummary,printExitInstructions,printScreenshotReadiness,promptShellCompletions}from"./chunk-v0syg2hx.js";import{loadCredential,saveCredential,verifyCredential,performDeviceLogin}from"./chunk-njhdn9j0.js";import{installChromiumInteractive}from"./chunk-2fgfex9b.js";import{detectHost,findComponentsDir}from"./chunk-38ev3cp3.js";import{copyToClipboard}from"./chunk-vbk1qhdz.js";import{upgradeFolder}from"./chunk-8sfx5x8k.js";import{findManifest,manifestListing,findDesigns,withoutDesign,isWithin,findGitRoot,chooseDesignName,registerDesign,registerLocalDesign,existingDesignFolder,isDesignFolderSync,isEmptyOrMissingSync,hasDesignConfig,resolveDesign}from"./chunk-46xykc71.js";import{openUrl}from"./chunk-hggap8fq.js";import{defaultCloudUrl}from"./chunk-25j8vgpa.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{randomUUID as randomUUID2}from"crypto";import{dirname as dirname2,relative as relative5,resolve as resolve7}from"path";var import_picocolors11=__toESM(require_picocolors(),1);import{existsSync}from"fs";import{readFile}from"fs/promises";import{join,relative,resolve,sep}from"path";function isSentinel(path){return path==="binary"||path.startsWith("~/")}function toPosix(path){return path.split(sep).join("/")||"."}function isUnder(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!/^(?:[\\/]|[A-Za-z]:)/.test(rel)}async function readConfig(folder){return ConfigSchema.parse(JSON.parse(await readFile(join(folder,".design","config.json"),"utf8")))}async function recordedAppRoot(designFolder){let folder=resolve(designFolder),local=localDesignOf(folder);if(local){let path=resolve(local.appRoot);return{path,kind:"local",localId:local.id,checkout:local.root,...appShape(path)}}let recorded=(await readConfig(folder)).hostApp?.root,path=recorded&&!isSentinel(recorded)?resolveAppPath(folder,recorded):resolve(folder,".."),found=await manifestListing(folder).catch(()=>null),designName=recordedDesignName(folder);return{path,kind:"in-repo",...designName?{designName}:{},...found?{manifestPath:found.path}:{},...appShape(path)}}function appShape(path){let exists=existsSync(path);return{exists,looksLikeApp:exists&&existsSync(join(path,"package.json"))}}async function planAppRootChange(designFolder,newRoot){let folder=resolve(designFolder),current=await recordedAppRoot(folder),to=resolve(newRoot),change={from:current.path,to,rewritten:[]};if(current.kind==="local"&¤t.localId&¤t.checkout){if(!isUnder(current.checkout,to))throw Error(`An application root must live inside the design's checkout (${current.checkout}). ${to} is outside it.`);return change.local={id:current.localId,root:current.checkout},change}let config=await readConfig(folder),reanchor=(field,stored)=>{if(isSentinel(stored))return;let abs=resolveAppPath(folder,stored);if(!isUnder(current.path,abs))return;let next=toPosix(relative(folder,join(to,relative(current.path,abs))));if(next===stored)return;return change.rewritten.push({field,from:stored,to:next}),next};if(config.hostApp)reanchor("hostApp.root",config.hostApp.root);else change.rewritten.push({field:"hostApp.root",from:"(unset)",to:toPosix(relative(folder,to))});for(let[name,app]of Object.entries(config.hostApps??{}))reanchor(`hostApps.${name}.root`,app.root);for(let[name,library]of Object.entries(config.libraries))if(library.source==="in-repo")reanchor(`libraries.${name}.componentsPath`,library.componentsPath);return change}async function applyAppRootChange(designFolder,change){let folder=resolve(designFolder);if(change.local){await writeLocalDesign(change.local.id,{root:change.local.root,appRoot:change.to,...managedDesignId(folder)?{}:{designPath:folder}});return}let config=await readConfig(folder),patch=new Map(change.rewritten.map((r)=>[r.field,r.to])),hostRoot=patch.get("hostApp.root");if(hostRoot!==void 0)config.hostApp=config.hostApp?{...config.hostApp,root:hostRoot}:{root:hostRoot};for(let[name,app]of Object.entries(config.hostApps??{})){let next=patch.get(`hostApps.${name}.root`);if(next!==void 0)app.root=next}for(let[name,library]of Object.entries(config.libraries)){let next=patch.get(`libraries.${name}.componentsPath`);if(next!==void 0)library.componentsPath=next}await writeJsonAtomic(join(folder,".design","config.json"),ConfigSchema.parse(config))}async function appRootCandidates(near){let start=resolve(near),from=(await findManifest(start).catch(()=>null))?.dir??await findGitRoot(start)??start;return(await discoverScanRoots(from)).map((app)=>({dir:app.dir,rel:relative(from,app.dir)||"."}))}async function appRootIsNotAnApp(appRoot){let abs=resolve(appRoot);if(existsSync(join(abs,"package.json"))&&(await discoverScanRoots(abs))[0]?.dir===abs)return null;let candidates=(await appRootCandidates(abs)).filter((c)=>c.dir!==abs);return candidates.length>0?candidates:null}async function promptAppRootChoice(current,opts={}){let message=opts.message??"Which application is this design for?",currentHint=opts.currentHint??"recorded now",abs=resolve(current),candidates=await appRootCandidates(abs),options=candidates.map((c)=>({value:c.dir,label:c.rel,...c.dir===abs?{hint:currentHint}:{}}));if(!candidates.some((c)=>c.dir===abs))options.push({value:abs,label:current,hint:currentHint});if(options.length<=1)return null;let chosen=await select({message,options});if(isCancel(chosen))return null;return chosen}import{createHash,randomUUID}from"crypto";import{existsSync as existsSync2,lstatSync,realpathSync}from"fs";import{cp,lstat,mkdir,readdir,readFile as readFile2,rm}from"fs/promises";import{basename,dirname,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";function physicalDestination(path){if(lstatSync(path,{throwIfNoEntry:!1}))return realpathSync(path);let parent=dirname(path);return join2(physicalDestination(parent),relative2(parent,path))}var RUNTIME=[".design/cache",".velloo","node_modules"];async function rebaseDesignConfig(source,destination,appRoot,external){let config=ConfigSchema.parse(JSON.parse(await readFile2(join2(destination,".design/config.json"),"utf8"))),rebase=(path)=>{if(path==="binary"||path.startsWith("~/"))return path;let abs=resolveAppPath(source,path),withinDesign=relative2(source,abs);if(withinDesign===""||withinDesign!==".."&&!withinDesign.startsWith(`..${sep2}`)&&!/^(?:[\\/]|[A-Za-z]:)/.test(withinDesign))return withinDesign.split(sep2).join("/")||".";return external?`${APP_PATH_PREFIX}${relative2(appRoot,abs).split(sep2).join("/")||"."}`:relative2(destination,abs).split(sep2).join("/")||"."};config.hostApp=config.hostApp?{...config.hostApp,root:rebase(config.hostApp.root)}:{root:rebase("..")};for(let app of Object.values(config.hostApps??{}))app.root=rebase(app.root);for(let library of Object.values(config.libraries))if(library.componentsPath)library.componentsPath=rebase(library.componentsPath);await writeJsonAtomic(join2(destination,".design/config.json"),config)}async function fingerprint(folder){let hash=createHash("sha256");async function walk(rel){if(rel===".git"||RUNTIME.some((p)=>rel===p||rel.startsWith(`${p}/`)))return;let path=join2(folder,rel),stat=await lstat(path);if(stat.isSymbolicLink())throw Error(`Cannot relocate symbolic link ${path}. Replace it with its actual design content first.`);if(stat.isDirectory())for(let name of(await readdir(path)).sort())await walk(rel?`${rel}/${name}`:name);else if(stat.isFile())hash.update(rel).update("\x00").update(await readFile2(path));else throw Error(`Unsupported filesystem entry: ${path}`)}return await walk(""),hash.digest("hex")}async function planRelocation(source,cwd,to,external){if(Boolean(to)===external)throw Error("Choose exactly one destination: --external or --to <path>.");let designs=await findDesigns(cwd),physicalSource=realpathSync(source),local=designs?.local.find((design)=>realOr(design.designRoot)===physicalSource),repo=designs?.repo??null,index=repo?.folders.findIndex((path)=>existsSync2(path)&&realpathSync(path)===physicalSource)??-1;if(!local&&index===-1)throw Error("Run relocation from the checkout the design belongs to; the source must be one of its designs.");let root=local?.root??repo?.dir,name=recordedDesignName(source);if(!name)throw Error(`${source} has no design name \u2014 run \`velloo upgrade\` first.`);let physicalRoot=realpathSync(root);if(physicalSource===physicalRoot)throw Error("Cannot relocate the application repository itself. Use a separate design directory.");if(external&&managedDesignId(source))throw Error("This design already lives in managed storage.");let destination=external?managedDesignPath(local?.id??randomUUID()):resolve2(cwd,to??""),physicalTarget=physicalDestination(destination),inRepo=!external&&isWithin(physicalRoot,physicalTarget);if(physicalTarget===physicalSource||physicalTarget.startsWith(physicalSource+sep2)||physicalSource.startsWith(physicalTarget+sep2))throw Error("Source and destination cannot contain each other.");if(existsSync2(destination))throw Error(`Destination is occupied: ${destination}. Choose a new path; nothing was changed.`);let manifestPath=repo?.path??join2(root,"velloo.json"),before=repo?await readFile2(repo.path,"utf8"):null,manifest,destinationManifestPath;if(repo?.legacy)throw Error(`${repo.path} is in the pre-designs format \u2014 run \`velloo upgrade\` first.`);if(inRepo){destinationManifestPath=relative2(physicalRoot,physicalTarget).split(sep2).join("/")||".";let designs=[...repo?.manifest.designs??[]];if(index===-1)designs.push(destinationManifestPath);else designs[index]=destinationManifestPath;manifest={...repo?.manifest??{},designs}}else if(repo&&index!==-1)manifest=withoutDesign(repo.manifest,index,name);let config=ConfigSchema.parse(JSON.parse(await readFile2(join2(source,".design/config.json"),"utf8")));return{source,destination,root,appRoot:local?.appRoot??hostAppRootFrom(source,config.hostApp),designName:name,sourceLocalId:local?.id,sourceManifestPath:index===-1?void 0:repo?.manifest.designs[index],destinationManifestPath,destinationLocal:inRepo?void 0:{id:local?.id??(external?basename(destination):randomUUID()),...external?{}:{designPath:destination}},manifestPath,before,manifest}}function realOr(path){return existsSync2(path)?realpathSync(path):resolve2(path)}async function relocateDesign(plan){await stopDaemon(daemonRoot(plan.source));let original=await fingerprint(plan.source);await mkdir(dirname(plan.destination),{recursive:!0}),await mkdir(plan.destination);let committed=!1;try{for(let entry of await readdir(plan.source))await cp(join2(plan.source,entry),join2(plan.destination,entry),{recursive:!0,errorOnExist:!0,force:!1,filter:(path)=>!RUNTIME.some((p)=>{let rel=relative2(plan.source,path).split(sep2).join("/");return rel===p||rel.startsWith(`${p}/`)})});if(await fingerprint(plan.destination)!==original)throw Error("Copied content did not match the source.");if(await rebaseDesignConfig(plan.source,plan.destination,plan.appRoot,plan.destinationLocal!==void 0),await loadDesignFolder(plan.destination,{preferences:!1}),await fingerprint(plan.source)!==original)throw Error("The source changed during relocation. Close editors and retry.");if(await readFile2(plan.manifestPath,"utf8").catch(()=>null)!==plan.before)throw Error("The manifest changed during relocation. Review it and retry.");if(plan.destinationLocal){if(plan.sourceLocalId&&plan.sourceLocalId!==plan.destinationLocal.id)await removeLocalDesign(plan.sourceLocalId);await writeLocalDesign(plan.destinationLocal.id,{root:plan.root,appRoot:plan.appRoot,...plan.destinationLocal.designPath?{designPath:plan.destinationLocal.designPath}:{}})}else if(plan.sourceLocalId)await removeLocalDesign(plan.sourceLocalId);if(plan.manifest===null)await rm(plan.manifestPath,{force:!0});else if(plan.manifest)await writeJsonAtomic(plan.manifestPath,plan.manifest);committed=!0}finally{if(!committed)await rm(plan.destination,{recursive:!0,force:!0})}try{await rm(plan.source,{recursive:!0})}catch(error){throw Error(`Relocation completed to ${plan.destination}, but the old copy remains at ${plan.source}. Remove it after checking the destination. ${error}`)}}var import_picocolors=__toESM(require_picocolors(),1);async function offerUpgradeBeforeInit(argv){if(process.env.VELLOO_DISABLE_UPDATE_CHECK==="1"||!process.stdin.isTTY)return!1;let status;try{status=await updateStatus({refresh:!0})}catch{return!1}if(!status.available||!status.upgradable||!status.latest)return!1;console.log(""),console.log(`${import_picocolors.default.yellow("A newer velloo is available:")} ${status.current} \u2192 ${import_picocolors.default.bold(status.latest)}`);let yes=await confirm({message:"Upgrade before setting up this design folder?",initialValue:!0});if(isCancel(yes)||!yes)return console.log(import_picocolors.default.dim(" Continuing on the current version.")),!1;try{if(!(await upgradeInstalledVelloo()).upgraded)return!1;let bin=installedVellooBin();if(!bin)return console.log(import_picocolors.default.dim(" Upgraded. Run `velloo init` again to continue on the new version.")),!0;let code=await Bun.spawn([bin,...argv],{stdin:"inherit",stdout:"inherit",stderr:"inherit",env:{...process.env,VELLOO_DISABLE_UPDATE_CHECK:"1"}}).exited;process.exit(code)}catch(error){return console.log(import_picocolors.default.yellow(` Could not upgrade (${error instanceof Error?error.message:String(error)}) \u2014 continuing on the current version.`)),!1}}import{resolve as resolve3}from"path";var START_TO_CONTENT={"redesign-screen":"redesign-screen","redesign-component":"component",custom:"custom",sample:"sample",blank:"blank",scratch:"sample",scan:"scan"};function isValidLibraryId(v){return LIBRARY_IDS.includes(v)}function isValidContent(v){return v==="sample"||v==="blank"||v==="scan"||v==="redesign-screen"||v==="component"||v==="custom"}function isValidStart(v){return v in START_TO_CONTENT}function shouldRunWizard(args,stdinIsTTY){if(args.nonInteractive)return!1;return stdinIsTTY}function answersFromArgs(args){if(args.library&&!isValidLibraryId(args.library))throw Error(`unknown --library ${JSON.stringify(args.library)}. Valid: ${LIBRARY_IDS.join(" | ")}.`);if(args.start&&!isValidStart(args.start))throw Error(`unknown --start ${JSON.stringify(args.start)}. Valid: redesign-screen | redesign-component | custom | sample | blank | scan.`);if(args.initialContent&&!isValidContent(args.initialContent))throw Error(`unknown --initial-content ${JSON.stringify(args.initialContent)}. Valid: sample | blank | scan | redesign-screen | component | custom.`);let themePreset=args.themePreset?.trim()||void 0;if(themePreset&&!isValidPreset(themePreset))throw Error(`unknown --theme-preset ${JSON.stringify(args.themePreset)}.`);let stack=args.stack?.trim()||void 0;if(stack&&!isValidStack(stack))throw Error(`unknown --stack ${JSON.stringify(args.stack)}. Valid: nextjs | vite | astro | remix.`);let appRoot=resolve3(args.folder??"."),folder=resolve3(appRoot,args.designFolder??"velloo"),library=args.library??DEFAULT_LIBRARY_ID,initialContent=args.initialContent??"sample";if(args.start)initialContent=START_TO_CONTENT[args.start]??initialContent;let screenName=args.screenName?.trim()||void 0,componentDescription=args.component?.trim()||void 0,customRequest=args.request?.trim()||void 0;if(initialContent==="component"&&!componentDescription)throw Error("--component is required for --start=redesign-component.");if(initialContent==="custom"&&!customRequest)throw Error("--request is required for --start=custom.");if(initialContent==="redesign-screen"&&!screenName&&args.start==="redesign-screen");let source=WIZARD_PROVIDERS[library].defaultSource;return{appRoot,scanRoot:appRoot,folder,library,source,componentsRelative:args.componentsDir??"src/components/ui",initialContent,themePreset,...stack?{stack}:{},...screenName?{screenName}:{},...componentDescription?{componentDescription}:{},...customRequest?{customRequest}:{}}}import{readFile as readFile3,rm as rm2,writeFile}from"fs/promises";import{tmpdir}from"os";import{join as join3}from"path";var import_picocolors2=__toESM(require_picocolors(),1);import{relative as relative3,sep as sep3}from"path";var SCREENS_PLACEHOLDER="{{screens}}";function libraryLabel(answers){let unsupported=answers.detected?.unsupportedUi;if(answers.library==="none"&&unsupported)return`own ${unsupported}`;return WIZARD_PROVIDERS[answers.library].handoffComponentsLabel}function appContextLines(answers){let lines=[],appRels=[...new Set((answers.selectedRoutes??[]).map((r)=>r.appRel).filter(Boolean))];if(appRels.length>1)lines.push(`This is a monorepo with ${appRels.length} apps (${appRels.map((r)=>`\`${r}\``).join(", ")}); each app's screens sit on their own board. Run the matching app's dev server when comparing screens.`);else{let uiRel=relative3(answers.appRoot,answers.scanRoot).split(sep3).join("/");if(uiRel&&!uiRel.startsWith(".."))lines.push(`This app's UI lives in \`${uiRel}/\` \u2014 run its dev server from there.`)}return lines}function setupFirstGuidance(){return"**Calibrate before you design** \u2014 run the **velloo-setup** skill first. My app's own components render on the canvas once their preview entry is set up (`preview_status`, then `set_preview_entry`); everything else is drawn with Velloo's components themed by this folder's tokens, so an uncalibrated board won't look like my app. Set up the preview entry, import my stylesheet, set the real fonts, and get the app running so there's something real to check against. Use my components from list_components' Repo shelves rather than rebuilding them from primitives. Tell me in a line what you matched and what stayed unverified."}function compareGuidance(){return"Compare against the real app, not memory: iterate with `screenshot` + `compare_to_url` at the same viewport and fix the `topMismatches` in the order they're ranked. If a capture comes back `unverified`, the similarity number means nothing \u2014 and if the reason is a login wall, call `start_capture_session` so I can sign in and capture the page for you, then verify against it with `compare_to_url { captureId }`."}function captureSiteGuidance(url){return[`**Start by capturing the site.** Call \`start_capture_session { url: "${url}" }\` \u2014 it opens a browser window I drive. I'll log in if needed and hit "Capture page" on each page worth designing from; the call returns straight away, so poll \`list_captures\` until my captures appear.`,"Then read each one with `get_capture`: it gives you a structural outline (repeated blocks are marked \u2014 those are your component candidates), the site's CSS custom properties as `import_theme`-ready CSS, and its images. Run `import_theme` with that CSS before composing so the real tokens resolve.","Re-express the pages with real components against the theme \u2014 do not transcribe the DOM node-for-node. Verify each screen with `compare_to_url { captureId }`."].join(`
|
|
3
3
|
`)}function exploreAlternativesGuidance(){return"After the faithful recreation, explore alternatives on the same board \u2014 side-by-side A/B/C (or more) frames or grouped explorations of different directions. Velloo's strength is comparing options visually; do not stop at a single redesign."}function wantsHandoff(answers){switch(answers.initialContent){case"scan":case"redesign-screen":case"component":case"custom":return!0;default:return!1}}function buildHandoffPrompt(answers,screens,boards){switch(answers.initialContent){case"redesign-screen":return buildRedesignScreenHandoff(answers,screens);case"component":return buildComponentHandoff(answers);case"custom":return buildCustomHandoff(answers);case"scan":return buildLegacyScanHandoff(answers,screens,boards);default:return buildLegacyScanHandoff(answers,screens,boards)}}function buildRedesignScreenHandoff(answers,screens){let name=answers.screenName?.trim()||screens[0]?.name||screens[0]?.id||"the chosen screen",lines=[`Recreate and then explore alternatives for **${name}** as a Velloo design. Work entirely through the velloo MCP tools.`,...appContextLines(answers)];if(screens.length>0)lines.push(`A placeholder screen is already scaffolded (desktop + mobile frames \u2014 edits sync). Design it with the project's ${libraryLabel(answers)} components:`,SCREENS_PLACEHOLDER);else lines.push(`- Create a Velloo screen for **${name}** using the project's ${libraryLabel(answers)} components.`);return lines.push(`1. ${setupFirstGuidance()}`,`2. **Recreate** the real page faithfully. ${compareGuidance()}`,`3. **Then explore alternatives** \u2014 ${exploreAlternativesGuidance()}`),lines.join(`
|
|
4
4
|
`)}function buildComponentHandoff(answers){let target=answers.componentDescription?.trim()||"the component";return[`Redesign **${target}** in Velloo \u2014 recreate it, then explore alternatives. Work entirely through the velloo MCP tools.`,...appContextLines(answers),`A component-focused board is scaffolded. Target: **${target}**. Prefer a snippet if the piece should be reused.`,`1. ${setupFirstGuidance()}`,`2. **Recreate** the current component faithfully with the project's ${libraryLabel(answers)} components. ${compareGuidance()}`,`3. **Then explore alternatives** \u2014 ${exploreAlternativesGuidance()}`].join(`
|
|
5
5
|
`)}function buildCustomHandoff(answers){return["Design this in Velloo. Work entirely through the velloo MCP tools.","","**Request:**",answers.customRequest?.trim()||"the design I described","",...answers.captureUrl?[captureSiteGuidance(answers.captureUrl),""]:[],setupFirstGuidance(),`Then design it with the project's ${libraryLabel(answers)} components. When comparing options, put alternatives side-by-side on the board (explorations) instead of overwriting a single direction.`].join(`
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{clsx,helperDescriptors,require_react_jsx_dev_runtime_development,helpersRegistry,TAILWIND_CLASSNAME,helperSourcePath}from"./chunk-5ezzc1h2.js";import{headingInlineStyle,textInlineStyle}from"./chunk-nte0t7v3.js";import{resolveProviderSrcDir}from"./chunk-fjbzk4sw.js";import{require_react_development}from"./chunk-c4fym47a.js";import{__toESM}from"./chunk-2smsg2ct.js";import{noLibVersion}from"./chunk-cxe6jjwj.js";import{dirname,join}from"path";import{fileURLToPath}from"url";var NONE_INTRO=["**This folder has no component library** \u2014 only bare primitives wrapping plain HTML: `Box`/`Stack`/`Container` for layout, `Card`, `Button`, `Input`, plus the velloo helpers (`Heading`/`Text`, `Image`, `Gradient`, `Layer`, `SVG`, `Divider`, `Placeholder`, `Icon`). The primitives stop there: for a `Badge`/`Tabs`/`Dialog`, use the app's own component when `list_components` shows one on a Repo shelf, else build it from primitives or a snippet. Call `list_components` for the exact set. Styling is **Tailwind classes**.",""],NONE_INLINE_INTRO=["**This folder has no component library and no CSS framework** \u2014 bare primitives wrapping plain HTML (`Box`/`Stack`/`Container`, `Card`, `Button`, `Input`) plus the velloo helpers (`Heading`/`Text`, `Image`, `Icon`, `SVG`, `Divider`, `Gradient`, `Layer`, `Placeholder`). The primitives stop there: for a `Badge`/`Tabs`/`Dialog`, use the app's own component when `list_components` shows one on a Repo shelf, else build it from primitives or a snippet. Call `list_components` for the exact set.","",'**Style through inline `style` objects.** `update_props { style: { display: "flex", gap: "16px", padding: "24px", borderRadius: "8px", color: "var(--color-foreground)" } }` \u2014 a plain React style object (merges shallowly; an inner `null` drops a key, `style: null` clears). Reference theme tokens as CSS variables (`var(--color-primary)`, `var(--radius)`) from `get_theme` so the design stays themable. `emit_code` emits `style={{\u2026}}` on plain elements \u2014 no imports, no Tailwind. Tailwind utility diagnostics do not apply.',""];var React=__toESM(require_react_development(),1),jsx_dev_runtime=__toESM(require_react_jsx_dev_runtime_development(),1),Box=React.forwardRef(({className,...rest},ref)=>jsx_dev_runtime.jsxDEV("div",{ref,className:clsx(className),...rest},void 0,!1,void 0,this));Box.displayName="Box";var alignClass={start:"items-start",center:"items-center",end:"items-end",stretch:"items-stretch"},justifyClass={start:"justify-start",center:"justify-center",end:"justify-end",between:"justify-between",around:"justify-around"},Stack=React.forwardRef(({direction="col",gap=4,align,justify,className,...rest},ref)=>jsx_dev_runtime.jsxDEV("div",{ref,className:clsx("flex",direction==="row"?"flex-row":"flex-col",`gap-${gap}`,align?alignClass[align]:void 0,justify?justifyClass[justify]:void 0,className),...rest},void 0,!1,void 0,this));Stack.displayName="Stack";var containerWidth={sm:"max-w-screen-sm",md:"max-w-screen-md",lg:"max-w-screen-lg",xl:"max-w-screen-xl",full:"max-w-full"},Container=React.forwardRef(({size="md",className,...rest},ref)=>jsx_dev_runtime.jsxDEV("div",{ref,className:clsx("mx-auto w-full px-4",containerWidth[size],className),...rest},void 0,!1,void 0,this));Container.displayName="Container";var buttonVariant={default:"bg-foreground text-background hover:bg-foreground/90",ghost:"bg-transparent text-foreground hover:bg-muted",outline:"bg-transparent text-foreground border border-border hover:bg-muted"},Button=React.forwardRef(({variant="default",className,type,...rest},ref)=>jsx_dev_runtime.jsxDEV("button",{ref,type:type??"button",className:clsx("inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium","focus-visible:outline-2 focus-visible:outline-offset-2",buttonVariant[variant],className),...rest},void 0,!1,void 0,this));Button.displayName="Button";var Input=React.forwardRef(({className,type="text",value,onChange,...rest},ref)=>jsx_dev_runtime.jsxDEV("input",{ref,type,value,onChange,readOnly:value!==void 0&&onChange===void 0||rest.readOnly,className:clsx("block w-full rounded-md border border-input bg-background px-3 py-2 text-sm","placeholder:text-muted-foreground focus-visible:outline-2 focus-visible:outline-offset-2",className),...rest},void 0,!1,void 0,this));Input.displayName="Input";var Card=React.forwardRef(({className,...rest},ref)=>jsx_dev_runtime.jsxDEV("div",{ref,className:clsx("rounded-lg border border-border bg-card p-6 text-card-foreground shadow-sm",className),...rest},void 0,!1,void 0,this));Card.displayName="Card";var REUSED_HELPER_IDS=["Heading","Text","Prose","Icon","SVG","Image","Layer","Divider","Gradient","Placeholder"],registry={Box,Stack,Container,Card,Button,Input,...helpersRegistry(REUSED_HELPER_IDS)};var PRIMITIVE_DESCRIPTORS=[{id:"Box",category:"ui",source:"velloo",props:[{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Stack",category:"ui",source:"velloo",props:[{name:"direction",type:'"row" | "col" | undefined',optional:!0,control:"enum",enumValues:["row","col"],defaultValue:"col"},{name:"gap",type:"number | undefined",optional:!0,control:"number",defaultValue:"4"},{name:"align",type:'"start" | "center" | "end" | "stretch" | undefined',optional:!0,control:"enum",enumValues:["start","center","end","stretch"]},{name:"justify",type:'"start" | "center" | "end" | "between" | "around" | undefined',optional:!0,control:"enum",enumValues:["start","center","end","between","around"]},{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Container",category:"ui",source:"velloo",props:[{name:"size",type:'"sm" | "md" | "lg" | "xl" | "full" | undefined',optional:!0,control:"enum",enumValues:["sm","md","lg","xl","full"],defaultValue:"md"},{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Card",category:"ui",source:"velloo",props:[{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Button",category:"ui",source:"velloo",props:[{name:"variant",type:'"default" | "ghost" | "outline" | undefined',optional:!0,control:"enum",enumValues:["default","ghost","outline"],defaultValue:"default"},{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Input",category:"ui",source:"velloo",props:[{name:"placeholder",type:"string | undefined",optional:!0,control:"string"},{name:"value",type:"string | undefined",optional:!0,control:"string"},{name:"type",type:"string | undefined",optional:!0,control:"string",defaultValue:"text"},{name:"className",type:"string | undefined",optional:!0,control:"string"}]}],NONE_MANIFEST=[...PRIMITIVE_DESCRIPTORS,...helperDescriptors(REUSED_HELPER_IDS)];var React2=__toESM(require_react_development(),1),jsx_dev_runtime2=__toESM(require_react_jsx_dev_runtime_development(),1);function merge(defaults,authored){return authored?{...defaults,...authored}:defaults}var space=(n)=>`${n*0.25}rem`,Box2=React2.forwardRef(({style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("div",{ref,style,...rest},void 0,!1,void 0,this));Box2.displayName="Box";var alignStyle={start:"flex-start",center:"center",end:"flex-end",stretch:"stretch"},justifyStyle={start:"flex-start",center:"center",end:"flex-end",between:"space-between",around:"space-around"},Stack2=React2.forwardRef(({direction="col",gap=4,align,justify,style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("div",{ref,style:merge({display:"flex",flexDirection:direction==="row"?"row":"column",gap:space(gap),...align?{alignItems:alignStyle[align]}:{},...justify?{justifyContent:justifyStyle[justify]}:{}},style),...rest},void 0,!1,void 0,this));Stack2.displayName="Stack";var containerMaxWidth={sm:"640px",md:"768px",lg:"1024px",xl:"1280px",full:"100%"},Container2=React2.forwardRef(({size="md",style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("div",{ref,style:merge({marginInline:"auto",width:"100%",paddingInline:"1rem",maxWidth:containerMaxWidth[size]},style),...rest},void 0,!1,void 0,this));Container2.displayName="Container";var buttonVariantStyle={default:{background:"var(--color-foreground)",color:"var(--color-background)"},ghost:{background:"transparent",color:"var(--color-foreground)"},outline:{background:"transparent",color:"var(--color-foreground)",border:"1px solid var(--color-border)"}},Button2=React2.forwardRef(({variant="default",type,style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("button",{ref,type:type??"button",style:merge({display:"inline-flex",alignItems:"center",justifyContent:"center",gap:"0.5rem",borderRadius:"var(--radius)",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,cursor:"pointer",...buttonVariantStyle[variant]},style),...rest},void 0,!1,void 0,this));Button2.displayName="Button";var Input2=React2.forwardRef(({type="text",value,onChange,style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("input",{ref,type,value,onChange,readOnly:value!==void 0&&onChange===void 0||rest.readOnly,style:merge({display:"block",width:"100%",borderRadius:"var(--radius)",border:"1px solid var(--color-input)",background:"var(--color-background)",padding:"0.5rem 0.75rem",fontSize:"0.875rem"},style),...rest},void 0,!1,void 0,this));Input2.displayName="Input";var Card2=React2.forwardRef(({style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("div",{ref,style:merge({borderRadius:"var(--radius)",border:"1px solid var(--color-border)",background:"var(--color-card)",color:"var(--color-card-foreground)",padding:"1.5rem",boxShadow:"0 1px 2px 0 rgb(0 0 0 / 0.05)"},style),...rest},void 0,!1,void 0,this));Card2.displayName="Card";var Heading=React2.forwardRef(({level=1,style,...rest},ref)=>{let Tag=`h${level}`;return jsx_dev_runtime2.jsxDEV(Tag,{ref,style:merge(headingInlineStyle(level),style),...rest},void 0,!1,void 0,this)});Heading.displayName="Heading";var Text=React2.forwardRef(({variant="default",style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("p",{ref,style:merge(textInlineStyle(variant),style),...rest},void 0,!1,void 0,this));Text.displayName="Text";var REUSED_HELPER_IDS2=["Prose","Icon","SVG","Image","Layer","Divider","Gradient","Placeholder"],inlineRegistry={Box:Box2,Stack:Stack2,Container:Container2,Card:Card2,Button:Button2,Input:Input2,Heading,Text,...helpersRegistry(REUSED_HELPER_IDS2)};var PRIMITIVES=new Set(["Box","Stack","Container","Card","Button","Input"]),INLINE_TYPOGRAPHY=new Set(["Heading","Text"]),here=dirname(fileURLToPath(import.meta.url)),srcDir=resolveProviderSrcDir(here,"provider-none",process.env.VELLOO_NOLIB_SRC),entryCssPath=join(srcDir,"tailwind-entry.css"),componentsDir=srcDir;async function loadManifest(){return NONE_MANIFEST}function createProvider(){return{id:"none",version:noLibVersion,componentsDir,styleEntryPath:entryCssPath,registry,loadManifest,label:`none ${noLibVersion}`,styleChannel:TAILWIND_CLASSNAME,styleChannels:["tailwind-classname","style"],mcpIntro:(channel)=>[...channel==="style"?NONE_INLINE_INTRO:NONE_INTRO],registryForChannel:(kind)=>kind==="style"?inlineRegistry:registry,canvasBundleSpec:{styleRuntime:{kind:"none"},onlyWithRepository:!0,components:(ids,context)=>{let inline=context?.channel==="style";return ids.flatMap((id)=>{let own=PRIMITIVES.has(id)||inline&&INLINE_TYPOGRAPHY.has(id)?join(srcDir,inline?"components-inline.tsx":"components.tsx"):helperSourcePath(id);return own?[{id,sources:[{importPath:own,exportName:id,fidelity:"exact",note:"Velloo primitive, mounted beside the app's components."}]}]:[]})}}}}export{createProvider};
|
|
2
|
+
import{clsx,helperDescriptors,require_react_jsx_dev_runtime_development,helpersRegistry,TAILWIND_CLASSNAME,helperSourcePath}from"./chunk-egccxmed.js";import{headingInlineStyle,textInlineStyle}from"./chunk-nte0t7v3.js";import{resolveProviderSrcDir}from"./chunk-fjbzk4sw.js";import{require_react_development}from"./chunk-c4fym47a.js";import{__toESM}from"./chunk-2smsg2ct.js";import{noLibVersion}from"./chunk-cxe6jjwj.js";import{dirname,join}from"path";import{fileURLToPath}from"url";var NONE_INTRO=["**This folder has no component library** \u2014 only bare primitives wrapping plain HTML: `Box`/`Stack`/`Container` for layout, `Card`, `Button`, `Input`, plus the velloo helpers (`Heading`/`Text`, `Image`, `Gradient`, `Layer`, `SVG`, `Divider`, `Placeholder`, `Icon`). The primitives stop there: for a `Badge`/`Tabs`/`Dialog`, use the app's own component when `list_components` shows one on a Repo shelf, else build it from primitives or a snippet. Call `list_components` for the exact set. Styling is **Tailwind classes**.",""],NONE_INLINE_INTRO=["**This folder has no component library and no CSS framework** \u2014 bare primitives wrapping plain HTML (`Box`/`Stack`/`Container`, `Card`, `Button`, `Input`) plus the velloo helpers (`Heading`/`Text`, `Image`, `Icon`, `SVG`, `Divider`, `Gradient`, `Layer`, `Placeholder`). The primitives stop there: for a `Badge`/`Tabs`/`Dialog`, use the app's own component when `list_components` shows one on a Repo shelf, else build it from primitives or a snippet. Call `list_components` for the exact set.","",'**Style through inline `style` objects.** `update_props { style: { display: "flex", gap: "16px", padding: "24px", borderRadius: "8px", color: "var(--color-foreground)" } }` \u2014 a plain React style object (merges shallowly; an inner `null` drops a key, `style: null` clears). Reference theme tokens as CSS variables (`var(--color-primary)`, `var(--radius)`) from `get_theme` so the design stays themable. `emit_code` emits `style={{\u2026}}` on plain elements \u2014 no imports, no Tailwind. Tailwind utility diagnostics do not apply.',""];var React=__toESM(require_react_development(),1),jsx_dev_runtime=__toESM(require_react_jsx_dev_runtime_development(),1),Box=React.forwardRef(({className,...rest},ref)=>jsx_dev_runtime.jsxDEV("div",{ref,className:clsx(className),...rest},void 0,!1,void 0,this));Box.displayName="Box";var alignClass={start:"items-start",center:"items-center",end:"items-end",stretch:"items-stretch"},justifyClass={start:"justify-start",center:"justify-center",end:"justify-end",between:"justify-between",around:"justify-around"},Stack=React.forwardRef(({direction="col",gap=4,align,justify,className,...rest},ref)=>jsx_dev_runtime.jsxDEV("div",{ref,className:clsx("flex",direction==="row"?"flex-row":"flex-col",`gap-${gap}`,align?alignClass[align]:void 0,justify?justifyClass[justify]:void 0,className),...rest},void 0,!1,void 0,this));Stack.displayName="Stack";var containerWidth={sm:"max-w-screen-sm",md:"max-w-screen-md",lg:"max-w-screen-lg",xl:"max-w-screen-xl",full:"max-w-full"},Container=React.forwardRef(({size="md",className,...rest},ref)=>jsx_dev_runtime.jsxDEV("div",{ref,className:clsx("mx-auto w-full px-4",containerWidth[size],className),...rest},void 0,!1,void 0,this));Container.displayName="Container";var buttonVariant={default:"bg-foreground text-background hover:bg-foreground/90",ghost:"bg-transparent text-foreground hover:bg-muted",outline:"bg-transparent text-foreground border border-border hover:bg-muted"},Button=React.forwardRef(({variant="default",className,type,...rest},ref)=>jsx_dev_runtime.jsxDEV("button",{ref,type:type??"button",className:clsx("inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium","focus-visible:outline-2 focus-visible:outline-offset-2",buttonVariant[variant],className),...rest},void 0,!1,void 0,this));Button.displayName="Button";var Input=React.forwardRef(({className,type="text",value,onChange,...rest},ref)=>jsx_dev_runtime.jsxDEV("input",{ref,type,value,onChange,readOnly:value!==void 0&&onChange===void 0||rest.readOnly,className:clsx("block w-full rounded-md border border-input bg-background px-3 py-2 text-sm","placeholder:text-muted-foreground focus-visible:outline-2 focus-visible:outline-offset-2",className),...rest},void 0,!1,void 0,this));Input.displayName="Input";var Card=React.forwardRef(({className,...rest},ref)=>jsx_dev_runtime.jsxDEV("div",{ref,className:clsx("rounded-lg border border-border bg-card p-6 text-card-foreground shadow-sm",className),...rest},void 0,!1,void 0,this));Card.displayName="Card";var REUSED_HELPER_IDS=["Heading","Text","Prose","Icon","SVG","Image","Layer","Divider","Gradient","Placeholder"],registry={Box,Stack,Container,Card,Button,Input,...helpersRegistry(REUSED_HELPER_IDS)};var PRIMITIVE_DESCRIPTORS=[{id:"Box",category:"ui",source:"velloo",props:[{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Stack",category:"ui",source:"velloo",props:[{name:"direction",type:'"row" | "col" | undefined',optional:!0,control:"enum",enumValues:["row","col"],defaultValue:"col"},{name:"gap",type:"number | undefined",optional:!0,control:"number",defaultValue:"4"},{name:"align",type:'"start" | "center" | "end" | "stretch" | undefined',optional:!0,control:"enum",enumValues:["start","center","end","stretch"]},{name:"justify",type:'"start" | "center" | "end" | "between" | "around" | undefined',optional:!0,control:"enum",enumValues:["start","center","end","between","around"]},{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Container",category:"ui",source:"velloo",props:[{name:"size",type:'"sm" | "md" | "lg" | "xl" | "full" | undefined',optional:!0,control:"enum",enumValues:["sm","md","lg","xl","full"],defaultValue:"md"},{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Card",category:"ui",source:"velloo",props:[{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Button",category:"ui",source:"velloo",props:[{name:"variant",type:'"default" | "ghost" | "outline" | undefined',optional:!0,control:"enum",enumValues:["default","ghost","outline"],defaultValue:"default"},{name:"className",type:"string | undefined",optional:!0,control:"string"}]},{id:"Input",category:"ui",source:"velloo",props:[{name:"placeholder",type:"string | undefined",optional:!0,control:"string"},{name:"value",type:"string | undefined",optional:!0,control:"string"},{name:"type",type:"string | undefined",optional:!0,control:"string",defaultValue:"text"},{name:"className",type:"string | undefined",optional:!0,control:"string"}]}],NONE_MANIFEST=[...PRIMITIVE_DESCRIPTORS,...helperDescriptors(REUSED_HELPER_IDS)];var React2=__toESM(require_react_development(),1),jsx_dev_runtime2=__toESM(require_react_jsx_dev_runtime_development(),1);function merge(defaults,authored){return authored?{...defaults,...authored}:defaults}var space=(n)=>`${n*0.25}rem`,Box2=React2.forwardRef(({style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("div",{ref,style,...rest},void 0,!1,void 0,this));Box2.displayName="Box";var alignStyle={start:"flex-start",center:"center",end:"flex-end",stretch:"stretch"},justifyStyle={start:"flex-start",center:"center",end:"flex-end",between:"space-between",around:"space-around"},Stack2=React2.forwardRef(({direction="col",gap=4,align,justify,style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("div",{ref,style:merge({display:"flex",flexDirection:direction==="row"?"row":"column",gap:space(gap),...align?{alignItems:alignStyle[align]}:{},...justify?{justifyContent:justifyStyle[justify]}:{}},style),...rest},void 0,!1,void 0,this));Stack2.displayName="Stack";var containerMaxWidth={sm:"640px",md:"768px",lg:"1024px",xl:"1280px",full:"100%"},Container2=React2.forwardRef(({size="md",style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("div",{ref,style:merge({marginInline:"auto",width:"100%",paddingInline:"1rem",maxWidth:containerMaxWidth[size]},style),...rest},void 0,!1,void 0,this));Container2.displayName="Container";var buttonVariantStyle={default:{background:"var(--color-foreground)",color:"var(--color-background)"},ghost:{background:"transparent",color:"var(--color-foreground)"},outline:{background:"transparent",color:"var(--color-foreground)",border:"1px solid var(--color-border)"}},Button2=React2.forwardRef(({variant="default",type,style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("button",{ref,type:type??"button",style:merge({display:"inline-flex",alignItems:"center",justifyContent:"center",gap:"0.5rem",borderRadius:"var(--radius)",padding:"0.5rem 1rem",fontSize:"0.875rem",fontWeight:500,cursor:"pointer",...buttonVariantStyle[variant]},style),...rest},void 0,!1,void 0,this));Button2.displayName="Button";var Input2=React2.forwardRef(({type="text",value,onChange,style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("input",{ref,type,value,onChange,readOnly:value!==void 0&&onChange===void 0||rest.readOnly,style:merge({display:"block",width:"100%",borderRadius:"var(--radius)",border:"1px solid var(--color-input)",background:"var(--color-background)",padding:"0.5rem 0.75rem",fontSize:"0.875rem"},style),...rest},void 0,!1,void 0,this));Input2.displayName="Input";var Card2=React2.forwardRef(({style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("div",{ref,style:merge({borderRadius:"var(--radius)",border:"1px solid var(--color-border)",background:"var(--color-card)",color:"var(--color-card-foreground)",padding:"1.5rem",boxShadow:"0 1px 2px 0 rgb(0 0 0 / 0.05)"},style),...rest},void 0,!1,void 0,this));Card2.displayName="Card";var Heading=React2.forwardRef(({level=1,style,...rest},ref)=>{let Tag=`h${level}`;return jsx_dev_runtime2.jsxDEV(Tag,{ref,style:merge(headingInlineStyle(level),style),...rest},void 0,!1,void 0,this)});Heading.displayName="Heading";var Text=React2.forwardRef(({variant="default",style,...rest},ref)=>jsx_dev_runtime2.jsxDEV("p",{ref,style:merge(textInlineStyle(variant),style),...rest},void 0,!1,void 0,this));Text.displayName="Text";var REUSED_HELPER_IDS2=["Prose","Icon","SVG","Image","Layer","Divider","Gradient","Placeholder"],inlineRegistry={Box:Box2,Stack:Stack2,Container:Container2,Card:Card2,Button:Button2,Input:Input2,Heading,Text,...helpersRegistry(REUSED_HELPER_IDS2)};var PRIMITIVES=new Set(["Box","Stack","Container","Card","Button","Input"]),INLINE_TYPOGRAPHY=new Set(["Heading","Text"]),here=dirname(fileURLToPath(import.meta.url)),srcDir=resolveProviderSrcDir(here,"provider-none",process.env.VELLOO_NOLIB_SRC),entryCssPath=join(srcDir,"tailwind-entry.css"),componentsDir=srcDir;async function loadManifest(){return NONE_MANIFEST}function createProvider(){return{id:"none",version:noLibVersion,componentsDir,styleEntryPath:entryCssPath,registry,loadManifest,label:`none ${noLibVersion}`,styleChannel:TAILWIND_CLASSNAME,styleChannels:["tailwind-classname","style"],mcpIntro:(channel)=>[...channel==="style"?NONE_INLINE_INTRO:NONE_INTRO],registryForChannel:(kind)=>kind==="style"?inlineRegistry:registry,canvasBundleSpec:{styleRuntime:{kind:"none"},onlyWithRepository:!0,components:(ids,context)=>{let inline=context?.channel==="style";return ids.flatMap((id)=>{let own=PRIMITIVES.has(id)||inline&&INLINE_TYPOGRAPHY.has(id)?join(srcDir,inline?"components-inline.tsx":"components.tsx"):helperSourcePath(id);return own?[{id,sources:[{importPath:own,exportName:id,fidelity:"exact",note:"Velloo primitive, mounted beside the app's components."}]}]:[]})}}}}export{createProvider};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{isCancel,select}from"./chunk-80fjfyha.js";import{closePooledBrowser,renderScreen,screenshot}from"./chunk-c4fym47a.js";import{ScreenSchema}from"./chunk-nte0t7v3.js";import{writeText,registryForScreen,renderPassForScreen}from"./chunk-
|
|
2
|
+
import{isCancel,select}from"./chunk-80fjfyha.js";import{closePooledBrowser,renderScreen,screenshot}from"./chunk-c4fym47a.js";import{ScreenSchema}from"./chunk-nte0t7v3.js";import{writeText,registryForScreen,renderPassForScreen}from"./chunk-egccxmed.js";import"./chunk-gsne0ap4.js";import{fail}from"./chunk-s23gtejf.js";import{captureWithBrowserSetup}from"./chunk-2fgfex9b.js";import{findDesignConfig}from"./chunk-bhx8k09w.js";import{loadPipeline}from"./chunk-7cktk5qr.js";import{withAssetServer}from"./chunk-0zen7bzb.js";import{createProgress}from"./chunk-pjtg3wnm.js";import{DESIGN_ARG_DESCRIPTION,resolveDesign,pickScreen}from"./chunk-46xykc71.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{readFile}from"fs/promises";import{extname,isAbsolute,resolve}from"path";var render_default=defineCommand({meta:{name:"render",description:"dev: render a screen headless to .html or .png"},args:{screen:{type:"positional",required:!1,description:"Screen id, or a path to a screen JSON. Omit to pick interactively."},design:{type:"string",description:DESIGN_ARG_DESCRIPTION},to:{type:"string",description:"Output path; extension picks the format (.html | .png). Default: ./<screen>.html"},w:{type:"string",description:"Viewport width in px (default: 1440)"},h:{type:"string",description:"Viewport height in px (default: 900)"}},async run({args}){let interactive=Boolean(process.stdin.isTTY),screenArg=args.screen,looksLikePath=!!screenArg&&(screenArg.includes("/")||screenArg.endsWith(".json")),folder,screenPath;if(looksLikePath&&screenArg)if(screenPath=resolve(screenArg),args.design)folder=await resolveDesign(args.design,"render",{designFlag:"--design"});else{let found=await findDesignConfig(screenPath);if(!found)fail("render",`${screenPath} is not inside a velloo design folder (no .design/config.json above it). Pass --design with a design name or a path.`);folder=found.folder}else folder=await resolveDesign(args.design,"render",{designFlag:"--design"}),screenPath=(await pickScreen(folder,screenArg,interactive,"render")).path;let screen=ScreenSchema.parse(JSON.parse(await readFile(screenPath,"utf8"))),viewport={w:args.w?Number(args.w):1440,h:args.h?Number(args.h):900},outPath;if(args.to)outPath=isAbsolute(args.to)?args.to:resolve(args.to);else{let ext="html";if(interactive){let fmt=await select({message:"Output format?",options:[{value:"html",label:"HTML",hint:"no browser needed"},{value:"png",label:"PNG",hint:"screenshot (needs a headless browser)"}]});if(isCancel(fmt))fail("render","cancelled.");ext=fmt}outPath=resolve(`${screen.id}.${ext}`)}let out=extname(outPath).toLowerCase();if(out!==".html"&&out!==".png")fail("render",`unsupported output extension ${JSON.stringify(out)}. Use .html or .png.`);let progress=createProgress();progress.start("preparing render");try{let pipeline=await loadPipeline(folder),{design,config,providers,defaultProvider,snapshotCss}=pipeline,theme=design.theme,registry=registryForScreen(screen,providers,defaultProvider,config.extensions??{}),renderPass=renderPassForScreen(screen,providers,defaultProvider,theme),renderHtml=async(baseHref)=>{let{html}=await renderScreen(screen,theme,{viewport,snapshotCss,registry,snippets:design.snippets,renderPass,customCss:design.customCss,...baseHref?{baseHref}:{}});return html};if(out===".html"){progress.step("rendering HTML"),await writeText(outPath,await renderHtml()),progress.succeed("rendered HTML"),console.log(`velloo render: wrote ${outPath} (screen=${screen.id})`);return}progress.step("rendering screen"),await withAssetServer(folder,null,async(baseHref)=>{let html=await renderHtml(baseHref);try{await captureWithBrowserSetup("render",async()=>{progress.step("capturing PNG");try{await screenshot({html,viewport,outPath})}catch(error){throw progress.fail("PNG capture failed"),error}})}finally{await closePooledBrowser()}}),progress.succeed("rendered PNG"),console.log(`velloo render: wrote ${outPath} (screen=${screen.id})`)}catch(error){throw progress.fail("render failed"),error}}});export{render_default as default};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{loadDesignFolder,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-
|
|
2
|
+
import{loadDesignFolder,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-egccxmed.js";import{join}from"path";async function loadPipeline(folderPath){let design=await loadDesignFolder(folderPath),config=design.config,{providers,defaultProvider}=await resolveProviders(config,folderPath),jit=new TailwindJit(Object.values(providers),join(folderPath,"screens"),void 0,()=>extraThemeBlock(design),void 0,()=>findHostTailwindConfig(folderPath,config.hostApp),config.styling?.framework),snapshotCss=await jit.build();return{folderPath,design,config,providers,defaultProvider,jit,snapshotCss}}
|
|
3
3
|
export{loadPipeline};
|