@hanzo/rooms 0.1.38 → 0.1.39
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/README.md +2 -2
- package/dist/Account.js +18 -9
- package/dist/Chat.js +122 -199
- package/dist/Column.js +16 -15
- package/dist/Directory.d.ts +4 -9
- package/dist/Directory.js +60 -155
- package/dist/Me.js +20 -16
- package/dist/Prose.js +5 -4
- package/dist/RightPane.d.ts +20 -28
- package/dist/RightPane.js +107 -327
- package/dist/Settings.js +42 -28
- package/dist/Shell.js +19 -22
- package/dist/dev.js +42 -26
- package/dist/host.d.ts +5 -9
- package/dist/host.js +5 -6
- package/dist/lib/destination.d.ts +6 -6
- package/dist/lib/destination.js +6 -6
- package/dist/lib/host.d.ts +50 -31
- package/dist/lib/host.js +98 -48
- package/dist/lib/limits.d.ts +13 -9
- package/dist/lib/limits.js +20 -0
- package/dist/lib/plans.d.ts +2 -13
- package/dist/lib/plans.js +2 -19
- package/dist/lib/session.d.ts +9 -12
- package/dist/lib/session.js +7 -19
- package/dist/pane.d.ts +4 -57
- package/dist/pane.js +13 -159
- package/package.json +3 -5
- package/dist/Billing.d.ts +0 -1
- package/dist/Billing.js +0 -32
- package/dist/enso.d.ts +0 -72
- package/dist/enso.js +0 -112
- package/dist/model.d.ts +0 -4
- package/dist/model.js +0 -44
package/dist/lib/host.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Which host this export is read on,
|
|
2
|
+
* Which host this export is read on, what that host keeps, and the app's
|
|
3
|
+
* addresses — the one module that writes and reads them.
|
|
3
4
|
*
|
|
4
5
|
* hanzo.ai is the site for a stranger and the app for a member: the web app is
|
|
5
|
-
*
|
|
6
|
-
* workspace, its own app (hanzo-apps/team) with Home
|
|
7
|
-
* still carries the rooms, so a room asked for on the
|
|
8
|
-
* room on hanzo.team, and `output: 'export'` has no
|
|
9
|
-
* decided in the browser off the hostname, here.
|
|
6
|
+
* /chat and /dev once signed in, and `/` takes a member to the mode they were
|
|
7
|
+
* last in. hanzo.team is the workspace, its own app (hanzo-apps/team) with Home
|
|
8
|
+
* at its root. This export still carries the rooms, so a room asked for on the
|
|
9
|
+
* apex is sent to the same room on hanzo.team, and `output: 'export'` has no
|
|
10
|
+
* server to do that, so it is decided in the browser off the hostname, here.
|
|
10
11
|
*
|
|
11
12
|
* Two predicates, one per product: `apex()` for the app, `team()` for the
|
|
12
13
|
* workspace. Every host that is neither (localhost, a preview) draws the whole
|
|
13
14
|
* workspace and admits the way the apex does.
|
|
14
15
|
*/
|
|
15
16
|
import APP from './rooms.json' with { type: 'json' };
|
|
17
|
+
import { href, parse, path, route, SESSION, SLUG } from '@hanzo/build/route';
|
|
16
18
|
import { site } from '../where.js';
|
|
17
19
|
/** The workspace's own address. */
|
|
18
20
|
export const WORKSPACE = 'https://hanzo.team';
|
|
@@ -32,59 +34,107 @@ export const ENTRY = `${WORKSPACE}/?signin`;
|
|
|
32
34
|
*/
|
|
33
35
|
export const START = `${WORKSPACE}/start`;
|
|
34
36
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
37
|
+
* THE APP'S ADDRESSES ARE PATHS, in two modes at two roots. Chat is /chat, a
|
|
38
|
+
* new conversation, and /chat/<id> one of the reader's. Dev is /dev, Dev's New,
|
|
39
|
+
* and every place of @hanzo/build's builder is a path under it, written and
|
|
40
|
+
* read by `href`/`parse` (@hanzo/build/route): /dev/run/<id>, /dev/projects,
|
|
41
|
+
* /dev/projects/<org>/<repo> a repository, /dev/projects/<slug> a deployed
|
|
42
|
+
* site, /dev/issues, /dev/artifacts, /dev/templates, /dev/automations[/<id>],
|
|
43
|
+
* /dev/machines, /dev/environments, /dev/customize[/<tab>],
|
|
44
|
+
* /dev/settings[/<section>], /dev/plans. The export holds one page per root,
|
|
45
|
+
* /chat and /dev, and the edge serves it for every path under it (hanzo.ai's
|
|
46
|
+
* lib/edge.ts `APP`), so every address reloads.
|
|
43
47
|
*/
|
|
48
|
+
export const CHAT = '/chat';
|
|
44
49
|
export const DEV = '/dev';
|
|
45
|
-
/**
|
|
46
|
-
export const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
/** Chat on a conversation, or /chat itself for a new one. */
|
|
51
|
+
export const talk = (id) => (id ? `${CHAT}/${encodeURIComponent(id)}` : CHAT);
|
|
52
|
+
/** Dev at a builder address (`route` in @hanzo/build); '' is New, /dev. What is not an address is New. */
|
|
53
|
+
export const app = (at) => DEV + href(route(at));
|
|
54
|
+
/** /chat/shared is a page of its own, where a chat shared by link is read (lib/share.ts). */
|
|
55
|
+
const SHARED = 'shared';
|
|
56
|
+
/** The place a path names, or null when it names none of the app's. */
|
|
57
|
+
export function place(pathname) {
|
|
58
|
+
const p = pathname.replace(/\/+$/, '') || '/';
|
|
59
|
+
if (p === CHAT)
|
|
60
|
+
return { mode: 'chat', thread: null };
|
|
61
|
+
if (p === DEV || p.startsWith(`${DEV}/`))
|
|
62
|
+
return { mode: 'dev', at: path(parse(p.slice(DEV.length))) };
|
|
63
|
+
if (!p.startsWith(`${CHAT}/`))
|
|
53
64
|
return null;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (!pathname.startsWith(`${DEV}/`))
|
|
65
|
+
const id = p.slice(CHAT.length + 1);
|
|
66
|
+
if (id.includes('/') || id === SHARED)
|
|
57
67
|
return null;
|
|
58
68
|
try {
|
|
59
|
-
return decodeURIComponent(
|
|
69
|
+
return { mode: 'chat', thread: decodeURIComponent(id) };
|
|
60
70
|
}
|
|
61
71
|
catch {
|
|
62
|
-
return
|
|
72
|
+
return null;
|
|
63
73
|
}
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
}
|
|
75
|
+
/** An address as analytics counts it: a record folded to its kind, so every conversation is one page. */
|
|
76
|
+
export function page(pathname) {
|
|
77
|
+
const at = place(pathname);
|
|
78
|
+
if (!at)
|
|
79
|
+
return pathname;
|
|
80
|
+
if (at.mode === 'chat')
|
|
81
|
+
return at.thread ? `${CHAT}/:id` : CHAT;
|
|
82
|
+
const r = route(at.at);
|
|
83
|
+
if (r.kind === 'run')
|
|
84
|
+
return `${DEV}/run/:id`;
|
|
85
|
+
if (r.kind === 'repo')
|
|
86
|
+
return `${DEV}/projects/:org/:repo`;
|
|
87
|
+
if (r.kind === 'project')
|
|
88
|
+
return `${DEV}/projects/:slug`;
|
|
89
|
+
if (r.kind === 'automation' && r.id !== 'new')
|
|
90
|
+
return `${DEV}/automations/:id`;
|
|
91
|
+
return DEV + href(r);
|
|
92
|
+
}
|
|
77
93
|
/** A repository on the forge, `<org>/<name>`. */
|
|
78
94
|
const REPO = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}\/[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
79
95
|
/**
|
|
80
|
-
* Dev opened on one run,
|
|
81
|
-
* link, a room's Dev, the console's "open in the builder". It signs a
|
|
82
|
-
* in on the way (app/(app)/dev/_door.tsx), and is reached through
|
|
83
|
-
* here on hanzo.ai, across to hanzo.ai from hanzo.team. Only a value
|
|
84
|
-
* exactly a session id, a repository or a site's slug is carried;
|
|
85
|
-
* opens /dev itself, so no caller can make this name another
|
|
96
|
+
* Dev opened on one run, repository or site, from anywhere: every "Try Hanzo
|
|
97
|
+
* Dev" link, a room's Dev, the console's "open in the builder". It signs a
|
|
98
|
+
* stranger in on the way (app/(app)/dev/_door.tsx), and is reached through
|
|
99
|
+
* `site()`: here on hanzo.ai, across to hanzo.ai from hanzo.team. Only a value
|
|
100
|
+
* that is exactly a session id, a repository or a site's slug is carried;
|
|
101
|
+
* anything else opens /dev itself, so no caller can make this name another
|
|
102
|
+
* address.
|
|
86
103
|
*/
|
|
87
|
-
export const dev = (ref) => site(ref && (SESSION.test(ref) || REPO.test(ref) || SLUG.test(ref)) ?
|
|
104
|
+
export const dev = (ref) => site(app(ref && (SESSION.test(ref) || REPO.test(ref) || SLUG.test(ref)) ? ref : ''));
|
|
105
|
+
/**
|
|
106
|
+
* Move the app to an address of this page: a history entry, or in place of this
|
|
107
|
+
* one. No document load and no route change — the Next router folds a native
|
|
108
|
+
* history entry into `usePathname`, so the app follows the path it reads.
|
|
109
|
+
*/
|
|
110
|
+
export function go(address, replace = false) {
|
|
111
|
+
if (`${window.location.pathname}${window.location.search}` === address)
|
|
112
|
+
return;
|
|
113
|
+
if (replace)
|
|
114
|
+
window.history.replaceState(null, '', address);
|
|
115
|
+
else
|
|
116
|
+
window.history.pushState(null, '', address);
|
|
117
|
+
}
|
|
118
|
+
/** Where this browser keeps the mode its reader was last in. */
|
|
119
|
+
const MODE = 'hanzo.app.mode';
|
|
120
|
+
/** Keep the mode the reader is in, for `resume`. */
|
|
121
|
+
export function remember(mode) {
|
|
122
|
+
try {
|
|
123
|
+
window.localStorage.setItem(MODE, mode);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
/* no store: `/` opens Chat */
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** Where `/` takes a member: the mode they were last in, Chat when none is kept. */
|
|
130
|
+
export function resume() {
|
|
131
|
+
try {
|
|
132
|
+
return window.localStorage.getItem(MODE) === 'dev' ? DEV : CHAT;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return CHAT;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
88
138
|
/** The routes under app/(app) the apex keeps: the app — /chat, a chat shared by link, and /dev. */
|
|
89
139
|
const KEPT = new Set(['/chat', '/chat/shared', '/dev']);
|
|
90
140
|
/** Settings, and every page under it: the person's and the organization's, served wherever the app is. */
|
|
@@ -135,7 +185,7 @@ export const GET = 'https://hanzo.app';
|
|
|
135
185
|
* is signed out is asked to sign in there, on hanzo.ai's own page, and lands
|
|
136
186
|
* back in the chat.
|
|
137
187
|
*/
|
|
138
|
-
export const WEB = `${SIGNIN}
|
|
188
|
+
export const WEB = `${SIGNIN}${CHAT}`;
|
|
139
189
|
/**
|
|
140
190
|
* Whether a host serves this export and signs nobody in: hanzo.app, whose root
|
|
141
191
|
* is the page above; every `<slug>.hanzo.app` alias the Sites plane serves it
|
package/dist/lib/limits.d.ts
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The plan's usage, as @hanzo/ui holds it: `GET /v1/ai/limits` read as whoever
|
|
3
|
-
* holds the bearer in this browser's organization, kept current by every AI
|
|
4
|
-
* call `observed` (lib/served.ts) hands to `observe`. Shares only.
|
|
5
|
-
*
|
|
6
|
-
* Same base URL and headers as every billing read (lib/hanzo/tier.ts): the
|
|
7
|
-
* bearer is the subject and `X-Org-Id` the organization this browser works in.
|
|
8
|
-
*/
|
|
9
1
|
import { type UseLimits } from '@hanzo/ui/product/useLimits';
|
|
10
|
-
import { paidPlan } from '@hanzo/ui/product/limits';
|
|
2
|
+
import { paidPlan, type LimitAction, type LimitNotice } from '@hanzo/ui/product/limits';
|
|
11
3
|
/** Reads the limits as whoever holds the bearer, in this browser's organization. */
|
|
12
4
|
export declare function readLimits(signal: AbortSignal): Promise<unknown>;
|
|
13
5
|
/**
|
|
@@ -28,6 +20,18 @@ export declare const useLimits: (enabled?: boolean, org?: string | null, name?:
|
|
|
28
20
|
*/
|
|
29
21
|
export declare const spendShown: ({ limits, answered }: Pick<UseLimits, 'limits' | 'answered'>) => boolean;
|
|
30
22
|
export { paidPlan };
|
|
23
|
+
/**
|
|
24
|
+
* What the banner over the composer says once the reader is turned away, and
|
|
25
|
+
* the ways on. A spent allowance is said by the plan's name and when it comes
|
|
26
|
+
* back — `Your Max 20x plan’s included premium model usage is used until 5:00
|
|
27
|
+
* PM.` — and a spent request window the same way; any other refusal in the
|
|
28
|
+
* server's own words. The ways are @hanzo/build's `ways`: continue with credits
|
|
29
|
+
* where the org holds some, else add credits; then the upgrade.
|
|
30
|
+
*/
|
|
31
|
+
export declare function paused(n: LimitNotice, plan: string, name?: (id: string) => string, now?: number): {
|
|
32
|
+
message: string;
|
|
33
|
+
actions: LimitAction[];
|
|
34
|
+
};
|
|
31
35
|
/** Whether a catalog family is one of Hanzo's chat families, Enso or Zen, whose turns carry the live web. */
|
|
32
36
|
export declare const house: (family: string | undefined) => boolean;
|
|
33
37
|
/**
|
package/dist/lib/limits.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Same base URL and headers as every billing read (lib/hanzo/tier.ts): the
|
|
8
8
|
* bearer is the subject and `X-Org-Id` the organization this browser works in.
|
|
9
9
|
*/
|
|
10
|
+
import { label, said, spent, ways, when } from '@hanzo/build/plan';
|
|
10
11
|
import { useLimits as useHeld } from '@hanzo/ui/product/useLimits';
|
|
11
12
|
import { paidPlan } from '@hanzo/ui/product/limits';
|
|
12
13
|
import { api } from './api.js';
|
|
@@ -47,6 +48,25 @@ export const useLimits = (enabled = true, org = null, name) => useHeld(enabled ?
|
|
|
47
48
|
*/
|
|
48
49
|
export const spendShown = ({ limits, answered }) => answered && !paidPlan(limits);
|
|
49
50
|
export { paidPlan };
|
|
51
|
+
/**
|
|
52
|
+
* What the banner over the composer says once the reader is turned away, and
|
|
53
|
+
* the ways on. A spent allowance is said by the plan's name and when it comes
|
|
54
|
+
* back — `Your Max 20x plan’s included premium model usage is used until 5:00
|
|
55
|
+
* PM.` — and a spent request window the same way; any other refusal in the
|
|
56
|
+
* server's own words. The ways are @hanzo/build's `ways`: continue with credits
|
|
57
|
+
* where the org holds some, else add credits; then the upgrade.
|
|
58
|
+
*/
|
|
59
|
+
export function paused(n, plan, name = (id) => id, now = Date.now()) {
|
|
60
|
+
const l = label(plan);
|
|
61
|
+
const until = when(n.resets_at ?? '', now);
|
|
62
|
+
const on = n.fallback ? ` You’re chatting on ${name(n.fallback)}.` : '';
|
|
63
|
+
const message = n.reason === 'plan_allowance_used' || (!n.reason && n.classes.length > 0)
|
|
64
|
+
? spent(l, n.classes, n.resets_at ?? '', now) + on
|
|
65
|
+
: n.reason === 'usage_cap_exceeded'
|
|
66
|
+
? `Your ${l ? `${said(l)} plan’s` : 'plan’s'} requests are used${until ? ` until ${until}` : ''}.${on}`
|
|
67
|
+
: n.message;
|
|
68
|
+
return { message, actions: ways(n.actions) };
|
|
69
|
+
}
|
|
50
70
|
/** Whether a catalog family is one of Hanzo's chat families, Enso or Zen, whose turns carry the live web. */
|
|
51
71
|
export const house = (family) => family === 'enso' || family === 'zen';
|
|
52
72
|
/**
|
package/dist/lib/plans.d.ts
CHANGED
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
* A SuperAdmin edits these rows at admin.hanzo.ai (/v1/commerce/plans/entries),
|
|
27
27
|
* and every reader here follows: the build bakes the authority's answer into the
|
|
28
28
|
* pricing snapshot for the first paint, and the page replaces it with the live
|
|
29
|
-
* read. No price in this repo is typed
|
|
30
|
-
*
|
|
29
|
+
* read. No price in this repo is typed, and no plan's name: a slug is named by
|
|
30
|
+
* @hanzo/build's `label` (`max-20x` reads Max, with 20x beside it).
|
|
31
31
|
*/
|
|
32
32
|
/** A row exactly as GET /v1/billing/plans returns it. */
|
|
33
33
|
export interface BillingPlan {
|
|
@@ -150,17 +150,6 @@ export declare function charge(plan: SubscriptionPlan, interval: Interval): numb
|
|
|
150
150
|
export declare function quote(plan: SubscriptionPlan, interval: Interval): string | null;
|
|
151
151
|
/** What a year saves against twelve months, as a whole percent: 1 − year ÷ (12 × month). Null where it saves nothing. */
|
|
152
152
|
export declare function saving(plan: SubscriptionPlan): number | null;
|
|
153
|
-
/**
|
|
154
|
-
* The catalog's own name for a plan slug — "max-20x" reads as "Max 20x" —
|
|
155
|
-
* or null where the slug is empty or the catalog does not carry it.
|
|
156
|
-
*
|
|
157
|
-
* Read off the published package: a plan's name does not move between a build and the next release the way
|
|
158
|
-
* its price can, so the bundled copy is not a stale answer here the way it
|
|
159
|
-
* would be for money. A reader of `GET /v1/billing/tier`'s `plan` field
|
|
160
|
-
* needs a name and nothing else, so no live fetch is spent proving what the
|
|
161
|
-
* package already states.
|
|
162
|
-
*/
|
|
163
|
-
export declare function planName(slug: string | null | undefined): string | null;
|
|
164
153
|
/**
|
|
165
154
|
* A usage rate as money. A sub-dollar rate needs at least two decimals to read as
|
|
166
155
|
* a price at all — 0.1 is $0.10, not $0.1 — while keeping the precision the
|
package/dist/lib/plans.js
CHANGED
|
@@ -26,11 +26,10 @@
|
|
|
26
26
|
* A SuperAdmin edits these rows at admin.hanzo.ai (/v1/commerce/plans/entries),
|
|
27
27
|
* and every reader here follows: the build bakes the authority's answer into the
|
|
28
28
|
* pricing snapshot for the first paint, and the page replaces it with the live
|
|
29
|
-
* read. No price in this repo is typed
|
|
30
|
-
*
|
|
29
|
+
* read. No price in this repo is typed, and no plan's name: a slug is named by
|
|
30
|
+
* @hanzo/build's `label` (`max-20x` reads Max, with 20x beside it).
|
|
31
31
|
*/
|
|
32
32
|
import { useEffect, useMemo, useState } from "react";
|
|
33
|
-
import { subscriptionPlans } from "@hanzo/plans";
|
|
34
33
|
import { where } from "../where.js";
|
|
35
34
|
import { api } from "./api.js";
|
|
36
35
|
import { BILLING_URL, checkoutUrl, dollars, money, payUrl } from "./pay.js";
|
|
@@ -222,22 +221,6 @@ export function saving(plan) {
|
|
|
222
221
|
return null;
|
|
223
222
|
return Math.round((1 - year / (12 * month)) * 100);
|
|
224
223
|
}
|
|
225
|
-
/**
|
|
226
|
-
* The catalog's own name for a plan slug — "max-20x" reads as "Max 20x" —
|
|
227
|
-
* or null where the slug is empty or the catalog does not carry it.
|
|
228
|
-
*
|
|
229
|
-
* Read off the published package: a plan's name does not move between a build and the next release the way
|
|
230
|
-
* its price can, so the bundled copy is not a stale answer here the way it
|
|
231
|
-
* would be for money. A reader of `GET /v1/billing/tier`'s `plan` field
|
|
232
|
-
* needs a name and nothing else, so no live fetch is spent proving what the
|
|
233
|
-
* package already states.
|
|
234
|
-
*/
|
|
235
|
-
export function planName(slug) {
|
|
236
|
-
if (!slug)
|
|
237
|
-
return null;
|
|
238
|
-
const row = subscriptionPlans.find((p) => p.id === slug);
|
|
239
|
-
return row?.name ?? null;
|
|
240
|
-
}
|
|
241
224
|
/**
|
|
242
225
|
* A usage rate as money. A sub-dollar rate needs at least two decimals to read as
|
|
243
226
|
* a price at all — 0.1 is $0.10, not $0.1 — while keeping the precision the
|
package/dist/lib/session.d.ts
CHANGED
|
@@ -31,18 +31,15 @@ export declare function bearer(): string | null;
|
|
|
31
31
|
export declare function live(): boolean;
|
|
32
32
|
/** The attribute on `<html>` that says `/` is showing the app, not the landing. */
|
|
33
33
|
export declare const APP = "data-app";
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
* this is decided before any stylesheet or component has run.
|
|
44
|
-
*/
|
|
45
|
-
export declare const BOOT: string;
|
|
34
|
+
/**
|
|
35
|
+
* `live()`, as the script `/` runs as the first child of its landing
|
|
36
|
+
* (app/_apex.tsx `[data-landing]`), before the rest of the landing is parsed
|
|
37
|
+
* (app/page.tsx). A live token marks the document with `APP` and takes the
|
|
38
|
+
* landing off the page at once, so a signed-in reader is never shown the pitch
|
|
39
|
+
* on the way to the app. Same keys, same test. An inline style, because this is
|
|
40
|
+
* decided before any stylesheet or component has run.
|
|
41
|
+
*/
|
|
42
|
+
export declare const BOOT = "try{var t=localStorage.getItem('hanzo_iam_access_token'),e=localStorage.getItem('hanzo_iam_expires_at');if(t&&(!e||Date.now()<Number(e))){document.documentElement.setAttribute('data-app','');var l=document.currentScript&&document.currentScript.parentElement;if(l&&l.hasAttribute('data-landing'))l.style.display='none'}}catch(_){}";
|
|
46
43
|
/**
|
|
47
44
|
* Whether this tab may still ask: a person's browser, and not asked in this tab
|
|
48
45
|
* yet. A browser that refuses storage cannot remember having asked, so it never
|
package/dist/lib/session.js
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
* who was signed in, which reads as a permissions problem rather than a typo.
|
|
15
15
|
* Two derivations of one name never agree; this is the one derivation.
|
|
16
16
|
*/
|
|
17
|
-
import { TALK } from './host.js';
|
|
18
17
|
import { api } from './api.js';
|
|
19
18
|
/** The SDK's own keys, `hanzo_iam_`-prefixed. The far side of a boundary. */
|
|
20
19
|
const PREFIX = 'hanzo_iam_';
|
|
@@ -59,25 +58,14 @@ export function live() {
|
|
|
59
58
|
/** The attribute on `<html>` that says `/` is showing the app, not the landing. */
|
|
60
59
|
export const APP = 'data-app';
|
|
61
60
|
/**
|
|
62
|
-
*
|
|
63
|
-
* (
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
61
|
+
* `live()`, as the script `/` runs as the first child of its landing
|
|
62
|
+
* (app/_apex.tsx `[data-landing]`), before the rest of the landing is parsed
|
|
63
|
+
* (app/page.tsx). A live token marks the document with `APP` and takes the
|
|
64
|
+
* landing off the page at once, so a signed-in reader is never shown the pitch
|
|
65
|
+
* on the way to the app. Same keys, same test. An inline style, because this is
|
|
66
|
+
* decided before any stylesheet or component has run.
|
|
67
67
|
*/
|
|
68
|
-
const
|
|
69
|
-
/** Whether `search` names a place in the app. */
|
|
70
|
-
export const place = (search) => PLACE.test(search);
|
|
71
|
-
/**
|
|
72
|
-
* `live()`, and a place in the app, as the script `/` runs as the first child
|
|
73
|
-
* of its landing (app/_apex.tsx `[data-landing]`), before the rest of the
|
|
74
|
-
* landing is parsed (app/page.tsx). Either marks the document with `APP` and
|
|
75
|
-
* takes the landing off the page at once: a signed-in reader is never shown the
|
|
76
|
-
* pitch while the app loads, and a reader on their way to sign in to a place is
|
|
77
|
-
* not shown it while they leave. Same keys, same test. An inline style, because
|
|
78
|
-
* this is decided before any stylesheet or component has run.
|
|
79
|
-
*/
|
|
80
|
-
export const BOOT = `try{var t=localStorage.getItem('${ACCESS}'),e=localStorage.getItem('${EXPIRES}');if((t&&(!e||Date.now()<Number(e)))||${PLACE}.test(location.search)){document.documentElement.setAttribute('${APP}','');var l=document.currentScript&&document.currentScript.parentElement;if(l&&l.hasAttribute('data-landing'))l.style.display='none'}}catch(_){}`;
|
|
68
|
+
export const BOOT = `try{var t=localStorage.getItem('${ACCESS}'),e=localStorage.getItem('${EXPIRES}');if(t&&(!e||Date.now()<Number(e))){document.documentElement.setAttribute('${APP}','');var l=document.currentScript&&document.currentScript.parentElement;if(l&&l.hasAttribute('data-landing'))l.style.display='none'}}catch(_){}`;
|
|
81
69
|
/**
|
|
82
70
|
* A RETURNING PERSON IS RECOGNISED WITHOUT GOING ANYWHERE.
|
|
83
71
|
*
|
package/dist/pane.d.ts
CHANGED
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
import { type Api, type WorkFile } from './lib/files.js';
|
|
2
|
-
/** One open tab in the pane's browser. */
|
|
3
|
-
export interface Leaf {
|
|
4
|
-
id: string;
|
|
5
|
-
url: string;
|
|
6
|
-
/** What the strip shows: host and path, which is all we can read across an
|
|
7
|
-
* origin we do not own. A frame's real title is not ours to ask for. */
|
|
8
|
-
title: string;
|
|
9
|
-
}
|
|
10
2
|
/**
|
|
11
3
|
* A file put into this conversation, on its way to the workspace or already
|
|
12
4
|
* there, waiting to go with the next message.
|
|
13
5
|
*
|
|
14
|
-
* `href` is an object URL and is the reason this is never written down
|
|
15
|
-
*
|
|
16
|
-
* workspace file's, never a guess.
|
|
6
|
+
* `href` is an object URL and is the reason this is never written down. A name
|
|
7
|
+
* and a size are the file system's, or the workspace file's, never a guess.
|
|
17
8
|
*/
|
|
18
9
|
export interface Held {
|
|
19
10
|
id: string;
|
|
@@ -37,13 +28,8 @@ export interface Via {
|
|
|
37
28
|
api: Api;
|
|
38
29
|
org: string | null;
|
|
39
30
|
}
|
|
40
|
-
/**
|
|
31
|
+
/** What one conversation holds for its next message. */
|
|
41
32
|
export interface Pane {
|
|
42
|
-
tabs: Leaf[];
|
|
43
|
-
/** The chosen tab, or null when this conversation has opened none. */
|
|
44
|
-
at: string | null;
|
|
45
|
-
/** Whether the summary column stands beside the browser. */
|
|
46
|
-
pinned: boolean;
|
|
47
33
|
held: Held[];
|
|
48
34
|
/** Why the last files offered were refused, in the reader's words, or null. */
|
|
49
35
|
refused: string | null;
|
|
@@ -55,52 +41,13 @@ export interface Pane {
|
|
|
55
41
|
* an inbox room arrives over a transport and is keyed by it, a thread is a
|
|
56
42
|
* conversation the org has recorded, and an agent is who you are talking to
|
|
57
43
|
* before a thread exists. Prefixed so a room called `x` and a thread called `x`
|
|
58
|
-
* cannot land on one entry.
|
|
59
|
-
*
|
|
60
|
-
* `new` is a real answer and not a fallback: a room nobody has spoken in yet is
|
|
61
|
-
* somewhere a reader can open tabs, and they should still be there when the
|
|
62
|
-
* first turn mints a thread. It is the ONE key that several conversations pass
|
|
63
|
-
* through, which is the honest cost of not having an id to key on yet.
|
|
44
|
+
* cannot land on one entry. `new` is a room nobody has spoken in yet.
|
|
64
45
|
*/
|
|
65
46
|
export declare function channel(open: {
|
|
66
47
|
room: string | null;
|
|
67
48
|
thread: string | null;
|
|
68
49
|
agent: string | null;
|
|
69
50
|
}): string;
|
|
70
|
-
/** The host and path of an address, which is as much as a title can honestly be. */
|
|
71
|
-
export declare function name(url: string): string;
|
|
72
|
-
/**
|
|
73
|
-
* An address as typed, made into one that can be opened.
|
|
74
|
-
*
|
|
75
|
-
* A bare host is `https`, because that is the web now; a loopback name is
|
|
76
|
-
* `http`, because nothing is listening on 443 of a laptop and offering a
|
|
77
|
-
* reader a URL that cannot connect is worse than guessing.
|
|
78
|
-
*/
|
|
79
|
-
export declare function address(typed: string): string;
|
|
80
|
-
/**
|
|
81
|
-
* Opens an address in this conversation, and chooses it.
|
|
82
|
-
*
|
|
83
|
-
* `called` is for the things that HAVE a name of their own — an answer's
|
|
84
|
-
* `index.html`, a file off the reader's disk — because their address is a blob
|
|
85
|
-
* and `name()` can read nothing useful out of one. Without it the host and path
|
|
86
|
-
* are the honest title.
|
|
87
|
-
*/
|
|
88
|
-
export declare function openTab(key: string, url: string, called?: string): void;
|
|
89
|
-
/** Points an already-open tab somewhere else. */
|
|
90
|
-
export declare function goTab(key: string, id: string, url: string): void;
|
|
91
|
-
/** Chooses an open tab. */
|
|
92
|
-
export declare function pickTab(key: string, id: string): void;
|
|
93
|
-
/**
|
|
94
|
-
* Shuts a tab.
|
|
95
|
-
*
|
|
96
|
-
* THE NEIGHBOUR IS THE ONE TO ITS RIGHT, falling back to its left — what every
|
|
97
|
-
* browser does, and the only choice that does not send the reader somewhere
|
|
98
|
-
* they were not looking. Closing the last tab leaves none chosen rather than
|
|
99
|
-
* refusing, because a pane with no tabs is a state this panel draws.
|
|
100
|
-
*/
|
|
101
|
-
export declare function shutTab(key: string, id: string): void;
|
|
102
|
-
/** Shows or hides the summary column beside this conversation's browser. */
|
|
103
|
-
export declare function pin(key: string, on: boolean): void;
|
|
104
51
|
/** The workspace file `id` as the index last said, or undefined until it has. */
|
|
105
52
|
export declare function useWorkFile(id: string | undefined): WorkFile | undefined;
|
|
106
53
|
/** The object URL over workspace file `id`'s bytes on this machine, or '' where this document never held them. */
|