Repository: Gumball12/text-vide Branch: main Commit: 8adb3593acc8 Files: 58 Total size: 106.9 KB Directory structure: gitextract_vz2xfrmz/ ├── .eslintignore ├── .eslintrc.js ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── feature_request.md │ │ └── question.md │ └── workflows/ │ ├── ci.yaml │ ├── publish-sandbox.yaml │ └── publish.yaml ├── .gitignore ├── .husky/ │ └── pre-commit ├── .nvmrc ├── .prettierrc ├── ABOUT_READABILITY.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── HOW.md ├── LICENSE ├── README.md ├── apps/ │ ├── benchmark/ │ │ ├── index.js │ │ └── package.json │ └── sandbox/ │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.tsx │ │ ├── index.css │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── package.json ├── packages/ │ ├── text-vide/ │ │ ├── .versionrc.json │ │ ├── package.json │ │ ├── src/ │ │ │ ├── __tests__/ │ │ │ │ ├── getOptions.test.ts │ │ │ │ └── index.test.ts │ │ │ ├── getFixationLength.ts │ │ │ ├── getHighlightedText.ts │ │ │ ├── getOptions.ts │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ ├── useCheckIsHtmlEntity.ts │ │ │ ├── useCheckIsHtmlTag.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── vite.config.ts │ │ └── vitest.config.ts │ ├── tsconfig/ │ │ ├── base.json │ │ ├── package.json │ │ └── vite.json │ └── utils/ │ ├── __tests__/ │ │ ├── defaults.test.ts │ │ ├── isEmpty.test.ts │ │ └── omitBy.test.ts │ ├── defaults.ts │ ├── isEmpty.ts │ ├── omitBy.ts │ └── package.json ├── pnpm-workspace.yaml └── turbo.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintignore ================================================ apps/benchmark ================================================ FILE: .eslintrc.js ================================================ module.exports = { parser: '@typescript-eslint/parser', parserOptions: { sourceType: 'module', }, extends: [ 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'prettier', ], rules: { 'no-unused-vars': ['error', { varsIgnorePattern: '.*', args: 'none' }], 'no-restricted-syntax': ['error', 'ObjectPattern > RestElement'], '@typescript-eslint/no-non-null-assertion': 'off', }, overrides: [ { files: ['./**/__tests__/**'], rules: { 'no-restricted-globals': 'off', 'no-restricted-syntax': 'off', }, }, ], }; ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug assignees: Gumball12 --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement assignees: Gumball12 --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/question.md ================================================ --- name: Question about: Ask and answer title: '' labels: question assignees: Gumball12 --- **Please write questions about the project** ================================================ FILE: .github/workflows/ci.yaml ================================================ name: ci on: push: pull_request: branches: - main jobs: ci: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: pnpm/action-setup@v4.2.0 - uses: actions/setup-node@v6 with: node-version: 22 registry-url: 'https://registry.npmjs.org' cache: pnpm cache-dependency-path: pnpm-lock.yaml - run: pnpm install --frozen-lockfile - run: pnpm run lint - run: pnpm run build - run: pnpm run test - uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} directory: ./packages/text-vide/coverage fail_ci_if_error: true verbose: true ================================================ FILE: .github/workflows/publish-sandbox.yaml ================================================ name: publish-sandbox on: push: branches: - main jobs: publish-sandbox: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: pnpm/action-setup@v4.2.0 - uses: actions/setup-node@v6 with: node-version: 22 registry-url: 'https://registry.npmjs.org' cache: pnpm cache-dependency-path: pnpm-lock.yaml - run: pnpm install --frozen-lockfile - run: pnpm run build - name: Deploy uses: peaceiris/actions-gh-pages@v3.9.3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./apps/sandbox/dist ================================================ FILE: .github/workflows/publish.yaml ================================================ name: publish on: release: types: [created] permissions: id-token: write contents: read jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - uses: pnpm/action-setup@v4.2.0 - uses: actions/setup-node@v6 with: node-version: 22 registry-url: 'https://registry.npmjs.org' cache: pnpm cache-dependency-path: pnpm-lock.yaml - run: npm i -g npm@latest - run: pnpm install --frozen-lockfile - run: pnpm run lint - run: pnpm run test - run: pnpm run build - run: | cp ./README.md ./packages/text-vide/README.md && cd ./packages/text-vide && npm publish ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .DS_STORE # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.test # parcel-bundler cache (https://parceljs.org/) .cache # Next.js build output .next # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and *not* Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # turbo .turbo ================================================ FILE: .husky/pre-commit ================================================ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npx lint-staged ================================================ FILE: .nvmrc ================================================ v22.12.0 ================================================ FILE: .prettierrc ================================================ { "trailingComma": "all", "singleQuote": true, "printWidth": 80, "arrowParens": "avoid", "endOfLine": "auto" } ================================================ FILE: ABOUT_READABILITY.md ================================================ # About Readability Bionic-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. ![study-results](./docs/study-results.png) And... I'm not the designer of Bionic-Reading, nor am I the researcher involved. So there's nothing I can say about this. I'm sorry if I didn't help you. Thank you for reading it. ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. ### [1.8.5](https://github.com/Gumball12/text-vide/compare/v1.8.4...v1.8.5) (2026-03-29) ### Bug Fixes * **security:** upgrade handlebars to 4.7.9 via pnpm overrides ([304c78d](https://github.com/Gumball12/text-vide/commit/304c78dec05ae2725178d378eff4772cc16a8341)) ### [1.8.2](https://github.com/Gumball12/text-vide/compare/v1.8.1...v1.8.2) (2025-03-17) ### [1.8.1](https://github.com/Gumball12/text-vide/compare/v1.8.0...v1.8.1) (2023-11-09) ## [1.8.0](https://github.com/Gumball12/text-vide/compare/v1.7.0...v1.8.0) (2023-11-09) ### Features * add ignoreHtmlEntity option ([#46](https://github.com/Gumball12/text-vide/issues/46)) ([6fbef3d](https://github.com/Gumball12/text-vide/commit/6fbef3d367cbb6e8b0a3f1028ce2c2a8b91c19b2)) ## [1.7.0](https://github.com/Gumball12/text-vide/compare/v1.6.2...v1.7.0) (2023-04-03) ### [1.6.2](https://github.com/Gumball12/text-vide/compare/v1.6.1...v1.6.2) (2023-03-27) ### [1.6.1](https://github.com/Gumball12/text-vide/compare/v1.6.0...v1.6.1) (2023-02-13) ## [1.6.0](https://github.com/Gumball12/text-vide/compare/v1.5.0...v1.6.0) (2022-08-06) ### Features * add benchmark tests ([977e008](https://github.com/Gumball12/text-vide/commit/977e0087b4670de2196e68574d697f13683d4fb1)) * add ignoreHtmlTag option ([c209f8c](https://github.com/Gumball12/text-vide/commit/c209f8cfffff4f61c12727ccbe02fb19d4f95c67)) * add mb-2px to Input paragraph ([f3f7160](https://github.com/Gumball12/text-vide/commit/f3f71605ea735ac1111c46ee1ff5cff2566cb8d7)) * make the utils directory into the utils package ([45e34a3](https://github.com/Gumball12/text-vide/commit/45e34a37eb1517114e12f1553eedf902c107b335)) ## [1.5.0](https://github.com/Gumball12/text-vide/compare/v1.4.0...v1.5.0) (2022-06-04) ### Features * add logo ([befd0d6](https://github.com/Gumball12/text-vide/commit/befd0d6029b88361765bdc12d1d3d17dda89b96d)) ## [1.4.0](https://github.com/Gumball12/bionic-reading/compare/v1.3.2...v1.4.0) (2022-05-28) ### Features * implement fixationPoint option field ([53697f4](https://github.com/Gumball12/bionic-reading/commit/53697f43d8c291890f1d496e2a7fb2ba9c160106)) * implement getOptions() ([100cb94](https://github.com/Gumball12/bionic-reading/commit/100cb94f83ecf181d04c1e4f67fc8ca0f07cd766)) * implement isEmpty util function ([9450ac9](https://github.com/Gumball12/bionic-reading/commit/9450ac9a67b23248d2254e6120de8ae6047acf57)) * implement isNumberString util function ([928c11b](https://github.com/Gumball12/bionic-reading/commit/928c11bf1e7df2fda56f74dbfcaa0190cc1444be)) * improve readability ([5b2789a](https://github.com/Gumball12/bionic-reading/commit/5b2789ab42997ef52cb06a4a7b1e78a65fbed9c1)) * improve sandbox ([2add790](https://github.com/Gumball12/bionic-reading/commit/2add7906eb040bbe81d90d6be88f29b1993247e2)) * migrate to react ([1f17c36](https://github.com/Gumball12/bionic-reading/commit/1f17c369cdf2cfc558beada554242ec7d24adab7)) ### Bug Fixes * fix footer style ([f9aa436](https://github.com/Gumball12/bionic-reading/commit/f9aa43689e86f102ba31978fced6a397e44f08ec)) * fix incorrect rendering for numbers ([2308eea](https://github.com/Gumball12/bionic-reading/commit/2308eeae27c6fca9f5ba5f8bb609b5e98b1e3ff1)) * use br tag instead of linefeed char ([f8ba04f](https://github.com/Gumball12/bionic-reading/commit/f8ba04f2dd8971d2817e4c0cdbe89416a7b13acb)) ### [1.3.2](https://github.com/Gumball12/bionic-reading/compare/v1.3.1...v1.3.2) (2022-05-27) ### Bug Fixes * copy README for npm publishing ([6cea4c8](https://github.com/Gumball12/bionic-reading/commit/6cea4c877fe8420b9a1ae9afa03ad51a67346027)) ### [1.3.1](https://github.com/Gumball12/bionic-reading/compare/v1.3.0...v1.3.1) (2022-05-27) ### Bug Fixes * fix npm publishing process ([13a7278](https://github.com/Gumball12/bionic-reading/commit/13a7278addc6d3599007610e8cea76acf16f09e0)) ## [1.3.0](https://github.com/Gumball12/bionic-reading/compare/v1.2.3...v1.3.0) (2022-05-27) ### Features * support multiple languages ([5c9c200](https://github.com/Gumball12/bionic-reading/commit/5c9c2002f3ae501554f54e26e56ff33ea3ec1823)) ### Bug Fixes * fix actions/cache key ([139171f](https://github.com/Gumball12/bionic-reading/commit/139171f9c5cd752dacd728f9b27e1eb299119460)) * fix npm script ([f6c6706](https://github.com/Gumball12/bionic-reading/commit/f6c6706570ed1b724add21c5fd2262a7ae123ca8)) * set cache field for test and release pipelines to false ([673f8fb](https://github.com/Gumball12/bionic-reading/commit/673f8fb0e3c89b037daa161c0e7b3138fd6ddf8e)) ### [1.2.3](https://github.com/Gumball12/bionic-reading/compare/v1.2.2...v1.2.3) (2022-05-26) ### Features * implement splitMap util function ([2b1db41](https://github.com/Gumball12/bionic-reading/commit/2b1db41ebc957a75d0bd52a4bebbbbe2085275c3)) * 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) ### [1.2.2](https://github.com/Gumball12/bionic-reading/compare/v1.2.1...v1.2.2) (2022-05-23) ### Features * define default options value ([f71cc5d](https://github.com/Gumball12/bionic-reading/commit/f71cc5dd35ece8e6a8bb05fac33e17e485c9d19b)) * define Options type ([d632ee7](https://github.com/Gumball12/bionic-reading/commit/d632ee747df61154257d6cde084a41ceb9c0a6a7)) * implement getBionicWordConvertor ([b184fb4](https://github.com/Gumball12/bionic-reading/commit/b184fb4956a218f18a9d87d505e033f9d02e401f)) * implement isEmptyString util ([1ffa4e3](https://github.com/Gumball12/bionic-reading/commit/1ffa4e3de4d0b722b621da26e46fb9a3ad39b9f7)) * implement omitBy util ([05c4579](https://github.com/Gumball12/bionic-reading/commit/05c4579c4bbc39f248f201f9a8ea02cfccdbe81a)) * implement publish-sandbox action ([12888db](https://github.com/Gumball12/bionic-reading/commit/12888db0edbe07348c10610e3825c1971f3bccd3)) * implement sandbox ([d56cb82](https://github.com/Gumball12/bionic-reading/commit/d56cb82ae3ca6e6ee69a5ca5109d6846ea205bfc)) * supports strings with newlines ([bce07f7](https://github.com/Gumball12/bionic-reading/commit/bce07f71fde302794e13124bd5e8ff0617d56a37)) ### Bug Fixes * add root field to test only src directory ([f9e00fd](https://github.com/Gumball12/bionic-reading/commit/f9e00fdf698981395e8b0592691be33b2bff8f35)) * handle if passing an empty style string ([7b60e96](https://github.com/Gumball12/bionic-reading/commit/7b60e9672e06a611990c95d726479b6ebd14c82e)) * use base field instead of publicDir ([09cc18c](https://github.com/Gumball12/bionic-reading/commit/09cc18cf545474db5c92cefeeb7e1148f159da1a)) ### [1.2.1](https://github.com/Gumball12/bionic-reading/compare/v1.2.0...v1.2.1) (2022-05-22) ## [1.2.0](https://github.com/Gumball12/bionic-reading/compare/v1.1.0...v1.2.0) (2022-05-22) ### Features * ensure valid text & options object ([a79ac05](https://github.com/Gumball12/bionic-reading/commit/a79ac055fa8afa3666c8f4bc67c31bf4e40df988)) * implement bionicConvertor ([c6ddb43](https://github.com/Gumball12/bionic-reading/commit/c6ddb432d9f140c2ff611191b40219abaf186266)) * implement defaults util function ([58a16d5](https://github.com/Gumball12/bionic-reading/commit/58a16d5577c05971184419aea7adccdf08a3540e)) * implement splitWord util function ([6b84b32](https://github.com/Gumball12/bionic-reading/commit/6b84b3232e380f0b379ad04f2b71893eb3096e45)) * support markdown ([d4f9892](https://github.com/Gumball12/bionic-reading/commit/d4f9892dc15ea58c39e1a3e3d7fc1f4e9e6985be)), closes [#2](https://github.com/Gumball12/bionic-reading/issues/2) ## [1.1.0](https://github.com/Gumball12/bionic-reading/compare/v1.0.2...v1.1.0) (2022-05-21) ### Bug Fixes * do not use deafult export ([fdc32b2](https://github.com/Gumball12/bionic-reading/commit/fdc32b23bccf1e540812dd0515745ffbb68fd866)) * fix release scripts ([b5495c3](https://github.com/Gumball12/bionic-reading/commit/b5495c3353a29087e2b5c8e219807063fb1490fc)) ### [1.0.2](https://github.com/Gumball12/bionic-reading/compare/v1.0.1...v1.0.2) (2022-05-21) ### Bug Fixes * add registry-url field to publish workflow ([0dbbb1d](https://github.com/Gumball12/bionic-reading/commit/0dbbb1deaffc1324a583d88f2250069066dbb2d4)) ### [1.0.1](https://github.com/Gumball12/bionic-reading/compare/v1.0.0...v1.0.1) (2022-05-21) ## 1.0.0 (2022-05-21) ### Features * implement bionicReading module ([1813a8c](https://github.com/Gumball12/bionic-reading/commit/1813a8c649cb3fcce15099573b3bd46095a2e80c)) * implement ci & publish workflows ([984bfda](https://github.com/Gumball12/bionic-reading/commit/984bfdae1bc04ae423aa06a2154a202bf2b678a7)) ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at to@shj.rip. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing ## Setup ```bash git clone https://github.com//text-vide.git cd text-vide pnpm install pnpm dev # run sandbox application (:5173) pnpm test pnpm lint pnpm build ``` ## Project structure ``` . ├── apps/ │ └── sandbox/ └── packages/ ├── text-vide/ <-- core ├── tsconfig/ └── utils/ ``` ```mermaid graph LR; utils --> TV["Text Vide"] tsconfig --> TV TV --> NPM(("Publish to
NPM")) TV --> Sandbox ``` ================================================ FILE: HOW.md ================================================ # How was this made? This 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. ## The Rules First, I found the rule below through the BR API call result. - [Rules for the Number of Characters](#char-length-rules) - [Rules for Special Characters](#special-chars-rules) - [Rules for Numbers](#number-rules) ### Rules for the Number of Characters At first, I tried to find a Pattern by adding characters one by one. Below is the result: | Length (Min) | Length (Max) | Max - Min | Number of Non-Highlighted Chars | | ------------ | ------------ | --------- | ------------------------------- | | 0 | 4 | 4 | 1 | | 5 | 12 | 7 | 2 | | 13 | 16 | 3 | 3 | | 17 | 24 | 7 | 4 | | 25 | 29 | 4 | 5 | | 30 | 35 | 5 | 6 | | 36 | 42 | 6 | 7 | | 43 | 48 | 5 | 8 | I couldn't find a specific pattern here but thought I could use a Heuristic instead. > [Pneumonoultramicroscopicsilicovolcanoconiosis](https://en.wikipedia.org/wiki/Longest_word_in_English#cite_note-p45-6) (45 Letters) The 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. Below are the final rules for character count: | Length (Min) | Length (Max) | Max - Min | Number of Non-Bold Chars | | ------------ | ------------ | --------- | ------------------------ | | 0 | 4 | 4 | 1 | | 5 | 12 | 7 | 2 | | 13 | 16 | 3 | 3 | | 17 | 24 | 7 | 4 | | 25 | 29 | 4 | 5 | | 30 | 35 | 5 | 6 | | 36 | 42 | 6 | 7 | | 43 | 48 | 5 | 8 | | 49 | infinity | infinity | 9 | The 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. ### Rules for Special Characters Special characters at the Beginning or End of a word are **not** highlighted. ```ts ';apple;' -> ';apple;' ``` Special characters placed **inside words** are treated the same as Regular characters. ```ts 'a;ppl;e' -> 'a;ppl;e' ``` However, a Dash (`-`) among special characters located inside a word is treated the same as a Space. ```ts 'app-le' -> 'app-le' ``` > I thought it was awkward to highlight special characters, so I implemented it to divide them based on special characters. > > ```ts > // Origin > 'a;ppl;e' -> 'a;ppl;e' > 'app-le' -> 'app-le' > > // This module > 'a;ppl;e' -> 'a;ppl;e' > 'app-le' -> 'app-le' > ``` ### Rules for numbers If there are only numbers, highlight nothing. ```ts '1234567890' -> '1234567890' ``` If there is a dash between the numbers, it is also not highlighted. ```ts '1234-567890' -> '1234-567890' ``` When numbers and letters are used together, they are treated as regular characters. ```ts 'a1234567890' -> 'a1234567890' '1234567890a' -> '1234567890a' '1234a567890' -> '1234a567890' ``` If a special character other than a dash is between numbers, treat it like a regular character. ```ts '1234!567890' -> '1234!567890' ``` Otherwise, it doesn't highlight anything. ```ts '!1234567890' -> '!1234567890' '1234567890!' -> '1234567890!' ``` Note: Emojis are treated as special characters, not dashes. > This module does not highlight even if there are special characters between numbers. > > ```ts > // Origin > '1234!567890' -> '1234!567890' > > // This module > '1234!567890' -> '1234!567890' > ``` ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 shj Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # TextVide (vide; Latin for "see") [![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) [![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) ![logo](./docs/logo-extended.png) > **Support all languages that separate words with spaces** > [Try on Runkit](https://npm.runkit.com/text-vide) or [Online Sandbox](https://gumball12.github.io/text-vide/) This module is an open source implementation that mimics the behavior of the [Bionic Reading API](https://bionic-reading.com/). It does not require any additional licenses, except for MIT. ([#38](https://github.com/Gumball12/text-vide/issues/38)) - _[How was this made?](./HOW.md)_ - _[I don't think it's going to be more readable](./ABOUT_READABILITY.md)_ - _[CONTRIBUTING.md](./CONTRIBUTING.md)_ ## 💫 Features | Feature | State | | ----------------------------------------------------------------------- | ----- | | [Support all languages](https://github.com/Gumball12/text-vide/pull/16) | ✅ | | [Support ESM and CommonJS](#usage) | ✅ | | [Custom `sep` Style](#options-sep) | ✅ | | [Fixation-Points](#options-fixationpoint) | ✅ | | [Ignore HTML Tags](#options-ignorehtmltag) | ✅ | | [Ignore HTML Entity](#options-ignorehtmlentity) | ✅ | | [Saccade](https://github.com/Gumball12/text-vide/issues/21) | ❌ | ### Benchmark ``` Sun Aug 07 2022 01:33:40 GM +0900 length of normal text: 590 length of text with html tags: 1579 normal#ignoreHtmlTags x 46,106 ops/sec ±4.22% (86 runs sampled) normal#notIgnoreHtmlTags x 53,200 ops/sec ±0.93% (89 runs sampled) withHtmlTags#ignoreHtmlTags x 3,213 ops/sec ±0.92% (87 runs sampled) withHtmlTags#notIgnoreHtmlTags x 3,605 ops/sec ±1.59% (87 runs sampled) ``` [code](./apps/benchmark/index.js) ## ⚙️ Install ```bash npm i text-vide yarn add text-vide pnpm add text-vide ``` ### CDN ```html ``` ## 📖 Usage ### Browser ```html ``` ### ESM ```ts import { textVide } from 'text-vide'; const text = '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.'; const highlightedText = textVide(text); console.log(highlightedText); // 'Bionic Reading ... written content.' ``` ### CommonJS ```ts const { textVide } = require('text-vide'); const text = '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.'; const highlightedText = textVide(text); console.log(highlightedText); // 'Bionic Reading ... written content.' ``` ## 📚 API ### `textVide(text: string, options?: Options)` ```ts textVide('text-vide'); textVide('text-vide', { // ... Options }); ``` ### Options ```ts type Options = Partial<{ sep: string | string[]; fixationPoint: number; ignoreHtmlTag: boolean; ignoreHtmlEntity: boolean; }>; ``` #### `sep` - Default: `['', '']` Passing a string allows you to specify the Beginning and End of the highlighted word at once. ```ts textVide('text-vide', { sep: '**' }); // '**tex**t-**vid**e' ``` It can also set them up by passing a list of length 2. ```ts textVide('text-vide', { sep: ['', ''] }); // 'text-vide' ``` #### `fixationPoint` - Default: `1` - Range: `[1, 5]` ```ts // Default fixation-point: 1 textVide('text-vide'); // 'text-vide' // Changed fixation-point: 5 textVide('text-vide', { fixationPoint: 5 }); // 'text-vide' ``` #### `ignoreHtmlTag` - Default: `true` If this option is `true`, HTML tags are not highlighted. ```ts textVite('
abcd
efg'); // '
abcd
efg' textVite('
abcd
efg', { ignoreHtmlTag: false }); // '<div>abcddiv>efg' ``` #### `ignoreHtmlEntity` - Default: `true` If this option is `true`, HTML entities are not highlighted. ```ts textVide(' abcd>'); // ' abcd>' textVide(' abcd>', { ignoreHtmlEntity: false }); // &nbsp;abcd&gt; ``` ## License [MIT](./LICENSE) @Gumball12 It does not require any additional licenses, except for MIT. ([#38](https://github.com/Gumball12/text-vide/issues/38)) ## Why Monorepo? I acknowledge that the current monorepo structure might seem complex for the project's requirements. Here's why I chose this approach: - This project served as a learning experience for implementing monorepo architecture, as I was preparing to introduce it at my workplace - I intentionally used this small project as a practical exercise to understand monorepo concepts and best practices - While the current structure might be over-engineered, I plan to maintain it since: - The project requirements are relatively stable - Major changes or additions are unlikely - However, I'm open to simplifying the architecture if there's a compelling reason to do so ================================================ FILE: apps/benchmark/index.js ================================================ const { textVide } = require('text-vide'); const Benchmark = require('benchmark'); const suite = new Benchmark.Suite(); const text = '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.'; const textWithHtmlTags = `
normal text: abcdefg
with a tag: abcdefg
with b tag: abcdefg
with div tag:
abcd
efg
`; suite .add('normal#ignoreHtmlTags', () => textVide(text)) .add('normal#notIgnoreHtmlTags', () => textVide(text, { ignoreHtmlTag: false }), ) .add('withHtmlTags#ignoreHtmlTags', () => textVide(textWithHtmlTags)) .add('withHtmlTags#notIgnoreHtmlTags', () => textVide(textWithHtmlTags, { ignoreHtmlTag: false }), ) .on('start', () => console.log( [ new Date().toString(), `length of normal text: ${text.length}`, `length of text with html tags: ${textWithHtmlTags.length}`, '', ].join('\n'), ), ) .on('cycle', evt => { console.log(evt.target.toString()); }) .run({ async: true }); ================================================ FILE: apps/benchmark/package.json ================================================ { "name": "benchmark", "version": "0.0.0", "scripts": { "benchmark": "node index.js" }, "devDependencies": { "benchmark": "^2.1.4", "text-vide": "workspace:*" } } ================================================ FILE: apps/sandbox/index.html ================================================ Text Vide Sandbox
================================================ FILE: apps/sandbox/package.json ================================================ { "name": "sandbox", "version": "0.0.0", "scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview" }, "dependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" }, "devDependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/material": "^5.8.1", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "@unocss/preset-wind": "^66.5.4", "@unocss/reset": "^66.5.4", "@vitejs/plugin-react": "^5.0.4", "text-vide": "workspace:*", "tsconfig": "workspace:*", "unocss": "^66.5.4" } } ================================================ FILE: apps/sandbox/src/App.tsx ================================================ import { Box, Button, TextField, ToggleButton, ToggleButtonGroup, } from '@mui/material'; import { Reducer, useEffect, useReducer } from 'react'; import { textVide } from 'text-vide'; import logo from './logo.png'; const DEBOUNCE_TIMEOUT = 400; const COPIED_EFFECT_DEBOUNCE_TIMEOUT = 1200; const INITIAL_INPUT = '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.'; type Edits = { firstSep: string; secondSep: string; fixationPoint: string; ignoreHtmlTag: string; ignoreHtmlEntity: string; input: string; }; const defaultEdits: Edits = { firstSep: '', secondSep: '', fixationPoint: '1', ignoreHtmlTag: '1', // 1 = true, 0 = false ignoreHtmlEntity: '1', // 1 = true, 0 = false input: INITIAL_INPUT, }; const storeEdits = ({ firstSep, secondSep, fixationPoint, input, ignoreHtmlTag, ignoreHtmlEntity, }: Edits) => { const search = [ `firstSep=${encodeURIComponent(firstSep)}`, `secondSep=${encodeURIComponent(secondSep)}`, `fixationPoint=${encodeURIComponent(fixationPoint)}`, `input=${encodeURIComponent(input)}`, `ignoreHtmlTag=${encodeURIComponent(ignoreHtmlTag)}`, `ignoreHtmlEntity=${encodeURIComponent(ignoreHtmlEntity)}`, ].join('&'); // eslint-disable-next-line // @ts-ignore history.replaceState(null, null, `?${search}`); }; const getEdits = (): Edits => { const maybeEditsString = location.search.slice(1); if (!maybeEditsString?.length) { return defaultEdits; } const maybeEdits = maybeEditsString.split('&').reduce((maybeEdits, str) => { const [maybeKey, maybeValue] = str.split('='); if (!maybeKey.length) { return maybeEdits; } const key = maybeKey as keyof Edits; const value = maybeValue ?? defaultEdits[key]; if (!value) { return maybeEdits; } // eslint-disable-next-line // @ts-ignore maybeEdits[key] = decodeURIComponent(value); return maybeEdits; }, {} as Partial); return { ...defaultEdits, ...maybeEdits, }; }; const initialEdits = getEdits(); type State = Edits & { highlightedText: string; copiedEffect: boolean; }; type Action = { type: | 'FIRST_SEP' | 'SECOND_SEP' | 'FIXATION_POINT' | 'INPUT' | 'HIGHLIGHTED_TEXT' | 'COPIED' | 'TOGGLE_IGNORE_HTML_TAG' | 'TOGGLE_IGNORE_HTML_ENTITY' | 'RESET'; value: string; copied: boolean; }; const reducer: Reducer = (state, { type, value, copied }) => { if (type === 'FIRST_SEP') { return { ...state, firstSep: value }; } if (type === 'SECOND_SEP') { return { ...state, secondSep: value }; } if (type === 'FIXATION_POINT') { return { ...state, fixationPoint: value }; } if (type === 'INPUT') { return { ...state, input: value }; } if (type === 'HIGHLIGHTED_TEXT') { return { ...state, highlightedText: value }; } if (type === 'COPIED') { return { ...state, copiedEffect: copied }; } if (type === 'TOGGLE_IGNORE_HTML_TAG') { const nextIgnoreHtmlTag = state.ignoreHtmlTag === '1' ? '0' : '1'; return { ...state, ignoreHtmlTag: nextIgnoreHtmlTag }; } if (type === 'TOGGLE_IGNORE_HTML_ENTITY') { const nextIgnoreHtmlEntity = state.ignoreHtmlEntity === '1' ? '0' : '1'; return { ...state, ignoreHtmlEntity: nextIgnoreHtmlEntity }; } if (type === 'RESET') { return { ...defaultEdits, highlightedText: '', copiedEffect: false, }; } return state; }; const App = () => { const [state, dispatchState] = useReducer(reducer, { ...initialEdits, highlightedText: '', copiedEffect: false, }); const { firstSep, secondSep, input, fixationPoint, copiedEffect, highlightedText, ignoreHtmlTag, ignoreHtmlEntity, } = state; useEffect(() => { const store = setTimeout(() => { const options = { sep: [firstSep, secondSep], fixationPoint: parseInt(fixationPoint), ignoreHtmlTag: ignoreHtmlTag === '1', ignoreHtmlEntity: ignoreHtmlEntity === '1', }; const highlightedText = textVide(input, options); dispatchState({ type: 'HIGHLIGHTED_TEXT', value: highlightedText, copied: false, }); storeEdits({ firstSep, secondSep, input, fixationPoint, ignoreHtmlTag, ignoreHtmlEntity, }); }, DEBOUNCE_TIMEOUT); return () => clearTimeout(store); }, [ firstSep, secondSep, input, fixationPoint, ignoreHtmlTag, ignoreHtmlEntity, ]); const copyUrl = () => { const { href: url } = location; navigator.clipboard.writeText(url); dispatchState({ type: 'COPIED', value: '', copied: true }); }; useEffect(() => { const store = setTimeout( () => dispatchState({ type: 'COPIED', value: '', copied: false }), COPIED_EFFECT_DEBOUNCE_TIMEOUT, ); return () => clearTimeout(store); }, [copiedEffect]); const reset = () => dispatchState({ type: 'RESET', value: '', copied: false }); const isResetDisabled = JSON.stringify(defaultEdits) === JSON.stringify({ firstSep, secondSep, fixationPoint, input, ignoreHtmlTag, ignoreHtmlEntity, }); return (

Text Vide Sandbox

dispatchState({ type: 'FIRST_SEP', value: (target as HTMLInputElement).value, copied: false, }) } required /> dispatchState({ type: 'SECOND_SEP', value: (target as HTMLInputElement).value, copied: false, }) } InputLabelProps={{ shrink: true }} />
value && dispatchState({ type: 'FIXATION_POINT', value, copied: false }) } > fixation - 1 2 3 4 5

Input