@driftime/sanity-plugin-handbook 0.1.0

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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +299 -0
  3. package/dist/index.cjs +1866 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +143 -0
  6. package/dist/index.d.ts +143 -0
  7. package/dist/index.js +1870 -0
  8. package/dist/index.js.map +1 -0
  9. package/package.json +92 -0
  10. package/src/blocks/callout/block.tsx +56 -0
  11. package/src/blocks/callout/content.tsx +39 -0
  12. package/src/blocks/callout/preview.tsx +15 -0
  13. package/src/blocks/code/block.tsx +18 -0
  14. package/src/blocks/code/content.tsx +18 -0
  15. package/src/blocks/code/preview.tsx +17 -0
  16. package/src/blocks/horizontal-rule/block.tsx +5 -0
  17. package/src/blocks/horizontal-rule/content.tsx +8 -0
  18. package/src/blocks/horizontal-rule/preview.tsx +5 -0
  19. package/src/blocks/image/block.tsx +23 -0
  20. package/src/blocks/image/content.tsx +31 -0
  21. package/src/blocks/image/preview.tsx +23 -0
  22. package/src/blocks/video/block.tsx +21 -0
  23. package/src/blocks/video/content.tsx +37 -0
  24. package/src/blocks/video/preview.tsx +21 -0
  25. package/src/components/content-panel.tsx +58 -0
  26. package/src/components/decorators.tsx +53 -0
  27. package/src/components/document-types.tsx +49 -0
  28. package/src/components/field.tsx +528 -0
  29. package/src/components/guide-content.tsx +117 -0
  30. package/src/components/guides.tsx +37 -0
  31. package/src/components/pane-header.tsx +26 -0
  32. package/src/components/sidebar-group.tsx +30 -0
  33. package/src/components/sidebar-tab.tsx +78 -0
  34. package/src/contexts/handbook-context.tsx +84 -0
  35. package/src/getting-started/document-types.tsx +104 -0
  36. package/src/getting-started/how-to-use.tsx +93 -0
  37. package/src/getting-started/overview.tsx +64 -0
  38. package/src/hooks/use-container-width.ts +41 -0
  39. package/src/hooks/use-handbook-configuration.ts +74 -0
  40. package/src/hooks/use-is-handbook-editor.ts +18 -0
  41. package/src/index.ts +145 -0
  42. package/src/layouts/content.tsx +13 -0
  43. package/src/layouts/sidebar.tsx +15 -0
  44. package/src/layouts/tool.tsx +205 -0
  45. package/src/lib/handbook-structure.ts +46 -0
  46. package/src/lib/icons.ts +18 -0
  47. package/src/lib/utils.ts +69 -0
  48. package/src/schemas/annotations/link.ts +19 -0
  49. package/src/schemas/blocks/callout.ts +69 -0
  50. package/src/schemas/blocks/code.ts +46 -0
  51. package/src/schemas/blocks/horizontal-rule.ts +31 -0
  52. package/src/schemas/blocks/image.ts +40 -0
  53. package/src/schemas/blocks/video.ts +34 -0
  54. package/src/schemas/guide-content.ts +58 -0
  55. package/src/schemas/types/guide.ts +48 -0
  56. package/src/schemas/types/handbook.ts +85 -0
  57. package/src/types.ts +101 -0
