From 9f959bc230089ae0de7e8231e24ae98b56eb06b0 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 20 May 2026 14:19:39 +0800 Subject: [PATCH 1/9] Init skills --- .claude/skills/dbr-js-sample-creator/SKILL.md | 32 ++ .codex/skills/dbr-js-sample-creator/SKILL.md | 32 ++ .../skills/dbr-js-sample-creator/README.md | 86 ++++ .github/skills/dbr-js-sample-creator/SKILL.md | 262 +++++++++++ .../dbr-js-sample-creator/evals/evals.json | 92 ++++ .../references/api-camera.md | 197 +++++++++ .../references/api-frameworks.md | 409 ++++++++++++++++++ .../references/api-image.md | 209 +++++++++ .../references/api-sdk.md | 175 ++++++++ .../references/sample-patterns.md | 330 ++++++++++++++ 10 files changed, 1824 insertions(+) create mode 100644 .claude/skills/dbr-js-sample-creator/SKILL.md create mode 100644 .codex/skills/dbr-js-sample-creator/SKILL.md create mode 100644 .github/skills/dbr-js-sample-creator/README.md create mode 100644 .github/skills/dbr-js-sample-creator/SKILL.md create mode 100644 .github/skills/dbr-js-sample-creator/evals/evals.json create mode 100644 .github/skills/dbr-js-sample-creator/references/api-camera.md create mode 100644 .github/skills/dbr-js-sample-creator/references/api-frameworks.md create mode 100644 .github/skills/dbr-js-sample-creator/references/api-image.md create mode 100644 .github/skills/dbr-js-sample-creator/references/api-sdk.md create mode 100644 .github/skills/dbr-js-sample-creator/references/sample-patterns.md diff --git a/.claude/skills/dbr-js-sample-creator/SKILL.md b/.claude/skills/dbr-js-sample-creator/SKILL.md new file mode 100644 index 00000000..361d6752 --- /dev/null +++ b/.claude/skills/dbr-js-sample-creator/SKILL.md @@ -0,0 +1,32 @@ +--- +name: dbr-js-sample-creator +description: > + Use when creating JavaScript, TypeScript, or HTML sample code using the Dynamsoft Barcode Reader + SDK (dynamsoft-barcode-reader-bundle npm package or CDN). This skill covers all web barcode + scanning use cases including live camera scanning, image file decoding, single-barcode scanning, + QR codes, 1D barcodes, DataMatrix, PDF417, DPM, and framework integrations (React, Vue, Angular, + Next.js, Nuxt, Svelte, Electron, Capacitor, Blazor, PWA, ES6 modules, plain HTML). + Use this skill whenever the user mentions Dynamsoft Barcode Reader JavaScript, DBR JS, + dynamsoft-barcode-reader-bundle, CaptureVisionRouter in the browser, barcode scanning with + Dynamsoft in a web app, or wants to create a JavaScript/TypeScript sample that scans or decodes + barcodes. +--- + +# DBR JavaScript Sample Creator + +The canonical skill definition lives in `.github/skills/dbr-js-sample-creator/`. + +**Read `.github/skills/dbr-js-sample-creator/SKILL.md` and its `references/` directory now.** + +The `SKILL.md` file contains: +- SDK architecture overview and loading methods (CDN UMD, CDN ES module, npm) +- License initialization patterns and the default public trial key +- Camera scanning and image capture code patterns +- Code style conventions for this repository + +The `references/` directory contains: +- `api-sdk.md` — SDK loading, namespaces, CoreModule, preset templates +- `api-camera.md` — CameraView, CameraEnhancer, result receiver, dedup filter +- `api-image.md` — File capture, CapturedResult, BarcodeResultItem properties +- `api-frameworks.md` — React, Vue, Angular complete component patterns +- `sample-patterns.md` — Copy-paste ready code for all common scenarios diff --git a/.codex/skills/dbr-js-sample-creator/SKILL.md b/.codex/skills/dbr-js-sample-creator/SKILL.md new file mode 100644 index 00000000..361d6752 --- /dev/null +++ b/.codex/skills/dbr-js-sample-creator/SKILL.md @@ -0,0 +1,32 @@ +--- +name: dbr-js-sample-creator +description: > + Use when creating JavaScript, TypeScript, or HTML sample code using the Dynamsoft Barcode Reader + SDK (dynamsoft-barcode-reader-bundle npm package or CDN). This skill covers all web barcode + scanning use cases including live camera scanning, image file decoding, single-barcode scanning, + QR codes, 1D barcodes, DataMatrix, PDF417, DPM, and framework integrations (React, Vue, Angular, + Next.js, Nuxt, Svelte, Electron, Capacitor, Blazor, PWA, ES6 modules, plain HTML). + Use this skill whenever the user mentions Dynamsoft Barcode Reader JavaScript, DBR JS, + dynamsoft-barcode-reader-bundle, CaptureVisionRouter in the browser, barcode scanning with + Dynamsoft in a web app, or wants to create a JavaScript/TypeScript sample that scans or decodes + barcodes. +--- + +# DBR JavaScript Sample Creator + +The canonical skill definition lives in `.github/skills/dbr-js-sample-creator/`. + +**Read `.github/skills/dbr-js-sample-creator/SKILL.md` and its `references/` directory now.** + +The `SKILL.md` file contains: +- SDK architecture overview and loading methods (CDN UMD, CDN ES module, npm) +- License initialization patterns and the default public trial key +- Camera scanning and image capture code patterns +- Code style conventions for this repository + +The `references/` directory contains: +- `api-sdk.md` — SDK loading, namespaces, CoreModule, preset templates +- `api-camera.md` — CameraView, CameraEnhancer, result receiver, dedup filter +- `api-image.md` — File capture, CapturedResult, BarcodeResultItem properties +- `api-frameworks.md` — React, Vue, Angular complete component patterns +- `sample-patterns.md` — Copy-paste ready code for all common scenarios diff --git a/.github/skills/dbr-js-sample-creator/README.md b/.github/skills/dbr-js-sample-creator/README.md new file mode 100644 index 00000000..ea74011a --- /dev/null +++ b/.github/skills/dbr-js-sample-creator/README.md @@ -0,0 +1,86 @@ +# DBR JavaScript Sample Creator — AI Coding Skill + +An AI coding skill that helps developers quickly build working web applications with the +[Dynamsoft Barcode Reader JavaScript Edition](https://www.dynamsoft.com/barcode-reader/sdk-javascript/) +SDK. Feed this skill to your AI agent (GitHub Copilot, Claude, Cursor, Windsurf, etc.) and +describe what you need — the agent will generate correct, production-ready code. + +## Who Is This For? + +Developers evaluating or integrating the `dynamsoft-barcode-reader-bundle` JavaScript SDK who want +to **accelerate POC development** using AI coding assistants. Instead of reading through +documentation and samples manually, let your AI agent do the heavy lifting with full SDK knowledge +built in. + +## No Installation Required + +This skill is organized into agent-specific directories so it is **auto-discovered** with no +configuration needed: + +| Directory | Agent | +|---|---| +| `.github/skills/dbr-js-sample-creator/` | GitHub Copilot (auto-discovered) | +| `.claude/skills/dbr-js-sample-creator/` | Claude Code (auto-discovered) | +| `.codex/skills/dbr-js-sample-creator/` | OpenAI Codex (auto-discovered) | + +Just clone the repository and open it in your AI-enabled editor. The skill is active immediately. + +## What's Inside + +``` +.github/skills/dbr-js-sample-creator/ +├── SKILL.md # Entry point — SDK architecture, patterns, conventions +├── README.md # This file +├── references/ +│ ├── api-sdk.md # SDK loading (CDN/npm/ES6), namespaces, CoreModule, templates +│ ├── api-camera.md # CameraView, CameraEnhancer, result receiver, filter +│ ├── api-image.md # File/image capture, CapturedResult, BarcodeResultItem +│ ├── api-frameworks.md # React, Vue, Angular integration patterns +│ └── sample-patterns.md # Complete working code for every common scenario +└── evals/ + └── evals.json # Test prompts & expectations for skill validation + +.claude/skills/dbr-js-sample-creator/SKILL.md # Claude Code entry point +.codex/skills/dbr-js-sample-creator/SKILL.md # Codex entry point +``` + +The AI agent reads `SKILL.md` first, which directs it to the appropriate reference files based on +your task. You do not need to understand these files — they are written for the AI. + +## SDK at a Glance + +- **Package:** `dynamsoft-barcode-reader-bundle@11.4.2001` +- **CDN:** `https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader-bundle@11.4.2001/dist/dbr.bundle.js` +- **npm:** `npm install dynamsoft-barcode-reader-bundle` +- **Trial license:** [Get a 30-day free trial](https://www.dynamsoft.com/customer/license/trialLicense?product=dbr&package=js&utm_source=sampleReadme) + +## Example Prompts + +Once the skill is active, try prompts like: + +| Prompt | What the AI generates | +|---|---| +| *"Create a plain HTML barcode scanner with camera"* | Full HTML with CDN UMD, camera pipeline, dedup filter | +| *"Build a React component that scans barcodes from camera"* | TypeScript React component with proper lifecycle + cleanup | +| *"Make a Vue 3 image barcode decoder"* | Vue SFC with ` +``` + +All APIs are on the global `Dynamsoft` object: +- `Dynamsoft.License.LicenseManager` +- `Dynamsoft.CVR.CaptureVisionRouter`, `Dynamsoft.CVR.CapturedResultReceiver` +- `Dynamsoft.DCE.CameraView`, `Dynamsoft.DCE.CameraEnhancer` +- `Dynamsoft.Utility.MultiFrameResultCrossFilter` +- `Dynamsoft.Core.CoreModule`, `Dynamsoft.Core.EnumCapturedResultItemType` + +### Method 2: CDN ES module (` + + +``` + +### ImageCapture Component (Vue) + +```vue + + + +``` + +--- + +## Angular + +The Angular pattern mirrors React/Vue with `ngAfterViewInit` / `ngOnDestroy` lifecycle hooks and +`@ViewChild` for the camera container: + +```ts +import { Component, AfterViewInit, OnDestroy, ViewChild, ElementRef } from "@angular/core"; +import "../../dynamsoft.config"; +import { CameraView, CameraEnhancer, CaptureVisionRouter, MultiFrameResultCrossFilter } from "dynamsoft-barcode-reader-bundle"; + +@Component({ selector: "app-video-capture", template: ` +
+
{{ resultText }}
+` }) +export class VideoCaptureComponent implements AfterViewInit, OnDestroy { + @ViewChild("cameraViewContainer") cameraViewContainer!: ElementRef; + resultText = ""; + private cvRouter!: CaptureVisionRouter; + private cameraEnhancer!: CameraEnhancer; + private isDestroyed = false; + + async ngAfterViewInit() { + const cameraView = await CameraView.createInstance(); + this.cameraEnhancer = await CameraEnhancer.createInstance(cameraView); + this.cameraViewContainer.nativeElement.append(cameraView.getUIElement()); + + this.cvRouter = await CaptureVisionRouter.createInstance(); + this.cvRouter.setInput(this.cameraEnhancer); + + await this.cvRouter.addResultReceiver({ + onDecodedBarcodesReceived: (result) => { + if (!result.barcodeResultItems.length) return; + this.resultText = result.barcodeResultItems.map(i => `${i.formatString}: ${i.text}`).join("\n"); + }, + }); + + const filter = new MultiFrameResultCrossFilter(); + filter.enableResultCrossVerification("barcode", true); + filter.enableResultDeduplication("barcode", true); + await this.cvRouter.addResultFilter(filter); + + await this.cameraEnhancer.open(); + cameraView.setScanLaserVisible(true); + await this.cvRouter.startCapturing("ReadBarcodes_SpeedFirst"); + } + + ngOnDestroy() { + this.isDestroyed = true; + this.cvRouter?.dispose(); + this.cameraEnhancer?.dispose(); + } +} +``` + +--- + +## Next.js + +- Use `"use client"` directive — all SDK code runs client-side only. +- Dynamic import with `ssr: false` for SDK if needed: + +```ts +"use client"; +import "../../dynamsoft.config"; // same config as React +// Everything else is identical to the React pattern +``` + +--- + +## Key Framework Rules + +1. **`isDestroyed` flag** — Set to `true` in cleanup. Check after every `await` before touching state + or DOM. This prevents React StrictMode double-invoke issues and Vue/Angular unmount race conditions. +2. **`pInit` promise** — Created before async work begins. Awaited in cleanup before `dispose()`. + Ensures resources are not disposed mid-initialization. +3. **Camera container ref** — Always a `ref` / `@ViewChild` / `useRef` pointing to a DOM element. + Never use `document.querySelector` inside framework components. +4. **`dynamsoft.config.ts` is a side effect** — Import it at the top of any component that uses the + SDK. Multiple imports are fine — the module is only evaluated once. +5. **Dispose order** — Always `cvRouter.dispose()` first, then `cameraEnhancer.dispose()`. diff --git a/.github/skills/dbr-js-sample-creator/references/api-image.md b/.github/skills/dbr-js-sample-creator/references/api-image.md new file mode 100644 index 00000000..52dc87d8 --- /dev/null +++ b/.github/skills/dbr-js-sample-creator/references/api-image.md @@ -0,0 +1,209 @@ +# DBR JavaScript Sample Creator — API Reference: Image / File Capture + +--- + +## CaptureVisionRouter.capture() + +Decodes barcodes from a single image synchronously (returns a Promise). + +```ts +const result = await cvRouter.capture(source, template); +``` + +**`source` accepts:** +- `File` — from `` +- `Blob` — binary data blob +- `string` — URL to an image file +- `HTMLImageElement` — an `` DOM element +- `HTMLCanvasElement` — a canvas element +- `HTMLVideoElement` — a video frame + +**`template`** — preset string or custom template name (see `api-sdk.md`). + +For image files, use `"ReadBarcodes_ReadRateFirst"` (accuracy-first) unless speed matters more. + +--- + +## CapturedResult (from `capture()`) + +```ts +const result = await cvRouter.capture(file, "ReadBarcodes_ReadRateFirst"); + +// Access barcode results — preferred approach +const barcodeItems = result.decodedBarcodesResult?.barcodeResultItems ?? []; + +// Alternative: iterate result.items and filter by type +for (let item of result.items) { + if (item.type !== EnumCapturedResultItemType.CRIT_BARCODE) continue; + const barcodeItem = item as BarcodeResultItem; // TypeScript + console.log(barcodeItem.formatString, barcodeItem.text); +} + +// Error info +result.errorCode // 0 = success +result.errorString // description +``` + +--- + +## BarcodeResultItem Properties + +```ts +item.text // string — decoded barcode content +item.formatString // string — e.g., "QR_CODE", "CODE_128", "EAN_13", "PDF_417" +item.format // number — numeric format enum (EnumBarcodeFormat) +item.location // Quadrilateral — four corner points of the barcode +item.location.points // [Point, Point, Point, Point] +item.confidence // number — confidence score (0–100) +``` + +--- + +## Handling No Results + +Always check for empty results to give feedback to the user: + +```js +const items = result.decodedBarcodesResult?.barcodeResultItems ?? []; +if (!items.length) { + outputDiv.innerText = "No barcode found"; + return; +} +``` + +--- + +## File Input Pattern (Plain HTML) + +```html + + +
+ + +``` + +--- + +## Multiple File Decoding + +```js +document.querySelector("#input-file").addEventListener("change", async function (e) { + const files = [...e.target.files]; + this.value = ""; + resultsDiv.innerText = ""; + + const { cvRouter } = await pInit; + + for (let file of files) { + if (files.length > 1) resultsDiv.innerText += `\n${file.name}:\n`; + + const result = await cvRouter.capture(file, "ReadBarcodes_ReadRateFirst"); + const items = result.decodedBarcodesResult?.barcodeResultItems ?? []; + + if (!items.length) { + resultsDiv.innerText += "No barcode found\n"; + continue; + } + for (let item of items) { + resultsDiv.innerText += `${item.formatString}: ${item.text}\n`; + } + } +}); +``` + +--- + +## Creating `CaptureVisionRouter` Once vs Per-Request + +**Create once, reuse for every decode** — router creation is expensive: + +```js +// Good: create once, use many times +const pInit = CaptureVisionRouter.createInstance(); + +async function decodeFile(file) { + const cvRouter = await pInit; + return cvRouter.capture(file, "ReadBarcodes_ReadRateFirst"); +} +``` + +**In React/Vue:** use a ref to hold the pending promise and check `isDestroyed` before use: + +```ts +// React +let pCvRouter = useRef | null>(null); +const cvRouter = await (pCvRouter.current = pCvRouter.current || CaptureVisionRouter.createInstance()); + +// Vue +let pCvRouter: Promise; +const cvRouter = await (pCvRouter = pCvRouter || CaptureVisionRouter.createInstance()); +``` + +--- + +## Disposal (Image Capture) + +For image-only workflows (no camera), dispose the router in component teardown: + +```ts +// React +useEffect(() => { + return () => { + isDestroyed.current = true; + pCvRouter.current?.then(r => r.dispose()).catch(() => {}); + }; +}, []); + +// Vue +onBeforeUnmount(async () => { + isDestroyed = true; + if (pCvRouter) { + try { (await pCvRouter).dispose(); } catch (_) {} + } +}); +``` + +--- + +## Accepted Image Formats + +The SDK accepts the same formats as the browser's ``: + +``` +.jpg, .jpeg, .png, .bmp, .gif, .svg, .webp, .ico +``` + +Use `accept=".jpg,.jpeg,.icon,.gif,.svg,.webp,.png,.bmp"` on file inputs (matches existing samples). diff --git a/.github/skills/dbr-js-sample-creator/references/api-sdk.md b/.github/skills/dbr-js-sample-creator/references/api-sdk.md new file mode 100644 index 00000000..258de216 --- /dev/null +++ b/.github/skills/dbr-js-sample-creator/references/api-sdk.md @@ -0,0 +1,175 @@ +# DBR JavaScript Sample Creator — API Reference: SDK Loading & Core + +**SDK version:** `dynamsoft-barcode-reader-bundle@11.4.2001` + +--- + +## Loading the SDK + +### UMD Bundle (Plain HTML, no build step) + +```html + + + + + +``` + +All classes are available as globals under the `Dynamsoft` namespace after the script loads. + +### ES Module Bundle (HTML with ` + + +

