@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/dist/lib/host.js CHANGED
@@ -1,18 +1,20 @@
1
1
  /**
2
- * Which host this export is read on, and what that host keeps.
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
- * `/` once signed in (app/page.tsx), beside /chat. hanzo.team is the
6
- * workspace, its own app (hanzo-apps/team) with Home at its root. This export
7
- * still carries the rooms, so a room asked for on the apex is sent to the same
8
- * room on hanzo.team, and `output: 'export'` has no server to do that, so it is
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
- * The web app, for a signed-in reader, in two modes. Chat is the default, at
36
- * `/`. Dev is @hanzo/build's `<Builder>` at `/dev`, and what it shows is the
37
- * PATH under it, in the builder's own grammar (`route` in @hanzo/build): `/dev`
38
- * New, `/dev/sess_<id>` a run, `/dev/<org>/<repo>` a project,
39
- * `/dev/-/<screen>`, `/dev/-/settings[/<section>]`, `/dev/-/customize[/<tab>]`,
40
- * `/dev/<slug>` a deployed site. The export holds one page for all of them,
41
- * `/dev`, and the edge serves it for every path under it (hanzo.ai's
42
- * lib/edge.ts `APP`).
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
- /** Dev at an app address; '' is Dev's New, `/dev` itself. */
46
- export const app = (path) => {
47
- const p = path.replace(/^\/+|\/+$/g, '');
48
- return p ? `${DEV}/${p}` : DEV;
49
- };
50
- /** The app address a pathname names under `/dev`, or null for a pathname that is not Dev's. */
51
- export const under = (pathname) => {
52
- if (!pathname)
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
- if (pathname === DEV || pathname === `${DEV}/`)
55
- return '';
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(pathname.slice(DEV.length + 1));
69
+ return { mode: 'chat', thread: decodeURIComponent(id) };
60
70
  }
61
71
  catch {
62
- return '';
72
+ return null;
63
73
  }
64
- };
65
- /**
66
- * Chat, the app's default mode: `/` is a new conversation and `?chat=<id>` one
67
- * of the reader's. So the address says which mode the app is in — a path under
68
- * `/dev` for Dev, anything else Chat — and a reload keeps it.
69
- */
70
- export const TALK = 'chat';
71
- /** `/` in Chat, on a conversation, or `/` itself for a new one. */
72
- export const talk = (id) => (id ? `/?${TALK}=${encodeURIComponent(id)}` : '/');
73
- /** A run, as the platform mints its id. */
74
- const SESSION = /^sess_[0-9a-f]{32}$/;
75
- /** A deployed site, as the Sites plane slugs it. */
76
- const SLUG = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
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, project or site, from anywhere: every "Try Hanzo Dev"
81
- * link, a room's Dev, the console's "open in the builder". It signs a stranger
82
- * in on the way (app/(app)/dev/_door.tsx), and is reached through `site()`:
83
- * here on hanzo.ai, across to hanzo.ai from hanzo.team. Only a value that is
84
- * exactly a session id, a repository or a site's slug is carried; anything else
85
- * opens /dev itself, so no caller can make this name another address.
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)) ? app(ref) : DEV);
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}/chat`;
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
@@ -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
  /**
@@ -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
  /**
@@ -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; @hanzo/plans is read only for what the
30
- * wire does not carry — a plan's name for a slug, and the Free row's models.
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; @hanzo/plans is read only for what the
30
- * wire does not carry — a plan's name for a slug, and the Free row's models.
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
@@ -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
- /** Whether `search` names a place in the app. */
35
- export declare const place: (search: string) => boolean;
36
- /**
37
- * `live()`, and a place in the app, as the script `/` runs as the first child
38
- * of its landing (app/_apex.tsx `[data-landing]`), before the rest of the
39
- * landing is parsed (app/page.tsx). Either marks the document with `APP` and
40
- * takes the landing off the page at once: a signed-in reader is never shown the
41
- * pitch while the app loads, and a reader on their way to sign in to a place is
42
- * not shown it while they leave. Same keys, same test. An inline style, because
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
@@ -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
- * An address on `/` that names a place in the app — `?chat` in Chat
63
- * (lib/host.ts `TALK`; Dev's places are paths under `/dev`, whose own page
64
- * signs a stranger in). The app is signed-in only, so a reader nobody has
65
- * signed in is sent to hanzo.id with it rather than shown the landing, and it
66
- * comes back with them.
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 PLACE = new RegExp(`[?&]${TALK}(?:[=&]|$)`);
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 — see
15
- * the note at the top. A name and a size are the file system's, or the
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
- /** Everything the column beside one conversation is holding. */
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. */