@hanzo/rooms 0.1.8 → 0.1.10

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.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/dist/Chat.js CHANGED
@@ -66,7 +66,7 @@ import { Upgrade, useFree } from './Upgrade.js';
66
66
  import { enter, LOGIN } from './lib/destination.js';
67
67
  import { first } from './lib/first.js';
68
68
  import { BAD, mix } from "./lib/mix.js";
69
- import { site } from './where.js';
69
+ import { brand, site } from './where.js';
70
70
  /** The veil under the free lane's consent sheet. */
71
71
  const SCRIM = mix("var(--pure-black)", 72, "srgb");
72
72
  // NO REASONING PANEL HERE. Every assistant turn opened a "Thinking" disclosure
@@ -237,12 +237,12 @@ export function Ask({ placeholder, idle, }) {
237
237
  *
238
238
  * The package defaults to "Ask anything" and argues for it against
239
239
  * "Message <model>" — a name that moves renames the control every time the
240
- * model does. This is not that: it is the PRODUCT's name, one constant string,
241
- * and it is already how this file addresses a character (`Message
242
- * ${persona.name}`). `label` stays "Ask anything", so the accessible name does
243
- * not move when the placeholder does.
240
+ * model does. This is not that: it is the PRODUCT's name — the host's brand,
241
+ * Hanzo AI or Hanzo Team — and it is already how this file addresses a
242
+ * character (`Message ${persona.name}`). `label` stays "Ask anything", so the
243
+ * accessible name does not move when the placeholder does.
244
244
  */
245
- const MESSAGE = "Message Hanzo AI";
245
+ const message = () => `Message ${brand()}`;
246
246
  /** The id the room's system turn is held under in the thread. */
247
247
  const ROOM = "room";
248
248
  /** The models that route a turn rather than name the one that answers it. */
@@ -305,7 +305,7 @@ const STALL = 300_000;
305
305
  * consent — is identical, which is the point: signing in later changes the
306
306
  * models on the menu, not the room.
307
307
  */
308
- function Thread({ placeholder = MESSAGE, initial, anonymous = false, heading, }) {
308
+ function Thread({ placeholder = message(), initial, anonymous = false, heading, }) {
309
309
  /**
310
310
  * The model, and WHO decides it.
311
311
  *
package/dist/Column.js CHANGED
@@ -25,6 +25,7 @@ import { useSpan } from './span.js';
25
25
  import { pane } from './ground.js';
26
26
  import { pick } from './lib/session.js';
27
27
  import { app, talk } from './lib/host.js';
28
+ import { brand } from './where.js';
28
29
  /** The grip stands in the gutter between the column and the pane. gui carries a `calc()` to the page unchanged; its
29
30
  * types only name custom properties. */
30
31
  const GUTTER_END = 'calc(-1 * var(--pane-gap))';
@@ -74,10 +75,10 @@ export function Column({ host, mode, thread, onChat, onDev, go, drawer = false,
74
75
  window.location.assign(mode === 'dev' ? app('') : talk());
75
76
  };
76
77
  if (collapsed)
77
- return (_jsx(YStack, { "data-slot": "app-column", "data-collapsed": "", position: "relative", shrink: 0, width: RAIL, "$max-md": { display: 'none' }, children: _jsxs(Sidebar, { "data-slot": "app-rail", "data-collapsed": "true", role: "navigation", "aria-label": "Hanzo", ...pane(), ...FILL, px: "$1.5", py: "$2.5", gap: "$1.5", items: "center", children: [_jsx(SidebarIconButton, { label: "Expand sidebar", onPress: () => setCollapsed(false), width: 36, height: 36, rounded: 999, children: _jsx(PanelLeft, { size: 16 }) }), _jsx(ModeMark, { mode: mode, onChat: onChat, onDev: onDev, stacked: true }), _jsx(SidebarIconButton, { label: mode === 'chat' ? 'New chat' : 'New run', onPress: create, width: 36, height: 36, rounded: 999, children: _jsx(Plus, { size: 16 }) }), _jsx(YStack, { flex: 1 }), _jsx(Me, { host: host, narrow: true, onOrg: org })] }) }));
78
+ return (_jsx(YStack, { "data-slot": "app-column", "data-collapsed": "", position: "relative", shrink: 0, width: RAIL, "$max-md": { display: 'none' }, children: _jsxs(Sidebar, { "data-slot": "app-rail", "data-collapsed": "true", role: "navigation", "aria-label": brand(), ...pane(), ...FILL, px: "$1.5", py: "$2.5", gap: "$1.5", items: "center", children: [_jsx(SidebarIconButton, { label: "Expand sidebar", onPress: () => setCollapsed(false), width: 36, height: 36, rounded: 999, children: _jsx(PanelLeft, { size: 16 }) }), _jsx(ModeMark, { mode: mode, onChat: onChat, onDev: onDev, stacked: true }), _jsx(SidebarIconButton, { label: mode === 'chat' ? 'New chat' : 'New run', onPress: create, width: 36, height: 36, rounded: 999, children: _jsx(Plus, { size: 16 }) }), _jsx(YStack, { flex: 1 }), _jsx(Me, { host: host, narrow: true, onOrg: org })] }) }));
78
79
  return (_jsxs(YStack, { "data-slot": "app-column", ref: drawer ? undefined : span.ref,
79
80
  // inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
80
- style: drawer ? undefined : span.style, position: "relative", shrink: 0, height: "100%", width: drawer ? '100%' : 'var(--span)', ...(drawer ? {} : { '$max-md': { display: 'none' } }), children: [_jsxs(Sidebar, { "data-slot": "app-rail", "data-collapsed": "false", role: "navigation", "aria-label": "Hanzo", ...pane(), ...FILL, px: "$2.5", pt: "$3", pb: "$2", gap: "$0", children: [_jsxs(XStack, { "data-slot": "mode-switch", items: "center", gap: "$2", px: "$1.5", pb: "$3", minW: 0, children: [_jsx(HanzoMark, { size: 18 }), _jsx(ModeMark, { mode: mode, onChat: onChat, onDev: onDev })] }), _jsxs(XStack, { "data-slot": "app-filter", items: "center", gap: "$2", px: "$3", height: 36, rounded: 999, borderWidth: 1, borderColor: "$borderColor", bg: "$edge", shrink: 0, children: [_jsx(Search, { size: 15, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }), _jsx(Text, { render: _jsx("input", { value: words, onChange: (e) => setWords(e.target.value), onKeyDown: (e) => {
81
+ style: drawer ? undefined : span.style, position: "relative", shrink: 0, height: "100%", width: drawer ? '100%' : 'var(--span)', ...(drawer ? {} : { '$max-md': { display: 'none' } }), children: [_jsxs(Sidebar, { "data-slot": "app-rail", "data-collapsed": "false", role: "navigation", "aria-label": brand(), ...pane(), ...FILL, px: "$2.5", pt: "$3", pb: "$2", gap: "$0", children: [_jsxs(XStack, { "data-slot": "mode-switch", items: "center", gap: "$2", px: "$1.5", pb: "$3", minW: 0, children: [_jsx(HanzoMark, { size: 18 }), _jsx(ModeMark, { mode: mode, onChat: onChat, onDev: onDev })] }), _jsxs(XStack, { "data-slot": "app-filter", items: "center", gap: "$2", px: "$3", height: 36, rounded: 999, borderWidth: 1, borderColor: "$borderColor", bg: "$edge", shrink: 0, children: [_jsx(Search, { size: 15, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }), _jsx(Text, { render: _jsx("input", { value: words, onChange: (e) => setWords(e.target.value), onKeyDown: (e) => {
81
82
  if (e.key === 'Escape')
82
83
  setWords('');
83
84
  }, placeholder: "Filter", "aria-label": "Filter recents" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$2" })] }), _jsxs(YStack, { role: "list", mt: "$2.5", gap: 2, shrink: 0, children: [_jsx(YStack, { role: "listitem", children: _jsx(SidebarItem, { "data-slot": "app-new", ...ROW, active: fresh, icon: _jsx(Plus, { size: 16, "aria-hidden": true }), onPress: create, ...keys(create), children: mode === 'chat' ? 'New chat' : 'New run' }) }), mode === 'dev'
package/dist/Directory.js CHANGED
@@ -44,7 +44,6 @@ import { ArrowUpRight, Check, Plus, Search, X } from 'lucide-react';
44
44
  import { Box, Dialog, DialogContent, DialogTitle, Input, Text, XStack, YStack, } from '@hanzo/ui';
45
45
  import { Grid } from '@hanzo/ui/grid';
46
46
  import { useIam } from '@hanzo/iam/react';
47
- import { where } from './where.js';
48
47
  import { ProviderMark } from './ProviderMark.js';
49
48
  import { say } from './failure.js';
50
49
  import { api } from './lib/api.js';
@@ -85,10 +84,14 @@ const ENDPOINT = {
85
84
  * provider the registry publishes is always drawn.
86
85
  */
87
86
  const LEAD = ['tiktok', 'instagram', 'linkedin', 'slack', 'x', 'whatsapp', 'discord', 'telegram', 'facebook', 'teams', 'threads', 'youtube'];
88
- /** The host's published integrations, as directory rows. Real, public, counted. Read once. */
87
+ /**
88
+ * The published integrations (lib/integrations), as directory rows. Real,
89
+ * public, counted. Loaded when the tab first opens, so a page the rooms frame
90
+ * carries none of the catalogue's guides.
91
+ */
89
92
  let listing = null;
90
- const apps = () => (listing ??= (where().integrations?.() ?? Promise.resolve([]))
91
- .then((all) => all.map((i) => ({
93
+ const apps = () => (listing ??= import('./lib/integrations.js')
94
+ .then(({ INTEGRATIONS }) => INTEGRATIONS.map((i) => ({
92
95
  id: i.slug,
93
96
  name: i.name,
94
97
  publisher: i.creator,
package/dist/Drive.js CHANGED
@@ -435,10 +435,10 @@ export function Drive() {
435
435
  setNaming(null);
436
436
  if (e.key === 'Enter')
437
437
  handleCreateFolder(naming.value);
438
- }, placeholder: "Folder name", "aria-label": "Folder name" }), ...BARE }), _jsx(Button, { variant: "primary", size: "sm", onClick: () => handleCreateFolder(naming.value), children: "Create" }), _jsx(Button, { variant: "ghost", size: "sm", color: "$soft", onClick: () => setNaming(null), children: "Cancel" })] })) : null, busy ? (_jsxs(Text, { fontSize: "$2", color: "$soft", children: [busy, "\u2026"] })) : null, bucket || prefix ? (_jsx(YStack, { gap: "$2", children: _jsxs(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: 8, overflowX: "auto", overflowY: "hidden", children: [_jsxs(View, { display: "grid", gridTemplateColumns: FILES, px: 16, py: 10, borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { ...HEAD, children: "NAME" }), _jsx(Text, { ...HEAD, children: "TYPE" }), _jsx(Text, { ...HEAD, children: "UPDATED \u2193" }), _jsx(Text, { ...HEAD, children: "SIZE" }), _jsx(View, {})] }), !filteredEntries || filteredEntries.length === 0 ? (_jsx(Text, { py: 36, px: 16, text: "center", fontSize: "$2", color: "$faint", children: "None" })) : (filteredEntries.map((e) => {
438
+ }, placeholder: "Folder name", "aria-label": "Folder name" }), ...BARE }), _jsx(Button, { variant: "primary", size: "sm", onClick: () => handleCreateFolder(naming.value), children: "Create" }), _jsx(Button, { variant: "ghost", size: "sm", color: "$soft", onClick: () => setNaming(null), children: "Cancel" })] })) : null, busy ? (_jsxs(Text, { fontSize: "$2", color: "$soft", children: [busy, "\u2026"] })) : null, bucket || prefix ? (_jsx(YStack, { gap: "$2", children: _jsxs(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: 8, overflowX: "auto", overflowY: "hidden", children: [_jsxs(View, { display: "grid", gridTemplateColumns: FILES, minW: "min-content", px: 16, py: 10, borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { ...HEAD, children: "NAME" }), _jsx(Text, { ...HEAD, children: "TYPE" }), _jsx(Text, { ...HEAD, children: "UPDATED \u2193" }), _jsx(Text, { ...HEAD, children: "SIZE" }), _jsx(View, {})] }), !filteredEntries || filteredEntries.length === 0 ? (_jsx(Text, { py: 36, px: 16, text: "center", fontSize: "$2", color: "$faint", children: "None" })) : (filteredEntries.map((e) => {
439
439
  const isFolder = e.isDir || e.key.endsWith('/');
440
440
  const cleanName = e.key.replace(/\/+$/, '');
441
- return (_jsxs(View, { display: "grid", gridTemplateColumns: FILES, px: 16, py: 11, items: "center", cursor: "pointer", borderBottomWidth: 1, borderColor: "$borderColor", transition: "100ms", hoverStyle: { bg: '$hover' }, onClick: () => {
441
+ return (_jsxs(View, { display: "grid", gridTemplateColumns: FILES, minW: "min-content", px: 16, py: 11, items: "center", cursor: "pointer", borderBottomWidth: 1, borderColor: "$borderColor", transition: "100ms", hoverStyle: { bg: '$hover' }, onClick: () => {
442
442
  if (isFolder) {
443
443
  setPrefix(`${prefix}${cleanName}/`);
444
444
  }
@@ -458,9 +458,9 @@ export function Drive() {
458
458
  }))] }) })) : (_jsxs(_Fragment, { children: [_jsxs(YStack, { gap: "$2.5", children: [_jsx(Text, { fontSize: 12, fontWeight: "600", color: "$soft", children: "Storage Buckets" }), _jsx(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: 8, overflowX: "auto", overflowY: "hidden", children: failed ? (_jsxs(Text, { ...NOTE, children: [failed, _jsx(Button, { size: "sm", mx: "auto", mt: 10, onClick: () => {
459
459
  setFailed(null);
460
460
  setReload((n) => n + 1);
461
- }, children: "Try again" })] })) : !buckets ? (_jsx(Text, { ...NOTE, children: "Reading your storage\u2026" })) : buckets.length === 0 ? (_jsx(Text, { ...NOTE, children: "No buckets yet." })) : (buckets.map((b) => (_jsxs(XStack, { onClick: () => openBucket(b.name), items: "center", justify: "space-between", px: 16, py: 12, cursor: "pointer", bg: "transparent", transition: "100ms", hoverStyle: { bg: '$hover' }, children: [_jsxs(XStack, { items: "center", gap: "$2.5", children: [_jsx(Folder, { size: 17, color: "var(--foreground)" }), _jsx(Text, { fontSize: "$2", fontWeight: "500", color: "$ink", children: getBucketDisplayName(b) })] }), _jsxs(XStack, { items: "center", gap: "$4", children: [_jsx(Text, { fontSize: "$2", color: "$faint", children: when(b.createdAt) }), _jsx(More, { onPress: (e) => e.stopPropagation() })] })] }, b.name)))) })] }), _jsxs(YStack, { gap: "$2.5", children: [_jsx(Text, { fontSize: 12, fontWeight: "600", color: "$soft", children: "Projects (Unified VFS)" }), _jsxs(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: 8, overflowX: "auto", overflowY: "hidden", children: [_jsxs(View, { display: "grid", gridTemplateColumns: PROJECTS, px: 16, py: 10, borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { ...HEAD, children: "NAME" }), _jsx(Text, { ...HEAD, children: "TYPE" }), _jsx(Text, { ...HEAD, children: "UPDATED \u2193" }), _jsx(Text, { ...HEAD, children: "SIZE" }), _jsx(View, {})] }), filteredProjects.length === 0 ? (_jsx(Text, { py: 36, px: 16, text: "center", fontSize: "$2", color: "$faint", children: "None" })) : (filteredProjects.map((p) => (_jsxs(View, { onClick: () => {
461
+ }, children: "Try again" })] })) : !buckets ? (_jsx(Text, { ...NOTE, children: "Reading your storage\u2026" })) : buckets.length === 0 ? (_jsx(Text, { ...NOTE, children: "No buckets yet." })) : (buckets.map((b) => (_jsxs(XStack, { onClick: () => openBucket(b.name), items: "center", justify: "space-between", px: 16, py: 12, cursor: "pointer", bg: "transparent", transition: "100ms", hoverStyle: { bg: '$hover' }, children: [_jsxs(XStack, { items: "center", gap: "$2.5", children: [_jsx(Folder, { size: 17, color: "var(--foreground)" }), _jsx(Text, { fontSize: "$2", fontWeight: "500", color: "$ink", children: getBucketDisplayName(b) })] }), _jsxs(XStack, { items: "center", gap: "$4", children: [_jsx(Text, { fontSize: "$2", color: "$faint", children: when(b.createdAt) }), _jsx(More, { onPress: (e) => e.stopPropagation() })] })] }, b.name)))) })] }), _jsxs(YStack, { gap: "$2.5", children: [_jsx(Text, { fontSize: 12, fontWeight: "600", color: "$soft", children: "Projects (Unified VFS)" }), _jsxs(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: 8, overflowX: "auto", overflowY: "hidden", children: [_jsxs(View, { display: "grid", gridTemplateColumns: PROJECTS, minW: "min-content", px: 16, py: 10, borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { ...HEAD, children: "NAME" }), _jsx(Text, { ...HEAD, children: "TYPE" }), _jsx(Text, { ...HEAD, children: "UPDATED \u2193" }), _jsx(Text, { ...HEAD, children: "SIZE" }), _jsx(View, {})] }), filteredProjects.length === 0 ? (_jsx(Text, { py: 36, px: 16, text: "center", fontSize: "$2", color: "$faint", children: "None" })) : (filteredProjects.map((p) => (_jsxs(View, { onClick: () => {
462
462
  const targetBucket = buckets[0]?.name || 'hanzo';
463
463
  openBucket(targetBucket);
464
464
  setPrefix(`projects/${p.name || p.id}/`);
465
- }, display: "grid", gridTemplateColumns: PROJECTS, px: 16, py: 11, items: "center", cursor: "pointer", borderBottomWidth: 1, borderColor: "$borderColor", transition: "100ms", hoverStyle: { bg: '$hover' }, children: [_jsxs(XStack, { items: "center", gap: "$2.5", minW: 0, children: [_jsx(Folder, { size: 16, color: "var(--foreground)" }), _jsx(Text, { ...NAME, children: p.name })] }), _jsx(View, { items: "flex-start", children: _jsx(Text, { ...CHIP, children: p.type }) }), _jsx(Text, { ...CELL, children: p.updated }), _jsx(Text, { ...CELL, children: p.size }), _jsx(View, { items: "flex-start", children: _jsx(More, { onPress: (e) => e.stopPropagation() }) })] }, p.id))))] })] })] }))] })] }), over ? (_jsx(Box, { position: "absolute", t: 0, l: 0, r: 0, b: 0, z: 1000, bg: VEIL, borderWidth: 2, borderStyle: "dashed", borderColor: "var(--ring)", rounded: "$3", display: "flex", items: "center", justify: "center", pointerEvents: "none", children: _jsxs(YStack, { items: "center", gap: "$2", children: [_jsx(Upload, { size: 36, color: "currentColor" }), _jsxs(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", children: ["Drop files to upload to ", bucket || workspace] })] }) })) : null] }));
465
+ }, display: "grid", gridTemplateColumns: PROJECTS, minW: "min-content", px: 16, py: 11, items: "center", cursor: "pointer", borderBottomWidth: 1, borderColor: "$borderColor", transition: "100ms", hoverStyle: { bg: '$hover' }, children: [_jsxs(XStack, { items: "center", gap: "$2.5", minW: 0, children: [_jsx(Folder, { size: 16, color: "var(--foreground)" }), _jsx(Text, { ...NAME, children: p.name })] }), _jsx(View, { items: "flex-start", children: _jsx(Text, { ...CHIP, children: p.type }) }), _jsx(Text, { ...CELL, children: p.updated }), _jsx(Text, { ...CELL, children: p.size }), _jsx(View, { items: "flex-start", children: _jsx(More, { onPress: (e) => e.stopPropagation() }) })] }, p.id))))] })] })] }))] })] }), over ? (_jsx(Box, { position: "absolute", t: 0, l: 0, r: 0, b: 0, z: 1000, bg: VEIL, borderWidth: 2, borderStyle: "dashed", borderColor: "var(--ring)", rounded: "$3", display: "flex", items: "center", justify: "center", pointerEvents: "none", children: _jsxs(YStack, { items: "center", gap: "$2", children: [_jsx(Upload, { size: 36, color: "currentColor" }), _jsxs(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", children: ["Drop files to upload to ", bucket || workspace] })] }) })) : null] }));
466
466
  }
package/dist/Guide.js CHANGED
@@ -29,6 +29,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } fr
29
29
  import { createPortal } from 'react-dom';
30
30
  import { Text, View, XStack, YStack } from '@hanzo/gui';
31
31
  import { Button } from '@hanzo/ui';
32
+ import { brand } from './where.js';
32
33
  import { useIam } from '@hanzo/iam/react';
33
34
  import { useAi } from './lib/ai.js';
34
35
  import { Next, Scene } from './Scene.js';
@@ -259,7 +260,7 @@ export function Guide({ room = 'chat' }) {
259
260
  // The dim is four panels around the spot rather than one box with a hole:
260
261
  // a hole wants a mask, and a mask over a blurred backdrop reads as a seam.
261
262
  const panel = { position: 'fixed', bg: 'var(--surface-scrim)', z: 'var(--z-overlay)', onClick: () => done('skipped') };
262
- return createPortal(_jsxs("div", { role: "dialog", "aria-modal": "true", "aria-label": "Getting started with Hanzo", children: [_jsx(View, { ...panel, t: 0, l: 0, r: 0, height: Math.max(0, spot.top) }), _jsx(View, { ...panel, t: spot.top + spot.height, l: 0, r: 0, b: 0 }), _jsx(View, { ...panel, t: spot.top, l: 0, width: Math.max(0, spot.left), height: spot.height }), _jsx(View, { ...panel, t: spot.top, l: spot.left + spot.width, r: 0, height: spot.height }), _jsx(View, { position: "fixed", t: spot.top, l: spot.left, width: spot.width, height: spot.height, rounded: 10, borderWidth: 1, borderColor: "var(--ring)", boxShadow: RING, pointerEvents: "none", z: ABOVE }), stop.scene ? (_jsx(Scene, { step: `${i + 1} / ${stops.length}`, onNext: next, onSkip: () => done('skipped') })) : (_jsxs(YStack, { ref: cardRef, position: "fixed", t: card.top, l: card.left, width: CARD_W, z: "var(--z-modal)", bg: "var(--popover)", borderWidth: 1, borderColor: "var(--border-control)", rounded: 12, boxShadow: "var(--shadow-floating)", pt: 18, px: 18, pb: 15, children: [_jsx(View, { "aria-hidden": true, position: "absolute", width: 11, height: 11, bg: "var(--popover)", rotate: "45deg", borderColor: "var(--border-control)", ...POINTER[side] }), _jsxs(XStack, { items: "center", gap: 8, mb: 9, children: [_jsxs(Text, { fontFamily: "$body", lineHeight: LEAD, fontSize: FLOOR, letterSpacing: 1.1, textTransform: "uppercase", color: "$faint", fontVariant: ['tabular-nums'], children: [i + 1, " / ", stops.length] }), _jsx(View, { flex: 1, height: 1, bg: "$borderColor" }), _jsx(XStack, { gap: 3, children: stops.map((s, n) => (_jsx(View, { width: n === i ? 14 : 5, height: 5, rounded: 3, bg: n <= i ? '$ink' : '$rim', transition: "200ms" }, s.id))) })] }), _jsx(Text, { render: "h2", fontFamily: "$body", mb: 7, fontSize: "$6", fontWeight: "600", lineHeight: 22.1, letterSpacing: -0.17, color: "$ink", children: stop.title }), _jsx(Text, { render: "p", fontFamily: "$body", mb: 16, fontSize: "$3", lineHeight: 22.4, color: "$quiet", children: stop.body }), _jsxs(XStack, { items: "center", gap: 8, children: [_jsx(Button, { variant: "linkMuted", size: "sm", px: 2, onClick: () => done('skipped'), children: "Skip" }), _jsx(View, { flex: 1 }), i > 0 ? (_jsx(Button, { size: "sm", onClick: back, children: "Back" })) : null, _jsx(Next, { onPress: next, label: last ? 'Done' : 'Next' })] })] }))] }), document.body);
263
+ return createPortal(_jsxs("div", { role: "dialog", "aria-modal": "true", "aria-label": `Getting started with ${brand()}`, children: [_jsx(View, { ...panel, t: 0, l: 0, r: 0, height: Math.max(0, spot.top) }), _jsx(View, { ...panel, t: spot.top + spot.height, l: 0, r: 0, b: 0 }), _jsx(View, { ...panel, t: spot.top, l: 0, width: Math.max(0, spot.left), height: spot.height }), _jsx(View, { ...panel, t: spot.top, l: spot.left + spot.width, r: 0, height: spot.height }), _jsx(View, { position: "fixed", t: spot.top, l: spot.left, width: spot.width, height: spot.height, rounded: 10, borderWidth: 1, borderColor: "var(--ring)", boxShadow: RING, pointerEvents: "none", z: ABOVE }), stop.scene ? (_jsx(Scene, { step: `${i + 1} / ${stops.length}`, onNext: next, onSkip: () => done('skipped') })) : (_jsxs(YStack, { ref: cardRef, position: "fixed", t: card.top, l: card.left, width: CARD_W, z: "var(--z-modal)", bg: "var(--popover)", borderWidth: 1, borderColor: "var(--border-control)", rounded: 12, boxShadow: "var(--shadow-floating)", pt: 18, px: 18, pb: 15, children: [_jsx(View, { "aria-hidden": true, position: "absolute", width: 11, height: 11, bg: "var(--popover)", rotate: "45deg", borderColor: "var(--border-control)", ...POINTER[side] }), _jsxs(XStack, { items: "center", gap: 8, mb: 9, children: [_jsxs(Text, { fontFamily: "$body", lineHeight: LEAD, fontSize: FLOOR, letterSpacing: 1.1, textTransform: "uppercase", color: "$faint", fontVariant: ['tabular-nums'], children: [i + 1, " / ", stops.length] }), _jsx(View, { flex: 1, height: 1, bg: "$borderColor" }), _jsx(XStack, { gap: 3, children: stops.map((s, n) => (_jsx(View, { width: n === i ? 14 : 5, height: 5, rounded: 3, bg: n <= i ? '$ink' : '$rim', transition: "200ms" }, s.id))) })] }), _jsx(Text, { render: "h2", fontFamily: "$body", mb: 7, fontSize: "$6", fontWeight: "600", lineHeight: 22.1, letterSpacing: -0.17, color: "$ink", children: stop.title }), _jsx(Text, { render: "p", fontFamily: "$body", mb: 16, fontSize: "$3", lineHeight: 22.4, color: "$quiet", children: stop.body }), _jsxs(XStack, { items: "center", gap: 8, children: [_jsx(Button, { variant: "linkMuted", size: "sm", px: 2, onClick: () => done('skipped'), children: "Skip" }), _jsx(View, { flex: 1 }), i > 0 ? (_jsx(Button, { size: "sm", onClick: back, children: "Back" })) : null, _jsx(Next, { onPress: next, label: last ? 'Done' : 'Next' })] })] }))] }), document.body);
263
264
  }
264
265
  /** Let a room's guide be asked for again — a menu row can call this and reload. */
265
266
  export function replayGuide(room = 'chat') {
package/dist/Market.js CHANGED
@@ -22,6 +22,7 @@ import { Box, Text, XStack, YStack } from '@hanzo/ui';
22
22
  import { useIam } from '@hanzo/iam/react';
23
23
  import { hasSession } from './lib/session.js';
24
24
  import { useAi } from './lib/ai.js';
25
+ import { brand } from './where.js';
25
26
  /** A listing reachable over HTTP is one this app can connect on its own. */
26
27
  const reachable = (listing) => (listing.transports ?? []).includes('streamable-http');
27
28
  /** Labels a namespace carries that name no brand: `com.cloudflare.mcp` is
@@ -137,7 +138,7 @@ export function Market({ scope }) {
137
138
  return _jsx(Empty, { children: "Sign in to browse agents you can add." });
138
139
  if (failed)
139
140
  return _jsx(Empty, { children: say(failed, 'the marketplace') });
140
- return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(YStack, { p: "$5", gap: "$2", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsx(Text, { fontSize: "$7", lineHeight: "$7", fontWeight: "500", color: "$ink", children: "Put AI to work in Hanzo" }), _jsx(Text, { fontSize: "$3", color: "$soft", children: "Browse agents that bring your tools into Hanzo." }), _jsxs(XStack, { mt: "$2", width: 420, maxW: "100%", height: 36, items: "center", gap: "$2", px: "$3", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(Search, { size: 14, "aria-hidden": true }), _jsx(Text, { render: _jsx("input", { value: ask, onChange: (e) => setAsk(e.target.value), placeholder: "Search agents" }), flex: 1, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit" })] })] }), !listings ? (_jsx(Empty, { children: "Reading the shelf." })) : shown.length === 0 ? (_jsx(Empty, { children: scope === 'installed'
141
+ return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(YStack, { p: "$5", gap: "$2", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsxs(Text, { fontSize: "$7", lineHeight: "$7", fontWeight: "500", color: "$ink", children: ["Put AI to work in ", brand()] }), _jsxs(Text, { fontSize: "$3", color: "$soft", children: ["Browse agents that bring your tools into ", brand(), "."] }), _jsxs(XStack, { mt: "$2", width: 420, maxW: "100%", height: 36, items: "center", gap: "$2", px: "$3", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(Search, { size: 14, "aria-hidden": true }), _jsx(Text, { render: _jsx("input", { value: ask, onChange: (e) => setAsk(e.target.value), placeholder: "Search agents" }), flex: 1, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit" })] })] }), !listings ? (_jsx(Empty, { children: "Reading the shelf." })) : shown.length === 0 ? (_jsx(Empty, { children: scope === 'installed'
141
142
  ? 'No agents installed yet.'
142
143
  : ask
143
144
  ? 'Nothing in the registry matches that.'
package/dist/Me.js CHANGED
@@ -32,6 +32,7 @@ import { org as current, orgs, pick, renew, scope, superAdmin } from './lib/sess
32
32
  import { payPage } from './lib/pay.js';
33
33
  import { planName } from './lib/plans.js';
34
34
  import { ENTRY } from './lib/host.js';
35
+ import { where } from './where.js';
35
36
  import { SupportPicker } from './Support.js';
36
37
  import { BAD } from './lib/mix.js';
37
38
  /** The documentation Get help opens. */
@@ -140,7 +141,7 @@ export function Me({ host, narrow = false, onOrg, }) {
140
141
  const other = usePlan(personal && personal.name !== current ? personal.name : null, menu.open);
141
142
  const planFor = (o) => (o.name === current ? planOf(tier) : o.personal ? other : '');
142
143
  const who = host.person?.name || host.person?.email || '';
143
- const where = [said(here?.role ?? ''), here?.display ?? current].filter(Boolean).join(' · ');
144
+ const standing = [said(here?.role ?? ''), here?.display ?? current].filter(Boolean).join(' · ');
144
145
  // The organization in use takes focus once the menu is drawn — after the
145
146
  // popover has placed its own focus — so the keys reach the rows at once.
146
147
  const land = (node) => {
@@ -161,11 +162,11 @@ export function Me({ host, narrow = false, onOrg, }) {
161
162
  return null;
162
163
  return (_jsxs(_Fragment, { children: [_jsxs(Popover, { open: menu.open, onOpenChange: menu.onOpenChange, placement: narrow ? 'right-end' : 'top-start', allowFlip: true, stayInFrame: true, offset: 8, children: [_jsx(Popover.Anchor, { width: "100%", children: _jsxs(XStack, { ref: (el) => {
163
164
  row.current = el instanceof HTMLElement ? el : null;
164
- }, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, "aria-haspopup": "menu", "aria-expanded": menu.open, onPress: menu.toggle, width: narrow ? 40 : '100%', minH: narrow ? 40 : 48, px: narrow ? 0 : '$2', gap: "$2.5", rounded: narrow ? 999 : '$5', items: "center", justify: narrow ? 'center' : 'flex-start', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: [_jsx(Face, { host: host, size: narrow ? 30 : 32 }), narrow ? null : (_jsxs(_Fragment, { children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, text: "left", children: who }), where ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: where })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsx(Popover.Content, { ...glass(2), items: "stretch", borderWidth: 1, rounded: "$6", p: "$1.5", width: 288, maxW: "calc(100vw - 16px)", maxH: "calc(100dvh - 32px)", overflowY: "auto", overflowX: "hidden", children: _jsx(View, { render: _jsx("div", { ref: land, role: "menu", "aria-label": "Account", onKeyDown: (e) => rove(e, close) }), self: "stretch", minW: 0, children: _jsxs(YStack, { gap: 2, children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person.email || who }), _jsx(Rule, {}), _jsx(Label, { children: "Organizations" }), _jsx(YStack, { role: "group", "aria-label": "Organizations", gap: 2, children: all.map((o) => (_jsx(Item, { icon: _jsx(Mark, { org: o }), label: o.display, sub: [o.personal ? 'Personal' : said(o.role), planFor(o)].filter(Boolean).join(' · '), checked: o.name === current, onPress: () => {
165
+ }, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, "aria-haspopup": "menu", "aria-expanded": menu.open, onPress: menu.toggle, width: narrow ? 40 : '100%', minH: narrow ? 40 : 48, px: narrow ? 0 : '$2', gap: "$2.5", rounded: narrow ? 999 : '$5', items: "center", justify: narrow ? 'center' : 'flex-start', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: [_jsx(Face, { host: host, size: narrow ? 30 : 32 }), narrow ? null : (_jsxs(_Fragment, { children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, text: "left", children: who }), standing ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: standing })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsx(Popover.Content, { ...glass(2), items: "stretch", borderWidth: 1, rounded: "$6", p: "$1.5", width: 288, maxW: "calc(100vw - 16px)", maxH: "calc(100dvh - 32px)", overflowY: "auto", overflowX: "hidden", children: _jsx(View, { render: _jsx("div", { ref: land, role: "menu", "aria-label": "Account", onKeyDown: (e) => rove(e, close) }), self: "stretch", minW: 0, children: _jsxs(YStack, { gap: 2, children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person.email || who }), _jsx(Rule, {}), _jsx(Label, { children: "Organizations" }), _jsx(YStack, { role: "group", "aria-label": "Organizations", gap: 2, children: all.map((o) => (_jsx(Item, { icon: _jsx(Mark, { org: o }), label: o.display, sub: [o.personal ? 'Personal' : said(o.role), planFor(o)].filter(Boolean).join(' · '), checked: o.name === current, onPress: () => {
165
166
  menu.onOpenChange(false);
166
167
  if (o.name !== current)
167
168
  onOrg(o.name);
168
- } }, o.name))) }), _jsx(Item, { icon: _jsx(Plus, { size: 15, "aria-hidden": true }), label: "Create organization", onPress: () => (menu.onOpenChange(false), setCreating(true)) }), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Organization settings", onPress: () => (menu.onOpenChange(false), window.location.assign(ORG_SETTINGS)) }), here && !here.personal ? (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Open in Hanzo Team", trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }), onPress: () => (menu.onOpenChange(false), window.location.assign(`${ENTRY}&org=${encodeURIComponent(here.name)}`)) })) : (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Create a team", onPress: () => (menu.onOpenChange(false), setCreating(true)) })), superAdmin() ? (_jsx(Item, { icon: _jsx(Building2, { size: 15, "aria-hidden": true }), label: "All organizations", onPress: () => (menu.onOpenChange(false), setSupporting(true)) })) : null, _jsx(Rule, {}), _jsx(Item, { icon: _jsx(Sparkles, { size: 15, "aria-hidden": true }), label: "Add funds", sub: typeof tier?.balance?.effectiveAvailable === 'number' ? `Credits ${formatCents(tier.balance.effectiveAvailable)}` : 'Credits', onPress: () => (menu.onOpenChange(false), window.open(payPage(), '_blank', 'noopener')), trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }) }), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Settings", onPress: () => go(path({ kind: 'settings', section: 'general' })) }), _jsx(Item, { icon: _jsx(Gauge, { size: 15, "aria-hidden": true }), label: "Usage", onPress: () => go(path({ kind: 'settings', section: 'usage' })) }), _jsx(Item, { icon: _jsx(Sparkles, { size: 15, "aria-hidden": true }), label: "View all plans", onPress: () => go(path({ kind: 'screen', screen: 'plans' })) }), _jsx(Item, { icon: _jsx(LifeBuoy, { size: 15, "aria-hidden": true }), label: "Get help", onPress: () => (menu.onOpenChange(false), window.open(DOCS, '_blank', 'noopener,noreferrer')) }), host.signOut ? (_jsxs(_Fragment, { children: [_jsx(Rule, {}), _jsx(Item, { icon: _jsx(LogOut, { size: 15, "aria-hidden": true }), label: "Log out", onPress: () => (menu.onOpenChange(false), host.signOut?.()) })] })) : null] }) }) })] }), _jsx(Create, { open: creating, onOpenChange: setCreating }), supporting ? _jsx(SupportPicker, { onClose: () => setSupporting(false) }) : null] }));
169
+ } }, o.name))) }), _jsx(Item, { icon: _jsx(Plus, { size: 15, "aria-hidden": true }), label: "Create organization", onPress: () => (menu.onOpenChange(false), setCreating(true)) }), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Organization settings", onPress: () => (menu.onOpenChange(false), window.location.assign(ORG_SETTINGS)) }), where().team ? null : here && !here.personal ? (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Open in Hanzo Team", trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }), onPress: () => (menu.onOpenChange(false), window.location.assign(`${ENTRY}&org=${encodeURIComponent(here.name)}`)) })) : (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Create a team", onPress: () => (menu.onOpenChange(false), setCreating(true)) })), superAdmin() ? (_jsx(Item, { icon: _jsx(Building2, { size: 15, "aria-hidden": true }), label: "All organizations", onPress: () => (menu.onOpenChange(false), setSupporting(true)) })) : null, _jsx(Rule, {}), _jsx(Item, { icon: _jsx(Sparkles, { size: 15, "aria-hidden": true }), label: "Add funds", sub: typeof tier?.balance?.effectiveAvailable === 'number' ? `Credits ${formatCents(tier.balance.effectiveAvailable)}` : 'Credits', onPress: () => (menu.onOpenChange(false), window.open(payPage(), '_blank', 'noopener')), trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }) }), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Settings", onPress: () => go(path({ kind: 'settings', section: 'general' })) }), _jsx(Item, { icon: _jsx(Gauge, { size: 15, "aria-hidden": true }), label: "Usage", onPress: () => go(path({ kind: 'settings', section: 'usage' })) }), _jsx(Item, { icon: _jsx(Sparkles, { size: 15, "aria-hidden": true }), label: "View all plans", onPress: () => go(path({ kind: 'screen', screen: 'plans' })) }), _jsx(Item, { icon: _jsx(LifeBuoy, { size: 15, "aria-hidden": true }), label: "Get help", onPress: () => (menu.onOpenChange(false), window.open(DOCS, '_blank', 'noopener,noreferrer')) }), host.signOut ? (_jsxs(_Fragment, { children: [_jsx(Rule, {}), _jsx(Item, { icon: _jsx(LogOut, { size: 15, "aria-hidden": true }), label: "Log out", onPress: () => (menu.onOpenChange(false), host.signOut?.()) })] })) : null] }) }) })] }), _jsx(Create, { open: creating, onOpenChange: setCreating }), supporting ? _jsx(SupportPicker, { onClose: () => setSupporting(false) }) : null] }));
169
170
  }
