=== OneCraft Block Insights ===
Contributors: onecraftne
Tags: analytics, statistics, engagement, scroll depth, block editor
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.0.5
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

It names the block your readers stop at: reach, dwell and clicks, block by block.

== Description ==

OneCraft Block Insights measures pages built with the WordPress block editor, one
block at a time: how far down the page each reader gets, how long each block
holds them, and where they click. It names the block your readers stop at.

**What it cannot measure. Read this first.**

Only content built with the block editor is measured. Pages built with a
page builder — Elementor, Divi, WPBakery, Oxygen, Bricks, Beaver Builder —
store their layout outside of blocks, so there is nothing for this plugin to
attach to. Those pages produce no numbers at all. After activation the
plugin scans your content and tells you exactly how many of your pages it
can and cannot measure, naming the builder it found.

This is also not a site-wide analytics plugin. There are no visitor totals,
no sessions, no traffic sources, no unique visitors. It answers one
question: within a page, which block do readers stop at.

**It writes an identifier into your content.**

Telling one block apart from another, across edits, needs a name that
survives the edit. So the first time you edit and save a page, the plugin
adds a short ID to each block's comment delimiter in the post content.
Nothing visible changes, and the front end is unaffected.

Taking the plugin out takes them with it. Deleting the plugin restores post
content to the exact bytes it had before the IDs were added, and
`wp block-insights strip` does the same without uninstalling. Any post whose
content would not survive that round trip is left completely untouched and
reported to you, never rewritten.

= What it measures =

* Reach — how far down the page readers actually get, block by block
* Dwell — how long each block stays on screen
* Clicks — where readers click, attributed to the block they clicked in

A block counts as reached once enough of it is genuinely on screen: half the
block, or half the screen for a block taller than the screen. Dwell accrues
only while that holds, and stops when the reader switches tabs.

= About the numbers =

Measurement happens in the reader's browser, so the figures are directional
rather than audited. Page views here will always be lower than your server
logs: content blockers, unusual browsers, crashed tabs and phones killing a
background app all cost measurements. Use these numbers to compare sections
within a page, not as a system of record.

Ratios survive this better than totals do. A block's reach and the page view
it is divided by come from the same single beacon, so a lost visit removes
both at once and the percentage stays honest.

One more caution: reached is not read. A reader who scrolls past a block in
200 milliseconds still counts as having reached it. That is what the dwell
figure is for.

= Privacy =

Measurement is cookieless and first-party. No cookies are set, no browser
storage is used, no visitor identifier of any kind is created, and unique
visitors are not counted. No IP addresses, page URLs, referrers, user agents
or personal data are stored.

What visitors do is written to your own WordPress database and read back
from it. There is no analytics service, no third-party script, no remote
API, no phone-home and no telemetry. Nothing about your readers leaves your
server.

Because no personal data is collected, no consent banner is required for
this plugin, and it adds nothing to personal data exports or erasure
requests.

= Where to find it =

* **Block Insights → Reports** — every measured page, worst drop-off first.
  Open one for a block-by-block breakdown in document order, so the reach
  percentages form a curve running down the page. The step where that curve
  falls is the block readers stop at.
* **Block Insights → Settings** — which post types are measured, with the
  daily row cost of each choice shown before you commit, plus the coverage
  report of what can and cannot be measured.
* **Block Insights → Manual** — what the numbers mean, where they come from,
  and how far to trust them.
* **The post editor** — open a page, then the ⋮ menu → Block Insights for
  that page's numbers next to the blocks themselves.

= Storage =

Storage does not grow with traffic. One row covers one block, on one day, on
one device type, however many readers it had. Rows grow with pages times
blocks times days.

Old data is never deleted. Once the daily table reaches its row budget, the
oldest days are folded into monthly totals, so disk usage stays predictable
no matter how large the site is. Reports that reach back past that point
print the date day-level detail begins on, so a shrinking figure is never
silent.

By default only posts and pages are measured. Other public post types can be
enabled in Settings.

Block Insights does not support WordPress Multisite.

= The Pro version =

The free version has no page limit, no page-view limit and no expiry.

Pro adds three things: the before-and-after comparison, which splits a
page's numbers at the day you edited it; the client report, a printable
document with your own letterhead; and a CSV export.

Your edits are recorded by the free version from the day you install it. If
you upgrade later, the comparison covers that whole history rather than
beginning on the day you paid.

= External services =

The free version talks to no external service on its own. It ships the
Freemius SDK, which powers licence activation and updates for Pro. On first
activation Freemius asks whether you want to share basic site data; the
question is skippable, and if you skip it nothing is sent.

