@hanzo/rooms 0.1.92 → 0.1.93
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/Chat.d.ts +3 -5
- package/dist/Chat.js +20 -74
- package/package.json +1 -1
package/dist/Chat.d.ts
CHANGED
|
@@ -2,10 +2,9 @@ export declare function Chat(): import("react").JSX.Element;
|
|
|
2
2
|
/**
|
|
3
3
|
* The same conversation, embedded in a page that is not /chat.
|
|
4
4
|
*
|
|
5
|
-
* R8: there were TWO live chat surfaces
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* drift, so /models mounts this and the second surface is gone. It carries no
|
|
5
|
+
* R8: there were TWO live chat surfaces, so /models mounts this one and the
|
|
6
|
+
* second is gone. The data-share choice is made once at onboarding; this room
|
|
7
|
+
* does not ask again when a message is sent. It carries no
|
|
9
8
|
* heading of its own: the host page already has one, and a second `<h1>` is the
|
|
10
9
|
* defect that took the landings off /chat and /dev.
|
|
11
10
|
*
|
|
@@ -32,4 +31,3 @@ export declare function refusalOf(error: Error): {
|
|
|
32
31
|
visitor: boolean;
|
|
33
32
|
provider: boolean;
|
|
34
33
|
};
|
|
35
|
-
/** The agreement free asks for, in @hanzo/ai's words so every surface says one thing. */
|
package/dist/Chat.js
CHANGED
|
@@ -17,9 +17,9 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
17
17
|
// the free pool, so what changes at sign-in is the menu, not the room.
|
|
18
18
|
//
|
|
19
19
|
// The FREE lane is about PRICE, not identity: a free turn spends no balance and
|
|
20
|
-
// is data-shared in exchange
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// is data-shared in exchange. That choice is made once, at onboarding, where
|
|
21
|
+
// the terms are linked. The line under the composer says so for as long as a
|
|
22
|
+
// free model answers. Signing in buys the paid models and a history that
|
|
23
23
|
// follows you; it is never the price of asking a question.
|
|
24
24
|
//
|
|
25
25
|
// This file used to branch on `isAuthenticated` and hand a visitor a composer
|
|
@@ -31,7 +31,7 @@ import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "r
|
|
|
31
31
|
import { billed, detail, familyOf, planRequired, plainly, refused } from "./failure.js";
|
|
32
32
|
import { Prose } from "./Prose.js";
|
|
33
33
|
import { useAgent, useAi, useChat, } from "@hanzo/ai/react";
|
|
34
|
-
import {
|
|
34
|
+
import { isFree } from "@hanzo/ai";
|
|
35
35
|
import { ASK, Chat as ChatSurface, Failure, Parts, words } from "@hanzo/ui/chat";
|
|
36
36
|
import { carriedIn, carrying, compose, partsOf, said as told, weigh } from "./lib/attach.js";
|
|
37
37
|
import { download, dropped, fileOf, pct, readable, refOf, retrieve } from "./lib/files.js";
|
|
@@ -69,10 +69,8 @@ import { checkoutUrl } from './lib/pay.js';
|
|
|
69
69
|
import { Upgrade, useFree } from './Upgrade.js';
|
|
70
70
|
import { enter, LOGIN } from './lib/destination.js';
|
|
71
71
|
import { first } from './lib/first.js';
|
|
72
|
-
import { BAD
|
|
72
|
+
import { BAD } from "./lib/mix.js";
|
|
73
73
|
import { brand, site } from './where.js';
|
|
74
|
-
/** The veil under the free lane's consent sheet. */
|
|
75
|
-
const SCRIM = mix("var(--pure-black)", 72, "srgb");
|
|
76
74
|
// NO REASONING PANEL HERE. Every assistant turn opened a "Thinking" disclosure
|
|
77
75
|
// listing seven steps — "Planning CSS extraction for chat styles", "Designing
|
|
78
76
|
// responsive top bar grid layout" — typed into this file as a literal array.
|
|
@@ -89,24 +87,6 @@ const SCRIM = mix("var(--pure-black)", 72, "srgb");
|
|
|
89
87
|
* room has always read at, so an untouched setting changes nothing.
|
|
90
88
|
*/
|
|
91
89
|
const MEASURE = "var(--container-prose, 48rem)";
|
|
92
|
-
/**
|
|
93
|
-
* The store, or null when the browser will not give us one.
|
|
94
|
-
*
|
|
95
|
-
* Reading `window.localStorage` THROWS `SecurityError` where site data is
|
|
96
|
-
* blocked — third-party contexts, Safari's Block All Cookies, a hardened
|
|
97
|
-
* profile — and this is called from inside a send handler, so an unguarded
|
|
98
|
-
* access takes the send down rather than the storage.
|
|
99
|
-
*/
|
|
100
|
-
const store = () => {
|
|
101
|
-
if (typeof window === "undefined")
|
|
102
|
-
return null;
|
|
103
|
-
try {
|
|
104
|
-
return window.localStorage;
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
90
|
/**
|
|
111
91
|
* The text this screen arrived with, TYPED INTO THE COMPOSER — never sent.
|
|
112
92
|
*
|
|
@@ -182,8 +162,8 @@ export function Chat() {
|
|
|
182
162
|
// mounted under the provider the shell already put above it.
|
|
183
163
|
//
|
|
184
164
|
// ONE room, for everyone. `anonymous` changes which models are on offer and
|
|
185
|
-
// nothing else — the composer
|
|
186
|
-
//
|
|
165
|
+
// nothing else — the composer and the transcript are the same components
|
|
166
|
+
// either way, because a visitor is having the same conversation.
|
|
187
167
|
//
|
|
188
168
|
// The conversations are NOT drawn here. `Threads` is the list and the shell
|
|
189
169
|
// owns the column it sits in — one sidebar for six rooms, already built, and a
|
|
@@ -194,10 +174,9 @@ export function Chat() {
|
|
|
194
174
|
/**
|
|
195
175
|
* The same conversation, embedded in a page that is not /chat.
|
|
196
176
|
*
|
|
197
|
-
* R8: there were TWO live chat surfaces
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* drift, so /models mounts this and the second surface is gone. It carries no
|
|
177
|
+
* R8: there were TWO live chat surfaces, so /models mounts this one and the
|
|
178
|
+
* second is gone. The data-share choice is made once at onboarding; this room
|
|
179
|
+
* does not ask again when a message is sent. It carries no
|
|
201
180
|
* heading of its own: the host page already has one, and a second `<h1>` is the
|
|
202
181
|
* defect that took the landings off /chat and /dev.
|
|
203
182
|
*
|
|
@@ -476,9 +455,9 @@ function Plan({ onPress }) {
|
|
|
476
455
|
*
|
|
477
456
|
* `anonymous` opens on the free pool and offers only what the free pool can
|
|
478
457
|
* answer, because listing a paid model to someone with no balance is an offer
|
|
479
|
-
* that fails at send. Everything else — the transcript, the composer
|
|
480
|
-
*
|
|
481
|
-
*
|
|
458
|
+
* that fails at send. Everything else — the transcript, the composer — is
|
|
459
|
+
* identical, which is the point: signing in later changes the models on the
|
|
460
|
+
* menu, not the room.
|
|
482
461
|
*/
|
|
483
462
|
function Thread({ placeholder = message(), initial, anonymous = false, heading, }) {
|
|
484
463
|
/**
|
|
@@ -487,16 +466,14 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
|
|
|
487
466
|
* `useState(anonymous ? FREE : ENSO)` read the flag ONCE, on the first render
|
|
488
467
|
* — which is the render before hydration settles, where `anonymous` is always
|
|
489
468
|
* false. So every visitor opened on ENSO while the composer beside them said
|
|
490
|
-
* "Enso Free", `isFree(model)` was false,
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
* nothing, because a state initialiser cannot see a value that arrives later.
|
|
469
|
+
* "Enso Free", and `isFree(model)` was false, so the data-sharing notice
|
|
470
|
+
* never appeared. A visitor was sending on the paid pool, because a state
|
|
471
|
+
* initialiser cannot see a value that arrives later.
|
|
494
472
|
*
|
|
495
473
|
* A CHOICE, or the identity. `null` is "nobody has chosen", which is a
|
|
496
474
|
* different fact from either model and the only one that can defer to a flag
|
|
497
475
|
* that settles after mount. Picking from the menu still wins, permanently.
|
|
498
476
|
*/
|
|
499
|
-
const [asking, setAsking] = useState(null);
|
|
500
477
|
// THE MODEL AND THE EFFORT are the one choice Chat and Dev share
|
|
501
478
|
// (@hanzo/build `useMind`): the catalog from `GET /v1/models` and what the
|
|
502
479
|
// reader kept, changed from the composer's chip or from Settings.
|
|
@@ -1138,17 +1115,9 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
|
|
|
1138
1115
|
* no longer allowed to draw. `:focus-within` would say this in CSS; the
|
|
1139
1116
|
* stylesheet that would hold it cannot reach past gui's own rule, so the
|
|
1140
1117
|
* state is read where the event is. */
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
// us no store answered "no consent needed" and sent a data-shared turn
|
|
1145
|
-
// having asked nobody. No store means no record, and no record means ask.
|
|
1146
|
-
if (isFree(model) && (!s || !hasConsent(s))) {
|
|
1147
|
-
setAsking(text);
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
speak(text);
|
|
1151
|
-
}, [model, speak]);
|
|
1118
|
+
// A free send goes. The data-share choice was made at onboarding, once,
|
|
1119
|
+
// and the line under the composer still says what a free model does.
|
|
1120
|
+
const guarded = speak;
|
|
1152
1121
|
/**
|
|
1153
1122
|
* The draft and the files, sent as one turn.
|
|
1154
1123
|
*
|
|
@@ -1317,25 +1286,6 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
|
|
|
1317
1286
|
enter(LOGIN);
|
|
1318
1287
|
}, children: "Sign in" })) : lift ? (_jsx(Button, { size: "sm", variant: "outline", onClick: () => setAsk({ ...lift, model: nameOf(model, listed) }), children: "Upgrade" })) : how?.visitor ? (_jsx(Button, { size: "sm", variant: "outline", onClick: () => enter(), children: "Sign in to keep going" })) : (_jsx(Anchor, { href: checkoutUrl(), children: _jsx(Button, { size: "sm", variant: "outline", children: "Add credit" }) })) })) : null] }) }));
|
|
1319
1288
|
};
|
|
1320
|
-
// The agreement is asked OVER the room, not instead of it. Returning the
|
|
1321
|
-
// consent on its own replaced the whole pane: the question the reader had
|
|
1322
|
-
// just typed left the screen, and the first thing a stranger saw of the
|
|
1323
|
-
// product was a page about training data. The room stays; the sheet sits on
|
|
1324
|
-
// top of it, and the question is still there behind it.
|
|
1325
|
-
const consent = asking === null ? null : (_jsx(Consent, { question: told(asking).text, onAgree: () => {
|
|
1326
|
-
const s = store();
|
|
1327
|
-
// Nothing to persist into is not a reason to refuse the turn the
|
|
1328
|
-
// reader just agreed to; it means the question is asked again next
|
|
1329
|
-
// time, which is the honest cost of a browser that stores nothing.
|
|
1330
|
-
if (s)
|
|
1331
|
-
grantConsent(s);
|
|
1332
|
-
const held = asking;
|
|
1333
|
-
setAsking(null);
|
|
1334
|
-
if (held)
|
|
1335
|
-
speak(held);
|
|
1336
|
-
}, onCancel: () => {
|
|
1337
|
-
setAsking(null);
|
|
1338
|
-
} }));
|
|
1339
1289
|
// Dev, from the Summary's Create file or site: a move inside the app where the
|
|
1340
1290
|
// host's app reads the path (hanzo.ai), else Dev on hanzo.ai, as the shell's
|
|
1341
1291
|
// Dev place goes from a host without it (hanzo.team).
|
|
@@ -1573,9 +1523,5 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
|
|
|
1573
1523
|
empty: heading === null ? undefined : (_jsxs(YStack, { "data-slot": "opening", items: "center", width: "100%", pt: "$6", pb: "$2", ...(media.sm ? null : { flex: 1, justify: "flex-end" }), children: [room.length > 1 ? (_jsx(XStack, { items: "center", justify: "center", mb: "$4", children: room.map((name, i) => (_jsx(Box, { ml: i ? -16 : 0, rounded: 40, borderWidth: 2, borderColor: "$background", z: room.length - i, children: _jsx(Face, { src: faces[name]?.avatar, emoji: faces[name]?.emoji, name: name, size: 72 }) }, name))) })) : portrait ? (_jsx(Box, { width: 160, height: 160, rounded: 24, overflow: "hidden", borderWidth: 2, borderColor: "var(--white-15)", boxShadow: "0 10px 24px color-mix(in srgb, var(--pure-black) 70%, transparent)", mb: "$4", bg: "var(--neutral-900)", children: _jsx(View, { render: _jsx("img", { src: portrait, alt: withWhom }), width: "100%", height: "100%", objectFit: "cover" }) })) : null, room.length === 1 && !portrait ? (_jsx(Box, { mb: "$3", children: _jsx(Face, { emoji: mate?.emoji ?? persona?.emoji, name: withWhom, size: 72 }) })) : null, _jsx(Text, { render: "h1", fontSize: "$7", lineHeight: "$7", fontWeight: "700", color: "$ink", text: "center", mb: "$1", children: join(room) || heading || (firstName ? `What’s up, ${firstName}?` : "What’s up?") }), room.length === 1 && (mate?.description ?? persona?.description) ? (_jsx(Text, { fontSize: "$3", color: "$soft", text: "center", maxW: 500, mb: "$4", lineHeight: "$4", children: mate?.description ?? persona?.description })) : null] })),
|
|
1574
1524
|
// The transcript reads at the same measure. `Thread` defaults to 768
|
|
1575
1525
|
// itself; handing it the column here is what lets Width reach it.
|
|
1576
|
-
column: { maxW: MEASURE }, composer: prompt({ ...composing, field: mention.field }, mention.state) }), _jsx(AgentMaker, {}), carry.dialog, _jsx(XStack, { items: "center", justify: "center", py: "$1", px: "$3", gap: "$2", children: isFree(model) ? (_jsxs(Text, { fontSize: "$1", color: "$soft", children: ["Free chat is data-shared to train models.", " ", _jsx(Anchor, { href: site("/legal/privacy"), fontSize: "$1", color: "$ink", children: "Privacy" })] })) : (_jsxs(_Fragment, { children: [_jsx(Anchor, { href: site("/legal/terms"), fontSize: "$1", color: "$soft", children: "Terms" }), _jsx(Text, { fontSize: "$1", color: "$soft", children: "\u00B7" }), _jsx(Anchor, { href: site("/legal/privacy"), fontSize: "$1", color: "$soft", children: "Privacy" })] })) }),
|
|
1577
|
-
}
|
|
1578
|
-
/** The agreement free asks for, in @hanzo/ai's words so every surface says one thing. */
|
|
1579
|
-
function Consent({ question, onAgree, onCancel, }) {
|
|
1580
|
-
return (_jsx(YStack, { position: "absolute", t: 0, l: 0, r: 0, b: 0, items: "center", justify: "center", p: "$4", z: 40, bg: SCRIM, children: _jsxs(YStack, { width: "100%", maxW: 512, px: "$5", py: "$5", gap: "$4", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$background", children: [_jsx(Text, { render: "h2", fontSize: "$6", fontWeight: "500", color: "$ink", children: freeCopy.consentTitle }), question ? (_jsxs(Text, { fontSize: "$3", color: "$soft", numberOfLines: 2, children: ["Your question is waiting: \u201C", question, "\u201D"] })) : null, _jsx(Text, { fontSize: "$4", color: "$soft", children: freeCopy.consentBody }), _jsx(YStack, { gap: "$2", children: freeCopy.consentPoints.map((point) => (_jsx(Text, { fontSize: "$3", color: "$soft", children: point }, point))) }), _jsxs(XStack, { gap: "$3", flexWrap: "wrap", children: [_jsx(Button, { onClick: onAgree, children: freeCopy.consentAgreeCta }), _jsx(Button, { variant: "outline", onClick: onCancel, children: freeCopy.consentCancelCta })] }), _jsxs(Text, { fontSize: "$2", color: "$soft", children: [freeCopy.termsText, " ", _jsx(Anchor, { href: site("/legal/terms"), children: "Terms" }), " · ", _jsx(Anchor, { href: site("/legal/privacy"), children: "Privacy" })] })] }) }));
|
|
1526
|
+
column: { maxW: MEASURE }, composer: prompt({ ...composing, field: mention.field }, mention.state) }), _jsx(AgentMaker, {}), carry.dialog, _jsx(XStack, { items: "center", justify: "center", py: "$1", px: "$3", gap: "$2", children: isFree(model) ? (_jsxs(Text, { fontSize: "$1", color: "$soft", children: ["Free chat is data-shared to train models.", " ", _jsx(Anchor, { href: site("/legal/privacy"), fontSize: "$1", color: "$ink", children: "Privacy" })] })) : (_jsxs(_Fragment, { children: [_jsx(Anchor, { href: site("/legal/terms"), fontSize: "$1", color: "$soft", children: "Terms" }), _jsx(Text, { fontSize: "$1", color: "$soft", children: "\u00B7" }), _jsx(Anchor, { href: site("/legal/privacy"), fontSize: "$1", color: "$soft", children: "Privacy" })] })) }), _jsx(Upgrade, { ask: ask, onClose: () => setAsk(null) })] }));
|
|
1581
1527
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/rooms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.93",
|
|
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",
|