@firstpick/pi-package-webui 0.3.8 → 0.3.9

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/bin/pi-webui.mjs CHANGED
@@ -2776,10 +2776,23 @@ function gitBranchFromStatus(statusText) {
2776
2776
  return branchLine.slice(3).trim().replace(/\.\.\..*$/, "") || "detached";
2777
2777
  }
2778
2778
 
2779
+ function gitDivergenceFromBranchStatus(line) {
2780
+ const details = String(line || "").match(/\[(.+)\]\s*$/)?.[1] || "";
2781
+ const ahead = Number.parseInt(details.match(/ahead\s+(\d+)/i)?.[1] || "0", 10) || 0;
2782
+ const behind = Number.parseInt(details.match(/behind\s+(\d+)/i)?.[1] || "0", 10) || 0;
2783
+ return { ahead, behind };
2784
+ }
2785
+
2779
2786
  function summarizeGitShortStatus(statusText) {
2780
- const summary = { staged: 0, unstaged: 0, untracked: 0, conflicted: 0 };
2787
+ const summary = { staged: 0, unstaged: 0, untracked: 0, conflicted: 0, ahead: 0, behind: 0 };
2781
2788
  for (const line of String(statusText || "").split(/\r?\n/)) {
2782
- if (!line || line.startsWith("## ")) continue;
2789
+ if (!line) continue;
2790
+ if (line.startsWith("## ")) {
2791
+ const divergence = gitDivergenceFromBranchStatus(line);
2792
+ summary.ahead = divergence.ahead;
2793
+ summary.behind = divergence.behind;
2794
+ continue;
2795
+ }
2783
2796
  const x = line[0] || " ";
2784
2797
  const y = line[1] || " ";
2785
2798
  if (x === "?" && y === "?") {
@@ -3559,8 +3572,19 @@ function buildPiArgsForTab(tabIndex, title) {
3559
3572
  return args;
3560
3573
  }
3561
3574
 
3575
+ function isNodeScriptCommand(command) {
3576
+ return [".cjs", ".js", ".mjs"].includes(path.extname(String(command || "")).toLowerCase());
3577
+ }
3578
+
3562
3579
  async function resolvePiCommand(piArgs) {
3563
3580
  if (options.piBinExplicit) {
3581
+ if (isNodeScriptCommand(options.piBin)) {
3582
+ return {
3583
+ command: process.execPath,
3584
+ args: [options.piBin, ...piArgs],
3585
+ displayCommand: `${process.execPath} ${options.piBin} ${piArgs.join(" ")}`,
3586
+ };
3587
+ }
3564
3588
  return { command: options.piBin, args: piArgs, displayCommand: `${options.piBin} ${piArgs.join(" ")}` };
3565
3589
  }
3566
3590
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firstpick/pi-package-webui",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "Pi Web UI companion package with a local browser UI CLI plus /webui-start and /webui-status commands.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/Firstp1ck/npm-packages/tree/main/pi-package-webui#readme",
package/public/app.js CHANGED
@@ -4955,7 +4955,7 @@ const GIT_FOOTER_TOOLTIP_COPY = {
4955
4955
  git: "Current Git branch. detached means HEAD is not on a branch; no repo means the cwd is outside a Git work tree.",
4956
4956
  "git-state": "Active Git operation or detached state. Finish or abort rebase/merge/cherry-pick/revert/bisect before normal commits.",
4957
4957
  sync: "Remote tracking divergence. ↑ means local commits ahead; ↓ means remote commits to pull.",
4958
- changes: "Working tree summary. 🟢 staged, ✏️ modified unstaged, ➕ untracked, ⚠️ conflicted; ✅ means no changes.",
4958
+ changes: "Working tree and fetched remote summary. 🟢 staged, ✏️ modified unstaged, ➕ untracked, ⚠️ conflicted; ⬇️ means fetched remote commits to pull; 🔄/✓/⚠️ fetch shows the tab git fetch state; ✅ means no changes.",
4959
4959
  "git-extra": "Extra Git signals. 📦 stash, 🧩 dirty submodules, 🌳 worktrees, 🏷️ tag at HEAD, 🕒 last commit age, 🔓 signing mismatch.",
4960
4960
  model: "Scoped model for this tab.",
4961
4961
  thinking: "Reasoning/thinking effort for this tab.",
@@ -5414,10 +5414,14 @@ function gitChangesChip(label, value, className = "") {
5414
5414
  function renderGitChangesOverview(data) {
5415
5415
  const summary = data?.summary || {};
5416
5416
  const untrackedCount = Array.isArray(data?.untracked) ? data.untracked.length : Number(summary.untracked || 0);
5417
+ const ahead = Number(summary.ahead || 0) || 0;
5418
+ const behind = Number(summary.behind || 0) || 0;
5417
5419
  const overview = make("div", "git-changes-overview");
5418
5420
  overview.append(
5419
5421
  gitChangesChip("repo", data?.root || "—", "wide"),
5420
5422
  gitChangesChip("branch", data?.branch || "detached"),
5423
+ gitChangesChip("ahead", ahead > 0 ? `↑${ahead}` : 0, ahead > 0 ? "warning" : "muted"),
5424
+ gitChangesChip("remote", behind > 0 ? `↓${behind}` : 0, behind > 0 ? "danger" : "muted"),
5421
5425
  gitChangesChip("staged", summary.staged || 0, "success"),
5422
5426
  gitChangesChip("modified", summary.unstaged || 0, "warning"),
5423
5427
  gitChangesChip("untracked", untrackedCount, "muted"),
@@ -6558,6 +6562,8 @@ function scheduleRefreshFooter(delay = 300, tabContext = activeTabContext()) {
6558
6562
  function formatCodexPlanType(value) {
6559
6563
  const text = String(value || "").trim();
6560
6564
  if (!text) return "unknown plan";
6565
+ const normalized = text.replace(/[\s_-]+/g, "").toLowerCase();
6566
+ if (normalized === "prolite") return "Usage";
6561
6567
  return text.replace(/[_-]+/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
6562
6568
  }
6563
6569
 
@@ -490,6 +490,7 @@ assert.match(
490
490
  "side-panel section toggles should expand at most one section at a time",
491
491
  );
492
492
  assert.match(app, /function renderCodexUsage\(\)/, "frontend should render Codex usage buckets in the side panel");
493
+ assert.match(app, /if \(normalized === "prolite"\) return "Usage";/, "Codex Prolite plan labels should display as Usage in the side panel");
493
494
  assert.match(app, /api\(`\/api\/codex-usage\$\{suffix\}`, \{ scoped: false \}\)/, "Codex usage should load through a server endpoint without browser credentials");
494
495
  assert.match(app, /restoreSidePanelSectionState\(\);\nbindSidePanelSectionToggles\(\);/, "side panel section state should restore before toggles are bound");
495
496
  assert.match(app, /OPTIONAL_FEATURES_STORAGE_KEY/, "optional feature disable toggles should persist in browser storage");