@naarang/glancebar 1.0.1 → 1.0.3
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 +46 -16
- package/package.json +1 -1
- package/src/cli.ts +248 -34
package/README.md
CHANGED
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@naarang/glancebar)
|
|
4
4
|
[](https://github.com/vishal-android-freak/glancebar/blob/main/LICENSE)
|
|
5
5
|
|
|
6
|
-
A customizable statusline for [Claude Code](https://claude.
|
|
6
|
+
A customizable statusline for [Claude Code](https://claude.com/product/claude-code) - display calendar events, tasks, and more at a glance.
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- Water
|
|
14
|
-
-
|
|
15
|
-
-
|
|
10
|
+
- **Session info** - Project name, git branch, model, cost, lines changed, and context usage
|
|
11
|
+
- **Calendar events** - Upcoming events from multiple Google accounts
|
|
12
|
+
- **Meeting warnings** - Red alert when a meeting is 5 minutes away
|
|
13
|
+
- **Health reminders** - Water, stretch, and eye break reminders
|
|
14
|
+
- **Color-coded** - Everything has distinct colors for quick scanning
|
|
15
|
+
- **Fully configurable** via CLI
|
|
16
|
+
- **Cross-platform** support (Windows, macOS, Linux)
|
|
16
17
|
|
|
17
18
|
## Requirements
|
|
18
19
|
|
|
@@ -47,7 +48,7 @@ npm install -g @naarang/glancebar
|
|
|
47
48
|
# 1. Run setup guide
|
|
48
49
|
glancebar setup
|
|
49
50
|
|
|
50
|
-
# 2. Add your Google account
|
|
51
|
+
# 2. Add your Google account (after setting up credentials)
|
|
51
52
|
glancebar auth --add your-email@gmail.com
|
|
52
53
|
|
|
53
54
|
# 3. Test it
|
|
@@ -138,11 +139,10 @@ glancebar config --max-title 80
|
|
|
138
139
|
# Toggle calendar name display
|
|
139
140
|
glancebar config --show-calendar false
|
|
140
141
|
|
|
141
|
-
# Enable/disable
|
|
142
|
+
# Enable/disable health reminders
|
|
142
143
|
glancebar config --water-reminder true
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
glancebar config --water-interval 45
|
|
144
|
+
glancebar config --stretch-reminder true
|
|
145
|
+
glancebar config --eye-reminder true
|
|
146
146
|
|
|
147
147
|
# Reset to defaults
|
|
148
148
|
glancebar config --reset
|
|
@@ -150,15 +150,44 @@ glancebar config --reset
|
|
|
150
150
|
|
|
151
151
|
## Display Format
|
|
152
152
|
|
|
153
|
+
Example output:
|
|
154
|
+
```
|
|
155
|
+
glancebar | main* | Opus | $0.12 | +156 -23 | 9.7k/200k (5%) | In 15m: Team Standup (work)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Session Info (from Claude Code)
|
|
159
|
+
|
|
160
|
+
| Field | Color | Example |
|
|
161
|
+
|-------|-------|---------|
|
|
162
|
+
| Project name | Blue | `glancebar`, `my-app` |
|
|
163
|
+
| Git branch | Magenta | `main`, `feature-x*` (asterisk = uncommitted changes) |
|
|
164
|
+
| Model name | Yellow | `Opus`, `Sonnet` |
|
|
165
|
+
| Cost | Green | `$0.01`, `$0.1234` |
|
|
166
|
+
| Lines changed | Green/Red | `+156 -23` |
|
|
167
|
+
| Context usage | Green/Yellow/Red | `9.7k/200k (5%)` |
|
|
168
|
+
|
|
169
|
+
Context usage color changes based on percentage:
|
|
170
|
+
- **Green**: < 50%
|
|
171
|
+
- **Yellow**: 50-80%
|
|
172
|
+
- **Red**: > 80%
|
|
173
|
+
|
|
174
|
+
### Calendar Events
|
|
175
|
+
|
|
153
176
|
| State | Format | Example |
|
|
154
177
|
|-------|--------|---------|
|
|
178
|
+
| **Meeting warning** | Red alert when ≤5m away | `Meeting in 3m - wrap up!` |
|
|
155
179
|
| Upcoming (within threshold) | `In Xm: Title (account)` | `In 15m: Team Standup (work)` |
|
|
156
180
|
| Current | `Now: Title (account)` | `Now: Team Standup (work)` |
|
|
157
181
|
| Later | `HH:MM AM/PM: Title (account)` | `2:30 PM: Meeting (work)` |
|
|
158
182
|
| No events | `No upcoming events` | |
|
|
159
|
-
| Water reminder | Random hydration message | `Stay hydrated! Drink some water` |
|
|
160
183
|
|
|
161
|
-
|
|
184
|
+
### Health Reminders (~30% chance)
|
|
185
|
+
|
|
186
|
+
| Type | Color | Example |
|
|
187
|
+
|------|-------|---------|
|
|
188
|
+
| Water | Cyan | `Stay hydrated! Drink some water` |
|
|
189
|
+
| Stretch | Green | `Time to stretch! Stand up and move` |
|
|
190
|
+
| Eye break | Magenta | `Eye break! Look 20ft away for 20s` |
|
|
162
191
|
|
|
163
192
|
## Configuration
|
|
164
193
|
|
|
@@ -180,8 +209,9 @@ All configuration is stored in `~/.glancebar/`:
|
|
|
180
209
|
| `countdownThresholdMinutes` | 60 | Minutes threshold for countdown display |
|
|
181
210
|
| `maxTitleLength` | 120 | Maximum event title length |
|
|
182
211
|
| `showCalendarName` | true | Show account name after event |
|
|
183
|
-
| `waterReminderEnabled` |
|
|
184
|
-
| `
|
|
212
|
+
| `waterReminderEnabled` | true | Enable random water break reminders |
|
|
213
|
+
| `stretchReminderEnabled` | true | Enable random stretch/posture reminders |
|
|
214
|
+
| `eyeReminderEnabled` | true | Enable random eye break reminders (20-20-20 rule) |
|
|
185
215
|
|
|
186
216
|
## Building from Source
|
|
187
217
|
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -16,7 +16,8 @@ interface Config {
|
|
|
16
16
|
countdownThresholdMinutes: number;
|
|
17
17
|
maxTitleLength: number;
|
|
18
18
|
waterReminderEnabled: boolean;
|
|
19
|
-
|
|
19
|
+
stretchReminderEnabled: boolean;
|
|
20
|
+
eyeReminderEnabled: boolean;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
const COLORS: Record<string, string> = {
|
|
@@ -48,7 +49,8 @@ const DEFAULT_CONFIG: Config = {
|
|
|
48
49
|
countdownThresholdMinutes: 60,
|
|
49
50
|
maxTitleLength: 120,
|
|
50
51
|
waterReminderEnabled: true,
|
|
51
|
-
|
|
52
|
+
stretchReminderEnabled: true,
|
|
53
|
+
eyeReminderEnabled: true,
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
const WATER_REMINDERS = [
|
|
@@ -62,6 +64,26 @@ const WATER_REMINDERS = [
|
|
|
62
64
|
"Quick reminder: Drink water!",
|
|
63
65
|
];
|
|
64
66
|
|
|
67
|
+
const STRETCH_REMINDERS = [
|
|
68
|
+
"Time to stretch! Stand up and move",
|
|
69
|
+
"Stretch break! Roll your shoulders",
|
|
70
|
+
"Stand up and stretch your legs",
|
|
71
|
+
"Posture check! Sit up straight",
|
|
72
|
+
"Take a quick stretch break",
|
|
73
|
+
"Move your body! Quick stretch",
|
|
74
|
+
"Stretch your neck and shoulders",
|
|
75
|
+
"Stand up! Your body will thank you",
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const EYE_REMINDERS = [
|
|
79
|
+
"Eye break! Look 20ft away for 20s",
|
|
80
|
+
"Rest your eyes - look at something distant",
|
|
81
|
+
"20-20-20: Look away from screen",
|
|
82
|
+
"Give your eyes a break!",
|
|
83
|
+
"Look away from the screen for a moment",
|
|
84
|
+
"Eye rest time! Focus on something far",
|
|
85
|
+
];
|
|
86
|
+
|
|
65
87
|
function getConfigDir(): string {
|
|
66
88
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
67
89
|
return join(home, ".glancebar");
|
|
@@ -75,10 +97,6 @@ function getTokensDir(): string {
|
|
|
75
97
|
return join(getConfigDir(), "tokens");
|
|
76
98
|
}
|
|
77
99
|
|
|
78
|
-
function getCredentialsPath(): string {
|
|
79
|
-
return join(getConfigDir(), "credentials.json");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
100
|
function ensureConfigDir(): void {
|
|
83
101
|
const dir = getConfigDir();
|
|
84
102
|
if (!existsSync(dir)) {
|
|
@@ -118,11 +136,15 @@ interface Credentials {
|
|
|
118
136
|
web?: { client_id: string; client_secret: string };
|
|
119
137
|
}
|
|
120
138
|
|
|
139
|
+
function getCredentialsPath(): string {
|
|
140
|
+
return join(getConfigDir(), "credentials.json");
|
|
141
|
+
}
|
|
142
|
+
|
|
121
143
|
function loadCredentials(): Credentials {
|
|
122
144
|
const credPath = getCredentialsPath();
|
|
123
145
|
if (!existsSync(credPath)) {
|
|
124
146
|
throw new Error(
|
|
125
|
-
`credentials.json not found at ${credPath}\n\nPlease download OAuth credentials from Google Cloud Console and save to:\n${credPath}
|
|
147
|
+
`credentials.json not found at ${credPath}\n\nPlease download OAuth credentials from Google Cloud Console and save to:\n${credPath}\n\nRun 'glancebar setup' for detailed instructions.`
|
|
126
148
|
);
|
|
127
149
|
}
|
|
128
150
|
return JSON.parse(readFileSync(credPath, "utf-8"));
|
|
@@ -412,6 +434,20 @@ function formatCountdown(minutes: number): string {
|
|
|
412
434
|
return mins === 0 ? `In ${hours}h` : `In ${hours}h${mins}m`;
|
|
413
435
|
}
|
|
414
436
|
|
|
437
|
+
function getMeetingWarning(event: CalendarEvent | null): string | null {
|
|
438
|
+
if (!event) return null;
|
|
439
|
+
|
|
440
|
+
const now = new Date();
|
|
441
|
+
const minutesUntil = Math.round((event.start.getTime() - now.getTime()) / 60000);
|
|
442
|
+
|
|
443
|
+
// Warning when meeting is 5 minutes or less away
|
|
444
|
+
if (minutesUntil > 0 && minutesUntil <= 5) {
|
|
445
|
+
return `${COLORS.brightRed}Meeting in ${minutesUntil}m - wrap up!${COLORS.reset}`;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
|
|
415
451
|
function formatTime(date: Date): string {
|
|
416
452
|
const hours = date.getHours();
|
|
417
453
|
const minutes = date.getMinutes();
|
|
@@ -442,14 +478,15 @@ Usage:
|
|
|
442
478
|
glancebar config --max-title <length> Set max title length (default: 120)
|
|
443
479
|
glancebar config --show-calendar <true|false> Show calendar name (default: true)
|
|
444
480
|
glancebar config --water-reminder <true|false> Enable/disable water reminders (default: true)
|
|
445
|
-
glancebar config --
|
|
481
|
+
glancebar config --stretch-reminder <true|false> Enable/disable stretch reminders (default: true)
|
|
482
|
+
glancebar config --eye-reminder <true|false> Enable/disable eye break reminders (default: true)
|
|
446
483
|
glancebar config --reset Reset to default configuration
|
|
447
484
|
glancebar setup Show setup instructions
|
|
448
485
|
|
|
449
486
|
Examples:
|
|
450
487
|
glancebar auth --add user@gmail.com
|
|
451
488
|
glancebar config --lookahead 12
|
|
452
|
-
glancebar config --
|
|
489
|
+
glancebar config --stretch-reminder false
|
|
453
490
|
|
|
454
491
|
Config location: ${getConfigDir()}
|
|
455
492
|
`);
|
|
@@ -684,17 +721,31 @@ function handleConfig(args: string[]) {
|
|
|
684
721
|
return;
|
|
685
722
|
}
|
|
686
723
|
|
|
687
|
-
// Handle --
|
|
688
|
-
const
|
|
689
|
-
if (
|
|
690
|
-
const value =
|
|
691
|
-
if (
|
|
692
|
-
console.error("Error:
|
|
724
|
+
// Handle --stretch-reminder
|
|
725
|
+
const stretchReminderIndex = args.indexOf("--stretch-reminder");
|
|
726
|
+
if (stretchReminderIndex !== -1) {
|
|
727
|
+
const value = args[stretchReminderIndex + 1]?.toLowerCase();
|
|
728
|
+
if (value !== "true" && value !== "false") {
|
|
729
|
+
console.error("Error: --stretch-reminder must be 'true' or 'false'");
|
|
730
|
+
process.exit(1);
|
|
731
|
+
}
|
|
732
|
+
config.stretchReminderEnabled = value === "true";
|
|
733
|
+
saveConfig(config);
|
|
734
|
+
console.log(`Stretch reminder ${value === "true" ? "enabled" : "disabled"}`);
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Handle --eye-reminder
|
|
739
|
+
const eyeReminderIndex = args.indexOf("--eye-reminder");
|
|
740
|
+
if (eyeReminderIndex !== -1) {
|
|
741
|
+
const value = args[eyeReminderIndex + 1]?.toLowerCase();
|
|
742
|
+
if (value !== "true" && value !== "false") {
|
|
743
|
+
console.error("Error: --eye-reminder must be 'true' or 'false'");
|
|
693
744
|
process.exit(1);
|
|
694
745
|
}
|
|
695
|
-
config.
|
|
746
|
+
config.eyeReminderEnabled = value === "true";
|
|
696
747
|
saveConfig(config);
|
|
697
|
-
console.log(`
|
|
748
|
+
console.log(`Eye break reminder ${value === "true" ? "enabled" : "disabled"}`);
|
|
698
749
|
return;
|
|
699
750
|
}
|
|
700
751
|
|
|
@@ -713,38 +764,195 @@ Calendar Settings:
|
|
|
713
764
|
|
|
714
765
|
Reminders:
|
|
715
766
|
Water reminder: ${config.waterReminderEnabled ? "enabled" : "disabled"}
|
|
716
|
-
|
|
767
|
+
Stretch reminder: ${config.stretchReminderEnabled ? "enabled" : "disabled"}
|
|
768
|
+
Eye break reminder: ${config.eyeReminderEnabled ? "enabled" : "disabled"}
|
|
717
769
|
`);
|
|
718
770
|
}
|
|
719
771
|
|
|
720
|
-
function
|
|
721
|
-
|
|
772
|
+
function getRandomReminder(config: Config): string | null {
|
|
773
|
+
const enabledReminders: Array<() => string> = [];
|
|
722
774
|
|
|
723
|
-
|
|
724
|
-
|
|
775
|
+
if (config.waterReminderEnabled) {
|
|
776
|
+
enabledReminders.push(() => {
|
|
777
|
+
const reminder = WATER_REMINDERS[Math.floor(Math.random() * WATER_REMINDERS.length)];
|
|
778
|
+
return `${COLORS.brightCyan}${reminder}${COLORS.reset}`;
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (config.stretchReminderEnabled) {
|
|
783
|
+
enabledReminders.push(() => {
|
|
784
|
+
const reminder = STRETCH_REMINDERS[Math.floor(Math.random() * STRETCH_REMINDERS.length)];
|
|
785
|
+
return `${COLORS.brightGreen}${reminder}${COLORS.reset}`;
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (config.eyeReminderEnabled) {
|
|
790
|
+
enabledReminders.push(() => {
|
|
791
|
+
const reminder = EYE_REMINDERS[Math.floor(Math.random() * EYE_REMINDERS.length)];
|
|
792
|
+
return `${COLORS.brightMagenta}${reminder}${COLORS.reset}`;
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (enabledReminders.length === 0) return null;
|
|
725
797
|
|
|
726
|
-
//
|
|
727
|
-
|
|
798
|
+
// ~30% chance to show any reminder
|
|
799
|
+
if (Math.random() >= 0.3) return null;
|
|
800
|
+
|
|
801
|
+
// Pick a random reminder type from enabled ones
|
|
802
|
+
const randomPicker = enabledReminders[Math.floor(Math.random() * enabledReminders.length)];
|
|
803
|
+
return randomPicker();
|
|
728
804
|
}
|
|
729
805
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
806
|
+
interface ClaudeCodeStatus {
|
|
807
|
+
model?: { display_name?: string };
|
|
808
|
+
cost?: {
|
|
809
|
+
total_cost_usd?: number;
|
|
810
|
+
total_lines_added?: number;
|
|
811
|
+
total_lines_removed?: number;
|
|
812
|
+
};
|
|
813
|
+
cwd?: string;
|
|
814
|
+
workspace?: {
|
|
815
|
+
project_dir?: string;
|
|
816
|
+
};
|
|
817
|
+
context_window?: {
|
|
818
|
+
context_window_size?: number;
|
|
819
|
+
current_usage?: {
|
|
820
|
+
input_tokens?: number;
|
|
821
|
+
output_tokens?: number;
|
|
822
|
+
cache_creation_input_tokens?: number;
|
|
823
|
+
cache_read_input_tokens?: number;
|
|
824
|
+
};
|
|
825
|
+
};
|
|
733
826
|
}
|
|
734
827
|
|
|
735
|
-
|
|
736
|
-
// Consume stdin (Claude Code sends JSON)
|
|
828
|
+
function getGitBranch(cwd?: string): string | null {
|
|
737
829
|
try {
|
|
738
|
-
|
|
739
|
-
|
|
830
|
+
const { execSync } = require("child_process");
|
|
831
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
832
|
+
cwd: cwd || process.cwd(),
|
|
833
|
+
encoding: "utf-8",
|
|
834
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
835
|
+
}).trim();
|
|
836
|
+
|
|
837
|
+
// Check if there are uncommitted changes
|
|
838
|
+
let isDirty = false;
|
|
839
|
+
try {
|
|
840
|
+
const status = execSync("git status --porcelain", {
|
|
841
|
+
cwd: cwd || process.cwd(),
|
|
842
|
+
encoding: "utf-8",
|
|
843
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
844
|
+
}).trim();
|
|
845
|
+
isDirty = status.length > 0;
|
|
846
|
+
} catch {}
|
|
847
|
+
|
|
848
|
+
return isDirty ? `${branch}*` : branch;
|
|
849
|
+
} catch {
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function getProjectName(projectDir?: string): string | null {
|
|
855
|
+
if (!projectDir) return null;
|
|
856
|
+
// Extract the last part of the path as project name
|
|
857
|
+
const parts = projectDir.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
858
|
+
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
async function readStdinJson(): Promise<ClaudeCodeStatus | null> {
|
|
862
|
+
try {
|
|
863
|
+
const chunks: Uint8Array[] = [];
|
|
864
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
865
|
+
chunks.push(chunk);
|
|
866
|
+
}
|
|
867
|
+
if (chunks.length === 0) return null;
|
|
868
|
+
const text = Buffer.concat(chunks).toString("utf-8").trim();
|
|
869
|
+
if (!text) return null;
|
|
870
|
+
return JSON.parse(text);
|
|
871
|
+
} catch {
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function formatSessionInfo(status: ClaudeCodeStatus): string {
|
|
877
|
+
const parts: string[] = [];
|
|
878
|
+
|
|
879
|
+
// Project name
|
|
880
|
+
const projectName = getProjectName(status.workspace?.project_dir);
|
|
881
|
+
if (projectName) {
|
|
882
|
+
parts.push(`${COLORS.brightBlue}${projectName}${COLORS.reset}`);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Git branch
|
|
886
|
+
const gitBranch = getGitBranch(status.cwd || status.workspace?.project_dir);
|
|
887
|
+
if (gitBranch) {
|
|
888
|
+
parts.push(`${COLORS.magenta}${gitBranch}${COLORS.reset}`);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Model name
|
|
892
|
+
if (status.model?.display_name) {
|
|
893
|
+
parts.push(`${COLORS.brightYellow}${status.model.display_name}${COLORS.reset}`);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Cost
|
|
897
|
+
if (status.cost?.total_cost_usd !== undefined) {
|
|
898
|
+
const cost = status.cost.total_cost_usd;
|
|
899
|
+
const costStr = cost < 0.01 ? `$${cost.toFixed(4)}` : `$${cost.toFixed(2)}`;
|
|
900
|
+
parts.push(`${COLORS.green}${costStr}${COLORS.reset}`);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// Lines changed
|
|
904
|
+
const linesAdded = status.cost?.total_lines_added || 0;
|
|
905
|
+
const linesRemoved = status.cost?.total_lines_removed || 0;
|
|
906
|
+
if (linesAdded > 0 || linesRemoved > 0) {
|
|
907
|
+
const linesStr = `${COLORS.green}+${linesAdded}${COLORS.reset} ${COLORS.red}-${linesRemoved}${COLORS.reset}`;
|
|
908
|
+
parts.push(linesStr);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Context usage (using current_usage for accurate context window state)
|
|
912
|
+
if (status.context_window?.current_usage && status.context_window?.context_window_size) {
|
|
913
|
+
const usage = status.context_window.current_usage;
|
|
914
|
+
// Sum all token types for total context usage
|
|
915
|
+
const totalUsed =
|
|
916
|
+
(usage.input_tokens || 0) +
|
|
917
|
+
(usage.output_tokens || 0) +
|
|
918
|
+
(usage.cache_creation_input_tokens || 0) +
|
|
919
|
+
(usage.cache_read_input_tokens || 0);
|
|
920
|
+
const windowSize = status.context_window.context_window_size;
|
|
921
|
+
const percentage = Math.round((totalUsed / windowSize) * 100);
|
|
922
|
+
const usedK = (totalUsed / 1000).toFixed(1);
|
|
923
|
+
const windowK = Math.round(windowSize / 1000);
|
|
924
|
+
|
|
925
|
+
// Color based on usage: green < 50%, yellow 50-80%, red > 80%
|
|
926
|
+
let color = COLORS.green;
|
|
927
|
+
if (percentage >= 80) color = COLORS.red;
|
|
928
|
+
else if (percentage >= 50) color = COLORS.yellow;
|
|
929
|
+
|
|
930
|
+
parts.push(`${color}${usedK}k/${windowK}k (${percentage}%)${COLORS.reset}`);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
return parts.join(" | ");
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
async function outputStatusline() {
|
|
937
|
+
// Read and parse stdin from Claude Code
|
|
938
|
+
const status = await readStdinJson();
|
|
740
939
|
|
|
741
940
|
try {
|
|
742
941
|
const config = loadConfig();
|
|
743
942
|
const parts: string[] = [];
|
|
744
943
|
|
|
745
|
-
//
|
|
746
|
-
if (
|
|
747
|
-
|
|
944
|
+
// Add session info from Claude Code
|
|
945
|
+
if (status) {
|
|
946
|
+
const sessionInfo = formatSessionInfo(status);
|
|
947
|
+
if (sessionInfo) {
|
|
948
|
+
parts.push(sessionInfo);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Check for health reminder (water, stretch, eye break)
|
|
953
|
+
const reminder = getRandomReminder(config);
|
|
954
|
+
if (reminder) {
|
|
955
|
+
parts.push(reminder);
|
|
748
956
|
}
|
|
749
957
|
|
|
750
958
|
// Get calendar events
|
|
@@ -752,6 +960,12 @@ async function outputStatusline() {
|
|
|
752
960
|
const events = await getUpcomingEvents(config);
|
|
753
961
|
const event = getCurrentOrNextEvent(events);
|
|
754
962
|
|
|
963
|
+
// Check for meeting warning (within 5 minutes)
|
|
964
|
+
const meetingWarning = getMeetingWarning(event);
|
|
965
|
+
if (meetingWarning) {
|
|
966
|
+
parts.push(meetingWarning);
|
|
967
|
+
}
|
|
968
|
+
|
|
755
969
|
if (event) {
|
|
756
970
|
parts.push(formatEvent(event, config));
|
|
757
971
|
} else if (parts.length === 0) {
|