@hanzo/build 0.2.24 → 0.2.25

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.
@@ -6,6 +6,7 @@
6
6
  * GET /v1/iam/invitations?owner= {invitations, total} (org admin)
7
7
  * POST /v1/iam/invitations issue one (org admin)
8
8
  * DELETE /v1/iam/invitations/{owner}/{name} withdraw one (org admin)
9
+ * GET /v1/iam/organizations/admin/{org} the org's record, whose `logo` is its mark (member)
9
10
  *
10
11
  * IAM decides the scope from the credential: a member reads the roster of an org
11
12
  * they belong to, and only an admin of the org reads or writes its invitations.
@@ -45,3 +46,5 @@ export declare const EMAIL: RegExp;
45
46
  /** Invite one address into the org: one seat, redeemable now, pinned to that address. */
46
47
  export declare function invite(t: Target, org: string, email: string): Promise<Invitation>;
47
48
  export declare function revoke(t: Target, i: Pick<Invitation, 'owner' | 'name'>): Promise<void>;
49
+ /** The org's own logo address as IAM holds it, or '' when it has none. */
50
+ export declare function logo(t: Target, org: string): Promise<string>;
@@ -6,6 +6,7 @@
6
6
  * GET /v1/iam/invitations?owner= {invitations, total} (org admin)
7
7
  * POST /v1/iam/invitations issue one (org admin)
8
8
  * DELETE /v1/iam/invitations/{owner}/{name} withdraw one (org admin)
9
+ * GET /v1/iam/organizations/admin/{org} the org's record, whose `logo` is its mark (member)
9
10
  *
10
11
  * IAM decides the scope from the credential: a member reads the roster of an org
11
12
  * they belong to, and only an admin of the org reads or writes its invitations.
@@ -86,3 +87,8 @@ export async function invite(t, org, email) {
86
87
  export async function revoke(t, i) {
87
88
  await call(t, 'DELETE', `/v1/iam/invitations/${seg(i.owner)}/${seg(i.name)}`);
88
89
  }
90
+ /** The org's own logo address as IAM holds it, or '' when it has none. */
91
+ export async function logo(t, org) {
92
+ const raw = obj(await call(t, 'GET', `/v1/iam/organizations/admin/${encodeURIComponent(org)}`));
93
+ return str(obj(raw.data ?? raw).logo);
94
+ }
package/lib/builder.js CHANGED
@@ -33,6 +33,7 @@ import { Button } from '@hanzo/ui';
33
33
  import { SessionRail } from '@hanzo/ui/chat';
34
34
  import { HanzoMark } from '@hanzo/ui/product';
35
35
  import { useMemo, useState } from 'react';
36
+ import { safe } from './api/projects.js';
36
37
  import { pinBoard } from './choice.js';
37
38
  import { Customize } from './customize/index.js';
38
39
  import { useKept, useRecents } from './data.js';
@@ -85,7 +86,7 @@ function Shell() {
85
86
  who.toggle();
86
87
  },
87
88
  }
