# Syntrologie Smart Canvas SDK

## Smart Canvas SDK (React + Shadow DOM)

The SDK now ships a framework-agnostic `<smart-canvas>` custom element with an **open shadow root**. We still render everything with React, but the canvas is encapsulated so host Tailwind configs, resets, or stacking contexts can’t break the UI.

### What’s Included

- `SmartCanvasElement` – the custom element that owns the shadow DOM, overlay root, and controller.
- `SmartCanvasApp` – the React tree (overlay, rectangles, toggle) you portal into the shadow.
- `SmartCanvasPortal` – helper for React hosts to portal their tree (and context) into the element mount.
- `createSmartCanvas` – imperative API (`window.SmartCanvas.create`) for non-React hosts.
- GrowthBook/PostHog wrappers, hooks, rectangle components, and wheel remain available.

### Installation

#### NPM/Yarn

```bash
npm install @syntrologie/smart-canvas-sdk
# or
yarn add @syntrologie/smart-canvas-sdk
```

#### CDN

For quick setup without a build process:

```html
<!-- Minified production build -->
<script src="https://unpkg.com/@syntrologie/smart-canvas-sdk@latest/dist/smart-canvas.min.js"></script>

<!-- Non-minified development build -->
<script src="https://unpkg.com/@syntrologie/smart-canvas-sdk@latest/dist/smart-canvas.js"></script>

<!-- ES Modules -->
<script type="module">
  import { createSmartCanvas } from 'https://unpkg.com/@syntrologie/smart-canvas-sdk@latest/dist/smart-canvas.esm.js';
</script>
```

#### Local Development

```bash
cd tech-core/sdks/shadow-canvas
npm run build
cd ../../tax_landing_page
npm install @syntrologie/smart-canvas-sdk@file:../tech-core/sdks/shadow-canvas
```

Rebuild and reinstall whenever the SDK changes.

### Using the Custom Element (React Host)

```tsx
import {
  createSyntroExperiment,
  createSyntroTelemetry,
  SmartCanvasApp,
  SmartCanvasPortal,
  registerSmartCanvasElement,
  type SmartCanvasElement,
} from "@syntrologie/smart-canvas-sdk";

const canvasFetcher = async () => {/* return ShadowCanvasConfigResponse */};

function SmartCanvasBridge() {
  const [element, setElement] = useState<SmartCanvasElement | null>(null);

  useEffect(() => {
    registerSmartCanvasElement();
  }, []);

  const controller = element?.getController();

  return (
    <>
      <smart-canvas ref={setElement as any} />
      {element && controller ? (
        <SmartCanvasPortal element={element}>
          <SmartCanvasApp
            controller={controller}
            fetcher={canvasFetcher}
            overlayConfigUri={import.meta.env.VITE_SHADOW_CANVAS_OVERLAY_URI}
          />
        </SmartCanvasPortal>
      ) : null}
    </>
  );
}
```

### Using the Imperative API (Plain JS Host)

When using via CDN, the SDK is available as `window.SyntrologieSDK`:

```ts
// Via npm/ES modules
await window.SmartCanvas?.create({
  defaultOpen: true,
  tokens: {
    "--sc-surface": "#050814",
    "--sc-fg": "#f8fafc",
  },
  integrations: {
    posthog,       // existing PostHog client
    growthbook,    // existing GrowthBook client
  },
});
```

### Styling & Overlays

- Fonts, colors, and CSS variables (`--sc-surface`, `--sc-fg`, etc.) are inherited from the host. Tokens can be overridden via `setTokens` or standard CSS:
  - `--syntro-tooltip-bg`, `--syntro-tooltip-fg`, `--syntro-tooltip-radius`, `--syntro-tooltip-shadow`
  - `--syntro-ring`, `--syntro-spotlight-backdrop` for highlights
- A scoped reset is injected via `adoptedStyleSheets` (fallbacks to `<style>`).
- Tooltips/popovers should use the Popover API or Floating UI with the exposed overlay root (`element.getOverlayRoot()`).

### Analytics Events

The `createSyntroTelemetry` client emits:

| Event | Description |
| --- | --- |
| `shadow_canvas_opened` / `shadow_canvas_closed` | Toggle button interactions. |
| `shadow_canvas_rectangle_viewed` | A rectangle becomes visible (overlay grid & wheel). |
| `shadow_canvas_action` | CTA click, chatbot send, overlay events (tooltips, dismiss, anchor missing). |

### Rectangle Config (unchanged)

The experimentation service still returns the same payload (`rectangles`, `style`, `content`, optional `experiment` metadata). Top-level fields now include optional `canvasTitle` so host teams can rename the overlay without code changes. See `src/types.ts` for the exact contract.

### Schema & Validation Tool

- JSON schema lives at `schema/canvas-config.schema.json` (published with the package).
- Validate a config file locally:

```bash
cd tech-core/sdks/shadow-canvas
npm run validate-config -- ../../tax_landing_page/public/smart-canvas-demo.json
```

The script evaluates structure/required fields without third-party dependencies so CI and content authors can catch issues early.

### Overlays (Tooltips & Highlights)

- Recipes use the schema in `src/overlays/schema.ts` (roughly `{ id, version, steps[] }`).
- Provide a recipe URI via `overlayConfigUri` or let GrowthBook supply `smart-canvas-overlay-uri`.
- Overlays render in `#syntro-overlays` (fixed, pointer-events: none) and use Floating UI for tethering.
- The Canvas runtime dynamically imports the overlay chunk only when a recipe resolves.
- Exposed helpers:
  - `createOverlayRecipeFetcher`, `resolveConfigUri`
  - `runOverlays`, `showTooltip`, `showHighlight`
  - `createAnchorResolver` (works for main DOM + shadow DOM)

Telemetry for overlays uses `telemetry.trackAction("syntro_overlay_*", stepId, "overlay")`.

### Testing

- Shadow root is **open**, so Playwright/Cypress can traverse via `.shadow()`.
- Data hooks: `[data-shadow-canvas-id="overlay-launcher"]`, `[data-shadow-canvas-id="rectangle-${id}"]`, etc.
- `SmartCanvasController` exposes `open/close` for deterministic tests.

### Directory Overview

```
tech-core/sdks/shadow-canvas
├─ src
│  ├─ SmartCanvasElement.tsx   // custom element + controller glue
│  ├─ SmartCanvasApp.tsx       // React UI
│  ├─ SmartCanvasPortal.tsx    // portal helper
│  ├─ api.tsx                  // window.SmartCanvas + imperative API
│  ├─ analytics/, experiments/, hooks/, components/
│  └─ controller.ts
└─ README.md
```

### Quick Checklist

1. Call `registerSmartCanvasElement()` once per app.
2. Render `<smart-canvas>` (custom element).
3. Portal your React tree into the element’s shadow via `SmartCanvasPortal`.
4. Configure the SDK with your API credentials.
5. For non-React pages, call `window.SmartCanvas.create()` with configuration.
