@readerseye2/cr_type 1.0.135 → 1.0.137

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.
@@ -1,3 +1,4 @@
1
+ import type { EyeTrackerType } from '../gaze/eye-tracker.types';
1
2
  /** 읽은 구간 (globalIndex 범위, inclusive) */
2
3
  export interface ReadRange {
3
4
  from: number;
@@ -8,6 +9,8 @@ export type CalibrationType = "quick" | "full";
8
9
  /** 섹션 읽기 중 발생한 캘리브레이션 기간 */
9
10
  export interface CalibrationPeriod {
10
11
  type: CalibrationType;
12
+ /** 사용한 시선추적 트래커 */
13
+ trackerType: EyeTrackerType;
11
14
  /** 사용한 포인트 수 (quick: 3, full: 5) */
12
15
  points: number;
13
16
  startedAt: string;
@@ -15,6 +18,14 @@ export interface CalibrationPeriod {
15
18
  durationMs: number;
16
19
  /** 캘리브레이션 결과 품질 (0~1, 기기 제공) */
17
20
  quality?: number;
21
+ /** 화면 가로 해상도 (px) */
22
+ screenWidth?: number;
23
+ /** 화면 세로 해상도 (px) */
24
+ screenHeight?: number;
25
+ /** 평균 오차 (px) */
26
+ accuracyPx?: number;
27
+ /** 평균 오차 (degree) */
28
+ accuracyDeg?: number;
18
29
  }
19
30
  /** 퀴즈 시도 결과 (개별 퀴즈, 서버 저장용) */
20
31
  export interface QuizAttemptResult {
@@ -78,9 +89,9 @@ export interface ReadingProgressReport {
78
89
  /** 섹션 전체 GI 수 (ViewerSnapshot.totalItems) */
79
90
  sectionGIMax: number;
80
91
  /** 이 구간에서 뷰포트에 표시된 GI 범위의 최소값 */
81
- scrolledFrom: number;
92
+ exposedFrom: number;
82
93
  /** 이 구간에서 뷰포트에 표시된 GI 범위의 최대값 */
83
- scrolledTo: number;
94
+ exposedTo: number;
84
95
  /**
85
96
  * GI별 시선 dwell time (ms)
86
97
  * - 키: globalIndex (string), 값: 해당 5초 윈도우 내 누적 dwell ms
@@ -120,24 +131,24 @@ export interface ChildSectionProgress {
120
131
  sectionGIMax: number;
121
132
  /** 섹션 단어 수 (SectionSummary.word_count, WPM 계산용) */
122
133
  sectionWordCount: number;
123
- scrolledRanges: ReadRange[];
134
+ exposedRanges: ReadRange[];
124
135
  /** unique GI 수 (mergeReadRanges 후) */
125
- scrolledCount: number;
126
- /** scrolledCount / sectionGIMax */
127
- scrolledCoverage: number;
128
- gazeReadRanges: ReadRange[];
129
- gazeReadCount: number;
130
- gazeReadCoverage: number;
131
- /** 모든 flush의 (scrolledTo - scrolledFrom + 1) 합산 */
132
- totalRawScrolledGIs: number;
136
+ exposedCount: number;
137
+ /** exposedCount / sectionGIMax */
138
+ exposedCoverage: number;
139
+ eyeReadRanges: ReadRange[];
140
+ eyeReadCount: number;
141
+ eyeReadCoverage: number;
142
+ /** 모든 flush의 (exposedTo - exposedFrom + 1) 합산 */
143
+ totalRawExposedGIs: number;
133
144
  /** 모든 flush의 gaze dwell ≥ threshold GI 수 합산 */
134
- totalRawGazeReadGIs: number;
145
+ totalRawEyeReadGIs: number;
135
146
  totalReadMs: number;
136
147
  lastGlobalIndex: number;
137
148
  lastReadAt: string;
138
- /** 검증된 읽기속도: gazeReadCount / (totalReadMs / 60000) — WPM 근사 (GI ≈ word) */
149
+ /** 검증된 읽기속도: eyeReadCount / (totalReadMs / 60000) — WPM 근사 (GI ≈ word) */
139
150
  readingSpeedWPM: number | null;
140
- /** 미검증 읽기속도: scrolledCount / (totalReadMs / 60000) */
151
+ /** 미검증 읽기속도: exposedCount / (totalReadMs / 60000) */
141
152
  unverifiedReadingSpeedWPM: number | null;
142
153
  /** 누적: 시선이 텍스트 위에 있었던 시간 (ms) */
143
154
  totalGazeOnTextMs: number;
@@ -172,10 +183,10 @@ export interface ChildBookBookmark {
172
183
  lastReadSectionId: string;
173
184
  lastGlobalIndex: number;
174
185
  totalGIMax: number;
175
- totalScrolledCount: number;
176
- totalScrolledCoverage: number;
177
- totalGazeReadCount: number;
178
- totalGazeReadCoverage: number;
186
+ totalExposedCount: number;
187
+ totalExposedCoverage: number;
188
+ totalEyeReadCount: number;
189
+ totalEyeReadCoverage: number;
179
190
  totalReadingScore: number;
180
191
  /** 책 전체 평균 WPM (검증된 속도) */
181
192
  avgReadingSpeedWPM: number | null;
@@ -190,8 +201,8 @@ export interface ChildReadingLog {
190
201
  childIdx: number;
191
202
  bookIdx: number;
192
203
  sectionId: string;
193
- scrolledFrom: number;
194
- scrolledTo: number;
204
+ exposedFrom: number;
205
+ exposedTo: number;
195
206
  /** 전체 dwell map 그대로 보존 */
196
207
  gazeDwellMap: Record<string, number>;
197
208
  blinkCount: number;
@@ -209,17 +220,27 @@ export interface ChildReadingLog {
209
220
  }
210
221
  export interface ChildCalibrationLog {
211
222
  childIdx: number;
212
- /** 읽기 세션 중 발생 시 세션 ID */
213
- sessionId?: string;
223
+ /** 읽기 세션 중 발생 시 읽기 세션 ID */
224
+ readingSessionId?: string;
214
225
  /** 읽기 중이었던 섹션 */
215
226
  sectionId?: string;
216
227
  type: CalibrationType;
228
+ /** 사용한 시선추적 트래커 */
229
+ trackerType: EyeTrackerType;
217
230
  /** 사용한 포인트 수 */
218
231
  points: number;
219
232
  startedAt: string;
220
233
  endedAt: string;
221
234
  durationMs: number;
222
235
  quality?: number;
236
+ /** 화면 가로 해상도 (px) */
237
+ screenWidth?: number;
238
+ /** 화면 세로 해상도 (px) */
239
+ screenHeight?: number;
240
+ /** 평균 오차 (px) */
241
+ accuracyPx?: number;
242
+ /** 평균 오차 (degree) */
243
+ accuracyDeg?: number;
223
244
  createdAt: string;
224
245
  }
225
246
  export interface SegmentReadingSummary {
@@ -230,17 +251,17 @@ export interface SegmentReadingSummary {
230
251
  startedAt: string;
231
252
  endedAt: string;
232
253
  durationMs: number;
233
- scrolledFrom: number;
234
- scrolledTo: number;
235
- scrolledGICount: number;
236
- gazeReadGICount: number;
237
- rawScrolledGICount: number;
238
- rawGazeReadGICount: number;
239
- /** gazeReadGICount / (durationMs / 60000) — WPM 근사 */
254
+ exposedFrom: number;
255
+ exposedTo: number;
256
+ exposedGICount: number;
257
+ eyeReadGICount: number;
258
+ rawExposedGICount: number;
259
+ rawEyeReadGICount: number;
260
+ /** eyeReadGICount / (durationMs / 60000) — WPM 근사 */
240
261
  readingSpeedWPM: number | null;
241
- /** scrolledGICount / (durationMs / 60000) */
262
+ /** exposedGICount / (durationMs / 60000) */
242
263
  unverifiedReadingSpeedWPM: number;
243
- /** gazeReadGICount / scrolledGICount (시선 없으면 null) */
264
+ /** eyeReadGICount / exposedGICount (시선 없으면 null) */
244
265
  focusRatio: number | null;
245
266
  /** 평균 dwell time (ms per GI) */
246
267
  avgDwellMs: number | null;
@@ -259,13 +280,13 @@ export interface SegmentReadingSummary {
259
280
  }
260
281
  export interface ChildReadingSessionSummary {
261
282
  childIdx: number;
262
- sessionId: string;
283
+ readingSessionId: string;
263
284
  startedAt: string;
264
285
  endedAt: string;
265
286
  totalDurationMs: number;
266
287
  segments: SegmentReadingSummary[];
267
- totalScrolledGICount: number;
268
- totalGazeReadGICount: number;
288
+ totalExposedGICount: number;
289
+ totalEyeReadGICount: number;
269
290
  overallFocusRatio: number | null;
270
291
  avgReadingSpeedWPM: number | null;
271
292
  avgUnverifiedReadingSpeedWPM: number;
@@ -287,9 +308,9 @@ export interface ChildReadingSessionSummary {
287
308
  */
288
309
  export interface ReadingScoreInput {
289
310
  /** 진도 (중첩비허용 coverage, 0~1) */
290
- scrolledCoverage: number;
291
- /** 검증된 진도 (gazeRead coverage, 0~1) */
292
- gazeReadCoverage: number;
311
+ exposedCoverage: number;
312
+ /** 검증된 진도 (eyeRead coverage, 0~1) */
313
+ eyeReadCoverage: number;
293
314
  /** 집중도 (%, 0~100) */
294
315
  concentrationPercent: number | null;
295
316
  /** 시선일치도 (0~1) */
@@ -312,9 +333,9 @@ export interface ReadingAccumulatedState {
312
333
  sectionId: string;
313
334
  sectionGIMax: number;
314
335
  sectionWordCount: number;
315
- scrolledRanges: ReadRange[];
316
- scrolledCoverage: number;
317
- totalRawScrolledGIs: number;
336
+ exposedRanges: ReadRange[];
337
+ exposedCoverage: number;
338
+ totalRawExposedGIs: number;
318
339
  totalReadMs: number;
319
340
  lastGlobalIndex: number;
320
341
  totalGazeOnTextMs: number;
@@ -329,6 +350,41 @@ export interface ReadingAccumulatedState {
329
350
  quizMaxScore: number;
330
351
  estimatedReadingScore: number;
331
352
  }
353
+ export interface ChildDailySummary {
354
+ childIdx: number;
355
+ /** KST 기준 날짜 "YYYY-MM-DD" */
356
+ date: string;
357
+ totalReadMs: number;
358
+ sessionCount: number;
359
+ /** 오늘 읽은 bookIdx 목록 (unique) */
360
+ booksRead: number[];
361
+ /** 오늘 노출 GI 합 (세션별 누적) */
362
+ totalExposedGICount: number;
363
+ /** 오늘 시선 확인 GI 합 (세션별 누적) */
364
+ totalEyeReadGICount: number;
365
+ avgConcentrationPercent: number | null;
366
+ avgFocusRatio: number | null;
367
+ avgReadingSpeedWPM: number | null;
368
+ totalQuizScore: number;
369
+ totalQuizMaxScore: number;
370
+ totalBlinks: number;
371
+ totalLookedUpWords: number;
372
+ /** 집중도 가중합 (concentrationPercent × durationMs 누적) */
373
+ _weightedConcentrationMs: number;
374
+ /** 집중도 분모 (concentration 데이터가 있는 세션의 durationMs 누적) */
375
+ _concentrationDurationMs: number;
376
+ /** 속도 가중합 (speedWPM × durationMs 누적) */
377
+ _weightedSpeedMs: number;
378
+ /** 속도 분모 (speed 데이터가 있는 세션의 durationMs 누적) */
379
+ _speedDurationMs: number;
380
+ /** focusRatio 가중합 */
381
+ _weightedFocusRatioMs: number;
382
+ /** focusRatio 분모 */
383
+ _focusRatioDurationMs: number;
384
+ lastReadingSessionId: string;
385
+ createdAt: string;
386
+ updatedAt: string;
387
+ }
332
388
  /** GET /api/reading-progress/:bookIdx */
333
389
  export interface ChildBookProgressResponse {
334
390
  bookmark: ChildBookBookmark | null;
@@ -8,5 +8,5 @@
8
8
  // 시선일치도(gazeAlignment, 오디오재생+시선추적시)
9
9
  // 집중도(concentration, gazeOnText/total %)
10
10
  // 찾아본단어(lookedUpWords) 퀴즈점수/정답률
11
- // 미검증 읽은양/속도(scrollOnly) 종합 읽기점수(readingScore, 누적)
11
+ // 미검증 읽은양/속도(exposedOnly) 종합 읽기점수(readingScore, 누적)
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -54,5 +54,5 @@ export interface SessionEventPayload {
54
54
  }
55
55
  /** 세션 구독 페이로드 */
56
56
  export interface SessionSubscribePayload {
57
- sessionId: string;
57
+ readingSessionId: string;
58
58
  }
@@ -21,34 +21,34 @@ export interface ServerToClientEvents {
21
21
  }) => void;
22
22
  /** 세션 종료됨 (서버 확인) */
23
23
  'session:ended': (payload: {
24
- sessionId: string;
24
+ readingSessionId: string;
25
25
  durationMs?: number;
26
26
  }) => void;
27
27
  /** 구독 성공 (Parent가 자녀 세션 구독 시) */
28
28
  'session:subscribed': (payload: {
29
- sessionId: string;
29
+ readingSessionId: string;
30
30
  snapshot: ViewerSnapshot | null;
31
31
  }) => void;
32
32
  /** 구독 중인 세션의 진행 상황 */
33
33
  'session:progress': (payload: {
34
- sessionId: string;
34
+ readingSessionId: string;
35
35
  snapshot: ViewerSnapshot;
36
36
  }) => void;
37
37
  /** 구독 중인 세션의 이벤트 */
38
38
  'session:events': (payload: {
39
- sessionId: string;
39
+ readingSessionId: string;
40
40
  events: ViewerEvent[];
41
41
  }) => void;
42
42
  /** 구독 중인 세션의 실시간 시선 데이터 (~1초 간격) */
43
43
  'session:gaze': (payload: {
44
- sessionId: string;
44
+ readingSessionId: string;
45
45
  gazeData: GazeDataPayload;
46
46
  }) => void;
47
47
  /** 구독 중인 세션의 세그먼트 변경 (섹션 변경) */
48
48
  'session:segment-changed': (payload: SessionSegmentChangedPayload) => void;
49
49
  /** 구독 중인 세션의 청크 (10초 간격) */
50
50
  'session:chunk': (payload: {
51
- sessionId: string;
51
+ readingSessionId: string;
52
52
  segmentIndex: number;
53
53
  chunk: UnifiedChunkFile;
54
54
  }) => void;
@@ -72,34 +72,34 @@ export interface SessionServerToClientEvents {
72
72
  }) => void;
73
73
  /** 세션 종료됨 */
74
74
  'session:ended': (payload: {
75
- sessionId: string;
75
+ readingSessionId: string;
76
76
  durationMs?: number;
77
77
  }) => void;
78
78
  /** 구독 성공 */
79
79
  'session:subscribed': (payload: {
80
- sessionId: string;
80
+ readingSessionId: string;
81
81
  snapshot: ViewerSnapshot | null;
82
82
  }) => void;
83
83
  /** 구독 중인 세션 진행 상황 */
84
84
  'session:progress': (payload: {
85
- sessionId: string;
85
+ readingSessionId: string;
86
86
  snapshot: ViewerSnapshot;
87
87
  }) => void;
88
88
  /** 구독 중인 세션 이벤트 */
89
89
  'session:events': (payload: {
90
- sessionId: string;
90
+ readingSessionId: string;
91
91
  events: ViewerEvent[];
92
92
  }) => void;
93
93
  /** 구독 중인 세션 실시간 시선 데이터 (~1초 간격) */
94
94
  'session:gaze': (payload: {
95
- sessionId: string;
95
+ readingSessionId: string;
96
96
  gazeData: GazeDataPayload;
97
97
  }) => void;
98
98
  /** 세그먼트 변경 (섹션 변경) */
99
99
  'session:segment-changed': (payload: SessionSegmentChangedPayload) => void;
100
100
  /** 실시간 청크 (10초 간격, 구독자에게) */
101
101
  'session:chunk': (payload: {
102
- sessionId: string;
102
+ readingSessionId: string;
103
103
  segmentIndex: number;
104
104
  chunk: UnifiedChunkFile;
105
105
  }) => void;
@@ -96,15 +96,14 @@ export interface UnifiedSegmentSummary {
96
96
  * - 기존 ReadingSessionRecord + RecordingManifest 대체
97
97
  */
98
98
  export interface UnifiedSessionManifest {
99
- /** 세션 ID (UUID) */
100
- sessionId: string;
99
+ /** 읽기 세션 ID (UUID) */
100
+ readingSessionId: string;
101
101
  /** 상태 */
102
102
  status: UnifiedSessionStatus;
103
103
  /** 스키마 버전 (구 포맷과 구분용, 항상 2) */
104
104
  version: 2;
105
- userId: number;
106
- userType: 'parent' | 'child';
107
- userName: string;
105
+ childIdx: number;
106
+ childName: string;
108
107
  familyId: number;
109
108
  /** 세션 시작 시간 (뷰어 열림, ms) */
110
109
  startedAt: number;
@@ -162,10 +161,9 @@ export interface GazeDataPayload {
162
161
  * - 현재 세그먼트 정보 포함
163
162
  */
164
163
  export interface UnifiedSessionInfo {
165
- sessionId: string;
166
- userId: number;
167
- userType: 'parent' | 'child';
168
- userName?: string;
164
+ readingSessionId: string;
165
+ childIdx: number;
166
+ childName?: string;
169
167
  familyId: number;
170
168
  startedAt: string;
171
169
  snapshot: ViewerSnapshot;
@@ -184,7 +182,7 @@ export interface UnifiedSessionInfo {
184
182
  }
185
183
  /** 세그먼트 변경 알림 (섹션 변경 시) */
186
184
  export interface SessionSegmentChangedPayload {
187
- sessionId: string;
185
+ readingSessionId: string;
188
186
  /** 종료된 세그먼트 인덱스 */
189
187
  oldSegmentIndex: number;
190
188
  /** 새로 시작된 세그먼트 */
@@ -200,24 +198,24 @@ export interface SessionSegmentChangedPayload {
200
198
  /** 세션 이력 목록 조회 요청 */
201
199
  export interface SessionHistoryListPayload {
202
200
  familyId?: number;
203
- userId?: number;
201
+ childIdx?: number;
204
202
  date?: string;
205
203
  limit?: number;
206
204
  offset?: number;
207
205
  }
208
206
  /** 세션 이력 상세 조회 요청 */
209
207
  export interface SessionHistoryGetPayload {
210
- sessionId: string;
208
+ readingSessionId: string;
211
209
  s3Key?: string;
212
210
  }
213
211
  /** 세션 이력 삭제 요청 */
214
212
  export interface SessionHistoryDeletePayload {
215
- sessionId: string;
213
+ readingSessionId: string;
216
214
  s3Key?: string;
217
215
  }
218
216
  /** 통합 청크 조회 요청 (seek/재생용) */
219
217
  export interface UnifiedChunksGetPayload {
220
- sessionId: string;
218
+ readingSessionId: string;
221
219
  segmentIndex: number;
222
220
  /** 시작 timestamp (이 시점부터 청크 조회) */
223
221
  fromTimestamp?: number;
@@ -226,7 +224,7 @@ export interface UnifiedChunksGetPayload {
226
224
  }
227
225
  /** 통합 세그먼트 상세 조회 요청 */
228
226
  export interface UnifiedSegmentGetPayload {
229
- sessionId: string;
227
+ readingSessionId: string;
230
228
  segmentIndex: number;
231
229
  }
232
230
  /** 세션 이력 목록 결과 */
@@ -245,7 +243,7 @@ export interface SessionHistoryGetResult {
245
243
  }
246
244
  /** 청크 조회 결과 */
247
245
  export interface UnifiedChunksResult {
248
- sessionId: string;
246
+ readingSessionId: string;
249
247
  segmentIndex: number;
250
248
  chunks: UnifiedChunkFile[];
251
249
  /** Seek 시 가장 가까운 스냅샷 (초기 상태 복원용) */
@@ -253,12 +251,12 @@ export interface UnifiedChunksResult {
253
251
  }
254
252
  /** 세그먼트 상세 결과 */
255
253
  export interface UnifiedSegmentResult {
256
- sessionId: string;
254
+ readingSessionId: string;
257
255
  segment: UnifiedSegmentMeta;
258
256
  }
259
257
  /** 세션 삭제 결과 */
260
258
  export interface SessionHistoryDeleteResult {
261
- sessionId: string;
259
+ readingSessionId: string;
262
260
  success: boolean;
263
261
  }
264
262
  /**
@@ -266,11 +264,10 @@ export interface SessionHistoryDeleteResult {
266
264
  * - 기존 ActiveRecording + SessionChunkState 대체
267
265
  */
268
266
  export interface ActiveUnifiedSession {
269
- sessionId: string;
267
+ readingSessionId: string;
270
268
  s3Prefix: string;
271
- userId: number;
272
- userType: 'parent' | 'child';
273
- userName: string;
269
+ childIdx: number;
270
+ childName: string;
274
271
  familyId: number;
275
272
  viewerSocketId: string;
276
273
  startedAt: number;
@@ -11,7 +11,7 @@
11
11
  // - Parent + Admin 동일한 subscribe 체계
12
12
  //
13
13
  // S3 경로:
14
- // reading-sessions/{familyId}/{userId}/{YYYY-MM-DD}/{sessionId}/
14
+ // reading-sessions/{familyId}/{childIdx}/{YYYY-MM-DD}/{readingSessionId}/
15
15
  // ├── manifest.json (UnifiedSessionManifest)
16
16
  // └── segments/
17
17
  // ├── 0/
@@ -130,9 +130,14 @@ export type CalibrationStartEvent = ViewerEventBase<'calibration_start', {
130
130
  }>;
131
131
  export type CalibrationEndEvent = ViewerEventBase<'calibration_end', {
132
132
  type: 'quick' | 'full';
133
+ trackerType: import('../gaze/eye-tracker.types').EyeTrackerType;
133
134
  points: number;
134
135
  durationMs: number;
135
136
  quality?: number;
137
+ screenWidth?: number;
138
+ screenHeight?: number;
139
+ accuracyPx?: number;
140
+ accuracyDeg?: number;
136
141
  }>;
137
142
  export type ShowGazeChangeEvent = ViewerEventBase<'show_gaze_change', {
138
143
  showGaze: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readerseye2/cr_type",
3
- "version": "1.0.135",
3
+ "version": "1.0.137",
4
4
  "description": "CheckReading shared TypeScript types",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",