170
171
  /**
171
172
  * The sheet that creates an organization: a name, then `POST /v1/account/orgs`,
@@ -1,4 +1,4 @@
1
- /** The product's name in each mode: Hanzo AI in Chat, Hanzo Dev in Dev. */
1
+ /** The product's name in each mode: the host's brand in Chat (Hanzo AI, Hanzo Team), Hanzo Dev in Dev. */
2
2
  export declare const wordmark: (mode: 'chat' | 'dev') => string;
3
3
  export declare function ModeMark({ mode, onChat, onDev, stacked, }: {
4
4
  mode: 'chat' | 'dev';
package/dist/ModeMark.js CHANGED
@@ -2,8 +2,9 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { MessageSquare, Code2 } from 'lucide-react';
4
4
  import { XStack, YStack, Text, Tooltip, TooltipContent, TooltipTrigger } from '@hanzo/ui';
5
- /** The product's name in each mode: Hanzo AI in Chat, Hanzo Dev in Dev. */
6
- export const wordmark = (mode) => (mode === 'dev' ? 'Hanzo Dev' : 'Hanzo AI');
5
+ import { brand } from './where.js';
6
+ /** The product's name in each mode: the host's brand in Chat (Hanzo AI, Hanzo Team), Hanzo Dev in Dev. */
7
+ export const wordmark = (mode) => (mode === 'dev' ? 'Hanzo Dev' : brand());
7
8
  /** One side of the switch: an icon button named for its mode, with the name as its tooltip. */
8
9
  function Side({ name, on, onPress, children }) {
9
10
  return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { children: _jsx(XStack, { render: "button", "aria-label": name, "aria-pressed": on, onPress: onPress, width: 28, height: 24, rounded: 999, items: "center", justify: "center", bg: on ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, cursor: "pointer", children: children }) }), _jsx(TooltipContent, { children: _jsx(Text, { fontSize: "$2", children: name }) })] }));
