@qti-editor/core 0.1.0 → 0.3.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.
@@ -7,12 +7,13 @@
7
7
  import type { ResponseProcessingKind } from '@qti-editor/interfaces';
8
8
  export interface ComposerItemContext {
9
9
  identifier?: string;
10
+ lang?: string;
10
11
  title?: string;
11
12
  itemBody?: Document;
12
13
  }
13
14
  export interface ResponseDeclaration {
14
15
  identifier: string;
15
- cardinality: 'single' | 'multiple';
16
+ cardinality: 'single' | 'multiple' | 'ordered';
16
17
  baseType: 'identifier' | 'point' | 'string';
17
18
  correctResponse?: string;
18
19
  stringMapping?: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composer/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAErE,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,QAAQ,GAAG,UAAU,CAAC;IACnC,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;YACpB,aAAa,EAAE,OAAO,CAAC;SACxB,CAAC,CAAC;KACJ,CAAC;IACF,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,WAAW,CAAC,EAAE;QACZ,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,CAAC;YACb,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC;YACzB,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACJ,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAUD,wBAAgB,2BAA2B,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,mBAAmB,EAAE,CAOhG;AAED,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAuHhF;AA6KD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4B7C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composer/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAErE,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;IAC/C,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE;QACd,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;YACpB,aAAa,EAAE,OAAO,CAAC;SACxB,CAAC,CAAC;KACJ,CAAC;IACF,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD,WAAW,CAAC,EAAE;QACZ,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,KAAK,CAAC;YACb,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC;YACzB,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACJ,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AAoCD,wBAAgB,2BAA2B,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,mBAAmB,EAAE,CAOhG;AAED,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAqHhF;AAgLD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4B7C"}
@@ -10,6 +10,28 @@ const XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance';
10
10
  const XML_NS = 'http://www.w3.org/XML/1998/namespace';
11
11
  const SCHEMA_LOCATION = 'http://www.imsglobal.org/xsd/imsqtiasi_v3p0 https://purl.imsglobal.org/spec/qti/v3p0/schema/xsd/imsqti_asiv3p0p1_v1p0.xsd';
12
12
  const MATCH_CORRECT_TEMPLATE = 'https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct';
13
+ function parseCorrectResponseValues(declaration) {
14
+ if (!declaration.correctResponse)
15
+ return [];
16
+ if (declaration.cardinality === 'single') {
17
+ return [declaration.correctResponse];
18
+ }
19
+ if (declaration.cardinality === 'ordered') {
20
+ try {
21
+ const parsed = JSON.parse(declaration.correctResponse);
22
+ if (Array.isArray(parsed)) {
23
+ return parsed
24
+ .filter((value) => typeof value === 'string')
25
+ .map(value => value.trim())
26
+ .filter(Boolean);
27
+ }
28
+ }
29
+ catch {
30
+ // Fall back to comma-separated parsing for malformed or legacy values.
31
+ }
32
+ }
33
+ return declaration.correctResponse.split(',').map(v => v.trim()).filter(Boolean);
34
+ }
13
35
  export function extractResponseDeclarations(itemBodyRoot) {
14
36
  if (!itemBodyRoot)
15
37
  return [];
@@ -29,7 +51,7 @@ export function buildAssessmentItemXml(itemContext) {
29
51
  root.setAttribute('title', itemContext.title?.trim() || 'Untitled Item');
30
52
  root.setAttribute('adaptive', 'false');
31
53
  root.setAttribute('time-dependent', 'false');
32
- root.setAttributeNS(XML_NS, 'xml:lang', 'en');
54
+ root.setAttributeNS(XML_NS, 'xml:lang', itemContext.lang?.trim() || 'en');
33
55
  const sourceBodyDoc = itemContext.itemBody;
34
56
  const sourceBodyRoot = sourceBodyDoc?.querySelector('qti-item-body') ??
35
57
  (sourceBodyDoc?.documentElement?.tagName.toLowerCase() === 'qti-item-body'
@@ -46,9 +68,7 @@ export function buildAssessmentItemXml(itemContext) {
46
68
  responseDeclaration.setAttribute('base-type', declaration.baseType);
47
69
  if (declaration.correctResponse) {
48
70
  const correctResponse = xmlDoc.createElementNS(QTI_NS, 'qti-correct-response');
49
- const values = declaration.cardinality === 'multiple'
50
- ? declaration.correctResponse.split(',').map(v => v.trim()).filter(Boolean)
51
- : [declaration.correctResponse];
71
+ const values = parseCorrectResponseValues(declaration);
52
72
  values.forEach(v => {
53
73
  const value = xmlDoc.createElementNS(QTI_NS, 'qti-value');
54
74
  value.textContent = v;
@@ -126,6 +146,7 @@ function composeAndNormalizeItemBody(itemBody, xmlDoc) {
126
146
  elements.forEach(element => {
127
147
  const tagName = element.tagName.toLowerCase();
128
148
  const handler = getInteractionComposerHandler(tagName);
149
+ const isInteractionCandidate = tagName.endsWith('-interaction') || element.hasAttribute('response-identifier');
129
150
  if (handler) {
130
151
  const composeResult = handler.compose(element, xmlDoc);
131
152
  composeResult.warnings.forEach(warning => {
@@ -152,7 +173,9 @@ function composeAndNormalizeItemBody(itemBody, xmlDoc) {
152
173
  }
153
174
  return;
154
175
  }
155
- console.warn(`[QTI Composer] Missing interaction composer handler for ${tagName}; keeping element as-is during compose.`);
176
+ if (isInteractionCandidate) {
177
+ console.warn(`[QTI Composer] Missing interaction composer handler for ${tagName}; keeping element as-is during compose.`);
178
+ }
156
179
  });
157
180
  itemBody.querySelectorAll('[correct-response]').forEach(interaction => {
158
181
  interaction.removeAttribute('correct-response');
@@ -1 +1 @@
1
- {"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/interactions/composer.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AA+BhC,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS,CAEvG;AAED,wBAAgB,4CAA4C,CAC1D,YAAY,EAAE,MAAM,GACnB,2BAA2B,GAAG,SAAS,CAEzC;AAED,wBAAgB,2CAA2C,CACzD,YAAY,EAAE,MAAM,GACnB,0BAA0B,GAAG,SAAS,CAExC;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,GAAG,SAAS,CAErG;AAED,wBAAgB,+BAA+B,IAAI,aAAa,CAAC,0BAA0B,CAAC,CAE3F;AAED,wBAAgB,0BAA0B,IAAI,aAAa,CAAC,qBAAqB,CAAC,CAEjF"}
1
+ {"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/interactions/composer.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,wBAAwB,CAAC;AAkChC,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,2BAA2B,GAAG,SAAS,CAEvG;AAED,wBAAgB,4CAA4C,CAC1D,YAAY,EAAE,MAAM,GACnB,2BAA2B,GAAG,SAAS,CAEzC;AAED,wBAAgB,2CAA2C,CACzD,YAAY,EAAE,MAAM,GACnB,0BAA0B,GAAG,SAAS,CAExC;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,0BAA0B,GAAG,SAAS,CAErG;AAED,wBAAgB,+BAA+B,IAAI,aAAa,CAAC,0BAA0B,CAAC,CAE3F;AAED,wBAAgB,0BAA0B,IAAI,aAAa,CAAC,qBAAqB,CAAC,CAEjF"}
@@ -1,14 +1,20 @@
1
+ import { associateInteractionDescriptor } from '@qti-editor/interaction-associate';
1
2
  import { choiceInteractionDescriptor } from '@qti-editor/interaction-choice';
2
3
  import { extendedTextInteractionDescriptor } from '@qti-editor/interaction-extended-text';
4
+ import { hottextInteractionDescriptor } from '@qti-editor/interaction-hottext';
3
5
  import { inlineChoiceInteractionDescriptor } from '@qti-editor/interaction-inline-choice';
4
6
  import { matchInteractionDescriptor } from '@qti-editor/interaction-match';
7
+ import { orderInteractionDescriptor } from '@qti-editor/interaction-order';
5
8
  import { selectPointInteractionDescriptor } from '@qti-editor/interaction-select-point';
6
9
  import { textEntryInteractionDescriptor } from '@qti-editor/interaction-text-entry';
7
10
  const registeredDescriptors = [
11
+ associateInteractionDescriptor,
8
12
  choiceInteractionDescriptor,
9
13
  extendedTextInteractionDescriptor,
14
+ hottextInteractionDescriptor,
10
15
  inlineChoiceInteractionDescriptor,
11
16
  matchInteractionDescriptor,
17
+ orderInteractionDescriptor,
12
18
  selectPointInteractionDescriptor,
13
19
  textEntryInteractionDescriptor,
14
20
  ];
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@qti-editor/core",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "QTI semantics, composer registry, and XML export orchestration for QTI Editor",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/Citolab/qti-editor",
8
+ "directory": "packages/qti/core"
9
+ },
5
10
  "type": "module",
6
11
  "main": "./dist/index.js",
7
12
  "exports": {
@@ -19,14 +24,17 @@
19
24
  }
20
25
  },
21
26
  "dependencies": {
22
- "@qti-editor/interfaces": "0.1.0",
23
- "@qti-editor/interaction-choice": "0.1.0",
24
- "@qti-editor/interaction-extended-text": "0.1.0",
25
- "@qti-editor/interaction-inline-choice": "0.1.0",
26
- "@qti-editor/interaction-match": "0.1.0",
27
- "@qti-editor/interaction-select-point": "0.1.0",
28
- "@qti-editor/interaction-text-entry": "0.1.0",
29
- "@qti-editor/interaction-shared": "0.1.0"
27
+ "@qti-editor/interfaces": "0.3.0",
28
+ "@qti-editor/interaction-associate": "0.2.0",
29
+ "@qti-editor/interaction-extended-text": "0.2.1",
30
+ "@qti-editor/interaction-choice": "0.2.1",
31
+ "@qti-editor/interaction-hottext": "0.2.0",
32
+ "@qti-editor/interaction-inline-choice": "0.2.1",
33
+ "@qti-editor/interaction-match": "0.3.0",
34
+ "@qti-editor/interaction-select-point": "0.2.1",
35
+ "@qti-editor/interaction-order": "0.3.0",
36
+ "@qti-editor/interaction-shared": "0.2.1",
37
+ "@qti-editor/interaction-text-entry": "0.2.1"
30
38
  },
31
39
  "devDependencies": {
32
40
  "@types/node": "^20.0.0",