@lightupai/polaris 0.0.46 → 0.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightupai/polaris",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "polaris": "bin/polaris",
package/src/web/app.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  getSessionPromptCounts,
16
16
  getProjectEvents,
17
17
  getRecentSignups,
18
+ listUsers,
18
19
  type Sql,
19
20
  } from "../service/db";
20
21
  import { layout, nav } from "./layout";
@@ -250,7 +251,8 @@ export function createApp(sql: Sql) {
250
251
  }
251
252
  } catch { /* _system project may not exist yet */ }
252
253
 
253
- // Query real projects, sessions, and prompt counts
254
+ // Query team members, projects, sessions, and prompt counts
255
+ const teamMembers = await listUsers(sql, payload.org_id);
254
256
  const projects = (await listProjects(sql, payload.org_id)).filter((p) => p.name !== "_system");
255
257
  const allSessions = (await listSessions(sql, payload.org_id)).filter((s) => s.project !== "_system");
256
258
  const promptCounts = await getSessionPromptCounts(sql, payload.org_id);
@@ -288,6 +290,7 @@ export function createApp(sql: Sql) {
288
290
  cliInstalled,
289
291
  hasConnectedSession,
290
292
  totalPrompts: Array.from(promptCounts.values()).reduce((a, b) => a + b, 0),
293
+ teamMembers: teamMembers.map((u) => ({ name: u.name, email: u.email })),
291
294
  };
292
295
 
