@femtomc/mu-agent 26.2.110 → 26.2.111
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/assets/mu-tui-logo.png +0 -0
- package/dist/extensions/ui.d.ts.map +1 -1
- package/dist/extensions/ui.js +165 -24
- package/package.json +2 -2
package/assets/mu-tui-logo.png
CHANGED
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/extensions/ui.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/extensions/ui.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,+BAA+B,CAAC;AAsqCpF,wBAAgB,WAAW,CAAC,EAAE,EAAE,YAAY,QA6N3C;AAED,eAAe,WAAW,CAAC"}
|
package/dist/extensions/ui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { normalizeUiDocs, parseUiDoc, resolveUiStatusProfileName, uiStatusProfileWarnings, } from "@femtomc/mu-core";
|
|
2
|
-
import { matchesKey } from "@mariozechner/pi-tui";
|
|
2
|
+
import { matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
3
3
|
import { registerMuSubcommand } from "./mu-command-dispatcher.js";
|
|
4
4
|
const UI_DISPLAY_DOCS_MAX = 16;
|
|
5
5
|
const UI_PICKER_COMPONENTS_MAX = 8;
|
|
@@ -8,6 +8,13 @@ const UI_PICKER_KEYVALUE_ROWS_MAX = 4;
|
|
|
8
8
|
const UI_SESSION_KEY_FALLBACK = "__mu_ui_active_session__";
|
|
9
9
|
const UI_PROMPT_PREVIEW_MAX = 160;
|
|
10
10
|
const UI_INTERACT_SHORTCUT = "ctrl+shift+u";
|
|
11
|
+
const UI_PICKER_PANEL_MIN_WIDTH = 56;
|
|
12
|
+
const UI_PICKER_PANEL_MAX_WIDTH = 118;
|
|
13
|
+
const UI_PICKER_PANEL_WIDTH_RATIO = 0.9;
|
|
14
|
+
const UI_PICKER_PANEL_TOP_MARGIN = 1;
|
|
15
|
+
const UI_PICKER_PANEL_BOTTOM_MARGIN = 1;
|
|
16
|
+
const UI_ENABLE_MOUSE_TRACKING = "\x1b[?1000h\x1b[?1006h";
|
|
17
|
+
const UI_DISABLE_MOUSE_TRACKING = "\x1b[?1000l\x1b[?1006l";
|
|
11
18
|
const UI_INTERACT_OVERLAY_OPTIONS = {
|
|
12
19
|
anchor: "top-left",
|
|
13
20
|
row: 0,
|
|
@@ -487,6 +494,34 @@ function boundedIndex(index, length) {
|
|
|
487
494
|
}
|
|
488
495
|
return index;
|
|
489
496
|
}
|
|
497
|
+
function parseSgrMouseEvent(data) {
|
|
498
|
+
const match = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])$/.exec(data);
|
|
499
|
+
if (!match) {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
const buttonCode = Number.parseInt(match[1] ?? "", 10);
|
|
503
|
+
const col = Number.parseInt(match[2] ?? "", 10);
|
|
504
|
+
const row = Number.parseInt(match[3] ?? "", 10);
|
|
505
|
+
if (!Number.isInteger(buttonCode) || !Number.isInteger(col) || !Number.isInteger(row)) {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
return {
|
|
509
|
+
buttonCode,
|
|
510
|
+
col,
|
|
511
|
+
row,
|
|
512
|
+
release: match[4] === "m",
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function isPrimaryMouseRelease(event) {
|
|
516
|
+
if (!event.release) {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
if (event.buttonCode >= 64) {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
const primaryButton = event.buttonCode & 0b11;
|
|
523
|
+
return primaryButton === 0;
|
|
524
|
+
}
|
|
490
525
|
function pickerComponentLines(component) {
|
|
491
526
|
switch (component.kind) {
|
|
492
527
|
case "text":
|
|
@@ -525,13 +560,22 @@ function pickerComponentLines(component) {
|
|
|
525
560
|
}
|
|
526
561
|
}
|
|
527
562
|
class UiActionPickerComponent {
|
|
563
|
+
#tui;
|
|
528
564
|
#entries;
|
|
529
565
|
#theme;
|
|
530
566
|
#done;
|
|
531
567
|
#mode = "doc";
|
|
532
568
|
#docIndex = 0;
|
|
533
569
|
#actionIndex = 0;
|
|
570
|
+
#mouseEnabled = false;
|
|
571
|
+
#panelRowStart = 1;
|
|
572
|
+
#panelRowEnd = 1;
|
|
573
|
+
#panelColStart = 1;
|
|
574
|
+
#panelColEnd = 1;
|
|
575
|
+
#docHitRows = new Map();
|
|
576
|
+
#actionHitRows = new Map();
|
|
534
577
|
constructor(opts) {
|
|
578
|
+
this.#tui = opts.tui;
|
|
535
579
|
this.#entries = opts.entries;
|
|
536
580
|
this.#theme = opts.theme;
|
|
537
581
|
this.#done = opts.done;
|
|
@@ -549,6 +593,21 @@ class UiActionPickerComponent {
|
|
|
549
593
|
this.#mode = "action";
|
|
550
594
|
}
|
|
551
595
|
}
|
|
596
|
+
this.#enableMouseTracking();
|
|
597
|
+
}
|
|
598
|
+
#enableMouseTracking() {
|
|
599
|
+
if (this.#mouseEnabled) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
this.#tui.terminal.write(UI_ENABLE_MOUSE_TRACKING);
|
|
603
|
+
this.#mouseEnabled = true;
|
|
604
|
+
}
|
|
605
|
+
#disableMouseTracking() {
|
|
606
|
+
if (!this.#mouseEnabled) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
this.#tui.terminal.write(UI_DISABLE_MOUSE_TRACKING);
|
|
610
|
+
this.#mouseEnabled = false;
|
|
552
611
|
}
|
|
553
612
|
#currentEntry() {
|
|
554
613
|
return this.#entries[this.#docIndex];
|
|
@@ -583,7 +642,36 @@ class UiActionPickerComponent {
|
|
|
583
642
|
action,
|
|
584
643
|
});
|
|
585
644
|
}
|
|
645
|
+
#handleMouseEvent(event) {
|
|
646
|
+
if (!isPrimaryMouseRelease(event)) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (event.row < this.#panelRowStart ||
|
|
650
|
+
event.row > this.#panelRowEnd ||
|
|
651
|
+
event.col < this.#panelColStart ||
|
|
652
|
+
event.col > this.#panelColEnd) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
const docIndex = this.#docHitRows.get(event.row);
|
|
656
|
+
if (docIndex !== undefined) {
|
|
657
|
+
this.#docIndex = boundedIndex(docIndex, this.#entries.length);
|
|
658
|
+
this.#actionIndex = boundedIndex(this.#actionIndex, this.#currentActions().length);
|
|
659
|
+
this.#mode = "doc";
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const actionIndex = this.#actionHitRows.get(event.row);
|
|
663
|
+
if (actionIndex !== undefined) {
|
|
664
|
+
this.#mode = "action";
|
|
665
|
+
this.#actionIndex = boundedIndex(actionIndex, this.#currentActions().length);
|
|
666
|
+
this.#submit();
|
|
667
|
+
}
|
|
668
|
+
}
|
|
586
669
|
handleInput(data) {
|
|
670
|
+
const mouse = parseSgrMouseEvent(data);
|
|
671
|
+
if (mouse) {
|
|
672
|
+
this.#handleMouseEvent(mouse);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
587
675
|
if (matchesKey(data, "escape")) {
|
|
588
676
|
this.#done(null);
|
|
589
677
|
return;
|
|
@@ -632,67 +720,120 @@ class UiActionPickerComponent {
|
|
|
632
720
|
invalidate() {
|
|
633
721
|
// No cached state.
|
|
634
722
|
}
|
|
723
|
+
dispose() {
|
|
724
|
+
this.#disableMouseTracking();
|
|
725
|
+
}
|
|
635
726
|
render(width) {
|
|
636
|
-
const
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
727
|
+
const panelTargetWidth = Math.max(UI_PICKER_PANEL_MIN_WIDTH, Math.min(UI_PICKER_PANEL_MAX_WIDTH, Math.floor(width * UI_PICKER_PANEL_WIDTH_RATIO)));
|
|
728
|
+
const panelWidth = Math.max(4, Math.min(width, panelTargetWidth));
|
|
729
|
+
const innerWidth = Math.max(1, panelWidth - 2);
|
|
730
|
+
const maxContentWidth = Math.max(8, innerWidth);
|
|
731
|
+
const content = [];
|
|
732
|
+
const docHitRowsByContentIndex = new Map();
|
|
733
|
+
const actionHitRowsByContentIndex = new Map();
|
|
734
|
+
const pushContent = (line) => {
|
|
735
|
+
content.push(truncateToWidth(line, innerWidth, "…", true));
|
|
736
|
+
};
|
|
737
|
+
pushContent(this.#theme.fg("accent", short("Programmable UI", maxContentWidth)));
|
|
738
|
+
pushContent(this.#theme.fg("dim", short("↑/↓ move · tab switch · enter select/submit · esc cancel · click action to submit", maxContentWidth)));
|
|
739
|
+
pushContent("");
|
|
740
|
+
pushContent(this.#theme.fg(this.#mode === "doc" ? "accent" : "dim", short(`Documents (${this.#entries.length})`, maxContentWidth)));
|
|
642
741
|
for (let idx = 0; idx < this.#entries.length; idx += 1) {
|
|
643
742
|
const entry = this.#entries[idx];
|
|
644
743
|
const active = idx === this.#docIndex;
|
|
645
744
|
const marker = active ? (this.#mode === "doc" ? "▶" : "▸") : " ";
|
|
646
745
|
const label = `${marker} ${entry.doc.ui_id} · ${entry.doc.title}`;
|
|
647
|
-
|
|
746
|
+
docHitRowsByContentIndex.set(content.length, idx);
|
|
747
|
+
pushContent(this.#theme.fg(active ? "accent" : "muted", short(label, maxContentWidth)));
|
|
648
748
|
}
|
|
649
749
|
const selectedDoc = this.#currentEntry().doc;
|
|
650
750
|
if (selectedDoc.summary) {
|
|
651
|
-
|
|
652
|
-
|
|
751
|
+
pushContent("");
|
|
752
|
+
pushContent(this.#theme.fg("dim", short(`Summary: ${selectedDoc.summary}`, maxContentWidth)));
|
|
653
753
|
}
|
|
654
|
-
|
|
655
|
-
|
|
754
|
+
pushContent("");
|
|
755
|
+
pushContent(this.#theme.fg("dim", short(`Components (${selectedDoc.components.length})`, maxContentWidth)));
|
|
656
756
|
const visibleComponents = selectedDoc.components.slice(0, UI_PICKER_COMPONENTS_MAX);
|
|
657
757
|
for (const component of visibleComponents) {
|
|
658
758
|
const componentLines = pickerComponentLines(component);
|
|
659
759
|
for (let idx = 0; idx < componentLines.length; idx += 1) {
|
|
660
760
|
const line = componentLines[idx];
|
|
661
761
|
const prefix = idx === 0 ? " " : " ";
|
|
662
|
-
|
|
762
|
+
pushContent(this.#theme.fg("text", short(`${prefix}${line}`, maxContentWidth)));
|
|
663
763
|
}
|
|
664
764
|
}
|
|
665
765
|
if (selectedDoc.components.length > visibleComponents.length) {
|
|
666
|
-
|
|
766
|
+
pushContent(this.#theme.fg("muted", short(` ... (+${selectedDoc.components.length - visibleComponents.length} more components)`, maxContentWidth)));
|
|
667
767
|
}
|
|
668
768
|
const actions = this.#currentActions();
|
|
669
|
-
|
|
670
|
-
|
|
769
|
+
pushContent("");
|
|
770
|
+
pushContent(this.#theme.fg(this.#mode === "action" ? "accent" : "dim", short(`Actions (${actions.length})`, maxContentWidth)));
|
|
671
771
|
for (let idx = 0; idx < actions.length; idx += 1) {
|
|
672
772
|
const action = actions[idx];
|
|
673
773
|
const active = idx === this.#actionIndex;
|
|
674
774
|
const marker = active ? (this.#mode === "action" ? "▶" : "▸") : " ";
|
|
675
775
|
const label = `${marker} ${action.id} · ${action.label}`;
|
|
676
|
-
|
|
776
|
+
actionHitRowsByContentIndex.set(content.length, idx);
|
|
777
|
+
pushContent(this.#theme.fg(active ? "accent" : "text", short(label, maxContentWidth)));
|
|
677
778
|
}
|
|
678
779
|
const action = this.#currentAction();
|
|
679
780
|
if (action?.description) {
|
|
680
|
-
|
|
681
|
-
|
|
781
|
+
pushContent("");
|
|
782
|
+
pushContent(this.#theme.fg("dim", short(`Ask: ${action.description}`, maxContentWidth)));
|
|
682
783
|
}
|
|
683
784
|
if (action?.component_id) {
|
|
684
|
-
|
|
785
|
+
pushContent(this.#theme.fg("dim", short(`Targets component: ${action.component_id}`, maxContentWidth)));
|
|
685
786
|
}
|
|
686
787
|
const commandText = action ? actionCommandText(action) : null;
|
|
687
788
|
if (commandText) {
|
|
688
|
-
|
|
689
|
-
|
|
789
|
+
pushContent("");
|
|
790
|
+
pushContent(this.#theme.fg("dim", short(`Prompt template: ${commandText}`, maxContentWidth)));
|
|
791
|
+
}
|
|
792
|
+
const topMarginRows = Math.max(0, UI_PICKER_PANEL_TOP_MARGIN);
|
|
793
|
+
const bottomMarginRows = Math.max(0, UI_PICKER_PANEL_BOTTOM_MARGIN);
|
|
794
|
+
const leftPadWidth = Math.max(0, Math.floor((width - panelWidth) / 2));
|
|
795
|
+
const leftPad = " ".repeat(leftPadWidth);
|
|
796
|
+
const frame = [];
|
|
797
|
+
for (let row = 0; row < topMarginRows; row += 1) {
|
|
798
|
+
frame.push("");
|
|
799
|
+
}
|
|
800
|
+
const title = " mu_ui ";
|
|
801
|
+
const titleWidth = Math.min(innerWidth, visibleWidth(title));
|
|
802
|
+
const leftRule = "─".repeat(Math.max(0, Math.floor((innerWidth - titleWidth) / 2)));
|
|
803
|
+
const rightRule = "─".repeat(Math.max(0, innerWidth - titleWidth - leftRule.length));
|
|
804
|
+
frame.push(`${leftPad}${this.#theme.fg("borderAccent", `╭${leftRule}`)}${this.#theme.fg("accent", title)}${this.#theme.fg("borderAccent", `${rightRule}╮`)}`);
|
|
805
|
+
this.#docHitRows.clear();
|
|
806
|
+
this.#actionHitRows.clear();
|
|
807
|
+
const contentStartRow = frame.length + 1;
|
|
808
|
+
for (let idx = 0; idx < content.length; idx += 1) {
|
|
809
|
+
const line = content[idx] ?? "";
|
|
810
|
+
const visible = visibleWidth(line);
|
|
811
|
+
const fill = " ".repeat(Math.max(0, innerWidth - visible));
|
|
812
|
+
frame.push(`${leftPad}${this.#theme.fg("border", "│")}${this.#theme.bg("customMessageBg", `${line}${fill}`)}${this.#theme.fg("border", "│")}`);
|
|
813
|
+
const row = contentStartRow + idx;
|
|
814
|
+
const docIndex = docHitRowsByContentIndex.get(idx);
|
|
815
|
+
if (docIndex !== undefined) {
|
|
816
|
+
this.#docHitRows.set(row, docIndex);
|
|
817
|
+
}
|
|
818
|
+
const actionIndex = actionHitRowsByContentIndex.get(idx);
|
|
819
|
+
if (actionIndex !== undefined) {
|
|
820
|
+
this.#actionHitRows.set(row, actionIndex);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
frame.push(`${leftPad}${this.#theme.fg("borderAccent", `╰${"─".repeat(innerWidth)}╯`)}`);
|
|
824
|
+
for (let row = 0; row < bottomMarginRows; row += 1) {
|
|
825
|
+
frame.push("");
|
|
690
826
|
}
|
|
691
|
-
|
|
827
|
+
this.#panelRowStart = topMarginRows + 1;
|
|
828
|
+
this.#panelRowEnd = this.#panelRowStart + content.length + 1;
|
|
829
|
+
this.#panelColStart = leftPadWidth + 1;
|
|
830
|
+
this.#panelColEnd = leftPadWidth + panelWidth;
|
|
831
|
+
return frame;
|
|
692
832
|
}
|
|
693
833
|
}
|
|
694
834
|
async function pickUiActionInteractively(opts) {
|
|
695
|
-
const selected = await opts.ctx.ui.custom((
|
|
835
|
+
const selected = await opts.ctx.ui.custom((tui, theme, _keybindings, done) => new UiActionPickerComponent({
|
|
836
|
+
tui,
|
|
696
837
|
entries: opts.entries,
|
|
697
838
|
theme: theme,
|
|
698
839
|
done,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@femtomc/mu-agent",
|
|
3
|
-
"version": "26.2.
|
|
3
|
+
"version": "26.2.111",
|
|
4
4
|
"description": "Shared operator runtime for mu assistant sessions and serve extensions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mu",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"themes/**"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@femtomc/mu-core": "26.2.
|
|
28
|
+
"@femtomc/mu-core": "26.2.111",
|
|
29
29
|
"@mariozechner/pi-agent-core": "^0.54.2",
|
|
30
30
|
"@mariozechner/pi-ai": "^0.54.2",
|
|
31
31
|
"@mariozechner/pi-coding-agent": "^0.54.2",
|