@codemem/server 0.22.0 → 0.22.2
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/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +182 -18
- package/dist/index.js.map +1 -1
- package/dist/request-rate-limit.d.ts +8 -0
- package/dist/request-rate-limit.d.ts.map +1 -0
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/stats.d.ts +1 -1
- package/dist/routes/stats.d.ts.map +1 -1
- package/dist/routes/sync.d.ts +11 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +3 -2
- package/static/app.js +860 -382
- package/static/index.html +162 -6
package/static/app.js
CHANGED
|
@@ -148,6 +148,11 @@
|
|
|
148
148
|
lastSyncJoinRequests: [],
|
|
149
149
|
lastTeamInvite: null,
|
|
150
150
|
lastTeamJoin: null,
|
|
151
|
+
syncJoinFlowFeedback: null,
|
|
152
|
+
syncPeerFeedbackById: /* @__PURE__ */ new Map(),
|
|
153
|
+
syncPeersSectionFeedback: null,
|
|
154
|
+
syncJoinRequestsFeedback: null,
|
|
155
|
+
syncDiscoveredFeedback: null,
|
|
151
156
|
lastSyncAttempts: [],
|
|
152
157
|
lastSyncLegacyDevices: [],
|
|
153
158
|
lastSyncViewModel: null,
|
|
@@ -235,6 +240,19 @@
|
|
|
235
240
|
if (!resp.ok) throw new Error(`${url}: ${resp.status} ${resp.statusText}`);
|
|
236
241
|
return resp.json();
|
|
237
242
|
}
|
|
243
|
+
async function pingViewerReady(timeoutMs = 1200) {
|
|
244
|
+
const controller = new AbortController();
|
|
245
|
+
const timeoutId = window.setTimeout(() => controller.abort(), timeoutMs);
|
|
246
|
+
try {
|
|
247
|
+
const resp = await fetch("/api/stats", {
|
|
248
|
+
cache: "no-store",
|
|
249
|
+
signal: controller.signal
|
|
250
|
+
});
|
|
251
|
+
if (!resp.ok) throw new Error(`/api/stats: ${resp.status} ${resp.statusText}`);
|
|
252
|
+
} finally {
|
|
253
|
+
window.clearTimeout(timeoutId);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
238
256
|
async function readJsonPayload(resp) {
|
|
239
257
|
const text = await resp.text();
|
|
240
258
|
try {
|
|
@@ -252,6 +270,9 @@
|
|
|
252
270
|
async function loadStats() {
|
|
253
271
|
return fetchJson("/api/stats");
|
|
254
272
|
}
|
|
273
|
+
async function loadRuntimeInfo() {
|
|
274
|
+
return fetchJson("/api/runtime");
|
|
275
|
+
}
|
|
255
276
|
async function loadUsage(project) {
|
|
256
277
|
return fetchJson(`/api/usage?project=${encodeURIComponent(project)}`);
|
|
257
278
|
}
|
|
@@ -2366,6 +2387,10 @@
|
|
|
2366
2387
|
if (peer?.status?.fresh) return "connected";
|
|
2367
2388
|
return "waiting";
|
|
2368
2389
|
}
|
|
2390
|
+
function isOfflineTeamDevice(device) {
|
|
2391
|
+
if (!device.discovered?.stale) return false;
|
|
2392
|
+
return device.peer ? derivePeerUiStatus(device.peer) !== "connected" : true;
|
|
2393
|
+
}
|
|
2369
2394
|
function peerErrorText(peer) {
|
|
2370
2395
|
return cleanText(peer?.last_error).toLowerCase();
|
|
2371
2396
|
}
|
|
@@ -2504,18 +2529,18 @@
|
|
|
2504
2529
|
priority: 10,
|
|
2505
2530
|
title: `${device.name} needs repair`,
|
|
2506
2531
|
summary: device.summary,
|
|
2507
|
-
actionLabel: "
|
|
2532
|
+
actionLabel: "Open device",
|
|
2508
2533
|
deviceId: device.id
|
|
2509
2534
|
};
|
|
2510
2535
|
}
|
|
2511
2536
|
function createReviewItem(device) {
|
|
2512
2537
|
return {
|
|
2513
|
-
id: `review:${device.id}`,
|
|
2538
|
+
id: `review:${device.id}:${device.key || "default"}`,
|
|
2514
2539
|
kind: "review-team-device",
|
|
2515
2540
|
priority: 20,
|
|
2516
2541
|
title: `${device.name} is available to review`,
|
|
2517
2542
|
summary: device.summary,
|
|
2518
|
-
actionLabel: "
|
|
2543
|
+
actionLabel: "Open device",
|
|
2519
2544
|
deviceId: device.id
|
|
2520
2545
|
};
|
|
2521
2546
|
}
|
|
@@ -2526,7 +2551,7 @@
|
|
|
2526
2551
|
priority: 30,
|
|
2527
2552
|
title: `Name ${device.name}`,
|
|
2528
2553
|
summary: device.summary,
|
|
2529
|
-
actionLabel: "
|
|
2554
|
+
actionLabel: "Go to name field",
|
|
2530
2555
|
deviceId: device.id
|
|
2531
2556
|
};
|
|
2532
2557
|
}
|
|
@@ -2574,7 +2599,7 @@
|
|
|
2574
2599
|
priority: candidate.includesLocal ? 5 : 15,
|
|
2575
2600
|
title: `Possible duplicate person: ${candidate.displayName}`,
|
|
2576
2601
|
summary: candidate.includesLocal ? "At least one entry is marked as you. Confirm whether these records represent the same person." : "Multiple people share this name. Confirm whether they should stay separate or be combined.",
|
|
2577
|
-
actionLabel: "
|
|
2602
|
+
actionLabel: "Go to people",
|
|
2578
2603
|
actorIds: candidate.actorIds
|
|
2579
2604
|
});
|
|
2580
2605
|
});
|
|
@@ -2610,9 +2635,22 @@
|
|
|
2610
2635
|
}));
|
|
2611
2636
|
if (device.peer && trustSummary?.state === "trusted-by-you") attentionItems.push(createReviewItem({
|
|
2612
2637
|
id: device.deviceId,
|
|
2638
|
+
key: "other-device-trust",
|
|
2613
2639
|
name,
|
|
2614
2640
|
summary: "You accepted this device. Finish onboarding on the other device so it trusts this one too."
|
|
2615
2641
|
}));
|
|
2642
|
+
if (!device.peer && device.discovered?.stale) attentionItems.push(createReviewItem({
|
|
2643
|
+
id: device.deviceId,
|
|
2644
|
+
key: "stale-discovery",
|
|
2645
|
+
name,
|
|
2646
|
+
summary: "This device is no longer advertising fresh coordinator presence. Wait for it to check in again before connecting it here."
|
|
2647
|
+
}));
|
|
2648
|
+
if (!device.peer && Array.isArray(device.discovered?.groups) && device.discovered.groups.length > 1) attentionItems.push(createReviewItem({
|
|
2649
|
+
id: device.deviceId,
|
|
2650
|
+
key: "ambiguous-groups",
|
|
2651
|
+
name,
|
|
2652
|
+
summary: "This device appears in multiple coordinator groups. Review the team setup before approving it here."
|
|
2653
|
+
}));
|
|
2616
2654
|
if (device.peer && deviceNeedsFriendlyName({
|
|
2617
2655
|
localName: device.localName,
|
|
2618
2656
|
coordinatorName: device.coordinatorName,
|
|
@@ -2627,7 +2665,7 @@
|
|
|
2627
2665
|
summary: {
|
|
2628
2666
|
connectedDeviceCount: peers.filter((peer) => derivePeerUiStatus(peer) === "connected").length,
|
|
2629
2667
|
seenOnTeamCount: discoveredDevices.length,
|
|
2630
|
-
offlineTeamDeviceCount:
|
|
2668
|
+
offlineTeamDeviceCount: mergedDevices.filter((device) => isOfflineTeamDevice(device)).length
|
|
2631
2669
|
},
|
|
2632
2670
|
duplicatePeople,
|
|
2633
2671
|
attentionItems: attentionItems.sort((a, b) => a.priority - b.priority || a.title.localeCompare(b.title))
|
|
@@ -3940,6 +3978,11 @@
|
|
|
3940
3978
|
placeholder: "Invite will appear here",
|
|
3941
3979
|
readOnly: true,
|
|
3942
3980
|
hidden: true
|
|
3981
|
+
}),
|
|
3982
|
+
/* @__PURE__ */ u("div", {
|
|
3983
|
+
className: "peer-meta",
|
|
3984
|
+
id: "syncInviteWarnings",
|
|
3985
|
+
hidden: true
|
|
3943
3986
|
})
|
|
3944
3987
|
]
|
|
3945
3988
|
});
|
|
@@ -4022,6 +4065,12 @@
|
|
|
4022
4065
|
function PairingText({ text }) {
|
|
4023
4066
|
return /* @__PURE__ */ u(k$2, { children: text });
|
|
4024
4067
|
}
|
|
4068
|
+
function SyncEmptyState({ title, detail }) {
|
|
4069
|
+
return /* @__PURE__ */ u("div", {
|
|
4070
|
+
class: "sync-empty-state",
|
|
4071
|
+
children: [/* @__PURE__ */ u("strong", { children: title }), /* @__PURE__ */ u("span", { children: detail })]
|
|
4072
|
+
});
|
|
4073
|
+
}
|
|
4025
4074
|
function renderDiagnosticsGrid(mount, items) {
|
|
4026
4075
|
if (!items.length) {
|
|
4027
4076
|
clearSyncMount(mount);
|
|
@@ -4036,6 +4085,9 @@
|
|
|
4036
4085
|
}
|
|
4037
4086
|
renderIntoSyncMount(mount, /* @__PURE__ */ u(AttemptsList, { attempts }));
|
|
4038
4087
|
}
|
|
4088
|
+
function renderSyncEmptyState(mount, view) {
|
|
4089
|
+
renderIntoSyncMount(mount, /* @__PURE__ */ u(SyncEmptyState, { ...view }));
|
|
4090
|
+
}
|
|
4039
4091
|
function renderPairingView(payloadMount, hintMount, view) {
|
|
4040
4092
|
renderIntoSyncMount(payloadMount, /* @__PURE__ */ u(PairingText, { text: view.payloadText }));
|
|
4041
4093
|
if (hintMount) renderIntoSyncMount(hintMount, /* @__PURE__ */ u(PairingText, { text: view.hintText }));
|
|
@@ -4145,11 +4197,6 @@
|
|
|
4145
4197
|
function assignedActorCount(actorId) {
|
|
4146
4198
|
return (Array.isArray(state.lastSyncPeers) ? state.lastSyncPeers : []).filter((peer) => String(peer?.actor_id || "") === actorId).length;
|
|
4147
4199
|
}
|
|
4148
|
-
function assignmentNote(actorId) {
|
|
4149
|
-
if (!actorId) return "Unassigned devices keep legacy fallback attribution until you choose a person.";
|
|
4150
|
-
if ((Array.isArray(state.lastSyncActors) ? state.lastSyncActors : []).find((item) => String(item?.actor_id || "") === actorId)?.is_local) return "Assigning this device to you keeps it in your identity across your devices, including private sync.";
|
|
4151
|
-
return "This person receives memories from allowed projects by default. Use Only me on a memory when it should stay local.";
|
|
4152
|
-
}
|
|
4153
4200
|
function visibleSyncActors() {
|
|
4154
4201
|
return deriveVisiblePeopleActors({
|
|
4155
4202
|
actors: state.lastSyncActors,
|
|
@@ -4257,6 +4304,13 @@
|
|
|
4257
4304
|
//#region src/tabs/sync/diagnostics.ts
|
|
4258
4305
|
var SYNC_REDACT_MOUNT_ID = "syncRedactMount";
|
|
4259
4306
|
var SYNC_REDACT_LABEL_ID = "syncRedactLabel";
|
|
4307
|
+
function newestPeerPing(peers) {
|
|
4308
|
+
const timestamps = Object.values(peers || {}).map((peer) => {
|
|
4309
|
+
if (!peer || typeof peer !== "object") return "";
|
|
4310
|
+
return String(peer.last_ping_at || "").trim();
|
|
4311
|
+
}).filter(Boolean).sort();
|
|
4312
|
+
return timestamps.length ? timestamps[timestamps.length - 1] : null;
|
|
4313
|
+
}
|
|
4260
4314
|
var _renderSyncPeers = () => {};
|
|
4261
4315
|
function setRenderSyncPeers(fn) {
|
|
4262
4316
|
_renderSyncPeers = fn;
|
|
@@ -4293,6 +4347,31 @@
|
|
|
4293
4347
|
hintText: "Copy this command and run it on the other device. Use --include/--exclude to control which projects sync."
|
|
4294
4348
|
};
|
|
4295
4349
|
}
|
|
4350
|
+
function diagnosticsLoadingState() {
|
|
4351
|
+
return {
|
|
4352
|
+
title: "Diagnostics still loading.",
|
|
4353
|
+
detail: "Wait a moment for local sync status. If it stays blank, refresh the page or check whether sync is enabled on this device."
|
|
4354
|
+
};
|
|
4355
|
+
}
|
|
4356
|
+
function diagnosticsUnavailableState() {
|
|
4357
|
+
return {
|
|
4358
|
+
title: "Diagnostics unavailable right now.",
|
|
4359
|
+
detail: "The viewer could not load sync status. Refresh this page, or check that the local codemem sync service is reachable before retrying."
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4362
|
+
function noAttemptsState() {
|
|
4363
|
+
const syncStatus = state.lastSyncStatus;
|
|
4364
|
+
return {
|
|
4365
|
+
title: "No recent sync attempts yet.",
|
|
4366
|
+
detail: syncStatus?.daemon_state === "disabled" || syncStatus?.enabled === false ? "Turn on sync in Settings → Device Sync first. Recent attempts will appear here after this device can actually run sync work." : "Trigger a sync pass or pair another device to generate activity here when you need low-level troubleshooting."
|
|
4367
|
+
};
|
|
4368
|
+
}
|
|
4369
|
+
function unavailableAttemptsState() {
|
|
4370
|
+
return {
|
|
4371
|
+
title: "Recent attempts unavailable right now.",
|
|
4372
|
+
detail: "Attempt history could not be loaded because sync diagnostics failed. Refresh the page after local sync status is reachable again."
|
|
4373
|
+
};
|
|
4374
|
+
}
|
|
4296
4375
|
function renderSyncStatus() {
|
|
4297
4376
|
const syncStatusGrid = document.getElementById("syncStatusGrid");
|
|
4298
4377
|
const syncMeta = document.getElementById("syncMeta");
|
|
@@ -4301,16 +4380,16 @@
|
|
|
4301
4380
|
hideSkeleton("syncDiagSkeleton");
|
|
4302
4381
|
const status = state.lastSyncStatus;
|
|
4303
4382
|
if (!status) {
|
|
4304
|
-
|
|
4383
|
+
renderSyncEmptyState(syncStatusGrid, diagnosticsLoadingState());
|
|
4305
4384
|
renderActionList(syncActions, []);
|
|
4306
|
-
if (syncMeta) syncMeta.textContent = "Loading sync
|
|
4385
|
+
if (syncMeta) syncMeta.textContent = "Loading advanced sync diagnostics…";
|
|
4307
4386
|
return;
|
|
4308
4387
|
}
|
|
4309
4388
|
const peers = status.peers || {};
|
|
4310
4389
|
const pingPayload = status.ping || {};
|
|
4311
4390
|
const syncPayload = status.sync || {};
|
|
4312
4391
|
const lastSync = status.last_sync_at || status.last_sync_at_utc || null;
|
|
4313
|
-
const lastPing = pingPayload.last_ping_at || status.last_ping_at || null;
|
|
4392
|
+
const lastPing = pingPayload.last_ping_at || status.last_ping_at || newestPeerPing(peers) || null;
|
|
4314
4393
|
const syncError = status.last_sync_error || "";
|
|
4315
4394
|
const pingError = status.last_ping_error || "";
|
|
4316
4395
|
const pending = Number(status.pending || 0);
|
|
@@ -4326,8 +4405,8 @@
|
|
|
4326
4405
|
const peerCount = Object.keys(peers).length;
|
|
4327
4406
|
const syncNoPeers = !syncDisabled && peerCount === 0;
|
|
4328
4407
|
if (syncMeta) {
|
|
4329
|
-
const parts = syncDisabled ? ["
|
|
4330
|
-
`
|
|
4408
|
+
const parts = syncDisabled ? ["Advanced sync is off on this device", "Turn on sync in Settings → Device Sync when you want pairing payloads, peer status, and recent attempt details here"] : syncNoPeers ? ["Advanced sync is ready but idle", "Use Show pairing to connect another device, then this panel will start showing live peer status and recent attempts"] : [
|
|
4409
|
+
`Advanced state: ${daemonStateLabel}`,
|
|
4331
4410
|
`Peers: ${peerCount}`,
|
|
4332
4411
|
lastSync ? `Last sync: ${formatAgeShort(secondsSince(lastSync))} ago` : "Last sync: never"
|
|
4333
4412
|
];
|
|
@@ -4352,7 +4431,7 @@
|
|
|
4352
4431
|
},
|
|
4353
4432
|
{
|
|
4354
4433
|
label: "Last sync",
|
|
4355
|
-
value: "
|
|
4434
|
+
value: "Not running"
|
|
4356
4435
|
}
|
|
4357
4436
|
] : syncNoPeers ? [
|
|
4358
4437
|
{
|
|
@@ -4361,7 +4440,7 @@
|
|
|
4361
4440
|
},
|
|
4362
4441
|
{
|
|
4363
4442
|
label: "Mode",
|
|
4364
|
-
value: "
|
|
4443
|
+
value: "Ready to pair"
|
|
4365
4444
|
},
|
|
4366
4445
|
{
|
|
4367
4446
|
label: "Pending events",
|
|
@@ -4369,7 +4448,7 @@
|
|
|
4369
4448
|
},
|
|
4370
4449
|
{
|
|
4371
4450
|
label: "Last sync",
|
|
4372
|
-
value: "
|
|
4451
|
+
value: "Waiting for first peer"
|
|
4373
4452
|
}
|
|
4374
4453
|
] : [
|
|
4375
4454
|
{
|
|
@@ -4385,7 +4464,7 @@
|
|
|
4385
4464
|
value: lastSync ? `${formatAgeShort(secondsSince(lastSync))} ago` : "never"
|
|
4386
4465
|
},
|
|
4387
4466
|
{
|
|
4388
|
-
label: "Last ping",
|
|
4467
|
+
label: "Last peer ping",
|
|
4389
4468
|
value: lastPing ? `${formatAgeShort(secondsSince(lastPing))} ago` : "never"
|
|
4390
4469
|
},
|
|
4391
4470
|
{
|
|
@@ -4402,7 +4481,7 @@
|
|
|
4402
4481
|
value: `${syncPayload.seconds_since_last}s`
|
|
4403
4482
|
});
|
|
4404
4483
|
if (!syncDisabled && !syncNoPeers && pingPayload.seconds_since_last) items.push({
|
|
4405
|
-
label: "Since last ping",
|
|
4484
|
+
label: "Since last peer ping",
|
|
4406
4485
|
value: `${pingPayload.seconds_since_last}s`
|
|
4407
4486
|
});
|
|
4408
4487
|
if (!syncDisabled && retentionEnabled && retentionLastError) items.push({
|
|
@@ -4417,7 +4496,7 @@
|
|
|
4417
4496
|
command: "codemem sync start"
|
|
4418
4497
|
});
|
|
4419
4498
|
actions.push({
|
|
4420
|
-
label: "
|
|
4499
|
+
label: "Run one sync pass now.",
|
|
4421
4500
|
command: "codemem sync once"
|
|
4422
4501
|
});
|
|
4423
4502
|
} else if (daemonState === "needs_attention") actions.push({
|
|
@@ -4434,7 +4513,7 @@
|
|
|
4434
4513
|
command: "codemem sync restart && codemem sync once"
|
|
4435
4514
|
});
|
|
4436
4515
|
actions.push({
|
|
4437
|
-
label: "
|
|
4516
|
+
label: "Run doctor for the root cause.",
|
|
4438
4517
|
command: "codemem sync doctor"
|
|
4439
4518
|
});
|
|
4440
4519
|
} else if (!syncDisabled && !syncNoPeers && pending > 0) actions.push({
|
|
@@ -4448,7 +4527,7 @@
|
|
|
4448
4527
|
if (!syncAttempts) return;
|
|
4449
4528
|
const attempts = state.lastSyncAttempts;
|
|
4450
4529
|
if (!Array.isArray(attempts) || !attempts.length) {
|
|
4451
|
-
|
|
4530
|
+
renderSyncEmptyState(syncAttempts, noAttemptsState());
|
|
4452
4531
|
return;
|
|
4453
4532
|
}
|
|
4454
4533
|
renderAttemptsList(syncAttempts, attempts.slice(0, 5).map((attempt) => {
|
|
@@ -4460,6 +4539,16 @@
|
|
|
4460
4539
|
};
|
|
4461
4540
|
}));
|
|
4462
4541
|
}
|
|
4542
|
+
function renderSyncDiagnosticsUnavailable() {
|
|
4543
|
+
const syncStatusGrid = document.getElementById("syncStatusGrid");
|
|
4544
|
+
const syncAttempts = document.getElementById("syncAttempts");
|
|
4545
|
+
const syncMeta = document.getElementById("syncMeta");
|
|
4546
|
+
const syncActions = document.getElementById("syncActions");
|
|
4547
|
+
if (syncStatusGrid) renderSyncEmptyState(syncStatusGrid, diagnosticsUnavailableState());
|
|
4548
|
+
if (syncAttempts) renderSyncEmptyState(syncAttempts, unavailableAttemptsState());
|
|
4549
|
+
if (syncMeta) syncMeta.textContent = "Advanced diagnostics are unavailable right now. Refresh the page, or verify the local sync service before retrying.";
|
|
4550
|
+
renderActionList(syncActions, []);
|
|
4551
|
+
}
|
|
4463
4552
|
function renderPairingCollapsible() {
|
|
4464
4553
|
const mount = document.getElementById("syncPairingDisclosureMount");
|
|
4465
4554
|
const contentHost = document.getElementById("syncPairingPanelMount");
|
|
@@ -4573,7 +4662,10 @@
|
|
|
4573
4662
|
className: "sync-action",
|
|
4574
4663
|
children: [/* @__PURE__ */ u("div", {
|
|
4575
4664
|
className: "sync-action-text",
|
|
4576
|
-
children: "
|
|
4665
|
+
children: ["Add another teammate.", /* @__PURE__ */ u("span", {
|
|
4666
|
+
className: "sync-action-command",
|
|
4667
|
+
children: "Generate an invite when you are ready to bring another device into this team."
|
|
4668
|
+
})]
|
|
4577
4669
|
}), /* @__PURE__ */ u("button", {
|
|
4578
4670
|
type: "button",
|
|
4579
4671
|
className: "settings-button",
|
|
@@ -4591,9 +4683,9 @@
|
|
|
4591
4683
|
className: "sync-action",
|
|
4592
4684
|
children: [/* @__PURE__ */ u("div", {
|
|
4593
4685
|
className: "sync-action-text",
|
|
4594
|
-
children: ["
|
|
4686
|
+
children: ["Pair another device.", /* @__PURE__ */ u("span", {
|
|
4595
4687
|
className: "sync-action-command",
|
|
4596
|
-
children: "
|
|
4688
|
+
children: "Use the pairing command in Advanced diagnostics when you are ready to connect one."
|
|
4597
4689
|
})]
|
|
4598
4690
|
}), /* @__PURE__ */ u("button", {
|
|
4599
4691
|
type: "button",
|
|
@@ -4605,26 +4697,34 @@
|
|
|
4605
4697
|
}
|
|
4606
4698
|
function SyncInviteJoinPanels({ invitePanel, invitePanelOpen, inviteRestoreParent, joinPanel, joinRestoreParent, onToggleInvitePanel, pairedPeerCount, presenceStatus }) {
|
|
4607
4699
|
return /* @__PURE__ */ u(k$2, { children: [
|
|
4608
|
-
/* @__PURE__ */ u(
|
|
4700
|
+
presenceStatus === "not_enrolled" ? /* @__PURE__ */ u(k$2, { children: [
|
|
4701
|
+
joinPanel ? /* @__PURE__ */ u(ExistingElementSlot$1, {
|
|
4702
|
+
element: joinPanel,
|
|
4703
|
+
hidden: false,
|
|
4704
|
+
restoreParent: joinRestoreParent
|
|
4705
|
+
}) : null,
|
|
4706
|
+
/* @__PURE__ */ u("div", {
|
|
4707
|
+
className: "peer-meta",
|
|
4708
|
+
id: "syncJoinFeedback",
|
|
4709
|
+
hidden: true
|
|
4710
|
+
}),
|
|
4711
|
+
/* @__PURE__ */ u("div", {
|
|
4712
|
+
className: "sync-action",
|
|
4713
|
+
children: /* @__PURE__ */ u("div", {
|
|
4714
|
+
className: "sync-action-text",
|
|
4715
|
+
children: ["This device is not on the team yet.", /* @__PURE__ */ u("span", {
|
|
4716
|
+
className: "sync-action-command",
|
|
4717
|
+
children: "Import an invite or ask an admin to enroll it first."
|
|
4718
|
+
})]
|
|
4719
|
+
})
|
|
4720
|
+
})
|
|
4721
|
+
] }) : null,
|
|
4722
|
+
presenceStatus !== "not_enrolled" ? /* @__PURE__ */ u(InviteToggleRow, {
|
|
4609
4723
|
invitePanel,
|
|
4610
4724
|
invitePanelOpen,
|
|
4611
4725
|
inviteRestoreParent,
|
|
4612
4726
|
onToggle: onToggleInvitePanel
|
|
4613
|
-
}),
|
|
4614
|
-
presenceStatus === "not_enrolled" ? /* @__PURE__ */ u(k$2, { children: [joinPanel ? /* @__PURE__ */ u(ExistingElementSlot$1, {
|
|
4615
|
-
element: joinPanel,
|
|
4616
|
-
hidden: false,
|
|
4617
|
-
restoreParent: joinRestoreParent
|
|
4618
|
-
}) : null, /* @__PURE__ */ u("div", {
|
|
4619
|
-
className: "sync-action",
|
|
4620
|
-
children: /* @__PURE__ */ u("div", {
|
|
4621
|
-
className: "sync-action-text",
|
|
4622
|
-
children: ["This device is not connected to the team yet.", /* @__PURE__ */ u("span", {
|
|
4623
|
-
className: "sync-action-command",
|
|
4624
|
-
children: "Import a team invite or ask your admin to enroll this device"
|
|
4625
|
-
})]
|
|
4626
|
-
})
|
|
4627
|
-
})] }) : null,
|
|
4727
|
+
}) : null,
|
|
4628
4728
|
!pairedPeerCount && presenceStatus === "posted" ? /* @__PURE__ */ u(PairingCopyRow, {}) : null
|
|
4629
4729
|
] });
|
|
4630
4730
|
}
|
|
@@ -4669,7 +4769,30 @@
|
|
|
4669
4769
|
}, `${item.peerName}:${item.actorId}:${item.scopeLabel}`)) });
|
|
4670
4770
|
}
|
|
4671
4771
|
//#endregion
|
|
4772
|
+
//#region src/tabs/sync/components/sync-inline-feedback.tsx
|
|
4773
|
+
function SyncInlineFeedback({ feedback }) {
|
|
4774
|
+
if (!feedback?.message) return null;
|
|
4775
|
+
return /* @__PURE__ */ u("div", {
|
|
4776
|
+
className: `sync-inline-feedback ${feedback.tone}`,
|
|
4777
|
+
role: feedback.tone === "warning" ? "alert" : "status",
|
|
4778
|
+
"aria-live": feedback.tone === "warning" ? "assertive" : "polite",
|
|
4779
|
+
children: feedback.message
|
|
4780
|
+
});
|
|
4781
|
+
}
|
|
4782
|
+
//#endregion
|
|
4672
4783
|
//#region src/tabs/sync/components/team-sync-panel.tsx
|
|
4784
|
+
function SectionHeading({ count, label }) {
|
|
4785
|
+
return /* @__PURE__ */ u("div", {
|
|
4786
|
+
className: "sync-section-heading",
|
|
4787
|
+
children: [/* @__PURE__ */ u("div", {
|
|
4788
|
+
className: "sync-action-text sync-section-label",
|
|
4789
|
+
children: label
|
|
4790
|
+
}), count ? /* @__PURE__ */ u("span", {
|
|
4791
|
+
className: "badge actor-badge sync-section-count",
|
|
4792
|
+
children: count
|
|
4793
|
+
}) : null]
|
|
4794
|
+
});
|
|
4795
|
+
}
|
|
4673
4796
|
function AttentionRow({ item, onAction }) {
|
|
4674
4797
|
const [busy, setBusy] = d(false);
|
|
4675
4798
|
return /* @__PURE__ */ u("div", {
|
|
@@ -4698,225 +4821,256 @@
|
|
|
4698
4821
|
}
|
|
4699
4822
|
function PendingJoinRequestRow({ request, onApprove, onDeny }) {
|
|
4700
4823
|
const [busyAction, setBusyAction] = d(null);
|
|
4824
|
+
const [feedback, setFeedback] = d(null);
|
|
4701
4825
|
const [approveLabel, setApproveLabel] = d("Approve");
|
|
4702
4826
|
const [denyLabel, setDenyLabel] = d("Deny");
|
|
4703
4827
|
return /* @__PURE__ */ u("div", {
|
|
4704
4828
|
className: "actor-row",
|
|
4705
|
-
children: [
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
})
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
type: "button",
|
|
4718
|
-
className: "settings-button",
|
|
4719
|
-
disabled: busyAction !== null,
|
|
4720
|
-
onClick: async () => {
|
|
4721
|
-
setBusyAction("approve");
|
|
4722
|
-
setApproveLabel("Approving…");
|
|
4723
|
-
try {
|
|
4724
|
-
await onApprove(request);
|
|
4725
|
-
setApproveLabel("Approve");
|
|
4726
|
-
} catch {
|
|
4727
|
-
setApproveLabel("Retry");
|
|
4728
|
-
} finally {
|
|
4729
|
-
setBusyAction(null);
|
|
4730
|
-
}
|
|
4731
|
-
},
|
|
4732
|
-
children: approveLabel
|
|
4733
|
-
}), /* @__PURE__ */ u("button", {
|
|
4734
|
-
type: "button",
|
|
4735
|
-
className: "settings-button",
|
|
4736
|
-
disabled: busyAction !== null,
|
|
4737
|
-
onClick: async () => {
|
|
4738
|
-
setBusyAction("deny");
|
|
4739
|
-
setDenyLabel("Denying…");
|
|
4740
|
-
try {
|
|
4741
|
-
await onDeny(request);
|
|
4742
|
-
setDenyLabel("Deny");
|
|
4743
|
-
} catch {
|
|
4744
|
-
setDenyLabel("Retry deny");
|
|
4745
|
-
} finally {
|
|
4746
|
-
setBusyAction(null);
|
|
4747
|
-
}
|
|
4748
|
-
},
|
|
4749
|
-
children: denyLabel
|
|
4750
|
-
})]
|
|
4751
|
-
})]
|
|
4752
|
-
});
|
|
4753
|
-
}
|
|
4754
|
-
function DiscoveredDeviceRow({ row, onInspectConflict, onRemoveConflict, onReview }) {
|
|
4755
|
-
const [busy, setBusy] = d(null);
|
|
4756
|
-
const [reviewLabel, setReviewLabel] = d(row.actionLabel || "Review device");
|
|
4757
|
-
const [removeLabel, setRemoveLabel] = d("Remove broken device record");
|
|
4758
|
-
return /* @__PURE__ */ u("div", {
|
|
4759
|
-
className: "actor-row",
|
|
4760
|
-
"data-discovered-device-id": row.deviceId,
|
|
4761
|
-
children: [/* @__PURE__ */ u("div", {
|
|
4762
|
-
className: "actor-details",
|
|
4763
|
-
children: [/* @__PURE__ */ u("div", {
|
|
4764
|
-
className: "actor-title",
|
|
4765
|
-
children: [
|
|
4766
|
-
/* @__PURE__ */ u("strong", { children: row.displayName }),
|
|
4767
|
-
/* @__PURE__ */ u("span", {
|
|
4768
|
-
className: `badge actor-badge${row.availabilityLabel === "Offline" ? "" : " local"}`,
|
|
4769
|
-
children: row.availabilityLabel
|
|
4770
|
-
}),
|
|
4771
|
-
/* @__PURE__ */ u("span", {
|
|
4772
|
-
className: "badge actor-badge",
|
|
4773
|
-
children: row.connectionLabel
|
|
4774
|
-
}),
|
|
4775
|
-
row.approvalBadgeLabel ? /* @__PURE__ */ u("span", {
|
|
4776
|
-
className: "badge actor-badge",
|
|
4777
|
-
children: row.approvalBadgeLabel
|
|
4778
|
-
}) : null
|
|
4779
|
-
]
|
|
4780
|
-
}), /* @__PURE__ */ u("div", {
|
|
4781
|
-
className: "peer-meta",
|
|
4782
|
-
children: row.note
|
|
4783
|
-
})]
|
|
4784
|
-
}), /* @__PURE__ */ u("div", {
|
|
4785
|
-
className: "actor-actions",
|
|
4786
|
-
children: [
|
|
4787
|
-
row.mode === "accept" ? /* @__PURE__ */ u("button", {
|
|
4829
|
+
children: [
|
|
4830
|
+
/* @__PURE__ */ u("div", {
|
|
4831
|
+
className: "actor-details",
|
|
4832
|
+
children: /* @__PURE__ */ u("div", {
|
|
4833
|
+
className: "actor-title",
|
|
4834
|
+
title: request.requestId || void 0,
|
|
4835
|
+
children: request.displayName
|
|
4836
|
+
})
|
|
4837
|
+
}),
|
|
4838
|
+
/* @__PURE__ */ u("div", {
|
|
4839
|
+
className: "actor-actions",
|
|
4840
|
+
children: [/* @__PURE__ */ u("button", {
|
|
4788
4841
|
type: "button",
|
|
4789
4842
|
className: "settings-button",
|
|
4790
|
-
disabled:
|
|
4843
|
+
disabled: busyAction !== null,
|
|
4791
4844
|
onClick: async () => {
|
|
4792
|
-
|
|
4793
|
-
|
|
4845
|
+
setBusyAction("approve");
|
|
4846
|
+
setApproveLabel("Approving…");
|
|
4794
4847
|
try {
|
|
4795
|
-
await
|
|
4796
|
-
|
|
4848
|
+
setFeedback(await onApprove(request) || null);
|
|
4849
|
+
setApproveLabel("Approve");
|
|
4797
4850
|
} catch {
|
|
4798
|
-
|
|
4851
|
+
setApproveLabel("Retry");
|
|
4799
4852
|
} finally {
|
|
4800
|
-
|
|
4853
|
+
setBusyAction(null);
|
|
4801
4854
|
}
|
|
4802
4855
|
},
|
|
4803
|
-
children:
|
|
4804
|
-
}) : null,
|
|
4805
|
-
row.mode === "stale" || row.mode === "ambiguous" || row.mode === "scope-pending" ? /* @__PURE__ */ u("div", {
|
|
4806
|
-
className: "peer-meta",
|
|
4807
|
-
children: row.actionMessage
|
|
4808
|
-
}) : null,
|
|
4809
|
-
row.mode === "paired" && row.pairedMessage ? /* @__PURE__ */ u("div", {
|
|
4810
|
-
className: "peer-meta",
|
|
4811
|
-
children: row.pairedMessage
|
|
4812
|
-
}) : null,
|
|
4813
|
-
row.mode === "conflict" ? /* @__PURE__ */ u(k$2, { children: [/* @__PURE__ */ u("button", {
|
|
4814
|
-
type: "button",
|
|
4815
|
-
className: "settings-button",
|
|
4816
|
-
disabled: busy !== null,
|
|
4817
|
-
onClick: () => onInspectConflict(row),
|
|
4818
|
-
children: "Open device details"
|
|
4856
|
+
children: approveLabel
|
|
4819
4857
|
}), /* @__PURE__ */ u("button", {
|
|
4820
4858
|
type: "button",
|
|
4821
4859
|
className: "settings-button",
|
|
4822
|
-
disabled:
|
|
4860
|
+
disabled: busyAction !== null,
|
|
4823
4861
|
onClick: async () => {
|
|
4824
|
-
|
|
4825
|
-
|
|
4862
|
+
setBusyAction("deny");
|
|
4863
|
+
setDenyLabel("Denying…");
|
|
4826
4864
|
try {
|
|
4827
|
-
await
|
|
4828
|
-
|
|
4865
|
+
setFeedback(await onDeny(request) || null);
|
|
4866
|
+
setDenyLabel("Deny");
|
|
4829
4867
|
} catch {
|
|
4830
|
-
|
|
4868
|
+
setDenyLabel("Retry");
|
|
4831
4869
|
} finally {
|
|
4832
|
-
|
|
4870
|
+
setBusyAction(null);
|
|
4833
4871
|
}
|
|
4834
4872
|
},
|
|
4835
|
-
children:
|
|
4836
|
-
})]
|
|
4837
|
-
|
|
4838
|
-
|
|
4873
|
+
children: denyLabel
|
|
4874
|
+
})]
|
|
4875
|
+
}),
|
|
4876
|
+
/* @__PURE__ */ u(SyncInlineFeedback, { feedback })
|
|
4877
|
+
]
|
|
4878
|
+
});
|
|
4879
|
+
}
|
|
4880
|
+
function DiscoveredDeviceRow({ row, onInspectConflict, onRemoveConflict, onReview }) {
|
|
4881
|
+
const [busy, setBusy] = d(null);
|
|
4882
|
+
const [feedback, setFeedback] = d(null);
|
|
4883
|
+
const [reviewLabel, setReviewLabel] = d(row.actionLabel || "Review device");
|
|
4884
|
+
const [removeLabel, setRemoveLabel] = d("Remove broken device record");
|
|
4885
|
+
return /* @__PURE__ */ u("div", {
|
|
4886
|
+
className: "actor-row",
|
|
4887
|
+
"data-discovered-device-id": row.deviceId,
|
|
4888
|
+
children: [
|
|
4889
|
+
/* @__PURE__ */ u("div", {
|
|
4890
|
+
className: "actor-details",
|
|
4891
|
+
children: [/* @__PURE__ */ u("div", {
|
|
4892
|
+
className: "actor-title",
|
|
4893
|
+
children: [
|
|
4894
|
+
/* @__PURE__ */ u("strong", {
|
|
4895
|
+
title: row.displayTitle || void 0,
|
|
4896
|
+
children: row.displayName
|
|
4897
|
+
}),
|
|
4898
|
+
/* @__PURE__ */ u("span", {
|
|
4899
|
+
className: `badge actor-badge${row.availabilityLabel === "Offline" ? "" : " local"}`,
|
|
4900
|
+
children: row.availabilityLabel
|
|
4901
|
+
}),
|
|
4902
|
+
/* @__PURE__ */ u("span", {
|
|
4903
|
+
className: "badge actor-badge",
|
|
4904
|
+
children: row.connectionLabel
|
|
4905
|
+
}),
|
|
4906
|
+
row.approvalBadgeLabel ? /* @__PURE__ */ u("span", {
|
|
4907
|
+
className: "badge actor-badge",
|
|
4908
|
+
children: row.approvalBadgeLabel
|
|
4909
|
+
}) : null
|
|
4910
|
+
]
|
|
4911
|
+
}), /* @__PURE__ */ u("div", {
|
|
4912
|
+
className: "peer-meta",
|
|
4913
|
+
children: row.note
|
|
4914
|
+
})]
|
|
4915
|
+
}),
|
|
4916
|
+
/* @__PURE__ */ u("div", {
|
|
4917
|
+
className: "actor-actions",
|
|
4918
|
+
children: [
|
|
4919
|
+
row.mode === "accept" ? /* @__PURE__ */ u("button", {
|
|
4920
|
+
type: "button",
|
|
4921
|
+
className: "settings-button",
|
|
4922
|
+
disabled: busy !== null,
|
|
4923
|
+
onClick: async () => {
|
|
4924
|
+
setBusy("review");
|
|
4925
|
+
setReviewLabel("Reviewing…");
|
|
4926
|
+
try {
|
|
4927
|
+
setFeedback(await onReview(row) || null);
|
|
4928
|
+
setReviewLabel(row.actionLabel || "Review device");
|
|
4929
|
+
} catch {
|
|
4930
|
+
setReviewLabel("Retry");
|
|
4931
|
+
} finally {
|
|
4932
|
+
setBusy(null);
|
|
4933
|
+
}
|
|
4934
|
+
},
|
|
4935
|
+
children: reviewLabel
|
|
4936
|
+
}) : null,
|
|
4937
|
+
(row.mode === "stale" || row.mode === "ambiguous" || row.mode === "scope-pending") && row.actionMessage ? /* @__PURE__ */ u("div", {
|
|
4938
|
+
className: "peer-meta",
|
|
4939
|
+
children: row.actionMessage
|
|
4940
|
+
}) : null,
|
|
4941
|
+
row.mode === "paired" && row.pairedMessage ? /* @__PURE__ */ u("div", {
|
|
4942
|
+
className: "peer-meta",
|
|
4943
|
+
children: row.pairedMessage
|
|
4944
|
+
}) : null,
|
|
4945
|
+
row.mode === "conflict" ? /* @__PURE__ */ u(k$2, { children: [/* @__PURE__ */ u("button", {
|
|
4946
|
+
type: "button",
|
|
4947
|
+
className: "settings-button",
|
|
4948
|
+
disabled: busy !== null,
|
|
4949
|
+
onClick: () => onInspectConflict(row),
|
|
4950
|
+
children: "Open device details"
|
|
4951
|
+
}), /* @__PURE__ */ u("button", {
|
|
4952
|
+
type: "button",
|
|
4953
|
+
className: "settings-button",
|
|
4954
|
+
disabled: busy !== null,
|
|
4955
|
+
onClick: async () => {
|
|
4956
|
+
setBusy("remove");
|
|
4957
|
+
setRemoveLabel("Removing…");
|
|
4958
|
+
try {
|
|
4959
|
+
setFeedback(await onRemoveConflict(row) || null);
|
|
4960
|
+
setRemoveLabel("Remove broken device record");
|
|
4961
|
+
} catch {
|
|
4962
|
+
setRemoveLabel("Retry");
|
|
4963
|
+
} finally {
|
|
4964
|
+
setBusy(null);
|
|
4965
|
+
}
|
|
4966
|
+
},
|
|
4967
|
+
children: removeLabel
|
|
4968
|
+
})] }) : null
|
|
4969
|
+
]
|
|
4970
|
+
}),
|
|
4971
|
+
/* @__PURE__ */ u(SyncInlineFeedback, { feedback })
|
|
4972
|
+
]
|
|
4839
4973
|
});
|
|
4840
4974
|
}
|
|
4841
4975
|
function ActionContent(props) {
|
|
4842
4976
|
const hasAttentionItems = props.actionItems.length > 0;
|
|
4977
|
+
const hasOtherActionableWork = props.actionableCount > props.actionItems.length;
|
|
4843
4978
|
return /* @__PURE__ */ u(k$2, { children: [
|
|
4844
|
-
hasAttentionItems ? /* @__PURE__ */ u("
|
|
4845
|
-
|
|
4846
|
-
children: "Needs attention"
|
|
4847
|
-
}) : null,
|
|
4848
|
-
hasAttentionItems ? props.actionItems.slice(0, 4).map((item) => /* @__PURE__ */ u(AttentionRow, {
|
|
4979
|
+
hasAttentionItems || hasOtherActionableWork || props.presenceStatus !== "posted" ? /* @__PURE__ */ u(SectionHeading, { label: "Next steps" }) : null,
|
|
4980
|
+
hasAttentionItems ? props.actionItems.map((item) => /* @__PURE__ */ u(AttentionRow, {
|
|
4849
4981
|
item,
|
|
4850
4982
|
onAction: props.onAttentionAction
|
|
4851
4983
|
}, item.id)) : null,
|
|
4852
|
-
!hasAttentionItems && props.presenceStatus === "posted" ? /* @__PURE__ */ u("div", {
|
|
4984
|
+
!hasAttentionItems && !hasOtherActionableWork && props.presenceStatus === "posted" ? /* @__PURE__ */ u("div", {
|
|
4853
4985
|
className: "sync-action",
|
|
4854
4986
|
children: /* @__PURE__ */ u("div", {
|
|
4855
4987
|
className: "sync-action-text",
|
|
4856
|
-
children:
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4988
|
+
children: "Everything is healthy."
|
|
4989
|
+
})
|
|
4990
|
+
}) : null,
|
|
4991
|
+
!hasAttentionItems && hasOtherActionableWork && props.presenceStatus === "posted" ? /* @__PURE__ */ u("div", {
|
|
4992
|
+
className: "sync-action",
|
|
4993
|
+
children: /* @__PURE__ */ u("div", {
|
|
4994
|
+
className: "sync-action-text",
|
|
4995
|
+
children: "Review the team items below when you are ready."
|
|
4860
4996
|
})
|
|
4861
4997
|
}) : null,
|
|
4862
4998
|
!hasAttentionItems && props.presenceStatus === "not_enrolled" ? /* @__PURE__ */ u("div", {
|
|
4863
4999
|
className: "sync-action",
|
|
4864
5000
|
children: /* @__PURE__ */ u("div", {
|
|
4865
5001
|
className: "sync-action-text",
|
|
4866
|
-
children: ["This device
|
|
5002
|
+
children: ["This device needs team enrollment", /* @__PURE__ */ u("span", {
|
|
4867
5003
|
className: "sync-action-command",
|
|
4868
|
-
children: "Import an invite or ask
|
|
5004
|
+
children: "Import an invite or ask an admin to enroll it."
|
|
4869
5005
|
})]
|
|
4870
5006
|
})
|
|
4871
|
-
}) : null
|
|
4872
|
-
props.children
|
|
5007
|
+
}) : null
|
|
4873
5008
|
] });
|
|
4874
5009
|
}
|
|
5010
|
+
function TeamActionsContent({ children }) {
|
|
5011
|
+
if (!children) return null;
|
|
5012
|
+
return /* @__PURE__ */ u(k$2, { children: [/* @__PURE__ */ u(SectionHeading, { label: "Keep the team moving" }), children] });
|
|
5013
|
+
}
|
|
4875
5014
|
function TeamStatusPortal({ mount, statusSummary }) {
|
|
4876
5015
|
return $(/* @__PURE__ */ u("div", {
|
|
4877
5016
|
className: "sync-team-summary",
|
|
4878
|
-
children: [
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
5017
|
+
children: [
|
|
5018
|
+
/* @__PURE__ */ u("div", {
|
|
5019
|
+
className: "sync-team-status-row",
|
|
5020
|
+
children: [/* @__PURE__ */ u("span", {
|
|
5021
|
+
className: "sync-team-status-label",
|
|
5022
|
+
children: "Team status"
|
|
5023
|
+
}), /* @__PURE__ */ u("span", {
|
|
5024
|
+
className: statusSummary.badgeClassName,
|
|
5025
|
+
children: statusSummary.presenceLabel
|
|
5026
|
+
})]
|
|
5027
|
+
}),
|
|
5028
|
+
statusSummary.headline ? /* @__PURE__ */ u("div", {
|
|
5029
|
+
className: "sync-team-headline",
|
|
5030
|
+
children: statusSummary.headline
|
|
5031
|
+
}) : null,
|
|
5032
|
+
/* @__PURE__ */ u("div", {
|
|
5033
|
+
className: "sync-team-metrics sync-team-metrics-secondary",
|
|
5034
|
+
children: statusSummary.metricsText
|
|
5035
|
+
})
|
|
5036
|
+
]
|
|
4891
5037
|
}), mount);
|
|
4892
5038
|
}
|
|
4893
5039
|
function DiscoveredPortal({ mount, rows, onInspectConflict, onRemoveConflict, onReview }) {
|
|
4894
|
-
if (!mount) return null;
|
|
4895
|
-
return $(/* @__PURE__ */ u(k$2, { children:
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
5040
|
+
if (!mount || !rows.length && !state.syncDiscoveredFeedback) return null;
|
|
5041
|
+
return $(/* @__PURE__ */ u(k$2, { children: [
|
|
5042
|
+
/* @__PURE__ */ u(SectionHeading, {
|
|
5043
|
+
count: rows.length || void 0,
|
|
5044
|
+
label: "Devices seen on team"
|
|
5045
|
+
}),
|
|
5046
|
+
/* @__PURE__ */ u(SyncInlineFeedback, { feedback: state.syncDiscoveredFeedback }),
|
|
5047
|
+
rows.map((row) => /* @__PURE__ */ u(DiscoveredDeviceRow, {
|
|
5048
|
+
row,
|
|
5049
|
+
onInspectConflict,
|
|
5050
|
+
onRemoveConflict,
|
|
5051
|
+
onReview
|
|
5052
|
+
}, row.deviceId))
|
|
5053
|
+
] }), mount);
|
|
4901
5054
|
}
|
|
4902
5055
|
function PendingRequestsPortal({ mount, requests, onApprove, onDeny }) {
|
|
4903
|
-
if (!mount || !requests.length) return null;
|
|
4904
|
-
return $(/* @__PURE__ */ u(k$2, { children: [
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
requests
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
5056
|
+
if (!mount || !requests.length && !state.syncJoinRequestsFeedback) return null;
|
|
5057
|
+
return $(/* @__PURE__ */ u(k$2, { children: [
|
|
5058
|
+
/* @__PURE__ */ u(SectionHeading, {
|
|
5059
|
+
count: requests.length || void 0,
|
|
5060
|
+
label: "Pending join requests"
|
|
5061
|
+
}),
|
|
5062
|
+
/* @__PURE__ */ u(SyncInlineFeedback, { feedback: state.syncJoinRequestsFeedback }),
|
|
5063
|
+
requests.map((request) => /* @__PURE__ */ u(PendingJoinRequestRow, {
|
|
5064
|
+
request,
|
|
5065
|
+
onApprove,
|
|
5066
|
+
onDeny
|
|
5067
|
+
}, request.requestId))
|
|
5068
|
+
] }), mount);
|
|
4916
5069
|
}
|
|
4917
5070
|
function TeamSyncPanel(props) {
|
|
4918
5071
|
return /* @__PURE__ */ u(k$2, { children: [
|
|
4919
5072
|
/* @__PURE__ */ u(ActionContent, { ...props }),
|
|
5073
|
+
/* @__PURE__ */ u(TeamActionsContent, { children: props.children }),
|
|
4920
5074
|
/* @__PURE__ */ u(TeamStatusPortal, {
|
|
4921
5075
|
mount: props.listMount,
|
|
4922
5076
|
statusSummary: props.statusSummary
|
|
@@ -9864,14 +10018,43 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
9864
10018
|
}
|
|
9865
10019
|
function setInviteOutputVisibility() {
|
|
9866
10020
|
const syncInviteOutput = document.getElementById("syncInviteOutput");
|
|
10021
|
+
const syncInviteWarnings = document.getElementById("syncInviteWarnings");
|
|
9867
10022
|
if (!syncInviteOutput) return;
|
|
9868
10023
|
const encoded = String(state.lastTeamInvite?.encoded || "").trim();
|
|
9869
10024
|
syncInviteOutput.value = encoded;
|
|
9870
10025
|
syncInviteOutput.hidden = !encoded;
|
|
10026
|
+
if (syncInviteWarnings) {
|
|
10027
|
+
const warnings = Array.isArray(state.lastTeamInvite?.warnings) ? state.lastTeamInvite.warnings : [];
|
|
10028
|
+
syncInviteWarnings.textContent = warnings.join(" · ");
|
|
10029
|
+
syncInviteWarnings.hidden = warnings.length === 0;
|
|
10030
|
+
}
|
|
10031
|
+
}
|
|
10032
|
+
function setJoinFeedbackVisibility() {
|
|
10033
|
+
const syncJoinFeedback = document.getElementById("syncJoinFeedback");
|
|
10034
|
+
if (!syncJoinFeedback) return;
|
|
10035
|
+
const feedback = state.syncJoinFlowFeedback;
|
|
10036
|
+
syncJoinFeedback.textContent = feedback?.message || "";
|
|
10037
|
+
syncJoinFeedback.hidden = !feedback?.message;
|
|
10038
|
+
syncJoinFeedback.setAttribute("role", feedback?.tone === "warning" ? "alert" : "status");
|
|
10039
|
+
syncJoinFeedback.setAttribute("aria-live", feedback?.tone === "warning" ? "assertive" : "polite");
|
|
10040
|
+
syncJoinFeedback.className = `peer-meta${feedback ? ` ${feedback.tone === "warning" ? "sync-inline-feedback warning" : "sync-inline-feedback success"}` : ""}`;
|
|
9871
10041
|
}
|
|
9872
10042
|
function clearContent(node) {
|
|
9873
10043
|
if (node) node.textContent = "";
|
|
9874
10044
|
}
|
|
10045
|
+
function pulseAttentionTarget(target) {
|
|
10046
|
+
if (!(target instanceof HTMLElement)) return;
|
|
10047
|
+
target.classList.remove("sync-attention-target");
|
|
10048
|
+
target.offsetWidth;
|
|
10049
|
+
target.classList.add("sync-attention-target");
|
|
10050
|
+
window.setTimeout(() => target.classList.remove("sync-attention-target"), 900);
|
|
10051
|
+
}
|
|
10052
|
+
function prefersReducedMotion() {
|
|
10053
|
+
return typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
10054
|
+
}
|
|
10055
|
+
function syncScrollBehavior() {
|
|
10056
|
+
return prefersReducedMotion() ? "auto" : "smooth";
|
|
10057
|
+
}
|
|
9875
10058
|
function renderInvitePolicySelect() {
|
|
9876
10059
|
const mount = document.getElementById("syncInvitePolicyMount");
|
|
9877
10060
|
if (!mount) return;
|
|
@@ -9939,11 +10122,13 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
9939
10122
|
const meta = document.getElementById("syncTeamMeta");
|
|
9940
10123
|
const setupPanel = document.getElementById("syncSetupPanel");
|
|
9941
10124
|
const list = document.getElementById("syncTeamStatus");
|
|
10125
|
+
const listHeading = list?.previousElementSibling;
|
|
9942
10126
|
const actions = document.getElementById("syncTeamActions");
|
|
9943
10127
|
if (!meta || !setupPanel || !list || !actions) return;
|
|
9944
10128
|
renderAdminSetupDisclosure();
|
|
9945
10129
|
renderInvitePolicySelect();
|
|
9946
10130
|
setInviteOutputVisibility();
|
|
10131
|
+
setJoinFeedbackVisibility();
|
|
9947
10132
|
const invitePanel = document.getElementById("syncInvitePanel");
|
|
9948
10133
|
const inviteRestoreParent = document.getElementById("syncAdminSection");
|
|
9949
10134
|
const joinPanel = document.getElementById("syncJoinPanel");
|
|
@@ -9957,16 +10142,24 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
9957
10142
|
ensureJoinPanelInSetupSection();
|
|
9958
10143
|
const coordinator = state.lastSyncCoordinator;
|
|
9959
10144
|
const syncView = state.lastSyncViewModel || {
|
|
9960
|
-
summary: {
|
|
10145
|
+
summary: {
|
|
10146
|
+
connectedDeviceCount: 0,
|
|
10147
|
+
seenOnTeamCount: 0,
|
|
10148
|
+
offlineTeamDeviceCount: 0
|
|
10149
|
+
},
|
|
10150
|
+
duplicatePeople: [],
|
|
9961
10151
|
attentionItems: []
|
|
9962
10152
|
};
|
|
9963
10153
|
const focusAttentionTarget = (item) => {
|
|
9964
10154
|
if (item.kind === "possible-duplicate-person") {
|
|
9965
10155
|
const actorList = document.getElementById("syncActorsList");
|
|
9966
|
-
if (actorList instanceof HTMLElement)
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
10156
|
+
if (actorList instanceof HTMLElement) {
|
|
10157
|
+
actorList.scrollIntoView({
|
|
10158
|
+
block: "center",
|
|
10159
|
+
behavior: syncScrollBehavior()
|
|
10160
|
+
});
|
|
10161
|
+
pulseAttentionTarget(actorList);
|
|
10162
|
+
}
|
|
9970
10163
|
return;
|
|
9971
10164
|
}
|
|
9972
10165
|
const deviceId = String(item.deviceId || "").trim();
|
|
@@ -9976,10 +10169,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
9976
10169
|
if (renameInput instanceof HTMLInputElement) {
|
|
9977
10170
|
renameInput.scrollIntoView({
|
|
9978
10171
|
block: "center",
|
|
9979
|
-
behavior:
|
|
10172
|
+
behavior: syncScrollBehavior()
|
|
9980
10173
|
});
|
|
9981
10174
|
renameInput.focus();
|
|
9982
10175
|
renameInput.select();
|
|
10176
|
+
pulseAttentionTarget(renameInput);
|
|
9983
10177
|
return;
|
|
9984
10178
|
}
|
|
9985
10179
|
}
|
|
@@ -9987,21 +10181,25 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
9987
10181
|
if (peerCard instanceof HTMLElement) {
|
|
9988
10182
|
peerCard.scrollIntoView({
|
|
9989
10183
|
block: "center",
|
|
9990
|
-
behavior:
|
|
10184
|
+
behavior: syncScrollBehavior()
|
|
9991
10185
|
});
|
|
10186
|
+
pulseAttentionTarget(peerCard);
|
|
9992
10187
|
return;
|
|
9993
10188
|
}
|
|
9994
10189
|
const discoveredRow = document.querySelector(`[data-discovered-device-id="${CSS.escape(deviceId)}"]`);
|
|
9995
|
-
if (discoveredRow instanceof HTMLElement)
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
10190
|
+
if (discoveredRow instanceof HTMLElement) {
|
|
10191
|
+
discoveredRow.scrollIntoView({
|
|
10192
|
+
block: "center",
|
|
10193
|
+
behavior: syncScrollBehavior()
|
|
10194
|
+
});
|
|
10195
|
+
pulseAttentionTarget(discoveredRow);
|
|
10196
|
+
}
|
|
9999
10197
|
};
|
|
10000
10198
|
const reviewDuplicatePeople = async (item) => {
|
|
10001
10199
|
const actorIds = Array.isArray(item.actorIds) ? item.actorIds.map((value) => String(value || "").trim()).filter(Boolean) : [];
|
|
10002
10200
|
const actors = (Array.isArray(state.lastSyncActors) ? state.lastSyncActors : []).filter((actor) => actorIds.includes(String(actor?.actor_id || "").trim()));
|
|
10003
10201
|
if (actors.length < 2) {
|
|
10004
|
-
showGlobalNotice("This
|
|
10202
|
+
showGlobalNotice("This duplicate review is outdated. Refresh the card and review the remaining people entries.", "warning");
|
|
10005
10203
|
return;
|
|
10006
10204
|
}
|
|
10007
10205
|
const result = await openDuplicatePersonDialog({
|
|
@@ -10023,7 +10221,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10023
10221
|
const primary = actors.find((actor) => String(actor?.actor_id || "") === result.primaryActorId);
|
|
10024
10222
|
const secondary = actors.find((actor) => String(actor?.actor_id || "") === result.secondaryActorId);
|
|
10025
10223
|
if (!primary?.actor_id || !secondary?.actor_id) {
|
|
10026
|
-
showGlobalNotice("Could not determine which people to combine.", "warning");
|
|
10224
|
+
showGlobalNotice("Could not determine which people to combine. Refresh People & devices and try the review again.", "warning");
|
|
10027
10225
|
return;
|
|
10028
10226
|
}
|
|
10029
10227
|
try {
|
|
@@ -10050,7 +10248,8 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10050
10248
|
});
|
|
10051
10249
|
};
|
|
10052
10250
|
const configured = Boolean(coordinator && coordinator.configured);
|
|
10053
|
-
meta.textContent = configured ? `
|
|
10251
|
+
meta.textContent = configured ? `Team: ${(coordinator.groups || []).join(", ") || "none"}` : "Start by joining an existing team or creating one, then connect people and devices.";
|
|
10252
|
+
meta.title = configured ? String(coordinator.coordinator_url || "").trim() : "";
|
|
10054
10253
|
if (!configured) {
|
|
10055
10254
|
teardownTeamSyncRender(actions, [
|
|
10056
10255
|
list,
|
|
@@ -10059,6 +10258,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10059
10258
|
]);
|
|
10060
10259
|
setupPanel.hidden = false;
|
|
10061
10260
|
list.hidden = true;
|
|
10261
|
+
if (listHeading) listHeading.hidden = true;
|
|
10062
10262
|
actions.hidden = true;
|
|
10063
10263
|
if (joinRequests) joinRequests.hidden = true;
|
|
10064
10264
|
if (discoveredPanel) discoveredPanel.hidden = true;
|
|
@@ -10066,21 +10266,24 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10066
10266
|
}
|
|
10067
10267
|
setupPanel.hidden = true;
|
|
10068
10268
|
list.hidden = false;
|
|
10269
|
+
if (listHeading) listHeading.hidden = false;
|
|
10069
10270
|
actions.hidden = false;
|
|
10070
10271
|
const presenceStatus = String(coordinator.presence_status || "");
|
|
10071
|
-
const presenceLabel = presenceStatus === "posted" ? "Connected" : presenceStatus === "not_enrolled" ? "
|
|
10072
|
-
const metricParts = [`Connected devices: ${Number(syncView.summary?.connectedDeviceCount || 0)}`, `Seen on team: ${Number(syncView.summary?.seenOnTeamCount || 0)}`];
|
|
10073
|
-
if (Number(syncView.summary?.offlineTeamDeviceCount || 0) > 0) metricParts.push(`Offline on team: ${Number(syncView.summary?.offlineTeamDeviceCount || 0)}`);
|
|
10074
|
-
const statusSummary = {
|
|
10075
|
-
badgeClassName: `pill ${presenceStatus === "posted" ? "pill-success" : presenceStatus === "not_enrolled" ? "pill-warning" : "pill-error"}`,
|
|
10076
|
-
metricsText: metricParts.join(" · "),
|
|
10077
|
-
presenceLabel
|
|
10078
|
-
};
|
|
10272
|
+
const presenceLabel = presenceStatus === "posted" ? "Connected" : presenceStatus === "not_enrolled" ? "Needs enrollment" : "Connection error";
|
|
10079
10273
|
const localPeers = Array.isArray(state.lastSyncPeers) ? state.lastSyncPeers : [];
|
|
10080
10274
|
const attentionItems = Array.isArray(syncView.attentionItems) ? syncView.attentionItems : [];
|
|
10275
|
+
const connectedCount = Number(syncView.summary?.connectedDeviceCount || 0);
|
|
10276
|
+
const seenOnTeamCount = Number(syncView.summary?.seenOnTeamCount || 0);
|
|
10277
|
+
const offlineTeamDeviceCount = Number(syncView.summary?.offlineTeamDeviceCount || 0);
|
|
10278
|
+
const metricParts = [`Connected ${connectedCount}`, `Team ${seenOnTeamCount}`];
|
|
10279
|
+
if (offlineTeamDeviceCount > 0) metricParts.push(`Offline ${offlineTeamDeviceCount}`);
|
|
10081
10280
|
const discoveredRows = (Array.isArray(coordinator.discovered_devices) ? coordinator.discovered_devices : []).map((device) => {
|
|
10082
10281
|
const deviceId = String(device.device_id || "").trim();
|
|
10083
|
-
const displayName =
|
|
10282
|
+
const displayName = resolveFriendlyDeviceName({
|
|
10283
|
+
coordinatorName: String(device.display_name || "").trim(),
|
|
10284
|
+
deviceId
|
|
10285
|
+
}) || "Discovered device";
|
|
10286
|
+
const displayTitle = deviceId && displayName !== deviceId ? deviceId : null;
|
|
10084
10287
|
const fingerprint = String(device.fingerprint || "").trim();
|
|
10085
10288
|
const hasAmbiguousCoordinatorGroup = (Array.isArray(device.groups) ? device.groups.map((value) => String(value || "").trim()).filter(Boolean) : []).length > 1;
|
|
10086
10289
|
const pairedPeer = localPeers.find((peer) => String(peer?.peer_device_id || "") === deviceId);
|
|
@@ -10092,33 +10295,29 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10092
10295
|
const hasConflict = Boolean(pairedPeer) && Boolean(fingerprint) && Boolean(pairedFingerprint) && pairedFingerprint !== fingerprint;
|
|
10093
10296
|
const canAccept = Boolean(deviceId) && Boolean(fingerprint) && !pairedPeer && !device.stale && !hasAmbiguousCoordinatorGroup;
|
|
10094
10297
|
const addresses = Array.isArray(device.addresses) ? device.addresses : [];
|
|
10095
|
-
const noteParts = [
|
|
10298
|
+
const noteParts = [addresses.length ? addresses.map((address) => isSyncRedactionEnabled() ? redactAddress(String(address || "")) : String(address || "")).filter(Boolean).join(" · ") : "No fresh addresses"];
|
|
10299
|
+
if (!addresses.length && displayTitle) noteParts.push(`device id: ${deviceId}`);
|
|
10096
10300
|
let actionMessage = null;
|
|
10097
10301
|
let mode = canAccept ? "accept" : "none";
|
|
10098
10302
|
let pairedMessage = null;
|
|
10099
|
-
if (hasConflict)
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
} else if (hasAmbiguousCoordinatorGroup) {
|
|
10103
|
-
noteParts.push("this device appears in multiple coordinator groups; review team setup before approving it here");
|
|
10104
|
-
actionMessage = "This device is visible through multiple coordinator groups. Fix that team setup first, then approve it here.";
|
|
10303
|
+
if (hasConflict) mode = "conflict";
|
|
10304
|
+
else if (hasAmbiguousCoordinatorGroup) {
|
|
10305
|
+
actionMessage = "This device appears in multiple coordinator groups. Review team setup first or ask an admin to clean up the duplicate enrollment before approving it here.";
|
|
10105
10306
|
mode = "ambiguous";
|
|
10106
10307
|
} else if (pairedPeer && isPeerScopeReviewPending(deviceId)) {
|
|
10107
|
-
|
|
10108
|
-
actionMessage = `Review this device's scope in People & devices next.`;
|
|
10308
|
+
actionMessage = `Finish this device's scope review in People & devices before you sync it.`;
|
|
10109
10309
|
mode = "scope-pending";
|
|
10110
10310
|
} else if (pairedPeer?.last_error) {
|
|
10111
|
-
noteParts.push(`
|
|
10311
|
+
noteParts.push(`error: ${String(pairedPeer.last_error)}`);
|
|
10112
10312
|
mode = "paired";
|
|
10113
10313
|
} else if (pairedPeer?.status?.peer_state) {
|
|
10114
|
-
noteParts.push(`
|
|
10314
|
+
noteParts.push(`status: ${String(pairedPeer.status.peer_state)}`);
|
|
10115
10315
|
mode = "paired";
|
|
10116
10316
|
} else if (!pairedPeer && device.stale) {
|
|
10117
|
-
actionMessage = "Wait for a fresh coordinator presence update.";
|
|
10317
|
+
actionMessage = "Wait for a fresh coordinator presence update, then review this device again here.";
|
|
10118
10318
|
mode = "stale";
|
|
10119
10319
|
} else if (pairedPeer) mode = "paired";
|
|
10120
|
-
if (approvalSummary.description)
|
|
10121
|
-
if (mode === "paired") pairedMessage = approvalSummary.state === "waiting-for-other-device" ? approvalSummary.description || "Waiting on the other device." : String(pairedPeer?.last_error || "").toLowerCase().includes("401") && String(pairedPeer?.last_error || "").toLowerCase().includes("unauthorized") ? "Waiting for the other device to trust this one before sync can work." : "Manage this device in People & devices.";
|
|
10320
|
+
if (mode === "paired") pairedMessage = approvalSummary.state === "waiting-for-other-device" ? approvalSummary.description || "Waiting on the other device." : String(pairedPeer?.last_error || "").toLowerCase().includes("401") && String(pairedPeer?.last_error || "").toLowerCase().includes("unauthorized") ? "Waiting for the other device to trust this one before sync can work." : null;
|
|
10122
10321
|
return {
|
|
10123
10322
|
actionMessage,
|
|
10124
10323
|
actionLabel: approvalSummary.actionLabel || "Review device",
|
|
@@ -10127,6 +10326,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10127
10326
|
connectionLabel: hasConflict ? SYNC_TERMINOLOGY.conflicts : pairedPeer ? SYNC_TERMINOLOGY.pairedLocally : "Not connected on this device",
|
|
10128
10327
|
deviceId,
|
|
10129
10328
|
displayName,
|
|
10329
|
+
displayTitle,
|
|
10130
10330
|
fingerprint,
|
|
10131
10331
|
mode,
|
|
10132
10332
|
note: noteParts.join(" · "),
|
|
@@ -10137,9 +10337,19 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10137
10337
|
displayName: String(request.display_name || request.device_id || "Pending device"),
|
|
10138
10338
|
requestId: String(request.request_id || "")
|
|
10139
10339
|
}));
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10340
|
+
const discoveredActionableCount = discoveredRows.filter((row) => row.mode === "accept" || row.mode === "scope-pending").length;
|
|
10341
|
+
const actionableCount = attentionItems.length + pendingJoinRequests.length + discoveredActionableCount;
|
|
10342
|
+
const teamLabel = (coordinator.groups || []).join(", ") || "none";
|
|
10343
|
+
const statusSummary = {
|
|
10344
|
+
badgeClassName: `pill ${presenceStatus === "posted" ? "pill-success" : presenceStatus === "not_enrolled" ? "pill-warning" : "pill-error"}`,
|
|
10345
|
+
headline: presenceStatus === "posted" ? actionableCount > 0 ? `${actionableCount} item${actionableCount === 1 ? "" : "s"} need attention` : "Everything is healthy" : presenceStatus === "not_enrolled" ? "This device is not enrolled in the team yet" : "Sync needs attention",
|
|
10346
|
+
metricsText: metricParts.join(" · "),
|
|
10347
|
+
presenceLabel
|
|
10348
|
+
};
|
|
10349
|
+
meta.textContent = presenceStatus === "posted" ? actionableCount > 0 ? `Team: ${teamLabel}. Start with the next step below, then scan the current team status.` : `Team: ${teamLabel}. Team status and device details are below.` : presenceStatus === "not_enrolled" ? `Team: ${teamLabel}. Enroll this device first, then return here to review the rest of the team.` : `Team: ${teamLabel}. Fix the current sync issue first, then use the rest of this card to verify the team state.`;
|
|
10350
|
+
if (discoveredPanel) discoveredPanel.hidden = discoveredRows.length === 0 && !state.syncDiscoveredFeedback;
|
|
10351
|
+
if (discoveredMeta) discoveredMeta.textContent = discoveredRows.length ? "Review anything here that still needs trust, repair, or approval." : "";
|
|
10352
|
+
if (joinRequests) joinRequests.hidden = pendingJoinRequests.length === 0 && !state.syncJoinRequestsFeedback;
|
|
10143
10353
|
teardownTeamSyncRender(actions, [
|
|
10144
10354
|
list,
|
|
10145
10355
|
joinRequests,
|
|
@@ -10150,6 +10360,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10150
10360
|
actions.appendChild(actionMount);
|
|
10151
10361
|
renderIntoSyncMount(actionMount, _$1(TeamSyncPanel, {
|
|
10152
10362
|
actionItems: attentionItems,
|
|
10363
|
+
actionableCount,
|
|
10153
10364
|
children: _$1(SyncInviteJoinPanels, {
|
|
10154
10365
|
invitePanel,
|
|
10155
10366
|
invitePanelOpen: teamInvitePanelOpen,
|
|
@@ -10171,11 +10382,18 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10171
10382
|
onApproveJoinRequest: async (request) => {
|
|
10172
10383
|
try {
|
|
10173
10384
|
await reviewJoinRequest(request.requestId, "approve");
|
|
10174
|
-
|
|
10385
|
+
const feedback = {
|
|
10386
|
+
message: `Approved ${request.displayName}. They can now sync with the team.`,
|
|
10387
|
+
tone: "success"
|
|
10388
|
+
};
|
|
10389
|
+
state.syncJoinRequestsFeedback = feedback;
|
|
10175
10390
|
await _loadSyncData$1();
|
|
10391
|
+
return feedback;
|
|
10176
10392
|
} catch (error) {
|
|
10177
|
-
|
|
10178
|
-
|
|
10393
|
+
return {
|
|
10394
|
+
message: friendlyError(error, "Failed to approve join request. Keep it pending and try again after the coordinator refreshes."),
|
|
10395
|
+
tone: "warning"
|
|
10396
|
+
};
|
|
10179
10397
|
}
|
|
10180
10398
|
},
|
|
10181
10399
|
onAttentionAction: async (item) => {
|
|
@@ -10192,14 +10410,21 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10192
10410
|
confirmLabel: "Deny request",
|
|
10193
10411
|
cancelLabel: "Keep request pending",
|
|
10194
10412
|
tone: "danger"
|
|
10195
|
-
})) return;
|
|
10413
|
+
})) return null;
|
|
10196
10414
|
try {
|
|
10197
10415
|
await reviewJoinRequest(request.requestId, "deny");
|
|
10198
|
-
|
|
10416
|
+
const feedback = {
|
|
10417
|
+
message: `Denied join request from ${request.displayName}.`,
|
|
10418
|
+
tone: "success"
|
|
10419
|
+
};
|
|
10420
|
+
state.syncJoinRequestsFeedback = feedback;
|
|
10199
10421
|
await _loadSyncData$1();
|
|
10422
|
+
return feedback;
|
|
10200
10423
|
} catch (error) {
|
|
10201
|
-
|
|
10202
|
-
|
|
10424
|
+
return {
|
|
10425
|
+
message: friendlyError(error, "Failed to deny join request. Leave it pending for now, then retry after the coordinator refreshes."),
|
|
10426
|
+
tone: "warning"
|
|
10427
|
+
};
|
|
10203
10428
|
}
|
|
10204
10429
|
},
|
|
10205
10430
|
onInspectConflict: (row) => {
|
|
@@ -10207,7 +10432,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10207
10432
|
if (peerCard instanceof HTMLElement) {
|
|
10208
10433
|
peerCard.scrollIntoView({
|
|
10209
10434
|
block: "center",
|
|
10210
|
-
behavior:
|
|
10435
|
+
behavior: syncScrollBehavior()
|
|
10211
10436
|
});
|
|
10212
10437
|
showGlobalNotice(`Opened the conflicting local device record for ${row.displayName}.`, "warning");
|
|
10213
10438
|
return;
|
|
@@ -10221,20 +10446,27 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10221
10446
|
confirmLabel: "Remove device record",
|
|
10222
10447
|
cancelLabel: "Keep device record",
|
|
10223
10448
|
tone: "danger"
|
|
10224
|
-
})) return;
|
|
10449
|
+
})) return null;
|
|
10225
10450
|
try {
|
|
10226
10451
|
await deletePeer(row.deviceId);
|
|
10227
|
-
|
|
10452
|
+
const feedback = {
|
|
10453
|
+
message: `Removed the broken local device record for ${row.displayName}. If it is still available, review it again from Next steps or Devices seen on team.`,
|
|
10454
|
+
tone: "success"
|
|
10455
|
+
};
|
|
10456
|
+
state.syncDiscoveredFeedback = feedback;
|
|
10228
10457
|
await _loadSyncData$1();
|
|
10458
|
+
return feedback;
|
|
10229
10459
|
} catch (error) {
|
|
10230
|
-
|
|
10231
|
-
|
|
10460
|
+
return {
|
|
10461
|
+
message: friendlyError(error, "Failed to remove the broken local device record. The old local record is still present in People & devices."),
|
|
10462
|
+
tone: "warning"
|
|
10463
|
+
};
|
|
10232
10464
|
}
|
|
10233
10465
|
},
|
|
10234
10466
|
onReviewDiscoveredDevice: async (row) => {
|
|
10235
10467
|
try {
|
|
10236
10468
|
const reviewedName = await reviewDiscoveredDeviceName(row.displayName);
|
|
10237
|
-
if (!reviewedName) return;
|
|
10469
|
+
if (!reviewedName) return null;
|
|
10238
10470
|
const result = await acceptDiscoveredPeer(row.deviceId, row.fingerprint);
|
|
10239
10471
|
const optimisticName = String(result?.name || row.displayName || "").trim() || row.displayName;
|
|
10240
10472
|
state.pendingAcceptedSyncPeers = [...Array.isArray(state.pendingAcceptedSyncPeers) ? state.pendingAcceptedSyncPeers.filter((peer) => String(peer?.peer_device_id || "").trim() !== row.deviceId) : [], {
|
|
@@ -10247,23 +10479,44 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10247
10479
|
last_error: "Waiting for the other device to approve this one."
|
|
10248
10480
|
}];
|
|
10249
10481
|
requestPeerScopeReview(row.deviceId);
|
|
10250
|
-
|
|
10482
|
+
let feedback = {
|
|
10483
|
+
message: row.approvalBadgeLabel === "Needs your approval" ? `Approved ${row.displayName} on this device. Two-way trust should be ready once both devices refresh.` : `Step 1 complete on this device for ${row.displayName}. Finish onboarding on the other device so both sides trust each other for sync.`,
|
|
10484
|
+
tone: "success"
|
|
10485
|
+
};
|
|
10251
10486
|
try {
|
|
10252
10487
|
if (reviewedName.trim() !== optimisticName.trim()) {
|
|
10253
10488
|
await renamePeer(row.deviceId, reviewedName.trim());
|
|
10254
|
-
|
|
10489
|
+
state.pendingAcceptedSyncPeers = state.pendingAcceptedSyncPeers.map((peer) => String(peer?.peer_device_id || "").trim() === row.deviceId ? {
|
|
10490
|
+
...peer,
|
|
10491
|
+
name: reviewedName.trim()
|
|
10492
|
+
} : peer);
|
|
10493
|
+
feedback = {
|
|
10494
|
+
message: `Connected ${reviewedName.trim()} and saved its name.`,
|
|
10495
|
+
tone: "success"
|
|
10496
|
+
};
|
|
10255
10497
|
}
|
|
10256
10498
|
} catch (error) {
|
|
10257
|
-
|
|
10499
|
+
feedback = {
|
|
10500
|
+
message: friendlyError(error, "Device connected, but naming did not finish."),
|
|
10501
|
+
tone: "warning"
|
|
10502
|
+
};
|
|
10258
10503
|
}
|
|
10504
|
+
state.syncDiscoveredFeedback = feedback;
|
|
10259
10505
|
try {
|
|
10260
10506
|
await _loadSyncData$1();
|
|
10261
10507
|
} catch (error) {
|
|
10262
|
-
|
|
10508
|
+
feedback = {
|
|
10509
|
+
message: friendlyError(error, "Device connected, but the screen did not refresh yet. Refresh this page before trying the next step."),
|
|
10510
|
+
tone: "warning"
|
|
10511
|
+
};
|
|
10512
|
+
state.syncDiscoveredFeedback = feedback;
|
|
10263
10513
|
}
|
|
10514
|
+
return feedback;
|
|
10264
10515
|
} catch (error) {
|
|
10265
|
-
|
|
10266
|
-
|
|
10516
|
+
return {
|
|
10517
|
+
message: friendlyError(error, "Failed to review this device. Wait for a fresh presence update and try again."),
|
|
10518
|
+
tone: "warning"
|
|
10519
|
+
};
|
|
10267
10520
|
}
|
|
10268
10521
|
},
|
|
10269
10522
|
pendingJoinRequests,
|
|
@@ -10300,17 +10553,23 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10300
10553
|
ttl_hours: ttlValue || 24
|
|
10301
10554
|
});
|
|
10302
10555
|
state.lastTeamInvite = result;
|
|
10556
|
+
setInviteOutputVisibility();
|
|
10303
10557
|
syncInviteOutput.value = String(result.encoded || "");
|
|
10304
10558
|
syncInviteOutput.hidden = false;
|
|
10305
10559
|
syncInviteOutput.focus();
|
|
10306
10560
|
syncInviteOutput.select();
|
|
10307
|
-
|
|
10308
|
-
(
|
|
10561
|
+
const warnings = Array.isArray(result.warnings) ? result.warnings : [];
|
|
10562
|
+
showGlobalNotice(warnings.length ? `Invite created. Copy it above and review ${warnings.length === 1 ? "1 warning" : `${warnings.length} warnings`}.` : "Invite created. Copy the text above and share it with your teammate.", warnings.length ? "warning" : "success");
|
|
10309
10563
|
} catch (error) {
|
|
10310
|
-
showGlobalNotice(friendlyError(error, "Failed to create invite."), "warning");
|
|
10311
|
-
|
|
10564
|
+
showGlobalNotice(friendlyError(error, "Failed to create invite. Check the team name, invite lifetime, and coordinator reachability, then try again."), "warning");
|
|
10565
|
+
syncCreateInviteButton.textContent = "Retry";
|
|
10312
10566
|
syncCreateInviteButton.disabled = false;
|
|
10313
|
-
|
|
10567
|
+
return;
|
|
10568
|
+
} finally {
|
|
10569
|
+
if (syncCreateInviteButton.disabled) {
|
|
10570
|
+
syncCreateInviteButton.disabled = false;
|
|
10571
|
+
syncCreateInviteButton.textContent = "Create invite";
|
|
10572
|
+
}
|
|
10314
10573
|
}
|
|
10315
10574
|
});
|
|
10316
10575
|
syncJoinButton?.addEventListener("click", async () => {
|
|
@@ -10326,14 +10585,37 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10326
10585
|
try {
|
|
10327
10586
|
const result = await importCoordinatorInvite(inviteValue);
|
|
10328
10587
|
state.lastTeamJoin = result;
|
|
10329
|
-
|
|
10588
|
+
let feedback = {
|
|
10589
|
+
message: result.status === "pending" ? "Join request sent. Waiting for admin approval." : "Joined the team.",
|
|
10590
|
+
tone: "success"
|
|
10591
|
+
};
|
|
10592
|
+
state.syncJoinFlowFeedback = feedback;
|
|
10593
|
+
setJoinFeedbackVisibility();
|
|
10330
10594
|
syncJoinInvite.value = "";
|
|
10331
|
-
|
|
10595
|
+
try {
|
|
10596
|
+
await loadSyncData();
|
|
10597
|
+
} catch (error) {
|
|
10598
|
+
feedback = {
|
|
10599
|
+
message: friendlyError(error, "Joined the team, but this view has not refreshed yet."),
|
|
10600
|
+
tone: "warning"
|
|
10601
|
+
};
|
|
10602
|
+
state.syncJoinFlowFeedback = feedback;
|
|
10603
|
+
setJoinFeedbackVisibility();
|
|
10604
|
+
}
|
|
10332
10605
|
} catch (error) {
|
|
10333
|
-
|
|
10334
|
-
|
|
10606
|
+
state.syncJoinFlowFeedback = {
|
|
10607
|
+
message: friendlyError(error, "Failed to import invite. Check that the invite is complete, current, and meant for this team, then try again."),
|
|
10608
|
+
tone: "warning"
|
|
10609
|
+
};
|
|
10610
|
+
setJoinFeedbackVisibility();
|
|
10611
|
+
syncJoinButton.textContent = "Retry";
|
|
10335
10612
|
syncJoinButton.disabled = false;
|
|
10336
|
-
|
|
10613
|
+
return;
|
|
10614
|
+
} finally {
|
|
10615
|
+
if (syncJoinButton.disabled) {
|
|
10616
|
+
syncJoinButton.disabled = false;
|
|
10617
|
+
syncJoinButton.textContent = "Join team";
|
|
10618
|
+
}
|
|
10337
10619
|
}
|
|
10338
10620
|
});
|
|
10339
10621
|
syncNowButton?.addEventListener("click", async () => {
|
|
@@ -10344,7 +10626,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10344
10626
|
const summary = summarizeSyncRunResult(await triggerSync());
|
|
10345
10627
|
showGlobalNotice(summary.message, summary.warning ? "warning" : void 0);
|
|
10346
10628
|
} catch (error) {
|
|
10347
|
-
showGlobalNotice(friendlyError(error, "Failed to start sync."), "warning");
|
|
10629
|
+
showGlobalNotice(friendlyError(error, "Failed to start sync. Retry once, then run codemem sync doctor if the problem keeps coming back."), "warning");
|
|
10630
|
+
syncNowButton.textContent = "Retry";
|
|
10631
|
+
syncNowButton.disabled = false;
|
|
10632
|
+
return;
|
|
10348
10633
|
}
|
|
10349
10634
|
syncNowButton.disabled = false;
|
|
10350
10635
|
syncNowButton.textContent = "Sync now";
|
|
@@ -10432,12 +10717,17 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10432
10717
|
className: "actor-title",
|
|
10433
10718
|
children: [/* @__PURE__ */ u("strong", { children: actorDisplayLabel(actor) }), /* @__PURE__ */ u("span", {
|
|
10434
10719
|
className: `badge actor-badge${actor.is_local ? " local" : ""}`,
|
|
10720
|
+
title: actor.is_local ? localActorNote(hiddenLocalDuplicateCount) : void 0,
|
|
10435
10721
|
children: actor.is_local ? "You" : `${count} device${count === 1 ? "" : "s"}`
|
|
10436
10722
|
})]
|
|
10437
|
-
}), /* @__PURE__ */ u("div", {
|
|
10723
|
+
}), !actor.is_local ? /* @__PURE__ */ u("div", {
|
|
10438
10724
|
className: "peer-meta",
|
|
10439
|
-
children:
|
|
10440
|
-
|
|
10725
|
+
children: [
|
|
10726
|
+
count,
|
|
10727
|
+
" assigned device",
|
|
10728
|
+
count === 1 ? "" : "s"
|
|
10729
|
+
]
|
|
10730
|
+
}) : null]
|
|
10441
10731
|
}), /* @__PURE__ */ u("div", {
|
|
10442
10732
|
className: "actor-actions",
|
|
10443
10733
|
children: actor.is_local ? /* @__PURE__ */ u("div", {
|
|
@@ -10493,7 +10783,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10493
10783
|
function SyncActorsList({ actors, hiddenLocalDuplicateCount, onRename, onMerge }) {
|
|
10494
10784
|
if (!actors.length) return /* @__PURE__ */ u("div", {
|
|
10495
10785
|
className: "sync-empty-state",
|
|
10496
|
-
children: "No people yet. Create
|
|
10786
|
+
children: [/* @__PURE__ */ u("strong", { children: "No people yet." }), /* @__PURE__ */ u("span", { children: "Create a named person here, then assign each device below to keep sync ownership readable." })]
|
|
10497
10787
|
});
|
|
10498
10788
|
return /* @__PURE__ */ u(k$2, { children: actors.map((actor) => {
|
|
10499
10789
|
const actorId = String(actor.actor_id || actor.display_name || actor.is_local || "");
|
|
@@ -10544,9 +10834,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10544
10834
|
function listText(value) {
|
|
10545
10835
|
return Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean) : [];
|
|
10546
10836
|
}
|
|
10547
|
-
function summaryText(prefix, values, emptyLabel) {
|
|
10548
|
-
return `${prefix}: ${values.join(", ") || emptyLabel}`;
|
|
10549
|
-
}
|
|
10550
10837
|
function ExistingElementSlot({ element }) {
|
|
10551
10838
|
const hostRef = A$1(null);
|
|
10552
10839
|
_(() => {
|
|
@@ -10569,9 +10856,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10569
10856
|
const scope = peer.project_scope || {};
|
|
10570
10857
|
const includeList = listText(scope.include);
|
|
10571
10858
|
const excludeList = listText(scope.exclude);
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10859
|
+
listText(scope.effective_include);
|
|
10860
|
+
listText(scope.effective_exclude);
|
|
10861
|
+
Boolean(scope.inherits_global);
|
|
10575
10862
|
const primaryAddress = pickPrimaryAddress(peer.addresses);
|
|
10576
10863
|
const peerAddresses = Array.isArray(peer.addresses) ? Array.from(new Set(peer.addresses.filter(Boolean).map((value) => String(value)))) : [];
|
|
10577
10864
|
const addressLine = peerAddresses.length ? peerAddresses.map((address) => isSyncRedactionEnabled() ? redactAddress(address) : address).join(" · ") : "No addresses";
|
|
@@ -10582,6 +10869,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10582
10869
|
const cardRef = A$1(null);
|
|
10583
10870
|
const [scopeHost, setScopeHost] = d(null);
|
|
10584
10871
|
const [renameValue, setRenameValue] = d(displayName);
|
|
10872
|
+
const [feedback, setFeedback] = d(() => state.syncPeerFeedbackById.get(peerId) ?? null);
|
|
10585
10873
|
const [renameBusy, setRenameBusy] = d(false);
|
|
10586
10874
|
const [renameLabel, setRenameLabel] = d("Save name");
|
|
10587
10875
|
const [syncBusy, setSyncBusy] = d(false);
|
|
@@ -10614,6 +10902,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10614
10902
|
const excludeEditor = T$1(() => createChipEditor(excludeList, "Add excluded project", "No exclusions"), [peerId, excludeList.join("|")]);
|
|
10615
10903
|
y(() => {
|
|
10616
10904
|
setRenameValue(displayName);
|
|
10905
|
+
setFeedback(state.syncPeerFeedbackById.get(peerId) ?? null);
|
|
10617
10906
|
setRenameBusy(false);
|
|
10618
10907
|
setRenameLabel("Save name");
|
|
10619
10908
|
setSyncBusy(false);
|
|
@@ -10637,28 +10926,33 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10637
10926
|
if (!scopeReviewRequested || !cardRef.current) return;
|
|
10638
10927
|
queueMicrotask(() => cardRef.current?.scrollIntoView({
|
|
10639
10928
|
block: "center",
|
|
10640
|
-
behavior: "smooth"
|
|
10929
|
+
behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth"
|
|
10641
10930
|
}));
|
|
10642
10931
|
}, [scopeReviewRequested]);
|
|
10643
10932
|
async function rename() {
|
|
10644
10933
|
if (!peerId) return;
|
|
10645
10934
|
const nextName = renameValue.trim();
|
|
10646
10935
|
if (!nextName) {
|
|
10647
|
-
|
|
10936
|
+
const warning = {
|
|
10937
|
+
message: "Enter a friendly name for this device.",
|
|
10938
|
+
tone: "warning"
|
|
10939
|
+
};
|
|
10940
|
+
setFeedback(warning);
|
|
10941
|
+
state.syncPeerFeedbackById.set(peerId, warning);
|
|
10648
10942
|
document.querySelector(`[data-device-name-input="${CSS.escape(peerId)}"]`)?.focus();
|
|
10649
10943
|
return;
|
|
10650
10944
|
}
|
|
10651
10945
|
setRenameBusy(true);
|
|
10652
10946
|
setRenameLabel("Saving…");
|
|
10653
|
-
let ok = false;
|
|
10654
10947
|
try {
|
|
10655
|
-
await onRename(peerId, nextName);
|
|
10656
|
-
|
|
10948
|
+
const nextFeedback = await onRename(peerId, nextName);
|
|
10949
|
+
setFeedback(nextFeedback);
|
|
10950
|
+
state.syncPeerFeedbackById.set(peerId, nextFeedback);
|
|
10951
|
+
setRenameLabel("Save name");
|
|
10657
10952
|
} catch {
|
|
10658
|
-
setRenameLabel("Retry
|
|
10953
|
+
setRenameLabel("Retry");
|
|
10659
10954
|
} finally {
|
|
10660
10955
|
setRenameBusy(false);
|
|
10661
|
-
if (ok) setRenameLabel("Save name");
|
|
10662
10956
|
}
|
|
10663
10957
|
}
|
|
10664
10958
|
async function sync() {
|
|
@@ -10673,7 +10967,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10673
10967
|
}
|
|
10674
10968
|
setSyncBusy(true);
|
|
10675
10969
|
try {
|
|
10676
|
-
await onSync(peer, primaryAddress);
|
|
10970
|
+
const nextFeedback = await onSync(peer, primaryAddress);
|
|
10971
|
+
setFeedback(nextFeedback);
|
|
10972
|
+
if (nextFeedback) state.syncPeerFeedbackById.set(peerId, nextFeedback);
|
|
10677
10973
|
} finally {
|
|
10678
10974
|
setSyncBusy(false);
|
|
10679
10975
|
}
|
|
@@ -10703,46 +10999,46 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10703
10999
|
async function savePerson() {
|
|
10704
11000
|
if (!peerId) return;
|
|
10705
11001
|
setApplyActorBusy(true);
|
|
10706
|
-
setApplyActorLabel("
|
|
10707
|
-
let ok = false;
|
|
11002
|
+
setApplyActorLabel("Saving…");
|
|
10708
11003
|
try {
|
|
10709
|
-
await onAssignActor(peerId, selectedActorId || null);
|
|
10710
|
-
|
|
11004
|
+
const nextFeedback = await onAssignActor(peerId, selectedActorId || null);
|
|
11005
|
+
setFeedback(nextFeedback);
|
|
11006
|
+
state.syncPeerFeedbackById.set(peerId, nextFeedback);
|
|
11007
|
+
setApplyActorLabel("Save person");
|
|
10711
11008
|
} catch {
|
|
10712
11009
|
setApplyActorLabel("Retry");
|
|
10713
11010
|
} finally {
|
|
10714
11011
|
setApplyActorBusy(false);
|
|
10715
|
-
if (ok) setApplyActorLabel("Save person");
|
|
10716
11012
|
}
|
|
10717
11013
|
}
|
|
10718
11014
|
async function saveScope() {
|
|
10719
11015
|
if (!peerId) return;
|
|
10720
11016
|
setSaveScopeBusy(true);
|
|
10721
11017
|
setSaveScopeLabel("Saving…");
|
|
10722
|
-
let ok = false;
|
|
10723
11018
|
try {
|
|
10724
|
-
await onSaveScope(peerId, includeEditor.values(), excludeEditor.values());
|
|
10725
|
-
|
|
11019
|
+
const nextFeedback = await onSaveScope(peerId, includeEditor.values(), excludeEditor.values());
|
|
11020
|
+
setFeedback(nextFeedback);
|
|
11021
|
+
state.syncPeerFeedbackById.set(peerId, nextFeedback);
|
|
11022
|
+
setSaveScopeLabel("Save scope");
|
|
10726
11023
|
} catch {
|
|
10727
|
-
setSaveScopeLabel("Retry
|
|
11024
|
+
setSaveScopeLabel("Retry");
|
|
10728
11025
|
} finally {
|
|
10729
11026
|
setSaveScopeBusy(false);
|
|
10730
|
-
if (ok) setSaveScopeLabel("Save scope");
|
|
10731
11027
|
}
|
|
10732
11028
|
}
|
|
10733
11029
|
async function resetScope() {
|
|
10734
11030
|
if (!peerId) return;
|
|
10735
11031
|
setResetScopeBusy(true);
|
|
10736
11032
|
setResetScopeLabel("Resetting…");
|
|
10737
|
-
let ok = false;
|
|
10738
11033
|
try {
|
|
10739
|
-
await onResetScope(peerId);
|
|
10740
|
-
|
|
11034
|
+
const nextFeedback = await onResetScope(peerId);
|
|
11035
|
+
setFeedback(nextFeedback);
|
|
11036
|
+
state.syncPeerFeedbackById.set(peerId, nextFeedback);
|
|
11037
|
+
setResetScopeLabel("Reset to global scope");
|
|
10741
11038
|
} catch {
|
|
10742
|
-
setResetScopeLabel("Retry
|
|
11039
|
+
setResetScopeLabel("Retry");
|
|
10743
11040
|
} finally {
|
|
10744
11041
|
setResetScopeBusy(false);
|
|
10745
|
-
if (ok) setResetScopeLabel("Reset to global scope");
|
|
10746
11042
|
}
|
|
10747
11043
|
}
|
|
10748
11044
|
return /* @__PURE__ */ u("div", {
|
|
@@ -10836,10 +11132,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10836
11132
|
children: [
|
|
10837
11133
|
scopeReviewRequested ? /* @__PURE__ */ u("div", {
|
|
10838
11134
|
className: "peer-meta",
|
|
10839
|
-
children: "Review this device's
|
|
11135
|
+
children: "Review this device's sharing scope now."
|
|
10840
11136
|
}) : pendingScopeReview ? /* @__PURE__ */ u("div", {
|
|
10841
11137
|
className: "peer-meta",
|
|
10842
|
-
children: "Scope review still pending.
|
|
11138
|
+
children: "Scope review still pending."
|
|
10843
11139
|
}) : null,
|
|
10844
11140
|
/* @__PURE__ */ u("div", {
|
|
10845
11141
|
className: "peer-scope-summary",
|
|
@@ -10849,10 +11145,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10849
11145
|
className: "peer-meta",
|
|
10850
11146
|
children: peer.actor_display_name ? `Assigned to ${peer.claimed_local_actor ? "You" : String(peer.actor_display_name)}` : "Unassigned person"
|
|
10851
11147
|
}),
|
|
10852
|
-
/* @__PURE__ */ u("div", {
|
|
10853
|
-
className: "peer-meta",
|
|
10854
|
-
children: trustSummary.description
|
|
10855
|
-
}),
|
|
10856
11148
|
/* @__PURE__ */ u("div", {
|
|
10857
11149
|
className: "peer-actor-row",
|
|
10858
11150
|
children: [/* @__PURE__ */ u("div", {
|
|
@@ -10876,18 +11168,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10876
11168
|
children: applyActorLabel
|
|
10877
11169
|
})]
|
|
10878
11170
|
}),
|
|
10879
|
-
/* @__PURE__ */ u(
|
|
10880
|
-
className: "peer-scope-effective",
|
|
10881
|
-
children: assignmentNote(selectedActorId)
|
|
10882
|
-
}),
|
|
10883
|
-
/* @__PURE__ */ u("div", {
|
|
10884
|
-
className: "peer-scope-summary",
|
|
10885
|
-
children: inheritsGlobal ? "Using global sync scope" : `Device override · include: ${includeList.join(", ") || "all"} · exclude: ${excludeList.join(", ") || "none"}`
|
|
10886
|
-
}),
|
|
10887
|
-
/* @__PURE__ */ u("div", {
|
|
10888
|
-
className: "peer-scope-effective",
|
|
10889
|
-
children: `Effective scope · ${summaryText("include", effectiveInclude, "all")} · ${summaryText("exclude", effectiveExclude, "none")}`
|
|
10890
|
-
}),
|
|
11171
|
+
/* @__PURE__ */ u(SyncInlineFeedback, { feedback }),
|
|
10891
11172
|
/* @__PURE__ */ u("div", { ref: setScopeHost })
|
|
10892
11173
|
]
|
|
10893
11174
|
})
|
|
@@ -10895,17 +11176,20 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10895
11176
|
});
|
|
10896
11177
|
}
|
|
10897
11178
|
function SyncPeersList(props) {
|
|
10898
|
-
|
|
11179
|
+
const sectionFeedback = state.syncPeersSectionFeedback;
|
|
11180
|
+
const syncStatus = state.lastSyncStatus;
|
|
11181
|
+
const syncDisabled = syncStatus?.daemon_state === "disabled" || syncStatus?.enabled === false;
|
|
11182
|
+
if (!props.peers.length) return /* @__PURE__ */ u(k$2, { children: [/* @__PURE__ */ u(SyncInlineFeedback, { feedback: sectionFeedback }), /* @__PURE__ */ u("div", {
|
|
10899
11183
|
className: "sync-empty-state",
|
|
10900
|
-
children: "No devices
|
|
10901
|
-
});
|
|
10902
|
-
return /* @__PURE__ */ u(k$2, { children: props.peers.map((peer) => {
|
|
11184
|
+
children: [/* @__PURE__ */ u("strong", { children: "No paired devices yet." }), /* @__PURE__ */ u("span", { children: syncDisabled ? "Turn on sync in Settings → Device Sync first, then use Show pairing in Advanced diagnostics to connect another device." : "Use the Show pairing control in Advanced diagnostics, run the command on the other device, then come back here to name and assign it." })]
|
|
11185
|
+
})] });
|
|
11186
|
+
return /* @__PURE__ */ u(k$2, { children: [/* @__PURE__ */ u(SyncInlineFeedback, { feedback: sectionFeedback }), props.peers.map((peer) => {
|
|
10903
11187
|
const peerId = String(peer.peer_device_id || peer.name || "unknown-peer");
|
|
10904
11188
|
return /* @__PURE__ */ u(SyncPeerCard, {
|
|
10905
11189
|
peer,
|
|
10906
11190
|
...props
|
|
10907
11191
|
}, peerId);
|
|
10908
|
-
}) });
|
|
11192
|
+
})] });
|
|
10909
11193
|
}
|
|
10910
11194
|
function renderSyncPeersList(mount, props) {
|
|
10911
11195
|
renderIntoSyncMount(mount, /* @__PURE__ */ u(SyncPeersList, { ...props }));
|
|
@@ -10961,6 +11245,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10961
11245
|
//#region src/tabs/sync/people.ts
|
|
10962
11246
|
var _loadSyncData = async () => {};
|
|
10963
11247
|
var legacyDeviceValue = "";
|
|
11248
|
+
function setPeopleCreateControlsDisabled(disabled) {
|
|
11249
|
+
const createButton = document.getElementById("syncActorCreateButton");
|
|
11250
|
+
const createInput = document.getElementById("syncActorCreateInput");
|
|
11251
|
+
if (createButton) createButton.disabled = disabled;
|
|
11252
|
+
if (createInput) createInput.disabled = disabled;
|
|
11253
|
+
}
|
|
10964
11254
|
function setLoadSyncData(fn) {
|
|
10965
11255
|
_loadSyncData = fn;
|
|
10966
11256
|
}
|
|
@@ -10969,6 +11259,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10969
11259
|
const actorMeta = document.getElementById("syncActorsMeta");
|
|
10970
11260
|
if (!actorList) return;
|
|
10971
11261
|
hideSkeleton("syncActorsSkeleton");
|
|
11262
|
+
setPeopleCreateControlsDisabled(false);
|
|
10972
11263
|
const actorVisibility = deriveVisiblePeopleActors({
|
|
10973
11264
|
actors: state.lastSyncActors,
|
|
10974
11265
|
peers: state.lastSyncPeers,
|
|
@@ -10976,7 +11267,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10976
11267
|
});
|
|
10977
11268
|
const actors = actorVisibility.visibleActors;
|
|
10978
11269
|
if (actorMeta) {
|
|
10979
|
-
actorMeta.textContent = actors.length ? "
|
|
11270
|
+
actorMeta.textContent = actors.length ? "Manage people here, then assign devices below." : "No named people yet. Create a person here, then assign devices below so sync ownership is easier to review.";
|
|
10980
11271
|
if (actorVisibility.hiddenLocalDuplicateCount > 0) actorMeta.textContent += ` ${actorVisibility.hiddenLocalDuplicateCount} unresolved duplicate ${actorVisibility.hiddenLocalDuplicateCount === 1 ? "entry is" : "entries are"} hidden here until reviewed in Needs attention.`;
|
|
10981
11272
|
}
|
|
10982
11273
|
renderSyncActorsList(actorList, {
|
|
@@ -10998,6 +11289,16 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10998
11289
|
}
|
|
10999
11290
|
});
|
|
11000
11291
|
}
|
|
11292
|
+
function renderSyncActorsUnavailable() {
|
|
11293
|
+
const actorList = document.getElementById("syncActorsList");
|
|
11294
|
+
const actorMeta = document.getElementById("syncActorsMeta");
|
|
11295
|
+
setPeopleCreateControlsDisabled(true);
|
|
11296
|
+
if (actorMeta) actorMeta.textContent = "People controls are temporarily unavailable. Refresh this page to retry, but device status and sync health are still available below.";
|
|
11297
|
+
if (actorList) renderSyncEmptyState(actorList, {
|
|
11298
|
+
title: "People unavailable right now.",
|
|
11299
|
+
detail: "Refresh this page to reload named people once the people endpoint is responding again."
|
|
11300
|
+
});
|
|
11301
|
+
}
|
|
11001
11302
|
function renderSyncPeers() {
|
|
11002
11303
|
const syncPeers = document.getElementById("syncPeers");
|
|
11003
11304
|
if (!syncPeers) return;
|
|
@@ -11008,75 +11309,133 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11008
11309
|
onRename: async (peerId, nextName) => {
|
|
11009
11310
|
try {
|
|
11010
11311
|
await renamePeer(peerId, nextName);
|
|
11011
|
-
showGlobalNotice("Device name saved.");
|
|
11012
11312
|
await _loadSyncData();
|
|
11313
|
+
return {
|
|
11314
|
+
message: "Device name saved.",
|
|
11315
|
+
tone: "success"
|
|
11316
|
+
};
|
|
11013
11317
|
} catch (error) {
|
|
11014
|
-
|
|
11015
|
-
|
|
11318
|
+
return {
|
|
11319
|
+
message: friendlyError(error, "Failed to save device name."),
|
|
11320
|
+
tone: "warning"
|
|
11321
|
+
};
|
|
11016
11322
|
}
|
|
11017
11323
|
},
|
|
11018
11324
|
onSync: async (peer, address) => {
|
|
11019
11325
|
try {
|
|
11020
11326
|
const summary = summarizeSyncRunResult(await triggerSync(address));
|
|
11021
11327
|
const peerId = String(peer?.peer_device_id || "");
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11328
|
+
let feedback;
|
|
11329
|
+
if (!summary.ok) feedback = {
|
|
11330
|
+
message: summary.message,
|
|
11331
|
+
tone: "warning"
|
|
11332
|
+
};
|
|
11333
|
+
else if (peerId && isPeerScopeReviewPending(peerId)) feedback = {
|
|
11334
|
+
message: `Triggered sync for ${peer?.name || (peerId ? peerId.slice(0, 8) : "unknown")}. Review scope in this card if you want tighter sharing rules.`,
|
|
11335
|
+
tone: "warning"
|
|
11336
|
+
};
|
|
11337
|
+
else feedback = {
|
|
11338
|
+
message: summary.message,
|
|
11339
|
+
tone: "success"
|
|
11340
|
+
};
|
|
11341
|
+
try {
|
|
11342
|
+
await _loadSyncData();
|
|
11343
|
+
} catch {
|
|
11344
|
+
feedback = {
|
|
11345
|
+
message: "Sync started, but this view has not refreshed yet. Refresh the page or use Sync now again before retrying.",
|
|
11346
|
+
tone: "warning"
|
|
11347
|
+
};
|
|
11348
|
+
}
|
|
11349
|
+
return feedback;
|
|
11025
11350
|
} catch (error) {
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11029
|
-
|
|
11030
|
-
await _loadSyncData();
|
|
11031
|
-
} catch {
|
|
11032
|
-
showGlobalNotice("Sync started, but the local status view did not refresh yet.", "warning");
|
|
11351
|
+
return {
|
|
11352
|
+
message: friendlyError(error, "Failed to trigger sync."),
|
|
11353
|
+
tone: "warning"
|
|
11354
|
+
};
|
|
11033
11355
|
}
|
|
11034
|
-
return null;
|
|
11035
11356
|
},
|
|
11036
11357
|
onRemove: async (peerId, label) => {
|
|
11037
11358
|
try {
|
|
11038
11359
|
await deletePeer(peerId);
|
|
11039
|
-
|
|
11360
|
+
const feedback = {
|
|
11361
|
+
message: `Removed peer ${label}.`,
|
|
11362
|
+
tone: "success"
|
|
11363
|
+
};
|
|
11364
|
+
state.syncPeerFeedbackById.delete(peerId);
|
|
11365
|
+
state.syncPeersSectionFeedback = feedback;
|
|
11040
11366
|
await _loadSyncData();
|
|
11367
|
+
return feedback;
|
|
11041
11368
|
} catch (error) {
|
|
11042
|
-
|
|
11043
|
-
|
|
11369
|
+
return {
|
|
11370
|
+
message: friendlyError(error, "Failed to remove peer. The local peer entry is still here."),
|
|
11371
|
+
tone: "warning"
|
|
11372
|
+
};
|
|
11044
11373
|
}
|
|
11045
11374
|
},
|
|
11046
11375
|
onAssignActor: async (peerId, actorId) => {
|
|
11047
11376
|
try {
|
|
11048
11377
|
await assignPeerActor(peerId, actorId);
|
|
11049
|
-
showGlobalNotice(actorId ? "Device person updated." : "Device person cleared.");
|
|
11050
11378
|
await _loadSyncData();
|
|
11379
|
+
return {
|
|
11380
|
+
message: actorId ? "Device person updated." : "Device person cleared.",
|
|
11381
|
+
tone: "success"
|
|
11382
|
+
};
|
|
11051
11383
|
} catch (error) {
|
|
11052
|
-
|
|
11053
|
-
|
|
11384
|
+
return {
|
|
11385
|
+
message: friendlyError(error, "Failed to update device person. The current assignment is unchanged."),
|
|
11386
|
+
tone: "warning"
|
|
11387
|
+
};
|
|
11054
11388
|
}
|
|
11055
11389
|
},
|
|
11056
11390
|
onSaveScope: async (peerId, include, exclude) => {
|
|
11057
11391
|
try {
|
|
11058
11392
|
await updatePeerScope(peerId, include, exclude);
|
|
11059
11393
|
clearPeerScopeReview(peerId);
|
|
11060
|
-
showGlobalNotice("Device sync scope saved.");
|
|
11061
11394
|
await _loadSyncData();
|
|
11395
|
+
return {
|
|
11396
|
+
message: "Device sync scope saved.",
|
|
11397
|
+
tone: "success"
|
|
11398
|
+
};
|
|
11062
11399
|
} catch (error) {
|
|
11063
|
-
|
|
11064
|
-
|
|
11400
|
+
return {
|
|
11401
|
+
message: friendlyError(error, "Failed to save device scope. The current sharing rules are still active."),
|
|
11402
|
+
tone: "warning"
|
|
11403
|
+
};
|
|
11065
11404
|
}
|
|
11066
11405
|
},
|
|
11067
11406
|
onResetScope: async (peerId) => {
|
|
11068
11407
|
try {
|
|
11069
11408
|
await updatePeerScope(peerId, null, null, true);
|
|
11070
11409
|
clearPeerScopeReview(peerId);
|
|
11071
|
-
showGlobalNotice("Device sync scope reset to global defaults.");
|
|
11072
11410
|
await _loadSyncData();
|
|
11411
|
+
return {
|
|
11412
|
+
message: "Device sync scope reset to global defaults.",
|
|
11413
|
+
tone: "success"
|
|
11414
|
+
};
|
|
11073
11415
|
} catch (error) {
|
|
11074
|
-
|
|
11075
|
-
|
|
11416
|
+
return {
|
|
11417
|
+
message: friendlyError(error, "Failed to reset device scope. The current sharing rules are still active."),
|
|
11418
|
+
tone: "warning"
|
|
11419
|
+
};
|
|
11076
11420
|
}
|
|
11077
11421
|
}
|
|
11078
11422
|
});
|
|
11079
11423
|
}
|
|
11424
|
+
function renderSyncPeopleUnavailable() {
|
|
11425
|
+
const actorList = document.getElementById("syncActorsList");
|
|
11426
|
+
const actorMeta = document.getElementById("syncActorsMeta");
|
|
11427
|
+
const syncPeers = document.getElementById("syncPeers");
|
|
11428
|
+
setPeopleCreateControlsDisabled(true);
|
|
11429
|
+
if (actorMeta) actorMeta.textContent = "People and device details are unavailable right now. Refresh this page to retry once local sync status is reachable again.";
|
|
11430
|
+
if (actorList) renderSyncEmptyState(actorList, {
|
|
11431
|
+
title: "People unavailable right now.",
|
|
11432
|
+
detail: "Refresh this page to reload named people once the local sync status endpoint is responding again."
|
|
11433
|
+
});
|
|
11434
|
+
if (syncPeers) renderSyncEmptyState(syncPeers, {
|
|
11435
|
+
title: "Devices unavailable right now.",
|
|
11436
|
+
detail: "Refresh this page to reload paired devices. When sync is reachable again, you can rename, assign, or pair devices here."
|
|
11437
|
+
});
|
|
11438
|
+
}
|
|
11080
11439
|
function renderLegacyDeviceClaims() {
|
|
11081
11440
|
const panel = document.getElementById("syncLegacyClaims");
|
|
11082
11441
|
const mount = document.getElementById("syncLegacyDeviceSelectMount");
|
|
@@ -11157,6 +11516,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11157
11516
|
//#region src/tabs/sync/index.ts
|
|
11158
11517
|
var lastSyncHash = "";
|
|
11159
11518
|
var cachedSyncStatus = null;
|
|
11519
|
+
var latestSyncLoadRequestId = 0;
|
|
11160
11520
|
var HEALTH_SYNC_STATUS_CACHE_TTL_MS = 15e3;
|
|
11161
11521
|
function syncStatusCacheKey(project) {
|
|
11162
11522
|
return `project:${project || ""}|includeJoinRequests:false`;
|
|
@@ -11182,21 +11542,35 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11182
11542
|
join_requests: []
|
|
11183
11543
|
};
|
|
11184
11544
|
}
|
|
11545
|
+
function hideStaleSyncSecondarySections() {
|
|
11546
|
+
const sharingReview = document.getElementById("syncSharingReview");
|
|
11547
|
+
const sharingReviewList = document.getElementById("syncSharingReviewList");
|
|
11548
|
+
const sharingReviewMeta = document.getElementById("syncSharingReviewMeta");
|
|
11549
|
+
const legacyClaims = document.getElementById("syncLegacyClaims");
|
|
11550
|
+
const legacyClaimsMeta = document.getElementById("syncLegacyClaimsMeta");
|
|
11551
|
+
if (sharingReview) sharingReview.hidden = true;
|
|
11552
|
+
if (sharingReviewList) sharingReviewList.textContent = "";
|
|
11553
|
+
if (sharingReviewMeta) sharingReviewMeta.textContent = "";
|
|
11554
|
+
if (legacyClaims) legacyClaims.hidden = true;
|
|
11555
|
+
if (legacyClaimsMeta) legacyClaimsMeta.textContent = "";
|
|
11556
|
+
}
|
|
11185
11557
|
async function loadSyncData() {
|
|
11558
|
+
const requestId = ++latestSyncLoadRequestId;
|
|
11186
11559
|
try {
|
|
11187
11560
|
const project = state.currentProject || "";
|
|
11188
11561
|
const includeJoinRequests = state.activeTab === "sync";
|
|
11189
11562
|
const useCache = state.activeTab === "health";
|
|
11563
|
+
let fetchedFreshSyncStatus = false;
|
|
11190
11564
|
let payload;
|
|
11191
11565
|
if (useCache) {
|
|
11192
11566
|
payload = readCachedSyncStatus(project);
|
|
11193
11567
|
if (!payload) {
|
|
11194
11568
|
payload = await loadSyncStatus(true, project, { includeJoinRequests: false });
|
|
11195
|
-
|
|
11569
|
+
fetchedFreshSyncStatus = true;
|
|
11196
11570
|
}
|
|
11197
11571
|
} else {
|
|
11198
11572
|
payload = await loadSyncStatus(true, project, { includeJoinRequests });
|
|
11199
|
-
|
|
11573
|
+
fetchedFreshSyncStatus = true;
|
|
11200
11574
|
}
|
|
11201
11575
|
let actorsPayload = null;
|
|
11202
11576
|
let actorLoadError = false;
|
|
@@ -11206,6 +11580,8 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11206
11580
|
} catch {
|
|
11207
11581
|
actorLoadError = true;
|
|
11208
11582
|
}
|
|
11583
|
+
if (requestId !== latestSyncLoadRequestId) return;
|
|
11584
|
+
if (fetchedFreshSyncStatus) writeCachedSyncStatus(project, normalizeSyncStatusForCache(payload));
|
|
11209
11585
|
const hash = JSON.stringify([
|
|
11210
11586
|
payload,
|
|
11211
11587
|
actorsPayload,
|
|
@@ -11216,7 +11592,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11216
11592
|
const statusPayload = payload.status && typeof payload.status === "object" ? payload.status : null;
|
|
11217
11593
|
if (statusPayload) state.lastSyncStatus = statusPayload;
|
|
11218
11594
|
if (Array.isArray(actorsPayload?.items)) state.lastSyncActors = actorsPayload.items;
|
|
11219
|
-
else
|
|
11595
|
+
else state.lastSyncActors = [];
|
|
11220
11596
|
const payloadPeers = Array.isArray(payload.peers) ? payload.peers : [];
|
|
11221
11597
|
const realPeerIds = new Set(payloadPeers.map((peer) => String(peer?.peer_device_id || "").trim()).filter(Boolean));
|
|
11222
11598
|
const pendingPeers = Array.isArray(state.pendingAcceptedSyncPeers) ? state.pendingAcceptedSyncPeers.filter((peer) => {
|
|
@@ -11245,17 +11621,17 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11245
11621
|
renderLegacyDeviceClaims();
|
|
11246
11622
|
renderSyncAttempts();
|
|
11247
11623
|
renderHealthOverview();
|
|
11248
|
-
if (actorLoadError)
|
|
11249
|
-
const actorMeta = document.getElementById("syncActorsMeta");
|
|
11250
|
-
if (actorMeta) actorMeta.textContent = "People controls are temporarily unavailable. Device status and sync health still loaded.";
|
|
11251
|
-
}
|
|
11624
|
+
if (actorLoadError) renderSyncActorsUnavailable();
|
|
11252
11625
|
} catch {
|
|
11626
|
+
if (requestId !== latestSyncLoadRequestId) return;
|
|
11627
|
+
lastSyncHash = "";
|
|
11253
11628
|
hideSkeleton("syncTeamSkeleton");
|
|
11254
11629
|
hideSkeleton("syncActorsSkeleton");
|
|
11255
11630
|
hideSkeleton("syncPeersSkeleton");
|
|
11256
11631
|
hideSkeleton("syncDiagSkeleton");
|
|
11257
|
-
|
|
11258
|
-
|
|
11632
|
+
hideStaleSyncSecondarySections();
|
|
11633
|
+
renderSyncPeopleUnavailable();
|
|
11634
|
+
renderSyncDiagnosticsUnavailable();
|
|
11259
11635
|
}
|
|
11260
11636
|
}
|
|
11261
11637
|
async function loadPairingData() {
|
|
@@ -11263,6 +11639,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11263
11639
|
state.pairingPayloadRaw = await loadPairing() || null;
|
|
11264
11640
|
renderPairing();
|
|
11265
11641
|
} catch {
|
|
11642
|
+
state.pairingPayloadRaw = null;
|
|
11266
11643
|
renderPairing();
|
|
11267
11644
|
}
|
|
11268
11645
|
}
|
|
@@ -11285,6 +11662,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11285
11662
|
var settingsEnvOverrides = {};
|
|
11286
11663
|
var settingsTouchedKeys = /* @__PURE__ */ new Set();
|
|
11287
11664
|
var settingsShellMounted = false;
|
|
11665
|
+
var settingsProtectedKeys = /* @__PURE__ */ new Set();
|
|
11288
11666
|
var settingsStartPolling = null;
|
|
11289
11667
|
var settingsRefresh = null;
|
|
11290
11668
|
var SETTINGS_ADVANCED_KEY = "codemem-settings-advanced";
|
|
@@ -11317,6 +11695,14 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11317
11695
|
syncCoordinatorTimeout: "sync_coordinator_timeout_s",
|
|
11318
11696
|
syncCoordinatorPresenceTtl: "sync_coordinator_presence_ttl_s"
|
|
11319
11697
|
};
|
|
11698
|
+
var PROTECTED_VIEWER_CONFIG_KEYS = new Set([
|
|
11699
|
+
"claude_command",
|
|
11700
|
+
"observer_base_url",
|
|
11701
|
+
"observer_auth_file",
|
|
11702
|
+
"observer_auth_command",
|
|
11703
|
+
"observer_headers",
|
|
11704
|
+
"sync_coordinator_url"
|
|
11705
|
+
]);
|
|
11320
11706
|
var settingsRenderState = {
|
|
11321
11707
|
effectiveText: "",
|
|
11322
11708
|
isSaving: false,
|
|
@@ -11802,6 +12188,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11802
12188
|
type: hasWarning ? "warning" : "success"
|
|
11803
12189
|
};
|
|
11804
12190
|
}
|
|
12191
|
+
function isProtectedConfigKey(key) {
|
|
12192
|
+
return settingsProtectedKeys.has(key) || PROTECTED_VIEWER_CONFIG_KEYS.has(key);
|
|
12193
|
+
}
|
|
12194
|
+
function protectedConfigHelp(key) {
|
|
12195
|
+
return `${key} is read-only in the viewer for security. Edit the config file or environment instead.`;
|
|
12196
|
+
}
|
|
11805
12197
|
function formStateFromPayload(payload) {
|
|
11806
12198
|
const config = payload.config || {};
|
|
11807
12199
|
const effective = payload.effective || {};
|
|
@@ -11842,8 +12234,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11842
12234
|
const defaults = payload.defaults || {};
|
|
11843
12235
|
const config = payload.config || {};
|
|
11844
12236
|
const envOverrides = payload.env_overrides && typeof payload.env_overrides === "object" ? payload.env_overrides : {};
|
|
12237
|
+
const protectedKeys = Array.isArray(payload.protected_keys) ? payload.protected_keys.filter((value) => typeof value === "string" && value.trim().length > 0) : [];
|
|
11845
12238
|
const values = formStateFromPayload(payload);
|
|
11846
12239
|
settingsEnvOverrides = envOverrides;
|
|
12240
|
+
settingsProtectedKeys = new Set(protectedKeys);
|
|
11847
12241
|
state.configDefaults = defaults;
|
|
11848
12242
|
state.configPath = payload.path || "";
|
|
11849
12243
|
updateRenderState({
|
|
@@ -11996,6 +12390,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11996
12390
|
const current = collectSettingsPayload({ allowUntouchedParseErrors: true });
|
|
11997
12391
|
const changed = {};
|
|
11998
12392
|
Object.entries(current).forEach(([key, value]) => {
|
|
12393
|
+
if (isProtectedConfigKey(key)) return;
|
|
11999
12394
|
if (hasOwn(settingsEnvOverrides, key) && !settingsTouchedKeys.has(key)) return;
|
|
12000
12395
|
if (!isEqualValue(value, settingsBaseline[key])) changed[key] = value;
|
|
12001
12396
|
});
|
|
@@ -12398,15 +12793,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12398
12793
|
children: "Claude command (JSON argv)"
|
|
12399
12794
|
}),
|
|
12400
12795
|
/* @__PURE__ */ u("textarea", {
|
|
12796
|
+
disabled: true,
|
|
12401
12797
|
id: "claudeCommand",
|
|
12402
|
-
onInput: onTextInput("claudeCommand"),
|
|
12403
12798
|
placeholder: "[\"claude\"]",
|
|
12404
12799
|
rows: 2,
|
|
12405
12800
|
value: values.claudeCommand
|
|
12406
12801
|
}),
|
|
12407
12802
|
/* @__PURE__ */ u("div", {
|
|
12408
12803
|
className: "small",
|
|
12409
|
-
children: "
|
|
12804
|
+
children: protectedConfigHelp("claude_command")
|
|
12410
12805
|
})
|
|
12411
12806
|
]
|
|
12412
12807
|
}),
|
|
@@ -12496,14 +12891,14 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12496
12891
|
children: "Token file path"
|
|
12497
12892
|
}),
|
|
12498
12893
|
/* @__PURE__ */ u("input", {
|
|
12894
|
+
disabled: true,
|
|
12499
12895
|
id: "observerAuthFile",
|
|
12500
|
-
onInput: onTextInput("observerAuthFile"),
|
|
12501
12896
|
placeholder: "~/.codemem/work-token.txt",
|
|
12502
12897
|
value: values.observerAuthFile
|
|
12503
12898
|
}),
|
|
12504
12899
|
/* @__PURE__ */ u("div", {
|
|
12505
12900
|
className: "small",
|
|
12506
|
-
children: "
|
|
12901
|
+
children: protectedConfigHelp("observer_auth_file")
|
|
12507
12902
|
})
|
|
12508
12903
|
]
|
|
12509
12904
|
}),
|
|
@@ -12525,15 +12920,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12525
12920
|
})]
|
|
12526
12921
|
}),
|
|
12527
12922
|
/* @__PURE__ */ u("textarea", {
|
|
12923
|
+
disabled: true,
|
|
12528
12924
|
id: "observerAuthCommand",
|
|
12529
|
-
onInput: onTextInput("observerAuthCommand"),
|
|
12530
12925
|
placeholder: "[\"iap-auth\", \"--audience\", \"gateway\"]",
|
|
12531
12926
|
rows: 3,
|
|
12532
12927
|
value: values.observerAuthCommand
|
|
12533
12928
|
}),
|
|
12534
12929
|
/* @__PURE__ */ u("div", {
|
|
12535
12930
|
className: "small",
|
|
12536
|
-
children: "
|
|
12931
|
+
children: protectedConfigHelp("observer_auth_command")
|
|
12537
12932
|
})
|
|
12538
12933
|
]
|
|
12539
12934
|
}),
|
|
@@ -12589,15 +12984,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12589
12984
|
})]
|
|
12590
12985
|
}),
|
|
12591
12986
|
/* @__PURE__ */ u("textarea", {
|
|
12987
|
+
disabled: true,
|
|
12592
12988
|
id: "observerHeaders",
|
|
12593
|
-
onInput: onTextInput("observerHeaders"),
|
|
12594
12989
|
placeholder: "{\"Authorization\":\"Bearer ${auth.token}\"}",
|
|
12595
12990
|
rows: 4,
|
|
12596
12991
|
value: values.observerHeaders
|
|
12597
12992
|
}),
|
|
12598
12993
|
/* @__PURE__ */ u("div", {
|
|
12599
12994
|
className: "small",
|
|
12600
|
-
children:
|
|
12995
|
+
children: protectedConfigHelp("observer_headers")
|
|
12601
12996
|
})
|
|
12602
12997
|
]
|
|
12603
12998
|
})
|
|
@@ -12781,14 +13176,14 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12781
13176
|
children: "Coordinator URL"
|
|
12782
13177
|
}),
|
|
12783
13178
|
/* @__PURE__ */ u("input", {
|
|
13179
|
+
disabled: true,
|
|
12784
13180
|
id: "syncCoordinatorUrl",
|
|
12785
|
-
onInput: onTextInput("syncCoordinatorUrl"),
|
|
12786
13181
|
placeholder: "https://coord.example.com",
|
|
12787
13182
|
value: values.syncCoordinatorUrl
|
|
12788
13183
|
}),
|
|
12789
13184
|
/* @__PURE__ */ u("div", {
|
|
12790
13185
|
className: "small",
|
|
12791
|
-
children: "
|
|
13186
|
+
children: protectedConfigHelp("sync_coordinator_url")
|
|
12792
13187
|
})
|
|
12793
13188
|
]
|
|
12794
13189
|
}),
|
|
@@ -12888,7 +13283,71 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12888
13283
|
}
|
|
12889
13284
|
//#endregion
|
|
12890
13285
|
//#region src/app.ts
|
|
13286
|
+
function setRuntimeLabel(version, commit) {
|
|
13287
|
+
const el = $$2("runtimeLabel");
|
|
13288
|
+
if (!el) return;
|
|
13289
|
+
el.textContent = commit ? `v${version} (${commit})` : `v${version}`;
|
|
13290
|
+
el.title = commit ? `codemem ${version} (${commit})` : `codemem ${version}`;
|
|
13291
|
+
el.hidden = false;
|
|
13292
|
+
}
|
|
13293
|
+
async function loadRuntimeLabel() {
|
|
13294
|
+
try {
|
|
13295
|
+
const runtime = await loadRuntimeInfo();
|
|
13296
|
+
if (!runtime?.version) return;
|
|
13297
|
+
setRuntimeLabel(runtime.version, "692076f");
|
|
13298
|
+
} catch {}
|
|
13299
|
+
}
|
|
12891
13300
|
var lastAnnouncedRefreshState = null;
|
|
13301
|
+
var RECONNECT_POLL_MS = 1500;
|
|
13302
|
+
var reconnectTimer = null;
|
|
13303
|
+
var reconnecting = false;
|
|
13304
|
+
function setReconnectOverlay(open, detail) {
|
|
13305
|
+
const overlay = $$2("viewerReconnectOverlay");
|
|
13306
|
+
const detailEl = $$2("viewerReconnectDetail");
|
|
13307
|
+
if (!overlay || !detailEl) return;
|
|
13308
|
+
overlay.hidden = !open;
|
|
13309
|
+
detailEl.textContent = detail || "Trying again automatically while the viewer comes back.";
|
|
13310
|
+
}
|
|
13311
|
+
async function isViewerReady() {
|
|
13312
|
+
try {
|
|
13313
|
+
await pingViewerReady();
|
|
13314
|
+
return true;
|
|
13315
|
+
} catch {
|
|
13316
|
+
return false;
|
|
13317
|
+
}
|
|
13318
|
+
}
|
|
13319
|
+
function stopReconnectLoop() {
|
|
13320
|
+
if (reconnectTimer) {
|
|
13321
|
+
clearTimeout(reconnectTimer);
|
|
13322
|
+
reconnectTimer = null;
|
|
13323
|
+
}
|
|
13324
|
+
reconnecting = false;
|
|
13325
|
+
setReconnectOverlay(false);
|
|
13326
|
+
}
|
|
13327
|
+
function canResumeRefresh() {
|
|
13328
|
+
return document.visibilityState !== "hidden" && !isSettingsOpen();
|
|
13329
|
+
}
|
|
13330
|
+
function scheduleReconnectLoop() {
|
|
13331
|
+
if (reconnecting) return;
|
|
13332
|
+
reconnecting = true;
|
|
13333
|
+
stopPolling();
|
|
13334
|
+
setRefreshStatus("error", "(reconnecting)");
|
|
13335
|
+
setReconnectOverlay(true, "The viewer server is restarting or temporarily unavailable. Trying again automatically…");
|
|
13336
|
+
const tick = async () => {
|
|
13337
|
+
if (await isViewerReady()) {
|
|
13338
|
+
stopReconnectLoop();
|
|
13339
|
+
if (canResumeRefresh()) {
|
|
13340
|
+
setRefreshStatus("refreshing");
|
|
13341
|
+
startPolling();
|
|
13342
|
+
doRefresh();
|
|
13343
|
+
} else setRefreshStatus("paused", document.visibilityState === "hidden" ? "(tab hidden)" : "(settings open)");
|
|
13344
|
+
return;
|
|
13345
|
+
}
|
|
13346
|
+
setReconnectOverlay(true, "Still reconnecting… the viewer will recover automatically as soon as the server responds.");
|
|
13347
|
+
reconnectTimer = setTimeout(tick, RECONNECT_POLL_MS);
|
|
13348
|
+
};
|
|
13349
|
+
reconnectTimer = setTimeout(tick, RECONNECT_POLL_MS);
|
|
13350
|
+
}
|
|
12892
13351
|
function setRefreshStatus(rs, detail) {
|
|
12893
13352
|
state.refreshState = rs;
|
|
12894
13353
|
const el = $$2("refreshStatus");
|
|
@@ -12908,6 +13367,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12908
13367
|
announce("Auto refresh paused.");
|
|
12909
13368
|
return;
|
|
12910
13369
|
}
|
|
13370
|
+
if (rs === "error" && detail === "(reconnecting)") {
|
|
13371
|
+
el.textContent = "reconnecting…";
|
|
13372
|
+
announce("Viewer reconnecting.");
|
|
13373
|
+
return;
|
|
13374
|
+
}
|
|
12911
13375
|
if (rs === "error") {
|
|
12912
13376
|
el.textContent = "refresh failed";
|
|
12913
13377
|
announce("Refresh failed.");
|
|
@@ -12931,7 +13395,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12931
13395
|
if (document.visibilityState === "hidden") {
|
|
12932
13396
|
stopPolling();
|
|
12933
13397
|
setRefreshStatus("paused", "(tab hidden)");
|
|
12934
|
-
} else if (!isSettingsOpen()) {
|
|
13398
|
+
} else if (!isSettingsOpen() && !reconnecting) {
|
|
12935
13399
|
startPolling();
|
|
12936
13400
|
refresh();
|
|
12937
13401
|
}
|
|
@@ -12987,10 +13451,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12987
13451
|
});
|
|
12988
13452
|
var refreshDebounceTimer = null;
|
|
12989
13453
|
async function refresh() {
|
|
13454
|
+
if (reconnecting) return;
|
|
12990
13455
|
if (refreshDebounceTimer) clearTimeout(refreshDebounceTimer);
|
|
12991
13456
|
refreshDebounceTimer = setTimeout(() => doRefresh(), 80);
|
|
12992
13457
|
}
|
|
12993
13458
|
async function doRefresh() {
|
|
13459
|
+
if (reconnecting) return;
|
|
12994
13460
|
if (state.refreshInFlight) {
|
|
12995
13461
|
state.refreshQueued = true;
|
|
12996
13462
|
return;
|
|
@@ -13005,10 +13471,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
13005
13471
|
await Promise.all(promises);
|
|
13006
13472
|
setRefreshStatus("idle");
|
|
13007
13473
|
} catch {
|
|
13008
|
-
|
|
13474
|
+
if (!await isViewerReady()) scheduleReconnectLoop();
|
|
13475
|
+
else setRefreshStatus("error");
|
|
13009
13476
|
} finally {
|
|
13010
13477
|
state.refreshInFlight = false;
|
|
13011
|
-
if (state.refreshQueued) {
|
|
13478
|
+
if (state.refreshQueued && !reconnecting) {
|
|
13012
13479
|
state.refreshQueued = false;
|
|
13013
13480
|
doRefresh();
|
|
13014
13481
|
}
|
|
@@ -13022,6 +13489,17 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
13022
13489
|
initSyncTab(() => refresh());
|
|
13023
13490
|
initSettings(stopPolling, startPolling, () => refresh());
|
|
13024
13491
|
loadProjects();
|
|
13492
|
+
$$2("viewerReconnectRetry")?.addEventListener("click", async () => {
|
|
13493
|
+
setReconnectOverlay(true, "Checking whether the viewer server is back…");
|
|
13494
|
+
if (!await isViewerReady()) {
|
|
13495
|
+
scheduleReconnectLoop();
|
|
13496
|
+
return;
|
|
13497
|
+
}
|
|
13498
|
+
stopReconnectLoop();
|
|
13499
|
+
startPolling();
|
|
13500
|
+
doRefresh();
|
|
13501
|
+
});
|
|
13502
|
+
loadRuntimeLabel();
|
|
13025
13503
|
refresh();
|
|
13026
13504
|
startPolling();
|
|
13027
13505
|
//#endregion
|