velloo 0.7.0 → 0.9.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.
Files changed (95) hide show
  1. package/BUN-LICENSE.md +4 -3
  2. package/README.md +8 -1
  3. package/THIRD-PARTY-NOTICES.md +3 -3
  4. package/canvas/assets/index-CkVteRZ9.css +2 -0
  5. package/canvas/assets/index-DFOlwAKW.js +31 -0
  6. package/canvas/assets/lucide-all-CezGNjGW.js +1 -0
  7. package/canvas/index.html +2 -2
  8. package/{chunk-qrnkrkkt.js → chunk-00n2d68z.js} +1 -1
  9. package/{chunk-mdgwdqnq.js → chunk-02gpgqj4.js} +8 -8
  10. package/{chunk-qp3wahg4.js → chunk-0ztxyzac.js} +1 -1
  11. package/{chunk-4y2xy1kf.js → chunk-2x5rw009.js} +3 -3
  12. package/{chunk-abr2hgay.js → chunk-3f2p94wa.js} +1 -1
  13. package/{chunk-srp5gyr1.js → chunk-3jqbybmp.js} +1 -1
  14. package/{chunk-0vaff40g.js → chunk-5tatkt2j.js} +1 -1
  15. package/{chunk-asfgvzse.js → chunk-640swnrm.js} +1 -1
  16. package/{chunk-bms1sg4c.js → chunk-6600wkmq.js} +1 -1
  17. package/{chunk-7gyzh0vq.js → chunk-6adv9eke.js} +1 -1
  18. package/{chunk-fvezyap4.js → chunk-6wfvb07c.js} +1 -1
  19. package/chunk-8a2agf28.js +22 -0
  20. package/{chunk-82x2jkfq.js → chunk-8kn6tp4b.js} +2 -2
  21. package/{chunk-fmcm06xb.js → chunk-9b4akcfp.js} +1 -1
  22. package/{chunk-0pxm5v0b.js → chunk-9kvbe53e.js} +3 -3
  23. package/{chunk-gfppsgvv.js → chunk-a5mxgvcz.js} +1 -1
  24. package/{chunk-g7d065ek.js → chunk-as4rn21e.js} +1 -1
  25. package/{chunk-xg71h7nb.js → chunk-avewjans.js} +4 -4
  26. package/chunk-bpb97rsk.js +17 -0
  27. package/{chunk-j306gr0c.js → chunk-bsn761jv.js} +1 -1
  28. package/{chunk-kkvf0xk2.js → chunk-c11n1tve.js} +2 -2
  29. package/{chunk-sme6hv6f.js → chunk-cfksd3zh.js} +1 -1
  30. package/{chunk-zv17t1n7.js → chunk-ck3bg8rf.js} +1 -1
  31. package/{chunk-d93kpv7s.js → chunk-cy1nmr0f.js} +1 -1
  32. package/{chunk-qqrac4va.js → chunk-dqy6a75x.js} +1 -1
  33. package/{chunk-xzf52wvn.js → chunk-f8jchnwe.js} +1 -1
  34. package/{chunk-s84vxcdv.js → chunk-fejhav0p.js} +1 -1
  35. package/{chunk-ykpctq15.js → chunk-fzqt4rsz.js} +1 -1
  36. package/{chunk-3m28pvzr.js → chunk-g7pbfad5.js} +1 -1
  37. package/{chunk-b8vcbbrg.js → chunk-gv209d5j.js} +1 -1
  38. package/chunk-j4rt31qb.js +2 -0
  39. package/{chunk-d0j8dg1e.js → chunk-kmsbcb3q.js} +1 -1
  40. package/{chunk-5s1wjhtj.js → chunk-ktps9v0k.js} +1 -1
  41. package/{chunk-667ag8ts.js → chunk-ktsdzzgx.js} +1 -1
  42. package/{chunk-0e5j6nqs.js → chunk-kvxn1cyx.js} +1 -1
  43. package/{chunk-ex36z3b3.js → chunk-n70zfv0m.js} +906 -906
  44. package/{chunk-rqd4swxx.js → chunk-n8pr0kc8.js} +1 -1
  45. package/{chunk-eh2xp4v6.js → chunk-pasvz8xw.js} +1 -1
  46. package/{chunk-j4fn2a2b.js → chunk-qewarcqf.js} +1 -1
  47. package/{chunk-dvh0b2r4.js → chunk-qf1c6jxh.js} +1 -1
  48. package/chunk-qn05hn7a.js +5 -0
  49. package/{chunk-gcce587w.js → chunk-qvdvxqs0.js} +1 -1
  50. package/{chunk-pjkkp7xa.js → chunk-r014f76h.js} +1 -1
  51. package/{chunk-acsv6m3n.js → chunk-seaar5na.js} +1 -1
  52. package/{chunk-84n18xgx.js → chunk-tax7fxav.js} +2 -2
  53. package/{chunk-gbq5wv7x.js → chunk-tmjs6xas.js} +1 -1
  54. package/{chunk-vs8j3x6t.js → chunk-v6w8akph.js} +1 -1
  55. package/{chunk-5x4nw0zz.js → chunk-w40pqmyz.js} +4 -4
  56. package/{chunk-vdbr81a3.js → chunk-x05grjd9.js} +1 -1
  57. package/{chunk-md4mpvqc.js → chunk-x61f42fj.js} +2 -2
  58. package/{chunk-531xzfq5.js → chunk-xc6mats8.js} +1 -1
  59. package/chunk-yp39f93e.js +815 -0
  60. package/{chunk-5yhnnhkd.js → chunk-ysf9388d.js} +1 -1
  61. package/{chunk-64pex3mw.js → chunk-z3r8px3d.js} +1 -1
  62. package/{chunk-ejppcfa5.js → chunk-z9kcdgj8.js} +1 -1
  63. package/chunk-zexzy6zv.js +2 -0
  64. package/chunk-zjbbd87h.js +2 -0
  65. package/cli.js +1 -1
  66. package/package.json +9 -9
  67. package/pkgs/provider-antd/src/index.ts +1 -0
  68. package/pkgs/provider-html/src/index.ts +1 -0
  69. package/pkgs/provider-mui/src/index.ts +10 -0
  70. package/pkgs/provider-none/src/index.ts +1 -0
  71. package/pkgs/schema/src/board.ts +13 -1
  72. package/pkgs/schema/src/config.ts +34 -0
  73. package/pkgs/schema/src/flow.ts +230 -0
  74. package/pkgs/schema/src/frame.ts +7 -0
  75. package/pkgs/schema/src/index.ts +22 -0
  76. package/pkgs/schema/src/node-identity.ts +48 -0
  77. package/pkgs/schema/src/node.ts +80 -9
  78. package/pkgs/schema/src/snippet-resolve.ts +43 -0
  79. package/pkgs/schema/src/theme.ts +56 -0
  80. package/plugins/claude/velloo/agents/velloo-designer.md +7 -4
  81. package/plugins/claude/velloo/commands/setup.md +13 -0
  82. package/plugins/gemini/GEMINI.md +9 -5
  83. package/plugins/gemini/commands/velloo/setup.toml +11 -0
  84. package/skills/velloo-design/SKILL.md +11 -6
  85. package/skills/velloo-setup/SKILL.md +179 -36
  86. package/canvas/assets/index-BQ5710lu.js +0 -29
  87. package/canvas/assets/index-GNLd0pCG.css +0 -2
  88. package/canvas/assets/lucide-all-DIRZdJPK.js +0 -1
  89. package/chunk-brnkprt5.js +0 -723
  90. package/chunk-bys7pqdb.js +0 -2
  91. package/chunk-g8xrwjvq.js +0 -22
  92. package/chunk-jbgde2v9.js +0 -2
  93. package/chunk-rxkvjyst.js +0 -18
  94. package/chunk-sjjnm4a1.js +0 -2
  95. package/chunk-snak5zpr.js +0 -5
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{normalizeRepoManifest,CURRENT_SCHEMA_VERSION,schemaVersionOf,planMigration}from"./chunk-5x4nw0zz.js";import{localDesignOf,findOwningManifest,recordedDesignName,loadDesignFolder}from"./chunk-brnkprt5.js";import{TOOL_VERSION}from"./chunk-3m28pvzr.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,writeFile}from"fs/promises";import{join,resolve}from"path";function isObject(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}var isAnnotationSidecar=(file)=>file.endsWith(".annotations.json"),isThemeDocument=(file)=>file.endsWith(".json");async function planRewrites(folder,dir,matches,transform){let files=[];try{files=await readdir(join(folder,dir))}catch{return[]}let out=[];for(let file of files.sort()){if(!matches(file))continue;let rel=join(dir,file),prev=await readFile(join(folder,rel),"utf8"),next=`${JSON.stringify(transform(JSON.parse(prev)),null,2)}
2
+ import{normalizeRepoManifest,CURRENT_SCHEMA_VERSION,schemaVersionOf,planMigration}from"./chunk-w40pqmyz.js";import{localDesignOf,findOwningManifest,recordedDesignName,loadDesignFolder}from"./chunk-yp39f93e.js";import{TOOL_VERSION}from"./chunk-g7pbfad5.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,writeFile}from"fs/promises";import{join,resolve}from"path";function isObject(value){return typeof value==="object"&&value!==null&&!Array.isArray(value)}var isAnnotationSidecar=(file)=>file.endsWith(".annotations.json"),isThemeDocument=(file)=>file.endsWith(".json");async function planRewrites(folder,dir,matches,transform){let files=[];try{files=await readdir(join(folder,dir))}catch{return[]}let out=[];for(let file of files.sort()){if(!matches(file))continue;let rel=join(dir,file),prev=await readFile(join(folder,rel),"utf8"),next=`${JSON.stringify(transform(JSON.parse(prev)),null,2)}
3
3
  `;if(next!==prev)out.push({rel,next})}return out}async function upgradeFolder(folder,opts={}){let configPath=join(folder,".design","config.json"),rawConfig=JSON.parse(await readFile(configPath,"utf8")),from=schemaVersionOf(rawConfig),manifest=await findOwningManifest(folder).catch(()=>null),run=planMigration(rawConfig,{name:legacyNameOf(folder,manifest)}),manifestFiles=manifest?.legacy?[manifest.path]:[];if(run.applied.length===0){if(manifest?.legacy&&!opts.dryRun)await migrateManifest(manifest);return{from,to:CURRENT_SCHEMA_VERSION,applied:[],changedFiles:manifestFiles}}let rewrites=[...await planRewrites(folder,"screens",isAnnotationSidecar,(parsed)=>Array.isArray(parsed)?parsed.map((e)=>typeof e==="object"&&e!==null?run.annotation(e):e):parsed),...await planRewrites(folder,"theme",isThemeDocument,(parsed)=>isObject(parsed)?run.theme(parsed):parsed)],nextConfig={...run.config,toolVersion:TOOL_VERSION},changedFiles=[join(".design","config.json"),...rewrites.map((r)=>r.rel),...manifestFiles];if(opts.dryRun)return{from,to:CURRENT_SCHEMA_VERSION,applied:run.applied,changedFiles};await writeFile(configPath,`${JSON.stringify(nextConfig,null,2)}
4
4
  `,"utf8");for(let r of rewrites)await writeFile(join(folder,r.rel),r.next,"utf8");if(manifest?.legacy)await migrateManifest(manifest);return await loadDesignFolder(folder),{from,to:CURRENT_SCHEMA_VERSION,applied:run.applied,changedFiles}}function legacyNameOf(folder,manifest){let local=localDesignOf(folder);if(local)return local.legacyName;let target=realOr(folder);for(let[path,name]of manifest?.legacyNames??[])if(realOr(path)===target)return name;return}function realOr(path){return existsSync(path)?realpathSync(path):resolve(path)}async function migrateManifest(manifest){for(let[path,name]of manifest.legacyNames){if(recordedDesignName(path))continue;let configPath=join(path,".design","config.json"),raw;try{raw=JSON.parse(await readFile(configPath,"utf8"))}catch{continue}if(isObject(raw))await writeFile(configPath,`${JSON.stringify({...raw,name},null,2)}
