@dataimago/interview 0.2.0-alpha.0 → 0.2.0-alpha.1
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/dist/index.cjs +513 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -74
- package/dist/index.d.ts +152 -74
- package/dist/index.js +509 -117
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -23,38 +23,108 @@ declare function ProgressBar({ current, total, estimatedMinutesRemaining }: Prop
|
|
|
23
23
|
* @dataimago/interview — schema contract.
|
|
24
24
|
*
|
|
25
25
|
* The types in this file are the package's public contract. Consumers
|
|
26
|
-
* (
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* package render whatever the consumer provides.
|
|
26
|
+
* (Level-2 hubs like dissertation-ai, future sgpc-ai, future rpkg-ai)
|
|
27
|
+
* populate the actual question content conforming to these shapes; the
|
|
28
|
+
* components in this package render whatever the consumer provides.
|
|
30
29
|
*
|
|
31
|
-
*
|
|
32
|
-
* `
|
|
33
|
-
*
|
|
34
|
-
*
|
|
30
|
+
* Originally extracted from `apps/hub/src/lib/interview-questions.ts` +
|
|
31
|
+
* `apps/hub/src/lib/interview-store.ts` during iteration-3-B.1 (2026-05-07).
|
|
32
|
+
* Extended for the dissertation-ai use case during D.2.1.b (2026-06-02) —
|
|
33
|
+
* see CHANGELOG for the new question types and the `select` → `single-select`
|
|
34
|
+
* rename.
|
|
35
35
|
*
|
|
36
36
|
* Design authority: `dataimago-design/wiki/patterns/onboarding-interview.md`.
|
|
37
37
|
*/
|
|
38
38
|
/**
|
|
39
39
|
* The set of input shapes the interview engine knows how to render.
|
|
40
40
|
*
|
|
41
|
+
* Scalar-answer types (value is a `string` or `string[]`):
|
|
41
42
|
* - `short-text` — single-line text field
|
|
42
43
|
* - `long-text` — multi-line textarea
|
|
43
|
-
* - `select` — single-choice radio group with `options[]`
|
|
44
|
+
* - `single-select` — single-choice radio group with `options[]`
|
|
45
|
+
* *(Renamed from `select` in v0.3; `select` is no longer recognized.)*
|
|
44
46
|
* - `multi-select` — multi-choice checkbox group with `options[]`
|
|
45
|
-
* - `scale` — numeric slider 1–N
|
|
46
|
-
* labels; engine renders as a horizontal scale)
|
|
47
|
+
* - `scale` — numeric slider 1–N
|
|
47
48
|
* - `list` — variable-length list of short-text items, bounded by `listRange`
|
|
49
|
+
* - `date` — single ISO 8601 date string (`<input type="date">`)
|
|
50
|
+
*
|
|
51
|
+
* Compound-answer types (value is a `Record` or `Record[]`):
|
|
52
|
+
* - `composite` — fixed set of sub-questions answered once. Value is
|
|
53
|
+
* `Record<subQuestionId, AnswerValue>`. Sub-questions live on
|
|
54
|
+
* `InterviewQuestion.subQuestions`.
|
|
55
|
+
* - `repeater` — variable-length list of records, each conforming to
|
|
56
|
+
* `itemSchema`. Value is `Array<Record<itemSchemaId, AnswerValue>>`. The
|
|
57
|
+
* user clicks "Add another" to add rows.
|
|
58
|
+
* - `file-upload` — like `repeater` but each row is anchored to an uploaded
|
|
59
|
+
* File. Value is `Array<FileEntry>`. The package's renderer collects
|
|
60
|
+
* `File` objects + per-file metadata; the actual upload-to-server
|
|
61
|
+
* pipeline is the consumer's responsibility (the package only collects
|
|
62
|
+
* answer state).
|
|
48
63
|
*/
|
|
49
|
-
type QuestionInputType = 'short-text' | 'long-text' | 'select' | 'multi-select' | 'scale' | 'list';
|
|
64
|
+
type QuestionInputType = 'short-text' | 'long-text' | 'single-select' | 'multi-select' | 'scale' | 'list' | 'date' | 'composite' | 'repeater' | 'file-upload';
|
|
65
|
+
/**
|
|
66
|
+
* The runtime value carried by a single answer. The shape depends on the
|
|
67
|
+
* question's `inputType`:
|
|
68
|
+
*
|
|
69
|
+
* | inputType | Value shape |
|
|
70
|
+
* | ---------------- | -------------------------------------- |
|
|
71
|
+
* | short-text | `string` |
|
|
72
|
+
* | long-text | `string` |
|
|
73
|
+
* | single-select | `string` (the selected option's value) |
|
|
74
|
+
* | multi-select | `string[]` |
|
|
75
|
+
* | scale | `string` (numeric string) |
|
|
76
|
+
* | list | `string[]` |
|
|
77
|
+
* | date | `string` (ISO 8601 date) |
|
|
78
|
+
* | composite | `AnswerComposite` |
|
|
79
|
+
* | repeater | `AnswerRepeater` |
|
|
80
|
+
* | file-upload | `AnswerFileUpload` |
|
|
81
|
+
*
|
|
82
|
+
* `undefined` is used to mean "not yet answered."
|
|
83
|
+
*/
|
|
84
|
+
type AnswerValue = string | string[] | AnswerComposite | AnswerRepeater | AnswerFileUpload | undefined;
|
|
85
|
+
/** Composite answer: a record keyed by sub-question id. */
|
|
86
|
+
type AnswerComposite = {
|
|
87
|
+
[subQuestionId: string]: AnswerValue;
|
|
88
|
+
};
|
|
89
|
+
/** Repeater answer: an array of records, each keyed by itemSchema sub-question id. */
|
|
90
|
+
type AnswerRepeater = Array<{
|
|
91
|
+
[itemSchemaId: string]: AnswerValue;
|
|
92
|
+
}>;
|
|
93
|
+
/**
|
|
94
|
+
* A single uploaded file entry for `file-upload` questions.
|
|
95
|
+
*
|
|
96
|
+
* - At interview time, `file` is a browser `File` object (from drag-and-drop
|
|
97
|
+
* or `<input type="file">`).
|
|
98
|
+
* - After the consumer uploads it (out-of-band), `file` may be replaced with
|
|
99
|
+
* a server-assigned descriptor (e.g., `{ name, path, size }`). The
|
|
100
|
+
* package treats anything matching the descriptor shape as "uploaded".
|
|
101
|
+
* - `metadata` carries per-file answers conforming to the question's
|
|
102
|
+
* `itemSchema` (e.g., title, author, description).
|
|
103
|
+
*/
|
|
104
|
+
type FileEntry = {
|
|
105
|
+
file: File | {
|
|
106
|
+
name: string;
|
|
107
|
+
size: number;
|
|
108
|
+
path?: string;
|
|
109
|
+
type?: string;
|
|
110
|
+
};
|
|
111
|
+
metadata: {
|
|
112
|
+
[itemSchemaId: string]: AnswerValue;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
type AnswerFileUpload = FileEntry[];
|
|
50
116
|
/**
|
|
51
117
|
* A single question in an interview flow. Consumers provide an array of
|
|
52
118
|
* these to drive the engine.
|
|
53
119
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
120
|
+
* Field availability by `inputType`:
|
|
121
|
+
*
|
|
122
|
+
* | Field | Required for |
|
|
123
|
+
* | --------------- | --------------------------------------------- |
|
|
124
|
+
* | options | single-select, multi-select, scale |
|
|
125
|
+
* | listRange | list (optional bounds) |
|
|
126
|
+
* | subQuestions | composite (the inline sub-question fields) |
|
|
127
|
+
* | itemSchema | repeater + file-upload (per-row sub-questions)|
|
|
58
128
|
*/
|
|
59
129
|
interface InterviewQuestion {
|
|
60
130
|
/** Stable unique identifier; used as the answer-record key. */
|
|
@@ -69,7 +139,7 @@ interface InterviewQuestion {
|
|
|
69
139
|
examples?: string[];
|
|
70
140
|
/** Which input shape to render. */
|
|
71
141
|
inputType: QuestionInputType;
|
|
72
|
-
/** Required for select / multi-select / scale; ignored
|
|
142
|
+
/** Required for single-select / multi-select / scale; ignored otherwise. */
|
|
73
143
|
options?: Array<{
|
|
74
144
|
value: string;
|
|
75
145
|
label: string;
|
|
@@ -90,17 +160,34 @@ interface InterviewQuestion {
|
|
|
90
160
|
min: number;
|
|
91
161
|
max: number;
|
|
92
162
|
};
|
|
163
|
+
/**
|
|
164
|
+
* Composite-only: the fixed set of sub-questions that get answered as part
|
|
165
|
+
* of this composite question. Their answers land at
|
|
166
|
+
* `answers[parentId][subQuestionId]`.
|
|
167
|
+
*/
|
|
168
|
+
subQuestions?: InterviewQuestion[];
|
|
169
|
+
/**
|
|
170
|
+
* Repeater + file-upload: the per-row sub-question schema. Each row of the
|
|
171
|
+
* answer array is `Record<itemSchemaId, AnswerValue>`.
|
|
172
|
+
*/
|
|
173
|
+
itemSchema?: InterviewQuestion[];
|
|
93
174
|
}
|
|
94
175
|
/**
|
|
95
|
-
* The runtime answer record. Keys are `InterviewQuestion.id`; values
|
|
96
|
-
*
|
|
97
|
-
* an array of strings (for multi-select, list).
|
|
176
|
+
* The runtime answer record. Keys are `InterviewQuestion.id`; values
|
|
177
|
+
* conform to the per-`inputType` shape described in `AnswerValue` above.
|
|
98
178
|
*
|
|
99
179
|
* Consumers manage their own state container (Zustand, Redux, server-side
|
|
100
|
-
* session, etc.) and pass the relevant slice into the components
|
|
101
|
-
* The engine is state-management-agnostic.
|
|
180
|
+
* session, useState, etc.) and pass the relevant slice into the components
|
|
181
|
+
* as props. The engine is state-management-agnostic.
|
|
182
|
+
*
|
|
183
|
+
* Consumers may narrow this to their domain-specific answer shape (e.g.,
|
|
184
|
+
* `dissertation-ai/apps/hub/src/lib/interview/answers.ts` defines a strict
|
|
185
|
+
* typed `InterviewAnswers`). This package's type is the loose lowest
|
|
186
|
+
* common denominator.
|
|
102
187
|
*/
|
|
103
|
-
type InterviewAnswers =
|
|
188
|
+
type InterviewAnswers = {
|
|
189
|
+
[questionId: string]: AnswerValue;
|
|
190
|
+
};
|
|
104
191
|
|
|
105
192
|
interface Props$2 {
|
|
106
193
|
question: InterviewQuestion;
|
|
@@ -109,14 +196,13 @@ interface Props$2 {
|
|
|
109
196
|
* state container). The component initializes its local form state
|
|
110
197
|
* from this value and re-syncs when the question id changes.
|
|
111
198
|
*/
|
|
112
|
-
existingAnswer?:
|
|
199
|
+
existingAnswer?: AnswerValue;
|
|
113
200
|
/**
|
|
114
|
-
* Called when the user submits
|
|
115
|
-
* the question is `required: false`). The consumer is expected to
|
|
201
|
+
* Called when the user submits an answer. The consumer is expected to
|
|
116
202
|
* persist the answer into its state container and then advance via
|
|
117
203
|
* `onNext`.
|
|
118
204
|
*/
|
|
119
|
-
onAnswerSubmit: (questionId: string, value:
|
|
205
|
+
onAnswerSubmit: (questionId: string, value: AnswerValue) => void;
|
|
120
206
|
/** Called after `onAnswerSubmit` to advance to the next question. */
|
|
121
207
|
onNext: () => void;
|
|
122
208
|
/** Called when the user clicks the Back button. */
|
|
@@ -126,34 +212,17 @@ interface Props$2 {
|
|
|
126
212
|
}
|
|
127
213
|
/**
|
|
128
214
|
* Single question renderer. Handles all input types: short-text, long-text,
|
|
129
|
-
* select, multi-select, scale, list
|
|
130
|
-
* safe.
|
|
215
|
+
* single-select, multi-select, scale, list, date, composite, repeater,
|
|
216
|
+
* file-upload. Keyboard-navigable and screen-reader safe.
|
|
131
217
|
*
|
|
132
|
-
* **Decoupled from any state-management library.**
|
|
133
|
-
*
|
|
134
|
-
* extracted version receives the existing answer + change handler as
|
|
135
|
-
* props, leaving the consumer free to use Zustand, Redux, useState,
|
|
136
|
-
* server-side session state, or any other approach.
|
|
218
|
+
* **Decoupled from any state-management library.** Consumers receive the
|
|
219
|
+
* existing answer + change handler as props.
|
|
137
220
|
*
|
|
138
|
-
* Iteration-1 visual identity
|
|
139
|
-
*
|
|
140
|
-
* + forest-700 focus border (interactive-primary focus indicator). Selected
|
|
221
|
+
* Iteration-1 visual identity: form input fields use stone-50 elevated
|
|
222
|
+
* surface + stone-200 default border + forest-700 focus border. Selected
|
|
141
223
|
* radio/checkbox option states use forest-700 border + forest-50 background.
|
|
142
|
-
* The examples disclosure has
|
|
143
|
-
*
|
|
144
|
-
* etymology box and ReviewSummary's "what happens next" callout, per
|
|
145
|
-
* iteration-1 §5.2's preserved-decorative-copper governance.
|
|
146
|
-
*
|
|
147
|
-
* Iteration-2 role-shift (2026-05-07): copper is no longer the brand-accent
|
|
148
|
-
* moral-weight scale (the brand accent moved to sky-blue accent-500 #1f618d).
|
|
149
|
-
* Copper survives as the "warm decorative framing on the editorial scale" —
|
|
150
|
-
* a distinct decorative palette, not subordinate to the brand accent. The
|
|
151
|
-
* border-copper-200 site below continues to serve its visual role (warm
|
|
152
|
-
* framing for examples-list editorial content); its semantic relationship
|
|
153
|
-
* to the brand accent is now zero. See wiki/decisions/iteration-2-blue-accent.md.
|
|
154
|
-
*
|
|
155
|
-
* Extracted from `apps/hub/src/components/interview/QuestionCard.tsx`
|
|
156
|
-
* during iteration-3-B.1 (2026-05-07). Decoupled from `useInterviewStore`.
|
|
224
|
+
* The examples disclosure has border-l-2 frame in border-copper-200 —
|
|
225
|
+
* preserved-decorative-copper role per iteration-2-blue-accent.md.
|
|
157
226
|
*/
|
|
158
227
|
declare function QuestionCard({ question, existingAnswer, onAnswerSubmit, onNext, onBack, isFirst, isLast, }: Props$2): react_jsx_runtime.JSX.Element;
|
|
159
228
|
|
|
@@ -170,9 +239,8 @@ interface Props$1 {
|
|
|
170
239
|
* question without losing later answers. Implements the wiki's pattern:
|
|
171
240
|
* "Edit any prior answer — a sidebar shows all answered questions."
|
|
172
241
|
*
|
|
173
|
-
* **Decoupled from any state-management library.**
|
|
174
|
-
*
|
|
175
|
-
* extracted version receives the answers + jump handler as props.
|
|
242
|
+
* **Decoupled from any state-management library.** Consumers receive the
|
|
243
|
+
* answers + jump handler as props.
|
|
176
244
|
*
|
|
177
245
|
* Iteration-1 visual identity (Phase B.2, 2026-05-06):
|
|
178
246
|
* Three item states with distinct token tiers:
|
|
@@ -180,8 +248,9 @@ interface Props$1 {
|
|
|
180
248
|
* - answered → stone-200 border + stone-50 bg (default, hover stone-400)
|
|
181
249
|
* - unanswered → stone-200/50 + stone-50/50 (muted via opacity preservation)
|
|
182
250
|
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
251
|
+
* D.2.1.b extension: previews for composite/repeater/file-upload answers
|
|
252
|
+
* delegated to `summarize.ts` so all eight scalar + three compound types
|
|
253
|
+
* render consistently.
|
|
185
254
|
*/
|
|
186
255
|
declare function AnswerSidebar({ questions, currentIndex, answers, onSelectQuestion }: Props$1): react_jsx_runtime.JSX.Element;
|
|
187
256
|
|
|
@@ -198,28 +267,37 @@ interface Props {
|
|
|
198
267
|
* Shows all answers so the user can confirm ownership of the output before
|
|
199
268
|
* committing.
|
|
200
269
|
*
|
|
201
|
-
* **Decoupled from any state-management library.**
|
|
202
|
-
*
|
|
203
|
-
* extracted version receives the answer record as a prop.
|
|
270
|
+
* **Decoupled from any state-management library.** Consumers receive the
|
|
271
|
+
* answer record as a prop.
|
|
204
272
|
*
|
|
205
273
|
* Iteration-1 visual identity (Phase B.2, 2026-05-06):
|
|
206
274
|
* Editorial Josefin display family on the heading and the "What happens next"
|
|
207
275
|
* callout title. Stone-tier surfaces and content text throughout. The
|
|
208
276
|
* "What happens next" callout box preserves border-copper-300 + bg-copper-50 —
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* Iteration-2 role-shift (2026-05-07): copper is no longer subordinate to
|
|
214
|
-
* the brand accent (which moved to sky-blue accent-500 #1f618d). Copper is
|
|
215
|
-
* its own "warm decorative framing on the editorial scale" — distinct from
|
|
216
|
-
* the brand-accent role. The "What happens next" callout continues to serve
|
|
217
|
-
* its visual function (warm forward-looking framing); its semantic relation
|
|
218
|
-
* to the brand accent is now zero. See wiki/decisions/iteration-2-blue-accent.md.
|
|
219
|
-
*
|
|
220
|
-
* Extracted from `apps/hub/src/components/interview/ReviewSummary.tsx`
|
|
221
|
-
* during iteration-3-B.1 (2026-05-07). Decoupled from `useInterviewStore`.
|
|
277
|
+
* preserved-decorative-copper role per iteration-2-blue-accent.md.
|
|
278
|
+
*
|
|
279
|
+
* D.2.1.b extension: rendering of composite/repeater/file-upload answers
|
|
280
|
+
* delegated to `summarize.ts` so all answer types display consistently.
|
|
222
281
|
*/
|
|
223
282
|
declare function ReviewSummary({ questions, answers, onGenerate, onEdit, generating }: Props): react_jsx_runtime.JSX.Element;
|
|
224
283
|
|
|
225
|
-
|
|
284
|
+
/**
|
|
285
|
+
* Helpers for rendering one-line / summary previews of answer values in
|
|
286
|
+
* `AnswerSidebar` and `ReviewSummary`. Centralized so the two components
|
|
287
|
+
* stay consistent when new answer shapes are added.
|
|
288
|
+
*/
|
|
289
|
+
|
|
290
|
+
declare function isAnswered(value: AnswerValue): boolean;
|
|
291
|
+
/**
|
|
292
|
+
* A short, single-line preview suitable for sidebar items. Truncates
|
|
293
|
+
* long content. Returns the empty string for unanswered values.
|
|
294
|
+
*/
|
|
295
|
+
declare function summarizeShort(question: InterviewQuestion, value: AnswerValue, maxLen?: number): string;
|
|
296
|
+
/**
|
|
297
|
+
* A richer multi-line summary suitable for the ReviewSummary list. May
|
|
298
|
+
* contain newlines; consumers should render in a `whitespace-pre-wrap`
|
|
299
|
+
* container.
|
|
300
|
+
*/
|
|
301
|
+
declare function summarizeLong(question: InterviewQuestion, value: AnswerValue): string;
|
|
302
|
+
|
|
303
|
+
export { type AnswerComposite, type AnswerFileUpload, type AnswerRepeater, AnswerSidebar, type AnswerValue, type FileEntry, type InterviewAnswers, type InterviewQuestion, ProgressBar, QuestionCard, type QuestionInputType, ReviewSummary, isAnswered, summarizeLong, summarizeShort };
|