foundry-design-web-adapter 0.2.0-beta.3 → 0.2.0-beta.5
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/README.md +32 -8
- package/dist/adapter.js +224 -15
- package/dist/diagnostics.d.ts +27 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,27 +4,51 @@ Foundry is a local-first precision design workbench for Codex, Cursor, and Claud
|
|
|
4
4
|
|
|
5
5
|
## Beta installation
|
|
6
6
|
|
|
7
|
-
Foundry is distributed through npm, so testers do not need
|
|
7
|
+
Foundry is distributed through npm, so testers do not need GitHub access.
|
|
8
|
+
|
|
9
|
+
### Recommended: let your coding agent install it
|
|
10
|
+
|
|
11
|
+
Open the project in Codex, Cursor, or Claude Code and paste:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
Install Foundry for this project by running npx foundry-design@beta setup --yes. Check the result and tell me when I need to restart.
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Setup detects the project and active coding agent, installs development-only instrumentation, configures the local agent connection, validates the integration, and records everything it owns for safe updates or removal.
|
|
18
|
+
|
|
19
|
+
Restart the coding agent once, reopen the same project folder, and paste:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
Start Foundry for this project and keep listening for Apply with agent requests.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That is the normal workflow. Foundry starts the project when needed, opens an authenticated local preview, and shows **Agent is ready** before it allows an apply request.
|
|
26
|
+
|
|
27
|
+
### Manual installation
|
|
28
|
+
|
|
29
|
+
From the project you want to inspect, run:
|
|
8
30
|
|
|
9
31
|
```bash
|
|
10
32
|
npx foundry-design@beta setup
|
|
11
33
|
```
|
|
12
34
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Restart the coding agent after setup, then ask:
|
|
35
|
+
Then restart the coding agent, reopen the same project folder, and ask:
|
|
16
36
|
|
|
17
37
|
```text
|
|
18
|
-
Start Foundry for this project.
|
|
38
|
+
Start Foundry for this project and keep listening for Apply with agent requests.
|
|
19
39
|
```
|
|
20
40
|
|
|
21
|
-
|
|
41
|
+
You do not need to run a second terminal command when the agent starts Foundry for you. For a manual-only preview, run `npx foundry-design@beta start`, but Apply with agent still requires a restarted and actively listening coding agent.
|
|
42
|
+
|
|
43
|
+
Update an existing installation with:
|
|
22
44
|
|
|
23
45
|
```bash
|
|
24
|
-
npx foundry-design@beta
|
|
46
|
+
npx foundry-design@beta update
|
|
25
47
|
```
|
|
26
48
|
|
|
27
|
-
Foundry
|
|
49
|
+
The updater refreshes checksum-matched Foundry files, agent connections, and skill bundles. It preserves and reports files changed since setup, validates the project, and rolls back the update if Foundry introduces a TypeScript or lint failure. Restart the coding agent after updating.
|
|
50
|
+
|
|
51
|
+
Remove only Foundry-managed integration with:
|
|
28
52
|
|
|
29
53
|
```bash
|
|
30
54
|
npx foundry-design@beta uninstall
|
package/dist/adapter.js
CHANGED
|
@@ -896,6 +896,25 @@ var FOUNDRY_UI_FOUNDATION_CSS = `
|
|
|
896
896
|
}
|
|
897
897
|
`;
|
|
898
898
|
|
|
899
|
+
// src/diagnostics.ts
|
|
900
|
+
function createSafeDiagnostics(input) {
|
|
901
|
+
return {
|
|
902
|
+
product: "Foundry Design Control",
|
|
903
|
+
protocolVersion: input.protocolVersion ?? "1.1.0",
|
|
904
|
+
interfaceTheme: input.interfaceTheme,
|
|
905
|
+
connection: {
|
|
906
|
+
runtime: input.runtimeConnected ? "connected" : "disconnected",
|
|
907
|
+
agent: input.agentConnected ? "connected" : "disconnected",
|
|
908
|
+
...input.agentConnected && input.agentName ? { agentName: input.agentName } : {}
|
|
909
|
+
},
|
|
910
|
+
workspace: {
|
|
911
|
+
selectedElementCount: Math.max(0, input.selectedCount),
|
|
912
|
+
recordedChangeCount: Math.max(0, input.recordedChangeCount),
|
|
913
|
+
latestApplyState: input.latestApplyState ?? "none"
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
|
|
899
918
|
// src/workspace.ts
|
|
900
919
|
function resolveInterfaceTheme(preference, systemPrefersDark) {
|
|
901
920
|
return preference === "system" ? systemPrefersDark ? "dark" : "light" : preference;
|
|
@@ -1197,6 +1216,8 @@ var PANEL_CSS = `
|
|
|
1197
1216
|
.review-head strong { font-size:12px; }.review-summary { padding:12px 16px 12px;border-bottom:1px solid var(--fdc-line);background:#fcfcfc; }.review-summary strong { display:block;font-size:12px;font-weight:550;letter-spacing:-.01em; }.review-summary span { display:block;margin-top:4px;color:var(--fdc-muted);font-size:12px;line-height:1.45; }.review-summary.attention strong { color:#8b4d3d; }.review-toolbar button { height:28px;padding:0 8px;font-size:12px; }
|
|
1198
1217
|
.onboarding-card { bottom:84px;width:340px;padding:16px;border-radius:12px;box-shadow:0 16px 40px rgb(0 0 0 / 15%); }.onboarding-card-head { gap:8px; }.onboarding-card-head span { width:28px;height:28px;display:grid;place-items:center;color:#a75031;background:#fbf1ed;border-radius:8px; }.onboarding-card-head svg { width:16px;height:16px;color:inherit; }.onboarding-card-head strong { font-size:12px;letter-spacing:-.015em; }.onboarding-card>p { margin:8px 0 16px;font-size:12px;line-height:1.55; }.onboarding-steps { display:grid;grid-template-columns:1fr;gap:1px;border:1px solid var(--fdc-line);border-radius:8px;overflow:hidden;background:var(--fdc-line); }.onboarding-step { display:grid;grid-template-columns:24px 1fr;gap:8px;padding:8px 12px;background:white;text-align:left; }.onboarding-step b { color:#8a8a8a;font-size:8px;font-weight:500;letter-spacing:.04em; }.onboarding-step strong { display:block;font-size:12px;font-weight:550; }.onboarding-step small { display:block;margin-top:4px;color:var(--fdc-muted);font-size:8px;line-height:1.35; }.onboarding-actions { gap:8px;margin-top:12px; }.onboarding-actions button { height:32px;padding:0 12px;border-radius:8px;font-size:12px; }
|
|
1199
1218
|
.onboarding-card,.onboarding-step { color:var(--fdc-ink);background:var(--fdc-surface); }.onboarding-card-head span { color:#e7a68c;background:#36231d; }.onboarding-actions button { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }.onboarding-actions .onboarding-start { color:#141416;background:#f0f1f3;border-color:#f0f1f3; }
|
|
1219
|
+
.status-popover { width:256px; }.status-popover-actions { display:grid;grid-template-columns:1fr 1fr;gap:4px;margin-top:8px; }.status-popover .status-popover-actions button { height:32px;margin:0; }.status-popover .status-popover-actions [data-status-retry] { grid-column:1/-1; }
|
|
1220
|
+
.onboarding-card { width:360px; }.onboarding-card-head>strong { flex:1; }.onboarding-card-head .onboarding-close { width:28px;height:28px;display:grid;place-items:center;padding:0;border:0;border-radius:4px;background:transparent;color:var(--fdc-muted);cursor:pointer; }.onboarding-card-head .onboarding-close:hover { color:var(--fdc-ink);background:var(--fdc-subtle); }.onboarding-card-head .onboarding-close svg { width:12px;height:12px; }.onboarding-steps { gap:4px;border:0;overflow:visible;background:transparent; }.onboarding-step { min-height:40px;grid-template-columns:24px minmax(0,1fr);align-items:center;gap:8px;padding:8px;border:1px solid var(--fdc-line);border-radius:8px; }.onboarding-step b { width:20px;height:20px;display:grid;place-items:center;border:1px solid var(--fdc-line);border-radius:50%;color:var(--fdc-muted);font:500 8px/1 var(--fdc-font);letter-spacing:0; }.onboarding-step b svg { width:12px;height:12px; }.onboarding-step.complete>b { color:#141416;background:#8ae0c2;border-color:#8ae0c2; }.onboarding-step.current { border-color:var(--fdc-signal);box-shadow:0 0 0 4px rgb(59 130 246 / 10%); }
|
|
1200
1221
|
.review-summary { color:var(--fdc-ink);background:var(--fdc-paper); }.empty-state-icon { color:#9ec5ff;background:var(--fdc-signal-soft); }
|
|
1201
1222
|
.component-card,.health-card { color:var(--fdc-ink);background:var(--fdc-surface);border-color:var(--fdc-line); }.component-card.selected { color:var(--fdc-ink);background:var(--fdc-component-soft);border-color:#5c4b9e;box-shadow:0 0 0 4px rgb(155 135 245 / 10%); }.component-variants span,.component-actions button,.component-variants button { color:#c1b5f5;background:var(--fdc-component-soft);border-color:#4a406f; }.health-score::before { background:var(--fdc-surface); }.health-filters button { color:var(--fdc-muted); }.health-filters button:hover,.health-filters button.active { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }.health-card p,.health-evidence { color:var(--fdc-muted); }.mapping-chooser { color:#f0c9a8;background:#35291d;border-color:#665039; }.mapping-chooser>strong,.mapping-option,.mapping-option small { color:#e7c29f; }.review-card.locating { background:var(--fdc-signal-soft); }.baseline-badge { color:#83d8bb;background:#18352c; }
|
|
1202
1223
|
/* Workspace ownership */
|
|
@@ -1264,6 +1285,14 @@ var PANEL_CSS = `
|
|
|
1264
1285
|
.review-overview .review-toolbar { position:static;z-index:auto;flex:none;padding:0;border:0;background:transparent;backdrop-filter:none; }
|
|
1265
1286
|
.review-overview .review-toolbar button { height:28px;padding:0 8px;border-color:transparent;background:transparent;color:var(--fdc-muted);font-size:8px; }
|
|
1266
1287
|
.review-overview .review-toolbar button:hover { color:var(--fdc-ink);background:var(--fdc-elevated);border-color:var(--fdc-line); }
|
|
1288
|
+
.review-agent { display:grid;grid-template-columns:8px minmax(0,1fr) auto;align-items:center;gap:12px;margin:12px 16px 0;padding:12px;border:1px solid var(--fdc-line);border-radius:8px;background:var(--fdc-paper); }
|
|
1289
|
+
.review-agent>i { width:8px;height:8px;border-radius:50%;background:#d16d51; }
|
|
1290
|
+
.review-agent.connected>i { background:#2ca67f;box-shadow:0 0 0 4px rgb(44 166 127 / 12%); }
|
|
1291
|
+
.review-agent strong,.review-agent span { display:block; }
|
|
1292
|
+
.review-agent strong { font-size:12px;font-weight:600; }
|
|
1293
|
+
.review-agent span { margin-top:4px;color:var(--fdc-muted);font-size:12px;line-height:1.45; }
|
|
1294
|
+
.review-agent button { height:32px;padding:0 12px;border:1px solid var(--fdc-line);border-radius:8px;background:var(--fdc-elevated);color:var(--fdc-ink);font-size:12px;cursor:pointer; }
|
|
1295
|
+
.review-agent button:hover { border-color:var(--fdc-muted); }
|
|
1267
1296
|
.change-tray .review-group { border-right:0;border-bottom:1px solid var(--fdc-line); }
|
|
1268
1297
|
.change-tray .review-group-title { position:sticky;top:52px;z-index:2;min-height:40px;padding:0 12px;background:rgb(28 28 31 / 98%); }
|
|
1269
1298
|
.review-group-title .included-count { font-size:8px; }
|
|
@@ -1883,7 +1912,7 @@ function installFoundryInspector(options = {}) {
|
|
|
1883
1912
|
statusPill.setAttribute("aria-label", "Foundry session status");
|
|
1884
1913
|
statusPill.insertAdjacentHTML(
|
|
1885
1914
|
"afterend",
|
|
1886
|
-
'<div class="status-popover" hidden><strong data-status-title>Session connected</strong><span data-status-detail>Changes are stored locally.</span><code data-status-project></code><code data-status-revision></code><button data-status-retry>Check connection</button></div>'
|
|
1915
|
+
'<div class="status-popover" hidden><strong data-status-title>Session connected</strong><span data-status-detail>Changes are stored locally.</span><code data-status-project></code><code data-status-revision></code><div class="status-popover-actions"><button data-status-checklist>Getting started</button><button data-status-diagnostics>Copy diagnostics</button><button data-status-retry>Check connection</button></div></div>'
|
|
1887
1916
|
);
|
|
1888
1917
|
shadow.querySelector(".panel").insertAdjacentHTML(
|
|
1889
1918
|
"beforeend",
|
|
@@ -1893,7 +1922,7 @@ function installFoundryInspector(options = {}) {
|
|
|
1893
1922
|
onboarding.className = "onboarding-card";
|
|
1894
1923
|
onboarding.hidden = true;
|
|
1895
1924
|
onboarding.setAttribute("aria-label", "Getting started with Foundry");
|
|
1896
|
-
onboarding.innerHTML = '<div class="onboarding-card-head"><span><i data-foundry-icon="sparkles"></i></span><strong>
|
|
1925
|
+
onboarding.innerHTML = '<div class="onboarding-card-head"><span><i data-foundry-icon="sparkles"></i></span><strong>Getting started</strong><button class="onboarding-close" aria-label="Close getting started"><i data-foundry-icon="x"></i></button></div><p>Complete one real design loop. Progress stays in this browser and contains no project content.</p><div class="onboarding-steps"></div><div class="onboarding-actions"><button class="onboarding-shortcuts">View shortcuts</button><button class="onboarding-start">Continue</button></div>';
|
|
1897
1926
|
shadow.append(onboarding);
|
|
1898
1927
|
const compareBar = document.createElement("div");
|
|
1899
1928
|
compareBar.className = "compare-bar";
|
|
@@ -2081,6 +2110,7 @@ function installFoundryInspector(options = {}) {
|
|
|
2081
2110
|
let selectedControls = [];
|
|
2082
2111
|
let resizeObserver;
|
|
2083
2112
|
let activeReviewPayload = null;
|
|
2113
|
+
let activeAgentPresence = { connected: false, presence: null };
|
|
2084
2114
|
const reviewDraftKey = `__foundry_review_draft:${sessionId || "local"}`;
|
|
2085
2115
|
let reviewDraft = (() => {
|
|
2086
2116
|
try {
|
|
@@ -2143,10 +2173,84 @@ function installFoundryInspector(options = {}) {
|
|
|
2143
2173
|
const capturedBaselineRuns = /* @__PURE__ */ new Set();
|
|
2144
2174
|
let projectRoot = location.origin;
|
|
2145
2175
|
let projectRevision = "";
|
|
2176
|
+
let runtimeConnected = false;
|
|
2177
|
+
let recordedChangeCount = 0;
|
|
2178
|
+
let latestApplyState = "none";
|
|
2179
|
+
const onboardingCompleted = /* @__PURE__ */ new Set();
|
|
2146
2180
|
let designMemory = emptyDesignMemory();
|
|
2147
2181
|
let matrixMode = false;
|
|
2148
2182
|
let lastUtilityTrigger = null;
|
|
2149
2183
|
const utilityRects = /* @__PURE__ */ new Map();
|
|
2184
|
+
const onboardingSteps = [
|
|
2185
|
+
{
|
|
2186
|
+
id: "setup",
|
|
2187
|
+
title: "Setup complete",
|
|
2188
|
+
detail: "The project adapter and local session are ready."
|
|
2189
|
+
},
|
|
2190
|
+
{
|
|
2191
|
+
id: "agent",
|
|
2192
|
+
title: "Agent restarted and connected",
|
|
2193
|
+
detail: "Keep the coding agent listening while you design."
|
|
2194
|
+
},
|
|
2195
|
+
{
|
|
2196
|
+
id: "selection",
|
|
2197
|
+
title: "First element selected",
|
|
2198
|
+
detail: "Click the canvas or choose an item in Layers."
|
|
2199
|
+
},
|
|
2200
|
+
{
|
|
2201
|
+
id: "change",
|
|
2202
|
+
title: "First change recorded",
|
|
2203
|
+
detail: "Adjust a measured value in the Inspector."
|
|
2204
|
+
},
|
|
2205
|
+
{
|
|
2206
|
+
id: "apply",
|
|
2207
|
+
title: "First batch applied and verified",
|
|
2208
|
+
detail: "Review once, update source, then measure the rebuild."
|
|
2209
|
+
}
|
|
2210
|
+
];
|
|
2211
|
+
function onboardingStorageKey(suffix) {
|
|
2212
|
+
return `__foundry_getting_started:${suffix}:${projectRoot}`;
|
|
2213
|
+
}
|
|
2214
|
+
function loadOnboardingProgress() {
|
|
2215
|
+
onboardingCompleted.clear();
|
|
2216
|
+
try {
|
|
2217
|
+
const stored = JSON.parse(localStorage.getItem(onboardingStorageKey("completed")) ?? "[]");
|
|
2218
|
+
for (const id of stored) {
|
|
2219
|
+
if (onboardingSteps.some((step) => step.id === id)) onboardingCompleted.add(id);
|
|
2220
|
+
}
|
|
2221
|
+
} catch {
|
|
2222
|
+
localStorage.removeItem(onboardingStorageKey("completed"));
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
function completeOnboardingStep(id) {
|
|
2226
|
+
if (onboardingCompleted.has(id)) return;
|
|
2227
|
+
onboardingCompleted.add(id);
|
|
2228
|
+
try {
|
|
2229
|
+
localStorage.setItem(
|
|
2230
|
+
onboardingStorageKey("completed"),
|
|
2231
|
+
JSON.stringify([...onboardingCompleted])
|
|
2232
|
+
);
|
|
2233
|
+
} catch {
|
|
2234
|
+
}
|
|
2235
|
+
renderOnboardingChecklist();
|
|
2236
|
+
}
|
|
2237
|
+
function renderOnboardingChecklist(forceOpen = false) {
|
|
2238
|
+
const nextStep = onboardingSteps.find((step) => !onboardingCompleted.has(step.id));
|
|
2239
|
+
const complete = !nextStep;
|
|
2240
|
+
const stepsRoot = onboarding.querySelector(".onboarding-steps");
|
|
2241
|
+
stepsRoot.innerHTML = onboardingSteps.map((step, index) => {
|
|
2242
|
+
const done = onboardingCompleted.has(step.id);
|
|
2243
|
+
const current = step.id === nextStep?.id;
|
|
2244
|
+
return `<div class="onboarding-step ${done ? "complete" : ""} ${current ? "current" : ""}"><b>${done ? '<i data-foundry-icon="check"></i>' : String(index + 1).padStart(2, "0")}</b><span><strong>${step.title}</strong><small>${step.detail}</small></span></div>`;
|
|
2245
|
+
}).join("");
|
|
2246
|
+
renderIcons(stepsRoot);
|
|
2247
|
+
const continueButton = onboarding.querySelector(".onboarding-start");
|
|
2248
|
+
const action = nextStep?.id ?? "done";
|
|
2249
|
+
continueButton.dataset.action = action;
|
|
2250
|
+
continueButton.textContent = action === "agent" ? "Copy restart prompt" : action === "selection" ? "Select an element" : action === "change" ? "Adjust a value" : action === "apply" ? "Review changes" : "Done";
|
|
2251
|
+
const dismissed = localStorage.getItem(onboardingStorageKey("dismissed")) === "1";
|
|
2252
|
+
onboarding.hidden = complete || !forceOpen && dismissed;
|
|
2253
|
+
}
|
|
2150
2254
|
function utilityStorageKey(utility) {
|
|
2151
2255
|
return `__foundry_utility_rect:${utility}:${projectRoot}`;
|
|
2152
2256
|
}
|
|
@@ -2380,16 +2484,23 @@ function installFoundryInspector(options = {}) {
|
|
|
2380
2484
|
async function hydrateSession() {
|
|
2381
2485
|
if (!sessionId || !token) return;
|
|
2382
2486
|
try {
|
|
2383
|
-
const response = await
|
|
2384
|
-
|
|
2385
|
-
|
|
2487
|
+
const [response, presence] = await Promise.all([
|
|
2488
|
+
fetch(`${runtimeUrl}/v1/sessions/${sessionId}`, {
|
|
2489
|
+
headers: { "x-foundry-token": token }
|
|
2490
|
+
}),
|
|
2491
|
+
sessionRequest("/agent-presence").catch(() => ({ connected: false, presence: null }))
|
|
2492
|
+
]);
|
|
2386
2493
|
if (!response.ok) throw new Error("The local session could not be read.");
|
|
2387
|
-
const
|
|
2494
|
+
const payload = await response.json();
|
|
2495
|
+
const { changeSet, designGraph: graph } = payload;
|
|
2496
|
+
runtimeConnected = true;
|
|
2497
|
+
activeAgentPresence = presence;
|
|
2388
2498
|
statusPopover.querySelector("[data-status-project]").textContent = changeSet.context.projectRoot;
|
|
2389
2499
|
statusPopover.querySelector("[data-status-revision]").textContent = `Revision ${changeSet.context.revision ?? "working tree"}`;
|
|
2390
2500
|
const nextProjectRoot = changeSet.context.projectRoot || location.origin;
|
|
2391
2501
|
if (projectRoot !== nextProjectRoot || !hydratedOnce) {
|
|
2392
2502
|
projectRoot = nextProjectRoot;
|
|
2503
|
+
loadOnboardingProgress();
|
|
2393
2504
|
designMemory = readDesignMemory(localStorage, projectRoot);
|
|
2394
2505
|
utilityRects.clear();
|
|
2395
2506
|
positionWorkspaceSurfaces();
|
|
@@ -2402,15 +2513,23 @@ function installFoundryInspector(options = {}) {
|
|
|
2402
2513
|
const activeChanges = changeSet.changes.filter(
|
|
2403
2514
|
(change) => change.status !== "rejected" && String(change.before) !== String(change.after)
|
|
2404
2515
|
);
|
|
2516
|
+
recordedChangeCount = activeChanges.length;
|
|
2517
|
+
latestApplyState = payload.applyRuns?.at(-1)?.state ?? "none";
|
|
2405
2518
|
updateChangeCount(activeChanges.length, activeChanges.at(-1));
|
|
2519
|
+
completeOnboardingStep("setup");
|
|
2520
|
+
if (activeAgentPresence.connected) completeOnboardingStep("agent");
|
|
2521
|
+
if (selectedElements.length) completeOnboardingStep("selection");
|
|
2522
|
+
if (activeChanges.length) completeOnboardingStep("change");
|
|
2523
|
+
if (latestApplyState === "passed") completeOnboardingStep("apply");
|
|
2524
|
+
renderOnboardingChecklist();
|
|
2406
2525
|
setSessionStatus("live");
|
|
2407
2526
|
if (!libraryPanel.hidden) renderDesignMemory();
|
|
2408
2527
|
if (changeSet.changes.length === 0 && !hydratedOnce) {
|
|
2409
2528
|
showToast("Click any element. Shift-click builds a selection.");
|
|
2410
|
-
if (!localStorage.getItem("__foundry_onboarded")) onboarding.hidden = false;
|
|
2411
2529
|
}
|
|
2412
2530
|
hydratedOnce = true;
|
|
2413
2531
|
} catch (error) {
|
|
2532
|
+
runtimeConnected = false;
|
|
2414
2533
|
setSessionStatus(
|
|
2415
2534
|
"error",
|
|
2416
2535
|
error instanceof Error ? error.message : "The local session could not be read."
|
|
@@ -3394,8 +3513,29 @@ function installFoundryInspector(options = {}) {
|
|
|
3394
3513
|
const count = group.querySelector(".included-count");
|
|
3395
3514
|
if (count) count.textContent = `${included} included`;
|
|
3396
3515
|
});
|
|
3397
|
-
applyButton.textContent = selectedCount ? `Apply ${selectedCount} with agent` : "Apply with agent";
|
|
3398
|
-
applyButton.disabled = selectedCount === 0;
|
|
3516
|
+
applyButton.textContent = !activeAgentPresence.connected ? "Connect agent to apply" : selectedCount ? `Apply ${selectedCount} with agent` : "Apply with agent";
|
|
3517
|
+
applyButton.disabled = selectedCount === 0 || !activeAgentPresence.connected;
|
|
3518
|
+
}
|
|
3519
|
+
function agentConnectionMarkup() {
|
|
3520
|
+
const agentName = activeAgentPresence.presence?.agent?.name;
|
|
3521
|
+
return `<div class="review-agent ${activeAgentPresence.connected ? "connected" : "disconnected"}" aria-live="polite"><i></i><div><strong>${activeAgentPresence.connected ? `${escapeHtml(agentName ?? "Coding agent")} is ready` : "Connect your coding agent"}</strong><span>${activeAgentPresence.connected ? "Apply requests will be claimed automatically while this agent keeps listening." : "In the same project, ask your agent to start Foundry and keep listening for Apply requests."}</span></div>${activeAgentPresence.connected ? "" : "<button data-copy-agent-listener>Copy instruction</button>"}</div>`;
|
|
3522
|
+
}
|
|
3523
|
+
function updateAgentConnection() {
|
|
3524
|
+
const current = reviewBody.querySelector(".review-agent");
|
|
3525
|
+
if (!current) {
|
|
3526
|
+
updateReviewSelection();
|
|
3527
|
+
return;
|
|
3528
|
+
}
|
|
3529
|
+
const replacement = document.createElement("div");
|
|
3530
|
+
replacement.innerHTML = agentConnectionMarkup();
|
|
3531
|
+
current.replaceWith(replacement.firstElementChild);
|
|
3532
|
+
reviewBody.querySelector("[data-copy-agent-listener]")?.addEventListener("click", () => {
|
|
3533
|
+
void navigator.clipboard.writeText(
|
|
3534
|
+
"Start Foundry for this project and keep listening for Apply with agent requests."
|
|
3535
|
+
);
|
|
3536
|
+
showToast("Agent instruction copied");
|
|
3537
|
+
});
|
|
3538
|
+
updateReviewSelection();
|
|
3399
3539
|
}
|
|
3400
3540
|
function renderReviewList(changes) {
|
|
3401
3541
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -3409,7 +3549,7 @@ function installFoundryInspector(options = {}) {
|
|
|
3409
3549
|
const unresolvedCount = changes.filter(
|
|
3410
3550
|
(change) => change.confidence === "unresolved" || change.mappingCandidates?.length > 1 && !change.selectedMappingId
|
|
3411
3551
|
).length;
|
|
3412
|
-
reviewBody.innerHTML = changes.length || rejectedCount ? `<div class="review-overview"><div class="review-summary" data-unresolved="${unresolvedCount}" aria-live="polite"><strong>Preparing review\u2026</strong><span>Only included, exactly mapped changes will be sent to your agent.</span></div><div class="review-toolbar"><button data-review-compare>Compare</button><button data-review-approve-exact>Include all</button><button data-review-toggle-rejected>${reviewShowRejected ? "Hide removed" : rejectedCount ? `${rejectedCount} removed` : "Removed"}</button></div></div>${[
|
|
3552
|
+
reviewBody.innerHTML = changes.length || rejectedCount ? `<div class="review-overview"><div class="review-summary" data-unresolved="${unresolvedCount}" aria-live="polite"><strong>Preparing review\u2026</strong><span>Only included, exactly mapped changes will be sent to your agent.</span></div><div class="review-toolbar"><button data-review-compare>Compare</button><button data-review-approve-exact>Include all</button><button data-review-toggle-rejected>${reviewShowRejected ? "Hide removed" : rejectedCount ? `${rejectedCount} removed` : "Removed"}</button></div></div>${agentConnectionMarkup()}${[
|
|
3413
3553
|
...groups.values()
|
|
3414
3554
|
].map((group) => {
|
|
3415
3555
|
const target = group[0].target;
|
|
@@ -3443,6 +3583,12 @@ function installFoundryInspector(options = {}) {
|
|
|
3443
3583
|
reviewCancel.dataset.action = "back";
|
|
3444
3584
|
reviewCancel.textContent = "Back";
|
|
3445
3585
|
renderIcons(reviewBody);
|
|
3586
|
+
reviewBody.querySelector("[data-copy-agent-listener]")?.addEventListener("click", () => {
|
|
3587
|
+
void navigator.clipboard.writeText(
|
|
3588
|
+
"Start Foundry for this project and keep listening for Apply with agent requests."
|
|
3589
|
+
);
|
|
3590
|
+
showToast("Agent instruction copied");
|
|
3591
|
+
});
|
|
3446
3592
|
reviewBody.querySelectorAll("[data-review-change]").forEach(
|
|
3447
3593
|
(field) => field.addEventListener("change", () => {
|
|
3448
3594
|
reviewDraft.selections[field.dataset.reviewChange] = field.checked;
|
|
@@ -3603,6 +3749,8 @@ function installFoundryInspector(options = {}) {
|
|
|
3603
3749
|
const passed = run.state === "passed";
|
|
3604
3750
|
const active = ["queued", "claimed", "applying", "rebuilding", "verifying"].includes(run.state);
|
|
3605
3751
|
const latestMessage = run.state === "queued" ? "The reviewed changes are queued. Keep your coding agent active, or ask it to apply your reviewed Foundry changes." : run.messages.at(-1)?.message ?? run.error ?? "Apply run created.";
|
|
3752
|
+
latestApplyState = run.state;
|
|
3753
|
+
if (run.state === "passed") completeOnboardingStep("apply");
|
|
3606
3754
|
captureVerifiedRun(run);
|
|
3607
3755
|
reviewCount.textContent = `Attempt ${run.attempts}`;
|
|
3608
3756
|
reviewBody.innerHTML = `<div class="run-summary"><div class="run-state"><i class="${passed ? "passed" : attention ? "attention" : active ? "active" : ""}"></i><strong>${escapeHtml(runStateLabels[run.state] ?? run.state)}</strong></div><p>${escapeHtml(latestMessage)}</p></div><div class="run-steps">${run.messages.map(
|
|
@@ -3668,7 +3816,12 @@ function installFoundryInspector(options = {}) {
|
|
|
3668
3816
|
async function refreshReview() {
|
|
3669
3817
|
if (!workspaceState.reviewOpen || !sessionId || !token) return;
|
|
3670
3818
|
try {
|
|
3671
|
-
const payload = await
|
|
3819
|
+
const [payload, presence] = await Promise.all([
|
|
3820
|
+
sessionRequest(),
|
|
3821
|
+
sessionRequest("/agent-presence")
|
|
3822
|
+
]);
|
|
3823
|
+
activeAgentPresence = presence;
|
|
3824
|
+
if (presence.connected) completeOnboardingStep("agent");
|
|
3672
3825
|
const latestRun = payload.applyRuns?.at(-1);
|
|
3673
3826
|
const runNeedsRefresh = latestRun && [
|
|
3674
3827
|
"queued",
|
|
@@ -3681,6 +3834,7 @@ function installFoundryInspector(options = {}) {
|
|
|
3681
3834
|
].includes(latestRun.state);
|
|
3682
3835
|
if (applyButton.dataset.action === "apply" && !runNeedsRefresh) {
|
|
3683
3836
|
activeReviewPayload = payload;
|
|
3837
|
+
updateAgentConnection();
|
|
3684
3838
|
return;
|
|
3685
3839
|
}
|
|
3686
3840
|
renderReviewPayload(payload);
|
|
@@ -3702,7 +3856,13 @@ function installFoundryInspector(options = {}) {
|
|
|
3702
3856
|
workspaceState = updateWorkspace(workspaceState, { type: "set-review", open: true });
|
|
3703
3857
|
reviewTakeover.hidden = false;
|
|
3704
3858
|
outline.hidden = true;
|
|
3705
|
-
|
|
3859
|
+
const [payload, presence] = await Promise.all([
|
|
3860
|
+
sessionRequest(),
|
|
3861
|
+
sessionRequest("/agent-presence")
|
|
3862
|
+
]);
|
|
3863
|
+
activeAgentPresence = presence;
|
|
3864
|
+
if (presence.connected) completeOnboardingStep("agent");
|
|
3865
|
+
renderReviewPayload(payload);
|
|
3706
3866
|
requestAnimationFrame(() => {
|
|
3707
3867
|
reviewBody.scrollTop = reviewScrollTop;
|
|
3708
3868
|
shadow.querySelector(".review-back")?.focus();
|
|
@@ -3743,6 +3903,12 @@ function installFoundryInspector(options = {}) {
|
|
|
3743
3903
|
);
|
|
3744
3904
|
applyButton.disabled = true;
|
|
3745
3905
|
try {
|
|
3906
|
+
activeAgentPresence = await sessionRequest("/agent-presence");
|
|
3907
|
+
if (!activeAgentPresence.connected) {
|
|
3908
|
+
updateAgentConnection();
|
|
3909
|
+
showToast("No coding agent is listening yet");
|
|
3910
|
+
return;
|
|
3911
|
+
}
|
|
3746
3912
|
const payload = await sessionRequest("/apply-runs", {
|
|
3747
3913
|
method: "POST",
|
|
3748
3914
|
body: JSON.stringify({
|
|
@@ -3997,6 +4163,8 @@ function installFoundryInspector(options = {}) {
|
|
|
3997
4163
|
const activeChanges = responsePayload.changeSet.changes.filter(
|
|
3998
4164
|
(change) => change.status !== "rejected" && String(change.before) !== String(change.after)
|
|
3999
4165
|
);
|
|
4166
|
+
recordedChangeCount = activeChanges.length;
|
|
4167
|
+
completeOnboardingStep("change");
|
|
4000
4168
|
lastRecordedSummary = `${target.label} \xB7 ${control.label} ${before}${control.unit ?? ""} \u2192 ${after}${control.unit ?? ""}`;
|
|
4001
4169
|
updateChangeCount(activeChanges.length, activeChanges.at(-1));
|
|
4002
4170
|
showToast(ambiguous ? "Choose the source intent in review" : "Change recorded");
|
|
@@ -5433,6 +5601,7 @@ function installFoundryInspector(options = {}) {
|
|
|
5433
5601
|
selectedElements = [element];
|
|
5434
5602
|
selected = element;
|
|
5435
5603
|
}
|
|
5604
|
+
completeOnboardingStep("selection");
|
|
5436
5605
|
if (!sectionPreferenceTouched) {
|
|
5437
5606
|
const categoryKeys = [
|
|
5438
5607
|
"position",
|
|
@@ -6033,6 +6202,25 @@ function installFoundryInspector(options = {}) {
|
|
|
6033
6202
|
}
|
|
6034
6203
|
});
|
|
6035
6204
|
statusPopover.querySelector("[data-status-retry]").addEventListener("click", () => void hydrateSession());
|
|
6205
|
+
statusPopover.querySelector("[data-status-checklist]").addEventListener("click", () => {
|
|
6206
|
+
localStorage.removeItem(onboardingStorageKey("dismissed"));
|
|
6207
|
+
renderOnboardingChecklist(true);
|
|
6208
|
+
statusPopover.hidden = true;
|
|
6209
|
+
sessionStatus.setAttribute("aria-expanded", "false");
|
|
6210
|
+
});
|
|
6211
|
+
statusPopover.querySelector("[data-status-diagnostics]").addEventListener("click", () => {
|
|
6212
|
+
const diagnostics = createSafeDiagnostics({
|
|
6213
|
+
interfaceTheme: resolvedInterfaceTheme(),
|
|
6214
|
+
runtimeConnected,
|
|
6215
|
+
agentConnected: activeAgentPresence.connected,
|
|
6216
|
+
agentName: activeAgentPresence.presence?.agent?.name,
|
|
6217
|
+
selectedCount: selectedElements.length,
|
|
6218
|
+
recordedChangeCount,
|
|
6219
|
+
latestApplyState
|
|
6220
|
+
});
|
|
6221
|
+
void navigator.clipboard.writeText(JSON.stringify(diagnostics, null, 2));
|
|
6222
|
+
showToast("Privacy-safe diagnostics copied");
|
|
6223
|
+
});
|
|
6036
6224
|
interfaceThemeTrigger.addEventListener("click", () => {
|
|
6037
6225
|
if (interfaceThemeMenu.hidden) openInterfaceThemeMenu();
|
|
6038
6226
|
else closeInterfaceThemeMenu();
|
|
@@ -6045,15 +6233,36 @@ function installFoundryInspector(options = {}) {
|
|
|
6045
6233
|
);
|
|
6046
6234
|
changeDock.querySelector(".dock-undo")?.addEventListener("click", () => void replayHistory(-1));
|
|
6047
6235
|
changeDock.querySelector(".dock-review")?.addEventListener("click", () => void openReview());
|
|
6236
|
+
onboarding.querySelector(".onboarding-close")?.addEventListener("click", () => {
|
|
6237
|
+
localStorage.setItem(onboardingStorageKey("dismissed"), "1");
|
|
6238
|
+
onboarding.hidden = true;
|
|
6239
|
+
});
|
|
6048
6240
|
onboarding.querySelector(".onboarding-start")?.addEventListener("click", () => {
|
|
6049
|
-
|
|
6241
|
+
const action = onboarding.querySelector(".onboarding-start")?.dataset.action;
|
|
6242
|
+
if (action === "agent") {
|
|
6243
|
+
void navigator.clipboard.writeText(
|
|
6244
|
+
"Start Foundry for this project and keep listening for Apply with agent requests."
|
|
6245
|
+
);
|
|
6246
|
+
showToast("Restart prompt copied");
|
|
6247
|
+
return;
|
|
6248
|
+
}
|
|
6249
|
+
if (action === "apply" && recordedChangeCount > 0) {
|
|
6250
|
+
onboarding.hidden = true;
|
|
6251
|
+
void openReview();
|
|
6252
|
+
return;
|
|
6253
|
+
}
|
|
6254
|
+
if (action === "done") {
|
|
6255
|
+
onboarding.hidden = true;
|
|
6256
|
+
return;
|
|
6257
|
+
}
|
|
6050
6258
|
onboarding.hidden = true;
|
|
6051
6259
|
inspecting = true;
|
|
6052
6260
|
updateInspectionMode();
|
|
6053
|
-
showToast(
|
|
6261
|
+
showToast(
|
|
6262
|
+
action === "change" ? "Adjust any Inspector value to record a change" : "Select mode stays on \xB7 click any element to begin"
|
|
6263
|
+
);
|
|
6054
6264
|
});
|
|
6055
6265
|
onboarding.querySelector(".onboarding-shortcuts")?.addEventListener("click", () => {
|
|
6056
|
-
localStorage.setItem("__foundry_onboarded", "1");
|
|
6057
6266
|
onboarding.hidden = true;
|
|
6058
6267
|
openCommands();
|
|
6059
6268
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface SafeDiagnosticsInput {
|
|
2
|
+
interfaceTheme: 'light' | 'dark';
|
|
3
|
+
runtimeConnected: boolean;
|
|
4
|
+
agentConnected: boolean;
|
|
5
|
+
agentName?: string;
|
|
6
|
+
selectedCount: number;
|
|
7
|
+
recordedChangeCount: number;
|
|
8
|
+
latestApplyState?: string;
|
|
9
|
+
protocolVersion?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Build useful support state without exposing project content or session credentials. */
|
|
12
|
+
export declare function createSafeDiagnostics(input: SafeDiagnosticsInput): {
|
|
13
|
+
product: string;
|
|
14
|
+
protocolVersion: string;
|
|
15
|
+
interfaceTheme: "light" | "dark";
|
|
16
|
+
connection: {
|
|
17
|
+
agentName?: string | undefined;
|
|
18
|
+
runtime: string;
|
|
19
|
+
agent: string;
|
|
20
|
+
};
|
|
21
|
+
workspace: {
|
|
22
|
+
selectedElementCount: number;
|
|
23
|
+
recordedChangeCount: number;
|
|
24
|
+
latestApplyState: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,OAAO,GAAG,MAAM,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,0FAA0F;AAC1F,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB;;;;;;;;;;;;;;EAgBhE"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA8DA,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,IAAI,IAAI,CAAC;IAChB,cAAc,IAAI,IAAI,CAAC;IACvB,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IACnC,OAAO,IAAI,IAAI,CAAC;CACjB;AAu/BD,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,uBAA4B,GACpC,0BAA0B,CAg9K5B"}
|