@codemem/server 0.32.2 → 0.33.0-alpha.1
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.js +183 -60
- package/dist/index.js.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +2 -2
- package/static/app.js +261 -477
package/static/app.js
CHANGED
|
@@ -3200,21 +3200,6 @@
|
|
|
3200
3200
|
async function loadPairing(includeDiagnostics = false) {
|
|
3201
3201
|
return fetchJson(`/api/sync/pairing${includeDiagnostics ? "?includeDiagnostics=1" : ""}`);
|
|
3202
3202
|
}
|
|
3203
|
-
async function updatePeerScope(peerDeviceId, include, exclude, inheritGlobal = false) {
|
|
3204
|
-
const resp = await fetch("/api/sync/peers/scope", {
|
|
3205
|
-
method: "POST",
|
|
3206
|
-
headers: { "Content-Type": "application/json" },
|
|
3207
|
-
body: JSON.stringify({
|
|
3208
|
-
peer_device_id: peerDeviceId,
|
|
3209
|
-
include,
|
|
3210
|
-
exclude,
|
|
3211
|
-
inherit_global: inheritGlobal
|
|
3212
|
-
})
|
|
3213
|
-
});
|
|
3214
|
-
const { text, payload } = await readJsonPayload(resp);
|
|
3215
|
-
if (!resp.ok) throw new Error(payloadError(payload) || text || "request failed");
|
|
3216
|
-
return payload;
|
|
3217
|
-
}
|
|
3218
3203
|
async function assignPeerActor(peerDeviceId, actorId) {
|
|
3219
3204
|
const resp = await fetch("/api/sync/peers/identity", {
|
|
3220
3205
|
method: "POST",
|
|
@@ -3248,13 +3233,12 @@
|
|
|
3248
3233
|
return payload;
|
|
3249
3234
|
}
|
|
3250
3235
|
async function acceptDiscoveredPeer(peerDeviceId, fingerprint) {
|
|
3236
|
+
const body = { peer_device_id: peerDeviceId };
|
|
3237
|
+
if (fingerprint) body.fingerprint = fingerprint;
|
|
3251
3238
|
const resp = await fetch("/api/sync/peers/accept-discovered", {
|
|
3252
3239
|
method: "POST",
|
|
3253
3240
|
headers: { "Content-Type": "application/json" },
|
|
3254
|
-
body: JSON.stringify(
|
|
3255
|
-
peer_device_id: peerDeviceId,
|
|
3256
|
-
fingerprint
|
|
3257
|
-
})
|
|
3241
|
+
body: JSON.stringify(body)
|
|
3258
3242
|
});
|
|
3259
3243
|
const text = await resp.text();
|
|
3260
3244
|
let payload = {};
|
|
@@ -3461,11 +3445,16 @@
|
|
|
3461
3445
|
if (!resp.ok) throw new Error(payloadError(payload) || text || "request failed");
|
|
3462
3446
|
return payload;
|
|
3463
3447
|
}
|
|
3464
|
-
async function triggerSync(
|
|
3448
|
+
async function triggerSync(target) {
|
|
3449
|
+
const address = typeof target === "string" ? target.trim() : target?.address?.trim();
|
|
3450
|
+
const peerDeviceId = typeof target === "string" ? "" : target?.peerDeviceId?.trim();
|
|
3451
|
+
const payload = {};
|
|
3452
|
+
if (address) payload.address = address;
|
|
3453
|
+
if (peerDeviceId) payload.peer_device_id = peerDeviceId;
|
|
3465
3454
|
const resp = await fetch("/api/sync/run", {
|
|
3466
3455
|
method: "POST",
|
|
3467
3456
|
headers: { "Content-Type": "application/json" },
|
|
3468
|
-
body: JSON.stringify(
|
|
3457
|
+
body: JSON.stringify(payload)
|
|
3469
3458
|
});
|
|
3470
3459
|
const { text, payload: body } = await readJsonPayload(resp);
|
|
3471
3460
|
if (!resp.ok) throw new Error(payloadError(body) || text || "request failed");
|
|
@@ -3493,7 +3482,7 @@
|
|
|
3493
3482
|
function escapeHtml(value) {
|
|
3494
3483
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3495
3484
|
}
|
|
3496
|
-
function escapeRegExp(value) {
|
|
3485
|
+
function escapeRegExp$1(value) {
|
|
3497
3486
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3498
3487
|
}
|
|
3499
3488
|
function highlightText(text, query) {
|
|
@@ -3501,7 +3490,7 @@
|
|
|
3501
3490
|
if (!q) return escapeHtml(text);
|
|
3502
3491
|
const safe = escapeHtml(text);
|
|
3503
3492
|
try {
|
|
3504
|
-
const re = new RegExp(`(${escapeRegExp(q)})`, "ig");
|
|
3493
|
+
const re = new RegExp(`(${escapeRegExp$1(q)})`, "ig");
|
|
3505
3494
|
return safe.replace(re, "<mark class=\"match\">$1</mark>");
|
|
3506
3495
|
} catch {
|
|
3507
3496
|
return safe;
|
|
@@ -4099,6 +4088,23 @@
|
|
|
4099
4088
|
return /* @__PURE__ */ u("input", { ...props });
|
|
4100
4089
|
}
|
|
4101
4090
|
//#endregion
|
|
4091
|
+
//#region src/tabs/coordinator-admin/data/device-card.ts
|
|
4092
|
+
function coordinatorAdminDeviceCardCopy(device, fallbackTeamId) {
|
|
4093
|
+
const deviceId = String(device.device_id || "").trim();
|
|
4094
|
+
const teamId = String(device.group_id || fallbackTeamId || "").trim();
|
|
4095
|
+
const displayName = String(device.display_name || deviceId || "Unnamed device");
|
|
4096
|
+
const enabled = device.enabled !== false && device.enabled !== 0;
|
|
4097
|
+
const advancedParts = [`Device ID ${deviceId || "unknown"}`];
|
|
4098
|
+
if (teamId) advancedParts.push(`Team ID ${teamId}`);
|
|
4099
|
+
return {
|
|
4100
|
+
advancedDetail: `Advanced: ${advancedParts.join(" · ")}`,
|
|
4101
|
+
deviceId,
|
|
4102
|
+
displayName,
|
|
4103
|
+
statusLabel: enabled ? "Enabled in this Team" : "Disabled in this Team",
|
|
4104
|
+
teamId
|
|
4105
|
+
};
|
|
4106
|
+
}
|
|
4107
|
+
//#endregion
|
|
4102
4108
|
//#region src/tabs/coordinator-admin/data/state.ts
|
|
4103
4109
|
var ADMIN_TARGET_GROUP_KEY = "codemem-coordinator-admin-target-group";
|
|
4104
4110
|
var coordinatorAdminState = {
|
|
@@ -4134,17 +4140,16 @@
|
|
|
4134
4140
|
return _$3(RadixTabsContent, {
|
|
4135
4141
|
className: "coordinator-admin-panel",
|
|
4136
4142
|
value: "devices"
|
|
4137
|
-
}, _$3("h3", null, "Enrolled devices"), _$3("p", { class: "peer-submeta" }, summary.readiness === "ready" ? "Rename, disable, re-enable, or remove
|
|
4138
|
-
const
|
|
4139
|
-
const
|
|
4140
|
-
const displayName = String(item.display_name || deviceId || "Unnamed device");
|
|
4143
|
+
}, _$3("h3", null, "Enrolled devices"), _$3("p", { class: "peer-submeta" }, summary.readiness === "ready" ? "Rename, disable, re-enable, or remove Team devices here. Space access is granted from Spaces below; Team membership alone does not share memories." : "Finish setup first. Device administration stays disabled until coordinator admin is ready."), !items.length ? _$3("div", { class: "peer-meta" }, summary.readiness === "ready" ? "No enrolled devices found for the active coordinator group." : "Device administration will appear here once setup is complete.") : _$3("div", { class: "peer-list" }, items.map((item) => {
|
|
4144
|
+
const copy = coordinatorAdminDeviceCardCopy(item, String(state.lastCoordinatorAdminStatus?.active_group || ""));
|
|
4145
|
+
const { deviceId, displayName, teamId } = copy;
|
|
4141
4146
|
const pending = coordinatorAdminState.deviceActionPendingId === deviceId;
|
|
4142
4147
|
const draft = coordinatorAdminState.deviceRenameDrafts.get(deviceId) ?? String(item.display_name || "");
|
|
4143
4148
|
const enabled = item.enabled !== false && item.enabled !== 0;
|
|
4144
4149
|
return _$3("div", {
|
|
4145
4150
|
class: "peer-card peer-card--padded",
|
|
4146
4151
|
key: deviceId || String(item.fingerprint || "unknown")
|
|
4147
|
-
}, _$3("div", { class: "peer-title" }, _$3("strong", null, draft || displayName)), _$3("div", { class: "peer-
|
|
4152
|
+
}, _$3("div", { class: "peer-title" }, _$3("strong", null, draft || displayName)), _$3("div", { class: "peer-submeta" }, copy.statusLabel), _$3("div", { class: "peer-meta" }, copy.advancedDetail), _$3("div", { class: "coordinator-admin-form-grid" }, _$3("label", { class: "coordinator-admin-field" }, _$3("span", null, "Display name"), _$3(TextInput, {
|
|
4148
4153
|
class: "peer-scope-input",
|
|
4149
4154
|
disabled: summary.readiness !== "ready" || pending,
|
|
4150
4155
|
onInput: (event) => {
|
|
@@ -4155,22 +4160,22 @@
|
|
|
4155
4160
|
}))), _$3("div", { class: "peer-actions" }, _$3("button", {
|
|
4156
4161
|
class: "settings-button",
|
|
4157
4162
|
disabled: !deviceId || pending || summary.readiness !== "ready",
|
|
4158
|
-
onClick: () => runDevice(deviceId,
|
|
4163
|
+
onClick: () => runDevice(deviceId, teamId, displayName, "rename"),
|
|
4159
4164
|
type: "button"
|
|
4160
4165
|
}, pending && coordinatorAdminState.deviceActionPendingKind === "rename" ? "Renaming…" : "Rename"), enabled ? _$3("button", {
|
|
4161
4166
|
class: "settings-button danger",
|
|
4162
4167
|
disabled: !deviceId || pending || summary.readiness !== "ready",
|
|
4163
|
-
onClick: () => runDevice(deviceId,
|
|
4168
|
+
onClick: () => runDevice(deviceId, teamId, displayName, "disable"),
|
|
4164
4169
|
type: "button"
|
|
4165
4170
|
}, pending && coordinatorAdminState.deviceActionPendingKind === "disable" ? "Disabling…" : "Disable") : _$3("button", {
|
|
4166
4171
|
class: "settings-button",
|
|
4167
4172
|
disabled: !deviceId || pending || summary.readiness !== "ready",
|
|
4168
|
-
onClick: () => runDevice(deviceId,
|
|
4173
|
+
onClick: () => runDevice(deviceId, teamId, displayName, "enable"),
|
|
4169
4174
|
type: "button"
|
|
4170
4175
|
}, pending && coordinatorAdminState.deviceActionPendingKind === "enable" ? "Enabling…" : "Enable"), _$3("button", {
|
|
4171
4176
|
class: "settings-button danger",
|
|
4172
4177
|
disabled: !deviceId || pending || summary.readiness !== "ready",
|
|
4173
|
-
onClick: () => runDevice(deviceId,
|
|
4178
|
+
onClick: () => runDevice(deviceId, teamId, displayName, "remove"),
|
|
4174
4179
|
type: "button"
|
|
4175
4180
|
}, pending && coordinatorAdminState.deviceActionPendingKind === "remove" ? "Removing…" : "Remove")));
|
|
4176
4181
|
})));
|
|
@@ -10053,6 +10058,36 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10053
10058
|
const value = String(status || "active").trim();
|
|
10054
10059
|
return value ? value.replaceAll("_", " ") : "active";
|
|
10055
10060
|
}
|
|
10061
|
+
function spaceCardCopy(scope) {
|
|
10062
|
+
const title = String(scope.label || "").trim() || "Untitled Space";
|
|
10063
|
+
const status = scopeStatusLabel(scope.status);
|
|
10064
|
+
const kind = String(scope.kind || "user").replaceAll("_", " ");
|
|
10065
|
+
const scopeId = String(scope.scope_id || "").trim() || "unknown";
|
|
10066
|
+
const epoch = scope.membership_epoch == null ? "—" : String(scope.membership_epoch);
|
|
10067
|
+
return {
|
|
10068
|
+
title,
|
|
10069
|
+
summary: `${status} Space · ${kind}`,
|
|
10070
|
+
advancedDetail: `Advanced: Space ID ${scopeId} · Membership epoch ${epoch}`
|
|
10071
|
+
};
|
|
10072
|
+
}
|
|
10073
|
+
function spaceAccessDeviceCopy(row) {
|
|
10074
|
+
const statusLabel = {
|
|
10075
|
+
active: "Space access active",
|
|
10076
|
+
revoked: "Space access revoked",
|
|
10077
|
+
not_member: "No Space access"
|
|
10078
|
+
};
|
|
10079
|
+
const role = row.role.replaceAll("_", " ");
|
|
10080
|
+
const detail = row.status === "not_member" ? statusLabel[row.status] : `${statusLabel[row.status]} · ${role}`;
|
|
10081
|
+
const epoch = row.membershipEpoch == null ? "—" : String(row.membershipEpoch);
|
|
10082
|
+
return {
|
|
10083
|
+
statusLabel: statusLabel[row.status],
|
|
10084
|
+
detail,
|
|
10085
|
+
advancedDetail: `Advanced: membership epoch ${epoch}`
|
|
10086
|
+
};
|
|
10087
|
+
}
|
|
10088
|
+
function spaceRevokeMemberTitle(scope, displayName, deviceId) {
|
|
10089
|
+
return `Revoke ${displayName || deviceId || "this device"} from ${spaceCardCopy(scope).title}?`;
|
|
10090
|
+
}
|
|
10056
10091
|
function deriveScopeMembershipDeviceRows(devices, members) {
|
|
10057
10092
|
const memberByDevice = new Map(members.map((member) => [String(member.device_id || "").trim(), member]).filter(([deviceId]) => deviceId.length > 0));
|
|
10058
10093
|
return devices.map((device) => {
|
|
@@ -10247,7 +10282,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10247
10282
|
const scopeId = String(scope.scope_id || "").trim();
|
|
10248
10283
|
if (!scopeId) return;
|
|
10249
10284
|
if (!await openSyncConfirmDialog({
|
|
10250
|
-
title:
|
|
10285
|
+
title: spaceRevokeMemberTitle(scope, displayName, deviceId),
|
|
10251
10286
|
description: "Revocation only blocks future sync for this Space. Data already copied to that device can remain there.",
|
|
10252
10287
|
confirmLabel: "Revoke membership",
|
|
10253
10288
|
cancelLabel: "Keep membership",
|
|
@@ -10286,31 +10321,30 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10286
10321
|
const pending = draft.actionPendingKey === pendingKey;
|
|
10287
10322
|
const canGrant = row.enabled && row.status !== "active";
|
|
10288
10323
|
const canRevoke = row.status === "active";
|
|
10289
|
-
const
|
|
10290
|
-
const epochCopy = row.membershipEpoch == null ? "epoch —" : `epoch ${row.membershipEpoch}`;
|
|
10324
|
+
const copy = spaceAccessDeviceCopy(row);
|
|
10291
10325
|
return _$3("div", {
|
|
10292
10326
|
class: "coordinator-admin-scope-member-row",
|
|
10293
10327
|
key: row.deviceId
|
|
10294
|
-
}, _$3("div", { class: "coordinator-admin-scope-member-copy" }, _$3("strong", null, row.displayName), _$3("span", null,
|
|
10328
|
+
}, _$3("div", { class: "coordinator-admin-scope-member-copy" }, _$3("strong", null, row.displayName), _$3("span", null, copy.detail), _$3("span", { class: "peer-meta" }, copy.advancedDetail), row.enabled ? null : _$3("span", null, "Device is disabled in this Team.")), _$3("div", { class: "peer-actions" }, canGrant ? _$3("button", {
|
|
10295
10329
|
class: "settings-button",
|
|
10296
10330
|
disabled: !ready || Boolean(draft.actionPendingKey),
|
|
10297
10331
|
onClick: () => void grantMember(groupId, scopeId, row.deviceId, renderShell),
|
|
10298
10332
|
type: "button"
|
|
10299
|
-
}, pending && draft.actionPendingKind === "grant" ? "Granting…" : "Grant") : null, canRevoke ? _$3("button", {
|
|
10333
|
+
}, pending && draft.actionPendingKind === "grant" ? "Granting…" : "Grant access") : null, canRevoke ? _$3("button", {
|
|
10300
10334
|
class: "settings-button danger",
|
|
10301
10335
|
disabled: !ready || Boolean(draft.actionPendingKey),
|
|
10302
10336
|
onClick: () => void revokeMember(groupId, scope, row.deviceId, row.displayName, renderShell),
|
|
10303
10337
|
type: "button"
|
|
10304
|
-
}, pending && draft.actionPendingKind === "revoke" ? "Revoking…" : "Revoke") : null));
|
|
10338
|
+
}, pending && draft.actionPendingKind === "revoke" ? "Revoking…" : "Revoke access") : null));
|
|
10305
10339
|
}));
|
|
10306
10340
|
}
|
|
10307
10341
|
function renderScopeCard(groupId, scope, draft, ready, renderShell) {
|
|
10308
10342
|
const scopeId = String(scope.scope_id || "").trim();
|
|
10309
|
-
const
|
|
10343
|
+
const copy = spaceCardCopy(scope);
|
|
10310
10344
|
return _$3("div", {
|
|
10311
10345
|
class: "peer-card peer-card--padded coordinator-admin-scope-card",
|
|
10312
|
-
key: scopeId ||
|
|
10313
|
-
}, _$3("div", { class: "peer-title" }, _$3("strong", null,
|
|
10346
|
+
key: scopeId || copy.title
|
|
10347
|
+
}, _$3("div", { class: "peer-title" }, _$3("strong", null, copy.title)), _$3("div", { class: "peer-submeta" }, copy.summary), _$3("div", { class: "peer-meta" }, copy.advancedDetail), _$3("div", { class: "peer-submeta" }, "Devices below are enrolled in the Team; only active Space members can sync this data boundary."), renderMembershipRows(groupId, scope, draft, ready, renderShell));
|
|
10314
10348
|
}
|
|
10315
10349
|
function renderGroupScopeManagementPanel(deps) {
|
|
10316
10350
|
const { groupId, ready, summary, renderShell } = deps;
|
|
@@ -10478,10 +10512,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10478
10512
|
const includeLabelId = `coord-admin-scope-include-${groupId}`;
|
|
10479
10513
|
const excludeLabelId = `coord-admin-scope-exclude-${groupId}`;
|
|
10480
10514
|
const fieldsDisabled = !ready || draft.saving || !draft.auto_seed_scope;
|
|
10481
|
-
return _$3(k$3, null, _$3("h4", { class: "coordinator-admin-drawer-title" }, "Team defaults"), _$3("div", { class: "peer-submeta" }, "
|
|
10515
|
+
return _$3(k$3, null, _$3("h4", { class: "coordinator-admin-drawer-title" }, "Team defaults"), _$3("div", { class: "peer-submeta" }, "Space access grants decide what new devices can sync. Advanced sharing rules only narrow future writes after Space access already exists."), _$3("label", { class: "coordinator-admin-inline-filter" }, _$3("span", {
|
|
10482
10516
|
class: "section-meta",
|
|
10483
10517
|
id: autoSeedLabelId
|
|
10484
|
-
}, "Auto-
|
|
10518
|
+
}, "Auto-apply advanced rules to new devices"), _$3(RadixSwitch, {
|
|
10485
10519
|
"aria-labelledby": autoSeedLabelId,
|
|
10486
10520
|
checked: draft.auto_seed_scope,
|
|
10487
10521
|
className: "coordinator-admin-switch",
|
|
@@ -10512,7 +10546,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10512
10546
|
renderShell();
|
|
10513
10547
|
},
|
|
10514
10548
|
thumbClassName: "coordinator-admin-switch-thumb"
|
|
10515
|
-
})), draft.default_space_scope_id ? _$3("div", { class: "peer-submeta" }, "Default Space configured.") : _$3("div", { class: "peer-submeta" }, "No default Space has been created yet."), _$3("div", { class: "coordinator-admin-field" }, _$3("span", { id: includeLabelId }, "Include
|
|
10549
|
+
})), draft.default_space_scope_id ? _$3("div", { class: "peer-submeta" }, "Default Space configured.") : _$3("div", { class: "peer-submeta" }, "No default Space has been created yet."), _$3("div", { class: "coordinator-admin-field" }, _$3("span", { id: includeLabelId }, "Include template"), _$3(ProjectScopePicker, {
|
|
10516
10550
|
"aria-labelledby": includeLabelId,
|
|
10517
10551
|
availableProjects: coordinatorAdminState.availableProjects,
|
|
10518
10552
|
disabled: fieldsDisabled,
|
|
@@ -10527,7 +10561,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10527
10561
|
},
|
|
10528
10562
|
placeholder: "Add project",
|
|
10529
10563
|
values: draft.projects_include
|
|
10530
|
-
}), _$3("span", { class: "peer-submeta" }, "Leave empty to allow every project. Type to search or create a new name.")), _$3("div", { class: "coordinator-admin-field" }, _$3("span", { id: excludeLabelId }, "Exclude
|
|
10564
|
+
}), _$3("span", { class: "peer-submeta" }, "Leave empty to allow every project inside the granted Space. Type to search or create a new name.")), _$3("div", { class: "coordinator-admin-field" }, _$3("span", { id: excludeLabelId }, "Exclude template"), _$3(ProjectScopePicker, {
|
|
10531
10565
|
"aria-labelledby": excludeLabelId,
|
|
10532
10566
|
availableProjects: coordinatorAdminState.availableProjects,
|
|
10533
10567
|
disabled: fieldsDisabled,
|
|
@@ -10833,7 +10867,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10833
10867
|
return _$3(RadixTabsContent, {
|
|
10834
10868
|
className: "coordinator-admin-panel",
|
|
10835
10869
|
value: "join-requests"
|
|
10836
|
-
}, _$3("h3", null, "Pending join requests"), _$3("p", { class: "peer-submeta" }, summary.readiness === "ready" ? "Approve or deny
|
|
10870
|
+
}, _$3("h3", null, "Pending join requests"), _$3("p", { class: "peer-submeta" }, summary.readiness === "ready" ? "Approve or deny devices that want to join this Team. Default Space access is handled by Team defaults and can be reviewed in Spaces." : "Finish setup first. Join request review stays disabled until coordinator admin is ready."), !items.length ? _$3("div", { class: "peer-meta" }, summary.readiness === "ready" ? "No pending join requests right now." : "Join request review will appear here once setup is complete.") : _$3("div", { class: "peer-list" }, items.map((item) => {
|
|
10837
10871
|
const requestId = String(item.request_id || "").trim();
|
|
10838
10872
|
const deviceId = String(item.device_id || "unknown-device");
|
|
10839
10873
|
const displayName = String(item.display_name || deviceId);
|
|
@@ -10841,7 +10875,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10841
10875
|
return _$3("div", {
|
|
10842
10876
|
class: "peer-card peer-card--padded",
|
|
10843
10877
|
key: requestId || deviceId
|
|
10844
|
-
}, _$3("div", { class: "peer-title" }, _$3("strong", null, displayName)), _$3("div", { class: "peer-meta" }, `
|
|
10878
|
+
}, _$3("div", { class: "peer-title" }, _$3("strong", null, displayName)), _$3("div", { class: "peer-meta" }, `Advanced: Device ID ${deviceId}`), _$3("div", { class: "peer-actions" }, _$3("button", {
|
|
10845
10879
|
class: "settings-button",
|
|
10846
10880
|
disabled: !requestId || pending,
|
|
10847
10881
|
onClick: () => reviewJoinRequest(requestId, "approve"),
|
|
@@ -10959,7 +10993,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10959
10993
|
state.lastTeamInvite = result;
|
|
10960
10994
|
coordinatorAdminState.inviteGroup = groupId;
|
|
10961
10995
|
const warnings = Array.isArray(result.warnings) ? result.warnings : [];
|
|
10962
|
-
showGlobalNotice(warnings.length ? `Invite created. Review ${warnings.length === 1 ? "the warning" : `${warnings.length} warnings`} before sharing it.` : "Invite created. Copy it from
|
|
10996
|
+
showGlobalNotice(warnings.length ? `Invite created. Review ${warnings.length === 1 ? "the warning" : `${warnings.length} warnings`} before sharing it.` : "Invite created. Copy it from Teams and share it with your teammate.", warnings.length ? "warning" : "success");
|
|
10963
10997
|
} catch (error) {
|
|
10964
10998
|
showGlobalNotice(error instanceof Error ? error.message : "Failed to create invite.", "warning");
|
|
10965
10999
|
} finally {
|
|
@@ -18061,9 +18095,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18061
18095
|
}
|
|
18062
18096
|
function shouldShowCoordinatorReviewAction(input) {
|
|
18063
18097
|
const approvalSummary = deriveCoordinatorApprovalSummary(input);
|
|
18064
|
-
|
|
18065
|
-
const fingerprint = cleanText(input.device?.fingerprint);
|
|
18066
|
-
if (!deviceId || !fingerprint) return false;
|
|
18098
|
+
if (!cleanText(input.device?.device_id)) return false;
|
|
18067
18099
|
if (input.device?.stale || input.hasAmbiguousCoordinatorGroup) return false;
|
|
18068
18100
|
if (!input.pairedLocally) return true;
|
|
18069
18101
|
return approvalSummary.state === "needs-your-approval";
|
|
@@ -18093,11 +18125,16 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18093
18125
|
warning: false
|
|
18094
18126
|
};
|
|
18095
18127
|
}
|
|
18096
|
-
if (failedItems.filter((item) =>
|
|
18128
|
+
if (failedItems.filter((item) => isTrustFailureError(cleanText(item.error))).length === failedItems.length) return {
|
|
18097
18129
|
ok: false,
|
|
18098
18130
|
message: "This device no longer has two-way trust with the peer. Pair it again from the other device, or remove the stale local record if it should be gone.",
|
|
18099
18131
|
warning: true
|
|
18100
18132
|
};
|
|
18133
|
+
if (failedItems.filter((item) => isScopeMembershipFailureError(cleanText(item.error))).length === failedItems.length) return {
|
|
18134
|
+
ok: false,
|
|
18135
|
+
message: "Sync ran, but the peer is not authorized for one or more Spaces. Review Space access for this device in Teams, then sync again.",
|
|
18136
|
+
warning: true
|
|
18137
|
+
};
|
|
18101
18138
|
if (failedItems.length < items.length) return {
|
|
18102
18139
|
ok: false,
|
|
18103
18140
|
message: `${failedItems.length} of ${items.length} device sync attempts failed. Open the affected device cards for the specific errors.`,
|
|
@@ -18109,6 +18146,41 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18109
18146
|
warning: true
|
|
18110
18147
|
};
|
|
18111
18148
|
}
|
|
18149
|
+
/**
|
|
18150
|
+
* True when a sync-failure error string looks like a two-way-trust failure
|
|
18151
|
+
* (401 unauthorized on the peer's /v1/status), which is the only class of
|
|
18152
|
+
* failure that should trigger the "no longer has two-way trust" toast. Any
|
|
18153
|
+
* other class — connectivity, scope/membership, generation mismatch — flows
|
|
18154
|
+
* through the generic mixed-failure or scoped paths so users do not get
|
|
18155
|
+
* pointed at re-pairing for problems that re-pairing will not fix.
|
|
18156
|
+
*/
|
|
18157
|
+
function isTrustFailureError(error) {
|
|
18158
|
+
const lower = error.toLowerCase();
|
|
18159
|
+
return lower.includes("401") && lower.includes("unauthorized");
|
|
18160
|
+
}
|
|
18161
|
+
/**
|
|
18162
|
+
* True when a sync-failure error string indicates the peer is not authorized
|
|
18163
|
+
* for a Space (or is authorized at a stale epoch). These failures map to the
|
|
18164
|
+
* scoped-sync protocol classes documented in
|
|
18165
|
+
* docs/plans/2026-05-25-scoped-sync-protocol.md:
|
|
18166
|
+
*
|
|
18167
|
+
* - `403: scope_rejected` from POST /v1/ops outbound scope filter.
|
|
18168
|
+
* - `409: reset_required` with `reason=missing_scope` or `stale_epoch`
|
|
18169
|
+
* from GET /v1/ops or GET /v1/snapshot.
|
|
18170
|
+
* - syncOneScope's aggregate string only when it includes one of those wire
|
|
18171
|
+
* reasons. Other scoped-sync failures remain generic sync failures.
|
|
18172
|
+
*
|
|
18173
|
+
* Detection is intentionally substring-based because the wire reason can
|
|
18174
|
+
* arrive embedded in a longer error string from the address-fallback loop.
|
|
18175
|
+
*/
|
|
18176
|
+
function isScopeMembershipFailureError(error) {
|
|
18177
|
+
const lower = error.toLowerCase();
|
|
18178
|
+
if (lower.includes("scope_rejected")) return true;
|
|
18179
|
+
if (lower.includes("missing_scope")) return true;
|
|
18180
|
+
if (lower.includes("stale_epoch")) return true;
|
|
18181
|
+
if (lower.includes("scope_inactive")) return true;
|
|
18182
|
+
return false;
|
|
18183
|
+
}
|
|
18112
18184
|
function outboundFilterLabel(detail) {
|
|
18113
18185
|
const reason = cleanText(detail?.reason);
|
|
18114
18186
|
return OUTBOUND_FILTER_LABELS[reason] ?? (reason.replaceAll("_", " ") || "sync filters");
|
|
@@ -18259,8 +18331,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18259
18331
|
function derivePeerAuthorizedDomainsView(peer) {
|
|
18260
18332
|
const domains = (Array.isArray(peer.authorized_scopes) ? peer.authorized_scopes : []).map((scope) => {
|
|
18261
18333
|
const scopeId = cleanText(scope.scope_id);
|
|
18262
|
-
const
|
|
18263
|
-
if (!scopeId && !
|
|
18334
|
+
const rawLabel = cleanText(scope.label);
|
|
18335
|
+
if (!scopeId && !rawLabel) return null;
|
|
18336
|
+
const label = rawLabel || "Untitled Space";
|
|
18264
18337
|
const detail = [
|
|
18265
18338
|
labelPart(cleanText(scope.kind), "user"),
|
|
18266
18339
|
labelPart(cleanText(scope.authority_type), "local"),
|
|
@@ -18268,17 +18341,17 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18268
18341
|
].filter(Boolean).join(" · ");
|
|
18269
18342
|
return {
|
|
18270
18343
|
scopeId: scopeId || label,
|
|
18271
|
-
label
|
|
18344
|
+
label,
|
|
18272
18345
|
detail
|
|
18273
18346
|
};
|
|
18274
18347
|
}).filter((item) => item != null);
|
|
18275
18348
|
const total = domains.length;
|
|
18276
18349
|
return {
|
|
18277
18350
|
total,
|
|
18278
|
-
badgeLabel: total === 1 ? "1
|
|
18351
|
+
badgeLabel: total === 1 ? "1 Space" : total > 1 ? `${total} Spaces` : "No Space access",
|
|
18279
18352
|
isWarning: total === 0,
|
|
18280
18353
|
domains,
|
|
18281
|
-
emptyMessage: "No
|
|
18354
|
+
emptyMessage: "No Space access grants exist for this device yet. Advanced project filters cannot send data by themselves."
|
|
18282
18355
|
};
|
|
18283
18356
|
}
|
|
18284
18357
|
var PERSONAL_SCOPE_WORDS = [
|
|
@@ -18358,25 +18431,32 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18358
18431
|
title: ""
|
|
18359
18432
|
};
|
|
18360
18433
|
return {
|
|
18361
|
-
badgeLabel: "Review
|
|
18362
|
-
detail: "
|
|
18434
|
+
badgeLabel: "Review Space fit",
|
|
18435
|
+
detail: "Team discovery helps find the device, and advanced project filters only narrow already-authorized Spaces. Neither one grants missing work/client Space access.",
|
|
18363
18436
|
isVisible: true,
|
|
18364
|
-
message: "This
|
|
18365
|
-
title: "Check this device's
|
|
18437
|
+
message: "This Team-discovered device has personal or OSS Space access, but no separate work/client-like Space access. If this device is meant for work/client sync, grant that Space explicitly before treating sync as validated.",
|
|
18438
|
+
title: "Check this device's Space access"
|
|
18366
18439
|
};
|
|
18367
18440
|
}
|
|
18441
|
+
function hasActiveProjectNarrowing(include, exclude) {
|
|
18442
|
+
const normalizedInclude = include.map((item) => item.trim()).filter(Boolean);
|
|
18443
|
+
return normalizedInclude.length > 0 && !normalizedInclude.includes("*") || exclude.length > 0;
|
|
18444
|
+
}
|
|
18368
18445
|
function derivePeerProjectNarrowingView(scope) {
|
|
18369
18446
|
const effectiveInclude = cleanList(scope?.effective_include);
|
|
18370
18447
|
const effectiveExclude = cleanList(scope?.effective_exclude);
|
|
18371
18448
|
const sourceLabel = scope?.inherits_global ? "Global defaults" : "Device override";
|
|
18372
18449
|
const includeLabel = `Include filter: ${shortList(effectiveInclude, "all projects")}`;
|
|
18373
18450
|
const excludeLabel = `Exclude filter: ${shortList(effectiveExclude, "no exclusions")}`;
|
|
18451
|
+
const hasAdvancedFilters = hasActiveProjectNarrowing(effectiveInclude, effectiveExclude);
|
|
18374
18452
|
return {
|
|
18453
|
+
hasAdvancedFilters,
|
|
18454
|
+
statusLabel: hasAdvancedFilters ? "Advanced filters active" : "No advanced filters",
|
|
18375
18455
|
sourceLabel,
|
|
18376
18456
|
includeLabel,
|
|
18377
18457
|
excludeLabel,
|
|
18378
18458
|
summary: `${sourceLabel}. ${includeLabel}; ${excludeLabel}.`,
|
|
18379
|
-
note: "
|
|
18459
|
+
note: "Advanced filters only narrow data after Space access; they never grant access to another Space."
|
|
18380
18460
|
};
|
|
18381
18461
|
}
|
|
18382
18462
|
//#endregion
|
|
@@ -18683,6 +18763,18 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18683
18763
|
const unique = Array.from(new Set(addresses.filter(Boolean)));
|
|
18684
18764
|
return typeof unique[0] === "string" ? unique[0] : "";
|
|
18685
18765
|
}
|
|
18766
|
+
/**
|
|
18767
|
+
* Returns true when a "Removed peer X" style feedback message is stale
|
|
18768
|
+
* because the related peer has reappeared in the loaded peers list (e.g.
|
|
18769
|
+
* the user re-paired the same device they just removed). Used by
|
|
18770
|
+
* renderSyncPeers to clear the persistent banner once the user's view
|
|
18771
|
+
* of reality contradicts it.
|
|
18772
|
+
*/
|
|
18773
|
+
function shouldClearStalePeersFeedback(feedback, peers) {
|
|
18774
|
+
const id = String(feedback?.relatedPeerDeviceId ?? "").trim();
|
|
18775
|
+
if (!id) return false;
|
|
18776
|
+
return peers.some((peer) => String(peer?.peer_device_id ?? "").trim() === id);
|
|
18777
|
+
}
|
|
18686
18778
|
function actorLabel(actor) {
|
|
18687
18779
|
if (!actor || typeof actor !== "object") return "Unknown person";
|
|
18688
18780
|
const displayName = String(actor.display_name || "").trim();
|
|
@@ -18728,208 +18820,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18728
18820
|
if (!targetActorId || !target) return "Choose which person should keep these devices.";
|
|
18729
18821
|
return `Merge into ${actorDisplayLabel(target)}. Assigned devices move now; existing memories keep their current provenance.`;
|
|
18730
18822
|
}
|
|
18731
|
-
function createChipEditor(initialValues, placeholder, emptyLabel, availableProjects = []) {
|
|
18732
|
-
let values = [...initialValues];
|
|
18733
|
-
let query = "";
|
|
18734
|
-
let activeIndex = 0;
|
|
18735
|
-
let popoverOpen = false;
|
|
18736
|
-
const container = el$1("div", "project-scope-picker");
|
|
18737
|
-
const selectedList = el$1("ul", "project-scope-picker-selected");
|
|
18738
|
-
const trigger = el$1("button", "project-scope-picker-trigger settings-button");
|
|
18739
|
-
trigger.type = "button";
|
|
18740
|
-
const triggerLabel = el$1("span", null, placeholder || "Add project");
|
|
18741
|
-
const triggerPlus = el$1("span", null, "+");
|
|
18742
|
-
triggerPlus.setAttribute("aria-hidden", "true");
|
|
18743
|
-
trigger.append(triggerPlus, triggerLabel);
|
|
18744
|
-
trigger.setAttribute("aria-haspopup", "listbox");
|
|
18745
|
-
trigger.setAttribute("aria-expanded", "false");
|
|
18746
|
-
const popover = el$1("div", "project-scope-picker-popover project-scope-picker-popover--inline");
|
|
18747
|
-
popover.hidden = true;
|
|
18748
|
-
const search = el$1("input", "project-scope-picker-search");
|
|
18749
|
-
search.type = "text";
|
|
18750
|
-
search.placeholder = "Search or create…";
|
|
18751
|
-
search.setAttribute("aria-label", "Search projects");
|
|
18752
|
-
const results = el$1("div", "project-scope-picker-results");
|
|
18753
|
-
results.setAttribute("role", "listbox");
|
|
18754
|
-
popover.append(search, results);
|
|
18755
|
-
const knownPool = () => {
|
|
18756
|
-
const seen = /* @__PURE__ */ new Set();
|
|
18757
|
-
const out = [];
|
|
18758
|
-
for (const item of [...availableProjects, ...values]) {
|
|
18759
|
-
const trimmed = item.trim();
|
|
18760
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
18761
|
-
seen.add(trimmed);
|
|
18762
|
-
out.push(trimmed);
|
|
18763
|
-
}
|
|
18764
|
-
return out.sort((a, b) => a.localeCompare(b));
|
|
18765
|
-
};
|
|
18766
|
-
const filteredRows = () => {
|
|
18767
|
-
const all = knownPool();
|
|
18768
|
-
const q = query.trim().toLowerCase();
|
|
18769
|
-
if (!q) return all;
|
|
18770
|
-
return all.filter((project) => project.toLowerCase().includes(q));
|
|
18771
|
-
};
|
|
18772
|
-
const shouldShowCreateRow = () => {
|
|
18773
|
-
const q = query.trim();
|
|
18774
|
-
if (!q) return false;
|
|
18775
|
-
return !knownPool().some((project) => project.toLowerCase() === q.toLowerCase());
|
|
18776
|
-
};
|
|
18777
|
-
const toggleValue = (project) => {
|
|
18778
|
-
if (values.includes(project)) values = values.filter((value) => value !== project);
|
|
18779
|
-
else values = Array.from(new Set([...values, project]));
|
|
18780
|
-
renderSelected();
|
|
18781
|
-
renderResults();
|
|
18782
|
-
};
|
|
18783
|
-
const createFromQuery = () => {
|
|
18784
|
-
const trimmed = query.trim();
|
|
18785
|
-
if (!trimmed) return;
|
|
18786
|
-
values = Array.from(new Set([...values, trimmed]));
|
|
18787
|
-
query = "";
|
|
18788
|
-
search.value = "";
|
|
18789
|
-
activeIndex = 0;
|
|
18790
|
-
renderSelected();
|
|
18791
|
-
renderResults();
|
|
18792
|
-
};
|
|
18793
|
-
const activateRow = (index) => {
|
|
18794
|
-
const rows = filteredRows();
|
|
18795
|
-
if (index < rows.length) {
|
|
18796
|
-
const project = rows[index];
|
|
18797
|
-
if (project) toggleValue(project);
|
|
18798
|
-
return;
|
|
18799
|
-
}
|
|
18800
|
-
if (shouldShowCreateRow()) createFromQuery();
|
|
18801
|
-
};
|
|
18802
|
-
const renderSelected = () => {
|
|
18803
|
-
selectedList.textContent = "";
|
|
18804
|
-
if (!values.length) {
|
|
18805
|
-
const note = el$1("li", "project-scope-picker-selected-empty", emptyLabel);
|
|
18806
|
-
selectedList.appendChild(note);
|
|
18807
|
-
return;
|
|
18808
|
-
}
|
|
18809
|
-
values.forEach((value) => {
|
|
18810
|
-
const chip = el$1("li", "project-scope-picker-selected-chip");
|
|
18811
|
-
const label = el$1("span", null, value);
|
|
18812
|
-
const remove = el$1("button", "project-scope-picker-selected-remove", "×");
|
|
18813
|
-
remove.type = "button";
|
|
18814
|
-
remove.setAttribute("aria-label", `Remove ${value}`);
|
|
18815
|
-
remove.addEventListener("click", () => {
|
|
18816
|
-
values = values.filter((existing) => existing !== value);
|
|
18817
|
-
renderSelected();
|
|
18818
|
-
renderResults();
|
|
18819
|
-
});
|
|
18820
|
-
chip.append(label, remove);
|
|
18821
|
-
selectedList.appendChild(chip);
|
|
18822
|
-
});
|
|
18823
|
-
};
|
|
18824
|
-
const renderResults = () => {
|
|
18825
|
-
results.textContent = "";
|
|
18826
|
-
const rows = filteredRows();
|
|
18827
|
-
const withCreate = shouldShowCreateRow();
|
|
18828
|
-
const rowCount = rows.length + (withCreate ? 1 : 0);
|
|
18829
|
-
if (activeIndex >= rowCount) activeIndex = Math.max(0, rowCount - 1);
|
|
18830
|
-
if (!rows.length && !withCreate) {
|
|
18831
|
-
const empty = el$1("div", "project-scope-picker-empty-row", "No projects yet. Type a name to create one.");
|
|
18832
|
-
results.appendChild(empty);
|
|
18833
|
-
return;
|
|
18834
|
-
}
|
|
18835
|
-
rows.forEach((project, index) => {
|
|
18836
|
-
const selected = values.includes(project);
|
|
18837
|
-
const row = el$1("button", index === activeIndex ? "project-scope-picker-row project-scope-picker-row--active" : "project-scope-picker-row");
|
|
18838
|
-
row.type = "button";
|
|
18839
|
-
row.setAttribute("role", "option");
|
|
18840
|
-
row.setAttribute("aria-selected", selected ? "true" : "false");
|
|
18841
|
-
const check = el$1("span", "project-scope-picker-row-check", selected ? "✓" : "");
|
|
18842
|
-
check.setAttribute("aria-hidden", "true");
|
|
18843
|
-
const labelEl = el$1("span", "project-scope-picker-row-label", project);
|
|
18844
|
-
row.append(check, labelEl);
|
|
18845
|
-
row.addEventListener("click", () => {
|
|
18846
|
-
activeIndex = index;
|
|
18847
|
-
toggleValue(project);
|
|
18848
|
-
});
|
|
18849
|
-
row.addEventListener("mouseenter", () => {
|
|
18850
|
-
activeIndex = index;
|
|
18851
|
-
renderResults();
|
|
18852
|
-
});
|
|
18853
|
-
results.appendChild(row);
|
|
18854
|
-
});
|
|
18855
|
-
if (withCreate) {
|
|
18856
|
-
const index = rows.length;
|
|
18857
|
-
const row = el$1("button", index === activeIndex ? "project-scope-picker-row project-scope-picker-row--create project-scope-picker-row--active" : "project-scope-picker-row project-scope-picker-row--create");
|
|
18858
|
-
row.type = "button";
|
|
18859
|
-
row.setAttribute("role", "option");
|
|
18860
|
-
const check = el$1("span", "project-scope-picker-row-check", "+");
|
|
18861
|
-
check.setAttribute("aria-hidden", "true");
|
|
18862
|
-
const labelEl = el$1("span", "project-scope-picker-row-label", `Create "${query.trim()}"`);
|
|
18863
|
-
row.append(check, labelEl);
|
|
18864
|
-
row.addEventListener("click", () => createFromQuery());
|
|
18865
|
-
row.addEventListener("mouseenter", () => {
|
|
18866
|
-
activeIndex = index;
|
|
18867
|
-
renderResults();
|
|
18868
|
-
});
|
|
18869
|
-
results.appendChild(row);
|
|
18870
|
-
}
|
|
18871
|
-
};
|
|
18872
|
-
const setOpen = (next) => {
|
|
18873
|
-
popoverOpen = next;
|
|
18874
|
-
popover.hidden = !next;
|
|
18875
|
-
trigger.setAttribute("aria-expanded", next ? "true" : "false");
|
|
18876
|
-
if (next) {
|
|
18877
|
-
query = "";
|
|
18878
|
-
search.value = "";
|
|
18879
|
-
activeIndex = 0;
|
|
18880
|
-
renderResults();
|
|
18881
|
-
requestAnimationFrame(() => search.focus());
|
|
18882
|
-
}
|
|
18883
|
-
};
|
|
18884
|
-
trigger.addEventListener("click", (event) => {
|
|
18885
|
-
event.stopPropagation();
|
|
18886
|
-
setOpen(!popoverOpen);
|
|
18887
|
-
});
|
|
18888
|
-
search.addEventListener("input", () => {
|
|
18889
|
-
query = search.value;
|
|
18890
|
-
activeIndex = 0;
|
|
18891
|
-
renderResults();
|
|
18892
|
-
});
|
|
18893
|
-
search.addEventListener("keydown", (event) => {
|
|
18894
|
-
const rowCount = filteredRows().length + (shouldShowCreateRow() ? 1 : 0);
|
|
18895
|
-
if (event.key === "ArrowDown") {
|
|
18896
|
-
event.preventDefault();
|
|
18897
|
-
activeIndex = Math.min(rowCount - 1, activeIndex + 1);
|
|
18898
|
-
renderResults();
|
|
18899
|
-
return;
|
|
18900
|
-
}
|
|
18901
|
-
if (event.key === "ArrowUp") {
|
|
18902
|
-
event.preventDefault();
|
|
18903
|
-
activeIndex = Math.max(0, activeIndex - 1);
|
|
18904
|
-
renderResults();
|
|
18905
|
-
return;
|
|
18906
|
-
}
|
|
18907
|
-
if (event.key === "Enter") {
|
|
18908
|
-
event.preventDefault();
|
|
18909
|
-
activateRow(activeIndex);
|
|
18910
|
-
return;
|
|
18911
|
-
}
|
|
18912
|
-
if (event.key === "Escape") {
|
|
18913
|
-
event.preventDefault();
|
|
18914
|
-
setOpen(false);
|
|
18915
|
-
}
|
|
18916
|
-
});
|
|
18917
|
-
const onDocumentClick = (event) => {
|
|
18918
|
-
if (!popoverOpen) return;
|
|
18919
|
-
const target = event.target;
|
|
18920
|
-
if (!target) return;
|
|
18921
|
-
if (container.contains(target)) return;
|
|
18922
|
-
setOpen(false);
|
|
18923
|
-
};
|
|
18924
|
-
document.addEventListener("click", onDocumentClick);
|
|
18925
|
-
container.append(selectedList, trigger, popover);
|
|
18926
|
-
renderSelected();
|
|
18927
|
-
renderResults();
|
|
18928
|
-
return {
|
|
18929
|
-
element: container,
|
|
18930
|
-
values: () => [...values]
|
|
18931
|
-
};
|
|
18932
|
-
}
|
|
18933
18823
|
function renderActionList(container, actions) {
|
|
18934
18824
|
if (!container) return;
|
|
18935
18825
|
container.textContent = "";
|
|
@@ -19526,46 +19416,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19526
19416
|
});
|
|
19527
19417
|
}
|
|
19528
19418
|
//#endregion
|
|
19529
|
-
//#region src/tabs/sync/peer-scope-collapsible.tsx
|
|
19530
|
-
function PeerScopeCollapsible({ contentHost, initialOpen, onOpenChange, children }) {
|
|
19531
|
-
const [open, setOpen] = d$2(initialOpen);
|
|
19532
|
-
const contentRef = A$1(null);
|
|
19533
|
-
y$2(() => {
|
|
19534
|
-
setOpen(initialOpen);
|
|
19535
|
-
}, [initialOpen]);
|
|
19536
|
-
_$2(() => {
|
|
19537
|
-
onOpenChange(open);
|
|
19538
|
-
}, [onOpenChange, open]);
|
|
19539
|
-
y$2(() => {
|
|
19540
|
-
if (!open) return;
|
|
19541
|
-
queueMicrotask(() => {
|
|
19542
|
-
(contentRef.current?.querySelector("button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])"))?.focus();
|
|
19543
|
-
});
|
|
19544
|
-
}, [open]);
|
|
19545
|
-
return /* @__PURE__ */ u(Root$3, {
|
|
19546
|
-
open,
|
|
19547
|
-
onOpenChange: setOpen,
|
|
19548
|
-
children: [/* @__PURE__ */ u(Trigger$4, {
|
|
19549
|
-
asChild: true,
|
|
19550
|
-
children: /* @__PURE__ */ u("button", {
|
|
19551
|
-
type: "button",
|
|
19552
|
-
className: "settings-button",
|
|
19553
|
-
children: open ? "Hide sharing scope" : "Show sharing scope"
|
|
19554
|
-
})
|
|
19555
|
-
}), contentHost ? $$1(/* @__PURE__ */ u(Content$2, {
|
|
19556
|
-
forceMount: true,
|
|
19557
|
-
ref: contentRef,
|
|
19558
|
-
className: `peer-scope-editor-wrap${open ? "" : " collapsed"}`,
|
|
19559
|
-
hidden: !open,
|
|
19560
|
-
inert: !open,
|
|
19561
|
-
children: /* @__PURE__ */ u(ScopeEditorContent, { children })
|
|
19562
|
-
}), contentHost) : null]
|
|
19563
|
-
});
|
|
19564
|
-
}
|
|
19565
|
-
function ScopeEditorContent({ children }) {
|
|
19566
|
-
return /* @__PURE__ */ u("div", { children });
|
|
19567
|
-
}
|
|
19568
|
-
//#endregion
|
|
19569
19419
|
//#region src/tabs/sync/components/sync-inline-feedback.tsx
|
|
19570
19420
|
function SyncInlineFeedback({ feedback }) {
|
|
19571
19421
|
if (!feedback?.message) return null;
|
|
@@ -19634,53 +19484,38 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19634
19484
|
},
|
|
19635
19485
|
none: null
|
|
19636
19486
|
};
|
|
19637
|
-
function listText(value) {
|
|
19638
|
-
return Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean) : [];
|
|
19639
|
-
}
|
|
19640
19487
|
function claimedLocalActorScopeMessage(scope) {
|
|
19641
|
-
|
|
19642
|
-
if (
|
|
19643
|
-
|
|
19644
|
-
return `Private same-person sync is blocked until this device is granted ${scopeId || "its personal Sharing domain"}.`;
|
|
19488
|
+
if (!scope) return "Private same-person sync is limited to an allowed personal Space; team or org Spaces do not carry your private memories.";
|
|
19489
|
+
if (scope.authorized) return "Private same-person sync is limited to your personal Space. Team and org Spaces do not carry your private memories.";
|
|
19490
|
+
return "Private same-person sync is blocked until this device is granted an allowed personal Space.";
|
|
19645
19491
|
}
|
|
19646
|
-
function
|
|
19647
|
-
|
|
19648
|
-
_$2(() => {
|
|
19649
|
-
const host = hostRef.current;
|
|
19650
|
-
if (!host) return;
|
|
19651
|
-
if (element.parentElement !== host) host.appendChild(element);
|
|
19652
|
-
return () => {
|
|
19653
|
-
if (element.parentElement === host) host.removeChild(element);
|
|
19654
|
-
};
|
|
19655
|
-
}, [element]);
|
|
19656
|
-
return /* @__PURE__ */ u("div", { ref: hostRef });
|
|
19492
|
+
function openTeamsAccessManagement() {
|
|
19493
|
+
window.location.hash = "coordinator-admin";
|
|
19657
19494
|
}
|
|
19658
|
-
function SyncPeerCard({ peer, onAssignActor, onRemove, onRename,
|
|
19495
|
+
function SyncPeerCard({ peer, onAssignActor, onRemove, onRename, onSync }) {
|
|
19659
19496
|
const peerId = String(peer.peer_device_id || "");
|
|
19660
19497
|
const displayName = peer.name || (peerId ? peerId.slice(0, 8) : "unknown");
|
|
19661
19498
|
const destructiveLabel = peer.name || peerId || displayName;
|
|
19662
|
-
const pendingScopeReview = isPeerScopeReviewPending(peerId);
|
|
19663
19499
|
const trustSummary = derivePeerTrustSummary(peer);
|
|
19664
19500
|
const directionHint = DIRECTION_GLYPH[derivePeerDirection(peer)];
|
|
19665
19501
|
const peerStatus = peer.status || {};
|
|
19666
19502
|
const authorizedDomains = derivePeerAuthorizedDomainsView(peer);
|
|
19503
|
+
const rawPendingScopeReview = isPeerScopeReviewPending(peerId);
|
|
19504
|
+
const pendingScopeReview = rawPendingScopeReview && authorizedDomains.total === 0;
|
|
19667
19505
|
const grantRoleMismatch = derivePeerGrantRoleMismatchView(peer);
|
|
19668
19506
|
const scopeRejections = derivePeerScopeRejectionsView(peer);
|
|
19669
|
-
const
|
|
19670
|
-
const projectNarrowing = derivePeerProjectNarrowingView(scope);
|
|
19671
|
-
const includeList = listText(scope.include);
|
|
19672
|
-
const excludeList = listText(scope.exclude);
|
|
19507
|
+
const projectNarrowing = derivePeerProjectNarrowingView(peer.project_scope || {});
|
|
19673
19508
|
const primaryAddress = pickPrimaryAddress(peer.addresses);
|
|
19674
19509
|
const peerAddresses = Array.isArray(peer.addresses) ? Array.from(new Set(peer.addresses.filter(Boolean).map((value) => String(value)))) : [];
|
|
19675
|
-
const
|
|
19510
|
+
const rawHiddenAddressCount = Number(peer.address_count ?? 0);
|
|
19511
|
+
const hiddenAddressCount = Number.isFinite(rawHiddenAddressCount) && rawHiddenAddressCount > 0 ? rawHiddenAddressCount : 0;
|
|
19512
|
+
const addressLine = peerAddresses.length ? peerAddresses.map((address) => isSyncRedactionEnabled() ? redactAddress(address) : address).join(" · ") : hiddenAddressCount > 0 ? `${hiddenAddressCount} ${hiddenAddressCount === 1 ? "address" : "addresses"} hidden` : "No addresses";
|
|
19676
19513
|
const assignmentSummary = peer.actor_display_name ? `This device belongs to ${peer.claimed_local_actor ? "you" : String(peer.actor_display_name)}.` : "This device is not assigned to anyone yet.";
|
|
19677
19514
|
const lastSyncAt = String(peerStatus.last_sync_at || peerStatus.last_sync_at_utc || "");
|
|
19678
19515
|
const lastPingAt = String(peerStatus.last_ping_at || peerStatus.last_ping_at_utc || "");
|
|
19679
|
-
const discoverySummary = peer.discovered_via_group_id ? `Discovery: seen through
|
|
19680
|
-
const scopeEditorOpen = openPeerScopeEditors.has(peerId);
|
|
19516
|
+
const discoverySummary = peer.discovered_via_group_id ? `Discovery: seen through Team ${peer.discovered_via_group_id}. Discovery helps find devices; Space access above decides data access.` : peer.discovered_via_coordinator_id ? `Discovery: seen through coordinator ${peer.discovered_via_coordinator_id}. Discovery helps find devices; Space access above decides data access.` : null;
|
|
19681
19517
|
const scopeReviewRequested = consumePeerScopeReviewRequest(peerId);
|
|
19682
19518
|
const cardRef = A$1(null);
|
|
19683
|
-
const [scopeHost, setScopeHost] = d$2(null);
|
|
19684
19519
|
const [renameValue, setRenameValue] = d$2(displayName);
|
|
19685
19520
|
const [feedback, setFeedback] = d$2(() => state.syncPeerFeedbackById.get(peerId) ?? null);
|
|
19686
19521
|
const [renameBusy, setRenameBusy] = d$2(false);
|
|
@@ -19691,10 +19526,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19691
19526
|
const [selectedActorId, setSelectedActorId] = d$2(String(peer.actor_id || ""));
|
|
19692
19527
|
const [applyActorBusy, setApplyActorBusy] = d$2(false);
|
|
19693
19528
|
const [applyActorLabel, setApplyActorLabel] = d$2("Save assignment");
|
|
19694
|
-
const [saveScopeBusy, setSaveScopeBusy] = d$2(false);
|
|
19695
|
-
const [saveScopeLabel, setSaveScopeLabel] = d$2("Save scope");
|
|
19696
|
-
const [resetScopeBusy, setResetScopeBusy] = d$2(false);
|
|
19697
|
-
const [resetScopeLabel, setResetScopeLabel] = d$2("Reset to global scope");
|
|
19698
19529
|
const actorSelectOptions = T$1(() => {
|
|
19699
19530
|
const options = buildActorSelectOptions(selectedActorId);
|
|
19700
19531
|
const hasSelected = options.some((option) => option.value === selectedActorId);
|
|
@@ -19711,15 +19542,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19711
19542
|
state.lastSyncPeers,
|
|
19712
19543
|
state.lastSyncViewModel
|
|
19713
19544
|
]);
|
|
19714
|
-
|
|
19715
|
-
peerId
|
|
19716
|
-
|
|
19717
|
-
|
|
19718
|
-
]);
|
|
19719
|
-
const excludeEditor = T$1(() => createChipEditor(excludeList, "Add project", "No exclusions", state.knownProjects), [
|
|
19545
|
+
y$2(() => {
|
|
19546
|
+
if (rawPendingScopeReview && authorizedDomains.total > 0) clearPeerScopeReview(peerId);
|
|
19547
|
+
}, [
|
|
19548
|
+
authorizedDomains.total,
|
|
19720
19549
|
peerId,
|
|
19721
|
-
|
|
19722
|
-
state.knownProjects.join("|")
|
|
19550
|
+
rawPendingScopeReview
|
|
19723
19551
|
]);
|
|
19724
19552
|
y$2(() => {
|
|
19725
19553
|
setRenameValue(displayName);
|
|
@@ -19732,16 +19560,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19732
19560
|
setSelectedActorId(String(peer.actor_id || ""));
|
|
19733
19561
|
setApplyActorBusy(false);
|
|
19734
19562
|
setApplyActorLabel("Save assignment");
|
|
19735
|
-
setSaveScopeBusy(false);
|
|
19736
|
-
setSaveScopeLabel("Save scope");
|
|
19737
|
-
setResetScopeBusy(false);
|
|
19738
|
-
setResetScopeLabel("Reset to global scope");
|
|
19739
19563
|
}, [
|
|
19740
19564
|
displayName,
|
|
19741
19565
|
peer.actor_id,
|
|
19742
|
-
peerId
|
|
19743
|
-
includeList.join("|"),
|
|
19744
|
-
excludeList.join("|")
|
|
19566
|
+
peerId
|
|
19745
19567
|
]);
|
|
19746
19568
|
y$2(() => {
|
|
19747
19569
|
if (!scopeReviewRequested || !cardRef.current) return;
|
|
@@ -19777,13 +19599,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19777
19599
|
}
|
|
19778
19600
|
}
|
|
19779
19601
|
async function sync() {
|
|
19780
|
-
if (!primaryAddress) return;
|
|
19781
19602
|
if (pendingScopeReview) {
|
|
19782
19603
|
if (!await openSyncConfirmDialog({
|
|
19783
|
-
title: `Sync ${displayName} before
|
|
19784
|
-
description: "This manual sync will use the current
|
|
19604
|
+
title: `Sync ${displayName} before advanced rule review?`,
|
|
19605
|
+
description: "This manual sync will use the current Space access and advanced filters until you review them in Teams.",
|
|
19785
19606
|
confirmLabel: "Sync anyway",
|
|
19786
|
-
cancelLabel: "Review
|
|
19607
|
+
cancelLabel: "Review in Teams first"
|
|
19787
19608
|
})) return;
|
|
19788
19609
|
}
|
|
19789
19610
|
setSyncBusy(true);
|
|
@@ -19832,36 +19653,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19832
19653
|
setApplyActorBusy(false);
|
|
19833
19654
|
}
|
|
19834
19655
|
}
|
|
19835
|
-
async function saveScope() {
|
|
19836
|
-
if (!peerId) return;
|
|
19837
|
-
setSaveScopeBusy(true);
|
|
19838
|
-
setSaveScopeLabel("Saving…");
|
|
19839
|
-
try {
|
|
19840
|
-
const nextFeedback = await onSaveScope(peerId, includeEditor.values(), excludeEditor.values());
|
|
19841
|
-
setFeedback(nextFeedback);
|
|
19842
|
-
state.syncPeerFeedbackById.set(peerId, nextFeedback);
|
|
19843
|
-
setSaveScopeLabel("Save scope");
|
|
19844
|
-
} catch {
|
|
19845
|
-
setSaveScopeLabel("Retry");
|
|
19846
|
-
} finally {
|
|
19847
|
-
setSaveScopeBusy(false);
|
|
19848
|
-
}
|
|
19849
|
-
}
|
|
19850
|
-
async function resetScope() {
|
|
19851
|
-
if (!peerId) return;
|
|
19852
|
-
setResetScopeBusy(true);
|
|
19853
|
-
setResetScopeLabel("Resetting…");
|
|
19854
|
-
try {
|
|
19855
|
-
const nextFeedback = await onResetScope(peerId);
|
|
19856
|
-
setFeedback(nextFeedback);
|
|
19857
|
-
state.syncPeerFeedbackById.set(peerId, nextFeedback);
|
|
19858
|
-
setResetScopeLabel("Reset to global scope");
|
|
19859
|
-
} catch {
|
|
19860
|
-
setResetScopeLabel("Retry");
|
|
19861
|
-
} finally {
|
|
19862
|
-
setResetScopeBusy(false);
|
|
19863
|
-
}
|
|
19864
|
-
}
|
|
19865
19656
|
const isExpanded = useExpandedPeer() === peerId && peerId !== "";
|
|
19866
19657
|
const drawerId = `device-drawer-${peerId || "unknown"}`;
|
|
19867
19658
|
const presenceState = presenceForPeer(peer);
|
|
@@ -19908,11 +19699,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19908
19699
|
}),
|
|
19909
19700
|
pendingScopeReview ? /* @__PURE__ */ u("span", {
|
|
19910
19701
|
className: "badge actor-badge",
|
|
19911
|
-
children: "
|
|
19702
|
+
children: "Review advanced rules"
|
|
19912
19703
|
}) : null,
|
|
19913
19704
|
/* @__PURE__ */ u("span", {
|
|
19914
19705
|
className: `badge ${authorizedDomains.isWarning ? "badge-offline" : "actor-badge"}`,
|
|
19915
|
-
title: "
|
|
19706
|
+
title: "Spaces are the hard access boundary; advanced filters only narrow inside them.",
|
|
19916
19707
|
children: authorizedDomains.badgeLabel
|
|
19917
19708
|
}),
|
|
19918
19709
|
scopeRejections.badgeLabel ? /* @__PURE__ */ u("span", {
|
|
@@ -19922,7 +19713,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19922
19713
|
}) : null,
|
|
19923
19714
|
grantRoleMismatch.badgeLabel ? /* @__PURE__ */ u("span", {
|
|
19924
19715
|
className: "badge badge-offline",
|
|
19925
|
-
title: "Review whether explicit
|
|
19716
|
+
title: "Review whether explicit Space access matches this device's intended role.",
|
|
19926
19717
|
children: grantRoleMismatch.badgeLabel
|
|
19927
19718
|
}) : null,
|
|
19928
19719
|
peer.discovered_via_group_id ? /* @__PURE__ */ u("span", {
|
|
@@ -19953,8 +19744,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19953
19744
|
/* @__PURE__ */ u("button", {
|
|
19954
19745
|
type: "button",
|
|
19955
19746
|
className: "settings-button",
|
|
19956
|
-
disabled:
|
|
19747
|
+
disabled: syncBusy,
|
|
19957
19748
|
onClick: () => void sync(),
|
|
19749
|
+
title: primaryAddress ? void 0 : "Address details are hidden; sync will target this device by ID.",
|
|
19958
19750
|
children: syncBusy ? "Syncing…" : "Sync now"
|
|
19959
19751
|
}),
|
|
19960
19752
|
/* @__PURE__ */ u("div", {
|
|
@@ -19995,10 +19787,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19995
19787
|
children: [
|
|
19996
19788
|
scopeReviewRequested ? /* @__PURE__ */ u("div", {
|
|
19997
19789
|
className: "peer-meta",
|
|
19998
|
-
children: "Review this device's
|
|
19790
|
+
children: "Review this device's Space access and advanced rules in Teams if the defaults are too broad."
|
|
19999
19791
|
}) : pendingScopeReview ? /* @__PURE__ */ u("div", {
|
|
20000
19792
|
className: "peer-meta",
|
|
20001
|
-
children: "
|
|
19793
|
+
children: "Advanced rule review is still pending for this device."
|
|
20002
19794
|
}) : null,
|
|
20003
19795
|
/* @__PURE__ */ u("div", {
|
|
20004
19796
|
className: "peer-scope-summary",
|
|
@@ -20018,15 +19810,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20018
19810
|
}),
|
|
20019
19811
|
/* @__PURE__ */ u("div", {
|
|
20020
19812
|
className: "peer-scope-summary",
|
|
20021
|
-
children: "
|
|
19813
|
+
children: "Space access"
|
|
20022
19814
|
}),
|
|
20023
19815
|
/* @__PURE__ */ u("div", {
|
|
20024
19816
|
className: "peer-meta",
|
|
20025
|
-
children: authorizedDomains.total > 0 ? "These
|
|
19817
|
+
children: authorizedDomains.total > 0 ? "These Spaces are the hard access boundary for this device." : authorizedDomains.emptyMessage
|
|
20026
19818
|
}),
|
|
20027
19819
|
authorizedDomains.domains.length > 0 ? /* @__PURE__ */ u("ul", {
|
|
20028
19820
|
className: "peer-scope-chips",
|
|
20029
|
-
"aria-label": "Authorized
|
|
19821
|
+
"aria-label": "Authorized Spaces",
|
|
20030
19822
|
children: authorizedDomains.domains.map((domain) => /* @__PURE__ */ u("li", {
|
|
20031
19823
|
className: "peer-scope-chip",
|
|
20032
19824
|
title: domain.detail,
|
|
@@ -20112,41 +19904,28 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20112
19904
|
}),
|
|
20113
19905
|
/* @__PURE__ */ u("div", {
|
|
20114
19906
|
className: "peer-scope-summary",
|
|
20115
|
-
children: "
|
|
19907
|
+
children: "Advanced sharing rules"
|
|
20116
19908
|
}),
|
|
20117
19909
|
/* @__PURE__ */ u("div", {
|
|
20118
19910
|
className: "peer-meta",
|
|
20119
|
-
children: [
|
|
19911
|
+
children: [
|
|
19912
|
+
projectNarrowing.statusLabel,
|
|
19913
|
+
". ",
|
|
19914
|
+
projectNarrowing.summary,
|
|
19915
|
+
" ",
|
|
19916
|
+
projectNarrowing.note
|
|
19917
|
+
]
|
|
20120
19918
|
}),
|
|
20121
|
-
/* @__PURE__ */ u(
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
|
|
20126
|
-
|
|
20127
|
-
|
|
20128
|
-
|
|
20129
|
-
className: "peer-scope-row",
|
|
20130
|
-
children: [/* @__PURE__ */ u(ExistingElementSlot$1, { element: includeEditor.element }), /* @__PURE__ */ u(ExistingElementSlot$1, { element: excludeEditor.element })]
|
|
20131
|
-
}), /* @__PURE__ */ u("div", {
|
|
20132
|
-
className: "peer-scope-actions",
|
|
20133
|
-
children: [/* @__PURE__ */ u("button", {
|
|
20134
|
-
type: "button",
|
|
20135
|
-
className: "settings-button",
|
|
20136
|
-
disabled: saveScopeBusy,
|
|
20137
|
-
onClick: () => void saveScope(),
|
|
20138
|
-
children: saveScopeLabel
|
|
20139
|
-
}), /* @__PURE__ */ u("button", {
|
|
20140
|
-
type: "button",
|
|
20141
|
-
className: "settings-button",
|
|
20142
|
-
disabled: resetScopeBusy,
|
|
20143
|
-
onClick: () => void resetScope(),
|
|
20144
|
-
children: resetScopeLabel
|
|
20145
|
-
})]
|
|
20146
|
-
})] })
|
|
19919
|
+
/* @__PURE__ */ u("div", {
|
|
19920
|
+
className: "peer-actions",
|
|
19921
|
+
children: /* @__PURE__ */ u("button", {
|
|
19922
|
+
type: "button",
|
|
19923
|
+
className: "settings-button",
|
|
19924
|
+
onClick: openTeamsAccessManagement,
|
|
19925
|
+
children: "Manage Spaces in Teams"
|
|
19926
|
+
})
|
|
20147
19927
|
}),
|
|
20148
|
-
/* @__PURE__ */ u(SyncInlineFeedback, { feedback })
|
|
20149
|
-
/* @__PURE__ */ u("div", { ref: setScopeHost })
|
|
19928
|
+
/* @__PURE__ */ u(SyncInlineFeedback, { feedback })
|
|
20150
19929
|
]
|
|
20151
19930
|
})]
|
|
20152
19931
|
})]
|
|
@@ -20245,8 +20024,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20245
20024
|
if (!syncPeers) return;
|
|
20246
20025
|
hideSkeleton("syncPeersSkeleton");
|
|
20247
20026
|
const peers = state.lastSyncPeers;
|
|
20027
|
+
const peersArray = Array.isArray(peers) ? peers : [];
|
|
20028
|
+
if (shouldClearStalePeersFeedback(state.syncPeersSectionFeedback, peersArray)) state.syncPeersSectionFeedback = null;
|
|
20248
20029
|
renderSyncPeersList(syncPeers, {
|
|
20249
|
-
peers:
|
|
20030
|
+
peers: peersArray,
|
|
20250
20031
|
onRename: async (peerId, nextName) => {
|
|
20251
20032
|
try {
|
|
20252
20033
|
await renamePeer(peerId, nextName);
|
|
@@ -20264,15 +20045,18 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20264
20045
|
},
|
|
20265
20046
|
onSync: async (peer, address) => {
|
|
20266
20047
|
try {
|
|
20267
|
-
const summary = summarizeSyncRunResult(await triggerSync(address));
|
|
20268
20048
|
const peerId = String(peer?.peer_device_id || "");
|
|
20049
|
+
const summary = summarizeSyncRunResult(await triggerSync({
|
|
20050
|
+
address,
|
|
20051
|
+
peerDeviceId: peerId || void 0
|
|
20052
|
+
}));
|
|
20269
20053
|
let feedback;
|
|
20270
20054
|
if (!summary.ok) feedback = {
|
|
20271
20055
|
message: summary.message,
|
|
20272
20056
|
tone: "warning"
|
|
20273
20057
|
};
|
|
20274
20058
|
else if (peerId && isPeerScopeReviewPending(peerId)) feedback = {
|
|
20275
|
-
message: `Triggered sync for ${peer?.name || (peerId ? peerId.slice(0, 8) : "unknown")}. Review
|
|
20059
|
+
message: `Triggered sync for ${peer?.name || (peerId ? peerId.slice(0, 8) : "unknown")}. Review Space access and advanced rules in Teams if this device needs tighter sharing.`,
|
|
20276
20060
|
tone: "warning"
|
|
20277
20061
|
};
|
|
20278
20062
|
else feedback = {
|
|
@@ -20303,7 +20087,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20303
20087
|
tone: "success"
|
|
20304
20088
|
};
|
|
20305
20089
|
state.syncPeerFeedbackById.delete(peerId);
|
|
20306
|
-
state.syncPeersSectionFeedback =
|
|
20090
|
+
state.syncPeersSectionFeedback = {
|
|
20091
|
+
...feedback,
|
|
20092
|
+
relatedPeerDeviceId: peerId
|
|
20093
|
+
};
|
|
20307
20094
|
await _loadSyncData();
|
|
20308
20095
|
return feedback;
|
|
20309
20096
|
} catch (error) {
|
|
@@ -20327,38 +20114,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20327
20114
|
tone: "warning"
|
|
20328
20115
|
};
|
|
20329
20116
|
}
|
|
20330
|
-
},
|
|
20331
|
-
onSaveScope: async (peerId, include, exclude) => {
|
|
20332
|
-
try {
|
|
20333
|
-
await updatePeerScope(peerId, include, exclude);
|
|
20334
|
-
clearPeerScopeReview(peerId);
|
|
20335
|
-
await _loadSyncData();
|
|
20336
|
-
return {
|
|
20337
|
-
message: "Device sync scope saved.",
|
|
20338
|
-
tone: "success"
|
|
20339
|
-
};
|
|
20340
|
-
} catch (error) {
|
|
20341
|
-
return {
|
|
20342
|
-
message: friendlyError(error, "Failed to save device scope. The current sharing rules are still active."),
|
|
20343
|
-
tone: "warning"
|
|
20344
|
-
};
|
|
20345
|
-
}
|
|
20346
|
-
},
|
|
20347
|
-
onResetScope: async (peerId) => {
|
|
20348
|
-
try {
|
|
20349
|
-
await updatePeerScope(peerId, null, null, true);
|
|
20350
|
-
clearPeerScopeReview(peerId);
|
|
20351
|
-
await _loadSyncData();
|
|
20352
|
-
return {
|
|
20353
|
-
message: "Device sync scope reset to global defaults.",
|
|
20354
|
-
tone: "success"
|
|
20355
|
-
};
|
|
20356
|
-
} catch (error) {
|
|
20357
|
-
return {
|
|
20358
|
-
message: friendlyError(error, "Failed to reset device scope. The current sharing rules are still active."),
|
|
20359
|
-
tone: "warning"
|
|
20360
|
-
};
|
|
20361
|
-
}
|
|
20362
20117
|
}
|
|
20363
20118
|
});
|
|
20364
20119
|
}
|
|
@@ -21347,7 +21102,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21347
21102
|
pairedLocally: Boolean(pairedPeer)
|
|
21348
21103
|
});
|
|
21349
21104
|
const addresses = Array.isArray(device.addresses) ? device.addresses : [];
|
|
21350
|
-
const
|
|
21105
|
+
const rawHiddenAddressCount = Number(device.address_count ?? 0);
|
|
21106
|
+
const hiddenAddressCount = Number.isFinite(rawHiddenAddressCount) && rawHiddenAddressCount > 0 ? rawHiddenAddressCount : 0;
|
|
21107
|
+
const noteParts = [addresses.length ? addresses.map((address) => isSyncRedactionEnabled() ? redactAddress(String(address || "")) : String(address || "")).filter(Boolean).join(" · ") : hiddenAddressCount > 0 ? `${hiddenAddressCount} ${device.stale ? "last-known" : "fresh"} ${hiddenAddressCount === 1 ? "address" : "addresses"} hidden` : "No fresh addresses"];
|
|
21351
21108
|
if (!addresses.length && displayTitle) noteParts.push(`device id: ${deviceId}`);
|
|
21352
21109
|
let actionMessage = null;
|
|
21353
21110
|
let mode = canAccept ? "accept" : "none";
|
|
@@ -21357,7 +21114,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21357
21114
|
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.";
|
|
21358
21115
|
mode = "ambiguous";
|
|
21359
21116
|
} else if (pairedPeer && isPeerScopeReviewPending(deviceId)) {
|
|
21360
|
-
actionMessage =
|
|
21117
|
+
actionMessage = "Review this device's Space access and advanced rules in Teams before you sync it.";
|
|
21361
21118
|
mode = "scope-pending";
|
|
21362
21119
|
} else if (pairedPeer?.last_error) {
|
|
21363
21120
|
noteParts.push(`error: ${String(pairedPeer.last_error)}`);
|
|
@@ -21545,14 +21302,41 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21545
21302
|
const label = group.displayProject.toLowerCase();
|
|
21546
21303
|
return label.includes("fatal:") || label.includes("not a git repository") || label === "unknown project" || group.identitySource === "unmapped";
|
|
21547
21304
|
}
|
|
21305
|
+
function displayProjectTitle(group) {
|
|
21306
|
+
return needsProjectCleanup(group) ? "Unclear project identity" : group.displayProject;
|
|
21307
|
+
}
|
|
21548
21308
|
function canReassignLegacyGroup(group) {
|
|
21549
21309
|
return (group.reassignableMemoryCount ?? group.memoryCount) > 0;
|
|
21550
21310
|
}
|
|
21551
|
-
function
|
|
21311
|
+
function suggestedScopeText(group, targetScopes) {
|
|
21312
|
+
if (!group.suggestedScopeId) return "";
|
|
21313
|
+
return ` · suggested ${targetScopes.find((scope) => scope.scopeId === group.suggestedScopeId)?.label || "Space"}`;
|
|
21314
|
+
}
|
|
21315
|
+
function escapeRegExp(value) {
|
|
21316
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
21317
|
+
}
|
|
21318
|
+
function targetScopeLabel(scopeId, targetScopes) {
|
|
21319
|
+
return targetScopes.find((scope) => scope.scopeId === scopeId)?.label || "selected Space";
|
|
21320
|
+
}
|
|
21321
|
+
function authorityLabel(authorityType) {
|
|
21322
|
+
switch (authorityType) {
|
|
21323
|
+
case "coordinator": return "Team Space";
|
|
21324
|
+
case "local": return "Local Space";
|
|
21325
|
+
default: return "Other Space";
|
|
21326
|
+
}
|
|
21327
|
+
}
|
|
21328
|
+
function formatLegacyReviewError(error, scopeId, targetScopes) {
|
|
21329
|
+
let result = (error instanceof Error ? error.message : "Unable to reassign legacy review memories.").replace(/sharing domain/gi, "Space");
|
|
21330
|
+
if (scopeId) result = result.replace(new RegExp(`\\b${escapeRegExp(scopeId)}\\b`, "g"), targetScopeLabel(scopeId, targetScopes));
|
|
21331
|
+
return result;
|
|
21332
|
+
}
|
|
21333
|
+
function groupSearchText(group, targetScopes) {
|
|
21334
|
+
const suggestedScopeLabel = group.suggestedScopeId ? targetScopes.find((scope) => scope.scopeId === group.suggestedScopeId)?.label : "";
|
|
21552
21335
|
return [
|
|
21553
21336
|
group.displayProject,
|
|
21554
21337
|
group.identitySource,
|
|
21555
21338
|
group.suggestedScopeId ?? "",
|
|
21339
|
+
suggestedScopeLabel ?? "",
|
|
21556
21340
|
group.suggestionReason ?? "",
|
|
21557
21341
|
group.workspaceIdentity
|
|
21558
21342
|
].join(" ").toLowerCase();
|
|
@@ -21613,7 +21397,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21613
21397
|
if (filter === "cleanup" && !cleanup) return false;
|
|
21614
21398
|
if (filter === "no-suggestion" && (group.suggestedScopeId || cleanup)) return false;
|
|
21615
21399
|
if (filter === "selected" && !selectedGroups[group.workspaceIdentity]) return false;
|
|
21616
|
-
return !query.trim() || groupSearchText(group).includes(query.trim().toLowerCase());
|
|
21400
|
+
return !query.trim() || groupSearchText(group, targetScopes).includes(query.trim().toLowerCase());
|
|
21617
21401
|
});
|
|
21618
21402
|
async function applyGroup(group) {
|
|
21619
21403
|
const selectedScopeId = selectedScopes[group.workspaceIdentity] || group.suggestedScopeId || "";
|
|
@@ -21638,7 +21422,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21638
21422
|
} catch (error) {
|
|
21639
21423
|
setErrors((current) => ({
|
|
21640
21424
|
...current,
|
|
21641
|
-
[group.workspaceIdentity]: error
|
|
21425
|
+
[group.workspaceIdentity]: formatLegacyReviewError(error, selectedScopeId, targetScopes)
|
|
21642
21426
|
}));
|
|
21643
21427
|
} finally {
|
|
21644
21428
|
setBusyIdentity(null);
|
|
@@ -21702,7 +21486,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21702
21486
|
} catch (error) {
|
|
21703
21487
|
setErrors((current) => ({
|
|
21704
21488
|
...current,
|
|
21705
|
-
[group.workspaceIdentity]: error
|
|
21489
|
+
[group.workspaceIdentity]: formatLegacyReviewError(error, selectedScopeId, targetScopes)
|
|
21706
21490
|
}));
|
|
21707
21491
|
}
|
|
21708
21492
|
}
|
|
@@ -21730,7 +21514,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21730
21514
|
" older",
|
|
21731
21515
|
" ",
|
|
21732
21516
|
groupCount === 1 ? "project needs" : "projects need",
|
|
21733
|
-
" a
|
|
21517
|
+
" a Space. They contain",
|
|
21734
21518
|
" ",
|
|
21735
21519
|
item.memoryCount.toLocaleString(),
|
|
21736
21520
|
" older shared memories total; review the projects, not individual memories.",
|
|
@@ -21803,7 +21587,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21803
21587
|
children: busyIdentity === "bulk" ? "Previewing…" : `Preview ${selectedCount.toLocaleString()} selected`
|
|
21804
21588
|
}) : onReassign && selectedReassignableCount > 0 ? /* @__PURE__ */ u("span", {
|
|
21805
21589
|
className: "legacy-review-cleanup-note",
|
|
21806
|
-
children: "Add or join a
|
|
21590
|
+
children: "Add or join a Space before bulk reassignment."
|
|
21807
21591
|
}) : null,
|
|
21808
21592
|
selectedCount > 0 && groups.filter((group) => selectedGroups[group.workspaceIdentity] && canReassignLegacyGroup(group) && Boolean(selectedScopes[group.workspaceIdentity])).every((group) => pending[group.workspaceIdentity]) ? /* @__PURE__ */ u("span", {
|
|
21809
21593
|
className: "legacy-review-cleanup-note",
|
|
@@ -21823,7 +21607,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21823
21607
|
children: [/* @__PURE__ */ u("div", {
|
|
21824
21608
|
className: "legacy-review-title-wrap",
|
|
21825
21609
|
children: [/* @__PURE__ */ u("input", {
|
|
21826
|
-
"aria-label": `Select ${group
|
|
21610
|
+
"aria-label": `Select ${displayProjectTitle(group)}`,
|
|
21827
21611
|
checked: Boolean(selectedGroups[group.workspaceIdentity]),
|
|
21828
21612
|
className: "cm-checkbox",
|
|
21829
21613
|
disabled: !canReassignLegacyGroup(group),
|
|
@@ -21832,15 +21616,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21832
21616
|
}), /* @__PURE__ */ u("div", { children: [
|
|
21833
21617
|
/* @__PURE__ */ u("div", {
|
|
21834
21618
|
className: "legacy-review-group-title",
|
|
21835
|
-
children: group
|
|
21619
|
+
children: displayProjectTitle(group)
|
|
21836
21620
|
}),
|
|
21837
21621
|
/* @__PURE__ */ u("div", {
|
|
21838
21622
|
className: "legacy-review-group-meta",
|
|
21839
|
-
children: [formatIdentitySource(group.identitySource), group
|
|
21623
|
+
children: [formatIdentitySource(group.identitySource), suggestedScopeText(group, targetScopes)]
|
|
21840
21624
|
}),
|
|
21841
21625
|
!canReassignLegacyGroup(group) ? /* @__PURE__ */ u("div", {
|
|
21842
21626
|
className: "legacy-review-cleanup-note",
|
|
21843
|
-
children: "Peer-owned only. These older memories were received from another device, so this device cannot reassign them to a
|
|
21627
|
+
children: "Peer-owned only. These older memories were received from another device, so this device cannot reassign them to a Space."
|
|
21844
21628
|
}) : group.peerOwnedMemoryCount ? /* @__PURE__ */ u("div", {
|
|
21845
21629
|
className: "legacy-review-cleanup-note",
|
|
21846
21630
|
children: [
|
|
@@ -21858,7 +21642,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21858
21642
|
}) : null,
|
|
21859
21643
|
needsProjectCleanup(group) ? /* @__PURE__ */ u("div", {
|
|
21860
21644
|
className: "legacy-review-cleanup-note",
|
|
21861
|
-
children: "
|
|
21645
|
+
children: "Inspect or correct the project identity before assigning old shared data to a Space."
|
|
21862
21646
|
}) : null
|
|
21863
21647
|
] })]
|
|
21864
21648
|
}), /* @__PURE__ */ u("span", {
|
|
@@ -21897,20 +21681,20 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21897
21681
|
}) : null,
|
|
21898
21682
|
targetScopes.length > 0 && onReassign && canReassignLegacyGroup(group) ? /* @__PURE__ */ u("label", {
|
|
21899
21683
|
className: "legacy-review-target",
|
|
21900
|
-
children: [/* @__PURE__ */ u("span", { children: "Destination
|
|
21684
|
+
children: [/* @__PURE__ */ u("span", { children: "Destination Space" }), /* @__PURE__ */ u("select", {
|
|
21901
21685
|
className: "peer-scope-input",
|
|
21902
21686
|
disabled: busyIdentity != null,
|
|
21903
21687
|
onChange: (event) => updateSelectedScope(group, event.currentTarget.value),
|
|
21904
21688
|
value: selectedScopes[group.workspaceIdentity] || "",
|
|
21905
21689
|
children: [/* @__PURE__ */ u("option", {
|
|
21906
21690
|
value: "",
|
|
21907
|
-
children: "Choose
|
|
21691
|
+
children: "Choose Space…"
|
|
21908
21692
|
}), targetScopes.map((scope) => /* @__PURE__ */ u("option", {
|
|
21909
21693
|
value: scope.scopeId,
|
|
21910
21694
|
children: [
|
|
21911
21695
|
scope.label,
|
|
21912
21696
|
" · ",
|
|
21913
|
-
scope.authorityType,
|
|
21697
|
+
authorityLabel(scope.authorityType),
|
|
21914
21698
|
scope.scopeId === group.suggestedScopeId ? " · suggested" : ""
|
|
21915
21699
|
]
|
|
21916
21700
|
}, scope.scopeId))]
|
|
@@ -22213,7 +21997,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
22213
21997
|
try {
|
|
22214
21998
|
const runtime = await loadRuntimeInfo();
|
|
22215
21999
|
if (!runtime?.version) return;
|
|
22216
|
-
setRuntimeLabel(runtime.version, "
|
|
22000
|
+
setRuntimeLabel(runtime.version, "2eab49c");
|
|
22217
22001
|
} catch {}
|
|
22218
22002
|
}
|
|
22219
22003
|
var lastAnnouncedRefreshState = null;
|