@le-space/ui 0.7.0 → 0.8.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/package.json +3 -3
- package/react/index.d.ts +51 -0
- package/react/index.js +601 -128
- package/shared/index.d.ts +61 -2
- package/shared/index.js +172 -4
- package/styles.css +6 -0
- package/svelte/SponsorRelayFab.svelte +284 -0
- package/svelte/styles/theme.css +6 -0
package/shared/index.d.ts
CHANGED
|
@@ -253,6 +253,12 @@ interface CompactInstanceDetails {
|
|
|
253
253
|
vmIpv4: string | null;
|
|
254
254
|
webUrl: string | null;
|
|
255
255
|
sshCommand: string | null;
|
|
256
|
+
/**
|
|
257
|
+
* Hostname of the deployment's HTTPS proxy. The mapped ports are plain HTTP,
|
|
258
|
+
* which a browser on an HTTPS page refuses to fetch, so this is the only
|
|
259
|
+
* address the card can read the relay's own documents from.
|
|
260
|
+
*/
|
|
261
|
+
proxyHostname: string | null;
|
|
256
262
|
mappedPorts: Array<{
|
|
257
263
|
label: string;
|
|
258
264
|
hostPort: number | null;
|
|
@@ -260,6 +266,32 @@ interface CompactInstanceDetails {
|
|
|
260
266
|
execution: InstanceExecution | null;
|
|
261
267
|
error: string | null;
|
|
262
268
|
}
|
|
269
|
+
/** One transport's worth of addresses, as the relay itself groups them. */
|
|
270
|
+
interface SponsorRelayAddressGroup {
|
|
271
|
+
key: string;
|
|
272
|
+
label: string;
|
|
273
|
+
addresses: string[];
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* What a deployment reports about itself at `/multiaddrs`. Deliberately not
|
|
277
|
+
* normalised into "the best address": the card shows what the relay says, and
|
|
278
|
+
* choosing between addresses is the consumer's business, not this UI's.
|
|
279
|
+
*/
|
|
280
|
+
interface SponsorRelayInstanceAddresses {
|
|
281
|
+
status: 'idle' | 'loading' | 'ready' | 'unsupported' | 'error';
|
|
282
|
+
/** Transport groups, empty for a profile that publishes no libp2p addresses. */
|
|
283
|
+
groups: SponsorRelayAddressGroup[];
|
|
284
|
+
/** Ready-to-paste environment block, when the profile publishes one. */
|
|
285
|
+
pwaEnv: Record<string, string> | null;
|
|
286
|
+
/** Raw payload, rendered verbatim on the health tab. */
|
|
287
|
+
payload: Record<string, unknown> | null;
|
|
288
|
+
peerId: string | null;
|
|
289
|
+
version: string | null;
|
|
290
|
+
fetchedAt: number | null;
|
|
291
|
+
error: string | null;
|
|
292
|
+
/** Source URL, shown so a person can curl exactly what the card read. */
|
|
293
|
+
sourceUrl: string | null;
|
|
294
|
+
}
|
|
263
295
|
interface CompactInstanceRecord {
|
|
264
296
|
instance: InstanceMessage;
|
|
265
297
|
details: CompactInstanceDetails;
|
|
@@ -324,6 +356,13 @@ interface SponsorRelayState {
|
|
|
324
356
|
orphanBootstrapRegistrations: CompactBootstrapRegistrationRecord[];
|
|
325
357
|
lastDeploymentHash: string | null;
|
|
326
358
|
deploymentProgress: DeploymentProgressEvent;
|
|
359
|
+
/**
|
|
360
|
+
* What each deployment reports at `/multiaddrs`, keyed by instance item hash.
|
|
361
|
+
* Fetched on demand rather than with the instance list: AutoTLS addresses only
|
|
362
|
+
* appear a minute or two after a deploy reports success, so this is state that
|
|
363
|
+
* has to be refreshable on its own.
|
|
364
|
+
*/
|
|
365
|
+
instanceAddresses: Record<string, SponsorRelayInstanceAddresses>;
|
|
327
366
|
}
|
|
328
367
|
type SponsorRelaySubscriber = (state: SponsorRelayState) => void;
|
|
329
368
|
|
|
@@ -347,7 +386,7 @@ declare const STALE_INSTANCE_ALLOCATION_COOLDOWN_MS: number;
|
|
|
347
386
|
declare const RECENT_INSTANCE_RUNTIME_GRACE_MS: number;
|
|
348
387
|
declare const DEPLOYMENT_PENDING_WARNING_MS: number;
|
|
349
388
|
|
|
350
|
-
declare const UI_PACKAGE_VERSION = "0.
|
|
389
|
+
declare const UI_PACKAGE_VERSION = "0.8.0";
|
|
351
390
|
|
|
352
391
|
interface RootfsRequiredPortForward {
|
|
353
392
|
port: number;
|
|
@@ -383,6 +422,14 @@ declare function connectWallet(provider?: EthereumProviderLike | null): Promise<
|
|
|
383
422
|
declare function personalSign(address: string, message: string, provider?: EthereumProviderLike | null): Promise<string>;
|
|
384
423
|
declare function watchWallet(onChange: () => void, provider?: EthereumProviderLike | null): () => void;
|
|
385
424
|
|
|
425
|
+
/**
|
|
426
|
+
* Normalise the two address documents in the fleet into one list of groups.
|
|
427
|
+
*
|
|
428
|
+
* orbitdb-relay reports `byTransport`; uc-go-peer's describe output uses
|
|
429
|
+
* `<transport>_multiaddrs` keys. Neither is rewritten or ranked here — the card
|
|
430
|
+
* shows what the relay said, and picking an address is the consumer's job.
|
|
431
|
+
*/
|
|
432
|
+
declare function parseInstanceAddressPayload(payload: Record<string, unknown>): SponsorRelayInstanceAddresses;
|
|
386
433
|
declare function rootfsHealth(args: {
|
|
387
434
|
manifestState: RootfsManifestState;
|
|
388
435
|
rootfsVerified: boolean;
|
|
@@ -438,6 +485,18 @@ declare class SponsorRelayController {
|
|
|
438
485
|
private refreshWalletDerivedState;
|
|
439
486
|
refresh(): Promise<void>;
|
|
440
487
|
deploy(): Promise<void>;
|
|
488
|
+
/**
|
|
489
|
+
* Read one deployment's own address document over its HTTPS proxy.
|
|
490
|
+
*
|
|
491
|
+
* The mapped ports are plain HTTP, which a browser on an HTTPS page refuses to
|
|
492
|
+
* fetch, so the proxy hostname is the only way in. Deliberately single-shot:
|
|
493
|
+
* unlike the deploy-time readiness probe this backs a button someone pressed,
|
|
494
|
+
* and a caller who wants newer data presses it again.
|
|
495
|
+
*/
|
|
496
|
+
loadInstanceAddresses(instanceHash: string, options?: {
|
|
497
|
+
force?: boolean;
|
|
498
|
+
}): Promise<void>;
|
|
499
|
+
private patchInstanceAddresses;
|
|
441
500
|
deleteInstance(instanceHash: string): Promise<void>;
|
|
442
501
|
deleteBootstrapRegistration(registrationHash: string): Promise<void>;
|
|
443
502
|
}
|
|
@@ -450,4 +509,4 @@ interface DeploymentProgressEmitter {
|
|
|
450
509
|
}
|
|
451
510
|
declare function createDeploymentProgressEmitter(): DeploymentProgressEmitter;
|
|
452
511
|
|
|
453
|
-
export { type CompactBootstrapRegistrationRecord, type CompactInstanceDetails, type CompactInstanceRecord, DEFAULT_INSTANCE_NAME, DEFAULT_MANIFEST_URL, DEFAULT_TIER_ID, DEFAULT_UCAN_STORE_ALLOWED_CAPABILITIES, DEFAULT_UCAN_STORE_MAX_DELEGATION_EXPIRATION_SECONDS, DEFAULT_UCAN_STORE_PWA_ORIGIN, DEFAULT_UCAN_STORE_SERVICE_DID, DEFAULT_UCAN_STORE_SERVICE_ORIGIN, DEFAULT_UCAN_STORE_USER_DELEGATION_EXPIRATION_SECONDS, DEPLOYMENT_PENDING_WARNING_MS, type DeploymentProgressEmitter, type EthereumProviderLike, IDLE_DEPLOYMENT_PROGRESS, INSTANCE_RUNTIME_PENDING_REFRESH_MS, INSTANCE_RUNTIME_READY_REFRESH_MS, INSTANCE_RUNTIME_STALE_REFRESH_MS, MANIFEST_SOURCE_REFRESH_DEBOUNCE_MS, RECENT_INSTANCE_RUNTIME_GRACE_MS, REFRESH_INTERVAL_MS, ROOTFS_MISSING_STATE, STALE_INSTANCE_ALLOCATION_COOLDOWN_MS, SponsorRelayController, type SponsorRelayHealthTone, type SponsorRelayPricingSummary, type SponsorRelayProps, type SponsorRelayRootfsHealth, type SponsorRelayState, type SponsorRelaySubscriber, type SponsorRelayUcanStoreBootstrapInput, type SponsorRelayWalletState, UI_PACKAGE_VERSION, buildSshCommand, connectWallet, createDeploymentProgressEmitter, createSponsorRelayController, formatDateTime, formatNumber, formatTierSpecLabel, getEthereumProvider, joinMappedPorts, joinRequiredPortForwards, personalSign, resolveManifestSource, rootfsHealth, shortHash, toChecksumAddress, watchWallet };
|
|
512
|
+
export { type CompactBootstrapRegistrationRecord, type CompactInstanceDetails, type CompactInstanceRecord, DEFAULT_INSTANCE_NAME, DEFAULT_MANIFEST_URL, DEFAULT_TIER_ID, DEFAULT_UCAN_STORE_ALLOWED_CAPABILITIES, DEFAULT_UCAN_STORE_MAX_DELEGATION_EXPIRATION_SECONDS, DEFAULT_UCAN_STORE_PWA_ORIGIN, DEFAULT_UCAN_STORE_SERVICE_DID, DEFAULT_UCAN_STORE_SERVICE_ORIGIN, DEFAULT_UCAN_STORE_USER_DELEGATION_EXPIRATION_SECONDS, DEPLOYMENT_PENDING_WARNING_MS, type DeploymentProgressEmitter, type EthereumProviderLike, IDLE_DEPLOYMENT_PROGRESS, INSTANCE_RUNTIME_PENDING_REFRESH_MS, INSTANCE_RUNTIME_READY_REFRESH_MS, INSTANCE_RUNTIME_STALE_REFRESH_MS, MANIFEST_SOURCE_REFRESH_DEBOUNCE_MS, RECENT_INSTANCE_RUNTIME_GRACE_MS, REFRESH_INTERVAL_MS, ROOTFS_MISSING_STATE, STALE_INSTANCE_ALLOCATION_COOLDOWN_MS, type SponsorRelayAddressGroup, SponsorRelayController, type SponsorRelayHealthTone, type SponsorRelayInstanceAddresses, type SponsorRelayPricingSummary, type SponsorRelayProps, type SponsorRelayRootfsHealth, type SponsorRelayState, type SponsorRelaySubscriber, type SponsorRelayUcanStoreBootstrapInput, type SponsorRelayWalletState, UI_PACKAGE_VERSION, buildSshCommand, connectWallet, createDeploymentProgressEmitter, createSponsorRelayController, formatDateTime, formatNumber, formatTierSpecLabel, getEthereumProvider, joinMappedPorts, joinRequiredPortForwards, parseInstanceAddressPayload, personalSign, resolveManifestSource, rootfsHealth, shortHash, toChecksumAddress, watchWallet };
|
package/shared/index.js
CHANGED
|
@@ -42,7 +42,7 @@ var RECENT_INSTANCE_RUNTIME_GRACE_MS = 10 * 60 * 1e3;
|
|
|
42
42
|
var DEPLOYMENT_PENDING_WARNING_MS = 3 * 60 * 1e3;
|
|
43
43
|
|
|
44
44
|
// src/shared/package-version.ts
|
|
45
|
-
var UI_PACKAGE_VERSION = "0.
|
|
45
|
+
var UI_PACKAGE_VERSION = "0.8.0";
|
|
46
46
|
|
|
47
47
|
// src/shared/format.ts
|
|
48
48
|
function shortHash(value, head = 8, tail = 6) {
|
|
@@ -3831,7 +3831,8 @@ function defaultState(props = {}) {
|
|
|
3831
3831
|
bootstrapRegistrations: [],
|
|
3832
3832
|
orphanBootstrapRegistrations: [],
|
|
3833
3833
|
lastDeploymentHash: null,
|
|
3834
|
-
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS
|
|
3834
|
+
deploymentProgress: IDLE_DEPLOYMENT_PROGRESS,
|
|
3835
|
+
instanceAddresses: {}
|
|
3835
3836
|
};
|
|
3836
3837
|
}
|
|
3837
3838
|
function manifestLoadErrorState(error) {
|
|
@@ -3859,6 +3860,83 @@ async function sha256Hex(payload) {
|
|
|
3859
3860
|
);
|
|
3860
3861
|
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
3861
3862
|
}
|
|
3863
|
+
function hostnameOfUrl(value) {
|
|
3864
|
+
if (!value) return null;
|
|
3865
|
+
try {
|
|
3866
|
+
return new URL(value).hostname || null;
|
|
3867
|
+
} catch {
|
|
3868
|
+
return null;
|
|
3869
|
+
}
|
|
3870
|
+
}
|
|
3871
|
+
function emptyInstanceAddresses() {
|
|
3872
|
+
return {
|
|
3873
|
+
status: "idle",
|
|
3874
|
+
groups: [],
|
|
3875
|
+
pwaEnv: null,
|
|
3876
|
+
payload: null,
|
|
3877
|
+
peerId: null,
|
|
3878
|
+
version: null,
|
|
3879
|
+
fetchedAt: null,
|
|
3880
|
+
error: null,
|
|
3881
|
+
sourceUrl: null
|
|
3882
|
+
};
|
|
3883
|
+
}
|
|
3884
|
+
var TRANSPORT_LABELS = {
|
|
3885
|
+
tcp: "TCP",
|
|
3886
|
+
websocket: "WebSocket",
|
|
3887
|
+
webrtc: "WebRTC",
|
|
3888
|
+
direct_tcp: "Direct TCP",
|
|
3889
|
+
autotls_wss: "AutoTLS WSS",
|
|
3890
|
+
proxy_wss: "Proxy WSS",
|
|
3891
|
+
webtransport: "WebTransport",
|
|
3892
|
+
webrtc_direct: "WebRTC Direct",
|
|
3893
|
+
browser_bootstrap: "Browser bootstrap",
|
|
3894
|
+
probe: "Probe"
|
|
3895
|
+
};
|
|
3896
|
+
function transportLabel(key) {
|
|
3897
|
+
return TRANSPORT_LABELS[key] ?? key.replace(/[_-]+/g, " ").replace(/^./, (first) => first.toUpperCase());
|
|
3898
|
+
}
|
|
3899
|
+
function addressList(value) {
|
|
3900
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && Boolean(entry.trim())) : [];
|
|
3901
|
+
}
|
|
3902
|
+
function parseInstanceAddressPayload(payload) {
|
|
3903
|
+
const groups = [];
|
|
3904
|
+
const byTransport = payload.byTransport;
|
|
3905
|
+
if (byTransport && typeof byTransport === "object") {
|
|
3906
|
+
for (const [key, value] of Object.entries(
|
|
3907
|
+
byTransport
|
|
3908
|
+
)) {
|
|
3909
|
+
const addresses = addressList(value);
|
|
3910
|
+
if (addresses.length > 0) {
|
|
3911
|
+
groups.push({ key, label: transportLabel(key), addresses });
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
3916
|
+
if (!key.endsWith("_multiaddrs")) continue;
|
|
3917
|
+
const addresses = addressList(value);
|
|
3918
|
+
if (addresses.length === 0) continue;
|
|
3919
|
+
const groupKey = key.replace(/_multiaddrs$/, "");
|
|
3920
|
+
groups.push({
|
|
3921
|
+
key: groupKey,
|
|
3922
|
+
label: transportLabel(groupKey),
|
|
3923
|
+
addresses
|
|
3924
|
+
});
|
|
3925
|
+
}
|
|
3926
|
+
const pwaEnvRaw = payload.pwa_env;
|
|
3927
|
+
const pwaEnv = pwaEnvRaw && typeof pwaEnvRaw === "object" ? Object.fromEntries(
|
|
3928
|
+
Object.entries(pwaEnvRaw).filter(([, entry]) => typeof entry === "string").map(([entryKey, entry]) => [entryKey, entry])
|
|
3929
|
+
) : null;
|
|
3930
|
+
const peerId = typeof payload.peerId === "string" ? payload.peerId : typeof payload.peer_id === "string" ? payload.peer_id : null;
|
|
3931
|
+
return {
|
|
3932
|
+
...emptyInstanceAddresses(),
|
|
3933
|
+
groups,
|
|
3934
|
+
pwaEnv,
|
|
3935
|
+
payload,
|
|
3936
|
+
peerId,
|
|
3937
|
+
version: typeof payload.version === "string" ? payload.version : null
|
|
3938
|
+
};
|
|
3939
|
+
}
|
|
3862
3940
|
function mappedPorts(execution) {
|
|
3863
3941
|
return Object.entries(execution?.networking?.mapped_ports ?? {}).map(
|
|
3864
3942
|
([port, mapping]) => ({
|
|
@@ -4007,6 +4085,7 @@ async function inspectInstanceRuntime(args) {
|
|
|
4007
4085
|
vmIpv4: null,
|
|
4008
4086
|
webUrl: null,
|
|
4009
4087
|
sshCommand: null,
|
|
4088
|
+
proxyHostname: null,
|
|
4010
4089
|
mappedPorts: [],
|
|
4011
4090
|
execution: null,
|
|
4012
4091
|
error: null
|
|
@@ -4071,6 +4150,7 @@ async function inspectInstanceRuntime(args) {
|
|
|
4071
4150
|
details.ipv6 = execution.networking.ipv6_ip ?? execution.networking.ipv6 ?? details.ipv6;
|
|
4072
4151
|
details.vmIpv4 = execution.networking.ipv4_ip ?? null;
|
|
4073
4152
|
details.webUrl = execution.networking.proxy_url ?? details.webUrl;
|
|
4153
|
+
details.proxyHostname = hostnameOfUrl(details.webUrl);
|
|
4074
4154
|
details.mappedPorts = mappedPorts(execution);
|
|
4075
4155
|
details.sshCommand = buildSshCommand(details.hostIpv4, details.mappedPorts);
|
|
4076
4156
|
return { details, lookup };
|
|
@@ -4420,10 +4500,14 @@ var SponsorRelayController = class {
|
|
|
4420
4500
|
detail: "Publishing the host port mapping into the guest relay configuration."
|
|
4421
4501
|
});
|
|
4422
4502
|
const tcpPort = mappedPorts2["9091"]?.host ?? 0;
|
|
4423
|
-
const wsPort = mappedPorts2["9092"]?.host ??
|
|
4503
|
+
const wsPort = mappedPorts2["9092"]?.host ?? 0;
|
|
4424
4504
|
if (!tcpPort || !wsPort) {
|
|
4505
|
+
const missing = [
|
|
4506
|
+
tcpPort ? null : "9091 (libp2p TCP)",
|
|
4507
|
+
wsPort ? null : "9092 (libp2p WebSocket)"
|
|
4508
|
+
].filter(Boolean);
|
|
4425
4509
|
throw new Error(
|
|
4426
|
-
|
|
4510
|
+
`OrbitDB relay runtime has no mapped host port for ${missing.join(" and ")}. Deploy a rootfs whose manifest requests these forwards.`
|
|
4427
4511
|
);
|
|
4428
4512
|
}
|
|
4429
4513
|
const configureResult = await configureOrbitdbRelaySetup2({
|
|
@@ -5406,6 +5490,7 @@ var SponsorRelayController = class {
|
|
|
5406
5490
|
vmIpv4: null,
|
|
5407
5491
|
webUrl: null,
|
|
5408
5492
|
sshCommand: null,
|
|
5493
|
+
proxyHostname: null,
|
|
5409
5494
|
mappedPorts: [],
|
|
5410
5495
|
execution: null,
|
|
5411
5496
|
error: null
|
|
@@ -5821,6 +5906,9 @@ var SponsorRelayController = class {
|
|
|
5821
5906
|
vmIpv4: runtime.execution?.networking?.ipv4_ip ?? null,
|
|
5822
5907
|
webUrl: runtime.webAccessUrl ?? runtime.proxyUrl ?? runtime.execution?.networking?.proxy_url ?? null,
|
|
5823
5908
|
sshCommand: runtime.sshCommand ?? null,
|
|
5909
|
+
proxyHostname: hostnameOfUrl(
|
|
5910
|
+
runtime.proxyUrl ?? runtime.webAccessUrl ?? runtime.execution?.networking?.proxy_url ?? null
|
|
5911
|
+
),
|
|
5824
5912
|
mappedPorts: runtime.execution != null ? mappedPorts(runtime.execution) : runtimeMappedPorts(runtime.mappedPorts),
|
|
5825
5913
|
execution: runtime.execution ?? null,
|
|
5826
5914
|
error: null
|
|
@@ -5918,6 +6006,9 @@ var SponsorRelayController = class {
|
|
|
5918
6006
|
vmIpv4: latestRuntime.execution?.networking?.ipv4_ip ?? null,
|
|
5919
6007
|
webUrl: latestRuntime.webAccessUrl ?? latestRuntime.proxyUrl ?? latestRuntime.execution?.networking?.proxy_url ?? null,
|
|
5920
6008
|
sshCommand: latestRuntime.sshCommand ?? null,
|
|
6009
|
+
proxyHostname: hostnameOfUrl(
|
|
6010
|
+
latestRuntime.proxyUrl ?? latestRuntime.webAccessUrl ?? latestRuntime.execution?.networking?.proxy_url ?? null
|
|
6011
|
+
),
|
|
5921
6012
|
mappedPorts: latestRuntime.execution != null ? mappedPorts(latestRuntime.execution) : runtimeMappedPorts(latestRuntime.mappedPorts),
|
|
5922
6013
|
execution: latestRuntime.execution ?? null,
|
|
5923
6014
|
error: null
|
|
@@ -6098,6 +6189,82 @@ var SponsorRelayController = class {
|
|
|
6098
6189
|
});
|
|
6099
6190
|
}
|
|
6100
6191
|
}
|
|
6192
|
+
/**
|
|
6193
|
+
* Read one deployment's own address document over its HTTPS proxy.
|
|
6194
|
+
*
|
|
6195
|
+
* The mapped ports are plain HTTP, which a browser on an HTTPS page refuses to
|
|
6196
|
+
* fetch, so the proxy hostname is the only way in. Deliberately single-shot:
|
|
6197
|
+
* unlike the deploy-time readiness probe this backs a button someone pressed,
|
|
6198
|
+
* and a caller who wants newer data presses it again.
|
|
6199
|
+
*/
|
|
6200
|
+
async loadInstanceAddresses(instanceHash, options = {}) {
|
|
6201
|
+
const existing = this.state.instanceAddresses[instanceHash];
|
|
6202
|
+
if (!options.force && existing && existing.status !== "idle") return;
|
|
6203
|
+
const record = this.state.instances.find(
|
|
6204
|
+
(entry) => entry.instance.item_hash === instanceHash
|
|
6205
|
+
);
|
|
6206
|
+
const proxyHostname = record?.details.proxyHostname ?? null;
|
|
6207
|
+
if (!proxyHostname) {
|
|
6208
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6209
|
+
...emptyInstanceAddresses(),
|
|
6210
|
+
status: "unsupported",
|
|
6211
|
+
error: "This deployment has no HTTPS proxy hostname yet, so its addresses cannot be read from the browser."
|
|
6212
|
+
});
|
|
6213
|
+
return;
|
|
6214
|
+
}
|
|
6215
|
+
const sourceUrl = `https://${proxyHostname}/multiaddrs`;
|
|
6216
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6217
|
+
...existing ?? emptyInstanceAddresses(),
|
|
6218
|
+
status: "loading",
|
|
6219
|
+
sourceUrl,
|
|
6220
|
+
error: null
|
|
6221
|
+
});
|
|
6222
|
+
try {
|
|
6223
|
+
const abort = new AbortController();
|
|
6224
|
+
const timeout = globalThis.setTimeout(() => abort.abort(), 1e4);
|
|
6225
|
+
const response = await fetch(sourceUrl, {
|
|
6226
|
+
cache: "no-cache",
|
|
6227
|
+
signal: abort.signal
|
|
6228
|
+
});
|
|
6229
|
+
globalThis.clearTimeout(timeout);
|
|
6230
|
+
if (response.status === 404) {
|
|
6231
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6232
|
+
...emptyInstanceAddresses(),
|
|
6233
|
+
status: "unsupported",
|
|
6234
|
+
sourceUrl,
|
|
6235
|
+
fetchedAt: Date.now(),
|
|
6236
|
+
error: "This profile does not publish an address document."
|
|
6237
|
+
});
|
|
6238
|
+
return;
|
|
6239
|
+
}
|
|
6240
|
+
if (!response.ok) {
|
|
6241
|
+
throw new Error(`${sourceUrl} answered ${response.status}.`);
|
|
6242
|
+
}
|
|
6243
|
+
const payload = await response.json();
|
|
6244
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6245
|
+
...parseInstanceAddressPayload(payload),
|
|
6246
|
+
status: "ready",
|
|
6247
|
+
sourceUrl,
|
|
6248
|
+
fetchedAt: Date.now(),
|
|
6249
|
+
error: null
|
|
6250
|
+
});
|
|
6251
|
+
} catch (error) {
|
|
6252
|
+
this.patchInstanceAddresses(instanceHash, {
|
|
6253
|
+
...this.state.instanceAddresses[instanceHash] ?? emptyInstanceAddresses(),
|
|
6254
|
+
status: "error",
|
|
6255
|
+
sourceUrl,
|
|
6256
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6257
|
+
});
|
|
6258
|
+
}
|
|
6259
|
+
}
|
|
6260
|
+
patchInstanceAddresses(instanceHash, value) {
|
|
6261
|
+
this.patch({
|
|
6262
|
+
instanceAddresses: {
|
|
6263
|
+
...this.state.instanceAddresses,
|
|
6264
|
+
[instanceHash]: value
|
|
6265
|
+
}
|
|
6266
|
+
});
|
|
6267
|
+
}
|
|
6101
6268
|
async deleteInstance(instanceHash) {
|
|
6102
6269
|
this.trace("delete:start", { instanceHash });
|
|
6103
6270
|
if (!this.state.wallet.address) {
|
|
@@ -6302,6 +6469,7 @@ export {
|
|
|
6302
6469
|
getEthereumProvider,
|
|
6303
6470
|
joinMappedPorts,
|
|
6304
6471
|
joinRequiredPortForwards,
|
|
6472
|
+
parseInstanceAddressPayload,
|
|
6305
6473
|
personalSign,
|
|
6306
6474
|
resolveManifestSource,
|
|
6307
6475
|
rootfsHealth,
|
package/styles.css
CHANGED
|
@@ -42,6 +42,11 @@
|
|
|
42
42
|
--relay-surface: rgba(11, 14, 21, 0.55);
|
|
43
43
|
--relay-surface-border: var(--relay-horizon);
|
|
44
44
|
--relay-field-bg: rgba(11, 14, 21, 0.85);
|
|
45
|
+
/* Opaque on purpose. A select's drop-down takes the control's own colours on
|
|
46
|
+
the platforms that honour author styling, and it opens over the page rather
|
|
47
|
+
than over the panel - a translucent background would composite against
|
|
48
|
+
whatever is behind it and leave light text on a light list. */
|
|
49
|
+
--relay-menu-bg: var(--relay-deep-space);
|
|
45
50
|
--relay-accent: var(--relay-coral);
|
|
46
51
|
--relay-accent-contrast: #0b0e15;
|
|
47
52
|
--relay-link: var(--relay-cyan);
|
|
@@ -81,6 +86,7 @@
|
|
|
81
86
|
--relay-surface: rgba(20, 27, 46, 0.04);
|
|
82
87
|
--relay-surface-border: #d9e0ec;
|
|
83
88
|
--relay-field-bg: #ffffff;
|
|
89
|
+
--relay-menu-bg: #ffffff;
|
|
84
90
|
--relay-accent: var(--relay-coral);
|
|
85
91
|
--relay-accent-contrast: #ffffff;
|
|
86
92
|
--relay-link: var(--relay-cyan);
|