Say how to build on the package

This commit is contained in:
2026-09-03 10:41:32 -07:00
parent 7889abf19c
commit 5657c31d2c
95 changed files with 357 additions and 314 deletions
+47 -7
View File
@@ -1,8 +1,8 @@
# Hanzo Chat
A chat client for the estate. Vite and React draw it, `@hanzo/ui` supplies the
components on the `@hanzo/gui` runtime, and `@hanzo/ai` is the only thing that
speaks to a server.
A chat client, and the package the desktop is built from. Vite and React draw
it, `@hanzo/ui` supplies the components on the `@hanzo/gui` runtime, and
`@hanzo/ai` is the only thing that speaks to a server.
One bundle serves three products. `src/brand.ts` states a record per brand — its
organization, issuer, name, mark, and the hosts it answers on — and `wear()`
@@ -13,9 +13,13 @@ no name for the product comes from the server.
## The wire
There is no HTTP client here. `src/data/ai.ts` builds one `createAiClient` and
that client owns the base URL, the bearer, the stale-token retry and the SSE
decode, so no path to the API appears in this repository at all.
There is no HTTP client here. `src/data/origin.ts` builds one `createAiClient`
per origin and that client owns the base URL, the bearer, the stale-token retry
and the SSE decode, so no path to the API appears in this repository at all. A
model's address is `origin/model`, so choosing a model chooses where it runs —
the estate under the visitor's IAM token, or an engine on this machine under the
key the shell started it with. Nothing downstream of the picker knows there is
more than one place.
| what | call | route |
| --- | --- | --- |
@@ -61,13 +65,49 @@ component props resolved by the gui config that `<Hanzo>` mounts, which is why
one tree renders on web, native and desktop and why the type scale moves for the
whole product from one place.
## Building on it
The package publishes its modules as built JavaScript beside their declarations,
so anything under `src` is reachable by its own path and a consumer compiles
nothing:
```tsx
import { Thread } from '@hanzo/chat/thread/Thread'
import { Compose } from '@hanzo/chat/compose/Compose'
import { client, ESTATE } from '@hanzo/chat/data/origin'
```
`@hanzo/vite` states the resolution the runtime needs — react-native-web, the
web-first extension order, one copy of gui — so a consumer's config is that call
and its own plugins:
```ts
import { hanzo } from '@hanzo/vite'
import react from '@vitejs/plugin-react'
const config = hanzo(
{ plugins: [react()], define: { 'process.env': '{}' } },
{ root: import.meta.dirname },
)
export default {
...config,
optimizeDeps: {
rollupOptions: { resolve: { extensions: (config.resolve as { extensions: string[] }).extensions } },
},
}
```
`@hanzo/desktop` is this package plus what only a machine has, so a desktop is a
fork of that rather than a second copy of this.
## Verifying
```sh
CI=true pnpm install --no-frozen-lockfile
pnpm dev # localhost:3090, /v1 proxied so calls stay same-origin
pnpm typecheck
pnpm test # 22 assertions, node --test, no framework
pnpm test # 31 assertions, node --test, no framework
pnpm build
```
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hanzo/chat",
"version": "0.1.0",
"version": "1.0.6",
"type": "module",
"license": "MIT",
"description": "Hanzo Chat \u2014 the client, on @hanzo/gui + @hanzo/ui.",
+3 -3
View File
@@ -14,9 +14,9 @@ import { Bot, Save, X } from '@hanzogui/lucide-icons-2'
import { SizableText, XStack, YStack } from '@hanzo/ui'
import { useState, type FormEvent } from 'react'
import { useModels } from '../data/config'
import { ESTATE } from '../data/origin'
import { create, useTools } from './store'
import { useModels } from '../data/config.ts'
import { ESTATE } from '../data/origin.ts'
import { create, useTools } from './store.ts'
export interface AgentBuilderProps {
isOpen: boolean
+1 -1
View File
@@ -15,7 +15,7 @@ import { Loader, Play, Plus, Trash2, X } from '@hanzogui/lucide-icons-2'
import { useState } from 'react'
import type { Agent } from '@hanzo/ai'
import { none, remove, run, swarmStore, unread, useAgent, useAgents, useSwarm } from './store'
import { none, remove, run, swarmStore, unread, useAgent, useAgents, useSwarm } from './store.ts'
export interface AgentsAndAppsModalProps {
onOpenAgentBuilder?: () => void
+2 -2
View File
@@ -11,8 +11,8 @@ import { SizableText, XStack, YStack } from '@hanzo/ui'
import { useEffect, useRef, useState } from 'react'
import type { Agent } from '@hanzo/ai'
import { useMultiplayer } from '../presence/store'
import { none, swarmStore, unread, useAgents, useSwarm } from './store'
import { useMultiplayer } from '../presence/store.ts'
import { none, swarmStore, unread, useAgents, useSwarm } from './store.ts'
/** An agent's face: the glyph somebody picked, or its initial. */
const face = (agent: Agent) => agent.emoji || agent.name.charAt(0).toUpperCase()
+4 -4
View File
@@ -14,10 +14,10 @@
import { APIError, type Agent, type AgentCreateParams, type AgentDetail, type AgentRun } from '@hanzo/ai'
import { useEffect, useState } from 'react'
import { client, ESTATE } from '../data/origin'
import type { Key } from '../data/keys'
import { invalidate, useRead } from '../data/query'
import { useSession } from '../data/session'
import { client, ESTATE } from '../data/origin.ts'
import type { Key } from '../data/keys.ts'
import { invalidate, useRead } from '../data/query.ts'
import { useSession } from '../data/session.tsx'
/**
* What a reader is told when the server answers nothing, or does not answer.
+5 -5
View File
@@ -1,11 +1,11 @@
import { Toaster, TooltipProvider } from '@hanzo/ui'
import { RouterProvider } from 'react-router/dom'
import { Session } from './data/session'
import { shell } from './data/shell'
import { router } from './routes'
import { consent, usePref } from './settings/prefs'
import { Welcome } from './shell/Welcome'
import { Session } from './data/session.tsx'
import { shell } from './data/shell.ts'
import { router } from './routes.tsx'
import { consent, usePref } from './settings/prefs.ts'
import { Welcome } from './shell/Welcome.tsx'
/** How long a hint waits before it appears, everywhere. ONE provider, so the
* product cannot have two hover speeds depending on which subtree the pointer
+2 -2
View File
@@ -24,8 +24,8 @@ import {
import { SizableText, XStack, YStack } from '@hanzo/ui'
import { useEffect, useRef, useState, type CSSProperties } from 'react'
import { useLease, type Line } from '../terminal/sandbox'
import { artifactStore, previewable, runner, useArtifact, type ArtifactTab } from './store'
import { useLease, type Line } from '../terminal/sandbox.ts'
import { artifactStore, previewable, runner, useArtifact, type ArtifactTab } from './store.ts'
const TAB = (active: boolean): CSSProperties => ({
display: 'inline-flex',
+1 -1
View File
@@ -10,7 +10,7 @@
* An artifact opens EMPTY. There is no code here until a fenced block or the
* palette puts some here.
*/
import { sandbox } from '../terminal/sandbox'
import { sandbox } from '../terminal/sandbox.ts'
import { useEffect, useState } from 'react'
+3 -3
View File
@@ -9,10 +9,10 @@
import { Clock, Play, Plus, X, Zap } from '@hanzogui/lucide-icons-2'
import { useState, type FormEvent } from 'react'
import { useSession } from '../data/session'
import { useSession } from '../data/session.tsx'
import { add, arm, start, useFlows, useRuns } from './auto'
import { automationsStore, useAutomations } from './store'
import { add, arm, start, useFlows, useRuns } from './auto.ts'
import { automationsStore, useAutomations } from './store.ts'
const clock = (at?: number) => (at ? new Date(at).toLocaleString() : '')
+2 -2
View File
@@ -16,8 +16,8 @@
* Nothing publishes a next firing time or a lifetime run total, so neither is
* read. The last run is the head of `/v1/auto/runs`, which is newest first.
*/
import { client, ESTATE } from '../data/origin'
import { invalidate, useRead } from '../data/query'
import { client, ESTATE } from '../data/origin.ts'
import { invalidate, useRead } from '../data/query.ts'
/** A flow row joined to its own record. Unix milliseconds on both clocks. */
export type Flow = {
+1 -1
View File
@@ -4,7 +4,7 @@
* The flows and their runs are the server's — `auto.ts` reads them — so the
* panel holds only whether it is open and which row is expanded.
*/
import { atom, useAtom } from '../data/store'
import { atom, useAtom } from '../data/store.ts'
export type Panel = {
isOpen: boolean
+3 -3
View File
@@ -10,9 +10,9 @@
import { ArrowLeft, ArrowRight, Hand, Kanban, Plus, Search, X } from '@hanzogui/lucide-icons-2'
import { useState, type FormEvent } from 'react'
import { useSession } from '../data/session'
import { useSession } from '../data/session.tsx'
import { boardStore, useBoard } from './store'
import { boardStore, useBoard } from './store.ts'
import {
COLUMNS,
PRIORITIES,
@@ -25,7 +25,7 @@ import {
type Column,
type Issue,
type Priority,
} from './todo'
} from './todo.ts'
const columnLook: Record<Column, { title: string; color: string }> = {
backlog: { title: 'Backlog', color: 'rgba(255, 255, 255, 0.4)' },
+1 -1
View File
@@ -5,7 +5,7 @@
* board, what was typed into the search box, and which card is open. None of
* that outlives the tab, which is why none of it is persisted.
*/
import { atom, useAtom } from '../data/store'
import { atom, useAtom } from '../data/store.ts'
export type View = {
isOpen: boolean
+2 -2
View File
@@ -19,8 +19,8 @@
* purpose: a board IS a repository on the forge, and creating one is a forge
* act with forge permissions.
*/
import { client, ESTATE } from '../data/origin'
import { invalidate, useRead } from '../data/query'
import { client, ESTATE } from '../data/origin.ts'
import { invalidate, useRead } from '../data/query.ts'
/** The five columns the server recognises. An unknown value is refused with 400. */
export const COLUMNS = ['backlog', 'todo', 'in_progress', 'done', 'canceled'] as const
+1 -1
View File
@@ -20,7 +20,7 @@
* Everything a brand can differ by is HERE, because split across two homes it
* gets half-applied and every brand ends up wearing the first one's name.
*/
import { shell } from './data/shell'
import { shell } from './data/shell.ts'
export type Brand = {
/** IAM organization, lowercase. The OAuth client is `<org>-<app>`. */
+2 -2
View File
@@ -13,8 +13,8 @@ import { SizableText, XStack, YStack } from '@hanzo/ui'
import { X } from '@hanzogui/lucide-icons-2'
import { useEffect } from 'react'
import { why } from '../data/missing'
import { channelsStore, refusal, speaker, useChannels, useTransports, when } from './store'
import { why } from '../data/missing.ts'
import { channelsStore, refusal, speaker, useChannels, useTransports, when } from './store.ts'
const Note = ({ children }: { children: string }) => (
<SizableText size="$1" style={{ fontSize: 12, color: 'rgba(255, 255, 255, 0.45)' }}>
+2 -2
View File
@@ -14,8 +14,8 @@
import { SizableText, XStack, YStack } from '@hanzo/ui'
import { Compass, Hash, MessageSquare } from '@hanzogui/lucide-icons-2'
import { why } from '../data/missing'
import { channelsStore, refusal, useChannels, when } from './store'
import { why } from '../data/missing.ts'
import { channelsStore, refusal, useChannels, when } from './store.ts'
const Note = ({ children }: { children: string }) => (
<SizableText
+6 -6
View File
@@ -21,12 +21,12 @@
*/
import { APIError, rooms as group, type Channel, type Room } from '@hanzo/ai'
import { client, ESTATE } from '../data/origin'
import type { Key } from '../data/keys'
import { peek, useRead, type Read } from '../data/query'
import { useSession } from '../data/session'
import { atom, useAtom } from '../data/store'
import { explain } from '../data/types'
import { client, ESTATE } from '../data/origin.ts'
import type { Key } from '../data/keys.ts'
import { peek, useRead, type Read } from '../data/query.ts'
import { useSession } from '../data/session.tsx'
import { atom, useAtom } from '../data/store.ts'
import { explain } from '../data/types.ts'
/** Two cache keys under one prefix, so both drop together. */
const TRANSPORTS: Key = ['channels', 'transports']
+5 -5
View File
@@ -6,11 +6,11 @@ import { ArrowUp, Mic, MicOff, Paperclip, Square, X } from '@hanzogui/lucide-ico
import { SizableText, XStack, YStack } from '@hanzo/ui'
import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react'
import { Starters, type Starter } from './Starters'
import { useDraft } from './draft'
import type { Handoff } from './link'
import { payload, type Conversation, type Draft, type Payload } from './submit'
import type { Attachment } from '../data/types'
import { Starters, type Starter } from './Starters.tsx'
import { useDraft } from './draft.ts'
import type { Handoff } from './link.ts'
import { payload, type Conversation, type Draft, type Payload } from './submit.ts'
import type { Attachment } from '../data/types.ts'
const COLUMN = 768
const MAX_TEXTAREA_HEIGHT = 220
+2 -2
View File
@@ -3,8 +3,8 @@
*/
import { useCallback, useEffect, useRef, useState } from 'react'
import { blank, type Draft } from './submit'
import type { Attachment } from '../data/types'
import { blank, type Draft } from './submit.ts'
import type { Attachment } from '../data/types.ts'
export const NEW = 'new'
+2 -2
View File
@@ -4,8 +4,8 @@
* One verb, streaming decoded `chat.completion.chunk` values from @hanzo/ai.
*/
import type { ChatCompletionChunk, ChatCompletionMessage } from '@hanzo/ai'
import { client, ESTATE } from '../data/origin'
import { explain } from '../data/types'
import { client, ESTATE } from '../data/origin.ts'
import { explain } from '../data/types.ts'
/** How a turn ended. The shell answers each differently, so it is told which. */
export type Ended =
+1 -1
View File
@@ -6,7 +6,7 @@
*/
import type { ChatCompletionContentPart, ChatCompletionMessage } from '@hanzo/ai'
import type { Attachment, Message } from '../data/types'
import type { Attachment, Message } from '../data/types.ts'
/**
* What the composer holds.
+1 -1
View File
@@ -16,7 +16,7 @@ import { test } from 'node:test'
import { createAiClient, type ChatCompletionChunk } from '@hanzo/ai'
import type { Message } from '../data/types'
import type { Message } from '../data/types.ts'
import { fold, opening, parts, spoken } from './frames.ts'
import { history } from './submit.ts'
+1 -1
View File
@@ -11,7 +11,7 @@
* go, and where this app's own doorway is. Both are navigations rather than
* requests — the browser leaves — so neither can be a method call.
*/
import { brand, clientId } from '../brand'
import { brand, clientId } from '../brand.ts'
export const api = {
/**
+4 -4
View File
@@ -16,10 +16,10 @@
* always was; what a guest may do is decided server-side from the absence of a
* bearer, which is where it belongs and where it was already decided.
*/
import { keys } from './keys'
import { client, origins } from './origin'
import { useRead } from './query'
import type { Served } from './types'
import { keys } from './keys.ts'
import { client, origins } from './origin.ts'
import { useRead } from './query.ts'
import type { Served } from './types.ts'
/** Who publishes a model, when it says. The menu's fallback group. */
const OTHER = 'other'
+4 -4
View File
@@ -12,10 +12,10 @@
* There is no route to write to and no SDK method to call, so nothing here
* pretends otherwise — `~/data/missing` is what the rail's controls report.
*/
import { client, ESTATE } from './origin'
import { keys } from './keys'
import { useRead } from './query'
import type { Convo } from './types'
import { client, ESTATE } from './origin.ts'
import { keys } from './keys.ts'
import { useRead } from './query.ts'
import type { Convo } from './types.ts'
/**
* Every conversation, newest first.
+1 -1
View File
@@ -7,7 +7,7 @@
* What a guest may do is decided there, from the absence of a bearer — which is
* also why becoming one cannot fail and asks nobody's permission.
*/
import type { User } from './types'
import type { User } from './types.ts'
export const GUEST = 'guest'
+2 -2
View File
@@ -11,8 +11,8 @@
*/
import { IAM } from '@hanzo/iam'
import { brand, clientId } from '../brand'
import { callbackPath, loginPath } from './api'
import { brand, clientId } from '../brand.ts'
import { callbackPath, loginPath } from './api.ts'
/**
* What a session asks for.
+4 -4
View File
@@ -16,10 +16,10 @@
* 200 with an empty transcript for another tenant's id, so the SDK reads empty
* as absent. An empty list is passed straight through as an empty conversation.
*/
import { client } from './origin'
import { keys } from './keys'
import { useRead } from './query'
import type { Message } from './types'
import { client } from './origin.ts'
import { keys } from './keys.ts'
import { useRead } from './query.ts'
import type { Message } from './types.ts'
/**
* Every turn of one conversation, oldest first.
+4 -4
View File
@@ -16,10 +16,10 @@
*/
import { createAiClient, type AiClient } from '@hanzo/ai'
import { brand } from '../brand'
import { iam } from './iam'
import { shell } from './shell'
import { key } from '../settings/prefs'
import { brand } from '../brand.ts'
import { iam } from './iam.ts'
import { shell } from './shell.ts'
import { key } from '../settings/prefs.ts'
/** The estate. Its base URL is the SDK's default and is not restated. */
export const ESTATE = 'hanzo'
+1 -1
View File
@@ -1,7 +1,7 @@
/**
* Ask the issuer, once per visit, whether this browser is already signed in.
*/
import { keepHere } from './back'
import { keepHere } from './back.ts'
const SLOT = 'chat.probed'
+1 -1
View File
@@ -25,7 +25,7 @@
*/
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react'
import type { Key } from './keys'
import type { Key } from './keys.ts'
/** A separator no key part contains, so a prefix match is a real prefix. */
const SEP = ''
+10 -10
View File
@@ -33,17 +33,17 @@ import {
type ReactNode,
} from 'react'
import { client } from './origin'
import { api } from './api'
import { keepHere, take } from './back'
import { guest } from './guest'
import { iam } from './iam'
import { client } from './origin.ts'
import { api } from './api.ts'
import { keepHere, take } from './back.ts'
import { guest } from './guest.ts'
import { iam } from './iam.ts'
import { HeldError } from '@hanzo/ai'
import { keys } from './keys'
import { exchanging, noSession, probe } from './probe'
import { invalidate, useRead } from './query'
import { shell, visit } from './shell'
import type { Standing, User } from './types'
import { keys } from './keys.ts'
import { exchanging, noSession, probe } from './probe.ts'
import { invalidate, useRead } from './query.ts'
import { shell, visit } from './shell.ts'
import type { Standing, User } from './types.ts'
export type Session = {
+1 -1
View File
@@ -19,7 +19,7 @@
*/
import { useSyncExternalStore } from 'react'
import type { Convo, Failure, Message } from './types'
import type { Convo, Failure, Message } from './types.ts'
export type Atom<T> = {
/** The value, right now. */
+1 -1
View File
@@ -1,4 +1,4 @@
import { brand } from '../brand'
import { brand } from '../brand.ts'
/**
* What the wire carries, and what a refusal means to a reader.
+1 -1
View File
@@ -22,7 +22,7 @@
* NOT fall back to is a store that answers every call and forgets on exit,
* which reads as a working keychain until the restart that loses the session.
*/
import { call, shell } from './shell'
import { call, shell } from './shell.ts'
/** One entry, holding the whole session: the tokens travel together. */
const NAME = 'session'
+2 -2
View File
@@ -10,8 +10,8 @@ import {
} from '@hanzogui/lucide-icons-2'
import { SizableText, XStack, YStack } from '@hanzo/ui'
import { useState, type FormEvent } from 'react'
import { useShown } from '../show'
import { intelligenceStore, useForget, useIntelligence, useRemember } from './store'
import { useShown } from '../show.ts'
import { intelligenceStore, useForget, useIntelligence, useRemember } from './store.ts'
export const IntelligenceModal = () => {
const { isOpen, memories, symbols, query, activeTab, pending, fault, degraded } =
+4 -4
View File
@@ -14,10 +14,10 @@
* every keystroke sent a request and minted a cache entry per character, and
* every one but the last was a question already finished being asked.
*/
import { atom, useAtom } from '../data/store'
import { keys } from '../data/keys'
import { client, ESTATE } from '../data/origin'
import { reason, useRead, useSend, useSettled } from '../data/query'
import { atom, useAtom } from '../data/store.ts'
import { keys } from '../data/keys.ts'
import { client, ESTATE } from '../data/origin.ts'
import { reason, useRead, useSend, useSettled } from '../data/query.ts'
export interface MemoryItem {
/** The memory's name in the store — how it is addressed. */
+3 -3
View File
@@ -7,9 +7,9 @@ import '@hanzo/font/css'
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { App } from './app'
import { brand, wear } from './brand'
import { unlock } from './data/vault'
import { App } from './app.tsx'
import { brand, wear } from './brand.ts'
import { unlock } from './data/vault.ts'
/**
* The mount. Three lines, and the second one is the whole design system.
+4 -4
View File
@@ -19,10 +19,10 @@ import { Blocks, Check, Plus, Power, Search, Trash2, Wrench, X } from '@hanzogui
import { SizableText, XStack, YStack } from '@hanzo/ui'
import { useEffect, useState } from 'react'
import { requireLogin } from '../data/gate'
import { useSession } from '../data/session'
import { mcpStore, useCatalog, useMcp } from './store'
import type { McpServer } from './types'
import { requireLogin } from '../data/gate.ts'
import { useSession } from '../data/session.tsx'
import { mcpStore, useCatalog, useMcp } from './store.ts'
import type { McpServer } from './types.ts'
const dim = 'rgba(255, 255, 255, 0.5)'
const faint = 'rgba(255, 255, 255, 0.4)'
+5 -5
View File
@@ -22,11 +22,11 @@
*/
import { useSyncExternalStore } from 'react'
import { client, ESTATE } from '../data/origin'
import { keys, sign, type Key } from '../data/keys'
import { invalidate, peek, useRead } from '../data/query'
import { useSession } from '../data/session'
import type { McpServer } from './types'
import { client, ESTATE } from '../data/origin.ts'
import { keys, sign, type Key } from '../data/keys.ts'
import { invalidate, peek, useRead } from '../data/query.ts'
import { useSession } from '../data/session.tsx'
import type { McpServer } from './types.ts'
const ACTIVATION = '/v1/tools/activation'
const SERVERS = '/v1/tools/mcp/servers'
+3 -3
View File
@@ -10,9 +10,9 @@
import { Blocks, Search, X } from '@hanzogui/lucide-icons-2'
import { useState } from 'react'
import { requireLogin } from '../data/gate'
import { useSession } from '../data/session'
import { pluginsStore, usePlugins } from './store'
import { requireLogin } from '../data/gate.ts'
import { useSession } from '../data/session.tsx'
import { pluginsStore, usePlugins } from './store.ts'
const dim = 'rgba(255, 255, 255, 0.5)'
const faint = 'rgba(255, 255, 255, 0.4)'
+4 -4
View File
@@ -15,10 +15,10 @@
import { useSyncExternalStore } from 'react'
import type { PluginMount } from '@hanzo/ai'
import { client, ESTATE } from '../data/origin'
import type { Key } from '../data/keys'
import { useRead } from '../data/query'
import { useSession } from '../data/session'
import { client, ESTATE } from '../data/origin.ts'
import type { Key } from '../data/keys.ts'
import { useRead } from '../data/query.ts'
import { useSession } from '../data/session.tsx'
const key: Key = ['plugins']
+2 -2
View File
@@ -16,8 +16,8 @@ import { APIError } from '@hanzo/ai'
import { Users, X } from '@hanzogui/lucide-icons-2'
import { SizableText, XStack, YStack } from '@hanzo/ui'
import { initial, label, tint } from './person'
import { multiplayerStore, useMultiplayer } from './store'
import { initial, label, tint } from './person.ts'
import { multiplayerStore, useMultiplayer } from './store.ts'
/** What an empty roster means. The refusal is the contract's own sentence. */
const nothing = (error: unknown, pending: boolean): string => {
+3 -3
View File
@@ -12,9 +12,9 @@
import { Terminal, Users } from '@hanzogui/lucide-icons-2'
import { XStack } from '@hanzo/ui'
import { initial, label, tint } from './person'
import { multiplayerStore, useMultiplayer } from './store'
import { terminalStore, useTerminal } from '../terminal/store'
import { initial, label, tint } from './person.ts'
import { multiplayerStore, useMultiplayer } from './store.ts'
import { terminalStore, useTerminal } from '../terminal/store.ts'
export const PresenceStack = () => {
const { participants } = useMultiplayer()
+3 -3
View File
@@ -25,9 +25,9 @@
import { useCallback, useSyncExternalStore } from 'react'
import type { Person } from '@hanzo/ai'
import { client, ESTATE } from '../data/origin'
import { useRead } from '../data/query'
import { useSession } from '../data/session'
import { client, ESTATE } from '../data/origin.ts'
import { useRead } from '../data/query.ts'
import { useSession } from '../data/session.tsx'
export type MultiplayerState = {
/** Whether the roster panel is open. */
+3 -3
View File
@@ -8,10 +8,10 @@
import { Building2, FolderGit2, Plus, Star, UserCheck, X } from '@hanzogui/lucide-icons-2'
import { useState, type FormEvent } from 'react'
import { useSession } from '../data/session'
import { useSession } from '../data/session.tsx'
import { create, star, useProjects, usePeople } from './projects'
import { projectsStore, useProjectPanel } from './store'
import { create, star, useProjects, usePeople } from './projects.ts'
import { projectsStore, useProjectPanel } from './store.ts'
const field = {
padding: '8px 12px',
+2 -2
View File
@@ -13,8 +13,8 @@
*/
import type { Person } from '@hanzo/ai'
import { client, ESTATE } from '../data/origin'
import { invalidate, useRead } from '../data/query'
import { client, ESTATE } from '../data/origin.ts'
import { invalidate, useRead } from '../data/query.ts'
/** A project row, as `/v1/projects` answers it. `slug` is what addresses it. */
export type Project = {
+1 -1
View File
@@ -5,7 +5,7 @@
* and the org is the principal's, so the only thing left to hold is whether
* this panel is open and which of its two lists is in front.
*/
import { atom, useAtom } from '../data/store'
import { atom, useAtom } from '../data/store.ts'
export type Pane = 'projects' | 'people'
+1 -1
View File
@@ -2,7 +2,7 @@
* The bottom user menu popover in the rail with Linear.app style liquid-glass aesthetics,
* org switcher, account controls, and appearance options.
*/
import { api } from '../data/api'
import { api } from '../data/api.ts'
import {
ChevronsUpDown,
ExternalLink,
+3 -3
View File
@@ -2,10 +2,10 @@ import { SizableText, YStack } from '@hanzo/ui'
import { SidebarScroll, SidebarSection } from '@hanzo/ui/chat'
import { useMemo } from 'react'
import type { Convo } from '../data/types'
import type { Convo } from '../data/types.ts'
import { hits, id } from './group'
import { Item } from './Item'
import { hits, id } from './group.ts'
import { Item } from './Item.tsx'
/**
* What finding one found.
+2 -2
View File
@@ -1,9 +1,9 @@
import { Spinner } from '@hanzo/ui'
import { SidebarItem } from '@hanzo/ui/chat'
import type { Convo } from '../data/types'
import type { Convo } from '../data/types.ts'
import { named } from './group'
import { named } from './group.ts'
/**
* ONE conversation, as a row.
+3 -3
View File
@@ -2,10 +2,10 @@ import { Paragraph, SizableText, Spinner, XStack } from '@hanzo/ui'
import { SidebarScroll, SidebarSection } from '@hanzo/ui/chat'
import { useEffect, useMemo, useRef } from 'react'
import type { Convo } from '../data/types'
import type { Convo } from '../data/types.ts'
import { group, id } from './group'
import { Item } from './Item'
import { group, id } from './group.ts'
import { Item } from './Item.tsx'
/**
* The set, as the rail draws it: banded by when, newest first, pinned above.
+9 -9
View File
@@ -5,15 +5,15 @@ import { HanzoMark } from '@hanzo/ui/product'
import { Blocks, Kanban, LayoutGrid, ListTodo } from '@hanzogui/lucide-icons-2'
import { useRef, useState, type ReactNode } from 'react'
import { boardStore } from '../boards/store'
import { ChannelSection } from '../channels/ChannelSection'
import { mcpStore } from '../mcp/store'
import { swarmStore } from '../agents/store'
import { taskQueueStore } from '../tasks/store'
import type { Convo } from '../data/types'
import { Find } from './Find'
import { Hits } from './Hits'
import { List } from './List'
import { boardStore } from '../boards/store.ts'
import { ChannelSection } from '../channels/ChannelSection.tsx'
import { mcpStore } from '../mcp/store.ts'
import { swarmStore } from '../agents/store.ts'
import { taskQueueStore } from '../tasks/store.ts'
import type { Convo } from '../data/types.ts'
import { Find } from './Find.tsx'
import { Hits } from './Hits.tsx'
import { List } from './List.tsx'
export const OPEN = 270
export const RAIL = 56
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Convo } from '../data/types'
import type { Convo } from '../data/types.ts'
/**
* The SET, ordered and named — the only rules about a list of conversations
+5 -5
View File
@@ -1,10 +1,10 @@
import { createBrowserRouter, Navigate } from 'react-router'
import { Boundary } from './shell/Boundary'
import { Callback } from './shell/Callback'
import { Chat } from './shell/Chat'
import { Login } from './shell/Login'
import { Root } from './shell/Root'
import { Boundary } from './shell/Boundary.tsx'
import { Callback } from './shell/Callback.tsx'
import { Chat } from './shell/Chat.tsx'
import { Login } from './shell/Login.tsx'
import { Root } from './shell/Root.tsx'
/**
* Where the app is mounted, from the document that served it.
+2 -2
View File
@@ -14,8 +14,8 @@ import { row, rows } from '@hanzo/ui/glass'
import { ConfirmDelete, DialogTemplate } from '@hanzo/ui/product'
import { useState } from 'react'
import { api } from '../data/api'
import type { User } from '../data/types'
import { api } from '../data/api.ts'
import type { User } from '../data/types.ts'
export interface AccountProps {
+3 -3
View File
@@ -1,9 +1,9 @@
import { Paragraph, SizableText, Switch, XStack, YStack } from '@hanzo/ui'
import { row, rows } from '@hanzo/ui/glass'
import { Model } from './Model'
import type { Served } from '../data/types'
import { model, steps, temporary, usePref, type Pref } from './prefs'
import { Model } from './Model.tsx'
import type { Served } from '../data/types.ts'
import { model, steps, temporary, usePref, type Pref } from './prefs.ts'
/**
* One preference, as a line: the name, what it does under it, the switch hard
+2 -2
View File
@@ -1,8 +1,8 @@
import { Paragraph, YStack } from '@hanzo/ui'
import { rows } from '@hanzo/ui/glass'
import { Toggle } from './General'
import { compact, full } from './prefs'
import { Toggle } from './General.tsx'
import { compact, full } from './prefs.ts'
/**
* How the conversation reads.
+1 -1
View File
@@ -4,7 +4,7 @@
import { Blocks, ExternalLink, Power } from '@hanzogui/lucide-icons-2'
import { Paragraph, SizableText, XStack, YStack } from '@hanzo/ui'
import { row, rows } from '@hanzo/ui/glass'
import { mcpStore, useMcp } from '../mcp/store'
import { mcpStore, useMcp } from '../mcp/store.ts'
export function McpSettings() {
const { servers } = useMcp()
+3 -3
View File
@@ -1,9 +1,9 @@
import { ModelSelector } from '@hanzo/ui/models'
import { useMemo } from 'react'
import { origins } from '../data/origin'
import type { Served } from '../data/types'
import { catalog } from './models'
import { origins } from '../data/origin.ts'
import type { Served } from '../data/types.ts'
import { catalog } from './models.ts'
/**
* Which model answers — one control, every surface that asks.
+6 -6
View File
@@ -16,12 +16,12 @@ import {
import { selected } from '@hanzo/ui/glass'
import { useState, type ReactNode } from 'react'
import { Account, type AccountProps } from './Account'
import { AiSettings } from './AiSettings'
import { General } from './General'
import { Look } from './Look'
import { McpSettings } from './McpSettings'
import type { Served } from '../data/types'
import { Account, type AccountProps } from './Account.tsx'
import { AiSettings } from './AiSettings.tsx'
import { General } from './General.tsx'
import { Look } from './Look.tsx'
import { McpSettings } from './McpSettings.tsx'
import type { Served } from '../data/types.ts'
export interface SettingsProps {
open: boolean
+2 -2
View File
@@ -1,7 +1,7 @@
import type { ModelCatalogEntry } from '@hanzo/ui/models'
import type { Origin } from '../data/origin'
import type { Served } from '../data/types'
import type { Origin } from '../data/origin.ts'
import type { Served } from '../data/types.ts'
/**
* What can be asked, as ONE list a picker can read.
+1 -1
View File
@@ -1,4 +1,4 @@
import { atom, useAtom, type Atom } from '../data/store'
import { atom, useAtom, type Atom } from '../data/store.ts'
/**
* Every preference that outlives the tab, and the only code in this app that
+3 -3
View File
@@ -1,9 +1,9 @@
import { useEffect, useRef } from 'react'
import { useNavigate } from 'react-router'
import { loginPath } from '../data/api'
import { useSession } from '../data/session'
import { Waiting } from './Boundary'
import { loginPath } from '../data/api.ts'
import { useSession } from '../data/session.tsx'
import { Waiting } from './Boundary.tsx'
/**
* Where a sign-in lands on the way back from the issuer.
+32 -32
View File
@@ -2,38 +2,38 @@ import { Fill, H2, Paragraph, XStack, YStack } from '@hanzo/ui'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useLocation, useNavigate, useParams } from 'react-router'
import { SwarmBar } from '../agents/SwarmBar'
import { ArtifactPanel } from '../artifact/ArtifactPanel'
import { artifactStore } from '../artifact/store'
import { brand } from '../brand'
import { Compose } from '../compose/Compose'
import { faulted, fold, opening, parts, spoken, stopped, type Part as Piece, type Reply } from '../compose/frames'
import { useHandoff } from '../compose/link'
import { run } from '../compose/stream'
import { history, type Conversation, type Payload } from '../compose/submit'
import { useChannels } from '../channels/store'
import { client, ESTATE } from '../data/origin'
import { useModels } from '../data/config'
import { useConvo } from '../data/convos'
import { requireLogin } from '../data/gate'
import { keys } from '../data/keys'
import { useTurns } from '../data/messages'
import { invalidate } from '../data/query'
import * as store from '../data/store'
import type { Message, Part } from '../data/types'
import { InviteModal } from '../presence/InviteModal'
import { Model } from '../settings/Model'
import { pick } from '../settings/models'
import { model as preferred, usePref } from '../settings/prefs'
import { Header } from './Header'
import { useFrame } from './Root'
import { useTitle } from './title'
import { TerminalPanel } from '../terminal/TerminalPanel'
import { BrowseChannelsModal } from '../channels/BrowseChannelsModal'
import { ShortcutsModal } from '../shortcuts/ShortcutsModal'
import { ThemeModal } from '../theme/ThemeModal'
import { ExportModal } from '../thread/ExportModal'
import { Thread } from '../thread/Thread'
import { SwarmBar } from '../agents/SwarmBar.tsx'
import { ArtifactPanel } from '../artifact/ArtifactPanel.tsx'
import { artifactStore } from '../artifact/store.ts'
import { brand } from '../brand.ts'
import { Compose } from '../compose/Compose.tsx'
import { faulted, fold, opening, parts, spoken, stopped, type Part as Piece, type Reply } from '../compose/frames.ts'
import { useHandoff } from '../compose/link.ts'
import { run } from '../compose/stream.ts'
import { history, type Conversation, type Payload } from '../compose/submit.ts'
import { useChannels } from '../channels/store.ts'
import { client, ESTATE } from '../data/origin.ts'
import { useModels } from '../data/config.ts'
import { useConvo } from '../data/convos.ts'
import { requireLogin } from '../data/gate.ts'
import { keys } from '../data/keys.ts'
import { useTurns } from '../data/messages.ts'
import { invalidate } from '../data/query.ts'
import * as store from '../data/store.ts'
import type { Message, Part } from '../data/types.ts'
import { InviteModal } from '../presence/InviteModal.tsx'
import { Model } from '../settings/Model.tsx'
import { pick } from '../settings/models.ts'
import { model as preferred, usePref } from '../settings/prefs.ts'
import { Header } from './Header.tsx'
import { useFrame } from './Root.tsx'
import { useTitle } from './title.ts'
import { TerminalPanel } from '../terminal/TerminalPanel.tsx'
import { BrowseChannelsModal } from '../channels/BrowseChannelsModal.tsx'
import { ShortcutsModal } from '../shortcuts/ShortcutsModal.tsx'
import { ThemeModal } from '../theme/ThemeModal.tsx'
import { ExportModal } from '../thread/ExportModal.tsx'
import { Thread } from '../thread/Thread.tsx'
/**
* One piece of a reply, in the wire's vocabulary.
+4 -4
View File
@@ -12,10 +12,10 @@ import {
} from '@hanzo/ui'
import { useEffect, useState } from 'react'
import { api } from '../data/api'
import { takePending, watchLogin, type Reason } from '../data/gate'
import { MACHINE, origins } from '../data/origin'
import { useSession } from '../data/session'
import { api } from '../data/api.ts'
import { takePending, watchLogin, type Reason } from '../data/gate.ts'
import { MACHINE, origins } from '../data/origin.ts'
import { useSession } from '../data/session.tsx'
/**
* Where somebody ready to pay goes. No price is repeated here: the plans page
+6 -6
View File
@@ -14,12 +14,12 @@ import {
import { XStack } from '@hanzo/ui'
import { useEffect, useState } from 'react'
import { PresenceStack } from '../presence/PresenceStack'
import { artifactStore, useArtifact } from '../artifact/store'
import { useChannels } from '../channels/store'
import { exportStore } from '../thread/exportStore'
import { shortcutsStore } from '../shortcuts/store'
import { themeCustomizerStore } from '../theme/store'
import { PresenceStack } from '../presence/PresenceStack.tsx'
import { artifactStore, useArtifact } from '../artifact/store.ts'
import { useChannels } from '../channels/store.ts'
import { exportStore } from '../thread/exportStore.ts'
import { shortcutsStore } from '../shortcuts/store.ts'
import { themeCustomizerStore } from '../theme/store.ts'
export interface HeaderProps {
title: string
+3 -3
View File
@@ -2,9 +2,9 @@ import { Button, H2, Paragraph, YStack } from '@hanzo/ui'
import { useEffect, useRef } from 'react'
import { Navigate, useSearchParams } from 'react-router'
import { brand } from '../brand'
import { useSession } from '../data/session'
import { Waiting } from './Boundary'
import { brand } from '../brand.ts'
import { useSession } from '../data/session.tsx'
import { Waiting } from './Boundary.tsx'
/**
* Where a refusal sends somebody, and it is a DOOR rather than a page.
+14 -14
View File
@@ -22,20 +22,20 @@ import {
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useNavigate } from 'react-router'
import { swarmStore } from '../agents/store'
import { artifactStore } from '../artifact/store'
import { automationsStore } from '../automations/store'
import { boardStore } from '../boards/store'
import { channelsStore } from '../channels/store'
import { api } from '../data/api'
import { useSession } from '../data/session'
import { intelligenceStore } from '../intelligence/store'
import { mcpStore } from '../mcp/store'
import { pluginsStore } from '../plugins/store'
import { multiplayerStore } from '../presence/store'
import { projectsStore } from '../projects/store'
import { taskQueueStore } from '../tasks/store'
import { terminalStore } from '../terminal/store'
import { swarmStore } from '../agents/store.ts'
import { artifactStore } from '../artifact/store.ts'
import { automationsStore } from '../automations/store.ts'
import { boardStore } from '../boards/store.ts'
import { channelsStore } from '../channels/store.ts'
import { api } from '../data/api.ts'
import { useSession } from '../data/session.tsx'
import { intelligenceStore } from '../intelligence/store.ts'
import { mcpStore } from '../mcp/store.ts'
import { pluginsStore } from '../plugins/store.ts'
import { multiplayerStore } from '../presence/store.ts'
import { projectsStore } from '../projects/store.ts'
import { taskQueueStore } from '../tasks/store.ts'
import { terminalStore } from '../terminal/store.ts'
export interface PaletteProps {
onSettings: () => void
+25 -25
View File
@@ -2,31 +2,31 @@ import { Screen, XStack, YStack } from '@hanzo/ui'
import { useCallback, useMemo, useState } from 'react'
import { Outlet, useMatch, useNavigate, useOutletContext } from 'react-router'
import { AgentBuilderModal } from '../agents/AgentBuilderModal'
import { AgentsAndAppsModal } from '../agents/AgentsAndAppsModal'
import { AutomationsModal } from '../automations/AutomationsModal'
import { BoardModal } from '../boards/BoardModal'
import { brand } from '../brand'
import { api } from '../data/api'
import { useModels } from '../data/config'
import { useConvos } from '../data/convos'
import { useSession } from '../data/session'
import type { Convo } from '../data/types'
import { useNarrow } from '../gui'
import { McpModal } from '../mcp/McpModal'
import { IntelligenceModal } from '../intelligence/IntelligenceModal'
import { PluginsModal } from '../plugins/PluginsModal'
import { ProjectModal } from '../projects/ProjectModal'
import { Account } from '../rail/Account'
import { id } from '../rail/group'
import { Rail } from '../rail/Rail'
import { Visitor } from '../rail/Visitor'
import { Settings } from '../settings/Settings'
import { Announce } from './Announce'
import { Gate } from './Gate'
import { Palette } from './Palette'
import { Scene } from './Scene'
import { TaskQueueModal } from '../tasks/TaskQueueModal'
import { AgentBuilderModal } from '../agents/AgentBuilderModal.tsx'
import { AgentsAndAppsModal } from '../agents/AgentsAndAppsModal.tsx'
import { AutomationsModal } from '../automations/AutomationsModal.tsx'
import { BoardModal } from '../boards/BoardModal.tsx'
import { brand } from '../brand.ts'
import { api } from '../data/api.ts'
import { useModels } from '../data/config.ts'
import { useConvos } from '../data/convos.ts'
import { useSession } from '../data/session.tsx'
import type { Convo } from '../data/types.ts'
import { useNarrow } from '../gui.ts'
import { McpModal } from '../mcp/McpModal.tsx'
import { IntelligenceModal } from '../intelligence/IntelligenceModal.tsx'
import { PluginsModal } from '../plugins/PluginsModal.tsx'
import { ProjectModal } from '../projects/ProjectModal.tsx'
import { Account } from '../rail/Account.tsx'
import { id } from '../rail/group.ts'
import { Rail } from '../rail/Rail.tsx'
import { Visitor } from '../rail/Visitor.tsx'
import { Settings } from '../settings/Settings.tsx'
import { Announce } from './Announce.tsx'
import { Gate } from './Gate.tsx'
import { Palette } from './Palette.tsx'
import { Scene } from './Scene.tsx'
import { TaskQueueModal } from '../tasks/TaskQueueModal.tsx'
/**
* What a screen inside the shell can reach.
+1 -1
View File
@@ -1,6 +1,6 @@
import { View } from '@hanzo/ui'
import { brand } from '../brand'
import { brand } from '../brand.ts'
/**
* The immersive ground the whole product floats on.
+3 -3
View File
@@ -1,9 +1,9 @@
import { Anchor, Button, Checkbox, H2, Image, Label, Paragraph, XStack, YStack } from '@hanzo/ui'
import { useState } from 'react'
import { brand } from '../brand'
import { useSession } from '../data/session'
import { consent, usePref } from '../settings/prefs'
import { brand } from '../brand.ts'
import { useSession } from '../data/session.tsx'
import { consent, usePref } from '../settings/prefs.ts'
/**
* What an installed app asks before it opens, and it asks once.
+1 -1
View File
@@ -12,7 +12,7 @@
*/
import { useEffect } from 'react'
import { brand } from '../brand'
import { brand } from '../brand.ts'
export const useTitle = (subject?: string | null) => {
useEffect(() => {
+1 -1
View File
@@ -1,6 +1,6 @@
import { useEffect } from 'react'
import { Keyboard, X } from '@hanzogui/lucide-icons-2'
import { shortcutsStore, useShortcuts } from './store'
import { shortcutsStore, useShortcuts } from './store.ts'
interface ShortcutItem {
keys: string[]
+3 -3
View File
@@ -11,10 +11,10 @@
import { ListTodo, Pause, Play, Square, X } from '@hanzogui/lucide-icons-2'
import { useState } from 'react'
import { useSession } from '../data/session'
import { useSession } from '../data/session.tsx'
import { steer, useRun, useRuns } from './runs'
import { taskQueueStore, useTaskQueue } from './store'
import { steer, useRun, useRuns } from './runs.ts'
import { taskQueueStore, useTaskQueue } from './store.ts'
import type { Session, SessionEvent, SessionStatus } from '@hanzo/ai'
const badge = (status: SessionStatus | undefined) => {
+2 -2
View File
@@ -18,8 +18,8 @@
*/
import type { Session, SessionCommand, SessionDetail } from '@hanzo/ai'
import { client, ESTATE } from '../data/origin'
import { invalidate, useRead } from '../data/query'
import { client, ESTATE } from '../data/origin.ts'
import { invalidate, useRead } from '../data/query.ts'
const runs = ['runs'] as const
const one = (id: string) => ['runs', 'one', id] as const
+1 -1
View File
@@ -6,7 +6,7 @@
* row is selected. Those are this browser's and nobody else's, so they are an
* atom rather than a cache entry.
*/
import { atom, useAtom } from '../data/store'
import { atom, useAtom } from '../data/store.ts'
export type Queue = {
isOpen: boolean
+2 -2
View File
@@ -11,8 +11,8 @@ import { SizableText, XStack, YStack } from '@hanzo/ui'
import type { SandboxRuntime } from '@hanzo/ai'
import { useEffect, useRef, useState, type CSSProperties, type FormEvent } from 'react'
import { sandbox, useLease, type Line } from './sandbox'
import { terminalStore, useTerminal } from './store'
import { sandbox, useLease, type Line } from './sandbox.ts'
import { terminalStore, useTerminal } from './store.ts'
const ACTION: CSSProperties = {
display: 'inline-flex',
+2 -2
View File
@@ -19,8 +19,8 @@
import { APIError, AuthError, type Leased, type Ran, type SandboxRuntime, type Wrote } from '@hanzo/ai'
import { useEffect, useState } from 'react'
import { client, ESTATE } from '../data/origin'
import { requireLogin } from '../data/gate'
import { client, ESTATE } from '../data/origin.ts'
import { requireLogin } from '../data/gate.ts'
/** `said` is what was typed; the rest is what the server answered. */
export type Line = { kind: 'said' | 'out' | 'err' | 'note'; text: string }
+1 -1
View File
@@ -1,6 +1,6 @@
import { useEffect } from 'react'
import { Check, Palette, X } from '@hanzogui/lucide-icons-2'
import { ACCENT_PALETTE, themeCustomizerStore, useThemeCustomizer } from './store'
import { ACCENT_PALETTE, themeCustomizerStore, useThemeCustomizer } from './store.ts'
export const ThemeModal = () => {
const { isOpen, accent, blurPx, glassOpacity } = useThemeCustomizer()
+1 -1
View File
@@ -13,7 +13,7 @@ import { Pencil, RefreshCw, ThumbsDown, ThumbsUp } from '@hanzogui/lucide-icons-
import { Button, XStack } from '@hanzo/ui'
import { CopyButton, TooltipAnchor } from '@hanzo/ui/product'
import type { Vote } from './tree'
import type { Vote } from './tree.ts'
export interface ActionsProps {
/** The turn as text — what copy puts on the clipboard. */
+4 -4
View File
@@ -11,10 +11,10 @@ import {
import { useEffect, useState } from 'react'
import { useNavigate } from 'react-router'
import { client, ESTATE } from '../data/origin'
import * as store from '../data/store'
import { useChannels } from '../channels/store'
import { exportStore, useExport } from './exportStore'
import { client, ESTATE } from '../data/origin.ts'
import * as store from '../data/store.ts'
import { useChannels } from '../channels/store.ts'
import { exportStore, useExport } from './exportStore.ts'
export const ExportModal = () => {
const navigate = useNavigate()
+1 -1
View File
@@ -3,7 +3,7 @@
*/
import { Sparkles } from '@hanzogui/lucide-icons-2'
import { Code } from '@hanzo/ui/chat'
import { artifactStore } from '../artifact/store'
import { artifactStore } from '../artifact/store.ts'
export interface FenceProps {
/** The word after the backticks. Shown in the label bar. */
+2 -2
View File
@@ -8,8 +8,8 @@
import { Paperclip } from '@hanzogui/lucide-icons-2'
import { SizableText, XStack } from '@hanzo/ui'
import type { Attachment } from '../data/types'
import { Picture } from './Picture'
import type { Attachment } from '../data/types.ts'
import { Picture } from './Picture.tsx'
const UNITS = ['B', 'KB', 'MB', 'GB']
+7 -7
View File
@@ -12,13 +12,13 @@
*/
import { Failure } from '@hanzo/ui/chat'
import type { Message, Part } from '../data/types'
import { Files } from './Files'
import { Picture } from './Picture'
import { Prose } from './Prose'
import { Think } from './Think'
import { Tool } from './Tool'
import { value } from './tree'
import type { Message, Part } from '../data/types.ts'
import { Files } from './Files.tsx'
import { Picture } from './Picture.tsx'
import { Prose } from './Prose.tsx'
import { Think } from './Think.tsx'
import { Tool } from './Tool.tsx'
import { value } from './tree.ts'
const OOPS = 'Something went wrong.'
+1 -1
View File
@@ -13,7 +13,7 @@
*/
import { Paragraph, YStack } from '@hanzo/ui'
import { Markdown } from './markdown'
import { Markdown } from './markdown.tsx'
export interface ProseProps {
source: string
+1 -1
View File
@@ -13,7 +13,7 @@
import { Step } from '@hanzo/ui/chat'
import { useState } from 'react'
import { Prose } from './Prose'
import { Prose } from './Prose.tsx'
export interface ThinkProps {
text: string
+3 -3
View File
@@ -22,9 +22,9 @@ import { Fill } from '@hanzo/ui'
import { Thread as Scroll, type Source } from '@hanzo/ui/chat'
import { useCallback, useMemo, useState, type ReactNode } from 'react'
import type { Message } from '../data/types'
import { build, path, type Choice, type Chosen, type Entry, type Vote } from './tree'
import { Turn } from './Turn'
import type { Message } from '../data/types.ts'
import { build, path, type Choice, type Chosen, type Entry, type Vote } from './tree.ts'
import { Turn } from './Turn.tsx'
/**
* The answer that has been asked for and has not started.
+2 -2
View File
@@ -12,8 +12,8 @@
import { Step, type Ran } from '@hanzo/ui/chat'
import type { ReactNode } from 'react'
import type { ToolCall } from '../data/types'
import { Fence } from './Fence'
import type { ToolCall } from '../data/types.ts'
import { Fence } from './Fence.tsx'
/** Tools whose name is machinery. Everything else reads fine as it arrives. */
const NAMES: Record<string, string> = {
+6 -6
View File
@@ -12,12 +12,12 @@
*/
import { Failure, Message, Sources, type Source } from '@hanzo/ui/chat'
import { Actions, acts, type ActionsProps } from './Actions'
import { Cited } from './Cite'
import { Edit } from './Edit'
import { Parts } from './Parts'
import { Siblings } from './Siblings'
import { plain, type Choice, type Vote } from './tree'
import { Actions, acts, type ActionsProps } from './Actions.tsx'
import { Cited } from './Cite.tsx'
import { Edit } from './Edit.tsx'
import { Parts } from './Parts.tsx'
import { Siblings } from './Siblings.tsx'
import { plain, type Choice, type Vote } from './tree.ts'
const OOPS = 'Something went wrong.'
+2 -2
View File
@@ -45,8 +45,8 @@ import ReactMarkdown from 'react-markdown'
import type { Components } from 'react-markdown'
import remarkGfm from 'remark-gfm'
import { Cite } from './Cite'
import { Fence } from './Fence'
import { Cite } from './Cite.tsx'
import { Fence } from './Fence.tsx'
/**
* The anchor a model writes beside a claim — U+E202 then `turn0search1`, in the
+1 -1
View File
@@ -22,7 +22,7 @@
*/
import type { Source } from '@hanzo/ui/chat'
import type { Feedback, Message } from '../data/types'
import type { Feedback, Message } from '../data/types.ts'
/** A thumbs-up or thumbs-down on an answer. */
export type Vote = Feedback['rating']
+4 -1
View File
@@ -8,7 +8,10 @@
"noEmit": false,
"declaration": true,
"declarationMap": true,
"allowImportingTsExtensions": false,
// Source names the file it means, so `node --test` can run it straight; on
// emit the same specifier becomes the `.js` beside it, which is the only
// spelling Node resolves in a package.
"rewriteRelativeImportExtensions": true,
"outDir": "dist",
"rootDir": "src"
},