diff --git a/README.md b/README.md index 016b00be78..b1a14e6164 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,9 @@ :kr: + + :bangladesh: +
diff --git a/docs/i18n/README-ar.md b/docs/i18n/README-ar.md index ea2f0ccf84..3a7f044a1b 100644 --- a/docs/i18n/README-ar.md +++ b/docs/i18n/README-ar.md @@ -90,6 +90,9 @@ :kr: + + :bangladesh: +
diff --git a/docs/i18n/README-bn.md b/docs/i18n/README-bn.md new file mode 100644 index 0000000000..991d115142 --- /dev/null +++ b/docs/i18n/README-bn.md @@ -0,0 +1,201 @@ +

MarkText

+ +

MarkText

+ +
+ + twitter + +
+
+ :high_brightness: আধুনিক প্রজন্মের Markdown এডিটর :crescent_moon:
+ একটি সাধারণ এবং অত্যাধুনিক ওপেন-সোর্স Markdown এডিটর যা গতি এবং ব্যবহারযোগ্যতার দিকে গুরুত্ত দেয়।
+ Linux, macOS এবং Windows-এর জন্য। +
+ +
+ +
+ + + LICENSE + + + + total download + + + + latest download + + + + sponsors + +
+ +
+

+ + Website + + | + + বৈশিষ্ট্য + + | + + ডাউনলোড + + | + + ডেভেলপমেন্ট + + | + + অবদান + +

+
+ +
+ অনুবাদসমূহ: + + :uk: + + + :cn: + + + :taiwan: + + + :de: + + + :jp: + + + :fr: + + + :tr: + + + :es: + + + :portugal: + + + :kr: + + + :saudi_arabia: + +
+ +
+ এই Markdown এডিটরটি ❤︎ দিয়ে তৈরি করেছেন + Jocs এবং + + অবদানকারীরা + + । + +
+ +
+ +

স্পনসর

