foundry-design-web-adapter 0.2.0-beta.4 → 0.2.0-beta.6

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 CHANGED
@@ -6,30 +6,40 @@ Foundry is a local-first precision design workbench for Codex, Cursor, and Claud
6
6
 
7
7
  Foundry is distributed through npm, so testers do not need GitHub access.
8
8
 
9
- ### Recommended: let your coding agent install it
9
+ > **Current public beta:** `0.2.0-beta.6`. Install Foundry with the `@beta` tag. The unqualified npm `latest` tag still points to an earlier beta and is not the current testing channel.
10
10
 
11
- Open the project in Codex, Cursor, or Claude Code and paste:
11
+ Full documentation is available at [withfoundry.ai](https://withfoundry.ai).
12
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.
13
+ ### Recommended: one command
14
+
15
+ Open a terminal in the project, or ask Codex, Cursor, or Claude Code to run:
16
+
17
+ ```bash
18
+ npx foundry-design@beta
15
19
  ```
16
20
 
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.
21
+ That command detects the project and active coding agent, installs or safely updates Foundry, repairs the shared agent connection, validates its integration, starts the project when needed, and opens the visual session. On the first installation only, restart the coding agent once so it can load the shared MCP connection. A reviewed batch can be queued before the restart and will be claimed when the agent reconnects.
18
22
 
19
- Restart the coding agent once, reopen the same project folder, and paste:
23
+ After the restart, reopen the same project folder and ask:
20
24
 
21
25
  ```text
22
26
  Start Foundry for this project and keep listening for Apply with agent requests.
23
27
  ```
24
28
 
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.
29
+ That is the normal workflow. Foundry resumes the most recent session for the current source revision and keeps all review and apply state local.
26
30
 
27
31
  ### Manual installation
28
32
 
29
- From the project you want to inspect, run:
33
+ From the first project you want to inspect, run:
34
+
35
+ ```bash
36
+ npx foundry-design@beta setup --global
37
+ ```
38
+
39
+ For later projects, keep the shared agent connection and install only the development adapter:
30
40
 
31
41
  ```bash
32
- npx foundry-design@beta setup
42
+ npx foundry-design@beta setup --agent none
33
43
  ```
34
44
 
35
45
  Then restart the coding agent, reopen the same project folder, and ask:
@@ -38,7 +48,7 @@ Then restart the coding agent, reopen the same project folder, and ask:
38
48
  Start Foundry for this project and keep listening for Apply with agent requests.
39
49
  ```
40
50
 
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.
51
+ You do not need to run a second terminal command when the agent starts Foundry for you. Apply requests remain queued safely when the agent is offline. Run `npx foundry-design@beta doctor --repair` if the connection or generated integration needs repair.
42
52
 
43
53
  Update an existing installation with:
44
54
 
@@ -56,13 +66,13 @@ npx foundry-design@beta uninstall
56
66
 
57
67
  Supported automatic web integration currently includes Next.js App Router, Vite, and plain HTML. Generic web, SwiftUI, and React Native projects receive explicit setup guidance when a safe automatic edit is not available.
58
68
 
59
- The repository also contains a portable plugin bundle for future marketplace distribution. The npm setup above is the public beta installation path.
69
+ The npm setup above is the public beta installation path and includes the same portable skill and MCP connection used by the agent plugin bundle.
60
70
 
61
- The beta supports Node.js 20 or newer. Read [Privacy](PRIVACY.md), [Security](SECURITY.md), and the [beta changelog](CHANGELOG.md) before using it with sensitive work.
71
+ The beta supports Node.js 20 or newer. Read the [local-first safety model](https://withfoundry.ai/#safety) before using it with sensitive work.
62
72
 
63
73
  ## Agent plugin
64
74
 
65
- The portable plugin lives at `plugins/foundry-design-control`. The repository also includes marketplace manifests for Codex, Cursor, and Claude-compatible plugin import. Once installed, ask your agent:
75
+ The portable plugin lives at `plugins/foundry-design-control`. The repository also includes marketplace manifests for Codex, Cursor, and Claude-compatible plugin import. Until public marketplace review is complete, `setup --global` installs the same reusable skill and MCP connection from npm without requiring source-repository access. Once installed, ask your agent:
66
76
 
67
77
  ```text
68
78
  Start Foundry for this project.
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],.status-popover .status-popover-actions [data-status-repair] { 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 */
@@ -1891,7 +1912,7 @@ function installFoundryInspector(options = {}) {
1891
1912
  statusPill.setAttribute("aria-label", "Foundry session status");
1892
1913
  statusPill.insertAdjacentHTML(
1893
1914
  "afterend",
1894
- '<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><button data-status-repair>Copy repair command</button></div></div>'
1895
1916
  );
1896
1917
  shadow.querySelector(".panel").insertAdjacentHTML(
1897
1918
  "beforeend",
@@ -1901,7 +1922,7 @@ function installFoundryInspector(options = {}) {
1901
1922
  onboarding.className = "onboarding-card";
1902
1923
  onboarding.hidden = true;
1903
1924
  onboarding.setAttribute("aria-label", "Getting started with Foundry");
1904
- onboarding.innerHTML = '<div class="onboarding-card-head"><span><i data-foundry-icon="sparkles"></i></span><strong>Start with the interface</strong></div><p>Foundry stays in Select mode while you work. Click anything, refine measured values, then apply one reviewed batch.</p><div class="onboarding-steps"><div class="onboarding-step"><b>01</b><span><strong>Select</strong><small>Click the page or choose a layer.</small></span></div><div class="onboarding-step"><b>02</b><span><strong>Refine</strong><small>Adjust real layout, type, color, and motion.</small></span></div><div class="onboarding-step"><b>03</b><span><strong>Apply</strong><small>Review once, update source, and verify.</small></span></div></div><div class="onboarding-actions"><button class="onboarding-shortcuts">View shortcuts</button><button class="onboarding-start">Select an element</button></div>';
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>';
1905
1926
  shadow.append(onboarding);
1906
1927
  const compareBar = document.createElement("div");
1907
1928
  compareBar.className = "compare-bar";
@@ -2152,10 +2173,84 @@ function installFoundryInspector(options = {}) {
2152
2173
  const capturedBaselineRuns = /* @__PURE__ */ new Set();
2153
2174
  let projectRoot = location.origin;
2154
2175
  let projectRevision = "";
2176
+ let runtimeConnected = false;
2177
+ let recordedChangeCount = 0;
2178
+ let latestApplyState = "none";
2179
+ const onboardingCompleted = /* @__PURE__ */ new Set();
2155
2180
  let designMemory = emptyDesignMemory();
2156
2181
  let matrixMode = false;
2157
2182
  let lastUtilityTrigger = null;
2158
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 connection",
2193
+ detail: "Keep it listening while you design. Offline batches wait safely."
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
+ }
2159
2254
  function utilityStorageKey(utility) {
2160
2255
  return `__foundry_utility_rect:${utility}:${projectRoot}`;
2161
2256
  }
@@ -2389,16 +2484,23 @@ function installFoundryInspector(options = {}) {
2389
2484
  async function hydrateSession() {
2390
2485
  if (!sessionId || !token) return;
2391
2486
  try {
2392
- const response = await fetch(`${runtimeUrl}/v1/sessions/${sessionId}`, {
2393
- headers: { "x-foundry-token": token }
2394
- });
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
+ ]);
2395
2493
  if (!response.ok) throw new Error("The local session could not be read.");
2396
- const { changeSet, designGraph: graph } = await response.json();
2494
+ const payload = await response.json();
2495
+ const { changeSet, designGraph: graph } = payload;
2496
+ runtimeConnected = true;
2497
+ activeAgentPresence = presence;
2397
2498
  statusPopover.querySelector("[data-status-project]").textContent = changeSet.context.projectRoot;
2398
2499
  statusPopover.querySelector("[data-status-revision]").textContent = `Revision ${changeSet.context.revision ?? "working tree"}`;
2399
2500
  const nextProjectRoot = changeSet.context.projectRoot || location.origin;
2400
2501
  if (projectRoot !== nextProjectRoot || !hydratedOnce) {
2401
2502
  projectRoot = nextProjectRoot;
2503
+ loadOnboardingProgress();
2402
2504
  designMemory = readDesignMemory(localStorage, projectRoot);
2403
2505
  utilityRects.clear();
2404
2506
  positionWorkspaceSurfaces();
@@ -2411,15 +2513,23 @@ function installFoundryInspector(options = {}) {
2411
2513
  const activeChanges = changeSet.changes.filter(
2412
2514
  (change) => change.status !== "rejected" && String(change.before) !== String(change.after)
2413
2515
  );
2516
+ recordedChangeCount = activeChanges.length;
2517
+ latestApplyState = payload.applyRuns?.at(-1)?.state ?? "none";
2414
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();
2415
2525
  setSessionStatus("live");
2416
2526
  if (!libraryPanel.hidden) renderDesignMemory();
2417
2527
  if (changeSet.changes.length === 0 && !hydratedOnce) {
2418
2528
  showToast("Click any element. Shift-click builds a selection.");
2419
- if (!localStorage.getItem("__foundry_onboarded")) onboarding.hidden = false;
2420
2529
  }
2421
2530
  hydratedOnce = true;
2422
2531
  } catch (error) {
2532
+ runtimeConnected = false;
2423
2533
  setSessionStatus(
2424
2534
  "error",
2425
2535
  error instanceof Error ? error.message : "The local session could not be read."
@@ -3403,12 +3513,12 @@ function installFoundryInspector(options = {}) {
3403
3513
  const count = group.querySelector(".included-count");
3404
3514
  if (count) count.textContent = `${included} included`;
3405
3515
  });
3406
- applyButton.textContent = !activeAgentPresence.connected ? "Connect agent to apply" : selectedCount ? `Apply ${selectedCount} with agent` : "Apply with agent";
3407
- applyButton.disabled = selectedCount === 0 || !activeAgentPresence.connected;
3516
+ applyButton.textContent = selectedCount ? activeAgentPresence.connected ? `Apply ${selectedCount} with agent` : `Queue ${selectedCount} for agent` : "Apply with agent";
3517
+ applyButton.disabled = selectedCount === 0;
3408
3518
  }
3409
3519
  function agentConnectionMarkup() {
3410
3520
  const agentName = activeAgentPresence.presence?.agent?.name;
3411
- 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>`;
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` : "Agent currently offline"}</strong><span>${activeAgentPresence.connected ? "Apply requests will be claimed automatically while this agent keeps listening." : "You can queue this batch now. Codex, Cursor, or Claude Code will claim it when the Foundry listener reconnects."}</span></div>${activeAgentPresence.connected ? "" : "<button data-copy-agent-listener>Copy reconnect instruction</button>"}</div>`;
3412
3522
  }
3413
3523
  function updateAgentConnection() {
3414
3524
  const current = reviewBody.querySelector(".review-agent");
@@ -3639,6 +3749,8 @@ function installFoundryInspector(options = {}) {
3639
3749
  const passed = run.state === "passed";
3640
3750
  const active = ["queued", "claimed", "applying", "rebuilding", "verifying"].includes(run.state);
3641
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");
3642
3754
  captureVerifiedRun(run);
3643
3755
  reviewCount.textContent = `Attempt ${run.attempts}`;
3644
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(
@@ -3709,6 +3821,7 @@ function installFoundryInspector(options = {}) {
3709
3821
  sessionRequest("/agent-presence")
3710
3822
  ]);
3711
3823
  activeAgentPresence = presence;
3824
+ if (presence.connected) completeOnboardingStep("agent");
3712
3825
  const latestRun = payload.applyRuns?.at(-1);
3713
3826
  const runNeedsRefresh = latestRun && [
3714
3827
  "queued",
@@ -3748,6 +3861,7 @@ function installFoundryInspector(options = {}) {
3748
3861
  sessionRequest("/agent-presence")
3749
3862
  ]);
3750
3863
  activeAgentPresence = presence;
3864
+ if (presence.connected) completeOnboardingStep("agent");
3751
3865
  renderReviewPayload(payload);
3752
3866
  requestAnimationFrame(() => {
3753
3867
  reviewBody.scrollTop = reviewScrollTop;
@@ -3790,11 +3904,6 @@ function installFoundryInspector(options = {}) {
3790
3904
  applyButton.disabled = true;
3791
3905
  try {
3792
3906
  activeAgentPresence = await sessionRequest("/agent-presence");
3793
- if (!activeAgentPresence.connected) {
3794
- updateAgentConnection();
3795
- showToast("No coding agent is listening yet");
3796
- return;
3797
- }
3798
3907
  const payload = await sessionRequest("/apply-runs", {
3799
3908
  method: "POST",
3800
3909
  body: JSON.stringify({
@@ -3804,6 +3913,8 @@ function installFoundryInspector(options = {}) {
3804
3913
  });
3805
3914
  clearReviewDraft();
3806
3915
  renderReviewPayload(payload);
3916
+ if (!activeAgentPresence.connected)
3917
+ showToast("Queued. Your coding agent will claim this batch when it reconnects.");
3807
3918
  } catch (error) {
3808
3919
  showToast(error instanceof Error ? error.message : "Could not start apply run");
3809
3920
  updateReviewSelection();
@@ -4049,6 +4160,8 @@ function installFoundryInspector(options = {}) {
4049
4160
  const activeChanges = responsePayload.changeSet.changes.filter(
4050
4161
  (change) => change.status !== "rejected" && String(change.before) !== String(change.after)
4051
4162
  );
4163
+ recordedChangeCount = activeChanges.length;
4164
+ completeOnboardingStep("change");
4052
4165
  lastRecordedSummary = `${target.label} \xB7 ${control.label} ${before}${control.unit ?? ""} \u2192 ${after}${control.unit ?? ""}`;
4053
4166
  updateChangeCount(activeChanges.length, activeChanges.at(-1));
4054
4167
  showToast(ambiguous ? "Choose the source intent in review" : "Change recorded");
@@ -5485,6 +5598,7 @@ function installFoundryInspector(options = {}) {
5485
5598
  selectedElements = [element];
5486
5599
  selected = element;
5487
5600
  }
5601
+ completeOnboardingStep("selection");
5488
5602
  if (!sectionPreferenceTouched) {
5489
5603
  const categoryKeys = [
5490
5604
  "position",
@@ -6085,6 +6199,29 @@ function installFoundryInspector(options = {}) {
6085
6199
  }
6086
6200
  });
6087
6201
  statusPopover.querySelector("[data-status-retry]").addEventListener("click", () => void hydrateSession());
6202
+ statusPopover.querySelector("[data-status-checklist]").addEventListener("click", () => {
6203
+ localStorage.removeItem(onboardingStorageKey("dismissed"));
6204
+ renderOnboardingChecklist(true);
6205
+ statusPopover.hidden = true;
6206
+ sessionStatus.setAttribute("aria-expanded", "false");
6207
+ });
6208
+ statusPopover.querySelector("[data-status-diagnostics]").addEventListener("click", () => {
6209
+ const diagnostics = createSafeDiagnostics({
6210
+ interfaceTheme: resolvedInterfaceTheme(),
6211
+ runtimeConnected,
6212
+ agentConnected: activeAgentPresence.connected,
6213
+ agentName: activeAgentPresence.presence?.agent?.name,
6214
+ selectedCount: selectedElements.length,
6215
+ recordedChangeCount,
6216
+ latestApplyState
6217
+ });
6218
+ void navigator.clipboard.writeText(JSON.stringify(diagnostics, null, 2));
6219
+ showToast("Privacy-safe diagnostics copied");
6220
+ });
6221
+ statusPopover.querySelector("[data-status-repair]").addEventListener("click", () => {
6222
+ void navigator.clipboard.writeText("npx foundry-design@beta doctor --repair");
6223
+ showToast("Repair command copied");
6224
+ });
6088
6225
  interfaceThemeTrigger.addEventListener("click", () => {
6089
6226
  if (interfaceThemeMenu.hidden) openInterfaceThemeMenu();
6090
6227
  else closeInterfaceThemeMenu();
@@ -6097,15 +6234,36 @@ function installFoundryInspector(options = {}) {
6097
6234
  );
6098
6235
  changeDock.querySelector(".dock-undo")?.addEventListener("click", () => void replayHistory(-1));
6099
6236
  changeDock.querySelector(".dock-review")?.addEventListener("click", () => void openReview());
6237
+ onboarding.querySelector(".onboarding-close")?.addEventListener("click", () => {
6238
+ localStorage.setItem(onboardingStorageKey("dismissed"), "1");
6239
+ onboarding.hidden = true;
6240
+ });
6100
6241
  onboarding.querySelector(".onboarding-start")?.addEventListener("click", () => {
6101
- localStorage.setItem("__foundry_onboarded", "1");
6242
+ const action = onboarding.querySelector(".onboarding-start")?.dataset.action;
6243
+ if (action === "agent") {
6244
+ void navigator.clipboard.writeText(
6245
+ "Start Foundry for this project and keep listening for Apply with agent requests."
6246
+ );
6247
+ showToast("Listener prompt copied");
6248
+ return;
6249
+ }
6250
+ if (action === "apply" && recordedChangeCount > 0) {
6251
+ onboarding.hidden = true;
6252
+ void openReview();
6253
+ return;
6254
+ }
6255
+ if (action === "done") {
6256
+ onboarding.hidden = true;
6257
+ return;
6258
+ }
6102
6259
  onboarding.hidden = true;
6103
6260
  inspecting = true;
6104
6261
  updateInspectionMode();
6105
- showToast("Select mode stays on \xB7 click any element to begin");
6262
+ showToast(
6263
+ action === "change" ? "Adjust any Inspector value to record a change" : "Select mode stays on \xB7 click any element to begin"
6264
+ );
6106
6265
  });
6107
6266
  onboarding.querySelector(".onboarding-shortcuts")?.addEventListener("click", () => {
6108
- localStorage.setItem("__foundry_onboarded", "1");
6109
6267
  onboarding.hidden = true;
6110
6268
  openCommands();
6111
6269
  });
@@ -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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6DA,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;AAq/BD,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,uBAA4B,GACpC,0BAA0B,CA4yK5B"}
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,CAm9K5B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foundry-design-web-adapter",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.6",
4
4
  "description": "Development-only browser inspector for Foundry",
5
5
  "type": "module",
6
6
  "main": "dist/adapter.js",