@aemonge-dev/pi-stardeck 0.6.0 → 0.7.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aemonge-dev/pi-stardeck",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "A Vim- and tmux-inspired command deck for Pi",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -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 devbox marker is a path prefix, then the path; the git
390
- // segment yields below 79 columns before the path does. The companion
391
- // chips ride line 1's right end and never drop (always-visible contract).
392
- const devbox = state.devbox ? `${color(theme, 'success', '[󰆧]', true)} ` : '';
393
- const path = `${devbox}${color(theme, 'success', ` ${safeText(state.cwd)}`)}`;
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
- const KNOWN_COMPANION_ICONS: ReadonlyArray<{
399
+ type CompanionSpec = {
400
400
  keys: readonly string[];
401
401
  icons: readonly string[];
402
- }> = [
403
- { keys: ['engram'], icons: ['\u{21BB}', '\u{25A4}'] },
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
  ];
@@ -424,15 +428,22 @@ function extensionStatusCodes(state: HeaderDeckState, theme: Theme): string {
424
428
  texts.set(item.key, item.text);
425
429
  }
426
430
  const parts: Array<{ bare: boolean; text: string }> = [];
431
+ if (state.devbox) {
432
+ // Corner lead chip (deck-machine-corner): the devbox marker joins
433
+ // the identity cluster ahead of the companions, success-colored as
434
+ // it was as a path prefix.
435
+ parts.push({ bare: false, text: color(theme, 'success', '[\u{F01A7}]', true) });
436
+ }
427
437
  for (const companion of KNOWN_COMPANION_ICONS) {
428
438
  const published = companion.keys
429
439
  .map((key) => texts.get(key))
430
440
  .find((text) => text !== undefined);
431
441
  for (const key of companion.keys) texts.delete(key);
432
442
  const joined = companion.icons.join(' ');
433
- const chip = companion.icons.length > 1 ? `[${joined}]` : joined;
443
+ const bracketed = companion.bracket === true || companion.icons.length > 1;
444
+ const chip = bracketed ? `[${joined}]` : joined;
434
445
  parts.push({
435
- bare: companion.icons.length === 1,
446
+ bare: !bracketed,
436
447
  text: color(
437
448
  theme,
438
449
  companionIconColor(published, published !== undefined),
@@ -516,37 +527,64 @@ function telemetryRowLeft(state: HeaderDeckState, theme: Theme): string {
516
527
  return modelGroupLeft(state, theme);
517
528
  }
518
529
 
519
- function telemetryRowRight(
530
+ export function machineCornerSegments(
520
531
  state: HeaderDeckState,
521
532
  theme: Theme,
522
533
  nowMs = Date.now(),
523
- ): string {
524
- // Line 4 right (2.4 second amendment): the machine group — the coding
525
- // quota flame with its countdown, machine readings, global cost — in
526
- // that order, anchoring the row's right end.
527
- const machineSegments: string[] = [];
534
+ ): string[] {
535
+ // Machine corner (deck-machine-corner): icon-led machine readings for
536
+ // the line-1 corner — coding quota flame with its countdown, CPU,
537
+ // memory, MCP availability, global cost — in that frozen order, joined
538
+ // by minor separators. Segments yield right-to-left on narrow frames.
539
+ const segments: string[] = [];
540
+ const flame = windowQuotaTiles(state, theme, nowMs);
541
+ if (flame !== '') segments.push(flame);
528
542
  const resources = state.resources;
529
543
  if (resources) {
530
544
  const resourceColor =
531
545
  resources.cpuWarning || resources.memoryWarning ? 'warning' : 'accent';
532
- const parts = [
533
- color(theme, resourceColor, `${formatDeckCpu(resources.cpuPercent)}`),
534
- color(theme, resourceColor, `${formatDeckBytes(resources.memoryBytes)}`),
535
- ];
536
- machineSegments.push(parts.join(` ${minorSeparator(theme, resourceColor)} `));
546
+ segments.push(
547
+ color(theme, resourceColor, `\u{F035B} ${formatDeckCpu(resources.cpuPercent)}`),
548
+ );
549
+ segments.push(
550
+ color(
551
+ theme,
552
+ resourceColor,
553
+ `\u{F061A} ${formatDeckBytes(resources.memoryBytes)}`,
554
+ ),
555
+ );
537
556
  }
538
557
  if (state.mcp) {
539
- machineSegments.push(
540
- color(theme, 'accent', `${state.mcp.healthy}/${state.mcp.total}`),
541
- );
558
+ segments.push(color(theme, 'accent', `${state.mcp.healthy}/${state.mcp.total}`));
559
+ }
560
+ const cost = costTile(state, theme);
561
+ if (cost !== '') segments.push(cost);
562
+ return segments;
563
+ }
564
+
565
+ function deckCornerRight(
566
+ state: HeaderDeckState,
567
+ theme: Theme,
568
+ width: number,
569
+ nowMs = Date.now(),
570
+ ): string {
571
+ // Line 1 corner: the identity cluster (devbox lead, companion chips)
572
+ // split from the machine group by one major separator. Machine segments
573
+ // yield right-to-left (cost first) before the path truncates below a
574
+ // 20-column floor; the identity cluster never drops (always-visible
575
+ // contract).
576
+ const identity = extensionStatusCodes(state, theme);
577
+ const segments = machineCornerSegments(state, theme, nowMs);
578
+ const pathFloor = 20;
579
+ for (let keep = segments.length; keep >= 0; keep -= 1) {
580
+ const tail = segments.slice(0, keep).join(` ${minorSeparator(theme)} `);
581
+ const corner =
582
+ tail === '' ? identity : `${identity} ${semanticSeparator(theme)} ${tail}`;
583
+ if (keep === 0 || width - visibleWidth(corner) - 1 >= pathFloor) {
584
+ return corner;
585
+ }
542
586
  }
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)} `);
587
+ return identity;
550
588
  }
551
589
 
552
590
  function modelGroupLeft(state: HeaderDeckState, theme: Theme): string {
@@ -760,19 +798,16 @@ export function renderHeaderDeck(
760
798
  turnCostTile(state, theme),
761
799
  boundedWidth,
762
800
  );
763
- // Line 4 (2.4 second amendment + Task 3 placement default): the engine
764
- // group holds the row's left end. Its right end belongs to the machine
765
- // group — until notices land: a non-empty notice slot owns the right end
766
- // and the machine group yields wholesale, riding the same ladder as the
767
- // narrow-frame yield (the right group renders only when it fits beside
768
- // the engine group at full width; the engine group never yields to it).
801
+ // Line 4 (deck-machine-corner amendment): the engine group holds the
802
+ // row's left end; the right end is notice-only. The machine group moved
803
+ // to the line-1 corner, so notices can no longer starve machine
804
+ // readings — the old wholesale yield is gone with the group itself.
769
805
  const engineGroup = telemetryRowLeft(state, theme);
770
806
  const noticeGroup = noticeTile(state, theme);
771
- const rightGroup = noticeGroup !== '' ? noticeGroup : telemetryRowRight(state, theme);
772
807
  const telemetryRow =
773
- rightGroup !== '' &&
774
- visibleWidth(engineGroup) + 1 + visibleWidth(rightGroup) <= boundedWidth
775
- ? alignedSingleRow(engineGroup, rightGroup, boundedWidth)
808
+ noticeGroup !== '' &&
809
+ visibleWidth(engineGroup) + 1 + visibleWidth(noticeGroup) <= boundedWidth
810
+ ? alignedSingleRow(engineGroup, noticeGroup, boundedWidth)
776
811
  : fit(engineGroup, boundedWidth);
777
812
 
778
813
  // Band adjacency (Human-directed, accepted mockups; third 2.4
@@ -781,17 +816,17 @@ export function renderHeaderDeck(
781
816
  // line 4; pi's own prompt chrome below closes the visual block — while
782
817
  // the internal seams stay dotted ┈ between lines 2 and 3 and between
783
818
  // line 3 (spans rows included) and line 4.
784
- const chips = extensionStatusCodes(state, theme);
819
+ const corner = deckCornerRight(state, theme, boundedWidth);
785
820
  const alert = statusAlertTile(
786
821
  state,
787
- Math.max(0, boundedWidth - visibleWidth(chips) - 2),
822
+ Math.max(0, boundedWidth - visibleWidth(corner) - 2),
788
823
  theme,
789
824
  lineColor,
790
825
  );
791
826
  const rows = [
792
827
  alignedSingleRow(
793
828
  projectDeckLeft(state, theme, boundedWidth),
794
- alert ? `${alert} ${chips}` : chips,
829
+ alert ? `${alert} ${corner}` : corner,
795
830
  boundedWidth,
796
831
  ),
797
832
  solidRule,