diff --git a/docs/.well-known/agent-skills/fliplet-js-api/SKILL.md b/docs/.well-known/agent-skills/fliplet-js-api/SKILL.md
index 10920288..9b3967e2 100644
--- a/docs/.well-known/agent-skills/fliplet-js-api/SKILL.md
+++ b/docs/.well-known/agent-skills/fliplet-js-api/SKILL.md
@@ -55,7 +55,7 @@ The Fliplet client-side JavaScript API: every Fliplet.X namespace (Storage, User
- [Fliplet.App.Submissions](https://developers.fliplet.com/API/fliplet-app-submissions.md): Read App Store and Google Play submission metadata for the current Fliplet app — version, status, build numbers — via the fliplet-app-submissions package.
- [Fliplet.Media.Audio.Player](https://developers.fliplet.com/API/fliplet-audio-player.md): Embed an audio player UI in a screen by tagging HTML elements with audio URLs; the framework auto-initializes players on screen load.
- [Fliplet.Media.Audio](https://developers.fliplet.com/API/fliplet-audio.md): Play, pause, stop, and seek audio files on device or from a URL in Fliplet apps via the Audio namespace.
-- [Fliplet.Barcode](https://developers.fliplet.com/API/fliplet-barcode.md): Generate QR codes and 1D/2D barcodes on screen, and scan them from the device camera, via the fliplet-barcode package.
+- [Fliplet.Barcode](https://developers.fliplet.com/API/fliplet-barcode.md): Scan QR codes and 1D/2D barcodes from the camera on web and native (attachScanner / scan) and generate barcode images, via the fliplet-barcode package.
- [Fliplet.Chat](https://developers.fliplet.com/API/fliplet-chat.md): Build one-to-one, group, and public-channel chat features. Fliplet.Chat owns the conversations and messages data sources internally — supply only the contacts list (who can chat with whom).
- [Fliplet.Communicate](https://developers.fliplet.com/API/fliplet-communicate.md): Send email, SMS, push notifications, and share URLs from a Fliplet app using a single Communicate namespace.
- [Fliplet.Content()](https://developers.fliplet.com/API/fliplet-content.md): Create, query, update, and delete shared content records (bookmarks, likes, saved searches) backed by a data source via Fliplet.Content.
@@ -103,6 +103,7 @@ The Fliplet client-side JavaScript API: every Fliplet.X namespace (Storage, User
- [V3 app bootstrap constraints](https://developers.fliplet.com/API/v3/app-bootstrap.md): The four constraints every V3 boot HTML must satisfy. Covers Fliplet.require.lazy for dependencies, Fliplet.Media.getContents for source files, the Fliplet().then(...) init sequence, and the locked v…
- [V3 App Settings Convention](https://developers.fliplet.com/API/v3/app-settings.md): V3 app settings convention for storing public and private configuration. Covers the underscore prefix convention for editor-private settings.
- [V3 Authentication Patterns](https://developers.fliplet.com/API/v3/auth.md): V3 authentication patterns for email/password login, session management, logout, and protected routes. Use these patterns when building authentication flows in V3 apps.
+- [V3 barcodes](https://developers.fliplet.com/API/v3/barcode.md): Scan and generate QR codes and barcodes in V3 apps with Fliplet.Barcode — attachScanner() to scan (web + native) and encode() to render barcode images.
- [V3 Alpine.js apps](https://developers.fliplet.com/API/v3/frameworks/alpine.md): Constraints for building V3 apps in Alpine.js. Alpine is attribute-driven HTML with no build step, so it maps cleanly to the V3 runtime. Covers x-init timing relative to Fliplet().then and platform-c…
- [V3 framework guide — picking and setup](https://developers.fliplet.com/API/v3/frameworks/overview.md): Picking a frontend framework for a V3 app. Lists the runtime constraints every framework must cope with (no build step, no bundler, no transpile) and compares Vue, React, Alpine, and vanilla JS again…
- [V3 React apps](https://developers.fliplet.com/API/v3/frameworks/react.md): Constraints for building V3 apps in React. Covers the JSX transpilation problem (the #1 cause of first-deploy failures) with three alternatives, React Router 6 createHashRouter wiring (no basename —…
diff --git a/docs/.well-known/agent-skills/index.json b/docs/.well-known/agent-skills/index.json
index 0c483af1..584b31d9 100644
--- a/docs/.well-known/agent-skills/index.json
+++ b/docs/.well-known/agent-skills/index.json
@@ -130,7 +130,7 @@
"tags": [
"js-api"
],
- "sha256": "c07cf31ad3a44743fc7a6e656685bd8dd20f07bc92fc7a76d6899a77d0092ea5"
+ "sha256": "7d2ee408458c7970f50f21310b0be09aba835bb86c8eeca41a6227bbc4c89530"
},
{
"name": "fliplet-docs-index",
diff --git a/docs/.well-known/llms-full.txt b/docs/.well-known/llms-full.txt
index 65e9d5e3..52bccc83 100644
--- a/docs/.well-known/llms-full.txt
+++ b/docs/.well-known/llms-full.txt
@@ -12596,19 +12596,58 @@ URL: https://developers.fliplet.com/API/fliplet-barcode.md
# `Fliplet.Barcode`
-Scan QR codes and other 1D/2D barcodes from the device camera, and generate barcode images on screen, via the `fliplet-barcode` package. Barcode scanning is only supported in native apps.
+Scan QR codes and other 1D/2D barcodes from the device camera, and generate barcode images on screen, via the `fliplet-barcode` package.
+
+The package offers two ways to scan: `attachScanner()` embeds a scanner in a container in your own UI and works on web and native, while `scan()` opens a ready-made full-screen scanner on native. Use `show()` or `encode()` to generate barcode images. Building a scanning screen in a V3 app? See the [V3 barcode scanning guide](./v3/barcode.md).
## Install
Add the `fliplet-barcode` dependency to your screen or app resources.
+## Fliplet.Barcode.attachScanner()
+
+(Returns a controller object)
+
+`attachScanner()` is a low-level scanner with **no built-in UI**: you provide a container element (placed and styled inside your own screen or modal) and `attachScanner()` drives the camera and decoder into it. It works the same on **web and native**.
+
+### Usage
+
+```js
+// 1. Put a container in your own UI:
+var scanner = Fliplet.Barcode.attachScanner(document.getElementById('reader'), {
+ onScan: function (result) {
+ // result.text — the decoded value
+ // result.format — e.g. 'QR_CODE', 'CODE_128'
+ scanner.stop(); // one-shot: stop after the first scan (or keep scanning)
+ },
+ onError: function (error) {
+ // camera permission denied, no camera, or library failed to load
+ }
+});
+
+// when the user leaves the screen or closes your scanner UI:
+scanner.stop();
+```
+
+A typical "tap a button to scan" screen shows your own button, reveals the `#reader` container when tapped, calls `attachScanner()`, and displays `result.text` on screen from inside `onScan`.
+
+* **element** (HTMLElement | String) The container to render the camera into (an element or its `id`). You own its placement and styling — `attachScanner()` neither creates nor removes it.
+* **options** (Object)
+ * **onScan** (Function) Called on every successful decode with `{ text, format }`. Call `scanner.stop()` inside it for one-shot scanning.
+ * **onError** (Function) Called on a fatal start error (camera permission denied, no camera, or library load failure).
+ * **fps** (Number) Decode attempts per second. Default `10`.
+ * **qrbox** (Object | Function) Scan-box size (html5-qrcode `qrbox`). Defaults to a centred square at 70% of the smaller edge.
+* **Returns** a controller `{ stop() }`. `stop()` ends the camera, tears down the decoder (your element is left in the DOM), and returns a `Promise`.
+
+**Permissions**: on web the browser prompts for camera access when scanning starts and requires a secure context (HTTPS). On native the OS prompts on first use.
+
## Fliplet.Barcode.scan()
(Returns `Promise`)
-Scan a QR code or barcode.
+Open a ready-made full-screen scanner and resolve with the result. This is a convenience shortcut for a quick, standalone scan where you do not need the scanner embedded in your own screen.
-**Note**: Barcode scanning is only supported in native apps.
+**Note**: `scan()` is only supported in native apps. For scanning that also works on the web, use [`attachScanner()`](#fliplet-barcode-attachscanner).
### Usage
@@ -27847,6 +27886,195 @@ var session = await Fliplet.User.getCachedSession();
---
+# V3 barcodes
+URL: https://developers.fliplet.com/API/v3/barcode.md
+
+# V3 barcodes
+
+The `fliplet-barcode` package does two things in a V3 app, both the same on **web and native**: it **scans** QR codes and barcodes with `Fliplet.Barcode.attachScanner()`, and it **generates** QR code and barcode images with `Fliplet.Barcode.encode()`.
+
+Scanning uses `attachScanner()` — a UI-less scanner you drive into a container element inside your own screen. It runs everywhere a V3 app runs (slug-hosted web, the Studio preview, and the native shell). You build the scanning UI yourself, the same way you build a login screen on top of `Fliplet.Session`; the API owns only the camera and the decoder.
+
+This guide covers the recommended embedded-scanner pattern, a full worked example, the controller and options, camera permissions, and generating barcode images.
+
+## Prerequisites
+
+Add the `fliplet-barcode` package to the screen, then load it before use:
+
+```js
+await Fliplet.require.lazy.chain('fliplet-barcode');
+```
+
+Once the package is loaded, `Fliplet.Barcode` exposes two methods, both of which work the same on **web and native**: `attachScanner()` (scan, documented below) and `encode()` (generate a QR code or barcode image, documented under [Generating barcodes](#generating-barcodes)).
+
+## The recommended pattern: attachScanner()
+
+`attachScanner()` has **no built-in UI**. You place and style a container element; `attachScanner()` runs the camera and decoder into it and calls you back on each successful decode. Because there is no platform-specific overlay, the same code works on web and native.
+
+```js
+// Your screen owns this element and its styling:
+//
+
+const scanner = Fliplet.Barcode.attachScanner(document.getElementById('reader'), {
+ onScan(result) {
+ // result.text — the decoded value, e.g. "https://example.com" or "5012345678900"
+ // result.format — the symbology, e.g. "QR_CODE", "CODE_128", "EAN_13"
+ scanner.stop(); // one-shot: stop after the first hit (omit to keep scanning)
+ showResult(result.text);
+ },
+ onError(error) {
+ // Fatal start error: camera permission denied, no camera, or library load failure.
+ showMessage('Could not start the scanner. Check camera permissions and try again.');
+ }
+});
+
+// Stop the camera when the user leaves the screen or closes your scanner UI:
+scanner.stop();
+```
+
+### Worked example: tap a button to scan
+
+A typical scanning screen keeps the viewfinder hidden until the user asks for it, scans once, then shows the result. This is the whole flow in vanilla JS:
+
+```html
+
+
+
+```
+
+```js
+await Fliplet.require.lazy.chain('fliplet-barcode');
+
+const btn = document.getElementById('scan-btn');
+const reader = document.getElementById('reader');
+const resultEl = document.getElementById('result');
+let scanner = null;
+
+btn.addEventListener('click', function () {
+ reader.hidden = false;
+ scanner = Fliplet.Barcode.attachScanner(reader, {
+ onScan(result) {
+ resultEl.textContent = `${result.format}: ${result.text}`;
+ scanner.stop();
+ reader.hidden = true;
+ },
+ onError() {
+ resultEl.textContent = 'Camera unavailable — check permissions.';
+ reader.hidden = true;
+ }
+ });
+});
+```
+
+### Vue component
+
+In a V3 Vue screen, start the scanner from a button handler and always stop it on unmount so the camera is released:
+
+```js
+// Scanner.vue (runtime template — V3 single-file components run without a build step)
+export default {
+ data() {
+ return { value: '', scanner: null };
+ },
+ methods: {
+ async start() {
+ await Fliplet.require.lazy.chain('fliplet-barcode');
+ this.scanner = Fliplet.Barcode.attachScanner(this.$refs.reader, {
+ onScan: (result) => {
+ this.value = result.text;
+ this.scanner.stop();
+ },
+ onError: () => { this.value = 'Camera unavailable.'; }
+ });
+ }
+ },
+ beforeUnmount() {
+ if (this.scanner) {
+ this.scanner.stop();
+ }
+ },
+ template: `
+
+
+
+
{{ value }}
+
+ `
+};
+```
+
+## Controller and options
+
+`attachScanner(element, options)` returns a controller and accepts:
+
+* **element** (HTMLElement | String) — the container to render the camera into (an element or its `id`). You own its placement and styling; `attachScanner()` neither creates nor removes it.
+* **options.onScan** (Function) — called on every successful decode with `{ text, format }`. Call `controller.stop()` inside it for one-shot scanning.
+* **options.onError** (Function) — called on a fatal start error (permission denied, no camera, or library load failure). Per-frame decode misses are *not* errors and are ignored.
+* **options.fps** (Number) — decode attempts per second. Default `10`.
+* **options.qrbox** (Object | Function) — scan-box size. Defaults to a centered square at 70% of the smaller edge.
+
+The controller is `{ stop() }`. `stop()` ends the camera, tears down the decoder (your element stays in the DOM), and returns a `Promise`.
+
+## Camera permissions
+
+* **Web** — the browser prompts for camera access when scanning starts, and `getUserMedia` requires a secure context (HTTPS). In the Studio preview the app runs in an iframe that already delegates `camera`; a published slug is served over HTTPS.
+* **Native** — the OS prompts on first use.
+
+Handle a denied permission in `onError` — show the user how to re-enable the camera rather than leaving an empty viewfinder.
+
+## Generating barcodes
+
+To render a QR code or barcode image, use `Fliplet.Barcode.encode()`. It works on web and native, returns the image as a Base64 string, and defaults to a QR code unless you pass a different `format`. You place the returned image in your own UI — the same "you own the UI" model as `attachScanner()` — for example into an `` `src`, or save it with `Fliplet.Media`.
+
+### Fliplet.Barcode.encode()
+
+Encode text into a QR code or barcode and resolve with the image as a Base64 string.
+
+```js
+await Fliplet.require.lazy.chain('fliplet-barcode');
+
+// QR code (default)
+Fliplet.Barcode.encode('https://example.com').then(function (data) {
+ document.getElementById('qr').src = data; //
+});
+
+// Barcode with options
+Fliplet.Barcode.encode('5012345678900', { format: 'ean13' }).then(function (data) {
+ document.getElementById('code').src = data;
+});
+```
+
+* **text** (String) — the value to encode.
+* **options** (Object)
+ * **format** (String) — `qr` (**default**), or `barcode` (encodes as `code128`), or a specific symbology: `code39`, `code128`, `code128A`, `code128B`, `code128C`, `ean13`, `ean8`, `ean5`, `ean2`, `upc`, `upce`, `itf14`, `itf`, `msi`, `msi10`, `msi11`, `msi1010`, `msi1110`, `pharmacode`, `codabar`, `genericbarcode`.
+ * **color** (String) — foreground color, keyword (`green`) or hex (`#00ff00`). **Default** `#000000`.
+ * **background** (String) — background color, keyword or hex. **Default** `#ffffff`.
+ * **width** (Number) — **QR code only.** Width of the QR image. **Default** `600`. (Barcode width is driven by the text length and `lineWidth`.)
+ * **height** (Number) — height of the image. **Default** `600` for QR, `150` for barcode.
+ * **lineWidth** (Number) — **barcode only.** Width of a single bar; larger values produce a wider image. **Default** `2`.
+
+> **Tip:** QR codes are always 1:1, so they scale cleanly. Prefer a QR code unless a linear barcode is specifically required; for barcodes, tune `height` and `lineWidth` to the length of the encoded text. `encode()` returns the raw image and renders no UI of its own — display it however your screen needs.
+
+## Patterns — DO and DON'T
+
+**DO**
+
+- Build your own scanning UI (button, viewfinder container, result area) and drive `attachScanner()` into it.
+- Call `controller.stop()` after the first scan for one-shot flows, and on screen teardown/unmount to release the camera.
+- Handle `onError` with a user-facing message about camera permissions.
+- Give the container an explicit size — the camera fills it.
+
+**DON'T**
+
+- Don't leave the scanner running after navigation — a live camera drains battery and blocks other capture.
+- Don't add a separate "scanner" package or a custom camera/getUserMedia stack — `attachScanner()` is the supported cross-platform scanner.
+
+## Related
+
+- [V3 routing](./routing.md) — base-path and navigation patterns for the screen that hosts your scanner.
+
+---
+
# V3 Alpine.js apps
URL: https://developers.fliplet.com/API/v3/frameworks/alpine.md
@@ -40418,7 +40646,7 @@ Every Fliplet JS API available to V3 apps, grouped by capability category. Each
## Media
-- [`Fliplet.Barcode`](https://developers.fliplet.com/API/fliplet-barcode.html) — Generate QR codes and 1D/2D barcodes on screen, and scan them from the device camera, via the fliplet-barcode package.
+- [`Fliplet.Barcode`](https://developers.fliplet.com/API/v3/barcode.html) — Scan and generate QR codes and barcodes in V3 apps with Fliplet.Barcode — attachScanner() to scan (web + native) and show()/encode() to render barcode images.
- [`Fliplet.Media`](https://developers.fliplet.com/API/fliplet-media.html) — Browse folders, upload and manage files, and download media to devices via the Fliplet Media namespace.
- [`Fliplet.Media.Audio`](https://developers.fliplet.com/API/fliplet-audio.html) — Play, pause, stop, and seek audio files on device or from a URL in Fliplet apps via the Audio namespace.
diff --git a/docs/.well-known/llms-v3-libraries.json b/docs/.well-known/llms-v3-libraries.json
index 732d20f2..c7fda002 100644
--- a/docs/.well-known/llms-v3-libraries.json
+++ b/docs/.well-known/llms-v3-libraries.json
@@ -1,6 +1,6 @@
{
"version": 1,
- "generatedAt": "2026-06-10T17:50:12.068Z",
+ "generatedAt": "2026-06-17T10:20:55.308Z",
"libraries": [
{
"package": "fliplet-analytics-spa",
@@ -61,9 +61,9 @@
{
"package": "fliplet-barcode",
"namespace": "Fliplet.Barcode",
- "title": "Fliplet.Barcode",
- "description": "Generate QR codes and 1D/2D barcodes on screen, and scan them from the device camera, via the fliplet-barcode package.",
- "docUrl": "https://developers.fliplet.com/API/fliplet-barcode.md",
+ "title": "V3 barcodes",
+ "description": "Scan and generate QR codes and barcodes in V3 apps with Fliplet.Barcode — attachScanner() to scan (web + native) and encode() to render barcode images.",
+ "docUrl": "https://developers.fliplet.com/API/v3/barcode.md",
"preloaded": false,
"capabilities": [
"barcode",
diff --git a/docs/.well-known/llms.txt b/docs/.well-known/llms.txt
index cbd7ba9b..9fb325d3 100644
--- a/docs/.well-known/llms.txt
+++ b/docs/.well-known/llms.txt
@@ -103,7 +103,7 @@
- [Fliplet.App.Submissions](https://developers.fliplet.com/API/fliplet-app-submissions.md): Read App Store and Google Play submission metadata for the current Fliplet app — version, status, build numbers — via the fliplet-app-submissions package.
- [Fliplet.Media.Audio.Player](https://developers.fliplet.com/API/fliplet-audio-player.md): Embed an audio player UI in a screen by tagging HTML elements with audio URLs; the framework auto-initializes players on screen load.
- [Fliplet.Media.Audio](https://developers.fliplet.com/API/fliplet-audio.md): Play, pause, stop, and seek audio files on device or from a URL in Fliplet apps via the Audio namespace.
-- [Fliplet.Barcode](https://developers.fliplet.com/API/fliplet-barcode.md): Generate QR codes and 1D/2D barcodes on screen, and scan them from the device camera, via the fliplet-barcode package.
+- [Fliplet.Barcode](https://developers.fliplet.com/API/fliplet-barcode.md): Scan QR codes and 1D/2D barcodes from the camera on web and native (attachScanner / scan) and generate barcode images, via the fliplet-barcode package.
- [Fliplet.Chat](https://developers.fliplet.com/API/fliplet-chat.md): Build one-to-one, group, and public-channel chat features. Fliplet.Chat owns the conversations and messages data sources internally — supply only the contacts list (who can chat with whom).
- [Fliplet.Communicate](https://developers.fliplet.com/API/fliplet-communicate.md): Send email, SMS, push notifications, and share URLs from a Fliplet app using a single Communicate namespace.
- [Fliplet.Content()](https://developers.fliplet.com/API/fliplet-content.md): Create, query, update, and delete shared content records (bookmarks, likes, saved searches) backed by a data source via Fliplet.Content.
@@ -149,6 +149,7 @@
- [V3 app bootstrap constraints](https://developers.fliplet.com/API/v3/app-bootstrap.md): The four constraints every V3 boot HTML must satisfy. Covers Fliplet.require.lazy for dependencies, Fliplet.Media.getContents for source files, the Fliplet().then(...) init sequence, and the locked v…
- [V3 App Settings Convention](https://developers.fliplet.com/API/v3/app-settings.md): V3 app settings convention for storing public and private configuration. Covers the underscore prefix convention for editor-private settings.
- [V3 Authentication Patterns](https://developers.fliplet.com/API/v3/auth.md): V3 authentication patterns for email/password login, session management, logout, and protected routes. Use these patterns when building authentication flows in V3 apps.
+- [V3 barcodes](https://developers.fliplet.com/API/v3/barcode.md): Scan and generate QR codes and barcodes in V3 apps with Fliplet.Barcode — attachScanner() to scan (web + native) and encode() to render barcode images.
- [V3 Alpine.js apps](https://developers.fliplet.com/API/v3/frameworks/alpine.md): Constraints for building V3 apps in Alpine.js. Alpine is attribute-driven HTML with no build step, so it maps cleanly to the V3 runtime. Covers x-init timing relative to Fliplet().then and platform-c…
- [V3 framework guide — picking and setup](https://developers.fliplet.com/API/v3/frameworks/overview.md): Picking a frontend framework for a V3 app. Lists the runtime constraints every framework must cope with (no build step, no bundler, no transpile) and compares Vue, React, Alpine, and vanilla JS again…
- [V3 React apps](https://developers.fliplet.com/API/v3/frameworks/react.md): Constraints for building V3 apps in React. Covers the JSX transpilation problem (the #1 cause of first-deploy failures) with three alternatives, React Router 6 createHashRouter wiring (no basename —…
diff --git a/docs/API/fliplet-barcode.md b/docs/API/fliplet-barcode.md
index a85e3a0f..b1443993 100644
--- a/docs/API/fliplet-barcode.md
+++ b/docs/API/fliplet-barcode.md
@@ -1,28 +1,66 @@
---
title: Fliplet.Barcode
-description: Generate QR codes and 1D/2D barcodes on screen, and scan them from the device camera, via the fliplet-barcode package.
+description: Scan QR codes and 1D/2D barcodes from the camera on web and native (attachScanner / scan) and generate barcode images, via the fliplet-barcode package.
type: api-reference
tags: [js-api, barcode]
-v3_relevant: true
+v3_relevant: false
deprecated: false
-category: media
-capabilities: [barcode, qr code, qrcode, scan, scanner, camera scan, generate barcode, ean, upc, 1d barcode, 2d barcode]
+exclude_from_v3_catalog: true
---
# `Fliplet.Barcode`
-Scan QR codes and other 1D/2D barcodes from the device camera, and generate barcode images on screen, via the `fliplet-barcode` package. Barcode scanning is only supported in native apps.
+Scan QR codes and other 1D/2D barcodes from the device camera, and generate barcode images on screen, via the `fliplet-barcode` package.
+
+The package offers two ways to scan: `attachScanner()` embeds a scanner in a container in your own UI and works on web and native, while `scan()` opens a ready-made full-screen scanner on native. Use `show()` or `encode()` to generate barcode images. Building a scanning screen in a V3 app? See the [V3 barcode scanning guide](./v3/barcode.md).
## Install
Add the `fliplet-barcode` dependency to your screen or app resources.
+## Fliplet.Barcode.attachScanner()
+
+(Returns a controller object)
+
+`attachScanner()` is a low-level scanner with **no built-in UI**: you provide a container element (placed and styled inside your own screen or modal) and `attachScanner()` drives the camera and decoder into it. It works the same on **web and native**.
+
+### Usage
+
+```js
+// 1. Put a container in your own UI:
+var scanner = Fliplet.Barcode.attachScanner(document.getElementById('reader'), {
+ onScan: function (result) {
+ // result.text — the decoded value
+ // result.format — e.g. 'QR_CODE', 'CODE_128'
+ scanner.stop(); // one-shot: stop after the first scan (or keep scanning)
+ },
+ onError: function (error) {
+ // camera permission denied, no camera, or library failed to load
+ }
+});
+
+// when the user leaves the screen or closes your scanner UI:
+scanner.stop();
+```
+
+A typical "tap a button to scan" screen shows your own button, reveals the `#reader` container when tapped, calls `attachScanner()`, and displays `result.text` on screen from inside `onScan`.
+
+* **element** (HTMLElement | String) The container to render the camera into (an element or its `id`). You own its placement and styling — `attachScanner()` neither creates nor removes it.
+* **options** (Object)
+ * **onScan** (Function) Called on every successful decode with `{ text, format }`. Call `scanner.stop()` inside it for one-shot scanning.
+ * **onError** (Function) Called on a fatal start error (camera permission denied, no camera, or library load failure).
+ * **fps** (Number) Decode attempts per second. Default `10`.
+ * **qrbox** (Object | Function) Scan-box size (html5-qrcode `qrbox`). Defaults to a centred square at 70% of the smaller edge.
+* **Returns** a controller `{ stop() }`. `stop()` ends the camera, tears down the decoder (your element is left in the DOM), and returns a `Promise`.
+
+**Permissions**: on web the browser prompts for camera access when scanning starts and requires a secure context (HTTPS). On native the OS prompts on first use.
+
## Fliplet.Barcode.scan()
(Returns `Promise`)
-Scan a QR code or barcode.
+Open a ready-made full-screen scanner and resolve with the result. This is a convenience shortcut for a quick, standalone scan where you do not need the scanner embedded in your own screen.
-**Note**: Barcode scanning is only supported in native apps.
+**Note**: `scan()` is only supported in native apps. For scanning that also works on the web, use [`attachScanner()`](#fliplet-barcode-attachscanner).
### Usage
diff --git a/docs/API/v3/barcode.md b/docs/API/v3/barcode.md
new file mode 100644
index 00000000..c1ee8b8c
--- /dev/null
+++ b/docs/API/v3/barcode.md
@@ -0,0 +1,196 @@
+---
+title: "V3 barcodes"
+description: Scan and generate QR codes and barcodes in V3 apps with Fliplet.Barcode — attachScanner() to scan (web + native) and encode() to render barcode images.
+type: guide
+tags: [js-api, v3, barcode]
+v3_relevant: true
+deprecated: false
+package: fliplet-barcode
+namespace: Fliplet.Barcode
+category: media
+capabilities: [barcode, qr code, qrcode, scan, scanner, camera scan, generate barcode, ean, upc, 1d barcode, 2d barcode]
+---
+
+# V3 barcodes
+
+The `fliplet-barcode` package does two things in a V3 app, both the same on **web and native**: it **scans** QR codes and barcodes with `Fliplet.Barcode.attachScanner()`, and it **generates** QR code and barcode images with `Fliplet.Barcode.encode()`.
+
+Scanning uses `attachScanner()` — a UI-less scanner you drive into a container element inside your own screen. It runs everywhere a V3 app runs (slug-hosted web, the Studio preview, and the native shell). You build the scanning UI yourself, the same way you build a login screen on top of `Fliplet.Session`; the API owns only the camera and the decoder.
+
+This guide covers the recommended embedded-scanner pattern, a full worked example, the controller and options, camera permissions, and generating barcode images.
+
+## Prerequisites
+
+Add the `fliplet-barcode` package to the screen, then load it before use:
+
+```js
+await Fliplet.require.lazy.chain('fliplet-barcode');
+```
+
+Once the package is loaded, `Fliplet.Barcode` exposes two methods, both of which work the same on **web and native**: `attachScanner()` (scan, documented below) and `encode()` (generate a QR code or barcode image, documented under [Generating barcodes](#generating-barcodes)).
+
+## The recommended pattern: attachScanner()
+
+`attachScanner()` has **no built-in UI**. You place and style a container element; `attachScanner()` runs the camera and decoder into it and calls you back on each successful decode. Because there is no platform-specific overlay, the same code works on web and native.
+
+```js
+// Your screen owns this element and its styling:
+//
+
+const scanner = Fliplet.Barcode.attachScanner(document.getElementById('reader'), {
+ onScan(result) {
+ // result.text — the decoded value, e.g. "https://example.com" or "5012345678900"
+ // result.format — the symbology, e.g. "QR_CODE", "CODE_128", "EAN_13"
+ scanner.stop(); // one-shot: stop after the first hit (omit to keep scanning)
+ showResult(result.text);
+ },
+ onError(error) {
+ // Fatal start error: camera permission denied, no camera, or library load failure.
+ showMessage('Could not start the scanner. Check camera permissions and try again.');
+ }
+});
+
+// Stop the camera when the user leaves the screen or closes your scanner UI:
+scanner.stop();
+```
+
+### Worked example: tap a button to scan
+
+A typical scanning screen keeps the viewfinder hidden until the user asks for it, scans once, then shows the result. This is the whole flow in vanilla JS:
+
+```html
+
+
+
+```
+
+```js
+await Fliplet.require.lazy.chain('fliplet-barcode');
+
+const btn = document.getElementById('scan-btn');
+const reader = document.getElementById('reader');
+const resultEl = document.getElementById('result');
+let scanner = null;
+
+btn.addEventListener('click', function () {
+ reader.hidden = false;
+ scanner = Fliplet.Barcode.attachScanner(reader, {
+ onScan(result) {
+ resultEl.textContent = `${result.format}: ${result.text}`;
+ scanner.stop();
+ reader.hidden = true;
+ },
+ onError() {
+ resultEl.textContent = 'Camera unavailable — check permissions.';
+ reader.hidden = true;
+ }
+ });
+});
+```
+
+### Vue component
+
+In a V3 Vue screen, start the scanner from a button handler and always stop it on unmount so the camera is released:
+
+```js
+// Scanner.vue (runtime template — V3 single-file components run without a build step)
+export default {
+ data() {
+ return { value: '', scanner: null };
+ },
+ methods: {
+ async start() {
+ await Fliplet.require.lazy.chain('fliplet-barcode');
+ this.scanner = Fliplet.Barcode.attachScanner(this.$refs.reader, {
+ onScan: (result) => {
+ this.value = result.text;
+ this.scanner.stop();
+ },
+ onError: () => { this.value = 'Camera unavailable.'; }
+ });
+ }
+ },
+ beforeUnmount() {
+ if (this.scanner) {
+ this.scanner.stop();
+ }
+ },
+ template: `
+
+
+
+
{{ value }}
+
+ `
+};
+```
+
+## Controller and options
+
+`attachScanner(element, options)` returns a controller and accepts:
+
+* **element** (HTMLElement | String) — the container to render the camera into (an element or its `id`). You own its placement and styling; `attachScanner()` neither creates nor removes it.
+* **options.onScan** (Function) — called on every successful decode with `{ text, format }`. Call `controller.stop()` inside it for one-shot scanning.
+* **options.onError** (Function) — called on a fatal start error (permission denied, no camera, or library load failure). Per-frame decode misses are *not* errors and are ignored.
+* **options.fps** (Number) — decode attempts per second. Default `10`.
+* **options.qrbox** (Object | Function) — scan-box size. Defaults to a centered square at 70% of the smaller edge.
+
+The controller is `{ stop() }`. `stop()` ends the camera, tears down the decoder (your element stays in the DOM), and returns a `Promise`.
+
+## Camera permissions
+
+* **Web** — the browser prompts for camera access when scanning starts, and `getUserMedia` requires a secure context (HTTPS). In the Studio preview the app runs in an iframe that already delegates `camera`; a published slug is served over HTTPS.
+* **Native** — the OS prompts on first use.
+
+Handle a denied permission in `onError` — show the user how to re-enable the camera rather than leaving an empty viewfinder.
+
+## Generating barcodes
+
+To render a QR code or barcode image, use `Fliplet.Barcode.encode()`. It works on web and native, returns the image as a Base64 string, and defaults to a QR code unless you pass a different `format`. You place the returned image in your own UI — the same "you own the UI" model as `attachScanner()` — for example into an `` `src`, or save it with `Fliplet.Media`.
+
+### Fliplet.Barcode.encode()
+
+Encode text into a QR code or barcode and resolve with the image as a Base64 string.
+
+```js
+await Fliplet.require.lazy.chain('fliplet-barcode');
+
+// QR code (default)
+Fliplet.Barcode.encode('https://example.com').then(function (data) {
+ document.getElementById('qr').src = data; //
+});
+
+// Barcode with options
+Fliplet.Barcode.encode('5012345678900', { format: 'ean13' }).then(function (data) {
+ document.getElementById('code').src = data;
+});
+```
+
+* **text** (String) — the value to encode.
+* **options** (Object)
+ * **format** (String) — `qr` (**default**), or `barcode` (encodes as `code128`), or a specific symbology: `code39`, `code128`, `code128A`, `code128B`, `code128C`, `ean13`, `ean8`, `ean5`, `ean2`, `upc`, `upce`, `itf14`, `itf`, `msi`, `msi10`, `msi11`, `msi1010`, `msi1110`, `pharmacode`, `codabar`, `genericbarcode`.
+ * **color** (String) — foreground color, keyword (`green`) or hex (`#00ff00`). **Default** `#000000`.
+ * **background** (String) — background color, keyword or hex. **Default** `#ffffff`.
+ * **width** (Number) — **QR code only.** Width of the QR image. **Default** `600`. (Barcode width is driven by the text length and `lineWidth`.)
+ * **height** (Number) — height of the image. **Default** `600` for QR, `150` for barcode.
+ * **lineWidth** (Number) — **barcode only.** Width of a single bar; larger values produce a wider image. **Default** `2`.
+
+> **Tip:** QR codes are always 1:1, so they scale cleanly. Prefer a QR code unless a linear barcode is specifically required; for barcodes, tune `height` and `lineWidth` to the length of the encoded text. `encode()` returns the raw image and renders no UI of its own — display it however your screen needs.
+
+## Patterns — DO and DON'T
+
+**DO**
+
+- Build your own scanning UI (button, viewfinder container, result area) and drive `attachScanner()` into it.
+- Call `controller.stop()` after the first scan for one-shot flows, and on screen teardown/unmount to release the camera.
+- Handle `onError` with a user-facing message about camera permissions.
+- Give the container an explicit size — the camera fills it.
+
+**DON'T**
+
+- Don't leave the scanner running after navigation — a live camera drains battery and blocks other capture.
+- Don't add a separate "scanner" package or a custom camera/getUserMedia stack — `attachScanner()` is the supported cross-platform scanner.
+
+## Related
+
+- [V3 routing](./routing.md) — base-path and navigation patterns for the screen that hosts your scanner.
diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md
index 8a9e09e7..e2399c0c 100644
--- a/docs/CLAUDE.md
+++ b/docs/CLAUDE.md
@@ -94,6 +94,34 @@ none.
the V3 design system replaces, meta/overview docs, deprecated namespaces,
or docs that aren't a Fliplet API per se.
+### Dedicated V3 capability docs (the V3-version pattern)
+
+When an API behaves differently enough on V3 that the builder needs V3-specific
+guidance, give the package its own doc at **`API/v3/.md`** and make *that*
+the catalog entry — don't steer the shared reference. This is the pattern used by
+`fliplet-barcode` (and `app-actions` before it):
+
+1. **The V3 doc** (`API/v3/.md`) declares the catalog fields —
+ `package:`, `category:`, `capabilities:`, and `namespace:` (the real JS
+ global, since the doc's title is a guide title like "V3 barcode scanning",
+ not `Fliplet.Barcode`). A doc under `API/v3/` becomes a catalog entry **only**
+ when it declares `package:`, so the general V3 guides (routing, auth,
+ frameworks) stay out.
+2. **The shared reference** (`API/fliplet-.md`) is set
+ `v3_relevant: false` **and** `exclude_from_v3_catalog: true`, so the catalog,
+ capabilities index, and Studio registry all derive the V3 doc's URL — no
+ hand-set URLs, no Studio-side override.
+3. **Content rule:** the V3 doc contains **only the recommended cross-platform
+ primitive**. Remove native-only or alternative methods entirely (don't just
+ de-emphasize them) — the builder reads this as its sole source for the
+ package and will pick whatever runnable method it sees, so an unwanted method
+ in the doc becomes unwanted code. Keep those methods in the shared reference.
+
+`build-agent-indexes.mjs` enforces **one catalog doc per package**
+(`validateCatalogUniqueness`, `--strict`) — forgetting the
+`exclude_from_v3_catalog` opt-out fails the build rather than silently shipping
+two `docUrl`s for one package.
+
## Exclusion list — do not index, do not polish
These files are handled at the server or build layer and must never be
diff --git a/docs/bin/__tests__/build-agent-indexes.test.mjs b/docs/bin/__tests__/build-agent-indexes.test.mjs
index c84a3164..fd3d65d8 100644
--- a/docs/bin/__tests__/build-agent-indexes.test.mjs
+++ b/docs/bin/__tests__/build-agent-indexes.test.mjs
@@ -29,6 +29,7 @@ import {
assignToCluster,
validateFrontmatter,
validateCapabilities,
+ validateCatalogUniqueness,
ALLOWED_TYPES,
CLUSTERS,
collectDocs,
@@ -740,6 +741,25 @@ describe('emitV3LibraryCatalog', () => {
assert.equal(catalog.libraries[0].package, 'fliplet-barcode');
});
+ it('includes an API/v3/*.md package doc and honours the namespace override', () => {
+ const docs = [
+ makeDoc(
+ 'API/v3/barcode.md',
+ { package: 'fliplet-barcode', namespace: 'Fliplet.Barcode', category: 'media' },
+ 'V3 barcode scanning',
+ 'Scan on web + native',
+ ),
+ ];
+ const catalog = emitV3LibraryCatalog(docs);
+ assert.equal(catalog.libraries.length, 1);
+ assert.equal(catalog.libraries[0].package, 'fliplet-barcode');
+ // namespace comes from frontmatter, NOT the guide-style title
+ assert.equal(catalog.libraries[0].namespace, 'Fliplet.Barcode');
+ assert.equal(catalog.libraries[0].title, 'V3 barcode scanning');
+ assert.equal(catalog.libraries[0].preloaded, false);
+ assert.equal(catalog.libraries[0].docUrl, 'https://developers.fliplet.com/API/v3/barcode.html');
+ });
+
it('excludes docs not matching the installable or ambient path patterns', () => {
const docs = [
makeDoc('Building-themes.md', {}, 'Themes'),
@@ -938,6 +958,47 @@ describe('isV3CatalogEntry', () => {
true,
);
});
+
+ it('includes API/v3/*.md only when it declares a package', () => {
+ assert.equal(isV3CatalogEntry(doc('API/v3/barcode.md', { package: 'fliplet-barcode' })), true);
+ // general V3 guides (no package) describe patterns, not a package — stay out
+ assert.equal(isV3CatalogEntry(doc('API/v3/routing.md')), false);
+ assert.equal(isV3CatalogEntry(doc('API/v3/auth.md', { package: '' })), false);
+ });
+
+ it('respects exclude_from_v3_catalog on an API/v3 package doc', () => {
+ assert.equal(
+ isV3CatalogEntry(doc('API/v3/barcode.md', { package: 'fliplet-barcode', exclude_from_v3_catalog: 'true' })),
+ false,
+ );
+ });
+
+ it('ignores nested API/v3 subdirs (e.g. frameworks/) regardless of package', () => {
+ assert.equal(isV3CatalogEntry(doc('API/v3/frameworks/vue.md', { package: 'x' })), false);
+ });
+});
+
+describe('validateCatalogUniqueness', () => {
+ function doc(relPath, fm = {}) {
+ return { relPath, fm };
+ }
+
+ it('flags a package that resolves to two catalog docs', () => {
+ const errors = validateCatalogUniqueness([
+ doc('API/fliplet-barcode.md', { package: 'fliplet-barcode', category: 'media', capabilities: '[barcode]' }),
+ doc('API/v3/barcode.md', { package: 'fliplet-barcode', category: 'media', capabilities: '[barcode]' }),
+ ]);
+ assert.equal(errors.length, 2);
+ assert.match(errors[0].message, /resolves to 2 V3 catalog entries/);
+ });
+
+ it('passes when the shared reference opts out (one catalog doc per package)', () => {
+ const errors = validateCatalogUniqueness([
+ doc('API/fliplet-barcode.md', { exclude_from_v3_catalog: 'true' }),
+ doc('API/v3/barcode.md', { package: 'fliplet-barcode', category: 'media', capabilities: '[barcode]' }),
+ ]);
+ assert.equal(errors.length, 0);
+ });
});
describe('validateCapabilities', () => {
diff --git a/docs/bin/build-agent-indexes.mjs b/docs/bin/build-agent-indexes.mjs
index 42137a99..b6eb5438 100644
--- a/docs/bin/build-agent-indexes.mjs
+++ b/docs/bin/build-agent-indexes.mjs
@@ -757,6 +757,41 @@ export function validateCapabilities(docs) {
return errors;
}
+// One catalog entry per package. A package's dedicated V3 doc
+// (API/v3/.md with `package:`) is meant to REPLACE its shared
+// API/fliplet-.md reference as the catalog entry — and the shared doc
+// must then opt out via `exclude_from_v3_catalog: true`. If both stay in the
+// catalog, the V3 builder gets two conflicting `docUrl`s for one package and
+// may read the wrong (e.g. native-only) version. This lint is the tripwire:
+// it fails the build when any package resolves to >1 catalog doc, so a
+// forgotten opt-out can't silently ship the wrong version.
+export function validateCatalogUniqueness(docs) {
+ const errors = [];
+ const byPackage = new Map();
+ for (const doc of docs) {
+ if (!isV3CatalogEntry(doc)) continue;
+ const fm = doc.fm || {};
+ const pkg = (fm.package && String(fm.package).trim()) || deriveV3Package(doc.relPath);
+ if (!pkg) continue;
+ if (!byPackage.has(pkg)) byPackage.set(pkg, []);
+ byPackage.get(pkg).push(doc.relPath);
+ }
+ for (const [pkg, paths] of byPackage) {
+ if (paths.length > 1) {
+ for (const p of paths) {
+ errors.push({
+ relPath: p,
+ field: 'package',
+ message: `package \`${pkg}\` resolves to ${paths.length} V3 catalog entries: ${paths.join(', ')}`,
+ hint: 'A package needs exactly one catalog doc. If a dedicated API/v3/ doc is the V3 entry, mark the shared API/fliplet-*.md with `exclude_from_v3_catalog: true`.',
+ docUrl: CONTRIBUTING_URL,
+ });
+ }
+ }
+ }
+ return errors;
+}
+
// ---------------------------------------------------------------------------
// Cross-link validation (strict-mode link-rot detection).
//
@@ -1029,6 +1064,24 @@ function main() {
}
}
+ // Catalog uniqueness: exactly one V3 catalog doc per package. Same --strict
+ // gate. Guards the "shared ref opts out, V3 doc takes over" handoff so a
+ // forgotten `exclude_from_v3_catalog` can't ship two docUrls for one package.
+ const dupErrors = validateCatalogUniqueness(docs);
+ if (dupErrors.length > 0) {
+ const tag = strict ? '[error]' : '[warn]';
+ for (const e of dupErrors) {
+ console.error(`${tag} ${e.relPath}: ${e.message}`);
+ if (e.hint) console.error(` hint: ${e.hint}`);
+ }
+ if (strict) {
+ console.error(
+ `\n${dupErrors.length} catalog-uniqueness error${dupErrors.length === 1 ? '' : 's'} — failing build (--strict).`,
+ );
+ process.exit(1);
+ }
+ }
+
// Cross-link validation: catch internal links that don't resolve to an
// existing doc file. Same --strict gate as the validators above.
const linkErrors = validateCrossLinks(docs, docsRoot);
diff --git a/docs/bin/emitters.mjs b/docs/bin/emitters.mjs
index 611f4932..b4cf52da 100644
--- a/docs/bin/emitters.mjs
+++ b/docs/bin/emitters.mjs
@@ -9,12 +9,20 @@
//
// Stdlib only — no npm deps. Imported by build-agent-indexes.mjs.
-// V3 catalog membership is determined by two path patterns plus the
+// V3 catalog membership is determined by the path patterns below plus the
// `exclude_from_v3_catalog: true` frontmatter opt-out. Keep these regexes
// private to this module — the predicate `isV3CatalogEntry` is the public
// contract.
const V3_INSTALLABLE_PATH_RE = /^API\/fliplet-[^/]+\.md$/;
const V3_AMBIENT_PATH_RE = /^API\/core\/[^/]+\.md$/;
+// A V3-specific capability doc under API/v3/ becomes the catalog entry for its
+// package ONLY when it declares `package:` in frontmatter. This lets an
+// installable package ship a dedicated V3 doc (e.g. API/v3/barcode.md leading
+// with the cross-platform primitive) as the canonical catalog entry, while the
+// shared API/fliplet-.md reference opts out via `exclude_from_v3_catalog`.
+// The `package:` gate keeps the general V3 guides (routing, auth, frameworks/)
+// out of the catalog — they describe patterns, not a single package.
+const V3_GUIDE_PATH_RE = /^API\/v3\/[^/]+\.md$/;
// Allowed values for the `category:` frontmatter field. Documented in
// docs/CLAUDE.md as the canonical schema. Each catalog entry picks exactly
@@ -52,22 +60,25 @@ export const ALLOWED_CATEGORIES_SET = new Set(ALLOWED_CATEGORIES);
// must use this — drift between them is a real bug (page would show entries
// the agent doesn't know about, or vice versa).
//
-// A doc is a V3 catalog entry when:
-// - its path matches `API/fliplet-*.md` (installable) OR
-// `API/core/*.md` (ambient, preloaded via fliplet-core), AND
-// - its frontmatter does NOT set `exclude_from_v3_catalog: true`.
+// A doc is a V3 catalog entry when its frontmatter does NOT set
+// `exclude_from_v3_catalog: true`, AND its path matches one of:
+// - `API/fliplet-*.md` (installable), OR
+// - `API/core/*.md` (ambient, preloaded via fliplet-core), OR
+// - `API/v3/*.md` that declares `package:` (a package's dedicated V3 doc;
+// the `package:` gate keeps the pattern guides — routing/auth/frameworks —
+// out, since they describe patterns rather than one package).
//
// Note: this is DIFFERENT from `shouldExclude(path)` in exclusions.mjs.
// `shouldExclude` skips a path from indexing entirely (redirect stubs,
// `_site/`, etc.); `isV3CatalogEntry` decides catalog membership for docs
// that ARE indexed. Conflating them mis-fires lints across all docs.
export function isV3CatalogEntry(doc) {
- const isInstallable = V3_INSTALLABLE_PATH_RE.test(doc.relPath);
- const isAmbient = V3_AMBIENT_PATH_RE.test(doc.relPath);
- if (!isInstallable && !isAmbient) return false;
const fm = doc.fm || {};
if (fm.exclude_from_v3_catalog === 'true') return false;
- return true;
+ const isInstallable = V3_INSTALLABLE_PATH_RE.test(doc.relPath);
+ const isAmbient = V3_AMBIENT_PATH_RE.test(doc.relPath);
+ const isV3Guide = V3_GUIDE_PATH_RE.test(doc.relPath) && !!(fm.package && String(fm.package).trim());
+ return isInstallable || isAmbient || isV3Guide;
}
export function deriveV3Package(relPath) {
@@ -127,9 +138,15 @@ export function emitV3LibraryCatalog(docs) {
pkg = 'fliplet-core';
}
+ // For most docs the title IS the JS namespace (e.g. "Fliplet.Barcode"). A
+ // dedicated V3 doc is titled as a guide ("V3 barcode scanning"), so it can
+ // declare the actual global via `namespace:` — the builder's registry shows
+ // `package → namespace`, so this must be the real global, not the doc title.
+ const namespace = (fm.namespace && fm.namespace.trim()) || doc.title;
+
const entry = {
package: pkg,
- namespace: doc.title,
+ namespace,
title: doc.title,
description: doc.description || '',
// AI-consumption surface: emit the raw .md URL (Studio's V3 builder fetches
@@ -184,7 +201,7 @@ export function emitCapabilitiesIndex(docs) {
const category = fm.category && fm.category.trim();
const isAmbient = V3_AMBIENT_PATH_RE.test(doc.relPath);
const item = {
- namespace: doc.title,
+ namespace: (fm.namespace && fm.namespace.trim()) || doc.title,
description: doc.description || '',
url: doc.url,
preloaded: isAmbient,
diff --git a/docs/v3/capabilities.md b/docs/v3/capabilities.md
index 16e5b555..0eeab48f 100644
--- a/docs/v3/capabilities.md
+++ b/docs/v3/capabilities.md
@@ -39,7 +39,7 @@ Every Fliplet JS API available to V3 apps, grouped by capability category. Each
## Media
-- [`Fliplet.Barcode`](https://developers.fliplet.com/API/fliplet-barcode.html) — Generate QR codes and 1D/2D barcodes on screen, and scan them from the device camera, via the fliplet-barcode package.
+- [`Fliplet.Barcode`](https://developers.fliplet.com/API/v3/barcode.html) — Scan and generate QR codes and barcodes in V3 apps with Fliplet.Barcode — attachScanner() to scan (web + native) and encode() to render barcode images.
- [`Fliplet.Media`](https://developers.fliplet.com/API/fliplet-media.html) — Browse folders, upload and manage files, and download media to devices via the Fliplet Media namespace.
- [`Fliplet.Media.Audio`](https://developers.fliplet.com/API/fliplet-audio.html) — Play, pause, stop, and seek audio files on device or from a URL in Fliplet apps via the Audio namespace.