5
5
  `)}let raw=JSON.parse(await readFile(manifest.path,"utf8"));await writeFile(manifest.path,`${JSON.stringify(normalizeRepoManifest(raw).manifest,null,2)}
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{BoardSchema,isArchived,designNameIssue,toDesignName,ConfigSchema,ScreenSchema}from"./chunk-5x4nw0zz.js";import{writeJsonAtomic,listLocalDesigns,writeLocalDesign,readRepoManifestAt,findOwningManifest,recordedDesignName,findDesigns2,pickDesign}from"./chunk-brnkprt5.js";import{isCancel,multiselect,select}from"./chunk-80fjfyha.js";import{fail}from"./chunk-s23gtejf.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,rm,stat}from"fs/promises";import{homedir}from"os";import{basename,dirname,isAbsolute,join,relative,resolve,sep}from"path";var MANIFEST_FILE="velloo.json",DEFAULT_FOLDER_NAME="velloo";function findManifest(dir){return readRepoManifestAt(dir,{strict:!0})}function manifestListing(folder){return findOwningManifest(folder,{strict:!0})}function findDesigns(cwd){return findDesigns2(cwd,{strict:!0})}function sameFolder(a,b){return a===b||existsSync(a)&&existsSync(b)&&realpathSync(a)===realpathSync(b)}function assertCurrent(found){if(found.legacy)throw Error(`${found.path} is in the pre-designs format (\`projects\`) \u2014 run \`velloo upgrade\` to migrate it first.`)}async function unregisterDesign(folder,projectDir){let abs=resolve(folder),here=projectDir?await findManifest(projectDir):null,found=here?.folders.some((path)=>sameFolder(path,abs))?here:await manifestListing(abs);if(!found)return{name:null,path:null,removedManifest:!1};let index=found.folders.findIndex((path)=>sameFolder(path,abs));if(index===-1)return{name:null,path:found.path,removedManifest:!1};assertCurrent(found);let name=recordedDesignName(abs)??basename(abs),manifest=withoutDesign(found.manifest,index,name);if(manifest===null)return await rm(found.path,{force:!0}),{name,path:found.path,removedManifest:!0};return await writeJsonAtomic(found.path,manifest),{name,path:found.path,removedManifest:!1}}function withoutDesign(manifest,index,name){let designs=manifest.designs.filter((_,i)=>i!==index),{defaultDesign,...rest}=manifest;if(designs.length===0&&!rest.feedback)return null;return{...rest,designs,...defaultDesign&&defaultDesign!==name?{defaultDesign}:{}}}function isWithin(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!isAbsolute(rel)}async function findGitRoot(start){let home=homedir(),dir=resolve(start);while(dir!==home){try{return await stat(join(dir,".git")),dir}catch{}let parent=dirname(dir);if(parent===dir)return null;dir=parent}return null}var SCAN_SKIP=new Set(["node_modules",".git","dist","build",".next",".turbo","coverage",".velloo"]);async function discoverDesignFolders(root,maxDepth=3){let out=[],walk=async(dir,depth)=>{let entries;try{entries=await readdir(dir,{withFileTypes:!0})}catch{return}for(let entry of entries){if(!entry.isDirectory()||entry.name.startsWith(".")||SCAN_SKIP.has(entry.name))continue;let child=join(dir,entry.name);try{await stat(join(child,".design","config.json")),out.push(child);continue}catch{}if(depth<maxDepth)await walk(child,depth+1)}};return await walk(resolve(root),1),out.sort()}function deriveName(folder,projectDir){let base=basename(folder),nested=base===DEFAULT_FOLDER_NAME&&dirname(folder)!==projectDir;return toDesignName(nested?basename(dirname(folder)):base)??"app"}async function chooseDesignName(request){let folder=resolve(request.folder),issue=request.requested===void 0?null:designNameIssue(request.requested);if(issue)throw Error(`invalid --name ${JSON.stringify(request.requested)} \u2014 ${issue}`);let taken=await takenNames(request,folder);if(request.requested){let holder=taken.get(request.requested);if(holder)throw Error(`a design named "${request.requested}" already exists at ${holder} \u2014 pick another --name.`);return request.requested}let base;if(request.storage==="repository")base=deriveName(folder,resolve(request.checkout));else{let chosen=request.storage==="chosen"&&basename(folder)!==DEFAULT_FOLDER_NAME;base=deriveName(chosen?folder:request.appRoot)}let name=base;for(let n=2;taken.has(name);n++)name=`${base}-${n}`;return name}async function takenNames(request,folder){let from=request.checkout,taken=new Map,set=await findDesigns(from).catch(()=>null);for(let design of set?.designs??[])if(!sameFolder(design.root,folder))taken.set(design.name,design.root);if(!set?.repo)for(let sibling of await discoverDesignFolders(request.checkout,1)){let name=recordedDesignName(sibling);if(name&&!sameFolder(sibling,folder))taken.set(name,sibling)}return taken}async function writeDesignName(folder,name){let path=join(folder,".design","config.json"),config=ConfigSchema.parse(JSON.parse(await readFile(path,"utf8")));await writeJsonAtomic(path,{...config,name})}async function registerDesign(folder,projectDir){let abs=resolve(folder),name=recordedDesignName(abs);if(!name)throw Error(`${abs} has no design name \u2014 run \`velloo design upgrade ${abs}\` first.`);let dir=resolve(projectDir),found=await findManifest(dir),path=found?found.path:join(dir,MANIFEST_FILE);if(found?.folders.some((target)=>sameFolder(target,abs)))return{name,path,created:!1};if(found)assertCurrent(found);let clash=(await findDesigns(dir))?.designs.find((d)=>d.name===name&&!sameFolder(d.root,abs));if(clash)throw Error(`a design named "${name}" already exists at ${clash.root} \u2014 rename one with \`velloo design rename\`.`);let designs=[...found?.manifest.designs??[]],rel=(target)=>relative(dir,target).split("\\").join("/")||".",defaultDesign=found?.manifest.defaultDesign;if(!found)for(let sibling of await discoverDesignFolders(dir,1)){if(sibling===abs)continue;if(designs.push(rel(sibling)),sibling===join(dir,DEFAULT_FOLDER_NAME))defaultDesign=recordedDesignName(sibling)??void 0}designs.push(rel(abs));let manifest={...found?.manifest??{},designs,...defaultDesign?{defaultDesign}:{}};return await writeJsonAtomic(path,manifest),{name,path,created:!0}}async function registerLocalDesign(opts){await writeLocalDesign(opts.id,{root:opts.root,appRoot:opts.appRoot,...opts.designPath?{designPath:opts.designPath}:{}})}import{existsSync as existsSync2,readdirSync}from"fs";import{readdir as readdir2,readFile as readFile2}from"fs/promises";import{homedir as homedir2}from"os";import{basename as basename2,dirname as dirname2,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";var DEFAULT_FOLDER="velloo",DESIGN_ARG_DESCRIPTION="A design name from ./velloo.json, a design folder, or a velloo.json file or its directory (default: the design folder you're in, else this directory's velloo.json: its only design or defaultDesign)";async function existingDesignFolder(appRoot){try{let found=await findDesigns(appRoot);for(let design of found?.designs??[])if(design.exists)return design.root}catch{}let conventional=join2(resolve2(appRoot),DEFAULT_FOLDER);if(await hasDesignConfig(conventional))return conventional;let[first]=await discoverDesignFolders(resolve2(appRoot));return first??null}function isDesignFolderSync(dir){return existsSync2(join2(dir,".design","config.json"))}function isEmptyOrMissingSync(dir){try{return readdirSync(dir).length===0}catch(err){if(err.code==="ENOENT")return!0;throw err}}async function hasDesignConfig(dir){try{return await readFile2(join2(dir,".design","config.json"),"utf8"),!0}catch{return!1}}var DESIGN_ID_ARG={type:"string",description:"The design's number in `velloo design list`, instead of its name or path"};async function designFolder(set,design,abort){let repo=set.repo;if(repo&&design.outsideRepo){let entry=relative2(repo.dir,design.root);abort(`design ${JSON.stringify(design.name)} in ${repo.path} points outside the repository (${entry}), which velloo.json cannot authorize. Pass the design path explicitly, or run \`velloo design bind ${design.root}\` here to keep it as a local design on this machine.`)}if(!design.exists)abort(`design ${JSON.stringify(design.name)} ${design.local?"(a local design)":`in ${repo?.path}`} points at ${design.root}, which is not a velloo design folder (no .design/config.json).`);return design.root}function quoteArg(value){return/^[\w@%+=:,./-]+$/.test(value)?value:`'${value.replace(/'/g,"'\\''")}'`}function designsLabel(set){return set.repo?.path??"this checkout's local designs"}function byName(set,name,abort){let shared=set.conflicts.get(name);if(shared)abort(`${shared.length} designs are named ${JSON.stringify(name)}: ${shared.map((d)=>d.root).join(", ")}. Pass a path, and rename one with \`velloo design rename <path> <new-name>\`.`);return set.byName.get(name)}async function resolveDesign(arg,cmd,opts={}){let abort=opts.onFail??((m)=>fail(cmd,m)),cwd=resolve2(opts.cwd??"."),set=null;try{set=await findDesigns(cwd)}catch(err){abort(err.message)}if(opts.id!==void 0){if(arg)abort("pass a design or --id, not both.");let n=Number(opts.id);if(!Number.isInteger(n)||n<1)abort("--id takes a design's number from `velloo design list`.");if(!set||set.designs.length===0){if(n===1)return resolveDesign(void 0,cmd,{...opts,id:void 0});abort(`there is no design #${n} here \u2014 \`velloo design list\` numbers them.`)}let entry=set.designs[n-1];if(!entry)abort(`there is no design #${n} \u2014 ${designsLabel(set)} has ${set.designs.length}. \`velloo design list\` numbers them.`);return designFolder(set,entry,abort)}if(arg){let named=set&&!arg.includes(sep2)?byName(set,arg,abort):void 0;if(set&&named)return designFolder(set,named,abort);let explicit=resolve2(cwd,arg);if(await hasDesignConfig(explicit))return explicit;let project=basename2(explicit)==="velloo.json"&&existsSync2(explicit)?dirname2(explicit):explicit;if((await findDesigns(project).catch((err)=>abort(err.message)))?.designs.length)return resolveDesign(void 0,cmd,{...opts,cwd:project});let nested=join2(explicit,DEFAULT_FOLDER);if(await hasDesignConfig(nested))return nested;if(!set&&!arg.includes(sep2)){let above=await enclosingProject(cwd),match=above?.designs.find((d)=>d.name===arg);if(above&&match)abort(`design ${JSON.stringify(arg)} is listed in \`${shortPath(cwd,above.label)}\`, and commands only read the directory they run in. Run from \`${shortPath(cwd,above.dir)}\`, or pass its folder (\`${quoteArg(shortPath(cwd,match.root))}\`) instead of its name.`)}if(set&&!arg.includes(sep2))abort(`unknown design ${JSON.stringify(arg)} \u2014 ${designsLabel(set)} lists: ${set.designs.map((d)=>d.name).join(", ")}. Pass a design name or a folder path.`);if(opts.requireConfig)abort(`${explicit} is not a velloo design folder (no .design/config.json).`);return explicit}if(set&&set.designs.length>0){if(await hasDesignConfig(cwd))return cwd;let picked=pickDesign(set,cwd);if(picked&&picked.reason!=="arbitrary")return designFolder(set,picked.design,abort);let names=set.designs.map((d)=>d.name);if(opts.interactive&&process.stdin.isTTY){let chosen=await select({message:"Which design?",options:set.designs.map((d)=>({value:d.root,label:d.name,hint:relative2(cwd,d.root)}))});if(isCancel(chosen))abort("cancelled.");let design=set.designs.find((d)=>d.root===chosen);return designFolder(set,design,abort)}let example=cmd.startsWith("design ")?`velloo ${cmd} --id 1\` for ${quoteArg(names[0]??"")}, as numbered by \`velloo design list`:`velloo ${cmd} ${designExample(opts,names[0]??"")}`;abort(`${designsLabel(set)} lists several designs: ${names.join(", ")}. Pass one (e.g. \`${example}\`)${set.repo?' or set "defaultDesign"':""}.`)}if(await hasDesignConfig(cwd))return cwd;let here=join2(cwd,DEFAULT_FOLDER);if(await hasDesignConfig(here))return here;let takesDesign=opts.takesDesign!==!1,above=await enclosingProject(cwd);if(above){let nearest=[...above.designs].sort((a,b)=>relative2(cwd,a.root).length-relative2(cwd,b.root).length)[0],example=takesDesign&&nearest?`, or pass a design folder (e.g. \`velloo ${cmd} ${designExample(opts,relative2(cwd,nearest.root).split(sep2).join("/"))}\`)`:"";abort(`no velloo.json or design folder here. \`${shortPath(cwd,above.label)}\` lists ${above.designs.map((d)=>JSON.stringify(d.name)).join(", ")}, and commands only read the directory they run in: run from \`${shortPath(cwd,above.dir)}\`${example}.`)}let example=takesDesign?`, pass a design folder or a velloo.json path (e.g. \`velloo ${cmd} ${designExample(opts,"apps/web")}\`)`:"";abort(`no velloo.json or design folder in ${cwd}. Run from the directory that has your velloo.json${example}, or \`velloo init\` here.${movedCheckoutHint(cwd)}`)}function shortPath(cwd,path){let rel=relative2(cwd,path);return rel!==""&&rel.length<path.length?rel.split(sep2).join("/"):path}function designExample(opts,design){return opts.designFlag?`${opts.designFlag}=${quoteArg(design)}`:quoteArg(design)}async function enclosingProject(cwd){let here=resolve2(cwd);for(let dir=dirname2(here);dir!==here;){let set=await findDesigns(dir).catch(()=>null),designs=set?.designs.filter((d)=>d.exists&&!d.outsideRepo)??[];if(set&&designs.length>0)return{dir,label:set.repo?.path??`${dir} (local designs)`,designs};let parent=dirname2(dir);if(parent===dir)return null;dir=parent}return null}async function checkoutDesigns(cwd){let set=await findDesigns(cwd).catch(()=>null);if(set&&set.designs.length>0)return set.designs.filter((d)=>d.exists&&!d.outsideRepo).map((d)=>d.root);try{return[await resolveDesign(void 0,"upgrade",{requireConfig:!0,cwd,onFail:()=>{throw Error("no design here")}})]}catch{return[]}}async function resolveDesignForSession(arg,cmd,cwd=resolve2(".")){if(!arg&&!await hasDesignConfig(cwd)){let set=await findDesigns(cwd).catch(()=>null),picked=set?pickDesign(set,cwd):null;if(set&&picked?.reason==="arbitrary")return{folder:await designFolder(set,picked.design,(m)=>fail(cmd,m)),pick:"arbitrary"}}return{folder:await resolveDesign(arg,cmd,{cwd}),pick:void 0}}function movedCheckoutHint(cwd){if(!insideProject(cwd))return"";let orphans=listLocalDesigns().filter((design)=>!existsSync2(design.root)&&existsSync2(design.designRoot));if(orphans.length===0)return"";return`
2
+ import{BoardSchema,isArchived,designNameIssue,toDesignName,ConfigSchema,ScreenSchema}from"./chunk-w40pqmyz.js";import{writeJsonAtomic,listLocalDesigns,writeLocalDesign,readRepoManifestAt,findOwningManifest,recordedDesignName,findDesigns2,pickDesign}from"./chunk-yp39f93e.js";import{isCancel,multiselect,select}from"./chunk-80fjfyha.js";import{fail}from"./chunk-s23gtejf.js";import{existsSync,realpathSync}from"fs";import{readdir,readFile,rm,stat}from"fs/promises";import{homedir}from"os";import{basename,dirname,isAbsolute,join,relative,resolve,sep}from"path";var MANIFEST_FILE="velloo.json",DEFAULT_FOLDER_NAME="velloo";function findManifest(dir){return readRepoManifestAt(dir,{strict:!0})}function manifestListing(folder){return findOwningManifest(folder,{strict:!0})}function findDesigns(cwd){return findDesigns2(cwd,{strict:!0})}function sameFolder(a,b){return a===b||existsSync(a)&&existsSync(b)&&realpathSync(a)===realpathSync(b)}function assertCurrent(found){if(found.legacy)throw Error(`${found.path} is in the pre-designs format (\`projects\`) \u2014 run \`velloo upgrade\` to migrate it first.`)}async function unregisterDesign(folder,projectDir){let abs=resolve(folder),here=projectDir?await findManifest(projectDir):null,found=here?.folders.some((path)=>sameFolder(path,abs))?here:await manifestListing(abs);if(!found)return{name:null,path:null,removedManifest:!1};let index=found.folders.findIndex((path)=>sameFolder(path,abs));if(index===-1)return{name:null,path:found.path,removedManifest:!1};assertCurrent(found);let name=recordedDesignName(abs)??basename(abs),manifest=withoutDesign(found.manifest,index,name);if(manifest===null)return await rm(found.path,{force:!0}),{name,path:found.path,removedManifest:!0};return await writeJsonAtomic(found.path,manifest),{name,path:found.path,removedManifest:!1}}function withoutDesign(manifest,index,name){let designs=manifest.designs.filter((_,i)=>i!==index),{defaultDesign,...rest}=manifest;if(designs.length===0&&!rest.feedback)return null;return{...rest,designs,...defaultDesign&&defaultDesign!==name?{defaultDesign}:{}}}function isWithin(parent,child){let rel=relative(parent,child);return rel===""||!rel.startsWith("..")&&!isAbsolute(rel)}async function findGitRoot(start){let home=homedir(),dir=resolve(start);while(dir!==home){try{return await stat(join(dir,".git")),dir}catch{}let parent=dirname(dir);if(parent===dir)return null;dir=parent}return null}var SCAN_SKIP=new Set(["node_modules",".git","dist","build",".next",".turbo","coverage",".velloo"]);async function discoverDesignFolders(root,maxDepth=3){let out=[],walk=async(dir,depth)=>{let entries;try{entries=await readdir(dir,{withFileTypes:!0})}catch{return}for(let entry of entries){if(!entry.isDirectory()||entry.name.startsWith(".")||SCAN_SKIP.has(entry.name))continue;let child=join(dir,entry.name);try{await stat(join(child,".design","config.json")),out.push(child);continue}catch{}if(depth<maxDepth)await walk(child,depth+1)}};return await walk(resolve(root),1),out.sort()}function deriveName(folder,projectDir){let base=basename(folder),nested=base===DEFAULT_FOLDER_NAME&&dirname(folder)!==projectDir;return toDesignName(nested?basename(dirname(folder)):base)??"app"}async function chooseDesignName(request){let folder=resolve(request.folder),issue=request.requested===void 0?null:designNameIssue(request.requested);if(issue)throw Error(`invalid --name ${JSON.stringify(request.requested)} \u2014 ${issue}`);let taken=await takenNames(request,folder);if(request.requested){let holder=taken.get(request.requested);if(holder)throw Error(`a design named "${request.requested}" already exists at ${holder} \u2014 pick another --name.`);return request.requested}let base;if(request.storage==="repository")base=deriveName(folder,resolve(request.checkout));else{let chosen=request.storage==="chosen"&&basename(folder)!==DEFAULT_FOLDER_NAME;base=deriveName(chosen?folder:request.appRoot)}let name=base;for(let n=2;taken.has(name);n++)name=`${base}-${n}`;return name}async function takenNames(request,folder){let from=request.checkout,taken=new Map,set=await findDesigns(from).catch(()=>null);for(let design of set?.designs??[])if(!sameFolder(design.root,folder))taken.set(design.name,design.root);if(!set?.repo)for(let sibling of await discoverDesignFolders(request.checkout,1)){let name=recordedDesignName(sibling);if(name&&!sameFolder(sibling,folder))taken.set(name,sibling)}return taken}async function writeDesignName(folder,name){let path=join(folder,".design","config.json"),config=ConfigSchema.parse(JSON.parse(await readFile(path,"utf8")));await writeJsonAtomic(path,{...config,name})}async function registerDesign(folder,projectDir){let abs=resolve(folder),name=recordedDesignName(abs);if(!name)throw Error(`${abs} has no design name \u2014 run \`velloo design upgrade ${abs}\` first.`);let dir=resolve(projectDir),found=await findManifest(dir),path=found?found.path:join(dir,MANIFEST_FILE);if(found?.folders.some((target)=>sameFolder(target,abs)))return{name,path,created:!1};if(found)assertCurrent(found);let clash=(await findDesigns(dir))?.designs.find((d)=>d.name===name&&!sameFolder(d.root,abs));if(clash)throw Error(`a design named "${name}" already exists at ${clash.root} \u2014 rename one with \`velloo design rename\`.`);let designs=[...found?.manifest.designs??[]],rel=(target)=>relative(dir,target).split("\\").join("/")||".",defaultDesign=found?.manifest.defaultDesign;if(!found)for(let sibling of await discoverDesignFolders(dir,1)){if(sibling===abs)continue;if(designs.push(rel(sibling)),sibling===join(dir,DEFAULT_FOLDER_NAME))defaultDesign=recordedDesignName(sibling)??void 0}designs.push(rel(abs));let manifest={...found?.manifest??{},designs,...defaultDesign?{defaultDesign}:{}};return await writeJsonAtomic(path,manifest),{name,path,created:!0}}async function registerLocalDesign(opts){await writeLocalDesign(opts.id,{root:opts.root,appRoot:opts.appRoot,...opts.designPath?{designPath:opts.designPath}:{}})}import{existsSync as existsSync2,readdirSync}from"fs";import{readdir as readdir2,readFile as readFile2}from"fs/promises";import{homedir as homedir2}from"os";import{basename as basename2,dirname as dirname2,join as join2,relative as relative2,resolve as resolve2,sep as sep2}from"path";var DEFAULT_FOLDER="velloo",DESIGN_ARG_DESCRIPTION="A design name from ./velloo.json, a design folder, or a velloo.json file or its directory (default: the design folder you're in, else this directory's velloo.json: its only design or defaultDesign)";async function existingDesignFolder(appRoot){try{let found=await findDesigns(appRoot);for(let design of found?.designs??[])if(design.exists)return design.root}catch{}let conventional=join2(resolve2(appRoot),DEFAULT_FOLDER);if(await hasDesignConfig(conventional))return conventional;let[first]=await discoverDesignFolders(resolve2(appRoot));return first??null}function isDesignFolderSync(dir){return existsSync2(join2(dir,".design","config.json"))}function isEmptyOrMissingSync(dir){try{return readdirSync(dir).length===0}catch(err){if(err.code==="ENOENT")return!0;throw err}}async function hasDesignConfig(dir){try{return await readFile2(join2(dir,".design","config.json"),"utf8"),!0}catch{return!1}}var DESIGN_ID_ARG={type:"string",description:"The design's number in `velloo design list`, instead of its name or path"};async function designFolder(set,design,abort){let repo=set.repo;if(repo&&design.outsideRepo){let entry=relative2(repo.dir,design.root);abort(`design ${JSON.stringify(design.name)} in ${repo.path} points outside the repository (${entry}), which velloo.json cannot authorize. Pass the design path explicitly, or run \`velloo design bind ${design.root}\` here to keep it as a local design on this machine.`)}if(!design.exists)abort(`design ${JSON.stringify(design.name)} ${design.local?"(a local design)":`in ${repo?.path}`} points at ${design.root}, which is not a velloo design folder (no .design/config.json).`);return design.root}function quoteArg(value){return/^[\w@%+=:,./-]+$/.test(value)?value:`'${value.replace(/'/g,"'\\''")}'`}function designsLabel(set){return set.repo?.path??"this checkout's local designs"}function byName(set,name,abort){let shared=set.conflicts.get(name);if(shared)abort(`${shared.length} designs are named ${JSON.stringify(name)}: ${shared.map((d)=>d.root).join(", ")}. Pass a path, and rename one with \`velloo design rename <path> <new-name>\`.`);return set.byName.get(name)}async function resolveDesign(arg,cmd,opts={}){let abort=opts.onFail??((m)=>fail(cmd,m)),cwd=resolve2(opts.cwd??"."),set=null;try{set=await findDesigns(cwd)}catch(err){abort(err.message)}if(opts.id!==void 0){if(arg)abort("pass a design or --id, not both.");let n=Number(opts.id);if(!Number.isInteger(n)||n<1)abort("--id takes a design's number from `velloo design list`.");if(!set||set.designs.length===0){if(n===1)return resolveDesign(void 0,cmd,{...opts,id:void 0});abort(`there is no design #${n} here \u2014 \`velloo design list\` numbers them.`)}let entry=set.designs[n-1];if(!entry)abort(`there is no design #${n} \u2014 ${designsLabel(set)} has ${set.designs.length}. \`velloo design list\` numbers them.`);return designFolder(set,entry,abort)}if(arg){let named=set&&!arg.includes(sep2)?byName(set,arg,abort):void 0;if(set&&named)return designFolder(set,named,abort);let explicit=resolve2(cwd,arg);if(await hasDesignConfig(explicit))return explicit;let project=basename2(explicit)==="velloo.json"&&existsSync2(explicit)?dirname2(explicit):explicit;if((await findDesigns(project).catch((err)=>abort(err.message)))?.designs.length)return resolveDesign(void 0,cmd,{...opts,cwd:project});let nested=join2(explicit,DEFAULT_FOLDER);if(await hasDesignConfig(nested))return nested;if(!set&&!arg.includes(sep2)){let above=await enclosingProject(cwd),match=above?.designs.find((d)=>d.name===arg);if(above&&match)abort(`design ${JSON.stringify(arg)} is listed in \`${shortPath(cwd,above.label)}\`, and commands only read the directory they run in. Run from \`${shortPath(cwd,above.dir)}\`, or pass its folder (\`${quoteArg(shortPath(cwd,match.root))}\`) instead of its name.`)}if(set&&!arg.includes(sep2))abort(`unknown design ${JSON.stringify(arg)} \u2014 ${designsLabel(set)} lists: ${set.designs.map((d)=>d.name).join(", ")}. Pass a design name or a folder path.`);if(opts.requireConfig)abort(`${explicit} is not a velloo design folder (no .design/config.json).`);return explicit}if(set&&set.designs.length>0){if(await hasDesignConfig(cwd))return cwd;let picked=pickDesign(set,cwd);if(picked&&picked.reason!=="arbitrary")return designFolder(set,picked.design,abort);let names=set.designs.map((d)=>d.name);if(opts.interactive&&process.stdin.isTTY){let chosen=await select({message:"Which design?",options:set.designs.map((d)=>({value:d.root,label:d.name,hint:relative2(cwd,d.root)}))});if(isCancel(chosen))abort("cancelled.");let design=set.designs.find((d)=>d.root===chosen);return designFolder(set,design,abort)}let example=cmd.startsWith("design ")?`velloo ${cmd} --id 1\` for ${quoteArg(names[0]??"")}, as numbered by \`velloo design list`:`velloo ${cmd} ${designExample(opts,names[0]??"")}`;abort(`${designsLabel(set)} lists several designs: ${names.join(", ")}. Pass one (e.g. \`${example}\`)${set.repo?' or set "defaultDesign"':""}.`)}if(await hasDesignConfig(cwd))return cwd;let here=join2(cwd,DEFAULT_FOLDER);if(await hasDesignConfig(here))return here;let takesDesign=opts.takesDesign!==!1,above=await enclosingProject(cwd);if(above){let nearest=[...above.designs].sort((a,b)=>relative2(cwd,a.root).length-relative2(cwd,b.root).length)[0],example=takesDesign&&nearest?`, or pass a design folder (e.g. \`velloo ${cmd} ${designExample(opts,relative2(cwd,nearest.root).split(sep2).join("/"))}\`)`:"";abort(`no velloo.json or design folder here. \`${shortPath(cwd,above.label)}\` lists ${above.designs.map((d)=>JSON.stringify(d.name)).join(", ")}, and commands only read the directory they run in: run from \`${shortPath(cwd,above.dir)}\`${example}.`)}let example=takesDesign?`, pass a design folder or a velloo.json path (e.g. \`velloo ${cmd} ${designExample(opts,"apps/web")}\`)`:"";abort(`no velloo.json or design folder in ${cwd}. Run from the directory that has your velloo.json${example}, or \`velloo init\` here.${movedCheckoutHint(cwd)}`)}function shortPath(cwd,path){let rel=relative2(cwd,path);return rel!==""&&rel.length<path.length?rel.split(sep2).join("/"):path}function designExample(opts,design){return opts.designFlag?`${opts.designFlag}=${quoteArg(design)}`:quoteArg(design)}async function enclosingProject(cwd){let here=resolve2(cwd);for(let dir=dirname2(here);dir!==here;){let set=await findDesigns(dir).catch(()=>null),designs=set?.designs.filter((d)=>d.exists&&!d.outsideRepo)??[];if(set&&designs.length>0)return{dir,label:set.repo?.path??`${dir} (local designs)`,designs};let parent=dirname2(dir);if(parent===dir)return null;dir=parent}return null}async function checkoutDesigns(cwd){let set=await findDesigns(cwd).catch(()=>null);if(set&&set.designs.length>0)return set.designs.filter((d)=>d.exists&&!d.outsideRepo).map((d)=>d.root);try{return[await resolveDesign(void 0,"upgrade",{requireConfig:!0,cwd,onFail:()=>{throw Error("no design here")}})]}catch{return[]}}async function resolveDesignForSession(arg,cmd,cwd=resolve2(".")){if(!arg&&!await hasDesignConfig(cwd)){let set=await findDesigns(cwd).catch(()=>null),picked=set?pickDesign(set,cwd):null;if(set&&picked?.reason==="arbitrary")return{folder:await designFolder(set,picked.design,(m)=>fail(cmd,m)),pick:"arbitrary"}}return{folder:await resolveDesign(arg,cmd,{cwd}),pick:void 0}}function movedCheckoutHint(cwd){if(!insideProject(cwd))return"";let orphans=listLocalDesigns().filter((design)=>!existsSync2(design.root)&&existsSync2(design.designRoot));if(orphans.length===0)return"";return`
3
3
  This machine has designs for checkouts that no longer exist at their old paths:${orphans.map((design)=>`
4
4
  "${recordedDesignName(design.designRoot)??design.legacyName??design.id}" at ${design.designRoot}, made for ${design.root}`).join("")}
5
5
  If this is one of those checkouts, moved, attach its design with \`velloo design bind <design folder>\`. Otherwise forget it with \`velloo design remove <design folder>\`.`}function insideProject(cwd){let home=homedir2();for(let dir=cwd;dir!==home&&dirname2(dir)!==dir;dir=dirname2(dir))if(existsSync2(join2(dir,".git"))||existsSync2(join2(dir,"package.json")))return!0;return!1}async function listScreens(folder){let files;try{files=(await readdir2(join2(folder,"screens"))).filter((f)=>f.endsWith(".json"))}catch{return[]}let out=[];for(let f of files.sort()){let path=join2(folder,"screens",f);try{let s=ScreenSchema.parse(JSON.parse(await readFile2(path,"utf8")));out.push({id:s.id,name:s.name||s.id,path})}catch{}}return out}async function listBoards(folder){let files;try{files=(await readdir2(join2(folder,"boards"))).filter((f)=>f.endsWith(".json"))}catch{return[]}let out=[];for(let f of files.sort())try{let b=BoardSchema.parse(JSON.parse(await readFile2(join2(folder,"boards",f),"utf8")));out.push({id:b.id,name:b.name||b.id,screens:b.frames.map((fr)=>fr.screen),archived:isArchived(b)})}catch{}return out}function boardsByScreen(boards){let map=new Map;for(let b of boards)for(let sid of b.screens){let names=map.get(sid)??[];names.push(b.name),map.set(sid,names)}return map}async function pickBoards(folder,arg,interactive,cmd){let all=await listBoards(folder);if(all.length===0)return[];if(arg){let ids=new Set(arg.split(",").map((s)=>s.trim()).filter(Boolean)),chosen=all.filter((b)=>ids.has(b.id));if(chosen.length===0)fail(cmd,`no boards matched "${arg}". Available: ${all.map((b)=>b.id).join(", ")}`);return chosen}let boards=all.filter((b)=>!b.archived);if(boards.length===0)return[];if(!interactive)return boards;let chosen=await multiselect(boardSelectionPrompt(boards));if(isCancel(chosen))fail(cmd,"cancelled.");return resolveBoardSelection(boards,chosen)}function boardSelectionPrompt(boards){return{message:"Which boards to publish? (a: select all / clear all)",options:boards.map((board)=>({value:board.id,label:board.name,hint:`${board.screens.length} screen${board.screens.length===1?"":"s"}`})),initialValues:[],required:!1}}function resolveBoardSelection(boards,selectedIds){let selected=new Set(selectedIds),chosen=boards.filter((board)=>selected.has(board.id));return chosen.length>0?chosen:null}async function pickScreen(folder,arg,interactive,cmd){let screens=await listScreens(folder);if(arg){let byId=screens.find((s)=>s.id===arg);if(byId)return byId;return{id:arg,name:arg,path:resolve2(arg)}}if(screens.length===0)fail(cmd,`no screens found in ${folder}/screens.`);if(screens.length===1)return screens[0];if(!interactive)fail(cmd,`pick a screen: ${screens.map((s)=>s.id).join(", ")}`);let onBoard=boardsByScreen(await listBoards(folder)),chosen=await select({message:"Which screen?",options:screens.map((s)=>({value:s.id,label:s.name,hint:onBoard.get(s.id)?.join(", ")??`${s.id}.json`}))});if(isCancel(chosen))fail(cmd,"cancelled.");return screens.find((s)=>s.id===chosen)}
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{ConfigSchema}from"./chunk-5x4nw0zz.js";import{readFile}from"fs/promises";import{dirname,join,resolve}from"path";async function findDesignConfig(startPath){let dir=resolve(startPath);if(!dir.endsWith("/"))dir=dirname(dir);while(!0){let candidate=join(dir,".design","config.json");try{let raw=await readFile(candidate,"utf8"),config=ConfigSchema.parse(JSON.parse(raw));return{folder:dir,config}}catch(err){if(err.code!=="ENOENT")throw err}let parent=dirname(dir);if(parent===dir)return null;dir=parent}}
2
+ import{ConfigSchema}from"./chunk-w40pqmyz.js";import{readFile}from"fs/promises";import{dirname,join,resolve}from"path";async function findDesignConfig(startPath){let dir=resolve(startPath);if(!dir.endsWith("/"))dir=dirname(dir);while(!0){let candidate=join(dir,".design","config.json");try{let raw=await readFile(candidate,"utf8"),config=ConfigSchema.parse(JSON.parse(raw));return{folder:dir,config}}catch(err){if(err.code!=="ENOENT")throw err}let parent=dirname(dir);if(parent===dir)return null;dir=parent}}
3
3
  export{findDesignConfig};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{NEVER,string,number,boolean,_null,unknown,array,object,strictObject,union,record,_enum,literal,preprocess,FrameSchemeSchema,MAX_BOARD_NAME_LENGTH,NodeIdSchema,RepoComponentRefSchema,NodeSchema,SnippetParamSchema}from"./chunk-w40pqmyz.js";function ok(value){return{ok:!0,value}}function err(error){return{ok:!1,error}}function map(r,fn){return r.ok?ok(fn(r.value)):r}function mapError(r,fn){return r.ok?r:err(fn(r.error))}async function tryCatchAsync(fn,onError){try{return ok(await fn())}catch(thrown){return err(onError(thrown))}}function*$(r){if(!r.ok)throw yield r,Error("unreachable: generator resumed after err");return r.value}async function DoAsync(gen){let iter=gen(),step=await iter.next();while(!step.done){let r=step.value;if(!r.ok)return r;step=await iter.next(r.value)}return ok(step.value)}var CLOUD_ERROR_CODES=["bad_request","unauthorized","sign_in_required","insufficient_credits","forbidden","not_found","conflict","payload_too_large","unsupported_media_type","rate_limited","generation_failed","billing_upstream","unavailable","storage_unavailable","internal"];function isCloudErrorCode(value){return typeof value==="string"&&CLOUD_ERROR_CODES.includes(value)}var ACTIVITY_EVENT_TYPE="activity",SocketFrameSchema=union([object({type:literal("screen-changed"),screenId:string().min(1)}),object({type:literal("board-changed"),boardId:string().min(1)}),object({type:literal("theme-changed")}),object({type:literal("snippet-changed"),snippetId:string().min(1)}),object({type:literal("annotations-changed"),screenId:string().min(1)}),object({type:literal("notes-changed"),boardId:string().min(1)}),object({type:literal("comments-changed"),boardId:string().min(1),scope:_enum(["local","shared"])}),object({type:literal("config-changed")}),object({type:literal("folder-reloaded")}),object({type:literal("reload-error"),source:string(),message:string()}),object({type:literal(ACTIVITY_EVENT_TYPE)}).loose()]);var Visibility=_enum(["public","private"]),AuthConfigResponseSchema=object({appUrl:string().optional(),issuer:string().optional(),clientId:string().optional()}),HealthResponseSchema=object({ok:boolean().optional(),db:object({ok:boolean().optional()}).optional(),blob:object({ok:boolean().optional()}).optional()}),AccountResponseSchema=object({email:string(),name:string().optional(),tier:string().optional(),creditMicros:number().nullable().optional(),accountId:string().optional()});function protectedSharesAllowed(tier){return tier!=="free"}var PROTECTED_SHARES_UNAVAILABLE="private and password-protected links need a paid plan \u2014 free accounts publish public links only";function teamOnlyAllowed(tier){return tier===void 0||tier==="business"||tier==="enterprise"}var GUESTS_UNAVAILABLE="sharing a board with guests needs a paid plan \u2014 upgrade your plan to invite guests",PublishSlotSchema=object({slug:string(),url:string(),title:string(),teamId:string().nullable(),visibility:Visibility,passwordProtected:boolean(),latestVersionId:string().nullable(),lastPublishedAt:string().nullable(),context:object({boardIds:array(string()),selectionFingerprint:string().nullable(),contextKnown:boolean(),repo:string().nullable(),branch:string().nullable()})}),PublishDestinationsResponseSchema=object({effectiveTeamId:string().nullable(),slots:array(PublishSlotSchema)}),LinkAudienceEntrySchema=object({type:_enum(["organization","team","account"]),id:string(),name:string().optional()}),LinkResponseSchema=object({slug:string(),visibility:Visibility.optional(),passwordProtected:boolean().optional(),audience:array(LinkAudienceEntrySchema).optional(),publicComments:boolean().optional()}),LinkAccessResponseSchema=object({visibility:Visibility,passwordProtected:boolean(),audience:array(LinkAudienceEntrySchema).optional(),publicComments:boolean().optional()}),VersionUploadResponseSchema=object({files:number(),bytes:number(),url:string(),tier:string().optional(),history:object({retained:boolean(),versions:number(),pruned:number()}).optional()}),PublishedDesignSchema=object({slug:string(),title:string().nullable(),url:string(),visibility:Visibility,passwordProtected:boolean(),canManage:boolean(),mine:boolean(),ownerEmail:string().nullable().optional(),ownerName:string().nullable().optional(),teamName:string().nullable().optional(),audience:array(LinkAudienceEntrySchema).optional(),publicComments:boolean().optional(),guestCount:number().optional(),git:object({repo:string().optional(),branch:string().optional()}).nullable().optional(),published:boolean(),lastPublishedAt:string().nullable()}),PublishedDesignsResponseSchema=object({links:array(PublishedDesignSchema).optional()}),TeamSchema=object({id:string(),name:string(),isDefault:boolean().optional(),role:string().optional(),canPublish:boolean().optional(),canManage:boolean().optional()}),TeamsResponseSchema=object({teams:array(TeamSchema)}),GuestSchema=object({id:string(),name:string().nullable(),email:string().optional(),createdAt:string(),lastSeenAt:string().nullable(),linkExpiresAt:string().nullable().optional()}),GuestsResponseSchema=object({guests:array(GuestSchema)}),GuestInviteResponseSchema=object({guest:GuestSchema,delivery:object({sent:boolean(),reason:string().optional()}),guestUrl:string().optional()}),GuestLinkResponseSchema=object({guestUrl:string()}),DeviceCodeResponseSchema=object({device_code:string(),user_code:string(),verification_uri:string(),verification_uri_complete:string(),expires_in:number(),interval:number()}),DeviceTokenResponseSchema=object({access_token:string().optional(),error:string().optional(),error_description:string().optional()}),CliTokenResponseSchema=object({token:string(),email:string()}),IntentPriceSchema=object({intent:string(),summary:string(),priceMicros:number(),output:_enum(["image","svg"]),aspects:array(string()),defaultAspect:string(),reference:_enum(["forbidden","optional","required"])}),IntentCatalogResponseSchema=object({intents:array(IntentPriceSchema),maxCount:number().optional()}),GeneratedAssetPayloadSchema=object({kind:_enum(["image","svg"]),dataUrl:string()}),GenerateResponseSchema=object({id:string(),intent:string().optional(),chargedMicros:number(),balanceMicros:number(),assets:array(GeneratedAssetPayloadSchema).min(1).optional(),kind:_enum(["image","svg"]).optional(),dataUrl:string().optional()}).transform((r,ctx)=>{let assets=r.assets??(r.dataUrl&&r.kind?[{kind:r.kind,dataUrl:r.dataUrl}]:null);if(!assets)return ctx.addIssue({code:"custom",message:"no assets in the generation reply"}),NEVER;return{id:r.id,intent:r.intent??"",assets,chargedMicros:r.chargedMicros,balanceMicros:r.balanceMicros}}),FeedbackIssuerResponseSchema=object({scheme:string(),publicKey:string()}),FeedbackSignaturesResponseSchema=object({signatures:array(string())});var unreachable=(error,opts={})=>({kind:"Unreachable",detail:error instanceof Error?error.message:String(error),...opts.url!==void 0?{url:opts.url}:{},...opts.timedOut?{timedOut:!0}:{}}),httpFailure=(operation,status,detail,code)=>({kind:"HttpFailure",operation,status,detail:detail&&detail.length>0?detail:"unknown",...code!==void 0?{code}:{}}),loggedOut=(detail)=>detail===void 0?{kind:"LoggedOut"}:{kind:"LoggedOut",detail},protocolViolation=(detail)=>({kind:"ProtocolViolation",detail}),uploadRaceLost=()=>({kind:"UploadRaceLost"}),insecureCloudUrl=(url)=>({kind:"InsecureCloudUrl",url}),invalidRequest=(detail)=>({kind:"InvalidRequest",detail});async function readFailure(res){let body=await res.json().catch(()=>{return});if(!body||typeof body!=="object")return{detail:"unknown"};let{message,error}=body,detail=typeof message==="string"&&message.length>0?message:"unknown";return isCloudErrorCode(error)?{detail,code:error}:{detail}}function cloudFailure(operation,status,detail,code){if(status!==401&&code!=="unauthorized")return httpFailure(operation,status,detail,code);return loggedOut(detail&&detail.length>0&&detail!=="unknown"?`velloo-cloud rejected the stored credential (${detail})`:"velloo-cloud rejected the stored credential")}async function httpFailureFrom(operation,res){let{detail,code}=await readFailure(res);return cloudFailure(operation,res.status,detail,code)}function boardLimitFrom(error){if(error.kind!=="HttpFailure"||error.status!==403)return null;let match=/\bthe (\w+) plan is limited to (\d+) active cloud boards\b/.exec(error.detail);if(!match?.[1]||!match[2])return null;return{tier:match[1],limit:Number(match[2])}}function describeBoardLimit({tier,limit}){return`the ${tier} plan keeps ${limit} board${limit===1?"":"s"} published at a time \u2014 take one down to publish another`}var serverTrouble=(status)=>status>=500?" \u2014 the cloud is having trouble; check its status or try again later":"";function describeCloudError(error){switch(error.kind){case"Unreachable":{let target=error.url?`cannot reach ${error.url}`:"cannot reach the cloud";return error.timedOut?`${target} \u2014 timed out (${error.detail}); check your connection and try again`:`${target} (${error.detail}) \u2014 check your connection and try again`}case"HttpFailure":return`${error.operation} failed (${error.status}): ${error.detail}${serverTrouble(error.status)}`;case"LoggedOut":return error.detail?`${error.detail} \u2014 run \`velloo login\``:"you are signed out \u2014 run `velloo login`";case"ProtocolViolation":return`${error.detail} \u2014 this is a bug; please report it`;case"UploadRaceLost":return"upload confirmation was lost and the retry found the publish slot changed \u2014 "+"the publish may have succeeded; check your published boards before retrying";case"InsecureCloudUrl":return`refusing to send credentials to ${error.url} over an insecure connection`;case"InvalidRequest":return error.detail;default:{let exhaustive=error;return"the cloud request failed"}}}async function cloudJson(res,schema,operation){let body;try{body=await res.json()}catch(error){return err(protocolViolation(`${operation}: the cloud's response was not JSON (${error instanceof Error?error.message:String(error)})`))}let parsed=schema.safeParse(body);if(!parsed.success)return err(protocolViolation(`${operation}: the cloud's response did not match the protocol`));return ok(parsed.data)}async function cloudFetch(url,schema,opts){let{operation,token,timeoutMs,...init}=opts,res;try{res=await fetch(url,{...init,headers:{...token!==void 0?{authorization:`Bearer ${token}`}:{},...init.headers},...timeoutMs!==void 0?{signal:AbortSignal.timeout(timeoutMs)}:{}})}catch(error){let timedOut=error instanceof Error&&error.name==="TimeoutError";return err(unreachable(error,{url,...timedOut?{timedOut:!0}:{}}))}if(!res.ok)return err(await httpFailureFrom(operation,res));return cloudJson(res,schema,operation)}var PathArraySchema=array(number()),IdLocatorSchema=string().regex(/^@[a-zA-Z][a-zA-Z0-9_-]*$/,{message:"id locator must match /^@[a-zA-Z][a-zA-Z0-9_-]*$/"}),jsonTolerant=(schema)=>preprocess((v)=>{if(typeof v==="string"){let t=v.trim();if(t.startsWith("[")||t.startsWith("{"))try{return JSON.parse(t)}catch{}}return v},schema),singularTolerant=(schema)=>preprocess((v)=>typeof v==="object"&&v!==null&&!Array.isArray(v)?[v]:v,schema),LocatorSchema=jsonTolerant(preprocess((v)=>typeof v==="string"&&/^\d+(\.\d+)*$/.test(v)?v.split(".").map(Number):v,union([PathArraySchema,IdLocatorSchema]))).describe('Node address: an "@id" like "@hero-cta", or an index path from the root ([0,2,1])'),LocatorOrRootSchema=LocatorSchema.default([]),InnerPathSchema=string().describe('Body node: "@id" (preferred \u2014 survives restructures), a dotted index path ("0.2"), or "" for the root'),PatchRecordSchema=record(string(),unknown());var invalid=(message,issues)=>issues===void 0?{ok:!1,message}:{ok:!1,message,issues},ScreenId=string().min(1),BoardId=string().min(1),FrameId=string().min(1),SnippetId=string().min(1),FramePatchSchema=strictObject({x:number().optional(),y:number().optional(),w:number().int().positive().optional(),h:number().int().positive().optional(),label:string().nullable().optional(),group:string().nullable().optional(),scheme:FrameSchemeSchema.nullable().optional(),fit:literal("content").nullable().optional()}),EmitAsSchema=object({name:string().min(1),importPath:string().min(1)}).describe("Mark the node a host-component facade: the canvas renders the subtree, emit_code emits <name /> from importPath instead."),AnnotationPositionSchema=union([object({x:number(),y:number()}),literal("auto")]),addNodeShape={screenId:ScreenId,parentPath:LocatorOrRootSchema,componentRef:string().min(1),id:NodeIdSchema.optional(),props:PatchRecordSchema.optional(),children:jsonTolerant(union([array(NodeSchema),string(),number()])).optional(),index:number().int().nonnegative().optional(),emitAs:EmitAsSchema.optional(),repo:RepoComponentRefSchema.optional().describe("Place an app component by identity (importPath + exportName [+ member]) when it isn't in list_components' repo catalog yet")},AddNodeBody=strictObject(addNodeShape);function normalizeAddNode(input){let{children,...rest}=input;if(typeof children==="string"||typeof children==="number")return invalid("add_node: `children` must be an array of nodes.",[{code:"invalid_type",expected:"array",path:["children"]}]);let props=input.props;return{ok:!0,args:{...rest,...props===void 0?{}:{props},...children===void 0?{}:{children}}}}var StylePayloadSchema=jsonTolerant(union([string(),PatchRecordSchema,_null()])).describe("Native style channel: a className string (Tailwind), an object of properties (sx / style), or null to clear"),PropsPatchEntrySchema=object({path:LocatorSchema,propPatch:PatchRecordSchema.optional(),style:StylePayloadSchema.optional()}).refine((e)=>e.propPatch!==void 0||e.style!==void 0,{message:"each patch needs propPatch, style, or both"}),GestureSchema=string().max(64).optional().describe("Opaque id of the drag this write belongs to; merges its undo steps into one"),updatePropsShape={screenId:ScreenId,patches:jsonTolerant(singularTolerant(array(PropsPatchEntrySchema).min(1))).describe("One entry per node; length 1 for a single edit"),gesture:GestureSchema},UpdatePropsBody=strictObject(updatePropsShape),applyClassesShape={screenId:ScreenId,path:LocatorOrRootSchema,classes:string(),gesture:GestureSchema},ApplyClassesBody=strictObject(applyClassesShape),removeNodeShape={screenId:ScreenId,path:LocatorSchema},RemoveNodeBody=strictObject(removeNodeShape),moveNodeShape={screenId:ScreenId,fromPath:LocatorSchema,toParent:LocatorSchema,toIndex:number().int().nonnegative().optional()},MoveNodeBody=strictObject(moveNodeShape),setNodeIdShape={screenId:ScreenId,path:LocatorSchema,id:NodeIdSchema.nullable()},SetNodeIdBody=strictObject(setNodeIdShape),RoutePath=string().trim().min(1).transform((value)=>value.startsWith("/")?value:`/${value}`),addScreenShape={name:string().min(1),id:string().min(1).optional(),fromScreenId:string().min(1).optional(),tree:NodeSchema.optional(),route:RoutePath.optional()},AddScreenBody=strictObject(addScreenShape),removeScreenShape={screenId:ScreenId},RemoveScreenBody=strictObject(removeScreenShape),setScreenTreeShape={screenId:ScreenId,tree:NodeSchema},SetScreenTreeBody=strictObject(setScreenTreeShape),updateScreenShape={screenId:ScreenId,patch:object({name:string().min(1).optional(),route:RoutePath.nullable().optional()})},UpdateScreenBody=strictObject(updateScreenShape),addBoardShape={name:string().min(1).max(MAX_BOARD_NAME_LENGTH),id:string().min(1).optional(),group:string().min(1).optional()},AddBoardBody=strictObject(addBoardShape),updateBoardShape={boardId:BoardId,patch:object({name:string().min(1).max(MAX_BOARD_NAME_LENGTH).optional(),theme:string().min(1).nullable().optional(),archived:boolean().optional(),group:string().min(1).nullable().optional()})},UpdateBoardBody=strictObject(updateBoardShape),removeBoardShape={boardId:BoardId},RemoveBoardBody=strictObject(removeBoardShape),reorderBoardsShape={order:array(string().min(1))},ReorderBoardsBody=strictObject(reorderBoardsShape),addBoardGroupShape={name:string().min(1),color:string().min(1).optional()},AddBoardGroupBody=strictObject(addBoardGroupShape),updateBoardGroupShape={groupId:string().min(1),patch:object({name:string().min(1).optional(),color:string().min(1).nullable().optional()})},UpdateBoardGroupBody=strictObject(updateBoardGroupShape),removeBoardGroupShape={groupId:string().min(1)},RemoveBoardGroupBody=strictObject(removeBoardGroupShape),reorderBoardGroupsShape={order:array(string().min(1))},ReorderBoardGroupsBody=strictObject(reorderBoardGroupsShape),addFrameShape={boardId:BoardId,screenId:ScreenId,x:number().optional(),y:number().optional(),w:number().int().positive(),h:number().int().positive(),label:string().optional(),group:string().optional(),id:string().min(1).optional()},AddFrameBody=strictObject(addFrameShape),FramePatchEntrySchema=preprocess((v)=>{if(typeof v!=="object"||v===null||Array.isArray(v)||"patch"in v)return v;let{frameId,...patch}=v;return Object.keys(patch).length>0?{frameId,patch}:v},object({frameId:FrameId,patch:FramePatchSchema})),updateFrameShape={boardId:BoardId,patches:jsonTolerant(singularTolerant(array(FramePatchEntrySchema).min(1))).describe("One entry per frame; length 1 for a single edit")},UpdateFrameBody=strictObject(updateFrameShape),removeFrameShape={boardId:BoardId,frameId:FrameId},RemoveFrameBody=strictObject(removeFrameShape),moveFrameShape={boardId:BoardId,frameId:FrameId,toBoardId:BoardId,x:number().optional(),y:number().optional()},MoveFrameBody=strictObject(moveFrameShape),flowEdgeEntryShape={from:string(),to:string(),label:string().nullable().optional(),state:string().nullable().optional(),remove:boolean().optional()},FlowEdgeEntrySchema=strictObject(flowEdgeEntryShape),connectFramesShape={boardId:BoardId,edges:jsonTolerant(singularTolerant(array(FlowEdgeEntrySchema).min(1)))},ConnectFramesBody=strictObject({boardId:BoardId,edges:jsonTolerant(singularTolerant(array(strictObject({...flowEdgeEntryShape,labelAt:number().min(0).max(1).nullable().optional()})).min(1)))}),addSnippetShape={name:string().min(1),id:string().min(1).optional(),params:array(SnippetParamSchema).default([]),tree:NodeSchema},AddSnippetBody=strictObject(addSnippetShape),updateSnippetShape={snippetId:SnippetId,patch:object({name:string().min(1).optional(),params:array(SnippetParamSchema).optional(),tree:NodeSchema.optional(),innerPatch:object({innerPath:InnerPathSchema,propPatch:PatchRecordSchema}).optional()})},UpdateSnippetBody=strictObject(updateSnippetShape),removeSnippetShape={snippetId:SnippetId},RemoveSnippetBody=strictObject(removeSnippetShape),instantiateSnippetShape={screenId:ScreenId,parentPath:LocatorOrRootSchema,snippetId:SnippetId,id:NodeIdSchema.optional(),args:PatchRecordSchema.optional(),extraClassName:string().optional().describe("One-off classes layered onto the snippet body's root, for this instance"),overrides:record(string(),object({props:PatchRecordSchema})).optional(),index:number().int().nonnegative().optional()},InstantiateSnippetBody=strictObject(instantiateSnippetShape),overrideSnippetPropsShape={screenId:ScreenId,path:LocatorSchema,innerPath:InnerPathSchema,propPatch:PatchRecordSchema},OverrideSnippetPropsBody=strictObject(overrideSnippetPropsShape),updateSnippetArgsShape={screenId:ScreenId,path:LocatorSchema,argPatch:PatchRecordSchema.default({}),extraClassName:string().nullable().optional(),gesture:GestureSchema},UpdateSnippetArgsBody=strictObject(updateSnippetArgsShape),updateSnippetInstanceShape={screenId:ScreenId,path:LocatorSchema,argPatch:PatchRecordSchema.optional(),extraClassName:string().nullable().optional(),innerPath:InnerPathSchema.optional(),propPatch:PatchRecordSchema.optional()},UpdateSnippetInstanceBody=strictObject(updateSnippetInstanceShape);function normalizeUpdateSnippetInstance(input){let wantsArgs=input.argPatch!==void 0||input.extraClassName!==void 0,hasInner=input.innerPath!==void 0,hasPatch=input.propPatch!==void 0;if(hasInner!==hasPatch)return invalid("update_snippet_instance: innerPath and propPatch go together.");if(!wantsArgs&&!hasInner)return invalid("update_snippet_instance: pass argPatch, extraClassName, or innerPath+propPatch.");let plan={};if(wantsArgs)plan.args={screenId:input.screenId,path:input.path,argPatch:input.argPatch??{},...input.extraClassName!==void 0?{extraClassName:input.extraClassName}:{}};if(hasInner&&input.innerPath!==void 0&&input.propPatch!==void 0)plan.override={screenId:input.screenId,path:input.path,innerPath:input.innerPath,propPatch:input.propPatch};return{ok:!0,args:plan}}var addAnnotationShape={screenId:ScreenId,target:object({locator:LocatorSchema}),body:string(),position:AnnotationPositionSchema.optional(),collapsed:boolean().optional()},AddAnnotationBody=strictObject(addAnnotationShape),updateAnnotationShape={screenId:ScreenId,annotationId:string().min(1),patch:object({body:string().optional(),position:AnnotationPositionSchema.optional(),collapsed:boolean().nullable().optional()})},UpdateAnnotationBody=strictObject(updateAnnotationShape),removeAnnotationShape={screenId:ScreenId,annotationId:string().min(1)},RemoveAnnotationBody=strictObject(removeAnnotationShape),NoteAttachmentSchema=object({frameId:FrameId,screenId:ScreenId,locator:LocatorSchema}),addNoteShape={boardId:BoardId,x:number().optional(),y:number().optional(),width:number().positive().optional(),height:number().positive().optional(),body:string(),attachment:NoteAttachmentSchema.optional()},AddNoteBody=object(addNoteShape).refine((a)=>a.attachment!==void 0||a.x!==void 0&&a.y!==void 0,{message:"A note without an attachment needs x and y",path:["x"]}),updateNoteShape={boardId:BoardId,noteId:string().min(1),patch:object({x:number().optional(),y:number().optional(),width:number().positive().optional(),height:number().positive().optional(),body:string().optional()})},UpdateNoteBody=strictObject(updateNoteShape),removeNoteShape={boardId:BoardId,noteId:string().min(1)},RemoveNoteBody=strictObject(removeNoteShape),updateViewportPresetsShape={presets:array(object({name:string().min(1),w:number().int().positive(),h:number().int().positive()}))},UpdateViewportPresetsBody=strictObject(updateViewportPresetsShape),updateDefaultsShape={defaultBoard:string().min(1).nullable().optional(),defaultScreen:string().min(1).nullable().optional()},UpdateDefaultsBody=strictObject(updateDefaultsShape),updateCodegenShape={componentsAlias:string().nullable().optional()},UpdateCodegenBody=strictObject(updateCodegenShape),updateDesignNameShape={name:string()},UpdateDesignNameBody=strictObject(updateDesignNameShape),updateFeedbackShape={enabled:boolean().optional(),contactOk:boolean().optional()},UpdateFeedbackBody=strictObject(updateFeedbackShape);export{ok,err,map,mapError,tryCatchAsync,$,DoAsync,isCloudErrorCode,AuthConfigResponseSchema,HealthResponseSchema,AccountResponseSchema,protectedSharesAllowed,PROTECTED_SHARES_UNAVAILABLE,teamOnlyAllowed,GUESTS_UNAVAILABLE,PublishDestinationsResponseSchema,LinkResponseSchema,LinkAccessResponseSchema,VersionUploadResponseSchema,PublishedDesignsResponseSchema,TeamsResponseSchema,GuestsResponseSchema,GuestInviteResponseSchema,GuestLinkResponseSchema,DeviceCodeResponseSchema,DeviceTokenResponseSchema,CliTokenResponseSchema,IntentCatalogResponseSchema,GenerateResponseSchema,FeedbackIssuerResponseSchema,FeedbackSignaturesResponseSchema,unreachable,loggedOut,protocolViolation,uploadRaceLost,insecureCloudUrl,invalidRequest,readFailure,cloudFailure,httpFailureFrom,boardLimitFrom,describeBoardLimit,describeCloudError,cloudJson,cloudFetch,LocatorSchema,AddNodeBody,normalizeAddNode,updatePropsShape,UpdatePropsBody,ApplyClassesBody,removeNodeShape,RemoveNodeBody,moveNodeShape,MoveNodeBody,setNodeIdShape,SetNodeIdBody,addScreenShape,AddScreenBody,removeScreenShape,RemoveScreenBody,SetScreenTreeBody,updateScreenShape,addBoardShape,AddBoardBody,updateBoardShape,UpdateBoardBody,removeBoardShape,RemoveBoardBody,reorderBoardsShape,ReorderBoardsBody,AddBoardGroupBody,UpdateBoardGroupBody,RemoveBoardGroupBody,ReorderBoardGroupsBody,addFrameShape,AddFrameBody,updateFrameShape,UpdateFrameBody,removeFrameShape,RemoveFrameBody,MoveFrameBody,connectFramesShape,ConnectFramesBody,addSnippetShape,AddSnippetBody,updateSnippetShape,UpdateSnippetBody,removeSnippetShape,RemoveSnippetBody,InstantiateSnippetBody,UpdateSnippetArgsBody,updateSnippetInstanceShape,UpdateSnippetInstanceBody,normalizeUpdateSnippetInstance,AddAnnotationBody,UpdateAnnotationBody,RemoveAnnotationBody,AddNoteBody,UpdateNoteBody,RemoveNoteBody,updateViewportPresetsShape,UpdateViewportPresetsBody,UpdateDefaultsBody,UpdateCodegenBody,UpdateDesignNameBody,UpdateFeedbackBody};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{init_default,runInit2}from"./chunk-8a2agf28.js";import"./chunk-gv209d5j.js";import"./chunk-c11n1tve.js";import"./chunk-2x5rw009.js";import"./chunk-ysf9388d.js";import"./chunk-02gpgqj4.js";import"./chunk-v6w8akph.js";import"./chunk-8kn6tp4b.js";import"./chunk-pasvz8xw.js";import"./chunk-f8jchnwe.js";import"./chunk-z3r8px3d.js";import"./chunk-80fjfyha.js";import"./chunk-yp39f93e.js";import"./chunk-n70zfv0m.js";import"./chunk-3f2p94wa.js";import"./chunk-zexzy6zv.js";import"./chunk-fejhav0p.js";import"./chunk-seaar5na.js";import"./chunk-g7pbfad5.js";import"./chunk-s23gtejf.js";import"./chunk-p93x4pn2.js";import"./chunk-w40pqmyz.js";export{init_default as default,runInit2 as runInit};
