@lightupai/polaris 0.0.47 → 0.0.49

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.47",
3
+ "version": "0.0.49",
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
@@ -355,6 +355,80 @@ export function renderLandingPage(): string {
355
355
  </div>
356
356
  </div>
357
357
 
358
+ <!-- Pricing -->
359
+ <div class="py-16 border-t border-gray-200">
360
+ <div class="max-w-5xl mx-auto px-6">
361
+
362
+ <div class="text-center">
363
+ <h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Pricing</h2>
364
+ <p class="mt-4 text-3xl font-bold text-gray-900">Start free. Upgrade when you're ready.</p>
365
+ <p class="mt-2 text-gray-500">Unlimited users on every plan. No per-seat pricing.</p>
366
+ </div>
367
+
368
+ <div class="mt-12 grid grid-cols-1 md:grid-cols-3 gap-6">
369
+
370
+ <!-- Free -->
371
+ <div class="bg-white border border-gray-200 rounded-xl p-8">
372
+ <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Free</h3>
373
+ <div class="mt-4">
374
+ <span class="text-4xl font-bold text-gray-900">$0</span>
375
+ <span class="text-gray-500 text-sm ml-1">forever</span>
376
+ </div>
377
+ <p class="mt-2 text-sm text-gray-500">For individuals and small teams getting started.</p>
378
+ <a href="/signup" class="mt-6 block w-full text-center px-4 py-2.5 bg-gray-900 text-white text-sm font-medium rounded-lg hover:bg-gray-800 transition">Get started</a>
379
+ <ul class="mt-6 space-y-3 text-sm text-gray-700">
380
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>Unlimited users</li>
381
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg><span><strong>1,000</strong> prompts / month</span></li>
382
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg><span><strong>5 GB</strong> data captured</span></li>
383
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>7 days session history</li>
384
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>Community support</li>
385
+ </ul>
386
+ </div>
387
+
388
+ <!-- Team -->
389
+ <div class="bg-white border-2 border-polaris-600 rounded-xl p-8">
390
+ <h3 class="text-sm font-semibold text-polaris-600 uppercase tracking-wider">Team</h3>
391
+ <div class="mt-4">
392
+ <span class="text-4xl font-bold text-gray-900">$49</span>
393
+ <span class="text-gray-500 text-sm ml-1">/month</span>
394
+ </div>
395
+ <p class="mt-2 text-sm text-gray-500">For teams that need more capacity, history, and search.</p>
396
+ <a href="/signup" class="mt-6 block w-full text-center px-4 py-2.5 bg-polaris-700 text-white text-sm font-medium rounded-lg hover:bg-polaris-800 transition">Get started</a>
397
+ <ul class="mt-6 space-y-3 text-sm text-gray-700">
398
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>Unlimited users</li>
399
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg><span><strong>10,000</strong> prompts / month</span></li>
400
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg><span><strong>50 GB</strong> data captured</span></li>
401
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg><span><strong>90 days</strong> session history</span></li>
402
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>Priority support</li>
403
+ </ul>
404
+ </div>
405
+
406
+ <!-- Enterprise -->
407
+ <div class="bg-white border border-gray-200 rounded-xl p-8">
408
+ <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Enterprise</h3>
409
+ <div class="mt-4">
410
+ <span class="text-4xl font-bold text-gray-900">Custom</span>
411
+ </div>
412
+ <p class="mt-2 text-sm text-gray-500">For organizations with compliance and scale needs.</p>
413
+ <a href="mailto:hello@withpolaris.ai" class="mt-6 block w-full text-center px-4 py-2.5 bg-white border border-gray-300 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-50 transition">Contact us</a>
414
+ <ul class="mt-6 space-y-3 text-sm text-gray-700">
415
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>Everything in Team</li>
416
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>Custom usage limits</li>
417
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>SSO, audit logs, compliance</li>
418
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>Custom integrations</li>
419
+ <li class="flex items-start gap-2"><svg class="w-4 h-4 text-green-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>Dedicated support</li>
420
+ </ul>
421
+ </div>
422
+
423
+ </div>
424
+
425
+ <div class="mt-12 text-center text-sm text-gray-400">
426
+ All plans include unlimited users and real-time Slack streaming. No credit card required for Free.
427
+ </div>
428
+
429
+ </div>
430
+ </div>
431
+
358
432
  <div class="max-w-3xl mx-auto px-6">
359
433
 
360
434
  <!-- How it works -->
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