Skip to content

Commit 6ba45e7

Browse files
authored
Merge pull request #5 from AR-js-org/copilot/replace-static-device-profiles
Replace static device profiles with runtime capability-based profiling
2 parents 2d7c53e + 420fec4 commit 6ba45e7

36 files changed

+1412
-984
lines changed

.gitattributes

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Enforce LF for source and text files across platforms
2+
*.js text eol=lf
3+
*.mjs text eol=lf
4+
*.cjs text eol=lf
5+
*.ts text eol=lf
6+
*.json text eol=lf
7+
*.md text eol=lf
8+
*.css text eol=lf
9+
*.html text eol=lf
10+
11+
# Keep binary files untouched
12+
*.png binary
13+
*.jpg binary
14+
*.jpeg binary
15+
*.gif binary
16+
*.webp binary
17+
*.mp4 binary
18+
*.mp3 binary
19+
*.woff binary
20+
*.woff2 binary
21+
*.ttf binary
22+
*.otf binary

.github/workflows/ci.yml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ name: CI
22

33
on:
44
push:
5-
branches: ["**"]
5+
branches: ['**']
66
paths:
7-
- "src/**"
8-
- "plugins/**"
9-
- "examples/**"
10-
- "tests/**"
11-
- "package.json"
12-
- "vite.config.js"
13-
- "vitest.config.js"
14-
- ".github/workflows/**"
7+
- 'src/**'
8+
- 'plugins/**'
9+
- 'examples/**'
10+
- 'tests/**'
11+
- 'package.json'
12+
- 'vite.config.js'
13+
- 'vitest.config.js'
14+
- '.github/workflows/**'
1515
pull_request:
16-
branches: ["**"]
16+
branches: ['**']
1717

1818
concurrency:
1919
group: ci-${{ github.ref }}
@@ -26,7 +26,7 @@ jobs:
2626
strategy:
2727
fail-fast: false
2828
matrix:
29-
node-version: [20.x] # stick to Node 20 LTS for CI stability
29+
node-version: [22.x] # stick to Node 22 LTS for CI stability
3030
steps:
3131
- name: Checkout
3232
uses: actions/checkout@v4
@@ -74,10 +74,10 @@ jobs:
7474
- name: Checkout
7575
uses: actions/checkout@v4
7676

77-
- name: Setup Node.js 20
77+
- name: Setup Node.js 22
7878
uses: actions/setup-node@v4
7979
with:
80-
node-version: 20.x
80+
node-version: 22.x
8181
cache: npm
8282

8383
- name: Install dependencies
@@ -101,4 +101,4 @@ jobs:
101101
run: npm run build:vite
102102
env:
103103
# Ask rollup to use JS fallback if native optional dep is still missing
104-
ROLLUP_SKIP_NODEJS_NATIVE: "1"
104+
ROLLUP_SKIP_NODEJS_NATIVE: '1'

.prettierrc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"printWidth": 100,
3+
"singleQuote": true,
4+
"trailingComma": "all",
5+
"semi": true,
6+
"arrowParens": "always",
7+
"endOfLine": "lf"
8+
}

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ AR.js Core now includes a modern Entity-Component-System (ECS) architecture with
1414
### Quick Start with ECS
1515

1616
```javascript
17-
import { Engine, CaptureSystem, SOURCE_TYPES } from "ar.js-core";
18-
import { webcamPlugin } from "./plugins/source/webcam.js";
19-
import { defaultProfilePlugin } from "./plugins/profile/default-policy.js";
17+
import { Engine, CaptureSystem, SOURCE_TYPES } from 'ar.js-core';
18+
import { webcamPlugin } from './plugins/source/webcam.js';
19+
import { defaultProfilePlugin } from './plugins/profile/default-policy.js';
2020

2121
// Create engine and register plugins
2222
const engine = new Engine();
@@ -94,7 +94,7 @@ If the camera doesn’t start automatically, click or tap once to allow autoplay
9494
The original Source and Profile classes are still available and fully supported:
9595

9696
```javascript
97-
import { Source, Profile } from "ar.js-core";
97+
import { Source, Profile } from 'arjs-core';
9898
```
9999

100-
See existing documentation for legacy API usage.
100+
See existing documentation for legacy API usage.

docs/ECS_ARCHITECTURE.md

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The Engine orchestrates the entire system, managing:
1919
**Usage:**
2020

2121
```javascript
22-
import { Engine } from "ar.js-core";
22+
import { Engine } from 'ar.js-core';
2323

2424
const engine = new Engine();
2525
engine.start(); // Start the game loop
@@ -38,8 +38,8 @@ Minimal Entity-Component-System implementation:
3838