Hello World (Scan Barcode via Camera)

+
+
+ + + + + + +``` + +--- + +## Pattern 2: Scan a Single Barcode (stop-on-first) + +Source: `scan-a-single-barcode.html` + +Key differences from Pattern 1: +- Shows a scan button; camera is hidden until button clicked. +- Stops after the first barcode is found and alerts the text. +- Uses `"ReadSingleBarcode"` template. + +```html + +
+ + +``` + +--- + +## Pattern 3: Read Barcodes from Image File + +Source: `read-an-image.html` + +```html + + +
+ + +``` + +--- + +## Pattern 4: ES6 Module (import from CDN .mjs) + +Source: `frameworks/es6/es6.html` + +```html + +``` + +--- + +## Pattern 5: Custom JSON Template (Scan QR Codes Only) + +Source: `scenarios/scan-qr-code/` + +Load a custom template JSON and use it by name: + +```js +Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); +(async () => { + try { + const cameraView = await Dynamsoft.DCE.CameraView.createInstance(); + const cameraEnhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance(cameraView); + const cvRouter = await Dynamsoft.CVR.CaptureVisionRouter.createInstance(); + + cvRouter.setInput(cameraEnhancer); + + const resultReceiver = new Dynamsoft.CVR.CapturedResultReceiver(); + resultReceiver.onDecodedBarcodesReceived = (result) => { + for (let item of result.barcodeResultItems) { + console.log(item.formatString, item.text); + } + }; + await cvRouter.addResultReceiver(resultReceiver); + + // Load custom template from JSON file, then use the template name from the JSON + await cvRouter.initSettings("./ReadQR.json"); + + document.querySelector(".barcode-scanner-view").append(cameraView.getUIElement()); + await cameraEnhancer.open(); + await cvRouter.startCapturing("ReadQR"); // name matches the template in ReadQR.json + } catch (ex) { + alert(ex.message || ex); + } +})(); +``` + +Minimal custom JSON template to read only QR codes: +```json +{ + "CaptureVisionTemplates": [ + { "Name": "ReadQR", "ImageROIProcessingNameArray": ["ROI_QR"], "Timeout": 1000 } + ], + "TargetROIDefOptions": [ + { "Name": "ROI_QR", "TaskSettingNameArray": ["Task_QR"] } + ], + "BarcodeReaderTaskSettingOptions": [ + { "Name": "Task_QR", "BarcodeFormatIds": ["BF_QR_CODE"], "ExpectedBarcodesCount": 1 } + ] +} +``` + +--- + +## Pattern 6: React VideoCapture (npm, TypeScript) + +Source: `frameworks/react/src/components/VideoCapture/VideoCapture.tsx` + +See full code in `references/api-frameworks.md` → "React → VideoCapture Component". + +**Critical React-specific rules:** +- Use `useEffect` with a cleanup return function. +- Set `isDestroyed = true` in the cleanup, check it after every `await`. +- Create a `pInit` Promise before the async IIFE; resolve it at the end of the IIFE. +- In cleanup, `await pInit` before calling `dispose()` to avoid disposing mid-init. +- Use `useRef` for the camera container DOM element. + +--- + +## Pattern 7: React ImageCapture (npm, TypeScript) + +Source: `frameworks/react/src/components/ImageCapture/ImageCapture.tsx` + +See full code in `references/api-frameworks.md` → "React → ImageCapture Component". + +**Critical points:** +- Use a `useRef | null>(null)` to lazily create and cache the router. +- Reset `this.value = ""` on the file input so the same file can be re-selected. +- Use `EnumCapturedResultItemType.CRIT_BARCODE` to filter `result.items`. + +--- + +## Pattern 8: Vue 3 VideoCapture (npm, TypeScript) + +Source: `frameworks/vue/src/components/VideoCapture.vue` + +See full code in `references/api-frameworks.md` → "Vue 3 → VideoCapture Component". + +**Key Vue differences vs React:** +- `onMounted` replaces `useEffect`, `onBeforeUnmount` replaces the cleanup return. +- Module-level `let isDestroyed = false` (not a ref) — works because Vue components are instances. +- `resultText` is a `ref("")` — access/set via `.value`. +- Camera container is a template ref: `const cameraViewContainer = ref(null)`. +- Access DOM via `cameraViewContainer.value!.append(...)`. + +--- + +## Common Mistakes to Avoid + +| Mistake | Fix | +|---|---| +| Calling `await LicenseManager.initLicense(...)` | Remove `await` — it's synchronous | +| Creating `CaptureVisionRouter` inside event handlers | Create once at init, reuse | +| Forgetting `isDestroyed` check after `await` in React | Add check after every `await` in component | +| Using `document.querySelector` inside Vue/React components | Use refs instead | +| Not resetting `e.target.value = ""` on file input | Reset so same file can be re-selected | +| Not calling `cameraView.getUIElement()` and appending to DOM | Required before `open()` | +| Calling `startCapturing` before `setInput` | Always `setInput` first | +| Forgetting `CoreModule.engineResourcePaths.rootDirectory` in npm/ES module projects | Add to `dynamsoft.config.ts` | From ab051a0d6cb5391763ba06f6ff3e1c5acd675c89 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 20 May 2026 16:46:06 +0800 Subject: [PATCH 2/9] Update license string in SDK documentation to reflect new default key --- .github/skills/dbr-js-sample-creator/SKILL.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/skills/dbr-js-sample-creator/SKILL.md b/.github/skills/dbr-js-sample-creator/SKILL.md index 27387404..6758e2c3 100644 --- a/.github/skills/dbr-js-sample-creator/SKILL.md +++ b/.github/skills/dbr-js-sample-creator/SKILL.md @@ -92,7 +92,7 @@ CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; **Always call `initLicense()` before any other SDK API.** It is synchronous (non-blocking) — do not await it. -**Default public trial license key:** `"DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"` +**License string priority:** Default hard-coded license string is `"DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"` **Required comment block** to accompany every license call: @@ -103,13 +103,13 @@ not await it. * to get your own trial license good for 30 days. * LICENSE ALERT - THE END */ -LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); +LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); ``` For the CDN UMD bundle, use the global: ```js -Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); +Dynamsoft.License.LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); ``` ## Preset Templates @@ -132,7 +132,7 @@ await cvRouter.startCapturing("MyTemplateName"); ## Camera Scanning Pattern (UMD) ```js -Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); +Dynamsoft.License.LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); (async () => { const cameraView = await Dynamsoft.DCE.CameraView.createInstance(); const cameraEnhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance(cameraView); @@ -162,7 +162,7 @@ Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMD ## Image/File Capture Pattern (UMD) ```js -Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); +Dynamsoft.License.LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); const pInit = (async () => { const cvRouter = await Dynamsoft.CVR.CaptureVisionRouter.createInstance(); return { cvRouter }; @@ -220,7 +220,7 @@ CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; * to get your own trial license good for 30 days. * LICENSE ALERT - THE END */ -LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); +LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); // Optional: preload WASM to reduce latency on first decode CoreModule.loadWasm(); From cc5fd8c63e2217a32a05cc9b4b59d87a4727c07c Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 22 May 2026 17:57:49 +0800 Subject: [PATCH 3/9] Add AI coding skill section to README for enhanced developer guidance --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index 81601dcd..46f88512 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,36 @@ The repository includes two main sample directories: --- +## Vibe Coding with AI Agents + +This repository ships an **AI coding skill** that gives GitHub Copilot, Claude, and Codex deep knowledge of the Dynamsoft Barcode Reader JavaScript SDK — so you can describe what you want and get working code instantly. + +### How It Works + +The skill lives in: + +``` +.github/skills/dbr-js-sample-creator/ ← GitHub Copilot (auto-discovered) +.claude/skills/dbr-js-sample-creator/ ← Claude Code (auto-discovered) +.codex/skills/dbr-js-sample-creator/ ← OpenAI Codex (auto-discovered) +``` + +No installation or configuration needed — just clone the repo and open it in your AI-enabled editor. + +### Example Prompts + +Open your AI agent (e.g. GitHub Copilot Chat in agent mode) and try: + +- *"Create a plain HTML barcode scanner that reads from camera and shows results"* +- *"Build a React component that scans barcodes with proper cleanup"* +- *"Make a Vue 3 page that decodes barcodes from an uploaded image"* +- *"Write an ES6 module barcode scanner using the CDN .mjs bundle"* +- *"Scan QR codes only using a custom JSON template"* + +The agent will automatically load the skill and generate correct, production-ready code using the right SDK version, license key, API patterns, and framework conventions — without you needing to read any documentation. + +--- + ## Documentation For the developer guide and full API reference of Dynamsoft Barcode Reader JavaScript library, please check out the [documentation](https://www.dynamsoft.com/barcode-reader/docs/web/programming/javascript/?ver=11.4.2001&utm_source=sampleReadme). From 8ed0e434a9f4c1f1b678cdb15ce2e9b8c5b07f26 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 26 May 2026 17:29:16 +0800 Subject: [PATCH 4/9] Enhance scan & search sample; add filter options for barcode formats, text length & Rex --- scenarios/scan-and-search/index.css | 482 +++++++++++++++++++++++---- scenarios/scan-and-search/index.html | 428 +++++++++++++++++++++--- 2 files changed, 798 insertions(+), 112 deletions(-) diff --git a/scenarios/scan-and-search/index.css b/scenarios/scan-and-search/index.css index cbbce166..2507e411 100644 --- a/scenarios/scan-and-search/index.css +++ b/scenarios/scan-and-search/index.css @@ -12,32 +12,130 @@ body { align-items: center; flex-direction: column; min-height: 100vh; - padding: 10px; + padding: 20px 10px; margin: 0; } -#container { - display: flex; - align-items: center; +/* ── Camera view (always visible, inline) ────────────────────────────────── */ + +#barcode-scanner-view { + position: relative; width: 100%; max-width: 400px; + height: 240px; + margin-bottom: 8px; + border-radius: 8px; + overflow: hidden; + background: #111; } -#scan-btn { - width: 48px; - height: 48px; - background-color: #ff5000; - border: none; - border-radius: 6px 0 0 6px; +/* Transparent overlay that catches clicks for play/pause */ +#camera-toggle-area { + position: absolute; + inset: 0; display: flex; align-items: center; justify-content: center; cursor: pointer; - transition: background-color 0.3s ease; + z-index: 10; } -#scan-btn:hover { - background-color: #e54c00; +/* Play/pause icon — always visible when paused, shown on hover when playing */ +#play-pause-icon { + width: 52px; + height: 52px; + border-radius: 50%; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 0.2s; + pointer-events: none; +} + +/* Loading spinner — shown while SDK initialises */ +#loading-spinner { + display: none; + width: 48px; + height: 48px; + border: 4px solid rgba(255, 255, 255, 0.85); + border-right-color: transparent; + border-radius: 50%; + animation: spin 0.9s linear infinite; + pointer-events: none; +} + +#camera-toggle-area.loading { + cursor: default; +} + +#camera-toggle-area.loading #loading-spinner { + display: block; +} + +#camera-toggle-area.loading #play-pause-icon { + display: none; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Pause symbol (two bars) — shown when playing */ +#play-pause-icon::before, +#play-pause-icon::after { + content: ""; + display: block; + width: 5px; + height: 18px; + background: #fff; + border-radius: 2px; + position: absolute; +} + +#play-pause-icon::before { transform: translateX(-5px); } +#play-pause-icon::after { transform: translateX(5px); } + +/* Play symbol (triangle) — shown when paused */ +#camera-toggle-area.paused #play-pause-icon::before, +#camera-toggle-area.paused #play-pause-icon::after { + content: none; +} + +#camera-toggle-area.paused #play-pause-icon { + opacity: 1; + background: rgba(0, 0, 0, 0.55); + /* Triangle via border trick */ + position: relative; +} + +#camera-toggle-area.paused #play-pause-icon::before { + content: ""; + display: block; + width: 0; + height: 0; + border-style: solid; + border-width: 10px 0 10px 18px; + border-color: transparent transparent transparent #fff; + background: none; + border-radius: 0; + transform: translateX(3px); + position: static; +} + +/* Show pause icon on hover when playing */ +#camera-toggle-area:not(.paused):hover #play-pause-icon { + opacity: 1; +} + +/* ── Input bar + submit button ───────────────────────────────────────────── */ + +#container { + display: flex; + align-items: center; + width: 100%; + max-width: 400px; } #text-input { @@ -46,22 +144,28 @@ body { padding: 0 12px; font-size: 16px; border: 1px solid #ccc; - border-left: none; border-right: none; + border-radius: 6px 0 0 6px; box-sizing: border-box; } +#text-input:focus { + outline: none; + border-color: #ff5000; +} + #search-btn { width: 48px; height: 48px; background-color: #ff5000; + color: #fff; border: none; border-radius: 0 6px 6px 0; display: flex; align-items: center; justify-content: center; cursor: pointer; - transition: background-color 0.3s ease; + transition: background-color 0.2s; } #search-btn:hover { @@ -89,76 +193,302 @@ body { transform-origin: center; } -.scan-icon { - position: relative; - width: 20px; - height: 20px; +h1 { + text-align: center; + margin-bottom: 6px; } -.scan-icon::before { - content: ""; - position: absolute; - left: 0; - right: 0; - top: 50%; - height: 2px; - background-color: white; - transform: translateY(-50%); +h2 { + text-align: center; + font-size: 14px; + font-weight: normal; + color: #666; + margin-bottom: 20px; + max-width: 400px; } -.corner { - position: absolute; - width: 5px; - height: 5px; - border: 2px solid white; -} +/* ── Filter toggle row ───────────────────────────────────────────────────── */ -.tl { - top: 0; - left: 0; - border-right: none; - border-bottom: none; +#filter-row { + display: flex; + justify-content: flex-end; + width: 100%; + max-width: 400px; + margin-top: 8px; } -.tr { - top: 0; - right: 0; - border-left: none; - border-bottom: none; +#filter-toggle-btn { + display: flex; + align-items: center; + gap: 5px; + padding: 5px 12px; + border: 1px solid #ddd; + border-radius: 20px; + background: #f8f8f8; + font-size: 13px; + color: #555; + cursor: pointer; + transition: background 0.2s, border-color 0.2s, color 0.2s; } -.bl { - bottom: 0; - left: 0; - border-right: none; - border-top: none; +#filter-toggle-btn:hover { + background: #f0f0f0; + border-color: #bbb; } -.br { - bottom: 0; - right: 0; - border-left: none; - border-top: none; +#filter-toggle-btn.open { + background: #fff3ee; + border-color: #ff5000; + color: #ff5000; } -h2 { - margin: 20px 0; +#filter-badge { + color: #ff5000; + font-size: 10px; } -#barcode-scanner-view { - position: fixed; - left: 0; - top: 0; +/* ── Filter panel ────────────────────────────────────────────────────────── */ + +#filters-panel { width: 100%; - height: 100vh; - display: none; - background-color: rgba(0, 0, 0, 0.8); - z-index: 9999; + max-width: 400px; + border: 1px solid #e0e0e0; + border-radius: 8px; + padding: 14px 16px; + background: #fafafa; + margin-top: 6px; } +.filter-section { + display: flex; + flex-direction: column; + gap: 8px; + padding: 12px 0; +} + +.filter-section:first-child { + padding-top: 0; +} + +.filter-section:last-child { + padding-bottom: 0; +} + +.filter-section + .filter-section { + border-top: 1px solid #ebebeb; +} + +.filter-section-title { + font-size: 11px; + font-weight: 700; + color: #888; + text-transform: uppercase; + letter-spacing: 0.6px; +} + +.filter-hint { + font-size: 11px; + color: #bbb; + margin-top: -4px; +} + +/* ── Format presets ──────────────────────────────────────────────────────── */ + +.preset-group { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.preset-btn { + padding: 4px 10px; + border: 1px solid #ddd; + border-radius: 4px; + background: #fff; + font-size: 12px; + color: #555; + cursor: pointer; + transition: background 0.15s, border-color 0.15s, color 0.15s; +} + +.preset-btn:hover { + background: #f5f5f5; + border-color: #bbb; +} + +.preset-btn.active { + background: #ff5000; + border-color: #ff5000; + color: #fff; +} + +/* ── Custom formats grid ─────────────────────────────────────────────────── */ + +#custom-formats-wrapper { + margin-top: 4px; +} + +#custom-formats-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); + gap: 6px; +} + +.format-checkbox-label { + display: flex; + align-items: center; + gap: 5px; + font-size: 12px; + color: #444; + cursor: pointer; +} + +.format-checkbox-label input[type="checkbox"] { + accent-color: #ff5000; + width: 14px; + height: 14px; + cursor: pointer; + flex-shrink: 0; +} + +/* ── Length filter ───────────────────────────────────────────────────────── */ + +.length-inputs { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.length-label { + display: flex; + align-items: center; + gap: 6px; + font-size: 13px; + color: #555; +} + +.length-label input[type="number"] { + width: 70px; + height: 32px; + padding: 0 8px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 13px; + text-align: center; + -moz-appearance: textfield; +} + +.length-label input[type="number"]::-webkit-outer-spin-button, +.length-label input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; +} + +.length-label input[type="number"]:focus { + outline: none; + border-color: #ff5000; +} + +.length-dash { + color: #bbb; + font-size: 16px; +} + +.length-unit { + font-size: 13px; + color: #999; +} + +/* ── Keyword rules ───────────────────────────────────────────────────────── */ + +#keyword-rules-list { + display: flex; + flex-direction: column; + gap: 6px; +} + +.rule-row { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; +} + +.rule-type-select { + height: 32px; + padding: 0 6px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 12px; + color: #444; + background: #fff; + cursor: pointer; + flex-shrink: 0; +} + +.rule-type-select:focus { + outline: none; + border-color: #ff5000; +} + +.rule-value-input { + flex: 1; + min-width: 80px; + height: 32px; + padding: 0 8px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 13px; +} + +.rule-value-input:focus { + outline: none; + border-color: #ff5000; +} + +.rule-remove-btn { + width: 28px; + height: 28px; + border: none; + background: none; + color: #bbb; + font-size: 14px; + cursor: pointer; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + transition: background 0.15s, color 0.15s; +} + +.rule-remove-btn:hover { + background: #fef0f0; + color: #c00; +} + +.add-rule-btn { + align-self: flex-start; + padding: 5px 12px; + border: 1px dashed #ddd; + border-radius: 4px; + background: #fff; + font-size: 12px; + color: #777; + cursor: pointer; + transition: border-color 0.15s, color 0.15s; +} + +.add-rule-btn:hover { + border-color: #ff5000; + color: #ff5000; +} + +/* ── Result textarea ─────────────────────────────────────────────────────── */ + textarea { width: 100%; - max-width: 380px; + max-width: 400px; height: 300px; margin-top: 20px; padding: 10px; @@ -174,12 +504,11 @@ textarea:focus { outline: none; } -@media (max-width: 480px) { +/* ── Responsive ──────────────────────────────────────────────────────────── */ - #scan-btn, - #search-btn { - width: 40px; - height: 40px; +@media (max-width: 480px) { + #barcode-scanner-view { + height: 200px; } #text-input { @@ -187,8 +516,21 @@ textarea:focus { font-size: 14px; } + #search-btn { + width: 40px; + height: 40px; + } + textarea { height: 200px; font-size: 14px; } + + .rule-row { + flex-wrap: wrap; + } + + .rule-type-select { + width: 100%; + } } \ No newline at end of file diff --git a/scenarios/scan-and-search/index.html b/scenarios/scan-and-search/index.html index a08d3291..935a2e0d 100644 --- a/scenarios/scan-and-search/index.html +++ b/scenarios/scan-and-search/index.html @@ -14,16 +14,74 @@

📦 Instant Product Information Lookup

-

Enter a UPC, EAN, or ISBN number to retrieve accurate product data from a specific database

-
- +
+ + + + + +
-
From 2eb38c2d465270263194fec44dbccaae24dc7a6c Mon Sep 17 00:00:00 2001 From: Tom Kent Date: Fri, 5 Jun 2026 13:39:08 -0700 Subject: [PATCH 5/9] Fix license key inconsistency; add BarcodeFormatIds reference and dynamic settings API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace incorrect license key in SKILL.md (5 occurrences) to match all reference files and actual samples - Add complete EnumBarcodeFormat table (45+ BF_* values) to api-sdk.md - Add common format presets for templates (retail, industrial, 2D) - Add dynamic settings modification pattern (outputSettings → modify → initSettings) - Document custom template usage with capture() for image decoding - Note framework template placement (public/ directory) --- .github/skills/dbr-js-sample-creator/SKILL.md | 12 +- .../references/api-sdk.md | 129 ++++++++++++++++++ 2 files changed, 135 insertions(+), 6 deletions(-) diff --git a/.github/skills/dbr-js-sample-creator/SKILL.md b/.github/skills/dbr-js-sample-creator/SKILL.md index 6758e2c3..23d60985 100644 --- a/.github/skills/dbr-js-sample-creator/SKILL.md +++ b/.github/skills/dbr-js-sample-creator/SKILL.md @@ -92,7 +92,7 @@ CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; **Always call `initLicense()` before any other SDK API.** It is synchronous (non-blocking) — do not await it. -**License string priority:** Default hard-coded license string is `"DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"` +**License string priority:** Default hard-coded license string is `"DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"` **Required comment block** to accompany every license call: @@ -103,13 +103,13 @@ not await it. * to get your own trial license good for 30 days. * LICENSE ALERT - THE END */ -LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); +LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); ``` For the CDN UMD bundle, use the global: ```js -Dynamsoft.License.LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); +Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); ``` ## Preset Templates @@ -132,7 +132,7 @@ await cvRouter.startCapturing("MyTemplateName"); ## Camera Scanning Pattern (UMD) ```js -Dynamsoft.License.LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); +Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); (async () => { const cameraView = await Dynamsoft.DCE.CameraView.createInstance(); const cameraEnhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance(cameraView); @@ -162,7 +162,7 @@ Dynamsoft.License.LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMD ## Image/File Capture Pattern (UMD) ```js -Dynamsoft.License.LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); +Dynamsoft.License.LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); const pInit = (async () => { const cvRouter = await Dynamsoft.CVR.CaptureVisionRouter.createInstance(); return { cvRouter }; @@ -220,7 +220,7 @@ CoreModule.engineResourcePaths.rootDirectory = "https://cdn.jsdelivr.net/npm/"; * to get your own trial license good for 30 days. * LICENSE ALERT - THE END */ -LicenseManager.initLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTEwNTI2NzQwMSJ9"); +LicenseManager.initLicense("DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"); // Optional: preload WASM to reduce latency on first decode CoreModule.loadWasm(); diff --git a/.github/skills/dbr-js-sample-creator/references/api-sdk.md b/.github/skills/dbr-js-sample-creator/references/api-sdk.md index 258de216..2d4abc91 100644 --- a/.github/skills/dbr-js-sample-creator/references/api-sdk.md +++ b/.github/skills/dbr-js-sample-creator/references/api-sdk.md @@ -173,3 +173,132 @@ Load and use: await cvRouter.initSettings("./template.json"); // or pass JSON string await cvRouter.startCapturing("MyTemplate"); // use template name from JSON ``` + +**In framework projects (Next.js, React, etc.):** place the template JSON in `public/` so it's +served at a URL. Then reference it as `"/MyTemplate.json"` (absolute path from web root). + +Custom template names also work with `cvRouter.capture()` for image decoding: +```js +const result = await cvRouter.capture(file, "MyTemplate"); // uses the custom template +``` + +--- + +## BarcodeFormatIds Reference + +Use these strings in custom template JSON (`BarcodeFormatIds` array) or with `SimplifiedBarcodeReaderSettings`. + +### Common 1D Formats + +| Format ID | Description | +|---|---| +| `BF_CODE_128` | Code 128 | +| `BF_CODE_39` | Code 39 | +| `BF_CODE_93` | Code 93 | +| `BF_CODABAR` | Codabar | +| `BF_ITF` | Interleaved 2 of 5 | +| `BF_EAN_13` | EAN-13 | +| `BF_EAN_8` | EAN-8 | +| `BF_UPC_A` | UPC-A | +| `BF_UPC_E` | UPC-E | +| `BF_INDUSTRIAL_25` | Industrial 2 of 5 | +| `BF_CODE_39_EXTENDED` | Code 39 Extended | +| `BF_CODE_11` | Code 11 | +| `BF_MSI_CODE` | MSI Code | +| `BF_CODE_32` | Code 32 | + +### Common 2D Formats + +| Format ID | Description | +|---|---| +| `BF_QR_CODE` | QR Code | +| `BF_PDF417` | PDF417 | +| `BF_DATAMATRIX` | DataMatrix | +| `BF_AZTEC` | Aztec | +| `BF_MICRO_QR` | Micro QR Code | +| `BF_MICRO_PDF417` | Micro PDF417 | +| `BF_MAXICODE` | MaxiCode | +| `BF_DOTCODE` | DotCode | +| `BF_PATCHCODE` | Patch Code | +| `BF_GS1_COMPOSITE` | GS1 Composite Code | + +### GS1 DataBar Formats + +| Format ID | Description | +|---|---| +| `BF_GS1_DATABAR_OMNIDIRECTIONAL` | GS1 DataBar Omnidirectional | +| `BF_GS1_DATABAR_TRUNCATED` | GS1 DataBar Truncated | +| `BF_GS1_DATABAR_STACKED` | GS1 DataBar Stacked | +| `BF_GS1_DATABAR_STACKED_OMNIDIRECTIONAL` | GS1 DataBar Stacked Omnidirectional | +| `BF_GS1_DATABAR_EXPANDED` | GS1 DataBar Expanded | +| `BF_GS1_DATABAR_EXPANDED_STACKED` | GS1 DataBar Expanded Stacked | +| `BF_GS1_DATABAR_LIMITED` | GS1 DataBar Limited | + +### Postal Formats + +| Format ID | Description | +|---|---| +| `BF_USPSINTELLIGENTMAIL` | USPS Intelligent Mail | +| `BF_POSTNET` | Postnet | +| `BF_PLANET` | Planet | +| `BF_AUSTRALIANPOST` | Australian Post | +| `BF_RM4SCC` | Royal Mail 4-State | +| `BF_KIX` | KIX | + +### Convenience Groups + +| Format ID | Description | +|---|---| +| `BF_ALL` | All supported formats | +| `BF_ONED` | All 1D formats combined | +| `BF_GS1_DATABAR` | All GS1 DataBar formats combined | +| `BF_POSTALCODE` | All postal formats combined | +| `BF_DEFAULT` | Default set of formats | +| `BF_NULL` | No format (disable) | + +### Common Format Presets for Templates + +```json +// Retail 1D +"BarcodeFormatIds": ["BF_EAN_13", "BF_EAN_8", "BF_UPC_A", "BF_UPC_E"] + +// Industrial 1D +"BarcodeFormatIds": ["BF_CODE_128", "BF_CODE_39", "BF_CODE_93", "BF_ITF", "BF_CODABAR"] + +// 2D only +"BarcodeFormatIds": ["BF_QR_CODE", "BF_PDF417", "BF_DATAMATRIX"] + +// Single format +"BarcodeFormatIds": ["BF_QR_CODE"] +``` + +--- + +## Dynamic Settings Modification + +Read the current settings, modify them programmatically, and re-apply — useful for runtime +barcode format, text length, or regex filters: + +```js +// 1. Get the current settings as a JSON object +const settings = await cvRouter.outputSettings("ReadBarcodes_SpeedFirst", true); + +// 2. Modify barcode format filter +settings.BarcodeReaderTaskSettingOptions[0].BarcodeFormatIds = ["BF_QR_CODE", "BF_CODE_128"]; + +// 3. Modify text length filter +settings.BarcodeFormatSpecificationOptions[0].BarcodeTextLengthRangeArray = [ + { MinValue: 5, MaxValue: 50 } +]; + +// 4. Modify regex filter +settings.BarcodeFormatSpecificationOptions[0].BarcodeTextRegExPattern = "^[A-Z0-9]+$"; + +// 5. Re-apply the modified settings +await cvRouter.initSettings(settings); + +// 6. Start capturing with the modified template name +await cvRouter.startCapturing("ReadBarcodes_SpeedFirst"); +``` + +This approach works for both camera scanning (`startCapturing`) and image decoding (`capture`). From b6be72016a5534e0ce2f0dd3a4156472d71589d7 Mon Sep 17 00:00:00 2001 From: Tom Kent Date: Fri, 5 Jun 2026 13:44:24 -0700 Subject: [PATCH 6/9] Add advanced DCE APIs to api-camera.md - Camera selection (getAllCameras, selectCamera) - Resolution control (setResolution, getResolution) - Enhanced features (auto-zoom, tap-to-focus) - Video fit mode, single frame mode - Coordinate conversion (convertToPageCoordinates, convertToClientCoordinates) - Tip messages (setTipConfig, updateTipMessage, setTipVisible, TipConfig interface) - Audio feedback (Feedback.beep) - Drawing layers and custom overlays (DrawingLayer, DrawingStyleManager) - UI element customization (hide camera/resolution dropdowns) --- .../references/api-camera.md | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/.github/skills/dbr-js-sample-creator/references/api-camera.md b/.github/skills/dbr-js-sample-creator/references/api-camera.md index d4afc780..f6f2aa6e 100644 --- a/.github/skills/dbr-js-sample-creator/references/api-camera.md +++ b/.github/skills/dbr-js-sample-creator/references/api-camera.md @@ -43,6 +43,76 @@ await cameraEnhancer.close(); cameraEnhancer.dispose(); ``` +### Camera Selection + +```js +// List all available cameras +const cameras = await cameraEnhancer.getAllCameras(); +// cameras = [{ deviceId: "...", label: "Front Camera" }, ...] + +// Switch to a specific camera +await cameraEnhancer.selectCamera(cameras[1]); + +// Get currently selected camera +const current = cameraEnhancer.getSelectedCamera(); +``` + +### Resolution + +```js +// Set resolution (before or after open) +await cameraEnhancer.setResolution({ width: 1920, height: 1080 }); + +// Get current resolution +const res = cameraEnhancer.getResolution(); // { width, height } +``` + +### Enhanced Features + +```js +// Enable auto-zoom for distant barcodes +cameraEnhancer.enableEnhancedFeatures(Dynamsoft.DCE.EnumEnhancedFeatures.EF_AUTO_ZOOM); +// (ES module: import { EnumEnhancedFeatures } from "dynamsoft-barcode-reader-bundle") + +// Disable auto-zoom +cameraEnhancer.disableEnhancedFeatures(Dynamsoft.DCE.EnumEnhancedFeatures.EF_AUTO_ZOOM); +``` + +Available enhanced features: + +- `EF_AUTO_ZOOM` — automatically zooms in when a barcode is detected far away +- `EF_TAP_TO_FOCUS` — tap the camera view to focus + +### Video Fit Mode + +```js +// "contain" (default) or "cover" +cameraEnhancer.setVideoFit("cover"); +``` + +### Single Frame Mode + +Toggle between camera video and image upload input: + +```js +cameraEnhancer.singleFrameMode = "image"; // show file picker instead of camera +cameraEnhancer.singleFrameMode = "disabled"; // back to camera video +``` + +### Coordinate Conversion + +Convert barcode location points from video coordinates to page/viewport coordinates: + +```js +// Convert to page coordinates (relative to document) +const pagePoint = cameraEnhancer.convertToPageCoordinates(item.location.points[0]); + +// Convert to client coordinates (relative to viewport) +const clientPoint = cameraEnhancer.convertToClientCoordinates(item.location.points[0]); +``` + +Useful for positioning custom overlays (tooltips, labels) on top of detected barcodes. + --- ## CaptureVisionRouter (camera usage) @@ -195,3 +265,144 @@ Always follow this exact sequence to avoid race conditions: 8. `cameraEnhancer.open()` — start video stream 9. `cameraView.setScanLaserVisible(true)` — optional visual indicator 10. `cvRouter.startCapturing(template)` — begin decoding + +--- + +## Tip Messages + +Show guidance text overlaid on the camera view (e.g., "Move closer to the barcode"). +Methods are on `CameraView` (inherited from the abstract `View` class). + +### TipConfig + +```ts +interface TipConfig { + topLeftPoint: { x: number; y: number }; // position of tip box + width: number; // tip box width + duration: number; // auto-hide after N milliseconds + coordinateBase?: "view" | "image"; // coordinate system (default: "view") +} +``` + +### Methods + +```js +// Configure tip position, size, and auto-hide duration +cameraView.setTipConfig({ + topLeftPoint: { x: 50, y: 50 }, + width: 200, + duration: 3000, +}); + +// Get current config +const config = cameraView.getTipConfig(); + +// Show or hide the tip +cameraView.setTipVisible(true); +cameraView.setTipVisible(false); + +// Check if visible +const visible = cameraView.isTipVisible(); + +// Update the displayed message +cameraView.updateTipMessage("Hold the phone closer to the barcode."); +``` + +### Typical Usage — Show Tips Based on Scan Results + +Tips are **not auto-triggered** by the SDK in JavaScript. Control them manually in your +result callback: + +```js +resultReceiver.onDecodedBarcodesReceived = (result) => { + if (!result.barcodeResultItems.length) { + // No barcode detected — show guidance + cameraView.updateTipMessage("Move closer to the barcode."); + cameraView.setTipVisible(true); + } else { + cameraView.setTipVisible(false); + // process results... + } +}; +``` + +--- + +## Audio Feedback + +Play a beep sound on successful scan: + +```js +// UMD +Dynamsoft.DCE.Feedback.beep(); + +// ES module / npm +import { Feedback } from "dynamsoft-barcode-reader-bundle"; +Feedback.beep(); +``` + +Typical usage inside a result callback: + +```js +resultReceiver.onDecodedBarcodesReceived = (result) => { + if (!result.barcodeResultItems.length) return; + Dynamsoft.DCE.Feedback.beep(); + // process results... +}; +``` + +--- + +## Drawing Layers and Overlays + +Draw custom visual elements (icons, text, shapes) on the camera view overlay. + +### Default Drawing Layers + +The SDK auto-creates drawing layers for barcode bounding boxes. Access by index: + +```js +// Layer 2 is the default barcode result overlay +cameraView.getDrawingLayer(2).setVisible(false); // hide default overlays +``` + +### Custom Drawing Layer + +```js +// Create a new layer for custom overlays +const customLayer = cameraView.createDrawingLayer(); + +// Create a custom drawing style +const styleId = Dynamsoft.DCE.DrawingStyleManager.createDrawingStyle({ + strokeStyle: "rgba(255, 0, 0, 1)", + fillStyle: "rgba(255, 0, 0, 0.3)", + lineWidth: 2, +}); + +// Add drawing items (e.g., highlight a barcode location) +const rectItem = new Dynamsoft.DCE.DrawingItem.RectDrawingItem( + { x: 100, y: 100, width: 200, height: 50 }, + styleId +); +customLayer.addDrawingItems([rectItem]); + +// Clear all items +customLayer.clearDrawingItems(); +``` + +--- + +## UI Element Customization + +The default camera UI includes dropdowns for camera and resolution selection. Hide or style +them as needed: + +```js +const uiElement = cameraView.getUIElement(); + +// Hide the camera selector dropdown +uiElement.querySelector(".dce-sel-camera").style.display = "none"; + +// Hide the resolution selector dropdown +uiElement.querySelector(".dce-sel-resolution").style.display = "none"; +``` From 4e575ad32e6d1efec1064e164afcac826a10f51e Mon Sep 17 00:00:00 2001 From: Tom Kent Date: Fri, 5 Jun 2026 13:53:23 -0700 Subject: [PATCH 7/9] Expand Next.js section with full App Router guidance - Replace 4-line stub with complete Next.js App Router documentation - Server vs client component architecture diagram - Page wrapper pattern with next/dynamic and ssr: false - Custom template placement in public/ directory - Server component layout example (no SDK imports) - Next.js-specific rules (5 key points) - Fix MD040 lint warnings (add language to fenced code blocks) --- .../references/api-frameworks.md | 141 +++++++++++++++++- 1 file changed, 135 insertions(+), 6 deletions(-) diff --git a/.github/skills/dbr-js-sample-creator/references/api-frameworks.md b/.github/skills/dbr-js-sample-creator/references/api-frameworks.md index c7c61f35..9b3406da 100644 --- a/.github/skills/dbr-js-sample-creator/references/api-frameworks.md +++ b/.github/skills/dbr-js-sample-creator/references/api-frameworks.md @@ -383,17 +383,146 @@ export class VideoCaptureComponent implements AfterViewInit, OnDestroy { --- -## Next.js +## Next.js (App Router) + +The Dynamsoft SDK is entirely client-side (WASM). Next.js App Router uses **server components +by default**, so you must explicitly opt into client rendering for any component that touches +the SDK. + +### Architecture: Server vs Client Components + +```text +app/ +├── layout.tsx ← server component (navigation, metadata) — no SDK imports +├── page.tsx ← server component (home page) — no SDK imports +├── scanner/ +│ └── page.tsx ← "use client" wrapper with dynamic import +└── upload/ + └── page.tsx ← "use client" wrapper with dynamic import +components/ +├── VideoCapture.tsx ← "use client" — SDK camera scanning logic +└── ImageCapture.tsx ← "use client" — SDK image decoding logic +dynamsoft.config.ts ← SDK init (client-side only, imported by components) +public/ +└── MyTemplate.json ← custom templates served as static files +``` -- Use `"use client"` directive — all SDK code runs client-side only. -- Dynamic import with `ssr: false` for SDK if needed: +**Rule:** `layout.tsx` and top-level `page.tsx` stay as server components. Only the page +wrappers that load SDK components need `"use client"`. -```ts +### Page Wrapper Pattern — `next/dynamic` with `ssr: false` + +The SDK accesses browser APIs (`navigator`, `document`, WASM). Import SDK components with +`next/dynamic` to prevent server-side rendering: + +```tsx +// app/scanner/page.tsx "use client"; -import "../../dynamsoft.config"; // same config as React -// Everything else is identical to the React pattern + +import dynamic from "next/dynamic"; + +const VideoCapture = dynamic(() => import("../../components/VideoCapture"), { + ssr: false, +}); + +export default function ScannerPage() { + return ( +
+

Camera Scanner

+ +
+ ); +} ``` +```tsx +// app/upload/page.tsx +"use client"; + +import dynamic from "next/dynamic"; + +const ImageCapture = dynamic(() => import("../../components/ImageCapture"), { + ssr: false, +}); + +export default function UploadPage() { + return ( +
+

