@hanzo/rooms 0.1.94 → 0.1.96
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/Chrome.d.ts +20 -0
- package/dist/Chrome.js +31 -0
- package/dist/Orgs.js +14 -11
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/dist/Chrome.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bar over a focused page: the organization chooser, the way in, the way to
|
|
3
|
+
* pay, a beta application. One bar for all of them, so none draws its own.
|
|
4
|
+
*
|
|
5
|
+
* The brand at the start (`HanzoLockup`: the mark and `brand()`), going to this
|
|
6
|
+
* host's root; then the host's `back`, where the page has a step to go back to.
|
|
7
|
+
* At the end, once someone is signed in, who it is: the face, the name and the
|
|
8
|
+
* address, with Use another account and Log out. Signed out, nothing but the
|
|
9
|
+
* brand. Under gui's `md` the account folds into its face, a menu of the address
|
|
10
|
+
* and the same two ways out.
|
|
11
|
+
*
|
|
12
|
+
* Use another account writes this page down first (destination.ts
|
|
13
|
+
* `rememberDestination`), so the sign-in that follows comes back to it, as
|
|
14
|
+
* hanzo.ai's /join does. Log out is the account menu's own (lib/signout). The
|
|
15
|
+
* session is IAM's: the host mounts `IamProvider`.
|
|
16
|
+
*/
|
|
17
|
+
import type { ReactNode } from 'react';
|
|
18
|
+
export declare function Chrome({ back }: {
|
|
19
|
+
back?: ReactNode;
|
|
20
|
+
}): import("react").JSX.Element;
|
package/dist/Chrome.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useIam } from '@hanzo/iam/react';
|
|
4
|
+
import { Text, View, XStack, YStack } from '@hanzo/gui';
|
|
5
|
+
import { Avatar, Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@hanzo/ui';
|
|
6
|
+
import { HanzoLockup } from '@hanzogui/shell';
|
|
7
|
+
import { rememberDestination } from './lib/destination.js';
|
|
8
|
+
import { named } from './lib/session.js';
|
|
9
|
+
import { useSignOut } from './lib/signout.js';
|
|
10
|
+
import { brand } from './where.js';
|
|
11
|
+
/** The bar's height under the notch: hanzo.ai's masthead's. */
|
|
12
|
+
const HEIGHT = 60;
|
|
13
|
+
/** The notch's inset. gui carries a CSS function to the page unchanged; its types only name custom properties. */
|
|
14
|
+
const NOTCH = 'env(safe-area-inset-top)';
|
|
15
|
+
export function Chrome({ back }) {
|
|
16
|
+
const { user, isAuthenticated } = useIam();
|
|
17
|
+
const signOut = useSignOut();
|
|
18
|
+
const claimed = named();
|
|
19
|
+
const u = user;
|
|
20
|
+
const name = u?.displayName || u?.name || claimed?.name || '';
|
|
21
|
+
const email = u?.email || claimed?.email || '';
|
|
22
|
+
const avatar = typeof u?.avatar === 'string' && u.avatar.startsWith('https://') ? u.avatar : '';
|
|
23
|
+
const who = name || email;
|
|
24
|
+
const other = () => {
|
|
25
|
+
rememberDestination();
|
|
26
|
+
void signOut();
|
|
27
|
+
};
|
|
28
|
+
const out = () => void signOut();
|
|
29
|
+
const signed = isAuthenticated && Boolean(who);
|
|
30
|
+
return (_jsxs(XStack, { render: "header", "data-slot": "masthead", position: "sticky", t: 0, z: "var(--z-header)", shrink: 0, boxSizing: "content-box", height: HEIGHT, pt: NOTCH, px: "var(--page-gutter, 20px)", gap: 12, items: "center", bg: "var(--background)", borderBottomWidth: 1, borderColor: "var(--border)", children: [_jsx(Text, { render: _jsx("a", { href: "/", "aria-label": `${brand()} home` }), display: "flex", items: "center", shrink: 0, color: "var(--foreground)", textDecorationLine: "none", children: _jsx(HanzoLockup, { label: brand(), size: 22 }) }), back, _jsx(View, { flex: 1 }), signed ? (_jsxs(_Fragment, { children: [_jsxs(XStack, { render: "section", "aria-label": "Account", items: "center", gap: 12, minW: 0, "$max-md": { display: 'none' }, children: [_jsx(Avatar, { src: avatar || null, name: who, alt: "" }), _jsxs(YStack, { minW: 0, gap: 2, children: [_jsx(Text, { fontSize: "$3", fontWeight: "500", color: "$ink", numberOfLines: 1, children: who }), name && email ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: email })) : null] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: other, children: "Use another account" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: out, children: "Log out" })] }), _jsx(View, { display: "none", "$max-md": { display: 'flex' }, children: _jsxs(DropdownMenu, { placement: "bottom-end", offset: 8, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(XStack, { render: "button", "aria-label": `Account: ${who}`, width: 40, height: 40, rounded: 999, items: "center", justify: "center", borderWidth: 0, bg: "transparent", hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: _jsx(Avatar, { src: avatar || null, name: who, alt: "" }) }) }), _jsxs(DropdownMenuContent, { minW: 240, maxW: "calc(100vw - 16px)", "aria-labelledby": undefined, "aria-label": "Account", children: [_jsx(DropdownMenuLabel, { children: _jsxs(YStack, { minW: 0, gap: 2, children: [_jsx(Text, { fontSize: "$3", fontWeight: "500", color: "$ink", numberOfLines: 1, children: who }), name && email ? (_jsx(Text, { fontSize: "$2", color: "$soft", numberOfLines: 1, children: email })) : null] }) }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { onSelect: other, children: _jsx(Text, { fontSize: "$3", color: "$ink", children: "Use another account" }) }), _jsx(DropdownMenuItem, { onSelect: out, children: _jsx(Text, { fontSize: "$3", color: "$ink", children: "Log out" }) })] })] }) })] })) : null] }));
|
|
31
|
+
}
|
package/dist/Orgs.js
CHANGED
|
@@ -52,7 +52,8 @@ import { createOrg, say } from './lib/org.js';
|
|
|
52
52
|
import { countSignup } from './lib/intent.js';
|
|
53
53
|
import { BAD, mix } from './lib/mix.js';
|
|
54
54
|
import { useRooms } from './host.js';
|
|
55
|
-
import {
|
|
55
|
+
import { Chrome } from './Chrome.js';
|
|
56
|
+
import { site, where } from './where.js';
|
|
56
57
|
/** A template to set up once the organization is live. */
|
|
57
58
|
const SETUP = 'hanzo:org:template';
|
|
58
59
|
/** Checkout for a plan, coming back to this room. */
|
|
@@ -292,9 +293,14 @@ const FIELD = {
|
|
|
292
293
|
bg: 'var(--background)',
|
|
293
294
|
color: 'var(--foreground)',
|
|
294
295
|
};
|
|
295
|
-
/**
|
|
296
|
+
/**
|
|
297
|
+
* The sheet every screen stands on: the whole viewport, scrolling, its column
|
|
298
|
+
* centred, under the focused pages' one bar (`Chrome`) once someone is signed
|
|
299
|
+
* in. "Signing you in" stands alone.
|
|
300
|
+
*/
|
|
296
301
|
function Page({ label, children }) {
|
|
297
|
-
|
|
302
|
+
const { isAuthenticated } = useIam();
|
|
303
|
+
return (_jsxs(YStack, { position: "fixed", inset: 0, z: SHEET, overflowY: "auto", bg: "var(--background)", role: "region", "aria-label": label, children: [isAuthenticated ? _jsx(Chrome, {}) : null, _jsx(YStack, { pt: TOP, px: 20, pb: 48, children: children })] }));
|
|
298
304
|
}
|
|
299
305
|
/** The column a screen's words sit in. */
|
|
300
306
|
function Column({ children }) {
|
|
@@ -331,9 +337,6 @@ function Forward({ to }) {
|
|
|
331
337
|
function Step({ at, of }) {
|
|
332
338
|
return (_jsxs(Text, { render: "p", mb: 10, fontSize: "$2", fontFamily: "$mono", color: "$soft", children: ["Step ", at, " of ", of] }));
|
|
333
339
|
}
|
|
334
|
-
function Brand() {
|
|
335
|
-
return (_jsx(Text, { fontSize: "$6", fontWeight: "500", mb: 28, color: "var(--foreground)", children: brand() }));
|
|
336
|
-
}
|
|
337
340
|
/** The free rung's name in the billing catalog. */
|
|
338
341
|
const FREE_TIER = 'free';
|
|
339
342
|
/**
|
|
@@ -362,7 +365,7 @@ function SignIn({ to }) {
|
|
|
362
365
|
function Create() {
|
|
363
366
|
const [open, setOpen] = useState(false);
|
|
364
367
|
const [details, setDetails] = useState(false);
|
|
365
|
-
return (_jsxs(Page, { label: "Organizations", children: [_jsxs(Column, { children: [
|
|
368
|
+
return (_jsxs(Page, { label: "Organizations", children: [_jsxs(Column, { children: [_jsxs(XStack, { items: "flex-start", justify: "space-between", gap: 16, flexWrap: "wrap", children: [_jsxs(YStack, { children: [_jsx(Text, { render: "h1", fontSize: "$10", fontWeight: "500", letterSpacing: -0.32, color: "var(--text-primary)", children: "Organizations" }), _jsx(Text, { render: "p", mt: 6, fontSize: "$4", color: muted, children: "You do not belong to one yet." })] }), _jsxs(Button, { variant: "primary", size: "lg", onClick: () => setOpen(true), children: [_jsx(Plus, { size: 16, "aria-hidden": true }), "New organization"] })] }), _jsx(Rule, {}), _jsxs(YStack, { items: "center", py: 28, children: [_jsx(Text, { render: "p", fontSize: "$6", fontWeight: "500", color: "var(--foreground)", text: "center", children: "No organizations yet" }), _jsx(Button, { variant: "linkMuted", size: "sm", mt: 8, onClick: () => setDetails((d) => !d), children: details ? '- Details' : '+ Details' }), details ? (_jsx(Text, { render: "p", mt: 12, maxW: 440, fontSize: "$4", color: muted, lineHeight: 23.25, text: "center", children: "An organization owns every agent, credit and member. Create one and you are its owner." })) : null] }), _jsx(Rule, {})] }), open ? _jsx(CreateDialog, { onClose: () => setOpen(false) }) : null] }));
|
|
366
369
|
}
|
|
367
370
|
/** Name it, pick what it runs and its plan: a paid plan goes to checkout, Free goes straight in. */
|
|
368
371
|
function CreateDialog({ onClose }) {
|
|
@@ -580,7 +583,7 @@ function Pick({ members, current, onPick, onClose, }) {
|
|
|
580
583
|
document.addEventListener('keydown', key);
|
|
581
584
|
return () => document.removeEventListener('keydown', key);
|
|
582
585
|
}, [onClose, founding]);
|
|
583
|
-
return (_jsxs(Page, { label: "Choose an organization", children: [_jsxs(Column, { children: [_jsxs(XStack, { items: "flex-start", justify: "space-between", gap: 12, children: [_jsx(
|
|
586
|
+
return (_jsxs(Page, { label: "Choose an organization", children: [_jsxs(Column, { children: [_jsxs(XStack, { items: "flex-start", justify: "space-between", gap: 12, children: [_jsx(YStack, { flex: 1, minW: 0, children: _jsx(Heading, { title: "Choose an organization", lede: _jsx(_Fragment, { children: "Everything you open \u2014 chats, agents, credit \u2014 is this one's." }) }) }), onClose ? (_jsx(Button, { variant: "ghost", size: "icon-sm", mr: -8, onClick: onClose, "aria-label": "Close", children: _jsx(X, { size: 16, "aria-hidden": true }) })) : null] }), _jsx(Rule, {}), _jsx(XStack, { render: "form", onSubmit: (e) => {
|
|
584
587
|
e.preventDefault();
|
|
585
588
|
if (shown[0])
|
|
586
589
|
onPick(shown[0].name);
|
|
@@ -712,7 +715,7 @@ function Found({ onClose }) {
|
|
|
712
715
|
function Subscribe({ org, members, onSwitch, }) {
|
|
713
716
|
const { plan, price } = usePlan('team');
|
|
714
717
|
const seat = plan?.pricePerUser ? ' / seat' : '';
|
|
715
|
-
return (_jsx(Page, { label: "Subscribe", children: _jsxs(Column, { children: [_jsx(
|
|
718
|
+
return (_jsx(Page, { label: "Subscribe", children: _jsxs(Column, { children: [_jsx(Heading, { title: org, lead: 22.5, lede: plan ? `This organization runs on the ${plan.name} plan at ${price}${seat} / month, and nothing in it works until that plan is live.` : 'Its plan is not live yet.' }), _jsx(Rule, {}), _jsxs(XStack, { gap: 10, flexWrap: "wrap", children: [plan ? (_jsx(Button, { asChild: true, variant: "primary", size: "lg", children: _jsx("a", { href: checkout(plan), children: `Subscribe — ${price}${seat} / month` }) })) : null, members.length > 1 ? (_jsx(Button, { size: "lg", onClick: onSwitch, children: "Switch organization" })) : null] })] }) }));
|
|
716
719
|
}
|
|
717
720
|
// ── the front door's setup ─────────────────────────────────────────────────────
|
|
718
721
|
/**
|
|
@@ -808,7 +811,7 @@ function Start({ founded = null, onFounded, first, members = [], onSwitch, }) {
|
|
|
808
811
|
};
|
|
809
812
|
if (step === 'plan' && founded)
|
|
810
813
|
return _jsx(Plan, { org: founded, members: [founded], seats: 1 + sent.filter((s) => s.link).length, setup: true });
|
|
811
|
-
return (_jsx(Page, { label: "Create your team", children:
|
|
814
|
+
return (_jsx(Page, { label: "Create your team", children: _jsx(Column, { children: step === 'name' ? (_jsxs(YStack, { render: "form", onSubmit: create, children: [_jsx(Step, { at: 1, of: 3 }), _jsx(Heading, { title: "Create your team", lede: "A team owns its agents, credit and members. You are its owner." }), _jsx(Rule, {}), _jsxs(View, { render: "label", display: "block", children: [_jsx(Text, { display: "block", fontSize: "$4", fontWeight: "500", mb: 8, color: "$ink", children: "Team name" }), _jsx(Text, { render: _jsx("input", { value: name, onChange: (e) => setName(e.target.value), placeholder: "Acme", autoFocus: true }), ...FIELD }), _jsx(Text, { display: "block", fontSize: "$3", color: "$soft", mt: 8, children: "What the rail and every invoice call it." })] }), refused ? (_jsx(Text, { render: "p", role: "alert", mt: 14, fontSize: "$3", color: BAD, children: refused })) : null, _jsxs(XStack, { gap: 10, flexWrap: "wrap", mt: 24, children: [_jsx(Button, { type: "submit", variant: "primary", size: "lg", disabled: busy, children: busy ? 'Creating…' : 'Continue' }), members.length > 1 && onSwitch ? (_jsx(Button, { type: "button", size: "lg", onClick: onSwitch, children: "Switch organization" })) : null] }), _jsx(Rule, {}), _jsxs(Text, { render: "p", fontSize: "$3", color: "$soft", children: ["Just you? Hanzo Chat is free for personal use at", ' ', _jsx(Text, { render: _jsx("a", { href: site('/chat') }), color: "$ink", textDecorationLine: "underline", children: "hanzo.ai/chat" }), "."] })] })) : (_jsxs(YStack, { render: "form", onSubmit: invite, children: [_jsx(Step, { at: 2, of: 3 }), _jsx(Heading, { title: "Invite your teammates", lede: "Each address gets its own single-use link to join. You can skip this and invite people later." }), _jsx(Rule, {}), _jsxs(View, { render: "label", display: "block", children: [_jsx(Text, { display: "block", fontSize: "$4", fontWeight: "500", mb: 8, color: "$ink", children: "Email addresses" }), _jsx(Text, { render: _jsx("textarea", { value: emails, onChange: (e) => setEmails(e.target.value), placeholder: "ada@acme.com, alan@acme.com", rows: 3 }), ...FIELD }), _jsx(Text, { display: "block", fontSize: "$3", color: "$soft", mt: 8, children: "Separate them with commas, spaces or new lines." })] }), sent.length ? (_jsx(YStack, { render: "ul", gap: 8, m: 0, p: 0, mt: 18, "aria-label": "Invitations", children: sent.map((s) => (_jsx(YStack, { render: "li", children: _jsxs(Text, { fontSize: "$3", color: "$soft", wordWrap: "break-word", children: [_jsx(Text, { fontWeight: "500", color: "$ink", children: s.email }), ' ', s.link ? (_jsxs(_Fragment, { children: ["invited. Their link:", ' ', _jsx(Text, { render: _jsx("a", { href: s.link }), color: "$ink", textDecorationLine: "underline", children: s.link })] })) : (_jsx(Text, { role: "alert", color: BAD, children: s.refused }))] }) }, s.email))) })) : null, _jsx(XStack, { gap: 10, flexWrap: "wrap", mt: 24, children: sent.length ? (_jsx(Button, { type: "button", variant: "primary", size: "lg", onClick: () => setStep('plan'), children: "Continue" })) : (_jsxs(_Fragment, { children: [_jsx(Button, { type: "submit", variant: "primary", size: "lg", disabled: busy, children: busy ? 'Inviting…' : emails.trim() ? 'Invite and continue' : 'Continue' }), _jsx(Button, { type: "button", size: "lg", onClick: () => setStep('plan'), children: "Skip for now" })] })) })] })) }) }));
|
|
812
815
|
}
|
|
813
816
|
/** Where a checkout from the setup returns: the rooms' Home, on this origin. */
|
|
814
817
|
const home = () => `${window.location.origin}${where().home}`;
|
|
@@ -835,7 +838,7 @@ function Plan({ org, members, onSwitch, outage, onRetry, seats: wanted = 1, setu
|
|
|
835
838
|
['month', 'Monthly', `${money(monthly)} / seat / month`],
|
|
836
839
|
...(yearly ? [['year', 'Annual', `${money(yearly)} / seat, billed once a year`]] : []),
|
|
837
840
|
];
|
|
838
|
-
return (_jsx(Page, { label: "Choose your plan", children: _jsxs(Column, { children: [
|
|
841
|
+
return (_jsx(Page, { label: "Choose your plan", children: _jsxs(Column, { children: [setup ? _jsx(Step, { at: 3, of: 3 }) : null, _jsx(Heading, { title: org, lead: 22.5, lede: outage
|
|
839
842
|
? 'Billing could not be read just now, so this team cannot open yet. That is ours to fix.'
|
|
840
843
|
: plan
|
|
841
844
|
? `A team runs on the ${plan.name} plan, and nothing in it works until that plan is live.`
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/rooms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.96",
|
|
4
4
|
"description": "The workspace rooms — Home, Chat, Inbox, Contacts, Meet, Cal, Drive, Board, Work, Bots — and the frame that holds them, once, for every Hanzo surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|