3939
```javascript
4040
const entityId = engine.ecs.createEntity();
41-
engine.ecs.setComponent(entityId, "Transform", { x: 0, y: 0, z: 0 });
42-
engine.ecs.setResource("ProcessingConfig", { threshold: 0.5 });
41+
engine.ecs.setComponent(entityId, 'Transform', { x: 0, y: 0, z: 0 });
42+
engine.ecs.setResource('ProcessingConfig', { threshold: 0.5 });
4343
```
4444

4545
### Event Bus (`src/core/event-bus.js`)
@@ -50,12 +50,12 @@ Lightweight publish-subscribe system for loose coupling:
5050

5151
```javascript
5252
// Subscribe to events
53-
engine.eventBus.on("capture:ready", (data) => {
54-
console.log("Capture ready:", data);
53+
engine.eventBus.on('capture:ready', (data) => {
54+
console.log('Capture ready:', data);
5555
});
5656

5757
// Emit events
58-
engine.eventBus.emit("custom:event", { message: "Hello" });
58+
engine.eventBus.emit('custom:event', { message: 'Hello' });
5959
```
6060

6161
### Plugin Manager (`src/core/plugin-manager.js`)
@@ -66,20 +66,20 @@ Manages plugin registration, enabling, and disabling:
6666

6767
```javascript
6868
// Register a plugin
69-
engine.pluginManager.register("my-plugin", {
69+
engine.pluginManager.register('my-plugin', {
7070
async init(context) {
71-
console.log("Plugin initialized");
71+
console.log('Plugin initialized');
7272
},
7373
async dispose() {
74-
console.log("Plugin disposed");
74+
console.log('Plugin disposed');
7575
},
7676
update(deltaTime, context) {
7777
// Called each frame
7878
},
7979
});
8080

8181
// Enable the plugin
82-
await engine.pluginManager.enable("my-plugin", engine.getContext());
82+
await engine.pluginManager.enable('my-plugin', engine.getContext());
8383
```
8484

8585
## Systems
@@ -91,7 +91,7 @@ Manages video/image capture from various sources:
9191
**Usage:**
9292

9393
```javascript
94-
import { CaptureSystem, SOURCE_TYPES } from "ar.js-core";
94+
import { CaptureSystem, SOURCE_TYPES } from 'ar.js-core';
9595

9696
await CaptureSystem.initialize(
9797
{
@@ -154,21 +154,21 @@ Profiles:
154154
**Usage:**
155155

156156
```javascript
157-
import { defaultProfilePlugin } from "./plugins/profile/default-policy.js";
157+
import { defaultProfilePlugin } from './plugins/profile/default-policy.js';
158158

159159
engine.pluginManager.register(defaultProfilePlugin.id, defaultProfilePlugin);
160160
await engine.pluginManager.enable(defaultProfilePlugin.id, engine.getContext());
161161

162162
const profile = engine.ecs.getResource(RESOURCES.DEVICE_PROFILE);
163-
console.log("Device profile:", profile.label);
163+
console.log('Device profile:', profile.label);
164164
```
165165

166166
## Component and Resource Keys
167167

168168
Standardized keys are defined in `src/core/components.js`:
169169

170170
```javascript
171-
import { COMPONENTS, RESOURCES, EVENTS } from "ar.js-core";
171+
import { COMPONENTS, RESOURCES, EVENTS } from 'ar.js-core';
172172

173173
// Component keys (entity-specific)
174174
COMPONENTS.TRACKING_TARGET;
@@ -193,9 +193,9 @@ Plugins are simple objects with lifecycle methods:
193193

194194
```javascript
195195
const myPlugin = {
196-
id: "my-plugin",
197-
name: "My Custom Plugin",
198-
type: "custom",
196+
id: 'my-plugin',
197+
name: 'My Custom Plugin',
198+
type: 'custom',
199199

200200
// Called when plugin is enabled
201201
async init(context) {
@@ -227,10 +227,10 @@ function mySystem(deltaTime, context) {
227227
const { ecs, eventBus } = context;
228228

229229
// Query entities with specific components
230-
const entities = ecs.query("Transform", "Visible");
230+
const entities = ecs.query('Transform', 'Visible');
231231

232232
for (const entityId of entities) {
233-
const transform = ecs.getComponent(entityId, "Transform");
233+
const transform = ecs.getComponent(entityId, 'Transform');
234234
// Process entity
235235
}
236236
}
@@ -294,10 +294,10 @@ The legacy `Source` and `Profile` classes remain unchanged and continue to work
294294

295295
```javascript
296296
// Legacy API (still works)
297-
import { Source, Profile } from "ar.js-core";
297+
import { Source, Profile } from 'ar.js-core';
298298