88
- : { name: 'Sign in', onPress: () => host.signIn?.() }, collapsed: collapsed, onCollapse: setCollapsed, mark: _jsx(Brand, { org: host.org }), pt: collapsed ? undefined : 92, open: drawer, onOpenChange: setDrawer, label: "Runs" }), collapsed ? null : (_jsxs(YStack, { position: "absolute", t: 10, l: 8, r: 8, z: 2, gap: "$2.5", "$max-md": { display: 'none' }, children: [_jsx(XStack, { px: "$2.5", children: name }), _jsxs(XStack, { render: "button", "aria-label": "Search runs", onPress: () => setFinding(true), items: "center", gap: "$2", px: "$2.5", height: 34, rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$panel", hoverStyle: { bg: '$hover' }, children: [_jsx(Search, { size: 15, color: "$soft" }), _jsx(SizableText, { size: "$2", color: "$soft", children: "Search" })] })] })), host.person ? _jsx(Who, { open: who.open, onOpenChange: who.onOpenChange, narrow: collapsed }) : null] }), _jsxs(YStack, { flex: 1, minW: 0, minH: 0, position: "relative", children: [_jsxs(XStack, { height: 44, px: "$2", gap: "$2", items: "center", shrink: 0, $md: { display: 'none' }, children: [_jsx(Button, { variant: "ghost", size: "icon-sm", onPress: () => setDrawer(true), "aria-label": "Open runs", children: _jsx(Menu, { size: 18 }) }), name, _jsx(XStack, { flex: 1 }), _jsx(Button, { variant: "ghost", size: "icon-sm", onPress: () => setFinding(true), "aria-label": "Search runs", children: _jsx(Search, { size: 18 }) })] }), _jsx(Pane, { onStarted: () => recents.reload() })] }), _jsx(Find, { open: finding, onOpenChange: setFinding, recents: rows, onOpen: (id) => { setFinding(false); go(id); } })] }));
89
+ : { name: 'Sign in', onPress: () => host.signIn?.() }, collapsed: collapsed, onCollapse: setCollapsed, mark: _jsx(Brand, { org: host.org, logo: host.logo }), pt: collapsed ? undefined : 92, open: drawer, onOpenChange: setDrawer, label: "Runs" }), collapsed ? null : (_jsxs(YStack, { position: "absolute", t: 10, l: 8, r: 8, z: 2, gap: "$2.5", "$max-md": { display: 'none' }, children: [_jsx(XStack, { px: "$2.5", children: name }), _jsxs(XStack, { render: "button", "aria-label": "Search runs", onPress: () => setFinding(true), items: "center", gap: "$2", px: "$2.5", height: 34, rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$panel", hoverStyle: { bg: '$hover' }, children: [_jsx(Search, { size: 15, color: "$soft" }), _jsx(SizableText, { size: "$2", color: "$soft", children: "Search" })] })] })), host.person ? _jsx(Who, { open: who.open, onOpenChange: who.onOpenChange, narrow: collapsed }) : null] }), _jsxs(YStack, { flex: 1, minW: 0, minH: 0, position: "relative", children: [_jsxs(XStack, { height: 44, px: "$2", gap: "$2", items: "center", shrink: 0, $md: { display: 'none' }, children: [_jsx(Button, { variant: "ghost", size: "icon-sm", onPress: () => setDrawer(true), "aria-label": "Open runs", children: _jsx(Menu, { size: 18 }) }), name, _jsx(XStack, { flex: 1 }), _jsx(Button, { variant: "ghost", size: "icon-sm", onPress: () => setFinding(true), "aria-label": "Search runs", children: _jsx(Search, { size: 18 }) })] }), _jsx(Pane, { onStarted: () => recents.reload() })] }), _jsx(Find, { open: finding, onOpenChange: setFinding, recents: rows, onOpen: (id) => { setFinding(false); go(id); } })] }));
89
90
  }
90
91
  /**
91
92
  * The builder's places, as a rail draws them: four in view and the rest under
@@ -154,8 +155,11 @@ function Pane({ onStarted }) {
154
155
  host.go(id);
155
156
  } }));
156
157
  }
157
- /** The Hanzo mark on a personal account. An organization shows its own initial. */
158
- function Brand({ org }) {
158
+ /** The org's mark: the logo it chose; else the Hanzo mark for Hanzo or a personal account, and an organization's initial. */
159
+ function Brand({ org, logo }) {
160
+ const chosen = logo ? safe(logo) : '';
161
+ if (chosen)
162
+ return _jsx("img", { src: chosen, alt: "", width: 18, height: 18, style: { borderRadius: 4, objectFit: 'cover' } });
159
163
  if (!org || org === 'hanzo')
160
164
  return _jsx(HanzoMark, { size: 18 });
161
165
  const letter = org.charAt(0).toUpperCase();
package/lib/host.d.ts CHANGED
@@ -25,6 +25,8 @@ export interface Host {
25
25
  token: () => string | null;
26
26
  /** The org every call is scoped to. */
27
27
  org: string | null;
28
+ /** The org's own logo, an https address, when it has chosen one; its mark is drawn from it. */
29
+ logo?: string;
28
30
  /** The organizations this person belongs to. The switcher lists these and no others. */
29
31
  memberships?: readonly string[];
30
32
  /** Stay on this page and scope every read to `org`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanzo/build",
3
- "version": "0.2.24",
3
+ "version": "0.2.25",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0 OR MIT",
6
6
  "description": "Hanzo Build — say what you want, watch it built, deployed and running. The builder as a component, mounted by the Hanzo app at hanzo.ai.",