@aemonge-dev/pi-stardeck 0.6.0 → 0.7.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 +1 -1
- package/packages/pi-stardeck/src/deck.ts +99 -49
package/package.json
CHANGED
|
@@ -386,21 +386,25 @@ function globalLeft(state: HeaderDeckState, theme: Theme): string {
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
function projectDeckLeft(state: HeaderDeckState, theme: Theme, width: number): string {
|
|
389
|
-
// Line 1 left: the
|
|
390
|
-
//
|
|
391
|
-
// chips
|
|
392
|
-
|
|
393
|
-
const path =
|
|
389
|
+
// Line 1 left: the path; the git segment yields below 79 columns before
|
|
390
|
+
// the path does. The devbox marker moved to the line-1 corner with the
|
|
391
|
+
// companion chips (deck-machine-corner); the identity cluster never
|
|
392
|
+
// drops, so the path never carries it again.
|
|
393
|
+
const path = color(theme, 'success', ` \u{F07B} ${safeText(state.cwd)}`);
|
|
394
394
|
const gitSegment =
|
|
395
395
|
width >= 79 ? truncateToWidth(gitRight(state, theme), 52, '…') : '';
|
|
396
396
|
return gitSegment ? `${path} ${semanticSeparator(theme)} ${gitSegment}` : path;
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
-
|
|
399
|
+
type CompanionSpec = {
|
|
400
400
|
keys: readonly string[];
|
|
401
401
|
icons: readonly string[];
|
|
402
|
-
|
|
403
|
-
|
|
402
|
+
/** Render the chip bracketed even at one icon (engram identity). */
|
|
403
|
+
bracket?: boolean;
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const KNOWN_COMPANION_ICONS: ReadonlyArray<CompanionSpec> = [
|
|
407
|
+
{ keys: ['engram'], icons: ['\u{21BB}'], bracket: true },
|
|
404
408
|
{ keys: ['graphify'], icons: ['\u{232C}'] },
|
|
405
409
|
{ keys: ['appa', 'openappa'], icons: ['\u{26E8}'] },
|
|
406
410
|
];
|
|
@@ -417,22 +421,37 @@ function companionIconColor(text: string | undefined, active: boolean): string {
|
|
|
417
421
|
: 'accent';
|
|
418
422
|
}
|
|
419
423
|
|
|
420
|
-
function
|
|
424
|
+
function identityCluster(
|
|
425
|
+
state: HeaderDeckState,
|
|
426
|
+
theme: Theme,
|
|
427
|
+
): { text: string; trailingBare: boolean } {
|
|
428
|
+
// Line-1 corner identity: devbox lead chip, then the companion chips.
|
|
429
|
+
// Returns the assembled text plus whether the last chip is bare — bare
|
|
430
|
+
// chips keep the wide gap to their right (0.7.1 amendment: the major
|
|
431
|
+
// separator rides double-spaced after a bare trailing chip, matching
|
|
432
|
+
// the bare→bare chip spacing).
|
|
421
433
|
const texts = new Map<string, string>();
|
|
422
434
|
for (const item of state.piStatus?.nativeStatuses ?? []) {
|
|
423
435
|
if (item.key === 'goal' || texts.has(item.key)) continue;
|
|
424
436
|
texts.set(item.key, item.text);
|
|
425
437
|
}
|
|
426
438
|
const parts: Array<{ bare: boolean; text: string }> = [];
|
|
439
|
+
if (state.devbox) {
|
|
440
|
+
// Corner lead chip (deck-machine-corner): the devbox marker joins
|
|
441
|
+
// the identity cluster ahead of the companions, success-colored as
|
|
442
|
+
// it was as a path prefix.
|
|
443
|
+
parts.push({ bare: false, text: color(theme, 'success', '[\u{F01A7}]', true) });
|
|
444
|
+
}
|
|
427
445
|
for (const companion of KNOWN_COMPANION_ICONS) {
|
|
428
446
|
const published = companion.keys
|
|
429
447
|
.map((key) => texts.get(key))
|
|
430
448
|
.find((text) => text !== undefined);
|
|
431
449
|
for (const key of companion.keys) texts.delete(key);
|
|
432
450
|
const joined = companion.icons.join(' ');
|
|
433
|
-
const
|
|
451
|
+
const bracketed = companion.bracket === true || companion.icons.length > 1;
|
|
452
|
+
const chip = bracketed ? `[${joined}]` : joined;
|
|
434
453
|
parts.push({
|
|
435
|
-
bare:
|
|
454
|
+
bare: !bracketed,
|
|
436
455
|
text: color(
|
|
437
456
|
theme,
|
|
438
457
|
companionIconColor(published, published !== undefined),
|
|
@@ -441,14 +460,14 @@ function extensionStatusCodes(state: HeaderDeckState, theme: Theme): string {
|
|
|
441
460
|
),
|
|
442
461
|
});
|
|
443
462
|
}
|
|
444
|
-
let
|
|
445
|
-
let
|
|
463
|
+
let text = '';
|
|
464
|
+
let trailingBare = false;
|
|
446
465
|
for (const part of parts) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
466
|
+
text +=
|
|
467
|
+
text === '' ? part.text : `${trailingBare && part.bare ? ' ' : ' '}${part.text}`;
|
|
468
|
+
trailingBare = part.bare;
|
|
450
469
|
}
|
|
451
|
-
return
|
|
470
|
+
return { text, trailingBare };
|
|
452
471
|
}
|
|
453
472
|
|
|
454
473
|
const BLOCKED_NOTICE = '\u26d4 blocked';
|
|
@@ -516,37 +535,68 @@ function telemetryRowLeft(state: HeaderDeckState, theme: Theme): string {
|
|
|
516
535
|
return modelGroupLeft(state, theme);
|
|
517
536
|
}
|
|
518
537
|
|
|
519
|
-
function
|
|
538
|
+
export function machineCornerSegments(
|
|
520
539
|
state: HeaderDeckState,
|
|
521
540
|
theme: Theme,
|
|
522
541
|
nowMs = Date.now(),
|
|
523
|
-
): string {
|
|
524
|
-
//
|
|
525
|
-
// quota flame with its countdown,
|
|
526
|
-
//
|
|
527
|
-
|
|
542
|
+
): string[] {
|
|
543
|
+
// Machine corner (deck-machine-corner): icon-led machine readings for
|
|
544
|
+
// the line-1 corner — coding quota flame with its countdown, CPU,
|
|
545
|
+
// memory, MCP availability, global cost — in that frozen order, joined
|
|
546
|
+
// by minor separators. Segments yield right-to-left on narrow frames.
|
|
547
|
+
const segments: string[] = [];
|
|
548
|
+
const flame = windowQuotaTiles(state, theme, nowMs);
|
|
549
|
+
if (flame !== '') segments.push(flame);
|
|
528
550
|
const resources = state.resources;
|
|
529
551
|
if (resources) {
|
|
530
552
|
const resourceColor =
|
|
531
553
|
resources.cpuWarning || resources.memoryWarning ? 'warning' : 'accent';
|
|
532
|
-
|
|
533
|
-
color(theme, resourceColor,
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
554
|
+
segments.push(
|
|
555
|
+
color(theme, resourceColor, `\u{F035B} ${formatDeckCpu(resources.cpuPercent)}`),
|
|
556
|
+
);
|
|
557
|
+
segments.push(
|
|
558
|
+
color(
|
|
559
|
+
theme,
|
|
560
|
+
resourceColor,
|
|
561
|
+
`\u{F061A} ${formatDeckBytes(resources.memoryBytes)}`,
|
|
562
|
+
),
|
|
563
|
+
);
|
|
537
564
|
}
|
|
538
565
|
if (state.mcp) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
566
|
+
segments.push(color(theme, 'accent', `${state.mcp.healthy}/${state.mcp.total}`));
|
|
567
|
+
}
|
|
568
|
+
const cost = costTile(state, theme);
|
|
569
|
+
if (cost !== '') segments.push(cost);
|
|
570
|
+
return segments;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function deckCornerRight(
|
|
574
|
+
state: HeaderDeckState,
|
|
575
|
+
theme: Theme,
|
|
576
|
+
width: number,
|
|
577
|
+
nowMs = Date.now(),
|
|
578
|
+
): string {
|
|
579
|
+
// Line 1 corner: the identity cluster (devbox lead, companion chips)
|
|
580
|
+
// split from the machine group by one major separator. A bare trailing
|
|
581
|
+
// chip keeps the wide gap before the separator (0.7.1 amendment);
|
|
582
|
+
// machine segments yield right-to-left (cost first) before the path
|
|
583
|
+
// truncates below a 20-column floor; the identity cluster never drops
|
|
584
|
+
// (always-visible contract).
|
|
585
|
+
const identity = identityCluster(state, theme);
|
|
586
|
+
const segments = machineCornerSegments(state, theme, nowMs);
|
|
587
|
+
const pathFloor = 20;
|
|
588
|
+
const cornerGap = identity.trailingBare ? ' ' : ' ';
|
|
589
|
+
for (let keep = segments.length; keep >= 0; keep -= 1) {
|
|
590
|
+
const tail = segments.slice(0, keep).join(` ${minorSeparator(theme)} `);
|
|
591
|
+
const corner =
|
|
592
|
+
tail === ''
|
|
593
|
+
? identity.text
|
|
594
|
+
: `${identity.text}${cornerGap}${semanticSeparator(theme)} ${tail}`;
|
|
595
|
+
if (keep === 0 || width - visibleWidth(corner) - 1 >= pathFloor) {
|
|
596
|
+
return corner;
|
|
597
|
+
}
|
|
542
598
|
}
|
|
543
|
-
return
|
|
544
|
-
windowQuotaTiles(state, theme, nowMs),
|
|
545
|
-
machineSegments.join(` ${semanticSeparator(theme)} `),
|
|
546
|
-
costTile(state, theme),
|
|
547
|
-
]
|
|
548
|
-
.filter((group) => group !== '')
|
|
549
|
-
.join(` ${semanticSeparator(theme)} `);
|
|
599
|
+
return identity.text;
|
|
550
600
|
}
|
|
551
601
|
|
|
552
602
|
function modelGroupLeft(state: HeaderDeckState, theme: Theme): string {
|
|
@@ -760,17 +810,17 @@ export function renderHeaderDeck(
|
|
|
760
810
|
turnCostTile(state, theme),
|
|
761
811
|
boundedWidth,
|
|
762
812
|
);
|
|
763
|
-
// Line 4 (
|
|
764
|
-
//
|
|
765
|
-
//
|
|
766
|
-
//
|
|
767
|
-
//
|
|
768
|
-
//
|
|
813
|
+
// Line 4 (deck-machine-corner amendment): the engine group holds the
|
|
814
|
+
// row's left end; the right end is notice-only. The machine group moved
|
|
815
|
+
// to the line-1 corner, so notices can no longer starve machine
|
|
816
|
+
// readings — the old wholesale yield is gone with the group itself.
|
|
817
|
+
// 0.7.1 amendment: a clean notice slot renders the dim `healthy`
|
|
818
|
+
// placeholder instead of trailing silence — it yields like a notice
|
|
819
|
+
// when the engine group needs the width.
|
|
769
820
|
const engineGroup = telemetryRowLeft(state, theme);
|
|
770
821
|
const noticeGroup = noticeTile(state, theme);
|
|
771
|
-
const rightGroup = noticeGroup !== '' ? noticeGroup :
|
|
822
|
+
const rightGroup = noticeGroup !== '' ? noticeGroup : color(theme, 'dim', 'healthy');
|
|
772
823
|
const telemetryRow =
|
|
773
|
-
rightGroup !== '' &&
|
|
774
824
|
visibleWidth(engineGroup) + 1 + visibleWidth(rightGroup) <= boundedWidth
|
|
775
825
|
? alignedSingleRow(engineGroup, rightGroup, boundedWidth)
|
|
776
826
|
: fit(engineGroup, boundedWidth);
|
|
@@ -781,17 +831,17 @@ export function renderHeaderDeck(
|
|
|
781
831
|
// line 4; pi's own prompt chrome below closes the visual block — while
|
|
782
832
|
// the internal seams stay dotted ┈ between lines 2 and 3 and between
|
|
783
833
|
// line 3 (spans rows included) and line 4.
|
|
784
|
-
const
|
|
834
|
+
const corner = deckCornerRight(state, theme, boundedWidth);
|
|
785
835
|
const alert = statusAlertTile(
|
|
786
836
|
state,
|
|
787
|
-
Math.max(0, boundedWidth - visibleWidth(
|
|
837
|
+
Math.max(0, boundedWidth - visibleWidth(corner) - 2),
|
|
788
838
|
theme,
|
|
789
839
|
lineColor,
|
|
790
840
|
);
|
|
791
841
|
const rows = [
|
|
792
842
|
alignedSingleRow(
|
|
793
843
|
projectDeckLeft(state, theme, boundedWidth),
|
|
794
|
-
alert ? `${alert} ${
|
|
844
|
+
alert ? `${alert} ${corner}` : corner,
|
|
795
845
|
boundedWidth,
|
|
796
846
|
),
|
|
797
847
|
solidRule,
|