Team is its own repo now

apps/team moved to hanzo-apps/team with its history, and builds there
against the published @hanzo/gui. It leaves the workspace list, the
lockfile, and the Written-at-Hanzo list in every NOTICE (ten names
become nine).


Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
zooqueen
2026-09-23 15:53:45 -07:00
co-authored by Hanzo Dev
parent f606295c76
commit 31ad1f039e
195 changed files with 162 additions and 6365 deletions
+7 -7
View File
@@ -65,11 +65,11 @@ dist/esm, `files` = src, types, dist. Named ESM imports from CommonJS packages
(`fast-glob`, `fs-extra`) fail under Node's lexer: use the default import or
`fs-extra/esm`.
## Two sites off one build, no image
## One site, no image
`hanzo.yml` declares `sites:` (team, gui) with one `site-build`; the reusable's
site lane publishes each to the Sites plane, routed by universe
`static-sites.yaml` (`team-static`, `gui-static`). The docs app's export script
is `build:web` (`one build`), the same name apps/team uses, and the turbo task
`build:web` depends on `^build` and the package's own `build`. There is no
Dockerfile and no image lane.
`hanzo.yml` declares one `site:` lane (slug `gui`); the reusable publishes it to
the Sites plane, routed by universe `static-sites.yaml` (`gui-static`). The docs
app's export script is `build:web` (`one build`), and the turbo task `build:web`
depends on `^build` and the package's own `build`. There is no Dockerfile and no
image lane. The native Team app that used to live at apps/team is its own repo,
hanzo-apps/team, with its own site lane.
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
-17
View File
@@ -1,17 +0,0 @@
# Hanzo Team
Native Hanzo Team skeleton on hanzogui — one codebase for web, mobile, and desktop.
Three screens share components with web: Shell (AppHeader: mark, org row,
five-surface switcher), Login (hanzo.id OIDC via system browser + deep link),
Wallet (balance and usage, monochrome tokens).
```bash
bun run dev # web dev server
bun run build:web # web production build → dist/client
bun run typecheck # tsc
bun run ios # native (one / expo)
```
Desktop wraps `dist/client` with Tauri: build web, then `cargo tauri dev` (or
`cargo check`) inside `src-tauri/`.
-16
View File
@@ -1,16 +0,0 @@
{
"expo": {
"name": "Hanzo Team",
"slug": "team",
"scheme": "hanzo-team",
"newArchEnabled": true,
"platforms": ["ios", "android"],
"plugins": ["vxrn/expo-plugin"],
"ios": {
"bundleIdentifier": "ai.hanzo.team"
},
"android": {
"package": "ai.hanzo.team"
}
}
}
-3
View File
@@ -1,3 +0,0 @@
body {
margin: 0;
}
-42
View File
@@ -1,42 +0,0 @@
import './_layout.css'
import { SchemeProvider, useUserScheme } from '@vxrn/color-scheme'
import { Slot } from 'one'
import { GuiProvider } from '@hanzo/gui'
import { Shell } from '~/components/Shell'
import { SessionProvider } from '~/src/session'
import config from '~/src/gui.config'
export default function Layout() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.svg" />
<title>Hanzo Team</title>
</head>
<body>
<SchemeProvider>
<Providers>
<SessionProvider>
<Shell>
<Slot />
</Shell>
</SessionProvider>
</Providers>
</SchemeProvider>
</body>
</html>
)
}
const Providers = ({ children }: { children: React.ReactNode }) => {
const userScheme = useUserScheme()
return (
<GuiProvider config={config} defaultTheme={userScheme.value}>
{children}
</GuiProvider>
)
}
-58
View File
@@ -1,58 +0,0 @@
import { useEffect, useState } from 'react'
import { ActivityIndicator } from 'react-native'
import { useLocalSearchParams } from 'one'
import { SizableText, YStack } from '@hanzo/gui'
import { Mark } from '~/components/Mark'
import { completeWebCallback } from '~/src/auth'
import { persistSession } from '~/src/session'
// Web-only: hanzo.id redirects here with ?code&state. Complete the PKCE exchange,
// persist the session, then hard-navigate home so the session provider re-reads it.
// (Native completes the flow in-process via expo-web-browser; this route is unused there.)
export default function Callback() {
const params = useLocalSearchParams<{ code?: string; state?: string; error?: string }>()
const [error, setError] = useState<string | null>(null)
useEffect(() => {
let live = true
void (async () => {
try {
const session = await completeWebCallback({
code: params.code,
state: params.state,
error: params.error,
})
await persistSession(session)
globalThis.location?.assign('/')
} catch (e) {
if (live) setError(e instanceof Error ? e.message : String(e))
}
})()
return () => {
live = false
}
}, [params.code, params.state, params.error])
return (
<YStack flex={1} items="center" justify="center" p="$4" gap="$4">
<Mark size={44} />
{error == null ? (
<>
<ActivityIndicator />
<SizableText size="$3" color="$color10">
Completing sign-in…
</SizableText>
</>
) : (
<YStack items="center" gap="$2">
<SizableText size="$4" fontWeight="600">
Sign-in failed
</SizableText>
<SizableText size="$2" color="$color10">
{error}
</SizableText>
</YStack>
)}
</YStack>
)
}
-70
View File
@@ -1,70 +0,0 @@
import { Link } from 'one'
import { SizableText, XStack, YStack } from '@hanzo/gui'
import { useSession } from '~/src/session'
export default function Home() {
const { session, loading } = useSession()
const signedIn = !loading && session != null
const who = session?.user?.email ?? session?.user?.name
const rooms = {
href: '/rooms',
title: 'Rooms',
caption: 'Conversations in your org',
} as const
const wallet = {
href: '/wallet',
title: 'Wallet',
caption: 'Balance and AI usage',
} as const
const views = signedIn
? [rooms, wallet]
: [
{ href: '/login', title: 'Sign in', caption: 'hanzo.id single sign-on' } as const,
rooms,
wallet,
]
return (
<YStack p="$4" gap="$4" maxW={560} width="100%" self="center">
<YStack gap="$1">
<SizableText size="$7" fontWeight="600">
Team
</SizableText>
<SizableText size="$3" color="$color10">
{signedIn && who != null
? `Signed in as ${who}.`
: 'Chat, projects, and planning for your org — one native app for mobile and desktop.'}
</SizableText>
</YStack>
{views.map((view) => (
<Link key={view.href} href={view.href} asChild>
<XStack
bg="$color1"
borderWidth={1}
borderColor="$borderColor"
rounded="$6"
p="$4"
items="center"
justify="space-between"
cursor="pointer"
pressStyle={{ opacity: 0.7 }}
>
<YStack gap="$1">
<SizableText size="$4" fontWeight="600">
{view.title}
</SizableText>
<SizableText size="$2" color="$color10">
{view.caption}
</SizableText>
</YStack>
<SizableText size="$4" color="$color10">
→
</SizableText>
</XStack>
</Link>
))}
</YStack>
)
}
-46
View File
@@ -1,46 +0,0 @@
import { Redirect } from 'one'
import { Button, SizableText, YStack } from '@hanzo/gui'
import { Mark } from '~/components/Mark'
import { useSession } from '~/src/session'
// Real hanzo.id OIDC (Authorization Code + PKCE) via the system browser (native) or a
// full-page redirect (web); the session returns through the app deep link / callback
// route. No in-app credential entry, no fake auth. See ~/src/auth.ts.
export default function Login() {
const { session, loading, signingIn, signIn } = useSession()
if (!loading && session != null) return <Redirect href="/" />
return (
<YStack flex={1} items="center" justify="center" p="$4" gap="$4">
<Mark size={44} />
<YStack items="center" gap="$1">
<SizableText size="$6" fontWeight="600">
Sign in to Hanzo
</SizableText>
<SizableText size="$2" color="$color10">
One account for every surface
</SizableText>
</YStack>
<Button
size="$4"
bg="$color"
borderWidth={0}
disabled={signingIn}
opacity={signingIn ? 0.6 : 1}
pressStyle={{ opacity: 0.8, bg: '$color' }}
onPress={() => void signIn()}
>
<Button.Text color="$background" fontWeight="600">
{signingIn ? 'Opening…' : 'Continue with hanzo.id'}
</Button.Text>
</Button>
<SizableText size="$1" color="$color10">
Opens hanzo.id · returns via hanzo-team://callback
</SizableText>
</YStack>
)
}
-126
View File
@@ -1,126 +0,0 @@
import { useCallback, useEffect, useState } from 'react'
import { useLocalSearchParams } from 'one'
import { Button, Input, ScrollView, SizableText, XStack, YStack } from '@hanzo/gui'
import { useSession } from '~/src/session'
import { Prompt } from '~/components/Prompt'
import { team, type TeamMessage } from '~/src/api'
// How often an open room re-reads its tail. Messages have no push channel of
// their own, so the room asks; often enough to feel like a conversation, rarely
// enough that an idle tab costs nothing worth counting.
const REFRESH_MS = 5000
// The surface stamps unix milliseconds; the reader wants a clock. A message with
// no stamp gets no time rather than a fabricated one at the epoch.
function clock(createdOn: number | undefined): string {
if (createdOn == null) return ''
return new Date(createdOn).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
})
}
// An author is an account uuid, not a display name — the roster owns names, and
// copying one onto every message is how the two come to disagree. Until a room
// reads the roster, show enough of the account to tell two people apart.
function who(author: string | undefined): string {
return author?.slice(0, 8) ?? 'unknown'
}
export default function RoomScreen() {
const { session, loading } = useSession()
const params = useLocalSearchParams<{ id?: string; space?: string }>()
const room = params.id ?? ''
const space = params.space ?? ''
const [messages, setMessages] = useState<TeamMessage[]>([])
const [state, setState] = useState<'idle' | 'loading' | 'error'>('idle')
const [draft, setDraft] = useState('')
const [sending, setSending] = useState(false)
const token = session?.accessToken
const refresh = useCallback(async () => {
if (token == null || room === '' || space === '') return
try {
setMessages(await team(token).messages(room, space))
setState('idle')
} catch {
setState('error')
}
}, [room, space, token])
useEffect(() => {
if (token == null) return
setState('loading')
void refresh()
const timer = setInterval(() => void refresh(), REFRESH_MS)
return () => clearInterval(timer)
}, [refresh, token])
const say = useCallback(async () => {
const text = draft.trim()
if (text === '' || token == null || sending) return
setSending(true)
try {
const said = await team(token).say(room, space, text)
setMessages((prior) => [...prior, said])
setDraft('')
} catch {
setState('error')
} finally {
setSending(false)
}
}, [draft, room, sending, space, token])
if (!loading && session == null)
return <Prompt title="Room" caption="Sign in to read this room." />
return (
<YStack flex={1} maxW={720} width="100%" self="center">
<ScrollView flex={1} contentContainerStyle={{ p: '$4', gap: '$3' }}>
{messages.map((message) => (
<YStack key={message.id ?? `${message.createdOn}-${message.author}`} gap="$1">
<XStack gap="$2" items="baseline">
<SizableText size="$2" fontWeight="600">
{who(message.author)}
</SizableText>
<SizableText size="$1" color="$color10">
{clock(message.createdOn)}
</SizableText>
</XStack>
<SizableText size="$3">{message.text ?? ''}</SizableText>
</YStack>
))}
{messages.length === 0 ? (
<SizableText size="$3" color="$color10">
{state === 'error'
? 'This room is unavailable right now.'
: state === 'loading'
? 'Loading…'
: 'Nothing said yet.'}
</SizableText>
) : null}
</ScrollView>
<XStack
p="$3"
gap="$2"
borderTopWidth={1}
borderColor="$borderColor"
items="center"
>
<Input
flex={1}
value={draft}
onChangeText={setDraft}
onSubmitEditing={() => void say()}
placeholder="Message"
returnKeyType="send"
/>
<Button disabled={sending || draft.trim() === ''} onPress={() => void say()}>
Send
</Button>
</XStack>
</YStack>
)
}
-94
View File
@@ -1,94 +0,0 @@
import { useEffect, useState } from 'react'
import { Link } from 'one'
import { Separator, SizableText, XStack, YStack } from '@hanzo/gui'
import { useSession } from '~/src/session'
import { Prompt } from '~/components/Prompt'
import { team, type TeamRoom } from '~/src/api'
// Every room the signed-in principal can see. Archived rooms are left out — they
// are still readable by id, but a list of them is a filing cabinet, not a place
// to talk. A room link carries its space because a room id alone is ambiguous.
export default function RoomsScreen() {
const { session, loading } = useSession()
const [rooms, setRooms] = useState<TeamRoom[] | null>(null)
const [state, setState] = useState<'idle' | 'loading' | 'error'>('idle')
useEffect(() => {
const token = session?.accessToken
if (token == null) return
let live = true
setState('loading')
void team(token)
.rooms()
.then((list) => {
if (live) {
setRooms(list)
setState('idle')
}
})
.catch(() => {
if (live) setState('error')
})
return () => {
live = false
}
}, [session?.accessToken])
if (!loading && session == null)
return <Prompt title="Rooms" caption="Sign in to read and join your org's rooms." />
// A room needs both halves of its address to be linkable, and the document
// marks neither required — so a room missing one is skipped rather than
// rendered into a link that cannot resolve.
const open = (rooms ?? []).filter(
(room) => !room.archived && room.id != null && room.space != null
)
return (
<YStack p="$4" gap="$4" maxW={560} width="100%" self="center">
<SizableText size="$7" fontWeight="600">
Rooms
</SizableText>
{open.length > 0 ? (
<YStack bg="$color1" borderWidth={1} borderColor="$borderColor" rounded="$6">
{open.map((room, index) => (
<YStack key={`${room.space}/${room.id}`}>
{index > 0 ? <Separator borderColor="$borderColor" /> : null}
<Link
href={`/rooms/${room.id}?space=${encodeURIComponent(room.space ?? '')}`}
asChild
>
<XStack p="$3.5" items="center" justify="space-between" cursor="pointer">
<YStack gap="$1" flex={1}>
<SizableText size="$4" fontWeight="600">
{room.name}
</SizableText>
{room.topic != null && room.topic !== '' ? (
<SizableText size="$2" color="$color10">
{room.topic}
</SizableText>
) : null}
</YStack>
{room.private ? (
<SizableText size="$1" color="$color10">
Private
</SizableText>
) : null}
</XStack>
</Link>
</YStack>
))}
</YStack>
) : (
<SizableText size="$3" color="$color10">
{state === 'error'
? 'Rooms are unavailable right now.'
: state === 'loading'
? 'Loading…'
: 'No rooms yet.'}
</SizableText>
)}
</YStack>
)
}
-84
View File
@@ -1,84 +0,0 @@
import { useEffect, useState } from 'react'
import { Separator, SizableText, XStack, YStack } from '@hanzo/gui'
import { useSession } from '~/src/session'
import { Prompt } from '~/components/Prompt'
import { billing, type Wallet } from '~/src/api'
// Real balance + usage from the billing surface, read through the generated client.
// No hardcoded money: signed-out shows a sign-in prompt; a failed fetch shows an
// honest unavailable state rather than a fake $0.00.
export default function WalletScreen() {
const { session, loading } = useSession()
const [wallet, setWallet] = useState<Wallet | null>(null)
const [state, setState] = useState<'idle' | 'loading' | 'error'>('idle')
useEffect(() => {
const token = session?.accessToken
if (token == null) return
let live = true
setState('loading')
void billing(token)
.wallet()
.then((w) => {
if (live) {
setWallet(w)
setState('idle')
}
})
.catch(() => {
if (live) setState('error')
})
return () => {
live = false
}
}, [session?.accessToken])
if (!loading && session == null)
return <Prompt title="Wallet" caption="Sign in to see your balance and AI usage." />
const balance =
wallet != null ? `$${wallet.balanceUsd.toFixed(2)}` : state === 'error' ? '—' : '…'
return (
<YStack p="$4" gap="$4" maxW={560} width="100%" self="center">
<YStack
bg="$color1"
borderWidth={1}
borderColor="$borderColor"
rounded="$6"
p="$4"
gap="$1"
>
<SizableText size="$2" color="$color10">
Balance
</SizableText>
<SizableText size="$9" fontWeight="600">
{balance}
</SizableText>
<SizableText size="$1" color="$color10">
{state === 'error'
? 'Balance unavailable right now'
: 'Usage-metered · billed via billing.hanzo.ai'}
</SizableText>
</YStack>
{wallet != null && wallet.usage.length > 0 ? (
<YStack bg="$color1" borderWidth={1} borderColor="$borderColor" rounded="$6">
{wallet.usage.map((row, index) => (
<YStack key={row.label}>
{index > 0 ? <Separator borderColor="$borderColor" /> : null}
<XStack p="$3.5" items="center" justify="space-between">
<SizableText size="$3" color="$color10">
{row.label}
</SizableText>
<SizableText size="$3" fontWeight="600">
{row.value}
</SizableText>
</XStack>
</YStack>
))}
</YStack>
) : null}
</YStack>
)
}
-14
View File
@@ -1,14 +0,0 @@
import { useTheme } from '@hanzo/gui'
import Svg, { Path } from 'react-native-svg'
const d =
'M45.9269 46.3536C45.65 45.3274 44.9038 44.8299 43.8628 44.9314C41.8474 45.1268 41.1782 44.4808 40.2218 43.0613C42.7423 43.5952 44.3679 42.9389 44.8756 42.6471C45.5295 42.2694 46.191 41.785 45.9295 40.9306C45.6731 40.0945 44.9038 39.9278 44.1141 40.0789C42.9936 40.2925 41.9192 40.4175 41.0474 39.3261C41.232 39.1594 41.4166 38.9875 41.6064 38.8234C42.0705 38.4249 42.4295 37.9561 42.1936 37.3231C41.9679 36.7188 41.3961 36.3724 40.8141 36.4792C40.3397 36.5678 39.3884 36.8647 39.0987 36.3802L39.1115 36.375C39.0013 36.349 38.832 35.8957 38.7961 35.5649C38.6269 33.9474 39.3269 32.395 40.2705 31.1057C41.4474 29.5038 42.2551 27.7378 42.6141 25.7791C43.1756 22.7055 42.5807 19.8456 40.9192 17.2174C38.9474 14.097 36.1679 12.0419 32.509 11.2865C31.65 11.1094 30.8115 11.0156 29.9987 11C29.1833 11.0156 28.3474 11.1094 27.4884 11.2865C23.832 12.0419 21.05 14.097 19.0782 17.2174C17.4192 19.8456 16.8243 22.7055 17.3833 25.7791C17.7397 27.7404 18.55 29.5038 19.7269 31.1057C20.6731 32.395 21.3731 33.9448 21.2013 35.5649C21.1654 35.8957 20.9961 36.349 20.8859 36.375L20.8987 36.3802C20.609 36.8621 19.6602 36.5678 19.1833 36.4792C18.6038 36.3724 18.0295 36.7188 17.8038 37.3231C17.5679 37.9561 17.9243 38.4249 18.391 38.8234C18.5807 38.9875 18.7654 39.1594 18.95 39.3261C18.0782 40.4175 17.0013 40.2925 15.8833 40.0789C15.0936 39.9278 14.3243 40.0945 14.0679 40.9306C13.8064 41.785 14.4679 42.2694 15.1218 42.6471C15.6269 42.9389 17.2551 43.5926 19.7756 43.0613C18.8192 44.4808 18.15 45.1268 16.1346 44.9314C15.0936 44.8299 14.3448 45.3274 14.0705 46.3536C13.8013 47.3564 14.332 48.0753 15.1731 48.5546C15.7243 48.8672 16.3397 48.9896 16.991 49C20.5577 49.0495 23.5013 47.5778 26.1525 45.3638C27.6115 44.1448 28.8115 43.5275 30.0013 43.5119C31.191 43.5275 32.3884 44.1448 33.85 45.3638C36.5013 47.5778 39.4448 49.0495 43.0115 49C43.6628 48.9922 44.2782 48.8672 44.8295 48.5546C45.6731 48.0753 46.2013 47.3564 45.932 46.3536H45.9269ZM25.0756 34.2027C23.9731 34.2001 23.0628 33.278 23.0602 32.1658C23.0602 31.0328 23.9807 30.1055 25.1166 30.0951C26.1961 30.0847 27.1577 31.1083 27.1474 32.257C27.1372 33.3952 26.2705 34.2079 25.0756 34.2027ZM34.9192 34.2027C33.7218 34.2079 32.8577 33.3926 32.8474 32.257C32.8372 31.1083 33.8013 30.0847 34.8782 30.0951C36.0141 30.1081 36.9346 31.0354 36.9346 32.1658C36.9346 33.278 36.0218 34.2001 34.9192 34.2027Z'
export const Mark = ({ size = 24 }: { size?: number }) => {
const theme = useTheme()
return (
<Svg width={size} height={size} viewBox="10 8 40 44" fill="none">
<Path d={d} fill={theme.color.get()} />
</Svg>
)
}
-38
View File
@@ -1,38 +0,0 @@
import { Link } from 'one'
import { SizableText, XStack, YStack } from '@hanzo/gui'
// The signed-out state, one place for every screen that has one: what the screen
// is, and the single way in. Screens differ only in the words.
export function Prompt({
title,
caption,
}: {
title: string
caption: string
}): React.ReactElement {
return (
<YStack p="$4" gap="$3" maxW={560} width="100%" self="center">
<SizableText size="$6" fontWeight="600">
{title}
</SizableText>
<SizableText size="$3" color="$color10">
{caption}
</SizableText>
<Link href="/login" asChild>
<XStack
bg="$color"
rounded="$10"
px="$4"
py="$2.5"
self="flex-start"
cursor="pointer"
pressStyle={{ opacity: 0.8 }}
>
<SizableText size="$3" fontWeight="600" color="$background">
Sign in
</SizableText>
</XStack>
</Link>
</YStack>
)
}
-30
View File
@@ -1,30 +0,0 @@
import { Moon, Sun, SunMoon } from '@hanzogui/lucide-icons-2'
import { useSystemScheme, useUserScheme } from '@vxrn/color-scheme'
import { Appearance } from 'react-native'
import { isWeb, View } from '@hanzo/gui'
const order = ['system', 'light', 'dark'] as const
export const SchemeToggle = () => {
const userScheme = useUserScheme()
const systemScheme = useSystemScheme()
const Icon =
userScheme.setting === 'system' ? SunMoon : userScheme.setting === 'dark' ? Moon : Sun
return (
<View
p="$2"
cursor="pointer"
pressStyle={{ opacity: 0.6 }}
onPress={() => {
const next = order[(order.indexOf(userScheme.setting) + 1) % 3]
if (!isWeb) {
Appearance.setColorScheme(next === 'system' ? systemScheme : next)
}
userScheme.set(next)
}}
>
<Icon size={18} color="$color10" />
</View>
)
}
-169
View File
@@ -1,169 +0,0 @@
import { useState } from 'react'
import { Linking } from 'react-native'
import { useRouter } from 'one'
import { ChevronDown } from '@hanzogui/lucide-icons-2'
import { ScrollView, SizableText, XStack, YStack } from '@hanzo/gui'
import { Mark } from './Mark'
import { SchemeToggle } from './Scheme'
import { SURFACES } from '~/src/surfaces'
import { useSession } from '~/src/session'
// app frame: AppHeader (mark · org switcher · seven-surface switcher · auth) over content
export const Shell = ({ children }: { children: React.ReactNode }) => {
const router = useRouter()
const { session, signOut } = useSession()
const [orgOpen, setOrgOpen] = useState(false)
const [activeOrg, setActiveOrg] = useState<string | undefined>(undefined)
const orgs = session?.user?.orgs ?? []
const currentOrg = activeOrg ?? orgs[0] ?? 'Hanzo'
const signedIn = session != null
return (
<YStack flex={1} minH="100%" bg="$background">
<YStack
borderBottomWidth={1}
borderColor="$borderColor"
px="$4"
pt="$3"
pb="$2"
gap="$2"
>
<XStack items="center" gap="$3">
<Mark size={22} />
<YStack position="relative">
<XStack
items="center"
gap="$2"
rounded="$4"
px="$2"
py="$1"
cursor="pointer"
pressStyle={{ bg: '$color1' }}
onPress={() => setOrgOpen((v) => (orgs.length > 1 ? !v : v))}
>
<YStack
width={20}
height={20}
rounded={999}
bg="$color"
items="center"
justify="center"
>
<SizableText size="$1" fontWeight="700" color="$background">
{currentOrg.charAt(0).toUpperCase()}
</SizableText>
</YStack>
<SizableText size="$3" fontWeight="600">
{currentOrg}
</SizableText>
{orgs.length > 1 ? <ChevronDown size={14} color="$color10" /> : null}
</XStack>
{orgOpen && orgs.length > 1 ? (
<YStack
position="absolute"
t={38}
l={0}
minW={180}
bg="$background"
borderWidth={1}
borderColor="$borderColor"
rounded="$4"
py="$1"
z={100}
>
{orgs.map((org) => (
<XStack
key={org}
px="$3"
py="$2"
cursor="pointer"
pressStyle={{ bg: '$color1' }}
onPress={() => {
setActiveOrg(org)
setOrgOpen(false)
}}
>
<SizableText
size="$3"
color={org === currentOrg ? '$color' : '$color10'}
>
{org}
</SizableText>
</XStack>
))}
</YStack>
) : null}
</YStack>
<XStack flex={1} />
{signedIn ? (
<XStack
rounded="$10"
px="$3"
py="$1.5"
borderWidth={1}
borderColor="$borderColor"
cursor="pointer"
pressStyle={{ opacity: 0.6 }}
onPress={() => void signOut()}
>
<SizableText size="$2" color="$color10">
Sign out
</SizableText>
</XStack>
) : (
<XStack
rounded="$10"
px="$3"
py="$1.5"
bg="$color"
cursor="pointer"
pressStyle={{ opacity: 0.8 }}
onPress={() => router.push('/login')}
>
<SizableText size="$2" color="$background" fontWeight="600">
Sign in
</SizableText>
</XStack>
)}
<SchemeToggle />
</XStack>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<XStack gap="$2">
{SURFACES.map((surface) => {
const active = surface.id === 'team'
return (
<XStack
key={surface.id}
rounded="$10"
px="$3"
py="$1.5"
borderWidth={1}
borderColor={active ? '$borderColor' : 'transparent'}
bg={active ? '$color1' : 'transparent'}
cursor="pointer"
pressStyle={{ opacity: 0.6 }}
onPress={() => {
if (!active) void Linking.openURL(surface.href)
}}
>
<SizableText size="$2" color={active ? '$color' : '$color10'}>
{surface.label}
</SizableText>
</XStack>
)
})}
</XStack>
</ScrollView>
</YStack>
{children}
</YStack>
)
}
-7
View File
@@ -1,7 +0,0 @@
import type { GuiBuildOptions } from '@hanzogui/core'
export default {
components: ['@hanzo/gui'],
config: './src/gui.config.ts',
disableExtraction: true,
} satisfies GuiBuildOptions
-37
View File
@@ -1,37 +0,0 @@
{
"name": "@hanzogui/team",
"version": "8.3.5",
"private": true,
"license": "BSD-3-Clause",
"author": "Hanzo AI <[email protected]>",
"type": "module",
"scripts": {
"dev": "one dev",
"build:web": "one build",
"serve": "one serve",
"ios": "one run:ios",
"android": "one run:android",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@hanzo/gui": "workspace:*",
"@hanzogui/config": "workspace:*",
"@hanzogui/core": "workspace:*",
"@hanzogui/lucide-icons-2": "workspace:*",
"@vxrn/color-scheme": "^1.25.8",
"expo": "^57.0.18",
"hanzoai": "^8.5.157",
"one": "^1.25.8",
"react": "19.2.8",
"react-native": "^0.87.1",
"react-native-safe-area-context": "^5.9.1",
"react-native-screens": "^4.27.0",
"react-native-svg": "^15.15.5",
"react-native-web": "^0.21.2"
},
"devDependencies": {
"@hanzogui/vite-plugin": "workspace:*",
"@types/react": "19.2.18",
"vite": "^8.2.2"
}
}
-19
View File
@@ -1,19 +0,0 @@
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<style>
.core { fill: #ffffff }
.rim { fill: #000000; stroke: #000000 }
@media (prefers-color-scheme: light) { .core { fill: #000000 } }
@media (prefers-color-scheme: dark) { .rim { fill: #ffffff; stroke: #ffffff } }
</style>
<defs><g id="m">
<path d="M22.21 67V44.6369H0V67H22.21Z"/>
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z"/>
<path d="M22.21 0H0V22.3184H22.21V0Z"/>
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z"/>
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z"/>
</g></defs>
<g transform="translate(8 8) scale(0.7164)">
<use href="#m" class="rim" stroke-width="5.5833" stroke-miterlimit="2"/>
<use href="#m" class="core"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 795 B

-2
View File
@@ -1,2 +0,0 @@
/target
/gen/schemas
-4392
View File
File diff suppressed because it is too large Load Diff
-12
View File
@@ -1,12 +0,0 @@
[package]
name = "team"
version = "0.1.0"
description = "Hanzo Team desktop shell"
license = "BSD-3-Clause"
edition = "2021"
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = [] }
-3
View File
@@ -1,3 +0,0 @@
fn main() {
tauri_build::build()
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

-7
View File
@@ -1,7 +0,0 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running hanzo team");
}
-24
View File
@@ -1,24 +0,0 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Hanzo Team",
"version": "0.1.0",
"identifier": "ai.hanzo.team",
"build": {
"frontendDist": "../dist/client"
},
"app": {
"windows": [
{
"title": "Hanzo Team",
"width": 1200,
"height": 800
}
],
"security": {
"csp": null
}
},
"bundle": {
"active": false
}
}
-58
View File
@@ -1,58 +0,0 @@
// A session token, applied once.
//
// The credential used to ride along as the last argument of every call —
// fetchRooms(token), fetchMessages(id, space, token), sendMessage(id, space,
// text, token) — so who you are and what you are doing were stitched together at
// each call site, and two modules each built their own client. Here the token is
// applied once and the operations come back bound to it.
//
// The SDK carries the routes, the request and response types, and the
// Authorization header, so nothing below states a path, a host or a header. A
// route that moves in the product moves here by regenerating hanzoai.
//
// Every generated field is optional — the document marks none required — so the
// list reads default to empty rather than pretending otherwise.
import { BillingApi, Configuration, TeamApi } from 'hanzoai'
import type { TeamMessage, TeamRoom } from 'hanzoai'
import { API } from './config'
import { read, type Wallet } from './money'
export type { TeamMessage, TeamRoom, Wallet }
/** Where we talk and who we are, met in one place. */
const client = (token: string) => new Configuration({ basePath: API, accessToken: token })
/** Rooms and what is said in them. */
export const team = (token: string) => {
const api = new TeamApi(client(token))
return {
/** Every room the signed-in principal can see. */
rooms: async (): Promise<TeamRoom[]> => (await api.getTeamRooms()).data.rooms ?? [],
/**
* One room's tail, oldest first — the order a conversation is read in.
*
* `space` rides along because a room id is unique within a space and not
* across the org; without it the answer depends on which space was searched
* first.
*/
messages: async (id: string, space: string): Promise<TeamMessage[]> =>
(await api.getTeamRoomsByIdMessages({ id, space })).data.messages ?? [],
/** Say something. Plain text in; the surface wraps it in the client's markup. */
say: async (id: string, space: string, text: string): Promise<TeamMessage> =>
(await api.postTeamRoomsByIdMessages({ id, teamMessageWrite: { space, text } }))
.data,
}
}
/** What the org has and what it has spent. */
export const billing = (token: string) => {
const api = new BillingApi(client(token))
return {
/** The org wallet. Throws on any non-2xx or unreadable shape. */
wallet: async (): Promise<Wallet> =>
read((await api.getBillingBalance()).data as Record<string, unknown>),
}
}
-268
View File
@@ -1,268 +0,0 @@
// Real hanzo.id OIDC — Authorization Code + PKCE. This module is the protocol (no
// platform imports): it builds the authorize URL, exchanges the code, and reads the
// profile. The browser step is delegated to the platform-split ./oidc-browser (native
// opens the system browser; web redirects the page), so expo stays out of the web bundle.
import { CLIENT_ID, ISSUER, SCOPES } from './config'
import { authorize, redirectUri } from './oidc-browser'
import type { AuthOutcome } from './oidc-types'
export { CLIENT_ID, redirectUri }
export interface Tokens {
accessToken: string
refreshToken?: string
/** epoch millis the access token expires, when the server reports expires_in */
expiresAt?: number
}
export interface UserInfo {
sub: string
name?: string
email?: string
/** org slugs the account belongs to, when the IAM userinfo carries groups */
orgs?: string[]
}
export interface Session extends Tokens {
user?: UserInfo
}
interface Endpoints {
authorization: string
token: string
userinfo: string
}
// IAM's endpoint paths, used when discovery is unreachable.
//
// These are what the issuer publishes today, checked against it rather than
// remembered: the three they replace answered 404, and two of them carried an
// /api/ prefix IAM has not served for a long time. A fallback is only ever read
// when discovery is down, so a stale one fails exactly when nothing else can
// cover for it — which is why it is worth keeping honest.
const FALLBACK: Endpoints = {
authorization: `${ISSUER}/v1/iam/oauth/authorize`,
token: `${ISSUER}/v1/iam/oauth/token`,
userinfo: `${ISSUER}/v1/iam/oauth/userinfo`,
}
let endpointsCache: Endpoints | undefined
/** Resolve OIDC endpoints from the discovery document, falling back to IAM's paths. */
export async function discover(): Promise<Endpoints> {
if (endpointsCache !== undefined) return endpointsCache
try {
const res = await fetch(`${ISSUER}/.well-known/openid-configuration`)
if (res.ok) {
const d = (await res.json()) as Record<string, string>
endpointsCache = {
authorization: d.authorization_endpoint ?? FALLBACK.authorization,
token: d.token_endpoint ?? FALLBACK.token,
userinfo: d.userinfo_endpoint ?? FALLBACK.userinfo,
}
return endpointsCache
}
} catch {
// discovery blocked — fall through to the known IAM paths
}
endpointsCache = FALLBACK
return endpointsCache
}
const UNRESERVED = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'
function randomString(length: number): string {
const bytes = new Uint8Array(length)
const c = globalThis.crypto
if (c?.getRandomValues != null) c.getRandomValues(bytes)
else for (let i = 0; i < length; i++) bytes[i] = Math.floor(Math.random() * 256)
let out = ''
for (let i = 0; i < length; i++) out += UNRESERVED[bytes[i] % UNRESERVED.length]
return out
}
function base64Url(bytes: Uint8Array): string {
let bin = ''
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i])
const b64 =
typeof btoa === 'function' ? btoa(bin) : Buffer.from(bytes).toString('base64')
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
}
interface Challenge {
value: string
method: 'S256' | 'plain'
}
/** PKCE challenge: SHA-256 where a subtle-crypto digest exists (web), else plain. */
async function codeChallenge(verifier: string): Promise<Challenge> {
const subtle = globalThis.crypto?.subtle
if (subtle?.digest != null) {
const data = new TextEncoder().encode(verifier)
const digest = await subtle.digest('SHA-256', data)
return { value: base64Url(new Uint8Array(digest)), method: 'S256' }
}
return { value: verifier, method: 'plain' }
}
function buildAuthUrl(opts: {
endpoint: string
state: string
challenge: Challenge
redirect: string
}): string {
const q = new URLSearchParams({
client_id: CLIENT_ID,
response_type: 'code',
scope: SCOPES,
redirect_uri: opts.redirect,
state: opts.state,
code_challenge: opts.challenge.value,
code_challenge_method: opts.challenge.method,
})
return `${opts.endpoint}?${q.toString()}`
}
function parseCallback(url: string): { code?: string; state?: string; error?: string } {
const q = url.includes('?') ? url.slice(url.indexOf('?') + 1) : ''
const p = new URLSearchParams(q)
return {
code: p.get('code') ?? undefined,
state: p.get('state') ?? undefined,
error: p.get('error') ?? undefined,
}
}
async function exchangeCode(
code: string,
verifier: string,
redirect: string
): Promise<Tokens> {
const { token } = await discover()
const body = new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: redirect,
client_id: CLIENT_ID,
code_verifier: verifier,
})
const res = await fetch(token, {
method: 'POST',
headers: {
'content-type': 'application/x-www-form-urlencoded',
accept: 'application/json',
},
body: body.toString(),
})
if (!res.ok) throw new Error(`token exchange failed (${res.status})`)
const d = (await res.json()) as Record<string, unknown>
const accessToken = d.access_token
if (typeof accessToken !== 'string' || accessToken.length === 0) {
throw new Error(
typeof d.error === 'string' ? d.error : 'no access_token in token response'
)
}
const expiresIn = typeof d.expires_in === 'number' ? d.expires_in : undefined
return {
accessToken,
refreshToken: typeof d.refresh_token === 'string' ? d.refresh_token : undefined,
expiresAt: expiresIn !== undefined ? Date.now() + expiresIn * 1000 : undefined,
}
}
/** Read the signed-in profile from the OIDC userinfo endpoint. */
export async function fetchUserInfo(accessToken: string): Promise<UserInfo | undefined> {
try {
const { userinfo } = await discover()
const res = await fetch(userinfo, {
headers: { authorization: `Bearer ${accessToken}` },
})
if (!res.ok) return undefined
const d = (await res.json()) as Record<string, unknown>
const groups = Array.isArray(d.groups)
? (d.groups as unknown[]).filter((g): g is string => typeof g === 'string')
: undefined
return {
sub: String(d.sub ?? d.id ?? ''),
name:
typeof d.name === 'string'
? d.name
: typeof d.preferred_username === 'string'
? d.preferred_username
: undefined,
email: typeof d.email === 'string' ? d.email : undefined,
orgs: groups,
}
} catch {
return undefined
}
}
/** Web PKCE spans a page navigation — stash the verifier/state for the /callback route. */
const PENDING_KEY = 'hanzo-team-oidc-pending'
interface Pending {
verifier: string
state: string
redirect: string
}
function savePending(p: Pending): void {
globalThis.sessionStorage?.setItem(PENDING_KEY, JSON.stringify(p))
}
function loadPending(): Pending | undefined {
const raw = globalThis.sessionStorage?.getItem(PENDING_KEY)
return raw != null ? (JSON.parse(raw) as Pending) : undefined
}
function clearPending(): void {
globalThis.sessionStorage?.removeItem(PENDING_KEY)
}
export type LoginResult =
| { status: 'session'; session: Session }
| { status: 'redirecting' }
| { status: 'cancelled' }
/**
* Begin sign-in. Native completes in-process (returns the session); web navigates
* away and completes on the /callback route (returns `redirecting`).
*/
export async function login(): Promise<LoginResult> {
const { authorization } = await discover()
const redirect = redirectUri()
const verifier = randomString(64)
const state = randomString(24)
const challenge = await codeChallenge(verifier)
const url = buildAuthUrl({ endpoint: authorization, state, challenge, redirect })
// Persist for the web /callback route; a no-op on native (no sessionStorage).
savePending({ verifier, state, redirect })
const outcome: AuthOutcome = await authorize(url)
if (outcome.kind === 'redirecting') return { status: 'redirecting' }
if (outcome.kind === 'cancelled') return { status: 'cancelled' }
const cb = parseCallback(outcome.url)
if (cb.error != null) throw new Error(cb.error)
if (cb.code == null) throw new Error('no authorization code in callback')
if (cb.state !== state) throw new Error('state mismatch')
const tokens = await exchangeCode(cb.code, verifier, redirect)
const user = await fetchUserInfo(tokens.accessToken)
return { status: 'session', session: { ...tokens, user } }
}
/** Complete the web redirect flow from the /callback route's query string. */
export async function completeWebCallback(query: {
code?: string
state?: string
error?: string
}): Promise<Session> {
if (query.error != null) throw new Error(query.error)
const pending = loadPending()
clearPending()
if (pending == null) throw new Error('no pending sign-in')
if (query.code == null) throw new Error('no authorization code')
if (query.state !== pending.state) throw new Error('state mismatch')
const tokens = await exchangeCode(query.code, pending.verifier, pending.redirect)
const user = await fetchUserInfo(tokens.accessToken)
return { ...tokens, user }
}
-23
View File
@@ -1,23 +0,0 @@
// Where this app talks to, in one place.
//
// Defaults are production, so a plain build needs no environment at all. Each
// value is overridable because the same bundle has to run against a cloud on
// localhost: pointing it there used to mean editing three files, which is how
// one of them stays edited.
//
// `process.env` rather than `import.meta.env`: the values are read on web and
// on native, and One inlines this form for both.
/** The one endpoint. Every Hanzo surface lives under it — not a host per product. */
export const API = process.env.HANZO_API ?? 'https://api.hanzo.ai'
/** hanzo.id, the issuer. Discovery hangs off it; auth.ts asks it first. */
export const ISSUER = process.env.HANZO_ISSUER ?? 'https://hanzo.id'
/** The public client registered in IAM, `<org>-<app>`. */
export const CLIENT_ID = process.env.HANZO_CLIENT_ID ?? 'hanzo-team-native'
export const SCOPES = 'openid profile email'
/** Deep-link scheme (app.json `scheme`) the IAM redirect must whitelist. */
export const REDIRECT_SCHEME = 'hanzo-team'
-30
View File
@@ -1,30 +0,0 @@
import { defaultConfig } from '@hanzogui/config/v5'
import { createGui } from '@hanzo/gui'
// hanzo monochrome: dark #000 / #0a0a0a / #1f1f1f · light #fff / #f7f7f7 / #ebebeb
export const config = createGui({
...defaultConfig,
themes: {
...defaultConfig.themes,
dark: {
...defaultConfig.themes.dark,
background: '#000000',
color1: '#0a0a0a',
borderColor: '#1f1f1f',
},
light: {
...defaultConfig.themes.light,
background: '#ffffff',
color1: '#f7f7f7',
borderColor: '#ebebeb',
},
},
})
export type Conf = typeof config
declare module '@hanzo/gui' {
interface GuiCustomConfig extends Conf {}
}
export default config
-58
View File
@@ -1,58 +0,0 @@
// Reading money off an untyped payload.
//
// /v1/billing/balance is one of the operations the document states without a
// response schema, so the SDK hands back `any` and someone has to decide what a
// balance looks like. That someone is here, and only here — it is a reading, not
// a request, and keeping it out of the transport is what lets each be read on its
// own terms.
//
// No hardcoded money: a shape this cannot read is an error, never a fake $0.00.
export interface UsageRow {
label: string
value: string
}
export interface Wallet {
/** balance in USD */
balanceUsd: number
usage: UsageRow[]
}
function num(v: unknown): number | undefined {
if (typeof v === 'number' && Number.isFinite(v)) return v
if (typeof v === 'string' && v.trim() !== '' && Number.isFinite(Number(v)))
return Number(v)
return undefined
}
/** atto-USD (18-dec) is the canonical money unit; plain USD and cents also read. */
function usd(record: Record<string, unknown>): number | undefined {
const atto = record.balance_atto_usd ?? record.balanceAtto ?? record.atto_usd
if (typeof atto === 'string' && /^-?\d+$/.test(atto)) return Number(BigInt(atto)) / 1e18
const dollars = num(
record.balance_usd ?? record.balanceUsd ?? record.balance ?? record.credits
)
if (dollars !== undefined) return dollars
const cents = num(record.balance_cents ?? record.balanceCents)
return cents === undefined ? undefined : cents / 100
}
/** One untyped payload in, a Wallet out. Throws when there is no balance to read. */
export function read(record: Record<string, unknown>): Wallet {
const balanceUsd = usd(record)
if (balanceUsd === undefined) throw new Error('no balance in billing response')
const usage: UsageRow[] = []
const period = record.period ?? record.month
if (typeof period === 'string') usage.push({ label: 'Period', value: period })
const spent = num(record.spent_usd ?? record.spentUsd ?? record.usage_usd)
if (spent !== undefined)
usage.push({ label: 'Spent this period', value: `$${spent.toFixed(2)}` })
const requests = num(record.requests ?? record.request_count)
if (requests !== undefined) usage.push({ label: 'Requests', value: String(requests) })
const seats = num(record.seats ?? record.seat_count)
if (seats !== undefined) usage.push({ label: 'Seats', value: String(seats) })
return { balanceUsd, usage }
}
-18
View File
@@ -1,18 +0,0 @@
// NATIVE authorize step: open hanzo.id in the system browser and read the
// `hanzo-team://callback` deep link back. Keeps the expo modules (which pull
// expo-modules-core) out of the web bundle — the web build resolves oidc-browser.web.ts.
import * as WebBrowser from 'expo-web-browser'
import * as Linking from 'expo-linking'
import { REDIRECT_SCHEME } from './config'
import type { AuthOutcome } from './oidc-types'
export function redirectUri(): string {
return Linking.createURL('callback', { scheme: REDIRECT_SCHEME })
}
export async function authorize(url: string): Promise<AuthOutcome> {
const result = await WebBrowser.openAuthSessionAsync(url, redirectUri())
if (result.type !== 'success' || result.url == null) return { kind: 'cancelled' }
return { kind: 'callback', url: result.url }
}
-13
View File
@@ -1,13 +0,0 @@
// WEB authorize step: full-page redirect to hanzo.id. The /callback route completes
// the exchange. No expo imports here, so the web bundle never pulls expo-modules-core.
import type { AuthOutcome } from './oidc-types'
export function redirectUri(): string {
return `${globalThis.location?.origin ?? 'https://hanzo.team'}/callback`
}
export async function authorize(url: string): Promise<AuthOutcome> {
globalThis.location?.assign(url)
return { kind: 'redirecting' }
}
-6
View File
@@ -1,6 +0,0 @@
// The result of driving the authorize step, produced by the platform-split
// ./oidc-browser module (native uses the system browser; web redirects the page).
export type AuthOutcome =
| { kind: 'callback'; url: string } // native: the app-scheme redirect came back
| { kind: 'redirecting' } // web: the page navigated to hanzo.id
| { kind: 'cancelled' } // the user dismissed the browser
-103
View File
@@ -1,103 +0,0 @@
// One session store for every screen. Persists the OIDC session (AsyncStorage works
// on web via localStorage and on native), exposes it through React context, and owns
// sign-in / sign-out so the screens never touch the protocol directly.
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react'
import { getItem, removeItem, setItem } from './store'
import { login as oidcLogin, type Session } from './auth'
const STORAGE_KEY = 'hanzo-team-session'
async function load(): Promise<Session | null> {
const raw = await getItem(STORAGE_KEY)
if (raw == null) return null
try {
return JSON.parse(raw) as Session
} catch {
return null
}
}
async function save(session: Session | null): Promise<void> {
if (session == null) await removeItem(STORAGE_KEY)
else await setItem(STORAGE_KEY, JSON.stringify(session))
}
export interface SessionState {
session: Session | null
loading: boolean
/** true while a sign-in is in flight */
signingIn: boolean
signIn: () => Promise<void>
signOut: () => Promise<void>
}
const SessionContext = createContext<SessionState | undefined>(undefined)
export function SessionProvider({
children,
}: {
children: React.ReactNode
}): React.ReactElement {
const [session, setSession] = useState<Session | null>(null)
const [loading, setLoading] = useState(true)
const [signingIn, setSigningIn] = useState(false)
useEffect(() => {
let live = true
void load().then((s) => {
if (live) {
setSession(s)
setLoading(false)
}
})
return () => {
live = false
}
}, [])
const signIn = useCallback(async () => {
setSigningIn(true)
try {
const result = await oidcLogin()
if (result.status === 'session') {
await save(result.session)
setSession(result.session)
}
// 'redirecting' (web) completes on the /callback route; 'cancelled' is a no-op
} finally {
setSigningIn(false)
}
}, [])
const signOut = useCallback(async () => {
await save(null)
setSession(null)
}, [])
const value = useMemo<SessionState>(
() => ({ session, loading, signingIn, signIn, signOut }),
[session, loading, signingIn, signIn, signOut]
)
return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>
}
export function useSession(): SessionState {
const ctx = useContext(SessionContext)
if (ctx === undefined)
throw new Error('useSession must be used within a SessionProvider')
return ctx
}
/** Persist a session obtained outside the provider (the web /callback route). */
export async function persistSession(session: Session): Promise<void> {
await save(session)
}
-9
View File
@@ -1,9 +0,0 @@
// NATIVE key/value store — AsyncStorage. Split so the web bundle (store.web.ts) uses
// localStorage directly and never pulls the native module into the web build.
import AsyncStorage from '@react-native-async-storage/async-storage'
export const getItem = (key: string): Promise<string | null> => AsyncStorage.getItem(key)
export const setItem = (key: string, value: string): Promise<void> =>
AsyncStorage.setItem(key, value)
export const removeItem = (key: string): Promise<void> => AsyncStorage.removeItem(key)
-11
View File
@@ -1,11 +0,0 @@
// WEB key/value store — localStorage, matching the async store.ts signature.
export async function getItem(key: string): Promise<string | null> {
return globalThis.localStorage?.getItem(key) ?? null
}
export async function setItem(key: string, value: string): Promise<void> {
globalThis.localStorage?.setItem(key, value)
}
export async function removeItem(key: string): Promise<void> {
globalThis.localStorage?.removeItem(key)
}
-40
View File
@@ -1,40 +0,0 @@
// MIRROR of the canonical hanzoai/ui `pkg/ui/src/product/surfaces.data.ts` — keep
// the data byte-identical. This native app cannot resolve the @hanzo/ui package, so
// the ONE cross-surface app-switcher list is mirrored here. Update all mirrors in the
// same change (hanzoai/ui, hanzoai/team Svelte fork, and here).
export type SurfaceId = 'ai' | 'console' | 'app' | 'chat' | 'bot' | 'team' | 'billing'
/** One Hanzo surface the app switcher offers. */
export interface Surface {
id: SurfaceId
label: string
href: string
hint: string
}
/** The seven Hanzo surfaces (`console` opens the cloud AI console). */
export const SURFACES: Surface[] = [
{ id: 'ai', label: 'Hanzo AI', href: 'https://hanzo.ai', hint: 'hanzo.ai' },
{
id: 'console',
label: 'Console',
href: 'https://console.hanzo.ai',
hint: 'console.hanzo.ai',
},
{ id: 'app', label: 'App', href: 'https://hanzo.app', hint: 'hanzo.app' },
{ id: 'chat', label: 'Chat', href: 'https://hanzo.chat', hint: 'hanzo.chat' },
{ id: 'bot', label: 'Bot', href: 'https://hanzo.bot', hint: 'hanzo.bot' },
{ id: 'team', label: 'Team', href: 'https://hanzo.team', hint: 'hanzo.team' },
{
id: 'billing',
label: 'Billing',
href: 'https://billing.hanzo.ai',
hint: 'billing.hanzo.ai',
},
]
/** Every surface except `current` — a launcher never links to itself. */
export function otherSurfaces(current?: SurfaceId): Surface[] {
return current !== undefined ? SURFACES.filter((s) => s.id !== current) : SURFACES
}
-18
View File
@@ -1,18 +0,0 @@
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler",
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"paths": {
"~/*": ["./*"]
},
"types": ["vite/client"],
"noEmit": true
}
}
-36
View File
@@ -1,36 +0,0 @@
import { hanzoguiPlugin } from '@hanzogui/vite-plugin'
import { one } from 'one/vite'
import type { UserConfig } from 'vite'
// The three addresses config.ts reads, replaced at build time.
//
// A `define` rather than `envPrefix` + `import.meta.env`: this app builds for
// native as well as web, and `import.meta` is not reliable in the native output
// format — the build already warns about it. A define is textual, so it lands
// the same way in both, and an unset variable leaves the `?? production default`
// in config.ts standing.
const endpoints = Object.fromEntries(
['HANZO_API', 'HANZO_ISSUER', 'HANZO_CLIENT_ID']
.filter((name) => process.env[name] != null)
.map((name) => [`process.env.${name}`, JSON.stringify(process.env[name])])
)
export default {
clearScreen: false,
define: endpoints,
plugins: [
one({
ssr: {
dedupeSymlinkedModules: true,
},
web: {
defaultRenderMode: 'spa',
},
}),
hanzoguiPlugin(),
],
} satisfies UserConfig
-47
View File
@@ -379,31 +379,6 @@
"vite-tsconfig-paths": "^6.1.1",
},
},
"apps/team": {
"name": "@hanzogui/team",
"version": "8.3.5",
"dependencies": {
"@hanzo/gui": "workspace:*",
"@hanzogui/config": "workspace:*",
"@hanzogui/core": "workspace:*",
"@hanzogui/lucide-icons-2": "workspace:*",
"@vxrn/color-scheme": "^1.25.8",
"expo": "^57.0.18",
"hanzoai": "^8.5.157",
"one": "^1.25.8",
"react": "19.2.8",
"react-native": "^0.87.1",
"react-native-safe-area-context": "^5.9.1",
"react-native-screens": "^4.27.0",
"react-native-svg": "^15.15.5",
"react-native-web": "^0.21.2",
},
"devDependencies": {
"@hanzogui/vite-plugin": "workspace:*",
"@types/react": "19.2.18",
"vite": "^8.2.2",
},
},
"apps/tests/configs": {
"name": "@hanzogui/configs-test",
"version": "8.3.5",
@@ -4181,8 +4156,6 @@
"@hanzogui/tauri": ["@hanzogui/tauri@workspace:pkgs/tauri"],
"@hanzogui/team": ["@hanzogui/team@workspace:apps/team"],
"@hanzogui/telemetry": ["@hanzogui/telemetry@workspace:pkgs/telemetry"],
"@hanzogui/test-design-system": ["@hanzogui/test-design-system@workspace:pkgs/core/test-design-system"],
@@ -5529,8 +5502,6 @@
"async-retry": ["[email protected]", "", { "dependencies": { "retry": "0.13.1" } }, "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw=="],
"asynckit": ["[email protected]", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"atomically": ["[email protected]", "", { "dependencies": { "stubborn-fs": "^2.0.0", "when-exit": "^2.1.4" } }, "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ=="],
"attr-accept": ["[email protected]", "", {}, "sha512-hmCnJClmeKNKlsBHgbM8yLZRiQZ4/20UXbLJb6OUT16eWcM5/xNZerr80a/zCYob768KIGq++aLrQNTuwPsIOQ=="],
@@ -5543,8 +5514,6 @@
"axe-core": ["[email protected]", "", {}, "sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A=="],
"axios": ["[email protected]", "", { "dependencies": { "follow-redirects": "^1.15.4", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-sGQArzERW2SI8IRkjuJ5y91Sm9QjiRq4Ay4kOLqpbBt5CeKDNq4g6nirJdyD+palK3yEDXnJiVXsesX66AjwyA=="],
"b4a": ["[email protected]", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw=="],
"babel-dead-code-elimination": ["[email protected]", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA=="],
@@ -5775,8 +5744,6 @@
"colorette": ["[email protected]", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="],
"combined-stream": ["[email protected]", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"comma-separated-tokens": ["[email protected]", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
"command-exists": ["[email protected]", "", {}, "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w=="],
@@ -5947,8 +5914,6 @@
"delaunay-find": ["[email protected]", "", { "dependencies": { "delaunator": "^4.0.0" } }, "sha512-1+almjfrnR7ZamBk0q3Nhg6lqSe6Le4vL0WJDSMx4IDbQwTpUTXPjxC00lqLBT8MYsJpPCbI16sIkw9cPsbi7Q=="],
"delayed-stream": ["[email protected]", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"depd": ["[email protected]", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
"dequal": ["[email protected]", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
@@ -6279,8 +6244,6 @@
"fn.name": ["[email protected]", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="],
"follow-redirects": ["[email protected]", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
"fontfaceobserver": ["[email protected]", "", {}, "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg=="],
"fontverter": ["[email protected]", "", { "dependencies": { "wawoff2": "^2.0.0", "woff2sfnt-sfnt2woff": "^1.0.0" } }, "sha512-DFVX5hvXuhi1Jven1tbpebYTCT9XYnvx6/Z+HFUPb7ZRMCW+pj2clU9VMhoTPgWKPhAs7JJDSk3CW1jNUvKCZQ=="],
@@ -6289,8 +6252,6 @@
"foreground-child": ["[email protected]", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
"form-data": ["[email protected]", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.4", "mime-types": "^2.1.35" } }, "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ=="],
"formatly": ["[email protected]", "", { "dependencies": { "fd-package-json": "^2.0.0", "package-manager-detector": "^1.8.0" }, "bin": { "formatly": "bin/index.mjs" } }, "sha512-7CXJtIIA0zy/u12StsYk25qVKxvdLA2ep2sTNxK3ov0mGNIIDqIvAXDSgTnAfDJFsPfWjuz0WjfYSdpvnLA5Tg=="],
"formdata-polyfill": ["[email protected]", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
@@ -6375,8 +6336,6 @@
"handlebars": ["[email protected]", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ=="],
"hanzoai": ["[email protected]", "", { "dependencies": { "axios": "1.18.1" } }, "sha512-slvCSjrpPz8VhIwxxITY7INhOsV7g9lmceKkaTnHUFzbiIUd8WfEWK2WiAvT1Y1YpaPmqXjtCU5P9m+ALOD00g=="],
"happy-dom": ["[email protected]", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-Tz23LR9T9jOGVZm2x1EPdXqwA37G/owYMxRwU0E4miurAtFsPMQ1d2Jc2okUaSjZqAFz2oEn3FLXC5a0a+siyA=="],
"harfbuzzjs": ["[email protected]", "", {}, "sha512-GJnLUrgLMadlMYrBGEXwYEimObbysy3prWT4HyPpFQERvgTU/OZ+ReUlEPOum6w4RBtFXzXiCCmECOr4sz3qwQ=="],
@@ -7271,8 +7230,6 @@
"proxy-addr": ["[email protected]", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
"proxy-from-env": ["[email protected]", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"ps-list": ["[email protected]", "", {}, "sha512-v4Bl6I3f2kJfr5o80ShABNHAokIgY+wFDTQfE+X3zWYgSGQOCBeYptLZUpoOALBqO5EawmDN/tjTldJesd0ujQ=="],
"punycode": ["[email protected]", "", {}, "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="],
@@ -9047,8 +9004,6 @@
"foreground-child/signal-exit": ["[email protected]", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"form-data/mime-types": ["[email protected]", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"globby/ignore": ["[email protected]", "", {}, "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q=="],
"globby/slash": ["[email protected]", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="],
@@ -11581,8 +11536,6 @@
"flow-remove-types/hermes-parser/hermes-estree": ["[email protected]", "", {}, "sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw=="],
"form-data/mime-types/mime-db": ["[email protected]", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"hast-util-from-html/parse5/entities": ["[email protected]", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="],
"import-local/pkg-dir/find-up": ["[email protected]", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
+1 -2
View File
@@ -23,8 +23,7 @@
"./pkgs/*/*",
"./apps/gui.hanzo.ai",
"./pkgs/ui/*",
"./pkgs/ui/*/*",
"./apps/team"
"./pkgs/ui/*/*"
],
"scripts": {
"postinstall": "bun ./scripts/postinstall.ts",
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than
+1 -2
View File
@@ -36,7 +36,7 @@ are reproduced below, as that license requires:
Hanzo GUI is a fork of Tamagui, not a product that bundles it. The git history
in this repository is Tamagui's history continued, and every package in this
workspace except the ten listed below is Tamagui's work, carried here under the
workspace except the nine listed below is Tamagui's work, carried here under the
MIT license above. Renaming those packages into the @hanzogui scope changed what
they are called, not who wrote them.
@@ -50,7 +50,6 @@ Written at Hanzo:
@hanzogui/get pkgs/get
@hanzogui/recipes pkgs/recipes
@hanzogui/font-zen pkgs/core/font-zen
@hanzogui/team apps/team
@hanzogui/desktop apps/desktop
This file is the whole of the attribution, and it has to be exact rather than

Some files were not shown because too many files have changed in this diff Show More