@bismawy/pi-arnative 0.3.0 → 0.3.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.1] - 2026-10-04
4
+
5
+ ### Changed
6
+ - `/arnative headers` and `/arnative footers` picker rows are reordered built-in first (`Pi (system)`, `Arnative (Full)`) and the parenthetical suffix now renders in the theme's `dim` token, so the preset name carries the row and the qualifier reads as a footnote (`extensions/arnative.ts`).
7
+ - Preset ids renamed: `Pi` → `Pi (system)`. `Arnative (Full)` is still the default and the header/footer slot logic is unchanged.
8
+ - Picker subtitles no longer repeat the preset names: "Selection applies live — custom header or built-in."
9
+ - README shows only `assets/pi-arnative.webp` as the hero image; the full-width `assets/banner.webp` stays reserved for the pi.dev package page via `pi.image`.
10
+ - `package.json` `description` now leads with the README tagline ("Refined aesthetics. Cohesive tools. Built for Pi.") followed by the capability summary, per the `/arnative-pi` manifest standard — pi.dev/packages renders this field verbatim as the package card description.
11
+
3
12
  ## [0.3.0] - 2026-10-03
4
13
 
5
14
  ### Fixed
package/README.md CHANGED
@@ -6,8 +6,6 @@ Refined aesthetics. Cohesive tools. Built for Pi.
6
6
  [![badge](https://shieldcn.dev/npm/@bismawy/pi-arnative.svg?variant=outline&size=xs)](https://www.npmjs.com/package/@bismawy/pi-arnative)
7
7
  [![license](https://shieldcn.dev/github/bismawy/pi-arnative/license.svg?variant=outline&size=xs)](https://github.com/bismawy/pi-arnative)
8
8
 
9
- <img src="https://raw.githubusercontent.com/bismawy/pi-arnative/main/assets/banner.webp" alt="Arnative" width="100%">
10
-
11
9
  ![Arnative: tabbed header, rounded tool boxes, transcript clock and /usage dashboard](https://raw.githubusercontent.com/bismawy/pi-arnative/main/assets/pi-arnative.webp)
12
10
 
13
11
  ## Overview
@@ -112,9 +112,13 @@ export class ListPicker implements Component {
112
112
  const name = this.names[i]!;
113
113
  const isSelected = i === this.selected;
114
114
  const mark = name === this.applied ? "● " : " ";
115
- const body = truncateToWidth(`${isSelected ? "▸" : " "} ${mark}${name}`, width - 4);
115
+ const plain = truncateToWidth(`${isSelected ? "▸" : " "} ${mark}${name}`, width - 4);
116
+ // Preset rows end in a parenthetical (`Pi (system)`, `Arnative (Full)`): the suffix reads softer than the name.
117
+ const suffix = /(\(.*\))$/.exec(plain)?.[1] ?? "";
118
+ const head = plain.slice(0, plain.length - suffix.length);
119
+ const tail = suffix ? th.fg("dim", suffix) : "";
116
120
  // Same rule as the select dialogs: marker accent, name soft.
117
- out.push(row(isSelected ? th.fg("accent", body.slice(0, 2 + mark.length)) + soft(body.slice(2 + mark.length)) : th.fg("text", body)));
121
+ out.push(row(isSelected ? th.fg("accent", head.slice(0, 2 + mark.length)) + soft(head.slice(2 + mark.length)) + tail : th.fg("text", head) + tail));
118
122
  }
119
123
  out.push(row(""));
120
124
  out.push(row(th.fg("dim", this.text.footer)));
@@ -168,7 +172,7 @@ const openThemes = async (ctx: ExtensionCommandContext): Promise<void> => {
168
172
 
169
173
  const FOOTERS_PICKER_TEXT: ListPickerText = {
170
174
  title: "Arnative · Footers",
171
- subtitle: "Selection applies live — Arnative (Full) or Pi's built-in footer.",
175
+ subtitle: "Selection applies live — custom footer or built-in.",
172
176
  footer: "[↑↓] Preview live [Enter] Keep [Esc] Revert",
173
177
  };
174
178
 
@@ -204,7 +208,7 @@ const openFooters = async (ctx: ExtensionCommandContext): Promise<void> => {
204
208
 
205
209
  const HEADERS_PICKER_TEXT: ListPickerText = {
206
210
  title: "Arnative · Headers",
207
- subtitle: "Selection applies live — Arnative (Full) or Pi's built-in header.",
211
+ subtitle: "Selection applies live — custom header or built-in.",
208
212
  footer: "[↑↓] Preview live [Enter] Keep [Esc] Revert",
209
213
  };
210
214
 
@@ -341,19 +345,19 @@ if (isMain(import.meta.url)) {
341
345
  assert(reverted === 1 && done === 2, "Esc reverts after preview");
342
346
 
343
347
  // Headers reuses the same picker with its own copy and presets.
344
- assert(HEADER_PRESETS[0] === "Arnative (Full)", "first preset is Arnative (Full)");
348
+ assert(HEADER_PRESETS[0] === "Pi (system)", "built-in header preset is listed first");
345
349
  const headerPicker = new ListPicker(fakeTheme, [...HEADER_PRESETS], "Arnative (Full)", { preview: () => {}, revert: () => {}, done: () => {}, requestRender: () => {} }, HEADERS_PICKER_TEXT);
346
350
  const headerLines = headerPicker.render(50);
347
351
  assert(headerLines.some((l) => l.includes("Arnative · Headers")), "headers picker title");
348
352
  assert(headerLines.some((l) => l.includes("● Arnative (Full)")), "headers picker marks the active preset");
349
- assert(headerLines.some((l) => l.includes(" Pi")), "headers picker lists Pi");
353
+ assert(headerLines.some((l) => l.includes(" Pi (system)")), "headers picker lists the built-in preset");
350
354
 
351
355
  // Footers reuses the same picker too.
352
- assert(FOOTER_PRESETS[0] === "Arnative (Full)", "first footer preset is Arnative (Full)");
353
- const footerPicker = new ListPicker(fakeTheme, [...FOOTER_PRESETS], "Pi", { preview: () => {}, revert: () => {}, done: () => {}, requestRender: () => {} }, FOOTERS_PICKER_TEXT);
356
+ assert(FOOTER_PRESETS[0] === "Pi (system)", "built-in footer preset is listed first");
357
+ const footerPicker = new ListPicker(fakeTheme, [...FOOTER_PRESETS], "Pi (system)", { preview: () => {}, revert: () => {}, done: () => {}, requestRender: () => {} }, FOOTERS_PICKER_TEXT);
354
358
  const footerLines = footerPicker.render(50);
355
359
  assert(footerLines.some((l) => l.includes("Arnative · Footers")), "footers picker title");
356
- assert(footerLines.some((l) => l.includes("● Pi")), "footers picker marks the active preset");
360
+ assert(footerLines.some((l) => l.includes("● Pi (system)")), "footers picker marks the active preset");
357
361
 
358
362
  console.log("arnative.ts self-check OK");
359
363
  }
@@ -4,7 +4,7 @@
4
4
  * Rows 2-3: extension status (mcp first) ......... tok/s · cache · token,
5
5
  * each side wrapped inside its half of the width and paired row by row.
6
6
  *
7
- * Preset (`/arnative footers`): "Arnative (Full)" (default) or "Pi" (built-in footer), live.
7
+ * Preset (`/arnative footers`): "Arnative (Full)" (default) or "Pi (system)" (built-in footer), live.
8
8
  * Transcript clock & bubble bg: extensions/timestamps.ts
9
9
  * /new header: extensions/section-headers.ts
10
10
  * Selection + box reload: extensions/ui-render-tweaks.ts
@@ -27,10 +27,11 @@ import { formatTokens } from "../lib/usage-store.ts";
27
27
  // fire this twice even when the terminal reports event types (Kitty flag 2).
28
28
 
29
29
  // Footer preset chosen through `/arnative footers`. "Arnative (Full)" = this extension's
30
- // 3-row grid footer; "Pi" = pi's built-in footer (the boxed editor stays arnative in both —
31
- // pi's `setEditorComponent()` would close the picker mid-preview). The picker lives in
32
- // arnative.ts, so the choice is shared through globalThis like the header preset.
33
- export const FOOTER_PRESETS = ["Arnative (Full)", "Pi"] as const;
30
+ // 3-row grid footer; "Pi (system)" = pi's built-in footer (the boxed editor stays arnative
31
+ // in both — pi's `setEditorComponent()` would close the picker mid-preview). The picker
32
+ // lives in arnative.ts, so the choice is shared through globalThis like the header preset.
33
+ // Listed built-in-first: the `(Full)` suffix is rendered dim, so the plain row reads cleaner on top.
34
+ export const FOOTER_PRESETS = ["Pi (system)", "Arnative (Full)"] as const;
34
35
  export type FooterPreset = (typeof FOOTER_PRESETS)[number];
35
36
  export const DEFAULT_FOOTER_PRESET: FooterPreset = "Arnative (Full)";
36
37
  const FOOTER_PRESET_KEY = Symbol.for("pi-arnative.footerPreset");
@@ -47,7 +48,7 @@ export function activeFooterPreset(): FooterPreset {
47
48
  * `disposeActiveSelector()`, which would close the `/arnative` picker mid-preview.
48
49
  */
49
50
  export function applyFooterPreset(ctx: ExtensionContext): void {
50
- if (activeFooterPreset() === "Pi") {
51
+ if (activeFooterPreset() === "Pi (system)") {
51
52
  ctx.ui?.setFooter?.(undefined);
52
53
  return;
53
54
  }
@@ -67,8 +68,8 @@ if (FooterComponent?.prototype?.render && !(globalThis as Record<symbol, boolean
67
68
  (globalThis as Record<symbol, boolean>)[PATCHED_KEY] = true;
68
69
  const origRender = FooterComponent.prototype.render;
69
70
  FooterComponent.prototype.render = function (width: number): string[] {
70
- // "Pi" preset = pi's own footer, so keep its stock line count.
71
- if (activeFooterPreset() === "Pi") return origRender.call(this, width);
71
+ // "Pi (system)" preset = pi's own footer, so keep its stock line count.
72
+ if (activeFooterPreset() === "Pi (system)") return origRender.call(this, width);
72
73
  const lines = origRender.call(this, width);
73
74
  return lines.length > 2 ? lines.slice(0, 2) : lines;
74
75
  };
@@ -893,9 +894,10 @@ if (isMain(import.meta.url)) {
893
894
  assert(git !== null && git.branch.length > 0, "queued git refresh re-ran on the newest cwd (not dropped)");
894
895
  rmSync(noGit, { recursive: true, force: true });
895
896
 
896
- // Footer presets: Arnative (Full) is the default; Pi hands the footer slot back to pi (the editor stays arnative).
897
+ // Footer presets: Arnative (Full) is the default; Pi (system) hands the footer slot back to pi (the editor stays arnative).
897
898
  assert(DEFAULT_FOOTER_PRESET === "Arnative (Full)" && FOOTER_PRESETS.length === 2, "Arnative (Full) is the default footer preset");
898
- assert(markedLabels(FOOTER_PRESETS, "Pi").join(" | ") === " Arnative (Full) | ● Pi", "footer preset labels mark the active one");
899
+ assert(FOOTER_PRESETS.join(" | ") === "Pi (system) | Arnative (Full)", "built-in preset is listed first");
900
+ assert(markedLabels(FOOTER_PRESETS, "Pi (system)").join(" | ") === "● Pi (system) | Arnative (Full)", "footer preset labels mark the active one");
899
901
  let footerCleared = 0;
900
902
  let editorTouched = 0;
901
903
  const presetCtx: any = {
@@ -904,8 +906,8 @@ if (isMain(import.meta.url)) {
904
906
  setEditorComponent: () => { editorTouched++; },
905
907
  },
906
908
  };
907
- setFooterPreset("Pi", presetCtx);
908
- assert(footerCleared === 1 && editorTouched === 0 && activeFooterPreset() === "Pi", "Pi clears the footer only (an editor swap would close the picker)");
909
+ setFooterPreset("Pi (system)", presetCtx);
910
+ assert(footerCleared === 1 && editorTouched === 0 && activeFooterPreset() === "Pi (system)", "Pi (system) clears the footer only (an editor swap would close the picker)");
909
911
  let registered = 0;
910
912
  (globalThis as Record<symbol, (ctx: ExtensionContext) => void>)[FOOTER_REGISTRAR_KEY] = () => { registered++; };
911
913
  setFooterPreset("Arnative (Full)", presetCtx);
@@ -298,9 +298,10 @@ export const tabStore: Map<TabKey, string[]> =
298
298
  (globalThis as Record<symbol, Map<TabKey, string[]>>)[STORE_KEY] = tabStore;
299
299
 
300
300
  // Header preset chosen through `/arnative headers`. "Arnative (Full)" = this extension's
301
- // header; "Pi" = pi's built-in header. The picker lives in arnative.ts, so the choice is
302
- // shared through globalThis like the other cross-module state.
303
- export const HEADER_PRESETS = ["Arnative (Full)", "Pi"] as const;
301
+ // header; "Pi (system)" = pi's built-in header. The picker lives in arnative.ts, so the
302
+ // choice is shared through globalThis like the other cross-module state.
303
+ // Listed built-in-first: the `(Full)` suffix is rendered dim, so the plain row reads cleaner on top.
304
+ export const HEADER_PRESETS = ["Pi (system)", "Arnative (Full)"] as const;
304
305
  export type HeaderPreset = (typeof HEADER_PRESETS)[number];
305
306
  export const DEFAULT_HEADER_PRESET: HeaderPreset = "Arnative (Full)";
306
307
  const HEADER_PRESET_KEY = Symbol.for("pi-arnative.headerPreset");
@@ -310,9 +311,9 @@ export function activeHeaderPreset(): HeaderPreset {
310
311
  return HEADER_PRESETS.includes(stored as HeaderPreset) ? (stored as HeaderPreset) : DEFAULT_HEADER_PRESET;
311
312
  }
312
313
 
313
- /** Hand the header slot to the active preset: arnative's box, or pi's built-in ("Pi"). */
314
+ /** Hand the header slot to the active preset: arnative's box, or pi's built-in ("Pi (system)"). */
314
315
  export function applyHeaderPreset(ctx: ExtensionContext): void {
315
- if (activeHeaderPreset() === "Pi") {
316
+ if (activeHeaderPreset() === "Pi (system)") {
316
317
  ctx.ui?.setHeader?.(undefined);
317
318
  } else {
318
319
  ctx.ui?.setHeader?.((tui, theme) => new ArnativeHeader(tui, theme, ctx));
@@ -642,8 +643,8 @@ if (UserMessageComponent?.prototype && !(globalThis as Record<symbol, boolean>)[
642
643
  if (typeof containerProto?.addChild === "function") {
643
644
  const origAddChild = containerProto.addChild;
644
645
  containerProto.addChild = function (child: any): unknown {
645
- // "Pi" preset = pi's own header, so leave pi's startup list untouched.
646
- if (activeHeaderPreset() === "Pi") return origAddChild.call(this, child);
646
+ // "Pi (system)" preset = pi's own header, so leave pi's startup list untouched.
647
+ if (activeHeaderPreset() === "Pi (system)") return origAddChild.call(this, child);
647
648
  try {
648
649
  const body = sectionBodyOf(child);
649
650
  const sectionName = body === null ? null : sectionNameOf(body);
@@ -730,13 +731,14 @@ if (isMain(import.meta.url)) {
730
731
  assert(sectionNameOf("\x1b[33m[Skills]\x1b[39m\n a, b") === "Skills", "header [Skills] detected");
731
732
  assert(sectionNameOf("pi v0.87.1") === null, "non-section text is not detected");
732
733
 
733
- // Header presets: Arnative (Full) is the default; Pi hands the header slot back to pi.
734
+ // Header presets: Arnative (Full) is the default; Pi (system) hands the header slot back to pi.
734
735
  assert(DEFAULT_HEADER_PRESET === "Arnative (Full)" && HEADER_PRESETS.length === 2, "Arnative (Full) is the default header preset");
735
- assert(markedLabels(HEADER_PRESETS, "Arnative (Full)").join(" | ") === "● Arnative (Full) | Pi", "preset labels mark the active one");
736
+ assert(HEADER_PRESETS.join(" | ") === "Pi (system) | Arnative (Full)", "built-in preset is listed first");
737
+ assert(markedLabels(HEADER_PRESETS, "Arnative (Full)").join(" | ") === " Pi (system) | ● Arnative (Full)", "preset labels mark the active one");
736
738
  let installed: unknown = "untouched";
737
739
  const presetCtx: any = { ui: { setHeader: (factory: unknown) => { installed = factory; } } };
738
- setHeaderPreset("Pi", presetCtx);
739
- assert(installed === undefined && activeHeaderPreset() === "Pi", "Pi clears the custom header");
740
+ setHeaderPreset("Pi (system)", presetCtx);
741
+ assert(installed === undefined && activeHeaderPreset() === "Pi (system)", "Pi (system) clears the custom header");
740
742
  setHeaderPreset("Arnative (Full)", presetCtx);
741
743
  assert(typeof installed === "function" && activeHeaderPreset() === "Arnative (Full)", "Arnative (Full) installs the header factory");
742
744
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bismawy/pi-arnative",
3
- "version": "0.3.0",
4
- "description": "Arnative look for Pi: warm-neutral themes, rounded tool boxes, tabbed header, compact footer, transcript clock, and a /usage dashboard.",
3
+ "version": "0.3.1",
4
+ "description": "Refined aesthetics. Cohesive tools. Built for Pi. Warm themes, rounded tool boxes, tabbed header, compact footer and a /usage dashboard.",
5
5
  "keywords": [
6
6
  "pi-package",
7
7
  "pi-theme",