Skip to content

URL: https://mkdocs.justinsforge.com/memory/handoffs/links-app-v2-build-2026-07-11/

Links app v2 build, 2026-07-11

Branch feat/links-app-v2 in creatortrack (10 commits ahead of main, all typechecked, merged from 3 parallel worker branches feat/links-v2-{assets,render,editor}). NOT merged to main, NOT deployed, no prod migrations run. Live dev slot for review: http://100.97.43.104:3062/link-in-bio (worktree ~/forge-suite-wt/feat/links-app-v2, DB clone ct_feat_links_app_v2, log /tmp/ct-agent-feat_links_app_v2.log).

What was built

Foundation (contract): lib/links/theme.ts (PageSettings, LinksBlockData, LinkItem, capture fields, asset refs, pv linkIds), links added to BlockKind, PageTheme.settings (additive jsonb, no migration).

Assets + analytics (c0d5359): links.assets table (bytea, RLS) + migration; POST/GET /api/links/assets (4MB cap, mime allowlist); reusable ImagePicker.tsx (upload from computer + drag-drop + URL paste); resolveAssetRefs inlines asset:<id> as data URIs at publish (15MB cap, app.links_publish_ws GUC for RLS); listPages fills views/linkCount; getLinkClickSeries + GET /api/links/stats?linkId=; stats page gains Page views tile; pv rows excluded from click rollups.

Render engine (b0e5ed1): links group block with 4 layouts (classic/carousel/grid/card), imagePlacement/textAlign/textSize/outline/shadow, title+subtitle, collapsed mode, block+item visibility, schedule windows gated client-side on the static page (no republish needed); theme-css.ts (fonts incl. Google Fonts publish-only, colors, button radius/outline/shadow, backgrounds: solid/gradient/image blur+dim/4 textures); effects.ts (aquarium bubbles + stars, per-link bubble bursts, prefers-reduced-motion); headerless pages render no head section (page name is internal only); footer CreatorTrack anchor to creatortrack.ai; capture form renders configurable fields (name/phone/birthday/instagram/note) and POSTs fields{}; pv beacon fetch('/r/<pv>'); desktop media query >=720px.

Editor UX (d6b7d19, f16bd83): in-app sidebar (Links/Stats + per-page Editor/Page settings), pages list kebab menu (Open/Rename/Delete) + "N views, N links" metrics, Beacons-style AddBlockModal with searchable kinds + 4 layout tiles, LinkItemsEditor (add/remove/drag items, per-item visibility eye, schedule datetime, effect, subtitle, image), eye toggle on every block row, email-capture signup-fields matrix, PageSettingsPanel (fonts/sizes/colors/radius/backgrounds/effects, debounced autosave), LinkAnalyticsPeek (per-link click series side panel), copy-link + open-in-new-tab buttons, phone/desktop preview toggle.

Migrations on the branch (applied to the dev clone ONLY): 20260712T000310_..._links_assets_table.sql, 20260712T001646_..._kind_check_widen...sql (adds 'links' to blocks_kind_check; without it group blocks cannot insert).

Verification

Each worker verified E2E on its own dev slot (render: 45/45 assertion script; editor: full click-through; assets: upload byte-roundtrip, cross-workspace 404, seeded stats). Post-merge integration verified on :3062 via Playwright: list page metrics, sidebar tabs, Page settings panel, AddBlockModal, carousel links block created through the UI (CHECK constraint proven), dry-run publish HTML contains pv beacon + linkMap entry, CreatorTrack anchor, links-group CSS. Zero console errors. Screenshots: forge/logs/site-screenshots/links-v2-{editor,settings,addblock}.png.

Go-live steps (when approved; NOT done)

  1. Review + merge feat/links-app-v2 to main.
  2. Apply both migrations to prod lifeos (forge_workspace_migrate.py) BEFORE deploying.
  3. Deploy creatortrack-prod (cap Node heap per reference_creatortrack_deploy_oom_typecheck).
  4. Republish live pages so they pick up pv beacon + new rendering.

Three more workers: growth (lead magnet on capture blocks with on-page unlock + Mautic tag delivery, After-signup funnel handoff, per-link vanity aliases /go/<name> + UTM toggle, SEO/OG + Meta/TikTok pixels in page settings, audience analytics: countries/devices/recent-visitor journeys with email-to-click-trail join), funnels (links.funnels table + Funnels sidebar tab + step builder with drop-off strip + edge publish to page:<host>:f:<slug>:<n>), and edge (forge branch feat/links-v2-edge, NOT deployed: _lvid visitor cookie, country/device logging, capture fields storage, /go/* alias + UTM redirect, /f/* funnel serving, sync mirrors + leadmagnet: Mautic tagging; deploy checklist in infra/links-edge/README.md). Two more CT migrations on the branch (funnels table, audience columns), applied to the dev clone only. Integration verified live on :3062 (funnel created via UI appears in the capture block's After-signup picker; stats sections render seeded audience data; pv/fpv rows excluded from click rollups). Research: clickfunnels-funnel-mechanics-2026-07-11. Resilience answer: capture/click DATA is edge-durable (D1); only Mautic email SENDING pauses in a home outage (on-page lead magnet unlock covers the critical path; edge ESP like Resend is the documented follow-up if instant email matters).

Two workers per Justin's feedback: shell (sidebar rebuilt on the shared components/apps/AppSidebar.tsx primitive the Tasks app uses, so collapse/resize/mobile come free; "Links" list renamed to "Pages" with per-page categories (link-in-bio/email-signup/event/product/landing/other, links.pages.category migration) and a filter chip row with ?cat= URL persistence; pv SQL regex widened for A/B ids) and funnels2 (ClickFunnels implementation: 4 create templates incl. default Affiliate Bridge whose step 1 is an offer card; per-step sticky A/B variants hashed off the visitor id with per-variant beacons; per-funnel pixels firing Meta Lead / TikTok CompleteRegistration only on the post-capture step; funnel:<slug> Mautic tag + 24h UTM stash landing on the contact; step conversion report with continuation %, sub-40% "rewrite this card" flags, and a value-per-entry rollup from configured subscriber value + EPC; per-button UTM passthrough toggles). Two more migrations (pages.category, funnels.settings) applied to the dev clone only. Verified live on :3062 after a clean dev server restart (stale webpack build initially masked the new FunnelsClient; wiping .next fixed it). Deferred ClickFunnels recs: evergreen countdown (rec 8), email re-entry tokens (rec 9).

Known gaps / follow-ups

  • Edge Worker (forge/infra/links-edge) stores only email/brand/source/pageId/tags: extended capture fields{} are sent but dropped until capture.ts + a D1 column land (small forge-repo follow-up).
  • ImagePicker is wired in assets code but BlockEditor image fields still use URL inputs with a commented ImagePicker usage ready to switch on (one-line swaps, left off to avoid cross-worker file conflicts).
  • Views only start counting after a republish (pv beacon must be in the deployed page HTML).
  • Parity research report with prioritized gap list (theme presets, lead magnets, pixels, SEO/OG fields, UTM tagging, geo/device analytics): links-parity-beacons-linktree-2026-07-11.