package/cli.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import"./chunk-s23gtejf.js";import{COMMANDS}from"./chunk-acsv6m3n.js";import{TOOL_VERSION}from"./chunk-3m28pvzr.js";import{maybeNotifyAboutUpdate}from"./chunk-s84vxcdv.js";import{defineCommand,runMain}from"./chunk-2smsg2ct.js";var main=defineCommand({meta:{name:"velloo",version:TOOL_VERSION,description:"Open-source, local-first canvas for agent-driven design."},subCommands:COMMANDS});await runMain(main);var command=process.argv[2];if(!["mcp","__daemon","__update_check","run","upgrade"].includes(command??""))await maybeNotifyAboutUpdate();
3
+ import"./chunk-s23gtejf.js";import{COMMANDS}from"./chunk-seaar5na.js";import{TOOL_VERSION}from"./chunk-g7pbfad5.js";import{maybeNotifyAboutUpdate}from"./chunk-fejhav0p.js";import{defineCommand,runMain}from"./chunk-2smsg2ct.js";var main=defineCommand({meta:{name:"velloo",version:TOOL_VERSION,description:"Open-source, local-first canvas for agent-driven design."},subCommands:COMMANDS});await runMain(main);var command=process.argv[2];if(!["mcp","__daemon","__update_check","run","upgrade"].includes(command??""))await maybeNotifyAboutUpdate();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "velloo",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "description": "Local-first, code-shaped design canvas — your AI agent designs with your real components, in your repo",
6
6
  "license": "Apache-2.0",
