Skip to content

Commit 6e774ce

Browse files
authored
Merge pull request #15 from pie-framework/develop
assset loading fixes
2 parents c705d9d + 084984d commit 6e774ce

14 files changed

Lines changed: 102 additions & 2 deletions

File tree

packages/qti2-assessment-player/src/components/AssessmentShell.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@
396396
onResponseChange={handleResponseChange}
397397
{typeset}
398398
{i18n}
399+
security={config.security}
399400
/>
400401
{/if}
401402
</SplitPaneResizer>
@@ -414,6 +415,7 @@
414415
onResponseChange={handleResponseChange}
415416
{typeset}
416417
{i18n}
418+
security={config.security}
417419
/>
418420
{/if}
419421
{/if}

packages/qti2-assessment-player/src/components/ItemRenderer.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { registerDefaultComponents } from '@pie-qti/qti2-default-components';
44
// @ts-expect-error - Svelte-check can't resolve workspace subpath exports, but runtime works correctly
55
import { ItemBody } from '@pie-qti/qti2-item-player/components';
6-
import { Player, type QTIRole } from '@pie-qti/qti2-item-player';
6+
import { Player, type PlayerSecurityConfig, type QTIRole } from '@pie-qti/qti2-item-player';
77
import type { I18nProvider } from '@pie-qti/qti2-i18n';
88
import { onMount } from 'svelte';
99
import type { QuestionRef } from '../types/index.js';
@@ -40,6 +40,8 @@
4040
typeset?: (root: HTMLElement) => void | Promise<void>;
4141
/** I18n provider for translations */
4242
i18n?: I18nProvider;
43+
/** Security configuration for URL policy and content restrictions */
44+
security?: PlayerSecurityConfig;
4345
}
4446
4547
const {
@@ -50,6 +52,7 @@
5052
onResponseChange,
5153
typeset,
5254
i18n,
55+
security,
5356
}: Props = $props();
5457
5558
// Derive player from questionRef
@@ -62,6 +65,7 @@
6265
const newPlayer = new Player({
6366
itemXml: questionRef.itemXml,
6467
role,
68+
security,
6569
});
6670
6771
// Ensure the item player can actually render interactions by registering the

packages/qti2-assessment-player/src/core/AssessmentPlayer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ export interface BackendAssessmentPlayerConfig {
4646
* If provided, this will be shared across all item players in the assessment.
4747
*/
4848
i18nProvider?: any; // Will be I18nProvider from @pie-qti/qti2-i18n
49+
/**
50+
* Security configuration for URL policy and content restrictions.
51+
* If provided, this will be passed to all item players in the assessment.
52+
*/
53+
security?: any; // Will be PlayerSecurityConfig from @pie-qti/qti2-item-player
4954
// UI options
5055
showSections?: boolean;
5156
allowSectionNavigation?: boolean;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Player Configuration Utilities
3+
*
4+
* Provides common configuration objects for the QTI Player that handle
5+
* deployment-specific concerns like asset URL resolution.
6+
*/
7+
8+
import { base } from '$app/paths';
9+
import type { PlayerConfig } from '@pie-qti/qti2-item-player';
10+
11+
/**
12+
* Get the asset base URL for the current deployment.
13+
* This handles the SvelteKit base path in production (e.g., /pie-qti/examples)
14+
* while working correctly in development (empty base).
15+
*
16+
* @returns The full base URL for resolving relative asset paths in QTI XML
17+
*/
18+
export function getAssetBaseUrl(): string {
19+
if (typeof window === 'undefined') return '';
20+
return `${window.location.origin}${base}/`;
21+
}
22+
23+
/**
24+
* Get security configuration for the current deployment.
25+
*
26+
* Returns a PlayerSecurityConfig object that configures asset URL resolution
27+
* for the current deployment path (e.g., GitHub Pages subpath routing).
28+
*
29+
* Note: Despite the name, this primarily configures URL resolution via
30+
* `urlPolicy.assetBaseUrl` rather than security filtering.
31+
*
32+
* @returns PlayerSecurityConfig with urlPolicy.assetBaseUrl configured
33+
*/
34+
export function getSecurityConfig(): NonNullable<PlayerConfig['security']> {
35+
return {
36+
urlPolicy: {
37+
assetBaseUrl: getAssetBaseUrl(),
38+
},
39+
};
40+
}

packages/qti2-example/src/routes/assessment-demo/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import { typesetAction } from '@pie-qti/qti2-default-components/shared';
66
import { typesetMathInElement } from '@pie-qti/qti2-typeset-katex';
77
import { SAMPLE_ASSESSMENTS, type SampleAssessment, toSecureAssessment } from '$lib/sample-assessments';
8+
import { getSecurityConfig } from '$lib/player-config';
89
import AssessmentEndScreen from './components/AssessmentEndScreen.svelte';
910
import FileUploader from './components/FileUploader.svelte';
1011
import SampleSelector from './components/SampleSelector.svelte';
@@ -158,6 +159,7 @@
158159
showSections: true,
159160
allowSectionNavigation: true,
160161
showProgress: true,
162+
security: getSecurityConfig(),
161163
}}
162164
onSubmit={(r) => (results = r)}
163165
typeset={(el) => typesetMathInElement(el)}

