audiencemeter 0.1.0 → 0.2.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/dist/index.js +177 -41
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -15,12 +15,16 @@ __export(config_exports, {
|
|
|
15
15
|
getApiUrl: () => getApiUrl,
|
|
16
16
|
getAuthToken: () => getAuthToken,
|
|
17
17
|
getConfig: () => getConfig,
|
|
18
|
+
getPrivacyMode: () => getPrivacyMode,
|
|
18
19
|
getRefreshToken: () => getRefreshToken,
|
|
19
20
|
setApiUrl: () => setApiUrl,
|
|
20
21
|
setAuthToken: () => setAuthToken,
|
|
22
|
+
setPrivacyMode: () => setPrivacyMode,
|
|
21
23
|
setRefreshToken: () => setRefreshToken
|
|
22
24
|
});
|
|
23
25
|
import Conf from "conf";
|
|
26
|
+
import fs from "fs";
|
|
27
|
+
import path from "path";
|
|
24
28
|
function getConfig() {
|
|
25
29
|
return config;
|
|
26
30
|
}
|
|
@@ -50,12 +54,19 @@ function getApiUrl() {
|
|
|
50
54
|
function setApiUrl(url) {
|
|
51
55
|
config.set("apiUrl", url);
|
|
52
56
|
}
|
|
57
|
+
function getPrivacyMode() {
|
|
58
|
+
return config.get("privacyMode");
|
|
59
|
+
}
|
|
60
|
+
function setPrivacyMode(value) {
|
|
61
|
+
config.set("privacyMode", value);
|
|
62
|
+
}
|
|
53
63
|
var config;
|
|
54
64
|
var init_config = __esm({
|
|
55
65
|
"src/lib/config.ts"() {
|
|
56
66
|
"use strict";
|
|
57
67
|
config = new Conf({
|
|
58
68
|
projectName: "audiencemeter",
|
|
69
|
+
projectSuffix: "",
|
|
59
70
|
schema: {
|
|
60
71
|
apiUrl: {
|
|
61
72
|
type: "string",
|
|
@@ -68,9 +79,33 @@ var init_config = __esm({
|
|
|
68
79
|
refreshToken: {
|
|
69
80
|
type: "string",
|
|
70
81
|
default: ""
|
|
82
|
+
},
|
|
83
|
+
privacyMode: {
|
|
84
|
+
type: "boolean",
|
|
85
|
+
default: true
|
|
71
86
|
}
|
|
72
87
|
}
|
|
73
88
|
});
|
|
89
|
+
(function migrateOldConfig() {
|
|
90
|
+
const newPath = config.path;
|
|
91
|
+
const oldPath = path.join(path.dirname(path.dirname(newPath)), "audiencemeter-nodejs", "config.json");
|
|
92
|
+
try {
|
|
93
|
+
if (!fs.existsSync(oldPath)) return;
|
|
94
|
+
if (config.get("authToken")) return;
|
|
95
|
+
const oldData = JSON.parse(fs.readFileSync(oldPath, "utf-8"));
|
|
96
|
+
if (oldData.authToken) {
|
|
97
|
+
config.set("authToken", oldData.authToken);
|
|
98
|
+
}
|
|
99
|
+
if (oldData.refreshToken) {
|
|
100
|
+
config.set("refreshToken", oldData.refreshToken);
|
|
101
|
+
}
|
|
102
|
+
if (oldData.apiUrl && oldData.apiUrl !== "https://api.audiencemeter.pro/v1") {
|
|
103
|
+
config.set("apiUrl", oldData.apiUrl);
|
|
104
|
+
}
|
|
105
|
+
fs.rmSync(path.dirname(oldPath), { recursive: true, force: true });
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
74
109
|
}
|
|
75
110
|
});
|
|
76
111
|
|
|
@@ -127,7 +162,7 @@ var init_theme = __esm({
|
|
|
127
162
|
BRAND = {
|
|
128
163
|
name: "AudienceMeter",
|
|
129
164
|
tagline: "Speaker feedback, beautifully managed",
|
|
130
|
-
version: "0.1.
|
|
165
|
+
version: "0.1.1"
|
|
131
166
|
};
|
|
132
167
|
}
|
|
133
168
|
});
|
|
@@ -520,7 +555,38 @@ function getUserEmailFromToken() {
|
|
|
520
555
|
);
|
|
521
556
|
return payload.email || "unknown";
|
|
522
557
|
}
|
|
523
|
-
|
|
558
|
+
function getTokenPayload() {
|
|
559
|
+
const token = getAuthToken();
|
|
560
|
+
return JSON.parse(
|
|
561
|
+
Buffer.from(token.split(".")[1], "base64").toString()
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
async function ensureUserExists(client) {
|
|
565
|
+
if (userEnsured) return;
|
|
566
|
+
const payload = getTokenPayload();
|
|
567
|
+
const userId = payload.sub;
|
|
568
|
+
const resp = await client.get(`/users/${userId}`);
|
|
569
|
+
if (resp.user) {
|
|
570
|
+
userEnsured = true;
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const meta = payload.user_metadata || {};
|
|
574
|
+
await client.post("/users", {
|
|
575
|
+
id: userId,
|
|
576
|
+
email: payload.email || "",
|
|
577
|
+
displayName: meta.full_name || payload.email || "",
|
|
578
|
+
avatarUrl: meta.avatar_url || "",
|
|
579
|
+
authProvider: payload.app_metadata?.provider || "google",
|
|
580
|
+
providerId: meta.provider_id || ""
|
|
581
|
+
});
|
|
582
|
+
userEnsured = true;
|
|
583
|
+
}
|
|
584
|
+
async function initApiClient() {
|
|
585
|
+
const client = await createApiClientWithRefresh();
|
|
586
|
+
await ensureUserExists(client);
|
|
587
|
+
return client;
|
|
588
|
+
}
|
|
589
|
+
var SUPABASE_URL2, SUPABASE_ANON_KEY, ApiClient, userEnsured;
|
|
524
590
|
var init_api_client = __esm({
|
|
525
591
|
"src/lib/api-client.ts"() {
|
|
526
592
|
"use strict";
|
|
@@ -532,8 +598,8 @@ var init_api_client = __esm({
|
|
|
532
598
|
this.baseUrl = baseUrl;
|
|
533
599
|
this.authToken = authToken;
|
|
534
600
|
}
|
|
535
|
-
async request(method,
|
|
536
|
-
const url = `${this.baseUrl}${
|
|
601
|
+
async request(method, path2, body) {
|
|
602
|
+
const url = `${this.baseUrl}${path2}`;
|
|
537
603
|
const headers = {
|
|
538
604
|
"Content-Type": "application/json",
|
|
539
605
|
Authorization: this.authToken
|
|
@@ -560,19 +626,20 @@ var init_api_client = __esm({
|
|
|
560
626
|
if (!text2) return void 0;
|
|
561
627
|
return JSON.parse(text2);
|
|
562
628
|
}
|
|
563
|
-
async get(
|
|
564
|
-
return this.request("GET",
|
|
629
|
+
async get(path2) {
|
|
630
|
+
return this.request("GET", path2);
|
|
565
631
|
}
|
|
566
|
-
async post(
|
|
567
|
-
return this.request("POST",
|
|
632
|
+
async post(path2, body) {
|
|
633
|
+
return this.request("POST", path2, body);
|
|
568
634
|
}
|
|
569
|
-
async patch(
|
|
570
|
-
return this.request("PATCH",
|
|
635
|
+
async patch(path2, body) {
|
|
636
|
+
return this.request("PATCH", path2, body);
|
|
571
637
|
}
|
|
572
|
-
async delete(
|
|
573
|
-
return this.request("DELETE",
|
|
638
|
+
async delete(path2) {
|
|
639
|
+
return this.request("DELETE", path2);
|
|
574
640
|
}
|
|
575
641
|
};
|
|
642
|
+
userEnsured = false;
|
|
576
643
|
}
|
|
577
644
|
});
|
|
578
645
|
|
|
@@ -776,7 +843,7 @@ var init_create = __esm({
|
|
|
776
843
|
}
|
|
777
844
|
const s = p2.spinner();
|
|
778
845
|
s.start("Creating session...");
|
|
779
|
-
const client =
|
|
846
|
+
const client = await initApiClient();
|
|
780
847
|
const userId = getUserIdFromToken();
|
|
781
848
|
let projectId;
|
|
782
849
|
const projectName = options.project || "";
|
|
@@ -861,6 +928,24 @@ var init_create = __esm({
|
|
|
861
928
|
}
|
|
862
929
|
});
|
|
863
930
|
|
|
931
|
+
// src/lib/pin-mask.ts
|
|
932
|
+
function maskPin(pin) {
|
|
933
|
+
if (!pin || pin === "--") return pin;
|
|
934
|
+
return getPrivacyMode() ? "*".repeat(pin.length) : pin;
|
|
935
|
+
}
|
|
936
|
+
function togglePrivacyMode() {
|
|
937
|
+
const newValue = !getPrivacyMode();
|
|
938
|
+
setPrivacyMode(newValue);
|
|
939
|
+
return newValue;
|
|
940
|
+
}
|
|
941
|
+
var init_pin_mask = __esm({
|
|
942
|
+
"src/lib/pin-mask.ts"() {
|
|
943
|
+
"use strict";
|
|
944
|
+
init_config();
|
|
945
|
+
init_config();
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
|
|
864
949
|
// src/lib/node-backend.ts
|
|
865
950
|
function createNodeBackend() {
|
|
866
951
|
const stdout = process.stdout;
|
|
@@ -1034,7 +1119,10 @@ var init_TabBar = __esm({
|
|
|
1034
1119
|
import { Text as Text3 } from "terminui/jsx";
|
|
1035
1120
|
import { jsx as jsx5 } from "terminui/jsx-runtime";
|
|
1036
1121
|
function ShortcutBar({ shortcuts }) {
|
|
1037
|
-
const text2 = shortcuts.map((s) =>
|
|
1122
|
+
const text2 = shortcuts.map((s) => {
|
|
1123
|
+
if (s.active) return `${s.key}: [${s.label}]`;
|
|
1124
|
+
return `${s.key}: ${s.label}`;
|
|
1125
|
+
}).join(" | ");
|
|
1038
1126
|
return /* @__PURE__ */ jsx5(Text3, { fg: BRAND_COLORS.dimCyan, children: ` ${text2}` });
|
|
1039
1127
|
}
|
|
1040
1128
|
var init_ShortcutBar = __esm({
|
|
@@ -1048,9 +1136,18 @@ var init_ShortcutBar = __esm({
|
|
|
1048
1136
|
import { VStack as VStack2, HStack as HStack2, Box as Box2, Text as Text4, List, Gauge } from "terminui/jsx";
|
|
1049
1137
|
import { fillConstraint as fillConstraint3, lengthConstraint as lengthConstraint3, createListState, createStyle as createStyle2, Modifier as Modifier2 } from "terminui";
|
|
1050
1138
|
import { jsx as jsx6, jsxs as jsxs2 } from "terminui/jsx-runtime";
|
|
1139
|
+
function getShortcuts(privacyMode) {
|
|
1140
|
+
return [
|
|
1141
|
+
{ key: "1-4", label: "tab" },
|
|
1142
|
+
{ key: "j/k", label: "navigate" },
|
|
1143
|
+
{ key: "Enter", label: "select" },
|
|
1144
|
+
{ key: "p", label: privacyMode ? "privacy on" : "privacy", active: privacyMode },
|
|
1145
|
+
{ key: "q", label: "quit" }
|
|
1146
|
+
];
|
|
1147
|
+
}
|
|
1051
1148
|
function DashboardTab({ sessions, stats, selectedIndex, loading }) {
|
|
1052
1149
|
const recent = sessions.slice(0, 5);
|
|
1053
|
-
const listItems = recent.length > 0 ? recent.map((s) => `${icon.session} ${s.name} ${s.pin} ${s.status}`) : [loading ? "Loading sessions..." : "No sessions yet. Press Tab to go to Create."];
|
|
1150
|
+
const listItems = recent.length > 0 ? recent.map((s) => `${icon.session} ${s.name} ${maskPin(s.pin)} ${s.status}`) : [loading ? "Loading sessions..." : "No sessions yet. Press Tab to go to Create."];
|
|
1054
1151
|
const listState = createListState();
|
|
1055
1152
|
listState.selected = recent.length > 0 ? selectedIndex : void 0;
|
|
1056
1153
|
return /* @__PURE__ */ jsxs2(HStack2, { constraints: [fillConstraint3(2), fillConstraint3(1)], children: [
|
|
@@ -1081,7 +1178,7 @@ function SessionsTab({ sessions, selectedIndex, loading }) {
|
|
|
1081
1178
|
return /* @__PURE__ */ jsx6(Box2, { border: true, borderType: "rounded", fg: BRAND_COLORS.dimCyan, children: /* @__PURE__ */ jsx6(Text4, { fg: BRAND_COLORS.dim, children: loading ? "Loading sessions..." : "No sessions found. Create one with the Create tab." }) });
|
|
1082
1179
|
}
|
|
1083
1180
|
const listItems = sessions.map(
|
|
1084
|
-
(s) => `${icon.session} ${s.name.padEnd(30).slice(0, 30)} ${s.pin} ${s.status.padEnd(10).slice(0, 10)} ${s.date}`
|
|
1181
|
+
(s) => `${icon.session} ${s.name.padEnd(30).slice(0, 30)} ${maskPin(s.pin)} ${s.status.padEnd(10).slice(0, 10)} ${s.date}`
|
|
1085
1182
|
);
|
|
1086
1183
|
const listState = createListState();
|
|
1087
1184
|
listState.selected = selectedIndex;
|
|
@@ -1104,7 +1201,7 @@ function AuthTab({ authEmail, authExpired }) {
|
|
|
1104
1201
|
}
|
|
1105
1202
|
return /* @__PURE__ */ jsx6(Box2, { border: true, borderType: "rounded", title: " Authentication ", children: /* @__PURE__ */ jsx6(Text4, { fg: authEmail ? BRAND_COLORS.green : BRAND_COLORS.yellow, children: authEmail ? ` ${icon.check} Logged in as ${authEmail}` : ` ${icon.cross} Not logged in. Press Enter to login.` }) });
|
|
1106
1203
|
}
|
|
1107
|
-
function Dashboard({ selectedTab, sessions, stats, selectedIndex, authEmail, authExpired, loading, error }) {
|
|
1204
|
+
function Dashboard({ selectedTab, sessions, stats, selectedIndex, authEmail, authExpired, loading, error, privacyMode = false }) {
|
|
1108
1205
|
let content;
|
|
1109
1206
|
switch (selectedTab) {
|
|
1110
1207
|
case 0:
|
|
@@ -1128,17 +1225,17 @@ function Dashboard({ selectedTab, sessions, stats, selectedIndex, authEmail, aut
|
|
|
1128
1225
|
/* @__PURE__ */ jsx6(TabBar, { titles: [...TAB_TITLES], selected: selectedTab }),
|
|
1129
1226
|
/* @__PURE__ */ jsx6(Text4, { fg: BRAND_COLORS.yellow, align: "center", children: `${icon.cross} ${error}` }),
|
|
1130
1227
|
content,
|
|
1131
|
-
/* @__PURE__ */ jsx6(ShortcutBar, { shortcuts:
|
|
1228
|
+
/* @__PURE__ */ jsx6(ShortcutBar, { shortcuts: getShortcuts(privacyMode) })
|
|
1132
1229
|
] });
|
|
1133
1230
|
}
|
|
1134
1231
|
return /* @__PURE__ */ jsxs2(VStack2, { constraints: [lengthConstraint3(1), lengthConstraint3(3), fillConstraint3(1), lengthConstraint3(1)], children: [
|
|
1135
1232
|
/* @__PURE__ */ jsx6(Header, {}),
|
|
1136
1233
|
/* @__PURE__ */ jsx6(TabBar, { titles: [...TAB_TITLES], selected: selectedTab }),
|
|
1137
1234
|
content,
|
|
1138
|
-
/* @__PURE__ */ jsx6(ShortcutBar, { shortcuts:
|
|
1235
|
+
/* @__PURE__ */ jsx6(ShortcutBar, { shortcuts: getShortcuts(privacyMode) })
|
|
1139
1236
|
] });
|
|
1140
1237
|
}
|
|
1141
|
-
var TAB_TITLES
|
|
1238
|
+
var TAB_TITLES;
|
|
1142
1239
|
var init_Dashboard = __esm({
|
|
1143
1240
|
"src/components/Dashboard.tsx"() {
|
|
1144
1241
|
"use strict";
|
|
@@ -1146,13 +1243,8 @@ var init_Dashboard = __esm({
|
|
|
1146
1243
|
init_Header();
|
|
1147
1244
|
init_TabBar();
|
|
1148
1245
|
init_ShortcutBar();
|
|
1246
|
+
init_pin_mask();
|
|
1149
1247
|
TAB_TITLES = ["Dashboard", "Sessions", "Create", "Auth"];
|
|
1150
|
-
SHORTCUTS = [
|
|
1151
|
-
{ key: "Tab/1-4", label: "switch" },
|
|
1152
|
-
{ key: "j/k", label: "navigate" },
|
|
1153
|
-
{ key: "Enter", label: "select" },
|
|
1154
|
-
{ key: "Esc/q", label: "quit" }
|
|
1155
|
-
];
|
|
1156
1248
|
}
|
|
1157
1249
|
});
|
|
1158
1250
|
|
|
@@ -1240,7 +1332,7 @@ function SessionDetail({ session, metrics, timeline, analysis, status }) {
|
|
|
1240
1332
|
const dateStr = session.date ? new Date(session.date).toLocaleString() : session.createdAt ? new Date(session.createdAt).toLocaleString() : "--";
|
|
1241
1333
|
const durationStr = session.durationMinutes ? `${session.durationMinutes} minutes` : "--";
|
|
1242
1334
|
const infoPairs = [
|
|
1243
|
-
["PIN", session.pin || "--"],
|
|
1335
|
+
["PIN", maskPin(session.pin || "--")],
|
|
1244
1336
|
["Date", dateStr],
|
|
1245
1337
|
["Duration", durationStr]
|
|
1246
1338
|
];
|
|
@@ -1344,7 +1436,7 @@ function SessionDetail({ session, metrics, timeline, analysis, status }) {
|
|
|
1344
1436
|
}
|
|
1345
1437
|
if (session.pin) {
|
|
1346
1438
|
sections.push(
|
|
1347
|
-
/* @__PURE__ */ jsx8(Box4, { border: true, borderType: "rounded", fg: BRAND_COLORS.dimCyan, title: " Join URL ", children: /* @__PURE__ */ jsx8(Text6, { bold: true, fg: BRAND_COLORS.cyan, children: `https://app.audiencemeter.pro/s/${session.pin}` }) })
|
|
1439
|
+
/* @__PURE__ */ jsx8(Box4, { border: true, borderType: "rounded", fg: BRAND_COLORS.dimCyan, title: " Join URL ", children: /* @__PURE__ */ jsx8(Text6, { bold: true, fg: BRAND_COLORS.cyan, children: `https://app.audiencemeter.pro/s/${maskPin(session.pin)}` }) })
|
|
1348
1440
|
);
|
|
1349
1441
|
sectionConstraints.push(lengthConstraint5(3));
|
|
1350
1442
|
}
|
|
@@ -1356,6 +1448,7 @@ var init_SessionDetail = __esm({
|
|
|
1356
1448
|
init_theme();
|
|
1357
1449
|
init_KeyValue();
|
|
1358
1450
|
init_MetricsPanel();
|
|
1451
|
+
init_pin_mask();
|
|
1359
1452
|
}
|
|
1360
1453
|
});
|
|
1361
1454
|
|
|
@@ -1419,13 +1512,16 @@ function renderDashboard(terminal, state) {
|
|
|
1419
1512
|
authEmail: state.authEmail,
|
|
1420
1513
|
authExpired: state.authExpired,
|
|
1421
1514
|
loading: state.loading,
|
|
1422
|
-
error: state.error
|
|
1515
|
+
error: state.error,
|
|
1516
|
+
privacyMode: getPrivacyMode()
|
|
1423
1517
|
}
|
|
1424
1518
|
));
|
|
1425
1519
|
}
|
|
1426
1520
|
function renderSessionDetail(terminal, session, metrics, timeline, analysis, status) {
|
|
1521
|
+
const privacyOn = getPrivacyMode();
|
|
1427
1522
|
const detailShortcuts = [
|
|
1428
|
-
{ key: "
|
|
1523
|
+
{ key: "q", label: "back" },
|
|
1524
|
+
{ key: "p", label: privacyOn ? "privacy on" : "privacy", active: privacyOn }
|
|
1429
1525
|
];
|
|
1430
1526
|
terminalDrawJsx2(terminal, /* @__PURE__ */ jsxs4(VStack5, { constraints: [fillConstraint5(1), lengthConstraint6(1)], children: [
|
|
1431
1527
|
/* @__PURE__ */ jsx9(
|
|
@@ -1443,7 +1539,7 @@ function renderSessionDetail(terminal, session, metrics, timeline, analysis, sta
|
|
|
1443
1539
|
}
|
|
1444
1540
|
function renderInteractiveList(terminal, sessionRows, total, selectedIndex) {
|
|
1445
1541
|
const listItems = sessionRows.map(
|
|
1446
|
-
(s) => `${icon.session} ${s.name.padEnd(30).slice(0, 30)} ${s.pin} ${s.status.padEnd(10).slice(0, 10)} ${s.date}`
|
|
1542
|
+
(s) => `${icon.session} ${s.name.padEnd(30).slice(0, 30)} ${maskPin(s.pin)} ${s.status.padEnd(10).slice(0, 10)} ${s.date}`
|
|
1447
1543
|
);
|
|
1448
1544
|
const listState = createListState2();
|
|
1449
1545
|
listState.selected = selectedIndex;
|
|
@@ -1458,14 +1554,19 @@ function renderInteractiveList(terminal, sessionRows, total, selectedIndex) {
|
|
|
1458
1554
|
highlightStyle: createStyle3({ fg: BRAND_COLORS.purple, addModifier: Modifier3.BOLD })
|
|
1459
1555
|
}
|
|
1460
1556
|
) }),
|
|
1461
|
-
/* @__PURE__ */ jsx9(
|
|
1557
|
+
/* @__PURE__ */ jsx9(ShortcutBar, { shortcuts: [
|
|
1558
|
+
{ key: "j/k", label: "navigate" },
|
|
1559
|
+
{ key: "Enter", label: "view" },
|
|
1560
|
+
{ key: "p", label: getPrivacyMode() ? "privacy on" : "privacy", active: getPrivacyMode() },
|
|
1561
|
+
{ key: "q", label: "quit" }
|
|
1562
|
+
] })
|
|
1462
1563
|
] }));
|
|
1463
1564
|
}
|
|
1464
1565
|
async function fetchSessionDetail(sessionId) {
|
|
1465
1566
|
const client = createApiClient();
|
|
1466
1567
|
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);
|
|
1467
|
-
const
|
|
1468
|
-
const session = await client.get(
|
|
1568
|
+
const path2 = !isUuid ? `/sessions/by-pin/${sessionId}` : `/sessions/${sessionId}`;
|
|
1569
|
+
const session = await client.get(path2);
|
|
1469
1570
|
let metrics = null;
|
|
1470
1571
|
let timeline = [];
|
|
1471
1572
|
let analysis = null;
|
|
@@ -1565,6 +1666,11 @@ async function startDashboard() {
|
|
|
1565
1666
|
return;
|
|
1566
1667
|
}
|
|
1567
1668
|
if (state.showingSession) {
|
|
1669
|
+
if (key.name === "p") {
|
|
1670
|
+
togglePrivacyMode();
|
|
1671
|
+
rerender();
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1568
1674
|
if (key.name === "escape" || key.name === "q") {
|
|
1569
1675
|
state.showingSession = null;
|
|
1570
1676
|
detailData = null;
|
|
@@ -1650,6 +1756,12 @@ async function startDashboard() {
|
|
|
1650
1756
|
return;
|
|
1651
1757
|
}
|
|
1652
1758
|
}
|
|
1759
|
+
if (key.name === "p") {
|
|
1760
|
+
togglePrivacyMode();
|
|
1761
|
+
state.sessions = toSessionRows(state.rawSessions);
|
|
1762
|
+
rerender();
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1653
1765
|
if (key.name === "q" || key.name === "escape") {
|
|
1654
1766
|
state.running = false;
|
|
1655
1767
|
cleanupInput();
|
|
@@ -1664,7 +1776,7 @@ async function startDashboard() {
|
|
|
1664
1776
|
process.stdout.on("resize", onResize);
|
|
1665
1777
|
(async () => {
|
|
1666
1778
|
try {
|
|
1667
|
-
const client = await
|
|
1779
|
+
const client = await initApiClient();
|
|
1668
1780
|
if (state.authExpired) {
|
|
1669
1781
|
state.authExpired = false;
|
|
1670
1782
|
state.authEmail = getUserEmailFromToken();
|
|
@@ -1751,6 +1863,11 @@ async function startInteractiveList(sessionRows, rawSessions, total) {
|
|
|
1751
1863
|
return;
|
|
1752
1864
|
}
|
|
1753
1865
|
if (showingDetail) {
|
|
1866
|
+
if (key.name === "p") {
|
|
1867
|
+
togglePrivacyMode();
|
|
1868
|
+
rerender();
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1754
1871
|
if (key.name === "escape" || key.name === "q") {
|
|
1755
1872
|
showingDetail = false;
|
|
1756
1873
|
detailData = null;
|
|
@@ -1798,6 +1915,11 @@ async function startInteractiveList(sessionRows, rawSessions, total) {
|
|
|
1798
1915
|
}
|
|
1799
1916
|
return;
|
|
1800
1917
|
}
|
|
1918
|
+
if (key.name === "p") {
|
|
1919
|
+
togglePrivacyMode();
|
|
1920
|
+
rerender();
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1801
1923
|
if (key.name === "q" || key.name === "escape") {
|
|
1802
1924
|
running = false;
|
|
1803
1925
|
cleanupInput();
|
|
@@ -1830,6 +1952,7 @@ var init_app = __esm({
|
|
|
1830
1952
|
init_SessionDetail();
|
|
1831
1953
|
init_ShortcutBar();
|
|
1832
1954
|
init_theme();
|
|
1955
|
+
init_pin_mask();
|
|
1833
1956
|
}
|
|
1834
1957
|
});
|
|
1835
1958
|
|
|
@@ -1847,6 +1970,7 @@ import * as p3 from "@clack/prompts";
|
|
|
1847
1970
|
|
|
1848
1971
|
// src/components/SessionTable.tsx
|
|
1849
1972
|
init_theme();
|
|
1973
|
+
init_pin_mask();
|
|
1850
1974
|
import { Table, Box } from "terminui/jsx";
|
|
1851
1975
|
import { fillConstraint as fillConstraint2, lengthConstraint as lengthConstraint2 } from "terminui";
|
|
1852
1976
|
import { jsx as jsx2 } from "terminui/jsx-runtime";
|
|
@@ -1868,7 +1992,7 @@ function SessionTable({ sessions, total }) {
|
|
|
1868
1992
|
const header = ["Name", "PIN", "Status", "Date", "Duration", "Ppl"];
|
|
1869
1993
|
const rows = sessions.map((s) => [
|
|
1870
1994
|
s.name,
|
|
1871
|
-
s.pin,
|
|
1995
|
+
maskPin(s.pin),
|
|
1872
1996
|
s.status,
|
|
1873
1997
|
s.date,
|
|
1874
1998
|
s.duration,
|
|
@@ -1910,7 +2034,7 @@ var listCommand = new Command3("list").alias("ls").description("List your feedba
|
|
|
1910
2034
|
const s = p3.spinner();
|
|
1911
2035
|
s.start("Fetching sessions...");
|
|
1912
2036
|
try {
|
|
1913
|
-
const client =
|
|
2037
|
+
const client = await initApiClient();
|
|
1914
2038
|
const userId = getUserIdFromToken();
|
|
1915
2039
|
const queryParams = new URLSearchParams();
|
|
1916
2040
|
queryParams.set("take", String(options.limit));
|
|
@@ -2005,10 +2129,10 @@ var showCommand = new Command4("show").description("Show session details with ri
|
|
|
2005
2129
|
const s = p4.spinner();
|
|
2006
2130
|
s.start("Fetching session...");
|
|
2007
2131
|
try {
|
|
2008
|
-
const client =
|
|
2132
|
+
const client = await initApiClient();
|
|
2009
2133
|
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);
|
|
2010
|
-
const
|
|
2011
|
-
const session = await client.get(
|
|
2134
|
+
const path2 = !isUuid ? `/sessions/by-pin/${sessionId}` : `/sessions/${sessionId}`;
|
|
2135
|
+
const session = await client.get(path2);
|
|
2012
2136
|
let metrics = null;
|
|
2013
2137
|
let timeline = [];
|
|
2014
2138
|
let analysis = null;
|
|
@@ -2129,7 +2253,7 @@ import { Command as Command5 } from "commander";
|
|
|
2129
2253
|
import * as p5 from "@clack/prompts";
|
|
2130
2254
|
var deleteCommand = new Command5("delete").alias("rm").description("Delete a session").argument("<session-id>", "Session ID (UUID)").option("--force", "Skip confirmation prompt").action(async (sessionId, options) => {
|
|
2131
2255
|
try {
|
|
2132
|
-
const client =
|
|
2256
|
+
const client = await initApiClient();
|
|
2133
2257
|
let sessionName = sessionId;
|
|
2134
2258
|
try {
|
|
2135
2259
|
const session = await client.get(
|
|
@@ -2166,6 +2290,7 @@ var deleteCommand = new Command5("delete").alias("rm").description("Delete a ses
|
|
|
2166
2290
|
|
|
2167
2291
|
// src/index.ts
|
|
2168
2292
|
init_theme();
|
|
2293
|
+
init_config();
|
|
2169
2294
|
var program = new Command6();
|
|
2170
2295
|
program.name("audiencemeter").description(
|
|
2171
2296
|
`${BRAND.name} CLI \u2014 ${BRAND.tagline}`
|
|
@@ -2177,6 +2302,17 @@ program.addCommand(createCommand);
|
|
|
2177
2302
|
program.addCommand(listCommand);
|
|
2178
2303
|
program.addCommand(showCommand);
|
|
2179
2304
|
program.addCommand(deleteCommand);
|
|
2305
|
+
var configCommand = new Command6("config").description("Manage CLI preferences");
|
|
2306
|
+
configCommand.command("privacy [value]").description("Get or set privacy mode (true/false). When on, PINs are hidden.").action((value) => {
|
|
2307
|
+
if (value === void 0) {
|
|
2308
|
+
console.log(`privacy: ${getPrivacyMode()}`);
|
|
2309
|
+
} else {
|
|
2310
|
+
const boolValue = value === "true" || value === "1";
|
|
2311
|
+
setPrivacyMode(boolValue);
|
|
2312
|
+
console.log(`privacy mode ${boolValue ? "enabled" : "disabled"}`);
|
|
2313
|
+
}
|
|
2314
|
+
});
|
|
2315
|
+
program.addCommand(configCommand);
|
|
2180
2316
|
var hasArgs = process.argv.length > 2;
|
|
2181
2317
|
if (hasArgs) {
|
|
2182
2318
|
program.parse(process.argv);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/config.ts","../src/lib/theme.ts","../src/lib/render.ts","../src/components/KeyValue.tsx","../src/commands/auth.ts","../src/lib/api-client.ts","../src/lib/templates.ts","../src/commands/create.ts","../src/lib/node-backend.ts","../src/lib/input.ts","../src/components/Header.tsx","../src/components/TabBar.tsx","../src/components/ShortcutBar.tsx","../src/components/Dashboard.tsx","../src/components/MetricsPanel.tsx","../src/components/SessionDetail.tsx","../src/lib/app.tsx","../src/index.ts","../src/commands/list.ts","../src/components/SessionTable.tsx","../src/commands/show.ts","../src/commands/delete.ts"],"sourcesContent":["import Conf from 'conf';\n\nconst config = new Conf({\n projectName: 'audiencemeter',\n schema: {\n apiUrl: {\n type: 'string' as const,\n default: 'https://api.audiencemeter.pro/v1',\n },\n authToken: {\n type: 'string' as const,\n default: '',\n },\n refreshToken: {\n type: 'string' as const,\n default: '',\n },\n },\n});\n\nexport function getConfig(): typeof config {\n return config;\n}\n\nexport function getAuthToken(): string {\n const token = config.get('authToken') as string;\n if (!token) {\n throw new Error('Not authenticated. Run: audiencemeter login');\n }\n return token;\n}\n\nexport function setAuthToken(token: string): void {\n config.set('authToken', token);\n}\n\nexport function clearAuthToken(): void {\n config.set('authToken', '');\n config.set('refreshToken', '');\n}\n\nexport function getRefreshToken(): string {\n return config.get('refreshToken') as string;\n}\n\nexport function setRefreshToken(token: string): void {\n config.set('refreshToken', token);\n}\n\nexport function getApiUrl(): string {\n return config.get('apiUrl') as string;\n}\n\nexport function setApiUrl(url: string): void {\n config.set('apiUrl', url);\n}\n","import { rgbColor, type Color } from 'terminui';\n\n// Brand colors as terminui Color objects (no ANSI escape strings)\nexport const BRAND_COLORS: Record<string, Color> = {\n purple: rgbColor(175, 95, 255), // Brand purple\n cyan: rgbColor(80, 220, 220), // Info/borders\n green: rgbColor(80, 220, 120), // Success/gauges\n yellow: rgbColor(255, 200, 60), // PINs\n dimCyan: rgbColor(60, 160, 170), // Borders, labels\n red: rgbColor(255, 100, 100), // Errors\n dim: rgbColor(128, 128, 128), // Dimmed text\n white: rgbColor(230, 230, 230), // Primary text\n};\n\nexport const icon = {\n session: '\\u25cf', // ●\n live: '\\u25cf', // ●\n ended: '\\u25cb', // ○\n upcoming: '\\u25d4', // ◔\n check: '\\u2714', // ✔\n cross: '\\u2718', // ✘\n arrow: '\\u276f', // ❯\n dot: '\\u00b7', // ·\n bar: '\\u2503', // ┃\n dash: '\\u2500', // ─\n star: '\\u2605', // ★\n sparkle: '\\u2728', // ✨\n};\n\nexport const BRAND = {\n name: 'AudienceMeter',\n tagline: 'Speaker feedback, beautifully managed',\n version: '0.1.0',\n};\n","import {\n createTestBackendState,\n createTestBackend,\n createTerminal,\n testBackendCellAt,\n type Cell,\n} from 'terminui';\nimport { terminalDrawJsx } from 'terminui/jsx';\nimport type { JsxNode } from 'terminui/jsx-runtime';\n\n// ── ANSI Color Conversion (shared with node-backend.ts) ──────────────\n\ntype CellColor = { type: string; r?: number; g?: number; b?: number; index?: number };\n\nconst NAMED_FG: Record<string, string> = {\n black: '30', red: '31', green: '32', yellow: '33', blue: '34',\n magenta: '35', cyan: '36', gray: '37', white: '97',\n 'dark-gray': '90', 'light-red': '91', 'light-green': '92',\n 'light-yellow': '93', 'light-blue': '94', 'light-magenta': '95', 'light-cyan': '96',\n};\n\nconst NAMED_BG: Record<string, string> = {\n black: '40', red: '41', green: '42', yellow: '43', blue: '44',\n magenta: '45', cyan: '46', gray: '47', white: '107',\n 'dark-gray': '100', 'light-red': '101', 'light-green': '102',\n 'light-yellow': '103', 'light-blue': '104', 'light-magenta': '105', 'light-cyan': '106',\n};\n\nconst RESET = '\\x1b[0m';\n\nexport function fgAnsi(c: CellColor | undefined): string {\n if (!c || c.type === 'reset') return '';\n if (c.type === 'rgb') return `\\x1b[38;2;${c.r};${c.g};${c.b}m`;\n if (c.type === 'indexed') return `\\x1b[38;5;${c.index}m`;\n return NAMED_FG[c.type] ? `\\x1b[${NAMED_FG[c.type]}m` : '';\n}\n\nexport function bgAnsi(c: CellColor | undefined): string {\n if (!c || c.type === 'reset') return '';\n if (c.type === 'rgb') return `\\x1b[48;2;${c.r};${c.g};${c.b}m`;\n if (c.type === 'indexed') return `\\x1b[48;5;${c.index}m`;\n return NAMED_BG[c.type] ? `\\x1b[${NAMED_BG[c.type]}m` : '';\n}\n\nexport function modAnsi(mod: number | undefined): string {\n if (!mod) return '';\n let s = '';\n if (mod & 1) s += '\\x1b[1m'; // Bold\n if (mod & 2) s += '\\x1b[2m'; // Dim\n if (mod & 4) s += '\\x1b[3m'; // Italic\n if (mod & 8) s += '\\x1b[4m'; // Underline\n if (mod & 64) s += '\\x1b[7m'; // Reversed\n return s;\n}\n\n/**\n * Convert a terminui Cell to an ANSI escape string (style + symbol).\n */\nexport function cellToAnsi(cell: Cell): string {\n const fg = fgAnsi(cell.fg as CellColor);\n const bg = bgAnsi(cell.bg as CellColor);\n const mod = modAnsi(cell.modifier);\n const style = fg + bg + mod;\n if (style) {\n return style + cell.symbol + RESET;\n }\n return cell.symbol;\n}\n\n// ── Render JSX to String (static commands) ───────────────────────────\n\n/**\n * Renders a JSX node tree to a styled ANSI string using the test backend.\n * Used for print-and-exit commands (list, show, create, delete, auth).\n */\nexport function renderJsxToString(width: number, height: number, node: JsxNode): string {\n const state = createTestBackendState(width, height);\n const terminal = createTerminal(createTestBackend(state));\n terminalDrawJsx(terminal, node);\n\n // Reconstruct ANSI-styled output from the cell grid\n const lines: string[] = [];\n for (let y = 0; y < height; y++) {\n let line = '';\n let hasStyle = false;\n for (let x = 0; x < width; x++) {\n const cell = testBackendCellAt(state, x, y) as {\n symbol: string; fg?: CellColor; bg?: CellColor; modifier?: number;\n } | undefined;\n if (!cell) {\n if (hasStyle) { line += RESET; hasStyle = false; }\n line += ' ';\n continue;\n }\n const style = fgAnsi(cell.fg) + bgAnsi(cell.bg) + modAnsi(cell.modifier);\n if (style) {\n if (hasStyle) line += RESET;\n line += style + cell.symbol;\n hasStyle = true;\n } else {\n if (hasStyle) { line += RESET; hasStyle = false; }\n line += cell.symbol;\n }\n }\n if (hasStyle) line += RESET;\n lines.push(line.trimEnd());\n }\n\n // Trim trailing empty lines\n while (lines.length > 0 && stripAnsi(lines[lines.length - 1]!).trim() === '') {\n lines.pop();\n }\n\n return lines.join('\\n');\n}\n\n// ── Utilities ────────────────────────────────────────────────────────\n\n/**\n * Returns the usable terminal width, capped at 120 columns.\n */\nexport function getTermWidth(): number {\n return Math.min(process.stdout.columns || 80, 120);\n}\n\n/**\n * Strips all ANSI escape sequences from a string.\n */\nexport function stripAnsi(s: string): string {\n return s.replace(/\\x1b\\[[0-9;]*m/g, '');\n}\n","import { VStack, HStack, Text } from 'terminui/jsx';\nimport { lengthConstraint, fillConstraint } from 'terminui';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface KeyValueProps {\n readonly pairs: readonly (readonly [string, string])[];\n}\n\nexport function KeyValue({ pairs }: KeyValueProps) {\n const maxKeyLen = Math.max(...pairs.map(([k]) => k.length));\n const constraints = pairs.map(() => lengthConstraint(1));\n\n const rows = pairs.map(([key, value]) => {\n const padded = key.padStart(maxKeyLen);\n return (\n <HStack constraints={[lengthConstraint(maxKeyLen + 2), fillConstraint(1)]}>\n <Text fg={BRAND_COLORS.dim}>{padded + ' '}</Text>\n <Text bold fg={BRAND_COLORS.white}>{value}</Text>\n </HStack>\n );\n });\n\n return (\n <VStack constraints={constraints}>\n {rows}\n </VStack>\n );\n}\n","import { Command } from 'commander';\nimport http from 'node:http';\nimport { URL } from 'node:url';\nimport * as p from '@clack/prompts';\nimport { setAuthToken, clearAuthToken, getApiUrl, setRefreshToken } from '../lib/config.js';\nimport { BRAND, icon } from '../lib/theme.js';\nimport { renderJsxToString, getTermWidth } from '../lib/render.js';\nimport { KeyValue } from '../components/KeyValue.js';\n\nconst SUPABASE_URL = 'https://gflvvytymdmrbjpmymhb.supabase.co';\n\nexport const authCommand = new Command('auth').description(\n 'Manage authentication'\n);\n\nexport const loginCommand = new Command('login')\n .description('Log in via browser (Google OAuth)');\n\nloginCommand.action(async () => {\n p.intro(`${BRAND.name} -- Login`);\n\n const s = p.spinner();\n s.start('Starting local auth server...');\n\n try {\n const token = await new Promise<string>((resolve, reject) => {\n const server = http.createServer((req, res) => {\n if (!req.url) {\n res.writeHead(400);\n res.end('Bad request');\n return;\n }\n\n const url = new URL(req.url, 'http://localhost');\n\n if (url.pathname === '/callback') {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>${BRAND.name} — Login</title>\n<style>body{font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#0a0a0a;color:#e5e5e5}\n.card{text-align:center;padding:2rem;border-radius:12px;background:#171717;border:1px solid #333}</style></head>\n<body><div class=\"card\"><p>Processing authentication...</p></div>\n<script>\nconst h=window.location.hash.substring(1);const p=new URLSearchParams(h);const t=p.get('access_token');const r=p.get('refresh_token');\nif(t){let u='/token?access_token='+encodeURIComponent(t);if(r)u+='&refresh_token='+encodeURIComponent(r);window.location.href=u}\nelse{document.querySelector('.card').innerHTML='<p style=\"color:#f87171\">Authentication failed. No token found.</p><p>You can close this tab.</p>'}\n</script></body></html>`);\n return;\n }\n\n if (url.pathname === '/token') {\n const accessToken = url.searchParams.get('access_token');\n const refreshTokenParam = url.searchParams.get('refresh_token');\n if (accessToken) {\n if (refreshTokenParam) {\n setRefreshToken(refreshTokenParam);\n }\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>${BRAND.name} — Logged In</title>\n<style>body{font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#0a0a0a;color:#e5e5e5}\n.card{text-align:center;padding:2rem;border-radius:12px;background:#171717;border:1px solid #333}\n.check{font-size:3rem;margin-bottom:1rem}</style></head>\n<body><div class=\"card\"><div class=\"check\">✔</div><h2>Logged in!</h2><p>Return to your terminal.</p></div></body></html>`);\n resolve(accessToken);\n server.close();\n } else {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end('<p>Missing access token.</p>');\n }\n return;\n }\n\n res.writeHead(404);\n res.end('Not found');\n });\n\n server.listen(0, () => {\n const address = server.address();\n if (!address || typeof address === 'string') {\n reject(new Error('Failed to start local server'));\n return;\n }\n\n const port = address.port;\n const redirectUrl = `http://localhost:${port}/callback`;\n const authUrl = `${SUPABASE_URL}/auth/v1/authorize?provider=google&redirect_to=${encodeURIComponent(redirectUrl)}`;\n\n s.message('Opening browser for authentication...');\n\n import('open')\n .then((m) => m.default(authUrl))\n .catch(() => {\n s.stop('Could not open browser automatically');\n p.note(authUrl, 'Open this URL manually');\n });\n });\n\n setTimeout(() => {\n server.close();\n reject(new Error('Authentication timed out after 120 seconds'));\n }, 120_000);\n });\n\n setAuthToken(token);\n s.stop(`${icon.check} Logged in successfully!`);\n\n // Show user info\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { email?: string };\n\n if (payload.email) {\n p.log.info(`Signed in as ${payload.email}`);\n }\n\n p.outro('Token stored. You can now use AudienceMeter CLI commands.');\n process.exit(0);\n } catch (error) {\n s.stop(\n `${icon.cross} Login failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n process.exit(1);\n }\n });\n\nexport const logoutCommand = new Command('logout')\n .description('Log out and clear stored credentials')\n .action(async () => {\n clearAuthToken();\n p.log.success(`${icon.check} Logged out successfully.`);\n });\n\nauthCommand\n .command('whoami')\n .description('Show current authenticated user')\n .action(async () => {\n try {\n const { getAuthToken } = await import('../lib/config.js');\n const token = getAuthToken();\n const apiUrl = getApiUrl();\n\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { email?: string; sub?: string };\n\n const output = renderJsxToString(\n getTermWidth(),\n 3,\n KeyValue({\n pairs: [\n ['Email', payload.email || 'unknown'],\n ['User ID', payload.sub || 'unknown'],\n ['API URL', apiUrl],\n ],\n })\n );\n console.log(output);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Not logged in. Run: audiencemeter login');\n } else {\n p.log.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n }\n }\n });\n\nauthCommand\n .command('token')\n .description('Print the current stored auth token')\n .action(async () => {\n try {\n const { getAuthToken } = await import('../lib/config.js');\n const token = getAuthToken();\n console.log(token);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Not logged in. Run: audiencemeter login');\n } else {\n p.log.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n }\n }\n });\n\n// Register login/logout as subcommands of auth so `auth login` / `auth logout` still works\nauthCommand.addCommand(loginCommand);\nauthCommand.addCommand(logoutCommand);\n","import { getAuthToken, getApiUrl, getRefreshToken, setAuthToken, setRefreshToken } from './config.js';\n\nconst SUPABASE_URL = 'https://gflvvytymdmrbjpmymhb.supabase.co';\nconst SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImdmbHZ2eXR5bWRtcmJqcG15bWhiIiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODAyNTIwMjIsImV4cCI6MTk5NTgyODAyMn0.1m-3IhFB-87AKk_-UIPzB0O1URgBwl78oKu8sNe8aFU';\n\nexport function isTokenExpired(token: string): boolean {\n try {\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { exp?: number };\n if (!payload.exp) return false;\n // Consider expired 60s before actual expiry to avoid edge cases\n return Date.now() >= (payload.exp - 60) * 1000;\n } catch {\n return true;\n }\n}\n\nexport async function refreshAccessToken(): Promise<string | null> {\n const refreshToken = getRefreshToken();\n if (!refreshToken) return null;\n\n try {\n const response = await fetch(`${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'apikey': SUPABASE_ANON_KEY,\n },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n\n if (!response.ok) return null;\n\n const data = await response.json() as {\n access_token?: string;\n refresh_token?: string;\n };\n\n if (data.access_token) {\n setAuthToken(data.access_token);\n if (data.refresh_token) {\n setRefreshToken(data.refresh_token);\n }\n return data.access_token;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport class ApiClient {\n constructor(\n private baseUrl: string,\n private authToken: string\n ) {}\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: this.authToken,\n };\n\n const options: RequestInit = { method, headers };\n\n if (body !== undefined) {\n options.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, options);\n\n if (!response.ok) {\n let errorMessage: string;\n try {\n const errorBody = (await response.json()) as Record<string, string>;\n errorMessage =\n errorBody.message || errorBody.error || response.statusText;\n } catch {\n errorMessage = response.statusText;\n }\n\n if (response.status === 401) {\n throw new Error('Not authenticated. Run: audiencemeter login');\n }\n\n throw new Error(`API error (${response.status}): ${errorMessage}`);\n }\n\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n\n async get<T>(path: string): Promise<T> {\n return this.request<T>('GET', path);\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', path, body);\n }\n\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', path, body);\n }\n\n async delete<T>(path: string): Promise<T> {\n return this.request<T>('DELETE', path);\n }\n}\n\nexport async function ensureValidToken(): Promise<string> {\n const token = getAuthToken();\n if (!isTokenExpired(token)) return token;\n\n const newToken = await refreshAccessToken();\n if (newToken) return newToken;\n\n throw new Error('Session expired. Run: audiencemeter login');\n}\n\nexport function createApiClient(): ApiClient {\n const baseUrl = getApiUrl();\n const authToken = getAuthToken();\n return new ApiClient(baseUrl, authToken);\n}\n\nexport async function createApiClientWithRefresh(): Promise<ApiClient> {\n const baseUrl = getApiUrl();\n const authToken = await ensureValidToken();\n return new ApiClient(baseUrl, authToken);\n}\n\nexport function getUserIdFromToken(): string {\n const token = getAuthToken();\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { sub: string };\n return payload.sub;\n}\n\nexport function getUserEmailFromToken(): string {\n const token = getAuthToken();\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { email?: string };\n return payload.email || 'unknown';\n}\n","export const TEMPLATES = {\n talk: {\n description: 'Conference talk or presentation (45 min)',\n durationMinutes: 45,\n ratings: [\n { label: 'Content Quality', maxValue: 5 },\n { label: 'Delivery', maxValue: 5 },\n { label: 'Clarity', maxValue: 5 },\n ],\n links: [],\n },\n workshop: {\n description: 'Hands-on workshop or lab session (120 min)',\n durationMinutes: 120,\n ratings: [\n { label: 'Content Quality', maxValue: 5 },\n { label: 'Hands-on Experience', maxValue: 5 },\n { label: 'Pace', maxValue: 5 },\n { label: 'Clarity', maxValue: 5 },\n ],\n links: [],\n },\n lightning: {\n description: 'Lightning talk (10 min)',\n durationMinutes: 10,\n ratings: [\n { label: 'Content Quality', maxValue: 5 },\n { label: 'Delivery', maxValue: 5 },\n ],\n links: [],\n },\n panel: {\n description: 'Panel discussion (60 min)',\n durationMinutes: 60,\n ratings: [\n { label: 'Topic Relevance', maxValue: 5 },\n { label: 'Discussion Quality', maxValue: 5 },\n { label: 'Moderation', maxValue: 5 },\n ],\n links: [],\n },\n} as const;\n\nexport type TemplateName = keyof typeof TEMPLATES;\n\nexport function getTemplate(name: string) {\n if (!(name in TEMPLATES)) {\n return null;\n }\n return TEMPLATES[name as TemplateName];\n}\n\nexport function getTemplateNames(): string[] {\n return Object.keys(TEMPLATES);\n}\n","import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport qrcode from 'qrcode-terminal';\nimport { createApiClient, getUserIdFromToken } from '../lib/api-client.js';\nimport {\n TEMPLATES,\n getTemplate,\n getTemplateNames,\n} from '../lib/templates.js';\nimport { BRAND, icon } from '../lib/theme.js';\nimport { renderJsxToString, getTermWidth } from '../lib/render.js';\nimport { KeyValue } from '../components/KeyValue.js';\n\ninterface Project {\n id: string;\n name: string;\n [key: string]: unknown;\n}\n\nfunction padDate(n: number): string {\n return String(n).padStart(2, '0');\n}\n\nfunction formatLocalDate(d: Date): string {\n return `${d.getFullYear()}-${padDate(d.getMonth() + 1)}-${padDate(d.getDate())}`;\n}\n\nfunction formatLocalTime(d: Date): string {\n return `${padDate(d.getHours())}:${padDate(d.getMinutes())}`;\n}\n\nexport const createCommand = new Command('create')\n .description('Create a new feedback session')\n .option('--template <type>', 'Session template')\n .option('--project <name>', 'Project name (optional)')\n .option('--name <name>', 'Session name')\n .option('--date <date>', 'Session date (YYYY-MM-DD)')\n .option('--time <time>', 'Session time (HH:MM, 24h format)')\n .option(\n '--duration <minutes>',\n 'Duration in minutes',\n parseInt\n )\n .option('--json', 'Output as JSON')\n .action(async (options) => {\n const isInteractive =\n !options.json && process.stdout.isTTY && !options.template;\n\n if (isInteractive) {\n p.intro(`${BRAND.name} -- Create Session`);\n }\n\n try {\n // -- Template selection ----------------------------------------\n let templateName: string = options.template;\n\n if (!templateName) {\n if (!isInteractive) {\n p.log.error(\n 'Missing --template. Available: ' + getTemplateNames().join(', ')\n );\n process.exit(1);\n }\n\n const selected = await p.select({\n message: 'What type of session?',\n options: Object.entries(TEMPLATES).map(([key, tmpl]) => ({\n value: key,\n label: key.charAt(0).toUpperCase() + key.slice(1),\n hint: tmpl.description,\n })),\n });\n\n if (p.isCancel(selected)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n templateName = selected as string;\n }\n\n const template = getTemplate(templateName);\n if (!template) {\n p.log.error(\n `Unknown template: \"${templateName}\". Available: ${getTemplateNames().join(', ')}`\n );\n process.exit(1);\n }\n\n // -- Session name ------------------------------------------------\n let sessionName: string = options.name;\n\n if (!sessionName && isInteractive) {\n const input = await p.text({\n message: 'Session name?',\n placeholder: 'My Talk at DevFest 2026',\n validate: (v) => {\n if (!v?.trim()) return 'Session name is required';\n },\n });\n\n if (p.isCancel(input)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n sessionName = input;\n }\n\n if (!sessionName) {\n p.log.error('Missing --name flag.');\n process.exit(1);\n }\n\n // -- Date & Time ------------------------------------------------\n const now = new Date();\n let sessionDate: string;\n\n if (options.date) {\n // Non-interactive: combine --date and optional --time\n const time = options.time || '00:00';\n sessionDate = new Date(`${options.date}T${time}`).toISOString();\n } else if (isInteractive) {\n const dateInput = await p.text({\n message: 'Session date? (YYYY-MM-DD)',\n placeholder: formatLocalDate(now),\n defaultValue: formatLocalDate(now),\n validate: (v) => {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(v || ''))\n return 'Use YYYY-MM-DD format';\n if (isNaN(new Date(v!).getTime()))\n return 'Invalid date';\n },\n });\n\n if (p.isCancel(dateInput)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n const timeInput = await p.text({\n message: 'Start time? (24h format)',\n placeholder: formatLocalTime(now),\n defaultValue: formatLocalTime(now),\n validate: (v) => {\n if (!/^\\d{1,2}:\\d{2}$/.test(v || ''))\n return 'Use HH:MM format (e.g. 14:30)';\n },\n });\n\n if (p.isCancel(timeInput)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n sessionDate = new Date(`${dateInput}T${timeInput}`).toISOString();\n } else {\n sessionDate = now.toISOString();\n }\n\n // -- Duration -----------------------------------------------\n let duration: number = options.duration || template.durationMinutes;\n\n if (!options.duration && isInteractive) {\n const input = await p.text({\n message: 'Duration (minutes)?',\n defaultValue: String(template.durationMinutes),\n placeholder: String(template.durationMinutes),\n validate: (v) => {\n const n = parseInt(v || '', 10);\n if (isNaN(n) || n < 1) return 'Enter a valid number of minutes';\n },\n });\n\n if (p.isCancel(input)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n duration = parseInt(input, 10);\n }\n\n // -- Create session -----------------------------------------\n const s = p.spinner();\n s.start('Creating session...');\n\n const client = createApiClient();\n const userId = getUserIdFromToken();\n\n // Resolve project only if --project flag was passed\n let projectId: string | undefined;\n const projectName: string = options.project || '';\n if (projectName) {\n try {\n s.message('Resolving project...');\n const projectsResp = await client.get<Project[] | { data: Project[] }>(\n `/users/${userId}/projects`\n );\n const projects = Array.isArray(projectsResp)\n ? projectsResp\n : projectsResp?.data || [];\n\n const existing = projects.find(\n (proj: Project) =>\n proj.name.toLowerCase() === projectName.toLowerCase()\n );\n\n if (existing) {\n projectId = existing.id;\n } else {\n s.message('Creating project...');\n const newProject = await client.post<Project>(\n `/users/${userId}/projects`,\n { name: projectName }\n );\n projectId = newProject.id;\n }\n } catch {\n // Continue without project ID\n }\n }\n\n // Build session payload\n const sessionBody = {\n name: sessionName,\n date: sessionDate,\n durationMinutes: duration,\n ratings: [...template.ratings],\n links: [...template.links],\n ...(projectId && { projectId }),\n };\n\n s.message('Creating session...');\n const session = (await client.post('/sessions', sessionBody)) as Record<\n string,\n unknown\n >;\n\n s.stop(`${icon.check} Session created!`);\n\n if (options.json) {\n console.log(JSON.stringify(session, null, 2));\n } else {\n const pairs: [string, string][] = [\n ['Name', String(session.name || sessionName)],\n ['PIN', String(session.pin || '')],\n ['Template', templateName],\n ['Duration', `${duration} minutes`],\n ['Date', new Date(sessionDate).toLocaleString()],\n ];\n if (session.id) {\n pairs.push(['ID', String(session.id)]);\n }\n\n console.log();\n const output = renderJsxToString(\n getTermWidth(),\n pairs.length,\n KeyValue({ pairs })\n );\n console.log(output);\n\n if (session.pin) {\n const joinUrl = `https://app.audiencemeter.pro/s/${session.pin}`;\n console.log();\n p.note(joinUrl, 'Join URL');\n\n // Print QR code in terminal\n console.log();\n await new Promise<void>((resolve) => {\n qrcode.generate(joinUrl, { small: true }, (code: string) => {\n console.log(code);\n resolve();\n });\n });\n }\n\n p.outro('Share the PIN, URL, or QR code with your audience!');\n }\n } catch (error) {\n p.log.error(\n `Failed to create session: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Run: audiencemeter login');\n }\n process.exit(1);\n }\n });\n","import type { Backend, Cell } from 'terminui';\nimport { cellToAnsi } from './render.js';\n\nconst CSI = '\\x1b[';\n\n/**\n * Creates a Node.js stdout Backend implementing terminui's Backend interface.\n * Used for persistent full-screen TUI modes (dashboard, interactive list).\n *\n * The draw() method converts terminui Cell objects to ANSI escape sequences\n * and writes them to stdout with cursor positioning.\n */\nexport function createNodeBackend(): Backend {\n const stdout = process.stdout;\n\n return {\n size: () => ({\n width: stdout.columns || 80,\n height: stdout.rows || 24,\n }),\n\n draw: (content) => {\n let out = '';\n for (const { x, y, cell } of content) {\n // Move cursor to position (1-indexed)\n out += `${CSI}${y + 1};${x + 1}H`;\n out += cellToAnsi(cell);\n }\n stdout.write(out);\n },\n\n flush: () => {\n // stdout.write is already unbuffered for TTY -- no-op\n },\n\n hideCursor: () => {\n stdout.write(`${CSI}?25l`);\n },\n\n showCursor: () => {\n stdout.write(`${CSI}?25h`);\n },\n\n getCursorPosition: () => ({ x: 0, y: 0 }),\n\n setCursorPosition: (pos) => {\n stdout.write(`${CSI}${pos.y + 1};${pos.x + 1}H`);\n },\n\n clear: () => {\n stdout.write(`${CSI}2J${CSI}H`);\n },\n };\n}\n","import readline from 'node:readline';\n\n// ── Types ────────────────────────────────────────────────────────────\n\nexport interface KeypressInfo {\n name: string;\n ctrl: boolean;\n meta: boolean;\n shift: boolean;\n sequence: string;\n}\n\n// ── Alternate Screen Constants ───────────────────────────────────────\n\nconst ENTER_ALT_SCREEN = '\\x1b[?1049h';\nconst EXIT_ALT_SCREEN = '\\x1b[?1049l';\nconst HIDE_CURSOR = '\\x1b[?25l';\nconst SHOW_CURSOR = '\\x1b[?25h';\n\n// Module-level flag to track whether full screen is active.\n// Cleanup handlers only run exitFullScreen when this is true,\n// avoiding interference with normal non-fullscreen commands.\nlet isFullScreen = false;\n\n// ── Keyboard Input ───────────────────────────────────────────────────\n\n/**\n * Sets up keyboard input in raw mode and attaches a keypress handler.\n * Returns a cleanup function that removes the listener, disables raw mode,\n * and pauses stdin.\n */\nexport function setupKeyboardInput(handler: (key: KeypressInfo) => void): () => void {\n readline.emitKeypressEvents(process.stdin);\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n }\n process.stdin.resume();\n\n const listener = (_str: string, key: { name: string; ctrl: boolean; meta: boolean; shift: boolean; sequence: string }) => {\n if (key) {\n handler({\n name: key.name ?? '',\n ctrl: key.ctrl ?? false,\n meta: key.meta ?? false,\n shift: key.shift ?? false,\n sequence: key.sequence ?? '',\n });\n }\n };\n\n process.stdin.on('keypress', listener);\n\n // Return cleanup function\n return () => {\n process.stdin.removeListener('keypress', listener);\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n process.stdin.pause();\n };\n}\n\n// ── Full Screen Management ───────────────────────────────────────────\n\n/**\n * Enters alternate screen buffer and hides the cursor.\n * Registers cleanup handlers for safe terminal restoration.\n */\nexport function enterFullScreen(): void {\n if (isFullScreen) return;\n isFullScreen = true;\n process.stdout.write(ENTER_ALT_SCREEN);\n process.stdout.write(HIDE_CURSOR);\n}\n\n/**\n * Shows the cursor and exits alternate screen buffer.\n * Safe to call multiple times (idempotent via flag check).\n */\nexport function exitFullScreen(): void {\n if (!isFullScreen) return;\n isFullScreen = false;\n process.stdout.write(SHOW_CURSOR);\n process.stdout.write(EXIT_ALT_SCREEN);\n}\n\n// ── Process Cleanup Handlers ─────────────────────────────────────────\n// These ensure the terminal is restored if the process exits while\n// full screen is active. The flag check prevents interference with\n// normal non-fullscreen commands.\n\nprocess.on('exit', () => {\n if (isFullScreen) {\n // Synchronous writes needed in 'exit' handler\n try { process.stdout.write(SHOW_CURSOR); } catch { /* ignore */ }\n try { process.stdout.write(EXIT_ALT_SCREEN); } catch { /* ignore */ }\n isFullScreen = false;\n }\n});\n\nprocess.on('SIGINT', () => {\n if (isFullScreen) {\n exitFullScreen();\n }\n process.exit(0);\n});\n\nprocess.on('SIGTERM', () => {\n if (isFullScreen) {\n exitFullScreen();\n }\n process.exit(0);\n});\n\nprocess.on('uncaughtException', (err) => {\n if (isFullScreen) {\n exitFullScreen();\n }\n console.error(err);\n process.exit(1);\n});\n","import { Text } from 'terminui/jsx';\nimport { BRAND_COLORS, BRAND } from '../lib/theme.js';\n\nexport interface HeaderProps {\n readonly version?: string;\n}\n\nexport function Header({ version }: HeaderProps = {}) {\n const ver = version || BRAND.version;\n return (\n <Text bold fg={BRAND_COLORS.purple} align=\"center\">\n {`${BRAND.name} v${ver}`}\n </Text>\n );\n}\n","import { Tabs } from 'terminui/jsx';\nimport { createStyle, Modifier } from 'terminui';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface TabBarProps {\n readonly titles: readonly string[];\n readonly selected: number;\n}\n\nexport function TabBar({ titles, selected }: TabBarProps) {\n return (\n <Tabs\n titles={titles}\n selected={selected}\n border\n fg={BRAND_COLORS.dimCyan}\n highlightStyle={createStyle({ fg: BRAND_COLORS.purple, addModifier: Modifier.BOLD })}\n />\n );\n}\n","import { Text } from 'terminui/jsx';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface ShortcutBarProps {\n readonly shortcuts: readonly { key: string; label: string }[];\n}\n\nexport function ShortcutBar({ shortcuts }: ShortcutBarProps) {\n const text = shortcuts.map((s) => `${s.key}: ${s.label}`).join(' | ');\n return (\n <Text fg={BRAND_COLORS.dimCyan}>{` ${text}`}</Text>\n );\n}\n","import { VStack, HStack, Box, Text, List, Gauge } from 'terminui/jsx';\nimport { fillConstraint, lengthConstraint, createListState, createStyle, Modifier } from 'terminui';\nimport type { ListState } from 'terminui';\nimport { BRAND_COLORS, icon } from '../lib/theme.js';\nimport { Header } from './Header.js';\nimport { TabBar } from './TabBar.js';\nimport { ShortcutBar } from './ShortcutBar.js';\nimport { SessionTable, type SessionRow } from './SessionTable.js';\n\nexport interface DashboardProps {\n readonly selectedTab: number;\n readonly sessions: readonly SessionRow[];\n readonly stats: {\n readonly totalSessions: number;\n readonly totalParticipants: number;\n readonly avgEngagement: number;\n };\n readonly selectedIndex: number;\n readonly authEmail: string | null;\n readonly authExpired?: boolean;\n readonly loading?: boolean;\n readonly error?: string | null;\n}\n\nconst TAB_TITLES = ['Dashboard', 'Sessions', 'Create', 'Auth'] as const;\n\nconst SHORTCUTS = [\n { key: 'Tab/1-4', label: 'switch' },\n { key: 'j/k', label: 'navigate' },\n { key: 'Enter', label: 'select' },\n { key: 'Esc/q', label: 'quit' },\n];\n\nfunction DashboardTab({ sessions, stats, selectedIndex, loading }: {\n sessions: readonly SessionRow[];\n stats: DashboardProps['stats'];\n selectedIndex: number;\n loading?: boolean;\n}) {\n const recent = sessions.slice(0, 5);\n const listItems = recent.length > 0\n ? recent.map((s) => `${icon.session} ${s.name} ${s.pin} ${s.status}`)\n : [loading ? 'Loading sessions...' : 'No sessions yet. Press Tab to go to Create.'];\n\n const listState: ListState = createListState();\n listState.selected = recent.length > 0 ? selectedIndex : undefined;\n\n return (\n <HStack constraints={[fillConstraint(2), fillConstraint(1)]}>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Recent Sessions \">\n <List\n items={listItems}\n state={listState}\n fg={BRAND_COLORS.cyan}\n highlightStyle={createStyle({ fg: BRAND_COLORS.purple, addModifier: Modifier.BOLD })}\n />\n </Box>\n <VStack constraints={[lengthConstraint(3), lengthConstraint(3), fillConstraint(1)]}>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Sessions \">\n <Text bold fg={BRAND_COLORS.white} align=\"center\">{String(stats.totalSessions)}</Text>\n </Box>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Participants \">\n <Text bold fg={BRAND_COLORS.white} align=\"center\">{String(stats.totalParticipants)}</Text>\n </Box>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Engagement \">\n <Gauge\n percent={stats.avgEngagement}\n fg={BRAND_COLORS.green}\n />\n </Box>\n </VStack>\n </HStack>\n );\n}\n\nfunction SessionsTab({ sessions, selectedIndex, loading }: {\n sessions: readonly SessionRow[];\n selectedIndex: number;\n loading?: boolean;\n}) {\n if (sessions.length === 0) {\n return (\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan}>\n <Text fg={BRAND_COLORS.dim}>{loading ? 'Loading sessions...' : 'No sessions found. Create one with the Create tab.'}</Text>\n </Box>\n );\n }\n\n const listItems = sessions.map((s) =>\n `${icon.session} ${s.name.padEnd(30).slice(0, 30)} ${s.pin} ${s.status.padEnd(10).slice(0, 10)} ${s.date}`\n );\n\n const listState: ListState = createListState();\n listState.selected = selectedIndex;\n\n return (\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title={` Sessions (${sessions.length}) `}>\n <List\n items={listItems}\n state={listState}\n fg={BRAND_COLORS.cyan}\n highlightStyle={createStyle({ fg: BRAND_COLORS.purple, addModifier: Modifier.BOLD })}\n />\n </Box>\n );\n}\n\nfunction CreateTab() {\n return (\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Create Session \">\n <Text fg={BRAND_COLORS.white}>{` Press Enter to create a new session`}</Text>\n </Box>\n );\n}\n\nfunction AuthTab({ authEmail, authExpired }: { authEmail: string | null; authExpired?: boolean }) {\n if (authExpired) {\n return (\n <Box border borderType=\"rounded\" title=\" Authentication \">\n <Text fg={BRAND_COLORS.yellow}>{` ${icon.cross} Session expired. Press Enter to re-login.`}</Text>\n </Box>\n );\n }\n\n return (\n <Box border borderType=\"rounded\" title=\" Authentication \">\n <Text fg={authEmail ? BRAND_COLORS.green : BRAND_COLORS.yellow}>\n {authEmail\n ? ` ${icon.check} Logged in as ${authEmail}`\n : ` ${icon.cross} Not logged in. Press Enter to login.`}\n </Text>\n </Box>\n );\n}\n\nexport function Dashboard({ selectedTab, sessions, stats, selectedIndex, authEmail, authExpired, loading, error }: DashboardProps) {\n let content;\n switch (selectedTab) {\n case 0:\n content = <DashboardTab sessions={sessions} stats={stats} selectedIndex={selectedIndex} loading={loading} />;\n break;\n case 1:\n content = <SessionsTab sessions={sessions} selectedIndex={selectedIndex} loading={loading} />;\n break;\n case 2:\n content = <CreateTab />;\n break;\n case 3:\n content = <AuthTab authEmail={authEmail} authExpired={authExpired} />;\n break;\n default:\n content = <DashboardTab sessions={sessions} stats={stats} selectedIndex={selectedIndex} />;\n }\n\n if (error) {\n return (\n <VStack constraints={[lengthConstraint(1), lengthConstraint(3), lengthConstraint(1), fillConstraint(1), lengthConstraint(1)]}>\n <Header />\n <TabBar titles={[...TAB_TITLES]} selected={selectedTab} />\n <Text fg={BRAND_COLORS.yellow} align=\"center\">{`${icon.cross} ${error}`}</Text>\n {content}\n <ShortcutBar shortcuts={SHORTCUTS} />\n </VStack>\n );\n }\n\n return (\n <VStack constraints={[lengthConstraint(1), lengthConstraint(3), fillConstraint(1), lengthConstraint(1)]}>\n <Header />\n <TabBar titles={[...TAB_TITLES]} selected={selectedTab} />\n {content}\n <ShortcutBar shortcuts={SHORTCUTS} />\n </VStack>\n );\n}\n","import { VStack, Gauge, LineGauge, Text, Box } from 'terminui/jsx';\nimport { lengthConstraint } from 'terminui';\nimport type { Color } from 'terminui';\nimport type { JsxNode } from 'terminui/jsx-runtime';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface MetricsPanelProps {\n readonly engagementRate?: number;\n readonly feedbackRate?: number;\n readonly participants?: number;\n}\n\nfunction gaugeColor(ratio: number): Color {\n if (ratio >= 70) return BRAND_COLORS.green;\n if (ratio >= 40) return BRAND_COLORS.cyan;\n return BRAND_COLORS.yellow;\n}\n\nexport function MetricsPanel({ engagementRate, feedbackRate, participants }: MetricsPanelProps) {\n const items: JsxNode[] = [];\n const constraints: ReturnType<typeof lengthConstraint>[] = [];\n\n if (participants !== undefined) {\n items.push(\n <Text bold fg={BRAND_COLORS.white}>{` ${String(participants)} participants`}</Text>\n );\n constraints.push(lengthConstraint(1));\n }\n\n if (engagementRate !== undefined && engagementRate > 0) {\n items.push(\n <Gauge\n percent={engagementRate}\n border\n title={` Engagement ${engagementRate}% `}\n fg={gaugeColor(engagementRate)}\n />\n );\n constraints.push(lengthConstraint(3));\n }\n\n if (feedbackRate !== undefined && feedbackRate > 0) {\n items.push(\n <LineGauge\n percent={feedbackRate}\n border\n title={` Feedback ${feedbackRate}% `}\n fg={gaugeColor(feedbackRate)}\n />\n );\n constraints.push(lengthConstraint(3));\n }\n\n if (items.length === 0) {\n return <Text fg={BRAND_COLORS.dim}>No metrics available</Text>;\n }\n\n return (\n <VStack constraints={constraints}>\n {items}\n </VStack>\n );\n}\n","import { VStack, HStack, Box, Text, Sparkline, BarChart } from 'terminui/jsx';\nimport { fillConstraint, lengthConstraint, createBarGroup, createBar } from 'terminui';\nimport type { BarGroup, Constraint } from 'terminui';\nimport type { JsxNode } from 'terminui/jsx-runtime';\nimport { BRAND_COLORS, icon } from '../lib/theme.js';\nimport { KeyValue } from './KeyValue.js';\nimport { MetricsPanel } from './MetricsPanel.js';\n\nexport interface SessionDetailSession {\n readonly id?: string;\n readonly name: string;\n readonly pin: string;\n readonly date?: string;\n readonly durationMinutes?: number;\n readonly createdAt?: string;\n readonly questions?: readonly { id: string; type: string; label: string }[];\n readonly links?: readonly { id: string; label: string; url: string }[];\n}\n\nexport interface SessionDetailMetrics {\n readonly totalParticipants?: number;\n readonly engagementRate?: number;\n readonly feedbackCompletionRate?: number;\n}\n\nexport interface TimelineBucket {\n readonly minute: number;\n readonly positive: number;\n readonly negative: number;\n readonly pace: number;\n}\n\nexport interface SessionDetailProps {\n readonly session: SessionDetailSession;\n readonly metrics: SessionDetailMetrics | null;\n readonly timeline: readonly TimelineBucket[];\n readonly analysis: Record<string, unknown> | null;\n readonly status: string;\n}\n\nfunction statusBadge(status: string): string {\n switch (status) {\n case 'live': return `${icon.live} live`;\n case 'ended': return `${icon.ended} ended`;\n case 'upcoming': return `${icon.upcoming} upcoming`;\n default: return status;\n }\n}\n\nexport function SessionDetail({ session, metrics, timeline, analysis, status }: SessionDetailProps) {\n // Build content sections as an array, then compose at the end\n const sections: JsxNode[] = [];\n const sectionConstraints: Constraint[] = [];\n\n // -- Header: session name + status --\n sections.push(\n <Text bold fg={BRAND_COLORS.purple}>\n {` ${session.name || 'Untitled Session'} ${statusBadge(status)}`}\n </Text>\n );\n sectionConstraints.push(lengthConstraint(1));\n\n // -- Info + Metrics side-by-side --\n const dateStr = session.date\n ? new Date(session.date).toLocaleString()\n : session.createdAt\n ? new Date(session.createdAt).toLocaleString()\n : '--';\n const durationStr = session.durationMinutes\n ? `${session.durationMinutes} minutes`\n : '--';\n\n const infoPairs: [string, string][] = [\n ['PIN', session.pin || '--'],\n ['Date', dateStr],\n ['Duration', durationStr],\n ];\n if (session.id) {\n infoPairs.push(['ID', session.id]);\n }\n\n const hasMetrics = metrics && (\n (metrics.engagementRate !== undefined && metrics.engagementRate > 0) ||\n (metrics.feedbackCompletionRate !== undefined && metrics.feedbackCompletionRate > 0) ||\n (metrics.totalParticipants !== undefined)\n );\n\n if (hasMetrics) {\n const metricsHeight = 1\n + (metrics!.totalParticipants !== undefined ? 1 : 0)\n + (metrics!.engagementRate !== undefined && metrics!.engagementRate! > 0 ? 3 : 0)\n + (metrics!.feedbackCompletionRate !== undefined && metrics!.feedbackCompletionRate! > 0 ? 3 : 0);\n const infoHeight = infoPairs.length + 2; // +2 for border\n const panelHeight = Math.max(infoHeight, metricsHeight + 2);\n\n sections.push(\n <HStack constraints={[fillConstraint(1), fillConstraint(1)]}>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Session Info \">\n <KeyValue pairs={infoPairs} />\n </Box>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Metrics \">\n <MetricsPanel\n engagementRate={metrics!.engagementRate}\n feedbackRate={metrics!.feedbackCompletionRate}\n participants={metrics!.totalParticipants}\n />\n </Box>\n </HStack>\n );\n sectionConstraints.push(lengthConstraint(panelHeight));\n } else {\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Session Info \">\n <KeyValue pairs={infoPairs} />\n </Box>\n );\n sectionConstraints.push(lengthConstraint(infoPairs.length + 2));\n }\n\n // -- Sparkline: reaction timeline --\n const positiveData = timeline.map((b) => b.positive || 0);\n if (positiveData.some((v) => v > 0)) {\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Reactions Over Time \">\n <Sparkline\n data={positiveData}\n fg={BRAND_COLORS.green}\n />\n </Box>\n );\n sectionConstraints.push(lengthConstraint(5));\n }\n\n // -- BarChart: reaction summary --\n const totalPositive = timeline.reduce((sum, b) => sum + (b.positive || 0), 0);\n const totalNegative = timeline.reduce((sum, b) => sum + (b.negative || 0), 0);\n if (totalPositive > 0 || totalNegative > 0) {\n const barData: BarGroup[] = [\n createBarGroup([createBar(totalPositive)], 'Positive'),\n createBarGroup([createBar(totalNegative)], 'Negative'),\n ];\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Reaction Summary \">\n <BarChart\n data={barData}\n fg={BRAND_COLORS.cyan}\n />\n </Box>\n );\n const maxVal = Math.max(totalPositive, totalNegative, 1);\n sectionConstraints.push(lengthConstraint(Math.max(8, maxVal + 4)));\n }\n\n // -- AI Analysis --\n if (analysis && typeof analysis === 'object') {\n const summary = (analysis.summary as string) || (analysis.feedbackSummary as string) || '';\n if (summary) {\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.purple} title={` ${icon.sparkle} AI Analysis `}>\n <Text fg={BRAND_COLORS.white}>{summary}</Text>\n </Box>\n );\n const summaryLines = Math.ceil(summary.length / 70) + 2;\n sectionConstraints.push(lengthConstraint(summaryLines));\n }\n\n const insights = analysis.coachingInsights as string[] | undefined;\n if (insights && insights.length > 0) {\n const insightLines = insights.slice(0, 3).map((insight) =>\n ` ${icon.arrow} ${insight}`\n ).join('\\n');\n\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Coaching Insights \">\n <Text fg={BRAND_COLORS.white}>{insightLines}</Text>\n </Box>\n );\n sectionConstraints.push(lengthConstraint(Math.min(insights.length, 3) + 2));\n }\n }\n\n // -- Questions --\n if (session.questions && session.questions.length > 0) {\n const questionLines = session.questions.map((q) =>\n ` ${icon.dot} [${q.type}] ${q.label}`\n ).join('\\n');\n\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Rating Criteria \">\n <Text fg={BRAND_COLORS.white}>{questionLines}</Text>\n </Box>\n );\n sectionConstraints.push(lengthConstraint(session.questions.length + 2));\n }\n\n // -- Links --\n if (session.links && session.links.length > 0) {\n const linkLines = session.links.map((link) =>\n ` ${icon.dot} ${link.label}: ${link.url}`\n ).join('\\n');\n\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Links \">\n <Text fg={BRAND_COLORS.white}>{linkLines}</Text>\n </Box>\n );\n sectionConstraints.push(lengthConstraint(session.links.length + 2));\n }\n\n // -- Join URL --\n if (session.pin) {\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Join URL \">\n <Text bold fg={BRAND_COLORS.cyan}>{`https://app.audiencemeter.pro/s/${session.pin}`}</Text>\n </Box>\n );\n sectionConstraints.push(lengthConstraint(3));\n }\n\n return (\n <VStack constraints={sectionConstraints}>\n {sections}\n </VStack>\n );\n}\n","import { createTerminal, terminalResize, createListState } from 'terminui';\nimport type { Terminal, ListState } from 'terminui';\nimport { VStack, Box, List, Text, terminalDrawJsx } from 'terminui/jsx';\nimport type { JsxNode } from 'terminui/jsx-runtime';\nimport { createNodeBackend } from './node-backend.js';\nimport { setupKeyboardInput, enterFullScreen, exitFullScreen, type KeypressInfo } from './input.js';\nimport { createApiClient, createApiClientWithRefresh, getUserIdFromToken, getUserEmailFromToken, isTokenExpired } from './api-client.js';\nimport { Dashboard } from '../components/Dashboard.js';\nimport {\n SessionDetail,\n type SessionDetailSession,\n type SessionDetailMetrics,\n type TimelineBucket,\n} from '../components/SessionDetail.js';\nimport { ShortcutBar } from '../components/ShortcutBar.js';\nimport type { SessionRow } from '../components/SessionTable.js';\nimport { BRAND_COLORS, icon } from './theme.js';\nimport { fillConstraint, lengthConstraint, createStyle, Modifier } from 'terminui';\n\n// ── Types ────────────────────────────────────────────────────────────\n\ninterface SessionSummary {\n id?: string;\n name: string;\n pin: string;\n date?: string | Date;\n durationMinutes?: number;\n createdAt?: string | Date;\n [key: string]: unknown;\n}\n\ninterface SessionsResponse {\n data: SessionSummary[];\n total: number;\n page: number;\n take: number;\n}\n\ninterface Metrics {\n totalParticipants?: number;\n engagementRate?: number;\n feedbackCompletionRate?: number;\n [key: string]: unknown;\n}\n\ninterface AppState {\n selectedTab: number;\n sessions: SessionRow[];\n rawSessions: SessionSummary[];\n total: number;\n stats: {\n totalSessions: number;\n totalParticipants: number;\n avgEngagement: number;\n };\n selectedIndex: number;\n authEmail: string | null;\n authExpired: boolean;\n showingSession: string | null;\n running: boolean;\n loading: boolean;\n error: string | null;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nfunction getSessionStatus(session: SessionSummary): string {\n const sessionDate = session.date\n ? new Date(session.date as string)\n : session.createdAt\n ? new Date(session.createdAt as string)\n : null;\n\n if (!sessionDate) return 'unknown';\n\n const now = new Date();\n const durationMs = (session.durationMinutes || 60) * 60 * 1000;\n const endTime = new Date(sessionDate.getTime() + durationMs);\n\n if (now < sessionDate) return 'upcoming';\n if (now >= sessionDate && now <= endTime) return 'live';\n return 'ended';\n}\n\nfunction statusDisplay(status: string): string {\n switch (status) {\n case 'live': return `${icon.live} live`;\n case 'ended': return `${icon.ended} ended`;\n case 'upcoming': return `${icon.upcoming} soon`;\n default: return status;\n }\n}\n\nfunction toSessionRows(sessions: SessionSummary[]): SessionRow[] {\n return sessions.map((session) => {\n const date = session.date\n ? new Date(session.date as string).toLocaleDateString()\n : session.createdAt\n ? new Date(session.createdAt as string).toLocaleDateString()\n : '--';\n\n const duration = session.durationMinutes\n ? `${session.durationMinutes}min`\n : '--';\n\n const status = getSessionStatus(session);\n\n const raw = session as Record<string, unknown>;\n const count = (raw._count as Record<string, unknown> | undefined)?.attendees\n ?? raw.participantCount\n ?? '--';\n\n return {\n name: session.name || '--',\n pin: session.pin || '--',\n status: statusDisplay(status),\n date,\n duration,\n participants: String(count),\n };\n });\n}\n\n// ── Render helpers ───────────────────────────────────────────────────\n\nfunction renderDashboard(terminal: Terminal, state: AppState): void {\n terminalDrawJsx(terminal, (\n <Dashboard\n selectedTab={state.selectedTab}\n sessions={state.sessions}\n stats={state.stats}\n selectedIndex={state.selectedIndex}\n authEmail={state.authEmail}\n authExpired={state.authExpired}\n loading={state.loading}\n error={state.error}\n />\n ));\n}\n\nfunction renderSessionDetail(\n terminal: Terminal,\n session: SessionDetailSession,\n metrics: SessionDetailMetrics | null,\n timeline: readonly TimelineBucket[],\n analysis: Record<string, unknown> | null,\n status: string,\n): void {\n const detailShortcuts = [\n { key: 'Esc/q', label: 'back' },\n ];\n\n terminalDrawJsx(terminal, (\n <VStack constraints={[fillConstraint(1), lengthConstraint(1)]}>\n <SessionDetail\n session={session}\n metrics={metrics}\n timeline={timeline}\n analysis={analysis}\n status={status}\n />\n <ShortcutBar shortcuts={detailShortcuts} />\n </VStack>\n ));\n}\n\nfunction renderInteractiveList(\n terminal: Terminal,\n sessionRows: readonly SessionRow[],\n total: number,\n selectedIndex: number,\n): void {\n const listItems = sessionRows.map((s) =>\n `${icon.session} ${s.name.padEnd(30).slice(0, 30)} ${s.pin} ${s.status.padEnd(10).slice(0, 10)} ${s.date}`\n );\n\n const listState: ListState = createListState();\n listState.selected = selectedIndex;\n\n terminalDrawJsx(terminal, (\n <VStack constraints={[lengthConstraint(1), fillConstraint(1), lengthConstraint(1)]}>\n <Text bold fg={BRAND_COLORS.purple} align=\"center\">{`Sessions (${sessionRows.length} of ${total})`}</Text>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan}>\n <List\n items={listItems}\n state={listState}\n fg={BRAND_COLORS.cyan}\n highlightStyle={createStyle({ fg: BRAND_COLORS.purple, addModifier: Modifier.BOLD })}\n />\n </Box>\n <Text fg={BRAND_COLORS.dimCyan}>{' j/k: navigate | Enter: view | q: quit'}</Text>\n </VStack>\n ));\n}\n\n// ── Session Detail Fetching ──────────────────────────────────────────\n\nasync function fetchSessionDetail(sessionId: string): Promise<{\n session: SessionDetailSession;\n metrics: SessionDetailMetrics | null;\n timeline: TimelineBucket[];\n analysis: Record<string, unknown> | null;\n status: string;\n}> {\n const client = createApiClient();\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);\n const path = !isUuid\n ? `/sessions/by-pin/${sessionId}`\n : `/sessions/${sessionId}`;\n\n const session = await client.get<SessionSummary>(path);\n\n let metrics: Metrics | null = null;\n let timeline: TimelineBucket[] = [];\n let analysis: Record<string, unknown> | null = null;\n\n if (session.id) {\n const results = await Promise.allSettled([\n client.get<Metrics>(`/sessions/${session.id}/metrics`),\n client.get<TimelineBucket[]>(`/sessions/${session.id}/sentiment-timeline`),\n client.get<Record<string, unknown>>(`/sessions/${session.id}/analysis`),\n ]);\n\n if (results[0]!.status === 'fulfilled') metrics = results[0]!.value;\n if (results[1]!.status === 'fulfilled') timeline = results[1]!.value || [];\n if (results[2]!.status === 'fulfilled') analysis = results[2]!.value;\n }\n\n const status = getSessionStatus(session);\n\n return {\n session: {\n id: session.id,\n name: session.name,\n pin: session.pin,\n date: session.date ? String(session.date) : undefined,\n durationMinutes: session.durationMinutes,\n createdAt: session.createdAt ? String(session.createdAt) : undefined,\n questions: (session as Record<string, unknown>).questions as SessionDetailSession['questions'],\n links: (session as Record<string, unknown>).links as SessionDetailSession['links'],\n },\n metrics: metrics ? {\n totalParticipants: metrics.totalParticipants,\n engagementRate: metrics.engagementRate,\n feedbackCompletionRate: metrics.feedbackCompletionRate,\n } : null,\n timeline,\n analysis,\n status,\n };\n}\n\n// ── Dashboard ────────────────────────────────────────────────────────\n\nexport async function startDashboard(): Promise<void> {\n // Check authentication\n let userId: string;\n try {\n userId = getUserIdFromToken();\n } catch {\n console.error('Not authenticated. Run: audiencemeter login');\n process.exit(1);\n return; // TypeScript needs this after process.exit\n }\n\n let authEmail: string | null = null;\n try {\n authEmail = getUserEmailFromToken();\n } catch {\n authEmail = null;\n }\n\n // Enter full screen\n enterFullScreen();\n\n const backend = createNodeBackend();\n const terminal = createTerminal(backend);\n\n // Check if token is expired before showing status\n let authExpired = false;\n try {\n const token = (await import('./config.js')).getAuthToken();\n authExpired = isTokenExpired(token);\n } catch {\n // no token at all\n }\n\n // Initialize state\n const state: AppState = {\n selectedTab: 0,\n sessions: [],\n rawSessions: [],\n total: 0,\n stats: { totalSessions: 0, totalParticipants: 0, avgEngagement: 0 },\n selectedIndex: 0,\n authEmail: authExpired ? null : authEmail,\n authExpired,\n showingSession: null,\n running: true,\n loading: true,\n error: null,\n };\n\n // Initial render with empty state (non-blocking)\n renderDashboard(terminal, state);\n\n // Session detail state\n let detailData: Awaited<ReturnType<typeof fetchSessionDetail>> | null = null;\n\n // Re-render helper\n const rerender = () => {\n if (state.showingSession && detailData) {\n renderSessionDetail(\n terminal,\n detailData.session,\n detailData.metrics,\n detailData.timeline,\n detailData.analysis,\n detailData.status,\n );\n } else {\n renderDashboard(terminal, state);\n }\n };\n\n // Setup keyboard input\n const cleanupInput = setupKeyboardInput(async (key: KeypressInfo) => {\n if (!state.running) return;\n\n // Ctrl+C always exits\n if (key.ctrl && key.name === 'c') {\n state.running = false;\n cleanupInput();\n exitFullScreen();\n process.exit(0);\n return;\n }\n\n // Showing session detail -- Esc/q goes back\n if (state.showingSession) {\n if (key.name === 'escape' || key.name === 'q') {\n state.showingSession = null;\n detailData = null;\n rerender();\n }\n return;\n }\n\n // Tab switching\n if (key.name === 'tab') {\n state.selectedTab = (state.selectedTab + 1) % 4;\n state.selectedIndex = 0;\n rerender();\n return;\n }\n\n // Number keys for tab selection\n if (['1', '2', '3', '4'].includes(key.name)) {\n state.selectedTab = parseInt(key.name, 10) - 1;\n state.selectedIndex = 0;\n rerender();\n return;\n }\n\n // Navigation (j/k/up/down)\n const itemCount = state.selectedTab === 0\n ? Math.min(state.sessions.length, 5)\n : state.sessions.length;\n\n if (itemCount > 0) {\n if (key.name === 'j' || key.name === 'down') {\n state.selectedIndex = (state.selectedIndex + 1) % itemCount;\n rerender();\n return;\n }\n\n if (key.name === 'k' || key.name === 'up') {\n state.selectedIndex = (state.selectedIndex - 1 + itemCount) % itemCount;\n rerender();\n return;\n }\n }\n\n // Enter key -- action depends on tab\n if (key.name === 'return') {\n if (state.selectedTab === 0 || state.selectedTab === 1) {\n // Dashboard or Sessions tab -- drill into session detail\n const rawSession = state.rawSessions[state.selectedIndex];\n if (rawSession) {\n const sessionId = rawSession.id || rawSession.pin;\n if (sessionId) {\n state.showingSession = sessionId;\n try {\n detailData = await fetchSessionDetail(sessionId);\n } catch {\n detailData = {\n session: {\n name: rawSession.name,\n pin: rawSession.pin,\n date: rawSession.date ? String(rawSession.date) : undefined,\n durationMinutes: rawSession.durationMinutes,\n createdAt: rawSession.createdAt ? String(rawSession.createdAt) : undefined,\n },\n metrics: null,\n timeline: [],\n analysis: null,\n status: getSessionStatus(rawSession),\n };\n }\n rerender();\n }\n }\n return;\n }\n\n if (state.selectedTab === 2) {\n // Create tab -- exit full screen, run create flow, then exit\n state.running = false;\n cleanupInput();\n exitFullScreen();\n\n try {\n const { createCommand } = await import('../commands/create.js');\n await createCommand.parseAsync(['node', 'audiencemeter'], { from: 'node' });\n } catch {\n // Command may throw on cancel\n }\n return;\n }\n\n if (state.selectedTab === 3) {\n // Auth tab -- exit full screen, run auth flow, then exit\n state.running = false;\n cleanupInput();\n exitFullScreen();\n\n try {\n const { authCommand } = await import('../commands/auth.js');\n const action = (state.authEmail && !state.authExpired) ? 'whoami' : 'login';\n await authCommand.parseAsync(['node', 'audiencemeter', action], { from: 'node' });\n } catch {\n // Command may throw on cancel\n }\n return;\n }\n }\n\n // Quit\n if (key.name === 'q' || key.name === 'escape') {\n state.running = false;\n cleanupInput();\n exitFullScreen();\n return;\n }\n });\n\n // Listen for terminal resize\n const onResize = () => {\n terminalResize(terminal, backend.size());\n rerender();\n };\n process.stdout.on('resize', onResize);\n\n // Fetch sessions asynchronously (non-blocking, with token auto-refresh)\n (async () => {\n try {\n const client = await createApiClientWithRefresh();\n\n // If token was refreshed, update auth display\n if (state.authExpired) {\n state.authExpired = false;\n state.authEmail = getUserEmailFromToken();\n state.error = null;\n }\n\n const queryParams = new URLSearchParams();\n queryParams.set('take', '50');\n queryParams.set('sort', 'createdAt');\n queryParams.set('order', 'desc');\n\n const response = await client.get<SessionsResponse | SessionSummary[]>(\n `/users/${userId}/sessions?${queryParams.toString()}`\n );\n\n const sessions: SessionSummary[] = Array.isArray(response)\n ? response\n : response?.data || [];\n const total = Array.isArray(response)\n ? sessions.length\n : response?.total || sessions.length;\n\n state.rawSessions = sessions;\n state.sessions = toSessionRows(sessions);\n state.total = total;\n\n // Calculate stats\n let totalParticipants = 0;\n for (const s of sessions) {\n const raw = s as Record<string, unknown>;\n const count = (raw._count as Record<string, unknown> | undefined)?.attendees\n ?? raw.participantCount\n ?? 0;\n totalParticipants += Number(count) || 0;\n }\n\n state.stats = {\n totalSessions: total,\n totalParticipants,\n avgEngagement: 0,\n };\n\n state.loading = false;\n rerender();\n } catch (err) {\n state.loading = false;\n const msg = err instanceof Error ? err.message : 'Failed to load sessions';\n if (msg.includes('expired') || msg.includes('Not authenticated')) {\n state.authExpired = true;\n state.authEmail = null;\n state.error = 'Session expired. Go to Auth tab and press Enter to re-login.';\n } else {\n state.error = msg;\n }\n rerender();\n }\n })();\n\n // Wait until the user quits\n return new Promise<void>((resolve) => {\n const check = setInterval(() => {\n if (!state.running) {\n clearInterval(check);\n process.stdout.removeListener('resize', onResize);\n resolve();\n }\n }, 100);\n });\n}\n\n// ── Interactive List ─────────────────────────────────────────────────\n\nexport async function startInteractiveList(\n sessionRows: SessionRow[],\n rawSessions: SessionSummary[],\n total: number,\n): Promise<void> {\n enterFullScreen();\n\n const backend = createNodeBackend();\n const terminal = createTerminal(backend);\n\n let selectedIndex = 0;\n let running = true;\n let showingDetail = false;\n let detailData: Awaited<ReturnType<typeof fetchSessionDetail>> | null = null;\n\n const rerender = () => {\n if (showingDetail && detailData) {\n renderSessionDetail(\n terminal,\n detailData.session,\n detailData.metrics,\n detailData.timeline,\n detailData.analysis,\n detailData.status,\n );\n } else {\n renderInteractiveList(terminal, sessionRows, total, selectedIndex);\n }\n };\n\n rerender();\n\n const cleanupInput = setupKeyboardInput(async (key: KeypressInfo) => {\n if (!running) return;\n\n if (key.ctrl && key.name === 'c') {\n running = false;\n cleanupInput();\n exitFullScreen();\n process.exit(0);\n return;\n }\n\n // Showing detail -- Esc/q goes back to list\n if (showingDetail) {\n if (key.name === 'escape' || key.name === 'q') {\n showingDetail = false;\n detailData = null;\n rerender();\n }\n return;\n }\n\n // Navigation\n if (sessionRows.length > 0) {\n if (key.name === 'j' || key.name === 'down') {\n selectedIndex = (selectedIndex + 1) % sessionRows.length;\n rerender();\n return;\n }\n\n if (key.name === 'k' || key.name === 'up') {\n selectedIndex = (selectedIndex - 1 + sessionRows.length) % sessionRows.length;\n rerender();\n return;\n }\n }\n\n // Enter -- drill into session detail\n if (key.name === 'return' && sessionRows.length > 0) {\n const rawSession = rawSessions[selectedIndex];\n if (rawSession) {\n const sessionId = rawSession.id || rawSession.pin;\n if (sessionId) {\n showingDetail = true;\n try {\n detailData = await fetchSessionDetail(sessionId);\n } catch {\n detailData = {\n session: {\n name: rawSession.name,\n pin: rawSession.pin,\n date: rawSession.date ? String(rawSession.date) : undefined,\n durationMinutes: rawSession.durationMinutes,\n createdAt: rawSession.createdAt ? String(rawSession.createdAt) : undefined,\n },\n metrics: null,\n timeline: [],\n analysis: null,\n status: getSessionStatus(rawSession),\n };\n }\n rerender();\n }\n }\n return;\n }\n\n // Quit\n if (key.name === 'q' || key.name === 'escape') {\n running = false;\n cleanupInput();\n exitFullScreen();\n return;\n }\n });\n\n // Listen for terminal resize\n const onResize = () => {\n terminalResize(terminal, backend.size());\n rerender();\n };\n process.stdout.on('resize', onResize);\n\n return new Promise<void>((resolve) => {\n const check = setInterval(() => {\n if (!running) {\n clearInterval(check);\n process.stdout.removeListener('resize', onResize);\n resolve();\n }\n }, 100);\n });\n}\n","import { Command } from 'commander';\nimport { authCommand, loginCommand, logoutCommand } from './commands/auth.js';\nimport { createCommand } from './commands/create.js';\nimport { listCommand } from './commands/list.js';\nimport { showCommand } from './commands/show.js';\nimport { deleteCommand } from './commands/delete.js';\nimport { BRAND } from './lib/theme.js';\n\nconst program = new Command();\n\nprogram\n .name('audiencemeter')\n .description(\n `${BRAND.name} CLI — ${BRAND.tagline}`\n )\n .version(BRAND.version, '-v, --version');\n\n// Register commands\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(authCommand);\nprogram.addCommand(createCommand);\nprogram.addCommand(listCommand);\nprogram.addCommand(showCommand);\nprogram.addCommand(deleteCommand);\n\n// Detect if args were passed\nconst hasArgs = process.argv.length > 2;\n\nif (hasArgs) {\n // Subcommands: parse with Commander (static print-and-exit)\n program.parse(process.argv);\n} else if (process.stdout.isTTY) {\n // No args + TTY: launch persistent full-screen TUI dashboard\n import('./lib/app.js')\n .then(({ startDashboard }) => startDashboard())\n .catch((err) => {\n // Ensure terminal is restored on error\n import('./lib/input.js').then(({ exitFullScreen }) => {\n exitFullScreen();\n }).catch(() => { /* ignore */ });\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n });\n} else {\n // No args + non-TTY (piped): show help text\n program.help();\n}\n","import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport { createApiClient, getUserIdFromToken } from '../lib/api-client.js';\nimport { icon } from '../lib/theme.js';\nimport { renderJsxToString, getTermWidth } from '../lib/render.js';\nimport { SessionTable, type SessionRow } from '../components/SessionTable.js';\n\ninterface SessionSummary {\n id?: string;\n name: string;\n pin: string;\n date?: string | Date;\n durationMinutes?: number;\n createdAt?: string | Date;\n [key: string]: unknown;\n}\n\ninterface SessionsResponse {\n data: SessionSummary[];\n total: number;\n page: number;\n take: number;\n}\n\nfunction getSessionStatus(session: SessionSummary): string {\n const sessionDate = session.date\n ? new Date(session.date)\n : session.createdAt\n ? new Date(session.createdAt)\n : null;\n\n if (!sessionDate) return 'unknown';\n\n const now = new Date();\n const durationMs = (session.durationMinutes || 60) * 60 * 1000;\n const endTime = new Date(sessionDate.getTime() + durationMs);\n\n if (now < sessionDate) return 'upcoming';\n if (now >= sessionDate && now <= endTime) return 'live';\n return 'ended';\n}\n\nexport const listCommand = new Command('list')\n .alias('ls')\n .description('List your feedback sessions')\n .option('--project <name>', 'Filter by project name')\n .option('--limit <n>', 'Max sessions to display', parseInt, 20)\n .option('--json', 'Output as JSON')\n .action(async (options) => {\n const s = p.spinner();\n s.start('Fetching sessions...');\n\n try {\n const client = createApiClient();\n const userId = getUserIdFromToken();\n\n const queryParams = new URLSearchParams();\n queryParams.set('take', String(options.limit));\n queryParams.set('sort', 'createdAt');\n queryParams.set('order', 'desc');\n\n const response = await client.get<SessionsResponse | SessionSummary[]>(\n `/users/${userId}/sessions?${queryParams.toString()}`\n );\n\n s.stop();\n\n const sessions: SessionSummary[] = Array.isArray(response)\n ? response\n : response?.data || [];\n const total = Array.isArray(response)\n ? sessions.length\n : response?.total || sessions.length;\n\n if (sessions.length === 0) {\n p.log.warn('No sessions found.');\n p.log.info(\n 'Create one: audiencemeter create --template talk --project \"My Talk\"'\n );\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(sessions, null, 2));\n return;\n }\n\n // Build rich table\n const statusDisplay = (status: string) => {\n switch (status) {\n case 'live':\n return `${icon.live} live`;\n case 'ended':\n return `${icon.ended} ended`;\n case 'upcoming':\n return `${icon.upcoming} soon`;\n default:\n return status;\n }\n };\n\n const rows: SessionRow[] = sessions.map((session) => {\n const date = session.date\n ? new Date(session.date).toLocaleDateString()\n : session.createdAt\n ? new Date(session.createdAt).toLocaleDateString()\n : '--';\n\n const duration = session.durationMinutes\n ? `${session.durationMinutes}min`\n : '--';\n\n const status = getSessionStatus(session);\n\n const raw = session as Record<string, unknown>;\n const count =\n (raw._count as Record<string, unknown> | undefined)?.attendees ??\n raw.participantCount ??\n '--';\n const participants = String(count);\n\n return {\n name: session.name || '--',\n pin: session.pin || '--',\n status: statusDisplay(status),\n date,\n duration,\n participants,\n };\n });\n\n // TTY mode: enter interactive full-screen list with keyboard navigation\n if (process.stdout.isTTY && !options.json) {\n const { startInteractiveList } = await import('../lib/app.js');\n await startInteractiveList(rows, sessions, total);\n return;\n }\n\n // Non-TTY / static output: render table and print\n const tableHeight = rows.length + 4; // header + border + rows\n const output = renderJsxToString(\n getTermWidth(),\n tableHeight,\n SessionTable({ sessions: rows, total })\n );\n console.log(output);\n } catch (error) {\n s.stop(\n `${icon.cross} Failed to list sessions: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Run: audiencemeter login');\n }\n process.exit(1);\n }\n });\n","import { Table, Box } from 'terminui/jsx';\nimport { fillConstraint, lengthConstraint } from 'terminui';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface SessionRow {\n readonly name: string;\n readonly pin: string;\n readonly status: string;\n readonly date: string;\n readonly duration: string;\n readonly participants: string;\n}\n\nexport interface SessionTableProps {\n readonly sessions: readonly SessionRow[];\n readonly total: number;\n}\n\nexport function SessionTable({ sessions, total }: SessionTableProps) {\n const widths = [\n fillConstraint(1), // Name -- takes remaining space\n lengthConstraint(7), // PIN\n lengthConstraint(10), // Status\n lengthConstraint(12), // Date\n lengthConstraint(10), // Duration\n lengthConstraint(6), // Ppl\n ];\n\n const header = ['Name', 'PIN', 'Status', 'Date', 'Duration', 'Ppl'];\n\n const rows = sessions.map((s) => [\n s.name,\n s.pin,\n s.status,\n s.date,\n s.duration,\n s.participants,\n ]);\n\n return (\n <Box\n border\n borderType=\"rounded\"\n fg={BRAND_COLORS.dimCyan}\n title={` Sessions (${sessions.length} of ${total}) `}\n >\n <Table\n widths={widths}\n header={header}\n rows={rows}\n fg={BRAND_COLORS.cyan}\n columnSpacing={1}\n />\n </Box>\n );\n}\n","import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport { createApiClient, getUserIdFromToken } from '../lib/api-client.js';\nimport { icon } from '../lib/theme.js';\nimport { renderJsxToString, getTermWidth } from '../lib/render.js';\nimport {\n SessionDetail,\n type SessionDetailSession,\n type SessionDetailMetrics,\n type TimelineBucket,\n} from '../components/SessionDetail.js';\n\ninterface Session {\n id?: string;\n userId?: string;\n name: string;\n pin: string;\n date?: string;\n durationMinutes?: number;\n createdAt?: string;\n questions?: Array<{ id: string; type: string; label: string }>;\n links?: Array<{ id: string; label: string; url: string }>;\n [key: string]: unknown;\n}\n\ninterface Metrics {\n totalParticipants?: number;\n engagementRate?: number;\n feedbackCompletionRate?: number;\n [key: string]: unknown;\n}\n\nfunction getSessionStatus(session: Session): string {\n const sessionDate = session.date\n ? new Date(session.date)\n : session.createdAt\n ? new Date(session.createdAt)\n : null;\n\n if (!sessionDate) return 'unknown';\n\n const now = new Date();\n const durationMs = (session.durationMinutes || 60) * 60 * 1000;\n const endTime = new Date(sessionDate.getTime() + durationMs);\n\n if (now < sessionDate) return 'upcoming';\n if (now >= sessionDate && now <= endTime) return 'live';\n return 'ended';\n}\n\nexport const showCommand = new Command('show')\n .description('Show session details with rich metrics')\n .argument('<session-id>', 'Session ID (UUID) or PIN (5 characters)')\n .option('--json', 'Output as JSON')\n .action(async (sessionId: string, options) => {\n const s = p.spinner();\n s.start('Fetching session...');\n\n try {\n const client = createApiClient();\n\n // Determine if input looks like a UUID or a PIN\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);\n const path = !isUuid\n ? `/sessions/by-pin/${sessionId}`\n : `/sessions/${sessionId}`;\n\n const session = await client.get<Session>(path);\n\n // Fetch metrics, timeline, and AI analysis in parallel (owner only)\n let metrics: Metrics | null = null;\n let timeline: TimelineBucket[] = [];\n let analysis: Record<string, unknown> | null = null;\n\n let isOwner = false;\n try {\n isOwner = !!session.userId && session.userId === getUserIdFromToken();\n } catch {\n // Not authenticated — skip owner-only data\n }\n\n if (session.id && isOwner) {\n s.message('Fetching metrics...');\n\n const results = await Promise.allSettled([\n client.get<Metrics>(`/sessions/${session.id}/metrics`),\n client.get<TimelineBucket[]>(\n `/sessions/${session.id}/sentiment-timeline`\n ),\n client.get<Record<string, unknown>>(\n `/sessions/${session.id}/analysis`\n ),\n ]);\n\n if (results[0]!.status === 'fulfilled') metrics = results[0]!.value;\n if (results[1]!.status === 'fulfilled')\n timeline = results[1]!.value || [];\n if (results[2]!.status === 'fulfilled') analysis = results[2]!.value;\n }\n\n s.stop();\n\n // -- JSON output ------------------------------------------------\n if (options.json) {\n const output = {\n ...session,\n ...(metrics && { metrics }),\n ...(timeline.length && { timeline }),\n ...(analysis && { analysis }),\n };\n console.log(JSON.stringify(output, null, 2));\n return;\n }\n\n // -- Rich JSX output ------------------------------------------------\n const status = getSessionStatus(session);\n\n // Calculate height based on content\n let height = 3; // header + spacing\n const infoPairCount = 3 + (session.id ? 1 : 0);\n const hasMetrics = metrics && (\n (metrics.engagementRate !== undefined && metrics.engagementRate > 0) ||\n (metrics.feedbackCompletionRate !== undefined && metrics.feedbackCompletionRate > 0) ||\n (metrics.totalParticipants !== undefined)\n );\n\n if (hasMetrics) {\n const metricsH = 1\n + (metrics!.totalParticipants !== undefined ? 1 : 0)\n + (metrics!.engagementRate !== undefined && metrics!.engagementRate! > 0 ? 3 : 0)\n + (metrics!.feedbackCompletionRate !== undefined && metrics!.feedbackCompletionRate! > 0 ? 3 : 0);\n height += Math.max(infoPairCount + 2, metricsH + 2);\n } else {\n height += infoPairCount + 2;\n }\n\n // Timeline sparkline\n const positiveData = timeline.map((b) => b.positive || 0);\n if (positiveData.some((v) => v > 0)) {\n height += 5;\n }\n\n // Bar chart\n const totalPositive = timeline.reduce((sum, b) => sum + (b.positive || 0), 0);\n const totalNegative = timeline.reduce((sum, b) => sum + (b.negative || 0), 0);\n if (totalPositive > 0 || totalNegative > 0) {\n const maxVal = Math.max(totalPositive, totalNegative, 1);\n height += Math.max(8, maxVal + 4);\n }\n\n // AI Analysis\n if (analysis && typeof analysis === 'object') {\n const summary = (analysis.summary as string) || (analysis.feedbackSummary as string) || '';\n if (summary) {\n height += Math.ceil(summary.length / 70) + 2;\n }\n const insights = analysis.coachingInsights as string[] | undefined;\n if (insights && insights.length > 0) {\n height += Math.min(insights.length, 3) + 2;\n }\n }\n\n // Questions\n if (session.questions && session.questions.length > 0) {\n height += session.questions.length + 2;\n }\n\n // Links\n if (session.links && session.links.length > 0) {\n height += session.links.length + 2;\n }\n\n // Join URL\n if (session.pin) {\n height += 3;\n }\n\n const sessionData: SessionDetailSession = {\n id: session.id,\n name: session.name,\n pin: session.pin,\n date: session.date,\n durationMinutes: session.durationMinutes,\n createdAt: session.createdAt,\n questions: session.questions,\n links: session.links,\n };\n\n const metricsData: SessionDetailMetrics | null = metrics\n ? {\n totalParticipants: metrics.totalParticipants,\n engagementRate: metrics.engagementRate,\n feedbackCompletionRate: metrics.feedbackCompletionRate,\n }\n : null;\n\n console.log();\n const output = renderJsxToString(\n getTermWidth(),\n height,\n SessionDetail({\n session: sessionData,\n metrics: metricsData,\n timeline,\n analysis,\n status,\n })\n );\n console.log(output);\n } catch (error) {\n s.stop(\n `${icon.cross} Failed to fetch session: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Run: audiencemeter login');\n }\n process.exit(1);\n }\n });\n","import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport { createApiClient } from '../lib/api-client.js';\nimport { icon } from '../lib/theme.js';\n\nexport const deleteCommand = new Command('delete')\n .alias('rm')\n .description('Delete a session')\n .argument('<session-id>', 'Session ID (UUID)')\n .option('--force', 'Skip confirmation prompt')\n .action(async (sessionId: string, options) => {\n try {\n const client = createApiClient();\n\n // Fetch session details to show name in confirmation\n let sessionName = sessionId;\n try {\n const session = await client.get<Record<string, unknown>>(\n `/sessions/${sessionId}`\n );\n if (session.name) {\n sessionName = session.name as string;\n }\n } catch {\n // If we can't fetch, proceed with the ID\n }\n\n // Confirm deletion unless --force\n if (!options.force) {\n const confirmed = await p.confirm({\n message: `Delete session \"${sessionName}\"? This cannot be undone.`,\n });\n\n if (p.isCancel(confirmed) || !confirmed) {\n p.log.info('Cancelled.');\n return;\n }\n }\n\n const s = p.spinner();\n s.start('Deleting session...');\n\n await client.delete(`/sessions/${sessionId}`);\n\n s.stop(`${icon.check} Session \"${sessionName}\" deleted.`);\n } catch (error) {\n p.log.error(\n `Failed to delete session: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Run: audiencemeter login');\n }\n process.exit(1);\n }\n });\n"],"mappings":"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","names":["getAuthToken","SUPABASE_URL","text","Command","p","Text","jsx","jsx","Text","jsx","text","VStack","HStack","Box","Text","fillConstraint","lengthConstraint","createStyle","Modifier","jsx","jsxs","VStack","Gauge","Text","lengthConstraint","jsx","VStack","HStack","Box","Text","fillConstraint","lengthConstraint","jsx","jsxs","createTerminal","createListState","VStack","Box","List","Text","terminalDrawJsx","fillConstraint","lengthConstraint","createStyle","Modifier","jsx","jsxs","createCommand","authCommand","Command","Command","p","fillConstraint","lengthConstraint","jsx","getSessionStatus","Command","statusDisplay","startInteractiveList","Command","p","getSessionStatus","output","Command","p","Command","startDashboard","exitFullScreen"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/config.ts","../src/lib/theme.ts","../src/lib/render.ts","../src/components/KeyValue.tsx","../src/commands/auth.ts","../src/lib/api-client.ts","../src/lib/templates.ts","../src/commands/create.ts","../src/lib/pin-mask.ts","../src/lib/node-backend.ts","../src/lib/input.ts","../src/components/Header.tsx","../src/components/TabBar.tsx","../src/components/ShortcutBar.tsx","../src/components/Dashboard.tsx","../src/components/MetricsPanel.tsx","../src/components/SessionDetail.tsx","../src/lib/app.tsx","../src/index.ts","../src/commands/list.ts","../src/components/SessionTable.tsx","../src/commands/show.ts","../src/commands/delete.ts"],"sourcesContent":["import Conf from 'conf';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst config = new Conf({\n projectName: 'audiencemeter',\n projectSuffix: '',\n schema: {\n apiUrl: {\n type: 'string' as const,\n default: 'https://api.audiencemeter.pro/v1',\n },\n authToken: {\n type: 'string' as const,\n default: '',\n },\n refreshToken: {\n type: 'string' as const,\n default: '',\n },\n privacyMode: {\n type: 'boolean' as const,\n default: true,\n },\n },\n});\n\n// One-time migration from old config path (audiencemeter-nodejs → audiencemeter)\n(function migrateOldConfig() {\n const newPath = config.path;\n const oldPath = path.join(path.dirname(path.dirname(newPath)), 'audiencemeter-nodejs', 'config.json');\n\n try {\n if (!fs.existsSync(oldPath)) return;\n // Only migrate if new config has no auth token (fresh install or first run after update)\n if (config.get('authToken')) return;\n\n const oldData = JSON.parse(fs.readFileSync(oldPath, 'utf-8'));\n if (oldData.authToken) {\n config.set('authToken', oldData.authToken);\n }\n if (oldData.refreshToken) {\n config.set('refreshToken', oldData.refreshToken);\n }\n if (oldData.apiUrl && oldData.apiUrl !== 'https://api.audiencemeter.pro/v1') {\n config.set('apiUrl', oldData.apiUrl);\n }\n\n // Remove old config directory\n fs.rmSync(path.dirname(oldPath), { recursive: true, force: true });\n } catch {\n // Migration is best-effort — don't break startup\n }\n})();\n\nexport function getConfig(): typeof config {\n return config;\n}\n\nexport function getAuthToken(): string {\n const token = config.get('authToken') as string;\n if (!token) {\n throw new Error('Not authenticated. Run: audiencemeter login');\n }\n return token;\n}\n\nexport function setAuthToken(token: string): void {\n config.set('authToken', token);\n}\n\nexport function clearAuthToken(): void {\n config.set('authToken', '');\n config.set('refreshToken', '');\n}\n\nexport function getRefreshToken(): string {\n return config.get('refreshToken') as string;\n}\n\nexport function setRefreshToken(token: string): void {\n config.set('refreshToken', token);\n}\n\nexport function getApiUrl(): string {\n return config.get('apiUrl') as string;\n}\n\nexport function setApiUrl(url: string): void {\n config.set('apiUrl', url);\n}\n\nexport function getPrivacyMode(): boolean {\n return config.get('privacyMode') as boolean;\n}\n\nexport function setPrivacyMode(value: boolean): void {\n config.set('privacyMode', value);\n}\n","import { rgbColor, type Color } from 'terminui';\n\n// Brand colors as terminui Color objects (no ANSI escape strings)\nexport const BRAND_COLORS: Record<string, Color> = {\n purple: rgbColor(175, 95, 255), // Brand purple\n cyan: rgbColor(80, 220, 220), // Info/borders\n green: rgbColor(80, 220, 120), // Success/gauges\n yellow: rgbColor(255, 200, 60), // PINs\n dimCyan: rgbColor(60, 160, 170), // Borders, labels\n red: rgbColor(255, 100, 100), // Errors\n dim: rgbColor(128, 128, 128), // Dimmed text\n white: rgbColor(230, 230, 230), // Primary text\n};\n\nexport const icon = {\n session: '\\u25cf', // ●\n live: '\\u25cf', // ●\n ended: '\\u25cb', // ○\n upcoming: '\\u25d4', // ◔\n check: '\\u2714', // ✔\n cross: '\\u2718', // ✘\n arrow: '\\u276f', // ❯\n dot: '\\u00b7', // ·\n bar: '\\u2503', // ┃\n dash: '\\u2500', // ─\n star: '\\u2605', // ★\n sparkle: '\\u2728', // ✨\n};\n\nexport const BRAND = {\n name: 'AudienceMeter',\n tagline: 'Speaker feedback, beautifully managed',\n version: '0.1.1',\n};\n","import {\n createTestBackendState,\n createTestBackend,\n createTerminal,\n testBackendCellAt,\n type Cell,\n} from 'terminui';\nimport { terminalDrawJsx } from 'terminui/jsx';\nimport type { JsxNode } from 'terminui/jsx-runtime';\n\n// ── ANSI Color Conversion (shared with node-backend.ts) ──────────────\n\ntype CellColor = { type: string; r?: number; g?: number; b?: number; index?: number };\n\nconst NAMED_FG: Record<string, string> = {\n black: '30', red: '31', green: '32', yellow: '33', blue: '34',\n magenta: '35', cyan: '36', gray: '37', white: '97',\n 'dark-gray': '90', 'light-red': '91', 'light-green': '92',\n 'light-yellow': '93', 'light-blue': '94', 'light-magenta': '95', 'light-cyan': '96',\n};\n\nconst NAMED_BG: Record<string, string> = {\n black: '40', red: '41', green: '42', yellow: '43', blue: '44',\n magenta: '45', cyan: '46', gray: '47', white: '107',\n 'dark-gray': '100', 'light-red': '101', 'light-green': '102',\n 'light-yellow': '103', 'light-blue': '104', 'light-magenta': '105', 'light-cyan': '106',\n};\n\nconst RESET = '\\x1b[0m';\n\nexport function fgAnsi(c: CellColor | undefined): string {\n if (!c || c.type === 'reset') return '';\n if (c.type === 'rgb') return `\\x1b[38;2;${c.r};${c.g};${c.b}m`;\n if (c.type === 'indexed') return `\\x1b[38;5;${c.index}m`;\n return NAMED_FG[c.type] ? `\\x1b[${NAMED_FG[c.type]}m` : '';\n}\n\nexport function bgAnsi(c: CellColor | undefined): string {\n if (!c || c.type === 'reset') return '';\n if (c.type === 'rgb') return `\\x1b[48;2;${c.r};${c.g};${c.b}m`;\n if (c.type === 'indexed') return `\\x1b[48;5;${c.index}m`;\n return NAMED_BG[c.type] ? `\\x1b[${NAMED_BG[c.type]}m` : '';\n}\n\nexport function modAnsi(mod: number | undefined): string {\n if (!mod) return '';\n let s = '';\n if (mod & 1) s += '\\x1b[1m'; // Bold\n if (mod & 2) s += '\\x1b[2m'; // Dim\n if (mod & 4) s += '\\x1b[3m'; // Italic\n if (mod & 8) s += '\\x1b[4m'; // Underline\n if (mod & 64) s += '\\x1b[7m'; // Reversed\n return s;\n}\n\n/**\n * Convert a terminui Cell to an ANSI escape string (style + symbol).\n */\nexport function cellToAnsi(cell: Cell): string {\n const fg = fgAnsi(cell.fg as CellColor);\n const bg = bgAnsi(cell.bg as CellColor);\n const mod = modAnsi(cell.modifier);\n const style = fg + bg + mod;\n if (style) {\n return style + cell.symbol + RESET;\n }\n return cell.symbol;\n}\n\n// ── Render JSX to String (static commands) ───────────────────────────\n\n/**\n * Renders a JSX node tree to a styled ANSI string using the test backend.\n * Used for print-and-exit commands (list, show, create, delete, auth).\n */\nexport function renderJsxToString(width: number, height: number, node: JsxNode): string {\n const state = createTestBackendState(width, height);\n const terminal = createTerminal(createTestBackend(state));\n terminalDrawJsx(terminal, node);\n\n // Reconstruct ANSI-styled output from the cell grid\n const lines: string[] = [];\n for (let y = 0; y < height; y++) {\n let line = '';\n let hasStyle = false;\n for (let x = 0; x < width; x++) {\n const cell = testBackendCellAt(state, x, y) as {\n symbol: string; fg?: CellColor; bg?: CellColor; modifier?: number;\n } | undefined;\n if (!cell) {\n if (hasStyle) { line += RESET; hasStyle = false; }\n line += ' ';\n continue;\n }\n const style = fgAnsi(cell.fg) + bgAnsi(cell.bg) + modAnsi(cell.modifier);\n if (style) {\n if (hasStyle) line += RESET;\n line += style + cell.symbol;\n hasStyle = true;\n } else {\n if (hasStyle) { line += RESET; hasStyle = false; }\n line += cell.symbol;\n }\n }\n if (hasStyle) line += RESET;\n lines.push(line.trimEnd());\n }\n\n // Trim trailing empty lines\n while (lines.length > 0 && stripAnsi(lines[lines.length - 1]!).trim() === '') {\n lines.pop();\n }\n\n return lines.join('\\n');\n}\n\n// ── Utilities ────────────────────────────────────────────────────────\n\n/**\n * Returns the usable terminal width, capped at 120 columns.\n */\nexport function getTermWidth(): number {\n return Math.min(process.stdout.columns || 80, 120);\n}\n\n/**\n * Strips all ANSI escape sequences from a string.\n */\nexport function stripAnsi(s: string): string {\n return s.replace(/\\x1b\\[[0-9;]*m/g, '');\n}\n","import { VStack, HStack, Text } from 'terminui/jsx';\nimport { lengthConstraint, fillConstraint } from 'terminui';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface KeyValueProps {\n readonly pairs: readonly (readonly [string, string])[];\n}\n\nexport function KeyValue({ pairs }: KeyValueProps) {\n const maxKeyLen = Math.max(...pairs.map(([k]) => k.length));\n const constraints = pairs.map(() => lengthConstraint(1));\n\n const rows = pairs.map(([key, value]) => {\n const padded = key.padStart(maxKeyLen);\n return (\n <HStack constraints={[lengthConstraint(maxKeyLen + 2), fillConstraint(1)]}>\n <Text fg={BRAND_COLORS.dim}>{padded + ' '}</Text>\n <Text bold fg={BRAND_COLORS.white}>{value}</Text>\n </HStack>\n );\n });\n\n return (\n <VStack constraints={constraints}>\n {rows}\n </VStack>\n );\n}\n","import { Command } from 'commander';\nimport http from 'node:http';\nimport { URL } from 'node:url';\nimport * as p from '@clack/prompts';\nimport { setAuthToken, clearAuthToken, getApiUrl, setRefreshToken } from '../lib/config.js';\nimport { BRAND, icon } from '../lib/theme.js';\nimport { renderJsxToString, getTermWidth } from '../lib/render.js';\nimport { KeyValue } from '../components/KeyValue.js';\n\nconst SUPABASE_URL = 'https://gflvvytymdmrbjpmymhb.supabase.co';\n\nexport const authCommand = new Command('auth').description(\n 'Manage authentication'\n);\n\nexport const loginCommand = new Command('login')\n .description('Log in via browser (Google OAuth)');\n\nloginCommand.action(async () => {\n p.intro(`${BRAND.name} -- Login`);\n\n const s = p.spinner();\n s.start('Starting local auth server...');\n\n try {\n const token = await new Promise<string>((resolve, reject) => {\n const server = http.createServer((req, res) => {\n if (!req.url) {\n res.writeHead(400);\n res.end('Bad request');\n return;\n }\n\n const url = new URL(req.url, 'http://localhost');\n\n if (url.pathname === '/callback') {\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>${BRAND.name} — Login</title>\n<style>body{font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#0a0a0a;color:#e5e5e5}\n.card{text-align:center;padding:2rem;border-radius:12px;background:#171717;border:1px solid #333}</style></head>\n<body><div class=\"card\"><p>Processing authentication...</p></div>\n<script>\nconst h=window.location.hash.substring(1);const p=new URLSearchParams(h);const t=p.get('access_token');const r=p.get('refresh_token');\nif(t){let u='/token?access_token='+encodeURIComponent(t);if(r)u+='&refresh_token='+encodeURIComponent(r);window.location.href=u}\nelse{document.querySelector('.card').innerHTML='<p style=\"color:#f87171\">Authentication failed. No token found.</p><p>You can close this tab.</p>'}\n</script></body></html>`);\n return;\n }\n\n if (url.pathname === '/token') {\n const accessToken = url.searchParams.get('access_token');\n const refreshTokenParam = url.searchParams.get('refresh_token');\n if (accessToken) {\n if (refreshTokenParam) {\n setRefreshToken(refreshTokenParam);\n }\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"><title>${BRAND.name} — Logged In</title>\n<style>body{font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;background:#0a0a0a;color:#e5e5e5}\n.card{text-align:center;padding:2rem;border-radius:12px;background:#171717;border:1px solid #333}\n.check{font-size:3rem;margin-bottom:1rem}</style></head>\n<body><div class=\"card\"><div class=\"check\">✔</div><h2>Logged in!</h2><p>Return to your terminal.</p></div></body></html>`);\n resolve(accessToken);\n server.close();\n } else {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end('<p>Missing access token.</p>');\n }\n return;\n }\n\n res.writeHead(404);\n res.end('Not found');\n });\n\n server.listen(0, () => {\n const address = server.address();\n if (!address || typeof address === 'string') {\n reject(new Error('Failed to start local server'));\n return;\n }\n\n const port = address.port;\n const redirectUrl = `http://localhost:${port}/callback`;\n const authUrl = `${SUPABASE_URL}/auth/v1/authorize?provider=google&redirect_to=${encodeURIComponent(redirectUrl)}`;\n\n s.message('Opening browser for authentication...');\n\n import('open')\n .then((m) => m.default(authUrl))\n .catch(() => {\n s.stop('Could not open browser automatically');\n p.note(authUrl, 'Open this URL manually');\n });\n });\n\n setTimeout(() => {\n server.close();\n reject(new Error('Authentication timed out after 120 seconds'));\n }, 120_000);\n });\n\n setAuthToken(token);\n s.stop(`${icon.check} Logged in successfully!`);\n\n // Show user info\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { email?: string };\n\n if (payload.email) {\n p.log.info(`Signed in as ${payload.email}`);\n }\n\n p.outro('Token stored. You can now use AudienceMeter CLI commands.');\n process.exit(0);\n } catch (error) {\n s.stop(\n `${icon.cross} Login failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n process.exit(1);\n }\n });\n\nexport const logoutCommand = new Command('logout')\n .description('Log out and clear stored credentials')\n .action(async () => {\n clearAuthToken();\n p.log.success(`${icon.check} Logged out successfully.`);\n });\n\nauthCommand\n .command('whoami')\n .description('Show current authenticated user')\n .action(async () => {\n try {\n const { getAuthToken } = await import('../lib/config.js');\n const token = getAuthToken();\n const apiUrl = getApiUrl();\n\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { email?: string; sub?: string };\n\n const output = renderJsxToString(\n getTermWidth(),\n 3,\n KeyValue({\n pairs: [\n ['Email', payload.email || 'unknown'],\n ['User ID', payload.sub || 'unknown'],\n ['API URL', apiUrl],\n ],\n })\n );\n console.log(output);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Not logged in. Run: audiencemeter login');\n } else {\n p.log.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n }\n }\n });\n\nauthCommand\n .command('token')\n .description('Print the current stored auth token')\n .action(async () => {\n try {\n const { getAuthToken } = await import('../lib/config.js');\n const token = getAuthToken();\n console.log(token);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Not logged in. Run: audiencemeter login');\n } else {\n p.log.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n }\n }\n });\n\n// Register login/logout as subcommands of auth so `auth login` / `auth logout` still works\nauthCommand.addCommand(loginCommand);\nauthCommand.addCommand(logoutCommand);\n","import { getAuthToken, getApiUrl, getRefreshToken, setAuthToken, setRefreshToken } from './config.js';\n\nconst SUPABASE_URL = 'https://gflvvytymdmrbjpmymhb.supabase.co';\nconst SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImdmbHZ2eXR5bWRtcmJqcG15bWhiIiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODAyNTIwMjIsImV4cCI6MTk5NTgyODAyMn0.1m-3IhFB-87AKk_-UIPzB0O1URgBwl78oKu8sNe8aFU';\n\nexport function isTokenExpired(token: string): boolean {\n try {\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { exp?: number };\n if (!payload.exp) return false;\n // Consider expired 60s before actual expiry to avoid edge cases\n return Date.now() >= (payload.exp - 60) * 1000;\n } catch {\n return true;\n }\n}\n\nexport async function refreshAccessToken(): Promise<string | null> {\n const refreshToken = getRefreshToken();\n if (!refreshToken) return null;\n\n try {\n const response = await fetch(`${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'apikey': SUPABASE_ANON_KEY,\n },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n\n if (!response.ok) return null;\n\n const data = await response.json() as {\n access_token?: string;\n refresh_token?: string;\n };\n\n if (data.access_token) {\n setAuthToken(data.access_token);\n if (data.refresh_token) {\n setRefreshToken(data.refresh_token);\n }\n return data.access_token;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport class ApiClient {\n constructor(\n private baseUrl: string,\n private authToken: string\n ) {}\n\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: this.authToken,\n };\n\n const options: RequestInit = { method, headers };\n\n if (body !== undefined) {\n options.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, options);\n\n if (!response.ok) {\n let errorMessage: string;\n try {\n const errorBody = (await response.json()) as Record<string, string>;\n errorMessage =\n errorBody.message || errorBody.error || response.statusText;\n } catch {\n errorMessage = response.statusText;\n }\n\n if (response.status === 401) {\n throw new Error('Not authenticated. Run: audiencemeter login');\n }\n\n throw new Error(`API error (${response.status}): ${errorMessage}`);\n }\n\n const text = await response.text();\n if (!text) return undefined as T;\n return JSON.parse(text) as T;\n }\n\n async get<T>(path: string): Promise<T> {\n return this.request<T>('GET', path);\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', path, body);\n }\n\n async patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', path, body);\n }\n\n async delete<T>(path: string): Promise<T> {\n return this.request<T>('DELETE', path);\n }\n}\n\nexport async function ensureValidToken(): Promise<string> {\n const token = getAuthToken();\n if (!isTokenExpired(token)) return token;\n\n const newToken = await refreshAccessToken();\n if (newToken) return newToken;\n\n throw new Error('Session expired. Run: audiencemeter login');\n}\n\nexport function createApiClient(): ApiClient {\n const baseUrl = getApiUrl();\n const authToken = getAuthToken();\n return new ApiClient(baseUrl, authToken);\n}\n\nexport async function createApiClientWithRefresh(): Promise<ApiClient> {\n const baseUrl = getApiUrl();\n const authToken = await ensureValidToken();\n return new ApiClient(baseUrl, authToken);\n}\n\nexport function getUserIdFromToken(): string {\n const token = getAuthToken();\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { sub: string };\n return payload.sub;\n}\n\nexport function getUserEmailFromToken(): string {\n const token = getAuthToken();\n const payload = JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n ) as { email?: string };\n return payload.email || 'unknown';\n}\n\nfunction getTokenPayload(): {\n sub: string;\n email?: string;\n user_metadata?: {\n full_name?: string;\n avatar_url?: string;\n provider_id?: string;\n };\n app_metadata?: { provider?: string };\n} {\n const token = getAuthToken();\n return JSON.parse(\n Buffer.from(token.split('.')[1]!, 'base64').toString()\n );\n}\n\nlet userEnsured = false;\n\n/**\n * Ensures a User row exists in the database for the current Supabase auth user.\n * The web app does this on every dashboard load; the CLI must do it before\n * creating sessions or other resources that have a FK to User.\n * Cached per process so repeated calls are no-ops.\n */\nexport async function ensureUserExists(client: ApiClient): Promise<void> {\n if (userEnsured) return;\n\n const payload = getTokenPayload();\n const userId = payload.sub;\n\n const resp = await client.get<{ user: unknown }>(`/users/${userId}`);\n if (resp.user) {\n userEnsured = true;\n return;\n }\n\n const meta = payload.user_metadata || {};\n await client.post('/users', {\n id: userId,\n email: payload.email || '',\n displayName: meta.full_name || payload.email || '',\n avatarUrl: meta.avatar_url || '',\n authProvider: payload.app_metadata?.provider || 'google',\n providerId: meta.provider_id || '',\n });\n userEnsured = true;\n}\n\n/**\n * Creates an API client with token refresh and ensures the user record exists.\n * Use this as the standard entry point for all authenticated commands.\n */\nexport async function initApiClient(): Promise<ApiClient> {\n const client = await createApiClientWithRefresh();\n await ensureUserExists(client);\n return client;\n}\n","export const TEMPLATES = {\n talk: {\n description: 'Conference talk or presentation (45 min)',\n durationMinutes: 45,\n ratings: [\n { label: 'Content Quality', maxValue: 5 },\n { label: 'Delivery', maxValue: 5 },\n { label: 'Clarity', maxValue: 5 },\n ],\n links: [],\n },\n workshop: {\n description: 'Hands-on workshop or lab session (120 min)',\n durationMinutes: 120,\n ratings: [\n { label: 'Content Quality', maxValue: 5 },\n { label: 'Hands-on Experience', maxValue: 5 },\n { label: 'Pace', maxValue: 5 },\n { label: 'Clarity', maxValue: 5 },\n ],\n links: [],\n },\n lightning: {\n description: 'Lightning talk (10 min)',\n durationMinutes: 10,\n ratings: [\n { label: 'Content Quality', maxValue: 5 },\n { label: 'Delivery', maxValue: 5 },\n ],\n links: [],\n },\n panel: {\n description: 'Panel discussion (60 min)',\n durationMinutes: 60,\n ratings: [\n { label: 'Topic Relevance', maxValue: 5 },\n { label: 'Discussion Quality', maxValue: 5 },\n { label: 'Moderation', maxValue: 5 },\n ],\n links: [],\n },\n} as const;\n\nexport type TemplateName = keyof typeof TEMPLATES;\n\nexport function getTemplate(name: string) {\n if (!(name in TEMPLATES)) {\n return null;\n }\n return TEMPLATES[name as TemplateName];\n}\n\nexport function getTemplateNames(): string[] {\n return Object.keys(TEMPLATES);\n}\n","import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport qrcode from 'qrcode-terminal';\nimport { initApiClient, getUserIdFromToken } from '../lib/api-client.js';\nimport {\n TEMPLATES,\n getTemplate,\n getTemplateNames,\n} from '../lib/templates.js';\nimport { BRAND, icon } from '../lib/theme.js';\nimport { renderJsxToString, getTermWidth } from '../lib/render.js';\nimport { KeyValue } from '../components/KeyValue.js';\n\ninterface Project {\n id: string;\n name: string;\n [key: string]: unknown;\n}\n\nfunction padDate(n: number): string {\n return String(n).padStart(2, '0');\n}\n\nfunction formatLocalDate(d: Date): string {\n return `${d.getFullYear()}-${padDate(d.getMonth() + 1)}-${padDate(d.getDate())}`;\n}\n\nfunction formatLocalTime(d: Date): string {\n return `${padDate(d.getHours())}:${padDate(d.getMinutes())}`;\n}\n\nexport const createCommand = new Command('create')\n .description('Create a new feedback session')\n .option('--template <type>', 'Session template')\n .option('--project <name>', 'Project name (optional)')\n .option('--name <name>', 'Session name')\n .option('--date <date>', 'Session date (YYYY-MM-DD)')\n .option('--time <time>', 'Session time (HH:MM, 24h format)')\n .option(\n '--duration <minutes>',\n 'Duration in minutes',\n parseInt\n )\n .option('--json', 'Output as JSON')\n .action(async (options) => {\n const isInteractive =\n !options.json && process.stdout.isTTY && !options.template;\n\n if (isInteractive) {\n p.intro(`${BRAND.name} -- Create Session`);\n }\n\n try {\n // -- Template selection ----------------------------------------\n let templateName: string = options.template;\n\n if (!templateName) {\n if (!isInteractive) {\n p.log.error(\n 'Missing --template. Available: ' + getTemplateNames().join(', ')\n );\n process.exit(1);\n }\n\n const selected = await p.select({\n message: 'What type of session?',\n options: Object.entries(TEMPLATES).map(([key, tmpl]) => ({\n value: key,\n label: key.charAt(0).toUpperCase() + key.slice(1),\n hint: tmpl.description,\n })),\n });\n\n if (p.isCancel(selected)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n templateName = selected as string;\n }\n\n const template = getTemplate(templateName);\n if (!template) {\n p.log.error(\n `Unknown template: \"${templateName}\". Available: ${getTemplateNames().join(', ')}`\n );\n process.exit(1);\n }\n\n // -- Session name ------------------------------------------------\n let sessionName: string = options.name;\n\n if (!sessionName && isInteractive) {\n const input = await p.text({\n message: 'Session name?',\n placeholder: 'My Talk at DevFest 2026',\n validate: (v) => {\n if (!v?.trim()) return 'Session name is required';\n },\n });\n\n if (p.isCancel(input)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n sessionName = input;\n }\n\n if (!sessionName) {\n p.log.error('Missing --name flag.');\n process.exit(1);\n }\n\n // -- Date & Time ------------------------------------------------\n const now = new Date();\n let sessionDate: string;\n\n if (options.date) {\n // Non-interactive: combine --date and optional --time\n const time = options.time || '00:00';\n sessionDate = new Date(`${options.date}T${time}`).toISOString();\n } else if (isInteractive) {\n const dateInput = await p.text({\n message: 'Session date? (YYYY-MM-DD)',\n placeholder: formatLocalDate(now),\n defaultValue: formatLocalDate(now),\n validate: (v) => {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(v || ''))\n return 'Use YYYY-MM-DD format';\n if (isNaN(new Date(v!).getTime()))\n return 'Invalid date';\n },\n });\n\n if (p.isCancel(dateInput)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n const timeInput = await p.text({\n message: 'Start time? (24h format)',\n placeholder: formatLocalTime(now),\n defaultValue: formatLocalTime(now),\n validate: (v) => {\n if (!/^\\d{1,2}:\\d{2}$/.test(v || ''))\n return 'Use HH:MM format (e.g. 14:30)';\n },\n });\n\n if (p.isCancel(timeInput)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n sessionDate = new Date(`${dateInput}T${timeInput}`).toISOString();\n } else {\n sessionDate = now.toISOString();\n }\n\n // -- Duration -----------------------------------------------\n let duration: number = options.duration || template.durationMinutes;\n\n if (!options.duration && isInteractive) {\n const input = await p.text({\n message: 'Duration (minutes)?',\n defaultValue: String(template.durationMinutes),\n placeholder: String(template.durationMinutes),\n validate: (v) => {\n const n = parseInt(v || '', 10);\n if (isNaN(n) || n < 1) return 'Enter a valid number of minutes';\n },\n });\n\n if (p.isCancel(input)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n duration = parseInt(input, 10);\n }\n\n // -- Create session -----------------------------------------\n const s = p.spinner();\n s.start('Creating session...');\n\n const client = await initApiClient();\n const userId = getUserIdFromToken();\n\n // Resolve project only if --project flag was passed\n let projectId: string | undefined;\n const projectName: string = options.project || '';\n if (projectName) {\n try {\n s.message('Resolving project...');\n const projectsResp = await client.get<Project[] | { data: Project[] }>(\n `/users/${userId}/projects`\n );\n const projects = Array.isArray(projectsResp)\n ? projectsResp\n : projectsResp?.data || [];\n\n const existing = projects.find(\n (proj: Project) =>\n proj.name.toLowerCase() === projectName.toLowerCase()\n );\n\n if (existing) {\n projectId = existing.id;\n } else {\n s.message('Creating project...');\n const newProject = await client.post<Project>(\n `/users/${userId}/projects`,\n { name: projectName }\n );\n projectId = newProject.id;\n }\n } catch {\n // Continue without project ID\n }\n }\n\n // Build session payload\n const sessionBody = {\n name: sessionName,\n date: sessionDate,\n durationMinutes: duration,\n ratings: [...template.ratings],\n links: [...template.links],\n ...(projectId && { projectId }),\n };\n\n s.message('Creating session...');\n const session = (await client.post('/sessions', sessionBody)) as Record<\n string,\n unknown\n >;\n\n s.stop(`${icon.check} Session created!`);\n\n if (options.json) {\n console.log(JSON.stringify(session, null, 2));\n } else {\n const pairs: [string, string][] = [\n ['Name', String(session.name || sessionName)],\n ['PIN', String(session.pin || '')],\n ['Template', templateName],\n ['Duration', `${duration} minutes`],\n ['Date', new Date(sessionDate).toLocaleString()],\n ];\n if (session.id) {\n pairs.push(['ID', String(session.id)]);\n }\n\n console.log();\n const output = renderJsxToString(\n getTermWidth(),\n pairs.length,\n KeyValue({ pairs })\n );\n console.log(output);\n\n if (session.pin) {\n const joinUrl = `https://app.audiencemeter.pro/s/${session.pin}`;\n console.log();\n p.note(joinUrl, 'Join URL');\n\n // Print QR code in terminal\n console.log();\n await new Promise<void>((resolve) => {\n qrcode.generate(joinUrl, { small: true }, (code: string) => {\n console.log(code);\n resolve();\n });\n });\n }\n\n p.outro('Share the PIN, URL, or QR code with your audience!');\n }\n } catch (error) {\n p.log.error(\n `Failed to create session: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Run: audiencemeter login');\n }\n process.exit(1);\n }\n });\n","import { getPrivacyMode, setPrivacyMode } from './config.js';\n\nexport { getPrivacyMode } from './config.js';\n\n/**\n * Mask a PIN string when privacy mode is on.\n * Returns '*'.repeat(pin.length) when hidden, or the raw PIN when visible.\n */\nexport function maskPin(pin: string): string {\n if (!pin || pin === '--') return pin;\n return getPrivacyMode() ? '*'.repeat(pin.length) : pin;\n}\n\n/**\n * Toggle privacy mode and persist it. Returns the new value (true = private).\n */\nexport function togglePrivacyMode(): boolean {\n const newValue = !getPrivacyMode();\n setPrivacyMode(newValue);\n return newValue;\n}\n","import type { Backend, Cell } from 'terminui';\nimport { cellToAnsi } from './render.js';\n\nconst CSI = '\\x1b[';\n\n/**\n * Creates a Node.js stdout Backend implementing terminui's Backend interface.\n * Used for persistent full-screen TUI modes (dashboard, interactive list).\n *\n * The draw() method converts terminui Cell objects to ANSI escape sequences\n * and writes them to stdout with cursor positioning.\n */\nexport function createNodeBackend(): Backend {\n const stdout = process.stdout;\n\n return {\n size: () => ({\n width: stdout.columns || 80,\n height: stdout.rows || 24,\n }),\n\n draw: (content) => {\n let out = '';\n for (const { x, y, cell } of content) {\n // Move cursor to position (1-indexed)\n out += `${CSI}${y + 1};${x + 1}H`;\n out += cellToAnsi(cell);\n }\n stdout.write(out);\n },\n\n flush: () => {\n // stdout.write is already unbuffered for TTY -- no-op\n },\n\n hideCursor: () => {\n stdout.write(`${CSI}?25l`);\n },\n\n showCursor: () => {\n stdout.write(`${CSI}?25h`);\n },\n\n getCursorPosition: () => ({ x: 0, y: 0 }),\n\n setCursorPosition: (pos) => {\n stdout.write(`${CSI}${pos.y + 1};${pos.x + 1}H`);\n },\n\n clear: () => {\n stdout.write(`${CSI}2J${CSI}H`);\n },\n };\n}\n","import readline from 'node:readline';\n\n// ── Types ────────────────────────────────────────────────────────────\n\nexport interface KeypressInfo {\n name: string;\n ctrl: boolean;\n meta: boolean;\n shift: boolean;\n sequence: string;\n}\n\n// ── Alternate Screen Constants ───────────────────────────────────────\n\nconst ENTER_ALT_SCREEN = '\\x1b[?1049h';\nconst EXIT_ALT_SCREEN = '\\x1b[?1049l';\nconst HIDE_CURSOR = '\\x1b[?25l';\nconst SHOW_CURSOR = '\\x1b[?25h';\n\n// Module-level flag to track whether full screen is active.\n// Cleanup handlers only run exitFullScreen when this is true,\n// avoiding interference with normal non-fullscreen commands.\nlet isFullScreen = false;\n\n// ── Keyboard Input ───────────────────────────────────────────────────\n\n/**\n * Sets up keyboard input in raw mode and attaches a keypress handler.\n * Returns a cleanup function that removes the listener, disables raw mode,\n * and pauses stdin.\n */\nexport function setupKeyboardInput(handler: (key: KeypressInfo) => void): () => void {\n readline.emitKeypressEvents(process.stdin);\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n }\n process.stdin.resume();\n\n const listener = (_str: string, key: { name: string; ctrl: boolean; meta: boolean; shift: boolean; sequence: string }) => {\n if (key) {\n handler({\n name: key.name ?? '',\n ctrl: key.ctrl ?? false,\n meta: key.meta ?? false,\n shift: key.shift ?? false,\n sequence: key.sequence ?? '',\n });\n }\n };\n\n process.stdin.on('keypress', listener);\n\n // Return cleanup function\n return () => {\n process.stdin.removeListener('keypress', listener);\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n process.stdin.pause();\n };\n}\n\n// ── Full Screen Management ───────────────────────────────────────────\n\n/**\n * Enters alternate screen buffer and hides the cursor.\n * Registers cleanup handlers for safe terminal restoration.\n */\nexport function enterFullScreen(): void {\n if (isFullScreen) return;\n isFullScreen = true;\n process.stdout.write(ENTER_ALT_SCREEN);\n process.stdout.write(HIDE_CURSOR);\n}\n\n/**\n * Shows the cursor and exits alternate screen buffer.\n * Safe to call multiple times (idempotent via flag check).\n */\nexport function exitFullScreen(): void {\n if (!isFullScreen) return;\n isFullScreen = false;\n process.stdout.write(SHOW_CURSOR);\n process.stdout.write(EXIT_ALT_SCREEN);\n}\n\n// ── Process Cleanup Handlers ─────────────────────────────────────────\n// These ensure the terminal is restored if the process exits while\n// full screen is active. The flag check prevents interference with\n// normal non-fullscreen commands.\n\nprocess.on('exit', () => {\n if (isFullScreen) {\n // Synchronous writes needed in 'exit' handler\n try { process.stdout.write(SHOW_CURSOR); } catch { /* ignore */ }\n try { process.stdout.write(EXIT_ALT_SCREEN); } catch { /* ignore */ }\n isFullScreen = false;\n }\n});\n\nprocess.on('SIGINT', () => {\n if (isFullScreen) {\n exitFullScreen();\n }\n process.exit(0);\n});\n\nprocess.on('SIGTERM', () => {\n if (isFullScreen) {\n exitFullScreen();\n }\n process.exit(0);\n});\n\nprocess.on('uncaughtException', (err) => {\n if (isFullScreen) {\n exitFullScreen();\n }\n console.error(err);\n process.exit(1);\n});\n","import { Text } from 'terminui/jsx';\nimport { BRAND_COLORS, BRAND } from '../lib/theme.js';\n\nexport interface HeaderProps {\n readonly version?: string;\n}\n\nexport function Header({ version }: HeaderProps = {}) {\n const ver = version || BRAND.version;\n return (\n <Text bold fg={BRAND_COLORS.purple} align=\"center\">\n {`${BRAND.name} v${ver}`}\n </Text>\n );\n}\n","import { Tabs } from 'terminui/jsx';\nimport { createStyle, Modifier } from 'terminui';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface TabBarProps {\n readonly titles: readonly string[];\n readonly selected: number;\n}\n\nexport function TabBar({ titles, selected }: TabBarProps) {\n return (\n <Tabs\n titles={titles}\n selected={selected}\n border\n fg={BRAND_COLORS.dimCyan}\n highlightStyle={createStyle({ fg: BRAND_COLORS.purple, addModifier: Modifier.BOLD })}\n />\n );\n}\n","import { Text } from 'terminui/jsx';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface Shortcut {\n readonly key: string;\n readonly label: string;\n readonly active?: boolean;\n}\n\nexport interface ShortcutBarProps {\n readonly shortcuts: readonly Shortcut[];\n}\n\nexport function ShortcutBar({ shortcuts }: ShortcutBarProps) {\n const text = shortcuts.map((s) => {\n if (s.active) return `${s.key}: [${s.label}]`;\n return `${s.key}: ${s.label}`;\n }).join(' | ');\n return (\n <Text fg={BRAND_COLORS.dimCyan}>{` ${text}`}</Text>\n );\n}\n","import { VStack, HStack, Box, Text, List, Gauge } from 'terminui/jsx';\nimport { fillConstraint, lengthConstraint, createListState, createStyle, Modifier } from 'terminui';\nimport type { ListState } from 'terminui';\nimport { BRAND_COLORS, icon } from '../lib/theme.js';\nimport { Header } from './Header.js';\nimport { TabBar } from './TabBar.js';\nimport { ShortcutBar } from './ShortcutBar.js';\nimport { SessionTable, type SessionRow } from './SessionTable.js';\nimport { maskPin } from '../lib/pin-mask.js';\n\nexport interface DashboardProps {\n readonly selectedTab: number;\n readonly sessions: readonly SessionRow[];\n readonly stats: {\n readonly totalSessions: number;\n readonly totalParticipants: number;\n readonly avgEngagement: number;\n };\n readonly selectedIndex: number;\n readonly authEmail: string | null;\n readonly authExpired?: boolean;\n readonly loading?: boolean;\n readonly error?: string | null;\n readonly privacyMode?: boolean;\n}\n\nconst TAB_TITLES = ['Dashboard', 'Sessions', 'Create', 'Auth'] as const;\n\nfunction getShortcuts(privacyMode: boolean) {\n return [\n { key: '1-4', label: 'tab' },\n { key: 'j/k', label: 'navigate' },\n { key: 'Enter', label: 'select' },\n { key: 'p', label: privacyMode ? 'privacy on' : 'privacy', active: privacyMode },\n { key: 'q', label: 'quit' },\n ];\n}\n\nfunction DashboardTab({ sessions, stats, selectedIndex, loading }: {\n sessions: readonly SessionRow[];\n stats: DashboardProps['stats'];\n selectedIndex: number;\n loading?: boolean;\n}) {\n const recent = sessions.slice(0, 5);\n const listItems = recent.length > 0\n ? recent.map((s) => `${icon.session} ${s.name} ${maskPin(s.pin)} ${s.status}`)\n : [loading ? 'Loading sessions...' : 'No sessions yet. Press Tab to go to Create.'];\n\n const listState: ListState = createListState();\n listState.selected = recent.length > 0 ? selectedIndex : undefined;\n\n return (\n <HStack constraints={[fillConstraint(2), fillConstraint(1)]}>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Recent Sessions \">\n <List\n items={listItems}\n state={listState}\n fg={BRAND_COLORS.cyan}\n highlightStyle={createStyle({ fg: BRAND_COLORS.purple, addModifier: Modifier.BOLD })}\n />\n </Box>\n <VStack constraints={[lengthConstraint(3), lengthConstraint(3), fillConstraint(1)]}>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Sessions \">\n <Text bold fg={BRAND_COLORS.white} align=\"center\">{String(stats.totalSessions)}</Text>\n </Box>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Participants \">\n <Text bold fg={BRAND_COLORS.white} align=\"center\">{String(stats.totalParticipants)}</Text>\n </Box>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Engagement \">\n <Gauge\n percent={stats.avgEngagement}\n fg={BRAND_COLORS.green}\n />\n </Box>\n </VStack>\n </HStack>\n );\n}\n\nfunction SessionsTab({ sessions, selectedIndex, loading }: {\n sessions: readonly SessionRow[];\n selectedIndex: number;\n loading?: boolean;\n}) {\n if (sessions.length === 0) {\n return (\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan}>\n <Text fg={BRAND_COLORS.dim}>{loading ? 'Loading sessions...' : 'No sessions found. Create one with the Create tab.'}</Text>\n </Box>\n );\n }\n\n const listItems = sessions.map((s) =>\n `${icon.session} ${s.name.padEnd(30).slice(0, 30)} ${maskPin(s.pin)} ${s.status.padEnd(10).slice(0, 10)} ${s.date}`\n );\n\n const listState: ListState = createListState();\n listState.selected = selectedIndex;\n\n return (\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title={` Sessions (${sessions.length}) `}>\n <List\n items={listItems}\n state={listState}\n fg={BRAND_COLORS.cyan}\n highlightStyle={createStyle({ fg: BRAND_COLORS.purple, addModifier: Modifier.BOLD })}\n />\n </Box>\n );\n}\n\nfunction CreateTab() {\n return (\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Create Session \">\n <Text fg={BRAND_COLORS.white}>{` Press Enter to create a new session`}</Text>\n </Box>\n );\n}\n\nfunction AuthTab({ authEmail, authExpired }: { authEmail: string | null; authExpired?: boolean }) {\n if (authExpired) {\n return (\n <Box border borderType=\"rounded\" title=\" Authentication \">\n <Text fg={BRAND_COLORS.yellow}>{` ${icon.cross} Session expired. Press Enter to re-login.`}</Text>\n </Box>\n );\n }\n\n return (\n <Box border borderType=\"rounded\" title=\" Authentication \">\n <Text fg={authEmail ? BRAND_COLORS.green : BRAND_COLORS.yellow}>\n {authEmail\n ? ` ${icon.check} Logged in as ${authEmail}`\n : ` ${icon.cross} Not logged in. Press Enter to login.`}\n </Text>\n </Box>\n );\n}\n\nexport function Dashboard({ selectedTab, sessions, stats, selectedIndex, authEmail, authExpired, loading, error, privacyMode = false }: DashboardProps) {\n let content;\n switch (selectedTab) {\n case 0:\n content = <DashboardTab sessions={sessions} stats={stats} selectedIndex={selectedIndex} loading={loading} />;\n break;\n case 1:\n content = <SessionsTab sessions={sessions} selectedIndex={selectedIndex} loading={loading} />;\n break;\n case 2:\n content = <CreateTab />;\n break;\n case 3:\n content = <AuthTab authEmail={authEmail} authExpired={authExpired} />;\n break;\n default:\n content = <DashboardTab sessions={sessions} stats={stats} selectedIndex={selectedIndex} />;\n }\n\n if (error) {\n return (\n <VStack constraints={[lengthConstraint(1), lengthConstraint(3), lengthConstraint(1), fillConstraint(1), lengthConstraint(1)]}>\n <Header />\n <TabBar titles={[...TAB_TITLES]} selected={selectedTab} />\n <Text fg={BRAND_COLORS.yellow} align=\"center\">{`${icon.cross} ${error}`}</Text>\n {content}\n <ShortcutBar shortcuts={getShortcuts(privacyMode)} />\n </VStack>\n );\n }\n\n return (\n <VStack constraints={[lengthConstraint(1), lengthConstraint(3), fillConstraint(1), lengthConstraint(1)]}>\n <Header />\n <TabBar titles={[...TAB_TITLES]} selected={selectedTab} />\n {content}\n <ShortcutBar shortcuts={getShortcuts(privacyMode)} />\n </VStack>\n );\n}\n","import { VStack, Gauge, LineGauge, Text, Box } from 'terminui/jsx';\nimport { lengthConstraint } from 'terminui';\nimport type { Color } from 'terminui';\nimport type { JsxNode } from 'terminui/jsx-runtime';\nimport { BRAND_COLORS } from '../lib/theme.js';\n\nexport interface MetricsPanelProps {\n readonly engagementRate?: number;\n readonly feedbackRate?: number;\n readonly participants?: number;\n}\n\nfunction gaugeColor(ratio: number): Color {\n if (ratio >= 70) return BRAND_COLORS.green;\n if (ratio >= 40) return BRAND_COLORS.cyan;\n return BRAND_COLORS.yellow;\n}\n\nexport function MetricsPanel({ engagementRate, feedbackRate, participants }: MetricsPanelProps) {\n const items: JsxNode[] = [];\n const constraints: ReturnType<typeof lengthConstraint>[] = [];\n\n if (participants !== undefined) {\n items.push(\n <Text bold fg={BRAND_COLORS.white}>{` ${String(participants)} participants`}</Text>\n );\n constraints.push(lengthConstraint(1));\n }\n\n if (engagementRate !== undefined && engagementRate > 0) {\n items.push(\n <Gauge\n percent={engagementRate}\n border\n title={` Engagement ${engagementRate}% `}\n fg={gaugeColor(engagementRate)}\n />\n );\n constraints.push(lengthConstraint(3));\n }\n\n if (feedbackRate !== undefined && feedbackRate > 0) {\n items.push(\n <LineGauge\n percent={feedbackRate}\n border\n title={` Feedback ${feedbackRate}% `}\n fg={gaugeColor(feedbackRate)}\n />\n );\n constraints.push(lengthConstraint(3));\n }\n\n if (items.length === 0) {\n return <Text fg={BRAND_COLORS.dim}>No metrics available</Text>;\n }\n\n return (\n <VStack constraints={constraints}>\n {items}\n </VStack>\n );\n}\n","import { VStack, HStack, Box, Text, Sparkline, BarChart } from 'terminui/jsx';\nimport { fillConstraint, lengthConstraint, createBarGroup, createBar } from 'terminui';\nimport type { BarGroup, Constraint } from 'terminui';\nimport type { JsxNode } from 'terminui/jsx-runtime';\nimport { BRAND_COLORS, icon } from '../lib/theme.js';\nimport { KeyValue } from './KeyValue.js';\nimport { MetricsPanel } from './MetricsPanel.js';\nimport { maskPin } from '../lib/pin-mask.js';\n\nexport interface SessionDetailSession {\n readonly id?: string;\n readonly name: string;\n readonly pin: string;\n readonly date?: string;\n readonly durationMinutes?: number;\n readonly createdAt?: string;\n readonly questions?: readonly { id: string; type: string; label: string }[];\n readonly links?: readonly { id: string; label: string; url: string }[];\n}\n\nexport interface SessionDetailMetrics {\n readonly totalParticipants?: number;\n readonly engagementRate?: number;\n readonly feedbackCompletionRate?: number;\n}\n\nexport interface TimelineBucket {\n readonly minute: number;\n readonly positive: number;\n readonly negative: number;\n readonly pace: number;\n}\n\nexport interface SessionDetailProps {\n readonly session: SessionDetailSession;\n readonly metrics: SessionDetailMetrics | null;\n readonly timeline: readonly TimelineBucket[];\n readonly analysis: Record<string, unknown> | null;\n readonly status: string;\n}\n\nfunction statusBadge(status: string): string {\n switch (status) {\n case 'live': return `${icon.live} live`;\n case 'ended': return `${icon.ended} ended`;\n case 'upcoming': return `${icon.upcoming} upcoming`;\n default: return status;\n }\n}\n\nexport function SessionDetail({ session, metrics, timeline, analysis, status }: SessionDetailProps) {\n // Build content sections as an array, then compose at the end\n const sections: JsxNode[] = [];\n const sectionConstraints: Constraint[] = [];\n\n // -- Header: session name + status --\n sections.push(\n <Text bold fg={BRAND_COLORS.purple}>\n {` ${session.name || 'Untitled Session'} ${statusBadge(status)}`}\n </Text>\n );\n sectionConstraints.push(lengthConstraint(1));\n\n // -- Info + Metrics side-by-side --\n const dateStr = session.date\n ? new Date(session.date).toLocaleString()\n : session.createdAt\n ? new Date(session.createdAt).toLocaleString()\n : '--';\n const durationStr = session.durationMinutes\n ? `${session.durationMinutes} minutes`\n : '--';\n\n const infoPairs: [string, string][] = [\n ['PIN', maskPin(session.pin || '--')],\n ['Date', dateStr],\n ['Duration', durationStr],\n ];\n if (session.id) {\n infoPairs.push(['ID', session.id]);\n }\n\n const hasMetrics = metrics && (\n (metrics.engagementRate !== undefined && metrics.engagementRate > 0) ||\n (metrics.feedbackCompletionRate !== undefined && metrics.feedbackCompletionRate > 0) ||\n (metrics.totalParticipants !== undefined)\n );\n\n if (hasMetrics) {\n const metricsHeight = 1\n + (metrics!.totalParticipants !== undefined ? 1 : 0)\n + (metrics!.engagementRate !== undefined && metrics!.engagementRate! > 0 ? 3 : 0)\n + (metrics!.feedbackCompletionRate !== undefined && metrics!.feedbackCompletionRate! > 0 ? 3 : 0);\n const infoHeight = infoPairs.length + 2; // +2 for border\n const panelHeight = Math.max(infoHeight, metricsHeight + 2);\n\n sections.push(\n <HStack constraints={[fillConstraint(1), fillConstraint(1)]}>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Session Info \">\n <KeyValue pairs={infoPairs} />\n </Box>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Metrics \">\n <MetricsPanel\n engagementRate={metrics!.engagementRate}\n feedbackRate={metrics!.feedbackCompletionRate}\n participants={metrics!.totalParticipants}\n />\n </Box>\n </HStack>\n );\n sectionConstraints.push(lengthConstraint(panelHeight));\n } else {\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Session Info \">\n <KeyValue pairs={infoPairs} />\n </Box>\n );\n sectionConstraints.push(lengthConstraint(infoPairs.length + 2));\n }\n\n // -- Sparkline: reaction timeline --\n const positiveData = timeline.map((b) => b.positive || 0);\n if (positiveData.some((v) => v > 0)) {\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Reactions Over Time \">\n <Sparkline\n data={positiveData}\n fg={BRAND_COLORS.green}\n />\n </Box>\n );\n sectionConstraints.push(lengthConstraint(5));\n }\n\n // -- BarChart: reaction summary --\n const totalPositive = timeline.reduce((sum, b) => sum + (b.positive || 0), 0);\n const totalNegative = timeline.reduce((sum, b) => sum + (b.negative || 0), 0);\n if (totalPositive > 0 || totalNegative > 0) {\n const barData: BarGroup[] = [\n createBarGroup([createBar(totalPositive)], 'Positive'),\n createBarGroup([createBar(totalNegative)], 'Negative'),\n ];\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Reaction Summary \">\n <BarChart\n data={barData}\n fg={BRAND_COLORS.cyan}\n />\n </Box>\n );\n const maxVal = Math.max(totalPositive, totalNegative, 1);\n sectionConstraints.push(lengthConstraint(Math.max(8, maxVal + 4)));\n }\n\n // -- AI Analysis --\n if (analysis && typeof analysis === 'object') {\n const summary = (analysis.summary as string) || (analysis.feedbackSummary as string) || '';\n if (summary) {\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.purple} title={` ${icon.sparkle} AI Analysis `}>\n <Text fg={BRAND_COLORS.white}>{summary}</Text>\n </Box>\n );\n const summaryLines = Math.ceil(summary.length / 70) + 2;\n sectionConstraints.push(lengthConstraint(summaryLines));\n }\n\n const insights = analysis.coachingInsights as string[] | undefined;\n if (insights && insights.length > 0) {\n const insightLines = insights.slice(0, 3).map((insight) =>\n ` ${icon.arrow} ${insight}`\n ).join('\\n');\n\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Coaching Insights \">\n <Text fg={BRAND_COLORS.white}>{insightLines}</Text>\n </Box>\n );\n sectionConstraints.push(lengthConstraint(Math.min(insights.length, 3) + 2));\n }\n }\n\n // -- Questions --\n if (session.questions && session.questions.length > 0) {\n const questionLines = session.questions.map((q) =>\n ` ${icon.dot} [${q.type}] ${q.label}`\n ).join('\\n');\n\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Rating Criteria \">\n <Text fg={BRAND_COLORS.white}>{questionLines}</Text>\n </Box>\n );\n sectionConstraints.push(lengthConstraint(session.questions.length + 2));\n }\n\n // -- Links --\n if (session.links && session.links.length > 0) {\n const linkLines = session.links.map((link) =>\n ` ${icon.dot} ${link.label}: ${link.url}`\n ).join('\\n');\n\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Links \">\n <Text fg={BRAND_COLORS.white}>{linkLines}</Text>\n </Box>\n );\n sectionConstraints.push(lengthConstraint(session.links.length + 2));\n }\n\n // -- Join URL --\n if (session.pin) {\n sections.push(\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan} title=\" Join URL \">\n <Text bold fg={BRAND_COLORS.cyan}>{`https://app.audiencemeter.pro/s/${maskPin(session.pin)}`}</Text>\n </Box>\n );\n sectionConstraints.push(lengthConstraint(3));\n }\n\n return (\n <VStack constraints={sectionConstraints}>\n {sections}\n </VStack>\n );\n}\n","import { createTerminal, terminalResize, createListState } from 'terminui';\nimport type { Terminal, ListState } from 'terminui';\nimport { VStack, Box, List, Text, terminalDrawJsx } from 'terminui/jsx';\nimport type { JsxNode } from 'terminui/jsx-runtime';\nimport { createNodeBackend } from './node-backend.js';\nimport { setupKeyboardInput, enterFullScreen, exitFullScreen, type KeypressInfo } from './input.js';\nimport { createApiClient, initApiClient, getUserIdFromToken, getUserEmailFromToken, isTokenExpired } from './api-client.js';\nimport { Dashboard } from '../components/Dashboard.js';\nimport {\n SessionDetail,\n type SessionDetailSession,\n type SessionDetailMetrics,\n type TimelineBucket,\n} from '../components/SessionDetail.js';\nimport { ShortcutBar } from '../components/ShortcutBar.js';\nimport type { SessionRow } from '../components/SessionTable.js';\nimport { BRAND_COLORS, icon } from './theme.js';\nimport { maskPin, togglePrivacyMode, getPrivacyMode } from './pin-mask.js';\nimport { fillConstraint, lengthConstraint, createStyle, Modifier } from 'terminui';\n\n// ── Types ────────────────────────────────────────────────────────────\n\ninterface SessionSummary {\n id?: string;\n name: string;\n pin: string;\n date?: string | Date;\n durationMinutes?: number;\n createdAt?: string | Date;\n [key: string]: unknown;\n}\n\ninterface SessionsResponse {\n data: SessionSummary[];\n total: number;\n page: number;\n take: number;\n}\n\ninterface Metrics {\n totalParticipants?: number;\n engagementRate?: number;\n feedbackCompletionRate?: number;\n [key: string]: unknown;\n}\n\ninterface AppState {\n selectedTab: number;\n sessions: SessionRow[];\n rawSessions: SessionSummary[];\n total: number;\n stats: {\n totalSessions: number;\n totalParticipants: number;\n avgEngagement: number;\n };\n selectedIndex: number;\n authEmail: string | null;\n authExpired: boolean;\n showingSession: string | null;\n running: boolean;\n loading: boolean;\n error: string | null;\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nfunction getSessionStatus(session: SessionSummary): string {\n const sessionDate = session.date\n ? new Date(session.date as string)\n : session.createdAt\n ? new Date(session.createdAt as string)\n : null;\n\n if (!sessionDate) return 'unknown';\n\n const now = new Date();\n const durationMs = (session.durationMinutes || 60) * 60 * 1000;\n const endTime = new Date(sessionDate.getTime() + durationMs);\n\n if (now < sessionDate) return 'upcoming';\n if (now >= sessionDate && now <= endTime) return 'live';\n return 'ended';\n}\n\nfunction statusDisplay(status: string): string {\n switch (status) {\n case 'live': return `${icon.live} live`;\n case 'ended': return `${icon.ended} ended`;\n case 'upcoming': return `${icon.upcoming} soon`;\n default: return status;\n }\n}\n\nfunction toSessionRows(sessions: SessionSummary[]): SessionRow[] {\n return sessions.map((session) => {\n const date = session.date\n ? new Date(session.date as string).toLocaleDateString()\n : session.createdAt\n ? new Date(session.createdAt as string).toLocaleDateString()\n : '--';\n\n const duration = session.durationMinutes\n ? `${session.durationMinutes}min`\n : '--';\n\n const status = getSessionStatus(session);\n\n const raw = session as Record<string, unknown>;\n const count = (raw._count as Record<string, unknown> | undefined)?.attendees\n ?? raw.participantCount\n ?? '--';\n\n return {\n name: session.name || '--',\n pin: session.pin || '--',\n status: statusDisplay(status),\n date,\n duration,\n participants: String(count),\n };\n });\n}\n\n// ── Render helpers ───────────────────────────────────────────────────\n\nfunction renderDashboard(terminal: Terminal, state: AppState): void {\n terminalDrawJsx(terminal, (\n <Dashboard\n selectedTab={state.selectedTab}\n sessions={state.sessions}\n stats={state.stats}\n selectedIndex={state.selectedIndex}\n authEmail={state.authEmail}\n authExpired={state.authExpired}\n loading={state.loading}\n error={state.error}\n privacyMode={getPrivacyMode()}\n />\n ));\n}\n\nfunction renderSessionDetail(\n terminal: Terminal,\n session: SessionDetailSession,\n metrics: SessionDetailMetrics | null,\n timeline: readonly TimelineBucket[],\n analysis: Record<string, unknown> | null,\n status: string,\n): void {\n const privacyOn = getPrivacyMode();\n const detailShortcuts = [\n { key: 'q', label: 'back' },\n { key: 'p', label: privacyOn ? 'privacy on' : 'privacy', active: privacyOn },\n ];\n\n terminalDrawJsx(terminal, (\n <VStack constraints={[fillConstraint(1), lengthConstraint(1)]}>\n <SessionDetail\n session={session}\n metrics={metrics}\n timeline={timeline}\n analysis={analysis}\n status={status}\n />\n <ShortcutBar shortcuts={detailShortcuts} />\n </VStack>\n ));\n}\n\nfunction renderInteractiveList(\n terminal: Terminal,\n sessionRows: readonly SessionRow[],\n total: number,\n selectedIndex: number,\n): void {\n const listItems = sessionRows.map((s) =>\n `${icon.session} ${s.name.padEnd(30).slice(0, 30)} ${maskPin(s.pin)} ${s.status.padEnd(10).slice(0, 10)} ${s.date}`\n );\n\n const listState: ListState = createListState();\n listState.selected = selectedIndex;\n\n terminalDrawJsx(terminal, (\n <VStack constraints={[lengthConstraint(1), fillConstraint(1), lengthConstraint(1)]}>\n <Text bold fg={BRAND_COLORS.purple} align=\"center\">{`Sessions (${sessionRows.length} of ${total})`}</Text>\n <Box border borderType=\"rounded\" fg={BRAND_COLORS.dimCyan}>\n <List\n items={listItems}\n state={listState}\n fg={BRAND_COLORS.cyan}\n highlightStyle={createStyle({ fg: BRAND_COLORS.purple, addModifier: Modifier.BOLD })}\n />\n </Box>\n <ShortcutBar shortcuts={[\n { key: 'j/k', label: 'navigate' },\n { key: 'Enter', label: 'view' },\n { key: 'p', label: getPrivacyMode() ? 'privacy on' : 'privacy', active: getPrivacyMode() },\n { key: 'q', label: 'quit' },\n ]} />\n </VStack>\n ));\n}\n\n// ── Session Detail Fetching ──────────────────────────────────────────\n\nasync function fetchSessionDetail(sessionId: string): Promise<{\n session: SessionDetailSession;\n metrics: SessionDetailMetrics | null;\n timeline: TimelineBucket[];\n analysis: Record<string, unknown> | null;\n status: string;\n}> {\n const client = createApiClient();\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);\n const path = !isUuid\n ? `/sessions/by-pin/${sessionId}`\n : `/sessions/${sessionId}`;\n\n const session = await client.get<SessionSummary>(path);\n\n let metrics: Metrics | null = null;\n let timeline: TimelineBucket[] = [];\n let analysis: Record<string, unknown> | null = null;\n\n if (session.id) {\n const results = await Promise.allSettled([\n client.get<Metrics>(`/sessions/${session.id}/metrics`),\n client.get<TimelineBucket[]>(`/sessions/${session.id}/sentiment-timeline`),\n client.get<Record<string, unknown>>(`/sessions/${session.id}/analysis`),\n ]);\n\n if (results[0]!.status === 'fulfilled') metrics = results[0]!.value;\n if (results[1]!.status === 'fulfilled') timeline = results[1]!.value || [];\n if (results[2]!.status === 'fulfilled') analysis = results[2]!.value;\n }\n\n const status = getSessionStatus(session);\n\n return {\n session: {\n id: session.id,\n name: session.name,\n pin: session.pin,\n date: session.date ? String(session.date) : undefined,\n durationMinutes: session.durationMinutes,\n createdAt: session.createdAt ? String(session.createdAt) : undefined,\n questions: (session as Record<string, unknown>).questions as SessionDetailSession['questions'],\n links: (session as Record<string, unknown>).links as SessionDetailSession['links'],\n },\n metrics: metrics ? {\n totalParticipants: metrics.totalParticipants,\n engagementRate: metrics.engagementRate,\n feedbackCompletionRate: metrics.feedbackCompletionRate,\n } : null,\n timeline,\n analysis,\n status,\n };\n}\n\n// ── Dashboard ────────────────────────────────────────────────────────\n\nexport async function startDashboard(): Promise<void> {\n // Check authentication\n let userId: string;\n try {\n userId = getUserIdFromToken();\n } catch {\n console.error('Not authenticated. Run: audiencemeter login');\n process.exit(1);\n return; // TypeScript needs this after process.exit\n }\n\n let authEmail: string | null = null;\n try {\n authEmail = getUserEmailFromToken();\n } catch {\n authEmail = null;\n }\n\n // Enter full screen\n enterFullScreen();\n\n const backend = createNodeBackend();\n const terminal = createTerminal(backend);\n\n // Check if token is expired before showing status\n let authExpired = false;\n try {\n const token = (await import('./config.js')).getAuthToken();\n authExpired = isTokenExpired(token);\n } catch {\n // no token at all\n }\n\n // Initialize state\n const state: AppState = {\n selectedTab: 0,\n sessions: [],\n rawSessions: [],\n total: 0,\n stats: { totalSessions: 0, totalParticipants: 0, avgEngagement: 0 },\n selectedIndex: 0,\n authEmail: authExpired ? null : authEmail,\n authExpired,\n showingSession: null,\n running: true,\n loading: true,\n error: null,\n };\n\n // Initial render with empty state (non-blocking)\n renderDashboard(terminal, state);\n\n // Session detail state\n let detailData: Awaited<ReturnType<typeof fetchSessionDetail>> | null = null;\n\n // Re-render helper\n const rerender = () => {\n if (state.showingSession && detailData) {\n renderSessionDetail(\n terminal,\n detailData.session,\n detailData.metrics,\n detailData.timeline,\n detailData.analysis,\n detailData.status,\n );\n } else {\n renderDashboard(terminal, state);\n }\n };\n\n // Setup keyboard input\n const cleanupInput = setupKeyboardInput(async (key: KeypressInfo) => {\n if (!state.running) return;\n\n // Ctrl+C always exits\n if (key.ctrl && key.name === 'c') {\n state.running = false;\n cleanupInput();\n exitFullScreen();\n process.exit(0);\n return;\n }\n\n // Showing session detail\n if (state.showingSession) {\n if (key.name === 'p') {\n togglePrivacyMode();\n rerender();\n return;\n }\n if (key.name === 'escape' || key.name === 'q') {\n state.showingSession = null;\n detailData = null;\n rerender();\n }\n return;\n }\n\n // Tab switching\n if (key.name === 'tab') {\n state.selectedTab = (state.selectedTab + 1) % 4;\n state.selectedIndex = 0;\n rerender();\n return;\n }\n\n // Number keys for tab selection\n if (['1', '2', '3', '4'].includes(key.name)) {\n state.selectedTab = parseInt(key.name, 10) - 1;\n state.selectedIndex = 0;\n rerender();\n return;\n }\n\n // Navigation (j/k/up/down)\n const itemCount = state.selectedTab === 0\n ? Math.min(state.sessions.length, 5)\n : state.sessions.length;\n\n if (itemCount > 0) {\n if (key.name === 'j' || key.name === 'down') {\n state.selectedIndex = (state.selectedIndex + 1) % itemCount;\n rerender();\n return;\n }\n\n if (key.name === 'k' || key.name === 'up') {\n state.selectedIndex = (state.selectedIndex - 1 + itemCount) % itemCount;\n rerender();\n return;\n }\n }\n\n // Enter key -- action depends on tab\n if (key.name === 'return') {\n if (state.selectedTab === 0 || state.selectedTab === 1) {\n // Dashboard or Sessions tab -- drill into session detail\n const rawSession = state.rawSessions[state.selectedIndex];\n if (rawSession) {\n const sessionId = rawSession.id || rawSession.pin;\n if (sessionId) {\n state.showingSession = sessionId;\n try {\n detailData = await fetchSessionDetail(sessionId);\n } catch {\n detailData = {\n session: {\n name: rawSession.name,\n pin: rawSession.pin,\n date: rawSession.date ? String(rawSession.date) : undefined,\n durationMinutes: rawSession.durationMinutes,\n createdAt: rawSession.createdAt ? String(rawSession.createdAt) : undefined,\n },\n metrics: null,\n timeline: [],\n analysis: null,\n status: getSessionStatus(rawSession),\n };\n }\n rerender();\n }\n }\n return;\n }\n\n if (state.selectedTab === 2) {\n // Create tab -- exit full screen, run create flow, then exit\n state.running = false;\n cleanupInput();\n exitFullScreen();\n\n try {\n const { createCommand } = await import('../commands/create.js');\n await createCommand.parseAsync(['node', 'audiencemeter'], { from: 'node' });\n } catch {\n // Command may throw on cancel\n }\n return;\n }\n\n if (state.selectedTab === 3) {\n // Auth tab -- exit full screen, run auth flow, then exit\n state.running = false;\n cleanupInput();\n exitFullScreen();\n\n try {\n const { authCommand } = await import('../commands/auth.js');\n const action = (state.authEmail && !state.authExpired) ? 'whoami' : 'login';\n await authCommand.parseAsync(['node', 'audiencemeter', action], { from: 'node' });\n } catch {\n // Command may throw on cancel\n }\n return;\n }\n }\n\n // Toggle PIN visibility\n if (key.name === 'p') {\n togglePrivacyMode();\n state.sessions = toSessionRows(state.rawSessions);\n rerender();\n return;\n }\n\n // Quit\n if (key.name === 'q' || key.name === 'escape') {\n state.running = false;\n cleanupInput();\n exitFullScreen();\n return;\n }\n });\n\n // Listen for terminal resize\n const onResize = () => {\n terminalResize(terminal, backend.size());\n rerender();\n };\n process.stdout.on('resize', onResize);\n\n // Fetch sessions asynchronously (non-blocking, with token auto-refresh)\n (async () => {\n try {\n const client = await initApiClient();\n\n // If token was refreshed, update auth display\n if (state.authExpired) {\n state.authExpired = false;\n state.authEmail = getUserEmailFromToken();\n state.error = null;\n }\n\n const queryParams = new URLSearchParams();\n queryParams.set('take', '50');\n queryParams.set('sort', 'createdAt');\n queryParams.set('order', 'desc');\n\n const response = await client.get<SessionsResponse | SessionSummary[]>(\n `/users/${userId}/sessions?${queryParams.toString()}`\n );\n\n const sessions: SessionSummary[] = Array.isArray(response)\n ? response\n : response?.data || [];\n const total = Array.isArray(response)\n ? sessions.length\n : response?.total || sessions.length;\n\n state.rawSessions = sessions;\n state.sessions = toSessionRows(sessions);\n state.total = total;\n\n // Calculate stats\n let totalParticipants = 0;\n for (const s of sessions) {\n const raw = s as Record<string, unknown>;\n const count = (raw._count as Record<string, unknown> | undefined)?.attendees\n ?? raw.participantCount\n ?? 0;\n totalParticipants += Number(count) || 0;\n }\n\n state.stats = {\n totalSessions: total,\n totalParticipants,\n avgEngagement: 0,\n };\n\n state.loading = false;\n rerender();\n } catch (err) {\n state.loading = false;\n const msg = err instanceof Error ? err.message : 'Failed to load sessions';\n if (msg.includes('expired') || msg.includes('Not authenticated')) {\n state.authExpired = true;\n state.authEmail = null;\n state.error = 'Session expired. Go to Auth tab and press Enter to re-login.';\n } else {\n state.error = msg;\n }\n rerender();\n }\n })();\n\n // Wait until the user quits\n return new Promise<void>((resolve) => {\n const check = setInterval(() => {\n if (!state.running) {\n clearInterval(check);\n process.stdout.removeListener('resize', onResize);\n resolve();\n }\n }, 100);\n });\n}\n\n// ── Interactive List ─────────────────────────────────────────────────\n\nexport async function startInteractiveList(\n sessionRows: SessionRow[],\n rawSessions: SessionSummary[],\n total: number,\n): Promise<void> {\n enterFullScreen();\n\n const backend = createNodeBackend();\n const terminal = createTerminal(backend);\n\n let selectedIndex = 0;\n let running = true;\n let showingDetail = false;\n let detailData: Awaited<ReturnType<typeof fetchSessionDetail>> | null = null;\n\n const rerender = () => {\n if (showingDetail && detailData) {\n renderSessionDetail(\n terminal,\n detailData.session,\n detailData.metrics,\n detailData.timeline,\n detailData.analysis,\n detailData.status,\n );\n } else {\n renderInteractiveList(terminal, sessionRows, total, selectedIndex);\n }\n };\n\n rerender();\n\n const cleanupInput = setupKeyboardInput(async (key: KeypressInfo) => {\n if (!running) return;\n\n if (key.ctrl && key.name === 'c') {\n running = false;\n cleanupInput();\n exitFullScreen();\n process.exit(0);\n return;\n }\n\n // Showing detail\n if (showingDetail) {\n if (key.name === 'p') {\n togglePrivacyMode();\n rerender();\n return;\n }\n if (key.name === 'escape' || key.name === 'q') {\n showingDetail = false;\n detailData = null;\n rerender();\n }\n return;\n }\n\n // Navigation\n if (sessionRows.length > 0) {\n if (key.name === 'j' || key.name === 'down') {\n selectedIndex = (selectedIndex + 1) % sessionRows.length;\n rerender();\n return;\n }\n\n if (key.name === 'k' || key.name === 'up') {\n selectedIndex = (selectedIndex - 1 + sessionRows.length) % sessionRows.length;\n rerender();\n return;\n }\n }\n\n // Enter -- drill into session detail\n if (key.name === 'return' && sessionRows.length > 0) {\n const rawSession = rawSessions[selectedIndex];\n if (rawSession) {\n const sessionId = rawSession.id || rawSession.pin;\n if (sessionId) {\n showingDetail = true;\n try {\n detailData = await fetchSessionDetail(sessionId);\n } catch {\n detailData = {\n session: {\n name: rawSession.name,\n pin: rawSession.pin,\n date: rawSession.date ? String(rawSession.date) : undefined,\n durationMinutes: rawSession.durationMinutes,\n createdAt: rawSession.createdAt ? String(rawSession.createdAt) : undefined,\n },\n metrics: null,\n timeline: [],\n analysis: null,\n status: getSessionStatus(rawSession),\n };\n }\n rerender();\n }\n }\n return;\n }\n\n // Toggle PIN visibility\n if (key.name === 'p') {\n togglePrivacyMode();\n rerender();\n return;\n }\n\n // Quit\n if (key.name === 'q' || key.name === 'escape') {\n running = false;\n cleanupInput();\n exitFullScreen();\n return;\n }\n });\n\n // Listen for terminal resize\n const onResize = () => {\n terminalResize(terminal, backend.size());\n rerender();\n };\n process.stdout.on('resize', onResize);\n\n return new Promise<void>((resolve) => {\n const check = setInterval(() => {\n if (!running) {\n clearInterval(check);\n process.stdout.removeListener('resize', onResize);\n resolve();\n }\n }, 100);\n });\n}\n","import { Command } from 'commander';\nimport { authCommand, loginCommand, logoutCommand } from './commands/auth.js';\nimport { createCommand } from './commands/create.js';\nimport { listCommand } from './commands/list.js';\nimport { showCommand } from './commands/show.js';\nimport { deleteCommand } from './commands/delete.js';\nimport { BRAND } from './lib/theme.js';\nimport { getPrivacyMode, setPrivacyMode } from './lib/config.js';\n\nconst program = new Command();\n\nprogram\n .name('audiencemeter')\n .description(\n `${BRAND.name} CLI — ${BRAND.tagline}`\n )\n .version(BRAND.version, '-v, --version');\n\n// Register commands\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(authCommand);\nprogram.addCommand(createCommand);\nprogram.addCommand(listCommand);\nprogram.addCommand(showCommand);\nprogram.addCommand(deleteCommand);\n\n// Config command for CLI preferences\nconst configCommand = new Command('config')\n .description('Manage CLI preferences');\n\nconfigCommand\n .command('privacy [value]')\n .description('Get or set privacy mode (true/false). When on, PINs are hidden.')\n .action((value?: string) => {\n if (value === undefined) {\n console.log(`privacy: ${getPrivacyMode()}`);\n } else {\n const boolValue = value === 'true' || value === '1';\n setPrivacyMode(boolValue);\n console.log(`privacy mode ${boolValue ? 'enabled' : 'disabled'}`);\n }\n });\n\nprogram.addCommand(configCommand);\n\n// Detect if args were passed\nconst hasArgs = process.argv.length > 2;\n\nif (hasArgs) {\n // Subcommands: parse with Commander (static print-and-exit)\n program.parse(process.argv);\n} else if (process.stdout.isTTY) {\n // No args + TTY: launch persistent full-screen TUI dashboard\n import('./lib/app.js')\n .then(({ startDashboard }) => startDashboard())\n .catch((err) => {\n // Ensure terminal is restored on error\n import('./lib/input.js').then(({ exitFullScreen }) => {\n exitFullScreen();\n }).catch(() => { /* ignore */ });\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n });\n} else {\n // No args + non-TTY (piped): show help text\n program.help();\n}\n","import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport { initApiClient, getUserIdFromToken } from '../lib/api-client.js';\nimport { icon } from '../lib/theme.js';\nimport { renderJsxToString, getTermWidth } from '../lib/render.js';\nimport { SessionTable, type SessionRow } from '../components/SessionTable.js';\n\ninterface SessionSummary {\n id?: string;\n name: string;\n pin: string;\n date?: string | Date;\n durationMinutes?: number;\n createdAt?: string | Date;\n [key: string]: unknown;\n}\n\ninterface SessionsResponse {\n data: SessionSummary[];\n total: number;\n page: number;\n take: number;\n}\n\nfunction getSessionStatus(session: SessionSummary): string {\n const sessionDate = session.date\n ? new Date(session.date)\n : session.createdAt\n ? new Date(session.createdAt)\n : null;\n\n if (!sessionDate) return 'unknown';\n\n const now = new Date();\n const durationMs = (session.durationMinutes || 60) * 60 * 1000;\n const endTime = new Date(sessionDate.getTime() + durationMs);\n\n if (now < sessionDate) return 'upcoming';\n if (now >= sessionDate && now <= endTime) return 'live';\n return 'ended';\n}\n\nexport const listCommand = new Command('list')\n .alias('ls')\n .description('List your feedback sessions')\n .option('--project <name>', 'Filter by project name')\n .option('--limit <n>', 'Max sessions to display', parseInt, 20)\n .option('--json', 'Output as JSON')\n .action(async (options) => {\n const s = p.spinner();\n s.start('Fetching sessions...');\n\n try {\n const client = await initApiClient();\n const userId = getUserIdFromToken();\n\n const queryParams = new URLSearchParams();\n queryParams.set('take', String(options.limit));\n queryParams.set('sort', 'createdAt');\n queryParams.set('order', 'desc');\n\n const response = await client.get<SessionsResponse | SessionSummary[]>(\n `/users/${userId}/sessions?${queryParams.toString()}`\n );\n\n s.stop();\n\n const sessions: SessionSummary[] = Array.isArray(response)\n ? response\n : response?.data || [];\n const total = Array.isArray(response)\n ? sessions.length\n : response?.total || sessions.length;\n\n if (sessions.length === 0) {\n p.log.warn('No sessions found.');\n p.log.info(\n 'Create one: audiencemeter create --template talk --project \"My Talk\"'\n );\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(sessions, null, 2));\n return;\n }\n\n // Build rich table\n const statusDisplay = (status: string) => {\n switch (status) {\n case 'live':\n return `${icon.live} live`;\n case 'ended':\n return `${icon.ended} ended`;\n case 'upcoming':\n return `${icon.upcoming} soon`;\n default:\n return status;\n }\n };\n\n const rows: SessionRow[] = sessions.map((session) => {\n const date = session.date\n ? new Date(session.date).toLocaleDateString()\n : session.createdAt\n ? new Date(session.createdAt).toLocaleDateString()\n : '--';\n\n const duration = session.durationMinutes\n ? `${session.durationMinutes}min`\n : '--';\n\n const status = getSessionStatus(session);\n\n const raw = session as Record<string, unknown>;\n const count =\n (raw._count as Record<string, unknown> | undefined)?.attendees ??\n raw.participantCount ??\n '--';\n const participants = String(count);\n\n return {\n name: session.name || '--',\n pin: session.pin || '--',\n status: statusDisplay(status),\n date,\n duration,\n participants,\n };\n });\n\n // TTY mode: enter interactive full-screen list with keyboard navigation\n if (process.stdout.isTTY && !options.json) {\n const { startInteractiveList } = await import('../lib/app.js');\n await startInteractiveList(rows, sessions, total);\n return;\n }\n\n // Non-TTY / static output: render table and print\n const tableHeight = rows.length + 4; // header + border + rows\n const output = renderJsxToString(\n getTermWidth(),\n tableHeight,\n SessionTable({ sessions: rows, total })\n );\n console.log(output);\n } catch (error) {\n s.stop(\n `${icon.cross} Failed to list sessions: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Run: audiencemeter login');\n }\n process.exit(1);\n }\n });\n","import { Table, Box } from 'terminui/jsx';\nimport { fillConstraint, lengthConstraint } from 'terminui';\nimport { BRAND_COLORS } from '../lib/theme.js';\nimport { maskPin } from '../lib/pin-mask.js';\n\nexport interface SessionRow {\n readonly name: string;\n readonly pin: string;\n readonly status: string;\n readonly date: string;\n readonly duration: string;\n readonly participants: string;\n}\n\nexport interface SessionTableProps {\n readonly sessions: readonly SessionRow[];\n readonly total: number;\n}\n\nexport function SessionTable({ sessions, total }: SessionTableProps) {\n const widths = [\n fillConstraint(1), // Name -- takes remaining space\n lengthConstraint(7), // PIN\n lengthConstraint(10), // Status\n lengthConstraint(12), // Date\n lengthConstraint(10), // Duration\n lengthConstraint(6), // Ppl\n ];\n\n const header = ['Name', 'PIN', 'Status', 'Date', 'Duration', 'Ppl'];\n\n const rows = sessions.map((s) => [\n s.name,\n maskPin(s.pin),\n s.status,\n s.date,\n s.duration,\n s.participants,\n ]);\n\n return (\n <Box\n border\n borderType=\"rounded\"\n fg={BRAND_COLORS.dimCyan}\n title={` Sessions (${sessions.length} of ${total}) `}\n >\n <Table\n widths={widths}\n header={header}\n rows={rows}\n fg={BRAND_COLORS.cyan}\n columnSpacing={1}\n />\n </Box>\n );\n}\n","import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport { initApiClient, getUserIdFromToken } from '../lib/api-client.js';\nimport { icon } from '../lib/theme.js';\nimport { renderJsxToString, getTermWidth } from '../lib/render.js';\nimport {\n SessionDetail,\n type SessionDetailSession,\n type SessionDetailMetrics,\n type TimelineBucket,\n} from '../components/SessionDetail.js';\n\ninterface Session {\n id?: string;\n userId?: string;\n name: string;\n pin: string;\n date?: string;\n durationMinutes?: number;\n createdAt?: string;\n questions?: Array<{ id: string; type: string; label: string }>;\n links?: Array<{ id: string; label: string; url: string }>;\n [key: string]: unknown;\n}\n\ninterface Metrics {\n totalParticipants?: number;\n engagementRate?: number;\n feedbackCompletionRate?: number;\n [key: string]: unknown;\n}\n\nfunction getSessionStatus(session: Session): string {\n const sessionDate = session.date\n ? new Date(session.date)\n : session.createdAt\n ? new Date(session.createdAt)\n : null;\n\n if (!sessionDate) return 'unknown';\n\n const now = new Date();\n const durationMs = (session.durationMinutes || 60) * 60 * 1000;\n const endTime = new Date(sessionDate.getTime() + durationMs);\n\n if (now < sessionDate) return 'upcoming';\n if (now >= sessionDate && now <= endTime) return 'live';\n return 'ended';\n}\n\nexport const showCommand = new Command('show')\n .description('Show session details with rich metrics')\n .argument('<session-id>', 'Session ID (UUID) or PIN (5 characters)')\n .option('--json', 'Output as JSON')\n .action(async (sessionId: string, options) => {\n const s = p.spinner();\n s.start('Fetching session...');\n\n try {\n const client = await initApiClient();\n\n // Determine if input looks like a UUID or a PIN\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(sessionId);\n const path = !isUuid\n ? `/sessions/by-pin/${sessionId}`\n : `/sessions/${sessionId}`;\n\n const session = await client.get<Session>(path);\n\n // Fetch metrics, timeline, and AI analysis in parallel (owner only)\n let metrics: Metrics | null = null;\n let timeline: TimelineBucket[] = [];\n let analysis: Record<string, unknown> | null = null;\n\n let isOwner = false;\n try {\n isOwner = !!session.userId && session.userId === getUserIdFromToken();\n } catch {\n // Not authenticated — skip owner-only data\n }\n\n if (session.id && isOwner) {\n s.message('Fetching metrics...');\n\n const results = await Promise.allSettled([\n client.get<Metrics>(`/sessions/${session.id}/metrics`),\n client.get<TimelineBucket[]>(\n `/sessions/${session.id}/sentiment-timeline`\n ),\n client.get<Record<string, unknown>>(\n `/sessions/${session.id}/analysis`\n ),\n ]);\n\n if (results[0]!.status === 'fulfilled') metrics = results[0]!.value;\n if (results[1]!.status === 'fulfilled')\n timeline = results[1]!.value || [];\n if (results[2]!.status === 'fulfilled') analysis = results[2]!.value;\n }\n\n s.stop();\n\n // -- JSON output ------------------------------------------------\n if (options.json) {\n const output = {\n ...session,\n ...(metrics && { metrics }),\n ...(timeline.length && { timeline }),\n ...(analysis && { analysis }),\n };\n console.log(JSON.stringify(output, null, 2));\n return;\n }\n\n // -- Rich JSX output ------------------------------------------------\n const status = getSessionStatus(session);\n\n // Calculate height based on content\n let height = 3; // header + spacing\n const infoPairCount = 3 + (session.id ? 1 : 0);\n const hasMetrics = metrics && (\n (metrics.engagementRate !== undefined && metrics.engagementRate > 0) ||\n (metrics.feedbackCompletionRate !== undefined && metrics.feedbackCompletionRate > 0) ||\n (metrics.totalParticipants !== undefined)\n );\n\n if (hasMetrics) {\n const metricsH = 1\n + (metrics!.totalParticipants !== undefined ? 1 : 0)\n + (metrics!.engagementRate !== undefined && metrics!.engagementRate! > 0 ? 3 : 0)\n + (metrics!.feedbackCompletionRate !== undefined && metrics!.feedbackCompletionRate! > 0 ? 3 : 0);\n height += Math.max(infoPairCount + 2, metricsH + 2);\n } else {\n height += infoPairCount + 2;\n }\n\n // Timeline sparkline\n const positiveData = timeline.map((b) => b.positive || 0);\n if (positiveData.some((v) => v > 0)) {\n height += 5;\n }\n\n // Bar chart\n const totalPositive = timeline.reduce((sum, b) => sum + (b.positive || 0), 0);\n const totalNegative = timeline.reduce((sum, b) => sum + (b.negative || 0), 0);\n if (totalPositive > 0 || totalNegative > 0) {\n const maxVal = Math.max(totalPositive, totalNegative, 1);\n height += Math.max(8, maxVal + 4);\n }\n\n // AI Analysis\n if (analysis && typeof analysis === 'object') {\n const summary = (analysis.summary as string) || (analysis.feedbackSummary as string) || '';\n if (summary) {\n height += Math.ceil(summary.length / 70) + 2;\n }\n const insights = analysis.coachingInsights as string[] | undefined;\n if (insights && insights.length > 0) {\n height += Math.min(insights.length, 3) + 2;\n }\n }\n\n // Questions\n if (session.questions && session.questions.length > 0) {\n height += session.questions.length + 2;\n }\n\n // Links\n if (session.links && session.links.length > 0) {\n height += session.links.length + 2;\n }\n\n // Join URL\n if (session.pin) {\n height += 3;\n }\n\n const sessionData: SessionDetailSession = {\n id: session.id,\n name: session.name,\n pin: session.pin,\n date: session.date,\n durationMinutes: session.durationMinutes,\n createdAt: session.createdAt,\n questions: session.questions,\n links: session.links,\n };\n\n const metricsData: SessionDetailMetrics | null = metrics\n ? {\n totalParticipants: metrics.totalParticipants,\n engagementRate: metrics.engagementRate,\n feedbackCompletionRate: metrics.feedbackCompletionRate,\n }\n : null;\n\n console.log();\n const output = renderJsxToString(\n getTermWidth(),\n height,\n SessionDetail({\n session: sessionData,\n metrics: metricsData,\n timeline,\n analysis,\n status,\n })\n );\n console.log(output);\n } catch (error) {\n s.stop(\n `${icon.cross} Failed to fetch session: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Run: audiencemeter login');\n }\n process.exit(1);\n }\n });\n","import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport { initApiClient } from '../lib/api-client.js';\nimport { icon } from '../lib/theme.js';\n\nexport const deleteCommand = new Command('delete')\n .alias('rm')\n .description('Delete a session')\n .argument('<session-id>', 'Session ID (UUID)')\n .option('--force', 'Skip confirmation prompt')\n .action(async (sessionId: string, options) => {\n try {\n const client = await initApiClient();\n\n // Fetch session details to show name in confirmation\n let sessionName = sessionId;\n try {\n const session = await client.get<Record<string, unknown>>(\n `/sessions/${sessionId}`\n );\n if (session.name) {\n sessionName = session.name as string;\n }\n } catch {\n // If we can't fetch, proceed with the ID\n }\n\n // Confirm deletion unless --force\n if (!options.force) {\n const confirmed = await p.confirm({\n message: `Delete session \"${sessionName}\"? This cannot be undone.`,\n });\n\n if (p.isCancel(confirmed) || !confirmed) {\n p.log.info('Cancelled.');\n return;\n }\n }\n\n const s = p.spinner();\n s.start('Deleting session...');\n\n await client.delete(`/sessions/${sessionId}`);\n\n s.stop(`${icon.check} Session \"${sessionName}\" deleted.`);\n } catch (error) {\n p.log.error(\n `Failed to delete session: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n if (\n error instanceof Error &&\n error.message.includes('Not authenticated')\n ) {\n p.log.warn('Run: audiencemeter login');\n }\n process.exit(1);\n }\n });\n"],"mappings":"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","names":["getAuthToken","SUPABASE_URL","path","text","Command","p","Text","jsx","jsx","Text","jsx","text","VStack","HStack","Box","Text","fillConstraint","lengthConstraint","createStyle","Modifier","jsx","jsxs","VStack","Gauge","Text","lengthConstraint","jsx","VStack","HStack","Box","Text","fillConstraint","lengthConstraint","jsx","jsxs","createTerminal","createListState","VStack","Box","List","Text","terminalDrawJsx","fillConstraint","lengthConstraint","createStyle","Modifier","jsx","jsxs","path","createCommand","authCommand","Command","Command","p","fillConstraint","lengthConstraint","jsx","getSessionStatus","Command","statusDisplay","startInteractiveList","Command","p","getSessionStatus","path","output","Command","p","Command","startDashboard","exitFullScreen"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "audiencemeter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "AudienceMeter CLI - Beautiful terminal UI for managing speaker feedback sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"images/"
|
|
34
34
|
],
|
|
35
35
|
"scripts": {
|
|
36
|
-
"build": "npx tsup"
|
|
36
|
+
"build": "npx tsup",
|
|
37
|
+
"test": "vitest run"
|
|
37
38
|
},
|
|
38
39
|
"publishConfig": {
|
|
39
40
|
"access": "public"
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/node": "^22.0.0",
|
|
51
52
|
"tsup": "^8.5.1",
|
|
52
|
-
"typescript": "5.7.3"
|
|
53
|
+
"typescript": "5.7.3",
|
|
54
|
+
"vitest": "^4.0.18"
|
|
53
55
|
}
|
|
54
56
|
}
|