@hanzo/rooms 0.1.74 → 0.1.75

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.js CHANGED
@@ -63,7 +63,7 @@ import { Share } from "./Share.js";
63
63
  import { ConversationMenu, ThreadMore, useThreadList } from "./thread.js";
64
64
  import { Schedule } from "./Schedule.js";
65
65
  import { keepFile, useAttach, useKeep } from "./keep.js";
66
- import { artifacts, lead, nameOf, PAGE, Panel, PanelToggle, prompt, renders, Reply, Served, Summary, useDeck, useKey, Suggestions, useCarry, useMind, useSuggestions, usePanel, frames, name as tabName, } from "@hanzo/build";
66
+ import { artifacts, lead, nameOf, PAGE, Panel, PanelToggle, prompt, renders, Reply, Served, Summary, useDeck, useKey, Suggestions, useCarry, useLanguage, useMind, useSuggestions, usePanel, frames, name as tabName, } from "@hanzo/build";
67
67
  import { checkoutUrl } from './lib/pay.js';
68
68
  import { Upgrade, useFree } from './Upgrade.js';
69
69
  import { enter, LOGIN } from './lib/destination.js';
@@ -876,7 +876,9 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
876
876
  // as a transcript.
877
877
  const { token } = useIamToken();
878
878
  const ear = useMemo(() => speech({ ...base(), ...(token ? { token } : {}) }), [token]);
879
- const voice = useVoice({ speech: ear, ...useDictation(draft, setDraft) });
879
+ // The language dictation listens in: the person's own (Settings › General), or the browser's.
880
+ const [spoken, speakIn] = useLanguage(target);
881
+ const voice = useVoice({ speech: ear, ...(spoken ? { language: spoken } : {}), ...useDictation(draft, setDraft) });
880
882
  // TALK MODE: a hands-free conversation on Hanzo's realtime socket, /v1/voice.
881
883
  // The mic streams up, the answer streams back as speech with its words in
882
884
  // `talk.reply`, and talking over it stops it. Its own conversation, so its
@@ -1242,7 +1244,7 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
1242
1244
  : how?.provider
1243
1245
  ? `${familyOf(model) || nameOf(model, listed)} is unavailable right now.`
1244
1246
  : plainly(wrong, "this conversation");
