What's new in Tessera.

We ship often. 30 releases so far — every change is documented here with version, date, and category.

LATESTv0.10.37May 24, 2026Drill-down respects joined Formidable data sources
v0.10.37

Drill-down respects joined Formidable data sources

  • FixedChart drill-down showed entries from the wrong Formidable form for joined-source widgets. Clicking a point on a line/bar/pie chart bound to a joined data source (primary form + one or more linked forms) opened the modal with entries from whichever form sat first alphabetically in the picker, not the joined view. The widget_drilldown REST handler skipped the data_source_id route for Formidable joins — the v0.10.35 fix only covered custom MySQL tables. Joined widgets now run through a new joined_drilldown() path that uses the same Tessera_Join_Builder the chart computes against, hydrating each primary entry with its linked-form values keyed as . so the modal columns line up with how the editor configured the widget.
  • ImprovedLine/area drill-down on joined sources now filters by the clicked bucket. Day/week/month/year labels are translated back into a half-open [start, end) window on created_at so the entry list matches the bar the user clicked instead of dumping every entry in the date range.
  • Improvedform_id on a source-bound widget falls back to the source's primary form when the editor left a stray default in place — keeps the no-join Formidable source path honest too.
v0.10.36

Custom-table rows: view + edit with Formidable-style modal

  • NewClick a custom MySQL table row in any drill-down or data-table widget to open a full view + edit modal — same shell as the Formidable entry modal. Inputs are typed by MySQL column kind: integer columns get , floats get step="any", date columns get a date picker, datetime/timestamp get a datetime-local picker (with MySQL ↔ ISO conversion handled), tinyint(1)/bool columns get a checkbox, long-string columns get a textarea. The primary-key column is displayed but marked read-only.
  • NewTwo new REST endpoints — GET /widgets/:id/row/:pk and PUT /widgets/:id/row/:pk. The widget resolves its bound data source server-side, so the JS only sends widget id + pk. Read access uses the same share-token-aware widget view cap; writes require manage_options. Column writes are filtered through the adapter's live SHOW COLUMNS check so stale UI keys never reach SQL, and the PK column is silently skipped — identity is never writable.
  • NewTessera_Custom_Table_Adapter gains find_row() and update_row() — typed wpdb operations with format specifiers chosen per MySQL column kind (%d for ints + bool, %f for decimals, %s for strings/datetime). Strings go through wp_strip_all_tags before write.
  • ImprovedDrill-down row click no longer assumes integer entry IDs — string PKs (UUIDs, slugs) survive the round-trip into the row-detail modal unchanged.
v0.10.35

Drill-down modal respects custom MySQL table source

  • FixedChart drill-down showed Formidable entries for custom-table widgets. Clicking a data point on a bar or line chart bound to a custom MySQL table data source opened the modal with rows from the editor's default Formidable form (the first one alphabetically) instead of rows from the table. The widget_drilldown REST handler only read form_id and went straight to the Formidable adapter, ignoring data_source_id. Now it mirrors Tessera_Data_Engine::compute()'s source-aware dispatch — custom-table widgets get rows from the actual table with the synthetic group-field filter applied.
v0.10.34

Custom-tables picker race + public-demo reset endpoint

  • Fixed"Pick tables to allow" button on Settings → Custom MySQL data sources did nothing on click. The inline-script binding ran before tessera-admin.js had loaded the window.Tessera global (footer-enqueued scripts execute after inline body scripts), so the click listener was never attached. Listener now attaches unconditionally and resolves the global at click time.
  • NewPublic-demo reset endpoint — new POST /tessera/v1/demo/reset that wipes + reseeds in one call, gated by a shared-secret header (X-Tessera-Demo-Secret) matched against the TESSERA_DEMO_RESET_SECRET constant or tessera_demo_reset_secret option. Powers the nightly Vercel cron that keeps the shared marketing-site demo fresh.
v0.10.32

