# InstantSlicer — Autonomous Agent API Reference

**Version:** 1.0
**Detection:** `<meta name="instantslicer-api" content="1.0" data-api-object="window.InstantSlicerAPI">` in `<head>`

---

## Choosing the right approach

| Method | URL / command | RAM | WebGL needed | Best for |
|--------|--------------|-----|-------------|----------|
| **Lightweight browser page** | `https://instantslicer.com/agent.html` | ~50 MB | No | Pi, low-RAM VMs, headless Chromium, OpenClaw |
| **Full UI page** | `https://instantslicer.com` | ~500 MB | Yes (3D viewer) | Desktop Chrome, debugging |
| **Node.js CLI** | `node slice.js model.stl` | ~100 MB | None | No browser available, CI pipelines, shell scripts |

**On memory-constrained devices (Raspberry Pi, 4 GB VMs):** always use
`agent.html` or the Node CLI. The full page loads Three.js + WebGL which can
spike to 500 MB+ and crash the browser on low-RAM hardware.

---

## Option A — Lightweight browser page (recommended for agents)

**URL:** `https://instantslicer.com/agent.html`

No Three.js. No WebGL. No UI. Just `instantslicer2.js` + `window.InstantSlicerAPI`.
~50 MB RAM. Works in headless Chromium **without** any GPU flags.

```js
// Playwright example (no GPU flags needed for agent.html)
const browser = await chromium.launch();
const page    = await browser.newPage();
await page.goto('https://instantslicer.com/agent.html');

const b64 = require('fs').readFileSync('cube.stl').toString('base64');
const stats = await page.evaluate(b64 => InstantSlicerAPI.loadSTLFromBase64(b64, 'cube.stl'), b64);
await page.evaluate(() => InstantSlicerAPI.applyPreset('prusa_mk4'));
await page.evaluate(() => InstantSlicerAPI.setSettings({ layerHeight: 0.2, wallCount: 3 }));
const gcode = await page.evaluate(() => InstantSlicerAPI.slice());
require('fs').writeFileSync('cube.gcode', gcode);
await browser.close();
```

---

## Option B — Node.js CLI (no browser at all)

Clone the repo (or download `scripts/slice.js` + `frontend/instantslicer2.js`)
and run:

```bash
# Basic
node scripts/slice.js model.stl

# With output path and settings
node scripts/slice.js model.stl output.gcode preset=prusa_mk4 layerHeight=0.15 wallCount=4

# Available presets
node scripts/slice.js --help
```

The output G-code is written directly to disk. No browser, no network required.

```bash
# Typical agent workflow on a Pi / server
base64 -w0 /home/agent/model.stl > /tmp/model.b64     # (only needed for browser approach)
node /path/to/scripts/slice.js /home/agent/model.stl /home/agent/model.gcode preset=ender5plus
```

---

## Option C — Full UI page (desktop only)

**URL:** `https://instantslicer.com`

Requires WebGL. For headless Chromium add GPU flags:

```js
const browser = await chromium.launch({
  args: ['--use-gl=angle', '--use-angle=swiftshader']
})
```

---

## What is InstantSlicer?

InstantSlicer is a **fully browser-based FDM slicer**. It converts STL files
into G-code entirely inside the browser tab using JavaScript — no server calls,
no uploads, no installs. The `agent.html` page strips the 3D viewer so slicing
runs with minimal resources.

### Key facts for agents
- All slicing happens in the browser's JS engine; the page **must stay open**
  during slicing. Do not navigate away or close the tab.
- Slicing a typical model takes 1–20 seconds depending on complexity.
- The API is available immediately on `DOMContentLoaded` — no login required.
- No file-size limits, no rate limits, no account.
- G-code is returned as a plain string from `slice()` — agents can read it
  directly without triggering any download.

---

## API Surface

Everything is on **`window.InstantSlicerAPI`**. All methods are safe to call
in a browser `evaluate()` / `page.evaluate()` / DevTools console.