@@ -54,13 +54,13 @@
54
54
  "tw-animate-css": "1.4.0"
55
55
  },
56
56
  "optionalDependencies": {
57
- "@oven/bun-darwin-aarch64": "1.4.2",
58
- "@oven/bun-darwin-x64-baseline": "1.4.2",
59
- "@oven/bun-linux-aarch64": "1.4.2",
60
- "@oven/bun-linux-aarch64-musl": "1.4.2",
61
- "@oven/bun-linux-x64-baseline": "1.4.2",
62
- "@oven/bun-linux-x64-musl-baseline": "1.4.2",
63
- "@oven/bun-windows-aarch64": "1.4.2",
64
- "@oven/bun-windows-x64-baseline": "1.4.2"
57
+ "@oven/bun-darwin-aarch64": "1.4.3",
58
+ "@oven/bun-darwin-x64-baseline": "1.4.3",
59
+ "@oven/bun-linux-aarch64": "1.4.3",
60
+ "@oven/bun-linux-aarch64-musl": "1.4.3",
61
+ "@oven/bun-linux-x64-baseline": "1.4.3",
62
+ "@oven/bun-linux-x64-musl-baseline": "1.4.3",
63
+ "@oven/bun-windows-aarch64": "1.4.3",
64
+ "@oven/bun-windows-x64-baseline": "1.4.3"
65
65
  }