Security: license key no longer in the DOM

  • SecurityLicense key removed from the License page DOM. The Stripe billing-portal button previously carried the full key as a data-key="…" attribute, which made it visible to anyone running View Source or DevTools on the License screen. Now the key never leaves the server — clicking Manage Billing hits a new POST /tessera/v1/billing-portal WP REST endpoint that reads the stored license + email server-side and forwards to the marketing portal API. Display still uses the masked TES-XXXX-XXXX-LAST4 pattern.
v0.10.31

AI sees custom tables + "make Tessera the admin landing page"

  • NewAI dashboard builder now sees custom MySQL tables in its schema. It can propose widgets bound to data_source_id (joined Formidable sources or custom MySQL table sources) in addition to form_id direct-Formidable widgets. group_field / metric_field / columns are validated against the live MySQL schema; if the model hallucinates a column, the normalizer auto-picks a real categorical or numeric one as fallback.
  • New"Make Tessera the admin landing page" toggle in Settings → General → Publishing. When on, admins log in and are redirected from the WP Dashboard straight to Tessera → Dashboards. Useful when Tessera is the primary tool on the site — keeps users from getting lost in WordPress chrome. Default off so we don't change behavior for existing customers.
  • ImprovedCustom-table widget fallback picks are smarter — categorical: prefers columns with names like status, region, chapter, type, then any short-string column, then anything non-datetime / non-number. Numeric: first column with MySQL type int/decimal/etc. Same logic the Formidable side has.
v0.10.30

Custom tables — picker + widget editor binding

  • NewData Sources → + New Data Source now shows a type chooser when at least one MySQL table is allow-listed: "Formidable form" or "Custom MySQL table". Single Formidable users see no extra step — the chooser only appears when you've enabled custom tables.
  • NewCustom-table source editor — pick from your allow-listed tables, confirm the auto-detected primary key + datetime column, name it, save. Display name is what shows in the widget editor and Data Sources list.
  • NewWidget editor field picker is source-aware — when a widget is bound to a custom-table source, the metric/group/stage/columns dropdowns populate from MySQL columns (with type hints) instead of Formidable fields. Same UI, polymorphic underneath.
  • ImprovedData Source cards distinguish custom-table sources at a glance — monospaced table-name badge + "TABLE" type chip.
  • FixedNative confirm() in Data Sources deletion replaced with the branded confirm modal (consistent with the rest of the plugin).
v0.10.29

Reporting modal: "Done" no longer looks like Save

  • FixedScheduled Reports modal trapped users into thinking "Done" was Save. Footer button was styled primary purple, so admins would fill in the schedule form, hit Done, and the form silently discarded — actual save was the smaller "+ Add Schedule" button inside the form card. Renamed footer to "Close", dropped the primary styling, and added an interception: if you have unsaved schedule fields and click Close, you get a "Add this schedule before closing?" prompt with Add-and-close / Discard options.
v0.10.28

Foundation: custom MySQL tables as data sources (allow-list + engine)

  • NewCustom MySQL tables can now be data sources alongside Formidable forms — opt-in via wp-config constant TESSERA_CUSTOM_TABLES or via a new allow-list picker in Settings → General → Custom MySQL data sources. Default = nothing exposed; admin must explicitly check each table.
  • NewTessera_Custom_Table_Adapter: full query surface (count, aggregate, group_by, time_series, distinct_values, rows) with three-layer security — table allow-list, live SHOW COLUMNS validation per query, and a row-scan cap. Identifier quoting via backtick + char-whitelist so column or table names are never concatenated raw.
  • NewData engine routes widgets to the custom adapter when their bound data source is source_type=custom_table. All widget types except formula (which references other widgets) are supported.
  • NewSchema migration: tessera_data_sources table gains source_type, table_name, pk_column, datetime_column. Existing Formidable + joined sources backfill to source_type=formidable automatically — no data shape changes.
  • NewREST endpoints: GET /custom-tables/database (all DB tables for picker), GET /custom-tables (allow-listed), GET /custom-tables/columns?table=X, POST /custom-tables/allowed (UI persist; 403 when locked by constant).
  • ImprovedSettings card includes a one-click table picker modal showing every base table in the database with row counts and auto-detected datetime columns. Filter as you type. Save updates the allow-list.
v0.10.27

