Showing preview only (2,891K chars total). Download the full file or copy to clipboard to get everything.
Repository: RSamaium/CanvasEngine
Branch: v2
Commit: 5e44e4a42782
Files: 320
Total size: 2.7 MB
Directory structure:
gitextract_dj33z3ap/
├── .agents/
│ └── skills/
│ ├── canvasengine/
│ │ ├── SKILL.md
│ │ └── agents/
│ │ └── openai.yaml
│ └── pixijs/
│ ├── SKILL.md
│ └── references/
│ └── index.md
├── .changeset/
│ └── config.json
├── .codex
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .npmignore
├── README.md
├── __mocks__/
│ └── pixi-viewport.ts
├── docs/
│ ├── .vitepress/
│ │ ├── config.ts
│ │ └── theme/
│ │ ├── Layout.vue
│ │ ├── components/
│ │ │ ├── CustomHome.vue
│ │ │ ├── Playground.vue
│ │ │ ├── config.ts
│ │ │ └── example.ts
│ │ ├── index.ts
│ │ └── style.css
│ ├── advanced/
│ │ ├── conditional-rendering.md
│ │ ├── performance.md
│ │ └── without-compiler.md
│ ├── api/
│ │ ├── context.md
│ │ ├── element.md
│ │ └── testing.md
│ ├── components/
│ │ ├── _display-object.md
│ │ ├── button.md
│ │ ├── canvas.md
│ │ ├── container.md
│ │ ├── dom-container.md
│ │ ├── examples/
│ │ │ ├── focus-navigation-dom.js
│ │ │ └── polygon-example.js
│ │ ├── graphic.md
│ │ ├── joystick.md
│ │ ├── mesh.md
│ │ ├── navigation.md
│ │ ├── nine-slice-sprite.md
│ │ ├── sprite.md
│ │ ├── svg.md
│ │ ├── text.md
│ │ ├── tiling-sprite.md
│ │ ├── video.md
│ │ └── viewport.md
│ ├── concepts/
│ │ ├── animation.md
│ │ ├── child-component.md
│ │ ├── context.md
│ │ ├── dependencies.md
│ │ ├── dynamic-components.md
│ │ ├── examples/
│ │ │ └── conditional-rendering.js
│ │ ├── lifecycle.md
│ │ ├── reactive.md
│ │ ├── ref.md
│ │ ├── slot.md
│ │ ├── styling.md
│ │ ├── template-syntax.md
│ │ └── trigger.md
│ ├── directives/
│ │ ├── controls.md
│ │ ├── drag.md
│ │ ├── flash.md
│ │ ├── shake.md
│ │ └── sound.md
│ ├── get_started/
│ │ ├── installation.md
│ │ ├── readme.md
│ │ └── start.md
│ ├── index.md
│ ├── package.json
│ ├── presets/
│ │ ├── _before.md
│ │ ├── bar.md
│ │ ├── fog-of-war.md
│ │ ├── footprints.md
│ │ ├── fx.md
│ │ ├── loading.md
│ │ ├── night-ambiant.md
│ │ ├── sprite-shadows.md
│ │ ├── tilemap.md
│ │ └── weather.md
│ └── public/
│ ├── [A]Dirt_pipo.tsx
│ ├── [A]Flower_pipo.tsx
│ ├── [A]Grass_pipo.tsx
│ ├── [A]Wall-Up_pipo.tsx
│ ├── [A]WaterFall_pipo.tsx
│ ├── [A]Water_pipo.tsx
│ ├── [Base]BaseChip_pipo.tsx
│ ├── grammar.pegjs
│ ├── map.tmx
│ ├── simplemap.tmx
│ ├── simplemap2.tmx
│ └── tileset.tsx
├── package.json
├── packages/
│ ├── compiler/
│ │ ├── grammar.pegjs
│ │ ├── grammar2.pegjs
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── tests/
│ │ │ ├── compiler.spec.ts
│ │ │ └── compiler2.spec.ts
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── core/
│ │ ├── index.d.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ ├── Button.ts
│ │ │ │ ├── Canvas.ts
│ │ │ │ ├── Container.ts
│ │ │ │ ├── DOMContainer.ts
│ │ │ │ ├── DOMElement.ts
│ │ │ │ ├── DOMSprite.ts
│ │ │ │ ├── DisplayObject.ts
│ │ │ │ ├── FocusContainer.ts
│ │ │ │ ├── Graphic.ts
│ │ │ │ ├── Joystick.ts
│ │ │ │ ├── Mesh.ts
│ │ │ │ ├── NineSliceSprite.ts
│ │ │ │ ├── ParticleEmitter.ts
│ │ │ │ ├── Scene.ts
│ │ │ │ ├── Sprite.ts
│ │ │ │ ├── Text.ts
│ │ │ │ ├── TilingSprite.ts
│ │ │ │ ├── Video.ts
│ │ │ │ ├── Viewport.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── types/
│ │ │ │ ├── DisplayObject.ts
│ │ │ │ ├── MouseEvent.ts
│ │ │ │ ├── Spritesheet.ts
│ │ │ │ └── index.ts
│ │ │ ├── directives/
│ │ │ │ ├── Controls.ts
│ │ │ │ ├── ControlsBase.ts
│ │ │ │ ├── Drag.ts
│ │ │ │ ├── Flash.ts
│ │ │ │ ├── FocusNavigation.ts
│ │ │ │ ├── FogVisibility.ts
│ │ │ │ ├── GamepadControls.ts
│ │ │ │ ├── JoystickControls.ts
│ │ │ │ ├── KeyboardControls.ts
│ │ │ │ ├── Scheduler.ts
│ │ │ │ ├── Shake.ts
│ │ │ │ ├── Sound.ts
│ │ │ │ ├── Transition.ts
│ │ │ │ ├── ViewportCull.ts
│ │ │ │ ├── ViewportFollow.ts
│ │ │ │ └── index.ts
│ │ │ ├── engine/
│ │ │ │ ├── FocusManager.ts
│ │ │ │ ├── animation.ts
│ │ │ │ ├── bootstrap.ts
│ │ │ │ ├── directive.ts
│ │ │ │ ├── reactive.ts
│ │ │ │ ├── signal.ts
│ │ │ │ ├── trigger.ts
│ │ │ │ └── utils.ts
│ │ │ ├── hooks/
│ │ │ │ ├── addContext.ts
│ │ │ │ ├── useFocus.ts
│ │ │ │ ├── useProps.ts
│ │ │ │ └── useRef.ts
│ │ │ ├── index.ts
│ │ │ ├── types/
│ │ │ │ └── pixi-cull.d.ts
│ │ │ └── utils/
│ │ │ ├── Ease.ts
│ │ │ ├── GlobalAssetLoader.ts
│ │ │ ├── RadialGradient.ts
│ │ │ ├── functions.ts
│ │ │ └── tabindex.ts
│ │ ├── testing/
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── presets/
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── Bar.ts
│ │ │ ├── Button.ts
│ │ │ ├── FogOfWar.ts
│ │ │ ├── Footprints.ts
│ │ │ ├── Loading.ts
│ │ │ ├── NightAmbiant.ts
│ │ │ ├── Particle.ts
│ │ │ ├── SpriteShadows.ts
│ │ │ ├── Tilemap/
│ │ │ │ ├── Tile.ts
│ │ │ │ ├── TileGroup.ts
│ │ │ │ ├── TileLayer.ts
│ │ │ │ ├── TileSet.ts
│ │ │ │ └── index.ts
│ │ │ ├── Weathers/
│ │ │ │ ├── fog.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── rain.ts
│ │ │ │ └── snow.ts
│ │ │ ├── fx/
│ │ │ │ ├── Fx.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── presets.ts
│ │ │ │ ├── runtime.ts
│ │ │ │ ├── textures.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── index.ts
│ │ │ └── shaders/
│ │ │ ├── defaultFilter.vert.glsl
│ │ │ ├── nightSpot.frag.glsl
│ │ │ └── shadowGradient.frag.glsl
│ │ ├── tsconfig.json
│ │ └── vite.config.ts
│ ├── testing/
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── helpers/
│ │ │ │ ├── createMockComponentInstance.ts
│ │ │ │ ├── createMockElement.ts
│ │ │ │ └── spyOnElement.ts
│ │ │ ├── index.ts
│ │ │ └── mocks/
│ │ │ └── pixi-base.ts
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ └── tiled/
│ ├── package.json
│ ├── readme.md
│ ├── src/
│ │ ├── classes/
│ │ │ ├── Gid.ts
│ │ │ ├── Layer.ts
│ │ │ ├── Map.ts
│ │ │ ├── Object.ts
│ │ │ ├── Properties.ts
│ │ │ ├── Tile.ts
│ │ │ └── Tileset.ts
│ │ ├── generate/
│ │ │ ├── tileset.ts
│ │ │ └── wangtile.ts
│ │ ├── index.ts
│ │ ├── parser/
│ │ │ ├── open-file.ts
│ │ │ └── parser.ts
│ │ ├── types/
│ │ │ ├── Layer.ts
│ │ │ ├── Map.ts
│ │ │ ├── Objects.ts
│ │ │ ├── Text.ts
│ │ │ ├── Tile.ts
│ │ │ ├── Tileset.ts
│ │ │ ├── Types.ts
│ │ │ └── WorldMaps.ts
│ │ └── utils.ts
│ ├── tests/
│ │ ├── class.spec.ts
│ │ ├── data.ts
│ │ ├── parser.spec.ts
│ │ ├── tile-properties.spec.ts
│ │ ├── tiledmap-multi-layers.spec.ts
│ │ └── tiledmap.spec.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── pnpm-workspace.yaml
├── sample/
│ ├── index.html
│ ├── package.json
│ ├── src/
│ │ ├── app.ce
│ │ ├── benchmark.ce
│ │ ├── box.ce
│ │ ├── child.ce
│ │ ├── control.ce
│ │ ├── controls-buttons.ce
│ │ ├── controls-rect.ce
│ │ ├── dependencies.ce
│ │ ├── flash.ce
│ │ ├── flex.ce
│ │ ├── focus-navigation-dom.ce
│ │ ├── focus-navigation.ce
│ │ ├── fogofwar.ce
│ │ ├── footprints.ce
│ │ ├── freeze.ce
│ │ ├── fx.ce
│ │ ├── light.ce
│ │ ├── loader-spritesheet.ce
│ │ ├── main.ts
│ │ ├── preset.ce
│ │ ├── shake.ce
│ │ ├── sprite-shadows.ce
│ │ ├── spritesheet.ce
│ │ ├── spritesheet2.ce
│ │ ├── test.ce
│ │ ├── tiled.ce
│ │ ├── viewport.ce
│ │ └── weather.ce
│ ├── tests/
│ │ ├── README.md
│ │ └── example.spec.ts
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── vitest.config.ts
├── skills-lock.json
├── starter/
│ ├── .gitignore
│ ├── components/
│ │ ├── app.ce
│ │ └── hello.ce
│ ├── index.html
│ ├── main.ts
│ ├── package.json
│ ├── tsconfig.json
│ └── vite.config.ts
├── tests/
│ ├── components/
│ │ ├── canvas.spec.ts
│ │ ├── component.spec.ts
│ │ ├── container.spec.ts
│ │ ├── displayobject.spec.ts
│ │ ├── domcontainer.spec.ts
│ │ ├── domsprite.spec.ts
│ │ ├── flex.spec.ts
│ │ ├── focus-container-tabindex.spec.ts
│ │ ├── focus-container.spec.ts
│ │ ├── graphics.spec.ts
│ │ ├── mesh.spec.ts
│ │ ├── nineslicesprite.spec.ts
│ │ ├── particleemitter.spec.ts
│ │ ├── scene.spec.ts
│ │ ├── sprite.spec.ts
│ │ ├── text.spec.ts
│ │ ├── tilingsprite.spec.ts
│ │ ├── video.spec.ts
│ │ └── viewport.spec.ts
│ ├── directives/
│ │ ├── drag.spec.ts
│ │ ├── flash.spec.ts
│ │ └── shake.spec.ts
│ ├── engine/
│ │ ├── animation.spec.ts
│ │ ├── cond-loop.spec.ts
│ │ ├── cond.spec.ts
│ │ ├── define-props.spec.ts
│ │ ├── dependencies.spec.ts
│ │ ├── focus-loop-sharing.spec.ts
│ │ ├── freeze.spec.ts
│ │ ├── lifecycle.spec.ts
│ │ ├── loop-sharing.spec.ts
│ │ ├── loop-spread-props.spec.ts
│ │ ├── loop.spec.ts
│ │ ├── nested-loop.spec.ts
│ │ ├── reactive-routing.spec.ts
│ │ └── trigger.spec.ts
│ ├── presets/
│ │ └── fx.spec.ts
│ ├── setup/
│ │ └── canvas.ts
│ └── utils/
│ ├── GlobalAssetLoader.spec.ts
│ └── tabindex.spec.ts
├── tsconfig.json
├── tsconfig.node.json
├── tsup.config.ts
└── vitest.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .agents/skills/canvasengine/SKILL.md
================================================
---
name: canvasengine
description: Work on CanvasEngine projects and .ce components using the official documentation. Use when Codex needs to explain, create, review, or modify CanvasEngine code, especially files ending in .ce, CanvasEngine reactivity with signals, computed, and effect, template directives such as @if and @for, component script/template/style structure, or CanvasEngine-specific syntax that mixes ideas from React, Vue, and Angular.
---
# CanvasEngine Project
## Documentation First
Before answering or editing CanvasEngine code, fetch and read the current documentation index:
```bash
curl -L https://canvasengine.net/llms.txt
```
If `curl` is unavailable, use an equivalent command or tool (`wget`, browser/web fetch, or another HTTP client). If network access is blocked, say that the current documentation could not be fetched and proceed only from local code plus any already available docs.
Use `llms.txt` as a table of contents:
1. Read the summary and section list.
2. Identify the Markdown documentation URLs relevant to the user request.
3. Fetch the relevant `.md` pages with `curl -L <url>` or an equivalent tool.
4. Base implementation and explanations on those pages, not on assumptions from React, Vue, Angular, or PixiJS.
5. Mention the specific docs or examples consulted when the answer depends on them.
## Project Model
Treat CanvasEngine components as `.ce` files with:
- a script block for state, imports, functions, signals, `computed`, and `effect`
- component content/template markup
- an optional CSS style block
Prefer existing project conventions for file layout, imports, naming, and component composition. Inspect nearby `.ce` files before adding new patterns.
## Reactivity
CanvasEngine reactivity is centered on signals plus `computed` and `effect`.
Use the documentation to confirm exact APIs before writing code. Do not assume that signal usage is identical to Solid, Angular, Vue, React, or another framework.
Important template rules:
- In a template, `<Rect x />` means bind the `x` variable from the script scope. `x` may be a signal or a plain value. Do not interpret this as a boolean-only JSX prop.
- For reactive conditions, pass the signal/reference to the directive: use `@if (show)`, not `@if (show())`.
- For reactive loops, pass the signal/reference to the directive in the same spirit: use `@for` with the reactive source itself rather than eagerly calling or unwrapping it, unless the official docs for the exact case say otherwise.
- Keep signal reads/writes consistent with the documented CanvasEngine syntax for the context: script code and template code may differ.
## Implementation Checklist
When modifying a CanvasEngine project:
1. Fetch `llms.txt` and the relevant Markdown docs.
2. Inspect local examples with `rg --files -g '*.ce'` and read nearby components.
3. Preserve `.ce` component structure: script, content/template, optional style.
4. Use CanvasEngine primitives and directives instead of importing framework habits from React/Vue/Angular.
5. Verify shorthand bindings, reactive `@if`, and reactive `@for` syntax before finishing.
6. Run the project’s relevant checks or at least a focused syntax/type check when available.
================================================
FILE: .agents/skills/canvasengine/agents/openai.yaml
================================================
interface:
display_name: "CanvasEngine"
short_description: "Guide projets CanvasEngine et fichiers .ce"
default_prompt: "Use $canvasengine to implement or explain a CanvasEngine .ce component with the official docs."
================================================
FILE: .agents/skills/pixijs/SKILL.md
================================================
---
name: pixijs
description: "Use this skill first for ANY PixiJS v8 task; it routes to the right specialized skill for the job. Covers the full PixiJS surface: Application setup, the scene graph (Container, Sprite, Graphics, Text, Mesh, ParticleContainer, DOMContainer, GifSprite), rendering (WebGL/WebGPU/Canvas, render loop, custom shaders, filters, blend modes), assets, events, color, math, ticker, accessibility, performance, environments, migration from v7, and project scaffolding. Triggers on: pixi, pixi.js, pixijs, PixiJS, v8, Application, app.init, Sprite, Container, Graphics, Text, Mesh, ParticleContainer, DOMContainer, GifSprite, Assets, Ticker, renderer, WebGL, WebGPU, scene graph, filter, shader, blend mode, texture, BitmapText, create-pixi, how do I draw, how do I render, how do I animate in pixi."
license: MIT
---
Entry point for the PixiJS v8 skill collection. PixiJS is the fastest library available for the web, working across all devices and allowing you to create rich, interactive graphics and cross-platform applications using WebGL, WebGPU, and Canvas as a fallback.
## How to use this skill
1. Find the specialized skill in the router below that best matches the task.
2. Load that skill's `SKILL.md` and follow its guidance.
3. If no sub-skill fits (the task references a specific class, function, option, or API surface not listed below), **WebFetch `https://pixijs.download/release/docs/llms.txt`**. That file is the auto-generated, always-current index of the full PixiJS API and guides. Each entry links to a `.html.md` page you can WebFetch for the detailed content.
For the long-form description and trigger keywords of every skill, see [references/index.md](references/index.md).
## Skill router
### Foundations
| Skill | Load when... |
|---|---|
| [pixijs-application](../pixijs-application/SKILL.md) | Creating or configuring a PixiJS `Application`, calling `app.init()`, accessing `app.stage`/`renderer`/`canvas`/`screen`, resize/ticker plugins, `app.destroy()`. |
| [pixijs-core-concepts](../pixijs-core-concepts/SKILL.md) | Understanding the renderer pipeline, choosing WebGL/WebGPU/Canvas, render loop internals, systems and pipes. |
| [pixijs-create](../pixijs-create/SKILL.md) | Scaffolding a new project with the `create-pixi` CLI (bundler-vite, creation-web, framework-react templates). |
| [pixijs-environments](../pixijs-environments/SKILL.md) | Running PixiJS in Web Workers, Node/SSR, or strict-CSP contexts (`DOMAdapter`, `WebWorkerAdapter`, `pixi.js/unsafe-eval`). |
| [pixijs-migration-v8](../pixijs-migration-v8/SKILL.md) | Upgrading from v7 to v8 or fixing v7 patterns (`beginFill`/`endFill`, `@pixi/*` packages, `BaseTexture`, `DisplayObject`). |
| [pixijs-scene-core-concepts](../pixijs-scene-core-concepts/SKILL.md) | Understanding the scene graph as a whole: containers vs leaves, transforms, render order, masking, `RenderLayer`. |
### Scene Objects
| Skill | Load when... |
|---|---|
| [pixijs-scene-container](../pixijs-scene-container/SKILL.md) | Working with `Container`: `addChild`/`removeChild`, transforms, `zIndex`, bounds, `toGlobal`/`toLocal`, `destroy`. |
| [pixijs-scene-sprite](../pixijs-scene-sprite/SKILL.md) | Drawing images: `Sprite`, `AnimatedSprite`, `NineSliceSprite`, `TilingSprite`. |
| [pixijs-scene-graphics](../pixijs-scene-graphics/SKILL.md) | Drawing vector shapes or paths: `Graphics`, `GraphicsContext`, `fill`/`stroke`, `FillGradient`, SVG. |
| [pixijs-scene-text](../pixijs-scene-text/SKILL.md) | Rendering text: `Text`, `BitmapText`, `HTMLText`, `SplitText`, `TextStyle`. |
| [pixijs-scene-mesh](../pixijs-scene-mesh/SKILL.md) | Custom geometry: `Mesh`, `MeshSimple`, `MeshPlane`, `MeshRope`, `PerspectiveMesh`. |
| [pixijs-scene-particle-container](../pixijs-scene-particle-container/SKILL.md) | Rendering thousands of lightweight sprites: `ParticleContainer`, `Particle`, `dynamicProperties`. |
| [pixijs-scene-dom-container](../pixijs-scene-dom-container/SKILL.md) | Overlaying HTML elements on the canvas: `DOMContainer`, `pixi.js/dom`. |
| [pixijs-scene-gif](../pixijs-scene-gif/SKILL.md) | Displaying animated GIFs: `GifSprite`, `GifSource`, `pixi.js/gif`. |
### Utilities
| Skill | Load when... |
|---|---|
| [pixijs-assets](../pixijs-assets/SKILL.md) | Loading resources: `Assets.init`, `Assets.load`, bundles, manifests, spritesheets, caching. |
| [pixijs-color](../pixijs-color/SKILL.md) | Creating or converting colors: `Color` class, hex/rgb/hsl, `tint`, `premultiply`. |
| [pixijs-events](../pixijs-events/SKILL.md) | Handling pointer/mouse/touch/wheel input: `eventMode`, `FederatedEvent`, `hitArea`, `cursor`, drag. |
| [pixijs-math](../pixijs-math/SKILL.md) | Points, vectors, matrices, shapes, hit testing: `Point`, `Matrix`, `Rectangle`, `toGlobal`/`toLocal`. |
| [pixijs-ticker](../pixijs-ticker/SKILL.md) | Per-frame logic or controlling the render loop: `Ticker`, `deltaTime`, `UPDATE_PRIORITY`, `maxFPS`. |
### Advanced
| Skill | Load when... |
|---|---|
| [pixijs-accessibility](../pixijs-accessibility/SKILL.md) | Screen reader or keyboard navigation: `AccessibilitySystem`, `accessibleTitle`, `tabIndex`. |
| [pixijs-blend-modes](../pixijs-blend-modes/SKILL.md) | Compositing with blend modes: `add`, `multiply`, `screen`, `overlay`, `pixi.js/advanced-blend-modes`. |
| [pixijs-custom-rendering](../pixijs-custom-rendering/SKILL.md) | Writing custom shaders, uniforms, or batchers: `Shader.from`, `GlProgram`/`GpuProgram`, `UniformGroup`, custom `Filter`. |
| [pixijs-filters](../pixijs-filters/SKILL.md) | Applying visual effects: `BlurFilter`, `ColorMatrixFilter`, `DisplacementFilter`, `Filter.from`, `pixi-filters`. |
| [pixijs-performance](../pixijs-performance/SKILL.md) | Profiling or optimizing FPS, draw calls, GPU memory: culling, `GCSystem`, `cacheAsTexture`, object pooling. |
## Fallback: canonical PixiJS docs
If the task references a class, function, option, or API surface not covered by any sub-skill above, **WebFetch `https://pixijs.download/release/docs/llms.txt`**. It's the auto-generated index of the full PixiJS API and guides, regenerated on every release. Each entry links to a `.html.md` page you can WebFetch for the detailed content. Use this fallback whenever the router table doesn't point at an obvious match.
================================================
FILE: .agents/skills/pixijs/references/index.md
================================================
# PixiJS Skills: Full Index
Detailed routing table for the PixiJS v8 skill collection. Each entry lists the skill's full description and the trigger keywords that should match it. For a scannable short-form table, see the parent `SKILL.md`.
## Foundations
### pixijs-application
Create and configure a PixiJS v8 `Application`. Covers `new Application()` + async `app.init()` options (width, height, background, antialias, resolution, autoDensity, preference, resizeTo, autoStart, sharedTicker, canvas), `app.stage`/`renderer`/`canvas`/`screen` access, `ResizePlugin`, `TickerPlugin`, and `app.destroy()`.
**Triggers:** Application, app.init, app.stage, app.renderer, app.canvas, app.screen, ApplicationOptions, resizeTo, preference, autoStart, sharedTicker, app.destroy.
### pixijs-core-concepts
How PixiJS v8 renders frames: the systems-and-pipes renderer, the render loop, and how the library adapts to different environments. Covers `WebGLRenderer`/`WebGPURenderer`/`CanvasRenderer` selection, `renderer.render()` pipeline, environment detection, and pointers to per-topic deep dives.
**Triggers:** renderer, WebGL, WebGPU, Canvas, render loop, render pipeline, systems, environments, autoDetectRenderer.
### pixijs-create
Scaffold a new PixiJS v8 project with the `create-pixi` CLI. Covers npm/yarn/pnpm/bun create commands, interactive vs non-interactive flows, available template presets (bundler-vite, bundler-webpack, bundler-esbuild, bundler-import-map, creation-web, framework-react, extension-default), Node version requirements, and post-scaffold dev flow.
**Triggers:** create pixi.js, npm create, scaffold, template, bundler-vite, bundler-webpack, creation-web, framework-react, new project, getting started.
### pixijs-environments
Run PixiJS v8 outside a standard browser: Web Workers, `OffscreenCanvas`, Node/SSR, or CSP-restricted contexts. Covers `DOMAdapter.set`, `BrowserAdapter`, `WebWorkerAdapter`, custom `Adapter` interface, `pixi.js/unsafe-eval` for strict CSP.
**Triggers:** DOMAdapter, BrowserAdapter, WebWorkerAdapter, Web Worker, OffscreenCanvas, Node, headless, SSR, CSP, unsafe-eval, Adapter.
### pixijs-migration-v8
Upgrade a PixiJS project from v7 to v8 or diagnose broken v7 code after an upgrade. Covers async `app.init`, single `pixi.js` package (deprecated `@pixi/*` sub-packages), `Graphics` shape-then-fill, `BaseTexture` -> `TextureSource`, shader/uniform rework, `ParticleContainer`+`Particle`, constructor options objects, `DisplayObject` removal, `settings`/`utils` removal, `Ticker` signature, events rewrite.
**Triggers:** migrate v7, v8 breaking changes, @pixi/ import, DisplayObject, beginFill, endFill, cacheAsBitmap, BaseTexture, deprecated.
### pixijs-scene-core-concepts
The PixiJS v8 scene graph as a whole: how containers, leaves, transforms, and render order fit together. Covers leaf vs container distinction, local/world coordinates, culling, render groups, sortable children, masking, `RenderLayer`, and which leaf skill covers which display object.
**Triggers:** scene graph, display list, Container, Sprite, Graphics, Text, Mesh, ParticleContainer, DOMContainer, GifSprite, masking, render group, RenderLayer, world transform.
## Scene Objects
### pixijs-scene-container
Group, position, or transform display objects. Covers `Container` constructor options (`isRenderGroup`, `sortableChildren`, `boundsArea`), `addChild`/`removeChild`/`addChildAt`/`swapChildren`/`setChildIndex`, `position`/`scale`/`rotation`/`pivot`/`skew`/`alpha`/`tint`, `getBounds`/`getGlobalPosition`/`toLocal`/`toGlobal`, `zIndex` sorting, `cullable`, `destroy`.
**Triggers:** Container, addChild, removeChild, addChildAt, swapChildren, sortableChildren, zIndex, position, scale, rotation, pivot, getBounds, toGlobal, toLocal, destroy.
### pixijs-scene-dom-container
Overlay HTML elements on the PixiJS v8 canvas. Covers `DOMContainer` with `element`, `anchor`, and scene-graph-driven CSS transforms, the `pixi.js/dom` side-effect import, `DOMPipe` registration, visibility sync, pointer-events handling.
**Triggers:** DOMContainer, pixi.js/dom, DOMPipe, HTML overlay, input on canvas, iframe overlay, DOMContainerOptions, element, anchor.
### pixijs-scene-gif
Display animated GIFs. Covers the `pixi.js/gif` side-effect import, `Assets.load` returning a `GifSource`, `GifSprite` playback (`play`/`stop`/`currentFrame`/`animationSpeed`), `autoPlay`/`loop` options, `onComplete`/`onLoop`/`onFrameChange` callbacks, `GifSource` sharing, `clone`, `destroy`.
**Triggers:** GifSprite, GifSource, pixi.js/gif, animationSpeed, currentFrame, autoPlay, onComplete, onFrameChange.
### pixijs-scene-graphics
Draw vector shapes and paths. Covers the `Graphics` shape-then-fill API (`rect`/`circle`/`ellipse`/`poly`/`roundRect`/`star`), path methods (`moveTo`/`lineTo`/`bezierCurveTo`/`arc`), `fill`/`stroke`/`cut`, `FillGradient`, `FillPattern`, `GraphicsContext` sharing, SVG markup.
**Triggers:** Graphics, GraphicsContext, rect, circle, poly, roundRect, fill, stroke, cut, FillGradient, FillPattern, moveTo, bezierCurveTo, svg.
### pixijs-scene-mesh
Render custom geometry. Covers `Mesh` with `MeshGeometry` (positions, uvs, indices, topology), `MeshSimple` for per-frame vertex animation, `MeshPlane` for subdivided deformation, `MeshRope` for path-following textures, `PerspectiveMesh` for 2.5D corners.
**Triggers:** Mesh, MeshGeometry, MeshSimple, MeshPlane, MeshRope, PerspectiveMesh, positions, uvs, indices, topology, setCorners.
### pixijs-scene-particle-container
Render thousands of lightweight sprites. Covers `ParticleContainer` with `Particle` instances, `addParticle`/`removeParticle`, `particleChildren` array, `dynamicProperties` (vertex, position, rotation, uvs, color), `boundsArea`, `roundPixels`, `update`.
**Triggers:** ParticleContainer, Particle, IParticle, addParticle, particleChildren, dynamicProperties, boundsArea, particle effects.
### pixijs-scene-sprite
Draw images. Covers `Sprite` with `anchor`/`tint`/`texture`, `AnimatedSprite` for frame animation, `NineSliceSprite` for resizable UI panels, `TilingSprite` for scrolling/repeating backgrounds.
**Triggers:** Sprite, AnimatedSprite, NineSliceSprite, TilingSprite, Sprite.from, anchor, tint, tilePosition, animationSpeed, gotoAndPlay, leftWidth, topHeight.
### pixijs-scene-text
Render text. Covers `Text` for canvas-quality styled labels, `BitmapText` for cheap per-frame updates via glyph atlas, `HTMLText` for HTML/CSS markup via SVG, `SplitText` and `SplitBitmapText` for per-character animation, `TextStyle`, `tagStyles`.
**Triggers:** Text, BitmapText, HTMLText, SplitText, SplitBitmapText, TextStyle, HTMLTextStyle, BitmapFont.install, tagStyles, fontFamily, wordWrap.
## Utilities
### pixijs-assets
Load and manage resources. Covers `Assets.init`, `Assets.load`/`add`/`unload`, bundles, manifests, background loading, `onProgress`, caching, spritesheets, compressed textures, SVG as texture or Graphics, resolution detection.
**Triggers:** Assets, Assets.load, Assets.init, loadBundle, manifest, backgroundLoad, Spritesheet, Cache, LoadOptions, unload.
### pixijs-color
Create, convert, or manipulate colors. Covers `Color` class input formats (hex, CSS names, RGB/HSL objects, arrays, `Uint8Array`), conversion methods (`toHex`, `toNumber`, `toArray`, `toRgba`), component access, `setAlpha`/`multiply`/`premultiply`, `Color.shared` singleton.
**Triggers:** Color, ColorSource, hex, rgb, hsl, tint, premultiply, Color.shared, color conversion.
### pixijs-events
Handle pointer, mouse, touch, or wheel input. Covers `eventMode` (none, passive, auto, static, dynamic), `FederatedEvent` types, propagation and capture phase, `hitArea`, `interactiveChildren`, `cursor` and `cursorStyles`, global move events for drag, `eventFeatures` config.
**Triggers:** eventMode, FederatedPointerEvent, pointerdown, click, tap, globalpointermove, drag, hitArea, cursor, stopPropagation.
### pixijs-math
Coordinates, vectors, matrices, shapes, and hit testing. Covers `Point`/`ObservablePoint`, `Matrix` (2D affine, decompose, apply), shapes (`Rectangle`, `Circle`, `Ellipse`, `Polygon`, `RoundedRectangle`, `Triangle`), `toGlobal`/`toLocal`, `PointData` types, `DEG_TO_RAD`, and `pixi.js/math-extras` vector helpers.
**Triggers:** Point, ObservablePoint, Matrix, Rectangle, Circle, Polygon, toGlobal, toLocal, hitArea, math-extras, DEG_TO_RAD, PointData.
### pixijs-ticker
Run per-frame logic or control the render loop. Covers `Ticker.add`/`addOnce`/`remove`, `deltaTime` vs `deltaMS` vs `elapsedMS`, `UPDATE_PRIORITY` ordering, `maxFPS`/`minFPS` capping, speed scaling, `Ticker.shared` vs new instances, per-object `onRender` hook, manual rendering.
**Triggers:** Ticker, UPDATE_PRIORITY, deltaTime, deltaMS, elapsedMS, onRender, app.ticker, maxFPS, minFPS, Ticker.shared.
## Advanced
### pixijs-accessibility
Add screen reader and keyboard navigation to PixiJS v8 apps. Covers `AccessibilitySystem` options (`enabledByDefault`, `debug`, `activateOnTab`, `deactivateOnMouseMove`), per-container accessibility properties, shadow DOM overlay, mobile touch-hook activation.
**Triggers:** accessibility, a11y, screen reader, ARIA, keyboard navigation, tab order, AccessibilitySystem, accessibleTitle, accessibleHint, tabIndex, accessibleChildren.
### pixijs-blend-modes
Composite display objects with blend modes. Covers standard modes (`normal`, `add`, `multiply`, `screen`, `erase`, `min`, `max`), advanced modes via `pixi.js/advanced-blend-modes` (`color-burn`, `overlay`, `hard-light`, etc.), batch-friendly ordering.
**Triggers:** blendMode, additive, multiply, screen, overlay, color-burn, color-dodge, advanced-blend-modes, glow, erase.
### pixijs-custom-rendering
Write custom shaders, uniforms, or batchers. Covers `Shader.from({gl, gpu, resources})`, `GlProgram`/`GpuProgram`, `UniformGroup` with typed uniforms (`f32`, `vec2`, `mat4x4`), UBO mode, textures as resources, custom `Filter`, custom `Batcher` via extensions.
**Triggers:** Shader, GlProgram, GpuProgram, UniformGroup, Batcher, Filter, GLSL, WGSL, UBO, uniform, custom shader.
### pixijs-filters
Apply visual effects to containers via the filter pipeline. Covers built-in filters (`AlphaFilter`, `BlurFilter`, `ColorMatrixFilter`, `DisplacementFilter`, `NoiseFilter`), custom `Filter.from()` with GLSL/WGSL, options (`resolution`, `padding`, `antialias`, `blendRequired`), `filterArea` optimization, `pixi-filters` community package.
**Triggers:** filters, BlurFilter, ColorMatrixFilter, DisplacementFilter, NoiseFilter, Filter.from, GLSL filter, pixi-filters, filterArea.
### pixijs-performance
Profile or optimize a PixiJS v8 app for FPS, draw calls, or GPU memory. Covers destroy patterns (`cacheAsTexture(false)`, `releaseGlobalResources`), `GCSystem` and `TextureGCSystem`, `PrepareSystem`, object pooling, batching rules, `BitmapText` for dynamic text, culling (`Culler`, `CullerPlugin`, `cullable`, `cullArea`), resolution/antialias tradeoffs.
**Triggers:** FPS, jank, draw calls, batching, object pool, GCSystem, PrepareSystem, Culler, cacheAsTexture, memory leak, destroy patterns.
================================================
FILE: .changeset/config.json
================================================
{
"$schema": "https://unpkg.com/@changesets/config@3.0.1/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "v2",
"updateInternalDependencies": "patch",
"ignore": []
}
================================================
FILE: .codex
================================================
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches:
- "*"
paths-ignore:
- "docs/**"
pull_request:
branches:
- "*"
paths-ignore:
- "docs/**"
jobs:
tests:
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [20]
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Build
run: pnpm build
- name: Run Tests
run: pnpm test
deploy:
needs: tests
runs-on: ubuntu-22.04
if: github.ref == 'refs/heads/v2'
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "pnpm"
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Build
run: pnpm build
- name: 📣 Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
title: "chore(release): version packages 🦋"
publish: pnpm publish:packages
version: pnpm version:packages
commit: "chore(release): version packages 🦋 [skip ci]"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Update Starter Package Versions
run: |
CORE_VERSION=$(jq -r '.version' packages/core/package.json)
jq --arg version "$CORE_VERSION" '.dependencies.canvasengine = $version | .devDependencies["@canvasengine/compiler"] = $version' starter/package.json > starter/package.tmp.json
mv starter/package.tmp.json starter/package.json
shell: bash
- name: Commit Changes
run: |
git add starter/package.json
git commit -m "chore(release): update starter package version to $CORE_VERSION"
git push
shell: bash
================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
lib
cache
.vitepress/dist
.vitepress/cache
coverage
sample/public
================================================
FILE: .npmignore
================================================
tests
template
sample
public
docs
.vitepress
node_modules
starter
.github
.cursorrules
logo.png
compiler
================================================
FILE: README.md
================================================
# CanvasEngine - A reactive HTML5 Canvas management library built on top of PixiJS and Vite

CanvasEngine is a reactive HTML5 Canvas management library built on top of PixiJS and Vite. It provides a component-oriented approach to canvas rendering, similar to modern frontend frameworks.
Features:
- Reactive components
- Use flex in Canvas !
- Easy Animation system
- Keyboard, Gamepad et Virtual Joystick
- Tiled Map Editor integration
- Particle Emitter
- Audio System
## Installation
```bash
npx degit RSamaium/CanvasEngine/starter my-project
cd my-project
npm install
npm run dev # and go to localhost:5173
```
## Documentation
https://canvasengine.net
## Example:
```html
<Container flexDirection="row" width={500}>
<Sprite
image="/assets/logo.png"
anchor={0.5}
rotation
scale
pointerenter={onEnter}
pointerleave={onLeave}
/>
<Text text size={70} fontFamily="Helvetica" x={90} y={-30} />
</Container>
<script>
import { signal, tick, animatedSignal, Easing } from "canvasengine";
const { text } = defineProps();
const rotation = signal(0);
const scale = animatedSignal(1, {
duration: 300,
ease: Easing.easeInOut,
});
tick(() => {
rotation.update(rotation => rotation + 0.01);
});
const onEnter = () => {
scale.set(1.5);
};
const onLeave = () => {
scale.set(1);
};
</script>
```
## Contributing
Before, install `pnpm` and run the following command:
```bash
git clone https://github.com/RSamaium/CanvasEngine.git
cd CanvasEngine
pnpm install
pnpm run dev # to build the libraries
```
To run the sample project:
```bash
pnpm run dev:sample
```
### Build Documentation/Website
```
cd docs
pnpm install
pnpm run dev
```
### Release
```bash
pnpm run release
```
> Choose the version you want to release
Push the release branch to the remote repository
```bash
git push origin v2
```
================================================
FILE: __mocks__/pixi-viewport.ts
================================================
import { vi } from "vitest";
import { Viewport as PixiViewport } from 'pixi-viewport';
export class Viewport extends PixiViewport {
drag = vi.fn().mockReturnThis();
wheel = vi.fn().mockReturnThis();
clamp = vi.fn().mockReturnThis();
decelerate = vi.fn().mockReturnThis();
pinch = vi.fn().mockReturnThis();
on = vi.fn().mockReturnThis();
update = vi.fn().mockReturnThis();
screenWidth = 0;
screenHeight = 0;
worldWidth = 0;
worldHeight = 0;
options: any = { events: {} }; // Initialize options
input: any = { wheelFunction: vi.fn() }; // Mock input property
}
================================================
FILE: docs/.vitepress/config.ts
================================================
import { defineConfig } from 'vitepress';
import llmstxt from 'vitepress-plugin-llms'
const guideMenu = [
{
text: "Quick Start",
collapsed: false,
items: [
{
text: "Installation",
link: "/get_started/installation",
},
{
text: "Start",
link: "/get_started/start",
}
],
},
{
text: "Concepts",
collapsed: false,
items: [
{
text: "Template Syntax",
link: "/concepts/template-syntax",
},
{
text: "Reactivity",
link: "/concepts/reactive",
},
{
text: "Child Component",
link: "/concepts/child-component",
},
{
text: "Trigger",
link: "/concepts/trigger",
},
{
text: "Lifecycle",
link: "/concepts/lifecycle",
},
{
text: "Dependencies",
link: "/concepts/dependencies",
},
{
text: "Slot",
link: "/concepts/slot",
},
{
text: "Dynamic Components",
link: "/concepts/dynamic-components",
},
{
text: "Primitive Animation",
link: "/concepts/animation",
},
{
text: "Styling",
link: "/concepts/styling",
}
],
},
{
text: "Components",
collapsed: false,
items: [
{
text: "Canvas",
link: "/components/canvas",
},
{
text: "Container",
link: "/components/container",
},
{
text: "Graphics",
link: "/components/graphic",
},
{
text: "Svg",
link: "/components/svg",
},
{
text: "Text",
link: "/components/text",
},
{
text: "Button",
link: "/components/button",
},
{
text: "Sprite",
link: "/components/sprite",
},
{
text: "NineSliceSprite",
link: "/components/nine-slice-sprite",
},
{
text: "Viewport",
link: "/components/viewport",
},
{
text: "TilingSprite",
link: "/components/tiling-sprite",
},
{
text: "Video",
link: "/components/video",
},
{
text: "Mesh",
link: "/components/mesh",
},
{
text: "DOMContainer",
link: "/components/dom-container",
},
{
text: "Navigation",
link: "/components/navigation",
},
{
text: "Joystick",
link: "/components/joystick",
}
],
},
{
text: "Directives",
collapsed: false,
items: [
{
text: "Controls",
link: "/directives/controls",
},
{
text: "Drag",
link: "/directives/drag",
},
{
text: "Sound",
link: "/directives/sound",
},
{
text: "Flash",
link: "/directives/flash",
},
{
text: "Shake",
link: "/directives/shake",
}
],
},
{
text: "Presets Components",
collapsed: false,
items: [
{
text: "Bar",
link: "/presets/bar",
},
{
text: "Loading",
link: "/presets/loading",
},
{
text: "Tilemap",
link: "/presets/tilemap",
},
{
text: "Weather",
link: "/presets/weather",
},
{
text: "NightAmbiant",
link: "/presets/night-ambiant",
},
{
text: "Footprints",
link: "/presets/footprints",
},
{
text: "Fx",
link: "/presets/fx",
}
],
},
{
text: "API",
collapsed: false,
items: [
{
text: "Element Object",
link: "/api/element",
},
{
text: "Context",
link: "/api/context",
},
{
text: "Testing",
link: "/api/testing",
},
{
text: "Use without Compiler",
link: "/advanced/without-compiler",
}
],
},
];
export default defineConfig({
title: "Canvas Engine Documentation",
description: "Reactive Canvas Framework",
ignoreDeadLinks: true,
themeConfig: {
search: {
provider: "local",
},
repo: "https://github.com/RSamaium/CanvasEngine",
nav: [
{
text: "Home",
link: "https://canvasengine.net",
},
{
text: "Guide",
link: "/guide/get-started",
},
],
sidebar: {
"/": guideMenu,
"/guide/": guideMenu,
"/components/": guideMenu,
"/directives/": guideMenu,
},
},
vite: {
plugins: [llmstxt()]
},
})
================================================
FILE: docs/.vitepress/theme/Layout.vue
================================================
<template>
<Layout v-if="!isHome" />
<CustomHome v-else />
</template>
<script setup>
import { computed } from 'vue'
import { useData } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import CustomHome from './components/CustomHome.vue'
const { Layout } = DefaultTheme
const { page } = useData()
const isHome = computed(() => page.value.relativePath === 'index.md')
</script>
================================================
FILE: docs/.vitepress/theme/components/CustomHome.vue
================================================
<template>
<div class="custom-home">
<!-- Custom Navbar -->
<nav class="custom-navbar">
<div class="navbar-container">
<div class="navbar-brand">
</div>
<div class="navbar-links">
<a href="/get_started/installation" class="nav-link">Documentation</a>
<a href="https://github.com/RSamaium/CanvasEngine" target="_blank" class="nav-link">Github</a>
<a href="https://discord.gg/W38yDyGfwC" class="nav-link">Discord</a>
</div>
</div>
</nav>
<!-- Hero Section -->
<section class="hero">
<div class="hero-content">
<div class="hero-text">
<h1 class="hero-title">
<img src="/logo.png" alt="Canvas Engine" class="logo" />
</h1>
<p class="hero-subtitle">
A reactive HTML5 Canvas management library built on top of PixiJS
</p>
<p class="hero-description">
Component-oriented approach to canvas rendering, similar to modern frontend frameworks.
Build interactive games and applications with ease.
</p>
<div class="hero-actions">
<a href="/get_started/installation" class="btn btn-primary">
Get Started
</a>
<a href="https://github.com/RSamaium/CanvasEngine" class="btn btn-secondary" target="_blank">
<svg class="github-icon" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
</svg>
GitHub
</a>
</div>
</div>
<div class="hero-visual">
<div class="canvas-preview">
<div class="canvas-mock">
<div class="canvas-element sprite"></div>
<div class="canvas-element container">
<div class="canvas-element text">Canvas Engine</div>
<div class="canvas-element graphic"></div>
</div>
<div class="canvas-element particle"></div>
<div class="canvas-element particle"></div>
<div class="canvas-element particle"></div>
</div>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section class="features">
<div class="features-container">
<h2 class="section-title">Why Canvas Engine?</h2>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
</svg>
</div>
<h3>Reactive Components</h3>
<p>Build interactive canvas applications with reactive components that automatically update when data changes.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
<line x1="8" y1="21" x2="16" y2="21"/>
<line x1="12" y1="17" x2="12" y2="21"/>
</svg>
</div>
<h3>Flexbox in Canvas</h3>
<p>Use familiar CSS Flexbox layout system directly in your canvas applications for responsive designs.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
</svg>
</div>
<h3>Easy Animations</h3>
<p>Create smooth animations with a simple and intuitive animation system built for performance.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="8" width="18" height="4" rx="1"/>
<path d="M12 8v13"/>
<path d="M19 12v7a2 2 0 01-2 2H7a2 2 0 01-2-2v-7"/>
<path d="M7.5 8a2.5 2.5 0 010-5A4.8 8 0 0112 8a4.8 8 0 014.5-5 2.5 2.5 0 010 5"/>
</svg>
</div>
<h3>Input Controls</h3>
<p>Support for keyboard, gamepad (ready when connected), and virtual joystick controls out of the box.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z"/>
<polyline points="3.27,6.96 12,12.01 20.73,6.96"/>
<line x1="12" y1="22.08" x2="12" y2="12"/>
</svg>
</div>
<h3>Tiled Map Support</h3>
<p>Seamless integration with Tiled Map Editor for creating complex game worlds and levels.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 18V5l12-2v13"/>
<circle cx="6" cy="18" r="3"/>
<circle cx="18" cy="16" r="3"/>
</svg>
</div>
<h3>Audio System</h3>
<p>Built-in audio system with particle emitters for creating immersive gaming experiences.</p>
</div>
</div>
</div>
</section>
<!-- Examples Section -->
<section class="examples">
<div class="examples-container">
<h2 class="section-title">Interactive Examples</h2>
<p class="section-description">Try these examples directly in your browser</p>
<div class="examples-grid">
<div v-for="example in examples" :key="example.title" class="example-item">
<Playground
:title="example.title"
:description="example.description"
:files="example.files"
/>
</div>
</div>
</div>
</section>
</div>
</template>
<script setup>
import Playground from './Playground.vue'
import examples from './example'
</script>
<style scoped>
.custom-home {
--primary-color: #646cff;
--primary-color-light: #747bff;
--secondary-color: #42b883;
--text-color: #213547;
--text-color-light: #64748b;
--bg-color: #ffffff;
--border-color: #e2e8f0;
--card-bg: #f8fafc;
}
.dark .custom-home {
--text-color: #f1f5f9;
--text-color-light: #94a3b8;
--bg-color: #0f172a;
--border-color: #334155;
--card-bg: #1e293b;
}
/* Custom Navbar */
.custom-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border-color);
padding: 1rem 0;
}
.dark .custom-navbar {
background: rgba(15, 23, 42, 0.95);
}
.navbar-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.brand-text {
font-size: 1.5rem;
font-weight: 700;
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.navbar-links {
display: flex;
gap: 2rem;
}
.nav-link {
color: var(--text-color);
text-decoration: none;
font-weight: 500;
transition: color 0.2s;
position: relative;
}
.nav-link:hover {
color: var(--primary-color);
}
.nav-link::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 0;
height: 2px;
background: var(--primary-color);
transition: width 0.2s;
}
.nav-link:hover::after {
width: 100%;
}
/* Hero Section */
.hero {
background: linear-gradient(135deg, var(--bg-color) 0%, #f1f5f9 100%);
padding: 8rem 2rem 4rem;
min-height: 70vh;
display: flex;
align-items: center;
}
.dark .hero {
background: linear-gradient(135deg, var(--bg-color) 0%, #1e293b 100%);
}
.hero-content {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: center;
}
.hero-title {
font-size: 3.5rem;
font-weight: 800;
line-height: 1.1;
margin-bottom: 1.5rem;
}
.gradient-text {
background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 1.5rem;
color: var(--text-color);
margin-bottom: 1rem;
font-weight: 600;
}
.hero-description {
font-size: 1.1rem;
color: var(--text-color-light);
line-height: 1.6;
margin-bottom: 2rem;
}
.hero-actions {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
text-decoration: none;
transition: all 0.2s;
border: 2px solid transparent;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-color-light);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(100, 108, 255, 0.3);
}
.btn-secondary {
background: var(--card-bg);
color: var(--text-color);
border-color: var(--border-color);
}
.btn-secondary:hover {
border-color: var(--primary-color);
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
.btn-outline {
background: transparent;
color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-outline:hover {
background: var(--primary-color);
color: white;
transform: translateY(-2px);
}
.btn-large {
padding: 1rem 2rem;
font-size: 1.1rem;
}
.github-icon {
width: 1.2rem;
height: 1.2rem;
}
/* Canvas Preview */
.canvas-preview {
perspective: 1000px;
}
.canvas-mock {
width: 400px;
height: 300px;
background: #1a1a1a;
border-radius: 1rem;
position: relative;
overflow: hidden;
transform: rotateY(-15deg) rotateX(5deg);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
animation: float 6s ease-in-out infinite;
}
.canvas-element {
position: absolute;
border-radius: 0.5rem;
}
.sprite {
width: 60px;
height: 60px;
background: linear-gradient(45deg, #ff6b6b, #feca57);
top: 50px;
left: 50px;
animation: bounce 2s ease-in-out infinite;
}
.container {
width: 200px;
height: 100px;
border: 2px dashed #646cff;
top: 100px;
left: 150px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.text {
color: white;
font-size: 14px;
font-weight: bold;
position: static;
margin-bottom: 10px;
}
.graphic {
width: 40px;
height: 40px;
background: #42b883;
position: static;
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}
.particle {
width: 6px;
height: 6px;
background: #feca57;
border-radius: 50%;
animation: particle 3s linear infinite;
}
.particle:nth-child(4) {
top: 200px;
left: 100px;
animation-delay: 0s;
}
.particle:nth-child(5) {
top: 180px;
left: 120px;
animation-delay: 1s;
}
.particle:nth-child(6) {
top: 220px;
left: 80px;
animation-delay: 2s;
}
@keyframes float {
0%, 100% { transform: rotateY(-15deg) rotateX(5deg) translateY(0px); }
50% { transform: rotateY(-15deg) rotateX(5deg) translateY(-20px); }
}
@keyframes bounce {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
}
@keyframes particle {
0% { opacity: 1; transform: translateY(0px) scale(1); }
100% { opacity: 0; transform: translateY(-50px) scale(0.5); }
}
/* Features Section */
.features {
padding: 6rem 2rem;
background: var(--card-bg);
}
.features-container {
max-width: 1400px;
margin: 0 auto;
}
.section-title {
text-align: center;
font-size: 2.5rem;
font-weight: 700;
color: var(--text-color);
margin-bottom: 4rem;
}
.features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2.5rem;
align-items: start;
}
.feature-card {
background: var(--bg-color);
padding: 2.5rem;
border-radius: 1rem;
border: 1px solid var(--border-color);
transition: all 0.3s ease;
height: 100%;
display: flex;
flex-direction: column;
}
.feature-card:hover {
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
border-color: var(--primary-color);
}
.dark .feature-card:hover {
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
.feature-icon {
width: 4rem;
height: 4rem;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
border-radius: 1rem;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1.5rem;
flex-shrink: 0;
}
.feature-icon svg {
width: 2rem;
height: 2rem;
color: white;
}
.feature-card h3 {
font-size: 1.4rem;
font-weight: 600;
color: var(--text-color);
margin-bottom: 1rem;
line-height: 1.3;
}
.feature-card p {
color: var(--text-color-light);
line-height: 1.6;
font-size: 1rem;
flex-grow: 1;
}
/* Examples Section */
.examples {
padding: 6rem 2rem;
background: var(--bg-color);
}
.examples-container {
max-width: 1400px;
margin: 0 auto;
}
.section-description {
text-align: center;
font-size: 1.2rem;
color: var(--text-color-light);
margin-bottom: 4rem;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.examples-grid {
display: flex;
flex-direction: column;
gap: 4rem;
}
.example-item {
width: 100%;
}
/* Code Example Section */
.code-example {
padding: 6rem 2rem;
background: var(--bg-color);
}
.code-showcase {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: center;
max-width: 1000px;
margin: 0 auto;
}
.code-block {
background: #1e293b;
border-radius: 1rem;
padding: 2rem;
overflow-x: auto;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.code-block pre {
margin: 0;
color: #e2e8f0;
font-family: 'Fira Code', monospace;
font-size: 0.9rem;
line-height: 1.6;
}
.code-description h3 {
font-size: 1.5rem;
font-weight: 600;
color: var(--text-color);
margin-bottom: 1rem;
}
.code-description p {
color: var(--text-color-light);
line-height: 1.6;
font-size: 1.1rem;
}
/* CTA Section */
.cta {
padding: 6rem 2rem;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
text-align: center;
}
.cta-content h2 {
font-size: 2.5rem;
font-weight: 700;
color: white;
margin-bottom: 1rem;
}
.cta-content p {
font-size: 1.2rem;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 2rem;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.cta-actions {
display: flex;
gap: 1rem;
justify-content: center;
flex-wrap: wrap;
}
.cta .btn-primary {
background: white;
color: var(--primary-color);
}
.cta .btn-primary:hover {
background: #f1f5f9;
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(255, 255, 255, 0.3);
}
.cta .btn-outline {
background: transparent;
color: white;
border-color: white;
}
.cta .btn-outline:hover {
background: white;
color: var(--primary-color);
transform: translateY(-2px);
}
/* Responsive Design */
@media (max-width: 1024px) {
.features-grid {
grid-template-columns: repeat(2, 1fr);
gap: 2rem;
}
}
@media (max-width: 768px) {
.navbar-container {
padding: 0 1rem;
}
.navbar-links {
gap: 1rem;
}
.hero {
padding: 6rem 1rem 4rem;
}
.hero-content {
grid-template-columns: 1fr;
text-align: center;
gap: 3rem;
}
.hero-title {
font-size: 2.5rem;
}
.canvas-mock {
width: 300px;
height: 225px;
transform: none;
}
.code-showcase {
grid-template-columns: 1fr;
}
.features-grid {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.feature-card {
padding: 2rem;
}
.section-title {
font-size: 2rem;
}
.cta-content h2 {
font-size: 2rem;
}
.cta-actions {
flex-direction: column;
align-items: center;
}
.btn-large {
width: 100%;
max-width: 300px;
}
}
@media (max-width: 480px) {
.navbar-links {
gap: 0.5rem;
}
.nav-link {
font-size: 0.9rem;
}
.hero {
padding: 5rem 1rem 3rem;
}
.hero-title {
font-size: 2rem;
}
.hero-subtitle {
font-size: 1.2rem;
}
.features {
padding: 4rem 1rem;
}
.code-example {
padding: 4rem 1rem;
}
.cta {
padding: 4rem 1rem;
}
}
</style>
================================================
FILE: docs/.vitepress/theme/components/Playground.vue
================================================
<template>
<div class="playground-container" ref="playgroundContainer" :class="{ 'fullscreen': isFullscreen }">
<div class="playground-header" v-if="title || description">
<div class="header-content">
<h3 v-if="title">{{ title }}</h3>
<p v-if="description" class="playground-description">{{ description }}</p>
</div>
<div class="header-controls">
<!-- View Mode Buttons -->
<div class="view-mode-controls">
<button
:class="['view-mode-btn', { active: viewMode === 'code' }]"
@click="setViewMode('code')"
title="Show code only"
>
<span class="icon">📝</span>
Code
</button>
<button
:class="['view-mode-btn', { active: viewMode === 'preview' }]"
@click="setViewMode('preview')"
title="Show preview only"
>
<span class="icon">👁️</span>
Preview
</button>
<button
:class="['view-mode-btn', { active: viewMode === 'both' }]"
@click="setViewMode('both')"
title="Show both code and preview"
>
<span class="icon">⚡</span>
Both
</button>
</div>
<!-- Fullscreen Button -->
<button
class="fullscreen-btn"
@click="toggleFullscreen"
:title="isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'"
>
<span class="icon">{{ isFullscreen ? '🗗' : '⛶' }}</span>
</button>
</div>
</div>
<!-- Simplified header when no title/description but need controls -->
<div class="playground-controls-only" v-else>
<div class="header-controls">
<!-- View Mode Buttons -->
<div class="view-mode-controls">
<button
:class="['view-mode-btn', { active: viewMode === 'code' }]"
@click="setViewMode('code')"
title="Show code only"
>
<span class="icon">📝</span>
Code
</button>
<button
:class="['view-mode-btn', { active: viewMode === 'preview' }]"
@click="setViewMode('preview')"
title="Show preview only"
>
<span class="icon">👁️</span>
Preview
</button>
<button
:class="['view-mode-btn', { active: viewMode === 'both' }]"
@click="setViewMode('both')"
title="Show both code and preview"
>
<span class="icon">⚡</span>
Both
</button>
</div>
<!-- Fullscreen Button -->
<button
class="fullscreen-btn"
@click="toggleFullscreen"
:title="isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'"
>
<span class="icon">{{ isFullscreen ? '🗗' : '⛶' }}</span>
</button>
</div>
</div>
<div class="playground-content" :class="`view-mode-${viewMode}`">
<!-- Code Editor with CodeMirror -->
<div class="code-editor" v-show="viewMode === 'code' || viewMode === 'both'">
<div class="editor-header">
<div class="tabs">
<button
v-for="file in fileList"
:key="file.name"
:class="['tab', { active: activeFile === file.name }]"
@click="setActiveFile(file.name)"
>
{{ file.name }}
</button>
</div>
</div>
<div class="editor-content">
<div ref="editorContainer" class="codemirror-container"></div>
</div>
</div>
<!-- Preview Panel -->
<div class="preview-panel" v-show="viewMode === 'preview' || viewMode === 'both'">
<div class="preview-header">
<span>Preview</span>
</div>
<div class="preview-content">
<div v-if="error" class="error-display">
<div class="error-header">
<i class="error-icon">⚠️</i>
<strong>Error</strong>
</div>
<pre class="error-message">{{ error }}</pre>
</div>
<div v-else ref="canvasContainer" class="canvas-container"></div>
</div>
<!-- Console Accordion -->
<div class="console-accordion">
<button
class="console-toggle"
@click="consoleOpen = !consoleOpen"
:class="{ active: consoleOpen }"
>
<span>Console</span>
<span class="console-badge" v-if="logs.length > 0">{{ logs.length }}</span>
<span class="toggle-icon" :class="{ rotated: consoleOpen }">▼</span>
</button>
<div ref="consoleContent" class="console-content" v-show="consoleOpen">
<div v-if="logs.length === 0" class="console-empty">
No console output
</div>
<div v-else>
<div v-for="(log, index) in logs" :key="index" class="console-line">
<span class="console-timestamp">{{ log.timestamp }}</span>
<span class="console-message" :class="log.type">{{ log.message }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Fullscreen overlay backdrop -->
<div v-if="isFullscreen" class="fullscreen-backdrop" @click="exitFullscreen"></div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, nextTick, watch, onUnmounted } from 'vue'
import { EditorView, basicSetup } from 'codemirror'
import { html } from '@codemirror/lang-html'
import { javascript } from '@codemirror/lang-javascript'
import { oneDark } from '@codemirror/theme-one-dark'
import { EditorState, Compartment } from '@codemirror/state'
import pkg from "peggy"
import { dependencyConfig, CORE_FUNCTIONS, PRIMITIVE_COMPONENTS } from './config'
const { generate } = pkg
/**
* Enhanced Playground component for CanvasEngine with CodeMirror
*
* Features:
* - CodeMirror editor with syntax highlighting
* - View mode controls (Code, Preview, Both)
* - Fullscreen mode
* - Accordion console
* - Auto-reload on code changes
* - Error display in preview
* - Automatic WebGL context management (destroys when out of viewport)
*
* The component automatically monitors its visibility using Intersection Observer
* and destroys the WebGL context when the playground is not visible to prevent
* the "Too many active WebGL contexts" warning. It recreates the context when
* the playground becomes visible again.
*
* @example
* ```vue
* <Playground
* title="Basic Canvas Example"
* description="A simple canvas with a red rectangle"
* :files="{
* 'app.ce': '<Canvas><Rect color="red" width={100} height={100} /></Canvas>'
* }"
* defaultViewMode="both"
* />
* ```
*/
interface PlaygroundProps {
/** Title of the playground example */
title?: string
/** Description of what the example demonstrates */
description?: string
/** Custom files to include in the editor */
files?: Record<string, string>
/** Height of the playground */
height?: number
/** Default view mode: 'code', 'preview', or 'both' */
defaultViewMode?: 'code' | 'preview' | 'both'
}
interface PlaygroundFile {
name: string
content: string
language: string
}
interface ConsoleLog {
timestamp: string
message: string
type: 'log' | 'error' | 'warn' | 'info'
}
const props = withDefaults(defineProps<PlaygroundProps>(), {
height: 600,
files: () => ({}),
defaultViewMode: 'both'
})
// Reactive state
const editorContainer = ref<HTMLDivElement>()
const canvasContainer = ref<HTMLDivElement>()
const consoleContent = ref<HTMLDivElement>()
const activeFile = ref('app.ce')
const error = ref('')
const logs = ref<ConsoleLog[]>([])
const consoleOpen = ref(false)
const viewMode = ref<'code' | 'preview' | 'both'>(props.defaultViewMode)
const isFullscreen = ref(false)
let currentApp: any = null
let editorView: EditorView | null = null
let parser: any = null
let isConsoleScrollAtBottom = ref(true)
let consoleScrollContainer: HTMLElement | null = null
let messageListener: ((event: MessageEvent) => void) | null = null
// Unique identifier for this playground instance
const playgroundId = ref(`playground-${Math.random().toString(36).substr(2, 9)}-${Date.now()}`)
// Intersection Observer for viewport detection
let intersectionObserver: IntersectionObserver | null = null
const isInViewport = ref(true)
const playgroundContainer = ref<HTMLDivElement>()
/**
* Set view mode and update layout
*/
const setViewMode = (mode: 'code' | 'preview' | 'both') => {
viewMode.value = mode
// Trigger editor resize after view mode change
nextTick(() => {
if (editorView) {
editorView.requestMeasure()
}
})
}
/**
* Toggle fullscreen mode
*/
const toggleFullscreen = () => {
isFullscreen.value = !isFullscreen.value
if (isFullscreen.value) {
// Add fullscreen class to body to prevent scrolling
document.body.classList.add('playground-fullscreen-active')
// Add escape key listener
document.addEventListener('keydown', handleEscapeKey)
} else {
exitFullscreen()
}
// Trigger editor resize after fullscreen change
nextTick(() => {
if (editorView) {
editorView.requestMeasure()
}
})
}
/**
* Exit fullscreen mode
*/
const exitFullscreen = () => {
isFullscreen.value = false
document.body.classList.remove('playground-fullscreen-active')
document.removeEventListener('keydown', handleEscapeKey)
// Trigger editor resize after exiting fullscreen
nextTick(() => {
if (editorView) {
editorView.requestMeasure()
}
})
}
/**
* Handle escape key to exit fullscreen
*/
const handleEscapeKey = (event: KeyboardEvent) => {
if (event.key === 'Escape' && isFullscreen.value) {
exitFullscreen()
}
}
/**
* Initialize viewport intersection observer to destroy playground when not visible
* This prevents accumulation of WebGL contexts that cause the "Too many active WebGL contexts" warning
*/
const initViewportObserver = () => {
if (!playgroundContainer.value) return
intersectionObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const wasInViewport = isInViewport.value
isInViewport.value = entry.isIntersecting
// If playground was visible and is now not visible, destroy it
if (wasInViewport && !isInViewport.value) {
addLog('Playground moved out of viewport, destroying to free WebGL context', 'info')
clearPreview()
}
// If playground becomes visible again, recreate it
else if (!wasInViewport && isInViewport.value) {
addLog('Playground back in viewport, recreating', 'info')
// Small delay to ensure DOM is ready
setTimeout(() => {
runCode()
}, 100)
}
})
},
{
// Trigger when 10% of the playground is visible/hidden
threshold: 0.1,
// Add some margin to trigger slightly before/after entering viewport
rootMargin: '50px'
}
)
intersectionObserver.observe(playgroundContainer.value)
}
/**
* Cleanup viewport observer
*/
const cleanupViewportObserver = () => {
if (intersectionObserver) {
intersectionObserver.disconnect()
intersectionObserver = null
}
}
/**
* Check if console scroll is at bottom
*/
const checkConsoleScrollPosition = () => {
if (consoleScrollContainer) {
const threshold = 5 // pixels threshold
const isAtBottom = (consoleScrollContainer as HTMLElement).scrollTop + (consoleScrollContainer as HTMLElement).clientHeight >= (consoleScrollContainer as HTMLElement).scrollHeight - threshold
isConsoleScrollAtBottom.value = isAtBottom
}
}
/**
* Scroll console to bottom if needed
*/
const scrollConsoleToBottomIfNeeded = () => {
if (isConsoleScrollAtBottom.value && consoleScrollContainer) {
nextTick(() => {
(consoleScrollContainer as HTMLElement).scrollTop = (consoleScrollContainer as HTMLElement).scrollHeight
})
}
}
/**
* Initialize console scroll tracking
*/
const initConsoleScroll = () => {
nextTick(() => {
if (consoleContent.value) {
consoleScrollContainer = consoleContent.value
// Add scroll event listener
consoleScrollContainer.addEventListener('scroll', checkConsoleScrollPosition)
// Initial scroll position check
checkConsoleScrollPosition()
}
})
}
/**
* Add log to console
*/
const addLog = (message: string, type: ConsoleLog['type'] = 'log') => {
const timestamp = new Date().toLocaleTimeString()
logs.value.push({ timestamp, message, type })
scrollConsoleToBottomIfNeeded()
}
/**
* Initialize the PEG.js parser with CanvasEngine grammar
*/
const initParser = async () => {
try {
// Load the grammar from the compiler package
const response = await fetch('/grammar.pegjs')
const grammar = await response.text()
parser = generate(grammar)
addLog('Parser initialized successfully', 'info')
} catch (err: any) {
addLog(`Failed to initialize parser: ${err.message}`, 'error')
console.error('Parser initialization error:', err)
}
}
/**
* Format a syntax error message with visual pointer to the error location
* Similar to the function in packages/compiler/index.ts
*/
const showErrorMessage = (template: string, error: any): string => {
if (!error.location) {
return `Syntax error: ${error.message}`;
}
const lines = template.split('\n');
const { line, column } = error.location.start;
const errorLine = lines[line - 1] || '';
// Create a visual pointer with an arrow
const pointer = ' '.repeat(column - 1) + '^';
return `Syntax error at line ${line}, column ${column}: ${error.message}\n\n` +
`${errorLine}\n${pointer}\n`;
}
/**
* Default files for the playground
*/
const defaultFiles: Record<string, PlaygroundFile> = {
'app.ce': {
name: 'app.ce',
content: ``,
language: 'html'
}
}
/**
* Merge default files with user-provided files
*/
const allFiles = computed(() => {
const result: Record<string, PlaygroundFile> = { ...defaultFiles }
if (props.files) {
Object.entries(props.files).forEach(([fileName, content]) => {
result[fileName] = {
name: fileName,
content,
language: fileName.endsWith('.ce') ? 'html' : fileName.endsWith('.ts') ? 'typescript' : 'javascript'
}
})
}
return result
})
/**
* List of files for the tab interface
*/
const fileList = computed(() => Object.values(allFiles.value))
/**
* Set the active file and update editor
*/
const setActiveFile = (fileName: string) => {
if (activeFile.value === fileName) return
// Save current file content
if (editorView && allFiles.value[activeFile.value]) {
allFiles.value[activeFile.value].content = editorView.state.doc.toString()
}
activeFile.value = fileName
updateEditor()
}
/**
* Update CodeMirror editor content and language
*/
const updateEditor = () => {
if (!editorView || !editorContainer.value) return
const currentFile = allFiles.value[activeFile.value]
if (!currentFile) return
// Destroy current editor and recreate with new language
editorView.destroy()
const extensions = [
basicSetup,
getLanguageExtension(activeFile.value),
oneDark,
EditorView.updateListener.of((update) => {
if (update.docChanged) {
// Update file content
allFiles.value[activeFile.value].content = update.state.doc.toString()
// Auto-reload preview with debounce
clearTimeout(autoReloadTimeout)
autoReloadTimeout = setTimeout(() => {
runCode()
}, 500)
}
}),
EditorView.theme({
'&': {
fontSize: '14px',
height: '100%',
},
'.cm-content': {
padding: '16px',
minHeight: '100%'
},
'.cm-editor': {
height: '100%',
},
'.cm-scroller': {
fontFamily: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
}
})
]
const state = EditorState.create({
doc: currentFile.content,
extensions
})
editorView = new EditorView({
state,
parent: editorContainer.value
})
}
/**
* Get language extension based on file type
*/
const getLanguageExtension = (fileName: string) => {
if (fileName.endsWith('.js')) {
return javascript()
} else if (fileName.endsWith('.ts')) {
return javascript({ typescript: true })
} else if (fileName.endsWith('.ce')) {
return html()
} else {
return html() // Default fallback
}
}
/**
* Initialize CodeMirror editor
*/
const initEditor = () => {
if (!editorContainer.value) return
const currentFile = allFiles.value[activeFile.value]
if (!currentFile) return
const extensions = [
basicSetup,
getLanguageExtension(activeFile.value),
oneDark,
EditorView.updateListener.of((update) => {
if (update.docChanged) {
// Update file content
allFiles.value[activeFile.value].content = update.state.doc.toString()
// Auto-reload preview with debounce
clearTimeout(autoReloadTimeout)
autoReloadTimeout = setTimeout(() => {
runCode()
}, 500)
}
}),
EditorView.theme({
'&': {
fontSize: '14px',
height: '100%',
},
'.cm-content': {
padding: '16px',
minHeight: '100%'
},
'.cm-editor': {
height: '100%',
},
'.cm-scroller': {
fontFamily: "'Monaco', 'Menlo', 'Ubuntu Mono', monospace"
}
})
]
const state = EditorState.create({
doc: currentFile.content,
extensions
})
editorView = new EditorView({
state,
parent: editorContainer.value
})
}
/**
* Clear the preview and reset state
*/
const clearPreview = () => {
error.value = ''
logs.value = []
// Clean up message listener
if (messageListener) {
window.removeEventListener('message', messageListener)
messageListener = null
}
if (currentApp) {
try {
// Destroy PIXI application properly to free WebGL context
if (currentApp.destroy) {
currentApp.destroy(true, { children: true, texture: true, baseTexture: true })
}
} catch (e) {
console.warn('Error destroying app:', e)
}
currentApp = null
}
if (canvasContainer.value) {
// Remove all child elements including iframes
while (canvasContainer.value.firstChild) {
const child = canvasContainer.value.firstChild
// If it's an iframe, ensure proper cleanup
if (child instanceof HTMLIFrameElement) {
try {
// Try to destroy any PIXI apps in the iframe
const iframeWindow = child.contentWindow
if (iframeWindow && (iframeWindow as any).PIXI) {
const pixiApps = (iframeWindow as any).PIXI.Application?.instances || []
pixiApps.forEach((app: any) => {
try {
app.destroy(true, { children: true, texture: true, baseTexture: true })
} catch (e) {
console.warn('Error destroying iframe PIXI app:', e)
}
})
}
} catch (e) {
// Cross-origin or other access issues, ignore
}
}
canvasContainer.value.removeChild(child)
}
}
}
/**
* Generate HTML content for the iframe sandbox
* Creates a complete HTML page with CanvasEngine imports and bootstrap code
*/
const generateIframeContent = (componentFunction: string, dependencies: Set<string> = new Set(), scopedStyle: string = ''): string => {
// Generate script tags for dependencies
const dependencyScripts = Array.from(dependencies)
.map(dep => dependencyConfig[dep])
.filter(config => config && config.url)
.map(config => `<script src="${config.url}"><\/script>`)
.join('\n ')
// Generate core functions extraction code
const coreExtractionCode = CORE_FUNCTIONS.map(func =>
`if (!CanvasEngine.${func}) throw new Error("${func} function not found in CanvasEngine");
coreExports.${func} = CanvasEngine.${func};`
).join('\n ')
// Generate component extraction code
const componentExtractionCode = PRIMITIVE_COMPONENTS.map(comp =>
`if (CanvasEngine.${comp}) componentExports.${comp} = CanvasEngine.${comp};`
).join('\n ')
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CanvasEngine Playground</title>
<script src="https://cdn.jsdelivr.net/npm/pixi.js@latest/dist/pixi.min.js"><\/script>
<style>
body { overflow: hidden; margin: 0; padding: 0; font-family: Arial, sans-serif; background: #f5f5f5; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
#root { width: 100%; height: 100%; min-height: 400px; }
.error {
color: #dc2626;
background: #fef2f2;
border: 1px solid #fecaca;
border-radius: 8px;
padding: 16px;
margin: 16px;
font-family: monospace;
white-space: pre-wrap;
line-height: 1.4;
max-height: 80vh;
overflow-y: auto;
}
.error strong {
color: #b91c1c;
font-weight: 600;
}
.error details {
margin-top: 12px;
cursor: pointer;
}
.error summary {
color: #b91c1c;
font-weight: 500;
padding: 4px 0;
user-select: none;
}
.error summary:hover {
background: rgba(220, 38, 38, 0.1);
border-radius: 4px;
padding: 4px 8px;
}
.error pre {
background: #fff;
border: 1px solid #fecaca;
border-radius: 4px;
padding: 8px;
margin: 8px 0;
font-size: 11px;
white-space: pre-wrap;
overflow-x: auto;
max-height: 200px;
overflow-y: auto;
}
.loading { text-align: center; padding: 20px; color: #666; }
</style>
<style id="playground-scoped-style">${scopedStyle}</style>
</head>
<body>
<div id="root"></div>
${dependencyScripts}
<script type="module">
// Set up console interception FIRST, before anything else
const originalConsole = window.console;
window.console = {
...originalConsole,
log: (...args) => {
try {
const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)).join(' ');
window.parent.postMessage({
type: 'playground-console',
playgroundId: '${playgroundId.value}',
logType: 'log',
message: message,
timestamp: new Date().toISOString()
}, '*');
} catch (e) {
// Fallback to original console if message posting fails
}
originalConsole.log(...args);
},
error: (...args) => {
const errorMsg = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)).join(' ');
window.parent.postMessage({
type: 'playground-console',
playgroundId: '${playgroundId.value}',
logType: 'error',
message: errorMsg,
timestamp: new Date().toISOString(),
isSignificantError: errorMsg.toLowerCase().includes('error') || errorMsg.toLowerCase().includes('failed')
}, '*');
originalConsole.error(...args);
},
warn: (...args) => {
const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)).join(' ');
window.parent.postMessage({
type: 'playground-console',
playgroundId: '${playgroundId.value}',
logType: 'warn',
message: message,
timestamp: new Date().toISOString()
}, '*');
originalConsole.warn(...args);
},
info: (...args) => {
const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)).join(' ');
window.parent.postMessage({
type: 'playground-console',
playgroundId: '${playgroundId.value}',
logType: 'info',
message: message,
timestamp: new Date().toISOString()
}, '*');
originalConsole.info(...args);
}
};
console.log("Starting CanvasEngine playground...");
// Enhanced error handling function
function handleError(error, context = 'Unknown') {
console.error(\`\${context} error:\`, error);
// Send error to parent window for console logging with unique playground ID
try {
window.parent.postMessage({
type: 'playground-error',
playgroundId: '${playgroundId.value}',
context: context,
message: error.message || error.toString(),
stack: error.stack || '',
timestamp: new Date().toISOString()
}, '*');
} catch (postError) {
console.warn('Could not send error to parent:', postError);
}
const rootElement = document.getElementById("root");
if (rootElement && !rootElement.querySelector('.error')) {
let errorMessage = error.message || error.toString();
if (errorMessage.includes("already has a handler")) {
errorMessage = "PixiJS extension conflict detected. Try refreshing the page.";
}
// Include stack trace if available
let stackTrace = '';
if (error.stack) {
stackTrace = '<br/><br/><details><summary>Stack Trace</summary><pre style="font-size: 11px; margin: 8px 0; white-space: pre-wrap;">' +
error.stack + '</pre></details>';
}
rootElement.innerHTML = '<div class="error"><strong>' + context + ' Error:</strong><br/>' +
errorMessage + stackTrace +
'<br/><br/><small>If this persists, try refreshing the page.</small></div>';
}
}
// Global error handler for uncaught exceptions
window.addEventListener('error', (event) => {
console.error('Global JavaScript Error:', event.error || event.message);
const error = event.error || new Error(event.message);
handleError(error, 'Global JavaScript');
// Prevent default browser error handling
event.preventDefault();
});
// Global promise rejection handler
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled Promise Rejection:', event.reason);
const error = event.reason instanceof Error ? event.reason : new Error(event.reason);
handleError(error, 'Unhandled Promise');
// Prevent default browser rejection handling
event.preventDefault();
});
async function initializeCanvas() {
try {
const rootElement = document.getElementById("root");
if (!rootElement) throw new Error("Root element not found");
// Extract all core functions and primitive components from CanvasEngine
const coreExports = {};
const componentExports = {};
// Get core functions
${coreExtractionCode}
// Get primitive components
${componentExtractionCode}
// Destructure for easy access
const { ${CORE_FUNCTIONS.join(', ')} } = coreExports;
const { ${PRIMITIVE_COMPONENTS.join(', ')} } = componentExports;
let comp = null
// Wrap component function execution in try-catch to catch syntax errors
try {
comp = ${componentFunction}
} catch (syntaxError) {
throw new Error("Syntax error in component code: " + syntaxError.message);
}
if (typeof comp !== "function") {
throw new Error("Component is not a function: " + typeof comp);
}
// Wrap the component function to catch async errors during execution
const wrappedComp = function(...args) {
try {
const result = comp(...args);
// If the result is a promise, catch any rejections
if (result && typeof result.then === 'function') {
return result.catch(asyncError => {
handleError(asyncError, 'Component Async');
throw asyncError;
});
}
return result;
} catch (syncError) {
handleError(syncError, 'Component Sync');
throw syncError;
}
};
// Wrap bootstrapCanvas call with additional error handling
const result = await Promise.resolve(bootstrapCanvas(rootElement, wrappedComp))
.catch(bootstrapError => {
handleError(bootstrapError, 'Bootstrap');
throw bootstrapError;
});
console.log("CanvasEngine initialized successfully");
} catch (error) {
handleError(error, 'Initialization');
}
}
// Initialize with additional promise rejection handling
initializeCanvas().catch(error => {
handleError(error, 'Main Initialization');
});
<\/script>
</body>
</html>`
}
/**
* Process all imports and resolve local files
*
* @param {string} scriptContent - The script content to transform
* @returns {Promise<{transformedContent: string, dependencies: Set<string>}>} - The transformed script content with resolved imports and dependencies
*/
const processImports = async (scriptContent: string): Promise<{transformedContent: string, dependencies: Set<string>}> => {
let transformedContent = scriptContent
let resolvedModules = ''
const dependencies = new Set<string>()
// Transform external library imports
for (const [packageName, config] of Object.entries(dependencyConfig)) {
const regex = new RegExp(`import\\s*\\{\\s*([^}]+)\\s*\\}\\s*from\\s*['"]${packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"];?`, 'g')
transformedContent = transformedContent.replace(regex, (match, imports) => {
const cleanImports = imports.trim()
dependencies.add(packageName)
return `const { ${cleanImports} } = ${config.globalName};`
})
}
// Process local file imports
const importRegex = /import\s+(.+?)\s+from\s+['"](.+?)['"];?/g
let importMatch
while ((importMatch = importRegex.exec(scriptContent)) !== null) {
const [fullMatch, importClause, filePath] = importMatch
// Skip CanvasEngine imports (already processed)
if (filePath[0] != '.') continue
// Resolve local file path (remove ./ and normalize)
const normalizedPath = filePath.replace(/^\.\//, '').replace(/^\//, '')
// Check if file exists in allFiles
const targetFile = allFiles.value[normalizedPath]
if (!targetFile) {
addLog(`Warning: File not found: ${filePath}`, 'warn')
continue
}
// Process the file based on its extension
let processedContent = ''
if (normalizedPath.endsWith('.ce')) {
// Parse .ce file like a component
const ceScriptMatch = targetFile.content.match(/<script>([\s\S]*?)<\/script>/)
const ceScriptContent = ceScriptMatch ? ceScriptMatch[1].trim() : ""
// Extract all style tags
const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/g
let ceStyle = ""
let styleMatch
while ((styleMatch = styleRegex.exec(targetFile.content)) !== null) {
ceStyle += styleMatch[1].trim() + "\n"
}
// Recursively process imports in the .ce file
const processedCeScript = await processImports(ceScriptContent)
// Merge dependencies from nested imports
processedCeScript.dependencies.forEach(dep => dependencies.add(dep))
const ceTemplate = targetFile.content.replace(/<script>[\s\S]*?<\/script>/, "")
.replace(/<style[^>]*>[\s\S]*?<\/style>/g, "")
.replace(/^\s+|\s+$/g, '')
let parsedCeTemplate
try {
parsedCeTemplate = parser.parse(ceTemplate)
} catch (parseError: any) {
const errorMsg = showErrorMessage(ceTemplate, parseError)
throw new Error(`Error parsing template in ${normalizedPath}:\n${errorMsg}`)
}
// Generate component function for .ce file with style
processedContent = `
function ${getModuleName(normalizedPath)}($$props = {}) {
const $props = useProps($$props);
const defineProps = useDefineProps($$props);
${processedCeScript.transformedContent}
const __style = ${JSON.stringify(ceStyle)};
if (__style) {
const styleEl = document.createElement('style');
styleEl.textContent = __style;
document.head.appendChild(styleEl);
}
return ${parsedCeTemplate};
}
`
} else if (normalizedPath.endsWith('.js') || normalizedPath.endsWith('.ts')) {
// Process .js/.ts files
const result = await processImports(targetFile.content)
// Transform ES6 exports to object assignments
const moduleName = getModuleName(normalizedPath)
const transformedJsContent = transformExports(result.transformedContent, moduleName)
// Wrap in a function that creates and returns the module object
processedContent = `
const ${moduleName} = (function() {
${transformedJsContent}
})();
`
// Merge dependencies from nested imports
result.dependencies.forEach(dep => dependencies.add(dep))
}
// Add the processed content to resolved modules
resolvedModules += processedContent + '\n'
// Replace the import statement with variable assignment
const moduleName = getModuleName(normalizedPath)
if (importClause.includes('{')) {
// Named imports: import { func1, func2 } from './utils.js'
transformedContent = transformedContent.replace(fullMatch, `const ${importClause} = ${moduleName};`)
} else {
// Default import: import HelloWorld from './hello.ce'
const varName = importClause.trim()
transformedContent = transformedContent.replace(fullMatch, `const ${varName} = ${moduleName};`)
}
}
return {
transformedContent: resolvedModules + transformedContent,
dependencies
}
}
/**
* Transform ES6 exports to return statement with object for sandbox compatibility
*/
const transformExports = (jsContent: string, moduleName: string): string => {
let transformedContent = jsContent
const exportedNames: string[] = []
let hasDefaultExport = false
let defaultExportValue = ''
// Transform named exports: export const foo = ... → const foo = ...;
transformedContent = transformedContent.replace(/export\s+const\s+(\w+)\s*=\s*([^;]+);?/g, (match, name, value) => {
exportedNames.push(name)
return `const ${name} = ${value};`
})
// Transform named exports: export function foo() {} → function foo() {}
transformedContent = transformedContent.replace(/export\s+function\s+(\w+)\s*\([^)]*\)\s*\{[^}]*\}/g, (match, name) => {
exportedNames.push(name)
const funcDeclaration = match.replace(/^export\s+/, '')
return funcDeclaration
})
// Transform default export: export default ... → store the value
transformedContent = transformedContent.replace(/export\s+default\s+([^;]+);?/g, (match, value) => {
hasDefaultExport = true
defaultExportValue = value
return '' // Remove the export default line
})
// Transform export { ... } syntax
transformedContent = transformedContent.replace(/export\s*\{\s*([^}]+)\s*\}/g, (match, exports) => {
const exportList = exports.split(',').map(exp => exp.trim())
exportList.forEach(exp => {
const [localName, exportedName] = exp.includes(' as ') ? exp.split(' as ').map(s => s.trim()) : [exp, exp]
if (!exportedNames.includes(exportedName)) {
exportedNames.push(exportedName)
}
})
return '' // Remove the export statement
})
// Build the return object
const returnObject: string[] = []
// Add named exports
exportedNames.forEach(name => {
returnObject.push(`${name}: ${name}`)
})
// Add default export if present
if (hasDefaultExport) {
returnObject.push(`default: ${defaultExportValue}`)
}
// Add return statement with the module object
if (returnObject.length > 0) {
transformedContent += `\n\nreturn {\n ${returnObject.join(',\n ')}\n};`
} else {
transformedContent += `\n\nreturn {};`
}
return transformedContent
}
/**
* Generate a module name from file path
*/
const getModuleName = (filePath: string): string => {
return filePath
.replace(/[^a-zA-Z0-9]/g, '_')
.replace(/^_+|_+$/g, '')
.replace(/_+/g, '_') + '_module'
}
/**
* Parse and run the CanvasEngine code using the PEG.js parser
*/
const runCode = async () => {
try {
// Don't run if not in viewport to save resources
if (!isInViewport.value) {
addLog('Playground not in viewport, skipping execution', 'info')
return
}
clearPreview()
// Ensure parser is initialized
if (!parser) {
await initParser()
if (!parser) {
throw new Error('Parser initialization failed')
}
}
// Get the main component file content
const mainFile = allFiles.value['app.ce']
if (!mainFile) {
throw new Error('app.ce file is required')
}
// Extract the script, style, and template content like in the compiler
const scriptMatch = mainFile.content.match(/<script>([\s\S]*?)<\/script>/)
let scriptContent = scriptMatch ? scriptMatch[1].trim() : ""
// Extract all style tags
const styleRegex = /<style[^>]*>([\s\S]*?)<\/style>/g
let scopedStyle = ""
let styleMatch
while ((styleMatch = styleRegex.exec(mainFile.content)) !== null) {
scopedStyle += styleMatch[1].trim() + "\n"
}
// Process all imports and resolve local files
const importResult = await processImports(scriptContent)
scriptContent = importResult.transformedContent
const dependencies = importResult.dependencies
// Always ensure CanvasEngine is available and is the first dependency
const orderedDependencies = new Set(['canvasengine'])
dependencies.forEach(dep => {
if (dep !== 'canvasengine') {
orderedDependencies.add(dep)
}
})
// Extract template (everything except script and style)
const template = mainFile.content.replace(/<script>[\s\S]*?<\/script>/, "")
.replace(/<style[^>]*>[\s\S]*?<\/style>/g, "")
.replace(/^\s+|\s+$/g, '')
let parsedTemplate
try {
// Parse the template using the PEG.js parser
parsedTemplate = parser.parse(template)
addLog(`Template parsed successfully: ${parsedTemplate}`, 'info')
} catch (parseError: any) {
const errorMsg = showErrorMessage(template, parseError)
throw new Error(`Error parsing template:\n${errorMsg}`)
}
// Generate the complete component function like in the compiler
// Make sure the component function returns the correct structure
const componentFunction = `
function component($$props = {}) {
const $props = useProps($$props);
const defineProps = useDefineProps($$props);
${scriptContent}
return ${parsedTemplate};
}`
// Create sandbox iframe with CanvasEngine
if (canvasContainer.value) {
const iframe = document.createElement('iframe')
iframe.style.cssText = `
width: 100%;
height: 100%;
border: none;
background: white;
`
// Generate the complete HTML for the iframe
const iframeContent = generateIframeContent(componentFunction, orderedDependencies, scopedStyle)
// Set up message listener for iframe communication
messageListener = (event: MessageEvent) => {
if (event.data && event.data.playgroundId === playgroundId.value) {
if (event.data.type === 'playground-error') {
const { context, message, stack } = event.data
const fullErrorMsg = stack ? `${message}\n\nStack trace:\n${stack}` : message
error.value = fullErrorMsg
addLog(`${context}: ${message}`, 'error')
} else if (event.data.type === 'playground-console') {
const { logType, message, isSignificantError } = event.data
addLog(message, logType as ConsoleLog['type'])
// Set error in preview if it's a significant console error
if (isSignificantError) {
error.value = message
}
}
}
}
window.addEventListener('message', messageListener)
iframe.onload = () => {
try {
iframe.contentDocument?.open()
iframe.contentDocument?.write(iframeContent)
iframe.contentDocument?.close()
} catch (writeError) {
console.error('Error writing to iframe:', writeError)
addLog(`Error writing to iframe: ${writeError.message}`, 'error')
}
}
canvasContainer.value.appendChild(iframe)
}
} catch (err: any) {
error.value = err.message || 'Unknown error occurred'
addLog(`Error: ${err.message}`, 'error')
console.error('Playground error:', err)
}
}
let autoReloadTimeout: ReturnType<typeof setTimeout>
// Watch for console open/close to initialize scroll tracking
watch(consoleOpen, (isOpen) => {
if (isOpen) {
initConsoleScroll()
}
})
// Lifecycle
onMounted(() => {
nextTick(async () => {
await initParser()
initEditor()
initViewportObserver()
runCode()
})
})
onUnmounted(() => {
if (editorView) {
editorView.destroy()
}
// Clean up console scroll event listener
if (consoleScrollContainer) {
(consoleScrollContainer as HTMLElement).removeEventListener('scroll', checkConsoleScrollPosition)
}
// Clean up message listener
if (messageListener) {
window.removeEventListener('message', messageListener)
messageListener = null
}
// Clean up viewport observer
cleanupViewportObserver()
// Clean up preview resources
clearPreview()
// Clean up fullscreen
if (isFullscreen.value) {
exitFullscreen()
}
clearTimeout(autoReloadTimeout)
})
</script>
<style scoped>
.playground-container {
margin: 20px 0;
border: 1px solid var(--vp-c-border);
border-radius: 12px;
overflow: hidden;
background: var(--vp-c-bg);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
position: relative;
}
/* Fullscreen styles */
.playground-container.fullscreen {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
margin: 0;
border-radius: 0;
z-index: 9999;
box-shadow: none;
}
.fullscreen-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
z-index: -1;
}
:global(.playground-fullscreen-active) {
overflow: hidden;
}
.playground-header {
padding: 20px 24px;
background: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-border);
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 20px;
}
.playground-controls-only {
padding: 12px 24px;
background: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-border);
display: flex;
justify-content: flex-end;
}
.header-content {
flex: 1;
}
.header-controls {
display: flex;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
.playground-header h3 {
margin: 0 0 8px 0;
font-size: 20px;
font-weight: 600;
color: var(--vp-c-text-1);
}
.playground-description {
margin: 0;
font-size: 14px;
color: var(--vp-c-text-2);
line-height: 1.6;
}
.playground-content {
display: flex;
height: v-bind('props.height + "px"');
}
/* Fullscreen content adjustments */
.playground-container.fullscreen .playground-content {
height: calc(100vh - 60px); /* Adjust for header */
}
/* View mode layouts */
.playground-content.view-mode-code {
display: block;
}
.playground-content.view-mode-code .code-editor {
width: 100%;
height: 100%;
border-right: none;
}
.playground-content.view-mode-preview {
display: block;
}
.playground-content.view-mode-preview .preview-panel {
width: 100%;
height: 100%;
}
.playground-content.view-mode-both {
display: flex;
}
.playground-content.view-mode-both .code-editor {
width: 40%;
border-right: 1px solid var(--vp-c-border);
}
.playground-content.view-mode-both .preview-panel {
flex: 1;
}
/* View Mode Controls */
.view-mode-controls {
display: flex;
gap: 4px;
background: var(--vp-c-bg-elv);
border-radius: 8px;
padding: 4px;
}
.view-mode-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 12px;
border: none;
background: transparent;
color: var(--vp-c-text-2);
cursor: pointer;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
white-space: nowrap;
}
.view-mode-btn:hover {
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
}
.view-mode-btn.active {
background: var(--vp-c-brand);
color: white;
}
.view-mode-btn .icon {
font-size: 14px;
}
/* Fullscreen Button */
.fullscreen-btn {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border: 1px solid var(--vp-c-border);
background: var(--vp-c-bg);
color: var(--vp-c-text-2);
cursor: pointer;
border-radius: 8px;
transition: all 0.2s;
font-size: 16px;
}
.fullscreen-btn:hover {
background: var(--vp-c-bg-elv);
color: var(--vp-c-text-1);
border-color: var(--vp-c-brand);
}
.fullscreen-btn .icon {
font-size: 18px;
}
/* Code Editor */
.code-editor {
width: 40%;
display: flex;
flex-direction: column;
border-right: 1px solid var(--vp-c-border);
background: var(--vp-c-bg-soft);
}
.editor-header {
padding: 12px 16px;
background: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-border);
}
.tabs {
display: flex;
gap: 4px;
}
.tab {
padding: 8px 16px;
border: none;
background: transparent;
color: var(--vp-c-text-2);
cursor: pointer;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
}
.tab:hover {
background: var(--vp-c-bg-elv);
color: var(--vp-c-text-1);
}
.tab.active {
background: var(--vp-c-brand);
color: white;
}
.editor-content {
flex: 1;
position: relative;
overflow: auto;
min-height: 0;
}
.codemirror-container {
height: 100%;
overflow: auto;
}
/* Preview Panel */
.preview-panel {
flex: 1;
display: flex;
flex-direction: column;
background: var(--vp-c-bg);
}
.preview-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: var(--vp-c-bg-soft);
border-bottom: 1px solid var(--vp-c-border);
font-size: 13px;
font-weight: 600;
color: var(--vp-c-text-1);
}
.clear-button {
padding: 6px 12px;
background: transparent;
color: var(--vp-c-text-2);
border: 1px solid var(--vp-c-border);
border-radius: 6px;
cursor: pointer;
font-size: 12px;
font-weight: 500;
transition: all 0.2s;
}
.clear-button:hover {
background: var(--vp-c-bg-elv);
color: var(--vp-c-text-1);
}
.preview-content {
flex: 1;
overflow: hidden;
display: flex;
background: #f8f9fa;
height: 100%;
position: relative;
}
.canvas-container {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* Error Display */
.error-display {
width: 100%;
height: 100%;
background: #fef2f2;
border: 1px solid #fecaca;
border-radius: 8px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.error-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
color: #dc2626;
font-weight: 600;
}
.error-icon {
font-size: 18px;
}
.error-message {
margin: 0;
padding: 12px;
background: #fff;
border: 1px solid #fecaca;
border-radius: 6px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 13px;
color: #dc2626;
white-space: pre-wrap;
overflow-x: auto;
}
/* Console Accordion */
.console-accordion {
border-top: 1px solid var(--vp-c-border);
background: var(--vp-c-bg-soft);
}
.console-toggle {
width: 100%;
padding: 12px 16px;
background: transparent;
border: none;
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
font-size: 13px;
font-weight: 600;
color: var(--vp-c-text-1);
transition: all 0.2s;
}
.console-toggle:hover {
background: var(--vp-c-bg-elv);
}
.console-toggle.active {
background: var(--vp-c-bg-elv);
}
.console-badge {
background: var(--vp-c-brand);
color: white;
padding: 2px 6px;
border-radius: 10px;
font-size: 11px;
font-weight: 600;
min-width: 18px;
text-align: center;
}
.toggle-icon {
transition: transform 0.2s;
font-size: 12px;
}
.toggle-icon.rotated {
transform: rotate(180deg);
}
.console-content {
max-height: 200px;
overflow-y: auto;
border-top: 1px solid var(--vp-c-border);
background: var(--vp-c-bg);
}
.console-empty {
padding: 16px;
text-align: center;
color: var(--vp-c-text-2);
font-size: 13px;
font-style: italic;
}
.console-line {
padding: 8px 16px;
border-bottom: 1px solid var(--vp-c-divider);
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 12px;
display: flex;
gap: 12px;
}
.console-line:last-child {
border-bottom: none;
}
.console-timestamp {
color: var(--vp-c-text-3);
font-size: 11px;
flex-shrink: 0;
}
.console-message {
color: var(--vp-c-text-1);
flex: 1;
}
.console-message.error {
color: #dc2626;
}
.console-message.warn {
color: #d97706;
}
.console-message.info {
color: #2563eb;
}
/* Responsive */
@media (max-width: 1024px) {
.playground-header {
flex-direction: column;
align-items: stretch;
gap: 16px;
}
.header-controls {
justify-content: center;
}
.playground-controls-only {
justify-content: center;
}
.playground-content.view-mode-both {
flex-direction: column;
height: auto;
min-height: 600px;
}
.playground-content.view-mode-both .code-editor {
width: 100%;
height: 250px;
border-right: none;
border-bottom: 1px solid var(--vp-c-border);
}
.playground-content.view-mode-code .code-editor {
height: 100%;
}
.playground-content.view-mode-preview .preview-panel {
height: 100%;
}
.editor-content {
overflow: auto;
}
.preview-panel {
flex: 1;
min-height: 350px;
}
/* View mode controls responsive */
.view-mode-controls {
flex-wrap: wrap;
justify-content: center;
}
.view-mode-btn {
font-size: 12px;
padding: 6px 10px;
}
.fullscreen-btn {
width: 36px;
height: 36px;
}
}
@media (max-width: 768px) {
.playground-header {
padding: 16px 20px;
}
.playground-controls-only {
padding: 8px 20px;
}
.playground-content.view-mode-both {
min-height: 700px;
}
.playground-content.view-mode-both .code-editor {
height: 280px;
}
.playground-content.view-mode-code .code-editor {
height: 100%;
}
.playground-content.view-mode-preview .preview-panel {
height: 100%;
}
.editor-content {
overflow: auto;
}
.preview-panel {
min-height: 420px;
}
.tabs {
flex-wrap: wrap;
gap: 2px;
}
.tab {
padding: 6px 12px;
font-size: 12px;
}
.console-content {
max-height: 150px;
}
/* Mobile view controls */
.view-mode-btn {
font-size: 11px;
padding: 6px 8px;
gap: 4px;
}
.view-mode-btn .icon {
font-size: 12px;
}
.fullscreen-btn {
width: 32px;
height: 32px;
}
.fullscreen-btn .icon {
font-size: 16px;
}
}
@media (max-width: 480px) {
.playground-header {
padding: 12px 16px;
gap: 12px;
}
.playground-controls-only {
padding: 8px 16px;
}
.playground-header h3 {
font-size: 18px;
}
.playground-description {
font-size: 13px;
}
.playground-content.view-mode-both {
min-height: 650px;
}
.playground-content.view-mode-both .code-editor {
height: 250px;
}
.playground-content.view-mode-code .code-editor {
height: 100%;
}
.playground-content.view-mode-preview .preview-panel {
height: 100%;
}
.editor-content {
overflow: auto;
}
.preview-panel {
min-height: 400px;
}
.preview-header,
.editor-header {
padding: 8px 12px;
}
.console-toggle {
padding: 8px 12px;
font-size: 12px;
}
.console-content {
max-height: 120px;
}
.console-line {
padding: 6px 12px;
font-size: 11px;
}
/* Extra small mobile adjustments */
.view-mode-controls {
padding: 2px;
gap: 2px;
}
.view-mode-btn {
font-size: 10px;
padding: 4px 6px;
gap: 2px;
}
.view-mode-btn .icon {
font-size: 11px;
}
.fullscreen-btn {
width: 28px;
height: 28px;
}
.fullscreen-btn .icon {
font-size: 14px;
}
.header-controls {
gap: 8px;
}
}
</style>
================================================
FILE: docs/.vitepress/theme/components/config.ts
================================================
const isDev = typeof window !== 'undefined' && window.location.hostname === 'localhost'
/**
* Configuration map for external dependencies
* Ensures all CanvasEngine exports from the compiler are always available
*/
export const dependencyConfig = {
'canvasengine': {
globalName: 'CanvasEngine',
url: isDev ? 'http://localhost:3000/packages/core/dist/index.global.js' : 'https://cdn.jsdelivr.net/npm/canvasengine@latest/dist/index.global.js'
},
'@canvasengine/presets': {
globalName: 'CanvasEnginePresets',
url: isDev ? 'http://localhost:3000/packages/presets/dist/index.global.js' : 'https://cdn.jsdelivr.net/npm/@canvasengine/presets@latest/dist/index.global.js'
},
'pixi.js': {
globalName: 'PIXI',
url: 'https://cdn.jsdelivr.net/npm/pixi.js@latest/dist/pixi.min.js'
}
}
/**
* List of primitive components that should always be available
* Matches the PRIMITIVE_COMPONENTS from the compiler
*/
export const PRIMITIVE_COMPONENTS = [
"Canvas",
"Sprite",
"Text",
"Viewport",
"Graphics",
"Container",
"ImageMap",
"NineSliceSprite",
"Rect",
"Circle",
"Ellipse",
"Triangle",
"TilingSprite",
"svg",
"Video",
"Mesh",
"Svg",
"DOMContainer",
"DOMElement",
"Button",
"Joystick",
"FocusContainer"
]
/**
* Core functions that should always be available
* Matches the required imports from the compiler
*/
export const CORE_FUNCTIONS = [
"h",
"computed",
"cond",
"loop",
"useProps",
"useDefineProps",
"bootstrapCanvas"
]
================================================
FILE: docs/.vitepress/theme/components/example.ts
================================================
interface Example {
title: string;
description: string;
files: Record<string, string>;
}
const examples: Example[] = [{
title: 'Hello World',
description: 'A simple example of how to use CanvasEngine',
files: {
"app.ce": `
<Canvas
backgroundColor="#fff"
width="100%"
height="100%"
antialias="true"
>
<Container
width="100%"
height="100%"
justifyContent="center"
alignItems="center">
<HelloWorld text="CanvasEngine" color="black" />
</Container>
</Canvas>
<script>
import HelloWorld from "./hello.ce";
</script>
`,
"hello.ce": `<Text text="Hello World" size={70} fontFamily="Helvetica" x={50} y={40} />`,
},
},
{
title: 'Reactivity',
description: 'Example of using reactivity in CanvasEngine',
files: {
"app.ce": `
<Canvas>
<Container flexDirection="column" alignItems="center" justifyContent="center" width="100%" height="100%">
<Text text="Click Me" color="white" size={50} click />
<Text text={counter} color="white" size={70} />
<Text text={double} color="white" size={70} />
</Container>
</Canvas>
<script>
import { signal, computed, effect } from 'canvasengine'
const counter = signal(0)
const double = computed(() => counter() * 2)
effect(() => {
console.log(counter())
})
const click = () => {
counter.update(c => c + 1)
}
</script>
`,
},
},
{
title: 'With loop and condition syntax',
description: 'Example of using loop and condition syntax in CanvasEngine',
files: {
"app.ce": `
<Canvas>
<Container>
@if (show) {
<Container>
@for ((color,index) of colors) {
<!-- we use @ in "@index" because index is not a signal -->
<Rect width={100} height={100} color={color} x={@index * 100} />
}
</Container>
}
<Container x={100} y={100} click>
<Rect width={100} height={100} color="red" />
<Text text="Click me" />
</Container>
</Container>
</Canvas>
<script>
import { signal, computed, effect } from 'canvasengine'
const colors = signal(['red', 'green', 'blue'])
const show = signal(true)
const click = () => {
show.update(show => !show)
}
</script>
`,
},
},
{
title: 'Drag and Drop',
description: 'Example of using drag and drop in CanvasEngine',
files: {
"app.ce": `
<Canvas>
<Container>
<Rect width={100} height={100} color="red" drag />
</Container>
</Canvas>
<script>
const drag = {
direction: 'all', // 'all', 'x', or 'y'
start() {
console.log("Drag started");
},
move(event) {
console.log("Dragging", event.global.x, event.global.y);
},
end() {
console.log("Drag ended");
}
};
</script>
`,
},
},
{
title: 'Sprite Animation',
description: 'Example of using sprite animation in CanvasEngine',
files: {
"app.ce": `
<Canvas>
<Container>
<Sprite
sheet={{
definition,
playing: "default"
}}
/>
</Container>
</Canvas>
<script>
const definition = {
id: "explosion",
image: "./exp.png",
width: 1024,
height: 1024,
framesWidth: 4,
framesHeight: 4,
textures: {
default: {
animations: () => [
[
{ time: 0, frameX: 0, frameY: 0 },
{ time: 10, frameX: 1, frameY: 0 },
{ time: 20, frameX: 2, frameY: 0 },
{ time: 30, frameX: 3, frameY: 0 },
{ time: 40, frameX: 0, frameY: 1 },
{ time: 50, frameX: 1, frameY: 1 },
{ time: 60, frameX: 2, frameY: 1 },
{ time: 70, frameX: 3, frameY: 1 },
{ time: 80, frameX: 0, frameY: 2 },
{ time: 90, frameX: 1, frameY: 2 },
{ time: 100, frameX: 2, frameY: 2 },
{ time: 110, frameX: 3, frameY: 2 },
{ time: 120, frameX: 0, frameY: 3 },
{ time: 130, frameX: 1, frameY: 3 },
{ time: 140, frameX: 2, frameY: 3 },
{ time: 150, frameX: 3, frameY: 3 },
]
]
}
}
}
</script>
`,
},
},
// {
// title: 'Joystick',
// description: 'Example of using joystick in CanvasEngine',
// files: {
// "app.ce": `
// <Canvas>
// <Container>
// <Joystick />
// </Container>
// </Canvas>
// <script>
// import { Joystick } from '@canvasengine/presets'
// </script>
// `,
// },
// },
{
title: 'Sprite Animation and controls',
description: 'Example of using sprite animation and controls in CanvasEngine. Use the arrow keys to move the sprite.',
files: {
"app.ce": `
<Canvas>
<Container>
<Sprite
x
y
sheet={{
definition,
playing: animationPlaying,
params: {
direction
}
}}
controls
/>
</Container>
</Canvas>
<script>
import { spritesheet } from "./spritesheet.js"
import { signal } from 'canvasengine'
const x = signal(0)
const y = signal(0)
const direction = signal("down")
const speed = signal(3)
const animationPlaying = signal("stand")
const keyUp = () => {
animationPlaying.set("stand")
}
const controls = signal({
down: {
repeat: true,
bind: "down",
keyDown() {
y.update(y => y + speed());
direction.set("down");
animationPlaying.set("walk")
},
keyUp
},
up: {
repeat: true,
bind: 'up',
keyDown() {
y.update(y => y - speed());
direction.set("up");
animationPlaying.set("walk")
},
keyUp
},
left: {
repeat: true,
bind: "left",
keyDown() {
x.update(x => x - speed());
direction.set("left");
animationPlaying.set("walk")
},
keyUp
},
right: {
repeat: true,
bind: "right",
keyDown() {
x.update(x => x + speed());
direction.set("right");
animationPlaying.set("walk")
},
keyUp
}
});
const definition = {
id: "hero",
image: "./hero.png",
width: 96,
height: 128,
...spritesheet(3, 4)
}
</script>
`,
"spritesheet.js": `
export const spritesheet = (framesWidth, framesHeight, frameStand = 1) => {
if (framesWidth <= frameStand) {
frameStand = framesWidth - 1
}
const frameY = direction => {
const gap = Math.max(4 - framesHeight, 0)
return {
'down': 0,
'left': Math.max(0, 1 - gap),
'right': Math.max(0, 2 - gap),
'up': Math.max(0, 3 - gap)
}[direction]
}
const stand = (direction) => [{ time: 0, frameX: frameStand, frameY: frameY(direction) }]
const walk = direction => {
const array = []
const durationFrame = 10
for (let i = 0; i < framesWidth; i++) {
array.push({ time: i * durationFrame, frameX: i, frameY: frameY(direction) })
}
array.push({ time: array[array.length - 1].time + durationFrame })
return array
}
return {
textures: {
'stand': {
animations: ({direction}) => [stand(direction)]
},
'walk': {
animations: ({direction}) => [walk(direction)]
}
},
framesHeight,
framesWidth
}
}
`
},
},
{
title: 'Tiled Map',
description: 'Example of using a Tiled map in CanvasEngine',
files: {
"app.ce": `
<Canvas>
<TiledMap
map={map}
createLayersPerTilesZ={true}
basePath="/"
objectLayer={(layer) => <Rect width={32} height={32} color="red" />}
/>
</Canvas>
<script>
import { TiledMap } from '@canvasengine/presets'
import { signal } from 'canvasengine'
let map = signal(null)
fetch('/simplemap.tmx')
.then((res) => res.text())
.then((text) => {
map.set(text)
})
</script>
`,
},
},
{
title: 'DOM with form',
description: 'Example of using a Tiled map in CanvasEngine',
files: {
"app.ce": `
<Canvas backgroundColor="white">
<DOMContainer x={100} y={100}>
<form submit={click}>
<input name="username" type="text" value={text} />
<button>Submit</button>
<p>{text}</p>
</form>
</DOMContainer>
</Canvas>
<script>
import { signal } from "canvasengine";
const text = signal("Hello");
const click = (event, formData) => {
console.log(formData)
}
</script>
`,
},
},
{
title: 'Animated signal',
description: 'Example of using an animated signal in CanvasEngine. Click the rect to move it.',
files: {
"app.ce": `
<Canvas backgroundColor="white">
<Container>
<Rect width={100} height={100} color="red" x click />
</Container>
</Canvas>
<script>
import { animatedSignal, Easing } from "canvasengine";
let direction = "left"
const x = animatedSignal(0, {
duration: 1000
})
const click = () => {
if (direction === "left") {
x.update(x => x + 500)
direction = "right"
} else {
x.update(x => x - 500)
direction = "left"
}
}
</script>
`,
},
},
{
title: 'Joystick',
description: 'Example of using a joystick in CanvasEngine. Use the joystick to move the rect.',
files: {
"app.ce": `
<Canvas backgroundColor="white">
<Container>
<Joystick x={100} y={100} onChange={onChange} />
<Container x={300} flexDirection="column" top={50} gap={10}>
<Text text={angleStr} />
<Text text={directionStr} />
<Text text={powerStr} />
</Container>
</Container>
</Canvas>
<script>
import { signal, computed } from 'canvasengine'
const angle = signal(0)
const direction = signal("up")
const power = signal(0)
const angleStr = computed(() => \`Angle: \${angle()}\`)
const directionStr = computed(() => \`Direction: \${direction()}\`)
const powerStr = computed(() => \`Power: \${power()}\`)
const onChange = (event) => {
angle.set(event.angle)
direction.set(event.direction)
power.set(event.power)
}
</script>
`,
},
},
{
title: 'Flash Effect',
description: 'Example of using flash effects in CanvasEngine. Click the rectangles to see different flash types.',
files: {
"app.ce": `
<Canvas backgroundColor="#2c3e50">
<Container flexDirection="column" alignItems="center" justifyContent="center" width="100%" height="100%" gap={30}>
<Text text="Click the rectangles to flash them!" color="white" size={24} />
<Container flexDirection="row" gap={30}>
<Container flexDirection="column" alignItems="center" gap={10}>
<Text text="Alpha Flash" color="#ecf0f1" size={14} />
<Rect
color="#e74c3c"
width={80}
height={80}
borderRadius={8}
flash={alphaFlashConfig}
click={() => alphaFlashTrigger.start()}
/>
</Container>
<Container flexDirection="column" alignItems="center" gap={10}>
<Text text="Tint Flash" color="#ecf0f1" size={14} />
<Rect
color="#3498db"
width={80}
height={80}
borderRadius={8}
flash={tintFlashConfig}
click={() => tintFlashTrigger.start()}
/>
</Container>
<Container flexDirection="column" alignItems="center" gap={10}>
<Text text="Both Flash" color="#ecf0f1" size={14} />
<Rect
color="#2ecc71"
width={80}
height={80}
borderRadius={8}
flash={bothFlashConfig}
click={() => bothFlashTrigger.start()}
/>
</Container>
<Container flexDirection="column" alignItems="center" gap={10}>
<Text text="Multi Cycle" color="#ecf0f1" size={14} />
<Rect
color="#f39c12"
width={80}
height={80}
borderRadius={8}
flash={multiCycleFlashConfig}
click={() => multiCycleFlashTrigger.start()}
/>
</Container>
</Container>
</Container>
</Canvas>
<script>
import { trigger } from 'canvasengine';
// Alpha flash - changes opacity
const alphaFlashTrigger = trigger();
const alphaFlashConfig = {
trigger: alphaFlashTrigger,
type: 'alpha',
alpha: 0.2,
duration: 300
};
// Tint flash - changes color
const tintFlashTrigger = trigger();
const tintFlashConfig = {
trigger: tintFlashTrigger,
type: 'tint',
tint: 0xff0000, // Red flash
duration: 300
};
// Both flash - changes opacity and color
const bothFlashTrigger = trigger();
const bothFlashConfig = {
trigger: bothFlashTrigger,
type: 'both',
alpha: 0.5,
tint: 0x00ff00, // Green flash
duration: 400
};
// Multi-cycle flash - flashes multiple times
const multiCycleFlashTrigger = trigger();
const multiCycleFlashConfig = {
trigger: multiCycleFlashTrigger,
type: 'alpha',
cycles: 3,
duration: 600
};
</script>
`,
},
},
{
title: 'Shake Effect',
description: 'Example of using shake effects in CanvasEngine. Click the rectangles to see different shake configurations.',
files: {
"app.ce": `
<Canvas backgroundColor="#2c3e50">
<Container flexDirection="column" alignItems="center" justifyContent="center" width="100%" height="100%" gap={30}>
<Text text="Click the rectangles to shake them!" color="white" size={24} />
<Container flexDirection="row" gap={30}>
<Container flexDirection="column" alignItems="center" gap={10}>
<Rect
color="#e74c3c"
width={100}
height={100}
borderRadius={8}
shake={basicShakeConfig}
click={() => basicShakeTrigger.start()}
/>
</Container>
<Container flexDirection="column" alignItems="center" gap={10}>
<Rect
color="#3498db"
width={100}
height={100}
borderRadius={8}
shake={horizontalShakeConfig}
click={() => horizontalShakeTrigger.start()}
/>
</Container>
<Container flexDirection="column" alignItems="center" gap={10}>
<Rect
color="#2ecc71"
width={100}
height={100}
borderRadius={8}
shake={verticalShakeConfig}
click={() => verticalShakeTrigger.start()}
/>
</Container>
<Container flexDirection="column" alignItems="center" gap={10}>
<Rect
color="#f39c12"
width={100}
height={100}
borderRadius={8}
shake={intenseShakeConfig}
click={() => intenseShakeTrigger.start()}
/>
</Container>
</Container>
</Container>
</Canvas>
<script>
import { trigger } from 'canvasengine';
// Basic shake - shakes in both directions
const basicShakeTrigger = trigger();
const basicShakeConfig = {
trigger: basicShakeTrigger,
intensity: 15,
duration: 500,
frequency: 10,
direction: 'both'
};
// Horizontal shake - only shakes on X axis
const horizontalShakeTrigger = trigger();
const horizontalShakeConfig = {
trigger: horizontalShakeTrigger,
intensity: 15,
duration: 500,
frequency: 10,
direction: 'x'
};
// Vertical shake - only shakes on Y axis
const verticalShakeTrigger = trigger();
const verticalShakeConfig = {
trigger: verticalShakeTrigger,
intensity: 15,
duration: 500,
frequency: 10,
direction: 'y'
};
// Intense shake - higher intensity and frequency
const intenseShakeTrigger = trigger();
const intenseShakeConfig = {
trigger: intenseShakeTrigger,
intensity: 25,
duration: 400,
frequency: 15,
direction: 'both'
};
</script>
`,
},
},
];
export default examples;
================================================
FILE: docs/.vitepress/theme/index.ts
================================================
import DefaultTheme from "vitepress/theme";
import CustomHome from "./components/CustomHome.vue";
import Playground from "./components/Playground.vue";
import Layout from "./Layout.vue";
import "./style.css";
export default {
...DefaultTheme,
Layout,
enhanceApp(ctx) {
DefaultTheme.enhanceApp(ctx);
ctx.app.component('CustomHome', CustomHome);
ctx.app.component('Playground', Playground);
},
};
================================================
FILE: docs/.vitepress/theme/style.css
================================================
/* Custom theme styles */
:root {
--vp-c-brand: #646cff;
--vp-c-brand-light: #747bff;
--vp-c-brand-lighter: #9499ff;
--vp-c-brand-lightest: #bcc0ff;
--vp-c-brand-dark: #535bf2;
--vp-c-brand-darker: #454ce1;
--vp-c-brand-dimm: rgba(100, 108, 255, 0.08);
}
/* Hide default layout for custom home */
.custom-layout .VPContent {
padding: 0 !important;
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--vp-c-bg-soft);
}
::-webkit-scrollbar-thumb {
background: var(--vp-c-brand);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--vp-c-brand-dark);
}
================================================
FILE: docs/advanced/conditional-rendering.md
================================================
# Conditional Rendering
The `cond()` function allows you to conditionally render elements based on reactive signals or static boolean values. It supports if/else if/else patterns for complex conditional logic.
## Basic Usage
### Simple Condition
```typescript
import { cond, signal, h, Text } from 'canvasengine';
const isVisible = signal(true);
const conditionalText = cond(
isVisible,
() => h(Text, { text: 'I am visible!' })
);
```
### With Else
```typescript
const conditionalText = cond(
isVisible,
() => h(Text, { text: 'Visible' }),
() => h(Text, { text: 'Hidden' }) // else case
);
```
## Advanced Usage
### Multiple Conditions (else if)
```typescript
const status = signal('loading');
const statusDisplay = cond(
() => status() === 'loading',
() => h(Text, { text: 'Loading...', color: 'yellow' }),
[() => status() === 'error', () => h(Text, { text: 'Error!', color: 'red' })], // else if
[() => status() === 'success', () => h(Text, { text: 'Success!', color: 'green' })], // else if
() => h(Text, { text: 'Unknown status', color: 'gray' }) // else
);
```
### Grade System Example
```typescript
const score = signal(85);
const gradeDisplay = cond(
() => score() >= 90,
() => h(Text, { text: 'A+', color: 'gold' }),
[() => score() >= 80, () => h(Text, { text: 'A', color: 'green' })],
[() => score() >= 70, () => h(Text, { text: 'B', color: 'blue' })],
[() => score() >= 60, () => h(Text, { text: 'C', color: 'orange' })],
() => h(Text, { text: 'F', color: 'red' }) // else
);
```
## Condition Types
The `cond()` function accepts three types of conditions:
1. **Signals**: Reactive values that trigger re-evaluation when changed
2. **Static booleans**: Simple true/false values
3. **Functions**: Functions that return boolean values (automatically converted to computed signals)
### Examples
```typescript
// Signal condition
const showElement = signal(true);
cond(showElement, () => h(Text, { text: 'Signal condition' }));
// Static boolean condition
cond(true, () => h(Text, { text: 'Static condition' }));
// Function condition (reactive)
const user = signal({ role: 'admin' });
cond(
() => user().role === 'admin',
() => h(Text, { text: 'Admin panel' })
);
```
## Reactivity
All conditions are reactive. When any signal used in the conditions changes, the `cond()` function automatically re-evaluates and updates the rendered element:
```typescript
const userRole = signal('guest');
const navigation = cond(
() => userRole() === 'admin',
() => h(Container, { children: [
h(Text, { text: 'Admin Dashboard' }),
h(Text, { text: 'User Management' }),
h(Text, { text: 'Settings' })
]}),
[() => userRole() === 'user', () => h(Container, { children: [
h(Text, { text: 'Dashboard' }),
h(Text, { text: 'Profile' })
]})],
() => h(Text, { text: 'Please log in' }) // guest
);
// Changing the role will automatically update the UI
userRole.set('admin'); // Shows admin navigation
userRole.set('user'); // Shows user navigation
userRole.set('guest'); // Shows login message
```
## Performance
- **Lazy evaluation**: Only the matching condition's element is created
- **Automatic cleanup**: Previous elements are properly destroyed when conditions change
- **Optimized updates**: Elements are only updated when the matching condition actually changes
## Best Practices
1. **Order matters**: Conditions are evaluated in order, the first `true` condition wins
2. **Use functions for complex logic**: Convert complex boolean expressions to functions for better reactivity
3. **Provide fallbacks**: Always consider adding an `else` case for unexpected states
4. **Keep conditions simple**: Complex logic should be extracted to computed signals or functions
```typescript
// Good: Simple, readable conditions
const theme = signal('dark');
cond(
() => theme() === 'dark',
() => h(Text, { text: 'Dark mode', color: 'white' }),
() => h(Text, { text: 'Light mode', color: 'black' })
);
// Better: Extract complex logic
const isDarkMode = computed(() => {
const currentTheme = theme();
const userPreference = getUserPreference();
const systemPreference = getSystemPreference();
return currentTheme === 'dark' ||
(currentTheme === 'auto' && (userPreference === 'dark' || systemPreference === 'dark'));
});
cond(
isDarkMode,
() => h(Text, { text: 'Dark mode', color: 'white' }),
() => h(Text, { text: 'Light mode', color: 'black' })
);
```
================================================
FILE: docs/advanced/performance.md
================================================
# Performance Optimization Guide
When building games or visualizations with many elements (1000+), performance becomes critical. This guide covers best practices for optimizing CanvasEngine applications.
## Understanding the Reactive System Cost
CanvasEngine uses a reactive system based on signals. Each signal creates:
- An RxJS subscription
- Change detection overhead
- Component update callbacks
**Rule of thumb**: Minimize the number of signals, especially for properties that change frequently.
### Signal Count Impact
| Elements | Signals/Element | Total Signals | Estimated Cost |
|----------|-----------------|---------------|----------------|
| 100 | 5 | 500 | Low |
| 1000 | 5 | 5,000 | Medium |
| 5000 | 5 | 25,000 | High |
| 5000 | 2 | 10,000 | Medium |
## Optimization Strategies
### 1. Reduce Signal Granularity
Instead of creating signals for every property, only use signals for properties that need reactive updates from external sources.
**Before (expensive)**:
```javascript
// 5 signals per element = 25,000 signals for 5000 elements
function generateItems(count) {
return Array.from({ length: count }, (_, i) => ({
x: signal(i * 10),
y: signal(i * 10),
rotation: signal(0), // Animated - DON'T use signal!
alpha: signal(1), // Animated - DON'T use signal!
scale: signal(1), // Animated - DON'T use signal!
}));
}
```
**After (optimized)**:
```javascript
// 2 signals per element = 10,000 signals for 5000 elements
function generateItems(count) {
return Array.from({ length: count }, (_, i) => ({
x: signal(i * 10), // Position needs signals for loop
y: signal(i * 10),
rotationSpeed: Math.random() * 0.1, // Plain value
alphaBase: 0.5 + Math.random() * 0.5, // Plain value
scaleBase: 0.8 + Math.random() * 0.4, // Plain value
}));
}
```
### 2. Imperative Animation in tick()
For animations, update Pixi objects directly instead of going through signals:
```html
<script>
import { tick } from 'canvasengine';
tick((tickValue, element) => {
// Get the container with sprites
const container = element.componentInstance.children[0];
const sprites = container.children;
const items = itemsSignal();
sprites.forEach((sprite, i) => {
const item = items[i];
// Direct Pixi manipulation - no signal overhead!
sprite.rotation = item.rotationSpeed * tickValue.frame;
sprite.alpha = item.alphaBase + Math.sin(tickValue.frame * 0.05) * 0.2;
});
});
</script>
```
### 3. Incremental Collection Updates
When adding/removing elements, avoid resetting the entire collection:
**Before (expensive)**:
```javascript
// This triggers a full reset - destroys and recreates ALL elements
items.set(generateItems(newCount));
```
**After (optimized)**:
```javascript
effect(() => {
const targetCount = elementCount();
const currentItems = items();
const currentCount = currentItems.length;
if (targetCount > currentCount) {
// Add only new elements
const newItems = [...currentItems];
for (let i = currentCount; i < targetCount; i++) {
newItems.push(createItem(i));
}
items.set(newItems);
} else if (targetCount < currentCount) {
// Remove from end - avoids full recreation
items.set(currentItems.slice(0, targetCount));
}
});
```
### 4. Frame Throttling
For non-critical updates, skip frames:
```javascript
tick((tickValue) => {
// Update every 2nd frame (30 FPS effective)
if (tickValue.frame % 2 !== 0) return;
// Or every 3rd frame (20 FPS effective)
if (tickValue.frame % 3 !== 0) return;
// Animation logic
});
```
### 5. Level of Detail (LOD)
Reduce visual complexity based on element count:
```javascript
tick((tickValue, element) => {
const count = items().length;
const sprites = getSprites(element);
sprites.forEach((sprite, i) => {
// Always animate rotation (cheap)
sprite.rotation += items()[i].rotationSpeed;
// Animate alpha only below 5000 elements
if (count < 5000) {
sprite.alpha = 0.5 + Math.sin(tickValue.frame * 0.02) * 0.5;
}
// Animate scale only below 2000 elements
if (count < 2000) {
const scale = 1 + Math.sin(tickValue.frame * 0.01) * 0.2;
sprite.scale.set(scale);
}
});
});
```
### 6. Viewport Culling
Use the `viewportCull` directive to hide off-screen elements:
```html
<Viewport worldWidth="5000" worldHeight="5000">
<Container viewportCull={true}>
@for (item of items) {
<Sprite x={item().x} y={item().y} />
}
</Container>
</Viewport>
```
## Complete Optimized Example
```html
<Canvas>
<Viewport worldWidth={worldWidth} worldHeight={worldHeight} drag={true}>
<Container>
@for (item of items) {
<Sprite
image="sprite.png"
x={item().x}
y={item().y}
/>
}
</Container>
</Viewport>
</Canvas>
<script>
import { signal, computed, effect, tick } from 'canvasengine';
const elementCount = signal(1000);
let spriteRefs = [];
// Optimized: Only position signals
function createItem(index, cols) {
const col = index % cols;
const row = Math.floor(index / cols);
return {
x: signal(col * 50),
y: signal(row * 50),
rotationSpeed: (Math.random() - 0.5) * 0.1,
alphaBase: 0.5 + Math.random() * 0.5,
};
}
const items = signal([]);
// Incremental updates
effect(() => {
const target = elementCount();
const current = items();
const cols = Math.ceil(Math.sqrt(target));
if (target > current.length) {
const newItems = [...current];
for (let i = current.length; i < target; i++) {
newItems.push(createItem(i, cols));
}
items.set(newItems);
spriteRefs = []; // Reset cache
} else if (target < current.length) {
items.set(current.slice(0, target));
spriteRefs = [];
}
});
// Imperative animation
tick((tickValue, element) => {
const viewport = element.componentInstance?.children?.[0];
const container = viewport?.children?.[0];
if (spriteRefs.length === 0 && container) {
spriteRefs = container.children;
}
const data = items();
const count = data.length;
spriteRefs.forEach((sprite, i) => {
if (!sprite || !data[i]) return;
sprite.rotation += data[i].rotationSpeed;
// LOD: alpha only below 3000
if (count < 3000) {
sprite.alpha = data[i].alphaBase +
Math.sin(tickValue.frame * 0.02) * 0.3;
}
});
});
const worldWidth = computed(() => Math.ceil(Math.sqrt(elementCount())) * 50);
const worldHeight = worldWidth;
</script>
```
## Performance Checklist
- [ ] Minimize signals per element (2-3 max for large collections)
- [ ] Use imperative updates in `tick()` for animations
- [ ] Implement incremental add/remove for collections
- [ ] Add frame throttling for non-critical updates
- [ ] Implement LOD for very large element counts
- [ ] Enable viewport culling for scrollable worlds
- [ ] Profile with browser DevTools to identify bottlenecks
## Benchmark
Run the benchmark in `sample/src/benchmark.ce` to test different optimization modes:
```bash
cd sample
npm run dev
```
Press `1-4` to switch between optimization modes and observe FPS differences.
================================================
FILE: docs/advanced/without-compiler.md
================================================
# Using CanvasEngine Without the Compiler
CanvasEngine provides a powerful template syntax that gets compiled to JavaScript functions. However, you can also use CanvasEngine directly without the compiler by using the core functions: `h`, `loop`, and `cond` from the `canvasengine` package.
This approach gives you more control and can be useful for:
- Dynamic template generation
- Integration with existing build systems
- Learning how the compiler works under the hood
- Performance-critical applications where you want to avoid compilation overhead
## Core Functions
### `h(component, props?, children?)`
Creates a component instance. This is the equivalent of JSX elements.
### `loop(iterable, callback)`
Renders a list of items. This is the equivalent of `@for` loops.
### `cond(condition, callback)`
Conditionally renders content. This is the equivalent of `@if` statements.
## Components
### Basic Component
Instead of writing:
```html
<Canvas />
```
Use:
```javascript
h('Canvas')
```
### Component with Properties
Instead of writing:
```html
<Canvas width="800" height="600" />
```
Use:
```javascript
h('Canvas', { width: '800', height: '600' })
```
### Dynamic Properties
Instead of writing:
```html
<Canvas width={screenWidth} height={screenHeight} />
```
Use:
```javascript
h('Canvas', { width: screenWidth, height: screenHeight })
```
### Computed Properties
For reactive expressions, instead of writing:
```html
<Canvas width={x * 2} />
```
Use:
```javascript
h('Canvas', { width: computed(() => x() * 2) })
```
### Complex Computed Properties
For multiple reactive variables:
```html
<Canvas width={x * 2 * y} />
```
Use:
```javascript
h('Canvas', { width: computed(() => x() * 2 * y()) })
```
### Object Properties
Instead of writing:
```html
<Sprite sheet={{
definition,
playing: "stand",
params: {
direction: "right"
},
onFinish
}} />
```
Use:
```javascript
h('Sprite', {
sheet: {
definition,
playing: "stand",
params: {
direction: "right"
},
onFinish
}
})
```
### Array Properties
Instead of writing:
```html
<Canvas positions={[x, 20]} />
```
Use:
```javascript
h('Canvas', { positions: computed(() => [x(), 20]) })
```
### Event Handlers
Instead of writing:
```html
<Sprite click={handleClick} />
```
Use:
```javascript
h('Sprite', { click: handleClick })
```
### Inline Event Handlers
Instead of writing:
```html
<Sprite click={() => console.log('clicked')} />
```
Use:
```javascript
h('Sprite', { click: () => console.log('clicked') })
```
### Spread Operator
Instead of writing:
```html
<Canvas ...props />
```
Use:
```javascript
h('Canvas', props)
```
### Component as Property
Instead of writing:
```html
<Canvas child={<Sprite />} />
```
Use:
```javascript
h('Canvas', { child: h('Sprite') })
```
### Function Returning Component
Instead of writing:
```html
<Canvas child={() => <Sprite />} />
```
Use:
```javascript
h('Canvas', { child: () => h('Sprite') })
```
## Children
### Single Child
Instead of writing:
```html
<Canvas>
<Sprite />
</Canvas>
```
Use:
```javascript
h('Canvas', null, h('Sprite'))
```
### Multiple Children
Instead of writing:
```html
<Canvas>
<Sprite />
<Text />
</Canvas>
```
Use:
```javascript
h('Canvas', null, [
h('Sprite'),
h('Text')
])
```
## Loops
### Basic Loop
Instead of writing:
```html
@for (sprite of sprites) {
<Sprite />
}
```
Use:
```javascript
loop(sprites, sprite => h('Sprite'))
```
### Loop with Properties
Instead of writing:
```html
@for (sprite of sprites) {
<Sprite x={sprite.x} y={sprite.y} />
}
```
Use:
```javascript
loop(sprites, sprite => h('Sprite', { x: sprite.x, y: sprite.y }))
```
### Loop with Index
Instead of writing:
```html
@for ((sprite, index) of sprites) {
<Sprite key={index} />
}
```
Use:
```javascript
loop(sprites, (sprite, index) => h('Sprite', { key: index }))
```
### Loop with Object Property
Instead of writing:
```html
@for (sprite of sprites.items) {
<Sprite />
}
```
Use:
```javascript
loop(sprites.items, sprite => h('Sprite'))
```
### Loop with Function Call
Instead of writing:
```html
@for (sprite of getSprites()) {
<Sprite />
}
```
Use:
```javascript
loop(getSprites(), sprite => h('Sprite'))
```
### Nested Loops
Instead of writing:
```html
@for (sprite of sprites) {
@for (other of others) {
<Sprite />
}
}
```
Use:
```javascript
loop(sprites, sprite =>
loop(others, other => h('Sprite'))
)
```
### Loop in Component
Instead of writing:
```html
<Canvas>
@for (sprite of sprites) {
<Sprite />
}
</Canvas>
```
Use:
```javascript
h('Canvas', null, loop(sprites, sprite => h('Sprite')))
```
## Conditions
### Basic Condition
Instead of writing:
```html
@if (sprite) {
<Sprite />
}
```
Use:
```javascript
cond(sprite, () => h('Sprite'))
```
### Property-based Condition
Instead of writing:
```html
@if (sprite.visible) {
<Sprite />
}
```
Use:
```javascript
cond(sprite.visible, () => h('Sprite'))
```
### Function-based Condition
Instead of writing:
```html
@if (isVisible()) {
<Sprite />
}
```
Use:
```javascript
cond(isVisible(), () => h('Sprite'))
```
### Complex Conditions
Instead of writing:
```html
@if (!sprite && other) {
<Sprite />
}
```
Use:
```javascript
cond(computed(() => !sprite() && other()), () => h('Sprite'))
```
### Multiple Conditions
Instead of writing:
```html
@if (sprite) {
<Sprite />
}
@if (other) {
<Text />
}
```
Use:
```javascript
[
cond(sprite, () => h('Sprite')),
cond(other, () => h('Text'))
]
```
### Nested Conditions
Instead of writing:
```html
@if (sprite.visible) {
@if (deep) {
<Sprite />
}
}
```
Use:
```javascript
cond(sprite.visible, () =>
cond(deep, () => h('Sprite'))
)
```
### Condition with Multiple Elements
Instead of writing:
```html
@if (sprite.visible) {
<Sprite />
<Text />
}
```
Use:
```javascript
cond(sprite.visible, () => [
h('Sprite'),
h('Text')
])
```
## Combining Loops and Conditions
### Condition in Loop
Instead of writing:
```html
<Canvas>
@for (sprite of sprites) {
@if (sprite.visible) {
<Sprite />
}
}
</Canvas>
```
Use:
```javascript
h('Canvas', null,
loop(sprites, sprite =>
cond(sprite.visible, () => h('Sprite'))
)
)
```
### Multiple Loops
Instead of writing:
```html
<Canvas>
@for (sprite of sprites) {
<Sprite />
}
@for (other of others) {
<Text />
}
</Canvas>
```
Use:
```javascript
h('Canvas', null, [
loop(sprites, sprite => h('Sprite')),
loop(others, other => h('Text'))
])
```
## Complete Example
Here's a complete example showing how to build a complex component without the compiler:
```javascript
import { h, loop, cond, computed } from 'canvasengine';
function GameScene({ sprites, enemies, showUI }) {
return h('Canvas', { width: 800, height: 600 }, [
// Background
h('Sprite', { texture: 'background' }),
// Player sprites
loop(sprites, sprite =>
cond(sprite.visible, () =>
h('Sprite', {
x: sprite.x,
y: sprite.y,
texture: sprite.texture,
click: () => sprite.onClick()
})
)
),
// Enemies
loop(enemies, (enemy, index) =>
h('Sprite', {
key: index,
x: computed(() => enemy.x() + 10),
y: computed(() => enemy.y() + 10),
texture: enemy.texture,
tint: enemy.isHit ? 0xff0000 : 0xffffff
})
),
// UI overlay
cond(showUI, () =>
h('Container', null, [
h('Text', { text: 'Score: 100', x: 10, y: 10 }),
h('Text', { text: 'Lives: 3', x: 10, y: 30 })
])
)
]);
}
```
This approach gives you the full power of CanvasEngine while maintaining complete control over your component structure and logic.
================================================
FILE: docs/api/context.md
================================================
# Context
The context is an object that is automatically provided to all components in the component tree. It contains shared resources and utilities that are available throughout your application.
## Available Properties
The context object contains the following properties:
| Name | Type | Description |
|------|------|-------------|
| `app` | `() => Application \| null` | Function that returns the PixiJS Application instance. Returns `null` until the canvas is rendered. |
| `canvasSize` | `Signal<{ width: number, height: number }>` | Signal containing the current width and height of the canvas. Updates automatically when the canvas is resized. |
| `globalLoader` | `GlobalAssetLoader` | Global asset loader instance that tracks loading progress of all assets across the component tree. See [GlobalAssetLoader documentation](/components/sprite.html#global-asset-loader) for more details. |
| `tick` | `Signal<Tick>` | Signal containing the ticker information. Updates on each frame with timing and frame data. See [Tick interface](#tick-interface) below. |
| `rootElement` | `Element` | The root Canvas element. See [Element documentation](./element.md) for more details. |
## Tick Interface
The `tick` signal contains the following properties:
| Property | Type | Description |
|----------|------|-------------|
| `timestamp` | `number` | Current timestamp in milliseconds |
| `deltaTime` | `number` | Time elapsed since the last frame in milliseconds |
| `frame` | `number` | Current frame number |
| `deltaRatio` | `number` | Ratio of deltaTime to the target frame time (useful for frame-independent animations) |
## Accessing Context
You can access the context through the `element.props.context` property in any component:
```html
<script>
import { mount, effect } from 'canvasengine';
mount((element) => {
const context = element.props.context;
// Access PixiJS Application
const app = context.app();
if (app) {
console.log('PixiJS Application:', app);
}
// Access canvas size signal
const canvasSize = context.canvasSize();
console.log('Canvas size:', canvasSize.width, canvasSize.height);
// Subscribe to canvas size changes using effect
effect(() => {
const size = context.canvasSize();
console.log('Canvas resized:', size.width, size.height);
});
// Access global loader
context.globalLoader.onProgress((progress) => {
console.log('Loading progress:', progress * 100 + '%');
});
context.globalLoader.onComplete(() => {
console.log('All assets loaded!');
});
// Access tick signal
const tick = context.tick();
console.log('Current frame:', tick.frame);
console.log('Delta time:', tick.deltaTime);
// Subscribe to tick updates using effect
effect(() => {
const tick = context.tick();
console.log('Frame:', tick.frame, 'Delta:', tick.deltaTime);
});
// Access root element
console.log('Root element:', context.rootElement);
});
</script>
```
## Examples
### Using Canvas Size for Responsive Layouts
```html
<Container>
<Sprite image="background.png" />
</Container>
<script>
import { mount, effect } from 'canvasengine';
mount((element) => {
const context = element.props.context;
// Center sprite based on canvas size
effect(() => {
const size = context.canvasSize();
const sprite = element.componentInstance;
sprite.x = size.width / 2;
sprite.y = size.height / 2;
sprite.anchor.set(0.5);
});
});
</script>
```
### Tracking Asset Loading Progress
```html
<Container>
<Sprite image="image1.png" />
<Sprite image="image2.png" />
<Sprite image="image3.png" />
</Container>
<script>
import { mount } from 'canvasengine';
mount((element) => {
const context = element.props.context;
// Show loading progress
context.globalLoader.onProgress((progress) => {
const percentage = Math.round(progress * 100);
console.log(`Loading: ${percentage}%`);
});
// Hide loading screen when complete
context.globalLoader.onComplete(() => {
console.log('All assets loaded!');
// Hide your loading screen here
});
});
</script>
```
### Using Tick for Frame-Independent Animations
```html
<Sprite image="sprite.png" />
<script>
import { mount, effect } from 'canvasengine';
mount((element) => {
const context = element.props.context;
const sprite = element.componentInstance;
// Move sprite at constant speed regardless of FPS
effect(() => {
const tick = context.tick();
// deltaRatio ensures consistent movement speed
sprite.x += 100 * tick.deltaRatio; // 100 pixels per second
if (sprite.x > 800) {
sprite.x = 0; // Reset when off screen
}
});
});
</script>
```
### Accessing PixiJS Application
```html
<Container>
<Sprite image="sprite.png" />
</Container>
<script>
import { mount, effect } from 'canvasengine';
mount((element) => {
const context = element.props.context;
// Wait for app to be initialized
effect(() => {
const app = context.app();
if (app) {
console.log('PixiJS Application:', app);
console.log('Renderer:', app.renderer);
console.log('Stage:', app.stage);
// You can now use any PixiJS API
app.ticker.add(() => {
// Custom ticker logic
});
}
});
});
</script>
```
================================================
FILE: docs/api/element.md
================================================
# Element Interface
The Element interface represents a component in the framework. It contains all the necessary properties and methods to manage a component's lifecycle, props, effects, and relationships.
## Properties and Methods
| Name | Type | Description |
|------|------|-------------|
| `tag` | `string` | The HTML tag name or component name |
| `props` | `Props` | The component's properties/attributes |
| `componentInstance` | `T` | The instance of the component. It is the PixiJS instance |
| `propObservables` | `NestedSignalObjects \| undefined` | Observable objects for reactive properties |
| `parent` | `Element \| null` | Reference to the parent element. Null if it's a root element |
| `context` | `{ [key: string]: any }` | Optional context object for sharing data between components |
| `directives` | `{ [key: string]: Directive }` | Map of directives applied to the element |
| `destroy` | `() => void` | Cleanup method called when the element is being destroyed |
| `isFrozen` | `boolean` | Indicates whether the element is currently frozen (read-only)
## Examples
### Get the tag name
```html
<Container x={5} y={5} />
<script>
import { mount } from 'canvasengine';
mount((element) => {
console.log(element.tag)
console.log(element.parent)
console.log(element.props)
});
</script>
```
### Get the children components:
```html
<Container x={5} y={5}>
<Rect x={5} y={5} width={10} height={10} color="red" />
</Container>
<script>
import { mount } from 'canvasengine';
mount((element) => {
console.log(element.props.children)
});
</script>
```
### Get context
```html
<script>
import { mount } from 'canvasengine';
mount((element) => {
console.log(element.props.context)
});
</script>
```
### Get the reactive props
```html
<Container x y />
<script>
import { mount, signal } from 'canvasengine'
const x = signal(5)
const y = signal(5)
mount((element) => {
const x = element.propObservables.x
// Get signal value
console.log(x())
// Subscribe to the RxJS observable
x.observable.subscribe((value) => {
console.log(value)
})
})
</script>
```
### Get directives
You can access directive instances through the `directives` property. Each directive is stored with its attribute name as the key.
```html
<Sprite image="path/to/image.png" controls drag />
<script>
import { mount } from 'canvasengine';
mount((element) => {
// Access the controls directive instance
const controlsInstance = element.directives.controls;
if (controlsInstance) {
// Use methods from the directive instance
// For example, with controls directive:
// controlsInstance.stopInputs()
// controlsInstance.getControls()
}
// Access the drag directive instance
const dragInstance = element.directives.drag;
// Check all available directives
console.log(Object.keys(element.directives));
});
</script>
```
## Freeze System
The `freeze` prop allows you to completely freeze an element, preventing all updates, controls, and events. When an element is frozen:
- **Reactive updates are blocked**: No `onUpdate` calls are made when signal values change
- **Controls are blocked**: All input controls (keyboard, gamepad, joystick) are stopped
- **Events are blocked**: Event handlers (click, mousedown, etc.) are not executed
The `freeze` prop accepts either a `boolean` or `Signal<boolean>`, allowing you to toggle the freeze state dynamically.
### Basic Usage
```html
<!-- Freeze with boolean -->
<Sprite image="path/to/image.png" freeze={true} />
<!-- Freeze with signal (dynamic) -->
<Sprite image="path/to/image.png" freeze={freezeSignal} />
```
### Example: Dynamic Freeze
```html
<Canvas>
<Container>
<Text text="Click to toggle freeze" x={10} y={10} />
<Sprite
image="hero.png"
x={x}
y={y}
freeze={isFrozen}
controls={{
up: {
bind: 'ArrowUp',
keyDown: () => y.set(y() - 10)
}
}}
click={() => {
console.log('Sprite clicked!')
}}
/>
</Container>
</Canvas>
<script>
import { signal } from 'canvasengine';
const isFrozen = signal(false);
const x = signal(100);
const y = signal(100);
// Toggle freeze state
function toggleFreeze() {
isFrozen.set(!isFrozen());
}
</script>
```
### Example: Blocking Updates
When frozen, reactive property updates are completely blocked:
```html
<Sprite
image="hero.png"
x={xSignal}
y={ySignal}
freeze={freezeSignal}
/>
<script>
import { signal } from 'canvasengine';
const freezeSignal = signal(false);
const xSignal = signal(0);
const ySignal = signal(0);
// When freezeSignal is true, these updates won't trigger onUpdate
xSignal.set(100);
ySignal.set(200);
</script>
```
### Example: Blocking Controls
When frozen, all controls are automatically stopped:
```html
<Sprite
image="hero.png"
freeze={freezeSignal}
controls={{
move: {
bind: 'ArrowUp',
keyDown: () => {
console.log('Moving...'); // Won't execute when frozen
}
}
}}
/>
```
### Example: Blocking Events
When frozen, event handlers are not executed:
```html
<Sprite
image="hero.png"
freeze={freezeSignal}
click={() => {
console.log('Clicked!'); // Won't execute when frozen
}}
mousedown={() => {
console.log('Mouse down!'); // Won't execute when frozen
}}
/>
```
### Checking Freeze State
You can check if an element is frozen using the `isElementFrozen` helper function:
```html
<script>
import { isElementFrozen } from 'canvasengine';
mount((element) => {
if (isElementFrozen(element)) {
console.log('Element is frozen');
}
});
</script>
```
Or access the `isFrozen` property directly:
```html
<script>
mount((element) => {
console.log('Is frozen:', element.isFrozen);
});
</script>
```
================================================
FILE: docs/api/testing.md
================================================
# Testing Package
The `@canvasengine/testing` package provides comprehensive testing utilities and mocks for CanvasEngine. It allows you to test your CanvasEngine components without requiring a full PixiJS environment or jsdom setup.
## Installation
```bash
npm install @canvasengine/testing --save-dev
```
or with pnpm:
```bash
pnpm add @canvasengine/testing --save-dev
```
## Overview
The testing package includes:
- **PixiJS Mocks**: Complete mocks for all PixiJS classes used in CanvasEngine (Container, Sprite, Text, Application, etc.)
- **Element Helpers**: Utilities to create mock elements with spyable `componentInstance`
- **Spy Utilities**: Helpers to easily spy on element properties and methods
## PixiJS Mocks
The package provides mocks for all major PixiJS classes:
- `MockContainer` - Mock for PixiJS Container
- `MockSprite` - Mock for PixiJS Sprite
- `MockText` - Mock for PixiJS Text
- `MockGraphics` - Mock for PixiJS Graphics
- `MockMesh` - Mock for PixiJS Mesh
- `MockTilingSprite` - Mock for PixiJS TilingSprite
- `MockNineSlicePlane` - Mock for PixiJS NineSlicePlane
- `MockDOMElement` - Mock for PixiJS DOMElement
- `MockDOMContainer` - Mock for PixiJS DOMContainer
- `MockApplication` - Mock for PixiJS Application
- `MockTexture` - Mock for PixiJS Texture
- `MockRectangle` - Mock for PixiJS Rectangle
- `MockObservablePoint` - Mock for PixiJS ObservablePoint
- `MockVideoResource` - Mock for PixiJS VideoResource
All mocks include:
- Essential properties (x, y, width, height, alpha, visible, rotation, etc.)
- Methods mocked with `vi.fn()` for easy spying
- Event support (on, off, emit)
- Child management for Container-based mocks
### Using PixiJS Mocks
```typescript
import { MockContainer, MockSprite } from '@canvasengine/testing';
// Create a mock container
const container = new MockContainer();
container.x = 100;
container.y = 50;
container.width = 200;
container.height = 150;
// Create a mock sprite
const sprite = new MockSprite();
sprite.x = 10;
sprite.y = 10;
// Add sprite to container
container.addChild(sprite);
// Spy on methods
import { vi } from 'vitest';
const addChildSpy = vi.spyOn(container, 'addChild');
container.addChild(new MockContainer());
expect(addChildSpy).toHaveBeenCalled();
```
## Creating Mock Elements
The `createMockElement` helper creates a complete mock Element with all required properties.
### Basic Usage
```typescript
import { createMockElement } from '@canvasengine/testing';
// Create a basic element with default mock
const element = createMockElement('Container', { x: 100, y: 50 });
expect(element.tag).toBe('Container');
expect(element.props.x).toBe(100);
expect(element.componentInstance.x).toBe(100);
```
### With Custom ComponentInstance
```typescript
import { createMockElement, MockSprite } from '@canvasengine/testing';
// Create an element with custom componentInstance
const customInstance = new MockSprite();
const spriteElement = createMockElement('Sprite', {
image: 'hero.png',
x: 200,
y: 100
}, customInstance);
expect(spriteElement.componentInstance).toBe(customInstance);
expect(spriteElement.componentInstance.x).toBe(200);
```
### Accessing componentInstance
The `componentInstance` property contains the mock PixiJS instance, which you can spy upon:
```typescript
import { createMockElement } from '@canvasengine/testing';
import { vi } from 'vitest';
const element = createMockElement('Container', { x: 100 });
// Access the componentInstance
const instance = element.componentInstance;
expect(instance.x).toBe(100);
// Spy on properties or methods
const xSpy = vi.spyOn(instance, 'x', 'get');
const addChildSpy = vi.spyOn(instance, 'addChild');
```
## Spying on Elements
The `spyOnElement` helper makes it easy to spy on element properties and methods.
### Spying on a Single Property
```typescript
import { createMockElement, spyOnElement } from '@canvasengine/testing';
const element = createMockElement('Container', { x: 100 });
// Spy on a property
const spy = spyOnElement(element, 'x');
element.componentInstance.x = 200;
// Note: For property setters, you may need to use vi.spyOn directly
// depending on your testing needs
```
### Spying on Methods
```typescript
import { createMockElement, spyOnElement } from '@canvasengine/testing';
const element = createMockElement('Container');
// Spy on a method
const addChildSpy = spyOnElement(element, 'addChild');
element.componentInstance.addChild(new MockContainer());
expect(addChildSpy).toHaveBeenCalled();
```
### Spying on Multiple Properties
```typescript
import { createMockElement, spyOnElementMultiple } from '@canvasengine/testing';
const element = createMockElement('Container');
// Spy on multiple methods at once
const spies = spyOnElementMultiple(element, ['addChild', 'removeChild', 'destroy']);
element.componentInstance.addChild(new MockContainer());
element.componentInstance.removeChild(element.componentInstance.children[0]);
element.componentInstance.destroy();
expect(spies.addChild).toHaveBeenCalled();
expect(spies.removeChild).toHaveBeenCalled();
expect(spies.destroy).toHaveBeenCalled();
```
## Creating Mock Component Instances
The `createMockComponentInstance` helper creates appropriate mock instances based on component type:
```typescript
import { createMockComponentInstance } from '@canvasengine/testing';
const containerInstance = createMockComponentInstance('Container');
const spriteInstance = createMockComponentInstance('Sprite');
const textInstance = createMockComponentInstance('Text');
```
## Common Use Cases
### Testing Component Logic
```typescript
import { describe, test, expect, vi } from 'vitest';
import { createMockElement, spyOnElement } from '@canvasengine/testing';
describe('MyComponent', () => {
test('should update position', () => {
const element = createMockElement('Container', { x: 0, y: 0 });
// Your component logic that updates position
element.componentInstance.x = 100;
element.componentInstance.y = 50;
expect(element.componentInstance.x).toBe(100);
expect(element.componentInstance.y).toBe(50);
});
test('should add children', () => {
const parent = createMockElement('Container');
const child = createMockElement('Sprite');
const addChildSpy = spyOnElement(parent, 'addChild');
parent.componentInstance.addChild(child.componentInstance);
expect(addChildSpy).toHaveBeenCalledWith(child.componentInstance);
expect(parent.componentInstance.children).toContain(child.componentInstance);
});
});
```
### Testing Directives
```typescript
import { describe, test, expect } from 'vitest';
import { createMockElement } from '@canvasengine/testing';
describe('MyDirective', () => {
test('should apply directive to element', () => {
const element = createMockElement('Sprite', { x: 100, y: 100 });
// Apply your directive
// directive.onInit(element);
// Test directive behavior
// expect(element.directives.myDirective).toBeDefined();
});
});
```
### Testing Event Handlers
```typescript
import { describe, test, expect } from 'vitest';
import { createMockElement } from '@canvasengine/testing';
describe('Event Handling', () => {
test('should handle click events', () => {
const element = createMockElement('Sprite');
const clickHandler = vi.fn();
element.componentInstance.on('click', clickHandler);
element.componentInstance.emit('click', { x: 100, y: 100 });
expect(clickHandler).toHaveBeenCalledWith({ x: 100, y: 100 });
});
});
```
## Migration from Manual Mocks
If you're currently creating manual mocks in your tests, you can migrate to use `@canvasengine/testing`:
### Before
```typescript
// Manual mock
class MockContainer {
x = 0;
y = 0;
children = [];
addChild() {}
}
const element = {
tag: 'Container',
props: { x: 100 },
componentInstance: new MockContainer(),
// ... other properties
};
```
### After
```typescript
import { createMockElement } from '@canvasengine/testing';
const element = createMockElement('Container', { x: 100 });
// All properties are automatically set up
```
## Best Practices
1. **Use createMockElement for Elements**: Always use `createMockElement` when you need an Element in tests. It ensures all required properties are present.
2. **Spy on componentInstance**: Use `spyOnElement` to spy on the PixiJS instance methods and properties.
3. **Use Appropriate Mocks**: Use the specific mock class (MockSprite, MockText, etc.) when you need a particular PixiJS class.
4. **Test Behavior, Not Implementation**: Focus on testing the behavior of your components rather than internal PixiJS implementation details.
5. **Clean Up**: The mocks are lightweight and don't require cleanup, but if you're using spies, remember to clear them between tests if needed.
## TypeScript Support
The package is fully typed and provides TypeScript definitions for all mocks and helpers. You'll get full autocomplete and type checking in your tests.
```typescript
import { createMockElement, Element, ComponentInstance } from '@canvasengine/testing';
const element: Element<ComponentInstance> = createMockElement('Container');
```
## Using Mocks with bootstrapCanvas
The `bootstrapCanvas` function supports component registration configuration, allowing you to use mocks for testing.
### mockComponents
The `mockComponents` object provides a mapping of all CanvasEngine component names to their corresponding mock classes. You can import it from `@canvasengine/testing`:
```typescript
import { mockComponents } from '@canvasengine/testing';
```
### Registering All Mocks
To register all components as mocks:
```typescript
import { bootstrapCanvas } from 'canvasengine';
import { mockComponents } from '@canvasengine/testing';
await bootstrapCanvas(rootElement, MyComponent, {
components: mockComponents,
autoRegister: false // Only register the specified (mocked) components
});
```
### Registering Specific Mocks
To register only specific components as mocks while keeping others as real components:
```typescript
import { bootstrapCanvas } from 'canvasengine';
import { MockSprite, MockContainer, mockComponents } from '@canvasengine/testing';
await bootstrapCanvas(rootElement, MyComponent, {
components: {
Sprite: MockSprite,
Container: MockContainer,
// Other components will be registered normally
},
autoRegister: true // Register all default components, then override with mocks
});
```
### Overriding Specific Components
To register all default components but override specific ones with mocks:
```typescript
import { bootstrapCanvas } from 'canvasengine';
import { MockSprite } from '@canvasengine/testing';
await bootstrapCanvas(rootElement, MyComponent, {
components: {
Sprite: MockSprite // Replace Sprite with mock, keep others as real
},
autoRegister: true // Register all default components first
});
```
### Available Mock Components
The `mockComponents` object includes mappings for:
- `Canvas` → `MockContainer`
- `Container` → `MockContainer`
- `Sprite` → `MockSprite`
- `Text` → `MockText`
- `Graphics`, `Rect`, `Circle`, `Ellipse`, `Triangle`, `Svg` → `MockGraphics`
- `Mesh` → `MockMesh`
- `TilingSprite` → `MockTilingSprite`
- `NineSliceSprite` → `MockNineSlicePlane`
- `DOMContainer` → `MockDOMContainer`
- `DOMElement` → `MockDOMElement`
- `Viewport` → `MockContainer`
- `ParticlesEmitter` → `MockContainer`
You can access individual mocks directly:
```typescript
import {
MockContainer,
MockSprite,
MockText,
MockGraphics,
MockMesh,
MockTilingSprite,
MockNineSlicePlane,
MockDOMElement,
MockDOMContainer,
mockComponents
} from '@canvasengine/testing';
```
## API Reference
### mockComponents
```typescript
const mockComponents: {
readonly Canvas: typeof MockContainer;
readonly Container: typeof MockContainer;
readonly Sprite: typeof MockSprite;
readonly Text: typeof MockText;
readonly Graphics: typeof MockGraphics;
readonly Rect: typeof MockGraphics;
readonly Circle: typeof MockGraphics;
readonly Ellipse: typeof MockGraphics;
readonly Triangle: typeof MockGraphics;
readonly Svg: typeof MockGraphics;
readonly Mesh: typeof MockMesh;
readonly TilingSprite: typeof MockTilingSprite;
readonly NineSliceSprite: typeof MockNineSlicePlane;
readonly DOMContainer: typeof MockDOMContainer;
readonly DOMElement: typeof MockDOMElement;
readonly Viewport: typeof MockContainer;
readonly ParticlesEmitter: typeof MockContainer;
}
```
Mapping of CanvasEngine component names to their corresponding mock classes. Can be used directly with `bootstrapCanvas()`.
### createMockElement
```typescript
function createMockElement<T extends ComponentInstance = ComponentInstance>(
tag: string,
props?: Props,
componentInstance?: T
): Element<T>
```
Creates a mock Element with all required properties.
### createMockComponentInstance
```typescript
function createMockComponentInstance(componentType: string): ComponentInstance
```
Creates a mock ComponentInstance based on the component type.
### spyOnElement
```typescript
function spyOnElement<T extends ComponentInstance>(
element: Element<T>,
property: keyof T | string
): ReturnType<typeof vi.spyOn>
```
Creates a spy on a property or method of an element's componentInstance.
### spyOnElementMultiple
```typescript
function spyOnElementMultiple<T extends ComponentInstance>(
element: Element<T>,
properties: (keyof T | string)[]
): Record<string, ReturnType<typeof vi.spyOn>>
```
Creates spies on multiple properties or methods at once.
================================================
FILE: docs/components/_display-object.md
================================================
## Common Properties
| Property | Type | Description |
|----------------|---------------------|-----------------------------------------------------------------------------|
| x | number | X-coordinate position of the display object. |
| y | number | Y-coordinate position of the display object. |
| width | number | Width of the display object. |
| height | number | Height of the display object. |
| scale | object | Scale of the display object. |
| anchor | object | Anchor point of the display object. |
| skew | object | Skew of the display object. |
| tint | number | Tint color of the display object. |
| rotation | number | Rotation of the display object in radians. |
| angle | number | Rotation of the display object in degrees. |
| zIndex | number | Z-index of the display object. |
| roundPixels | boolean | Whether to round pixel values. |
| cursor | string | Cursor style when hovering over the display object. |
| visible | boolean | Visibility of the display object. |
| alpha | number | Alpha transparency of the display object. |
| pivot | object | Pivot point of the display object. |
| filters | array | Filters applied to the display object. |
| maskOf | Element | Element that this display object masks. |
| blendMode | string | Blend mode for rendering. |
| filterArea | object | Filter area for rendering. |
## Layout Properties
Pour obtenir la documentation complète et détaillée sur toutes les propriétés de mise en page, consultez la documentation officielle de [PixiJS Layout](https://layout.pixijs.io/).
### Sizing and Dimensions
| Property | Type | Description |
|----------------|---------------------|-----------------------------------------------------------------------------|
| width | number/string | Width of the display object. Accepts pixels or percentage (e.g. '50%'). |
| height | number/string | Height of the display object. Accepts pixels or percentage (e.g. '50%'). |
| minWidth | number/string | Minimum width the object can shrink to. |
| minHeight | number/string | Minimum height the object can shrink to. |
| maxWidth | number/string | Maximum width the object can expand to. |
| maxHeight | number/string | Maximum height the object can expand to. |
| aspectRatio | number | Ratio between width and height (e.g. 1.5 for 3:2 ratio). |
### Flex Layout
| Property | Type | Description |
|----------------|---------------------|-----------------------------------------------------------------------------|
| flexDirection | string | Direction of flex items. Values: 'row', 'column', 'row-reverse', 'column-reverse'. |
| flexWrap | string | Whether items wrap. Values: 'wrap', 'nowrap', 'wrap-reverse'. |
| justifyContent | string | Alignment along main axis. Values: 'flex-start', 'flex-end', 'center', 'space-between', 'space-around'. |
| alignItems | string | Alignment along cross axis. Values: 'flex-start', 'flex-end', 'center', 'stretch', 'baseline'. |
| alignContent | string | Alignment of lines with multiple items. Values: 'flex-start', 'flex-end', 'center', 'stretch', 'space-between', 'space-around'. |
| alignSelf | string | Override of parent's alignItems for specific item. |
| flexGrow | number | Grow factor of item relative to other items. |
| flexShrink | number | Shrink factor of item relative to other items. |
| flexBasis | number/string | Initial size of item before flex growing/shrinking. |
| gap | number/object | Gap between items. |
| rowGap | number | Gap between rows. |
| columnGap | number | Gap between columns. |
### Positioning
| Property | Type | Description |
|----------------|---------------------|-----------------------------------------------------------------------------|
| positionType | string | Type of positioning. Values: 'relative', 'absolute', 'static'. |
| top | number/string | Distance from the top edge. |
| right | number/string | Distance from the right edge. |
| bottom | number/string | Distance from the bottom edge. |
| left | number/string | Distance from the left edge. |
### Spacing, Margins and Borders
| Property | Type | Description |
|----------------|---------------------|-----------------------------------------------------------------------------|
| margin | number/array | Space outside border box. Can be single value or array for different sides. |
| padding | number/array | Space inside border box. Can be single value or array for different sides. |
| border | number/array | Border width. Can be single value or array for different sides. |
### Object Fitting and Alignment
| Property | Type | Description |
|----------------|---------------------|-----------------------------------------------------------------------------|
| objectFit | string | How object is resized to fit layout box. Values: 'contain', 'cover', 'fill', 'none', 'scale-down'. |
| objectPosition | string | Anchor point of object inside layout box. E.g. 'center', 'top left'. |
| transformOrigin| string | Pivot point for rotation and scaling of layout box. |
## Shadow
| Property | Type | Description |
|----------------|---------------------|-----------------------------------------------------------------------------|
| blur | number | Blur strength. |
| color | number | Color of the shadow. |
| offset | object | Offset of the shadow. |
| quality | number | Quality of the shadow. |
# Hook before destroy
```html
<script>
import {
signal,
animatedSignal,
effect,
animatedSequence,
} from "canvasengine";
import MyViewport from "./viewport.ce";
let bool = signal(true)
const opacity = animatedSignal(1, { duration: 500 });
const click = async () => {
bool.set(!bool())
}
const beforeDestroy = async () => {
await animatedSequence([
() => opacity.set(0),
])
console.log("before destroy")
}
</script>
<Canvas antialias={true}>
<Container onBeforeDestroy={beforeDestroy}>
@if (bool) {
<Rect width={300} height={300} color="red" alpha={opacity} click />
}
</Container>
</Canvas>
```
================================================
FILE: docs/components/button.md
================================================
# Button
The Button component provides an interactive button with visual feedback for different states (normal, hover, pressed, disabled). It supports both sprite-based and graphics-based rendering approaches.
## Basic Usage
```html
<Button
text="Click Me"
width={150}
height={50}
click={() => console.log("Button clicked!")}
/>
```
## Properties
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `text` | `string` | `""` | Button text content |
| `disabled` | `boolean` | `false` | Whether the button is disabled |
| `width` | `number` | `120` | Button width in pixels |
| `height` | `number` | `40` | Button height in pixels |
| `x` | `number` | `0` | Button X position |
| `y` | `number` | `0` | Button Y position |
| `alpha` | `number` | `1` | Button opacity (0-1) |
| `visible` | `boolean` | `true` | Button visibility |
| `style` | `ButtonStyle` | `{}` | Visual styling configuration |
| `cursor` | `string` | `"pointer"` | Button cursor |
| `controls` | `ControlsDirective \| JoystickControls` | - | Controls instance to automatically apply button events to. Can be accessed via `element.directives.controls` |
| `controlName` | `string` | - | Name of the control to trigger with `applyControl` when button is clicked |
| `shape` | `'rect' \| 'circle' \| 'ellipse'` | `'rect'` | Shape of the button background |
| `background` | `Element \| ComponentFunction` | - | Custom background component or element (replaces default background if provided) |
| `children` | `Element[]` | - | Custom children components for button content (takes priority over `text` if provided) |
## Events
| Event | Type | Description |
|-------|------|-------------|
| `click` | `(event: FederatedPointerEvent) => void` | Fired when button is clicked |
| `hoverEnter` | `(event: FederatedPointerEvent) => void` | Fired when mouse enters button |
| `hoverLeave` | `(event: FederatedPointerEvent) => void` | Fired when mouse leaves button |
| `pressDown` | `(event: FederatedPointerEvent) => void` | Fired when button is pressed down |
| `pressUp` | `(event: FederatedPointerEvent) => void` | Fired when button is released |
## Button States
The button automatically manages four visual states:
- **Normal**: Default appearance
- **Hover**: When mouse is over the button
- **Pressed**: When button is being clicked
- **Disabled**: When button is not interactive
## Styling
### Background Colors
```html
<Button
text="Styled Button"
style={{
backgroundColor: {
normal: "#28a745",
hover: "#218838",
pressed: "#1e7e34",
disabled: "#6c757d"
}
}}
/>
```
### Text Styling
```html
<Button
text="Custom Text"
style={{
text: {
fontSize: 18,
fontFamily: "Arial",
color: "#ffffff"
}
}}
/>
```
### Border Styling
```html
<Button
text="Bordered Button"
style={{
border: {
radius: 8,
width: 2,
color: "#ffffff"
}
}}
/>
```
### Sprite-based Button
For more advanced styling, you can use different sprite textures for each state:
```html
<Button
text="Play Game"
style={{
textures: {
normal: "/assets/button-normal.png",
hover: "/assets/button-hover.png",
pressed: "/assets/button-pressed.png",
disabled: "/assets/button-disabled.png"
}
}}
/>
```
## Controls Integration
The Button can be automatically integrated with the controls system, similar to joystick controls:
```html
<Canvas>
<Button
text="Jump"
controls={element.directives.controls}
controlName="jump"
width={120}
height={40}
/>
</Canvas>
<script>
import { signal, mount } from "canvasengine";
import { Button } from "@canvasengine/core";
const controls = signal({
jump: {
bind: "space",
keyDown() {
console.log("Jump!");
// Jump logic here
},
},
});
mount((element) => {
// The button will automatically trigger the "jump" control when clicked
});
</script>
```
## Button Shapes
You can customize the button shape using the `shape` property:
### Rectangular Button (default)
```html
<Button
text="Rectangular"
shape="rect"
width={150}
height={50}
/>
```
### Circular Button
```html
<Button
text="Circular"
shape="circle"
width={100}
height={100}
/>
```
### Elliptical Button
```html
<Button
text="Elliptical"
shape="ellipse"
width={150}
height={80}
/>
```
## Custom Content
You can provide custom content using children components instead of text:
### Button with Icon
```html
<Button shape="circle" width={80} height={80}>
<Sprite image="icon.png" width={50} height={50} />
</Button>
```
### Button with Multiple Elements
```html
<Button shape="rect" width={200} height={60}>
<Sprite image="icon.png" x={20} y={30} width={30} height={30} />
<Text text="Custom" x={60} y={30} />
</Button>
```
When `children` are provided, they take priority over the `text` property.
## Custom Background
You can provide a custom background component:
```html
<Button
text="Custom Background"
background={<Sprite image="custom-bg.png" />}
width={150}
height={50}
/>
```
## Complete Example
```html
<Canvas width={800} height={600}>
<Button
text="Start Game"
x={350}
y={250}
width={200}
height={80}
style={{
backgroundColor: {
normal: "#007bff",
hover: "#0056b3",
pressed: "#004085",
disabled: "#6c757d"
},
border: {
radius: 10
},
text: {
fontSize: 20,
fontFamily: "Arial Bold",
color: "#ffffff"
}
}}
click={() => {
console.log("Starting game...");
// Game start logic here
}}
/>
<Button
text="Settings"
x={350}
y={350}
width={200}
height={60}
style={{
backgroundColor: {
normal: "#6c757d",
hover: "#5a6268",
pressed: "#545b62"
},
text: {
fontSize: 16,
color: "#ffffff"
}
}}
click={() => {
console.log("Opening settings...");
}}
/>
<!-- Circular button with controls -->
<Button
text="Jump"
shape="circle"
x={100}
y={100}
width={80}
height={80}
controls={element.directives.controls}
controlName="jump"
style={{
backgroundColor: {
normal: "#28a745",
hover: "#218838",
pressed: "#1e7e34"
}
}}
/>
<!-- Button with custom content -->
<Button
shape="circle"
x={200}
y={100}
width={80}
height={80}
style={{
backgroundColor: {
normal: "#ffc107",
hover: "#e0a800",
pressed: "#d39e00"
}
}}
>
<Sprite image="power-icon.png" width={50} height={50} />
</Button>
</Canvas>
<script>
import { signal, mount } from "canvasengine";
import { Button } from "@canvasengine/core";
const controls = signal({
jump: {
bind: "space",
keyDown() {
console.log("Jump action!");
},
},
});
mount((element) => {
// Controls are automatically applied when buttons are clicked
});
</script>
```
<script>
// You can also create buttons programmatically
const createButton = (text, onClick) => {
return Button({
text,
click,
style: {
backgroundColor: {
normal: "#28a745",
hover: "#218838",
pressed: "#1e7e34"
}
}
});
};
</script>
```
## TypeScript Types
```typescript
interface ButtonStyle {
backgroundColor?: {
normal?: string;
hover?: string;
pressed?: string;
disabled?: string;
};
border?: {
color?: string;
width?: number;
radius?: number;
};
text?: {
color?: string;
fontSize?: number;
fontFamily?: string;
};
textures?: {
normal?: string;
hover?: string;
pressed?: string;
disabled?: string;
};
}
interface ButtonProps {
text?: string;
disabled?: boolean;
click?: (event: FederatedPointerEvent) => void;
hoverEnter?: (event: FederatedPointerEvent) => void;
hoverLeave?: (event: FederatedPointerEvent) => void;
pressDown?: (event: FederatedPointerEvent) => void;
pressUp?: (event: FederatedPointerEvent) => void;
style?: ButtonStyle;
width?: number;
height?: number;
x?: number;
y?: number;
alpha?: number;
visible?: boolean;
cursor?: string;
controls?: ControlsDirective | JoystickControls;
controlName?: string;
shape?: 'rect' | 'circle' | 'ellipse';
background?: Element | ComponentFunction;
children?: Element[];
}
```
================================================
FILE: docs/components/canvas.md
================================================
# Use Canvas component
It's the starting point for all the other components.
Common example:
```html
<Canvas antialias="true">
</Canvas>
```
### options
You can use all properties from [PixiJS Canvas Renderer](https://pixijs.download/release/docs/rendering.html#autoDetectRenderer)
================================================
FILE: docs/components/container.md
================================================
# Use Container component
Common example:
```html
<Container />
```
Example with x and y:
```html
<script>
import { signal } from 'canvasengine'
const x = signal(10)
const y = signal(10)
const click = () => {
x.update(x => x + 10)
y.update(y => y + 10)
}
</script>
<Container x y @click />
```
<!-- @include: ./_display-object.md -->
================================================
FILE: docs/components/dom-container.md
================================================
# DOMContainer Component
The `DOMContainer` component provides a bridge between the canvas rendering system and traditional DOM manipulation. It allows you to render standard DOM elements within your canvas application while maintaining proper transform hierarchy and visibility.
This component is especially useful for rendering form elements like `<input>`, `<textarea>`, or `<select>` that handle user input, as this is often simpler and more flexible than implementing text input directly in PixiJS.
## Basic Usage
```html
<DOMContainer x={100} y={50}>
<input type="text" placeholder="Enter text..." />
</DOMContainer>
```
## Sprite in DOMContainer
You can use the standard `Sprite` component inside a `DOMContainer`. It will automatically render as a `DOMSprite` when placed anywhere under a `DOMContainer` (even if the parent is distant).
Use the same props as `Sprite`:
- See `docs/components/sprite.md`
## Form Elements with Reactive Signals
For form elements (`input`, `textarea`, `select`), the component supports reactive two-way data binding using signals:
```html
<script>
import { signal } from 'canvasengine'
const inputValue = signal('initial value')
</script>
<DOMContainer>
<input type="text" value={inputValue} />
</DOMContainer>
```
The component automatically:
- Sets the initial value from the signal
- Listens for `input` events and updates the signal with the new value
- Updates the DOM element when the signal value changes programmatically
## Form Submission with Automatic Data Collection
When a `form` element has a `submit` event handler, the component automatically:
- Prevents the default form submission behavior (stops propagation)
- Collects all form data from input elements within the form
- Passes both the event and the collected form data as parameters to the submit handler
- Handles multiple values for the same field name (e.g., checkboxes with same name)
```html
<script>
const handleSubmit = (event, formData) => {
console.log('Form submitted with data:', formData)
// Example formData: { username: 'john', password: 'secret', remember: 'on' }
}
</script>
<DOMContainer>
<form submit={handleSubmit}>
<input name="username" type="text" placeholder="Username" />
<input name="password" type="password" placeholder="Password" />
<input name="remember" type="checkbox" value="on" />
<button type="submit">Login</button>
</form>
</DOMContainer>
```
## Styling DOM Elements
### CSS Classes
You can apply CSS classes using different formats:
```html
<!-- String format: space-separated classes -->
<DOMContainer>
<div class="container primary-theme">Content</div>
</DOMContainer>
<!-- You can also declare multiple class attributes; they are merged -->
<DOMContainer>
<div class="container" class={['primary-theme', { active: isActive } ]}>Content</div>
</DOMContainer>
<!-- Array format: array of class names -->
<DOMContainer>
<div class={['container', 'primary-theme', 'active']}>Content</div>
</DOMContainer>
<!-- Mixed array format: combine static and reactive classes -->
<DOMContainer>
<div class={['container', { active: isActive } ]}>Content</div>
</DOMContainer>
<!-- Object format: conditional classes -->
<script>
const isActive = signal(false)
const theme = signal('light')
</script>
<DOMContainer>
<div class={{
'container': true,
'active': isActive(),
'theme-light': theme() === 'light',
'theme-dark': theme() === 'dark'
}}>Content</div>
</DOMContainer>
```
### Inline Styles
You can apply styles using different formats:
```html
<!-- String format: CSS style string -->
<DOMContainer>
<div style="background-color: red; padding: 10px;">Content</div>
</DOMContainer>
<!-- Object format: style properties -->
<DOMContainer>
<div style={{
backgroundColor: 'blue',
padding: '20px',
fontSize: '16px'
}}>Content</div>
</DOMContainer>
```
## Event Handling
You can attach event handlers to DOM elements:
```html
<script>
const handleClick = (event) => {
console.log('Button clicked!', event)
}
const handleFocus = (event) => {
console.log('Input focused!', event)
}
</script>
<DOMContainer>
<button click={handleClick}>Click me</button>
<input type="text" focus={handleFocus} placeholder="Focus me" />
</DOMContainer>
```
## Nested DOM Elements
You can create complex DOM structures with nested elements:
```html
<DOMContainer>
<div class="outer-container">
<div class="inner-container">
<h2>Nested Content</h2>
<p>This is nested inside multiple divs</p>
</div>
</div>
</DOMContainer>
```
## Complete Form Example
Here's a comprehensive example showing a login form with reactive inputs:
```html
<script>
import { signal } from 'canvasengine'
const username = signal('')
const password = signal('')
const rememberMe = signal(false)
const handleSubmit = (event, formData) => {
console.log('Login attempt:', formData)
// Handle login logic here
}
const handleReset = () => {
username.set('')
password.set('')
rememberMe.set(false)
}
</script>
<DOMContainer x={100} y={100}>
<form submit={handleSubmit} class="login-form">
<div class="form-group">
<label for="username">Username:</label>
<input
id="username"
name="username"
type="text"
placeholder="Enter username"
value={username}
required
/>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input
id="password"
name="password"
type="password"
placeholder="Enter password"
value={password}
required
/>
</div>
<div class="form-group">
<label>
<input
name="remember"
type="checkbox"
checked={rememberMe()}
/>
Remember me
</label>
</div>
<div class="form-actions">
<button type="submit">Login</button>
<button type="button" click={handleReset}>Reset</button>
</div>
</form>
</DOMContainer>
```
## Supported HTML Elements
The DOMContainer supports all standard HTML elements, including:
- **Form elements**: `input`, `textarea`, `select`, `button`, `form`
- **Text elements**: `p`, `span`, `div`, `h1`-`h6`, `label`
- **List elements**: `ul`, `ol`, `li`
- **Media elements**: `img`, `video`, `audio`
- **Interactive elements**: `a`, `button`
- **Semantic elements**: `section`, `article`, `header`, `footer`, `nav`
## Supported Events
The component supports all standard DOM events:
- **Mouse events**: `click`, `mouseover`, `mouseout`, `mouseenter`, `mouseleave`, `mousemove`, `mouseup`, `mousedown`
- **Touch events**: `touchstart`, `touchend`, `touchmove`, `touchcancel`
- **Keyboard events**: `keydown`, `keyup`, `keypress`
- **Form events**: `submit`, `reset`, `change`, `input`, `focus`, `blur`
- **Drag events**: `drag`, `dragend`, `dragenter`, `dragleave`, `dragover`, `drop`, `dragstart`
- **Other events**: `wheel`, `scroll`, `resize`, `contextmenu`, `select`
================================================
FILE: docs/components/examples/focus-navigation-dom.js
================================================
export default {
title: "Focus Navigation with DOM",
description: "Navigate through a scrollable list of DOM buttons using keyboard controls (arrow keys or gamepad)",
defaultViewMode: "both",
files: {
"app.ce": `
<Canvas>
<Container>
<Text
text="Focus Navigation DOM Example"
x={0}
y={-250}
style={{ fontSize: 32, fill: "#ecf0f1" }}
/>
<Navigation
tabindex={tabindex}
controls={controls}
>
<DOMContainer>
<div class="button-container">
<div>
<div>
@for (item of items) {
<button tabindex={@item.@id}>{@item.@label}</button>
}
</div>
</div>
</div>
</DOMContainer>
</Navigation>
</Container>
</Canvas>
<style scoped>
.button-container {
display: flex;
flex-direction: column;
gap: 10px;
padding: 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
overflow: hidden;
height: 300px;
}
button {
padding: 10px 20px;
f
gitextract_dj33z3ap/ ├── .agents/ │ └── skills/ │ ├── canvasengine/ │ │ ├── SKILL.md │ │ └── agents/ │ │ └── openai.yaml │ └── pixijs/ │ ├── SKILL.md │ └── references/ │ └── index.md ├── .changeset/ │ └── config.json ├── .codex ├── .github/ │ └── workflows/ │ └── ci.yml ├── .gitignore ├── .npmignore ├── README.md ├── __mocks__/ │ └── pixi-viewport.ts ├── docs/ │ ├── .vitepress/ │ │ ├── config.ts │ │ └── theme/ │ │ ├── Layout.vue │ │ ├── components/ │ │ │ ├── CustomHome.vue │ │ │ ├── Playground.vue │ │ │ ├── config.ts │ │ │ └── example.ts │ │ ├── index.ts │ │ └── style.css │ ├── advanced/ │ │ ├── conditional-rendering.md │ │ ├── performance.md │ │ └── without-compiler.md │ ├── api/ │ │ ├── context.md │ │ ├── element.md │ │ └── testing.md │ ├── components/ │ │ ├── _display-object.md │ │ ├── button.md │ │ ├── canvas.md │ │ ├── container.md │ │ ├── dom-container.md │ │ ├── examples/ │ │ │ ├── focus-navigation-dom.js │ │ │ └── polygon-example.js │ │ ├── graphic.md │ │ ├── joystick.md │ │ ├── mesh.md │ │ ├── navigation.md │ │ ├── nine-slice-sprite.md │ │ ├── sprite.md │ │ ├── svg.md │ │ ├── text.md │ │ ├── tiling-sprite.md │ │ ├── video.md │ │ └── viewport.md │ ├── concepts/ │ │ ├── animation.md │ │ ├── child-component.md │ │ ├── context.md │ │ ├── dependencies.md │ │ ├── dynamic-components.md │ │ ├── examples/ │ │ │ └── conditional-rendering.js │ │ ├── lifecycle.md │ │ ├── reactive.md │ │ ├── ref.md │ │ ├── slot.md │ │ ├── styling.md │ │ ├── template-syntax.md │ │ └── trigger.md │ ├── directives/ │ │ ├── controls.md │ │ ├── drag.md │ │ ├── flash.md │ │ ├── shake.md │ │ └── sound.md │ ├── get_started/ │ │ ├── installation.md │ │ ├── readme.md │ │ └── start.md │ ├── index.md │ ├── package.json │ ├── presets/ │ │ ├── _before.md │ │ ├── bar.md │ │ ├── fog-of-war.md │ │ ├── footprints.md │ │ ├── fx.md │ │ ├── loading.md │ │ ├── night-ambiant.md │ │ ├── sprite-shadows.md │ │ ├── tilemap.md │ │ └── weather.md │ └── public/ │ ├── [A]Dirt_pipo.tsx │ ├── [A]Flower_pipo.tsx │ ├── [A]Grass_pipo.tsx │ ├── [A]Wall-Up_pipo.tsx │ ├── [A]WaterFall_pipo.tsx │ ├── [A]Water_pipo.tsx │ ├── [Base]BaseChip_pipo.tsx │ ├── grammar.pegjs │ ├── map.tmx │ ├── simplemap.tmx │ ├── simplemap2.tmx │ └── tileset.tsx ├── package.json ├── packages/ │ ├── compiler/ │ │ ├── grammar.pegjs │ │ ├── grammar2.pegjs │ │ ├── index.ts │ │ ├── package.json │ │ ├── tests/ │ │ │ ├── compiler.spec.ts │ │ │ └── compiler2.spec.ts │ │ ├── tsconfig.json │ │ └── tsup.config.ts │ ├── core/ │ │ ├── index.d.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── Button.ts │ │ │ │ ├── Canvas.ts │ │ │ │ ├── Container.ts │ │ │ │ ├── DOMContainer.ts │ │ │ │ ├── DOMElement.ts │ │ │ │ ├── DOMSprite.ts │ │ │ │ ├── DisplayObject.ts │ │ │ │ ├── FocusContainer.ts │ │ │ │ ├── Graphic.ts │ │ │ │ ├── Joystick.ts │ │ │ │ ├── Mesh.ts │ │ │ │ ├── NineSliceSprite.ts │ │ │ │ ├── ParticleEmitter.ts │ │ │ │ ├── Scene.ts │ │ │ │ ├── Sprite.ts │ │ │ │ ├── Text.ts │ │ │ │ ├── TilingSprite.ts │ │ │ │ ├── Video.ts │ │ │ │ ├── Viewport.ts │ │ │ │ ├── index.ts │ │ │ │ └── types/ │ │ │ │ ├── DisplayObject.ts │ │ │ │ ├── MouseEvent.ts │ │ │ │ ├── Spritesheet.ts │ │ │ │ └── index.ts │ │ │ ├── directives/ │ │ │ │ ├── Controls.ts │ │ │ │ ├── ControlsBase.ts │ │ │ │ ├── Drag.ts │ │ │ │ ├── Flash.ts │ │ │ │ ├── FocusNavigation.ts │ │ │ │ ├── FogVisibility.ts │ │ │ │ ├── GamepadControls.ts │ │ │ │ ├── JoystickControls.ts │ │ │ │ ├── KeyboardControls.ts │ │ │ │ ├── Scheduler.ts │ │ │ │ ├── Shake.ts │ │ │ │ ├── Sound.ts │ │ │ │ ├── Transition.ts │ │ │ │ ├── ViewportCull.ts │ │ │ │ ├── ViewportFollow.ts │ │ │ │ └── index.ts │ │ │ ├── engine/ │ │ │ │ ├── FocusManager.ts │ │ │ │ ├── animation.ts │ │ │ │ ├── bootstrap.ts │ │ │ │ ├── directive.ts │ │ │ │ ├── reactive.ts │ │ │ │ ├── signal.ts │ │ │ │ ├── trigger.ts │ │ │ │ └── utils.ts │ │ │ ├── hooks/ │ │ │ │ ├── addContext.ts │ │ │ │ ├── useFocus.ts │ │ │ │ ├── useProps.ts │ │ │ │ └── useRef.ts │ │ │ ├── index.ts │ │ │ ├── types/ │ │ │ │ └── pixi-cull.d.ts │ │ │ └── utils/ │ │ │ ├── Ease.ts │ │ │ ├── GlobalAssetLoader.ts │ │ │ ├── RadialGradient.ts │ │ │ ├── functions.ts │ │ │ └── tabindex.ts │ │ ├── testing/ │ │ │ └── index.ts │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── presets/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── Bar.ts │ │ │ ├── Button.ts │ │ │ ├── FogOfWar.ts │ │ │ ├── Footprints.ts │ │ │ ├── Loading.ts │ │ │ ├── NightAmbiant.ts │ │ │ ├── Particle.ts │ │ │ ├── SpriteShadows.ts │ │ │ ├── Tilemap/ │ │ │ │ ├── Tile.ts │ │ │ │ ├── TileGroup.ts │ │ │ │ ├── TileLayer.ts │ │ │ │ ├── TileSet.ts │ │ │ │ └── index.ts │ │ │ ├── Weathers/ │ │ │ │ ├── fog.ts │ │ │ │ ├── index.ts │ │ │ │ ├── rain.ts │ │ │ │ └── snow.ts │ │ │ ├── fx/ │ │ │ │ ├── Fx.ts │ │ │ │ ├── index.ts │ │ │ │ ├── presets.ts │ │ │ │ ├── runtime.ts │ │ │ │ ├── textures.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ ├── index.ts │ │ │ └── shaders/ │ │ │ ├── defaultFilter.vert.glsl │ │ │ ├── nightSpot.frag.glsl │ │ │ └── shadowGradient.frag.glsl │ │ ├── tsconfig.json │ │ └── vite.config.ts │ ├── testing/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helpers/ │ │ │ │ ├── createMockComponentInstance.ts │ │ │ │ ├── createMockElement.ts │ │ │ │ └── spyOnElement.ts │ │ │ ├── index.ts │ │ │ └── mocks/ │ │ │ └── pixi-base.ts │ │ ├── tsconfig.json │ │ └── tsup.config.ts │ └── tiled/ │ ├── package.json │ ├── readme.md │ ├── src/ │ │ ├── classes/ │ │ │ ├── Gid.ts │ │ │ ├── Layer.ts │ │ │ ├── Map.ts │ │ │ ├── Object.ts │ │ │ ├── Properties.ts │ │ │ ├── Tile.ts │ │ │ └── Tileset.ts │ │ ├── generate/ │ │ │ ├── tileset.ts │ │ │ └── wangtile.ts │ │ ├── index.ts │ │ ├── parser/ │ │ │ ├── open-file.ts │ │ │ └── parser.ts │ │ ├── types/ │ │ │ ├── Layer.ts │ │ │ ├── Map.ts │ │ │ ├── Objects.ts │ │ │ ├── Text.ts │ │ │ ├── Tile.ts │ │ │ ├── Tileset.ts │ │ │ ├── Types.ts │ │ │ └── WorldMaps.ts │ │ └── utils.ts │ ├── tests/ │ │ ├── class.spec.ts │ │ ├── data.ts │ │ ├── parser.spec.ts │ │ ├── tile-properties.spec.ts │ │ ├── tiledmap-multi-layers.spec.ts │ │ └── tiledmap.spec.ts │ ├── tsconfig.json │ └── vite.config.ts ├── pnpm-workspace.yaml ├── sample/ │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── app.ce │ │ ├── benchmark.ce │ │ ├── box.ce │ │ ├── child.ce │ │ ├── control.ce │ │ ├── controls-buttons.ce │ │ ├── controls-rect.ce │ │ ├── dependencies.ce │ │ ├── flash.ce │ │ ├── flex.ce │ │ ├── focus-navigation-dom.ce │ │ ├── focus-navigation.ce │ │ ├── fogofwar.ce │ │ ├── footprints.ce │ │ ├── freeze.ce │ │ ├── fx.ce │ │ ├── light.ce │ │ ├── loader-spritesheet.ce │ │ ├── main.ts │ │ ├── preset.ce │ │ ├── shake.ce │ │ ├── sprite-shadows.ce │ │ ├── spritesheet.ce │ │ ├── spritesheet2.ce │ │ ├── test.ce │ │ ├── tiled.ce │ │ ├── viewport.ce │ │ └── weather.ce │ ├── tests/ │ │ ├── README.md │ │ └── example.spec.ts │ ├── tsconfig.json │ ├── vite.config.ts │ └── vitest.config.ts ├── skills-lock.json ├── starter/ │ ├── .gitignore │ ├── components/ │ │ ├── app.ce │ │ └── hello.ce │ ├── index.html │ ├── main.ts │ ├── package.json │ ├── tsconfig.json │ └── vite.config.ts ├── tests/ │ ├── components/ │ │ ├── canvas.spec.ts │ │ ├── component.spec.ts │ │ ├── container.spec.ts │ │ ├── displayobject.spec.ts │ │ ├── domcontainer.spec.ts │ │ ├── domsprite.spec.ts │ │ ├── flex.spec.ts │ │ ├── focus-container-tabindex.spec.ts │ │ ├── focus-container.spec.ts │ │ ├── graphics.spec.ts │ │ ├── mesh.spec.ts │ │ ├── nineslicesprite.spec.ts │ │ ├── particleemitter.spec.ts │ │ ├── scene.spec.ts │ │ ├── sprite.spec.ts │ │ ├── text.spec.ts │ │ ├── tilingsprite.spec.ts │ │ ├── video.spec.ts │ │ └── viewport.spec.ts │ ├── directives/ │ │ ├── drag.spec.ts │ │ ├── flash.spec.ts │ │ └── shake.spec.ts │ ├── engine/ │ │ ├── animation.spec.ts │ │ ├── cond-loop.spec.ts │ │ ├── cond.spec.ts │ │ ├── define-props.spec.ts │ │ ├── dependencies.spec.ts │ │ ├── focus-loop-sharing.spec.ts │ │ ├── freeze.spec.ts │ │ ├── lifecycle.spec.ts │ │ ├── loop-sharing.spec.ts │ │ ├── loop-spread-props.spec.ts │ │ ├── loop.spec.ts │ │ ├── nested-loop.spec.ts │ │ ├── reactive-routing.spec.ts │ │ └── trigger.spec.ts │ ├── presets/ │ │ └── fx.spec.ts │ ├── setup/ │ │ └── canvas.ts │ └── utils/ │ ├── GlobalAssetLoader.spec.ts │ └── tabindex.spec.ts ├── tsconfig.json ├── tsconfig.node.json ├── tsup.config.ts └── vitest.config.ts
SYMBOL INDEX (943 symbols across 123 files)
FILE: __mocks__/pixi-viewport.ts
class Viewport (line 4) | class Viewport extends PixiViewport {
FILE: docs/.vitepress/theme/components/config.ts
constant PRIMITIVE_COMPONENTS (line 26) | const PRIMITIVE_COMPONENTS = [
constant CORE_FUNCTIONS (line 55) | const CORE_FUNCTIONS = [
FILE: docs/.vitepress/theme/components/example.ts
type Example (line 1) | interface Example {
FILE: docs/.vitepress/theme/index.ts
method enhanceApp (line 10) | enhanceApp(ctx) {
FILE: packages/compiler/index.ts
constant DEV_SRC (line 11) | const DEV_SRC = "../../src"
function generateHash (line 19) | function generateHash(str: string): string {
function showErrorMessage (line 50) | function showErrorMessage(template: string, error: any): string {
function scopeCSS (line 82) | function scopeCSS(css: string, scopeClass: string): string {
function splitCallArguments (line 179) | function splitCallArguments(argsText: string): string[] {
function addScopeClassToDOMContainer (line 267) | function addScopeClassToDOMContainer(parsedTemplate: string, scopeClass:...
function shaderLoader (line 405) | function shaderLoader() {
function canvasengine (line 428) | function canvasengine() {
FILE: packages/compiler/tests/compiler.spec.ts
function generateHash (line 14) | function generateHash(str: string): string {
function scopeCSS (line 32) | function scopeCSS(css: string, scopeClass: string): string {
function splitCallArguments (line 129) | function splitCallArguments(argsText: string): string[] {
function addScopeClassToDOMContainer (line 217) | function addScopeClassToDOMContainer(parsedTemplate: string, scopeClass:...
FILE: packages/compiler/tests/compiler2.spec.ts
function generateHash (line 14) | function generateHash(str: string): string {
function scopeCSS (line 32) | function scopeCSS(css: string, scopeClass: string): string {
function splitCallArguments (line 129) | function splitCallArguments(argsText: string): string[] {
function addScopeClassToDOMContainer (line 217) | function addScopeClassToDOMContainer(parsedTemplate: string, scopeClass:...
FILE: packages/core/src/components/Button.ts
type ButtonState (line 15) | enum ButtonState {
type ButtonStyle (line 25) | interface ButtonStyle {
type ButtonProps (line 57) | interface ButtonProps {
function Button (line 193) | function Button(props: ButtonProps) {
FILE: packages/core/src/components/Canvas.ts
type CanvasElement (line 17) | interface CanvasElement extends Element<ComponentInstance> {
type CanvasProps (line 29) | interface CanvasProps extends Props {
FILE: packages/core/src/components/Container.ts
type ContainerProps (line 9) | interface ContainerProps extends DisplayObjectProps {
class CanvasContainer (line 13) | class CanvasContainer extends DisplayObject(PixiContainer) {
method onUpdate (line 16) | onUpdate(props) {
method onMount (line 29) | async onMount(args) {
type CanvasContainer (line 57) | interface CanvasContainer extends DisplayObjectProps {}
method onUpdate (line 16) | onUpdate(props) {
method onMount (line 29) | async onMount(args) {
FILE: packages/core/src/components/DOMContainer.ts
constant EVENTS (line 71) | const EVENTS = [
class CanvasDOMContainer (line 111) | class CanvasDOMContainer extends DisplayObject(PixiDOMContainer) {
method hasDomContainerAncestor (line 145) | private hasDomContainerAncestor(): boolean {
method getPercentRatio (line 155) | private getPercentRatio(value: string): number | null {
method getCanvasSize (line 161) | private getCanvasSize() {
method shouldUseCanvasPercent (line 166) | private shouldUseCanvasPercent(): boolean {
method syncCanvasSizeEffect (line 173) | private syncCanvasSizeEffect() {
method applyElementSize (line 189) | private applyElementSize() {
method routeDomChildren (line 237) | private routeDomChildren(children: any): any {
method onInit (line 267) | onInit(props: any) {
method onMount (line 345) | async onMount(element: Element<any>, index?: number) {
method onUpdate (line 351) | onUpdate(props: any) {
method onLayoutComputed (line 357) | onLayoutComputed() {
method onDestroy (line 361) | async onDestroy(parent: Element<any>, afterDestroy?: () => void) {
type CanvasDOMContainer (line 373) | interface CanvasDOMContainer extends DisplayObjectProps { }
method hasDomContainerAncestor (line 145) | private hasDomContainerAncestor(): boolean {
method getPercentRatio (line 155) | private getPercentRatio(value: string): number | null {
method getCanvasSize (line 161) | private getCanvasSize() {
method shouldUseCanvasPercent (line 166) | private shouldUseCanvasPercent(): boolean {
method syncCanvasSizeEffect (line 173) | private syncCanvasSizeEffect() {
method applyElementSize (line 189) | private applyElementSize() {
method routeDomChildren (line 237) | private routeDomChildren(children: any): any {
method onInit (line 267) | onInit(props: any) {
method onMount (line 345) | async onMount(element: Element<any>, index?: number) {
method onUpdate (line 351) | onUpdate(props: any) {
method onLayoutComputed (line 357) | onLayoutComputed() {
method onDestroy (line 361) | async onDestroy(parent: Element<any>, afterDestroy?: () => void) {
FILE: packages/core/src/components/DOMElement.ts
type DOMElementProps (line 13) | interface DOMElementProps extends DisplayObjectProps {
type DOMContainerProps (line 35) | interface DOMContainerProps extends DOMElementProps {
constant EVENTS (line 169) | const EVENTS = [
class CanvasDOMElement (line 209) | class CanvasDOMElement {
method isFormElement (line 223) | private isFormElement(elementType: string): boolean {
method collectClassTokens (line 228) | private collectClassTokens(value: any, tokens: string[]) {
method collectClassSignals (line 252) | private collectClassSignals(value: any, signals: Set<any>) {
method applyClassList (line 269) | private applyClassList(classList: any) {
method syncClassSubscriptions (line 286) | private syncClassSubscriptions(classList: any) {
method appendChildElement (line 305) | private appendChildElement(child: any) {
method onInit (line 358) | onInit(props: DOMElementProps) {
method onMount (line 415) | onMount(context: Element<CanvasDOMElement>) {
method onUpdate (line 447) | onUpdate(props: DOMElementProps) {
method onDestroy (line 516) | async onDestroy(
type CanvasDOMElement (line 550) | interface CanvasDOMElement extends DisplayObjectProps { }
method isFormElement (line 223) | private isFormElement(elementType: string): boolean {
method collectClassTokens (line 228) | private collectClassTokens(value: any, tokens: string[]) {
method collectClassSignals (line 252) | private collectClassSignals(value: any, signals: Set<any>) {
method applyClassList (line 269) | private applyClassList(classList: any) {
method syncClassSubscriptions (line 286) | private syncClassSubscriptions(classList: any) {
method appendChildElement (line 305) | private appendChildElement(child: any) {
method onInit (line 358) | onInit(props: DOMElementProps) {
method onMount (line 415) | onMount(context: Element<CanvasDOMElement>) {
method onUpdate (line 447) | onUpdate(props: DOMElementProps) {
method onDestroy (line 516) | async onDestroy(
FILE: packages/core/src/components/DOMSprite.ts
type DOMSpriteFrame (line 17) | interface DOMSpriteFrame {
type DOMSpriteProps (line 24) | interface DOMSpriteProps extends DOMElementProps {
constant EVENTS (line 63) | const EVENTS = [
type DOMSpriteTextureOptionsMerging (line 103) | type DOMSpriteTextureOptionsMerging = TextureOptions & {
type DOMSpriteFrameOptions (line 109) | type DOMSpriteFrameOptions = FrameOptions & DOMSpriteFrame;
type DOMSpriteAnimationData (line 111) | type DOMSpriteAnimationData = {
type DOMSpriteSheetDefinition (line 119) | type DOMSpriteSheetDefinition = SpritesheetOptions & {
class CanvasDOMSprite (line 123) | class CanvasDOMSprite extends CanvasDOMElement {
method onInit (line 162) | onInit(props: DOMElementProps) {
method onMount (line 183) | onMount(context: Element<CanvasDOMElement>) {
method onUpdate (line 191) | onUpdate(props: DOMElementProps) {
method onDestroy (line 201) | async onDestroy(
method resolveRectangle (line 217) | private resolveRectangle(
method resolveSheetDefinition (line 230) | private resolveSheetDefinition(
method detectImageDimensions (line 245) | private async detectImageDimensions(
method createSheetAnimations (line 259) | private async createSheetAnimations(definition: DOMSpriteSheetDefiniti...
method setSheetDefinition (line 328) | private async setSheetDefinition(
method playSheet (line 368) | private playSheet(name: string, params: any[] = []) {
method getCurrentSheetFrame (line 417) | private getCurrentSheetFrame(): DOMSpriteFrame | null {
method advanceSheet (line 424) | private advanceSheet(tick: Tick) {
method mergeEventAttrs (line 460) | private mergeEventAttrs(props: DOMSpriteProps): DOMSpriteProps {
method applyProps (line 498) | private applyProps(props: DOMSpriteProps) {
method resolveValue (line 573) | private resolveValue<T>(value: T | Signal<T> | { value?: T } | undefin...
method resolvePoint (line 582) | private resolvePoint(
method resolveSize (line 600) | private resolveSize(value: DOMSpriteProps["width"] | DOMSpriteProps["h...
method resolvePixelSize (line 608) | private resolvePixelSize(value?: string): number | undefined {
method toCssColor (line 621) | private toCssColor(tint: number): string {
method applyDisplayProps (line 626) | private applyDisplayProps(props: DOMSpriteProps) {
method syncRenderElement (line 742) | private syncRenderElement() {
method getRenderElement (line 769) | private getRenderElement() {
method applyContainScale (line 773) | private applyContainScale() {
method bindPlayingSignal (line 791) | private bindPlayingSignal(context: Element<CanvasDOMElement>) {
method bindSheetParams (line 812) | private bindSheetParams(context: Element<CanvasDOMElement>) {
method getFrames (line 830) | private getFrames(): DOMSpriteFrame[] {
method normalizeIndex (line 836) | private normalizeIndex(index: number, length: number) {
method render (line 847) | private render() {
method applyFrame (line 874) | private applyFrame(frame: DOMSpriteFrame) {
method updateAnimationLoop (line 910) | private updateAnimationLoop() {
method startAnimationLoop (line 926) | private startAnimationLoop() {
method stopAnimationLoop (line 998) | private stopAnimationLoop() {
method advance (line 1016) | private advance(deltaTime: number) {
FILE: packages/core/src/components/DisplayObject.ts
type ComponentInstance (line 17) | interface ComponentInstance extends PixiMixins.ContainerOptions {
constant EVENTS (line 30) | const EVENTS = [
type OnHook (line 101) | type OnHook = (() => void) | (() => Promise<void> | void);
function DisplayObject (line 103) | function DisplayObject(extendClass) {
FILE: packages/core/src/components/FocusContainer.ts
type FocusContainerProps (line 21) | interface FocusContainerProps extends DisplayObjectProps {
class CanvasFocusContainer (line 69) | class CanvasFocusContainer {
method onInit (line 80) | onInit(props: FocusContainerProps) {
method onMount (line 110) | async onMount(element: Element<CanvasFocusContainer>): Promise<void> {
method onUpdate (line 165) | onUpdate(props: FocusContainerProps) {
method onDestroy (line 181) | async onDestroy(parent: Element<any>, afterDestroy?: () => void): Prom...
method registerChildren (line 200) | private registerChildren(element: Element<CanvasFocusContainer>) {
method getContainerId (line 331) | getContainerId(): string {
method getCurrentIndexSignal (line 340) | getCurrentIndexSignal(): Signal<number | null> | null {
method getFocusedElementSignal (line 349) | getFocusedElementSignal(): Signal<Element | null> | null {
type CanvasFocusContainer (line 354) | interface CanvasFocusContainer extends DisplayObjectProps { }
method onInit (line 80) | onInit(props: FocusContainerProps) {
method onMount (line 110) | async onMount(element: Element<CanvasFocusContainer>): Promise<void> {
method onUpdate (line 165) | onUpdate(props: FocusContainerProps) {
method onDestroy (line 181) | async onDestroy(parent: Element<any>, afterDestroy?: () => void): Prom...
method registerChildren (line 200) | private registerChildren(element: Element<CanvasFocusContainer>) {
method getContainerId (line 331) | getContainerId(): string {
method getCurrentIndexSignal (line 340) | getCurrentIndexSignal(): Signal<number | null> | null {
method getFocusedElementSignal (line 349) | getFocusedElementSignal(): Signal<Element | null> | null {
FILE: packages/core/src/components/Graphic.ts
type GraphicsProps (line 11) | interface GraphicsProps extends DisplayObjectProps {
type RectProps (line 15) | interface RectProps extends DisplayObjectProps {
type CircleProps (line 19) | interface CircleProps extends DisplayObjectProps {
type EllipseProps (line 24) | interface EllipseProps extends DisplayObjectProps {
type TriangleProps (line 28) | interface TriangleProps extends DisplayObjectProps {
type SvgProps (line 33) | interface SvgProps extends DisplayObjectProps {
class CanvasGraphics (line 42) | class CanvasGraphics extends DisplayObject(PixiGraphics) {
method onInit (line 78) | async onInit(props) {
method onMount (line 89) | async onMount(element: Element<any>, index?: number): Promise<void> {
method onUpdate (line 139) | onUpdate(props: any) {
method onDestroy (line 163) | async onDestroy(parent: Element<ComponentInstance>, afterDestroy: () =...
function Graphics (line 174) | function Graphics(props: GraphicsProps) {
function Rect (line 187) | function Rect(props: RectProps) {
function Circle (line 210) | function Circle(props: CircleProps) {
function Ellipse (line 232) | function Ellipse(props: EllipseProps) {
function Triangle (line 236) | function Triangle(props: TriangleProps) {
function Svg (line 283) | function Svg(props: SvgProps) {
FILE: packages/core/src/components/Joystick.ts
type JoystickChangeEvent (line 10) | interface JoystickChangeEvent {
type Direction (line 16) | enum Direction {
type JoystickSettings (line 27) | interface JoystickSettings {
function Joystick (line 41) | function Joystick(opts: JoystickSettings = {}) {
FILE: packages/core/src/components/Mesh.ts
type MeshProps (line 14) | interface MeshProps extends DisplayObjectProps {
class CanvasMesh (line 53) | class CanvasMesh extends DisplayObject(PixiMesh) {
method constructor (line 64) | constructor() {
method onInit (line 86) | onInit(props: MeshProps) {
method onUpdate (line 118) | async onUpdate(props: MeshProps) {
method onDestroy (line 174) | async onDestroy(parent: Element<ComponentInstance>, afterDestroy: () =...
FILE: packages/core/src/components/NineSliceSprite.ts
type NineSliceSpriteProps (line 7) | interface NineSliceSpriteProps extends DisplayObjectProps {
class CanvasNineSliceSprite (line 19) | class CanvasNineSliceSprite extends DisplayObject(PixiNineSliceSprite) {
method constructor (line 20) | constructor() {
method onUpdate (line 28) | async onUpdate(props: NineSliceSpriteProps) {
type CanvasNineSliceSprite (line 41) | interface CanvasNineSliceSprite extends PixiNineSliceSprite {
method constructor (line 20) | constructor() {
method onUpdate (line 28) | async onUpdate(props: NineSliceSpriteProps) {
function NineSliceSprite (line 47) | function NineSliceSprite(props: NineSliceSpriteProps) {
FILE: packages/core/src/components/ParticleEmitter.ts
class CanvasParticlesEmitter (line 7) | class CanvasParticlesEmitter extends CanvasContainer {
method onMount (line 11) | async onMount(params) {
method onUpdate (line 26) | onUpdate(props) {}
method onDestroy (line 28) | async onDestroy(parent: Element<ComponentInstance>, afterDestroy: () =...
function ParticlesEmitter (line 41) | function ParticlesEmitter(props) {
FILE: packages/core/src/components/Scene.ts
function Scene (line 4) | function Scene(props) {
FILE: packages/core/src/components/Sprite.ts
type Image (line 36) | type Image = { image: string };
type TextureOptionsMerging (line 38) | type TextureOptionsMerging = TextureOptions & {
type FrameOptionsMerging (line 45) | type FrameOptionsMerging = TextureOptionsMerging & FrameOptions;
type SpritesheetOptionsMerging (line 46) | type SpritesheetOptionsMerging = TextureOptionsMerging & SpritesheetOpti...
type TransformOptionsAsArray (line 47) | type TransformOptionsAsArray = Pick<
type AnimationDataFrames (line 52) | type AnimationDataFrames = {
type HitboxAnchorMode (line 61) | type HitboxAnchorMode = "top-left" | "center" | "foot";
type Hitbox (line 63) | type Hitbox = {
type StandardAnimation (line 69) | enum StandardAnimation {
class CanvasSprite (line 74) | class CanvasSprite extends DisplayObject(PixiSprite) {
method renderer (line 95) | get renderer() {
method detectImageDimensions (line 114) | private async detectImageDimensions(imagePath: string): Promise<{ widt...
method createTextures (line 173) | private async createTextures(
method createAnimations (line 242) | private async createAnimations() {
method onMount (line 310) | async onMount(params: Element<any>) {
method onUpdate (line 389) | async onUpdate(props) {
method onDestroy (line 489) | async onDestroy(parent: Element, afterDestroy: () => void): Promise<vo...
method has (line 510) | has(name: string): boolean {
method get (line 514) | get(name: string): AnimationDataFrames {
method isPlaying (line 518) | isPlaying(name?: string): boolean {
method stop (line 524) | stop() {
method play (line 528) | play(name: string, params: any[] = []) {
method resetAnimations (line 596) | async resetAnimations(): Promise<void> {
method update (line 619) | update({ deltaRatio }) {
method applyHitboxAnchor (line 720) | private applyHitboxAnchor(
method normalizeHitbox (line 767) | private normalizeHitbox(hitbox: unknown): Hitbox | null {
method clamp (line 785) | private clamp(value: number) {
type CanvasSprite (line 790) | interface CanvasSprite extends PixiSprite {
method renderer (line 95) | get renderer() {
method detectImageDimensions (line 114) | private async detectImageDimensions(imagePath: string): Promise<{ widt...
method createTextures (line 173) | private async createTextures(
method createAnimations (line 242) | private async createAnimations() {
method onMount (line 310) | async onMount(params: Element<any>) {
method onUpdate (line 389) | async onUpdate(props) {
method onDestroy (line 489) | async onDestroy(parent: Element, afterDestroy: () => void): Promise<vo...
method has (line 510) | has(name: string): boolean {
method get (line 514) | get(name: string): AnimationDataFrames {
method isPlaying (line 518) | isPlaying(name?: string): boolean {
method stop (line 524) | stop() {
method play (line 528) | play(name: string, params: any[] = []) {
method resetAnimations (line 596) | async resetAnimations(): Promise<void> {
method update (line 619) | update({ deltaRatio }) {
method applyHitboxAnchor (line 720) | private applyHitboxAnchor(
method normalizeHitbox (line 767) | private normalizeHitbox(hitbox: unknown): Hitbox | null {
method clamp (line 785) | private clamp(value: number) {
type SpriteProps (line 797) | interface SpriteProps extends DisplayObjectProps {
type SpritePropsWithImage (line 818) | interface SpritePropsWithImage extends Omit<SpriteProps, "sheet"> {
type SpritePropsWithSheet (line 828) | interface SpritePropsWithSheet
type SpritePropTypes (line 842) | type SpritePropTypes = SpritePropsWithImage | SpritePropsWithSheet;
FILE: packages/core/src/components/Text.ts
type TextEffect (line 9) | enum TextEffect {
type TextProps (line 13) | interface TextProps extends DisplayObjectProps {
class CanvasText (line 33) | class CanvasText extends DisplayObject(PixiText) {
method onMount (line 51) | async onMount(element: Element<any>, index?: number): Promise<void> {
method onUpdate (line 83) | onUpdate(props: TextProps) {
method onCompleteCallback (line 126) | get onCompleteCallback() {
method initializeTypewriterSound (line 135) | private initializeTypewriterSound() {
method playTypewriterSound (line 158) | private playTypewriterSound(currentTime: number) {
method updateLayout (line 173) | private updateLayout() {
method typewriterEffect (line 182) | private typewriterEffect() {
method skipTypewriter (line 210) | private skipTypewriter() {
method onDestroy (line 227) | async onDestroy(parent: Element<any>, afterDestroy?: () => void): Prom...
function Text (line 250) | function Text(props: TextProps) {
FILE: packages/core/src/components/TilingSprite.ts
type TilingSpriteProps (line 6) | interface TilingSpriteProps extends DisplayObjectProps {
class CanvasTilingSprite (line 14) | class CanvasTilingSprite extends DisplayObject(PixiTilingSprite) {
method onUpdate (line 15) | onUpdate(props: TilingSpriteProps): void {
function TilingSprite (line 37) | function TilingSprite(props: TilingSpriteProps) {
FILE: packages/core/src/components/Video.ts
type VideoProps (line 7) | interface VideoProps {
function Video (line 18) | function Video(props: VideoProps) {
FILE: packages/core/src/components/Viewport.ts
constant EVENTS (line 8) | const EVENTS = [
type ViewportProps (line 32) | interface ViewportProps extends Props {
class CanvasViewport (line 48) | class CanvasViewport extends DisplayObject(Container) {
method constructor (line 54) | constructor() {
method addChild (line 73) | addChild<U extends any[]>(...children: U): U[0] {
method addChildAt (line 77) | addChildAt<T extends ContainerChild | IRenderLayer>(child: T, index: n...
method onInit (line 81) | onInit(props) {
method onMount (line 94) | async onMount(element: Element<any>, index?: number): Promise<void> {
method onUpdate (line 131) | onUpdate(props) {
method updateViewportSettings (line 136) | private updateViewportSettings(props) {
method updateMask (line 182) | private updateMask() {
method onDestroy (line 196) | async onDestroy(parent: Element<any>, afterDestroy?: () => void): Prom...
method follow (line 205) | follow(...args: any[]) {
method plugins (line 209) | get plugins() {
function Viewport (line 216) | function Viewport(props: ViewportProps) {
FILE: packages/core/src/components/types/DisplayObject.ts
type FlexDirection (line 9) | type FlexDirection = 'row' | 'column' | 'row-reverse' | 'column-reverse';
type JustifyContent (line 10) | type JustifyContent = 'flex-start' | 'flex-end' | 'center' | 'space-betw...
type AlignContent (line 11) | type AlignContent = 'flex-start' | 'flex-end' | 'center' | 'space-betwee...
type Size (line 12) | type Size = number | `${number}%`
type EdgeSize (line 13) | type EdgeSize = SignalOrPrimitive<Size | [Size, Size] | [Size, Size, Siz...
type ObjectFit (line 14) | type ObjectFit = 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
type ObjectPosition (line 15) | type ObjectPosition = string;
type TransformOrigin (line 16) | type TransformOrigin = string;
type PositionType (line 17) | type PositionType = 'relative' | 'absolute' | 'static';
type ObservablePointSignal (line 19) | type ObservablePointSignal = [number, number] | SignalOrPrimitive<[numbe...
type DisplayObjectProps (line 21) | interface DisplayObjectProps {
FILE: packages/core/src/components/types/MouseEvent.ts
type MouseEvent (line 1) | interface MouseEvent {
FILE: packages/core/src/components/types/Spritesheet.ts
type TransformOptions (line 1) | interface TransformOptions {
type FrameOptions (line 140) | interface FrameOptions extends TransformOptions {
type TextureOptions (line 146) | interface TextureOptions {
type AnimationFrames (line 204) | type AnimationFrames = FrameOptions[][] | ((...args: any) => FrameOption...
type TexturesOptions (line 206) | interface TexturesOptions extends TextureOptions, TransformOptions {
type SpritesheetOptions (line 210) | interface SpritesheetOptions extends TransformOptions, TextureOptions {
type SpritesheetImageOptions (line 216) | type SpritesheetImageOptions = SpritesheetOptions & {
type SpritesheetImagesOptions (line 241) | type SpritesheetImagesOptions = SpritesheetOptions & {
function Spritesheet (line 262) | function Spritesheet(options: SpritesheetImageOptions | SpritesheetImage...
FILE: packages/core/src/components/types/index.ts
type SignalOrPrimitive (line 4) | type SignalOrPrimitive<T> = T | Signal<T> | AnimatedSignal<T>;
FILE: packages/core/src/directives/Controls.ts
class ControlsDirective (line 28) | class ControlsDirective extends Directive {
method onInit (line 39) | onInit(element: Element<any>) {
method onMount (line 87) | onMount(element: Element<any>) { }
method onUpdate (line 93) | onUpdate(props: any, element: Element<any>) {
method onDestroy (line 118) | onDestroy(element: Element<any>) {
method getControl (line 149) | getControl(inputName: string) {
method getControls (line 159) | getControls() {
method applyControl (line 172) | async applyControl(controlName: string | number, isDown?: boolean, pay...
method stopInputs (line 188) | stopInputs() {
method listenInputs (line 204) | listenInputs() {
method options (line 222) | get options(): Controls {
method keyboard (line 231) | get keyboard(): KeyboardControls | null {
method gamepad (line 240) | get gamepad(): GamepadControls | null {
method joystick (line 249) | get joystick(): JoystickControls | null {
FILE: packages/core/src/directives/ControlsBase.ts
type ControlOptions (line 3) | interface ControlOptions {
type Controls (line 15) | interface Controls {
type BoundKey (line 19) | type BoundKey = { actionName: string, options: ControlOptions, parameter...
method start (line 78) | start() {
method destroy (line 88) | destroy() {
method bindKey (line 103) | protected bindKey(keys: string | string[], actionName: string, options: ...
method applyInput (line 119) | protected applyInput(keyName: string) {
method getControl (line 147) | getControl(inputName: string): BoundKey | undefined {
method getControls (line 161) | getControls(): { [key: string]: BoundKey } {
method stopInputs (line 198) | stopInputs() {
method listenInputs (line 212) | listenInputs() {
method setInputs (line 242) | setInputs(inputs: Controls) {
method options (line 264) | get options(): Controls {
FILE: packages/core/src/directives/Drag.ts
type DragProps (line 11) | type DragProps = {
class Drop (line 24) | class Drop extends Directive {
method onInit (line 27) | onInit(element: Element<Container>) {
method onMount (line 31) | onMount(element: Element<Container>) {
method onUpdate (line 35) | onUpdate() {}
method onDestroy (line 37) | onDestroy() {
class Drag (line 42) | class Drag extends Directive {
method onInit (line 61) | onInit(element: Element<Container>) {
method onMount (line 70) | onMount(element: Element<Container>) {
method dragProps (line 115) | get dragProps() {
method axis (line 130) | get axis() {
method onDragMove (line 149) | private onDragMove(event: FederatedPointerEvent) {
method updateViewportPosition (line 196) | private updateViewportPosition(globalPosition: Point) {
method onDragEnd (line 259) | private onDragEnd() {
method onKeyDown (line 274) | onKeyDown(event: KeyboardEvent) {
method onKeyUp (line 283) | onKeyUp(event: KeyboardEvent) {
method areRequiredKeysPressed (line 291) | private areRequiredKeysPressed(): boolean {
method onPointerDown (line 328) | private onPointerDown(event: FederatedPointerEvent) {
method startDrag (line 349) | private startDrag() {
method onUpdate (line 358) | onUpdate(props) {
method onDestroy (line 365) | onDestroy() {
FILE: packages/core/src/directives/Flash.ts
type FlashType (line 11) | type FlashType = 'alpha' | 'tint' | 'both';
type FlashProps (line 13) | type FlashProps = {
class Flash (line 84) | class Flash extends Directive {
method onInit (line 104) | onInit(element: Element<Container>) {
method onMount (line 112) | onMount(element: Element<Container>) {
method flashProps (line 148) | get flashProps(): FlashProps {
method performFlash (line 163) | private async performFlash(data?: any): Promise<void> {
method onUpdate (line 359) | onUpdate(props: any) {
method onDestroy (line 372) | onDestroy() {
FILE: packages/core/src/directives/FocusNavigation.ts
class FocusNavigationDirective (line 26) | class FocusNavigationDirective extends Directive {
method onInit (line 36) | onInit(element: Element<CanvasFocusContainer>) {
method onMount (line 45) | onMount(element: Element<CanvasFocusContainer>) {
method onUpdate (line 86) | onUpdate(props: any, element: Element<CanvasFocusContainer>) {
method onDestroy (line 101) | onDestroy(element: Element<CanvasFocusContainer>) {
FILE: packages/core/src/directives/FogVisibility.ts
type FogVisibilityState (line 8) | type FogVisibilityState = "visible" | "explored" | "unknown";
type FogVisibilityMode (line 9) | type FogVisibilityMode = "visible" | "explored";
type FogVisibilityHideAs (line 10) | type FogVisibilityHideAs = "visible" | "alpha";
type FogVisibilityController (line 12) | type FogVisibilityController = {
type FogVisibilityPoint (line 20) | type FogVisibilityPoint = {
type FogVisibilityProps (line 25) | type FogVisibilityProps = {
type NormalizedFogVisibilityProps (line 35) | type NormalizedFogVisibilityProps = {
class FogVisibility (line 48) | class FogVisibility extends Directive {
method onInit (line 54) | onInit(element: Element<Container>) {
method onMount (line 58) | onMount(element: Element<Container>) {
method onUpdate (line 64) | onUpdate() {
method onDestroy (line 69) | onDestroy() {
method bindTick (line 86) | private bindTick() {
method resolveSignalValue (line 107) | private resolveSignalValue<T>(value: SignalOrPrimitive<T> | undefined,...
method readOptions (line 116) | private readOptions(): NormalizedFogVisibilityProps {
method readBaseVisible (line 159) | private readBaseVisible() {
method readBaseAlpha (line 164) | private readBaseAlpha() {
method samplePoint (line 171) | private samplePoint(options: NormalizedFogVisibilityProps) {
method resolveVisibleState (line 187) | private resolveVisibleState(
method resolveExploredState (line 208) | private resolveExploredState(
method evaluateVisibility (line 224) | private evaluateVisibility(explicitOptions?: NormalizedFogVisibilityPr...
FILE: packages/core/src/directives/GamepadControls.ts
type GamepadConfig (line 28) | interface GamepadConfig {
constant DEFAULT_BUTTON_MAPPING (line 54) | const DEFAULT_BUTTON_MAPPING: { [buttonName: string]: string } = {
constant DEFAULT_AXIS_MAPPING (line 63) | const DEFAULT_AXIS_MAPPING: { [axisDirection: string]: string } = {
class GamepadControls (line 99) | class GamepadControls extends ControlsBase {
method setupListeners (line 121) | protected setupListeners(): void {
method cleanup (line 128) | protected cleanup(): void {
method initGamepad (line 138) | private initGamepad(): void {
method handleGamepadConnect (line 164) | private handleGamepadConnect(): void {
method handleGamepadDisconnect (line 184) | private handleGamepadDisconnect(): void {
method handleGamepadButtonPress (line 211) | private handleGamepadButtonPress(e: any): void {
method handleGamepadAxisMove (line 232) | private handleGamepadAxisMove(e: any): void {
method processGamepadMovement (line 286) | private processGamepadMovement(): void {
method preStep (line 314) | protected preStep(): void {
method updateGamepadConfig (line 338) | updateGamepadConfig(config: Partial<GamepadConfig>): void {
method extractGamepadConfig (line 373) | extractGamepadConfig(inputs: Controls & { gamepad?: GamepadConfig }): ...
method getGamepadConfig (line 384) | getGamepadConfig(): GamepadConfig {
method applyControl (line 397) | async applyControl(controlName: string | number, isDown?: boolean, pay...
method setInputs (line 454) | setInputs(inputs: Controls & { gamepad?: GamepadConfig }): void {
method onConnect (line 470) | onConnect(callback: () => void): void {
method onDisconnect (line 485) | onDisconnect(callback: () => void): void {
method offConnect (line 494) | offConnect(callback: () => void): void {
method offDisconnect (line 506) | offDisconnect(callback: () => void): void {
method isConnected (line 518) | isConnected(): boolean {
method reinit (line 532) | reinit(): void {
FILE: packages/core/src/directives/JoystickControls.ts
type JoystickDirection (line 6) | type JoystickDirection =
type JoystickChangeEvent (line 19) | interface JoystickChangeEvent {
type JoystickConfig (line 47) | interface JoystickConfig {
constant DEFAULT_DIRECTION_MAPPING (line 63) | const DEFAULT_DIRECTION_MAPPING: { [direction: string]: string | string[...
class JoystickControls (line 106) | class JoystickControls extends ControlsBase {
method setupListeners (line 124) | protected setupListeners(): void {
method cleanup (line 132) | protected cleanup(): void {
method preStep (line 143) | protected preStep(): void {
method handleJoystickChange (line 154) | handleJoystickChange(event: JoystickChangeEvent): void {
method handleJoystickStart (line 230) | handleJoystickStart(): void {
method handleJoystickEnd (line 240) | handleJoystickEnd(): void {
method stopAllMovements (line 248) | private stopAllMovements(): void {
method processJoystickMovement (line 270) | private processJoystickMovement(): void {
method updateJoystickConfig (line 289) | updateJoystickConfig(config: Partial<JoystickConfig>): void {
method extractJoystickConfig (line 303) | extractJoystickConfig(inputs: Controls & { joystick?: JoystickConfig }...
method getJoystickConfig (line 314) | getJoystickConfig(): JoystickConfig {
method applyControl (line 326) | async applyControl(controlName: string | number, isDown?: boolean, pay...
method setInputs (line 383) | setInputs(inputs: Controls & { joystick?: JoystickConfig }): void {
method isActive (line 393) | isActive(): boolean {
FILE: packages/core/src/directives/KeyboardControls.ts
type Input (line 3) | enum Input {
class KeyboardControls (line 346) | class KeyboardControls extends ControlsBase {
method setupListeners (line 370) | protected setupListeners(): void {
method cleanup (line 378) | protected cleanup(): void {
method preStep (line 386) | protected preStep() {
method applyInput (line 412) | protected applyInput(keyName: string) {
method applyKeyDown (line 445) | private applyKeyDown(name: string) {
method applyKeyUp (line 452) | private applyKeyUp(name: string) {
method applyKeyPress (line 459) | private applyKeyPress(name: string): Promise<void> {
method onKeyChange (line 469) | private onKeyChange(e: KeyboardEvent, isDown: boolean) {
method updateDirectionState (line 501) | private updateDirectionState(keyName: string, isDown: boolean) {
method getDirection (line 518) | private getDirection(): string {
method applyControl (line 552) | async applyControl(controlName: string | number, isDown?: boolean | un...
method listenInputs (line 572) | listenInputs() {
FILE: packages/core/src/directives/Scheduler.ts
type Tick (line 6) | interface Tick {
class Scheduler (line 13) | class Scheduler extends Directive {
method onInit (line 24) | onInit(element: Element) {
method onDestroy (line 28) | onDestroy() { }
method onMount (line 29) | onMount(element: Element) { }
method onUpdate (line 30) | onUpdate(props: any) { }
method nextTick (line 32) | nextTick(timestamp: number) {
method start (line 57) | start(options: {
method stop (line 104) | stop() {
FILE: packages/core/src/directives/Shake.ts
type ShakeProps (line 11) | type ShakeProps = {
class Shake (line 66) | class Shake extends Directive {
method onInit (line 83) | onInit(element: Element<Container>) {
method onMount (line 91) | onMount(element: Element<Container>) {
method shakeProps (line 121) | get shakeProps(): ShakeProps {
method resolveSignalValue (line 131) | private resolveSignalValue<T>(value: SignalOrPrimitive<T>): T {
method performShake (line 139) | private async performShake(data?: any): Promise<void> {
method onUpdate (line 252) | onUpdate(props: any) {
method onDestroy (line 265) | onDestroy() {
FILE: packages/core/src/directives/Sound.ts
constant EVENTS (line 9) | const EVENTS = ['load', 'loaderror', 'playerror', 'play', 'end', 'pause'...
class Sound (line 21) | class Sound extends Directive {
method onInit (line 28) | onInit(element: Element<Container>) { }
method onMount (line 30) | onMount(element: Element<Container>) {
method onUpdate (line 88) | onUpdate(props: any) {
method onDestroy (line 111) | onDestroy() {
class SoundListenerPosition (line 144) | class SoundListenerPosition extends Directive {
method onMount (line 145) | onMount(element: Element<any>) {
method onInit (line 148) | onInit(element: Element<any>) { }
method onUpdate (line 149) | onUpdate(props: any) { }
method onDestroy (line 150) | onDestroy() { }
FILE: packages/core/src/directives/Transition.ts
class Transition (line 7) | class Transition extends Directive {
method onInit (line 10) | onInit(element: Element<Container>) {
method onMount (line 13) | onMount(element: Element<Container>) {
method onUpdate (line 36) | onUpdate(props: any) {
method onDestroy (line 40) | onDestroy() {
FILE: packages/core/src/directives/ViewportCull.ts
class ViewportCull (line 8) | class ViewportCull extends Directive {
method onInit (line 11) | onInit(element) {
method onMount (line 16) | onMount(element: Element<Container>) {
method onUpdate (line 36) | onUpdate(props: any) { }
method onDestroy (line 37) | onDestroy() { }
FILE: packages/core/src/directives/ViewportFollow.ts
type ViewportFollowProps (line 8) | type ViewportFollowProps = {
class ViewportFollow (line 16) | class ViewportFollow extends Directive {
method onInit (line 17) | onInit(element: Element<ComponentInstance>) {
method onMount (line 20) | onMount(element: Element) {
method onUpdate (line 23) | onUpdate(viewportFollow: any, element: Element) {
method onDestroy (line 50) | onDestroy(element: Element) {
FILE: packages/core/src/engine/FocusManager.ts
type ScrollOptions (line 14) | interface ScrollOptions {
type WritableElementSignal (line 24) | type WritableElementSignal = WritableSignal<Element | null> | WritableOb...
type FocusContainerData (line 26) | interface FocusContainerData {
class FocusManager (line 53) | class FocusManager {
method getInstance (line 63) | static getInstance(): FocusManager {
method registerContainer (line 76) | registerContainer(id: string, data: Omit<FocusContainerData, 'id'>): v...
method updateContainer (line 86) | updateContainer(id: string, data: Partial<Omit<FocusContainerData, 'id...
method setTabindex (line 93) | setTabindex(id: string, tabindex?: SignalOrPrimitive<number> | null): ...
method unregisterContainer (line 128) | unregisterContainer(id: string): void {
method registerFocusable (line 140) | registerFocusable(containerId: string, element: Element, index: number...
method unregisterFocusable (line 162) | unregisterFocusable(containerId: string, index: number): void {
method navigate (line 174) | navigate(containerId: string, direction: 'next' | 'previous'): void {
method setIndex (line 230) | setIndex(containerId: string, index: number): void {
method applyFocus (line 242) | private applyFocus(
method getElement (line 296) | getElement(containerId: string, index: number): Element | null {
method getCurrentIndexSignal (line 308) | getCurrentIndexSignal(containerId: string): Signal<number | null> | nu...
method getFocusedElementSignal (line 319) | getFocusedElementSignal(containerId: string): Signal<Element | null> |...
method isElementVisible (line 331) | isElementVisible(element: Element, viewport?: CanvasViewport): boolean {
method getElementBounds (line 351) | getElementBounds(element: Element): { x: number; y: number; width: num...
method scrollToElement (line 385) | scrollToElement(
method animateScroll (line 460) | private animateScroll(
FILE: packages/core/src/engine/animation.ts
function getGlobalTickSignal (line 12) | function getGlobalTickSignal(): Signal<Tick> | undefined {
type AnimateOptions (line 16) | interface AnimateOptions<T> {
type AnimatedState (line 24) | interface AnimatedState<T> {
type AnimatedSignal (line 30) | interface AnimatedSignal<T> extends Omit<WritableSignal<T>, 'set'> {
function isAnimatedSignal (line 39) | function isAnimatedSignal(signal: WritableSignal<any>): boolean {
function createTickDriver (line 58) | function createTickDriver(tickSignal: Signal<Tick>) {
function animatedSignal (line 119) | function animatedSignal<T>(initialValue: T, options: AnimateOptions<T> =...
function animatedSequence (line 259) | async function animatedSequence(sequence: ((() => Promise<void>) | (() =...
FILE: packages/core/src/engine/bootstrap.ts
type BootstrapOptions (line 28) | interface BootstrapOptions extends ApplicationOptions {
FILE: packages/core/src/engine/directive.ts
function registerDirective (line 12) | function registerDirective(name: string, directive: any) {
function applyDirective (line 16) | function applyDirective(element: Element<any>, directiveName: string) {
FILE: packages/core/src/engine/reactive.ts
type Props (line 25) | interface Props {
type NestedSignalObjects (line 29) | type NestedSignalObjects = {
type Element (line 33) | interface Element<T = ComponentInstance> {
type FlowResult (line 54) | type FlowResult = {
type FlowObservable (line 60) | type FlowObservable = Observable<FlowResult>;
constant DOM_ROUTING_MAP (line 84) | const DOM_ROUTING_MAP: Record<string, string> = {
constant DOM_ALLOWED_TAGS (line 88) | const DOM_ALLOWED_TAGS = new Set(["DOMContainer", "DOMElement", "DOMSpri...
constant DOM_UNSUPPORTED_TAGS (line 89) | const DOM_UNSUPPORTED_TAGS = new Set([
function registerComponent (line 150) | function registerComponent(name, component) {
function registerAllComponents (line 176) | function registerAllComponents() {
function checkDependencies (line 197) | async function checkDependencies(
function waitForDependencies (line 213) | function waitForDependencies(deps: any[]): Promise<void> {
function isElementFrozen (line 245) | function isElementFrozen(element: Element): boolean {
function handleAnimatedSignalsFreeze (line 277) | function handleAnimatedSignalsFreeze(element: Element, shouldPause: bool...
function destroyElement (line 297) | function destroyElement(element: Element | Element[]) {
function createComponent (line 345) | function createComponent(tag: string, props?: Props): Element {
function loop (line 774) | function loop<T>(
function cond (line 1022) | function cond(
FILE: packages/core/src/engine/signal.ts
type MountFunction (line 12) | type MountFunction = (fn: (element: Element) => void) => void;
type ComponentFunction (line 15) | type ComponentFunction<P = {}> = (props: P) => Element | Promise<Element>;
function mount (line 41) | function mount(fn: (element: Element) => void) {
function tick (line 55) | function tick(fn: (tickValue: Tick, element: Element) => void) {
function _h (line 102) | function _h<C extends ComponentFunction<any>>(
function h (line 201) | function h<C extends ComponentFunction<any>>(
FILE: packages/core/src/engine/trigger.ts
type Listen (line 3) | interface Listen<T = any> {
type Trigger (line 12) | interface Trigger<T = any> {
function isTrigger (line 22) | function isTrigger(arg: any): arg is Trigger<any> {
function trigger (line 41) | function trigger<T = any>(globalConfig?: T): Trigger<T> {
function on (line 88) | function on(triggerSignal: any, callback: (config: any) => void | Promis...
FILE: packages/core/src/engine/utils.ts
function isBrowser (line 9) | function isBrowser(): boolean {
function preciseNow (line 17) | function preciseNow(): number {
function fps2ms (line 26) | function fps2ms(fps: number): number {
function isPromise (line 35) | function isPromise(value: any): boolean {
function arrayEquals (line 39) | function arrayEquals(a: any[], b: any[]): boolean {
function objectEquals (line 58) | function objectEquals(a: object, b: object): boolean {
function deepEquals (line 69) | function deepEquals(a: any, b: any): boolean {
function isFunction (line 86) | function isFunction(val: unknown): boolean {
function isObject (line 102) | function isObject(val: unknown): boolean {
function isObservable (line 106) | function isObservable(val: unknown): boolean {
function set (line 118) | function set(
function get (line 157) | function get(obj: Record<string, any>, path: string): any {
function log (line 175) | function log(text: any): void {
function error (line 183) | function error(text: any): void {
function setObservablePoint (line 192) | function setObservablePoint(
function calculateDistance (line 218) | function calculateDistance(
FILE: packages/core/src/hooks/useFocus.ts
function useFocusIndex (line 22) | function useFocusIndex(containerId: string): Signal<number | null> | null {
function useFocusedElement (line 45) | function useFocusedElement(containerId: string): Signal<Element | null> ...
function useFocusChange (line 68) | function useFocusChange(
FILE: packages/core/src/hooks/useProps.ts
type PropType (line 34) | type PropType = NumberConstructor | StringConstructor | BooleanConstruct...
type PropConfig (line 38) | interface PropConfig {
type PropSchema (line 45) | type PropSchema = {
function validateType (line 132) | function validateType(key: string, value: any, types: any[]) {
FILE: packages/core/src/hooks/useRef.ts
function useRef (line 4) | function useRef(element: Element<ComponentInstance>, ref: string): Eleme...
FILE: packages/core/src/types/pixi-cull.d.ts
class Simple (line 2) | class Simple {
FILE: packages/core/src/utils/GlobalAssetLoader.ts
class GlobalAssetLoader (line 29) | class GlobalAssetLoader {
method registerAsset (line 47) | registerAsset(assetPath: string): string {
method updateProgress (line 66) | updateProgress(assetId: string, progress: number): void {
method completeAsset (line 87) | completeAsset(assetId: string): void {
method removeAsset (line 105) | removeAsset(assetId: string): void {
method onProgress (line 126) | onProgress(callback: (progress: number) => void): () => void {
method onComplete (line 157) | onComplete(callback: () => void): () => void {
method getGlobalProgress (line 175) | getGlobalProgress(): number {
method getAssetCount (line 193) | getAssetCount(): number {
method getCompletedCount (line 202) | getCompletedCount(): number {
method checkCompletion (line 213) | private checkCompletion(): void {
method updateGlobalProgress (line 233) | private updateGlobalProgress(): void {
method reset (line 249) | reset(): void {
FILE: packages/core/src/utils/RadialGradient.ts
class RadialGradient (line 11) | class RadialGradient {
method constructor (line 30) | constructor(
method addColorStop (line 64) | addColorStop(offset: number, color: string) {
method render (line 76) | render({ translate }: { translate?: { x: number; y: number } } = {}) {
FILE: packages/core/src/utils/functions.ts
function isPercent (line 1) | function isPercent(value?: string | number) {
FILE: packages/core/src/utils/tabindex.ts
type TabindexBoundaryMode (line 3) | type TabindexBoundaryMode = "wrap" | "clamp" | "none";
type TabindexBounds (line 5) | type TabindexBounds =
type TabindexNavigator (line 9) | type TabindexNavigator = {
function resolveBounds (line 14) | function resolveBounds(bounds: TabindexBounds): { min: number; max: numb...
function normalizeValue (line 29) | function normalizeValue(
function createTabindexNavigator (line 51) | function createTabindexNavigator(
FILE: packages/core/testing/index.ts
class TestBed (line 4) | class TestBed {
method createComponent (line 7) | static async createComponent(
FILE: packages/presets/src/Bar.ts
type BarProps (line 4) | interface BarProps {
function componentToHex (line 16) | function componentToHex(c) {
function rgbToHex (line 21) | function rgbToHex(r, g, b) {
function Bar (line 25) | function Bar(opts: BarProps) {
FILE: packages/presets/src/FogOfWar.ts
type FogColor (line 4) | type FogColor = [number, number, number, number];
type FogVisibilityState (line 5) | type FogVisibilityState = "visible" | "explored" | "unknown";
type VisionSource (line 7) | type VisionSource = {
type ResolvedVisionSource (line 14) | type ResolvedVisionSource = {
type FogOfWarColors (line 21) | type FogOfWarColors = {
type FogOfWarProps (line 26) | type FogOfWarProps = {
type FogSample (line 42) | type FogSample = {
type FogSampler (line 47) | type FogSampler = (x: number, y: number) => FogSample;
type FogControllerInternals (line 49) | type FogControllerInternals = {
type FogOfWarController (line 54) | type FogOfWarController = {
constant DEFAULT_UNKNOWN (line 62) | const DEFAULT_UNKNOWN: FogColor = [0, 0, 0, 1];
constant DEFAULT_EXPLORED (line 63) | const DEFAULT_EXPLORED: FogColor = [0, 0, 0, 0.55];
constant DEFAULT_SAMPLE (line 64) | const DEFAULT_SAMPLE: FogSample = { clarity: 0, explored: false };
function createFogOfWarController (line 82) | function createFogOfWarController(): FogOfWarController {
function FogOfWar (line 143) | function FogOfWar(options: FogOfWarProps) {
FILE: packages/presets/src/Footprints.ts
type ReactiveValue (line 10) | type ReactiveValue<T> = T | (() => T);
type PointLike (line 11) | type PointLike = { x: number; y: number };
type ColorInput (line 12) | type ColorInput = string | number;
type FootSide (line 13) | type FootSide = "left" | "right";
type ResolvedSurfaceProfile (line 15) | type ResolvedSurfaceProfile = {
type ResolvedCaster (line 29) | type ResolvedCaster = {
type ManagedCasterState (line 44) | type ManagedCasterState = {
type ManagedFootprint (line 53) | type ManagedFootprint = {
type FootprintSurfaceProfile (line 73) | type FootprintSurfaceProfile = {
type FootprintCasterOptions (line 87) | type FootprintCasterOptions = {
type FootprintsProps (line 103) | type FootprintsProps = {
constant FOOTPRINT_MANAGED_MARK (line 110) | const FOOTPRINT_MANAGED_MARK = "__footprintManaged";
constant DEFAULT_SURFACE (line 111) | const DEFAULT_SURFACE = "default";
constant DEFAULT_UPDATE_HZ (line 112) | const DEFAULT_UPDATE_HZ = 30;
constant DEFAULT_MAX_FOOTPRINTS (line 113) | const DEFAULT_MAX_FOOTPRINTS = 260;
constant DEFAULT_CASTER (line 115) | const DEFAULT_CASTER: ResolvedCaster = {
constant BASE_PROFILE (line 130) | const BASE_PROFILE: ResolvedSurfaceProfile = {
constant BUILTIN_PROFILES (line 144) | const BUILTIN_PROFILES: Record<string, ResolvedSurfaceProfile> = {
function Footprints (line 518) | function Footprints(options: FootprintsProps = {}) {
FILE: packages/presets/src/Loading.ts
type LoadingProps (line 7) | interface LoadingProps {
function Loading (line 97) | function Loading(opts: LoadingProps = {}) {
FILE: packages/presets/src/NightAmbiant.ts
constant MAX_SPOTS (line 6) | const MAX_SPOTS = 24;
constant SPOT_RADIUS_PX (line 7) | const SPOT_RADIUS_PX = 180;
constant DARKNESS (line 9) | const DARKNESS = 0.75;
constant FOG_RADIUS (line 11) | const FOG_RADIUS = 0.5;
constant FOG_SOFTNESS (line 13) | const FOG_SOFTNESS = 0.35;
constant FOG_COLOR (line 15) | const FOG_COLOR = new Float32Array([0.08, 0.08, 0.14]);
type PointLike (line 17) | type PointLike = { x: number; y: number };
type BoundsLike (line 18) | type BoundsLike = { x: number; y: number; width: number; height: number };
type ScreenPointLike (line 19) | type ScreenPointLike = { x: number; y: number };
type ReactiveValue (line 20) | type ReactiveValue<T> = T | (() => T);
type ViewportLike (line 21) | type ViewportLike = {
type NightSpot (line 25) | type NightSpot = PointLike & {
type NightSpotInput (line 35) | type NightSpotInput = {
type ColorInput (line 62) | type ColorInput = string | number | [number, number, number];
type NightAmbiantProps (line 64) | type NightAmbiantProps = {
type NightFilter (line 81) | type NightFilter = Filter & {
function createNightFilter (line 160) | function createNightFilter(
function NightAmbiant (line 389) | function NightAmbiant(options: NightAmbiantProps = {}) {
FILE: packages/presets/src/Particle.ts
function Particle (line 3) | function Particle(options) {
FILE: packages/presets/src/SpriteShadows.ts
type ReactiveValue (line 11) | type ReactiveValue<T> = T | (() => T);
type PointLike (line 12) | type PointLike = { x: number; y: number };
type ColorInput (line 13) | type ColorInput = string | number;
type ShadowLight (line 15) | type ShadowLight = {
type ShadowLightInput (line 25) | type ShadowLightInput = {
type ShadowCasterOptions (line 35) | type ShadowCasterOptions = {
type ShadowMode (line 51) | type ShadowMode = "strongest" | "blend2";
type SpriteShadowsProps (line 53) | type SpriteShadowsProps = {
type ResolvedCaster (line 61) | type ResolvedCaster = {
type ResolvedLight (line 76) | type ResolvedLight = {
type LightCandidate (line 87) | type LightCandidate = {
type GradientFilter (line 94) | type GradientFilter = Filter & {
type ManagedShadow (line 98) | type ManagedShadow = {
constant SHADOW_MANAGED_MARK (line 114) | const SHADOW_MANAGED_MARK = "__spriteShadowManaged";
constant DEFAULT_LIGHT_Z (line 115) | const DEFAULT_LIGHT_Z = 220;
constant DEFAULT_LIGHT_RADIUS (line 116) | const DEFAULT_LIGHT_RADIUS = 360;
constant DEFAULT_LIGHT_INTENSITY (line 117) | const DEFAULT_LIGHT_INTENSITY = 1;
constant DEFAULT_MODE (line 118) | const DEFAULT_MODE: ShadowMode = "strongest";
constant DEFAULT_UPDATE_HZ (line 119) | const DEFAULT_UPDATE_HZ = 30;
constant DEFAULT_SHADOW_COLOR (line 120) | const DEFAULT_SHADOW_COLOR = 0x000000;
constant DEFAULT_CASTER (line 121) | const DEFAULT_CASTER: ResolvedCaster = {
function SpriteShadows (line 700) | function SpriteShadows(options: SpriteShadowsProps = {}) {
FILE: packages/presets/src/Tilemap/Tile.ts
class Tile (line 6) | class Tile extends AnimatedSprite {
method getTextures (line 7) | static getTextures(tile: TiledTileClass, tileSet: TileSet) {
method constructor (line 27) | constructor(
method z (line 39) | get z() {
method gid (line 43) | get gid() {
method setAnimation (line 47) | setAnimation(frame: CompositeTilemap) {
method flip (line 55) | flip() {
FILE: packages/presets/src/Tilemap/TileGroup.ts
type TileOptions (line 1) | interface TileOptions {
type TilesGroupOptions (line 8) | interface TilesGroupOptions {
class TileInfo (line 18) | class TileInfo {
method constructor (line 24) | constructor(obj: TileOptions) {
method addFlag (line 29) | addFlag(key: string, value: any) {
class TilesGroup (line 34) | class TilesGroup {
method constructor (line 46) | constructor(tiles: TileOptions[], public tilesetIndex: number = 0, opt...
method getRect (line 66) | getRect(x: number, y: number): { minX: number, minY: number, maxX: num...
method tilesBase (line 76) | get tilesBase() {
method tilesBaseWidth (line 80) | get tilesBaseWidth(): number {
method tilesBaseHeight (line 84) | get tilesBaseHeight(): number {
method forEach (line 88) | forEach(cb: (tileInfo: TileInfo | null, x: number, y: number) => void) {
method find (line 96) | find(cb: (tileInfo: TileInfo | null, x: number, y: number) => boolean)...
method getOffsetY (line 105) | getOffsetY(): number {
method fillTiles (line 116) | fillTiles() {
method shiftToTopLeft (line 125) | shiftToTopLeft(): void {
method addTile (line 176) | addTile(x: number, y: number, tileOptions: TileOptions) {
method addTileFlag (line 181) | addTileFlag(x: number, y: number, key: string, value: any) {
method getTile (line 185) | getTile(x: number, y: number): TileInfo | null {
method getTilesByFlag (line 189) | getTilesByFlag(key: string, value: any): { tileInfo: TileInfo, x: numb...
method isTileBase (line 204) | isTileBase(tileInfo: TileInfo): boolean {
FILE: packages/presets/src/Tilemap/TileLayer.ts
class CanvasTileLayer (line 20) | class CanvasTileLayer extends DisplayObject(CompositeTilemap) {
method findTileSet (line 28) | static findTileSet(gid: number, tileSets: TileSet[]) {
method createTile (line 40) | createTile(x: number, y: number, options: any = {}): Tile | undefined {
method _addFrame (line 77) | private _addFrame(tile: Tile, x: number, y: number) {
method onMount (line 88) | async onMount(args) {
method onUpdate (line 111) | onUpdate(props) {
method onDestroy (line 133) | async onDestroy(parent: any) {
type CanvasTileLayer (line 140) | interface CanvasTileLayer extends CompositeTilemap {}
method findTileSet (line 28) | static findTileSet(gid: number, tileSets: TileSet[]) {
method createTile (line 40) | createTile(x: number, y: number, options: any = {}): Tile | undefined {
method _addFrame (line 77) | private _addFrame(tile: Tile, x: number, y: number) {
method onMount (line 88) | async onMount(args) {
method onUpdate (line 111) | onUpdate(props) {
method onDestroy (line 133) | async onDestroy(parent: any) {
function CompositeTileLayer (line 144) | function CompositeTileLayer(props) {
FILE: packages/presets/src/Tilemap/TileSet.ts
class TileSet (line 4) | class TileSet extends TiledTilesetClass {
method constructor (line 8) | constructor(tileSet: TiledTileset) {
method loadGroup (line 12) | loadGroup() {
method load (line 18) | async load(image: string) {
FILE: packages/presets/src/Tilemap/index.ts
function reorganizeLayersByTileZ (line 22) | function reorganizeLayersByTileZ(originalLayers: TiledLayer[], tilesets:...
function TiledMap (line 136) | function TiledMap(props) {
FILE: packages/presets/src/Weathers/fog.ts
function createFogShader (line 3) | function createFogShader(): GlProgram {
function createCloudShader (line 122) | function createCloudShader(): GlProgram {
FILE: packages/presets/src/Weathers/index.ts
constant RAIN_PRESETS (line 15) | const RAIN_PRESETS = {
constant SNOW_PRESETS (line 21) | const SNOW_PRESETS = {
constant FOG_PRESETS (line 27) | const FOG_PRESETS = {
constant CLOUD_PRESETS (line 35) | const CLOUD_PRESETS = {
constant WEATHER_PRESETS (line 46) | const WEATHER_PRESETS = {
FILE: packages/presets/src/Weathers/rain.ts
function createRainShader (line 3) | function createRainShader(): GlProgram {
FILE: packages/presets/src/Weathers/snow.ts
function createSnowShader (line 7) | function createSnowShader(): GlProgram {
FILE: packages/presets/src/fx/Fx.ts
function resolvePreset (line 9) | function resolvePreset(name, preset): FxPreset {
function Fx (line 20) | function Fx(options) {
FILE: packages/presets/src/fx/presets.ts
constant FX_PRESETS (line 3) | const FX_PRESETS = {
FILE: packages/presets/src/fx/runtime.ts
type RuntimeEmitter (line 21) | type RuntimeEmitter = FxEmitterConfig & {
type RuntimeInstance (line 28) | type RuntimeInstance = FxInstance & {
class FxRuntime (line 33) | class FxRuntime {
method constructor (line 40) | constructor(options: { seed?: number; maxParticles?: number } = {}) {
method preload (line 45) | async preload(preset: FxPreset) {
method spawn (line 49) | spawn(container: Container, preset: FxPreset, options: FxRuntimeOption...
method update (line 80) | update(deltaMs: number) {
method clear (line 87) | clear() {
method activeInstances (line 94) | get activeInstances() {
method updateInstance (line 98) | private updateInstance(instance: RuntimeInstance, deltaMs: number) {
method updateEmitter (line 127) | private updateEmitter(instance: RuntimeInstance, emitter: RuntimeEmitt...
method isEmitterActive (line 156) | private isEmitterActive(instance: RuntimeInstance, emitter: RuntimeEmi...
method spawnParticle (line 167) | private spawnParticle(instance: RuntimeInstance, emitter: RuntimeEmitt...
method updateParticle (line 232) | private updateParticle(particle: FxParticle, deltaMs: number) {
method recycleParticle (line 254) | private recycleParticle(container: Container, particle: FxParticle) {
method recycleInstance (line 264) | private recycleInstance(instance: RuntimeInstance) {
FILE: packages/presets/src/fx/textures.ts
function getShapeTexture (line 7) | function getShapeTexture(shape: FxShape = "softCircle"): Texture {
function createShapeTexture (line 14) | function createShapeTexture(shape: FxShape): Texture {
function drawStar (line 59) | function drawStar(
function preloadParticleTextures (line 80) | async function preloadParticleTextures(config: FxParticleConfig = {}) {
function preloadPresetTextures (line 97) | async function preloadPresetTextures(preset) {
function getParticleTextures (line 103) | function getParticleTextures(config: FxParticleConfig = {}, random = Mat...
FILE: packages/presets/src/fx/types.ts
type FxSignal (line 3) | type FxSignal<T> = T | (() => T);
type FxRange (line 4) | type FxRange = number | [number, number];
type FxColor (line 5) | type FxColor = string | number;
type FxColorRange (line 6) | type FxColorRange = FxColor | [FxColor, FxColor];
type FxShape (line 7) | type FxShape = "circle" | "softCircle" | "spark" | "square" | "star";
type FxFrameMode (line 8) | type FxFrameMode = "first" | "random" | "animated";
type FxBlendMode (line 9) | type FxBlendMode = "normal" | "add" | "multiply" | "screen" | string;
type FxParticleConfig (line 11) | interface FxParticleConfig {
type FxEmitterConfig (line 32) | interface FxEmitterConfig {
type FxPreset (line 52) | interface FxPreset {
type FxParticle (line 58) | interface FxParticle {
type FxInstance (line 79) | interface FxInstance {
type FxRuntimeOptions (line 89) | interface FxRuntimeOptions {
FILE: packages/presets/src/fx/utils.ts
function resolveValue (line 3) | function resolveValue<T>(value: T | (() => T)): T {
function createRandom (line 7) | function createRandom(seed = Date.now()) {
function rangeValue (line 15) | function rangeValue(value: FxRange | undefined, random: () => number, fa...
function normalizeAngle (line 23) | function normalizeAngle(value: FxRange | undefined, random: () => number...
function colorToNumber (line 27) | function colorToNumber(value: FxColor | undefined, fallback = 0xffffff):...
function lerp (line 37) | function lerp(start: number, end: number, progress: number): number {
function lerpColor (line 41) | function lerpColor(start: number, end: number, progress: number): number {
function easeValue (line 54) | function easeValue(name: string | undefined, progress: number): number {
FILE: packages/testing/src/helpers/createMockComponentInstance.ts
function createMockComponentInstance (line 19) | function createMockComponentInstance(componentType: string): ComponentIn...
FILE: packages/testing/src/helpers/createMockElement.ts
function createMockElement (line 30) | function createMockElement<T extends ComponentInstance = ComponentInstan...
FILE: packages/testing/src/helpers/spyOnElement.ts
function spyOnElement (line 29) | function spyOnElement<T extends ComponentInstance>(
function spyOnElementMultiple (line 58) | function spyOnElementMultiple<T extends ComponentInstance>(
FILE: packages/testing/src/mocks/pixi-base.ts
class MockEventEmitter (line 7) | class MockEventEmitter {
method on (line 10) | on(event: string, handler: Function) {
method off (line 18) | off(event: string, handler?: Function) {
method emit (line 32) | emit(event: string, data?: any) {
method once (line 39) | once(event: string, handler: Function) {
method removeAllListeners (line 47) | removeAllListeners(event?: string) {
class MockObservablePoint (line 61) | class MockObservablePoint {
method constructor (line 66) | constructor(x = 0, y = 0, cb?: (point: MockObservablePoint) => void) {
method set (line 72) | set(x: number, y: number) {
method copyFrom (line 79) | copyFrom(point: { x: number; y: number }) {
method copyTo (line 86) | copyTo(point: { x: number; y: number }) {
method equals (line 92) | equals(point: { x: number; y: number }) {
class MockRectangle (line 100) | class MockRectangle {
method constructor (line 106) | constructor(x = 0, y = 0, width = 0, height = 0) {
class MockTexture (line 126) | class MockTexture {
method constructor (line 133) | constructor(width = 100, height = 100) {
class MockContainer (line 153) | class MockContainer extends MockEventEmitter {
class MockGraphics (line 261) | class MockGraphics extends MockContainer {
class MockSprite (line 283) | class MockSprite extends MockContainer {
method constructor (line 287) | constructor(texture?: MockTexture) {
class MockText (line 305) | class MockText extends MockContainer {
method constructor (line 312) | constructor(text = '', style?: any) {
class MockMesh (line 325) | class MockMesh extends MockContainer {
method constructor (line 330) | constructor(geometry?: any, shader?: any, texture?: MockTexture) {
class MockTilingSprite (line 341) | class MockTilingSprite extends MockContainer {
method constructor (line 346) | constructor(texture: MockTexture, width = 100, height = 100) {
class MockNineSlicePlane (line 357) | class MockNineSlicePlane extends MockContainer {
method constructor (line 364) | constructor(texture: MockTexture, leftWidth = 10, topHeight = 10, righ...
class MockDOMElement (line 377) | class MockDOMElement extends MockContainer {
method constructor (line 380) | constructor(element?: HTMLElement | string) {
class MockDOMContainer (line 393) | class MockDOMContainer extends MockContainer {
method constructor (line 394) | constructor() {
class MockApplication (line 402) | class MockApplication extends MockEventEmitter {
method constructor (line 428) | constructor(options?: any) {
class MockVideoResource (line 456) | class MockVideoResource {
method constructor (line 462) | constructor(source?: HTMLVideoElement) {
FILE: packages/tiled/src/classes/Gid.ts
constant FLIPPED_HORIZONTALLY_FLAG (line 3) | const FLIPPED_HORIZONTALLY_FLAG = 0x80000000
constant FLIPPED_VERTICALLY_FLAG (line 4) | const FLIPPED_VERTICALLY_FLAG = 0x40000000
constant FLIPPED_DIAGONALLY_FLAG (line 5) | const FLIPPED_DIAGONALLY_FLAG = 0x20000000
constant ROTATED_HEXAGONAL_120_FLAG (line 6) | const ROTATED_HEXAGONAL_120_FLAG = 0x10000000
class TileGid (line 8) | class TileGid extends TiledProperties {
method constructor (line 11) | constructor(public obj?) {
method getRealGid (line 16) | static getRealGid(gid: number): number {
method horizontalFlip (line 23) | get horizontalFlip(): boolean {
method verticalFlip (line 27) | get verticalFlip(): boolean {
method diagonalFlip (line 31) | get diagonalFlip(): boolean {
method rotatedHex120 (line 35) | get rotatedHex120(): boolean {
method gid (line 39) | get gid(): number {
method gid (line 43) | set gid(val: number) {
FILE: packages/tiled/src/classes/Layer.ts
class Layer (line 8) | class Layer extends TiledProperties {
method size (line 13) | get size(): number {
method constructor (line 17) | constructor(layer: TiledLayer, private tilesets: Tileset[], private pa...
method createTile (line 27) | createTile(gid: number, tileIndex: number, layerIndex?: number): Tile ...
method mergePropertiesWithParent (line 52) | private mergePropertiesWithParent() {
method propertiesTiles (line 77) | private propertiesTiles() {
method mapObjects (line 86) | private mapObjects() {
method getTileByIndex (line 96) | getTileByIndex(tileIndex: number): Tile | undefined {
method findTileSet (line 103) | static findTileSet(gid: number, tileSets: Tileset[]): Tileset | undefi...
method getLayerParent (line 114) | getLayerParent(): Layer | undefined {
method tilesForEach (line 118) | tilesForEach(cb: (tile: Tile | undefined, index: number) => void) {
method setData (line 128) | setData(tileIndex: number, gid: number): void {
type Layer (line 133) | interface Layer extends TiledLayer {
method size (line 13) | get size(): number {
method constructor (line 17) | constructor(layer: TiledLayer, private tilesets: Tileset[], private pa...
method createTile (line 27) | createTile(gid: number, tileIndex: number, layerIndex?: number): Tile ...
method mergePropertiesWithParent (line 52) | private mergePropertiesWithParent() {
method propertiesTiles (line 77) | private propertiesTiles() {
method mapObjects (line 86) | private mapObjects() {
method getTileByIndex (line 96) | getTileByIndex(tileIndex: number): Tile | undefined {
method findTileSet (line 103) | static findTileSet(gid: number, tileSets: Tileset[]): Tileset | undefi...
method getLayerParent (line 114) | getLayerParent(): Layer | undefined {
method tilesForEach (line 118) | tilesForEach(cb: (tile: Tile | undefined, index: number) => void) {
method setData (line 128) | setData(tileIndex: number, gid: number): void {
FILE: packages/tiled/src/classes/Map.ts
type TileInfo (line 10) | interface TileInfo {
type GetTileOptions (line 19) | interface GetTileOptions {
class MapClass (line 26) | class MapClass extends TiledProperties {
method constructor (line 54) | constructor(map?: TiledMap) {
method load (line 59) | load(map: TiledMap) {
method widthPx (line 81) | get widthPx(): number {
method heightPx (line 92) | get heightPx(): number {
method zTileHeight (line 103) | get zTileHeight(): number {
method getLayerByName (line 121) | getLayerByName(name: string): TiledLayer | undefined {
method getTileIndex (line 136) | getTileIndex(x: number, y: number, [z] = [0]): number {
method getTilePosition (line 140) | getTilePosition(index: number): { x: number, y: number } {
method getTileOriginPosition (line 165) | getTileOriginPosition(x: number, y: number): {
method getTileByPosition (line 190) | getTileByPosition(x: number, y: number, z: [number, number] = [0, 0], ...
method getTileByIndex (line 211) | getTileByIndex(
method getAllObjects (line 257) | getAllObjects(): TiledObjectClass[] {
method getData (line 264) | getData() {
method setTile (line 271) | setTile(x: number, y: number, layerFilter: string | ((layer: any) => b...
method removeCacheTileset (line 317) | removeCacheTileset(name: string) {
method clearCacheTilesets (line 321) | clearCacheTilesets() {
method mapTilesets (line 325) | private mapTilesets() {
method mapLayers (line 338) | private mapLayers(layers: TiledLayer[] = [], parent?: Layer) {
method setTileIndex (line 350) | private setTileIndex(layer: Layer, oldTile: Tile, newTile: Tile, tileI...
method realAllocateMemory (line 398) | private get realAllocateMemory() {
method addTileIndex (line 406) | private addTileIndex(layer: Layer, tile: Tile | undefined, tileIndex: ...
method setTilesIndex (line 430) | private setTilesIndex() {
type MapClass (line 443) | interface MapClass extends TiledMap { }
method constructor (line 54) | constructor(map?: TiledMap) {
method load (line 59) | load(map: TiledMap) {
method widthPx (line 81) | get widthPx(): number {
method heightPx (line 92) | get heightPx(): number {
method zTileHeight (line 103) | get zTileHeight(): number {
method getLayerByName (line 121) | getLayerByName(name: string): TiledLayer | undefined {
method getTileIndex (line 136) | getTileIndex(x: number, y: number, [z] = [0]): number {
method getTilePosition (line 140) | getTilePosition(index: number): { x: number, y: number } {
method getTileOriginPosition (line 165) | getTileOriginPosition(x: number, y: number): {
method getTileByPosition (line 190) | getTileByPosition(x: number, y: number, z: [number, number] = [0, 0], ...
method getTileByIndex (line 211) | getTileByIndex(
method getAllObjects (line 257) | getAllObjects(): TiledObjectClass[] {
method getData (line 264) | getData() {
method setTile (line 271) | setTile(x: number, y: number, layerFilter: string | ((layer: any) => b...
method removeCacheTileset (line 317) | removeCacheTileset(name: string) {
method clearCacheTilesets (line 321) | clearCacheTilesets() {
method mapTilesets (line 325) | private mapTilesets() {
method mapLayers (line 338) | private mapLayers(layers: TiledLayer[] = [], parent?: Layer) {
method setTileIndex (line 350) | private setTileIndex(layer: Layer, oldTile: Tile, newTile: Tile, tileI...
method realAllocateMemory (line 398) | private get realAllocateMemory() {
method addTileIndex (line 406) | private addTileIndex(layer: Layer, tile: Tile | undefined, tileIndex: ...
method setTilesIndex (line 430) | private setTilesIndex() {
FILE: packages/tiled/src/classes/Object.ts
class TiledObjectClass (line 4) | class TiledObjectClass extends TileGid {
method constructor (line 7) | constructor(object?: TiledObject) {
type TiledObjectClass (line 16) | interface TiledObjectClass extends TiledObject {}
method constructor (line 7) | constructor(object?: TiledObject) {
FILE: packages/tiled/src/classes/Properties.ts
class TiledProperties (line 1) | class TiledProperties {
method constructor (line 7) | constructor(data?: any) {
method getProperty (line 11) | getProperty<P, D = undefined>(name: string, defaultValue?: D): P | D {
method hasProperty (line 19) | hasProperty(name: string): boolean {
method setProperty (line 23) | setProperty<T>(name: string, value: T) {
method getType (line 27) | getType(): string {
FILE: packages/tiled/src/classes/Tile.ts
type TileInfo (line 4) | type TileInfo = TilesetTile & { gid?: number, index: number, layerIndex?...
class Tile (line 6) | class Tile extends TileGid {
method constructor (line 9) | constructor(public tile: TileInfo | { gid: number }) {
type Tile (line 22) | interface Tile extends TileInfo {}
method constructor (line 9) | constructor(public tile: TileInfo | { gid: number }) {
FILE: packages/tiled/src/classes/Tileset.ts
class Tileset (line 6) | class Tileset extends TiledProperties {
method constructor (line 9) | constructor(private tileset: TiledTileset) {
method addTile (line 23) | addTile(tileObj: TilesetTile): Tile {
method getTile (line 29) | getTile(id: number): Tile | undefined {
type Tileset (line 34) | interface Tileset extends TiledTileset {}
method constructor (line 9) | constructor(private tileset: TiledTileset) {
method addTile (line 23) | addTile(tileObj: TilesetTile): Tile {
method getTile (line 29) | getTile(id: number): Tile | undefined {
FILE: packages/tiled/src/generate/tileset.ts
class Tileset (line 4) | class Tileset {
method constructor (line 5) | constructor(protected nbTilesWidth: number, protected nbTilesHeight: n...
method generate (line 7) | generate(attr: {
method createTile (line 21) | createTile(id: number, properties): builder.XMLElement {
FILE: packages/tiled/src/generate/wangtile.ts
class Autotile (line 4) | class Autotile extends Tileset {
method constructor (line 8) | constructor(private nbGroupTilesWidth: number, private nbGroupTilesHei...
method getWangTiles (line 14) | static getWangTiles(id: number): [number, number, number, number, numb...
method getRandomColor (line 65) | static getRandomColor() {
method hasAnimation (line 74) | get hasAnimation(): boolean {
method getIndex (line 78) | getIndex(x: number, y: number): number {
method generate (line 82) | generate(attr: {
method generateAnimationTile (line 112) | generateAnimationTile(tileId: number): builder.XMLElement {
method generateWangTiles (line 120) | generateWangTiles(tileId: number = 0, name = 'Autotile'): builder.XMLE...
FILE: packages/tiled/src/parser/open-file.ts
type ParseOptions (line 6) | type ParseOptions = { getOnlyBasename?: boolean }
class TiledParserFile (line 9) | class TiledParserFile {
method constructor (line 12) | constructor(private file: string, {
method isBrowser (line 20) | static isBrowser() {
method typeOfFile (line 25) | static typeOfFile(file: string): {
method _parseFile (line 44) | private _parseFile<T>(file: string, type: string, cb: Function) {
method parseFile (line 92) | parseFile(cb: Function, options: ParseOptions = {}) {
method parseFilePromise (line 147) | parseFilePromise(options: ParseOptions = {}): Promise<TiledMap> {
FILE: packages/tiled/src/parser/parser.ts
class TiledParser (line 8) | class TiledParser {
method constructor (line 11) | constructor(private xml: string, private filePath: string = '', privat...
method toArray (line 32) | static toArray<T>(prop): T[] {
method getImagePath (line 38) | getImagePath(image: string) {
method isTilesetSource (line 47) | private isTilesetSource(obj: any): boolean {
method unpackTileBytes (line 189) | static unpackTileBytes(buffer: Buffer, size: number): number[] | never {
method decode (line 204) | static decode(obj: { encoding: string, data: string }, size: number) {
method parseMap (line 215) | parseMap(): TiledMap {
method parseTileset (line 288) | parseTileset(): TiledTileset {
FILE: packages/tiled/src/types/Layer.ts
type TiledLayerType (line 4) | enum TiledLayerType {
type TiledLayer (line 11) | interface TiledLayer {
FILE: packages/tiled/src/types/Map.ts
type TiledMap (line 5) | interface TiledMap {
FILE: packages/tiled/src/types/Objects.ts
type TiledObject (line 4) | interface TiledObject {
FILE: packages/tiled/src/types/Text.ts
type TiledText (line 1) | interface TiledText {
FILE: packages/tiled/src/types/Tile.ts
type TilesetTile (line 5) | interface TilesetTile {
FILE: packages/tiled/src/types/Tileset.ts
type TiledTileset (line 4) | interface TiledTileset {
FILE: packages/tiled/src/types/Types.ts
type TiledProperty (line 1) | interface TiledProperty<T = unknown> {
type TiledEncoding (line 16) | type TiledEncoding = 'csv' | 'base64';
type TiledCompression (line 17) | type TiledCompression = 'zlib' | 'gzip' | 'zstd';
type TiledChunk (line 19) | interface TiledChunk {
type TiledTileOffset (line 42) | interface TiledTileOffset {
type TiledWangSet (line 53) | interface TiledWangSet {
type TiledWangTile (line 71) | interface TiledWangTile {
type TiledWangColor (line 94) | interface TiledWangColor {
type TiledGrid (line 113) | interface TiledGrid {
type TiledFrame (line 128) | interface TiledFrame {
type TiledMapTerrain (line 138) | interface TiledMapTerrain {
type TiledPoint (line 147) | interface TiledPoint {
type TiledImage (line 152) | interface TiledImage {
FILE: packages/tiled/src/types/WorldMaps.ts
type TiledWorldMap (line 1) | interface TiledWorldMap {
type TiledWorld (line 9) | interface TiledWorld {
FILE: packages/tiled/src/utils.ts
function isTiledFormat (line 1) | function isTiledFormat(val: any): boolean {
function joinPath (line 12) | function joinPath(...segments: string[]): string {
function getBaseName (line 20) | function getBaseName(path: string): string {
FILE: packages/tiled/tests/class.spec.ts
function setLayer (line 9) | function setLayer(xml) {
FILE: packages/tiled/tests/tile-properties.spec.ts
function getMap (line 47) | function getMap(xml: string): MapClass {
FILE: packages/tiled/tests/tiledmap-multi-layers.spec.ts
function getMap (line 35) | function getMap(xml) {
FILE: packages/tiled/tests/tiledmap.spec.ts
function getMap (line 7) | function getMap(xml) {
FILE: tests/components/component.spec.ts
function MyComponent (line 6) | function MyComponent() {
class TestComponent (line 17) | class TestComponent {
method onInit (line 18) | onInit(props) {}
method onUpdate (line 19) | onUpdate(props) {}
method onMount (line 20) | onMount(element, index) {}
FILE: tests/components/displayobject.spec.ts
class MockContainer (line 6) | class MockContainer {
method addChild (line 28) | addChild() {}
method addChildAt (line 29) | addChildAt() {}
method destroy (line 30) | destroy() {
method on (line 35) | on(event: string, handler: Function) {
method off (line 42) | off(event: string, handler?: Function) {
method emit (line 55) | emit(event: string, data?: any) {
FILE: tests/components/graphics.spec.ts
function findPrototypeWithMethod (line 7) | function findPrototypeWithMethod(instance: object, method: string) {
FILE: tests/components/sprite.spec.ts
function createAnimatedHitboxSprite (line 8) | function createAnimatedHitboxSprite(hitbox: { w: number; h: number; anch...
FILE: tests/directives/drag.spec.ts
function findPrototypeWithMethod (line 6) | function findPrototypeWithMethod(instance: object, method: string) {
FILE: tests/engine/cond-loop.spec.ts
class ChildComponent (line 56) | class ChildComponent {
method onInit (line 60) | onInit(props) {
FILE: tests/engine/dependencies.spec.ts
function MyComponent (line 11) | function MyComponent() {
function MyComponent (line 30) | function MyComponent() {
function MyComponent (line 49) | function MyComponent() {
function MyComponent (line 73) | function MyComponent() {
function MyComponent (line 92) | function MyComponent() {
function MyComponent (line 109) | function MyComponent() {
function MyComponent (line 130) | function MyComponent() {
function MyComponent (line 153) | function MyComponent() {
FILE: tests/engine/freeze.spec.ts
class TestComponent (line 15) | class TestComponent {
method onInit (line 17) | onInit(props) {}
method onUpdate (line 18) | onUpdate(props) {
method onMount (line 21) | onMount(element, index) {}
class TestComponentWithUpdate (line 74) | class TestComponentWithUpdate {
method onInit (line 76) | onInit(props) {}
method onUpdate (line 77) | onUpdate(props) {
method onInit (line 100) | onInit(props) {}
method onUpdate (line 101) | onUpdate(props) {
method onInit (line 124) | onInit(props) {}
method onUpdate (line 125) | onUpdate(props) {
method onInit (line 155) | onInit(props) {}
method onUpdate (line 156) | onUpdate(props) {
class TestComponentWithUpdate (line 98) | class TestComponentWithUpdate {
method onInit (line 76) | onInit(props) {}
method onUpdate (line 77) | onUpdate(props) {
method onInit (line 100) | onInit(props) {}
method onUpdate (line 101) | onUpdate(props) {
method onInit (line 124) | onInit(props) {}
method onUpdate (line 125) | onUpdate(props) {
method onInit (line 155) | onInit(props) {}
method onUpdate (line 156) | onUpdate(props) {
class TestComponentWithUpdate (line 122) | class TestComponentWithUpdate {
method onInit (line 76) | onInit(props) {}
method onUpdate (line 77) | onUpdate(props) {
method onInit (line 100) | onInit(props) {}
method onUpdate (line 101) | onUpdate(props) {
method onInit (line 124) | onInit(props) {}
method onUpdate (line 125) | onUpdate(props) {
method onInit (line 155) | onInit(props) {}
method onUpdate (line 156) | onUpdate(props) {
class TestComponentWithUpdate (line 153) | class TestComponentWithUpdate {
method onInit (line 76) | onInit(props) {}
method onUpdate (line 77) | onUpdate(props) {
method onInit (line 100) | onInit(props) {}
method onUpdate (line 101) | onUpdate(props) {
method onInit (line 124) | onInit(props) {}
method onUpdate (line 125) | onUpdate(props) {
method onInit (line 155) | onInit(props) {}
method onUpdate (line 156) | onUpdate(props) {
FILE: tests/engine/lifecycle.spec.ts
function MyComponent (line 10) | function MyComponent() {
function MyComponent (line 32) | function MyComponent() {
function MyComponent (line 55) | function MyComponent() {
function MyComponent (line 84) | function MyComponent() {
function MyComponent (line 110) | function MyComponent() {
function MyComponent (line 136) | function MyComponent() {
function MyComponent (line 171) | function MyComponent() {
FILE: tests/engine/loop.spec.ts
function Item (line 51) | function Item(props: { value: number }) {
FILE: tests/setup/canvas.ts
constant LOAD_FAILURE_SRC (line 3) | const LOAD_FAILURE_SRC = 'LOAD_FAILURE_SRC';
method set (line 7) | set(src) {
method get (line 18) | get() {
method get (line 26) | get() {
Condensed preview — 320 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,039K chars).
[
{
"path": ".agents/skills/canvasengine/SKILL.md",
"chars": 3231,
"preview": "---\nname: canvasengine\ndescription: Work on CanvasEngine projects and .ce components using the official documentation. U"
},
{
"path": ".agents/skills/canvasengine/agents/openai.yaml",
"chars": 223,
"preview": "interface:\n display_name: \"CanvasEngine\"\n short_description: \"Guide projets CanvasEngine et fichiers .ce\"\n default_pr"
},
{
"path": ".agents/skills/pixijs/SKILL.md",
"chars": 6280,
"preview": "---\nname: pixijs\ndescription: \"Use this skill first for ANY PixiJS v8 task; it routes to the right specialized skill for"
},
{
"path": ".agents/skills/pixijs/references/index.md",
"chars": 11113,
"preview": "# PixiJS Skills: Full Index\n\nDetailed routing table for the PixiJS v8 skill collection. Each entry lists the skill's ful"
},
{
"path": ".changeset/config.json",
"chars": 292,
"preview": "{\n \"$schema\": \"https://unpkg.com/@changesets/config@3.0.1/schema.json\",\n \"changelog\": \"@changesets/cli/changelog\","
},
{
"path": ".codex",
"chars": 0,
"preview": ""
},
{
"path": ".github/workflows/ci.yml",
"chars": 2294,
"preview": "name: CI\n\non:\n push:\n branches:\n - \"*\"\n paths-ignore:\n - \"docs/**\"\n pull_request:\n branches:\n "
},
{
"path": ".gitignore",
"chars": 319,
"preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndis"
},
{
"path": ".npmignore",
"chars": 104,
"preview": "tests\ntemplate\nsample\npublic\ndocs\n.vitepress\nnode_modules\nstarter\n.github\n.cursorrules\nlogo.png\ncompiler"
},
{
"path": "README.md",
"chars": 1929,
"preview": "# CanvasEngine - A reactive HTML5 Canvas management library built on top of PixiJS and Vite\n\n` function allows you to conditionally render elements based on reactive signals or "
},
{
"path": "docs/advanced/performance.md",
"chars": 7347,
"preview": "# Performance Optimization Guide\n\nWhen building games or visualizations with many elements (1000+), performance becomes "
},
{
"path": "docs/advanced/without-compiler.md",
"chars": 8079,
"preview": "# Using CanvasEngine Without the Compiler\n\nCanvasEngine provides a powerful template syntax that gets compiled to JavaSc"
},
{
"path": "docs/api/context.md",
"chars": 5515,
"preview": "# Context\n\nThe context is an object that is automatically provided to all components in the component tree. It contains "
},
{
"path": "docs/api/element.md",
"chars": 6222,
"preview": "# Element Interface\n\nThe Element interface represents a component in the framework. It contains all the necessary proper"
},
{
"path": "docs/api/testing.md",
"chars": 13624,
"preview": "# Testing Package\n\nThe `@canvasengine/testing` package provides comprehensive testing utilities and mocks for CanvasEngi"
},
{
"path": "docs/components/_display-object.md",
"chars": 9291,
"preview": "## Common Properties\n\n| Property | Type | Description "
},
{
"path": "docs/components/button.md",
"chars": 8561,
"preview": "# Button\n\nThe Button component provides an interactive button with visual feedback for different states (normal, hover, "
},
{
"path": "docs/components/canvas.md",
"chars": 291,
"preview": "# Use Canvas component\n\nIt's the starting point for all the other components.\n\nCommon example:\n\n```html\n<Canvas antialia"
},
{
"path": "docs/components/container.md",
"chars": 349,
"preview": "# Use Container component\n\nCommon example:\n\n```html\n<Container />\n```\n\nExample with x and y:\n\n```html\n<script>\nimport { "
},
{
"path": "docs/components/dom-container.md",
"chars": 7024,
"preview": "# DOMContainer Component\n\nThe `DOMContainer` component provides a bridge between the canvas rendering system and traditi"
},
{
"path": "docs/components/examples/focus-navigation-dom.js",
"chars": 1878,
"preview": "export default {\n title: \"Focus Navigation with DOM\",\n description: \"Navigate through a scrollable list of DOM buttons"
},
{
"path": "docs/components/examples/polygon-example.js",
"chars": 2780,
"preview": "export default {\n title: \"Polygon Example\",\n description: \"Multiple polygons with different shapes, colors and styles "
},
{
"path": "docs/components/graphic.md",
"chars": 1279,
"preview": "# Use Graphics component\n\n<script setup>\nimport polygonExample from './examples/polygon-example.js'\n</script>\n\nCommon ex"
},
{
"path": "docs/components/joystick.md",
"chars": 4355,
"preview": "# Joystick\n\n<!-- @include: ./_before.md -->\n\n## Usage\n\n### Basic Usage\n\n```html\n<Canvas>\n <Joystick />\n</Canvas>\n```\n"
},
{
"path": "docs/components/mesh.md",
"chars": 8304,
"preview": "# Mesh\n\nThe `Mesh` component allows you to render custom 3D meshes with shaders and textures in your canvas application."
},
{
"path": "docs/components/navigation.md",
"chars": 14440,
"preview": "# Navigation Component\n\n::: Warning Experimental\nThis component is experimental and may change in the future.\n:::\n\n<scri"
},
{
"path": "docs/components/nine-slice-sprite.md",
"chars": 1069,
"preview": "# Use NineSliceSprite component\n\n```html\n<NineSliceSprite\n image=\"button.png\"\n width={200}\n height={100}\n leftWidth="
},
{
"path": "docs/components/sprite.md",
"chars": 11001,
"preview": "# Sprite Component\n\n## Simple Image:\n\n```html\n<Sprite \n image=\"path/to/image.png\"\n/>\n```\n\n## Part of an image:\n\n```ht"
},
{
"path": "docs/components/svg.md",
"chars": 2250,
"preview": "# SVG Component\n\nThe SVG component allows you to render SVG graphics in your CanvasEngine applications. There are three "
},
{
"path": "docs/components/text.md",
"chars": 1997,
"preview": "# Use Text component\n\nCommon example:\n\n```html\n<Text text=\"Hello World\" size=\"20\" />\n```\n\n## Properties\n\nYou can use all"
},
{
"path": "docs/components/tiling-sprite.md",
"chars": 1002,
"preview": "# Use TilingSprite component\n\nThe TilingSprite component allows you to create a repeating sprite pattern that can be sca"
},
{
"path": "docs/components/video.md",
"chars": 395,
"preview": "# Use Video component\n\n```html\n<Video src=\"myvideo.mp4\" play={true} />\n```\n\n## Props\n\n- `src`: string\n- `paused`: boolea"
},
{
"path": "docs/components/viewport.md",
"chars": 6716,
"preview": "# Use Viewport component\n\nCommon example:\n\n```html\n<Viewport worldWidth=\"2000\" worldHeight=\"2000\" clamp={ {direction: 'a"
},
{
"path": "docs/concepts/animation.md",
"chars": 9193,
"preview": "# Animation\n\nCanvasEngine provides powerful tools for creating animations. At the core of the animation system are `anim"
},
{
"path": "docs/concepts/child-component.md",
"chars": 1124,
"preview": "# Child Component\n\nA child component is a component that is defined inside another component.\n\n`child.ce`\n\n```html\n<Cont"
},
{
"path": "docs/concepts/context.md",
"chars": 0,
"preview": ""
},
{
"path": "docs/concepts/dependencies.md",
"chars": 5028,
"preview": "# Dependencies\n\nThe `dependencies` prop allows you to delay component mounting until all specified dependencies are read"
},
{
"path": "docs/concepts/dynamic-components.md",
"chars": 503,
"preview": "# Dynamic Components\n\nCanvasEngine allows you to create and use dynamic components that can be defined directly in your "
},
{
"path": "docs/concepts/examples/conditional-rendering.js",
"chars": 1745,
"preview": "export default {\n title: \"Grade System with @if/@else if/@else\",\n description: \"Click the buttons to change the score "
},
{
"path": "docs/concepts/lifecycle.md",
"chars": 1189,
"preview": "# Lifecycle\n\nLifecycle is a core concept in Canvas Engine. It is used to manage the lifecycle of a component.\n\n## Mounti"
},
{
"path": "docs/concepts/reactive.md",
"chars": 2713,
"preview": "# Reactive Programming\n\nCanvasEngine uses a reactive programming model to manage state and animations. This is a declara"
},
{
"path": "docs/concepts/ref.md",
"chars": 0,
"preview": ""
},
{
"path": "docs/concepts/slot.md",
"chars": 1513,
"preview": "# Children Components Slots\n\nIn CanvasEngine, you can access child components that are passed to a parent component.\n\n``"
},
{
"path": "docs/concepts/styling.md",
"chars": 3616,
"preview": "# Styling\n\nCanvasEngine allows you to add CSS styles to your components using the `<style>` tag. Styles are automaticall"
},
{
"path": "docs/concepts/template-syntax.md",
"chars": 5125,
"preview": "# Template Syntax\n\n<script setup>\nimport conditionalRenderingExample from './examples/conditional-rendering.js'\n</script"
},
{
"path": "docs/concepts/trigger.md",
"chars": 2294,
"preview": "# Trigger\n\nTrigger is a type of signal that can be used to pass data to a component. They are created using the `trigger"
},
{
"path": "docs/directives/controls.md",
"chars": 8446,
"preview": "# Use Controls directive\n\nThe controls directive allows you to control the movement and actions of a display object usin"
},
{
"path": "docs/directives/drag.md",
"chars": 4228,
"preview": "# Using the Drag Directive\n\nThe Drag directive allows you to make display objects draggable with the mouse or touch. Thi"
},
{
"path": "docs/directives/flash.md",
"chars": 9506,
"preview": "# Using the Flash Directive\n\nThe Flash directive allows you to create a flash animation effect on display objects when a"
},
{
"path": "docs/directives/shake.md",
"chars": 7280,
"preview": "# Using the Shake Directive\n\nThe Shake directive allows you to create a shake animation effect on display objects when a"
},
{
"path": "docs/directives/sound.md",
"chars": 3200,
"preview": "# Sound Directive\n\nThe Sound directive allows you to add audio capabilities to your game elements in CanvasEngine. It in"
},
{
"path": "docs/get_started/installation.md",
"chars": 473,
"preview": "# Installation\n\n## From template\n\n```bash\nnpx degit RSamaium/CanvasEngine/starter my-project\ncd my-project\nnpm install\nn"
},
{
"path": "docs/get_started/readme.md",
"chars": 713,
"preview": "# Get Started\n\nCanvas Engine is a framework for creating 2D games using a reactive programming model. It is built on top"
},
{
"path": "docs/get_started/start.md",
"chars": 745,
"preview": "# Start with CanvasEngine\n\n## 1. Add div with id `root` to your HTML\n\nIn your `index.html` file, add a `div` with the id"
},
{
"path": "docs/index.md",
"chars": 531,
"preview": "# Canvas Engine\n\nCanvasEngine is a reactive HTML5 Canvas management library built on top of PixiJS and Vite. It provides"
},
{
"path": "docs/package.json",
"chars": 816,
"preview": "{\n \"name\": \"docs\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"dev\": \"vitepres"
},
{
"path": "docs/presets/_before.md",
"chars": 164,
"preview": "## Install\n\nPlease install the `@canvasengine/presets` package first.\n\n```bash\nnpm install @canvasengine/presets\n```\n\nTh"
},
{
"path": "docs/presets/bar.md",
"chars": 771,
"preview": "# Bar\n\n<!-- @include: ./_before.md -->\n\n## Usage\n\n```html\n<Canvas>\n <Bar width={200} height={20} value={50} maxValue="
},
{
"path": "docs/presets/fog-of-war.md",
"chars": 4663,
"preview": "# Fog Of War\n\n<!-- @include: ./_before.md -->\n\n## Overview\n\n`FogOfWar` provides an Age-of-Empires style fog system with "
},
{
"path": "docs/presets/footprints.md",
"chars": 5755,
"preview": "# Footprints\n\n<!-- @include: ./_before.md -->\n\n## Overview\n\n`Footprints` adds fading footprints behind moving sprites.\n\n"
},
{
"path": "docs/presets/fx.md",
"chars": 11535,
"preview": "# Fx\n\n<!-- @include: ./_before.md -->\n\n## Overview\n\n`Fx` is a small particle effect preset system built into `@canvaseng"
},
{
"path": "docs/presets/loading.md",
"chars": 6851,
"preview": "# Loading\n\n<!-- @include: ./_before.md -->\n\n## Overview\n\nThe Loading component creates an animated circular loading spin"
},
{
"path": "docs/presets/night-ambiant.md",
"chars": 3791,
"preview": "# NightAmbiant\n\n<!-- @include: ./_before.md -->\n\n## Overview\n\n`NightAmbiant` adds a night overlay with dynamic light spo"
},
{
"path": "docs/presets/sprite-shadows.md",
"chars": 4563,
"preview": "# SpriteShadows\n\n<!-- @include: ./_before.md -->\n\n## Overview\n\n`SpriteShadows` adds RPG-style projected shadows for spri"
},
{
"path": "docs/presets/tilemap.md",
"chars": 4021,
"preview": "# TileMap\n\n<!-- @include: ./_before.md -->\n\n## Overview\n\nThe TileMap component integrates with **Tiled Map Editor** to r"
},
{
"path": "docs/presets/weather.md",
"chars": 5224,
"preview": "# Weather\n\n<!-- @include: ./_before.md -->\n\n## Overview\n\n`Weather` is a shader-based overlay preset for:\n\n- rain\n- snow\n"
},
{
"path": "docs/public/[A]Dirt_pipo.tsx",
"chars": 18672,
"preview": "<?xml version=\"1.0\"?>\n<tileset version=\"1.8\" tiledversion=\"1.8.2\" name=\"[A]Dirt_pipo\" tilewidth=\"32\" tileheight=\"32\" til"
},
{
"path": "docs/public/[A]Flower_pipo.tsx",
"chars": 5488,
"preview": "<?xml version=\"1.0\"?>\n<tileset version=\"1.8\" tiledversion=\"1.8.2\" name=\"[A]Flower_pipo\" tilewidth=\"32\" tileheight=\"32\" t"
},
{
"path": "docs/public/[A]Grass_pipo.tsx",
"chars": 29602,
"preview": "<?xml version=\"1.0\"?>\n<tileset version=\"1.8\" tiledversion=\"1.8.2\" name=\"[A]Grass_pipo\" tilewidth=\"32\" tileheight=\"32\" ti"
},
{
"path": "docs/public/[A]Wall-Up_pipo.tsx",
"chars": 5490,
"preview": "<?xml version=\"1.0\"?>\n<tileset version=\"1.8\" tiledversion=\"1.8.2\" name=\"[A]Wall-Up_pipo\" tilewidth=\"32\" tileheight=\"32\" "
},
{
"path": "docs/public/[A]WaterFall_pipo.tsx",
"chars": 108351,
"preview": "<?xml version=\"1.0\"?>\n<tileset version=\"1.8\" tiledversion=\"1.8.2\" name=\"[A]WaterFall_pipo\" tilewidth=\"32\" tileheight=\"32"
},
{
"path": "docs/public/[A]Water_pipo.tsx",
"chars": 508743,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<tileset version=\"1.8\" tiledversion=\"1.8.2\" name=\"[A]Water_pipo\" tilewidth=\"32\" t"
},
{
"path": "docs/public/[Base]BaseChip_pipo.tsx",
"chars": 126361,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<tileset version=\"1.8\" tiledversion=\"1.8.2\" name=\"[Base]BaseChip_pipo\" tilewidth="
},
{
"path": "docs/public/grammar.pegjs",
"chars": 31357,
"preview": "{\n function generateError(message, location) {\n const { start, end } = location;\n const errorMessage = `${message"
},
{
"path": "docs/public/map.tmx",
"chars": 17852,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<map version=\"1.9\" tiledversion=\"1.9.2\" orientation=\"orthogonal\" renderorder=\"rig"
},
{
"path": "docs/public/simplemap.tmx",
"chars": 9051,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<map version=\"1.9\" tiledversion=\"1.9.2\" orientation=\"orthogonal\" renderorder=\"rig"
},
{
"path": "docs/public/simplemap2.tmx",
"chars": 7198,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<map version=\"1.8\" tiledversion=\"1.8.2\" orientation=\"orthogonal\" renderorder=\"rig"
},
{
"path": "docs/public/tileset.tsx",
"chars": 126480,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<tileset version=\"1.9\" tiledversion=\"1.9.2\" name=\"[Base]BaseChip_pipo\" tilewidth="
},
{
"path": "package.json",
"chars": 1506,
"preview": "{\n \"name\": \"canvasengine-project\",\n \"description\": \"A reactive HTML5 Canvas management library built on top of PixiJS\""
},
{
"path": "packages/compiler/grammar.pegjs",
"chars": 40001,
"preview": "{\n function generateError(message, location) {\n const { start, end } = location;\n const errorMessage = `${message"
},
{
"path": "packages/compiler/grammar2.pegjs",
"chars": 31357,
"preview": "{\n function generateError(message, location) {\n const { start, end } = location;\n const errorMessage = `${message"
},
{
"path": "packages/compiler/index.ts",
"chars": 18424,
"preview": "import { createFilter } from \"vite\";\nimport { parse } from \"acorn\";\nimport fs from \"fs\";\nimport pkg from \"peggy\";\nimport"
},
{
"path": "packages/compiler/package.json",
"chars": 632,
"preview": "{\n \"name\": \"@canvasengine/compiler\",\n \"version\": \"2.0.0-beta.58\",\n \"description\": \"\",\n \"main\": \"dist/index.js\",\n \"t"
},
{
"path": "packages/compiler/tests/compiler.spec.ts",
"chars": 56149,
"preview": "import pkg from \"peggy\";\nimport fs from \"fs\";\nimport { beforeAll, describe, test, expect } from \"vitest\";\n\nconst { gener"
},
{
"path": "packages/compiler/tests/compiler2.spec.ts",
"chars": 61760,
"preview": "import pkg from \"peggy\";\nimport fs from \"fs\";\nimport { beforeAll, describe, test, expect } from \"vitest\";\n\nconst { gener"
},
{
"path": "packages/compiler/tsconfig.json",
"chars": 776,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ES2020\",\n \"outDir\": \"./dist\",\n \"rootDir\""
},
{
"path": "packages/compiler/tsup.config.ts",
"chars": 733,
"preview": "import { defineConfig } from 'tsup'\nimport { copyFileSync } from 'fs'\nimport { join } from 'path'\n\nexport default defin"
},
{
"path": "packages/core/index.d.ts",
"chars": 114,
"preview": "declare module '*.ce' {\n const content: import(\"./dist/index\").ComponentFunction;\n export default content;\n}"
},
{
"path": "packages/core/package.json",
"chars": 1104,
"preview": "{\n \"name\": \"canvasengine\",\n \"version\": \"2.0.0-beta.58\",\n \"type\": \"module\",\n \"main\": \"dist/index.js\",\n \"types\": \"dis"
},
{
"path": "packages/core/src/components/Button.ts",
"chars": 11144,
"preview": "import { effect, signal, computed, isSignal, Signal } from \"@signe/reactive\";\nimport { FederatedPointerEvent } from \"pix"
},
{
"path": "packages/core/src/components/Canvas.ts",
"chars": 3937,
"preview": "import { effect, Signal, signal } from \"@signe/reactive\";\nimport { Application, Container } from \"pixi.js\";\nimport {\n P"
},
{
"path": "packages/core/src/components/Container.ts",
"chars": 2196,
"preview": "import { Container as PixiContainer } from \"pixi.js\";\nimport { createComponent, registerComponent } from \"../engine/reac"
},
{
"path": "packages/core/src/components/DOMContainer.ts",
"chars": 11319,
"preview": "import { DOMContainer as PixiDOMContainer } from \"pixi.js\";\nimport { effect } from \"@signe/reactive\";\nimport {\n createC"
},
{
"path": "packages/core/src/components/DOMElement.ts",
"chars": 16876,
"preview": "import { DOMContainer as PixiDOMContainer } from \"pixi.js\";\nimport {\n createComponent,\n Element,\n registerComponent,\n"
},
{
"path": "packages/core/src/components/DOMSprite.ts",
"chars": 31891,
"preview": "import { isSignal, Signal } from \"@signe/reactive\";\nimport { Subscription } from \"rxjs\";\nimport { createComponent, Eleme"
},
{
"path": "packages/core/src/components/DisplayObject.ts",
"chars": 21181,
"preview": "import { Element, isElement, Props, isElementFrozen } from \"../engine/reactive\";\nimport { setObservablePoint } from \"../"
},
{
"path": "packages/core/src/components/FocusContainer.ts",
"chars": 12983,
"preview": "import { createComponent, registerComponent, type Element } from \"../engine/reactive\";\nimport { applyDirective } from \"."
},
{
"path": "packages/core/src/components/Graphic.ts",
"chars": 10241,
"preview": "import { Effect, effect, isSignal, signal, Signal, WritableSignal } from \"@signe/reactive\";\nimport { Assets, ObservableP"
},
{
"path": "packages/core/src/components/Joystick.ts",
"chars": 9893,
"preview": "/*\n * Joystick\n *\n * Inspired by https://github.com/endel/pixi-virtual-joystick\n */\n\nimport * as PIXI from \"pixi.js\";\nim"
},
{
"path": "packages/core/src/components/Mesh.ts",
"chars": 6288,
"preview": "import { Effect, effect } from \"@signe/reactive\";\nimport { Mesh as PixiMesh, Geometry, Shader, Texture, Assets, BLEND_MO"
},
{
"path": "packages/core/src/components/NineSliceSprite.ts",
"chars": 1340,
"preview": "import { Assets, NineSliceSprite as PixiNineSliceSprite, Texture } from \"pixi.js\";\nimport { createComponent, registerCom"
},
{
"path": "packages/core/src/components/ParticleEmitter.ts",
"chars": 1376,
"preview": "import * as particles from \"@barvynkoa/particle-emitter\";\nimport { createComponent, Element, registerComponent } from \"."
},
{
"path": "packages/core/src/components/Scene.ts",
"chars": 136,
"preview": "import { h } from \"../engine/signal\";\nimport { Container } from \"./Container\";\n\nexport function Scene(props) {\n retur"
},
{
"path": "packages/core/src/components/Sprite.ts",
"chars": 25044,
"preview": "import { Howl } from 'howler';\nimport { computed, effect, isSignal, Signal } from \"@signe/reactive\";\nimport {\n Applicat"
},
{
"path": "packages/core/src/components/Text.ts",
"chars": 7959,
"preview": "import { Text as PixiText, TextStyle } from \"pixi.js\";\nimport { createComponent, registerComponent, Element, Props } fro"
},
{
"path": "packages/core/src/components/TilingSprite.ts",
"chars": 1283,
"preview": "import { TilingSprite as PixiTilingSprite, Texture } from 'pixi.js';\nimport { createComponent, registerComponent } from "
},
{
"path": "packages/core/src/components/Video.ts",
"chars": 2826,
"preview": "import { Texture } from \"pixi.js\";\nimport { h, mount } from \"../engine/signal\";\nimport { useDefineProps } from \"../hooks"
},
{
"path": "packages/core/src/components/Viewport.ts",
"chars": 6498,
"preview": "import { Viewport as PixiViewport } from 'pixi-viewport';\nimport { Subscription } from 'rxjs';\nimport { createComponent,"
},
{
"path": "packages/core/src/components/index.ts",
"chars": 913,
"preview": "export { Canvas } from './Canvas'\nexport { Container } from './Container'\nexport { Graphics, Rect, Circle, Ellipse, Tria"
},
{
"path": "packages/core/src/components/types/DisplayObject.ts",
"chars": 4816,
"preview": "import * as PIXI from \"pixi.js\";\nimport { SignalOrPrimitive } from \".\";\nimport { DragProps } from \"../../directives/Drag"
},
{
"path": "packages/core/src/components/types/MouseEvent.ts",
"chars": 60,
"preview": "export interface MouseEvent {\n click?: (e: any) => void\n}"
},
{
"path": "packages/core/src/components/types/Spritesheet.ts",
"chars": 8124,
"preview": "export interface TransformOptions {\n /** \n * The global value of opacity (between 0 and 1)\n * \n * @prop {"
},
{
"path": "packages/core/src/components/types/index.ts",
"chars": 169,
"preview": "import { Signal } from \"@signe/reactive\";\nimport { AnimatedSignal } from \"../../engine/animation\";\n\nexport type SignalOr"
},
{
"path": "packages/core/src/directives/Controls.ts",
"chars": 8266,
"preview": "import { Directive, registerDirective } from \"../engine/directive\";\nimport { Element, isElementFrozen } from \"../engine/"
},
{
"path": "packages/core/src/directives/ControlsBase.ts",
"chars": 7216,
"preview": "import { fps2ms } from \"../engine/utils\";\n\nexport interface ControlOptions {\n repeat?: boolean;\n bind: string | st"
},
{
"path": "packages/core/src/directives/Drag.ts",
"chars": 13918,
"preview": "import { effect, isComputed, isSignal, signal } from '@signe/reactive';\nimport { Container, Rectangle, Point, FederatedP"
},
{
"path": "packages/core/src/directives/Flash.ts",
"chars": 15837,
"preview": "import { Container } from 'pixi.js';\nimport { Directive, registerDirective } from '../engine/directive';\nimport { Elemen"
},
{
"path": "packages/core/src/directives/FocusNavigation.ts",
"chars": 3606,
"preview": "import { Directive, registerDirective, applyDirective } from \"../engine/directive\";\nimport { type Element } from \"../eng"
},
{
"path": "packages/core/src/directives/FogVisibility.ts",
"chars": 8695,
"preview": "import { isSignal } from \"@signe/reactive\";\nimport { Container } from \"pixi.js\";\nimport { Subscription } from \"rxjs\";\nim"
},
{
"path": "packages/core/src/directives/GamepadControls.ts",
"chars": 17809,
"preview": "import { ControlsBase, Controls } from \"./ControlsBase\";\nimport { WritableSignal } from \"@signe/reactive\";\nimport 'joypa"
},
{
"path": "packages/core/src/directives/JoystickControls.ts",
"chars": 13028,
"preview": "import { ControlsBase, Controls } from \"./ControlsBase\";\n\n/**\n * Joystick directions reported by the Joystick component\n"
},
{
"path": "packages/core/src/directives/KeyboardControls.ts",
"chars": 14220,
"preview": "import { ControlsBase, ControlOptions, Controls, BoundKey } from \"./ControlsBase\";\n\nexport enum Input {\n Break = 'bre"
},
{
"path": "packages/core/src/directives/Scheduler.ts",
"chars": 3289,
"preview": "import { WritableSignal } from '@signe/reactive';\nimport { Directive, registerDirective } from '../engine/directive';\nim"
},
{
"path": "packages/core/src/directives/Shake.ts",
"chars": 10523,
"preview": "import { Container, Point } from 'pixi.js';\nimport { Directive, registerDirective } from '../engine/directive';\nimport {"
},
{
"path": "packages/core/src/directives/Sound.ts",
"chars": 5359,
"preview": "import { effect } from '@signe/reactive';\nimport { Howl } from 'howler';\nimport { Container } from 'pixi.js';\nimport { S"
},
{
"path": "packages/core/src/directives/Transition.ts",
"chars": 1248,
"preview": "import { Container, DisplacementFilter, Sprite, Texture, WRAP_MODES } from 'pixi.js';\nimport { animate } from 'popmotion"
},
{
"path": "packages/core/src/directives/ViewportCull.ts",
"chars": 1245,
"preview": "import { effect } from '@signe/reactive';\nimport { Simple } from \"pixi-cull\";\nimport { Container } from 'pixi.js';\nimpor"
},
{
"path": "packages/core/src/directives/ViewportFollow.ts",
"chars": 2052,
"preview": "import { ComponentInstance } from '../components/DisplayObject';\nimport { SignalOrPrimitive } from '../components/types'"
},
{
"path": "packages/core/src/directives/index.ts",
"chars": 405,
"preview": "export * from './ControlsBase'\nexport * from './KeyboardControls'\nexport * from './GamepadControls'\nexport * from './Joy"
},
{
"path": "packages/core/src/engine/FocusManager.ts",
"chars": 15816,
"preview": "import { isSignal, signal, Signal, WritableSignal, WritableObjectSignal } from \"@signe/reactive\";\nimport { Element, isEl"
},
{
"path": "packages/core/src/engine/animation.ts",
"chars": 7829,
"preview": "import { effect, signal, type WritableSignal, type Signal } from \"@signe/reactive\";\nimport { animate as animatePopmotion"
},
{
"path": "packages/core/src/engine/bootstrap.ts",
"chars": 3773,
"preview": "import { Application, ApplicationOptions } from \"pixi.js\";\nimport { ComponentFunction, h } from \"./signal\";\nimport { use"
},
{
"path": "packages/core/src/engine/directive.ts",
"chars": 678,
"preview": "import { Element } from \"./reactive\"\n\nexport const directives: { [key: string]: any } = {}\n\nexport abstract class Direct"
},
{
"path": "packages/core/src/engine/reactive.ts",
"chars": 39450,
"preview": "import { ArrayChange, ObjectChange, Signal, WritableArraySignal, WritableObjectSignal, isComputed, isSignal, signal, com"
},
{
"path": "packages/core/src/engine/signal.ts",
"chars": 6020,
"preview": "import {\n Observable,\n Subscription\n} from \"rxjs\";\nimport { isSignal } from \"@signe/reactive\";\nimport type { Element }"
},
{
"path": "packages/core/src/engine/trigger.ts",
"chars": 3137,
"preview": "import { effect, signal } from \"@signe/reactive\";\n\nexport interface Listen<T = any> {\n config: T | undefined;\n seed: {"
},
{
"path": "packages/core/src/engine/utils.ts",
"chars": 6463,
"preview": "import { isSignal } from \"@signe/reactive\"\nimport { ObservablePoint } from \"pixi.js\"\nimport { Observable } from \"rxjs\"\n\n"
},
{
"path": "packages/core/src/hooks/addContext.ts",
"chars": 153,
"preview": "export const addContext = (element, key, value) => {\n element.props.context = {\n ...(element.props.context ?? "
},
{
"path": "packages/core/src/hooks/useFocus.ts",
"chars": 2635,
"preview": "import { Signal } from \"@signe/reactive\";\nimport { Element } from \"../engine/reactive\";\nimport { focusManager } from \".."
},
{
"path": "packages/core/src/hooks/useProps.ts",
"chars": 5479,
"preview": "import { isSignal, signal } from \"@signe/reactive\"\nimport { isPrimitive } from \"../engine/reactive\"\n\n/**\n * Converts pro"
},
{
"path": "packages/core/src/hooks/useRef.ts",
"chars": 634,
"preview": "import { Element } from \"../engine/reactive\";\nimport { ComponentInstance } from \"../components/DisplayObject\";\n\nexport f"
},
{
"path": "packages/core/src/index.ts",
"chars": 832,
"preview": "\nexport * from './directives'\nexport * from '@signe/reactive'\nexport { Howler } from 'howler'\nexport * from './component"
},
{
"path": "packages/core/src/types/pixi-cull.d.ts",
"chars": 143,
"preview": "declare module \"pixi-cull\" {\n export class Simple {\n constructor(options?: any);\n lists: Array<any>;\n cull(bou"
},
{
"path": "packages/core/src/utils/Ease.ts",
"chars": 441,
"preview": "import { \n linear,\n easeIn,\n easeInOut,\n easeOut,\n circIn,\n circInOut,\n circOut,\n backIn,\n ba"
},
{
"path": "packages/core/src/utils/GlobalAssetLoader.ts",
"chars": 6723,
"preview": "/**\n * Global Asset Loader\n * \n * Tracks the loading progress of all assets (images, spritesheets, etc.) across all spri"
},
{
"path": "packages/core/src/utils/RadialGradient.ts",
"chars": 3522,
"preview": "import { Texture, ImageSource, DOMAdapter, Matrix } from \"pixi.js\";\n\n/**\n * Creates a radial gradient texture that can b"
},
{
"path": "packages/core/src/utils/functions.ts",
"chars": 178,
"preview": "export function isPercent(value?: string | number) {\n if (!value) return false\n if (typeof value === \"string\") {\n "
},
{
"path": "packages/core/src/utils/tabindex.ts",
"chars": 1949,
"preview": "import { WritableSignal } from \"@signe/reactive\";\n\nexport type TabindexBoundaryMode = \"wrap\" | \"clamp\" | \"none\";\n\nexport"
},
{
"path": "packages/core/testing/index.ts",
"chars": 1389,
"preview": "import { bootstrapCanvas, Canvas, ComponentInstance, Element, h } from \"canvasengine\";\nimport type { Application } from "
},
{
"path": "packages/core/tsconfig.json",
"chars": 308,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"dist\",\n \"declaration\": true,\n \"emitDec"
},
{
"path": "packages/core/vite.config.ts",
"chars": 856,
"preview": "import { defineConfig } from 'vite'\nimport { resolve } from 'path'\nimport dts from 'vite-plugin-dts'\n\nexport default def"
},
{
"path": "packages/presets/package.json",
"chars": 995,
"preview": "{\n \"name\": \"@canvasengine/presets\",\n \"version\": \"2.0.0-beta.58\",\n \"type\": \"module\",\n \"main\": \"dist/index.js\",\n \"typ"
},
{
"path": "packages/presets/src/Bar.ts",
"chars": 2085,
"preview": "import { Graphics, h, useProps } from \"canvasengine\";\nimport * as PIXI from \"pixi.js\";\n\ninterface BarProps {\n backgroun"
},
{
"path": "packages/presets/src/Button.ts",
"chars": 0,
"preview": ""
},
{
"path": "packages/presets/src/FogOfWar.ts",
"chars": 15655,
"preview": "import { h, signal, Sprite, tick, useProps } from \"canvasengine\";\nimport { Texture } from \"pixi.js\";\n\ntype FogColor = [n"
},
{
"path": "packages/presets/src/Footprints.ts",
"chars": 31734,
"preview": "import { Container, h, mount, useProps } from \"canvasengine\";\nimport {\n BlurFilter,\n Container as PixiContainer,\n Gra"
},
{
"path": "packages/presets/src/Loading.ts",
"chars": 6228,
"preview": "import { Graphics, Container, h, useProps, tick, signal, effect, mount } from \"canvasengine\";\nimport * as PIXI from \"pix"
},
{
"path": "packages/presets/src/NightAmbiant.ts",
"chars": 17651,
"preview": "import { Filter } from \"pixi.js\";\nimport { Container, effect, h, mount, useProps } from \"canvasengine\";\nimport fragmentS"
},
{
"path": "packages/presets/src/Particle.ts",
"chars": 174,
"preview": "import { Fx } from \"./fx\";\n\nexport function Particle(options) {\n const { emit, ...props } = options;\n return Fx({\n "
},
{
"path": "packages/presets/src/SpriteShadows.ts",
"chars": 25584,
"preview": "import { Container, h, mount, useProps } from \"canvasengine\";\nimport {\n BlurFilter,\n Container as PixiContainer,\n Fil"
},
{
"path": "packages/presets/src/Tilemap/Tile.ts",
"chars": 2264,
"preview": "import { CompositeTilemap } from \"@pixi/tilemap\";\nimport { Tile as TiledTileClass } from '@rpgjs/tiled';\nimport { Animat"
},
{
"path": "packages/presets/src/Tilemap/TileGroup.ts",
"chars": 6087,
"preview": "interface TileOptions {\n tilesetIndex?: number\n tileId: number\n x: number\n y: number\n}\n\ninterface TilesGroup"
},
{
"path": "packages/presets/src/Tilemap/TileLayer.ts",
"chars": 3883,
"preview": "import {\n CompositeTilemap,\n POINT_STRUCT_SIZE,\n Tilemap,\n settings,\n} from \"@canvasengine/tilemap\";\nimport { Layer,"
},
{
"path": "packages/presets/src/Tilemap/TileSet.ts",
"chars": 972,
"preview": "import { TiledTileset, Tileset as TiledTilesetClass } from \"@canvasengine/tiled\";\nimport { Assets, Rectangle, Texture } "
},
{
"path": "packages/presets/src/Tilemap/index.ts",
"chars": 7962,
"preview": "import { TiledLayer, TiledLayerType, TiledMap, TiledParserFile, TiledTileset } from \"@canvasengine/tiled\"\nimport { loop,"
},
{
"path": "packages/presets/src/Weathers/fog.ts",
"chars": 8985,
"preview": "import { GlProgram } from \"pixi.js\";\n\nexport function createFogShader(): GlProgram {\n const vertexSrc = /* glsl */ `\n "
},
{
"path": "packages/presets/src/Weathers/index.ts",
"chars": 18663,
"preview": "import {\n tick,\n useProps,\n h,\n Mesh,\n signal,\n mount,\n effect as watchEffect,\n} from \"canvasengine\";\nimport { Ge"
},
{
"path": "packages/presets/src/Weathers/rain.ts",
"chars": 3197,
"preview": "import { GlProgram } from \"pixi.js\";\n\nexport function createRainShader(): GlProgram {\n const vertexSrc = /* glsl */ `\n "
},
{
"path": "packages/presets/src/Weathers/snow.ts",
"chars": 3357,
"preview": "import { GlProgram } from \"pixi.js\";\n\n/**\n * Creates a performant snow shader program.\n * Optimized with early exits, ca"
},
{
"path": "packages/presets/src/fx/Fx.ts",
"chars": 2423,
"preview": "import { Container, h, mount, on, tick, useProps } from \"canvasengine\";\nimport type { Container as PixiContainer } from "
},
{
"path": "packages/presets/src/fx/index.ts",
"chars": 102,
"preview": "export * from \"./Fx\";\nexport * from \"./presets\";\nexport * from \"./runtime\";\nexport * from \"./types\";\n\n"
},
{
"path": "packages/presets/src/fx/presets.ts",
"chars": 8178,
"preview": "import type { FxPreset } from \"./types\";\n\nexport const FX_PRESETS = {\n hitSpark: {\n duration: 180,\n emitters: [\n "
},
{
"path": "packages/presets/src/fx/runtime.ts",
"chars": 9291,
"preview": "import { Container, Sprite, Texture } from \"pixi.js\";\nimport type {\n FxEmitterConfig,\n FxInstance,\n FxParticle,\n FxP"
},
{
"path": "packages/presets/src/fx/textures.ts",
"chars": 3955,
"preview": "import { Assets, Texture } from \"pixi.js\";\nimport type { FxParticleConfig, FxShape } from \"./types\";\n\nconst shapeTexture"
},
{
"path": "packages/presets/src/fx/types.ts",
"chars": 2266,
"preview": "import type { Container, Texture } from \"pixi.js\";\n\nexport type FxSignal<T> = T | (() => T);\nexport type FxRange = numbe"
},
{
"path": "packages/presets/src/fx/utils.ts",
"chars": 1997,
"preview": "import type { FxColor, FxRange } from \"./types\";\n\nexport function resolveValue<T>(value: T | (() => T)): T {\n return ty"
},
{
"path": "packages/presets/src/index.ts",
"chars": 268,
"preview": "export * from './Bar'\nexport * from './Particle'\nexport * from './fx'\nexport * from './NightAmbiant'\nexport * from './Lo"
},
{
"path": "packages/presets/src/shaders/defaultFilter.vert.glsl",
"chars": 740,
"preview": "/**\n * Default Pixi filter vertex shader.\n * Outputs vTextureCoord (0-1) for sampling the input texture in fragment shad"
},
{
"path": "packages/presets/src/shaders/nightSpot.frag.glsl",
"chars": 1995,
"preview": "/**\n * Night + spot light + fog fragment shader.\n * - Supports multiple circular spots (uSpots) in screen-pixel space.\n "
},
{
"path": "packages/presets/src/shaders/shadowGradient.frag.glsl",
"chars": 597,
"preview": "/**\n * Gradient alpha for projected sprite shadows.\n * Strong alpha near the caster's feet (vTextureCoord.y ~ 1),\n * sof"
},
{
"path": "packages/presets/tsconfig.json",
"chars": 283,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"dist\",\n \"declaration\": true,\n \"emitDec"
},
{
"path": "packages/presets/vite.config.ts",
"chars": 961,
"preview": "import { defineConfig } from 'vite'\nimport { resolve } from 'path'\nimport dts from 'vite-plugin-dts'\n\nexport default def"
},
{
"path": "packages/testing/package.json",
"chars": 938,
"preview": "{\n \"name\": \"@canvasengine/testing\",\n \"version\": \"2.0.0-beta.58\",\n \"description\": \"Testing utilities and mocks for Can"
},
{
"path": "packages/testing/src/helpers/createMockComponentInstance.ts",
"chars": 1752,
"preview": "import { ComponentInstance } from 'canvasengine';\nimport { MockContainer, MockSprite, MockText, MockGraphics, MockMesh, "
},
{
"path": "packages/testing/src/helpers/createMockElement.ts",
"chars": 2445,
"preview": "import { Element, Props } from 'canvasengine';\nimport { ComponentInstance } from 'canvasengine';\nimport { Subject } from"
},
{
"path": "packages/testing/src/helpers/spyOnElement.ts",
"chars": 2342,
"preview": "import { vi } from 'vitest';\nimport { Element, ComponentInstance } from 'canvasengine';\n\n/**\n * Creates a spy on a prope"
},
{
"path": "packages/testing/src/index.ts",
"chars": 2479,
"preview": "/**\n * @canvasengine/testing\n * \n * Testing utilities and mocks for CanvasEngine\n * \n * This package provides comprehens"
},
{
"path": "packages/testing/src/mocks/pixi-base.ts",
"chars": 11992,
"preview": "import { vi } from 'vitest';\n\n/**\n * Base class for creating mock event emitters\n * Provides event handling functionalit"
},
{
"path": "packages/testing/tsconfig.json",
"chars": 357,
"preview": "{\n \"extends\": \"../../tsconfig.json\",\n \"compilerOptions\": {\n \"outDir\": \"dist\",\n \"declaration\": true,\n \"emitDec"
},
{
"path": "packages/testing/tsup.config.ts",
"chars": 276,
"preview": "import { defineConfig } from 'tsup'\n\nexport default defineConfig({\n format: ['esm'],\n target: 'node20',\n splitting: t"
}
]
// ... and 120 more files (download for full content)
About this extraction
This page contains the full source code of the RSamaium/CanvasEngine GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 320 files (2.7 MB), approximately 723.4k tokens, and a symbol index with 943 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.