sanity-plugin-sgntech-interview 1.0.0 → 2.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/README.md +1 -1
- package/dist/index.js +220 -3
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/dist/_chunks-es/index.js +0 -17543
- package/dist/_chunks-es/index.js.map +0 -1
- package/dist/_chunks-es/refractor.js +0 -4653
- package/dist/_chunks-es/refractor.js.map +0 -1
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
npm install sanity-plugin-sgntech-interview
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
Sanity Studio v5 or v6,
|
|
34
|
+
Sanity Studio 6.9.2 or newer (Sanity UI v4) and React 19.2. For Studio v5 or older v6 releases, install version 1.x. `@portabletext/react` is an optional peer — only the
|
|
35
35
|
renderer needs it, and only when the site does not pass its own.
|
|
36
36
|
|
|
37
37
|
## Usage
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,223 @@
|
|
|
1
|
-
import "sanity";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useFormValue, set, unset, defineArrayMember, defineField, defineType, definePlugin } from "sanity";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Card, Stack, Text, Select, Flex } from "@sanity/ui";
|
|
4
|
+
import { useCallback } from "react";
|
|
5
|
+
import { usableSpeakers } from "./_chunks-es/speakers.js";
|
|
6
|
+
import { askingSpeaker, findSpeaker, speakerLabel, timestampSeconds, timestampUrl } from "./_chunks-es/speakers.js";
|
|
7
|
+
const FIELD = "interviewSpeakers";
|
|
8
|
+
function readSpeakers(value) {
|
|
9
|
+
return Array.isArray(value) ? value.flatMap((entry) => {
|
|
10
|
+
if (!entry || typeof entry != "object") return [];
|
|
11
|
+
const id = Reflect.get(entry, "id"), name = Reflect.get(entry, "name"), role = Reflect.get(entry, "role");
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
id: typeof id == "string" ? id : void 0,
|
|
15
|
+
name: typeof name == "string" ? name : void 0,
|
|
16
|
+
role: typeof role == "string" ? role : void 0
|
|
17
|
+
}
|
|
18
|
+
];
|
|
19
|
+
}) : [];
|
|
20
|
+
}
|
|
21
|
+
function SpeakerInput(props) {
|
|
22
|
+
const { value, onChange, elementProps, schemaType } = props, options = schemaType.options, named = options && typeof options == "object" && "speakersField" in options ? Reflect.get(options, "speakersField") : void 0, field = typeof named == "string" ? named : FIELD, speakers = usableSpeakers(readSpeakers(useFormValue([field]))), handle = useCallback(
|
|
23
|
+
(event) => {
|
|
24
|
+
const next = event.currentTarget.value;
|
|
25
|
+
onChange(next ? set(next) : unset());
|
|
26
|
+
},
|
|
27
|
+
[onChange]
|
|
28
|
+
);
|
|
29
|
+
return speakers.length === 0 ? /* @__PURE__ */ jsx(Card, { padding: 3, radius: 2, border: !0, tone: "caution", children: /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
30
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "No speakers yet" }),
|
|
31
|
+
/* @__PURE__ */ jsxs(Text, { size: 1, children: [
|
|
32
|
+
"Add them in the \u201C",
|
|
33
|
+
field,
|
|
34
|
+
"\u201D field of this document, then pick one here."
|
|
35
|
+
] })
|
|
36
|
+
] }) }) : /* @__PURE__ */ jsxs(Stack, { gap: 3, children: [
|
|
37
|
+
/* @__PURE__ */ jsxs(Select, { ...elementProps, value: value ?? "", onChange: handle, children: [
|
|
38
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Not set" }),
|
|
39
|
+
speakers.map((speaker) => /* @__PURE__ */ jsxs("option", { value: speaker.id, children: [
|
|
40
|
+
speaker.name ?? speaker.id,
|
|
41
|
+
speaker.role ? ` \u2014 ${speaker.role}` : ""
|
|
42
|
+
] }, speaker.id))
|
|
43
|
+
] }),
|
|
44
|
+
value && !speakers.some((speaker) => speaker.id === value) && /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsxs(Text, { size: 0, style: { color: "var(--card-critical-fg-color)" }, children: [
|
|
45
|
+
"\u201C",
|
|
46
|
+
value,
|
|
47
|
+
"\u201D is not in the speaker list \u2014 it was renamed or removed."
|
|
48
|
+
] }) })
|
|
49
|
+
] });
|
|
50
|
+
}
|
|
51
|
+
function InterviewIcon() {
|
|
52
|
+
return /* @__PURE__ */ jsxs(
|
|
53
|
+
"svg",
|
|
54
|
+
{
|
|
55
|
+
width: "1em",
|
|
56
|
+
height: "1em",
|
|
57
|
+
viewBox: "0 0 25 25",
|
|
58
|
+
fill: "none",
|
|
59
|
+
stroke: "currentColor",
|
|
60
|
+
strokeWidth: "1.2",
|
|
61
|
+
strokeLinecap: "round",
|
|
62
|
+
strokeLinejoin: "round",
|
|
63
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
64
|
+
"aria-hidden": "true",
|
|
65
|
+
"data-sanity-icon": "interview",
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ jsx("path", { d: "M4 5.5h9a1.5 1.5 0 0 1 1.5 1.5v3A1.5 1.5 0 0 1 13 11.5H7L4.5 14v-2.5H4A1.5 1.5 0 0 1 2.5 10V7A1.5 1.5 0 0 1 4 5.5z" }),
|
|
68
|
+
/* @__PURE__ */ jsx("path", { d: "M21 12.5h-8a1.5 1.5 0 0 0-1.5 1.5v3a1.5 1.5 0 0 0 1.5 1.5h5l2.5 2.5V18.5h.5a1.5 1.5 0 0 0 1.5-1.5v-3a1.5 1.5 0 0 0-1.5-1.5z" })
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const DEFAULT_DECORATORS = ["strong", "em"];
|
|
74
|
+
function createInterviewBlockType(config = {}) {
|
|
75
|
+
const {
|
|
76
|
+
name = "interviewBlock",
|
|
77
|
+
title = "Interview",
|
|
78
|
+
timestamps = !0,
|
|
79
|
+
translation = !0,
|
|
80
|
+
answerMarks
|
|
81
|
+
} = config, decorators = (answerMarks?.decorators ?? DEFAULT_DECORATORS).map((value) => ({
|
|
82
|
+
title: value === "strong" ? "Bold" : value === "em" ? "Italic" : value,
|
|
83
|
+
value
|
|
84
|
+
})), annotations = answerMarks?.link === !1 ? [] : [
|
|
85
|
+
defineArrayMember({
|
|
86
|
+
type: "object",
|
|
87
|
+
name: "link",
|
|
88
|
+
title: "Link",
|
|
89
|
+
fields: [defineField({ name: "href", type: "url", validation: (r) => r.required() })]
|
|
90
|
+
})
|
|
91
|
+
];
|
|
92
|
+
return defineType({
|
|
93
|
+
name,
|
|
94
|
+
title,
|
|
95
|
+
type: "object",
|
|
96
|
+
icon: InterviewIcon,
|
|
97
|
+
fields: [
|
|
98
|
+
defineField({
|
|
99
|
+
name: "speaker",
|
|
100
|
+
title: "Answered by",
|
|
101
|
+
type: "string",
|
|
102
|
+
components: { input: SpeakerInput },
|
|
103
|
+
validation: (rule) => rule.required()
|
|
104
|
+
}),
|
|
105
|
+
defineField({
|
|
106
|
+
name: "question",
|
|
107
|
+
type: "string",
|
|
108
|
+
validation: (rule) => rule.required()
|
|
109
|
+
}),
|
|
110
|
+
defineField({
|
|
111
|
+
name: "answer",
|
|
112
|
+
type: "array",
|
|
113
|
+
of: [
|
|
114
|
+
defineArrayMember({
|
|
115
|
+
type: "block",
|
|
116
|
+
styles: [{ title: "Normal", value: "normal" }],
|
|
117
|
+
lists: [],
|
|
118
|
+
marks: { decorators, annotations }
|
|
119
|
+
})
|
|
120
|
+
],
|
|
121
|
+
validation: (rule) => rule.required()
|
|
122
|
+
}),
|
|
123
|
+
defineField({
|
|
124
|
+
name: "askedBy",
|
|
125
|
+
title: "Asked by",
|
|
126
|
+
description: "Only needed when somebody other than the usual interviewer asks.",
|
|
127
|
+
type: "string",
|
|
128
|
+
components: { input: SpeakerInput }
|
|
129
|
+
}),
|
|
130
|
+
...timestamps ? [
|
|
131
|
+
defineField({
|
|
132
|
+
name: "timestamp",
|
|
133
|
+
description: "Where this sits in the recording, as 12:34 or 1:02:33.",
|
|
134
|
+
type: "string",
|
|
135
|
+
validation: (rule) => rule.regex(/^\d{1,3}:\d{2}(:\d{2})?$/, { name: "mm:ss or hh:mm:ss" })
|
|
136
|
+
})
|
|
137
|
+
] : [],
|
|
138
|
+
...translation ? [
|
|
139
|
+
defineField({
|
|
140
|
+
name: "translated",
|
|
141
|
+
title: "Answer was translated",
|
|
142
|
+
description: "A translated answer is not a verbatim quote \u2014 say so.",
|
|
143
|
+
type: "boolean",
|
|
144
|
+
initialValue: !1
|
|
145
|
+
}),
|
|
146
|
+
defineField({
|
|
147
|
+
name: "originalAnswer",
|
|
148
|
+
title: "Original wording",
|
|
149
|
+
type: "text",
|
|
150
|
+
rows: 3,
|
|
151
|
+
hidden: ({ parent }) => parent?.translated !== !0
|
|
152
|
+
})
|
|
153
|
+
] : []
|
|
154
|
+
],
|
|
155
|
+
preview: {
|
|
156
|
+
select: { question: "question", speaker: "speaker", timestamp: "timestamp" },
|
|
157
|
+
prepare: (select) => ({
|
|
158
|
+
title: typeof select.question == "string" ? select.question : "Question",
|
|
159
|
+
subtitle: [select.speaker, select.timestamp].filter(Boolean).join(" \xB7 ")
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function createSpeakersField(config = {}) {
|
|
165
|
+
const { speakersField = "interviewSpeakers", speakerTypes = [] } = config;
|
|
166
|
+
return defineField({
|
|
167
|
+
name: speakersField,
|
|
168
|
+
title: "Interview speakers",
|
|
169
|
+
description: "Everyone taking part. Blocks pick from this list, so a name is only typed once.",
|
|
170
|
+
type: "array",
|
|
171
|
+
of: [
|
|
172
|
+
defineArrayMember({
|
|
173
|
+
type: "object",
|
|
174
|
+
name: "speaker",
|
|
175
|
+
fields: [
|
|
176
|
+
defineField({
|
|
177
|
+
name: "id",
|
|
178
|
+
title: "Short id",
|
|
179
|
+
description: "Used by the blocks, e.g. `till`. Lowercase, no spaces.",
|
|
180
|
+
type: "string",
|
|
181
|
+
validation: (rule) => rule.required().regex(/^[a-z0-9-]+$/, { name: "lowercase letters, digits and dashes" })
|
|
182
|
+
}),
|
|
183
|
+
defineField({ name: "name", type: "string", validation: (rule) => rule.required() }),
|
|
184
|
+
defineField({
|
|
185
|
+
name: "role",
|
|
186
|
+
description: "Shown under the name, e.g. Vocals or Editor.",
|
|
187
|
+
type: "string"
|
|
188
|
+
}),
|
|
189
|
+
defineField({
|
|
190
|
+
name: "interviewer",
|
|
191
|
+
title: "Asks the questions",
|
|
192
|
+
description: "Blocks without their own asker fall back to the first one marked here.",
|
|
193
|
+
type: "boolean",
|
|
194
|
+
initialValue: !1
|
|
195
|
+
}),
|
|
196
|
+
...speakerTypes.length > 0 ? [
|
|
197
|
+
defineField({
|
|
198
|
+
name: "person",
|
|
199
|
+
title: "Person",
|
|
200
|
+
description: "Optional link to an existing document.",
|
|
201
|
+
type: "reference",
|
|
202
|
+
to: speakerTypes.map((type) => ({ type }))
|
|
203
|
+
})
|
|
204
|
+
] : []
|
|
205
|
+
],
|
|
206
|
+
preview: {
|
|
207
|
+
select: { title: "name", role: "role", id: "id", interviewer: "interviewer" },
|
|
208
|
+
prepare: (select) => ({
|
|
209
|
+
title: typeof select.title == "string" ? select.title : "Speaker",
|
|
210
|
+
subtitle: [select.id, select.role, select.interviewer ? "asks" : void 0].filter(Boolean).join(" \xB7 ")
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
]
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
const interview = definePlugin((config) => ({
|
|
218
|
+
name: "sanity-plugin-sgntech-interview",
|
|
219
|
+
schema: { types: [createInterviewBlockType(config || {})] }
|
|
220
|
+
}));
|
|
4
221
|
export {
|
|
5
222
|
InterviewIcon,
|
|
6
223
|
SpeakerInput,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/components/SpeakerInput.tsx","../src/components/InterviewIcon.tsx","../src/schema/interviewBlock.ts","../src/schema/speakers.ts","../src/index.ts"],"sourcesContent":["import {Card, Flex, Select, Stack, Text} from '@sanity/ui'\nimport {useCallback} from 'react'\nimport {set, type StringInputProps, unset, useFormValue} from 'sanity'\n\nimport {usableSpeakers} from '../lib/speakers'\nimport type {Speaker} from '../lib/types'\n\nconst FIELD = 'interviewSpeakers'\n\nfunction readSpeakers(value: unknown): Speaker[] {\n if (!Array.isArray(value)) return []\n\n return value.flatMap((entry) => {\n if (!entry || typeof entry !== 'object') return []\n const id = Reflect.get(entry, 'id')\n const name = Reflect.get(entry, 'name')\n const role = Reflect.get(entry, 'role')\n return [\n {\n id: typeof id === 'string' ? id : undefined,\n name: typeof name === 'string' ? name : undefined,\n role: typeof role === 'string' ? role : undefined,\n },\n ]\n })\n}\n\n/**\n * Picks one of the speakers defined on the document.\n *\n * @public\n */\nexport function SpeakerInput(props: StringInputProps): React.JSX.Element {\n const {value, onChange, elementProps, schemaType} = props\n\n const options: unknown = schemaType.options\n const named =\n options && typeof options === 'object' && 'speakersField' in options\n ? Reflect.get(options, 'speakersField')\n : undefined\n const field = typeof named === 'string' ? named : FIELD\n\n const speakers = usableSpeakers(readSpeakers(useFormValue([field])))\n\n const handle = useCallback(\n (event: React.ChangeEvent<HTMLSelectElement>) => {\n const next = event.currentTarget.value\n onChange(next ? set(next) : unset())\n },\n [onChange],\n )\n\n if (speakers.length === 0) {\n return (\n <Card padding={3} radius={2} border tone=\"caution\">\n <Stack gap={3}>\n <Text size={1} weight=\"medium\">\n No speakers yet\n </Text>\n <Text size={1}>\n Add them in the “{field}” field of this document, then pick one here.\n </Text>\n </Stack>\n </Card>\n )\n }\n\n return (\n <Stack gap={3}>\n <Select {...elementProps} value={value ?? ''} onChange={handle}>\n <option value=\"\">Not set</option>\n {speakers.map((speaker) => (\n <option key={speaker.id} value={speaker.id}>\n {speaker.name ?? speaker.id}\n {speaker.role ? ` — ${speaker.role}` : ''}\n </option>\n ))}\n </Select>\n\n {value && !speakers.some((speaker) => speaker.id === value) && (\n <Flex>\n <Text size={0} style={{color: 'var(--card-critical-fg-color)'}}>\n “{value}” is not in the speaker list — it was renamed or removed.\n </Text>\n </Flex>\n )}\n </Stack>\n )\n}\n","/**\n * Two speech turns, drawn to match the stroke weight of `@sanity/icons`.\n */\nexport function InterviewIcon(): React.JSX.Element {\n return (\n <svg\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 25 25\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n data-sanity-icon=\"interview\"\n >\n <path d=\"M4 5.5h9a1.5 1.5 0 0 1 1.5 1.5v3A1.5 1.5 0 0 1 13 11.5H7L4.5 14v-2.5H4A1.5 1.5 0 0 1 2.5 10V7A1.5 1.5 0 0 1 4 5.5z\" />\n <path d=\"M21 12.5h-8a1.5 1.5 0 0 0-1.5 1.5v3a1.5 1.5 0 0 0 1.5 1.5h5l2.5 2.5V18.5h.5a1.5 1.5 0 0 0 1.5-1.5v-3a1.5 1.5 0 0 0-1.5-1.5z\" />\n </svg>\n )\n}\n","import {defineArrayMember, defineField, defineType} from 'sanity'\n\nimport {SpeakerInput} from '../components/SpeakerInput'\nimport {InterviewIcon} from '../components/InterviewIcon'\nimport type {InterviewConfig} from '../lib/types'\n\nconst DEFAULT_DECORATORS = ['strong', 'em']\n\n/**\n * The question-and-answer block.\n *\n * @public\n */\nexport function createInterviewBlockType(config: InterviewConfig = {}) {\n const {\n name = 'interviewBlock',\n title = 'Interview',\n timestamps = true,\n translation = true,\n answerMarks,\n } = config\n\n const decorators = (answerMarks?.decorators ?? DEFAULT_DECORATORS).map((value) => ({\n title: value === 'strong' ? 'Bold' : value === 'em' ? 'Italic' : value,\n value,\n }))\n\n const annotations =\n answerMarks?.link === false\n ? []\n : [\n defineArrayMember({\n type: 'object',\n name: 'link',\n title: 'Link',\n fields: [defineField({name: 'href', type: 'url', validation: (r) => r.required()})],\n }),\n ]\n\n return defineType({\n name,\n title,\n type: 'object',\n icon: InterviewIcon,\n fields: [\n defineField({\n name: 'speaker',\n title: 'Answered by',\n type: 'string',\n components: {input: SpeakerInput},\n validation: (rule) => rule.required(),\n }),\n\n defineField({\n name: 'question',\n type: 'string',\n validation: (rule) => rule.required(),\n }),\n\n defineField({\n name: 'answer',\n type: 'array',\n of: [\n defineArrayMember({\n type: 'block',\n styles: [{title: 'Normal', value: 'normal'}],\n lists: [],\n marks: {decorators, annotations},\n }),\n ],\n validation: (rule) => rule.required(),\n }),\n\n defineField({\n name: 'askedBy',\n title: 'Asked by',\n description: 'Only needed when somebody other than the usual interviewer asks.',\n type: 'string',\n components: {input: SpeakerInput},\n }),\n\n ...(timestamps\n ? [\n defineField({\n name: 'timestamp',\n description: 'Where this sits in the recording, as 12:34 or 1:02:33.',\n type: 'string',\n validation: (rule) =>\n rule.regex(/^\\d{1,3}:\\d{2}(:\\d{2})?$/, {name: 'mm:ss or hh:mm:ss'}),\n }),\n ]\n : []),\n\n ...(translation\n ? [\n defineField({\n name: 'translated',\n title: 'Answer was translated',\n description: 'A translated answer is not a verbatim quote — say so.',\n type: 'boolean',\n initialValue: false,\n }),\n defineField({\n name: 'originalAnswer',\n title: 'Original wording',\n type: 'text',\n rows: 3,\n hidden: ({parent}) => parent?.translated !== true,\n }),\n ]\n : []),\n ],\n\n preview: {\n select: {question: 'question', speaker: 'speaker', timestamp: 'timestamp'},\n prepare: (select) => ({\n title: typeof select.question === 'string' ? select.question : 'Question',\n subtitle: [select.speaker, select.timestamp].filter(Boolean).join(' · '),\n }),\n },\n })\n}\n","import {defineArrayMember, defineField} from 'sanity'\n\nimport type {InterviewConfig} from '../lib/types'\n\n/**\n * The document field the blocks read their speakers from.\n *\n * Add it to any type that carries interviews:\n *\n * ```ts\n * defineType({name: 'post', fields: [createSpeakersField(), /* … *\\/]})\n * ```\n *\n * @public\n */\nexport function createSpeakersField(config: InterviewConfig = {}) {\n const {speakersField = 'interviewSpeakers', speakerTypes = []} = config\n\n return defineField({\n name: speakersField,\n title: 'Interview speakers',\n description: 'Everyone taking part. Blocks pick from this list, so a name is only typed once.',\n type: 'array',\n of: [\n defineArrayMember({\n type: 'object',\n name: 'speaker',\n fields: [\n defineField({\n name: 'id',\n title: 'Short id',\n description: 'Used by the blocks, e.g. `till`. Lowercase, no spaces.',\n type: 'string',\n validation: (rule) =>\n rule.required().regex(/^[a-z0-9-]+$/, {name: 'lowercase letters, digits and dashes'}),\n }),\n defineField({name: 'name', type: 'string', validation: (rule) => rule.required()}),\n defineField({\n name: 'role',\n description: 'Shown under the name, e.g. Vocals or Editor.',\n type: 'string',\n }),\n defineField({\n name: 'interviewer',\n title: 'Asks the questions',\n description: 'Blocks without their own asker fall back to the first one marked here.',\n type: 'boolean',\n initialValue: false,\n }),\n ...(speakerTypes.length > 0\n ? [\n defineField({\n name: 'person',\n title: 'Person',\n description: 'Optional link to an existing document.',\n type: 'reference',\n to: speakerTypes.map((type) => ({type})),\n }),\n ]\n : []),\n ],\n preview: {\n select: {title: 'name', role: 'role', id: 'id', interviewer: 'interviewer'},\n prepare: (select) => ({\n title: typeof select.title === 'string' ? select.title : 'Speaker',\n subtitle: [select.id, select.role, select.interviewer ? 'asks' : undefined]\n .filter(Boolean)\n .join(' · '),\n }),\n },\n }),\n ],\n })\n}\n","import {definePlugin} from 'sanity'\n\nimport type {InterviewConfig} from './lib/types'\nimport {createInterviewBlockType} from './schema/interviewBlock'\n\n/**\n * Adds an `interviewBlock` to Sanity Studio: one question and its answer, as a\n * block in the article body, so intros, photos and section headings can sit\n * between the turns the way a printed interview does.\n *\n * Speakers are defined once on the document with `createSpeakersField`, and\n * each block picks from that list — renaming somebody fixes every turn at once.\n * An answer takes bold, italic and links, and can carry a timestamp into the\n * recording plus a note when it was translated rather than quoted.\n *\n * ```ts\n * import {defineArrayMember, defineConfig, defineField} from 'sanity'\n * import {createSpeakersField, interview} from 'sanity-plugin-sgntech-interview'\n *\n * export default defineConfig({\n * plugins: [interview()],\n * schema: {\n * types: [\n * {\n * type: 'document',\n * name: 'post',\n * fields: [\n * createSpeakersField(),\n * defineField({\n * type: 'array',\n * name: 'body',\n * of: [defineArrayMember({type: 'block'}), defineArrayMember({type: 'interviewBlock'})],\n * }),\n * ],\n * },\n * ],\n * },\n * })\n * ```\n *\n * The React renderer lives under `sanity-plugin-sgntech-interview/render` and\n * imports neither `sanity` nor `@sanity/ui`.\n *\n * @public\n */\nexport const interview = definePlugin<InterviewConfig | void>((config) => {\n const options = config || {}\n\n return {\n name: 'sanity-plugin-sgntech-interview',\n schema: {types: [createInterviewBlockType(options)]},\n }\n})\n\nexport {InterviewIcon} from './components/InterviewIcon'\nexport {SpeakerInput} from './components/SpeakerInput'\nexport {\n askingSpeaker,\n findSpeaker,\n speakerLabel,\n timestampSeconds,\n timestampUrl,\n usableSpeakers,\n} from './lib/speakers'\nexport type {InterviewConfig, InterviewValue, Speaker} from './lib/types'\nexport {createInterviewBlockType} from './schema/interviewBlock'\nexport {createSpeakersField} from './schema/speakers'\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-sgntech-interview",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Interview blocks for Sanity Studio — question and answer pairs with speakers, timestamps and a note when an answer was translated.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
"watch": "pkg-utils watch --strict"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@sanity/icons": "^5.2.
|
|
50
|
+
"@sanity/icons": "^5.2.3",
|
|
51
|
+
"@sanity/ui": "^4.4.10"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
54
|
"@sanity/browserslist-config": "^1.0.5",
|
|
@@ -57,9 +58,9 @@
|
|
|
57
58
|
"oxfmt": "^0.61.0",
|
|
58
59
|
"oxlint": "^1.77.0",
|
|
59
60
|
"oxlint-tsgolint": "^7.0.2001",
|
|
60
|
-
"react": "^19.
|
|
61
|
-
"react-dom": "^19.
|
|
62
|
-
"sanity": "^6.
|
|
61
|
+
"react": "^19.3.1",
|
|
62
|
+
"react-dom": "^19.3.1",
|
|
63
|
+
"sanity": "^6.20.0",
|
|
63
64
|
"styled-components": "^6.5.0",
|
|
64
65
|
"typescript": "^6.0.3",
|
|
65
66
|
"vitest": "^3.2.4",
|
|
@@ -67,8 +68,8 @@
|
|
|
67
68
|
"@portabletext/types": "^4.0.2"
|
|
68
69
|
},
|
|
69
70
|
"peerDependencies": {
|
|
70
|
-
"react": "^
|
|
71
|
-
"sanity": "^
|
|
71
|
+
"react": "^19.2",
|
|
72
|
+
"sanity": "^6.9.2",
|
|
72
73
|
"@portabletext/react": "^3 || ^4 || ^5 || ^6 || ^7"
|
|
73
74
|
},
|
|
74
75
|
"engines": {
|