package/dist/Orgs.js CHANGED
@@ -45,7 +45,7 @@ import { createInvitation, inviteLink } from './lib/invite.js';
45
45
  import { countSignup } from './lib/intent.js';
46
46
  import { BAD, mix } from './lib/mix.js';
47
47
  import { useRooms } from './host.js';
48
- import { site, where } from './where.js';
48
+ import { brand, site, where } from './where.js';
49
49
  /** A template to set up once the organization is live. */
50
50
  const SETUP = 'hanzo:org:template';
51
51
  /** Checkout for a plan, coming back to this room. */
@@ -304,7 +304,7 @@ function Step({ at, of }) {
304
304
  return (_jsxs(Text, { render: "p", mb: 10, fontSize: "$2", fontFamily: "$mono", color: "$soft", children: ["Step ", at, " of ", of] }));
305
305
  }
306
306
  function Brand() {
307
- return (_jsx(Text, { fontSize: "$6", fontWeight: "500", mb: 28, color: "var(--foreground)", children: where().brand ?? 'Hanzo' }));
307
+ return (_jsx(Text, { fontSize: "$6", fontWeight: "500", mb: 28, color: "var(--foreground)", children: brand() }));
308
308
  }
309
309
  /** The free rung's name in the billing catalog. */
310
310
  const FREE_TIER = 'free';
