pi-powerline 0.5.1 → 0.6.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/footer.ts +7 -50
- package/header.ts +204 -31
- package/index.ts +22 -47
- package/package.json +1 -1
- package/settings.ts +4 -11
package/footer.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { join } from 'node:path';
|
|
|
16
16
|
import type { AssistantMessage } from '@earendil-works/pi-ai';
|
|
17
17
|
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
|
|
18
18
|
import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
|
|
19
|
+
import { hasNerdFonts, hexFg, withIcon } from './breadcrumb.ts';
|
|
19
20
|
import { readPowerlineSettings } from './settings.ts';
|
|
20
21
|
|
|
21
22
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -54,24 +55,12 @@ function formatTokens(count: number): string {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
57
|
-
// think level display
|
|
58
|
+
// think level display
|
|
58
59
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
59
60
|
|
|
60
|
-
function hasNerdFonts(): boolean {
|
|
61
|
-
if (process.env.POWERLINE_NERD_FONTS === '1') return true;
|
|
62
|
-
if (process.env.POWERLINE_NERD_FONTS === '0') return false;
|
|
63
|
-
if (process.env.GHOSTTY_RESOURCES_DIR) return true;
|
|
64
|
-
const term = (process.env.TERM_PROGRAM || '').toLowerCase();
|
|
65
|
-
return ['iterm', 'wezterm', 'kitty', 'ghostty', 'alacritty'].some((t) => term.includes(t));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
61
|
const ICON_THINK = hasNerdFonts() ? '' : '';
|
|
69
62
|
const ICON_GIT = hasNerdFonts() ? '' : '⎇';
|
|
70
63
|
|
|
71
|
-
function withIcon(icon: string, text: string): string {
|
|
72
|
-
return icon ? `${icon} ${text}` : text;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
64
|
const THINK_LABELS: Record<string, string> = {
|
|
76
65
|
minimal: 'min',
|
|
77
66
|
low: 'low',
|
|
@@ -127,15 +116,6 @@ let autoCompactEnabled = true;
|
|
|
127
116
|
// footer renderer
|
|
128
117
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
129
118
|
|
|
130
|
-
// hex → ANSI true color (for git branch, not using pi theme tokens)
|
|
131
|
-
function hexFg(hex: string, text: string): string {
|
|
132
|
-
const h = hex.replace('#', '');
|
|
133
|
-
const r = parseInt(h.slice(0, 2), 16);
|
|
134
|
-
const g = parseInt(h.slice(2, 4), 16);
|
|
135
|
-
const b = parseInt(h.slice(4, 6), 16);
|
|
136
|
-
return `\x1b[38;2;${r};${g};${b}m${text}`;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
119
|
/** Sanitize text for single-line status display. */
|
|
140
120
|
function sanitizeStatusText(text: string): string {
|
|
141
121
|
return text
|
|
@@ -256,48 +236,25 @@ function createFooterRenderer(ctx: ExtensionContext) {
|
|
|
256
236
|
const rightWidth = visibleWidth(rightSidePlain);
|
|
257
237
|
|
|
258
238
|
const minPad = 2;
|
|
239
|
+
const thinkToken = THINK_COLORS[liveThinkLevel || 'off'] ?? 'thinkingOff';
|
|
240
|
+
const coloredRight = rightSidePlain ? theme.fg(thinkToken, rightSidePlain) : '';
|
|
259
241
|
let statsLine: string;
|
|
260
242
|
|
|
261
243
|
const totalBase = gitFullWidth + statsLeftWidth + minPad + rightWidth;
|
|
262
244
|
if (totalBase <= width) {
|
|
263
|
-
// everything fits
|
|
264
245
|
const pad = width - gitFullWidth - statsLeftWidth - rightWidth;
|
|
265
246
|
const dimPadding = pad > 0 ? theme.fg('dim', ' '.repeat(pad)) : '';
|
|
266
|
-
let coloredRight = '';
|
|
267
|
-
if (rightSidePlain) {
|
|
268
|
-
const tl = liveThinkLevel || 'off';
|
|
269
|
-
coloredRight = theme.fg(THINK_COLORS[tl] ?? 'thinkingOff', rightSidePlain);
|
|
270
|
-
}
|
|
271
247
|
statsLine = gitFull + dimLeft + dimPadding + coloredRight;
|
|
272
248
|
} else if (gitFullWidth + minPad + rightWidth <= width) {
|
|
273
|
-
// drop git → fit statsLeft
|
|
274
249
|
const availStats = width - gitFullWidth - minPad - rightWidth;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (availStats > 0) {
|
|
278
|
-
statsTrimmed = truncateToWidth(statsLeft, availStats, '');
|
|
279
|
-
statsTrimmedWidth = visibleWidth(statsTrimmed);
|
|
280
|
-
} else {
|
|
281
|
-
statsTrimmed = '';
|
|
282
|
-
statsTrimmedWidth = 0;
|
|
283
|
-
}
|
|
250
|
+
const statsTrimmed = availStats > 0 ? truncateToWidth(statsLeft, availStats, '') : '';
|
|
251
|
+
const statsTrimmedWidth = visibleWidth(statsTrimmed);
|
|
284
252
|
const pad = width - gitFullWidth - statsTrimmedWidth - rightWidth;
|
|
285
253
|
const dimPadding = pad > 0 ? theme.fg('dim', ' '.repeat(pad)) : '';
|
|
286
|
-
let coloredRight = '';
|
|
287
|
-
if (rightSidePlain) {
|
|
288
|
-
const tl = liveThinkLevel || 'off';
|
|
289
|
-
coloredRight = theme.fg(THINK_COLORS[tl] ?? 'thinkingOff', rightSidePlain);
|
|
290
|
-
}
|
|
291
254
|
statsLine = gitFull + theme.fg('dim', statsTrimmed) + dimPadding + coloredRight;
|
|
292
255
|
} else {
|
|
293
|
-
// drop git, drop right → only stats
|
|
294
256
|
const availStats = width - minPad;
|
|
295
|
-
|
|
296
|
-
if (availStats > 0) {
|
|
297
|
-
statsTrimmed = truncateToWidth(statsLeft, availStats, '');
|
|
298
|
-
} else {
|
|
299
|
-
statsTrimmed = '';
|
|
300
|
-
}
|
|
257
|
+
const statsTrimmed = availStats > 0 ? truncateToWidth(statsLeft, availStats, '') : '';
|
|
301
258
|
statsLine = theme.fg('dim', statsTrimmed);
|
|
302
259
|
}
|
|
303
260
|
|
package/header.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Shows a gradient-colored PI logo.
|
|
5
5
|
* Controlled by .pi/settings.json → header (boolean, default true).
|
|
6
6
|
*/
|
|
7
|
-
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
7
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
8
8
|
import { homedir } from 'node:os';
|
|
9
9
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
10
10
|
import type {
|
|
@@ -52,6 +52,10 @@ function gradientLine(line: string): string {
|
|
|
52
52
|
|
|
53
53
|
const ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
54
54
|
|
|
55
|
+
function getHomeDir(): string {
|
|
56
|
+
return process.env.HOME ?? homedir();
|
|
57
|
+
}
|
|
58
|
+
|
|
55
59
|
function visibleLength(line: string): number {
|
|
56
60
|
return line.replace(ANSI_PATTERN, '').length;
|
|
57
61
|
}
|
|
@@ -131,14 +135,13 @@ function formatReasonStatus(theme: Theme, reason: SessionStartEvent['reason']):
|
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
interface HeaderInfo {
|
|
134
|
-
themeName: string;
|
|
135
|
-
cwd: string;
|
|
136
|
-
commands: string[];
|
|
137
138
|
prompts: string[];
|
|
138
139
|
skills: string[];
|
|
139
140
|
extensions: string[];
|
|
141
|
+
packages: string[];
|
|
140
142
|
contextItems: string[];
|
|
141
143
|
contextCount: number;
|
|
144
|
+
packagesCount: number;
|
|
142
145
|
themesCount: number;
|
|
143
146
|
skillsCount: number;
|
|
144
147
|
promptsCount: number;
|
|
@@ -192,12 +195,13 @@ function renderLogo(
|
|
|
192
195
|
|
|
193
196
|
const counts = [
|
|
194
197
|
`context: ${info.contextCount}`,
|
|
195
|
-
`
|
|
198
|
+
`packages: ${info.packagesCount}`,
|
|
199
|
+
`tools: ${info.toolsCount}`,
|
|
196
200
|
`skills: ${info.skillsCount}`,
|
|
197
201
|
`prompts: ${info.promptsCount}`,
|
|
198
|
-
`extensions: ${info.extensionsCount}`,
|
|
199
202
|
`commands: ${info.commandsCount}`,
|
|
200
|
-
`
|
|
203
|
+
`extensions: ${info.extensionsCount}`,
|
|
204
|
+
`themes: ${info.themesCount}`,
|
|
201
205
|
].join(' | ');
|
|
202
206
|
|
|
203
207
|
return [
|
|
@@ -207,6 +211,8 @@ function renderLogo(
|
|
|
207
211
|
'',
|
|
208
212
|
...renderInfoSection(theme, 'Context', info.contextItems, width),
|
|
209
213
|
'',
|
|
214
|
+
...renderInfoSection(theme, 'Packages', info.packages, width),
|
|
215
|
+
'',
|
|
210
216
|
...renderInfoSection(theme, 'Tools', info.tools, width),
|
|
211
217
|
'',
|
|
212
218
|
...renderInfoSection(theme, 'Skills', info.skills, width),
|
|
@@ -249,9 +255,9 @@ function formatRelativePath(cwd: string, filePath: string): string {
|
|
|
249
255
|
}
|
|
250
256
|
|
|
251
257
|
function formatDisplayPath(cwd: string, filePath: string): string {
|
|
252
|
-
const home =
|
|
258
|
+
const home = getHomeDir();
|
|
253
259
|
const rel = relative(cwd, filePath);
|
|
254
|
-
if (!rel ||
|
|
260
|
+
if (!rel || !rel.startsWith('..')) return rel || '.';
|
|
255
261
|
if (filePath === home) return '~';
|
|
256
262
|
if (filePath.startsWith(`${home}/`)) return `~/${relative(home, filePath)}`;
|
|
257
263
|
return filePath;
|
|
@@ -328,57 +334,224 @@ function readPackageLabel(startPath: string): string | undefined {
|
|
|
328
334
|
return undefined;
|
|
329
335
|
}
|
|
330
336
|
|
|
331
|
-
function
|
|
332
|
-
|
|
337
|
+
function getNpmRoot(): string | undefined {
|
|
338
|
+
if (_npmRootResolved) return _npmRoot;
|
|
339
|
+
_npmRootResolved = true;
|
|
340
|
+
|
|
341
|
+
const home = getHomeDir();
|
|
342
|
+
|
|
343
|
+
// NVM: ~/.nvm/versions/node/<version>/lib/node_modules
|
|
344
|
+
if (process.env.NVM_DIR) {
|
|
345
|
+
const versionsDir = join(process.env.NVM_DIR, 'versions', 'node');
|
|
346
|
+
try {
|
|
347
|
+
if (existsSync(versionsDir)) {
|
|
348
|
+
for (const v of readdirSync(versionsDir).sort().reverse()) {
|
|
349
|
+
const dir = join(versionsDir, v, 'lib', 'node_modules');
|
|
350
|
+
if (existsSync(dir)) {
|
|
351
|
+
_npmRoot = dir;
|
|
352
|
+
return _npmRoot;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
} catch {
|
|
357
|
+
/* ignore */
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Bun: ~/.bun/install/global/node_modules
|
|
362
|
+
const bunDir = join(home, '.bun', 'install', 'global', 'node_modules');
|
|
363
|
+
if (existsSync(bunDir)) {
|
|
364
|
+
_npmRoot = bunDir;
|
|
365
|
+
return _npmRoot;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Fallbacks
|
|
369
|
+
for (const dir of ['/usr/local/lib/node_modules', '/usr/lib/node_modules']) {
|
|
370
|
+
if (existsSync(dir)) {
|
|
371
|
+
_npmRoot = dir;
|
|
372
|
+
return _npmRoot;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return undefined;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
let _npmRoot: string | undefined;
|
|
380
|
+
let _npmRootResolved = false;
|
|
381
|
+
|
|
382
|
+
// ── shared: read array field from settings.json ──
|
|
383
|
+
|
|
384
|
+
function readSettingsArray(path: string, key: string): string[] {
|
|
385
|
+
if (!existsSync(path)) return [];
|
|
386
|
+
try {
|
|
387
|
+
const value = JSON.parse(readFileSync(path, 'utf-8'))[key];
|
|
388
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
389
|
+
} catch {
|
|
390
|
+
return [];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ── shared: read raw package sources from settings.json ──
|
|
395
|
+
|
|
396
|
+
interface PackageSource {
|
|
397
|
+
source: string;
|
|
398
|
+
scope: 'project' | 'user';
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
interface ExtensionSource {
|
|
402
|
+
source: string;
|
|
403
|
+
baseDir: string;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function readPackageSources(cwd: string, home = getHomeDir()): PackageSource[] {
|
|
407
|
+
const globalPkgs = readSettingsArray(join(home, '.pi', 'agent', 'settings.json'), 'packages');
|
|
408
|
+
const projectPkgs = readSettingsArray(join(cwd, '.pi', 'settings.json'), 'packages');
|
|
333
409
|
|
|
334
|
-
|
|
335
|
-
|
|
410
|
+
// dedupe: project wins over global
|
|
411
|
+
const seen = new Set<string>();
|
|
412
|
+
const all: PackageSource[] = [];
|
|
413
|
+
for (const s of projectPkgs) {
|
|
414
|
+
if (seen.has(s)) continue;
|
|
415
|
+
seen.add(s);
|
|
416
|
+
all.push({ source: s, scope: 'project' });
|
|
417
|
+
}
|
|
418
|
+
for (const s of globalPkgs) {
|
|
419
|
+
if (seen.has(s)) continue;
|
|
420
|
+
seen.add(s);
|
|
421
|
+
all.push({ source: s, scope: 'user' });
|
|
422
|
+
}
|
|
423
|
+
return all;
|
|
424
|
+
}
|
|
336
425
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
426
|
+
// ── resolve a package source to its directory path ──
|
|
427
|
+
|
|
428
|
+
function resolvePackageDir(source: string, cwd: string, home = getHomeDir()): string | undefined {
|
|
429
|
+
if (source.startsWith('npm:')) {
|
|
430
|
+
const name = source.slice(4);
|
|
431
|
+
const npmRoot = getNpmRoot();
|
|
432
|
+
// project-scoped install (.pi/npm/node_modules/<name>)
|
|
433
|
+
const projectDir = join(cwd, '.pi', 'npm', 'node_modules', name);
|
|
434
|
+
if (existsSync(projectDir)) return projectDir;
|
|
435
|
+
// user-scoped install (~/.pi/agent/node_modules/<name>)
|
|
436
|
+
const userDir = join(home, '.pi', 'agent', 'node_modules', name);
|
|
437
|
+
if (existsSync(userDir)) return userDir;
|
|
438
|
+
// global npm root
|
|
439
|
+
if (npmRoot) {
|
|
440
|
+
const globalDir = join(npmRoot, name);
|
|
441
|
+
if (existsSync(globalDir)) return globalDir;
|
|
442
|
+
}
|
|
443
|
+
return undefined;
|
|
345
444
|
}
|
|
346
445
|
|
|
347
|
-
return
|
|
446
|
+
return source.startsWith('~')
|
|
447
|
+
? join(home, source.slice(source.startsWith('~/') ? 2 : 1))
|
|
448
|
+
: resolve(cwd, source);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// ── getPackages: name+version from each configured package ──
|
|
452
|
+
|
|
453
|
+
function getPackages(cwd: string, home = getHomeDir()): string[] {
|
|
454
|
+
const sources = readPackageSources(cwd, home);
|
|
455
|
+
const results: string[] = [];
|
|
456
|
+
|
|
457
|
+
for (const { source, scope } of sources) {
|
|
458
|
+
const pkgDir = resolvePackageDir(source, cwd, home);
|
|
459
|
+
const scopeTag = scope === 'project' ? ' [l]' : ' [g]';
|
|
460
|
+
if (!pkgDir) {
|
|
461
|
+
// fallback: show npm package name or raw source
|
|
462
|
+
const name = source.startsWith('npm:') ? source.slice(4) : source;
|
|
463
|
+
results.push(name + scopeTag);
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
const label = readPackageLabel(pkgDir) ?? source;
|
|
467
|
+
results.push(label + scopeTag);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return results.sort((a, b) => a.localeCompare(b));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// ── getExtensionItems: scan .ts files from settings.json extensions dirs ──
|
|
474
|
+
|
|
475
|
+
function readExtensionSources(cwd: string, home = getHomeDir()): ExtensionSource[] {
|
|
476
|
+
const projectBaseDir = join(cwd, '.pi');
|
|
477
|
+
const globalBaseDir = join(home, '.pi', 'agent');
|
|
478
|
+
const projectExts = readSettingsArray(join(projectBaseDir, 'settings.json'), 'extensions');
|
|
479
|
+
const globalExts = readSettingsArray(join(globalBaseDir, 'settings.json'), 'extensions');
|
|
480
|
+
|
|
481
|
+
return [
|
|
482
|
+
...projectExts.map((source) => ({ source, baseDir: projectBaseDir })),
|
|
483
|
+
...globalExts.map((source) => ({ source, baseDir: globalBaseDir })),
|
|
484
|
+
];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function resolveSettingsSource(source: string, baseDir: string, home = getHomeDir()): string {
|
|
488
|
+
return source.startsWith('~')
|
|
489
|
+
? join(home, source.slice(source.startsWith('~/') ? 2 : 1))
|
|
490
|
+
: resolve(baseDir, source);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function getExtensionItems(cwd: string, home = getHomeDir()): string[] {
|
|
494
|
+
const results: string[] = [];
|
|
495
|
+
const seenFiles = new Set<string>();
|
|
496
|
+
|
|
497
|
+
function addFile(filePath: string) {
|
|
498
|
+
const key = resolve(filePath);
|
|
499
|
+
if (seenFiles.has(key)) return;
|
|
500
|
+
seenFiles.add(key);
|
|
501
|
+
results.push(formatDisplayPath(cwd, filePath));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
for (const { source, baseDir } of readExtensionSources(cwd, home)) {
|
|
505
|
+
const resolved = resolveSettingsSource(source, baseDir, home);
|
|
506
|
+
|
|
507
|
+
if (!existsSync(resolved)) continue;
|
|
508
|
+
|
|
509
|
+
try {
|
|
510
|
+
const s = statSync(resolved);
|
|
511
|
+
if (s.isDirectory()) {
|
|
512
|
+
for (const f of readdirSync(resolved).sort()) {
|
|
513
|
+
if (f.endsWith('.ts')) addFile(join(resolved, f));
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
addFile(resolved);
|
|
517
|
+
}
|
|
518
|
+
} catch {
|
|
519
|
+
// ignore unreadable paths
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return results;
|
|
348
524
|
}
|
|
349
525
|
|
|
350
526
|
function shouldShowHeaderInfo(ctx: ExtensionContext, reason: SessionStartEvent['reason']): boolean {
|
|
351
527
|
if (reason !== 'startup' && reason !== 'reload') return false;
|
|
352
528
|
const settings = readPowerlineSettings(ctx.cwd);
|
|
353
|
-
|
|
354
|
-
return settings['header-info'];
|
|
529
|
+
return settings.quietStartup && settings['header-info'];
|
|
355
530
|
}
|
|
356
531
|
|
|
357
532
|
function collectHeaderInfo(
|
|
358
533
|
pi: ExtensionAPI,
|
|
359
534
|
ctx: ExtensionContext,
|
|
360
|
-
theme: Theme,
|
|
361
535
|
contextItems: string[],
|
|
362
536
|
skillItems: string[],
|
|
363
537
|
): HeaderInfo {
|
|
364
538
|
const commands = typeof pi.getCommands === 'function' ? pi.getCommands() : [];
|
|
365
539
|
const allThemes = typeof ctx.ui.getAllThemes === 'function' ? ctx.ui.getAllThemes() : [];
|
|
366
|
-
const
|
|
367
|
-
const
|
|
540
|
+
const extensions = getExtensionItems(ctx.cwd);
|
|
541
|
+
const packages = getPackages(ctx.cwd);
|
|
368
542
|
const activeTools =
|
|
369
543
|
typeof pi.getActiveTools === 'function'
|
|
370
544
|
? pi.getActiveTools().sort((a, b) => a.localeCompare(b))
|
|
371
545
|
: [];
|
|
372
546
|
|
|
373
547
|
return {
|
|
374
|
-
themeName,
|
|
375
|
-
cwd: ctx.cwd,
|
|
376
|
-
commands: getCommandNames(commands),
|
|
377
548
|
prompts: getCommandNames(commands, 'prompt'),
|
|
378
549
|
skills: skillItems,
|
|
379
550
|
extensions,
|
|
551
|
+
packages,
|
|
380
552
|
contextItems,
|
|
381
553
|
contextCount: contextItems.length,
|
|
554
|
+
packagesCount: packages.length,
|
|
382
555
|
themesCount: allThemes.length,
|
|
383
556
|
skillsCount: skillItems.length,
|
|
384
557
|
promptsCount: countUniqueSources(commands, 'prompt'),
|
|
@@ -407,7 +580,7 @@ export function registerHeader(pi: ExtensionAPI) {
|
|
|
407
580
|
reason,
|
|
408
581
|
width,
|
|
409
582
|
shouldShowHeaderInfo(ctx, reason)
|
|
410
|
-
? collectHeaderInfo(pi, ctx,
|
|
583
|
+
? collectHeaderInfo(pi, ctx, contextItems, skillItems)
|
|
411
584
|
: undefined,
|
|
412
585
|
);
|
|
413
586
|
},
|
package/index.ts
CHANGED
|
@@ -143,58 +143,33 @@ export default function (pi: ExtensionAPI) {
|
|
|
143
143
|
|
|
144
144
|
const ns = arg.slice(0, colonIdx);
|
|
145
145
|
const val = arg.slice(colonIdx + 1);
|
|
146
|
-
let msg = '';
|
|
147
146
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
writePowerlineSetting(ctx.cwd, 'breadcrumb', val);
|
|
155
|
-
pi.events.emit('powerline_settings_changed', ctx);
|
|
156
|
-
msg = `breadcrumb → ${val}`;
|
|
157
|
-
break;
|
|
158
|
-
}
|
|
159
|
-
case 'footer': {
|
|
160
|
-
if (val !== 'on' && val !== 'off') {
|
|
161
|
-
ctx.ui.notify('footer must be: on or off', 'warning');
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
writePowerlineSetting(ctx.cwd, 'footer', val === 'on');
|
|
165
|
-
pi.events.emit('powerline_settings_changed', ctx);
|
|
166
|
-
msg = `footer → ${val}`;
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
case 'header': {
|
|
170
|
-
if (val !== 'on' && val !== 'off') {
|
|
171
|
-
ctx.ui.notify('header must be: on or off', 'warning');
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
writePowerlineSetting(ctx.cwd, 'header', val === 'on');
|
|
175
|
-
pi.events.emit('powerline_settings_changed', ctx);
|
|
176
|
-
msg = `header → ${val}`;
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
case 'header-info': {
|
|
180
|
-
if (val !== 'on' && val !== 'off') {
|
|
181
|
-
ctx.ui.notify('header-info must be: on or off', 'warning');
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
writePowerlineSetting(ctx.cwd, 'header-info', val === 'on');
|
|
185
|
-
pi.events.emit('powerline_settings_changed', ctx);
|
|
186
|
-
msg = `header-info → ${val}`;
|
|
187
|
-
break;
|
|
147
|
+
if (ns === 'breadcrumb') {
|
|
148
|
+
if (!['hide', 'top', 'inner'].includes(val)) {
|
|
149
|
+
ctx.ui.notify('breadcrumb must be: hide, top, or inner', 'warning');
|
|
150
|
+
return;
|
|
188
151
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
152
|
+
writePowerlineSetting(ctx.cwd, 'breadcrumb', val);
|
|
153
|
+
pi.events.emit('powerline_settings_changed', ctx);
|
|
154
|
+
ctx.ui.notify(`breadcrumb → ${val}`, 'info');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (ns === 'footer' || ns === 'header' || ns === 'header-info') {
|
|
159
|
+
if (val !== 'on' && val !== 'off') {
|
|
160
|
+
ctx.ui.notify(`${ns} must be: on or off`, 'warning');
|
|
194
161
|
return;
|
|
162
|
+
}
|
|
163
|
+
writePowerlineSetting(ctx.cwd, ns, val === 'on');
|
|
164
|
+
pi.events.emit('powerline_settings_changed', ctx);
|
|
165
|
+
ctx.ui.notify(`${ns} → ${val}`, 'info');
|
|
166
|
+
return;
|
|
195
167
|
}
|
|
196
168
|
|
|
197
|
-
ctx.ui.notify(
|
|
169
|
+
ctx.ui.notify(
|
|
170
|
+
'Usage: /powerline <breadcrumb:hide|top|inner|footer:on|off|header:on|off|header-info:on|off>',
|
|
171
|
+
'warning',
|
|
172
|
+
);
|
|
198
173
|
},
|
|
199
174
|
});
|
|
200
175
|
}
|
package/package.json
CHANGED
package/settings.ts
CHANGED
|
@@ -43,18 +43,11 @@ function readSettingsFile(settingsPath: string): Record<string, unknown> {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
function mergeSettings(
|
|
47
|
-
globalSettings: Record<string, unknown>,
|
|
48
|
-
projectSettings: Record<string, unknown>,
|
|
49
|
-
): Record<string, unknown> {
|
|
50
|
-
return { ...globalSettings, ...projectSettings };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
46
|
export function readSettings(cwd: string = process.cwd()): Record<string, unknown> {
|
|
54
|
-
return
|
|
55
|
-
readSettingsFile(getSettingsPath()),
|
|
56
|
-
readSettingsFile(getProjectSettingsPath(cwd)),
|
|
57
|
-
|
|
47
|
+
return {
|
|
48
|
+
...readSettingsFile(getSettingsPath()),
|
|
49
|
+
...readSettingsFile(getProjectSettingsPath(cwd)),
|
|
50
|
+
};
|
|
58
51
|
}
|
|
59
52
|
|
|
60
53
|
function writeSettings(cwd: string, settings: Record<string, unknown>): void {
|