pi-firecode 1.0.1 → 1.0.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/README.md CHANGED
@@ -5,6 +5,11 @@
5
5
  </picture>
6
6
  </p>
7
7
 
8
+ <p align="center">
9
+ <a href="README.md"><img alt="English" src="https://img.shields.io/badge/English-2b2b2b?style=for-the-badge"></a>
10
+ <a href="README.zh-CN.md"><img alt="简体中文" src="https://img.shields.io/badge/%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87-FF7A0F?style=for-the-badge"></a>
11
+ </p>
12
+
8
13
  <h3 align="center">Install one extension. Uninstall the rest.</h3>
9
14
 
10
15
  <p align="center">Parallel sub-agents, multi-model code review and a cleaner terminal for <a href="https://pi.dev">Pi</a>. One install, works out of the box.</p>
@@ -13,7 +18,6 @@
13
18
  <a href="https://www.npmjs.com/package/pi-firecode"><img alt="npm" src="https://img.shields.io/npm/v/pi-firecode?color=FF7A0F"></a>
14
19
  <a href="https://github.com/Suge8/firecode/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Suge8/firecode/actions/workflows/ci.yml/badge.svg"></a>
15
20
  <a href="LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
16
- · <a href="README.zh-CN.md">中文</a>
17
21
  </p>
18
22
 
19
23
  <p align="center"><img alt="The commander dispatching three sub-agents in parallel" src="https://raw.githubusercontent.com/Suge8/firecode/main/design/promo/hero.gif" width="860"></p>
package/README.zh-CN.md CHANGED
@@ -5,6 +5,11 @@
5
5
  </picture>
6
6
  </p>
7
7
 
8
+ <p align="center">
9
+ <a href="README.md"><img alt="English" src="https://img.shields.io/badge/English-FF7A0F?style=for-the-badge"></a>
10
+ <a href="README.zh-CN.md"><img alt="简体中文" src="https://img.shields.io/badge/%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87-2b2b2b?style=for-the-badge"></a>
11
+ </p>
12
+
8
13
  <h3 align="center">装这一个,其余扩展都可以卸了。</h3>
9
14
 
10
15
  <p align="center">给 <a href="https://pi.dev">Pi</a> 加上并行子代理、多模型代码审查和更干净的终端界面。装一个就够,开箱即用。</p>
@@ -13,7 +18,6 @@
13
18
  <a href="https://www.npmjs.com/package/pi-firecode"><img alt="npm" src="https://img.shields.io/npm/v/pi-firecode?color=FF7A0F"></a>
14
19
  <a href="https://github.com/Suge8/firecode/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Suge8/firecode/actions/workflows/ci.yml/badge.svg"></a>
15
20
  <a href="LICENSE"><img alt="MIT" src="https://img.shields.io/badge/license-MIT-blue"></a>
16
- · <a href="README.md">English</a>
17
21
  </p>
18
22
 
19
23
  <p align="center"><img alt="指挥官并行派出三个子代理" src="https://raw.githubusercontent.com/Suge8/firecode/main/design/promo/hero.zh.gif" width="860"></p>
package/dist/index.js CHANGED
@@ -720,94 +720,83 @@ function formatModelName(id) {
720
720
  }
721
721
 
722
722
  // header.ts
723
- var LARGE_MIN_WIDTH = 83;
724
- var MID_MIN_WIDTH = 52;
725
- var REVEAL_END = 0.7;
726
- var SWEEP_END = 1.5;
723
+ var WORD = "firecode";
724
+ var IGNITE_END = 0.6;
725
+ var REVEAL_START = 0.2;
726
+ var SETTLE_END = 1;
727
727
  var TINY_FLAME_PHASE = 0.4;
728
+ var LOGO_GAP = 3;
729
+ var FLAME_RED_END = 0.45;
730
+ var WORD_TOP = 3;
728
731
  var FADE_FROM = [20, 19, 18];
729
732
  var RULE2 = [60, 57, 53];
730
733
  var TINY_SUBTITLE_MIN = 8;
731
734
  var TINY_RULE_MIN = 3;
732
735
  var clamp2 = (value) => Math.min(1, Math.max(0, value));
733
736
  var ease = (k) => 1 - (1 - clamp2(k)) ** 3;