package/dist/Setup.js CHANGED
@@ -15,7 +15,7 @@ import { firstRun } from '@hanzo/ui/onboarding';
15
15
  import { useKept } from './kept.js';
16
16
  import { orgs } from './lib/session.js';
17
17
  import { api } from './lib/api.js';
18
- import { site } from './where.js';
18
+ import { brand, site } from './where.js';
19
19
  /** Put away in this browser. */
20
20
  const LATER = 'hanzo.onboarding.later';
21
21
  export function Setup() {
@@ -32,5 +32,5 @@ export function Setup() {
32
32
  }, [later]);
33
33
  if (later || !owed)
34
34
  return null;
35
- return (_jsxs(XStack, { "data-slot": "setup", role: "region", "aria-label": "Set up Hanzo", items: "center", gap: "$2.5", px: "$3", py: "$2.5", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$edge", children: [_jsx(Sparkles, { size: 16, "aria-hidden": true }), _jsxs(YStack, { flex: 1, minW: 0, gap: "$0.5", children: [_jsx(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, children: "Finish setting up Hanzo" }), _jsx(Text, { render: _jsx("a", { href: site(`/onboarding?next=${encodeURIComponent('/')}`) }), color: "var(--soft, var(--muted-foreground))", fontSize: "$1", textDecorationLine: "underline", self: "flex-start", children: "Set up" })] }), _jsx(XStack, { render: "button", "aria-label": "Not now", onPress: () => setLater(true), p: "$1", rounded: 999, hoverStyle: { bg: '$hover' }, children: _jsx(X, { size: 14, "aria-hidden": true }) })] }));
35
+ return (_jsxs(XStack, { "data-slot": "setup", role: "region", "aria-label": `Set up ${brand()}`, items: "center", gap: "$2.5", px: "$3", py: "$2.5", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$edge", children: [_jsx(Sparkles, { size: 16, "aria-hidden": true }), _jsxs(YStack, { flex: 1, minW: 0, gap: "$0.5", children: [_jsxs(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, children: ["Finish setting up ", brand()] }), _jsx(Text, { render: _jsx("a", { href: site(`/onboarding?next=${encodeURIComponent('/')}`) }), color: "var(--soft, var(--muted-foreground))", fontSize: "$1", textDecorationLine: "underline", self: "flex-start", children: "Set up" })] }), _jsx(XStack, { render: "button", "aria-label": "Not now", onPress: () => setLater(true), p: "$1", rounded: 999, hoverStyle: { bg: '$hover' }, children: _jsx(X, { size: 14, "aria-hidden": true }) })] }));
36
36
  }
package/dist/Shared.js CHANGED
@@ -16,6 +16,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
16
16
  import { useEffect, useState } from 'react';
17
17
  import { useIam } from '@hanzo/iam/react';
18
18
  import { Anchor, Button, Text, XStack, YStack } from '@hanzo/ui';
19
+ import { brand } from './where.js';
19
20
  import { useHydrated } from './lib/hydrated.js';
20
21
  import { enter, LOGIN, signUp } from './lib/destination.js';
21
22
  import { Prose } from './Prose.js';
@@ -76,14 +77,14 @@ export function Shared() {
76
77
  : hydrated && !isLoading && aim && !('token' in aim) && !isAuthenticated
77
78
  ? { kind: 'ask' }
78
79
  : state;
79
- return (_jsxs(YStack, { role: "main", height: "100dvh", overflowY: "auto", bg: "$background", children: [_jsxs(XStack, { height: 48, px: "$4", items: "center", justify: "space-between", borderBottomWidth: 1, borderColor: "$borderColor", shrink: 0, children: [_jsx(Anchor, { href: "/", "aria-label": "Hanzo", children: _jsx(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", children: "Hanzo" }) }), shown.kind === 'read' && shown.shared.full ? (_jsx(Text, { fontSize: "$1", color: "$soft", children: "Shared with you \u00B7 Read only" })) : null] }), _jsx(YStack, { width: "100%", maxW: MEASURE, mx: "auto", px: "$4", py: "$6", gap: "$4", children: _jsx(Body, { state: shown, signedIn: Boolean(isAuthenticated), onOpen: () => setAgreed(true) }) })] }));
80
+ return (_jsxs(YStack, { role: "main", height: "100dvh", overflowY: "auto", bg: "$background", children: [_jsxs(XStack, { height: 48, px: "$4", items: "center", justify: "space-between", borderBottomWidth: 1, borderColor: "$borderColor", shrink: 0, children: [_jsx(Anchor, { href: "/", "aria-label": brand(), children: _jsx(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", children: brand() }) }), shown.kind === 'read' && shown.shared.full ? (_jsx(Text, { fontSize: "$1", color: "$soft", children: "Shared with you \u00B7 Read only" })) : null] }), _jsx(YStack, { width: "100%", maxW: MEASURE, mx: "auto", px: "$4", py: "$6", gap: "$4", children: _jsx(Body, { state: shown, signedIn: Boolean(isAuthenticated), onOpen: () => setAgreed(true) }) })] }));
80
81
  }
81
82
  function Body({ state, signedIn, onOpen }) {
82
83
  if (state.kind === 'reading') {
83
84
  return (_jsx(Text, { fontSize: "$3", color: "$soft", "aria-live": "polite", children: "Opening the chat." }));
84
85
  }
85
86
  if (state.kind === 'missing' || state.kind === 'gone') {
86
- return (_jsxs(YStack, { gap: "$3", items: "flex-start", children: [_jsx(Text, { render: "h1", fontSize: "$7", fontWeight: "600", color: "$ink", children: "This chat is no longer shared." }), _jsx(Text, { fontSize: "$3", color: "$soft", children: state.kind === 'missing' ? 'The link is incomplete. Ask for it again.' : 'The link was turned off, or it was never a link to a chat.' }), _jsx(Anchor, { href: "/", children: _jsx(Button, { size: "sm", variant: "outline", children: "Go to Hanzo" }) })] }));
87
+ return (_jsxs(YStack, { gap: "$3", items: "flex-start", children: [_jsx(Text, { render: "h1", fontSize: "$7", fontWeight: "600", color: "$ink", children: "This chat is no longer shared." }), _jsx(Text, { fontSize: "$3", color: "$soft", children: state.kind === 'missing' ? 'The link is incomplete. Ask for it again.' : 'The link was turned off, or it was never a link to a chat.' }), _jsx(Anchor, { href: "/", children: _jsxs(Button, { size: "sm", variant: "outline", children: ["Go to ", brand()] }) })] }));
87
88
  }
88
89
  if (state.kind === 'ask') {
89
90
  return _jsx(Ask, { signedIn: signedIn });
package/dist/Shell.js CHANGED
@@ -113,7 +113,7 @@ const CONTROL = 28;
113
113
  import { getStoredPeople, getStoredAgents, getCustomThreads, createDirectChat, } from "./contacts-store.js";
114
114
  import { bare } from './lib/bare.js';
115
115
  import { BAD } from './lib/mix.js';
116
- import { site, where } from './where.js';
116
+ import { brand, site, where } from './where.js';
117
117
  /**
118
118
  * The destinations, in rail order.
119
119
  *
@@ -128,7 +128,7 @@ const HOME = {
128
128
  return where().home;
129
129
  },
130
130
  get title() {
131
- return where().brand ?? "Hanzo";
131
+ return brand();
132
132
  },
133
133
  label: "Home",
134
134
  // The chat bubble. Home IS the conversation with the org — the rooms it talks
@@ -152,7 +152,9 @@ const HOME = {
152
152
  const CHAT = {
153
153
  id: "chat",
154
154
  route: "/chat",
155
- title: "Hanzo AI",
155
+ get title() {
156
+ return brand();
157
+ },
156
158
  label: "Chat",
157
159
  icon: MessageSquare,
158
160
  create: "New chat",
@@ -1206,7 +1208,7 @@ function Frame({ mode, children, navigate, back, forward, }) {
1206
1208
  return;
1207
1209
  e.preventDefault();
1208
1210
  navigate("/");
1209
- }, "aria-label": "Hanzo home", title: "Home" }), "data-slot": "home", display: "flex", items: "center", justify: "center", width: 28, height: 28, color: "inherit", children: _jsx(HanzoMark, { size: 18 }) }), settled && isAuthenticated ? (_jsx(XStack, { shrink: 1, minW: 0, children: _jsx(OrgProjectSwitcher, { ...orgState, alwaysShow: true }) })) : null] }), _jsxs(YStack, { "data-slot": "places-list", px: "$2", pb: "$2", "$max-md": { display: "none" }, children: [places.filter((place) => matches(place.label, filter)).map((place) => (_jsx(SidebarItem, { icon: _jsx(place.icon, { size: 16, "aria-hidden": true }), active: place.id === mode, onPress: () => {
1211
+ }, "aria-label": `${brand()} home`, title: "Home" }), "data-slot": "home", display: "flex", items: "center", justify: "center", width: 28, height: 28, color: "inherit", children: _jsx(HanzoMark, { size: 18 }) }), settled && isAuthenticated ? (_jsx(XStack, { shrink: 1, minW: 0, children: _jsx(OrgProjectSwitcher, { ...orgState, alwaysShow: true }) })) : null] }), _jsxs(YStack, { "data-slot": "places-list", px: "$2", pb: "$2", "$max-md": { display: "none" }, children: [places.filter((place) => matches(place.label, filter)).map((place) => (_jsx(SidebarItem, { icon: _jsx(place.icon, { size: 16, "aria-hidden": true }), active: place.id === mode, onPress: () => {
1210
1212
  navigate?.(place.route);
1211
1213
  onPick?.();
1212
1214
  }, children: place.label }, place.id))), matches("Settings", filter) ? (_jsx(SidebarItem, { icon: _jsx(Settings2, { size: 16, "aria-hidden": true }), onPress: () => showSettings(FIRST), children: "Settings" })) : null] }), _jsx(XStack, { px: "$3", pb: "$2", children: _jsxs(XStack, { flex: 1, height: 30, items: "center", gap: "$2", px: "$2", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(Search, { size: 13, "aria-hidden": true }), _jsx(Text, { render: _jsx("input", { value: filter, onChange: (e) => setFilter(e.target.value), onKeyDown: (e) => {
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * The root names what a host passes; each room is its own subpath, so a page
9
9
  * loads the rooms it draws and no others.
10
10
  */
11
- export { configure, where, site } from './where.js';
11
+ export { configure, where, site, brand } from './where.js';
12
12
  export type { Addresses } from './where.js';
13
13
  export { Rooms, Look, useRooms, useLook, useOrg } from './host.js';
14
14
  export type { Router, Link, RoomsProps } from './host.js';
package/dist/index.js CHANGED
@@ -8,5 +8,5 @@
8
8
  * The root names what a host passes; each room is its own subpath, so a page
9
9
  * loads the rooms it draws and no others.
10
10
  */
11
- export { configure, where, site } from './where.js';
11
+ export { configure, where, site, brand } from './where.js';
12
12
  export { Rooms, Look, useRooms, useLook, useOrg } from './host.js';
@@ -1,7 +1,11 @@
1
1
  /**
2
- * An SDK, framework or platform the host publishes a guide for. The catalogue
3
- * is the host's content (hanzo.ai's /integrations) and arrives through
4
- * `configure({ integrations })`; Directory lists it as the apps a team connects.
2
+ * The SDKs, frameworks and platforms Hanzo publishes a guide for: one catalogue,
3
+ * here. hanzo.ai's /integrations pages render it; Directory's "Works with" tab
4
+ * lists it, each row linking its page there (`site('/integrations/<slug>')`).
5
+ *
6
+ * No count of models is written into the copy: the catalogue ships with the
7
+ * package, and a number typed here would be stale the day the gateway serves
8
+ * one more.
5
9
  */
6
10
  export interface Integration {
7
11
  slug: string;
@@ -19,3 +23,13 @@ export interface Integration {
19
23
  creatorUrl: string;
20
24
  license: string;
21
25
  }
26
+ export declare const INTEGRATIONS: Integration[];
27
+ export declare const INTEGRATION_CATEGORIES: {
28
+ readonly sdk: 'AI SDKs';
29
+ readonly framework: 'Frameworks';
30
+ readonly platform: 'Platforms & Editors';
31
+ readonly infra: 'Infrastructure';
32
+ readonly language: 'Languages';
33
+ };
34
+ export declare function getIntegration(slug: string): Integration | undefined;
35
+ export declare function getIntegrationsByCategory(category: Integration['category']): Integration[];