npm package: @maestrio/sdk
Description: Official JavaScript/TypeScript SDK for [Maestrio](https://app.maestrio.com) — submit bug reports and feature requests from your app.
Latest version: 0.0.2

# @maestrio/sdk

Official JavaScript/TypeScript SDK for [Maestrio](https://app.maestrio.com) — submit bug reports and feature requests from your app.

## Installation

```bash
npm install @maestrio/sdk
# or
pnpm add @maestrio/sdk
# or
yarn add @maestrio/sdk
```

### CDN (UMD)

```html
<script src="https://unpkg.com/@maestrio/sdk"></script>
<script>
  const maestrio = Maestrio.createMaestrio({
    apiKey: "mst_...",
    baseUrl: "https://app.maestrio.com",
    projectId: "your-project-id",
  });
</script>
```

## Quick Start

```typescript
import { createMaestrio } from "@maestrio/sdk";

const maestrio = createMaestrio({
  apiKey: "mst_...",
  baseUrl: "https://app.maestrio.com",
  projectId: "your-project-id",
});

// Identify the current user
maestrio.identify("user-123", {
  name: "Jane Doe",
  email: "jane@example.com",
});

// Submit a bug report
const result = await maestrio.submitRequest({
  title: "Button doesn't work",
  description: "The submit button on the settings page is unresponsive.",
  type: "bug",
});

result.match({
  ok: (data) => console.log("Request created:", data.id),
  err: (error) => console.error("Failed:", error.message),
});
```

## API Reference

### `createMaestrio(config)`

Creates a new Maestrio SDK instance.

```typescript
const maestrio = createMaestrio({
  apiKey: string;      // Your Maestrio API key
  baseUrl: string;     // Maestrio API base URL
  projectId: string;   // Your project identifier
  widget?: WidgetConfig; // Optional widget customization
});
```

### `maestrio.identify(userId, userProperties?, systemProperties?)`

Identifies the current user. Must be called before `submitRequest()` or `show()`.

```typescript
maestrio.identify(
  "user-123",
  { name: "Jane", email: "jane@example.com", avatar: "https://..." },
  { autoShow: true }, // automatically call show() after identifying
);
```

| Parameter          | Type               | Description                      |
| ------------------ | ------------------ | -------------------------------- |
| `userId`           | `string`           | Unique user identifier           |
| `userProperties`   | `UserProperties?`  | Optional user metadata           |
| `systemProperties` | `SystemProperties?`| Options like `autoShow` (default `false`) |

### `maestrio.submitRequest(params)`

Submits a bug report or feature request. Returns a `Result` (from `better-result`).

```typescript
const result = await maestrio.submitRequest({
  title: "Add dark mode",
  description: "Please add a dark mode toggle to settings.",
  type: "feature", // "bug" | "feature"
});
```

**Success response:**

```typescript
{ id: string; status: string; queued: boolean }
```

### `maestrio.show()`

Opens the feedback widget. Requires a prior `identify()` call.

### `maestrio.hide()`

Closes the feedback widget.

### `maestrio.destroy()`

Removes the widget from the DOM and cleans up all resources.

## Widget Customization

### Position

Control where the widget appears on screen:

```typescript
createMaestrio({
  apiKey: "mst_...",
  baseUrl: "https://app.maestrio.com",
  projectId: "your-project-id",
  widget: {
    position: "right-center-vertical", // "right-center-horizontal" | "right-center-vertical"
  },
});
```

### Theme

Override the widget's visual style by passing a `theme` object. Under the hood, each theme property maps to a CSS custom property on the `<maestrio-widget>` web component. When you pass a theme value, the SDK calls `style.setProperty()` on the host element, overriding the built-in defaults.

#### How it works

The widget renders inside a Shadow DOM, so its styles are fully isolated from your app. All visual tokens are defined as CSS custom properties on the `:host` element:

```
:host {
  --maestrio-primary: #4a7c59;
  --maestrio-primary-foreground: #ffffff;
  --maestrio-bg: #ffffff;
  --maestrio-text: #1a1a1a;
  --maestrio-text-secondary: #6b7280;
  --maestrio-border: #e5e7eb;
  --maestrio-input-bg: #f5f5f5;
  --maestrio-radius: 12px;
  --maestrio-font: system-ui, -apple-system, sans-serif;
}
```

When you provide a `theme` in the config, each key is mapped to its corresponding CSS variable and applied at runtime:

| Theme Property | CSS Variable | Default | Used for |
| --- | --- | --- | --- |
| `primaryColor` | `--maestrio-primary` | `#4a7c59` | Buttons, accents, focus rings, selected states |
| `primaryForeground` | `--maestrio-primary-foreground` | `#ffffff` | Text/icon color on primary elements |
| `backgroundColor` | `--maestrio-bg` | `#ffffff` | Modal panel background |
| `textColor` | `--maestrio-text` | `#1a1a1a` | Headings, labels, body text |
| `borderRadius` | `--maestrio-radius` | `12px` | Modal corners, buttons, inputs (derived via `calc`) |

#### Usage

```typescript
createMaestrio({
  // ...
  widget: {
    theme: {
      primaryColor: "#6366f1",
      primaryForeground: "#ffffff",
      backgroundColor: "#1e1e2e",
      textColor: "#cdd6f4",
      borderRadius: "12px",
    },
  },
});
```

All theme properties are optional — only the values you provide will override the defaults.

#### Derived values

Some internal CSS variables are not directly configurable through the `theme` object but are derived from the tokens above:

- `--maestrio-text-secondary` (`#6b7280`) — used for descriptions and placeholders
- `--maestrio-border` (`#e5e7eb`) — used for card borders, input borders, dividers
- `--maestrio-input-bg` (`#f5f5f5`) — used for textarea and card backgrounds
- `--maestrio-font` (`system-ui, -apple-system, sans-serif`) — widget font stack

Border radius is automatically scaled down for inner elements (buttons, inputs, cards) using `calc(var(--maestrio-radius) - 2px)` and `calc(var(--maestrio-radius) - 4px)`, so changing `borderRadius` keeps proportions consistent across the entire widget.

### Labels

Override any user-facing text in the widget. All label properties are optional — defaults are used for any you don't provide.

```typescript
createMaestrio({
  // ...
  widget: {
    labels: {
      triggerText: "Feedback",
      headerText: "Send us feedback",
      bugLabel: "Report a Bug",
      bugDescription: "Something isn't working as expected",
      suggestionLabel: "Suggestion",
      suggestionDescription: "Got an idea? We'd love to hear it.",
      bugPlaceholder: "Describe the problem and when it happened...",
      suggestionPlaceholder: "Describe what you'd like to see...",
      bugTextareaLabel: "What went wrong?",
      suggestionTextareaLabel: "What would you like to see?",
      sendButton: "Send",
      cancelButton: "Cancel",
    },
  },
});
```

| Label | Default | Description |
| --- | --- | --- |
| `triggerText` | `"Feedback"` | Floating trigger button text |
| `headerText` | `"What is your feedback?"` | Modal title |
| `bugLabel` | `"Bug"` | Bug card heading |
| `bugDescription` | `"Found a problem? Let us know."` | Bug card subtext |
| `suggestionLabel` | `"Suggestion"` | Feature card heading |
| `suggestionDescription` | `"Got an idea? We'd love to hear it."` | Feature card subtext |
| `bugPlaceholder` | `"Describe the problem and when it happened..."` | Textarea placeholder for bugs |
| `suggestionPlaceholder` | `"Describe what you'd like to see..."` | Textarea placeholder for features |
| `bugTextareaLabel` | `"What went wrong?"` | Textarea label for bugs |
| `suggestionTextareaLabel` | `"What would you like to see?"` | Textarea label for features |
| `sendButton` | `"Send"` | Submit button text |
| `cancelButton` | `"Cancel"` | Cancel button text |

### Full Example

```typescript
const maestrio = createMaestrio({
  apiKey: "mst_...",
  baseUrl: "https://app.maestrio.com",
  projectId: "your-project-id",
  widget: {
    position: "right-center-vertical",
    theme: {
      primaryColor: "#6366f1",
      primaryForeground: "#ffffff",
      backgroundColor: "#ffffff",
      textColor: "#1f2937",
      borderRadius: "8px",
    },
    labels: {
      triggerText: "Help & Feedback",
      headerText: "How can we help?",
    },
  },
});
```

## Error Handling

The SDK uses the [Result pattern](https://github.com/akutruff/better-result) instead of throwing errors.

```typescript
import { NotIdentifiedError, ApiError, NetworkError, ValidationError } from "@maestrio/sdk";

const result = await maestrio.submitRequest({ ... });

result.match({
  ok: (data) => {
    console.log("Created:", data.id);
  },
  err: (error) => {
    // error._tag tells you the type
    switch (error._tag) {
      case "NotIdentifiedError":
        console.error("Call identify() first");
        break;
      case "ApiError":
        console.error(`API error ${error.status}: ${error.message}`);
        break;
      case "NetworkError":
        console.error("Network failed:", error.message);
        break;
      case "ValidationError":
        console.error(`Invalid ${error.field}: ${error.message}`);
        break;
    }
  },
});
```

## TypeScript Types

All types are exported for use in your application:

```typescript
import type {
  MaestrioConfig,
  MaestrioSDK,
  UserProperties,
  SystemProperties,
  RequestType,
  SubmitRequestParams,
  SubmitRequestResponse,
  SdkError,
} from "@maestrio/sdk";
```