@volter/world-console 3.0.1 → 3.0.3
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/client/console.tsx +21 -8
- package/dist/client/console.bundle.js +11 -11
- package/dist/client/console.tsx +21 -8
- package/dist/client/main.bundle.js +49 -49
- package/package.json +3 -3
package/client/console.tsx
CHANGED
|
@@ -535,10 +535,10 @@ const TABS = ['changes', 'events', 'trace', 'vendors', 'screens', 'canvas', 'bra
|
|
|
535
535
|
type Tab = (typeof TABS)[number] | 'overview';
|
|
536
536
|
/** A workspace screen of a twin's vendor, as its `GET /twin` lists it (Viewing a World). */
|
|
537
537
|
type Workspace = { id: string; path: string };
|
|
538
|
-
/** Where a twin's vendor screens are shown: its
|
|
539
|
-
* World; null when it lists none. */
|
|
538
|
+
/** Where a twin's vendor screens are shown: its root workspace (the vendor's home, as a dashboard's root is), else its
|
|
539
|
+
* first that names no parameter, at the twin's place in the World; null when it lists none. */
|
|
540
540
|
const screenUrl = (name: string, t: { twin: string; screens?: Workspace[] } | undefined): string | null => {
|
|
541
|
-
const shown = t?.screens?.find((w) => !/[{<]/.test(w.path));
|
|
541
|
+
const shown = t?.screens?.find((w) => w.path.replace(/\/+$/, '') === '') ?? t?.screens?.find((w) => !/[{<]/.test(w.path));
|
|
542
542
|
return t && shown ? `${WORLDS}/${name}/${t.twin}${shown.path.startsWith('/') ? shown.path : `/${shown.path}`}` : null;
|
|
543
543
|
};
|
|
544
544
|
/** What an entry did, in words: the thing's kind and the vendor's verb, the thing's own label where it has one. */
|
|
@@ -611,6 +611,10 @@ function WorldView({ name, token, twin, tab, arg, sub, admin, snapshot, parentTo
|
|
|
611
611
|
|
|
612
612
|
const current: Tab = (TABS as readonly string[]).includes(tab) ? (tab as Tab) : 'overview';
|
|
613
613
|
const vendors = twins.state === 'ok' ? twins.data.twins.map((t) => t.twin) : [];
|
|
614
|
+
// an address naming no vendor of this World (mistyped, or guessed) says so, never shows as a vendor that stores nothing
|
|
615
|
+
if (twin && twins.state === 'ok' && !vendors.includes(twin)) {
|
|
616
|
+
return <main className="center"><Empty title={`${name} has no ${twin}`} action={<a href={`${BASE}/${name}`} onClick={(e) => { e.preventDefault(); go(name.split('/')); }}>Open {name}</a>}><p>No vendor of this World is called {twin}. Its changesets are under Changes › History.</p></Empty></main>;
|
|
617
|
+
}
|
|
614
618
|
const chosen = current === 'vendors' && arg ? arg : undefined;
|
|
615
619
|
const retry = (): void => { setScope('pending'); setAttempt((a) => a + 1); };
|
|
616
620
|
// ALL SCREENS is a canvas app's own page: the whole window, the World's pages a click away (Figma's, tldraw's)
|
|
@@ -1791,12 +1795,19 @@ const changesetChanges = (c: ChangesetView): Array<{ twin: string; entry: Entry
|
|
|
1791
1795
|
* here), or a push receipt `confirmed` (the remote performed it). Every action performed is Deployed, some is Partly
|
|
1792
1796
|
* deployed; a refused or failed one is said as a failure; otherwise the push decides: every change replayed at the remote
|
|
1793
1797
|
* is Pushed (the remote World, never a vendor), else Not sent. */
|
|
1794
|
-
|
|
1795
|
-
|
|
1798
|
+
/** Whether an action is the World's own (a door, a screen, the gap, the clock or a seed made it): no deploy offers it to
|
|
1799
|
+
* the vendor (the kernel's settlingEntries), so it is never counted as waiting to be performed. */
|
|
1800
|
+
const worldOwn = (a: ChangesetView['actions'][number]): boolean => {
|
|
1801
|
+
const input = ((a.action as { input?: Record<string, unknown> }).input ?? {});
|
|
1802
|
+
return !(typeof input.operationId === 'string' || (input.graphql !== undefined && input.graphql !== null));
|
|
1803
|
+
};
|
|
1804
|
+
function sentOf(c: ChangesetView): { label: string; tone: 'done' | 'good' | 'warn' | 'bad' | 'plain'; performed: number; failed: number; total: number; own: number } {
|
|
1805
|
+
const own = c.actions.filter(worldOwn).length;
|
|
1806
|
+
const total = c.actions.length - own;
|
|
1796
1807
|
const pushed = c.applied?.receipts ?? []; const deployed = c.deployed ?? [];
|
|
1797
1808
|
const performed = new Set([...deployed.filter((r) => r.status === 'deployed'), ...pushed.filter((r) => r.status === 'confirmed')].map((r) => r.actionId)).size;
|
|
1798
1809
|
const failed = new Set([...deployed.filter((r) => r.status === 'refused' || r.status === 'failed'), ...pushed.filter((r) => r.status === 'failed')].map((r) => r.actionId)).size;
|
|
1799
|
-
const n = { performed, failed, total };
|
|
1810
|
+
const n = { performed, failed, total, own };
|
|
1800
1811
|
if (total > 0 && performed >= total) return { label: 'Deployed', tone: 'done', ...n };
|
|
1801
1812
|
if (performed > 0) return { label: `Partly deployed: ${performed} of ${total}`, tone: 'warn', ...n };
|
|
1802
1813
|
if (failed > 0) return { label: `${failed} refused or failed`, tone: 'bad', ...n };
|
|
@@ -1899,7 +1910,7 @@ function ChangesetPage({ name, token, signsAs, c, canWrite, rooted, refused, onC
|
|
|
1899
1910
|
<h4>Where it went</h4>
|
|
1900
1911
|
<div className="block where">
|
|
1901
1912
|
{sent.performed > 0 ? (
|
|
1902
|
-
<p className="pad">{sent.performed >= sent.total ? 'Deployed' : 'Partly deployed'}: {sent.performed} of {sent.total} change{sent.total === 1 ? '' : 's'} performed at {listed([...new Set([...here.map((r) => r.service), ...receipts.filter((r) => r.status === 'confirmed').map((r) => r.service)].map(vendorName))])}{here.length ? ` (${here.length} from here` : ' ('}{here.length && byRemote ? ', ' : ''}{byRemote ? `${byRemote} by the remote` : ''}).</p>
|
|
1913
|
+
<p className="pad">{sent.performed >= sent.total ? 'Deployed' : 'Partly deployed'}: {sent.performed} of {sent.total} change{sent.total === 1 ? '' : 's'}{sent.own ? ` (and ${sent.own} of the World's own, which stay in it)` : ''} performed at {listed([...new Set([...here.map((r) => r.service), ...receipts.filter((r) => r.status === 'confirmed').map((r) => r.service)].map(vendorName))])}{here.length ? ` (${here.length} from here` : ' ('}{here.length && byRemote ? ', ' : ''}{byRemote ? `${byRemote} by the remote` : ''}).</p>
|
|
1903
1914
|
) : sent.label === 'Pushed' ? (
|
|
1904
1915
|
<p className="pad">Pushed to the remote{there ? <> <code>{there}</code></> : null}{c.applied ? <> · <At iso={c.applied.at} /></> : null}: every change was replayed there. No vendor has performed any of it.</p>
|
|
1905
1916
|
) : sent.failed === 0 ? (
|
|
@@ -2123,7 +2134,9 @@ function usePushPull(name: string, token: string, tick: number, refused: (r: Res
|
|
|
2123
2134
|
};
|
|
2124
2135
|
const there = origin.state === 'ok' && origin.data.origin ? originAddress(origin.data.origin) : null;
|
|
2125
2136
|
const changes = diff.state === 'ok' ? diff.data.changes.length : null;
|
|
2126
|
-
|
|
2137
|
+
// what waits to be pushed: this World's own changesets, and only when it has a remote to push them to (a shared World
|
|
2138
|
+
// holds what others pushed, which is theirs)
|
|
2139
|
+
const waiting = sets.state === 'ok' && there ? unpushed(sets.data.changesets.filter((c) => !c.pushedBy)).sort((a, b) => a.createdAt.localeCompare(b.createdAt)) : [];
|
|
2127
2140
|
const push = async (): Promise<void> => {
|
|
2128
2141
|
const b = await send<{ pushed: Array<{ changeset: string; outcome: string; receipts?: Array<{ status: string }>; refusal?: string[] }> }>('Push', 'origin/push', { method: 'POST' });
|
|
2129
2142
|
if (!b) return;
|