Teamemo for professionals

Teamemo’s fields of application are very different and individual. And so are its users.
We have developed a new feature so that power users also get their money’s worth at Teamemo.

The Quickbox

With the Quickbox it is possible to directly execute many commands that were previously only found in submenus. And the best: You don’t even have to take your hand off the keyboard to operate the mouse. With the help of the keyboard control str + P (Under max cmd + p) the Quickbox opens.

 

The new Quickbox in Teamemo

The new Quickbox in Teamemo

 

Now you can start searching for the desired command. If you want to export the current memo as a PDF file, for example, you can simply type “pdf”. A confirmation with “Return” executes the desired action directly.

 

New interactive help

To help you get started with Teamemo, our new help function is now available in the Teamemo software. A help topic can be selected by clicking on the help icon in the navigation bar. This then guides you through the Teamemo software and presents the corresponding functions. This makes it easy to learn how to use Teamemo.

Using teamemo as an intranet.

Many small and medium-sized companies do not have their own intranet. But companies with a manageable number of employees in particular can benefit greatly from an internal knowledge platform. For example, if an employee is absent due to illness, many tasks cannot be completed without asking the employee.

An intranet with integrated knowledge management can help here. Through logged procedures and procedures, unnecessary queries can be avoided and employees can enjoy their well-earned vacation more intensively.

 

Permissions management settings in Teamemo

Permissions management settings in Teamemo

Teamemo’s rights management allows you to set how the information in the workspace can be accessed. This allows external users to be completely denied access and only internal users to access (sensitive) company data.

Teamemo loves Feedback

Direct customer contact and feedback from all our customers is very important to us. Only in this way can we identify the needs and develop a modern knowledge management platform that suits our customers.
That’s why there’s a news item. A chat window allows customers and potential customers to communicate directly with our support department.
There you can get direct and uncomplicated help. If no direct contact partner is available, the request will be answered promptly by e-mail.

We at Teamemo welcome any kind of feedback. Be it criticism, praise or the desire for a special feature. Of course, we are also happy to help with problems if you have questions about the operation or the development of your own knowledge management platform.

More Export Options

We have added several Export options for Teamemo Memos. In additional to the exiting PDF-Export, you can now export Memos as Microsoft Word Document, Open Document, HTML Document and Markdown.

Here is a little comparison table of all export formats:

PDF Word / Open Document HTML Markdown
Best for print, offline copy, share as digital document modify, special layout / format, copy into other word document include into website,
standalone webpage, offline copy
copy into markdown document, edit in markdown editor
Applications Adobe Acrobat Reader Microsoft Word, LibreOffice Writer, OpenOffice Writer Browser Markdown Editor

Change the appearance of your sidebar!

We recently added a new Feature to Teamemo. It is now possible to choose your favourite theme for your workspace.

If it’s just your personal preferences or to quickly recognize which workspace you are working on, setting your personal style can have several purposes.

 

Try it out for yourself.

 

Behind Teamemo: Using a C-library in JavaScript

Recently we integrated spellchecking into Teamemo. Therefore we searched for a good JavaScript spellchecking library. There were some very promising available, for example: nspell. It wasn’t very popular, however was good documented and based on hunspell dictionaries – the same spellchecking library used in Apache OpenOffice / LibreOffice and all major browsers.

After some tests it turned out, that it works very well for English texts, however not so well for German ones. While in LibreOffice German texts were spellchecked correctly, with nspell a lot of words were incorrectly marked as misspelled. The problem was that nspell did not support all word combination rules in the aff-Files supplied with the dictionaries and so had trouble with compound words. Adding support for the missing rules in nspell would have been too hard and so we dropped nspell as a solution.

We couldn’t find a pure JavaScript library with better support for affix rules. There are some server side solutions like nodehun. These provide Node.js bindings for the C-library hunspell. However server side spellchecking was not an option for us, because of bigger latency, no offline support and higher server load, so we decided to try something new: Compiling the real hunspell C-library to JavaScript.

