@promptctl/cc-candybar 1.56.1 → 1.56.2
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/dist/index.mjs +1 -1
- package/package.json +5 -5
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/bin/preview.sh +118 -255
- package/plugin/commands/candybar.md +112 -246
- package/plugin/templates/config.json +8 -0
- package/plugin/templates/nerd-fonts-sample.txt +5 -3
- package/plugin/templates/config-essential.json +0 -36
- package/plugin/templates/config-full.json +0 -55
- package/plugin/templates/config-standard.json +0 -39
- package/plugin/templates/config-tui-compact.json +0 -48
- package/plugin/templates/config-tui-full.json +0 -89
- package/plugin/templates/config-tui-standard.json +0 -56
- package/plugin/templates/config-tui.json +0 -18
package/dist/index.mjs
CHANGED
|
@@ -149,7 +149,7 @@ Config tooling:
|
|
|
149
149
|
Replace: layout: [["seg1", "seg2"], ["seg3"]]
|
|
150
150
|
With: root: { v: [{ h: ["seg1", "seg2"] }, "seg3"] }
|
|
151
151
|
Single-row example: root: { h: ["seg1", "seg2"] }`,line:J(e.source,[`layout`])}),t.root!==void 0&&(n.root=pc(e,`root`,t.root)),t.actions!==void 0&&(n.actions=Tg(e,t.actions)),t.looks!==void 0&&(n.looks=p_(e,t.looks)),t.presets!==void 0&&(n.presets=h_(e,t.presets)),t.editGlobals!==void 0&&(n.editGlobals=wo(e,`editGlobals`,t.editGlobals)),t.helpers!==void 0&&(n.helpers=g_(e,t.helpers)),Sc(e,n),km(e,n),Rm(e,n),Xs(e,n,hh,`the global settings menu`),n}const sv=new Set([`globals`,`variables`,`segments`,`layout`,`root`,`actions`,`looks`,`presets`,`editGlobals`,`helpers`]);function cv(e){let t=fv(e.termCols),n=fv(e.termRows),r=pv(e.ssh),i=uv(e.tmux),a=lv(e.configEnv);return{...t!==void 0&&{termCols:t},...n!==void 0&&{termRows:n},...r!==void 0&&{ssh:r},...i!==void 0&&{tmux:i},...a!==void 0&&{configEnv:a}}}function lv(e){return typeof e==`string`&&e!==``?J_(e):void 0}function uv(e){if(e===null)return null;if(typeof e!=`object`)return;let{socket:t,pane:n,truecolor:r}=e;if(!(typeof t!=`string`||t===``)&&!(typeof n!=`string`||n===``)&&!(r!==null&&(typeof r!=`string`||r===``)))return{socket:t,pane:n,truecolor:r}}const dv=1e4;function fv(e){if(typeof e!=`number`||!Number.isFinite(e))return;let t=Math.floor(e);if(!(t<=0))return t>dv?dv:t}function pv(e){return typeof e==`boolean`?e:void 0}var mv=class extends Error{constructor(e){super(e),this.name=`ProtocolError`}};function hv(e){let t=Buffer.from(JSON.stringify(e),`utf8`),n=Buffer.alloc(4);return n.writeUInt32BE(t.length,0),Buffer.concat([n,t])}function gv(e,t){let n=Buffer.alloc(0);return function(r){for(n=Buffer.concat([n,r]);n.length>=4;){let r=n.readUInt32BE(0);if(r>16777216){t(new mv(`frame too large: ${r}`));return}if(n.length<4+r)return;let i=n.subarray(4,4+r);n=n.subarray(4+r);try{e(JSON.parse(i.toString(`utf8`)))}catch(e){let n=new mv(e instanceof Error?e.message:String(e));e instanceof Error&&(n.cause=e),t(n);return}}}}function _v(e,t,n){return new Promise((r,i)=>{let a=!1,o=t=>{if(!a){a=!0,clearTimeout(s),e.removeAllListeners();try{t()}catch{}}},s=setTimeout(()=>{o(()=>{e.destroy(),i(Error(`TIMEOUT`))})},n),c=gv(e=>o(()=>r(e)),e=>o(()=>i(e)));e.on(`data`,c),e.on(`error`,e=>o(()=>i(e))),e.on(`close`,()=>o(()=>i(Error(`socket closed before response`)))),e.write(hv(t))})}async function vv(e,t,n){let r=null;try{r=await Cv(k(),t.connectMs);let i=await _v(r,{v:3,...e},t.budgetMs);return xv(e.kind,i,n)}catch(e){return Sv(e)}finally{r&&r.destroy()}}function yv(e,t){return typeof e==`string`?e:t}function bv(e){return typeof e==`number`&&Number.isInteger(e)&&e>=0?e:0}function xv(e,t,n){let r=t;if(r.ok===!0){let r=n(t);return r===void 0?{kind:`permanent`,cause:`malformed_response`,message:`ok response without payload for "${e}" request`}:{kind:`ok`,value:r}}let i=yv(r.error,`(no error message)`);switch(r.code){case`VERSION_MISMATCH`:return{kind:`permanent`,cause:`version_mismatch`,clientV:3,daemonV:bv(r.daemonV)};case`TIMEOUT`:return{kind:`transient`,cause:`timeout`,message:i};case`BAD_REQUEST`:return{kind:`permanent`,cause:`bad_request`,message:i};case`RENDER_FAILED`:return{kind:`permanent`,cause:`render_failed`,message:i};default:return{kind:`permanent`,cause:`malformed_response`,message:`unknown error code: ${String(r.code)}`}}}function Sv(e){if(e instanceof mv||e instanceof SyntaxError)return{kind:`permanent`,cause:`malformed_response`,message:e instanceof Error?e.message:String(e)};let t=e instanceof Error?e.message:String(e);return t===`CONNECT_TIMEOUT`||t===`TIMEOUT`?{kind:`transient`,cause:`timeout`,message:t}:t.includes(`ECONNREFUSED`)||t.includes(`ENOENT`)||t.includes(`ENOTSOCK`)?{kind:`transient`,cause:`unreachable`,message:t}:{kind:`transient`,cause:`io_error`,message:t}}function Cv(e,t){return new Promise((n,r)=>{let i=g.createConnection({path:e}),a=setTimeout(()=>{i.destroy(),r(Error(`CONNECT_TIMEOUT`))},t);i.once(`connect`,()=>{clearTimeout(a),n(i)}),i.once(`error`,e=>{clearTimeout(a),r(e)})})}function wv(e){if(e.kind===`transient`)return`daemon unavailable (${e.cause}: ${e.message})\nHint: daemon may not be running. Run \`cc-candybar\` once to spawn it.`;switch(e.cause){case`version_mismatch`:{let t=e.daemonV===0?`unknown`:`v${e.daemonV}`;return`daemon protocol mismatch (client v${e.clientV} ≠ daemon ${t})`}case`bad_request`:case`render_failed`:case`malformed_response`:return`daemon error (${e.cause}): ${e.message}`}}const Tv={connectMs:50,budgetMs:150},Ev={connectMs:50,budgetMs:200};function Dv(e){let t=e.output;return typeof t==`string`?t:void 0}function Ov(e,t,n,r){return vv({kind:`render`,hookData:e,args:t,cwd:n,...r},Tv,Dv)}function kv(e,t){return vv({kind:`click`,verb:e,value:t},Ev,Dv)}let Av=null;function jv(e){if(Av!==null)return Av;r.mkdirSync(c.dirname(e),{recursive:!0});try{Av=r.statSync(e).size}catch{Av=0}return Av}function Mv(e){for(let t=2;t>=1;t--){let n=`${e}.${t}`,i=`${e}.${t+1}`;try{r.renameSync(n,i)}catch{}}try{r.renameSync(e,`${e}.1`)}catch{}Av=0}function Z(e,t){let n=`${new Date().toISOString()} [${e}] ${t}\n`;try{let e=He(),t=jv(e);r.appendFileSync(e,n),Av=t+Buffer.byteLength(n,`utf8`),Av>=5242880&&Mv(e)}catch(e){Av=null;try{process.stderr.write(`${n}cc-candybar: daemon.log unwritable: ${e.message}\n`)}catch{}}}const Nv=`CC_CANDYBAR_RSS_LIMIT_MB`;function Pv(e){let t=e[Nv];if(t===void 0)return 512;let n=/^\d+$/.test(t)?Number(t):NaN;if(!Number.isSafeInteger(n)||n<=0)throw Error(`${Nv} must be a positive integer (MB), got ${JSON.stringify(t)}`);return n}function Fv(e){return Pv(e)*2}const Iv=1024*1024;function Lv(e){return Pv(e)*Iv}function Rv(e){let t=e.rssLimitBytes??512*Iv,n=e.snapshotsKeep??3,r=!1;function i(){if(r)return!0;let i=e.rssBytes();if(i<=t)return!1;r=!0,e.log(`warn`,`RSS ${i} > limit ${t}; writing heap snapshot then shutting down`);try{let t=new Date(e.now()).toISOString().replace(/[:.]/g,`-`),r=c.join(e.snapshotDir,`heap-${t}-${e.pid}.heapsnapshot`),i=e.writeHeapSnapshot(r);e.log(`info`,`heap snapshot written: ${i}`),zv(e.listSnapshots(),n,e.removeFile)}catch(t){e.log(`warn`,`heap snapshot failed: ${t.message}`)}return e.shutdown(0),!0}function a(){let n=e.rssBytes();return n>t*.75?`rss ${n} approaching limit ${t}`:null}function o(e=6e4){let t=setInterval(()=>{i()},e);return t.unref(),{disarm:()=>clearInterval(t)}}return{checkRss:i,describeNextRestart:a,arm:o}}function zv(e,t,n){let r=[...e].sort((e,t)=>{let n=e.slice(e.lastIndexOf(`/`)+1);return t.slice(t.lastIndexOf(`/`)+1).localeCompare(n)});for(let e of r.slice(t))try{n(e)}catch{}}function Bv(e,t,n={}){let i=Ae();return{now:()=>Date.now(),pid:process.pid,snapshotDir:i,log:Z,rssBytes:()=>process.memoryUsage().rss,writeHeapSnapshot:e=>_.writeHeapSnapshot(e),listSnapshots:()=>{try{return r.readdirSync(i).filter(e=>e.startsWith(`heap-`)&&e.endsWith(`.heapsnapshot`)).map(e=>c.join(i,e))}catch{return[]}},removeFile:e=>r.unlinkSync(e),shutdown:t,startedAtMs:e,...n}}function Vv(e={}){if(ry()!==null)return;let n=e.spawn??iy,r=ey();if(r.kind===`contended`){let e=Uv();e!==null&&e>2e3&&(t.stderr.write(`cc-candybar: spawn-lock held ${e}ms (likely crashed holder) — spawning unlocked\n`),Hv(n));return}if(r.kind===`error`){t.stderr.write(`cc-candybar: spawn-lock unavailable (${r.reason}) — spawning unlocked\n`),Hv(n);return}try{Hv(n)}finally{ny()}}function Hv(e){Xv()&&Wv(e)}function Uv(){try{let e=r.statSync(Re());return Date.now()-e.mtimeMs}catch{return null}}function Wv(e){try{e()||t.stderr.write(`cc-candybar: daemon spawn returned false (unable to resolve script path?)
|
|
152
|
-
`)}catch(e){t.stderr.write(`cc-candybar: daemon spawn failed: ${e.message}\n`)}}function Gv(e,t){return e===null?{kind:`allow`}:e<-1e4?{kind:`allow-future-garbage`,futureMs:-e}:e<t?{kind:`deny`}:{kind:`allow`}}function Kv(e){return Math.min(3e3*2**Math.min(Math.max(e,0),5),6e4)}function qv(e){try{let t=r.readFileSync(e,`utf8`).trim(),n=Number(t);return!Number.isInteger(n)||n<0?0:Math.min(n,5)}catch{return 0}}function Jv(e,n){try{r.mkdirSync(c.dirname(e),{recursive:!0}),r.writeFileSync(e,String(n),{mode:384})}catch(e){t.stderr.write(`cc-candybar: could not record spawn.backoff: ${e.message}\n`)}}function Yv(){try{r.unlinkSync(Be())}catch(e){e.code!==`ENOENT`&&t.stderr.write(`cc-candybar: could not reset spawn.backoff: ${e.message}\n`)}}function Xv(){let e=ze(),n=Be(),r=qv(n),i=Gv(Zv(e),Kv(r));return i.kind===`deny`?!1:(i.kind===`allow-future-garbage`&&t.stderr.write(`cc-candybar: spawn.cooldown mtime is ${i.futureMs}ms in the future — ignoring and spawning\n`),Qv(e),Jv(n,Math.min(r+1,5)),!0)}function Zv(e){try{return Date.now()-r.statSync(e).mtimeMs}catch{return null}}function Qv(e){try{r.mkdirSync(c.dirname(e),{recursive:!0}),r.writeFileSync(e,`${t.pid} ${Date.now()}\n`,{mode:384})}catch(e){t.stderr.write(`cc-candybar: could not record spawn.cooldown: ${e.message}\n`)}}let $v=null;function ey(){let e=Re();for(let n=0;n<2;n++){try{let n=r.openSync(e,`wx`,384);try{r.writeSync(n,JSON.stringify({pid:t.pid,ts:Date.now()}))}catch{}return $v={fd:n,path:e},{kind:`held`}}catch(t){let n=t.code;if(n!==`EEXIST`)return{kind:`error`,reason:`openSync(${e}): ${n??t.message}`}}if(!ty(e))return{kind:`contended`};try{r.unlinkSync(e)}catch(e){if(e.code!==`ENOENT`)return{kind:`error`,reason:`unlink stale spawn.lock: ${e.message}`}}}return{kind:`contended`}}function ty(e){try{let t=r.statSync(e);return Date.now()-t.mtimeMs>1e4}catch{return!0}}function ny(){if(!$v)return;let{fd:e,path:t}=$v;$v=null;try{r.closeSync(e)}catch{}try{r.unlinkSync(t)}catch{}}function ry(){try{return r.mkdirSync(Ae(),{recursive:!0}),null}catch(e){return`mkdir ${Ae()}: ${e.message}`}}function iy(){let e=t.execPath,n=t.argv[1];return n?we({bin:e,args:[`--max-old-space-size=${Fv(t.env)}`,n,`daemon`],category:`daemon-spawn`}).ok:!1}const ay=`cc-candybar`,oy=`dispatch`,sy=`set-state`,cy=`step-state`,ly=`copy`,uy=`open-vscode`,dy=`toolbar-toggle`,fy=`show-config-error`,py=`show-config-warning`,my=`set-config`,hy=`step-config`,gy=`reset-config`,_y=`apply-layout-op`,vy=`undo`,yy=`redo`,by=`apply-update`,xy=`doctor-run`,Sy=`doctor-fix`;function Cy(e){return e.map(encodeURIComponent).join(`/`)}function wy(e){return e.length===0?[]:e.split(`/`).map(decodeURIComponent)}function Ty(e){return`${ay}://${oy}/${e.map(e=>`e=${encodeURIComponent(`${e.verb}/${Cy(e.args)}`)}`).join(`&`)}`}function Ey(e){return new y(e).getAll(`e`).map(Dy)}function Dy(e){let t=e.indexOf(`/`);return t===-1?{verb:e,value:``}:{verb:e.slice(0,t),value:e.slice(t+1)}}const Oy=`1.56.
|
|
152
|
+
`)}catch(e){t.stderr.write(`cc-candybar: daemon spawn failed: ${e.message}\n`)}}function Gv(e,t){return e===null?{kind:`allow`}:e<-1e4?{kind:`allow-future-garbage`,futureMs:-e}:e<t?{kind:`deny`}:{kind:`allow`}}function Kv(e){return Math.min(3e3*2**Math.min(Math.max(e,0),5),6e4)}function qv(e){try{let t=r.readFileSync(e,`utf8`).trim(),n=Number(t);return!Number.isInteger(n)||n<0?0:Math.min(n,5)}catch{return 0}}function Jv(e,n){try{r.mkdirSync(c.dirname(e),{recursive:!0}),r.writeFileSync(e,String(n),{mode:384})}catch(e){t.stderr.write(`cc-candybar: could not record spawn.backoff: ${e.message}\n`)}}function Yv(){try{r.unlinkSync(Be())}catch(e){e.code!==`ENOENT`&&t.stderr.write(`cc-candybar: could not reset spawn.backoff: ${e.message}\n`)}}function Xv(){let e=ze(),n=Be(),r=qv(n),i=Gv(Zv(e),Kv(r));return i.kind===`deny`?!1:(i.kind===`allow-future-garbage`&&t.stderr.write(`cc-candybar: spawn.cooldown mtime is ${i.futureMs}ms in the future — ignoring and spawning\n`),Qv(e),Jv(n,Math.min(r+1,5)),!0)}function Zv(e){try{return Date.now()-r.statSync(e).mtimeMs}catch{return null}}function Qv(e){try{r.mkdirSync(c.dirname(e),{recursive:!0}),r.writeFileSync(e,`${t.pid} ${Date.now()}\n`,{mode:384})}catch(e){t.stderr.write(`cc-candybar: could not record spawn.cooldown: ${e.message}\n`)}}let $v=null;function ey(){let e=Re();for(let n=0;n<2;n++){try{let n=r.openSync(e,`wx`,384);try{r.writeSync(n,JSON.stringify({pid:t.pid,ts:Date.now()}))}catch{}return $v={fd:n,path:e},{kind:`held`}}catch(t){let n=t.code;if(n!==`EEXIST`)return{kind:`error`,reason:`openSync(${e}): ${n??t.message}`}}if(!ty(e))return{kind:`contended`};try{r.unlinkSync(e)}catch(e){if(e.code!==`ENOENT`)return{kind:`error`,reason:`unlink stale spawn.lock: ${e.message}`}}}return{kind:`contended`}}function ty(e){try{let t=r.statSync(e);return Date.now()-t.mtimeMs>1e4}catch{return!0}}function ny(){if(!$v)return;let{fd:e,path:t}=$v;$v=null;try{r.closeSync(e)}catch{}try{r.unlinkSync(t)}catch{}}function ry(){try{return r.mkdirSync(Ae(),{recursive:!0}),null}catch(e){return`mkdir ${Ae()}: ${e.message}`}}function iy(){let e=t.execPath,n=t.argv[1];return n?we({bin:e,args:[`--max-old-space-size=${Fv(t.env)}`,n,`daemon`],category:`daemon-spawn`}).ok:!1}const ay=`cc-candybar`,oy=`dispatch`,sy=`set-state`,cy=`step-state`,ly=`copy`,uy=`open-vscode`,dy=`toolbar-toggle`,fy=`show-config-error`,py=`show-config-warning`,my=`set-config`,hy=`step-config`,gy=`reset-config`,_y=`apply-layout-op`,vy=`undo`,yy=`redo`,by=`apply-update`,xy=`doctor-run`,Sy=`doctor-fix`;function Cy(e){return e.map(encodeURIComponent).join(`/`)}function wy(e){return e.length===0?[]:e.split(`/`).map(decodeURIComponent)}function Ty(e){return`${ay}://${oy}/${e.map(e=>`e=${encodeURIComponent(`${e.verb}/${Cy(e.args)}`)}`).join(`&`)}`}function Ey(e){return new y(e).getAll(`e`).map(Dy)}function Dy(e){let t=e.indexOf(`/`);return t===-1?{verb:e,value:``}:{verb:e.slice(0,t),value:e.slice(t+1)}}const Oy=`1.56.2`;function ky(){return c.join(f.homedir(),`.claude`,`settings.json`)}function Q(e){return{kind:`ok`,value:e}}const Ay={kind:`absent`};function jy(e){return{kind:`failed`,reason:e}}function My(e,t){return e?.kind===`ok`?e.value:t}const Ny=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;function Py(e){let t=Ny.exec(e);return t?{kind:`ok`,value:[Number(t[1]),Number(t[2]),Number(t[3])]}:{kind:`failed`,reason:`"${e}" is not a release version (expected MAJOR.MINOR.PATCH)`}}function Fy(e){return e.join(`.`)}function Iy([e,t,n],[r,i,a]){return e-r||t-i||n-a}const Ly=`@promptctl/cc-candybar`;async function Ry(e,t,n){let r=`${n}/-/package/${encodeURIComponent(e)}/dist-tags`;try{let e=await t(r,{signal:AbortSignal.timeout(5e3)});if(!e.ok)return jy(`registry responded ${e.status} for ${r}`);let n=await e.json();return typeof n.latest==`string`?Py(n.latest):Ay}catch(e){return jy(e instanceof Error?e.message:String(e))}}function zy(e,t){let n=Py(e);if(n.kind===`failed`)return{kind:`unchecked`,installed:e,reason:n.reason};switch(t.kind){case`failed`:return{kind:`unchecked`,installed:e,reason:t.reason};case`absent`:return{kind:`unchecked`,installed:e,reason:"the registry lists no `latest` dist-tag"};case`ok`:{let e=Iy(n.value,t.value);return e<0?{kind:`stale`,installed:n.value,latest:t.value}:e>0?{kind:`ahead`,installed:n.value,latest:t.value}:{kind:`current`,installed:n.value}}}}function By(e,t){switch(t.kind){case`current`:return{stream:`stdout`,text:`✓ cc-candybar ${Fy(t.installed)} is the latest release.\n`};case`ahead`:return{stream:`stdout`,text:`cc-candybar ${Fy(t.installed)} is newer than the registry's latest release (${Fy(t.latest)}): an unpublished build.\n`};case`stale`:{let n=Fy(t.latest);return{stream:`stderr`,text:`⚠ cc-candybar ${Fy(t.installed)} was staged, but the latest release is ${n}.\n pnpm's release-age gate (minimumReleaseAge) and its dlx cache can both\n resolve \`@latest\` to an older release without saying so.\n To install ${n} now, name it explicitly:\n pnpm dlx ${e}@${n} install\n`}}case`unchecked`:return{stream:`stderr`,text:`⚠ Could not check for a cc-candybar release newer than ${t.installed} (${t.reason}); the registry check was skipped.\n`}}}const Vy=`CCCandybarURLHandler`,Hy=[],Uy={"darwin-arm64":`@promptctl/cc-candybar-darwin-arm64`,"darwin-x64":`@promptctl/cc-candybar-darwin-x64`,"linux-x64":`@promptctl/cc-candybar-linux-x64`,"linux-arm64":`@promptctl/cc-candybar-linux-arm64`};function Wy(e){return/^[A-Za-z0-9_./=,:-]+$/.test(e)?e:`'${e.replace(/'/g,`'\\''`)}'`}function Gy(e,t){return[e,...t].map(Wy).join(` `)}function Ky(){return c.join(f.homedir(),`Applications`,`${Vy}.app`)}function qy(){if(process.platform!==`darwin`)throw Error(`URL handler installation requires macOS (found platform: ${process.platform}).`)}function Jy(){if(process.platform===`darwin`)return c.join(f.homedir(),`Library`,`Application Support`,`CCCandybar`);let e=process.env.XDG_DATA_HOME??c.join(f.homedir(),`.local`,`share`);return c.join(e,`cc-candybar`)}function Yy(){return c.join(Jy(),`bin`,`cc-candybar`)}function Xy(){return c.join(Jy(),`dist`,`index.mjs`)}function Zy(e,t){return[`on open location L`,`\tdo shell script "'${e.replace(/"/g,`\\"`)}' '${t.replace(/"/g,`\\"`)}' url-handle " & quoted form of L`,`end open location`].join(`
|
|
153
153
|
`)}function Qy(e){if(!e)throw Error(`install: process.argv[1] not set`);return e.endsWith(`.mjs`)||e.endsWith(`.js`)?e:c.resolve(c.dirname(e),`..`,`dist`,`index.mjs`)}function $y(t){let n=Uy[`${process.platform}-${process.arch}`];if(n){let r=e(t);try{return{kind:`native`,sourcePath:r.resolve(`${n}/bin/cc-candybar`)}}catch{}}return{kind:`node-shim`,sourcePath:c.resolve(c.dirname(t),`..`,`bin`,`cc-candybar`)}}function eb(e,t){c.resolve(e)!==c.resolve(t)&&r.copyFileSync(e,t)}function tb(e){let t=r.openSync(e,`r`);try{let n=Buffer.alloc(2),i=r.readSync(t,n,0,2,0);if(i<2)throw Error(`install: staged render entry at ${e} is truncated (${i} byte(s)). Re-run: pnpm dlx ${Ly}@latest install`);return n.toString(`latin1`)===`#!`?`node-shim`:`native`}finally{r.closeSync(t)}}function nb(){let e=Qy(process.argv[1]);if(!r.existsSync(e))throw Error(`install: bundled dist not found at ${e}. Reinstall the package.`);let t=$y(e);if(!r.existsSync(t.sourcePath))throw Error(`install: render entry not found at ${t.sourcePath}. Reinstall the package.`);r.mkdirSync(c.dirname(Xy()),{recursive:!0}),r.mkdirSync(c.dirname(Yy()),{recursive:!0}),eb(e,Xy()),eb(t.sourcePath,Yy()),r.chmodSync(Yy(),493),r.rmSync(c.join(Jy(),`url-handler.mjs`),{force:!0});let n=tb(Yy());return process.stdout.write(`Staged cc-candybar v${Oy} runtime at ${Jy()}\n`+(n===`native`?` render entry: native binary (${process.platform}-${process.arch})\n`:` render entry: node shim (no native binary for ${process.platform}-${process.arch}; renders are correct but pay node startup)\n`)),{binPath:Yy(),distPath:Xy(),entryKind:n}}function rb(){return[{key:`CFBundleIdentifier`,xml:`<string>com.cccandybar.url-handler</string>`},{key:`CFBundleURLTypes`,xml:[`<array>`,` <dict>`,` <key>CFBundleURLName</key>`,` <string>Claude Powerline Click Action</string>`,` <key>CFBundleURLSchemes</key>`,` <array>`,` <string>${ay}</string>`,` </array>`,` </dict>`,`</array>`].join(`
|
|
154
154
|
`)}]}function ib(e){let t=Ky();r.mkdirSync(c.dirname(t),{recursive:!0}),r.existsSync(t)&&r.rmSync(t,{recursive:!0,force:!0}),process.stdout.write(`Building ${t}\n`);let n=Ce({bin:`/usr/bin/osacompile`,args:[`-o`,t,`-e`,Zy(process.execPath,e)],category:`install.osacompile`});if(!n.ok)throw process.stderr.write(n.stderr),Error(`osacompile failed (${n.reason})`);let i=c.join(t,`Contents`,`Info.plist`);for(let{key:e}of rb())Ce({bin:`/usr/bin/plutil`,args:[`-remove`,e,i],category:`install.plutil`});for(let{key:e,xml:t}of rb()){let n=Ce({bin:`/usr/bin/plutil`,args:[`-insert`,e,`-xml`,t,i],category:`install.plutil`});if(!n.ok)throw process.stderr.write(n.stderr),Error(`plutil -insert ${e} failed (${n.reason})`)}process.stdout.write(`Registering ${ay}:// with Launch Services\n`);let a=Ce({bin:`/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister`,args:[`-f`,t],category:`install.lsregister`});if(!a.ok)throw process.stderr.write(a.stderr),Error(`lsregister failed (${a.reason})`);process.stdout.write(`✓ ${Vy}.app installed and registered.\n`),process.stdout.write(` Test: open '${ay}://hello-world' && pbpaste\n`)}function ab(){qy(),ib(nb().distPath)}function ob(e,t=ay){let n=`${t}://`;if(!e.startsWith(n))throw Error(`expected ${n} scheme, got: ${e}`);let r=e.slice(n.length),i=r.indexOf(`/`);return i===-1?{verb:ly,value:r}:{verb:decodeURIComponent(r.slice(0,i)),value:r.slice(i+1)}}async function sb(e){e||(process.stderr.write(`url-handle: missing URL argument.
|
|
155
155
|
`),process.exit(1));let t;try{t=ob(e)}catch(e){process.stderr.write(`url-handle: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}let n=await kv(t.verb,t.value);n.kind===`ok`&&process.exit(0),n.kind===`transient`&&(Vv(),process.stderr.write(`url-handle: daemon unavailable (${n.cause}: ${n.message})\n`),process.exit(1)),process.stderr.write(cb(n)+`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptctl/cc-candybar",
|
|
3
|
-
"version": "1.56.
|
|
3
|
+
"version": "1.56.2",
|
|
4
4
|
"description": "Statusline renderer for Claude Code — a JSON5-configurable DSL with daemon-cached data sources, byte-clean palette-aware composition, and OSC8 click verbs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
@@ -90,9 +90,9 @@
|
|
|
90
90
|
"mobx": "^6.15.0"
|
|
91
91
|
},
|
|
92
92
|
"optionalDependencies": {
|
|
93
|
-
"@promptctl/cc-candybar-darwin-arm64": "1.56.
|
|
94
|
-
"@promptctl/cc-candybar-darwin-x64": "1.56.
|
|
95
|
-
"@promptctl/cc-candybar-linux-x64": "1.56.
|
|
96
|
-
"@promptctl/cc-candybar-linux-arm64": "1.56.
|
|
93
|
+
"@promptctl/cc-candybar-darwin-arm64": "1.56.2",
|
|
94
|
+
"@promptctl/cc-candybar-darwin-x64": "1.56.2",
|
|
95
|
+
"@promptctl/cc-candybar-linux-x64": "1.56.2",
|
|
96
|
+
"@promptctl/cc-candybar-linux-arm64": "1.56.2"
|
|
97
97
|
}
|
|
98
98
|
}
|
package/plugin/bin/preview.sh
CHANGED
|
@@ -1,305 +1,168 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
+
# Render the wizard's template config against a fixture session so the user can
|
|
3
|
+
# see a real bar before committing to a theme, style, charset, or preset.
|
|
4
|
+
#
|
|
5
|
+
# The config rendered here IS the template the wizard writes — templates/config.json
|
|
6
|
+
# with its four placeholders filled by the same substitution the wizard performs —
|
|
7
|
+
# so the preview cannot drift from what the wizard installs.
|
|
8
|
+
# (test/plugin-templates.test.ts drives every name below through `cc-candybar check`.)
|
|
2
9
|
|
|
3
10
|
set -euo pipefail
|
|
4
11
|
|
|
5
12
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
13
|
readonly SCRIPT_DIR
|
|
7
14
|
readonly PLUGIN_ROOT="${SCRIPT_DIR}/.."
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
git -C "${fake_repo}" checkout -b feat/my-feature --quiet 2>/dev/null
|
|
29
|
-
touch "${fake_repo}/newfile.txt"
|
|
30
|
-
printf 'change\n' > "${fake_repo}/README.md"
|
|
31
|
-
git -C "${fake_repo}" add README.md 2>/dev/null
|
|
32
|
-
|
|
33
|
-
# Use native path for Node.js compatibility (Windows needs C:\... not /c/...)
|
|
34
|
-
preview_cwd="$(cd "${fake_repo}" && pwd -W 2>/dev/null || pwd)"
|
|
35
|
-
cwd_encoded="$(printf '%s' "${preview_cwd}" | sed 's|[/\\]|-|g; s|:||g')"
|
|
36
|
-
TUI_PREVIEW_CWD="${preview_cwd}"
|
|
37
|
-
|
|
38
|
-
transcript_dir="${HOME}/.claude/projects/${cwd_encoded}"
|
|
39
|
-
TUI_TRANSCRIPT="${transcript_dir}/preview-123.jsonl"
|
|
40
|
-
mkdir -p "${transcript_dir}"
|
|
41
|
-
printf '{"timestamp":"%s","type":"user","message":{"role":"user","content":[{"type":"text","text":"hello"}]}}\n' "${user_iso}" > "${TUI_TRANSCRIPT}"
|
|
42
|
-
printf '{"timestamp":"%s","type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"done"}]}}\n' "${now_iso}" >> "${TUI_TRANSCRIPT}"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
make_tui_sample_json() {
|
|
46
|
-
local resets_5h resets_7d
|
|
47
|
-
resets_5h=$(( $(date +%s) + 15480 ))
|
|
48
|
-
resets_7d=$(( $(date +%s) + 432000 ))
|
|
49
|
-
|
|
50
|
-
printf '{"model":{"id":"claude-sonnet-4-20250514","display_name":"Sonnet 4"},"cost":{"total_cost_usd":2.85,"message_cost_usd":0.12,"total_duration_ms":16200000,"total_api_duration_ms":480000,"total_lines_added":342,"total_lines_removed":87},"context_window":{"context_window_size":200000,"used_percentage":42,"total_input_tokens":72000,"total_output_tokens":12000,"current_usage":{"input_tokens":72000,"output_tokens":12000,"cache_creation_input_tokens":15000,"cache_read_input_tokens":8000}},"rate_limits":{"five_hour":{"used_percentage":35,"resets_at":%d},"seven_day":{"used_percentage":28,"resets_at":%d}},"cwd":"%s","workspace":{"current_dir":"%s","project_dir":"%s"},"session_id":"preview-123","version":"1.0.47"}' \
|
|
51
|
-
"${resets_5h}" "${resets_7d}" "${TUI_PREVIEW_CWD}" "${TUI_PREVIEW_CWD}" "${TUI_PREVIEW_CWD}"
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
readonly PREVIEW_CONFIG='{
|
|
55
|
-
"theme": "dark",
|
|
56
|
-
"display": {
|
|
57
|
-
"style": "minimal",
|
|
58
|
-
"charset": "unicode",
|
|
59
|
-
"colorCompatibility": "auto",
|
|
60
|
-
"autoWrap": true,
|
|
61
|
-
"padding": 1,
|
|
62
|
-
"lines": [
|
|
63
|
-
{
|
|
64
|
-
"segments": {
|
|
65
|
-
"directory": { "enabled": true, "style": "fish" },
|
|
66
|
-
"git": { "enabled": true },
|
|
67
|
-
"model": { "enabled": true },
|
|
68
|
-
"session": { "enabled": true, "type": "cost", "costSource": "calculated" },
|
|
69
|
-
"today": { "enabled": true, "type": "cost" },
|
|
70
|
-
"context": { "enabled": true, "showPercentageOnly": false, "displayStyle": "text", "autocompactBuffer": 33000 }
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
]
|
|
74
|
-
},
|
|
75
|
-
"budget": {
|
|
76
|
-
"session": { "warningThreshold": 80 },
|
|
77
|
-
"today": { "amount": 50, "warningThreshold": 80 }
|
|
78
|
-
}
|
|
79
|
-
}'
|
|
80
|
-
|
|
81
|
-
THEME="dark"
|
|
82
|
-
STYLE="minimal"
|
|
83
|
-
CHARSET="unicode"
|
|
84
|
-
COMPARE_STYLES=false
|
|
85
|
-
COMPARE_THEMES=false
|
|
86
|
-
COMPARE_TUI_LAYOUTS=false
|
|
87
|
-
BIN=""
|
|
88
|
-
TEMP_FILES=()
|
|
15
|
+
readonly TEMPLATE="${PLUGIN_ROOT}/templates/config.json"
|
|
16
|
+
|
|
17
|
+
# The options the wizard offers, one list per template placeholder. Each name
|
|
18
|
+
# must be one the daemon accepts; the test pins that against the real domains.
|
|
19
|
+
readonly THEMES=(dark light nord tokyo-night rose-pine gruvbox catppuccin-mocha dracula solarized-dark monokai)
|
|
20
|
+
readonly STYLES=(powerline capsule plain)
|
|
21
|
+
readonly CHARSETS=(unicode ascii)
|
|
22
|
+
readonly PRESETS=(default compact verbose)
|
|
23
|
+
|
|
24
|
+
THEME="${THEMES[0]}"
|
|
25
|
+
STYLE="${STYLES[0]}"
|
|
26
|
+
CHARSET="${CHARSETS[0]}"
|
|
27
|
+
PRESET="${PRESETS[0]}"
|
|
28
|
+
COMPARE=""
|
|
29
|
+
|
|
30
|
+
WORK="$(mktemp -d)"
|
|
31
|
+
readonly WORK
|
|
32
|
+
readonly REPO="${WORK}/my-project"
|
|
33
|
+
readonly TRANSCRIPT="${WORK}/preview.jsonl"
|
|
34
|
+
CLIENT=()
|
|
89
35
|
|
|
90
36
|
# shellcheck disable=SC2329
|
|
91
37
|
cleanup() {
|
|
92
|
-
|
|
93
|
-
rm -f "${f}"
|
|
94
|
-
done
|
|
38
|
+
rm -rf "${WORK}"
|
|
95
39
|
}
|
|
96
40
|
trap cleanup EXIT
|
|
97
41
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
local npm_bin="${PLUGIN_ROOT}/../bin/cc-candybar"
|
|
105
|
-
local dist_file="${PLUGIN_ROOT}/../dist/index.mjs"
|
|
106
|
-
if [[ -f "${npm_bin}" ]] && [[ -f "${dist_file}" ]] && test_binary "${npm_bin}"; then
|
|
107
|
-
printf '%s' "${npm_bin}"
|
|
42
|
+
# The statusline client: this checkout's shim (developing against a checkout),
|
|
43
|
+
# an installed cc-candybar on PATH, else the published package via npx.
|
|
44
|
+
find_client() {
|
|
45
|
+
local checkout_bin="${PLUGIN_ROOT}/../bin/cc-candybar"
|
|
46
|
+
if [[ -x "${checkout_bin}" ]] && [[ -f "${PLUGIN_ROOT}/../dist/index.mjs" ]]; then
|
|
47
|
+
CLIENT=("${checkout_bin}")
|
|
108
48
|
return 0
|
|
109
49
|
fi
|
|
110
|
-
|
|
111
50
|
local path_bin
|
|
112
|
-
if path_bin="$(command -v cc-candybar
|
|
113
|
-
|
|
51
|
+
if path_bin="$(command -v cc-candybar)"; then
|
|
52
|
+
CLIENT=("${path_bin}")
|
|
114
53
|
return 0
|
|
115
54
|
fi
|
|
116
|
-
|
|
117
|
-
printf 'npx'
|
|
118
|
-
return 0
|
|
55
|
+
CLIENT=(npx -y @promptctl/cc-candybar@latest)
|
|
119
56
|
}
|
|
120
57
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
printf '%s' "${tmp}"
|
|
58
|
+
# A git repo on a feature branch with one untracked file, so the git segment has
|
|
59
|
+
# something to show, and a two-turn transcript for the session-derived segments.
|
|
60
|
+
make_fixture() {
|
|
61
|
+
mkdir -p "${REPO}"
|
|
62
|
+
git -C "${REPO}" init -b main --quiet
|
|
63
|
+
git -C "${REPO}" -c user.name="User" -c user.email="u@e.co" commit --allow-empty -m "init" --quiet
|
|
64
|
+
git -C "${REPO}" checkout -b feat/my-feature --quiet
|
|
65
|
+
touch "${REPO}/newfile.txt"
|
|
66
|
+
|
|
67
|
+
local now_iso
|
|
68
|
+
now_iso="$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")"
|
|
69
|
+
printf '{"timestamp":"%s","type":"user","message":{"role":"user","content":[{"type":"text","text":"hello"}]}}\n' "${now_iso}" >"${TRANSCRIPT}"
|
|
70
|
+
printf '{"timestamp":"%s","type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"done"}]}}\n' "${now_iso}" >>"${TRANSCRIPT}"
|
|
135
71
|
}
|
|
136
72
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
tmp_config="$(make_temp_config "${preview_theme}" "${preview_style}" "${preview_charset}")"
|
|
144
|
-
|
|
145
|
-
if [[ "${BIN}" == "npx" ]]; then
|
|
146
|
-
printf '%s' "${SAMPLE_JSON}" | FORCE_COLOR=3 npx -y @promptctl/cc-candybar@latest \
|
|
147
|
-
--config="${tmp_config}"
|
|
148
|
-
else
|
|
149
|
-
printf '%s' "${SAMPLE_JSON}" | FORCE_COLOR=3 "${BIN}" \
|
|
150
|
-
--config="${tmp_config}"
|
|
151
|
-
fi
|
|
73
|
+
# One Claude Code status hook payload, shaped like the daemon's required input.
|
|
74
|
+
sample_json() {
|
|
75
|
+
printf '{"hook_event_name":"Status","session_id":"preview-123","transcript_path":"%s","cwd":"%s","workspace":{"current_dir":"%s","project_dir":"%s"},"model":{"id":"claude-opus-4-8","display_name":"Opus 4.8"},"version":"1.0.47","cost":{"total_cost_usd":2.85,"total_duration_ms":16200000,"total_api_duration_ms":480000,"total_lines_added":342,"total_lines_removed":87},"context_window":{"context_window_size":200000,"used_percentage":42,"total_input_tokens":72000,"total_output_tokens":12000,"current_usage":{"input_tokens":72000,"output_tokens":12000,"cache_creation_input_tokens":15000,"cache_read_input_tokens":8000}},"rate_limits":{"five_hour":{"used_percentage":35,"resets_at":%d},"seven_day":{"used_percentage":28,"resets_at":%d}}}' \
|
|
76
|
+
"${TRANSCRIPT}" "${REPO}" "${REPO}" "${REPO}" \
|
|
77
|
+
"$(($(date +%s) + 15480))" "$(($(date +%s) + 432000))"
|
|
152
78
|
}
|
|
153
79
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if [[ "${BIN}" == "npx" ]]; then
|
|
159
|
-
printf '%s' "${sample_data}" | FORCE_COLOR=3 npx -y @promptctl/cc-candybar@latest \
|
|
160
|
-
--config="${config_file}"
|
|
161
|
-
else
|
|
162
|
-
printf '%s' "${sample_data}" | FORCE_COLOR=3 "${BIN}" \
|
|
163
|
-
--config="${config_file}"
|
|
164
|
-
fi
|
|
80
|
+
# The one render request, sent from inside the fixture repo (the daemon reads
|
|
81
|
+
# git state from the client's working directory).
|
|
82
|
+
render_request() {
|
|
83
|
+
(cd "${REPO}" && sample_json | "${CLIENT[@]}" "$@")
|
|
165
84
|
}
|
|
166
85
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
local template_file tmp tui_json fake_repo
|
|
171
|
-
|
|
172
|
-
setup_tui_transcript
|
|
173
|
-
tui_json="$(make_tui_sample_json)"
|
|
174
|
-
fake_repo="${TUI_PREVIEW_CWD}"
|
|
86
|
+
probe_daemon() {
|
|
87
|
+
{ "${CLIENT[@]}" daemon-stats --json >/dev/null; } 2>&1
|
|
88
|
+
}
|
|
175
89
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
90
|
+
# The first request on a cold daemon spawns it in the background and prints an
|
|
91
|
+
# empty line; a preview must not be that line. Kick once, then wait until the
|
|
92
|
+
# daemon answers or the deadline passes. The bound is wall-clock, not a probe
|
|
93
|
+
# count: a probe through npx costs seconds, a native one milliseconds.
|
|
94
|
+
ensure_daemon() {
|
|
95
|
+
local probe deadline=$((SECONDS + 15))
|
|
96
|
+
if probe="$(probe_daemon)"; then
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
render_request >/dev/null
|
|
100
|
+
until probe="$(probe_daemon)"; do
|
|
101
|
+
if ((SECONDS >= deadline)); then
|
|
102
|
+
printf 'preview: the cc-candybar daemon did not start within 15s: %s\n' "${probe}" >&2
|
|
103
|
+
exit 1
|
|
181
104
|
fi
|
|
182
|
-
|
|
183
|
-
tmp="$(mktemp)"
|
|
184
|
-
TEMP_FILES+=("${tmp}")
|
|
185
|
-
sed -e "s/replace:THEME/${THEME}/g" \
|
|
186
|
-
-e "s/replace:CHARSET/${CHARSET}/g" \
|
|
187
|
-
-e "s/\"replace:TODAY_BUDGET\"/50/g" \
|
|
188
|
-
"${template_file}" >"${tmp}"
|
|
189
|
-
|
|
190
|
-
printf '%s:\n' "${label}"
|
|
191
|
-
run_preview_config "${tmp}" "${tui_json}"
|
|
192
|
-
printf '\n\n'
|
|
105
|
+
sleep 0.1
|
|
193
106
|
done
|
|
194
|
-
|
|
195
|
-
# Clean up fake transcript and git repo
|
|
196
|
-
rm -f "${TUI_TRANSCRIPT}" 2>/dev/null
|
|
197
|
-
rm -rf "${fake_repo%/my-project}" 2>/dev/null
|
|
198
107
|
}
|
|
199
108
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
109
|
+
# The wizard's substitution, exactly: the template with its four placeholders
|
|
110
|
+
# filled. Prints the written path.
|
|
111
|
+
fill_template() {
|
|
112
|
+
local theme="$1" style="$2" charset="$3" preset="$4"
|
|
113
|
+
local out="${WORK}/config-${theme}-${style}-${charset}-${preset}.json"
|
|
114
|
+
sed -e "s/replace:THEME/${theme}/" \
|
|
115
|
+
-e "s/replace:STYLE/${style}/" \
|
|
116
|
+
-e "s/replace:CHARSET/${charset}/" \
|
|
117
|
+
-e "s/replace:PRESET/${preset}/" \
|
|
118
|
+
"${TEMPLATE}" >"${out}"
|
|
119
|
+
printf '%s' "${out}"
|
|
209
120
|
}
|
|
210
121
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
for t in "${themes[@]}"; do
|
|
216
|
-
printf '%s:\n' "${t}"
|
|
217
|
-
run_preview "${t}" "${STYLE}" "${CHARSET}"
|
|
218
|
-
printf '\n\n'
|
|
219
|
-
done
|
|
122
|
+
render() {
|
|
123
|
+
render_request --config="$(fill_template "$@")"
|
|
124
|
+
printf '\n'
|
|
220
125
|
}
|
|
221
126
|
|
|
222
127
|
parse_args() {
|
|
223
128
|
while [[ $# -gt 0 ]]; do
|
|
224
129
|
case "$1" in
|
|
225
|
-
--theme=*)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
--
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
THEME="$2"
|
|
235
|
-
shift 2
|
|
236
|
-
;;
|
|
237
|
-
--style=*)
|
|
238
|
-
STYLE="${1#*=}"
|
|
239
|
-
shift
|
|
240
|
-
;;
|
|
241
|
-
--style)
|
|
242
|
-
[[ $# -ge 2 ]] || {
|
|
243
|
-
printf 'Missing value for --style\n' >&2
|
|
244
|
-
exit 1
|
|
245
|
-
}
|
|
246
|
-
STYLE="$2"
|
|
247
|
-
shift 2
|
|
248
|
-
;;
|
|
249
|
-
--charset=*)
|
|
250
|
-
CHARSET="${1#*=}"
|
|
251
|
-
shift
|
|
252
|
-
;;
|
|
253
|
-
--charset)
|
|
254
|
-
[[ $# -ge 2 ]] || {
|
|
255
|
-
printf 'Missing value for --charset\n' >&2
|
|
256
|
-
exit 1
|
|
257
|
-
}
|
|
258
|
-
CHARSET="$2"
|
|
259
|
-
shift 2
|
|
260
|
-
;;
|
|
261
|
-
--compare-styles)
|
|
262
|
-
COMPARE_STYLES=true
|
|
263
|
-
shift
|
|
264
|
-
;;
|
|
265
|
-
--compare-themes)
|
|
266
|
-
COMPARE_THEMES=true
|
|
267
|
-
shift
|
|
268
|
-
;;
|
|
269
|
-
--compare-tui-layouts)
|
|
270
|
-
COMPARE_TUI_LAYOUTS=true
|
|
271
|
-
shift
|
|
272
|
-
;;
|
|
130
|
+
--theme=*) THEME="${1#*=}" ;;
|
|
131
|
+
--style=*) STYLE="${1#*=}" ;;
|
|
132
|
+
--charset=*) CHARSET="${1#*=}" ;;
|
|
133
|
+
--preset=*) PRESET="${1#*=}" ;;
|
|
134
|
+
--compare-themes) COMPARE="themes" ;;
|
|
135
|
+
--compare-styles) COMPARE="styles" ;;
|
|
136
|
+
--compare-charsets) COMPARE="charsets" ;;
|
|
137
|
+
--compare-presets) COMPARE="presets" ;;
|
|
273
138
|
*)
|
|
274
139
|
printf 'Unknown option: %s\n' "$1" >&2
|
|
275
|
-
|
|
140
|
+
printf 'Usage: preview.sh [--theme=NAME] [--style=NAME] [--charset=NAME] [--preset=NAME] [--compare-themes|--compare-styles|--compare-charsets|--compare-presets]\n' >&2
|
|
141
|
+
exit 2
|
|
276
142
|
;;
|
|
277
143
|
esac
|
|
144
|
+
shift
|
|
278
145
|
done
|
|
279
146
|
}
|
|
280
147
|
|
|
281
148
|
main() {
|
|
282
149
|
parse_args "$@"
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
run_preview "${THEME}" "${STYLE}" "${CHARSET}"
|
|
301
|
-
printf '\n'
|
|
302
|
-
exit 0
|
|
150
|
+
find_client
|
|
151
|
+
make_fixture
|
|
152
|
+
ensure_daemon
|
|
153
|
+
# The flag values are checked once, as the wizard's user would see them
|
|
154
|
+
# (exit 1 names the error). A compare varies one axis over its array, whose
|
|
155
|
+
# members test/plugin-templates.test.ts pins to the loader's own domain.
|
|
156
|
+
"${CLIENT[@]}" check "$(fill_template "${THEME}" "${STYLE}" "${CHARSET}" "${PRESET}")" >/dev/null
|
|
157
|
+
|
|
158
|
+
local name
|
|
159
|
+
case "${COMPARE}" in
|
|
160
|
+
"") render "${THEME}" "${STYLE}" "${CHARSET}" "${PRESET}" ;;
|
|
161
|
+
themes) for name in "${THEMES[@]}"; do printf '%s:\n' "${name}"; render "${name}" "${STYLE}" "${CHARSET}" "${PRESET}"; printf '\n'; done ;;
|
|
162
|
+
styles) for name in "${STYLES[@]}"; do printf '%s:\n' "${name}"; render "${THEME}" "${name}" "${CHARSET}" "${PRESET}"; printf '\n'; done ;;
|
|
163
|
+
charsets) for name in "${CHARSETS[@]}"; do printf '%s:\n' "${name}"; render "${THEME}" "${STYLE}" "${name}" "${PRESET}"; printf '\n'; done ;;
|
|
164
|
+
presets) for name in "${PRESETS[@]}"; do printf '%s:\n' "${name}"; render "${THEME}" "${STYLE}" "${CHARSET}" "${name}"; printf '\n'; done ;;
|
|
165
|
+
esac
|
|
303
166
|
}
|
|
304
167
|
|
|
305
168
|
main "$@"
|