AI proposal editor — table columns match the real widget editor

  • ImprovedTable columns now picked from a checkbox grid in the AI proposal editor, identical to the real widget editor. The previous text-area-with-one-key-per-line was wrong UX — admins shouldn't need to type field keys when a labeled checkbox list exists for the same purpose. Auto-fill grid, 180px min column, scrolls past 220px.
v0.10.26

Data-safe uninstall — never lose dashboards again

  • SecurityUninstall no longer drops your data by default. Historical Tessera dropped wp_tessera_dashboards, wp_tessera_widgets, and wp_tessera_data_sources whenever an admin clicked Delete on the plugin row — even on a deactivated duplicate copy. Now the uninstall script preserves all tables unless tessera_purge_on_uninstall is explicitly set to true. Reinstalling Tessera picks every dashboard back up.
  • NewNew "Danger zone" section in Settings → General with an opt-in "Purge all Tessera data when the plugin is deleted" toggle. Off by default. Customers who actually want a clean uninstall flip it on, save, then delete — everyone else is safe even from a misclick.
  • ImprovedLicense key now survives uninstall unless the purge toggle is on. Reactivation no longer requires re-entering the key.
v0.10.25

AI builder — actions persist + rich edit modal

  • FixedAI-proposed actions and filters were lost on commit. Dashboard::create() ignored the settings payload, so the new dashboard came up with no actions, no filters, and no theme even though the proposal had them. Now create() accepts settings directly + top-level actions / filters shortcuts (same shape as update()).
  • ImprovedRich proposal widget editor — clicking a proposed widget now opens the same two-column layout as the real widget editor: config form on the left, live preview tile on the right. Type / form / metric / group field / etc. changes debounce a /widgets/preview call and re-render the tile via the same Tessera.render pipeline. No more guessing what your edits will look like.
  • ImprovedType-change refreshes the form — switching a widget from bar to line re-renders the editor with the correct required fields. Same for changing the bound form (drops stale metric/group/stage).
v0.10.24

Editable AI proposal — tweak before you commit

  • NewClick any proposed widget in the AI preview to edit it before building. Adjust title, type, form, metric, metric field, group / stage field, bucket, date range, columns (for tables), and size — all in-memory until you hit Build. Cancel discards.
  • NewDelete a widget from the proposal with the × on each row, without regenerating the whole thing.
  • ImprovedBetter preview UX — rows highlight on hover, show meta like "count by chapter_id · 30d", and column/height are shown inline. Click anywhere on a row to edit.
  • ImprovedForm change in editor refreshes field dropdowns — changing the bound form clears stale metric/group/stage values and re-fetches the new form's fields.
v0.10.23

AI builder fills in the blanks

  • NewAI now proposes Actions too — create-entry buttons alongside widgets and filters. e.g. "Operations overview" can include a "Log daily count" action button. Each action carries a form_id, label, and scope (admin / public). Persisted alongside filters on commit.
  • ImprovedSmarter prompt — explicit per-type required fields (bar/leaderboard/pie/doughnut need group_field, funnel needs stage_field, table needs columns, line/area need bucket). Tells the model to always pick a best-fit field from the schema rather than omit it.
  • FixedEmpty data-table widgets — AI sometimes forgot the columns array. Normalizer now auto-picks 3-5 useful columns from the form (skipping UI-only field types) when missing.
  • FixedBar / pie / doughnut / leaderboard widgets that lacked group_field now get a sensible default chosen server-side — prioritizing select/radio/checkbox fields, then category-named text fields, then any non-UI field.
  • FixedFunnel widgets without stage_field get the same categorical-field fallback.
  • FixedNon-count metric without metric_field auto-picks a numeric field, or falls back to count if the form has no numeric fields.
v0.10.22

