@hanzo/rooms 0.1.0
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/LICENSE-APACHE +205 -0
- package/LICENSE-MIT +21 -0
- package/README.md +23 -0
- package/dist/Account.d.ts +8 -0
- package/dist/Account.js +58 -0
- package/dist/Beluga.d.ts +12 -0
- package/dist/Beluga.js +152 -0
- package/dist/Billing.d.ts +1 -0
- package/dist/Billing.js +31 -0
- package/dist/Board.d.ts +9 -0
- package/dist/Board.js +447 -0
- package/dist/Boxes.d.ts +1 -0
- package/dist/Boxes.js +173 -0
- package/dist/CalScheduler.d.ts +15 -0
- package/dist/CalScheduler.js +196 -0
- package/dist/Chat.d.ts +35 -0
- package/dist/Chat.js +1049 -0
- package/dist/Column.d.ts +15 -0
- package/dist/Column.js +88 -0
- package/dist/Contacts.d.ts +1 -0
- package/dist/Contacts.js +301 -0
- package/dist/Directory.d.ts +24 -0
- package/dist/Directory.js +356 -0
- package/dist/Drive.d.ts +2 -0
- package/dist/Drive.js +466 -0
- package/dist/Find.d.ts +7 -0
- package/dist/Find.js +82 -0
- package/dist/Guide.d.ts +6 -0
- package/dist/Guide.js +272 -0
- package/dist/Home.d.ts +39 -0
- package/dist/Home.js +306 -0
- package/dist/Inbox.d.ts +9 -0
- package/dist/Inbox.js +193 -0
- package/dist/Market.d.ts +3 -0
- package/dist/Market.js +149 -0
- package/dist/Me.d.ts +27 -0
- package/dist/Me.js +251 -0
- package/dist/Meet.d.ts +9 -0
- package/dist/Meet.js +370 -0
- package/dist/ModeMark.d.ts +8 -0
- package/dist/ModeMark.js +18 -0
- package/dist/OrgSwitcher.d.ts +31 -0
- package/dist/OrgSwitcher.js +120 -0
- package/dist/Orgs.d.ts +10 -0
- package/dist/Orgs.js +582 -0
- package/dist/Profile.d.ts +4 -0
- package/dist/Profile.js +67 -0
- package/dist/Prose.d.ts +5 -0
- package/dist/Prose.js +111 -0
- package/dist/ProviderMark.d.ts +17 -0
- package/dist/ProviderMark.js +277 -0
- package/dist/Recents.d.ts +10 -0
- package/dist/Recents.js +91 -0
- package/dist/RightPane.d.ts +13 -0
- package/dist/RightPane.js +426 -0
- package/dist/Room.d.ts +8 -0
- package/dist/Room.js +50 -0
- package/dist/Scene.d.ts +12 -0
- package/dist/Scene.js +105 -0
- package/dist/Settings.d.ts +46 -0
- package/dist/Settings.js +321 -0
- package/dist/Setup.d.ts +1 -0
- package/dist/Setup.js +36 -0
- package/dist/Share.d.ts +3 -0
- package/dist/Share.js +130 -0
- package/dist/Shared.d.ts +1 -0
- package/dist/Shared.js +113 -0
- package/dist/Shell.d.ts +63 -0
- package/dist/Shell.js +1225 -0
- package/dist/Start.d.ts +1 -0
- package/dist/Start.js +96 -0
- package/dist/Support.d.ts +14 -0
- package/dist/Support.js +122 -0
- package/dist/Talk.d.ts +4 -0
- package/dist/Talk.js +107 -0
- package/dist/Upgrade.d.ts +11 -0
- package/dist/Upgrade.js +75 -0
- package/dist/Usage.d.ts +1 -0
- package/dist/Usage.js +21 -0
- package/dist/Vibe.d.ts +2 -0
- package/dist/Vibe.js +188 -0
- package/dist/Work.d.ts +12 -0
- package/dist/Work.js +256 -0
- package/dist/bots/Bots.d.ts +2 -0
- package/dist/bots/Bots.js +456 -0
- package/dist/bots/Lights.d.ts +8 -0
- package/dist/bots/Lights.js +13 -0
- package/dist/bots/api.d.ts +120 -0
- package/dist/bots/api.js +75 -0
- package/dist/bots/face.d.ts +5 -0
- package/dist/bots/face.js +13 -0
- package/dist/bots/ink.d.ts +7 -0
- package/dist/bots/ink.js +29 -0
- package/dist/bots/kit.d.ts +113 -0
- package/dist/bots/kit.js +43 -0
- package/dist/bots/runtime.d.ts +4 -0
- package/dist/bots/runtime.js +326 -0
- package/dist/card.d.ts +52 -0
- package/dist/card.js +52 -0
- package/dist/cast.d.ts +143 -0
- package/dist/cast.js +251 -0
- package/dist/connect.d.ts +29 -0
- package/dist/connect.js +64 -0
- package/dist/contacts-store.d.ts +111 -0
- package/dist/contacts-store.js +276 -0
- package/dist/conversations.d.ts +54 -0
- package/dist/conversations.js +98 -0
- package/dist/copy.d.ts +23 -0
- package/dist/copy.js +39 -0
- package/dist/crew.d.ts +36 -0
- package/dist/crew.js +304 -0
- package/dist/dev.d.ts +3 -0
- package/dist/dev.js +102 -0
- package/dist/enso.d.ts +70 -0
- package/dist/enso.js +112 -0
- package/dist/failure.d.ts +57 -0
- package/dist/failure.js +143 -0
- package/dist/ground.d.ts +32 -0
- package/dist/ground.js +32 -0
- package/dist/host.d.ts +76 -0
- package/dist/host.js +76 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +12 -0
- package/dist/kept.d.ts +5 -0
- package/dist/kept.js +27 -0
- package/dist/lib/account.d.ts +49 -0
- package/dist/lib/account.js +229 -0
- package/dist/lib/ai.d.ts +46 -0
- package/dist/lib/ai.js +140 -0
- package/dist/lib/api.d.ts +43 -0
- package/dist/lib/api.js +62 -0
- package/dist/lib/bare.d.ts +9 -0
- package/dist/lib/bare.js +7 -0
- package/dist/lib/coding.d.ts +105 -0
- package/dist/lib/coding.js +87 -0
- package/dist/lib/destination.d.ts +102 -0
- package/dist/lib/destination.js +260 -0
- package/dist/lib/durable.d.ts +66 -0
- package/dist/lib/durable.js +107 -0
- package/dist/lib/first.d.ts +11 -0
- package/dist/lib/first.js +23 -0
- package/dist/lib/home.d.ts +61 -0
- package/dist/lib/home.js +54 -0
- package/dist/lib/host.d.ts +104 -0
- package/dist/lib/host.js +132 -0
- package/dist/lib/hostname.d.ts +1 -0
- package/dist/lib/hostname.js +14 -0
- package/dist/lib/hydrated.d.ts +1 -0
- package/dist/lib/hydrated.js +20 -0
- package/dist/lib/integrations.d.ts +21 -0
- package/dist/lib/integrations.js +1 -0
- package/dist/lib/intent.d.ts +6 -0
- package/dist/lib/intent.js +71 -0
- package/dist/lib/invite.d.ts +155 -0
- package/dist/lib/invite.js +245 -0
- package/dist/lib/limits.d.ts +55 -0
- package/dist/lib/limits.js +119 -0
- package/dist/lib/list.d.ts +9 -0
- package/dist/lib/list.js +11 -0
- package/dist/lib/mark.d.ts +13 -0
- package/dist/lib/mark.js +85 -0
- package/dist/lib/mix.d.ts +22 -0
- package/dist/lib/mix.js +22 -0
- package/dist/lib/models.d.ts +27 -0
- package/dist/lib/models.js +106 -0
- package/dist/lib/org.d.ts +109 -0
- package/dist/lib/org.js +140 -0
- package/dist/lib/path.d.ts +9 -0
- package/dist/lib/path.js +29 -0
- package/dist/lib/plans.d.ts +193 -0
- package/dist/lib/plans.js +310 -0
- package/dist/lib/prose.d.ts +13 -0
- package/dist/lib/prose.js +62 -0
- package/dist/lib/reach.d.ts +17 -0
- package/dist/lib/reach.js +70 -0
- package/dist/lib/rooms.json +23 -0
- package/dist/lib/served.d.ts +20 -0
- package/dist/lib/served.js +92 -0
- package/dist/lib/session.d.ts +211 -0
- package/dist/lib/session.js +493 -0
- package/dist/lib/share.d.ts +131 -0
- package/dist/lib/share.js +216 -0
- package/dist/lib/signout.d.ts +14 -0
- package/dist/lib/signout.js +40 -0
- package/dist/lib/tags.d.ts +27 -0
- package/dist/lib/tags.js +46 -0
- package/dist/lib/tap.d.ts +17 -0
- package/dist/lib/tap.js +9 -0
- package/dist/lib/tier.d.ts +115 -0
- package/dist/lib/tier.js +207 -0
- package/dist/lib/tiers.d.ts +9 -0
- package/dist/lib/tiers.js +141 -0
- package/dist/lib/todo.d.ts +125 -0
- package/dist/lib/todo.js +379 -0
- package/dist/look.d.ts +1 -0
- package/dist/look.js +21 -0
- package/dist/meters.d.ts +8 -0
- package/dist/meters.js +31 -0
- package/dist/model.d.ts +4 -0
- package/dist/model.js +44 -0
- package/dist/network.d.ts +64 -0
- package/dist/network.js +216 -0
- package/dist/open.d.ts +81 -0
- package/dist/open.js +149 -0
- package/dist/pane.d.ts +92 -0
- package/dist/pane.js +231 -0
- package/dist/progress.d.ts +46 -0
- package/dist/progress.js +51 -0
- package/dist/recent.d.ts +67 -0
- package/dist/recent.js +161 -0
- package/dist/rooms.d.ts +131 -0
- package/dist/rooms.js +195 -0
- package/dist/roster.d.ts +28 -0
- package/dist/roster.js +70 -0
- package/dist/span.d.ts +14 -0
- package/dist/span.js +50 -0
- package/dist/speech.d.ts +44 -0
- package/dist/speech.js +229 -0
- package/dist/stars.d.ts +16 -0
- package/dist/stars.js +75 -0
- package/dist/tabs.d.ts +26 -0
- package/dist/tabs.js +25 -0
- package/dist/team.d.ts +11 -0
- package/dist/team.gen.d.ts +10 -0
- package/dist/team.gen.js +11 -0
- package/dist/team.js +30 -0
- package/dist/tools.d.ts +35 -0
- package/dist/tools.js +143 -0
- package/dist/tours.d.ts +24 -0
- package/dist/tours.js +311 -0
- package/dist/waiting.d.ts +15 -0
- package/dist/waiting.js +17 -0
- package/dist/where.d.ts +54 -0
- package/dist/where.js +14 -0
- package/package.json +108 -0
package/dist/lib/todo.js
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* THE COLUMNS, and they are the platform's own closed set rather than a list a
|
|
4
|
+
* reader types. `newIssue` names them: "backlog, todo, in_progress, done or
|
|
5
|
+
* canceled". A column that is not one of these cannot be moved to, because the
|
|
6
|
+
* move is a relabel and the forge would not know the label.
|
|
7
|
+
*/
|
|
8
|
+
export const COLUMNS = [
|
|
9
|
+
{ id: 'backlog', label: 'Backlog' },
|
|
10
|
+
{ id: 'todo', label: 'To do' },
|
|
11
|
+
{ id: 'in_progress', label: 'Doing' },
|
|
12
|
+
{ id: 'done', label: 'Done' },
|
|
13
|
+
{ id: 'canceled', label: 'Canceled' },
|
|
14
|
+
];
|
|
15
|
+
/** The priorities the store accepts, loudest first. */
|
|
16
|
+
export const PRIORITIES = ['urgent', 'high', 'medium', 'low', 'none'];
|
|
17
|
+
const STORAGE_KEY = 'hanzo_board_cards';
|
|
18
|
+
export const DEFAULT_CARDS = [
|
|
19
|
+
{
|
|
20
|
+
id: 'hanzo-1',
|
|
21
|
+
identifier: 'HANZO#1',
|
|
22
|
+
projectKey: 'hanzo',
|
|
23
|
+
number: 1,
|
|
24
|
+
kind: 'task',
|
|
25
|
+
source: 'agent',
|
|
26
|
+
title: 'Deploy 3D Gaussian Splat Whale & Video Reconstructor',
|
|
27
|
+
description: '- [x] Build 148,000 particle oceanic kinematics\n- [x] Integrate real-time WebXR canvas\n- [x] Add custom .mp4 / .ply dropzone',
|
|
28
|
+
status: 'done',
|
|
29
|
+
priority: 'high',
|
|
30
|
+
assignee: 'Architect',
|
|
31
|
+
labels: ['3d', 'splat', 'webxr'],
|
|
32
|
+
createdAt: Date.now() - 3600000 * 4,
|
|
33
|
+
updatedAt: Date.now() - 3600000 * 2,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'hanzo-2',
|
|
37
|
+
identifier: 'HANZO#2',
|
|
38
|
+
projectKey: 'hanzo',
|
|
39
|
+
number: 2,
|
|
40
|
+
kind: 'task',
|
|
41
|
+
source: 'team',
|
|
42
|
+
title: 'Sovereign Cloud Exec Runner & MicroVM Sandbox',
|
|
43
|
+
description: '- [x] Mount /v1/exec native command runner\n- [x] Add gVisor runtime container isolation\n- [ ] Add real-time PTY terminal streaming',
|
|
44
|
+
status: 'in_progress',
|
|
45
|
+
priority: 'urgent',
|
|
46
|
+
assignee: 'Satoshi',
|
|
47
|
+
labels: ['cloud', 'microvm', 'sandbox'],
|
|
48
|
+
createdAt: Date.now() - 3600000 * 8,
|
|
49
|
+
updatedAt: Date.now() - 3600000 * 1,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'hanzo-3',
|
|
53
|
+
identifier: 'HANZO#3',
|
|
54
|
+
projectKey: 'hanzo',
|
|
55
|
+
number: 3,
|
|
56
|
+
kind: 'task',
|
|
57
|
+
source: 'team',
|
|
58
|
+
title: 'Pay & Billing Architecture Unification',
|
|
59
|
+
description: '- [x] Fix root basePath for billing.hanzo.ai\n- [x] Re-route commerceUrl to api.hanzo.ai\n- [ ] Enable multi-org ledger checkout',
|
|
60
|
+
status: 'todo',
|
|
61
|
+
priority: 'high',
|
|
62
|
+
assignee: 'Turing',
|
|
63
|
+
labels: ['billing', 'pay', 'checkout'],
|
|
64
|
+
createdAt: Date.now() - 3600000 * 12,
|
|
65
|
+
updatedAt: Date.now() - 3600000 * 3,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'hanzo-4',
|
|
69
|
+
identifier: 'HANZO#4',
|
|
70
|
+
projectKey: 'hanzo',
|
|
71
|
+
number: 4,
|
|
72
|
+
kind: 'task',
|
|
73
|
+
source: 'agent',
|
|
74
|
+
title: 'Autonomous Swarm Collaboration in Channels',
|
|
75
|
+
description: '- [x] Route multi-agent turns with voice dictation\n- [ ] Sync forge issues directly into channel threads',
|
|
76
|
+
status: 'backlog',
|
|
77
|
+
priority: 'medium',
|
|
78
|
+
assignee: 'Ada',
|
|
79
|
+
labels: ['swarm', 'chat', 'agents'],
|
|
80
|
+
createdAt: Date.now() - 3600000 * 24,
|
|
81
|
+
updatedAt: Date.now() - 3600000 * 12,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
function getLocalCards(key) {
|
|
85
|
+
if (typeof window === 'undefined')
|
|
86
|
+
return DEFAULT_CARDS;
|
|
87
|
+
try {
|
|
88
|
+
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
89
|
+
const cards = raw ? JSON.parse(raw) : DEFAULT_CARDS;
|
|
90
|
+
const list = Array.isArray(cards) && cards.length > 0 ? cards : DEFAULT_CARDS;
|
|
91
|
+
if (!key || key === 'all')
|
|
92
|
+
return list;
|
|
93
|
+
const filtered = list.filter((c) => c.projectKey?.toLowerCase() === key.toLowerCase());
|
|
94
|
+
if (filtered.length > 0)
|
|
95
|
+
return filtered;
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
id: `${key}-1`,
|
|
99
|
+
identifier: `${key.toUpperCase()}#1`,
|
|
100
|
+
projectKey: key,
|
|
101
|
+
number: 1,
|
|
102
|
+
kind: 'task',
|
|
103
|
+
source: 'team',
|
|
104
|
+
title: `Welcome to ${key} board`,
|
|
105
|
+
description: '- [ ] Add tasks\n- [ ] Configure workflow',
|
|
106
|
+
status: 'todo',
|
|
107
|
+
priority: 'medium',
|
|
108
|
+
labels: [key],
|
|
109
|
+
createdAt: Date.now(),
|
|
110
|
+
updatedAt: Date.now(),
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return DEFAULT_CARDS;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function saveLocalCards(cards) {
|
|
119
|
+
if (typeof window === 'undefined')
|
|
120
|
+
return;
|
|
121
|
+
try {
|
|
122
|
+
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
123
|
+
const existing = raw ? JSON.parse(raw) : DEFAULT_CARDS;
|
|
124
|
+
const map = new Map();
|
|
125
|
+
for (const c of existing)
|
|
126
|
+
map.set(c.id || c.identifier, c);
|
|
127
|
+
for (const c of cards)
|
|
128
|
+
map.set(c.id || c.identifier, c);
|
|
129
|
+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(Array.from(map.values())));
|
|
130
|
+
}
|
|
131
|
+
catch { }
|
|
132
|
+
}
|
|
133
|
+
/** Every card on the org's board, or one project's when `key` is given. */
|
|
134
|
+
export async function board(client, params = {}, signal) {
|
|
135
|
+
if (!client)
|
|
136
|
+
return getLocalCards(params.key);
|
|
137
|
+
try {
|
|
138
|
+
const query = {};
|
|
139
|
+
if (params.key)
|
|
140
|
+
query.key = params.key;
|
|
141
|
+
if (params.status)
|
|
142
|
+
query.status = params.status;
|
|
143
|
+
if (params.label)
|
|
144
|
+
query.label = params.label;
|
|
145
|
+
if (params.scheduled !== undefined)
|
|
146
|
+
query.scheduled = String(params.scheduled);
|
|
147
|
+
const out = await client.http.json({
|
|
148
|
+
path: '/v1/todo/board',
|
|
149
|
+
query,
|
|
150
|
+
signal,
|
|
151
|
+
});
|
|
152
|
+
const fetched = rows(out);
|
|
153
|
+
// A SUCCESSFUL READ IS THE ANSWER, EMPTY OR NOT. An org whose board holds
|
|
154
|
+
// no cards yet is a fact about the org — the empty state a reader is owed
|
|
155
|
+
// — not a fact this browser failed to learn. Treating the two alike is
|
|
156
|
+
// what used to hand a brand-new org four invented demo cards on its first
|
|
157
|
+
// paint: the store answered `[]`, this fell to the local cache, and the
|
|
158
|
+
// cache had never been written for that org, so `DEFAULT_CARDS` stood in
|
|
159
|
+
// as if it were real. Only a request that never came back reaches the
|
|
160
|
+
// fallback below.
|
|
161
|
+
if (fetched.length > 0)
|
|
162
|
+
saveLocalCards(fetched);
|
|
163
|
+
return fetched;
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
const code = e?.status;
|
|
167
|
+
if (typeof code === 'number' && code >= 500)
|
|
168
|
+
throw e;
|
|
169
|
+
return getLocalCards(params.key);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/** The boards themselves — a project is a repository on the forge. */
|
|
173
|
+
export async function projects(client, signal) {
|
|
174
|
+
const fallback = [
|
|
175
|
+
{ key: 'hanzo', name: 'Hanzo Core' },
|
|
176
|
+
{ key: 'cloud', name: 'Hanzo Cloud' },
|
|
177
|
+
{ key: 'swarm', name: 'Agent Swarm' },
|
|
178
|
+
{ key: 'dev', name: 'Hanzo Dev' },
|
|
179
|
+
];
|
|
180
|
+
if (!client)
|
|
181
|
+
return fallback;
|
|
182
|
+
try {
|
|
183
|
+
const [todoPs, devPs] = await Promise.all([
|
|
184
|
+
client.http
|
|
185
|
+
.json({
|
|
186
|
+
path: '/v1/todo/projects',
|
|
187
|
+
signal,
|
|
188
|
+
})
|
|
189
|
+
.catch(() => null),
|
|
190
|
+
client.http
|
|
191
|
+
.json({
|
|
192
|
+
path: '/v1/projects',
|
|
193
|
+
signal,
|
|
194
|
+
})
|
|
195
|
+
.catch(() => null),
|
|
196
|
+
]);
|
|
197
|
+
const map = new Map();
|
|
198
|
+
for (const f of fallback)
|
|
199
|
+
map.set(f.key, f);
|
|
200
|
+
const todoList = rows(todoPs);
|
|
201
|
+
for (const p of todoList ?? []) {
|
|
202
|
+
if (p?.key)
|
|
203
|
+
map.set(p.key, { key: p.key, name: p.name || p.key });
|
|
204
|
+
}
|
|
205
|
+
for (const p of devPs ?? []) {
|
|
206
|
+
const k = (p.name || p.id).toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
207
|
+
if (k && !map.has(k))
|
|
208
|
+
map.set(k, { key: k, name: p.name || k });
|
|
209
|
+
}
|
|
210
|
+
return Array.from(map.values());
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return fallback;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/** Opens a card in a column. */
|
|
217
|
+
export async function open(client, key, card, signal) {
|
|
218
|
+
const current = getLocalCards();
|
|
219
|
+
const nextNum = current.length > 0 ? Math.max(...current.map((c) => c.number || 0)) + 1 : 1;
|
|
220
|
+
const newCard = {
|
|
221
|
+
id: `${key}-${nextNum}-${Date.now()}`,
|
|
222
|
+
identifier: `${key.toUpperCase()}#${nextNum}`,
|
|
223
|
+
projectKey: key || 'hanzo',
|
|
224
|
+
number: nextNum,
|
|
225
|
+
kind: 'task',
|
|
226
|
+
source: 'team',
|
|
227
|
+
title: card.title,
|
|
228
|
+
description: card.description ?? '',
|
|
229
|
+
status: card.status ?? 'backlog',
|
|
230
|
+
priority: card.priority ?? 'medium',
|
|
231
|
+
labels: card.labels ?? ['task'],
|
|
232
|
+
createdAt: Date.now(),
|
|
233
|
+
updatedAt: Date.now(),
|
|
234
|
+
};
|
|
235
|
+
if (client) {
|
|
236
|
+
try {
|
|
237
|
+
const created = await client.http.json({
|
|
238
|
+
method: 'POST',
|
|
239
|
+
path: `/v1/todo/projects/${encodeURIComponent(key)}/issues`,
|
|
240
|
+
body: card,
|
|
241
|
+
signal,
|
|
242
|
+
});
|
|
243
|
+
if (created?.id) {
|
|
244
|
+
saveLocalCards([created, ...current]);
|
|
245
|
+
return created;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// Fall through to local save
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
saveLocalCards([newCard, ...current]);
|
|
253
|
+
return newCard;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Edits a card. Absent fields are left alone.
|
|
257
|
+
*/
|
|
258
|
+
export async function edit(client, key, num, patch, signal) {
|
|
259
|
+
const current = getLocalCards();
|
|
260
|
+
let updatedCard = null;
|
|
261
|
+
const updatedList = current.map((c) => {
|
|
262
|
+
if (c.number === num || c.identifier.endsWith(`#${num}`)) {
|
|
263
|
+
updatedCard = {
|
|
264
|
+
...c,
|
|
265
|
+
...patch,
|
|
266
|
+
updatedAt: Date.now(),
|
|
267
|
+
};
|
|
268
|
+
return updatedCard;
|
|
269
|
+
}
|
|
270
|
+
return c;
|
|
271
|
+
});
|
|
272
|
+
if (updatedCard) {
|
|
273
|
+
saveLocalCards(updatedList);
|
|
274
|
+
}
|
|
275
|
+
if (client) {
|
|
276
|
+
try {
|
|
277
|
+
const serverUpdated = await client.http.json({
|
|
278
|
+
method: 'PATCH',
|
|
279
|
+
path: `/v1/todo/projects/${encodeURIComponent(key)}/issues/${num}`,
|
|
280
|
+
body: patch,
|
|
281
|
+
signal,
|
|
282
|
+
});
|
|
283
|
+
if (serverUpdated?.id) {
|
|
284
|
+
return serverUpdated;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch { }
|
|
288
|
+
}
|
|
289
|
+
return updatedCard ?? current[0];
|
|
290
|
+
}
|
|
291
|
+
/** Takes the card — the store's own verb for who is doing the work. */
|
|
292
|
+
export async function claim(client, key, num, signal) {
|
|
293
|
+
if (client) {
|
|
294
|
+
try {
|
|
295
|
+
return await client.http.json({
|
|
296
|
+
method: 'POST',
|
|
297
|
+
path: `/v1/todo/projects/${encodeURIComponent(key)}/issues/${num}/claim`,
|
|
298
|
+
body: {},
|
|
299
|
+
signal,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
catch { }
|
|
303
|
+
}
|
|
304
|
+
return edit(client, key, num, { assignee: 'me' }, signal);
|
|
305
|
+
}
|
|
306
|
+
/** The rows out of whichever envelope the route used. */
|
|
307
|
+
function rows(out) {
|
|
308
|
+
if (Array.isArray(out))
|
|
309
|
+
return out;
|
|
310
|
+
return out?.issues ?? out?.items ?? out?.projects ?? [];
|
|
311
|
+
}
|
|
312
|
+
const TASK = /^(\s*)[-*]\s+\[([ xX])\]\s?(.*)$/;
|
|
313
|
+
/**
|
|
314
|
+
* The task items in a description.
|
|
315
|
+
*/
|
|
316
|
+
export function steps(description = '') {
|
|
317
|
+
const out = [];
|
|
318
|
+
description.split('\n').forEach((raw, line) => {
|
|
319
|
+
const m = TASK.exec(raw);
|
|
320
|
+
if (m)
|
|
321
|
+
out.push({ done: m[2].toLowerCase() === 'x', text: m[3].trim(), line });
|
|
322
|
+
});
|
|
323
|
+
return out;
|
|
324
|
+
}
|
|
325
|
+
/** The description with one step's box flipped, and every other line untouched. */
|
|
326
|
+
export function tick(description, line, done) {
|
|
327
|
+
const lines = description.split('\n');
|
|
328
|
+
const m = TASK.exec(lines[line] ?? '');
|
|
329
|
+
if (!m)
|
|
330
|
+
return description;
|
|
331
|
+
lines[line] = `${m[1]}- [${done ? 'x' : ' '}] ${m[3]}`;
|
|
332
|
+
return lines.join('\n');
|
|
333
|
+
}
|
|
334
|
+
/** The description with one more step at the end of its list. */
|
|
335
|
+
export function addStep(description, text) {
|
|
336
|
+
const body = description.trimEnd();
|
|
337
|
+
return `${body}${body ? '\n' : ''}- [ ] ${text.trim()}`;
|
|
338
|
+
}
|
|
339
|
+
export const SETTLED = new Set(['done', 'canceled']);
|
|
340
|
+
export function sift(cards, want, me, agents) {
|
|
341
|
+
const now = Date.now() / 1000;
|
|
342
|
+
const within = { day: 1, week: 7, month: 31 };
|
|
343
|
+
return cards.filter((c) => {
|
|
344
|
+
if (want.holder === 'none' && c.assignee)
|
|
345
|
+
return false;
|
|
346
|
+
if (want.holder === 'me' && c.assignee !== me)
|
|
347
|
+
return false;
|
|
348
|
+
if (want.holder && !['none', 'me'].includes(want.holder) && c.assignee !== want.holder)
|
|
349
|
+
return false;
|
|
350
|
+
if (want.by === 'agent' && !(c.source === 'agent' || (c.assignee && agents?.has(c.assignee))))
|
|
351
|
+
return false;
|
|
352
|
+
if (want.by === 'person' && (c.source === 'agent' || (c.assignee && agents?.has(c.assignee))))
|
|
353
|
+
return false;
|
|
354
|
+
if (want.state === 'done' && !SETTLED.has(c.status))
|
|
355
|
+
return false;
|
|
356
|
+
if (want.state === 'open' && SETTLED.has(c.status))
|
|
357
|
+
return false;
|
|
358
|
+
if (want.due === 'none' && c.dueAt)
|
|
359
|
+
return false;
|
|
360
|
+
if (want.due && want.due !== 'none') {
|
|
361
|
+
if (!c.dueAt)
|
|
362
|
+
return false;
|
|
363
|
+
if (want.due === 'late' && c.dueAt >= now)
|
|
364
|
+
return false;
|
|
365
|
+
if (want.due !== 'late' && !(c.dueAt >= now && c.dueAt <= now + within[want.due] * 86400))
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
if (want.labels?.length && !want.labels.some((l) => c.labels.includes(l)))
|
|
369
|
+
return false;
|
|
370
|
+
if (want.active && !(c.updatedAt >= now - want.active * 86400))
|
|
371
|
+
return false;
|
|
372
|
+
if (want.text) {
|
|
373
|
+
const hay = `${c.title} ${c.description ?? ''}`.toLowerCase();
|
|
374
|
+
if (!hay.includes(want.text.toLowerCase()))
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
return true;
|
|
378
|
+
});
|
|
379
|
+
}
|
package/dist/look.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function Look(): import("react").JSX.Element;
|
package/dist/look.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* HOW THE APP LOOKS.
|
|
5
|
+
*
|
|
6
|
+
* One panel, `@hanzo/appearance`'s: theme, type, spacing, corners and accent,
|
|
7
|
+
* resolved for the organization in scope and kept with the person's IAM
|
|
8
|
+
* account, so it follows them to every Hanzo surface. The theme used to be a
|
|
9
|
+
* row of its own here, written to next-themes and to IAM's per-subject key —
|
|
10
|
+
* two more places a theme lived, neither of which crossed an origin.
|
|
11
|
+
*/
|
|
12
|
+
import { Text, YStack } from "@hanzo/ui";
|
|
13
|
+
import { Appearance } from "@hanzo/appearance";
|
|
14
|
+
import { useIam } from "@hanzo/iam/react";
|
|
15
|
+
import { iam } from "./lib/api.js";
|
|
16
|
+
import { useOrg } from "./host.js";
|
|
17
|
+
export function Look() {
|
|
18
|
+
const { accessToken } = useIam();
|
|
19
|
+
const org = useOrg();
|
|
20
|
+
return (_jsxs(YStack, { gap: "$3", maxW: 420, children: [_jsx(Text, { fontSize: "$3", color: "$soft", children: "Kept with your account, so it follows you to every Hanzo surface." }), _jsx(Appearance, { org: org.id, orgName: org.name || "Hanzo", account: { base: iam(), token: accessToken ?? undefined } })] }));
|
|
21
|
+
}
|
package/dist/meters.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Limits } from './lib/limits.js';
|
|
2
|
+
/** What a spent month says. */
|
|
3
|
+
export declare const SPENT = "Monthly included usage is used; Enso and Zen keep answering.";
|
|
4
|
+
/** The plan holder's windows, or nothing where the limits name none. */
|
|
5
|
+
export declare function Meters({ limits, large }: {
|
|
6
|
+
limits: Limits;
|
|
7
|
+
large?: boolean;
|
|
8
|
+
}): import("react").JSX.Element;
|
package/dist/meters.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
// A plan's windows as bars of what is LEFT: the five-hour session, the day and
|
|
4
|
+
// the billing month, each with when it starts over. Shares only — a plan is a
|
|
5
|
+
// subscription with limits, never a balance, so no count and no amount is
|
|
6
|
+
// drawn. The month spent refuses nothing (Enso and Zen keep answering), which
|
|
7
|
+
// is why it is a quiet line under the bars rather than a warning.
|
|
8
|
+
//
|
|
9
|
+
// One component for the composer's foot and for the usage page: `large` is the
|
|
10
|
+
// page's size. Each bar is drawn above its words so the three bars stay on one
|
|
11
|
+
// line however the words under them wrap on a phone.
|
|
12
|
+
import { Progress, Text, XStack, YStack } from '@hanzo/ui';
|
|
13
|
+
import { clock, day, left } from './lib/limits.js';
|
|
14
|
+
/** What a spent month says. */
|
|
15
|
+
export const SPENT = 'Monthly included usage is used; Enso and Zen keep answering.';
|
|
16
|
+
const WINDOWS = [
|
|
17
|
+
['session', 'Session', (s) => (s.resets_at ? `resets ${clock(s.resets_at)}` : '')],
|
|
18
|
+
['day', 'Today', (s) => (s.resets_at ? `resets ${clock(s.resets_at)}` : '')],
|
|
19
|
+
['month', 'This month', (s) => (s.resets_at ? `renews ${day(s.resets_at)}` : '')],
|
|
20
|
+
];
|
|
21
|
+
/** The plan holder's windows, or nothing where the limits name none. */
|
|
22
|
+
export function Meters({ limits, large = false }) {
|
|
23
|
+
const shown = WINDOWS.flatMap(([key, name, when]) => {
|
|
24
|
+
const span = limits[key];
|
|
25
|
+
return span ? [{ name, span, when: when(span) }] : [];
|
|
26
|
+
});
|
|
27
|
+
if (!shown.length)
|
|
28
|
+
return null;
|
|
29
|
+
const size = large ? '$3' : '$1';
|
|
30
|
+
return (_jsxs(YStack, { gap: large ? '$4' : '$1.5', width: "100%", minW: 0, children: [_jsx(XStack, { gap: large ? '$6' : '$3', rowGap: "$4", width: "100%", flexWrap: large ? 'wrap' : 'nowrap', children: shown.map(({ name, span, when }) => (_jsxs(YStack, { flex: 1, minW: large ? 200 : 0, gap: large ? '$2' : '$1', children: [_jsx(Progress, { value: left(span), height: large ? 8 : 4, minW: 0, "aria-label": name }), _jsxs(XStack, { flexWrap: "wrap", columnGap: "$1.5", items: "baseline", minW: 0, children: [_jsx(Text, { fontSize: size, color: large ? '$ink' : '$soft', numberOfLines: 1, children: name }), _jsxs(XStack, { children: [_jsxs(Text, { fontSize: size, color: "$ink", fontWeight: "500", fontVariant: ['tabular-nums'], children: [left(span), "%"] }), _jsx(Text, { fontSize: size, color: "$ink", display: large ? 'flex' : 'none', $sm: { display: 'flex' }, children: "\u00A0left" })] }), when ? (_jsx(Text, { fontSize: large ? '$2' : '$1', color: "$soft", numberOfLines: 1, children: when })) : null] })] }, name))) }), limits.month && limits.month.percent >= 100 ? (_jsx(Text, { fontSize: large ? '$2' : '$1', color: "$soft", children: SPENT })) : null] }));
|
|
31
|
+
}
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Make `model` the chat default, in this browser, for every open chat. */
|
|
2
|
+
export declare function remember(model: string): void;
|
|
3
|
+
/** The signed-in chat default, shared by Settings and every new conversation. */
|
|
4
|
+
export declare function useModel(): readonly [string, (model: string) => void];
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { ENSO } from "./lib/ai.js";
|
|
4
|
+
const KEY = "hanzo.chat.model";
|
|
5
|
+
const CHANGE = "hanzo:model";
|
|
6
|
+
function read() {
|
|
7
|
+
if (typeof window === "undefined")
|
|
8
|
+
return ENSO;
|
|
9
|
+
try {
|
|
10
|
+
return window.localStorage.getItem(KEY) || ENSO;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return ENSO;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Make `model` the chat default, in this browser, for every open chat. */
|
|
17
|
+
export function remember(model) {
|
|
18
|
+
try {
|
|
19
|
+
window.localStorage.setItem(KEY, model);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// A private browser may refuse persistence; this tab still keeps the choice.
|
|
23
|
+
}
|
|
24
|
+
window.dispatchEvent(new Event(CHANGE));
|
|
25
|
+
}
|
|
26
|
+
/** The signed-in chat default, shared by Settings and every new conversation. */
|
|
27
|
+
export function useModel() {
|
|
28
|
+
const [model, setModel] = useState(ENSO);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
setModel(read());
|
|
31
|
+
const sync = () => setModel(read());
|
|
32
|
+
window.addEventListener("storage", sync);
|
|
33
|
+
window.addEventListener(CHANGE, sync);
|
|
34
|
+
return () => {
|
|
35
|
+
window.removeEventListener("storage", sync);
|
|
36
|
+
window.removeEventListener(CHANGE, sync);
|
|
37
|
+
};
|
|
38
|
+
}, []);
|
|
39
|
+
const choose = (next) => {
|
|
40
|
+
setModel(next);
|
|
41
|
+
remember(next);
|
|
42
|
+
};
|
|
43
|
+
return [model, choose];
|
|
44
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type LucideIcon } from 'lucide-react';
|
|
2
|
+
interface Net {
|
|
3
|
+
/** What a person calls it. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** The brand's own colour, `#rrggbb`, where a silhouette needs a ground. */
|
|
6
|
+
hex?: string;
|
|
7
|
+
/** The 24x24 silhouette, where simple-icons ships one. */
|
|
8
|
+
path?: string;
|
|
9
|
+
/** The vendor's own SVG, where simple-icons ships none and the vendor
|
|
10
|
+
* publishes one that can be fetched. */
|
|
11
|
+
art?: string;
|
|
12
|
+
/** Set where the row is a capability rather than a brand. */
|
|
13
|
+
glyph?: LucideIcon;
|
|
14
|
+
}
|
|
15
|
+
/** What we know about a channel key, including one nobody taught us — which is
|
|
16
|
+
* a name and nothing else, and lands on the same initial Yahoo does. */
|
|
17
|
+
export declare function of(channel: string): Net;
|
|
18
|
+
/**
|
|
19
|
+
* WHAT TO CALL THE SPEAKER.
|
|
20
|
+
*
|
|
21
|
+
* NOTHING THAT ARRIVES HERE CARRIES A NAME. `sender` is the network's own id
|
|
22
|
+
* for the person — "U031THJJ7F1" on Slack, a snowflake on Discord, a GUID on
|
|
23
|
+
* Teams — because that is what every adapter puts there; the envelope declares
|
|
24
|
+
* a `Display` beside it (cloud apps/channels/envelope.go) and not one of the
|
|
25
|
+
* five adapters fills it, so no name is stored and none is served.
|
|
26
|
+
* `senderUser` is not the name either: it is the linked account's OIDC `sub`,
|
|
27
|
+
* which IAM mints from a UUID, so it identifies a Hanzo account without saying
|
|
28
|
+
* who holds it — printing it swaps one opaque string for another.
|
|
29
|
+
*
|
|
30
|
+
* An id printed where a name goes READS as a name, and a reader takes
|
|
31
|
+
* "U031THJJ7F1" for what somebody calls themselves. So the id is MARKED as an
|
|
32
|
+
* id — '@' is how every one of these networks writes a handle it has not
|
|
33
|
+
* resolved — and it is kept, because it is the only thing that tells two
|
|
34
|
+
* speakers apart. A room holds messages from many people (useInbox groups by
|
|
35
|
+
* channel and room, never by sender), so dropping it renders every line of a
|
|
36
|
+
* group identically and a reader cannot tell who said what. Only where no id
|
|
37
|
+
* arrived at all is the network the one thing left to say. The day the
|
|
38
|
+
* platform resolves a name, this is where it goes.
|
|
39
|
+
*/
|
|
40
|
+
export declare const speaker: (channel: string, sender?: string) => string;
|
|
41
|
+
/** A room is named by its own address, not by whoever spoke in it last: the
|
|
42
|
+
* handle for a DM, the network's room id otherwise. */
|
|
43
|
+
export declare function roomName(room: {
|
|
44
|
+
channel: string;
|
|
45
|
+
roomId?: string;
|
|
46
|
+
roomKind: string;
|
|
47
|
+
last: {
|
|
48
|
+
sender?: string;
|
|
49
|
+
};
|
|
50
|
+
}): string;
|
|
51
|
+
/**
|
|
52
|
+
* One channel's mark, at whatever size the caller has room for.
|
|
53
|
+
*
|
|
54
|
+
* A rounded square rather than a disc, because in the Inbox the face beside it
|
|
55
|
+
* IS a disc and two circles the same size read as two people rather than as a
|
|
56
|
+
* person and where they wrote from. The Channels grid draws the same mark at
|
|
57
|
+
* 46px, which is the size the vendor files were cut for and still the size the
|
|
58
|
+
* simple-icons silhouettes were meant to scale to.
|
|
59
|
+
*/
|
|
60
|
+
export declare function Network({ channel, size }: {
|
|
61
|
+
channel: string;
|
|
62
|
+
size?: number;
|
|
63
|
+
}): import("react").JSX.Element;
|
|
64
|
+
export {};
|