@drawnagency/primitives 0.1.86 → 0.1.87
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-665DW3HR.js → chunk-6VOIZP2O.js} +3 -3
- package/dist/{chunk-YKOLOZB2.js → chunk-DRCPDOW3.js} +14 -0
- package/dist/{chunk-MBJUWOM2.js → chunk-EO7K45GV.js} +1 -1
- package/dist/{chunk-DKXISNJ2.js → chunk-NEDOJHHR.js} +2 -2
- package/dist/{chunk-DO3ZENLN.js → chunk-OEL57NYV.js} +1 -1
- package/dist/{chunk-FDBZZ4AT.js → chunk-PTVY3Q7P.js} +1 -1
- package/dist/{chunk-2CVP2AGK.js → chunk-VY5V4HBV.js} +6 -6
- package/dist/{chunk-EP3LVOXZ.js → chunk-WA3HFN54.js} +1 -1
- package/dist/{chunk-YAPG44NS.js → chunk-XDG2K6UT.js} +1 -1
- package/dist/{chunk-E3MTIMOG.js → chunk-XPUBUR64.js} +10 -0
- 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/Navigation.d.ts +6 -1
- package/dist/components/shared/Navigation.d.ts.map +1 -1
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -1
- package/dist/hooks/useMediaPipeline.d.ts +2 -1
- package/dist/hooks/useMediaPipeline.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/lib/image-refs.d.ts +9 -0
- package/dist/lib/image-refs.d.ts.map +1 -1
- package/dist/lib/index.js +2 -2
- package/dist/media/index.js +4 -2
- package/dist/media/resolve.d.ts +25 -0
- package/dist/media/resolve.d.ts.map +1 -1
- package/dist/schemas/index.js +2 -2
- package/dist/schemas/site-config.d.ts +10 -0
- package/dist/schemas/site-config.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/shared/Navigation.tsx +20 -2
- package/src/components/shell/EditorShell.tsx +4 -0
- package/src/components/shell/SiteSettingsDisplay.tsx +106 -0
- package/src/hooks/useMediaPipeline.ts +16 -3
- package/src/lib/image-refs.ts +19 -0
- package/src/media/resolve.ts +34 -0
- package/src/schemas/site-config.ts +13 -0
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
MediaBlock,
|
|
10
10
|
containerGridClass,
|
|
11
11
|
useEditableCollection
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-NEDOJHHR.js";
|
|
13
13
|
import {
|
|
14
14
|
AddIcon,
|
|
15
15
|
DeleteIcon,
|
|
16
16
|
DragHandle,
|
|
17
17
|
IconButton
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-EO7K45GV.js";
|
|
19
19
|
import {
|
|
20
20
|
DEFAULT_LINK,
|
|
21
21
|
LinkValueSchema
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
import {
|
|
31
31
|
HexColorSchema,
|
|
32
32
|
SingleMediaReferenceSchema
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-XPUBUR64.js";
|
|
34
34
|
import {
|
|
35
35
|
defineSection
|
|
36
36
|
} from "./chunk-VKAGMEKE.js";
|
|
@@ -229,6 +229,19 @@ function rebindImageRef(ref, imageId) {
|
|
|
229
229
|
for (const key of BAKED_MEDIA_KEYS) delete fresh[key];
|
|
230
230
|
return fresh;
|
|
231
231
|
}
|
|
232
|
+
function resolveLogo(logo, manifest, provider) {
|
|
233
|
+
if (!logo) return null;
|
|
234
|
+
const light = resolveMedia({ imageId: logo.light.imageId }, manifest, [1], provider);
|
|
235
|
+
if (!light?.src || light.tag === "video") return null;
|
|
236
|
+
const dark = logo.dark ? resolveMedia({ imageId: logo.dark.imageId }, manifest, [1], provider) : null;
|
|
237
|
+
const darkUrl = dark && dark.tag !== "video" ? dark.src : null;
|
|
238
|
+
return {
|
|
239
|
+
lightUrl: light.src,
|
|
240
|
+
darkUrl,
|
|
241
|
+
invertInDark: logo.invertInDark,
|
|
242
|
+
showInNav: logo.showInNav
|
|
243
|
+
};
|
|
244
|
+
}
|
|
232
245
|
|
|
233
246
|
// src/media/videoPoster.ts
|
|
234
247
|
function generateVideoPoster(blob, quality) {
|
|
@@ -324,6 +337,7 @@ export {
|
|
|
324
337
|
resolveManifestReferences,
|
|
325
338
|
BAKED_MEDIA_KEYS,
|
|
326
339
|
rebindImageRef,
|
|
340
|
+
resolveLogo,
|
|
327
341
|
generateVideoPoster,
|
|
328
342
|
setMediaProvider,
|
|
329
343
|
getMediaProvider
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
SettingsIcon,
|
|
7
7
|
useMediaLibrary,
|
|
8
8
|
useResolvedMedia
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-EO7K45GV.js";
|
|
10
10
|
import {
|
|
11
11
|
rebindImageRef
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-DRCPDOW3.js";
|
|
13
13
|
import {
|
|
14
14
|
cn
|
|
15
15
|
} from "./chunk-7EOSDFS3.js";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
pluralize,
|
|
4
4
|
stripHtmlToPlainText,
|
|
5
5
|
truncate
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-6VOIZP2O.js";
|
|
7
7
|
import {
|
|
8
8
|
DeleteButton,
|
|
9
9
|
DragHandle,
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
buildBlockDropData,
|
|
16
16
|
canDropBlock,
|
|
17
17
|
isBlockDragData
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-XDG2K6UT.js";
|
|
19
19
|
import {
|
|
20
20
|
EditableRichText,
|
|
21
21
|
defineHeadingSection
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-PTVY3Q7P.js";
|
|
23
23
|
import {
|
|
24
24
|
EditablePlainText,
|
|
25
25
|
ImageDropZone,
|
|
@@ -28,11 +28,11 @@ import {
|
|
|
28
28
|
colSpanClass,
|
|
29
29
|
containerGridClass,
|
|
30
30
|
spacerHiddenClass
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-NEDOJHHR.js";
|
|
32
32
|
import {
|
|
33
33
|
useMediaLibrary,
|
|
34
34
|
useResolvedMedia
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-EO7K45GV.js";
|
|
36
36
|
import {
|
|
37
37
|
embedUrl
|
|
38
38
|
} from "./chunk-Y4JQGO6G.js";
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
} from "./chunk-TZQPOR5A.js";
|
|
52
52
|
import {
|
|
53
53
|
SingleMediaReferenceSchema
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-XPUBUR64.js";
|
|
55
55
|
import {
|
|
56
56
|
defineSection,
|
|
57
57
|
getSection
|
|
@@ -277,6 +277,16 @@ var SiteConfigSchema = z3.object({
|
|
|
277
277
|
// Favicon stored as a data URL (SVG/PNG, small) so it travels with
|
|
278
278
|
// site-config.json — no media manifest or CDN round-trip needed.
|
|
279
279
|
favicon: z3.string().refine((v) => v.startsWith("data:image/"), "Favicon must be an image data URL").nullable().default(null),
|
|
280
|
+
// Optional site logo, stored as media-library references (so it participates
|
|
281
|
+
// in usage tracking + GC protection, and its resolved URL is reusable for the
|
|
282
|
+
// Phase 3 social-preview image). Every field defaults so pre-existing
|
|
283
|
+
// site-config.json files keep parsing.
|
|
284
|
+
logo: z3.object({
|
|
285
|
+
light: z3.object({ imageId: z3.string() }),
|
|
286
|
+
dark: z3.object({ imageId: z3.string() }).nullable().default(null),
|
|
287
|
+
invertInDark: z3.boolean().default(false),
|
|
288
|
+
showInNav: z3.boolean().default(true)
|
|
289
|
+
}).nullable().default(null),
|
|
280
290
|
media: MediaConfigSchema.default(MediaConfigSchema.parse({})),
|
|
281
291
|
// Brand chatbot config (editor-owned; the platform kill switch lives in the
|
|
282
292
|
// sites table, not here). Every field defaults so pre-existing
|
|
@@ -11,20 +11,20 @@ import {
|
|
|
11
11
|
buildBlockDropData,
|
|
12
12
|
canDropBlock,
|
|
13
13
|
isBlockDragData
|
|
14
|
-
} from "../../chunk-
|
|
14
|
+
} from "../../chunk-XDG2K6UT.js";
|
|
15
15
|
import "../../chunk-5THS4K5D.js";
|
|
16
16
|
import "../../chunk-ZSPCE6MZ.js";
|
|
17
17
|
import {
|
|
18
18
|
AddIcon,
|
|
19
19
|
IconButton
|
|
20
|
-
} from "../../chunk-
|
|
21
|
-
import "../../chunk-
|
|
20
|
+
} from "../../chunk-EO7K45GV.js";
|
|
21
|
+
import "../../chunk-DRCPDOW3.js";
|
|
22
22
|
import "../../chunk-677NTHAZ.js";
|
|
23
23
|
import {
|
|
24
24
|
cn
|
|
25
25
|
} from "../../chunk-7EOSDFS3.js";
|
|
26
26
|
import "../../chunk-YBVK254P.js";
|
|
27
|
-
import "../../chunk-
|
|
27
|
+
import "../../chunk-XPUBUR64.js";
|
|
28
28
|
import "../../chunk-FM4M4Q2W.js";
|
|
29
29
|
import "../../chunk-JSBRDJBE.js";
|
|
30
30
|
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
EditableRichText,
|
|
3
3
|
LinkPopover,
|
|
4
4
|
defineHeadingSection
|
|
5
|
-
} from "../../chunk-
|
|
5
|
+
} from "../../chunk-PTVY3Q7P.js";
|
|
6
6
|
import {
|
|
7
7
|
EditableGrid,
|
|
8
8
|
EditablePlainText,
|
|
@@ -10,20 +10,20 @@ import {
|
|
|
10
10
|
MediaBlock,
|
|
11
11
|
ResolvedMedia,
|
|
12
12
|
useEditableCollection
|
|
13
|
-
} from "../../chunk-
|
|
13
|
+
} from "../../chunk-NEDOJHHR.js";
|
|
14
14
|
import "../../chunk-ZSPCE6MZ.js";
|
|
15
15
|
import {
|
|
16
16
|
DeleteIcon,
|
|
17
17
|
DragHandle,
|
|
18
18
|
IconButton
|
|
19
|
-
} from "../../chunk-
|
|
20
|
-
import "../../chunk-
|
|
19
|
+
} from "../../chunk-EO7K45GV.js";
|
|
20
|
+
import "../../chunk-DRCPDOW3.js";
|
|
21
21
|
import "../../chunk-53DKVAB6.js";
|
|
22
22
|
import {
|
|
23
23
|
cn
|
|
24
24
|
} from "../../chunk-7EOSDFS3.js";
|
|
25
25
|
import "../../chunk-YBVK254P.js";
|
|
26
|
-
import "../../chunk-
|
|
26
|
+
import "../../chunk-XPUBUR64.js";
|
|
27
27
|
import "../../chunk-VKAGMEKE.js";
|
|
28
28
|
import "../../chunk-FM4M4Q2W.js";
|
|
29
29
|
import "../../chunk-JSBRDJBE.js";
|
|
@@ -2,16 +2,16 @@ import {
|
|
|
2
2
|
ColorItemSchema,
|
|
3
3
|
ColorSpaceSchema,
|
|
4
4
|
brandGuideSectionDefs
|
|
5
|
-
} from "../../chunk-
|
|
5
|
+
} from "../../chunk-6VOIZP2O.js";
|
|
6
6
|
import "../../chunk-5THS4K5D.js";
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
9
|
-
import "../../chunk-
|
|
7
|
+
import "../../chunk-NEDOJHHR.js";
|
|
8
|
+
import "../../chunk-EO7K45GV.js";
|
|
9
|
+
import "../../chunk-DRCPDOW3.js";
|
|
10
10
|
import "../../chunk-PZJL2S3Z.js";
|
|
11
11
|
import "../../chunk-WDSVHKAH.js";
|
|
12
12
|
import "../../chunk-7EOSDFS3.js";
|
|
13
13
|
import "../../chunk-YBVK254P.js";
|
|
14
|
-
import "../../chunk-
|
|
14
|
+
import "../../chunk-XPUBUR64.js";
|
|
15
15
|
import "../../chunk-VKAGMEKE.js";
|
|
16
16
|
import "../../chunk-FM4M4Q2W.js";
|
|
17
17
|
import "../../chunk-JSBRDJBE.js";
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
allSectionDefs
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
3
|
+
} from "../../chunk-VY5V4HBV.js";
|
|
4
|
+
import "../../chunk-6VOIZP2O.js";
|
|
5
|
+
import "../../chunk-XDG2K6UT.js";
|
|
6
6
|
import "../../chunk-5THS4K5D.js";
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
7
|
+
import "../../chunk-PTVY3Q7P.js";
|
|
8
|
+
import "../../chunk-NEDOJHHR.js";
|
|
9
9
|
import "../../chunk-ZSPCE6MZ.js";
|
|
10
|
-
import "../../chunk-
|
|
10
|
+
import "../../chunk-EO7K45GV.js";
|
|
11
11
|
import "../../chunk-Y4JQGO6G.js";
|
|
12
|
-
import "../../chunk-
|
|
12
|
+
import "../../chunk-DRCPDOW3.js";
|
|
13
13
|
import "../../chunk-PZJL2S3Z.js";
|
|
14
14
|
import "../../chunk-WDSVHKAH.js";
|
|
15
15
|
import "../../chunk-53DKVAB6.js";
|
|
16
16
|
import "../../chunk-7EOSDFS3.js";
|
|
17
17
|
import "../../chunk-YBVK254P.js";
|
|
18
18
|
import "../../chunk-TZQPOR5A.js";
|
|
19
|
-
import "../../chunk-
|
|
19
|
+
import "../../chunk-XPUBUR64.js";
|
|
20
20
|
import {
|
|
21
21
|
registerRichText,
|
|
22
22
|
registerSchema
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
allSectionDefs
|
|
3
|
-
} from "../../chunk-
|
|
4
|
-
import "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
3
|
+
} from "../../chunk-VY5V4HBV.js";
|
|
4
|
+
import "../../chunk-6VOIZP2O.js";
|
|
5
|
+
import "../../chunk-XDG2K6UT.js";
|
|
6
6
|
import "../../chunk-5THS4K5D.js";
|
|
7
|
-
import "../../chunk-
|
|
8
|
-
import "../../chunk-
|
|
7
|
+
import "../../chunk-PTVY3Q7P.js";
|
|
8
|
+
import "../../chunk-NEDOJHHR.js";
|
|
9
9
|
import "../../chunk-ZSPCE6MZ.js";
|
|
10
|
-
import "../../chunk-
|
|
10
|
+
import "../../chunk-EO7K45GV.js";
|
|
11
11
|
import "../../chunk-Y4JQGO6G.js";
|
|
12
|
-
import "../../chunk-
|
|
12
|
+
import "../../chunk-DRCPDOW3.js";
|
|
13
13
|
import "../../chunk-PZJL2S3Z.js";
|
|
14
14
|
import "../../chunk-WDSVHKAH.js";
|
|
15
15
|
import "../../chunk-53DKVAB6.js";
|
|
16
16
|
import "../../chunk-7EOSDFS3.js";
|
|
17
17
|
import "../../chunk-YBVK254P.js";
|
|
18
18
|
import "../../chunk-TZQPOR5A.js";
|
|
19
|
-
import "../../chunk-
|
|
19
|
+
import "../../chunk-XPUBUR64.js";
|
|
20
20
|
import {
|
|
21
21
|
registerSection
|
|
22
22
|
} from "../../chunk-VKAGMEKE.js";
|
|
@@ -4,8 +4,13 @@ interface Props {
|
|
|
4
4
|
siteName: string;
|
|
5
5
|
darkMode: "light" | "dark" | "optional";
|
|
6
6
|
lastUpdated: string | null;
|
|
7
|
+
logo?: {
|
|
8
|
+
lightUrl: string;
|
|
9
|
+
darkUrl: string | null;
|
|
10
|
+
invertInDark: boolean;
|
|
11
|
+
} | null;
|
|
7
12
|
onPageSelect?: (pageId: string) => void;
|
|
8
13
|
}
|
|
9
|
-
export default function Navigation({ siteNav: initialNav, siteName, darkMode, lastUpdated, onPageSelect }: Props): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export default function Navigation({ siteNav: initialNav, siteName, darkMode, lastUpdated, logo, onPageSelect }: Props): import("react/jsx-runtime").JSX.Element;
|
|
10
15
|
export {};
|
|
11
16
|
//# sourceMappingURL=Navigation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Navigation.d.ts","sourceRoot":"","sources":["../../../src/components/shared/Navigation.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAW,MAAM,eAAe,CAAC;AAOtD,UAAU,KAAK;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;IACxC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"Navigation.d.ts","sourceRoot":"","sources":["../../../src/components/shared/Navigation.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAW,MAAM,eAAe,CAAC;AAOtD,UAAU,KAAK;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC;IACxC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAGlF,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,KAAK,2CAyPrH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AASjD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAuDtF,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AAmCnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAoBxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AASjD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAuDtF,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AAmCnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAoBxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,2CA0hDP;AAuED;6EAC6E;AAC7E,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAK/B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SiteSettingsDisplay.d.ts","sourceRoot":"","sources":["../../../src/components/shell/SiteSettingsDisplay.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SiteSettingsDisplay.d.ts","sourceRoot":"","sources":["../../../src/components/shell/SiteSettingsDisplay.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAG5D,UAAU,KAAK;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AA4BD,wBAAgB,mBAAmB,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,KAAK,2CA2QlE"}
|
|
@@ -15,8 +15,9 @@ interface UseMediaPipelineInput {
|
|
|
15
15
|
markSectionDirty: (sectionId: string, content: SectionContent) => void;
|
|
16
16
|
onUploadError?: (message: string) => void;
|
|
17
17
|
onUploadNotice?: (message: string) => void;
|
|
18
|
+
onSiteConfigChange?: (updater: (prev: SiteConfig) => SiteConfig) => void;
|
|
18
19
|
}
|
|
19
|
-
export declare function useMediaPipeline({ siteConfig, mediaManifest, setMediaManifest, sections, setSections, setLocalChangesExist, setDirtySectionIds, markSectionDirty, onUploadError, onUploadNotice, }: UseMediaPipelineInput): {
|
|
20
|
+
export declare function useMediaPipeline({ siteConfig, mediaManifest, setMediaManifest, sections, setSections, setLocalChangesExist, setDirtySectionIds, markSectionDirty, onUploadError, onUploadNotice, onSiteConfigChange, }: UseMediaPipelineInput): {
|
|
20
21
|
processingItems: QueueItem[];
|
|
21
22
|
pendingMediaItems: {
|
|
22
23
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMediaPipeline.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaPipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,SAAS,EAAmB,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,gBAAgB,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AAexF,UAAU,qBAAqB;IAC7B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;IACtE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACnE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtE,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAIvE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAG1C,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"useMediaPipeline.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaPipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAmB,KAAK,SAAS,EAAmB,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,gBAAgB,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AAexF,UAAU,qBAAqB;IAC7B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,CAAC;IACtE,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACnE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtE,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAIvE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAG1C,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAI3C,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,UAAU,KAAK,IAAI,CAAC;CAC1E;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,EAAE,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAkS8B,IAAI,EAAE,mBAAkB,MAAM,GAAG,IAAI;6BAyDvC,MAAM,EAAE;+BAsDZ,MAAM,OAAO,MAAM;+BAiBnB,MAAM,YAAY,MAAM,GAAG,IAAI,KAAG,MAAM;6BAW1C,MAAM,QAAQ,MAAM;2BAWtB,MAAM,YAAY,MAAM,GAAG,IAAI;6BA4BvB,MAAM,YAAY,MAAM,GAAG,QAAQ;2BAd3C,MAAM,EAAE,YAAY,MAAM,GAAG,IAAI;kCA4DhE;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,mBAC9B,MAAM,GAAG,IAAI;;;wBAkCO,IAAI,eAAe,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;EAoFpF"}
|
package/dist/index.js
CHANGED
|
@@ -26,11 +26,12 @@ import {
|
|
|
26
26
|
hashFileBuffer,
|
|
27
27
|
mimeToExt,
|
|
28
28
|
rebindImageRef,
|
|
29
|
+
resolveLogo,
|
|
29
30
|
resolveManifestReferences,
|
|
30
31
|
resolveMedia,
|
|
31
32
|
sanitizeMediaName,
|
|
32
33
|
setMediaProvider
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-DRCPDOW3.js";
|
|
34
35
|
import {
|
|
35
36
|
CollabPeerSchema,
|
|
36
37
|
HEARTBEAT_INTERVAL_MS,
|
|
@@ -49,7 +50,7 @@ import {
|
|
|
49
50
|
AudienceNameSchema,
|
|
50
51
|
MediaGridOptionsSchema,
|
|
51
52
|
slugifyAudienceName
|
|
52
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-WA3HFN54.js";
|
|
53
54
|
import {
|
|
54
55
|
AudienceSchema,
|
|
55
56
|
RoleSchema,
|
|
@@ -71,7 +72,7 @@ import {
|
|
|
71
72
|
loadStaticSiteContent,
|
|
72
73
|
mergeSiteContent,
|
|
73
74
|
safeRedirect
|
|
74
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-OEL57NYV.js";
|
|
75
76
|
import {
|
|
76
77
|
curatedIcons,
|
|
77
78
|
getIcon
|
|
@@ -124,7 +125,7 @@ import {
|
|
|
124
125
|
TextLineSchema,
|
|
125
126
|
VariantSchema,
|
|
126
127
|
normalizeSiteIndex
|
|
127
|
-
} from "./chunk-
|
|
128
|
+
} from "./chunk-XPUBUR64.js";
|
|
128
129
|
import {
|
|
129
130
|
clearRegistry,
|
|
130
131
|
createRegistry,
|
|
@@ -243,6 +244,7 @@ export {
|
|
|
243
244
|
registerSchema,
|
|
244
245
|
registerSection,
|
|
245
246
|
requireSessionSecret,
|
|
247
|
+
resolveLogo,
|
|
246
248
|
resolveManifestReferences,
|
|
247
249
|
resolveMedia,
|
|
248
250
|
safeNextPath,
|
package/dist/lib/image-refs.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SiteConfig } from "../schemas/site-config";
|
|
1
2
|
/**
|
|
2
3
|
* Structural walkers over section content for `imageId` references.
|
|
3
4
|
*
|
|
@@ -18,4 +19,12 @@ export declare function collectImageIds(value: unknown): Set<string>;
|
|
|
18
19
|
* including arrays and nesting, is preserved unchanged.
|
|
19
20
|
*/
|
|
20
21
|
export declare function clearImageIds(value: unknown, ids: Set<string>): unknown;
|
|
22
|
+
/**
|
|
23
|
+
* Applies a media-deletion id set to a site logo config, granularly: deleting
|
|
24
|
+
* the light variant's backing asset removes the logo entirely (no light = no
|
|
25
|
+
* logo to show), while deleting only the dark variant's asset keeps the
|
|
26
|
+
* light logo + flags and drops just the dark override. Returns the same
|
|
27
|
+
* `logo` reference unchanged when neither id was deleted.
|
|
28
|
+
*/
|
|
29
|
+
export declare function applyLogoDeletion(logo: SiteConfig["logo"], deletedIds: Set<string>): SiteConfig["logo"];
|
|
21
30
|
//# sourceMappingURL=image-refs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-refs.d.ts","sourceRoot":"","sources":["../../src/lib/image-refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,GAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAa1G;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAevE"}
|
|
1
|
+
{"version":3,"file":"image-refs.d.ts","sourceRoot":"","sources":["../../src/lib/image-refs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;;;;;;;GASG;AAEH,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,GAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAa1G;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAevE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,EACxB,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,UAAU,CAAC,MAAM,CAAC,CAKpB"}
|
package/dist/lib/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
loadStaticSiteContent,
|
|
8
8
|
mergeSiteContent,
|
|
9
9
|
safeRedirect
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-OEL57NYV.js";
|
|
11
11
|
import {
|
|
12
12
|
curatedIcons,
|
|
13
13
|
getIcon
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
} from "../chunk-YBVK254P.js";
|
|
34
34
|
import "../chunk-75P6HWPN.js";
|
|
35
35
|
import "../chunk-TZQPOR5A.js";
|
|
36
|
-
import "../chunk-
|
|
36
|
+
import "../chunk-XPUBUR64.js";
|
|
37
37
|
import {
|
|
38
38
|
clearRegistry,
|
|
39
39
|
createRegistry,
|
package/dist/media/index.js
CHANGED
|
@@ -12,11 +12,12 @@ import {
|
|
|
12
12
|
hashFileBuffer,
|
|
13
13
|
mimeToExt,
|
|
14
14
|
rebindImageRef,
|
|
15
|
+
resolveLogo,
|
|
15
16
|
resolveManifestReferences,
|
|
16
17
|
resolveMedia,
|
|
17
18
|
sanitizeMediaName,
|
|
18
19
|
setMediaProvider
|
|
19
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-DRCPDOW3.js";
|
|
20
21
|
import "../chunk-YBVK254P.js";
|
|
21
22
|
import {
|
|
22
23
|
ImageManifestSchema,
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
MediaFolderSchema,
|
|
25
26
|
MediaItemSchema,
|
|
26
27
|
VariantSchema
|
|
27
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-XPUBUR64.js";
|
|
28
29
|
import "../chunk-FM4M4Q2W.js";
|
|
29
30
|
import "../chunk-JSBRDJBE.js";
|
|
30
31
|
export {
|
|
@@ -46,6 +47,7 @@ export {
|
|
|
46
47
|
hashFileBuffer,
|
|
47
48
|
mimeToExt,
|
|
48
49
|
rebindImageRef,
|
|
50
|
+
resolveLogo,
|
|
49
51
|
resolveManifestReferences,
|
|
50
52
|
resolveMedia,
|
|
51
53
|
sanitizeMediaName,
|
package/dist/media/resolve.d.ts
CHANGED
|
@@ -26,4 +26,29 @@ export declare const BAKED_MEDIA_KEYS: readonly ["src", "srcset", "alt", "width"
|
|
|
26
26
|
export declare function rebindImageRef<T extends {
|
|
27
27
|
imageId?: string;
|
|
28
28
|
}>(ref: T, imageId: string): T;
|
|
29
|
+
export interface ResolvedLogo {
|
|
30
|
+
lightUrl: string;
|
|
31
|
+
darkUrl: string | null;
|
|
32
|
+
invertInDark: boolean;
|
|
33
|
+
showInNav: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolves a SiteConfig["logo"] entry into concrete URLs. Returns null when
|
|
37
|
+
* there is no logo configured, when the light variant's imageId can't be
|
|
38
|
+
* resolved against the manifest, or when the light variant resolves to a
|
|
39
|
+
* video (an `<img>` can't render mp4/webm) — callers should fall back to
|
|
40
|
+
* rendering the site name as text rather than a broken image. Still-image and
|
|
41
|
+
* animated (gif) picks are both fine — only a video pick is rejected, since
|
|
42
|
+
* both those tags resolve to `tag: "img"`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveLogo(logo: {
|
|
45
|
+
light: {
|
|
46
|
+
imageId: string;
|
|
47
|
+
};
|
|
48
|
+
dark: {
|
|
49
|
+
imageId: string;
|
|
50
|
+
} | null;
|
|
51
|
+
invertInDark: boolean;
|
|
52
|
+
showInNav: boolean;
|
|
53
|
+
} | null, manifest: MediaManifest, provider: MediaProvider): ResolvedLogo | null;
|
|
29
54
|
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/media/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,wBAAgB,YAAY,CAC1B,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACxC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,aAAa,GACtB,aAAa,GAAG,IAAI,CAKtB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,aAAa,GACtB,OAAO,CA0CT;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,gEAAiE,CAAC;AAE/F;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAIzF"}
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/media/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,wBAAgB,YAAY,CAC1B,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACxC,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,aAAa,GACtB,aAAa,GAAG,IAAI,CAKtB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,aAAa,EACvB,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,aAAa,GACtB,OAAO,CA0CT;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,gEAAiE,CAAC;AAE/F;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAIzF;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE;IAAE,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,EACxH,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,aAAa,GACtB,YAAY,GAAG,IAAI,CAYrB"}
|
package/dist/schemas/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
AudienceNameSchema,
|
|
4
4
|
MediaGridOptionsSchema,
|
|
5
5
|
slugifyAudienceName
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-WA3HFN54.js";
|
|
7
7
|
import {
|
|
8
8
|
AudienceSchema,
|
|
9
9
|
RoleSchema,
|
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
TextLineSchema,
|
|
41
41
|
VariantSchema,
|
|
42
42
|
normalizeSiteIndex
|
|
43
|
-
} from "../chunk-
|
|
43
|
+
} from "../chunk-XPUBUR64.js";
|
|
44
44
|
import "../chunk-VKAGMEKE.js";
|
|
45
45
|
import "../chunk-FM4M4Q2W.js";
|
|
46
46
|
import "../chunk-NSCT3AMV.js";
|
|
@@ -456,6 +456,16 @@ export declare const SiteConfigSchema: z.ZodObject<{
|
|
|
456
456
|
uppercaseNavHeadings: z.ZodDefault<z.ZodBoolean>;
|
|
457
457
|
googleFontsUrl: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
458
458
|
favicon: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
459
|
+
logo: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
460
|
+
light: z.ZodObject<{
|
|
461
|
+
imageId: z.ZodString;
|
|
462
|
+
}, z.core.$strip>;
|
|
463
|
+
dark: z.ZodDefault<z.ZodNullable<z.ZodObject<{
|
|
464
|
+
imageId: z.ZodString;
|
|
465
|
+
}, z.core.$strip>>>;
|
|
466
|
+
invertInDark: z.ZodDefault<z.ZodBoolean>;
|
|
467
|
+
showInNav: z.ZodDefault<z.ZodBoolean>;
|
|
468
|
+
}, z.core.$strip>>>;
|
|
459
469
|
media: z.ZodDefault<z.ZodObject<{
|
|
460
470
|
sizes: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
461
471
|
maxFileSize: z.ZodDefault<z.ZodNumber>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"site-config.d.ts","sourceRoot":"","sources":["../../src/schemas/site-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,eAAO,MAAM,iBAAiB;;;;;;;;;iBAI5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAQ5D,eAAO,MAAM,cAAc,8EAA+E,CAAC;AAW3G,eAAO,MAAM,gBAAgB;;;EAA+B,CAAC;AAE7D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AA0C9C,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6CtB,CAAC;AAEL,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAAyE,CAAC;AAElG,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAE7D,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAE1D;AAMD,eAAO,MAAM,iBAAiB;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,mBAAmB;;;;;;;;;;;iBAwB5B,CAAC;AACL,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"site-config.d.ts","sourceRoot":"","sources":["../../src/schemas/site-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,eAAO,MAAM,iBAAiB;;;;;;;;;iBAI5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAQ5D,eAAO,MAAM,cAAc,8EAA+E,CAAC;AAW3G,eAAO,MAAM,gBAAgB;;;EAA+B,CAAC;AAE7D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAUrB,CAAC;AAEH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AA0C9C,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6CtB,CAAC;AAEL,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAAyE,CAAC;AAElG,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAE7D,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAE1D;AAMD,eAAO,MAAM,iBAAiB;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,mBAAmB;;;;;;;;;;;iBAwB5B,CAAC;AACL,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyC3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -14,12 +14,13 @@ interface Props {
|
|
|
14
14
|
siteName: string;
|
|
15
15
|
darkMode: "light" | "dark" | "optional";
|
|
16
16
|
lastUpdated: string | null;
|
|
17
|
+
logo?: { lightUrl: string; darkUrl: string | null; invertInDark: boolean } | null;
|
|
17
18
|
// Optional same-island handler (tests). In production the editor nav is a
|
|
18
19
|
// separate island from EditorShell, so a click dispatches pageSelectEvent.
|
|
19
20
|
onPageSelect?: (pageId: string) => void;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export default function Navigation({ siteNav: initialNav, siteName, darkMode, lastUpdated, onPageSelect }: Props) {
|
|
23
|
+
export default function Navigation({ siteNav: initialNav, siteName, darkMode, lastUpdated, logo, onPageSelect }: Props) {
|
|
23
24
|
const [isOpen, setIsOpen] = useState(false);
|
|
24
25
|
const [isEditMode, setIsEditMode] = useState(false);
|
|
25
26
|
const [currentDarkMode, setCurrentDarkMode] = useState(darkMode);
|
|
@@ -135,6 +136,19 @@ export default function Navigation({ siteNav: initialNav, siteName, darkMode, la
|
|
|
135
136
|
</ul>
|
|
136
137
|
);
|
|
137
138
|
|
|
139
|
+
const logoMark = logo ? (
|
|
140
|
+
<>
|
|
141
|
+
<img
|
|
142
|
+
src={logo.lightUrl}
|
|
143
|
+
alt={currentSiteName}
|
|
144
|
+
className={logo.darkUrl ? "block h-8 w-auto object-contain dark:hidden" : logo.invertInDark ? "h-8 w-auto object-contain dark:invert" : "h-8 w-auto object-contain"}
|
|
145
|
+
/>
|
|
146
|
+
{logo.darkUrl && (
|
|
147
|
+
<img src={logo.darkUrl} alt={currentSiteName} className="hidden h-8 w-auto object-contain dark:block" />
|
|
148
|
+
)}
|
|
149
|
+
</>
|
|
150
|
+
) : null;
|
|
151
|
+
|
|
138
152
|
const handlePageClick = useCallback((id: string) => {
|
|
139
153
|
if (onPageSelect) onPageSelect(id);
|
|
140
154
|
else pageSelectEvent.dispatch(id);
|
|
@@ -193,7 +207,10 @@ export default function Navigation({ siteNav: initialNav, siteName, darkMode, la
|
|
|
193
207
|
<>
|
|
194
208
|
<header className="fixed top-0 left-0 right-0 z-50 bg-base lg:hidden">
|
|
195
209
|
<div className="mx-auto max-w-screen-xl flex h-16 items-center justify-between px-4">
|
|
196
|
-
<
|
|
210
|
+
<div className="flex flex-col items-start gap-1">
|
|
211
|
+
{logoMark}
|
|
212
|
+
<span className="text-lg font-bold text-primary">{currentSiteName}</span>
|
|
213
|
+
</div>
|
|
197
214
|
<button onClick={() => setIsOpen(!isOpen)} className="cursor-pointer p-2 text-base-contrast" aria-label="Toggle navigation">
|
|
198
215
|
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
|
199
216
|
{isOpen
|
|
@@ -209,6 +226,7 @@ export default function Navigation({ siteNav: initialNav, siteName, darkMode, la
|
|
|
209
226
|
<nav className={cn("fixed top-0 left-0 lg:left-auto z-40 h-full w-64 flex flex-col overflow-y-auto border-r border-base-200 bg-base pt-16 transition-transform lg:translate-x-0 nav-sidebar",
|
|
210
227
|
isOpen ? "translate-x-0" : "-translate-x-full")}>
|
|
211
228
|
<div className="hidden px-4 py-4 lg:block">
|
|
229
|
+
{logoMark}
|
|
212
230
|
<span className="text-lg font-bold text-primary">{currentSiteName}</span>
|
|
213
231
|
</div>
|
|
214
232
|
|
|
@@ -363,6 +363,10 @@ export default function EditorShell({
|
|
|
363
363
|
markSectionDirty: persistence.markSectionDirty,
|
|
364
364
|
onUploadError: (message) => setUploadErrorMessage(message),
|
|
365
365
|
onUploadNotice: setUploadNotice,
|
|
366
|
+
onSiteConfigChange: (updater) => {
|
|
367
|
+
if (!siteConfig) return;
|
|
368
|
+
handleSiteConfigChange(updater(siteConfig));
|
|
369
|
+
},
|
|
366
370
|
});
|
|
367
371
|
|
|
368
372
|
const buildStatus = useBuildStatus();
|
|
@@ -6,15 +6,27 @@ import { FontPicker } from "../shared/FontPicker";
|
|
|
6
6
|
import { Input } from "../shared/Input";
|
|
7
7
|
import { Select } from "../shared/Select";
|
|
8
8
|
import { FormLabel } from "../shared/FormLabel";
|
|
9
|
+
import { useMediaLibrary } from "./MediaLibraryContext";
|
|
9
10
|
import { deriveContrast } from "../../lib/contrast";
|
|
10
11
|
import { buildGoogleFontsUrl } from "../../lib/google-fonts";
|
|
11
12
|
import type { SiteConfig } from "../../schemas/site-config";
|
|
13
|
+
import type { MediaItem } from "../../media/types";
|
|
12
14
|
|
|
13
15
|
interface Props {
|
|
14
16
|
siteConfig: SiteConfig;
|
|
15
17
|
onChange: (config: SiteConfig) => void;
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
// Smallest generated variant for the editor-preview thumbnail (mirrors
|
|
21
|
+
// DocInspector's thumbSrc) — avoids needing a MediaProvider in this context.
|
|
22
|
+
function thumbSrc(imageId: string, item: MediaItem | undefined): string | null {
|
|
23
|
+
if (!item || item.kind !== "image") return null;
|
|
24
|
+
if (item.mimeType === "image/svg+xml") return `/api/media/${imageId}/original.svg`;
|
|
25
|
+
const widths = item.variants.map((v) => v.width).sort((a, b) => a - b);
|
|
26
|
+
if (widths.length === 0) return null;
|
|
27
|
+
return `/api/media/${imageId}/${widths[0]}.webp`;
|
|
28
|
+
}
|
|
29
|
+
|
|
18
30
|
const darkModeOptions = [
|
|
19
31
|
{ label: "Light", value: "light" },
|
|
20
32
|
{ label: "Dark", value: "dark" },
|
|
@@ -33,11 +45,19 @@ const FAVICON_MAX_BYTES = 100 * 1024;
|
|
|
33
45
|
|
|
34
46
|
export function SiteSettingsDisplay({ siteConfig, onChange }: Props) {
|
|
35
47
|
const [faviconError, setFaviconError] = useState<string | null>(null);
|
|
48
|
+
const mediaLibrary = useMediaLibrary();
|
|
36
49
|
|
|
37
50
|
function update(patch: Partial<SiteConfig>) {
|
|
38
51
|
onChange({ ...siteConfig, ...patch });
|
|
39
52
|
}
|
|
40
53
|
|
|
54
|
+
function pickLogo(which: "light" | "dark") {
|
|
55
|
+
mediaLibrary?.openSelectModal((imageId) => {
|
|
56
|
+
const base = siteConfig.logo ?? { light: { imageId: "" }, dark: null, invertInDark: false, showInNav: true };
|
|
57
|
+
update({ logo: { ...base, [which]: { imageId } } });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
function handleFaviconFile(file: File | undefined) {
|
|
42
62
|
if (!file) return;
|
|
43
63
|
if (!FAVICON_TYPES.includes(file.type)) {
|
|
@@ -154,6 +174,92 @@ export function SiteSettingsDisplay({ siteConfig, onChange }: Props) {
|
|
|
154
174
|
{faviconError && <p className="mt-1 text-xs text-red-600">{faviconError}</p>}
|
|
155
175
|
</div>
|
|
156
176
|
|
|
177
|
+
{(() => {
|
|
178
|
+
const logo = siteConfig.logo;
|
|
179
|
+
const lightSrc = logo ? thumbSrc(logo.light.imageId, mediaLibrary?.manifest.images[logo.light.imageId]) : null;
|
|
180
|
+
const darkSrc = logo?.dark ? thumbSrc(logo.dark.imageId, mediaLibrary?.manifest.images[logo.dark.imageId]) : null;
|
|
181
|
+
const needsDarkHint = logo != null && siteConfig.darkMode !== "light" && !logo.dark && !logo.invertInDark;
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div>
|
|
185
|
+
<FormLabel>Logo</FormLabel>
|
|
186
|
+
<div className="flex items-center gap-3">
|
|
187
|
+
{logo && (lightSrc ? (
|
|
188
|
+
<img
|
|
189
|
+
src={lightSrc}
|
|
190
|
+
alt="Current logo"
|
|
191
|
+
className="h-8 w-auto max-w-[8rem] rounded border border-base-200 bg-base-accent object-contain p-1"
|
|
192
|
+
/>
|
|
193
|
+
) : (
|
|
194
|
+
<span className="text-xs text-base-contrast-light">Logo selected</span>
|
|
195
|
+
))}
|
|
196
|
+
<Button variant="secondary" size="sm" onClick={() => pickLogo("light")}>
|
|
197
|
+
{logo ? "Replace logo" : "Add logo"}
|
|
198
|
+
</Button>
|
|
199
|
+
{logo && (
|
|
200
|
+
<Button variant="ghost" tone="destructive" size="sm" aria-label="Remove logo" onClick={() => update({ logo: null })}>
|
|
201
|
+
Remove logo
|
|
202
|
+
</Button>
|
|
203
|
+
)}
|
|
204
|
+
</div>
|
|
205
|
+
<p className="mt-1 text-xs text-base-contrast/70">Shown in the site navigation in place of the site name.</p>
|
|
206
|
+
|
|
207
|
+
{logo && (
|
|
208
|
+
<div className="mt-3 space-y-3 border-l-2 border-base-200 pl-3">
|
|
209
|
+
<div>
|
|
210
|
+
<FormLabel>Dark variant</FormLabel>
|
|
211
|
+
<div className="flex items-center gap-3">
|
|
212
|
+
{logo.dark && (darkSrc ? (
|
|
213
|
+
<img
|
|
214
|
+
src={darkSrc}
|
|
215
|
+
alt="Current dark-mode logo"
|
|
216
|
+
className="h-8 w-auto max-w-[8rem] rounded border border-base-200 bg-base-accent object-contain p-1"
|
|
217
|
+
/>
|
|
218
|
+
) : (
|
|
219
|
+
<span className="text-xs text-base-contrast-light">Dark logo selected</span>
|
|
220
|
+
))}
|
|
221
|
+
<Button variant="secondary" size="sm" onClick={() => pickLogo("dark")}>
|
|
222
|
+
{logo.dark ? "Replace dark variant" : "Add dark variant"}
|
|
223
|
+
</Button>
|
|
224
|
+
{logo.dark && (
|
|
225
|
+
<Button
|
|
226
|
+
variant="ghost"
|
|
227
|
+
tone="destructive"
|
|
228
|
+
size="sm"
|
|
229
|
+
aria-label="Remove dark variant"
|
|
230
|
+
onClick={() => update({ logo: { ...logo, dark: null } })}
|
|
231
|
+
>
|
|
232
|
+
Remove
|
|
233
|
+
</Button>
|
|
234
|
+
)}
|
|
235
|
+
</div>
|
|
236
|
+
<Checkbox
|
|
237
|
+
className="mt-2"
|
|
238
|
+
checked={logo.invertInDark}
|
|
239
|
+
onChange={(v) => update({ logo: { ...logo, invertInDark: v } })}
|
|
240
|
+
label="Invert in dark mode"
|
|
241
|
+
description="Flips the light logo's colors instead of uploading a separate file."
|
|
242
|
+
disabled={Boolean(logo.dark)}
|
|
243
|
+
/>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
<Checkbox
|
|
247
|
+
checked={logo.showInNav}
|
|
248
|
+
onChange={(v) => update({ logo: { ...logo, showInNav: v } })}
|
|
249
|
+
label="Show in navigation"
|
|
250
|
+
/>
|
|
251
|
+
|
|
252
|
+
{needsDarkHint && (
|
|
253
|
+
<p className="text-xs text-amber-600">
|
|
254
|
+
Add a dark variant or enable invert so the logo stays legible in dark mode.
|
|
255
|
+
</p>
|
|
256
|
+
)}
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
})()}
|
|
262
|
+
|
|
157
263
|
<Select
|
|
158
264
|
label="Dark mode"
|
|
159
265
|
value={siteConfig.darkMode}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
removePendingMediaDeletion,
|
|
14
14
|
} from "../lib/dexie";
|
|
15
15
|
import { generateVideoPoster } from "../media/videoPoster";
|
|
16
|
-
import { tallyImageIds, collectImageIds, clearImageIds } from "../lib/image-refs";
|
|
16
|
+
import { tallyImageIds, collectImageIds, clearImageIds, applyLogoDeletion } from "../lib/image-refs";
|
|
17
17
|
import {
|
|
18
18
|
createFolderId, uniqueFolderName, effectiveParentId, canMoveFolder,
|
|
19
19
|
descendantFolderIds, itemFolderId, planFolderUpload,
|
|
@@ -36,6 +36,10 @@ interface UseMediaPipelineInput {
|
|
|
36
36
|
// Surfaces non-error, informational notices (e.g. "N files already exist
|
|
37
37
|
// and were left where they are" on dedup skip) to the caller.
|
|
38
38
|
onUploadNotice?: (message: string) => void;
|
|
39
|
+
// Lets handleMediaDelete null out siteConfig.logo when its backing asset is
|
|
40
|
+
// deleted. Updater form (not a plain setter) so the caller can adapt its own
|
|
41
|
+
// value-based site-config change handler.
|
|
42
|
+
onSiteConfigChange?: (updater: (prev: SiteConfig) => SiteConfig) => void;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export function useMediaPipeline({
|
|
@@ -49,6 +53,7 @@ export function useMediaPipeline({
|
|
|
49
53
|
markSectionDirty,
|
|
50
54
|
onUploadError,
|
|
51
55
|
onUploadNotice,
|
|
56
|
+
onSiteConfigChange,
|
|
52
57
|
}: UseMediaPipelineInput) {
|
|
53
58
|
const [processingItems, setProcessingItems] = useState<QueueItem[]>([]);
|
|
54
59
|
const [pendingMediaItems, setPendingMediaItems] = useState<LibraryMediaItem[]>([]);
|
|
@@ -440,8 +445,15 @@ export function useMediaPipeline({
|
|
|
440
445
|
}),
|
|
441
446
|
);
|
|
442
447
|
|
|
448
|
+
if (siteConfig?.logo && onSiteConfigChange) {
|
|
449
|
+
const logoIds = collectImageIds(siteConfig.logo);
|
|
450
|
+
if (ids.some((id) => logoIds.has(id))) {
|
|
451
|
+
onSiteConfigChange((prev) => ({ ...prev, logo: applyLogoDeletion(prev.logo, idSet) }));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
443
455
|
setLocalChangesExist(true);
|
|
444
|
-
}, [pendingMediaItems, markSectionDirty, setSections, setDirtySectionIds, setLocalChangesExist]);
|
|
456
|
+
}, [pendingMediaItems, markSectionDirty, setSections, setDirtySectionIds, setLocalChangesExist, siteConfig, onSiteConfigChange]);
|
|
445
457
|
|
|
446
458
|
const handleMediaAltChange = useCallback((id: string, alt: string) => {
|
|
447
459
|
setMediaManifest((prev) => {
|
|
@@ -585,8 +597,9 @@ export function useMediaPipeline({
|
|
|
585
597
|
const referenceCountMap = useMemo((): Record<string, number> => {
|
|
586
598
|
const counts = new Map<string, number>();
|
|
587
599
|
for (const { section } of sections) tallyImageIds(section, counts);
|
|
600
|
+
if (siteConfig) tallyImageIds(siteConfig, counts);
|
|
588
601
|
return Object.fromEntries(counts);
|
|
589
|
-
}, [sections]);
|
|
602
|
+
}, [sections, siteConfig]);
|
|
590
603
|
|
|
591
604
|
// --- Unified enqueue ---
|
|
592
605
|
|
package/src/lib/image-refs.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { SiteConfig } from "../schemas/site-config";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Structural walkers over section content for `imageId` references.
|
|
3
5
|
*
|
|
@@ -51,3 +53,20 @@ export function clearImageIds(value: unknown, ids: Set<string>): unknown {
|
|
|
51
53
|
}
|
|
52
54
|
return value;
|
|
53
55
|
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Applies a media-deletion id set to a site logo config, granularly: deleting
|
|
59
|
+
* the light variant's backing asset removes the logo entirely (no light = no
|
|
60
|
+
* logo to show), while deleting only the dark variant's asset keeps the
|
|
61
|
+
* light logo + flags and drops just the dark override. Returns the same
|
|
62
|
+
* `logo` reference unchanged when neither id was deleted.
|
|
63
|
+
*/
|
|
64
|
+
export function applyLogoDeletion(
|
|
65
|
+
logo: SiteConfig["logo"],
|
|
66
|
+
deletedIds: Set<string>,
|
|
67
|
+
): SiteConfig["logo"] {
|
|
68
|
+
if (!logo) return logo;
|
|
69
|
+
if (deletedIds.has(logo.light.imageId)) return null;
|
|
70
|
+
if (logo.dark && deletedIds.has(logo.dark.imageId)) return { ...logo, dark: null };
|
|
71
|
+
return logo;
|
|
72
|
+
}
|
package/src/media/resolve.ts
CHANGED
|
@@ -86,3 +86,37 @@ export function rebindImageRef<T extends { imageId?: string }>(ref: T, imageId:
|
|
|
86
86
|
for (const key of BAKED_MEDIA_KEYS) delete fresh[key];
|
|
87
87
|
return fresh as T;
|
|
88
88
|
}
|
|
89
|
+
|
|
90
|
+
export interface ResolvedLogo {
|
|
91
|
+
lightUrl: string;
|
|
92
|
+
darkUrl: string | null;
|
|
93
|
+
invertInDark: boolean;
|
|
94
|
+
showInNav: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolves a SiteConfig["logo"] entry into concrete URLs. Returns null when
|
|
99
|
+
* there is no logo configured, when the light variant's imageId can't be
|
|
100
|
+
* resolved against the manifest, or when the light variant resolves to a
|
|
101
|
+
* video (an `<img>` can't render mp4/webm) — callers should fall back to
|
|
102
|
+
* rendering the site name as text rather than a broken image. Still-image and
|
|
103
|
+
* animated (gif) picks are both fine — only a video pick is rejected, since
|
|
104
|
+
* both those tags resolve to `tag: "img"`.
|
|
105
|
+
*/
|
|
106
|
+
export function resolveLogo(
|
|
107
|
+
logo: { light: { imageId: string }; dark: { imageId: string } | null; invertInDark: boolean; showInNav: boolean } | null,
|
|
108
|
+
manifest: MediaManifest,
|
|
109
|
+
provider: MediaProvider,
|
|
110
|
+
): ResolvedLogo | null {
|
|
111
|
+
if (!logo) return null;
|
|
112
|
+
const light = resolveMedia({ imageId: logo.light.imageId }, manifest, [1], provider);
|
|
113
|
+
if (!light?.src || light.tag === "video") return null;
|
|
114
|
+
const dark = logo.dark ? resolveMedia({ imageId: logo.dark.imageId }, manifest, [1], provider) : null;
|
|
115
|
+
const darkUrl = dark && dark.tag !== "video" ? dark.src : null;
|
|
116
|
+
return {
|
|
117
|
+
lightUrl: light.src,
|
|
118
|
+
darkUrl,
|
|
119
|
+
invertInDark: logo.invertInDark,
|
|
120
|
+
showInNav: logo.showInNav,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -211,6 +211,19 @@ export const SiteConfigSchema = z.object({
|
|
|
211
211
|
.refine((v) => v.startsWith("data:image/"), "Favicon must be an image data URL")
|
|
212
212
|
.nullable()
|
|
213
213
|
.default(null),
|
|
214
|
+
// Optional site logo, stored as media-library references (so it participates
|
|
215
|
+
// in usage tracking + GC protection, and its resolved URL is reusable for the
|
|
216
|
+
// Phase 3 social-preview image). Every field defaults so pre-existing
|
|
217
|
+
// site-config.json files keep parsing.
|
|
218
|
+
logo: z
|
|
219
|
+
.object({
|
|
220
|
+
light: z.object({ imageId: z.string() }),
|
|
221
|
+
dark: z.object({ imageId: z.string() }).nullable().default(null),
|
|
222
|
+
invertInDark: z.boolean().default(false),
|
|
223
|
+
showInNav: z.boolean().default(true),
|
|
224
|
+
})
|
|
225
|
+
.nullable()
|
|
226
|
+
.default(null),
|
|
214
227
|
media: MediaConfigSchema.default(MediaConfigSchema.parse({})),
|
|
215
228
|
// Brand chatbot config (editor-owned; the platform kill switch lives in the
|
|
216
229
|
// sites table, not here). Every field defaults so pre-existing
|