Commit Graph
13 Commits
Author SHA1 Message Date
wyuc 67584fe9e8 fix(whiteboard): correct the inverted viewportRatio so boards render 16:9 landscape (#1257) 2026-08-27 20:39:26 +08:00
LING DUAN 2cbe4ae293 feat(pi): add Native Child whiteboard tools (#1152)
* feat(pi): add native whiteboard tools

* style(pi): format native whiteboard tests

* fix(pi): clarify whiteboard CAS handoff

* fix(whiteboard): reconcile runtime projection on open
2026-08-21 18:07:49 +08:00
wyuc 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.
2026-06-24 15:12:23 +08:00
wyucandClaude Opus 4.8 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>
2026-06-15 15:04:06 +08:00
杨慎andClaude Opus 4.7 45929cf5d5 fix(whiteboard): code element captures internal scroll/drag (#530) (#544)
* fix(whiteboard): code element captures internal scroll/drag (#530)

Wheel inside the code element no longer reaches the whiteboard zoom
handler, and pointer-down drag inside the code body now scrolls the
content vertically and horizontally instead of triggering whiteboard
pan. Native listeners are used to bypass React event delegation races
with the parent's pointer/wheel handlers, and the rows are wrapped in
a max-content container so horizontal overflow is reachable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(whiteboard): keep code element content in sync during pan

Memoize the whiteboard's AnimatedElement wrapper and drop the per-line
`layout="position"` motion prop. Together these stop Framer Motion's
projection system from remeasuring code line positions against the
panning parent transform and animating the diff, which was making the
inner code lag behind the surrounding element box during a pan. Line
insert/remove animations still play via height/opacity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(whiteboard): tighten code element gesture handling

Address review feedback:
- Collapse the wrapper's native pointer/mouse/click listeners into a
  React `onPointerDown` / `onClick` (whiteboard pan is itself a React
  handler, so synthetic stopPropagation suffices).
- Merge the body drag-scroll and wheel listeners into a single effect.
- Handle `lostpointercapture` so an interrupted drag (alt-tab, devtools)
  releases capture, resets cursor, and clears the dragging flag.
- Run the same cleanup on unmount so a captured pointer is released and
  the cursor does not leak.
- Clarify the `AnimatedElement` memo comment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:30:22 +08:00
杨慎andClaude Opus 4.6 ad9e0ee7c6 refactor(i18n): migrate to i18next framework (#331)
* refactor(i18n): migrate to i18next framework

Replace hand-rolled i18n with i18next + react-i18next so that adding a
new language only requires dropping a JSON file in lib/i18n/locales/.

- Add i18next, react-i18next, i18next-browser-languagedetector deps
- Generate zh-CN.json / en-US.json from existing TS translation modules
- Rewrite lib/i18n/index.ts as a thin wrapper around i18n.t()
- Rewrite use-i18n hook to delegate to useTranslation(); external API
  (locale, setLocale, t) is unchanged so consumers need no changes
- SSR-safe: LanguageDetector only loaded on client side

Closes #327

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(i18n): use interpolation for greeting to support natural phrasing

Replace string concatenation (greeting + displayName) with two
i18next keys: greetingWithName (with {{name}} interpolation) and
greetingDefault (standalone, no name).

This lets each locale choose natural phrasing independently:
- zh-CN: "嗨,同学" / "嗨,Alice"
- en-US: "Hi there" / "Hi, Alice"
- Future locales can avoid gender issues by choosing genderless defaults

Also widen the t() type signature to accept interpolation options.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(i18n): auto-discover locale files via dynamic import

Replace hardcoded zh-CN/en-US imports with i18next-resources-to-backend
and dynamic import(`./locales/${language}.json`). Bundler scans the
locales/ directory at build time, so adding a new language now requires
only dropping a JSON file — zero changes to existing code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(i18n): resolve hydration mismatch by deferring language detection

LanguageDetector ran during i18next init(), detecting browser language
before React hydrated — server rendered zh-CN while client switched to
en-US immediately, causing a hydration mismatch.

Fix: remove i18next-browser-languagedetector; init with a fixed lng
(zh-CN) so server and client agree on the first render. Language
detection is now done in I18nProvider's useEffect after hydration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(i18n): remove hardcoded locale list from language detection

Replace manual locale validation and startsWith('zh') prefix matching
with i18next's built-in fallback mechanism. Now changeLanguage() is
called with navigator.language directly — if the exact locale has no
JSON file, i18next automatically falls back to fallbackLng.

Also widen Locale type from union to string so adding new languages
doesn't require modifying types.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(i18n): add course language config with 11 languages and UI linkage

- Add lib/i18n/course-languages.ts with curated language list
  (zh-CN, zh-TW, en-US, ja, ko, fr, de, es, pt, ru, ar) including
  native labels and English prompt names
- Course language defaults to UI locale on first visit; once user
  explicitly picks a language, that choice persists across sessions
- Replace toggle button with dropdown selector showing native labels
- Widen language types from 'zh-CN'|'en-US' to string throughout
- Fix hardcoded language ternaries in LLM prompt injection:
  - prompt-builder.ts: use getCourseLanguagePromptName()
  - classroom-generation.ts: remove normalizeLanguage() that forced
    all non-English to zh-CN
  - PBL system prompt, agent templates, generate-pbl: append language
    instruction for non-zh/en languages
  - quiz-grade API: add language suffix for grading feedback

Closes #327

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(quiz): use course language instead of UI locale for grading

quiz-view was passing the UI locale to the grading API, causing AI
feedback to follow the student's answer language instead of the course
language. Now reads stage.language from the store.

Also strengthen the grading prompt: explicitly instruct the LLM to
write comments in the course language regardless of the student's
input language.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert: remove course language config and quiz fix from i18next branch

Reverts f7bd6bd and 5bb5133 — these are independent features that
should go into a separate branch/PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(i18n): regenerate locale JSON after merging latest main

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(i18n): remove obsolete TS translation files

These files are replaced by lib/i18n/locales/*.json and are no longer
imported anywhere in the codebase.

Removed: chat.ts, common.ts, generation.ts, settings.ts, stage.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style(i18n): fix prettier formatting in locale JSON files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(i18n): replace hardcoded language options with central locale registry

Add lib/i18n/locales.ts as a single source of truth for supported languages.
Language selectors in header and homepage now render dynamically from this
registry. Also adds supportedLngs to i18next config so unsupported languages
fall back correctly. Adding a new language now only requires a JSON file and
one line in the registry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(i18n): add missing rename keys to JSON locale files

Add classroom.rename, classroom.renamePlaceholder, and
classroom.renameFailed that were added to generation.ts on main
but missing from the JSON locale files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(i18n): extract LanguageSwitcher, restore type safety, fix locale resolution

- Extract shared <LanguageSwitcher /> component from page.tsx and header.tsx
- Derive Locale type from supportedLocales registry (as const satisfies)
- Add resolveLocale() to match browser language prefixes (e.g. 'en' → 'en-US')
- Remove config.ts changes (nonExplicitSupportedLngs + inline resources) that
  broke translation loading when combined with resourcesToBackend

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(i18n): use i18next double-brace interpolation instead of manual .replace()

- Convert all {var} placeholders in locale JSONs to {{var}} (i18next default)
- Replace .replace('{var}', value) calls with t(key, {var: value}) in all
  consuming components: lecture-notes-view, pbl-renderer, use-pbl-chat,
  generation-preview, whiteboard-history, agent-settings
- Widen t() type signature in handleIssueComplete to accept interpolation options

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(i18n): unify greeting with default nickname, add translation guide

- Remove greetingDefault key; greeting always uses greetingWithName
  with displayName (falls back to profile.defaultNickname)
- Change en-US defaultNickname from "Student" to "Learner"
- Add TRANSLATION_GUIDE.md documenting how to add languages and
  explaining keys with non-obvious UX design intent

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 12:04:42 +08:00
杨慎andClaude Opus 4.6 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>
2026-03-24 17:06:14 +08:00
Yizuki_AmeandYizukiAme 0533adbe82 feat: refine presentation mode speech bubbles, input flow, and accessibility (#195)
* feat: add presentation mode with fullscreen, idle-hide, and keyboard navigation

- Fullscreen via toolbar button or F11; exit via ESC/F11/button
- Header auto-hides, sidebars collapse, slide fills viewport
- Idle auto-hide (3s): toolbar/avatars fade out, speech bubble stays visible
- Smart suspension: idle-hide pauses during typing/recording/voice input
- Keyboard navigation: Arrow keys (prev/next), Space (play/pause), ESC (exit)
feat: add presentation mode with fullscreen, idle-hide, and keyboard navigation

- Fullscreen via toolbar button or F11; exit via ESC/F11/button
- Header auto-hides, sidebars collapse, slide fills viewport
- Idle auto-hide (3s): toolbar/avatars fade out, speech bubble stays visible
- Smart suspension: idle-hide pauses during typing/recording/voice input
- Keyboard navigation: Arrow keys (prev/next), Space (play/pause), ESC (exit)
- F11 intercepted to use Fullscreen API (ESC-friendly) instead of browser native
- Whiteboard hints reposition from bottom to top corners in fullscreen
- i18n: fullscreen/exitFullscreen keys (zh-CN + en-US)

Closes #102

.git/COMMIT_EDITMSG [unix] (14:47 22/03/2026)                                                                    1,1 Top
feat: add presentation mode with fullscreen, idle-hide, and keyboard navigation

- Fullscreen via toolbar button or F11; exit via ESC/F11/button
- Header auto-hides, sidebars collapse, slide fills viewport
- Idle auto-hide (3s): toolbar/avatars fade out, speech bubble stays visible
- Smart suspension: idle-hide pauses during typing/recording/voice input
- Keyboard navigation: Arrow keys (prev/next), Space (play/pause), ESC (exit)
- F11 intercepted to use Fullscreen API (ESC-friendly) instead of browser native
- Whiteboard hints reposition from bottom to top corners in fullscreen
- i18n: fullscreen/exitFullscreen keys (zh-CN + en-US)

Closes #102

.git/COMMIT_EDITMSG [unix] (14:47 22/03/2026)                                                                    1,1 Top
- F11 intercepted to use Fullscreen API (ESC-friendly) instead of browser native
- Whiteboard hints reposition from bottom to top corners in fullscreen
- i18n: fullscreen/exitFullscreen keys (zh-CN + en-US)

Closes #102

* feat: refine presentation mode speech bubbles, input flow, and accessibility

- User messages now display as right-side speech bubbles for send confirmation
- 'Your turn' cue is a clickable glass button respecting ASR preference
- Cue properly excludes bubbleRole/thinkingState to avoid UI overlap
- Right-side dock + bubble uses flexbox for dynamic stacking
- Voice-to-text switch now stops recording to prevent mic leaks
- Backdrop no longer blocks toolbar (bottom-14 cutoff)
- User message clears immediately when agent starts responding
- Dock buttons and avatar have proper aria-labels and button semantics
- Voice panel mic trigger converted from div to button with aria-label
- Added 5 i18n keys (zh-CN + en-US) for new accessible labels
- matchesSide typed as boolean, removed unused userAvatar from left overlay
- Avatar alt text localized via i18n

Refs #102

* fix: address code review audit  cancel ghost sends, consolidate getAgentConfig, fix a11y

* fix: guard concurrent voice recording and suppress cancel error toast

* style: vertically center placeholder text in presentation input bar

* style: full light-mode adaptation for presentation UI, toolbar dividers, bubble opacity 60%

* chore: address #129 follow-up  remove orphan i18n keys, clarify waitUntilDrained pause behavior

* fix: resolve spacebar conflict and address review feedback

- Stage: skip Space during active QA/discussion (let Roundtable own
  buffer-level pause); deterministic, no listener-order dependency
- Stage: wrap handlePlayPause in useCallback([playbackCompleted,
  currentScene]) to avoid keyboard listener re-registration
- Stage: clear presentationIdleTimerRef on unmount
- PresentationBubbleCard: add aria-live='polite' for screen readers
- Extract shared avatar constants to roundtable/constants.ts

* style: format with prettier

* fix: render ProactiveCard inside fullscreen container via portalContainer prop

* feat: show requesting agent avatar in dock on discussion request

---------

Co-authored-by: YizukiAme <yizukiame@github.com>
2026-03-24 13:04:55 +08:00
a680fcf31b feat: add pan, zoom, and auto-fit to whiteboard canvas (#31)
* feat: add pan, zoom, and auto-fit to whiteboard canvas

When AI generates whiteboard elements that exceed the fixed 1000x562.5
canvas, content was clipped by overflow-hidden with no way to view it.

This commit adds interactive navigation to the whiteboard:

- Auto-fit: when elements overflow, content is scaled and centered to
  ensure nothing is clipped (with 24px padding)
- Drag-to-pan: hold and drag to pan around the whiteboard
- Scroll-to-zoom: mouse wheel zooms in/out (0.2x to 5x range)
- Double-click or Reset View button to return to default view
- View auto-resets when new whiteboard content loads

When content fits within the canvas, behavior is unchanged - no extra
cursor, no transforms, no visual regression.

Fixes #19

* fix: correct pan tracking math and reset button event propagation

* fix: guard against zero containerScale and fix zoom docstring

* fix: cleanup reviewer feedback and disable pan/zoom on empty whiteboard

- Remove dead code: ?? 'Reset View' fallback (i18n key exists)
- Add useMemo for elementsKey (perf optimization)
- Fix el.height type narrowing (remove implicit any)
- Disable pan/zoom/cursor when whiteboard is empty (UX improvement)

* docs: revamp OpenClaw TIP section with playful copy and clearer install flow

Restructure the OpenClaw quickstart: install skill first, then pick mode.
Add meme-style nudge for OpenClaw to star the repo. Enlarge logo and
put "OpenClaw" in the TIP heading for visibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(build): use cross-platform file copy in mathml2omml (#20)

* fix(build): use cross-platform file copy in mathml2omml build script

Replace Unix `cp` with Node.js `fs.copyFileSync` so that `pnpm install`
works correctly on Windows where `cp` is not available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: add .worktrees to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(build):Next.js 16 要求 Node.js >= 20.9.0 (#21)

* fix(build):
1、README.md / README-zh.md — Node.js 版本要求 >= 18 → >= 20
2、package.json — 新增 engines: { node: ">=20.9.0" }
3、ci.yml — node-version: 20 → 22(与 Dockerfile 一致)
4、.nvmrc — 新建,内容 22

Co-authored-by: humingfeng <humfsss@gmail.com>

* Update Vercel configuration by removing bodyParser (#45)

Removed bodyParser configuration from Vercel functions.

* Fix SSRF/credential forwarding via client-supplied baseUrl (#30)

* Fix SSRF/credential forwarding via client-supplied baseUrl

* chore: format SSRF fix

* fix: tighten whiteboard pan and auto-fit bounds

* refactor: reset whiteboard viewport via keyed remount

* fix: preserve viewport while whiteboard content streams

* feat: add scroll-to-zoom interaction hint on whiteboard

* feat: add pan, zoom, and auto-fit to whiteboard canvas

When AI generates whiteboard elements that exceed the fixed 1000x562.5
canvas, content was clipped by overflow-hidden with no way to view it.

This commit adds interactive navigation to the whiteboard:

- Auto-fit: when elements overflow, content is scaled and centered to
  ensure nothing is clipped (with 24px padding)
- Drag-to-pan: hold and drag to pan around the whiteboard
- Scroll-to-zoom: mouse wheel zooms in/out (0.2x to 5x range)
- Double-click or Reset View button to return to default view
- View auto-resets when new whiteboard content loads

When content fits within the canvas, behavior is unchanged - no extra
cursor, no transforms, no visual regression.

Fixes #19

* fix: correct pan tracking math and reset button event propagation

* fix: guard against zero containerScale and fix zoom docstring

* fix: cleanup reviewer feedback and disable pan/zoom on empty whiteboard

- Remove dead code: ?? 'Reset View' fallback (i18n key exists)
- Add useMemo for elementsKey (perf optimization)
- Fix el.height type narrowing (remove implicit any)
- Disable pan/zoom/cursor when whiteboard is empty (UX improvement)

* fix: tighten whiteboard pan and auto-fit bounds

* refactor: reset whiteboard viewport via keyed remount

* fix: preserve viewport while whiteboard content streams

* feat: add scroll-to-zoom interaction hint on whiteboard

* fix: avoid synchronous setState in useEffect for hint visibility

---------

Co-authored-by: wyuc <wang-yc24@mails.tsinghua.edu.cn>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: 杨慎 <117187635+cosarah@users.noreply.github.com>
Co-authored-by: 1/2极客 <humf@vip.qq.com>
Co-authored-by: humingfeng <humfsss@gmail.com>
Co-authored-by: Rowtion <65099725+Rowtion@users.noreply.github.com>
Co-authored-by: Wing900 <167857578+Wing900@users.noreply.github.com>
2026-03-19 17:52:35 +08:00
Yizuki_Ame 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)
2026-03-18 23:56:11 +08:00
wyucandClaude Opus 4.6 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>
2026-03-13 16:09:45 +08:00
wyucandClaude Opus 4.6 cc44f32ca8 Refactor: directory structure cleanup + Google Fonts localization
- Replace next/font/google with local fonts (geist + @fontsource-variable/inter)
  to eliminate build-time network dependency
- Resolve lib/utils.ts vs lib/utils/ naming conflict (cn() → lib/utils/cn.ts)
- Move React Context Providers from lib/store/ to lib/contexts/
- Consolidate hooks into lib/hooks/ (use-theme, use-i18n)
- Organize loose components into subdirectories:
  agent-* → components/agent/
  canvas-* → components/canvas/
  generation-toolbar, media-popover → components/generation/
  speech-button, tts-config-popover → components/audio/
  proactive-card → components/chat/

65 files changed, lint 0 errors, build 28 pages passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:35:06 +08:00
wyucandClaude Opus 4.6 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>
2026-03-12 16:41:07 +08:00