734
- var background = (color) => `\x1B[48;2;${color[0] | 0};${color[1] | 0};${color[2] | 0}m`;
735
- function halfBlocks(pixels) {
736
- const lines = [];
737
- for (let y = 0;y < pixels.length; y += 2) {
738
- let line = "";
739
- pixels[y].forEach((top, x) => {
740
- const bottom = pixels[y + 1]?.[x];
741
- if (top && bottom)
742
- line += `${background(bottom)}${paint(top, "▀")}\x1B[49m`;
743
- else if (top)
744
- line += paint(top, "▀");
745
- else if (bottom)
746
- line += paint(bottom, "▄");
747
- else
748
- line += " ";
749
- });
750
- lines.push(line);
751
- }
752
- return lines;
753
- }
754
- function roundFlame(widthPx, heightPx, t) {
755
- const radius = widthPx / 2;
756
- const belly = 0.36;
757
- const tall = 0.9 + 0.1 * flicker(0.2, t);
758
- return Array.from({ length: heightPx }, (_, y) => {
759
- const row = Array(widthPx).fill(undefined);
760
- const v = (heightPx - 0.5 - y) / heightPx / tall;
761
- if (v > 1)
762
- return row;
763
- const half = v < belly ? radius * Math.sqrt(1 - ((belly - v) / belly) ** 2) : radius * ((1 - v) / (1 - belly)) ** 1.25;
764
- const sway = 1.3 * v * v * Math.sin(Math.PI * 2 * 0.6 * t) + 0.4 * v * Math.sin(Math.PI * 2 * (1.7 * t + v * 2));
765
- const reach = half + 0.25 * flicker(v * 3.1, t) * v;
766
- for (let x = 0;x < widthPx; x++) {
767
- const distance = Math.abs(x + 0.5 - radius - sway);
768
- if (distance > reach)
769
- continue;
770
- const core = 1 - distance / (reach + 0.01);
771
- row[x] = heat(0.12 + 0.55 * core * (1 - v * 0.6) + 0.45 * (1 - v) ** 2);
772
- }
773
- return row;
774
- });
775
- }
737
+ var FLAME = [
738
+ "......#......",
739
+ "......##.....",
740
+ ".....###.....",
741
+ ".....####....",
742
+ "....#####....",
743
+ "...######..#.",
744
+ "..#######.##.",
745
+ ".###########.",
746
+ "#############",
747
+ "##..#########",
748
+ "###..########",
749
+ "####..#######",
750
+ "###..########",
751
+ "##..##.....##",
752
+ ".###########.",
753
+ "...#######..."
754
+ ];
776
755
  var FONT = {
777
- F: ["#####", "#....", "#....", "####.", "#....", "#....", "#....", "#...."],
778
- i: ["#", ".", "#", "#", "#", "#", "#", "#"],
779
- r: ["....", "....", ".###", "#...", "#...", "#...", "#...", "#..."],
780
- e: [".....", ".....", ".###.", "#...#", "#####", "#....", "#....", ".####"],
781
- C: [".####", "#....", "#....", "#....", "#....", "#....", "#....", ".####"],
782
- o: [".....", ".....", ".###.", "#...#", "#...#", "#...#", "#...#", ".###."],
783
- d: ["....#", "....#", ".####", "#...#", "#...#", "#...#", "#...#", ".####"]
756
+ f: ["..###", ".####", ".##..", "#####", "#####", ".##..", ".##..", ".##..", ".##..", ".##.."],
757
+ i: ["..", "##", "..", "##", "##", "##", "##", "##", "##", "##"],
758
+ r: ["....", "....", "....", "##.##", "#####", "###..", "##...", "##...", "##...", "##..."],
759
+ e: ["......", "......", "......", ".####.", "######", "##..##", "######", "##....", "######", ".####."],
760
+ c: ["......", "......", "......", ".####.", "######", "##..#.", "##....", "##..#.", "######", ".####."],
761
+ o: ["......", "......", "......", ".####.", "######", "##..##", "##..##", "##..##", "######", ".####."],
762
+ d: ["....##", "....##", "....##", ".#####", "######", "##..##", "##..##", "##..##", "######", ".#####"]
784
763
  };
