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.
@@ -0,0 +1,36 @@
1
+ function findSpeaker(speakers, id) {
2
+ if (Array.isArray(speakers) && id)
3
+ return speakers.find((speaker) => speaker.id === id);
4
+ }
5
+ function askingSpeaker(speakers, value) {
6
+ return findSpeaker(speakers, value?.askedBy) || (speakers ?? []).find((speaker) => speaker.interviewer);
7
+ }
8
+ function speakerLabel(speaker, fallback = "\u2014") {
9
+ return speaker?.name?.trim() || speaker?.id?.trim() || fallback;
10
+ }
11
+ function timestampSeconds(input) {
12
+ const raw = (input ?? "").trim();
13
+ if (!raw) return;
14
+ const parts = raw.split(":");
15
+ if (parts.length < 2 || parts.length > 3 || !parts.every((part) => /^\d{1,3}$/.test(part))) return;
16
+ const numbers = parts.map(Number), [hours, minutes, seconds] = numbers.length === 3 ? numbers : [0, numbers[0] ?? 0, numbers[1] ?? 0];
17
+ if (!(minutes > 59 || seconds > 59))
18
+ return hours * 3600 + minutes * 60 + seconds;
19
+ }
20
+ function timestampUrl(source, seconds) {
21
+ if (!source || seconds === void 0) return source;
22
+ const separator = source.includes("?") ? "&" : "?";
23
+ return /youtu\.?be/.test(source) ? `${source}${separator}t=${seconds}` : /vimeo\./.test(source) ? `${source}#t=${seconds}s` : `${source}${separator}t=${seconds}`;
24
+ }
25
+ function usableSpeakers(speakers) {
26
+ return Array.isArray(speakers) ? speakers.filter((speaker) => !!speaker?.id?.trim()) : [];
27
+ }
28
+ export {
29
+ askingSpeaker,
30
+ findSpeaker,
31
+ speakerLabel,
32
+ timestampSeconds,
33
+ timestampUrl,
34
+ usableSpeakers
35
+ };
36
+ //# sourceMappingURL=speakers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speakers.js","sources":["../../src/lib/speakers.ts"],"sourcesContent":["import type {InterviewValue, Speaker} from './types'\n\n/**\n * The speaker a block refers to, or the first interviewer when it asks for one\n * without naming it.\n *\n * @public\n */\nexport function findSpeaker(\n speakers: Speaker[] | undefined,\n id: string | undefined,\n): Speaker | undefined {\n if (!Array.isArray(speakers)) return undefined\n if (id) return speakers.find((speaker) => speaker.id === id)\n return undefined\n}\n\n/**\n * Who is asking: the block's own choice, otherwise the first speaker marked as\n * an interviewer.\n *\n * @public\n */\nexport function askingSpeaker(\n speakers: Speaker[] | undefined,\n value: InterviewValue | undefined,\n): Speaker | undefined {\n const named = findSpeaker(speakers, value?.askedBy)\n if (named) return named\n return (speakers ?? []).find((speaker) => speaker.interviewer)\n}\n\n/**\n * What to print for a speaker, falling back through name, id and a dash so a\n * half-filled interview still renders.\n *\n * @public\n */\nexport function speakerLabel(speaker: Speaker | undefined, fallback = '—'): string {\n return speaker?.name?.trim() || speaker?.id?.trim() || fallback\n}\n\n/**\n * Turns `12:34` or `1:02:33` into seconds, for links into a recording.\n *\n * Returns `undefined` for anything it cannot read, so a typo becomes a plain\n * label rather than a link to second zero.\n *\n * @public\n */\nexport function timestampSeconds(input: string | undefined): number | undefined {\n const raw = (input ?? '').trim()\n if (!raw) return undefined\n\n const parts = raw.split(':')\n if (parts.length < 2 || parts.length > 3) return undefined\n if (!parts.every((part) => /^\\d{1,3}$/.test(part))) return undefined\n\n const numbers = parts.map(Number)\n const [hours, minutes, seconds] =\n numbers.length === 3 ? numbers : [0, numbers[0] ?? 0, numbers[1] ?? 0]\n\n if (minutes > 59 || seconds > 59) return undefined\n return hours * 3600 + minutes * 60 + seconds\n}\n\n/**\n * Adds a start time to a media URL, in the form each service expects.\n *\n * @public\n */\nexport function timestampUrl(source: string | undefined, seconds: number | undefined): string | undefined {\n if (!source || seconds === undefined) return source\n\n const separator = source.includes('?') ? '&' : '?'\n if (/youtu\\.?be/.test(source)) return `${source}${separator}t=${seconds}`\n if (/vimeo\\./.test(source)) return `${source}#t=${seconds}s`\n return `${source}${separator}t=${seconds}`\n}\n\n/**\n * Speakers that can actually be offered in a picker.\n *\n * @public\n */\nexport function usableSpeakers(speakers: Speaker[] | undefined): Speaker[] {\n if (!Array.isArray(speakers)) return []\n return speakers.filter((speaker) => Boolean(speaker?.id?.trim()))\n}\n"],"names":[],"mappings":"AAQO,SAAS,YACd,UACA,IACqB;AACrB,MAAK,MAAM,QAAQ,QAAQ,KACvB;AAAI,WAAO,SAAS,KAAK,CAAC,YAAY,QAAQ,OAAO,EAAE;AAE7D;AAQO,SAAS,cACd,UACA,OACqB;AAErB,SADc,YAAY,UAAU,OAAO,OAAO,MAE1C,YAAY,CAAA,GAAI,KAAK,CAAC,YAAY,QAAQ,WAAW;AAC/D;AAQO,SAAS,aAAa,SAA8B,WAAW,UAAa;AACjF,SAAO,SAAS,MAAM,KAAA,KAAU,SAAS,IAAI,UAAU;AACzD;AAUO,SAAS,iBAAiB,OAA+C;AAC9E,QAAM,OAAO,SAAS,IAAI,KAAA;AAC1B,MAAI,CAAC,IAAK;AAEV,QAAM,QAAQ,IAAI,MAAM,GAAG;AAE3B,MADI,MAAM,SAAS,KAAK,MAAM,SAAS,KACnC,CAAC,MAAM,MAAM,CAAC,SAAS,YAAY,KAAK,IAAI,CAAC,EAAG;AAEpD,QAAM,UAAU,MAAM,IAAI,MAAM,GAC1B,CAAC,OAAO,SAAS,OAAO,IAC5B,QAAQ,WAAW,IAAI,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;AAEvE,MAAI,EAAA,UAAU,MAAM,UAAU;AAC9B,WAAO,QAAQ,OAAO,UAAU,KAAK;AACvC;AAOO,SAAS,aAAa,QAA4B,SAAiD;AACxG,MAAI,CAAC,UAAU,YAAY,OAAW,QAAO;AAE7C,QAAM,YAAY,OAAO,SAAS,GAAG,IAAI,MAAM;AAC/C,SAAI,aAAa,KAAK,MAAM,IAAU,GAAG,MAAM,GAAG,SAAS,KAAK,OAAO,KACnE,UAAU,KAAK,MAAM,IAAU,GAAG,MAAM,MAAM,OAAO,MAClD,GAAG,MAAM,GAAG,SAAS,KAAK,OAAO;AAC1C;AAOO,SAAS,eAAe,UAA4C;AACzE,SAAK,MAAM,QAAQ,QAAQ,IACpB,SAAS,OAAO,CAAC,YAAY,CAAA,CAAQ,SAAS,IAAI,KAAA,CAAO,IAD3B,CAAA;AAEvC;"}
@@ -0,0 +1,517 @@
1
+ import { ArrayRule } from "sanity";
2
+ import { ConditionalPropertyCallbackContext } from "sanity";
3
+ import { Plugin as Plugin_2 } from "sanity";
4
+ import { StringInputProps } from "sanity";
5
+ import { StringRule } from "sanity";
6
+ import { UrlRule } from "sanity";
7
+ import { WidenInitialValue } from "sanity";
8
+ import { WidenValidation } from "sanity";
9
+
10
+ /**
11
+ * Any object with an `_type` that is a string. Can hold any other properties.
12
+ * @public
13
+ */
14
+ declare type ArbitraryTypedObject = TypedObject & {
15
+ [key: string]: any;
16
+ };
17
+
18
+ /**
19
+ * Who is asking: the block's own choice, otherwise the first speaker marked as
20
+ * an interviewer.
21
+ *
22
+ * @public
23
+ */
24
+ export declare function askingSpeaker(
25
+ speakers: Speaker[] | undefined,
26
+ value: InterviewValue | undefined,
27
+ ): Speaker | undefined;
28
+
29
+ /**
30
+ * The question-and-answer block.
31
+ *
32
+ * @public
33
+ */
34
+ export declare function createInterviewBlockType(config?: InterviewConfig): {
35
+ name: string;
36
+ title: string;
37
+ type: "object";
38
+ icon: typeof InterviewIcon;
39
+ fields: (
40
+ | ({
41
+ name: "speaker";
42
+ title: string;
43
+ type: "string";
44
+ components: {
45
+ input: typeof SpeakerInput;
46
+ };
47
+ validation: (rule: StringRule) => StringRule;
48
+ } & WidenValidation)
49
+ | ({
50
+ name: "question";
51
+ type: "string";
52
+ validation: (rule: StringRule) => StringRule;
53
+ } & WidenValidation)
54
+ | ({
55
+ name: "answer";
56
+ type: "array";
57
+ of: {
58
+ type: "block";
59
+ styles: {
60
+ title: string;
61
+ value: string;
62
+ }[];
63
+ lists: never[];
64
+ marks: {
65
+ decorators: {
66
+ title: string;
67
+ value: string;
68
+ }[];
69
+ annotations: {
70
+ type: "object";
71
+ name: "link";
72
+ title: string;
73
+ fields: ({
74
+ name: "href";
75
+ type: "url";
76
+ validation: (r: UrlRule) => UrlRule;
77
+ } & WidenValidation)[];
78
+ }[];
79
+ };
80
+ }[];
81
+ validation: (rule: ArrayRule<unknown[]>) => ArrayRule<unknown[]>;
82
+ } & WidenValidation)
83
+ | {
84
+ name: "askedBy";
85
+ title: string;
86
+ description: string;
87
+ type: "string";
88
+ components: {
89
+ input: typeof SpeakerInput;
90
+ };
91
+ }
92
+ | ({
93
+ name: "timestamp";
94
+ description: string;
95
+ type: "string";
96
+ validation: (rule: StringRule) => StringRule;
97
+ } & WidenValidation)
98
+ | ({
99
+ name: "translated";
100
+ title: string;
101
+ description: string;
102
+ type: "boolean";
103
+ initialValue: false;
104
+ } & WidenInitialValue)
105
+ | {
106
+ name: "originalAnswer";
107
+ title: string;
108
+ type: "text";
109
+ rows: number;
110
+ hidden: ({ parent }: ConditionalPropertyCallbackContext) => boolean;
111
+ }
112
+ )[];
113
+ preview: {
114
+ select: {
115
+ question: string;
116
+ speaker: string;
117
+ timestamp: string;
118
+ };
119
+ prepare: (select: Record<"speaker" | "question" | "timestamp", any>) => {
120
+ title: string;
121
+ subtitle: string;
122
+ };
123
+ };
124
+ };
125
+
126
+ /**
127
+ * The document field the blocks read their speakers from.
128
+ *
129
+ * Add it to any type that carries interviews:
130
+ *
131
+ * ```ts
132
+ * defineType({name: 'post', fields: [createSpeakersField(), /* … *\/]})
133
+ * ```
134
+ *
135
+ * @public
136
+ */
137
+ export declare function createSpeakersField(config?: InterviewConfig): {
138
+ name: string;
139
+ title: string;
140
+ description: string;
141
+ type: "array";
142
+ of: {
143
+ type: "object";
144
+ name: "speaker";
145
+ fields: (
146
+ | ({
147
+ name: "id";
148
+ title: string;
149
+ description: string;
150
+ type: "string";
151
+ validation: (rule: StringRule) => StringRule;
152
+ } & WidenValidation)
153
+ | ({
154
+ name: "name";
155
+ type: "string";
156
+ validation: (rule: StringRule) => StringRule;
157
+ } & WidenValidation)
158
+ | {
159
+ name: "role";
160
+ description: string;
161
+ type: "string";
162
+ }
163
+ | ({
164
+ name: "interviewer";
165
+ title: string;
166
+ description: string;
167
+ type: "boolean";
168
+ initialValue: false;
169
+ } & WidenInitialValue)
170
+ | {
171
+ name: "person";
172
+ title: string;
173
+ description: string;
174
+ type: "reference";
175
+ to: {
176
+ type: string;
177
+ }[];
178
+ }
179
+ )[];
180
+ preview: {
181
+ select: {
182
+ title: string;
183
+ role: string;
184
+ id: string;
185
+ interviewer: string;
186
+ };
187
+ prepare: (
188
+ select: Record<"id" | "role" | "title" | "interviewer", any>,
189
+ ) => {
190
+ title: string;
191
+ subtitle: string;
192
+ };
193
+ };
194
+ }[];
195
+ };
196
+
197
+ /**
198
+ * The speaker a block refers to, or the first interviewer when it asks for one
199
+ * without naming it.
200
+ *
201
+ * @public
202
+ */
203
+ export declare function findSpeaker(
204
+ speakers: Speaker[] | undefined,
205
+ id: string | undefined,
206
+ ): Speaker | undefined;
207
+
208
+ /**
209
+ * Adds an `interviewBlock` to Sanity Studio: one question and its answer, as a
210
+ * block in the article body, so intros, photos and section headings can sit
211
+ * between the turns the way a printed interview does.
212
+ *
213
+ * Speakers are defined once on the document with `createSpeakersField`, and
214
+ * each block picks from that list — renaming somebody fixes every turn at once.
215
+ * An answer takes bold, italic and links, and can carry a timestamp into the
216
+ * recording plus a note when it was translated rather than quoted.
217
+ *
218
+ * ```ts
219
+ * import {defineArrayMember, defineConfig, defineField} from 'sanity'
220
+ * import {createSpeakersField, interview} from 'sanity-plugin-sgntech-interview'
221
+ *
222
+ * export default defineConfig({
223
+ * plugins: [interview()],
224
+ * schema: {
225
+ * types: [
226
+ * {
227
+ * type: 'document',
228
+ * name: 'post',
229
+ * fields: [
230
+ * createSpeakersField(),
231
+ * defineField({
232
+ * type: 'array',
233
+ * name: 'body',
234
+ * of: [defineArrayMember({type: 'block'}), defineArrayMember({type: 'interviewBlock'})],
235
+ * }),
236
+ * ],
237
+ * },
238
+ * ],
239
+ * },
240
+ * })
241
+ * ```
242
+ *
243
+ * The React renderer lives under `sanity-plugin-sgntech-interview/render` and
244
+ * imports neither `sanity` nor `@sanity/ui`.
245
+ *
246
+ * @public
247
+ */
248
+ export declare const interview: Plugin_2<void | InterviewConfig>;
249
+
250
+ /**
251
+ * Configuration for the plugin.
252
+ *
253
+ * @public
254
+ */
255
+ export declare interface InterviewConfig {
256
+ /** Block type name. Defaults to `interviewBlock`. */
257
+ name?: string;
258
+ /** Label in the editor. Defaults to `Interview`. */
259
+ title?: string;
260
+ /** Field name the speakers live in. Defaults to `interviewSpeakers`. */
261
+ speakersField?: string;
262
+ /** Document types a speaker may point at, e.g. `['author', 'bandMember']`. */
263
+ speakerTypes?: string[];
264
+ /** Marks allowed in an answer. Defaults to bold, italic and link. */
265
+ answerMarks?: {
266
+ decorators?: string[];
267
+ link?: boolean;
268
+ };
269
+ /** Adds the timestamp field. Defaults to `true`. */
270
+ timestamps?: boolean;
271
+ /** Adds the translation fields. Defaults to `true`. */
272
+ translation?: boolean;
273
+ }
274
+
275
+ /**
276
+ * Two speech turns, drawn to match the stroke weight of `@sanity/icons`.
277
+ */
278
+ export declare function InterviewIcon(): React.JSX.Element;
279
+
280
+ /**
281
+ * One question and its answer.
282
+ *
283
+ * @public
284
+ */
285
+ export declare interface InterviewValue {
286
+ _key?: string;
287
+ _type?: string;
288
+ question?: string;
289
+ answer?: PortableTextBlock[];
290
+ /** Who is answering; matches {@link Speaker.id}. */
291
+ speaker?: string;
292
+ /** Who is asking, when it is not the document's default interviewer. */
293
+ askedBy?: string;
294
+ /** `12:34` or `1:02:33`, pointing into the recording. */
295
+ timestamp?: string;
296
+ /** Set when the answer was translated rather than quoted verbatim. */
297
+ translated?: boolean;
298
+ /** The answer as it was actually said. */
299
+ originalAnswer?: string;
300
+ }
301
+
302
+ /**
303
+ * A Portable Text Block can be thought of as one paragraph, quote or list item.
304
+ * In other words, it is a container for text, that can have a visual style associated with it.
305
+ * The actual text value is stored in portable text spans inside of the `childen` array.
306
+ *
307
+ * @typeParam M - Mark types that be used for text spans
308
+ * @typeParam C - Types allowed as children of this block
309
+ * @typeParam S - Allowed block styles (eg `normal`, `blockquote`, `h3` etc)
310
+ * @typeParam L - Allowed list item types (eg `number`, `bullet` etc)
311
+ * @public
312
+ */
313
+ declare interface PortableTextBlock<
314
+ M extends PortableTextMarkDefinition = PortableTextMarkDefinition,
315
+ C extends TypedObject = ArbitraryTypedObject | PortableTextSpan,
316
+ S extends string = PortableTextBlockStyle,
317
+ L extends string = PortableTextListItemType,
318
+ > extends TypedObject {
319
+ /**
320
+ * Type name identifying this as a portable text block.
321
+ * All items within a portable text array should have a `_type` property.
322
+ *
323
+ * Usually 'block', but can be customized to other values
324
+ */
325
+ _type: "block" | (string & {});
326
+ /**
327
+ * A key that identifies this block uniquely within the parent array. Used to more easily address
328
+ * the block when editing collaboratively, but is also very useful for keys inside of React and
329
+ * other rendering frameworks that can use keys to optimize operations.
330
+ */
331
+ _key?: string;
332
+ /**
333
+ * Array of inline items for this block. Usually contain text spans, but can be
334
+ * configured to include inline objects of other types as well.
335
+ */
336
+ children: C[];
337
+ /**
338
+ * Array of mark definitions used in child text spans. By having them be on the block level,
339
+ * the same mark definition can be reused for multiple text spans, which is often the case
340
+ * with nested marks.
341
+ */
342
+ markDefs?: M[];
343
+ /**
344
+ * Visual style of the block
345
+ * Common values: 'normal', 'blockquote', 'h1'...'h6'
346
+ */
347
+ style?: S;
348
+ /**
349
+ * If this block is a list item, identifies which style of list item this is
350
+ * Common values: 'bullet', 'number', but can be configured
351
+ */
352
+ listItem?: L;
353
+ /**
354
+ * If this block is a list item, identifies which level of nesting it belongs within
355
+ */
356
+ level?: number;
357
+ }
358
+
359
+ /**
360
+ * A set of _common_ (but not required/standarized) block styles
361
+ * @public
362
+ */
363
+ declare type PortableTextBlockStyle =
364
+ | "normal"
365
+ | "blockquote"
366
+ | "h1"
367
+ | "h2"
368
+ | "h3"
369
+ | "h4"
370
+ | "h5"
371
+ | "h6"
372
+ | (string & {});
373
+
374
+ /**
375
+ * A set of _common_ (but not required/standardized) list item types
376
+ * @public
377
+ */
378
+ declare type PortableTextListItemType = "bullet" | "number" | (string & {});
379
+
380
+ /**
381
+ * A mark definition holds information for marked text. For instance, a text span could reference
382
+ * a mark definition for a hyperlink, a geoposition, a reference to a document or anything that is
383
+ * representable as a JSON object.
384
+ * @public
385
+ */
386
+ declare interface PortableTextMarkDefinition {
387
+ /**
388
+ * Unknown properties
389
+ */
390
+ [key: string]: unknown;
391
+ /**
392
+ * Identifies the type of mark this is, and is used to pick the correct React components to use
393
+ * when rendering a text span marked with this mark type.
394
+ */
395
+ _type: string;
396
+ /**
397
+ * Uniquely identifies this mark definition within the block
398
+ */
399
+ _key: string;
400
+ }
401
+
402
+ /**
403
+ * A Portable Text Span holds a chunk of the actual text value of a Portable Text Block
404
+ * @public
405
+ */
406
+ declare interface PortableTextSpan {
407
+ /**
408
+ * Type is always `span` for portable text spans, as these don't vary in shape
409
+ */
410
+ _type: "span";
411
+ /**
412
+ * Unique (within parent block) key for this portable text span
413
+ */
414
+ _key?: string;
415
+ /**
416
+ * The actual text value of this text span
417
+ */
418
+ text: string;
419
+ /**
420
+ * An array of marks this text span is annotated with, identified by its `_key`.
421
+ * If the key cannot be found in the parent blocks mark definition, the mark is assumed to be a
422
+ * decorator (a simpler mark without any properties - for instance `strong` or `em`)
423
+ */
424
+ marks?: string[];
425
+ }
426
+
427
+ /**
428
+ * Someone taking part in the interview, defined once on the document.
429
+ *
430
+ * @public
431
+ */
432
+ export declare interface Speaker {
433
+ _key?: string;
434
+ /** Short id the blocks refer to, e.g. `till`. */
435
+ id?: string;
436
+ name?: string;
437
+ /** Shown under the name, e.g. `Guitarist` or `Editor`. */
438
+ role?: string;
439
+ /** Marks who is asking, so a renderer can style questions differently. */
440
+ interviewer?: boolean;
441
+ /** Set when the speaker is a reference to a person document. */
442
+ person?: {
443
+ _ref?: string;
444
+ _type?: string;
445
+ };
446
+ }
447
+
448
+ /**
449
+ * Picks one of the speakers defined on the document.
450
+ *
451
+ * @public
452
+ */
453
+ export declare function SpeakerInput(
454
+ props: StringInputProps,
455
+ ): React.JSX.Element;
456
+
457
+ /**
458
+ * What to print for a speaker, falling back through name, id and a dash so a
459
+ * half-filled interview still renders.
460
+ *
461
+ * @public
462
+ */
463
+ export declare function speakerLabel(
464
+ speaker: Speaker | undefined,
465
+ fallback?: string,
466
+ ): string;
467
+
468
+ /**
469
+ * Turns `12:34` or `1:02:33` into seconds, for links into a recording.
470
+ *
471
+ * Returns `undefined` for anything it cannot read, so a typo becomes a plain
472
+ * label rather than a link to second zero.
473
+ *
474
+ * @public
475
+ */
476
+ export declare function timestampSeconds(
477
+ input: string | undefined,
478
+ ): number | undefined;
479
+
480
+ /**
481
+ * Adds a start time to a media URL, in the form each service expects.
482
+ *
483
+ * @public
484
+ */
485
+ export declare function timestampUrl(
486
+ source: string | undefined,
487
+ seconds: number | undefined,
488
+ ): string | undefined;
489
+
490
+ /**
491
+ * Any object with an `_type` property (which is required in portable text arrays),
492
+ * as well as a _potential_ `_key` (highly encouraged)
493
+ * @public
494
+ */
495
+ declare interface TypedObject {
496
+ /**
497
+ * Identifies the type of object/span this is, and is used to pick the correct React components
498
+ * to use when rendering a span or inline object with this type.
499
+ */
500
+ _type: string;
501
+ /**
502
+ * Uniquely identifies this object within its parent block.
503
+ * Not _required_, but highly encouraged.
504
+ */
505
+ _key?: string;
506
+ }
507
+
508
+ /**
509
+ * Speakers that can actually be offered in a picker.
510
+ *
511
+ * @public
512
+ */
513
+ export declare function usableSpeakers(
514
+ speakers: Speaker[] | undefined,
515
+ ): Speaker[];
516
+
517
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ import "sanity";
2
+ import { InterviewIcon, SpeakerInput, createInterviewBlockType, createSpeakersField, interview } from "./_chunks-es/index.js";
3
+ import { askingSpeaker, findSpeaker, speakerLabel, timestampSeconds, timestampUrl, usableSpeakers } from "./_chunks-es/speakers.js";
4
+ export {
5
+ InterviewIcon,
6
+ SpeakerInput,
7
+ askingSpeaker,
8
+ createInterviewBlockType,
9
+ createSpeakersField,
10
+ findSpeaker,
11
+ interview,
12
+ speakerLabel,
13
+ timestampSeconds,
14
+ timestampUrl,
15
+ usableSpeakers
16
+ };
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}