@certrev/cert-block 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +1 -1
- package/dist/builder/cert-review-card.d.ts +6 -2
- package/dist/builder/cert-review-card.d.ts.map +1 -1
- package/dist/builder/cert-review-card.js +22 -6
- package/dist/builder/cert-review-card.js.map +1 -1
- package/dist/builder/index.js +1 -1
- package/dist/builder/index.js.map +1 -1
- package/dist/components/preview-block.js +1 -1
- package/dist/components/preview-block.js.map +1 -1
- package/dist/modal/certrev-cert-modal.styles.d.ts +1 -1
- package/dist/modal/certrev-cert-modal.styles.js +9 -9
- package/dist/modal/certrev-cert.js +10 -10
- package/dist/verify/fetch-with-deadline.d.ts.map +1 -1
- package/dist/verify/fetch-with-deadline.js +3 -1
- package/dist/verify/fetch-with-deadline.js.map +1 -1
- package/package.json +1 -1
- package/src/builder/cert-review-card.tsx +28 -8
- package/src/builder/index.tsx +1 -1
- package/src/components/preview-block.ts +1 -1
- package/src/modal/certrev-cert-modal.styles.ts +9 -9
- package/src/verify/fetch-with-deadline.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,64 @@ change the rendered cert output and never break a compiling integration** (regis
|
|
|
5
5
|
exports, types). Anything that would alter what a visitor sees, or require integration changes,
|
|
6
6
|
is announced loudly here first.
|
|
7
7
|
|
|
8
|
+
## 1.0.3 · 2026-08-18
|
|
9
|
+
|
|
10
|
+
Patch: restores the placement a pre-existing Builder entry renders. **If you integrated via
|
|
11
|
+
Builder before 1.0.0, upgrade to this from any 1.0.x.**
|
|
12
|
+
|
|
13
|
+
- **1.0.0 silently repainted already-exported Builder entries from banner to sidebar.** `mode` is
|
|
14
|
+
a PLACEMENT input, not one of the 19 wire keys, and the exporter emits only the wire projection,
|
|
15
|
+
so every entry exported to date carries no `mode` key. Through 0.5.5 `CertReviewCard` read
|
|
16
|
+
`props.mode ?? 'banner'` and those entries rendered the banner face. 1.0.0 removed that fallback
|
|
17
|
+
so the renderer would own a single default, which changed what those entries paint. Consumers
|
|
18
|
+
install unpinned, so it reached live pages with no entry edit and no upgrade on their side.
|
|
19
|
+
- **The fallback is restored, as a bridge and not as a reversal.** The sidebar default stands
|
|
20
|
+
everywhere it was actually the point: a direct `renderCertBlock` caller omitting `mode` still
|
|
21
|
+
gets sidebar, and `sidebar` is still the registered `defaultValue`, so every block created in
|
|
22
|
+
the editor from now on carries an explicit `mode` and never reaches the fallback. The bridge
|
|
23
|
+
covers only entries that predate the exporter emitting `mode`, and is deleted once the exporter
|
|
24
|
+
writes it and existing entries are backfilled. The registered enum, `defaultValue`, helper text
|
|
25
|
+
and `render-cert-block.ts` are untouched by this release.
|
|
26
|
+
- **A third gate, of a kind neither existing one could be.** The rendered-surface sweep checks
|
|
27
|
+
text that IS on a surface; the string-literal rule checks strings that ARE in the source. This
|
|
28
|
+
regression was the absence of a value: what an entry paints when an input is missing. That is
|
|
29
|
+
invisible to both, and it is why 1.0.0 shipped green. `builder.test.tsx` now pins the
|
|
30
|
+
exported-entry shape (no `mode`, and `mode: null`) to the banner face, pins an explicit `mode`
|
|
31
|
+
as still winning, and pins the direct renderer as still defaulting to sidebar so the bridge can
|
|
32
|
+
never quietly widen into a package-wide change. Confirmed to fail with the fallback removed.
|
|
33
|
+
- **One editor-facing em dash**, in the design-time anchor placeholder (`builder/index.tsx`), now
|
|
34
|
+
a colon. It is JSX text rather than a quoted string, so the 1.0.2 literal rule could not see it
|
|
35
|
+
either; recorded here because it is the same blind spot, not a separate lesson.
|
|
36
|
+
|
|
37
|
+
## 1.0.2 · 2026-08-18
|
|
38
|
+
|
|
39
|
+
Patch: copy only, in strings nobody renders. No API change, no type change, no behaviour change.
|
|
40
|
+
|
|
41
|
+
- **The 1.0.1 sweep checked the wrong layer, and 1.0.1 shipped an em dash anyway.** It reached a
|
|
42
|
+
reader through the timeout error in `fetchWithDeadline`, which set one between the deadline and
|
|
43
|
+
the URL and prints into a consumer's console with our name on it. That is visible copy by every
|
|
44
|
+
test this changelog has been applying, and the 33-surface rendered sweep could not see it, because
|
|
45
|
+
a thrown error is not a surface anything renders. Found by auditing the published tarball rather
|
|
46
|
+
than the render. It now uses a middle dot: the URL it appends is labelled data.
|
|
47
|
+
- **Two more in the same class, found by sweeping every string in shipped code rather than the one
|
|
48
|
+
that was reported.** `renderPreviewBlock`'s missing-`expiresAt` error takes a full stop, because
|
|
49
|
+
the second half is a complete sentence stating why. Nine CSS section comments inside
|
|
50
|
+
`CERT_MODAL_CSS` take middle dots: that template literal is injected verbatim into the modal's
|
|
51
|
+
shadow root, so its comments ship to every page the modal opens on and are readable in devtools,
|
|
52
|
+
unlike the TypeScript comments around it, which `tsc` emits but nothing displays.
|
|
53
|
+
- **The gate is now two tests, because one could never have been enough.** The rendered sweep and
|
|
54
|
+
the new string-literal sweep catch disjoint sets: the rendered one sees assembled output but only
|
|
55
|
+
for surfaces something renders, and the literal one sees every shipped string but cannot tell
|
|
56
|
+
which ones reach a human. The literal rule lives in `scripts/check-publish-hygiene.mjs`, strips
|
|
57
|
+
comments with a real tokenizer rather than a regex (the ~795 in comments stay out of scope, and a
|
|
58
|
+
regex cannot tell `//` in a URL from a comment), skips test files, and runs in CI. Confirmed to
|
|
59
|
+
fail on all four 1.0.1 strings and pass with them fixed.
|
|
60
|
+
- **A note on how the first check missed the modal bundle.** Grepping the built
|
|
61
|
+
`dist/modal/certrev-cert.js` for the character returned 0 on 1.0.1, and that was a false negative:
|
|
62
|
+
esbuild's default `--charset` is ascii, so it emits non-ASCII as a `\u` escape rather than as the
|
|
63
|
+
character. Ten occurrences were in there. Any future check of a minified artifact has to decode it
|
|
64
|
+
first; grepping the raw bytes reads as clean no matter what is in it.
|
|
65
|
+
|
|
8
66
|
## 1.0.1 · 2026-08-18
|
|
9
67
|
|
|
10
68
|
Patch: copy only. No API change, no type change, no behaviour change beyond the five characters
|
package/README.md
CHANGED
|
@@ -606,7 +606,7 @@ published at `@certrev/cert-block/fixtures`).
|
|
|
606
606
|
|
|
607
607
|
## Version
|
|
608
608
|
|
|
609
|
-
`1.0.
|
|
609
|
+
`1.0.3`: see [CHANGELOG.md](./CHANGELOG.md) for the release history and the semver contract
|
|
610
610
|
(patch/minor never change the rendered cert output or break a compiling integration). Publishes
|
|
611
611
|
**publicly** to npm as `@certrev/cert-block` (`publishConfig.access: public`) via GitHub Actions
|
|
612
612
|
trusted publishing (OIDC); the internal `@certrev` GitHub-Packages channel mirrors it.
|
|
@@ -28,8 +28,12 @@ export declare const CERT_COMPONENT_NAME = "CertREV Cert";
|
|
|
28
28
|
export interface CertReviewCardProps extends Partial<BuilderCertChromeData> {
|
|
29
29
|
/**
|
|
30
30
|
* Placement face — the ENTRY/editor chooses this, NOT the exporter (not a wire-type field).
|
|
31
|
-
*
|
|
32
|
-
*
|
|
31
|
+
*
|
|
32
|
+
* Absent ⇒ `banner`, via the compatibility bridge in the render body below. That is NOT the
|
|
33
|
+
* package default: a direct `renderCertBlock` caller omitting `mode` gets `sidebar`, and
|
|
34
|
+
* `sidebar` is the registered input's `defaultValue`, so blocks created in the editor carry
|
|
35
|
+
* an explicit `mode`. The bridge covers only entries exported before the exporter emitted
|
|
36
|
+
* `mode` at all, and is deleted once those are backfilled. See the render body for why.
|
|
33
37
|
*/
|
|
34
38
|
mode?: CertBlockMode | null;
|
|
35
39
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cert-review-card.d.ts","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,oCAAoC,CAAA;AAExF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAGlE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,iBAAiB,CAAA;AAEjD,MAAM,WAAW,mBAAoB,SAAQ,OAAO,CAAC,qBAAqB,CAAC;IAC1E
|
|
1
|
+
{"version":3,"file":"cert-review-card.d.ts","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAmB,MAAM,oCAAoC,CAAA;AAExF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAGlE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,iBAAiB,CAAA;AAEjD,MAAM,WAAW,mBAAoB,SAAQ,OAAO,CAAC,qBAAqB,CAAC;IAC1E;;;;;;;;OAQG;IACH,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAA;CACxC;AAsCD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,sCAoFxD;AAMD;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;IACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACpE,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC3B;AA0CD;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,YAAY,EAO5C,CAAA;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,YAAY,EA2BnD,CAAA;AAED,gGAAgG;AAChG,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,OAAO,cAAc,CAAA;IACzC,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;CACxC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,EAAE,4BAIlC,CAAA;AAED;;;;;GAKG;AACH,MAAM,WAAW,iCAAiC;IACjD,SAAS,EAAE,OAAO,cAAc,CAAA;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,YAAY,EAAE,CAAA;CACtB;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,iCAMlC,CAAA"}
|
|
@@ -81,12 +81,28 @@ export function CertReviewCard(props) {
|
|
|
81
81
|
}
|
|
82
82
|
: undefined;
|
|
83
83
|
const html = renderCertBlock({
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
84
|
+
// COMPATIBILITY BRIDGE, NOT THE DESIGN. Delete this fallback once the exporter emits an
|
|
85
|
+
// explicit `mode` and existing entries have been backfilled; until then it must stay.
|
|
86
|
+
//
|
|
87
|
+
// `renderCertBlock` owns the ONE placement default (absent or unrecognised ⇒ sidebar) and
|
|
88
|
+
// that decision STANDS: it is what a direct caller gets, and `sidebar` is the registered
|
|
89
|
+
// input's `defaultValue`, so every block created in the editor from now on carries an
|
|
90
|
+
// explicit `mode` and never reaches this line.
|
|
91
|
+
//
|
|
92
|
+
// The problem is the entries that already exist. `mode` is a PLACEMENT input, not one of
|
|
93
|
+
// the 19 wire keys (`BUILDER_CERT_CHROME_KEYS`), and the exporter emits only the wire
|
|
94
|
+
// projection (`options: { ...certData }`), so every entry exported to date has NO `mode`
|
|
95
|
+
// key at all. Through 0.5.5 this line read `?? 'banner'` and those entries rendered the
|
|
96
|
+
// banner face. Removing it in 1.0.0 did not give them the renderer's default in the
|
|
97
|
+
// abstract; it silently repainted live pages from banner to sidebar, with no entry edit
|
|
98
|
+
// and no version pin to protect them, because their install line is unpinned.
|
|
99
|
+
//
|
|
100
|
+
// So this is a bridge for pre-existing entries, NOT a reversal of the sidebar default.
|
|
101
|
+
// The three-copies-of-a-default problem that motivated removing it is still real and is
|
|
102
|
+
// still fixed: the helper text and the wire type's doc comment no longer name a default,
|
|
103
|
+
// and this line names one only for input the renderer can no longer distinguish from a
|
|
104
|
+
// deliberate choice. The exporter writing `mode` is what retires it.
|
|
105
|
+
mode: props.mode ?? 'banner',
|
|
90
106
|
theme,
|
|
91
107
|
// First-class banner memo split — renderCertBlock composes the requested
|
|
92
108
|
// part directly; no string-splitting. Ignored outside banner mode, and an unrecognised
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cert-review-card.js","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAsB,eAAe,EAAE,MAAM,oCAAoC,CAAA;AAGxF,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAEhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"cert-review-card.js","sourceRoot":"","sources":["../../src/builder/cert-review-card.tsx"],"names":[],"mappings":"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"}
|
package/dist/builder/index.js
CHANGED
|
@@ -67,7 +67,7 @@ export function CertRevEditorPlaceholder() {
|
|
|
67
67
|
font: '13px system-ui',
|
|
68
68
|
color: '#6b7280',
|
|
69
69
|
background: '#fff5fa',
|
|
70
|
-
}, children: ["CertREV Review
|
|
70
|
+
}, children: ["CertREV Review: ", _jsx("b", { children: "resolves at publish" }), " from issuer verification. The expert badge appears here once this page\u2019s article is certified, and only while the credential is valid."] }));
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
73
73
|
* The Builder block. ZERO inputs — a layout anchor only. Renders the current page's VERIFIED
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/builder/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,MAAM,OAAO,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AAexD,MAAM,cAAc,GAAG,aAAa,CAAsB,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,EAC/B,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,QAAQ,GAKR;IACA,OAAO,KAAC,cAAc,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,YAAG,QAAQ,GAA2B,CAAA;AACpG,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB;IACvC,OAAO,CACN,kDACiC,EAAE,EAClC,KAAK,EAAE;YACN,MAAM,EAAE,oBAAoB;YAC5B,YAAY,EAAE,CAAC;YACf,OAAO,EAAE,WAAW;YACpB,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,SAAS;YAChB,UAAU,EAAE,SAAS;SACrB,iCAEe,8CAA0B,oJAErC,CACN,CAAA;AACF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC5B,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACzD,IAAI,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACtD,OAAO,KAAC,UAAU,IAAC,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,UAAU,SAAG,CAAA;IACrF,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,KAAC,wBAAwB,KAAG,CAAA;IAClD,OAAO,IAAI,CAAA;AACZ,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA+B;IACjE,SAAS,EAAE,aAAa;IACxB,IAAI,EAAE,gBAAgB;IACtB,MAAM,EAAE,EAAE;CACV,CAAA;AAED,gFAAgF;AAChF,OAAO,EACN,wBAAwB,GAIxB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACN,oBAAoB,EAEpB,mBAAmB,EAEnB,cAAc,EAGd,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACX,MAAM,uBAAuB,CAAA"}
|
|
@@ -32,7 +32,7 @@ export function previewExpiry(html) {
|
|
|
32
32
|
export function renderPreviewBlock(input, options) {
|
|
33
33
|
const expires = options.expiresAt.trim();
|
|
34
34
|
if (!expires) {
|
|
35
|
-
throw new Error('renderPreviewBlock: `expiresAt` is required
|
|
35
|
+
throw new Error('renderPreviewBlock: `expiresAt` is required. A preview must be time-bounded.');
|
|
36
36
|
}
|
|
37
37
|
// The bound must be REAL — an unparseable expiry is a toothless bound (downstream can't refuse a
|
|
38
38
|
// stale preview it can't date). Reject anything Date can't parse, so `previewExpiry()` consumers
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-block.js","sourceRoot":"","sources":["../../src/components/preview-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAA6B,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAkBnF,MAAM,aAAa,GAAG,wCAAwC,CAAA;AAE9D,+FAA+F;AAC/F,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC9D,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA2B,EAAE,OAA4B;IAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC
|
|
1
|
+
{"version":3,"file":"preview-block.js","sourceRoot":"","sources":["../../src/components/preview-block.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAA6B,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAkBnF,MAAM,aAAa,GAAG,wCAAwC,CAAA;AAE9D,+FAA+F;AAC/F,MAAM,UAAU,aAAa,CAAC,IAAY;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC9D,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACvB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAA2B,EAAE,OAA4B;IAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAA;IAChG,CAAC;IACD,iGAAiG;IACjG,iGAAiG;IACjG,iCAAiC;IACjC,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,oEAAoE,OAAO,KAAK,CAAC,CAAA;IAClG,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,KAAK,KAAK,CAAA;IAC7C,MAAM,KAAK,GAAG,SAAS;QACtB,CAAC,CAAC,wLAAwL,UAAU,CAAC,OAAO,CAAC,SAAS,IAAI,aAAa,CAAC,QAAQ;QAChP,CAAC,CAAC,EAAE,CAAA;IACL,OAAO,CACN,sFAAsF,UAAU,CAAC,OAAO,CAAC,8BAA8B;QACvI,+DAA+D,eAAe,CAAC,OAAO,CAAC,IAAI;QAC3F,KAAK;QACL,eAAe,CAAC,KAAK,CAAC;QACtB,QAAQ,CACR,CAAA;AACF,CAAC"}
|
|
@@ -31,5 +31,5 @@ export declare function verifiedStamp(idSuffix?: string): string;
|
|
|
31
31
|
*/
|
|
32
32
|
export declare const CERT_MODAL_FONT_FAMILIES: readonly ["DM Sans", "Playfair Display", "JetBrains Mono", "Plus Jakarta Sans", "Allura"];
|
|
33
33
|
/** The full scoped stylesheet, adopted via a constructable `CSSStyleSheet`. */
|
|
34
|
-
export declare const CERT_MODAL_CSS = "\n:host {\n\t--cr-navy: #0a1b3f;\n\t--cr-lime: #d4e157;\n\t--cr-white: #ffffff;\n\t--cr-navy-06: rgba(10, 27, 63, 0.06);\n\t--cr-navy-10: rgba(10, 27, 63, 0.1);\n\t--cr-navy-25: rgba(10, 27, 63, 0.25);\n\t--cr-navy-40: rgba(10, 27, 63, 0.4);\n\t--cr-navy-45: rgba(10, 27, 63, 0.45);\n\t--cr-navy-50: rgba(10, 27, 63, 0.5);\n\t--cr-navy-55: rgba(10, 27, 63, 0.55);\n\t--cr-navy-60: rgba(10, 27, 63, 0.6);\n\t--cr-navy-85: rgba(10, 27, 63, 0.85);\n\t--cr-white-60: rgba(255, 255, 255, 0.6);\n\t--cr-white-85: rgba(255, 255, 255, 0.85);\n\t--cr-cta-hover: #0c2149;\n\t--cr-ghost-hover: rgba(10, 27, 63, 0.12);\n\t--cr-shadow: 0 30px 60px -18px rgba(10, 27, 63, 0.5);\n\t--cr-backdrop: rgba(10, 27, 63, 0.45);\n\t--cr-sans: 'DM Sans', system-ui, -apple-system, sans-serif;\n\t--cr-serif: 'Playfair Display', Georgia, serif;\n\t--cr-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;\n\t--cr-wordmark: 'Plus Jakarta Sans', system-ui, sans-serif;\n\t--cr-script: 'Allura', cursive;\n}\n\n.crm-dialog {\n\tborder: none;\n\tborder-radius: 14px;\n\tpadding: 0;\n\twidth: 92%;\n\tmax-height: 92vh;\n\toverflow: auto;\n\tbox-shadow: var(--cr-shadow);\n\tbackground: var(--cr-white);\n\tcolor: var(--cr-navy);\n\tfont-family: var(--cr-sans);\n\t/* fixed, NOT relative: a :modal dialog (showModal) renders in the TOP LAYER; a\n\t * relative dialog anchors to its scrolled document position, so once the page is scrolled the\n\t * panel renders off-screen while the ::backdrop still covers the viewport (measured top -1972 at\n\t * scrollY 2537). fixed is viewport-anchored (the UA inset:0 + margin:auto centers it) and still\n\t * provides the containing block for the absolutely-positioned .crm-close (x stays in-corner). */\n\tposition: fixed;\n\t/* defensive resets against a hostile brand theme reaching the top-layer dialog */\n\tbox-sizing: border-box;\n\ttext-align: left;\n\tline-height: 1.4;\n}\n.crm-dialog *,\n.crm-dialog *::before,\n.crm-dialog *::after {\n\tbox-sizing: border-box;\n}\n.crm-dialog::backdrop {\n\tbackground: var(--cr-backdrop);\n}\n.crm-dialog--cert {\n\tmax-width: 484px;\n}\n.crm-dialog--expert {\n\tmax-width: 436px;\n}\n\n/* Close (\u00D7) */\n.crm-close {\n\tposition: absolute;\n\ttop: 12px;\n\tright: 15px;\n\tz-index: 2;\n\tbackground: none;\n\tborder: none;\n\tcursor: pointer;\n\tpadding: 2px;\n\tcolor: var(--cr-white-60);\n\tfont-size: 1.35rem;\n\tline-height: 1;\n\tfont-family: var(--cr-sans);\n\ttransition: color 0.15s;\n}\n.crm-close:hover {\n\tcolor: var(--cr-white);\n}\n\n/* Header bar */\n.crm-head {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 9px;\n\tpadding: 13px 22px;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n}\n.crm-head__eyebrow {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 600;\n\tletter-spacing: 0.16em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-white-85);\n}\n.crm-head__wordmark {\n\tmargin-left: auto;\n\tmargin-right: 22px;\n\tfont-family: var(--cr-wordmark);\n\tfont-weight: 800;\n\tfont-size: 13px;\n\tletter-spacing: 0.03em;\n\tcolor: var(--cr-white);\n}\n\n/* Body */\n.crm-body--cert {\n\tpadding: 24px 28px 26px;\n}\n.crm-body--expert {\n\tpadding: 22px 26px 24px;\n}\n\n/* Avatar (shared, lime ring) */\n.crm-avatar {\n\tposition: relative;\n\tflex-shrink: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: 50%;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n\tfont-weight: 700;\n\tobject-fit: cover;\n\tbox-shadow: 0 0 0 2px var(--cr-white), 0 0 0 3.5px var(--cr-lime);\n}\n.crm-avatar--56 {\n\twidth: 56px;\n\theight: 56px;\n\tfont-size: 17px;\n}\n.crm-avatar--64 {\n\twidth: 64px;\n\theight: 64px;\n\tfont-size: 1.35rem;\n\tletter-spacing: 0.02em;\n}\n\n/* Certificate \
|
|
34
|
+
export declare const CERT_MODAL_CSS = "\n:host {\n\t--cr-navy: #0a1b3f;\n\t--cr-lime: #d4e157;\n\t--cr-white: #ffffff;\n\t--cr-navy-06: rgba(10, 27, 63, 0.06);\n\t--cr-navy-10: rgba(10, 27, 63, 0.1);\n\t--cr-navy-25: rgba(10, 27, 63, 0.25);\n\t--cr-navy-40: rgba(10, 27, 63, 0.4);\n\t--cr-navy-45: rgba(10, 27, 63, 0.45);\n\t--cr-navy-50: rgba(10, 27, 63, 0.5);\n\t--cr-navy-55: rgba(10, 27, 63, 0.55);\n\t--cr-navy-60: rgba(10, 27, 63, 0.6);\n\t--cr-navy-85: rgba(10, 27, 63, 0.85);\n\t--cr-white-60: rgba(255, 255, 255, 0.6);\n\t--cr-white-85: rgba(255, 255, 255, 0.85);\n\t--cr-cta-hover: #0c2149;\n\t--cr-ghost-hover: rgba(10, 27, 63, 0.12);\n\t--cr-shadow: 0 30px 60px -18px rgba(10, 27, 63, 0.5);\n\t--cr-backdrop: rgba(10, 27, 63, 0.45);\n\t--cr-sans: 'DM Sans', system-ui, -apple-system, sans-serif;\n\t--cr-serif: 'Playfair Display', Georgia, serif;\n\t--cr-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;\n\t--cr-wordmark: 'Plus Jakarta Sans', system-ui, sans-serif;\n\t--cr-script: 'Allura', cursive;\n}\n\n.crm-dialog {\n\tborder: none;\n\tborder-radius: 14px;\n\tpadding: 0;\n\twidth: 92%;\n\tmax-height: 92vh;\n\toverflow: auto;\n\tbox-shadow: var(--cr-shadow);\n\tbackground: var(--cr-white);\n\tcolor: var(--cr-navy);\n\tfont-family: var(--cr-sans);\n\t/* fixed, NOT relative: a :modal dialog (showModal) renders in the TOP LAYER; a\n\t * relative dialog anchors to its scrolled document position, so once the page is scrolled the\n\t * panel renders off-screen while the ::backdrop still covers the viewport (measured top -1972 at\n\t * scrollY 2537). fixed is viewport-anchored (the UA inset:0 + margin:auto centers it) and still\n\t * provides the containing block for the absolutely-positioned .crm-close (x stays in-corner). */\n\tposition: fixed;\n\t/* defensive resets against a hostile brand theme reaching the top-layer dialog */\n\tbox-sizing: border-box;\n\ttext-align: left;\n\tline-height: 1.4;\n}\n.crm-dialog *,\n.crm-dialog *::before,\n.crm-dialog *::after {\n\tbox-sizing: border-box;\n}\n.crm-dialog::backdrop {\n\tbackground: var(--cr-backdrop);\n}\n.crm-dialog--cert {\n\tmax-width: 484px;\n}\n.crm-dialog--expert {\n\tmax-width: 436px;\n}\n\n/* Close (\u00D7) */\n.crm-close {\n\tposition: absolute;\n\ttop: 12px;\n\tright: 15px;\n\tz-index: 2;\n\tbackground: none;\n\tborder: none;\n\tcursor: pointer;\n\tpadding: 2px;\n\tcolor: var(--cr-white-60);\n\tfont-size: 1.35rem;\n\tline-height: 1;\n\tfont-family: var(--cr-sans);\n\ttransition: color 0.15s;\n}\n.crm-close:hover {\n\tcolor: var(--cr-white);\n}\n\n/* Header bar */\n.crm-head {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 9px;\n\tpadding: 13px 22px;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n}\n.crm-head__eyebrow {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 600;\n\tletter-spacing: 0.16em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-white-85);\n}\n.crm-head__wordmark {\n\tmargin-left: auto;\n\tmargin-right: 22px;\n\tfont-family: var(--cr-wordmark);\n\tfont-weight: 800;\n\tfont-size: 13px;\n\tletter-spacing: 0.03em;\n\tcolor: var(--cr-white);\n}\n\n/* Body */\n.crm-body--cert {\n\tpadding: 24px 28px 26px;\n}\n.crm-body--expert {\n\tpadding: 22px 26px 24px;\n}\n\n/* Avatar (shared, lime ring) */\n.crm-avatar {\n\tposition: relative;\n\tflex-shrink: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: 50%;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n\tfont-weight: 700;\n\tobject-fit: cover;\n\tbox-shadow: 0 0 0 2px var(--cr-white), 0 0 0 3.5px var(--cr-lime);\n}\n.crm-avatar--56 {\n\twidth: 56px;\n\theight: 56px;\n\tfont-size: 17px;\n}\n.crm-avatar--64 {\n\twidth: 64px;\n\theight: 64px;\n\tfont-size: 1.35rem;\n\tletter-spacing: 0.02em;\n}\n\n/* Certificate \u00B7 reviewing-expert row */\n.crm-expert-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 14px;\n}\n.crm-expert-row__info {\n\tflex: 1;\n\tmin-width: 0;\n}\n.crm-expert-row__eyebrow {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 600;\n\tletter-spacing: 0.14em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy-45);\n}\n.crm-expert-row__name {\n\tfont-size: 15px;\n\tfont-weight: 700;\n\tcolor: var(--cr-navy);\n\tmargin-top: 2px;\n}\n.crm-expert-row__creds {\n\tfont-size: 12px;\n\tcolor: var(--cr-navy-60);\n\tmargin-top: 1px;\n}\n\n/* Certificate \u00B7 \"Certifies the article\" */\n.crm-certifies {\n\tmargin: 18px 0 0;\n\tpadding-top: 16px;\n\tborder-top: 1px solid var(--cr-navy-10);\n}\n.crm-certifies__eyebrow {\n\tfont-family: var(--cr-mono);\n\tfont-size: 9px;\n\tfont-weight: 600;\n\tletter-spacing: 0.16em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy-40);\n}\n.crm-certifies__title {\n\tfont-family: var(--cr-serif);\n\tfont-weight: 700;\n\tfont-size: 18px;\n\tline-height: 1.25;\n\tcolor: var(--cr-navy);\n\tmargin-top: 5px;\n\ttext-wrap: balance;\n}\n\n/* Certificate \u00B7 meta row */\n.crm-meta {\n\tdisplay: flex;\n\tgap: 10px;\n\tmargin-top: 16px;\n}\n.crm-meta__col {\n\tflex: 1;\n}\n.crm-meta__label {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8px;\n\tletter-spacing: 0.12em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy-40);\n}\n.crm-meta__value {\n\tfont-size: 12.5px;\n\tfont-weight: 600;\n\tcolor: var(--cr-navy);\n\tmargin-top: 3px;\n}\n.crm-meta__value--mono {\n\tfont-family: var(--cr-mono);\n\tfont-size: 12px;\n}\n.crm-meta__status {\n\tmargin-top: 3px;\n}\n\n/* Certified chip (meta status) */\n.crm-chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 3px;\n\tpadding: 2px 7px;\n\tborder-radius: 4px;\n\tbackground: var(--cr-lime);\n\tcolor: var(--cr-navy);\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n}\n\n/* Certificate \u00B7 signature + stamp footer */\n.crm-sig {\n\tdisplay: flex;\n\talign-items: flex-end;\n\tjustify-content: space-between;\n\tmargin-top: 20px;\n\tpadding-top: 16px;\n\tborder-top: 1px solid var(--cr-navy-10);\n\tgap: 12px;\n}\n.crm-sig__name {\n\tfont-family: var(--cr-script);\n\tfont-size: 30px;\n\tline-height: 0.9;\n\tcolor: var(--cr-navy);\n}\n.crm-sig__rule {\n\twidth: 146px;\n\tmax-width: 100%;\n\theight: 1px;\n\tbackground: var(--cr-navy-25);\n\tmargin: 4px 0 6px;\n}\n.crm-sig__url {\n\tfont-family: var(--cr-mono);\n\tfont-size: 8px;\n\tfont-weight: 500;\n\tletter-spacing: 0.12em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy-50);\n}\n.crm-stamp {\n\ttransform: rotate(-8deg);\n\tflex-shrink: 0;\n}\n.crm-stamp svg {\n\tdisplay: block;\n}\n\n/* Actions (shared) */\n.crm-actions {\n\tdisplay: flex;\n\tgap: 10px;\n\tmargin-top: 20px;\n}\n.crm-cta {\n\tflex: 1;\n\tdisplay: inline-block;\n\ttext-align: center;\n\tpadding: 11px 16px;\n\tbackground: var(--cr-navy);\n\tcolor: var(--cr-white);\n\ttext-decoration: none;\n\tborder-radius: 8px;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tfont-family: var(--cr-sans);\n\tborder: none;\n\tcursor: pointer;\n\ttransition: background 0.15s;\n}\n.crm-cta:hover {\n\tbackground: var(--cr-cta-hover);\n}\n.crm-ghost {\n\tpadding: 11px 18px;\n\tbackground: var(--cr-navy-06);\n\tcolor: var(--cr-navy);\n\tborder: none;\n\tborder-radius: 8px;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tfont-family: var(--cr-sans);\n\tcursor: pointer;\n\ttransition: background 0.15s;\n}\n.crm-ghost:hover {\n\tbackground: var(--cr-ghost-hover);\n}\n\n/* Reviewer \u00B7 identity row */\n.crm-id {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 14px;\n}\n.crm-id__info {\n\tmin-width: 0;\n}\n.crm-id__name {\n\tfont-size: 16px;\n\tfont-weight: 700;\n\tline-height: 1.25;\n\tcolor: var(--cr-navy);\n}\n.crm-id__subtitle {\n\tfont-size: 12.5px;\n\tline-height: 1.4;\n\tcolor: var(--cr-navy-60);\n\tmargin-top: 2px;\n}\n\n/* Reviewer \u00B7 credential chips */\n.crm-chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 7px;\n\tmargin-top: 16px;\n}\n.crm-chips__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tpadding: 4px 10px;\n\tborder-radius: 999px;\n\tbackground: var(--cr-navy-06);\n\tcolor: var(--cr-navy);\n\tfont-size: 11px;\n\tfont-weight: 600;\n}\n\n/* Reviewer \u00B7 bio */\n.crm-bio {\n\tmargin: 16px 0 0;\n\tfont-size: 0.9rem;\n\tline-height: 1.55;\n\tcolor: var(--cr-navy-85);\n}\n\n/* Reviewer \u00B7 trust line (the disclosure footer below now owns the spacing to the actions) */\n.crm-trust {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 9px;\n\tflex-wrap: wrap;\n\tmargin: 16px 0 0;\n\tpadding-top: 16px;\n\tborder-top: 1px solid var(--cr-navy-10);\n}\n.crm-trust__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 4px;\n\tpadding: 3px 8px;\n\tborder-radius: 4px;\n\tbackground: var(--cr-lime);\n\tcolor: var(--cr-navy);\n\tfont-family: var(--cr-mono);\n\tfont-size: 8.5px;\n\tfont-weight: 700;\n\tletter-spacing: 0.08em;\n\ttext-transform: uppercase;\n}\n.crm-trust__text {\n\tfont-family: var(--cr-mono);\n\tfont-size: 9px;\n\tletter-spacing: 0.05em;\n\tcolor: var(--cr-navy-50);\n}\n\n/* FTC disclosure footer \u00B7 material-connection cue (compensated only) + scope line\n * (always). Rendered on both the certificate + reviewer dialogs, immediately above the actions. */\n.crm-disclosure {\n\tmargin-top: 14px;\n\tpadding-top: 14px;\n\tborder-top: 1px solid var(--cr-navy-10);\n\tfont-family: var(--cr-sans);\n\tfont-size: 11.5px;\n\tline-height: 1.5;\n\tcolor: var(--cr-navy-50);\n}\n.crm-disclosure__cue {\n\tfont-family: var(--cr-mono);\n\tfont-size: 10px;\n\tfont-weight: 700;\n\tletter-spacing: 0.06em;\n\ttext-transform: uppercase;\n\tcolor: var(--cr-navy);\n}\n\n/* Narrow storefronts / phones */\n@media (max-width: 440px) {\n\t.crm-body--cert {\n\t\tpadding: 20px 18px 22px;\n\t}\n\t.crm-body--expert {\n\t\tpadding: 18px 18px 20px;\n\t}\n}\n";
|
|
35
35
|
//# sourceMappingURL=certrev-cert-modal.styles.d.ts.map
|
|
@@ -196,7 +196,7 @@ export const CERT_MODAL_CSS = `
|
|
|
196
196
|
letter-spacing: 0.02em;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
/* Certificate
|
|
199
|
+
/* Certificate · reviewing-expert row */
|
|
200
200
|
.crm-expert-row {
|
|
201
201
|
display: flex;
|
|
202
202
|
align-items: center;
|
|
@@ -226,7 +226,7 @@ export const CERT_MODAL_CSS = `
|
|
|
226
226
|
margin-top: 1px;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
/* Certificate
|
|
229
|
+
/* Certificate · "Certifies the article" */
|
|
230
230
|
.crm-certifies {
|
|
231
231
|
margin: 18px 0 0;
|
|
232
232
|
padding-top: 16px;
|
|
@@ -250,7 +250,7 @@ export const CERT_MODAL_CSS = `
|
|
|
250
250
|
text-wrap: balance;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
/* Certificate
|
|
253
|
+
/* Certificate · meta row */
|
|
254
254
|
.crm-meta {
|
|
255
255
|
display: flex;
|
|
256
256
|
gap: 10px;
|
|
@@ -296,7 +296,7 @@ export const CERT_MODAL_CSS = `
|
|
|
296
296
|
text-transform: uppercase;
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
/* Certificate
|
|
299
|
+
/* Certificate · signature + stamp footer */
|
|
300
300
|
.crm-sig {
|
|
301
301
|
display: flex;
|
|
302
302
|
align-items: flex-end;
|
|
@@ -376,7 +376,7 @@ export const CERT_MODAL_CSS = `
|
|
|
376
376
|
background: var(--cr-ghost-hover);
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
/* Reviewer
|
|
379
|
+
/* Reviewer · identity row */
|
|
380
380
|
.crm-id {
|
|
381
381
|
display: flex;
|
|
382
382
|
align-items: center;
|
|
@@ -398,7 +398,7 @@ export const CERT_MODAL_CSS = `
|
|
|
398
398
|
margin-top: 2px;
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
-
/* Reviewer
|
|
401
|
+
/* Reviewer · credential chips */
|
|
402
402
|
.crm-chips {
|
|
403
403
|
display: flex;
|
|
404
404
|
flex-wrap: wrap;
|
|
@@ -416,7 +416,7 @@ export const CERT_MODAL_CSS = `
|
|
|
416
416
|
font-weight: 600;
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
-
/* Reviewer
|
|
419
|
+
/* Reviewer · bio */
|
|
420
420
|
.crm-bio {
|
|
421
421
|
margin: 16px 0 0;
|
|
422
422
|
font-size: 0.9rem;
|
|
@@ -424,7 +424,7 @@ export const CERT_MODAL_CSS = `
|
|
|
424
424
|
color: var(--cr-navy-85);
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
-
/* Reviewer
|
|
427
|
+
/* Reviewer · trust line (the disclosure footer below now owns the spacing to the actions) */
|
|
428
428
|
.crm-trust {
|
|
429
429
|
display: flex;
|
|
430
430
|
align-items: center;
|
|
@@ -455,7 +455,7 @@ export const CERT_MODAL_CSS = `
|
|
|
455
455
|
color: var(--cr-navy-50);
|
|
456
456
|
}
|
|
457
457
|
|
|
458
|
-
/* FTC disclosure footer
|
|
458
|
+
/* FTC disclosure footer · material-connection cue (compensated only) + scope line
|
|
459
459
|
* (always). Rendered on both the certificate + reviewer dialogs, immediately above the actions. */
|
|
460
460
|
.crm-disclosure {
|
|
461
461
|
margin-top: 14px;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* biome-ignore-all format: generated cert-badge bundle, see @certrev/cert-block src/modal/ */ /* biome-ignore-all lint: generated cert-badge bundle, see @certrev/cert-block src/modal/ */
|
|
2
|
-
"use strict";(()=>{var H="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",$e=(()=>{let e=new Int8Array(128).fill(-1);for(let t=0;t<H.length;t++)e[H.charCodeAt(t)]=t;return e[45]=62,e[95]=63,e})();function g(e){let t=[];for(let a=0;a<e.length;a++){let c=e.charCodeAt(a),d=c<128?$e[c]:-1;d>=0&&t.push(d)}let r=Math.floor(t.length*6/8),n=new Uint8Array(r),o=0,i=0,s=0;for(let a=0;a<t.length;a++)o=o<<6|t[a],i+=6,i>=8&&(i-=8,n[s++]=o>>i&255);return n}function C(e){return g(e)}function p(e,t=new Set){if(typeof e=="number"&&isNaN(e))throw new Error("NaN is not allowed");if(typeof e=="number"&&!isFinite(e))throw new Error("Infinity is not allowed");if(e===null||typeof e!="object")return JSON.stringify(e);if(typeof e.toJSON=="function"){if(t.has(e))throw new Error("Circular reference detected");t.add(e);let n=p(e.toJSON(),t);return t.delete(e),n}if(t.has(e))throw new Error("Circular reference detected");t.add(e);let r;if(Array.isArray(e))r=`[${e.map(o=>p(o===void 0||typeof o=="symbol"?null:o,t)).join(",")}]`;else{let n=[];for(let o of Object.keys(e).sort())e[o]===void 0||typeof e[o]=="symbol"||n.push(`${p(o)}:${p(e[o],t)}`);r=`{${n.join(",")}}`}return t.delete(e),r}var Oe=typeof p=="function"?p:p.default;function K(e){let t=Oe(e);if(typeof t!="string")throw new Error("canonicalize: value is not JSON-serializable");return t}function T(e){return new TextEncoder().encode(K(e))}function b(e,t="$"){if(typeof e=="number"){if(!Number.isInteger(e)||!Number.isSafeInteger(e))throw new Error(`canonicalPayloadBytes: number at ${t} (${e}) is not a safe integer
|
|
2
|
+
"use strict";(()=>{var H="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",$e=(()=>{let e=new Int8Array(128).fill(-1);for(let t=0;t<H.length;t++)e[H.charCodeAt(t)]=t;return e[45]=62,e[95]=63,e})();function g(e){let t=[];for(let a=0;a<e.length;a++){let c=e.charCodeAt(a),d=c<128?$e[c]:-1;d>=0&&t.push(d)}let r=Math.floor(t.length*6/8),n=new Uint8Array(r),o=0,i=0,s=0;for(let a=0;a<t.length;a++)o=o<<6|t[a],i+=6,i>=8&&(i-=8,n[s++]=o>>i&255);return n}function C(e){return g(e)}function p(e,t=new Set){if(typeof e=="number"&&isNaN(e))throw new Error("NaN is not allowed");if(typeof e=="number"&&!isFinite(e))throw new Error("Infinity is not allowed");if(e===null||typeof e!="object")return JSON.stringify(e);if(typeof e.toJSON=="function"){if(t.has(e))throw new Error("Circular reference detected");t.add(e);let n=p(e.toJSON(),t);return t.delete(e),n}if(t.has(e))throw new Error("Circular reference detected");t.add(e);let r;if(Array.isArray(e))r=`[${e.map(o=>p(o===void 0||typeof o=="symbol"?null:o,t)).join(",")}]`;else{let n=[];for(let o of Object.keys(e).sort())e[o]===void 0||typeof e[o]=="symbol"||n.push(`${p(o)}:${p(e[o],t)}`);r=`{${n.join(",")}}`}return t.delete(e),r}var Oe=typeof p=="function"?p:p.default;function K(e){let t=Oe(e);if(typeof t!="string")throw new Error("canonicalize: value is not JSON-serializable");return t}function T(e){return new TextEncoder().encode(K(e))}function b(e,t="$"){if(typeof e=="number"){if(!Number.isInteger(e)||!Number.isSafeInteger(e))throw new Error(`canonicalPayloadBytes: number at ${t} (${e}) is not a safe integer. Signed-payload numbers must be safe integers so PHP/Go/Java verifiers reproduce identical canonical bytes`);return}if(Array.isArray(e)){e.forEach((r,n)=>b(r,`${t}[${n}]`));return}if(e&&typeof e=="object")for(let[r,n]of Object.entries(e))b(n,`${t}.${r}`)}function R(e){return b(e),T(e)}function k(e){return b(e),T(e)}var E="ed25519";var m={name:"Ed25519"};function Pe(e){return e.replace(/-----BEGIN [^-]+-----/g,"").replace(/-----END [^-]+-----/g,"").replace(/\s+/g,"")}async function q(e){switch(e.format){case"pem":return crypto.subtle.importKey("spki",g(Pe(e.pem)),m,!1,["verify"]);case"spki-base64":return crypto.subtle.importKey("spki",g(e.base64),m,!1,["verify"]);case"spki-der":return crypto.subtle.importKey("spki",e.bytes,m,!1,["verify"]);case"raw":{if(e.bytes.length!==32)throw new Error(`ed25519 raw public key must be 32 bytes, got ${e.bytes.length}`);return crypto.subtle.importKey("raw",e.bytes,m,!1,["verify"])}}}function ze(e){if("format"in e)return!1;let t=e;return typeof t.type=="string"&&typeof t.algorithm=="object"&&Array.isArray(t.usages)}async function G(e){return ze(e)?e:q(e)}async function j(e,t,r){let n=R(e),o=C(t);try{return await crypto.subtle.verify(m,r,o,n)}catch{return!1}}async function Ve(e,t){let{payload:r,signature:n}=e??{};if(!r||!n||r.contractVersion!==1)return{ok:!1,reason:"unsupported_contract_version"};if(n.alg!==E)return{ok:!1,reason:"unsupported_alg"};let o=await t(n.kid);if(!o)return{ok:!1,reason:"unknown_key"};let i;try{i=await G(o)}catch{return{ok:!1,reason:"unknown_key"}}return await j(r,n.sig,i)?{ok:!0,key:i}:{ok:!1,reason:"invalid_signature"}}function J(e,t){let r=t.now??new Date;if(e.subject.platform!==t.platform)return{decision:"suppress",reason:"platform_mismatch"};if(e.subject.externalId!==t.externalId)return{decision:"suppress",reason:"subject_mismatch"};if(e.lifecycle.revokedAt!==null)return{decision:"suppress",reason:"revoked"};if(r.getTime()>=Date.parse(e.lifecycle.expiresAt))return{decision:"suppress",reason:"expired"};let n=e.subject.contentDigest,o=t.liveContentHash;return n!==null&&o!=null&&n!==o?{decision:"suppress",reason:"content_drift"}:{decision:"render",payload:e}}async function W(e,t,r){let n;try{n=await Ve(e,t)}catch{return{decision:"suppress",reason:"unknown_key"}}return n.ok?J(e.payload,r):{decision:"suppress",reason:n.reason}}function v(e){return e?.kind==="tombstone"}async function He(e,t){let{signature:r}=e??{};if(!e?.subject||!r||e.contractVersion!==1)return{ok:!1,reason:"unsupported_contract_version"};if(r.alg!==E)return{ok:!1,reason:"unsupported_alg"};let n=await t(r.kid);if(!n)return{ok:!1,reason:"unknown_key"};let o;try{o=await G(n)}catch{return{ok:!1,reason:"unknown_key"}}let{signature:i,...s}=e,a=C(r.sig),c=!1;try{c=await crypto.subtle.verify(m,o,a,k(s))}catch{c=!1}return c?{ok:!0,key:o}:{ok:!1,reason:"invalid_signature"}}async function Z(e,t,r){let n;try{n=await He(e,t)}catch{return{decision:"suppress",reason:"unknown_key"}}return n.ok?e.subject.platform!==r.platform?{decision:"suppress",reason:"platform_mismatch"}:e.subject.externalId!==r.externalId?{decision:"suppress",reason:"subject_mismatch"}:{decision:"suppress",reason:"revoked"}:{decision:"suppress",reason:n.reason}}async function I(e,t,r){return v(e)?Z(e,t,r):W(e,t,r)}var Y=`
|
|
3
3
|
@font-face {
|
|
4
4
|
font-family: "DM Sans";
|
|
5
5
|
font-style: normal;
|
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
letter-spacing: 0.02em;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
/* Certificate \
|
|
267
|
+
/* Certificate \xB7 reviewing-expert row */
|
|
268
268
|
.crm-expert-row {
|
|
269
269
|
display: flex;
|
|
270
270
|
align-items: center;
|
|
@@ -294,7 +294,7 @@
|
|
|
294
294
|
margin-top: 1px;
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
-
/* Certificate \
|
|
297
|
+
/* Certificate \xB7 "Certifies the article" */
|
|
298
298
|
.crm-certifies {
|
|
299
299
|
margin: 18px 0 0;
|
|
300
300
|
padding-top: 16px;
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
text-wrap: balance;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
/* Certificate \
|
|
321
|
+
/* Certificate \xB7 meta row */
|
|
322
322
|
.crm-meta {
|
|
323
323
|
display: flex;
|
|
324
324
|
gap: 10px;
|
|
@@ -364,7 +364,7 @@
|
|
|
364
364
|
text-transform: uppercase;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
/* Certificate \
|
|
367
|
+
/* Certificate \xB7 signature + stamp footer */
|
|
368
368
|
.crm-sig {
|
|
369
369
|
display: flex;
|
|
370
370
|
align-items: flex-end;
|
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
background: var(--cr-ghost-hover);
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
/* Reviewer \
|
|
447
|
+
/* Reviewer \xB7 identity row */
|
|
448
448
|
.crm-id {
|
|
449
449
|
display: flex;
|
|
450
450
|
align-items: center;
|
|
@@ -466,7 +466,7 @@
|
|
|
466
466
|
margin-top: 2px;
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
-
/* Reviewer \
|
|
469
|
+
/* Reviewer \xB7 credential chips */
|
|
470
470
|
.crm-chips {
|
|
471
471
|
display: flex;
|
|
472
472
|
flex-wrap: wrap;
|
|
@@ -484,7 +484,7 @@
|
|
|
484
484
|
font-weight: 600;
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
-
/* Reviewer \
|
|
487
|
+
/* Reviewer \xB7 bio */
|
|
488
488
|
.crm-bio {
|
|
489
489
|
margin: 16px 0 0;
|
|
490
490
|
font-size: 0.9rem;
|
|
@@ -492,7 +492,7 @@
|
|
|
492
492
|
color: var(--cr-navy-85);
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
-
/* Reviewer \
|
|
495
|
+
/* Reviewer \xB7 trust line (the disclosure footer below now owns the spacing to the actions) */
|
|
496
496
|
.crm-trust {
|
|
497
497
|
display: flex;
|
|
498
498
|
align-items: center;
|
|
@@ -523,7 +523,7 @@
|
|
|
523
523
|
color: var(--cr-navy-50);
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
-
/* FTC disclosure footer \
|
|
526
|
+
/* FTC disclosure footer \xB7 material-connection cue (compensated only) + scope line
|
|
527
527
|
* (always). Rendered on both the certificate + reviewer dialogs, immediately above the actions. */
|
|
528
528
|
.crm-disclosure {
|
|
529
529
|
margin-top: 14px;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-with-deadline.d.ts","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAE7C;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,OAAO,KAAK,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,SAAS,GAAE,MAAiC,GAC1C,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"fetch-with-deadline.d.ts","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAQ,CAAA;AAE7C;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,OAAO,KAAK,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,SAAS,GAAE,MAAiC,GAC1C,OAAO,CAAC,QAAQ,CAAC,CAqBnB"}
|
|
@@ -39,7 +39,9 @@ export async function fetchWithDeadline(fetchImpl, url, init, timeoutMs = DEFAUL
|
|
|
39
39
|
const deadline = new Promise((_, reject) => {
|
|
40
40
|
timer = setTimeout(() => {
|
|
41
41
|
controller.abort();
|
|
42
|
-
|
|
42
|
+
// Middle dot, not an em dash: this prints into a consumer's console with our name on it,
|
|
43
|
+
// so it is visible copy, and the URL it appends is labelled data.
|
|
44
|
+
reject(new Error(`cert-block: fetch exceeded its ${timeoutMs}ms deadline · ${url}`));
|
|
43
45
|
}, timeoutMs);
|
|
44
46
|
});
|
|
45
47
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-with-deadline.js","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,SAAuB,EACvB,GAAW,EACX,IAAiB,EACjB,YAAoB,wBAAwB;IAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAE9E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,IAAI,KAAgD,CAAA;IACpD,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACvB,UAAU,CAAC,KAAK,EAAE,CAAA;YAClB,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,SAAS,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAA;QACrF,CAAC,EAAE,SAAS,CAAC,CAAA;IACd,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC;QACJ,uFAAuF;QACvF,0EAA0E;QAC1E,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC9F,CAAC;YAAS,CAAC;QACV,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IAC7C,CAAC;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"fetch-with-deadline.js","sourceRoot":"","sources":["../../src/verify/fetch-with-deadline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAA;AAE7C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,SAAuB,EACvB,GAAW,EACX,IAAiB,EACjB,YAAoB,wBAAwB;IAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAE9E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,IAAI,KAAgD,CAAA;IACpD,MAAM,QAAQ,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QACjD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACvB,UAAU,CAAC,KAAK,EAAE,CAAA;YAClB,yFAAyF;YACzF,kEAAkE;YAClE,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,SAAS,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAA;QACrF,CAAC,EAAE,SAAS,CAAC,CAAA;IACd,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC;QACJ,uFAAuF;QACvF,0EAA0E;QAC1E,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC9F,CAAC;YAAS,CAAC;QACV,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IAC7C,CAAC;AACF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@certrev/cert-block",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Headless render edge for the CertREV CertDeliveryEnvelope: SSR-safe React cert components, a framework-agnostic <certrev-badge> Web Component, a schema.org JSON-LD projector, and a fail-closed verify layer over the shared VerdictKernel.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,8 +33,12 @@ export const CERT_COMPONENT_NAME = 'CertREV Cert'
|
|
|
33
33
|
export interface CertReviewCardProps extends Partial<BuilderCertChromeData> {
|
|
34
34
|
/**
|
|
35
35
|
* Placement face — the ENTRY/editor chooses this, NOT the exporter (not a wire-type field).
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
*
|
|
37
|
+
* Absent ⇒ `banner`, via the compatibility bridge in the render body below. That is NOT the
|
|
38
|
+
* package default: a direct `renderCertBlock` caller omitting `mode` gets `sidebar`, and
|
|
39
|
+
* `sidebar` is the registered input's `defaultValue`, so blocks created in the editor carry
|
|
40
|
+
* an explicit `mode`. The bridge covers only entries exported before the exporter emitted
|
|
41
|
+
* `mode` at all, and is deleted once those are backfilled. See the render body for why.
|
|
38
42
|
*/
|
|
39
43
|
mode?: CertBlockMode | null
|
|
40
44
|
/**
|
|
@@ -106,12 +110,28 @@ export function CertReviewCard(props: CertReviewCardProps) {
|
|
|
106
110
|
: undefined
|
|
107
111
|
|
|
108
112
|
const html = renderCertBlock({
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
113
|
+
// COMPATIBILITY BRIDGE, NOT THE DESIGN. Delete this fallback once the exporter emits an
|
|
114
|
+
// explicit `mode` and existing entries have been backfilled; until then it must stay.
|
|
115
|
+
//
|
|
116
|
+
// `renderCertBlock` owns the ONE placement default (absent or unrecognised ⇒ sidebar) and
|
|
117
|
+
// that decision STANDS: it is what a direct caller gets, and `sidebar` is the registered
|
|
118
|
+
// input's `defaultValue`, so every block created in the editor from now on carries an
|
|
119
|
+
// explicit `mode` and never reaches this line.
|
|
120
|
+
//
|
|
121
|
+
// The problem is the entries that already exist. `mode` is a PLACEMENT input, not one of
|
|
122
|
+
// the 19 wire keys (`BUILDER_CERT_CHROME_KEYS`), and the exporter emits only the wire
|
|
123
|
+
// projection (`options: { ...certData }`), so every entry exported to date has NO `mode`
|
|
124
|
+
// key at all. Through 0.5.5 this line read `?? 'banner'` and those entries rendered the
|
|
125
|
+
// banner face. Removing it in 1.0.0 did not give them the renderer's default in the
|
|
126
|
+
// abstract; it silently repainted live pages from banner to sidebar, with no entry edit
|
|
127
|
+
// and no version pin to protect them, because their install line is unpinned.
|
|
128
|
+
//
|
|
129
|
+
// So this is a bridge for pre-existing entries, NOT a reversal of the sidebar default.
|
|
130
|
+
// The three-copies-of-a-default problem that motivated removing it is still real and is
|
|
131
|
+
// still fixed: the helper text and the wire type's doc comment no longer name a default,
|
|
132
|
+
// and this line names one only for input the renderer can no longer distinguish from a
|
|
133
|
+
// deliberate choice. The exporter writing `mode` is what retires it.
|
|
134
|
+
mode: props.mode ?? 'banner',
|
|
115
135
|
theme,
|
|
116
136
|
// First-class banner memo split — renderCertBlock composes the requested
|
|
117
137
|
// part directly; no string-splitting. Ignored outside banner mode, and an unrecognised
|
package/src/builder/index.tsx
CHANGED
|
@@ -95,7 +95,7 @@ export function CertRevEditorPlaceholder() {
|
|
|
95
95
|
background: '#fff5fa',
|
|
96
96
|
}}
|
|
97
97
|
>
|
|
98
|
-
CertREV Review
|
|
98
|
+
CertREV Review: <b>resolves at publish</b> from issuer verification. The expert badge appears here once this
|
|
99
99
|
page’s article is certified, and only while the credential is valid.
|
|
100
100
|
</div>
|
|
101
101
|
)
|
|
@@ -52,7 +52,7 @@ export function previewExpiry(html: string): string | null {
|
|
|
52
52
|
export function renderPreviewBlock(input: RenderCertBlockInput, options: PreviewBlockOptions): string {
|
|
53
53
|
const expires = options.expiresAt.trim()
|
|
54
54
|
if (!expires) {
|
|
55
|
-
throw new Error('renderPreviewBlock: `expiresAt` is required
|
|
55
|
+
throw new Error('renderPreviewBlock: `expiresAt` is required. A preview must be time-bounded.')
|
|
56
56
|
}
|
|
57
57
|
// The bound must be REAL — an unparseable expiry is a toothless bound (downstream can't refuse a
|
|
58
58
|
// stale preview it can't date). Reject anything Date can't parse, so `previewExpiry()` consumers
|
|
@@ -203,7 +203,7 @@ export const CERT_MODAL_CSS = `
|
|
|
203
203
|
letter-spacing: 0.02em;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
/* Certificate
|
|
206
|
+
/* Certificate · reviewing-expert row */
|
|
207
207
|
.crm-expert-row {
|
|
208
208
|
display: flex;
|
|
209
209
|
align-items: center;
|
|
@@ -233,7 +233,7 @@ export const CERT_MODAL_CSS = `
|
|
|
233
233
|
margin-top: 1px;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
/* Certificate
|
|
236
|
+
/* Certificate · "Certifies the article" */
|
|
237
237
|
.crm-certifies {
|
|
238
238
|
margin: 18px 0 0;
|
|
239
239
|
padding-top: 16px;
|
|
@@ -257,7 +257,7 @@ export const CERT_MODAL_CSS = `
|
|
|
257
257
|
text-wrap: balance;
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
/* Certificate
|
|
260
|
+
/* Certificate · meta row */
|
|
261
261
|
.crm-meta {
|
|
262
262
|
display: flex;
|
|
263
263
|
gap: 10px;
|
|
@@ -303,7 +303,7 @@ export const CERT_MODAL_CSS = `
|
|
|
303
303
|
text-transform: uppercase;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
/* Certificate
|
|
306
|
+
/* Certificate · signature + stamp footer */
|
|
307
307
|
.crm-sig {
|
|
308
308
|
display: flex;
|
|
309
309
|
align-items: flex-end;
|
|
@@ -383,7 +383,7 @@ export const CERT_MODAL_CSS = `
|
|
|
383
383
|
background: var(--cr-ghost-hover);
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
/* Reviewer
|
|
386
|
+
/* Reviewer · identity row */
|
|
387
387
|
.crm-id {
|
|
388
388
|
display: flex;
|
|
389
389
|
align-items: center;
|
|
@@ -405,7 +405,7 @@ export const CERT_MODAL_CSS = `
|
|
|
405
405
|
margin-top: 2px;
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
/* Reviewer
|
|
408
|
+
/* Reviewer · credential chips */
|
|
409
409
|
.crm-chips {
|
|
410
410
|
display: flex;
|
|
411
411
|
flex-wrap: wrap;
|
|
@@ -423,7 +423,7 @@ export const CERT_MODAL_CSS = `
|
|
|
423
423
|
font-weight: 600;
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
-
/* Reviewer
|
|
426
|
+
/* Reviewer · bio */
|
|
427
427
|
.crm-bio {
|
|
428
428
|
margin: 16px 0 0;
|
|
429
429
|
font-size: 0.9rem;
|
|
@@ -431,7 +431,7 @@ export const CERT_MODAL_CSS = `
|
|
|
431
431
|
color: var(--cr-navy-85);
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
-
/* Reviewer
|
|
434
|
+
/* Reviewer · trust line (the disclosure footer below now owns the spacing to the actions) */
|
|
435
435
|
.crm-trust {
|
|
436
436
|
display: flex;
|
|
437
437
|
align-items: center;
|
|
@@ -462,7 +462,7 @@ export const CERT_MODAL_CSS = `
|
|
|
462
462
|
color: var(--cr-navy-50);
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
-
/* FTC disclosure footer
|
|
465
|
+
/* FTC disclosure footer · material-connection cue (compensated only) + scope line
|
|
466
466
|
* (always). Rendered on both the certificate + reviewer dialogs, immediately above the actions. */
|
|
467
467
|
.crm-disclosure {
|
|
468
468
|
margin-top: 14px;
|
|
@@ -46,7 +46,9 @@ export async function fetchWithDeadline(
|
|
|
46
46
|
const deadline = new Promise<never>((_, reject) => {
|
|
47
47
|
timer = setTimeout(() => {
|
|
48
48
|
controller.abort()
|
|
49
|
-
|
|
49
|
+
// Middle dot, not an em dash: this prints into a consumer's console with our name on it,
|
|
50
|
+
// so it is visible copy, and the URL it appends is labelled data.
|
|
51
|
+
reject(new Error(`cert-block: fetch exceeded its ${timeoutMs}ms deadline · ${url}`))
|
|
50
52
|
}, timeoutMs)
|
|
51
53
|
})
|
|
52
54
|
|