299299
// New ECS API
300-
import { Engine, CaptureSystem } from "ar.js-core";
300+
import { Engine, CaptureSystem } from 'ar.js-core';
301301
```
302302

303303
Future versions may add adapters that allow the legacy classes to use the new ECS internals while maintaining the same external API.
@@ -309,39 +309,39 @@ Future versions may add adapters that allow the legacy classes to use the new EC
309309
**Before:**
310310

311311
```javascript
312-
import { Source } from "ar.js-core";
312+
import { Source } from 'ar.js-core';
313313

314314
const source = new Source({
315-
sourceType: "webcam",
315+
sourceType: 'webcam',
316316
sourceWidth: 640,
317317
sourceHeight: 480,
318318
});
319319

320320
source.init(
321321
() => {
322-
console.log("Source ready");
322+
console.log('Source ready');
323323
},
324324
(error) => {
325-
console.error("Source error:", error);
325+
console.error('Source error:', error);
326326
},
327327
);
328328
```
329329

330330
**After:**
331331

332332
```javascript
333-
import { Engine, CaptureSystem, SOURCE_TYPES } from "ar.js-core";
334-
import { webcamPlugin } from "./plugins/source/webcam.js";
333+
import { Engine, CaptureSystem, SOURCE_TYPES } from 'ar.js-core';
334+
import { webcamPlugin } from './plugins/source/webcam.js';
335335

336336
const engine = new Engine();
337337
engine.pluginManager.register(webcamPlugin.id, webcamPlugin);
338338

339-
engine.eventBus.on("capture:ready", () => {
340-
console.log("Source ready");
339+
engine.eventBus.on('capture:ready', () => {
340+
console.log('Source ready');
341341
});
342342

343-
engine.eventBus.on("capture:init:error", ({ error }) => {
344-
console.error("Source error:", error);
343+
engine.eventBus.on('capture:init:error', ({ error }) => {
344+
console.error('Source error:', error);
345345
});
346346

347347
await CaptureSystem.initialize(

examples/basic-ecs/image-example.html

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<meta
6-
name="viewport"
7-
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
8-
/>
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
96
<title>AR.js-core ECS - Image Source Example</title>
107
<style>
118
html,
@@ -98,13 +95,13 @@
9895
</div>
9996

10097
<script type="module">
101-
import { Engine } from "../../src/core/engine.js";
102-
import { imagePlugin } from "../../plugins/source/image.js";
98+
import { Engine } from '../../src/core/engine.js';
99+
import { imagePlugin } from '../../plugins/source/image.js';
103100

104-
const statusEl = document.getElementById("status");
105-
const urlEl = document.getElementById("url");
106-
const loadBtn = document.getElementById("load");
107-
const clearBtn = document.getElementById("clear");
101+
const statusEl = document.getElementById('status');
102+
const urlEl = document.getElementById('url');
103+
const loadBtn = document.getElementById('load');
104+
const clearBtn = document.getElementById('clear');
108105

109106
// Initialize engine (provides eventBus and context)
110107
const engine = new Engine();
@@ -115,18 +112,18 @@
115112

116113
function setStatus(msg, ok = true) {
117114
statusEl.textContent = msg;
118-
statusEl.className = ok ? "status-ok" : "status-bad";
115+
statusEl.className = ok ? 'status-ok' : 'status-bad';
119116
}
120117

121118
function removeExisting() {
122-
const el = document.getElementById("arjs-video");
119+
const el = document.getElementById('arjs-video');
123120
if (el && el.parentNode) el.parentNode.removeChild(el);
124121
}
125122

126123
async function loadImage(url) {
127124
if (!url) return;
128125
try {
129-
setStatus("Loading image...", true);
126+
setStatus('Loading image...', true);
130127
// Remove any previous image first
131128
removeExisting();
132129

@@ -147,22 +144,22 @@
147144
document.body.appendChild(frame.element);
148145
}
149146

150-
setStatus("Image loaded", true);
147+
setStatus('Image loaded', true);
151148
} catch (e) {
152149
console.error(e);
153-
setStatus("Failed to load image. Check the URL and CORS.", false);
150+
setStatus('Failed to load image. Check the URL and CORS.', false);
154151
}
155152
}
156153

157154
// Wire up UI
158-
loadBtn.addEventListener("click", () => {
159-
const url = (urlEl.value || "").trim();
155+
loadBtn.addEventListener('click', () => {
156+
const url = (urlEl.value || '').trim();
160157
loadImage(url);
161158
});
162159

163-
clearBtn.addEventListener("click", () => {
160+
clearBtn.addEventListener('click', () => {
164161
removeExisting();
165-
setStatus("Cleared", true);
162+
setStatus('Cleared', true);
166163
});
167164

168165
// Auto-load the initial URL

0 commit comments

Comments
 (0)