@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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Driftime®
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,299 @@
1
+ <br />
2
+
3
+ <div align="center">
4
+ <picture>
5
+ <source media="(prefers-color-scheme: dark)" srcset="/.github/assets/icon-dark.svg" />
6
+ <source media="(prefers-color-scheme: light)" srcset="/.github/assets/icon.svg" />
7
+ <img src="/.github/assets/icon.svg" alt="Handbook icon" width="48" />
8
+ </picture>
9
+ <h1>Handbook — A Sanity Plugin</h1>
10
+ <p>Schema-driven documentation and editorial guides, built right into Sanity Studio.</p>
11
+ </div>
12
+
13
+ <br />
14
+
15
+ ## Overview
16
+
17
+ Handbook adds a dedicated tool to Sanity Studio that serves two purposes: it automatically generates browsable documentation from your document type schemas, and it provides a rich text guide authoring system for editorial teams. Field descriptions, examples, tips, and warnings are pulled directly from your schema definitions, so your documentation stays in sync with your content model.
18
+
19
+ <br />
20
+
21
+ <figure>
22
+ <picture>
23
+ <source media="(prefers-color-scheme: dark)" srcset="/.github/assets/handbook-fields-dark.png" />
24
+ <source media="(prefers-color-scheme: light)" srcset="/.github/assets/handbook-fields.png" />
25
+ <img src="/.github/assets/handbook-fields.png" alt="The Handbook tool displaying field documentation for a Pages document type, with field names, types, descriptions, and example values" />
26
+ </picture>
27
+ <p align="center"><sub><em>Field documentation generated automatically from your schema definitions.</em></sub></p>
28
+ </figure>
29
+
30
+ <br />
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ bun add -E @driftime/sanity-plugin-handbook
36
+ ```
37
+
38
+ ## Basic Setup
39
+
40
+ Add the plugin to your Sanity configuration and pass in the document types you want to document. Each group appears as a labelled section in the sidebar.
41
+
42
+ ```typescript
43
+ import { defineConfig } from "sanity";
44
+ import { handbookPlugin } from "@driftime/sanity-plugin-handbook";
45
+ // ...
46
+
47
+ export default defineConfig({
48
+ // ...
49
+ plugins: [
50
+ handbookPlugin({
51
+ groups: [
52
+ {
53
+ title: "Singletons",
54
+ description: "Unique pages that exist as a single instance and cannot be duplicated.",
55
+ documents: [home, work, latest],
56
+ },
57
+ {
58
+ title: "Collections",
59
+ description:
60
+ "Repeatable document types with multiple entries, each representing an individually routable page.",
61
+ documents: [page, caseStudy, article],
62
+ },
63
+ ],
64
+ }),
65
+ ],
66
+ });
67
+ ```
68
+
69
+ This registers a Handbook tool in the Studio navigation. Opening it displays a sidebar with your document types, each showing its fields with their descriptions. See [Configuration](#configuration) for all available options.
70
+
71
+ <br />
72
+
73
+ <figure>
74
+ <picture>
75
+ <source media="(prefers-color-scheme: dark)" srcset="/.github/assets/handbook-document-types-dark.png" />
76
+ <source media="(prefers-color-scheme: light)" srcset="/.github/assets/handbook-document-types.png" />
77
+ <img src="/.github/assets/handbook-document-types.png" alt="The Handbook tool showing document type groups in the sidebar with Singletons and Collections listed" />
78
+ </picture>
79
+ <p align="center"><sub><em>Document types organised into groups, browsable from the sidebar.</em></sub></p>
80
+ </figure>
81
+
82
+ <br />
83
+
84
+ ## Handbook Metadata
85
+
86
+ The plugin augments Sanity's `FieldDefinitionBase` and `DocumentDefinition` interfaces, adding a `handbook` property that TypeScript recognises automatically. Fields fall back to their standard `title` and `description` properties when no `handbook` metadata is provided.
87
+
88
+ ```typescript
89
+ import { defineField, defineType } from "sanity";
90
+
91
+ export const pageType = defineType({
92
+ name: "page",
93
+ title: "Page",
94
+ type: "document",
95
+ handbook: {
96
+ title: "Page",
97
+ description: "A standalone page with a unique URL.",
98
+ },
99
+ fields: [
100
+ defineField({
101
+ name: "title",
102
+ title: "Title",
103
+ type: "string",
104
+ description: "The primary heading displayed on this page.",
105
+ handbook: {
106
+ example: "About Acme Inc.",
107
+ tip: "Keep titles concise — under 60 characters for best results.",
108
+ info: "The title is also used to generate the page's URL slug.",
109
+ caution:
110
+ "This field is required. Publishing without a title will cause the page to be excluded from navigation and search indexing.",
111
+ },
112
+ }),
113
+ ],
114
+ });
115
+ ```
116
+
117
+ The `handbook` property supports the following keys:
118
+
119
+ | Key | Purpose |
120
+ | ------------- | ----------------------------------------------------------------------- |
121
+ | `title` | Display title override for the field or document. |
122
+ | `description` | Description shown beneath the field or document heading. |
123
+ | `example` | Illustrative example value, displayed in italics. |
124
+ | `tip` | Helpful guidance shown as a popover hint. |
125
+ | `info` | Additional context shown as a popover hint. |
126
+ | `caution` | Warning about constraints or potential issues, shown as a popover hint. |
127
+
128
+ If no `handbook` property is provided, the tool falls back to the field's `title` and `description` properties. If neither exists, the field name is converted to title case. Fields without any description display a configurable fallback message (see `undocumentedFieldMessage` in [Configuration](#configuration)).
129
+
130
+ ### Subfield Browsing
131
+
132
+ Fields with nested structure display a collapsible section that editors can expand to explore subfields. Custom types extending built-in types like `image` or `file` show only the fields you add — inherited fields are excluded automatically. Circular type references are detected and labelled rather than rendering infinitely.
133
+
134
+ ## Guide Content
135
+
136
+ Handbook includes a guide authoring system powered by Portable Text. The plugin automatically registers a `handbook.handbook` singleton and a `handbook.guide` document type — no additional schema setup is required.
137
+
138
+ The singleton defines the groups and ordering of your guides. Each group contains references to guide documents, which are authored using a rich text editor with built-in support for:
139
+
140
+ - Paragraph, heading, and blockquote styles
141
+ - Bold, italic, strikethrough, and inline code formatting
142
+ - Bullet and numbered lists
143
+ - Inline links
144
+ - Image blocks with caption and alternative text
145
+ - Video blocks with caption
146
+ - Syntax-highlighted code blocks with language selection
147
+ - Callout blocks with tip, information, and warning variants
148
+ - Horizontal rule dividers
149
+
150
+ The Handbook tool displays guides in the sidebar alongside your document types. Selecting a guide opens its content in the main panel.
151
+
152
+ <br />
153
+
154
+ <figure>
155
+ <picture>
156
+ <source media="(prefers-color-scheme: dark)" srcset="/.github/assets/handbook-structure-guide-dark.png" />
157
+ <source media="(prefers-color-scheme: light)" srcset="/.github/assets/handbook-structure-guide.png" />
158
+ <img src="/.github/assets/handbook-structure-guide.png" alt="A guide displayed in the Handbook tool with headings, paragraphs, a callout, a bullet list, and a code block" />
159
+ </picture>
160
+ <p align="center"><sub><em>Editorial guides authored with Portable Text and rendered directly in the tool.</em></sub></p>
161
+ </figure>
162
+
163
+ <br />
164
+
165
+ ## Custom Blocks
166
+
167
+ Register custom Portable Text blocks for use in guide content by providing a schema definition and a React component. Custom blocks appear as insertion options in the guide content editor and are rendered using the provided component in the Handbook viewer.
168
+
169
+ ```typescript
170
+ import { defineConfig, defineField, defineType } from "sanity";
171
+ import { handbookPlugin } from "@driftime/sanity-plugin-handbook";
172
+ // ...
173
+
174
+ const exampleSchema = defineType({
175
+ name: "handbook.example",
176
+ title: "Example",
177
+ type: "object",
178
+ fields: [
179
+ defineField({
180
+ name: "label",
181
+ title: "Label",
182
+ type: "string",
183
+ }),
184
+ ],
185
+ });
186
+
187
+ function ExampleBlock({ value }: { value: PortableTextObject & { label?: string } }) {
188
+ return <div>{value.label}</div>;
189
+ }
190
+
191
+ export default defineConfig({
192
+ // ...
193
+ plugins: [
194
+ handbookPlugin({
195
+ blocks: [
196
+ {
197
+ schema: exampleSchema,
198
+ component: ExampleBlock,
199
+ },
200
+ ],
201
+ }),
202
+ ],
203
+ });
204
+ ```
205
+
206
+ ## Editor Permissions
207
+
208
+ Restrict who can create and edit handbook documents by providing a list of email addresses.
209
+
210
+ ```typescript
211
+ handbookPlugin({
212
+ editors: ["editor@acme.com", "admin@acme.com"],
213
+ });
214
+ ```
215
+
216
+ When an editors list is provided, only users whose email appears in the list can create or manage handbook documents. Users not in the list are excluded entirely. If no editors list is provided, all users have full access.
217
+
218
+ The `useIsHandbookEditor` hook is exported for use in your own components if you need to conditionally render UI based on editor permissions.
219
+
220
+ ```typescript
221
+ import { useIsHandbookEditor } from "@driftime/sanity-plugin-handbook";
222
+
223
+ const isEditor = useIsHandbookEditor(["editor@acme.com"]);
224
+ ```
225
+
226
+ ## Structure Integration
227
+
228
+ To add handbook documents to the Structure tool sidebar, use the `handbookStructure` helper. This adds a Handbook singleton editor and a Handbook Guides list. The structure helper respects the same editor permissions configured via the plugin.
229
+
230
+ <br />
231
+
232
+ <figure>
233
+ <picture>
234
+ <source media="(prefers-color-scheme: dark)" srcset="/.github/assets/handbook-structure-dark.png" />
235
+ <source media="(prefers-color-scheme: light)" srcset="/.github/assets/handbook-structure.png" />
236
+ <img src="/.github/assets/handbook-structure.png" alt="The Handbook singleton editor in the Structure tool, showing reorderable groups each containing guide references" />
237
+ </picture>
238
+ <p align="center"><sub><em>The Handbook singleton where groups and their ordering are managed.</em></sub></p>
239
+ </figure>
240
+
241
+ <br />
242
+
243
+ ```typescript
244
+ import { handbookStructure } from "@driftime/sanity-plugin-handbook";
245
+
246
+ export const structure = (structureBuilder, context) => {
247
+ return structureBuilder
248
+ .list()
249
+ .title("Content")
250
+ .items([
251
+ ...structureBuilder
252
+ .documentTypeListItems()
253
+ .filter((item) => !["handbook.handbook", "handbook.guide"].includes(item.getId() ?? "")),
254
+ structureBuilder.divider(),
255
+ ...handbookStructure(structureBuilder, context),
256
+ ]);
257
+ };
258
+ ```
259
+
260
+ Individual guides are authored as `handbook.guide` documents, which can be created and edited directly from the Structure tool.
261
+
262
+ <br />
263
+
264
+ <figure>
265
+ <picture>
266
+ <source media="(prefers-color-scheme: dark)" srcset="/.github/assets/handbook-structure-guides-dark.png" />
267
+ <source media="(prefers-color-scheme: light)" srcset="/.github/assets/handbook-structure-guides.png" />
268
+ <img src="/.github/assets/handbook-structure-guides.png" alt="The Handbook Guides list in the Structure tool, showing individual guide documents available for editing" />
269
+ </picture>
270
+ <p align="center"><sub><em>Handbook Guides listed in the Structure tool for authoring and editing.</em></sub></p>
271
+ </figure>
272
+
273
+ <br />
274
+
275
+ ## Configuration
276
+
277
+ All configuration is optional. The plugin works out of the box with sensible defaults.
278
+
279
+ | Option | Type | Default | Purpose |
280
+ | -------------------------- | --------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
281
+ | `title` | `string` | `"Handbook"` | Title shown in the Studio tool navigation. |
282
+ | `sidebarTitle` | `string` | `"Handbook"` | Heading displayed at the top of the sidebar. |
283
+ | `groups` | `HandbookStructureGroup[]` | `[]` | Document type groups defining the sidebar hierarchy. |
284
+ | `blocks` | `HandbookBlockDefinition[]` | `[]` | Custom Portable Text block definitions for guide content. |
285
+ | `editors` | `string[]` | `undefined` | Email addresses permitted to edit handbook documents. Unrestricted when omitted. |
286
+ | `undocumentedFieldMessage` | `string` | `"This field has not been documented yet. Contact your development team for guidance."` | Fallback message shown when a field has no description. |
287
+
288
+ <br />
289
+ <br />
290
+
291
+ <div align="center">
292
+ <a href="https://driftime.com" target="_blank">
293
+ <picture>
294
+ <source media="(prefers-color-scheme: dark)" srcset="https://driftime.com/driftime-github-logo-dark.svg" />
295
+ <source media="(prefers-color-scheme: light)" srcset="https://driftime.com/driftime-github-logo.svg" />
296
+ <img src="https://driftime.com/driftime-github-logo.svg" alt="Driftime® Logo" width="100" />
297
+ </picture>
298
+ </a>
299
+ </div>