```
window.InstantSlicerAPI = {
  version,

  // Loading
  loadSTLFromURL(url, filename?)   → Promise<ModelStats>
  loadSTLFromBytes(bytes, name?)   → ModelStats
  loadSTLFromBase64(b64, name?)    → ModelStats

  // Configuration
  getSettings()                    → object
  setSettings(partial)             → void
  getPresets()                     → string[]
  applyPreset(key)                 → void

  // Slicing
  slice(onProgress?)               → Promise<string>   ← G-code

  // Output
  getGcode()                       → string | null
  downloadGcode(filename?)         → void

  // Inspection
  getState()                       → StateObject
  help()                           → string  (also prints to console)
}
```

---

## Step-by-step: Minimal Slice Workflow

```js
// 1. Load an STL
await InstantSlicerAPI.loadSTLFromURL('https://example.com/part.stl')

// 2. Choose printer
InstantSlicerAPI.applyPreset('ender5plus')

// 3. Adjust settings if needed (optional)
InstantSlicerAPI.setSettings({ layerHeight: 0.2, wallCount: 3, infillPercent: 15 })

// 4. Slice — returns G-code string
const gcode = await InstantSlicerAPI.slice()

// 5a. Read it as a string
console.log(gcode.slice(0, 500))

// 5b. Or trigger a browser download
InstantSlicerAPI.downloadGcode('part.gcode')
```

---

## Method Reference

### `loadSTLFromURL(url, filename?)`
**Returns:** `Promise<ModelStats>`

Fetches an STL from a public URL. The STL host must allow CORS. The filename
is used as the base name for the downloaded G-code file; if omitted, it is
inferred from the URL's last path segment.

```js
const stats = await InstantSlicerAPI.loadSTLFromURL(
  'https://cdn.example.com/cube.stl',
  'cube.stl'
)
// stats = { surfaceMm2: 2400, volumeMm3: 8000, bboxMm: { x: 20, y: 20, z: 20 } }
```

**Errors thrown:**
- `HTTP 404 fetching …` — URL not found
- `HTTP 403 fetching …` — CORS blocked or access denied

---

### `loadSTLFromBytes(bytes, filename?)`
**Returns:** `ModelStats` (synchronous)

Accepts a `Uint8Array`, `Int8Array`, or `ArrayBuffer`. Use this when you have
read the file from disk (e.g. via Playwright `readFile`) or received raw bytes.

```js
// Playwright example
const stlBytes = await page.evaluate(async () => {
  // inside the page — if you injected the bytes via evaluate args:
})

// More practical: inject from Node.js
const buf = require('fs').readFileSync('part.stl')
const stats = await page.evaluate(
  bytes => InstantSlicerAPI.loadSTLFromBytes(new Uint8Array(bytes)),
  Array.from(buf)
)
```

---

### `loadSTLFromBase64(b64, filename?)`
**Returns:** `ModelStats` (synchronous)

Accepts a standard base64-encoded string of the STL file bytes. Useful when
the STL has been encoded for transport (e.g. from an API response or a
clipboard operation).

```js
// Node.js side
const b64 = fs.readFileSync('part.stl').toString('base64')

// Page side (via Playwright evaluate with args)
await page.evaluate(
  ([b64]) => InstantSlicerAPI.loadSTLFromBase64(b64, 'part.stl'),
  [b64]
)
```

---

### `getSettings()`
**Returns:** plain object with all current slicer settings

Returns a JSON-serializable snapshot of everything currently configured. Call
this to verify what settings are active before slicing.

```js
const s = InstantSlicerAPI.getSettings()
console.log(s.layerHeight)    // 0.2
console.log(s.wallCount)      // 3
console.log(s.nozzleTemp)     // 215
```

---

### `setSettings(partial)`
**Returns:** void

Merges a partial settings object into the current configuration. Unknown keys
are silently ignored. Existing settings not mentioned are unchanged.

```js
InstantSlicerAPI.setSettings({
  layerHeight:     0.15,
  wallCount:       4,
  infillPercent:   20,
  infillType:      'gyroid',
  nozzleTemp:      220,
  supportEnabled:  true,
  seamPosition:    'back',
})
```

**Special keys:**
- `supportEnabled` and `ironingEnabled` accept `true`/`false` (boolean)
- `wallOverlap` is a fraction `0.0–1.0` (e.g. `0.1` = 10%)
- All other numeric keys accept numbers directly

---

### `getPresets()`
**Returns:** `string[]`

Returns the list of all valid printer preset keys. Pass any of these to
`applyPreset()`.

```js
InstantSlicerAPI.getPresets()
// → ['bambu_x1c', 'bambu_p1s', 'bambu_a1', 'bambu_a1mini',
//    'ender3', 'ender3v2', 'ender3s1pro', 'ender3v3se', 'ender3v3',
//    'ender5pro', 'ender5plus', 'ender5s1',
//    'k1', 'k1max', 'cr10s',
//    'prusa_mk4', 'prusa_mk3s', 'prusa_mini', 'prusa_xl',
//    'kobra2pro', 'kobra3',
//    'neptune3pro', 'neptune3plus', 'neptune4pro', 'neptune4max',
//    'sv06', 'sv08',
//    'genius_pro', 'sidewinderx4pro',
//    'voron01', 'voron2_250', 'voron2_300', 'voron2_350', 'voron_trident_250',
//    'flashforge_adventurer5m', 'qidi_x_max3']
```

---

### `applyPreset(key)`
**Returns:** void

Applies a named printer preset. This sets build plate dimensions, nozzle size,
temperatures, speeds, and retraction in one call. Always call this before
manually overriding individual settings with `setSettings()`.

```js
InstantSlicerAPI.applyPreset('bambu_x1c')
InstantSlicerAPI.applyPreset('prusa_mk4')
InstantSlicerAPI.applyPreset('ender5plus')
```

**Throws:** `'Unknown preset "xyz"'` if the key is invalid.

---

### `slice(onProgress?)`
**Returns:** `Promise<string>` — resolves with the full G-code string

Slices the currently loaded model with the currently configured settings.
Optionally accepts a progress callback that receives a float `0.0–1.0`.

```js
// Simple
const gcode = await InstantSlicerAPI.slice()

// With progress
const gcode = await InstantSlicerAPI.slice(pct => {
  console.log(Math.round(pct * 100) + '%')
})
```

**Errors thrown:**
- `'No model loaded. Call loadSTLFromURL() / loadSTLFromBytes() first.'`
- `'Slice already in progress.'`

**Important:** Do not call `slice()` again until the previous Promise has
resolved. Check `getState().isSlicing` if unsure.

---

### `getGcode()`
**Returns:** `string | null`

Returns the G-code string from the most recent successful `slice()` call, or
`null` if no slice has been completed yet.

```js
const gcode = InstantSlicerAPI.getGcode()
if (gcode) {
  console.log('Lines:', gcode.split('\n').length)
  console.log('Size:', Math.round(gcode.length / 1024), 'KB')
}
```

---

### `downloadGcode(filename?)`
**Returns:** void

Triggers a browser download of the current G-code. If `filename` is omitted,
defaults to `<model-name>-instantslicer.gcode`. Throws if no G-code has been
generated yet.

```js
InstantSlicerAPI.downloadGcode('my-part-0.2mm.gcode')
```

In headless/automated contexts, prefer reading `getGcode()` directly and
writing to disk in Node.js rather than using `downloadGcode()`.

---

### `getState()`
**Returns:** object

```js
{
  hasModel:      boolean,      // true if an STL is loaded
  modelFilename: string|null,  // e.g. 'cube.stl'
  modelStats: {
    surfaceMm2:  number,       // surface area in mm²
    volumeMm3:   number,       // volume in mm³
    bboxMm: { x, y, z }       // bounding box in mm
  } | null,
  hasGcode:      boolean,      // true if slice() has completed
  isSlicing:     boolean,      // true while slice() is in progress
  currentPreset: string|null,  // active preset key, or '' for custom
  buildPlate: { x, y, z }     // active build plate dimensions in mm
}
```

---

### `help()`
**Returns:** string (also prints to console)

Prints a concise usage cheat sheet. Call this first when arriving at the page.

```js
InstantSlicerAPI.help()
```

---

## All Settings Reference

These are all keys accepted by `setSettings()` and returned by `getSettings()`.

### Quality

| Key | Type | Default | Range / Values | Description |
|-----|------|---------|----------------|-------------|
| `layerHeight` | float | 0.2 | 0.05–0.35 mm | Layer thickness. Lower = finer detail, slower print. |
| `firstLayerHeight` | float | 0.2 | 0.1–0.35 mm | First layer height (can be taller for better bed adhesion). |
| `wallCount` | int | 3 | 1–10 | Number of perimeter shells. 2–3 for normal parts, 4+ for strength. |
| `topLayers` | int | 4 | 1–20 | Solid top layers. 4–6 for smooth top surface. |
| `bottomLayers` | int | 4 | 1–20 | Solid bottom layers. 4–6 recommended. |
| `seamPosition` | string | `'back'` | `'back'`, `'nearest'`, `'random'` | Where to place the loop seam. `'back'` hides it behind the part. |

### Infill

| Key | Type | Default | Range / Values | Description |
|-----|------|---------|----------------|-------------|
| `infillPercent` | int | 15 | 0–100 | Infill density %. 10–20% for most parts. 40–100% for maximum strength. |
| `infillType` | string | `'gyroid'` | see table below | Infill pattern. |

**Available infill patterns:**

| Value | Name | Best for |
|-------|------|----------|
| `'gyroid'` | Gyroid | General purpose, strong, isotropic |
| `'lines'` | Lines | Fast, minimal material |
| `'grid'` | Grid | Strong, simple |
| `'triangles'` | Triangles | Good strength-to-weight |
| `'cubic'` | Cubic | Isotropic strength |
| `'honeycomb'` | Honeycomb | High strength, classic |
| `'concentric'` | Concentric | Flexible parts, artistic |
| `'lightning'` | Lightning | Fastest, top-surface support only |
| `'hilbert'` | Hilbert Curve | Artistic, even coverage |
| `'octagram'` | Octagram Spiral | Decorative, low material |

### Temperatures

| Key | Type | Default | Range | Description |
|-----|------|---------|-------|-------------|
| `nozzleTemp` | int | 215 | 150–300 | Nozzle temperature °C. PLA: 200–220. PETG: 230–245. ABS: 240–260. |
| `bedTemp` | int | 60 | 0–120 | Bed temperature °C. PLA: 55–65. PETG: 70–85. ABS: 100–110. |
| `fanSpeed` | int | 100 | 0–100 | Part cooling fan % (layers 2+). PLA: 100%. ABS: 0–25%. |
| `firstLayerFan` | int | 0 | 0–100 | Fan % for first layer only. Usually 0 for bed adhesion. |

### Speeds (all in mm/s)

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `outerWallSpeed` | float | 25 | Outer perimeter. Lower = better surface quality. |
| `innerWallSpeed` | float | 50 | Inner perimeters. Can be 2× outer wall speed. |
| `topSkinSpeed` | float | 25 | Top solid layers. Lower = smoother top surface. |
| `infillSpeed` | float | 80 | Sparse infill. Can be fast. |
| `supportSpeed` | float | 60 | Support structures. |
| `bridgeSpeed` | float | 30 | Bridges over open air. Slow = less sag. |
| `travelSpeed` | float | 200 | Non-extrusion moves. |
| `firstLayerSpeed` | float | 20 | All features on first layer. Slow for adhesion. |

### Retraction

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `retractDist` | float | 1.0 | Retraction distance mm. Direct: 0.5–1.5. Bowden: 3–7. |
| `retractSpeed` | float | 45 | Retraction speed mm/s. |
| `zHopDist` | float | 0 | Z-lift on retract mm (0 = disabled). Prevents nozzle dragging. |

### Bed Adhesion

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `skirtLoops` | int | 0 | Skirt loops around model (0 = none). Primes nozzle before print. |
| `brimWidth` | float | 0 | Brim width mm (0 = none). Increases first-layer adhesion. |
| `purgeLineLen` | float | 0 | Purge line length mm along left edge (0 = none). |

### Hardware

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `nozzleDia` | float | 0.4 | Nozzle diameter mm. Set by preset; change only if using a different nozzle. |
| `filamentDia` | float | 1.75 | Filament diameter mm. `1.75` or `2.85`. |

### Support

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `supportEnabled` | boolean | false | Enable support structures. |
| `supportAngle` | int | 50 | Minimum overhang angle (°) to generate support. 45–55 is typical. |
| `supportType` | string | `'normal'` | `'normal'` or `'tree'`. Tree supports use less material. |
| `supportStyle` | string | `'lines'` | `'lines'`, `'grid'`, or `'concentric'`. |
| `supportZDistTop` | float | 0.2 | Z gap between top of support and model mm. |
| `supportZDistBot` | float | 0.0 | Z gap at bottom of support mm. |
| `supportXYDist` | float | 0.35 | Horizontal gap between support and model mm. |
| `supportIfaceLayers` | int | 2 | Dense interface layers at top of support. |
| `supportIfacePattern` | string | `'lines'` | `'lines'` or `'concentric'`. |
| `supportIfaceSpacing` | float | 0.15 | Interface line spacing mm. |

### Ironing

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `ironingEnabled` | boolean | false | Ironing post-processing pass on top surfaces. |
| `ironingSpeed` | float | 15 | Ironing pass speed mm/s. |
| `ironingFlow` | float | 15 | Ironing flow % of normal extrusion. |
| `ironingSpacing` | float | 0.1 | Spacing between ironing lines mm. |

### Advanced

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `wallOverlap` | float | 0.1 | Infill overlap fraction into innermost wall (0.0–1.0). `0.1` = 10%. |

---

## All Printer Presets

Apply any of these with `applyPreset('key')`.

### Bambu Lab
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `bambu_x1c` | Bambu Lab X1C | 256×256×256 mm | Direct |
| `bambu_p1s` | Bambu Lab P1S | 256×256×256 mm | Direct |
| `bambu_a1` | Bambu Lab A1 | 256×256×256 mm | Direct |
| `bambu_a1mini` | Bambu Lab A1 Mini | 180×180×180 mm | Direct |

### Creality Ender
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `ender3` | Ender 3 | 220×220×250 mm | Bowden |
| `ender3v2` | Ender 3 V2 | 220×220×250 mm | Bowden |
| `ender3s1pro` | Ender 3 S1 Pro | 220×220×270 mm | Direct |
| `ender3v3se` | Ender 3 V3 SE | 220×220×250 mm | Direct |
| `ender3v3` | Ender 3 V3 | 220×220×270 mm | Direct |
| `ender5pro` | Ender 5 Pro | 220×220×300 mm | Bowden |
| `ender5plus` | Ender 5 Plus | 350×350×400 mm | Bowden |
| `ender5s1` | Ender 5 S1 | 220×220×280 mm | Direct |

### Creality K-series / CR
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `k1` | Creality K1 | 220×220×250 mm | Direct |
| `k1max` | Creality K1 Max | 300×300×300 mm | Direct |
| `cr10s` | Creality CR-10S | 300×300×400 mm | Bowden |

### Prusa
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `prusa_mk4` | Prusa MK4 | 250×210×220 mm | Direct |
| `prusa_mk3s` | Prusa i3 MK3S+ | 250×210×210 mm | Direct |
| `prusa_mini` | Prusa MINI+ | 180×180×180 mm | Bowden |
| `prusa_xl` | Prusa XL | 360×360×360 mm | Direct |

### Anycubic Kobra
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `kobra2pro` | Kobra 2 Pro | 220×220×250 mm | Direct |
| `kobra3` | Kobra 3 | 250×250×260 mm | Direct |

### Elegoo Neptune
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `neptune3pro` | Neptune 3 Pro | 225×225×280 mm | Direct |
| `neptune3plus` | Neptune 3 Plus | 320×320×400 mm | Direct |
| `neptune4pro` | Neptune 4 Pro | 225×225×265 mm | Direct |
| `neptune4max` | Neptune 4 Max | 420×420×480 mm | Direct |

### Sovol
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `sv06` | Sovol SV06 | 220×220×250 mm | Direct |
| `sv08` | Sovol SV08 | 350×350×350 mm | Direct |

### Artillery
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `genius_pro` | Artillery Genius Pro | 220×220×250 mm | Direct |
| `sidewinderx4pro` | Sidewinder X4 Pro | 300×300×400 mm | Direct |

### Voron
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `voron01` | Voron 0.1 | 120×120×120 mm | Direct |
| `voron2_250` | Voron 2.4 | 250×250×250 mm | Direct |
| `voron2_300` | Voron 2.4 | 300×300×300 mm | Direct |
| `voron2_350` | Voron 2.4 | 350×350×350 mm | Direct |
| `voron_trident_250` | Voron Trident | 250×250×250 mm | Direct |

### Other
| Key | Printer | Build Volume | Type |
|-----|---------|-------------|------|
| `flashforge_adventurer5m` | Flashforge Adventurer 5M | 220×220×220 mm | Direct |
| `qidi_x_max3` | QIDI X-MAX 3 | 325×315×315 mm | Direct |

---

## Complete Use Case Examples

### Use case 1: Slice from URL, read G-code as string
```js
await InstantSlicerAPI.loadSTLFromURL('https://example.com/part.stl')
InstantSlicerAPI.applyPreset('prusa_mk4')
const gcode = await InstantSlicerAPI.slice()
console.log(gcode.split('\n').length, 'lines')
```

### Use case 2: Slice with custom quality settings
```js
await InstantSlicerAPI.loadSTLFromURL('https://example.com/part.stl')
InstantSlicerAPI.applyPreset('bambu_x1c')
InstantSlicerAPI.setSettings({
  layerHeight:     0.15,       // fine detail
  firstLayerHeight: 0.2,
  wallCount:        4,
  topLayers:        6,
  bottomLayers:     4,
  infillPercent:    25,
  infillType:       'gyroid',
  seamPosition:     'back',
  nozzleTemp:       220,
  bedTemp:          65,
  fanSpeed:         100,
  firstLayerFan:    0,
})
const gcode = await InstantSlicerAPI.slice()
```

### Use case 3: Load STL from disk (Playwright)
```js
// Node.js / Playwright
const buf = require('fs').readFileSync('/path/to/part.stl')
const b64 = buf.toString('base64')

await page.evaluate(async ([b64]) => {
  InstantSlicerAPI.loadSTLFromBase64(b64, 'part.stl')
  InstantSlicerAPI.applyPreset('ender5plus')
  InstantSlicerAPI.setSettings({ layerHeight: 0.2, infillPercent: 20 })
  return await InstantSlicerAPI.slice()
}, [b64])
```

### Use case 4: Save G-code to disk (Playwright, no download dialog)
```js
const gcode = await page.evaluate(async ([b64]) => {
  InstantSlicerAPI.loadSTLFromBase64(b64, 'part.stl')
  InstantSlicerAPI.applyPreset('prusa_mk4')
  return await InstantSlicerAPI.slice()
}, [b64])

// Back in Node.js
require('fs').writeFileSync('output.gcode', gcode)
```

### Use case 5: Slice with supports
```js
await InstantSlicerAPI.loadSTLFromURL('https://example.com/bridge.stl')
InstantSlicerAPI.applyPreset('ender3v3')
InstantSlicerAPI.setSettings({
  layerHeight:     0.2,
  supportEnabled:  true,
  supportType:     'tree',      // 'normal' or 'tree'
  supportAngle:    50,           // degrees
  supportStyle:    'lines',
  zHopDist:        0.2,          // lift nozzle over supports
})
const gcode = await InstantSlicerAPI.slice()
```

### Use case 6: Slice multiple files in a loop
```js
const jobs = [
  { url: 'https://example.com/a.stl', preset: 'bambu_x1c', lh: 0.2 },
  { url: 'https://example.com/b.stl', preset: 'prusa_mk4', lh: 0.15 },
]

const results = []
for (const job of jobs) {
  await InstantSlicerAPI.loadSTLFromURL(job.url)
  InstantSlicerAPI.applyPreset(job.preset)
  InstantSlicerAPI.setSettings({ layerHeight: job.lh })
  const gcode = await InstantSlicerAPI.slice()
  results.push({ url: job.url, lines: gcode.split('\n').length })
}
console.log(results)
```

### Use case 7: Check model stats before slicing
```js
const stats = await InstantSlicerAPI.loadSTLFromURL('https://example.com/part.stl')
console.log('Volume:', (stats.volumeMm3 / 1000).toFixed(2), 'cm³')
console.log('Size:', stats.bboxMm.x + '×' + stats.bboxMm.y + '×' + stats.bboxMm.z, 'mm')

// Decide settings based on model dimensions
const longest = Math.max(stats.bboxMm.x, stats.bboxMm.y, stats.bboxMm.z)
if (longest > 200) {
  InstantSlicerAPI.applyPreset('ender5plus')  // large build volume
} else {
  InstantSlicerAPI.applyPreset('bambu_x1c')
}
const gcode = await InstantSlicerAPI.slice()
```

### Use case 8: Inspect state at each step
```js
// Confirm nothing is loaded yet
let state = InstantSlicerAPI.getState()
console.assert(!state.hasModel, 'Expected no model')

// Load
await InstantSlicerAPI.loadSTLFromURL('https://example.com/part.stl')
state = InstantSlicerAPI.getState()
console.assert(state.hasModel, 'Model should be loaded')
console.log('Model:', state.modelFilename, state.modelStats.bboxMm)

// Slice
const gcode = await InstantSlicerAPI.slice()
state = InstantSlicerAPI.getState()
console.assert(state.hasGcode, 'G-code should exist')
console.assert(!state.isSlicing, 'Should not be slicing anymore')
```

### Use case 9: Progress monitoring
```js
await InstantSlicerAPI.loadSTLFromURL('https://example.com/complex.stl')
InstantSlicerAPI.applyPreset('voron2_350')
InstantSlicerAPI.setSettings({ infillPercent: 40 })

const gcode = await InstantSlicerAPI.slice(pct => {
  process.stdout.write('\rSlicing: ' + Math.round(pct * 100) + '%')
})
console.log('\nDone:', gcode.split('\n').length, 'lines')
```

### Use case 10: Custom nozzle / filament override
```js
// 0.6mm nozzle on a Prusa MK4
InstantSlicerAPI.applyPreset('prusa_mk4')
InstantSlicerAPI.setSettings({
  nozzleDia:   0.6,    // override the 0.4mm default
  layerHeight: 0.3,    // larger layer height suits a 0.6mm nozzle
  filamentDia: 1.75,
})
```

---

## postMessage Bridge

For agents controlling the page **inside an iframe** or from a cross-origin
parent window. All API methods are mirrored through `postMessage`.

### Sending commands
```js
// From a parent page / automation script
window.postMessage({
  type: 'IS_CMD',
  cmd:  'slice',
  args: [],           // array of arguments, matching the method signature
  id:   'req-001'     // optional correlation ID, echoed in response
}, '*')
```

### Receiving responses
```js
window.addEventListener('message', (ev) => {
  if (ev.data.type !== 'IS_RESULT') return
  const { cmd, id, ok, data, error } = ev.data

  if (ok) {
    console.log(cmd, 'succeeded:', data)
  } else {
    console.error(cmd, 'failed:', error)
  }
})
```

### Response shape
```js
{
  type:  'IS_RESULT',
  cmd:   'slice',          // echoed command name
  id:    'req-001',        // echoed ID (undefined if not provided)
  ok:    true,             // false on error
  data:  '<gcode…>',      // return value of the method (if ok)
  error: undefined         // error message string (if !ok)
}
```

### Full postMessage example
```js
// 1. Load
parent.postMessage({ type:'IS_CMD', cmd:'loadSTLFromURL',
  args:['https://example.com/part.stl'], id:'r1' }, '*')

// 2. Wait for IS_RESULT{cmd:'loadSTLFromURL', ok:true}

// 3. Apply preset
parent.postMessage({ type:'IS_CMD', cmd:'applyPreset',
  args:['ender5plus'], id:'r2' }, '*')

// 4. Slice
parent.postMessage({ type:'IS_CMD', cmd:'slice',
  args:[], id:'r3' }, '*')

// 5. Receive gcode in IS_RESULT{cmd:'slice', data:'<gcode>'}
```

---

## Claude Computer-Use System Prompt

Paste this verbatim as the system prompt when giving Claude a browser tool
pointed at `https://instantslicer.com`:

```
You are controlling a browser tab open at https://instantslicer.com.

This page runs a fully browser-based FDM slicer. All slicing happens in
JavaScript — do NOT navigate away or close the tab while slicing.

The page exposes window.InstantSlicerAPI for programmatic control.
Always use the API via evaluate() rather than clicking UI elements.

STANDARD WORKFLOW
─────────────────
Step 1 — Load STL (pick one):
  evaluate: await InstantSlicerAPI.loadSTLFromURL('https://…/part.stl')
  evaluate: InstantSlicerAPI.loadSTLFromBase64(base64String, 'part.stl')

Step 2 — Apply printer preset:
  evaluate: InstantSlicerAPI.applyPreset('ender5plus')
  (list all presets: InstantSlicerAPI.getPresets())

Step 3 — Optionally adjust settings:
  evaluate: InstantSlicerAPI.setSettings({ layerHeight: 0.2, wallCount: 3, infillPercent: 15 })
  (inspect current settings: InstantSlicerAPI.getSettings())

Step 4 — Slice:
  evaluate: const gcode = await InstantSlicerAPI.slice()
  Slicing takes 1–20 seconds. Wait for the Promise to resolve.

Step 5 — Retrieve or download:
  evaluate: InstantSlicerAPI.getGcode()           // get gcode string
  evaluate: InstantSlicerAPI.downloadGcode('x.gcode')  // browser download

CHECK STATE AT ANY TIME
───────────────────────
  evaluate: InstantSlicerAPI.getState()
  // Returns: { hasModel, modelFilename, modelStats, hasGcode, isSlicing, currentPreset, buildPlate }

IF STUCK
────────
  evaluate: InstantSlicerAPI.help()
  // Prints full usage cheat sheet to the browser console and returns it as a string.

COMMON MISTAKES TO AVOID
─────────────────────────
- Do not call slice() before loadSTLFrom*() — it throws.
- Do not call slice() while isSlicing is true — it throws.
- Do not use loadSTLFromURL() for STLs that block CORS — use loadSTLFromBase64() instead.
- Do not navigate the page between API calls — state is in-memory.
```

---

## Playwright Full Example

```js
const { chromium } = require('playwright')
const fs = require('fs')

;(async () => {
  // Launch with WebGL support (needed for 3D preview, also helps slicing)
  const browser = await chromium.launch({
    headless: true,
    args: ['--use-gl=angle', '--use-angle=swiftshader']
  })
  const page = await browser.newPage()
  await page.goto('https://instantslicer.com', { waitUntil: 'networkidle' })

  // Verify API is available
  const version = await page.evaluate(() => InstantSlicerAPI.version)
  console.log('API version:', version)

  // Load STL from disk via base64 (avoids CORS issues)
  const stlBytes = fs.readFileSync('part.stl')
  const b64 = stlBytes.toString('base64')

  const stats = await page.evaluate(async ([b64]) => {
    return InstantSlicerAPI.loadSTLFromBase64(b64, 'part.stl')
  }, [b64])
  console.log('Model:', stats.bboxMm.x.toFixed(1), '×', stats.bboxMm.y.toFixed(1), '×', stats.bboxMm.z.toFixed(1), 'mm')

  // Configure
  await page.evaluate(() => {
    InstantSlicerAPI.applyPreset('prusa_mk4')
    InstantSlicerAPI.setSettings({
      layerHeight:    0.2,
      wallCount:      3,
      infillPercent:  20,
      infillType:     'gyroid',
      supportEnabled: false,
    })
  })

  // Slice (may take several seconds)
  console.log('Slicing…')
  const gcode = await page.evaluate(async () => {
    return await InstantSlicerAPI.slice(pct => {
      // Progress events fire inside the page context
    })
  })

  // Save to disk from Node.js
  fs.writeFileSync('output.gcode', gcode)
  console.log('Saved output.gcode —', gcode.split('\n').length, 'lines')

  await browser.close()
})()
```

---

## Troubleshooting

| Problem | Cause | Fix |
|---------|-------|-----|
| `InstantSlicerAPI is not defined` | Page not fully loaded | `await page.waitForFunction(() => typeof InstantSlicerAPI !== 'undefined')` |
| `slice()` takes forever | Complex model or low-end CPU | Increase Playwright timeout: `page.evaluate(..., { timeout: 120000 })` |
| `loadSTLFromURL()` fails with HTTP error | CORS blocked | Use `loadSTLFromBase64()` with locally-read bytes instead |
| `No model loaded` error | Called `slice()` before load | Always `await loadSTLFrom*()` before `slice()` |
| `Slice already in progress` | Concurrent `slice()` calls | Wait for first `slice()` to resolve; check `getState().isSlicing` |
| G-code is very short (5–10 lines) | Model outside build plate | Check `modelStats.bboxMm` vs `getState().buildPlate`; choose a larger preset |
| 3D preview blank in headless | No GPU / WebGL | Launch with `--use-angle=swiftshader`; preview is cosmetic and does not affect G-code |
| Settings not applying | Called `setSettings()` before `applyPreset()` | `applyPreset()` overwrites all settings — always call it first, then `setSettings()` for overrides |