packages/qti2-example/src/routes/fixtures/assessment-player/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import AssessmentShell from '@pie-qti/qti2-assessment-player/components/Assessme
66
import { typesetAction } from '@pie-qti/qti2-default-components/shared';
77
import { typesetMathInElement } from '@pie-qti/qti2-typeset-katex';
88
import { SAMPLE_ASSESSMENTS, type SampleAssessment, toSecureAssessment } from '$lib/sample-assessments';
9+
import { getSecurityConfig } from '$lib/player-config';
910
1011
let selectedSampleId = $state('reading-comp-1');
1112
let selectedAssessment = $state<SampleAssessment | null>(null);
@@ -55,6 +56,7 @@ import AssessmentShell from '@pie-qti/qti2-assessment-player/components/Assessme
5556
showSections: true,
5657
allowSectionNavigation: true,
5758
showProgress: true,
59+
security: getSecurityConfig(),
5860
onComplete: () => {
5961
// No-op for test fixtures
6062
},

packages/qti2-example/src/routes/fixtures/item-player/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import { typesetMathInElement } from '@pie-qti/qti2-typeset-katex';
1111
import { untrack } from 'svelte';
1212
import { SAMPLE_ITEMS } from '$lib/sample-items';
13+
import { getSecurityConfig } from '$lib/player-config';
1314
1415
let selectedSampleId = $state('simple-choice');
1516
let xmlContent = $state('');
@@ -31,6 +32,7 @@
3132
const newPlayer = new Player({
3233
itemXml: xml,
3334
role: selectedRole,
35+
security: getSecurityConfig(),
3436
});
3537
3638
player = newPlayer;

packages/qti2-example/src/routes/iframe-demo/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { onDestroy, onMount, getContext } from 'svelte';
44
import { base } from '$app/paths';
55
import { SAMPLE_ITEMS } from '$lib/sample-items';
6+
import { getSecurityConfig } from '$lib/player-config';
67
import type { SvelteI18nProvider } from '@pie-qti/qti2-i18n';
78
89
const i18nContext = getContext<{ value: SvelteI18nProvider | null }>('i18n');
@@ -54,6 +55,7 @@
5455
await host.init({
5556
itemXml: item.xml,
5657
role: 'candidate',
58+
security: getSecurityConfig(),
5759
});
5860
}
5961

packages/qti2-example/src/routes/item-demo/[sample]/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import type { SvelteI18nProvider } from '@pie-qti/qti2-i18n';
1313
import { SAMPLE_ITEMS } from '$lib/sample-items';
1414
import { getItemXmlForLocale, hasMultilingualVariants } from '$lib/locale-aware-items';
15+
import { getSecurityConfig } from '$lib/player-config';
1516
import ConfigurationPanel from './components/ConfigurationPanel.svelte';
1617
import QuestionPanel from './components/QuestionPanel.svelte';
1718
import ResizableDivider from './components/ResizableDivider.svelte';
@@ -73,6 +74,7 @@
7374
const newPlayer = new Player({
7475
itemXml: xml,
7576
role: selectedRole,
77+
security: getSecurityConfig(),
7678
});
7779
7880
// Register default components with the player's registry

packages/qti2-example/src/routes/likert-demo/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import { typesetMathInElement } from '@pie-qti/qti2-typeset-katex';
99
import { browser } from '$app/environment';
1010
import { ALL_LIKERT_ITEMS } from '$lib/sample-likert-items';
11+
import { getSecurityConfig } from '$lib/player-config';
1112
1213
let selectedItemIndex = $state(0);
1314
let currentItem = $derived(ALL_LIKERT_ITEMS[selectedItemIndex]);
@@ -18,6 +19,7 @@
1819
const p = new Player({
1920
itemXml: currentItem.xml,
2021
plugins: [likertScalePlugin],
22+
security: getSecurityConfig(),
2123
});
2224
2325
// Register default components with the player's registry so ItemBody can render interactions.

0 commit comments

Comments
 (0)