1245
- return (_jsx(Failure, { onRetry: resign || bill?.code === "free_plan_cap" ? undefined : () => speak(text, question.id), children: _jsxs(YStack, { gap: "$2", children: [_jsxs(Text, { fontSize: "$3", color: "$ink", children: [whom.length ? `No answer from ${join(whom)}. ` : "", said] }), how?.provider && wrong ? _jsx(Why, { text: detail(wrong) }) : null, !bill && (resign || lift || how?.wallet || how?.visitor) ? (_jsx(XStack, { gap: "$2", flexWrap: "wrap", children: resign ? (_jsx(Button, { size: "sm", onClick: () => {
1247
+ return (_jsx(Failure, { onRetry: resign || lift || bill?.code === "free_plan_cap" ? undefined : () => speak(text, question.id), children: _jsxs(YStack, { gap: "$2", children: [_jsxs(Text, { fontSize: "$3", color: "$ink", children: [whom.length ? `No answer from ${join(whom)}. ` : "", said] }), how?.provider && wrong ? _jsx(Why, { text: detail(wrong) }) : null, lift || (!bill && (resign || how?.wallet || how?.visitor)) ? (_jsx(XStack, { gap: "$2", flexWrap: "wrap", children: resign ? (_jsx(Button, { size: "sm", onClick: () => {
1246
1248
  if (typeof window !== "undefined") {
1247
1249
  localStorage.removeItem("hanzo_iam_access_token");
1248
1250
  localStorage.removeItem("hanzo_iam_refresh_token");
@@ -1331,12 +1333,12 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
1331
1333
  if (!open)
1332
1334
  setPlan(null);
1333
1335
  }, prompt: plan ?? "", model: model, models: models.map((m) => ({ id: m.id, name: nameOf(m.id, listed) })) }), dragging ? (_jsx(YStack, { "aria-hidden": true, position: "absolute", t: 0, r: 0, b: 0, l: 0, z: 10, items: "center", justify: "center", bg: "var(--surface-scrim)", borderWidth: 2, borderStyle: "dashed", borderColor: "$borderColor", rounded: "var(--pane-round)", pointerEvents: "none", children: _jsxs(XStack, { items: "center", gap: "$2", px: "$4", py: "$2.5", rounded: "$10", bg: "$background", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(Paperclip, { size: 15, "aria-hidden": true }), _jsx(Text, { fontSize: "$4", color: "$ink", children: "Drop files to attach" })] }) })) : null, _jsxs(XStack, { "data-slot": "conversation-head", height: 52, items: "center", justify: "space-between", gap: "$3", pl: "$4", pr: "$2", shrink: 0, children: [_jsxs(XStack, { items: "center", gap: "$2", minW: 0, flex: 1, children: [mine ? (_jsx(Text, { fontSize: 15, lineHeight: 20, color: "$ink", numberOfLines: 1, shrink: 1, minW: 0, children: mine.title || "Untitled" })) : null, room.length ? (_jsx(XStack, { role: "list", "aria-label": "Agents in this chat", items: "center", gap: "$1.5", minW: 0, overflowX: "auto", "$platform-web": { scrollbarWidth: "none" }, children: room.map((name) => (_jsxs(XStack, { role: "listitem", items: "center", gap: "$1.5", pl: "$1", pr: "$0.5", py: "$0.5", rounded: 999, borderWidth: 1, borderColor: "$borderColor", shrink: 0, children: [_jsx(Face, { src: faces[name]?.avatar ?? (name === withWhom ? portrait : undefined), emoji: faces[name]?.emoji ?? (name === withWhom ? persona?.emoji : undefined), name: name, size: 22 }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$color", numberOfLines: 1, display: "none", $sm: { display: "flex" }, children: name }), _jsx(Box, { render: "button", onClick: () => openAgent(name), "aria-label": `Remove ${name} from this chat`, title: `Remove ${name}`, width: 24, height: 24, rounded: 999, items: "center", justify: "center", hoverStyle: { bg: "$hover" }, children: _jsx(X, { size: 13, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }) })] }, name))) })) : null] }), _jsxs(XStack, { "data-slot": "conversation-controls", items: "center", gap: 2, shrink: 0, children: [open ? _jsx(Share, { thread: open }) : null, mine ? (_jsx(ConversationMenu, { threads: alone, active: open, onSchedule: anonymous ? undefined : () => setPlan(lastAsked()), children: _jsx(XStack, { children: _jsx(ThreadMore, { t: mine }) }) })) : null, _jsx(Summary, { sections: sections, onOpen: () => setPanel(true) }), _jsx(PanelToggle, { open: panel, onToggle: () => setPanel(!panel) })] })] }), wide && aside !== null ? (_jsx(Beside, { children: _jsx(Panel, { side: deck, kinds: NONE, onHide: () => setPanel(false), label: "Tabs", ...browsing }) })) : null, !wide && panel ? _jsx(Panel, { side: deck, kinds: NONE, sheet: true, onHide: () => setPanel(false), label: "Tabs", ...browsing }) : null, isFree(model) ? (_jsxs(Text, { fontSize: "$2", color: "$soft", text: "center", maxW: MEASURE, mx: "auto", px: "$4", py: "$2", children: [freeCopy.notice, " ", _jsx(Anchor, { href: site("/legal/privacy"), children: freeCopy.noticeCta })] })) : null, _jsx(ChatSurface, { ...chat, messages: shown, send: guarded,
1334
- // AN EMPTY ROOM CENTRES ITS COMPOSER. While nothing has been said the
1335
- // opening (the heading and the starters) and the composer sit as one
1336
- // group in the middle of the height, with or without the plan bars in
1337
- // the composer's foot; the moment a turn exists the transcript takes the
1338
- // height and the composer docks under it.
1339
- center: true,
1336
+ // AN EMPTY ROOM CENTRES ITS COMPOSER on a laptop. While nothing has been
1337
+ // said the opening (the heading) and the composer sit as one group in the
1338
+ // middle of the height; the moment a turn exists the transcript takes the
1339
+ // height and the composer docks under it. A phone keeps it at the foot,
1340
+ // over the keyboard, from the first moment.
1341
+ center: media.sm,
1340
1342
  // AS IT WAS WRITTEN. The package renders a turn's words plainly and
1341
1343
  // says so — it ships no markdown pipeline because the plugin set is the
1342
1344
  // surface's to choose — so an answer arrived here reading "```ts" with
@@ -1459,6 +1461,8 @@ function Thread({ placeholder = message(), initial, anonymous = false, heading,
1459
1461
  limits,
1460
1462
  onUpgrade: (id) => setAsk({ model: nameOf(id, listed) }),
1461
1463
  voice,
1464
+ language: spoken,
1465
+ onLanguage: speakIn,
1462
1466
  talk,
1463
1467
  // While nothing has been said: what is worth doing next.
1464
1468
  below: said.length === 0 && !anonymous ? _jsx(Suggestions, { rows: hints.rows, onPick: setDraft, onDismiss: hints.dismiss }) : undefined,
package/dist/failure.d.ts CHANGED
@@ -25,7 +25,7 @@ export declare function plainly(error: unknown, subject: string): string;
25
25
  *
26
26
  * The gateway refuses a model the plan does not include with 402
27
27
  * `plan_required`, a Free caller whose share is spent with 402
28
- * `allowance_spent`, and a free request the shared pool cannot serve with 429
28
+ * (now 429) `allowance_spent`, and a free request the shared pool cannot serve with 429
29
29
  * `pool_busy` or `pool_exhausted`, each with an `upgrade_url`. The code decides,
30
30
  * never the sentence. The address is kept only when it is an https page on
31
31
  * hanzo.ai or one of its hosts; any other leaves `href` empty and the caller
package/dist/failure.js CHANGED
@@ -86,17 +86,18 @@ export function plainly(error, subject) {
86
86
  * the pool spent for the window, and the pool every free user shares used up.
87
87
  */
88
88
  const LIFTED = {
89
- plan_required: 402,
90
- allowance_spent: 402,
91
- pool_busy: 429,
92
- pool_exhausted: 429,
89
+ plan_required: [402],
90
+ // 429 since the gateway's free notice (object/free_notice.go); 402 before it.
91
+ allowance_spent: [402, 429],
92
+ pool_busy: [429],
93
+ pool_exhausted: [429],
93
94
  };
94
95
  /**
95
96
  * Whether a refusal asks for a plan, and the checkout it names.
96
97
  *
97
98
  * The gateway refuses a model the plan does not include with 402
98
99
  * `plan_required`, a Free caller whose share is spent with 402
99
- * `allowance_spent`, and a free request the shared pool cannot serve with 429
100
+ * (now 429) `allowance_spent`, and a free request the shared pool cannot serve with 429
100
101
  * `pool_busy` or `pool_exhausted`, each with an `upgrade_url`. The code decides,
101
102
  * never the sentence. The address is kept only when it is an https page on
102
103
  * hanzo.ai or one of its hosts; any other leaves `href` empty and the caller
@@ -105,7 +106,7 @@ const LIFTED = {
105
106
  export function planRequired(error) {
106
107
  const said = error?.body?.error;
107
108
  const code = typeof said?.code === 'string' ? said.code : '';
108
- if (!LIFTED[code] || status(error) !== LIFTED[code])
109
+ if (!LIFTED[code]?.includes(status(error) ?? 0))
109
110
  return null;
110
111
  return { href: ours(said?.upgrade_url) };
111
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/rooms",
3
- "version": "0.1.74",
3
+ "version": "0.1.75",
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",
@@ -49,7 +49,7 @@
49
49
  "peerDependencies": {
50
50
  "@hanzo/ai": ">=0.6.23",
51
51
  "@hanzo/appearance": ">=0.2.6",
52
- "@hanzo/build": ">=0.2.61",
52
+ "@hanzo/build": ">=0.2.62",
53
53
  "@hanzo/composer": ">=0.2.0",
54
54
  "@hanzo/event": ">=0.3.56",
55
55
  "@hanzo/gui": "^8.3.5",
@@ -67,7 +67,7 @@
67
67
  "devDependencies": {
68
68
  "@hanzo/ai": "0.6.23",
69
69
  "@hanzo/appearance": "0.2.8",
70
- "@hanzo/build": "0.2.61",
70
+ "@hanzo/build": "0.2.62",
71
71
  "@hanzo/composer": "0.2.0",
72
72
  "@hanzo/design": "0.5.34",
73
73
  "@hanzo/event": "0.3.56",