Image Upload

+ +
+ ); +} +``` + +### SDK Components + +The actual `VideoCapture` and `ImageCapture` components are **identical to the React +patterns** shown above, with one addition — add `"use client"` at the top of each file: + +```tsx +// components/VideoCapture.tsx +"use client"; + +import { useEffect, useRef, useState } from "react"; +import "../dynamsoft.config"; +import { CameraEnhancer, CameraView, CaptureVisionRouter, MultiFrameResultCrossFilter } from "dynamsoft-barcode-reader-bundle"; + +// ... rest is identical to the React VideoCapture pattern +``` + +### Custom Templates in Next.js + +Place template JSON files in `public/` — they're served at the web root: + +```text +public/ReadPDF417.json → accessible at "/ReadPDF417.json" +``` + +Load in your component: + +```ts +await cvRouter.initSettings("/ReadPDF417.json"); +await cvRouter.startCapturing("ReadPDF417_SpeedFirst"); // template name from JSON +``` + +### Server Component Layout (no SDK) + +```tsx +// app/layout.tsx — stays as a server component +import type { Metadata } from "next"; +import Link from "next/link"; + +export const metadata: Metadata = { + title: "Barcode Scanner — Dynamsoft", +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + + {children} + + + ); +} +``` + +### Next.js Key Rules + +1. **Never import SDK packages in server components** — they access browser globals. +2. **Always use `dynamic()` with `ssr: false`** for components that import from + `dynamsoft-barcode-reader-bundle`. +3. **`dynamsoft.config.ts` is client-only** — import it inside `"use client"` components only. +4. **Custom templates go in `public/`** and are referenced by absolute URL path. +5. **Everything else follows the React pattern** — `useEffect`, `isDestroyed`, `pInit`, + `useRef`, cleanup/dispose. + --- ## Key Framework Rules From 615564ee8751dcc30808ef493a66d9c20e38ae39 Mon Sep 17 00:00:00 2001 From: Tom Kent Date: Fri, 5 Jun 2026 13:58:09 -0700 Subject: [PATCH 8/9] Add CodeParser reference, update SKILL.md routing table, expand evals - New api-parsing.md: CodeParser API for GS1, driver's license (AAMVA), VIN parsing with complete patterns (loadSpec, parse, onParsedResultsReceived) - Update SKILL.md routing table to include api-parsing.md - Update .claude/skills/ descriptions to reflect new content in all ref files - Expand evals from 5 to 10 test cases covering: Next.js App Router, tip messages + beep, dynamic format filtering, GS1 parsing, auto-zoom --- .claude/skills/dbr-js-sample-creator/SKILL.md | 7 +- .github/skills/dbr-js-sample-creator/SKILL.md | 3 +- .../dbr-js-sample-creator/evals/evals.json | 74 +++++++ .../references/api-parsing.md | 192 ++++++++++++++++++ 4 files changed, 272 insertions(+), 4 deletions(-) create mode 100644 .github/skills/dbr-js-sample-creator/references/api-parsing.md diff --git a/.claude/skills/dbr-js-sample-creator/SKILL.md b/.claude/skills/dbr-js-sample-creator/SKILL.md index 361d6752..709dc77f 100644 --- a/.claude/skills/dbr-js-sample-creator/SKILL.md +++ b/.claude/skills/dbr-js-sample-creator/SKILL.md @@ -25,8 +25,9 @@ The `SKILL.md` file contains: - Code style conventions for this repository The `references/` directory contains: -- `api-sdk.md` — SDK loading, namespaces, CoreModule, preset templates -- `api-camera.md` — CameraView, CameraEnhancer, result receiver, dedup filter +- `api-sdk.md` — SDK loading, namespaces, CoreModule, preset templates, BarcodeFormatIds, dynamic settings +- `api-camera.md` — CameraView, CameraEnhancer, tip messages, auto-zoom, drawing layers, audio feedback - `api-image.md` — File capture, CapturedResult, BarcodeResultItem properties -- `api-frameworks.md` — React, Vue, Angular complete component patterns +- `api-frameworks.md` — React, Vue, Angular, Next.js (App Router) complete component patterns +- `api-parsing.md` — CodeParser for GS1, driver's license (AAMVA), VIN parsing - `sample-patterns.md` — Copy-paste ready code for all common scenarios diff --git a/.github/skills/dbr-js-sample-creator/SKILL.md b/.github/skills/dbr-js-sample-creator/SKILL.md index 23d60985..69658a33 100644 --- a/.github/skills/dbr-js-sample-creator/SKILL.md +++ b/.github/skills/dbr-js-sample-creator/SKILL.md @@ -27,7 +27,8 @@ Read the reference files in `references/` based on the task at hand: | `references/api-sdk.md` | Always — SDK loading, namespaces, CoreModule, LicenseManager | | `references/api-camera.md` | When the task involves live camera/video scanning | | `references/api-image.md` | When the task involves decoding image files or file uploads | -| `references/api-frameworks.md` | When the task targets React, Vue, Angular, or other frameworks | +| `references/api-frameworks.md` | When the task targets React, Vue, Angular, Next.js, or other frameworks | +| `references/api-parsing.md` | When the task involves parsing barcode content (GS1, driver's license, VIN) | | `references/sample-patterns.md` | Always — canonical patterns from existing samples; copy-paste ready | ## SDK Architecture diff --git a/.github/skills/dbr-js-sample-creator/evals/evals.json b/.github/skills/dbr-js-sample-creator/evals/evals.json index 362e72a6..59db04bb 100644 --- a/.github/skills/dbr-js-sample-creator/evals/evals.json +++ b/.github/skills/dbr-js-sample-creator/evals/evals.json @@ -87,6 +87,80 @@ "Sets up camera scanning pipeline (CameraView, CameraEnhancer, cvRouter)", "Adds result receiver and displays barcode text" ] + }, + { + "id": 6, + "prompt": "Build a Next.js 14 App Router page that scans barcodes from a camera, using proper SSR guards.", + "expected_output": "A Next.js App Router page with a 'use client' wrapper that uses next/dynamic with ssr: false to load a VideoCapture component. The component follows the React pattern with useEffect, isDestroyed, pInit, and proper disposal.", + "files": [], + "expectations": [ + "Page file uses 'use client' directive", + "Uses next/dynamic with { ssr: false } to import the VideoCapture component", + "VideoCapture component imports from 'dynamsoft-barcode-reader-bundle' (npm)", + "VideoCapture component imports dynamsoft.config as side effect", + "Uses the full React lifecycle pattern (useEffect, isDestroyed, pInit, dispose)", + "Does NOT import SDK packages in any server component", + "Camera container uses useRef, not document.querySelector" + ] + }, + { + "id": 7, + "prompt": "Create a plain HTML barcode scanner that shows a tip message when no barcode is detected, and plays a beep sound when a barcode is found.", + "expected_output": "An HTML page with camera scanning that uses cameraView.setTipConfig(), cameraView.updateTipMessage(), cameraView.setTipVisible() for guidance, and Dynamsoft.DCE.Feedback.beep() on successful detection.", + "files": [], + "expectations": [ + "Loads SDK from CDN", + "Sets up camera scanning pipeline (CameraView, CameraEnhancer, CaptureVisionRouter)", + "Calls cameraView.setTipConfig() with topLeftPoint, width, and duration", + "In result callback: calls cameraView.updateTipMessage() and cameraView.setTipVisible(true) when no barcodes found", + "In result callback: calls cameraView.setTipVisible(false) when barcodes are found", + "Calls Dynamsoft.DCE.Feedback.beep() on successful barcode detection", + "Adds result receiver and dedup filter" + ] + }, + { + "id": 8, + "prompt": "Write a plain HTML page that scans barcodes and lets the user filter by barcode format (1D retail, 1D industrial, 2D, or all) using a dropdown.", + "expected_output": "An HTML page with a select dropdown for format presets, that uses cvRouter.outputSettings() to get current settings, modifies BarcodeFormatIds, and re-applies with cvRouter.initSettings().", + "files": [], + "expectations": [ + "Loads SDK from CDN", + "Has a