23 | 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:
24 |
25 |
26 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
27 |
28 |
29 | 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.
30 |
This addon lets you create and edit text based graphs directly in the google docs ui. Add this google docs addon to enjoy the power of the mermaid library in google docs. See the
26 | Privacy Policy, License or check the code on Github
27 |
28 |
29 |
Presentation video
30 |
31 |
32 |
When you add a graph, you can type mermaid code in the left panel and see the preview on the right update.
33 |
34 |
Feel free to change the theme. Then click "insert" to generate the image and add it to your document.
35 |
36 |
If you do a typo, the plugin will try to tell you where
37 |
38 |
You can always get the source code of your graph by checking its alt texts
39 |
40 |
Check out the mermaid website to see what graph types are avaliable
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/Mermaid Slides.gs:
--------------------------------------------------------------------------------
1 | /**
2 | * @OnlyCurrentDoc
3 | */
4 | function onInstall() {
5 | onOpen();
6 | }
7 |
8 | function onOpen() {
9 | SlidesApp.getUi()
10 | .createAddonMenu()
11 | .addItem('New chart', 'addNewChart')
12 | .addItem('Edit selected chart', 'editSelectedChart')
13 | .addToUi();
14 |
15 | }
16 |
17 |
18 | function addNewChart(){
19 | var selected=findSelectedImage()
20 | if(selected){
21 | SlidesApp.getUi().alert('You have a chart selected, please unselect it first, or click "edit" to edit it.');
22 | }else{
23 | openDialog("graph LR\n A -->B", 'Insert',"")
24 | }
25 | }
26 |
27 |
28 | function editSelectedChart(){
29 | var selected=findSelectedImage()
30 | if(!selected){
31 | SlidesApp.getUi().alert('Please select an existing chart created with this app first.');
32 | }else{
33 | let source=selected.getDescription();
34 | let theme= selected.getTitle().replace('mermaid-graph/','') || ""
35 |
36 | try{
37 | // backward compat
38 | const decoded=JSON.parse(source);
39 | if(decoded.source){
40 | source=decoded.source
41 | }
42 | if(decoded.theme){
43 | theme=decoded.theme
44 | }
45 | }catch(e){
46 | }
47 |
48 | openDialog(source, 'Update', theme, selected.getWidth())
49 | }
50 | }
51 |
52 | function openDialog(source,label,theme, currentWidth=0) {
53 |
54 | var html = HtmlService.createHtmlOutputFromFile('index')
55 | .setWidth(3000)
56 | .setHeight(2000)
57 | .append(``) ;
60 |
61 |
62 | SlidesApp.getUi()
63 | .showModalDialog(html, 'Graph editor')
64 | }
65 |
66 |
67 | function findSelectedImage(){
68 |
69 | try{
70 | return SlidesApp.getActivePresentation()
71 | .getSelection()
72 | ?.getPageElementRange()
73 | ?.getPageElements()
74 | //?.getRangeElements()
75 | // ?.map(rangeElement=>rangeElement.getElement())
76 | ?.find(element=>element.getPageElementType()==SlidesApp.PageElementType.IMAGE && element.asImage().getTitle()?.startsWith('mermaid-graph'))
77 | ?.asImage()
78 | }catch(e){
79 | console.error(e);
80 |
81 | SlidesApp.getUi().alert( 'It looks like you are trying to use this addon with multiple google accounts'+
82 | ' logged in. Only the "default" account (the one shown when you open google) will work here,'+
83 | ' because of a limitation of google docs. You can either open this page in a private navigation, or change'+
84 | ' the default account. To change the default account, log out of all accounts, then log in with the desired'+
85 | ' default account first. Sorry for the inconvenience, it is an issue out of our control. Error message : '+e.message);
86 | }
87 |
88 | }
89 |
90 | function insertImage(source, theme, base64,width, height){
91 | var blob=Utilities.newBlob(Utilities.base64Decode(base64.split(',')[1]), 'image/png', "mermaid-chart.png");
92 |
93 | var selected=findSelectedImage()
94 |
95 | var posImage=null;
96 |
97 | if(selected){
98 | posImage=selected.replace(blob)
99 | }
100 |
101 | if(!posImage){
102 | posImage=SlidesApp.getActivePresentation().getSelection()
103 | ?.getCurrentPage()
104 | ?.insertImage(blob)
105 | }
106 |
107 |
108 | if(!posImage){
109 | posImage=SlidesApp.getActivePresentation().appendSlide()
110 | ?.insertImage(blob)
111 | }
112 |
113 | posImage.setDescription(source);
114 | posImage.setTitle('mermaid-graph/'+theme)
115 |
116 | posImage.setWidth(width)
117 | posImage.setHeight(height)
118 |
119 | }
--------------------------------------------------------------------------------
/src/Code.gs:
--------------------------------------------------------------------------------
1 | /**
2 | * @OnlyCurrentDoc
3 | */
4 | function onInstall() {
5 | onOpen();
6 | }
7 |
8 | function onOpen() {
9 | DocumentApp.getUi()
10 | .createAddonMenu()
11 | .addItem('New chart', 'addNewChart')
12 | .addItem('Edit selected chart', 'editSelectedChart')
13 | .addToUi();
14 | }
15 |
16 |
17 | function addNewChart(){
18 | var selected=findSelectedImage()
19 | if(selected){
20 | DocumentApp.getUi().alert('You have a chart selected, please unselect it first, or click "edit" to edit it.');
21 | }else{
22 | openDialog("graph LR\n A -->B", 'Insert',"")
23 | }
24 | }
25 |
26 |
27 | function editSelectedChart(){
28 | var selected=findSelectedImage()
29 | if(!selected){
30 | DocumentApp.getUi().alert('Please select an existing chart created with this app first. Make sure the graph image placement is "in line" or it will not work.');
31 | }else{
32 | let source=selected.getAltDescription();
33 | let theme= selected.getAltTitle().replace('mermaid-graph/','') || ""
34 |
35 | try{
36 | // backward compat
37 | const decoded=JSON.parse(source);
38 | if(decoded.source){
39 | source=decoded.source
40 | }
41 | if(decoded.theme){
42 | theme=decoded.theme
43 | }
44 | }catch(e){
45 | }
46 |
47 | openDialog(source, 'Update', theme, selected.getWidth())
48 | }
49 | }
50 |
51 | function openDialog(source,label,theme, currentWidth=0) {
52 |
53 | var html = HtmlService.createHtmlOutputFromFile('index')
54 | .setWidth(3000)
55 | .setHeight(2000)
56 | .append(``) ;
59 |
60 |
61 | DocumentApp.getUi()
62 | .showModalDialog(html, 'Graph editor')
63 | }
64 |
65 |
66 | function findSelectedImage(){
67 |
68 | try{
69 | return DocumentApp.getActiveDocument().getSelection()
70 | ?.getRangeElements()
71 | ?.map(rangeElement=>rangeElement.getElement())
72 | .find(element=>element.getType()==DocumentApp.ElementType.INLINE_IMAGE && element.asInlineImage().getAltTitle()?.startsWith('mermaid-graph'))
73 | ?.asInlineImage()
74 | }catch(e){
75 | DocumentApp.getUi().alert('It looks like you are trying to use this addon with multiple google accounts'+
76 | ' logged in. Only the "default" account (the one shown when you open google) will work here,'+
77 | ' because of a limitation of google docs. You can either open this page in a private navigation, or change'+
78 | ' the default account. To change the default account, log out of all accounts, then log in with the desired'+
79 | ' default account first. Sorry for the inconvenience, it is an issue out of our control. ');
80 | }
81 |
82 | }
83 |
84 | function insertImage(source, theme, base64,width, height){
85 | var blob=Utilities.newBlob(Utilities.base64Decode(base64.split(',')[1]), 'image/png', "mermaid-chart.png");
86 |
87 | var selected=findSelectedImage()
88 |
89 | var cursor=DocumentApp.getActiveDocument().getCursor(),posImage=null;
90 |
91 | if(selected){
92 | var parent = selected.getParent();
93 | posImage= parent.insertInlineImage(parent.getChildIndex(selected)+1, blob) ;
94 | selected.removeFromParent();
95 | }
96 |
97 | if(!posImage && cursor){
98 | posImage=cursor.insertInlineImage(blob)
99 | }
100 |
101 | if(!posImage){
102 | // No cursor or could not insert
103 | var body = DocumentApp.getActiveDocument().getBody();
104 | var paragraph = body.appendParagraph('')
105 | posImage = paragraph.appendInlineImage(blob);
106 | }
107 |
108 | posImage.setAltDescription(source);
109 | posImage.setAltTitle('mermaid-graph/'+theme)
110 |
111 | posImage.setWidth(width)
112 | posImage.setHeight(height)
113 |
114 | }
--------------------------------------------------------------------------------
/src/Mermaid Gdocs.gs:
--------------------------------------------------------------------------------
1 | /**
2 | * @OnlyCurrentDoc
3 | */
4 | function onInstall() {
5 | onOpen();
6 | }
7 |
8 | function onOpen() {
9 | DocumentApp.getUi()
10 | .createAddonMenu()
11 | .addItem('New chart', 'addNewChart')
12 | .addItem('Edit selected chart', 'editSelectedChart')
13 | .addToUi();
14 |
15 | }
16 |
17 |
18 | function addNewChart(){
19 | var selected=findSelectedImage()
20 | if(selected){
21 | DocumentApp.getUi().alert('You have a chart selected, please unselect it first, or click "edit" to edit it.');
22 | }else{
23 | openDialog("graph LR\n A -->B", 'Insert',"")
24 | }
25 | }
26 |
27 |
28 | function editSelectedChart(){
29 | var selected=findSelectedImage()
30 | if(!selected){
31 | DocumentApp.getUi().alert('Please select an existing chart created with this app first. Make sure the graph image placement is "in line" or it will not work.');
32 | }else{
33 | let source=selected.getAltDescription();
34 | let theme= selected.getAltTitle().replace('mermaid-graph/','') || ""
35 |
36 | try{
37 | // backward compat
38 | const decoded=JSON.parse(source);
39 | if(decoded.source){
40 | source=decoded.source
41 | }
42 | if(decoded.theme){
43 | theme=decoded.theme
44 | }
45 | }catch(e){
46 | }
47 |
48 | openDialog(source, 'Update', theme, selected.getWidth())
49 | }
50 | }
51 |
52 | function openDialog(source,label,theme, currentWidth=0) {
53 |
54 | var html = HtmlService.createHtmlOutputFromFile('index')
55 | .setWidth(3000)
56 | .setHeight(2000)
57 | .append(``) ;
60 |
61 |
62 | DocumentApp.getUi()
63 | .showModalDialog(html, 'Graph editor')
64 | }
65 |
66 |
67 | function findSelectedImage(){
68 |
69 | try{
70 | return DocumentApp.getActiveDocument().getSelection()
71 | ?.getRangeElements()
72 | ?.map(rangeElement=>rangeElement.getElement())
73 | .find(element=>element.getType()==DocumentApp.ElementType.INLINE_IMAGE && element.asInlineImage().getAltTitle()?.startsWith('mermaid-graph'))
74 | ?.asInlineImage()
75 | }catch(e){
76 | DocumentApp.getUi().alert('It looks like you are trying to use this addon with multiple google accounts'+
77 | ' logged in. Only the "default" account (the one shown when you open google) will work here,'+
78 | ' because of a limitation of google docs. You can either open this page in a private navigation, or change'+
79 | ' the default account. To change the default account, log out of all accounts, then log in with the desired'+
80 | ' default account first. Sorry for the inconvenience, it is an issue out of our control. ');
81 | }
82 |
83 | }
84 |
85 | function insertImage(source, theme, base64,width, height){
86 | var blob=Utilities.newBlob(Utilities.base64Decode(base64.split(',')[1]), 'image/png', "mermaid-chart.png");
87 |
88 | var selected=findSelectedImage()
89 |
90 | var cursor=DocumentApp.getActiveDocument().getCursor(),posImage=null;
91 |
92 | if(selected){
93 | var parent = selected.getParent();
94 | posImage= parent.insertInlineImage(parent.getChildIndex(selected)+1, blob) ;
95 | selected.removeFromParent();
96 | }
97 |
98 | if(!posImage && cursor){
99 | posImage=cursor.insertInlineImage(blob)
100 | }
101 |
102 | if(!posImage){
103 | // No cursor or could not insert
104 | var body = DocumentApp.getActiveDocument().getBody();
105 | var paragraph = body.appendParagraph('')
106 | posImage = paragraph.appendInlineImage(blob);
107 | }
108 |
109 | posImage.setAltDescription(source);
110 | posImage.setAltTitle('mermaid-graph/'+theme)
111 |
112 | posImage.setWidth(width)
113 | posImage.setHeight(height)
114 |
115 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mermaid-gdocs
2 | [Mermaidsjs](https://mermaidjs.github.io/) wrapper for google docs
3 |
4 |
5 | # known limitations
6 |
7 | - only inline images work in google docs
8 | - icons (fontawesome) not supported
9 |
10 | # Development setup
11 |
12 | To test the editor ui outside of google docs, serve the src folder, for example `npx http-server src` and open http://127.0.0.1:8080 in your browser.
13 |
14 | `src/Code.gs` is a copy of what's running in apps script, same for `src/appsscript.json`.
15 |
16 | # License : MIT
17 |
18 | Do whatever you want with this code, but i'm not responsible if it breaks. I'm trying to publish it on the google apps store so that people can use it in their google docs
19 |
20 | # Sample graphs
21 |
22 | This one is good to bebug scaling issues
23 |
24 | graph TB
25 | sq[Square shape] --> ci((Circle shape))
26 |
27 | subgraph A
28 | od>Odd shape]-- Two line edge comment --> ro
29 | di{Diamond with line break} -.-> ro(Rounded square shape)
30 | di==>ro2(Rounded square shape)
31 | end
32 |
33 | %% Notice that no text in shape are added here instead that is appended further down
34 | e --> od3>Really long text with linebreak in an Odd shape]
35 |
36 | %% Comments after double percent signs
37 | e((Inner / circle and some odd special characters)) --> f(,.?!+-*ز)
38 |
39 | cyr[Cyrillic]-->cyr2((Circle shape Начало));
40 |
41 | classDef green fill:#9f6,stroke:#333,stroke-width:2px;
42 | classDef orange fill:#f96,stroke:#333,stroke-width:4px;
43 | class sq,e green
44 | class di orange
45 |
46 | Another big one
47 |
48 | sequenceDiagram
49 | participant web as Web Browser
50 | participant blog as Blog Service
51 | participant account as Account Service
52 | participant mail as Mail Service
53 | participant db as Storage
54 |
55 | Note over web,db: The user must be logged in to submit blog posts
56 | web->>+account: Logs in using credentials
57 | account->>db: Query stored accounts
58 | db->>account: Respond with query result
59 |
60 | alt Credentials not found
61 | account->>web: Invalid credentials
62 | else Credentials found
63 | account->>-web: Successfully logged in
64 |
65 | Note over web,db: When the user is authenticated, they can now submit new posts
66 | web->>+blog: Submit new post
67 | blog->>db: Store post data
68 |
69 | par Notifications
70 | blog--)mail: Send mail to blog subscribers
71 | blog--)db: Store in-site notifications
72 | and Response
73 | blog-->>-web: Successfully posted
74 | end
75 | end
76 |
77 | An excellent example from canonical
78 |
79 | graph TD;
80 | A["Create Video Issue in Backlog"] --> B["Assign 10 Points"];
81 | B --> C["Move to 'In Progress'"];
82 | C --> D["Video Template Appears in Description"];
83 | D --> E["Move to 'In Review' for Content Team"];
84 | E --> F["Ensure Issue Contains Copy Doc"];
85 | F --> G["Issue Visible in Review Board"];
86 | G --> H["Comment Appears Tagging Content Team"];
87 | H --> I["Content Team Assigns T-Shirt Size"];
88 |
89 | I -->|Not Satisfied| J["Move Back to 'In Progress'"];
90 | I -->|Satisfied| K["Assign to 'Pending Marketing Acceptance' & Tag Marketing Manager"];
91 |
92 | K --> L["Marketing Manager Notifies Social Media Team"];
93 | L --> M["Create Ticket or Add to Campaign Epic"];
94 |
95 | K -->|Ready & Approved| N["Brief Design Team & Move to 'In Design'"];
96 | N --> O["Complete Design Brief Subtask (Creates Linked Issue in Brand Project)"];
97 | O --> P["Brand PM Acknowledges & Informs Planning"];
98 |
99 | P --> Q["Designer Moves Task to 'In Progress'"];
100 | Q --> R["Designer Moves Task to 'In Review' & Uploads File"];
101 | R --> S["Tag Requestor & Stakeholders for Review"];
102 | S --> T["Designer Iterates Based on Feedback"];
103 |
104 | T -->|Design Approved| U["Upload Final Version to Design Drive & Paste Link in Jira"];
105 | U --> V["Set Task Status to 'Done'"];
106 | V --> W["Design Team Marks Linked Issue as Done"];
107 | W --> X["Video Issue Owner Marks as 'Marketing Ready' & Tags Marketing Team"];
108 |
109 | X -->|Published| Y["Issue Owner Marks as 'Done'"];
110 |
--------------------------------------------------------------------------------
/docs/privacy-policy.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Mermaid plugin for google docs
7 |
10 |
11 |
12 |
13 |
Mermaid google docs addon ("us", "we", or "our") operates the https://renanlecaro.github.io/mermaid-gdocs/ website (the "Service").
27 |
28 |
This page informs you of our policies regarding the collection, use, and disclosure of personal data when you use our Service and the choices you have associated with that data.
29 |
30 |
We use your data to provide and improve the Service. By using the Service, you agree to the collection and use of information in accordance with this policy. Unless otherwise defined in this Privacy Policy, terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, accessible from https://renanlecaro.github.io/mermaid-gdocs/
31 |
32 |
33 |
Information Collection And Use
34 |
35 |
We collect several different types of information for various purposes to provide and improve our Service to you.
36 |
37 |
Types of Data Collected
38 |
39 |
Personal Data
40 |
41 |
While using our Service, we may ask you to provide us with certain personally identifiable information that can be used to contact or identify you ("Personal Data"). Personally identifiable information may include, but is not limited to:
42 |
43 |
44 |
Cookies and Usage Data
45 |
46 |
47 |
Usage Data
48 |
49 |
We may also collect information how the Service is accessed and used ("Usage Data"). This Usage Data may include information such as your computer's Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that you visit, the time and date of your visit, the time spent on those pages, unique device identifiers and other diagnostic data.
50 |
51 |
Tracking & Cookies Data
52 |
We use cookies and similar tracking technologies to track the activity on our Service and hold certain information.
53 |
Cookies are files with small amount of data which may include an anonymous unique identifier. Cookies are sent to your browser from a website and stored on your device. Tracking technologies also used are beacons, tags, and scripts to collect and track information and to improve and analyze our Service.
54 |
You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent. However, if you do not accept cookies, you may not be able to use some portions of our Service.
55 |
Examples of Cookies we use:
56 |
57 |
Session Cookies. We use Session Cookies to operate our Service.
58 |
Preference Cookies. We use Preference Cookies to remember your preferences and various settings.
59 |
Security Cookies. We use Security Cookies for security purposes.
60 |
61 |
62 |
Use of Data
63 |
64 |
Mermaid google docs addon uses the collected data for various purposes:
65 |
66 |
To provide and maintain the Service
67 |
To notify you about changes to our Service
68 |
To allow you to participate in interactive features of our Service when you choose to do so
69 |
To provide customer care and support
70 |
To provide analysis or valuable information so that we can improve the Service
71 |
To monitor the usage of the Service
72 |
To detect, prevent and address technical issues
73 |
74 |
75 |
Transfer Of Data
76 |
Your information, including Personal Data, may be transferred to — and maintained on — computers located outside of your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from your jurisdiction.
77 |
If you are located outside Germany and choose to provide information to us, please note that we transfer the data, including Personal Data, to Germany and process it there.
78 |
Your consent to this Privacy Policy followed by your submission of such information represents your agreement to that transfer.
79 |
Mermaid google docs addon will take all steps reasonably necessary to ensure that your data is treated securely and in accordance with this Privacy Policy and no transfer of your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of your data and other personal information.
80 |
81 |
Disclosure Of Data
82 |
83 |
Legal Requirements
84 |
Mermaid google docs addon may disclose your Personal Data in the good faith belief that such action is necessary to:
85 |
86 |
To comply with a legal obligation
87 |
To protect and defend the rights or property of Mermaid google docs addon
88 |
To prevent or investigate possible wrongdoing in connection with the Service
89 |
To protect the personal safety of users of the Service or the public
90 |
To protect against legal liability
91 |
92 |
93 |
Security Of Data
94 |
The security of your data is important to us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While we strive to use commercially acceptable means to protect your Personal Data, we cannot guarantee its absolute security.
95 |
96 |
Service Providers
97 |
We may employ third party companies and individuals to facilitate our Service ("Service Providers"), to provide the Service on our behalf, to perform Service-related services or to assist us in analyzing how our Service is used.
98 |
These third parties have access to your Personal Data only to perform these tasks on our behalf and are obligated not to disclose or use it for any other purpose.
99 |
100 |
101 |
102 |
Links To Other Sites
103 |
Our Service may contain links to other sites that are not operated by us. If you click on a third party link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit.
104 |
We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.
105 |
106 |
107 |
Children's Privacy
108 |
Our Service does not address anyone under the age of 18 ("Children").
109 |
We do not knowingly collect personally identifiable information from anyone under the age of 18. If you are a parent or guardian and you are aware that your Children has provided us with Personal Data, please contact us. If we become aware that we have collected Personal Data from children without verification of parental consent, we take steps to remove that information from our servers.
110 |
111 |
112 |
Changes To This Privacy Policy
113 |
We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.
114 |
We will let you know via email and/or a prominent notice on our Service, prior to the change becoming effective and update the "effective date" at the top of this Privacy Policy.
115 |
You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.
116 |
117 |
118 |
Contact Us
119 |
If you have any questions about this Privacy Policy, please contact us: