@orjok/commons 1.0.2 → 1.1.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/dist/index.cjs CHANGED
@@ -1,4 +1,21 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/services/user.service.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+ var _chunkDB3CMKPPcjs = require('./chunk-DB3CMKPP.cjs');
17
+
18
+ // src/services/user.service.ts
2
19
  var UserService = class {
3
20
  constructor(network) {
4
21
  this.network = network;
@@ -30,10 +47,10 @@ var UserService = class {
30
47
  );
31
48
  return result.data.updateUser;
32
49
  }
33
- async getQuestions(userId, nextToken) {
50
+ async getQuestions(userId, nextToken, limit = 10) {
34
51
  const result = await this.network.query(
35
- `query ListUserQuestions($owner: ID!, $nextToken: String, $sortDirection: ModelSortDirection) {
36
- listQuestionObjectByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection, nextToken: $nextToken) {
52
+ `query ListUserQuestions($owner: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {
53
+ listQuestionObjectByOwnerAndCreatedAt(owner: $owner, limit: $limit, sortDirection: $sortDirection, nextToken: $nextToken) {
37
54
  items {
38
55
  id question imageUrl language level difficulty type voteCount
39
56
  verificationStatus owner packId createdAt
@@ -43,14 +60,14 @@ var UserService = class {
43
60
  nextToken
44
61
  }
45
62
  }`,
46
- { owner: userId, nextToken, sortDirection: "DESC" }
63
+ { owner: userId, limit, nextToken, sortDirection: "DESC" }
47
64
  );
48
65
  return result.data.listQuestionObjectByOwnerAndCreatedAt;
49
66
  }
50
- async getPacks(userId, nextToken) {
67
+ async getPacks(userId, nextToken, limit = 10) {
51
68
  const result = await this.network.query(
52
- `query ListUserPacks($owner: ID!, $nextToken: String, $sortDirection: ModelSortDirection) {
53
- listPackByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection, nextToken: $nextToken) {
69
+ `query ListUserPacks($owner: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {
70
+ listPackByOwnerAndCreatedAt(owner: $owner, limit: $limit, sortDirection: $sortDirection, nextToken: $nextToken) {
54
71
  items {
55
72
  id name language level difficulty questionCount automaticNumbering
56
73
  owner subjectId chapterId topicId verificationStatus createdAt
@@ -59,7 +76,7 @@ var UserService = class {
59
76
  nextToken
60
77
  }
61
78
  }`,
62
- { owner: userId, nextToken, sortDirection: "DESC" }
79
+ { owner: userId, limit, nextToken, sortDirection: "DESC" }
63
80
  );
64
81
  return result.data.listPackByOwnerAndCreatedAt;
65
82
  }
@@ -159,6 +176,23 @@ var QuestionService = class {
159
176
  );
160
177
  return _nullishCoalesce(_optionalChain([result, 'access', _2 => _2.data, 'access', _3 => _3.listQuestionObjectByPackIdAndOrder, 'access', _4 => _4.items, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.id]), () => ( null));
161
178
  }
179
+ async listByPack(packId, limit = 200, sortDirection = "ASC", nextToken) {
180
+ const result = await this.network.query(
181
+ `query ListQuestionsByPack($packId: ID!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {
182
+ listQuestionObjectByPackIdAndOrder(packId: $packId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
183
+ items {
184
+ id question answer explanation extra imageUrl language level difficulty type
185
+ markingInstructions voteCount owner packId subjectId chapterId topicId order createdAt
186
+ options { items { id content } }
187
+ tags { items { id tagId } }
188
+ }
189
+ nextToken
190
+ }
191
+ }`,
192
+ { packId, sortDirection, limit, nextToken }
193
+ );
194
+ return result.data.listQuestionObjectByPackIdAndOrder;
195
+ }
162
196
  async update(id, fields) {
163
197
  const result = await this.network.mutate(
164
198
  `mutation UpdateQuestion($input: UpdateQuestionObjectInput!) {
@@ -172,6 +206,41 @@ var QuestionService = class {
172
206
  );
173
207
  return result.data.updateQuestionObject;
174
208
  }
209
+ async updateOption(id, content) {
210
+ const result = await this.network.mutate(
211
+ `mutation UpdateOption($input: UpdateOptionInput!) {
212
+ updateOption(input: $input) {
213
+ id
214
+ content
215
+ }
216
+ }`,
217
+ { input: { id, content } }
218
+ );
219
+ return result.data.updateOption;
220
+ }
221
+ async createOption(input) {
222
+ const result = await this.network.mutate(
223
+ `mutation CreateOption($input: CreateOptionInput!) {
224
+ createOption(input: $input) {
225
+ id
226
+ content
227
+ }
228
+ }`,
229
+ { input }
230
+ );
231
+ return result.data.createOption;
232
+ }
233
+ async deleteOption(id) {
234
+ const result = await this.network.mutate(
235
+ `mutation DeleteOption($input: DeleteOptionInput!) {
236
+ deleteOption(input: $input) {
237
+ id
238
+ }
239
+ }`,
240
+ { input: { id } }
241
+ );
242
+ return result.data.deleteOption;
243
+ }
175
244
  async delete(id) {
176
245
  const result = await this.network.mutate(
177
246
  `mutation DeleteQuestion($input: DeleteQuestionObjectInput!) {
@@ -239,7 +308,7 @@ var QuestionService = class {
239
308
  listQuestionObjectBySubjectIdAndCreatedAt(subjectId: $subjectId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
240
309
  items {
241
310
  id question imageUrl language level difficulty type voteCount
242
- verificationStatus owner packId createdAt
311
+ verificationStatus owner packId subjectId chapterId topicId createdAt
243
312
  options { items { id content } }
244
313
  }
245
314
  nextToken
@@ -261,7 +330,7 @@ var QuestionService = class {
261
330
  listQuestionObjectByChapterIdAndCreatedAt(chapterId: $chapterId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
262
331
  items {
263
332
  id question imageUrl language level difficulty type voteCount
264
- verificationStatus owner packId createdAt
333
+ verificationStatus owner packId subjectId chapterId topicId createdAt
265
334
  options { items { id content } }
266
335
  }
267
336
  nextToken
@@ -283,7 +352,7 @@ var QuestionService = class {
283
352
  listQuestionObjectByTopicIdAndCreatedAt(topicId: $topicId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
284
353
  items {
285
354
  id question imageUrl language level difficulty type voteCount
286
- verificationStatus owner packId createdAt
355
+ verificationStatus owner packId subjectId chapterId topicId createdAt
287
356
  options { items { id content } }
288
357
  }
289
358
  nextToken
@@ -368,7 +437,22 @@ var PackService = class {
368
437
  }`,
369
438
  { id }
370
439
  );
371
- return result.data.getPack;
440
+ const pack = result.data.getPack;
441
+ if (pack && pack.questions && Array.isArray(pack.questions.items)) {
442
+ pack.questions.items.sort((a, b) => {
443
+ const orderA = typeof a.order === "number" ? a.order : a.order ? Number(a.order) : 0;
444
+ const orderB = typeof b.order === "number" ? b.order : b.order ? Number(b.order) : 0;
445
+ if (orderA !== orderB) {
446
+ if (orderA === 0) return 1;
447
+ if (orderB === 0) return -1;
448
+ return orderA - orderB;
449
+ }
450
+ const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
451
+ const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
452
+ return timeA - timeB;
453
+ });
454
+ }
455
+ return pack;
372
456
  }
373
457
  async update(id, fields) {
374
458
  const result = await this.network.mutate(
@@ -954,13 +1038,34 @@ var MediaService = class {
954
1038
  this.storage = storage;
955
1039
  }
956
1040
  async uploadImage(questionId, image) {
957
- const result = await this.network.mutate(
958
- `mutation UploadImage($questionId: ID!, $image: String!) {
959
- uploadImage(questionId: $questionId, image: $image) { status imageUrl }
960
- }`,
961
- { questionId, image }
962
- );
963
- return result.data.uploadImage;
1041
+ const payloadLength = image ? image.length : 0;
1042
+ console.log(`[MediaService.uploadImage] Initiating upload mutation: questionId="${questionId}", payloadLength=${payloadLength} chars`);
1043
+ try {
1044
+ const result = await this.network.mutate(
1045
+ `mutation UploadImage($questionId: ID!, $image: String!) {
1046
+ uploadImage(questionId: $questionId, image: $image) { status imageUrl }
1047
+ }`,
1048
+ { questionId, image }
1049
+ );
1050
+ if (result.errors && result.errors.length > 0) {
1051
+ console.error(
1052
+ `[MediaService.uploadImage] GraphQL errors returned for questionId "${questionId}":`,
1053
+ JSON.stringify(result.errors, null, 2)
1054
+ );
1055
+ }
1056
+ const response = _optionalChain([result, 'access', _10 => _10.data, 'optionalAccess', _11 => _11.uploadImage]);
1057
+ if (!response) {
1058
+ console.error(`[MediaService.uploadImage] No uploadImage object returned in GraphQL response for questionId "${questionId}". Full result:`, result);
1059
+ } else if (response.status === "Error" || response.status === "error" || !response.imageUrl) {
1060
+ console.error(`[MediaService.uploadImage] Backend returned error status or missing imageUrl for questionId "${questionId}":`, response);
1061
+ } else {
1062
+ console.log(`[MediaService.uploadImage] Upload mutation success for questionId "${questionId}": imageUrl="${response.imageUrl}"`);
1063
+ }
1064
+ return response;
1065
+ } catch (err) {
1066
+ console.error(`[MediaService.uploadImage] Network/GraphQL mutation failed for questionId "${questionId}":`, err);
1067
+ throw err;
1068
+ }
964
1069
  }
965
1070
  async deleteImage(imageUrl) {
966
1071
  const result = await this.network.mutate(
@@ -1023,68 +1128,100 @@ var ProgressService = class {
1023
1128
  this.network = network;
1024
1129
  }
1025
1130
  async track(input) {
1131
+ const variables = {
1132
+ questionId: input.questionId,
1133
+ isCorrect: input.isCorrect,
1134
+ subjectId: _nullishCoalesce(input.subjectId, () => ( null)),
1135
+ chapterId: _nullishCoalesce(input.chapterId, () => ( null)),
1136
+ topicId: _nullishCoalesce(input.topicId, () => ( null))
1137
+ };
1026
1138
  const result = await this.network.mutate(
1027
1139
  `mutation TrackProgress($questionId: ID!, $isCorrect: Boolean!, $subjectId: ID, $chapterId: ID, $topicId: ID) {
1028
1140
  trackPracticeProgress(questionId: $questionId, isCorrect: $isCorrect, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId) { status }
1029
1141
  }`,
1030
- input
1142
+ variables
1031
1143
  );
1032
- return result.data.trackPracticeProgress;
1144
+ return _optionalChain([result, 'access', _12 => _12.data, 'optionalAccess', _13 => _13.trackPracticeProgress]) || { status: "success" };
1033
1145
  }
1034
1146
  async getSubjectProgress(userId) {
1035
- const result = await this.network.query(
1036
- `query ListSubjectProgress($userId: String!) {
1037
- listUserSubjectProgressByUserIdAndSubjectId(userId: $userId) {
1038
- items { id userId subjectId practicedCount mistakeCount correctedCount accuracyScore }
1039
- }
1040
- }`,
1041
- { userId }
1042
- );
1043
- return result.data.listUserSubjectProgressByUserIdAndSubjectId.items;
1147
+ try {
1148
+ const result = await this.network.query(
1149
+ `query ListSubjectProgress($userId: String!) {
1150
+ listUserSubjectProgressByUserIdAndSubjectId(userId: $userId) {
1151
+ items { id userId subjectId practicedCount mistakeCount correctedCount accuracyScore }
1152
+ }
1153
+ }`,
1154
+ { userId }
1155
+ );
1156
+ return _optionalChain([result, 'access', _14 => _14.data, 'optionalAccess', _15 => _15.listUserSubjectProgressByUserIdAndSubjectId, 'optionalAccess', _16 => _16.items]) || _optionalChain([result, 'access', _17 => _17.data, 'optionalAccess', _18 => _18.listUserSubjectProgressesByUserIdAndSubjectId, 'optionalAccess', _19 => _19.items]) || [];
1157
+ } catch (e) {
1158
+ console.error("Error fetching subject progress:", e);
1159
+ return [];
1160
+ }
1044
1161
  }
1045
1162
  async getChapterProgress(userId) {
1046
- const result = await this.network.query(
1047
- `query ListChapterProgress($userId: String!) {
1048
- listUserChapterProgressByUserIdAndChapterId(userId: $userId) {
1049
- items { id userId chapterId practicedCount mistakeCount correctedCount accuracyScore }
1050
- }
1051
- }`,
1052
- { userId }
1053
- );
1054
- return result.data.listUserChapterProgressByUserIdAndChapterId.items;
1163
+ try {
1164
+ const result = await this.network.query(
1165
+ `query ListChapterProgress($userId: String!) {
1166
+ listUserChapterProgressByUserIdAndChapterId(userId: $userId) {
1167
+ items { id userId chapterId practicedCount mistakeCount correctedCount accuracyScore }
1168
+ }
1169
+ }`,
1170
+ { userId }
1171
+ );
1172
+ return _optionalChain([result, 'access', _20 => _20.data, 'optionalAccess', _21 => _21.listUserChapterProgressByUserIdAndChapterId, 'optionalAccess', _22 => _22.items]) || _optionalChain([result, 'access', _23 => _23.data, 'optionalAccess', _24 => _24.listUserChapterProgressesByUserIdAndChapterId, 'optionalAccess', _25 => _25.items]) || [];
1173
+ } catch (e) {
1174
+ console.error("Error fetching chapter progress:", e);
1175
+ return [];
1176
+ }
1055
1177
  }
1056
1178
  async getTopicProgress(userId) {
1057
- const result = await this.network.query(
1058
- `query ListTopicProgress($userId: String!) {
1059
- listUserTopicProgressByUserIdAndTopicId(userId: $userId) {
1060
- items { id userId topicId practicedCount mistakeCount correctedCount accuracyScore }
1061
- }
1062
- }`,
1063
- { userId }
1064
- );
1065
- return result.data.listUserTopicProgressByUserIdAndTopicId.items;
1179
+ try {
1180
+ const result = await this.network.query(
1181
+ `query ListTopicProgress($userId: String!) {
1182
+ listUserTopicProgressByUserIdAndTopicId(userId: $userId) {
1183
+ items { id userId topicId practicedCount mistakeCount correctedCount accuracyScore }
1184
+ }
1185
+ }`,
1186
+ { userId }
1187
+ );
1188
+ return _optionalChain([result, 'access', _26 => _26.data, 'optionalAccess', _27 => _27.listUserTopicProgressByUserIdAndTopicId, 'optionalAccess', _28 => _28.items]) || _optionalChain([result, 'access', _29 => _29.data, 'optionalAccess', _30 => _30.listUserTopicProgressesByUserIdAndTopicId, 'optionalAccess', _31 => _31.items]) || [];
1189
+ } catch (e) {
1190
+ console.error("Error fetching topic progress:", e);
1191
+ return [];
1192
+ }
1066
1193
  }
1067
1194
  async getTopicMetrics(userId) {
1068
- const result = await this.network.query(
1069
- `query ListTopicMetrics($filter: ModelUserTopicMetricFilterInput) {
1070
- listUserTopicMetrics(filter: $filter) {
1071
- items { id userId topicId totalAttempted totalCorrect accuracy }
1072
- }
1073
- }`,
1074
- { filter: { userId: { eq: userId } } }
1075
- );
1076
- return result.data.listUserTopicMetrics.items;
1195
+ try {
1196
+ const result = await this.network.query(
1197
+ `query ListTopicMetrics($filter: ModelUserTopicMetricFilterInput) {
1198
+ listUserTopicMetrics(filter: $filter) {
1199
+ items { id userId topicId totalAttempted totalCorrect accuracy }
1200
+ }
1201
+ }`,
1202
+ { filter: { userId: { eq: userId } } }
1203
+ );
1204
+ return _optionalChain([result, 'access', _32 => _32.data, 'optionalAccess', _33 => _33.listUserTopicMetrics, 'optionalAccess', _34 => _34.items]) || [];
1205
+ } catch (e) {
1206
+ console.error("Error fetching topic metrics:", e);
1207
+ return [];
1208
+ }
1077
1209
  }
1078
1210
  async getMistakenQuestions(userId) {
1079
- const result = await this.network.query(
1080
- `query ListMistakes($userId: String!, $filter: ModelQuestionTrackingFilterInput) {
1081
- listQuestionTrackingByUserIdAndSubjectId(userId: $userId, filter: $filter) {
1082
- items { id userId questionId subjectId chapterId topicId status attempts }
1083
- }
1084
- }`,
1085
- { userId, filter: { status: { eq: "MISTAKE" } } }
1086
- );
1087
- return result.data.listQuestionTrackingByUserIdAndSubjectId.items;
1211
+ try {
1212
+ const result = await this.network.query(
1213
+ `query ListMistakes($userId: String!, $filter: ModelQuestionTrackingFilterInput) {
1214
+ listQuestionTrackingByUserIdAndQuestionId(userId: $userId, filter: $filter) {
1215
+ items { id userId questionId subjectId chapterId topicId status attempts }
1216
+ }
1217
+ }`,
1218
+ { userId, filter: { status: { eq: "MISTAKE" } } }
1219
+ );
1220
+ return _optionalChain([result, 'access', _35 => _35.data, 'optionalAccess', _36 => _36.listQuestionTrackingByUserIdAndQuestionId, 'optionalAccess', _37 => _37.items]) || _optionalChain([result, 'access', _38 => _38.data, 'optionalAccess', _39 => _39.listQuestionTrackingsByUserIdAndQuestionId, 'optionalAccess', _40 => _40.items]) || _optionalChain([result, 'access', _41 => _41.data, 'optionalAccess', _42 => _42.listQuestionTrackings, 'optionalAccess', _43 => _43.items]) || [];
1221
+ } catch (e) {
1222
+ console.error("Error fetching mistaken questions:", e);
1223
+ return [];
1224
+ }
1088
1225
  }
1089
1226
  };
1090
1227
 
@@ -1147,6 +1284,7 @@ var NewsService = class {
1147
1284
  var OrjokClient = class {
1148
1285
  constructor(config) {
1149
1286
  this.auth = config.authProvider;
1287
+ this.storage = config.storageProvider;
1150
1288
  this.users = new UserService(config.networkProvider);
1151
1289
  this.questions = new QuestionService(config.networkProvider);
1152
1290
  this.packs = new PackService(config.networkProvider);
@@ -1163,6 +1301,118 @@ function createOrjokClient(config) {
1163
1301
  return new OrjokClient(config);
1164
1302
  }
1165
1303
 
1304
+ // src/types/select-options.ts
1305
+ var levelOptions = [
1306
+ { value: "SSC", label: "SSC" },
1307
+ { value: "HSC+Admission", label: "HSC/Admission" },
1308
+ { value: "BCS", label: "BCS" }
1309
+ ];
1310
+ var languageOptions = [
1311
+ { value: "Bangla", label: "Bangla" },
1312
+ { value: "English", label: "English" },
1313
+ { value: "Any", label: "Any" }
1314
+ ];
1315
+ var difficultyOptions = [
1316
+ { value: "Easy", label: "Easy" },
1317
+ { value: "Medium", label: "Medium" },
1318
+ { value: "Hard", label: "Hard" }
1319
+ ];
1320
+
1321
+ // src/logic/avatar.ts
1322
+ var HAIR_COLORS = [
1323
+ { name: "Original", filter: "none", hex: "#e2e8f0", brightness: 1 },
1324
+ { name: "White", filter: "grayscale(100%) brightness(500%)", hex: "#ffffff", brightness: 2 },
1325
+ { name: "Black", filter: "grayscale(100%) brightness(40%)", hex: "#1a202c", brightness: 0.15 },
1326
+ { name: "Brown", filter: "sepia(100%) saturate(300%) hue-rotate(330deg) brightness(80%)", hex: "#7b3f00", brightness: 0.35 },
1327
+ { name: "Blonde", filter: "sepia(100%) saturate(400%) hue-rotate(20deg) brightness(130%)", hex: "#e6c27a", brightness: 0.8 },
1328
+ { name: "Red", filter: "sepia(100%) saturate(500%) hue-rotate(320deg) brightness(90%)", hex: "#9b2c2c", brightness: 0.4 },
1329
+ { name: "Blue", filter: "sepia(100%) saturate(500%) hue-rotate(180deg) brightness(90%)", hex: "#2b6cb0", brightness: 0.4 },
1330
+ { name: "Green", filter: "sepia(100%) saturate(400%) hue-rotate(80deg) brightness(90%)", hex: "#2f855a", brightness: 0.45 }
1331
+ ];
1332
+ var CLOTHES_COLORS = [
1333
+ { name: "Original", filter: "none", hex: "#e2e8f0", brightness: 1 },
1334
+ { name: "White", filter: "grayscale(100%) brightness(500%)", hex: "#ffffff", brightness: 2 },
1335
+ { name: "Black", filter: "grayscale(100%) brightness(30%)", hex: "#1a202c", brightness: 0.12 },
1336
+ { name: "Red", filter: "sepia(100%) saturate(500%) hue-rotate(320deg)", hex: "#c53030", brightness: 0.45 },
1337
+ { name: "Orange", filter: "sepia(100%) saturate(500%) hue-rotate(350deg)", hex: "#dd6b20", brightness: 0.55 },
1338
+ { name: "Yellow", filter: "sepia(100%) saturate(500%) hue-rotate(20deg) brightness(120%)", hex: "#d69e2e", brightness: 0.8 },
1339
+ { name: "Green", filter: "sepia(100%) saturate(500%) hue-rotate(80deg)", hex: "#38a169", brightness: 0.55 },
1340
+ { name: "Blue", filter: "sepia(100%) saturate(500%) hue-rotate(180deg)", hex: "#3182ce", brightness: 0.45 },
1341
+ { name: "Purple", filter: "sepia(100%) saturate(500%) hue-rotate(240deg)", hex: "#805ad5", brightness: 0.45 },
1342
+ { name: "Pink", filter: "sepia(100%) saturate(400%) hue-rotate(290deg)", hex: "#d53f8c", brightness: 0.55 }
1343
+ ];
1344
+ var COLOR_HUES = {
1345
+ Original: 0,
1346
+ Red: 0,
1347
+ Orange: 24,
1348
+ Yellow: 45,
1349
+ Green: 140,
1350
+ Blue: 210,
1351
+ Purple: 270,
1352
+ Pink: 320,
1353
+ Brown: 25,
1354
+ Blonde: 45
1355
+ };
1356
+ var RENDER_ORDER = [
1357
+ "Background",
1358
+ "Hair Back",
1359
+ "Clothes Back",
1360
+ "Skin Color Body",
1361
+ "Clothes Front",
1362
+ "Skin Color Head",
1363
+ "Accessories",
1364
+ "Facial Expression",
1365
+ "Beard",
1366
+ "Hair Front",
1367
+ "Eyewear",
1368
+ "Headwears"
1369
+ ];
1370
+ var INITIAL_AVATAR_DATA = {
1371
+ Background: [],
1372
+ Hair: [],
1373
+ "Skin Color": [],
1374
+ Clothes: [],
1375
+ Accessories: [],
1376
+ Headwears: [],
1377
+ Eyewear: [],
1378
+ "Facial Expression": [],
1379
+ Beard: []
1380
+ };
1381
+ function getActualOptionFileName(category, baseName, config, selections) {
1382
+ if (baseName === "None") return null;
1383
+ const items = config[category] || [];
1384
+ const matched = items.filter((item) => item.base_name === baseName);
1385
+ if (matched.length === 0) return null;
1386
+ for (const item of matched) {
1387
+ if (item.conditions && item.conditions.length > 0) {
1388
+ const allMet = item.conditions.every((cond) => {
1389
+ return Object.values(selections).some(
1390
+ (sel) => sel && (sel.includes(cond) || cond.includes(sel))
1391
+ );
1392
+ });
1393
+ if (allMet) return item;
1394
+ }
1395
+ }
1396
+ return matched.find((item) => !item.conditions || item.conditions.length === 0) || matched[0] || null;
1397
+ }
1398
+ function parseAvatarConfig(configStr) {
1399
+ if (!configStr) {
1400
+ return { selections: {}, colors: {} };
1401
+ }
1402
+ try {
1403
+ const parsed = JSON.parse(configStr);
1404
+ return {
1405
+ selections: parsed.selections || {},
1406
+ colors: parsed.colors || {}
1407
+ };
1408
+ } catch (e2) {
1409
+ return { selections: {}, colors: {} };
1410
+ }
1411
+ }
1412
+ function serializeAvatarConfig(state) {
1413
+ return JSON.stringify(state);
1414
+ }
1415
+
1166
1416
  // src/logic/elo.ts
1167
1417
  var TIERS = [
1168
1418
  { name: "Iron", minRating: 0, maxRating: 799, color: "#6B7280" },
@@ -1263,6 +1513,40 @@ function detectQuestionType(option2, option3, option4) {
1263
1513
  }
1264
1514
  return "MCQ";
1265
1515
  }
1516
+ function isCorrectAnswer(selected, correct) {
1517
+ if (!selected || !correct) return false;
1518
+ const selTrim = String(selected).trim();
1519
+ const corrTrim = String(correct).trim();
1520
+ if (selTrim === corrTrim) return true;
1521
+ const selNum = Number(selTrim);
1522
+ const corrNum = Number(corrTrim);
1523
+ if (!isNaN(selNum) && !isNaN(corrNum) && selNum === corrNum) {
1524
+ return true;
1525
+ }
1526
+ return false;
1527
+ }
1528
+ function calculateDefaultExamMinutes(mode, questions) {
1529
+ const mcqCount = questions.filter((q) => q.type !== "WRITTEN").length;
1530
+ const writtenCount = questions.filter((q) => q.type === "WRITTEN").length;
1531
+ if (mode === "MCQ") return Math.max(1, mcqCount * 1);
1532
+ if (mode === "WRITTEN") return Math.max(1, writtenCount * 10);
1533
+ return Math.max(1, mcqCount * 1 + writtenCount * 10);
1534
+ }
1535
+ function reorderObjectKeys(obj, oldIndex, newIndex, length) {
1536
+ const arr = [];
1537
+ for (let i = 0; i < length; i++) {
1538
+ arr.push(obj[i]);
1539
+ }
1540
+ const [movedItem] = arr.splice(oldIndex, 1);
1541
+ arr.splice(newIndex, 0, movedItem);
1542
+ const newObj = {};
1543
+ arr.forEach((item, idx) => {
1544
+ if (item !== void 0) {
1545
+ newObj[idx] = item;
1546
+ }
1547
+ });
1548
+ return newObj;
1549
+ }
1266
1550
 
1267
1551
  // src/logic/import-export.ts
1268
1552
  var MAX_QUESTIONS = 200;
@@ -1280,50 +1564,50 @@ function parseFields(parts) {
1280
1564
  let option3ImageUrl = "";
1281
1565
  let option4ImageUrl = "";
1282
1566
  if (parts.length >= 13) {
1283
- extra = _nullishCoalesce(_optionalChain([parts, 'access', _10 => _10[7], 'optionalAccess', _11 => _11.trim, 'call', _12 => _12()]), () => ( ""));
1284
- const rawDiff = _nullishCoalesce(_optionalChain([parts, 'access', _13 => _13[8], 'optionalAccess', _14 => _14.trim, 'call', _15 => _15()]), () => ( ""));
1567
+ extra = _nullishCoalesce(_optionalChain([parts, 'access', _44 => _44[7], 'optionalAccess', _45 => _45.trim, 'call', _46 => _46()]), () => ( ""));
1568
+ const rawDiff = _nullishCoalesce(_optionalChain([parts, 'access', _47 => _47[8], 'optionalAccess', _48 => _48.trim, 'call', _49 => _49()]), () => ( ""));
1285
1569
  if (rawDiff) {
1286
1570
  const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();
1287
1571
  if (cap === "Easy" || cap === "Medium" || cap === "Hard") difficulty = cap;
1288
1572
  }
1289
- subjectId = _nullishCoalesce(_optionalChain([parts, 'access', _16 => _16[9], 'optionalAccess', _17 => _17.trim, 'call', _18 => _18()]), () => ( ""));
1290
- chapterId = _nullishCoalesce(_optionalChain([parts, 'access', _19 => _19[10], 'optionalAccess', _20 => _20.trim, 'call', _21 => _21()]), () => ( ""));
1291
- topicId = _nullishCoalesce(_optionalChain([parts, 'access', _22 => _22[11], 'optionalAccess', _23 => _23.trim, 'call', _24 => _24()]), () => ( ""));
1292
- markingInstructions = _nullishCoalesce(_optionalChain([parts, 'access', _25 => _25[12], 'optionalAccess', _26 => _26.trim, 'call', _27 => _27()]), () => ( ""));
1293
- if (parts.length > 13) id = _nullishCoalesce(_optionalChain([parts, 'access', _28 => _28[13], 'optionalAccess', _29 => _29.trim, 'call', _30 => _30()]), () => ( ""));
1294
- if (parts.length > 14) imageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _31 => _31[14], 'optionalAccess', _32 => _32.trim, 'call', _33 => _33()]), () => ( ""));
1295
- if (parts.length > 15) answerImageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _34 => _34[15], 'optionalAccess', _35 => _35.trim, 'call', _36 => _36()]), () => ( ""));
1296
- if (parts.length > 16) option2ImageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _37 => _37[16], 'optionalAccess', _38 => _38.trim, 'call', _39 => _39()]), () => ( ""));
1297
- if (parts.length > 17) option3ImageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _40 => _40[17], 'optionalAccess', _41 => _41.trim, 'call', _42 => _42()]), () => ( ""));
1298
- if (parts.length > 18) option4ImageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _43 => _43[18], 'optionalAccess', _44 => _44.trim, 'call', _45 => _45()]), () => ( ""));
1573
+ subjectId = _nullishCoalesce(_optionalChain([parts, 'access', _50 => _50[9], 'optionalAccess', _51 => _51.trim, 'call', _52 => _52()]), () => ( ""));
1574
+ chapterId = _nullishCoalesce(_optionalChain([parts, 'access', _53 => _53[10], 'optionalAccess', _54 => _54.trim, 'call', _55 => _55()]), () => ( ""));
1575
+ topicId = _nullishCoalesce(_optionalChain([parts, 'access', _56 => _56[11], 'optionalAccess', _57 => _57.trim, 'call', _58 => _58()]), () => ( ""));
1576
+ markingInstructions = _nullishCoalesce(_optionalChain([parts, 'access', _59 => _59[12], 'optionalAccess', _60 => _60.trim, 'call', _61 => _61()]), () => ( ""));
1577
+ if (parts.length > 13) id = _nullishCoalesce(_optionalChain([parts, 'access', _62 => _62[13], 'optionalAccess', _63 => _63.trim, 'call', _64 => _64()]), () => ( ""));
1578
+ if (parts.length > 14) imageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _65 => _65[14], 'optionalAccess', _66 => _66.trim, 'call', _67 => _67()]), () => ( ""));
1579
+ if (parts.length > 15) answerImageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _68 => _68[15], 'optionalAccess', _69 => _69.trim, 'call', _70 => _70()]), () => ( ""));
1580
+ if (parts.length > 16) option2ImageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _71 => _71[16], 'optionalAccess', _72 => _72.trim, 'call', _73 => _73()]), () => ( ""));
1581
+ if (parts.length > 17) option3ImageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _74 => _74[17], 'optionalAccess', _75 => _75.trim, 'call', _76 => _76()]), () => ( ""));
1582
+ if (parts.length > 18) option4ImageUrl = _nullishCoalesce(_optionalChain([parts, 'access', _77 => _77[18], 'optionalAccess', _78 => _78.trim, 'call', _79 => _79()]), () => ( ""));
1299
1583
  } else if (parts.length === 8) {
1300
- extra = _nullishCoalesce(_optionalChain([parts, 'access', _46 => _46[7], 'optionalAccess', _47 => _47.trim, 'call', _48 => _48()]), () => ( ""));
1584
+ extra = _nullishCoalesce(_optionalChain([parts, 'access', _80 => _80[7], 'optionalAccess', _81 => _81.trim, 'call', _82 => _82()]), () => ( ""));
1301
1585
  } else if (parts.length === 9) {
1302
- extra = _nullishCoalesce(_optionalChain([parts, 'access', _49 => _49[7], 'optionalAccess', _50 => _50.trim, 'call', _51 => _51()]), () => ( ""));
1303
- const rawDiff = _nullishCoalesce(_optionalChain([parts, 'access', _52 => _52[8], 'optionalAccess', _53 => _53.trim, 'call', _54 => _54()]), () => ( ""));
1586
+ extra = _nullishCoalesce(_optionalChain([parts, 'access', _83 => _83[7], 'optionalAccess', _84 => _84.trim, 'call', _85 => _85()]), () => ( ""));
1587
+ const rawDiff = _nullishCoalesce(_optionalChain([parts, 'access', _86 => _86[8], 'optionalAccess', _87 => _87.trim, 'call', _88 => _88()]), () => ( ""));
1304
1588
  if (rawDiff) {
1305
1589
  const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();
1306
1590
  if (cap === "Easy" || cap === "Medium" || cap === "Hard") difficulty = cap;
1307
1591
  }
1308
1592
  } else if (parts.length === 10) {
1309
- subjectId = _nullishCoalesce(_optionalChain([parts, 'access', _55 => _55[7], 'optionalAccess', _56 => _56.trim, 'call', _57 => _57()]), () => ( ""));
1310
- chapterId = _nullishCoalesce(_optionalChain([parts, 'access', _58 => _58[8], 'optionalAccess', _59 => _59.trim, 'call', _60 => _60()]), () => ( ""));
1311
- topicId = _nullishCoalesce(_optionalChain([parts, 'access', _61 => _61[9], 'optionalAccess', _62 => _62.trim, 'call', _63 => _63()]), () => ( ""));
1593
+ subjectId = _nullishCoalesce(_optionalChain([parts, 'access', _89 => _89[7], 'optionalAccess', _90 => _90.trim, 'call', _91 => _91()]), () => ( ""));
1594
+ chapterId = _nullishCoalesce(_optionalChain([parts, 'access', _92 => _92[8], 'optionalAccess', _93 => _93.trim, 'call', _94 => _94()]), () => ( ""));
1595
+ topicId = _nullishCoalesce(_optionalChain([parts, 'access', _95 => _95[9], 'optionalAccess', _96 => _96.trim, 'call', _97 => _97()]), () => ( ""));
1312
1596
  } else if (parts.length === 11) {
1313
- extra = _nullishCoalesce(_optionalChain([parts, 'access', _64 => _64[7], 'optionalAccess', _65 => _65.trim, 'call', _66 => _66()]), () => ( ""));
1314
- subjectId = _nullishCoalesce(_optionalChain([parts, 'access', _67 => _67[8], 'optionalAccess', _68 => _68.trim, 'call', _69 => _69()]), () => ( ""));
1315
- chapterId = _nullishCoalesce(_optionalChain([parts, 'access', _70 => _70[9], 'optionalAccess', _71 => _71.trim, 'call', _72 => _72()]), () => ( ""));
1316
- topicId = _nullishCoalesce(_optionalChain([parts, 'access', _73 => _73[10], 'optionalAccess', _74 => _74.trim, 'call', _75 => _75()]), () => ( ""));
1597
+ extra = _nullishCoalesce(_optionalChain([parts, 'access', _98 => _98[7], 'optionalAccess', _99 => _99.trim, 'call', _100 => _100()]), () => ( ""));
1598
+ subjectId = _nullishCoalesce(_optionalChain([parts, 'access', _101 => _101[8], 'optionalAccess', _102 => _102.trim, 'call', _103 => _103()]), () => ( ""));
1599
+ chapterId = _nullishCoalesce(_optionalChain([parts, 'access', _104 => _104[9], 'optionalAccess', _105 => _105.trim, 'call', _106 => _106()]), () => ( ""));
1600
+ topicId = _nullishCoalesce(_optionalChain([parts, 'access', _107 => _107[10], 'optionalAccess', _108 => _108.trim, 'call', _109 => _109()]), () => ( ""));
1317
1601
  } else if (parts.length === 12) {
1318
- extra = _nullishCoalesce(_optionalChain([parts, 'access', _76 => _76[7], 'optionalAccess', _77 => _77.trim, 'call', _78 => _78()]), () => ( ""));
1319
- const rawDiff = _nullishCoalesce(_optionalChain([parts, 'access', _79 => _79[8], 'optionalAccess', _80 => _80.trim, 'call', _81 => _81()]), () => ( ""));
1602
+ extra = _nullishCoalesce(_optionalChain([parts, 'access', _110 => _110[7], 'optionalAccess', _111 => _111.trim, 'call', _112 => _112()]), () => ( ""));
1603
+ const rawDiff = _nullishCoalesce(_optionalChain([parts, 'access', _113 => _113[8], 'optionalAccess', _114 => _114.trim, 'call', _115 => _115()]), () => ( ""));
1320
1604
  if (rawDiff) {
1321
1605
  const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();
1322
1606
  if (cap === "Easy" || cap === "Medium" || cap === "Hard") difficulty = cap;
1323
1607
  }
1324
- subjectId = _nullishCoalesce(_optionalChain([parts, 'access', _82 => _82[9], 'optionalAccess', _83 => _83.trim, 'call', _84 => _84()]), () => ( ""));
1325
- chapterId = _nullishCoalesce(_optionalChain([parts, 'access', _85 => _85[10], 'optionalAccess', _86 => _86.trim, 'call', _87 => _87()]), () => ( ""));
1326
- topicId = _nullishCoalesce(_optionalChain([parts, 'access', _88 => _88[11], 'optionalAccess', _89 => _89.trim, 'call', _90 => _90()]), () => ( ""));
1608
+ subjectId = _nullishCoalesce(_optionalChain([parts, 'access', _116 => _116[9], 'optionalAccess', _117 => _117.trim, 'call', _118 => _118()]), () => ( ""));
1609
+ chapterId = _nullishCoalesce(_optionalChain([parts, 'access', _119 => _119[10], 'optionalAccess', _120 => _120.trim, 'call', _121 => _121()]), () => ( ""));
1610
+ topicId = _nullishCoalesce(_optionalChain([parts, 'access', _122 => _122[11], 'optionalAccess', _123 => _123.trim, 'call', _124 => _124()]), () => ( ""));
1327
1611
  }
1328
1612
  return {
1329
1613
  id: id || void 0,
@@ -1342,18 +1626,18 @@ function parseFields(parts) {
1342
1626
  }
1343
1627
  function buildQuestion(parts) {
1344
1628
  const tags = parts.length >= 7 && parts[6] ? parts[6].split(",").map((t) => t.trim()).filter(Boolean) : [];
1345
- const opt2 = _nullishCoalesce(_optionalChain([parts, 'access', _91 => _91[2], 'optionalAccess', _92 => _92.trim, 'call', _93 => _93()]), () => ( ""));
1346
- const opt3 = _nullishCoalesce(_optionalChain([parts, 'access', _94 => _94[3], 'optionalAccess', _95 => _95.trim, 'call', _96 => _96()]), () => ( ""));
1347
- const opt4 = _nullishCoalesce(_optionalChain([parts, 'access', _97 => _97[4], 'optionalAccess', _98 => _98.trim, 'call', _99 => _99()]), () => ( ""));
1629
+ const opt2 = _nullishCoalesce(_optionalChain([parts, 'access', _125 => _125[2], 'optionalAccess', _126 => _126.trim, 'call', _127 => _127()]), () => ( ""));
1630
+ const opt3 = _nullishCoalesce(_optionalChain([parts, 'access', _128 => _128[3], 'optionalAccess', _129 => _129.trim, 'call', _130 => _130()]), () => ( ""));
1631
+ const opt4 = _nullishCoalesce(_optionalChain([parts, 'access', _131 => _131[4], 'optionalAccess', _132 => _132.trim, 'call', _133 => _133()]), () => ( ""));
1348
1632
  const isWritten = parts.length < 6 || !opt2 && !opt3 && !opt4;
1349
1633
  const extra = parseFields(parts);
1350
1634
  return {
1351
- question: _nullishCoalesce(_optionalChain([parts, 'access', _100 => _100[0], 'optionalAccess', _101 => _101.trim, 'call', _102 => _102()]), () => ( "")),
1352
- answer: _nullishCoalesce(_optionalChain([parts, 'access', _103 => _103[1], 'optionalAccess', _104 => _104.trim, 'call', _105 => _105()]), () => ( "")),
1635
+ question: _nullishCoalesce(_optionalChain([parts, 'access', _134 => _134[0], 'optionalAccess', _135 => _135.trim, 'call', _136 => _136()]), () => ( "")),
1636
+ answer: _nullishCoalesce(_optionalChain([parts, 'access', _137 => _137[1], 'optionalAccess', _138 => _138.trim, 'call', _139 => _139()]), () => ( "")),
1353
1637
  option2: opt2,
1354
1638
  option3: opt3,
1355
1639
  option4: opt4,
1356
- explanation: _nullishCoalesce(_optionalChain([parts, 'access', _106 => _106[5], 'optionalAccess', _107 => _107.trim, 'call', _108 => _108()]), () => ( "")),
1640
+ explanation: _nullishCoalesce(_optionalChain([parts, 'access', _140 => _140[5], 'optionalAccess', _141 => _141.trim, 'call', _142 => _142()]), () => ( "")),
1357
1641
  tags,
1358
1642
  type: isWritten ? "WRITTEN" : "MCQ",
1359
1643
  ...extra
@@ -1404,9 +1688,9 @@ function formatQuestionsToText(questions, keyword, packSubjectId, packChapterId,
1404
1688
  const activeSubject = q.subjectId || packSubjectId;
1405
1689
  const activeChapter = q.chapterId || packChapterId;
1406
1690
  const activeTopic = q.topicId || packTopicId;
1407
- const subjectSlug = activeSubject ? _nullishCoalesce(_optionalChain([activeSubject, 'access', _109 => _109.split, 'call', _110 => _110("::"), 'access', _111 => _111[0], 'optionalAccess', _112 => _112.trim, 'call', _113 => _113()]), () => ( "")) : "";
1408
- const chapterSlug = activeChapter ? _nullishCoalesce(_optionalChain([activeChapter, 'access', _114 => _114.split, 'call', _115 => _115("::"), 'access', _116 => _116[0], 'optionalAccess', _117 => _117.trim, 'call', _118 => _118()]), () => ( "")) : "";
1409
- const topicSlug = activeTopic ? _nullishCoalesce(_optionalChain([activeTopic, 'access', _119 => _119.split, 'call', _120 => _120("::"), 'access', _121 => _121[0], 'optionalAccess', _122 => _122.trim, 'call', _123 => _123()]), () => ( "")) : "";
1691
+ const subjectSlug = activeSubject ? _nullishCoalesce(_optionalChain([activeSubject, 'access', _143 => _143.split, 'call', _144 => _144("::"), 'access', _145 => _145[0], 'optionalAccess', _146 => _146.trim, 'call', _147 => _147()]), () => ( "")) : "";
1692
+ const chapterSlug = activeChapter ? _nullishCoalesce(_optionalChain([activeChapter, 'access', _148 => _148.split, 'call', _149 => _149("::"), 'access', _150 => _150[0], 'optionalAccess', _151 => _151.trim, 'call', _152 => _152()]), () => ( "")) : "";
1693
+ const topicSlug = activeTopic ? _nullishCoalesce(_optionalChain([activeTopic, 'access', _153 => _153.split, 'call', _154 => _154("::"), 'access', _155 => _155[0], 'optionalAccess', _156 => _156.trim, 'call', _157 => _157()]), () => ( "")) : "";
1410
1694
  const fields = [
1411
1695
  q.question,
1412
1696
  q.answer,
@@ -1439,9 +1723,9 @@ function formatQuestionsToCsv(questions, packSubjectId, packChapterId, packTopic
1439
1723
  const activeSubject = q.subjectId || packSubjectId;
1440
1724
  const activeChapter = q.chapterId || packChapterId;
1441
1725
  const activeTopic = q.topicId || packTopicId;
1442
- const subjectSlug = activeSubject ? _nullishCoalesce(_optionalChain([activeSubject, 'access', _124 => _124.split, 'call', _125 => _125("::"), 'access', _126 => _126[0], 'optionalAccess', _127 => _127.trim, 'call', _128 => _128()]), () => ( "")) : "";
1443
- const chapterSlug = activeChapter ? _nullishCoalesce(_optionalChain([activeChapter, 'access', _129 => _129.split, 'call', _130 => _130("::"), 'access', _131 => _131[0], 'optionalAccess', _132 => _132.trim, 'call', _133 => _133()]), () => ( "")) : "";
1444
- const topicSlug = activeTopic ? _nullishCoalesce(_optionalChain([activeTopic, 'access', _134 => _134.split, 'call', _135 => _135("::"), 'access', _136 => _136[0], 'optionalAccess', _137 => _137.trim, 'call', _138 => _138()]), () => ( "")) : "";
1726
+ const subjectSlug = activeSubject ? _nullishCoalesce(_optionalChain([activeSubject, 'access', _158 => _158.split, 'call', _159 => _159("::"), 'access', _160 => _160[0], 'optionalAccess', _161 => _161.trim, 'call', _162 => _162()]), () => ( "")) : "";
1727
+ const chapterSlug = activeChapter ? _nullishCoalesce(_optionalChain([activeChapter, 'access', _163 => _163.split, 'call', _164 => _164("::"), 'access', _165 => _165[0], 'optionalAccess', _166 => _166.trim, 'call', _167 => _167()]), () => ( "")) : "";
1728
+ const topicSlug = activeTopic ? _nullishCoalesce(_optionalChain([activeTopic, 'access', _168 => _168.split, 'call', _169 => _169("::"), 'access', _170 => _170[0], 'optionalAccess', _171 => _171.trim, 'call', _172 => _172()]), () => ( "")) : "";
1445
1729
  return [
1446
1730
  escapeCSV(q.question),
1447
1731
  escapeCSV(q.answer),
@@ -1468,126 +1752,37 @@ function formatQuestionsToCsv(questions, packSubjectId, packChapterId, packTopic
1468
1752
  ${rows.join("\n")}`;
1469
1753
  }
1470
1754
 
1471
- // src/logic/media-url.ts
1472
- function isStorageKey(value) {
1473
- return !value.startsWith("http://") && !value.startsWith("https://");
1474
- }
1475
- var MediaUrlCache = class {
1476
- constructor(ttlMs = 50 * 60 * 1e3) {
1477
- this.cache = /* @__PURE__ */ new Map();
1478
- this.ttlMs = ttlMs;
1479
- }
1480
- get(key) {
1481
- const entry = this.cache.get(key);
1482
- if (!entry) return void 0;
1483
- if (Date.now() >= entry.expiresAt) {
1484
- this.cache.delete(key);
1485
- return void 0;
1486
- }
1487
- return entry.url;
1755
+ // src/navigation.ts
1756
+ function getActiveTab(pathname) {
1757
+ const normalized = pathname.toLowerCase();
1758
+ if (normalized.startsWith("/contests") || normalized.startsWith("/contest")) {
1759
+ return "contests";
1488
1760
  }
1489
- set(key, url) {
1490
- this.cache.set(key, { url, expiresAt: Date.now() + this.ttlMs });
1761
+ if (normalized.startsWith("/courses") || normalized.startsWith("/course") || normalized.startsWith("/exams")) {
1762
+ return "courses";
1491
1763
  }
1492
- clear() {
1493
- this.cache.clear();
1764
+ if (normalized.startsWith("/profile") || normalized.startsWith("/dashboard") || normalized.startsWith("/edit-profile")) {
1765
+ return "profile";
1494
1766
  }
1495
- };
1496
- var warnedNoStorage = false;
1497
- async function resolveMediaUrl(value, storage, cache) {
1498
- if (!value || value.length === 0) return null;
1499
- if (!isStorageKey(value)) return value;
1500
- if (cache) {
1501
- const cached = cache.get(value);
1502
- if (cached) return cached;
1503
- }
1504
- if (!storage) {
1505
- if (!warnedNoStorage) {
1506
- warnedNoStorage = true;
1507
- console.warn("@orjok/commons: resolveMediaUrl called without a StorageProvider \u2014 returning raw key");
1508
- }
1509
- return value;
1767
+ if (normalized.startsWith("/learn") || normalized.startsWith("/practice") || normalized.startsWith("/questions") || normalized.startsWith("/question") || normalized.startsWith("/bank") || normalized.startsWith("/question-bank") || normalized.startsWith("/packs") || normalized.startsWith("/pack") || normalized.startsWith("/taxonomy") || normalized.startsWith("/explore")) {
1768
+ return "learn";
1510
1769
  }
1511
- const { url } = await storage.getFileUrl(value);
1512
- _optionalChain([cache, 'optionalAccess', _139 => _139.set, 'call', _140 => _140(value, url)]);
1513
- return url;
1514
- }
1515
- async function resolveMediaUrls(obj, fields, storage, cache) {
1516
- const copy = { ...obj };
1517
- await Promise.all(
1518
- fields.map(async (field) => {
1519
- const val = copy[field];
1520
- if (typeof val === "string") {
1521
- copy[field] = await resolveMediaUrl(val, storage, cache);
1522
- }
1523
- })
1524
- );
1525
- return copy;
1770
+ return "home";
1526
1771
  }
1527
- function _resetMediaUrlWarning() {
1528
- warnedNoStorage = false;
1772
+ function getCanonicalPath(pathname) {
1773
+ const normalized = pathname.toLowerCase();
1774
+ if (normalized === "/explore/questions") return "/learn/questions";
1775
+ if (normalized === "/explore/packs") return "/learn/packs";
1776
+ if (normalized === "/question-bank") return "/learn/bank";
1777
+ if (normalized === "/contest/all") return "/contests";
1778
+ if (normalized === "/dashboard") return "/profile";
1779
+ if (normalized === "/edit-profile") return "/profile/edit";
1780
+ return pathname;
1529
1781
  }
1530
-
1531
- // src/logic/practice.ts
1532
- function aggregateSubjectMetrics(progress) {
1533
- let totalPracticed = 0;
1534
- let totalMistakes = 0;
1535
- let totalCorrected = 0;
1536
- for (const p of progress) {
1537
- totalPracticed += _nullishCoalesce(p.practicedCount, () => ( 0));
1538
- totalMistakes += _nullishCoalesce(p.mistakeCount, () => ( 0));
1539
- totalCorrected += _nullishCoalesce(p.correctedCount, () => ( 0));
1540
- }
1541
- const accuracy = totalPracticed > 0 ? Math.round((totalPracticed - totalMistakes) / totalPracticed * 100) : 0;
1542
- return { totalPracticed, totalMistakes, totalCorrected, accuracy };
1543
- }
1544
- function aggregateChapterMetrics(progress) {
1545
- let totalPracticed = 0;
1546
- let totalMistakes = 0;
1547
- let totalCorrected = 0;
1548
- for (const p of progress) {
1549
- totalPracticed += _nullishCoalesce(p.practicedCount, () => ( 0));
1550
- totalMistakes += _nullishCoalesce(p.mistakeCount, () => ( 0));
1551
- totalCorrected += _nullishCoalesce(p.correctedCount, () => ( 0));
1552
- }
1553
- const accuracy = totalPracticed > 0 ? Math.round((totalPracticed - totalMistakes) / totalPracticed * 100) : 0;
1554
- return { totalPracticed, totalMistakes, totalCorrected, accuracy };
1555
- }
1556
- function aggregateTopicMetrics(progress) {
1557
- let totalPracticed = 0;
1558
- let totalMistakes = 0;
1559
- let totalCorrected = 0;
1560
- for (const p of progress) {
1561
- totalPracticed += _nullishCoalesce(p.practicedCount, () => ( 0));
1562
- totalMistakes += _nullishCoalesce(p.mistakeCount, () => ( 0));
1563
- totalCorrected += _nullishCoalesce(p.correctedCount, () => ( 0));
1564
- }
1565
- const accuracy = totalPracticed > 0 ? Math.round((totalPracticed - totalMistakes) / totalPracticed * 100) : 0;
1566
- return { totalPracticed, totalMistakes, totalCorrected, accuracy };
1567
- }
1568
- function calculateAccuracy(practiced, mistakes) {
1569
- if (practiced <= 0) return 0;
1570
- return Math.round((practiced - mistakes) / practiced * 100);
1571
- }
1572
-
1573
- // src/utils/shuffle.ts
1574
- function shuffle(array) {
1575
- const result = [...array];
1576
- let currentIndex = result.length;
1577
- while (currentIndex !== 0) {
1578
- const randomIndex = Math.floor(Math.random() * currentIndex);
1579
- currentIndex--;
1580
- [result[currentIndex], result[randomIndex]] = [result[randomIndex], result[currentIndex]];
1581
- }
1582
- return result;
1583
- }
1584
- function shuffleInPlace(array) {
1585
- let currentIndex = array.length;
1586
- while (currentIndex !== 0) {
1587
- const randomIndex = Math.floor(Math.random() * currentIndex);
1588
- currentIndex--;
1589
- [array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
1590
- }
1782
+ function isAuthGatedRoute(pathname) {
1783
+ const normalized = pathname.toLowerCase();
1784
+ const isProtectedPath = normalized.includes("/exam") || normalized.includes("/enroll") || normalized.startsWith("/profile") || normalized.startsWith("/dashboard") || normalized.startsWith("/edit-profile") || normalized.startsWith("/avatar-creator") || normalized.startsWith("/practice");
1785
+ return isProtectedPath;
1591
1786
  }
1592
1787
 
1593
1788
  // src/utils/bangla-numbers.ts
@@ -1600,15 +1795,51 @@ function engToBanglaNumber(num) {
1600
1795
  }
1601
1796
 
1602
1797
  // src/utils/format-curriculum.ts
1603
- function formatCurriculumName(idString, dbName) {
1604
- if (dbName) return dbName;
1798
+ function extractBilingualName(name, language) {
1799
+ if (!name) return "";
1800
+ const trimmed = name.trim();
1801
+ const match = trimmed.match(/^(.*?)\s*[\(\[(]\s*([^\)\])]+)\s*[\)\])]\s*$/);
1802
+ if (!match) {
1803
+ return trimmed;
1804
+ }
1805
+ const part1 = _optionalChain([match, 'access', _173 => _173[1], 'optionalAccess', _174 => _174.trim, 'call', _175 => _175()]) || "";
1806
+ const part2 = _optionalChain([match, 'access', _176 => _176[2], 'optionalAccess', _177 => _177.trim, 'call', _178 => _178()]) || "";
1807
+ if (!part1 && !part2) return trimmed;
1808
+ if (!part1) return part2;
1809
+ if (!part2) return part1;
1810
+ const hasBengali = (text) => /[\u0980-\u09FF]/.test(text);
1811
+ const isPart1Bengali = hasBengali(part1);
1812
+ const isPart2Bengali = hasBengali(part2);
1813
+ let banglaPart = part1;
1814
+ let englishPart = part2;
1815
+ if (!isPart1Bengali && isPart2Bengali) {
1816
+ banglaPart = part2;
1817
+ englishPart = part1;
1818
+ } else {
1819
+ banglaPart = part1;
1820
+ englishPart = part2;
1821
+ }
1822
+ const lang = _optionalChain([language, 'optionalAccess', _179 => _179.toLowerCase, 'call', _180 => _180()]);
1823
+ if (lang === "en") {
1824
+ return englishPart || banglaPart || trimmed;
1825
+ }
1826
+ if (lang === "bn") {
1827
+ return banglaPart || englishPart || trimmed;
1828
+ }
1829
+ return trimmed;
1830
+ }
1831
+ function formatCurriculumName(idString, dbName, language) {
1832
+ if (dbName) {
1833
+ return extractBilingualName(dbName, language);
1834
+ }
1605
1835
  if (!idString) return "";
1606
1836
  const namePart = _nullishCoalesce(idString.split("::")[0], () => ( ""));
1607
1837
  const clean = namePart.replace(/[-_]/g, " ");
1608
- return clean.split(/\s+/).map((word) => {
1838
+ const formatted = clean.split(/\s+/).map((word) => {
1609
1839
  if (!word) return "";
1610
1840
  return word.charAt(0).toUpperCase() + word.slice(1);
1611
1841
  }).join(" ");
1842
+ return extractBilingualName(formatted, language);
1612
1843
  }
1613
1844
 
1614
1845
  // src/utils/relative-time.ts
@@ -1674,6 +1905,141 @@ function processLatexText(str) {
1674
1905
  return result;
1675
1906
  }
1676
1907
 
1908
+ // src/utils/time.ts
1909
+ function convertToBangladeshTime(isoString) {
1910
+ const options = {
1911
+ year: "numeric",
1912
+ month: "long",
1913
+ day: "numeric",
1914
+ hour: "numeric",
1915
+ minute: "numeric",
1916
+ hour12: true
1917
+ };
1918
+ const date = new Date(isoString);
1919
+ return date.toLocaleString("en-GB", {
1920
+ ...options,
1921
+ timeZone: "Asia/Dhaka"
1922
+ });
1923
+ }
1924
+ function getDhakaNow() {
1925
+ const now = /* @__PURE__ */ new Date();
1926
+ const parts = new Intl.DateTimeFormat("en-CA", {
1927
+ timeZone: "Asia/Dhaka",
1928
+ year: "numeric",
1929
+ month: "2-digit",
1930
+ day: "2-digit",
1931
+ hour: "2-digit",
1932
+ minute: "2-digit",
1933
+ second: "2-digit",
1934
+ hour12: false
1935
+ }).formatToParts(now);
1936
+ const get = (type) => _nullishCoalesce(_optionalChain([parts, 'access', _181 => _181.find, 'call', _182 => _182((p) => p.type === type), 'optionalAccess', _183 => _183.value]), () => ( "00"));
1937
+ return /* @__PURE__ */ new Date(`${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}`);
1938
+ }
1939
+ function calculateTimeLeft(endTime) {
1940
+ const target = new Date(endTime).getTime();
1941
+ const now = (/* @__PURE__ */ new Date()).getTime();
1942
+ const difference = target - now;
1943
+ if (difference <= 0 || isNaN(difference)) {
1944
+ return {
1945
+ days: 0,
1946
+ hours: 0,
1947
+ minutes: 0,
1948
+ seconds: 0,
1949
+ isEnded: true
1950
+ };
1951
+ }
1952
+ return {
1953
+ days: Math.floor(difference / (1e3 * 60 * 60 * 24)),
1954
+ hours: Math.floor(difference / (1e3 * 60 * 60) % 24),
1955
+ minutes: Math.floor(difference / 1e3 / 60 % 60),
1956
+ seconds: Math.floor(difference / 1e3 % 60),
1957
+ isEnded: false
1958
+ };
1959
+ }
1960
+ function formatDate(date, locale = "en-GB") {
1961
+ const d = typeof date === "string" ? new Date(date) : date;
1962
+ return d.toLocaleDateString(locale, {
1963
+ day: "2-digit",
1964
+ month: "short",
1965
+ year: "numeric"
1966
+ });
1967
+ }
1968
+
1969
+ // src/utils/currency.ts
1970
+ function formatBDT(amount) {
1971
+ return amount.toLocaleString("en-US");
1972
+ }
1973
+
1974
+ // src/utils/url.ts
1975
+ function getYouTubeId(url) {
1976
+ if (!url) return null;
1977
+ const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|[?&]v=)([^#&?]*).*/;
1978
+ const match = url.match(regExp);
1979
+ return match && _optionalChain([match, 'access', _184 => _184[2], 'optionalAccess', _185 => _185.length]) === 11 ? match[2] : null;
1980
+ }
1981
+
1982
+ // src/utils/file.ts
1983
+ function sanitizeFileName(name) {
1984
+ return name.replace(/[\s,]+/g, "_");
1985
+ }
1986
+
1987
+ // src/utils/guards.ts
1988
+ function isQuestionLevel(value) {
1989
+ return [
1990
+ "One",
1991
+ "Two",
1992
+ "Three",
1993
+ "Four",
1994
+ "Five",
1995
+ "Six",
1996
+ "Seven",
1997
+ "Eight",
1998
+ "SSC",
1999
+ "HSC",
2000
+ "Admission",
2001
+ "HSC+Admission",
2002
+ "BCS"
2003
+ ].includes(value);
2004
+ }
2005
+ function isLanguage(value) {
2006
+ return ["Bangla", "English", "Any"].includes(value);
2007
+ }
2008
+ function isDifficulty(value) {
2009
+ return ["Easy", "Medium", "Hard"].includes(value);
2010
+ }
2011
+
2012
+
2013
+
2014
+
2015
+
2016
+
2017
+
2018
+
2019
+
2020
+
2021
+
2022
+
2023
+
2024
+
2025
+
2026
+
2027
+
2028
+
2029
+
2030
+
2031
+
2032
+
2033
+
2034
+
2035
+
2036
+
2037
+
2038
+
2039
+
2040
+
2041
+
2042
+
1677
2043
 
1678
2044
 
1679
2045
 
@@ -1719,5 +2085,5 @@ function processLatexText(str) {
1719
2085
 
1720
2086
 
1721
2087
 
1722
- exports.AIService = AIService; exports.ContestService = ContestService; exports.CourseService = CourseService; exports.CurriculumService = CurriculumService; exports.MediaService = MediaService; exports.MediaUrlCache = MediaUrlCache; exports.NewsService = NewsService; exports.OrjokClient = OrjokClient; exports.PackService = PackService; exports.ProgressService = ProgressService; exports.QuestionService = QuestionService; exports.UserService = UserService; exports._resetMediaUrlWarning = _resetMediaUrlWarning; exports.aggregateChapterMetrics = aggregateChapterMetrics; exports.aggregateSubjectMetrics = aggregateSubjectMetrics; exports.aggregateTopicMetrics = aggregateTopicMetrics; exports.buildImageAnswer = buildImageAnswer; exports.calculateAccuracy = calculateAccuracy; exports.calculateExamTime = calculateExamTime; exports.computeRatingDelta = computeRatingDelta; exports.createOrjokClient = createOrjokClient; exports.detectQuestionType = detectQuestionType; exports.engToBanglaNumber = engToBanglaNumber; exports.filterQuestionsByMode = filterQuestionsByMode; exports.formatCurriculumName = formatCurriculumName; exports.formatExamTime = formatExamTime; exports.formatQuestionsToCsv = formatQuestionsToCsv; exports.formatQuestionsToText = formatQuestionsToText; exports.getAllTiers = getAllTiers; exports.getEloProgress = getEloProgress; exports.getEloTier = getEloTier; exports.getRatingForSubject = getRatingForSubject; exports.getRelativeTime = getRelativeTime; exports.isImageAnswer = isImageAnswer; exports.isStorageKey = isStorageKey; exports.normalizeDifficulty = normalizeDifficulty; exports.parseCsvQuestions = parseCsvQuestions; exports.parseImageKeys = parseImageKeys; exports.parseTextQuestions = parseTextQuestions; exports.processLatexText = processLatexText; exports.resolveMediaUrl = resolveMediaUrl; exports.resolveMediaUrls = resolveMediaUrls; exports.scoreExam = scoreExam; exports.shuffle = shuffle; exports.shuffleInPlace = shuffleInPlace;
2088
+ exports.AIService = AIService; exports.CLOTHES_COLORS = CLOTHES_COLORS; exports.COLOR_HUES = COLOR_HUES; exports.ContestService = ContestService; exports.CourseService = CourseService; exports.CurriculumService = CurriculumService; exports.HAIR_COLORS = HAIR_COLORS; exports.INITIAL_AVATAR_DATA = INITIAL_AVATAR_DATA; exports.MediaService = MediaService; exports.MediaUrlCache = _chunkDB3CMKPPcjs.MediaUrlCache; exports.NewsService = NewsService; exports.OrjokClient = OrjokClient; exports.PackService = PackService; exports.ProgressService = ProgressService; exports.QuestionService = QuestionService; exports.RENDER_ORDER = RENDER_ORDER; exports.UserService = UserService; exports._resetMediaUrlWarning = _chunkDB3CMKPPcjs._resetMediaUrlWarning; exports.aggregateChapterMetrics = _chunkDB3CMKPPcjs.aggregateChapterMetrics; exports.aggregateSubjectMetrics = _chunkDB3CMKPPcjs.aggregateSubjectMetrics; exports.aggregateTopicMetrics = _chunkDB3CMKPPcjs.aggregateTopicMetrics; exports.buildImageAnswer = buildImageAnswer; exports.calculateAccuracy = _chunkDB3CMKPPcjs.calculateAccuracy; exports.calculateDefaultExamMinutes = calculateDefaultExamMinutes; exports.calculateExamTime = calculateExamTime; exports.calculateTimeLeft = calculateTimeLeft; exports.computeRatingDelta = computeRatingDelta; exports.convertToBangladeshTime = convertToBangladeshTime; exports.createOrjokClient = createOrjokClient; exports.detectQuestionType = detectQuestionType; exports.difficultyOptions = difficultyOptions; exports.engToBanglaNumber = engToBanglaNumber; exports.extractBilingualName = extractBilingualName; exports.filterQuestionsByMode = filterQuestionsByMode; exports.formatBDT = formatBDT; exports.formatCurriculumName = formatCurriculumName; exports.formatDate = formatDate; exports.formatExamTime = formatExamTime; exports.formatQuestionsToCsv = formatQuestionsToCsv; exports.formatQuestionsToText = formatQuestionsToText; exports.getActiveTab = getActiveTab; exports.getActualOptionFileName = getActualOptionFileName; exports.getAllTiers = getAllTiers; exports.getCanonicalPath = getCanonicalPath; exports.getDhakaNow = getDhakaNow; exports.getEloProgress = getEloProgress; exports.getEloTier = getEloTier; exports.getRatingForSubject = getRatingForSubject; exports.getRelativeTime = getRelativeTime; exports.getYouTubeId = getYouTubeId; exports.isAuthGatedRoute = isAuthGatedRoute; exports.isCorrectAnswer = isCorrectAnswer; exports.isDifficulty = isDifficulty; exports.isDifficultyMatch = _chunkDB3CMKPPcjs.isDifficultyMatch; exports.isImageAnswer = isImageAnswer; exports.isLanguage = isLanguage; exports.isLanguageMatch = _chunkDB3CMKPPcjs.isLanguageMatch; exports.isQuestionLevel = isQuestionLevel; exports.isStorageKey = _chunkDB3CMKPPcjs.isStorageKey; exports.isValidMCQ = _chunkDB3CMKPPcjs.isValidMCQ; exports.languageOptions = languageOptions; exports.levelOptions = levelOptions; exports.normalizeDifficulty = normalizeDifficulty; exports.parseAvatarConfig = parseAvatarConfig; exports.parseCsvQuestions = parseCsvQuestions; exports.parseImageKeys = parseImageKeys; exports.parseTextQuestions = parseTextQuestions; exports.processLatexText = processLatexText; exports.reorderObjectKeys = reorderObjectKeys; exports.resolveMediaUrl = _chunkDB3CMKPPcjs.resolveMediaUrl; exports.resolveMediaUrls = _chunkDB3CMKPPcjs.resolveMediaUrls; exports.sanitizeFileName = sanitizeFileName; exports.scoreExam = scoreExam; exports.serializeAvatarConfig = serializeAvatarConfig; exports.shuffle = _chunkDB3CMKPPcjs.shuffle; exports.shuffleInPlace = _chunkDB3CMKPPcjs.shuffleInPlace;
1723
2089
  //# sourceMappingURL=index.cjs.map