66
66
  }
@@ -54,6 +54,7 @@ export function createProvider(): FrameworkAdapter {
54
54
  ),
55
55
  styleChannel: STYLE_PROP,
56
56
  styleChannels: ["style"],
57
+ themeRoles: ["background", "foreground", "primary", "destructive", "card"],
57
58
  mcpIntro: () => [...ANTD_INTRO],
58
59
  renderPass: (theme, dark) => makeRenderPass(theme, dark ?? false),
59
60
  codegenModule: "antd",
@@ -59,6 +59,7 @@ export function createProvider(): FrameworkAdapter {
59
59
  loadManifest: async () => HTML_MANIFEST,
60
60
  styleChannel: STYLE_PROP,
61
61
  styleChannels: ["style"],
62
+ themeRoles: ["background", "foreground", "card", "muted", "border", "input"],
62
63
  registryForChannel: () => registry,
63
64
  mcpIntro: () => HTML_INTRO,
64
65
  hostStylesheets: true,
@@ -57,6 +57,16 @@ export function createProvider(): FrameworkAdapter {
57
57
  ),
58
58
  styleChannel: SX_PROP,
59
59
  styleChannels: ["sx"],
60
+ themeRoles: [
61
+ "background",
62
+ "foreground",
63
+ "primary",
64
+ "secondary",
65
+ "destructive",
66
+ "card",
67
+ "muted",
68
+ "border",
69
+ ],
60
70
  mcpIntro: () => [...MUI_INTRO],
61
71
  renderPass: (theme, dark) => makeRenderPass(theme, dark ?? false),
62
72
  codegenModule: "@mui/material",
@@ -59,6 +59,7 @@ export function createProvider(): FrameworkAdapter {
59
59
  // is `none` resolves to the inline-`style` channel instead.
60
60
  styleChannel: TAILWIND_CLASSNAME,
61
61
  styleChannels: ["tailwind-classname", "style"],
62
+ themeRoles: ["background", "foreground", "card", "muted", "border", "input"],
62
63
  mcpIntro: (channel) => [...(channel === "style" ? NONE_INLINE_INTRO : NONE_INTRO)],
63
64
  // Inline-styled primitives for the `style` channel; Tailwind-classed for
64
65
  // every other channel. Lets a `none/none` folder paint with the JIT off.
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { FlowEdgeSchema } from "./flow.ts";
2
3
  import { FrameSchema } from "./frame.ts";
3
4
  import { ResourceIdSchema } from "./ids.ts";
4
5
 
@@ -12,7 +13,11 @@ import { ResourceIdSchema } from "./ids.ts";
12
13
  export const BoardGroupSchema = z.object({
13
14
  id: ResourceIdSchema,
14
15
  name: z.string().min(1),
15
- /** CSS color value; canvas uses it for the group region tint and tag chip. */
16
+ /**
17
+ * CSS color value. The sidebar uses a board group's for its chip and rail;
18
+ * a frame group may carry one, but the canvas draws nothing for frame
19
+ * groups yet.
20
+ */
16
21
  color: z.string().optional(),
17
22
  });
18
23
 
@@ -61,6 +66,13 @@ export const BoardSchema = z.object({
61
66
  frames: z.array(FrameSchema).default([]),
62
67
  /** Frame regions within this board (see {@link BoardGroupSchema}). */
63
68
  groups: z.array(BoardGroupSchema).default([]),
69
+ /**
70
+ * Arrows between this board's frames (see {@link FlowEdgeSchema}). Absent ⇒
71
+ * a board of unconnected frames, which is every existing one — and the field
72
+ * is dropped again when the last arrow goes, so such a board's file never
73
+ * changes shape.
74
+ */
75
+ edges: z.array(FlowEdgeSchema).optional(),
64
76
  });
65
77
 
66
78
  export type Board = z.infer<typeof BoardSchema>;
@@ -106,6 +106,35 @@ export const HostAppSchema = z.object({
106
106
 
107
107
  export type HostApp = z.infer<typeof HostAppSchema>;
108
108
 
109
+ /**
110
+ * Where the design's theme came from in the app: what `velloo init` read (or
111
+ * left alone), and what an `import_theme` from a stylesheet into the default
112
+ * theme records again. Agents are told by it what checking the theme against
113
+ * the app means — a theme read from a JavaScript theme object (`mantine`,
114
+ * `mui`) is compared with that module, since no stylesheet holds it.
115
+ *
116
+ * "stylesheet" — the app's stylesheet, and what it brings with it.
117
+ * "design-md" — a DESIGN.md, layered on the stylesheet.
118
+ * "mantine" / "mui" — the theme object the app hands that library.
119
+ * "library-defaults" — the app uses `library` and sets no theme of its own,
120
+ * so the design is on that library's.
121
+ * "preset" — nothing of the app's: a built-in preset.
122
+ */
123
+ export const ThemeSourceSchema = z.object({
124
+ kind: z.enum(["stylesheet", "design-md", "mantine", "mui", "library-defaults", "preset"]),
125
+ /**
126
+ * The app's files it was read from, relative to the app root, the one
127
+ * `kind` names first: a stylesheet is followed by the sheets it imports,
128
+ * the tailwind.config it merged and the modules whose font loader calls
129
+ * set a face. Absent when nothing was read from a file in the app.
130
+ */
131
+ files: z.array(AppRelativePathSchema).min(1).optional(),
132
+ /** The library whose own theme it is — for `"library-defaults"`. */
133
+ library: z.string().min(1).optional(),
134
+ });
135
+
136
+ export type ThemeSource = z.infer<typeof ThemeSourceSchema>;
137
+
109
138
  /** The CSS framework a folder styles with. See `ConfigSchema.styling`. */
110
139
  export const StylingSchema = z.object({ framework: z.enum(["tailwind", "none"]) });
111
140
 
@@ -206,6 +235,11 @@ export const ConfigSchema = z
206
235
  * a file somewhere unconventional, or pins the choice when several exist.
207
236
  */
208
237
  designSystem: z.object({ path: AppRelativePathSchema }).optional(),
238
+ /**
239
+ * Where the default theme was read from in the app. See `ThemeSourceSchema`.
240
+ * Absent for a design made before it was recorded: nothing is assumed.
241
+ */
242
+ themeSource: ThemeSourceSchema.optional(),
209
243
  /**
210
244
  * Named host apps for monorepos — the multi-app twin of `hostApp`, keyed
211
245
  * by a short app name (`"web"`, `"admin"`; `init`'s multi-app scan uses
@@ -0,0 +1,230 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * An arrow between two frames of one board — what makes a board readable as a
5
+ * flow. It is a spec, not behaviour: nothing here runs, and nothing here emits.
6
+ *
7
+ * One arrow says one of two things, and `state` is what tells them apart:
8
+ *
9
+ * - **A transition** (no `state`): from this step, `label` happens, and the
10
+ * user is on `to`.
11
+ * - **A state** (`state` set): `to` shows the *same step* as `from`, in that
12
+ * state — its loading, empty or error version — and `label` is when.
13
+ *
14
+ * Different layouts are separate screens, so the states of a step are separate
15
+ * frames; the arrows between them are what groups them into one step, which is
16
+ * why a frame carries no state of its own (see {@link deriveFlowSteps}).
17
+ *
18
+ * An arrow has no id: a board holds at most one per ordered pair of frames, so
19
+ * the pair is the identity.
20
+ */
21
+ export const FlowEdgeSchema = z.object({
22
+ /** Frame id the arrow leaves. */
23
+ from: z.string().min(1),
24
+ /** Frame id the arrow points at. */
25
+ to: z.string().min(1),
26
+ /** The trigger or condition, in plain words ("Submit", "Email already taken"). */
27
+ label: z.string().min(1).optional(),
28
+ /** Present ⇒ `to` is `from`'s step in this state. Free text; see {@link FLOW_STATES}. */
29
+ state: z.string().min(1).optional(),
30
+ /**
31
+ * Where along the arrow its label sits, from 0 at the frame it leaves to 1
32
+ * at the one it reaches. Set when someone slides the label out of the way;
33
+ * absent, the canvas puts it mid-way along the part of the arrow in view.
34
+ * Layout only: no reader of the flow looks at it.
35
+ */
36
+ labelAt: z.number().min(0).max(1).optional(),
37
+ });
38
+
39
+ export type FlowEdge = z.infer<typeof FlowEdgeSchema>;
40
+
41
+ /**
42
+ * The states a step conventionally has besides its default one. A vocabulary to
43
+ * offer, not a constraint: `state` stays free text on disk so "offline" or
44
+ * "expired" need no schema change.
45
+ */
46
+ export const FLOW_STATES = ["loading", "empty", "error", "success"] as const;
47
+
48
+ /** What the frame a step's state arrows start from is called. Never stored. */
49
+ export const DEFAULT_FLOW_STATE = "default";
50
+
51
+ interface FlowBoard {
52
+ frames: ReadonlyArray<{ id: string }>;
53
+ edges?: FlowEdge[] | undefined;
54
+ }
55
+
56
+ /**
57
+ * The arrows whose two ends are both still frames on the board. Reading goes
58
+ * through this so a hand-edited or half-synced file draws what it can instead
59
+ * of failing, and writing goes through it so removing a frame takes its arrows
60
+ * with it wherever the removal happened.
61
+ */
62
+ export function liveFlowEdges(board: FlowBoard): FlowEdge[] {
63
+ if (!board.edges || board.edges.length === 0) return [];
64
+ const frames = new Set(board.frames.map((frame) => frame.id));
65
+ return board.edges.filter(
66
+ (edge) => edge.from !== edge.to && frames.has(edge.from) && frames.has(edge.to),
67
+ );
68
+ }
69
+
70
+ /** One frame of a step other than its default one. */
71
+ interface FlowStepState {
72
+ frame: string;
73
+ state: string;
74
+ /** The arrow's label: when the step is in this state. */
75
+ when?: string;
76
+ /**
77
+ * The state this one follows, when its arrow leaves another state rather
78
+ * than the step's default frame: loading → error is "it was sent, then it
79
+ * failed", and without this the read would say only that both exist.
80
+ */
81
+ after?: string;
82
+ }
83
+
84
+ /**
85
+ * A step of the flow: the frame its state arrows start from, plus the frames
86
+ * those arrows reach.
87
+ */
88
+ export interface FlowStep {
89
+ /** The default state's frame. Its id stands for the step. */
90
+ frame: string;
91
+ states: FlowStepState[];
92
+ }
93
+
94
+ export interface FlowSteps {
95
+ /** Every frame belongs to exactly one step; a frame with no state arrows is a step alone. */
96
+ steps: FlowStep[];
97
+ /** Arrows that move between steps, endpoints as drawn (possibly a state's frame). */
98
+ transitions: FlowEdge[];
99
+ /** Frame id → the frame that stands for its step. */
100
+ stepOf: Map<string, string>;
101
+ }
102
+
103
+ /**
104
+ * Group a board's frames into steps by following its state arrows.
105
+ *
106
+ * A step is a tree: the default frame at the root, a state arrow down to each
107
+ * state, and onward from there (loading → error is "it loaded, then failed").
108
+ * Writes keep that shape — one state arrow into a frame, no cycles — but this
109
+ * reads whatever is on disk, so a state arrow that would break the tree is
110
+ * handed back as a transition rather than trusted.
111
+ */
112
+ export function deriveFlowSteps(board: FlowBoard): FlowSteps {
113
+ const parent = new Map<string, FlowEdge>();
114
+ const transitions: FlowEdge[] = [];
115
+ for (const edge of liveFlowEdges(board)) {
116
+ if (edge.state === undefined || parent.has(edge.to) || reaches(parent, edge.from, edge.to)) {
117
+ transitions.push(edge);
118
+ } else {
119
+ parent.set(edge.to, edge);
120
+ }
121
+ }
122
+
123
+ const children = new Map<string, FlowEdge[]>();
124
+ for (const edge of parent.values()) {
125
+ const siblings = children.get(edge.from);
126
+ if (siblings) siblings.push(edge);
127
+ else children.set(edge.from, [edge]);
128
+ }
129
+
130
+ const steps: FlowStep[] = [];
131
+ const stepOf = new Map<string, string>();
132
+ for (const { id } of board.frames) {
133
+ if (parent.has(id)) continue;
134
+ const states: FlowStepState[] = [];
135
+ stepOf.set(id, id);
136
+ const pending = [...(children.get(id) ?? [])].reverse();
137
+ for (let edge = pending.pop(); edge; edge = pending.pop()) {
138
+ stepOf.set(edge.to, id);
139
+ states.push({
140
+ frame: edge.to,
141
+ state: edge.state as string,
142
+ ...(edge.label !== undefined ? { when: edge.label } : {}),
143
+ ...(edge.from !== id ? { after: edge.from } : {}),
144
+ });
145
+ pending.push(...[...(children.get(edge.to) ?? [])].reverse());
146
+ }
147
+ steps.push({ frame: id, states });
148
+ }
149
+ return { steps, transitions, stepOf };
150
+ }
151
+
152
+ /** Whether walking state arrows up from `start` arrives at `target`. */
153
+ function reaches(parent: Map<string, FlowEdge>, start: string, target: string): boolean {
154
+ for (let at: string | undefined = start; at !== undefined; at = parent.get(at)?.from) {
155
+ if (at === target) return true;
156
+ }
157
+ return false;
158
+ }
159
+
160
+ /**
161
+ * State arrows that can only be transitions inside their step. Anyone who
162
+ * reads `state` as "what the arrow arrives at" draws these, and none of them
163
+ * contradicts anything — so a writer keeps each as the transition it is
164
+ * instead of refusing it:
165
+ *
166
+ * - **a way back**: from a state to the frame it came from, or to the step's
167
+ * default ("fix the fields and resubmit", tagged `recovery`). Taken as a
168
+ * state it would make the step's default a state of its own state.
169
+ * - **a state restated**: into a frame that is already that state of the same
170
+ * step ("try again", arriving at `loading` a second time).
171
+ * - **`default`**: the frame a step's states start from is not one of them.
172
+ *
173
+ * A *different* state for a frame that has one, or the same state claimed by
174
+ * another step, is a real contradiction and is left for the writer to refuse.
175
+ */
176
+ export function transitionsDrawnAsStates(board: FlowBoard): FlowEdge[] {
177
+ const parent = new Map<string, FlowEdge>();
178
+ const drawn = new Set<FlowEdge>();
179
+ const second: FlowEdge[] = [];
180
+ const edges = liveFlowEdges(board);
181
+ for (const edge of edges) {
182
+ if (edge.state === undefined) continue;
183
+ if (edge.state === DEFAULT_FLOW_STATE || reaches(parent, edge.from, edge.to)) drawn.add(edge);
184
+ else if (parent.has(edge.to)) second.push(edge);
185
+ else parent.set(edge.to, edge);
186
+ }
187
+ // Which step a frame is in is only known once every arrow has been read:
188
+ // the one that puts `from` in the step may come after the one being judged.
189
+ const stepOf = (frame: string): string => {
190
+ let at = frame;
191
+ for (let up = parent.get(at); up; up = parent.get(at)) at = up.from;
192
+ return at;
193
+ };
194
+ for (const edge of second) {
195
+ if (parent.get(edge.to)?.state === edge.state && stepOf(edge.from) === stepOf(edge.to)) {
196
+ drawn.add(edge);
197
+ }
198
+ }
199
+ return edges.filter((edge) => drawn.has(edge));
200
+ }
201
+
202
+ /**
203
+ * Why a set of arrows can't be a board's flow, or null when it can. The one
204
+ * rule set shared by every writer, so a board never holds arrows
205
+ * {@link deriveFlowSteps} would have to reinterpret.
206
+ */
207
+ export function flowEdgeProblem(board: FlowBoard): string | null {
208
+ const frames = new Set(board.frames.map((frame) => frame.id));
209
+ const pairs = new Set<string>();
210
+ const parent = new Map<string, FlowEdge>();
211
+ for (const edge of board.edges ?? []) {
212
+ for (const end of [edge.from, edge.to]) {
213
+ if (!frames.has(end)) return `no frame "${end}" on this board`;
214
+ }
215
+ if (edge.from === edge.to) return `"${edge.from}" can't point at itself`;
216
+ const pair = `${edge.from}\u0000${edge.to}`;
217
+ if (pairs.has(pair)) return `two arrows from "${edge.from}" to "${edge.to}"`;
218
+ pairs.add(pair);
219
+ if (edge.state === undefined) continue;
220
+ const other = parent.get(edge.to);
221
+ if (other) {
222
+ return `"${edge.to}" is already the ${other.state} state of "${other.from}" — a frame is one state of one step`;
223
+ }
224
+ if (reaches(parent, edge.from, edge.to)) {
225
+ return `"${edge.to}" → "${edge.from}" → "${edge.to}" loops — a step's states need one default frame to start from`;
226
+ }
227
+ parent.set(edge.to, edge);
228
+ }
229
+ return null;
230
+ }
@@ -27,6 +27,13 @@ export const FrameSchema = z.object({
27
27
  group: z.string().optional(),
28
28
  /** Pinned render scheme; absent means follow the canvas default. */
29
29
  scheme: FrameSchemeSchema.optional(),
30
+ /**
31
+ * `"content"`: the height follows the screen — the daemon measures how tall
32
+ * the screen renders at this width after each change to it and sets `h`.
33
+ * Absent ⇒ `h` is whatever it was last set to, which is every frame someone
34
+ * sized: setting a frame's height drops this.
35
+ */
36
+ fit: z.literal("content").optional(),
30
37
  });
31
38
 
32
39
  export type Frame = z.infer<typeof FrameSchema>;