Data leaves your site in two cases: if you accept that opt-in, and if you
activate a Pro licence key. What is sent then is your site's homepage URL,
the plugin version and the SDK version — the minimum needed to enforce a
per-site licence and deliver updates. The optional extras the SDK can send,
your list of installed plugins and themes and your WordPress/PHP environment
report, are switched off by default in this plugin. No visitor data, and
nothing from the statistics tables, is ever included.

Freemius terms: https://freemius.com/terms/
Freemius privacy policy: https://freemius.com/privacy/

== Installation ==

1. Install and activate the plugin.
2. Open **Block Insights → Settings** and read the coverage report. It tells
   you how many of your pages can be measured before you invest any time.
3. Open a page in the block editor, **make an actual change**, and update.

Step 3 is required, and it is the step people miss. Block identifiers are
added in the editor as you work; existing content is never rewritten in
bulk, because that would touch every post on the site to solve a problem you
may not have. A post that has never been opened and saved in the editor
since activation is never measured. Opening it is not enough — the editor
has to consider the post edited. A space added and removed again counts.

Numbers then appear once someone has visited the published page. Your own
visits are not counted while you are logged in and able to edit that page.

== Frequently Asked Questions ==

= Does it work with Elementor, Divi, WPBakery, Oxygen, Bricks or Beaver Builder? =

No. Those builders store their layout outside of blocks, so there are no
blocks to measure and those pages produce nothing. The coverage report in
Settings names each builder it finds on your site and counts the pages
affected. If most of your site is built that way, this plugin has little to
tell you.

= I activated it and the Reports screen is empty. =

Two things have to be true before a number exists. The page must have been
opened in the block editor, actually changed, and saved since activation.
Then someone other than an editor of that page must have visited it. Your
own visits and previews are never counted.

= Do I need a cookie consent banner for this? =

No. No cookies are set, no browser storage is used, no identifier is
created, and no IP address is stored. There is nothing to consent to.

= Why are the numbers lower than Google Analytics or my server logs? =

Measurement runs in the reader's browser and the beacon is sent when the
reader leaves the page. Content blockers, crashed tabs, closed laptops and
unusual browsers all cost measurements. Treat the figures as directional and
compare blocks within a page rather than reading the totals as a count of
visits.

= Does it count unique visitors? =

No. There is no visitor identifier, so two people reading the same block on
the same day are two additions to the same number, and so are the same
person reading it twice.

= How much will this add to my database? =

One row per block, per day, per device type. Traffic volume does not change
it. The Settings screen estimates the daily row cost of each post type
before you enable it, and once the daily table passes its row budget the
oldest days are folded into monthly totals rather than deleted.

= What is not counted, by design? =

Anyone who can edit the post they are viewing, plus previews and the editor
itself. Archives, the front page and search results, where the same block
can appear many times on one screen. Headers, footers and other template
parts. Blocks nested more than two levels deep, known crawlers, feeds,
embeds and unpublished posts. Subscribers and customers *are* counted — on a
membership or shop site they are the real audience.

= Does it support Multisite? =

No. On a Multisite install the plugin registers nothing and says so.

= What happens if I delete the plugin? =

Deleting from the Plugins screen erases your measurement history and strips
the block identifiers back out of your post content. That cannot be undone.
Deactivating is safe and destroys nothing. To install a new version, upload
it over the existing installation rather than deleting first.

If you want the identifiers taken out of your content while keeping the
plugin, run `wp block-insights strip`. It restores each post to the exact
bytes it had before the identifiers were added, and posts whose content
would not survive a parse/serialize round trip are left untouched and
reported as skipped.

= Is there a paid version? =

Yes. Pro adds the before-and-after comparison, the printable client report
and a CSV export. Everything described above is in the free version, without
limits or expiry.

== Screenshots ==

1. Reports — every measured page, worst drop-off first, with the block that
   lost the most readers named on each row.
2. A single page, block by block in document order. The reach column is the
   curve; the Change column marks the step where readers stopped.
3. Settings. The coverage report says how many pages can be measured and
   names the page builder found on the ones that cannot.
4. The Manual, shipped in the plugin: what the numbers mean, when a block
   counts as reached, and how far to trust the figures.
5. The editor sidebar. Reach, dwell and clicks for the block you are editing,
   next to the block itself.

== Changelog ==

= 1.0.5 =
* First release on wordpress.org.
* The Reports screen, the page breakdowns and the editor sidebar are free,
  with no page limit, no page-view limit and no expiry.
