distinctPreviewsById

Removes duplicates from the aggregated preview list by CollectedPreview.id, warning via stdout when a duplicate is observed.

Called from the IR generated by collectAllModulePreviews() to merge previews from this module with previews discovered through the per-declaration hint mechanism (previewHint_<scope>(value: PreviewHintMarker_<hash>?): CollectedPreview functions). When a dependency module also uses collectAllModulePreviews() (i.e. it re-exports its own dependencies), the same CollectedPreview may reach the aggregator through more than one hint chain. Deduplicating by id guarantees a stable result regardless of how many paths a preview travels through.

Cross-artifact same-FQN previews — silent edge case: when two dependency modules declare a @Preview with identical fully-qualified name (e.g. both expose com.example.SharedPreview()), the FIR/IR generator produces a marker class with the same <canonical-key> hash on both sides. The JVM classloader and the KLIB linker each resolve the duplicate to a single symbol — the other preview's body is silently lost. The runtime can detect this only by observing duplicate CollectedPreview.id values reaching this aggregator, which is rare because the same FQN collapse already eliminated one of them upstream. We surface those duplicates via warnDuplicatePreview, which routes per platform to each target's dedicated warning surface: JVM writes to System.err, Android uses Log.w, iOS uses NSLog, and JS / Wasm JS use console.warn (yellow-highlighted in browser DevTools and surfaced separately from console.log by headless test runners that filter by severity). Every actual prepends the shared WarnPrefix ("[ComposePreviewLab WARN] ") so log-collector filters configured against that literal match every platform's emission.

The compiler plugin also attempts a best-effort compile-time warning when its symbol scan returns duplicate hints (in HintDiscovery.discoverHints, which lives in the :compiler-plugin module and is therefore not Dokka-linkable from here), but the same upstream collapse means that warning rarely fires; the runtime signal here is the durable detection path.

Resolution: rename the underlying @Preview function in one of the colliding artifacts so its FQN no longer matches. @ComposePreviewLabOption(id = "...") override does NOT help on any platform: the FQN-based hint generator name collides at IR time before the override id is read on JVM / Android, and on KLIB-based targets the linker further dedups by IdSignature. Renaming the function is the only fix that works on every CMP target.

Web visibility caveat: even with console.warn, browser console output is not visible to a user who has not opened DevTools. If a @Preview "disappears" on a JS / Wasm JS deploy target, opening DevTools is the first triage step — the warning fires once per app launch, on first read of the collect[All]ModulePreviews() property.

Internal API — meant only as a callsite for the compiler plugin.