diff --git a/docs/contract-requests/import.md b/docs/contract-requests/import.md new file mode 100644 index 0000000..22513da --- /dev/null +++ b/docs/contract-requests/import.md @@ -0,0 +1,11 @@ +# Contract requests from WP-B (Importer) + +1. **`loadConfig` should add `/courses` to `courseDirs`.** The importer writes courses there. Worked around in `apps/server/src/main.ts` (the main guard pushes it) and in `packages/feature-import/dev/serve.ts`. `/api/import/info` reports `coursesDirServed` and the UI warns when it is false. +2. **`startServer` accepts feature factories** (`Feature | ((ctx) => Feature)`). A `Feature` needs `ServerContext`, but `ServerContext.jobs` is built from the features' job handlers. `importFeature(ctx)` only reads `ctx` while a job or request runs, so `main.ts` now builds `ctx` first, resolves the factories, then assigns `ctx.jobs`. The edit is about ten lines in `apps/server/src/main.ts`; Phase 2 should keep or replace it. +3. **Web entry for features.** `apps/web` must not pull a package's server code into the browser bundle. `@stc/feature-import` exposes a browser-safe entry as `./web` (package.json `exports`), but the root tsconfig `paths` (`@stc/*` to `packages/*/src`) cannot resolve `@stc/feature-import/web`, so `apps/web/src/features.ts` imports it by relative path. Suggest a tsconfig `paths` entry per feature, or a `src/web.ts` convention. +4. **Derived cache layout (no contract defines one).** Written by the importer, to be read by the learner and AI packages: + - `/cache/decks///p-001.jpg ...` (1280px wide JPEGs) and `text.json` (`[{ page, text }]`); + - `/cache/covers//frame-N.jpg` and `deck-.jpg` (cover candidates); + - `/cache/transcripts//.json` (`{ lessonId, text, segments: [{ start, end, text }] }`). + Suggest a `cacheDir`/`deckPageUrl` helper in core, and a media route for `cache/decks` so the deck viewer can load pages. +5. **Video lessons have no description field.** Packt clips come with a one-line description. It is dropped, because `transcript` is the only Markdown a video lesson can carry and the transcription job writes it. diff --git a/packages/feature-import/src/web/ImportPage.tsx b/packages/feature-import/src/web/ImportPage.tsx index e69ed81..44ebbdd 100644 --- a/packages/feature-import/src/web/ImportPage.tsx +++ b/packages/feature-import/src/web/ImportPage.tsx @@ -65,7 +65,7 @@ function JobRow({ job }: { job: Job }) { {warnings.length ? (
{warnings.length} warning{warnings.length > 1 ? "s" : ""}
    {warnings.map((w, i) =>
  • {w}
  • )}
) : null} - {job.status === "done" && job.kind !== "import.transcribe" && result?.courseId ? : null} + {job.status === "done" && (job.kind === "import.packt-video" || job.kind === "import.video-folder") && result?.courseId ? : null} ); } diff --git a/packages/feature-import/src/web/import.css b/packages/feature-import/src/web/import.css index 4fc8d78..6dfc535 100644 --- a/packages/feature-import/src/web/import.css +++ b/packages/feature-import/src/web/import.css @@ -26,6 +26,7 @@ .imp-status[data-s="running"] { color: var(--accent); } .imp-job-bar { display: flex; align-items: center; gap: 12px; } .imp-job-msg { font-size: 12.5px; color: var(--faint); } +.imp-job-bar .imp-job-msg { flex: none; width: 200px; text-align: right; } .imp-job details { font-size: 12.5px; color: var(--muted); } .imp-job summary { cursor: pointer; } .imp-job ul.warn { margin: 6px 0 0; padding-left: 18px; }