mirror of
https://github.com/THU-MAIC/OpenMAIC.git
synced 2026-10-18 05:40:25 +08:00
@openmaic/renderer@0.0.3
16
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bb8d4f30e7 |
feat(chat): add experimental Pi classroom runtime behind a flag (#914)
* feat(chat): add experimental Pi classroom runtime * fix(chat): harden Pi whiteboard action sequencing * style(chat): format session retirement flow * style(playback): format async scene transition * fix(chat): retire actions on session errors * fix(chat): close Pi runtime lifecycle gaps * fix(chat): avoid replaying historical whiteboard actions * fix(chat): prevent Pi structured-output residue from leaking and inflating turns Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(chat): expose whiteboard element/line ids and bound code-line context Surface each element's id (and code line ids) in the Pi whiteboard context so later child agents can target wb_delete / wb_edit_code, and cap the rendered code lines with a shared character budget so a code-heavy board cannot grow the prompt without bound. Return only the current turn's whiteboard ledger from the Pi director loop instead of carrying history forward across requests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(chat): bound Pi whiteboard code context Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(chat): prioritize newer persisted whiteboard code in budget Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(chat): enforce whiteboard boundaries across Pi sessions * feat(chat): refine soft-closing discussion controls * fix(chat): make whiteboard clearing model-directed --------- Co-authored-by: apple <apple@GigideMacBook-Air.local> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: LING-6150 <duan.lin@northeastern.edu> |
||
|
|
7fffec78f6 |
feat(choreography): shared orchestration spec in lib/choreography (#863) (#890)
* feat(choreography): shared orchestration spec in lib/choreography (#863) Introduce lib/choreography/ as the single source of truth for the orchestration semantics a faithful classroom-video exporter needs from playback, so the app runtime and the exporter interpret one spec instead of each re-implementing (and silently drifting from) the other. Kept in lib/ rather than a package: these semantics co-evolve with the playback engine, and the exporter will also live in the app, so both consumers share them via ordinary imports. Purity is machine-enforced by an eslint boundary on lib/choreography/** (blocks @/ host-app paths + react/react-dom/gsap/framer-motion/motion), so the exporter can interpret the spec in a pure Node environment. - timing.ts — timing constants + the deterministic no-audio speech estimate, moved verbatim from the engines. - cursor.ts — resolvePlaybackCursor + EMPTY_SCENE_DWELL, moved from lib/playback/engine-cursor.ts (typed on dsl SceneCore). - timeline.ts — new pure resolveActionTimeline: index-domain -> time-domain expansion (blocking cursor-advance vs fire-and-forget visual duration), keyed off the DSL fire-and-forget partition. - descriptors/— versioned, zod-schema-validated animation descriptors spotlight.v1 + laser.v1 (declarative: property/from/to/ duration/easing; no implementation), pinned to the current overlay components. Behavior-neutral engine refactor: lib/action/engine.ts and lib/playback/engine.ts import from lib/choreography and the local literals are deleted, so the timing dimension now has exactly one copy. The spotlight/laser overlay components still hardcode their animation values (they do not yet READ the descriptors) — tracked in #889. Closes #863 * fix(choreography): address cross-review findings on resolveActionTimeline + descriptors - P1: model implicit whiteboard auto-open — a wb_* mutation on a closed board now prepends a synthetic IMPLICIT_WB_OPEN (WB_OPEN_MS) beat, mirroring the engine's ensureWhiteboardOpen; open state carries across scenes and toggles on wb_open/wb_close (new `whiteboardOpen` option to seed it). - P2: scale real speech audio duration by playbackSpeed too (live path sets AudioPlayer.setPlaybackRate), keeping it in lockstep with the estimate path. - P2: express the spotlight mask relationship in the descriptor model — LayerSchema gains `role` ('content'|'mask') + `maskedBy` (subtract|intersect); spotlight.v1's cutout is now a mask layer the dim layer subtracts, so a non-React consumer reconstructs the cutout instead of a black rect. - P3: wb_clear on an empty board is 0ms (engine early-returns), not wbClearMs(0). Tests: tests/lib/choreography 45 pass (+6); engine regression 28 pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(choreography): address final-audit findings on effect lifetime + spotlight descriptor Second cross-review round (fresh-session codex final audit) surfaced three deeper mismatches with live playback: - P1: fire-and-forget effect lifetime is not a flat EFFECT_AUTO_CLEAR_MS. The engine's processNext clears effects at every scene boundary and on completion, and scheduleEffectClear uses one shared timer each new effect resets. Added clampFireAndForgetLifetimes: an effect's visual durationMs is now min(next scene boundary / completion, shared-timer deadline chained through later effects in the same scene). advancesCursorMs (0) is untouched. - P2: spotlight dimness default is 0.5 (executeSpotlight: dimOpacity ?? 0.5; DSL documents 0.5), not the component's unreachable ?? 0.7 fallback. Fixed the descriptor param + test. - P2: model the spotlight wrapper's enter/exit opacity fade (motion.div, no explicit duration → engine default). TrackSchema.durationMs is now optional to express "use the consumer's engine default"; dim layer carries the fade tracks. Tests: tests/lib/choreography 50 pass (+5, incl. boundary-cut / completion-cut / full-lifetime / shared-timer-extension / wrapper-fade); engine regression 28 pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(choreography): pin spotlight fade duration + laser dot geometry in descriptors Third cross-review round (codex) flagged two descriptor-completeness gaps that would make a non-Motion consumer (the exporter) diverge from the app: - Spotlight wrapper fade: the enter/exit opacity tracks left durationMs implicit (Motion default). A literal consumer treats a missing duration as instant, so the spotlight would pop on/off. Pinned to Motion's default 300ms tween. - Laser dot geometry: the descriptor captured only tracks, not the dot group's center anchor (translate -50%,-50%) or the rounded-full ring/core. A literal renderer would draw an offset 10px square. Added the static geometry (anchor, borderRadius 9999, ring inset/position) so the shape/position match the app. Also refined the effect-lifetime docstring to cite the app's per-scene engine teardown/completion (the actual clearEffects path) rather than an intra-engine boundary gate that is dead in the single-scene-per-engine configuration. The empty-scene "speech dwell → blank chat bubble" observation is pre-existing behavior: EMPTY_SCENE_DWELL is a verbatim move from lib/playback/engine-cursor.ts (unchanged from origin/main), out of scope for this move-only PR; tracked separately. Tests: tests/lib/choreography 54 pass; engine regression 28 pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(choreography): zero-duration for engine-skipped/no-op actions in timeline Fourth cross-review round (codex) flagged two remaining timeline divergences: - Skipped discussions: the engine skips a discussion outright (no timer) when it's already consumed or its agent isn't selected, but the timeline always charged DISCUSSION_TRIGGER_DELAY_MS. Added `isDiscussionSkipped` resolver (runtime-state-dependent, like getVideoDurationMs) → 0ms when skipped. - No-op whiteboard draws: executeWbDrawText (empty content) and executeWbDrawTable (no rows/cols) return before any delay. The timeline now charges 0ms for these determinable-from-the-action no-ops instead of WB_DRAW_MS. (KaTeX-failure / missing-edit-target no-ops depend on runtime state and remain out of scope, consistent with the resolver pattern.) Tests: tests/lib/choreography 56 pass; engine regression 28 pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(choreography): exact-5s effect chain break + spotlight dim full-screen geometry Fifth cross-review round (codex), both P2: - Effect chain break at an EXACT 5s boundary: the earlier effect's clear timer is queued before the reading timer that triggers the later effect (same 5000ms delay), so it fires first — the predecessor is cleared at exactly deadlineMs, not extended. Changed the chain guard from `> deadlineMs` to `>= deadlineMs`. - Spotlight dim layer full-screen geometry: the descriptor recorded only fill + mask relation, leaving a literal consumer no way to know the dim rect spans the 0..100 viewport. Added explicit x/y/width/height (100×100 at origin) so the descriptor is self-contained. Tests: tests/lib/choreography 57 pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(choreography): no-op wb_edit_code resolver in timeline Sixth cross-review round (codex), one P2: executeWbEditCode returns before its delay when the edit can't apply (missing/non-code target, stale line refs). The timeline always charged WB_EDIT_MS. Added `isEditCodeNoop` resolver (runtime- state-dependent, same pattern as getClearElementCount / isDiscussionSkipped) → 0ms when the caller flags a no-op. Tests: tests/lib/choreography 58 pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(choreography): descriptor layer inheritance for nested effect layers Seventh cross-review round (codex), two P2 with one root cause: the flat layers[] model couldn't express a child layer riding a parent's animation (the source nests some layers inside an animated wrapper). Added an `inheritsFrom: {parentId, props}` relation to LayerSchema: - Laser ring + core inheritsFrom the animated `dot` (left/top/opacity), so a literal consumer flies them in/out with the dot instead of leaving them at a static origin while only the dot moves. - Spotlight border inheritsFrom `dim` (opacity), so the outline fades out with the wrapper instead of lingering after the dimming layer disappears. Tests: tests/lib/choreography 60 pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(choreography): address review — discussion auto-skip, explicit video policy, import allowlist Human review (wyuc) raised two blocking timing issues + one non-blocking guard: - Discussion dwell (blocking): a non-skipped discussion in unattended playback/export blocks for the trigger delay AND the ProactiveCard's own auto-skip countdown, not just DISCUSSION_TRIGGER_DELAY_MS. Added DISCUSSION_AUTO_SKIP_MS (5000) to the timing spec and charge DISCUSSION_TRIGGER_DELAY_MS + DISCUSSION_AUTO_SKIP_MS. ProactiveCard now reads the same constant (was a hardcoded 5000), so card countdown and timeline can't drift. A `spotlight -> discussion -> speech` timeline now extends the spotlight across the full discussion interval. - play_video (blocking): an unresolved duration no longer silently becomes a 0ms segment (which shifted every later action early). New `onUnresolvedVideoDuration` policy defaults to 'throw' (fail loudly); 'cap' assumes MAX_VIDEO_WAIT_MS, 'zero' opts back into no-dwell explicitly. - Purity guard (non-blocking): turned the lib/choreography boundary into a true import allowlist. Beyond the existing @/… + render-package blocks, it now rejects parent-escape (../…) imports/re-exports, any bare package other than @openmaic/dsl / zod, and dynamic import()/require(). Negative-tested: ../store, a stray bare package, export * from ../playback, and import('react') all fail. Tests: tests/lib/choreography 60 pass; engine regression 28 pass; tsc + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: wyuc <wang-yc24@mails.tsinghua.edu.cn> |
||
|
|
d00795094c |
feat(playback): add action-level navigation (#843)
Squashed from #843. Adds Notes click-to-jump action navigation and action-level playback resume/restore, with visible toolbar sentence controls removed after review. |
||
|
|
cec3c6aaea |
Refactor widget actions into the scene action pipeline (#796)
* refactor: align widget actions with scene pipeline * feat(edit): migrate legacy teacherActions off interactive content on load The widget-actions pipeline no longer persists a teacherActions authoring layer; playback reads only scene.actions. Add migrateInteractiveContent to strip the dead field from existing documents as they load through migrateScene (wired into all store/classroom load paths). Satisfies the #794 forward-migration acceptance item. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(generation): default widget_setState.state to {} when the LLM omits it The removed TeacherAction->Action converter always defaulted state to {}. Restore that guard in the structured-output parser so an action missing state can't forward state: undefined into the widget iframe, where SET_WIDGET_STATE handlers dereference it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(edit): also strip nested widgetConfig.teacherActions in migration Legacy WidgetConfig variants each declared their own teacherActions, so existing documents can carry the dead field nested inside content.widgetConfig as well as at the top level. Extend migrateInteractiveContent to drop both, preserving all other widgetConfig fields. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(prompts): document stable widget-action selectors for diagram/game/viz3d The interactive-actions prompt documented stable target selectors only for procedural-skill and simulation widgets. Add the canonical selectors the diagram / game / visualization3d content prompts actually emit (node IDs via revealOrder; #zoom-in-btn / #canvas-container / #speed-slider / ...; #start-btn), plus a rule to use widget_setState or a speech-only beat instead of guessing a target when no stable selector is known (e.g. free-form code widgets). Hardens against widget actions silently no-oping on non-existent selectors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: 杨慎 <117187635+cosarah@users.noreply.github.com> |
||
|
|
7cb129183d |
chore(packages): publish the @openmaic/* SDK family to npm (#778) (#780)
* chore(packages): publish the @openmaic/* SDK family to npm (#778) Prepares the @openmaic/{dsl,renderer,importer} family for its first npm publish, and moves the SDK packages onto the @openmaic scope. Why the scope move: the @maic org name is unavailable on npm (an unscoped `maic` package already holds the name), so @maic/* is not claimable. @openmaic matches the project name, the scope is free, and the repo already ships an @openmaic/docs package — so the SDK family now lines up with that convention. - rename @maic/{dsl,renderer,importer} -> @openmaic/* across packages, the workspace glob, the package dir, and all import sites; lockfile regenerated - renderer: add publishConfig (public, registry.npmjs.org) — was missing, so a scoped publish would default to the wrong registry / restricted access - importer: add a files allowlist (dist, README, LICENSE) and drop the fragile .npmignore blacklist that shipped src; add an exports map so ESM consumers resolve dist/index.js instead of falling back to the .cjs main - all three: add a prepublishOnly build (+ test/typecheck) guard so a publish can never ship a stale or empty dist - add a tag-triggered publish workflow with npm provenance, pinned by name to the three @openmaic packages so the vendored forks (mathml2omml, pptxgenjs) are never published Refs #778, #720 (Phase 1). * fix(packages): address cross-review on the @openmaic publish prep Cross-review (Claude /code-review + codex) on this PR surfaced: - renderer's advertised CJS entry was broken: it keeps @openmaic/dsl external and imports a runtime enum from it, but dsl is ESM-only (no `require` condition), so `require('@openmaic/renderer')` would throw ERR_PACKAGE_PATH_NOT_EXPORTED. Make renderer ESM-only: drop the `.cjs` rollup output, `main` now points at the ESM build, and the `require` conditions are removed from `exports`. (importer is unaffected — it bundles dsl, so its CJS build still works.) - prepublishOnly re-ran the test suite during `pnpm -r publish`, so a flaky test after dsl had already published gave a non-atomic partial release. Reduce prepublishOnly to a build-only guard (never ship stale/empty dist) and move the real test/typecheck gate into the workflow, before any publish. - document that an @openmaic/* tag publishes the whole family via `pnpm -r` (pnpm skips already-published versions); the tag is a release marker, not a per-package gate. Verified: dsl + renderer + importer build; renderer emits ESM only (0 .cjs), all exports entries resolve; `npm pack` ships dist + README + LICENSE with no src leak; frozen-lockfile passes. Refs #778. * style: reflow @openmaic/dsl type imports past print-width after rename The @maic -> @openmaic rename lengthened two single-line type imports past prettier's 100-col width; prettier --check flagged them. Pure formatting. Refs #778. * docs(importer): mark @openmaic/importer browser-only (cr-loop accepted limitation) codex cross-review flagged that the published @openmaic/importer throws `XMLHttpRequest is not a constructor` when loaded in a pure Node process — its rollup build is browser-targeted (`nodeResolve({browser:true})` + a browser pdf.js build). The app only consumes it client-side ('use client'), so this is by design. Document it as an accepted limitation: prominent browser-only note in the README and a `browser` field in the manifest. Refs #778. |
||
|
|
4dce1f7414 |
refactor(types): import the slide DSL directly from @maic/dsl; drop the shim (#738)
#707 left lib/types/slides.ts as a thin `export * from '@maic/dsl'` shim so the ~100 existing `@/lib/types/slides` import sites kept working unchanged. Point them at `@maic/dsl` directly and delete the shim, so the app consumes the package contract with no indirection and the legacy module path is gone. Pure module-specifier migration — every symbol imported from `@/lib/types/slides` was already a re-export of the same `@maic/dsl` symbol, so behavior is identical. Also redirects the two `lib/types` relative importers (`./slides`), the e2e fixture's relative import, and the dynamic `import('../types/slides').Slide` type alias in stage-storage; merges the now same-module import pair in slide-edit-elements. Verified: tsc --noEmit clean, eslint clean, vitest 804/804, next build OK, e2e (Playwright) 19/19. Part of #720 (Phase 1). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2dff6d10be |
feat: add generated video manifest refs (#540)
* feat: add generated video manifest refs * style: format video manifest changes * fix: address video manifest review feedback |
||
|
|
c02a607377 |
feat: interactive mode clean (#461)
* feat: add interactive mode * feat: add TeacherActions for interactive mode * fix: better prompt for ultra mode * feat: upgrade normal mode's interactive generation with 5 widget type and actions * feat: make interactive mode button clear * fix: remove unused TeacherOverlay * fix: resolve PR #458 review feedback - Revert unrelated infra changes (Dockerfile, docker-compose.yml) - Fix placeholder strings in generateWidgetContent - Rename ultraMode to interactiveMode consistently across codebase - Extract duplicated patchHtmlForIframe to shared utility - Fix widget store race condition by keying callbacks by sceneId - Fix teacher-action ID collisions by using descriptive IDs in prompt - Relax outline prompt constraints for flexibility - Optimize sidebar thumbnail with IntersectionObserver lazy loading - Add trailing newlines to modified files Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: update generateSceneActions call signatures The function signature changed to individual parameters instead of options object pattern after cherry-pick. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * style: fix prettier formatting Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: removed extra language arg * style: fix prettier formatting v2 * fix: add missing i18n keys --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
c75cf6e602 |
feat: add code element support for whiteboard (#385)
* feat: add CODE element type and wb_draw_code/wb_edit_code action types Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: implement wb_draw_code and wb_edit_code in ActionEngine * feat: add BaseCodeElement with Shiki syntax highlighting and animations * feat: register CodeElement in renderer and add inline action tags * feat: add wb_draw_code and wb_edit_code to agent action schemas Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add code element context to director prompt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add code element guidelines and context to prompt builder Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add i18n keys for code element Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: prevent whiteboard zoom from intercepting code element scroll * chore: remove unused code element i18n keys The code.untitled and code.lines keys were added but never referenced by the BaseCodeElement component. Remove to reduce i18n maintenance burden and merge conflict surface. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
cce7087f67 |
Fix action filtering logic and add safety improvements (#163)
* fix: fix 3 bugs found during code review 1. formatQuestionsForPrompt called .join() on QuizOption objects, producing "[object Object]" in AI prompts instead of readable option text (e.g. "A. Option1, B. Option2"). 2. json-repair Fix 1 regex double-escaped valid JSON escapes (\t, \n, \r, \b, \f) and couldn't handle strings with escaped quotes. Now uses a proper string-aware regex and preserves valid escapes. 3. PlaybackEngine spotlight/laser actions used synchronous recursion via processNext(), risking stack overflow with many consecutive non-speech actions. Now uses queueMicrotask() to break recursion. https://claude.ai/code/session_01JdigqQtiAvomeifvWSQbjm * fix: ensure allowedActions whitelist applies to non-slide scenes The early return in Step 6 (slide-only action filter) caused Step 7 (allowedActions whitelist) to be skipped for non-slide scenes. This meant hallucinated actions from agents in quiz/interactive/pbl scenes were not filtered by the role-based allowedActions whitelist. Refactored to use a mutable result variable instead of early returns so both filters always apply. https://claude.ai/code/session_01JdigqQtiAvomeifvWSQbjm * fix: prevent video playback hang and division by zero in CJK detection 1. executePlayVideo could hang forever if the video element was invalid or the state change was missed between playVideo() and subscribe(). Added a 5-minute safety timeout to prevent indefinite blocking. 2. CJK language detection in browser TTS divided by chunkText.length without checking for empty strings, producing NaN. Now guards against zero-length text. https://claude.ai/code/session_01JdigqQtiAvomeifvWSQbjm * fix: prevent ordered entry with invalid index when partial text has no remaining content When a previously-partial text item completed but all content had already been streamed as deltas (remaining is empty), the code still pushed an ordered entry pointing to textChunks.length - 1, which could be -1 or reference an unrelated chunk. This caused spurious warnings in director-graph and potential text loss. Now only pushes the ordered entry when there is actual remaining content to emit. https://claude.ai/code/session_01JdigqQtiAvomeifvWSQbjm * fix: validate quiz points parameter and fix parse-pdf metadata spread order 1. quiz-grade: `points` from request body was used without validation, causing NaN in score calculation and invalid JSON responses when points was undefined, negative, or non-numeric. Now validates it is a positive finite number. 2. parse-pdf: metadata spread order put the default `pageCount` before `...result.metadata`, so the spread would overwrite the fallback. Fixed by spreading metadata first, then applying defaults after. Also changed `||` to `??` so pageCount=0 is preserved correctly. https://claude.ai/code/session_01JdigqQtiAvomeifvWSQbjm * fix: crash in moveUpElement, dead-code in video options, wrong parens in canCombine 1. use-order-element.ts: moveUpElement crashed with TypeError when a grouped element was already at the top of the z-order. The code accessed copyOfElementList[maxLevel + 1] without bounds checking, then dereferenced .groupId on undefined. Added early return guard matching the pattern used in the non-grouped branch. 2. video-providers.ts: normalizeVideoOptions had a dead-code ternary inside an if-block that already proved the condition was false. Simplified to directly use the fallback value. 3. use-canvas-operations.ts: canCombine check had incorrect parenthesization: (el.groupId && el.groupId) === firstGroupId evaluated the && first, which is equivalent to just el.groupId. Fixed to: el.groupId && el.groupId === firstGroupId. https://claude.ai/code/session_01JdigqQtiAvomeifvWSQbjm * style: fix prettier formatting in 3 files https://claude.ai/code/session_01JdigqQtiAvomeifvWSQbjm * fix: use valid ApiErrorCode in quiz-grade route INVALID_PARAMETER is not a member of API_ERROR_CODES. Use INVALID_REQUEST instead, which is the appropriate code for invalid request parameters. https://claude.ai/code/session_01JdigqQtiAvomeifvWSQbjm --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
66c6229736 |
fix: improve whiteboard UX with bounded canvas and history fixes (#235)
* fix: improve whiteboard UX with bounded canvas, toolbar cleanup, and history fixes - Refactor canvas to bounded viewport with visible white rectangle that moves with pan/zoom - Add reset view button in toolbar with fade animation (replaces in-canvas overlay) - Enable always-on pan (no toggle needed) - Add pan boundary clamping to prevent canvas from going too far offscreen - Separate autoFit transform from pan/zoom transform for cleaner architecture - Move history panel to popover anchored to history button - Save current content before restore to prevent data loss - Fix snapshot deduplication to check all history entries (not just last) - Remove dead code: restoredKey mechanism, zoomHint i18n keys, zoom hint overlay Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: simplify whiteboard history labels to sequential numbering Replace descriptive snapshot labels (beforeClear, beforeAIClear, beforeRestore) with simple #1, #2, ... numbering by creation order. Remove label field from WhiteboardSnapshot type and unused i18n keys. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restructure canvas layers to fix viewport clipping The viewport was the same size as the bounded canvas with overflow-hidden, causing panning to clip at the viewport edge instead of the workspace edge. - Viewport now fills the workspace (w-full h-full), no overflow-hidden - Outer container is the sole clip boundary - Bounded canvas centered via computed position with transformOrigin: 0 0 - Pan boundaries now account for zoom level - Zoom toward cursor instead of canvas center - Pan drag accounts for both containerScale and viewZoom - Removed unused isViewModified from WhiteboardCanvasHandle Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove autoFit transform, render elements at raw coordinates Remove the autoFitTransform that silently scaled content to fit within the fixed canvas. Elements now render at their raw coordinates and can overflow the white canvas background into transparent space. Users navigate with pan/zoom instead of relying on auto-shrink. - Remove autoFitTransform computation and all bounds calculation helpers - Remove overflow-hidden from bounded canvas so elements can spill out - Remove content transform layer (elements render directly) - Clean up unused imports (getElementRange, PPTLineElement) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
f3508ca15e |
fix: guard against undefined action.content in executeWbDrawText (#193)
Closes #192 action.content can be undefined when the AI generates a wb_draw_text action without providing content. This caused a TypeError on .startsWith(). Now falls back to empty string and returns early. Co-authored-by: wyuc <wang-yc24@mails.tsinghua.edu.cn> |
||
|
|
fbe8399678 |
feat(generate): server-side media & TTS generation (#75)
* feat(generate): server-side media & TTS generation in classroom pipeline When `enableImageGeneration`, `enableVideoGeneration`, or `enableTTS` flags are passed to /api/generate-classroom, the server now actually generates media files and TTS audio, persists them to disk, and replaces placeholders in the classroom JSON with serving URLs. - Add media serving API route (GET /api/classroom-media/[id]/[...path]) - Add server-side media generation utility (image, video, TTS) - Wire generation phases into classroom pipeline with progress steps - Update AudioPlayer to support URL-based playback (server-generated TTS) - Add audioUrl field to SpeechAction type - Extend /api/health with capabilities detection - Pass through feature flags in generate-classroom route - Update skill docs for optional feature configuration Closes #49 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: fix prettier formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(generate): add agentMode support for server-side classroom generation Allow callers to choose between built-in default agents ('default') or LLM-generated custom agent profiles ('generate') via a new agentMode field on GenerateClassroomInput. The resolved agents are injected into outline, scene content, and scene action prompts via teacherContext. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(media): harden download, parallelize media gen, fix TTS concatenation - Add timeout (2min) and max size (100MB) guard to downloadToBuffer - Run image and video generation in parallel via Promise.all - Only split TTS text for MP3 (frame-based); skip for WAV/OGG/AAC whose container headers break on naive byte concatenation - Remove unnecessary type assertion for speechAction.audioUrl Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: fix prettier formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(media): harden media serving and TTS generation - Add realpath check to prevent symlink-based path traversal in media route - Stream files via createReadStream instead of readFile to avoid OOM on large videos - Truncate TTS text for non-concatenable formats (WAV/OGG/AAC) when exceeding provider limit - Add 'researching' step to distinguish web search from outline generation progress Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tts): split long speech actions instead of concatenating audio bytes Adopt the same strategy as client-side: split a long speech action into multiple shorter actions before TTS generation, so each sub-action gets its own audio file. This avoids the broken byte-concatenation approach that corrupted non-MP3 formats (WAV/OGG/AAC container headers). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(tts): extract shared TTS splitting logic into lib/audio/tts-utils Both client-side (use-scene-generator) and server-side (classroom-media-generation) had duplicated TTS_MAX_TEXT_LENGTH, text splitting, and action splitting logic. Extract into a shared module to keep them in sync. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove unused TTS_MAX_TEXT_LENGTH import Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: wyuc <wang-yc24@mails.tsinghua.edu.cn> |
||
|
|
43a0c8e39e |
feat: whiteboard history and auto-save (#40)
* feat: add whiteboard history and auto-save (closes #32) - New whiteboard-history.tsx: browseable history panel UI - New whiteboard-history.ts: Zustand store with snapshot stack, fingerprint-based deduplication, and restoredKey one-shot guard - New element-fingerprint.ts: shared fingerprint utility (id+position+size) - whiteboard-canvas.tsx: auto-snapshot effect that saves the current stable state after 2s debounce; unified fingerprint import; useMemo for elements - index.tsx: pushSnapshot before UI clear; history panel trigger button - stage.ts: i18n keys for history UI (zh/en) * fix: address audit findings for whiteboard history - engine.ts: AI wb_clear now saves snapshot before clearing (audit #3) - page.tsx: clear whiteboard history on classroom switch (audit #4) * fix: address review round 2 (P1/P2a/P2b/P3) - P1: Block restore during clear animation (race condition) - P2a: Skip no-op restores to avoid stale restoredKey - P2b: clearHistory() now also resets restoredKey - P3: Dedicated restoreError i18n key * fix: fingerprint includes semantic content per element type (P2c) * fix: address reviewer feedback (fingerprint cache, AI clear label, exhaustive check) |
||
|
|
843d33e5cc |
chore: enforce Prettier formatting and fix lint issues
- Add .prettierignore to exclude vendor packages, lock files, markdown, and YAML - Update .prettierrc: printWidth 100, singleQuote, trailingComma "all" - Run Prettier across all source files for consistent formatting - Fix unused imports (UserRequirements, setTTSProvider) - Fix eslint-disable comment placement after Prettier reformat Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
0d20abfed9 |
Initial commit: OpenMAIC AI classroom platform
Next.js 16 App Router application with: - AI-powered interactive classroom generation from PDF/text requirements - Multi-agent discussion system (teacher, students, assistant roles) - Provider abstraction for LLM, TTS, ASR, image, video, web search - Local-first data architecture (IndexedDB/Dexie) - i18n support (zh-CN/en-US) - Structured logger, SSRF guard, lint/build passing (0 errors) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |