Add export environment plan

This commit is contained in:
th committed 2026-06-07 13:24:34 +02:00
1 parent f469ab7444
commit b18e66c28d
4 files changed
+295 -2

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

+293
View File
@@ -0,0 +1,293 @@
# Export Environment Copy Plan
Date: 2026-06-07
## Goal
Make the export dialog copy describe what actually happens in each runtime:
- **Browser/PWA:** MP4 uses the render server; WebM records through browser
`MediaRecorder`.
- **Electron desktop:** MP4 uses the desktop render bridge and bundled ffmpeg;
WebM still records through the Chromium `MediaRecorder` runtime inside
Electron.
The current labels are accurate for the browser build, but misleading inside
Electron:
- `SERVER - FRAME-ACCURATE` on MP4, even when Electron is rendering locally.
- `BROWSER - REALTIME` on WebM, even when the app is running as Electron.
## Current Code
Primary files:
- `src/components/ExportDialog.vue`
- `src/i18n/messages.ts`
- `src/lib/desktopRenderExport.ts`
- `src/types/desktop.d.ts`
Runtime detection already exists as an exported helper in
`desktopRenderExport.ts`:
```ts
export function hasDesktopRender(): boolean {
return !!window.ruwioDesktop?.renderMp4
}
```
This is the right signal for MP4 copy because `ExportDialog.vue` routes MP4
through `renderMp4OnDesktop()` when `hasDesktopRender()` returns true, otherwise
it calls `downloadFromServer()`. The MP4 copy should reuse this exact function
rather than reimplementing the predicate, so the text can never drift from the
route (see Implementation Plan step 1).
`window.ruwioDesktop` is injected by the Electron preload before any app module
loads and never changes during a session, so a runtime check is stable at
module-init time — no reactivity is required to read it correctly.
## Proposed Copy Matrix
### MP4
Browser/PWA:
- Badge: `Server · Frame-by-frame`
- Description: `H.264 export via render server`
- Progress: `Rendering MP4 with render server...`
Electron:
- Badge: `Desktop · Frame-by-frame`
- Description: `H.264 export via bundled ffmpeg`
- Progress: `Rendering MP4 in desktop app...`
Rationale: both MP4 paths render deterministic frames through `server/render.html`.
The key difference is where rendering happens: HTTP render server vs Electron
desktop worker plus ffmpeg.
Avoid `Frame-accurate` in the short badge. It sounds like a stronger quality
claim than the dialog needs, and it obscures the real user-facing distinction:
server vs desktop. The help text can explain frame-by-frame rendering if needed.
Badge style: store badges in title case with a `·` middot separator, matching
the existing sibling badges (`gifBadge: 'Browser · Frame-by-frame'`,
`apngBadge: 'Browser · Lossless'`). The `.badge` CSS already applies
`text-transform: uppercase`, so do **not** write the strings in all-caps or
switch to a `-` hyphen separator — that would render MP4/WebM inconsistently
next to GIF/APNG in the same column.
### WebM
Browser/PWA:
- Badge: `Browser · Realtime`
- Description: `Recorded in real time with MediaRecorder`
- Progress: `Rendering WebM in browser realtime...`
Electron:
- Badge: `Chromium · Realtime`
- Description: `Recorded in real time by Electron's Chromium runtime`
- Progress: `Rendering WebM in Chromium realtime...`
Rationale: the implementation does not become a desktop ffmpeg pipeline in
Electron. It still uses `MediaRecorder` from the renderer process. Calling that
`browser` in the desktop app is imprecise, but `Chromium` is technically true
and short enough for the badge.
## Implementation Plan
1. Add environment booleans in `ExportDialog.vue`.
```ts
import { hasDesktopRender } from '@/lib/desktopRenderExport'
const isDesktop = computed(() => !!window.ruwioDesktop)
const hasDesktopMp4 = computed(() => hasDesktopRender())
```
Use `hasDesktopMp4` for MP4 text. It wraps `hasDesktopRender()` — the *same*
function the MP4 route already calls in `exportMp4Server()` — so the copy
decision and the actual route share one predicate and can never diverge. Do
not reimplement `!!window.ruwioDesktop?.renderMp4` inline.
Use `isDesktop` for WebM text because WebM is still available through renderer
Chromium even if MP4 desktop rendering is unavailable; there is no existing
helper for that distinction, so a local boolean is fine.
2. Rename `exportMp4Server()` to `exportMp4()`.
The current name is now misleading because the function already branches:
- Desktop: `renderMp4OnDesktop(scene, ...)`
- Browser/server: `downloadFromServer(scene, ...)`
3. Replace static MP4 bindings.
Current:
```vue
{{ t('export.mp4Badge') }}
{{ t('export.mp4Desc') }}
```
Proposed:
```vue
{{ t(hasDesktopMp4 ? 'export.mp4DesktopBadge' : 'export.mp4ServerBadge') }}
{{ t(hasDesktopMp4 ? 'export.mp4DesktopDesc' : 'export.mp4ServerDesc') }}
```
4. Replace static WebM bindings.
Current:
```vue
{{ t('export.webmBadge') }}
{{ videoSupported ? t('export.webmDesc') : t('export.webmNotSupported') }}
```
Proposed:
```vue
{{ t(isDesktop ? 'export.webmDesktopBadge' : 'export.webmBrowserBadge') }}
{{
videoSupported
? t(isDesktop ? 'export.webmDesktopDesc' : 'export.webmBrowserDesc')
: t('export.webmNotSupported')
}}
```
5. Split progress labels.
MP4 currently uses:
```ts
label: t('export.labelMp4')
```
Proposed:
```ts
label: t(hasDesktopMp4.value ? 'export.labelMp4Desktop' : 'export.labelMp4Server')
```
WebM currently uses:
```ts
label: t('export.labelWebm')
```
Proposed:
```ts
label: t(isDesktop.value ? 'export.labelWebmDesktop' : 'export.labelWebmBrowser')
```
6. Update `src/i18n/messages.ts` in both locales.
English keys to add:
```ts
mp4ServerBadge: 'Server · Frame-by-frame',
mp4ServerDesc: 'H.264 export via render server',
mp4DesktopBadge: 'Desktop · Frame-by-frame',
mp4DesktopDesc: 'H.264 export via bundled ffmpeg',
webmBrowserBadge: 'Browser · Realtime',
webmBrowserDesc: 'Recorded in real time with MediaRecorder',
webmDesktopBadge: 'Chromium · Realtime',
webmDesktopDesc: "Recorded in real time by Electron's Chromium runtime",
labelMp4Server: 'Rendering MP4 with render server...',
labelMp4Desktop: 'Rendering MP4 in desktop app...',
labelWebmBrowser: 'Rendering WebM in browser realtime...',
labelWebmDesktop: 'Rendering WebM in Chromium realtime...',
```
Badges stay title case with a `·` separator; the `.badge` CSS uppercases
them at render time (see the MP4 copy-matrix note above). German keys should
mirror the meaning rather than preserve the old wording.
7. Fix the export-button help string `exportHelp` in `messages.ts`.
This is separate from the help dialog and is easy to miss. Both locales
currently hardcode the misleading claim:
- `messages.ts:43` (en): "...MP4 needs the render server; WebM records in
real time in the browser."
- `messages.ts:1153` (de): "...MP4 braucht den Render-Server. WebM wird
direkt im Browser in Echtzeit aufgenommen."
Add runtime-branched variants (e.g. `exportHelpServer` / `exportHelpDesktop`)
and select with the same `isDesktop` signal wherever `t('...exportHelp')` is
consumed. This is exactly the "globally visible text saying MP4 always needs
the render server" that must not be left in place.
8. Update help-dialog format text in `messages.ts`, and branch it now.
The strings live at `help.content.export.formats.mp4` / `.webm`
(`messages.ts:1081/1083` en, `:1923/1925` de) and `exportHelp` (handled in
step 7). Browser help should describe:
- MP4 as render-server frame-by-frame export.
- WebM as browser `MediaRecorder` realtime recording.
Electron help should describe:
- MP4 as bundled-ffmpeg desktop frame-by-frame export.
- WebM as Chromium `MediaRecorder` realtime recording.
Wiring the branch is cheaper than the old draft assumed. `HelpDialog.vue`
defines `CHAPTERS: Chapter[]` as a module-level `const` with static
`bodyKey` strings, but `window.ruwioDesktop` is set at preload before that
module loads and never changes, so a plain ternary on the `bodyKey` resolves
correctly at module-init time without making `CHAPTERS` reactive:
```ts
{ bodyKey: window.ruwioDesktop?.renderMp4
? 'help.content.export.formats.mp4Desktop'
: 'help.content.export.formats.mp4' },
{ bodyKey: window.ruwioDesktop
? 'help.content.export.formats.webmDesktop'
: 'help.content.export.formats.webm' },
```
Add the `*Desktop` keys in both locales and wire the branch in the same
change. Do not defer this and leave the help dialog asserting MP4 always
needs the render server.
## Verification
Browser/Vite:
- Open the export dialog.
- MP4 badge renders `SERVER · FRAME-BY-FRAME` (stored title case, uppercased by CSS).
- MP4 description mentions the render server.
- WebM badge renders `BROWSER · REALTIME`.
- WebM description mentions `MediaRecorder`.
- Export-button help (`exportHelp`) and help dialog describe MP4 as a
render-server export.
Electron:
- Open the export dialog.
- MP4 badge renders `DESKTOP · FRAME-BY-FRAME`.
- MP4 description mentions bundled ffmpeg.
- WebM badge renders `CHROMIUM · REALTIME`.
- WebM description mentions Electron Chromium.
- Export-button help (`exportHelp`) and help dialog describe MP4 as a
bundled-ffmpeg desktop export, not the render server.
Build checks:
```bash
npm run build
npm run desktop:dir
```
Optional automated coverage:
- Add a small in-app devtools test that stubs/removes `window.ruwioDesktop` and
verifies the selected i18n keys or rendered badge text. `src/devtools/headless.ts`
already reads `!!window.ruwioDesktop` (line 19) and is the natural home for it.
- Add an Electron harness assertion that export dialog MP4 copy is desktop copy.
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "ruwio",
"private": true,
"version": "1.3.1",
"version": "1.3.2",
"type": "module",
"main": "dist-electron/main.cjs",
"scripts": {
+1 -1
View File
@@ -1,2 +1,2 @@
export const APP_VERSION = '1.3.1'
export const APP_VERSION = '1.3.2'
export const APP_NAME = 'RUWIO'