package/dist/index.js ADDED
@@ -0,0 +1,1870 @@
1
+ import { LightbulbIcon, InfoIcon, TriangleAlertIcon, BookOpenTextIcon, BookTextIcon, LayoutGridIcon, MessageCircleQuestionMarkIcon, CodeIcon, MinusIcon, ImageIcon, VideoIcon, ListPlusIcon } from "lucide-react";
2
+ import { useClient, useSchema, defineArrayMember, defineField, defineType, useCurrentUser, definePlugin } from "sanity";
3
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
4
+ import { forwardRef, useState, useEffect, createContext, useContext, useRef, createElement, useCallback, useMemo } from "react";
5
+ import { Card, Flex, Text, Box, Stack, Heading, useClickOutsideEvent, Popover, Code, Button } from "@sanity/ui";
6
+ const ArrowLeftIcon = /* @__PURE__ */ forwardRef(function(props, ref) {
7
+ return /* @__PURE__ */ jsxs(
8
+ "svg",
9
+ {
10
+ "data-sanity-icon": "arrow-left",
11
+ width: "1em",
12
+ height: "1em",
13
+ viewBox: "0 0 25 25",
14
+ fill: "none",
15
+ xmlns: "http://www.w3.org/2000/svg",
16
+ ...props,
17
+ ref,
18
+ children: [
19
+ /* @__PURE__ */ jsx("path", { d: "M5.5 12.5H20", stroke: "currentColor", strokeWidth: 1.2, strokeLinejoin: "round" }),
20
+ /* @__PURE__ */ jsx(
21
+ "path",
22
+ {
23
+ d: "M11 18L5.5 12.5L11 7",
24
+ stroke: "currentColor",
25
+ strokeWidth: 1.2,
26
+ strokeLinejoin: "round"
27
+ }
28
+ )
29
+ ]
30
+ }
31
+ );
32
+ }), ChevronDownIcon = /* @__PURE__ */ forwardRef(function(props, ref) {
33
+ return /* @__PURE__ */ jsx(
34
+ "svg",
35
+ {
36
+ "data-sanity-icon": "chevron-down",
37
+ width: "1em",
38
+ height: "1em",
39
+ viewBox: "0 0 25 25",
40
+ fill: "none",
41
+ xmlns: "http://www.w3.org/2000/svg",
42
+ ...props,
43
+ ref,
44
+ children: /* @__PURE__ */ jsx(
45
+ "path",
46
+ {
47
+ d: "M17 10L12.5 14.5L8 10",
48
+ stroke: "currentColor",
49
+ strokeWidth: 1.2,
50
+ strokeLinejoin: "round"
51
+ }
52
+ )
53
+ }
54
+ );
55
+ }), ChevronRightIcon = /* @__PURE__ */ forwardRef(function(props, ref) {
56
+ return /* @__PURE__ */ jsx(
57
+ "svg",
58
+ {
59
+ "data-sanity-icon": "chevron-right",
60
+ width: "1em",
61
+ height: "1em",
62
+ viewBox: "0 0 25 25",
63
+ fill: "none",
64
+ xmlns: "http://www.w3.org/2000/svg",
65
+ ...props,
66
+ ref,
67
+ children: /* @__PURE__ */ jsx(
68
+ "path",
69
+ {
70
+ d: "M10 8L14.5 12.5L10 17",
71
+ stroke: "currentColor",
72
+ strokeWidth: 1.2,
73
+ strokeLinejoin: "round"
74
+ }
75
+ )
76
+ }
77
+ );
78
+ });
79
+ function PaneHeader({ title }) {
80
+ return /* @__PURE__ */ jsx("div", { style: { position: "sticky", top: 0, zIndex: 1, lineHeight: 0 }, children: /* @__PURE__ */ jsx(Card, { tone: "inherit", children: /* @__PURE__ */ jsx(Flex, { direction: "column", padding: 3, style: { boxSizing: "border-box" }, children: /* @__PURE__ */ jsx(Flex, { align: "flex-start", children: /* @__PURE__ */ jsx(Card, { flex: 1, paddingLeft: 2, padding: 2, children: /* @__PURE__ */ jsx(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, textOverflow: "ellipsis", weight: "semibold", style: { cursor: "default", outline: "none" }, children: title }) }) }) }) }) }) });
81
+ }
82
+ const query = `*[_type == "handbook.handbook"][0]{
83
+ groups[]{
84
+ _key,
85
+ title,
86
+ guides[]{
87
+ _key,
88
+ ...@->{
89
+ _id,
90
+ _type,
91
+ title,
92
+ description,
93
+ content[]{
94
+ ...,
95
+ _type == "handbook.image" => {
96
+ ...,
97
+ "asset": { "url": asset.asset->url }
98
+ },
99
+ _type == "handbook.video" => {
100
+ ...,
101
+ "asset": { "url": asset.asset->url }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }`, listenQuery = '*[_type == "handbook.handbook" || _type == "handbook.guide"]';
108
+ function useHandbookConfiguration() {
109
+ const client = useClient({ apiVersion: "2026-01-01" }), [configuration, setConfiguration] = useState(null), [loading, setLoading] = useState(!0);
110
+ return useEffect(() => {
111
+ async function fetchConfiguration() {
112
+ const result = await client.fetch(query);
113
+ setConfiguration(result), setLoading(!1);
114
+ }
115
+ fetchConfiguration();
116
+ const subscription = client.listen(listenQuery).subscribe(() => {
117
+ fetchConfiguration();
118
+ });
119
+ return () => {
120
+ subscription.unsubscribe();
121
+ };
122
+ }, [client]), { configuration, loading };
123
+ }
124
+ function isDefined(value) {
125
+ return value == null || value === !1 ? !1 : typeof value == "string" ? value.trim() !== "" : Array.isArray(value) ? value.some((element) => isDefined(element)) : typeof value == "object" ? Object.keys(value).length > 0 : !0;
126
+ }
127
+ function isPermittedEditor(editors, email) {
128
+ return isDefined(editors) ? isDefined(email) ? editors.includes(email) : !1 : !0;
129
+ }
130
+ function convertCase(value, format) {
131
+ const words = value.replaceAll(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll(/[-_\s]+/g, " ").trim().toLowerCase();
132
+ switch (format) {
133
+ case "kebab":
134
+ return words.replaceAll(/\s+/g, "-");
135
+ case "snake":
136
+ return words.replaceAll(/\s+/g, "_");
137
+ case "camel":
138
+ return words.replaceAll(/\s+(.)/g, (_match, character) => character.toUpperCase());
139
+ case "pascal":
140
+ return words.replaceAll(/(?:^|\s+)(.)/g, (_match, character) => character.toUpperCase());
141
+ case "title":
142
+ return words.replaceAll(/\b\w/g, (character) => character.toUpperCase());
143
+ case "sentence":
144
+ return words.replaceAll(/^(.)/g, (character) => character.toUpperCase());
145
+ default:
146
+ return value;
147
+ }
148
+ }
149
+ const HandbookContext = createContext(void 0);
150
+ function HandbookProvider({
151
+ sidebarTitle,
152
+ groups,
153
+ blocks,
154
+ undocumentedFieldMessage,
155
+ children
156
+ }) {
157
+ const [activeTab, setActiveTab] = useState(null), [sidebarExpanded, setSidebarExpanded] = useState(!0), { configuration, loading } = useHandbookConfiguration();
158
+ return /* @__PURE__ */ jsx(
159
+ HandbookContext,
160
+ {
161
+ value: {
162
+ sidebarTitle,
163
+ activeTab,
164
+ sidebarExpanded,
165
+ loading,
166
+ groups,
167
+ configuration,
168
+ blocks,
169
+ undocumentedFieldMessage,
170
+ setActiveTab,
171
+ setSidebarExpanded
172
+ },
173
+ children
174
+ }
175
+ );
176
+ }
177
+ function useHandbookContext() {
178
+ const handbookContext = useContext(HandbookContext);
179
+ if (!isDefined(handbookContext)) throw new Error("useHandbookContext can only be used within <HandbookProvider>");
180
+ return handbookContext;
181
+ }
182
+ function useContainerWidth() {
183
+ const reference = useRef(null), [width, setWidth] = useState(void 0);
184
+ return useEffect(() => {
185
+ const element = reference.current;
186
+ if (!isDefined(element)) return;
187
+ const observer = new ResizeObserver((entries2) => {
188
+ for (const entry of entries2)
189
+ setWidth(entry.contentRect.width);
190
+ });
191
+ return observer.observe(element), () => {
192
+ observer.disconnect();
193
+ };
194
+ }, []), { reference, width };
195
+ }
196
+ function ContentPanel({ id, title, description, children }) {
197
+ const { activeTab } = useHandbookContext();
198
+ return activeTab !== id ? null : /* @__PURE__ */ jsxs(
199
+ "div",
200
+ {
201
+ id: `panel-${id}`,
202
+ "aria-labelledby": id,
203
+ role: "tabpanel",
204
+ style: { display: "flex", flexDirection: "column", height: "100%" },
205
+ children: [
206
+ /* @__PURE__ */ jsx(PaneHeader, { title }),
207
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, overflowY: "auto" }, children: /* @__PURE__ */ jsxs(
208
+ Box,
209
+ {
210
+ style: {
211
+ maxWidth: "640px",
212
+ boxSizing: "border-box",
213
+ marginInline: "auto",
214
+ paddingInline: "20px",
215
+ paddingBlockStart: "32px",
216
+ paddingBlockEnd: "220px"
217
+ },
218
+ children: [
219
+ /* @__PURE__ */ jsxs(Stack, { marginBottom: 6, space: 5, children: [
220
+ /* @__PURE__ */ jsx(Heading, { as: "h2", size: 4, children: title }),
221
+ isDefined(description) && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: description })
222
+ ] }),
223
+ children
224
+ ]
225
+ }
226
+ ) })
227
+ ]
228
+ }
229
+ );
230
+ }
231
+ const defaultIconProps = { width: "1em", height: "1em", strokeWidth: 1.5 };
232
+ function createSanityIcon(icon, props) {
233
+ return () => createElement(icon, { ...defaultIconProps, ...props });
234
+ }
235
+ const opaqueTypes = /* @__PURE__ */ new Set([
236
+ "block",
237
+ "boolean",
238
+ "crossDatasetReference",
239
+ "date",
240
+ "datetime",
241
+ "file",
242
+ "geopoint",
243
+ "image",
244
+ "number",
245
+ "reference",
246
+ "slug",
247
+ "string",
248
+ "text",
249
+ "url"
250
+ ]), inheritedFieldNames = {
251
+ block: /* @__PURE__ */ new Set(["children", "level", "listItem", "markDefs", "style"]),
252
+ file: /* @__PURE__ */ new Set(["asset", "media"]),
253
+ geopoint: /* @__PURE__ */ new Set(["alt", "lat", "lng"]),
254
+ image: /* @__PURE__ */ new Set(["asset", "crop", "hotspot", "media"]),
255
+ slug: /* @__PURE__ */ new Set(["current", "source"])
256
+ };
257
+ function isCompiledField(value) {
258
+ return typeof value != "object" || value === null || !("name" in value) || !("type" in value) ? !1 : typeof value.type == "object" && value.type !== null;
259
+ }
260
+ function hasFieldsArray(value) {
261
+ return typeof value != "object" || value === null || !("fields" in value) ? !1 : Array.isArray(value.fields);
262
+ }
263
+ function hasOfArray(value) {
264
+ return typeof value != "object" || value === null || !("of" in value) ? !1 : Array.isArray(value.of);
265
+ }
266
+ function getTypeName(value) {
267
+ if (!(typeof value != "object" || value === null || !("type" in value)))
268
+ return typeof value.type == "string" ? value.type : void 0;
269
+ }
270
+ function isOpaqueType(typeName) {
271
+ return opaqueTypes.has(typeName);
272
+ }
273
+ function getParentTypeName(resolvedType) {
274
+ if (!(typeof resolvedType != "object" || resolvedType === null || !("type" in resolvedType)) && !(typeof resolvedType.type != "object" || resolvedType.type === null || !("name" in resolvedType.type)))
275
+ return String(resolvedType.type.name);
276
+ }
277
+ function normaliseField(field, visited = /* @__PURE__ */ new Set()) {
278
+ if (isCompiledField(field)) {
279
+ const typeName = field.type.name, result = {
280
+ name: field.name,
281
+ type: typeName,
282
+ title: field.title ?? field.type.title,
283
+ description: field.description ?? field.type.description,
284
+ handbook: field.handbook
285
+ };
286
+ if (!visited.has(typeName)) {
287
+ const next = !opaqueTypes.has(typeName) && typeName !== "object" && typeName !== "array" ? /* @__PURE__ */ new Set([...visited, typeName]) : visited, fieldSource = hasFieldsArray(field) ? field : field.type, ofSource = hasOfArray(field) ? field : field.type;
288
+ hasFieldsArray(fieldSource) && (result.fields = fieldSource.fields.map((subfield) => normaliseField(subfield, next))), hasOfArray(ofSource) && (result.of = ofSource.of.map((member) => normaliseField(member, next)));
289
+ }
290
+ return result;
291
+ }
292
+ return field;
293
+ }
294
+ function resolveCustomFields(resolvedType, parentName) {
295
+ if (!hasFieldsArray(resolvedType)) return;
296
+ const inherited = inheritedFieldNames[parentName], customFields = resolvedType.fields.map((field) => normaliseField(field)).filter((field) => inherited === void 0 || !inherited.has(field.name));
297
+ return isDefined(customFields) ? customFields : void 0;
298
+ }
299
+ function resolveNormalisedFields(items) {
300
+ const normalised = items.map((item) => normaliseField(item));
301
+ return isDefined(normalised) ? normalised : void 0;
302
+ }
303
+ function resolveNormalisedMembers(members) {
304
+ const normalised = members.map((member) => normaliseField(member)).filter((member) => !isOpaqueType(member.type));
305
+ return isDefined(normalised) ? normalised : void 0;
306
+ }
307
+ function resolveParentName(value, schema) {
308
+ const direct = getParentTypeName(value);
309
+ if (isDefined(direct)) return direct;
310
+ const typeName = getTypeName(value);
311
+ if (!isDefined(typeName)) return;
312
+ const resolvedType = schema.get(typeName);
313
+ return isDefined(resolvedType) ? getParentTypeName(resolvedType) : void 0;
314
+ }
315
+ function resolveFields(value, schema) {
316
+ if (hasFieldsArray(value)) {
317
+ const parentName = resolveParentName(value, schema);
318
+ return isDefined(parentName) && isOpaqueType(parentName) ? resolveCustomFields(value, parentName) : resolveNormalisedFields(value.fields);
319
+ }
320
+ if (hasOfArray(value)) {
321
+ const members = value.of;
322
+ if (members.length === 1) {
323
+ const [member] = members, normalised = normaliseField(member);
324
+ if (isOpaqueType(normalised.type)) return;
325
+ if (hasFieldsArray(normalised)) {
326
+ const parentName = resolveParentName(normalised, schema);
327
+ return isDefined(parentName) && isOpaqueType(parentName) ? resolveCustomFields(normalised, parentName) : resolveNormalisedFields(normalised.fields);
328
+ }
329
+ const memberType = schema.get(normalised.type);
330
+ if (isDefined(memberType)) return resolveFields(memberType, schema);
331
+ }
332
+ return resolveNormalisedMembers(members);
333
+ }
334
+ }
335
+ function getSubfields(field, schema) {
336
+ if (isOpaqueType(field.type)) return;
337
+ const directFields = resolveFields(field, schema);
338
+ if (directFields !== void 0) return directFields;
339
+ const resolvedType = schema.get(field.type);
340
+ if (!isDefined(resolvedType)) return;
341
+ const parentName = getParentTypeName(resolvedType);
342
+ return isDefined(parentName) && isOpaqueType(parentName) ? resolveCustomFields(resolvedType, parentName) : resolveFields(resolvedType, schema);
343
+ }
344
+ function FieldHint({ label, icon, color, open, children, onToggle, onClose }) {
345
+ const buttonReference = useRef(null), popoverReference = useRef(null);
346
+ return useClickOutsideEvent(open ? onClose : void 0, () => [buttonReference.current, popoverReference.current]), /* @__PURE__ */ jsx(
347
+ Popover,
348
+ {
349
+ open,
350
+ placement: "top",
351
+ portal: !0,
352
+ radius: 2,
353
+ shadow: 2,
354
+ content: /* @__PURE__ */ jsx("div", { ref: popoverReference, children: /* @__PURE__ */ jsx(Box, { padding: 3, style: { maxWidth: "280px" }, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
355
+ /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: label }),
356
+ /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children })
357
+ ] }) }) }),
358
+ children: /* @__PURE__ */ jsx(
359
+ "button",
360
+ {
361
+ ref: buttonReference,
362
+ "aria-label": label,
363
+ type: "button",
364
+ onClick: onToggle,
365
+ onMouseEnter: onToggle,
366
+ onMouseLeave: onClose,
367
+ style: {
368
+ display: "flex",
369
+ alignItems: "center",
370
+ padding: "2px",
371
+ fontSize: "16px",
372
+ color,
373
+ background: "none",
374
+ border: "none",
375
+ cursor: "pointer"
376
+ },
377
+ children: icon
378
+ }
379
+ )
380
+ }
381
+ );
382
+ }
383
+ function FieldHints({ tip, info, caution }) {
384
+ const [openHint, setOpenHint] = useState(null), closeHint = useCallback(() => {
385
+ setOpenHint(null);
386
+ }, []);
387
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
388
+ isDefined(tip) && /* @__PURE__ */ jsx(
389
+ FieldHint,
390
+ {
391
+ label: "Tip",
392
+ icon: /* @__PURE__ */ jsx(LightbulbIcon, { ...defaultIconProps }),
393
+ color: "var(--card-icon-color)",
394
+ open: openHint === "tip",
395
+ onToggle: () => {
396
+ setOpenHint(openHint === "tip" ? null : "tip");
397
+ },
398
+ onClose: closeHint,
399
+ children: tip
400
+ }
401
+ ),
402
+ isDefined(info) && /* @__PURE__ */ jsx(
403
+ FieldHint,
404
+ {
405
+ label: "Information",
406
+ icon: /* @__PURE__ */ jsx(InfoIcon, { ...defaultIconProps }),
407
+ color: "var(--card-icon-color)",
408
+ open: openHint === "info",
409
+ onToggle: () => {
410
+ setOpenHint(openHint === "info" ? null : "info");
411
+ },
412
+ onClose: closeHint,
413
+ children: info
414
+ }
415
+ ),
416
+ isDefined(caution) && /* @__PURE__ */ jsx(
417
+ FieldHint,
418
+ {
419
+ label: "Caution",
420
+ icon: /* @__PURE__ */ jsx(TriangleAlertIcon, { ...defaultIconProps }),
421
+ color: "var(--card-icon-color)",
422
+ open: openHint === "caution",
423
+ onToggle: () => {
424
+ setOpenHint(openHint === "caution" ? null : "caution");
425
+ },
426
+ onClose: closeHint,
427
+ children: caution
428
+ }
429
+ )
430
+ ] });
431
+ }
432
+ function Field({ field, depth = 0, ancestors = /* @__PURE__ */ new Set() }) {
433
+ const schema = useSchema(), { undocumentedFieldMessage } = useHandbookContext(), [expanded, setExpanded] = useState(!1), effectiveTitle = field.handbook?.title ?? field.title ?? (field.name && convertCase(field.name, "title")), effectiveDescription = field.handbook?.description ?? field.description ?? undocumentedFieldMessage, { example, tip, info, caution } = field.handbook ?? {}, hasHints = isDefined([tip, info, caution]), isCycle = ancestors.has(field.type), subfields = isCycle ? void 0 : getSubfields(field, schema), hasSubfields = isDefined(subfields);
434
+ return /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
435
+ /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
436
+ /* @__PURE__ */ jsxs(Flex, { align: "center", paddingY: 1, gap: 2, children: [
437
+ /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
438
+ /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: effectiveTitle }),
439
+ /* @__PURE__ */ jsx(Text, { size: 0, muted: !0, style: { marginTop: "2px" }, children: field.type })
440
+ ] }) }),
441
+ hasHints && /* @__PURE__ */ jsx(FieldHints, { tip, info, caution })
442
+ ] }),
443
+ isDefined(effectiveDescription) && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: effectiveDescription }),
444
+ isDefined(example) && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: [
445
+ "e.g. ",
446
+ example
447
+ ] }) })
448
+ ] }),
449
+ isCycle && /* @__PURE__ */ jsx(Box, { marginTop: 2, children: /* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: [
450
+ "See ",
451
+ convertCase(field.type, "title"),
452
+ " above."
453
+ ] }) }),
454
+ hasSubfields && /* @__PURE__ */ jsxs(Fragment, { children: [
455
+ /* @__PURE__ */ jsx(
456
+ Card,
457
+ {
458
+ as: "button",
459
+ padding: 3,
460
+ radius: 2,
461
+ tone: "transparent",
462
+ onClick: () => {
463
+ setExpanded(!expanded);
464
+ },
465
+ style: { width: "100%", marginTop: "1rem", textAlign: "left", border: "none", cursor: "pointer" },
466
+ children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
467
+ /* @__PURE__ */ jsx(Text, { size: 0, children: expanded ? /* @__PURE__ */ jsx(ChevronDownIcon, {}) : /* @__PURE__ */ jsx(ChevronRightIcon, {}) }),
468
+ /* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, weight: "medium", children: [
469
+ subfields.length,
470
+ " subfield",
471
+ subfields.length === 1 ? "" : "s"
472
+ ] })
473
+ ] })
474
+ }
475
+ ),
476
+ expanded && /* @__PURE__ */ jsx(
477
+ "div",
478
+ {
479
+ style: {
480
+ marginTop: "16px",
481
+ marginBottom: "8px",
482
+ paddingLeft: "16px",
483
+ borderLeft: "1px solid var(--card-border-color)"
484
+ },
485
+ children: /* @__PURE__ */ jsx(Stack, { space: 5, children: subfields.map((subfield) => /* @__PURE__ */ jsx(
486
+ Field,
487
+ {
488
+ field: subfield,
489
+ depth: depth + 1,
490
+ ancestors: /* @__PURE__ */ new Set([...ancestors, field.type])
491
+ },
492
+ subfield.name
493
+ )) })
494
+ }
495
+ )
496
+ ] })
497
+ ] });
498
+ }
499
+ function SidebarGroup({ heading, children }) {
500
+ return /* @__PURE__ */ jsxs("div", { children: [
501
+ /* @__PURE__ */ jsx(Box, { paddingY: 2, children: /* @__PURE__ */ jsx(Card, { paddingLeft: 2, padding: 2, children: /* @__PURE__ */ jsx(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx(
502
+ Text,
503
+ {
504
+ size: 1,
505
+ muted: !0,
506
+ textOverflow: "ellipsis",
507
+ weight: "semibold",
508
+ style: { cursor: "default", outline: "none" },
509
+ children: heading
510
+ }
511
+ ) }) }) }),
512
+ children
513
+ ] });
514
+ }
515
+ function SidebarTab({ id, label, icon: Icon }) {
516
+ const { activeTab, setActiveTab, setSidebarExpanded } = useHandbookContext(), selected = activeTab === id;
517
+ return /* @__PURE__ */ jsx(
518
+ Card,
519
+ {
520
+ id,
521
+ "aria-controls": `panel-${id}`,
522
+ "aria-selected": selected,
523
+ role: "option",
524
+ as: "button",
525
+ selected,
526
+ marginBottom: 1,
527
+ radius: 2,
528
+ sizing: "border",
529
+ tabIndex: -1,
530
+ tone: "inherit",
531
+ onClick: () => {
532
+ setActiveTab(id), setSidebarExpanded(!1);
533
+ },
534
+ style: { width: "100%", textAlign: "left", border: "none", cursor: "pointer" },
535
+ children: /* @__PURE__ */ jsx(
536
+ Flex,
537
+ {
538
+ align: "center",
539
+ paddingLeft: isDefined(Icon) ? 1 : 2,
540
+ paddingRight: 2,
541
+ paddingY: 1,
542
+ style: { height: "1.5625rem", boxSizing: "content-box" },
543
+ children: /* @__PURE__ */ jsxs(Flex, { align: "center", flex: 1, gap: 2, children: [
544
+ isDefined(Icon) && /* @__PURE__ */ jsx(
545
+ "div",
546
+ {
547
+ style: {
548
+ display: "flex",
549
+ alignItems: "center",
550
+ justifyContent: "center",
551
+ width: "25px",
552
+ height: "25px",
553
+ color: "var(--card-icon-color)"
554
+ },
555
+ children: /* @__PURE__ */ jsx(Icon, {})
556
+ }
557
+ ),
558
+ /* @__PURE__ */ jsx(Stack, { flex: 1, space: 2, children: /* @__PURE__ */ jsx(Text, { size: 1, textOverflow: "ellipsis", weight: "medium", style: { color: "inherit" }, children: label }) }),
559
+ /* @__PURE__ */ jsx(Box, { paddingLeft: 4, paddingRight: 1, children: /* @__PURE__ */ jsx(Box, { style: { opacity: 0.5 }, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) }) }) })
560
+ ] })
561
+ }
562
+ )
563
+ }
564
+ );
565
+ }
566
+ function DocumentTypesTabs() {
567
+ const { groups } = useHandbookContext();
568
+ return groups.map((group) => {
569
+ const { title, documents } = group;
570
+ return /* @__PURE__ */ jsx(SidebarGroup, { heading: title, children: documents.map((document) => {
571
+ const effectiveTitle = document.handbook?.title ?? document.title ?? convertCase(document.name, "title"), icon = typeof document.icon == "function" ? document.icon : void 0;
572
+ return /* @__PURE__ */ jsx(SidebarTab, { id: document.name, label: effectiveTitle, icon }, document.name);
573
+ }) }, title);
574
+ });
575
+ }
576
+ function DocumentTypesPanels() {
577
+ const { groups } = useHandbookContext();
578
+ return groups.map((group) => {
579
+ const { documents } = group;
580
+ return documents.map((document) => {
581
+ const effectiveTitle = document.handbook?.title ?? document.title ?? convertCase(document.name, "title"), effectiveDescription = document.handbook?.description ?? (typeof document.description == "string" ? document.description : void 0);
582
+ return /* @__PURE__ */ jsx(ContentPanel, { id: document.name, title: effectiveTitle, description: effectiveDescription, children: /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "3.25rem" }, children: document.fields.map((field) => /* @__PURE__ */ jsx(Field, { field }, field.name)) }) }, document.name);
583
+ });
584
+ });
585
+ }
586
+ function isPortableTextSpan(node) {
587
+ return node._type === "span" && "text" in node && typeof node.text == "string" && (node.marks === void 0 || Array.isArray(node.marks) && node.marks.every((mark) => typeof mark == "string"));
588
+ }
589
+ function isPortableTextBlock(node) {
590
+ return typeof node._type == "string" && node._type[0] !== "@" && (!("markDefs" in node) || !node.markDefs || Array.isArray(node.markDefs) && node.markDefs.every((def) => typeof def._key == "string")) && "children" in node && Array.isArray(node.children) && node.children.every((child) => typeof child == "object" && "_type" in child);
591
+ }
592
+ function isPortableTextListItemBlock(block) {
593
+ return isPortableTextBlock(block) && "listItem" in block && typeof block.listItem == "string" && (block.level === void 0 || typeof block.level == "number");
594
+ }
595
+ function isPortableTextToolkitList(block) {
596
+ return block._type === "@list";
597
+ }
598
+ function isPortableTextToolkitSpan(span) {
599
+ return span._type === "@span";
600
+ }
601
+ function isPortableTextToolkitTextNode(node) {
602
+ return node._type === "@text";
603
+ }
604
+ const knownDecorators = [
605
+ "strong",
606
+ "em",
607
+ "code",
608
+ "underline",
609
+ "strike-through"
610
+ ];
611
+ function sortMarksByOccurences(span, index, blockChildren) {
612
+ if (!isPortableTextSpan(span) || !span.marks || !span.marks.length) return [];
613
+ let marks = span.marks.slice(), occurences = {};
614
+ return marks.forEach((mark) => {
615
+ occurences[mark] = 1;
616
+ for (let siblingIndex = index + 1; siblingIndex < blockChildren.length; siblingIndex++) {
617
+ let sibling = blockChildren[siblingIndex];
618
+ if (sibling && isPortableTextSpan(sibling) && Array.isArray(sibling.marks) && sibling.marks.indexOf(mark) !== -1) occurences[mark]++;
619
+ else break;
620
+ }
621
+ }), marks.sort((markA, markB) => sortMarks(occurences, markA, markB));
622
+ }
623
+ function sortMarks(occurences, markA, markB) {
624
+ let aOccurences = occurences[markA], bOccurences = occurences[markB];
625
+ if (aOccurences !== bOccurences) return bOccurences - aOccurences;
626
+ let aKnownPos = knownDecorators.indexOf(markA), bKnownPos = knownDecorators.indexOf(markB);
627
+ return aKnownPos === bKnownPos ? markA.localeCompare(markB) : aKnownPos - bKnownPos;
628
+ }
629
+ function buildMarksTree(block) {
630
+ let { children } = block, markDefs = block.markDefs ?? [];
631
+ if (!children || !children.length) return [];
632
+ let sortedMarks = children.map(sortMarksByOccurences), rootNode = {
633
+ _type: "@span",
634
+ children: [],
635
+ markType: "<unknown>"
636
+ }, nodeStack = [rootNode];
637
+ for (let i = 0; i < children.length; i++) {
638
+ let span = children[i];
639
+ if (!span) continue;
640
+ let marksNeeded = sortedMarks[i] || [], pos = 1;
641
+ if (nodeStack.length > 1) for (; pos < nodeStack.length; pos++) {
642
+ let mark = nodeStack[pos]?.markKey || "", index = marksNeeded.indexOf(mark);
643
+ if (index === -1) break;
644
+ marksNeeded.splice(index, 1);
645
+ }
646
+ nodeStack = nodeStack.slice(0, pos);
647
+ let currentNode = nodeStack[nodeStack.length - 1];
648
+ if (currentNode) {
649
+ for (let markKey of marksNeeded) {
650
+ let markDef = markDefs?.find((def) => def._key === markKey), node = {
651
+ _type: "@span",
652
+ _key: span._key,
653
+ children: [],
654
+ markDef,
655
+ markType: markDef ? markDef._type : markKey,
656
+ markKey
657
+ };
658
+ currentNode.children.push(node), nodeStack.push(node), currentNode = node;
659
+ }
660
+ if (isPortableTextSpan(span)) {
661
+ let lines = span.text.split(`
662
+ `);
663
+ for (let line = lines.length; line-- > 1; ) lines.splice(line, 0, `
664
+ `);
665
+ currentNode.children = currentNode.children.concat(lines.map((text) => ({
666
+ _type: "@text",
667
+ text
668
+ })));
669
+ } else currentNode.children = currentNode.children.concat(span);
670
+ }
671
+ }
672
+ return rootNode.children;
673
+ }
674
+ function nestLists(blocks, mode) {
675
+ let tree = [], currentList;
676
+ for (let i = 0; i < blocks.length; i++) {
677
+ let block = blocks[i];
678
+ if (block) {
679
+ if (!isPortableTextListItemBlock(block)) {
680
+ tree.push(block), currentList = void 0;
681
+ continue;
682
+ }
683
+ if (!currentList) {
684
+ currentList = listFromBlock(block, i, mode), tree.push(currentList);
685
+ continue;
686
+ }
687
+ if (blockMatchesList(block, currentList)) {
688
+ currentList.children.push(block);
689
+ continue;
690
+ }
691
+ if ((block.level || 1) > currentList.level) {
692
+ let newList = listFromBlock(block, i, mode);
693
+ if (mode === "html") {
694
+ let lastListItem = currentList.children[currentList.children.length - 1], newLastChild = {
695
+ ...lastListItem,
696
+ children: [...lastListItem.children, newList]
697
+ };
698
+ currentList.children[currentList.children.length - 1] = newLastChild;
699
+ } else currentList.children.push(newList);
700
+ currentList = newList;
701
+ continue;
702
+ }
703
+ if ((block.level || 1) < currentList.level) {
704
+ let matchingBranch = tree[tree.length - 1], match = matchingBranch && findListMatching(matchingBranch, block);
705
+ if (match) {
706
+ currentList = match, currentList.children.push(block);
707
+ continue;
708
+ }
709
+ currentList = listFromBlock(block, i, mode), tree.push(currentList);
710
+ continue;
711
+ }
712
+ if (block.listItem !== currentList.listItem) {
713
+ let matchingBranch = tree[tree.length - 1], match = matchingBranch && findListMatching(matchingBranch, { level: block.level || 1 });
714
+ if (match && match.listItem === block.listItem) {
715
+ currentList = match, currentList.children.push(block);
716
+ continue;
717
+ } else {
718
+ currentList = listFromBlock(block, i, mode), tree.push(currentList);
719
+ continue;
720
+ }
721
+ }
722
+ console.warn("Unknown state encountered for block", block), tree.push(block);
723
+ }
724
+ }
725
+ return tree;
726
+ }
727
+ function blockMatchesList(block, list) {
728
+ return (block.level || 1) === list.level && block.listItem === list.listItem;
729
+ }
730
+ function listFromBlock(block, index, mode) {
731
+ return {
732
+ _type: "@list",
733
+ _key: `${block._key || `${index}`}-parent`,
734
+ mode,
735
+ level: block.level || 1,
736
+ listItem: block.listItem,
737
+ children: [block]
738
+ };
739
+ }
740
+ function findListMatching(rootNode, matching) {
741
+ let level = matching.level || 1, style = matching.listItem || "normal", filterOnType = typeof matching.listItem == "string";
742
+ if (isPortableTextToolkitList(rootNode) && (rootNode.level || 1) === level && filterOnType && (rootNode.listItem || "normal") === style) return rootNode;
743
+ if (!("children" in rootNode)) return;
744
+ let node = rootNode.children[rootNode.children.length - 1];
745
+ return node && !isPortableTextSpan(node) ? findListMatching(node, matching) : void 0;
746
+ }
747
+ function spanToPlainText(span) {
748
+ let text = "";
749
+ return span.children.forEach((current) => {
750
+ isPortableTextToolkitTextNode(current) ? text += current.text : isPortableTextToolkitSpan(current) && (text += spanToPlainText(current));
751
+ }), text;
752
+ }
753
+ const LIST_NEST_MODE_HTML = "html", defaultLists = {
754
+ number: ({ children }) => /* @__PURE__ */ jsx("ol", { children }),
755
+ bullet: ({ children }) => /* @__PURE__ */ jsx("ul", { children })
756
+ }, DefaultListItem = ({ children }) => /* @__PURE__ */ jsx("li", { children }), link = ({ children, value }) => /* @__PURE__ */ jsx("a", {
757
+ href: value?.href,
758
+ children
759
+ }), underlineStyle = { textDecoration: "underline" }, defaultMarks = {
760
+ em: ({ children }) => /* @__PURE__ */ jsx("em", { children }),
761
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", { children }),
762
+ code: ({ children }) => /* @__PURE__ */ jsx("code", { children }),
763
+ underline: ({ children }) => /* @__PURE__ */ jsx("span", {
764
+ style: underlineStyle,
765
+ children
766
+ }),
767
+ "strike-through": ({ children }) => /* @__PURE__ */ jsx("del", { children }),
768
+ link
769
+ }, getTemplate = (type, prop) => `[@portabletext/react] Unknown ${type}, specify a component for it in the \`components.${prop}\` prop`, unknownTypeWarning = (typeName) => getTemplate(`block type "${typeName}"`, "types"), unknownMarkWarning = (markType) => getTemplate(`mark type "${markType}"`, "marks"), unknownBlockStyleWarning = (blockStyle) => getTemplate(`block style "${blockStyle}"`, "block"), unknownListStyleWarning = (listStyle) => getTemplate(`list style "${listStyle}"`, "list"), unknownListItemStyleWarning = (listStyle) => getTemplate(`list item style "${listStyle}"`, "listItem");
770
+ function printWarning(message) {
771
+ console.warn(message);
772
+ }
773
+ const hidden = { display: "none" }, defaultComponents = {
774
+ types: {},
775
+ block: {
776
+ normal: ({ children }) => /* @__PURE__ */ jsx("p", { children }),
777
+ blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { children }),
778
+ h1: ({ children }) => /* @__PURE__ */ jsx("h1", { children }),
779
+ h2: ({ children }) => /* @__PURE__ */ jsx("h2", { children }),
780
+ h3: ({ children }) => /* @__PURE__ */ jsx("h3", { children }),
781
+ h4: ({ children }) => /* @__PURE__ */ jsx("h4", { children }),
782
+ h5: ({ children }) => /* @__PURE__ */ jsx("h5", { children }),
783
+ h6: ({ children }) => /* @__PURE__ */ jsx("h6", { children })
784
+ },
785
+ marks: defaultMarks,
786
+ list: defaultLists,
787
+ listItem: DefaultListItem,
788
+ hardBreak: () => /* @__PURE__ */ jsx("br", {}),
789
+ unknownType: ({ value, isInline }) => {
790
+ let warning = unknownTypeWarning(value._type);
791
+ return jsx(isInline ? "span" : "div", {
792
+ style: hidden,
793
+ children: warning
794
+ });
795
+ },
796
+ unknownMark: ({ markType, children }) => /* @__PURE__ */ jsx("span", {
797
+ className: `unknown__pt__mark__${markType}`,
798
+ children
799
+ }),
800
+ unknownList: ({ children }) => /* @__PURE__ */ jsx("ul", { children }),
801
+ unknownListItem: ({ children }) => /* @__PURE__ */ jsx("li", { children }),
802
+ unknownBlockStyle: ({ children }) => /* @__PURE__ */ jsx("p", { children })
803
+ };
804
+ function mergeComponents(parent, overrides) {
805
+ let { block: _block, list: _list, listItem: _listItem, marks: _marks, types: _types, ...rest } = overrides;
806
+ return {
807
+ ...parent,
808
+ block: mergeDeeply(parent, overrides, "block"),
809
+ list: mergeDeeply(parent, overrides, "list"),
810
+ listItem: mergeDeeply(parent, overrides, "listItem"),
811
+ marks: mergeDeeply(parent, overrides, "marks"),
812
+ types: mergeDeeply(parent, overrides, "types"),
813
+ ...rest
814
+ };
815
+ }
816
+ function mergeDeeply(parent, overrides, key) {
817
+ let override = overrides[key], parentVal = parent[key];
818
+ return typeof override == "function" || override && typeof parentVal == "function" ? override : override ? {
819
+ ...parentVal,
820
+ ...override
821
+ } : parentVal;
822
+ }
823
+ function PortableText({ value: input, components: componentOverrides, listNestingMode, onMissingComponent: missingComponentHandler = printWarning }) {
824
+ let handleMissingComponent = missingComponentHandler || noop, nested = nestLists(Array.isArray(input) ? input : [input], listNestingMode || LIST_NEST_MODE_HTML), components = useMemo(() => componentOverrides ? mergeComponents(defaultComponents, componentOverrides) : defaultComponents, [componentOverrides]), renderNode = useMemo(() => getNodeRenderer(components, handleMissingComponent), [components, handleMissingComponent]);
825
+ return /* @__PURE__ */ jsx(Fragment, { children: nested.map((node, index) => renderNode({
826
+ node,
827
+ index,
828
+ isInline: !1,
829
+ renderNode
830
+ })) });
831
+ }
832
+ const getNodeRenderer = (components, handleMissingComponent) => {
833
+ function renderNode(options) {
834
+ let { node, index, isInline } = options, key = node._key || `node-${index}`;
835
+ return isPortableTextToolkitList(node) ? renderList(node, index, key) : isPortableTextListItemBlock(node) ? renderListItem(node, index, key) : isPortableTextToolkitSpan(node) ? renderSpan(node, index, key) : hasCustomComponentForNode(node) ? renderCustomBlock(node, index, key, isInline) : isPortableTextBlock(node) ? renderBlock(node, index, key, isInline) : isPortableTextToolkitTextNode(node) ? renderText(node, key) : renderUnknownType(node, index, key, isInline);
836
+ }
837
+ function hasCustomComponentForNode(node) {
838
+ return node._type in components.types;
839
+ }
840
+ function renderListItem(node, index, key) {
841
+ let tree = serializeBlock({
842
+ node,
843
+ index,
844
+ isInline: !1,
845
+ renderNode
846
+ }), renderer = components.listItem, Li = (typeof renderer == "function" ? renderer : renderer[node.listItem]) || components.unknownListItem;
847
+ if (Li === components.unknownListItem) {
848
+ let style = node.listItem || "bullet";
849
+ handleMissingComponent(unknownListItemStyleWarning(style), {
850
+ type: style,
851
+ nodeType: "listItemStyle"
852
+ });
853
+ }
854
+ let children = tree.children;
855
+ if (node.style && node.style !== "normal") {
856
+ let { listItem: _listItem, ...blockNode } = node;
857
+ children = renderNode({
858
+ node: blockNode,
859
+ index,
860
+ isInline: !1
861
+ });
862
+ }
863
+ return /* @__PURE__ */ jsx(Li, {
864
+ value: node,
865
+ index,
866
+ isInline: !1,
867
+ renderNode,
868
+ children
869
+ }, key);
870
+ }
871
+ function renderList(node, index, key) {
872
+ let children = node.children.map((child, childIndex) => renderNode({
873
+ node: child._key ? child : {
874
+ ...child,
875
+ _key: `li-${index}-${childIndex}`
876
+ },
877
+ index: childIndex,
878
+ isInline: !1
879
+ })), component = components.list, List = (typeof component == "function" ? component : component[node.listItem]) || components.unknownList;
880
+ if (List === components.unknownList) {
881
+ let style = node.listItem || "bullet";
882
+ handleMissingComponent(unknownListStyleWarning(style), {
883
+ nodeType: "listStyle",
884
+ type: style
885
+ });
886
+ }
887
+ return /* @__PURE__ */ jsx(List, {
888
+ value: node,
889
+ index,
890
+ isInline: !1,
891
+ renderNode,
892
+ children
893
+ }, key);
894
+ }
895
+ function renderSpan(node, _index, key) {
896
+ let { markDef, markType, markKey } = node, Span = components.marks[markType] || components.unknownMark, children = node.children.map((child, childIndex) => renderNode({
897
+ node: child,
898
+ index: childIndex,
899
+ isInline: !0
900
+ }));
901
+ return Span === components.unknownMark && handleMissingComponent(unknownMarkWarning(markType), {
902
+ nodeType: "mark",
903
+ type: markType
904
+ }), /* @__PURE__ */ jsx(Span, {
905
+ text: spanToPlainText(node),
906
+ value: markDef,
907
+ markType,
908
+ markKey,
909
+ renderNode,
910
+ children
911
+ }, key);
912
+ }
913
+ function renderBlock(node, index, key, isInline) {
914
+ let { _key, ...props } = serializeBlock({
915
+ node,
916
+ index,
917
+ isInline,
918
+ renderNode
919
+ }), style = props.node.style || "normal", Block = (typeof components.block == "function" ? components.block : components.block[style]) || components.unknownBlockStyle;
920
+ return Block === components.unknownBlockStyle && handleMissingComponent(unknownBlockStyleWarning(style), {
921
+ nodeType: "blockStyle",
922
+ type: style
923
+ }), /* @__PURE__ */ jsx(Block, {
924
+ ...props,
925
+ value: props.node,
926
+ renderNode
927
+ }, key);
928
+ }
929
+ function renderText(node, key) {
930
+ if (node.text === `
931
+ `) {
932
+ let HardBreak = components.hardBreak;
933
+ return HardBreak ? /* @__PURE__ */ jsx(HardBreak, {}, key) : `
934
+ `;
935
+ }
936
+ return node.text;
937
+ }
938
+ function renderUnknownType(node, index, key, isInline) {
939
+ let nodeOptions = {
940
+ value: node,
941
+ isInline,
942
+ index,
943
+ renderNode
944
+ };
945
+ handleMissingComponent(unknownTypeWarning(node._type), {
946
+ nodeType: "block",
947
+ type: node._type
948
+ });
949
+ let UnknownType = components.unknownType;
950
+ return /* @__PURE__ */ jsx(UnknownType, { ...nodeOptions }, key);
951
+ }
952
+ function renderCustomBlock(node, index, key, isInline) {
953
+ let nodeOptions = {
954
+ value: node,
955
+ isInline,
956
+ index,
957
+ renderNode
958
+ }, Node = components.types[node._type];
959
+ return Node ? /* @__PURE__ */ jsx(Node, { ...nodeOptions }, key) : null;
960
+ }
961
+ return renderNode;
962
+ };
963
+ function serializeBlock(options) {
964
+ let { node, index, isInline, renderNode } = options, children = buildMarksTree(node).map((child, i) => renderNode({
965
+ node: child,
966
+ isInline: !0,
967
+ index: i,
968
+ renderNode
969
+ }));
970
+ return {
971
+ _key: node._key || `block-${index}`,
972
+ children,
973
+ index,
974
+ isInline,
975
+ node
976
+ };
977
+ }
978
+ function noop() {
979
+ }
980
+ const variantConfig = {
981
+ tip: { icon: LightbulbIcon, tone: "positive" },
982
+ info: { icon: InfoIcon, tone: "primary" },
983
+ warning: { icon: TriangleAlertIcon, tone: "caution" }
984
+ };
985
+ function CalloutContent({ variant, children }) {
986
+ const config = variantConfig[variant], Icon = config.icon;
987
+ return /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, tone: config.tone, children: /* @__PURE__ */ jsxs(Flex, { align: "flex-start", gap: 3, children: [
988
+ /* @__PURE__ */ jsx("div", { style: { flexShrink: 0, marginTop: "-2px", fontSize: "16px", color: "var(--card-icon-color)" }, children: /* @__PURE__ */ jsx(Icon, { ...defaultIconProps }) }),
989
+ /* @__PURE__ */ jsx(Text, { size: 1, style: { color: "var(--card-icon-color)" }, children })
990
+ ] }) });
991
+ }
992
+ function CalloutBlock({ value }) {
993
+ return /* @__PURE__ */ jsx("div", { style: { marginBlock: "2rem" }, children: /* @__PURE__ */ jsx(CalloutContent, { variant: value.variant, children: /* @__PURE__ */ jsx(
994
+ PortableText,
995
+ {
996
+ value: value.body,
997
+ components: {
998
+ block: { normal: ({ children }) => /* @__PURE__ */ jsx(Fragment, { children }) },
999
+ marks: {
1000
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", { children }),
1001
+ em: ({ children }) => /* @__PURE__ */ jsx("em", { children }),
1002
+ code: ({ children }) => /* @__PURE__ */ jsx(
1003
+ "code",
1004
+ {
1005
+ style: {
1006
+ padding: "0.125rem 0.25rem",
1007
+ fontSize: "0.875em",
1008
+ backgroundColor: "var(--card-code-bg-color)",
1009
+ borderRadius: "0.125rem"
1010
+ },
1011
+ children
1012
+ }
1013
+ ),
1014
+ link: ({ children, value: mark }) => /* @__PURE__ */ jsx(
1015
+ "a",
1016
+ {
1017
+ href: mark?.href,
1018
+ target: "_blank",
1019
+ rel: "noopener noreferrer",
1020
+ style: { color: "var(--card-fg-color)" },
1021
+ children
1022
+ }
1023
+ )
1024
+ }
1025
+ }
1026
+ }
1027
+ ) }) });
1028
+ }
1029
+ function CodeContent({ code, language }) {
1030
+ return /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, tone: "transparent", style: { overflowX: "auto" }, children: /* @__PURE__ */ jsx(Code, { size: 1, language, children: code }) });
1031
+ }
1032
+ function CodeBlock({ value }) {
1033
+ return /* @__PURE__ */ jsx("div", { style: { marginBlock: "1.5rem" }, children: /* @__PURE__ */ jsx(CodeContent, { code: value.code, language: value.language }) });
1034
+ }
1035
+ function HorizontalRuleContent({ marginBlock = "0" }) {
1036
+ return /* @__PURE__ */ jsx("hr", { style: { marginBlock, border: "none", borderTop: "1px solid var(--card-border-color)" } });
1037
+ }
1038
+ function HorizontalRuleBlock() {
1039
+ return /* @__PURE__ */ jsx(HorizontalRuleContent, { marginBlock: "2.5rem" });
1040
+ }
1041
+ function ImageContent({ url, alt, caption }) {
1042
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1043
+ /* @__PURE__ */ jsx(
1044
+ "img",
1045
+ {
1046
+ src: url,
1047
+ alt: alt ?? "",
1048
+ style: { display: "block", maxWidth: "100%", borderRadius: "0.25rem", overflow: "hidden" }
1049
+ }
1050
+ ),
1051
+ isDefined(caption) && /* @__PURE__ */ jsx("div", { style: { marginBlockStart: "0.75rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: caption }) })
1052
+ ] });
1053
+ }
1054
+ function ImageBlock({ value }) {
1055
+ return isDefined(value.asset?.url) ? /* @__PURE__ */ jsx("div", { style: { marginBlock: "1.5rem" }, children: /* @__PURE__ */ jsx(ImageContent, { url: value.asset.url, alt: value.alt, caption: value.caption }) }) : null;
1056
+ }
1057
+ function VideoContent({ url, caption }) {
1058
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1059
+ /* @__PURE__ */ jsx(
1060
+ "video",
1061
+ {
1062
+ src: url,
1063
+ controls: !0,
1064
+ style: {
1065
+ display: "block",
1066
+ maxWidth: "100%",
1067
+ borderRadius: "0.25rem",
1068
+ overflow: "hidden",
1069
+ WebkitTransform: "translateZ(0)"
1070
+ },
1071
+ children: /* @__PURE__ */ jsx("track", { kind: "captions", label: caption ?? "Video" })
1072
+ }
1073
+ ),
1074
+ isDefined(caption) && /* @__PURE__ */ jsx("div", { style: { marginBlockStart: "0.75rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: caption }) })
1075
+ ] });
1076
+ }
1077
+ function VideoBlock({ value }) {
1078
+ return isDefined(value.asset?.url) ? /* @__PURE__ */ jsx("div", { style: { marginBlock: "1.5rem" }, children: /* @__PURE__ */ jsx(VideoContent, { url: value.asset.url, caption: value.caption }) }) : null;
1079
+ }
1080
+ const portableTextComponents = {
1081
+ block: {
1082
+ h2: ({ children }) => /* @__PURE__ */ jsx("div", { style: { marginBlockStart: "3rem", marginBlockEnd: "0.25rem" }, children: /* @__PURE__ */ jsx(Text, { as: "h2", size: 1, weight: "medium", children }) }),
1083
+ normal: ({ children }) => /* @__PURE__ */ jsx("div", { style: { marginBlock: "1rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children }) }),
1084
+ blockquote: ({ children }) => /* @__PURE__ */ jsx("div", { style: { marginBlock: "2rem" }, children: /* @__PURE__ */ jsx(Box, { paddingLeft: 3, paddingY: 1, style: { borderLeft: "2px solid var(--card-border-color)" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children }) }) })
1085
+ },
1086
+ list: {
1087
+ bullet: ({ children, value }) => /* @__PURE__ */ jsx("div", { style: { marginBlock: value.level > 1 ? "0.75rem" : "2rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: /* @__PURE__ */ jsx("ul", { style: { display: "flex", flexDirection: "column", gap: "0.375rem", margin: 0, paddingLeft: "1.25em" }, children }) }) }),
1088
+ number: ({ children, value }) => /* @__PURE__ */ jsx("div", { style: { marginBlock: value.level > 1 ? "0.75rem" : "2rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: /* @__PURE__ */ jsx("ol", { style: { display: "flex", flexDirection: "column", gap: "0.375rem", margin: 0, paddingLeft: "1.25em" }, children }) }) })
1089
+ },
1090
+ listItem: {
1091
+ bullet: ({ children }) => /* @__PURE__ */ jsx("li", { children }),
1092
+ number: ({ children }) => /* @__PURE__ */ jsx("li", { children })
1093
+ },
1094
+ marks: {
1095
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", { children }),
1096
+ em: ({ children }) => /* @__PURE__ */ jsx("em", { children }),
1097
+ code: ({ children }) => /* @__PURE__ */ jsx(
1098
+ "code",
1099
+ {
1100
+ style: {
1101
+ padding: "0.125rem 0.25rem",
1102
+ fontSize: "0.875em",
1103
+ backgroundColor: "var(--card-code-bg-color)",
1104
+ borderRadius: "0.125rem"
1105
+ },
1106
+ children
1107
+ }
1108
+ ),
1109
+ link: ({ children, value }) => /* @__PURE__ */ jsx("a", { href: value?.href, target: "_blank", rel: "noopener noreferrer", style: { color: "var(--card-link-fg-color)" }, children }),
1110
+ "strike-through": ({ children }) => /* @__PURE__ */ jsx("del", { children })
1111
+ }
1112
+ };
1113
+ function GuideContent({ content }) {
1114
+ const { blocks: customBlocks } = useHandbookContext(), components = useMemo(() => {
1115
+ const customTypes = {};
1116
+ for (const block of customBlocks)
1117
+ customTypes[block.schema.name] = block.component;
1118
+ return {
1119
+ ...portableTextComponents,
1120
+ types: {
1121
+ "handbook.image": ImageBlock,
1122
+ "handbook.video": VideoBlock,
1123
+ "handbook.code": CodeBlock,
1124
+ "handbook.callout": CalloutBlock,
1125
+ "handbook.horizontalRule": HorizontalRuleBlock,
1126
+ ...customTypes
1127
+ }
1128
+ };
1129
+ }, [customBlocks]);
1130
+ return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(PortableText, { value: content, components }) });
1131
+ }
1132
+ function GuidesTabs() {
1133
+ const { configuration, loading } = useHandbookContext();
1134
+ return loading || !isDefined(configuration) ? null : configuration.groups.map((group) => /* @__PURE__ */ jsx(SidebarGroup, { heading: group.title, children: group.guides.map((guide) => /* @__PURE__ */ jsx(SidebarTab, { id: guide._key, label: guide.title, icon: createSanityIcon(BookOpenTextIcon) }, guide._key)) }, group._key));
1135
+ }
1136
+ function GuidesPanels() {
1137
+ const { configuration, loading } = useHandbookContext();
1138
+ return loading || !isDefined(configuration) ? null : configuration.groups.flatMap(
1139
+ (group) => group.guides.map((guide) => /* @__PURE__ */ jsx(ContentPanel, { id: guide._key, title: guide.title, description: guide.description, children: /* @__PURE__ */ jsx(GuideContent, { content: guide.content }) }, guide._key))
1140
+ );
1141
+ }
1142
+ function DocumentTypesOverview() {
1143
+ const { groups, setActiveTab, setSidebarExpanded } = useHandbookContext();
1144
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "2.5rem" }, children: groups.map((group) => {
1145
+ const { title, description, documents } = group;
1146
+ return /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
1147
+ /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
1148
+ /* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: title }) }),
1149
+ isDefined(description) && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: description })
1150
+ ] }),
1151
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Stack, { space: 2, children: documents.map((document) => {
1152
+ const Icon = typeof document.icon == "function" ? document.icon : void 0, documentDescription = document.handbook?.description ?? (typeof document.description == "string" ? document.description : void 0);
1153
+ return /* @__PURE__ */ jsx(
1154
+ Card,
1155
+ {
1156
+ as: "button",
1157
+ padding: 3,
1158
+ radius: 2,
1159
+ sizing: "border",
1160
+ tone: "inherit",
1161
+ onClick: () => {
1162
+ setActiveTab(document.name), setSidebarExpanded(!1);
1163
+ },
1164
+ style: {
1165
+ width: "calc(100% + 24px)",
1166
+ marginLeft: "-12px",
1167
+ textAlign: "left",
1168
+ border: "none",
1169
+ cursor: "pointer"
1170
+ },
1171
+ children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
1172
+ /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
1173
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, style: { marginLeft: "-4px" }, children: [
1174
+ isDefined(Icon) && /* @__PURE__ */ jsx(
1175
+ "div",
1176
+ {
1177
+ style: {
1178
+ display: "flex",
1179
+ alignItems: "center",
1180
+ justifyContent: "center",
1181
+ width: "25px",
1182
+ height: "25px",
1183
+ color: "var(--card-icon-color)"
1184
+ },
1185
+ children: /* @__PURE__ */ jsx(Icon, {})
1186
+ }
1187
+ ),
1188
+ /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: document.title ?? document.name })
1189
+ ] }),
1190
+ isDefined(documentDescription) && /* @__PURE__ */ jsx(Box, { paddingBottom: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: documentDescription }) })
1191
+ ] }) }),
1192
+ /* @__PURE__ */ jsx(Box, { flex: "none", style: { opacity: 0.5 }, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) }) })
1193
+ ] })
1194
+ },
1195
+ document.name
1196
+ );
1197
+ }) }) })
1198
+ ] }, title);
1199
+ }) });
1200
+ }
1201
+ function HintRow({ icon, children }) {
1202
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
1203
+ /* @__PURE__ */ jsx(
1204
+ "div",
1205
+ {
1206
+ style: {
1207
+ display: "flex",
1208
+ alignItems: "center",
1209
+ flexShrink: 0,
1210
+ fontSize: "16px",
1211
+ color: "var(--card-icon-color)"
1212
+ },
1213
+ children: icon
1214
+ }
1215
+ ),
1216
+ /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children })
1217
+ ] });
1218
+ }
1219
+ function HowToUse() {
1220
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "3rem" }, children: [
1221
+ /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
1222
+ /* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Navigating the Handbook" }) }),
1223
+ /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Use the sidebar to browse document types and guides. Each entry opens a detailed view in the main panel showing field descriptions, examples, and tips." })
1224
+ ] }),
1225
+ /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
1226
+ /* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Understanding Fields" }) }),
1227
+ /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Each document type lists its fields with a name and type label. Below the name you will find a description explaining what the field is for and how to use it. Some fields include an example value shown in italics." }),
1228
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: "e.g. Acme Inc." }) })
1229
+ ] }),
1230
+ /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
1231
+ /* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Hints and Warnings" }) }),
1232
+ /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Look for these icons next to field names. Hover or click them to reveal additional guidance." }),
1233
+ /* @__PURE__ */ jsxs(Stack, { space: 3, marginTop: 2, children: [
1234
+ /* @__PURE__ */ jsx(HintRow, { icon: /* @__PURE__ */ jsx(LightbulbIcon, { ...defaultIconProps }), children: "Helpful tips and best practices for content editors." }),
1235
+ /* @__PURE__ */ jsx(HintRow, { icon: /* @__PURE__ */ jsx(InfoIcon, { ...defaultIconProps }), children: "Additional context about how the field is used." }),
1236
+ /* @__PURE__ */ jsx(HintRow, { icon: /* @__PURE__ */ jsx(TriangleAlertIcon, { ...defaultIconProps }), children: "Important warnings about constraints or potential issues." })
1237
+ ] })
1238
+ ] }),
1239
+ /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
1240
+ /* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Nested Fields" }) }),
1241
+ /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Some fields contain subfields \u2014 for example, a content block might include a heading and body text. These are shown as collapsible sections you can expand to explore the full structure." })
1242
+ ] })
1243
+ ] });
1244
+ }
1245
+ const entries = [
1246
+ {
1247
+ name: "how-to-use-this-handbook",
1248
+ title: "How to Use This Handbook",
1249
+ icon: createSanityIcon(BookTextIcon),
1250
+ description: "A quick guide to navigating the Handbook, understanding the information presented for each field, and making the most of tips, hints, and warnings.",
1251
+ component: HowToUse
1252
+ },
1253
+ {
1254
+ name: "document-types",
1255
+ title: "Document Types",
1256
+ icon: createSanityIcon(LayoutGridIcon),
1257
+ description: "An overview of all document types, organised by group. Select a document type to view its fields, descriptions, and examples.",
1258
+ component: DocumentTypesOverview
1259
+ }
1260
+ ];
1261
+ function GettingStartedTabs() {
1262
+ return /* @__PURE__ */ jsx(SidebarGroup, { heading: "Getting Started", children: entries.map((entry) => /* @__PURE__ */ jsx(SidebarTab, { id: entry.name, label: entry.title, icon: entry.icon }, entry.name)) });
1263
+ }
1264
+ function GettingStartedPanels() {
1265
+ return entries.map((entry) => {
1266
+ const { name, title, description, component: Component } = entry;
1267
+ return /* @__PURE__ */ jsx(ContentPanel, { id: name, title, description, children: /* @__PURE__ */ jsx(Component, {}) }, name);
1268
+ });
1269
+ }
1270
+ function Content() {
1271
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1272
+ /* @__PURE__ */ jsx(GettingStartedPanels, {}),
1273
+ /* @__PURE__ */ jsx(DocumentTypesPanels, {}),
1274
+ /* @__PURE__ */ jsx(GuidesPanels, {})
1275
+ ] });
1276
+ }
1277
+ function Sidebar() {
1278
+ return /* @__PURE__ */ jsxs(Box, { paddingX: 3, paddingBottom: 1, children: [
1279
+ /* @__PURE__ */ jsx(GettingStartedTabs, {}),
1280
+ /* @__PURE__ */ jsx(DocumentTypesTabs, {}),
1281
+ /* @__PURE__ */ jsx(GuidesTabs, {})
1282
+ ] });
1283
+ }
1284
+ const defaultUndocumentedFieldMessage = "This field has not been documented yet. Contact your development team for guidance.", wideBreakpoint = 920, narrowBreakpoint = 480, sidebarWidth = 320, collapsedSidebarWidth = 51;
1285
+ function CollapsedSidebar() {
1286
+ const { sidebarTitle, setSidebarExpanded } = useHandbookContext();
1287
+ return /* @__PURE__ */ jsx(
1288
+ "div",
1289
+ {
1290
+ style: {
1291
+ width: `${collapsedSidebarWidth}px`,
1292
+ minWidth: `${collapsedSidebarWidth}px`,
1293
+ overflow: "hidden",
1294
+ borderRight: "1px solid var(--card-border-color)"
1295
+ },
1296
+ children: /* @__PURE__ */ jsx(Card, { tone: "inherit", "data-collapsed": "", children: /* @__PURE__ */ jsx(
1297
+ Flex,
1298
+ {
1299
+ direction: "column",
1300
+ padding: 3,
1301
+ onClick: () => {
1302
+ setSidebarExpanded(!0);
1303
+ },
1304
+ style: {
1305
+ width: "100vh",
1306
+ boxSizing: "border-box",
1307
+ transform: "rotate(90deg)",
1308
+ transformOrigin: `calc(${collapsedSidebarWidth}px / 2)`,
1309
+ cursor: "pointer"
1310
+ },
1311
+ children: /* @__PURE__ */ jsx(Flex, { align: "flex-start", children: /* @__PURE__ */ jsx(Card, { flex: 1, paddingLeft: 2, padding: 2, children: /* @__PURE__ */ jsx(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, textOverflow: "ellipsis", weight: "semibold", style: { cursor: "default", outline: "none" }, children: sidebarTitle }) }) }) })
1312
+ }
1313
+ ) })
1314
+ }
1315
+ );
1316
+ }
1317
+ function NarrowContentHeader() {
1318
+ const { sidebarTitle, setSidebarExpanded } = useHandbookContext();
1319
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: 3, children: [
1320
+ /* @__PURE__ */ jsx(
1321
+ Button,
1322
+ {
1323
+ icon: ArrowLeftIcon,
1324
+ mode: "bleed",
1325
+ onClick: () => {
1326
+ setSidebarExpanded(!0);
1327
+ }
1328
+ }
1329
+ ),
1330
+ /* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: sidebarTitle })
1331
+ ] });
1332
+ }
1333
+ function ExpandedSidebar() {
1334
+ const { sidebarTitle } = useHandbookContext();
1335
+ return /* @__PURE__ */ jsxs(
1336
+ Card,
1337
+ {
1338
+ borderRight: !0,
1339
+ style: { display: "flex", flexDirection: "column", width: `${sidebarWidth}px`, minWidth: `${sidebarWidth}px` },
1340
+ children: [
1341
+ /* @__PURE__ */ jsx(PaneHeader, { title: sidebarTitle }),
1342
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, paddingBottom: "4px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Sidebar, {}) })
1343
+ ]
1344
+ }
1345
+ );
1346
+ }
1347
+ function NarrowLayout() {
1348
+ const { sidebarTitle, sidebarExpanded } = useHandbookContext();
1349
+ return sidebarExpanded ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", flex: 1 }, children: [
1350
+ /* @__PURE__ */ jsx(PaneHeader, { title: sidebarTitle }),
1351
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, paddingBottom: "4px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Sidebar, {}) })
1352
+ ] }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", flex: 1 }, children: [
1353
+ /* @__PURE__ */ jsx(NarrowContentHeader, {}),
1354
+ /* @__PURE__ */ jsx(Content, {})
1355
+ ] });
1356
+ }
1357
+ function MediumLayout() {
1358
+ const { sidebarExpanded, setSidebarExpanded } = useHandbookContext();
1359
+ return sidebarExpanded ? /* @__PURE__ */ jsxs(Flex, { style: { flex: 1 }, children: [
1360
+ /* @__PURE__ */ jsx(ExpandedSidebar, {}),
1361
+ /* @__PURE__ */ jsx(
1362
+ Card,
1363
+ {
1364
+ flex: 1,
1365
+ padding: 3,
1366
+ onClick: () => {
1367
+ setSidebarExpanded(!1);
1368
+ },
1369
+ style: { opacity: 0.4, overflowY: "auto", cursor: "pointer" }
1370
+ }
1371
+ )
1372
+ ] }) : /* @__PURE__ */ jsxs(Flex, { style: { flex: 1 }, children: [
1373
+ /* @__PURE__ */ jsx(CollapsedSidebar, {}),
1374
+ /* @__PURE__ */ jsx(Card, { flex: 1, style: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsx(Content, {}) })
1375
+ ] });
1376
+ }
1377
+ function WideLayout() {
1378
+ return /* @__PURE__ */ jsxs(Flex, { style: { flex: 1 }, children: [
1379
+ /* @__PURE__ */ jsx(ExpandedSidebar, {}),
1380
+ /* @__PURE__ */ jsx(Card, { flex: 1, style: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsx(Content, {}) })
1381
+ ] });
1382
+ }
1383
+ function HandbookLayout() {
1384
+ const { reference: containerReference, width: containerWidth } = useContainerWidth(), isNarrow = isDefined(containerWidth) && containerWidth < narrowBreakpoint, isMedium = isDefined(containerWidth) && containerWidth >= narrowBreakpoint && containerWidth < wideBreakpoint;
1385
+ function renderLayout() {
1386
+ return isNarrow ? /* @__PURE__ */ jsx(NarrowLayout, {}) : isMedium ? /* @__PURE__ */ jsx(MediumLayout, {}) : /* @__PURE__ */ jsx(WideLayout, {});
1387
+ }
1388
+ return /* @__PURE__ */ jsx("div", { ref: containerReference, style: { display: "flex", flexDirection: "column", height: "100%" }, children: renderLayout() });
1389
+ }
1390
+ function createHandbookTool(config) {
1391
+ return function() {
1392
+ return /* @__PURE__ */ jsx(
1393
+ HandbookProvider,
1394
+ {
1395
+ sidebarTitle: config.sidebarTitle ?? "Handbook",
1396
+ groups: config.groups ?? [],
1397
+ blocks: config.blocks ?? [],
1398
+ undocumentedFieldMessage: config.undocumentedFieldMessage ?? defaultUndocumentedFieldMessage,
1399
+ children: /* @__PURE__ */ jsx(HandbookLayout, {})
1400
+ }
1401
+ );
1402
+ };
1403
+ }
1404
+ let registeredEditors;
1405
+ function setHandbookEditors(editors) {
1406
+ registeredEditors = editors;
1407
+ }
1408
+ function handbookStructure(structureBuilder, context) {
1409
+ if (!isPermittedEditor(registeredEditors, context.currentUser?.email)) return [];
1410
+ const { listItem, document, documentTypeList } = structureBuilder;
1411
+ return [
1412
+ listItem().title("Handbook").icon(createSanityIcon(BookTextIcon)).child(document().id("handbook.handbook").schemaType("handbook.handbook").title("Handbook")),
1413
+ listItem().title("Handbook Guides").icon(createSanityIcon(BookOpenTextIcon)).child(documentTypeList("handbook.guide").title("Handbook Guides"))
1414
+ ];
1415
+ }
1416
+ function CalloutPreview({ variant, text }) {
1417
+ return /* @__PURE__ */ jsx(CalloutContent, { variant: variant ?? "tip", children: text });
1418
+ }
1419
+ function ParagraphDecorator({ children }) {
1420
+ return /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8125rem" }, children });
1421
+ }
1422
+ function HeadingDecorator({ children }) {
1423
+ return /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8125rem", fontWeight: "500" }, children });
1424
+ }
1425
+ function BlockquoteDecorator({ children }) {
1426
+ return /* @__PURE__ */ jsx(
1427
+ "div",
1428
+ {
1429
+ style: {
1430
+ paddingLeft: "0.75em",
1431
+ fontSize: "0.8125rem",
1432
+ fontStyle: "italic",
1433
+ borderLeft: "2px solid var(--card-border-color)"
1434
+ },
1435
+ children
1436
+ }
1437
+ );
1438
+ }
1439
+ function BulletListDecorator({ children }) {
1440
+ return /* @__PURE__ */ jsx("div", { style: { marginTop: "-0.25em", fontSize: "0.8125rem" }, children });
1441
+ }
1442
+ function NumberListDecorator({ children }) {
1443
+ return /* @__PURE__ */ jsx("div", { style: { marginTop: "-0.25em", fontSize: "0.8125rem" }, children });
1444
+ }
1445
+ function InlineCodeDecorator({ children }) {
1446
+ return /* @__PURE__ */ jsx(
1447
+ "code",
1448
+ {
1449
+ style: {
1450
+ padding: "0.125rem 0.25rem",
1451
+ fontSize: "0.8125rem",
1452
+ color: "var(--card-muted-fg-color)",
1453
+ backgroundColor: "var(--card-code-bg-color)",
1454
+ borderRadius: "0.125rem"
1455
+ },
1456
+ children
1457
+ }
1458
+ );
1459
+ }
1460
+ const linkAnnotation = defineArrayMember({
1461
+ name: "link",
1462
+ type: "object",
1463
+ fields: [
1464
+ defineField({
1465
+ name: "href",
1466
+ title: "URL",
1467
+ type: "url",
1468
+ description: "Web address for this link. Supports http, https, mailto, and tel schemes.",
1469
+ validation: (rule) => rule.uri({ scheme: ["http", "https", "mailto", "tel"] })
1470
+ })
1471
+ ]
1472
+ }), handbookCalloutType = defineType({
1473
+ name: "handbook.callout",
1474
+ title: "Callout",
1475
+ type: "object",
1476
+ icon: createSanityIcon(MessageCircleQuestionMarkIcon),
1477
+ description: "A highlighted message block used to draw attention to tips, supplementary information, or warnings.",
1478
+ preview: {
1479
+ select: { variant: "variant", body: "body" },
1480
+ prepare({ variant, body }) {
1481
+ const text = Array.isArray(body) ? body.flatMap((block) => (block.children ?? []).map((child) => child.text ?? "")).join("") : "";
1482
+ return { variant, text };
1483
+ }
1484
+ },
1485
+ components: {
1486
+ preview: CalloutPreview
1487
+ },
1488
+ fields: [
1489
+ defineField({
1490
+ name: "variant",
1491
+ title: "Variant",
1492
+ type: "string",
1493
+ description: "Visual style and intent of the callout.",
1494
+ initialValue: "tip",
1495
+ validation: (rule) => rule.required(),
1496
+ options: {
1497
+ list: [
1498
+ { title: "Tip", value: "tip" },
1499
+ { title: "Information", value: "info" },
1500
+ { title: "Warning", value: "warning" }
1501
+ ],
1502
+ layout: "radio",
1503
+ direction: "horizontal"
1504
+ }
1505
+ }),
1506
+ defineField({
1507
+ name: "body",
1508
+ title: "Body",
1509
+ type: "array",
1510
+ description: "Content displayed inside the callout. Supports inline formatting and links.",
1511
+ of: [
1512
+ defineArrayMember({
1513
+ type: "block",
1514
+ styles: [{ title: "Normal", value: "normal", component: ParagraphDecorator }],
1515
+ marks: {
1516
+ decorators: [
1517
+ { title: "Bold", value: "strong" },
1518
+ { title: "Italic", value: "em" },
1519
+ { title: "Strikethrough", value: "strike-through" },
1520
+ { title: "Code", value: "code", component: InlineCodeDecorator }
1521
+ ],
1522
+ annotations: [linkAnnotation]
1523
+ },
1524
+ lists: []
1525
+ })
1526
+ ]
1527
+ })
1528
+ ]
1529
+ });
1530
+ function CodePreview({ code, language }) {
1531
+ return isDefined(code) ? /* @__PURE__ */ jsx(CodeContent, { code, language }) : null;
1532
+ }
1533
+ const handbookCodeType = defineType({
1534
+ name: "handbook.code",
1535
+ title: "Code",
1536
+ type: "object",
1537
+ icon: createSanityIcon(CodeIcon),
1538
+ description: "A syntax-highlighted code block with language selection.",
1539
+ preview: {
1540
+ select: { code: "code", language: "language" }
1541
+ },
1542
+ components: {
1543
+ preview: CodePreview
1544
+ },
1545
+ fields: [
1546
+ defineField({
1547
+ name: "code",
1548
+ title: "Code",
1549
+ type: "text",
1550
+ description: "The source code to display."
1551
+ }),
1552
+ defineField({
1553
+ name: "language",
1554
+ title: "Language",
1555
+ type: "string",
1556
+ description: "Programming language used for syntax highlighting.",
1557
+ options: {
1558
+ list: [
1559
+ { title: "CSS", value: "css" },
1560
+ { title: "GROQ", value: "groq" },
1561
+ { title: "HTML", value: "html" },
1562
+ { title: "JavaScript", value: "javascript" },
1563
+ { title: "JSON", value: "json" },
1564
+ { title: "Plain Text", value: "text" },
1565
+ { title: "React", value: "tsx" },
1566
+ { title: "Shell", value: "shell" },
1567
+ { title: "TypeScript", value: "typescript" }
1568
+ ]
1569
+ }
1570
+ })
1571
+ ]
1572
+ });
1573
+ function HorizontalRulePreview() {
1574
+ return /* @__PURE__ */ jsx(HorizontalRuleContent, { marginBlock: "0.5rem" });
1575
+ }
1576
+ const handbookHorizontalRuleType = defineType({
1577
+ name: "handbook.horizontalRule",
1578
+ title: "Horizontal Rule",
1579
+ type: "object",
1580
+ icon: createSanityIcon(MinusIcon),
1581
+ description: "A visual divider between sections of content.",
1582
+ preview: {
1583
+ prepare() {
1584
+ return { title: "Horizontal Rule" };
1585
+ }
1586
+ },
1587
+ components: {
1588
+ preview: HorizontalRulePreview
1589
+ },
1590
+ fields: [
1591
+ defineField({
1592
+ name: "style",
1593
+ title: "Style",
1594
+ type: "string",
1595
+ description: "Presentation style for the divider.",
1596
+ hidden: !0,
1597
+ initialValue: "default"
1598
+ })
1599
+ ]
1600
+ });
1601
+ function ImagePreview({ url, alt, caption }) {
1602
+ return isDefined(url) ? /* @__PURE__ */ jsx("div", { style: { padding: "0.5rem" }, children: /* @__PURE__ */ jsx(ImageContent, { url, alt, caption }) }) : null;
1603
+ }
1604
+ const handbookImageType = defineType({
1605
+ name: "handbook.image",
1606
+ title: "Image",
1607
+ type: "object",
1608
+ icon: createSanityIcon(ImageIcon),
1609
+ description: "An image with optional caption and alternative text for accessibility.",
1610
+ preview: {
1611
+ select: { caption: "caption", alt: "alt", url: "asset.asset.url" }
1612
+ },
1613
+ components: {
1614
+ preview: ImagePreview
1615
+ },
1616
+ fields: [
1617
+ defineField({
1618
+ name: "asset",
1619
+ title: "Image",
1620
+ type: "image",
1621
+ description: "Upload or select an image from the media library.",
1622
+ options: { hotspot: !0 }
1623
+ }),
1624
+ defineField({
1625
+ name: "caption",
1626
+ title: "Caption",
1627
+ type: "string",
1628
+ description: "Text displayed beneath the image in the handbook."
1629
+ }),
1630
+ defineField({
1631
+ name: "alt",
1632
+ title: "Alternative Text",
1633
+ type: "string",
1634
+ description: "Describes the image for screen readers and accessibility tools."
1635
+ })
1636
+ ]
1637
+ });
1638
+ function VideoPreview({ url, caption }) {
1639
+ return isDefined(url) ? /* @__PURE__ */ jsx("div", { style: { padding: "0.5rem" }, children: /* @__PURE__ */ jsx(VideoContent, { url, caption }) }) : null;
1640
+ }
1641
+ const handbookVideoType = defineType({
1642
+ name: "handbook.video",
1643
+ title: "Video",
1644
+ type: "object",
1645
+ icon: createSanityIcon(VideoIcon),
1646
+ description: "An uploaded video with optional caption.",
1647
+ preview: {
1648
+ select: { caption: "caption", url: "asset.asset.url" }
1649
+ },
1650
+ components: {
1651
+ preview: VideoPreview
1652
+ },
1653
+ fields: [
1654
+ defineField({
1655
+ name: "asset",
1656
+ title: "Video",
1657
+ type: "file",
1658
+ description: "Upload or select a video from the media library.",
1659
+ options: { accept: "video/*" }
1660
+ }),
1661
+ defineField({
1662
+ name: "caption",
1663
+ title: "Caption",
1664
+ type: "string",
1665
+ description: "Text displayed beneath the video in the handbook."
1666
+ })
1667
+ ]
1668
+ });
1669
+ function createGuideContentField(customBlocks = []) {
1670
+ return defineField({
1671
+ name: "content",
1672
+ title: "Content",
1673
+ type: "array",
1674
+ description: "Rich text content for this guide. Supports headings, lists, images, code blocks, and custom blocks.",
1675
+ of: [
1676
+ defineArrayMember({
1677
+ name: "block",
1678
+ type: "block",
1679
+ styles: [
1680
+ { title: "Paragraph", value: "normal", component: ParagraphDecorator },
1681
+ { title: "Heading", value: "h2", component: HeadingDecorator },
1682
+ { title: "Blockquote", value: "blockquote", component: BlockquoteDecorator }
1683
+ ],
1684
+ marks: {
1685
+ decorators: [
1686
+ { title: "Bold", value: "strong" },
1687
+ { title: "Italic", value: "em" },
1688
+ { title: "Strikethrough", value: "strike-through" },
1689
+ { title: "Code", value: "code", component: InlineCodeDecorator }
1690
+ ],
1691
+ annotations: [linkAnnotation]
1692
+ },
1693
+ lists: [
1694
+ { title: "Bullet", value: "bullet", component: BulletListDecorator },
1695
+ { title: "Number", value: "number", component: NumberListDecorator }
1696
+ ]
1697
+ }),
1698
+ defineArrayMember({ type: "handbook.image" }),
1699
+ defineArrayMember({ type: "handbook.video" }),
1700
+ defineArrayMember({ type: "handbook.code" }),
1701
+ defineArrayMember({ type: "handbook.callout" }),
1702
+ defineArrayMember({ type: "handbook.horizontalRule" }),
1703
+ ...customBlocks.map((block) => defineArrayMember({ type: block.schema.name }))
1704
+ ]
1705
+ });
1706
+ }
1707
+ function createHandbookGuideType(customBlocks = []) {
1708
+ return defineType({
1709
+ name: "handbook.guide",
1710
+ title: "Handbook Guide",
1711
+ type: "document",
1712
+ icon: createSanityIcon(BookOpenTextIcon),
1713
+ description: "A handbook page authored with rich text, images, and custom blocks.",
1714
+ preview: {
1715
+ select: { title: "title", description: "description" },
1716
+ prepare({ title, description }) {
1717
+ return {
1718
+ title: title ?? "Untitled Guide",
1719
+ subtitle: description
1720
+ };
1721
+ }
1722
+ },
1723
+ fields: [
1724
+ defineField({
1725
+ name: "title",
1726
+ title: "Title",
1727
+ type: "string",
1728
+ description: "Display title shown in the handbook sidebar and as the page heading.",
1729
+ validation: (rule) => rule.required()
1730
+ }),
1731
+ defineField({
1732
+ name: "description",
1733
+ title: "Description",
1734
+ type: "string",
1735
+ description: "Brief summary displayed beneath the guide heading."
1736
+ }),
1737
+ createGuideContentField(customBlocks)
1738
+ ]
1739
+ });
1740
+ }
1741
+ const handbookType = defineType({
1742
+ name: "handbook.handbook",
1743
+ title: "Handbook",
1744
+ type: "document",
1745
+ icon: createSanityIcon(BookTextIcon),
1746
+ description: "Defines the structure and ordering of the handbook, including groups and their guides.",
1747
+ preview: {
1748
+ prepare() {
1749
+ return { title: "Handbook" };
1750
+ }
1751
+ },
1752
+ fields: [
1753
+ defineField({
1754
+ name: "groups",
1755
+ title: "Groups",
1756
+ type: "array",
1757
+ description: "Ordered list of groups displayed in the handbook sidebar. Each group contains a title and an ordered list of guide references.",
1758
+ of: [
1759
+ defineArrayMember({
1760
+ name: "group",
1761
+ type: "object",
1762
+ icon: createSanityIcon(ListPlusIcon),
1763
+ fields: [
1764
+ defineField({
1765
+ name: "title",
1766
+ title: "Title",
1767
+ type: "string",
1768
+ description: "Heading displayed above this group of guides in the sidebar.",
1769
+ validation: (rule) => rule.required()
1770
+ }),
1771
+ defineField({
1772
+ name: "guides",
1773
+ title: "Guides",
1774
+ type: "array",
1775
+ description: "Ordered list of guides within this group. Drag to reorder.",
1776
+ of: [
1777
+ defineArrayMember({
1778
+ type: "reference",
1779
+ to: [{ type: "handbook.guide" }]
1780
+ })
1781
+ ]
1782
+ })
1783
+ ],
1784
+ preview: {
1785
+ select: { title: "title", guide0: "guides.0.title", guide1: "guides.1.title", guide2: "guides.2.title" },
1786
+ prepare({
1787
+ title,
1788
+ guide0,
1789
+ guide1,
1790
+ guide2
1791
+ }) {
1792
+ const shown = [guide0, guide1, guide2].filter((guide) => isDefined(guide)).slice(0, 2), hasMore = isDefined(guide2);
1793
+ function formatSubtitle() {
1794
+ return isDefined(shown) ? hasMore ? `${shown.join(", ")}, and others` : shown.join(" and ") : "No guides selected";
1795
+ }
1796
+ return {
1797
+ title: title ?? "Untitled Group",
1798
+ subtitle: formatSubtitle()
1799
+ };
1800
+ }
1801
+ }
1802
+ })
1803
+ ]
1804
+ })
1805
+ ]
1806
+ });
1807
+ function useIsHandbookEditor(editors) {
1808
+ const user = useCurrentUser();
1809
+ return isPermittedEditor(editors, user?.email);
1810
+ }
1811
+ const handbookSingletonTypes = /* @__PURE__ */ new Set(["handbook.handbook"]), handbookDocumentTypes = /* @__PURE__ */ new Set(["handbook.handbook", "handbook.guide"]);
1812
+ function isHandbookTemplate(templateId, typeSet) {
1813
+ return typeSet.has(templateId) || [...typeSet].some((type) => templateId.startsWith(`${type}-`));
1814
+ }
1815
+ function validateGroup(index, group) {
1816
+ const prefix = `sanity-handbook: groups[${index}]`;
1817
+ if (!isDefined(group.title)) throw new Error(`${prefix} is missing a required "title" property.`);
1818
+ if (!Array.isArray(group.documents) || group.documents.length === 0)
1819
+ throw new Error(`${prefix} must have a non-empty "documents" array.`);
1820
+ for (const document of group.documents) {
1821
+ if (!isDefined(document.name))
1822
+ throw new Error(`${prefix}.documents contains a document without a "name" property.`);
1823
+ if (!Array.isArray(document.fields))
1824
+ throw new TypeError(`${prefix}.documents.${document.name} is missing a "fields" array.`);
1825
+ }
1826
+ }
1827
+ function validateConfig(config) {
1828
+ if (config.groups)
1829
+ for (const [index, group] of config.groups.entries())
1830
+ validateGroup(index, group);
1831
+ }
1832
+ function handbookPlugin(config) {
1833
+ config && validateConfig(config);
1834
+ const customBlocks = config?.blocks ?? [], editors = config?.editors;
1835
+ setHandbookEditors(editors);
1836
+ const guideType = createHandbookGuideType(customBlocks);
1837
+ return definePlugin(() => ({
1838
+ name: "@driftime/sanity-plugin-handbook",
1839
+ schema: {
1840
+ types: [
1841
+ handbookType,
1842
+ guideType,
1843
+ handbookImageType,
1844
+ handbookVideoType,
1845
+ handbookCodeType,
1846
+ handbookCalloutType,
1847
+ handbookHorizontalRuleType,
1848
+ ...customBlocks.map((block) => block.schema)
1849
+ ]
1850
+ },
1851
+ document: {
1852
+ newDocumentOptions: (templateItems, { creationContext, currentUser }) => creationContext.type === "global" || creationContext.type === "structure" ? isPermittedEditor(editors, currentUser?.email) ? templateItems.filter((item) => !isHandbookTemplate(item.templateId, handbookSingletonTypes)) : templateItems.filter((item) => !isHandbookTemplate(item.templateId, handbookDocumentTypes)) : templateItems,
1853
+ actions: (actionComponents, { schemaType }) => handbookSingletonTypes.has(schemaType) ? actionComponents.filter(({ action }) => action !== "duplicate") : actionComponents
1854
+ },
1855
+ tools: [
1856
+ {
1857
+ name: "handbook",
1858
+ title: config?.title ?? "Handbook",
1859
+ icon: createSanityIcon(BookTextIcon),
1860
+ component: createHandbookTool({ ...config, blocks: customBlocks })
1861
+ }
1862
+ ]
1863
+ }))();
1864
+ }
1865
+ export {
1866
+ handbookPlugin,
1867
+ handbookStructure,
1868
+ useIsHandbookEditor
1869
+ };
1870
+ //# sourceMappingURL=index.js.map