velloo 0.1.0 → 0.3.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 +67 -41
- package/canvas/assets/index-CqXW6xA_.js +29 -0
- package/canvas/assets/index-gB0StCrx.css +2 -0
- package/canvas/assets/lucide-all-C_CNpsMs.js +1 -0
- package/canvas/assets/{radix-C3HaOB-d.js → radix-gmpYzWRH.js} +1 -1
- package/canvas/index.html +3 -3
- package/chunk-0zen7bzb.js +3 -0
- package/{chunk-h0c36khx.js → chunk-1jzhw3p4.js} +1 -1
- package/chunk-1n5kmsxb.js +2 -0
- package/{chunk-k6ferbam.js → chunk-2fgfex9b.js} +1 -1
- package/{chunk-t2ttkxe9.js → chunk-3g3dqej3.js} +1 -1
- package/chunk-3tmsrvaj.js +2 -0
- package/chunk-49cgk5s2.js +2 -0
- package/{chunk-e2ag33vd.js → chunk-4nqr089t.js} +2 -2
- package/chunk-52hxv18g.js +6 -0
- package/chunk-5ezzc1h2.js +688 -0
- package/{chunk-74skmg76.js → chunk-5wv5a52y.js} +1 -1
- package/chunk-6nzqhfqq.js +5 -0
- package/{chunk-fgw3hp2v.js → chunk-6zz9kfbd.js} +1 -1
- package/chunk-788t8y5x.js +2 -0
- package/chunk-7bvvbsqy.js +26 -0
- package/{chunk-cep3ktr3.js → chunk-7k7dpj5y.js} +1 -1
- package/{chunk-hs2j6s1v.js → chunk-7njj1qmq.js} +8 -31
- package/{chunk-8ssr8a8w.js → chunk-80fjfyha.js} +3 -6
- package/chunk-94vx1yb0.js +3 -0
- package/{chunk-n5007jcz.js → chunk-9f22qnge.js} +1 -1
- package/chunk-9g4tr0r6.js +2 -0
- package/{chunk-1gx45384.js → chunk-9nhw49vv.js} +7 -8
- package/{chunk-2z0k59hz.js → chunk-as08hzp3.js} +1 -1
- package/chunk-bhx8k09w.js +3 -0
- package/chunk-bpeezw7w.js +3 -0
- package/{chunk-b2ebm1t3.js → chunk-bvmtq0vn.js} +2 -2
- package/{chunk-w5rzk6ms.js → chunk-c4fym47a.js} +64 -20
- package/{chunk-d385435m.js → chunk-c6hywdvq.js} +1 -1
- package/{chunk-jnsfgtn9.js → chunk-cvn689d5.js} +1 -1
- package/chunk-cxzkkn12.js +2 -0
- package/{chunk-56vqqr8s.js → chunk-d9p6s05n.js} +1 -1
- package/{chunk-5js0qw32.js → chunk-dsks52m7.js} +1 -1
- package/{chunk-b7j86xq7.js → chunk-hfzg98c8.js} +1 -1
- package/{chunk-7e39xe21.js → chunk-js6mz7h1.js} +1 -1
- package/chunk-jx11jvq8.js +7 -0
- package/{chunk-2vx420g2.js → chunk-k6n249vb.js} +2 -2
- package/chunk-k7sehkm8.js +3 -0
- package/{chunk-k784090q.js → chunk-k9219kmj.js} +1 -1
- package/{chunk-65wb0g7d.js → chunk-kz4423th.js} +1 -1
- package/{chunk-g02nd9hn.js → chunk-m3czg1tt.js} +1 -1
- package/chunk-maz6z7tz.js +5 -0
- package/{chunk-rksq3w72.js → chunk-n9kcxaj7.js} +2 -2
- package/chunk-n9t36h0q.js +2 -0
- package/chunk-nr3ej3qf.js +5 -0
- package/{chunk-bk8b7f9a.js → chunk-nte0t7v3.js} +4 -3
- package/{chunk-kxk5x7g6.js → chunk-nycs0z7h.js} +1 -1
- package/{chunk-79ykzywe.js → chunk-pjtg3wnm.js} +1 -1
- package/chunk-qdnb2t6a.js +3 -0
- package/chunk-qgwhd4n6.js +86 -0
- package/chunk-sn9vg42q.js +2 -0
- package/{chunk-7p7ha0qk.js → chunk-t5bb30zy.js} +1 -1
- package/chunk-tbmkq7wy.js +6 -0
- package/chunk-tc4mzzfw.js +2 -0
- package/chunk-td5z2sm4.js +6 -0
- package/chunk-wa6qamwh.js +3 -0
- package/chunk-wcqqx6tz.js +2 -0
- package/chunk-wzngjjeb.js +10 -0
- package/chunk-xyfed1j0.js +2 -0
- package/chunk-y5r2w2z3.js +2 -0
- package/chunk-yeh9gb5w.js +3 -0
- package/cli.js +1 -1
- package/launcher.cjs +1 -1
- package/package.json +3 -3
- package/pkgs/helpers/src/descriptors.ts +7 -1
- package/pkgs/provider-antd/src/index.ts +1 -0
- package/pkgs/provider-chakra/src/index.ts +1 -0
- package/pkgs/provider-mui/src/index.ts +1 -0
- package/pkgs/provider-none/src/index.ts +42 -1
- package/pkgs/provider-none/src/intro.ts +2 -2
- package/pkgs/schema/src/config.ts +27 -1
- package/pkgs/schema/src/extension.ts +26 -0
- package/pkgs/schema/src/index.ts +18 -0
- package/pkgs/schema/src/migrate.ts +57 -4
- package/pkgs/schema/src/node.ts +99 -0
- package/pkgs/schema/src/repo.ts +98 -16
- package/pkgs/schema/src/screen.ts +7 -0
- package/pkgs/schema/src/svg-sanitize.ts +472 -59
- package/pkgs/shadcn-snapshot/dist/manifest.json +9 -1
- package/skills/velloo-setup/SKILL.md +70 -48
- package/canvas/assets/index-BCvDZiBo.css +0 -2
- package/canvas/assets/index-BcBJ11d2.js +0 -28
- package/canvas/assets/lucide-all-BdEyryfF.js +0 -1
- package/chunk-1bywxgc5.js +0 -4
- package/chunk-1y50g9vp.js +0 -2
- package/chunk-1yhc252b.js +0 -3
- package/chunk-2mv65qxs.js +0 -5
- package/chunk-329e3phg.js +0 -451
- package/chunk-40657ggd.js +0 -10
- package/chunk-582y60bv.js +0 -2
- package/chunk-5x173xay.js +0 -2
- package/chunk-75wbh438.js +0 -2
- package/chunk-7dggmqwq.js +0 -53
- package/chunk-946pcnt5.js +0 -5
- package/chunk-9vg3yafz.js +0 -3
- package/chunk-aapybh30.js +0 -6
- package/chunk-bjnhhsak.js +0 -2
- package/chunk-dt03awr1.js +0 -2
- package/chunk-erwfk47h.js +0 -3
- package/chunk-mq4znqv2.js +0 -2
- package/chunk-nfr3q7mg.js +0 -5
- package/chunk-nsqs1bqf.js +0 -2
- package/chunk-pphgrsxa.js +0 -2
- package/chunk-qpvj6kzq.js +0 -6
- package/chunk-qwv7xkms.js +0 -2
- package/chunk-tmtzxcs5.js +0 -2
- package/chunk-xr6f94rj.js +0 -2
- package/chunk-y0zxzefg.js +0 -5
- package/chunk-zf3xc1wz.js +0 -2
package/chunk-1bywxgc5.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{RepoManifestSchema}from"./chunk-bk8b7f9a.js";import{writeJsonAtomic,localDesignsAt,localDesignOf,writeLocalDesign}from"./chunk-329e3phg.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,rm,stat,writeFile}from"fs/promises";import{homedir}from"os";import{basename,dirname,isAbsolute,join,relative,resolve,sep}from"path";var MANIFEST_FILE="velloo.json",DEFAULT_FOLDER_NAME="velloo",PROJECT_NAME=/^[a-z0-9][a-z0-9._-]*$/i,ManifestSchema=RepoManifestSchema;async function findManifest(startDir){let dir=resolve(startDir);while(!0){let path=join(dir,MANIFEST_FILE),raw=null;try{raw=await readFile(path,"utf8")}catch(err){if(err.code!=="ENOENT")throw err}if(raw!==null){let parsed;try{parsed=JSON.parse(raw)}catch(err){throw Error(`${path} is not valid JSON: ${err.message}`)}let projects=parsed?.projects;for(let[name,entry]of Object.entries(projects&&typeof projects==="object"?projects:{}))if(entry&&typeof entry==="object"&&"managed"in entry)throw Error(`${path}: project "${name}" is a managed design locator, which velloo.json no longer holds \u2014 a design outside the repository is recorded only on the machine that has it. Remove the entry, then run \`velloo folder bind <design folder>\` from this checkout.`);let result=ManifestSchema.safeParse(parsed);if(!result.success){let issues=result.error.issues.map((i)=>`${i.path.join(".")||"(root)"}: ${i.message}`).join("; ");throw Error(`${path} is not a valid velloo manifest: ${issues}`)}let folders=new Map(Object.entries(result.data.projects).map(([name,rel])=>[name,resolve(dir,rel.replace(/\\/g,"/"))]));return{path,dir,manifest:result.data,folders}}let parent=dirname(dir);if(parent===dir)return null;dir=parent}}async function findProjects(cwd){let repo=await findManifest(cwd),local=localDesignsAt(cwd);if(!repo&&local.length===0)return null;let folders=new Map(repo?.folders??[]);for(let design of local){let clash=folders.get(design.projectName);if(clash!==void 0)throw Error(`Project "${design.projectName}" names both ${clash} (in ${repo?.path??"another local design"}) and the local design ${design.designRoot}. Remove one with \`velloo folder remove\`, or rename the velloo.json entry.`);folders.set(design.projectName,design.designRoot)}return{repo,local,folders,defaultProject:repo?.manifest.defaultProject}}function pickProject(projects,cwd){let entries=[...projects.folders],appMatches=projects.local.filter((design)=>resolve(design.appRoot)!==resolve(design.root)&&isWithin(resolve(design.appRoot),cwd));if(appMatches.length===1&&appMatches[0])return appMatches[0].projectName;let containing=entries.find(([,folder])=>cwd===folder||cwd.startsWith(folder+sep));if(containing)return containing[0];let under=entries.filter(([,folder])=>folder.startsWith(cwd+sep));if(under.length===1&&under[0])return under[0][0];if(entries.length===1&&entries[0])return entries[0][0];return projects.defaultProject??null}async function projectLabel(folder){let abs=resolve(folder),local=localDesignOf(abs);if(local)return`${local.projectName} \u2014 ${abs} (local, for ${local.root})`;let found;try{found=await findManifest(abs)}catch{return null}if(!found)return null;let entry=[...found.folders].find(([,path])=>path===abs||existsSync(path)&&existsSync(abs)&&realpathSync(path)===realpathSync(abs));if(!entry)return null;let rel=relative(found.dir,abs)||".";return`${entry[0]} \u2014 ${rel} (in ${found.dir})`}async function unregisterProject(folder,appRoot){let abs=resolve(folder),found=await findManifest(appRoot);if(!found)return{name:null,path:null,removedManifest:!1};let name=null;for(let[candidate,target]of found.folders)if(target===abs||existsSync(target)&&existsSync(abs)&&realpathSync(target)===realpathSync(abs))name=candidate;if(!name)return{name:null,path:found.path,removedManifest:!1};let{[name]:_dropped,...projects}=found.manifest.projects;if(Object.keys(projects).length===0)return await rm(found.path,{force:!0}),{name,path:found.path,removedManifest:!0};let manifest={...found.manifest,projects,...found.manifest.defaultProject===name?{defaultProject:void 0}:{}};if(manifest.defaultProject===void 0)delete manifest.defaultProject;return await writeFile(found.path,`${JSON.stringify(manifest,null,2)}
|
|
3
|
-
`,"utf8"),{name,path:found.path,removedManifest:!1}}async function manifestDirFor(folder,appRoot){let git=await findGitRoot(appRoot);if(git)return git;let dir=resolve(appRoot);while(!isWithin(dir,folder)&&dirname(dir)!==dir)dir=dirname(dir);return dir}async function checkoutRoot(appRoot,launchRoot){let found=await findManifest(launchRoot).catch(()=>null);if(found)return found.dir;let git=await findGitRoot(appRoot);if(git)return git;return isWithin(resolve(launchRoot),resolve(appRoot))?resolve(launchRoot):resolve(appRoot)}function isWithin(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!isAbsolute(rel)}async function findGitRoot(start){let home=homedir(),dir=resolve(start);while(dir!==home){try{return await stat(join(dir,".git")),dir}catch{}let parent=dirname(dir);if(parent===dir)return null;dir=parent}return null}var SCAN_SKIP=new Set(["node_modules",".git","dist","build",".next",".turbo","coverage",".velloo"]);async function discoverDesignFolders(root,maxDepth=3){let out=[],walk=async(dir,depth)=>{let entries;try{entries=await readdir(dir,{withFileTypes:!0})}catch{return}for(let entry of entries){if(!entry.isDirectory()||entry.name.startsWith(".")||SCAN_SKIP.has(entry.name))continue;let child=join(dir,entry.name);try{await stat(join(child,".design","config.json")),out.push(child);continue}catch{}if(depth<maxDepth)await walk(child,depth+1)}};return await walk(resolve(root),1),out.sort()}function deriveName(folder){let base=basename(folder);return(base==="velloo"||base==="."?basename(dirname(folder)):base).toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^[^a-z0-9]+/,"")||"app"}async function registerProject(folder,appRoot,requestedName){checkProjectName(requestedName);let abs=resolve(folder),found=await findManifest(abs),dir=found?found.dir:await manifestDirFor(abs,appRoot),path=found?found.path:join(dir,MANIFEST_FILE),localNames=new Set(localDesignsAt(dir).map((design)=>design.projectName)),projects={...found?.manifest.projects??{}};for(let[existing,target]of found?.folders??[])if(target===abs||existsSync(target)&&existsSync(abs)&&realpathSync(target)===realpathSync(abs))return{name:existing,path,created:!1};let add=(folder,wanted)=>{let name=wanted??deriveName(folder);for(let n=2;name in projects||localNames.has(name);n++)name=`${wanted??deriveName(folder)}-${n}`;return projects[name]=relative(dir,folder)||".",name},defaultProject=found?.manifest.defaultProject;if(!found)for(let sibling of await discoverDesignFolders(dir)){if(sibling===abs)continue;let name=add(sibling);if(sibling===join(dir,DEFAULT_FOLDER_NAME))defaultProject=name}let outsideApp=!isWithin(resolve(appRoot),abs),name=add(abs,requestedName??(outsideApp?deriveName(appRoot):void 0)),manifest={...found?.manifest??{},projects,...defaultProject?{defaultProject}:{}};return await writeJsonAtomic(path,manifest),{name,path,created:!0}}function checkProjectName(name){if(name&&!PROJECT_NAME.test(name))throw Error(`invalid --project ${JSON.stringify(name)} \u2014 names are letters/digits plus . _ -`)}async function registerLocalDesign(opts){checkProjectName(opts.requestedName);let existing=localDesignsAt(opts.root).find((design)=>design.id===opts.id),taken=new Set((await findProjects(opts.root))?.folders.keys()??[]);if(existing)taken.delete(existing.projectName);let base=opts.requestedName??existing?.projectName??deriveName(opts.appRoot);if(opts.requestedName&&taken.has(base))throw Error(`project "${base}" already exists for ${opts.root} \u2014 pick another --project.`);let name=base;for(let n=2;taken.has(name);n++)name=`${base}-${n}`;return await writeLocalDesign(opts.id,{root:opts.root,appRoot:opts.appRoot,projectName:name,...opts.designPath?{designPath:opts.designPath}:{}}),name}
|
|
4
|
-
export{findManifest,findProjects,pickProject,projectLabel,unregisterProject,checkoutRoot,isWithin,findGitRoot,discoverDesignFolders,registerProject,registerLocalDesign};
|
package/chunk-1y50g9vp.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import"./chunk-65wb0g7d.js";import{refreshUpdateCache}from"./chunk-cep3ktr3.js";import{defineCommand}from"./chunk-2smsg2ct.js";var update_check_default=defineCommand({meta:{name:"__update_check",description:"Internal release check"},async run(){await refreshUpdateCache()}});export{update_check_default as default};
|
package/chunk-1yhc252b.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{failWithError}from"./chunk-s23gtejf.js";import{traceEnabled}from"./chunk-6tx68d90.js";var LOADERS={init:()=>import("./chunk-qwv7xkms.js").then((m)=>m.default),login:()=>import("./chunk-7p7ha0qk.js").then((m)=>m.default),logout:()=>import("./chunk-74skmg76.js").then((m)=>m.default),connect:()=>import("./chunk-erwfk47h.js").then((m)=>m.default),run:()=>import("./chunk-nsqs1bqf.js").then((m)=>m.default),folder:()=>import("./chunk-qpvj6kzq.js").then((m)=>m.default),mcp:()=>import("./chunk-5x173xay.js").then((m)=>m.default),stop:()=>import("./chunk-75wbh438.js").then((m)=>m.default),status:()=>import("./chunk-zf3xc1wz.js").then((m)=>m.default),__daemon:()=>import("./chunk-n5007jcz.js").then((m)=>m.default),__update_check:()=>import("./chunk-1y50g9vp.js").then((m)=>m.default),render:()=>import("./chunk-582y60bv.js").then((m)=>m.default),capture:()=>import("./chunk-aapybh30.js").then((m)=>m.default),browser:()=>import("./chunk-dt03awr1.js").then((m)=>m.default),export:()=>import("./chunk-bjnhhsak.js").then((m)=>m.default),upgrade:()=>import("./chunk-tmtzxcs5.js").then((m)=>m.default),publish:()=>import("./chunk-2mv65qxs.js").then((m)=>m.default),emit:()=>import("./chunk-40657ggd.js").then((m)=>m.default),"theme:export":()=>import("./chunk-nfr3q7mg.js").then((m)=>m.default),completions:()=>import("./chunk-h0c36khx.js").then((m)=>m.default),...traceEnabled()?{trace:()=>import("./chunk-e2ag33vd.js").then((m)=>m.default)}:{}};function guarded(name,load){return async()=>{let cmd=await load(),run=cmd.run;if(!run)return cmd;return{...cmd,async run(ctx){try{await run(ctx)}catch(err){failWithError(name,err)}}}}}var COMMANDS=Object.fromEntries(Object.entries(LOADERS).map(([name,load])=>[name,guarded(name,load)]));
|
|
3
|
-
export{COMMANDS};
|
package/chunk-2mv65qxs.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{isCancel,confirm,password,select,FOLDER_ARG_DESCRIPTION,resolveDesignFolder,pickBoards}from"./chunk-8ssr8a8w.js";import{PRICING_URL,protectedSharesAllowed,PROTECTED_SHARES_UNAVAILABLE,describeCloudError}from"./chunk-mq4znqv2.js";import"./chunk-bk8b7f9a.js";import{closePooledBrowser}from"./chunk-w5rzk6ms.js";import{loadDesignFolder,screensForBoards,resolveProviders,findHostTailwindConfig,TailwindJit,extraThemeBlock}from"./chunk-329e3phg.js";import{loadCredential}from"./chunk-b7j86xq7.js";import{fail}from"./chunk-s23gtejf.js";import{confirmRenderFailures}from"./chunk-g02nd9hn.js";import{publishedDesignSubtitle,listPublishedDesigns,unpublishDesign,listPublishDestinations,designGitEnv,describePublishError,recommendedPublishSlot,exactPublishSlots,gitContext,resolveTeam,publishDesign,createPublishBundler}from"./chunk-946pcnt5.js";import{createProgress}from"./chunk-79ykzywe.js";import{fetchAccount}from"./chunk-fgw3hp2v.js";import"./chunk-1bywxgc5.js";import{defaultCloudUrl,publishedBoardsUrl,billingPageUrl,checkCloudHealth}from"./chunk-2z0k59hz.js";import{defineCommand}from"./chunk-2smsg2ct.js";import{join as join2}from"path";import{execFileSync}from"child_process";import{realpathSync}from"fs";import{join,relative,resolve}from"path";function plainJsonStem(path,dir){let prefix=`${dir}/`;if(!path.startsWith(prefix)||!path.endsWith(".json"))return null;let stem=path.slice(prefix.length,-5);if(stem.includes("/")||stem.includes("."))return null;return stem}function classifyChanges(changes){let set={screens:new Map,global:!1,snippets:new Set,assets:new Set};for(let{status,path}of changes){let screen=plainJsonStem(path,"screens");if(screen){set.screens.set(screen,status);continue}if(path.startsWith("theme/")||path===".design/config.json"){set.global=!0;continue}let snippet=plainJsonStem(path,"snippets");if(snippet){set.snippets.add(snippet);continue}if(path.startsWith("assets/"))set.assets.add(`/${path}`)}return set}function snippetIdsIn(value,acc){if(Array.isArray(value)){for(let v of value)snippetIdsIn(v,acc);return}if(value!==null&&typeof value==="object"){let id=value.$snippet;if(typeof id==="string")acc.add(id);for(let v of Object.values(value))snippetIdsIn(v,acc)}}function snippetsUsedByScreen(screen,snippets){let used=new Set;snippetIdsIn(screen.tree,used);let grew=!0;while(grew){grew=!1;for(let id of[...used]){let body=snippets.get(id);if(!body)continue;let inner=new Set;snippetIdsIn(body,inner);for(let innerId of inner)if(!used.has(innerId))used.add(innerId),grew=!0}}return used}function detectChangedScreens(changes,afterScreens,afterSnippets){let out=new Map;for(let[id,status]of changes.screens)out.set(id,{id,status:status==="A"?"added":status==="D"?"deleted":"modified",reasons:["screen"]});let markModified=(id,reason)=>{let existing=out.get(id);if(existing){if(!existing.reasons.includes(reason))existing.reasons.push(reason);return}out.set(id,{id,status:"modified",reasons:[reason]})};if(changes.global||changes.snippets.size>0||changes.assets.size>0)for(let[id,screen]of afterScreens){if(changes.global)markModified(id,"theme");if(changes.snippets.size===0&&changes.assets.size===0)continue;let used=snippetsUsedByScreen(screen,afterSnippets);for(let snippetId of changes.snippets)if(used.has(snippetId))markModified(id,`snippet:${snippetId}`);if(changes.assets.size>0){let haystack=JSON.stringify(screen);for(let snippetId of used){let body=afterSnippets.get(snippetId);if(body)haystack+=JSON.stringify(body)}for(let asset of changes.assets)if(haystack.includes(asset))markModified(id,`asset:${asset}`)}}return[...out.values()].sort((a,b)=>a.id.localeCompare(b.id))}function git(repo,args){return execFileSync("git",["-C",repo,...args],{encoding:"utf8",maxBuffer:67108864,stdio:["ignore","pipe","pipe"]})}function resolveGitContext(designFolder,base,head){let repoRoot;try{repoRoot=execFileSync("git",["-C",designFolder,"rev-parse","--show-toplevel"],{encoding:"utf8",stdio:["ignore","pipe","pipe"],env:designGitEnv(designFolder)}).trim()}catch{throw Error(`design folder ${designFolder} is not in a git repository, so there are no refs to compare`)}let designRel=relative(realpathSync(repoRoot),realpathSync(resolve(designFolder)));if(designRel.startsWith(".."))throw Error(`design folder ${designFolder} is outside the git repo at ${repoRoot}`);let rev=(ref)=>{try{return git(repoRoot,["rev-parse","--verify",`${ref}^{commit}`]).trim()}catch{throw Error(`cannot resolve git ref '${ref}'`)}},baseSha=rev(base),headSha=rev(head),currentSha=rev("HEAD");return{repoRoot,designRel,baseSha,headSha,headIsWorktree:headSha===currentSha}}function diffDesignFolder(ctx){let args=["diff","--name-status","--no-renames",ctx.baseSha];if(!ctx.headIsWorktree)args.push(ctx.headSha);if(ctx.designRel!=="")args.push("--",ctx.designRel);let output=git(ctx.repoRoot,args),prefix=ctx.designRel===""?"":`${ctx.designRel}/`,out=[];for(let line of output.split(`
|
|
3
|
-
`)){if(line.trim()==="")continue;let tab=line.indexOf("\t");if(tab===-1)continue;let code=line.slice(0,tab).trim(),repoPath=line.slice(tab+1);if(!repoPath.startsWith(prefix))continue;let status=code==="A"?"A":code==="D"?"D":code==="M"||code==="T"?"M":null;if(!status)continue;out.push({status,path:repoPath.slice(prefix.length)})}return out}function changedBoardId(path){if(!path.startsWith("boards/")||!path.endsWith(".json"))return null;let id=path.slice(7,-5);return id.includes("/")||id.includes(".")?null:id}function selectChangedPreviews(changes,screens,snippets,boards){let availableScreens=new Map(screens.map((screen)=>[screen.id,screen])),screenIds=new Set(detectChangedScreens(classifyChanges(changes),availableScreens,snippets).filter((change)=>change.status!=="deleted"&&availableScreens.has(change.id)).map((change)=>change.id)),directlyChangedBoards=new Set(changes.map((change)=>changedBoardId(change.path)).filter((id)=>id!==null)),boardIds=new Set(boards.filter((board)=>directlyChangedBoards.has(board.id)||board.frames.some((frame)=>screenIds.has(frame.screen))).map((board)=>board.id));return{screenIds,boardIds}}function changedPreviewsSince(designFolder,baseRef,screens,snippets,boards){let git=resolveGitContext(designFolder,baseRef,"HEAD");return selectChangedPreviews(diffDesignFolder(git),screens,snippets,boards)}async function resolveCloud(command,args){let baseUrl=args.url?args.url.replace(/\/+$/,""):defaultCloudUrl(),token=args.token??process.env.VELLOO_CLOUD_TOKEN??(await loadCredential(baseUrl))?.token;if(!token)fail(command,"not logged in. Run `velloo login` first.");return{baseUrl,token}}function cloudFailure(command,error){fail(command,describeCloudError(error))}async function listPublished(args){let{baseUrl,token}=await resolveCloud("publish",args),listed=await listPublishedDesigns({baseUrl,token});if(!listed.ok)cloudFailure("publish",listed.error);let designs=listed.value;if(designs.length===0){console.log("velloo publish: no published designs."),console.log(` ${await publishedBoardsUrl(baseUrl)}`);return}console.log(`velloo publish: ${designs.length} design${designs.length===1?"":"s"}`);for(let design of designs){let access=design.passwordProtected?"password protected":design.visibility==="private"?"private":"public";console.log(`
|
|
4
|
-
${design.title?.trim()||"Untitled design"}`),console.log(` ${access}`),console.log(` ${publishedDesignSubtitle(design)}`),console.log(` ${design.url}`)}console.log(`
|
|
5
|
-
Manage in velloo-cloud: ${await publishedBoardsUrl(baseUrl)}`)}async function removePublished(args){let{baseUrl,token}=await resolveCloud("publish",args),interactive=Boolean(process.stdin.isTTY);if(!interactive&&!args.design)fail("publish","pass the full share URL and --yes when there is no interactive terminal");if(!interactive&&args.yes!==!0)fail("publish","refusing to remove a published design without --yes");let listed=await listPublishedDesigns({baseUrl,token});if(!listed.ok)cloudFailure("publish",listed.error);let designs=listed.value,manageable=designs.filter((design)=>design.canManage);if(manageable.length===0)fail("publish",`there are no published designs you can remove. Manage boards at ${await publishedBoardsUrl(baseUrl)}`);let target;if(args.design){if(target=designFromReference(args.design,designs),!target)fail("publish",`that share URL is not in your published designs. Manage boards at ${await publishedBoardsUrl(baseUrl)}`);if(!target.canManage)fail("publish","you do not have permission to remove that design")}else{let value=await select({message:"Design to unpublish",options:manageable.map((design)=>({value:design.slug,label:design.title?.trim()||"Untitled design",hint:publishedDesignSubtitle(design)}))});if(target=resolveUnpublishSelection(isCancel(value)?null:String(value),manageable),!target)fail("publish","cancelled")}if(args.yes!==!0){let approved=await confirm({message:`Unpublish \u201C${target.title?.trim()||"Untitled design"}\u201D? Its share URL will stop working.`,initialValue:!1});if(isCancel(approved)||!approved)fail("publish","cancelled")}let removed=await unpublishDesign({baseUrl,token,slug:target.slug});if(!removed.ok)cloudFailure("publish",removed.error);console.log(`velloo publish: removed ${target.title?.trim()||"Untitled design"}`),console.log(` ${target.url}`)}function resolveUnpublishSelection(value,designs){return value===null?null:designs.find((design)=>design.slug===value)??null}function designFromReference(reference,designs){if(!/^https?:\/\//i.test(reference.trim()))return null;let normalize=(raw)=>{try{let url=new URL(raw.trim());return url.search="",url.hash="",url.toString().replace(/\/+$/,"")}catch{return null}},normalized=normalize(reference);if(normalized===null)return null;return designs.find((design)=>normalize(design.url)===normalized)??null}function privacyFlagsError(args,protectedShares=!0,upgradeUrl=PRICING_URL){if(args.visibility!==void 0&&args.visibility!=="public"&&args.visibility!=="private")return`--visibility must be 'public' or 'private', got '${args.visibility}'`;if(args.private&&args.public)return"--private and --public cannot be used together";if(args.private&&args.visibility==="public")return"--private conflicts with --visibility public";if(args.public&&args.visibility==="private")return"--public conflicts with --visibility private";if((args.private===!0||args.visibility==="private"||args.password===!0)&&!protectedShares)return`${PROTECTED_SHARES_UNAVAILABLE}. Publish with --public, or upgrade at ${upgradeUrl}`;return null}async function resolvePublishPrivacy(args,interactive,{protectedShares=!0,upgradeUrl=PRICING_URL,choose=promptForPrivacyMode}={}){let invalid=privacyFlagsError(args,protectedShares,upgradeUrl);if(invalid)throw Error(invalid);if(args.visibility!==void 0||args.private===!0||args.public===!0||args.password===!0)return{visibility:args.private||args.visibility==="private"?"private":"public",password:args.password===!0};if(!interactive)throw Error("privacy mode is required without a terminal; pass --public, --private, --password, or --visibility");let mode=await choose(protectedShares,upgradeUrl);if(typeof mode==="symbol")throw Error("cancelled");if(mode!=="public"&&!protectedShares)throw Error(PROTECTED_SHARES_UNAVAILABLE);return mode==="private"?{visibility:"private",password:!1}:{visibility:"public",password:mode==="password"}}async function promptForPrivacyMode(protectedShares,upgradeUrl){let locked=protectedShares?{}:{disabled:!0,hint:"Team & Business plans"};return select({message:protectedShares?"Who should be able to view this publish?":`Who should be able to view this publish? (free plan: public links \u2014 upgrade at ${upgradeUrl})`,initialValue:"public",options:[{value:"public",label:"Public",hint:"anyone with the link"},{value:"private",label:"Private",hint:"only your organization",...locked},{value:"password",label:"Password protected",hint:"anyone with the password",...locked}]})}var publish_default=defineCommand({meta:{name:"publish",description:"Publish the design folder as a velloo-cloud share link (the cloud renders it). --list shows what you've published; --remove takes one down"},args:{folder:{type:"positional",required:!1,description:`${FOLDER_ARG_DESCRIPTION}. With --remove, the share URL to take down instead`},list:{type:"boolean",description:"List your published designs instead of publishing"},remove:{type:"boolean",description:"Take a published design down instead of publishing \u2014 pass its share URL, or pick one interactively"},yes:{type:"boolean",description:"Skip confirmations \u2014 the --remove prompt, and publishing a screen whose component fails to render (required without an interactive terminal)"},url:{type:"string",description:"velloo-cloud base URL (default: $VELLOO_CLOUD_URL or the built-in default)"},token:{type:"string",description:"velloo-cloud access token (default: $VELLOO_CLOUD_TOKEN)"},slug:{type:"string",description:"Custom slug for --new (default: server-generated)"},new:{type:"boolean",description:"Create a separate publish link instead of updating an existing slot"},update:{type:"boolean",description:"Update the exact matching published design"},title:{type:"string",description:"Link title (default: folder name)"},boards:{type:"string",description:"Comma-separated board ids to publish (default: pick interactively; all when non-interactive)"},visibility:{type:"string",description:"public | private \u2014 explicit alternative to the interactive privacy prompt"},public:{type:"boolean",description:"Publish for anyone with the link; skips the privacy prompt"},private:{type:"boolean",description:"Publish for your organization only; skips the privacy prompt"},password:{type:"boolean",description:"Password-protect the link; anyone with the password can view. Prompts for it, or reads $VELLOO_SHARE_PASSWORD"},"password-expires":{type:"string",description:"Stop accepting the password after this date (YYYY-MM-DD or ISO timestamp)"},team:{type:"string",description:"Publish into a team by name or UUID (default: personal workspace)"},w:{type:"string",description:"Viewport width in px (default: 1440)"},h:{type:"string",description:"Viewport height in px (default: 900)"},"changed-since":{type:"string",description:"Capture only screen/board previews affected since this git ref (the full design still publishes)"}},async run({args}){if(args.list&&args.remove)fail("publish","--list and --remove do different things");if(args.list)return listPublished(args);if(args.remove)return removePublished({...args,design:args.folder,yes:args.yes});let folder=await resolveDesignFolder(args.folder,"publish"),baseUrl=args.url?args.url.replace(/\/+$/,""):defaultCloudUrl(),token=args.token??process.env.VELLOO_CLOUD_TOKEN??(await loadCredential(baseUrl))?.token;if(!token)fail("publish","not logged in. Run `velloo login` (or pass --token / VELLOO_CLOUD_TOKEN).");let interactive=Boolean(process.stdin.isTTY),team=await resolveTeam(baseUrl,token,args.team);if(!team.ok)fail("publish",describePublishError(team.error));let teamId=team.value,viewport={w:args.w?Number(args.w):1440,h:args.h?Number(args.h):900},design=await loadDesignFolder(folder),config=design.config,health=await checkCloudHealth(baseUrl);if(health.status==="unreachable")fail("publish",`cannot reach ${baseUrl} (${health.reason}). Is velloo-cloud up? (bun cloud:up)`);if(health.status==="unhealthy")fail("publish",health.detail);let account=await fetchAccount(baseUrl,token,5000),protectedShares=protectedSharesAllowed(account.status==="ok"?account.account.tier:void 0),upgradeUrl=protectedShares?void 0:await billingPageUrl(baseUrl),flagsError=privacyFlagsError(args,protectedShares,upgradeUrl);if(flagsError)fail("publish",flagsError);let selected=await pickBoards(folder,args.boards,interactive,"publish");if(selected===null){console.log("velloo publish: no boards selected \u2014 nothing was published.");return}let provenance=gitContext(folder),selectedBoardIds=new Set(selected.map((board)=>board.id)),selectedBoards=[...design.boards.values()].filter((board)=>selectedBoardIds.has(board.id)),screenshotSelection=args["changed-since"]?(()=>{try{return changedPreviewsSince(folder,args["changed-since"],[...design.screens.values()],design.snippets,selectedBoards)}catch(error){fail("publish",`cannot determine previews changed since '${args["changed-since"]}': ${error instanceof Error?error.message:String(error)}`)}})():void 0,listed=await(async()=>{if(!config.folderId||args.new===!0)return{effectiveTeamId:teamId??null,slots:[]};let destinations=await listPublishDestinations({baseUrl,token,folderId:config.folderId,...teamId?{teamId}:{}});if(!destinations.ok)fail("publish",describePublishError(destinations.error));return destinations.value})(),source={boardIds:selected.map((board)=>board.id),teamId:listed.effectiveTeamId,...provenance},destination=await choosePublishDestination({slots:listed.slots,source,interactive,createNew:args.new===!0,updateExisting:args.update===!0,customSlug:args.slug,manageUrl:await publishedBoardsUrl(baseUrl)}),privacy=await resolvePublishPrivacy(args,interactive,{protectedShares,...upgradeUrl?{upgradeUrl}:{}}).catch((error)=>fail("publish",error instanceof Error?error.message:String(error))),visibility=privacy.visibility,password=privacy.password?await readSharePassword():void 0,passwordExpiresAt=resolvePasswordExpiry(args["password-expires"],password!=null),{providers,defaultProvider}=await resolveProviders(config,folder);await confirmRenderFailures("publish",{folder:design,providers,defaultProvider},source.boardIds.length>0?screensForBoards(design,source.boardIds):[...design.screens.values()],args.yes===!0);let bundler=createPublishBundler(folder,config),jit=new TailwindJit(Object.values(providers),join2(folder,"screens"),void 0,()=>extraThemeBlock(design),()=>bundler.hostSourceDirs(),()=>findHostTailwindConfig(folder,config.hostApp),config.styling?.framework),progress=createProgress(),report=createPublishReporter(progress),outcome;try{let published=await publishDesign({baseUrl,token},{folder:design,providers,defaultProvider,snapshotCss:()=>jit.build(),liveBundler:bundler},{boardIds:selected.map((b)=>b.id),...args.title?{title:args.title}:{},visibility,...password?{password}:{},...passwordExpiresAt?{passwordExpiresAt}:{},destination,...teamId?{teamId}:{},provenance,viewport,...screenshotSelection?{screenshotSelection}:{}},report);if(!published.ok)progress.fail("publish failed"),fail("publish",describePublishError(published.error));outcome=published.value,progress.succeed("published design")}catch(error){progress.fail("publish failed"),fail("publish",error instanceof Error?error.message:String(error))}finally{await closePooledBrowser()}console.log(`velloo publish: ${outcome.files} files, ${Math.round(outcome.bytes/1024)} KB${outcome.screenshots>0?`, ${outcome.screenshots} screenshots`:""}${outcome.commitSha?`, commit ${outcome.commitSha.slice(0,7)}`:""}`),console.log(` ${outcome.shareUrl}`),console.log(` ${describeAccess(outcome)}`);let history=outcome.history;if(history&&!history.retained&&history.pruned>0)console.log(" replaced the previous version \u2014 the free plan keeps only the latest."),console.log(` Upgrade to Team to keep version history and revisit past publishes: ${PRICING_URL}`);else if(history?.retained&&history.versions>1)console.log(` history: ${history.versions} versions kept \u2014 pin any of them from ${outcome.shareUrl}v/<version>/`)}});function resolvePublishDestinationChoice(value,slots){if(value===null)return null;if(value==="new")return{mode:"new"};let slug=value.replace(/^update:/,""),slot=slots.find((candidate)=>candidate.slug===slug);if(!slot)return null;return{mode:"update",slug:slot.slug,expectedVersionId:slot.latestVersionId}}async function choosePublishDestination(opts){if(opts.createNew&&opts.updateExisting)fail("publish","choose either --new or --update, not both");if(opts.customSlug&&!opts.createNew)fail("publish","--slug names a new link; use it together with --new");let matching=exactPublishSlots(opts.slots,opts.source);if(opts.updateExisting){let slot=matching[0];if(!slot)fail("publish",`there is no exact published-design match to update. Manage existing boards at ${opts.manageUrl}`);return{mode:"update",slug:slot.slug,expectedVersionId:slot.latestVersionId}}if(opts.createNew)return{mode:"new",...opts.customSlug?{slug:opts.customSlug}:{}};if(matching.length===0)return(opts.log??console.log)("velloo publish: no matching published design \u2014 a new link will be created."),{mode:"new"};if(!opts.interactive)fail("publish","this folder already has published designs; pass --update or --new explicitly");let recommended=recommendedPublishSlot(opts.slots,opts.source),latestMatch=matching[0],value=await select({message:"Publish destination",initialValue:recommended?`update:${recommended.slug}`:"new",options:[...latestMatch?[{value:`update:${latestMatch.slug}`,label:`Update ${latestMatch.title||"Untitled design"}`,hint:"latest exact match for this team, board selection, and source"}]:[],{value:"new",label:"Create a new link",hint:recommended?"keep the matching design separate":"recommended for this source"}]}),destination=resolvePublishDestinationChoice(isCancel(value)?null:String(value),matching);if(!destination)fail("publish",isCancel(value)?"cancelled":"selected publish slot is no longer available");return destination}function createPublishReporter(progress){let latestCapture;return(event)=>{if(event.kind==="capture"){latestCapture=`capturing previews ${event.done}/${event.total}`,progress.step(latestCapture,{transient:!0});return}if(event.kind==="step"){if(latestCapture)progress.step(latestCapture);latestCapture=void 0,progress.step(event.message);return}progress.log(event.message)}}async function readSharePassword(){let fromEnv=process.env.VELLOO_SHARE_PASSWORD?.trim();if(fromEnv){if(fromEnv.length<3)fail("publish","VELLOO_SHARE_PASSWORD must be at least 3 characters");return fromEnv}if(!process.stdin.isTTY)fail("publish","--password needs a terminal to prompt \u2014 set $VELLOO_SHARE_PASSWORD instead");let entered=await password({message:"Password for this share link",validate:(value)=>(value??"").length<3?"At least 3 characters.":void 0});if(isCancel(entered))fail("publish","cancelled");return entered}function resolvePasswordExpiry(raw,hasPassword){if(!raw)return;if(!hasPassword)fail("publish","--password-expires needs --password");let value=/^\d{4}-\d{2}-\d{2}$/.test(raw)?`${raw}T23:59:59`:raw,when=new Date(value);if(Number.isNaN(when.getTime()))fail("publish",`--password-expires must be a date (YYYY-MM-DD) or ISO timestamp, got '${raw}'`);return when.toISOString()}function describeAccess(outcome){if(outcome.visibility==="private")return outcome.passwordProtected?"private \u2014 your organization, or anyone with the password":"private \u2014 anyone signed in at your organization";return outcome.passwordProtected?"public \u2014 anyone with the link and the password":"public \u2014 anyone with the link"}export{publish_default as default};
|