coursecode 0.1.6 → 0.1.7
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/bin/cli.js +1 -0
- package/framework/css/02-layout.css +8 -1
- package/framework/css/components/audio-player.css +4 -3
- package/framework/css/components/buttons.css +7 -6
- package/framework/css/components/footer.css +5 -2
- package/framework/css/components/hero.css +31 -4
- package/framework/css/framework.css +2 -1
- package/framework/css/layouts/article.css +1 -1
- package/framework/css/responsive-structure.css +210 -0
- package/framework/css/responsive.css +15 -170
- package/framework/docs/COURSE_AUTHORING_GUIDE.md +8 -15
- package/framework/docs/DATA_MODEL.md +1 -1
- package/framework/docs/FRAMEWORK_GUIDE.md +31 -10
- package/framework/docs/USER_GUIDE.md +3 -1
- package/framework/js/utilities/icons.js +4 -2
- package/lib/cloud.js +4 -0
- package/lib/headless-browser.js +1 -1
- package/lib/mcp-prompts.js +22 -22
- package/lib/mcp-server.js +1 -1
- package/lib/preview-routes-api.js +59 -1
- package/lib/stub-player/outline-mode.js +187 -13
- package/lib/stub-player/styles/_header-bar.css +18 -0
- package/lib/stub-player/styles/_outline-mode.css +257 -20
- package/package.json +10 -1
- package/template/course/slides/example-course-structure.js +1 -1
- package/template/course/slides/example-finishing.js +4 -4
- package/template/course/slides/example-preview-tour.js +1 -1
- package/template/course/slides/example-ui-showcase.js +1 -1
- package/template/course/slides/example-welcome.js +4 -4
- package/template/course/slides/example-workflow.js +1 -1
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
**Related Docs:**
|
|
6
6
|
- `COURSE_AUTHORING_GUIDE.md` - For course authors (not framework devs)
|
|
7
|
-
|
|
8
7
|
- `DATA_MODEL.md` - Complete learner data schemas and storage architecture
|
|
9
8
|
|
|
10
9
|
---
|
|
@@ -143,7 +142,7 @@ const stampedHtml = stampFormat(indexHtml, 'scorm2004');
|
|
|
143
142
|
const { filename, content } = generateManifest('scorm2004', config, files, options);
|
|
144
143
|
```
|
|
145
144
|
|
|
146
|
-
Both are pure Node utilities
|
|
145
|
+
Both are pure Node utilities.
|
|
147
146
|
|
|
148
147
|
#### Key Files
|
|
149
148
|
|
|
@@ -574,6 +573,7 @@ This codebase serves two roles:
|
|
|
574
573
|
|------|---------|---------|
|
|
575
574
|
| `vite.framework-dev.config.js` | Framework developers (this repo) | Builds from `template/course/`, references `lib/` directly |
|
|
576
575
|
| `template/vite.config.js` | Course authors (their project) | Builds from `course/`, imports from `coursecode` package |
|
|
576
|
+
|
|
577
577
|
### Preview Architecture
|
|
578
578
|
|
|
579
579
|
Preview is **not** part of the build output. It is platform infrastructure, served separately from `dist/`:
|
|
@@ -599,7 +599,7 @@ User: coursecode build → uploads dist/
|
|
|
599
599
|
Cloud Preview Cloud ZIP (SCORM 2004) Cloud ZIP (SCORM 1.2)
|
|
600
600
|
┌────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
601
601
|
│ Cloud's own │ │ Copy dist/ │ │ Copy dist/ │
|
|
602
|
-
│ stub player │ │
|
|
602
|
+
│ stub player │ │ stampFormat │ │ stampFormat │
|
|
603
603
|
│ iframes the │ │ generateManifest │ │ generateManifest │
|
|
604
604
|
│ uploaded │ │ → ZIP │ │ → ZIP │
|
|
605
605
|
│ dist/ │ └──────────────────┘ └──────────────────┘
|
|
@@ -609,6 +609,7 @@ User: coursecode build → uploads dist/
|
|
|
609
609
|
**Cloud dependencies:** The cloud app imports `stampFormat` and `generateManifest` directly from the `coursecode` npm package. These are pure functions — no filesystem, no Vite, no dynamic imports of user code, no `eval`. All inputs (title, version, file list) come from scanning the uploaded `dist/` or the cloud's own database.
|
|
610
610
|
|
|
611
611
|
**Security boundary:** The cloud never executes `course-config.js` or any user-authored JavaScript. The meta tag and manifest are the only format-specific artifacts, and both are generated from trusted framework source code.
|
|
612
|
+
|
|
612
613
|
---
|
|
613
614
|
|
|
614
615
|
## Course Validation
|
|
@@ -819,9 +820,6 @@ environment: {
|
|
|
819
820
|
**Interactions**: `{id}-check-answer`, `{id}-reset`, `{id}-controls`, `{id}-feedback`, `{id}-choice-{index}`, `{id}-blank-{index}`, `{id}-input`, `{id}-drag-item-{itemId}`, `{id}-drop-zone-{zoneId}`
|
|
820
821
|
|
|
821
822
|
**Assessments**: `assessment-start`, `assessment-nav-{prev|next}`, `assessment-submit`, `assessment-retake`, `assessment-review-question-{index}`
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
823
|
---
|
|
826
824
|
|
|
827
825
|
## MCP Integration (AI Agent Control)
|
|
@@ -833,7 +831,7 @@ The MCP server runs a **persistent headless Chrome** internally via `puppeteer-c
|
|
|
833
831
|
The MCP does **not** start or manage the preview server. The preview must be running before using runtime tools:
|
|
834
832
|
|
|
835
833
|
- **Human**: run `coursecode preview` (or `npm run preview`) in a terminal
|
|
836
|
-
- **AI agent**: use
|
|
834
|
+
- **AI agent**: use your terminal/command execution tool to run `npm run preview`
|
|
837
835
|
|
|
838
836
|
If the preview is not running, runtime tools fail fast with a clear error message.
|
|
839
837
|
|
|
@@ -856,12 +854,12 @@ If the preview is not running, runtime tools fail fast with a clear error messag
|
|
|
856
854
|
|
|
857
855
|
| Tool | Purpose | Returns |
|
|
858
856
|
|------|---------|--------|
|
|
859
|
-
| `coursecode_state` | Full course snapshot | `{slide,
|
|
860
|
-
| `coursecode_navigate` | Go to slide by ID | `{
|
|
857
|
+
| `coursecode_state` | Full course snapshot | `{slide, toc, interactions, engagement, lmsState, apiLog, errors, frameworkLogs, consoleLogs}` |
|
|
858
|
+
| `coursecode_navigate` | Go to slide by ID | `{slide, interactions, engagement, accessibility}` |
|
|
861
859
|
| `coursecode_interact` | Set response + evaluate | `{interactionId, response}` → `{correct, score, feedback}` |
|
|
862
860
|
| `coursecode_screenshot` | Visual capture (JPEG) | Optional `slideId` to navigate first, `fullPage` for scroll capture |
|
|
863
861
|
| `coursecode_viewport` | Set viewport size | Breakpoint name or `{width, height}` → persists until changed |
|
|
864
|
-
| `coursecode_reset` | Clear learner state |
|
|
862
|
+
| `coursecode_reset` | Clear learner state | No input; clears local state and reloads |
|
|
865
863
|
|
|
866
864
|
### Screenshot Quality Modes
|
|
867
865
|
|
|
@@ -881,6 +879,8 @@ Use `coursecode_viewport` for responsive design testing. Two input modes:
|
|
|
881
879
|
|
|
882
880
|
The viewport **persists** until explicitly changed again. Default is 1280×720.
|
|
883
881
|
|
|
882
|
+
> **AI tip:** For realistic mobile QA, use explicit phone dimensions (for example `{width: 375, height: 812}`) in addition to named breakpoints.
|
|
883
|
+
|
|
884
884
|
### Navigation API
|
|
885
885
|
|
|
886
886
|
Use MCP tools for all course interaction — never use external browser tools:
|
|
@@ -902,6 +902,18 @@ MCP Server (IDE) ──puppeteer──▶ Headless Chrome ──HTTP──▶ Pr
|
|
|
902
902
|
- **Preview not running?** → Tools return clear error: "Start preview server first"
|
|
903
903
|
- **Chrome not found?** → Install Google Chrome or set `CHROME_PATH` env var
|
|
904
904
|
|
|
905
|
+
### Pre-Release Responsive Checks (Framework)
|
|
906
|
+
|
|
907
|
+
Before merging responsive/layout changes:
|
|
908
|
+
|
|
909
|
+
```bash
|
|
910
|
+
npm run prerelease:check
|
|
911
|
+
npm run smoke:responsive -- --profile=expanded
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
- `lint:responsive` guards `responsive.css` ownership (no shell/chrome selectors)
|
|
915
|
+
- `lint:responsive:structure` enforces layout exclusions/scoping for high-risk shell selectors
|
|
916
|
+
|
|
905
917
|
---
|
|
906
918
|
|
|
907
919
|
## Audio Manager (Internals)
|
|
@@ -983,6 +995,8 @@ eventBus.on('assessment:retake', ({ id, attemptNumber }) => { });
|
|
|
983
995
|
| `components/*.css` | Individual UI component styles (cards, hero, tabs, steps, timeline, etc.) |
|
|
984
996
|
| `interactions/*.css` | Interaction-specific styles |
|
|
985
997
|
| `utilities/*.css` | Utility classes (spacing, display, flex) |
|
|
998
|
+
| `responsive.css` | Shared content/component responsive rules (non-shell) |
|
|
999
|
+
| `responsive-structure.css` | App shell/header/footer/nav/audio responsive rules |
|
|
986
1000
|
| `framework.css` | Main import file, orchestrates all modules |
|
|
987
1001
|
|
|
988
1002
|
### Layout System
|
|
@@ -1002,11 +1016,18 @@ CSS-only layouts controlled via `data-layout` attribute on `<html>`. Files in `f
|
|
|
1002
1016
|
|
|
1003
1017
|
Set via `layout` in `course-config.js`. The `main.js` automatically applies the attribute from config.
|
|
1004
1018
|
|
|
1019
|
+
### Responsive CSS Ownership
|
|
1020
|
+
|
|
1021
|
+
- Put shared content/component responsive rules in `framework/css/responsive.css`.
|
|
1022
|
+
- Put shell/chrome responsive rules (`#app`, header/brand, footer/nav/audio) in `framework/css/responsive-structure.css`.
|
|
1023
|
+
- For generic shell selectors, exclude layout-owned behavior (especially `article` and `focused`) unless explicitly layout-scoped.
|
|
1024
|
+
|
|
1005
1025
|
### Auto-Wrapping
|
|
1006
1026
|
|
|
1007
1027
|
Slides are automatically wrapped with content width class (default: `.content-medium`).
|
|
1008
1028
|
|
|
1009
1029
|
Override per-slide with `data-content-width` attribute or globally via `slideDefaults.contentWidth` in `course-config.js`.
|
|
1030
|
+
|
|
1010
1031
|
### Document Gallery
|
|
1011
1032
|
|
|
1012
1033
|
`framework/js/navigation/document-gallery.js` — Collapsible sidebar gallery for reference documents.
|
|
@@ -286,8 +286,9 @@ Once connected, your AI assistant gains these capabilities:
|
|
|
286
286
|
|
|
287
287
|
| Tool | What It Does |
|
|
288
288
|
|------|--------------|
|
|
289
|
-
| `coursecode_state` | Get the full course state — current slide,
|
|
289
|
+
| `coursecode_state` | Get the full course state — current slide, TOC, interactions, engagement, LMS state, logs, and errors |
|
|
290
290
|
| `coursecode_navigate` | Jump to any slide by ID |
|
|
291
|
+
| `coursecode_viewport` | Set the preview viewport (named breakpoint or explicit width/height) for responsive testing |
|
|
291
292
|
| `coursecode_screenshot` | Take a screenshot of any slide |
|
|
292
293
|
| `coursecode_interact` | Answer an interaction and check if it's correct |
|
|
293
294
|
| `coursecode_reset` | Clear progress and restart the course |
|
|
@@ -296,6 +297,7 @@ Once connected, your AI assistant gains these capabilities:
|
|
|
296
297
|
| `coursecode_interaction_catalog` | Browse available interaction types (multiple choice, drag-drop, etc.) |
|
|
297
298
|
| `coursecode_css_catalog` | Browse available CSS classes by category |
|
|
298
299
|
| `coursecode_icon_catalog` | Browse available icons by name/category |
|
|
300
|
+
| `coursecode_export_content` | Export course content/interactions as Markdown or JSON for review |
|
|
299
301
|
| `coursecode_workflow_status` | Get guidance on what to do next based on your project's current state |
|
|
300
302
|
| `coursecode_build` | Build the course for LMS deployment |
|
|
301
303
|
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// Map pixel values to size class names
|
|
9
|
+
import { logger } from './logger.js';
|
|
10
|
+
|
|
9
11
|
const SIZE_MAP = {
|
|
10
12
|
12: 'xs',
|
|
11
13
|
16: 'sm',
|
|
@@ -99,14 +101,14 @@ class IconManager {
|
|
|
99
101
|
|
|
100
102
|
// Check if it's an emoji - warn but handle gracefully
|
|
101
103
|
if (this._isEmoji(name)) {
|
|
102
|
-
|
|
104
|
+
logger.warn(`[IconManager] Emoji "${name}" passed to getIcon(). Use getEmoji() instead.`);
|
|
103
105
|
const size = typeof options === 'object' ? options.size : undefined;
|
|
104
106
|
const extraClass = typeof options === 'object' ? options.class : (typeof options === 'string' ? options : '');
|
|
105
107
|
return this.getEmoji(name, size, extraClass);
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
// Warn about unknown icons but don't block course loading - return a placeholder
|
|
109
|
-
|
|
111
|
+
logger.warn(`[IconManager] Unknown icon: "${name}". Add it to DEFAULT_ICONS or course/icons.js.`);
|
|
110
112
|
const classes = ['icon', `icon-${name}`, 'icon-missing', sizeClass, className].filter(Boolean).join(' ');
|
|
111
113
|
// Return a simple question mark circle as placeholder
|
|
112
114
|
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${color}" stroke-width="${strokeWidth}" stroke-linecap="round" stroke-linejoin="round" class="${classes}" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>`;
|
package/lib/cloud.js
CHANGED
|
@@ -583,6 +583,10 @@ export async function deploy(options = {}) {
|
|
|
583
583
|
formData.append('file', new Blob([zipBuffer], { type: 'application/zip' }), 'deploy.zip');
|
|
584
584
|
formData.append('orgId', orgId);
|
|
585
585
|
|
|
586
|
+
if (options.message) {
|
|
587
|
+
formData.append('message', options.message);
|
|
588
|
+
}
|
|
589
|
+
|
|
586
590
|
if (options.preview && options.password) {
|
|
587
591
|
const pw = await prompt(' Preview password: ');
|
|
588
592
|
formData.append('password', pw);
|
package/lib/headless-browser.js
CHANGED
|
@@ -169,7 +169,7 @@ class HeadlessBrowser {
|
|
|
169
169
|
// CourseCodeAutomation exists early but .ready is set after full boot
|
|
170
170
|
await this.courseFrame.waitForFunction(
|
|
171
171
|
() => window.CourseCodeAutomation?.ready === true,
|
|
172
|
-
{ timeout:
|
|
172
|
+
{ timeout: 30000 }
|
|
173
173
|
);
|
|
174
174
|
}
|
|
175
175
|
|
package/lib/mcp-prompts.js
CHANGED
|
@@ -497,7 +497,7 @@ This creates a course/ directory with the starter template.
|
|
|
497
497
|
After creating the project, the author should add reference files (PDF, DOCX, PPTX) to course/references/ for conversion.
|
|
498
498
|
|
|
499
499
|
## Next Stage
|
|
500
|
-
Once the project exists and reference files are added, Stage 1 (Source Ingestion) begins. Run
|
|
500
|
+
Once the project exists and reference files are added, Stage 1 (Source Ingestion) begins. Run coursecode_workflow_status to refresh.`;
|
|
501
501
|
|
|
502
502
|
case 1: { // Source Ingestion
|
|
503
503
|
const rawFiles = listSafe(refsDir, ['.pdf', '.docx', '.doc', '.pptx', '.ppt']);
|
|
@@ -525,7 +525,7 @@ Convert to markdown: coursecode convert
|
|
|
525
525
|
Converted files appear in course/references/converted/
|
|
526
526
|
|
|
527
527
|
## Next Stage
|
|
528
|
-
Once references are converted to markdown, Stage 2 (Outline Creation) begins. Call
|
|
528
|
+
Once references are converted to markdown, Stage 2 (Outline Creation) begins. Call coursecode_workflow_status after conversion to get updated guidance.`;
|
|
529
529
|
}
|
|
530
530
|
|
|
531
531
|
case 2: { // Outline Creation
|
|
@@ -552,7 +552,7 @@ The outline is a DESIGN document. Define content, interactions, structure, engag
|
|
|
552
552
|
Pause for author review after creating the outline.
|
|
553
553
|
|
|
554
554
|
## Next Stage
|
|
555
|
-
Once the outline is approved, Stage 3 (Course Building) begins. Call
|
|
555
|
+
Once the outline is approved, Stage 3 (Course Building) begins. Call coursecode_workflow_status to get updated guidance.`;
|
|
556
556
|
}
|
|
557
557
|
|
|
558
558
|
case 3: { // Course Building
|
|
@@ -584,8 +584,8 @@ ${refList}
|
|
|
584
584
|
Only import local assets (images, SVGs): import myImage from '../assets/images/photo.png';
|
|
585
585
|
Interactions: const { createMultipleChoiceQuestion } = CourseCode; (destructure from global, NOT import)
|
|
586
586
|
Components: use data-component="tabs" in HTML (declarative, no JS needed)
|
|
587
|
-
- Use
|
|
588
|
-
- Use
|
|
587
|
+
- Use coursecode_css_catalog to discover available CSS classes by category. Lint catches invalid classes with fix suggestions.
|
|
588
|
+
- Use coursecode_component_catalog and coursecode_interaction_catalog to discover available components and interactions.
|
|
589
589
|
- Run lint after each batch of file changes. Fix all errors before proceeding.
|
|
590
590
|
- Never modify files in framework/ — all work goes in course/ only.
|
|
591
591
|
- No em-dashes in sentence structure. Use alternative phrasing.
|
|
@@ -593,7 +593,7 @@ ${refList}
|
|
|
593
593
|
Pause for author review after the initial slide build.
|
|
594
594
|
|
|
595
595
|
## Next Stage
|
|
596
|
-
Once slides and config are built, Stage 4 (Preview & Polish) begins. Start the preview and call
|
|
596
|
+
Once slides and config are built, Stage 4 (Preview & Polish) begins. Start the preview and call coursecode_workflow_status for updated guidance.`;
|
|
597
597
|
}
|
|
598
598
|
|
|
599
599
|
case 4: { // Preview & Polish
|
|
@@ -608,7 +608,7 @@ Once slides and config are built, Stage 4 (Preview & Polish) begins. Start the p
|
|
|
608
608
|
|
|
609
609
|
This course was imported from a PowerPoint presentation. Each slide is currently a static image. Your job is to enhance it into an interactive course.
|
|
610
610
|
|
|
611
|
-
1. Ensure the preview server is running (\`coursecode preview\` in a terminal, or AI uses
|
|
611
|
+
1. Ensure the preview server is running (\`coursecode preview\` in a terminal, or AI uses a terminal/command execution tool)
|
|
612
612
|
2. Review slides: screenshot each to understand the content
|
|
613
613
|
3. Enhancement priorities:
|
|
614
614
|
- **Replace image slides** with interactive HTML — use the extracted text from references/converted/ as source content
|
|
@@ -618,7 +618,7 @@ This course was imported from a PowerPoint presentation. Each slide is currently
|
|
|
618
618
|
- **Customize theme** — update colors in course/theme.css
|
|
619
619
|
${refList ? `\n4. REFERENCE MATERIALS (extracted text from presentation):\n${refList}\n` : ''}
|
|
620
620
|
5. RULES:
|
|
621
|
-
- Use
|
|
621
|
+
- Use coursecode_css_catalog, coursecode_component_catalog, and coursecode_interaction_catalog to discover available options
|
|
622
622
|
- Run lint after changes. Fix all errors before proceeding.
|
|
623
623
|
- Efficient loop: edit files → lint → fix errors → screenshot to verify
|
|
624
624
|
|
|
@@ -630,15 +630,15 @@ Once polished and lint passes, Stage 5 (Export Ready) begins.`;
|
|
|
630
630
|
|
|
631
631
|
Visually verify and polish the course using the preview server.
|
|
632
632
|
|
|
633
|
-
1. Ensure the preview server is running (\`coursecode preview\` in a terminal, or AI uses
|
|
633
|
+
1. Ensure the preview server is running (\`coursecode preview\` in a terminal, or AI uses a terminal/command execution tool)
|
|
634
634
|
2. Do NOT open a browser yourself — the MCP has its own headless Chrome
|
|
635
635
|
3. Workflow (all tools execute instantly via internal headless browser):
|
|
636
|
-
-
|
|
637
|
-
-
|
|
638
|
-
-
|
|
639
|
-
-
|
|
640
|
-
-
|
|
641
|
-
-
|
|
636
|
+
- coursecode_state — get course structure, current slide, interactions, engagement
|
|
637
|
+
- coursecode_navigate — go to any slide by ID (get IDs from coursecode_state)
|
|
638
|
+
- coursecode_screenshot — capture visual state (accepts slideId to navigate+capture in one call)
|
|
639
|
+
- coursecode_interact — test interactions with responses
|
|
640
|
+
- coursecode_export_content — extract all text content to review or compare against outline
|
|
641
|
+
- coursecode_lint — validate after file changes
|
|
642
642
|
|
|
643
643
|
4. Efficient iteration loop:
|
|
644
644
|
Edit files → lint → fix errors → screenshot to verify visual result
|
|
@@ -647,7 +647,7 @@ ${refList ? `\n5. REFERENCE MATERIALS (for verifying content accuracy):\n${refLi
|
|
|
647
647
|
Run lint and ensure zero errors before moving to export.
|
|
648
648
|
|
|
649
649
|
## Next Stage
|
|
650
|
-
Once the course is polished and lint passes, Stage 5 (Export Ready) begins. Call
|
|
650
|
+
Once the course is polished and lint passes, Stage 5 (Export Ready) begins. Call coursecode_workflow_status for export guidance.`;
|
|
651
651
|
}
|
|
652
652
|
|
|
653
653
|
case 5: // Export Ready
|
|
@@ -660,10 +660,10 @@ Export the finished course for LMS deployment.
|
|
|
660
660
|
- cmi5 (default) for modern LMS
|
|
661
661
|
- scorm1.2 for legacy systems
|
|
662
662
|
|
|
663
|
-
The build produces a
|
|
663
|
+
The build produces a dist/ output for deployment. If you need a packaged ZIP for LMS upload, use the CLI packaging commands outside MCP (for example \`coursecode package\`).`;
|
|
664
664
|
|
|
665
665
|
default:
|
|
666
|
-
return 'Call
|
|
666
|
+
return 'Call coursecode_workflow_status to determine the current authoring stage.';
|
|
667
667
|
}
|
|
668
668
|
}
|
|
669
669
|
|
|
@@ -693,7 +693,7 @@ All runtime tools (state, navigate, interact, screenshot, viewport, reset) execu
|
|
|
693
693
|
|
|
694
694
|
### Preview Server Ownership
|
|
695
695
|
- The MCP does NOT start or manage the preview server
|
|
696
|
-
- The preview must be started externally: run \`coursecode preview\` in a terminal (human) or via
|
|
696
|
+
- The preview must be started externally: run \`coursecode preview\` in a terminal (human) or via a terminal/command execution tool (AI agent)
|
|
697
697
|
- If preview is not running, runtime tools will fail with a clear error message
|
|
698
698
|
- The headless browser auto-reconnects when Vite rebuilds (file changes)
|
|
699
699
|
|
|
@@ -713,8 +713,8 @@ All runtime tools (state, navigate, interact, screenshot, viewport, reset) execu
|
|
|
713
713
|
- Efficient loop: edit files → lint → fix errors → screenshot specific slides to verify
|
|
714
714
|
|
|
715
715
|
### Customization (all in course/, never in framework/)
|
|
716
|
-
- **CSS overrides**: Edit \`course/theme.css\` — override palette tokens to rebrand (all colors cascade via color-mix). Use framework utility classes first (
|
|
717
|
-
- **Custom components**: Add \`.js\` files to \`course/components/\` — auto-discovered at build time. Use
|
|
716
|
+
- **CSS overrides**: Edit \`course/theme.css\` — override palette tokens to rebrand (all colors cascade via color-mix). Use framework utility classes first (\`coursecode_css_catalog\`), \`theme.css\` only for brand-specific overrides.
|
|
717
|
+
- **Custom components**: Add \`.js\` files to \`course/components/\` — auto-discovered at build time. Use \`coursecode_component_catalog\` for built-in options first.
|
|
718
718
|
- **Custom interactions**: Add \`.js\` files to \`course/interactions/\` — auto-discovered. See \`course/interactions/PLUGIN_GUIDE.md\` for the template.
|
|
719
719
|
- **Custom icons**: Add SVG definitions to \`course/icons.js\` — merged with built-in icons. Use icon_catalog to check existing icons first.`;
|
|
720
720
|
}
|
|
@@ -769,7 +769,7 @@ export async function buildInstructions(port = 4173) {
|
|
|
769
769
|
let automationWarning = '';
|
|
770
770
|
if (checklist.hasCourseConfig && !checklist.hasAutomationEnabled) {
|
|
771
771
|
automationWarning = `\n\n## ⚠️ Automation Disabled
|
|
772
|
-
MCP runtime tools (
|
|
772
|
+
MCP runtime tools (\`coursecode_state\`, \`coursecode_navigate\`, \`coursecode_interact\`, and \`coursecode_reset\`, plus screenshot/navigation features that rely on course API access) require \`environment.automation.enabled: true\` in course-config.js. Without it, the headless browser cannot access the course API and these tools will fail.
|
|
773
773
|
You MUST notify the author about this and let them decide whether to enable it. Do not silently modify the config.`;
|
|
774
774
|
}
|
|
775
775
|
|
package/lib/mcp-server.js
CHANGED
|
@@ -47,7 +47,7 @@ async function ensureHeadless(port) {
|
|
|
47
47
|
throw new Error(
|
|
48
48
|
'Preview server not running. Start it first:\n' +
|
|
49
49
|
' • Human: run `coursecode preview` in a terminal\n' +
|
|
50
|
-
' • AI agent: use
|
|
50
|
+
' • AI agent: use your terminal/command execution tool to run `coursecode preview`\n' +
|
|
51
51
|
'Then retry this tool call.'
|
|
52
52
|
);
|
|
53
53
|
}
|
|
@@ -11,7 +11,7 @@ import os from 'os';
|
|
|
11
11
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
12
12
|
import { generateContentHtml } from './stub-player/content-generator.js';
|
|
13
13
|
import { parseCourse } from './course-parser.js';
|
|
14
|
-
import { getComponentCatalog, getInteractionCatalog, getIconCatalog, getWorkflowStatus, getRefsStatus } from './authoring-api.js';
|
|
14
|
+
import { getComponentCatalog, getInteractionCatalog, getIconCatalog, getWorkflowStatus, getRefsStatus, buildCourse } from './authoring-api.js';
|
|
15
15
|
import { getAllIcons, getAllSchemas } from './schema-extractor.js';
|
|
16
16
|
|
|
17
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -612,6 +612,46 @@ export async function handleApiRoutes(ctx, req, res, url) {
|
|
|
612
612
|
return true;
|
|
613
613
|
}
|
|
614
614
|
|
|
615
|
+
// Preview a converted reference file (for dashboard Stage 2 links)
|
|
616
|
+
if (url === '/__stub-player/ref-preview') {
|
|
617
|
+
const params = new URL(req.url, 'http://localhost').searchParams;
|
|
618
|
+
const fileName = params.get('file');
|
|
619
|
+
if (!fileName || fileName.includes('..')) {
|
|
620
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
621
|
+
res.end('Invalid file parameter');
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
const filePath = path.join(paths.coursePath, 'references', 'converted', fileName);
|
|
625
|
+
if (!fs.existsSync(filePath)) {
|
|
626
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
627
|
+
res.end('File not found');
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
631
|
+
const html = ctx.simpleMarkdownToHtml(raw);
|
|
632
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' });
|
|
633
|
+
res.end(`<!DOCTYPE html>
|
|
634
|
+
<html lang="en">
|
|
635
|
+
<head>
|
|
636
|
+
<meta charset="UTF-8">
|
|
637
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
638
|
+
<title>${fileName} – Reference Preview</title>
|
|
639
|
+
<link rel="stylesheet" href="/__stub-player/styles.css">
|
|
640
|
+
<style>
|
|
641
|
+
body { padding: 40px; max-width: 900px; margin: 0 auto; font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; background: var(--color-primary-deep, #0b1628); color: var(--color-gray-200, #d1d5db); }
|
|
642
|
+
h1 { font-size: 18px; color: var(--color-white, #fff); border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 8px; margin-bottom: 24px; }
|
|
643
|
+
pre { background: rgba(0,0,0,0.3); padding: 12px; border-radius: 6px; overflow-x: auto; }
|
|
644
|
+
code { background: rgba(0,0,0,0.2); padding: 2px 6px; border-radius: 3px; font-size: 13px; }
|
|
645
|
+
</style>
|
|
646
|
+
</head>
|
|
647
|
+
<body>
|
|
648
|
+
<h1>${fileName}</h1>
|
|
649
|
+
${html}
|
|
650
|
+
</body>
|
|
651
|
+
</html>`);
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
|
|
615
655
|
// Stub player static files
|
|
616
656
|
if (url.startsWith('/__stub-player/')) {
|
|
617
657
|
const relativePath = url.slice('/__stub-player/'.length);
|
|
@@ -639,6 +679,24 @@ export async function handleApiRoutes(ctx, req, res, url) {
|
|
|
639
679
|
return true;
|
|
640
680
|
}
|
|
641
681
|
|
|
682
|
+
// Build course (triggered from dashboard)
|
|
683
|
+
if (url.startsWith('/__build') && req.method === 'POST') {
|
|
684
|
+
(async () => {
|
|
685
|
+
try {
|
|
686
|
+
const params = new URL(req.url, 'http://localhost').searchParams;
|
|
687
|
+
const format = params.get('format') || 'cmi5';
|
|
688
|
+
const result = await buildCourse({ format });
|
|
689
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
690
|
+
res.end(JSON.stringify(result));
|
|
691
|
+
} catch (err) {
|
|
692
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
693
|
+
res.end(JSON.stringify({ success: false, error: err.message }));
|
|
694
|
+
}
|
|
695
|
+
})();
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
|
|
642
700
|
// Reset (clear storage + redirect)
|
|
643
701
|
if (url === '/__reset') {
|
|
644
702
|
const resetHtml = `<!DOCTYPE html>
|