pi-todo 1.0.0 โ†’ 1.1.0

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.
Files changed (3) hide show
  1. package/README.md +18 -6
  2. package/package.json +1 -1
  3. package/src/render.ts +75 -3
package/README.md CHANGED
@@ -96,18 +96,30 @@ Tasks are stored in `.pi/todo.json` in the project root:
96
96
 
97
97
  ## Widget
98
98
 
99
- The live widget appears above the editor and shows:
99
+ The live widget appears above the editor with a green border and shows:
100
100
 
101
101
  ```
102
- ๐Ÿ“‹ TODO ยท 3 tasks
103
- 2 pending ยท 1 active
104
- โšช T-001 [HIGH] Build StorefrontController ยท Ultron
105
- โšช T-002 [MED] Port landing.html ยท Maya
106
- ๐Ÿ”ต T-003 [LOW] Verify storefront routes ยท Quinn
102
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
103
+ โ”‚ ๐Ÿ“‹ TODO ยท 3 tasks โ”‚
104
+ โ”‚ 2 pending ยท 1 active โ”‚
105
+ โ”‚ โšช T-001 [HIGH] Build StorefrontController ยท Ultron โ”‚
106
+ โ”‚ โšช T-002 [MED] Port landing.html ยท Maya โ”‚
107
+ โ”‚ ๐Ÿ”ต T-003 [LOW] Verify storefront routes ยท Quinn โ”‚
108
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
107
109
  ```
108
110
 
109
111
  Status icons: โšช pending, ๐Ÿ”ต in-progress, โœ… done, ๐Ÿ”ด blocked
110
112
 
113
+ ## Changelog
114
+
115
+ ### v1.1.0
116
+
117
+ - **Bordered widget** โ€” The live widget now renders inside a green Unicode box border (uses `theme.fg("success")`) for better visual separation from the chat area.
118
+
119
+ ### v1.0.0
120
+
121
+ - Initial release: todo tool, live widget, slash commands, per-project persistence.
122
+
111
123
  ## Design Decisions
112
124
 
113
125
  - **Per-project, not global** โ€” Each project has its own todo list
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-todo",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Persistent per-project todo tracker with live TUI widget for Pi",
5
5
  "author": "mrg2400xx",
6
6
  "license": "MIT",
package/src/render.ts CHANGED
@@ -8,6 +8,78 @@ import type { TodoItem, TodoStatus, TodoPriority } from "./store.ts";
8
8
 
9
9
  type Theme = ExtensionContext["ui"]["theme"];
10
10
 
11
+ /**
12
+ * BorderedBox โ€” a container that draws a Unicode box border around its children.
13
+ * The border color is controlled by the `colorFn` argument (e.g. theme.fg("success", ...)).
14
+ */
15
+ class BorderedBox implements Component {
16
+ private children: Component[] = [];
17
+ private colorFn: (text: string) => string;
18
+ private cachedLines: string[] | null = null;
19
+ private cachedWidth: number | null = null;
20
+
21
+ constructor(colorFn: (text: string) => string) {
22
+ this.colorFn = colorFn;
23
+ }
24
+
25
+ addChild(component: Component): void {
26
+ this.children.push(component);
27
+ this.invalidate();
28
+ }
29
+
30
+ invalidate(): void {
31
+ this.cachedLines = null;
32
+ this.cachedWidth = null;
33
+ for (const child of this.children) {
34
+ child.invalidate?.();
35
+ }
36
+ }
37
+
38
+ render(width: number): string[] {
39
+ // Cache hit
40
+ if (this.cachedLines && this.cachedWidth === width) {
41
+ return this.cachedLines;
42
+ }
43
+
44
+ // Inner width: total - 2 (left/right border chars)
45
+ const innerWidth = Math.max(0, width - 2);
46
+
47
+ // Render all children into flat line array
48
+ const innerLines: string[] = [];
49
+ for (const child of this.children) {
50
+ const rendered = child.render(innerWidth);
51
+ for (const line of rendered) {
52
+ innerLines.push(line);
53
+ }
54
+ }
55
+
56
+ const c = this.colorFn;
57
+ const topLeft = c("โ”Œ");
58
+ const topRight = c("โ”");
59
+ const bottomLeft = c("โ””");
60
+ const bottomRight = c("โ”˜");
61
+ const horizontal = c("โ”€");
62
+ const vertical = c("โ”‚");
63
+
64
+ const lines: string[] = [];
65
+
66
+ // Top border
67
+ lines.push(`${topLeft}${horizontal.repeat(innerWidth)}${topRight}`);
68
+
69
+ // Middle rows
70
+ for (const inner of innerLines) {
71
+ lines.push(`${vertical}${inner}${vertical}`);
72
+ }
73
+
74
+ // Bottom border
75
+ lines.push(`${bottomLeft}${horizontal.repeat(innerWidth)}${bottomRight}`);
76
+
77
+ this.cachedLines = lines;
78
+ this.cachedWidth = width;
79
+ return lines;
80
+ }
81
+ }
82
+
11
83
  const STATUS_ICON: Record<TodoStatus, string> = {
12
84
  pending: "โšช",
13
85
  "in-progress": "๐Ÿ”ต",
@@ -83,11 +155,11 @@ export function createWidgetComponent(items: TodoItem[]): (tui: unknown, theme:
83
155
  return (_tui: unknown, theme: Theme) => {
84
156
  const width = process.stdout.columns || 120;
85
157
  const lines = buildWidgetLines(items, theme, width);
86
- const container = new Container();
158
+ const box = new BorderedBox((text: string) => theme.fg("success", text));
87
159
  for (const line of lines) {
88
- container.addChild(new Text(line, 1, 0));
160
+ box.addChild(new Text(line, 1, 0));
89
161
  }
90
- return container;
162
+ return box;
91
163
  };
92
164
  }
93
165