├── favicon.ico
├── preview.jpg
├── favicon-16x16.png
├── favicon-32x32.png
├── apple-touch-icon.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── site.webmanifest
├── css
└── ts.css
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── js
├── ts.min.js
└── techscriptor.js
├── tests.md
└── index.html
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbrincoveanu/techscriptor/HEAD/favicon.ico
--------------------------------------------------------------------------------
/preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbrincoveanu/techscriptor/HEAD/preview.jpg
--------------------------------------------------------------------------------
/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbrincoveanu/techscriptor/HEAD/favicon-16x16.png
--------------------------------------------------------------------------------
/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbrincoveanu/techscriptor/HEAD/favicon-32x32.png
--------------------------------------------------------------------------------
/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbrincoveanu/techscriptor/HEAD/apple-touch-icon.png
--------------------------------------------------------------------------------
/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbrincoveanu/techscriptor/HEAD/android-chrome-192x192.png
--------------------------------------------------------------------------------
/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cbrincoveanu/techscriptor/HEAD/android-chrome-512x512.png
--------------------------------------------------------------------------------
/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
--------------------------------------------------------------------------------
/css/ts.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | height: 100%;
3 | }
4 | #editor {
5 | position: absolute;
6 | top: 0;
7 | right: 0;
8 | bottom: 0;
9 | left: 0;
10 | }
11 | #mdviewContainer {
12 | background: #f1f1ff;
13 | }
14 | #mdview {
15 | padding: 20px;
16 | }
17 | blockquote {
18 | background: #f9f9f9;
19 | border-left: 3px solid #ccc;
20 | margin: 1.5em 10px;
21 | padding: 0.5em 10px;
22 | }
23 | blockquote p {
24 | display: inline-block;
25 | }
26 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution guidelines
2 |
3 | First of all, thanks for thinking of contributing to this project. 😄
4 |
5 | Before sending a Pull Request, please make sure that you're assigned the task on
6 | a GitHub issue.
7 |
8 | * If a relevant issue already exists, discuss on the issue and get it assigned
9 | to yourself on GitHub.
10 | * If no relevant issue exists, open a new issue and get it assigned to yourself
11 | on GitHub.
12 |
13 | Please proceed with a Pull Request only after you're assigned. It'd be sad if
14 | your Pull Request (and your hardwork) isn't accepted just because it isn't
15 | ideologically compatible.
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Constantin Brîncoveanu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # techscriptor [](https://twitter.com/intent/tweet?text=Techscriptor%20makes%20your%20writing%20precise%20and%20clear&url=https://www.techscriptor.com&hashtags=developers,technical,writing,markdown,editor,documentation)
2 |
3 | 
4 | 
5 | 
6 | 
7 |
8 | Markdown editor for technical writing.
9 |
10 | [](https://www.techscriptor.com)
11 |
12 | > ***Techscriptor*** is a Markdown editor that makes your writing precise and
13 | > clear.
14 |
15 | Techscriptor helps you avoid the following:
16 |
17 | * **Long sentences:** Make your sentences short and clear.
18 | * **Passive voice:** Use the active voice most of the time.
19 | * **Generic expressions:** Reduce imprecise, weak, or generic words.
20 | * **Adverbs:** Avoid adverbs if they add no significant meaning.
21 |
22 | A live demo is available here: https://www.techscriptor.com
23 |
24 | ## Getting Started
25 |
26 | You only need a web browser for viewing `index.html` after cloning the
27 | repository to your local machine.
28 |
29 | Techscriptor loads its dependencies from CDNs (see below). Other than that, it
30 | loads `ts.min.js` which is the minified[^1] version of `js/techscriptor.js`.
31 |
32 | [^1]: minification using https://www.toptal.com/developers/javascript-minifier
33 |
34 | ## Dependencies
35 |
36 | Techscriptor depends on the following (loaded from
37 | [jsdelivr.com](https://www.jsdelivr.com/) and
38 | [cdnjs.cloudflare.com](https://cdnjs.cloudflare.com/)):
39 |
40 | * [Ace](https://ace.c9.io/) v1.10.1
41 | * [markdown-it](https://github.com/markdown-it/markdown-it) v13.0.1
42 | * [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) v3.0.3
43 | * [highlight.js](https://highlightjs.org/) v11.6.0
44 | * [Bootstrap](https://getbootstrap.com/) v5.2.1
45 |
46 | ## Contribute
47 |
48 | Your contributions are always welcome! Please have a look at the [contribution
49 | guidelines](/CONTRIBUTING.md) first. 🎉
50 |
51 | ## License
52 |
53 | This software is licensed under the MIT license.
54 |
--------------------------------------------------------------------------------
/js/ts.min.js:
--------------------------------------------------------------------------------
1 | var editor=ace.edit("editor");function loadPopovers(){[].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')).map(function(e){return new bootstrap.Popover(e,{trigger:"hover focus",html:!0})})}function getPopover(e,r,t){return''+e+""}editor.setTheme("ace/theme/chrome"),editor.session.setMode("ace/mode/markdown"),editor.setOptions({fontSize:14,vScrollBarAlwaysVisible:!0,wrap:!0});var md=window.markdownit().use(window.markdownitFootnote);const beforeUnloadListener=e=>(e.preventDefault(),e.returnValue="Are you sure you want to exit?");function getReplacementString(e){return"[#r"+e+"]"}function countWords(e){let r=e.split(" ");return r.filter(e=>""!==e).length}function countSentences(e){return null==(s=e.match(/[\w|\)][.?!](\s|$)/g))?0:s.length}function updateView(){htmlOutput=md.render(editor.getValue());var e=[];let r={hardToRead:{regex:/((\w+,\s+)|(\w+\s+)){20,}(\w+[\.|?|!])/g,message:"This sentence is hard to read.",color:"#f0f0a0",summary:" sentences are hard to read.",summarySingle:" sentence is hard to read."},adverbs:{regex:/((\w+)ly)|sometimes|perhaps|maybe/g,message:"Adverb: Use a forceful verb.",color:"#a0e0ff",summary:" adverbs.",summarySingle:" adverb."},passiveVoice:{regex:/\b((be(en)?)|(w(as|ere))|(is)|(a(er|m)))(\.|\,)?\s(\w+\s)?(\w+(en|ed))(\s|\.|\,)/g,message:"Passive voice: Use active voice.",color:"#a0f0a0",summary:" uses of passive voice.",summarySingle:" use of passive voice."},thereIsThereAre:{regex:/\bThere (is|are)\b/g,message:"Generic: Be precise.",color:"#f0a0f0",summary:" uses of There is/There are.",summarySingle:" use of There is/There are."},genericVerb:{regex:/\b(happen|occur)(s?)/g,message:"Generic verb: Use precise verbs.",color:"#f0a0f0",summary:" uses of generic verbs.",summarySingle:" use of generic verbs."}};var t={};for(var n in r)rule=r[n],t[n]=0,htmlOutput=htmlOutput.replaceAll(rule.regex,function(r){return e.push(getPopover(r,rule.message,rule.color)),t[n]++,getReplacementString(e.length-1)});for(var n in console.log(t),rulesSummary="",r){var a=r[n].summary;1==t[n]&&(a=r[n].summarySingle),rulesSummary+='
'),document.getElementById("mdview").innerHTML=htmlOutput,sentences=countSentences(outputWithoutHtml=htmlOutput.replace(/(<([^>]+)>)/ig,"")),words=countWords(outputWithoutHtml),(characters=outputWithoutHtml.length-1)<0&&(characters=0),warnings=e.length,document.getElementById("sentences").innerHTML=sentences,document.getElementById("words").innerHTML=words,document.getElementById("characters").innerHTML=characters,document.getElementById("warnings").innerHTML=warnings,warningsClass="text-success",warnings/words>.03&&(warningsClass="text-warning"),warnings/words>.05&&(warningsClass="text-danger"),document.getElementById("warnings").className=warningsClass,loadPopovers(),hljs.highlightAll(),addEventListener("beforeunload",beforeUnloadListener,{capture:!0})}updateView(),editor.session.on("change",function(e){updateView()});
2 |
--------------------------------------------------------------------------------
/tests.md:
--------------------------------------------------------------------------------
1 | # Tests
2 |
3 | These sentences should be rendered and highlighted correctly.
4 |
5 | ## Long sentences
6 |
7 | If you see a yellow highlight, your sentence is so dense and complicated that your readers will get lost trying to understand it and then you should consider splitting it up.
8 |
9 | ## Passive Voice
10 |
11 | Harry ate six shrimp at dinner. (active)
12 | At dinner, six shrimp were eaten by Harry. (passive)
13 |
14 | Beautiful giraffes roam the savannah. (active)
15 | The savannah is roamed by beautiful giraffes. (passive)
16 |
17 | Sue changed the flat tire. (active)
18 | The flat tire was changed by Sue. (passive)
19 |
20 | We are going to watch a movie tonight. (active)
21 | A movie is going to be watched by us tonight. (passive)
22 |
23 | I ran the obstacle course in record time. (active)
24 | The obstacle course was run by me in record time. (passive)
25 |
26 | The crew paved the entire stretch of highway. (active)
27 | The entire stretch of highway was paved by the crew. (passive)
28 |
29 | Mom read the novel in one day. (active)
30 | The novel was read by Mom in one day. (passive)
31 |
32 | be --- given.
33 |
34 | ”be beaten” and “were liked”, but not “between” or “be tenth” or “be a person who walked”
35 |
36 | The critic wrote a scathing review. (active)
37 | A scathing review was written by the critic. (passive)
38 |
39 | I will clean the house every Saturday. (active)
40 | The house will be cleaned by me every Saturday. (passive)
41 |
42 | The staff is required to watch a safety video every year. (active)
43 | A safety video will be watched by the staff every year. (passive)
44 |
45 | She faxed her application for a new job. (active)
46 | The application for a new job was faxed by her. (passive)
47 |
48 | Tom painted the entire house. (active)
49 | The entire house was painted by Tom. (passive)
50 |
51 | The teacher always answers the students’ questions. (active)
52 | The students’ questions are always answered by the teacher. (passive)
53 |
54 | The choir really enjoys that piece. (active)
55 | That piece is really enjoyed by the choir. (passive)
56 |
57 | Who taught you to ski? (active)
58 | By whom were you taught to ski? (passive)
59 |
60 | The forest fire destroyed the whole suburb. (active)
61 | The whole suburb was destroyed by the forest fire. (passive)
62 |
63 | The two kings are signing the treaty. (active)
64 | The treaty is being signed by the two kings. (passive)
65 |
66 | The cleaning crew vacuums and dusts the office every night. (active)
67 | Every night the office is vacuumed and dusted by the cleaning crew. (passive)
68 |
69 | Larry generously donated money to the homeless shelter. (active)
70 | Money was generously donated to the homeless shelter by Larry. (passive)
71 |
72 | No one responded to my sales ad. (active)
73 | My sales ad was not responded to by anyone. (passive)
74 |
75 | The wedding planner is making all the reservations. (active)
76 | All the reservations will be made by the wedding planner. (passive)
77 |
78 | Susan will bake two dozen cupcakes for the bake sale. (active)
79 | For the bake sale, two dozen cookies will be baked by Susan. (passive)
80 |
81 | The science class viewed the comet. (active)
82 | The comet was viewed by the science class. (passive)
83 |
84 | Who ate the last cookie? (active)
85 | The last cookie was eaten by whom? (passive)
86 |
87 | Alex posted the video on Facebook. (active)
88 | The video was posted on Facebook by Alex. (passive)
89 |
90 | The director will give you instructions. (active)
91 | Instructions will be given to you by the director. (passive)
92 |
93 | Thousands of tourists view the Grand Canyon every year. (active)
94 | The Grand Canyon is viewed by thousands of tourists every year. (passive)
95 |
96 | The homeowners remodeled the house to help it sell. (active)
97 | The house was remodeled by the homeowners to help it sell. (passive)
98 |
99 | The team will celebrate their victory tomorrow. (active)
100 | The victory will be celebrated by the team tomorrow. (passive)
101 |
102 | The saltwater eventually corroded the metal beams. (active)
103 | The metal beams were eventually corroded by the saltwater. (passive)
104 |
105 | The kangaroo carried her baby in her pouch. (active)
106 | The baby was carried by the kangaroo in her pouch. (passive)
107 |
108 | Some people raise sugar cane in Hawaii. (active)
109 | Sugar cane is raised by some people in Hawaii. (passive)
110 |
111 | ## Generic expressions
112 |
113 | There are all kinds of generic expressions.
114 |
115 | It can happen that your readers will find them difficult to interpret.
116 |
117 | Something occurred here.
118 |
119 | ## Adverbs
120 |
121 | Unfortunately, adverbs sometimes make technical readers bark loudly and ferociously.
122 |
123 | Perhaps or maybe. Sometimes and often.
124 |
125 | ## Special characters
126 |
127 | It learns to buy & hold for years, so we slowly drop the rewards to zero after 20 days of holding a position open.
128 |
129 | If you see a yellow highlight, your sentence is so dense and complicated; your readers will get lost trying to understand it and then you should consider splitting it up.
130 |
--------------------------------------------------------------------------------
/js/techscriptor.js:
--------------------------------------------------------------------------------
1 | var editor = ace.edit("editor");
2 | editor.setTheme("ace/theme/chrome");
3 | editor.session.setMode("ace/mode/markdown");
4 | editor.setOptions({
5 | fontSize: 14,
6 | vScrollBarAlwaysVisible: true,
7 | wrap: true
8 | });
9 | function loadPopovers() {
10 | var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
11 | // console.log(popoverTriggerList);
12 | var popoverList = popoverTriggerList.map(function (element) {
13 | return new bootstrap.Popover(element, {
14 | trigger: 'hover focus',
15 | html: true
16 | });
17 | });
18 | }
19 | function getPopover(text, message, color) {
20 | return `
22 | ${text}
23 | `;
24 | }
25 | var md = window.markdownit().use(window.markdownitFootnote);
26 | const beforeUnloadListener = (event) => {
27 | event.preventDefault();
28 | return event.returnValue = "Are you sure you want to exit?";
29 | };
30 | function getReplacementString(i) {
31 | return "[#r" + i + "]";
32 | }
33 | function countWords(str) {
34 | const arr = str.split(' ');
35 | return arr.filter(word => word !== '').length;
36 | }
37 | function countSentences(str) {
38 | s = str.match(/[\w|\)][.?!](\s|$)/g);
39 | if (s == null) {
40 | return 0;
41 | }
42 | return s.length;
43 | }
44 | function updateView() {
45 | htmlOutput = md.render(editor.getValue());
46 | var replacements = [];
47 | const rules = {
48 | "hardToRead": {
49 | "regex": /((\w+,\s+)|(\w+\s+)){20,}(\w+[\.|?|!])/g,
50 | "message": "This sentence is hard to read.",
51 | "color": "#f0f0a0",
52 | "summary": " sentences are hard to read.",
53 | "summarySingle": " sentence is hard to read."
54 | },
55 | "adverbs": {
56 | "regex": /((\w+)ly)|sometimes|perhaps|maybe/g,
57 | "message": "Adverb: Use a forceful verb.",
58 | "color": "#a0e0ff",
59 | "summary": " adverbs.",
60 | "summarySingle": " adverb."
61 | },
62 | "passiveVoice": {
63 | "regex": /\b((be(en)?)|(w(as|ere))|(is)|(a(er|m)))(\.|\,)?\s(\w+\s)?(\w+(en|ed))(\s|\.|\,)/g,
64 | "message": "Passive voice: Use active voice.",
65 | "color": "#a0f0a0",
66 | "summary": " uses of passive voice.",
67 | "summarySingle": " use of passive voice."
68 | },
69 | "thereIsThereAre": {
70 | "regex": /\bThere (is|are)\b/g,
71 | "message": "Generic: Be precise.",
72 | "color": "#f0a0f0",
73 | "summary": " uses of There is/There are.",
74 | "summarySingle": " use of There is/There are."
75 | },
76 | "genericVerb": {
77 | "regex": /\b(happen|occur)(s?)/g,
78 | "message": "Generic verb: Use precise verbs.",
79 | "color": "#f0a0f0",
80 | "summary": " uses of generic verbs.",
81 | "summarySingle": " use of generic verbs."
82 | }
83 | }
84 | var ruleReplacements = {}
85 | for (var label in rules) {
86 | rule = rules[label];
87 | ruleReplacements[label] = 0;
88 | htmlOutput = htmlOutput.replaceAll(rule["regex"], function (match) {
89 | replacements.push(getPopover(match, rule["message"], rule["color"]));
90 | ruleReplacements[label]++;
91 | return getReplacementString(replacements.length - 1);
92 | });
93 | }
94 | console.log(ruleReplacements);
95 | rulesSummary = "";
96 | for (var label in rules) {
97 | var s = rules[label]["summary"];
98 | if (ruleReplacements[label] == 1) {
99 | s = rules[label]["summarySingle"];
100 | }
101 | rulesSummary += `
# Built for Technical Writing
27 |
28 | > 
29 | > ***Techscriptor*** is a Markdown editor that makes your writing precise and
30 | > clear.
31 |
32 | Techscriptor helps you avoid the following:
33 |
34 | * **Long sentences:** Make your sentences short and clear. If you see a yellow
35 | highlight, your sentence is so dense and complicated that your readers will get
36 | lost trying to understand it and then you should consider splitting it up.
37 | * **Passive voice:** Use the active voice most of the time. If the passive voice
38 | is used, sentences tend to be more wordy and vague.
39 | * **Generic expressions:** Reduce imprecise, weak, or generic words. There are
40 | all kinds of generic expressions. It can happen that your readers will find them
41 | difficult to interpret. Try replacing them with more specific expressions.
42 | * **Adverbs:** Avoid adverbs if they add no significant meaning. Unfortunately,
43 | adverbs sometimes make technical readers bark loudly and ferociously.
44 |
45 | Techscriptor renders Markdown in the browser:
46 |
47 | * inline `code`
48 | * code blocks with syntax highlighting for many languages:
49 | ```python
50 | def F(n):
51 | if n == 0 or n == 1:
52 | return n
53 | else:
54 | return F(n - 1) + F(n - 2)
55 |
56 | x = 7
57 | print(f"F({x}) = {F(x)}")
58 | ```
59 | * footnotes[^1]
60 |
61 | [^1]: My reference.
62 |
63 | * tables:
64 |
65 | | Tables | Are | Cool |
66 | | ------------- |:-------------:| -----:|
67 | | col 3 is | right-aligned | $1600 |
68 | | col 2 is | centered | $12 |
69 | | zebra stripes | are neat | $1 |
70 |
71 | * blockquotes:
72 | > Life is a long lesson in humility.
73 | > — James M. Barrie
74 |