Fix: AI dashboard builder 500 error

  • FixedPOST /dashboards/ai-build was 500-ing on every request. Tessera_Insights::complete() returns { summary, model, generated } — the new AI dashboard builder was treating it as a raw string, so (string) coerced the array to "Array", which json_decode rejected. Unwrap to $resp['summary'] like the widget builder does, plus strip any conversational preamble before the first { so a chatty model doesn't break parsing.
v0.10.21

Action-modal success: buttons + spacing fixed

  • FixedDashboard action submission now triggers the parent "Entry created · Add another / Close" overlay. The bare-form postMessage detector was looking for Formidable's native success class (.frm_message) but filter_form_success replaces the output with .tessera-form-success. Detector now matches both — parent modal correctly shows the overlay and refreshes widgets without a full page reload.
  • FixedSuccess-message text spacing — "Submitted." was running into "Thanks…" because the title + sub were inline spans inside a flex parent. Both now display:block so they stack cleanly.
v0.10.20

✨ AI dashboard builder

  • NewBuild a dashboard from a description — Click "✨ Build with AI" on the dashboards list, type something like *"Operations overview for sorority chapters with member counts, top performers, and a chapter filter"*, and the AI proposes a complete dashboard: title, theme, widgets with metric/group/date_range/size, and 1-3 dashboard filters. Review the proposal, regenerate if it's off, or click Build this dashboard to commit.
  • NewTwo-step REST flow: POST /dashboards/ai-build returns a draft proposal (no DB writes); POST /dashboards/ai-build/commit persists the proposal as a draft dashboard + widgets + filters + theme in one go.
  • NewScope to specific forms — Optional chips in the builder modal let you restrict the AI to a chosen subset of Formidable forms. Leave all unchecked to give it every form.
  • NewServer-side validation of the model output: widget types whitelisted, sizes clamped to the 12-column grid, widget configs pointing to unknown form IDs dropped before commit. The AI can't produce a broken dashboard even when it hallucinates.
  • ImprovedUses the existing Tessera_Insights provider abstraction — works with Anthropic Claude, OpenAI, Google Gemini, or Groq (BYO key). Same provider set as AI Insights chat.
v0.10.19

Filter bar visible in the editor too

  • ImprovedDashboard filter bar now renders inside the editor, not just on the published page. Configured filters appear right above the widget grid in wp-admin — pick a value and widgets re-fetch live. Matches what viewers see.
  • ImprovedWidget tiles in the editor now carry data-form-id so the same filter-scope logic (form / all / specific widgets) used on the public dashboard works identically in admin.
  • NewTessera.initFilters exposed on the JS namespace so the editor (and any future surface that renders dashboards via the runtime) can wire up filter dropdowns without re-implementing them.
v0.10.18

Fixed: plugin self-deactivating after WP update

  • FixedPlugin would self-deactivate after every WP plugin update. WordPress fires the deactivation hook during the deactivate-reactivate file swap, and Tessera was using it to release the license slot on the server. The freshly-updated plugin then reactivated with no slot and stranded the customer. Now we sniff action=upgrade-plugin|update-plugin|upload-plugin|overwrite=1 and skip the slot release during upgrades. Only an explicit "Deactivate" click in WP Admin → Plugins releases the slot.
  • SecurityDefence in depth — the Tessera_License::deactivate_remote() call now runs only when WP is *not* in upgrade context, so accidental slot leaks during automatic updates can't silently un-license a paying customer.
v0.10.17

Interactive dashboard filters

  • NewDashboard-level field filters — Add one or more dropdowns above the widget grid so viewers can narrow the dashboard by any Formidable field. e.g. a sorority dashboard with a "Chapter" filter that re-fetches every widget for the selected chapter. Configure from the new Filters toolbar button.
  • NewMulti-select filters — Pick more than one value (checkbox dropdown). Widgets refetch with IN (…) semantics so the filter holds e.g. Alpha + Beta chapters at once.
  • NewCascading filters — Mark a filter as a child of another. When the parent changes, the child's option list re-fetches via /forms/:id/field-values with the parent overlay, so District options narrow to whichever Region is currently selected.
  • NewFilter scope per widget — A filter can target "all widgets from this form" (default), "every widget on the dashboard", or a hand-picked subset of widgets. Lets you keep certain context tiles (e.g. global KPI cards) un-filtered while everything else refilters.
  • NewAuto-populated or custom value lists — Auto pulls distinct values from real entries; custom lets the admin type a fixed list (Alpha / Beta / Gamma).
  • NewDeep-link filter state — Selected filter values write to the URL as ?f_=value so admins can share a pre-filtered dashboard URL. Reloading the page restores the same view.
  • ImprovedField-values endpoint now accepts a parent filter overlay (parent[i][field_id] + parent[i][values][]) and looks up by field ID, not just key — supports the cascade flow.
  • ImprovedWidget data endpoint accepts a df[field_id]=value overlay (comma-separated for multi-select) and merges it AND-style with the widget's own configured filters before computing.
v0.10.16

Widget editor decluttered

  • ImprovedRegex extraction collapsed by default in pie / doughnut / bar / leaderboard / funnel widget editors. The field is now hidden behind a clear "Advanced — extract a sub-value with a regex" disclosure so 99% of users never see it. Auto-expands when an existing pattern is already saved on the widget.
v0.10.15

Polished Quick Actions bar, fixed toolbar wrap

  • FixedToolbar no longer breaks when buttons wrap to a second line. Vertical separators added between Configure / Display / Primary clusters; theme button gets a label ("☾ Dark" / "☀ Light") instead of an orphan icon.
  • ImprovedQuick Actions bar redesign — proper card styling with brand-color left rail, "Quick Actions" label with bolt icon, compact pill buttons (no longer jumbo primary), proper gear icon for Edit instead of an unstyled cog character.
  • ImprovedAdmin scope chip on action buttons (small "admin" pill inside the button) so it's instantly clear which buttons backend users see vs. which everyone sees.
v0.10.14

Branded modals, success buttons, generic success copy

  • NewAdd another / Close buttons in the create-entry success state. After submission the modal stays open with two clear next steps instead of auto-closing.
  • ImprovedGeneric success message on every submitted form. No more "a real human will be in touch within 12 hours" appearing in customer dashboards.
  • ImprovedBranded prompt dialogs replace native window.prompt() everywhere — Rename Dashboard now opens a styled modal that matches the rest of the plugin.
  • ImprovedEditor toolbar reorganized into Configure / Display / Primary clusters with subtle separators. "Actions" button kept its name but gained a tooltip that explains it adds create-entry buttons to the dashboard.
  • NewAction buttons in the editor — configured Quick Actions now render under the toolbar in the admin editor too, not just on the public dashboard. Admins can test entry submission without opening the View page.
  • ImprovedCache-bust on iframe reload so Formidable's [increment] auto-ID defaults bump on every "Add another" submission.
  • FixedNo more native alert() boxes on entry save failures. Branded toast notification used everywhere.
v0.10.13

Dashboard Actions — create entries from connected forms

  • NewDashboard Actions — Configure one or more "+ New" buttons per dashboard that launch a connected Formidable form in a modal. Submitters never have to leave the dashboard. New entries auto-refresh widgets via the existing Formidable cache-bust hook.
  • NewPer-button visibility scope — Each action is either admin-only (visible in WP backend) or public (visible to everyone with view access). Pairs naturally with role-based dashboard visibility for front-end management apps.
  • ImprovedDashboard model — actions live in settings.actions and are hydrated onto $dashboard['actions'] for renderers; updates merge into existing settings instead of replacing.
  • ImprovedBare form embed (?tessera_form=N) now posts a submitted event up to its parent window via postMessage. The parent dashboard listens once and refreshes itself on success.
  • FixedUpdater respects ?force-check=1 on the Updates screen. The plugin's 12-hour internal cache no longer blocks a fresh version check when the admin clicks "Check Again".
v0.10.12

Role-based dashboard visibility, properly

  • NewRole checkbox UI in the Visibility modal — pulls live from wp_roles(), so custom roles from WooCommerce, Members, User Role Editor, etc. show up automatically. Each row shows role display name + slug.
  • FixedValidation — Saving "Specific roles" with no roles checked now warns instead of silently saving an empty allow-list.
  • ImprovedVisibility modal copy clarifies that administrators always retain access regardless of the role-based gate.
v0.5.0

Resizable widgets, full data-table, billing portal

  • NewResizable widgets — Drag the corner handle on any widget to resize. Snaps to the 12-column grid (and to row units). Inner CSS responds to the new size via container queries.
  • NewFull data-table widget — Real-time search, click-to-sort columns, pagination, CSV export, column visibility toggle, dense/normal row density.
  • NewAnnual subscriptions — Licenses auto-renew. Customers can cancel anytime from the customer portal (Stripe Billing Portal).
  • NewAdmin dashboard — Internal /admin console for purchases, license issuance, refunds, and coupon management.
  • NewLaunch coupons — Promo codes accepted at checkout (LAUNCH30, EARLY20, FRIEND50).
v0.4.0

Multi-form joins, AI chat, dark mode, share customization

  • NewMulti-form joins — Data Sources admin page + JOIN query engine. Link an intake form to a follow-up form by request ID; widgets read across both.
  • New4 new widget types — Trend KPI (sparkline + delta %), Funnel, Calendar Heatmap (365-day), Gauge.
  • NewAI Insights chat — Multi-turn conversation about your dashboard data. Supports Claude, OpenAI, Gemini, and Groq (free tier).
  • NewDashboard templates — 7 pre-built starters: Operations, Customer Satisfaction, Conversion Funnel, Executive Summary, Sales Pipeline, Marketing.
  • NewShare-page customization — Logo (WP media picker), header HTML, footer HTML, logo position.
  • NewBare share view — Dashboards opened via share link render without WordPress theme.
  • NewDark mode — Per-dashboard light/dark toggle, persisted to settings.
  • NewWidget live preview — Configuration modal shows real-time preview as you change filters/metrics/group.
  • ImprovedFilter value autocomplete — Filter inputs show clickable example values from real data; categorical fields render as dropdowns.
  • NewFirst-run onboarding — Welcome panel with 3 step-by-step CTAs, dismissible.
  • NewIn-plugin documentation — 14-section docs hub built into the admin.
v0.3.0

Auto-publish, scheduled reports, PDF, AI insights, share links

  • NewAuto-publish — Publishing a dashboard auto-creates a WordPress page. Title syncs; page trashed on unpublish.
  • NewPublic share links — Per-dashboard 40-char token URL that bypasses visibility. HMAC-verified, instantly revocable.
  • NewScheduled email reports — Daily/weekly/monthly cadence. Hourly WP cron runner; HTML email with KPI/leaderboard/table snapshots.
  • NewPDF export — jsPDF + html2canvas bundled locally (SHA-256 verified). Button in editor + every front-end dashboard.
  • NewWhite-label — Brand name, color, logo. Applied via CSS variables. Agency feature.
  • NewAI Insights — Claude API integration. 1-hour cache per dashboard.
  • ImprovedPer-widget polish — Value format (currency/percent/K-M suffix), color picker, auto-refresh interval.
  • ImprovedVersioned schema auto-migration — dbDelta runs on plugins_loaded when version differs.
v0.2.0

Full data engine, REST API, admin UI, shortcode, demo seeder

  • NewData layer — Models (Dashboard, Widget, Data_Source), Formidable adapter (count/sum/avg/min/max, group_by, time-bucket, filters).
  • NewSafe formula evaluator — Recursive-descent parser with whitelist functions. No eval(), no PHP function exposure.
  • NewCached aggregation engine — Transient-based caching with configurable TTL.
  • NewREST API — Full CRUD on dashboards + widgets + data sources, plus widget-data, CSV export, license, demo. All with permission callbacks.
  • NewAdmin UI — Design system with CSS tokens, modal/toast/spinner primitives. Dashboards list, dashboard editor with drag-drop, demo data page, settings, license.
  • NewRender layer — Shortcode + Gutenberg block + role-based visibility. Chart.js + Sortable.js vendored locally with SHA-256.
  • NewDemo data seeder — One-click creates 3 sample forms, ~500 entries, 4 prebuilt dashboards.
v0.1.0

Initial scaffold

  • NewPlugin bootstrap with version constants and license server config.
  • NewCustom tables for dashboards, widgets, data sources (dbDelta migrations).
  • NewLicense client with HMAC-verified responses, activation, weekly cron recheck, soft-fail.
  • NewPlan-gated feature flag system (Starter / Pro / Agency).
  • NewAdmin menu shell + License admin page with masked key display.