@hanzo/rooms 0.1.44 → 0.1.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts +1 -0
- package/dist/App.js +87 -0
- package/package.json +1 -1
package/dist/App.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function App(): import("react").JSX.Element;
|
package/dist/App.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// The web app as the whole window, in two modes: Chat, a conversation with the
|
|
4
|
+
// product's AI, and Dev, @hanzo/build's `<Builder>` without a rail of its own.
|
|
5
|
+
// One column serves both (Column.tsx): the wordmark and the switch, New, Dev's
|
|
6
|
+
// places, the reader's Recents in both modes, and the account. The address says
|
|
7
|
+
// the mode, as a path (lib/host `talk`, `app`, `place`): `/` and `/chat` are
|
|
8
|
+
// Chat on a new conversation, `/chat/<id>` one of the reader's, and a path under
|
|
9
|
+
// `/dev` is Dev: `/dev/run/<id>` a run, `/dev/projects/<org>/<repo>` a project.
|
|
10
|
+
// Moving between the modes is a history entry (`go`), never a load, so a host
|
|
11
|
+
// serves this one component for every path under each.
|
|
12
|
+
//
|
|
13
|
+
// Every host draws this: hanzo.ai at `/`, the desktop app as its main window.
|
|
14
|
+
// The host mounts IamProvider and `<Rooms>` with its router and the path, and
|
|
15
|
+
// draws this only for a signed-in reader; what a stranger sees is the host's.
|
|
16
|
+
//
|
|
17
|
+
// DEV'S PANE HAS NO COLUMN BESIDE IT. The column beside a pane is Chat's (its
|
|
18
|
+
// pinned summary, `useOpen().aside`), and that choice outlives a move to Dev,
|
|
19
|
+
// where nothing fills it: drawn there it was an empty pane to the right of the
|
|
20
|
+
// builder. So Dev's pane is cut on its own and the summary is Chat's alone.
|
|
21
|
+
//
|
|
22
|
+
// THE WINDOW IS A FRAME (`data-slot="frame"`): the column, the pane and the
|
|
23
|
+
// column beside it are three panes floating on a lit floor (ground.ts), one
|
|
24
|
+
// gutter round them and between them, `--pane-gap`, each sized by the edge
|
|
25
|
+
// beside it.
|
|
26
|
+
import { useEffect, useState } from 'react';
|
|
27
|
+
import { Menu } from 'lucide-react';
|
|
28
|
+
import { AiProvider } from '@hanzo/ai/react';
|
|
29
|
+
import { Text, XStack, YStack } from '@hanzo/gui';
|
|
30
|
+
import { Builder, HostProvider, route } from '@hanzo/build';
|
|
31
|
+
import { SidebarIconButton } from '@hanzo/ui/chat';
|
|
32
|
+
import { scrim } from '@hanzo/ui/glass';
|
|
33
|
+
import { useAi } from './lib/ai.js';
|
|
34
|
+
import { Chat } from './Chat.js';
|
|
35
|
+
import { Column } from './Column.js';
|
|
36
|
+
import { useDevHost } from './dev.js';
|
|
37
|
+
import { useRooms } from './host.js';
|
|
38
|
+
import { wordmark } from './ModeMark.js';
|
|
39
|
+
import { empty, openThread, useOpen } from './open.js';
|
|
40
|
+
import { Paned } from './Shell.js';
|
|
41
|
+
import { floor, pane } from './ground.js';
|
|
42
|
+
import { app, go, place, talk } from './lib/host.js';
|
|
43
|
+
/** `/` is Chat on a new conversation. */
|
|
44
|
+
const HOME = { mode: 'chat', thread: null };
|
|
45
|
+
/** Where each mode was last, so the switch goes back to it. */
|
|
46
|
+
const last = { chat: '', dev: '' };
|
|
47
|
+
export function App() {
|
|
48
|
+
const host = useDevHost();
|
|
49
|
+
const { client } = useAi();
|
|
50
|
+
const here = useRooms().path ?? '/';
|
|
51
|
+
const at = place(here) ?? HOME;
|
|
52
|
+
const mode = at.mode;
|
|
53
|
+
// A run floats two panes of its own, so the pane it stands in is not cut.
|
|
54
|
+
const run = at.mode === 'dev' && route(at.at).kind === 'run';
|
|
55
|
+
const asked = at.mode === 'chat' ? at.thread : null;
|
|
56
|
+
const { thread } = useOpen();
|
|
57
|
+
const [drawer, setDrawer] = useState(false);
|
|
58
|
+
// An address names Chat's conversation, and one just begun writes its id back.
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (mode !== 'chat')
|
|
61
|
+
return;
|
|
62
|
+
if (asked && asked !== thread)
|
|
63
|
+
openThread(asked);
|
|
64
|
+
else if (!asked && thread)
|
|
65
|
+
empty();
|
|
66
|
+
// The address decides; the selection follows it.
|
|
67
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
68
|
+
}, [mode, asked]);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (mode === 'chat' && thread && thread !== asked)
|
|
71
|
+
go(talk(thread), true);
|
|
72
|
+
// The selection writes the address only when it moves.
|
|
73
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
|
+
}, [mode, thread]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
last[mode] = `${window.location.pathname}${window.location.search}`;
|
|
77
|
+
setDrawer(false);
|
|
78
|
+
}, [mode, here]);
|
|
79
|
+
const toChat = () => go(last.chat || talk());
|
|
80
|
+
const toDev = () => go(last.dev || app(''));
|
|
81
|
+
return (_jsx(AiProvider, { client: client ?? undefined, children: _jsx(HostProvider, { host: host, children: _jsxs(XStack, { "data-room": "", "data-app": mode, "data-slot": "frame", ...floor, flex: 1, minW: 0, p: "var(--pane-gap)", gap: "var(--pane-gap)", height: "100dvh", overflow: "hidden", "$max-md": {
|
|
82
|
+
p: 0,
|
|
83
|
+
gap: 0,
|
|
84
|
+
bg: 'var(--black)',
|
|
85
|
+
backgroundImage: 'radial-gradient(ellipse 80% 50% at 50% -20%, rgba(255, 255, 255, 0.04), transparent 70%), linear-gradient(180deg, #000000 0%, #030303 50%, #080808 100%)',
|
|
86
|
+
}, children: [_jsx(Column, { host: host, mode: mode, thread: thread, onChat: toChat, onDev: toDev, go: go }), _jsxs(YStack, { "data-slot": "stage", flex: 1, minW: 0, minH: 0, gap: "var(--pane-gap)", "$max-md": { gap: 0 }, children: [_jsxs(XStack, { "data-slot": "app-bar", height: 44, px: "$1", gap: "$1", items: "center", shrink: 0, $md: { display: 'none' }, children: [_jsx(SidebarIconButton, { label: "Open sidebar", onPress: () => setDrawer(true), width: 44, height: 44, rounded: 999, children: _jsx(Menu, { size: 18 }) }), _jsx(Text, { fontSize: "$4", fontWeight: "600", color: "$ink", numberOfLines: 1, children: wordmark(mode) })] }), _jsx(XStack, { "data-slot": "panes", flex: 1, minW: 0, minH: 0, children: mode === 'chat' ? (_jsx(Paned, { framed: true, children: _jsx(Chat, {}) })) : (_jsx(YStack, { render: "main", "data-slot": "pane", ...(run ? null : { ...pane(), overflow: 'hidden' }), flex: 1, minW: 0, minH: 0, children: _jsx(Builder, { host: host, rail: false }) })) })] }), drawer ? (_jsxs(_Fragment, { children: [_jsx(YStack, { render: "button", "aria-label": "Close sidebar", onPress: () => setDrawer(false), ...scrim, position: "absolute", t: 0, r: 0, b: 0, l: 0, z: "var(--z-overlay)", $md: { display: 'none' } }), _jsx(YStack, { "data-slot": "drawer", position: "absolute", t: "var(--pane-gap)", b: "var(--pane-gap)", l: "var(--pane-gap)", width: "min(320px, calc(100% - 2 * var(--pane-gap) - 48px))", z: "var(--z-modal)", $md: { display: 'none' }, children: _jsx(Column, { host: host, mode: mode, thread: thread, onChat: toChat, onDev: toDev, go: go, drawer: true }) })] })) : null] }) }) }));
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/rooms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.45",
|
|
4
4
|
"description": "The workspace rooms — Home, Chat, Inbox, Contacts, Meet, Cal, Drive, Board, Work, Bots — and the frame that holds them, once, for every Hanzo surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|