There is an awesome compiler called emscripten which compiles C/C++ Code to JavaScript. Actual emscripten works on LLVM bitcode, which can be generated from most programming languages. It claims that the generated JS-code runs at near native speed so we wanted to give it a try.

Teamemo is a modern wiki software featuring collaborative editing in an innovative WYSIWYG Editor.

We cloned the hunspell repository and installed the emscripten SDK. Respectively for  convenience we used a emscripten docker image so the emscripten compiler was just one command away:

docker run --rm -v $(pwd):/src -ti apiaryio/emcc bash

Emscripten does provide its own C/C++ compiler for the build toolchain and some helper scripts to set the correct environment variables to build projects using the GNU make:

emconfigure ./configure
emmake make

This compiled the hunspell C/C++ code to LLVM bitcode, which can then be compiled to JavaScript. The final build step to get a JS-File is to invoke em++ directly:

em++ \
  -O3 \
  -Oz \
  --llvm-lto 1 \
  -s NO_EXIT_RUNTIME=1 \
  -s EXPORTED_FUNCTIONS="['_Hunspell_create', '_Hunspell_destroy', '_Hunspell_spell']" \
  ../src/hunspell/.libs/libhunspell-1.6.a \
  -o hunspell.js

The first four parameters are some optimization flags. EXPORTED_FUNCTIONS defines which functions should be exported to the JavaScript-World. If you change the output file from hunspell.js to hunspell.html then emscripten generates a html page which does some required definitions and includes the compiled JS so that you can test your compiled source directly by firing up a simple web server serving the files:

python -m SimpleHTTPServer 8080

However to call the C-Functions from JS some wrapper code is required. The simplest way is to use Module.cwrap. You only have to provide the function name and return and parameter types:

Hunspell_create = Module.cwrap('Hunspell_create', 'number', ['string', 'string']);
Hunspell_spell = Module.cwrap('Hunspell_spell', 'number', ['number', 'string']);
Hunspell_destroy = Module.cwrap('Hunspell_destroy', 'number', ['number']);

You can now directly call the compiled C-Code from JS:

var handle = Hunspell_create('dictionary.aff', 'dictionary.dic');
console.log(Hunspell_spell(handle, 'mispelled'));
Hunspell_destroy(handle);

There is just one thing missing: The files are not present in the virtual file system, so hunspell can’t open the dictionary files. You can either specific the files in the compilation step using the –embed-file option or download the files manually using XHR and write the content to a file with FS.writeFile.

Results

The compiled JS-File has a size of 850 kB. In additional a file containing the initial memory must be loaded, which has around 150 kB. So the working example including the wrapper code has around 1 MB size, which is OK for desktop browsers, however could be problematic for some mobile devices.

The performance is very good, we could not find a noticeable performance problem, even for long texts. You can check the performance yourself in Teamemo.

Bonus

In addition to Module.cwrap you can write the wrapper for you C-Function your own. Here is an example for the spell function.  It demonstrates allocating a string on the stack calling the function and restoring the stack.

var _Hunspell_spell = getCFunc('Hunspell_spell');

function allocStr(str) {
  var len = (str.length<<2)+1;
  var ret = Runtime.stackAlloc(len);
  stringToUTF8(str, ret, len);
  return ret;
}

function Hunspell_spell (word) {
  var stack = Runtime.stackSave();
  var wordPtr = allocStr(word);
  var ret = _Hunspell_spell(handle, wordPtr);
  Runtime.stackRestore(stack);
  return !!ret;
}

Since spellchecking is quite expensive and emscripten pollutes the global scope very extensively, we run our spellchecker in a webworkers context. You can find the full example code in the emscripten-hunspell git repository. Visit teamemo.com to see the spellchecking in action.

Since WebAssembly is on its way into all majors Browsers, next step would be to use wasm as a alternative in supported browsers.

Behind Teamemo: Collaborative Editing of Rich Text Documents

