@drawnagency/primitives 0.1.90 → 0.1.92
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/dist/{chunk-ZLG7S54V.js → chunk-7QEJDYTG.js} +6 -6
- package/dist/{chunk-XUIJHYKI.js → chunk-A2V7BZPD.js} +3 -3
- package/dist/{chunk-NV4R6VHN.js → chunk-CI624HRS.js} +1 -1
- package/dist/{chunk-SIYQ37QY.js → chunk-IZLXF3CR.js} +1 -1
- package/dist/{chunk-XKRQMTE4.js → chunk-LQQY2RW6.js} +2 -2
- package/dist/{chunk-KMQZYORS.js → chunk-RUKO2N3F.js} +5 -1
- package/dist/{chunk-CZ3YXGK5.js → chunk-SOILPZ7U.js} +27 -3
- package/dist/{chunk-AGWE2LJG.js → chunk-WOYGQK2D.js} +1 -1
- package/dist/{chunk-KBQNJZUX.js → chunk-YI2HUFE3.js} +1 -1
- package/dist/{chunk-MK7YB2A4.js → chunk-Z66TBL4Q.js} +1 -1
- package/dist/components/chat/ChatPanel.d.ts +14 -6
- package/dist/components/chat/ChatPanel.d.ts.map +1 -1
- package/dist/components/chat/ChatWidget.d.ts.map +1 -1
- package/dist/components/editor/index.js +4 -4
- package/dist/components/primitives/index.js +5 -5
- package/dist/components/sections/brand-guide.js +5 -5
- package/dist/components/sections/register-schemas.js +8 -8
- package/dist/components/sections/register.js +8 -8
- package/dist/components/shared/Fab.d.ts +37 -0
- package/dist/components/shared/Fab.d.ts.map +1 -0
- package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/components/shell/ViewerLoginForm.d.ts +1 -0
- package/dist/components/shell/ViewerLoginForm.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/lib/index.js +2 -2
- package/dist/media/compose-preview.d.ts +9 -0
- package/dist/media/compose-preview.d.ts.map +1 -1
- package/dist/media/index.js +2 -2
- package/dist/media/preview-inputs.d.ts.map +1 -1
- package/dist/schemas/index.js +2 -2
- package/dist/schemas/site-config.d.ts +1 -0
- package/dist/schemas/site-config.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/ChatPanel.tsx +51 -19
- package/src/components/chat/ChatWidget.tsx +113 -51
- package/src/components/shared/Fab.tsx +57 -0
- package/src/components/shell/BugReportFAB.tsx +3 -7
- package/src/components/shell/EditorShell.tsx +38 -19
- package/src/components/shell/ViewerLoginForm.tsx +19 -8
- package/src/media/compose-preview.ts +56 -4
- package/src/media/preview-inputs.ts +3 -1
- package/src/schemas/site-config.ts +4 -0
|
@@ -10,6 +10,9 @@ export interface PreviewImageInputs {
|
|
|
10
10
|
invertLogo: boolean;
|
|
11
11
|
siteName: string;
|
|
12
12
|
headingFont: string;
|
|
13
|
+
/** The site's Google Fonts CSS URL (or null for system-ui), used to load the
|
|
14
|
+
* heading font before drawing so text mode never falls back to system-ui. */
|
|
15
|
+
googleFontsUrl: string | null;
|
|
13
16
|
/** Computed contrasting text color (text mode). */
|
|
14
17
|
textColor: string;
|
|
15
18
|
}
|
|
@@ -17,6 +20,53 @@ export interface PreviewImageInputs {
|
|
|
17
20
|
const WIDTH = 1200;
|
|
18
21
|
const HEIGHT = 630;
|
|
19
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Bump when the composition logic changes so already-composed rasters (stamped
|
|
25
|
+
* with the old version, or 0 for pre-versioning ones) regenerate on next editor
|
|
26
|
+
* open. v2 = load the Google heading font before drawing + draw at weight 700.
|
|
27
|
+
*/
|
|
28
|
+
export const PREVIEW_COMPOSE_VERSION = 2;
|
|
29
|
+
|
|
30
|
+
// The raster draws the site name at this weight; the loader ensures this exact
|
|
31
|
+
// weight is available so canvas doesn't silently fall back to a nearest weight.
|
|
32
|
+
const HEADING_WEIGHT = 700;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Ensure the heading font is actually loaded before canvas draws with it.
|
|
36
|
+
* `document.fonts.load` only fetches a family that already has a matching
|
|
37
|
+
* @font-face rule registered; in the editor that rule comes from a <link> that
|
|
38
|
+
* may not be parsed yet when composition runs, so an ambient load() can resolve
|
|
39
|
+
* empty and the draw silently falls back to system-ui. If the ambient load
|
|
40
|
+
* yields nothing, fetch the Google Fonts CSS and register the weight-700
|
|
41
|
+
* face(s) explicitly via the CSS Font Loading API.
|
|
42
|
+
*/
|
|
43
|
+
async function loadHeadingFont(family: string, googleFontsUrl: string | null): Promise<void> {
|
|
44
|
+
if (!family || family === "system-ui" || !googleFontsUrl || typeof FontFace === "undefined") return;
|
|
45
|
+
const spec = `${HEADING_WEIGHT} 84px "${family}"`;
|
|
46
|
+
try {
|
|
47
|
+
const loaded = await document.fonts.load(spec);
|
|
48
|
+
if (loaded.length > 0) return;
|
|
49
|
+
} catch {
|
|
50
|
+
// fall through to explicit registration
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const css = await (await fetch(googleFontsUrl)).text();
|
|
54
|
+
const blocks = css.match(/@font-face\s*\{[^}]*\}/g) ?? [];
|
|
55
|
+
const wanted = blocks.filter((b) => new RegExp(`font-weight:\\s*${HEADING_WEIGHT}\\b`).test(b));
|
|
56
|
+
await Promise.all(
|
|
57
|
+
(wanted.length ? wanted : blocks).map(async (block) => {
|
|
58
|
+
const url = block.match(/url\(([^)]+)\)/)?.[1];
|
|
59
|
+
if (!url) return;
|
|
60
|
+
const face = new FontFace(family, `url(${url})`, { weight: String(HEADING_WEIGHT) });
|
|
61
|
+
await face.load();
|
|
62
|
+
document.fonts.add(face);
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
} catch {
|
|
66
|
+
// give up — the draw below falls back to system-ui
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
20
70
|
/**
|
|
21
71
|
* Composes the 1200×630 social-preview raster in the browser (Canvas API only,
|
|
22
72
|
* no dependency). Returns a WebP blob. Must run client-side (uses document/Image
|
|
@@ -60,13 +110,15 @@ export async function composePreviewImage(inputs: PreviewImageInputs): Promise<B
|
|
|
60
110
|
|
|
61
111
|
if (!drewLogo) {
|
|
62
112
|
if (document.fonts?.ready) await document.fonts.ready;
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
await
|
|
113
|
+
// Load the heading font (exact weight) before drawing — fillText doesn't
|
|
114
|
+
// itself trigger a webfont load, so without this canvas falls back silently.
|
|
115
|
+
await loadHeadingFont(inputs.headingFont, inputs.googleFontsUrl);
|
|
66
116
|
ctx.fillStyle = inputs.textColor;
|
|
67
117
|
ctx.textAlign = "center";
|
|
68
118
|
ctx.textBaseline = "middle";
|
|
69
|
-
|
|
119
|
+
// Quote the family: multi-word names ("Playfair Display") break the CSS font
|
|
120
|
+
// shorthand parse when unquoted, which would drop the whole declaration.
|
|
121
|
+
ctx.font = `${HEADING_WEIGHT} 84px "${inputs.headingFont}", system-ui, sans-serif`;
|
|
70
122
|
ctx.fillText(inputs.siteName, WIDTH / 2, HEIGHT / 2, WIDTH * 0.86);
|
|
71
123
|
}
|
|
72
124
|
|
|
@@ -19,7 +19,7 @@ export function derivePreviewInputs(
|
|
|
19
19
|
siteConfig: SiteConfig,
|
|
20
20
|
resolveLogoUrl: (imageId: string) => string | null,
|
|
21
21
|
): { inputs: PreviewImageInputs; signature: string } {
|
|
22
|
-
const { logo, darkMode, primaryColor, siteName, headingFont } = siteConfig;
|
|
22
|
+
const { logo, darkMode, primaryColor, siteName, headingFont, googleFontsUrl } = siteConfig;
|
|
23
23
|
const dark = darkMode === "dark";
|
|
24
24
|
|
|
25
25
|
let mode: "logo" | "text" = "text";
|
|
@@ -43,6 +43,7 @@ export function derivePreviewInputs(
|
|
|
43
43
|
invertLogo,
|
|
44
44
|
siteName,
|
|
45
45
|
headingFont,
|
|
46
|
+
googleFontsUrl,
|
|
46
47
|
textColor: contrastColor(primaryColor),
|
|
47
48
|
};
|
|
48
49
|
|
|
@@ -53,6 +54,7 @@ export function derivePreviewInputs(
|
|
|
53
54
|
invertLogo: inputs.invertLogo,
|
|
54
55
|
siteName: inputs.siteName,
|
|
55
56
|
headingFont: inputs.headingFont,
|
|
57
|
+
googleFontsUrl: inputs.googleFontsUrl,
|
|
56
58
|
textColor: inputs.textColor,
|
|
57
59
|
});
|
|
58
60
|
|
|
@@ -245,6 +245,10 @@ export const SiteConfigSchema = z.object({
|
|
|
245
245
|
imageId: z.string(),
|
|
246
246
|
width: z.number().int().positive(),
|
|
247
247
|
height: z.number().int().positive(),
|
|
248
|
+
// Compose-logic version that produced this raster. Pre-existing rasters
|
|
249
|
+
// (and manifests written before this field) default to 0, which is below
|
|
250
|
+
// any current PREVIEW_COMPOSE_VERSION, so the editor regenerates them.
|
|
251
|
+
version: z.number().int().nonnegative().default(0),
|
|
248
252
|
})
|
|
249
253
|
.nullable()
|
|
250
254
|
.default(null),
|