feat(maic-editor): enablement infrastructure (pre-slide-surface)#571
Merged
Conversation
Contributor
Author
Self-CR passRan a code-review loop. Verdict:
Tests: 46 new (feature-flag 5 + slide-schema 10 + round-trip 2
Ready for review. |
Pre-requisite for the slide surface (#562). Ships the safety infrastructure so each subsequent surface PR is small and recoverable: 1. Feature flag NEXT_PUBLIC_MAIC_EDITOR_ENABLED, default OFF — gates the Pro toggle in Header. StageMode unchanged. 2. SlideContent.schemaVersion + pure idempotent migrateSlideContent / migrateScene; setScenes / addScene funnel legacy data through the migrate at the store boundary. 3. tests/edit/round-trip/ harness: apply ops -> buildPptxBlob -> JSZip parse -> assert content survived. No PPTX -> Slide reimport exists in the codebase, so the full reimport-diff shape isn't doable; per-op assertions extend the harness in #562. buildPptxBlob is now exported (hook is still the only runtime caller). 4. Per-scene slide-history persistence helpers (persist / load / has / clear, keyed maic-editor:slide-history:${sceneId}, swallow storage failures) + standalone SlideHistoryRestorePrompt dialog + 4 new i18n strings x 6 locales. Stage wiring deferred to #562. 5. Concurrency guards: isSceneEditLocked predicate (defensive; no current call path structurally hits it); localStorage-backed multi-tab edit lock with tryAcquire / refresh / release / heldByOther, stale-lock takeover after 3x heartbeat; standalone MultiTabEditConflictPrompt + 3 new i18n strings x 6 locales. Stage wiring deferred to #562. The slide-surface PR owns the edit-entry effect machinery (where the history-state lifecycle and per-tab tabId ref naturally live), so shipping half-wired dialogs here would speculatively build Stage state we know we'll restructure on contact with the surface. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…grade CR follow-up: previously, content with schemaVersion newer than CURRENT (e.g. v2 written by a future client) was silently truncated back to the current version. Now: if schemaVersion >= CURRENT, return the content untouched. The slide may not render correctly on an older client, but its on-disk shape stays intact for the next compatible client to read. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
9746d0a to
be675dd
Compare
cosarah
approved these changes
May 17, 2026
Collaborator
cosarah
left a comment
There was a problem hiding this comment.
LGTM. Locally verified:
- Feature flag gates Pro toggle (with/without
NEXT_PUBLIC_MAIC_EDITOR_ENABLED) - 45 new tests pass (lock, schema migration, history persistence, regen lock, round-trip)
- Confirmed lock/history APIs are infra-only with no runtime callers yet — matches PR scope note (#562 owns Stage wiring)
Code quality: lock atomicity and stale-takeover logic are sound; migrateSlideContent is idempotent and forward-compatible; localStorage paths are SSR-safe and exception-tolerant. No blocking issues.
wyuc
added a commit
that referenced
this pull request
May 17, 2026
) PR1 of the slide-surface work (infra-first slice). Registers the slide SceneEditorSurface so EditShell lights up Pro mode for slide scenes. - SceneEditorSurface impl + sceneEditorRegistry registration; the surface owns a SlideEditHistory via the #564 kernel. - Reuse the unmodified slide renderer Canvas through a surface-owned scene context; geometry drag/resize/rotate commits funnel into element.update ops (scene-edit bridge), one gesture = one undo step. - Geometry numeric x/y/w/h/rotate popover as the precise fallback; gated off for line elements (PPTLineElement omits height/rotate). - Wire #571 infra: cross-tab edit lock + conflict prompt, slide-history persistence + restore prompt, regen-lock guard. - Renderer-commit classification: a real geometry gesture commits synchronously inside a pointer interaction; the renderer's ResizeObserver text-normalization commits with none, so it is folded into the baseline (no undo step / no persist / no spurious restore prompt on entry) instead of being staged as a user edit. - Per-op round-trip test for element.update geometry; bridge + session unit tests; edit.geometry i18n across all 6 locales. Upstream-shared changes are kept minimal and additive: an optional `controller` prop on SceneProvider (uncontrolled/playback path unchanged) so staged edits don't write through to the live stage store, and a FloatingToolbar trigger-nesting fix (it wrapped PopoverTrigger around <Tooltip>, a provider, so no popoverContent action could open). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
wyuc
added a commit
that referenced
this pull request
May 17, 2026
) PR1 of the slide-surface work (infra-first slice). Registers the slide SceneEditorSurface so EditShell lights up Pro mode for slide scenes. - SceneEditorSurface impl + sceneEditorRegistry registration; the surface owns a SlideEditHistory via the #564 kernel. - Reuse the unmodified slide renderer Canvas through a surface-owned scene context; geometry drag/resize/rotate commits funnel into element.update ops (scene-edit bridge), one gesture = one undo step. - Geometry numeric x/y/w/h/rotate popover as the precise fallback; gated off for line elements (PPTLineElement omits height/rotate). - Wire #571 infra: cross-tab edit lock + conflict prompt, slide-history persistence + restore prompt, regen-lock guard. - Renderer-commit classification: a real geometry gesture commits synchronously inside a pointer interaction; the renderer's ResizeObserver text-normalization commits with none, so it is folded into the baseline (no undo step / no persist / no spurious restore prompt on entry) instead of being staged as a user edit. - Per-op round-trip test for element.update geometry; bridge + session unit tests; edit.geometry i18n across all 6 locales. Upstream-shared changes are kept minimal and additive: an optional `controller` prop on SceneProvider (uncontrolled/playback path unchanged) so staged edits don't write through to the live stage store, and a FloatingToolbar trigger-nesting fix (it wrapped PopoverTrigger around <Tooltip>, a provider, so no popoverContent action could open). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
wyuc
added a commit
that referenced
this pull request
May 17, 2026
) PR1 of the slide-surface work (infra-first slice). Registers the slide SceneEditorSurface so EditShell lights up Pro mode for slide scenes. - SceneEditorSurface impl + sceneEditorRegistry registration; the surface owns a SlideEditHistory via the #564 kernel. - Reuse the unmodified slide renderer Canvas through a surface-owned scene context; geometry drag/resize/rotate commits funnel into element.update ops (scene-edit bridge), one gesture = one undo step. - Geometry numeric x/y/w/h/rotate popover as the precise fallback; gated off for line elements (PPTLineElement omits height/rotate). - Wire #571 infra: cross-tab edit lock + conflict prompt, slide-history persistence + restore prompt, regen-lock guard. - Renderer-commit classification: a real geometry gesture commits synchronously inside a pointer interaction; the renderer's ResizeObserver text-normalization commits with none, so it is folded into the baseline (no undo step / no persist / no spurious restore prompt on entry) instead of being staged as a user edit. - Per-op round-trip test for element.update geometry; bridge + session unit tests; edit.geometry i18n across all 6 locales. Upstream-shared changes are kept minimal and additive: an optional `controller` prop on SceneProvider (uncontrolled/playback path unchanged) so staged edits don't write through to the live stage store, and a FloatingToolbar trigger-nesting fix (it wrapped PopoverTrigger around <Tooltip>, a provider, so no popoverContent action could open). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
wyuc
added a commit
that referenced
this pull request
May 17, 2026
) (#579) PR1 of the slide-surface work (infra-first slice). Registers the slide SceneEditorSurface so EditShell lights up Pro mode for slide scenes. - SceneEditorSurface impl + sceneEditorRegistry registration; the surface owns a SlideEditHistory via the #564 kernel. - Reuse the unmodified slide renderer Canvas through a surface-owned scene context; geometry drag/resize/rotate commits funnel into element.update ops (scene-edit bridge), one gesture = one undo step. - Geometry numeric x/y/w/h/rotate popover as the precise fallback; gated off for line elements (PPTLineElement omits height/rotate). - Wire #571 infra: cross-tab edit lock + conflict prompt, slide-history persistence + restore prompt, regen-lock guard. - Renderer-commit classification: a real geometry gesture commits synchronously inside a pointer interaction; the renderer's ResizeObserver text-normalization commits with none, so it is folded into the baseline (no undo step / no persist / no spurious restore prompt on entry) instead of being staged as a user edit. - Per-op round-trip test for element.update geometry; bridge + session unit tests; edit.geometry i18n across all 6 locales. Upstream-shared changes are kept minimal and additive: an optional `controller` prop on SceneProvider (uncontrolled/playback path unchanged) so staged edits don't write through to the live stage store, and a FloatingToolbar trigger-nesting fix (it wrapped PopoverTrigger around <Tooltip>, a provider, so no popoverContent action could open). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
wyuc
added a commit
that referenced
this pull request
May 29, 2026
* feat(maic-editor): enablement infrastructure (pre-slide-surface) Pre-requisite for the slide surface (#562). Ships the safety infrastructure so each subsequent surface PR is small and recoverable: 1. Feature flag NEXT_PUBLIC_MAIC_EDITOR_ENABLED, default OFF — gates the Pro toggle in Header. StageMode unchanged. 2. SlideContent.schemaVersion + pure idempotent migrateSlideContent / migrateScene; setScenes / addScene funnel legacy data through the migrate at the store boundary. 3. tests/edit/round-trip/ harness: apply ops -> buildPptxBlob -> JSZip parse -> assert content survived. No PPTX -> Slide reimport exists in the codebase, so the full reimport-diff shape isn't doable; per-op assertions extend the harness in #562. buildPptxBlob is now exported (hook is still the only runtime caller). 4. Per-scene slide-history persistence helpers (persist / load / has / clear, keyed maic-editor:slide-history:${sceneId}, swallow storage failures) + standalone SlideHistoryRestorePrompt dialog + 4 new i18n strings x 6 locales. Stage wiring deferred to #562. 5. Concurrency guards: isSceneEditLocked predicate (defensive; no current call path structurally hits it); localStorage-backed multi-tab edit lock with tryAcquire / refresh / release / heldByOther, stale-lock takeover after 3x heartbeat; standalone MultiTabEditConflictPrompt + 3 new i18n strings x 6 locales. Stage wiring deferred to #562. The slide-surface PR owns the edit-entry effect machinery (where the history-state lifecycle and per-tab tabId ref naturally live), so shipping half-wired dialogs here would speculatively build Stage state we know we'll restructure on contact with the surface. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(maic-editor): migrateSlideContent forward-compat — no silent downgrade CR follow-up: previously, content with schemaVersion newer than CURRENT (e.g. v2 written by a future client) was silently truncated back to the current version. Now: if schemaVersion >= CURRENT, return the content untouched. The slide may not render correctly on an older client, but its on-disk shape stays intact for the next compatible client to read. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
wyuc
added a commit
that referenced
this pull request
May 29, 2026
) (#579) PR1 of the slide-surface work (infra-first slice). Registers the slide SceneEditorSurface so EditShell lights up Pro mode for slide scenes. - SceneEditorSurface impl + sceneEditorRegistry registration; the surface owns a SlideEditHistory via the #564 kernel. - Reuse the unmodified slide renderer Canvas through a surface-owned scene context; geometry drag/resize/rotate commits funnel into element.update ops (scene-edit bridge), one gesture = one undo step. - Geometry numeric x/y/w/h/rotate popover as the precise fallback; gated off for line elements (PPTLineElement omits height/rotate). - Wire #571 infra: cross-tab edit lock + conflict prompt, slide-history persistence + restore prompt, regen-lock guard. - Renderer-commit classification: a real geometry gesture commits synchronously inside a pointer interaction; the renderer's ResizeObserver text-normalization commits with none, so it is folded into the baseline (no undo step / no persist / no spurious restore prompt on entry) instead of being staged as a user edit. - Per-op round-trip test for element.update geometry; bridge + session unit tests; edit.geometry i18n across all 6 locales. Upstream-shared changes are kept minimal and additive: an optional `controller` prop on SceneProvider (uncontrolled/playback path unchanged) so staged edits don't write through to the live stage store, and a FloatingToolbar trigger-nesting fix (it wrapped PopoverTrigger around <Tooltip>, a provider, so no popoverContent action could open). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
wyuc
added a commit
that referenced
this pull request
May 29, 2026
* feat(maic-editor): enablement infrastructure (pre-slide-surface) Pre-requisite for the slide surface (#562). Ships the safety infrastructure so each subsequent surface PR is small and recoverable: 1. Feature flag NEXT_PUBLIC_MAIC_EDITOR_ENABLED, default OFF — gates the Pro toggle in Header. StageMode unchanged. 2. SlideContent.schemaVersion + pure idempotent migrateSlideContent / migrateScene; setScenes / addScene funnel legacy data through the migrate at the store boundary. 3. tests/edit/round-trip/ harness: apply ops -> buildPptxBlob -> JSZip parse -> assert content survived. No PPTX -> Slide reimport exists in the codebase, so the full reimport-diff shape isn't doable; per-op assertions extend the harness in #562. buildPptxBlob is now exported (hook is still the only runtime caller). 4. Per-scene slide-history persistence helpers (persist / load / has / clear, keyed maic-editor:slide-history:${sceneId}, swallow storage failures) + standalone SlideHistoryRestorePrompt dialog + 4 new i18n strings x 6 locales. Stage wiring deferred to #562. 5. Concurrency guards: isSceneEditLocked predicate (defensive; no current call path structurally hits it); localStorage-backed multi-tab edit lock with tryAcquire / refresh / release / heldByOther, stale-lock takeover after 3x heartbeat; standalone MultiTabEditConflictPrompt + 3 new i18n strings x 6 locales. Stage wiring deferred to #562. The slide-surface PR owns the edit-entry effect machinery (where the history-state lifecycle and per-tab tabId ref naturally live), so shipping half-wired dialogs here would speculatively build Stage state we know we'll restructure on contact with the surface. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(maic-editor): migrateSlideContent forward-compat — no silent downgrade CR follow-up: previously, content with schemaVersion newer than CURRENT (e.g. v2 written by a future client) was silently truncated back to the current version. Now: if schemaVersion >= CURRENT, return the content untouched. The slide may not render correctly on an older client, but its on-disk shape stays intact for the next compatible client to read. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
wyuc
added a commit
that referenced
this pull request
May 29, 2026
) (#579) PR1 of the slide-surface work (infra-first slice). Registers the slide SceneEditorSurface so EditShell lights up Pro mode for slide scenes. - SceneEditorSurface impl + sceneEditorRegistry registration; the surface owns a SlideEditHistory via the #564 kernel. - Reuse the unmodified slide renderer Canvas through a surface-owned scene context; geometry drag/resize/rotate commits funnel into element.update ops (scene-edit bridge), one gesture = one undo step. - Geometry numeric x/y/w/h/rotate popover as the precise fallback; gated off for line elements (PPTLineElement omits height/rotate). - Wire #571 infra: cross-tab edit lock + conflict prompt, slide-history persistence + restore prompt, regen-lock guard. - Renderer-commit classification: a real geometry gesture commits synchronously inside a pointer interaction; the renderer's ResizeObserver text-normalization commits with none, so it is folded into the baseline (no undo step / no persist / no spurious restore prompt on entry) instead of being staged as a user edit. - Per-op round-trip test for element.update geometry; bridge + session unit tests; edit.geometry i18n across all 6 locales. Upstream-shared changes are kept minimal and additive: an optional `controller` prop on SceneProvider (uncontrolled/playback path unchanged) so staged edits don't write through to the live stage store, and a FloatingToolbar trigger-nesting fix (it wrapped PopoverTrigger around <Tooltip>, a provider, so no popoverContent action could open). Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #570. Lands before #562. Stacked on #568 (the diff against
feat/maic-editor-v0includes #568's commits until that merges).What ships
Feature flag —
NEXT_PUBLIC_MAIC_EDITOR_ENABLED, default OFF.Pro toggle in
Headeronly renders when ON.StageModeunchanged.Slide DSL schema versioning —
SlideContent.schemaVersion?: number(current = 1).
migrateSlideContent/migrateSceneare pure +idempotent; the stage store funnels
setScenes/addScenethroughmigrateSceneso legacy data gets stamped at the boundary.Round-trip test harness —
tests/edit/round-trip/. Caveat: noPPTX → Slide reimport exists in the codebase, so the harness does
apply ops → buildPptxBlob → JSZip parse → assert content survivedinstead of a full reimport diff. Per-op cases extend it in [MAIC Editor] Slide surface — first concrete static-display surface #562.
buildPptxBlobis now exported (the hook is still the only runtimecaller).
Edit history persistence — per-scene
persist/load/has/clearhelpers undermaic-editor:slide-history:${sceneId}, plus astandalone
SlideHistoryRestorePromptdialog. 4 new i18n strings × 6locales. Not wired into Stage yet — [MAIC Editor] Slide surface — first concrete static-display surface #562 owns the entry effect.
Concurrency guards —
isSceneEditLockedpredicate for the AI-regen lock (defensive;current call paths can't structurally trigger it).
tryAcquireEditLock/refreshEditLock/releaseEditLock/isEditLockHeldByOtherfor multi-tab, localStorage-backed; atomicacquire, idempotent heartbeat, stale-lock takeover after 3×
heartbeat (default 15s).
MultiTabEditConflictPrompt. 3 new i18n strings × 6locales. Not wired into Stage yet — same reason as (4).
Why dialogs aren't wired here
The slide-surface PR owns the edit-entry effect machinery — that's where
the history-state lifecycle and the per-tab
tabIdref naturally live.Shipping half-wired dialogs now means speculatively building Stage state
we'd restructure on contact with the surface.
Out of scope
Server-side history sync (Phase 2); cross-device conflict resolution;
optimistic regen guard.
Test plan
check:i18n-keys· build — greenpnpm test— 410 passing / 9 failing; the 9 are baselinetests/server/ssrf-guard.test.ts(DNS-dependent). +45 new testsacross feature-flag (5), slide-schema (9), round-trip (2),
slide-history-persistence (8), regen-lock (4), edit-mode-lock (17).
as in refactor(maic-editor): drop EditModeSidebar; clean Pro mode chrome #568. No new user-visible affordances.
🤖 Generated with Claude Code