feat: v9 beta, CJK support, HiDPI fix, browser-native file editing#96
Merged
Conversation
- sdkjs: 7.5.0 → 9.3.0 (word/cell/slide/common; removed pdf/visio) - web-apps: 7.5.0 → 9.3.0 (doc/spreadsheet/presentation editors only; removed mobile/embed/forms/pdfeditor/visioeditor/help/ie/.gz) - x2t WASM: 7.5.0 → 9.3.0+0 (cryptpad build; 55MB → 34MB) Key gains: v9.2 Plugin API expansion (needed for Agent dev), v9.3 PDF API, image/shape hyperlinks, REGEX functions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docs/explorations/: new directory for tracking change explorations - 2026-05-31-onlyoffice-9.3.0-upgrade.md: full process log covering Docker extraction, x2t WASM upgrade, URL fix, version-hash rewrite, and root cause (Document Server vs Desktop Editors build difference) - lib/document-converter.ts: use absolute URL when loading x2t.js - vite.config.ts: Vite plugin to strip OnlyOffice version-hash prefix and return 404 for socket.io polling paths during dev Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New: 2026-05-31-onlyoffice-desktop-mode-mock.md - Two initialization paths: Server vs Desktop mode - AscDesktopEditor mock method list with root cause for each - GetInstallPlugins must return 2 groups (SDK accesses a[0].url/a[1].url) - LocalStartOpen fires per font script — need guard - Common.Gateway event system internals (jQuery-wrapped, no external trigger) - asc_openDocumentFromBytes confirmed working via DevTools - Emscripten WASM heap copy pitfall - api.js patches: ver='', parentOrigin='file://' - AllFonts.js missing from Desktop sdkjs — copy from Docker backup - Remaining: appReady intercept timing for binary injection - Update: upgrade doc status to reflect current progress Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… loading The key discoveries that make this work: - 9.3.0 Shc() is overridden in Desktop mode to call LocalStartOpen instead of processing binary data; clearing AscDesktopEditor temporarily makes it use the original BRj() path that processes the binary directly - Binary must be injected at 500ms after LocalStartOpen (font engine ready) - AllFonts.js replaced with original 7.5.0 version (empty __fonts_files to avoid ascdesktop://fonts/ protocol errors) - api.js patched: ver='' (no hash prefix) and parentOrigin="file://" - Vite plugin injects AscDesktopEditor mock into editor index.html - onlyoffice-editor.ts stores binary in window.__pendingBinary for iframe access Remaining: suppress Connection is lost dialog, fix font loading via ascdesktop:// → /fonts/ redirect. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
….warning intercept - vite.config.ts: add Common.UI.warning intercept in mock to suppress the Connection is lost dialog (which fires when socket.io disconnects) - types/editor.d.ts: add openDocument? method to DocEditor type - Removed XMLHttpRequest intercept for ascdesktop://fonts/ (caused SDK hang) SDK handles CORS failures on ascdesktop:// gracefully; 404 caused hangs Status: document loading works (~1min due to socket.io timeout). Text renders as gray bars pending font loading resolution. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The EIO4 fake handshake caused socket.io to reconnect indefinitely, blocking asc_onCoAuthoringDisconnect from firing. 404 causes socket.io to retry with backoff; after ~60s the SDK fires the disconnect event and document rendering proceeds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document the full working initialization chain, all key discoveries, current status (60s load, gray bars text), and next steps. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- vite.config.ts: XHR prototype patch maps Windows font filenames to our open-source alternatives (Arial→LiberationSans, Times→DejaVuSans, CJK→NotoSans*). Unmapped fonts keep ascdesktop:// → CORS → SDK skips. Common.UI.warning suppression for Connection is lost dialog. - public/sdkjs/common/AllFonts.js: restored to 7.5.0 version (with Windows paths) which correctly triggers LocalStartOpen via font-script loading chain. Our new AllFonts.js broke the initialization order. Status: canvas renders actual content (verified via pixel sampling), 21 fonts load successfully (70MB). Gray bars = CSS skeleton overlay, not actual rendering state. editor:onready fires, document loads. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full record of all findings, mechanisms, pitfalls and current status: - Complete initialization chain with step-by-step flow - Shc() override as the core discovery (Desktop mode ignores binary data) - All critical pitfalls with cause and solution table - Font redirect mechanism (XHR prototype patch + 50+ mappings) - socket.io strategy (404 not fake handshake) - Current status: canvas renders content, 2 UI issues remain - Verification method for confirming canvas has actual content Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Key findings from today's investigation: - asc_nativeOpenFile(N): processes OOXML (docType=2, title changes) but canvas stays black — SetDrawingFreeze(false) not triggered - asc_openDocumentFromBytes(BRj path): triggers font loading (21 fonts) but also needs server for actual content rendering - Wrapping api.asc_openDocumentFromBytes in CreateEditorApi to auto-clear AscDesktopEditor, so every call uses BRj path without manual management - Store original file in window.__pendingOriginalFile for asc_nativeOpenFile - editor:onready → appReady() → parent openDocument → loadBinary chain works - asc_onDocumentContentReady intercept timing: api.ta is null at CreateEditorApi, must wait until LocalStartOpen when ta is initialized Still investigating: asc_onDocumentContentReady not firing after asc_nativeOpenFile / SetDrawingFreeze not called → canvas stays black Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add detailed analysis of why canvas stays black: - BRj path loads fonts but needs server for document content - asc_nativeOpenFile doesn't trigger asc_onDocumentContentReady - SetDrawingFreeze(false) never called → rendering never starts - Next: intercept SetDrawingFreeze and asc_onDocumentContentReady callbacks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gging - vite.config.ts: wrap api.asc_openDocumentFromBytes in CreateEditorApi to auto-clear AscDesktopEditor (BRj path); add LocalFileRecents stub (needed by app:ready); log SetDrawingFreeze/asc_nativeOpenFile intercepts - lib/converter.ts: store original file in window.__pendingOriginalFile before x2t conversion for asc_nativeOpenFile Findings: asc_nativeOpenFile returns docType=2 but never creates WordControl. T_f(ooxml) and wKa.wt(binary) both fail to initialize rendering pipeline. _isDocReady guard blocks SetDrawingFreeze from running on second call. Need to investigate wKa.wt failure mode (font context mismatch?). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WordControl never created despite docType=2
- _isDocReady guard blocks SetDrawingFreeze on second call
- Both T_f(ooxml) and wKa.wt(binary) fail to initialize rendering
- title:button disabled:{} confirms app:ready fired but render engine unlinked
- Next: investigate wKa.wt requirements (ta.Ga initialization)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ec.Ms (Word Control) is the key rendering gate in M_f() - window.Asc.editor uses jre() not qYg() → Ec.Ms never set - Document model loads correctly (aaLen=3) but render context missing - Manual Xxh creation possible but needs more initialization - Best next path: implement minimal socket.io server protocol Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace all ranuts.github.io URLs with bybrowser.com across 9 HTML pages, sitemap, robots.txt, and manifest.json - Expand each page's #seo-content from 1 sentence to 300-500 words: features, how-to steps, use cases, FAQ - Add cross-linking nav at the bottom of every sub-page (related tools + home) - Add SoftwareApplication JSON-LD structured data to all 9 pages - Update manifest name/description to ByBrowser branding Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- zh-cn/ subdirectory: 9 Chinese pages (homepage + 8 tool pages) with full translated content, hreflang zh-Hans, canonical URLs - sitemap.xml: expanded to 18 URLs with xhtml:link alternate pairs - vite.config.ts: added 9 zh-cn build entries + injectGtag plugin (GA G-VQCV194W8Q injected into all HTML at build time, skipped in dev) - lib/i18n.ts: /zh-cn/ path detection as priority-0 language signal - lib/loading.ts: replaced spinner with staged progress bar (auto-advances through 4 stages; setProgress() for milestone hooks) - lib/document.ts + events.ts: call setProgress at initX2T/handleDoc milestones - lib/ui.ts: ad slot infrastructure — landing page ad unit between hero and feature cards; editor-mode bottom strip shown on editor-open - styles/base.css: ad-unit, editor-ad-strip, loading-overlay CSS - All EN pages: hreflang + zh-Hans alternate already committed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract GitHub/Issues/lang-switch into fixed `.landing-nav` (position:fixed, 52px, z-index 1001) - Remove `.lang-bar` from landing-shell; no more content-dependent positioning - Adjust `.landing-shell` padding-top to 94px (nav height + gap) so content never shifts - Change `.landing-hero` from align-items:end to center; remove min-height:56vh - Add all UI strings to i18n.ts; replace ternary patterns with t() calls throughout ui.ts - Inject critical CSS via Vite plugin to eliminate FOUC on language switch - Add GA4 (G-VQCV194W8Q) via prod-only Vite plugin; create zh-cn/ pages and sitemap hreflang Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Show current language in picker (English/中文) instead of alternate. Extensible: adding a new locale is one option element + getLangUrl branch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lib/ store/ styles/ types/ index.ts → src/* Update all 18 HTML entry points to reference ../src/index.ts Update tsconfig path aliases to point into src/ URL routes unchanged; HTML page dirs stay at root. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All HTML entry points moved into pages/ to separate route definitions from source code. URLs unchanged — Vite root: 'pages' serves them at the same paths as before (/docx-editor/, /zh-cn/, etc.). - pages/ HTML page directories (18 entries, EN + zh-cn) - src/ TypeScript source, styles, types - public/ Static assets - Fix vite resolve.alias to point into src/ (missed in prior commit) - Add server.fs.allow so Vite can resolve src/ outside root Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 7.5 build is custom-compiled (build:1), not from any installer - Desktop Editors 9.x also uses server architecture (code.js exists) - Architecture changed somewhere between 7.x and 8.0 - Desktop Mode Mock abandoned: Ec.Ms init path unreachable in mock env - Document three paths forward (compile from source / socket.io server / stay on 7.5) - Mark current sdkjs/web-apps (Docker 9.3.0) as breaking — needs rollback Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All 9.x releases (Docker, macOS DMG, Linux tarball) confirmed to use server architecture (code.js present, app.js ~2MB). No offline path exists. Key findings: - Linux Desktop Editors 9.3.0 tarball verified: code.js exists - build_tools --desktop flag no longer produces offline bundle in 9.x - Architecture changed between 7.x and 8.x (exact version TBD) - 7.5 build:1 was hand-compiled in the old standalone era Three paths documented: maintain 7.5 / socket.io mock server / find last offline version Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
关键发现(通过 Desktop tarball 9.3.0 二进制分析): - code.js(1.1MB):0 socket 引用,是纯 UI 懒加载包,与服务器无关 - socket.io 协议实现在 sdk-all-min.js(sdkjs 渲染引擎) - sdk-all-min.js 同时包含 202 个 AscDesktopEditor 引用 → Desktop 模式有完整独立路径 - Desktop 路径:nativeOpenFile → jre() → AscCommonWord.e2(ta.Ga)→ 不经过 socket.io 修正之前 Desktop Mode Mock 失败的真实原因: - 当时使用了 Docker 服务端 sdkjs(非 Desktop 构建) - 从未调用 asc_nativeOpenFile 传入实际文档数据 - Ec.Ms 是 PDF viewer 路径,Word 编辑器用 ta.Ga 新增路径 C(v8.1.1 最后单体包版本)和路径 D(Desktop tarball + 正确 mock) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ument API
- Remove parentOrigin: 'file://' (was for Desktop mock path, no longer needed)
- Replace openDocument() call (9.3.0+ only) with sendCommand({ command: 'asc_openDocument' })
- Remove onlyofficeDesktopMock() plugin from vite.config.ts (caused uitheme crash in 7.4.1)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- v8.1.1 also requires socket.io (tested: /doc/{key}/c/?EIO=4 → 404)
- socket.io was introduced before v8.1.1, not between 8.1.1 and 8.2.0
- Update architecture timeline with 8.x section
- Mark Path C as failed with test evidence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Inject __IS_BETA__, __STABLE_URL__, __BETA_URL__ at build time via Vite define so the version selector uses port-based URLs in dev mode (5173↔5174) and path-based URLs in production (/9.3.0/). Also fix seo-pages test to read sitemap.xml from public-beta/ after the public→public-beta rename. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename public-stable/ → public-v7/, public-beta/ → public-v9/ - Rename vite.stable.config.ts → vite.v7.config.ts - Extract vite.shared.ts: rollupInputs, sharedAlias, DEV_URLS, PROD_PATHS (eliminates 18-entry duplication across version configs) - Scripts: dev:v7 / dev:v9 (versioned); root `dev` defaults to v7 (stable) - Fix seo-pages test to read shared config for page entry assertions Adding a new OnlyOffice version now only requires a new vite.vN.config.ts that calls the shared helpers — no copy-pasting 70 lines of boilerplate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
x2t converts GIF media to PNG during PPTX→PPSY conversion, stripping animation. Two-layer fix in packages/editor-v7: 1. readMediaFiles(): set correct MIME type on each Blob (was missing, causing browsers to treat GIF data as binary). 2. For PPTX, pre-extract all media from the original ZIP (via the same docx-zip.ts ZIP parser already used in v9). After x2t conversion, merge media maps so that any x2t-output PNG whose basename matches an original GIF serves the GIF blob URL instead — preserving animation even when x2t renames image1.gif → image1.png. Bug 2 (animation sequences merged into one click) is an x2t WASM limitation that cannot be fixed at the JavaScript layer in v7. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The fixed 60px ad bar overlapped the bottom of the OnlyOffice iframe in all document types (pptx/docx/xlsx). Remove the element, its DOM creation, show/hide logic, and all associated CSS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- v9: Fix GetSupportedScaleValues returning [] which disabled all DPR correction, causing blurry icons (#15), cursor offset (#12), and Excel cursor not moving (#92). Now returns [1, 1.25, ... 5]. - v7: Add minimal SmartArts.bin stub (5 bytes) so SDK no longer 404s on SmartArt shapes; shapes don't render yet (no data) but the editor loads cleanly (#20 partial). - Docs: Add exploration records for PPTX GIF analysis and this fix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sages - Load DejaVuSans.ttf and LiberationSans-Regular.ttf into /working/fonts/ in the x2t WASM filesystem before PDF conversion. Without fonts, x2t generates a valid but text-invisible PDF (#28). - Fonts are fetched from /fonts/ at first PDF export and cached in-session. - Add human-readable hints for known x2t error codes: code 88 → .doc binary format / password-protected / corrupted (#49) code 55 → DRM/encrypted code 1 → invalid/corrupted file Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ign text) v7 SDK requests fonts via ascdesktop://fonts/ XHR scheme, which silently fails in pure browser environment — causing CJK chars and calibri metrics to be missing. - Add public-v7/font-map.json with CJK→NotoSansSC-VF and Latin→Liberation mappings - Add public-v7/onlyoffice-v7-iframe-patch.js that patches XMLHttpRequest.prototype.open to rewrite ascdesktop://fonts/<file> → /fonts/<mapped> - Inject the patch script into all three v7 editor iframe HTML <head> elements - Add fontRemapMiddleware() to vite.v7.config.ts for HTTP-layer font remap (fixes split-brain: FreeType loads DejaVuSans but HarfBuzz shapes with CJK font) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v7 SDK expects a single SmartArts.bin (151 SmartArt types concatenated with an index header). v9 switched to per-type directory structure (SmartArtData/*.bin). The numeric type IDs are identical between v7 and v9 (both use 0-150 sequentially). Built a valid SmartArts.bin by: 1. Mapping v9 Db enum (propName → numeric ID) against v9 d-dict (propName → filename) 2. Loading all 151 SmartArtData/*.bin files in type_id order 3. Writing: [1B version][4B boundary_marker=756][151×(1B id + 4B offset)][data blobs] Result: 7.8MB file covering all 151 SmartArt layout types. SmartArt shapes should now render in v7 editors (previously 404 + 5-byte stub). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v7 cell SDK in Chrome/macOS requests fonts via Windows absolute paths (c:\Windows\Fonts\<file>) rather than the ascdesktop://fonts/ scheme documented in the source. The previous XHR interceptor only handled the latter, leaving all 19 font requests to fail with ERR_FAILED. Add a /^[a-zA-Z]:[\/\\]/ regex branch to detect and remap Windows drive-letter paths. Also change FALLBACK from DejaVuSans.ttf to NotoSansSC-VF.ttf so unmapped fonts still render CJK text. Verified in browser: Deng.ttf → NotoSansSC-VF.ttf, calibri.ttf → LiberationSans-Regular.ttf — all 19 requests return HTTP 200/304. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ybrid approach - Introduced app-router module to handle routing logic and startup actions. - Refactored index.ts to utilize new routing logic for document creation and opening. - Updated document.ts to manage local file routes and handle document operations. - Removed unused UI components and callbacks related to menu guide. - Enhanced event handling for editor state transitions. - Added comprehensive documentation for routing design and implementation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- editor-file-lost branch now calls registerLocalFilePopstate so the Back button works correctly after re-opening a local file from the recovery screen - fontsLoaded is only set true when at least one font is actually written to WASM FS, preventing silent PDF invisible-text on network failure - fontsLoaded is reset to false in doInitialize() catch so fonts are reloaded into the fresh FS after a WASM reinit retry (v7 + v9) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- onOpenDocument now rolls back the URL via history.replaceState when
initX2T/handleDocumentOperation fails, preventing a permanent mismatch
between the address bar (/docx/?file=…) and the visible home panel
- fontsLoaded is now only set true when ALL fonts load successfully;
a partial failure (e.g. NotoSansSC 404) no longer permanently blocks
CJK text in subsequent PDF exports (v7 + v9)
- editor-new case suppresses the re-thrown rejection from onCreateNew
with .catch(() => {}) to avoid an unhandledrejection browser event
- getVersionPrefix() now uses a semver regex instead of hardcoding
'/9.3.0/', so it survives the planned v9.4.0 upgrade automatically
- ?file= with an empty value now correctly returns editor-file-lost
instead of editor-new (null check instead of falsy check)
- extract shared destroyEditor const in index.ts to remove duplicate
closure in home and editor-file-lost switch branches
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Running `prettier --write .` from the repo root scanned OnlyOffice vendor minified JS under apps/web/public-v7/ and apps/web/public-v9/, which caused OOM or reformatted minified files. The root .prettierignore already excluded `public/` but not the versioned directories. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pnpm's frozen-lockfile validation now rejects '*' as a range for pre-release versions like 0.1.0-alpha-23 (semver '*' only matches stable releases). Pin editor-v7 and editor-v9 peerDependencies to the same versions already used by apps/web to fix CI install failure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ditor-v7 Sync lockfile specifiers from '*' to exact pre-release versions (ranui@0.1.10-alpha-27, ranuts@0.1.0-alpha-23) so that `pnpm install --frozen-lockfile` passes in CI. Also bumps apps/web vite to ^8.0.16. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- vite: bump ^8.0.14 → ^8.0.16 (fixes high server.fs.deny bypass on Windows alternate paths #15, medium NTLMv2 hash disclosure #16) - undici: add override in pnpm-workspace.yaml to force >=7.28.0 for all consumers (fixes high TLS cert bypass #17, medium cross-user info disclosure #18); undici comes from jsdom via vitest (dev-only), the pnpm.overrides in package.json had no effect — workspace-level overrides in pnpm-workspace.yaml is the correct mechanism for pnpm 9+ - add undici ^7.28.0 as direct devDependency to make the constraint explicit alongside the workspace override Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`pnpm exec` at repo root can't find playwright because @playwright/test is only in apps/web. Use --filter so pnpm resolves the binary from the correct workspace. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getCookie() accesses document.cookie and getUrlParameter() accesses window.location via ranuts utilities. When Playwright imports @bybrowser/editor in Node.js (for onlyoffice-new-document.spec.ts), the module-level `new I18n()` call crashes with 'ReferenceError: document is not defined'. Add typeof guards so both methods return null safely in SSR/Node.js environments. Co-Authored-By: Claude Sonnet 4.6 <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.
No description provided.