* Edits are recorded by wording as well as by structure, so rewriting a
  heading or a paragraph is registered as a change rather than only adding,
  removing or reordering blocks. The change log also records which blocks
  were touched, which it never did before.
* The plugin is now translatable; previously the strings were marked up but
  no translation was ever loaded.

= 1.0.4 =
* Fixed: block labels in reports ran their text together, so a list read as
  "Unlimited seatsPriority supportQuarterly review" and a table came out as
  every cell concatenated. Parts that were separate on the page are separated
  in the label, and a figure or table now takes its caption when it has one.
* Fixed: a container block with no text of its own was reported by its type
  name, so a call to action appeared as "core/buttons". It now takes the text
  of what it contains, which for a button is the button label.

= 1.0.3 =
* Changed: the product slug this build reports was renamed to match the
  store. Licence activation is unaffected for new installs; an existing
  activation may need the licence key entering once more.

= 1.0.2 =
* Added: a Manual screen under Block Insights, explaining what reach, dwell
  and clicks mean, when a block counts as reached, how far the figures can
  be trusted, and what deleting the plugin destroys. Reachable from the
  Plugins screen too.

= 1.0.1 =
* Fixed: the client report printed its period one day early in time zones west
  of UTC, so an August report could read "July 31 - August 30".
* Fixed: a visit that reached more than 64 blocks was discarded entirely, page
  view included. Long pages lost their most engaged readers. Payloads are now
  truncated rather than rejected, and the limit is far higher.
* Fixed: blocks that nobody reached were left out of reports, so "reached the
  end" described the last block anyone reached instead of the end of the page.
  They now appear at 0%, except where the block did not exist during the period.
* Reports now say so when a period reaches further back than the day-level data
  retention has kept, instead of quietly showing a smaller number.

= 1.0.0 =
* First public release.
* Client Report: a printable, letterheaded document, with period presets and
  a CSV export of every measured page.
* Self-view exclusion: anyone who can edit a page is not counted on it,
  including page previews.
* Report documents now say when they show only the worst pages of a larger set.

= 0.2.0 =
* Measurement: reach, dwell, and clicks per block; daily aggregates.
* Site-wide Reports screen: drop-off per page and per block.
* Editor sidebar with per-block numbers and before/after comparison.
* Row-budget retention: old daily data is compacted to monthly, not deleted.
* Settings screen for choosing which post types are measured.

= 0.1.0 =
* Persistent block ID attribute registration and front-end injection.

== Upgrade Notice ==

= 1.0.5 =
First wordpress.org release. The Reports screen and the editor sidebar are
free, with no limits. Rewriting a heading or a paragraph now counts as a
change, so before/after has something to compare against.

== Uninstalling ==

Deactivating the plugin stops measurement and changes nothing else.

Deleting the plugin runs a full cleanup, and this one modifies post content,
so it is worth knowing in advance:

1. Every block ID this plugin added is removed from every post, restoring
   post content to the exact bytes it had before the plugin was installed.
   Posts whose content would not survive a parse/serialize round trip are
   left completely untouched and reported as skipped.
2. The statistics tables are dropped and all collected data is deleted.
3. Plugin options and the cached block-ID lists are removed.

To remove the block IDs without uninstalling, run `wp block-insights strip`.

== Verifying that strip restores your content exactly ==

`wp block-insights strip` removes every block ID this plugin has written and
restores post content to its pre-assignment bytes. To verify that on your own
site:

1. Dump post content before stripping:

   wp db export before.sql --tables=$(wp db prefix)posts

2. Run the strip (use --dry-run first to see what would change):

   wp block-insights strip --yes

3. Dump again and compare:

   wp db export after.sql --tables=$(wp db prefix)posts
   diff before.sql after.sql

The only differences must be the removed `"biId":"..."` entries in block
comment delimiters.

== For developers ==

WP-CLI commands: `wp block-insights audit`, `render`, `compact`, `strip`.

Filters: `block_insights_post_types`, `block_insights_reports_capability`,
`block_insights_exclude_viewer`, `block_insights_row_budget`,
`block_insights_trusted_proxies`.

Set `block_insights_trusted_proxies` behind a CDN or reverse proxy, or rate
limiting treats every visitor as one address.

== Building from source ==

The editor scripts in `build/` are compiled from the uncompiled sources in
`src/`, which are included in this plugin. To rebuild them:

   npm install
   npm run build

The build uses @wordpress/scripts (webpack). The front-end measurement
script, `assets/js/bi.js`, is plain JavaScript with no build step and is
shipped exactly as written.