+ +MarkText একটি ওপেন-সোর্স Markdown এডিটর, যা এর কমিউনিটির সহায়তায় এগিয়ে চলছে। MarkText যদি আপনার কাজের ধারা উন্নত করে থাকে, তাহলে অনুগ্রহ করে [প্রজেক্টটিকে স্পনসর করার](https://github.com/sponsors/marktext) কথা বিবেচনা করুন। সব স্পনসরকে ধন্যবাদ ❤️ + +**বিশেষ স্পনসর** + +| [](https://serpapi.com/?utm_source=marktext) | [আমাদের দ্রুত, সহজ এবং পূর্ণাঙ্গ API দিয়ে Google ও অন্যান্য সার্চ ইঞ্জিন থেকে ফলাফল সংগ্রহ করুন।](https://serpapi.com/?utm_source=marktext) | +| ------------- |:-------------| + +## স্ক্রিনশট + +![](../assets/marktext.png?raw=true) + +## বৈশিষ্ট্য + +- রিয়েলটাইম প্রিভিউ (WYSIWYG) এবং পরিচ্ছন্ন, আধুনিক ইন্টারফেস আপনাকে গভীর মনোযোগের সাথে লেখার অভিজ্ঞতা দেয়। +- [CommonMark Spec](https://spec.commonmark.org), [GitHub Flavored Markdown Spec](https://github.github.com/gfm/) এবং আংশিকভাবে [Pandoc markdown](https://pandoc.org/MANUAL.html#pandocs-markdown) সমর্থন করে। +- Markdown এক্সটেনশন যেমন গণিতের অভিব্যক্তি (KaTeX), front matter এবং emoji সমর্থন করে। +- দ্রুত লেখার জন্য paragraph ও inline style shortcut সমর্থন করে। +- **HTML** এবং **PDF** ফাইলে এক্সপোর্ট করা যায়। +- বিভিন্ন [theme](https://marktext.me/docs/themes): **Cadmium Light**, **Material Dark** ইত্যাদি। +- বিভিন্ন এডিটিং মোড: **Source Code mode**, **Typewriter mode**, **Focus mode**। +- ক্লিপবোর্ড থেকে সরাসরি ছবি পেস্ট করা যায়। + +## ডাউনলোড ও ইনস্টলেশন + +![platform](https://img.shields.io/static/v1.svg?label=Platform&message=Linux%20x64%20|%20macOS%20x64%2Farm64%20|%20Windows%20x64%2Farm64&style=for-the-badge) + +| ![](https://raw.githubusercontent.com/wiki/ryanoasis/nerd-fonts/screenshots/v1.0.x/mac-pass-sm.png) | ![](https://raw.githubusercontent.com/wiki/ryanoasis/nerd-fonts/screenshots/v1.0.x/windows-pass-sm.png) | ![](https://raw.githubusercontent.com/wiki/ryanoasis/nerd-fonts/screenshots/v1.0.x/linux-pass-sm.png) | +|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| +| [![Download for macOS](https://img.shields.io/badge/macOS-Download-blue)](https://github.com/marktext/marktext/releases/latest) | [![Download for Windows](https://img.shields.io/badge/Windows-Download-blue)](https://github.com/marktext/marktext/releases/latest) | [![Download for Linux](https://img.shields.io/badge/Linux-Download-blue)](https://github.com/marktext/marktext/releases/latest) | + +সর্বশেষ সংস্করণের নতুন ফিচারগুলো দেখতে [CHANGELOG](https://marktext.me/docs/changelog) দেখুন। + +#### macOS + +macOS 11 (Big Sur) বা পরবর্তী সংস্করণ প্রয়োজন। Universal build প্রকাশ করা হয় না, তাই আপনার মেশিনের সঙ্গে মিল থাকা `arm64` বা `x64` installer বেছে নিন। + +আপনি [release page](https://github.com/marktext/marktext/releases/latest) থেকে সর্বশেষ `marktext-mac-(arm64|x64)-%version%.dmg` ডাউনলোড করতে পারেন, অথবা [**homebrew cask**](https://github.com/caskroom/homebrew-cask) ব্যবহার করে MarkText ইনস্টল করতে পারেন। Homebrew-Cask ব্যবহার করতে হলে আগে [Homebrew](https://brew.sh/) ইনস্টল করা থাকতে হবে। + +```bash +brew install --cask mark-text +``` + +#### Windows + +Windows 10 অথবা 11 প্রয়োজন। x64 এবং arm64 - উভয় installer প্রকাশ করা হয়, তাই আপনার মেশিনের architecture অনুযায়ীটি বেছে নিন। + +শুধু setup wizard (`marktext-win-(x64|arm64)-%version%-setup.exe`) ডাউনলোড করে ইনস্টল করুন এবং user-only নাকি machine-wide ইনস্টল করবেন তা বেছে নিন। বিকল্পভাবে, [Chocolatey](https://chocolatey.org/) বা [Winget](https://docs.microsoft.com/en-us/windows/package-manager/winget/) এর মতো package manager ব্যবহার করেও MarkText ইনস্টল করতে পারেন। + +Chocolatey ব্যবহার করতে চাইলে আগে [Chocolatey](https://chocolatey.org/install) ইনস্টল করা থাকতে হবে: + +```bash +choco install marktext +``` + +Winget ব্যবহার করতে চাইলে আগে [Winget](https://docs.microsoft.com/en-us/windows/package-manager/winget/#install-winget) ইনস্টল করা থাকতে হবে: + +```bash +winget install marktext +``` + +#### Linux + +অনুগ্রহ করে [Linux installation instructions](https://marktext.me/docs/installation) অনুসরণ করুন। + +#### অন্যান্য + +Linux, macOS এবং Windows-এর সব binary [release page](https://github.com/marktext/marktext/releases/latest) থেকে ডাউনলোড করা যাবে। আপনার সিস্টেমের জন্য কোনো সংস্করণ না থাকলে একটি [issue](https://github.com/marktext/marktext/issues) খুলুন। + +## ডেভেলপমেন্ট + +আপনি যদি নিজে MarkText build করতে চান, তাহলে আমাদের [build instructions](https://marktext.me/docs/dev/build) দেখুন। + +- [ব্যবহারকারী ডকুমেন্টেশন](https://marktext.me/docs/introduction) +- [ডেভেলপার ডকুমেন্টেশন](https://marktext.me/docs/dev/overview) + +MarkText সম্পর্কে আপনার কোনো প্রশ্ন থাকলে issue খুলতে পারেন। Issue খোলার সময় অনুগ্রহ করে ডিফল্ট ফরম্যাটটি ব্যবহার করুন। অবশ্যই, সরাসরি PR পাঠালে সেটিও অত্যন্ত প্রশংসিত হবে। + +## অবদান + +MarkText এখনো উন্নয়নাধীন, তাই pull request করার আগে [Contributing Guide](../../.github/CONTRIBUTING.md) পড়ে নিন। MarkText-এ নতুন ফিচার যোগ করতে চান? আমাদের [roadmap](https://github.com/marktext/marktext/projects) এবং open issue দেখুন। + +## অবদানকারীরা + +যারা ইতিমধ্যেই MarkText-এ অবদান রেখেছেন, তাদের সবাইকে ধন্যবাদ [[contributors](https://github.com/marktext/marktext/graphs/contributors)]। + + + +## লাইসেন্স + +[**MIT**](../../LICENSE)। diff --git a/docs/i18n/README-de.md b/docs/i18n/README-de.md index 0ca1d68fdd..c5acaad42b 100644 --- a/docs/i18n/README-de.md +++ b/docs/i18n/README-de.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/docs/i18n/README-es.md b/docs/i18n/README-es.md index 4c4f0b8afe..7780e76563 100644 --- a/docs/i18n/README-es.md +++ b/docs/i18n/README-es.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/docs/i18n/README-fr.md b/docs/i18n/README-fr.md index 27be6e8e80..98180a1549 100644 --- a/docs/i18n/README-fr.md +++ b/docs/i18n/README-fr.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/docs/i18n/README-jp.md b/docs/i18n/README-jp.md index 4394d0b26e..768910774c 100644 --- a/docs/i18n/README-jp.md +++ b/docs/i18n/README-jp.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/docs/i18n/README-kr.md b/docs/i18n/README-kr.md index 676c72e3c3..4750cefa29 100644 --- a/docs/i18n/README-kr.md +++ b/docs/i18n/README-kr.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/docs/i18n/README-pt.md b/docs/i18n/README-pt.md index 4f6af02f12..c0dc805f2e 100644 --- a/docs/i18n/README-pt.md +++ b/docs/i18n/README-pt.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/docs/i18n/README-tr.md b/docs/i18n/README-tr.md index 3ed94b8782..bdefccbbf0 100644 --- a/docs/i18n/README-tr.md +++ b/docs/i18n/README-tr.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/docs/i18n/README-zh_cn.md b/docs/i18n/README-zh_cn.md index b8202f1157..c2e64e3161 100644 --- a/docs/i18n/README-zh_cn.md +++ b/docs/i18n/README-zh_cn.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/docs/i18n/README-zh_tw.md b/docs/i18n/README-zh_tw.md index 2fdb91fb71..000fed1c99 100644 --- a/docs/i18n/README-zh_tw.md +++ b/docs/i18n/README-zh_tw.md @@ -90,6 +90,9 @@ :saudi_arabia: + + :bangladesh: +
diff --git a/packages/muya/src/history/__tests__/identityOpJsonChange.spec.ts b/packages/muya/src/history/__tests__/identityOpJsonChange.spec.ts new file mode 100644 index 0000000000..55e9f6a26e --- /dev/null +++ b/packages/muya/src/history/__tests__/identityOpJsonChange.spec.ts @@ -0,0 +1,77 @@ +// @vitest-environment happy-dom + +// Regression for #4806: an identity (`null`) ot-json1 op must never crash +// History. `compose()` legitimately yields `null` when queued ops cancel out +// (e.g. IME edits), and both the deferred-flush emitter and the `dispatch` +// path can surface it through `json-change`. `History` reads `op.length`, so a +// forwarded `null` threw `Cannot read properties of null (reading 'length')`. + +import * as json1 from 'ot-json1'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { Muya } from '../../muya'; +import { asDoc } from '../../state'; + +const bootedHosts: HTMLElement[] = []; + +beforeEach(() => { + window.MUYA_VERSION = 'test'; +}); + +afterEach(() => { + while (bootedHosts.length) + bootedHosts.pop()!.remove(); + document.getSelection()?.removeAllRanges(); + delete (window as Partial).MUYA_VERSION; +}); + +function bootMuya(markdown: string): Muya { + const host = document.createElement('div'); + document.body.appendChild(host); + const muya = new Muya(host, { markdown } as ConstructorParameters[1]); + muya.init(); + bootedHosts.push(muya.domNode); + return muya; +} + +function undoDepth(muya: Muya): number { + // @ts-expect-error — reach into the private stack for test assertions. + return muya.editor.history._stack.undo.length; +} + +describe('identity (null) op via json-change — #4806', () => { + it('updateContents(null) does not crash History and records nothing', () => { + const muya = bootMuya('hello\n'); + + // `Editor.updateContents` deliberately forwards the identity op to + // `dispatch`, which emits a `json-change` carrying `op: null`. + expect(() => muya.editor.updateContents(null, null, 'user')).not.toThrow(); + + expect(muya.getMarkdown().trim()).toBe('hello'); + expect(undoDepth(muya)).toBe(0); + }); + + it('flushing a compose-to-null batch is a no-op (locks #4815)', () => { + const muya = bootMuya('hello\n'); + const jsonState = muya.editor.jsonState; + + // A real op and its exact inverse compose to the identity op — the + // op-level shape of an IME insert cancelled by a delete in one frame. + const op = json1.insertOp([1], asDoc({ name: 'paragraph', text: 'x' } as never))!; + const inverse = json1.type.invert(op); + expect(json1.type.compose(op, inverse)).toBe(null); + + let changes = 0; + muya.eventCenter.on('json-change', () => { + changes += 1; + }); + + // @ts-expect-error — drive the private deferred-flush path directly. + jsonState._operationCache.push(op, inverse); + // @ts-expect-error — the function the crash stack named. + expect(() => jsonState._flushOperationCache()).not.toThrow(); + + expect(changes).toBe(0); + expect(muya.getMarkdown().trim()).toBe('hello'); + expect(undoDepth(muya)).toBe(0); + }); +}); diff --git a/packages/muya/src/history/index.ts b/packages/muya/src/history/index.ts index e7033fc523..cce428ce57 100644 --- a/packages/muya/src/history/index.ts +++ b/packages/muya/src/history/index.ts @@ -130,7 +130,7 @@ class History { source, prevDoc, }: { - op: JSONOpList; + op: Nullable; source: string; prevDoc: TState[]; doc: TState[]; @@ -138,6 +138,13 @@ class History { if (this._ignoreChange) return; + // The identity op (`null`) carries no change to record or + // transform. It can still reach here through `json-change` when + // queued ops compose away (e.g. IME edits, #4806); `_record` + // would otherwise crash reading `op.length`. + if (op == null) + return; + if (!this._options.userOnly || source === 'user') this._record(op, prevDoc); else diff --git a/packages/muya/src/state/index.ts b/packages/muya/src/state/index.ts index d7515f1b4f..39bcdf7bf6 100644 --- a/packages/muya/src/state/index.ts +++ b/packages/muya/src/state/index.ts @@ -272,8 +272,9 @@ class JSONState { // (acc, current, index, array) to the callback, but // `json1.type.compose` only accepts (op1, op2). Without the // wrapper TS rejects the signature mismatch. - // `compose` returns JSONOp (= null | JSONOpList); a non-empty cache - // (guarded above) always composes to a non-null op. + // `compose` returns JSONOp (= null | JSONOpList). Multiple queued + // operations may cancel each other out (for example during IME + // composition), producing the identity operation (`null`). const op = this._operationCache.reduce( (acc, curr) => json1.type.compose(acc, curr) as JSONOpList, ); @@ -284,6 +285,10 @@ class JSONState { // Clear before emitting: a listener that edits synchronously then starts // a fresh batch instead of mutating the one being flushed. this._operationCache = []; + + if (op === null) + return; + this._muya.eventCenter.emit('json-change', { op, source: 'user',