@codemem/server 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +210 -4
- 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 +1068 -54
- package/static/index.html +295 -1
package/static/app.js
CHANGED
|
@@ -3280,9 +3280,26 @@
|
|
|
3280
3280
|
this.guardrailWarnings = input.guardrail_warnings ?? [];
|
|
3281
3281
|
}
|
|
3282
3282
|
};
|
|
3283
|
+
var LegacySharedReviewConfirmationError = class extends Error {
|
|
3284
|
+
preview;
|
|
3285
|
+
constructor(preview) {
|
|
3286
|
+
super("Legacy shared review confirmation required");
|
|
3287
|
+
this.name = "LegacySharedReviewConfirmationError";
|
|
3288
|
+
this.preview = preview;
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3283
3291
|
async function loadSharingDomainSettings() {
|
|
3284
3292
|
return fetchJson("/api/sync/sharing-domains/settings");
|
|
3285
3293
|
}
|
|
3294
|
+
async function loadProjectScopeInventory(input = {}) {
|
|
3295
|
+
const params = new URLSearchParams();
|
|
3296
|
+
for (const [key, value] of Object.entries(input)) {
|
|
3297
|
+
if (value == null || value === "") continue;
|
|
3298
|
+
params.set(key, String(value));
|
|
3299
|
+
}
|
|
3300
|
+
const query = params.toString();
|
|
3301
|
+
return fetchJson(`/api/sync/projects${query ? `?${query}` : ""}`);
|
|
3302
|
+
}
|
|
3286
3303
|
async function saveSharingDomainProjectMapping(input) {
|
|
3287
3304
|
const resp = await fetch("/api/sync/sharing-domains/project-mappings", {
|
|
3288
3305
|
method: "PUT",
|
|
@@ -3307,6 +3324,31 @@
|
|
|
3307
3324
|
if (!resp.ok) throw new Error(payloadError(payload) || text || "request failed");
|
|
3308
3325
|
return Boolean(payload?.deleted);
|
|
3309
3326
|
}
|
|
3327
|
+
async function reassignProjectInventoryProject(input) {
|
|
3328
|
+
const resp = await fetch("/api/sync/projects/reassign-project", {
|
|
3329
|
+
method: "POST",
|
|
3330
|
+
headers: { "Content-Type": "application/json" },
|
|
3331
|
+
body: JSON.stringify(input)
|
|
3332
|
+
});
|
|
3333
|
+
const { text, payload } = await readJsonPayload(resp);
|
|
3334
|
+
if (!resp.ok) throw new Error(payloadError(payload) || text || "request failed");
|
|
3335
|
+
if (!payload?.workspace_identity) throw new Error("response missing project reassignment");
|
|
3336
|
+
return payload;
|
|
3337
|
+
}
|
|
3338
|
+
async function reassignLegacySharedReviewGroup$1(input) {
|
|
3339
|
+
const resp = await fetch("/api/sync/legacy-shared-review/reassign", {
|
|
3340
|
+
method: "POST",
|
|
3341
|
+
headers: { "Content-Type": "application/json" },
|
|
3342
|
+
body: JSON.stringify(input)
|
|
3343
|
+
});
|
|
3344
|
+
const { text, payload } = await readJsonPayload(resp);
|
|
3345
|
+
if (!resp.ok) {
|
|
3346
|
+
if (payload?.error === "legacy_review_confirmation_required" && payload.preview) throw new LegacySharedReviewConfirmationError(payload.preview);
|
|
3347
|
+
throw new Error(payloadError(payload) || text || "request failed");
|
|
3348
|
+
}
|
|
3349
|
+
if (!payload?.workspace_identity) throw new Error("response missing legacy review reassignment");
|
|
3350
|
+
return payload;
|
|
3351
|
+
}
|
|
3310
3352
|
async function loadCoordinatorGroupPreferences(groupId) {
|
|
3311
3353
|
const resp = await fetch(`/api/coordinator/admin/groups/${encodeURIComponent(groupId)}/preferences`);
|
|
3312
3354
|
const { text, payload } = await readJsonPayload(resp);
|
|
@@ -3397,7 +3439,7 @@
|
|
|
3397
3439
|
}
|
|
3398
3440
|
//#endregion
|
|
3399
3441
|
//#region src/lib/dom.ts
|
|
3400
|
-
function el(tag, className, text) {
|
|
3442
|
+
function el$1(tag, className, text) {
|
|
3401
3443
|
const node = document.createElement(tag);
|
|
3402
3444
|
if (className) node.className = className;
|
|
3403
3445
|
if (text !== void 0 && text !== null) node.textContent = String(text);
|
|
@@ -3445,8 +3487,9 @@
|
|
|
3445
3487
|
//#region src/lib/state.ts
|
|
3446
3488
|
var ALL_TAB_IDS = [
|
|
3447
3489
|
"feed",
|
|
3448
|
-
"
|
|
3490
|
+
"projects",
|
|
3449
3491
|
"sync",
|
|
3492
|
+
"health",
|
|
3450
3493
|
"coordinator-admin"
|
|
3451
3494
|
];
|
|
3452
3495
|
var TAB_KEY = "codemem-tab";
|
|
@@ -14344,6 +14387,385 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
14344
14387
|
if (state.activeTab === "feed" && previousActorId !== nextActorId) updateFeedView(true);
|
|
14345
14388
|
}
|
|
14346
14389
|
//#endregion
|
|
14390
|
+
//#region src/tabs/projects.ts
|
|
14391
|
+
var STATUS_OPTIONS = [
|
|
14392
|
+
["", "All projects"],
|
|
14393
|
+
["needs_attention", "Needs review"],
|
|
14394
|
+
["suggested", "Has suggestion"],
|
|
14395
|
+
["local_only", "Stays on this device"],
|
|
14396
|
+
["explicitly_mapped", "Already assigned"],
|
|
14397
|
+
["legacy_review", "Older shared data"],
|
|
14398
|
+
["unmapped", "Missing project identity"]
|
|
14399
|
+
];
|
|
14400
|
+
var refreshProjects = null;
|
|
14401
|
+
var currentOffset = 0;
|
|
14402
|
+
var lastLimit = 25;
|
|
14403
|
+
var scopes = [];
|
|
14404
|
+
var openProjectDetails = /* @__PURE__ */ new Set();
|
|
14405
|
+
var draftDomainSelections = /* @__PURE__ */ new Map();
|
|
14406
|
+
var pendingConfirmations = /* @__PURE__ */ new Map();
|
|
14407
|
+
function el(id) {
|
|
14408
|
+
return document.getElementById(id);
|
|
14409
|
+
}
|
|
14410
|
+
function formatStatus(status) {
|
|
14411
|
+
return STATUS_OPTIONS.find(([value]) => value === status)?.[1] ?? status.replaceAll("_", " ");
|
|
14412
|
+
}
|
|
14413
|
+
function formatResolution(reason) {
|
|
14414
|
+
switch (reason) {
|
|
14415
|
+
case "exact_mapping": return "assigned to a Sharing domain";
|
|
14416
|
+
case "pattern_mapping": return "assigned by matching rule";
|
|
14417
|
+
case "explicit_override": return "manually assigned";
|
|
14418
|
+
default: return "stays on this device";
|
|
14419
|
+
}
|
|
14420
|
+
}
|
|
14421
|
+
function formatLatest(value) {
|
|
14422
|
+
if (!value) return "No recent sessions";
|
|
14423
|
+
const date = new Date(value);
|
|
14424
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
14425
|
+
return date.toLocaleString();
|
|
14426
|
+
}
|
|
14427
|
+
function strongestSignal(project) {
|
|
14428
|
+
if (project.git_remote) return project.git_remote;
|
|
14429
|
+
if (project.cwd) return project.cwd;
|
|
14430
|
+
return project.workspace_identity;
|
|
14431
|
+
}
|
|
14432
|
+
function scopeLabel(scopeId) {
|
|
14433
|
+
if (!scopeId) return "—";
|
|
14434
|
+
const scope = scopes.find((item) => item.scope_id === scopeId);
|
|
14435
|
+
return scope?.label ? `${scope.label} (${scope.scope_id})` : scopeId;
|
|
14436
|
+
}
|
|
14437
|
+
function assignableScopes() {
|
|
14438
|
+
return scopes.filter((scope) => scope.scope_id !== "legacy-shared-review");
|
|
14439
|
+
}
|
|
14440
|
+
function guardrailHeading(warning) {
|
|
14441
|
+
switch (warning.code) {
|
|
14442
|
+
case "unknown_project_local_only": return "Current behavior";
|
|
14443
|
+
case "basename_collision_review": return "Name collision";
|
|
14444
|
+
case "scope_reassignment_old_copies": return "Previous copies";
|
|
14445
|
+
case "broad_org_domain_pattern":
|
|
14446
|
+
case "home_directory_org_domain_pattern": return "Broad mapping";
|
|
14447
|
+
default: return "Review item";
|
|
14448
|
+
}
|
|
14449
|
+
}
|
|
14450
|
+
async function saveProjectMapping(project, scopeId, confirmedGuardrailTokens = []) {
|
|
14451
|
+
try {
|
|
14452
|
+
await saveSharingDomainProjectMapping({
|
|
14453
|
+
...project.mapping_id && project.resolution_reason === "exact_mapping" ? { id: project.mapping_id } : {},
|
|
14454
|
+
...confirmedGuardrailTokens.length > 0 ? { confirmed_guardrail_tokens: confirmedGuardrailTokens } : {},
|
|
14455
|
+
project_pattern: project.display_project,
|
|
14456
|
+
scope_id: scopeId,
|
|
14457
|
+
workspace_identity: project.workspace_identity
|
|
14458
|
+
});
|
|
14459
|
+
pendingConfirmations.delete(project.workspace_identity);
|
|
14460
|
+
draftDomainSelections.delete(project.workspace_identity);
|
|
14461
|
+
showGlobalNotice("Project Sharing domain updated. Device access grants are unchanged.");
|
|
14462
|
+
refreshProjects?.();
|
|
14463
|
+
} catch (error) {
|
|
14464
|
+
if (error instanceof SharingDomainGuardrailConfirmationError) {
|
|
14465
|
+
pendingConfirmations.set(project.workspace_identity, {
|
|
14466
|
+
requiredGuardrailTokens: error.requiredGuardrailTokens,
|
|
14467
|
+
scopeId,
|
|
14468
|
+
warnings: error.guardrailWarnings
|
|
14469
|
+
});
|
|
14470
|
+
refreshProjects?.();
|
|
14471
|
+
return;
|
|
14472
|
+
}
|
|
14473
|
+
showGlobalNotice(error instanceof Error ? error.message : "Unable to update project Sharing domain.", "warning");
|
|
14474
|
+
}
|
|
14475
|
+
}
|
|
14476
|
+
async function removeProjectMapping(project) {
|
|
14477
|
+
if (project.mapping_id == null) return;
|
|
14478
|
+
try {
|
|
14479
|
+
await deleteSharingDomainProjectMapping(project.mapping_id);
|
|
14480
|
+
pendingConfirmations.delete(project.workspace_identity);
|
|
14481
|
+
draftDomainSelections.delete(project.workspace_identity);
|
|
14482
|
+
showGlobalNotice("Project Sharing domain mapping removed. The next fallback now applies.");
|
|
14483
|
+
refreshProjects?.();
|
|
14484
|
+
} catch (error) {
|
|
14485
|
+
showGlobalNotice(error instanceof Error ? error.message : "Unable to remove project Sharing domain mapping.", "warning");
|
|
14486
|
+
}
|
|
14487
|
+
}
|
|
14488
|
+
async function reassignProject(project) {
|
|
14489
|
+
if (project.identity_source === "unmapped") return;
|
|
14490
|
+
const currentProject = String(project.project || project.display_project || "").trim();
|
|
14491
|
+
let suggestions = [];
|
|
14492
|
+
try {
|
|
14493
|
+
suggestions = (await loadProjects$1()).filter((name) => name && name !== currentProject);
|
|
14494
|
+
} catch {}
|
|
14495
|
+
const nextProject = await openSyncInputDialog({
|
|
14496
|
+
cancelLabel: "Cancel",
|
|
14497
|
+
confirmLabel: "Change project",
|
|
14498
|
+
description: `This will update ${project.session_count} session${project.session_count === 1 ? "" : "s"} and ${project.memory_count ?? 0} memor${project.memory_count === 1 ? "y" : "ies"} by changing the stored project. Sharing-domain assignment stays unchanged.`,
|
|
14499
|
+
initialValue: currentProject,
|
|
14500
|
+
placeholder: "Project name",
|
|
14501
|
+
suggestions,
|
|
14502
|
+
title: "Change project",
|
|
14503
|
+
validate: (value) => {
|
|
14504
|
+
const trimmed = value.trim();
|
|
14505
|
+
if (!trimmed) return "Enter a project name.";
|
|
14506
|
+
if (trimmed === currentProject) return "Already assigned to this project.";
|
|
14507
|
+
return null;
|
|
14508
|
+
}
|
|
14509
|
+
});
|
|
14510
|
+
if (nextProject == null) return;
|
|
14511
|
+
try {
|
|
14512
|
+
const result = await reassignProjectInventoryProject({
|
|
14513
|
+
project: nextProject.trim(),
|
|
14514
|
+
workspace_identity: project.workspace_identity
|
|
14515
|
+
});
|
|
14516
|
+
showGlobalNotice(`Changed project to ${result.project} for ${result.moved_session_count} session${result.moved_session_count === 1 ? "" : "s"}.`);
|
|
14517
|
+
refreshProjects?.();
|
|
14518
|
+
} catch (error) {
|
|
14519
|
+
showGlobalNotice(error instanceof Error ? error.message : "Unable to change project.", "warning");
|
|
14520
|
+
}
|
|
14521
|
+
}
|
|
14522
|
+
function renderProjectActions(project) {
|
|
14523
|
+
const actions = document.createElement("div");
|
|
14524
|
+
actions.className = "project-inventory-actions";
|
|
14525
|
+
if (project.identity_source === "unmapped") {
|
|
14526
|
+
const note = document.createElement("div");
|
|
14527
|
+
note.className = "settings-note";
|
|
14528
|
+
note.textContent = "This project is missing a stable path, git remote, or workspace id. It stays Local only until it has a stable identity.";
|
|
14529
|
+
actions.appendChild(note);
|
|
14530
|
+
return actions;
|
|
14531
|
+
}
|
|
14532
|
+
const label = document.createElement("label");
|
|
14533
|
+
label.className = "sr-only";
|
|
14534
|
+
const selectId = `project-domain-${project.workspace_identity.replace(/[^a-z0-9_-]/gi, "-")}`;
|
|
14535
|
+
label.htmlFor = selectId;
|
|
14536
|
+
label.textContent = `Sharing domain for ${project.display_project}`;
|
|
14537
|
+
const select = document.createElement("select");
|
|
14538
|
+
select.id = selectId;
|
|
14539
|
+
select.className = "project-domain-select";
|
|
14540
|
+
const currentAssignable = assignableScopes().some((scope) => scope.scope_id === project.resolved_scope_id);
|
|
14541
|
+
if (!currentAssignable && project.resolved_scope_id) {
|
|
14542
|
+
const current = document.createElement("option");
|
|
14543
|
+
current.value = project.resolved_scope_id;
|
|
14544
|
+
current.textContent = `${scopeLabel(project.resolved_scope_id)} — not assignable`;
|
|
14545
|
+
current.disabled = true;
|
|
14546
|
+
select.appendChild(current);
|
|
14547
|
+
}
|
|
14548
|
+
for (const scope of assignableScopes()) {
|
|
14549
|
+
const option = document.createElement("option");
|
|
14550
|
+
option.value = scope.scope_id;
|
|
14551
|
+
option.textContent = scope.label ? `${scope.label} · ${scope.scope_id}` : scope.scope_id;
|
|
14552
|
+
select.appendChild(option);
|
|
14553
|
+
}
|
|
14554
|
+
select.value = draftDomainSelections.get(project.workspace_identity) ?? project.suggested_scope_id ?? project.resolved_scope_id;
|
|
14555
|
+
const save = document.createElement("button");
|
|
14556
|
+
save.className = "settings-button";
|
|
14557
|
+
save.type = "button";
|
|
14558
|
+
save.textContent = project.suggested_scope_id && select.value === project.suggested_scope_id ? "Confirm suggestion" : "Save domain";
|
|
14559
|
+
save.disabled = select.value === project.resolved_scope_id && !currentAssignable;
|
|
14560
|
+
save.addEventListener("click", () => void saveProjectMapping(project, select.value));
|
|
14561
|
+
select.addEventListener("change", () => {
|
|
14562
|
+
draftDomainSelections.set(project.workspace_identity, select.value);
|
|
14563
|
+
pendingConfirmations.delete(project.workspace_identity);
|
|
14564
|
+
save.textContent = "Save domain";
|
|
14565
|
+
save.disabled = false;
|
|
14566
|
+
refreshProjects?.();
|
|
14567
|
+
});
|
|
14568
|
+
const keepLocal = document.createElement("button");
|
|
14569
|
+
keepLocal.className = "settings-button";
|
|
14570
|
+
keepLocal.type = "button";
|
|
14571
|
+
keepLocal.textContent = "Keep local-only";
|
|
14572
|
+
keepLocal.addEventListener("click", () => void saveProjectMapping(project, "local-default"));
|
|
14573
|
+
const remove = document.createElement("button");
|
|
14574
|
+
remove.className = "settings-button";
|
|
14575
|
+
remove.type = "button";
|
|
14576
|
+
remove.textContent = "Remove mapping";
|
|
14577
|
+
remove.disabled = project.mapping_id == null || project.resolution_reason !== "exact_mapping";
|
|
14578
|
+
remove.addEventListener("click", () => void removeProjectMapping(project));
|
|
14579
|
+
const changeProject = document.createElement("button");
|
|
14580
|
+
changeProject.className = "settings-button";
|
|
14581
|
+
changeProject.type = "button";
|
|
14582
|
+
changeProject.textContent = "Change project…";
|
|
14583
|
+
changeProject.disabled = project.session_count === 0;
|
|
14584
|
+
if (changeProject.disabled) changeProject.title = "No sessions are available to reassign for this saved mapping.";
|
|
14585
|
+
changeProject.addEventListener("click", () => void reassignProject(project));
|
|
14586
|
+
actions.append(label, select, save, keepLocal, remove, changeProject);
|
|
14587
|
+
const pending = pendingConfirmations.get(project.workspace_identity);
|
|
14588
|
+
if (pending) {
|
|
14589
|
+
const warningBox = document.createElement("div");
|
|
14590
|
+
warningBox.className = "settings-note project-guardrail-confirmation";
|
|
14591
|
+
warningBox.setAttribute("role", "alert");
|
|
14592
|
+
const title = document.createElement("strong");
|
|
14593
|
+
title.textContent = "Confirmation required before saving this Sharing domain.";
|
|
14594
|
+
const intro = document.createElement("p");
|
|
14595
|
+
intro.textContent = "Codemem can save this change after you acknowledge the checks below. Verify the workspace details, then confirm to complete the save.";
|
|
14596
|
+
const list = document.createElement("ul");
|
|
14597
|
+
for (const warning of pending.warnings) {
|
|
14598
|
+
const item = document.createElement("li");
|
|
14599
|
+
const itemTitle = document.createElement("strong");
|
|
14600
|
+
itemTitle.textContent = `${guardrailHeading(warning)}: `;
|
|
14601
|
+
const message = document.createElement("span");
|
|
14602
|
+
message.textContent = warning.message;
|
|
14603
|
+
item.append(itemTitle, message);
|
|
14604
|
+
list.appendChild(item);
|
|
14605
|
+
}
|
|
14606
|
+
const confirm = document.createElement("button");
|
|
14607
|
+
confirm.className = "settings-button";
|
|
14608
|
+
confirm.type = "button";
|
|
14609
|
+
confirm.textContent = "I understand, save domain";
|
|
14610
|
+
confirm.addEventListener("click", () => {
|
|
14611
|
+
const currentPending = pendingConfirmations.get(project.workspace_identity);
|
|
14612
|
+
if (!currentPending || currentPending.scopeId !== select.value) return;
|
|
14613
|
+
saveProjectMapping(project, currentPending.scopeId, currentPending.requiredGuardrailTokens);
|
|
14614
|
+
});
|
|
14615
|
+
const cancel = document.createElement("button");
|
|
14616
|
+
cancel.className = "settings-button";
|
|
14617
|
+
cancel.type = "button";
|
|
14618
|
+
cancel.textContent = "Cancel";
|
|
14619
|
+
cancel.addEventListener("click", () => {
|
|
14620
|
+
pendingConfirmations.delete(project.workspace_identity);
|
|
14621
|
+
refreshProjects?.();
|
|
14622
|
+
});
|
|
14623
|
+
warningBox.append(title, intro, list, confirm, cancel);
|
|
14624
|
+
actions.appendChild(warningBox);
|
|
14625
|
+
}
|
|
14626
|
+
return actions;
|
|
14627
|
+
}
|
|
14628
|
+
function renderProjectRow(project) {
|
|
14629
|
+
const row = document.createElement("article");
|
|
14630
|
+
row.className = "project-inventory-row";
|
|
14631
|
+
const header = document.createElement("div");
|
|
14632
|
+
header.className = "project-inventory-row-header";
|
|
14633
|
+
const title = document.createElement("div");
|
|
14634
|
+
title.className = "project-inventory-title";
|
|
14635
|
+
title.textContent = project.display_project;
|
|
14636
|
+
header.appendChild(title);
|
|
14637
|
+
const domain = document.createElement("div");
|
|
14638
|
+
domain.className = "project-inventory-domain";
|
|
14639
|
+
domain.textContent = scopeLabel(project.resolved_scope_id);
|
|
14640
|
+
header.appendChild(domain);
|
|
14641
|
+
row.appendChild(header);
|
|
14642
|
+
const meta = document.createElement("div");
|
|
14643
|
+
meta.className = "project-inventory-meta";
|
|
14644
|
+
meta.textContent = `${formatResolution(project.resolution_reason)} · ${project.identity_source} · ${formatLatest(project.latest_session_at)}`;
|
|
14645
|
+
row.appendChild(meta);
|
|
14646
|
+
const signal = document.createElement("div");
|
|
14647
|
+
signal.className = "project-inventory-signal mono";
|
|
14648
|
+
signal.textContent = strongestSignal(project);
|
|
14649
|
+
row.appendChild(signal);
|
|
14650
|
+
if (project.suggested_scope_id && project.suggested_scope_id !== project.resolved_scope_id) {
|
|
14651
|
+
const suggestion = document.createElement("div");
|
|
14652
|
+
suggestion.className = "settings-note project-suggestion-note";
|
|
14653
|
+
suggestion.textContent = project.suggestion_reason ? `Suggestion: ${project.suggestion_reason}` : `Suggestion: map this project to ${scopeLabel(project.suggested_scope_id)}.`;
|
|
14654
|
+
row.appendChild(suggestion);
|
|
14655
|
+
}
|
|
14656
|
+
const warnings = project.guardrail_warnings.filter((warning) => warning.severity === "warning");
|
|
14657
|
+
if (warnings.length > 0) {
|
|
14658
|
+
const warningBox = document.createElement("div");
|
|
14659
|
+
warningBox.className = "settings-note project-attention-note";
|
|
14660
|
+
warningBox.textContent = `Needs attention: ${warnings.map((warning) => warning.message).join(" ")}`;
|
|
14661
|
+
row.appendChild(warningBox);
|
|
14662
|
+
}
|
|
14663
|
+
if (project.statuses.length > 0) {
|
|
14664
|
+
const badges = document.createElement("div");
|
|
14665
|
+
badges.className = "project-inventory-badges";
|
|
14666
|
+
for (const status of project.statuses) {
|
|
14667
|
+
const badge = document.createElement("span");
|
|
14668
|
+
badge.className = `project-status-badge ${status}`;
|
|
14669
|
+
badge.textContent = formatStatus(status);
|
|
14670
|
+
badges.appendChild(badge);
|
|
14671
|
+
}
|
|
14672
|
+
row.appendChild(badges);
|
|
14673
|
+
}
|
|
14674
|
+
const detail = document.createElement("details");
|
|
14675
|
+
detail.className = "project-inventory-details";
|
|
14676
|
+
detail.open = openProjectDetails.has(project.workspace_identity);
|
|
14677
|
+
detail.addEventListener("toggle", () => {
|
|
14678
|
+
if (detail.open) openProjectDetails.add(project.workspace_identity);
|
|
14679
|
+
else openProjectDetails.delete(project.workspace_identity);
|
|
14680
|
+
});
|
|
14681
|
+
const summary = document.createElement("summary");
|
|
14682
|
+
summary.textContent = "Identity and mapping details";
|
|
14683
|
+
detail.appendChild(summary);
|
|
14684
|
+
const list = document.createElement("dl");
|
|
14685
|
+
list.className = "project-detail-grid";
|
|
14686
|
+
const fields = [
|
|
14687
|
+
["Workspace identity", project.workspace_identity],
|
|
14688
|
+
["Project", project.project],
|
|
14689
|
+
["CWD", project.cwd],
|
|
14690
|
+
["Git remote", project.git_remote],
|
|
14691
|
+
["Git branch", project.git_branch],
|
|
14692
|
+
["Suggested domain", project.suggested_scope_id],
|
|
14693
|
+
["Suggestion reason", project.suggestion_reason],
|
|
14694
|
+
["Sessions", project.session_count],
|
|
14695
|
+
["Memories", project.memory_count ?? "count unavailable"]
|
|
14696
|
+
];
|
|
14697
|
+
for (const [label, value] of fields) {
|
|
14698
|
+
const dt = document.createElement("dt");
|
|
14699
|
+
dt.textContent = label;
|
|
14700
|
+
const dd = document.createElement("dd");
|
|
14701
|
+
dd.textContent = value == null || value === "" ? "—" : String(value);
|
|
14702
|
+
list.append(dt, dd);
|
|
14703
|
+
}
|
|
14704
|
+
detail.appendChild(list);
|
|
14705
|
+
detail.appendChild(renderProjectActions(project));
|
|
14706
|
+
row.appendChild(detail);
|
|
14707
|
+
return row;
|
|
14708
|
+
}
|
|
14709
|
+
function renderEmpty(message) {
|
|
14710
|
+
const list = el("projectsInventoryList");
|
|
14711
|
+
if (!list) return;
|
|
14712
|
+
list.textContent = "";
|
|
14713
|
+
const empty = document.createElement("div");
|
|
14714
|
+
empty.className = "settings-note";
|
|
14715
|
+
empty.textContent = message;
|
|
14716
|
+
list.appendChild(empty);
|
|
14717
|
+
}
|
|
14718
|
+
async function loadProjectsData() {
|
|
14719
|
+
const meta = el("projectsInventoryMeta");
|
|
14720
|
+
const list = el("projectsInventoryList");
|
|
14721
|
+
if (!meta || !list) return;
|
|
14722
|
+
meta.textContent = "Loading project inventory…";
|
|
14723
|
+
try {
|
|
14724
|
+
const [result, settings] = await Promise.all([loadProjectScopeInventory({
|
|
14725
|
+
limit: lastLimit,
|
|
14726
|
+
offset: currentOffset,
|
|
14727
|
+
q: el("projectsSearch")?.value.trim() || void 0,
|
|
14728
|
+
status: el("projectsStatusFilter")?.value || void 0
|
|
14729
|
+
}), loadSharingDomainSettings()]);
|
|
14730
|
+
scopes = settings.scopes;
|
|
14731
|
+
list.textContent = "";
|
|
14732
|
+
if (result.projects.length === 0) renderEmpty("No projects match those filters.");
|
|
14733
|
+
else for (const project of result.projects) list.appendChild(renderProjectRow(project));
|
|
14734
|
+
meta.textContent = result.total === 0 ? "0 projects found" : `${result.total} project${result.total === 1 ? "" : "s"} found · showing ${result.offset + 1}-${Math.min(result.offset + result.projects.length, result.total)}`;
|
|
14735
|
+
const prev = el("projectsPrevPage");
|
|
14736
|
+
const next = el("projectsNextPage");
|
|
14737
|
+
if (prev) prev.disabled = result.offset === 0;
|
|
14738
|
+
if (next) next.disabled = !result.has_more;
|
|
14739
|
+
} catch (error) {
|
|
14740
|
+
meta.textContent = "Project inventory failed to load.";
|
|
14741
|
+
renderEmpty(error instanceof Error ? error.message : "Unable to load project inventory.");
|
|
14742
|
+
}
|
|
14743
|
+
}
|
|
14744
|
+
function initProjectsTab(refresh) {
|
|
14745
|
+
refreshProjects = refresh;
|
|
14746
|
+
const status = el("projectsStatusFilter");
|
|
14747
|
+
if (status && status.options.length === 0) for (const [value, label] of STATUS_OPTIONS) {
|
|
14748
|
+
const option = document.createElement("option");
|
|
14749
|
+
option.value = value;
|
|
14750
|
+
option.textContent = label;
|
|
14751
|
+
status.appendChild(option);
|
|
14752
|
+
}
|
|
14753
|
+
const requestRefresh = () => {
|
|
14754
|
+
currentOffset = 0;
|
|
14755
|
+
refreshProjects?.();
|
|
14756
|
+
};
|
|
14757
|
+
el("projectsSearch")?.addEventListener("input", requestRefresh);
|
|
14758
|
+
status?.addEventListener("change", requestRefresh);
|
|
14759
|
+
el("projectsPrevPage")?.addEventListener("click", () => {
|
|
14760
|
+
currentOffset = Math.max(0, currentOffset - lastLimit);
|
|
14761
|
+
refreshProjects?.();
|
|
14762
|
+
});
|
|
14763
|
+
el("projectsNextPage")?.addEventListener("click", () => {
|
|
14764
|
+
currentOffset += lastLimit;
|
|
14765
|
+
refreshProjects?.();
|
|
14766
|
+
});
|
|
14767
|
+
}
|
|
14768
|
+
//#endregion
|
|
14347
14769
|
//#region src/tabs/settings/data/parse.ts
|
|
14348
14770
|
function parseCommandArgv(raw, options) {
|
|
14349
14771
|
const text = raw.trim();
|
|
@@ -15964,7 +16386,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
15964
16386
|
return scopes.find((scope) => scope.scope_id === scopeId)?.label || scopeId;
|
|
15965
16387
|
}
|
|
15966
16388
|
function resetDrafts(settings) {
|
|
15967
|
-
return Object.fromEntries(settings.projects.map((project) => [project.workspace_identity, project.resolved_scope_id]));
|
|
16389
|
+
return Object.fromEntries(settings.projects.map((project) => [project.workspace_identity, project.suggested_scope_id ?? project.resolved_scope_id]));
|
|
15968
16390
|
}
|
|
15969
16391
|
function canAssignProject(project) {
|
|
15970
16392
|
return project.identity_source !== "unmapped";
|
|
@@ -15980,6 +16402,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
15980
16402
|
function visibleProjectWarnings(project) {
|
|
15981
16403
|
return (project.guardrail_warnings ?? []).filter((warning) => warning.severity === "warning" || warning.code === "unknown_project_local_only");
|
|
15982
16404
|
}
|
|
16405
|
+
function isUsingSuggestion(project, currentValue) {
|
|
16406
|
+
return Boolean(project.suggested_scope_id && currentValue === project.suggested_scope_id && project.suggested_scope_id !== project.resolved_scope_id);
|
|
16407
|
+
}
|
|
15983
16408
|
function settingsMappingWarnings(settings) {
|
|
15984
16409
|
return settings.mappings.flatMap((mapping) => mapping.guardrail_warnings ?? []);
|
|
15985
16410
|
}
|
|
@@ -16015,7 +16440,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
16015
16440
|
y$2(() => {
|
|
16016
16441
|
reload();
|
|
16017
16442
|
}, []);
|
|
16018
|
-
const scopeOptions = T$1(() => (settings?.scopes ?? []).map((scope) => ({
|
|
16443
|
+
const scopeOptions = T$1(() => (settings?.scopes ?? []).filter((scope) => scope.scope_id !== "legacy-shared-review").map((scope) => ({
|
|
16019
16444
|
value: scope.scope_id,
|
|
16020
16445
|
label: domainLabel(scope)
|
|
16021
16446
|
})), [settings?.scopes]);
|
|
@@ -16070,20 +16495,32 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
16070
16495
|
setSavingKey(null);
|
|
16071
16496
|
}
|
|
16072
16497
|
};
|
|
16498
|
+
const openProjects = () => {
|
|
16499
|
+
window.location.hash = "projects";
|
|
16500
|
+
};
|
|
16073
16501
|
return /* @__PURE__ */ u("div", {
|
|
16074
16502
|
className: "settings-group",
|
|
16075
16503
|
children: [
|
|
16076
16504
|
/* @__PURE__ */ u("h3", {
|
|
16077
16505
|
className: "settings-group-title",
|
|
16078
|
-
children: "Sharing
|
|
16506
|
+
children: "Advanced Sharing-domain mappings"
|
|
16079
16507
|
}),
|
|
16080
16508
|
/* @__PURE__ */ u("div", {
|
|
16081
16509
|
className: "small",
|
|
16082
|
-
children: "
|
|
16510
|
+
children: "Use Projects for day-to-day project/domain review. This fallback panel exposes the same mapping controls for troubleshooting and recovery."
|
|
16083
16511
|
}),
|
|
16084
16512
|
/* @__PURE__ */ u("div", {
|
|
16085
16513
|
className: "small",
|
|
16086
|
-
children: "
|
|
16514
|
+
children: "Saving a mapping changes local scope resolution for future writes; it does not grant any peer or coordinator member access by itself."
|
|
16515
|
+
}),
|
|
16516
|
+
/* @__PURE__ */ u("div", {
|
|
16517
|
+
className: "section-actions",
|
|
16518
|
+
children: /* @__PURE__ */ u("button", {
|
|
16519
|
+
className: "settings-button",
|
|
16520
|
+
onClick: openProjects,
|
|
16521
|
+
type: "button",
|
|
16522
|
+
children: "Open Projects review"
|
|
16523
|
+
})
|
|
16087
16524
|
}),
|
|
16088
16525
|
settings ? /* @__PURE__ */ u(GuardrailMessages, {
|
|
16089
16526
|
label: "Sharing domain mapping warnings",
|
|
@@ -16109,6 +16546,8 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
16109
16546
|
const unchanged = currentValue === project.resolved_scope_id;
|
|
16110
16547
|
const assignable = canAssignProject(project);
|
|
16111
16548
|
const currentScopeName = scopeName(settings.scopes, project.resolved_scope_id);
|
|
16549
|
+
const usingSuggestion = isUsingSuggestion(project, currentValue);
|
|
16550
|
+
const suggestedScopeName = project.suggested_scope_id ? scopeName(settings.scopes, project.suggested_scope_id) : null;
|
|
16112
16551
|
const pendingConfirmation = confirmation?.workspaceIdentity === project.workspace_identity ? confirmation : null;
|
|
16113
16552
|
const canRemoveProjectMapping = assignable && project.mapping_id != null && project.resolution_reason === "exact_mapping";
|
|
16114
16553
|
return /* @__PURE__ */ u("div", {
|
|
@@ -16150,6 +16589,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
16150
16589
|
resolutionLabel(project.resolution_reason)
|
|
16151
16590
|
]
|
|
16152
16591
|
}),
|
|
16592
|
+
project.suggested_scope_id && project.suggestion_reason ? /* @__PURE__ */ u("div", {
|
|
16593
|
+
className: "settings-note",
|
|
16594
|
+
role: "status",
|
|
16595
|
+
children: [
|
|
16596
|
+
/* @__PURE__ */ u("strong", { children: ["Suggested mapping: ", suggestedScopeName] }),
|
|
16597
|
+
/* @__PURE__ */ u("div", { children: project.suggestion_reason }),
|
|
16598
|
+
/* @__PURE__ */ u("div", { children: "Folders, paths, and git remotes are hints only. Saving this mapping requires your confirmation and does not grant any peer access." })
|
|
16599
|
+
]
|
|
16600
|
+
}) : null,
|
|
16153
16601
|
/* @__PURE__ */ u(GuardrailMessages, { warnings: visibleProjectWarnings(project) }),
|
|
16154
16602
|
!assignable ? /* @__PURE__ */ u("div", {
|
|
16155
16603
|
className: "settings-note",
|
|
@@ -16186,7 +16634,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
16186
16634
|
disabled: !assignable || saving || unchanged,
|
|
16187
16635
|
onClick: () => void saveProject(project),
|
|
16188
16636
|
type: "button",
|
|
16189
|
-
children: saving ? "Saving…" : "Save Sharing domain"
|
|
16637
|
+
children: saving ? "Saving…" : usingSuggestion ? "Confirm suggested mapping" : "Save Sharing domain"
|
|
16190
16638
|
}), /* @__PURE__ */ u("button", {
|
|
16191
16639
|
className: "settings-button",
|
|
16192
16640
|
disabled: saving || !canRemoveProjectMapping,
|
|
@@ -17346,6 +17794,90 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
17346
17794
|
emptyMessage: "No Sharing-domain grants exist for this device yet. Project filters below cannot send data by themselves."
|
|
17347
17795
|
};
|
|
17348
17796
|
}
|
|
17797
|
+
var PERSONAL_SCOPE_WORDS = [
|
|
17798
|
+
"personal",
|
|
17799
|
+
"private",
|
|
17800
|
+
"home",
|
|
17801
|
+
"mine",
|
|
17802
|
+
"me"
|
|
17803
|
+
];
|
|
17804
|
+
var OSS_SCOPE_WORDS = [
|
|
17805
|
+
"oss",
|
|
17806
|
+
"open source",
|
|
17807
|
+
"opensource",
|
|
17808
|
+
"community",
|
|
17809
|
+
"public"
|
|
17810
|
+
];
|
|
17811
|
+
var WORK_LIKE_SCOPE_WORDS = ["work", "client"];
|
|
17812
|
+
var LOCAL_SCOPE_WORDS = [
|
|
17813
|
+
"local",
|
|
17814
|
+
"legacy",
|
|
17815
|
+
"review"
|
|
17816
|
+
];
|
|
17817
|
+
function scopeSearchText(scope) {
|
|
17818
|
+
return [
|
|
17819
|
+
scope.scope_id,
|
|
17820
|
+
scope.label,
|
|
17821
|
+
scope.kind,
|
|
17822
|
+
scope.authority_type
|
|
17823
|
+
].map((value) => cleanText(value) ?? "").join(" ").toLowerCase();
|
|
17824
|
+
}
|
|
17825
|
+
function scopeIdentitySearchText(scope) {
|
|
17826
|
+
return [
|
|
17827
|
+
scope.scope_id,
|
|
17828
|
+
scope.label,
|
|
17829
|
+
scope.kind
|
|
17830
|
+
].map((value) => cleanText(value) ?? "").join(" ").toLowerCase();
|
|
17831
|
+
}
|
|
17832
|
+
function escapeRegex(value) {
|
|
17833
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
17834
|
+
}
|
|
17835
|
+
function hasScopeWord(scope, words) {
|
|
17836
|
+
const text = scopeSearchText(scope);
|
|
17837
|
+
return words.some((word) => {
|
|
17838
|
+
const escaped = escapeRegex(word.toLowerCase());
|
|
17839
|
+
return new RegExp(`(^|[^a-z0-9])${escaped}([^a-z0-9]|$)`).test(text);
|
|
17840
|
+
});
|
|
17841
|
+
}
|
|
17842
|
+
function isPersonalOrOssScope(scope) {
|
|
17843
|
+
return hasScopeWord(scope, PERSONAL_SCOPE_WORDS) || hasScopeWord(scope, OSS_SCOPE_WORDS);
|
|
17844
|
+
}
|
|
17845
|
+
function isLocalOrLegacyScope(scope) {
|
|
17846
|
+
const text = scopeIdentitySearchText(scope);
|
|
17847
|
+
return LOCAL_SCOPE_WORDS.some((word) => {
|
|
17848
|
+
const escaped = escapeRegex(word.toLowerCase());
|
|
17849
|
+
return new RegExp(`(^|[^a-z0-9])${escaped}([^a-z0-9]|$)`).test(text);
|
|
17850
|
+
});
|
|
17851
|
+
}
|
|
17852
|
+
function isWorkLikeScope(scope) {
|
|
17853
|
+
return !isPersonalOrOssScope(scope) && (!isLocalOrLegacyScope(scope) || hasScopeWord(scope, WORK_LIKE_SCOPE_WORDS));
|
|
17854
|
+
}
|
|
17855
|
+
function derivePeerGrantRoleMismatchView(peer) {
|
|
17856
|
+
const scopes = Array.isArray(peer.authorized_scopes) ? peer.authorized_scopes : [];
|
|
17857
|
+
if (!Boolean(cleanText(peer.discovered_via_group_id) || cleanText(peer.discovered_via_coordinator_id)) || scopes.length === 0) return {
|
|
17858
|
+
badgeLabel: null,
|
|
17859
|
+
detail: "",
|
|
17860
|
+
isVisible: false,
|
|
17861
|
+
message: "",
|
|
17862
|
+
title: ""
|
|
17863
|
+
};
|
|
17864
|
+
const hasPersonalOrOssGrant = scopes.some(isPersonalOrOssScope);
|
|
17865
|
+
const hasWorkLikeGrant = scopes.some(isWorkLikeScope);
|
|
17866
|
+
if (!hasPersonalOrOssGrant || hasWorkLikeGrant) return {
|
|
17867
|
+
badgeLabel: null,
|
|
17868
|
+
detail: "",
|
|
17869
|
+
isVisible: false,
|
|
17870
|
+
message: "",
|
|
17871
|
+
title: ""
|
|
17872
|
+
};
|
|
17873
|
+
return {
|
|
17874
|
+
badgeLabel: "Review domain fit",
|
|
17875
|
+
detail: "Coordinator/group context helps find the device, and project filters only narrow already-authorized domains. Neither one grants a missing work/client domain.",
|
|
17876
|
+
isVisible: true,
|
|
17877
|
+
message: "This coordinator-discovered device has personal or OSS Sharing-domain grants, but no separate work/client-like domain grant. If this device is meant for work/client sync, add that Sharing domain explicitly before treating sync as validated.",
|
|
17878
|
+
title: "Check this device's Sharing-domain role"
|
|
17879
|
+
};
|
|
17880
|
+
}
|
|
17349
17881
|
function derivePeerProjectNarrowingView(scope) {
|
|
17350
17882
|
const effectiveInclude = cleanList(scope?.effective_include);
|
|
17351
17883
|
const effectiveExclude = cleanList(scope?.effective_exclude);
|
|
@@ -17714,23 +18246,23 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
17714
18246
|
let query = "";
|
|
17715
18247
|
let activeIndex = 0;
|
|
17716
18248
|
let popoverOpen = false;
|
|
17717
|
-
const container = el("div", "project-scope-picker");
|
|
17718
|
-
const selectedList = el("ul", "project-scope-picker-selected");
|
|
17719
|
-
const trigger = el("button", "project-scope-picker-trigger settings-button");
|
|
18249
|
+
const container = el$1("div", "project-scope-picker");
|
|
18250
|
+
const selectedList = el$1("ul", "project-scope-picker-selected");
|
|
18251
|
+
const trigger = el$1("button", "project-scope-picker-trigger settings-button");
|
|
17720
18252
|
trigger.type = "button";
|
|
17721
|
-
const triggerLabel = el("span", null, placeholder || "Add project");
|
|
17722
|
-
const triggerPlus = el("span", null, "+");
|
|
18253
|
+
const triggerLabel = el$1("span", null, placeholder || "Add project");
|
|
18254
|
+
const triggerPlus = el$1("span", null, "+");
|
|
17723
18255
|
triggerPlus.setAttribute("aria-hidden", "true");
|
|
17724
18256
|
trigger.append(triggerPlus, triggerLabel);
|
|
17725
18257
|
trigger.setAttribute("aria-haspopup", "listbox");
|
|
17726
18258
|
trigger.setAttribute("aria-expanded", "false");
|
|
17727
|
-
const popover = el("div", "project-scope-picker-popover project-scope-picker-popover--inline");
|
|
18259
|
+
const popover = el$1("div", "project-scope-picker-popover project-scope-picker-popover--inline");
|
|
17728
18260
|
popover.hidden = true;
|
|
17729
|
-
const search = el("input", "project-scope-picker-search");
|
|
18261
|
+
const search = el$1("input", "project-scope-picker-search");
|
|
17730
18262
|
search.type = "text";
|
|
17731
18263
|
search.placeholder = "Search or create…";
|
|
17732
18264
|
search.setAttribute("aria-label", "Search projects");
|
|
17733
|
-
const results = el("div", "project-scope-picker-results");
|
|
18265
|
+
const results = el$1("div", "project-scope-picker-results");
|
|
17734
18266
|
results.setAttribute("role", "listbox");
|
|
17735
18267
|
popover.append(search, results);
|
|
17736
18268
|
const knownPool = () => {
|
|
@@ -17783,14 +18315,14 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
17783
18315
|
const renderSelected = () => {
|
|
17784
18316
|
selectedList.textContent = "";
|
|
17785
18317
|
if (!values.length) {
|
|
17786
|
-
const note = el("li", "project-scope-picker-selected-empty", emptyLabel);
|
|
18318
|
+
const note = el$1("li", "project-scope-picker-selected-empty", emptyLabel);
|
|
17787
18319
|
selectedList.appendChild(note);
|
|
17788
18320
|
return;
|
|
17789
18321
|
}
|
|
17790
18322
|
values.forEach((value) => {
|
|
17791
|
-
const chip = el("li", "project-scope-picker-selected-chip");
|
|
17792
|
-
const label = el("span", null, value);
|
|
17793
|
-
const remove = el("button", "project-scope-picker-selected-remove", "×");
|
|
18323
|
+
const chip = el$1("li", "project-scope-picker-selected-chip");
|
|
18324
|
+
const label = el$1("span", null, value);
|
|
18325
|
+
const remove = el$1("button", "project-scope-picker-selected-remove", "×");
|
|
17794
18326
|
remove.type = "button";
|
|
17795
18327
|
remove.setAttribute("aria-label", `Remove ${value}`);
|
|
17796
18328
|
remove.addEventListener("click", () => {
|
|
@@ -17809,19 +18341,19 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
17809
18341
|
const rowCount = rows.length + (withCreate ? 1 : 0);
|
|
17810
18342
|
if (activeIndex >= rowCount) activeIndex = Math.max(0, rowCount - 1);
|
|
17811
18343
|
if (!rows.length && !withCreate) {
|
|
17812
|
-
const empty = el("div", "project-scope-picker-empty-row", "No projects yet. Type a name to create one.");
|
|
18344
|
+
const empty = el$1("div", "project-scope-picker-empty-row", "No projects yet. Type a name to create one.");
|
|
17813
18345
|
results.appendChild(empty);
|
|
17814
18346
|
return;
|
|
17815
18347
|
}
|
|
17816
18348
|
rows.forEach((project, index) => {
|
|
17817
18349
|
const selected = values.includes(project);
|
|
17818
|
-
const row = el("button", index === activeIndex ? "project-scope-picker-row project-scope-picker-row--active" : "project-scope-picker-row");
|
|
18350
|
+
const row = el$1("button", index === activeIndex ? "project-scope-picker-row project-scope-picker-row--active" : "project-scope-picker-row");
|
|
17819
18351
|
row.type = "button";
|
|
17820
18352
|
row.setAttribute("role", "option");
|
|
17821
18353
|
row.setAttribute("aria-selected", selected ? "true" : "false");
|
|
17822
|
-
const check = el("span", "project-scope-picker-row-check", selected ? "✓" : "");
|
|
18354
|
+
const check = el$1("span", "project-scope-picker-row-check", selected ? "✓" : "");
|
|
17823
18355
|
check.setAttribute("aria-hidden", "true");
|
|
17824
|
-
const labelEl = el("span", "project-scope-picker-row-label", project);
|
|
18356
|
+
const labelEl = el$1("span", "project-scope-picker-row-label", project);
|
|
17825
18357
|
row.append(check, labelEl);
|
|
17826
18358
|
row.addEventListener("click", () => {
|
|
17827
18359
|
activeIndex = index;
|
|
@@ -17835,12 +18367,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
17835
18367
|
});
|
|
17836
18368
|
if (withCreate) {
|
|
17837
18369
|
const index = rows.length;
|
|
17838
|
-
const row = el("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");
|
|
18370
|
+
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");
|
|
17839
18371
|
row.type = "button";
|
|
17840
18372
|
row.setAttribute("role", "option");
|
|
17841
|
-
const check = el("span", "project-scope-picker-row-check", "+");
|
|
18373
|
+
const check = el$1("span", "project-scope-picker-row-check", "+");
|
|
17842
18374
|
check.setAttribute("aria-hidden", "true");
|
|
17843
|
-
const labelEl = el("span", "project-scope-picker-row-label", `Create "${query.trim()}"`);
|
|
18375
|
+
const labelEl = el$1("span", "project-scope-picker-row-label", `Create "${query.trim()}"`);
|
|
17844
18376
|
row.append(check, labelEl);
|
|
17845
18377
|
row.addEventListener("click", () => createFromQuery());
|
|
17846
18378
|
row.addEventListener("mouseenter", () => {
|
|
@@ -17920,11 +18452,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
17920
18452
|
}
|
|
17921
18453
|
container.hidden = false;
|
|
17922
18454
|
actions.slice(0, 2).forEach((item) => {
|
|
17923
|
-
const row = el("div", "sync-action");
|
|
17924
|
-
const textWrap = el("div", "sync-action-text");
|
|
18455
|
+
const row = el$1("div", "sync-action");
|
|
18456
|
+
const textWrap = el$1("div", "sync-action-text");
|
|
17925
18457
|
textWrap.textContent = item.label;
|
|
17926
|
-
textWrap.appendChild(el("span", "sync-action-command", item.command));
|
|
17927
|
-
const btn = el("button", "settings-button sync-action-copy", "Copy");
|
|
18458
|
+
textWrap.appendChild(el$1("span", "sync-action-command", item.command));
|
|
18459
|
+
const btn = el$1("button", "settings-button sync-action-copy", "Copy");
|
|
17928
18460
|
btn.addEventListener("click", () => copyToClipboard(item.command, btn));
|
|
17929
18461
|
row.append(textWrap, btn);
|
|
17930
18462
|
container.appendChild(row);
|
|
@@ -18645,6 +19177,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18645
19177
|
const directionHint = DIRECTION_GLYPH[derivePeerDirection(peer)];
|
|
18646
19178
|
const peerStatus = peer.status || {};
|
|
18647
19179
|
const authorizedDomains = derivePeerAuthorizedDomainsView(peer);
|
|
19180
|
+
const grantRoleMismatch = derivePeerGrantRoleMismatchView(peer);
|
|
18648
19181
|
const scopeRejections = derivePeerScopeRejectionsView(peer);
|
|
18649
19182
|
const scope = peer.project_scope || {};
|
|
18650
19183
|
const projectNarrowing = derivePeerProjectNarrowingView(scope);
|
|
@@ -18900,6 +19433,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
18900
19433
|
title: "Inbound ops rejected by the sharing-domain scope gate. Expand for reason codes.",
|
|
18901
19434
|
children: scopeRejections.badgeLabel
|
|
18902
19435
|
}) : null,
|
|
19436
|
+
grantRoleMismatch.badgeLabel ? /* @__PURE__ */ u("span", {
|
|
19437
|
+
className: "badge badge-offline",
|
|
19438
|
+
title: "Review whether explicit Sharing-domain grants match this device's intended role.",
|
|
19439
|
+
children: grantRoleMismatch.badgeLabel
|
|
19440
|
+
}) : null,
|
|
18903
19441
|
peer.discovered_via_group_id ? /* @__PURE__ */ u("span", {
|
|
18904
19442
|
className: "badge actor-badge",
|
|
18905
19443
|
title: `Discovered through coordinator group ${peer.discovered_via_group_id}`,
|
|
@@ -19008,6 +19546,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
19008
19546
|
children: domain.label
|
|
19009
19547
|
}, domain.scopeId))
|
|
19010
19548
|
}) : null,
|
|
19549
|
+
grantRoleMismatch.isVisible ? /* @__PURE__ */ u("div", {
|
|
19550
|
+
className: "settings-note",
|
|
19551
|
+
role: "status",
|
|
19552
|
+
children: [
|
|
19553
|
+
/* @__PURE__ */ u("strong", { children: grantRoleMismatch.title }),
|
|
19554
|
+
/* @__PURE__ */ u("div", { children: grantRoleMismatch.message }),
|
|
19555
|
+
/* @__PURE__ */ u("div", { children: grantRoleMismatch.detail })
|
|
19556
|
+
]
|
|
19557
|
+
}) : null,
|
|
19011
19558
|
/* @__PURE__ */ u("div", {
|
|
19012
19559
|
className: "peer-meta",
|
|
19013
19560
|
children: projectNarrowing.note
|
|
@@ -20497,6 +21044,29 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20497
21044
|
}
|
|
20498
21045
|
//#endregion
|
|
20499
21046
|
//#region src/tabs/sync/components/sync-sharing-review.tsx
|
|
21047
|
+
function formatIdentitySource(source) {
|
|
21048
|
+
switch (source) {
|
|
21049
|
+
case "cwd": return "Matched by folder";
|
|
21050
|
+
case "git_remote": return "Matched by git remote";
|
|
21051
|
+
case "workspace_id": return "Matched by workspace";
|
|
21052
|
+
case "project": return "Matched by project name";
|
|
21053
|
+
case "unmapped": return "Missing project identity";
|
|
21054
|
+
default: return `Matched by ${source.replace(/_/g, " ")}`;
|
|
21055
|
+
}
|
|
21056
|
+
}
|
|
21057
|
+
function needsProjectCleanup(group) {
|
|
21058
|
+
const label = group.displayProject.toLowerCase();
|
|
21059
|
+
return label.includes("fatal:") || label.includes("not a git repository") || label === "unknown project" || group.identitySource === "unmapped";
|
|
21060
|
+
}
|
|
21061
|
+
function groupSearchText(group) {
|
|
21062
|
+
return [
|
|
21063
|
+
group.displayProject,
|
|
21064
|
+
group.identitySource,
|
|
21065
|
+
group.suggestedScopeId ?? "",
|
|
21066
|
+
group.suggestionReason ?? "",
|
|
21067
|
+
group.workspaceIdentity
|
|
21068
|
+
].join(" ").toLowerCase();
|
|
21069
|
+
}
|
|
20500
21070
|
function SharingReviewRow({ item, onReview }) {
|
|
20501
21071
|
return /* @__PURE__ */ u("div", {
|
|
20502
21072
|
className: "actor-row",
|
|
@@ -20530,31 +21100,305 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20530
21100
|
})]
|
|
20531
21101
|
});
|
|
20532
21102
|
}
|
|
20533
|
-
function LegacySharedReviewRow({ item }) {
|
|
21103
|
+
function LegacySharedReviewRow({ item, onReassign, onReview }) {
|
|
21104
|
+
const groups = item.groups ?? [];
|
|
21105
|
+
const targetScopes = item.targetScopes ?? [];
|
|
21106
|
+
const firstTargetScopeId = targetScopes[0]?.scopeId ?? null;
|
|
21107
|
+
const initialSelections = Object.fromEntries(groups.map((group) => [group.workspaceIdentity, group.suggestedScopeId ?? firstTargetScopeId ?? ""]));
|
|
21108
|
+
const [pending, setPending] = d$2({});
|
|
21109
|
+
const [selectedScopes, setSelectedScopes] = d$2(initialSelections);
|
|
21110
|
+
const [selectedGroups, setSelectedGroups] = d$2({});
|
|
21111
|
+
const [filter, setFilter] = d$2("all");
|
|
21112
|
+
const [query, setQuery] = d$2("");
|
|
21113
|
+
const [busyIdentity, setBusyIdentity] = d$2(null);
|
|
21114
|
+
const shownGroupCount = groups.length;
|
|
21115
|
+
const groupCount = item.totalGroupCount ?? shownGroupCount;
|
|
21116
|
+
const cleanupCount = groups.filter(needsProjectCleanup).length;
|
|
21117
|
+
const suggestedCount = groups.filter((group) => group.suggestedScopeId && !needsProjectCleanup(group)).length;
|
|
21118
|
+
const selectedCount = groups.filter((group) => selectedGroups[group.workspaceIdentity]).length;
|
|
21119
|
+
const visibleGroups = groups.filter((group) => {
|
|
21120
|
+
const cleanup = needsProjectCleanup(group);
|
|
21121
|
+
if (filter === "suggested" && (!group.suggestedScopeId || cleanup)) return false;
|
|
21122
|
+
if (filter === "cleanup" && !cleanup) return false;
|
|
21123
|
+
if (filter === "no-suggestion" && (group.suggestedScopeId || cleanup)) return false;
|
|
21124
|
+
if (filter === "selected" && !selectedGroups[group.workspaceIdentity]) return false;
|
|
21125
|
+
return !query.trim() || groupSearchText(group).includes(query.trim().toLowerCase());
|
|
21126
|
+
});
|
|
21127
|
+
async function applyGroup(group) {
|
|
21128
|
+
const selectedScopeId = selectedScopes[group.workspaceIdentity] || group.suggestedScopeId || "";
|
|
21129
|
+
if (!selectedScopeId || !onReassign || busyIdentity) return;
|
|
21130
|
+
setBusyIdentity(group.workspaceIdentity);
|
|
21131
|
+
try {
|
|
21132
|
+
const preview = await onReassign(group, selectedScopeId, Boolean(pending[group.workspaceIdentity]));
|
|
21133
|
+
if (preview) setPending((current) => ({
|
|
21134
|
+
...current,
|
|
21135
|
+
[group.workspaceIdentity]: preview
|
|
21136
|
+
}));
|
|
21137
|
+
else setPending((current) => {
|
|
21138
|
+
const next = { ...current };
|
|
21139
|
+
delete next[group.workspaceIdentity];
|
|
21140
|
+
return next;
|
|
21141
|
+
});
|
|
21142
|
+
} finally {
|
|
21143
|
+
setBusyIdentity(null);
|
|
21144
|
+
}
|
|
21145
|
+
}
|
|
21146
|
+
function updateSelectedScope(group, scopeId) {
|
|
21147
|
+
setSelectedScopes((current) => ({
|
|
21148
|
+
...current,
|
|
21149
|
+
[group.workspaceIdentity]: scopeId
|
|
21150
|
+
}));
|
|
21151
|
+
setPending((current) => {
|
|
21152
|
+
const next = { ...current };
|
|
21153
|
+
delete next[group.workspaceIdentity];
|
|
21154
|
+
return next;
|
|
21155
|
+
});
|
|
21156
|
+
}
|
|
21157
|
+
function toggleGroup(group, checked) {
|
|
21158
|
+
setSelectedGroups((current) => ({
|
|
21159
|
+
...current,
|
|
21160
|
+
[group.workspaceIdentity]: checked
|
|
21161
|
+
}));
|
|
21162
|
+
}
|
|
21163
|
+
function setVisibleSelected(checked) {
|
|
21164
|
+
setSelectedGroups((current) => ({
|
|
21165
|
+
...current,
|
|
21166
|
+
...Object.fromEntries(visibleGroups.map((group) => [group.workspaceIdentity, checked]))
|
|
21167
|
+
}));
|
|
21168
|
+
}
|
|
21169
|
+
function selectSuggestedGroups() {
|
|
21170
|
+
setSelectedGroups((current) => ({
|
|
21171
|
+
...current,
|
|
21172
|
+
...Object.fromEntries(groups.filter((group) => group.suggestedScopeId && !needsProjectCleanup(group)).map((group) => [group.workspaceIdentity, true]))
|
|
21173
|
+
}));
|
|
21174
|
+
}
|
|
21175
|
+
async function applySelectedGroups() {
|
|
21176
|
+
if (!onReassign || busyIdentity || selectedCount === 0) return;
|
|
21177
|
+
const selected = groups.filter((group) => selectedGroups[group.workspaceIdentity]);
|
|
21178
|
+
const allPreviewed = selected.every((group) => pending[group.workspaceIdentity]);
|
|
21179
|
+
setBusyIdentity("bulk");
|
|
21180
|
+
try {
|
|
21181
|
+
for (const group of selected) {
|
|
21182
|
+
const selectedScopeId = selectedScopes[group.workspaceIdentity] || group.suggestedScopeId || "";
|
|
21183
|
+
if (!selectedScopeId) continue;
|
|
21184
|
+
const preview = await onReassign(group, selectedScopeId, allPreviewed);
|
|
21185
|
+
if (preview) setPending((current) => ({
|
|
21186
|
+
...current,
|
|
21187
|
+
[group.workspaceIdentity]: preview
|
|
21188
|
+
}));
|
|
21189
|
+
else if (allPreviewed) {
|
|
21190
|
+
setPending((current) => {
|
|
21191
|
+
const next = { ...current };
|
|
21192
|
+
delete next[group.workspaceIdentity];
|
|
21193
|
+
return next;
|
|
21194
|
+
});
|
|
21195
|
+
setSelectedGroups((current) => ({
|
|
21196
|
+
...current,
|
|
21197
|
+
[group.workspaceIdentity]: false
|
|
21198
|
+
}));
|
|
21199
|
+
}
|
|
21200
|
+
}
|
|
21201
|
+
} finally {
|
|
21202
|
+
setBusyIdentity(null);
|
|
21203
|
+
}
|
|
21204
|
+
}
|
|
20534
21205
|
return /* @__PURE__ */ u("div", {
|
|
20535
|
-
className: "
|
|
20536
|
-
children:
|
|
20537
|
-
|
|
20538
|
-
|
|
20539
|
-
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
21206
|
+
className: "legacy-review-card",
|
|
21207
|
+
children: [
|
|
21208
|
+
/* @__PURE__ */ u("div", {
|
|
21209
|
+
className: "legacy-review-header",
|
|
21210
|
+
children: [/* @__PURE__ */ u("div", { children: [
|
|
21211
|
+
/* @__PURE__ */ u("div", {
|
|
21212
|
+
className: "actor-title",
|
|
21213
|
+
children: [/* @__PURE__ */ u("strong", { children: "Legacy shared review" }), /* @__PURE__ */ u("span", {
|
|
21214
|
+
className: "badge badge-offline",
|
|
21215
|
+
children: "Needs project review"
|
|
21216
|
+
})]
|
|
21217
|
+
}),
|
|
21218
|
+
/* @__PURE__ */ u("div", {
|
|
21219
|
+
className: "peer-meta legacy-review-summary",
|
|
21220
|
+
children: [
|
|
21221
|
+
groupCount.toLocaleString(),
|
|
21222
|
+
" older",
|
|
21223
|
+
" ",
|
|
21224
|
+
groupCount === 1 ? "project needs" : "projects need",
|
|
21225
|
+
" a Sharing domain. They contain",
|
|
21226
|
+
" ",
|
|
21227
|
+
item.memoryCount.toLocaleString(),
|
|
21228
|
+
" older shared memories total; review the projects, not individual memories.",
|
|
21229
|
+
shownGroupCount < groupCount ? ` Showing the first ${shownGroupCount.toLocaleString()} projects by memory count.` : ""
|
|
21230
|
+
]
|
|
21231
|
+
}),
|
|
21232
|
+
/* @__PURE__ */ u("div", {
|
|
21233
|
+
className: "peer-meta legacy-review-warning",
|
|
21234
|
+
children: "Nothing moves automatically. Reassignment changes future sync authorization; it does not erase copies peers already received."
|
|
21235
|
+
})
|
|
21236
|
+
] }), /* @__PURE__ */ u("div", {
|
|
21237
|
+
className: "actor-actions",
|
|
21238
|
+
children: /* @__PURE__ */ u("button", {
|
|
21239
|
+
type: "button",
|
|
21240
|
+
className: "settings-button",
|
|
21241
|
+
onClick: onReview,
|
|
21242
|
+
children: "Manage all projects"
|
|
21243
|
+
})
|
|
20543
21244
|
})]
|
|
20544
|
-
}),
|
|
20545
|
-
|
|
20546
|
-
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
21245
|
+
}),
|
|
21246
|
+
/* @__PURE__ */ u("div", {
|
|
21247
|
+
className: "legacy-review-toolbar",
|
|
21248
|
+
children: [/* @__PURE__ */ u("div", {
|
|
21249
|
+
className: "legacy-review-filter-row",
|
|
21250
|
+
children: [
|
|
21251
|
+
["all", `Showing ${shownGroupCount.toLocaleString()}`],
|
|
21252
|
+
["suggested", `Suggested ${suggestedCount.toLocaleString()}`],
|
|
21253
|
+
["cleanup", `Needs cleanup ${cleanupCount.toLocaleString()}`],
|
|
21254
|
+
["no-suggestion", "No suggestion"],
|
|
21255
|
+
["selected", `Selected ${selectedCount.toLocaleString()}`]
|
|
21256
|
+
].map(([value, label]) => /* @__PURE__ */ u("button", {
|
|
21257
|
+
className: filter === value ? "settings-button legacy-filter active" : "settings-button legacy-filter",
|
|
21258
|
+
onClick: () => setFilter(value),
|
|
21259
|
+
type: "button",
|
|
21260
|
+
children: label
|
|
21261
|
+
}, value))
|
|
21262
|
+
}), /* @__PURE__ */ u("div", {
|
|
21263
|
+
className: "legacy-review-bulk-row",
|
|
21264
|
+
children: [
|
|
21265
|
+
/* @__PURE__ */ u("input", {
|
|
21266
|
+
"aria-label": "Search legacy project groups",
|
|
21267
|
+
className: "peer-scope-input legacy-review-search",
|
|
21268
|
+
onInput: (event) => setQuery(event.currentTarget.value),
|
|
21269
|
+
placeholder: "Search project, signal, or destination…",
|
|
21270
|
+
value: query
|
|
21271
|
+
}),
|
|
21272
|
+
/* @__PURE__ */ u("button", {
|
|
21273
|
+
className: "settings-button",
|
|
21274
|
+
onClick: () => setVisibleSelected(true),
|
|
21275
|
+
type: "button",
|
|
21276
|
+
children: "Select visible"
|
|
21277
|
+
}),
|
|
21278
|
+
/* @__PURE__ */ u("button", {
|
|
21279
|
+
className: "settings-button",
|
|
21280
|
+
onClick: selectSuggestedGroups,
|
|
21281
|
+
type: "button",
|
|
21282
|
+
children: "Select suggested"
|
|
21283
|
+
}),
|
|
21284
|
+
/* @__PURE__ */ u("button", {
|
|
21285
|
+
className: "settings-button",
|
|
21286
|
+
onClick: () => setVisibleSelected(false),
|
|
21287
|
+
type: "button",
|
|
21288
|
+
children: "Clear visible"
|
|
21289
|
+
}),
|
|
21290
|
+
onReassign && targetScopes.length > 0 ? /* @__PURE__ */ u("button", {
|
|
21291
|
+
className: "settings-save",
|
|
21292
|
+
disabled: busyIdentity != null || selectedCount === 0,
|
|
21293
|
+
onClick: () => void applySelectedGroups(),
|
|
21294
|
+
type: "button",
|
|
21295
|
+
children: busyIdentity === "bulk" ? "Working…" : selectedCount > 0 && groups.filter((group) => selectedGroups[group.workspaceIdentity]).every((group) => pending[group.workspaceIdentity]) ? `Apply ${selectedCount.toLocaleString()} selected` : `Preview ${selectedCount.toLocaleString()} selected`
|
|
21296
|
+
}) : onReassign && selectedCount > 0 ? /* @__PURE__ */ u("span", {
|
|
21297
|
+
className: "legacy-review-cleanup-note",
|
|
21298
|
+
children: "Add or join a Sharing domain before bulk reassignment."
|
|
21299
|
+
}) : null
|
|
21300
|
+
]
|
|
21301
|
+
})]
|
|
21302
|
+
}),
|
|
21303
|
+
groups.length > 0 ? /* @__PURE__ */ u("ul", {
|
|
21304
|
+
className: "legacy-review-groups",
|
|
21305
|
+
"aria-label": "Legacy shared review project groups",
|
|
21306
|
+
children: visibleGroups.map((group) => /* @__PURE__ */ u("li", {
|
|
21307
|
+
className: "legacy-review-group",
|
|
21308
|
+
children: [
|
|
21309
|
+
/* @__PURE__ */ u("div", {
|
|
21310
|
+
className: "legacy-review-group-main",
|
|
21311
|
+
children: [/* @__PURE__ */ u("div", {
|
|
21312
|
+
className: "legacy-review-title-wrap",
|
|
21313
|
+
children: [/* @__PURE__ */ u("input", {
|
|
21314
|
+
"aria-label": `Select ${group.displayProject}`,
|
|
21315
|
+
checked: Boolean(selectedGroups[group.workspaceIdentity]),
|
|
21316
|
+
className: "cm-checkbox",
|
|
21317
|
+
onChange: (event) => toggleGroup(group, event.currentTarget.checked),
|
|
21318
|
+
type: "checkbox"
|
|
21319
|
+
}), /* @__PURE__ */ u("div", { children: [
|
|
21320
|
+
/* @__PURE__ */ u("div", {
|
|
21321
|
+
className: "legacy-review-group-title",
|
|
21322
|
+
children: group.displayProject
|
|
21323
|
+
}),
|
|
21324
|
+
/* @__PURE__ */ u("div", {
|
|
21325
|
+
className: "legacy-review-group-meta",
|
|
21326
|
+
children: [formatIdentitySource(group.identitySource), group.suggestedScopeId ? ` · suggested ${group.suggestedScopeId}` : ""]
|
|
21327
|
+
}),
|
|
21328
|
+
needsProjectCleanup(group) ? /* @__PURE__ */ u("div", {
|
|
21329
|
+
className: "legacy-review-cleanup-note",
|
|
21330
|
+
children: "Unclear project identity. Inspect or correct the project before trusting a domain."
|
|
21331
|
+
}) : null
|
|
21332
|
+
] })]
|
|
21333
|
+
}), /* @__PURE__ */ u("span", {
|
|
21334
|
+
className: "legacy-review-count",
|
|
21335
|
+
children: [
|
|
21336
|
+
group.memoryCount.toLocaleString(),
|
|
21337
|
+
" memor",
|
|
21338
|
+
group.memoryCount === 1 ? "y" : "ies"
|
|
21339
|
+
]
|
|
21340
|
+
})]
|
|
21341
|
+
}),
|
|
21342
|
+
group.suggestionReason ? /* @__PURE__ */ u("div", {
|
|
21343
|
+
className: "legacy-review-reason",
|
|
21344
|
+
children: group.suggestionReason
|
|
21345
|
+
}) : null,
|
|
21346
|
+
pending[group.workspaceIdentity] ? /* @__PURE__ */ u("div", {
|
|
21347
|
+
className: "legacy-review-confirmation",
|
|
21348
|
+
role: "alert",
|
|
21349
|
+
children: [
|
|
21350
|
+
pending[group.workspaceIdentity].warning,
|
|
21351
|
+
" This will reassign",
|
|
21352
|
+
" ",
|
|
21353
|
+
pending[group.workspaceIdentity].reassignable_memory_count.toLocaleString(),
|
|
21354
|
+
" of",
|
|
21355
|
+
" ",
|
|
21356
|
+
pending[group.workspaceIdentity].memory_count.toLocaleString(),
|
|
21357
|
+
" memories",
|
|
21358
|
+
pending[group.workspaceIdentity].skipped_memory_count ? `; ${pending[group.workspaceIdentity].skipped_memory_count.toLocaleString()} peer-owned copies will be left unchanged` : "",
|
|
21359
|
+
"."
|
|
21360
|
+
]
|
|
21361
|
+
}) : null,
|
|
21362
|
+
targetScopes.length > 0 && onReassign ? /* @__PURE__ */ u("label", {
|
|
21363
|
+
className: "legacy-review-target",
|
|
21364
|
+
children: [/* @__PURE__ */ u("span", { children: "Destination Sharing domain" }), /* @__PURE__ */ u("select", {
|
|
21365
|
+
className: "peer-scope-input",
|
|
21366
|
+
disabled: busyIdentity != null,
|
|
21367
|
+
onChange: (event) => updateSelectedScope(group, event.currentTarget.value),
|
|
21368
|
+
value: selectedScopes[group.workspaceIdentity] || group.suggestedScopeId || "",
|
|
21369
|
+
children: targetScopes.map((scope) => /* @__PURE__ */ u("option", {
|
|
21370
|
+
value: scope.scopeId,
|
|
21371
|
+
children: [
|
|
21372
|
+
scope.label,
|
|
21373
|
+
" · ",
|
|
21374
|
+
scope.authorityType,
|
|
21375
|
+
scope.scopeId === group.suggestedScopeId ? " · suggested" : ""
|
|
21376
|
+
]
|
|
21377
|
+
}, scope.scopeId))
|
|
21378
|
+
})]
|
|
21379
|
+
}) : null,
|
|
21380
|
+
targetScopes.length > 0 && onReassign ? /* @__PURE__ */ u("div", {
|
|
21381
|
+
className: "legacy-review-actions",
|
|
21382
|
+
children: /* @__PURE__ */ u("button", {
|
|
21383
|
+
type: "button",
|
|
21384
|
+
className: "settings-button",
|
|
21385
|
+
disabled: busyIdentity != null,
|
|
21386
|
+
onClick: () => void applyGroup(group),
|
|
21387
|
+
children: busyIdentity === group.workspaceIdentity ? "Reassigning…" : pending[group.workspaceIdentity] ? "I understand, reassign memories" : "Preview reassignment"
|
|
21388
|
+
})
|
|
21389
|
+
}) : null
|
|
21390
|
+
]
|
|
21391
|
+
}, group.workspaceIdentity))
|
|
21392
|
+
}) : null
|
|
21393
|
+
]
|
|
20554
21394
|
});
|
|
20555
21395
|
}
|
|
20556
|
-
function SyncSharingReview({ items, legacyReview, onReview }) {
|
|
20557
|
-
return /* @__PURE__ */ u(k$3, { children: [legacyReview ? /* @__PURE__ */ u(LegacySharedReviewRow, {
|
|
21396
|
+
function SyncSharingReview({ items, legacyReview, onLegacyReassign, onLegacyReview, onReview }) {
|
|
21397
|
+
return /* @__PURE__ */ u(k$3, { children: [legacyReview ? /* @__PURE__ */ u(LegacySharedReviewRow, {
|
|
21398
|
+
item: legacyReview,
|
|
21399
|
+
onReassign: onLegacyReassign,
|
|
21400
|
+
onReview: onLegacyReview ?? onReview
|
|
21401
|
+
}) : null, items.map((item) => /* @__PURE__ */ u(SharingReviewRow, {
|
|
20558
21402
|
item,
|
|
20559
21403
|
onReview
|
|
20560
21404
|
}, `${item.peerName}:${item.actorId}:${item.scopeLabel}`))] });
|
|
@@ -20566,6 +21410,26 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20566
21410
|
state.feedQuery = "";
|
|
20567
21411
|
window.location.hash = "feed";
|
|
20568
21412
|
}
|
|
21413
|
+
function openProjectsReview() {
|
|
21414
|
+
window.location.hash = "projects";
|
|
21415
|
+
}
|
|
21416
|
+
async function reassignLegacySharedReviewGroup(group, scopeId, confirmedOldCopies) {
|
|
21417
|
+
try {
|
|
21418
|
+
const result = await reassignLegacySharedReviewGroup$1({
|
|
21419
|
+
confirmed_old_copies: confirmedOldCopies,
|
|
21420
|
+
scope_id: scopeId,
|
|
21421
|
+
workspace_identity: group.workspaceIdentity
|
|
21422
|
+
});
|
|
21423
|
+
state.lastSyncLegacySharedReview = result.legacy_shared_review ?? null;
|
|
21424
|
+
showGlobalNotice(`Reassigned ${result.reassigned_memory_count.toLocaleString()} legacy review memor${result.reassigned_memory_count === 1 ? "y" : "ies"} to ${result.target_scope_label}.`);
|
|
21425
|
+
renderSyncSharingReview();
|
|
21426
|
+
return null;
|
|
21427
|
+
} catch (error) {
|
|
21428
|
+
if (error instanceof LegacySharedReviewConfirmationError) return error.preview;
|
|
21429
|
+
showGlobalNotice(error instanceof Error ? error.message : "Unable to reassign legacy review memories.", "warning");
|
|
21430
|
+
return null;
|
|
21431
|
+
}
|
|
21432
|
+
}
|
|
20569
21433
|
function renderSyncSharingReview() {
|
|
20570
21434
|
const panel = document.getElementById("syncSharingReview");
|
|
20571
21435
|
const meta = document.getElementById("syncSharingReviewMeta");
|
|
@@ -20576,8 +21440,29 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20576
21440
|
const legacyRaw = state.lastSyncLegacySharedReview;
|
|
20577
21441
|
const legacyCount = Math.max(0, Number(legacyRaw?.memory_count ?? 0));
|
|
20578
21442
|
const legacyReview = legacyRaw?.has_data ? {
|
|
21443
|
+
groups: Array.isArray(legacyRaw.groups) ? legacyRaw.groups.map((group) => {
|
|
21444
|
+
const raw = group;
|
|
21445
|
+
return {
|
|
21446
|
+
displayProject: String(raw.display_project || raw.project || "Unknown project"),
|
|
21447
|
+
identitySource: String(raw.identity_source || "unknown"),
|
|
21448
|
+
lastUpdatedAt: typeof raw.last_updated_at === "string" ? raw.last_updated_at : null,
|
|
21449
|
+
memoryCount: Number(raw.memory_count || 0),
|
|
21450
|
+
suggestedScopeId: typeof raw.suggested_scope_id === "string" ? raw.suggested_scope_id : null,
|
|
21451
|
+
suggestionReason: typeof raw.suggestion_reason === "string" ? raw.suggestion_reason : null,
|
|
21452
|
+
workspaceIdentity: String(raw.workspace_identity || raw.display_project || "unknown")
|
|
21453
|
+
};
|
|
21454
|
+
}) : [],
|
|
20579
21455
|
memoryCount: legacyCount,
|
|
20580
|
-
scopeId: String(legacyRaw.scope_id || "legacy-shared-review")
|
|
21456
|
+
scopeId: String(legacyRaw.scope_id || "legacy-shared-review"),
|
|
21457
|
+
totalGroupCount: Number(legacyRaw.total_group_count || legacyRaw.groups?.length || 0),
|
|
21458
|
+
targetScopes: Array.isArray(legacyRaw.target_scopes) ? legacyRaw.target_scopes.map((scope) => {
|
|
21459
|
+
const raw = scope;
|
|
21460
|
+
return {
|
|
21461
|
+
authorityType: String(raw.authority_type || "unknown"),
|
|
21462
|
+
label: String(raw.label || raw.scope_id || "Unknown domain"),
|
|
21463
|
+
scopeId: String(raw.scope_id || "")
|
|
21464
|
+
};
|
|
21465
|
+
}).filter((scope) => scope.scopeId) : []
|
|
20581
21466
|
} : null;
|
|
20582
21467
|
if (!items.length && !legacyReview) {
|
|
20583
21468
|
clearSyncMount(list);
|
|
@@ -20598,6 +21483,8 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20598
21483
|
shareableCount: Number(item.shareable_count || 0)
|
|
20599
21484
|
})),
|
|
20600
21485
|
legacyReview,
|
|
21486
|
+
onLegacyReassign: reassignLegacySharedReviewGroup,
|
|
21487
|
+
onLegacyReview: openProjectsReview,
|
|
20601
21488
|
onReview: openFeedSharingReview
|
|
20602
21489
|
}));
|
|
20603
21490
|
}
|
|
@@ -20783,13 +21670,121 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20783
21670
|
try {
|
|
20784
21671
|
const runtime = await loadRuntimeInfo();
|
|
20785
21672
|
if (!runtime?.version) return;
|
|
20786
|
-
setRuntimeLabel(runtime.version, "
|
|
21673
|
+
setRuntimeLabel(runtime.version, "2ee815b");
|
|
20787
21674
|
} catch {}
|
|
20788
21675
|
}
|
|
20789
21676
|
var lastAnnouncedRefreshState = null;
|
|
20790
21677
|
var RECONNECT_POLL_MS = 1500;
|
|
21678
|
+
var LEGACY_UPGRADE_NOTICE_DISMISSED_KEY = "codemem-legacy-upgrade-notice-dismissed";
|
|
20791
21679
|
var reconnectTimer = null;
|
|
20792
21680
|
var reconnecting = false;
|
|
21681
|
+
var legacyUpgradeNoticeShown = false;
|
|
21682
|
+
var legacyUpgradeNoticePreviousFocus = null;
|
|
21683
|
+
function readNonNegativeCount(value, fallback = 0) {
|
|
21684
|
+
const count = Number(value);
|
|
21685
|
+
return Number.isFinite(count) ? Math.max(0, count) : fallback;
|
|
21686
|
+
}
|
|
21687
|
+
function readLegacyUpgradeReviewSummary(payload) {
|
|
21688
|
+
if (!payload || typeof payload !== "object") return null;
|
|
21689
|
+
const review = payload.legacy_shared_review ?? payload;
|
|
21690
|
+
if (!review || typeof review !== "object") return null;
|
|
21691
|
+
const raw = review;
|
|
21692
|
+
if (raw.has_data !== true) return null;
|
|
21693
|
+
const groups = Array.isArray(raw.groups) ? raw.groups : [];
|
|
21694
|
+
const groupCount = readNonNegativeCount(raw.total_group_count, groups.length);
|
|
21695
|
+
const memoryCount = readNonNegativeCount(raw.memory_count);
|
|
21696
|
+
if (groupCount <= 0 || memoryCount <= 0) return null;
|
|
21697
|
+
return {
|
|
21698
|
+
groupCount,
|
|
21699
|
+
memoryCount
|
|
21700
|
+
};
|
|
21701
|
+
}
|
|
21702
|
+
function isLegacyUpgradeNoticeDismissed() {
|
|
21703
|
+
try {
|
|
21704
|
+
return localStorage.getItem(LEGACY_UPGRADE_NOTICE_DISMISSED_KEY) === "1";
|
|
21705
|
+
} catch {
|
|
21706
|
+
return false;
|
|
21707
|
+
}
|
|
21708
|
+
}
|
|
21709
|
+
function dismissLegacyUpgradeNoticeIfRequested() {
|
|
21710
|
+
if (!document.getElementById("legacyUpgradeDontShow")?.checked) return;
|
|
21711
|
+
try {
|
|
21712
|
+
localStorage.setItem(LEGACY_UPGRADE_NOTICE_DISMISSED_KEY, "1");
|
|
21713
|
+
} catch {}
|
|
21714
|
+
}
|
|
21715
|
+
function legacyUpgradeFocusableElements() {
|
|
21716
|
+
const modal = $("legacyUpgradeModal");
|
|
21717
|
+
if (!modal || modal.hidden) return [];
|
|
21718
|
+
return Array.from(modal.querySelectorAll("a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])")).filter((element) => !element.hidden && element.offsetParent !== null);
|
|
21719
|
+
}
|
|
21720
|
+
function focusLegacyUpgradeModal() {
|
|
21721
|
+
const focusable = legacyUpgradeFocusableElements();
|
|
21722
|
+
const primary = $("legacyUpgradeReviewGroups");
|
|
21723
|
+
(primary && focusable.includes(primary) ? primary : focusable[0])?.focus();
|
|
21724
|
+
}
|
|
21725
|
+
function handleLegacyUpgradeModalKeydown(event) {
|
|
21726
|
+
const modal = $("legacyUpgradeModal");
|
|
21727
|
+
if (!modal || modal.hidden) return;
|
|
21728
|
+
if (event.key === "Escape") {
|
|
21729
|
+
event.preventDefault();
|
|
21730
|
+
dismissLegacyUpgradeNoticeIfRequested();
|
|
21731
|
+
setLegacyUpgradeNotice(false);
|
|
21732
|
+
return;
|
|
21733
|
+
}
|
|
21734
|
+
if (event.key !== "Tab") return;
|
|
21735
|
+
const focusable = legacyUpgradeFocusableElements();
|
|
21736
|
+
if (focusable.length === 0) return;
|
|
21737
|
+
const first = focusable[0];
|
|
21738
|
+
const last = focusable[focusable.length - 1];
|
|
21739
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
21740
|
+
event.preventDefault();
|
|
21741
|
+
last.focus();
|
|
21742
|
+
return;
|
|
21743
|
+
}
|
|
21744
|
+
if (!event.shiftKey && document.activeElement === last) {
|
|
21745
|
+
event.preventDefault();
|
|
21746
|
+
first.focus();
|
|
21747
|
+
}
|
|
21748
|
+
}
|
|
21749
|
+
function setLegacyUpgradeBackgroundInert(inert) {
|
|
21750
|
+
for (const element of document.querySelectorAll("header, .tab-bar, .tab-panel, #toastRoot, #viewerReconnectOverlay, #settingsDialogMount")) {
|
|
21751
|
+
if (element.id === "viewerReconnectOverlay" && element.hidden) continue;
|
|
21752
|
+
element.inert = inert;
|
|
21753
|
+
if (inert) element.setAttribute("aria-hidden", "true");
|
|
21754
|
+
else element.removeAttribute("aria-hidden");
|
|
21755
|
+
}
|
|
21756
|
+
}
|
|
21757
|
+
function setLegacyUpgradeNotice(open, summary) {
|
|
21758
|
+
const overlay = $("legacyUpgradeModalBackdrop");
|
|
21759
|
+
const modal = $("legacyUpgradeModal");
|
|
21760
|
+
const summaryEl = $("legacyUpgradeSummary");
|
|
21761
|
+
if (!overlay || !modal || !summaryEl) return;
|
|
21762
|
+
if (summary) summaryEl.textContent = `${summary.groupCount.toLocaleString()} older ${summary.groupCount === 1 ? "project needs" : "projects need"} a Sharing domain. They contain ${summary.memoryCount.toLocaleString()} older shared memories total; you will review the projects, not individual memories.`;
|
|
21763
|
+
overlay.hidden = !open;
|
|
21764
|
+
modal.hidden = !open;
|
|
21765
|
+
if (open) {
|
|
21766
|
+
legacyUpgradeNoticePreviousFocus = document.activeElement;
|
|
21767
|
+
document.addEventListener("keydown", handleLegacyUpgradeModalKeydown);
|
|
21768
|
+
focusLegacyUpgradeModal();
|
|
21769
|
+
setLegacyUpgradeBackgroundInert(true);
|
|
21770
|
+
return;
|
|
21771
|
+
}
|
|
21772
|
+
setLegacyUpgradeBackgroundInert(false);
|
|
21773
|
+
document.removeEventListener("keydown", handleLegacyUpgradeModalKeydown);
|
|
21774
|
+
if (legacyUpgradeNoticePreviousFocus && document.contains(legacyUpgradeNoticePreviousFocus)) legacyUpgradeNoticePreviousFocus.focus();
|
|
21775
|
+
legacyUpgradeNoticePreviousFocus = null;
|
|
21776
|
+
}
|
|
21777
|
+
function maybeShowLegacyUpgradeNotice(summary) {
|
|
21778
|
+
if (!summary || legacyUpgradeNoticeShown || isLegacyUpgradeNoticeDismissed()) return;
|
|
21779
|
+
legacyUpgradeNoticeShown = true;
|
|
21780
|
+
setLegacyUpgradeNotice(true, summary);
|
|
21781
|
+
}
|
|
21782
|
+
async function checkLegacyUpgradeNotice() {
|
|
21783
|
+
if (legacyUpgradeNoticeShown || isLegacyUpgradeNoticeDismissed()) return;
|
|
21784
|
+
try {
|
|
21785
|
+
maybeShowLegacyUpgradeNotice(readLegacyUpgradeReviewSummary(await loadSyncStatus(false, "", { includeJoinRequests: false })));
|
|
21786
|
+
} catch {}
|
|
21787
|
+
}
|
|
20793
21788
|
function setReconnectOverlay(open, detail) {
|
|
20794
21789
|
const overlay = $("viewerReconnectOverlay");
|
|
20795
21790
|
const detailEl = $("viewerReconnectDetail");
|
|
@@ -20980,10 +21975,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
20980
21975
|
state.lastCoordinatorAdminStatus = null;
|
|
20981
21976
|
}));
|
|
20982
21977
|
if (refreshTab === "feed") promises.push(loadFeedData());
|
|
21978
|
+
if (refreshTab === "projects") promises.push(loadProjectsData());
|
|
20983
21979
|
if (refreshTab === "sync" || refreshTab === "health") promises.push(loadSyncData());
|
|
20984
21980
|
if (refreshTab === "coordinator-admin") promises.push(loadCoordinatorAdminData());
|
|
20985
21981
|
if (state.syncPairingOpen) promises.push(loadPairingData());
|
|
20986
21982
|
await Promise.all(promises);
|
|
21983
|
+
maybeShowLegacyUpgradeNotice(readLegacyUpgradeReviewSummary(state.lastSyncLegacySharedReview));
|
|
20987
21984
|
const nextTab = resolveAccessibleTab(state.activeTab, state.lastCoordinatorAdminStatus);
|
|
20988
21985
|
if (nextTab !== state.activeTab) setActiveTab(nextTab);
|
|
20989
21986
|
renderTabs(state.activeTab);
|
|
@@ -21006,6 +22003,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21006
22003
|
setTheme(getTheme());
|
|
21007
22004
|
initTabs();
|
|
21008
22005
|
initFeedTab();
|
|
22006
|
+
initProjectsTab(() => refresh());
|
|
21009
22007
|
initSyncTab(() => refresh());
|
|
21010
22008
|
initCoordinatorAdminTab();
|
|
21011
22009
|
initSettings(stopPolling, startPolling, () => refresh());
|
|
@@ -21028,7 +22026,23 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
21028
22026
|
} catch {}
|
|
21029
22027
|
if (!reconnecting) setReconnectOverlay(false);
|
|
21030
22028
|
});
|
|
22029
|
+
$("legacyUpgradeReviewGroups")?.addEventListener("click", () => {
|
|
22030
|
+
dismissLegacyUpgradeNoticeIfRequested();
|
|
22031
|
+
setLegacyUpgradeNotice(false);
|
|
22032
|
+
window.location.hash = "sync";
|
|
22033
|
+
setTimeout(() => document.getElementById("syncSharingReview")?.scrollIntoView({ block: "start" }), 120);
|
|
22034
|
+
});
|
|
22035
|
+
$("legacyUpgradeReviewProjects")?.addEventListener("click", () => {
|
|
22036
|
+
dismissLegacyUpgradeNoticeIfRequested();
|
|
22037
|
+
setLegacyUpgradeNotice(false);
|
|
22038
|
+
window.location.hash = "projects";
|
|
22039
|
+
});
|
|
22040
|
+
$("legacyUpgradeNotNow")?.addEventListener("click", () => {
|
|
22041
|
+
dismissLegacyUpgradeNoticeIfRequested();
|
|
22042
|
+
setLegacyUpgradeNotice(false);
|
|
22043
|
+
});
|
|
21031
22044
|
loadRuntimeLabel();
|
|
22045
|
+
checkLegacyUpgradeNotice();
|
|
21032
22046
|
refresh();
|
|
21033
22047
|
startPolling();
|
|
21034
22048
|
//#endregion
|