pi-one-ui 0.2.0 → 0.2.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.
@@ -1,4 +1,5 @@
1
1
  import {
2
+ type ExtensionCommandContext,
2
3
  type ExtensionContext,
3
4
  getSettingsListTheme,
4
5
  } from "@earendil-works/pi-coding-agent";
@@ -84,6 +85,26 @@ function renderTabs(theme: any, active: number, width: number): string {
84
85
  return truncateToWidth(text, Math.max(0, width));
85
86
  }
86
87
 
88
+ /** Adds a stable chrome frame around the settings content. */
89
+ function renderPanelFrame(theme: any, width: number, rows: string[]): string[] {
90
+ const safeWidth = Math.max(1, width);
91
+ const border = (text: string) => theme.fg("border", text);
92
+ if (safeWidth === 1)
93
+ return [border("│"), ...rows.map(() => border("│")), border("│")];
94
+
95
+ const innerWidth = safeWidth - 2;
96
+ const content = rows.map(
97
+ (row) =>
98
+ `${border("│")}${truncateToWidth(row, innerWidth, "", true)}${border("│")}`,
99
+ );
100
+ if (safeWidth === 2) return [border("╭╮"), ...content, border("╰╯")];
101
+ return [
102
+ border(`╭${"─".repeat(innerWidth)}╮`),
103
+ ...content,
104
+ border(`╰${"─".repeat(innerWidth)}╯`),
105
+ ];
106
+ }
107
+
87
108
  function sectionDescription(section: SectionId): string {
88
109
  switch (section) {
89
110
  case "header":
@@ -554,7 +575,7 @@ let unifiedPanelOpen = false;
554
575
  * @param deps Runtime controllers used for live updates.
555
576
  */
556
577
  export async function showOneUiPanel(
557
- ctx: any,
578
+ ctx: ExtensionCommandContext,
558
579
  deps: UnifiedPanelDeps = {},
559
580
  ): Promise<void> {
560
581
  if (
@@ -572,9 +593,10 @@ export async function showOneUiPanel(
572
593
 
573
594
  unifiedPanelOpen = true;
574
595
  try {
596
+ let panelHandle: { focus: () => void } | undefined;
575
597
  await overlayManager.run(() =>
576
598
  ctx.ui.custom(
577
- (tui: any, theme: any, _keybindings: any, done: () => void) => {
599
+ (tui, theme, _keybindings, done) => {
578
600
  let activeIndex = 0;
579
601
  let list: SettingsList;
580
602
  const createList = () => {
@@ -583,11 +605,29 @@ export async function showOneUiPanel(
583
605
  10,
584
606
  getSettingsListTheme(),
585
607
  (id, value) => {
608
+ // Replacing Pi's editor synchronously moves focus away from
609
+ // this overlay. Restore the overlay handle after the editor
610
+ // has been reconciled so the panel remains interactive.
611
+ if (id === "editorEnabled") {
612
+ try {
613
+ updateSetting(id, value, ctx, deps);
614
+ list.updateValue(id, value);
615
+ } catch (error) {
616
+ ctx.ui?.notify?.(
617
+ `Could not update Editor setting: ${error instanceof Error ? error.message : String(error)}`,
618
+ "error",
619
+ );
620
+ } finally {
621
+ panelHandle?.focus();
622
+ tui.requestRender();
623
+ }
624
+ return;
625
+ }
586
626
  updateSetting(id, value, ctx, deps);
587
627
  list.updateValue(id, value);
588
628
  tui.requestRender();
589
629
  },
590
- done,
630
+ () => done(undefined),
591
631
  );
592
632
  };
593
633
  createList();
@@ -600,29 +640,32 @@ export async function showOneUiPanel(
600
640
  };
601
641
 
602
642
  return {
603
- render: (width: number) => [
604
- renderTabs(theme, activeIndex, width),
605
- theme.fg("dim", "─".repeat(Math.max(0, width))),
606
- theme.fg(
607
- "muted",
608
- ` ${sectionDescription(SECTIONS[activeIndex].id)}`,
609
- ),
610
- "",
611
- ...list.render(width),
612
- ...(SECTIONS[activeIndex].id === "editor"
613
- ? renderEditorPreview(theme, width)
614
- : SECTIONS[activeIndex].id === "context"
615
- ? renderContextPreview(theme, width)
616
- : []),
617
- "",
618
- truncateToWidth(
643
+ render: (width: number) => {
644
+ const contentWidth = Math.max(0, width - 2);
645
+ return renderPanelFrame(theme, width, [
646
+ renderTabs(theme, activeIndex, contentWidth),
647
+ theme.fg("dim", "─".repeat(contentWidth)),
619
648
  theme.fg(
620
- "dim",
621
- " Tab/Shift+Tab switch sections · Enter/Space change · Esc close",
649
+ "muted",
650
+ ` ${sectionDescription(SECTIONS[activeIndex].id)}`,
651
+ ),
652
+ "",
653
+ ...list.render(contentWidth),
654
+ ...(SECTIONS[activeIndex].id === "editor"
655
+ ? renderEditorPreview(theme, contentWidth)
656
+ : SECTIONS[activeIndex].id === "context"
657
+ ? renderContextPreview(theme, contentWidth)
658
+ : []),
659
+ "",
660
+ truncateToWidth(
661
+ theme.fg(
662
+ "dim",
663
+ " Tab/Shift+Tab switch sections · Enter/Space change · Esc close",
664
+ ),
665
+ contentWidth,
622
666
  ),
623
- width,
624
- ),
625
- ],
667
+ ]);
668
+ },
626
669
  invalidate: () => list.invalidate(),
627
670
  handleInput: (data: string) => {
628
671
  if (data === "\x1b[Z" || matchesKey(data, "shift+tab")) {
@@ -640,10 +683,18 @@ export async function showOneUiPanel(
640
683
  {
641
684
  overlay: true,
642
685
  overlayOptions: {
643
- anchor: "center",
686
+ anchor: "top-center",
644
687
  width: "85%",
645
688
  maxHeight: "90%",
646
- margin: 1,
689
+ margin: {
690
+ top: 10,
691
+ right: 1,
692
+ bottom: 1,
693
+ left: 1,
694
+ },
695
+ },
696
+ onHandle: (handle: { focus: () => void }) => {
697
+ panelHandle = handle;
647
698
  },
648
699
  },
649
700
  ),
package/package.json CHANGED
@@ -1,9 +1,17 @@
1
1
  {
2
2
  "name": "pi-one-ui",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "A single, customizable Pi UI package with layout-oriented Context and Shell rendering for Pi.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/kerolt/pi-one-ui"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/kerolt/pi-one-ui/issues"
13
+ },
14
+ "homepage": "https://github.com/kerolt/pi-one-ui#readme",
7
15
  "files": [
8
16
  "extensions",
9
17
  "themes",