velloo 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -128
- package/THIRD-PARTY-NOTICES.md +58 -58
- package/canvas/assets/InitWizard-C1CkcS7C.js +3 -0
- package/canvas/assets/index-CZPX0col.css +2 -0
- package/canvas/assets/index-D7gy-ZUv.js +49 -0
- package/canvas/assets/lucide-all-B49qZ9YE.js +1 -0
- package/canvas/assets/radix-CuVEihKE.js +41 -0
- package/canvas/assets/react-C1iE8mnY.js +9 -0
- package/canvas/assets/rolldown-runtime-XKyaHtl9.js +1 -0
- package/canvas/assets/vendor-DXkvBgGY.js +58 -0
- package/canvas/assets/wand-sparkles-BxGgFkAt.js +1 -0
- package/canvas/index.html +6 -6
- package/{chunk-seaar5na.js → chunk-0rvdh1j3.js} +1 -1
- package/chunk-12pgt83m.js +8 -0
- package/chunk-12vc3f1h.js +3 -0
- package/{chunk-qvdvxqs0.js → chunk-28mhcvw2.js} +1 -1
- package/{chunk-ysf9388d.js → chunk-3zm7ydna.js} +1 -1
- package/{chunk-qn05hn7a.js → chunk-457fm9yy.js} +5 -3
- package/chunk-46jg9aqy.js +40 -0
- package/chunk-5gwh0zpn.js +3 -0
- package/{chunk-bsn761jv.js → chunk-5hb1e233.js} +1 -1
- package/{chunk-3f2p94wa.js → chunk-5ts4fvd9.js} +1 -1
- package/{chunk-c11n1tve.js → chunk-62amy8bj.js} +1 -1
- package/{chunk-ktps9v0k.js → chunk-6p8zwxxx.js} +1 -1
- package/{chunk-02gpgqj4.js → chunk-75gfv9g4.js} +10 -11
- package/chunk-84qjmqmz.js +2 -0
- package/{chunk-6adv9eke.js → chunk-85m9p67t.js} +2 -2
- package/chunk-91qesxwh.js +2 -0
- package/{chunk-ck3bg8rf.js → chunk-976rm76x.js} +1 -1
- package/chunk-99ccjbaa.js +4 -0
- package/{chunk-8kn6tp4b.js → chunk-9hc7m55p.js} +3 -4
- package/{chunk-xc6mats8.js → chunk-a7xka0bj.js} +1 -1
- package/chunk-ak57bgnh.js +2 -0
- package/chunk-bf5808x5.js +2 -0
- package/{chunk-00n2d68z.js → chunk-bj47gjs9.js} +1 -1
- package/chunk-d0m56cjx.js +2 -0
- package/chunk-d6a6198y.js +3 -0
- package/{chunk-r014f76h.js → chunk-etv7qsw7.js} +1 -1
- package/chunk-f2793pj6.js +85 -0
- package/{chunk-a5mxgvcz.js → chunk-fnv123mm.js} +1 -1
- package/{chunk-pjtg3wnm.js → chunk-fzevxpdz.js} +1 -1
- package/chunk-ghwy99py.js +68 -0
- package/chunk-gtt1qww8.js +2 -0
- package/chunk-hhkjqvww.js +1501 -0
- package/{chunk-fejhav0p.js → chunk-hrvqwp8z.js} +1 -1
- package/{chunk-6600wkmq.js → chunk-hxk0wkt3.js} +1 -1
- package/{chunk-f8jchnwe.js → chunk-jc95phff.js} +2 -2
- package/chunk-kwym0g80.js +11 -0
- package/chunk-kxcjme3n.js +6 -0
- package/{chunk-pasvz8xw.js → chunk-mkctjrrz.js} +2 -2
- package/{chunk-v6w8akph.js → chunk-mknhfnkn.js} +1 -1
- package/{chunk-yp39f93e.js → chunk-mseawt58.js} +89 -55
- package/{chunk-gv209d5j.js → chunk-nccfyeyy.js} +1 -1
- package/{chunk-tmjs6xas.js → chunk-npnxs0fy.js} +1 -1
- package/{chunk-x61f42fj.js → chunk-p9p2vbat.js} +2 -2
- package/chunk-pqc7dbhc.js +2 -0
- package/{chunk-tax7fxav.js → chunk-qc7c328q.js} +1 -1
- package/chunk-qe6pp4p6.js +72 -0
- package/{chunk-3jqbybmp.js → chunk-qwy0jba8.js} +1 -1
- package/{chunk-5tatkt2j.js → chunk-s26bgjqz.js} +1 -1
- package/{chunk-9kvbe53e.js → chunk-s8pqbr4n.js} +1 -1
- package/{chunk-p93x4pn2.js → chunk-tjy12q19.js} +1 -1
- package/{chunk-9b4akcfp.js → chunk-v1by80c5.js} +1 -1
- package/chunk-vdy4ave9.js +2 -0
- package/chunk-vwhee991.js +3 -0
- package/chunk-wf7r45wh.js +12 -0
- package/{chunk-bpb97rsk.js → chunk-x4jsr5et.js} +1 -1
- package/{chunk-z9kcdgj8.js → chunk-x9gk2n9v.js} +1 -1
- package/chunk-xxjevh62.js +2 -0
- package/{chunk-6wfvb07c.js → chunk-y2y1b3zm.js} +1 -1
- package/chunk-y6jq6y8t.js +2 -0
- package/cli.js +1 -1
- package/package.json +3 -3
- package/pkgs/helpers/src/icon-data.ts +868 -172
- package/pkgs/shadcn-snapshot/dist/manifest.json +348 -0
- package/pkgs/shadcn-snapshot/src/components/canvas-portal.tsx +3 -3
- package/plugins/claude/velloo/.claude-plugin/plugin.json +5 -2
- package/plugins/gemini/gemini-extension.json +7 -2
- package/canvas/assets/index-CkVteRZ9.css +0 -2
- package/canvas/assets/index-DFOlwAKW.js +0 -31
- package/canvas/assets/lucide-all-CezGNjGW.js +0 -1
- package/canvas/assets/radix-gmpYzWRH.js +0 -41
- package/canvas/assets/react-D0Ec-185.js +0 -9
- package/canvas/assets/rolldown-runtime-hePW80VL.js +0 -1
- package/canvas/assets/vendor-CKmQ-Zst.js +0 -53
- package/chunk-0ztxyzac.js +0 -2
- package/chunk-2x5rw009.js +0 -40
- package/chunk-640swnrm.js +0 -4
- package/chunk-80fjfyha.js +0 -86
- package/chunk-8a2agf28.js +0 -22
- package/chunk-as4rn21e.js +0 -2
- package/chunk-avewjans.js +0 -85
- package/chunk-cfksd3zh.js +0 -2
- package/chunk-cy1nmr0f.js +0 -12
- package/chunk-dqy6a75x.js +0 -2
- package/chunk-fzqt4rsz.js +0 -6
- package/chunk-g7pbfad5.js +0 -3
- package/chunk-j4rt31qb.js +0 -2
- package/chunk-kmsbcb3q.js +0 -3
- package/chunk-ktsdzzgx.js +0 -2
- package/chunk-kvxn1cyx.js +0 -8
- package/chunk-n70zfv0m.js +0 -1491
- package/chunk-n8pr0kc8.js +0 -2
- package/chunk-qewarcqf.js +0 -2
- package/chunk-qf1c6jxh.js +0 -3
- package/chunk-vbk1qhdz.js +0 -3
- package/chunk-w40pqmyz.js +0 -63
- package/chunk-x05grjd9.js +0 -2
- package/chunk-z3r8px3d.js +0 -6
- package/chunk-zexzy6zv.js +0 -2
- package/chunk-zjbbd87h.js +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{PACKAGE_VERSION,TOOL_VERSION,BUILD_TIME_MS}from"./chunk-
|
|
2
|
+
import{PACKAGE_VERSION,TOOL_VERSION,BUILD_TIME_MS}from"./chunk-d6a6198y.js";import{selfCommand}from"./chunk-skk6yvnw.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM}from"./chunk-2smsg2ct.js";var import_picocolors=__toESM(require_picocolors(),1);import{existsSync as existsSync2}from"fs";import{mkdir,mkdtemp,readFile as readFile2,rename,rm,writeFile}from"fs/promises";import{homedir as homedir2,tmpdir}from"os";import{dirname as dirname2,join as join2}from"path";import{existsSync,realpathSync}from"fs";import{readFile}from"fs/promises";import{homedir}from"os";import{dirname,join}from"path";var DEFAULT_DOWNLOAD_BASE={stable:"https://get.velloo.design",dev:"https://get.dev.velloo.design"},NPM_LATEST_URL="https://registry.npmjs.org/velloo/latest";function localReleasePath(){return process.env.VELLOO_LOCAL_RELEASE??join(homedir(),".velloo","local-release.json")}async function readLocalRelease(){try{let raw=JSON.parse(await readFile(localReleasePath(),"utf8"));if(typeof raw!=="object"||raw===null)return null;let value=raw;if(typeof value.builtAt!=="number"||typeof value.tarball!=="string")return null;return{version:typeof value.version==="string"?value.version:PACKAGE_VERSION,build:typeof value.build==="string"?value.build:value.version??PACKAGE_VERSION,builtAt:value.builtAt,tarball:value.tarball,repo:typeof value.repo==="string"?value.repo:dirname(value.tarball)}}catch{return null}}function looksLikeHomebrew(env){let entry=env.VELLOO_LAUNCHER_PATH??Bun.main;if(!entry)return!1;try{return/[/\\]Cellar[/\\]velloo[/\\]/.test(realpathSync(entry))}catch{return/[/\\]Cellar[/\\]velloo[/\\]/.test(entry)}}var EXPLICIT=["npm","direct","homebrew","local","source"];function installMethod(env=process.env){let declared=env.VELLOO_INSTALL_METHOD;if(declared&&EXPLICIT.includes(declared)){if(declared==="npm"&&looksLikeHomebrew(env))return"homebrew";return declared}if(looksLikeHomebrew(env))return"homebrew";return"unknown"}function selfUpgradable(method){return method==="npm"||method==="direct"||method==="homebrew"||method==="local"}function releaseChannel(env=process.env){let declared=env.VELLOO_RELEASE_CHANNEL??bakedChannel();return declared==="dev"||declared==="local"?declared:"stable"}function bakedChannel(){return"stable"}function downloadBase(env=process.env){let explicit=env.VELLOO_DOWNLOAD_BASE;if(explicit)return explicit.replace(/\/+$/,"");let channel=releaseChannel(env);return DEFAULT_DOWNLOAD_BASE[channel==="local"?"stable":channel]}function installerUrl(env=process.env){return env.VELLOO_INSTALLER_URL??`${downloadBase(env)}/install.sh`}function latestVersionUrl(env=process.env){if(env.VELLOO_UPDATE_URL)return env.VELLOO_UPDATE_URL;if(releaseChannel(env)==="stable"&&installMethod(env)==="npm")return NPM_LATEST_URL;return`${downloadBase(env)}/latest.json`}function runningRelease(){return{version:PACKAGE_VERSION,build:TOOL_VERSION,...BUILD_TIME_MS===void 0?{}:{builtAt:BUILD_TIME_MS}}}function compareVersions(left,right){let parse=(value)=>{let match=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value);if(!match)return null;return{core:[Number(match[1]),Number(match[2]),Number(match[3])],prerelease:match[4]??null}},a=parse(left),b=parse(right);if(!a||!b)return 0;for(let index=0;index<3;index++){let delta=(a.core[index]??0)-(b.core[index]??0);if(delta!==0)return Math.sign(delta)}if(a.prerelease===b.prerelease)return 0;if(a.prerelease===null)return 1;if(b.prerelease===null)return-1;return a.prerelease.localeCompare(b.prerelease,void 0,{numeric:!0})}function isNewerRelease(candidate,running=runningRelease()){let byVersion=compareVersions(candidate.version,running.version);if(byVersion!==0)return byVersion>0;if(typeof candidate.builtAt!=="number")return!1;if(typeof running.builtAt!=="number")return!0;return candidate.builtAt>running.builtAt}function releaseLabel(info){return info.build??info.version}var FETCH_TIMEOUT_MS=2000;async function fetchLatestRelease(env=process.env){if(releaseChannel(env)==="local"&&!env.VELLOO_UPDATE_URL){let local=await readLocalRelease();if(!local)return null;return{version:local.version,build:local.build,builtAt:local.builtAt}}let response=await fetch(latestVersionUrl(env),{headers:{accept:"application/json","user-agent":`velloo/${PACKAGE_VERSION}`},signal:AbortSignal.timeout(FETCH_TIMEOUT_MS)});if(!response.ok)throw Error(`release check returned HTTP ${response.status}`);let body=await response.json();if(typeof body.version!=="string"||!/^\d+\.\d+\.\d+/.test(body.version))throw Error("release check returned an invalid version");return{version:body.version,...typeof body.build==="string"?{build:body.build}:{},...typeof body.builtAt==="number"?{builtAt:body.builtAt}:{}}}function installedVellooBin(env=process.env){let candidates=[],method=installMethod(env);if(method==="direct")candidates.push(join(env.VELLOO_BIN_DIR??join(homedir(),".local","bin"),"velloo"));if(method==="homebrew"){let prefix=env.HOMEBREW_PREFIX??(process.platform==="darwin"?"/opt/homebrew":null);if(prefix)candidates.push(join(prefix,"bin","velloo"));candidates.push("/usr/local/bin/velloo")}if(method==="npm"||method==="local"){let node=env.VELLOO_NODE_EXECUTABLE;if(node)candidates.push(join(dirname(node),"velloo"));let prefix=npmGlobalPrefix(env);if(prefix)candidates.push(process.platform==="win32"?join(prefix,"velloo.cmd"):join(prefix,"bin","velloo"))}return candidates.find((path)=>existsSync(path))??null}function npmGlobalPrefix(env){try{let result=Bun.spawnSync([npmExecutable(env),"prefix","-g"],{env}),out=result.stdout.toString().trim();return result.success&&out?out:null}catch{return null}}function npmExecutable(env=process.env){let node=env.VELLOO_NODE_EXECUTABLE;if(node){let adjacent=join(dirname(node),process.platform==="win32"?"npm.cmd":"npm");if(existsSync(adjacent))return adjacent}return process.platform==="win32"?"npm.cmd":"npm"}var CHECK_INTERVAL_MS=86400000,LOCAL_CHECK_INTERVAL_MS=1e4;function releaseSource(){return releaseChannel()==="local"&&!process.env.VELLOO_UPDATE_URL?`file:${localReleasePath()}`:latestVersionUrl()}function cachePath(){return process.env.VELLOO_UPDATE_CACHE??join2(homedir2(),".velloo","update-check.json")}async function readCacheFile(){try{let value=JSON.parse(await readFile2(cachePath(),"utf8"));if(typeof value!=="object"||value===null)return{};return Object.fromEntries(Object.entries(value).filter(([,entry])=>typeof entry==="object"&&entry!==null))}catch{return{}}}async function readCache(){let entry=(await readCacheFile())[releaseSource()];if(typeof entry?.checkedAt!=="number"||typeof entry.latest!=="string")return null;return entry}async function writeCache(value){let path=cachePath(),file={...await readCacheFile(),[releaseSource()]:value};await mkdir(dirname2(path),{recursive:!0});let temporary=`${path}.${process.pid}.tmp`;await writeFile(temporary,`${JSON.stringify(file,null,2)}
|
|
3
3
|
`);try{await rename(temporary,path)}catch(error){if(process.platform!=="win32")throw error;await rm(path,{force:!0}),await rename(temporary,path)}}function cachedRelease(cache){return{version:cache.latest,...cache.latestBuild===void 0?{}:{build:cache.latestBuild},...cache.latestBuiltAt===void 0?{}:{builtAt:cache.latestBuiltAt}}}async function refreshUpdateCache(){try{let previous=await readCache(),latest=await fetchLatestRelease();if(!latest)return;await writeCache({checkedAt:Date.now(),latest:latest.version,...latest.build===void 0?{}:{latestBuild:latest.build},...latest.builtAt===void 0?{}:{latestBuiltAt:latest.builtAt},...previous?.notifiedAt?{notifiedAt:previous.notifiedAt}:{},...previous?.notifiedVersion?{notifiedVersion:previous.notifiedVersion}:{}})}catch{}}function spawnUpdateCheck(){try{Bun.spawn(selfCommand(["__update_check"]),{stdin:"ignore",stdout:"ignore",stderr:"ignore",env:{...process.env,VELLOO_UPDATE_CHECK_CHILD:"1"}}).unref()}catch{}}function checkIntervalMs(){return releaseChannel()==="local"?LOCAL_CHECK_INTERVAL_MS:CHECK_INTERVAL_MS}function checkSuppressed(){return process.env.VELLOO_DISABLE_UPDATE_CHECK==="1"||process.env.VELLOO_UPDATE_CHECK_CHILD==="1"||!selfUpgradable(installMethod())}async function maybeNotifyAboutUpdate(options={}){if(checkSuppressed()||!(options.isTTY??process.stderr.isTTY))return;let now=options.now??Date.now(),cached=await readCache(),latest=(releaseChannel()==="local"?await fetchLatestRelease().catch(()=>null):null)??(cached?cachedRelease(cached):null);if(latest&&isNewerRelease(latest)){if(!(cached?.notifiedVersion===releaseLabel(latest)&&typeof cached?.notifiedAt==="number"&&now-cached.notifiedAt<checkIntervalMs())){let running=runningRelease();console.error(""),console.error(`${import_picocolors.default.yellow("Update available:")} ${releaseLabel(running)} \u2192 ${releaseLabel(latest)}. Run ${import_picocolors.default.cyan("velloo upgrade")}.`),await writeCache({checkedAt:cached?.checkedAt??now,latest:latest.version,...latest.build===void 0?{}:{latestBuild:latest.build},...latest.builtAt===void 0?{}:{latestBuiltAt:latest.builtAt},notifiedAt:now,notifiedVersion:releaseLabel(latest)}).catch(()=>{})}}if(!cached||now-cached.checkedAt>=checkIntervalMs())spawnUpdateCheck()}async function runInherited(command,env=process.env){let code=await Bun.spawn(command,{stdin:"inherit",stdout:"inherit",stderr:"inherit",env}).exited;if(code!==0)throw Error(`\`${command.join(" ")}\` exited with status ${code}`)}async function updateStatus(opts={}){let method=installMethod(),channel=releaseChannel(),running=runningRelease(),base={method,channel,current:releaseLabel(running),latest:null,available:!1,upgradable:selfUpgradable(method),...selfUpgradable(method)?{}:{reason:unsupportedReason(method)}};if(!selfUpgradable(method))return base;let release=null;if(opts.refresh)try{if(release=await fetchLatestRelease(),release)await writeCache({checkedAt:Date.now(),latest:release.version,...release.build===void 0?{}:{latestBuild:release.build},...release.builtAt===void 0?{}:{latestBuiltAt:release.builtAt}}).catch(()=>{})}catch(error){return{...base,reason:error instanceof Error?error.message:String(error)}}else{let cached=await readCache();if(cached)release=cachedRelease(cached);if(!cached||Date.now()-cached.checkedAt>=checkIntervalMs())spawnUpdateCheck()}if(!release)return base;return{...base,latest:releaseLabel(release),available:isNewerRelease(release,running)}}function unsupportedReason(method){return method==="source"?"this Velloo is running from source; update the checkout with your Git workflow":"could not identify this installation. Reinstall with `npm install -g velloo@latest` or the installer at https://get.velloo.design/install.sh"}async function upgradeInstalledVelloo(options={}){let method=installMethod(),channel=releaseChannel(),running=runningRelease();if(!selfUpgradable(method))throw Error(unsupportedReason(method));let latest=await fetchLatestRelease();if(!latest)throw Error(channel==="local"?`no local build found at ${localReleasePath()} \u2014 run \`bun run cli:build\` in the velloo checkout first`:"this channel has no published release");let done=(upgraded)=>({upgraded,from:releaseLabel(running),to:releaseLabel(latest),method,channel});if(!isNewerRelease(latest,running))return console.log(`velloo: ${releaseLabel(running)} is already the latest ${channel} build.`),done(!1);if(options.checkOnly)return console.log(`velloo: ${releaseLabel(running)} \u2192 ${releaseLabel(latest)} is available (${method} installation, ${channel} channel).`),done(!1);if(method==="homebrew")await runInherited(["brew","upgrade",process.env.VELLOO_BREW_FORMULA??"velloo"]);else if(channel==="local")await upgradeFromLocalBuild();else if(method==="npm"){let packageName=process.env.VELLOO_NPM_PACKAGE??"velloo",spec=channel==="dev"?`${downloadBase()}/velloo.tgz`:`${packageName}@${latest.version}`;await runInherited([npmExecutable(),"install","-g",spec])}else{let response=await fetch(installerUrl(),{signal:AbortSignal.timeout(1e4)});if(!response.ok)throw Error(`installer download returned HTTP ${response.status}`);let directory=await mkdtemp(join2(tmpdir(),"velloo-upgrade-")),installer=join2(directory,"install.sh");try{await writeFile(installer,await response.text(),{mode:448}),await runInherited(["bash",installer],{...process.env,VELLOO_VERSION:latest.version,VELLOO_DOWNLOAD_BASE:downloadBase()})}finally{await rm(directory,{recursive:!0,force:!0})}}return console.log(`velloo: upgraded ${releaseLabel(running)} \u2192 ${releaseLabel(latest)} via ${method}.`),done(!0)}async function upgradeFromLocalBuild(){let local=await readLocalRelease();if(!local)throw Error("no local build recorded \u2014 run `bun run cli:build` first");if(!existsSync2(local.tarball))throw Error(`the recorded local build is gone (${local.tarball}) \u2014 run \`bun run cli:build\` in ${local.repo} again`);await runInherited([npmExecutable(),"install","-g","--include=optional",local.tarball])}
|
|
4
4
|
export{installedVellooBin,refreshUpdateCache,maybeNotifyAboutUpdate,updateStatus,upgradeInstalledVelloo};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{helperSourcePath}from"./chunk-yp39f93e.js";import"./chunk-w40pqmyz.js";import{snapshotVersion2,entryCssPath3,componentsDir3}from"./chunk-p93x4pn2.js";import{registry2}from"./chunk-avewjans.js";import{TAILWIND_CLASSNAME}from"./chunk-n70zfv0m.js";import"./chunk-kvxn1cyx.js";import{readFile}from"fs/promises";import{join}from"path";async function readManifest(cacheDir){let raw=await readFile(join(cacheDir,"manifest.json"),"utf8");return JSON.parse(raw)}import{existsSync as existsSync4,readFileSync as readFileSync4}from"fs";import{basename,join as join5}from"path";import{existsSync as existsSync3,readFileSync as readFileSync3}from"fs";import{join as join4}from"path";import{existsSync,readFileSync}from"fs";import{join as join2}from"path";function exportsName(source,id){let name=id.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function|const|let|var|class)\\s+${name}\\b`).test(source))return!0;for(let match of source.matchAll(/\bexport\s*\{([^}]*)\}/g)){let clause=match[1]??"";for(let part of clause.split(",")){let local=part.trim().split(/\s+as\s+/);if((local.length>1?local[1]:local[0])?.trim()===id)return!0}}return/\bexport\s+\*\s+from\b/.test(source)}function hostComponentFile(uiDir,addName,id){for(let extension of["tsx","ts","jsx","js"]){let path=join2(uiDir,`${addName}.${extension}`);if(!existsSync(path))continue;try{return exportsName(readFileSync(path,"utf8"),id)?path:null}catch{return null}}return null}import{existsSync as existsSync2,readdirSync,readFileSync as readFileSync2}from"fs";import{join as join3,resolve}from"path";function kebab(id){return id.replace(/(?<!^)(?=[A-Z])/g,"-").toLowerCase()}function addNameIndex(manifest){let byId=new Map(manifest.flatMap((c)=>c.registryName?[[c.id,c.registryName]]:[]));return(id)=>byId.get(id)??kebab(id)}function findUiDir(hostAppRoot){let candidates=[];try{let cj=JSON.parse(readFileSync2(join3(hostAppRoot,"components.json"),"utf8")),fromAlias=(alias,suffix="")=>{if(!alias)return;let srcBase=existsSync2(join3(hostAppRoot,"src"))?"src":"",rel=alias.replace(/^@\//,srcBase?`${srcBase}/`:"")+suffix;candidates.push(resolve(hostAppRoot,rel))};fromAlias(cj.aliases?.ui),fromAlias(cj.aliases?.components,"/ui")}catch{}candidates.push(resolve(hostAppRoot,"src/components/ui"),resolve(hostAppRoot,"components/ui"),resolve(hostAppRoot,"app/components/ui"));for(let dir of candidates)if(existsSync2(dir))return dir;return null}function installedAddNames(dir){if(!dir)return new Set;try{return new Set(readdirSync(dir).filter((f)=>f.endsWith(".tsx")||f.endsWith(".ts")).map((f)=>f.replace(/\.(tsx|ts)$/,"")))}catch{return new Set}}function enrichManifestFromHost(manifest,uiDir){if(!uiDir)return manifest;let addNameOf=addNameIndex(manifest),byFile=new Map,read=(addName)=>{if(byFile.has(addName))return byFile.get(addName)??null;for(let ext of["tsx","ts","jsx","js"]){let path=join4(uiDir,`${addName}.${ext}`);if(!existsSync3(path))continue;let source=readFileSync3(path,"utf8");return byFile.set(addName,source),source}return byFile.set(addName,""),null};return manifest.map((descriptor)=>{if(descriptor.source==="velloo")return descriptor;let source=read(addNameOf(descriptor.id));if(!source||!exportsName(source,descriptor.id))return descriptor;let additions=[...variantPropsFor(source,descriptor.id===descriptor.family),...declaredPropsFor(source,descriptor.id)];if(additions.length===0)return descriptor;let props=[...descriptor.props];for(let addition of additions){let index=props.findIndex((prop)=>prop.name===addition.name);if(index>=0)props[index]={...props[index],...addition};else props.push(addition)}return{...descriptor,props,designModeNotes:appendNote(descriptor.designModeNotes,"Props include variants detected from the app's component source.")}})}function appendNote(current,next){return current?`${current} ${next}`:next}function variantPropsFor(source,primary){let out=[];if(!primary)return out;let variantsAt=source.indexOf("variants:");if(variantsAt<0)return out;let open=source.indexOf("{",variantsAt),block=balancedBlock(source,open);if(!block)return out;for(let[name,value]of objectEntries(block.slice(1,-1))){let values=objectEntries(value.slice(1,-1)).map(([key])=>key);if(values.length===0)continue;out.push({name,type:`${values.map((value)=>JSON.stringify(value)).join(" | ")} | undefined`,optional:!0,control:"enum",enumValues:values,...defaultVariant(source,name)?{defaultValue:defaultVariant(source,name)}:{}})}return out}function defaultVariant(source,name){let at=source.indexOf("defaultVariants:");if(at<0)return;let open=source.indexOf("{",at),block=balancedBlock(source,open);if(!block)return;return objectEntries(block.slice(1,-1)).find(([key])=>key===name)?.[1].replace(/["'`\s]/g,"")}function declaredPropsFor(source,componentId){let match=[new RegExp(`interface\\s+${componentId}Props(?:\\s+extends[^\\{]+)?\\s*\\{`,"m"),new RegExp(`type\\s+${componentId}Props\\s*=.*?&\\s*\\{`,"m")].map((pattern)=>pattern.exec(source)).find(Boolean);if(!match||match.index===void 0)return[];let open=source.indexOf("{",match.index),block=balancedBlock(source,open);if(!block)return[];let out=[],property=/(?:^|[;{\n])\s*([A-Za-z_$][\w$]*)(\?)?\s*:\s*([^;\n}]+)/gm;for(let found of block.slice(1,-1).matchAll(property)){let name=found[1],raw=found[3]?.trim();if(!name||!raw||name==="children")continue;let enumValues=[...raw.matchAll(/["']([^"']+)["']/g)].map((value)=>value[1]),optional=found[2]==="?"||raw.includes("undefined");out.push({name,type:optional&&!raw.includes("undefined")?`${raw} | undefined`:raw,optional,control:enumValues.length>0?"enum":/boolean/.test(raw)?"boolean":/number/.test(raw)?"number":"string",...enumValues.length>0?{enumValues}:{}})}return out}function balancedBlock(source,open){if(open<0||source[open]!=="{")return null;let depth=0,quote="";for(let index=open;index<source.length;index++){let char=source[index],prev=source[index-1];if(quote){if(char===quote&&prev!=="\\")quote="";continue}if(char==='"'||char==="'"||char==="`"){quote=char;continue}if(char==="{")depth++;if(char==="}"&&--depth===0)return source.slice(open,index+1)}return null}function objectEntries(body){let out=[],index=0;while(index<body.length){while(index<body.length&&/[\s,]/.test(body[index]))index++;let keyMatch=/^(?:["']([^"']+)["']|([A-Za-z_$][\w$-]*))\s*:/.exec(body.slice(index));if(!keyMatch)break;let key=keyMatch[1]??keyMatch[2];if(!key)break;index+=keyMatch[0].length;while(/\s/.test(body[index]??""))index++;if(body[index]==="{"){let block=balancedBlock(body,index);if(!block)break;out.push([key,block]),index+=block.length;continue}let end=valueEnd(body,index);out.push([key,body.slice(index,end).trim()]),index=end+1}return out}function valueEnd(body,start){let quote="",depth=0;for(let index=start;index<body.length;index++){let char=body[index];if(quote){if(char===quote&&body[index-1]!=="\\")quote=""}else if(char==='"'||char==="'"||char==="`")quote=char;else if("([{".includes(char))depth++;else if(")]}".includes(char))depth--;else if(char===","&&depth===0)return index}return body.length}function createProvider(opts={}){let cacheDir=opts.cacheDir,plannedUiDir=cacheDir?join5(cacheDir,"ui"):void 0,hostUiDir=()=>plannedUiDir&&existsSync4(plannedUiDir)?plannedUiDir:opts.hostAppRoot?findUiDir(opts.hostAppRoot):null,componentsDir=componentsDir3,version=opts.version??snapshotVersion2,loadManifest=async()=>{if(cacheDir&&existsSync4(join5(cacheDir,"manifest.json")))return enrichManifestFromHost(await readManifest(cacheDir),hostUiDir());let{loadManifest:snapshotManifest}=await import("./chunk-qewarcqf.js");return enrichManifestFromHost(await snapshotManifest(),hostUiDir())};return{id:"shadcn-upstream",version,componentsDir,styleEntryPath:entryCssPath3,registry:registry2,loadManifest,label:`shadcn (upstream @ ${version})`,styleChannel:TAILWIND_CLASSNAME,styleChannels:["tailwind-classname"],mcpIntro:()=>['**This folder targets shadcn/ui with Tailwind.** The canvas imports client-safe components directly from the app\'s `components/ui`, adapts overlay families to stay inline, and falls back per component when a file is missing \u2014 `component_status` says which, before you claim fidelity. Style with a className string: `update_props { style: "flex gap-4 p-6" }`.',""],catalog:async()=>{let manifest=await loadManifest(),addNameOf=addNameIndex(manifest),shadcn=manifest.filter((c)=>c.source!=="velloo"),present=installedAddNames(hostUiDir());return shadcn.map((c)=>{let addName=addNameOf(c.id);return{id:c.id,installed:present.has(addName),importPath:`@/components/ui/${addName}`}})},ownedModules:{dirs:()=>{let uiDir=hostUiDir();return uiDir?[uiDir]:[]},supplies:(file,exportName)=>{let family=basename(file).replace(/\.[jt]sx?$/,""),counterpart=join5(componentsDir3,"ui",`${family}.tsx`);return familyExports(counterpart).has(exportName)}},canvasBundleSpec:{styleRuntime:{kind:"none"},components:async(ids)=>{let manifest=await loadManifest(),addNameOf=addNameIndex(manifest),sourceById=new Map(manifest.map((entry)=>[entry.id,entry.source])),uiDir=hostUiDir();return ids.flatMap((id)=>{if(sourceById.get(id)==="velloo"){let path=helperSourcePath(id);return path?[{id,sources:[{importPath:path,exportName:id,fidelity:"exact",note:"Velloo helper, mounted beside the app's components."}]}]:[]}if(!sourceById.has(id))return[];let addName=addNameOf(id),snapshotPath=join5(componentsDir3,"ui",`${addName}.tsx`);if(CANVAS_ADAPTED_FAMILIES.has(addName)){let installed=uiDir!==null&&hostComponentFile(uiDir,addName,id)!==null;return[{id,sources:[{importPath:snapshotPath,exportName:id,fidelity:"adapted",note:installed?"The app's own component uses portals or runtime state; Velloo renders its canvas-safe counterpart inline.":"The app has not installed this component. Velloo renders its own canvas-safe one inline; install it in the app before implementing a design that uses it."}]}]}let hostPath=uiDir?hostComponentFile(uiDir,addName,id):null;return[{id,sources:[...hostPath?[{importPath:hostPath,exportName:id,fidelity:"exact",preflight:!0,note:"Rendered directly from the app's shadcn source file."}]:[],{importPath:snapshotPath,exportName:id,fidelity:"fallback",note:hostPath?"The app source did not compile for the browser canvas; using the bundled canvas fallback.":"The app does not install this component under this name (it may be renamed in its own file); using the bundled canvas fallback."}]}]})},sourceDirs:()=>{let uiDir=hostUiDir()??plannedUiDir;return uiDir?[uiDir]:[]}}}}var CANVAS_ADAPTED_FAMILIES=new Set(["alert-dialog","calendar","carousel","chart","dialog","dropdown-menu","popover","select","sheet","sonner","tooltip"]),familyExportCache=new Map;function familyExports(path){let names=familyExportCache.get(path);if(!names){names=new Set;try{let source=readFileSync4(path,"utf8");for(let id of Object.keys(registry2))if(exportsName(source,id))names.add(id)}catch{}familyExportCache.set(path,names)}return names}export{createProvider};
|
|
2
|
+
import{helperSourcePath}from"./chunk-mseawt58.js";import"./chunk-ghwy99py.js";import{snapshotVersion2,entryCssPath3,componentsDir3}from"./chunk-tjy12q19.js";import{registry2}from"./chunk-f2793pj6.js";import{TAILWIND_CLASSNAME}from"./chunk-hhkjqvww.js";import"./chunk-12pgt83m.js";import{readFile}from"fs/promises";import{join}from"path";async function readManifest(cacheDir){let raw=await readFile(join(cacheDir,"manifest.json"),"utf8");return JSON.parse(raw)}import{existsSync as existsSync4,readFileSync as readFileSync4}from"fs";import{basename,join as join5}from"path";import{existsSync as existsSync3,readFileSync as readFileSync3}from"fs";import{join as join4}from"path";import{existsSync,readFileSync}from"fs";import{join as join2}from"path";function exportsName(source,id){let name=id.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");if(new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function|const|let|var|class)\\s+${name}\\b`).test(source))return!0;for(let match of source.matchAll(/\bexport\s*\{([^}]*)\}/g)){let clause=match[1]??"";for(let part of clause.split(",")){let local=part.trim().split(/\s+as\s+/);if((local.length>1?local[1]:local[0])?.trim()===id)return!0}}return/\bexport\s+\*\s+from\b/.test(source)}function hostComponentFile(uiDir,addName,id){for(let extension of["tsx","ts","jsx","js"]){let path=join2(uiDir,`${addName}.${extension}`);if(!existsSync(path))continue;try{return exportsName(readFileSync(path,"utf8"),id)?path:null}catch{return null}}return null}import{existsSync as existsSync2,readdirSync,readFileSync as readFileSync2}from"fs";import{join as join3,resolve}from"path";function kebab(id){return id.replace(/(?<!^)(?=[A-Z])/g,"-").toLowerCase()}function addNameIndex(manifest){let byId=new Map(manifest.flatMap((c)=>c.registryName?[[c.id,c.registryName]]:[]));return(id)=>byId.get(id)??kebab(id)}function findUiDir(hostAppRoot){let candidates=[];try{let cj=JSON.parse(readFileSync2(join3(hostAppRoot,"components.json"),"utf8")),fromAlias=(alias,suffix="")=>{if(!alias)return;let srcBase=existsSync2(join3(hostAppRoot,"src"))?"src":"",rel=alias.replace(/^@\//,srcBase?`${srcBase}/`:"")+suffix;candidates.push(resolve(hostAppRoot,rel))};fromAlias(cj.aliases?.ui),fromAlias(cj.aliases?.components,"/ui")}catch{}candidates.push(resolve(hostAppRoot,"src/components/ui"),resolve(hostAppRoot,"components/ui"),resolve(hostAppRoot,"app/components/ui"));for(let dir of candidates)if(existsSync2(dir))return dir;return null}function installedAddNames(dir){if(!dir)return new Set;try{return new Set(readdirSync(dir).filter((f)=>f.endsWith(".tsx")||f.endsWith(".ts")).map((f)=>f.replace(/\.(tsx|ts)$/,"")))}catch{return new Set}}function enrichManifestFromHost(manifest,uiDir){if(!uiDir)return manifest;let addNameOf=addNameIndex(manifest),byFile=new Map,read=(addName)=>{if(byFile.has(addName))return byFile.get(addName)??null;for(let ext of["tsx","ts","jsx","js"]){let path=join4(uiDir,`${addName}.${ext}`);if(!existsSync3(path))continue;let source=readFileSync3(path,"utf8");return byFile.set(addName,source),source}return byFile.set(addName,""),null};return manifest.map((descriptor)=>{if(descriptor.source==="velloo")return descriptor;let source=read(addNameOf(descriptor.id));if(!source||!exportsName(source,descriptor.id))return descriptor;let additions=[...variantPropsFor(source,descriptor.id===descriptor.family),...declaredPropsFor(source,descriptor.id)];if(additions.length===0)return descriptor;let props=[...descriptor.props];for(let addition of additions){let index=props.findIndex((prop)=>prop.name===addition.name);if(index>=0)props[index]={...props[index],...addition};else props.push(addition)}return{...descriptor,props,designModeNotes:appendNote(descriptor.designModeNotes,"Props include variants detected from the app's component source.")}})}function appendNote(current,next){return current?`${current} ${next}`:next}function variantPropsFor(source,primary){let out=[];if(!primary)return out;let variantsAt=source.indexOf("variants:");if(variantsAt<0)return out;let open=source.indexOf("{",variantsAt),block=balancedBlock(source,open);if(!block)return out;for(let[name,value]of objectEntries(block.slice(1,-1))){let values=objectEntries(value.slice(1,-1)).map(([key])=>key);if(values.length===0)continue;out.push({name,type:`${values.map((value)=>JSON.stringify(value)).join(" | ")} | undefined`,optional:!0,control:"enum",enumValues:values,...defaultVariant(source,name)?{defaultValue:defaultVariant(source,name)}:{}})}return out}function defaultVariant(source,name){let at=source.indexOf("defaultVariants:");if(at<0)return;let open=source.indexOf("{",at),block=balancedBlock(source,open);if(!block)return;return objectEntries(block.slice(1,-1)).find(([key])=>key===name)?.[1].replace(/["'`\s]/g,"")}function declaredPropsFor(source,componentId){let match=[new RegExp(`interface\\s+${componentId}Props(?:\\s+extends[^\\{]+)?\\s*\\{`,"m"),new RegExp(`type\\s+${componentId}Props\\s*=.*?&\\s*\\{`,"m")].map((pattern)=>pattern.exec(source)).find(Boolean);if(!match||match.index===void 0)return[];let open=source.indexOf("{",match.index),block=balancedBlock(source,open);if(!block)return[];let out=[],property=/(?:^|[;{\n])\s*([A-Za-z_$][\w$]*)(\?)?\s*:\s*([^;\n}]+)/gm;for(let found of block.slice(1,-1).matchAll(property)){let name=found[1],raw=found[3]?.trim();if(!name||!raw||name==="children")continue;let enumValues=[...raw.matchAll(/["']([^"']+)["']/g)].map((value)=>value[1]),optional=found[2]==="?"||raw.includes("undefined");out.push({name,type:optional&&!raw.includes("undefined")?`${raw} | undefined`:raw,optional,control:enumValues.length>0?"enum":/boolean/.test(raw)?"boolean":/number/.test(raw)?"number":"string",...enumValues.length>0?{enumValues}:{}})}return out}function balancedBlock(source,open){if(open<0||source[open]!=="{")return null;let depth=0,quote="";for(let index=open;index<source.length;index++){let char=source[index],prev=source[index-1];if(quote){if(char===quote&&prev!=="\\")quote="";continue}if(char==='"'||char==="'"||char==="`"){quote=char;continue}if(char==="{")depth++;if(char==="}"&&--depth===0)return source.slice(open,index+1)}return null}function objectEntries(body){let out=[],index=0;while(index<body.length){while(index<body.length&&/[\s,]/.test(body[index]))index++;let keyMatch=/^(?:["']([^"']+)["']|([A-Za-z_$][\w$-]*))\s*:/.exec(body.slice(index));if(!keyMatch)break;let key=keyMatch[1]??keyMatch[2];if(!key)break;index+=keyMatch[0].length;while(/\s/.test(body[index]??""))index++;if(body[index]==="{"){let block=balancedBlock(body,index);if(!block)break;out.push([key,block]),index+=block.length;continue}let end=valueEnd(body,index);out.push([key,body.slice(index,end).trim()]),index=end+1}return out}function valueEnd(body,start){let quote="",depth=0;for(let index=start;index<body.length;index++){let char=body[index];if(quote){if(char===quote&&body[index-1]!=="\\")quote=""}else if(char==='"'||char==="'"||char==="`")quote=char;else if("([{".includes(char))depth++;else if(")]}".includes(char))depth--;else if(char===","&&depth===0)return index}return body.length}function createProvider(opts={}){let cacheDir=opts.cacheDir,plannedUiDir=cacheDir?join5(cacheDir,"ui"):void 0,hostUiDir=()=>plannedUiDir&&existsSync4(plannedUiDir)?plannedUiDir:opts.hostAppRoot?findUiDir(opts.hostAppRoot):null,componentsDir=componentsDir3,version=opts.version??snapshotVersion2,loadManifest=async()=>{if(cacheDir&&existsSync4(join5(cacheDir,"manifest.json")))return enrichManifestFromHost(await readManifest(cacheDir),hostUiDir());let{loadManifest:snapshotManifest}=await import("./chunk-84qjmqmz.js");return enrichManifestFromHost(await snapshotManifest(),hostUiDir())};return{id:"shadcn-upstream",version,componentsDir,styleEntryPath:entryCssPath3,registry:registry2,loadManifest,label:`shadcn (upstream @ ${version})`,styleChannel:TAILWIND_CLASSNAME,styleChannels:["tailwind-classname"],mcpIntro:()=>['**This folder targets shadcn/ui with Tailwind.** The canvas imports client-safe components directly from the app\'s `components/ui`, adapts overlay families to stay inline, and falls back per component when a file is missing \u2014 `component_status` says which, before you claim fidelity. Style with a className string: `update_props { style: "flex gap-4 p-6" }`.',""],catalog:async()=>{let manifest=await loadManifest(),addNameOf=addNameIndex(manifest),shadcn=manifest.filter((c)=>c.source!=="velloo"),present=installedAddNames(hostUiDir());return shadcn.map((c)=>{let addName=addNameOf(c.id);return{id:c.id,installed:present.has(addName),importPath:`@/components/ui/${addName}`}})},ownedModules:{dirs:()=>{let uiDir=hostUiDir();return uiDir?[uiDir]:[]},supplies:(file,exportName)=>{let family=basename(file).replace(/\.[jt]sx?$/,""),counterpart=join5(componentsDir3,"ui",`${family}.tsx`);return familyExports(counterpart).has(exportName)}},canvasBundleSpec:{styleRuntime:{kind:"none"},components:async(ids)=>{let manifest=await loadManifest(),addNameOf=addNameIndex(manifest),sourceById=new Map(manifest.map((entry)=>[entry.id,entry.source])),uiDir=hostUiDir();return ids.flatMap((id)=>{if(sourceById.get(id)==="velloo"){let path=helperSourcePath(id);return path?[{id,sources:[{importPath:path,exportName:id,fidelity:"exact",note:"Velloo helper, mounted beside the app's components."}]}]:[]}if(!sourceById.has(id))return[];let addName=addNameOf(id),snapshotPath=join5(componentsDir3,"ui",`${addName}.tsx`);if(CANVAS_ADAPTED_FAMILIES.has(addName)){let installed=uiDir!==null&&hostComponentFile(uiDir,addName,id)!==null;return[{id,sources:[{importPath:snapshotPath,exportName:id,fidelity:"adapted",note:installed?"The app's own component uses portals or runtime state; Velloo renders its canvas-safe counterpart inline.":"The app has not installed this component. Velloo renders its own canvas-safe one inline; install it in the app before implementing a design that uses it."}]}]}let hostPath=uiDir?hostComponentFile(uiDir,addName,id):null;return[{id,sources:[...hostPath?[{importPath:hostPath,exportName:id,fidelity:"exact",preflight:!0,note:"Rendered directly from the app's shadcn source file."}]:[],{importPath:snapshotPath,exportName:id,fidelity:"fallback",note:hostPath?"The app source did not compile for the browser canvas; using the bundled canvas fallback.":"The app does not install this component under this name (it may be renamed in its own file); using the bundled canvas fallback."}]}]})},sourceDirs:()=>{let uiDir=hostUiDir()??plannedUiDir;return uiDir?[uiDir]:[]}}}}var CANVAS_ADAPTED_FAMILIES=new Set(["alert-dialog","calendar","carousel","chart","dialog","dropdown-menu","popover","select","sheet","sonner","tooltip"]),familyExportCache=new Map;function familyExports(path){let names=familyExportCache.get(path);if(!names){names=new Set;try{let source=readFileSync4(path,"utf8");for(let id of Object.keys(registry2))if(exportsName(source,id))names.add(id)}catch{}familyExportCache.set(path,names)}return names}export{createProvider};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{string,number,object,CURRENT_SCHEMA_VERSION,schemaVersionOf}from"./chunk-
|
|
2
|
+
import{string,number,object,CURRENT_SCHEMA_VERSION,schemaVersionOf}from"./chunk-ghwy99py.js";import{writeJsonAtomic}from"./chunk-mseawt58.js";import{TOOL_VERSION}from"./chunk-d6a6198y.js";import{defaultCloudUrl}from"./chunk-5ts4fvd9.js";import{selfCommand}from"./chunk-skk6yvnw.js";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";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)`)}function spawnOutliving(cmd,output){let proc=Bun.spawn(cmd,{stdin:"ignore",stdout:output,stderr:output,detached:!0,windowsHide:!0});return proc.unref(),proc}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));return selfCommand(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");return{exited:spawnOutliving(daemonSpawnCmd(root,preferredPort,host),log).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
|
-
`)}`}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<
|
|
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<80;i++){if(!await isLive(rec))break;await new Promise((r)=>setTimeout(r,100))}return removeLock(root),await registryRemove(root),!0}
|
|
6
6
|
export{assertLoopbackHost,daemonRoot,DesignFolderFormatError,assertFolderFormatCurrent,readLock,writeLock,removeLock,readRememberedPort,rememberPort,portCandidates,isLive,registryUpsert,registryRemove,listDaemons,ensureDaemon,stopDaemon};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{writeJsonAtomic,managedDesignPath,managedDesignId,localDesignOf,writeLocalDesign,removeLocalDesign,resolveAppPath,hostAppRootFrom,recordedDesignName,pickDesign,readRepoFeedback,writeRepoFeedback,writeFeedbackContactOk,loadDesignFolder,hostIsLoopback,requestIsLocal,serveCanvasApp}from"./chunk-mseawt58.js";import{string,number,boolean,array,object,strictObject,_enum,designNameIssue,APP_PATH_PREFIX,ConfigSchema}from"./chunk-ghwy99py.js";import{sessionStatePath,HeadedBrowserMissingError,startCaptureSession}from"./chunk-hhkjqvww.js";import{AgentSessionSummarySchema}from"./chunk-ak57bgnh.js";import{isCancel,confirm,select,findManifest,manifestListing,findDesigns,withoutDesign,isWithin,findGitRoot,chooseDesignName,registerDesign,registerLocalDesign,isDesignFolderSync,isEmptyOrMissingSync,hasDesignConfig}from"./chunk-qe6pp4p6.js";import{PROJECT_AGENT_IDS,manualSetupText,agentOptions,connect}from"./chunk-46jg9aqy.js";import{fail}from"./chunk-s23gtejf.js";import{installedVellooBin,updateStatus,upgradeInstalledVelloo}from"./chunk-hrvqwp8z.js";import{detectHost,findComponentsDir}from"./chunk-nccfyeyy.js";import{dirExists,hasAppToMatch,DEFAULT_THEME_PRESET,NEUTRAL_THEME_PRESET,isValidPreset,WIZARD_PROVIDERS,LIBRARY_IDS,DEFAULT_LIBRARY_ID,interactiveLibraryChoices,planInstall,scanAdoption,libraryForApp,libraryForSample,DEFAULT_STACK_ID,stackForFramework,isValidStack,discoverScanRoots,scanApps,importThemeFromDesignMd,isEmptyOrMissing,resolveTheme,buildScaffold,writeScaffold,displayPath,printSummary,printThemeImport,printExitInstructions,printScreenshotReadiness}from"./chunk-75gfv9g4.js";import{canvasDistPath,createCanvasAuth}from"./chunk-12vc3f1h.js";import{daemonRoot,ensureDaemon,stopDaemon}from"./chunk-jc95phff.js";import{openUrl}from"./chunk-hggap8fq.js";import{defaultCloudUrl}from"./chunk-5ts4fvd9.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{randomUUID as randomUUID4}from"crypto";import{resolve as resolve7}from"path";var import_picocolors7=__toESM(require_picocolors(),1);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}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=resolve(args.folder??"."),folder=resolve(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??findComponentsDir(appRoot)??"src/components/ui",initialContent,themePreset,...args.designMd===!1?{useDesignMd:!1}:{},...stack?{stack}:{},...screenName?{screenName}:{},...componentDescription?{componentDescription}:{},...customRequest?{customRequest}:{}}}import{relative,sep}from"path";var SCREENS_PLACEHOLDER="{{screens}}";function appContext(answers){let appRels=[...new Set((answers.selectedRoutes??[]).map((r)=>r.appRel).filter(Boolean))];if(appRels.length>1)return[`It's a monorepo with ${appRels.length} apps (${appRels.map((r)=>`\`${r}\``).join(", ")}), one board each.`];let uiRel=relative(answers.appRoot,answers.scanRoot).split(sep).join("/");return uiRel&&!uiRel.startsWith("..")?[`The app lives in \`${uiRel}/\`.`]:[]}var SETUP="Set Velloo up for my app (theme, fonts, components) before anything else.",SETUP_ONLY="Set Velloo up for my app (theme, fonts, components): get its own components rendering on the canvas, check every one of them, then propose its design language for me to review.",RECREATE_BAR="keep fixing `compare_to_url`'s top mismatches and don't stop below 0.9. Start the app yourself.",ALTERNATIVES="Explore a few different directions beside it on the same board.";function steps(answers,...rest){return[...hasAppToMatch(answers)?[SETUP]:[],...rest].map((step,i)=>`${i+1}. ${step}`)}function wantsHandoff(answers){switch(answers.initialContent){case"scan":case"redesign-screen":case"component":case"custom":return!0;default:return!1}}function buildHandoffPrompt(answers,screens){return[...goalLines(answers,screens),...appContext(answers)].join(`
|
|
3
|
+
`)}function goalLines(answers,screens){switch(answers.initialContent){case"redesign-screen":return[`Use Velloo to redesign **${answers.screenName?.trim()||screens[0]?.name||screens[0]?.id||"the chosen screen"}**:`,...steps(answers,`Recreate the whole real page on its scaffolded screen \u2014 ${RECREATE_BAR}`,ALTERNATIVES)];case"component":return[`Use Velloo to redesign **${answers.componentDescription?.trim()||"the component"}**:`,...steps(answers,`Recreate it on the scaffolded board \u2014 ${RECREATE_BAR}`,ALTERNATIVES)];case"custom":return["Use Velloo to design this:","",answers.customRequest?.trim()||"the design I described","",...steps(answers,...answers.captured?.length?[`Recreate the pages I captured (\`list_captures\`): ${answers.captured.join(", ")}.`]:answers.captureUrl?[`Capture ${answers.captureUrl} with \`start_capture_session\` \u2014 I'll sign in if needed.`]:[],"Design it, with alternative directions side by side on the board.")];default:return[`Use Velloo to recreate my app's pages, ${screens.length>0?"one scaffolded screen each:":"one screen per route:"}`,...screens.length>0?[SCREENS_PLACEHOLDER]:[],...steps(answers,`Recreate each page top to bottom, the most important first \u2014 ${RECREATE_BAR}`,"Each screen has a desktop and a mobile frame; make both hold up.")]}}function expandHandoffPrompt(prompt,screens){if(!prompt.includes(SCREENS_PLACEHOLDER))return prompt;let list=screens.map((s)=>` - ${s.name||s.id}`).join(`
|
|
4
|
+
`);return prompt.split(SCREENS_PLACEHOLDER).join(list)}function firstPrompt(answers,screens){if(wantsHandoff(answers))return expandHandoffPrompt(buildHandoffPrompt(answers,screens),screens);return hasAppToMatch(answers)?SETUP_ONLY:void 0}var import_picocolors2=__toESM(require_picocolors(),1);function printAgentHandoff(answers,scaffold){if(!wantsHandoff(answers))return;let{screens}=scaffold,prompt=expandHandoffPrompt(buildHandoffPrompt(answers,screens),screens);console.log(import_picocolors2.default.bold(" Start your agent")),console.log(import_picocolors2.default.dim(" Paste this to your AI agent:")),console.log("");for(let line of prompt.split(`
|
|
5
|
+
`))console.log(import_picocolors2.default.cyan(` ${line}`));console.log("")}var import_picocolors3=__toESM(require_picocolors(),1);import{homedir}from"os";import{sep as sep2}from"path";var NOT_WIRED={wiredIds:[]};async function applyAgentWiring(folder,wiring){let result;if(wiring.agents.length>0)try{result=await connect({designFolder:folder,agents:wiring.agents,installSkill:!0})}catch{result=void 0}if(wiring.manual){console.log(""),console.log(import_picocolors3.default.bold(" Manual MCP setup"));for(let line of manualSetupText().split(`
|
|
6
|
+
`))console.log(` ${line}`)}return{connected:result,wiredIds:[...wiring.preWired,...result?.configs.map((c)=>c.agent)??[]]}}function printWired(outcome){let connected=outcome.connected;if(!connected||connected.configs.length===0)return;let wired=connected.configs.map((c)=>c.agent).join(" + "),under=(dir)=>connected.configs.every((c)=>c.path.startsWith(dir.endsWith(sep2)?dir:dir+sep2)),where=under(connected.projectRoot)?`MCP config under ${connected.projectRoot}`:under(homedir())?"MCP config in your user settings, for every project":`MCP config in ${connected.configs.map((c)=>c.path).join(", ")}`;if(console.log(""),console.log(import_picocolors3.default.bold(" Agents wired")),console.log(` ${import_picocolors3.default.green("\u2713")} ${wired} ${import_picocolors3.default.dim(`(${where})`)}`),connected.plugin)console.log(import_picocolors3.default.dim(` + Claude Code plugin (skills \xB7 commands \xB7 subagents), enabled in ${connected.plugin.settingsPath}`));if(connected.skills?.length)console.log(import_picocolors3.default.dim(` + ${connected.skills.length} agent skill${connected.skills.length===1?"":"s"} (.agents/skills)`));if(connected.geminiExtension)console.log(import_picocolors3.default.dim(` + Gemini extension${connected.geminiExtension.linked?"":` (link it: gemini extensions link ${connected.geminiExtension.dir})`}`));if(connected.cursorRules?.installed)console.log(import_picocolors3.default.dim(" + Cursor rule"))}import{randomUUID as randomUUID3}from"crypto";import{basename as basename2,resolve as resolve6}from"path";var import_picocolors6=__toESM(require_picocolors(),1);import{existsSync}from"fs";import{readFile}from"fs/promises";import{join,relative as relative2,resolve as resolve2,sep as sep3}from"path";function isSentinel(path){return path==="binary"||path.startsWith("~/")}function toPosix(path){return path.split(sep3).join("/")||"."}function isUnder(parent,child){let rel=relative2(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=resolve2(designFolder),local=localDesignOf(folder);if(local){let path=resolve2(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):resolve2(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"))}}function pathSlots(config){let slots=[];for(let[name,app]of Object.entries(config.hostApps??{}))slots.push({field:`hostApps.${name}.root`,kind:"folder",value:app.root,set:(next)=>{app.root=next}});for(let[name,library]of Object.entries(config.libraries)){if(library.source!=="in-repo")continue;slots.push({field:`libraries.${name}.componentsPath`,kind:"folder",value:library.componentsPath,set:(next)=>{library.componentsPath=next}})}let aliases=config.hostApp?.aliases;for(let[pattern,target]of Object.entries(aliases??{}))slots.push({field:`hostApp.aliases["${pattern}"]`,kind:"app",value:target,set:(next)=>{if(aliases)aliases[pattern]=next}});let include=config.hostApp?.components?.include;for(let[index,entry]of(include??[]).entries())slots.push({field:`hostApp.components.include[${index}]`,kind:"app",value:entry,onlyIfExists:!0,set:(next)=>{if(include)include[index]=next}});let codegen=config.codegen;if(codegen?.componentsDir!==void 0)slots.push({field:"codegen.componentsDir",kind:"app",value:codegen.componentsDir,set:(next)=>{codegen.componentsDir=next}});return slots}async function planAppRootChange(designFolder,newRoot){let folder=resolve2(designFolder),current=await recordedAppRoot(folder),from=current.path,to=resolve2(newRoot),change={from,to,rewritten:[]},local=current.kind==="local"&¤t.localId!==void 0;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.`);change.local={id:current.localId,root:current.checkout}}let config=await readConfig(folder);if(!local){let stored=config.hostApp?.root,next=toPosix(relative2(folder,to));if(stored===void 0||!isSentinel(stored)&&next!==stored)change.rewritten.push({field:"hostApp.root",from:stored??"(unset)",to:next})}let nested=isUnder(from,to)||isUnder(to,from),staysPut=(abs,moved)=>{if(!nested)return!1;let here=existsSync(abs);if(here!==existsSync(moved))return here;return isUnder(to,abs)};for(let slot of pathSlots(config)){if(isSentinel(slot.value))continue;let appRef=slot.kind==="folder"&&slot.value.startsWith(APP_PATH_PREFIX),tail=slot.kind==="app"?/\/?\*?$/.exec(slot.value)?.[0]??"":"",stored=slot.value.slice(0,slot.value.length-tail.length),abs=slot.kind==="app"?resolve2(from,stored):resolveAppPath(folder,stored);if(!isUnder(from,abs))continue;if(slot.onlyIfExists&&!existsSync(abs))continue;let stays=staysPut(abs,join(to,relative2(from,abs))),followsRoot=slot.kind==="app"||appRef;if(stays!==followsRoot)continue;let next;if(!stays)next=toPosix(relative2(folder,join(to,relative2(from,abs))));else{let rel=abs===to?"":toPosix(relative2(to,abs));if(appRef)next=`${APP_PATH_PREFIX}${rel||"."}`;else next=rel===""?tail.replace(/^\//,"")||".":`${rel}${tail}`}if(next!==slot.value)change.rewritten.push({field:slot.field,from:slot.value,to:next})}return change}async function applyAppRootChange(designFolder,change){let folder=resolve2(designFolder);if(change.rewritten.length>0){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 slot of pathSlots(config)){let next=patch.get(slot.field);if(next!==void 0)slot.set(next)}await writeJsonAtomic(join(folder,".design","config.json"),ConfigSchema.parse(config))}if(change.local)await writeLocalDesign(change.local.id,{root:change.local.root,appRoot:change.to,...managedDesignId(folder)?{}:{designPath:folder}})}async function appRootCandidates(near){let start=resolve2(near),from=(await findManifest(start).catch(()=>null))?.dir??await findGitRoot(start)??start;return(await discoverScanRoots(from)).map((app)=>({dir:app.dir,rel:relative2(from,app.dir)||"."}))}async function appRootIsNotAnApp(appRoot){let abs=resolve2(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=resolve2(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{readFile as readFile2}from"fs/promises";import{join as join2}from"path";async function readConfig2(folder){return JSON.parse(await readFile2(join2(folder,".design","config.json"),"utf8"))??{}}async function inheritedFromFolder(folder){try{let config=await readConfig2(folder),libraries=config.libraries??{},defaultLibrary=typeof config.defaultLibrary==="string"?config.defaultLibrary:"",codegen=config.codegen??{};return{library:libraries[defaultLibrary]?.id??Object.values(libraries)[0]?.id,componentsDir:codegen.componentsDir}}catch{return{library:void 0,componentsDir:void 0}}}import{dirname as dirname2,relative as relative7,resolve as resolve5}from"path";var InitGoalSchema=_enum(["setup","redesign-screen","redesign-component","custom","capture-site","sample","blank"]),InitStorageSchema=_enum(["repository","external","custom"]),InitRouteSchema=object({id:string(),name:string(),routePath:string(),app:string().optional()}),InitAppSchema=object({found:boolean(),library:object({id:string(),label:string()}),libraryNote:string(),uiLibrary:string().optional(),tailwind:number().int().optional(),themeSource:string().optional(),designMd:object({path:string(),system:string(),mapped:number().int(),total:number().int(),recommended:boolean()}).optional(),routes:array(InitRouteSchema)}),InitGoalOptionSchema=object({value:InitGoalSchema,label:string(),hint:string()}),InitAgentScopeSchema=object({id:string(),wired:boolean()}),InitAgentOptionSchema=object({label:string(),detected:boolean(),global:InitAgentScopeSchema.optional(),project:InitAgentScopeSchema.optional()}),InitStateSchema=object({launchRoot:string(),appRoot:string(),apps:array(object({dir:string(),rel:string()})),app:InitAppSchema,existing:object({name:string(),folder:string()}).optional(),fixed:object({storage:InitStorageSchema,folder:string()}).optional(),libraries:array(object({id:string(),label:string(),hint:string()})),storage:object({repository:string(),external:string()}),suggestedName:string(),goals:array(InitGoalOptionSchema),agents:array(InitAgentOptionSchema),feedbackAnswered:boolean()}),PlaceSchema={appRoot:string().min(1).max(4096),storage:InitStorageSchema,folder:string().max(4096).optional(),name:string().max(200).optional()},InitCheckBodySchema=strictObject(PlaceSchema),InitPlaceSchema=object({folder:string(),local:boolean(),problem:string().optional()}),InitCreateBodySchema=strictObject({...PlaceSchema,goal:InitGoalSchema,routeId:string().max(500).optional(),screenName:string().max(200).optional(),component:string().max(2000).optional(),request:string().max(1e4).optional(),captureUrl:string().max(2000).optional(),library:string().max(100).optional(),useDesignMd:boolean().optional(),agents:array(string().max(100)).max(20),feedback:strictObject({enabled:boolean(),contactOk:boolean()}).optional()}),InitCreatedSchema=object({name:string(),folder:string(),prompt:string().optional(),setup:boolean()}),InitModelsBodySchema=strictObject({provider:string().max(100)}),InitModelsSchema=object({models:array(object({id:string(),label:string(),description:string().optional()})),model:string().optional()}),InitOpenBodySchema=strictObject({provider:string().max(100).optional(),model:string().max(200).optional(),prompt:string().max(1e5).optional()}),InitCaptureSchema=object({state:_enum(["open","done","unavailable"]),currentUrl:string().optional(),captured:array(object({id:string(),url:string(),title:string()})),problem:string().optional()});import{randomUUID as randomUUID2}from"crypto";import{relative as relative4}from"path";var import_picocolors4=__toESM(require_picocolors(),1);import{createHash,randomUUID}from"crypto";import{existsSync as existsSync2,lstatSync,realpathSync}from"fs";import{cp,lstat,mkdir,readdir,readFile as readFile3,rm}from"fs/promises";import{basename,dirname,join as join3,relative as relative3,resolve as resolve3,sep as sep4}from"path";function physicalDestination(path){if(lstatSync(path,{throwIfNoEntry:!1}))return realpathSync(path);let parent=dirname(path);return join3(physicalDestination(parent),relative3(parent,path))}var RUNTIME=[".design/cache",".velloo","node_modules"];async function rebaseDesignConfig(source,destination,appRoot,external){let config=ConfigSchema.parse(JSON.parse(await readFile3(join3(destination,".design/config.json"),"utf8"))),rebase=(path)=>{if(path==="binary"||path.startsWith("~/"))return path;let abs=resolveAppPath(source,path),withinDesign=relative3(source,abs);if(withinDesign===""||withinDesign!==".."&&!withinDesign.startsWith(`..${sep4}`)&&!/^(?:[\\/]|[A-Za-z]:)/.test(withinDesign))return withinDesign.split(sep4).join("/")||".";return external?`${APP_PATH_PREFIX}${relative3(appRoot,abs).split(sep4).join("/")||"."}`:relative3(destination,abs).split(sep4).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(join3(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=join3(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 readFile3(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()):resolve3(cwd,to??""),physicalTarget=physicalDestination(destination),inRepo=!external&&isWithin(physicalRoot,physicalTarget);if(physicalTarget===physicalSource||physicalTarget.startsWith(physicalSource+sep4)||physicalSource.startsWith(physicalTarget+sep4))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??join3(root,"velloo.json"),before=repo?await readFile3(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=relative3(physicalRoot,physicalTarget).split(sep4).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 readFile3(join3(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):resolve3(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(join3(plan.source,entry),join3(plan.destination,entry),{recursive:!0,errorOnExist:!0,force:!1,filter:(path)=>!RUNTIME.some((p)=>{let rel=relative3(plan.source,path).split(sep4).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 readFile3(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}`)}}class InitError extends Error{}async function createDesign(answers,opts){let sample=answers.initialContent==="sample",scansRoutes=answers.initialContent==="scan"||answers.initialContent==="redesign-screen";if(!answers.detected&&!opts.appRead){let scanned=await scanApps(answers.appRoot,opts.scanDir),primary=scanned.apps[0],app=primary??scanned.roots[0];if(app)answers.scanRoot=app.dir;if(scansRoutes&&scanned.apps.length>1)console.log(import_picocolors4.default.dim(` Found ${scanned.apps.length} apps (${scanned.apps.map((a)=>a.rel||".").join(", ")}) \u2014 one board per app.`));else if(scansRoutes&&primary?.rel&&primary.rel!==".")console.log(import_picocolors4.default.dim(` Scanning UI in ${primary.rel} (app root has no package.json).`));if(scansRoutes&&!answers.selectedRoutes){if(answers.selectedRoutes=answers.initialContent==="redesign-screen"?scanned.routes.slice(0,1):scanned.routes,answers.initialContent==="redesign-screen"&&scanned.routes[0])answers.screenName=answers.screenName??scanned.routes[0].name}let detected=detectHost(answers.scanRoot,answers.appRoot);if(!sample)answers.detected=detected;let adopted=opts.pinnedLibrary?void 0:app?(sample?libraryForSample:libraryForApp)(detected):scanAdoption(detected);if(adopted)answers.library=adopted.library,answers.source=sample?WIZARD_PROVIDERS[adopted.library].defaultSource:"binary",console.log(import_picocolors4.default.dim(` ${adopted.note}`))}let folder=answers.folder;if(!opts.force&&!await isEmptyOrMissing(folder))throw new InitError(`design folder is not empty: ${folder}
|
|
7
|
+
Pick an empty path, or pass --force to scaffold over it.`);let plan;try{plan=planInstall(answers)}catch(err){throw new InitError(err.message)}let{theme,source:themeSource,importedFrom,importedVia,tokenlessStylesheet,designMd,libraryDefaults,fonts,fontsFrom,leftForSetup}=resolveTheme(answers),scaffold;try{scaffold=await buildScaffold(answers,theme)}catch(err){throw new InitError(err.message)}let{launchRoot:root,managedId}=opts,localId=managedId??(isWithin(root,folder)?void 0:randomUUID2()),name;try{name=await chooseDesignName({folder,appRoot:answers.appRoot,storage:localId?managedId?"managed":"chosen":"repository",checkout:root,requested:opts.name})}catch(err){throw new InitError(err.message)}let hostStyles;try{if(hostStyles=await writeScaffold({folder,scaffold,plan,answers,name,local:localId!==void 0,...designMd?{designSystemPath:designMd.path}:{},themeSource}),localId)await rebaseDesignConfig(folder,folder,answers.appRoot,!0)}catch(error){if(localId)throw new InitError(`Could not finish the design at ${folder}. Existing content was retained. Check write permissions and free disk space before retrying. ${error instanceof Error?error.message:error}`);throw error}if(console.log(`velloo: scaffolded ${folder} (${plan.library.id} ${plan.library.version})`),hostStyles){let copied=hostStyles.stored.filter((path)=>path.endsWith(".css")).length;console.log(import_picocolors4.default.dim(copied>0?` Copied ${copied} of the app's stylesheets into the design, so it looks like the app without it running.`:" Found none of the app's stylesheets on disk (built CSS appears once the app is built). Your agent copies them from a captured page with store_host_files."))}try{if(localId)await registerLocalDesign({id:localId,root,appRoot:answers.appRoot,...managedId?{}:{designPath:folder}}),console.log(import_picocolors4.default.dim(` Recorded as local design "${name}" for ${displayPath(root)} \u2014 on this machine only; nothing was added to the repository.`));else{let reg=await registerDesign(folder,root);if(reg.created)console.log(import_picocolors4.default.dim(` Listed design "${reg.name}" in ${relative4(process.cwd(),reg.path)||reg.path}.`))}if(answers.feedback)await writeRepoFeedback(folder,answers.feedback),await writeFeedbackContactOk(answers.feedback.contactOk===!0)}catch(err){if(localId)throw new InitError(`Design retained at ${folder}, but registration failed: ${err.message}`);console.log(import_picocolors4.default.yellow(` Couldn't update the repo manifest: ${err.message}`))}if(printThemeImport(answers.appRoot,{importedFrom,importedVia,libraryDefaults,fonts,fontsFrom,leftForSetup}),tokenlessStylesheet)console.log(import_picocolors4.default.yellow(` Found ${relative4(answers.appRoot,tokenlessStylesheet)}, but no theme tokens in it or in the stylesheets it imports \u2014 your design starts on a preset instead.`)),console.log(import_picocolors4.default.dim(" Ask your agent to `import_theme` from the file that holds your colors (or `set_theme`, if the app is themed in JavaScript)."));if(designMd){let{coverage}=designMd;if(console.log(import_picocolors4.default.dim(` Read the "${designMd.name}" design system \u2014 ${coverage.semantic} of ${coverage.semanticTotal} color roles mapped, and the folder now follows that file (read live, never copied).`)),coverage.semantic<coverage.semanticTotal)console.log(import_picocolors4.default.dim(" The roles it didn't name kept the preset's colors \u2014 ask your agent to `import_theme` from your stylesheet if the canvas looks off."))}if(answers.initialContent==="scan"&&scaffold.screens.length===0)console.log(import_picocolors4.default.dim(" No routes detected \u2014 started you with a blank folder instead of a scan."));printSummary(folder,scaffold,plan,answers,{importedFrom,tokenlessStylesheet,libraryDefaults});let wireOutcome=opts.wiring?await applyAgentWiring(folder,opts.wiring):NOT_WIRED;return{folder,name,scaffold,local:localId!==void 0,wireOutcome}}import{join as join5,resolve as resolve4}from"path";import{join as join4,relative as relative5,sep as sep5}from"path";var DEFAULT_COMPONENTS_DIR="src/components/ui";function componentsDirFor(appRoot,scanRoot=appRoot,inherited){if(inherited)return inherited;let atRoot=findComponentsDir(appRoot);if(atRoot)return atRoot;let nested=scanRoot===appRoot?void 0:findComponentsDir(scanRoot);if(!nested)return DEFAULT_COMPONENTS_DIR;return join4(relative5(appRoot,scanRoot),nested).split(sep5).join("/")}var repositoryFolderName=(run)=>run.secondFolder?"velloo-brand":"velloo";async function placeFor(body,run){let storage=run.fixed?.storage??body.storage,place=storage==="external"?{folder:run.managedFolder,managedId:run.managedId}:{folder:run.fixed?run.fixed.folder:resolve4(run.launchRoot,storage==="custom"?body.folder?.trim()||"":repositoryFolderName(run))};if(storage==="custom"&&!run.fixed&&!body.folder?.trim())return{place,problem:"Enter a folder for the design."};if(isDesignFolderSync(place.folder))return{place,problem:`${place.folder} is already a Velloo design \u2014 pick another folder.`};if(!isEmptyOrMissingSync(place.folder))return{place,problem:`${place.folder} isn't empty \u2014 pick an empty folder.`};let name=body.name?.trim();if(name){let issue=designNameIssue(name);if(issue)return{place,problem:issue};try{await chooseDesignName({folder:place.folder,appRoot:body.appRoot,storage:place.managedId?"managed":isWithin(run.launchRoot,place.folder)?"repository":"chosen",checkout:run.launchRoot,requested:name})}catch(err){return{place,problem:err.message.replace(/ \u2014 pick another --name\.$/,".")}}}return{place}}var CONTENT={setup:"blank","redesign-screen":"redesign-screen","redesign-component":"component",custom:"custom","capture-site":"custom",sample:"sample",blank:"blank"};function captureUrlProblem(url){try{let scheme=new URL(url).protocol;return scheme==="http:"||scheme==="https:"?void 0:"Use an http(s) URL."}catch{return"That doesn't look like a URL."}}function answersFor(body,reading,folder,run){let{appRoot}=body,goal=body.goal,text=(value)=>value?.trim()||void 0,scanRoot=reading.found?reading.scanRoot:appRoot,detected=reading.found?reading.detected:void 0,extra={};if(goal==="redesign-screen"){let route=body.routeId?reading.routes.find((r)=>r.id===body.routeId):void 0,screenName=route?.name??text(body.screenName);if(!screenName)return{problem:"Pick one of your app's screens, or name one."};if(extra.screenName=screenName,route){if(extra.selectedRoutes=[route],route.appRel)scanRoot=join5(appRoot,route.appRel),detected=detectHost(scanRoot,appRoot)}}else if(goal==="redesign-component"){let component=text(body.component);if(!component)return{problem:"Describe the component so the agent knows what to redesign."};extra.componentDescription=component}else if(goal==="custom"){let request=text(body.request);if(!request)return{problem:"Describe what you want designed."};extra.customRequest=request}else if(goal==="capture-site"){let url=text(body.captureUrl),problem=url?captureUrlProblem(url):"Enter the URL of the site to design from.";if(problem||!url)return{problem:problem??"Enter a URL."};extra.captureUrl=url,extra.customRequest=`Recreate the pages I capture from ${url}, then explore alternatives.`}let sample=goal==="sample",chosen=!reading.found&&body.library&&isValidLibraryId(body.library)?body.library:void 0,fromApp=sample?reading.sampleLibrary:detected&&scanRoot!==reading.scanRoot?libraryForApp(detected).library:reading.found?reading.library:goal==="blank"||goal==="setup"?"none":reading.library,library=run.pinnedLibrary??chosen??fromApp,useDesignMd=!sample&&!run.skipDesignMd&&detected?.designMdPath?body.useDesignMd!==!1:void 0;return{answers:{appRoot,scanRoot,folder,library,source:WIZARD_PROVIDERS[library].defaultSource,componentsRelative:componentsDirFor(appRoot,scanRoot,run.inheritComponentsDir),initialContent:CONTENT[goal],themePreset:sample?DEFAULT_THEME_PRESET:NEUTRAL_THEME_PRESET,stack:sample?DEFAULT_STACK_ID:reading.stack,...useDesignMd!==void 0?{useDesignMd}:{},...!sample&&detected?{detected}:{},...extra}}}var SAMPLE={value:"sample",label:"Explore the welcome sample",hint:"A travel app with imagery, dark mode and three agentic trip explorations"};function goalOptions(hasApp){if(!hasApp)return[{value:"capture-site",label:"Design from a live site",hint:"A browser window opens so you can sign in and capture pages"},SAMPLE,{value:"blank",label:"Start blank",hint:"An empty canvas \u2014 you and your agent build from nothing"}];return[{value:"setup",label:"Just set Velloo up",hint:"Find your components, theme and fonts, then propose your design language"},{value:"redesign-screen",label:"Redesign a screen",hint:"Recreate one of your pages, then explore alternatives"},{value:"redesign-component",label:"Redesign a component",hint:"One region or widget \u2014 recreate it, then try variations"},{value:"capture-site",label:"Capture a page",hint:"Open your app in a browser, sign in if needed, capture pages \u2014 then redesign them"},{value:"custom",label:"Design something new",hint:"Describe what you need; your agent starts there"},SAMPLE]}import{relative as relative6,sep as sep6}from"path";var UI_LIBRARY_NAMES={shadcn:"shadcn",html:"Server-rendered HTML",mui:"Material UI",antd:"Ant Design",chakra:"Chakra UI"};async function readApp(appRoot,scanDir){let scanned=await scanApps(appRoot,scanDir).catch(()=>null),primary=scanned?.apps[0],app=primary??scanned?.roots[0],scanRoot=app?.dir??appRoot,detected=detectHost(scanRoot,appRoot),adopted=app?libraryForApp(detected):void 0;return{found:Boolean(app),scanRoot,detected,routes:scanned?.routes??[],stack:primary?stackForFramework(primary.framework):DEFAULT_STACK_ID,library:adopted?.library??DEFAULT_LIBRARY_ID,libraryNote:libraryNote(Boolean(app),detected,adopted?.note),sampleLibrary:app?libraryForSample(detected).library:DEFAULT_LIBRARY_ID}}function libraryNote(app,detected,note){if(!app)return"No app here \u2014 the design starts on the default library.";if(detected.unsupportedUi)return note??"";if(detected.uiLibrary)return"";return"The design starts on Velloo's primitives, and the setup step finds the components your app has."}var rel=(appRoot,path)=>relative6(appRoot,path).split(sep6).join("/");function designMdOffer(appRoot,detected){if(!detected.designMdPath)return;let imported=importThemeFromDesignMd(detected.designMdPath,NEUTRAL_THEME_PRESET);if(!imported)return;let{semantic,semanticTotal}=imported.coverage;return{path:rel(appRoot,detected.designMdPath),system:imported.designSystem,mapped:semantic,total:semanticTotal,recommended:!(semantic<semanticTotal/2&&detected.globalsCssPath)}}function describeApp(appRoot,reading){let{detected}=reading,designMd=designMdOffer(appRoot,detected),uiLibrary=detected.uiLibrary?UI_LIBRARY_NAMES[detected.uiLibrary]:detected.unsupportedUi;return{found:reading.found,library:{id:reading.library,label:WIZARD_PROVIDERS[reading.library].label},libraryNote:reading.libraryNote,...uiLibrary?{uiLibrary}:{},...detected.tailwindMajor?{tailwind:detected.tailwindMajor}:{},...detected.globalsCssPath?{themeSource:rel(appRoot,detected.globalsCssPath)}:{},...designMd?{designMd}:{},routes:reading.routes.map((route)=>({id:route.id,name:route.name,routePath:route.routePath,...route.appRel?{app:route.appRel}:{}}))}}class HttpError extends Error{status;constructor(status,message){super(message);this.status=status}}var json=(body,status=200)=>Response.json(body,{status});async function parse(req,schema){let parsed=schema.safeParse(await req.json().catch(()=>{return}));if(!parsed.success)throw new HttpError(400,parsed.error.issues[0]?.message??"Invalid body.");return parsed.data}async function daemonRequest(rec,method,path,body){let res=await fetch(new URL(path,rec.canvasUrl),{method,...body===void 0?{}:{headers:{"Content-Type":"application/json"},body:JSON.stringify(body)}}),payload=await res.json().catch(()=>{return});if(!res.ok){let message=payload?.error?.message;throw new HttpError(res.status===404?409:res.status,message??`${path}: ${res.status}`)}return payload}async function dropSession(rec,sessionId){let session=`/api/agent/sessions/${encodeURIComponent(sessionId)}`;await daemonRequest(rec,"POST",`${session}/stop`).catch(()=>{return}),await daemonRequest(rec,"DELETE",session).catch(()=>{return})}var CONTROLS_TIMEOUT_MS=20000;async function startInitServer(opts){let auth=opts.auth??createCanvasAuth(defaultCloudUrl()),allowedRoots=new Set([opts.appRoot,...opts.apps.map((a)=>a.dir)]);if(opts.apps.length>0)allowedRoots.add(opts.launchRoot);let readings=new Map,reading=(appRoot)=>{let pending=readings.get(appRoot);if(!pending)pending=readApp(appRoot,appRoot===opts.appRoot?opts.scanDir:void 0),readings.set(appRoot,pending);return pending},appRootOf=(raw)=>{let appRoot=raw?resolve5(raw):opts.appRoot;if(!allowedRoots.has(appRoot))throw new HttpError(400,"That isn't one of this repo's apps.");return appRoot},isLocal=(place)=>Boolean(place.managedId)||!isWithin(opts.launchRoot,place.folder),creating=!1,created,settle,outcome=new Promise((resolve)=>{settle=resolve});async function state(appRoot){let read=await reading(appRoot),suggestedName=await chooseDesignName({folder:opts.managedFolder,appRoot,storage:"managed",checkout:opts.launchRoot}).catch(()=>"design");return{launchRoot:opts.launchRoot,appRoot,apps:opts.apps,app:describeApp(appRoot,read),...opts.existing?{existing:opts.existing}:{},...opts.fixed?{fixed:opts.fixed}:{},libraries:interactiveLibraryChoices().map((c)=>({id:c.value,label:c.label,hint:c.hint})),storage:{repository:repositoryFolderName(opts),external:displayPath(dirname2(opts.managedFolder))},suggestedName,goals:goalOptions(read.found),agents:await agentOptions(opts.launchRoot),feedbackAnswered:await readRepoFeedback(opts.launchRoot)!==null}}async function create(req){if(created||creating)throw new HttpError(409,"The design is already being created.");let body=await parse(req,InitCreateBodySchema),appRoot=appRootOf(body.appRoot),{place,problem}=await placeFor({...body,appRoot},opts);if(problem)throw new HttpError(400,problem);let read=await reading(appRoot),built=answersFor({...body,appRoot},read,place.folder,opts);if("problem"in built)throw new HttpError(400,built.problem);let local=isLocal(place),offered=new Set((await agentOptions(opts.launchRoot)).flatMap((a)=>[...a.global?[a.global.id]:[],...a.project&&!local?[a.project.id]:[]])),agents=body.agents.filter((id)=>offered.has(id)),feedback=body.feedback&&(await auth.status()).loggedIn&&await readRepoFeedback(opts.launchRoot)===null?body.feedback:void 0;creating=!0;try{let design=await createDesign({...built.answers,...feedback?{feedback}:{}},{launchRoot:opts.launchRoot,managedId:place.managedId,name:body.name?.trim()||void 0,pinnedLibrary:Boolean(opts.pinnedLibrary),appRead:!0,wiring:agents.length>0?{agents,manual:!1,preWired:[]}:void 0}),rec=await ensureDaemon(design.folder),prompt=firstPrompt(built.answers,design.scaffold.screens);return created={design,answers:built.answers,goal:body.goal,rec,...prompt?{prompt}:{}},{name:design.name,folder:design.folder,setup:hasAppToMatch(built.answers),...prompt?{prompt}:{}}}finally{creating=!1}}async function providers(){if(!created)throw new HttpError(409,"Create the design first.");let res=await fetch(new URL("/api/agent/providers",created.rec.canvasUrl));if(!res.ok)return{providers:[]};return await res.json()}let prepared;async function startSession(rec,provider){let started=AgentSessionSummarySchema.pick({id:!0}).safeParse((await daemonRequest(rec,"POST","/api/agent/sessions",{provider}))?.session);if(!started.success)throw new HttpError(502,"The agent didn't start.");return started.data.id}async function controlsOf(rec,sessionId){let deadline=Date.now()+CONTROLS_TIMEOUT_MS;while(Date.now()<deadline){let snapshot=await daemonRequest(rec,"GET",`/api/agent/sessions/${encodeURIComponent(sessionId)}`);if(snapshot.controls)return snapshot.controls;await Bun.sleep(250)}return}async function models(req){if(!created)throw new HttpError(409,"Create the design first.");let{provider}=await parse(req,InitModelsBodySchema),session=prepared;if(session?.provider!==provider){if(session)await dropSession(created.rec,session.sessionId);session={provider,sessionId:await startSession(created.rec,provider)},prepared=session}let controls=await controlsOf(created.rec,session.sessionId);return{models:controls?.models??[],...controls?.model?{model:controls.model}:{}}}async function open(req){if(!created)throw new HttpError(409,"Create the design first.");let{rec}=created,body=await parse(req,InitOpenBodySchema),provider=body.provider,prompt=(body.prompt??created.prompt)?.trim(),url=new URL(rec.canvasUrl);if(provider){let sessionId=prepared?.provider===provider?prepared.sessionId:await startSession(rec,provider);if(prepared&&prepared.provider!==provider)await dropSession(rec,prepared.sessionId);prepared=void 0;let session=`/api/agent/sessions/${encodeURIComponent(sessionId)}`;if(body.model){if((await controlsOf(rec,sessionId))?.model!==body.model)await daemonRequest(rec,"POST",`${session}/model`,{model:body.model})}if(prompt)await daemonRequest(rec,"POST",`${session}/turns`,{text:prompt})}else if(prepared)await dropSession(rec,prepared.sessionId),prepared=void 0;if(capture&&!capture.done)await capture.handle.close().catch(()=>{return});if(url.searchParams.set("pane","agent"),created.goal==="setup")url.searchParams.set("view","language");return settle({kind:"created",design:created.design,canvasUrl:rec.canvasUrl,...provider?{provider}:{}}),{url:url.toString()}}let capture,captureProblem;function captureState(){if(!capture)return{state:captureProblem?"unavailable":"done",captured:[],...captureProblem?{problem:captureProblem}:{}};let{handle,done}=capture,currentUrl;try{currentUrl=done?void 0:handle.currentUrl()}catch{currentUrl=void 0}return{state:done?"done":"open",...currentUrl?{currentUrl}:{},captured:handle.captures().filter((m)=>!m.themeOnly).map((m)=>({id:m.id,url:m.finalUrl||m.url,title:m.title}))}}async function startCapture(){let url=created?.answers.captureUrl;if(!created||!url)throw new HttpError(409,"There is no page to capture.");if(capture||captureProblem)return captureState();let folderId=(await loadDesignFolder(created.design.folder)).config.folderId;try{let handle=await startCaptureSession({folderRoot:created.design.folder,...folderId?{folderId}:{},url,sessionStatePath:sessionStatePath(created.design.folder,new URL(url).origin,folderId)}),session={handle,done:!1};capture=session,handle.finished.then(()=>{session.done=!0})}catch(error){captureProblem=error instanceof HeadedBrowserMissingError?error.message:`The browser window didn't open: ${error.message}`}return captureState()}async function finishCapture(){if(!created)throw new HttpError(409,"Create the design first.");let captured=captureState().captured.map((c)=>c.url);if(capture&&!capture.done)await capture.handle.close().catch(()=>{return}),capture.done=!0;if(captured.length>0){let answers={...created.answers,captured,customRequest:`Recreate the pages I captured from ${created.answers.captureUrl}, then explore alternatives.`},prompt=firstPrompt(answers,created.design.scaffold.screens);created={...created,answers,...prompt?{prompt}:{}}}return created.prompt?{prompt:created.prompt}:{}}async function openExisting(){if(!opts.existing)throw new HttpError(404,"There is no design here yet.");let rec=await ensureDaemon(opts.existing.folder);return settle({kind:"opened",folder:opts.existing.folder,canvasUrl:rec.canvasUrl}),{url:rec.canvasUrl}}async function api(req,path){if(req.method==="GET"&&path==="/api/init/state")return json(await state(appRootOf(new URL(req.url).searchParams.get("appRoot"))));if(req.method==="POST"&&path==="/api/init/check"){let body=await parse(req,InitCheckBodySchema),{place,problem}=await placeFor({...body,appRoot:appRootOf(body.appRoot)},opts),answer={folder:place.managedId?displayPath(place.folder):relative7(opts.launchRoot,place.folder)||".",local:isLocal(place),...problem?{problem}:{}};return json(answer)}if(req.method==="POST"&&path==="/api/init/create")return json(await create(req));if(req.method==="GET"&&path==="/api/init/agents")return json(await providers());if(req.method==="POST"&&path==="/api/init/models")return json(await models(req));if(req.method==="POST"&&path==="/api/init/open")return json(await open(req));if(req.method==="GET"&&path==="/api/init/capture")return json(captureState());if(req.method==="POST"&&path==="/api/init/capture/start")return json(await startCapture());if(req.method==="POST"&&path==="/api/init/capture/finish")return json(await finishCapture());if(req.method==="POST"&&path==="/api/init/open-existing")return json(await openExisting());if(req.method==="GET"&&path==="/api/init/auth")return json(await auth.status());if(req.method==="POST"&&path==="/api/init/auth/login")return json(await auth.beginLogin());if(req.method==="POST"&&path==="/api/init/auth/cancel")return await auth.cancelLogin(),json(await auth.status());return json({error:{message:"Not found"}},404)}let fetchHandler=async(req)=>{let url=new URL(req.url),headers={host:req.headers.get("host"),origin:req.headers.get("origin")};if(url.pathname.startsWith("/api/")){if(!requestIsLocal(headers))return new Response("Forbidden",{status:403});try{return await api(req,url.pathname)}catch(error){if(error instanceof HttpError)return json({error:{message:error.message}},error.status);if(error instanceof InitError)return json({error:{message:error.message}},400);return console.error(error),json({error:{message:error.message}},500)}}if(!hostIsLoopback(headers.host))return new Response("Forbidden",{status:403});if(url.pathname==="/")return Response.redirect(new URL("/init",url).toString(),302);return serveCanvasApp(req,opts.canvasDist)},hostname=opts.host??"127.0.0.1",serve=(port)=>Bun.serve({port,hostname,fetch:fetchHandler}),server;try{server=serve(opts.preferredPort??0)}catch{server=serve(0)}return{url:`http://${hostname.includes(":")?`[${hostname}]`:hostname}:${server.port}/init`,outcome,stop:()=>server.stop(!0)}}var import_picocolors5=__toESM(require_picocolors(),1),RGB={amber:[255,171,31],coral:[255,111,77]},ANSI256={amber:214,coral:209},TAGLINE="Decide the design with your agent.",V=[" "," ","oooo ooo"," `88. .8' "," `88..8' "," `888' "," `8' "],E=[" "," "," .ooooo. ","d88' `88b","888ooo888","888 .o","`Y8bod8P'"],L=["oooo ","`888 "," 888 "," 888 "," 888 "," 888 ","o888o"],O=[" "," "," .ooooo. ","d88' `88b","888 888","888 888","`Y8bod8P'"],MARK=[" .o8888888o. ","d88 88b ","88 .o8888888o. ","88 d88 88 88b","88 88 88 88","Y88 88 88P 88"," `o8888888P' 88"," Y88 88P"," `o8888888P' "],MARK_OWN=[" ccccccccccc ","ccc ccc ","cc aaaaaccaaaa ","cc aaa cc aaa","cc aa cc aa","ccc aa ccc aa"," ccccaaccccc aa"," aaa aaa"," aaaaaaaaaaa "];function supportsTruecolor(){let colorterm=process.env.COLORTERM??"";if(/truecolor|24bit/i.test(colorterm))return!0;if(process.env.TERM_PROGRAM==="Apple_Terminal")return!1;let program=process.env.TERM_PROGRAM??"";if(["iTerm.app","vscode","ghostty","WarpTerminal","WezTerm"].includes(program))return!0;return/truecolor|-direct$/i.test(process.env.TERM??"")}function paint(frame,text){if(!text||!import_picocolors5.default.isColorSupported)return text;if(supportsTruecolor()){let[r,g,b]=RGB[frame];return`\x1B[38;2;${r};${g};${b}m${text}\x1B[39m`}return`\x1B[38;5;${ANSI256[frame]}m${text}\x1B[39m`}function paintMarkRow(chars,owners){let out="",i=0;while(i<chars.length){let who=owners[i],j=i+1;while(j<owners.length&&owners[j]===who)j++;let slice=chars.slice(i,j);out+=who==="c"?paint("coral",slice):who==="a"?paint("amber",slice):slice,i=j}return out}function renderLogo(){return[...MARK.map((chars,i)=>{let mark=paintMarkRow(chars,MARK_OWN[i]??""),k=i-1,v=V[k],o=O[k],right=v===void 0||o===void 0?"":` ${import_picocolors5.default.bold(`${v}${E[k]}${L[k]}${L[k]}`)}${paint("amber",o)} ${paint("coral",o)}`;return` ${mark}${right}`}),""," "+import_picocolors5.default.dim(TAGLINE)].join(`
|
|
8
|
+
`)}function printLogo(){console.log(""),console.log(renderLogo()),console.log("")}var INIT_PORT=7299,CTRL_C="\x03";async function existingDesign(appRoot,cliArgs){if(cliArgs.force||cliArgs.external||cliArgs.addFolder||cliArgs.designFolder)return;let designs=await findDesigns(appRoot),picked=designs?pickDesign(designs,appRoot):null,folder=picked&&picked.reason!=="arbitrary"?picked.design.root:resolve6(appRoot,"velloo");if(!await hasDesignConfig(folder))return;return{name:recordedDesignName(folder)??basename2(folder),folder}}function untilDone(url,outcome){let stdin=process.stdin;if(!stdin.isTTY)return outcome;stdin.setRawMode(!0),stdin.resume(),stdin.setEncoding("utf8");let onData=(key)=>{if(key===CTRL_C)stdin.setRawMode(!1),console.log(import_picocolors6.default.dim(`
|
|
9
|
+
Cancelled.`)),process.exit(130);if(key==="\r"||key===`
|
|
10
|
+
`)openUrl(url)};return stdin.on("data",onData),outcome.finally(()=>{stdin.off("data",onData),stdin.setRawMode(!1),stdin.pause()})}async function runBrowserInit(cliArgs){let launchRoot=resolve6(cliArgs.folder??"."),existing=await existingDesign(launchRoot,cliArgs),inherited=existing?await inheritedFromFolder(existing.folder):void 0,apps=cliArgs.folder?null:await appRootIsNotAnApp(launchRoot).catch(()=>null),managedId=randomUUID3(),managedFolder=managedDesignPath(managedId),pinned=cliArgs.library&&isValidLibraryId(cliArgs.library)?cliArgs.library:inherited?.library&&isValidLibraryId(inherited.library)?inherited.library:void 0;printLogo();let server=await startInitServer({launchRoot,appRoot:apps?.[0]?.dir??launchRoot,apps:apps?[...apps,{dir:launchRoot,rel:"."}]:[],existing,scanDir:cliArgs.scanDir,pinnedLibrary:pinned,inheritComponentsDir:inherited?.componentsDir,skipDesignMd:cliArgs.designMd===!1,...cliArgs.external?{fixed:{storage:"external",folder:managedFolder}}:cliArgs.designFolder?{fixed:{storage:"custom",folder:resolve6(launchRoot,cliArgs.designFolder)}}:{},secondFolder:Boolean(existing||cliArgs.addFolder),managedId,managedFolder,canvasDist:canvasDistPath,preferredPort:INIT_PORT});console.log(` Set up Velloo in your browser: ${import_picocolors6.default.cyan(server.url)}`),console.log(import_picocolors6.default.dim(` Press ${import_picocolors6.default.bold("Enter")} to open it \xB7 Ctrl+C to cancel`)),console.log("");let outcome=await untilDone(server.url,server.outcome);if(setTimeout(()=>server.stop(),1000),outcome.kind==="opened")console.log(`velloo: opened ${displayPath(outcome.folder)} at ${outcome.canvasUrl}`);else{let{design}=outcome;if(printWired(design.wireOutcome),console.log(""),console.log(import_picocolors6.default.bold(` ${design.name} is ready`)),console.log(` Canvas: ${import_picocolors6.default.cyan(outcome.canvasUrl)}`),console.log(` Design: ${displayPath(design.folder)}`),outcome.provider)console.log(import_picocolors6.default.dim(" Your agent is setting it up in the canvas's Agent tab."));console.log(""),await printScreenshotReadiness()}console.log(import_picocolors6.default.dim(" The canvas keeps running in the background while it's open. Reopen it with `velloo run`; stop it with `velloo stop`."))}var init_default=defineCommand({meta:{name:"init",description:"Set up a Velloo design \u2014 opens a setup page in your browser"},args:{folder:{type:"positional",required:!1,description:"Your app root \u2014 where Velloo installs (default: current directory)"},designFolder:{type:"string",description:"Design folder, relative to the app root (default: velloo)"},external:{type:"boolean",description:"Keep the design in Velloo-managed storage outside the application repository"},force:{type:"boolean",default:!1,description:"Allow scaffolding into a non-empty design folder"},nonInteractive:{type:"boolean",default:!1,description:"Skip the browser setup page and use defaults / flags"},connect:{type:"boolean",default:!0,description:"Wire your AI agents' MCP config + guidance \u2014 Claude Code / Cursor / Codex / Continue; non-interactive default wires Claude Code + Cursor (use --no-connect to skip)"},start:{type:"string",description:"Goal: redesign-screen | redesign-component | custom | sample | blank | scan (legacy multi-route)"},scanDir:{type:"string",description:"Subfolder to scan when your UI isn't at the root (e.g. web/frontend). Auto-detected if omitted."},library:{type:"string",description:`Component library: ${LIBRARY_IDS.join(" | ")} (default ${DEFAULT_LIBRARY_ID})`},componentsDir:{type:"string",description:"Upstream components subfolder inside your app (default src/components/ui)"},initialContent:{type:"string",description:"Initial content: sample | blank | redesign-screen | component | custom | scan"},screenName:{type:"string",description:"Screen name for --start=redesign-screen (optional if routes are scanned)"},component:{type:"string",description:"Component name or description for --start=redesign-component"},request:{type:"string",description:"Free-text design request for --start=custom"},themePreset:{type:"string",description:"Theme preset: elsewhere | indigo | violet | blue | emerald | rose | orange | amber | zinc"},designMd:{type:"boolean",default:!0,description:"Seed the theme from a DESIGN.md found at the app or repo root (use --no-design-md to keep the stylesheet / preset)"},stack:{type:"string",description:"Your app's stack \u2014 sets the emitted import alias: nextjs | vite | astro (@/components/ui) | remix (~/components/ui)"},name:{type:"string",description:"The design's name (default: its folder's, or its app's for a `velloo` folder)"}},run:({args})=>runInit2(args)});async function runInit2(cliArgs){let appRoot=resolve7(cliArgs.folder??"."),interactive=shouldRunWizard(cliArgs,Boolean(process.stdin.isTTY));if(cliArgs.scanDir&&!await dirExists(resolve7(appRoot,cliArgs.scanDir)))fail("init",`--scan-dir "${cliArgs.scanDir}" doesn't exist under ${appRoot}.`);if(cliArgs.external&&cliArgs.designFolder)fail("init","Choose --external or --design-folder, not both.");if(interactive){if(await offerUpgradeBeforeInit(process.argv.slice(2)))return;await runBrowserInit(cliArgs);return}let answers;try{answers=answersFromArgs(cliArgs)}catch(err){fail("init",err.message)}console.log(`velloo: app root ${answers.appRoot}`);let managedId=cliArgs.external?randomUUID4():void 0;if(managedId)answers.folder=managedDesignPath(managedId);let created;try{created=await createDesign(answers,{launchRoot:appRoot,managedId,name:cliArgs.name,pinnedLibrary:Boolean(cliArgs.library),scanDir:cliArgs.scanDir,force:cliArgs.force,wiring:cliArgs.connect===!1||managedId?void 0:{agents:PROJECT_AGENT_IDS,manual:!1,preWired:[]}})}catch(err){if(err instanceof InitError)fail("init",err.message);throw err}let{folder,scaffold,wireOutcome}=created;if(created.local&&cliArgs.connect!==!1)console.log(import_picocolors7.default.dim(" Agents not wired: a local design uses global configs \u2014 run `velloo connect`."));printWired(wireOutcome),await printScreenshotReadiness(),printAgentHandoff(answers,scaffold),printExitInstructions(folder,wireOutcome,{setup:hasAppToMatch(answers),handoff:wantsHandoff(answers)})}
|
|
11
|
+
export{recordedAppRoot,planAppRootChange,applyAppRootChange,promptAppRootChoice,planRelocation,relocateDesign,init_default,runInit2};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{isCancel,confirm,select,findManifest,manifestListing,findDesigns,unregisterDesign,isWithin,writeDesignName,registerLocalDesign,DESIGN_ARG_DESCRIPTION,hasDesignConfig,DESIGN_ID_ARG,resolveDesign,checkoutDesigns}from"./chunk-qe6pp4p6.js";import{designNameIssue}from"./chunk-ghwy99py.js";import{managedDesignId,localDesignOf,removeLocalDesign,recordedDesignName,designRenameConflict,followDesignRename,loadDesignFolder}from"./chunk-mseawt58.js";import"./chunk-hhkjqvww.js";import"./chunk-ak57bgnh.js";import{agentRootCandidates}from"./chunk-46jg9aqy.js";import"./chunk-d6a6198y.js";import{daemonRoot,readLock,isLive,stopDaemon}from"./chunk-jc95phff.js";import{fail}from"./chunk-s23gtejf.js";import"./chunk-tjy12q19.js";import{displayPath}from"./chunk-75gfv9g4.js";import"./chunk-hrvqwp8z.js";import{recordedAppRoot,planAppRootChange,applyAppRootChange,promptAppRootChoice,planRelocation,relocateDesign,runInit2}from"./chunk-kwym0g80.js";import"./chunk-nccfyeyy.js";import"./chunk-12vc3f1h.js";import"./chunk-62amy8bj.js";import{repinAgentConfigs,migrateFolder2}from"./chunk-s26bgjqz.js";import"./chunk-fzevxpdz.js";import"./chunk-3zm7ydna.js";import"./chunk-mknhfnkn.js";import"./chunk-5ts4fvd9.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM,defineCommand}from"./chunk-2smsg2ct.js";import{randomUUID}from"crypto";import{existsSync,realpathSync}from"fs";import{readdir,rm}from"fs/promises";import{join,relative,resolve}from"path";var import_picocolors=__toESM(require_picocolors(),1);async function countJson(dir,skipSuffix){try{return(await readdir(dir)).filter((f)=>f.endsWith(".json")&&!(skipSuffix&&f.endsWith(skipSuffix))).length}catch{return 0}}async function daemonState(folder){let rec=await readLock(daemonRoot(folder));if(!rec)return"stopped";return await isLive(rec)?`running :${rec.canvasPort}`:"stopped"}var list=defineCommand({meta:{name:"list",description:"List the repo's designs"},async run(){let cwd=resolve("."),found=await findDesigns(cwd).catch((err)=>{fail("design list",err.message)});if(!found||found.designs.length===0){let only=await resolveDesign(void 0,"design list",{requireConfig:!0,cwd,takesDesign:!1,onFail:(message)=>fail("design list",message)}),name=recordedDesignName(only)??"(unnamed)",owner=await manifestListing(only).catch(()=>null);if(owner&&owner.dir!==cwd){console.log(`velloo design: "${name}" (${relative(cwd,only)||only}) is listed in \`${relative(cwd,owner.path)}\`. Run \`velloo design list\` from \`${relative(cwd,owner.dir)||"."}\` to see that project's designs.`);return}console.log("velloo design: 1 design (no velloo.json yet)"),console.log(` ${import_picocolors.default.dim("1")} ${import_picocolors.default.bold(name)} ${relative(cwd,only)||only} ${await daemonState(only)}`),console.log(import_picocolors.default.dim(" `velloo design add` creates a second design and lists both."));return}let base=found.repo?.dir??found.local[0]?.root??cwd,rows=[];for(let design of found.designs){let live=design.exists&&!design.outsideRepo;rows.push({name:found.conflicts.has(design.name)?`${design.name} (duplicate)`:design.name,path:design.local?`${displayPath(design.root)} (local)`:relative(base,design.root)||".",boards:live?await countJson(join(design.root,"boards"),".notes.json"):0,state:design.outsideRepo?import_picocolors.default.yellow("outside the repo"):live?await daemonState(design.root):import_picocolors.default.yellow("missing"),live})}let pad=(text,width)=>text+" ".repeat(Math.max(0,width-Bun.stringWidth(text))),width=Math.max(...rows.map((r)=>Bun.stringWidth(r.name))),pathWidth=Math.max(...rows.map((r)=>Bun.stringWidth(r.path))),idWidth=String(rows.length).length;console.log(`velloo design: ${rows.length} in ${base}`);for(let[index,row]of rows.entries()){let marker=row.name===found.defaultDesign?import_picocolors.default.cyan("\u25CF"):" ",id=import_picocolors.default.dim(String(index+1).padStart(idWidth)),boards=row.live?`${row.boards} board${row.boards===1?"":"s"}`:"\u2014";console.log(` ${id} ${marker} ${import_picocolors.default.bold(pad(row.name,width))} ${import_picocolors.default.dim(pad(row.path,pathWidth))} ${boards.padEnd(9)} ${row.state}`)}console.log(import_picocolors.default.dim(" Pass a number as --id to the other `velloo design` commands."));for(let[name,designs]of found.conflicts)console.log(import_picocolors.default.yellow(` ! ${designs.length} designs are named "${name}" \u2014 rename one with \`velloo design rename <path> <new-name>\`.`))}}),add=defineCommand({meta:{name:"add",description:"Create another design in this repo (the init wizard, folder pre-chosen)"},args:{path:{type:"positional",required:!1,description:"Where the new design folder goes, relative to the repo (asked when omitted)"},external:{type:"boolean",description:"Create in Velloo-managed external storage"},nonInteractive:{type:"boolean",description:"Use defaults without prompting"},connect:{type:"boolean",default:!0,description:"Wire project agents"},library:{type:"string",description:"Component library (default: the sibling folder's)"},name:{type:"string",description:"The design's name (default: its folder's, or its app's)"}},async run({args}){await runInit2({addFolder:!0,external:args.external,nonInteractive:args.nonInteractive,connect:args.connect,...args.path?{designFolder:args.path}:{},...args.library?{library:args.library}:{},...args.name?{name:args.name}:{}})}}),remove=defineCommand({meta:{name:"remove",description:"Remove a design \u2014 one outside the repo can keep its files (asked, or --delete-content)"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,deleteContent:{type:"boolean",description:"Also delete the files of a design outside the repo (with --yes, or to skip the question)"},yes:{type:"boolean",description:"Skip the confirmation (required without an interactive terminal)"}},async run({args}){let cwd=resolve("."),folder=await resolveDesign(args.design,"design remove",{id:args.id,interactive:!0,requireConfig:!0,cwd}),local=localDesignOf(folder);if(!Boolean(process.stdin.isTTY)&&args.yes!==!0)fail("design remove","refusing to delete a design folder without --yes");let boards=await countJson(join(folder,"boards"),".notes.json"),screens=await countJson(join(folder,"screens"),".annotations.json"),name=recordedDesignName(folder)??folder;console.log(`velloo design: ${name} (${folder})`),console.log(import_picocolors.default.dim(` ${boards} board${boards===1?"":"s"}, ${screens} screen${screens===1?"":"s"}`));let keepContent=local!==null&&args.deleteContent!==!0;if(args.yes!==!0)if(local&&args.deleteContent!==!0){let managed=managedDesignId(folder)!==null,choice=await select({message:"Remove this design?",initialValue:managed?"delete":"keep",options:[{value:"delete",label:"Delete it and its files",hint:managed?"in Velloo's storage":folder},{value:"keep",label:"Forget it, keep the files",hint:folder},{value:"cancel",label:"Cancel"}]});if(isCancel(choice)||choice==="cancel"){console.log(import_picocolors.default.dim(" Nothing changed."));return}keepContent=choice==="keep"}else{let approved=await confirm({message:"Delete this design folder and everything in it?",initialValue:!1});if(isCancel(approved)||!approved){console.log(import_picocolors.default.dim(" Nothing changed."));return}}if(await stopDaemon(daemonRoot(folder)))console.log(import_picocolors.default.dim(" Stopped its canvas."));let dropped=local?null:await unregisterDesign(folder,cwd).catch((err)=>fail("design remove",err.message));if(local)await removeLocalDesign(local.id);if(!keepContent)await rm(folder,{recursive:!0,force:!0});if(console.log(`velloo design: ${keepContent?"retained content at":"removed"} ${folder}`),local)console.log(import_picocolors.default.dim(` Forgot the local design "${name}".`));if(dropped?.name)console.log(import_picocolors.default.dim(dropped.removedManifest?` Dropped "${dropped.name}" \u2014 velloo.json listed no other designs, so it's gone too.`:` Dropped "${dropped.name}" from velloo.json.`))}}),move=defineCommand({meta:{name:"move",description:"Preview moving a design between the repository, managed storage and another directory; --yes applies"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,external:{type:"boolean",description:"Move to managed storage (a local design)"},to:{type:"string",description:"New directory \u2014 inside the repository it goes in velloo.json, outside it is local"},yes:{type:"boolean",description:"Apply the displayed move"}},async run({args}){let source=await resolveDesign(args.design,"design move",{id:args.id,interactive:!0,requireConfig:!0}),plan=await planRelocation(source,resolve("."),args.to,args.external===!0).catch((err)=>fail("design move",err.message)),local="a local design on this machine",recorded=plan.sourceManifestPath?plan.destinationLocal?`velloo.json path "${plan.sourceManifestPath}" \u2192 ${"a local design on this machine"}`:`velloo.json path "${plan.sourceManifestPath}" \u2192 "${plan.destinationManifestPath}"`:plan.destinationLocal?"stays a local design on this machine":`${"a local design on this machine"} \u2192 velloo.json path "${plan.destinationManifestPath}"`;if(console.log(`Source: ${plan.source}
|
|
3
|
+
Destination: ${plan.destination}
|
|
4
|
+
Design "${plan.designName}": ${recorded}`),!args.yes){console.log("Preview only. Add --yes to apply the move.");return}await relocateDesign(plan),console.log(`Moved ${plan.designName} to ${plan.destination}. Run velloo connect to refresh existing agent guidance.`)}});async function renameThroughCanvas(folder,name){let rec=await readLock(daemonRoot(folder));if(!rec||!await isLive(rec))return!1;let res;try{res=await fetch(`http://127.0.0.1:${rec.canvasPort}/api/mutate/update_design_name`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name}),signal:AbortSignal.timeout(1e4)})}catch(err){fail("design rename",`the running canvas didn't answer (${err.message}).`)}if(res.ok)return!0;let body=await res.json().catch(()=>null);fail("design rename",body?.error?.message??`the running canvas refused (HTTP ${res.status}).`)}var rename=defineCommand({meta:{name:"rename",description:"Rename a design"},args:{design:{type:"positional",required:!1,description:"The design to rename \u2014 its current name or its folder path (or use --id)"},name:{type:"positional",required:!1,description:"The new name"},id:DESIGN_ID_ARG},async run({args}){let cwd=resolve("."),newName=args.id!==void 0&&args.name===void 0?args.design:args.name,target=args.id!==void 0&&args.name===void 0?void 0:args.design;if(newName===void 0)fail("design rename","pass the design and its new name: `velloo design rename <design> <new name>`.");let folder=await resolveDesign(target,"design rename",{id:args.id,requireConfig:!0,cwd}),previous=recordedDesignName(folder);if(previous===newName){console.log(import_picocolors.default.dim(` Already named "${newName}" \u2014 nothing changed.`));return}let conflict=await designRenameConflict(folder,newName).catch((err)=>fail("design rename",err.message));if(conflict)fail("design rename",conflict);if(!await renameThroughCanvas(folder,newName)){if(await writeDesignName(folder,newName),previous&&await followDesignRename(folder,previous,newName))console.log(import_picocolors.default.dim(` velloo.json defaultDesign \u2192 "${newName}"`))}console.log(`velloo design: renamed ${previous?`"${previous}" `:""}to "${newName}".`);let repinned=previous?await repinAgentConfigs({projectRoots:await agentRootCandidates(folder),designFolder:folder,names:[previous]}).catch(()=>[]):[];for(let path of repinned)console.log(import_picocolors.default.dim(` ${relative(cwd,path)||path}: no longer names the design`))}}),upgrade=defineCommand({meta:{name:"upgrade",description:"Migrate a design to this Velloo's format, without upgrading Velloo itself"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,all:{type:"boolean",description:"Migrate every design in this checkout"},"dry-run":{type:"boolean",default:!1,description:"Show what would change without writing"},skills:{type:"boolean",default:!0,description:"Also refresh the installed agent skills / plugin / rules to this velloo's versions"}},async run({args}){if(args.all&&(args.design||args.id!==void 0))fail("design upgrade","pass a design or --all, not both.");let folders=args.all?await checkoutDesigns(resolve(".")):[await resolveDesign(args.design,"design upgrade",{id:args.id,interactive:!0,requireConfig:!0})];if(folders.length===0)fail("design upgrade","no designs here \u2014 `velloo init` creates one.");for(let folder of folders)await migrateFolder2(folder,{dryRun:args["dry-run"],skills:args.skills})}}),bind=defineCommand({meta:{name:"bind",description:"Attach a design folder outside this checkout to it, on this machine only"},args:{design:{type:"positional",required:!0,description:"The design folder (in managed storage or anywhere outside the checkout)"},name:{type:"string",description:"Rename the design as it is bound"},yes:{type:"boolean",description:"Confirm the local binding"}},async run({args}){let cwd=resolve("."),folder=resolve(args.design);if(!await hasDesignConfig(folder))fail("design bind",`${folder} is not a velloo design folder (no .design/config.json).`);let root=cwd;if(isWithin(root,folder))fail("design bind",`${folder} is inside this checkout (${root}); a design here belongs in velloo.json \u2014 see \`velloo design add\`.`);let name=args.name??recordedDesignName(folder);if(!name)fail("design bind",`${folder} has no design name \u2014 run \`velloo upgrade ${folder}\` first.`);let issue=designNameIssue(name);if(issue)fail("design bind",`invalid --name ${JSON.stringify(name)} \u2014 ${issue}`);let clash=(await findDesigns(root).catch((err)=>fail("design bind",err.message)))?.designs.find((d)=>d.name===name&&!(existsSync(d.root)&&realpathSync(d.root)===realpathSync(folder)));if(clash)fail("design bind",`a design named "${name}" already exists at ${clash.root} \u2014 pass --name to bind this one under another.`);let existing=localDesignOf(folder),appRoot=existing?join(root,relative(existing.root,existing.appRoot)):root,listed=(await findManifest(cwd).catch(()=>null))?.folders.some((path)=>existsSync(path)&&realpathSync(path)===realpathSync(folder));if(console.log(`Design: ${name} (${folder})
|
|
5
|
+
Checkout: ${root}
|
|
6
|
+
Application: ${appRoot}`),listed)console.log("velloo.json: drops its entry, which cannot point outside the repo");if(!args.yes){console.log("Preview only. Add --yes to confirm this local binding.");return}if(await loadDesignFolder(folder,{preferences:!1}),await stopDaemon(daemonRoot(folder)),listed)await unregisterDesign(folder,cwd).catch((err)=>fail("design bind",err.message));if(name!==recordedDesignName(folder))await writeDesignName(folder,name);await registerLocalDesign({id:existing?.id??managedDesignId(folder)??randomUUID(),root,appRoot:existsSync(appRoot)?appRoot:root,...managedDesignId(folder)?{}:{designPath:folder}}).catch((err)=>fail("design bind",err.message)),console.log(`Bound "${name}" to ${root} on this machine.`)}}),setAppRoot=defineCommand({meta:{name:"set-app-root",description:"Point a design at a different application root"},args:{design:{type:"positional",required:!1,description:DESIGN_ARG_DESCRIPTION},id:DESIGN_ID_ARG,to:{type:"string",description:"New application root (asked from the repo's apps when omitted)"},yes:{type:"boolean",description:"Apply the displayed change"}},async run({args}){let folder=await resolveDesign(args.design,"design set-app-root",{id:args.id,interactive:!0,requireConfig:!0}),current=await recordedAppRoot(folder);console.log(`Design: ${folder}`),console.log(`Application: ${current.path}${current.exists?current.looksLikeApp?"":import_picocolors.default.yellow(" (no package.json)"):import_picocolors.default.yellow(" (missing)")}`);let target=args.to?resolve(args.to):await promptAppRootChoice(current.path);if(!target){console.log(import_picocolors.default.dim(" No other application found in this repo \u2014 pass --to <path>."));return}let change=await planAppRootChange(folder,target).catch((err)=>fail("design set-app-root",err.message));if(resolve(change.to)===resolve(change.from)){console.log(import_picocolors.default.dim(" Already pointing there \u2014 nothing changed."));return}if(!existsSync(change.to))fail("design set-app-root",`${change.to} does not exist.`);if(console.log(`New application: ${change.to}`),!existsSync(join(change.to,"package.json")))console.log(import_picocolors.default.yellow(" ! no package.json there \u2014 codegen and live islands will not resolve"));if(change.local)console.log(import_picocolors.default.dim(` local design record: appRoot \u2192 ${change.to}`));for(let r of change.rewritten)console.log(import_picocolors.default.dim(` ${r.field}: ${r.from} \u2192 ${r.to}`));if(!args.yes){console.log("Preview only. Add --yes to apply.");return}await stopDaemon(daemonRoot(folder)),await applyAppRootChange(folder,change),console.log(`velloo design: application root is now ${change.to}`)}}),design_default=defineCommand({meta:{name:"design",description:"Manage this repo's designs (list, add, remove, move, rename, upgrade, \u2026)"},subCommands:{list,add,remove,move,rename,upgrade,bind,"set-app-root":setAppRoot},run:({args})=>args._?.length?void 0:list.run?.({args,cmd:list,rawArgs:[]})});export{design_default as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{isCancel,confirm}from"./chunk-
|
|
2
|
+
import{isCancel,confirm}from"./chunk-qe6pp4p6.js";import{CHROMIUM_INSTALL_ARGV,CHROMIUM_INSTALL_CMD,CHROMIUM_DEPS_INSTALL_ARGV,CHROMIUM_DEPS_INSTALL_CMD,BrowserMissingError}from"./chunk-hhkjqvww.js";import{fail}from"./chunk-s23gtejf.js";import{require_picocolors}from"./chunk-jrsknzjt.js";import{__toESM}from"./chunk-2smsg2ct.js";var import_picocolors=__toESM(require_picocolors(),1);var BROWSER_PROMPT_DETAIL=import_picocolors.default.dim("Headless Chromium \u2014 powers the agent's screenshot tool, velloo render --to=png, and PNG/PDF export. The canvas itself never needs it."),BROWSER_PROMPT_SIZE="(~110MB, one-time)";async function runChromiumInstall(){let proc=Bun.spawn([...CHROMIUM_INSTALL_ARGV],{stdout:"pipe",stderr:"pipe",stdin:"inherit"}),captured="",pump=async(stream,out)=>{let decoder=new TextDecoder;for await(let chunk of stream)out.write(chunk),captured+=decoder.decode(chunk,{stream:!0})},[,,code]=await Promise.all([pump(proc.stdout,process.stdout),pump(proc.stderr,process.stderr),proc.exited]),missingSystemLibs=/missing dependencies to run browsers|Missing libraries:/i.test(captured);return{ok:code===0&&!missingSystemLibs,missingSystemLibs}}async function installChromiumInteractive(){let result=await runChromiumInstall();if(result.ok)return!0;if(!result.missingSystemLibs)return!1;console.log(""),console.log(` ${import_picocolors.default.yellow("\u26A0")} Browser downloaded, but your system is missing libraries it needs to run.`);let proceed=await confirm({message:`Install the missing system libraries now? ${import_picocolors.default.dim(`(${CHROMIUM_DEPS_INSTALL_CMD} \u2014 uses your distro's package manager, needs root/sudo)`)}`,initialValue:!0});if(isCancel(proceed)||!proceed)return console.log(import_picocolors.default.dim(` Install them later with: ${import_picocolors.default.cyan(CHROMIUM_DEPS_INSTALL_CMD)}`)),!1;if(await Bun.spawn([...CHROMIUM_DEPS_INSTALL_ARGV],{stdout:"inherit",stderr:"inherit",stdin:"inherit"}).exited!==0)return console.log(import_picocolors.default.dim(` That didn't finish \u2014 run it manually: ${import_picocolors.default.cyan(`sudo ${CHROMIUM_DEPS_INSTALL_CMD}`)}`)),!1;return!0}async function captureWithBrowserSetup(cmd,capture){try{await capture();return}catch(e){if(!(e instanceof BrowserMissingError))throw e;if(!process.stdin.isTTY)fail(cmd,e.message);let proceed=await confirm({message:`Chromium isn't installed \u2014 \`${CHROMIUM_INSTALL_CMD}\` now? ${BROWSER_PROMPT_SIZE}
|
|
3
3
|
${BROWSER_PROMPT_DETAIL}`,initialValue:!0});if(isCancel(proceed)||!proceed)fail(cmd,e.message);if(!await installChromiumInteractive())fail(cmd,"the browser isn't usable yet \u2014 see the output above.")}await capture()}
|
|
4
|
-
export{
|
|
4
|
+
export{installChromiumInteractive,captureWithBrowserSetup};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{snapshotVersion2}from"./chunk-
|
|
2
|
+
import{snapshotVersion2}from"./chunk-tjy12q19.js";import{CURRENT_SCHEMA_VERSION}from"./chunk-ghwy99py.js";import{TOOL_VERSION}from"./chunk-d6a6198y.js";function buildDefaultConfig(opts={}){return{schemaVersion:CURRENT_SCHEMA_VERSION,toolVersion:TOOL_VERSION,name:opts.name??"design",folderId:crypto.randomUUID(),libraries:{default:opts.library??{id:"shadcn-upstream",version:snapshotVersion2,source:"binary",componentsPath:"binary"}},defaultLibrary:"default",viewportPresets:[{name:"Mobile",w:390,h:844},{name:"Tablet",w:768,h:1024},{name:"Desktop",w:1440,h:900}],...opts.defaultScreen?{defaultScreen:opts.defaultScreen}:{},...opts.defaultBoard?{defaultBoard:opts.defaultBoard}:{},...opts.hostApp?{hostApp:opts.hostApp}:{},...opts.hostApps?{hostApps:opts.hostApps}:{},...opts.feedback?{feedback:opts.feedback}:{},...opts.styling?{styling:opts.styling}:{},...opts.codegen?{codegen:opts.codegen}:{},...opts.designSystem?{designSystem:opts.designSystem}:{},...opts.themeSource?{themeSource:opts.themeSource}:{}}}
|
|
3
3
|
export{buildDefaultConfig};
|