One of the best features of Teamemo is that you can edit memos together with you team mates in real time. We are using a technology named Operational Transformation (OT) to sync the text changes of all connected editors.

Document Model and Operations

Rich Text document are typically represented as HTML or an Markup language like Markdown in Web Services. However both can be hardly used together with OT in a WYSIWYG-Editor. Therefore we developed our own document model, wich should have the following properties:

  1. Changes should be syncable using Operational Transformation
  2. Two equal rendered documents should have the same representation in the document model
  3. Is should support custom annotations like comments, highlights, authors

For OT our document model must consist of an linear list of text strings annotated with attributes. We choose a simple representation:

 

[
  { insert: 'This is a '},
  { insert: 'bold', attributes: {bold: true}},
  { insert: ' Text' }
]

 

which renders to the following html:

This is a bold Text.

Changes can be described through the change type and the position. For example:

{ at: 5, insert: 'Text '} 

=> 

[
   { insert: 'This Text is a '}, 
   { insert: 'bold', attributes: {bold: true}},
   { insert: ' Text' }
]

 

{ at: 10, delete: 4}  

=> 

[
    { insert: 'This Text is a '},
    { insert: ' Text' }
]

 

After the application of a change, the document is normalized (see examples above). This is achieved by merging insert operations with the same attributes together into one insert operation:

[{ insert: 'This is a '}, { insert: 'Text '}]
 =>[{ insert: 'This Text is a '}]

 

This ensures a unique representation. Nick Santos from Medium wrote a nice article about why this is important.

Further custom annotations such as authorship can easily be added:

[
  { insert: 'This is a ', attributes: { authorId: 42}},
  { insert: 'bold', attributes: {bold: true, authorId: 7}},
  { insert: 'Text', attributes: { authorId: 42} }
]

 

Transform Operations

Let’s imagine Bob and Alice edit the same document:

[ { insert: 'This is a text'} ]

 

They each change the document in a different way:
Bob adds the word ” fantastic ” at position 9.
Alice deletes the phrase ” a text” starting at position 7.

Bob’s change is send to Alice. Alice already changed the document, so Bob’s changes must be transformed with her local changes, using OT:

transform({at: 9, insert: 'fantastic'}, {at: 7, delete: 7} ) = [{at: 7, insert: 'fantastic'}]

 

She updates her document with the transformed change.

[ { insert: 'This is'}, {at: 7, insert: 'fantastic'} ]
 => [ { insert: 'This is fantastic'} ]

 

In the meantime Bob received the changes from Alice and transforms it in respect to his local change:

transform({at: 7, delete: 7}, {at: 9, insert: 'fantastic'} )
 = [{at: 7, delete: 2}, {at: 16, delete: 5}]

 

He updates his document:

[ { insert: 'This is a fantastic text'}, {at: 7, delete: 2}, {at: 16, delete: 5} ]
 => [ { insert: 'This is fantastic'} ]

 

Magically (or thanks to OT) Bob and Alice have the same document. If you want to play with OT, Tim Baumann created a nice visualization.

What is teamemo?

We think one of the most valuable things in a company is knowledge. Knowledge ensures quality, is the source of innovation and the base of your success. And it’s often distributed through the whole company. So why not merge it in one space?

Each member of your team should be able to participate. So our principle is to offer an easy to use platform to share, collect and store your ideas. The best ideas develop in a team. That’s why we appreciate collaborative writing.

Teamemo is the platform to bring your team together.

Teamemo is perfect for real time colaboration

It’s live

After 5 Month of private beta testing we are proud to announce that the registration for teamemo is now open for everyone. So if you are searching for a tool that supports real-time-collaboration in a wiki-structure, just take a look at teamemo.com.

(via giphy)

We want to express our special thanks to all beta testers for their feedback. Your feedback was amazing and very useful for the development of teamemo.

teamemo feedback dialog

Did you know that you can always send us feedback through the dialog on every page in teamemo. If you want to let us know anything, just write us.