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 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.0"
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
- var SUPABASE_URL2, SUPABASE_ANON_KEY, ApiClient;
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, path, body) {
536
- const url = `${this.baseUrl}${path}`;
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(path) {
564
- return this.request("GET", path);
629
+ async get(path2) {
630
+ return this.request("GET", path2);
565
631
  }
566
- async post(path, body) {
567
- return this.request("POST", path, body);
632
+ async post(path2, body) {
633
+ return this.request("POST", path2, body);
568
634
  }
569
- async patch(path, body) {
570
- return this.request("PATCH", path, body);
635
+ async patch(path2, body) {
636
+ return this.request("PATCH", path2, body);
571
637
  }
572
- async delete(path) {
573
- return this.request("DELETE", path);
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 = createApiClient();
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) => `${s.key}: ${s.label}`).join(" | ");
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: 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: SHORTCUTS })
1235
+ /* @__PURE__ */ jsx6(ShortcutBar, { shortcuts: getShortcuts(privacyMode) })
1139
1236
  ] });
1140
1237
  }
1141
- var TAB_TITLES, SHORTCUTS;
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: "Esc/q", label: "back" }
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(Text7, { fg: BRAND_COLORS.dimCyan, children: " j/k: navigate | Enter: view | q: quit" })
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 path = !isUuid ? `/sessions/by-pin/${sessionId}` : `/sessions/${sessionId}`;
1468
- const session = await client.get(path);
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 createApiClientWithRefresh();
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 = createApiClient();
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 = createApiClient();
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 path = !isUuid ? `/sessions/by-pin/${sessionId}` : `/sessions/${sessionId}`;
2011
- const session = await client.get(path);
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 = createApiClient();
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\">&#x2714;</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\">&#x2714;</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.1.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
  }