[
  {
    "path": ".eslintignore",
    "content": "apps/benchmark"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  parser: '@typescript-eslint/parser',\n  parserOptions: {\n    sourceType: 'module',\n  },\n  extends: [\n    'plugin:@typescript-eslint/recommended',\n    'plugin:prettier/recommended',\n    'prettier',\n  ],\n  rules: {\n    'no-unused-vars': ['error', { varsIgnorePattern: '.*', args: 'none' }],\n    'no-restricted-syntax': ['error', 'ObjectPattern > RestElement'],\n    '@typescript-eslint/no-non-null-assertion': 'off',\n  },\n  overrides: [\n    {\n      files: ['./**/__tests__/**'],\n      rules: {\n        'no-restricted-globals': 'off',\n        'no-restricted-syntax': 'off',\n      },\n    },\n  ],\n};\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: Gumball12\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: Gumball12\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: Question\nabout: Ask and answer\ntitle: ''\nlabels: question\nassignees: Gumball12\n\n---\n\n**Please write questions about the project**\n"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "content": "name: ci\n\non:\n  push:\n  pull_request:\n    branches:\n      - main\n\njobs:\n  ci:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - uses: pnpm/action-setup@v4.2.0\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: 'https://registry.npmjs.org'\n          cache: pnpm\n          cache-dependency-path: pnpm-lock.yaml\n\n      - run: pnpm install --frozen-lockfile\n\n      - run: pnpm run lint\n      - run: pnpm run build\n      - run: pnpm run test\n\n      - uses: codecov/codecov-action@v4\n        with:\n          token: ${{ secrets.CODECOV_TOKEN }}\n          directory: ./packages/text-vide/coverage\n          fail_ci_if_error: true\n          verbose: true\n"
  },
  {
    "path": ".github/workflows/publish-sandbox.yaml",
    "content": "name: publish-sandbox\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  publish-sandbox:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - uses: pnpm/action-setup@v4.2.0\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: 'https://registry.npmjs.org'\n          cache: pnpm\n          cache-dependency-path: pnpm-lock.yaml\n\n      - run: pnpm install --frozen-lockfile\n\n      - run: pnpm run build\n\n      - name: Deploy\n        uses: peaceiris/actions-gh-pages@v3.9.3\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_dir: ./apps/sandbox/dist\n"
  },
  {
    "path": ".github/workflows/publish.yaml",
    "content": "name: publish\n\non:\n  release:\n    types: [created]\n\npermissions:\n  id-token: write\n  contents: read\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - uses: pnpm/action-setup@v4.2.0\n      - uses: actions/setup-node@v6\n        with:\n          node-version: 22\n          registry-url: 'https://registry.npmjs.org'\n          cache: pnpm\n          cache-dependency-path: pnpm-lock.yaml\n\n      - run: npm i -g npm@latest\n      - run: pnpm install --frozen-lockfile\n\n      - run: pnpm run lint\n      - run: pnpm run test\n      - run: pnpm run build\n\n      - run: |\n          cp ./README.md ./packages/text-vide/README.md &&\n          cd ./packages/text-vide &&\n          npm publish\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.DS_STORE\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# TypeScript v1 declaration files\ntypings/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n\n# Next.js build output\n.next\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and *not* Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# turbo\n.turbo\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx lint-staged\n"
  },
  {
    "path": ".nvmrc",
    "content": "v22.12.0"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"trailingComma\": \"all\",\n  \"singleQuote\": true,\n  \"printWidth\": 80,\n  \"arrowParens\": \"avoid\",\n  \"endOfLine\": \"auto\"\n}"
  },
  {
    "path": "ABOUT_READABILITY.md",
    "content": "# About Readability\n\nBionic-Reading is a technique still under study (see `Statements` at the top of [BR Document](https://bionic-reading.com/) or the photo below). So it seems to some people that it doesn't work (or rather it's less readability), but I think it's going to improve over time.\n\n![study-results](./docs/study-results.png)\n\nAnd... I'm not the designer of Bionic-Reading, nor am I the researcher involved. So there's nothing I can say about this.\n\nI'm sorry if I didn't help you. Thank you for reading it.\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.\n\n### [1.8.5](https://github.com/Gumball12/text-vide/compare/v1.8.4...v1.8.5) (2026-03-29)\n\n\n### Bug Fixes\n\n* **security:** upgrade handlebars to 4.7.9 via pnpm overrides ([304c78d](https://github.com/Gumball12/text-vide/commit/304c78dec05ae2725178d378eff4772cc16a8341))\n\n### [1.8.2](https://github.com/Gumball12/text-vide/compare/v1.8.1...v1.8.2) (2025-03-17)\n\n### [1.8.1](https://github.com/Gumball12/text-vide/compare/v1.8.0...v1.8.1) (2023-11-09)\n\n## [1.8.0](https://github.com/Gumball12/text-vide/compare/v1.7.0...v1.8.0) (2023-11-09)\n\n\n### Features\n\n* add ignoreHtmlEntity option ([#46](https://github.com/Gumball12/text-vide/issues/46)) ([6fbef3d](https://github.com/Gumball12/text-vide/commit/6fbef3d367cbb6e8b0a3f1028ce2c2a8b91c19b2))\n\n## [1.7.0](https://github.com/Gumball12/text-vide/compare/v1.6.2...v1.7.0) (2023-04-03)\n\n### [1.6.2](https://github.com/Gumball12/text-vide/compare/v1.6.1...v1.6.2) (2023-03-27)\n\n### [1.6.1](https://github.com/Gumball12/text-vide/compare/v1.6.0...v1.6.1) (2023-02-13)\n\n## [1.6.0](https://github.com/Gumball12/text-vide/compare/v1.5.0...v1.6.0) (2022-08-06)\n\n\n### Features\n\n* add benchmark tests ([977e008](https://github.com/Gumball12/text-vide/commit/977e0087b4670de2196e68574d697f13683d4fb1))\n* add ignoreHtmlTag option ([c209f8c](https://github.com/Gumball12/text-vide/commit/c209f8cfffff4f61c12727ccbe02fb19d4f95c67))\n* add mb-2px to Input paragraph ([f3f7160](https://github.com/Gumball12/text-vide/commit/f3f71605ea735ac1111c46ee1ff5cff2566cb8d7))\n* make the utils directory into the utils package ([45e34a3](https://github.com/Gumball12/text-vide/commit/45e34a37eb1517114e12f1553eedf902c107b335))\n\n## [1.5.0](https://github.com/Gumball12/text-vide/compare/v1.4.0...v1.5.0) (2022-06-04)\n\n\n### Features\n\n* add logo ([befd0d6](https://github.com/Gumball12/text-vide/commit/befd0d6029b88361765bdc12d1d3d17dda89b96d))\n\n## [1.4.0](https://github.com/Gumball12/bionic-reading/compare/v1.3.2...v1.4.0) (2022-05-28)\n\n\n### Features\n\n* implement fixationPoint option field ([53697f4](https://github.com/Gumball12/bionic-reading/commit/53697f43d8c291890f1d496e2a7fb2ba9c160106))\n* implement getOptions() ([100cb94](https://github.com/Gumball12/bionic-reading/commit/100cb94f83ecf181d04c1e4f67fc8ca0f07cd766))\n* implement isEmpty util function ([9450ac9](https://github.com/Gumball12/bionic-reading/commit/9450ac9a67b23248d2254e6120de8ae6047acf57))\n* implement isNumberString util function ([928c11b](https://github.com/Gumball12/bionic-reading/commit/928c11bf1e7df2fda56f74dbfcaa0190cc1444be))\n* improve readability ([5b2789a](https://github.com/Gumball12/bionic-reading/commit/5b2789ab42997ef52cb06a4a7b1e78a65fbed9c1))\n* improve sandbox ([2add790](https://github.com/Gumball12/bionic-reading/commit/2add7906eb040bbe81d90d6be88f29b1993247e2))\n* migrate to react ([1f17c36](https://github.com/Gumball12/bionic-reading/commit/1f17c369cdf2cfc558beada554242ec7d24adab7))\n\n\n### Bug Fixes\n\n* fix footer style ([f9aa436](https://github.com/Gumball12/bionic-reading/commit/f9aa43689e86f102ba31978fced6a397e44f08ec))\n* fix incorrect rendering for numbers ([2308eea](https://github.com/Gumball12/bionic-reading/commit/2308eeae27c6fca9f5ba5f8bb609b5e98b1e3ff1))\n* use br tag instead of linefeed char ([f8ba04f](https://github.com/Gumball12/bionic-reading/commit/f8ba04f2dd8971d2817e4c0cdbe89416a7b13acb))\n\n### [1.3.2](https://github.com/Gumball12/bionic-reading/compare/v1.3.1...v1.3.2) (2022-05-27)\n\n\n### Bug Fixes\n\n* copy README for npm publishing ([6cea4c8](https://github.com/Gumball12/bionic-reading/commit/6cea4c877fe8420b9a1ae9afa03ad51a67346027))\n\n### [1.3.1](https://github.com/Gumball12/bionic-reading/compare/v1.3.0...v1.3.1) (2022-05-27)\n\n\n### Bug Fixes\n\n* fix npm publishing process ([13a7278](https://github.com/Gumball12/bionic-reading/commit/13a7278addc6d3599007610e8cea76acf16f09e0))\n\n## [1.3.0](https://github.com/Gumball12/bionic-reading/compare/v1.2.3...v1.3.0) (2022-05-27)\n\n\n### Features\n\n* support multiple languages ([5c9c200](https://github.com/Gumball12/bionic-reading/commit/5c9c2002f3ae501554f54e26e56ff33ea3ec1823))\n\n\n### Bug Fixes\n\n* fix actions/cache key ([139171f](https://github.com/Gumball12/bionic-reading/commit/139171f9c5cd752dacd728f9b27e1eb299119460))\n* fix npm script ([f6c6706](https://github.com/Gumball12/bionic-reading/commit/f6c6706570ed1b724add21c5fd2262a7ae123ca8))\n* set cache field for test and release pipelines to false ([673f8fb](https://github.com/Gumball12/bionic-reading/commit/673f8fb0e3c89b037daa161c0e7b3138fd6ddf8e))\n\n### [1.2.3](https://github.com/Gumball12/bionic-reading/compare/v1.2.2...v1.2.3) (2022-05-26)\n\n\n### Features\n\n* implement splitMap util function ([2b1db41](https://github.com/Gumball12/bionic-reading/commit/2b1db41ebc957a75d0bd52a4bebbbbe2085275c3))\n* improve to perform the same behavior as the real bionic-reading API ([4e8d6bd](https://github.com/Gumball12/bionic-reading/commit/4e8d6bd93d72c21c12d054521b4b9da8cabd8cee)), closes [#12](https://github.com/Gumball12/bionic-reading/issues/12)\n\n### [1.2.2](https://github.com/Gumball12/bionic-reading/compare/v1.2.1...v1.2.2) (2022-05-23)\n\n\n### Features\n\n* define default options value ([f71cc5d](https://github.com/Gumball12/bionic-reading/commit/f71cc5dd35ece8e6a8bb05fac33e17e485c9d19b))\n* define Options type ([d632ee7](https://github.com/Gumball12/bionic-reading/commit/d632ee747df61154257d6cde084a41ceb9c0a6a7))\n* implement getBionicWordConvertor ([b184fb4](https://github.com/Gumball12/bionic-reading/commit/b184fb4956a218f18a9d87d505e033f9d02e401f))\n* implement isEmptyString util ([1ffa4e3](https://github.com/Gumball12/bionic-reading/commit/1ffa4e3de4d0b722b621da26e46fb9a3ad39b9f7))\n* implement omitBy util ([05c4579](https://github.com/Gumball12/bionic-reading/commit/05c4579c4bbc39f248f201f9a8ea02cfccdbe81a))\n* implement publish-sandbox action ([12888db](https://github.com/Gumball12/bionic-reading/commit/12888db0edbe07348c10610e3825c1971f3bccd3))\n* implement sandbox ([d56cb82](https://github.com/Gumball12/bionic-reading/commit/d56cb82ae3ca6e6ee69a5ca5109d6846ea205bfc))\n* supports strings with newlines ([bce07f7](https://github.com/Gumball12/bionic-reading/commit/bce07f71fde302794e13124bd5e8ff0617d56a37))\n\n\n### Bug Fixes\n\n* add root field to test only src directory ([f9e00fd](https://github.com/Gumball12/bionic-reading/commit/f9e00fdf698981395e8b0592691be33b2bff8f35))\n* handle if passing an empty style string ([7b60e96](https://github.com/Gumball12/bionic-reading/commit/7b60e9672e06a611990c95d726479b6ebd14c82e))\n* use base field instead of publicDir ([09cc18c](https://github.com/Gumball12/bionic-reading/commit/09cc18cf545474db5c92cefeeb7e1148f159da1a))\n\n### [1.2.1](https://github.com/Gumball12/bionic-reading/compare/v1.2.0...v1.2.1) (2022-05-22)\n\n## [1.2.0](https://github.com/Gumball12/bionic-reading/compare/v1.1.0...v1.2.0) (2022-05-22)\n\n\n### Features\n\n* ensure valid text & options object ([a79ac05](https://github.com/Gumball12/bionic-reading/commit/a79ac055fa8afa3666c8f4bc67c31bf4e40df988))\n* implement bionicConvertor ([c6ddb43](https://github.com/Gumball12/bionic-reading/commit/c6ddb432d9f140c2ff611191b40219abaf186266))\n* implement defaults util function ([58a16d5](https://github.com/Gumball12/bionic-reading/commit/58a16d5577c05971184419aea7adccdf08a3540e))\n* implement splitWord util function ([6b84b32](https://github.com/Gumball12/bionic-reading/commit/6b84b3232e380f0b379ad04f2b71893eb3096e45))\n* support markdown ([d4f9892](https://github.com/Gumball12/bionic-reading/commit/d4f9892dc15ea58c39e1a3e3d7fc1f4e9e6985be)), closes [#2](https://github.com/Gumball12/bionic-reading/issues/2)\n\n## [1.1.0](https://github.com/Gumball12/bionic-reading/compare/v1.0.2...v1.1.0) (2022-05-21)\n\n\n### Bug Fixes\n\n* do not use deafult export ([fdc32b2](https://github.com/Gumball12/bionic-reading/commit/fdc32b23bccf1e540812dd0515745ffbb68fd866))\n* fix release scripts ([b5495c3](https://github.com/Gumball12/bionic-reading/commit/b5495c3353a29087e2b5c8e219807063fb1490fc))\n\n### [1.0.2](https://github.com/Gumball12/bionic-reading/compare/v1.0.1...v1.0.2) (2022-05-21)\n\n\n### Bug Fixes\n\n* add registry-url field to publish workflow ([0dbbb1d](https://github.com/Gumball12/bionic-reading/commit/0dbbb1deaffc1324a583d88f2250069066dbb2d4))\n\n### [1.0.1](https://github.com/Gumball12/bionic-reading/compare/v1.0.0...v1.0.1) (2022-05-21)\n\n## 1.0.0 (2022-05-21)\n\n\n### Features\n\n* implement bionicReading module ([1813a8c](https://github.com/Gumball12/bionic-reading/commit/1813a8c649cb3fcce15099573b3bd46095a2e80c))\n* implement ci & publish workflows ([984bfda](https://github.com/Gumball12/bionic-reading/commit/984bfdae1bc04ae423aa06a2154a202bf2b678a7))\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\nto@shj.rip.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## Setup\n\n```bash\ngit clone https://github.com/<username>/text-vide.git\ncd text-vide\n\npnpm install\npnpm dev # run sandbox application (:5173)\npnpm test\npnpm lint\npnpm build\n```\n\n## Project structure\n\n```\n.\n├── apps/\n│   └── sandbox/\n└── packages/\n    ├── text-vide/    <-- core\n    ├── tsconfig/\n    └── utils/\n```\n\n```mermaid\ngraph LR;\n  utils --> TV[\"Text Vide\"]\n  tsconfig --> TV\n  TV --> NPM((\"Publish to<br>NPM\"))\n  TV --> Sandbox\n```\n"
  },
  {
    "path": "HOW.md",
    "content": "# How was this made?\n\nThis module was developed by referring to the official [BR API](https://rapidapi.com/bionic-reading-bionic-reading-default/api/bionic-reading1/). However, there is a difference in that it is implemented with a slightly different logic, such as not highlighting special characters. I'm going to talk about this here.\n\n## The Rules\n\nFirst, I found the rule below through the BR API call result.\n\n- [Rules for the Number of Characters](#char-length-rules)\n- [Rules for Special Characters](#special-chars-rules)\n- [Rules for Numbers](#number-rules)\n\n### Rules for the Number of Characters<a id=\"char-length-rules\"></a>\n\nAt first, I tried to find a Pattern by adding characters one by one. Below is the result:\n\n| Length (Min) | Length (Max) | Max - Min | Number of Non-Highlighted Chars |\n| ------------ | ------------ | --------- | ------------------------------- |\n| 0            | 4            | 4         | 1                               |\n| 5            | 12           | 7         | 2                               |\n| 13           | 16           | 3         | 3                               |\n| 17           | 24           | 7         | 4                               |\n| 25           | 29           | 4         | 5                               |\n| 30           | 35           | 5         | 6                               |\n| 36           | 42           | 6         | 7                               |\n| 43           | 48           | 5         | 8                               |\n\nI couldn't find a specific pattern here but thought I could use a Heuristic instead.\n\n> [Pneumonoultramicroscopicsilicovolcanoconiosis](https://en.wikipedia.org/wiki/Longest_word_in_English#cite_note-p45-6) (45 Letters)\n\nThe above word is the longest word that exists in the dictionary. That means, the number of characters will never exceed 46. So, I implemented the logic that I don't care about characters longer than a specific number of characters.\n\nBelow are the final rules for character count:\n\n| Length (Min) | Length (Max) | Max - Min | Number of Non-Bold Chars |\n| ------------ | ------------ | --------- | ------------------------ |\n| 0            | 4            | 4         | 1                        |\n| 5            | 12           | 7         | 2                        |\n| 13           | 16           | 3         | 3                        |\n| 17           | 24           | 7         | 4                        |\n| 25           | 29           | 4         | 5                        |\n| 30           | 35           | 5         | 6                        |\n| 36           | 42           | 6         | 7                        |\n| 43           | 48           | 5         | 8                        |\n| 49           | infinity     | infinity  | 9                        |\n\nThe above is a description of the `fixationPoint` option value of `1`, and the same is done for other cases. [See here](https://docs.google.com/spreadsheets/d/1nG8OoYUK6rXsWdi-L8pWihx9i_aSn9V0eYfLKy9-B-U/edit?usp=sharing) for a complete list of test results for Fixation Points.\n\n### Rules for Special Characters<a id=\"special-chars-rules\"></a>\n\nSpecial characters at the Beginning or End of a word are **not** highlighted.\n\n```ts\n';apple;' -> ';<b>app</b>le;'\n```\n\nSpecial characters placed **inside words** are treated the same as Regular characters.\n\n```ts\n'a;ppl;e' -> '<b>a;ppl</b>;e'\n```\n\nHowever, a Dash (`-`) among special characters located inside a word is treated the same as a Space.\n\n```ts\n'app-le' -> '<b>ap</b>p-<b>l</b>e'\n```\n\n> I thought it was awkward to highlight special characters, so I implemented it to divide them based on special characters.\n>\n> ```ts\n> // Origin\n> 'a;ppl;e' -> '<b>a;ppl</b>;e'\n> 'app-le' -> '<b>ap</b>p-<b>l</b>e'\n>\n> // This module\n> 'a;ppl;e' -> 'a;<b>pp</b>l;e'\n> 'app-le' -> '<b>ap</b>p-<b>l</b>e'\n> ```\n\n### Rules for numbers<a id=\"number-rules\"></a>\n\nIf there are only numbers, highlight nothing.\n\n```ts\n'1234567890' -> '1234567890'\n```\n\nIf there is a dash between the numbers, it is also not highlighted.\n\n```ts\n'1234-567890' -> '1234-567890'\n```\n\nWhen numbers and letters are used together, they are treated as regular characters.\n\n```ts\n'a1234567890' -> '<b>a12345678</b>90'\n'1234567890a' -> '<b>123456789</b>0a'\n'1234a567890' -> '<b>1234a5678</b>90'\n```\n\nIf a special character other than a dash is between numbers, treat it like a regular character.\n\n```ts\n'1234!567890' -> '<b>1234!5678</b>90'\n```\n\nOtherwise, it doesn't highlight anything.\n\n```ts\n'!1234567890' -> '!1234567890'\n'1234567890!' -> '1234567890!'\n```\n\nNote: Emojis are treated as special characters, not dashes.\n\n> This module does not highlight even if there are special characters between numbers.\n>\n> ```ts\n> // Origin\n> '1234!567890' -> '<b>1234!5678</b>90'\n>\n> // This module\n> '1234!567890' -> '1234!567890'\n> ```\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 shj\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# TextVide (vide; Latin for \"see\")\n\n[![npm bundle size](https://img.shields.io/bundlephobia/minzip/text-vide)](https://www.npmjs.com/package/text-vide) ![[npm downloads]((https://img.shields.io/bundlephobia/minzip/text-vide))](https://img.shields.io/npm/dm/text-vide) ![jsDelivr hits (npm)](https://img.shields.io/jsdelivr/npm/hm/text-vide) ![GitHub package.json version (subfolder of monorepo)](https://img.shields.io/github/package-json/v/Gumball12/text-vide?filename=packages%2Ftext-vide%2Fpackage.json) [![changelog](https://img.shields.io/badge/CHANGELOG-gray)](./CHANGELOG.md)\n\n[![ci](https://github.com/Gumball12/text-vide/actions/workflows/ci.yaml/badge.svg)](https://github.com/Gumball12/text-vide/actions/workflows/ci.yaml) [![codecov](https://codecov.io/gh/Gumball12/text-vide/branch/main/graph/badge.svg?token=MsLDgzri5B)](https://codecov.io/gh/Gumball12/text-vide)\n\n![logo](./docs/logo-extended.png)\n\n> **Support all languages that separate words with spaces**\n\n> [Try on Runkit](https://npm.runkit.com/text-vide) or [Online Sandbox](https://gumball12.github.io/text-vide/)\n\nThis module is an open source implementation that mimics the behavior of the [Bionic Reading API](https://bionic-reading.com/).\n\nIt does not require any additional licenses, except for MIT. ([#38](https://github.com/Gumball12/text-vide/issues/38))\n\n- _[How was this made?](./HOW.md)_\n- _[I don't think it's going to be more readable](./ABOUT_READABILITY.md)_\n- _[CONTRIBUTING.md](./CONTRIBUTING.md)_\n\n## 💫 Features\n\n| Feature                                                                 | State |\n| ----------------------------------------------------------------------- | ----- |\n| [Support all languages](https://github.com/Gumball12/text-vide/pull/16) | ✅    |\n| [Support ESM and CommonJS](#usage)                                      | ✅    |\n| [Custom `sep` Style](#options-sep)                                      | ✅    |\n| [Fixation-Points](#options-fixationpoint)                               | ✅    |\n| [Ignore HTML Tags](#options-ignorehtmltag)                              | ✅    |\n| [Ignore HTML Entity](#options-ignorehtmlentity)                         | ✅    |\n| [Saccade](https://github.com/Gumball12/text-vide/issues/21)             | ❌    |\n\n### Benchmark\n\n```\nSun Aug 07 2022 01:33:40 GM +0900\nlength of normal text: 590\nlength of text with html tags: 1579\n\nnormal#ignoreHtmlTags x 46,106 ops/sec ±4.22% (86 runs sampled)\nnormal#notIgnoreHtmlTags x 53,200 ops/sec ±0.93% (89 runs sampled)\nwithHtmlTags#ignoreHtmlTags x 3,213 ops/sec ±0.92% (87 runs sampled)\nwithHtmlTags#notIgnoreHtmlTags x 3,605 ops/sec ±1.59% (87 runs sampled)\n```\n\n[code](./apps/benchmark/index.js)\n\n## ⚙️ Install\n\n```bash\nnpm i text-vide\nyarn add text-vide\npnpm add text-vide\n```\n\n### CDN\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/text-vide/dist/index.iife.js\"></script>\n```\n\n## 📖 Usage<a id=\"usage\"></a>\n\n### Browser\n\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/text-vide/dist/index.iife.js\"></script>\n<script>\n  const text =\n    'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n\n  const highlightedText = textVide.textVide(text);\n\n  console.log(highlightedText); // '<b>Bion</b>ic <b>Readi</b>ng ... <b>writt</b>en <b>conte</b>nt.'\n</script>\n```\n\n### ESM\n\n```ts\nimport { textVide } from 'text-vide';\n\nconst text =\n  'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n\nconst highlightedText = textVide(text);\n\nconsole.log(highlightedText); // '<b>Bion</b>ic <b>Readi</b>ng ... <b>writt</b>en <b>conte</b>nt.'\n```\n\n### CommonJS\n\n```ts\nconst { textVide } = require('text-vide');\n\nconst text =\n  'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n\nconst highlightedText = textVide(text);\n\nconsole.log(highlightedText); // '<b>Bion</b>ic <b>Readi</b>ng ... <b>writt</b>en <b>conte</b>nt.'\n```\n\n## 📚 API\n\n### `textVide(text: string, options?: Options)`\n\n```ts\ntextVide('text-vide');\ntextVide('text-vide', {\n  // ... Options\n});\n```\n\n### Options\n\n```ts\ntype Options = Partial<{\n  sep: string | string[];\n  fixationPoint: number;\n  ignoreHtmlTag: boolean;\n  ignoreHtmlEntity: boolean;\n}>;\n```\n\n#### `sep`<a id=\"options-sep\"></a>\n\n- Default: `['<b>', '</b>']`\n\nPassing a string allows you to specify the Beginning and End of the highlighted word at once.\n\n```ts\ntextVide('text-vide', { sep: '**' }); // '**tex**t-**vid**e'\n```\n\nIt can also set them up by passing a list of length 2.\n\n```ts\ntextVide('text-vide', { sep: ['<strong>', '</strong>'] }); // '<strong>tex</strong>t-<strong>vid</strong>e'\n```\n\n#### `fixationPoint`<a id=\"options-fixationpoint\"></a>\n\n- Default: `1`\n- Range: `[1, 5]`\n\n```ts\n// Default fixation-point: 1\ntextVide('text-vide'); // '<b>tex</b>t-<b>vid</b>e'\n\n// Changed fixation-point: 5\ntextVide('text-vide', { fixationPoint: 5 }); // '<b>t</b>ext-<b>v</b>ide'\n```\n\n#### `ignoreHtmlTag`<a id=\"options-ignorehtmltag\"></a>\n\n- Default: `true`\n\nIf this option is `true`, HTML tags are not highlighted.\n\n```ts\ntextVite('<div>abcd</div>efg'); // '<div><b>abc</b>d</div><b>ef</b>g'\ntextVite('<div>abcd</div>efg', { ignoreHtmlTag: false }); // '<<b>di</b>v><b>abc</b>d</<b>di</b>v><b>ef</b>g'\n```\n\n#### `ignoreHtmlEntity`<a id=\"options-ignorehtmlentity\"></a>\n\n- Default: `true`\n\nIf this option is `true`, HTML entities are not highlighted.\n\n```ts\ntextVide('&nbsp;abcd&gt;'); // '&nbsp;<b>abc</b>d&gt;'\ntextVide('&nbsp;abcd&gt;', { ignoreHtmlEntity: false }); // &<b>nbs</b>p;<b>abc</b>d&<b>g</b>t;\n```\n\n## License\n\n[MIT](./LICENSE) @Gumball12\n\nIt does not require any additional licenses, except for MIT. ([#38](https://github.com/Gumball12/text-vide/issues/38))\n\n## Why Monorepo?\n\nI acknowledge that the current monorepo structure might seem complex for the project's requirements. Here's why I chose this approach:\n\n- This project served as a learning experience for implementing monorepo architecture, as I was preparing to introduce it at my workplace\n- I intentionally used this small project as a practical exercise to understand monorepo concepts and best practices\n- While the current structure might be over-engineered, I plan to maintain it since:\n  - The project requirements are relatively stable\n  - Major changes or additions are unlikely\n- However, I'm open to simplifying the architecture if there's a compelling reason to do so\n"
  },
  {
    "path": "apps/benchmark/index.js",
    "content": "const { textVide } = require('text-vide');\nconst Benchmark = require('benchmark');\nconst suite = new Benchmark.Suite();\n\nconst text =\n  'orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';\n\nconst textWithHtmlTags = `<div class=\"bionic-reader-container\">\n          \n          \n<span class=\"w bionic\"><b class=\"b bionic\">nor</b>mal </span><span class=\"w bionic\"><b class=\"b bionic\">te</b>xt</span>: <span class=\"w bionic\"><b class=\"b bionic\">abcd</b>efg</span><br><span class=\"w bionic\"><b class=\"b bionic\">wi</b>th </span><span class=\"w bionic\"><b class=\"b bionic\">a</b> </span><span class=\"w bionic\"><b class=\"b bionic\">t</b>ag</span>: <a target=\"_blank\"><span class=\"w bionic\"><b class=\"b bionic\">ab</b>cd</span></a><span class=\"w bionic\"><b class=\"b bionic\">e</b>fg</span><br><span class=\"w bionic\"><b class=\"b bionic\">wi</b>th </span><span class=\"w bionic\"><b class=\"b bionic\">b</b> </span><span class=\"w bionic\"><b class=\"b bionic\">t</b>ag</span>: <b><span class=\"w bionic\"><b class=\"b bionic\">ab</b>cd</span></b><span class=\"w bionic\"><b class=\"b bionic\">e</b>fg</span><br><span class=\"w bionic\"><b class=\"b bionic\">wi</b>th </span><span class=\"w bionic\"><b class=\"b bionic\">d</b>iv </span><span class=\"w bionic\"><b class=\"b bionic\">t</b>ag</span>: <div><span class=\"w bionic\"><b class=\"b bionic\">ab</b>cd</span></div><span class=\"w bionic\"><b class=\"b bionic\">e</b>fg</span><br>\n\n  <!-- <div class=\"br-foot-node\">\n      <p style=\"margin: 32px 0 32px 70px; font-weight: 700; font-size: 26px; line-height: 1.6em;\">\n          —\n      </p>\n      <p>\n          Bionic Reading<sup>®</sup><br>\n          A higher dimension of reading.<br>\n          <a href=\"https://bionic-reading.com\">bionic-reading.com</a>\n      </p>\n      <br/>\n      <br/>\n      <p>\n          \n      </p>\n  </div> -->\n\n</div>`;\n\nsuite\n  .add('normal#ignoreHtmlTags', () => textVide(text))\n  .add('normal#notIgnoreHtmlTags', () =>\n    textVide(text, { ignoreHtmlTag: false }),\n  )\n\n  .add('withHtmlTags#ignoreHtmlTags', () => textVide(textWithHtmlTags))\n  .add('withHtmlTags#notIgnoreHtmlTags', () =>\n    textVide(textWithHtmlTags, { ignoreHtmlTag: false }),\n  )\n\n  .on('start', () =>\n    console.log(\n      [\n        new Date().toString(),\n        `length of normal text: ${text.length}`,\n        `length of text with html tags: ${textWithHtmlTags.length}`,\n        '',\n      ].join('\\n'),\n    ),\n  )\n  .on('cycle', evt => {\n    console.log(evt.target.toString());\n  })\n  .run({ async: true });\n"
  },
  {
    "path": "apps/benchmark/package.json",
    "content": "{\n  \"name\": \"benchmark\",\n  \"version\": \"0.0.0\",\n  \"scripts\": {\n    \"benchmark\": \"node index.js\"\n  },\n  \"devDependencies\": {\n    \"benchmark\": \"^2.1.4\",\n    \"text-vide\": \"workspace:*\"\n  }\n}\n"
  },
  {
    "path": "apps/sandbox/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Text Vide Sandbox</title>\n\n    <link\n      rel=\"stylesheet\"\n      href=\"https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap\"\n    />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://fonts.googleapis.com/icon?family=Material+Icons\"\n    />\n    <link\n      href=\"https://cdn.jsdelivr.net/gh/sunn-us/SUIT/fonts/static/woff2/SUIT.css\"\n      rel=\"stylesheet\"\n    />\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "apps/sandbox/package.json",
    "content": "{\n  \"name\": \"sandbox\",\n  \"version\": \"0.0.0\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"react\": \"^18.0.0\",\n    \"react-dom\": \"^18.0.0\"\n  },\n  \"devDependencies\": {\n    \"@emotion/react\": \"^11.14.0\",\n    \"@emotion/styled\": \"^11.14.1\",\n    \"@mui/material\": \"^5.8.1\",\n    \"@types/react\": \"^18.0.0\",\n    \"@types/react-dom\": \"^18.0.0\",\n    \"@unocss/preset-wind\": \"^66.5.4\",\n    \"@unocss/reset\": \"^66.5.4\",\n    \"@vitejs/plugin-react\": \"^5.0.4\",\n    \"text-vide\": \"workspace:*\",\n    \"tsconfig\": \"workspace:*\",\n    \"unocss\": \"^66.5.4\"\n  }\n}\n"
  },
  {
    "path": "apps/sandbox/src/App.tsx",
    "content": "import {\n  Box,\n  Button,\n  TextField,\n  ToggleButton,\n  ToggleButtonGroup,\n} from '@mui/material';\nimport { Reducer, useEffect, useReducer } from 'react';\nimport { textVide } from 'text-vide';\nimport logo from './logo.png';\n\nconst DEBOUNCE_TIMEOUT = 400;\nconst COPIED_EFFECT_DEBOUNCE_TIMEOUT = 1200;\n\nconst INITIAL_INPUT =\n  'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n\ntype Edits = {\n  firstSep: string;\n  secondSep: string;\n  fixationPoint: string;\n  ignoreHtmlTag: string;\n  ignoreHtmlEntity: string;\n  input: string;\n};\n\nconst defaultEdits: Edits = {\n  firstSep: '<b>',\n  secondSep: '</b>',\n  fixationPoint: '1',\n  ignoreHtmlTag: '1', // 1 = true, 0 = false\n  ignoreHtmlEntity: '1', // 1 = true, 0 = false\n  input: INITIAL_INPUT,\n};\n\nconst storeEdits = ({\n  firstSep,\n  secondSep,\n  fixationPoint,\n  input,\n  ignoreHtmlTag,\n  ignoreHtmlEntity,\n}: Edits) => {\n  const search = [\n    `firstSep=${encodeURIComponent(firstSep)}`,\n    `secondSep=${encodeURIComponent(secondSep)}`,\n    `fixationPoint=${encodeURIComponent(fixationPoint)}`,\n    `input=${encodeURIComponent(input)}`,\n    `ignoreHtmlTag=${encodeURIComponent(ignoreHtmlTag)}`,\n    `ignoreHtmlEntity=${encodeURIComponent(ignoreHtmlEntity)}`,\n  ].join('&');\n\n  // eslint-disable-next-line\n  // @ts-ignore\n  history.replaceState(null, null, `?${search}`);\n};\n\nconst getEdits = (): Edits => {\n  const maybeEditsString = location.search.slice(1);\n\n  if (!maybeEditsString?.length) {\n    return defaultEdits;\n  }\n\n  const maybeEdits = maybeEditsString.split('&').reduce((maybeEdits, str) => {\n    const [maybeKey, maybeValue] = str.split('=');\n\n    if (!maybeKey.length) {\n      return maybeEdits;\n    }\n\n    const key = maybeKey as keyof Edits;\n    const value = maybeValue ?? defaultEdits[key];\n\n    if (!value) {\n      return maybeEdits;\n    }\n\n    // eslint-disable-next-line\n    // @ts-ignore\n    maybeEdits[key] = decodeURIComponent(value);\n    return maybeEdits;\n  }, {} as Partial<Edits>);\n\n  return {\n    ...defaultEdits,\n    ...maybeEdits,\n  };\n};\n\nconst initialEdits = getEdits();\n\ntype State = Edits & {\n  highlightedText: string;\n  copiedEffect: boolean;\n};\n\ntype Action = {\n  type:\n    | 'FIRST_SEP'\n    | 'SECOND_SEP'\n    | 'FIXATION_POINT'\n    | 'INPUT'\n    | 'HIGHLIGHTED_TEXT'\n    | 'COPIED'\n    | 'TOGGLE_IGNORE_HTML_TAG'\n    | 'TOGGLE_IGNORE_HTML_ENTITY'\n    | 'RESET';\n  value: string;\n  copied: boolean;\n};\n\nconst reducer: Reducer<State, Action> = (state, { type, value, copied }) => {\n  if (type === 'FIRST_SEP') {\n    return { ...state, firstSep: value };\n  }\n\n  if (type === 'SECOND_SEP') {\n    return { ...state, secondSep: value };\n  }\n\n  if (type === 'FIXATION_POINT') {\n    return { ...state, fixationPoint: value };\n  }\n\n  if (type === 'INPUT') {\n    return { ...state, input: value };\n  }\n\n  if (type === 'HIGHLIGHTED_TEXT') {\n    return { ...state, highlightedText: value };\n  }\n\n  if (type === 'COPIED') {\n    return { ...state, copiedEffect: copied };\n  }\n\n  if (type === 'TOGGLE_IGNORE_HTML_TAG') {\n    const nextIgnoreHtmlTag = state.ignoreHtmlTag === '1' ? '0' : '1';\n    return { ...state, ignoreHtmlTag: nextIgnoreHtmlTag };\n  }\n\n  if (type === 'TOGGLE_IGNORE_HTML_ENTITY') {\n    const nextIgnoreHtmlEntity = state.ignoreHtmlEntity === '1' ? '0' : '1';\n    return { ...state, ignoreHtmlEntity: nextIgnoreHtmlEntity };\n  }\n\n  if (type === 'RESET') {\n    return {\n      ...defaultEdits,\n      highlightedText: '',\n      copiedEffect: false,\n    };\n  }\n\n  return state;\n};\n\nconst App = () => {\n  const [state, dispatchState] = useReducer(reducer, {\n    ...initialEdits,\n    highlightedText: '',\n    copiedEffect: false,\n  });\n\n  const {\n    firstSep,\n    secondSep,\n    input,\n    fixationPoint,\n    copiedEffect,\n    highlightedText,\n    ignoreHtmlTag,\n    ignoreHtmlEntity,\n  } = state;\n\n  useEffect(() => {\n    const store = setTimeout(() => {\n      const options = {\n        sep: [firstSep, secondSep],\n        fixationPoint: parseInt(fixationPoint),\n        ignoreHtmlTag: ignoreHtmlTag === '1',\n        ignoreHtmlEntity: ignoreHtmlEntity === '1',\n      };\n\n      const highlightedText = textVide(input, options);\n\n      dispatchState({\n        type: 'HIGHLIGHTED_TEXT',\n        value: highlightedText,\n        copied: false,\n      });\n\n      storeEdits({\n        firstSep,\n        secondSep,\n        input,\n        fixationPoint,\n        ignoreHtmlTag,\n        ignoreHtmlEntity,\n      });\n    }, DEBOUNCE_TIMEOUT);\n\n    return () => clearTimeout(store);\n  }, [\n    firstSep,\n    secondSep,\n    input,\n    fixationPoint,\n    ignoreHtmlTag,\n    ignoreHtmlEntity,\n  ]);\n\n  const copyUrl = () => {\n    const { href: url } = location;\n    navigator.clipboard.writeText(url);\n    dispatchState({ type: 'COPIED', value: '', copied: true });\n  };\n\n  useEffect(() => {\n    const store = setTimeout(\n      () => dispatchState({ type: 'COPIED', value: '', copied: false }),\n      COPIED_EFFECT_DEBOUNCE_TIMEOUT,\n    );\n\n    return () => clearTimeout(store);\n  }, [copiedEffect]);\n\n  const reset = () =>\n    dispatchState({ type: 'RESET', value: '', copied: false });\n\n  const isResetDisabled =\n    JSON.stringify(defaultEdits) ===\n    JSON.stringify({\n      firstSep,\n      secondSep,\n      fixationPoint,\n      input,\n      ignoreHtmlTag,\n      ignoreHtmlEntity,\n    });\n\n  return (\n    <div className=\"max-w-4xl m-auto sm:px-8 px-4 py-4 leading-tight\">\n      <header>\n        <section>\n          <img src={logo} className=\"h-20\" />\n        </section>\n        <section className=\"flex justify-between mb-3\">\n          <h1 className=\"text-2xl\">Text Vide Sandbox</h1>\n          <Button variant=\"outlined\" onClick={copyUrl}>\n            Copy URL\n          </Button>\n        </section>\n      </header>\n\n      <main className=\"flex flex-col gap-y-8\">\n        <section className=\"flex flex-col gap-y-4\">\n          <section className=\"flex justify-between\">\n            <div className=\"flex gap-x-2\">\n              <TextField\n                size=\"small\"\n                label=\"first sep\"\n                InputLabelProps={{ shrink: true }}\n                value={firstSep}\n                onInput={({ target }) =>\n                  dispatchState({\n                    type: 'FIRST_SEP',\n                    value: (target as HTMLInputElement).value,\n                    copied: false,\n                  })\n                }\n                required\n              />\n              <TextField\n                size=\"small\"\n                label=\"second sep\"\n                value={secondSep}\n                onInput={({ target }) =>\n                  dispatchState({\n                    type: 'SECOND_SEP',\n                    value: (target as HTMLInputElement).value,\n                    copied: false,\n                  })\n                }\n                InputLabelProps={{ shrink: true }}\n              />\n            </div>\n          </section>\n\n          <section className=\"flex gap-2 flex-wrap\">\n            <ToggleButtonGroup\n              size=\"small\"\n              exclusive\n              color=\"primary\"\n              value={fixationPoint}\n              onChange={(_, value) =>\n                value &&\n                dispatchState({ type: 'FIXATION_POINT', value, copied: false })\n              }\n            >\n              <ToggleButton value=\"1\">fixation - 1</ToggleButton>\n              <ToggleButton value=\"2\">2</ToggleButton>\n              <ToggleButton value=\"3\">3</ToggleButton>\n              <ToggleButton value=\"4\">4</ToggleButton>\n              <ToggleButton value=\"5\">5</ToggleButton>\n            </ToggleButtonGroup>\n\n            <Button\n              variant=\"outlined\"\n              color=\"success\"\n              onClick={() =>\n                dispatchState({\n                  type: 'TOGGLE_IGNORE_HTML_TAG',\n                  value: '',\n                  copied: false,\n                })\n              }\n            >\n              {ignoreHtmlTag === '1'\n                ? 'not ignore html tags'\n                : 'ignore html tags'}\n            </Button>\n\n            <Button\n              variant=\"outlined\"\n              color=\"success\"\n              onClick={() =>\n                dispatchState({\n                  type: 'TOGGLE_IGNORE_HTML_ENTITY',\n                  value: '',\n                  copied: false,\n                })\n              }\n            >\n              {ignoreHtmlEntity === '1'\n                ? 'not ignore html entities'\n                : 'ignore html entities'}\n            </Button>\n          </section>\n\n          <section className=\"flex gap-2\">\n            <Button\n              variant=\"outlined\"\n              color=\"error\"\n              onClick={reset}\n              disabled={isResetDisabled}\n            >\n              reset\n            </Button>\n          </section>\n        </section>\n\n        <section>\n          <p className=\"section-name mb-2px\">Input</p>\n          <textarea\n            className=\"border-gray-100 resize-y w-full rounded outline-none p-2 text-lg shadow-md focus:shadow-lg transition-shadow-300 min-h-48\"\n            value={input}\n            onInput={({ currentTarget: { value } }) =>\n              dispatchState({ type: 'INPUT', value, copied: false })\n            }\n          />\n        </section>\n\n        <section>\n          <p className=\"section-name\">Rendered</p>\n          <Box className=\"rendered-box\">\n            <pre\n              className=\"whitespace-pre-wrap\"\n              dangerouslySetInnerHTML={{ __html: highlightedText }}\n            />\n          </Box>\n        </section>\n\n        <section>\n          <p className=\"section-name\">Raw Data</p>\n          <Box className=\"rendered-box\">\n            <pre className=\"whitespace-pre-wrap text-sm\">{highlightedText}</pre>\n          </Box>\n        </section>\n      </main>\n\n      <footer className=\"fixed right-4 bottom-4 bg-white px-2 py-1\">\n        <a\n          className=\"text-gray-400 underline-gray-300\"\n          href=\"https://github.com/Gumball12/text-vide\"\n          target=\"_blank\"\n        >\n          GitHub\n        </a>\n      </footer>\n    </div>\n  );\n};\n\nexport default App;\n"
  },
  {
    "path": "apps/sandbox/src/index.css",
    "content": "* {\n  font-family: 'SUIT', sans-serif !important;\n  box-sizing: border-box;\n}\n\npre {\n  font-weight: 300;\n}\n\npre b {\n  font-weight: 800;\n}\n"
  },
  {
    "path": "apps/sandbox/src/main.tsx",
    "content": "import ReactDOM from 'react-dom/client';\nimport App from './App';\nimport 'uno.css';\nimport '@unocss/reset/eric-meyer.css';\nimport './index.css';\n\n// eslint-disable-next-line\nReactDOM.createRoot(document.getElementById('root')!).render(<App />);\n"
  },
  {
    "path": "apps/sandbox/src/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "apps/sandbox/tsconfig.json",
    "content": "{\n  \"extends\": \"tsconfig/vite.json\",\n  \"compilerOptions\": {\n    \"lib\": [\n      \"DOM\",\n      \"DOM.Iterable\",\n      \"ESNext\"\n    ],\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"jsx\": \"react-jsx\"\n  },\n  \"include\": [\n    \"src\"\n  ],\n  \"references\": [\n    {\n      \"path\": \"./tsconfig.node.json\"\n    }\n  ]\n}"
  },
  {
    "path": "apps/sandbox/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n  },\n  \"include\": [\n    \"vite.config.ts\"\n  ]\n}"
  },
  {
    "path": "apps/sandbox/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport Unocss from 'unocss/vite';\nimport presetWind from '@unocss/preset-wind';\n\nexport default defineConfig({\n  base: '/text-vide/',\n  plugins: [\n    Unocss({\n      presets: [presetWind()],\n      shortcuts: {\n        'section-name': 'text-sm text-neutral-600',\n        'transition-shadow-300': 'transition-shadow duration-300',\n        'rendered-box':\n          'w-full min-h-24 max-h-48 rounded bg-light-200 p-2 text-lg shadow-md hover:shadow-lg transition-shadow-300 overflow-y-scroll',\n      },\n    }),\n    react(),\n  ],\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"packageManager\": \"pnpm@10.19.0+sha512.c9fc7236e92adf5c8af42fd5bf1612df99c2ceb62f27047032f4720b33f8eacdde311865e91c411f2774f618d82f320808ecb51718bfa82c060c4ba7c76a32b8\",\n  \"engines\": {\n    \"node\": \">=22.12.0\"\n  },\n  \"private\": true,\n  \"workspaces\": [\n    \"apps/*\",\n    \"packages/*\"\n  ],\n  \"scripts\": {\n    \"build\": \"turbo run build\",\n    \"dev\": \"turbo run dev --no-cache --parallel --continue\",\n    \"preview\": \"turbo run preview\",\n    \"test\": \"turbo run test\",\n    \"benchmark\": \"turbo run benchmark\",\n    \"release\": \"turbo run release\",\n    \"lint\": \"eslint --ext .ts ./**/src/**.ts\",\n    \"prepare\": \"husky install\"\n  },\n  \"devDependencies\": {\n    \"@typescript-eslint/eslint-plugin\": \"^5.25.0\",\n    \"@typescript-eslint/parser\": \"^5.25.0\",\n    \"eslint\": \"^8.16.0\",\n    \"eslint-config-prettier\": \"^8.5.0\",\n    \"eslint-plugin-prettier\": \"^4.0.0\",\n    \"husky\": \"^8.0.1\",\n    \"lint-staged\": \"^12.4.1\",\n    \"prettier\": \"^2.6.2\",\n    \"turbo\": \"^1.2.14\",\n    \"typescript\": \"^4.6.4\",\n    \"vite\": \"^7.1.11\",\n    \"vitest\": \"^4.0.1\"\n  },\n  \"lint-staged\": {\n    \"*.{js,ts,tsx}\": [\n      \"eslint --fix\",\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"pnpm\": {\n    \"overrides\": {\n      \"handlebars\": \">=4.7.9\"\n    }\n  }\n}\n"
  },
  {
    "path": "packages/text-vide/.versionrc.json",
    "content": "{\n  \"infile\": \"../../CHANGELOG.md\"\n}\n"
  },
  {
    "path": "packages/text-vide/package.json",
    "content": "{\n  \"name\": \"text-vide\",\n  \"version\": \"1.8.5\",\n  \"description\": \"An Open-Source JavaScript Implementation of Bionic Reading.\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/Gumball12/text-vide.git\"\n  },\n  \"author\": {\n    \"name\": \"Gumball12\",\n    \"email\": \"to@shj.rip\",\n    \"url\": \"https://github.com/Gumball12\"\n  },\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/Gumball12/text-vide/issues\"\n  },\n  \"homepage\": \"https://github.com/Gumball12/text-vide#readme\",\n  \"keywords\": [\n    \"bionic-reading\",\n    \"bionic reading\",\n    \"bionic\",\n    \"reading\",\n    \"readable\",\n    \"text\",\n    \"highlighted\",\n    \"fixation-points\"\n  ],\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.mjs\",\n  \"types\": \"dist/packages/text-vide/src/index.d.ts\",\n  \"files\": [\n    \"dist\"\n  ],\n  \"scripts\": {\n    \"test\": \"vitest run --coverage\",\n    \"build\": \"vite build\",\n    \"release\": \"standard-version\"\n  },\n  \"devDependencies\": {\n    \"@vitest/coverage-v8\": \"^4.0.1\",\n    \"standard-version\": \"^9.5.0\",\n    \"tsconfig\": \"workspace:*\",\n    \"utils\": \"workspace:*\",\n    \"vite-plugin-dts\": \"^2.1.0\"\n  }\n}\n"
  },
  {
    "path": "packages/text-vide/src/__tests__/getOptions.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport getOptions from '../getOptions';\nimport { Options } from '../types';\n\ndescribe('test getOptions()', () => {\n  it('pass empty object', () => {\n    const expected: Options = {\n      sep: ['<b>', '</b>'],\n      fixationPoint: 1,\n      ignoreHtmlTag: true,\n      ignoreHtmlEntity: true,\n    };\n\n    expect(getOptions({})).toEqual(expected);\n  });\n\n  it('pass undefined value', () => {\n    const undefinedOptionValues = {\n      sep: undefined,\n      fixationPoint: undefined,\n      ignoreHtmlTag: undefined,\n      ignoreHtmlEntity: undefined,\n    };\n\n    const expected: Options = {\n      sep: ['<b>', '</b>'],\n      fixationPoint: 1,\n      ignoreHtmlTag: true,\n      ignoreHtmlEntity: true,\n    };\n\n    expect(getOptions(undefinedOptionValues)).toEqual(expected);\n  });\n\n  it('pass empty string value', () => {\n    const maybeOptions = {\n      sep: ['', ''],\n      fixationPoint: undefined,\n      ignoreHtmlTag: undefined,\n      ignoreHtmlEntity: undefined,\n    };\n\n    const expected: Options = {\n      sep: ['', ''],\n      fixationPoint: 1,\n      ignoreHtmlTag: true,\n      ignoreHtmlEntity: true,\n    };\n\n    expect(getOptions(maybeOptions)).toEqual(expected);\n  });\n\n  it('pass valid value', () => {\n    const expected: Options = {\n      sep: ['a', 'b'],\n      fixationPoint: 0, // but it's okay\n      ignoreHtmlTag: false,\n      ignoreHtmlEntity: false,\n    };\n\n    expect(getOptions(expected)).toEqual(expected);\n  });\n});\n"
  },
  {
    "path": "packages/text-vide/src/__tests__/index.test.ts",
    "content": "import { textVide } from '..';\nimport { describe, expect, it } from 'vitest';\n\ndescribe('test textVide module', () => {\n  it('test paragraph 1', () => {\n    const text =\n      'orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';\n    const expectedText =\n      '<b>ore</b>m <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t, <b>consetet</b>ur <b>sadipsci</b>ng <b>eli</b>tr, <b>se</b>d <b>dia</b>m <b>nonu</b>my <b>eirm</b>od <b>temp</b>or <b>invidu</b>nt <b>u</b>t <b>labo</b>re <b>e</b>t <b>dolo</b>re <b>mag</b>na <b>aliquy</b>am <b>era</b>t, <b>se</b>d <b>dia</b>m <b>volupt</b>ua. <b>A</b>t <b>ver</b>o <b>eo</b>s <b>e</b>t <b>accus</b>am <b>e</b>t <b>jus</b>to <b>du</b>o <b>dolor</b>es <b>e</b>t <b>e</b>a <b>reb</b>um. <b>Ste</b>t <b>cli</b>ta <b>kas</b>d <b>gubergr</b>en, <b>n</b>o <b>se</b>a <b>takima</b>ta <b>sanct</b>us <b>es</b>t <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t. <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t, <b>consetet</b>ur <b>sadipsci</b>ng <b>eli</b>tr, <b>se</b>d <b>dia</b>m <b>nonu</b>my <b>eirm</b>od <b>temp</b>or <b>invidu</b>nt <b>u</b>t <b>labo</b>re <b>e</b>t <b>dolo</b>re <b>mag</b>na <b>aliquy</b>am <b>era</b>t, <b>se</b>d <b>dia</b>m <b>volupt</b>ua. <b>A</b>t <b>ver</b>o <b>eo</b>s <b>e</b>t <b>accus</b>am <b>e</b>t <b>jus</b>to <b>du</b>o <b>dolor</b>es <b>e</b>t <b>e</b>a <b>reb</b>um. <b>Ste</b>t <b>cli</b>ta <b>kas</b>d <b>gubergr</b>en, <b>n</b>o <b>se</b>a <b>takima</b>ta <b>sanct</b>us <b>es</b>t <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t.';\n\n    expect(textVide(text)).toBe(expectedText);\n  });\n\n  it('test paragraph 2', () => {\n    const text =\n      'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n    const expectedText =\n      '<b>Bion</b>ic <b>Readi</b>ng <b>i</b>s a <b>ne</b>w <b>meth</b>od <b>facilitati</b>ng <b>th</b>e <b>readi</b>ng <b>proce</b>ss <b>b</b>y <b>guidi</b>ng <b>th</b>e <b>eye</b>s <b>throu</b>gh <b>tex</b>t <b>wit</b>h <b>artifici</b>al <b>fixati</b>on <b>poin</b>ts. <b>A</b>s a <b>resu</b>lt, <b>th</b>e <b>read</b>er <b>i</b>s <b>onl</b>y <b>focusi</b>ng <b>o</b>n <b>th</b>e <b>highlight</b>ed <b>initi</b>al <b>lette</b>rs <b>an</b>d <b>let</b>s <b>th</b>e <b>bra</b>in <b>cent</b>er <b>comple</b>te <b>th</b>e <b>wor</b>d. <b>I</b>n a <b>digit</b>al <b>wor</b>ld <b>dominat</b>ed <b>b</b>y <b>shall</b>ow <b>for</b>ms <b>o</b>f <b>readi</b>ng, <b>Bion</b>ic <b>Readi</b>ng <b>aim</b>s <b>t</b>o <b>encoura</b>ge a <b>mor</b>e <b>i</b>n-<b>dep</b>th <b>readi</b>ng <b>an</b>d <b>understand</b>ing <b>o</b>f <b>writt</b>en <b>conte</b>nt.';\n\n    expect(textVide(text)).toBe(expectedText);\n  });\n\n  it('test paragraph 3 (with number)', () => {\n    const text = `Pan Am Flight 7 was a westbound round-the-world flight operated by Pan American World Airways that crashed in the Pacific Ocean on November 8, 1957, while flying from San Francisco International Airport to Honolulu International Airport. The crash of the Boeing 377 Stratocruiser 10-29 (example pictured) killed all thirty-six passengers and eight crew members. The flight's fate was not known until about nine hours after its last radio transmission. No emergency radio reports were received.`;\n    const expectedText = `<b>Pa</b>n <b>A</b>m <b>Flig</b>ht 7 <b>wa</b>s a <b>westbou</b>nd <b>rou</b>nd-<b>th</b>e-<b>wor</b>ld <b>flig</b>ht <b>operat</b>ed <b>b</b>y <b>Pa</b>n <b>Americ</b>an <b>Wor</b>ld <b>Airwa</b>ys <b>tha</b>t <b>crash</b>ed <b>i</b>n <b>th</b>e <b>Pacif</b>ic <b>Oce</b>an <b>o</b>n <b>Novemb</b>er 8, 1957, <b>whi</b>le <b>flyi</b>ng <b>fro</b>m <b>Sa</b>n <b>Francis</b>co <b>Internatio</b>nal <b>Airpo</b>rt <b>t</b>o <b>Honolu</b>lu <b>Internatio</b>nal <b>Airpo</b>rt. <b>Th</b>e <b>cra</b>sh <b>o</b>f <b>th</b>e <b>Boei</b>ng 377 <b>Stratocrui</b>ser 10-29 (<b>examp</b>le <b>pictur</b>ed) <b>kill</b>ed <b>al</b>l <b>thir</b>ty-<b>si</b>x <b>passenge</b>rs <b>an</b>d <b>eig</b>ht <b>cre</b>w <b>membe</b>rs. <b>Th</b>e <b>flig</b>ht's <b>fat</b>e <b>wa</b>s <b>no</b>t <b>kno</b>wn <b>unt</b>il <b>abo</b>ut <b>nin</b>e <b>hou</b>rs <b>aft</b>er <b>it</b>s <b>las</b>t <b>rad</b>io <b>transmissi</b>on. <b>N</b>o <b>emergen</b>cy <b>rad</b>io <b>repor</b>ts <b>wer</b>e <b>receiv</b>ed.`;\n\n    expect(textVide(text)).toBe(expectedText);\n  });\n\n  it('special char (dash)', () => {\n    const text = '-----';\n    const expected = '-----';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('test Korean', () => {\n    const text =\n      '바이오닉 리딩은 인위적인 fixation point를 사용하여 문장을 읽기 쉽게 만들어줍니다. 눈은 강조된 단어만 따라가며 뇌를 의존해 문장을 완성합니다. 얕고 넓은 디지털 정보 시대에 바이오닉 리딩은 콘텐츠를 깊게 음미할 수 있도록 도와줍니다.';\n    const expectedText =\n      '<b>바이오</b>닉 <b>리딩</b>은 <b>인위적</b>인 <b>fixati</b>on <b>poin</b>t를 <b>사용하</b>여 <b>문장</b>을 <b>읽</b>기 <b>쉽</b>게 <b>만들어줍</b>니다. <b>눈</b>은 <b>강조</b>된 <b>단어</b>만 <b>따라가</b>며 <b>뇌</b>를 <b>의존</b>해 <b>문장</b>을 <b>완성합</b>니다. <b>얕</b>고 <b>넓</b>은 <b>디지</b>털 <b>정</b>보 <b>시대</b>에 <b>바이오</b>닉 <b>리딩</b>은 <b>콘텐츠</b>를 <b>깊</b>게 <b>음미</b>할 수 <b>있도</b>록 <b>도와줍</b>니다.';\n\n    expect(textVide(text)).toBe(expectedText);\n  });\n\n  it('test Russian', () => {\n    const text = `Конституция СФРЮ 1974 года - третья и последняя конституция Социалистической Федеративной Республики Югославии. Вступила в силу 21 февраля 1974 года, действие окончательно прекратилось в результате начавшегося распада Югославии в 1992 году.`;\n    const expected = `<b>Конституц</b>ия <b>СФР</b>Ю 1974 <b>год</b>а - <b>трет</b>ья и <b>последн</b>яя <b>конституц</b>ия <b>Социалистичес</b>кой <b>Федеративн</b>ой <b>Республи</b>ки <b>Югослав</b>ии. <b>Вступи</b>ла в <b>сил</b>у 21 <b>февра</b>ля 1974 <b>год</b>а, <b>действ</b>ие <b>окончатель</b>но <b>прекратило</b>сь в <b>результа</b>те <b>начавшего</b>ся <b>распа</b>да <b>Югослав</b>ии в 1992 <b>год</b>у.`;\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('pass empty string', () => {\n    const text = '';\n    const expected = '';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('pass len 1 string', () => {\n    const text = 'a';\n    const expected = 'a';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('pass strings with line break', () => {\n    const text = `\n    a\n    b\n    c\n    `;\n\n    const expected = `\n    a\n    b\n    c\n    `;\n\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('very long word', () => {\n    const text =\n      'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';\n    const expected =\n      '<b>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</b>aaaaaaaaa';\n    expect(textVide(text)).toBe(expected);\n  });\n});\n\ndescribe('test options', () => {\n  it('pass empty options object', () => {\n    expect(textVide('aaaa', {})).toBe('<b>aaa</b>a');\n  });\n\n  it('options.sep :: [<strong>, </strong>]', () => {\n    const text =\n      'orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';\n    const expectedText =\n      '<strong>ore</strong>m <strong>ips</strong>um <strong>dol</strong>or <strong>si</strong>t <strong>ame</strong>t, <strong>consetet</strong>ur <strong>sadipsci</strong>ng <strong>eli</strong>tr, <strong>se</strong>d <strong>dia</strong>m <strong>nonu</strong>my <strong>eirm</strong>od <strong>temp</strong>or <strong>invidu</strong>nt <strong>u</strong>t <strong>labo</strong>re <strong>e</strong>t <strong>dolo</strong>re <strong>mag</strong>na <strong>aliquy</strong>am <strong>era</strong>t, <strong>se</strong>d <strong>dia</strong>m <strong>volupt</strong>ua. <strong>A</strong>t <strong>ver</strong>o <strong>eo</strong>s <strong>e</strong>t <strong>accus</strong>am <strong>e</strong>t <strong>jus</strong>to <strong>du</strong>o <strong>dolor</strong>es <strong>e</strong>t <strong>e</strong>a <strong>reb</strong>um. <strong>Ste</strong>t <strong>cli</strong>ta <strong>kas</strong>d <strong>gubergr</strong>en, <strong>n</strong>o <strong>se</strong>a <strong>takima</strong>ta <strong>sanct</strong>us <strong>es</strong>t <strong>Lor</strong>em <strong>ips</strong>um <strong>dol</strong>or <strong>si</strong>t <strong>ame</strong>t. <strong>Lor</strong>em <strong>ips</strong>um <strong>dol</strong>or <strong>si</strong>t <strong>ame</strong>t, <strong>consetet</strong>ur <strong>sadipsci</strong>ng <strong>eli</strong>tr, <strong>se</strong>d <strong>dia</strong>m <strong>nonu</strong>my <strong>eirm</strong>od <strong>temp</strong>or <strong>invidu</strong>nt <strong>u</strong>t <strong>labo</strong>re <strong>e</strong>t <strong>dolo</strong>re <strong>mag</strong>na <strong>aliquy</strong>am <strong>era</strong>t, <strong>se</strong>d <strong>dia</strong>m <strong>volupt</strong>ua. <strong>A</strong>t <strong>ver</strong>o <strong>eo</strong>s <strong>e</strong>t <strong>accus</strong>am <strong>e</strong>t <strong>jus</strong>to <strong>du</strong>o <strong>dolor</strong>es <strong>e</strong>t <strong>e</strong>a <strong>reb</strong>um. <strong>Ste</strong>t <strong>cli</strong>ta <strong>kas</strong>d <strong>gubergr</strong>en, <strong>n</strong>o <strong>se</strong>a <strong>takima</strong>ta <strong>sanct</strong>us <strong>es</strong>t <strong>Lor</strong>em <strong>ips</strong>um <strong>dol</strong>or <strong>si</strong>t <strong>ame</strong>t.';\n\n    expect(textVide(text, { sep: ['<strong>', '</strong>'] })).toBe(\n      expectedText,\n    );\n  });\n\n  it('options.sep :: `**`', () => {\n    const text =\n      'orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';\n    const expectedText =\n      '**ore**m **ips**um **dol**or **si**t **ame**t, **consetet**ur **sadipsci**ng **eli**tr, **se**d **dia**m **nonu**my **eirm**od **temp**or **invidu**nt **u**t **labo**re **e**t **dolo**re **mag**na **aliquy**am **era**t, **se**d **dia**m **volupt**ua. **A**t **ver**o **eo**s **e**t **accus**am **e**t **jus**to **du**o **dolor**es **e**t **e**a **reb**um. **Ste**t **cli**ta **kas**d **gubergr**en, **n**o **se**a **takima**ta **sanct**us **es**t **Lor**em **ips**um **dol**or **si**t **ame**t. **Lor**em **ips**um **dol**or **si**t **ame**t, **consetet**ur **sadipsci**ng **eli**tr, **se**d **dia**m **nonu**my **eirm**od **temp**or **invidu**nt **u**t **labo**re **e**t **dolo**re **mag**na **aliquy**am **era**t, **se**d **dia**m **volupt**ua. **A**t **ver**o **eo**s **e**t **accus**am **e**t **jus**to **du**o **dolor**es **e**t **e**a **reb**um. **Ste**t **cli**ta **kas**d **gubergr**en, **n**o **se**a **takima**ta **sanct**us **es**t **Lor**em **ips**um **dol**or **si**t **ame**t.';\n\n    expect(textVide(text, { sep: '**' })).toBe(expectedText);\n  });\n\n  it('options.sep :: `__`', () => {\n    const text =\n      'orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';\n    const expectedText =\n      '__ore__m __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t. __Lor__em __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t.';\n\n    expect(textVide(text, { sep: '__' })).toBe(expectedText);\n  });\n\n  it('undefeined options.sep', () => {\n    const text =\n      'orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';\n    const expectedText =\n      '<b>ore</b>m <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t, <b>consetet</b>ur <b>sadipsci</b>ng <b>eli</b>tr, <b>se</b>d <b>dia</b>m <b>nonu</b>my <b>eirm</b>od <b>temp</b>or <b>invidu</b>nt <b>u</b>t <b>labo</b>re <b>e</b>t <b>dolo</b>re <b>mag</b>na <b>aliquy</b>am <b>era</b>t, <b>se</b>d <b>dia</b>m <b>volupt</b>ua. <b>A</b>t <b>ver</b>o <b>eo</b>s <b>e</b>t <b>accus</b>am <b>e</b>t <b>jus</b>to <b>du</b>o <b>dolor</b>es <b>e</b>t <b>e</b>a <b>reb</b>um. <b>Ste</b>t <b>cli</b>ta <b>kas</b>d <b>gubergr</b>en, <b>n</b>o <b>se</b>a <b>takima</b>ta <b>sanct</b>us <b>es</b>t <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t. <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t, <b>consetet</b>ur <b>sadipsci</b>ng <b>eli</b>tr, <b>se</b>d <b>dia</b>m <b>nonu</b>my <b>eirm</b>od <b>temp</b>or <b>invidu</b>nt <b>u</b>t <b>labo</b>re <b>e</b>t <b>dolo</b>re <b>mag</b>na <b>aliquy</b>am <b>era</b>t, <b>se</b>d <b>dia</b>m <b>volupt</b>ua. <b>A</b>t <b>ver</b>o <b>eo</b>s <b>e</b>t <b>accus</b>am <b>e</b>t <b>jus</b>to <b>du</b>o <b>dolor</b>es <b>e</b>t <b>e</b>a <b>reb</b>um. <b>Ste</b>t <b>cli</b>ta <b>kas</b>d <b>gubergr</b>en, <b>n</b>o <b>se</b>a <b>takima</b>ta <b>sanct</b>us <b>es</b>t <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t.';\n\n    expect(textVide(text, { sep: undefined })).toBe(expectedText);\n  });\n\n  it('pass strings with line break (w/ set options.sep to `__`)', () => {\n    const text = `\n    orem ipsum dolor sit amet, consetetur sadipscing elitr,\n    sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,\n    sed diam voluptua.`;\n\n    const expected = `\n    __ore__m __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr,\n    __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t,\n    __se__d __dia__m __volupt__ua.`;\n\n    expect(textVide(text, { sep: '__' })).toBe(expected);\n  });\n\n  it('invalid fixation point', () => {\n    const text =\n      'orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.';\n    const expectedText =\n      '<b>ore</b>m <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t, <b>consetet</b>ur <b>sadipsci</b>ng <b>eli</b>tr, <b>se</b>d <b>dia</b>m <b>nonu</b>my <b>eirm</b>od <b>temp</b>or <b>invidu</b>nt <b>u</b>t <b>labo</b>re <b>e</b>t <b>dolo</b>re <b>mag</b>na <b>aliquy</b>am <b>era</b>t, <b>se</b>d <b>dia</b>m <b>volupt</b>ua. <b>A</b>t <b>ver</b>o <b>eo</b>s <b>e</b>t <b>accus</b>am <b>e</b>t <b>jus</b>to <b>du</b>o <b>dolor</b>es <b>e</b>t <b>e</b>a <b>reb</b>um. <b>Ste</b>t <b>cli</b>ta <b>kas</b>d <b>gubergr</b>en, <b>n</b>o <b>se</b>a <b>takima</b>ta <b>sanct</b>us <b>es</b>t <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t. <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t, <b>consetet</b>ur <b>sadipsci</b>ng <b>eli</b>tr, <b>se</b>d <b>dia</b>m <b>nonu</b>my <b>eirm</b>od <b>temp</b>or <b>invidu</b>nt <b>u</b>t <b>labo</b>re <b>e</b>t <b>dolo</b>re <b>mag</b>na <b>aliquy</b>am <b>era</b>t, <b>se</b>d <b>dia</b>m <b>volupt</b>ua. <b>A</b>t <b>ver</b>o <b>eo</b>s <b>e</b>t <b>accus</b>am <b>e</b>t <b>jus</b>to <b>du</b>o <b>dolor</b>es <b>e</b>t <b>e</b>a <b>reb</b>um. <b>Ste</b>t <b>cli</b>ta <b>kas</b>d <b>gubergr</b>en, <b>n</b>o <b>se</b>a <b>takima</b>ta <b>sanct</b>us <b>es</b>t <b>Lor</b>em <b>ips</b>um <b>dol</b>or <b>si</b>t <b>ame</b>t.';\n\n    expect(textVide(text, { fixationPoint: -1 })).toBe(expectedText);\n  });\n\n  it('emojis', () => {\n    const text = '👆 h👆el👆lo there, this is s👆ome dummy text';\n    const expected =\n      '👆 h👆<b>e</b>l👆<b>l</b>o <b>the</b>re, <b>thi</b>s <b>i</b>s s👆<b>om</b>e <b>dum</b>my <b>tex</b>t';\n\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('ignoreHtmlTag: true', () => {\n    const text = '<div>abcd</div>efg';\n    const expectedText = '<div><b>abc</b>d</div><b>ef</b>g';\n    expect(textVide(text, { ignoreHtmlTag: true })).toBe(expectedText);\n  });\n\n  it('ignoreHtmlTag: false', () => {\n    const text = '<div>abcd</div>efg';\n    const expected = '<<b>di</b>v><b>abc</b>d</<b>di</b>v><b>ef</b>g';\n    expect(textVide(text, { ignoreHtmlTag: false })).toBe(expected);\n  });\n\n  it('ignoreHtmlEntity: true', () => {\n    const text = '&nbsp;abcd&gt;';\n    const expectedText = '&nbsp;<b>abc</b>d&gt;';\n    expect(textVide(text, { ignoreHtmlEntity: true })).toBe(expectedText);\n  });\n\n  it('ignoreHtmlEntity: false', () => {\n    const text = '&nbsp;abcd&gt;';\n    const expected = '&<b>nbs</b>p;<b>abc</b>d&<b>g</b>t;';\n    expect(textVide(text, { ignoreHtmlEntity: false })).toBe(expected);\n  });\n});\n\ndescribe('fixation point ([2, 5])', () => {\n  it('fixation 2 - 1', () => {\n    const text =\n      'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n    const expected =\n      '<b>Bion</b>ic <b>Readi</b>ng <b>i</b>s <b>a</b> <b>n</b>ew <b>meth</b>od <b>facilita</b>ting <b>t</b>he <b>readi</b>ng <b>proce</b>ss <b>b</b>y <b>guidi</b>ng <b>t</b>he <b>ey</b>es <b>throu</b>gh <b>te</b>xt <b>wi</b>th <b>artific</b>ial <b>fixat</b>ion <b>poin</b>ts. <b>A</b>s <b>a</b> <b>resu</b>lt, <b>t</b>he <b>read</b>er <b>i</b>s <b>on</b>ly <b>focus</b>ing <b>o</b>n <b>t</b>he <b>highlig</b>hted <b>initi</b>al <b>lette</b>rs <b>a</b>nd <b>le</b>ts <b>t</b>he <b>bra</b>in <b>cent</b>er <b>compl</b>ete <b>t</b>he <b>wo</b>rd. <b>I</b>n <b>a</b> <b>digit</b>al <b>wor</b>ld <b>domina</b>ted <b>b</b>y <b>shall</b>ow <b>for</b>ms <b>o</b>f <b>readi</b>ng, <b>Bion</b>ic <b>Readi</b>ng <b>ai</b>ms <b>t</b>o <b>encour</b>age <b>a</b> <b>mo</b>re <b>i</b>n-<b>dep</b>th <b>readi</b>ng <b>a</b>nd <b>understan</b>ding <b>o</b>f <b>writt</b>en <b>conte</b>nt.';\n    expect(textVide(text, { fixationPoint: 2 })).toBe(expected);\n  });\n  it('fixation 2 - 2', () => {\n    const text =\n      '__ore__m __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t. __Lor__em __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t.';\n    const expected =\n      '__<b>o</b>re__<b>m</b> __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__<b>t</b> __<b>a</b>me__<b>t</b>, __<b>conse</b>tet__<b>u</b>r __<b>sadip</b>sci__<b>n</b>g __<b>e</b>li__<b>t</b>r, __<b>s</b>e__<b>d</b> __<b>d</b>ia__<b>m</b> __<b>no</b>nu__<b>m</b>y __<b>ei</b>rm__<b>o</b>d __<b>te</b>mp__<b>o</b>r __<b>invi</b>du__<b>n</b>t __<b>u</b>__<b>t</b> __<b>la</b>bo__<b>r</b>e __<b>e</b>__<b>t</b> __<b>do</b>lo__<b>r</b>e __<b>m</b>ag__<b>n</b>a __<b>aliq</b>uy__<b>a</b>m __<b>e</b>ra__<b>t</b>, __<b>s</b>e__<b>d</b> __<b>d</b>ia__<b>m</b> __<b>volu</b>pt__<b>u</b>a. __<b>A</b>__<b>t</b> __<b>v</b>er__<b>o</b> __<b>e</b>o__<b>s</b> __<b>e</b>__<b>t</b> __<b>acc</b>us__<b>a</b>m __<b>e</b>__<b>t</b> __<b>j</b>us__<b>t</b>o __<b>d</b>u__<b>o</b> __<b>dol</b>or__<b>e</b>s __<b>e</b>__<b>t</b> __<b>e</b>__<b>a</b> __<b>r</b>eb__<b>u</b>m. __<b>S</b>te__<b>t</b> __<b>c</b>li__<b>t</b>a __<b>k</b>as__<b>d</b> __<b>guber</b>gr__<b>e</b>n, __<b>n</b>__<b>o</b> __<b>s</b>e__<b>a</b> __<b>taki</b>ma__<b>t</b>a __<b>san</b>ct__<b>u</b>s __<b>e</b>s__<b>t</b> __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__<b>t</b> __<b>a</b>me__<b>t</b>. __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__<b>t</b> __<b>a</b>me__<b>t</b>, __<b>conse</b>tet__<b>u</b>r __<b>sadip</b>sci__<b>n</b>g __<b>e</b>li__<b>t</b>r, __<b>s</b>e__<b>d</b> __<b>d</b>ia__<b>m</b> __<b>no</b>nu__<b>m</b>y __<b>ei</b>rm__<b>o</b>d __<b>te</b>mp__<b>o</b>r __<b>invi</b>du__<b>n</b>t __<b>u</b>__<b>t</b> __<b>la</b>bo__<b>r</b>e __<b>e</b>__<b>t</b> __<b>do</b>lo__<b>r</b>e __<b>m</b>ag__<b>n</b>a __<b>aliq</b>uy__<b>a</b>m __<b>e</b>ra__<b>t</b>, __<b>s</b>e__<b>d</b> __<b>d</b>ia__<b>m</b> __<b>volu</b>pt__<b>u</b>a. __<b>A</b>__<b>t</b> __<b>v</b>er__<b>o</b> __<b>e</b>o__<b>s</b> __<b>e</b>__<b>t</b> __<b>acc</b>us__<b>a</b>m __<b>e</b>__<b>t</b> __<b>j</b>us__<b>t</b>o __<b>d</b>u__<b>o</b> __<b>dol</b>or__<b>e</b>s __<b>e</b>__<b>t</b> __<b>e</b>__<b>a</b> __<b>r</b>eb__<b>u</b>m. __<b>S</b>te__<b>t</b> __<b>c</b>li__<b>t</b>a __<b>k</b>as__<b>d</b> __<b>guber</b>gr__<b>e</b>n, __<b>n</b>__<b>o</b> __<b>s</b>e__<b>a</b> __<b>taki</b>ma__<b>t</b>a __<b>san</b>ct__<b>u</b>s __<b>e</b>s__<b>t</b> __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__<b>t</b> __<b>a</b>me__<b>t</b>.';\n    expect(textVide(text, { fixationPoint: 2 })).toBe(expected);\n  });\n\n  it('fixation 3 - 1', () => {\n    const text =\n      'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n    const expected =\n      '<b>Bio</b>nic <b>Read</b>ing <b>i</b>s <b>a</b> <b>n</b>ew <b>met</b>hod <b>facili</b>tating <b>t</b>he <b>read</b>ing <b>proc</b>ess <b>b</b>y <b>guid</b>ing <b>t</b>he <b>ey</b>es <b>thro</b>ugh <b>te</b>xt <b>wi</b>th <b>artif</b>icial <b>fixa</b>tion <b>poi</b>nts. <b>A</b>s <b>a</b> <b>res</b>ult, <b>t</b>he <b>rea</b>der <b>i</b>s <b>on</b>ly <b>focu</b>sing <b>o</b>n <b>t</b>he <b>highli</b>ghted <b>init</b>ial <b>lett</b>ers <b>a</b>nd <b>le</b>ts <b>t</b>he <b>bra</b>in <b>cen</b>ter <b>comp</b>lete <b>t</b>he <b>wo</b>rd. <b>I</b>n <b>a</b> <b>digi</b>tal <b>wor</b>ld <b>domin</b>ated <b>b</b>y <b>shal</b>low <b>for</b>ms <b>o</b>f <b>read</b>ing, <b>Bio</b>nic <b>Read</b>ing <b>ai</b>ms <b>t</b>o <b>encou</b>rage <b>a</b> <b>mo</b>re <b>i</b>n-<b>dep</b>th <b>read</b>ing <b>a</b>nd <b>underst</b>anding <b>o</b>f <b>writ</b>ten <b>cont</b>ent.';\n    expect(textVide(text, { fixationPoint: 3 })).toBe(expected);\n  });\n  it('fixation 3 - 2', () => {\n    const text =\n      '__ore__m __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t. __Lor__em __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t.';\n    const expected =\n      '__<b>o</b>re__<b>m</b> __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__<b>t</b> __<b>a</b>me__<b>t</b>, __<b>cons</b>etet__<b>u</b>r __<b>sadi</b>psci__<b>n</b>g __<b>e</b>li__<b>t</b>r, __<b>s</b>e__<b>d</b> __<b>d</b>ia__<b>m</b> __<b>no</b>nu__<b>m</b>y __<b>ei</b>rm__<b>o</b>d __<b>te</b>mp__<b>o</b>r __<b>inv</b>idu__<b>n</b>t __<b>u</b>__<b>t</b> __<b>la</b>bo__<b>r</b>e __<b>e</b>__<b>t</b> __<b>do</b>lo__<b>r</b>e __<b>m</b>ag__<b>n</b>a __<b>ali</b>quy__<b>a</b>m __<b>e</b>ra__<b>t</b>, __<b>s</b>e__<b>d</b> __<b>d</b>ia__<b>m</b> __<b>vol</b>upt__<b>u</b>a. __<b>A</b>__<b>t</b> __<b>v</b>er__<b>o</b> __<b>e</b>o__<b>s</b> __<b>e</b>__<b>t</b> __<b>acc</b>us__<b>a</b>m __<b>e</b>__<b>t</b> __<b>j</b>us__<b>t</b>o __<b>d</b>u__<b>o</b> __<b>dol</b>or__<b>e</b>s __<b>e</b>__<b>t</b> __<b>e</b>__<b>a</b> __<b>r</b>eb__<b>u</b>m. __<b>S</b>te__<b>t</b> __<b>c</b>li__<b>t</b>a __<b>k</b>as__<b>d</b> __<b>gube</b>rgr__<b>e</b>n, __<b>n</b>__<b>o</b> __<b>s</b>e__<b>a</b> __<b>tak</b>ima__<b>t</b>a __<b>san</b>ct__<b>u</b>s __<b>e</b>s__<b>t</b> __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__<b>t</b> __<b>a</b>me__<b>t</b>. __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__<b>t</b> __<b>a</b>me__<b>t</b>, __<b>cons</b>etet__<b>u</b>r __<b>sadi</b>psci__<b>n</b>g __<b>e</b>li__<b>t</b>r, __<b>s</b>e__<b>d</b> __<b>d</b>ia__<b>m</b> __<b>no</b>nu__<b>m</b>y __<b>ei</b>rm__<b>o</b>d __<b>te</b>mp__<b>o</b>r __<b>inv</b>idu__<b>n</b>t __<b>u</b>__<b>t</b> __<b>la</b>bo__<b>r</b>e __<b>e</b>__<b>t</b> __<b>do</b>lo__<b>r</b>e __<b>m</b>ag__<b>n</b>a __<b>ali</b>quy__<b>a</b>m __<b>e</b>ra__<b>t</b>, __<b>s</b>e__<b>d</b> __<b>d</b>ia__<b>m</b> __<b>vol</b>upt__<b>u</b>a. __<b>A</b>__<b>t</b> __<b>v</b>er__<b>o</b> __<b>e</b>o__<b>s</b> __<b>e</b>__<b>t</b> __<b>acc</b>us__<b>a</b>m __<b>e</b>__<b>t</b> __<b>j</b>us__<b>t</b>o __<b>d</b>u__<b>o</b> __<b>dol</b>or__<b>e</b>s __<b>e</b>__<b>t</b> __<b>e</b>__<b>a</b> __<b>r</b>eb__<b>u</b>m. __<b>S</b>te__<b>t</b> __<b>c</b>li__<b>t</b>a __<b>k</b>as__<b>d</b> __<b>gube</b>rgr__<b>e</b>n, __<b>n</b>__<b>o</b> __<b>s</b>e__<b>a</b> __<b>tak</b>ima__<b>t</b>a __<b>san</b>ct__<b>u</b>s __<b>e</b>s__<b>t</b> __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__<b>t</b> __<b>a</b>me__<b>t</b>.';\n    expect(textVide(text, { fixationPoint: 3 })).toBe(expected);\n  });\n\n  it('fixation 4 - 1', () => {\n    const text =\n      'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n    const expected =\n      '<b>Bi</b>onic <b>Re</b>ading <b>i</b>s a <b>n</b>ew <b>me</b>thod <b>faci</b>litating <b>t</b>he <b>re</b>ading <b>pr</b>ocess <b>b</b>y <b>gu</b>iding <b>t</b>he <b>ey</b>es <b>th</b>rough <b>te</b>xt <b>wi</b>th <b>art</b>ificial <b>fix</b>ation <b>po</b>ints. <b>A</b>s a <b>re</b>sult, <b>t</b>he <b>re</b>ader <b>i</b>s <b>on</b>ly <b>foc</b>using <b>o</b>n <b>t</b>he <b>high</b>lighted <b>in</b>itial <b>le</b>tters <b>a</b>nd <b>le</b>ts <b>t</b>he <b>br</b>ain <b>ce</b>nter <b>com</b>plete <b>t</b>he <b>wo</b>rd. <b>I</b>n a <b>di</b>gital <b>wo</b>rld <b>dom</b>inated <b>b</b>y <b>sh</b>allow <b>fo</b>rms <b>o</b>f <b>re</b>ading, <b>Bi</b>onic <b>Re</b>ading <b>ai</b>ms <b>t</b>o <b>enc</b>ourage a <b>mo</b>re <b>i</b>n-<b>de</b>pth <b>re</b>ading <b>a</b>nd <b>under</b>standing <b>o</b>f <b>wr</b>itten <b>co</b>ntent.';\n    expect(textVide(text, { fixationPoint: 4 })).toBe(expected);\n  });\n  it('fixation 4 - 2', () => {\n    const text =\n      '__ore__m __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t. __Lor__em __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t.';\n    const expected =\n      '__<b>o</b>re__m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__t __<b>a</b>me__t, __<b>con</b>setet__<b>u</b>r __<b>sad</b>ipsci__<b>n</b>g __<b>e</b>li__<b>t</b>r, __<b>s</b>e__d __<b>d</b>ia__m __<b>no</b>nu__<b>m</b>y __<b>ei</b>rm__<b>o</b>d __<b>te</b>mp__<b>o</b>r __<b>in</b>vidu__<b>n</b>t __u__t __<b>la</b>bo__<b>r</b>e __e__t __<b>do</b>lo__<b>r</b>e __<b>m</b>ag__<b>n</b>a __<b>al</b>iquy__<b>a</b>m __<b>e</b>ra__t, __<b>s</b>e__d __<b>d</b>ia__m __<b>vo</b>lupt__<b>u</b>a. __A__t __<b>v</b>er__o __<b>e</b>o__s __e__t __<b>ac</b>cus__<b>a</b>m __e__t __<b>j</b>us__<b>t</b>o __<b>d</b>u__o __<b>do</b>lor__<b>e</b>s __e__t __e__a __<b>r</b>eb__<b>u</b>m. __<b>S</b>te__t __<b>c</b>li__<b>t</b>a __<b>k</b>as__d __<b>gu</b>bergr__<b>e</b>n, __n__o __<b>s</b>e__a __<b>ta</b>kima__<b>t</b>a __<b>sa</b>nct__<b>u</b>s __<b>e</b>s__t __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__t __<b>a</b>me__t. __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__t __<b>a</b>me__t, __<b>con</b>setet__<b>u</b>r __<b>sad</b>ipsci__<b>n</b>g __<b>e</b>li__<b>t</b>r, __<b>s</b>e__d __<b>d</b>ia__m __<b>no</b>nu__<b>m</b>y __<b>ei</b>rm__<b>o</b>d __<b>te</b>mp__<b>o</b>r __<b>in</b>vidu__<b>n</b>t __u__t __<b>la</b>bo__<b>r</b>e __e__t __<b>do</b>lo__<b>r</b>e __<b>m</b>ag__<b>n</b>a __<b>al</b>iquy__<b>a</b>m __<b>e</b>ra__t, __<b>s</b>e__d __<b>d</b>ia__m __<b>vo</b>lupt__<b>u</b>a. __A__t __<b>v</b>er__o __<b>e</b>o__s __e__t __<b>ac</b>cus__<b>a</b>m __e__t __<b>j</b>us__<b>t</b>o __<b>d</b>u__o __<b>do</b>lor__<b>e</b>s __e__t __e__a __<b>r</b>eb__<b>u</b>m. __<b>S</b>te__t __<b>c</b>li__<b>t</b>a __<b>k</b>as__d __<b>gu</b>bergr__<b>e</b>n, __n__o __<b>s</b>e__a __<b>ta</b>kima__<b>t</b>a __<b>sa</b>nct__<b>u</b>s __<b>e</b>s__t __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__t __<b>a</b>me__t.';\n    expect(textVide(text, { fixationPoint: 4 })).toBe(expected);\n  });\n\n  it('fixation 5 - 1', () => {\n    const text =\n      'Bionic Reading is a new method facilitating the reading process by guiding the eyes through text with artificial fixation points. As a result, the reader is only focusing on the highlighted initial letters and lets the brain center complete the word. In a digital world dominated by shallow forms of reading, Bionic Reading aims to encourage a more in-depth reading and understanding of written content.';\n    const expected =\n      '<b>Bi</b>onic <b>Re</b>ading <b>i</b>s a <b>n</b>ew <b>me</b>thod <b>fac</b>ilitating <b>t</b>he <b>re</b>ading <b>pr</b>ocess <b>b</b>y <b>gu</b>iding <b>t</b>he <b>e</b>yes <b>th</b>rough <b>t</b>ext <b>w</b>ith <b>art</b>ificial <b>fi</b>xation <b>po</b>ints. <b>A</b>s a <b>re</b>sult, <b>t</b>he <b>re</b>ader <b>i</b>s <b>o</b>nly <b>fo</b>cusing <b>o</b>n <b>t</b>he <b>hig</b>hlighted <b>in</b>itial <b>le</b>tters <b>a</b>nd <b>l</b>ets <b>t</b>he <b>br</b>ain <b>ce</b>nter <b>co</b>mplete <b>t</b>he <b>w</b>ord. <b>I</b>n a <b>di</b>gital <b>wo</b>rld <b>do</b>minated <b>b</b>y <b>sh</b>allow <b>fo</b>rms <b>o</b>f <b>re</b>ading, <b>Bi</b>onic <b>Re</b>ading <b>a</b>ims <b>t</b>o <b>en</b>courage a <b>m</b>ore <b>i</b>n-<b>de</b>pth <b>re</b>ading <b>a</b>nd <b>und</b>erstanding <b>o</b>f <b>wr</b>itten <b>co</b>ntent.';\n    expect(textVide(text, { fixationPoint: 5 })).toBe(expected);\n  });\n  it('fixation 5 - 2', () => {\n    const text =\n      '__ore__m __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t. __Lor__em __ips__um __dol__or __si__t __ame__t, __consetet__ur __sadipsci__ng __eli__tr, __se__d __dia__m __nonu__my __eirm__od __temp__or __invidu__nt __u__t __labo__re __e__t __dolo__re __mag__na __aliquy__am __era__t, __se__d __dia__m __volupt__ua. __A__t __ver__o __eo__s __e__t __accus__am __e__t __jus__to __du__o __dolor__es __e__t __e__a __reb__um. __Ste__t __cli__ta __kas__d __gubergr__en, __n__o __se__a __takima__ta __sanct__us __es__t __Lor__em __ips__um __dol__or __si__t __ame__t.';\n    const expected =\n      '__<b>o</b>re__m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__t __<b>a</b>me__t, __<b>co</b>nsetet__<b>u</b>r __<b>sa</b>dipsci__<b>n</b>g __<b>e</b>li__<b>t</b>r, __<b>s</b>e__d __<b>d</b>ia__m __<b>n</b>onu__<b>m</b>y __<b>e</b>irm__<b>o</b>d __<b>t</b>emp__<b>o</b>r __<b>in</b>vidu__<b>n</b>t __u__t __<b>l</b>abo__<b>r</b>e __e__t __<b>d</b>olo__<b>r</b>e __<b>m</b>ag__<b>n</b>a __<b>al</b>iquy__<b>a</b>m __<b>e</b>ra__t, __<b>s</b>e__d __<b>d</b>ia__m __<b>vo</b>lupt__<b>u</b>a. __A__t __<b>v</b>er__o __<b>e</b>o__s __e__t __<b>ac</b>cus__<b>a</b>m __e__t __<b>j</b>us__<b>t</b>o __<b>d</b>u__o __<b>do</b>lor__<b>e</b>s __e__t __e__a __<b>r</b>eb__<b>u</b>m. __<b>S</b>te__t __<b>c</b>li__<b>t</b>a __<b>k</b>as__d __<b>gu</b>bergr__<b>e</b>n, __n__o __<b>s</b>e__a __<b>ta</b>kima__<b>t</b>a __<b>sa</b>nct__<b>u</b>s __<b>e</b>s__t __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__t __<b>a</b>me__t. __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__t __<b>a</b>me__t, __<b>co</b>nsetet__<b>u</b>r __<b>sa</b>dipsci__<b>n</b>g __<b>e</b>li__<b>t</b>r, __<b>s</b>e__d __<b>d</b>ia__m __<b>n</b>onu__<b>m</b>y __<b>e</b>irm__<b>o</b>d __<b>t</b>emp__<b>o</b>r __<b>in</b>vidu__<b>n</b>t __u__t __<b>l</b>abo__<b>r</b>e __e__t __<b>d</b>olo__<b>r</b>e __<b>m</b>ag__<b>n</b>a __<b>al</b>iquy__<b>a</b>m __<b>e</b>ra__t, __<b>s</b>e__d __<b>d</b>ia__m __<b>vo</b>lupt__<b>u</b>a. __A__t __<b>v</b>er__o __<b>e</b>o__s __e__t __<b>ac</b>cus__<b>a</b>m __e__t __<b>j</b>us__<b>t</b>o __<b>d</b>u__o __<b>do</b>lor__<b>e</b>s __e__t __e__a __<b>r</b>eb__<b>u</b>m. __<b>S</b>te__t __<b>c</b>li__<b>t</b>a __<b>k</b>as__d __<b>gu</b>bergr__<b>e</b>n, __n__o __<b>s</b>e__a __<b>ta</b>kima__<b>t</b>a __<b>sa</b>nct__<b>u</b>s __<b>e</b>s__t __<b>L</b>or__<b>e</b>m __<b>i</b>ps__<b>u</b>m __<b>d</b>ol__<b>o</b>r __<b>s</b>i__t __<b>a</b>me__t.';\n    expect(textVide(text, { fixationPoint: 5 })).toBe(expected);\n  });\n});\n\ndescribe('numbers', () => {\n  it('1234567890', () => {\n    const text = '1234567890';\n    const expected = '1234567890';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('1234-567890', () => {\n    const text = '1234-567890';\n    const expected = '1234-567890';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('a1234567890', () => {\n    const text = 'a1234567890';\n    const expected = '<b>a12345678</b>90';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('1234567890a', () => {\n    const text = '1234567890a';\n    const expected = '<b>123456789</b>0a';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('1234a567890', () => {\n    const text = '1234a567890';\n    const expected = '<b>1234a5678</b>90';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('@1234567890', () => {\n    const text = '@1234567890';\n    const expected = '@1234567890';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('1234567890@', () => {\n    const text = '1234567890@';\n    const expected = '1234567890@';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('1234@567890', () => {\n    const text = '1234@567890';\n    const expected = '1234@567890';\n    expect(textVide(text)).toBe(expected);\n  });\n});\n\ndescribe('with html tags', () => {\n  it('normal text', () => {\n    const text = 'abcdefg';\n    const expected = '<b>abcde</b>fg';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('with a tag', () => {\n    const text = '<a>abcd</a>efg';\n    const expected = '<a><b>abc</b>d</a><b>ef</b>g';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('with b tag', () => {\n    const text = '<b>abcd</b>efg';\n    const expected = '<b><b>abc</b>d</b><b>ef</b>g';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('with div tag', () => {\n    const text = '<div>abcd</div>efg';\n    const expected = '<div><b>abc</b>d</div><b>ef</b>g';\n    expect(textVide(text)).toBe(expected);\n  });\n\n  it('complex html tags', () => {\n    const text = `<div class=\"bionic-reader-container\">\n            \n            \n    <span class=\"w bionic\"><b class=\"b bionic\">nor</b>mal </span><span class=\"w bionic\"><b class=\"b bionic\">te</b>xt</span>: <span class=\"w bionic\"><b class=\"b bionic\">abcd</b>efg</span><br><span class=\"w bionic\"><b class=\"b bionic\">wi</b>th </span><span class=\"w bionic\"><b class=\"b bionic\">a</b> </span><span class=\"w bionic\"><b class=\"b bionic\">t</b>ag</span>: <a target=\"_blank\"><span class=\"w bionic\"><b class=\"b bionic\">ab</b>cd</span></a><span class=\"w bionic\"><b class=\"b bionic\">e</b>fg</span><br><span class=\"w bionic\"><b class=\"b bionic\">wi</b>th </span><span class=\"w bionic\"><b class=\"b bionic\">b</b> </span><span class=\"w bionic\"><b class=\"b bionic\">t</b>ag</span>: <b><span class=\"w bionic\"><b class=\"b bionic\">ab</b>cd</span></b><span class=\"w bionic\"><b class=\"b bionic\">e</b>fg</span><br><span class=\"w bionic\"><b class=\"b bionic\">wi</b>th </span><span class=\"w bionic\"><b class=\"b bionic\">d</b>iv </span><span class=\"w bionic\"><b class=\"b bionic\">t</b>ag</span>: <div><span class=\"w bionic\"><b class=\"b bionic\">ab</b>cd</span></div><span class=\"w bionic\"><b class=\"b bionic\">e</b>fg</span><br>\n    \n        <!-- <div class=\"br-foot-node\">\n            <p style=\"margin: 32px 0 32px 70px; font-weight: 700; font-size: 26px; line-height: 1.6em;\">\n                —\n            </p>\n            <p>\n                Bionic Reading<sup>®</sup><br>\n                A higher dimension of reading.<br>\n                <a href=\"https://bionic-reading.com\">bionic-reading.com</a>\n            </p>\n            <br/>\n            <br/>\n            <p>\n                \n            </p>\n        </div> -->\n    \n</div>`;\n\n    const expected = `<div class=\"bionic-reader-container\">\n            \n            \n    <span class=\"w bionic\"><b class=\"b bionic\"><b>no</b>r</b><b>ma</b>l </span><span class=\"w bionic\"><b class=\"b bionic\"><b>t</b>e</b><b>x</b>t</span>: <span class=\"w bionic\"><b class=\"b bionic\"><b>abc</b>d</b><b>ef</b>g</span><br><span class=\"w bionic\"><b class=\"b bionic\"><b>w</b>i</b><b>t</b>h </span><span class=\"w bionic\"><b class=\"b bionic\">a</b> </span><span class=\"w bionic\"><b class=\"b bionic\">t</b><b>a</b>g</span>: <a target=\"_blank\"><span class=\"w bionic\"><b class=\"b bionic\"><b>a</b>b</b><b>c</b>d</span></a><span class=\"w bionic\"><b class=\"b bionic\">e</b><b>f</b>g</span><br><span class=\"w bionic\"><b class=\"b bionic\"><b>w</b>i</b><b>t</b>h </span><span class=\"w bionic\"><b class=\"b bionic\">b</b> </span><span class=\"w bionic\"><b class=\"b bionic\">t</b><b>a</b>g</span>: <b><span class=\"w bionic\"><b class=\"b bionic\"><b>a</b>b</b><b>c</b>d</span></b><span class=\"w bionic\"><b class=\"b bionic\">e</b><b>f</b>g</span><br><span class=\"w bionic\"><b class=\"b bionic\"><b>w</b>i</b><b>t</b>h </span><span class=\"w bionic\"><b class=\"b bionic\">d</b><b>i</b>v </span><span class=\"w bionic\"><b class=\"b bionic\">t</b><b>a</b>g</span>: <div><span class=\"w bionic\"><b class=\"b bionic\"><b>a</b>b</b><b>c</b>d</span></div><span class=\"w bionic\"><b class=\"b bionic\">e</b><b>f</b>g</span><br>\n    \n        <!-- <div class=\"br-foot-node\">\n            <p style=\"margin: 32px 0 32px 70px; font-weight: 700; font-size: 26px; line-height: 1.6em;\">\n                —\n            </p>\n            <p>\n                Bionic Reading<sup>®</sup><br>\n                A higher dimension of reading.<br>\n                <a href=\"https://bionic-reading.com\">bionic-reading.com</a>\n            </p>\n            <br/>\n            <br/>\n            <p>\n                \n            </p>\n        </div> -->\n    \n</div>`;\n\n    expect(textVide(text)).toBe(expected);\n  });\n});\n"
  },
  {
    "path": "packages/text-vide/src/getFixationLength.ts",
    "content": "const FIXATION_BOUNDARY_LIST = [\n  [0, 4, 12, 17, 24, 29, 35, 42, 48],\n  [1, 2, 7, 10, 13, 14, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49],\n  [\n    1, 2, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39,\n    41, 43, 45, 47, 49,\n  ],\n  [\n    0, 2, 4, 5, 6, 8, 9, 11, 14, 15, 17, 18, 20, 0, 21, 23, 24, 26, 27, 29, 30,\n    32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 47, 48,\n  ],\n  [\n    0, 2, 3, 5, 6, 7, 8, 10, 11, 12, 14, 15, 17, 19, 20, 21, 23, 24, 25, 26, 28,\n    29, 30, 32, 33, 34, 35, 37, 38, 39, 41, 42, 43, 44, 46, 47, 48,\n  ],\n];\n\n// TODO: caching\nexport default (word: string, fixationPoint: number) => {\n  const { length: wordLength } = word;\n  const fixationBoundary =\n    FIXATION_BOUNDARY_LIST[fixationPoint - 1] ?? FIXATION_BOUNDARY_LIST[0];\n\n  const fixationLengthFromLast = fixationBoundary.findIndex(\n    boundary => wordLength <= boundary,\n  );\n\n  let fixationLength = wordLength - fixationLengthFromLast;\n\n  if (fixationLengthFromLast === -1) {\n    fixationLength = wordLength - fixationBoundary.length;\n  }\n\n  return Math.max(fixationLength, 0);\n};\n"
  },
  {
    "path": "packages/text-vide/src/getHighlightedText.ts",
    "content": "export default (text: string, sep: string | string[]) => {\n  if (typeof sep === 'string') {\n    return `${sep}${text}${sep}`;\n  }\n\n  return `${sep[0]}${text}${sep[1]}`;\n};\n"
  },
  {
    "path": "packages/text-vide/src/getOptions.ts",
    "content": "import { Options } from './types';\nimport defaults from 'utils/defaults';\n\nconst DEFAULT_SEP = ['<b>', '</b>'];\nconst DEFAULT_FIXATION_POINT = 1;\nconst DEFAULT_IGNORE_HTML_TAG = true;\nconst DEFAULT_IGNORE_HTML_ENTITY = true;\n\nexport default (maybeOptions: Partial<Options>): Options =>\n  defaults(maybeOptions, {\n    sep: DEFAULT_SEP,\n    fixationPoint: DEFAULT_FIXATION_POINT,\n    ignoreHtmlTag: DEFAULT_IGNORE_HTML_TAG,\n    ignoreHtmlEntity: DEFAULT_IGNORE_HTML_ENTITY,\n  });\n"
  },
  {
    "path": "packages/text-vide/src/index.ts",
    "content": "import { Options } from './types';\nimport getOptions from './getOptions';\nimport getFixationLength from './getFixationLength';\nimport getHighlightedText from './getHighlightedText';\nimport { useCheckIsHtmlTag } from './useCheckIsHtmlTag';\nimport { useCheckIsHtmlEntity } from './useCheckIsHtmlEntity';\n\nconst CONVERTIBLE_REGEX = /(\\p{L}|\\p{Nd})*\\p{L}(\\p{L}|\\p{Nd})*/gu;\n\nexport const textVide = (text: string, maybeOptions: Partial<Options> = {}) => {\n  if (!text?.length) {\n    return '';\n  }\n\n  const { fixationPoint, sep, ignoreHtmlTag, ignoreHtmlEntity } =\n    getOptions(maybeOptions);\n  const convertibleMatchList = Array.from(text.matchAll(CONVERTIBLE_REGEX));\n\n  let result = '';\n  let lastMatchedIndex = 0;\n\n  let checkIsHtmlTag: ReturnType<typeof useCheckIsHtmlTag> | undefined;\n  if (ignoreHtmlTag) {\n    checkIsHtmlTag = useCheckIsHtmlTag(text);\n  }\n\n  let checkIsHtmlEntity: ReturnType<typeof useCheckIsHtmlEntity> | undefined;\n  if (ignoreHtmlEntity) {\n    checkIsHtmlEntity = useCheckIsHtmlEntity(text);\n  }\n\n  for (const match of convertibleMatchList) {\n    const isHtmlTag = checkIsHtmlTag?.(match);\n    if (isHtmlTag) {\n      continue;\n    }\n\n    const isHtmlEntity = checkIsHtmlEntity?.(match);\n    if (isHtmlEntity) {\n      continue;\n    }\n\n    const [matchedWord] = match;\n    const startIndex = match.index!;\n    const endIndex = startIndex + getFixationLength(matchedWord, fixationPoint);\n\n    const plainText = text.slice(lastMatchedIndex, startIndex);\n    result += plainText;\n\n    if (startIndex !== endIndex) {\n      result += getHighlightedText(text.slice(startIndex, endIndex), sep);\n    }\n\n    lastMatchedIndex = endIndex;\n  }\n\n  const remainText = text.slice(lastMatchedIndex);\n  return result + remainText;\n};\n"
  },
  {
    "path": "packages/text-vide/src/types.ts",
    "content": "export type Options = {\n  sep: string | string[]; // default: ['<b>', '</b>']\n  fixationPoint: number; // default: 1\n  ignoreHtmlTag: boolean; // default: true\n  ignoreHtmlEntity: boolean; // default: true\n};\n"
  },
  {
    "path": "packages/text-vide/src/useCheckIsHtmlEntity.ts",
    "content": "import { extractMatchRangeList } from './utils';\n\nconst HTML_ENTITY_REGEX = /&[\\w#]+;/g;\n\nexport const useCheckIsHtmlEntity = (text: string) => {\n  const htmlEntityMatchList = text.matchAll(HTML_ENTITY_REGEX);\n  const htmlEntityRangeList = extractMatchRangeList(htmlEntityMatchList);\n  const reversedHtmlEntityRangeList = htmlEntityRangeList.reverse();\n\n  return (match: RegExpMatchArray) => {\n    const startIndex = match.index!;\n    const entityRange = reversedHtmlEntityRangeList.find(\n      ([rangeStart]) => startIndex > rangeStart,\n    );\n\n    if (!entityRange) {\n      return false;\n    }\n\n    const [, rangeEnd] = entityRange;\n    const isInclude = startIndex < rangeEnd;\n    return isInclude;\n  };\n};\n"
  },
  {
    "path": "packages/text-vide/src/useCheckIsHtmlTag.ts",
    "content": "import { extractMatchRangeList } from './utils';\n\nconst HTML_TAG_REGEX = /<!--[^]*?-->|<[^>]+>/g;\n\nexport const useCheckIsHtmlTag = (text: string) => {\n  const htmlTagMatchList = text.matchAll(HTML_TAG_REGEX);\n  const htmlTagRangeList = extractMatchRangeList(htmlTagMatchList);\n  const reversedHtmlTagRangeList = htmlTagRangeList.reverse();\n\n  return (match: RegExpMatchArray) => {\n    const startIndex = match.index!;\n    const tagRange = reversedHtmlTagRangeList.find(\n      ([rangeStart]) => startIndex > rangeStart,\n    );\n\n    if (!tagRange) {\n      return false;\n    }\n\n    const [, rangeEnd] = tagRange;\n    const isInclude = startIndex < rangeEnd;\n    return isInclude;\n  };\n};\n"
  },
  {
    "path": "packages/text-vide/src/utils.ts",
    "content": "export const extractMatchRangeList = (\n  matchList: IterableIterator<RegExpMatchArray>,\n) =>\n  Array.from(matchList).map(match => {\n    const startIndex = match.index!;\n    const [matchedWord] = match;\n    const { length: matchedWordLength } = matchedWord;\n\n    return [startIndex, startIndex + matchedWordLength - 1];\n  });\n"
  },
  {
    "path": "packages/text-vide/tsconfig.json",
    "content": "{\n  \"extends\": \"tsconfig/base.json\",\n  \"include\": [\n    \"src\"\n  ]\n}"
  },
  {
    "path": "packages/text-vide/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport { resolve } from 'path';\nimport dts from 'vite-plugin-dts';\n\nexport default defineConfig({\n  build: {\n    lib: {\n      entry: resolve(__dirname, './src/index.ts'),\n      formats: ['es', 'cjs', 'iife'],\n      name: 'textVide',\n      fileName: 'index',\n    },\n  },\n  plugins: [dts()],\n});\n"
  },
  {
    "path": "packages/text-vide/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    coverage: {\n      reporter: ['text', 'lcov', 'html'],\n    },\n  },\n});\n"
  },
  {
    "path": "packages/tsconfig/base.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"compilerOptions\": {\n    \"target\": \"ESNext\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"allowJs\": false,\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"experimentalDecorators\": true,\n    \"isolatedModules\": true,\n    \"esModuleInterop\": true,\n    \"removeComments\": false,\n    \"noLib\": false\n  },\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "packages/tsconfig/package.json",
    "content": "{\n  \"name\": \"tsconfig\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"files\": [\n    \"base.json\",\n    \"vite.json\"\n  ]\n}\n"
  },
  {
    "path": "packages/tsconfig/vite.json",
    "content": "{\n  \"$schema\": \"https://json.schemastore.org/tsconfig\",\n  \"extends\": \"./base.json\",\n  \"compilerOptions\": {\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ESNext\", \"DOM\"],\n    \"sourceMap\": true,\n    \"resolveJsonModule\": true,\n    \"noEmit\": true,\n    \"noUnusedParameters\": true,\n    \"noImplicitReturns\": true,\n    \"skipLibCheck\": true\n  }\n}\n"
  },
  {
    "path": "packages/utils/__tests__/defaults.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport defaults from '../defaults';\n\ndescribe('test defaultValue util function', () => {\n  it('tests including default values', () => {\n    const defaultValue = {\n      a: 1,\n      b: '2',\n      c: false,\n    };\n\n    const value = {\n      a: 3,\n      c: true,\n    };\n\n    const expected = {\n      a: 3,\n      b: '2',\n      c: true,\n    };\n\n    expect(defaults(value, defaultValue)).toEqual(expected);\n  });\n\n  it('pass empty object', () => {\n    expect(defaults({}, {})).toEqual({});\n  });\n\n  it('pass empty default object', () => {\n    expect(defaults({ a: 1 }, {})).toEqual({ a: 1 });\n  });\n\n  it('pass empty value object', () => {\n    expect(defaults({}, { a: 1 })).toEqual({ a: 1 });\n  });\n});\n"
  },
  {
    "path": "packages/utils/__tests__/isEmpty.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport isEmpty from '../isEmpty';\n\ndescribe('test isEmptyString util', () => {\n  it('pass not empty string', () => {\n    expect(isEmpty('NOT_EMPTY')).toBeFalsy();\n  });\n\n  it('pass empty string', () => {\n    expect(isEmpty('')).toBeTruthy();\n  });\n\n  it('pass null value', () => {\n    expect(isEmpty(null)).toBeTruthy();\n  });\n\n  it('pass undefined value', () => {\n    expect(isEmpty(undefined)).toBeTruthy();\n  });\n});\n"
  },
  {
    "path": "packages/utils/__tests__/omitBy.test.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport omitBy from '../omitBy';\n\ndescribe('test omitBy util function', () => {\n  it('omit number values', () => {\n    const symbolValue = Symbol();\n\n    const obj = {\n      a: 1,\n      b: '2',\n      c: false,\n      d: undefined,\n      e: null,\n      f: symbolValue,\n    };\n\n    const omitted = omitBy(obj, value => typeof value === 'number');\n\n    expect(omitted).toEqual({\n      b: '2',\n      c: false,\n      d: undefined,\n      e: null,\n      f: symbolValue,\n    });\n  });\n\n  it('omit empty string', () => {\n    const obj = {\n      a: '1',\n      b: '2',\n      c: '',\n      d: undefined,\n      e: null,\n    };\n\n    const omitted = omitBy(obj, value => value === '');\n\n    expect(omitted).toEqual({\n      a: '1',\n      b: '2',\n      d: undefined,\n      e: null,\n    });\n  });\n});\n"
  },
  {
    "path": "packages/utils/defaults.ts",
    "content": "import isEmpty from './isEmpty';\nimport omitBy from './omitBy';\n\nexport default <T>(origin: Partial<T>, defaultValue: T): T => ({\n  ...defaultValue,\n  ...omitBy(origin, isEmpty),\n});\n"
  },
  {
    "path": "packages/utils/isEmpty.ts",
    "content": "export default (value: unknown) =>\n  value === undefined || value === null || value === '';\n"
  },
  {
    "path": "packages/utils/omitBy.ts",
    "content": "export default <T extends object>(\n  obj: T,\n  omitFilter: (value: T[keyof typeof obj]) => boolean,\n) => {\n  const keyList = Object.keys(obj) as (keyof typeof obj)[];\n  return keyList.reduce((obj, key) => {\n    if (omitFilter(obj[key])) {\n      delete obj[key];\n    }\n\n    return obj;\n  }, obj);\n};\n"
  },
  {
    "path": "packages/utils/package.json",
    "content": "{\n  \"name\": \"utils\",\n  \"version\": \"0.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"test\": \"vitest run\"\n  },\n  \"files\": [\n    \"./\"\n  ]\n}\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - 'apps/*'\n  - 'packages/*'\n"
  },
  {
    "path": "turbo.json",
    "content": "{\n  \"pipeline\": {\n    \"build\": {\n      \"dependsOn\": [\"^build\"],\n      \"outputs\": [\"dist/**\"]\n    },\n    \"dev\": {\n      \"cache\": false\n    },\n    \"sandbox#dev\": {\n      \"dependsOn\": [\"text-vide#build\"],\n      \"outputs\": []\n    },\n    \"preview\": {\n      \"dependsOn\": [\"build\"]\n    },\n    \"test\": {\n      \"dependsOn\": [\"text-vide#build\"],\n      \"cache\": false\n    },\n    \"benchmark\": {\n      \"outputs\": []\n    },\n    \"release\": {\n      \"cache\": false\n    }\n  }\n}\n"
  }
]