groupchat 0.0.7 → 0.0.8
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/dist/index.js +417 -239
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -424,9 +424,132 @@ var init_Layout = __esm({
|
|
|
424
424
|
}
|
|
425
425
|
});
|
|
426
426
|
|
|
427
|
+
// src/lib/constants.ts
|
|
428
|
+
function getAgentDisplayName(agent) {
|
|
429
|
+
if (!agent) return "";
|
|
430
|
+
return AGENT_CONFIG[agent].displayName;
|
|
431
|
+
}
|
|
432
|
+
function getAgentColor(agent) {
|
|
433
|
+
if (!agent) return void 0;
|
|
434
|
+
return AGENT_CONFIG[agent].color;
|
|
435
|
+
}
|
|
436
|
+
var AGENT_CONFIG;
|
|
437
|
+
var init_constants = __esm({
|
|
438
|
+
"src/lib/constants.ts"() {
|
|
439
|
+
"use strict";
|
|
440
|
+
AGENT_CONFIG = {
|
|
441
|
+
claude: {
|
|
442
|
+
type: "claude",
|
|
443
|
+
displayName: "Claude Code",
|
|
444
|
+
color: "redBright"
|
|
445
|
+
},
|
|
446
|
+
codex: {
|
|
447
|
+
type: "codex",
|
|
448
|
+
displayName: "Codex",
|
|
449
|
+
color: "cyan"
|
|
450
|
+
},
|
|
451
|
+
cursor: {
|
|
452
|
+
type: "cursor",
|
|
453
|
+
displayName: "Cursor",
|
|
454
|
+
color: "blueBright"
|
|
455
|
+
},
|
|
456
|
+
windsurf: {
|
|
457
|
+
type: "windsurf",
|
|
458
|
+
displayName: "Windsurf",
|
|
459
|
+
color: "magenta"
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// src/components/AtAGlance.tsx
|
|
466
|
+
import { Box as Box5, Text as Text4 } from "ink";
|
|
467
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
468
|
+
function AtAGlance({ presenceState }) {
|
|
469
|
+
const userStats = Object.values(presenceState).reduce(
|
|
470
|
+
(acc, userData) => {
|
|
471
|
+
acc.total++;
|
|
472
|
+
const agent = userData.metas[0]?.current_agent;
|
|
473
|
+
if (agent === "claude") {
|
|
474
|
+
acc.claude++;
|
|
475
|
+
} else if (agent === "codex") {
|
|
476
|
+
acc.codex++;
|
|
477
|
+
} else if (agent === "cursor") {
|
|
478
|
+
acc.cursor++;
|
|
479
|
+
} else if (agent === "windsurf") {
|
|
480
|
+
acc.windsurf++;
|
|
481
|
+
}
|
|
482
|
+
return acc;
|
|
483
|
+
},
|
|
484
|
+
{ total: 0, claude: 0, codex: 0, cursor: 0, windsurf: 0 }
|
|
485
|
+
);
|
|
486
|
+
return /* @__PURE__ */ jsxs5(
|
|
487
|
+
Box5,
|
|
488
|
+
{
|
|
489
|
+
flexDirection: "column",
|
|
490
|
+
flexShrink: 0,
|
|
491
|
+
borderStyle: "single",
|
|
492
|
+
borderColor: "gray",
|
|
493
|
+
width: 26,
|
|
494
|
+
paddingX: 1,
|
|
495
|
+
children: [
|
|
496
|
+
/* @__PURE__ */ jsx5(Box5, { marginBottom: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "white", children: "At A Glance" }) }),
|
|
497
|
+
/* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", gap: 1, children: [
|
|
498
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
499
|
+
/* @__PURE__ */ jsx5(Text4, { color: "green", children: "\u25CF " }),
|
|
500
|
+
/* @__PURE__ */ jsxs5(Text4, { color: "white", children: [
|
|
501
|
+
userStats.total,
|
|
502
|
+
" Online"
|
|
503
|
+
] })
|
|
504
|
+
] }),
|
|
505
|
+
userStats.claude > 0 && /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
506
|
+
/* @__PURE__ */ jsx5(Text4, { color: AGENT_CONFIG.claude.color, children: "\u25CF " }),
|
|
507
|
+
/* @__PURE__ */ jsxs5(Text4, { color: "white", children: [
|
|
508
|
+
userStats.claude,
|
|
509
|
+
" Using ",
|
|
510
|
+
AGENT_CONFIG.claude.displayName
|
|
511
|
+
] })
|
|
512
|
+
] }),
|
|
513
|
+
userStats.codex > 0 && /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
514
|
+
/* @__PURE__ */ jsx5(Text4, { color: AGENT_CONFIG.codex.color, children: "\u25CF " }),
|
|
515
|
+
/* @__PURE__ */ jsxs5(Text4, { color: "white", children: [
|
|
516
|
+
userStats.codex,
|
|
517
|
+
" Using ",
|
|
518
|
+
AGENT_CONFIG.codex.displayName
|
|
519
|
+
] })
|
|
520
|
+
] }),
|
|
521
|
+
userStats.cursor > 0 && /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
522
|
+
/* @__PURE__ */ jsx5(Text4, { color: AGENT_CONFIG.cursor.color, children: "\u25CF " }),
|
|
523
|
+
/* @__PURE__ */ jsxs5(Text4, { color: "white", children: [
|
|
524
|
+
userStats.cursor,
|
|
525
|
+
" Using ",
|
|
526
|
+
AGENT_CONFIG.cursor.displayName
|
|
527
|
+
] })
|
|
528
|
+
] }),
|
|
529
|
+
userStats.windsurf > 0 && /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
530
|
+
/* @__PURE__ */ jsx5(Text4, { color: AGENT_CONFIG.windsurf.color, children: "\u25CF " }),
|
|
531
|
+
/* @__PURE__ */ jsxs5(Text4, { color: "white", children: [
|
|
532
|
+
userStats.windsurf,
|
|
533
|
+
" Using ",
|
|
534
|
+
AGENT_CONFIG.windsurf.displayName
|
|
535
|
+
] })
|
|
536
|
+
] }),
|
|
537
|
+
userStats.total === 0 && /* @__PURE__ */ jsx5(Box5, { children: /* @__PURE__ */ jsx5(Text4, { color: "gray", children: "No users online" }) })
|
|
538
|
+
] })
|
|
539
|
+
]
|
|
540
|
+
}
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
var init_AtAGlance = __esm({
|
|
544
|
+
"src/components/AtAGlance.tsx"() {
|
|
545
|
+
"use strict";
|
|
546
|
+
init_constants();
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
427
550
|
// src/routes/Router.tsx
|
|
428
551
|
import { createContext, useContext, useState as useState2, useCallback } from "react";
|
|
429
|
-
import { jsx as
|
|
552
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
430
553
|
function useNavigation() {
|
|
431
554
|
const context = useContext(NavigationContext);
|
|
432
555
|
if (!context) {
|
|
@@ -450,7 +573,7 @@ function Router({ initialRoute = "menu", children }) {
|
|
|
450
573
|
return newHistory;
|
|
451
574
|
});
|
|
452
575
|
}, []);
|
|
453
|
-
return /* @__PURE__ */
|
|
576
|
+
return /* @__PURE__ */ jsx6(NavigationContext.Provider, { value: { route, navigate, goBack }, children });
|
|
454
577
|
}
|
|
455
578
|
var NavigationContext;
|
|
456
579
|
var init_Router = __esm({
|
|
@@ -462,8 +585,8 @@ var init_Router = __esm({
|
|
|
462
585
|
|
|
463
586
|
// src/components/Menu.tsx
|
|
464
587
|
import { useState as useState3, useEffect as useEffect2, useMemo } from "react";
|
|
465
|
-
import { Box as
|
|
466
|
-
import { jsx as
|
|
588
|
+
import { Box as Box6, Text as Text5, useInput as useInput3, useStdout as useStdout2 } from "ink";
|
|
589
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
467
590
|
function Menu({
|
|
468
591
|
width,
|
|
469
592
|
height,
|
|
@@ -475,7 +598,8 @@ function Menu({
|
|
|
475
598
|
topPadding = 0,
|
|
476
599
|
publicChannels,
|
|
477
600
|
privateChannels,
|
|
478
|
-
unreadCounts
|
|
601
|
+
unreadCounts,
|
|
602
|
+
aggregatedPresence
|
|
479
603
|
}) {
|
|
480
604
|
const { stdout } = useStdout2();
|
|
481
605
|
const { navigate } = useNavigation();
|
|
@@ -538,110 +662,114 @@ function Menu({
|
|
|
538
662
|
const headerHeight = 3;
|
|
539
663
|
const contentHeight = height - topPadding - headerHeight;
|
|
540
664
|
const privateStartIndex = sortedPublicChannels.length;
|
|
541
|
-
return /* @__PURE__ */
|
|
542
|
-
/* @__PURE__ */
|
|
665
|
+
return /* @__PURE__ */ jsxs6(Layout, { width, height, topPadding, children: [
|
|
666
|
+
/* @__PURE__ */ jsx7(Layout.Header, { children: /* @__PURE__ */ jsx7(
|
|
543
667
|
Header,
|
|
544
668
|
{
|
|
545
669
|
username,
|
|
546
670
|
roomName: "Menu",
|
|
547
671
|
connectionStatus,
|
|
548
672
|
onLogout,
|
|
549
|
-
title: /* @__PURE__ */
|
|
673
|
+
title: /* @__PURE__ */ jsx7(Text5, { bold: true, color: "cyan", children: "Menu" }),
|
|
550
674
|
showStatus: false
|
|
551
675
|
}
|
|
552
676
|
) }),
|
|
553
|
-
/* @__PURE__ */
|
|
554
|
-
|
|
555
|
-
/* @__PURE__ */
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
677
|
+
/* @__PURE__ */ jsx7(Layout.Content, { children: /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", height: contentHeight, children: [
|
|
678
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "row", flexGrow: 1, children: [
|
|
679
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", flexGrow: 1, padding: 2, children: [
|
|
680
|
+
sortedPublicChannels.length > 0 && /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 1, children: [
|
|
681
|
+
/* @__PURE__ */ jsx7(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx7(Text5, { bold: true, color: "white", children: "Global Channels" }) }),
|
|
682
|
+
sortedPublicChannels.map((channel, idx) => {
|
|
683
|
+
const isSelected = selectedIndex === idx;
|
|
684
|
+
const unreadCount = unreadCounts[channel.slug] || 0;
|
|
685
|
+
return /* @__PURE__ */ jsx7(
|
|
686
|
+
ChannelItem,
|
|
687
|
+
{
|
|
688
|
+
channel,
|
|
689
|
+
isSelected,
|
|
690
|
+
unreadCount
|
|
691
|
+
},
|
|
692
|
+
channel.id
|
|
693
|
+
);
|
|
694
|
+
})
|
|
695
|
+
] }),
|
|
696
|
+
privateChannels.length > 0 && /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 1, children: [
|
|
697
|
+
/* @__PURE__ */ jsx7(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx7(Text5, { bold: true, color: "white", children: "Private Channels" }) }),
|
|
698
|
+
privateChannels.map((channel, idx) => {
|
|
699
|
+
const absoluteIndex = privateStartIndex + idx;
|
|
700
|
+
const isSelected = selectedIndex === absoluteIndex;
|
|
701
|
+
const unreadCount = unreadCounts[channel.slug] || 0;
|
|
702
|
+
return /* @__PURE__ */ jsx7(
|
|
703
|
+
ChannelItem,
|
|
704
|
+
{
|
|
705
|
+
channel,
|
|
706
|
+
isSelected,
|
|
707
|
+
isPrivate: true,
|
|
708
|
+
unreadCount
|
|
709
|
+
},
|
|
710
|
+
channel.id
|
|
711
|
+
);
|
|
712
|
+
})
|
|
713
|
+
] }),
|
|
714
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginBottom: 1, children: [
|
|
715
|
+
privateChannels.length === 0 && /* @__PURE__ */ jsx7(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx7(Text5, { bold: true, color: "white", children: "Private Channels" }) }),
|
|
716
|
+
/* @__PURE__ */ jsx7(
|
|
717
|
+
ActionItem,
|
|
718
|
+
{
|
|
719
|
+
label: "+ Create New Private Channel",
|
|
720
|
+
isSelected: selectedIndex === allChannels.length
|
|
721
|
+
}
|
|
722
|
+
)
|
|
723
|
+
] }),
|
|
724
|
+
allChannels.length === 0 && /* @__PURE__ */ jsx7(Box6, { children: /* @__PURE__ */ jsx7(Text5, { color: "gray", children: "No channels available" }) })
|
|
725
|
+
] }),
|
|
726
|
+
/* @__PURE__ */ jsx7(Box6, { paddingRight: 2, paddingTop: 2, children: /* @__PURE__ */ jsx7(AtAGlance, { presenceState: aggregatedPresence }) })
|
|
597
727
|
] }),
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
/* @__PURE__ */ jsxs5(
|
|
601
|
-
Box5,
|
|
728
|
+
/* @__PURE__ */ jsx7(Box6, { paddingX: 2, paddingBottom: 2, children: /* @__PURE__ */ jsxs6(
|
|
729
|
+
Box6,
|
|
602
730
|
{
|
|
603
731
|
flexDirection: "column",
|
|
604
732
|
borderStyle: "single",
|
|
605
733
|
borderColor: "gray",
|
|
606
734
|
paddingX: 1,
|
|
607
735
|
children: [
|
|
608
|
-
/* @__PURE__ */
|
|
609
|
-
/* @__PURE__ */
|
|
736
|
+
/* @__PURE__ */ jsxs6(Text5, { color: "gray", children: [
|
|
737
|
+
/* @__PURE__ */ jsx7(Text5, { color: "cyan", children: "Up/Down" }),
|
|
610
738
|
" Navigate channels"
|
|
611
739
|
] }),
|
|
612
|
-
/* @__PURE__ */
|
|
613
|
-
/* @__PURE__ */
|
|
740
|
+
/* @__PURE__ */ jsxs6(Text5, { color: "gray", children: [
|
|
741
|
+
/* @__PURE__ */ jsx7(Text5, { color: "cyan", children: "Enter" }),
|
|
614
742
|
" Join selected channel"
|
|
615
743
|
] }),
|
|
616
|
-
/* @__PURE__ */
|
|
617
|
-
/* @__PURE__ */
|
|
744
|
+
/* @__PURE__ */ jsxs6(Text5, { color: "gray", children: [
|
|
745
|
+
/* @__PURE__ */ jsx7(Text5, { color: "cyan", children: "ESC" }),
|
|
618
746
|
" Back to chat"
|
|
619
747
|
] }),
|
|
620
|
-
/* @__PURE__ */
|
|
621
|
-
/* @__PURE__ */
|
|
748
|
+
/* @__PURE__ */ jsxs6(Text5, { color: "gray", children: [
|
|
749
|
+
/* @__PURE__ */ jsx7(Text5, { color: "cyan", children: "Ctrl+C" }),
|
|
622
750
|
" Exit the app"
|
|
623
751
|
] })
|
|
624
752
|
]
|
|
625
753
|
}
|
|
626
|
-
)
|
|
754
|
+
) })
|
|
627
755
|
] }) })
|
|
628
756
|
] });
|
|
629
757
|
}
|
|
630
758
|
function ChannelItem({ channel, isSelected, isPrivate = false, unreadCount = 0 }) {
|
|
631
|
-
return /* @__PURE__ */
|
|
632
|
-
/* @__PURE__ */
|
|
759
|
+
return /* @__PURE__ */ jsxs6(Box6, { marginLeft: 2, children: [
|
|
760
|
+
/* @__PURE__ */ jsxs6(Text5, { color: isSelected ? "green" : "white", bold: isSelected, children: [
|
|
633
761
|
isSelected ? "> " : " ",
|
|
634
|
-
isPrivate && /* @__PURE__ */
|
|
762
|
+
isPrivate && /* @__PURE__ */ jsx7(Text5, { color: "yellow", children: "\u{1F512} " }),
|
|
635
763
|
"#",
|
|
636
764
|
channel.name || channel.slug,
|
|
637
|
-
unreadCount > 0 && /* @__PURE__ */
|
|
765
|
+
unreadCount > 0 && /* @__PURE__ */ jsxs6(Text5, { color: "green", bold: true, children: [
|
|
638
766
|
" ",
|
|
639
767
|
"(",
|
|
640
768
|
unreadCount,
|
|
641
769
|
")"
|
|
642
770
|
] })
|
|
643
771
|
] }),
|
|
644
|
-
isSelected && channel.description && /* @__PURE__ */
|
|
772
|
+
isSelected && channel.description && /* @__PURE__ */ jsxs6(Text5, { color: "gray", dimColor: true, children: [
|
|
645
773
|
" ",
|
|
646
774
|
"- ",
|
|
647
775
|
channel.description
|
|
@@ -649,7 +777,7 @@ function ChannelItem({ channel, isSelected, isPrivate = false, unreadCount = 0 }
|
|
|
649
777
|
] });
|
|
650
778
|
}
|
|
651
779
|
function ActionItem({ label, isSelected }) {
|
|
652
|
-
return /* @__PURE__ */
|
|
780
|
+
return /* @__PURE__ */ jsx7(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsxs6(Text5, { color: isSelected ? "green" : "cyan", bold: isSelected, children: [
|
|
653
781
|
isSelected ? "> " : " ",
|
|
654
782
|
label
|
|
655
783
|
] }) });
|
|
@@ -659,13 +787,14 @@ var init_Menu = __esm({
|
|
|
659
787
|
"use strict";
|
|
660
788
|
init_Header();
|
|
661
789
|
init_Layout();
|
|
790
|
+
init_AtAGlance();
|
|
662
791
|
init_Router();
|
|
663
792
|
}
|
|
664
793
|
});
|
|
665
794
|
|
|
666
795
|
// src/components/MessageItem.tsx
|
|
667
|
-
import { Box as
|
|
668
|
-
import { jsx as
|
|
796
|
+
import { Box as Box7, Text as Text6 } from "ink";
|
|
797
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
669
798
|
function getUsernameColor(username) {
|
|
670
799
|
const colors = [
|
|
671
800
|
"cyan",
|
|
@@ -693,30 +822,30 @@ function MessageItem({ message, isOwnMessage }) {
|
|
|
693
822
|
const time = formatTime(message.timestamp);
|
|
694
823
|
const usernameColor = getUsernameColor(message.username);
|
|
695
824
|
if (isOwnMessage) {
|
|
696
|
-
return /* @__PURE__ */
|
|
697
|
-
/* @__PURE__ */
|
|
698
|
-
/* @__PURE__ */
|
|
825
|
+
return /* @__PURE__ */ jsx8(Box7, { justifyContent: "flex-end", paddingY: 0, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", alignItems: "flex-end", children: [
|
|
826
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
827
|
+
/* @__PURE__ */ jsxs7(Text6, { color: "gray", children: [
|
|
699
828
|
"[",
|
|
700
829
|
time,
|
|
701
830
|
"] "
|
|
702
831
|
] }),
|
|
703
|
-
/* @__PURE__ */
|
|
704
|
-
/* @__PURE__ */
|
|
832
|
+
/* @__PURE__ */ jsx8(Text6, { color: usernameColor, bold: true, children: message.username }),
|
|
833
|
+
/* @__PURE__ */ jsx8(Text6, { color: "gray", children: " \u2192" })
|
|
705
834
|
] }),
|
|
706
|
-
/* @__PURE__ */
|
|
835
|
+
/* @__PURE__ */ jsx8(Box7, { paddingLeft: 2, children: /* @__PURE__ */ jsx8(Text6, { children: message.content }) })
|
|
707
836
|
] }) });
|
|
708
837
|
}
|
|
709
|
-
return /* @__PURE__ */
|
|
710
|
-
/* @__PURE__ */
|
|
711
|
-
/* @__PURE__ */
|
|
712
|
-
/* @__PURE__ */
|
|
713
|
-
/* @__PURE__ */
|
|
838
|
+
return /* @__PURE__ */ jsx8(Box7, { justifyContent: "flex-start", paddingY: 0, children: /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
839
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
840
|
+
/* @__PURE__ */ jsx8(Text6, { color: "gray", children: "\u2190 " }),
|
|
841
|
+
/* @__PURE__ */ jsx8(Text6, { color: usernameColor, bold: true, children: message.username }),
|
|
842
|
+
/* @__PURE__ */ jsxs7(Text6, { color: "gray", children: [
|
|
714
843
|
" [",
|
|
715
844
|
time,
|
|
716
845
|
"]"
|
|
717
846
|
] })
|
|
718
847
|
] }),
|
|
719
|
-
/* @__PURE__ */
|
|
848
|
+
/* @__PURE__ */ jsx8(Box7, { paddingLeft: 2, children: /* @__PURE__ */ jsx8(Text6, { children: message.content }) })
|
|
720
849
|
] }) });
|
|
721
850
|
}
|
|
722
851
|
var init_MessageItem = __esm({
|
|
@@ -727,8 +856,8 @@ var init_MessageItem = __esm({
|
|
|
727
856
|
|
|
728
857
|
// src/components/MessageList.tsx
|
|
729
858
|
import { useMemo as useMemo2 } from "react";
|
|
730
|
-
import { Box as
|
|
731
|
-
import { jsx as
|
|
859
|
+
import { Box as Box8, Text as Text7 } from "ink";
|
|
860
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
732
861
|
function MessageList({
|
|
733
862
|
messages,
|
|
734
863
|
currentUsername,
|
|
@@ -745,16 +874,16 @@ function MessageList({
|
|
|
745
874
|
const startIndex = Math.max(0, endIndex - maxMessages);
|
|
746
875
|
return messages.slice(startIndex, endIndex);
|
|
747
876
|
}, [messages, height, scrollOffset]);
|
|
748
|
-
return /* @__PURE__ */
|
|
749
|
-
|
|
877
|
+
return /* @__PURE__ */ jsxs8(
|
|
878
|
+
Box8,
|
|
750
879
|
{
|
|
751
880
|
flexDirection: "column",
|
|
752
881
|
height,
|
|
753
882
|
paddingX: 1,
|
|
754
883
|
overflow: "hidden",
|
|
755
884
|
children: [
|
|
756
|
-
/* @__PURE__ */
|
|
757
|
-
messages.length === 0 ? /* @__PURE__ */
|
|
885
|
+
/* @__PURE__ */ jsx9(Box8, { flexGrow: 1 }),
|
|
886
|
+
messages.length === 0 ? /* @__PURE__ */ jsx9(Box8, { justifyContent: "center", paddingY: 2, children: /* @__PURE__ */ jsx9(Text7, { color: "gray", children: "No messages yet. Say hello!" }) }) : visibleMessages.map((message) => /* @__PURE__ */ jsx9(
|
|
758
887
|
MessageItem,
|
|
759
888
|
{
|
|
760
889
|
message,
|
|
@@ -762,12 +891,12 @@ function MessageList({
|
|
|
762
891
|
},
|
|
763
892
|
message.id
|
|
764
893
|
)),
|
|
765
|
-
isDetached && /* @__PURE__ */
|
|
894
|
+
isDetached && /* @__PURE__ */ jsx9(Box8, { justifyContent: "center", children: /* @__PURE__ */ jsxs8(Text7, { color: "yellow", bold: true, children: [
|
|
766
895
|
"-- ",
|
|
767
896
|
scrollOffset,
|
|
768
897
|
" more below (\u2193 to scroll down) --"
|
|
769
898
|
] }) }),
|
|
770
|
-
othersTyping.length > 0 && !isDetached && /* @__PURE__ */
|
|
899
|
+
othersTyping.length > 0 && !isDetached && /* @__PURE__ */ jsx9(Box8, { paddingTop: 1, children: /* @__PURE__ */ jsx9(Text7, { color: "gray", italic: true, children: othersTyping.length === 1 ? `${othersTyping[0]} is typing...` : `${othersTyping.join(", ")} are typing...` }) })
|
|
771
900
|
]
|
|
772
901
|
}
|
|
773
902
|
);
|
|
@@ -780,8 +909,8 @@ var init_MessageList = __esm({
|
|
|
780
909
|
});
|
|
781
910
|
|
|
782
911
|
// src/components/UserList.tsx
|
|
783
|
-
import { Box as
|
|
784
|
-
import { Fragment as Fragment4, jsx as
|
|
912
|
+
import { Box as Box9, Text as Text8 } from "ink";
|
|
913
|
+
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
785
914
|
function UserList({
|
|
786
915
|
users,
|
|
787
916
|
currentUsername,
|
|
@@ -797,8 +926,8 @@ function UserList({
|
|
|
797
926
|
if (!a.isOnline && b.isOnline) return 1;
|
|
798
927
|
return 0;
|
|
799
928
|
});
|
|
800
|
-
return /* @__PURE__ */
|
|
801
|
-
|
|
929
|
+
return /* @__PURE__ */ jsxs9(
|
|
930
|
+
Box9,
|
|
802
931
|
{
|
|
803
932
|
flexDirection: "column",
|
|
804
933
|
flexShrink: 0,
|
|
@@ -810,36 +939,36 @@ function UserList({
|
|
|
810
939
|
marginBottom: 1,
|
|
811
940
|
overflow: "hidden",
|
|
812
941
|
children: [
|
|
813
|
-
/* @__PURE__ */
|
|
814
|
-
/* @__PURE__ */
|
|
815
|
-
/* @__PURE__ */
|
|
942
|
+
/* @__PURE__ */ jsx10(Box9, { marginBottom: 1, children: isPrivateChannel ? /* @__PURE__ */ jsx10(Text8, { color: "white", bold: true, children: "MEMBERS" }) : /* @__PURE__ */ jsxs9(Fragment4, { children: [
|
|
943
|
+
/* @__PURE__ */ jsx10(Text8, { color: "green", bold: true, children: "\u25CF " }),
|
|
944
|
+
/* @__PURE__ */ jsx10(Text8, { color: "white", bold: true, children: "ONLINE USERS" })
|
|
816
945
|
] }) }),
|
|
817
|
-
/* @__PURE__ */
|
|
946
|
+
/* @__PURE__ */ jsx10(Box9, { marginBottom: 1, children: isPrivateChannel ? /* @__PURE__ */ jsxs9(Text8, { color: "cyan", children: [
|
|
818
947
|
"[",
|
|
819
948
|
onlineCount,
|
|
820
949
|
" online]"
|
|
821
|
-
] }) : /* @__PURE__ */
|
|
950
|
+
] }) : /* @__PURE__ */ jsxs9(Text8, { color: "cyan", children: [
|
|
822
951
|
"[",
|
|
823
952
|
onlineCount,
|
|
824
953
|
" connected]"
|
|
825
954
|
] }) }),
|
|
826
|
-
/* @__PURE__ */
|
|
955
|
+
/* @__PURE__ */ jsx10(Box9, { flexDirection: "column", children: sortedUsers.map((user) => {
|
|
827
956
|
const isTruncated = user.username.length > 8;
|
|
828
957
|
const displayName = isTruncated ? user.username.substring(0, 8) : user.username;
|
|
829
|
-
return /* @__PURE__ */
|
|
830
|
-
/* @__PURE__ */
|
|
831
|
-
/* @__PURE__ */
|
|
832
|
-
/* @__PURE__ */
|
|
833
|
-
/* @__PURE__ */
|
|
958
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
959
|
+
/* @__PURE__ */ jsxs9(Box9, { children: [
|
|
960
|
+
/* @__PURE__ */ jsx10(Text8, { color: user.isOnline ? "green" : "gray", children: "\u25CF" }),
|
|
961
|
+
/* @__PURE__ */ jsx10(Text8, { children: " " }),
|
|
962
|
+
/* @__PURE__ */ jsxs9(Text8, { color: user.username === currentUsername ? "yellow" : "white", children: [
|
|
834
963
|
displayName,
|
|
835
964
|
isTruncated && "\u2026"
|
|
836
965
|
] }),
|
|
837
|
-
user.username === currentUsername && /* @__PURE__ */
|
|
838
|
-
user.role === "admin" && /* @__PURE__ */
|
|
966
|
+
user.username === currentUsername && /* @__PURE__ */ jsx10(Text8, { color: "gray", children: " (you)" }),
|
|
967
|
+
user.role === "admin" && /* @__PURE__ */ jsx10(Text8, { color: "yellow", children: " \u2605" })
|
|
839
968
|
] }),
|
|
840
|
-
user.currentAgent && /* @__PURE__ */
|
|
969
|
+
user.currentAgent && /* @__PURE__ */ jsx10(Box9, { marginLeft: 2, children: /* @__PURE__ */ jsxs9(Text8, { color: getAgentColor(user.currentAgent), children: [
|
|
841
970
|
"\u2937 Using ",
|
|
842
|
-
user.currentAgent
|
|
971
|
+
getAgentDisplayName(user.currentAgent)
|
|
843
972
|
] }) })
|
|
844
973
|
] }, user.username);
|
|
845
974
|
}) })
|
|
@@ -850,14 +979,15 @@ function UserList({
|
|
|
850
979
|
var init_UserList = __esm({
|
|
851
980
|
"src/components/UserList.tsx"() {
|
|
852
981
|
"use strict";
|
|
982
|
+
init_constants();
|
|
853
983
|
}
|
|
854
984
|
});
|
|
855
985
|
|
|
856
986
|
// src/components/InputBox.tsx
|
|
857
987
|
import { useState as useState4, useCallback as useCallback2, useRef, useEffect as useEffect3 } from "react";
|
|
858
|
-
import { Box as
|
|
988
|
+
import { Box as Box10, Text as Text9 } from "ink";
|
|
859
989
|
import TextInput from "ink-text-input";
|
|
860
|
-
import { jsx as
|
|
990
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
861
991
|
function InputBox({
|
|
862
992
|
onSend,
|
|
863
993
|
onTypingStart,
|
|
@@ -916,8 +1046,8 @@ function InputBox({
|
|
|
916
1046
|
}
|
|
917
1047
|
};
|
|
918
1048
|
}, []);
|
|
919
|
-
return /* @__PURE__ */
|
|
920
|
-
|
|
1049
|
+
return /* @__PURE__ */ jsxs10(
|
|
1050
|
+
Box10,
|
|
921
1051
|
{
|
|
922
1052
|
borderStyle: "single",
|
|
923
1053
|
borderColor: "gray",
|
|
@@ -926,9 +1056,9 @@ function InputBox({
|
|
|
926
1056
|
width: "100%",
|
|
927
1057
|
flexShrink: 0,
|
|
928
1058
|
children: [
|
|
929
|
-
/* @__PURE__ */
|
|
930
|
-
/* @__PURE__ */
|
|
931
|
-
/* @__PURE__ */
|
|
1059
|
+
/* @__PURE__ */ jsxs10(Box10, { children: [
|
|
1060
|
+
/* @__PURE__ */ jsx11(Text9, { color: "cyan", children: "$ " }),
|
|
1061
|
+
/* @__PURE__ */ jsx11(Box10, { flexGrow: 1, children: /* @__PURE__ */ jsx11(
|
|
932
1062
|
TextInput,
|
|
933
1063
|
{
|
|
934
1064
|
value,
|
|
@@ -937,12 +1067,12 @@ function InputBox({
|
|
|
937
1067
|
placeholder: disabled ? "Connecting..." : "Type a message..."
|
|
938
1068
|
}
|
|
939
1069
|
) }),
|
|
940
|
-
/* @__PURE__ */
|
|
1070
|
+
/* @__PURE__ */ jsxs10(Text9, { color: disabled || !value.trim() ? "gray" : "green", children: [
|
|
941
1071
|
" ",
|
|
942
1072
|
"[SEND]"
|
|
943
1073
|
] })
|
|
944
1074
|
] }),
|
|
945
|
-
/* @__PURE__ */
|
|
1075
|
+
/* @__PURE__ */ jsx11(Box10, { children: /* @__PURE__ */ jsx11(Text9, { color: "gray", dimColor: true, children: "Enter to send" }) })
|
|
946
1076
|
]
|
|
947
1077
|
}
|
|
948
1078
|
);
|
|
@@ -954,8 +1084,8 @@ var init_InputBox = __esm({
|
|
|
954
1084
|
});
|
|
955
1085
|
|
|
956
1086
|
// src/components/StatusBar.tsx
|
|
957
|
-
import { Box as
|
|
958
|
-
import { Fragment as Fragment5, jsx as
|
|
1087
|
+
import { Box as Box11, Text as Text10 } from "ink";
|
|
1088
|
+
import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
959
1089
|
function StatusBar({
|
|
960
1090
|
connectionStatus,
|
|
961
1091
|
error,
|
|
@@ -963,8 +1093,8 @@ function StatusBar({
|
|
|
963
1093
|
}) {
|
|
964
1094
|
const presenceText = connectionStatus === "connected" ? "Active" : connectionStatus === "connecting" ? "Connecting" : "Disconnected";
|
|
965
1095
|
const presenceColor = connectionStatus === "connected" ? "green" : connectionStatus === "connecting" ? "yellow" : "red";
|
|
966
|
-
return /* @__PURE__ */
|
|
967
|
-
|
|
1096
|
+
return /* @__PURE__ */ jsxs11(
|
|
1097
|
+
Box11,
|
|
968
1098
|
{
|
|
969
1099
|
borderStyle: "single",
|
|
970
1100
|
borderColor: "gray",
|
|
@@ -973,18 +1103,18 @@ function StatusBar({
|
|
|
973
1103
|
width: "100%",
|
|
974
1104
|
flexShrink: 0,
|
|
975
1105
|
children: [
|
|
976
|
-
/* @__PURE__ */
|
|
1106
|
+
/* @__PURE__ */ jsx12(Box11, { children: error ? /* @__PURE__ */ jsxs11(Text10, { color: "red", children: [
|
|
977
1107
|
"[Error: ",
|
|
978
1108
|
error,
|
|
979
1109
|
"]"
|
|
980
|
-
] }) : /* @__PURE__ */
|
|
981
|
-
/* @__PURE__ */
|
|
982
|
-
/* @__PURE__ */
|
|
1110
|
+
] }) : /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
1111
|
+
/* @__PURE__ */ jsx12(Text10, { color: "gray", children: "\u2192 Presence: " }),
|
|
1112
|
+
/* @__PURE__ */ jsx12(Text10, { color: presenceColor, children: presenceText })
|
|
983
1113
|
] }) }),
|
|
984
|
-
/* @__PURE__ */
|
|
985
|
-
/* @__PURE__ */
|
|
986
|
-
/* @__PURE__ */
|
|
987
|
-
/* @__PURE__ */
|
|
1114
|
+
/* @__PURE__ */ jsxs11(Box11, { children: [
|
|
1115
|
+
/* @__PURE__ */ jsx12(Text10, { color: "gray", children: "Users: " }),
|
|
1116
|
+
/* @__PURE__ */ jsx12(Text10, { color: "cyan", children: userCount }),
|
|
1117
|
+
/* @__PURE__ */ jsx12(Text10, { color: "gray", children: " | \u2191/\u2193 scroll | Ctrl+E users | Ctrl+C exit" })
|
|
988
1118
|
] })
|
|
989
1119
|
]
|
|
990
1120
|
}
|
|
@@ -997,21 +1127,21 @@ var init_StatusBar = __esm({
|
|
|
997
1127
|
});
|
|
998
1128
|
|
|
999
1129
|
// src/components/ToolTip.tsx
|
|
1000
|
-
import { Box as
|
|
1001
|
-
import { jsx as
|
|
1130
|
+
import { Box as Box12, Text as Text11 } from "ink";
|
|
1131
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1002
1132
|
var ToolTip;
|
|
1003
1133
|
var init_ToolTip = __esm({
|
|
1004
1134
|
"src/components/ToolTip.tsx"() {
|
|
1005
1135
|
"use strict";
|
|
1006
1136
|
ToolTip = ({ tips, type }) => {
|
|
1007
|
-
return /* @__PURE__ */
|
|
1008
|
-
/* @__PURE__ */
|
|
1009
|
-
type === "Command" && tips.map((tip) => /* @__PURE__ */
|
|
1010
|
-
/* @__PURE__ */
|
|
1137
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", paddingX: 1, children: [
|
|
1138
|
+
/* @__PURE__ */ jsx13(Text11, { children: " " }),
|
|
1139
|
+
type === "Command" && tips.map((tip) => /* @__PURE__ */ jsxs12(Text11, { color: "gray", children: [
|
|
1140
|
+
/* @__PURE__ */ jsx13(Text11, { color: "cyan", children: tip.syntax }),
|
|
1011
1141
|
" - ",
|
|
1012
1142
|
tip.description
|
|
1013
1143
|
] }, tip.name)),
|
|
1014
|
-
type === "User" && tips.map((suggestion) => /* @__PURE__ */
|
|
1144
|
+
type === "User" && tips.map((suggestion) => /* @__PURE__ */ jsx13(Text11, { color: "gray", children: /* @__PURE__ */ jsx13(Text11, { color: "cyan", children: suggestion }) }, suggestion))
|
|
1015
1145
|
] });
|
|
1016
1146
|
};
|
|
1017
1147
|
}
|
|
@@ -1592,8 +1722,8 @@ var init_use_command_input = __esm({
|
|
|
1592
1722
|
|
|
1593
1723
|
// src/components/ChatView.tsx
|
|
1594
1724
|
import { useEffect as useEffect5 } from "react";
|
|
1595
|
-
import { Box as
|
|
1596
|
-
import { Fragment as Fragment6, jsx as
|
|
1725
|
+
import { Box as Box13, Text as Text12, useStdout as useStdout3 } from "ink";
|
|
1726
|
+
import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1597
1727
|
function ChatView({
|
|
1598
1728
|
terminalSize,
|
|
1599
1729
|
currentChannel,
|
|
@@ -1638,34 +1768,34 @@ function ChatView({
|
|
|
1638
1768
|
const prefix = connectionStatus === "connected" ? "\u2022 " : "";
|
|
1639
1769
|
stdout.write(`\x1B]0;${prefix}#${displayName}\x07`);
|
|
1640
1770
|
}, [stdout, connectionStatus, displayName]);
|
|
1641
|
-
return /* @__PURE__ */
|
|
1642
|
-
/* @__PURE__ */
|
|
1771
|
+
return /* @__PURE__ */ jsxs13(Layout, { width: terminalSize.columns, height: terminalSize.rows, topPadding, children: [
|
|
1772
|
+
/* @__PURE__ */ jsx14(Layout.Header, { children: /* @__PURE__ */ jsx14(
|
|
1643
1773
|
Header,
|
|
1644
1774
|
{
|
|
1645
1775
|
username,
|
|
1646
1776
|
roomName: currentChannel,
|
|
1647
1777
|
connectionStatus,
|
|
1648
1778
|
onLogout,
|
|
1649
|
-
title: /* @__PURE__ */
|
|
1650
|
-
/* @__PURE__ */
|
|
1651
|
-
/* @__PURE__ */
|
|
1652
|
-
/* @__PURE__ */
|
|
1653
|
-
/* @__PURE__ */
|
|
1779
|
+
title: /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
1780
|
+
/* @__PURE__ */ jsx14(Text12, { color: "gray", children: "\u2190 Menu " }),
|
|
1781
|
+
/* @__PURE__ */ jsx14(Text12, { color: "gray", dimColor: true, children: "[CTRL+Q]" }),
|
|
1782
|
+
/* @__PURE__ */ jsx14(Text12, { color: "gray", children: " | " }),
|
|
1783
|
+
/* @__PURE__ */ jsxs13(Text12, { color: "cyan", bold: true, children: [
|
|
1654
1784
|
"#",
|
|
1655
1785
|
displayText
|
|
1656
1786
|
] })
|
|
1657
1787
|
] })
|
|
1658
1788
|
}
|
|
1659
1789
|
) }),
|
|
1660
|
-
/* @__PURE__ */
|
|
1661
|
-
/* @__PURE__ */
|
|
1662
|
-
|
|
1790
|
+
/* @__PURE__ */ jsxs13(Layout.Content, { children: [
|
|
1791
|
+
/* @__PURE__ */ jsxs13(
|
|
1792
|
+
Box13,
|
|
1663
1793
|
{
|
|
1664
1794
|
flexDirection: "row",
|
|
1665
1795
|
height: Math.max(1, middleSectionHeight - tooltip.height),
|
|
1666
1796
|
overflow: "hidden",
|
|
1667
1797
|
children: [
|
|
1668
|
-
/* @__PURE__ */
|
|
1798
|
+
/* @__PURE__ */ jsx14(Box13, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children: /* @__PURE__ */ jsx14(
|
|
1669
1799
|
MessageList,
|
|
1670
1800
|
{
|
|
1671
1801
|
messages,
|
|
@@ -1676,7 +1806,7 @@ function ChatView({
|
|
|
1676
1806
|
isDetached
|
|
1677
1807
|
}
|
|
1678
1808
|
) }),
|
|
1679
|
-
showUserList && /* @__PURE__ */
|
|
1809
|
+
showUserList && /* @__PURE__ */ jsx14(
|
|
1680
1810
|
UserList,
|
|
1681
1811
|
{
|
|
1682
1812
|
users,
|
|
@@ -1688,8 +1818,8 @@ function ChatView({
|
|
|
1688
1818
|
]
|
|
1689
1819
|
}
|
|
1690
1820
|
),
|
|
1691
|
-
tooltip.show && tooltip.tips.length > 0 && /* @__PURE__ */
|
|
1692
|
-
/* @__PURE__ */
|
|
1821
|
+
tooltip.show && tooltip.tips.length > 0 && /* @__PURE__ */ jsx14(ToolTip, { tips: tooltip.tips, type: tooltip.type }),
|
|
1822
|
+
/* @__PURE__ */ jsx14(
|
|
1693
1823
|
InputBox,
|
|
1694
1824
|
{
|
|
1695
1825
|
onSend: handleSubmit,
|
|
@@ -1699,7 +1829,7 @@ function ChatView({
|
|
|
1699
1829
|
disabled: isInputDisabled
|
|
1700
1830
|
}
|
|
1701
1831
|
),
|
|
1702
|
-
/* @__PURE__ */
|
|
1832
|
+
/* @__PURE__ */ jsx14(
|
|
1703
1833
|
StatusBar,
|
|
1704
1834
|
{
|
|
1705
1835
|
connectionStatus,
|
|
@@ -1726,9 +1856,9 @@ var init_ChatView = __esm({
|
|
|
1726
1856
|
|
|
1727
1857
|
// src/components/CreateChannelScreen.tsx
|
|
1728
1858
|
import { useState as useState7, useEffect as useEffect6 } from "react";
|
|
1729
|
-
import { Box as
|
|
1859
|
+
import { Box as Box14, Text as Text13, useInput as useInput5, useStdout as useStdout4 } from "ink";
|
|
1730
1860
|
import TextInput2 from "ink-text-input";
|
|
1731
|
-
import { jsx as
|
|
1861
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1732
1862
|
function CreateChannelScreen({
|
|
1733
1863
|
width,
|
|
1734
1864
|
height,
|
|
@@ -1807,65 +1937,65 @@ function CreateChannelScreen({
|
|
|
1807
1937
|
};
|
|
1808
1938
|
const headerHeight = 3;
|
|
1809
1939
|
const contentHeight = height - topPadding - headerHeight;
|
|
1810
|
-
return /* @__PURE__ */
|
|
1811
|
-
/* @__PURE__ */
|
|
1940
|
+
return /* @__PURE__ */ jsxs14(Layout, { width, height, topPadding, children: [
|
|
1941
|
+
/* @__PURE__ */ jsx15(Layout.Header, { children: /* @__PURE__ */ jsx15(
|
|
1812
1942
|
Header,
|
|
1813
1943
|
{
|
|
1814
1944
|
username,
|
|
1815
1945
|
roomName: "Create Channel",
|
|
1816
1946
|
connectionStatus,
|
|
1817
1947
|
onLogout,
|
|
1818
|
-
title: /* @__PURE__ */
|
|
1948
|
+
title: /* @__PURE__ */ jsx15(Text13, { bold: true, color: "cyan", children: "Create New Private Channel" }),
|
|
1819
1949
|
showStatus: false
|
|
1820
1950
|
}
|
|
1821
1951
|
) }),
|
|
1822
|
-
/* @__PURE__ */
|
|
1823
|
-
/* @__PURE__ */
|
|
1824
|
-
/* @__PURE__ */
|
|
1952
|
+
/* @__PURE__ */ jsx15(Layout.Content, { children: /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", height: contentHeight, padding: 2, children: [
|
|
1953
|
+
/* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginBottom: 1, children: [
|
|
1954
|
+
/* @__PURE__ */ jsx15(Box14, { marginBottom: 0, children: /* @__PURE__ */ jsxs14(Text13, { bold: true, color: activeField === "name" ? "green" : "white", children: [
|
|
1825
1955
|
"Channel Name ",
|
|
1826
1956
|
activeField === "name" ? "(editing)" : ""
|
|
1827
1957
|
] }) }),
|
|
1828
|
-
/* @__PURE__ */
|
|
1829
|
-
|
|
1958
|
+
/* @__PURE__ */ jsx15(
|
|
1959
|
+
Box14,
|
|
1830
1960
|
{
|
|
1831
1961
|
borderStyle: "single",
|
|
1832
1962
|
borderColor: activeField === "name" ? "green" : "gray",
|
|
1833
1963
|
paddingX: 1,
|
|
1834
|
-
children: activeField === "name" ? /* @__PURE__ */
|
|
1964
|
+
children: activeField === "name" ? /* @__PURE__ */ jsx15(
|
|
1835
1965
|
TextInput2,
|
|
1836
1966
|
{
|
|
1837
1967
|
value: name,
|
|
1838
1968
|
onChange: setName,
|
|
1839
1969
|
placeholder: "Enter channel name..."
|
|
1840
1970
|
}
|
|
1841
|
-
) : /* @__PURE__ */
|
|
1971
|
+
) : /* @__PURE__ */ jsx15(Text13, { color: name ? "white" : "gray", children: name || "Enter channel name..." })
|
|
1842
1972
|
}
|
|
1843
1973
|
)
|
|
1844
1974
|
] }),
|
|
1845
|
-
/* @__PURE__ */
|
|
1846
|
-
/* @__PURE__ */
|
|
1975
|
+
/* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginBottom: 1, children: [
|
|
1976
|
+
/* @__PURE__ */ jsx15(Box14, { marginBottom: 0, children: /* @__PURE__ */ jsxs14(Text13, { bold: true, color: activeField === "description" ? "green" : "white", children: [
|
|
1847
1977
|
"Description (optional) ",
|
|
1848
1978
|
activeField === "description" ? "(editing)" : ""
|
|
1849
1979
|
] }) }),
|
|
1850
|
-
/* @__PURE__ */
|
|
1851
|
-
|
|
1980
|
+
/* @__PURE__ */ jsx15(
|
|
1981
|
+
Box14,
|
|
1852
1982
|
{
|
|
1853
1983
|
borderStyle: "single",
|
|
1854
1984
|
borderColor: activeField === "description" ? "green" : "gray",
|
|
1855
1985
|
paddingX: 1,
|
|
1856
|
-
children: activeField === "description" ? /* @__PURE__ */
|
|
1986
|
+
children: activeField === "description" ? /* @__PURE__ */ jsx15(
|
|
1857
1987
|
TextInput2,
|
|
1858
1988
|
{
|
|
1859
1989
|
value: description,
|
|
1860
1990
|
onChange: setDescription,
|
|
1861
1991
|
placeholder: "Enter channel description..."
|
|
1862
1992
|
}
|
|
1863
|
-
) : /* @__PURE__ */
|
|
1993
|
+
) : /* @__PURE__ */ jsx15(Text13, { color: description ? "white" : "gray", children: description || "Enter channel description..." })
|
|
1864
1994
|
}
|
|
1865
1995
|
)
|
|
1866
1996
|
] }),
|
|
1867
|
-
/* @__PURE__ */
|
|
1868
|
-
|
|
1997
|
+
/* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsxs14(
|
|
1998
|
+
Text13,
|
|
1869
1999
|
{
|
|
1870
2000
|
color: activeField === "submit" ? "green" : "white",
|
|
1871
2001
|
bold: activeField === "submit",
|
|
@@ -1877,30 +2007,30 @@ function CreateChannelScreen({
|
|
|
1877
2007
|
]
|
|
1878
2008
|
}
|
|
1879
2009
|
) }),
|
|
1880
|
-
error && /* @__PURE__ */
|
|
1881
|
-
/* @__PURE__ */
|
|
1882
|
-
/* @__PURE__ */
|
|
1883
|
-
|
|
2010
|
+
error && /* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text13, { color: "red", children: error }) }),
|
|
2011
|
+
/* @__PURE__ */ jsx15(Box14, { flexGrow: 1 }),
|
|
2012
|
+
/* @__PURE__ */ jsxs14(
|
|
2013
|
+
Box14,
|
|
1884
2014
|
{
|
|
1885
2015
|
flexDirection: "column",
|
|
1886
2016
|
borderStyle: "single",
|
|
1887
2017
|
borderColor: "gray",
|
|
1888
2018
|
paddingX: 1,
|
|
1889
2019
|
children: [
|
|
1890
|
-
/* @__PURE__ */
|
|
1891
|
-
/* @__PURE__ */
|
|
2020
|
+
/* @__PURE__ */ jsxs14(Text13, { color: "gray", children: [
|
|
2021
|
+
/* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "Tab/Down" }),
|
|
1892
2022
|
" Next field"
|
|
1893
2023
|
] }),
|
|
1894
|
-
/* @__PURE__ */
|
|
1895
|
-
/* @__PURE__ */
|
|
2024
|
+
/* @__PURE__ */ jsxs14(Text13, { color: "gray", children: [
|
|
2025
|
+
/* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "Shift+Tab/Up" }),
|
|
1896
2026
|
" Previous field"
|
|
1897
2027
|
] }),
|
|
1898
|
-
/* @__PURE__ */
|
|
1899
|
-
/* @__PURE__ */
|
|
2028
|
+
/* @__PURE__ */ jsxs14(Text13, { color: "gray", children: [
|
|
2029
|
+
/* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "Enter" }),
|
|
1900
2030
|
" Submit (when on button)"
|
|
1901
2031
|
] }),
|
|
1902
|
-
/* @__PURE__ */
|
|
1903
|
-
/* @__PURE__ */
|
|
2032
|
+
/* @__PURE__ */ jsxs14(Text13, { color: "gray", children: [
|
|
2033
|
+
/* @__PURE__ */ jsx15(Text13, { color: "cyan", children: "ESC" }),
|
|
1904
2034
|
" Back to menu"
|
|
1905
2035
|
] })
|
|
1906
2036
|
]
|
|
@@ -2303,6 +2433,50 @@ var init_channel_manager = __esm({
|
|
|
2303
2433
|
const channelState = this.channelStates.get(channelSlug);
|
|
2304
2434
|
return channelState?.presence || {};
|
|
2305
2435
|
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Get aggregated presence across all channels, deduplicated by user_id.
|
|
2438
|
+
* When a user appears in multiple channels, we prefer:
|
|
2439
|
+
* 1. Presence with current_agent set (if available)
|
|
2440
|
+
* 2. Most recent online_at timestamp
|
|
2441
|
+
*/
|
|
2442
|
+
getAggregatedPresence() {
|
|
2443
|
+
const userMap = /* @__PURE__ */ new Map();
|
|
2444
|
+
this.channelStates.forEach((channelState) => {
|
|
2445
|
+
Object.entries(channelState.presence).forEach(([username, data]) => {
|
|
2446
|
+
const meta = data.metas[0];
|
|
2447
|
+
if (!meta) return;
|
|
2448
|
+
const userId = meta.user_id;
|
|
2449
|
+
const hasAgent = !!meta.current_agent;
|
|
2450
|
+
const onlineAt = meta.online_at;
|
|
2451
|
+
const existing = userMap.get(userId);
|
|
2452
|
+
if (!existing) {
|
|
2453
|
+
userMap.set(userId, {
|
|
2454
|
+
username,
|
|
2455
|
+
metas: data.metas,
|
|
2456
|
+
online_at: onlineAt,
|
|
2457
|
+
has_agent: hasAgent
|
|
2458
|
+
});
|
|
2459
|
+
} else {
|
|
2460
|
+
const shouldReplace = hasAgent && !existing.has_agent || hasAgent === existing.has_agent && onlineAt > existing.online_at;
|
|
2461
|
+
if (shouldReplace) {
|
|
2462
|
+
userMap.set(userId, {
|
|
2463
|
+
username,
|
|
2464
|
+
metas: data.metas,
|
|
2465
|
+
online_at: onlineAt,
|
|
2466
|
+
has_agent: hasAgent
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
});
|
|
2471
|
+
});
|
|
2472
|
+
const aggregated = {};
|
|
2473
|
+
userMap.forEach((data) => {
|
|
2474
|
+
aggregated[data.username] = {
|
|
2475
|
+
metas: data.metas
|
|
2476
|
+
};
|
|
2477
|
+
});
|
|
2478
|
+
return aggregated;
|
|
2479
|
+
}
|
|
2306
2480
|
/**
|
|
2307
2481
|
* Get buffered real-time messages for a specific channel.
|
|
2308
2482
|
* These are messages that arrived while viewing other channels.
|
|
@@ -2369,6 +2543,20 @@ var init_channel_manager = __esm({
|
|
|
2369
2543
|
});
|
|
2370
2544
|
});
|
|
2371
2545
|
}
|
|
2546
|
+
/**
|
|
2547
|
+
* Best-effort mark as read without waiting for an ack.
|
|
2548
|
+
* Useful during shutdown paths to avoid timeouts.
|
|
2549
|
+
*/
|
|
2550
|
+
markChannelAsReadBestEffort(channelSlug) {
|
|
2551
|
+
const channelState = this.channelStates.get(channelSlug);
|
|
2552
|
+
if (!channelState || !channelState.channel) {
|
|
2553
|
+
return;
|
|
2554
|
+
}
|
|
2555
|
+
try {
|
|
2556
|
+
channelState.channel.push("mark_as_read", {});
|
|
2557
|
+
} catch {
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2372
2560
|
/**
|
|
2373
2561
|
* Mark all messages in channel as read (used when first joining).
|
|
2374
2562
|
* Gracefully handles disconnected channels (returns silently during shutdown).
|
|
@@ -2431,6 +2619,7 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2431
2619
|
const [typingUsers, setTypingUsers] = useState8([]);
|
|
2432
2620
|
const [presenceState, setPresenceState] = useState8({});
|
|
2433
2621
|
const [subscribers, setSubscribers] = useState8([]);
|
|
2622
|
+
const [channelsReady, setChannelsReady] = useState8(false);
|
|
2434
2623
|
const managerRef = useRef3(null);
|
|
2435
2624
|
const prevChannelRef = useRef3(null);
|
|
2436
2625
|
const isLoadingHistory = useRef3(false);
|
|
@@ -2440,6 +2629,7 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2440
2629
|
managerRef.current.disconnect();
|
|
2441
2630
|
managerRef.current = null;
|
|
2442
2631
|
}
|
|
2632
|
+
setChannelsReady(false);
|
|
2443
2633
|
return;
|
|
2444
2634
|
}
|
|
2445
2635
|
if (managerRef.current) {
|
|
@@ -2545,6 +2735,7 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2545
2735
|
...channelsResponse.channels.private
|
|
2546
2736
|
];
|
|
2547
2737
|
await manager.subscribeToChannels(allChannels);
|
|
2738
|
+
setChannelsReady(true);
|
|
2548
2739
|
setError(null);
|
|
2549
2740
|
} catch (err) {
|
|
2550
2741
|
setError(err instanceof Error ? err.message : "Connection failed");
|
|
@@ -2557,6 +2748,7 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2557
2748
|
managerRef.current.disconnect();
|
|
2558
2749
|
managerRef.current = null;
|
|
2559
2750
|
}
|
|
2751
|
+
setChannelsReady(false);
|
|
2560
2752
|
};
|
|
2561
2753
|
}, [token]);
|
|
2562
2754
|
useEffect7(() => {
|
|
@@ -2639,7 +2831,7 @@ function useMultiChannelChat(token, currentChannel, onChannelListChanged) {
|
|
|
2639
2831
|
// No-op for backward compatibility
|
|
2640
2832
|
disconnect,
|
|
2641
2833
|
// No-op for backward compatibility
|
|
2642
|
-
channelManager: managerRef.current
|
|
2834
|
+
channelManager: channelsReady ? managerRef.current : null
|
|
2643
2835
|
};
|
|
2644
2836
|
}
|
|
2645
2837
|
var init_use_multi_channel_chat = __esm({
|
|
@@ -2700,19 +2892,21 @@ var init_use_presence = __esm({
|
|
|
2700
2892
|
// src/hooks/use-agent-detection.ts
|
|
2701
2893
|
import { useEffect as useEffect8, useRef as useRef4, useCallback as useCallback4 } from "react";
|
|
2702
2894
|
import { execSync as execSync2 } from "child_process";
|
|
2703
|
-
function
|
|
2895
|
+
function detectCurrentAgent() {
|
|
2704
2896
|
try {
|
|
2705
|
-
execSync2(`
|
|
2706
|
-
|
|
2897
|
+
const result = execSync2(`ps -p $(pgrep -x -n 'codex|claude|Cursor|Windsurf\\ Helper') -o comm=`, {
|
|
2898
|
+
stdio: "pipe",
|
|
2899
|
+
encoding: "utf-8"
|
|
2900
|
+
}).trim();
|
|
2901
|
+
if (result.includes("@openai/codex")) return "codex";
|
|
2902
|
+
if (result === "claude") return "claude";
|
|
2903
|
+
if (result.includes("Cursor.app")) return "cursor";
|
|
2904
|
+
if (result.includes("Windsurf.app")) return "windsurf";
|
|
2905
|
+
return null;
|
|
2707
2906
|
} catch {
|
|
2708
|
-
return
|
|
2907
|
+
return null;
|
|
2709
2908
|
}
|
|
2710
2909
|
}
|
|
2711
|
-
function detectCurrentAgent() {
|
|
2712
|
-
if (isProcessRunning("codex")) return "codex";
|
|
2713
|
-
if (isProcessRunning("claude")) return "claude";
|
|
2714
|
-
return null;
|
|
2715
|
-
}
|
|
2716
2910
|
function useAgentDetection(channelManager, isConnected) {
|
|
2717
2911
|
const lastSentAgentRef = useRef4(void 0);
|
|
2718
2912
|
const broadcastAgentUpdate = useCallback4(
|
|
@@ -2818,10 +3012,10 @@ __export(App_exports, {
|
|
|
2818
3012
|
App: () => App
|
|
2819
3013
|
});
|
|
2820
3014
|
import { useState as useState10, useEffect as useEffect10, useCallback as useCallback6, useRef as useRef5 } from "react";
|
|
2821
|
-
import { Box as
|
|
2822
|
-
import { jsx as
|
|
3015
|
+
import { Box as Box15, useApp as useApp2, useInput as useInput6, useStdout as useStdout5 } from "ink";
|
|
3016
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2823
3017
|
function App() {
|
|
2824
|
-
return /* @__PURE__ */
|
|
3018
|
+
return /* @__PURE__ */ jsx16(Router, { initialRoute: "menu", children: /* @__PURE__ */ jsx16(AppContent, {}) });
|
|
2825
3019
|
}
|
|
2826
3020
|
function AppContent() {
|
|
2827
3021
|
const { exit } = useApp2();
|
|
@@ -2940,14 +3134,7 @@ function AppContent() {
|
|
|
2940
3134
|
useEffect10(() => {
|
|
2941
3135
|
return () => {
|
|
2942
3136
|
if (currentChannel && channelManager) {
|
|
2943
|
-
|
|
2944
|
-
try {
|
|
2945
|
-
await channelManager.markChannelAsRead(currentChannel);
|
|
2946
|
-
} catch (err) {
|
|
2947
|
-
console.error("Failed to mark as read on unmount:", err);
|
|
2948
|
-
}
|
|
2949
|
-
};
|
|
2950
|
-
markOnUnmount();
|
|
3137
|
+
channelManager.markChannelAsReadBestEffort(currentChannel);
|
|
2951
3138
|
}
|
|
2952
3139
|
};
|
|
2953
3140
|
}, [currentChannel, channelManager]);
|
|
@@ -2974,11 +3161,7 @@ function AppContent() {
|
|
|
2974
3161
|
}, []);
|
|
2975
3162
|
const handleLogout = useCallback6(async () => {
|
|
2976
3163
|
if (currentChannel && channelManager) {
|
|
2977
|
-
|
|
2978
|
-
await channelManager.markChannelAsRead(currentChannel);
|
|
2979
|
-
} catch (err) {
|
|
2980
|
-
console.error("Failed to mark as read on logout:", err);
|
|
2981
|
-
}
|
|
3164
|
+
channelManager.markChannelAsReadBestEffort(currentChannel);
|
|
2982
3165
|
}
|
|
2983
3166
|
disconnect();
|
|
2984
3167
|
setToken(null);
|
|
@@ -3009,18 +3192,11 @@ function AppContent() {
|
|
|
3009
3192
|
const maxVisibleMessages = Math.floor(middleSectionHeight / linesPerMessage);
|
|
3010
3193
|
useInput6((input, key) => {
|
|
3011
3194
|
if (input === "c" && key.ctrl) {
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
console.error("Failed to mark as read on exit:", err);
|
|
3018
|
-
}
|
|
3019
|
-
}
|
|
3020
|
-
disconnect();
|
|
3021
|
-
exit();
|
|
3022
|
-
};
|
|
3023
|
-
handleExit();
|
|
3195
|
+
if (currentChannel && channelManager) {
|
|
3196
|
+
channelManager.markChannelAsReadBestEffort(currentChannel);
|
|
3197
|
+
}
|
|
3198
|
+
disconnect();
|
|
3199
|
+
exit();
|
|
3024
3200
|
}
|
|
3025
3201
|
if (input === "o" && key.ctrl && authState === "authenticated") {
|
|
3026
3202
|
handleLogout();
|
|
@@ -3054,14 +3230,14 @@ function AppContent() {
|
|
|
3054
3230
|
}
|
|
3055
3231
|
});
|
|
3056
3232
|
if (authState !== "authenticated") {
|
|
3057
|
-
return /* @__PURE__ */
|
|
3058
|
-
|
|
3233
|
+
return /* @__PURE__ */ jsx16(
|
|
3234
|
+
Box15,
|
|
3059
3235
|
{
|
|
3060
3236
|
flexDirection: "column",
|
|
3061
3237
|
width: terminalSize.columns,
|
|
3062
3238
|
height: terminalSize.rows,
|
|
3063
3239
|
overflow: "hidden",
|
|
3064
|
-
children: /* @__PURE__ */
|
|
3240
|
+
children: /* @__PURE__ */ jsx16(
|
|
3065
3241
|
LoginScreen,
|
|
3066
3242
|
{
|
|
3067
3243
|
onLogin: handleLogin,
|
|
@@ -3073,14 +3249,15 @@ function AppContent() {
|
|
|
3073
3249
|
);
|
|
3074
3250
|
}
|
|
3075
3251
|
if (route === "menu") {
|
|
3076
|
-
|
|
3077
|
-
|
|
3252
|
+
const aggregatedPresence = channelManager?.getAggregatedPresence() || {};
|
|
3253
|
+
return /* @__PURE__ */ jsx16(
|
|
3254
|
+
Box15,
|
|
3078
3255
|
{
|
|
3079
3256
|
flexDirection: "column",
|
|
3080
3257
|
width: terminalSize.columns,
|
|
3081
3258
|
height: terminalSize.rows,
|
|
3082
3259
|
overflow: "hidden",
|
|
3083
|
-
children: /* @__PURE__ */
|
|
3260
|
+
children: /* @__PURE__ */ jsx16(
|
|
3084
3261
|
Menu,
|
|
3085
3262
|
{
|
|
3086
3263
|
width: terminalSize.columns,
|
|
@@ -3093,21 +3270,22 @@ function AppContent() {
|
|
|
3093
3270
|
topPadding,
|
|
3094
3271
|
publicChannels,
|
|
3095
3272
|
privateChannels,
|
|
3096
|
-
unreadCounts
|
|
3273
|
+
unreadCounts,
|
|
3274
|
+
aggregatedPresence
|
|
3097
3275
|
}
|
|
3098
3276
|
)
|
|
3099
3277
|
}
|
|
3100
3278
|
);
|
|
3101
3279
|
}
|
|
3102
3280
|
if (route === "create-channel") {
|
|
3103
|
-
return /* @__PURE__ */
|
|
3104
|
-
|
|
3281
|
+
return /* @__PURE__ */ jsx16(
|
|
3282
|
+
Box15,
|
|
3105
3283
|
{
|
|
3106
3284
|
flexDirection: "column",
|
|
3107
3285
|
width: terminalSize.columns,
|
|
3108
3286
|
height: terminalSize.rows,
|
|
3109
3287
|
overflow: "hidden",
|
|
3110
|
-
children: /* @__PURE__ */
|
|
3288
|
+
children: /* @__PURE__ */ jsx16(
|
|
3111
3289
|
CreateChannelScreen,
|
|
3112
3290
|
{
|
|
3113
3291
|
width: terminalSize.columns,
|
|
@@ -3122,7 +3300,7 @@ function AppContent() {
|
|
|
3122
3300
|
}
|
|
3123
3301
|
);
|
|
3124
3302
|
}
|
|
3125
|
-
return /* @__PURE__ */
|
|
3303
|
+
return /* @__PURE__ */ jsx16(
|
|
3126
3304
|
ChatView,
|
|
3127
3305
|
{
|
|
3128
3306
|
terminalSize,
|
|
@@ -3333,7 +3511,7 @@ function UpdatePrompt({ updateInfo, onComplete }) {
|
|
|
3333
3511
|
// package.json
|
|
3334
3512
|
var package_default = {
|
|
3335
3513
|
name: "groupchat",
|
|
3336
|
-
version: "0.0.
|
|
3514
|
+
version: "0.0.8",
|
|
3337
3515
|
description: "CLI chat client for Groupchat",
|
|
3338
3516
|
type: "module",
|
|
3339
3517
|
main: "./dist/index.js",
|