Skip to content

feat(sdk): loading state = scene image + frosted card + circular loader#64

Open
LEVI-RIVKIN wants to merge 1 commit into
mainfrom
claude/sdk-iframe-skeleton-ywhs0k
Open

feat(sdk): loading state = scene image + frosted card + circular loader#64
LEVI-RIVKIN wants to merge 1 commit into
mainfrom
claude/sdk-iframe-skeleton-ywhs0k

Conversation

@LEVI-RIVKIN

@LEVI-RIVKIN LEVI-RIVKIN commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the embed loading skeleton with the interview's real chrome, rendered outside-in:

  1. the research's scene image (from {host}/interview/{researchId}/scene-image) behind
  2. a frosted translucent card (55% panel + backdrop-filter: blur(22px), matching the app's card treatment), with
  3. a circular loader centered in the card — a conic-gradient arc with a fading tail over a faint track, round cap at the leading edge.

Details

  • Loader tint follows the SDK's usual precedence (same as the float launcher): local brand.primary override → API embed config (primaryColor / darkPrimaryColor) → Perspective default (#7c3aed / #a78bfa). LoadingOptions gains apiConfig; all five embeds pass _apiConfig through. Empty strings fall through to the next source; browsers without color-mix() keep an arc-only fallback (the plain conic-gradient is declared first).
  • Scene image is applied only after it has actually loaded, layered over the app's default surface color (#f5f2f0 light / #15171e dark, or a custom brand.bg) — a research without a scene (404), slow network, or blocked request degrades to a clean solid color. prefetchSceneImage() warms it on intent signals (float-bubble hover/focus, popup/slider trigger elements), backed by a bounded (capped) dedupe set.
  • Container-query responsive, matched to the app. The overlay lives in the host page DOM next to the cross-origin iframe, so layout keys off the consumer's slot via container-type: size (both axes) — not the viewport. The card's sizing mirrors the interview app's own container queries: full-bleed below 672px width; a centered max-width: 672px box above it, with vertical padding stepping 4px → 48px → 80px at the app's 448px / 768px container-height breakpoints. Verified pixel-for-pixel against the live iframe across the full width×height grid, so the card doesn't jump at handoff. Breakpoints are in px, not the app's rem: rem would resolve against the host page's (uncontrolled) root font-size, whereas the card must match the iframe's fixed pixels.
  • Seamless handoff. When the iframe signals visual-ready, it snaps to opacity: 1 beneath the overlay (no transition) while the overlay fades out on top and is removed — so combined coverage never dips below 100% (a cross-fade would flash mid-fade).
  • Always animates: a loading indicator is essential motion; a frozen spinner reads as hung (no prefers-reduced-motion override, which OS-level animation toggles also flip).

Testing

  • packages/sdk unit suite: 426 passed (structure, scene URL building + normalization + 404 path, panel luminance palettes, loader tint precedence incl. empty-string fallthrough, container-query breakpoints, prefetch dedupe + bounded eviction).
  • Typecheck, oxlint, prettier: clean.
  • Verified in a real browser against the live interview iframe: card geometry matches the app pixel-for-pixel across the width×height breakpoint grid (including narrow-but-tall slots staying full-bleed); scene frosting on desktop; mobile full-bleed; API-config and local-brand tint; dark no-scene 404 fallback.
  • Existing e2e specs depend only on the .perspective-loading root class (preserved) detaching at handoff.

Requires (app-side, graceful until then)

GET {host}/interview/{slug}/scene-image — 302 to the versioned CDN asset (short-TTL redirect, immutable asset), 404 when the research has no scene. Until it ships, the loading state simply shows the solid background + card + loader.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

📦 Snapshot Release

Published snapshot packages for this PR:

pnpm add @perspective-ai/sdk@1.14.0-pr-64-20260708153147
pnpm add @perspective-ai/sdk-react@1.14.0-pr-64-20260708153147

Or use the npm tag (always points to the latest snapshot from this PR):

pnpm add @perspective-ai/sdk@pr-64
pnpm add @perspective-ai/sdk-react@pr-64

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the SDK’s embed loading skeleton to be a neutral, container-query–responsive overlay that reflects only the iframe’s outer “shape” (boxed on wide slots, full-bleed on narrow slots), with a calmer single-bar shimmer animation and updated default background colors aligned to the interview app’s surface tokens.

Changes:

  • Replaced the detailed, UI-mirroring skeleton DOM with a minimal structure (content/surface + single shimmer bar) and moved layout/animation to a shared injected stylesheet driven by container queries.
  • Updated default light/dark background and shimmer/border translucency values to match the app’s interview surface and reduce visual flash at handoff.
  • Rewrote unit tests to assert the new structure, shared-style injection, container-query usage, and reduced-motion behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
packages/sdk/src/loading.ts Reworks skeleton implementation to a generic, container-query–responsive overlay with a shared injected stylesheet and per-instance CSS variables.
packages/sdk/src/loading.test.ts Updates tests to validate the new DOM structure, stylesheet injection, and animation/scoping behavior.
.changeset/generic-loading-skeleton.md Adds a patch changeset describing the new neutral skeleton behavior and compatibility notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/sdk/src/loading.test.ts
Comment thread packages/sdk/src/loading.ts
@LEVI-RIVKIN LEVI-RIVKIN changed the title feat(sdk): neutral, container-query loading skeleton feat(sdk): loading state — scene image, frosted card, animated mark Jul 6, 2026
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch from 1c8f3c9 to 3e4fbcb Compare July 6, 2026 20:14
@LEVI-RIVKIN LEVI-RIVKIN requested a review from Copilot July 6, 2026 20:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread packages/sdk/src/loading.ts Outdated
Comment thread packages/sdk/src/loading.ts
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch 3 times, most recently from 7d90c82 to ab32b62 Compare July 6, 2026 21:10
@LEVI-RIVKIN LEVI-RIVKIN requested a review from vinaypuppal July 6, 2026 21:20
@LEVI-RIVKIN LEVI-RIVKIN changed the title feat(sdk): loading state — scene image, frosted card, animated mark feat(sdk): loading state = scene image + frosted card + circular loader Jul 7, 2026
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch 3 times, most recently from ca44f6e to 0966116 Compare July 8, 2026 12:30
@LEVI-RIVKIN LEVI-RIVKIN requested a review from Copilot July 8, 2026 12:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread packages/sdk/src/loading.ts Outdated
Comment thread packages/sdk/src/loading.ts
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch 5 times, most recently from 9b7824e to 0dd030f Compare July 8, 2026 15:07
@LEVI-RIVKIN LEVI-RIVKIN requested a review from Copilot July 8, 2026 15:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread packages/sdk/src/loading.ts
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch from 0dd030f to a49fb6c Compare July 8, 2026 15:22
Replaces the loading skeleton with the interview's real chrome, outside-in:
the research's scene image, a frosted translucent card over it (55% panel +
backdrop blur, matching the app's card treatment), and a 40px circular
loader centered in the card (conic-gradient arc with a fading tail over a
faint track, round cap at the leading edge).

- Loader tint follows the SDK's usual precedence (same as the float
  launcher): local brand.primary → API embed config primaryColor /
  darkPrimaryColor → Perspective default (#7c3aed / #a78bfa). LoadingOptions
  gains `apiConfig`; all five embeds pass _apiConfig through. Browsers
  without color-mix() keep an arc-only fallback.
- Scene image comes from {host}/interview/{researchId}/scene-image and is
  applied only after it actually loads, layered over the app's default
  surface color (#f5f2f0 light / #15171e dark, or brand.bg) — a research
  without a scene (404), slow network, or blocked request degrades to a
  clean solid color. prefetchSceneImage() warms it on intent signals
  (float-bubble hover/focus, popup/slider trigger elements).
- Container-query responsive: the overlay lives in the host page DOM next to
  the cross-origin iframe, so layout keys off the consumer's slot, not the
  viewport. At the app's own 672px breakpoint the card switches between a
  centered box (height-relative percentage inset) and full-bleed.
- The loader always animates — essential loading motion; a frozen spinner
  reads as hung — so there is no prefers-reduced-motion override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016kJptBdnAQ2VfzrE9ob1Wb
@LEVI-RIVKIN LEVI-RIVKIN force-pushed the claude/sdk-iframe-skeleton-ywhs0k branch from a49fb6c to fe18680 Compare July 8, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants