diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 306957b7..c0efb76c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: publish: needs: build runs-on: ubuntu-latest - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 @@ -53,9 +53,14 @@ jobs: run: npm ci - name: Build packages - run: npm run build + run: npm run build:packages + + - name: Publish @computekit/core + run: cd packages/core && npm publish --access public || echo "Already published or failed" + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Publish to npm - run: npm publish --workspaces --access public + - name: Publish @computekit/react + run: cd packages/react && npm publish --access public || echo "Already published or failed" env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..ed54dde4 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,45 @@ +name: Deploy Documentation to GitHub Pages + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Build with Jekyll + uses: actions/jekyll-build-pages@v1 + with: + source: ./docs + destination: ./_site + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + + deploy: + runs-on: ubuntu-latest + needs: build + permissions: + pages: write + id-token: write + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..79990136 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,43 @@ +name: Publish to npm + +on: + workflow_dispatch: + push: + branches: [main] + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 22.x + uses: actions/setup-node@v4 + with: + node-version: 22.x + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm ci + + - name: Build packages + run: npm run build:packages + + - name: Publish @computekit/core + run: cd packages/core && npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + continue-on-error: true + + - name: Publish @computekit/react + run: cd packages/react && npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + continue-on-error: true + + - name: Publish @computekit/react-query + run: cd packages/react-query && npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + continue-on-error: true diff --git a/.gitignore b/.gitignore index 07a84104..6b2d1c36 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ build/ *.wasm *.wasm.map +# But include the demo WASM file for StackBlitz +!examples/react-demo/public/compute.wasm + # IDE .idea/ .vscode/ @@ -30,6 +33,9 @@ yarn-error.log* .env.local .env.*.local +# npm tokens - NEVER commit these! +.npmrc + # Temporary files *.tmp *.temp diff --git a/README.md b/README.md index 30f3f7e6..3c6d4e50 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,18 @@ # ComputeKit - **The React-first toolkit for WASM and Web Workers** + **A tiny toolkit for heavy computations using Web Workers** - *Run heavy computations with React hooks. Use WASM for native-speed performance. Keep your UI at 60fps.* + *Integration with React hooks and WASM* [![npm version](https://img.shields.io/npm/v/@computekit/core.svg)](https://www.npmjs.com/package/@computekit/core) -[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@computekit/core)](https://bundlephobia.com/package/@computekit/core) +[![Bundle Size Core](https://img.shields.io/bundlephobia/minzip/@computekit/core?label=core%20size)](https://bundlephobia.com/package/@computekit/core) +[![Bundle Size React](https://img.shields.io/bundlephobia/minzip/@computekit/react?label=react%20size)](https://bundlephobia.com/package/@computekit/react) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue)](https://www.typescriptlang.org/) +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/compute-kit?file=README.md) -[Getting Started](#-getting-started) • [Examples](#-examples) • [API](#-api) • [React Hooks](#-react-hooks) • [WASM](#-webassembly-support) +[📚 Documentation](https://tapava.github.io/compute-kit) • [Live Demo](https://computekit-demo.vercel.app/) • [Getting Started](#-getting-started) • [Examples](#-examples) • [API](#-api) • [React Hooks](#-react-hooks) • [WASM](#-webassembly-support) @@ -20,14 +22,14 @@ ## ✨ Features -- ⚛️ **React-first** — Purpose-built hooks like `useCompute` with loading, error, and progress states -- 🦀 **WASM integration** — Load and call AssemblyScript/Rust WASM modules with zero boilerplate -- 🚀 **Non-blocking** — Everything runs in Web Workers, keeping your UI at 60fps -- 🔧 **Zero config** — No manual worker files, postMessage handlers, or WASM glue code -- 📦 **Tiny** — Core library is ~3KB gzipped -- 🎯 **TypeScript** — Full type safety for your compute functions and WASM bindings -- 🔄 **Worker pool** — Automatic load balancing across CPU cores -- 📊 **Progress tracking** — Built-in progress reporting for long-running tasks +- 🔄 **Worker pool** : Automatic load balancing across CPU cores +- ⚛️ **React-first** : Provides hooks like `useCompute` with loading, error, and progress states +- 🦀 **WASM integration** : Easily load and call AssemblyScript/Rust WASM modules +- 🚀 **Non-blocking** : Everything runs in Web Workers +- 🔧 **Zero config** : No manual worker files or postMessage handlers +- 📦 **Tiny** : Core library is ~5KB gzipped +- 🎯 **TypeScript** : Full type safety for your compute functions and WASM bindings +- 📊 **Progress tracking** : Built-in progress reporting for long-running tasks --- @@ -48,7 +50,7 @@ You _can_ use Web Workers and WASM without a library. But here's the reality: --- -## 🎯 When to use ComputeKit +## 🎯 When to use this toolkit (And when not to use it) | ✅ Use ComputeKit | ❌ Don't use ComputeKit | | ---------------------------------- | ---------------------------- | @@ -102,7 +104,7 @@ kit.register('fibonacci', (n: number) => { // 3. Run it (non-blocking!) const result = await kit.run('fibonacci', 50); -console.log(result); // 12586269025 — UI never froze! +console.log(result); // 12586269025 : UI never froze! ``` ### React Usage @@ -158,7 +160,7 @@ function Calculator() { ### React + WASM (Full Example) -This is where ComputeKit shines — combining `useCompute` with WASM for native-speed performance: +This is where ComputeKit shines : combining `useCompute` with WASM for native-speed performance: ```tsx import { ComputeKitProvider, useComputeKit, useCompute } from '@computekit/react'; @@ -252,7 +254,7 @@ function ImageProcessor() { **Key benefits:** -- WASM runs in a Web Worker via `useCompute` — UI stays responsive +- WASM runs in a Web Worker via `useCompute` : UI stays responsive - Same familiar `loading`, `data`, `error` pattern as other compute functions - WASM memory management encapsulated in the registered function - Can easily add progress reporting, cancellation, etc. @@ -323,11 +325,29 @@ const kit = new ComputeKit(options?: ComputeKitOptions); #### Options -| Option | Type | Default | Description | -| ------------ | --------- | ------------------------------- | ----------------------- | -| `maxWorkers` | `number` | `navigator.hardwareConcurrency` | Max workers in the pool | -| `timeout` | `number` | `30000` | Default timeout in ms | -| `debug` | `boolean` | `false` | Enable debug logging | +| Option | Type | Default | Description | +| -------------------- | ---------- | ------------------------------- | ----------------------------------- | +| `maxWorkers` | `number` | `navigator.hardwareConcurrency` | Max workers in the pool | +| `timeout` | `number` | `30000` | Default timeout in ms | +| `debug` | `boolean` | `false` | Enable debug logging | +| `remoteDependencies` | `string[]` | `[]` | External scripts to load in workers | + +### Remote Dependencies + +Load external libraries inside your workers: + +```typescript +const kit = new ComputeKit({ + remoteDependencies: [ + 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js', + ], +}); + +kit.register('processData', (data: number[]) => { + // @ts-ignore - lodash loaded via importScripts + return _.chunk(data, 3); +}); +``` #### Methods @@ -363,12 +383,15 @@ const { loading, // Boolean loading state error, // Error if failed progress, // Progress info + status, // 'idle' | 'running' | 'success' | 'error' | 'cancelled' run, // Function to execute reset, // Reset state cancel, // Cancel current operation } = useCompute(functionName, options?); ``` +```` + ### `useComputeCallback` Returns a memoized async function (similar to `useCallback`). @@ -376,7 +399,7 @@ Returns a memoized async function (similar to `useCallback`). ```typescript const calculate = useComputeCallback('sum'); const result = await calculate([1, 2, 3, 4, 5]); -``` +```` ### `usePoolStats` @@ -433,13 +456,13 @@ const wasmModule = await loadWasmModule('/compute/sum.wasm'); ## ⚡ Performance Tips -1. **Transfer large data** — Use typed arrays (Uint8Array, Float64Array) for automatic transfer optimization +1. **Transfer large data** : Use typed arrays (Uint8Array, Float64Array) for automatic transfer optimization -2. **Batch small operations** — Combine many small tasks into one larger task +2. **Batch small operations** : Combine many small tasks into one larger task -3. **Right-size your pool** — More workers ≠ better. Match to CPU cores. +3. **Right-size your pool** : More workers ≠ better. Match to CPU cores. -4. **Use WASM for math** — AssemblyScript functions can be 10-100x faster for numeric work +4. **Use WASM for math** : AssemblyScript functions can be 10-100x faster for numeric work ```typescript // ❌ Slow: Many small calls @@ -500,13 +523,14 @@ computekit/ │ └── package.json │ ├── compute/ # AssemblyScript functions +│ ├── blur.ts │ ├── fibonacci.ts │ ├── mandelbrot.ts -│ └── matrix.ts +│ ├── matrix.ts +│ └── sum.ts │ ├── examples/ -│ ├── react-demo/ # React example app -│ └── vanilla-demo/ # Vanilla JS example +│ └── react-demo/ # React example app │ └── docs/ # Documentation ``` @@ -519,8 +543,8 @@ Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) ```bash # Clone the repo -git clone https://github.com/your-username/computekit.git -cd computekit +git clone https://github.com/tapava/compute-kit.git +cd compute-kit # Install dependencies npm install @@ -539,7 +563,7 @@ npm test ## 📄 License -MIT © [Your Name](https://github.com/your-username) +MIT © [Ghassen Lassoued](https://github.com/tapava) --- @@ -548,6 +572,7 @@ MIT © [Your Name](https://github.com/your-username) Built with ❤️ for the web platform

- ⭐ Star on GitHub + 📚 Read the Docs • + ⭐ Star on GitHub

diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..c806272a --- /dev/null +++ b/TODO.md @@ -0,0 +1,26 @@ +# ComputeKit TODO + +## Features + +- [ ] **Progress throttling** - Add optional `progressThrottle` option to prevent state flooding when compute functions fire progress updates in tight loops. Should throttle/debounce progress callbacks to avoid choking the main thread with re-renders. + - Add `progressThrottle?: number` option (ms) to `ComputeOptions` + - Throttle `onProgress` calls in the React hook + - Consider both throttle (regular intervals) and debounce (wait for pause) strategies + +- [ ] **Typed registry** - Add TypeScript support to narrow function names to only registered functions for autocomplete and type safety. + - Make `useCompute('functionName')` autocomplete only registered function names + - Type safety on input/output based on registered function signatures + - Consider using TypeScript's string literal types or const assertions + +## Improvements + +- [ ] Add more WASM examples (Rust, C++) +- [ ] Benchmark suite for comparing JS vs WASM performance +- [ ] Documentation site (Docusaurus or similar) + +## Ideas + +- [ ] `useComputeMultiple` hook for managing multiple parallel tasks +- [ ] `useComputeFile` hook for loading functions from separate files +- [ ] Built-in caching for compute results +- [ ] Streaming results for very large outputs diff --git a/packages/wasm/assembly/index.ts b/compute/blur.ts similarity index 100% rename from packages/wasm/assembly/index.ts rename to compute/blur.ts diff --git a/compute/index.ts b/compute/index.ts index 9bfc9f17..8a25f047 100644 --- a/compute/index.ts +++ b/compute/index.ts @@ -15,3 +15,4 @@ export { vectorMagnitude, vectorNormalize, } from './matrix'; +export { getBufferPtr, blurImage } from './blur'; diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..e913d3b1 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,6 @@ +# Ignore Jekyll build output +_site/ +.sass-cache/ +.jekyll-cache/ +.jekyll-metadata +vendor/ diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 00000000..23ecc604 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "jekyll", "~> 4.3" +gem "just-the-docs", "~> 0.8" +gem "jekyll-seo-tag" +gem "jekyll-include-cache" diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..62f77d1c --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,67 @@ +title: ComputeKit +description: The React-first toolkit for WASM and Web Workers +url: 'https://tapava.github.io' +baseurl: '/compute-kit' + +remote_theme: just-the-docs/just-the-docs@v0.8.2 + +color_scheme: light + +logo: '/assets/logo.svg' + +aux_links: + GitHub: https://github.com/tapava/compute-kit + npm: https://www.npmjs.com/package/@computekit/core + +aux_links_new_tab: true + +heading_anchors: true + +search_enabled: true +search: + heading_level: 2 + previews: 3 + preview_words_before: 5 + preview_words_after: 10 + tokenizer_separator: /[\s/]+/ + rel_url: true + button: false + +nav_enabled: true +nav_sort: case_insensitive + +back_to_top: true +back_to_top_text: 'Back to top' + +footer_content: 'Copyright © 2024-2025 Ghassen Lassoued. Distributed under the MIT license.' + +ga_tracking: +ga_tracking_anonymize_ip: true + +callouts: + warning: + title: Warning + color: yellow + note: + title: Note + color: blue + tip: + title: Tip + color: green + +plugins: + - jekyll-seo-tag + - jekyll-include-cache + +kramdown: + syntax_highlighter_opts: + block: + line_numbers: false + +compress_html: + clippings: all + comments: all + endings: all + startings: [] + blanklines: false + profile: false diff --git a/docs/_sass/custom/custom.scss b/docs/_sass/custom/custom.scss new file mode 100644 index 00000000..952f62a1 --- /dev/null +++ b/docs/_sass/custom/custom.scss @@ -0,0 +1,612 @@ +// Modern ComputeKit Theme - Inspired by TanStack +// Clean, professional, and readable + +// Import Inter font for modern typography +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap'); + +// Root variables +:root { + // Colors - Modern purple/blue accent like TanStack + --color-primary: #7c3aed; + --color-primary-light: #a78bfa; + --color-primary-dark: #5b21b6; + --color-accent: #06b6d4; + + // Background + --color-bg: #ffffff; + --color-bg-secondary: #f8fafc; + --color-bg-tertiary: #f1f5f9; + + // Text + --color-text: #1e293b; + --color-text-secondary: #64748b; + --color-text-muted: #94a3b8; + + // Borders + --color-border: #e2e8f0; + --color-border-light: #f1f5f9; + + // Code + --color-code-bg: #f8fafc; + --color-code-border: #e2e8f0; + + // Shadows + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1); + + // Fonts + --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', monospace; +} + +// Base typography +body { + font-family: var(--font-body) !important; + font-size: 16px; + line-height: 1.7; + color: var(--color-text); + background-color: var(--color-bg); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +// Headings +h1, +h2, +h3, +h4, +h5, +h6, +.site-title { + font-family: var(--font-body) !important; + font-weight: 700; + color: var(--color-text); + letter-spacing: -0.02em; +} + +h1 { + font-size: 2.5rem !important; + margin-bottom: 1rem; +} + +h2 { + font-size: 1.75rem !important; + margin-top: 2.5rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--color-border); +} + +h3 { + font-size: 1.35rem !important; + margin-top: 2rem; +} + +// Sidebar styles +.side-bar { + background-color: var(--color-bg-secondary) !important; + border-right: 1px solid var(--color-border); +} + +.site-title { + font-size: 1.25rem !important; + font-weight: 700 !important; + color: var(--color-primary) !important; +} + +.site-logo { + max-height: 2.5rem; +} + +// Navigation +.nav-list { + font-size: 0.9rem; + + .nav-list-item { + margin: 0; + + .nav-list-link { + padding: 0.5rem 1rem; + border-radius: 0.5rem; + margin: 0.125rem 0.5rem; + color: var(--color-text-secondary); + font-weight: 500; + transition: all 0.15s ease; + + &:hover { + background-color: var(--color-bg-tertiary); + color: var(--color-primary); + } + + &.active { + background-color: rgba(124, 58, 237, 0.1); + color: var(--color-primary); + font-weight: 600; + } + } + } +} + +// Main content +.main { + background-color: var(--color-bg); +} + +.main-content { + max-width: 52rem; + padding: 2rem 3rem; + + p { + margin-bottom: 1.25rem; + color: var(--color-text); + } + + a { + color: var(--color-primary); + text-decoration: none; + font-weight: 500; + + &:hover { + text-decoration: underline; + color: var(--color-primary-dark); + } + } +} + +// Code blocks +code { + font-family: var(--font-mono) !important; + font-size: 0.875em; + background-color: var(--color-code-bg); + padding: 0.2em 0.4em; + border-radius: 0.375rem; + color: var(--color-primary-dark); +} + +pre { + background-color: #0d1117 !important; + border: none !important; + border-radius: 0.75rem !important; + padding: 1.25rem !important; + margin: 1.5rem 0 !important; + box-shadow: var(--shadow-md); + overflow-x: auto; + + code { + background-color: transparent !important; + color: #e6edf3 !important; + padding: 0; + font-size: 0.875rem; + line-height: 1.6; + } +} + +// Syntax highlighting - GitHub Dark theme (high contrast) +.highlight { + background-color: #0d1117 !important; + border-radius: 0.75rem; + + // Plain text + .p, + .w { + color: #e6edf3; + } + + // Comments + .c, + .c1, + .cm, + .cs, + .cp { + color: #8b949e; + font-style: italic; + } + + // Keywords (import, from, const, function, async, await, return) + .k, + .kd, + .kn, + .kp, + .kr, + .kc, + .kt { + color: #ff7b72; + } + + // Strings + .s, + .s1, + .s2, + .sb, + .sc, + .sd, + .se, + .sh, + .si, + .sx, + .sr, + .ss { + color: #a5d6ff; + } + + // Function names and method calls + .nf, + .fm { + color: #d2a8ff; + } + + // Class names and types + .nc, + .nn, + .nx { + color: #ffa657; + } + + // Variables and identifiers + .n, + .na, + .nb, + .ni, + .nl, + .no, + .nv, + .ne, + .nd { + color: #e6edf3; + } + + // HTML/JSX tags + .nt { + color: #7ee787; + } + + // Numbers + .m, + .mf, + .mh, + .mi, + .mo, + .il { + color: #79c0ff; + } + + // Operators + .o, + .ow { + color: #ff7b72; + } + + // Punctuation (braces, parentheses, etc) + .p { + color: #e6edf3; + } + + // Property names + .py, + .na { + color: #79c0ff; + } + + // Special - imports and exports + .kn { + color: #ff7b72; + } + + // Decorators + .nd { + color: #d2a8ff; + } + + // Built-in functions + .nb { + color: #ffa657; + } + + // Error + .err { + color: #ffa198; + background-color: transparent; + } +} + +// Tables +table { + border-collapse: collapse; + width: 100%; + margin: 1.5rem 0; + font-size: 0.9rem; + border-radius: 0.5rem; + overflow: hidden; + box-shadow: var(--shadow-sm); + border: 1px solid var(--color-border); +} + +th { + background-color: var(--color-bg-tertiary); + font-weight: 600; + text-align: left; + padding: 0.75rem 1rem; + border-bottom: 2px solid var(--color-border); +} + +td { + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--color-border-light); +} + +tr:last-child td { + border-bottom: none; +} + +tr:hover { + background-color: var(--color-bg-secondary); +} + +// Buttons +.btn { + font-family: var(--font-body); + font-weight: 600; + font-size: 0.9rem; + padding: 0.625rem 1.25rem; + border-radius: 0.5rem; + transition: all 0.15s ease; + text-decoration: none !important; +} + +.btn-primary { + background-color: var(--color-primary) !important; + color: white !important; + border: none; + box-shadow: var(--shadow-sm); + + &:hover { + background-color: var(--color-primary-dark) !important; + transform: translateY(-1px); + box-shadow: var(--shadow-md); + } +} + +// Callouts +.warning, +.note, +.tip { + border-radius: 0.75rem; + padding: 1rem 1.25rem; + margin: 1.5rem 0; + border-left: 4px solid; + + p:last-child { + margin-bottom: 0; + } +} + +.note { + background-color: #eff6ff; + border-left-color: #3b82f6; +} + +.warning { + background-color: #fef3c7; + border-left-color: #f59e0b; +} + +.tip { + background-color: #ecfdf5; + border-left-color: #10b981; +} + +// Search +.search { + position: relative; + margin-bottom: 1rem; + padding: 0 1rem; +} + +.search-input-wrap { + display: flex; + align-items: center; + position: relative; +} + +.search-input { + font-family: var(--font-body); + width: 100%; + padding: 0.625rem 1rem 0.625rem 2.5rem; + font-size: 0.9rem; + background-color: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: 0.5rem; + color: var(--color-text); + transition: all 0.15s ease; + + &::placeholder { + color: var(--color-text-muted); + } + + &:focus { + border-color: var(--color-primary); + box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1); + outline: none; + } +} + +.search-label { + position: absolute; + left: 0.75rem; + top: 50%; + transform: translateY(-50%); + color: var(--color-text-muted); + pointer-events: none; + + .search-icon { + width: 1rem; + height: 1rem; + } +} + +.search-results { + position: absolute; + top: 100%; + left: 0; + right: 0; + margin-top: 0.5rem; + background-color: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: 0.5rem; + box-shadow: var(--shadow-md); + max-height: 20rem; + overflow-y: auto; + z-index: 100; +} + +.search-results-list { + list-style: none; + padding: 0.5rem; + margin: 0; +} + +.search-results-list-item { + padding: 0.5rem 0.75rem; + border-radius: 0.375rem; + cursor: pointer; + + &:hover, + &.active { + background-color: var(--color-bg-tertiary); + } +} + +.search-result-title { + font-weight: 600; + color: var(--color-text); + font-size: 0.9rem; +} + +.search-result-doc { + font-size: 0.8rem; + color: var(--color-text-secondary); + margin-top: 0.125rem; + + .search-result-doc-title { + font-weight: 500; + } +} + +.search-result-previews { + font-size: 0.8rem; + color: var(--color-text-muted); + margin-top: 0.25rem; +} + +.search-result-preview + .search-result-preview { + margin-top: 0.125rem; +} + +.search-result-highlight { + background-color: rgba(124, 58, 237, 0.15); + color: var(--color-primary-dark); + padding: 0.125em 0.25em; + border-radius: 0.25rem; +} + +.search-no-result { + padding: 1rem; + text-align: center; + color: var(--color-text-muted); + font-size: 0.9rem; +} + +// TOC +.toc { + background-color: var(--color-bg-secondary); + border-radius: 0.75rem; + padding: 1rem 1.25rem; + border: 1px solid var(--color-border); +} + +// Footer +.site-footer { + border-top: 1px solid var(--color-border); + color: var(--color-text-muted); + font-size: 0.875rem; +} + +// Aux links (GitHub, npm) +.aux-nav { + .aux-nav-list-item { + a { + font-size: 0.875rem; + font-weight: 500; + color: var(--color-text-secondary); + + &:hover { + color: var(--color-primary); + } + } + } +} + +// Home page hero adjustments +.fs-9 { + font-size: 3rem !important; + font-weight: 800 !important; + background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.fs-6 { + font-size: 1.35rem !important; + color: var(--color-text-secondary) !important; +} + +// Lists +ul, +ol { + margin-bottom: 1.25rem; + + li { + margin-bottom: 0.5rem; + + &::marker { + color: var(--color-primary); + } + } +} + +// Blockquotes +blockquote { + border-left: 3px solid var(--color-primary); + padding-left: 1rem; + color: var(--color-text-secondary); + font-style: italic; +} + +// Back to top button +.back-to-top { + background-color: var(--color-primary); + color: white; + border-radius: 9999px; + padding: 0.5rem 1rem; + font-size: 0.875rem; + font-weight: 500; + + &:hover { + background-color: var(--color-primary-dark); + } +} + +// Responsive +@media (max-width: 800px) { + .main-content { + padding: 1.5rem; + } + + h1 { + font-size: 2rem !important; + } + + .fs-9 { + font-size: 2.25rem !important; + } +} diff --git a/docs/api-reference.md b/docs/api-reference.md new file mode 100644 index 00000000..5635d46f --- /dev/null +++ b/docs/api-reference.md @@ -0,0 +1,333 @@ +--- +layout: default +title: API Reference +nav_order: 4 +description: 'Complete API reference for ComputeKit' +permalink: /api-reference +--- + +# API Reference + +{: .no_toc } + +Complete API documentation for the ComputeKit core library. +{: .fs-6 .fw-300 } + + +
+ Table of contents + {: .text-delta } +- TOC +{:toc} +
+ +--- + +## ComputeKit Class + +The main entry point for using ComputeKit. + +### Constructor + +```typescript +new ComputeKit(options?: ComputeKitOptions) +``` + +### ComputeKitOptions + +| Property | Type | Default | Description | +| -------------------- | ---------- | -------------------------------------- | ------------------------------------- | +| `maxWorkers` | `number` | `navigator.hardwareConcurrency \|\| 4` | Maximum number of workers in the pool | +| `timeout` | `number` | `30000` | Default timeout for operations (ms) | +| `debug` | `boolean` | `false` | Enable debug logging | +| `workerPath` | `string` | `''` | Custom path to worker script | +| `useSharedMemory` | `boolean` | `true` | Use SharedArrayBuffer when available | +| `remoteDependencies` | `string[]` | `[]` | External scripts to load in workers | + +--- + +## Methods + +### initialize() + +Manually initialize the worker pool. Called automatically on first `run()`. + +```typescript +const kit = new ComputeKit(); +await kit.initialize(); // Optional: eager initialization +``` + +### register() + +Register a compute function. + +```typescript +register( + name: string, + fn: (input: TInput, context: ComputeContext) => TOutput | Promise +): this +``` + +**Parameters:** + +- `name` - Unique identifier for the function +- `fn` - The function to execute (runs in a Web Worker) + +**Returns:** `this` (for chaining) + +```typescript +kit.register('double', (n: number) => n * 2); + +kit.register('asyncTask', async (data, { reportProgress }) => { + // Report progress during long operations + reportProgress({ percent: 50 }); + return await processData(data); +}); + +// Chaining +kit.register('add', (a, b) => a + b).register('multiply', (a, b) => a * b); +``` + +### run() + +Execute a registered function. + +```typescript +run( + name: string, + input: TInput, + options?: ComputeOptions +): Promise +``` + +**Parameters:** + +- `name` - Name of the registered function +- `input` - Input data (will be serialized) +- `options` - Optional execution options + +```typescript +const result = await kit.run('double', 21); +console.log(result); // 42 +``` + +### runWithMetadata() + +Execute a function and receive metadata about the execution. + +```typescript +runWithMetadata( + name: string, + input: TInput, + options?: ComputeOptions +): Promise> +``` + +```typescript +const result = await kit.runWithMetadata('heavy', data); +console.log(`Took ${result.duration}ms on worker ${result.workerId}`); +``` + +### getStats() + +Get current worker pool statistics. + +```typescript +const stats = kit.getStats(); +console.log(`Active workers: ${stats.activeWorkers}`); +console.log(`Queue length: ${stats.queueLength}`); +``` + +### isWasmSupported() + +Check if WebAssembly is supported in the current environment. + +```typescript +if (kit.isWasmSupported()) { + // Load WASM module +} +``` + +### terminate() + +Terminate all workers and clean up resources. + +```typescript +await kit.terminate(); +``` + +--- + +## ComputeOptions + +Options for individual compute operations. + +| Property | Type | Description | +| ------------ | ------------------------------------- | ------------------------------------- | +| `timeout` | `number` | Operation timeout in ms | +| `transfer` | `ArrayBuffer[]` | ArrayBuffers to transfer (not copy) | +| `priority` | `number` | Priority level (0-10, higher = first) | +| `signal` | `AbortSignal` | Abort signal for cancellation | +| `onProgress` | `(progress: ComputeProgress) => void` | Progress callback | + +```typescript +const controller = new AbortController(); + +await kit.run('task', data, { + timeout: 5000, + priority: 10, + signal: controller.signal, + onProgress: (p) => console.log(`${p.percent}%`), +}); + +// Cancel the operation +controller.abort(); +``` + +--- + +## ComputeProgress + +Progress information for long-running tasks. + +| Property | Type | Description | +| ------------------------ | ---------- | --------------------------- | +| `percent` | `number` | Progress percentage (0-100) | +| `phase` | `string?` | Current phase name | +| `estimatedTimeRemaining` | `number?` | Estimated ms remaining | +| `data` | `unknown?` | Additional custom data | + +--- + +## ComputeResult + +Result wrapper with execution metadata. + +| Property | Type | Description | +| ---------- | --------- | ------------------------------------ | +| `data` | `T` | The computed result | +| `duration` | `number` | Execution time in ms | +| `cached` | `boolean` | Whether result was cached | +| `workerId` | `string` | ID of the worker that processed this | + +--- + +## PoolStats + +Worker pool statistics. + +| Property | Type | Description | +| --------------------- | -------------- | -------------------------- | +| `workers` | `WorkerInfo[]` | Info about each worker | +| `totalWorkers` | `number` | Total worker count | +| `activeWorkers` | `number` | Currently busy workers | +| `idleWorkers` | `number` | Currently idle workers | +| `queueLength` | `number` | Tasks waiting in queue | +| `tasksCompleted` | `number` | Total completed tasks | +| `tasksFailed` | `number` | Total failed tasks | +| `averageTaskDuration` | `number` | Average task duration (ms) | + +--- + +## Event Handling + +ComputeKit extends EventEmitter and emits events: + +```typescript +kit.on('worker:created', (info) => { + console.log('New worker:', info.id); +}); + +kit.on('worker:terminated', (info) => { + console.log('Worker terminated:', info.id); +}); + +kit.on('task:start', (taskId, name) => { + console.log(`Starting ${name}`); +}); + +kit.on('task:complete', (taskId, duration) => { + console.log(`Done in ${duration}ms`); +}); + +kit.on('task:error', (taskId, error) => { + console.error('Task failed:', error); +}); + +kit.on('task:progress', (taskId, progress) => { + console.log(`${progress.percent}%`); +}); +``` + +--- + +## Utility Functions + +### isWasmSupported() + +Check if WebAssembly is available. + +```typescript +import { isWasmSupported } from '@computekit/core'; + +if (isWasmSupported()) { + // Use WASM +} +``` + +### isSharedArrayBufferAvailable() + +Check if SharedArrayBuffer is available. + +```typescript +import { isSharedArrayBufferAvailable } from '@computekit/core'; + +if (isSharedArrayBufferAvailable()) { + // Use shared memory +} +``` + +### getHardwareConcurrency() + +Get the number of logical CPU cores. + +```typescript +import { getHardwareConcurrency } from '@computekit/core'; + +const cores = getHardwareConcurrency(); +console.log(`${cores} CPU cores available`); +``` + +### findTransferables() + +Detect transferable objects in data for efficient worker communication. + +```typescript +import { findTransferables } from '@computekit/core'; + +const data = { buffer: new ArrayBuffer(1024), values: [1, 2, 3] }; +const transferables = findTransferables(data); +// [ArrayBuffer(1024)] +``` + +--- + +## Error Handling + +ComputeKit throws errors in these cases: + +```typescript +try { + await kit.run('unknown', data); +} catch (error) { + if (error.message.includes('not registered')) { + // Function not registered + } else if (error.message.includes('timed out')) { + // Timeout + } else if (error.message.includes('aborted')) { + // Cancelled via AbortSignal + } else { + // Worker error + } +} +``` diff --git a/docs/api.md b/docs/api.md index a9814dad..ab2a40c1 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1,3 +1,11 @@ +--- +layout: default +title: Core API (Detailed) +nav_order: 8 +description: 'Detailed API reference for @computekit/core' +permalink: /core-api +--- + # @computekit/core API Reference Complete API documentation for the ComputeKit core library. @@ -24,13 +32,13 @@ new ComputeKit(options?: ComputeKitOptions) #### ComputeKitOptions -| Property | Type | Default | Description | -|----------|------|---------|-------------| -| `maxWorkers` | `number` | `navigator.hardwareConcurrency \|\| 4` | Maximum number of workers in the pool | -| `timeout` | `number` | `30000` | Default timeout for operations (ms) | -| `debug` | `boolean` | `false` | Enable debug logging | -| `workerPath` | `string` | `''` | Custom path to worker script | -| `useSharedMemory` | `boolean` | `true` | Use SharedArrayBuffer when available | +| Property | Type | Default | Description | +| ----------------- | --------- | -------------------------------------- | ------------------------------------- | +| `maxWorkers` | `number` | `navigator.hardwareConcurrency \|\| 4` | Maximum number of workers in the pool | +| `timeout` | `number` | `30000` | Default timeout for operations (ms) | +| `debug` | `boolean` | `false` | Enable debug logging | +| `workerPath` | `string` | `''` | Custom path to worker script | +| `useSharedMemory` | `boolean` | `true` | Use SharedArrayBuffer when available | ### Methods @@ -56,6 +64,7 @@ kit.register('asyncTask', async (data) => { ``` **Parameters:** + - `name` - Unique identifier for the function - `fn` - The function to execute (runs in a Web Worker) @@ -71,6 +80,7 @@ console.log(result); // 42 ``` **Parameters:** + - `name` - Name of the registered function - `input` - Input data (will be serialized) - `options` - Optional execution options @@ -115,13 +125,13 @@ await kit.terminate(); Options for individual compute operations. -| Property | Type | Description | -|----------|------|-------------| -| `timeout` | `number` | Operation timeout in ms | -| `transfer` | `ArrayBuffer[]` | ArrayBuffers to transfer (not copy) | -| `priority` | `number` | Priority level (0-10, higher = first) | -| `signal` | `AbortSignal` | Abort signal for cancellation | -| `onProgress` | `(progress: ComputeProgress) => void` | Progress callback | +| Property | Type | Description | +| ------------ | ------------------------------------- | ------------------------------------- | +| `timeout` | `number` | Operation timeout in ms | +| `transfer` | `ArrayBuffer[]` | ArrayBuffers to transfer (not copy) | +| `priority` | `number` | Priority level (0-10, higher = first) | +| `signal` | `AbortSignal` | Abort signal for cancellation | +| `onProgress` | `(progress: ComputeProgress) => void` | Progress callback | ```typescript const controller = new AbortController(); @@ -140,12 +150,12 @@ await kit.run('task', data, { Progress information for long-running tasks. -| Property | Type | Description | -|----------|------|-------------| -| `percent` | `number` | Progress percentage (0-100) | -| `phase` | `string?` | Current phase name | -| `estimatedTimeRemaining` | `number?` | Estimated ms remaining | -| `data` | `unknown?` | Additional data | +| Property | Type | Description | +| ------------------------ | ---------- | --------------------------- | +| `percent` | `number` | Progress percentage (0-100) | +| `phase` | `string?` | Current phase name | +| `estimatedTimeRemaining` | `number?` | Estimated ms remaining | +| `data` | `unknown?` | Additional data | --- @@ -153,12 +163,12 @@ Progress information for long-running tasks. Result wrapper with execution metadata. -| Property | Type | Description | -|----------|------|-------------| -| `data` | `T` | The computed result | -| `duration` | `number` | Execution time in ms | -| `cached` | `boolean` | Whether result was cached | -| `workerId` | `string` | ID of the worker that processed this | +| Property | Type | Description | +| ---------- | --------- | ------------------------------------ | +| `data` | `T` | The computed result | +| `duration` | `number` | Execution time in ms | +| `cached` | `boolean` | Whether result was cached | +| `workerId` | `string` | ID of the worker that processed this | --- @@ -166,16 +176,16 @@ Result wrapper with execution metadata. Worker pool statistics. -| Property | Type | Description | -|----------|------|-------------| -| `workers` | `WorkerInfo[]` | Info about each worker | -| `totalWorkers` | `number` | Total worker count | -| `activeWorkers` | `number` | Currently busy workers | -| `idleWorkers` | `number` | Currently idle workers | -| `queueLength` | `number` | Tasks waiting in queue | -| `tasksCompleted` | `number` | Total completed tasks | -| `tasksFailed` | `number` | Total failed tasks | -| `averageTaskDuration` | `number` | Average task duration (ms) | +| Property | Type | Description | +| --------------------- | -------------- | -------------------------- | +| `workers` | `WorkerInfo[]` | Info about each worker | +| `totalWorkers` | `number` | Total worker count | +| `activeWorkers` | `number` | Currently busy workers | +| `idleWorkers` | `number` | Currently idle workers | +| `queueLength` | `number` | Tasks waiting in queue | +| `tasksCompleted` | `number` | Total completed tasks | +| `tasksFailed` | `number` | Total failed tasks | +| `averageTaskDuration` | `number` | Average task duration (ms) | --- @@ -192,7 +202,7 @@ Load a WASM module from various sources. const module = await loadWasmModule('/path/to/module.wasm'); // From ArrayBuffer -const bytes = await fetch('/module.wasm').then(r => r.arrayBuffer()); +const bytes = await fetch('/module.wasm').then((r) => r.arrayBuffer()); const module = await loadWasmModule(bytes); // From base64 @@ -207,7 +217,7 @@ Load and instantiate a WASM module. const { module, instance } = await loadAndInstantiate({ source: '/module.wasm', imports: { - env: { log: console.log } + env: { log: console.log }, }, memory: { initial: 256, diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg new file mode 100644 index 00000000..da53943e --- /dev/null +++ b/docs/assets/logo.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/debugging.md b/docs/debugging.md new file mode 100644 index 00000000..aa840181 --- /dev/null +++ b/docs/debugging.md @@ -0,0 +1,462 @@ +--- +layout: default +title: Debugging +nav_order: 8 +--- + +# Debugging ComputeKit + +{: .no_toc } + +Learn how to debug worker code, understand errors, and troubleshoot issues in ComputeKit. +{: .fs-6 .fw-300 } + + +
+ Table of contents + {: .text-delta } +- TOC +{:toc} +
+ +--- + +## Overview + +Debugging Web Workers can be tricky because they run in a separate thread with their own global scope. ComputeKit provides several tools to make debugging easier: + +- Enhanced error messages with context +- Debug mode with verbose logging +- Console forwarding from workers +- Chrome DevTools integration +- Validation mode for main-thread debugging + +--- + +## Enable Debug Mode + +The simplest way to start debugging is to enable debug mode: + +```typescript +import { ComputeKit } from '@computekit/core'; + +const kit = new ComputeKit({ + debug: true, // Enable verbose logging +}); +``` + +With React: + +```tsx + + + +``` + +Debug mode logs: + +- Worker creation and termination +- Function registration +- Task execution (start, complete, error) +- Message passing between main thread and workers +- Payload sizes for data transfer + +--- + +## Understanding Error Messages + +When a compute function throws an error, ComputeKit captures and enriches it with context: + +```typescript +kit.register('riskyFunction', (input: number) => { + if (input < 0) { + throw new Error('Input must be non-negative'); + } + return Math.sqrt(input); +}); + +try { + await kit.run('riskyFunction', -5); +} catch (error) { + console.error(error); + // ComputeError: Input must be non-negative + // Function: riskyFunction + // Worker: worker-abc123 + // Duration: 2ms + // Stack: ...original stack trace... +} +``` + +### Error Properties + +All errors from ComputeKit include: + +| Property | Description | +| -------------- | ---------------------------------------- | +| `message` | The original error message | +| `functionName` | Name of the compute function that failed | +| `workerId` | ID of the worker that processed the task | +| `duration` | Time elapsed before the error occurred | +| `stack` | Full stack trace from the worker | +| `inputSize` | Size of the input data (in debug mode) | + +--- + +## Chrome DevTools Integration + +### Debugging Workers Directly + +Chrome DevTools supports debugging Web Workers: + +1. Open DevTools (F12) +2. Go to **Sources** tab +3. In the left panel, find **Threads** section +4. Click on a worker thread to debug it + +### Setting Breakpoints + +To set breakpoints in your compute functions: + +1. Enable source maps in your bundler (see below) +2. In DevTools Sources, find your worker code +3. Set breakpoints as normal +4. Use the worker thread selector to switch contexts + +### Console Output + +Worker `console.log()` calls appear in the main DevTools console, prefixed with the worker context. Enable **Verbose** log level to see all worker output: + +```typescript +kit.register('debugMe', (input: number[]) => { + console.log('Processing', input.length, 'items'); // Visible in DevTools + console.time('processing'); + + const result = input.map((x) => x * 2); + + console.timeEnd('processing'); + console.log('Result:', result.slice(0, 5), '...'); + + return result; +}); +``` + +--- + +## Source Maps + +For the best debugging experience, configure your bundler to generate source maps for workers. + +### Vite + +```typescript +// vite.config.ts +export default defineConfig({ + build: { + sourcemap: true, + }, + worker: { + format: 'es', + rollupOptions: { + output: { + sourcemap: true, + }, + }, + }, +}); +``` + +### Webpack + +```javascript +// webpack.config.js +module.exports = { + devtool: 'source-map', + module: { + rules: [ + { + test: /\.worker\.ts$/, + use: { + loader: 'worker-loader', + options: { + inline: 'fallback', + }, + }, + }, + ], + }, +}; +``` + +### esbuild + +```typescript +import * as esbuild from 'esbuild'; + +await esbuild.build({ + entryPoints: ['src/index.ts'], + bundle: true, + sourcemap: true, + outfile: 'dist/bundle.js', +}); +``` + +--- + +## Validation Mode (Main Thread Execution) + +For difficult bugs, run your compute function on the main thread to use standard debugging tools: + +```typescript +import { ComputeKit } from '@computekit/core'; + +const kit = new ComputeKit({ + // Force main thread execution (bypasses workers) + forceMainThread: true, +}); + +// Now you can set breakpoints directly in your compute functions +kit.register('myFunction', (input) => { + debugger; // This will pause execution in DevTools + return processData(input); +}); +``` + +{: .warning } + +> **forceMainThread** should only be used for debugging. It defeats the purpose of using workers and will block the UI thread. + +--- + +## Common Issues + +### "Function not found in worker" + +This error occurs when a compute function isn't registered before calling `run()`: + +```typescript +// ❌ Wrong - function not registered +await kit.run('myFunction', data); + +// ✅ Correct - register first +kit.register('myFunction', (data) => processData(data)); +await kit.run('myFunction', data); +``` + +**In React**, ensure registration happens before the component that uses the function mounts: + +```tsx +// ❌ Wrong - might race with child components +function App() { + const kit = useComputeKit(); + + useEffect(() => { + kit.register('myFunction', fn); + }, [kit]); + + return ; +} + +// ✅ Correct - use a registration component or context +function App() { + return ( + + + + + ); +} + +function RegisterFunctions() { + const kit = useComputeKit(); + + useEffect(() => { + kit.register('myFunction', fn); + }, [kit]); + + return null; +} +``` + +### "DataCloneError: Failed to execute 'postMessage'" + +This error occurs when trying to transfer non-cloneable data: + +```typescript +// ❌ Wrong - functions can't be cloned +kit.register('bad', () => { + return { + data: [1, 2, 3], + callback: () => console.log('hi'), // Can't clone functions! + }; +}); + +// ✅ Correct - return only cloneable data +kit.register('good', () => { + return { + data: [1, 2, 3], + callbackName: 'logHi', // Return a reference instead + }; +}); +``` + +**Non-cloneable types include:** + +- Functions +- DOM nodes +- Error objects (use `{ message, stack }` instead) +- Symbols +- WeakMap/WeakSet + +### Timeout Errors + +If tasks are timing out unexpectedly: + +```typescript +// Increase timeout for long-running operations +const result = await kit.run('heavyComputation', data, { + timeout: 120000, // 2 minutes +}); + +// Or set a global default +const kit = new ComputeKit({ + timeout: 60000, // 1 minute default +}); +``` + +### Worker Creation Failures + +If workers fail to create, check: + +1. **Content Security Policy** - Ensure your CSP allows `worker-src 'self' blob:` +2. **Cross-origin issues** - Workers must be same-origin +3. **Memory limits** - Each worker uses ~2-5MB of memory + +--- + +## Logging Events + +Subscribe to ComputeKit events for detailed logging: + +```typescript +const kit = new ComputeKit(); + +kit.on('worker:created', ({ info }) => { + console.log(`Worker ${info.id} created`); +}); + +kit.on('worker:error', ({ error, info }) => { + console.error(`Worker ${info.id} error:`, error); +}); + +kit.on('task:start', ({ taskId, functionName }) => { + console.log(`Task ${taskId} started: ${functionName}`); +}); + +kit.on('task:complete', ({ taskId, duration }) => { + console.log(`Task ${taskId} completed in ${duration}ms`); +}); + +kit.on('task:error', ({ taskId, error }) => { + console.error(`Task ${taskId} failed:`, error); +}); + +kit.on('task:progress', ({ taskId, progress }) => { + console.log(`Task ${taskId}: ${progress.percent}%`); +}); +``` + +--- + +## Pool Statistics + +Monitor worker pool health: + +```typescript +const stats = kit.getStats(); + +console.log(stats); +// { +// workers: [ +// { id: 'w1', state: 'idle', tasksCompleted: 42, errors: 0 }, +// { id: 'w2', state: 'busy', tasksCompleted: 38, errors: 1 }, +// ], +// totalWorkers: 2, +// activeWorkers: 1, +// idleWorkers: 1, +// queueLength: 0, +// tasksCompleted: 80, +// tasksFailed: 1, +// averageTaskDuration: 145, +// } +``` + +In React, use the `usePoolStats` hook: + +```tsx +function PoolMonitor() { + const stats = usePoolStats(); + + return ( +
+

+ Workers: {stats.activeWorkers}/{stats.totalWorkers} active +

+

Queue: {stats.queueLength} pending

+

Completed: {stats.tasksCompleted}

+

Failed: {stats.tasksFailed}

+
+ ); +} +``` + +--- + +## Debugging Pipelines + +For multi-stage pipelines, use the built-in debugging features: + +```tsx +const pipeline = usePipeline(stages); + +// Get a detailed report +const report = pipeline.getReport(); +console.log(report.summary); +console.log(report.timeline); +console.log(report.insights); + +// Access metrics +console.log(pipeline.metrics); +// { +// totalStages: 4, +// completedStages: 3, +// failedStages: 1, +// slowestStage: { id: 'process', duration: 2340 }, +// timeline: [...] +// } + +// Check individual stage status +pipeline.stages.forEach((stage) => { + console.log(`${stage.name}: ${stage.status}`); + if (stage.error) { + console.error(` Error: ${stage.error.message}`); + } + if (stage.duration) { + console.log(` Duration: ${stage.duration}ms`); + } +}); +``` + +--- + +## Getting Help + +If you're stuck: + +1. Enable `debug: true` and check the console +2. Check the [Common Issues](#common-issues) section +3. Use `forceMainThread: true` to debug on the main thread +4. [Open an issue](https://github.com/pzaino/compute-kit/issues) with: + - ComputeKit version + - Browser and version + - Minimal reproduction code + - Console output with debug mode enabled diff --git a/docs/demo.html b/docs/demo.html index 45ff747a..f85b473d 100644 --- a/docs/demo.html +++ b/docs/demo.html @@ -1,459 +1,594 @@ - + - - - + + + ComputeKit | WASM + Worker Toolkit + + +
+
+ + + + + ComputeKit Demo +
+ +
- .modal-header { - padding: 15px 20px; - border-bottom: 1px solid var(--border); - display: flex; - justify-content: space-between; - align-items: center; - } - .modal-body { - padding: 20px; - overflow-y: auto; - line-height: 1.6; - font-size: 0.95rem; - } - .modal-body code { background: rgba(110,118,129,0.2); padding: 2px 4px; border-radius: 4px; font-family: var(--font-code); } - .modal-body h3 { margin-top: 0; color: var(--text-main); } - - .close-btn { - background: none; border: none; color: var(--text-muted); - font-size: 1.5rem; cursor: pointer; - } +
+ - /* Responsive */ - @media (max-width: 800px) { - main { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; } - .sidebar { border-right: none; border-bottom: 1px solid var(--border); } - } - - - +
+ -
-
- - ComputeKit Demo +
+
+
Computing...
- -
-
- - -
- - -
-
-
Computing...
-
- -
-
- Time: 0ms -
-
- Mode: Idle -
-
- Resolution: 0x0 -
-
-
+
+
+ Time: 0ms +
+
+ Mode: Idle +
+
+ Resolution: 0x0 +
+
+