785
- var WORD = "FireCode";
786
- var wordBits = (gap) => FONT.F.map((_, y) => [...WORD].flatMap((char, index) => [
787
- ...Array(index ? gap : 0).fill(false),
788
- ...[...FONT[char][y]].map((pixel) => pixel === "#")
789
- ]));
790
- var WORD_BITS = { 1: wordBits(1), 2: wordBits(2) };
791
- function wordmark(gap, t) {
792
- const bits = WORD_BITS[gap];
793
- const width = bits[0].length;
794
- const edge = t / REVEAL_END * (width + 8);
795
- const sweep = (t - REVEAL_END - 0.1) / (SWEEP_END - REVEAL_END) * (width + 16) - 8;
796
- return bits.map((row, y) => row.map((on, x) => {
797
- if (!on || x > edge)
798
- return;
799
- const half = width / 2;
800
- const base = x < half ? mix(HEAT_COLORS.red, HEAT_COLORS.orange, x / half) : mix(HEAT_COLORS.orange, HEAT_COLORS.gold, (x - half) / half);
801
- const hotEdge = t < REVEAL_END ? clamp2(1 - (edge - x) / 6) : 0;
802
- const shine = t > REVEAL_END && t < SWEEP_END ? clamp2(1 - Math.abs(x - y * 0.6 - sweep) / 4.5) : 0;
803
- return mix(base, HEAT_COLORS.white, Math.max(hotEdge, shine * 0.9));
804
- }));
764
+ var WORD_ROWS = FONT.f.map((_, y) => [...WORD].map((char) => FONT[char][y]).join("."));
765
+ var FLAME_WIDTH = FLAME[0].length;
766
+ var WORD_LEFT = FLAME_WIDTH + LOGO_GAP;
767
+ var LOGO_WIDTH = WORD_LEFT + WORD_ROWS[0].length;
768
+ var LARGE_MIN_WIDTH = LOGO_WIDTH + 2;
769
+ var background = (color) => `\x1B[48;2;${color[0] | 0};${color[1] | 0};${color[2] | 0}m`;
770
+ function cell(top, bottom) {
771
+ if (top && bottom)
772
+ return `${background(bottom)}${paint(top, "▀")}\x1B[49m`;
773
+ if (top)
774
+ return paint(top, "▀");
775
+ return bottom ? paint(bottom, "▄") : " ";
805
776
  }
806
- function sideBySide(left, right, gap, rightTop = 0) {
807
- const leftWidth = Math.max(...left.map(visibleWidth2));
808
- return Array.from({ length: Math.max(left.length, right.length + rightTop) }, (_, index) => {
809
- const cell = left[index] ?? "";
810
- return `${cell}${" ".repeat(leftWidth - visibleWidth2(cell) + gap)}${right[index - rightTop] ?? ""}`;
777
+ function halfBlocks(pixels) {
778
+ const rows = [];
779
+ for (let y = 0;y < pixels.length; y += 2)
780
+ rows.push(pixels[y].map((top, x) => cell(top, pixels[y + 1]?.[x])));
781
+ return rows;
782
+ }
783
+ function flameHeat(y) {
784
+ const v = y / (FLAME.length - 1);
785
+ return v < FLAME_RED_END ? mix(HEAT_COLORS.red, HEAT_COLORS.orange, v / FLAME_RED_END) : mix(HEAT_COLORS.orange, HEAT_COLORS.gold, (v - FLAME_RED_END) / (1 - FLAME_RED_END));
786
+ }
787
+ function logo(t) {
788
+ const flameLine = ease(t / IGNITE_END) * (FLAME.length + 4);
789
+ const wordWidth = WORD_ROWS[0].length;
790
+ const wordEdge = (t - REVEAL_START) / (SETTLE_END - REVEAL_START) * wordWidth;
791
+ return FLAME.map((row, y) => {
792
+ const burnt = flameLine - (FLAME.length - y);
793
+ const color = mix(HEAT_COLORS.white, flameHeat(y), clamp2(burnt / 4));
794
+ const pixels = [...row].map((dot) => dot === "#" && burnt >= 0 ? color : undefined);
795
+ pixels.push(...Array(LOGO_GAP).fill(undefined));
796
+ const word = WORD_ROWS[y - WORD_TOP] ?? "";
797
+ for (let x = 0;x < wordWidth; x++)
798
+ pixels.push(word[x] === "#" && x < wordEdge ? mix(HEAT_COLORS.orange, HEAT_COLORS.gold, x / wordWidth) : undefined);
799
+ return pixels;
811
800
  });
812
801
  }
813
802
  function fitSubtitle({ version, cwd }, width) {
@@ -826,45 +815,35 @@ function subtitleLine(subtitle, width, t) {
826
815
  return paint(mix(FADE_FROM, HEAT_COLORS.ash, ease((t - 0.4) / 0.6)), fitSubtitle(subtitle, width));
827
816
  }
828
817
  function large(t, subtitle) {
829
- const word = halfBlocks(wordmark(2, t));
830
- return sideBySide(halfBlocks(roundFlame(10, 16, t)), [...word, "", subtitleLine(subtitle, visibleWidth2(word[0]), t)], 3, 2);
831
- }
832
- function mid(t, subtitle) {
833
- const word = halfBlocks(wordmark(1, t));
834
- return sideBySide(halfBlocks(roundFlame(6, 8, t)), [...word, subtitleLine(subtitle, visibleWidth2(word[0]), t)], 2);
818
+ const rows = halfBlocks(logo(t));
819
+ const last = rows.pop().slice(0, WORD_LEFT).join("") + subtitleLine(subtitle, LOGO_WIDTH - WORD_LEFT, t);
820
+ return [...rows.map((row) => row.join("").trimEnd()), last];
835
821
  }
836
822
  function tiny(width, t, glyph, subtitle) {
837
- const sweep = (t - REVEAL_END) / (SWEEP_END - REVEAL_END) * 14 - 3;
838
- const letters = [...WORD].map((char, index) => {
839
- if (t <= index / WORD.length * REVEAL_END)
840
- return " ";
841
- const shine = t > REVEAL_END && t < SWEEP_END ? clamp2(1 - Math.abs(index - sweep) / 2) : 0;
842
- return paint(mix(mix(HEAT_COLORS.orange, HEAT_COLORS.gold, index / (WORD.length - 1)), HEAT_COLORS.white, shine), char);
843
- }).join("");
823
+ const shown = Math.ceil((t - REVEAL_START) / (SETTLE_END - REVEAL_START) * WORD.length);
824
+ const letters = [...WORD].map((char, index) => index < shown ? char : " ").join("");
844
825
  const word = `${glyph} \x1B[1m${letters}\x1B[22m `;
845
826
  const room = width - visibleWidth2(word) - 1 - TINY_RULE_MIN;
846
827
  const head = room >= TINY_SUBTITLE_MIN ? `${word}${subtitleLine(subtitle, room, t)} ` : word;
847
828
  const length = width - visibleWidth2(head);
848
- const reveal = clamp2((t - 0.2) / REVEAL_END);
829
+ const reveal = clamp2((t - REVEAL_START) / (SETTLE_END - REVEAL_START));
849
830
  let rule = "";
850
831
  for (let index = 0;index < length; index++)
851
832
  rule += index / length <= reveal ? paint(mix(HEAT_COLORS.gold, RULE2, ease(index / Math.max(8, length * 0.7))), "─") : " ";
852
833
  return clip(head + rule, width, "end", "");
853
834
  }
854
835
  function center(lines, width) {
855
- const pad = " ".repeat(Math.max(0, Math.floor((width - Math.max(...lines.map(visibleWidth2))) / 2)));
836
+ const pad = " ".repeat(Math.max(0, Math.floor((width - LOGO_WIDTH) / 2)));
856
837
  return lines.map((line) => clip(pad + line, width, "end", ""));
857
838
  }
858
839
  function banner(width, t, glyph, subtitle) {
859
840
  if (width >= LARGE_MIN_WIDTH)
860
841
  return ["", ...center(large(t, subtitle), width), ""];
861
- if (width >= MID_MIN_WIDTH)
862
- return ["", ...center(mid(t, subtitle), width), ""];
863
842
  return [tiny(width, t, glyph, subtitle), ""];
864
843
  }
865
844
  function createBanner(tui, subtitle) {
866
845
  const start = Date.now();
867
- const elapsed = () => Math.min(SWEEP_END, (Date.now() - start) / 1000);
846
+ const elapsed = () => Math.min(SETTLE_END, (Date.now() - start) / 1000);
868
847
  let unsubscribe;
869
848
  const dispose = () => {
870
849
  unsubscribe?.();
@@ -872,7 +851,7 @@ function createBanner(tui, subtitle) {
872
851
  };
873
852
  unsubscribe = onFrame(() => {
874
853
  tui.requestRender();
875
- if (elapsed() >= SWEEP_END)
854
+ if (elapsed() >= SETTLE_END)
876
855
  dispose();
877
856
  });
878
857
  let frozenGlyph;
@@ -882,7 +861,7 @@ function createBanner(tui, subtitle) {
882
861
  dispose,
883
862
  render(width) {
884
863
  const t = elapsed();
885
- if (t < SWEEP_END)
864
+ if (t < SETTLE_END)
886
865
  return banner(width, t, flame(3, TINY_FLAME_PHASE), subtitle);
887
866
  if (frozen?.width !== width) {
888
867
  frozenGlyph ??= flame(3, TINY_FLAME_PHASE);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-firecode",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "A modular Pi extension for terminal UI, session workflows, adversarial review, and delegated agents",
5
5
  "type": "module",
6
6
  "license": "MIT",