@eiei114/pi-sub-bar 1.5.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/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@eiei114/pi-sub-bar",
3
+ "version": "1.5.1",
4
+ "description": "Usage widget extension for pi-coding-agent - shows current provider usage above the editor",
5
+ "keywords": [
6
+ "pi-package"
7
+ ],
8
+ "type": "module",
9
+ "license": "MIT",
10
+ "publishConfig": {
11
+ "access": "public",
12
+ "registry": "https://registry.npmjs.org"
13
+ },
14
+ "pi": {
15
+ "extensions": [
16
+ "./index.ts",
17
+ "node_modules/@eiei114/pi-sub-core/index.ts",
18
+ "../pi-sub-core/index.ts"
19
+ ]
20
+ },
21
+ "scripts": {
22
+ "check": "tsc --noEmit",
23
+ "check:watch": "tsc --noEmit --watch",
24
+ "test": "tsx test/all.test.ts",
25
+ "test:watch": "tsx watch test/all.test.ts"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^22.0.0",
29
+ "tsx": "^4.19.2",
30
+ "typescript": "^5.8.0"
31
+ },
32
+ "dependencies": {
33
+ "@eiei114/pi-sub-core": "^1.5.1",
34
+ "@eiei114/pi-sub-shared": "^1.5.1"
35
+ },
36
+ "peerDependencies": {
37
+ "@mariozechner/pi-coding-agent": "*"
38
+ }
39
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Core settings fallbacks for sub-bar when sub-core settings are unavailable.
3
+ */
4
+
5
+ import type { CoreSettings } from "@eiei114/pi-sub-shared";
6
+ import type { Settings } from "./settings-types.js";
7
+ import { PROVIDERS, PROVIDER_METADATA } from "./providers/metadata.js";
8
+
9
+ export function getFallbackCoreSettings(settings: Settings): CoreSettings {
10
+ const providers = {} as CoreSettings["providers"];
11
+ for (const provider of PROVIDERS) {
12
+ providers[provider] = {
13
+ enabled: "auto",
14
+ fetchStatus: Boolean(PROVIDER_METADATA[provider]?.status),
15
+ };
16
+ }
17
+
18
+ return {
19
+ providers,
20
+ behavior: settings.behavior,
21
+ statusRefresh: settings.statusRefresh ?? settings.behavior,
22
+ providerOrder: settings.providerOrder,
23
+ defaultProvider: settings.defaultProvider ?? null,
24
+ };
25
+ }
@@ -0,0 +1,48 @@
1
+ import type { Theme, ThemeColor } from "@mariozechner/pi-coding-agent";
2
+ import { visibleWidth } from "@mariozechner/pi-tui";
3
+ import type { DividerCharacter } from "./settings-types.js";
4
+
5
+ const ANSI_REGEX = /\x1b\[[0-9;]*m/g;
6
+ const SEGMENTER = new Intl.Segmenter(undefined, { granularity: "grapheme" });
7
+
8
+ const DIVIDER_JOIN_MAP: Partial<Record<DividerCharacter, { top: string; bottom: string; line: string }>> = {
9
+ "|": { top: "┬", bottom: "┴", line: "─" },
10
+ "│": { top: "┬", bottom: "┴", line: "─" },
11
+ "┆": { top: "┬", bottom: "┴", line: "─" },
12
+ "┃": { top: "┳", bottom: "┻", line: "━" },
13
+ "┇": { top: "┳", bottom: "┻", line: "━" },
14
+ "║": { top: "╦", bottom: "╩", line: "═" },
15
+ };
16
+
17
+ export function buildDividerLine(
18
+ width: number,
19
+ baseLine: string,
20
+ dividerChar: DividerCharacter,
21
+ joinEnabled: boolean,
22
+ position: "top" | "bottom",
23
+ dividerColor: ThemeColor,
24
+ theme: Theme
25
+ ): string {
26
+ let lineChar = "─";
27
+ let joinChar: string | undefined;
28
+ if (joinEnabled) {
29
+ const joinInfo = DIVIDER_JOIN_MAP[dividerChar];
30
+ if (joinInfo) {
31
+ lineChar = joinInfo.line;
32
+ joinChar = position === "top" ? joinInfo.top : joinInfo.bottom;
33
+ }
34
+ }
35
+ const lineChars = Array.from(lineChar.repeat(Math.max(1, width)));
36
+ if (joinChar) {
37
+ const stripped = baseLine.replace(ANSI_REGEX, "");
38
+ let pos = 0;
39
+ for (const { segment } of SEGMENTER.segment(stripped)) {
40
+ if (pos >= lineChars.length) break;
41
+ if (segment === dividerChar) {
42
+ lineChars[pos] = joinChar;
43
+ }
44
+ pos += Math.max(1, visibleWidth(segment));
45
+ }
46
+ }
47
+ return theme.fg(dividerColor, lineChars.join(""));
48
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Error utilities for the sub-bar extension
3
+ */
4
+
5
+ import type { UsageError, UsageErrorCode } from "./types.js";
6
+
7
+ export function createError(code: UsageErrorCode, message: string, httpStatus?: number): UsageError {
8
+ return { code, message, httpStatus };
9
+ }
10
+
11
+ export function noCredentials(): UsageError {
12
+ return createError("NO_CREDENTIALS", "No credentials found");
13
+ }
14
+
15
+ export function noCli(cliName: string): UsageError {
16
+ return createError("NO_CLI", `${cliName} CLI not found`);
17
+ }
18
+
19
+ export function notLoggedIn(): UsageError {
20
+ return createError("NOT_LOGGED_IN", "Not logged in");
21
+ }
22
+
23
+ export function fetchFailed(reason?: string): UsageError {
24
+ return createError("FETCH_FAILED", reason ?? "Fetch failed");
25
+ }
26
+
27
+ export function httpError(status: number): UsageError {
28
+ return createError("HTTP_ERROR", `HTTP ${status}`, status);
29
+ }
30
+
31
+ export function apiError(message: string): UsageError {
32
+ return createError("API_ERROR", message);
33
+ }
34
+
35
+ export function timeout(): UsageError {
36
+ return createError("TIMEOUT", "Request timed out");
37
+ }
38
+
39
+ /**
40
+ * Check if an error should be considered "no data available" vs actual error
41
+ * These are expected states when provider isn't configured
42
+ */
43
+ export function isExpectedMissingData(error: UsageError): boolean {
44
+ const ignoreCodes = new Set<UsageErrorCode>(["NO_CREDENTIALS", "NO_CLI", "NOT_LOGGED_IN"]);
45
+ return ignoreCodes.has(error.code);
46
+ }
47
+
48
+ /**
49
+ * Format error for display in the usage widget
50
+ */
51
+ export function formatErrorForDisplay(error: UsageError): string {
52
+ switch (error.code) {
53
+ case "NO_CREDENTIALS":
54
+ return "No creds";
55
+ case "NO_CLI":
56
+ return "No CLI";
57
+ case "NOT_LOGGED_IN":
58
+ return "Not logged in";
59
+ case "HTTP_ERROR":
60
+ if (error.httpStatus === 401) {
61
+ return "token no longer valid – please /login again";
62
+ }
63
+ return `${error.httpStatus}`;
64
+ case "FETCH_FAILED":
65
+ case "API_ERROR":
66
+ case "TIMEOUT":
67
+ case "UNKNOWN":
68
+ default:
69
+ return "Fetch failed";
70
+ }
71
+ }