293
296
  if (hasConnectedSession) {
@@ -328,10 +331,11 @@ export function createApp(sql: Sql) {
328
331
  const mockToken = "preview-token";
329
332
  const base = { token: mockToken, userName: mockUser.name, orgName: mockOrg.name, orgSlug: "lightup-data" as string | null, email: mockUser.email };
330
333
 
334
+ const mockTeam = [{ name: mockUser.name, email: mockUser.email }, { name: "Alice Chen", email: "alice@lightup.ai" }, { name: "Laura Mowry", email: "laura@lightup.ai" }];
331
335
  const fresh = { ...base, orgSlug: null, slackConnected: false, cliInstalled: false, hasConnectedSession: false, totalPrompts: 0 };
332
- const slackDone = { ...base, slackConnected: true, cliInstalled: false, hasConnectedSession: false, totalPrompts: 0 };
333
- const cliDone = { ...base, slackConnected: true, cliInstalled: true, hasConnectedSession: false, totalPrompts: 0 };
334
- const allDone = { ...base, slackConnected: true, cliInstalled: true, hasConnectedSession: true, totalPrompts: 127 };
336
+ const slackDone = { ...base, slackConnected: true, cliInstalled: false, hasConnectedSession: false, totalPrompts: 0, teamMembers: mockTeam };
337
+ const cliDone = { ...base, slackConnected: true, cliInstalled: true, hasConnectedSession: false, totalPrompts: 0, teamMembers: mockTeam };
338
+ const allDone = { ...base, slackConnected: true, cliInstalled: true, hasConnectedSession: true, totalPrompts: 127, teamMembers: mockTeam };
335
339
 
336
340
  return layout(`
337
341
  <div class="max-w-5xl mx-auto px-6 py-12">
package/src/web/pages.ts CHANGED
@@ -48,31 +48,19 @@ export function renderLandingPage(): string {
48
48
  <span class="text-white text-[10px] font-bold mt-0.5">Polaris</span>
49
49
  </div>
50
50
 
51
- <!-- Branching arrows: Polaris ↔ sessions -->
52
- <!-- Branching arrows: desktop -->
53
- <svg class="hidden md:block w-80 h-16 text-gray-300" viewBox="0 0 320 64" fill="none" stroke="currentColor" stroke-width="1.5" overflow="visible">
54
- <path d="M160 0 L160 20" stroke-linecap="round"/>
55
- <path d="M157 20 L160 0 L163 20" stroke-linecap="round" stroke-linejoin="round"/>
56
- <path d="M160 20 L48 58" stroke-linecap="round"/>
57
- <path d="M52 46 L48 58 L60 54" stroke-linecap="round" stroke-linejoin="round"/>
58
- <path d="M160 20 L160 104" stroke-linecap="round"/>
59
- <path d="M157 101 L160 104 L163 101" stroke-linecap="round" stroke-linejoin="round"/>
60
- <path d="M160 20 L272 58" stroke-linecap="round"/>
61
- <path d="M260 54 L272 58 L268 46" stroke-linecap="round" stroke-linejoin="round"/>
62
- </svg>
63
- <!-- Branching arrow: mobile (simple vertical) -->
64
- <svg class="md:hidden w-5 h-8 text-gray-300" viewBox="0 0 20 32" fill="none" stroke="currentColor" stroke-width="1.5">
51
+ <!-- Arrow: Polaris ↔ sessions -->
52
+ <svg class="w-5 h-8 text-gray-300" viewBox="0 0 20 32" fill="none" stroke="currentColor" stroke-width="1.5">
65
53
  <path d="M7 4 L7 28 M4 7 L7 4 L10 7" stroke-linecap="round" stroke-linejoin="round"/>
66
54
  <path d="M13 28 L13 4 M10 25 L13 28 L16 25" stroke-linecap="round" stroke-linejoin="round"/>
67
55
  </svg>
68
56
 
69
57
  <!-- Session nodes -->
70
- <div class="flex flex-wrap justify-center items-start gap-4 md:gap-6">
58
+ <div class="flex flex-wrap justify-center gap-4">
71
59
  <div class="flex items-center gap-2 px-4 py-2.5 bg-gray-900 rounded-lg shadow-sm">
72
60
  <img class="w-5 h-5" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAKACAMAAAA7EzkRAAAAFVBMVEVMaXHZd1fZd1babUjZd1faf1rZd1epRaWRAAAABnRSTlMAXawH8g5t5RLrAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFOklEQVR42u3WUQ6EIAxAQcDV+x95r1Bjk2Kdid81wkMdAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADebtHa9gFedPYTIAIUoAAFiAAFKEABIkABClCACFCAAhQgAkSAAkSACFCACBABChABIkABIkAEKEAEiAAFiAARoAARIAIUIAJEgAJEgAhQgAgQAQoQASJAAQpQgAhQgAIUIAIUoAAFiAAFKEABIkABClCACBABChABIkABIkAEKEAEiAAFiAARoAARIAIUIAJEgAJEgAhQgAgQAQoQASJAASJABChABIgABShAASJAAQpQgAhQgAIUIAIUoAAFiAARoAARIAIUIAJEgAJEgAhQgAjwnjNmfq2EGVwYAT4UvO33AqzZDwEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoACTHTHfCzC4MNkBHsnGBYUEiAARIAgQAYIAESAIEAGCABEgCBABggARIAgQAYIAESAIEAGCABEgCBABggARIAgQAYIAESAIEAEiQBAgAgQBIkAQIAIEASJAECACBAEiQBAgAgQBIkAQIAIEASJAECACBAEiQBAgAgQBIkAQIAIEASJABAgCRIAgQAQIAkSAIEAECAJEgCBABAgCRIDwNMCVzJL2lt3LyGaLmr+xdmeLBChABIgABYgAEaAAESACFCACRIACRIAIUIAIEAEKEAEiQAEiQAQoQASIAAWIABGgABEgAhQgAkSAAhSgABGgAAUoQAQoQAEKEAEKUIACRIAIUIAIEAEKEAEiQAEiQAQoQASIAAWIABGgABEgAhQgAkSAAkSACFCACBABChABIkABIkAEKEABChABClCAAkSAAhSgABGgAAUoQAQoQAEKEAEiQAEiQAQoQASIAAWIABGgABEgAhQgAkSAAkSACDDJjFnRBy6aVyb6HKto3mhiJp+4W/OOXa8bX5CZ/EVqU9YbAuzwCyNAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKEABClCAAhSgAAUoQAEKcEvnDCqaV3cyg86ieQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwJv8Af3P8SOrUE9bAAAAAElFTkSuQmCC"/>
73
61
  <span class="text-xs text-gray-300">Alice working on auth</span>
74
62
  </div>
75
- <div class="flex items-center gap-2 px-4 py-2.5 md:mt-10 bg-white border border-gray-200 rounded-lg shadow-sm">
63
+ <div class="flex items-center gap-2 px-4 py-2.5 bg-white border border-gray-200 rounded-lg shadow-sm">
76
64
  <svg class="w-4 h-4 text-blue-500" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C8 4.4 4.4 8 0 8c4.4 0 8 3.6 8 8 0-4.4 3.6-8 8-8-4.4 0-8-3.6-8-8z"/></svg>
77
65
  <span class="text-xs text-gray-700">Martha writing docs</span>
78
66
  </div>
package/src/web/views.ts CHANGED
@@ -4,6 +4,11 @@
4
4
  import { nav, slackIcon, type NavOpts } from "./layout";
5
5
  import type { SessionFixture, ProjectFixture, DeviceFixture } from "./fixtures";
6
6
 
7
+ interface TeamMember {
8
+ name: string;
9
+ email: string;
10
+ }
11
+
7
12
  interface ViewContext {
8
13
  token: string;
9
14
  userName: string;
@@ -14,6 +19,7 @@ interface ViewContext {
14
19
  cliInstalled: boolean;
15
20
  hasConnectedSession: boolean;
16
21
  totalPrompts: number;
22
+ teamMembers?: TeamMember[];
17
23
  }
18
24
 
19
25
  function navOpts(ctx: ViewContext): NavOpts {
@@ -48,6 +54,24 @@ function statusBadge(label: string, done: boolean): string {
48
54
  : `<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-500">${label}</span>`;
49
55
  }
50
56
 
57
+ // --- Team members ---
58
+
59
+ function renderTeamMembers(members: TeamMember[], currentEmail: string): string {
60
+ if (members.length === 0) return "";
61
+ return `
62
+ <div class="mt-3 bg-white border border-gray-200 rounded-lg divide-y divide-gray-100">
63
+ ${members.map((m) => {
64
+ const isYou = m.email === currentEmail;
65
+ return `
66
+ <div class="px-4 py-2.5 flex items-center gap-3">
67
+ <div class="w-7 h-7 rounded-full bg-polaris-600 flex items-center justify-center text-white text-xs font-bold shrink-0">${m.name.charAt(0).toUpperCase()}</div>
68
+ <p class="text-sm text-gray-900">${m.name}${isYou ? ' <span class="text-xs text-gray-400">(you)</span>' : ""}</p>
69
+ <span class="text-xs text-gray-400 ml-auto">${m.email}</span>
70
+ </div>`;
71
+ }).join("")}
72
+ </div>`;
73
+ }
74
+
51
75
  // --- Floor section ---
52
76
 
53
77
  type StepState = "done" | "active" | "future";
@@ -67,11 +91,13 @@ function renderFloorSection(ctx: ViewContext, compact = false, state: StepState
67
91
  const promptStat = ctx.totalPrompts > 0
68
92
  ? `<span class="text-xs text-gray-400 ml-auto">${ctx.totalPrompts} prompt${ctx.totalPrompts !== 1 ? "s" : ""}</span>`
69
93
  : '';
94
+ const teamCount = ctx.teamMembers?.length ?? 0;
70
95
  return `
71
96
  <div>
72
97
  <div class="flex items-baseline gap-2 mb-3">
73
98
  <h2 class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Floor</h2>
74
99
  ${statusBadge("Connected", true)}
100
+ ${teamCount > 0 ? `<span class="text-xs text-gray-400">${teamCount} member${teamCount !== 1 ? "s" : ""}</span>` : ""}
75
101
  </div>
76
102
  <div class="bg-white border border-gray-200 rounded-lg px-5 py-3 flex items-center gap-3">
77
103
  <div class="w-8 h-8 rounded-lg bg-[#4A154B] flex items-center justify-center shrink-0">
@@ -81,6 +107,7 @@ function renderFloorSection(ctx: ViewContext, compact = false, state: StepState
81
107
  ${ctx.orgSlug ? `<span class="text-xs text-gray-400 font-mono">${ctx.orgSlug}</span>` : ''}
82
108
  ${promptStat}
83
109
  </div>
110
+ ${ctx.teamMembers ? renderTeamMembers(ctx.teamMembers, ctx.email) : ""}
84
111
  </div>`;
85
112
  }
86
113
 
@@ -88,12 +115,14 @@ function renderFloorSection(ctx: ViewContext, compact = false, state: StepState
88
115
  const slugLabel = ctx.orgSlug
89
116
  ? `<span class="text-xs text-gray-400 font-mono">${ctx.orgSlug}</span>`
90
117
  : '';
118
+ const teamCount = ctx.teamMembers?.length ?? 0;
91
119
 
92
120
  return `
93
121
  <div>
94
122
  <div class="flex items-baseline gap-2 mb-3">
95
123
  <h2 class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Floor</h2>
96
124
  ${statusBadge("Connected", true)}
125
+ ${teamCount > 0 ? `<span class="text-xs text-gray-400">${teamCount} member${teamCount !== 1 ? "s" : ""}</span>` : ""}
97
126
  </div>
98
127
  <div class="bg-white border border-gray-200 rounded-lg px-5 py-3 flex items-center gap-3">
99
128
  <div class="w-8 h-8 rounded-lg bg-[#4A154B] flex items-center justify-center shrink-0">
@@ -102,6 +131,7 @@ function renderFloorSection(ctx: ViewContext, compact = false, state: StepState
102
131
  <p class="text-sm font-medium text-gray-900">Slack</p>
103
132
  ${slugLabel}
104
133
  </div>
134
+ ${ctx.teamMembers ? renderTeamMembers(ctx.teamMembers, ctx.email) : ""}
105
135
  </div>`;
106
136
  }
107
137