sanity-plugin-sgntech-interview 1.0.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.
- package/LICENSE +21 -0
- package/README.md +173 -0
- package/dist/_chunks-es/index.js +17543 -0
- package/dist/_chunks-es/index.js.map +1 -0
- package/dist/_chunks-es/refractor.js +4653 -0
- package/dist/_chunks-es/refractor.js.map +1 -0
- package/dist/_chunks-es/speakers.js +36 -0
- package/dist/_chunks-es/speakers.js.map +1 -0
- package/dist/index.d.ts +517 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/render.d.ts +382 -0
- package/dist/render.js +165 -0
- package/dist/render.js.map +1 -0
- package/package.json +97 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SGNTech
|
|
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,173 @@
|
|
|
1
|
+
<!-- Social preview / OpenGraph image -->
|
|
2
|
+
<p align="center">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/netz-sg/sanity-plugin-sgntech-interview/main/assets/og-image.png" alt="sanity-plugin-sgntech-interview — question and answer blocks for Sanity Studio" width="640" />
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<h1 align="center">sanity-plugin-sgntech-interview</h1>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
Interviews as Portable Text: question and answer pairs with speakers, timestamps and a note when an answer was translated.
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://www.npmjs.com/package/sanity-plugin-sgntech-interview"><img alt="npm version" src="https://img.shields.io/npm/v/sanity-plugin-sgntech-interview.svg?style=flat-square" /></a>
|
|
14
|
+
<a href="./LICENSE"><img alt="license" src="https://img.shields.io/npm/l/sanity-plugin-sgntech-interview.svg?style=flat-square" /></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## What you get
|
|
20
|
+
|
|
21
|
+
- **One block per turn**, so intros, photos and section headings sit between the questions the way a printed interview does.
|
|
22
|
+
- **Speakers defined once** on the document. Blocks pick from the list, so renaming somebody fixes every turn at once.
|
|
23
|
+
- **Answers take bold, italic and links** — enough for the album someone is talking about, without an editor inside the editor.
|
|
24
|
+
- **Timestamps** into the recording, turned into a link that starts at the right second on YouTube and Vimeo.
|
|
25
|
+
- **A translation note.** A translated answer is not a verbatim quote, and the original wording can be kept alongside it.
|
|
26
|
+
- **A renderer that carries no design** — two columns by default, one CSS variable away from the classic `NAME: text` form.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npm install sanity-plugin-sgntech-interview
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Sanity Studio v5 or v6, React 18 or 19. `@portabletext/react` is an optional peer — only the
|
|
35
|
+
renderer needs it, and only when the site does not pass its own.
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// sanity.config.ts
|
|
41
|
+
import {defineArrayMember, defineConfig, defineField, defineType} from 'sanity'
|
|
42
|
+
import {createSpeakersField, interview} from 'sanity-plugin-sgntech-interview'
|
|
43
|
+
|
|
44
|
+
export default defineConfig({
|
|
45
|
+
plugins: [interview()],
|
|
46
|
+
schema: {
|
|
47
|
+
types: [
|
|
48
|
+
defineType({
|
|
49
|
+
type: 'document',
|
|
50
|
+
name: 'post',
|
|
51
|
+
fields: [
|
|
52
|
+
defineField({type: 'string', name: 'title'}),
|
|
53
|
+
createSpeakersField(),
|
|
54
|
+
defineField({
|
|
55
|
+
type: 'array',
|
|
56
|
+
name: 'body',
|
|
57
|
+
of: [
|
|
58
|
+
defineArrayMember({type: 'block'}),
|
|
59
|
+
defineArrayMember({type: 'interviewBlock'}),
|
|
60
|
+
],
|
|
61
|
+
}),
|
|
62
|
+
],
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`createSpeakersField()` adds the list the blocks read from. Fill it in first — name, a short id
|
|
70
|
+
like `till`, an optional role, and a tick on whoever asks the questions. Blocks without their own
|
|
71
|
+
asker fall back to the first one ticked.
|
|
72
|
+
|
|
73
|
+
### Options
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
interview({
|
|
77
|
+
name: 'interviewBlock', // block type name
|
|
78
|
+
title: 'Interview',
|
|
79
|
+
speakersField: 'interviewSpeakers', // where the speakers live
|
|
80
|
+
speakerTypes: [], // e.g. ['author', 'bandMember'] to link real documents
|
|
81
|
+
answerMarks: {decorators: ['strong', 'em'], link: true},
|
|
82
|
+
timestamps: true,
|
|
83
|
+
translation: true,
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`createSpeakersField()` takes the same `speakersField` and `speakerTypes`, so pass the config to
|
|
88
|
+
both when you change them.
|
|
89
|
+
|
|
90
|
+
## Rendering
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import {PortableText} from '@portabletext/react'
|
|
94
|
+
import {
|
|
95
|
+
createInterviewPortableTextComponents,
|
|
96
|
+
ensureInterviewStyles,
|
|
97
|
+
} from 'sanity-plugin-sgntech-interview/render'
|
|
98
|
+
|
|
99
|
+
ensureInterviewStyles() // or paste `interviewStyles` into your own CSS
|
|
100
|
+
|
|
101
|
+
export function Article({post}) {
|
|
102
|
+
return (
|
|
103
|
+
<PortableText
|
|
104
|
+
value={post.body}
|
|
105
|
+
components={createInterviewPortableTextComponents({
|
|
106
|
+
speakers: post.interviewSpeakers,
|
|
107
|
+
source: post.videoUrl, // makes the timestamps clickable
|
|
108
|
+
})}
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The speakers are passed in because they live on the document, not on the block — one GROQ
|
|
115
|
+
projection away:
|
|
116
|
+
|
|
117
|
+
```groq
|
|
118
|
+
*[_type == "post"][0]{..., interviewSpeakers}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Styling
|
|
122
|
+
|
|
123
|
+
Two columns by default: speaker left, text right, stacking below 30rem. Every value is a custom
|
|
124
|
+
property with its default at the point of use, so setting one on any ancestor works:
|
|
125
|
+
|
|
126
|
+
```css
|
|
127
|
+
.article {
|
|
128
|
+
--sgn-interview-speaker-width: 6rem;
|
|
129
|
+
--sgn-interview-pair-gap: 2.5rem;
|
|
130
|
+
--sgn-interview-question-weight: 700;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The full list is in [`src/render/styles.ts`](./src/render/styles.ts).
|
|
135
|
+
|
|
136
|
+
## What is stored
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"_type": "interviewBlock",
|
|
141
|
+
"speaker": "till",
|
|
142
|
+
"askedBy": "seb",
|
|
143
|
+
"question": "How did the album come about?",
|
|
144
|
+
"answer": [{"_type": "block", "children": [{"_type": "span", "text": "It took a while…"}]}],
|
|
145
|
+
"timestamp": "12:34",
|
|
146
|
+
"translated": true,
|
|
147
|
+
"originalAnswer": "Das hat gedauert…"
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## No JSON-LD
|
|
152
|
+
|
|
153
|
+
There is no correct schema.org type for a journalistic interview. `QAPage` describes pages where
|
|
154
|
+
users submit answers, and marking an interview up as one would be a false declaration with a real
|
|
155
|
+
risk in search results. So this plugin emits none — unlike the
|
|
156
|
+
[review plugin](https://github.com/netz-sg/sanity-plugin-sgntech-review), where a correct type
|
|
157
|
+
exists.
|
|
158
|
+
|
|
159
|
+
## Develop
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
npm install
|
|
163
|
+
npm test # speaker resolution and timestamp parsing
|
|
164
|
+
npm run lint
|
|
165
|
+
npm run build
|
|
166
|
+
npm run link-watch
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Built with [@sanity/plugin-kit](https://github.com/sanity-io/plugins/tree/main/packages/@sanity/plugin-kit).
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT © SGNTech
|